扩展一个简单算法资源

发送反馈


在“REST 服务发布机制简述”中,讲述了 REST 服务发布的过程,资源的信息保存在资源配置文件里,并通过 REST 应用上下文传递给 REST 应用对象,从而在 HTTP 请求到达 REST 应用对象的时候,能够找到合适的资源实现来处理。由此可见,要扩展一个简单算法资源,要分两步:

  1. 实现一个简单算法资源类

  2. 添加资源信息到资源配置文件

1. 实现一个简单算法资源类

在 SuperMap iServer Restlet 机制的 REST 实现框架中,最高层的资源类是 ResourceBase,所有的资源实现类都直接或间接地继承于它,例如 SuperMap iServer 已提供的所有的资源的实现类。

在扩展 REST 资源的时候,需要继承 ResourceBase 或它的子类,SuperMap iServer 在最终实现类和 ResourceBase 类之间抽象出了许多抽象类,标志不同类型的资源,这为扩展 REST 资源的开发提供了极大方便,你可以根据欲扩展的资源的功能去寻找合适的抽象类进行扩展,当然,如果预期的资源跟 SuperMap iServer 某个已实现的资源比较类似的话,也可以直接对该资源实现进行继承,改写一些方法,从而快速地扩展一个你需要的资源。

有关 SuperMap iServer 中, REST 资源抽象类的情况,请参见 JavaDoc 文档:com.supermap.services.rest.resources 包。

在 SuperMap iServer 已实现的资源类中,可以分为如下表所示的几类(参考 JavaDoc 文档中的 ResourceType 枚举),可以作为扩展资源时候的参考。

名称 资源实现的直接父类 对应的已实现的资源
简单算法资源 SimpleAlgorithmResultResourceBase

map 模块的 symbol、clearCache  等资源,data 模块的 statistic 等资源

ImageResourceBase entireImage、highlightImage、image、overview、tileImage 等资源
MeasureResourceBase area、distance 等资源
算法结果资源 AlgorithmResultResource map 模块的 queryResult  等资源,data 模块的 featureResult 等资源
算法结果集资源 AlgorithmResultSetResource map 模块的 queryResults 等资源,data 模块的 featureResults 等资源
目录资源 CatalogListResourceBase map 模块的 maps、root、tempLayersSet、trackingLayers、domainComponents、domainComponentdata 等资源,data 模块的 data、datasources 等资源,networkanalyst 模块的 networkanalyst 等资源,3D 模块的 3D 、scenes、datas 等资源
静态资源 StaticResource map 模块的 layers、map、templayers 等资源,networkanalyst 模块的 networkDataName 等资源

此外,CommonAlgorithmResultResource 是简单算法资源 SimpleAlgorithmResultResourceBase 的一个子类,基于 CommonAlgorithmResultResource 实现的资源有 networkanalyst 模块的 closestfacilities、weightMatrix、TSPPaths、serviceAreas、MTSPPaths、location 、paths 等,3D 模块的 tileData 等。SimpleResource 是 ResourceBase 的一个子类,基于 SimpleResource 实现的资源有 3D 模块的 scene、 layers、 layer、 data 、config、modelIndex、tileDataVersion 等。

这里,扩展一个名为 rectangleArea 的资源,实现量算地图上一块矩形区域的面积的功能,并将结果直接返回。

由于要量算的对象是地图,把 rectangleArea 资源作为地图资源(map 资源)的一个子资源比较合适,设计 URI 为:

http://<server>:<port>/iserver/services/components-rest/rest/maps/{mapName}/rectangleArea[.<format>]

设计请求参数为:

名称 类型 含义
rect2D Rectangle2D 地图上欲量算的目标矩形区域。
unit Unit 期望返回结果的单位。

设计响应结构为:

字段 类型 说明
area double 量算的面积大小的值。
unit Unit 量算结果的单位。

rectangleArea 资源跟简单算法资源比较类似,所以它的实现类 rectangleAreaResource 通过继承 SimpleAlgorithmResultResourceBase 抽象类来实现。SimpleAlgorithmResultResourceBase 类中的抽象方法是必须实现的,如下表所示,详细请参考 JavaDoc 文档。

抽象方法名称 含义
isResourceExist() 判断当前资源是否可用。
checkUrlParamValid(Map) 检查 URI 中的参数是否合法,不合法则抛出异常。
createArithParamClassMappings() 给出算法参数的类型,即请求参数的字段名和类型。
runArithMetic(Map) 利用参数运行算法得到算法结果。

业务逻辑,即量算的功能可以由地图组件(com.supermap.services.components.Map)的一个实现类 com.supermap.services.components.impl.MapImpl(SuperMap iServer 已经提供)来进行,在示例 RectangleAreaResource 类中,地图名和 MapImpl 组件的获取放在了构造函数中进行,地图名从请求对象(URI)中获取,MapImpl 对象从 REST 应用上下文获取。示例代码如下:

package com.supermap.sample.extendREST;

import java.util.HashMap;

import org.restlet.Context;

import org.restlet.Request;

import org.restlet.Response;

