it.cnr.icar.eric.server.common.Utility.java Source code

Java tutorial

Introduction

Here is the source code for it.cnr.icar.eric.server.common.Utility.java

Source

/*
 * ====================================================================
 * 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.server.common;

import it.cnr.icar.eric.common.BindingUtility;
import it.cnr.icar.eric.common.exceptions.DuplicateIdException;
import it.cnr.icar.eric.server.util.ServerResourceBundle;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import javax.xml.bind.JAXBException;
import javax.xml.registry.JAXRException;
import javax.xml.registry.RegistryException;
import javax.xml.soap.SOAPMessage;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.oasis.ebxml.registry.bindings.rim.ExternalLinkType;
import org.oasis.ebxml.registry.bindings.rim.RegistryObjectType;
import org.oasis.ebxml.registry.bindings.rim.ServiceBindingType;
import org.oasis.ebxml.registry.bindings.rs.RegistryError;
import org.oasis.ebxml.registry.bindings.rs.RegistryResponseType;
import org.w3c.dom.DOMException;
import org.w3c.dom.Node;

/**
 * Class Declaration.
 * @see
 * @author Farrukh S. Najmi
 * @author Adrian Chong
 * @version   1.2, 05/02/00
 */
public class Utility {
    /**
     * @link
     * @shapeType PatternLink
     * @pattern Singleton
     * @supplierRole Singleton factory
     */

    /* # private Utility _utility; */
    private static Utility instance = null;
    BindingUtility bu = BindingUtility.getInstance();
    private static final Log log = LogFactory.getLog(Utility.class);
    private it.cnr.icar.eric.common.UUIDFactory uf = it.cnr.icar.eric.common.UUIDFactory.getInstance();
    @SuppressWarnings("unused")
    private String ebxmlrrHome = null;

    /**
     * Class Constructor.
     *
     *
     * @see
     */
    protected Utility() {
    }

    /**
     *     Get the <?xml version="1.0"?> decalaration from XML bytes
     */
    public String getXMLDeclaration(byte[] bytes) {
        String str = new String(bytes);

        return removeXMLDeclaration(new StringBuffer(str));
    }

    /**
     * Removes the <?xml version="1.0"?> decalaration from XML bytes
     *    @return the bytes of the XML document
     */
    public byte[] removeXMLDeclaration(byte[] bytes) throws IOException {
        java.io.ByteArrayOutputStream bos = new java.io.ByteArrayOutputStream();
        int declEndIndex = 0;

        // Get the end index of declaration
        if (bytes[0] == '<') {
            boolean hasDecl = false;

            for (int i = 1; i < bytes.length; i++) {
                if (bytes[1] == ' ') {
                    //Skip spaces after '<'
                    declEndIndex++;
                } else if (bytes[i] == '?') {
                    // It has declaration
                    hasDecl = true;
                }

                if (hasDecl) {
                    if (bytes[i] != '>') {
                        declEndIndex++;
                    } else {
                        declEndIndex++;

                        break;
                    }
                }
            }
        }

        // skip all spaces after the xml declaration
        for (int i = declEndIndex; i < bytes.length; i++) {
            declEndIndex++;

            if (bytes[i] != ' ') {
                break;
            }
        }

        // System.err.println(declEndIndex + "!!!!!");
        bos.write(bytes, declEndIndex, bytes.length - declEndIndex);
        bos.flush();
        bos.close();

        //System.err.println(new String(bos.toByteArray()));
        return bos.toByteArray();
    }

    /**
     * Removes the <?xml version="1.0"?> decalaration from XML string
     *      @return the XML declaration
     */
    public String removeXMLDeclaration(StringBuffer str) {
        String decl = "";
        int len = str.length();
        int startIndex = -1; //start of what we are removing
        int endIndex = -1; //end of what we are removing

        for (int i = 0; i < len; i++) {
            if (str.charAt(i) == '<') {
                decl += "<";
                startIndex = i;

                //Skip spaces after '<'
                i++;

                while (str.charAt(i) == ' ') {
                    decl += " ";
                    i++;
                }

                if (str.charAt(i) == '?') {
                    //We have an XML declaration to remove
                    //Skip forward and find matching '>'
                    while (str.charAt(i) != '>') {
                        decl += str.charAt(i);
                        i++;
                    }

                    decl += ">";
                    endIndex = i;

                    break;
                } else {
                    //Not an XML declaration
                    startIndex = -1;

                    break;
                }
            }
        }

        if (startIndex != -1) {
            str.delete(startIndex, endIndex + 1);
        }

        return decl;
    }

