在Restlet实战(二)我给出的例子中,把Order和Customer两个资源attach到Order Application上,看如下代码:
public class OrderApplication extends Application { @Override public synchronized Restlet createRoot() { Router router = new Router(getContext()); router.attach("/order/{orderId}/{subOrderId}", OrderResource.class); router.attach("/customer/{custId}", CustomerResource.class); return router; } }
这显而易见不是一个好的应用,从关注点来看,Order Resource应该attach到Order Application,那么对应Customer Resource应该有一个Customer Application与之对应。
ok,基于这样的思路,我们从OrderApplication删除如下代码:
router.attach("/customer/{custId}", CustomerResource.class);
另外我们创建一个CustomerApplication,并包含上述代码:
public class CustomerApplication extends Application{ @Override public synchronized Restlet createRoot() { Router router = new Router(getContext()); router.attach("/customers/{custId}", CustomerResource.class); return router; } }
有了CustomerApplication,那么如何设置才能生效呢?根据之前的配制,很容易想到的是在web.xml中把CustomerApplication配制进去,事实上是不行的,看看ServerServlet类里面这段代码:
private static final String APPLICATION_KEY = "org.restlet.application"; protected Application createApplication(Context parentContext) { Application application = null; final String applicationClassName = getInitParameter(APPLICATION_KEY, null);
这段代码是根据参数名org.restlet.application来获得配制在web.xml中application,而之前我们已经配制了OrderApplication,所以,我们不能再把CustomerApplication配制的参数名设置为org.restlet.application。那么在ServerServlet加载application的时候,就加载不到。
那么怎么作能让这两个Application生效呢?答案是Component,实际上,从上一篇文章中的图可以看出,一个Component可以设置多个Virtual Host,而一个Virtual Host能attach多个Application。
至于使用Component的做法有两种,一种是在web.xml中配制一个名字为“org.restlet.component”的context参数,例如:
<context-param> <param-name>org.restlet.component</param-name> <param-value>com.mycompany.MyComponent</param-value> </context-param>
另外一种是,WEB-INF/下存在restlet.xml,可以在这个xml文件里定义包含application和connector的Commponent。
下面就第二种方式来解决上面出现的问题,首先在WEB-INF/下建立一个名为restlet.xml,这个名字不能改变成别的名字,为啥呢?仍然看一下ServerServlet里面的一段代码:
protected Component createComponent() { Component component = null; // Look for the Component XML configuration file. Client client = createWarClient(new Context(), getServletConfig()); Response response = client.get("war:///WEB-INF/restlet.xml");
既然不让改,不改好了,在restlet.xml里面放入配制Component的代码:
<?xml version="1.0"?> <component xmlns="http://www.restlet.org/schemas/1.1/Component" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.restlet.org/schemas/1.1/Component"> <!-- <client protocol="CLAP" /> <client protocol="FILE" /> <client protocols="HTTP HTTPS" /> <server protocols="HTTP HTTPS" port="8080"/> --> <defaultHost> <attach uriPattern="/customers/{custId}" targetClass="com.mycompany.restlet.application.CustomerApplication" /> <attach uriPattern="/orders/{orderId}/{subOrderId}" targetClass="com.mycompany.restlet.application.OrderApplication" /> <!-- <attach uriPattern="/efgh/{xyz}" targetDescriptor="clap://class/org/restlet/test/MyApplication.wadl" /> --> </defaultHost> </component>
看上面的配制文件,很清楚的我们能知道,如果在浏览器输入http://localhost:8080/restlet/customers/1,那么CustomerApplication这个类应该被访问到。
咦,好象有点不对,好像CustomerApplication里面在attach资源的时候,也指定了同样的URL(/customers/{custId}),那么是当前这两个URL定义的URL重复了?还是有别的含义?
我们可以这样理解,在restlet.xml中定义的URL附加到Context上,而在application里面定义的URL会附加到restlet.xml中定义的URL上。
例如,如果想通过http://localhost:8080/restlet/customers/1 来使用CustomerResource,则需要在CustomerApplication中有如下代码:
router.attach("", CustomerResource.class);
而如果想通过http://localhost:8080/restlet/customers/1/orders/2 来使用CustomerResource,则需要在CustomerApplication中定义如下代码:
router.attach("/orders/{orderId}", CustomerResource.class);
Ok,让我们测试一下,首先修改
CustomerApplication.java
public class CustomerApplication extends Application{ @Override public synchronized Restlet createRoot() { Router router = new Router(getContext()); //router.attach("", CustomerResource.class); router.attach("/orders/{orderId}", CustomerResource.class); return router; } }
CustomerResource.java
public class CustomerResource extends Resource { String customerId; String orderId; public CustomerResource(Context context, Request request, Response response) { super(context, request, response); customerId = (String) request.getAttributes().get("custId"); orderId = (String) request.getAttributes().get("orderId"); // This representation has only one type of representation. getVariants().add(new Variant(MediaType.TEXT_PLAIN)); } @Override public Representation getRepresentation(Variant variant) { Representation representation = new StringRepresentation( "The customer which id is " + customerId + " has the order which id : " + orderId, MediaType.TEXT_PLAIN); return representation; } }
输入http://localhost:8080/restlet/customers/1/orders/2,结果如下:
The customer which id is 1 has the order which id : 2
请注意,上面CustomerApplicaiton被注释的一行代码
router.attach("", CustomerResource.class);
如果反注释这行代码,则所有基于http://localhost:8080/restlet/customers/1/****(除了/customers/{custId}/orders/{orderId} )的访问都会路由到同一个资源(CustomerResource),换句话说,如果我访问一个我没有定义的URL,如/customers/{customerId}/orders,则也会被路由到CustomerResource,这是不对的。。所以,应慎用之。
总结,现在我们可以分别用不同的Application来管理不同的资源了,并采用restlet.xml这种方式来定义包含applicaiton和connector的component.