欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Restlet实战(三)使用Component让不同的Application对应不同的资源

程序员文章站 2022-07-03 20:53:06
...

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.