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 }