上一章我们通过实现一个服务对如何扩展GeoServer有了一定的了解,但是,对于为何要这样做并没有说明,本章我们重点来说说GeoServer的结构,下图来自GeoServer官网(希望没有侵权),它很好的揭示了GeoServer处理请求的全过程。

我们说GeoServer使用Spring框架来构建,这里就可以看到Spring的使用,虚线框中的Restlet就是用Spring引入系统的,每个服务包的“applicationContext.xml”文件里都包含了描述Route映射的信息,例如WMS就有如下片段:

    <bean id="wmsURLMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="alwaysUseFullPath" value="true"/>
<property name="mappings">
<props>
<prop key="/wms">dispatcher</prop>
<!-- prop key="/wms/putstyles">putStylesWrapper</prop-->
<prop key="/wms/*">dispatcher</prop>
</props>
</property>
</bean>

它将形如“/wms/”的请求映射到“dispatcher”对象,而这个对象就是上一章调试的重点“org.geoserver.ows.Dispatcher”。所以,如果我们希望我们的服务也和WMS有相似的处理方式,我们就需要在自己的配置文件里加上类似的一段。

本章我准备先两个方面入手讲解GeoServer是如何处理OWS请求的。首先介绍GeoServer的运行时环境,包括对象是如何创建并且引用的,以及SpringFramework的配置体系。然后以Dispatcher类的处理算法入手,重点介绍扩展点,熟悉了扩展点,我们就可以对GeoServer的OWS处理进行扩展,开发符合我们要求的应用。

搞清楚处理机制后,我会谈谈GeoServer的资源API。这个部分主要罗列了GeoServer的一些接口,以及它们代表的概念。

一 OWS请求处理

我们知道applicationContext.xml文件是SpringFramework的配置文件,许多对象都在这个文件中定义,但是为什么是这个文件呢,它们之间有没有关系呢。

让我们先来看看“web-app”的“web.xml”文件,中间有这样一段

    <context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:/applicationContext.xml classpath*:/applicationSecurityContext.xml</param-value>
</context-param>

可以看到,这里定义了classpath下面的applicationContext.xml文件和applicationSecurityContext.xml作为配置文件,系统运行起来后SpringFramework会扫描classpath下面所有的applicationContext.xml文件和applicationSecurityContext.xml文件,创建里面定义的对象。所以只要我们提供的包里面包含这两个文件中的任意一个,就可以把自定义的对象装载到系统运行时中,同时还可以引用已经创建的对象。

main包中的applicationContext.xml文件定义了许多基础对象,很重要的有:catalog,geoServer和dispatcher,这几个对象将是我们后面讲解的重点。

上面这幅图描述了GeoServer处理OWS请求的步骤,右边的红色方框是每个步骤的扩展点,每个扩展点都对应一个抽象类或者接口。可以通过实现这些类和接口来扩展GeoServer的功能。下面我们来介绍Dispatcher类处理OWS请求的步骤,每一步都有具体的函数相对应。

第一步 解析HTTP请求参数(见org.geoserver.ows.Dispatcher.init(Request))

程序并不会直接采用HTTP的参数,它们是字符串键值对(KVP)或者是XML字符串,程序会先把它们转换成相应的对象。类型org.geoserver.ows.Request是代表请求参数的类型,它包含转换后的KVP Map就已经将原来的字符串变成了对象。

转换参数的工作由Parser来完成。举例说明:参数“BBOX=-180,-90,180,90”的值是字符串“-180,-90,180,90”,经过org.geoserver.wfs.kvp.BBoxKvpParser的处理,就变成了org.geotools.geometry.jts.ReferencedEnvelope的对象,显然它更容易使用。

设想,如果我们有一个参数需要传递数据表,我们可以这样构造参数格式:TABLE=cell11,cell12,cell13|cell21,cell22,cell23|cell31,cell32,cell33。当程序遇到这个参数的时候我们就可以使用我们开发的Parser对象来将它转换成一个表对象,供后续代码使用。我们只需要将这个Parser对象放到applicationContext.xml文件立即可,就像下面这样

     <bean id="tableKvpParser" class="examples.TableKvpParser"/>

所有Parser都必须从org.geoserver.ows.KvpParser继承。

