用JAX-WS发布RESTful Web Service,首先要创建一个对javax.xml.ws.Provider
我们要创建的示例Web服务非常简单,它把两个数相加然后返回结果,而我们将在Provider实现中使用Provider
public class AddNumbersImpl implements Provider {}
接下来,声明@Resource标记,它被用来在JAX-WS 运行时注射WebServiceContext到我们的AddNumbersImpl 实例中。
public class AddNumbersImpl implements Provider { @Resource protected WebServiceContext wsContext; }
下一步是实现T Provider.invoke(T request)方法。我们首先写出下面的方法声明,它是一个简单的try-catch块用来处理异常。注意,该方法以一个Source对象为请求,并返回一个Source对象作为响应。这符合我们创建的Provider类型。
public class AddNumbersImpl implements Provider { @Resource protected WebServiceContext wsContext;
public Source invoke(Source request) { try {
} catch(Exception e) { e.printStackTrace(); throw new HTTPException(500); } } }
在本例中,AddNumbers Web服务将通过从URL路径或HTTP请求中抽取要相加的数字来获得请求。请求字符串和路径字符串可以从MessageContext中获取,而它又从WebServiceContext wsContext中获得,它会被注射到我们的AddNumbers对象中。下面的代码背用来从URL中获取PATH_INFO并检查看它是否是合适的格式。
String path = (String)mc.get(MessageContext.PATH_INFO); if (path != null && path.contains("/num1") && path.contains("/num2")) { return createResultSource(path); }
createResultSource(String str)方法只从合适的被格式化的MessageContext.PATH_INFO字符串中创建Source对象。它从路径中抽取要相加的数字,然后把它们相加并调用createResultSource(int sum)方法。下面是这两个方法的源代码:
private Source createResultSource(String str) { StringTokenizer st = new StringTokenizer(str, "=&/"); String token = st.nextToken(); int number1 = Integer.parseInt(st.nextToken()); st.nextToken(); int number2 = Integer.parseInt(st.nextToken()); int sum = number1+number2; return createResultSource(sum); }
private Source createResultSource(int sum) { String body = "
于是,我们的示例invoke方法就会是下面这个样子。
public Source invoke(Source source) { try { MessageContext mc = wsContext.getMessageContext(); // check for a PATH_INFO request String path = (String)mc.get(MessageContext.PATH_INFO); if (path != null && path.contains("/num1") && path.contains("/num2")) { return createResultSource(path); } throw new HTTPException(404); } catch(Exception e) { e.printStackTrace(); throw new HTTPException(500); } }
你会发现,如果MessageContext.PATH_INFO没有找到,该示例会抛出HTTPException(404)异常。我们的示例终端并不抛出该异常,而是能为方法参数检查请求字符串。例如MessageContext.PATH_INFO,请求字符串可以用下面的代码从MessageContext中获得。
String query = (String)mc.get(MessageContext.QUERY_STRING);
于是,我们可以把请求字符串传递到createResultSource(String str)方法中来解析参数,如果要为我们的示例部署一个servlet容器也可以使用标准ServletRequest对象来抽取请求字符串。而如果我们想部署一个基于Java SE的终端,我们就必须使用MessageContext.QUERY_STRING。
下面的代码获取了num1和num2的值,相加后调用createResultSource(int sum)方法来创建Source对象,它将从invoke方法中被返回。
ServletRequest req = (ServletRequest)mc.get(MessageContext.SERVLET_REQUEST); int num1 = Integer.parseInt(req.getParameter("num1")); int num2 = Integer.parseInt(req.getParameter("num2")); return createResultSource(num1+num2);
至此,我们的AddNumbers类应该如下所示:
public class AddNumbersImpl implements Provider {
@Resource protected WebServiceContext wsContext;
public Source invoke(Source source) { try { MessageContext mc = wsContext.getMessageContext(); // check for a PATH_INFO request String path = (String)mc.get(MessageContext.PATH_INFO); if (path != null && path.contains("/num1") && path.contains("/num2")) { return createResultSource(path); } String query = (String)mc.get(MessageContext.QUERY_STRING); System.out.println("Query String = "+query); ServletRequest req = (ServletRequest)mc.get(MessageContext.SERVLET_REQUEST); int num1 = Integer.parseInt(req.getParameter("num1")); int num2 = Integer.parseInt(req.getParameter("num2")); return createResultSource(num1+num2); } catch(Exception e) { e.printStackTrace(); throw new HTTPException(500); } } private Source createResultSource(String str) { StringTokenizer st = new StringTokenizer(str, "=&/"); String token = st.nextToken(); int number1 = Integer.parseInt(st.nextToken()); st.nextToken(); int number2 = Integer.parseInt(st.nextToken()); int sum = number1+number2; return createResultSource(sum); } private Source createResultSource(int sum) { String body = "
要完成AddNumbers,我们需要声明一些标记来说明JAX-WS运行时如何使用这个类。@WebServiceProvider表明该类是一个基于Provider的终端而不是一个需要用@WebService指明的服务终端实现类。最后需要我们添加的标记是@BindingType(value=HTTPBinding.HTTP_BINDING)。该标记指明了AddNumbers终端将用HTTPBinding.HTTP_BINDING而不是SOAPBinding.SOAP11HTTP_BINDING或者SOAPBinding.SOAP12HTTP_BINDING来绑定。下面是最终该类的代码:
@WebServiceProvider @BindingType(value=HTTPBinding.HTTP_BINDING) public class AddNumbersImpl implements Provider {
@Resource protected WebServiceContext wsContext;
public Source invoke(Source source) { try { MessageContext mc = wsContext.getMessageContext(); // check for a PATH_INFO request String path = (String)mc.get(MessageContext.PATH_INFO); if (path != null && path.contains("/num1") && path.contains("/num2")) { return createResultSource(path); } String query = (String)mc.get(MessageContext.QUERY_STRING); System.out.println("Query String = "+query); ServletRequest req = (ServletRequest)mc.get(MessageContext.SERVLET_REQUEST); int num1 = Integer.parseInt(req.getParameter("num1")); int num2 = Integer.parseInt(req.getParameter("num2")); return createResultSource(num1+num2); } catch(Exception e) { e.printStackTrace(); throw new HTTPException(500); } } private Source createResultSource(String str) { StringTokenizer st = new StringTokenizer(str, "=&/"); String token = st.nextToken(); int number1 = Integer.parseInt(st.nextToken()); st.nextToken(); int number2 = Integer.parseInt(st.nextToken()); int sum = number1+number2; return createResultSource(sum); } private Source createResultSource(int sum) { String body = "
要在运行了JAX-WS RI的servlet容器上部署我们的终端,我们需要创建一个WAR文件。例如,我们把WAR文件命名为jaxws-restful.war。该文件需要一个非常简单的web.xml文件来配置JAX-WS Ri servlet。代码如下:
接着,我们添加一个 sun-jaxws.xml部署描述符到该WAR文件。下面是示例:
你会注意到在web.xml和sun-jaxws.xml中有
你还会从sun-jaxws.xml部署描述符中注意到指定的一个wsdl文件。它很重要的告诉JAX-WS RI不要为该终端生成一个WSDL。下面是一个简单的AddNumbers.wsdl的内容。
一旦部署了AddNumbers终端,你就能在浏览器中输入下面的URL来测试它了。(根据你配置的不同可能会有一点点不同)
http://localhost:8080/jaxws-restful/addnumbers/num1/10/num2/20 Should return the following:
http://localhost:8080/jaxws-restful/addnumbers?num1=10&num2=50 Should return the following: