Restlet的简单介绍
说道java开发Restful webservie,除了JAX-RS,还有一个就是Restet。Restlet个人感觉要比JAX-RS更灵活,当然,也更复杂点。
特点
和JAX-RS类似,也可以使用注解。但是没JAX-RS那么多注解,很少,Restlet的所有注解类有(有@符号的就是):
Restlet有一个特点,就是可以既作为web应用,放到tomcat之类的容器中,响应http请求。这种方式和JAX-RS差不多,都是通过一个类似于拦截器的servlet将请求拦截,然后转发给Restlet写的应用处理。web.xml中配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<!-- Restlet adapter -->
<servlet>
<servlet-name>RestletServlet</servlet-name>
<servlet-class>org.restlet.ext.servlet.ServerServlet</servlet-class>
<init-param>
<!-- Application class name -->
<param-name>org.restlet.application</param-name>
<param-value>adages.AdagesApplication</param-value>
</init-param>
</servlet>
<!-- Dispach all requests to the Restlet servlet. -->
<servlet-mapping>
<servlet-name>RestletServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
同时,Restlet自带了一个http服务器的功能,也就是说Restlet写的webservice,我们可以导出为jar(运行main函数),直接运行jar文件。如下:
编译为jar文件后,运行截图:
按ctrl+c终止。
开发的时候遇到的问题
1.jar库的版本
编译过程导入 org.restlet的相关jar库的时候,必须选择是jee的而不是是jse的。否则接收请求后会报错,如下:
WARNING: Exception or error caught in server resource
java.lang.AbstractMethodError: org.restlet.engine.converter.ConverterHelper.score(Ljava/lang/Object;Lorg/restlet/representation/Variant;Lorg/restlet/resource/Resource;)F
at org.restlet.engine.converter.ConverterUtils.getBestHelper(ConverterUtils.java:137)
at org.restlet.service.ConverterService.toRepresentation(ConverterService.java:229)
at org.restlet.resource.Resource.toRepresentation(Resource.java:738)
at org.restlet.resource.ServerResource.doHandle(ServerResource.java:509)
at org.restlet.resource.ServerResource.get(ServerResource.java:695)
at org.restlet.resource.ServerResource.head(ServerResource.java:977)
at org.restlet.resource.ServerResource.doHandle(ServerResource.java:587)
at org.restlet.resource.ServerResource.doNegotiatedHandle(ServerResource.java:637)
at org.restlet.resource.ServerResource.doConditionalHandle(ServerResource.java:336)
at org.restlet.resource.ServerResource.handle(ServerResource.java:899)
at org.restlet.resource.Finder.handle(Finder.java:243)
at org.restlet.routing.Filter.doHandle(Filter.java:156)
at org.restlet.routing.Filter.handle(Filter.java:203)
at org.restlet.routing.Router.doHandle(Router.java:428)
at org.restlet.routing.Router.handle(Router.java:645)
at org.restlet.routing.Filter.doHandle(Filter.java:156)
at org.restlet.routing.Filter.handle(Filter.java:203)
at org.restlet.routing.Filter.doHandle(Filter.java:156)
at org.restlet.routing.Filter.handle(Filter.java:203)
at org.restlet.routing.Filter.doHandle(Filter.java:156)
at org.restlet.engine.application.StatusFilter.doHandle(StatusFilter.java:151)
at org.restlet.routing.Filter.handle(Filter.java:203)
at org.restlet.routing.Filter.doHandle(Filter.java:156)
at org.restlet.routing.Filter.handle(Filter.java:203)
at org.restlet.engine.CompositeHelper.handle(CompositeHelper.java:208)
at org.restlet.engine.application.ApplicationHelper.handle(ApplicationHelper.java:81)
at org.restlet.Application.handle(Application.java:378)
at org.restlet.routing.Filter.doHandle(Filter.java:156)
at org.restlet.routing.Filter.handle(Filter.java:203)
at org.restlet.routing.Router.doHandle(Router.java:428)
at org.restlet.routing.Router.handle(Router.java:645)
at org.restlet.routing.Filter.doHandle(Filter.java:156)
at org.restlet.routing.Filter.handle(Filter.java:203)
at org.restlet.routing.Router.doHandle(Router.java:428)
at org.restlet.routing.Router.handle(Router.java:645)
at org.restlet.routing.Filter.doHandle(Filter.java:156)
at org.restlet.routing.Filter.handle(Filter.java:203)
at org.restlet.engine.CompositeHelper.handle(CompositeHelper.java:208)
at org.restlet.Component.handle(Component.java:389)
at org.restlet.Server.handle(Server.java:513)
at org.restlet.engine.ServerHelper.handle(ServerHelper.java:69)
at org.restlet.engine.adapter.HttpServerHelper.handle(HttpServerHelper.java:149)
at org.restlet.ext.servlet.ServerServlet.service(ServerServlet.java:1086)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
at org.apache.catalina.valves.RemoteIpValve.invoke(RemoteIpValve.java:647)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:298)
at org.apache.coyote.http11.Http11AprProcessor.process(Http11AprProcessor.java:864)
at org.apache.coyote.http11.Http11AprProtocol$Http11ConnectionHandler.process(Http11AprProtocol.java:579)
at org.apache.tomcat.util.net.AprEndpoint$Worker.run(AprEndpoint.java:1665)
at java.lang.Thread.run(Thread.java:636) ``
``
2.导入为jar的问题
如果编译的jar运行的时候有报错“中没有主清单属性”,则说明你MANIFEST.MF(清单文件)放错了位置。
你MANIFEST.MF(清单文件)必须放在 src/main/resources/META_INF/
实例:
Restlet使用的jar库也比较多,所以还是使用maven管理:
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>groupId</groupId>
<artifactId>RestletForRestWebservcie</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>maven-restlet</id>
<name>Restlet repository</name>
<url>https://maven.restlet.com</url>
</repository>
</repositories>
<properties>
<restlet-version>2.3.4</restlet-version>
</properties>
<dependencies>
<dependency>
<groupId>org.restlet.jse</groupId>
<artifactId>org.restlet</artifactId>
<version>${restlet-version}</version>
</dependency>
<dependency>
<groupId>org.restlet.jse</groupId>
<artifactId>org.restlet.ext.jackson</artifactId>
<version>${restlet-version}</version>
</dependency>
<dependency>
<groupId>org.restlet.jee</groupId>
<artifactId>org.restlet</artifactId>
<version>${restlet-version}</version>
</dependency>
<dependency>
<groupId>org.restlet.jee</groupId>
<artifactId>org.restlet.ext.servlet</artifactId>
<version>${restlet-version}</version>
</dependency>
<dependency>
<groupId>org.restlet.jee</groupId>
<artifactId>org.restlet.ext.json</artifactId>
<version>${restlet-version}</version>
</dependency>
<dependency>
<groupId>org.restlet.jee</groupId>
<artifactId>org.restlet.ext.xml</artifactId>
<version>${restlet-version}</version>
</dependency>
</dependencies>
</project>
web.xml:
当Restlet项目是放在tomcat容器中,则需要在web.xml中配置一个类似拦截器的servlet来拦截和转发请求。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<!-- Restlet adapter -->
<servlet>
<servlet-name>RestletServlet</servlet-name>
<servlet-class>org.restlet.ext.servlet.ServerServlet</servlet-class>
<init-param>
<!-- Application class name -->
<param-name>org.restlet.application</param-name>
<param-value>adages.AdagesApplication</param-value>
</init-param>
</servlet>
<!-- Dispach all requests to the Restlet servlet. -->
<servlet-mapping>
<servlet-name>RestletServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
Adage:
javabean类:
package adages;
/**
* Created by AlexY on 2016/7/1.
*/
public class Adage {
private String words; //一行字符串
private int wordCount;// 一行字符串单词的个数
private int id; //每行的行号
public Adage() {
}
@Override
public String toString() {
return String.format("%2d:",id)+ words + "--" + wordCount + "words";
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getWordCount() {
return wordCount;
}
// 因为在setWord的时候就可以得到wordCount
public void setWordCount(int wordCount) {
}
public String getWords() {
return words;
}
public void setWords(String words) {
this.words = words;
this.wordCount = words.trim().split("\\S+").length;
}
}
Adages :
存放Adage的list,
注意:为了线程安全,使用了CopyOnWriteArrayList来作为集合存放,同时使用了 AtomicInteger。
package adages;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Created by AlexY on 2016/7/1.
*/
public class Adages {
private static CopyOnWriteArrayList<Adage> adages;
private static AtomicInteger id;
static {
String[] aphorisms =
{"What can be shown cannot be said.",
"If a lion could talk, we could not understand him.",
"Philosophy is a battle against the bewitchment of our intelligence by means of language.",
"Ambition is the death of thought.",
"The limits of my language mean the limits of my world."};
adages = new CopyOnWriteArrayList<>();
id = new AtomicInteger();
for (String str : aphorisms){
add(str);
}
}
public static String toPlain(){
// TODO: 2016/7/1 可以用stringbuffer优化
String retval = "";
int i = 1;
for (Adage adage : adages){
retval += adage.toString() + "\n";
}
return retval;
}
public static CopyOnWriteArrayList<Adage> getList(){
return adages;
}
public static Adage find(int id){
Adage adage = null;
for ( Adage a : adages){
if ( a.getId() == id){
adage = a;
break;
}
}
return adage;
}
// 支持post请求
public static void add(String str) {
int localId = id.incrementAndGet();
Adage adage = new Adage();
adage.setWords(str);
adage.setId(localId);
adages.add(adage);
}
}
XmlAllResource :
这是个资源类,必须继承ServerResource类。
可以使用@Get之类的注解,表示在接收到该类型的http请求的 时候,使用该方法做出响应。
但是这个响应的类型没有JAX-RS方便,需要自己手动写方法生成xml或json
返回整个list,以xml的形式。
package adages;
import org.restlet.data.MediaType;
import org.restlet.ext.xml.DomRepresentation;
import org.restlet.representation.Representation;
import org.restlet.resource.Get;
import org.restlet.resource.ServerResource;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import java.io.IOException;
import java.util.List;
/**
* Created by AlexY on 2016/7/1.
*/
public class XmlAllResource extends ServerResource {
public XmlAllResource() {
}
@Get
public Representation toXml(){
List<Adage> list = Adages.getList();
DomRepresentation dom = null;
try {
dom = new DomRepresentation(MediaType.TEXT_XML);
dom.setIndenting(true);
Document doc = dom.getDocument();
Element root = doc.createElement("adages");
for ( Adage adage : list){
Element next = doc.createElement("adage");
next.appendChild(doc.createTextNode(adage.toString()));
root.appendChild(next);
}
doc.appendChild(root);
} catch (IOException e) {
e.printStackTrace();
}
return dom;
}
}
JsonAllResource
资源类
返回整个list,以json形式
package adages;
import org.restlet.ext.json.JsonRepresentation;
import org.restlet.representation.Representation;
import org.restlet.representation.StringRepresentation;
import org.restlet.resource.Get;
import org.restlet.resource.ServerResource;
import java.util.List;
/**
* Created by AlexY on 2016/7/1.
*/
public class JsonAllResource extends ServerResource {
public JsonAllResource() {
}
@Get
public Representation toJson(){
List<Adage> list = Adages.getList();
// 生成jspn Representation
JsonRepresentation json = null;
try {
json = new JsonRepresentation(new StringRepresentation(list.toString())) ;
}catch (Exception e){
e.printStackTrace();
}
return json;
}
}
XmlOneResource :
这是个资源类,必须继承ServerResource类。
返回单条记录,以xml的形式。
package adages;
import org.restlet.data.MediaType;
import org.restlet.data.Status;
import org.restlet.ext.xml.DomRepresentation;
import org.restlet.representation.Representation;
import org.restlet.representation.StringRepresentation;
import org.restlet.resource.Get;
import org.restlet.resource.ServerResource;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import java.io.IOException;
import java.util.List;
/**
* Created by AlexY on 2016/7/1.
*/
public class XmlOneResource extends ServerResource {
public XmlOneResource() {
}
// 响应get请求,并返回xml
@Get
public Representation toXml(){
String sid = (String) getRequest().getAttributes().get("id");
if ( null == sid){
return badRequest("No ID provided\n");
}
int id;
try {
id = Integer.parseInt(sid.trim());
}catch (Exception e){
return badRequest("No such ID\n");
}
// 搜索这个Adage
List<Adage> list = Adages.getList();
Adage adage = Adages.find(id);
if ( null == adage){
return badRequest("No adage with ID " + id + "\n");
}
// 生成xml响应
DomRepresentation dom = null;
try {
dom = new DomRepresentation(MediaType.TEXT_XML);
// xml文档是否缩进
dom.setIndenting(true);
Document doc = dom.getDocument();
// 创建根节点
Element root = doc.createElement("adage");
root.appendChild(doc.createTextNode(adage.toString()));
// 将生成的xml放到响应中
doc.appendChild(root);
} catch (IOException e) {
e.printStackTrace();
}
return dom;
}
private Representation badRequest(String msg) {
Status error = new Status(Status.CLIENT_ERROR_BAD_REQUEST, msg);
return new StringRepresentation(error.toString());
}
}
CreateResource
资源类
响应post请求,用于创建新记录
返回TEXT_PLAIN (纯文本)
package adages;
import org.restlet.data.Form;
import org.restlet.data.MediaType;
import org.restlet.data.Status;
import org.restlet.representation.Representation;
import org.restlet.representation.StringRepresentation;
import org.restlet.resource.Post;
import org.restlet.resource.ServerResource;
/**
* Created by AlexY on 2016/7/1.
*/
public class CreateResource extends ServerResource {
public CreateResource() {
}
@Post
public Representation create(Representation data){
Status status = null;
String msg = null;
// 从post的body中提取数据
Form form = new Form(data);
String words = form.getFirstValue("words");
if ( null == words){
msg = "No words were given for the adage.\n";
status = Status.CLIENT_ERROR_BAD_REQUEST;
}else{
Adages.add(words);
msg = "The adage '" + words + "' has been added.\n";
status = Status.SUCCESS_OK;
}
setStatus(status);
return new StringRepresentation(msg, MediaType.TEXT_PLAIN);
}
}
UpdateResource
资源类
响应put请求,用于更新记录
package adages;
import org.restlet.data.Form;
import org.restlet.data.MediaType;
import org.restlet.data.Status;
import org.restlet.representation.Representation;
import org.restlet.representation.StringRepresentation;
import org.restlet.resource.Put;
import org.restlet.resource.ServerResource;
/**
* Created by AlexY on 2016/7/1.
*/
public class UpdateResource extends ServerResource {
public UpdateResource() { }
@Put
public Representation update(Representation data) {
Status status = null;
String msg = null;
// Extract the data from the POST body.
Form form = new Form(data);
String sid = form.getFirstValue("id");
String words = form.getFirstValue("words");
if (sid == null || words == null) {
msg = "An ID and new words must be provided.\n";
status = Status.CLIENT_ERROR_BAD_REQUEST;
}
else {
int id = Integer.parseInt(sid.trim());
Adage adage = Adages.find(id);
if (adage == null) {
msg = "There is no adage with ID " + id + "\n";
status = Status.CLIENT_ERROR_BAD_REQUEST;
}
else {
adage.setWords(words);
msg = "Adage " + id + " has been updated to '" + words + "'.\n";
status = Status.SUCCESS_OK;
}
}
setStatus(status);
return new StringRepresentation(msg, MediaType.TEXT_PLAIN);
}
}
AdagesApplication
Application类
这里使用了一个匿名类来处理DETELE请求,用于删除记录。
该类还用来配置路由。
package adages;
import org.restlet.Application;
import org.restlet.Request;
import org.restlet.Response;
import org.restlet.Restlet;
import org.restlet.data.Status;
import org.restlet.routing.Router;
/**
* Created by AlexY on 2016/7/1.
*/
public class AdagesApplication extends Application {
@Override
public synchronized Restlet createInboundRoot() {
// 通过一个匿名类来实现了DELETE操作,其他的操作通过独立的java类实现
// DELETE处理
Restlet janitor = new Restlet(getContext()) {
@Override
public void handle(Request request, Response response) {
String msg = null;
String sid = (String ) request.getAttributes().get("id");
if ( null == sid){
msg = badRequest("No ID given.\n");
}
Integer id = null;
try {
id = Integer.parseInt(sid.trim());
} catch (Exception e){
msg = badRequest("Ill-formed ID.\n");
}
Adage adage = Adages.find(id);
if ( null == adage){
msg = badRequest("No adage with ID " + id + "\n");
}else {
Adages.getList().remove(adage);
msg = "Adage " + id + " removed.\n";
}
response.setEntity(msg, org.restlet.data.MediaType.TEXT_PLAIN);
}
};
// 创建路由表
Router router = new Router(getContext());
router.attach("/", PlainResource.class);
router.attach("/xml", XmlAllResource.class);
router.attach("/xml/{id}", XmlOneResource.class);
router.attach("/json", JsonAllResource.class);
router.attach("/create", CreateResource.class);
router.attach("/update", UpdateResource.class);
return router;
}
private String badRequest(String msg) {
Status error = new Status(Status.CLIENT_ERROR_BAD_REQUEST,msg);
return error.toString();
}
}
Main
如果你是将Restlet放到tomcat之类的容器,则不需要这个类。
如果是像Restlet编译为jar文件,单独作为httpserver来响应http请求,则需要这个类。当然也可以将main方法放到其他的类里面,这里为了方便区分,所以单独放到一个java文件中。
package adages;
import org.restlet.Component;
import org.restlet.data.Protocol;
/**
* Created by AlexY on 2016/7/1.
*/
public class Main {
public static void main(String[] args) throws Exception {
// 创建一个ixn的Component
Component component = new Component();
// 新增一个http server监听8182端口
component.getServers().add(Protocol.HTTP, 8182);
// 将应用的appilcation类添加进去
// 应用的url就是:
// http://localhost:8182/adages/xml/1
component.getDefaultHost().attach("/adages", new AdagesApplication());
// 启动服务.
component.start();
}
}
在tomcat下运行的效果:
运行后访问的地址:
http://localhost:8182/adages/
获取单个的xml记录:
http://localhost:8182/adages/xml/1