    /**
     * Mimics javamail boundary id generator
     */
    public static String getMimeBoundary() {
        javax.mail.Session session = null; //Session.getDefaultInstance(new Properties());

        String s = null;
        javax.mail.internet.InternetAddress internetaddress = javax.mail.internet.InternetAddress
                .getLocalAddress(session);

        if (internetaddress != null) {
            s = internetaddress.getAddress();
        } else {
            s = "javamailuser@localhost";
        }

        StringBuffer stringbuffer = new StringBuffer();
        stringbuffer.append(stringbuffer.hashCode()).append('.').append(System.currentTimeMillis()).append('.')
                .append("eric.").append(s);

        return stringbuffer.toString();
    }

    /**
     *     Get bytes array from InputStream
     */
    public byte[] getBytesFromInputStream(InputStream is) throws IOException {
        java.io.ByteArrayOutputStream bos = new java.io.ByteArrayOutputStream();

        while (true) {
            byte[] buffer = new byte[100];
            int noOfBytes = is.read(buffer);

            if (noOfBytes == -1) {
                break;
            } else {
                bos.write(buffer, 0, noOfBytes);
            }
        }

        bos.flush();
        bos.close();

        return bos.toByteArray();
    }

    /**
     *     Create a SOAPMessage containing a registry request (e.g. SubmitObjectsRequest)
     *     @param req the InputStream to the registry request
     *     @return the InputStream to the the created SOAPMessage
     */
    public InputStream createSOAPStreamFromRequestStream(InputStream req)
            throws javax.xml.soap.SOAPException, IOException, javax.mail.internet.ParseException {
        String soapNS = "http://schemas.xmlsoap.org/soap/envelope/";
        String soapEnv = "soap-env";
        String soapEnvPrefix = soapEnv + ":";

        byte[] requestBytesWithDecl = getBytesFromInputStream(req);
        String decl = getXMLDeclaration(requestBytesWithDecl);
        byte[] requestBytes = removeXMLDeclaration(requestBytesWithDecl);
        requestBytesWithDecl = null;

        // System.err.println(decl);
        String partBeforeRequestStr = decl + "\n<" + soapEnvPrefix + "Envelope" + "\n\txmlns:soap-env=\"" + soapNS
                + "\""
                + "\n\txmlns:SOAP-SEC=\"http://schemas.xmlsoap.org/soap/security/2000-12\" soap-env:actor=\"some-uri\""
                + "\n\tsoap-env:mustUnderstand=\"1\">" +
                //The SOAP Header
                "\n\t<" + soapEnvPrefix + "Header>" +
                // tell server about our superior SOAP Fault capabilities
                "\n\t\t<" + BindingUtility.SOAP_CAPABILITY_HEADER_LocalName + " xmlns='"
                + BindingUtility.SOAP_CAPABILITY_HEADER_Namespace + "'>"
                + BindingUtility.SOAP_CAPABILITY_ModernFaultCodes + "</"
                + BindingUtility.SOAP_CAPABILITY_HEADER_LocalName + ">" +
                //DSIG goes here
                "\n\t</" + soapEnvPrefix + "Header>" +
                //The SOAP Body
                "\n\t<" + soapEnvPrefix + "Body SOAP-SEC:id=\"Body\">" + "\n";

        //The ebXML Registry request goes here
        String partAfterRequestStr = "\n\t</" + soapEnvPrefix + "Body>" + "\n</" + soapEnvPrefix + "Envelope>";

        // We concatenate the three bytes array to a single big array
        byte[] partBeforeRequestBytes = partBeforeRequestStr.getBytes("ISO-8859-1"); // The soap env does not have non ascii chars
        byte[] partAfterRequestBytes = partAfterRequestStr.getBytes("ISO-8859-1"); // The soap env does not have non ascii chars
        byte[] soapBytes = new byte[partBeforeRequestBytes.length + requestBytes.length
                + partAfterRequestBytes.length];

        int soapBytesIndex = 0;

        for (int a = 0; a < partBeforeRequestBytes.length; a++) {
            soapBytes[soapBytesIndex] = partBeforeRequestBytes[a];
            soapBytesIndex++;
        }

        for (int b = 0; b < requestBytes.length; b++) {
            soapBytes[soapBytesIndex] = requestBytes[b];
            soapBytesIndex++;
        }

        for (int c = 0; c < partAfterRequestBytes.length; c++) {
            soapBytes[soapBytesIndex] = partAfterRequestBytes[c];
            soapBytesIndex++;
        }

        // System.err.println("L343:" + new String(soapBytes));
        return new java.io.ByteArrayInputStream(soapBytes);
    }

