📄 dispatcher.java
字号:
/* Copyright (c) 2001 - 2007 TOPP - www.openplans.org. All rights reserved.
* This code is licensed under the GPL 2.0 license, availible at the root
* application directory.
*/
package org.geoserver.ows;
import org.acegisecurity.AcegiSecurityException;
import org.eclipse.emf.ecore.EObject;
import org.geoserver.ows.security.OperationInterceptor;
import org.geoserver.ows.util.EncodingInfo;
import org.geoserver.ows.util.OwsUtils;
import org.geoserver.ows.util.RequestUtils;
import org.geoserver.ows.util.XmlCharsetDetector;
import org.geoserver.platform.GeoServerExtensions;
import org.geoserver.platform.Operation;
import org.geoserver.platform.Service;
import org.geoserver.platform.ServiceException;
import org.geotools.util.Version;
import org.geotools.xml.EMFUtils;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.namespace.QName;
/**
* Dispatches an http request to an open web service (OWS).
* <p>
* An OWS request contains three bits of information:
* <ol>
* <li>The service being called
* <li>The operation of the service to execute
* <li>The version of the service ( optional )
* </ol>
* Additional, an OWS request can contain an arbitray number of additional
* parameters.
* </p>
* <p>
* An OWS request can be specified in two forms. The first form is known as "KVP"
* in which all the parameters come in the form of a set of key-value pairs.
* Commonly this type of request is made in an http "GET" request, the parameters
* being specified in the query string:
*
* <pre>
* <code>http://www.xyz.com/geoserver?service=someService&request=someRequest&version=X.Y.Z¶m1=...¶m2=...
* </pre>
*
* This type of request can also be made in a "POST" request in with a
* mime-type of "application/x-www-form-urlencoded".
* </p>
* <p>
* The second form is known as "XML" in which all the parameters come in the
* form of an xml document. This type of request is made in an http "POST"
* request.
*
* <pre>
* <code>
* <?xml version="1.0" encoding="UTF-8"?>
* <SomeRequest service="someService" version="X.Y.Z">
* <Param1>...</Param1>
* <Param2>...</Param2>
* ...
* </SomeRequest>
* </code>
* </pre>
* </p>
* <p>
* When a request is received, the <b>service</b> the <b>version</b> parameters
* are used to locate a service desciptor, an instance of {@link Service}. With
* the service descriptor, the <b>request</b> parameter is used to locate the
* operation of the service to call.
* </p>
*
* @author Justin Deoliveira, The Open Planning Project, jdeolive@openplans.org
*
*/
public class Dispatcher extends AbstractController {
/**
* Logging instance
*/
static Logger logger = org.geotools.util.logging.Logging.getLogger("org.geoserver.ows");
/** flag to control wether the dispatcher is cite compliant */
boolean citeCompliant = false;
/** The security interceptor to be used for authorization checks **/
OperationInterceptor securityInterceptor = null;
/**
* Sets the flag to control wether the dispatcher is cite compliante.
* <p>
* If set to <code>true</code>, the dispatcher with throw exceptions when
* it encounters something that is not 100% compliant with CITE standards.
* An example would be a request which specifies the servce in the context
* path: '.../geoserver/wfs?request=...' and not with the kvp '&service=wfs'.
* </p>
*
* @param citeCompliant <code>true</code> to set compliance,
* <code>false</code> to unset it.
*/
public void setCiteCompliant(boolean citeCompliant) {
this.citeCompliant = citeCompliant;
}
public boolean isCiteCompliant() {
return citeCompliant;
}
protected void preprocessRequest(HttpServletRequest request)
throws Exception {
//set the charset
Charset charSet = null;
try {
charSet = Charset.forName(request.getCharacterEncoding());
} catch (Exception e) {
//TODO: make this server settable
charSet = Charset.forName("UTF-8");
}
request.setCharacterEncoding(charSet.name());
}
protected ModelAndView handleRequestInternal(HttpServletRequest httpRequest,
HttpServletResponse httpResponse) throws Exception {
preprocessRequest(httpRequest);
//create a new request instance
Request request = new Request();
//set request / response
request.httpRequest = httpRequest;
request.httpResponse = httpResponse;
Service service = null;
try {
//initialize the request
init(request);
//find the service
try {
service = service(request);
} catch (Throwable t) {
exception(t, null, request);
return null;
}
//throw any outstanding errors
if (request.error != null) {
throw request.error;
}
//dispatch the operation
Operation operation = dispatch(request, service);
//execute it
Object result = execute(request, operation);
//write the response
if (result != null) {
response(result, request, operation);
}
} catch (AcegiSecurityException e) {
// make Acegi exceptions flow so that exception transformer filter can handle them
throw e;
} catch (Throwable t) {
exception(t, service, request);
}
return null;
}
Request init(Request request) throws ServiceException, IOException {
HttpServletRequest httpRequest = request.httpRequest;
//figure out method
request.get = "GET".equalsIgnoreCase(httpRequest.getMethod())
|| "application/x-www-form-urlencoded".equals(httpRequest.getContentType());
//create the kvp map
parseKVP(request);
if ( !request.get ) {
//wrap the input stream in a buffer input stream
request.input = reader(httpRequest);
//mark the input stream, support up to 2KB, TODO: make this configuratable
request.input.mark(2048);
if (logger.isLoggable(Level.FINE)) {
char[] req = new char[1024];
int read = request.input.read(req, 0, 1024);
if (read < 1024) {
logger.fine("Raw XML request starts with: " + new String(req));
} else {
logger.fine("Raw XML request starts with: " + new String(req) + "...");
}
request.input.reset();
}
}
return request;
}
BufferedReader reader(HttpServletRequest httpRequest)
throws IOException {
//create a buffer so we can reset the input stream
BufferedInputStream input = new BufferedInputStream(httpRequest.getInputStream());
input.mark(2048);
//create object to hold encoding info
EncodingInfo encoding = new EncodingInfo();
//call this method to set the encoding info
XmlCharsetDetector.getCharsetAwareReader(input, encoding);
//call this method to create the reader
Reader reader = XmlCharsetDetector.createReader(input, encoding);
//rest the input
input.reset();
//ensure the reader is a buffered reader
if (reader instanceof BufferedReader) {
return (BufferedReader) reader;
}
return new BufferedReader(reader);
}
Service service(Request req) throws Exception {
//check kvp
if (req.kvp != null) {
req.service = normalize((String) req.kvp.get("service"));
req.version = normalize((String) req.kvp.get("version"));
req.request = normalize((String) req.kvp.get("request"));
req.outputFormat = normalize((String) req.kvp.get("outputFormat"));
}
//check the body
if (req.input != null) {
Map xml = readOpPost(req.input);
if (req.service == null) {
req.service = normalize((String) xml.get("service"));
}
if (req.version == null) {
req.version = normalize((String) xml.get("version"));
}
if (req.request == null) {
req.request = normalize((String) xml.get("request"));
}
if (req.outputFormat == null) {
req.outputFormat = normalize((String) xml.get("outputFormat"));
}
}
//try to infer from context
//JD: for cite compliance, a service *must* be specified explicitley by
// either a kvp, or an xml attribute, however in reality the context
// is often a good way to infer the service or request
String service = req.service;
if ((service == null) || (req.request == null)) {
Map map = readOpContext(req.httpRequest);
if (service == null) {
service = normalize((String) map.get("service"));
if ((service != null) && !citeCompliant) {
req.service = service;
}
}
if (req.request == null) {
req.request = normalize((String) map.get("request"));
}
}
if (service == null) {
//give up
throw new ServiceException("Could not determine service", "MissingParameterValue",
"service");
}
//load from teh context
return findService(service, req.version);
}
String normalize(String value) {
if (value == null) {
return null;
}
if ("".equals(value.trim())) {
return null;
}
return value.trim();
}
Operation dispatch(Request req, Service serviceDescriptor)
throws Throwable {
if (req.request == null) {
String msg = "Could not determine geoserver request from http request " + req.httpRequest;
throw new ServiceException(msg, "MissingParameterValue", "request");
}
// lookup the operation, initial lookup based on (service,request)
Object serviceBean = serviceDescriptor.getService();
Method operation = OwsUtils.method(serviceBean.getClass(), req.request);
if (operation == null) {
String msg = "No such operation " + req;
throw new ServiceException(msg, "OperationNotSupported", req.request);
}
//step 4: setup the paramters
Object[] parameters = new Object[operation.getParameterTypes().length];
for (int i = 0; i < parameters.length; i++) {
Class parameterType = operation.getParameterTypes()[i];
//first check for servlet request and response
if (parameterType.isAssignableFrom(HttpServletRequest.class)) {
parameters[i] = req.httpRequest;
} else if (parameterType.isAssignableFrom(HttpServletResponse.class)) {
parameters[i] = req.httpResponse;
}
//next check for input and output
else if (parameterType.isAssignableFrom(InputStream.class)) {
parameters[i] = req.httpRequest.getInputStream();
} else if (parameterType.isAssignableFrom(OutputStream.class)) {
parameters[i] = req.httpResponse.getOutputStream();
} else {
//check for a request object
Object requestBean = null;
if (req.kvp != null) {
//use the kvp reader mechanism
requestBean = parseRequestKVP(parameterType, req);
}
if (req.input != null) {
//use the xml reader mechanism
requestBean = parseRequestXML(requestBean,req.input, req);
}
// GEOS-934 and GEOS-1288
Method setBaseUrl = OwsUtils.setter(requestBean.getClass(), "baseUrl", String.class);
if (setBaseUrl != null) {
setBaseUrl.invoke(requestBean, new String[] { RequestUtils.baseURL(req.httpRequest)});
}
// another couple of thos of those lovley cite things, version+service has to specified for
// non capabilities request, so if we dont have either thus far, check the request
// objects to try and find one
// TODO: should make this configurable
if (requestBean != null) {
//if we dont have a version thus far, check the request object
if (req.service == null) {
req.service = lookupRequestBeanProperty(requestBean, "service", false);
}
if (req.version == null) {
req.version = lookupRequestBeanProperty(requestBean, "version", false);
}
if (req.outputFormat == null) {
req.outputFormat = lookupRequestBeanProperty(requestBean, "outputFormat",
true);
}
parameters[i] = requestBean;
}
}
}
//if we are in cite compliant mode, do some additional checks to make
// sure the "mandatory" parameters are specified, even though we
// succesfully dispatched the request.
if (citeCompliant) {
if (!"GetCapabilities".equalsIgnoreCase(req.request)) {
if (req.version == null) {
//must be a version on non-capabilities requests
throw new ServiceException("Could not determine version",
"MissingParameterValue", "version");
} else {
//version must be valid
if (!req.version.matches("[0-99].[0-99].[0-99]")) {
throw new ServiceException("Invalid version: " + req.version,
"InvalidParameterValue", "version");
}
//make sure the versoin actually exists
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -