001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/ogcwebservices/wfs/operation/AbstractWFSRequest.java $ 002 /*---------------- FILE HEADER ------------------------------------------ 003 004 This file is part of deegree. 005 Copyright (C) 2001-2008 by: 006 EXSE, Department of Geography, University of Bonn 007 http://www.giub.uni-bonn.de/deegree/ 008 lat/lon GmbH 009 http://www.lat-lon.de 010 011 This library is free software; you can redistribute it and/or 012 modify it under the terms of the GNU Lesser General Public 013 License as published by the Free Software Foundation; either 014 version 2.1 of the License, or (at your option) any later version. 015 016 This library is distributed in the hope that it will be useful, 017 but WITHOUT ANY WARRANTY; without even the implied warranty of 018 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 019 Lesser General Public License for more details. 020 021 You should have received a copy of the GNU Lesser General Public 022 License along with this library; if not, write to the Free Software 023 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 024 025 Contact: 026 027 Andreas Poth 028 lat/lon GmbH 029 Aennchenstraße 19 030 53177 Bonn 031 Germany 032 E-Mail: poth@lat-lon.de 033 034 Prof. Dr. Klaus Greve 035 Department of Geography 036 University of Bonn 037 Meckenheimer Allee 166 038 53115 Bonn 039 Germany 040 E-Mail: greve@giub.uni-bonn.de 041 042 ---------------------------------------------------------------------------*/ 043 package org.deegree.ogcwebservices.wfs.operation; 044 045 import java.io.StringReader; 046 import java.net.URI; 047 import java.net.URISyntaxException; 048 import java.util.HashMap; 049 import java.util.Map; 050 051 import org.deegree.datatypes.QualifiedName; 052 import org.deegree.framework.log.ILogger; 053 import org.deegree.framework.log.LoggerFactory; 054 import org.deegree.framework.xml.NamespaceContext; 055 import org.deegree.framework.xml.XMLTools; 056 import org.deegree.i18n.Messages; 057 import org.deegree.model.filterencoding.AbstractFilter; 058 import org.deegree.model.filterencoding.ComplexFilter; 059 import org.deegree.model.filterencoding.Filter; 060 import org.deegree.ogcwebservices.AbstractOGCWebServiceRequest; 061 import org.deegree.ogcwebservices.InconsistentRequestException; 062 import org.deegree.ogcwebservices.InvalidParameterValueException; 063 import org.deegree.ogcwebservices.wfs.WFService; 064 import org.w3c.dom.Document; 065 066 /** 067 * Abstract base class for requests to web feature services. 068 * 069 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth </a> 070 * @author <a href="mailto:schneider@lat-lon.de">Markus Schneider </a> 071 * @author last edited by: $Author: apoth $ 072 * 073 * @version $Revision: 9345 $, $Date: 2007-12-27 17:22:25 +0100 (Do, 27 Dez 2007) $ 074 */ 075 public class AbstractWFSRequest extends AbstractOGCWebServiceRequest { 076 077 private static final ILogger LOG = LoggerFactory.getLogger( AbstractWFSRequest.class ); 078 079 private static final long serialVersionUID = 6691114984307038750L; 080 081 /** GML2 format * */ 082 public static String FORMAT_GML2 = "text/xml; subtype=gml/2.1.2"; 083 084 /** GML2 format (WFS 1.00 style) * */ 085 public static String FORMAT_GML2_WFS100 = "GML2"; 086 087 /** GML3 format * */ 088 public static String FORMAT_GML3 = "text/xml; subtype=gml/3.1.1"; 089 090 /** Generic XML format * */ 091 public static String FORMAT_XML = "XML"; 092 093 private String handle = null; 094 095 /** 096 * Creates a new <code>AbstractWFSRequest</code> instance. 097 * 098 * @param version 099 * @param id 100 * @param handle 101 * @param vendorSpecificParameter 102 */ 103 protected AbstractWFSRequest( String version, String id, String handle, Map<String, String> vendorSpecificParameter ) { 104 super( version, id, vendorSpecificParameter ); 105 this.handle = handle; 106 } 107 108 /** 109 * Returns the value of the service attribute (WFS). 110 * 111 * @return the value of the service attribute (WFS) 112 */ 113 public String getServiceName() { 114 return "WFS"; 115 } 116 117 /** 118 * Returns the value of the handle attribute. 119 * <p> 120 * The purpose of the <b>handle</b> attribute is to allow a client application to associate a 121 * mnemonic name with a request for error handling purposes. If a <b>handle</b> is specified, 122 * and an exception is encountered, a Web Feature Service may use the <b>handle</b> to identify 123 * the offending element. 124 * 125 * @return the value of the handle attribute 126 */ 127 public String getHandle() { 128 return this.handle; 129 } 130 131 /** 132 * Checks that the "VERSION" parameter value equals a supported version. 133 * 134 * @param model 135 * contains the parameters of the request 136 * @return value for "VERSION" parameter, never null 137 * @throws InconsistentRequestException 138 * if parameter is not present 139 * @throws InvalidParameterValueException 140 */ 141 protected static String checkVersionParameter( Map<String, String> model ) 142 throws InconsistentRequestException, InvalidParameterValueException { 143 String version = model.get( "VERSION" ); 144 if ( version == null ) { 145 throw new InconsistentRequestException( "'VERSION' parameter must be set." ); 146 } 147 148 if ( !WFService.VERSION.equals( version ) && !"1.0.0".equals( version ) ) { 149 String msg = Messages.getMessage( "WFS_REQUEST_UNSUPPORTED_VERSION", version, 150 "1.0.0 and " + WFService.VERSION ); 151 throw new InvalidParameterValueException( msg ); 152 } 153 return version; 154 } 155 156 /** 157 * Checks that the "SERVICE" parameter value equals the name of the service. 158 * 159 * TODO move this to AbstractOGCWebServiceRequest 160 * 161 * @param model 162 * contains the parameters of the request 163 * @throws InconsistentRequestException 164 * if parameter is not present or does not the service name 165 */ 166 protected static void checkServiceParameter( Map<String, String> model ) 167 throws InconsistentRequestException { 168 String service = model.get( "SERVICE" ); 169 if ( !"WFS".equals( service ) ) { 170 throw new InconsistentRequestException( "'SERVICE' parameter must be 'WFS', but is '" + service + "'." ); 171 } 172 } 173 174 /** 175 * Extracts the qualified type names from the TYPENAME parameter. 176 * 177 * @param kvp 178 * @return qualified type names (empty array if TYPENAME parameter is not present) 179 * @throws InvalidParameterValueException 180 */ 181 protected static QualifiedName[] extractTypeNames( Map<String, String> kvp ) 182 throws InvalidParameterValueException { 183 QualifiedName[] typeNames = new QualifiedName[0]; 184 NamespaceContext nsContext = extractNamespaceParameter( kvp ); 185 String typeNameString = kvp.get( "TYPENAME" ); 186 if ( typeNameString != null ) { 187 String[] typeNameStrings = typeNameString.split( "," ); 188 typeNames = new QualifiedName[typeNameStrings.length]; 189 for ( int i = 0; i < typeNameStrings.length; i++ ) { 190 typeNames[i] = transformToQualifiedName( typeNameStrings[i], nsContext ); 191 } 192 } 193 return typeNames; 194 } 195 196 /** 197 * Extracts the namespace bindings from the NAMESPACE parameter. 198 * <p> 199 * Example: 200 * <ul> 201 * <li><code>NAMESPACE=xmlns(myns=http://www.someserver.com),xmlns(yourns=http://www.someotherserver.com)</code></li> 202 * </ul> 203 * <p> 204 * The default namespace may also be bound (two variants are supported): 205 * <ul> 206 * <li><code>NAMESPACE=xmlns(=http://www.someserver.com)</code></li> 207 * <li><code>NAMESPACE=xmlns(http://www.someserver.com)</code></li> 208 * </ul> 209 * 210 * @param model 211 * the parameters of the request 212 * @return namespace context 213 * @throws InvalidParameterValueException 214 */ 215 protected static NamespaceContext extractNamespaceParameter( Map<String, String> model ) 216 throws InvalidParameterValueException { 217 218 String nsString = model.get( "NAMESPACE" ); 219 220 NamespaceContext nsContext = new NamespaceContext(); 221 if ( nsString != null ) { 222 String nsDecls[] = nsString.split( "," ); 223 for ( int i = 0; i < nsDecls.length; i++ ) { 224 String nsDecl = nsDecls[i]; 225 if ( nsDecl.startsWith( "xmlns(" ) && nsDecl.endsWith( ")" ) ) { 226 nsDecl = nsDecl.substring( 6, nsDecl.length() - 1 ); 227 int assignIdx = nsDecl.indexOf( '=' ); 228 String prefix = ""; 229 String nsURIString = null; 230 if ( assignIdx != -1 ) { 231 prefix = nsDecl.substring( 0, assignIdx ); 232 nsURIString = nsDecl.substring( assignIdx + 1 ); 233 } else { 234 nsURIString = nsDecl; 235 } 236 try { 237 URI nsURI = new URI( nsURIString ); 238 nsContext.addNamespace( prefix, nsURI ); 239 } catch ( URISyntaxException e ) { 240 String msg = Messages.getMessage( "WFS_NAMESPACE_PARAM_INVALID_URI", nsURIString, prefix ); 241 throw new InvalidParameterValueException( msg ); 242 } 243 } else { 244 String msg = Messages.getMessage( "WFS_NAMESPACE_PARAM" ); 245 throw new InvalidParameterValueException( msg ); 246 } 247 } 248 } 249 return nsContext; 250 } 251 252 /** 253 * Extracts a <code>Filter</code> from the BBOX parameter. 254 * 255 * TODO handle other dimension count and crs 256 * 257 * @param model 258 * @return filter representing the BBOX parameter (null, if no BBOX parameter specified) 259 * @throws InvalidParameterValueException 260 */ 261 protected static Filter extractBBOXFilter( Map<String, String> model ) 262 throws InvalidParameterValueException { 263 264 ComplexFilter filter = null; 265 String bboxString = model.get( "BBOX" ); 266 if ( bboxString != null ) { 267 String msg = "Parameter 'BBOX' is currently not supported. Please use the 'FILTER' parameter instead."; 268 throw new InvalidParameterValueException( msg ); 269 // String[] parts = bboxString.split( "," ); 270 // double[] coords = new double[4]; 271 // 272 // if ( parts.length > 5 ) { 273 // String msg = Messages.getString( "WFS_BBOX_PARAM_WRONG_COORD_COUNT" ); 274 // throw new InvalidParameterValueException( msg ); 275 // } 276 // 277 // for ( int i = 0; i < coords.length; i++ ) { 278 // try { 279 // coords[i] = Double.parseDouble( parts[i] ); 280 // } catch ( NumberFormatException e ) { 281 // String msg = Messages.getMessage( "WFS_BBOX_PARAM_COORD_INVALID", coords[i] ); 282 // throw new InvalidParameterValueException( msg ); 283 // } 284 // } 285 // 286 // // build filter 287 // Envelope bbox = GeometryFactory.createEnvelope( coords[0], coords[1], coords[2], 288 // coords[3], null ); 289 // Surface surface; 290 // try { 291 // surface = GeometryFactory.createSurface( bbox, null ); 292 // } catch ( GeometryException e ) { 293 // String msg = Messages.getMessage( "WFS_BBOX_PARAM_BBOX_INVALID", e.getMessage() ); 294 // throw new InvalidParameterValueException( msg ); 295 // } 296 // Operation op = new SpatialOperation( OperationDefines.BBOX, null, surface ); 297 // filter = new ComplexFilter( op ); 298 } 299 return filter; 300 } 301 302 /** 303 * Extracts the FILTER parameter and assigns them to the requested type names. 304 * <p> 305 * This is necessary, because it is allowed to specify a filter for each requested feature type. 306 * 307 * @param kvp 308 * @param typeNames 309 * @return map with the assignments of type names to filters 310 * @throws InvalidParameterValueException 311 */ 312 protected static Map<QualifiedName, Filter> extractFilters( Map<String, String> kvp, QualifiedName[] typeNames ) 313 throws InvalidParameterValueException { 314 Map<QualifiedName, Filter> filterMap = new HashMap<QualifiedName, Filter>(); 315 String filterString = kvp.get( "FILTER" ); 316 if ( filterString != null ) { 317 String[] filterStrings = filterString.split( "\\)" ); 318 if ( filterStrings.length != typeNames.length ) { 319 String msg = Messages.getMessage( "WFS_FILTER_PARAM_WRONG_COUNT", Integer.toString( filterStrings.length ), 320 Integer.toString( typeNames.length ) ); 321 throw new InvalidParameterValueException( msg ); 322 } 323 for ( int i = 0; i < filterStrings.length; i++ ) { 324 // remove possible leading parenthesis 325 if ( filterStrings[i].startsWith( "(" ) ) { 326 filterStrings[i] = filterStrings[i].substring( 1 ); 327 } 328 Document doc; 329 try { 330 doc = XMLTools.parse( new StringReader( filterStrings[i] ) ); 331 Filter filter = AbstractFilter.buildFromDOM( doc.getDocumentElement() ); 332 filterMap.put( typeNames[i], filter ); 333 } catch ( Exception e ) { 334 LOG.logError( e.getMessage(), e ); 335 String msg = Messages.getMessage( "WFS_FILTER_PARAM_PARSING", e.getMessage() ); 336 throw new InvalidParameterValueException( msg ); 337 } 338 } 339 } 340 return filterMap; 341 } 342 343 /** 344 * Transforms a type name to a qualified name using the given namespace bindings. 345 * 346 * @param name 347 * @param nsContext 348 * @return QualifiedName 349 * @throws InvalidParameterValueException 350 */ 351 private static QualifiedName transformToQualifiedName( String name, NamespaceContext nsContext ) 352 throws InvalidParameterValueException { 353 QualifiedName typeName; 354 String prefix = ""; 355 int idx = name.indexOf( ':' ); 356 if ( idx != -1 ) { 357 prefix = name.substring( 0, idx ); 358 String localName = name.substring( idx + 1 ); 359 URI nsURI = nsContext.getURI( prefix ); 360 if ( nsURI == null ) { 361 String msg = Messages.getMessage( "WFS_TYPENAME_PARAM_INVALID_URI", prefix ); 362 throw new InvalidParameterValueException( msg ); 363 } 364 typeName = new QualifiedName( prefix, localName, nsURI ); 365 } else { 366 // default namespace prefix ("") 367 URI nsURI = nsContext.getURI( "" ); 368 typeName = new QualifiedName( name, nsURI ); 369 } 370 return typeName; 371 } 372 }