    /**
     *     Create a SOAPMessage containing a registry request (e.g. SubmitObjectsRequest)
     *     @param req the InputStream to the registry request
     *     @return the created SOAPMessage
     */
    public SOAPMessage createSOAPMessageFromRequestStream(InputStream reqStream)
            throws javax.xml.soap.SOAPException, IOException, javax.mail.internet.ParseException {
        InputStream is = createSOAPStreamFromRequestStream(reqStream);

        javax.xml.soap.MimeHeaders mimeHeaders = new javax.xml.soap.MimeHeaders();

        javax.mail.internet.ContentType contentType = new javax.mail.internet.ContentType("text/xml"); //"multipart/related");

        String contentTypeStr = contentType.toString();

        //System.err.println("contentTypeStr = '" + contentTypeStr + "'");
        mimeHeaders.addHeader("Content-Type", contentTypeStr);
        mimeHeaders.addHeader("Content-Id", "ebXML Registry SOAP request");

        javax.xml.soap.MessageFactory factory = javax.xml.soap.MessageFactory.newInstance();
        SOAPMessage msg = factory.createMessage(mimeHeaders, is);

        msg.saveChanges();

        return msg;
    }

    /**
     *     Create a SOAPMessage object from a InputStream to a SOAPMessage
     *     @param soapStream the InputStream to the SOAPMessage
     *     @return the created SOAPMessage
     */
    public SOAPMessage createSOAPMessageFromSOAPStream(InputStream soapStream)
            throws javax.xml.soap.SOAPException, IOException, javax.mail.internet.ParseException {

        javax.xml.soap.MimeHeaders mimeHeaders = new javax.xml.soap.MimeHeaders();

        javax.mail.internet.ContentType contentType = new javax.mail.internet.ContentType("text/xml"); //"multipart/related");

        String contentTypeStr = contentType.toString();

        //System.err.println("contentTypeStr = '" + contentTypeStr + "'");
        mimeHeaders.addHeader("Content-Type", contentTypeStr);
        mimeHeaders.addHeader("Content-Id", "ebXML Registry SOAP request");

        javax.xml.soap.MessageFactory factory = javax.xml.soap.MessageFactory.newInstance();
        SOAPMessage msg = factory.createMessage(mimeHeaders, soapStream);

        // this saveChanges() call breaks SaaJ attachment branch with JDK6 code switch!!!
        // resulting in broken multipart/related + length calculated wrong 
        //        msg.saveChanges();

        return msg;
    }

    public RegistryError createRegistryErrorFromThrowable(Throwable t, String codeContext, String errCode)
            throws JAXBException {
        RegistryError re = bu.rsFac.createRegistryError();

        String stackTrace = getStackTraceFromThrowable(t);

        //System.err.println(stackTrace);
        String nestedStackTrace = null;

        if (t instanceof JAXRException) {
            Throwable nestedException = ((JAXRException) t).getCause();
            nestedStackTrace = getStackTraceFromThrowable(nestedException);
        }

        stackTrace += ("\n\nNested exception was:\n" + nestedStackTrace);

        re.setValue(stackTrace);
        re.setCodeContext(codeContext);
        re.setErrorCode(errCode);

        return re;
    }

    public RegistryResponseType createRegistryResponseFromThrowable(Throwable t, String codeContext,
            String errCode) {
        RegistryResponseType resp = null;

        resp = bu.rsFac.createRegistryResponseType();
        updateRegistryResponseFromThrowable(resp, t, codeContext, errCode);

        return resp;
    }

