/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2008-2016, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotools.data.wfs; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.NoSuchElementException; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import javax.xml.namespace.QName; import net.opengis.wfs20.ListStoredQueriesResponseType; import net.opengis.wfs20.StoredQueryDescriptionType; import net.opengis.wfs20.StoredQueryListItemType; import org.geotools.data.store.ContentDataStore; import org.geotools.data.store.ContentEntry; import org.geotools.data.store.ContentFeatureSource; import org.geotools.data.wfs.internal.DescribeFeatureTypeRequest; import org.geotools.data.wfs.internal.DescribeFeatureTypeResponse; import org.geotools.data.wfs.internal.DescribeStoredQueriesRequest; import org.geotools.data.wfs.internal.DescribeStoredQueriesResponse; import org.geotools.data.wfs.internal.ListStoredQueriesRequest; import org.geotools.data.wfs.internal.ListStoredQueriesResponse; import org.geotools.data.wfs.internal.WFSClient; import org.geotools.data.wfs.internal.parsers.EmfAppSchemaParser; import org.geotools.factory.CommonFactoryFinder; import org.geotools.feature.NameImpl; import org.geotools.feature.type.FeatureTypeFactoryImpl; import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.feature.type.FeatureType; import org.opengis.feature.type.Name; import org.xml.sax.EntityResolver; import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.geom.impl.PackedCoordinateSequenceFactory; public class WFSDataStore extends ContentDataStore { public static final String STORED_QUERY_CONFIGURATION_HINT = "WFS_NG_STORED_QUERY_CONFIGURATION"; private final WFSClient client; private final Map<Name, QName> names; private final Map<QName, FeatureType> remoteFeatureTypes; private final Map<String, StoredQueryDescriptionType> storedQueryDescriptionTypes; private ReadWriteLock storedQueryDescriptionTypesLock; private ListStoredQueriesResponseType remoteStoredQueries; protected Map<String, String> configuredStoredQueries = new ConcurrentHashMap<String, String>(); public WFSDataStore(final WFSClient client) { this.client = client; this.names = new ConcurrentHashMap<Name, QName>(); this.remoteFeatureTypes = new ConcurrentHashMap<QName, FeatureType>(); this.storedQueryDescriptionTypes = new ConcurrentHashMap<String, StoredQueryDescriptionType>(); this.storedQueryDescriptionTypesLock = new ReentrantReadWriteLock(); // default factories setFilterFactory(CommonFactoryFinder.getFilterFactory(null)); setGeometryFactory(new GeometryFactory(PackedCoordinateSequenceFactory.DOUBLE_FACTORY)); setFeatureTypeFactory(new FeatureTypeFactoryImpl()); setFeatureFactory(CommonFactoryFinder.getFeatureFactory(null)); } /** * @see WFSDataStore#getInfo() */ @Override public WFSServiceInfo getInfo() { return client.getInfo(); } @Override protected WFSContentState createContentState(ContentEntry entry) { return new WFSContentState(entry); } /** * @see org.geotools.data.store.ContentDataStore#createTypeNames() */ @Override protected List<Name> createTypeNames() throws IOException { String namespaceURI = getNamespaceURI(); Set<QName> remoteTypeNames = client.getRemoteTypeNames(); List<Name> names = new ArrayList<Name>(remoteTypeNames.size()); for (QName remoteTypeName : remoteTypeNames) { String localTypeName = client.getConfig().localTypeName(remoteTypeName); Name typeName = new NameImpl(namespaceURI==null? remoteTypeName.getNamespaceURI() : namespaceURI, localTypeName); names.add(typeName); this.names.put(typeName, remoteTypeName); } for(Entry<String, String> e : configuredStoredQueries.entrySet()) { String name = e.getKey(); String storedQueryId = e.getValue(); Name typeName = new NameImpl(namespaceURI, name); names.add(typeName); this.names.put(typeName, getStoredQueryReturnType(storedQueryId)); } return names; } /** * @see WFSFeatureSource * @see WFSFeatureStore * @see WFSClient#supportsTransaction(QName) * @see org.geotools.data.store.ContentDataStore#createFeatureSource(org.geotools.data.store.ContentEntry) */ @Override protected ContentFeatureSource createFeatureSource(final ContentEntry entry) throws IOException { ContentFeatureSource source; if (!isStoredQuery(entry.getName())) { final QName remoteTypeName = getRemoteTypeName(entry.getName()); source = new WFSFeatureSource(entry, client); if (client.supportsTransaction(remoteTypeName)) { source = new WFSFeatureStore((WFSFeatureSource) source); } } else { String storedQueryId = configuredStoredQueries.get(entry.getName().getLocalPart()); StoredQueryDescriptionType desc = getStoredQueryDescriptionType(storedQueryId); source = new WFSStoredQueryFeatureSource(entry, client, desc); } return source; } private boolean isStoredQuery(Name name) { return configuredStoredQueries.containsKey(name.getLocalPart()); } public QName getRemoteTypeName(Name localTypeName) throws IOException { if (names.isEmpty()) { createTypeNames(); } QName qName = names.get(localTypeName); if (null == qName) { throw new NoSuchElementException(localTypeName.toString()); } return qName; } public ListStoredQueriesResponseType getStoredQueryListResponse() throws IOException { synchronized(this) { if (remoteStoredQueries == null) { ListStoredQueriesRequest request = client.createListStoredQueriesRequest(); ListStoredQueriesResponse response = client.issueRequest(request); remoteStoredQueries = response.getListStoredQueriesResponse(); } } return remoteStoredQueries; } public boolean supportsStoredQueries() { return client.supportsStoredQueries(); } public FeatureType getRemoteFeatureType(final QName remoteTypeName) throws IOException { FeatureType remoteFeatureType; final String lockObj = remoteTypeName.toString().intern(); synchronized (lockObj) { remoteFeatureType = remoteFeatureTypes.get(remoteTypeName); if (remoteFeatureType == null) { DescribeFeatureTypeRequest request = client.createDescribeFeatureTypeRequest(); request.setTypeName(remoteTypeName); DescribeFeatureTypeResponse response = client.issueRequest(request); remoteFeatureType = response.getFeatureType(); remoteFeatureTypes.put(remoteTypeName, remoteFeatureType); } } return remoteFeatureType; } public StoredQueryDescriptionType getStoredQueryDescriptionType(String storedQueryId) throws IOException { StoredQueryDescriptionType desc = null; storedQueryDescriptionTypesLock.readLock().lock(); desc = storedQueryDescriptionTypes.get(storedQueryId); storedQueryDescriptionTypesLock.readLock().unlock(); if (desc == null) { storedQueryDescriptionTypesLock.writeLock().lock(); // Make sure another thread has not retrieved this information while waiting for locks desc = storedQueryDescriptionTypes.get(storedQueryId); try { // If desc is still null, retrieve it if (desc == null) { DescribeStoredQueriesRequest request = client.createDescribeStoredQueriesRequest(); URI id; try { id = new URI(storedQueryId); } catch(URISyntaxException use) { throw new IOException(use); } request.getStoredQueryIds().add(id); DescribeStoredQueriesResponse response = client.issueRequest(request); desc = response.getStoredQueryDescriptions().get(0); storedQueryDescriptionTypes.put(storedQueryId, desc); } } finally { storedQueryDescriptionTypesLock.writeLock().unlock(); } } return desc; } public SimpleFeatureType getRemoteSimpleFeatureType(final QName remoteTypeName) throws IOException { final FeatureType remoteFeatureType = getRemoteFeatureType(remoteTypeName); final SimpleFeatureType remoteSimpleFeatureType; // remove GML properties remoteSimpleFeatureType = EmfAppSchemaParser.toSimpleFeatureType(remoteFeatureType); return remoteSimpleFeatureType; } public WFSClient getWfsClient() { return client; } public Name addStoredQuery(String localName, String storedQueryId) throws IOException { Name name = new NameImpl(namespaceURI, localName); try { configuredStoredQueries.put(localName, storedQueryId); // the new stored query might be overriding a previous definition removeEntry(name); getStoredQuerySchema(storedQueryId); this.names.put(name, getStoredQueryReturnType(storedQueryId)); return name; } catch (IOException e) { configuredStoredQueries.remove(localName); throw e; } } public QName getStoredQueryReturnType(String storedQueryId) throws IOException { ListStoredQueriesResponseType list = getStoredQueryListResponse(); for (StoredQueryListItemType query : list.getStoredQuery()) { if (query.getId().equals(storedQueryId)) { return query.getReturnFeatureType().get(0); } } throw new IOException("Unknown stored query "+storedQueryId); } public SimpleFeatureType getStoredQuerySchema(String storedQueryId) throws IOException { QName returnType = getStoredQueryReturnType(storedQueryId); return getRemoteSimpleFeatureType(returnType); } public Map<String, String> getConfiguredStoredQueries() { return configuredStoredQueries; } public void removeStoredQuery(String localName) { Name name = new NameImpl(namespaceURI, localName); this.names.remove(name); configuredStoredQueries.remove(localName); } public URL getCapabilitiesURL() { return client.getCapabilitiesURL(); } }