Skip to content

Commit

Permalink
GEOS-4854,GEOS-4899 - handling reflective describefeaturetype calls i…
Browse files Browse the repository at this point in the history
…nline, rather than as separate request, deals with deadlocking issues

git-svn-id: https://svn.codehaus.org/geoserver/trunk@16662 ef1d6e69-97e5-0310-af46-8a06194da32a
  • Loading branch information
jdeolive committed Dec 20, 2011
1 parent 9d3a297 commit 4bd35f0
Show file tree
Hide file tree
Showing 10 changed files with 236 additions and 26 deletions.
2 changes: 1 addition & 1 deletion src/community/wfsv/src/main/java/applicationContext.xml
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@

<!-- 1.0 wfs readers handling extended elements -->
<bean id="wfs-x-xmlReader-1.0.0" class="org.geoserver.wfsv.xml.v1_0_0.WfsXmlReader" abstract="true">
<constructor-arg ref="catalog"/>
<constructor-arg ref="geoServer"/>
<constructor-arg ref="wfsvXmlConfiguration-1.0"/>
</bean>
<bean id="wfs-x-TransactionXmlReader-1.0.0" parent="wfs-x-xmlReader-1.0.0">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*/
package org.geoserver.wfsv.xml.v1_0_0;

import org.geoserver.catalog.Catalog;
import org.geoserver.config.GeoServer;
import org.geoserver.platform.ExtensionPriority;
import org.geotools.xml.Configuration;