    public void updateRegistryResponseFromThrowable(org.oasis.ebxml.registry.bindings.rs.RegistryResponseType resp,
            Throwable t, String codeContext, String errCode) {
        try {
            resp.setStatus(BindingUtility.CANONICAL_RESPONSE_STATUS_TYPE_ID_Failure);

            org.oasis.ebxml.registry.bindings.rs.RegistryErrorList el = bu.rsFac.createRegistryErrorList();
            el.getRegistryError().add(createRegistryErrorFromThrowable(t, codeContext, errCode));
            resp.setRegistryErrorList(el);
        } catch (JAXBException e) {
            log.error(e);
        }
    }

    public String getStackTraceFromThrowable(Throwable t) {
        String trace = null;

        if (t != null) {
            java.io.StringWriter sw = new java.io.StringWriter();
            java.io.PrintWriter pw = new java.io.PrintWriter(sw);
            t.printStackTrace(pw);
            trace = sw.toString();
        }

        return trace;
    }

    public String createTimestamp() {
        // Get the time zone offset in +HH:mm format
        java.util.TimeZone tz = java.util.TimeZone.getDefault();

        float rawOffset = tz.getRawOffset() / 1000 / 60 / 60.0f;
        int offsetHr = (int) Math.abs(rawOffset);
        int offsetMin = (int) (rawOffset - offsetHr) * 60;

        String offset = (rawOffset > 0) ? "+" : "-";
        offset += ((offsetHr < 10) ? "0" : "");
        offset += offsetHr;
        offset += ":";
        offset += ((offsetMin < 10) ? "0" : "");
        offset += offsetMin;

        java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");

        return sdf.format(new java.util.Date()) + offset;
    }

    /**
     *         Return ArrayList of ExternalLink that points to unresolvable Http URLs. Any
     *         non-Http URLs will not be checked. Any non-Http URLs and other types
     *         of URIs will not be checked. If the http response code is smaller than 200
     *         or bigger than 299, the http URL is considered invalid.
     */
    public ArrayList<Object> validateURIs(ArrayList<?> sourceRegistryObjects) throws RegistryException {
        ArrayList<Object> invalidURLROs = new ArrayList<Object>();
        Iterator<?> iter = sourceRegistryObjects.iterator();

        while (iter.hasNext()) {
            Object ro = iter.next();
            String uRI = null;

            if (ro instanceof ExternalLinkType) {
                uRI = ((ExternalLinkType) ro).getExternalURI();
            } else if (ro instanceof ServiceBindingType) {
                uRI = ((ServiceBindingType) ro).getAccessURI();
            } else {
                throw new RegistryException(
                        ServerResourceBundle.getInstance().getString("message.unknownRegistryObjectType"));
            }

            if (!it.cnr.icar.eric.common.Utility.getInstance().isValidURI(uRI)) {
                invalidURLROs.add(ro);
            }
        }

        return invalidURLROs;
    }

    //Based on code contributed by Bobby Bissett
    public org.w3c.dom.Element getSignatureElement(SOAPMessage msg)
            throws javax.xml.soap.SOAPException, javax.xml.transform.TransformerException {
        org.w3c.dom.Element sigElement = null;

        javax.xml.transform.Transformer xFormer = javax.xml.transform.TransformerFactory.newInstance()
                .newTransformer();

        // grab info out of msg
        javax.xml.soap.SOAPPart msgPart = msg.getSOAPPart();
        javax.xml.transform.Source msgSource = msgPart.getContent();

        // transform
        javax.xml.transform.dom.DOMResult domResult = new javax.xml.transform.dom.DOMResult();
        xFormer.transform(msgSource, domResult);

        //root node is the soap:Envelope
        Node envelopeNode = domResult.getNode();

        //now you have the node. the following code strips off the envelope of
        //the soap message to get to the actual content
        // Advance to envelope node in case of text nodes preceding it
        while ((envelopeNode.getLocalName() == null)
                || (!envelopeNode.getLocalName().equalsIgnoreCase("envelope"))) {
            envelopeNode = envelopeNode.getFirstChild();
        }

        // Advance to header within envelope node
        Node headerNode = envelopeNode.getFirstChild();

        while ((headerNode.getLocalName() == null) || (!headerNode.getLocalName().equalsIgnoreCase("header"))) {
            headerNode = headerNode.getNextSibling();
        }

        //System.err.println("headerNode name is: " + headerNode.getLocalName());
        // Advance to signature node within header
        Node sigNode = headerNode.getFirstChild();

        if (sigNode == null) {
            return null;
        }

        //System.err.println("sigNode: " + sigNode);
        while ((sigNode.getLocalName() == null) || (!sigNode.getLocalName().equalsIgnoreCase("signature"))) {
            sigNode = sigNode.getNextSibling();

            if (sigNode == null) {
                return null;
            }
        }

        //Desired Signature element may be inside a SOAP-SEC signature element
        if (!sigNode.getNamespaceURI().equals("http://www.w3.org/2000/09/xmldsig#")) {
            sigNode = sigNode.getFirstChild();

            while ((sigNode.getLocalName() == null) || (!sigNode.getLocalName().equalsIgnoreCase("signature"))) {
                sigNode = sigNode.getNextSibling();
            }
        }

        if (sigNode.getNamespaceURI().equals("http://www.w3.org/2000/09/xmldsig#")) {
            if (sigNode instanceof org.w3c.dom.Element) {
                sigElement = (org.w3c.dom.Element) sigNode;
            }
        }

        return sigElement;
    }