第二步 匹配服务对象(见org.geoserver.ows.Dispatcher.service(Request))

程序根据参数SERVICE和VERSION的值来选择合适的服务,例如:http://www.dummy.com/geoserver/wms?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetCapabilities,这个URL是在请求1.1.1版本的WMS服务。所以注册服务时需要指明服务ID和版本号,下面是wms服务的注册代码

     <bean id="wmsService2" class="org.geoserver.wms.DefaultWebMapService">
<constructor-arg ref="wms"/>
</bean>
<alias name="wmsService2" alias="webMapService"/> <bean id="wmsServiceDescriptor" class="org.geoserver.platform.Service">
<constructor-arg index="0" value="wms"/>
<constructor-arg index="1" ref="wmsService2"/>
<constructor-arg index="2" value="1.1.1"/>
<constructor-arg index="3">
<list>
<value>Capabilities</value>
<value>GetCapabilities</value>
<value>DescribeLayer</value>
<value>GetFeatureInfo</value>
<value>GetLegendGraphic</value>
<value>GetMap</value>
<value>Map</value>
<value>reflect</value>
<value>kml</value>
<value>GetStyles</value>
</list>
</constructor-arg>
</bean>

来看Service类的构造函数

public Service(String id, Object service, Version version, List<String> operations)

不难看出上面的配置信息其实定义的是构造函数的参数,其中id,version,operations的含义都不难猜出,然我们来看看service这个参数。它是一个Object,也就说对service这个对象并没有强类型要求。但是并非任何对象都可以作为service参数传进来,关于这个问题,我们会在后面加以说明,在这个例子里,service是类org.geoserver.wms.DefaultWebMapService的对象。

上面的配置信息将一个ID是“wms”版本是“1.1.1”的OWS服务注册到系统运行时中,当一个OWS请求到来时,系统就会遍历所有注册的服务,寻找符合要求的服务。

第三步 执行操作(见org.geoserver.ows.Dispatcher.dispatch(Request, Service))

这一步主要的操作就是创建执行对象org.geoserver.platform.Operation,这个对象采用了Java的反射原理来实现函数调用,所以需要创建函数参数数组。前面提到过OWS参数的格式有KVP和XML两种,因此对参数的处理也分为两种,具体到类就是org.geoserver.ows.KvpRequestReader和org.geoserver.ows.XmlRequestReader。这两个类将参数字符串转换成相应的对象,在这点上与前面的Parser类似,不同的是这里的转换对应的是一个服务调用,而不是具体一个参数。例如org.geoserver.wms.kvp.GetMapKvpRequestReader就负责将GetMap的参数转换成org.vfny.geoserver.wms.requests.GetMapRequest对象。

这一步的扩展点就是以上两个Reader,来看WMS中GetMap Reader的定义

    <bean id="getMapKvpReader"
class="org.geoserver.wms.kvp.GetMapKvpRequestReader">
<constructor-arg ref="wms"/>
</bean>
    <bean id="getMapXmlReader"
class="org.geoserver.wms.xml.WMSXmlRequestReaderAdapter">
<constructor-arg index="0" value="http://www.opengis.net/ows"/>
<constructor-arg index="1" value="GetMap"/>
<constructor-arg index="2" ref="wms"/>
<constructor-arg index="3" value="org.vfny.geoserver.wms.requests.GetMapXmlReader"/>
</bean>

与前面的扩展一样,我们只需要把我们设计的相关Reader注册到系统中,程序就会自己找到它。下面说说匹配Reader的算法。

第二步提到了一个service对象,除了知道它是一个Object之外,我们并没有过多说明。实际上,Operation类会持有这个对象,并且从里面查找与注册的操作同名的公共成员函数,这个函数将通过反射来调用。显然,我们提供给这个函数的参数必须符合它声明的参数类型。所以,匹配KVP Reader的算法就是匹配参数类型的过程。DefaultWebMapService的getMap函数的签名如下:

public GetMapResponse getMap(GetMapRequest request)

程序会遍历所有注册的GetMapKvpRequestReader,将注册函数的参数类型与org.geoserver.ows.KvpRequestReader.getRequestBean()的返回值比较,如果两者可以交换(Assignable)则匹配成功。

XML Reader的匹配与KVP Reader完全不同,这一点很奇怪,它是根据注册的操作名称,服务ID和服务版本来匹配的。

