Na empresa onde trabalho, temos um sistema razoavelmente grande e multi-plataforma. Nesta semana nos deparamos com um problema* na comunicação (Mac Leopard) com um webservice SOAP 1.1. Este problema deveria ser solucionado em menos de 8 horas, a qualquer custo. Nosso webservice é baseado na especificação Jax-RPC (Java™ API for XML-based RPC) e estávamos utilizando bibliotecas providas pela Oracle para trabalhar com ele. Infelizmente não encontramos uma forma de solucionar o problema (no tempo necessário) com qualquer outra API disponível, afinal deveríamos implementar o proxy todo e nos assegurar que o problema não ocorreria novamente. Nossa decisão foi implementar manualmente a comunicação proxy-webservice (após muita pesquisa).
Aqui apresento uma solução inicial criada rapidamente apenas para teste (justificando a falta de beleza e orientação a objeto do código
). Note que esta solução não deve ser utilizada, ela serve apenas para dar um embasamento de como poderia ser implementada tal comunicação. Se entendeu a idéia, continue lendo
Então, sem mais delongas, qual a idéia?
Abrir uma conexão HTTP , enviar um XML e devolver um Objeto.Que tipo de objeto?
Qualquer objeto. Aqui apresento numa forma não tão bela, como tratar o XML de resposta para obter uma combinação qualquer dos seguintes tipos: String, Long, HashMap e List.Vamos começar?
Iniciando “pelo nível mais alto”, temos o método que recebe um XML (como String) de parâmetro, abre uma conexão que permite escrita, envia o XML recebido e retorna o nodo no qual o conteúdo está disponível.
public Node enviarEReceber(String envelopeXML){ Node node = null; try { URL u = new URL("enderecoDaPortaHttpDoServicoWeb"); HttpURLConnection connection = (HttpURLConnection)u.openConnection(); connection.setDoOutput(true); connection.setRequestMethod("POST"); connection.setRequestProperty("Content-Type","text/xml; charset=utf-8"); connection.connect(); enviarEnvelopeXML(connection,envelopeXML); node = getResposta(connection).getDocumentElement() .getFirstChild().getFirstChild().getFirstChild(); }catch (IOException e) { e.printStackTrace(); }catch (SAXException e) { e.printStackTrace(); }catch (ParserConfigurationException e){ e.printStackTrace(); }return node; }
Para enviar o envelope XML, tudo o que temos de fazer é escrevê-lo no stream da conexão.
private void enviarEnvelopeXML(HttpURLConnection connection, String envelopeXML) throws IOException {
OutputStream out = connection.getOutputStream();
Writer wout = new OutputStreamWriter(out);
wout.write(envelopeXML);
wout.flush();
wout.close();
}
Obtemos a resposta do serviço e o transformamos num documento XML.
private Document getResposta(HttpURLConnection connection) throws ParserConfigurationException, IOException, SAXException { Document doc = null; DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db = dbf.newDocumentBuilder(); InputStream in = connection.getInputStream(); doc = db.parse(in); in.close(); }
Precisamos do método que retorna um objeto baseado no documento XML retornado pelo serviço. Caso seja um objeto simples,
sem tipo especificado, como um Boolean, retornamos apenas o valor do nodo, senão tratamos o XML devidamente.
public Object getObjeto(Node nodo){ String xsiType = getXSIType(nodo); if (xsiType == null) // tipo simples, não precisa tratamento return nodo.getFirstChild().getNodeValue(); return getValueOf(nodo); }
Acima utilizamos dois métodos, um para verificar o tipo de dados (XSIType) e outro para obter o valor do nodo, que pode ser qualquer objeto (dentre os suportados).
private String getXSIType(Node nodo){ Node xsiType = nodo.getAttributes().getNamedItem("xsi:type"); if (xsiType == null) return null; return xsiType.getNodeValue(); } private Object getValueOf(Node nodo){ String xsiType = getXSIType(nodo); String primalValue = getValue(nodo); primalValue = primalValue==null?"":primalValue; if (xsiType == null) return null; if (xsiType.contains("string")) return primalValue; if (xsiType.contains("long")) return new Long(primalValue); if (xsiType.contains("int")) return new Integer(primalValue).intValue(); if (xsiType.contains("arrayList")) return getArrayListFromNode(nodo); if (xsiType.contains("hashMap")) return getHashMapFromNode(nodo); return primalValue; } private String getValue(Node nodo){ Node valor = nodo.getFirstChild(); if (valor==null) return null; return valor.getNodeValue(); }
Os tipos básicos estão de bom entendimento. Vamos verificar o que os métodos getHashMapFromNode e getArrayListFromNode fazem para retornar um HashMap ou uma Lista respectivamente.
Obtendo uma Lista:
private List getArrayListFromNode(Node nodo){ List lista = new ArrayList(); NodeList nodos = nodo.getChildNodes(); for (int j=0; j< nodos.getLength(); j++) lista.add(getValueOf(nodos.item(j))); return lista;
}
Obtendo um HashMap:
private HashMap getHashMapFromNode(Node nodo){ HashMap hashMap = new HashMap(); NodeList nodos = nodo.getChildNodes(); for (int k=0; k< nodos.getLength(); k++){ Node entry = nodos.item(k); Node key = entry.getFirstChild(); Node value = key.getNextSibling(); hashMap.put(key.getFirstChild().getNodeValue(),getValueOf(value)); } return hashMap; }
Já temos tudo pronto. Rápido e rasteiro não?
E como se usa isso???
Vamos chamar um método que retorna uma lista de hashmaps e exibir seu retorno.
Primeiro criamos o método de acesso:
protected List<HashMap> getListContentInfo(){
Node returnNode = soap.enviarEReceber(
"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">\n" +
" <soap:Body xmlns:ns1=\"http://tibox.deskpop.ws/\">\n" +
" <ns1:getListContentInfo>\n" +
" <ns1:cdCliente>1</ns1:cdCliente>\n" +
" <ns1:cdUsuario>6</ns1:cdUsuario>\n" +
" <ns1:listaConteudos xmlns:ns2=\"http://www.oracle.com/webservices/internal/literal\">\n" +
" <ns2:item></ns2:item>\n" +
" </ns1:listaConteudos>\n" +
" </ns1:getListContentInfo>\n" +
" </soap:Body>\n" +
"</soap:Envelope>\n");
return (List<HashMap>)soap.getObjeto(returnNode);
}
E então, exibimos seu retorno.
System.out.println(getListContentInfo());
Bom, é isso aí. Agora cabe a você melhorar o código e criar a “deserialização” do objeto num envelope SOAP válido.
Abraço!
* javax.xml.soap.SOAPException: Message send failed: HTTPClient.ParseException: length of IP-address (2) != length of netmask (1) at oracle.j2ee.ws.saaj.client.p2p.HttpSOAPConnection.call(HttpSOAPConnection.java:152)