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

6 comments:

  1. Eduveks, vi um post seu há muito tempo sobre instalar o driver da placa de wireless rt61, procurei lá o link que você colocou mas não existe mais. Instalei internet via radio e não consigo acessar pelo linux, se puder me ajudar ficaria grato. Eu uso o Ubuntu 8.04
    rodrigocliraATyahoo.com.br

    ReplyDelete
  2. Olá Sou Pedro Almeida, sou desenvolvedor jata, e criei um webservice em jetty utilizando seu exemplo como base, porém não consigo consumir o mesmo com HTTPS, vc tem algum exemplo?

    ReplyDelete
  3. Olá Pedro!

    Nunca usei o HTTPS do Jetty, por que normalmente uso o Nginx fazendo de proxy reverso para o Jetty, e o HTTPS fica do lado do Nginx.

    Por isso não tenho nenhum exemplo...

    HTTP SSL Module:
    http://wiki.nginx.org/HttpSslModule

    Proxy Reverso:
    http://wiki.nginx.org/JavaServers

    E tem aqui mais um exemplo:
    http://eduveks.blogspot.com/search/label/Nginx

    Qualquer dúvida só dizer.

    ReplyDelete
  4. Obrigado pela resposta Eduardo, talvez consiga me ajudar, pois estou no seguinte cenário:

    Criei o webservice utilizando as annotations como explicado através do Jax-WS, gerei um ".war"

    Este é carregado através de uma aplicação Jetty embedded que abre o war, utilizando um connector SelectChannelConnector.

    Para acesso ao jetty uso o Apache que faz o caminho http->jetty:9000, https->jetty:9000, porém fazendo esse proxy através do apache ao gerar as classes para consumo do método através do wsimport do netbeans métodos não são consumidos, gerando exception (Cannot find 'https://webservice.comunika.com.br/WSComunika/WSReceive?wsdl' wsdl. Place the resource correctly in the classpath.)

    Estou com este problema, e não consigo solucioná-lo.

    Estou tentando diversas soluções, porém ainda sem êxito.

    WSDL gerado consigo consumi-lo através do soapUI tanto através do http ou https, porém com classes geradas apenas no http.

    WSDL: https://webservice.comunika.com.br/WSComunika/WSReceive?wsdl

    Imaginei que fosse por que não existia internamente no wsdl os endereços HTTPS, no ambiente de desenvolvimento consegui alterar o apache de forma que os endereços fossem alterados para https:443 mas mesmo assim não funciona.

    Você imagina o que pode estar ocorrendo?

    ReplyDelete
  5. Pelo que observo logo é o schemaLocation e os namespace do HTTPS estão como HTTP.

    Ou seja vc tem que publicar ter tudo publicado como se fosse HTTPS.

    Talvez publicando dois webservices assim:

    https://webservice.comunika.com.br/WSComunika/WSReceive

    E o outro com outro nome, mas com as configurações de HTTPS:

    https://webservice.comunika.com.br/WSComunika/WSReceiveSSL

    Ou então vc tb pode tentar usar o mod_rewrite do apache para alterar o header e simular para o cliente que vem do HTTPS vc traduz o pedido para HTTP para o Jetty conseguir encontrar o web service.

    Basicamente o Jetty esta configurado o web service como HTTP, e chega um pedido invocando um web service em HTTPS, mas n existe nada publicado com o namespace HTTPS e ai ele n encontra...

    ReplyDelete
  6. Eu criei uma entrada no fórum do javafree com maiores detalhes:

    http://javafree.uol.com.br/topic-881423-Jetty-falha-no-consumo-de-Webservice-HTTPS.html

    Você acredita que possa ser o mesmo erro?
    pois modifiquei o namespace para https e coninua mesmo erro

    ReplyDelete