至于执行操作,实在没什么需要特殊说明的,就是调用方法而已,唯一值得注意的是它的返回值,因为我们要把它写到返回流(Response Stream)中。而这是下一步的事情了。

第四步 返回结果(见org.geoserver.ows.Dispatcher.response(Object, Request, Operation))

现在需要把结果返回给客户端了,这个步骤叫做Response。这一步的扩展点是一个叫org.geoserver.ows.Response的类,程序会遍历所有注册的Response类(与前面的那些匹配完全一样),比较返回值的类型与org.geoserver.ows.Response.getBinding()的值,如果两者可以交换(Assignable)则匹配成功。匹配成功后就调用org.geoserver.ows.Response.write(Object, OutputStream, Operation)函数回写结果。下面是WMS GetMap的Response配置信息

    <bean id="getMapResponse"
class="org.geoserver.ows.adapters.ResponseAdapter">
<constructor-arg value="org.vfny.geoserver.wms.responses.GetMapResponse"/>
<constructor-arg ref="geoServer"/>
</bean>

这里用到了一个叫ResponseAdapter的类主要是为了适配接口。

到此OWS的处理就介绍完了,下面来看看GeoServer的资源API。

二 资源对象模型

这是我起的名称,实际上是一套接口。在包“main”的命名空间org.geoserver.catalog下面有许多接口描述了GeoServer中许多基本概念,搞清楚这些是学习GeoServer的关键之一。

特别需要说明一下,GeoServer所有的资源都派生自接口org.geoserver.catalog.Info,它唯一的方法是org.geoserver.catalog.Info.getId()。这说明,GeoServer里面所有的资源都有一个全局唯一的ID。我们会在后面的文章中详细介绍,包括它的产生和保存。

1 Catalog

这里有一段摘录自main配置文件的脚本

    <bean id="rawCatalog" class="org.geoserver.catalog.impl.CatalogImpl">
<property name="resourceLoader" ref="resourceLoader"/>
</bean>
<bean id="secureCatalog" class="org.geoserver.security.SecureCatalogImpl">
<constructor-arg ref="rawCatalog" />
</bean>
<!-- Switch this when you want to enable the secure catalog by default -->
<alias name="secureCatalog" alias="catalog"/>

里面定义了一个叫“catalog”的变量,下面是一个引用它的例子

    <bean id="geoServer" class="org.geoserver.config.impl.GeoServerImpl">
<property name="catalog" ref="catalog"/>
</bean>

这就是我们要说的Catalog。查看Catalog接口的代码,会看到它定义了许多函数(代码太长就不贴出了),这些函数基本涵盖了“增删改查”所有的方法,而每一套“增删改查”都对应了我们将要介绍一个概念,例如这一段:

    void add(LayerInfo layer);
void remove(LayerInfo layer);
void save(LayerInfo layer);
LayerInfo getLayer(String id);

后面还有很多getLayer方法就不赘述了。

我们可以这样定义:Catalog是一个抽象概念,它提供了一套访问GeoServer资源的方法,通过这些方法程序可以对GeoServer的资源进行“增删改查”的操作,而无需知道资源的具体保存形式。当然,目前唯一的实现就是CatalogImpl,但是我们完全可以用我们自己的Catalog来替换它,只需要修改一下上面的配置信息就可以了。

需要说明的是,很多时候我们都是通过GeoServer这个对象来获得Catalog的,下面我们就来说说GeoServer。

2 GeoServer

它的完整名称是org.geoserver.config.GeoServer,根据注释的解释,它是用来访问GeoServer服务器的配置信息的接口,而它的名称也反映出这个特点。我们可以通过它来获得与具体服务无关的数据,例如服务的字符集,服务的联系人,发布了哪些OWS服务等。当然还有服务的资源接口Catalog。在自定义的配置文件里,可以用“geoServer”来引用它。

3 Layer

Layer是空间数据源与表现样式的组合,WMS GetMap中我们指定的参数LAYERS指的就是它。org.geoserver.catalog.LayerInfo是它的代码形式。通过这个接口,我们可以访问与Layer相关联的资源,主要有空间数据源(Resource),样式(Style),图例(Legend)等。Layer可以相互嵌套形成LayerGroup,LayerGroup在行为上与Layer完全一样,这是一个组合模式的应用。

