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 }