001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/tags/2.1/src/org/deegree/ogcwebservices/csw/manager/Manager.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.manager; 045 046 import java.io.IOException; 047 import java.io.StringReader; 048 import java.io.StringWriter; 049 import java.net.URISyntaxException; 050 import java.net.URL; 051 import java.util.ArrayList; 052 import java.util.Collection; 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.framework.log.ILogger; 061 import org.deegree.framework.log.LoggerFactory; 062 import org.deegree.framework.util.StringTools; 063 import org.deegree.framework.xml.NamespaceContext; 064 import org.deegree.framework.xml.XMLFragment; 065 import org.deegree.framework.xml.XMLParsingException; 066 import org.deegree.framework.xml.XMLTools; 067 import org.deegree.framework.xml.XSLTDocument; 068 import org.deegree.ogcbase.CommonNamespaces; 069 import org.deegree.ogcwebservices.EchoRequest; 070 import org.deegree.ogcwebservices.MissingParameterValueException; 071 import org.deegree.ogcwebservices.OGCWebServiceException; 072 import org.deegree.ogcwebservices.csw.configuration.CatalogueConfiguration; 073 import org.deegree.ogcwebservices.csw.configuration.CatalogueDeegreeParams; 074 import org.deegree.ogcwebservices.csw.manager.HarvestRepository.ResourceType; 075 import org.deegree.ogcwebservices.wfs.WFService; 076 import org.deegree.ogcwebservices.wfs.capabilities.WFSCapabilities; 077 import org.deegree.ogcwebservices.wfs.operation.transaction.TransactionResponse; 078 import org.w3c.dom.Element; 079 import org.xml.sax.SAXException; 080 081 /** 082 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth </a> 083 * 084 * @author last edited by: $Author: rbezema $ 085 * 086 * @version 2.0, $Revision: 7480 $, $Date: 2007-06-06 13:03:40 +0200 (Mi, 06 Jun 2007) $ 087 */ 088 089 public class Manager { 090 091 private static final ILogger LOG = LoggerFactory.getLogger( Manager.class ); 092 093 private static Map<ResourceType, AbstractHarvester> harvester = null; 094 095 private static XSLTDocument IN_XSL = null; 096 097 private static XSLTDocument OUT_XSL = null; 098 099 private WFService wfsService; 100 101 /** 102 * initializes a Manager instance 103 * 104 * @param wfsService 105 * @param cswConfiguration 106 * @throws MissingParameterValueException 107 */ 108 public Manager( WFService wfsService, CatalogueConfiguration cswConfiguration ) throws MissingParameterValueException { 109 this.wfsService = wfsService; 110 111 // try { 112 CatalogueDeegreeParams cdp = cswConfiguration.getDeegreeParams(); 113 URL url = cdp.getTransformationInputXSLT().getLinkage().getHref(); 114 IN_XSL = new XSLTDocument(); 115 try { 116 IN_XSL.load( url ); 117 } catch ( IOException e ) { 118 String s = "If a CS-W is defined to handle Transaction and/or Harvest requests, XSLT scripts for request transformations (e.g. mapping the input schema to gml) must be defined in the deegreeParams section of the capabilities document. While trying to read an xslt script from: '" + url.toString() 119 + "' (which was defined for the transformation of incoming request), the following error occurred: " 120 + e.getMessage(); 121 LOG.logError( s, e ); 122 throw new MissingParameterValueException( getClass().getName(), s ); 123 } catch ( SAXException e ) { 124 String s = "The xslt script (transforming incoming requests) read from the location: '" + url 125 + "' could not be parsed because: " 126 + e.getMessage(); 127 LOG.logError( s, e ); 128 throw new MissingParameterValueException( getClass().getName(), s ); 129 } 130 url = cdp.getTransformationOutputXSLT().getLinkage().getHref(); 131 OUT_XSL = new XSLTDocument(); 132 try { 133 OUT_XSL.load( url ); 134 } catch ( IOException e ) { 135 String s = "If a CS-W is defined to handle Transaction and/or Harvest requests, XSLT scripts for request transformations (e.g. mapping the input schema to gml) must be defined in the deegreeParams section of the capabilities document. While trying to read an xslt script from: '" + url.toString() 136 + "' (which was defined for the transformation of the response), the following error occurred: " 137 + e.getMessage(); 138 LOG.logError( s, e ); 139 throw new MissingParameterValueException( getClass().getName(), s ); 140 } catch ( SAXException e ) { 141 String s = "The xslt script (transforming the response) read from the location: '" + url 142 + "' could not be parsed because: " 143 + e.getMessage(); 144 LOG.logError( s, e ); 145 throw new MissingParameterValueException( getClass().getName(), s ); 146 } 147 WFSCapabilities capa = wfsService.getCapabilities(); 148 149 initHarvester(); 150 151 LOG.logInfo( "CSW Manager initialized with WFS resource, wfs version:" + capa.getVersion() ); 152 } 153 154 /** 155 * initializes a static Map containing a harvester for other coatalogues, for services and for single CSW-profile 156 * documents. 157 */ 158 public static void initHarvester() { 159 if ( harvester == null ) { 160 harvester = new HashMap<ResourceType, AbstractHarvester>(); 161 harvester.put( ResourceType.catalogue, CatalogueHarvester.getInstance() ); 162 harvester.put( ResourceType.service, ServiceHarvester.getInstance() ); 163 harvester.put( ResourceType.csw_profile, CSWProfileHarvester.getInstance() ); 164 } 165 } 166 167 /** 168 * starts all known/registered harvester. This method can be used to start harvesting using requests e.g. if a 169 * server has been shutdown and restarted. 170 */ 171 public static void startAllHarvester() { 172 initHarvester(); 173 Collection con = harvester.values(); 174 for ( Iterator iter = con.iterator(); iter.hasNext(); ) { 175 AbstractHarvester h = (AbstractHarvester) iter.next(); 176 if ( !h.isRunning() ) { 177 h.startHarvesting(); 178 } 179 } 180 } 181 182 /** 183 * stpos all known/registered harvester. This method can be used to stop harvesting using requests e.g. if a server 184 * has shall be shut down. 185 * 186 */ 187 public static void stopAllHarvester() { 188 if ( harvester != null ) { 189 Collection con = harvester.values(); 190 for ( Iterator iter = con.iterator(); iter.hasNext(); ) { 191 AbstractHarvester h = (AbstractHarvester) iter.next(); 192 if ( h.isRunning() ) { 193 LOG.logInfo( "stop harvesting for: " + h.getClass().getName() ); 194 h.stopHarvesting(); 195 } 196 } 197 } 198 } 199 200 /** 201 * @param request 202 * @return An echorequest (e.g. a response to an harvest request). 203 * @throws OGCWebServiceException if something went wrong while processing the request 204 */ 205 public EchoRequest harvestRecords( Harvest request ) throws OGCWebServiceException { 206 207 try { 208 HarvesterFactory hf = new HarvesterFactory( harvester ); 209 AbstractHarvester h = hf.findHarvester( request ); 210 h.addRequest( request ); 211 if ( !h.isRunning() ) { 212 h.startHarvesting(); 213 } 214 if ( request.getHarvestInterval() == null ) { 215 // h.removeRequest( request ); 216 } 217 } catch ( Exception e ) { 218 LOG.logError( "could not perform harvest operation", e ); 219 throw new OGCWebServiceException( getClass().getName(), 220 "could not perform harvest operation" + e.getMessage() ); 221 } 222 223 return new EchoRequest( request.getId(), null ); 224 } 225 226 /** 227 * performs a transaction request by transforming and forwarding it to the WFS used as backend 228 * 229 * @param request 230 * @return Created by sending the request to the underlying wfs. 231 * @throws XMLParsingException 232 * @throws OGCWebServiceException 233 */ 234 public TransactionResult transaction( Transaction request ) throws OGCWebServiceException { 235 236 XMLFragment wfsTransactionDocument = null; 237 try { 238 XMLFragment transactionDocument = new XMLFragment( XMLFactory.export( request ).getRootElement() ); 239 StringWriter sww = new StringWriter( 15000 ); 240 transactionDocument.write( sww ); 241 transactionDocument.load( new StringReader( sww.getBuffer().toString() ), XMLFragment.DEFAULT_URL ); 242 wfsTransactionDocument = IN_XSL.transform( transactionDocument ); 243 if ( LOG.getLevel() == ILogger.LOG_DEBUG ) { 244 LOG.logDebug( "The (first) resulting wfs:Transaction document: \n " + wfsTransactionDocument.getAsPrettyString() ); 245 } 246 } catch ( SAXException saxe ) { 247 String msg = "Can't create an appropriate CSW-Transaction from the request because: " + saxe.getMessage(); 248 LOG.logError( msg, saxe ); 249 throw new OGCWebServiceException( msg ); 250 } catch ( IOException ioe ) { 251 String msg = "Can't create an appropriate CSW-Transaction from the request because: " + ioe.getMessage(); 252 LOG.logError( msg, ioe ); 253 throw new OGCWebServiceException( msg ); 254 } catch ( TransformerException te ) { 255 String msg = "Can't create an appropriate CSW-Transaction from the request because: " + te.getMessage(); 256 LOG.logError( msg, te ); 257 throw new OGCWebServiceException( msg ); 258 } catch ( XMLParsingException xmle ) { 259 String msg = "Can't create an appropriate CSW-Transaction from the request because: " + xmle.getMessage(); 260 LOG.logError( msg, xmle ); 261 throw new OGCWebServiceException( msg ); 262 } 263 264 org.deegree.ogcwebservices.wfs.operation.transaction.Transaction wfstrans = null; 265 try { 266 LOG.logDebug( "Creating a wfs transaction from the document" ); 267 wfstrans = org.deegree.ogcwebservices.wfs.operation.transaction.Transaction.create( request.getId(), 268 wfsTransactionDocument.getRootElement() ); 269 } catch ( OGCWebServiceException ogcwe ) { 270 LOG.logError( ogcwe.getMessage(), ogcwe ); 271 String msg = "Cannot create a required object representation (which is used for the interaction with the backend) of your request because: " + ogcwe.getMessage(); 272 throw new OGCWebServiceException( msg ); 273 } 274 275 Object wfsResponse = null; 276 try { 277 LOG.logDebug( "Sending the wfs transaction to the wfservice." ); 278 wfsResponse = wfsService.doService( wfstrans ); 279 } catch ( OGCWebServiceException e ) { 280 // String msg = "Generated WFS Transaction request failed: " + e.getMessage(); 281 String msg = "Generated WFS Transaction request failed: " + e.getMessage(); 282 LOG.logError( msg, e ); 283 throw new OGCWebServiceException( "While handling your request, the interaction with the csw-backend failed because: " + e.getMessage() ); 284 } 285 286 if ( !( wfsResponse instanceof org.deegree.ogcwebservices.wfs.operation.transaction.TransactionResponse ) ) { 287 String msg = "Unexpected result type '" + wfsResponse.getClass().getName() 288 + "' from WFS (must be TransactionResponse)." 289 + " Maybe a FeatureType is not correctly registered!?"; 290 LOG.logError( msg ); 291 throw new OGCWebServiceException( "The csw-backend gave an unanticipated response, this could mean, that your featuretypes aren't registered correctly! Please make sure the given featuretypes are correct." ); 292 } 293 294 TransactionResponse transResp = (TransactionResponse) wfsResponse; 295 XMLFragment wfsTransRespDoc = null; 296 try { 297 LOG.logDebug( "Parsing the wfs response." ); 298 wfsTransRespDoc = org.deegree.ogcwebservices.wfs.XMLFactory.export( transResp ); 299 } catch ( IOException e ) { 300 String msg = "export of WFS Transaction response as XML failed: " + e.getMessage(); 301 LOG.logError( msg, e ); 302 throw new OGCWebServiceException( msg ); 303 } 304 305 // -------------------------------------------------------------- 306 // the following section will replace the feature ids returned by 307 // the WFS for Insert requests by the ID of the inserted metadata sets 308 List<String> ids = new ArrayList<String>(); 309 List<Operation> ops = request.getOperations(); 310 for ( int i = 0; i < ops.size(); i++ ) { 311 if ( ops.get( i ) instanceof Insert ) { 312 try { 313 ids = extractIdentifiers( ids, (Insert) ops.get( i ) ); 314 } catch ( Exception e ) { 315 LOG.logError( e.getMessage(), e ); 316 throw new OGCWebServiceException( getClass().getName(), e.getMessage() ); 317 } 318 } 319 } 320 try { 321 if ( ids.size() > 0 ) { 322 wfsTransRespDoc = replaceIds( wfsTransRespDoc, ids ); 323 } 324 } catch ( Exception e ) { 325 LOG.logError( e.getMessage(), e ); 326 throw new OGCWebServiceException( getClass().getName(), e.getMessage() ); 327 } 328 // --------------------------------------------------------------- 329 330 TransactionResultDocument cswTransactionDocument = null; 331 try { 332 XMLFragment tmp = OUT_XSL.transform( wfsTransRespDoc ); 333 cswTransactionDocument = new TransactionResultDocument(); 334 cswTransactionDocument.setRootElement( tmp.getRootElement() ); 335 } catch ( TransformerException e ) { 336 String msg = "Can't transform GetRecord request to WFS GetFeature request: " + e.getMessage(); 337 LOG.logError( msg, e ); 338 throw new OGCWebServiceException( msg ); 339 } 340 TransactionResult result = null; 341 try { 342 result = cswTransactionDocument.parseTransactionResponse( request ); 343 } catch ( XMLParsingException e ) { 344 throw new OGCWebServiceException( "could not create TransactionResponse" ); 345 } 346 return result; 347 348 } 349 350 /** 351 * replaces the id values of WFS Insert result with corresponding metadata identifieres 352 * 353 * @param wfsTransRespDoc 354 * @param ids 355 * @return an xmlFragment with the gml:Feature ids replaced with the id' s given in the list 356 * @throws URISyntaxException 357 * @throws XMLParsingException 358 */ 359 private XMLFragment replaceIds( XMLFragment wfsTransRespDoc, List<String> ids ) throws XMLParsingException { 360 361 List nodes = XMLTools.getRequiredNodes( wfsTransRespDoc.getRootElement(), 362 "./wfs:InsertResults/wfs:Feature/ogc:FeatureId", 363 CommonNamespaces.getNamespaceContext() ); 364 for ( int i = 0; i < nodes.size(); i++ ) { 365 Element elem = (Element) nodes.get( i ); 366 elem.setAttribute( "fid", ids.get( i ) ); 367 } 368 369 return wfsTransRespDoc; 370 } 371 372 /** 373 * extracts all identifiers of the records to be inserted in correct order 374 * 375 * @param ids 376 * @param insert 377 * @return a list of identifiers which should be keys to xpaths which are defined in the messages.properties 378 * @throws XMLParsingException 379 * @throws URISyntaxException 380 */ 381 private List<String> extractIdentifiers( List<String> ids, Insert insert ) throws XMLParsingException { 382 List<Element> records = insert.getRecords(); 383 384 NamespaceContext nsc = CommonNamespaces.getNamespaceContext(); 385 386 //nsc.addNamespace( "dc", new URI( "http://www.purl.org/dc/elements/1.1/" ) ); 387 for ( int i = 0; i < records.size(); i++ ) { 388 String xpath = getIdentifierXPath( records.get( i ) ); 389 String fileIdentifier = XMLTools.getRequiredNodeAsString( records.get( i ), xpath, nsc ); 390 ids.add( fileIdentifier ); 391 } 392 return ids; 393 } 394 395 /** 396 * returns the XPath the metadata records identifier 397 * 398 * @param metaData 399 * @return the XPath the metadata records identifier 400 */ 401 private String getIdentifierXPath( Element metaData ) { 402 403 // default is iso 19115 404 String xpath = "iso19115:fileIdentifier/smXML:CharacterString"; 405 if ( metaData != null ) { 406 String nspace = metaData.getNamespaceURI(); 407 nspace = StringTools.replace( nspace, "http://", "", true ); 408 xpath = Messages.getString( "Identifier_" + nspace ); 409 } 410 return xpath; 411 } 412 413 /** 414 * @return the wfsService. 415 */ 416 public WFService getWfsService() { 417 return wfsService; 418 } 419 420 }