001 //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.4_testing/src/org/deegree/ogcwebservices/csw/discovery/GetRecords.java $
002 /*----------------------------------------------------------------------------
003 This file is part of deegree, http://deegree.org/
004 Copyright (C) 2001-2009 by:
005 Department of Geography, University of Bonn
006 and
007 lat/lon GmbH
008
009 This library is free software; you can redistribute it and/or modify it under
010 the terms of the GNU Lesser General Public License as published by the Free
011 Software Foundation; either version 2.1 of the License, or (at your option)
012 any later version.
013 This library is distributed in the hope that it will be useful, but WITHOUT
014 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
015 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
016 details.
017 You should have received a copy of the GNU Lesser General Public License
018 along with this library; if not, write to the Free Software Foundation, Inc.,
019 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020
021 Contact information:
022
023 lat/lon GmbH
024 Aennchenstr. 19, 53177 Bonn
025 Germany
026 http://lat-lon.de/
027
028 Department of Geography, University of Bonn
029 Prof. Dr. Klaus Greve
030 Postfach 1147, 53001 Bonn
031 Germany
032 http://www.geographie.uni-bonn.de/deegree/
033
034 e-mail: info@deegree.org
035 ----------------------------------------------------------------------------*/
036 package org.deegree.ogcwebservices.csw.discovery;
037
038 import java.io.StringReader;
039 import java.net.URI;
040 import java.net.URISyntaxException;
041 import java.util.Map;
042
043 import org.deegree.framework.log.ILogger;
044 import org.deegree.framework.log.LoggerFactory;
045 import org.deegree.framework.util.StringTools;
046 import org.deegree.framework.xml.NamespaceContext;
047 import org.deegree.framework.xml.XMLParsingException;
048 import org.deegree.framework.xml.XMLTools;
049 import org.deegree.i18n.Messages;
050 import org.deegree.model.filterencoding.AbstractFilter;
051 import org.deegree.model.filterencoding.Filter;
052 import org.deegree.ogcbase.CommonNamespaces;
053 import org.deegree.ogcbase.ExceptionCode;
054 import org.deegree.ogcbase.SortProperty;
055 import org.deegree.ogcwebservices.InvalidParameterValueException;
056 import org.deegree.ogcwebservices.MissingParameterValueException;
057 import org.deegree.ogcwebservices.OGCWebServiceException;
058 import org.deegree.ogcwebservices.OperationNotSupportedException;
059 import org.deegree.ogcwebservices.csw.AbstractCSWRequest;
060 import org.deegree.ogcwebservices.csw.CSWPropertiesAccess;
061 import org.w3c.dom.Document;
062 import org.w3c.dom.Element;
063
064 /**
065 * Class representation of a <code>GetRecords</code> request.
066 * <p>
067 * The primary means of resource discovery in the general model are the two operations search and present. In the HTTP
068 * protocol binding these are combined in the form of the mandatory <code>GetRecords</code> operation, which does a
069 * search.
070 * <p>
071 * Parameters specific to the <code>GetRecords</code> -request (omitting REQUEST, SERVICE and VERSION): <table
072 * border="1">
073 * <tr>
074 * <th>Name</th>
075 * <th>Occurences</th>
076 * <th>Function</th>
077 * </tr>
078 * <tr>
079 * <td align="center">NAMESPACE</td>
080 * <td align="center">0|1</td>
081 * <td>The NAMESPACE parameter is included in the KVP encoding to allow clients to bind any namespace prefixes that
082 * might be used for qualified names specified in other parameters. For example, the typeName parameter may include
083 * qualified names of the form namespace prefix:name. The value of the NAMESPACE parameter is a comma separated list of
084 * character strings of the form [namespace prefix:] namespace url. Not including the name namespace prefix binds the
085 * specified URL to the default namespace. As in XML, only one default namespace may be bound. This parameter is not
086 * required for the XML encoding since XML includes a mechanism for binding namespace prefixes.</td>
087 * </tr>
088 * <tr>
089 * <td align="center">resultType</td>
090 * <td align="center">0|1 (default: RESULTS)</td>
091 * <td>The resultType parameter may have the values HITS, RESULTS or VALIDATE and is used to indicate whether the
092 * catalogue service returns the full result set, the number of hits the query found or validates the request. If the
093 * resultType parameter is set to HITS, the catalogue service shall return an empty <GetRecordsResponse> element
094 * with the numberOfRecordsMatched attribute set to indicate the number of hits. The other attributes may be set to zero
095 * or not specified at all if they are optional. If the resultType parameter is set to HITS, then the values for the
096 * parameters outputFormat and outputSchema (if specified) shall be ignored since no actual records will be returned. If
097 * the resultType parameter is set to RESULTS, the catalogue service should generate a complete response with the
098 * <GetRecordsResponse> element containing the result set for the request. If the resultType parameter is set to
099 * VALIDATE, the catalogue service shall validate the request and return an empty <GetRecordsResponse>. All
100 * mandatory attributes may be given a value of zero and all optional attributes may be omitted. If the request does not
101 * validate then a service exception shall be raised as describe in Subclause 10.3.2.3.</td>
102 * </tr>
103 * <tr>
104 * <td align="center">outputFormat</td>
105 * <td align="center">0|1 (default: text/xml)</td>
106 * <td>The outputFormat parameter is used to control the format of the output that is generated in response to a
107 * GetRecords request. Its value must be a MIME type. The default value, "text/xml", means that the output shall be an
108 * XML document. All registries shall at least support XML as an output format. Other output formats may be supported
109 * and may include output formats such as TEXT (MIME type text/plain), or HTML (MIME type text/html). The list of output
110 * formats that a CSW instance provides must be advertised in the Capabilities document. In the case where the output
111 * format is text/xml, the CSW must generate an XML document that validates against a schema document that is specified
112 * in the output document via the xsi:schemaLocation attribute defined in XML.</td>
113 * </tr>
114 * <tr>
115 * <td align="center">outputSchema</td>
116 * <td align="center">0|1 (default: OGCCORE)</td>
117 * <td>The outputSchema parameter is used to indicate the schema of the output that is generated in response to a
118 * GetRecords request. The default value for this parameter shall be OGCCORE indicating that the schema for the core
119 * returnable properties (as defined in subclause 6.3.3) shall be used. Application profiles may define additional
120 * values for outputSchema and may redefine the default value but all profiles must support the value OGCCORE. Examples
121 * values for the outputSchema parameter might be FGDC, or ISO19119, ISO19139 or ANZLIC. The list of supported output
122 * schemas must be advertised in the capabilities document.</tr>
123 * <tr>
124 * <td align="center">startPosition</td>
125 * <td align="center">0|1 (default: 1)</td>
126 * <td>The startPosition paramater is used to indicate at which record position the catalogue should start generating
127 * output. The default value is 1 meaning it starts at the first record in the result set.</td>
128 * </tr>
129 * <tr>
130 * <td align="center">maxRecords</td>
131 * <td align="center">0|1 (default: 10)</td>
132 * <td>The maxRecords parameter is used to define the maximum number of records that should be returned from the result
133 * set of a query. If it is not specified, then 10 records shall be returned. If its value is set to zero, then the
134 * behavior is indentical to setting "resultType=HITS" as described above.</td>
135 * </tr>
136 * <tr>
137 * <td align="center">typeName</td>
138 * <td align="center">1</td>
139 * <td>The typeName parameter is a list of record type names that define a set of metadata record element names which
140 * will be constrained in the predicate of the query. In addition, all or some of the these names may be specified in
141 * the query to define which metadata record elements the query should present in the response to the GetRecords
142 * operation.</td>
143 * </tr>
144 * <tr>
145 * <td align="center">ElementSetName / ElementName</td>
146 * <td align="center">* (default: 10)</td>
147 * <td>The ElementName parameter is used to specify one or more metadata record elements that the query should present
148 * in the response to the a GetRecords operation. Well known sets of element may be named, in which case the
149 * ElementSetName parameter may be used (e.g.brief, summary or full). If neither parameter is specified, then a CSW
150 * shall present all metadata record elements. As mentioned above, if the outputFormat parameter is set to text/xml,
151 * then the response to the GetRecords operation shall validate against a schema document that is referenced in the
152 * response using the xmlns attributes. If the set of metadata record elements that the client specifies in the query in
153 * insufficient to generate a valid XML response document, a CSW may augment the list of elements presented to the
154 * client in order to be able to generate a valid document. Thus a client application should expect to receive more than
155 * the requested elements if the output format is set to XML. </td>
156 * </tr>
157 * <tr>
158 * <td align="center">CONSTRAINTLANGUAGE / Constraint</td>
159 * <td align="center">0|1</td>
160 * <td>Each request encoding (XML and KVP) has a specific mechanism for specifying the predicate language that will be
161 * used to constrain a query. In the XML encoding, the element <Constraint> is used to define the query predicate.
162 * The root element of the content of the <Constraint> element defines the predicate language that is being used.
163 * Two possible root elements are <ogc:Filter> for the OGC XML filter encoding, and <csw:CqlText> for a
164 * common query language string. An example predicate specification in the XML encoding is:
165 *
166 * <Constraint> <CqlText>prop1!=10</CqlText> </Constraint>
167 *
168 * In the KVP encoding, the parameter CONSTRAINTLANGUAGE is used to specify the predicate language being used. The
169 * Constraint parameter is used to specify the actual predicate. For example, to specify a CQL predicate, the following
170 * parameters would be set in the KVP encoding: <br>
171 *
172 * ...CONSTRAINTLANGUAGE=CQL_TEXT&CONSTRAINT="prop1!=10"...
173 *
174 * </td>
175 * </tr>
176 * <tr>
177 * <td align="center">SortBy</td>
178 * <td align="center">0|1</td>
179 * <td>The result set may be sorted by specifying one or more metadata record elements upon which to sort. In KVP
180 * encoding, the SORTBY parameter is used to specify the list of sort elements. The value for the SORTBY parameter is a
181 * comma-separated list of metadata record element names upon which to sort the result set. The format for each element
182 * in the list shall be either element name:A indicating that the element values should be sorted in ascending order or
183 * element name:D indicating that the element values should be sorted in descending order. For XML encoded requests, the
184 * <ogc:SortBy> element is used to specify a list of sort metadata record elements. The attribute sortOrder is
185 * used to specify the sort order for each element. Valid values for the sortOrder attribute are ASC indicating an
186 * ascending sort and DESC indicating a descending sort.</td>
187 * </tr>
188 * <tr>
189 * <td align="center">DistributedSearch / hopCount</td>
190 * <td align="center">0|1 (default: FALSE)</td>
191 * <td>The DistributedSearch parameter may be used to indicate that the query should be distributed. The default query
192 * behaviour, if the DistributedSearch parameter is set to FALSE (or is not specified at all), is to execute the query
193 * on the local server. In the XML encoding, if the <DistributedSearch> element is not specified then the query is
194 * executed on the local server. <br>
195 * <br>
196 * The hopCount parameter controls the distributed query behaviour by limiting the maximum number of message hops before
197 * the search is terminated. Each catalogue decrements this value by one when the request is received and does not
198 * propagate the request if the hopCount=0.</td>
199 * </tr>
200 * <tr>
201 * <td align="center">ResponseHandler</td>
202 * <td align="center">0|1</td>
203 * <td>The ResponseHandler parameter is a flag that indicates how the GetRecords operation should be processed by a
204 * CSW. If the parameter is not present, then the GetRecords operation is processed synchronously meaning that the
205 * client sends the GetRecords request to a CSW and waits to receive a valid response or exception message. The CSW
206 * immediately processes the GetRecords request while the client waits for a response. The problem with this mode of
207 * operation is that the client may timeout waiting for the CSW to process the request. If the ResponseHandler parameter
208 * is present, the GetRecords operation is processed asynchronously. In this case, the CSW responds immediately to a
209 * client's request with an acknowledgment message that tells the client that the request has been received and
210 * validated, and notification of completion will be sent to the URI specified as the value of the ResponseHandler
211 * parameter.</td>
212 * </tr>
213 * </table>
214 *
215 * @author <a href="mailto:mschneider@lat-lon.de">Markus Schneider </a>
216 * @version $Revision: 25619 $
217 *
218 *
219 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth </a>
220 * @author <a href="mailto:tfr@users.sourceforge.net">Torsten Friebe </a>
221 * @author <a href="mailto:mschneider@lat-lon.de">Markus Schneider </a>
222 *
223 * @author last edited by: $Author: apoth $
224 *
225 * @version $Revision: 25619 $, $Date: 2010-08-02 08:17:24 +0200 (Mo, 02. Aug 2010) $
226 */
227
228 public class GetRecords extends AbstractCSWRequest {
229
230 private static final long serialVersionUID = 2796229558893029054L;
231
232 private static final ILogger LOG = LoggerFactory.getLogger( GetRecords.class );
233
234 protected static final String DEFAULT_OUTPUTFORMAT = "application/xml";
235
236 protected static final String DEFAULT_OUTPUTSCHEMA = "csw:Record";
237
238 protected static final String DEFAULT_OUTPUTSCHEMA_202 = "http://www.opengis.net/cat/csw/2.0.2";
239
240 protected static final int DEFAULT_STARTPOSITION = 1;
241
242 protected static final int DEFAULT_MAX_RECORDS = 10;
243
244 protected static final int DEFAULT_HOPCOUNT = 2;
245
246 protected static final String DEFAULT_VERSION = "2.0.0";
247
248 /**
249 * defining HITS as String
250 */
251 public static String RESULT_TYPE_STRING_HITS = "hits";
252
253 /**
254 * defining VALIDATE as String
255 */
256 public static String RESULT_TYPE_STRING_VALIDATE = "validate";
257
258 /**
259 * defining RESULTS as String
260 */
261 public static String RESULT_TYPE_STRING_RESULTS = "results";
262
263 private static NamespaceContext nsContext = CommonNamespaces.getNamespaceContext();
264
265 private RESULT_TYPE resultType = RESULT_TYPE.RESULTS;
266
267 // keys are Strings (namespace prefix or "" for default namespace), values
268 // are URIs
269 private Map<String, URI> namespace;
270
271 private String outputFormat;
272
273 private String outputSchema;
274
275 private int startPosition;
276
277 private int maxRecords;
278
279 private int hopCount;
280
281 private URI responseHandler;
282
283 // private Query[] queries;
284
285 private Query query;
286
287 /**
288 * Creates a new <code>GetRecords</code> instance.
289 *
290 * @param id
291 * @param version
292 * @param vendorSpecificParameters
293 * @param namespace
294 * @param resultType
295 * @param outputFormat
296 * @param outputSchema
297 * @param startPosition
298 * @param maxRecords
299 * @param hopCount
300 * @param responseHandler
301 * @param query
302 */
303 public GetRecords( String id, String version, Map<String, String> vendorSpecificParameters,
304 Map<String, URI> namespace, RESULT_TYPE resultType, String outputFormat, String outputSchema,
305 int startPosition, int maxRecords, int hopCount, URI responseHandler, Query query ) {
306 super( version, id, vendorSpecificParameters );
307 this.namespace = namespace;
308 this.resultType = resultType;
309 this.outputFormat = outputFormat;
310 this.outputSchema = outputSchema;
311 this.startPosition = startPosition;
312 this.maxRecords = maxRecords;
313 this.hopCount = hopCount;
314 this.responseHandler = responseHandler;
315 this.query = query;
316 }
317
318 /**
319 * creates a GetRecords request from the XML fragment passed. The passed element must be valid against the OGC CSW
320 * 2.0 GetRecords schema.
321 *
322 * TODO respect namespaces (use QualifiedNames) for type names
323 *
324 * @param id
325 * unique ID of the request
326 * @param root
327 * root element of the GetRecors request
328 * @return a GetRecords instance with given id and parsed values from the root element
329 * @throws MissingParameterValueException
330 * if a required parameter was not set
331 * @throws InvalidParameterValueException
332 * if a parameter is invalid
333 * @throws OGCWebServiceException
334 * if something went wrong while creating the Request
335 */
336 public static GetRecords create( String id, Element root )
337 throws MissingParameterValueException, InvalidParameterValueException,
338 OGCWebServiceException {
339 String version = null;
340 try {
341 // first try to read verdsion attribute which is optional for CSW 2.0.0 and 2.0.1
342 version = XMLTools.getNodeAsString( root, "./@version", nsContext, null );
343 } catch ( XMLParsingException e ) {
344 // default version?
345 }
346 if ( version == null ) {
347 // if no version attribute has been set try mapping namespace URI to a version;
348 // this is not well defined for 2.0.0 and 2.0.1 which uses the same namespace.
349 // in this case 2.0.0 will be returned!
350 version = CSWPropertiesAccess.getString( root.getNamespaceURI() );
351 }
352
353 // read class for version depenging parsing of GetRecords request from properties
354 String className = CSWPropertiesAccess.getString( "GetRecords" + version );
355 Class<?> clzz = null;
356 try {
357 clzz = Class.forName( className );
358 } catch ( ClassNotFoundException e ) {
359 LOG.logError( e.getMessage(), e );
360 throw new InvalidParameterValueException( e.getMessage(), e );
361 }
362 GetRecordsDocument document = null;
363 try {
364 document = (GetRecordsDocument) clzz.newInstance();
365 } catch ( InstantiationException e ) {
366 LOG.logError( e.getMessage(), e );
367 throw new InvalidParameterValueException( e.getMessage(), e );
368 } catch ( IllegalAccessException e ) {
369 LOG.logError( e.getMessage(), e );
370 throw new InvalidParameterValueException( e.getMessage(), e );
371 }
372
373 document.setRootElement( root );
374
375 GetRecords ogcRequest = document.parse( id );
376
377 return ogcRequest;
378 }
379
380 /**
381 * Creates a new <code>GetRecords</code> instance from the values stored in the submitted Map. Keys (parameter
382 * names) in the Map must be uppercase.
383 *
384 * @TODO evaluate vendorSpecificParameter
385 *
386 * @param kvp
387 * Map containing the parameters
388 * @return a GetRecords instance with given id and values from the kvp
389 * @exception InvalidParameterValueException
390 * @exception MissingParameterValueException
391 * @throws OperationNotSupportedException
392 * if an CQL_TEXT constrain is requested
393 */
394 public static GetRecords create( Map<String, String> kvp )
395 throws InvalidParameterValueException, MissingParameterValueException,
396 OperationNotSupportedException {
397
398 // String version = "2.0.0";
399 // Map<String, String> vendorSpecificParameters = null;
400 // RESULT_TYPE resultType = RESULT_TYPE.HITS;
401 // String outputFormat = "text/xml";
402 // String outputSchema = "OGCCORE";
403 // int startPosition = 1;
404 // int maxRecords = 10;
405 // int hopCount = 2;
406
407 String service = getParam( "SERVICE", kvp, "CSW" );
408 if ( !"CSW".equals( service ) ) {
409 throw new InvalidParameterValueException( "GetRecordDocument",
410 Messages.getMessage( "CSW_INVALID_SERVICE_PARAM" ),
411 ExceptionCode.INVALIDPARAMETERVALUE );
412 }
413
414 String id = getParam( "ID", kvp, "" );
415 LOG.logDebug( "GetRecordRequest id=" + id );
416
417 String version = getParam( "VERSION", kvp, DEFAULT_VERSION );
418 if ( !( DEFAULT_VERSION.equals( version ) || "2.0.1".equals( version ) || "2.0.2".equals( version ) ) ) {
419 throw new InvalidParameterValueException( "GetRecords", Messages.getMessage( "CSW_NOT_SUPPORTED_VERSION",
420 GetRecords.DEFAULT_VERSION,
421 "2.0.1", "2.0.2", version ),
422 ExceptionCode.INVALIDPARAMETERVALUE );
423 }
424
425 // extract namespace mappings
426 Map<String, URI> namespaceMappings = getNSMappings( getParam( "NAMESPACE", kvp, null ) );
427
428 String resultTypeString = getParam( "RESULTTYPE", kvp, RESULT_TYPE_STRING_HITS );
429 RESULT_TYPE resultType = RESULT_TYPE.HITS;
430 if ( RESULT_TYPE_STRING_HITS.equalsIgnoreCase( resultTypeString ) ) {
431 resultType = RESULT_TYPE.HITS;
432 } else if ( RESULT_TYPE_STRING_RESULTS.equalsIgnoreCase( resultTypeString ) ) {
433 resultType = RESULT_TYPE.RESULTS;
434 } else if ( RESULT_TYPE_STRING_VALIDATE.equalsIgnoreCase( resultTypeString ) ) {
435 resultType = RESULT_TYPE.VALIDATE;
436 } else {
437 throw new InvalidParameterValueException( "GetRecords",
438 Messages.getMessage( "CSW_INVALID_RESULTTYPE", resultTypeString,
439 GetRecords.RESULT_TYPE_STRING_HITS,
440 GetRecords.RESULT_TYPE_STRING_RESULTS,
441 GetRecords.RESULT_TYPE_STRING_VALIDATE ),
442 ExceptionCode.INVALIDPARAMETERVALUE );
443 }
444
445 String outputFormat = getParam( "OUTPUTFORMAT", kvp, DEFAULT_OUTPUTFORMAT );
446 String defaultOutputSchema = DEFAULT_OUTPUTSCHEMA;
447 if ( version.equals( "2.0.2" ) ) {
448 defaultOutputSchema = DEFAULT_OUTPUTSCHEMA_202;
449 }
450 String outputSchema = getParam( "OUTPUTSCHEMA", kvp, defaultOutputSchema );
451 int startPosition = getParamAsInt( "STARTPOSITION", kvp, DEFAULT_STARTPOSITION );
452 if ( startPosition < 1 ) {
453 String msg = Messages.getMessage( "CSW_INVALID_STARTPOSITION", new Integer( startPosition ) );
454 throw new InvalidParameterValueException( msg );
455 }
456 int maxRecords = getParamAsInt( "MAXRECORDS", kvp, DEFAULT_MAX_RECORDS );
457
458 if ( maxRecords < 0 ) {
459 maxRecords = DEFAULT_MAX_RECORDS;
460 }
461
462 // build one Query object for each specified typeName
463 String tmp = getRequiredParam( "TYPENAMES", kvp );
464 String[] typeNames = StringTools.toArray( tmp, ",", false );
465 if ( typeNames.length == 0 ) {
466 throw new MissingParameterValueException( "Mandatory parameter 'TYPENAMES' is missing!" );
467 }
468
469 String elementSetName = kvp.remove( "ELEMENTSETNAME" );
470 String elementName = kvp.remove( "ELEMENTNAME" );
471 String[] elementNames = null;
472
473 if ( version.equals( "2.0.2" ) ) {
474 if ( elementSetName == null ) {
475 elementSetName = "summary";
476 } else {
477 if ( elementName != null ) {
478 LOG.logInfo( Messages.getMessage( "CSW_ELEMENT_SET_NAME_DUPLICATE" ) );
479 } else {
480 elementNames = StringTools.toArray( elementName, ",", false );
481 }
482 }
483 } else {
484
485 if ( elementSetName == null ) {
486 elementSetName = kvp.remove( "ELEMENTNAME" );
487 } else {
488 String test = kvp.remove( "ELEMENTNAME" );
489 if ( test != null ) {
490 LOG.logInfo( Messages.getMessage( "CSW_ELEMENT_SET_NAME_DUPLICATE" ) );
491 }
492 }
493
494 if ( elementSetName != null ) {
495 elementNames = StringTools.toArray( elementSetName, ",", false );
496 if ( elementNames.length == 0 ) {
497 elementNames = null;
498 }
499 }
500 if ( elementNames == null ) {
501 elementNames = new String[] { "full" };
502 }
503
504 }
505
506 String constraintString = kvp.remove( "CONSTRAINT" );
507 if ( constraintString == null ) {
508 // not really clear if CSW 2.0.2 uses parameter QUERYCONSTRAINT instead
509 constraintString = kvp.remove( "QUERYCONSTRAINT" );
510 }
511 Filter constraint = null;
512 String constraintLanguage = null;
513 String cnstrntVersion = null;
514 if ( constraintString != null ) {
515 // build Filter object (from CONSTRAINT parameter)
516 constraintLanguage = kvp.remove( "CONSTRAINTLANGUAGE" );
517 if ( constraintLanguage != null ) {
518 if ( "CQL_TEXT".equalsIgnoreCase( constraintLanguage.trim() ) ) {
519 throw new OperationNotSupportedException( Messages.getMessage( "CSW_NO_CQL_IMPLEMENTATION" ) );
520 } else if ( !"FILTER".equalsIgnoreCase( constraintLanguage.trim() ) ) {
521 throw new InvalidParameterValueException( Messages.getMessage( "CSW_INVALID_CONSTRAINT_LANGUAGE",
522 constraintLanguage.trim() ) );
523 }
524 } else {
525 throw new InvalidParameterValueException( Messages.getMessage( "CSW_CQL_NOR_FILTER_KVP" ) );
526 }
527 cnstrntVersion = kvp.remove( "CONSTRAINT_LANGUAGE_VERSION" );
528 if ( "2.0.2".equals( version ) && cnstrntVersion == null ) {
529 throw new InvalidParameterValueException(
530 Messages.getMessage( "CSW_MISSING_CONSTRAINT_LANGUAGE_VERSION" ) );
531 }
532
533 try {
534 Document doc = XMLTools.parse( new StringReader( constraintString ) );
535 Element element = doc.getDocumentElement();
536 constraint = AbstractFilter.buildFromDOM( element, "1.0.0".equals( cnstrntVersion ) );
537 } catch ( Exception e ) {
538 String msg = "An error occured when parsing the 'CONSTRAINT' parameter " + "Filter expression: "
539 + e.getMessage();
540 throw new InvalidParameterValueException( msg );
541 }
542 }
543
544 SortProperty[] sortProperties = SortProperty.create( kvp.remove( "SORTBY" ), namespaceMappings );
545
546 // Query[] queries = new Query[typeNames.length];
547 // for ( int i = 0; i < typeNames.length; i++ ) {
548 Query query = new Query( elementSetName, elementNames, constraint, sortProperties, typeNames );
549 // }
550
551 // find out if the query should be performed locally or in a distributed
552 // fashion
553 int hopCount = DEFAULT_HOPCOUNT;
554 String distributedSearch = getParam( "DISTRIBUTEDSEARCH", kvp, "false" );
555 if ( distributedSearch.equalsIgnoreCase( "true" ) ) {
556 hopCount = getParamAsInt( "HOPCOUNT", kvp, DEFAULT_HOPCOUNT );
557 }
558
559 String rHandler = kvp.remove( "RESPONSEHANDLER" );
560 URI responseHandler = null;
561 if ( rHandler != null ) {
562 try {
563 responseHandler = new URI( rHandler );
564 } catch ( URISyntaxException e ) {
565 throw new InvalidParameterValueException(
566 Messages.getMessage( "CSW_INVALID_RESPONSE_HANDLER", rHandler ) );
567 }
568 throw new OperationNotSupportedException( Messages.getMessage( "CSW_NO_REPONSE_HANDLER_IMPLEMENTATION" ) );
569
570 }
571
572 return new GetRecords( id, version, kvp, namespaceMappings, resultType, outputFormat, outputSchema,
573 startPosition, maxRecords, hopCount, responseHandler, query );
574 }
575
576 /**
577 * Used to specify a namespace and its prefix. Format must be [ <prefix>:] <url>. If the prefix is not specified
578 * then this is the default namespace
579 * <p>
580 * Zero or one (Optional) ; Include value for each distinct namespace used by all qualified names in the request. If
581 * not included, all qualified names are in default namespace
582 * <p>
583 * The NAMESPACE parameter is included in the KVP encoding to allow clients to bind any namespace prefixes that
584 * might be used for qualified names specified in other parameters. For example, the typeName parameter may include
585 * qualified names of the form namespace prefix:name.
586 * <p>
587 * The value of the NAMESPACE parameter is separated list of character strings of the form [namespace
588 * prefix:]namespace url. Not including the name namespace prefix binds the specified URL to the default namespace.
589 * As in XML, only one default namespace may be bound.
590 *
591 * @return the mapped namespaces or <code>null</code> if all qualified names are in default namespace.
592 *
593 */
594 public Map<String, URI> getNamespace() {
595 return this.namespace;
596 }
597
598 /**
599 * The resultType parameter may have the values HITS, RESULTS or VALIDATE and is used to indicate whether the
600 * catalogue service returns the full result set, the number of hits the query found or validates the request.
601 * <p>
602 * If the resultType parameter is set to HITS, the catalogue service shall return an empty
603 * <GetRecordsResponse>element with the numberOfRecordsMatched attribute set to indicate the number of hits.
604 * The other attributes may be set to zero or not specified at all if they are optional.
605 * <p>
606 * If the resultType parameter is set to HITS, then the values for the parameters outputFormat and outputSchema (if
607 * specified) shall be ignored since no actual records will be returned
608 * <p>
609 * If the resultType parameter is set to RESULTS, the catalogue service should generate a complete response with the
610 * <GetRecordsResponse>element containing the result set for the request
611 * <p>
612 * If the resultType parameter is set to VALIDATE, the catalogue service shall validate the request and return an
613 * empty <GetRecordsResponse>. All mandatory attributes may be given a value of zero and all optional
614 * attributes may be omitted. If the request does not validate then a service exception shall be raised
615 *
616 * @return one of HITS, RESULTS or VALIDATE
617 *
618 */
619 public RESULT_TYPE getResultType() {
620 return this.resultType;
621 }
622
623 /**
624 * The resultType parameter may have the values HITS, RESULTS or VALIDATE and is used to indicate whether the
625 * catalogue service returns the full result set, the number of hits the query found or validates the request.
626 * <p>
627 * If the resultType parameter is set to HITS, the catalogue service shall return an empty
628 * <GetRecordsResponse>element with the numberOfRecordsMatched attribute set to indicate the number of hits.
629 * The other attributes may be set to zero or not specified at all if they are optional.
630 * <p>
631 * If the resultType parameter is set to HITS, then the values for the parameters outputFormat and outputSchema (if
632 * specified) shall be ignored since no actual records will be returned
633 * <p>
634 * If the resultType parameter is set to RESULTS, the catalogue service should generate a complete response with the
635 * <GetRecordsResponse>element containing the result set for the request
636 * <p>
637 * If the resultType parameter is set to VALIDATE, the catalogue service shall validate the request and return an
638 * empty <GetRecordsResponse>. All mandatory attributes may be given a value of zero and all optional
639 * attributes may be omitted. If the request does not validate then a service exception shall be raised
640 *
641 * @return the resulttype as a String, one of "HITS", "VALIDATE" or "RESULTS"
642 *
643 */
644 public String getResultTypeAsString() {
645 String resultTypeString = null;
646 switch ( this.resultType ) {
647 case HITS: {
648 resultTypeString = RESULT_TYPE_STRING_HITS;
649 break;
650 }
651 case RESULTS: {
652 resultTypeString = RESULT_TYPE_STRING_RESULTS;
653 break;
654 }
655 case VALIDATE: {
656 resultTypeString = RESULT_TYPE_STRING_VALIDATE;
657 break;
658 }
659 }
660 return resultTypeString;
661 }
662
663 /**
664 * sets the resultType of a request. This may be useful to perform a request first with resultType = HITS to
665 * determine the total number of records matching a query and afterwards performing the same request with resultType =
666 * RESULTS (and maxRecords < number of matched records).
667 *
668 * @param resultType
669 */
670 public void setResultType( RESULT_TYPE resultType ) {
671 this.resultType = resultType;
672 }
673
674 /**
675 * returns <= 0 if no distributed search shall be performed. otherwise the recursion depht is returned.
676 * <p>
677 * The hopCount parameter controls the distributed query behaviour by limiting the maximum number of message hops
678 * before the search is terminated. Each catalogue decrements this value by one when the request is received and
679 * does not propagate the request if the hopCount=0
680 *
681 * @return <= 0 if no distributed search shall be performed. otherwise the recursion depht is returned.
682 *
683 */
684 public int getHopCount() {
685 return this.hopCount;
686 }
687
688 /**
689 * Value is Mime type;The only value that must be supported is text/xml. Other suppored values may include text/html
690 * and text/plain
691 * <p>
692 * The outputFormat parameter is used to control the format of the output that is generated in response to a
693 * GetRecords request. Its value must be a MIME type. The default value, "text/xml", means that the output shall be
694 * an XML document. All registries shall at least support XML as an output format. Other output formats may be
695 * supported and may include output formats such as TEXT (MIME type text/plain), or HTML (MIME type text/html). The
696 * list of output formats that a CSW instance provides must be advertised in the Capabilities document
697 * <p>
698 * In the case where the output format is text/xml, the CSW must generate an XML document that validates against a
699 * schema document that is specified in the output document via the xsi:schemaLocation attribute defined in XML
700 *
701 * @return Value is a Mime type
702 *
703 */
704 public String getOutputFormat() {
705 return this.outputFormat;
706 }
707
708 /**
709 * The outputSchema parameter is used to indicate the schema of the output that is generated in response to a
710 * GetRecords request. The default value for this parameter shall be OGCCORE indicating that the schema for the core
711 * returnable properties shall be used. Application profiles may define additional values for outputSchema and may
712 * redefine the default value but all profiles must support the value OGCCORE
713 * <p>
714 * Examples values for the outputSchema parameter might be FGDC, or ISO19119, ISO19139 or ANZLIC. The list of
715 * supported output schemas must be advertised in the capabilities document
716 *
717 * @return The default value for this parameter shall be OGCCORE
718 *
719 */
720 public String getOutputSchema() {
721 return this.outputSchema;
722 }
723
724 /**
725 * @return the number of the first returned dataset. Zero or one (Optional)Default value is 1. If startPosition >
726 * the number of datasets satisfying the constraint, no dataset will be returned
727 *
728 */
729 public int getStartPosition() {
730 return this.startPosition;
731 }
732
733 /**
734 * @return The maxRecords parameter. It is used to define the maximum number of records that should be returned from
735 * the result set of a query. If it is not specified, then 10 records shall be returned. If its value is set
736 * to zero, then the behavior is indentical to setting "resultType=HITS"
737 *
738 */
739 public int getMaxRecords() {
740 return this.maxRecords;
741 }
742
743 /**
744 * @return the location of a response adress to which an asynchronous result may be sent.
745 */
746 public URI getResponseHandler() {
747 return responseHandler;
748 }
749
750 /**
751 * @return the query object.
752 */
753 public Query getQuery() {
754 return query;
755 }
756
757 /**
758 * @see #getQuery()
759 * @param query
760 */
761 public void setQuery( Query query ) {
762 this.query = query;
763 }
764
765 /**
766 * The <code>RESULT_TYPE</code> a simple enum which defines some result values of a GetRecord.
767 *
768 * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a>
769 *
770 * @author last edited by: $Author: apoth $
771 *
772 * @version $Revision: 25619 $, $Date: 2010-08-02 08:17:24 +0200 (Mo, 02. Aug 2010) $
773 *
774 */
775
776 public static enum RESULT_TYPE {
777 /**
778 * HITS, the catalogue service shall return an empty <GetRecordsResponse>element with the
779 * numberOfRecordsMatched attribute set to indicate the number of hits
780 */
781 HITS,
782 /**
783 * VALIDATE, the catalogue service shall validate the request
784 */
785 VALIDATE,
786 /**
787 * RESULTS, the catalogue service should generate a complete response with the <GetRecordsResponse>element
788 * containing the result set for the request
789 */
790 RESULTS
791 }
792 }