4 Resource

这里的Resource并非泛指的资源,而是与空间相关联的资源,所以org.geoserver.catalog.ResourceInfo中有访问空间参考(SRS或CRS)的方法。另外,通过这个接口我们可以访问资源的Store(又是一个概念,我们姑且就使用原文),也就是资源的存储器。GeoServer中有两个概念是从Resource派生来的,Coverage和FeatureType,并且它们有各自的Store。

5 Store

Store表示Resource的存储。org.geoserver.catalog.StoreInfo是它的代码形式,最重要的函数是org.geoserver.catalog.StoreInfo.getConnectionParameters(),返回连接参数,参数的具体含义由具体的存储介质来决定。Coverage和FeatureType都有各自的Store,org.geoserver.catalog.CoverageStoreInfo和org.geoserver.catalog.DataStoreInfo。

6 Coverage

Coverage一直是让我迷惑的概念,我把wiki的解释原文抄录在这里:In geographic information systems, a coverage is a mapping of one aspect of data in space.大意是:在GIS领域,一个Coverage就是一附地图,它反应了空间数据的一个方面。(很笼统是吧,如果你有准确的解释,希望你能发给我,我将不胜感激)。org.geoserver.catalog.CoverageInfo是这个概念的代码形式。

7 FeatureType

要解释FeatureType就必须先解释Feature。Feature,即要素,是一个具有空间意义的实体,并且拥有附加的属性。例如:某个城市,它的位置是东经116.46度 北纬 39.92 度,它的名字是“北京”,它的常住人口是1972万。我们就可以用一个要素来表示它,这个要素是一个点,点的坐标是[116.46 39.92],它的属性表示为

属性名称

属性值

Name 北京
Popu 1972

如果我们有许多城市数据,都具有相似的特征,我们就可以定义一个叫“CITIES”的FeatureType,它的几何类型是Point(点),它的属性Scehma是

属性名称

值类型

Name 字符串
Popu 数值

可以把FeatureType与Feature的关系想象为类与类实例的关系。也可以把它们的关系想象为数据表与数据记录的关系。后者其实更实用,因为许多时候Feature数据就是以表的形式组织和访问的。

org.geoserver.catalog.FeatureTypeInfo是它的代码形式。它最重要的方法就是org.geoserver.catalog.FeatureTypeInfo.getFeatureSource(ProgressListener, Hints),这个方法会返回数据源,我们可以用这个数据源来查询Feature。

8 Style

Style,可以翻译成渲染样式,提供了一套方法用来描述如何渲染Feature。WMS GetMap中我们指定的参数STYLES指的就是它。OGC的标准SLD提供了一种语言用来描述Style。GeoServer采用了这个语言,其他很多GIS平台也支持这个语言。推荐我的一篇文章《OGC之路(2) 之 Style之谜》说得比较详细,还附有代码。

org.geoserver.catalog.StyleInfo是它的代码形式。它最重要的方法就是org.geoserver.catalog.StyleInfo.getStyle(),返回样式对象org.geotools.styling.Style。我们可以通过它来获得SLD里面定义的元素。

至此,我把GeoServer的结构做了一个简单的介绍,从OWS处理请求的方式到GeoServer资源访问的API。其实GeoServer的结构远不止这么简单,它还提供了一套完整的管理服务以及相关GUI,这个部分不是我们介绍的内容,所以在此略过,但是作为用户接触最频繁的部分,它的结构还是很值得研究的。此外,GeoServer保存资源的方式也很重要,GeoServer采用文件目录结构来分类保存各种资源,并且设置了访问权限,我们将在下一章对此做详细说明。

转自:http://www.cnblogs.com/sillyemperor/archive/2011/01/26/1933248.html