import com.supermap.services.components.Map;

import com.supermap.services.components.MapException;

import com.supermap.services.components.commontypes.MeasureParameter;

import com.supermap.services.components.commontypes.MeasureResult;

import com.supermap.services.components.commontypes.Point2D;

import com.supermap.services.components.commontypes.Rectangle2D;

import com.supermap.services.components.commontypes.Unit;

import com.supermap.services.rest.resources.SimpleAlgorithmResultResourceBase;

import com.supermap.services.rest.util.MappingUtil;

public class RectangleAreaResource extends SimpleAlgorithmResultResourceBase{

    private String mapName;

    private MappingUtil mappingUtil;

    

    public RectangleAreaResource(Context context, Request request, Response response) {

        super(context, request, response);

        mappingUtil = new MappingUtil(this);

        this.mapName = this.mappingUtil.getMapName(this.getRequest());

        this.mapName = this.mapName.trim();

    }

    //判断资源是否存在

    public boolean isResourceExist(){

//      Logger.info("rectangle area resource exist");

        boolean flag = false;

        flag = this.mappingUtil.isMapExist(this.mapName);

        return flag;

    }

  //运行算法得到结果

    protected Object runArithMetic(java.util.Map params){

        Object arithResult=null;

        //如果参数为空,返回 area 字段为 -1

        if(params==null||0==params.size()){

            MeasureResult  result =   new MeasureResult();

            result.area = -1;

            arithResult = result ;

        }else{

                Rectangle2D rect = (Rectangle2D)params.get("rect2D");

            Unit  unit = (Unit)params.get("unit");

            //单位参数可以不传,默认为米。

            if(unit == null){

                unit = Unit.METER;

            }

            //量算参数类,其中存储了单位信息。

            MeasureParameter measureParam = new MeasureParameter();

            measureParam.unit = unit;

            //将 Rectangle2D 转化为 Point2Ds

            Point2D leftBottom=rect.leftBottom;

            Point2D rightTop=rect.rightTop;

            Point2D leftTop=new Point2D(leftBottom.x,rightTop.y);

            Point2D rightBottom=new Point2D(rightTop.x,leftBottom.y);

            Point2D[] point2Ds={leftBottom,leftTop,rightTop,rightBottom};

            

            //量算结果

            try {

                Map mapComponent = this.mappingUtil.getMapComponent(mapName);

                                arithResult = mapComponent.measureArea(mapName, point2Ds, measureParam);

                        } catch (MapException e) {

                                // TODO Auto-generated catch block

                                e.printStackTrace();

                        }

        }

        

        return arithResult;

    }

    //给出算法参数的类型,即请求参数的结构。

    protected java.util.Map<String, Class> createArithParamClassMappings(){

        java.util.Map<String, Class> paramClassMapping = new HashMap<String, Class>();

        paramClassMapping.put("rect2D", Rectangle2D.class);

        paramClassMapping.put("unit", Unit.class);

        return paramClassMapping;

    }

    

    //判断 URI 中的参数是否合法,不合法则抛出异常。为了示例的清晰,这里没有判断。

        protected void checkUrlParamValid(java.util.Map arg0) {

                // TODO Auto-generated method stub

                

        }

}

你可以在%SuperMap iServer_HOME%/samples/code/ExtendREST 下找到该实例程序的源代码。

编译后,打成一个 Jar 包,例如 ExtendREST.jar,放到%SuperMap iServer_HOME%/webapps/iserver/WEB-INF/lib 目录下(其实所有的扩展实现类,包括后面要讲的几种扩展的实现,都需要打成 Jar 包,放到%SuperMap iServer_HOME%/webapps/iserver/WEB-INF/lib 目录下)。

注意:SuperMap iServer 的 REST 框架利用 FreeMarker 生成 HTML 格式的表述,为了使 rectangleArea 资源支持 HTML 格式的表述,还需要编写 *.ftl (即模板)文件,可参见%SuperMap iServer_HOME%/webapps/iserver/WEB-INF/lib/iserver-all-{version}.jar 中 templates 目录(已有资源的模板文件)。

FreeMarker 是基于模板生成文本输出的通用工具。在 SuperMap iServer 的 REST 框架下,资源的 *.ftl 模板文件中的 ${resource.content.*} 表示表述结果的字段,而且文件名必须跟资源的 ID 一致。

编写 rectangleArea 资源对应的 FreeMarker 模板文件 rectangleArea.ftl 如下,rectangleArea.ftl 需要放在%SuperMap iServer_HOME%/webapps/iserver/WEB-INF/lib 目录下的一个 Jar 包中,建议跟资源实现类打在一个 Jar 包 ExtendREST.jar 中,rectangleArea.ftl 在 Jar 包中,必须放在 【Jar 包】/templates 目录下。

<#include "head.ftl">

<h1>rectangleArea 资源</h1>

<br>

<b>资源描述:</b> 面积量算结果资源,用于描述一个面积量算的结果。

<br>

