001: //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/trunk/src/org/deegree/io/datastore/wfs/CascadingWFSDatastore.java $
002: /*---------------- FILE HEADER ------------------------------------------
003:
004: This file is part of deegree.
005: Copyright (C) 2001-2008 by:
006: EXSE, Department of Geography, University of Bonn
007: http://www.giub.uni-bonn.de/deegree/
008: lat/lon GmbH
009: http://www.lat-lon.de
010:
011: This library is free software; you can redistribute it and/or
012: modify it under the terms of the GNU Lesser General Public
013: License as published by the Free Software Foundation; either
014: version 2.1 of the License, or (at your option) any later version.
015:
016: This library is distributed in the hope that it will be useful,
017: but WITHOUT ANY WARRANTY; without even the implied warranty of
018: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
019: Lesser General Public License for more details.
020:
021: You should have received a copy of the GNU Lesser General Public
022: License along with this library; if not, write to the Free Software
023: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
024:
025: Contact:
026:
027: Andreas Poth
028: lat/lon GmbH
029: Aennchenstr. 19
030: 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: package org.deegree.io.datastore.wfs;
044:
045: import java.io.ByteArrayOutputStream;
046: import java.io.IOException;
047: import java.io.InputStream;
048: import java.net.MalformedURLException;
049: import java.net.URL;
050: import java.util.ArrayList;
051: import java.util.HashMap;
052: import java.util.List;
053: import java.util.Map;
054: import java.util.concurrent.Callable;
055: import java.util.concurrent.CancellationException;
056:
057: import javax.xml.transform.TransformerException;
058:
059: import org.apache.commons.httpclient.HttpClient;
060: import org.apache.commons.httpclient.methods.PostMethod;
061: import org.apache.commons.httpclient.methods.StringRequestEntity;
062: import org.deegree.datatypes.QualifiedName;
063: import org.deegree.enterprise.WebUtils;
064: import org.deegree.framework.concurrent.ExecutionFinishedEvent;
065: import org.deegree.framework.concurrent.Executor;
066: import org.deegree.framework.log.ILogger;
067: import org.deegree.framework.log.LoggerFactory;
068: import org.deegree.framework.util.CharsetUtils;
069: import org.deegree.framework.xml.XMLFragment;
070: import org.deegree.framework.xml.XMLParsingException;
071: import org.deegree.framework.xml.XSLTDocument;
072: import org.deegree.i18n.Messages;
073: import org.deegree.io.datastore.Datastore;
074: import org.deegree.io.datastore.DatastoreException;
075: import org.deegree.io.datastore.DatastoreTransaction;
076: import org.deegree.io.datastore.schema.MappedFeatureType;
077: import org.deegree.model.crs.UnknownCRSException;
078: import org.deegree.model.feature.FeatureCollection;
079: import org.deegree.model.feature.FeatureFactory;
080: import org.deegree.model.feature.GMLFeatureCollectionDocument;
081: import org.deegree.ogcwebservices.OGCWebServiceException;
082: import org.deegree.ogcwebservices.OWSUtils;
083: import org.deegree.ogcwebservices.getcapabilities.InvalidCapabilitiesException;
084: import org.deegree.ogcwebservices.wfs.XMLFactory;
085: import org.deegree.ogcwebservices.wfs.capabilities.WFSCapabilities;
086: import org.deegree.ogcwebservices.wfs.capabilities.WFSCapabilitiesDocument;
087: import org.deegree.ogcwebservices.wfs.operation.GetFeature;
088: import org.deegree.ogcwebservices.wfs.operation.Query;
089: import org.deegree.ogcwebservices.wfs.operation.GetFeature.RESULT_TYPE;
090: import org.xml.sax.SAXException;
091:
092: /**
093: *
094: *
095: *
096: * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
097: * @author last edited by: $Author: aschmitz $
098: *
099: * @version $Revision: 10506 $, $Date: 2008-03-06 08:50:33 -0800 (Thu, 06 Mar 2008) $
100: */
101: public class CascadingWFSDatastore extends Datastore {
102:
103: ILogger LOG = LoggerFactory.getLogger(CascadingWFSDatastore.class);
104:
105: private static Map<URL, WFSCapabilities> wfsCapabilities;
106: static {
107: if (wfsCapabilities == null) {
108: wfsCapabilities = new HashMap<URL, WFSCapabilities>();
109: }
110: }
111:
112: @Override
113: public CascadingWFSAnnotationDocument getAnnotationParser() {
114: return new CascadingWFSAnnotationDocument();
115: }
116:
117: @Override
118: public void close() throws DatastoreException {
119: // is already closed
120: }
121:
122: @Override
123: public FeatureCollection performQuery(Query query,
124: MappedFeatureType[] rootFts, DatastoreTransaction context)
125: throws DatastoreException, UnknownCRSException {
126: return performQuery(query, rootFts);
127: }
128:
129: @Override
130: public FeatureCollection performQuery(Query query,
131: MappedFeatureType[] rootFts) throws DatastoreException,
132: UnknownCRSException {
133:
134: GetFeature getFeature = GetFeature.create("1.1.0", "ID",
135: RESULT_TYPE.RESULTS, "text/xml; subtype=gml/3.1.1", "",
136: query.getMaxFeatures(), query.getStartPosition(), -1,
137: -1, new Query[] { query });
138: XMLFragment gfXML = null;
139: try {
140: gfXML = XMLFactory.export(getFeature);
141: } catch (IOException e) {
142: LOG.logError(e.getMessage(), e);
143: throw new DatastoreException(e.getMessage());
144: } catch (XMLParsingException e) {
145: LOG.logError(e.getMessage(), e);
146: throw new DatastoreException(e.getMessage());
147: }
148:
149: // get URL that is target of a GetFeature request
150: CascadingWFSDatastoreConfiguration config = (CascadingWFSDatastoreConfiguration) this
151: .getConfiguration();
152: WFSDescription[] wfs = config.getWFSDescription();
153: List<Callable<FeatureCollection>> queryTasks = new ArrayList<Callable<FeatureCollection>>(
154: wfs.length);
155: int timeout = 0;
156: for (int i = 0; i < wfs.length; i++) {
157: QueryTask task = new QueryTask(gfXML, wfs[i]);
158: queryTasks.add(task);
159: timeout += wfs[i].getTimeout();
160: }
161:
162: List<ExecutionFinishedEvent<FeatureCollection>> finishedEvents = null;
163: try {
164: finishedEvents = Executor.getInstance()
165: .performSynchronously(queryTasks, timeout);
166: } catch (InterruptedException e) {
167: LOG.logError(e.getMessage(), e);
168: throw new DatastoreException(Messages
169: .getMessage("WFS_CASCDS_PERFORM_GF"), e);
170: }
171:
172: return mergeResults(getFeature.getId(), finishedEvents);
173: }
174:
175: /**
176: * Merges the results of the request subparts into one feature collection.
177: *
178: * @param fcid
179: * id of the new (result) feature collection
180: * @param finishedEvents
181: * @return feature collection containing all features from all responses
182: * @throws OGCWebServiceException
183: */
184: private FeatureCollection mergeResults(
185: String fcid,
186: List<ExecutionFinishedEvent<FeatureCollection>> finishedEvents)
187: throws DatastoreException {
188:
189: FeatureCollection result = null;
190:
191: try {
192: for (ExecutionFinishedEvent<FeatureCollection> event : finishedEvents) {
193: if (result == null) {
194: result = event.getResult();
195: } else {
196: result.addAll(event.getResult());
197: }
198: }
199: } catch (CancellationException e) {
200: LOG.logError(e.getMessage(), e);
201: String msg = Messages.getMessage("WFS_GET_FEATURE_TIMEOUT",
202: e.getMessage());
203: throw new DatastoreException(msg, e);
204: } catch (Throwable t) {
205: LOG.logError(t.getMessage(), t);
206: String msg = Messages.getMessage("WFS_GET_FEATURE_BACKEND",
207: t.getMessage());
208: throw new DatastoreException(msg, t);
209: }
210:
211: result.setId(fcid);
212: result.setAttribute("numberOfFeatures", "" + result.size());
213: return result;
214: }
215:
216: WFSCapabilities getWFSCapabilities(URL url)
217: throws DatastoreException {
218: String href = OWSUtils.validateHTTPGetBaseURL(url
219: .toExternalForm());
220: href = href
221: + "request=GetCapabilities&version=1.1.0&service=WFS";
222:
223: LOG.logDebug("requested capabilities: ", href);
224:
225: try {
226: url = new URL(href);
227: } catch (MalformedURLException e1) {
228: e1.printStackTrace();
229: }
230:
231: WFSCapabilities caps = wfsCapabilities.get(url);
232: if (caps == null) {
233: // access capabilities if not already has been loaded
234: WFSCapabilitiesDocument cd = new WFSCapabilitiesDocument();
235: try {
236: cd.load(url);
237: } catch (IOException e) {
238: LOG.logError(e.getMessage(), e);
239: throw new DatastoreException(e.getMessage());
240: } catch (SAXException e) {
241: LOG.logError(e.getMessage(), e);
242: throw new DatastoreException(e.getMessage());
243: }
244: try {
245: caps = (WFSCapabilities) cd.parseCapabilities();
246: } catch (InvalidCapabilitiesException e) {
247: LOG.logError(e.getMessage(), e);
248: throw new DatastoreException(e.getMessage());
249: }
250: wfsCapabilities.put(url, caps);
251: }
252: return caps;
253: }
254:
255: // ///////////////////////////////////////////////////////////////////////////
256: // inner classes
257: // ///////////////////////////////////////////////////////////////////////////
258:
259: /**
260: * Inner class for performing queries on a datastore.
261: */
262: private class QueryTask implements Callable<FeatureCollection> {
263:
264: private XMLFragment getFeature;
265:
266: private WFSDescription wfs;
267:
268: /**
269: *
270: * @param getFeature
271: * @param wfs
272: */
273: QueryTask(XMLFragment getFeature, WFSDescription wfs) {
274: this .getFeature = getFeature;
275: this .wfs = wfs;
276: }
277:
278: /**
279: * Performs the associated {@link Query} and returns the result.
280: *
281: * @return resulting feature collection
282: * @throws Exception
283: */
284: public FeatureCollection call() throws Exception {
285:
286: URL url = OWSUtils.getHTTPPostOperationURL(
287: getWFSCapabilities(wfs.getUrl()), GetFeature.class);
288:
289: // filter request if necessary
290: XSLTDocument inFilter = wfs.getInFilter();
291: if (inFilter != null) {
292: try {
293: getFeature = inFilter.transform(getFeature);
294: } catch (TransformerException e) {
295: LOG.logError(e.getMessage(), e);
296: throw new DatastoreException(e.getMessage());
297: }
298: }
299:
300: if (isFeatureTypeSupported(getFeature, wfs.getUrl())) {
301:
302: InputStream is = null;
303: FeatureCollection fc = null;
304: try {
305: // perform GetFeature request against cascaded WFS
306: HttpClient client = new HttpClient();
307: client = WebUtils.enableProxyUsage(client, url);
308: client.getHttpConnectionManager().getParams()
309: .setSoTimeout(wfs.getTimeout());
310: PostMethod post = new PostMethod(url
311: .toExternalForm());
312: StringRequestEntity se = new StringRequestEntity(
313: getFeature.getAsString(), "text/xml",
314: CharsetUtils.getSystemCharset());
315: post.setRequestEntity(se);
316: client.executeMethod(post);
317: is = post.getResponseBodyAsStream();
318: } catch (Exception e) {
319: throw new DatastoreException(Messages.getMessage(
320: "DATASTORE_WFS_ACCESS", url));
321: }
322:
323: // read result as GMLFeatureColllection
324: GMLFeatureCollectionDocument fcd = new GMLFeatureCollectionDocument(
325: true);
326: try {
327: fcd.load(is, url.toExternalForm());
328: } catch (Exception e) {
329: if (LOG.getLevel() == ILogger.LOG_DEBUG) {
330: ByteArrayOutputStream bos = new ByteArrayOutputStream(
331: 50000);
332: int c = 0;
333: while (c > -1) {
334: c = is.read();
335: bos.write(c);
336: }
337: byte[] b = bos.toByteArray();
338: bos.close();
339: System.out.println(new String(b));
340: }
341: LOG.logError(e.getMessage(), e);
342: throw new DatastoreException(e.getMessage());
343: } finally {
344: try {
345: is.close();
346: } catch (IOException shouldNeverHappen) {
347: // and is ignored
348: }
349: }
350:
351: // filter result if necessary
352: XSLTDocument outFilter = wfs.getOutFilter();
353: if (outFilter != null) {
354: try {
355: XMLFragment xml = outFilter.transform(fcd);
356: fcd = new GMLFeatureCollectionDocument();
357: fcd.setRootElement(xml.getRootElement());
358: } catch (TransformerException e) {
359: LOG.logError(e.getMessage(), e);
360: throw new DatastoreException(e.getMessage());
361: }
362: }
363: try {
364: fc = fcd.parse();
365: } catch (XMLParsingException e) {
366: LOG.logError(e.getMessage(), e);
367: throw new DatastoreException(e.getMessage());
368: }
369:
370: return fc;
371: }
372: return FeatureFactory.createFeatureCollection("ID", 1);
373: }
374:
375: /**
376: * @return true if the WFS reachable through the passed URL supports all feature types
377: * targeted by the passed GetFeature request.
378: *
379: * @param getFeature
380: * @param url
381: * @throws OGCWebServiceException
382: * @throws DatastoreException
383: */
384: private boolean isFeatureTypeSupported(XMLFragment getFeature,
385: URL url) throws OGCWebServiceException,
386: DatastoreException {
387:
388: WFSCapabilities caps = getWFSCapabilities(url);
389:
390: GetFeature gf = GetFeature.create("ID"
391: + System.currentTimeMillis(), getFeature
392: .getRootElement());
393: Query[] queries = gf.getQuery();
394: for (int i = 0; i < queries.length; i++) {
395: QualifiedName featureType = queries[i].getTypeNames()[0];
396: if (caps.getFeatureTypeList().getFeatureType(
397: featureType) == null) {
398: return false;
399: }
400: }
401: return true;
402: }
403: }
404: }
|