/*
* Geotoolkit - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2009, Geomatys
*
* 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.geotoolkit.data.wfs;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Level;
import org.apache.sis.storage.DataStoreException;
import org.apache.sis.storage.IllegalNameException;
import org.geotoolkit.client.AbstractFeatureClient;
import org.geotoolkit.client.Client;
import org.geotoolkit.data.FeatureReader;
import org.geotoolkit.data.FeatureStore;
import org.geotoolkit.data.FeatureStoreRuntimeException;
import org.geotoolkit.data.FeatureWriter;
import org.geotoolkit.data.query.Query;
import org.geotoolkit.data.query.QueryCapabilities;
import org.geotoolkit.data.session.Session;
import org.geotoolkit.data.wfs.v110.*;
import org.geotoolkit.factory.Hints;
import org.geotoolkit.parameter.Parameters;
import org.geotoolkit.security.ClientSecurity;
import org.geotoolkit.storage.StorageListener;
import org.geotoolkit.version.Version;
import org.geotoolkit.version.VersionControl;
import org.geotoolkit.version.VersioningException;
import org.geotoolkit.wfs.xml.WFSBindingUtilities;
import org.geotoolkit.wfs.xml.WFSCapabilities;
import org.geotoolkit.wfs.xml.WFSVersion;
import org.opengis.util.GenericName;
import org.geotoolkit.storage.DataStores;
import org.opengis.feature.Feature;
import org.opengis.feature.FeatureType;
import org.opengis.feature.MismatchedFeatureException;
import org.opengis.filter.Filter;
import org.opengis.filter.identity.FeatureId;
import org.opengis.geometry.Envelope;
import org.opengis.parameter.ParameterValueGroup;
/**
* WFS server, used to aquiere capabilites and requests objects.
*
* @author Johann Sorel (Geomatys)
* @module
*/
public class WebFeatureClient extends AbstractFeatureClient implements Client {
/**
* Default timeout (in milliseconds).
*
* @todo Should be a parameter.
*/
private static final long TIMEOUT = 60000;
private volatile WFSCapabilities capabilities;
private WFSFeatureStore store; //created when needed
public WebFeatureClient(final URL serverURL, final String version) {
this(serverURL,null,version);
}
public WebFeatureClient(final URL serverURL, final ClientSecurity security, final String version) {
this(create(WFSFeatureStoreFactory.PARAMETERS_DESCRIPTOR, serverURL, security));
if(version.equals("1.1.0")){
Parameters.getOrCreate(WFSFeatureStoreFactory.VERSION, parameters).setValue(version);
}else{
throw new IllegalArgumentException("unknowned version : "+ version);
}
Parameters.getOrCreate(WFSFeatureStoreFactory.POST_REQUEST, parameters).setValue(false);
}
public WebFeatureClient(final URL serverURL, final ClientSecurity security, final WFSVersion version, final boolean usePost) {
this(create(WFSFeatureStoreFactory.PARAMETERS_DESCRIPTOR, serverURL, security));
if(version == null){
throw new IllegalArgumentException("unknowned version : "+ version);
}
Parameters.getOrCreate(WFSFeatureStoreFactory.VERSION, parameters).setValue(version.getCode());
Parameters.getOrCreate(WFSFeatureStoreFactory.POST_REQUEST, parameters).setValue(usePost);
}
public WebFeatureClient(final ParameterValueGroup params) {
super(params);
Parameters.getOrCreate(WFSFeatureStoreFactory.VERSION, parameters).setValue("1.1.0");
parameters.parameter(WFSFeatureStoreFactory.POST_REQUEST.getName().getCode());
}
@Override
public WFSFeatureStoreFactory getFactory() {
return (WFSFeatureStoreFactory)DataStores.getFactoryById(WFSFeatureStoreFactory.NAME);
}
public WFSVersion getVersion(){
return WFSVersion.fromCode(Parameters.value(WFSFeatureStoreFactory.VERSION, parameters));
}
public boolean getUsePost(){
return Parameters.value(WFSFeatureStoreFactory.POST_REQUEST, parameters);
}
public boolean getLongitudeFirst(){
return Parameters.getOrCreate(WFSFeatureStoreFactory.LONGITUDE_FIRST, parameters).booleanValue();
}
private synchronized FeatureStore getStore() {
if(store == null){
store = new WFSFeatureStore(this);
}
return store;
}
/**
* @return The WFS server capabilities.
* @throws WebFeatureException if an error occurred while querying the capabilities from the server.
*/
public WFSCapabilities getCapabilities() throws WebFeatureException {
WFSCapabilities cap = capabilities;
if (cap != null) {
return cap;
}
// Thread to prevent infinite request on a server
// TODO: This is costly - we should use an other mechanism.
final AtomicReference<Exception> error = new AtomicReference<>();
final Thread thread = new Thread() {
@Override
public void run() {
try {
capabilities = (WFSCapabilities) WFSBindingUtilities.unmarshall(createGetCapabilities().getResponseStream(), getVersion());
} catch (Exception ex) {
error.set(ex);
}
}
};
thread.start();
final long start = System.currentTimeMillis();
try {
thread.join(TIMEOUT);
} catch (InterruptedException ex) {
// Someone doesn't want to let us sleep. Go back to work.
}
cap = capabilities;
if (cap == null) {
final Exception cause = error.get();
if (cause == null) {
throw new WebFeatureException("TimeOut error, the server takes too much time to answer.");
}
String message;
try {
message = "Can not parse server answer at URL " + createGetCapabilities().getURL().toString();
} catch (MalformedURLException ex1) {
message = "Malformed URL, can not get server answer.";
}
throw new WebFeatureException(message, cause);
}
return cap;
}
/**
* Create a getCapabilities request.
* @return GetCapabilitiesRequest : getCapabilities request.
*/
public GetCapabilitiesRequest createGetCapabilities() {
switch (getVersion()) {
case v110:
return new GetCapabilities110(serverURL.toString(),getClientSecurity());
default:
throw new IllegalArgumentException("Version was not defined");
}
}
/**
* Create a describe feature request
* @return DescribeFeatureTypeRequest : describe feature request.
*/
public DescribeFeatureTypeRequest createDescribeFeatureType(){
switch (getVersion()) {
case v110:
return new DescribeFeatureType110(serverURL.toString(),getClientSecurity());
default:
throw new IllegalArgumentException("Version was not defined");
}
}
/**
* Create a get feature request
* @return GetFeatureRequest : get feature request.
*/
public GetFeatureRequest createGetFeature(){
switch (getVersion()) {
case v110:
return new GetFeature110(serverURL.toString(),getClientSecurity());
default:
throw new IllegalArgumentException("Version was not defined");
}
}
/**
* Create a transaction request
* @return TransactionRequest : transaction request.
*/
public TransactionRequest createTransaction(){
switch (getVersion()) {
case v110:
return new Transaction110(serverURL.toString(),getClientSecurity());
default:
throw new IllegalArgumentException("Version was not defined");
}
}
public Insert createInsertElement(){
switch (getVersion()) {
case v110:
return new Insert110();
default:
throw new IllegalArgumentException("Version was not defined");
}
}
public Update createUpdateElement(){
switch (getVersion()) {
case v110:
return new Update110();
default:
throw new IllegalArgumentException("Version was not defined");
}
}
public Delete createDeleteElement(){
switch (getVersion()) {
case v110:
return new Delete110();
default:
throw new IllegalArgumentException("Version was not defined");
}
}
public Native createNativeElement(){
switch (getVersion()) {
case v110:
return new Native110();
default:
throw new IllegalArgumentException("Version was not defined");
}
}
////////////////////////////////////////////////////////////////////////////
// FeatureStore methods ///////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
@Override
public Session createSession(boolean asynchrone, Version version) {
return getStore().createSession(asynchrone,version);
}
@Override
public VersionControl getVersioning(String typeName) throws VersioningException {
return store.getVersioning(typeName);
}
@Override
public Set<GenericName> getNames() throws DataStoreException {
return getStore().getNames();
}
@Override
public void createFeatureType(FeatureType featureType) throws DataStoreException {
getStore().createFeatureType(featureType);
}
@Override
public void updateFeatureType(FeatureType featureType) throws DataStoreException {
getStore().updateFeatureType(featureType);
}
@Override
public void deleteFeatureType(String typeName) throws DataStoreException {
getStore().deleteFeatureType(typeName);
}
@Override
public FeatureType getFeatureType(String typeName) throws DataStoreException {
return getStore().getFeatureType(typeName);
}
@Override
public FeatureType getFeatureType(Query query) throws DataStoreException, MismatchedFeatureException {
return getStore().getFeatureType(query);
}
@Override
public boolean isWritable(String typeName) throws DataStoreException {
return getStore().isWritable(typeName);
}
@Override
public QueryCapabilities getQueryCapabilities() {
return getStore().getQueryCapabilities();
}
@Override
public long getCount(Query query) throws DataStoreException {
return getStore().getCount(query);
}
@Override
public Envelope getEnvelope(Query query) throws DataStoreException {
return getStore().getEnvelope(query);
}
@Override
public List<FeatureId> addFeatures(String groupName, Collection<? extends Feature> newFeatures, Hints hints) throws DataStoreException {
return getStore().addFeatures(groupName, newFeatures, hints);
}
@Override
public void updateFeatures(String groupName, Filter filter, Map<String, ? extends Object> values) throws DataStoreException {
getStore().updateFeatures(groupName, filter, values);
}
@Override
public void removeFeatures(String groupName, Filter filter) throws DataStoreException {
getStore().removeFeatures(groupName, filter);
}
@Override
public FeatureReader getFeatureReader(Query query) throws DataStoreException {
return getStore().getFeatureReader(query);
}
@Override
public FeatureWriter getFeatureWriter(Query query) throws DataStoreException {
return getStore().getFeatureWriter(query);
}
@Override
public void close() throws DataStoreException {
getStore().close();
}
@Override
public void addStorageListener(StorageListener listener) {
getStore().addStorageListener(listener);
}
@Override
public void removeStorageListener(StorageListener listener) {
getStore().removeStorageListener(listener);
}
@Override
public void refreshMetaModel() throws DataStoreException {
getStore().refreshMetaModel();
}
}