Java tutorial
/* * ==================================================================== * This file is part of the ebXML Registry by Icar Cnr v3.2 * ("eRICv32" in the following disclaimer). * * "eRICv32" is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * "eRICv32" is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License Version 3 * along with "eRICv32". If not, see <http://www.gnu.org/licenses/>. * * eRICv32 is a forked, derivative work, based on: * - freebXML Registry, a royalty-free, open source implementation of the ebXML Registry standard, * which was published under the "freebxml License, Version 1.1"; * - ebXML OMAR v3.2 Edition, published under the GNU GPL v3 by S. Krushe & P. Arwanitis. * * All derivative software changes and additions are made under * * Copyright (C) 2013 Ing. Antonio Messina <messina@pa.icar.cnr.it> * * This software consists of voluntary contributions made by many * individuals on behalf of the freebxml Software Foundation. For more * information on the freebxml Software Foundation, please see * "http://www.freebxml.org/". * * This product includes software developed by the Apache Software * Foundation (http://www.apache.org/). * * ==================================================================== */ package it.cnr.icar.eric.common; import it.cnr.icar.eric.common.security.wss4j.WSS4JSecurityUtilSAML; import java.io.ByteArrayInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; import java.io.Reader; import java.io.StringReader; import java.io.StringWriter; import java.io.UnsupportedEncodingException; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Properties; import javax.activation.DataHandler; import javax.mail.MessagingException; import javax.xml.bind.JAXBElement; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.registry.JAXRException; import javax.xml.registry.RegistryException; import javax.xml.soap.AttachmentPart; import javax.xml.soap.MessageFactory; import javax.xml.soap.MimeHeaders; import javax.xml.soap.SOAPBody; import javax.xml.soap.SOAPConnection; import javax.xml.soap.SOAPConnectionFactory; import javax.xml.soap.SOAPEnvelope; import javax.xml.soap.SOAPException; import javax.xml.soap.SOAPFault; import javax.xml.soap.SOAPHeader; import javax.xml.soap.SOAPHeaderElement; import javax.xml.soap.SOAPMessage; import javax.xml.soap.SOAPPart; import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMResult; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.oasis.ebxml.registry.bindings.rs.RegistryResponseType; import org.xml.sax.InputSource; import org.xml.sax.SAXException; /** * Class is responsible for communicating w/ registry using SOAP. * */ public class SOAPMessenger { protected static CommonResourceBundle resourceBundle = CommonResourceBundle.getInstance(); private String endpoint; private CredentialInfo credentialInfo = null; @SuppressWarnings("unused") private BindingUtility bindingUtil = BindingUtility.getInstance(); private Log log = LogFactory.getLog(SOAPMessenger.class); public SOAPMessenger(String registryUrl, CredentialInfo credentialInfo) { endpoint = registryUrl; this.credentialInfo = credentialInfo; } void dumpMessage(String info, SOAPMessage msg) throws SOAPException { if (log.isTraceEnabled()) { if (info != null) { System.err.print(info); } try { msg.writeTo(System.err); System.err.println(); } catch (IOException x) { return; } } } /** * Send a SOAP request to the registry server. Main entry point for * this class. * * @param requestString String that will be placed in the body of the * SOAP message to be sent to the server * * @return RegistryResponseHolder that represents the response from the * server */ public RegistryResponseHolder sendSoapRequest(String requestString) throws JAXRException { return sendSoapRequest(requestString, null); } /** Send a SOAP request to the registry server. Main entry point for * this class. If credentials have been set on the registry connection, * they will be used to sign the request. * * @param requestString * String that will be placed in the body of the * SOAP message to be sent to the server * * @param attachments * HashMap consisting of entries each of which * corresponds to an attachment where the entry key is the ContentId * and the entry value is a javax.activation.DataHandler of the * attachment. A parameter value of null means no attachments. * * @return * RegistryResponseHolder that represents the response from the * server */ @SuppressWarnings("unchecked") public RegistryResponseHolder sendSoapRequest(String requestString, Map<?, ?> attachments) throws JAXRException { boolean logRequests = Boolean.valueOf( CommonProperties.getInstance().getProperty("eric.common.soapMessenger.logRequests", "false")) .booleanValue(); if (logRequests) { PrintStream requestLogPS = null; try { requestLogPS = new PrintStream( new FileOutputStream(java.io.File.createTempFile("SOAPMessenger_requestLog", ".xml"))); requestLogPS.println(requestString); } catch (IOException e) { e.printStackTrace(); } finally { if (requestLogPS != null) { requestLogPS.close(); } } } // ================================================================= // // Remove the XML Declaration, if any // if (requestString.startsWith("<?xml")) { // requestString = requestString.substring(requestString.indexOf("?>")+2).trim(); // } // // StringBuffer soapText = new StringBuffer( // "<soap-env:Envelope xmlns:soap-env=\"http://schemas.xmlsoap.org/soap/envelope/\">"); // // soapText.append("<soap-env:Header>\n"); // // tell server about our superior SOAP Fault capabilities // soapText.append("<"); // soapText.append(BindingUtility.SOAP_CAPABILITY_HEADER_LocalName); // soapText.append(" xmlns='"); // soapText.append(BindingUtility.SOAP_CAPABILITY_HEADER_Namespace); // soapText.append("'>"); // soapText.append(BindingUtility.SOAP_CAPABILITY_ModernFaultCodes); // soapText.append("</"); // soapText.append(BindingUtility.SOAP_CAPABILITY_HEADER_LocalName); // soapText.append(">\n"); // soapText.append("</soap-env:Header>\n"); // soapText.append("<soap-env:Body>\n"); // soapText.append(requestString); // soapText.append("</soap-env:Body>"); // soapText.append("</soap-env:Envelope>"); MessageFactory messageFactory; SOAPMessage message = null; SOAPPart sp = null; SOAPEnvelope se = null; SOAPBody sb = null; SOAPHeader sh = null; if (log.isTraceEnabled()) { log.trace("requestString=\"" + requestString + "\""); } try { messageFactory = MessageFactory.newInstance(); message = messageFactory.createMessage(); sp = message.getSOAPPart(); se = sp.getEnvelope(); sb = se.getBody(); sh = se.getHeader(); /* * <soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/"> * <soap-env:Header> * <capabilities xmlns="urn:freebxml:registry:soap">urn:freebxml:registry:soap:modernFaultCodes</capabilities> * * change with explicit namespace * <ns1:capabilities xmlns:ns1="urn:freebxml:registry:soap">urn:freebxml:registry:soap:modernFaultCodes</ns1:capabilities> */ SOAPHeaderElement capabilityElement = sh .addHeaderElement(se.createName(BindingUtility.SOAP_CAPABILITY_HEADER_LocalName, "ns1", BindingUtility.SOAP_CAPABILITY_HEADER_Namespace)); // capabilityElement.addAttribute( // se.createName("xmlns"), // BindingUtility.SOAP_CAPABILITY_HEADER_Namespace); capabilityElement.setTextContent(BindingUtility.SOAP_CAPABILITY_ModernFaultCodes); /* * body */ // Remove the XML Declaration, if any if (requestString.startsWith("<?xml")) { requestString = requestString.substring(requestString.indexOf("?>") + 2).trim(); } // Generate DOM Document from request xml string DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); builderFactory.setNamespaceAware(true); InputStream stream = new ByteArrayInputStream(requestString.getBytes("UTF-8")); // Inject request into body sb.addDocument(builderFactory.newDocumentBuilder().parse(stream)); // Is it never the case that there attachments but no credentials if ((attachments != null) && !attachments.isEmpty()) { addAttachments(message, attachments); } if (credentialInfo == null) { throw new JAXRException(resourceBundle.getString("message.credentialInfo")); } WSS4JSecurityUtilSAML.signSOAPEnvelopeOnClientSAML(se, credentialInfo); SOAPMessage response = send(message); // Check to see if the session has expired // by checking for an error response code // TODO: what error code to we look for? if (isSessionExpired(response)) { credentialInfo.sessionId = null; // sign the SOAPMessage this time // TODO: session - add method to do the signing // signSOAPMessage(msg); // send signed message // SOAPMessage response = send(msg); } // Process the main SOAPPart of the response //check for soapfault and throw RegistryException SOAPFault fault = response.getSOAPBody().getFault(); if (fault != null) { throw createRegistryException(fault); } Reader reader = processResponseBody(response, "Response"); RegistryResponseType ebResponse = null; try { Object obj = BindingUtility.getInstance().getJAXBContext().createUnmarshaller() .unmarshal(new InputSource(reader)); if (obj instanceof JAXBElement<?>) // if Element: take ComplexType from Element obj = ((JAXBElement<RegistryResponseType>) obj).getValue(); ebResponse = (RegistryResponseType) obj; } catch (Exception x) { log.error(CommonResourceBundle.getInstance().getString("message.FailedToUnmarshalServerResponse"), x); throw new JAXRException(resourceBundle.getString("message.invalidServerResponse")); } // Process the attachments of the response if any HashMap<String, Object> responseAttachments = processResponseAttachments(response); return new RegistryResponseHolder(ebResponse, responseAttachments); } catch (SAXException e) { throw new JAXRException(e); } catch (ParserConfigurationException e) { throw new JAXRException(e); } catch (UnsupportedEncodingException x) { throw new JAXRException(x); } catch (MessagingException x) { throw new JAXRException(x); } catch (FileNotFoundException x) { throw new JAXRException(x); } catch (IOException e) { throw new JAXRException(e); } catch (SOAPException x) { x.printStackTrace(); throw new JAXRException(resourceBundle.getString("message.cannotConnect"), x); } catch (TransformerConfigurationException x) { throw new JAXRException(x); } catch (TransformerException x) { throw new JAXRException(x); } } private void addAttachments(SOAPMessage msg, Map<?, ?> attachments) throws MessagingException, FileNotFoundException, RegistryException { for (Iterator<?> it = attachments.entrySet().iterator(); it.hasNext();) { @SuppressWarnings("rawtypes") Map.Entry entry = (Map.Entry) it.next(); String id = (String) entry.getKey(); DataHandler dh = (DataHandler) entry.getValue(); addAttachment(msg, id, dh); } } private void addAttachment(SOAPMessage msg, String id, DataHandler dh) throws FileNotFoundException, MessagingException, RegistryException { String cid = WSS4JSecurityUtilSAML.convertUUIDToContentId(id); AttachmentPart ap = msg.createAttachmentPart(dh); ap.setContentId(cid); msg.addAttachmentPart(ap); if (log.isTraceEnabled()) { log.trace("adding attachment: contentId=" + cid); } } /** * Convert SOAPFault back to RegistryException (if possible) * @param fault SOAPFault * @return RegistryException */ RegistryException createRegistryException(SOAPFault fault) { RegistryException result = null; //is this message too generic? String unknownError = resourceBundle.getString("message.unknown"); if (log.isDebugEnabled()) { log.debug(fault.toString()); } String exceptionName = null; if (fault.getFaultCode().startsWith(BindingUtility.SOAP_FAULT_PREFIX)) { // Old style faultcode value, skip prefix and colon exceptionName = fault.getFaultCode().substring(BindingUtility.SOAP_FAULT_PREFIX.length() + 1); } else if ( // TODO: SAAJ 1.3 has introduced preferred QName interfaces fault.getFaultCodeAsName().getURI().equals(BindingUtility.SOAP_FAULT_PREFIX)) { // New style exceptionName = fault.getFaultCodeAsName().getLocalName(); } if (null == exceptionName) { // not a recognized ebXML fault result = new RegistryException(unknownError); } else { // ebXML fault String exceptionMessage = fault.getFaultString(); unknownError = resourceBundle.getString("message.exception", new String[] { exceptionName, exceptionMessage }); /* Detail detail = fault.getDetail(); Iterator iter = detail.getDetailEntries(); int i=0; while (iter.hasNext()) { DetailEntry detailEntry = (DetailEntry)iter.next(); unknownError += " detailEntry[" + i++ + "] = " + detailEntry.toString(); } **/ //TODO: get and reconstruct Stacktrace try { Class<?> exceptionClass = null; //exceptionClass = Class.forName("it.cnr.icar.eric.common.exceptions." + exceptionName); exceptionClass = Class.forName(exceptionName); if (RegistryException.class.isAssignableFrom(exceptionClass)) { //Exception is a RegistryException. Reconstitute it as a RegistryException // NPE has null message.. if (exceptionMessage != null) { @SuppressWarnings("rawtypes") Class[] parameterDefinition = { String.class }; Constructor<?> exceptionConstructor = exceptionClass.getConstructor(parameterDefinition); Object[] parameters = { exceptionMessage }; result = (RegistryException) exceptionConstructor.newInstance(parameters); } else { @SuppressWarnings("rawtypes") Class[] parameterDefinition = {}; Constructor<?> exceptionConstructor = exceptionClass.getConstructor(parameterDefinition); Object[] parameters = {}; result = (RegistryException) exceptionConstructor.newInstance(parameters); } } else { //Exception is not a RegistryException. //Make it a RegistryException with exceptionMessage //In future make it a nested Throwable of a RegistryException // NPE has null message.. result = new RegistryException(unknownError); } } catch (ClassNotFoundException e) { //could happen with non-eric server? result = new RegistryException(unknownError, e); } catch (NoSuchMethodException e) { //should not happen result = new RegistryException(unknownError, e); } catch (IllegalAccessException e) { //happens when? result = new RegistryException(unknownError, e); } catch (InvocationTargetException e) { //happens when? result = new RegistryException(unknownError, e); } catch (InstantiationException e) { //happens when trying to instantiate Interface result = new RegistryException(unknownError, e); } } return result; } SOAPMessage send(SOAPMessage msg) throws SOAPException { SOAPConnectionFactory scf = SOAPConnectionFactory.newInstance(); SOAPConnection connection = scf.createConnection(); dumpMessage("Request=", msg); long t1 = System.currentTimeMillis(); // if the sessionId exists, set it as a Mime header // This header will be used by the server to track authenticated // sessions if (credentialInfo != null) { if (credentialInfo.sessionId != null) { msg.getMimeHeaders().addHeader("Cookie", credentialInfo.sessionId); } } SOAPMessage reply = connection.call(msg, endpoint); // if the credentialInfo.sessionId is not null, cache the sessionId if (credentialInfo != null) { cacheSessionId(reply); } long t2 = System.currentTimeMillis(); dumpMessage("Response=", reply); double secs = ((double) t2 - t1) / 1000; log.debug("Call elapsed time in seconds: " + secs); return reply; } // TODO: session - fill in this method private boolean isSessionExpired(SOAPMessage message) { boolean sessionExpired = false; return sessionExpired; } private void cacheSessionId(SOAPMessage message) { MimeHeaders mimeHeaders = message.getMimeHeaders(); String[] header = mimeHeaders.getHeader("Set-Cookie"); if (header != null) { for (int i = 0; i < header.length; i++) { if (header[i].startsWith("JSESSIONID")) { // parse JSESSIONID attribute String[] attributes = header[i].split(";"); // JSESSIONID will be first attribute credentialInfo.sessionId = attributes[0]; break; } } } } Reader processResponseBody(SOAPMessage response, String lookFor) throws JAXRException, SOAPException, TransformerConfigurationException, TransformerException { // grab info out of reply SOAPPart replyPart = response.getSOAPPart(); Source replySource = replyPart.getContent(); // transform TransformerFactory tFactory = TransformerFactory.newInstance(); Transformer xFormer = tFactory.newTransformer(); DOMResult domResult = new DOMResult(); xFormer.transform(replySource, domResult); org.w3c.dom.Node node = domResult.getNode(); while (node != null) { String nodeLocalName = node.getLocalName(); if ((nodeLocalName != null) && (nodeLocalName.endsWith(lookFor))) { break; } node = nextNode(node); } if (node == null) { node = domResult.getNode(); while (node != null) { String nodeLocalName = node.getLocalName(); if ((nodeLocalName != null) && (nodeLocalName.endsWith(lookFor))) { break; } node = nextNode(node); } throw new JAXRException(resourceBundle.getString("message.elementNotFound", new String[] { lookFor })); } return domNode2StringReader(node); } private static org.w3c.dom.Node nextNode(org.w3c.dom.Node node) { // assert(node != null); org.w3c.dom.Node child = node.getFirstChild(); if (child != null) { return child; } org.w3c.dom.Node sib; while ((sib = node.getNextSibling()) == null) { node = node.getParentNode(); if (node == null) { // End of document return null; } } return sib; } StringReader domNode2StringReader(org.w3c.dom.Node node) throws TransformerConfigurationException, TransformerException { TransformerFactory tfactory = TransformerFactory.newInstance(); StringWriter writer = null; Transformer serializer = tfactory.newTransformer(); Properties oprops = new Properties(); oprops.put("method", "xml"); oprops.put("indent", "yes"); serializer.setOutputProperties(oprops); writer = new StringWriter(); serializer.transform(new DOMSource(node), new StreamResult(writer)); String outString = writer.toString(); //log.trace("outString=" + outString); StringReader reader = new StringReader(outString); return reader; } /** * @return HashMap containing {contentId, DataHandler} entries or null * if no attachments */ private HashMap<String, Object> processResponseAttachments(SOAPMessage response) throws JAXRException, SOAPException, MessagingException { if (response.countAttachments() == 0) { return null; } HashMap<String, Object> attachMap = new HashMap<String, Object>(); for (Iterator<?> it = response.getAttachments(); it.hasNext();) { AttachmentPart ap = (AttachmentPart) it.next(); String uuid = WSS4JSecurityUtilSAML.convertContentIdToUUID(ap.getContentId()); if (log.isTraceEnabled()) { log.trace("Processing attachment w/ contentId=" + uuid); } DataHandler dh = ap.getDataHandler(); attachMap.put(uuid, dh); } return attachMap; } @SuppressWarnings("unused") private boolean isSessionEstablished() { return false; } @SuppressWarnings("unused") private void establishSession() { } }