Expand All @@ -15,8 +15,8 @@
*/
public class WfsXmlReader extends org.geoserver.wfs.xml.v1_0_0.WfsXmlReader implements ExtensionPriority {

public WfsXmlReader(String element, Configuration configuration, Catalog catalog) {
super(element, configuration, catalog, "wfsv");
public WfsXmlReader(String element, Configuration configuration, GeoServer geoServer) {
super(element, configuration, geoServer, "wfsv");
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/wfs/src/main/java/applicationContext.xml
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,7 @@
<!-- 1.0 xml readers -->
<bean id="xmlReader-1.0.0" class="org.geoserver.wfs.xml.v1_0_0.WfsXmlReader" abstract="true">
<constructor-arg index="1" ref="xmlConfiguration-1.0"/>
<constructor-arg index="2" ref="catalog"/>
<constructor-arg index="2" ref="geoServer"/>
</bean>
<bean id="wfsGetCapabilitiesXmlReader"
class="org.geoserver.wfs.xml.v1_0_0.WfsXmlReader" parent="xmlReader-1.0.0">
Expand Down
4 changes: 4 additions & 0 deletions src/wfs/src/main/java/org/geoserver/wfs/WFSInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ public static Version negotiate(String ver) {
}
return V_20;
}

public static Version latest() {
return V_20;
}
};

static enum Operation {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ public String getBaseUrl() {
return eGet(adaptee, "baseUrl", String.class);
}

public void setBaseUrl(String baseUrl) {
eSet(adaptee, "baseUrl", baseUrl);
}

public String getVersion() {
return eGet(adaptee, "version", String.class);
}
Expand Down
202 changes: 202 additions & 0 deletions src/wfs/src/main/java/org/geoserver/wfs/xml/WFSURIHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
/*
* Copyright (c) 2001 - 2011 TOPP - www.openplans.org. All rights reserved.
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.wfs.xml;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.impl.URIHandlerImpl;
import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.FeatureTypeInfo;
import org.geoserver.config.GeoServer;
import org.geoserver.ows.util.KvpMap;
import org.geoserver.ows.util.KvpUtils;
import org.geoserver.platform.Operation;
import org.geoserver.platform.Service;
import org.geoserver.wfs.DescribeFeatureType;
import org.geoserver.wfs.WFSInfo;
import org.geoserver.wfs.kvp.DescribeFeatureTypeKvpRequestReader;
import org.geoserver.wfs.request.DescribeFeatureTypeRequest;
import org.geoserver.wfs.xml.v1_1_0.XmlSchemaEncoder;
import org.geotools.util.logging.Logging;

/**
* URI handler that handles reflective references back to the server to avoid processing them in
* a separate request.
*/
public class WFSURIHandler extends URIHandlerImpl {

static final Logger LOGGER = Logging.getLogger(WFSURIHandler.class);

static final Boolean DISABLED;
static {
//check for disabled flag
DISABLED = Boolean.getBoolean(WFSURIHandler.class.getName()+".disabled");
}

static final List<InetAddress> ADDRESSES = new ArrayList<InetAddress>();
static {
if (!DISABLED) {
// in order to determine if a request is reflective we need to know what all the
// addresses and hostnames that we are addressable on. Since hostnames are expensive we
// do it once and cache the results
Enumeration<NetworkInterface> e = null;
try {
e = NetworkInterface.getNetworkInterfaces();
} catch (SocketException ex) {
LOGGER.log(Level.WARNING, "Unable to determine network interface info", ex);
}
while(e != null && e.hasMoreElements()) {
NetworkInterface ni = e.nextElement();
Enumeration<InetAddress> f = ni.getInetAddresses();
while(f.hasMoreElements()) {
InetAddress add = f.nextElement();

//do the hostname lookup, these are cached after the first call so we only pay the
// price now
add.getHostName();
ADDRESSES.add(add);
}
}
}
}

GeoServer geoServer;

public WFSURIHandler(GeoServer geoServer) {
this.geoServer = geoServer;
}

@Override
public boolean canHandle(URI uri) {
if (DISABLED) return false;

//check if this uri is a reflective one
if (uriIsReflective(uri)) {
// it is, check the query string to determine if it is a DescribeFeatureType request
String q = uri.query();
if (q != null && !"".equals(q.trim())) {
KvpMap kv = parseQueryString(q);

if ("DescribeFeatureType".equalsIgnoreCase((String)kv.get("REQUEST"))) {
return true;
}
}
}
return false;
}

private KvpMap parseQueryString(String q) {
return KvpUtils.normalize(KvpUtils.parseQueryString("?" + q));
}

private boolean uriIsReflective(URI uri) {
//TODO: this doesn't take into account the port... or even a different geoserver running
// on the same port but in a different application, regardless in that situation the handling
// of the DFT request will likely fail, falling back to default behavior

//first check the proxy uri if there is one
String proxyBaseUrl = geoServer.getGlobal().getProxyBaseUrl();
if (proxyBaseUrl != null) {
try {
URI proxyBaseUri = URI.createURI(proxyBaseUrl);
if(uri.host().equals(proxyBaseUri.host())) {
return true;
}
}
catch(IllegalArgumentException e) {
LOGGER.fine("Unable to parse proxy base url to a uri: " + proxyBaseUrl);
}
}

//check the network interfaces to see if the host matches
for (InetAddress add : ADDRESSES) {
if (uri.host().equals(add.getHostAddress()) || uri.host().equals(add.getHostName())) {
return true;
}
}
return false;
}

@Override
public InputStream createInputStream(URI uri, Map<?, ?> options) throws IOException {
Catalog catalog = geoServer.getCatalog();
try {
KvpMap kv = parseQueryString(uri.query());

//dispatch the correct describe feature type reader
WFSInfo.Version ver = WFSInfo.Version.negotiate((String)kv.get("VERSION"));
DescribeFeatureTypeKvpRequestReader dftReqReader = null;
switch(ver) {
case V_10:
case V_11:
dftReqReader = new DescribeFeatureTypeKvpRequestReader(catalog);
break;
default:
dftReqReader =
new org.geoserver.wfs.kvp.v2_0.DescribeFeatureTypeKvpRequestReader(catalog);
}

//parse the key value pairs
KvpMap parsed = new KvpMap(kv);
KvpUtils.parse(parsed);

//create/read the request object
DescribeFeatureTypeRequest request = DescribeFeatureTypeRequest.adapt(
dftReqReader.read(dftReqReader.createRequest(), parsed, kv));

//set the base url
//TODO: should this be run through the url mangler? not sure since the uri should
// already be "proxified"
request.setBaseUrl(uri.scheme() + "://" + uri.host() + ":" + uri.port() + uri.path());

//dispatch the dft operation
DescribeFeatureType dft =
new DescribeFeatureType(geoServer.getService(WFSInfo.class), catalog);
FeatureTypeInfo[] featureTypes = dft.run(request);

//generate the response
XmlSchemaEncoder schemaEncoder = null;
switch(ver) {
case V_10:
schemaEncoder = new XmlSchemaEncoder.V10(geoServer);
break;
case V_11:
schemaEncoder = new XmlSchemaEncoder.V11(geoServer);
break;
case V_20:
default:
schemaEncoder = new XmlSchemaEncoder.V20(geoServer);
}

//build a "dummy" operation descriptor and call the encoder
Operation op = new Operation("DescribeFeatureType", new Service("WFS",null,null,null),
null, new Object[]{request.getAdaptee()});
ByteArrayOutputStream bout = new ByteArrayOutputStream();
schemaEncoder.write(featureTypes, bout, op);

return new ByteArrayInputStream(bout.toByteArray());
} catch (Exception e) {
LOGGER.log(Level.WARNING, "Unable to handle DescribeFeatureType uri: " + uri, e);
}

//fall back on regular behaviour
return super.createInputStream(uri, options);
}

}
6 changes: 4 additions & 2 deletions src/wfs/src/main/java/org/geoserver/wfs/xml/WFSXmlUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
*/
public class WFSXmlUtils {

public static void initRequestParser(Parser parser, WFSInfo wfs, Catalog catalog, Map kvp) {
public static void initRequestParser(Parser parser, WFSInfo wfs, GeoServer geoServer, Map kvp) {
//check the strict flag to determine if we should validate or not
Boolean strict = (Boolean) kvp.get("strict");
if ( strict == null ) {
Expand All @@ -46,7 +46,9 @@ public static void initRequestParser(Parser parser, WFSInfo wfs, Catalog catalog
strict = Boolean.TRUE;
}
parser.setValidating(strict.booleanValue());

parser.getURIHandlers().add(new WFSURIHandler(geoServer));

Catalog catalog = geoServer.getCatalog();
//"inject" namespace mappings
List<NamespaceInfo> namespaces = catalog.getNamespaces();
for ( NamespaceInfo ns : namespaces ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@

import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.NamespaceInfo;
import org.geoserver.config.GeoServer;
import org.geoserver.ows.XmlRequestReader;
import org.geoserver.wfs.WFSException;
import org.geoserver.wfs.xml.WFSURIHandler;
import org.geotools.util.Version;
import org.geotools.xml.Configuration;
import org.geotools.xml.Parser;
Expand All @@ -32,23 +34,24 @@ public class WfsXmlReader extends XmlRequestReader {
*/
Configuration configuration;
/**
* Catalog, to access namespaces
* geoserver configuration
*/
Catalog catalog;
GeoServer geoServer;

public WfsXmlReader(String element, Configuration configuration, Catalog catalog) {
this(element, configuration, catalog, "wfs");
public WfsXmlReader(String element, Configuration configuration, GeoServer geoServer) {
this(element, configuration, geoServer, "wfs");
}

protected WfsXmlReader(String element, Configuration configuration, Catalog catalog, String serviceId) {
protected WfsXmlReader(String element, Configuration configuration, GeoServer geoServer, String serviceId) {
super(new QName(WFS.NAMESPACE, element), new Version("1.0.0"), serviceId);
this.configuration = configuration;
this.catalog = catalog;
this.geoServer = geoServer;
}

public Object read(Object request, Reader reader, Map kvp) throws Exception {
//TODO: refactor this method to use WFSXmlUtils

Catalog catalog = geoServer.getCatalog();

//check the strict flag to determine if we should validate or not
Boolean strict = (Boolean) kvp.get("strict");
if ( strict == null ) {
Expand All @@ -69,7 +72,8 @@ public Object read(Object request, Reader reader, Map kvp) throws Exception {
}
//set validation based on strict or not
parser.setValidating(strict.booleanValue());

parser.getURIHandlers().add(new WFSURIHandler(geoServer));

//parse
Object parsed = parser.parse(reader);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,17 @@
package org.geoserver.wfs.xml.v1_1_0;

import java.io.Reader;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.xml.namespace.QName;

import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.NamespaceInfo;
import org.geoserver.config.GeoServer;
import org.geoserver.ows.XmlRequestReader;
import org.geoserver.wfs.WFSException;
import org.geoserver.wfs.WFSInfo;
import org.geoserver.wfs.xml.WFSXmlUtils;
import org.geotools.util.Version;
import org.geotools.xml.Configuration;
import org.geotools.xml.Parser;
import org.xml.sax.InputSource;

/**
* Xml reader for wfs 1.1.0 xml requests.
Expand All @@ -42,9 +36,9 @@ public class WfsXmlReader extends XmlRequestReader {
Configuration configuration;

/**
* The catalog, used to access namespaces
* geoserver configuartion
*/
Catalog catalog;
GeoServer geoServer;

public WfsXmlReader(String element, GeoServer gs, Configuration configuration) {
this(element, gs, configuration, "wfs");
Expand All @@ -53,8 +47,8 @@ public WfsXmlReader(String element, GeoServer gs, Configuration configuration) {
protected WfsXmlReader(String element, GeoServer gs, Configuration configuration, String serviceId) {
super(new QName(org.geoserver.wfs.xml.v1_1_0.WFS.NAMESPACE, element), new Version("1.1.0"),
serviceId);
this.geoServer = gs;
this.wfs = gs.getService( WFSInfo.class );
this.catalog = gs.getCatalog();
this.configuration = configuration;
}

Expand All @@ -63,7 +57,7 @@ public Object read(Object request, Reader reader, Map kvp) throws Exception {
configuration.getProperties().add(Parser.Properties.PARSE_UNKNOWN_ELEMENTS);

Parser parser = new Parser(configuration);
WFSXmlUtils.initRequestParser(parser, wfs, catalog, kvp);
WFSXmlUtils.initRequestParser(parser, wfs, geoServer, kvp);

Object parsed = WFSXmlUtils.parseRequest(parser, reader, wfs);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public Object read(Object request, Reader reader, Map kvp) throws Exception {

WFSInfo wfs = wfs();

WFSXmlUtils.initRequestParser(parser, wfs, gs.getCatalog(), kvp);
WFSXmlUtils.initRequestParser(parser, wfs, gs, kvp);
Object parsed = null;
try {
parsed = WFSXmlUtils.parseRequest(parser, reader, wfs);
Expand Down

0 comments on commit 4bd35f0

Please sign in to comment.