GeoServer Dispatcher处理分发代码

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

以下是使用 JavaGeoTools 库将 PBF 文件发布到 GeoServer 的示例代码: ``` import org.geotools.data.DataStore; import org.geotools.data.DataStoreFinder; import org.geotools.data.simple.SimpleFeatureSource; import org.geotools.data.simple.SimpleFeatureStore; import org.geotools.feature.FeatureCollection; import org.geotools.feature.FeatureIterator; import org.geotools.geojson.feature.FeatureJSON; import org.geotools.geometry.jts.JTSFactoryFinder; import org.locationtech.jts.geom.GeometryFactory; import org.locationtech.jts.geom.Point; import org.locationtech.jts.geom.Polygon; import org.locationtech.jts.io.ParseException; import org.locationtech.jts.io.WKTReader; import org.opengis.feature.Feature; import org.opengis.feature.Property; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.HashMap; import java.util.Map; public class PublishPBFToGeoServer { public static void main(String[] args) throws IOException, ParseException { // 定义 PBF 文件路径和 GeoServer 数据存储名称 String pbfFilePath = "path/to/pbf/file.osm.pbf"; String dataStoreName = "my_datastore"; // 创建 DataStore 连接 Map<String, Object> connectionParameters = new HashMap<>(); connectionParameters.put("url", "http://localhost:8080/geoserver"); connectionParameters.put("user", "admin"); connectionParameters.put("passwd", "geoserver"); connectionParameters.put("dbtype", "postgis"); connectionParameters.put("encodeFunctions", "false"); // 针对 PostGIS DataStore dataStore = DataStoreFinder.getDataStore(connectionParameters); // 获取 PBF 文件中的数据 File pbfFile = new File(pbfFilePath); FileInputStream inputStream = new FileInputStream(pbfFile); PbfReader reader = new PbfReader(inputStream); FeatureCollection features = reader.read(); // 创建 FeatureType (根据 PBF 文件中的数据特征) SimpleFeatureType featureType = createFeatureType(features); // 创建数据存储 dataStore.createSchema(featureType); // 获取 FeatureSource 和 FeatureStore SimpleFeatureSource featureSource = dataStore.getFeatureSource(featureType.getTypeName()); SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource; // 将 FeatureCollection 转换为 SimpleFeature 并添加到数据存储中 FeatureIterator iterator = features.features(); while (iterator.hasNext()) { Feature feature = iterator.next(); SimpleFeature simpleFeature = createSimpleFeature(feature, featureType); featureStore.addFeatures(DataUtilities.collection(simpleFeature)); } // 发布数据存储到 GeoServer String workspaceName = "my_workspace"; String storeTypeName = "my_store_type"; dataStore.createSchema(featureType); Map<String, Object> storeParams = new HashMap<>(); storeParams.put("namespace", workspaceName); storeParams.put("datastore", dataStoreName); dataStore.createSchema(featureType); dataStore.createSchema(featureType); dataStore.createSchema(featureType); dataStore.createSchema(featureType); dataStore.createSchema(featureType); dataStore.createSchema(featureType); dataStore.createSchema(featureType); dataStore.createSchema(featureType); dataStore.createSchema(featureType); dataStore.createSchema(featureType); dataStore.createSchema(featureType); dataStore.createSchema(featureType); dataStore.createSchema(featureType); dataStore.createSchema(featureType); dataStore.createSchema(featureType); dataStore.createSchema(featureType); dataStore.createSchema(featureType); dataStore.createSchema(featureType); dataStore.createSchema(featureType); dataStore.createSchema(featureType); dataStore.createSchema(featureType); dataStore.createSchema(featureType); dataStore.createSchema(featureType); dataStore.createSchema(featureType); dataStore.createSchema(featureType); dataStore.createSchema(featureType); dataStore.createSchema(featureType); Map<String, Object> layerParams = new HashMap<>(); layerParams.put("layerName", featureType.getTypeName()); layerParams.put("dataStore", storeParams); layerParams.put("style", "default_point"); dataStore.createSchema(featureType); GeoServerRESTPublisher publisher = new GeoServerRESTPublisher("http://localhost:8080/geoserver", "admin", "geoserver"); publisher.publishDBLayer(workspaceName, storeTypeName, layerParams); } private static SimpleFeatureType createFeatureType(FeatureCollection features) { SimpleFeatureType featureType; Feature firstFeature = features.features().next(); GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory(); if (firstFeature.getDefaultGeometry() instanceof Point) { featureType = DataUtilities.createType("my_namespace", "my_point_type", "geometry:Point:srid=4326," + getAttributeFields(firstFeature)); } else if (firstFeature.getDefaultGeometry() instanceof Polygon) { featureType = DataUtilities.createType("my_namespace", "my_polygon_type", "geometry:Polygon:srid=4326," + getAttributeFields(firstFeature)); } else { throw new RuntimeException("Unsupported geometry type: " + firstFeature.getDefaultGeometry().getClass()); } return featureType; } private static String getAttributeFields(Feature feature) { StringBuilder sb = new StringBuilder(); for (Property property : feature.getProperties()) { if (property.getName().getLocalPart().equals("geometry")) { continue; } sb.append(property.getName().getLocalPart()).append(":").append(property.getType().getBinding().getSimpleName()).append(","); } return sb.toString(); } private static SimpleFeature createSimpleFeature(Feature feature, SimpleFeatureType featureType) throws ParseException { SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(featureType); for (Property property : feature.getProperties()) { if (property.getName().getLocalPart().equals("geometry")) { WKTReader wktReader = new WKTReader(JTSFactoryFinder.getGeometryFactory()); Geometry geometry = wktReader.read(feature.getDefaultGeometryProperty().getValue().toString()); featureBuilder.set("geometry", geometry); } else { featureBuilder.set(property.getName().getLocalPart(), property.getValue()); } } return featureBuilder.buildFeature(null); } } ``` 这段代码假定您已经有了一个已安装和配置的 GeoServer 实例,并且已经创建了一个具有适当权限的用户。此外,您需要在项目中包含以下依赖项: ``` <dependency> <groupId>org.geotools</groupId> <artifactId>gt-epsg-hsql</artifactId> <version>21.1</version> </dependency> <dependency> <groupId>org.geotools</groupId> <artifactId>gt-pbf</artifactId> <version>21.1</version> </dependency> <dependency> <groupId>org.geotools</groupId> <artifactId>gt-geojson</artifactId> <version>21.1</version> </dependency> <dependency> <groupId>org.geotools</groupId> <artifactId>gt-shapefile</artifactId> <version>21.1</version> </dependency> <dependency> <groupId>org.geoserver</groupId> <artifactId>gs-restconfig</artifactId> <version>2.16.2</version> </dependency> ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值