geoserver 源码介绍的更多相关文章

  1. Eclipse下建立geoserver源码工程

    摘要:本文详细阐述,如何基于geoserver源码构建eclipse工程文件,操作过程中除用到jdk.eclipse以外,还有git和maven,操作系统为windows8. 1安装Git 从(htt ...

  2. 关于导入geoserver 源码到Eclipse编译运行

    参考http://blog.csdn.net/gisshixisheng/article/details/43016443 和  http://blog.sina.com.cn/s/blog_6e37 ...

  3. eclipse中建geoserver源码

    概述:本文讲述的是在eclipse中如何构建geoserver源码工程,其中涉及到了jdk,github,marven等. 1.安装git 从(http://git-scm.com/download/ ...

  4. GGTalk——C#开源即时通讯系统源码介绍系列(一)

    坦白讲,我们公司其实没啥技术实力,之所以还能不断接到各种项目,全凭我们老板神通广大!要知道他每次的饭局上可都是些什么人物! 但是项目接下一大把,就凭咱哥儿几个的水平,想要独立自主.保质保量保期地一个个 ...

  5. geoserver源码学习与扩展——跨域访问配置

    在 geoserver源码学习与扩展——restAPI访问 博客中提到了geoserver的跨域参数设置,本文详细讲一下geoserver的跨域访问配置. geoserver的跨域访问依赖java-p ...

  6. geoserver源码maven编译相关问题

    1.登陆失败跳转404错误 登陆失败后指向的路径为: http://192.168.15.97:8080/hgisserver/web/wicket/bookmarkable/org.geoserve ...

  7. 4- vue django restful framework 打造生鲜超市 -restful api 与前端源码介绍

    4- vue django restful framework 打造生鲜超市 -restful api 与前端源码介绍 天涯明月笙 关注 2018.02.20 19:23* 字数 762 阅读 135 ...

  8. 1-开发共享版APP(源码介绍)-BUG修复

    这一系列文章将介绍APP的源码,这一节作为所有BUG问题修复! https://www.cnblogs.com/yangfengwu/category/1512162.html    //开发共享版A ...

  9. Java中常用的七个阻塞队列第二篇DelayQueue源码介绍

    Java中常用的七个阻塞队列第二篇DelayQueue源码介绍 通过前面两篇文章,我们对队列有了了解及已经认识了常用阻塞队列中的三个了.本篇我们继续介绍剩下的几个队列. 本文主要内容:通过源码学习De ...

随机推荐

  1. python之路之迭代器与生成器

    一  迭代器 那么在研究迭代器之前首先应该要知道什么是迭代. 迭代:是一个重复的过程,并且每次重复都是建立基于上一次的结果而来的,所以在迭代的过程其实是在不断变化的. 迭代器:就是迭代取值的工具. 那 ...

  2. python 之路06day

    一   字符编码 1   字符编码的定义: 计算机要想工作必须通电,即用‘电’驱使计算机干活,也就是说‘电’的特性决定了计算机的特性.电的特性即高低电平(人类从逻辑上将二进制数1对应高电平,二进制数0 ...

  3. url 路由系统

    Django的路由系统 URL配置(URLconf)就像Django所支撑网站的目录.它的本质是URL与要为该URL调用的视图函数之间的映射表. 我们就是以这种方式告诉Django,遇到哪个URL的时 ...

  4. swagger配置

    1.pom.xml <!--swagger2--> <dependency> <groupId>io.springfox</groupId> <a ...

  5. 支持向量机(理论+opencv实现)

    从基础开始讲起,没有这些东西看支持向量机真的很难!   1.拉格朗日乘子(Lagrangemultiplier)   假设需要求极值的目标函数(objectivefunction)为f(x,y),限制 ...

  6. JQ-用户注册用到的图形验证码,短信验证码点击事件,切换active类

    // 点击切换图形验证码 页面加载完后执行,类似window.onload $(function () { var imgCaptcha = $(".img-captcha"); ...

  7. 超酷的Prezi在线ppt制作网站

    prezi.com 你还在用office Power Point 制作PPT吗? 使用prezi.com制作ppt试试.http://prezi.com/explore/staff-picks/

  8. 哈希学习(2)—— Hashing图像检索资源

    CVPR14 图像检索papers——图像检索 1.  Triangulation embedding and democratic aggregation for imagesearch (Oral ...

  9. jpa 一对多and 多对一

    配置: <?xml version="1.0" encoding="UTF-8"?> <persistence version="2 ...

  10. JAVA 常用注解( JDK, Spring, AspectJ )

    JDK自带注解   @Override   表示当前方法覆盖了父类的方法   @Deprecation   表示方法已经过时,方法上有横线,使用时会有警告   @SuppviseWarnings    ...