GeoServer简单介绍
GeoServer 是一个用 Java 编写的开源软件服务器,允许用户共享和编辑地理空间数据。 它专为互操作性而设计,可使用开放标准发布来自任何主要空间数据源的数据。 作为一个社区驱动的项目,GeoServer 由来自世界各地的不同个人和组织开发、测试和支持。 GeoServer 是开放地理空间联盟(OGC)网络地物服务(WFS)和网络覆盖服务(WCS)标准的参考实施,也是高性能认证的网络地图服务(WMS)、网络目录服务(CSW)和网络处理服务(WPS)的实施。 GeoServer 是地理空间网络的核心组成部分。
开放地理空间联盟 (OGC) 定义了一系列 Web 协议,这些协议都遵循类似的设计。OGC Open Web Services (OWS) 使用以下方法定义服务:
- 服务
- 版本
- 请求 - 服务
OWS服务定义
GeoServer提供了一个框架,用于接受这些请求并将其分派给适当的实现。这些服务是使用 jar 中包含的 Spring applicationContext.xml 文件为 Dispatcher 配置的。
例如gs-wfs-2.32.2.jar的applicationContext.xml 文件
其中部分代码如下
<!-- 这会创建一个服务描述符,允许 org.geoserver.ows.Dispatcher 定位它。 -->
<bean id="wfsService-1.0.0" class="org.geoserver.platform.Service">
<constructor-arg index="0" value="wfs"/>
<constructor-arg index="1" value="http://www.opengis.net/wfs"/>
<constructor-arg index="2" ref="wfsService"/>
<constructor-arg index="3" value="1.0.0"/>
<constructor-arg index="4">
<list>
<value>GetCapabilities</value>
<value>DescribeFeatureType</value>
<value>GetFeature</value>
<value>GetFeatureWithLock</value>
<value>LockFeature</value>
<value>Transaction</value>
</list>
</constructor-arg>
</bean>
<bean id="wfsService-1.1.0" class="org.geoserver.platform.Service">
<constructor-arg index="0" value="wfs"/>
<constructor-arg index="1" value="http://www.opengis.net/wfs"/>
<constructor-arg index="2" ref="wfsService"/>
<constructor-arg index="3" value="1.1.0"/>
<constructor-arg index="4">
<list>
<value>GetCapabilities</value>
<value>DescribeFeatureType</value>
<value>GetGmlObject</value>
<value>GetFeature</value>
<value>GetFeatureWithLock</value>
<value>LockFeature</value>
<value>Transaction</value>
</list>
</constructor-arg>
</bean>
<bean id="wfsService-2.0" class="org.geoserver.platform.Service">
<constructor-arg index="0" value="wfs"/>
<constructor-arg index="1" value="http://www.opengis.net/wfs/2.0"/>
<constructor-arg index="2" ref="wfsService20"/>
<constructor-arg index="3" value="2.0.0"/>
<constructor-arg index="4">
<list>
<value>GetCapabilities</value>
<value>DescribeFeatureType</value>
<value>GetFeature</value>
<value>GetFeatureWithLock</value>
<value>GetPropertyValue</value>
<value>LockFeature</value>
<value>Transaction</value>
<value>ListStoredQueries</value>
<value>DescribeStoredQueries</value>
<value>CreateStoredQuery</value>
<value>DropStoredQuery</value>
</list>
</constructor-arg>
</bean>
可以通过下面GET或POST请求访问这些服务
http://example.com/geoserver/wfs?
service=wfs&
version=2.0.0&
request=GetPropertyValue&
typeNames=topp:states&
valueReference=the_geom
<wfs:GetPropertyValue service='WFS' version='2.0.0'
xmlns:topp='http://www.openplans.org/topp'
xmlns:fes='http://www.opengis.net/fes/2.0'
xmlns:wfs='http://www.opengis.net/wfs/2.0'
valueReference='the_geom'>
<wfs:Query typeNames='topp:states'/>
</wfs:GetPropertyValue>
Dispatcher关键代码解释
(源码ows包下)
处理请求handleRequestInternal
protected ModelAndView handleRequestInternal(
HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws Exception {
// 预处理请求
preprocessRequest(httpRequest);
// 创建一个新的请求实例
Request request = new Request();
// 设置请求和响应
request.setHttpRequest(httpRequest);
request.setHttpResponse(httpResponse);
Service service = null;
try {
// 初始化请求,并允许回调覆盖它
request = init(request);
// 存储在线程本地变量中
REQUEST.set(request);
// 查找服务
try {
service = service(request);
} catch (Throwable t) {
// 处理异常
exception(t, null, request);
return null;
}
// 抛出任何未解决的错误
if (request.getError() != null) {
throw request.getError();
}
// 分派操作
Operation operation = dispatch(request, service);
request.setOperation(operation);
// 如果是SOAP请求,标记为SOAP
if (request.isSOAP()) {
flagAsSOAP(operation);
}
// 执行操作
Object result = execute(request, operation);
// 写入响应
if (result != null) {
response(result, request, operation);
}
} catch (Throwable t) {
// 让Spring安全异常流动,以便异常转换过滤器可以处理它们
if (isSecurityException(t)) throw (Exception) t;
exception(t, service, request);
} finally {
// 触发完成回调
fireFinishedCallback(request);
// 移除线程本地变量
REQUEST.remove();
}
return null;
}
用于处理传入的 HTTP 请求并生成响应
- 调用 `init(request)` 方法初始化请求对象,并将其存储在线程本地变量 `REQUEST` 中。
- 尝试查找并初始化服务对象 `service = service(request)`,如果发生异常,调用 `exception` 方法处理异常,并返回 `null`。
- 检查请求是否存在错误,如果有,则抛出该错误。
- 调用 `dispatch` 方法分派请求,获取操作对象 `operation`。
- 如果请求是 SOAP 请求,调用 `flagAsSOAP` 方法标记为 SOAP 请求。
- 执行操作 `execute(request, operation)` 并获取结果 `result`。
- 如果有结果,调用 `response` 方法写入响应。
- 捕获所有异常,如果是安全异常,重新抛出;否则调用 `exception` 方法处理异常。
- 最后调用 `fireFinishedCallback` 方法执行完成回调,并从线程本地变量中移除请求对象 `REQUEST.remove()`。
处理分发关键三步
- 查找服务
- 分派操作
- 执行操作
相关方法
查找服务service
Service service(Request req) throws Exception {
// 检查键值对(KVP)
if (req.getKvp() != null) {
req.setService(normalize(KvpUtils.getSingleValue(req.getKvp(), "service"))); // 规范化服务参数
req.setVersion(normalizeVersion(normalize(KvpUtils.getSingleValue(req.getKvp(), "version")))); // 规范化版本参数
req.setRequest(normalize(KvpUtils.getSingleValue(req.getKvp(), "request"))); // 规范化请求参数
req.setOutputFormat(normalize(KvpUtils.getSingleValue(req.getKvp(), "outputFormat"))); // 规范化输出格式参数
}
// 检查请求体
if (req.getInput() != null && "POST".equalsIgnoreCase(req.getHttpRequest().getMethod())) {
req = readOpPost(req); // 如果是POST请求,读取请求体
}
// 尝试从上下文中推断服务和请求
// JD: 为了符合cite规定,服务必须通过KVP或XML属性明确指定,
// 但实际上,上下文通常是推断服务或请求的好方法
String service = req.getService();
if ((service == null) || (req.getRequest() == null)) {
Map map = readOpContext(req); // 读取操作上下文
if (service == null) {
service = normalize((String) map.get("service")); // 从上下文中获取并规范化服务
if ((service != null) && !citeCompliant) {
req.setService(service); // 设置服务
}
}
if (req.getRequest() == null) {
req.setRequest(normalize((String) map.get("request"))); // 从上下文中获取并规范化请求
}
}
if (service == null) {
// 如果无法确定服务,抛出异常
throw new ServiceException("Could not determine service", "MissingParameterValue", "service");
}
// 从上下文加载服务描述符
Service serviceDescriptor = findService(service, req.getVersion(), req.getNamespace());
if (serviceDescriptor == null) {
// 向后兼容的黑客方法,尝试使用上下文而不是服务查找服务
if (req.getContext() != null) {
serviceDescriptor = findService(req.getContext(), req.getVersion(), req.getNamespace());
if (serviceDescriptor != null) {
// 找到服务,假设客户端使用<service>/<request>
if (req.getRequest() == null) {
req.setRequest(req.getService());
}
req.setService(req.getContext());
req.setContext(null);
}
}
if (serviceDescriptor == null) {
String msg = "No service: ( " + service + " )";
throw new ServiceException(msg, "InvalidParameterValue", "service"); // 抛出无效参数值异常
}
}
req.setServiceDescriptor(serviceDescriptor); // 设置服务描述符
return fireServiceDispatchedCallback(req, serviceDescriptor); // 调用服务分发回调
}
分派操作dispatch
Operation dispatch(Request req, Service serviceDescriptor) throws Throwable {
// 检查请求中是否包含操作请求,如果没有则抛出异常
if (req.getRequest() == null) {
String msg = "Could not determine geoserver request from http request " + req.getHttpRequest();
throw new ServiceException(msg, "MissingParameterValue", "request");
}
// 确保请求的操作存在
boolean exists = operationExists(req, serviceDescriptor);
// 如果请求中包含混合的键值对(KVP)和POST请求,并且信任请求体中的操作请求
if (!exists && req.getKvp().get("request") != null) {
req.setRequest(normalize(KvpUtils.getSingleValue(req.getKvp(), "request")));
exists = operationExists(req, serviceDescriptor);
}
// 基于服务和请求查找操作
Object serviceBean = serviceDescriptor.getService();
Method operation = OwsUtils.method(serviceBean.getClass(), req.getRequest());
// 如果操作不存在,抛出异常
if (operation == null || !exists) {
String msg = "No such operation " + req;
throw new ServiceException(msg, "OperationNotSupported", req.getRequest());
}
// 设置操作参数
Object[] parameters = new Object[operation.getParameterTypes().length];
for (int i = 0; i < parameters.length; i++) {
Class<?> parameterType = operation.getParameterTypes()[i];
// 检查是否为Servlet请求或响应类型
if (parameterType.isAssignableFrom(HttpServletRequest.class)) {
parameters[i] = req.getHttpRequest();
} else if (parameterType.isAssignableFrom(HttpServletResponse.class)) {
parameters[i] = req.getHttpResponse();
}
// 检查是否为输入或输出流类型
else if (parameterType.isAssignableFrom(InputStream.class)) {
parameters[i] = req.getHttpRequest().getInputStream();
} else if (parameterType.isAssignableFrom(OutputStream.class)) {
parameters[i] = req.getHttpResponse().getOutputStream();
} else {
// 检查是否为请求对象类型
Object requestBean = null;
Throwable t = null; // 用于跟踪异常
boolean kvpParsed = false; // 用于评估请求对象是否通过KVP解析
boolean xmlParsed = false; // 用于评估请求对象是否通过XML解析
// 如果请求包含KVP
if (req.getKvp() != null && req.getKvp().size() > 0) {
try {
requestBean = parseRequestKVP(parameterType, req);
kvpParsed = true;
} catch (Exception e) {
t = e; // 不立即终止,可能还有请求体需要解析
}
}
// 如果请求包含输入流
if (req.getInput() != null) {
requestBean = parseRequestXML(requestBean, req.getInput(), req);
xmlParsed = true;
}
// 如果未找到请求解析器,抛出异常
if (requestBean == null) {
if (t != null) {
throw t;
}
if (kvpParsed && xmlParsed || (!kvpParsed && !xmlParsed)) {
throw new ServiceException(
"Could not find request reader (either kvp or xml) for: "
+ parameterType.getName()
+ ", it might be that some request parameters are missing, "
+ "please check the documentation");
} else if (kvpParsed) {
throw new ServiceException("Could not parse the KVP for: " + parameterType.getName());
} else {
throw new ServiceException("Could not parse the XML for: " + parameterType.getName());
}
}
// GEOS-934和GEOS-1288
Method setBaseUrl = OwsUtils.setter(requestBean.getClass(), "baseUrl", String.class);
if (setBaseUrl != null) {
setBaseUrl.invoke(requestBean, new String[]{ResponseUtils.baseURL(req.getHttpRequest())});
}
// 检查请求对象是否包含service和version属性
if (requestBean != null) {
if (req.getService() == null) {
req.setService(lookupRequestBeanProperty(requestBean, "service", false));
}
if (req.getVersion() == null) {
req.setVersion(normalizeVersion(lookupRequestBeanProperty(requestBean, "version", false)));
}
if (req.getOutputFormat() == null) {
req.setOutputFormat(lookupRequestBeanProperty(requestBean, "outputFormat", true));
}
parameters[i] = requestBean;
}
}
}
// 如果处于CITE合规模式,执行额外检查以确保必需参数已指定
if (citeCompliant) {
if (!"GetCapabilities".equalsIgnoreCase(req.getRequest())) {
if (req.getVersion() == null) {
throw new ServiceException("Could not determine version", "MissingParameterValue", "version");
} else {
if (!req.getVersion().matches("[0-99].[0-99].[0-99]")) {
throw new ServiceException("Invalid version: " + req.getVersion(), "InvalidParameterValue", "version");
}
boolean found = false;
Version version = new Version(req.getVersion());
for (Service service : loadServices()) {
if (version.equals(service.getVersion())) {
found = true;
break;
}
}
if (!found) {
throw new ServiceException("Invalid version: " + req.getVersion(), "InvalidParameterValue", "version");
}
}
}
// 服务参数是所有请求的必需参数
if (req.getService() == null) {
throw new ServiceException("Could not determine service", "MissingParameterValue", "service");
}
}
Operation op = new Operation(req.getRequest(), serviceDescriptor, operation, parameters);
return fireOperationDispatchedCallback(req, op);
}
用于处理传入的请求并分派相应的操作。
执行操作execute
Object execute(Request req, Operation opDescriptor) throws Throwable {
// 获取服务描述符并从中获取服务Bean
Service serviceDescriptor = opDescriptor.getService();
Object serviceBean = serviceDescriptor.getService();
Object[] parameters = opDescriptor.getParameters(); // 获取操作参数
// 第5步:执行操作
Object result = null;
try {
if (serviceBean instanceof DirectInvocationService) {
// invokeDirect 期望操作按操作描述符中声明的名称调用,
// 尽管它曾用于匹配方法名称,但为了契约一致性,我们使用声明的操作名称。
String operationName = opDescriptor.getId();
result = ((DirectInvocationService) serviceBean)
.invokeDirect(operationName, parameters);
} else {
// 获取操作方法并调用它
Method operation = opDescriptor.getMethod();
result = operation.invoke(serviceBean, parameters);
}
} catch (Exception e) {
// 如果异常有原因,则抛出原因;否则,抛出异常本身
if (e.getCause() != null) {
throw e.getCause();
}
throw e;
}
// 触发操作执行后的回调并返回结果
return fireOperationExecutedCallback(req, opDescriptor, result);
}
参考链接:
https://docs.geoserver.org/latest/en/developer/programming-guide/ows-services/overview.html
https://docs.geoserver.org/latest/en/user/introduction/history.html
https://docs.geoserver.org/latest/en/developer/programming-guide/ows-services/implementing.html
https://www.osgeo.cn/geoserver-user-manual/services/wfs/reference.html