<#if resource.content.area!= -1>

 <p>

         <table width="150px" id="userTable">

                <tr valign="left">

                <#setting number_format="#0.000000000#">

                <td style="width:50px"> <h5>area:</h5></td> <td style="width:50px"> ${resource.content.area}</td>

                </tr>

                <tr valign="left">

                <td style="width:50px"> <h5 >unit:</h5></td> <td style="width:50px">${resource.content.unit}</td>

                </tr>

         </table>

 </p>

<#else>

 <br><br>

    请设置 rect2D 参数。 没有设置 rect2D 参数,则结果返回-1 表示量算失败。

</#if>

<p>

<form method="get" >

        <table style="border:1px solid #000000;width:800;" >

           <#assign prerect2D>{leftBottom:{x:23,y:34},rightTop:{x:40,y:50}}</#assign>

            <tr><td>rect2D</td><td><textarea type="text" style="width:255px;height:50px" name="rect2D" >${prerect2D}</textarea></td></tr>

            <tr>

                <td>unit</td>

                <td>

                    <select name="unit">

                                <option value="METER" selected="selected">METER</option>

                                <option value="KILOMETER" selected="selected">KILOMETER</option>

                        </select>

                        </td>

                </tr>

            <tr><td></td><td><input type="submit" value="rect2DareaMeasure"/></td></tr>

        </table>

</form>

</p>

<br>

<br>

<hr>

<#include "tail.ftl">

2. 添加资源信息到资源配置文件

添加资源信息到资源配置文件有两种方式,任意一种都可以:

2.1,添加到外部 iserver-rest-resources.xml 文件

2.2,添加到功能模块自己的 XML 配置文件

 

2.1,添加到外部 iserver-rest-resources.xml 文件

此种方式,是在系统管理员将扩展资源 Jar 包放到 %SuperMap iServer_HOME%/webapps/iserver/WEB-INF/lib 目录后,需要手动将扩展资源的资源配置信息放在%SuperMap iServer_HOME%/webapps/iserver/WEB-INF/iserver-rest-resources.xml 中,各个功能模块的扩展资源配置信息,都可以配置到 iserver-rest-resources.xml 中。

SuperMap iServer REST 服务启动时,通过 iserver-rest-resources.xml  找到扩展的资源,并发布成 REST 服务。iserver-rest-resources.xml 文件的结构如下:

<resources>

     ......

     <resource>

            ......

     </resource>

</resources>

其中,一个 <resource/> 节点表示的就是一个资源的信息,<components/> 节点是发布领域组件时,配置领域组件的地方,这里暂时不描述,在后面发布领域组件为资源一节会详细讲。

以 rectangleArea 资源为例,<resource/> 节点的结构如下所示:

<resource>

    <configID>rectangleArea</configID>

    <urlTemplate>/maps/{mapName}/rectangleArea</urlTemplate>

    <resourceType>ArithmeticResource</resourceType>

    <implementClass>com.supermap.sample.extendREST.RectangleAreaResource</implementClass>

    <extensionEncoderBeanNames></extensionEncoderBeanNames>

    <extensionDecoderBeanNames></extensionDecoderBeanNames>

    <extensionHttpActionHandlerBeanName></extensionHttpActionHandlerBeanName>

</resource>

其中含义如下:

注意:资源配置中,extensionEncoderBeanNames、extensionDecoderBeanNames、extensionActionHandlerBeanName 节点可以没有或为空,这时 SuperMap iServer 会使用默认的值。它们表示该资源对应的扩展表述生成器、扩展参数解码器、扩展 HTTP 请求处理器的实现,在使用这些扩展点的时候会用到,这里暂不深入介绍。

2.2,添加到功能模块自己的 XML 配置文件

此种方式,可以将配置信息一起打在 Jar 包里,实现扩展模块的即插即用,方便系统管理员配置。

本示例扩展的是地图模块的资源,这里添加 Jar 包:///config/resource/rest/mappingResources.xml 文件(扩展各功能模块时,需要在 Jar 包中添加的 XML 配置文件位置参见:功能模块、资源配置文件对应表)如下:

<resources>

        <resource>

            <configID>rectangleArea</configID>

            <urlTemplate>/maps/{mapName}/rectangleArea</urlTemplate>

            <resourceType>ArithmeticResource</resourceType>

            <implementClass>com.supermap.sample.extendREST.RectangleAreaResource</implementClass>

        </resource>

</resources>

<resources/>标签中可以配置多个<resource/>标签,对应多个资源。

此步骤可以在实现资源类的 Java 工程中一起进行,打在同一个Jar 包中,如本示例中的 ExtendREST.jar。

 

至此,一个扩展的资源就完成了。rectangleArea 资源可以实现量算地图上一块矩形区域的面积。启动服务(这里从本机启动),访问 http://localhost:8090/iserver/services/components-rest/rest/maps/世界地图/rectangleArea.html(这里以 HTML 表示格式为例,同时支持其他形式的表述),即访问“世界地图”下的 rectangleArea 资源,如下图所示:

在 rect2D 中输入矩形左下角、右上角坐标,在 unit 中选择单位,然后点击 rect2DareaMeasure 按钮,即可获取矩形坐标面积,如下图所示。