    /**
     *     Convert the apostrophes in a String field in a SQL statement to 2
     *     consecutive apostrohes
     */
    public static String escapeSQLChars(String stringField) {
        StringBuffer result = new StringBuffer();

        for (int i = 0; i < stringField.length(); i++) {
            char c = stringField.charAt(i);
            result.append(c);

            if (c == '\'') {
                result.append(c);
            }
        }

        return result.toString();
    }

    /**
     * Method Declaration.
     *
     *
     * @return
     *
     * @see
     */
    public synchronized static Utility getInstance() {
        if (instance == null) {
            instance = new Utility();
        }

        return instance;
    }

    /*********************************************************/

    /*  Methods refactored from LifeCycleManagerImpl - START */

    /*********************************************************/
    /**
     * Check if id is a proper UUID. If not make a proper UUID based
     * URN and add a mapping in idMap between old and new Id. The
     * parent attribute of ClassificationNode will be also fixed here
     * according to their hierarchy if the parent attributes are not
     * provided explicitly by the clients.
     *
     * @param ids The List holding all the UUIDs in the SubmitObjectsRequest
     * document
     * @throws UUIDNotUniqueException if any UUID is not unique within a
     * SubmitObjectsRequest
     */
    @SuppressWarnings("unused")
    private void checkId(List<String> ids, HashMap<String, String> idMap, RegistryObjectType ro)
            throws RegistryException {
        try {
            String id = ro.getId();

            // Check for uniqueness
            if ((ids != null) && ids.contains(id)) {
                throw new DuplicateIdException(id);
            }

            if (id != null) {
                ids.add(id);
            }

            if ((id == null) || !id.startsWith("urn:uuid:")) {
                // Generate UUID if the request does not provide ID for a RO
                // or it does not start with urn:uuid:
                it.cnr.icar.eric.common.UUID uuid = uf.newUUID();
                String newId = "urn:uuid:" + uuid;
                ro.setId(newId);
                idMap.put(id, newId);
            } else {
                // id starts with "urn:uuid:"
                String uuidStr = id.substring(9);

                if (!uf.isValidUUID(uuidStr)) {
                    // but invalid
                    it.cnr.icar.eric.common.UUID uuid = uf.newUUID();
                    String newId = "urn:uuid:" + uuid;
                    idMap.put(id, newId);
                }
            }

            //Now checkId for all composed objects
            Set<RegistryObjectType> composedObjects = BindingUtility.getInstance().getComposedRegistryObjects(ro,
                    -1);
            Iterator<RegistryObjectType> iter = composedObjects.iterator();

            while (iter.hasNext()) {
                RegistryObjectType obj = iter.next();
                checkId(ids, idMap, ro);
            }
        } catch (JAXRException e) {
            throw new RegistryException(e);
        }
    }

    /**
     * This is an implementation of the JAXP DOM Node.setTextContent
     * method, which does not exist in JDK 1.4. This code was adapted
     * from the Apache Xerces 2.8.0 implementation of this method.
     */
    public static void setTextContent(Node node, String textContent) throws DOMException {
        Node child;

        // get rid of any existing children
        while ((child = node.getFirstChild()) != null) {
            node.removeChild(child);
        }

        // create a Text node to hold the given content
        if (textContent != null && textContent.length() != 0) {
            node.appendChild(node.getOwnerDocument().createTextNode(textContent));
        }
    }
}