001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/tags/2.1/src/org/deegree/enterprise/servlet/WFSHandler.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 Aennchenstraße 19 030 53177 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.enterprise.servlet; 045 046 import java.io.IOException; 047 import java.io.ObjectOutputStream; 048 import java.io.OutputStream; 049 import java.net.MalformedURLException; 050 import java.net.URI; 051 import java.net.URL; 052 import java.util.HashMap; 053 import java.util.HashSet; 054 import java.util.Iterator; 055 import java.util.List; 056 import java.util.Map; 057 import java.util.Set; 058 059 import javax.servlet.http.HttpServletResponse; 060 061 import org.deegree.datatypes.QualifiedName; 062 import org.deegree.enterprise.ServiceException; 063 import org.deegree.framework.log.ILogger; 064 import org.deegree.framework.log.LoggerFactory; 065 import org.deegree.framework.util.CharsetUtils; 066 import org.deegree.framework.xml.XMLFragment; 067 import org.deegree.framework.xml.XSLTDocument; 068 import org.deegree.i18n.Messages; 069 import org.deegree.io.datastore.schema.MappedFeatureType; 070 import org.deegree.io.datastore.schema.MappedGMLSchema; 071 import org.deegree.model.feature.FeatureCollection; 072 import org.deegree.model.feature.FeatureTupleCollection; 073 import org.deegree.model.feature.GMLFeatureAdapter; 074 import org.deegree.model.feature.GMLFeatureCollectionDocument; 075 import org.deegree.ogcwebservices.OGCWebServiceException; 076 import org.deegree.ogcwebservices.OGCWebServiceRequest; 077 import org.deegree.ogcwebservices.getcapabilities.DCPType; 078 import org.deegree.ogcwebservices.getcapabilities.HTTP; 079 import org.deegree.ogcwebservices.getcapabilities.Operation; 080 import org.deegree.ogcwebservices.wfs.WFService; 081 import org.deegree.ogcwebservices.wfs.WFServiceFactory; 082 import org.deegree.ogcwebservices.wfs.XMLFactory; 083 import org.deegree.ogcwebservices.wfs.XMLFactory_1_0_0; 084 import org.deegree.ogcwebservices.wfs.capabilities.FormatType; 085 import org.deegree.ogcwebservices.wfs.capabilities.WFSCapabilities; 086 import org.deegree.ogcwebservices.wfs.capabilities.WFSCapabilitiesDocument; 087 import org.deegree.ogcwebservices.wfs.capabilities.WFSFeatureType; 088 import org.deegree.ogcwebservices.wfs.capabilities.WFSOperationsMetadata; 089 import org.deegree.ogcwebservices.wfs.configuration.WFSConfiguration; 090 import org.deegree.ogcwebservices.wfs.operation.AbstractWFSRequest; 091 import org.deegree.ogcwebservices.wfs.operation.DescribeFeatureType; 092 import org.deegree.ogcwebservices.wfs.operation.FeatureResult; 093 import org.deegree.ogcwebservices.wfs.operation.FeatureTypeDescription; 094 import org.deegree.ogcwebservices.wfs.operation.GetFeature; 095 import org.deegree.ogcwebservices.wfs.operation.LockFeature; 096 import org.deegree.ogcwebservices.wfs.operation.LockFeatureResponse; 097 import org.deegree.ogcwebservices.wfs.operation.LockFeatureResponseDocument; 098 import org.deegree.ogcwebservices.wfs.operation.Query; 099 import org.deegree.ogcwebservices.wfs.operation.WFSGetCapabilities; 100 import org.deegree.ogcwebservices.wfs.operation.transaction.Delete; 101 import org.deegree.ogcwebservices.wfs.operation.transaction.Insert; 102 import org.deegree.ogcwebservices.wfs.operation.transaction.Transaction; 103 import org.deegree.ogcwebservices.wfs.operation.transaction.TransactionOperation; 104 import org.deegree.ogcwebservices.wfs.operation.transaction.TransactionResponse; 105 import org.deegree.ogcwebservices.wfs.operation.transaction.Update; 106 107 /** 108 * Web servlet client for WFS. 109 * <p> 110 * NOTE: Currently, the <code>WFSHandler</code> is responsible for the pre- and postprocessing of 111 * virtual feature types. For virtual feature types, requests and responses are transformed using an 112 * XSL-script. Virtual feature types can also provide their own schema document that is sent as a 113 * response to {@link DescribeFeatureType} requests. 114 * <p> 115 * The heuristics that determines whether pre- or postprocessing is necessary, is not very accurate; 116 * check the methods: 117 * <ul> 118 * <li><code>#determineFormat(DescribeFeatureType, WFSConfiguration)</code></li> 119 * <li><code>#determineFormat(GetFeature, WFSConfiguration)</code></li> 120 * <li><code>#determineFormat(Transaction, WFSConfiguration)</code></li> 121 * </ul> 122 * <p> 123 * The code for the handling of virtual features should probably be moved to the {@link WFService} 124 * class. 125 * 126 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth </a> 127 * @author <a href="mailto:tfr@users.sourceforge.net">Torsten Friebe </a> 128 * @author <a href="mailto:schneider@lat-lon.de">Markus Schneider </a> 129 * @author last edited by: $Author: rbezema $ 130 * 131 * @version $Revision: 7929 $, $Date: 2007-08-07 10:43:20 +0200 (Di, 07 Aug 2007) $ 132 */ 133 class WFSHandler extends AbstractOWServiceHandler { 134 135 private static ILogger LOG = LoggerFactory.getLogger( WFSHandler.class ); 136 137 private static Map<URL, XSLTDocument> xsltCache; 138 static { 139 if ( xsltCache == null ) { 140 xsltCache = new HashMap<URL, XSLTDocument>(); 141 } 142 } 143 144 /** 145 * Performs the given {@link OGCWebServiceRequest} on the {@link WFService} and sends the 146 * response to the given {@link HttpServletResponse} object. 147 * 148 * @param request 149 * OGCWebServiceRequest to be performed 150 * @param httpResponse 151 * servlet response object to write to 152 * @throws ServiceException 153 */ 154 public void perform( OGCWebServiceRequest request, HttpServletResponse httpResponse ) 155 throws ServiceException { 156 157 LOG.logDebug( "Performing request: " + request.toString() ); 158 159 try { 160 WFService service = WFServiceFactory.createInstance(); 161 if ( request instanceof WFSGetCapabilities ) { 162 performGetCapabilities( service, (WFSGetCapabilities) request, httpResponse ); 163 } else if ( request instanceof DescribeFeatureType ) { 164 performDescribeFeatureType( service, (DescribeFeatureType) request, httpResponse ); 165 } else if ( request instanceof GetFeature ) { 166 performGetFeature( service, (GetFeature) request, httpResponse ); 167 } else if ( request instanceof Transaction ) { 168 performTransaction( service, (Transaction) request, httpResponse ); 169 } else if ( request instanceof LockFeature ) { 170 performLockFeature( service, (LockFeature) request, httpResponse ); 171 } else { 172 assert false : "Unhandled WFS request type: '" + request.getClass().getName() + "'"; 173 } 174 } catch ( OGCWebServiceException e ) { 175 LOG.logInfo( "Error while performing WFS request.", e ); 176 sendException( httpResponse, e ); 177 } catch ( Exception e ) { 178 LOG.logError( "Fatal error while performing WFS request.", e ); 179 sendException( httpResponse, new OGCWebServiceException( getClass().getName(), e.getMessage() ) ); 180 } 181 } 182 183 /** 184 * Performs a {@link WFSGetCapabilities} request and sends the response to the given 185 * {@link HttpServletResponse} object. 186 * 187 * @param service 188 * WFService instance to be used 189 * @param request 190 * GetCapabilities request to be performed 191 * @param httpResponse 192 * servlet response object to write to 193 * @throws OGCWebServiceException 194 */ 195 private void performGetCapabilities( WFService service, WFSGetCapabilities request, HttpServletResponse httpResponse ) 196 throws OGCWebServiceException { 197 198 WFSCapabilities capa = (WFSCapabilities) service.doService( request ); 199 200 try { 201 httpResponse.setContentType( "text/xml; charset=" + CharsetUtils.getSystemCharset() ); 202 WFSCapabilitiesDocument document = null; 203 String version = request.getVersion(); 204 boolean use_1_1_0 = true; 205 LOG.logDebug( "Version of incoming request is: " + version ); 206 if( "1.0.0".compareTo( version ) >= 0 ) { 207 use_1_1_0 = false; 208 } 209 if( ! use_1_1_0 ) { 210 document = XMLFactory_1_0_0.getInstance().export( (WFSConfiguration)capa ); 211 } else{ 212 document = XMLFactory.export( capa, request.getSections() ); 213 } 214 document.write( httpResponse.getOutputStream() ); 215 } catch ( IOException e ) { 216 LOG.logError( "Error sending GetCapabilities response to client.", e ); 217 } 218 } 219 220 /** 221 * Performs a {@link DescribeFeatureType} request and sends the response to the given 222 * {@link HttpServletResponse} object. 223 * 224 * @param service 225 * WFService instance to be used 226 * @param request 227 * DescribeFeatureType request to be performed 228 * @param httpResponse 229 * servlet response object to write to 230 * @throws OGCWebServiceException 231 */ 232 private void performDescribeFeatureType( WFService service, DescribeFeatureType request, 233 HttpServletResponse httpResponse ) 234 throws OGCWebServiceException { 235 236 WFSConfiguration config = (WFSConfiguration) service.getCapabilities(); 237 FormatType format = determineFormat( request, config, service ); 238 239 XMLFragment schemaDoc = null; 240 241 if ( format.getSchemaLocation() != null ) { 242 // read special schema for virtual format 243 try { 244 schemaDoc = new XMLFragment( format.getSchemaLocation().toURL() ); 245 } catch ( Exception e ) { 246 String msg = Messages.getMessage( "WFS_VIRTUAL_FORMAT_SCHEMA_READ_ERROR", format.getSchemaLocation(), 247 format.getValue(), e ); 248 LOG.logError( msg, e ); 249 throw new OGCWebServiceException( getClass().getName(), msg ); 250 } 251 } else { 252 // get schema from WFService 253 FeatureTypeDescription ftDescription = (FeatureTypeDescription) service.doService( request ); 254 schemaDoc = ftDescription.getFeatureTypeSchema(); 255 } 256 257 httpResponse.setContentType( "text/xml; charset=" + CharsetUtils.getSystemCharset() ); 258 try { 259 schemaDoc.write( httpResponse.getOutputStream() ); 260 } catch ( IOException e ) { 261 LOG.logError( "Error sending DescribeFeatureType response to client.", e ); 262 } 263 } 264 265 /** 266 * Performs a {@link GetFeature} request and sends the response to the given 267 * {@link HttpServletResponse} object. 268 * 269 * @param service 270 * WFService instance to be used 271 * @param request 272 * GetFeature request to be performed 273 * @param httpResponse 274 * servlet response object to write to 275 * @throws OGCWebServiceException 276 */ 277 private void performGetFeature( WFService service, GetFeature request, HttpServletResponse httpResponse ) 278 throws OGCWebServiceException { 279 280 if ( LOG.getLevel() == ILogger.LOG_DEBUG ) { 281 try { 282 XMLFragment xml = XMLFactory.export( request ); 283 LOG.logDebug( xml.getAsPrettyString() ); 284 } catch ( Exception e ) { 285 // nothing to do 286 } 287 } 288 289 WFSConfiguration config = (WFSConfiguration) service.getCapabilities(); 290 FormatType formatType = determineFormat( request, config, service ); 291 292 // perform pre-processing if necessary (XSLT) 293 if ( formatType.isVirtual() ) { 294 request = transformGetFeature( request, formatType ); 295 } 296 297 // perform request on WFService 298 FeatureResult result = (FeatureResult) service.doService( request ); 299 FeatureCollection fc = (FeatureCollection) result.getResponse(); 300 301 String format = formatType.getValue(); 302 303 if ( GetFeature.FORMAT_FEATURECOLLECTION.equals( format ) ) { 304 sendBinaryResponse( fc, httpResponse ); 305 } else if ( AbstractWFSRequest.FORMAT_GML2_WFS100.equals( format ) || AbstractWFSRequest.FORMAT_XML.equals( format ) || format.startsWith( "text/xml; subtype=" ) ) { 306 String schemaURL = buildSchemaURL( service, request ); 307 boolean suppressXLink = suppressXLinkOutput( fc ); 308 if ( formatType.getOutFilter() != null ) { 309 sendTransformedResponse( fc, httpResponse, schemaURL, suppressXLink, formatType ); 310 } else { 311 sendGMLResponse( fc, httpResponse, schemaURL, suppressXLink ); 312 } 313 } else { 314 String msg = Messages.getMessage( "WFS_QUERY_UNSUPPORTED_FORMAT2", format ); 315 throw new OGCWebServiceException( msg ); 316 } 317 } 318 319 /** 320 * Performs a {@link LockFeature} request and sends the response to the given 321 * {@link HttpServletResponse} object. 322 * 323 * @param service 324 * WFService instance to be used 325 * @param request 326 * LockFeature request to be performed 327 * @param httpResponse 328 * servlet response object to write to 329 * @throws OGCWebServiceException 330 */ 331 private void performLockFeature( WFService service, LockFeature request, HttpServletResponse httpResponse ) 332 throws OGCWebServiceException { 333 334 LockFeatureResponse response = (LockFeatureResponse) service.doService( request ); 335 LockFeatureResponseDocument responseDoc; 336 try { 337 responseDoc = XMLFactory.export( response ); 338 } catch ( Exception e ) { 339 throw new OGCWebServiceException( this.getClass().getName(), e.getMessage() ); 340 } 341 342 httpResponse.setContentType( "text/xml; charset=" + CharsetUtils.getSystemCharset() ); 343 try { 344 responseDoc.write( httpResponse.getOutputStream() ); 345 } catch ( IOException e ) { 346 LOG.logError( "Error sending LockFeature response to client.", e ); 347 } 348 } 349 350 /** 351 * Builds a KVP-encoded DescribeFeatureType-request that can be used to fetch the schemas for 352 * all feature types are that queried in the given {@link GetFeature} request. 353 * 354 * @param service 355 * @param request 356 * @return KVP-encoded DescribeFeatureType-request 357 */ 358 private String buildSchemaURL( WFService service, GetFeature request ) { 359 360 String schemaURL = null; 361 362 WFSCapabilities capa = service.getCapabilities(); 363 WFSOperationsMetadata opMetadata = (WFSOperationsMetadata) capa.getOperationsMetadata(); 364 Operation describeFTOperation = opMetadata.getDescribeFeatureType(); 365 DCPType[] dcpTypes = describeFTOperation.getDCPs(); 366 if ( dcpTypes.length > 0 && dcpTypes[0].getProtocol() instanceof HTTP ) { 367 HTTP http = (HTTP) dcpTypes[0].getProtocol(); 368 if ( http.getGetOnlineResources().length > 0 ) { 369 URL baseURL = http.getGetOnlineResources()[0]; 370 String requestPart = buildDescribeFTRequest( request ); 371 schemaURL = baseURL.toString() + requestPart; 372 } 373 } 374 return schemaURL; 375 } 376 377 /** 378 * Builds the parameter part for a KVP-encoded DescribeFeatureType-request that fetches the 379 * necessary schemas for all feature types that are queried in the given {@link GetFeature} 380 * request. 381 * 382 * @param request 383 * @return the URL-encoded parameter part of a KVP-DescribeFeatureType request 384 */ 385 private String buildDescribeFTRequest( GetFeature request ) { 386 387 Set<QualifiedName> ftNames = new HashSet<QualifiedName>(); 388 Map<String, URI> nsBindings = new HashMap<String, URI>(); 389 390 // get all requested feature types 391 Query[] queries = request.getQuery(); 392 for ( Query query : queries ) { 393 QualifiedName[] typeNames = query.getTypeNames(); 394 for ( QualifiedName name : typeNames ) { 395 ftNames.add( name ); 396 } 397 } 398 Iterator<QualifiedName> ftNameIter = ftNames.iterator(); 399 QualifiedName qn = ftNameIter.next(); 400 StringBuffer typeNameSb = new StringBuffer( qn.getPrefix() ); 401 typeNameSb.append( ':' ).append( qn.getLocalName() ); 402 while ( ftNameIter.hasNext() ) { 403 typeNameSb.append( ',' ); 404 qn = ftNameIter.next(); 405 typeNameSb.append( qn.getPrefix() ); 406 typeNameSb.append( ':' ).append( qn.getLocalName() ); 407 } 408 409 // get all used namespace bindings 410 for ( QualifiedName ftName : ftNames ) { 411 nsBindings.put( ftName.getPrefix(), ftName.getNamespace() ); 412 } 413 StringBuffer nsParamSb = new StringBuffer( "xmlns(" ); 414 Iterator<String> prefixIter = nsBindings.keySet().iterator(); 415 String prefix = prefixIter.next(); 416 nsParamSb.append( prefix ); 417 nsParamSb.append( '=' ); 418 nsParamSb.append( nsBindings.get( prefix ) ); 419 while ( prefixIter.hasNext() ) { 420 nsParamSb.append( ',' ); 421 prefix = prefixIter.next(); 422 nsParamSb.append( prefix ); 423 nsParamSb.append( '=' ); 424 nsParamSb.append( nsBindings.get( prefix ) ); 425 } 426 nsParamSb.append( ')' ); 427 428 // build KVP-DescribeFeatureType-request 429 StringBuffer sb = new StringBuffer( "SERVICE=WFS" ); 430 sb.append( "&VERSION=1.1.0" ); 431 sb.append( "&REQUEST=DescribeFeatureType" ); 432 433 // append TYPENAME parameter 434 sb.append( "&TYPENAME=" ); 435 sb.append( typeNameSb ); 436 437 // append NAMESPACE parameter 438 sb.append( "&NAMESPACE=" ); 439 sb.append( nsParamSb.toString() ); 440 441 return sb.toString(); 442 } 443 444 /** 445 * Transforms a {@link GetFeature} request depending on the requested virtual format. 446 * 447 * @param request 448 * GetFeature request to be transformed 449 * @param format 450 * requested (virtual) output format 451 * @return transformed GetFeature requested 452 * @throws OGCWebServiceException 453 * if transformation script could not be loaded or transformation failed 454 */ 455 private GetFeature transformGetFeature( GetFeature request, FormatType format ) 456 throws OGCWebServiceException { 457 458 LOG.logDebug( "Transforming GetFeature request." ); 459 long start = System.currentTimeMillis(); 460 461 URL inFilterURL = null; 462 try { 463 inFilterURL = format.getInFilter().toURL(); 464 } catch ( MalformedURLException e1 ) { 465 // never happens 466 } 467 XSLTDocument xsl = xsltCache.get( inFilterURL ); 468 if ( xsl == null ) { 469 xsl = new XSLTDocument(); 470 try { 471 xsl.load( inFilterURL ); 472 xsltCache.put( inFilterURL, xsl ); 473 } catch ( Exception e ) { 474 String msg = Messages.getMessage( "WFS_PREPROCESS_XSL_FILE_ERROR", format.getValue(), 475 format.getInFilter().toString(), e ); 476 LOG.logError( msg, e ); 477 throw new OGCWebServiceException( getClass().getName(), msg ); 478 } 479 } 480 481 XMLFragment xml = null; 482 try { 483 xml = XMLFactory.export( request ); 484 xml = xsl.transform( xml, format.getInFilter().toASCIIString(), null, null ); 485 } catch ( Exception e ) { 486 String msg = Messages.getMessage( "WFS_PREPROCESS_XSL_ERROR", format.getValue(), e ); 487 LOG.logError( msg ); 488 throw new OGCWebServiceException( getClass().getName(), msg ); 489 } 490 491 if ( LOG.getLevel() == ILogger.LOG_DEBUG ) { 492 LOG.logDebug( "Successfully transformed GetFeature request in " + ( System.currentTimeMillis() - start ) 493 + " milliseconds." ); 494 try { 495 LOG.logDebugXMLFile( "WFSHandler_GetFeature_transformed", xml ); 496 } catch ( Exception e ) { 497 LOG.logError( e.getMessage(), e ); 498 } 499 } 500 return GetFeature.create( request.getId(), xml.getRootElement() ); 501 } 502 503 /** 504 * Sends the given {@link FeatureCollection} as GML to the given {@link HttpServletResponse} 505 * object. 506 * 507 * @param fc 508 * feature collection to send 509 * @param httpResponse 510 * servlet response object to write to 511 * @param schemaURL 512 * URL to schema document (DescribeFeatureType request) 513 * @param suppressXLinks 514 * true, if no XLinks must be used in the output, false otherwise 515 */ 516 private void sendGMLResponse( FeatureCollection fc, HttpServletResponse httpResponse, String schemaURL, 517 boolean suppressXLinks ) { 518 519 try { 520 httpResponse.setContentType( "text/xml; charset=" + CharsetUtils.getSystemCharset() ); 521 OutputStream os = httpResponse.getOutputStream(); 522 GMLFeatureAdapter featureAdapter = new GMLFeatureAdapter( suppressXLinks, schemaURL ); 523 featureAdapter.export( fc, os, CharsetUtils.getSystemCharset() ); 524 } catch ( Exception e ) { 525 LOG.logError( "Error sending GetFeature response (GML) to client.", e ); 526 } 527 } 528 529 /** 530 * Sends the given {@link FeatureCollection} as a serialized Java object to the given 531 * {@link HttpServletResponse} object. 532 * 533 * @param fc 534 * feature collection to send 535 * @param httpResponse 536 * servlet response object to write to 537 */ 538 private void sendBinaryResponse( FeatureCollection fc, HttpServletResponse httpResponse ) { 539 try { 540 OutputStream os = httpResponse.getOutputStream(); 541 ObjectOutputStream oos = new ObjectOutputStream( os ); 542 oos.writeObject( fc ); 543 oos.flush(); 544 } catch ( IOException e ) { 545 LOG.logError( "Error sending GetFeature response (binary) to client.", e ); 546 } 547 } 548 549 /** 550 * Transforms a {@link FeatureCollection} to the given format using XSLT and sends it to the 551 * specified {@link HttpServletResponse} object. 552 * 553 * @param fc 554 * feature collection to send 555 * @param schemaURL 556 * URL to schema document (DescribeFeatureType request) 557 * @param httpResponse 558 * servlet response object to write to 559 * @param suppressXLinks 560 * true, if no XLinks must be used in the output, false otherwise 561 * @param format 562 * requested format 563 */ 564 private void sendTransformedResponse( FeatureCollection fc, HttpServletResponse httpResponse, String schemaURL, 565 boolean suppressXLinks, FormatType format ) 566 throws OGCWebServiceException { 567 568 GMLFeatureCollectionDocument fcgml = null; 569 try { 570 // export result feature collection as GML to enable transformation 571 // into another (XML) format 572 GMLFeatureAdapter featureAdapter = new GMLFeatureAdapter( suppressXLinks, schemaURL ); 573 fcgml = featureAdapter.export( fc ); 574 } catch ( Exception e ) { 575 String msg = "Could not export feature collection to GML: " + e.getMessage(); 576 LOG.logError( msg, e ); 577 throw new OGCWebServiceException( msg ); 578 } 579 580 LOG.logDebug( "Transforming GetFeature response." ); 581 long start = System.currentTimeMillis(); 582 583 // TODO: cache Transformer 584 XSLTDocument xsl = null; 585 try { 586 xsl = new XSLTDocument( format.getOutFilter().toURL() ); 587 } catch ( Exception e ) { 588 String msg = Messages.getMessage( "WFS_POSTPROCESS_XSL_FILE_ERROR", format.getValue(), 589 format.getOutFilter().toString(), e ); 590 LOG.logError( msg ); 591 throw new OGCWebServiceException( msg ); 592 } 593 594 try { 595 if ( LOG.getLevel() == ILogger.LOG_DEBUG ) { 596 LOG.logDebugFile( "WFSHandler_GetFeature_result", ".xml", fcgml.getAsString() ); 597 } 598 599 String type = format.getValue().split( ";" )[0]; 600 httpResponse.setContentType( type + "; charset=" + CharsetUtils.getSystemCharset() ); 601 602 OutputStream os = httpResponse.getOutputStream(); 603 xsl.transform( fcgml, os ); 604 os.close(); 605 } catch ( Exception e ) { 606 String msg = Messages.getMessage( "WFS_POSTPROCESS_XSL_ERROR", format.getValue(), e ); 607 LOG.logError( msg, e ); 608 throw new OGCWebServiceException( getClass().getName(), msg ); 609 } 610 611 LOG.logDebug( "Successfully transformed GetFeature response in " + ( System.currentTimeMillis() - start ) 612 + " milliseconds." ); 613 } 614 615 /** 616 * Performs a {@link Transaction} request and sends the response to the given 617 * {@link HttpServletResponse} object. 618 * 619 * @param service 620 * WFService instance to be used 621 * @param request 622 * Transaction request to be performed 623 * @param httpResponse 624 * servlet response object to write to 625 * @throws OGCWebServiceException 626 */ 627 private void performTransaction( WFService service, Transaction request, HttpServletResponse httpResponse ) 628 throws OGCWebServiceException { 629 630 WFSConfiguration config = (WFSConfiguration) service.getCapabilities(); 631 FormatType format = determineFormat( request, config ); 632 633 // perform pre-processing if necessary (XSLT) 634 if ( format.isVirtual() ) { 635 request = transformTransaction( request, format ); 636 } 637 638 TransactionResponse response = (TransactionResponse) service.doService( request ); 639 640 try { 641 httpResponse.setContentType( "text/xml; charset=" + CharsetUtils.getSystemCharset() ); 642 XMLFragment document = XMLFactory.export( response ); 643 document.write( httpResponse.getOutputStream() ); 644 } catch ( IOException e ) { 645 LOG.logError( "Error sending Transaction response to client.", e ); 646 } 647 } 648 649 /** 650 * Transforms a {@link Transaction} request depending on the requested virtual format. 651 * 652 * @param request 653 * Transaction request to be transformed 654 * @param formatType 655 * requested (virtual) output format 656 * @return transformed Transaction 657 * @throws OGCWebServiceException 658 * if transformation script could not be loaded or transformation failed 659 */ 660 private Transaction transformTransaction( Transaction request, FormatType format ) 661 throws OGCWebServiceException { 662 663 LOG.logDebug( "Transforming Transaction request." ); 664 long start = System.currentTimeMillis(); 665 666 URL inFilterURL = null; 667 try { 668 inFilterURL = format.getInFilter().toURL(); 669 } catch ( MalformedURLException e1 ) { 670 // never happens 671 } 672 XSLTDocument xsl = xsltCache.get( inFilterURL ); 673 if ( xsl == null ) { 674 xsl = new XSLTDocument(); 675 try { 676 LOG.logDebug( "Read Filter ... " ); 677 xsl.load( inFilterURL ); 678 xsltCache.put( inFilterURL, xsl ); 679 } catch ( Exception e ) { 680 String msg = Messages.getMessage( "WFS_PREPROCESS_XSL_FILE_ERROR", format.getValue(), 681 format.getInFilter().toString(), e ); 682 LOG.logError( msg, e ); 683 throw new OGCWebServiceException( getClass().getName(), msg ); 684 } 685 } 686 687 XMLFragment xml = null; 688 try { 689 xml = request.getSourceDocument(); 690 if ( LOG.getLevel() == ILogger.LOG_DEBUG ) { 691 try { 692 LOG.logDebugXMLFile( "WFSHandler_Transaction_incoming", xml ); 693 } catch ( Exception e ) { 694 LOG.logError( e.getMessage(), e ); 695 } 696 } 697 } catch ( Exception e ) { 698 LOG.logError( e.getMessage(), e ); 699 throw new OGCWebServiceException( getClass().getName(), e.getMessage() ); 700 } 701 // transform Transaction request 702 try { 703 LOG.logDebug( "start transform ..." ); 704 xml = xsl.transform( xml, format.getInFilter().toASCIIString(), null, null ); 705 LOG.logDebug( "end transform ..." ); 706 if ( LOG.getLevel() == ILogger.LOG_DEBUG ) { 707 try { 708 LOG.logDebugXMLFile( "WFSHandler_Transaction_transformed", xml ); 709 } catch ( Exception e ) { 710 LOG.logError( e.getMessage(), e ); 711 } 712 } 713 } catch ( Exception e ) { 714 String msg = Messages.getMessage( "WFS_PREPROCESS_XSL_ERROR", format.getInFilter().toString(), e ); 715 LOG.logError( msg, e ); 716 throw new OGCWebServiceException( getClass().getName(), msg ); 717 } 718 719 try { 720 request = Transaction.create( request.getId(), xml.getRootElement() ); 721 } catch ( Exception e ) { 722 LOG.logError( e.getMessage(), e ); 723 throw new OGCWebServiceException( getClass().getName(), e.getMessage() ); 724 } 725 726 LOG.logDebug( "Successfully transformed Transaction request in " + ( System.currentTimeMillis() - start ) 727 + " milliseconds." ); 728 729 return request; 730 } 731 732 /** 733 * Determines whether the response to the given {@link GetFeature} request may use XLinks or 734 * not. 735 * <p> 736 * The first feature of the collection is checked; if it's {@link MappedGMLSchema} requests the 737 * suppression of XLinks, xlinks are disabled, otherwise they are enabled. 738 * 739 * @param request 740 * @return true, if the response document must not contain XLinks, false otherwise 741 */ 742 private boolean suppressXLinkOutput( FeatureCollection fc ) { 743 744 boolean suppressXLinkOutput = false; 745 746 if ( fc instanceof FeatureTupleCollection ) { 747 suppressXLinkOutput = true; 748 } else if ( fc.size() > 0 ) { 749 suppressXLinkOutput = ( (MappedFeatureType) fc.getFeature( 0 ).getFeatureType() ).getGMLSchema().suppressXLinkOutput(); 750 } 751 return suppressXLinkOutput; 752 } 753 754 private FormatType determineFormat( GetFeature request, WFSConfiguration config, WFService service ) 755 throws OGCWebServiceException { 756 757 Query firstQuery = request.getQuery()[0]; 758 QualifiedName ftName = firstQuery.getTypeNames()[0]; 759 WFSFeatureType wfsFT = config.getFeatureTypeList().getFeatureType( ftName ); 760 if ( wfsFT == null ) { 761 MappedFeatureType ft = service.getMappedFeatureType( ftName ); 762 String msg = null; 763 if ( ft == null ) { 764 msg = Messages.getMessage( "WFS_FEATURE_TYPE_UNKNOWN", ftName ); 765 } else { 766 assert !ft.isVisible(); 767 msg = Messages.getMessage( "WFS_FEATURE_TYPE_INVISIBLE", ftName ); 768 } 769 throw new OGCWebServiceException( getClass().getName(), msg ); 770 } 771 String requestedFormat = request.getOutputFormat(); 772 FormatType format = wfsFT.getOutputFormat( requestedFormat ); 773 if ( format == null ) { 774 String msg = Messages.getMessage( "WFS_QUERY_UNSUPPORTED_FORMAT", requestedFormat, ftName ); 775 throw new OGCWebServiceException( getClass().getName(), msg ); 776 } 777 return format; 778 } 779 780 private FormatType determineFormat( DescribeFeatureType request, WFSConfiguration config, WFService service ) 781 throws OGCWebServiceException { 782 783 // NOTE: this cannot cope with a mix of virtual and real features 784 QualifiedName ftName = null; 785 if ( request.getTypeNames().length > 0 ) { 786 ftName = request.getTypeNames()[0]; 787 } else { 788 ftName = config.getFeatureTypeList().getFeatureTypes()[0].getName(); 789 } 790 791 WFSFeatureType wfsFT = config.getFeatureTypeList().getFeatureType( ftName ); 792 if ( wfsFT == null ) { 793 MappedFeatureType ft = service.getMappedFeatureType( ftName ); 794 String msg = null; 795 if ( ft == null ) { 796 msg = Messages.getMessage( "WFS_FEATURE_TYPE_UNKNOWN", ftName ); 797 } else { 798 assert !ft.isVisible(); 799 msg = Messages.getMessage( "WFS_FEATURE_TYPE_INVISIBLE", ftName ); 800 } 801 throw new OGCWebServiceException( getClass().getName(), msg ); 802 } 803 String requestedFormat = request.getOutputFormat(); 804 FormatType format = wfsFT.getOutputFormat( requestedFormat ); 805 if ( format == null ) { 806 String msg = Messages.getMessage( "WFS_QUERY_UNSUPPORTED_FORMAT", requestedFormat, ftName ); 807 throw new OGCWebServiceException( getClass().getName(), msg ); 808 } 809 return format; 810 } 811 812 private FormatType determineFormat( Transaction request, WFSConfiguration config ) 813 throws OGCWebServiceException { 814 815 FormatType format = null; 816 817 WFSFeatureType wfsFT = config.getFeatureTypeList().getFeatureTypes()[0]; 818 819 List<TransactionOperation> list = request.getOperations(); 820 TransactionOperation op = list.get( 0 ); 821 if ( op instanceof Insert ) { 822 QualifiedName qn = ( (Insert) op ).getAffectedFeatureTypes().get( 0 ); 823 wfsFT = config.getFeatureTypeList().getFeatureType( qn ); 824 if ( wfsFT == null ) { 825 throw new OGCWebServiceException( Messages.getMessage( "WFS_INSERT_UNSUPPORTED_FT", qn ) ); 826 } 827 } else if ( op instanceof Update ) { 828 QualifiedName qn = ( (Update) op ).getAffectedFeatureTypes().get( 0 ); 829 wfsFT = config.getFeatureTypeList().getFeatureType( qn ); 830 if ( wfsFT == null ) { 831 throw new OGCWebServiceException( Messages.getMessage( "WFS_UPDATE_UNSUPPORTED_FT", qn ) ); 832 } 833 } else if ( op instanceof Delete ) { 834 QualifiedName qn = ( (Delete) op ).getAffectedFeatureTypes().get( 0 ); 835 wfsFT = config.getFeatureTypeList().getFeatureType( qn ); 836 if ( wfsFT == null ) { 837 throw new OGCWebServiceException( Messages.getMessage( "WFS_DELETE_UNSUPPORTED_FT", qn ) ); 838 } 839 } 840 841 FormatType[] formats = wfsFT.getOutputFormats(); 842 for ( int i = 0; i < formats.length; i++ ) { 843 format = formats[i]; 844 if ( format.getInFilter() != null ) { 845 break; 846 } 847 } 848 return format; 849 } 850 }