INSTALAÇÃO E USO DO GOOGLE APP ENGINE PLUGIN utilizando o Eclipse Galileo Desenvolvido por: Fabrício Alves e Daniela Claro contato: [email protected], [email protected] Requisitos de Ambiente: − Java 1.6 http://java.sun.com/javase/downloads/widget/jdk6.jsp − Google App engine Plugin http://code.google.com/intl/pt-BR/eclipse/docs/download.html Sumário: A partir da versão 1.4.2 do Google App Engine é possível utilizar java.soap.xml e JAX-B para desenvolver um servidor SOAP. Este tutorial visa construir uma simples aplicação que recebe como parâmetro uma temperatura medida em Farenheit e devolve se este tempo é considerado Frio ou Quente. 1. Abra o Wizard de Criação de novo projeto, clicando no botão “Google Service and Development Tools” que se encontra na Barra de Ferramentas. Figura 1: Criando um novo Projeto de Aplicação web do App Engine Dê ao projeto o nome WeatherSOAPServer e insira o valor com.example no campo Package. Desmarque a opção “Use Google Web Toolkit”. Figura 2: Nomeando o Serviço Perceba que a classe WeatherSOAPServerServlet.java foi criada automaticamente. Esta classe será importante futuramente, pois gerencia as requisições feitas ao servidor. 2. Crie dentro do pacote com.example a classe ClimaInfo, clicando com o botão direito sobre o pacote e escolhendo a opção New > Class. Figura 3: Criação de Classe Nós vamos manter esta classe muito simples. Ela terá apenas o método comoEstaOTempo: package com.example; public class ClimaInfo { public String comoEstaOTempo(int temperatura){ if(temperatura > 30){ return "Quente"; } else if(temperatura < 15){ return "Frio"; } else{ return "Agradavel"; } } } 3. Adicione as anotações de WebService, presentes no pacote javax.jws.WebMethod. Tais anotações serão levadas em consideração no momento de criação do WSDL. package com.example; import javax.jws.WebMethod; import javax.jws.WebService; @WebService public class ClimaInfo { @WebMethod public String comoEstaOTempo(int temperatura){ if(temperatura > 30){ return "Quente"; } else if(temperatura < 15){ return "Frio"; } else{ return "Agradavel"; } } } Apesar do Google App Engine não suportar a utilização de JAX-WS no SOAP Server, iremos utilizá-lo para criar o arquivo wsdl automaticamente. 4. Vamos utilizar o wsgen, programa contigo no java JDK (à partir da versão 1.6), que gera wsdl a partir de anotações feitas no código. Além disso, o wsgen gera os POJOS, que são as classes que serão responsáveis pelo request e response. Entre na interface de comandos, acesse o diretório do projeto criado (workspace/WeatherSOAPServer/) e cole o seguinte comando: wsgen -cp ".\war\WEB-INF\classes" -wsdl -keep -r ".\war" -d ".\war\WEB-INF\classes" -s ".\src" com.example.ClimaInfo Este comando é explicado da seguinte forma: A opção -cp indica onde o wsgen irá encontrar o arquivo ClimaInfo.class para gerar o WSDL. A opção -r indica onde o wsdl será gerado. A opção -d indica onde serão colocados os arquivos .class gerados. A opção -s indica onde serão colocados os arquivos fontes dos POJOS gerados. Por fim, a última opção indica qual será a classe que será transformada em Serviço Web. Selecione o projeto e pressione a tecla “f5”, para que os arquivos gerados sejam vistos dentro do Eclipse. 5. Coloque a URL de request do serviço dentro do WSDL. Abra o WSDL gerado, dentro do diretório war, e veja que o campo location, possui o valor REPLACE_WITH_ACTUAL_URI. Tal URI é o ponto de acesso para o serviço, ou seja, a URI da sua aplicação no Google App Engine. Geralmente esta URI é formada por <application-id>.appspot.com . <service name="ClimaInfoService"> <port name="ClimaInfoPort" binding="tns:ClimaInfoPortBinding"> <soap:address location="http://<application-id>.appspot.com/weathersoapserver"/> </port> </service> 6. Codificar um Adapatador que faça a ligação entre os POJOS gerados e a o verdadeiro código do Serviço Web. Obs: arquivo fonte presente em http://homes.dcc.ufba.br/~fabufbc#tutorialOwls package com.example; import com.example.jaxws.ComoEstaOTempo; import com.example.jaxws.ComoEstaOTempoResponse; public class ClimaInfoAdaptador { private ClimaInfo climaInfo = new ClimaInfo(); public ComoEstaOTempoResponse comoEstaOTempo(ComoEstaOTempo request){ int temperatura = request.getArg0(); String comoEstaOTempo = climaInfo.comoEstaOTempo(temperatura); ComoEstaOTempoResponse response = new ComoEstaOTempoResponse(); response.setReturn(comoEstaOTempo); return response; } } 7. Criar um Gerenciador entre a requisição e o Adaptador. Quando a requisição SOAP é feita para o servidor, a única coisa que é passada é um XML contendo o “soapBody”. Este soapBody informa quais os argumentos e seus valores. No entanto, é necessário que haja uma classe que recupere este XML e saiba qual serviço chamar. Caso o Google App Engine suportasse JAX-WS ou AXIS, este passo não seria necessário. Obs: arquivo fonte presente em http://homes.dcc.ufba.br/~fabufbc#tutorialOwls package com.example; import java.util.Iterator; import javax.xml.bind.JAXB; import javax.xml.namespace.QName; import javax.xml.soap.MessageFactory; import javax.xml.soap.SAAJResult; import javax.xml.soap.SOAPBody; import javax.xml.soap.SOAPElement; import javax.xml.soap.SOAPException; import javax.xml.soap.SOAPFault; import javax.xml.soap.SOAPMessage; import javax.xml.transform.dom.DOMSource; import com.example.jaxws.ComoEstaOTempo; public class ClimaInfoSoapHandler { private static final String NAMESPACE_URI = "http://example.com/"; private static final QName COMO_ESTA_O_TEMPO_QNAME = new QName(NAMESPACE_URI, "comoEstaOTempo"); private MessageFactory messageFactory; private ClimaInfoAdaptador climaInfoAdapter; public ClimaInfoSoapHandler() throws SOAPException { messageFactory = MessageFactory.newInstance(); climaInfoAdapter = new ClimaInfoAdaptador(); } public SOAPMessage handleSOAPRequest(SOAPMessage request) throws SOAPException { SOAPBody soapBody = request.getSOAPBody(); Iterator iterator = soapBody.getChildElements(); Object responsePojo = null; while (iterator.hasNext()) { Object next = iterator.next(); if (next instanceof SOAPElement) { SOAPElement soapElement = (SOAPElement) next; QName qname = soapElement.getElementQName(); if (COMO_ESTA_O_TEMPO_QNAME.equals(qname)) { responsePojo = handleComoEstaOTempoRequest(soapElement); break; } } } SOAPMessage soapResponse = messageFactory.createMessage(); soapBody = soapResponse.getSOAPBody(); if (responsePojo != null) { JAXB.marshal(responsePojo, new SAAJResult(soapBody)); } else { SOAPFault fault = soapBody.addFault(); fault.setFaultString("Unrecognized SOAP request."); } return soapResponse; } private Object handleComoEstaOTempoRequest(SOAPElement soapElement) { ComoEstaOTempo comoEstaOTempoRequest = JAXB.unmarshal(new DOMSource(soapElement), ComoEstaOTempo.class); return climaInfoAdapter.comoEstaOTempo(comoEstaOTempoRequest); } } 8. Informar ao Servlet o que fazer quando for uma requisição SOAP. Todas as requisições feitas ao servidor são gerenciadas pelo Servlet. Logo, é necessário informá-lo que deverá chamar o handler quando a requisição for SOAP. Sendo assim, dentro do servlet vamos criar uma função doPost() que realizará esta chamada. Obs: arquivo fonte presente em http://homes.dcc.ufba.br/~fabufbc#tutorialOwls package com.example; import import import import import java.io.IOException; java.io.InputStream; java.io.OutputStream; java.util.Enumeration; java.util.StringTokenizer; import import import import import import import javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse; javax.xml.soap.MessageFactory; javax.xml.soap.MimeHeaders; javax.xml.soap.SOAPException; javax.xml.soap.SOAPMessage; @SuppressWarnings("serial") public class WeatherSOAPServerServlet extends HttpServlet { static MessageFactory messageFactory; static ClimaInfoSoapHandler soapHandler; static { try { messageFactory = MessageFactory.newInstance(); soapHandler = new ClimaInfoSoapHandler(); } catch (Exception ex) { throw new RuntimeException(ex); } } public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { resp.setContentType("text/plain"); resp.getWriter().println("Hello, world"); } @Override public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { try { // Get all the headers from the HTTP request MimeHeaders headers = getHeaders(req); // Construct a SOAPMessage from the XML in the request body InputStream is = req.getInputStream(); SOAPMessage soapRequest = messageFactory.createMessage(headers, is); // Handle soapReqest SOAPMessage soapResponse = soapHandler .handleSOAPRequest(soapRequest); } // Write to HttpServeltResponse resp.setStatus(HttpServletResponse.SC_OK); resp.setContentType("text/xml;charset=\"utf-8\""); OutputStream os = resp.getOutputStream(); soapResponse.writeTo(os); os.flush(); } catch (SOAPException e) { throw new IOException("Exception while creating SOAP message.", e); } @SuppressWarnings("unchecked") static MimeHeaders getHeaders(HttpServletRequest req) { Enumeration headerNames = req.getHeaderNames(); MimeHeaders headers = new MimeHeaders(); while (headerNames.hasMoreElements()) { String headerName = (String) headerNames.nextElement(); String headerValue = req.getHeader(headerName); StringTokenizer values = new StringTokenizer(headerValue, ","); while (values.hasMoreTokens()) { headers.addHeader(headerName, values.nextToken().trim()); } } } } return headers; 9. Por fim, realizar a publicação no Google App Engine. Clique com o botão direito sobre o projeto e escolha Google > App Engine Settings. No campo Application ID, vamos colocar o ID da aplicação, criada anteriormente. Referências: [1] HOW TO: Build a SOAP Server and a SOAP Client on Google App Engine, disponível em: http://code.google.com/intl/pt-BR/appengine/articles/soap.html , último acesso em 23/11/2011