Friday, June 19, 2009

Jetty e Web Services com JAX-WS

O Jetty é um poderoso servidor web, disto não há dúvidas, mas no que toca a web services paira uma nuvem negra, não há muita documentação e nem integração com IDEs.

Depois de pesquisar um pouco vi que era possível integrar com o JAX-WS.

Não encontrei nenhuma explicação detalhada de como fazer, e que explicasse também a parte do cliente, então resolvi fazer o download do JAX-WS e ver os exemplos e a documentação, e a conclusão que cheguei segue abaixo.

Levando em consideração que já tem o Jetty configurado e rodando, no meu caso foi a versão 6 e com o JDK 6.

Fazer o download do JAX-WS no meu caso foi a versão 2.1.7 em:

https://jax-ws.dev.java.net/

Descompactar e em jaxws-ri/docs/UsersGuide.html#3.1_Server da para ver alguma explicação de como fazer.

Copiar todos os jars que estão na pasta lib do JAX-WS para a pasta lib/ext do Jetty.

Agora no seu projeto web criar as seguintes classes de exemplo (também estão em jaxws-ri/samples/fromjava/src), aqui dei alguma melhoria nas anotações:

-> fromjava/server/AddNumbersImpl.java:

package fromjava.server;
import javax.jws.WebService;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.ws.WebServiceContext;
import javax.xml.ws.handler.MessageContext;
import javax.annotation.Resource;
@WebService
public class AddNumbersImpl {
@Resource
private WebServiceContext context;
@WebMethod(operationName = "add")
public int addNumbers(@WebParam(name = "first") int number1, @WebParam(name = "second") int number2) throws AddNumbersException {
// SERVLET - IF NEED SERVLET RESOURCES. For example to do session control.
MessageContext messageContext = context.getMessageContext();
ServletContext servletContext = (ServletContext)messageContext.get(MessageContext.SERVLET_CONTEXT);
HttpServletRequest servletRequest = (HttpServletRequest)messageContext.get(MessageContext.SERVLET_REQUEST);
HttpServletResponse servletResponse = (HttpServletResponse)messageContext.get(MessageContext.SERVLET_RESPONSE);
// --
if (number1 < 0 || number2 < 0) {
throw new AddNumbersException("Negative number cant be added!",
"Numbers: " + number1 + ", " + number2);
}
return number1 + number2;
}
}

-> fromjava/server/AddNumbersException.java:

package fromjava.server;
public class AddNumbersException extends Exception {
String detail;
public AddNumbersException (String message, String detail) {
super (message);
this.detail = detail;
}
public String getDetail () {
return detail;
}
}

Acrescentar no web.xml:

<listener>
<listener-class>com.sun.xml.ws.transport.http.servlet.WSServletContextListener</listener-class>
</listener>
<servlet>
<description>JAX-WS endpoint - fromjava</description>
<display-name>fromjava</display-name>
<servlet-name>fromjava</servlet-name>
<servlet-class>com.sun.xml.ws.transport.http.servlet.WSServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>fromjava</servlet-name>
<url-pattern>/addnumbers</url-pattern>
</servlet-mapping>

E junto com o web.xml criar o arquivo sun-jaxws.xml:

<?xml version="1.0" encoding="UTF-8"?>
<endpoints xmlns='http://java.sun.com/xml/ns/jax-ws/ri/runtime' version='2.0'>
<endpoint
name='fromjava'
implementation='fromjava.server.AddNumbersImpl'
url-pattern='/addnumbers'/>
</endpoints>

Feito isto, é rodar o Jetty e ver o WSDL:

http://localhost:8080/WSTester/addnumbers?WSDL

Onde o WSTester é o nome do seu projeto.

Agora temos que criar as classes para chamar o web service, para isto o JAX-WS trás o bin/wsimport, no meu caso como estou no Linux vou usar o .sh, caso esteja no Windows é só usar o .bat, criar as pastas src e dest dentro da pasta do JAX-WS.

$ chmod +x bin/wsimport.sh
$ mkdir dest
$ mkdir src
$ bin/wsimport.sh -d dest -s src http://localhost:8080/WSTester/addnumbers?WSDL

O wsimport vai gerar o código das classes para invocar o web service na pasta src, e as classes compiladas em dest.

Eu peguei na pasta src/fromjava e copiei para o um outro projeto de aplicação do tipo consola em Java. Assim pode chamar o web service:

TestAddNumbers.java:

package javaapplicationWSTester;
import fromjava.server.*;
public class TestAddNumbers {
public static void main(String[] args) throws AddNumbersException_Exception {
int result = new AddNumbersImplService().getAddNumbersImplPort().add(3, 5);
System.out.println(result);
}
}

O processo é simples, ter a classe do web service, configurar no web.xml e no sun-jaxws.xml, gerar as classes de invocação com o wsimport e boa :P