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