001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/tags/2.1/src/org/deegree/ogcwebservices/csw/discovery/Discovery.java $
002 /*---------------- FILE HEADER ------------------------------------------
003
004 This file is part of deegree.
005 Copyright (C) 2001-2006 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 Aennchenstr. 19
030 53115 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 ---------------------------------------------------------------------------*/
044 package org.deegree.ogcwebservices.csw.discovery;
045
046 import java.io.ByteArrayInputStream;
047 import java.io.ByteArrayOutputStream;
048 import java.io.IOException;
049 import java.io.StringWriter;
050 import java.net.URI;
051 import java.net.URISyntaxException;
052 import java.net.URL;
053 import java.util.HashMap;
054 import java.util.Iterator;
055 import java.util.List;
056 import java.util.Map;
057
058 import javax.xml.transform.TransformerException;
059
060 import org.deegree.datatypes.QualifiedName;
061 import org.deegree.enterprise.servlet.OGCServletController;
062 import org.deegree.framework.log.ILogger;
063 import org.deegree.framework.log.LoggerFactory;
064 import org.deegree.framework.util.FileUtils;
065 import org.deegree.framework.util.StringTools;
066 import org.deegree.framework.util.TimeTools;
067 import org.deegree.framework.xml.XMLFragment;
068 import org.deegree.framework.xml.XSLTDocument;
069 import org.deegree.framework.xml.schema.XSDocument;
070 import org.deegree.i18n.Messages;
071 import org.deegree.model.feature.FeatureCollection;
072 import org.deegree.model.feature.FeatureException;
073 import org.deegree.model.feature.GMLFeatureAdapter;
074 import org.deegree.ogcbase.ExceptionCode;
075 import org.deegree.ogcwebservices.InvalidParameterValueException;
076 import org.deegree.ogcwebservices.OGCWebServiceException;
077 import org.deegree.ogcwebservices.csw.capabilities.CatalogueOperationsMetadata;
078 import org.deegree.ogcwebservices.csw.configuration.CatalogueConfiguration;
079 import org.deegree.ogcwebservices.csw.configuration.CatalogueConfigurationDocument;
080 import org.deegree.ogcwebservices.csw.configuration.CatalogueOutputSchemaParameter;
081 import org.deegree.ogcwebservices.csw.configuration.CatalogueOutputSchemaValue;
082 import org.deegree.ogcwebservices.csw.configuration.CatalogueTypeNameSchemaParameter;
083 import org.deegree.ogcwebservices.csw.configuration.CatalogueTypeNameSchemaValue;
084 import org.deegree.ogcwebservices.csw.discovery.GetRecords.RESULT_TYPE;
085 import org.deegree.ogcwebservices.wfs.WFService;
086 import org.deegree.ogcwebservices.wfs.capabilities.WFSCapabilities;
087 import org.deegree.ogcwebservices.wfs.operation.FeatureResult;
088 import org.deegree.ogcwebservices.wfs.operation.GetFeature;
089
090 /**
091 * The Discovery class allows clients to discover resources registered in a catalogue, by providing four operations
092 * named <code>query</code>,<code>present</code>, <code>describeRecordType</code>, and <code>getDomain</code>.
093 * This class has a required association from the Catalogue Service class, and is thus always implemented by all
094 * Catalogue Service implementations. The Session class can be included with the Discovery class, in associations with
095 * the Catalogue Service class. The "e;query"e; and "e;present"e; operations may be executed in a
096 * session or stateful context. If a session context exists, the dynamic model uses internal states of the session and
097 * the allowed transitions between states. When the "e;query"e; and "e;present"e; state does not include
098 * a session between a server and a client, any memory or shared information between the client and the server may be
099 * based on private understandings or features available in the protocol binding. The describeRecordType and getDomain
100 * operations do not require a session context.
101 *
102 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth </a>
103 * @author <a href="mailto:tfr@users.sourceforge.net">Torsten Friebe </a>
104 *
105 * @author last edited by: $Author: rbezema $
106 *
107 * @version $Revision: 7571 $, $Date: 2007-06-14 13:56:27 +0200 (Do, 14 Jun 2007) $
108 *
109 */
110 public class Discovery {
111
112 private static final ILogger LOG = LoggerFactory.getLogger( Discovery.class );
113
114 // Keys are Strings, values are XSLDocuments
115 private static final Map<String, XSLTDocument> IN_XSL = new HashMap<String, XSLTDocument>();
116
117 // Keys are Strings, values are XSLDocuments
118 private static final Map<String, XSLTDocument> OUT_XSL = new HashMap<String, XSLTDocument>();
119
120 // Keys are Strings, values are URLs
121 private static final Map<String, URL> SCHEMA_URLS = new HashMap<String, URL>();
122
123 // Keys are Strings, values are XMLFragments
124 private static final Map<String, XSDocument> SCHEMA_DOCS = new HashMap<String, XSDocument>();
125
126 private static final String DEFAULT_SCHEMA = "DublinCore";
127
128 private static final String OGC_CORE_SCHEMA = "OGCCORE";
129
130 private CatalogueConfiguration cswConfiguration = null;
131
132 /**
133 * The complete data access of a catalog service is managed by one instances of WFService.
134 */
135 private WFService wfsResource; // single instance only for this CSW
136
137 /**
138 * @param wfsService
139 * to contact
140 * @param cswConfiguration
141 * of this service
142 */
143 public Discovery( WFService wfsService, CatalogueConfiguration cswConfiguration ) {
144 this.wfsResource = wfsService;
145 this.cswConfiguration = cswConfiguration;
146 try {
147 CatalogueOperationsMetadata catalogMetadata = (CatalogueOperationsMetadata) cswConfiguration.getOperationsMetadata();
148 CatalogueOutputSchemaParameter outputSchemaParameter = (CatalogueOutputSchemaParameter) catalogMetadata.getGetRecords()
149 .getParameter( "outputSchema" );
150
151 CatalogueConfigurationDocument document = new CatalogueConfigurationDocument();
152 document.setSystemId( cswConfiguration.getSystemId() );
153 CatalogueOutputSchemaValue[] values = outputSchemaParameter.getSpecializedValues();
154 for ( int i = 0; i < values.length; i++ ) {
155 CatalogueOutputSchemaValue value = values[i];
156 String schemaName = value.getValue().toUpperCase();
157
158 URL fileURL = document.resolve( value.getInXsl() );
159 LOG.logInfo( StringTools.concat( 300,
160 "Input schema '",
161 schemaName,
162 "' is processed using XSLT-sheet from URL '",
163 fileURL,
164 "'" ) );
165 XSLTDocument inXSLSheet = new XSLTDocument();
166 inXSLSheet.load( fileURL );
167 IN_XSL.put( schemaName, inXSLSheet );
168
169 fileURL = document.resolve( value.getOutXsl() );
170 LOG.logInfo( StringTools.concat( 300,
171 "Output schema '",
172 schemaName,
173 "' is processed using XSLT-sheet from URL '",
174 fileURL,
175 "'" ) );
176 XSLTDocument outXSLSheet = new XSLTDocument();
177 outXSLSheet.load( fileURL );
178 OUT_XSL.put( schemaName, outXSLSheet );
179
180 }
181
182 // read and store schema definitions
183 // each type(Name) provided by a CS-W is assigned to one schema
184 CatalogueTypeNameSchemaParameter outputTypeNameParameter = (CatalogueTypeNameSchemaParameter) catalogMetadata.getGetRecords()
185 .getParameter( "typeName" );
186 CatalogueTypeNameSchemaValue[] tn_values = outputTypeNameParameter.getSpecializedValues();
187 for ( int i = 0; i < tn_values.length; i++ ) {
188 CatalogueTypeNameSchemaValue value = tn_values[i];
189 URL fileURL = document.resolve( value.getSchema() );
190 XSDocument schemaDoc = new XSDocument();
191 schemaDoc.load( fileURL );
192 String typeName = value.getValue().toUpperCase();
193 LOG.logInfo( StringTools.concat( 300,
194 "Schema for type '",
195 typeName,
196 "' is defined in XSD-file at URL '",
197 fileURL,
198 "'" ) );
199 SCHEMA_URLS.put( typeName, fileURL );
200 SCHEMA_DOCS.put( typeName, schemaDoc );
201 }
202 } catch ( Exception e ) {
203 e.printStackTrace();
204 LOG.logError( "Error while creating CSW Discovery: " + e.getMessage(), e );
205 }
206 WFSCapabilities capa = wfsResource.getCapabilities();
207 LOG.logInfo( "CSW Discovery initialized with WFS resource, wfs version: " + capa.getVersion() );
208 }
209
210 /**
211 * Performs the submitted <code>DescribeRecord</code> -request.
212 *
213 * TODO: Check output schema & Co.
214 *
215 * @param request
216 * @return The DescribeRecordResult created from the given request
217 * @throws OGCWebServiceException
218 */
219 public DescribeRecordResult describeRecordType( DescribeRecord request ) throws OGCWebServiceException {
220
221 // requested output format must be 'text/xml'
222 if ( !( "text/xml".equals( request.getOutputFormat() ) || "application/xml".equals( request.getOutputFormat() ) ) ) {
223 String s = Messages.getMessage( "CSW_DESCRIBERECORD_INVALID_FORMAT", request.getOutputFormat() );
224 throw new OGCWebServiceException( getClass().getName(), s, ExceptionCode.INVALID_FORMAT );
225 }
226
227 // requested schema language must be 'XMLSCHEMA'
228 if ( !( "XMLSCHEMA".equals( request.getSchemaLanguage().toString() ) || "http://www.w3.org/XML/Schema".equals( request.getSchemaLanguage().toString() ) ) ) {
229 String s = Messages.getMessage( "CSW_DESCRIBERECORD_INVALID_SCHEMA", request.getSchemaLanguage() );
230 throw new InvalidParameterValueException( s );
231 }
232
233 // if no type names are specified, describe all known types
234 String[] typeNames = request.getTypeNames();
235 if ( typeNames == null || typeNames.length == 0 ) {
236 typeNames = SCHEMA_DOCS.keySet().toArray( new String[SCHEMA_DOCS.keySet().size()] );
237 }
238
239 SchemaComponent[] schemaComponents = new SchemaComponent[typeNames.length];
240
241 for ( int i = 0; i < typeNames.length; i++ ) {
242 XSDocument doc = SCHEMA_DOCS.get( typeNames[i].toUpperCase() );
243 if ( doc == null ) {
244 LOG.logDebug( "Discovery.describeRecord, no key found for: " + typeNames[i].toUpperCase() + " trying again with added 'RIM:' prefix");
245 doc = SCHEMA_DOCS.get( "RIM:"+typeNames[i].toUpperCase() );
246 }
247 if( doc == null ){
248 String msg = Messages.getMessage( "CSW_DESCRIBERECORD_UNSUPPORTED_TN", typeNames[i] );
249 throw new OGCWebServiceException( getClass().getName(), msg );
250 }
251 try {
252 schemaComponents[i] = new SchemaComponent( doc,
253 new URI( "http://www.deegree.org/csw" ),
254 null,
255 new URI( "XMLSCHEMA" ) );
256 } catch ( URISyntaxException e ) {
257 throw new OGCWebServiceException( this.getClass().getName(), e.getMessage() );
258 }
259 }
260
261 return new DescribeRecordResult( request, "2.0.0", schemaComponents );
262 }
263
264 /**
265 * @param request
266 * which is not handled
267 * @return just a new empty DomainValues instance.
268 * @todo not implemented, yet
269 */
270 public DomainValues getDomain( @SuppressWarnings("unused")
271 GetDomain request ) {
272 return new DomainValues();
273 }
274
275 private String normalizeOutputSchema( String outputSchema ) throws InvalidParameterValueException {
276 LOG.logDebug( "Normalizing following outputschema: " + outputSchema );
277 if ( outputSchema == null ) {
278 LOG.logDebug( "Setting the outputSchema to: " + DEFAULT_SCHEMA);
279 outputSchema = DEFAULT_SCHEMA;
280 } else if ( outputSchema.equalsIgnoreCase( OGC_CORE_SCHEMA ) ) {
281 LOG.logDebug( "Setting the outputSchema to: " + DEFAULT_SCHEMA);
282 outputSchema = DEFAULT_SCHEMA;
283 }
284 outputSchema = outputSchema.toUpperCase();
285 if ( IN_XSL.get( outputSchema ) == null ) {
286 String msg = "Unsupported output schema '" + outputSchema + "' requested. Supported schemas are: ";
287 Iterator it = IN_XSL.keySet().iterator();
288 while ( it.hasNext() ) {
289 msg += (String) it.next();
290 if ( it.hasNext() ) {
291 msg += ", ";
292 } else {
293 msg += ".";
294 }
295 }
296 throw new InvalidParameterValueException( msg );
297 }
298 return outputSchema;
299 }
300
301 /**
302 * Performs a <code>GetRecords</code> request.
303 * <p>
304 * This involves the following steps:
305 * <ul>
306 * <li><code>GetRecords</code>-><code>GetRecordsDocument</code></li>
307 * <li><code>GetRecordsDocument</code>-><code>GetFeatureDocument</code> using XSLT</li>
308 * <li><code>GetFeatureDocument</code>-><code>GetFeature</code></li>
309 * <li><code>GetFeature</code> request is performed against the underlying WFS</li>
310 * <li>WFS answers with a <code>FeatureResult</code> object (which contains a <code>FeatureCollection</code>)</li>
311 * <li><code>FeatureCollection</code>-> GMLFeatureCollectionDocument (as a String)</li>
312 * <li>GMLFeatureCollectionDocument</code>-><code>GetRecordsResultDocument</code> using XSLT</li>
313 * <li><code>GetRecordsResultDocument</code>-><code>GetRecordsResult</code></li>
314 * </ul>
315 * </p>
316 *
317 * @param getRecords
318 * @return GetRecordsResult
319 * @throws OGCWebServiceException
320 */
321 public GetRecordsResult query( GetRecords getRecords ) throws OGCWebServiceException {
322 GetFeature getFeature = null;
323 XMLFragment getFeatureDocument = null;
324 Object wfsResponse = null;
325 GetRecordsResult cswResponse = null;
326 String outputSchema = normalizeOutputSchema( getRecords.getOutputSchema() );
327
328 // TODO remove this (only necessary because determineRecordsMatched changes the resultType)
329 String resultType = getRecords.getResultTypeAsString();
330
331 XMLFragment getRecordsDocument = new XMLFragment( XMLFactory.export( getRecords ).getRootElement() );
332 LOG.logDebug( "Input GetRecords request:\n" + getRecordsDocument.getAsPrettyString() );
333 try {
334 // incoming GetRecord request must be transformed to a GetFeature
335 // request because the underlying 'data engine' of the CSW is a WFS
336 XSLTDocument xslSheet = IN_XSL.get( outputSchema );
337 synchronized ( xslSheet ) {
338 getFeatureDocument = xslSheet.transform( getRecordsDocument );
339 LOG.logDebug( "*****First Generated WFS GetFeature request:\n" + getFeatureDocument.getAsPrettyString() );
340 xslSheet.notifyAll();
341 }
342
343
344 } catch ( TransformerException e ) {
345 e.printStackTrace();
346 String msg = "Can't transform GetRecord request to WFS GetFeature request: " + e.getMessage();
347 LOG.logError( msg, e );
348 throw new OGCWebServiceException( msg );
349 }
350
351 // if ( LOG.getLevel() == ILogger.LOG_DEBUG ) {
352 // StringWriter sw = new StringWriter( 5000 );
353 // try {
354 // getFeatureDocument.prettyPrint( sw );
355 // } catch ( TransformerException e ) {
356 // getFeatureDocument.write( sw );
357 // }
358 // LOG.logDebug( sw.getBuffer().toString() );
359 // }
360
361 try {
362 LOG.logDebug( "Creating the GetFeature bean from the transformed GetRecordsDocument" );
363 getFeature = GetFeature.create( getRecords.getId(), getFeatureDocument.getRootElement() );
364 } catch ( Exception e ) {
365 String msg = "Cannot generate object representation for GetFeature request: " + e.getMessage();
366 LOG.logError( msg, e );
367 throw new OGCWebServiceException( msg );
368 }
369
370 try {
371 LOG.logDebug( "Sending the GetFeature Request to the local wfs" );
372 wfsResponse = wfsResource.doService( getFeature );
373 } catch ( OGCWebServiceException e ) {
374 String msg = "Generated WFS GetFeature request failed: " + e.getMessage();
375 LOG.logError( msg, e );
376 throw new OGCWebServiceException( msg );
377 }
378
379 // theoretical it is possible the result of a GetFeature request is not
380 // an instance of FeatureResult; but this never should happen
381 if ( !( wfsResponse instanceof FeatureResult ) ) {
382 String msg = "Unexpected result type '" + wfsResponse.getClass().getName()
383 + "' from WFS (must be FeatureResult)."
384 + " Maybe a FeatureType is not correctly registered!?";
385 LOG.logError( msg );
386 throw new OGCWebServiceException( msg );
387 }
388
389 FeatureResult featureResult = (FeatureResult) wfsResponse;
390
391 // this never should happen too - but it is possible
392 if ( !( featureResult.getResponse() instanceof FeatureCollection ) ) {
393 String msg = "Unexpected reponse type: '" + featureResult.getResponse().getClass().getName()
394 + " "
395 + featureResult.getResponse().getClass()
396 + "' in FeatureResult of WFS (must be a FeatureCollection).";
397 LOG.logError( msg );
398 throw new OGCWebServiceException( msg );
399 }
400 FeatureCollection featureCollection = (FeatureCollection) featureResult.getResponse();
401
402 try {
403 int numberOfRecordsReturned = featureCollection.size();
404 int numberOfMatchedRecords = 0;
405 if ( getRecords.getResultType().equals( RESULT_TYPE.HITS ) ) {
406 numberOfMatchedRecords = Integer.parseInt( featureCollection.getAttribute( "numberOfFeatures" ) );
407 } else {
408 // if result type does not equal 'HITS', a separate request must
409 // be created and performed to determine how many records match
410 // the query
411 LOG.logDebug( "Going to determine the number of matched records" );
412 numberOfMatchedRecords = determineRecordsMatched( getRecords );
413 }
414
415 int startPosition = getRecords.getStartPosition();
416 if ( startPosition < 1 )
417 startPosition = 1;
418 int nextRecord = startPosition + featureCollection.size();
419
420 HashMap<String, String> params = new HashMap<String, String>();
421 params.put( "REQUEST_ID", getRecords.getId() );
422 if ( numberOfRecordsReturned != 0 ) {
423 params.put( "SEARCH_STATUS", "complete" );
424 } else {
425 params.put( "SEARCH_STATUS", "none" );
426 }
427 params.put( "TIMESTAMP", TimeTools.getISOFormattedTime() );
428 List<QualifiedName> typenames = getRecords.getQuery().getTypeNamesAsList();
429 // this is a bit critical because
430 // a) not the complete result can be validated but just single records
431 // b) it is possible that several different record types are part
432 // of a response that must be validated against different schemas
433 String s = null;
434 String version = getRecords.getVersion();
435 if( version == null || "".equals( version.trim() ) ){
436 version = GetRecords.DEFAULT_VERSION;
437 }
438 if ( "2.0.0".equals( version ) ) {
439 s = StringTools.concat( 300,
440 OGCServletController.address,
441 "?service=CSW&version=2.0.0&",
442 "request=DescribeRecord&typeName=",
443 typenames.get( 0 ).getPrefix(),
444 ":",
445 typenames.get( 0 ).getLocalName() );
446 } else {
447 s = StringTools.concat( 300,
448 OGCServletController.address,
449 "?service=CSW&version="+version+"&",
450 "request=DescribeRecord&typeName=",
451 typenames.get( 0 ).getFormattedString() );
452 }
453 params.put( "VERSION", version );
454 params.put( "RECORD_SCHEMA", s );
455 params.put( "RECORDS_MATCHED", "" + numberOfMatchedRecords );
456 params.put( "RECORDS_RETURNED", "" + numberOfRecordsReturned );
457 params.put( "NEXT_RECORD", "" + nextRecord );
458 String elementSet = getRecords.getQuery().getElementSetName();
459 if ( elementSet == null ) {
460 elementSet = "brief";
461 }
462 params.put( "ELEMENT_SET", elementSet.toLowerCase() );
463 params.put( "RESULT_TYPE", resultType );
464 params.put( "REQUEST_NAME", "GetRecords" );
465
466 ByteArrayOutputStream bos = new ByteArrayOutputStream( 50000 );
467 GMLFeatureAdapter ada = new GMLFeatureAdapter( true );
468
469 ada.export( featureCollection, bos );
470 if ( LOG.getLevel() == ILogger.LOG_DEBUG ) {
471 s = new String( bos.toByteArray() );
472 LOG.logDebug( s );
473 FileUtils.writeToFile( "CSW_GetRecord_FC.xml", s );
474 }
475
476 // vice versa to request transforming the feature collection being result
477 // to the GetFeature request must be transformed into a GetRecords result
478 ByteArrayInputStream bis = new ByteArrayInputStream( bos.toByteArray() );
479 XSLTDocument xslSheet = OUT_XSL.get( outputSchema );
480 XMLFragment resultDocument = xslSheet.transform( bis, null, null, params );
481 GetRecordsResultDocument cswResponseDocument = new GetRecordsResultDocument();
482 cswResponseDocument.setRootElement( resultDocument.getRootElement() );
483 cswResponse = cswResponseDocument.parseGetRecordsResponse( getRecords );
484 } catch ( IOException e ) {
485 String msg = "Can't transform WFS response (FeatureCollection) to CSW response: " + e.getMessage();
486 LOG.logError( msg, e );
487 throw new OGCWebServiceException( msg );
488
489 } catch ( FeatureException e ) {
490 String msg = "Can't transform WFS response (FeatureCollection) to CSW response: " + e.getMessage();
491 LOG.logError( msg, e );
492 throw new OGCWebServiceException( msg );
493
494 } catch ( TransformerException e ) {
495 String msg = "Can't transform WFS response (FeatureCollection) to CSW response: " + e.getMessage();
496 LOG.logError( msg, e );
497 throw new OGCWebServiceException( msg );
498
499 }
500
501 return cswResponse;
502 }
503
504 /**
505 * Returns the number of records matching a GetRecords request.
506 *
507 * @param getRecords
508 * @return the number of records matching a GetRecords request
509 * @throws OGCWebServiceException
510 */
511 private int determineRecordsMatched( GetRecords getRecords ) throws OGCWebServiceException {
512 getRecords.setResultType( GetRecords.RESULT_TYPE.HITS );
513 GetFeature getFeature = null;
514 XMLFragment getFeatureDocument = null;
515 Object wfsResponse = null;
516 String outputSchema = normalizeOutputSchema( getRecords.getOutputSchema() );
517
518 XMLFragment getRecordsDocument = new XMLFragment( XMLFactory.export( getRecords ).getRootElement() );
519 try {
520 LOG.logDebug( "Getting the xslt sheet for the determination of the number of matched records" );
521 XSLTDocument xslSheet = IN_XSL.get( outputSchema );
522 synchronized ( xslSheet ) {
523 getFeatureDocument = xslSheet.transform( getRecordsDocument );
524 LOG.logDebug( "*****Second Generated WFS GetFeature request (to determine records matched):\n" + getFeatureDocument.getAsPrettyString() );
525 xslSheet.notifyAll();
526 }
527
528 // getFeatureDocument = xslSheet.transform( getRecordsDocument );
529 // LOG.logDebug( "Generated WFS GetFeature request (HITS):\n" + getFeatureDocument );
530 } catch ( TransformerException e ) {
531 e.printStackTrace();
532 String msg = "Can't transform GetRecord request to WFS GetFeature request: " + e.getMessage();
533 LOG.logError( msg, e );
534 throw new OGCWebServiceException( msg );
535 }
536
537 try {
538 getFeature = GetFeature.create( getRecords.getId(), getFeatureDocument.getRootElement() );
539 } catch ( Exception e ) {
540 String msg = "Cannot generate object representation for GetFeature request: " + e.getMessage();
541 LOG.logError( msg, e );
542 throw new OGCWebServiceException( msg );
543 }
544
545 try {
546 wfsResponse = wfsResource.doService( getFeature );
547 } catch ( OGCWebServiceException e ) {
548 String msg = "Generated WFS GetFeature request failed: " + e.getMessage();
549 LOG.logError( msg, e );
550 throw new OGCWebServiceException( msg );
551 }
552
553 if ( !( wfsResponse instanceof FeatureResult ) ) {
554 String msg = "Unexpected result type '" + wfsResponse.getClass().getName()
555 + "' from WFS (must be FeatureResult)."
556 + " Maybe a FeatureType is not correctly registered!?";
557 LOG.logError( msg );
558 throw new OGCWebServiceException( msg );
559 }
560
561 FeatureResult featureResult = (FeatureResult) wfsResponse;
562
563 if ( !( featureResult.getResponse() instanceof FeatureCollection ) ) {
564 String msg = "Unexpected reponse type: '" + featureResult.getResponse().getClass().getName()
565 + " "
566 + featureResult.getResponse().getClass()
567 + "' in FeatureResult of WFS (must be a FeatureCollection).";
568 LOG.logError( msg );
569 throw new OGCWebServiceException( msg );
570 }
571 FeatureCollection featureCollection = (FeatureCollection) featureResult.getResponse();
572
573 return Integer.parseInt( featureCollection.getAttribute( "numberOfFeatures" ) );
574 }
575
576 /**
577 * Performs a <code>GetRecordById</code> request.
578 * <p>
579 * This involves the following steps:
580 * <ul>
581 * <li><code>GetRecordById</code>-><code>GetRecordByIdDocument</code></li>
582 * <li><code>GetRecordByIdDocument</code>-><code>GetFeatureDocument</code> using XSLT</li>
583 * <li><code>GetFeatureDocument</code>-><code>GetFeature</code></li>
584 * <li><code>GetFeature</code> request is performed against the underlying WFS</li>
585 * <li>WFS answers with a <code>FeatureResult</code> object (which contains a <code>FeatureCollection</code>)</li>
586 * <li><code>FeatureCollection</code>-> GMLFeatureCollectionDocument (as a String)</li>
587 * <li>GMLFeatureCollectionDocument</code>-><code>GetRecordsResultDocument</code> using XSLT</li>
588 * <li><code>GetRecordsResultDocument</code>-><code>GetRecordsResult</code></li>
589 * </ul>
590 * </p>
591 *
592 * @param getRecordById
593 * @return The GetRecordByIdResult created from teh given GetRecordById
594 * @throws OGCWebServiceException
595 */
596 public GetRecordByIdResult query( GetRecordById getRecordById ) throws OGCWebServiceException {
597
598 GetFeature getFeature = null;
599 XMLFragment getFeatureDocument = null;
600 Object wfsResponse = null;
601 GetRecordByIdResult cswResponse = null;
602 String outputSchema = cswConfiguration.getDeegreeParams().getDefaultOutputSchema();
603
604 XMLFragment getRecordsDocument = new XMLFragment( XMLFactory.export( getRecordById ).getRootElement() );
605 try {
606 XSLTDocument xslSheet = IN_XSL.get( outputSchema.toUpperCase() );
607 getFeatureDocument = xslSheet.transform( getRecordsDocument );
608 LOG.logDebug( "Generated WFS GetFeature request:\n" + getFeatureDocument );
609 } catch ( TransformerException e ) {
610 String msg = "Can't transform GetRecordById request to WFS GetFeature request: " + e.getMessage();
611 LOG.logError( msg, e );
612 throw new OGCWebServiceException( msg );
613 }
614
615 if ( LOG.getLevel() == ILogger.LOG_DEBUG ) {
616 StringWriter sw = new StringWriter( 5000 );
617 getFeatureDocument.write( sw );
618 LOG.logDebug( sw.getBuffer().toString() );
619 }
620
621 try {
622 getFeature = GetFeature.create( getRecordById.getId(), getFeatureDocument.getRootElement() );
623 } catch ( Exception e ) {
624 String msg = "Cannot generate object representation for GetFeature request: " + e.getMessage();
625 LOG.logError( msg, e );
626 throw new OGCWebServiceException( msg );
627 }
628
629 try {
630 wfsResponse = wfsResource.doService( getFeature );
631 } catch ( OGCWebServiceException e ) {
632 String msg = "Generated WFS GetFeature request failed: " + e.getMessage();
633 LOG.logError( msg, e );
634 throw new OGCWebServiceException( msg );
635 }
636
637 if ( !( wfsResponse instanceof FeatureResult ) ) {
638 String msg = "Unexpected result type '" + wfsResponse.getClass().getName()
639 + "' from WFS (must be FeatureResult)."
640 + " Maybe a FeatureType is not correctly registered!?";
641 LOG.logError( msg );
642 throw new OGCWebServiceException( msg );
643 }
644
645 FeatureResult featureResult = (FeatureResult) wfsResponse;
646
647 if ( !( featureResult.getResponse() instanceof FeatureCollection ) ) {
648 String msg = "Unexpected reponse type: '" + featureResult.getResponse().getClass().getName()
649 + " "
650 + featureResult.getResponse().getClass()
651 + "' in FeatureResult of WFS (must be a FeatureCollection).";
652 LOG.logError( msg );
653 throw new OGCWebServiceException( msg );
654 }
655 FeatureCollection featureCollection = (FeatureCollection) featureResult.getResponse();
656
657 try {
658 int numberOfMatchedRecords = featureCollection == null ? 0 : featureCollection.size();
659 int startPosition = 1;
660 long maxRecords = Integer.MAX_VALUE;
661 long numberOfRecordsReturned = startPosition + maxRecords < numberOfMatchedRecords ? maxRecords
662 : numberOfMatchedRecords - startPosition
663 + 1;
664 long nextRecord = numberOfRecordsReturned + startPosition > numberOfMatchedRecords ? 0
665 : numberOfRecordsReturned + startPosition;
666
667 HashMap<String, String> params = new HashMap<String, String>();
668 params.put( "REQUEST_ID", getRecordById.getId() );
669 if ( numberOfRecordsReturned != 0 ) {
670 params.put( "SEARCH_STATUS", "complete" );
671 } else {
672 params.put( "SEARCH_STATUS", "none" );
673 }
674 params.put( "TIMESTAMP", TimeTools.getISOFormattedTime() );
675 String s = OGCServletController.address + "?service=CSW&version=2.0.0&request=DescribeRecord";
676 params.put( "RECORD_SCHEMA", s );
677 params.put( "RECORDS_MATCHED", "" + numberOfMatchedRecords );
678 params.put( "RECORDS_RETURNED", "" + numberOfRecordsReturned );
679 params.put( "NEXT_RECORD", "" + nextRecord );
680 params.put( "ELEMENT_SET", "full" );
681 params.put( "REQUEST_NAME", "GetRecordById" );
682
683 featureCollection.setAttribute( "byID", "true" );
684 ByteArrayOutputStream bos = new ByteArrayOutputStream( 50000 );
685 GMLFeatureAdapter ada = new GMLFeatureAdapter( true );
686 ada.export( featureCollection, bos );
687
688 if ( LOG.getLevel() == ILogger.LOG_DEBUG ) {
689 LOG.logDebug( new String( bos.toByteArray() ) );
690 }
691
692 ByteArrayInputStream bis = new ByteArrayInputStream( bos.toByteArray() );
693 XSLTDocument xslSheet = OUT_XSL.get( outputSchema.toUpperCase() );
694 XMLFragment resultDocument = xslSheet.transform( bis, null, null, params );
695 GetRecordByIdResultDocument cswResponseDocument = new GetRecordByIdResultDocument();
696 cswResponseDocument.setRootElement( resultDocument.getRootElement() );
697 cswResponse = cswResponseDocument.parseGetRecordByIdResponse( getRecordById );
698 } catch ( Exception e ) {
699 e.printStackTrace();
700 String msg = "Can't transform WFS response (FeatureCollection) " + "to CSW response: " + e.getMessage();
701 LOG.logError( msg, e );
702 throw new OGCWebServiceException( msg );
703 }
704
705 return cswResponse;
706 }
707 }