扩展一个简单算法资源 |
在“REST 服务发布机制简述”中,讲述了 REST 服务发布的过程,资源的信息保存在资源配置文件里,并通过 REST 应用上下文传递给 REST 应用对象,从而在 HTTP 请求到达 REST 应用对象的时候,能够找到合适的资源实现来处理。由此可见,要扩展一个简单算法资源,要分两步:
在 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.1,添加到外部 iserver-rest-resources.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>
其中含义如下:
configID:资源的名称。
urlTemplate:欲设置的资源 URI 路径(根节点之后的路径)。
resourceType:资源的类型,资源的类型有:ArithmeticResource(简单算法资源)、ArithResultResource(算法结果资源)、ArithResultSetResource(算法结果集资源)、CatalogList(目录资源)、DomainArithmeticResource(领域算法资源)、DomainArithResultResource(领域算法结果资源)、StaticResource(静态资源)等,参见 JavaDoc API 文档中 ResourceType 枚举。
implementClass:资源的实现类,即该类在 Jar 包(%SuperMap iServer_HOME%/webapps/iserver/WEB-INF/lib 目录下,如 ExtendREST.jar)中的路径。
extensionEncoderBeanNames:表述生成器组件名称。
extensionDecoderBeanNames:参数解析器组件名称。
extensionActionHandlerBeanName: HTTP 请求处理器组件名称。
注意:资源配置中,extensionEncoderBeanNames、extensionDecoderBeanNames、extensionActionHandlerBeanName 节点可以没有或为空,这时 SuperMap iServer 会使用默认的值。它们表示该资源对应的扩展表述生成器、扩展参数解码器、扩展 HTTP 请求处理器的实现,在使用这些扩展点的时候会用到,这里暂不深入介绍。
此种方式,可以将配置信息一起打在 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 按钮,即可获取矩形坐标面积,如下图所示。