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 }