FileDocCategorySizeDatePackage
WrappedMessage.javaAPI DocGlassfish v2 API20538Wed Jul 04 08:20:20 BST 2007com.sun.enterprise.jbi.serviceengine.comm

WrappedMessage.java

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 * 
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License. You can obtain
 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
 * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
 * Sun designates this particular file as subject to the "Classpath" exception
 * as provided by Sun in the GPL Version 2 section of the License file that
 * accompanied this code.  If applicable, add the following below the License
 * Header, with the fields enclosed by brackets [] replaced by your own
 * identifying information: "Portions Copyrighted [year]
 * [name of copyright owner]"
 * 
 * Contributor(s):
 * 
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

/*
 * WrappedMessage.java
 *
 * Created on November 20, 2006, 6:03 PM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package com.sun.enterprise.jbi.serviceengine.comm;

import com.sun.enterprise.jbi.serviceengine.core.JavaEEServiceEngineContext;
import com.sun.enterprise.jbi.serviceengine.util.DOMUtil;
import com.sun.enterprise.jbi.serviceengine.util.JBIConstants;
import com.sun.enterprise.jbi.serviceengine.util.soap.EndpointMetaData;
import com.sun.enterprise.jbi.serviceengine.util.soap.SOAPConstants;
import com.sun.logging.LogDomains;
import com.sun.xml.bind.api.Bridge;
import com.sun.xml.ws.api.message.AttachmentSet;
import com.sun.xml.ws.api.message.HeaderList;
import com.sun.xml.ws.api.message.Message;
import com.sun.xml.ws.message.DataHandlerAttachment;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.wsdl.Part;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamWriter;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamSource;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.ContentHandler;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;

/**
 *
 * @author bhavani
 */
public class WrappedMessage extends Message
        implements JBIConstants, SOAPConstants {
    
    private static Logger logger =
            LogDomains.getLogger(LogDomains.SERVER_LOGGER);
    
    private static DocumentBuilderFactory dbf;
    
    /**
     * Holds the contents of the wrapped message.
     */
    Source wrappedMessage;
    
    /**
     * Variables used for wrapping the message.
     */
    Message abstractMessage;
    QName wsdlMessageType;
    String wsdlBindingStyle;
    String wsdlMessageName;
    List<Part> wsdlOrderedParts;
    int[] wsdlPartBindings; // represents whether the part is bound to 'header', 'body', or 'attachment'.
    
    public void setAbstractMessage(Message abstractMessage) {
        this.abstractMessage = abstractMessage;
    }
    
    public void setWSDLMessageType(QName wsdlMessageType) {
        this.wsdlMessageType = wsdlMessageType;
    }
    
    public void setWSDLBindingStyle(String wsdlBindingStyle) {
        this.wsdlBindingStyle = wsdlBindingStyle;
    }
    
    public void setWSDLMessageName(String wsdlMessageName) {
        this.wsdlMessageName = wsdlMessageName;
    }
    
    public void setWSDLOrderedParts(List<Part> wsdlOrderedParts) {
        this.wsdlOrderedParts = wsdlOrderedParts;
    }
    
    public void setWSDLPartBindings(int[] wsdlPartBindings) {
        this.wsdlPartBindings = wsdlPartBindings;
    }
    
    private boolean log = false;
    
    private void setLog() {
        if(logger.isLoggable(Level.FINE)) {
            log = true;
        }
    }
    
    public WrappedMessage() {
        if(dbf == null) {
            dbf = DocumentBuilderFactory.newInstance();
            dbf.setNamespaceAware(true);
        }
        setLog();
    }
    
    public void wrap() throws Exception {
        
        if(JavaEEServiceEngineContext.getInstance().isServiceMix()) {
            if(log) {
                logger.log(Level.FINE, "Skipping the wrapping...");
            }
            wrappedMessage = abstractMessage.readPayloadAsSource();
        } else {
            if(RPC_STYLE.equalsIgnoreCase(wsdlBindingStyle)) {
                new RPCStyleWrapper().wrap();
            } else {
                new DocumentStyleWrapper().wrap();
            }
        }
    }
    
    private void writeJBIHeader(XMLStreamWriter writer) throws Exception {
        writer.writeStartDocument();
        writer.writeStartElement(WRAPPER_MESSAGE_QNAME);
        writeMessageAttributes(writer, wsdlMessageName);
    }
    
    private void writeJBIFooter(XMLStreamWriter writer) throws Exception {
        writer.writeEndElement();
        writer.writeEndDocument();
        writer.flush();
    }
    
    private void setMessage(byte[] data) throws Exception {
        ByteArrayInputStream bais = new ByteArrayInputStream(data);
        String usedWith = System.getProperty(USED_WITH);
        if(usedWith != null && usedWith.indexOf(USED_WITH_HTTP_SOAP_BC) != -1) {
            DocumentBuilder db = dbf.newDocumentBuilder();
            wrappedMessage = new DOMSource(db.parse(bais));
        } else {
            wrappedMessage = new StreamSource(bais);
        }
    }
    
    class DocumentStyleWrapper {
        void wrap() {
            try {
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                XMLStreamWriter writer = XOF.createXMLStreamWriter(baos);
                
                writeJBIHeader(writer);
                
                for(int i=0, headerIndex=0; i<wsdlPartBindings.length; i++) {
                    switch(wsdlPartBindings[i]) {
                        case SOAP_BODY_BINDING :
                            if(log) {
                                logger.log(Level.FINE, "Adding part number " + i + " from payLoad");
                            }
                            if(abstractMessage.hasPayload()) {
                                writer.writeStartElement(WRAPPER_PART_QNAME); // assuming only one part in the response.
                                writer.writeCharacters(""); // Force completion of open elems
                                writePayloadTo(writer);
                                writer.writeEndElement();
                            }
                            break;
                        case SOAP_HEADER_BINDING :
                            if(log) {
                                logger.log(Level.FINE, "Adding part number " + i + " from header");
                            }
                            writer.writeStartElement(WRAPPER_PART_QNAME); // assuming only one part in the response.
                            writer.writeCharacters(""); // Force completion of open elems
                            writer.flush();
                            abstractMessage.getHeaders().get(headerIndex++).writeTo(writer);
                            writer.writeEndElement();
                            break;
                        case SOAP_ATTACHMENT_BINDING :
                            if(log) {
                                logger.log(Level.FINE, "Part number " + i + " is an attachment");
                            }
                            break;
                    }
                }
                
                writeJBIFooter(writer);
                
                setMessage(baos.toByteArray());
                
                if(log) {
                    logger.log(Level.FINE, "\n\nWrapped message = " + baos.toString() + "\n\n");
                }
            } catch(Exception ex) {
                logger.log(Level.SEVERE, ex.getMessage(), ex);
            }
        }
        
    }
    
    class RPCStyleWrapper {
        
        void wrap() throws Exception {
            
            try {
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                XMLStreamWriter writer = XOF.createXMLStreamWriter(baos);
                
                writeJBIHeader(writer);
                
                XMLStreamReader payLoad = abstractMessage.readPayload();
                if(payLoad.getEventType() == XMLStreamReader.START_DOCUMENT) {
                    payLoad.next();
                }
                Map<String,String> payLoadNSAttrs = new HashMap<String,String>();
                copyNamespaceAttributes(payLoad, payLoadNSAttrs);
                payLoad.next(); // skip the operation node.
                
                writeJBIParts(payLoad, writer, payLoadNSAttrs);
                
                writeJBIFooter(writer);
                
                setMessage(baos.toByteArray());
                
                if(log) {
                    logger.log(Level.FINE, "\n\nWrapped message = " + baos.toString() + "\n\n");
                }
            } catch(Exception ex) {
                logger.log(Level.SEVERE, ex.getMessage(), ex);
            }
        }
        
        private void copyNamespaceAttributes(
                XMLStreamReader reader,
                Map<String,String> m) throws Exception {
            for(int i=0; i<reader.getNamespaceCount(); i++) {
                m.put(reader.getNamespacePrefix(i),
                        reader.getNamespaceURI(i));
            }
        }
        
        private void writeJBIParts(
                XMLStreamReader payLoad,
                XMLStreamWriter writer,
                Map<String,String> ancestorNamespaceAttrs) throws Exception {
            if(log) {
                logger.log(Level.FINE, "Number of parts = " + wsdlPartBindings.length);
            }
            for(int partIndex = 0, headerIndex = 0
                    ; partIndex < wsdlPartBindings.length && payLoad.hasNext()
                    ; partIndex++) {
                switch(wsdlPartBindings[partIndex]) {
                    case SOAP_HEADER_BINDING :
                        if(log) {
                            logger.log(Level.FINE, "Adding part number " + partIndex + " from header");
                        }
                        writer.writeStartElement(WRAPPER_PART_QNAME); // assuming only one part in the response.
                        writer.writeCharacters(""); // Force completion of open elems
                        writer.flush();
                        abstractMessage.getHeaders().get(headerIndex++).writeTo(writer);
                        writer.writeEndElement();
                        break;
                        
                    case SOAP_BODY_BINDING :
                        if(log) {
                            logger.log(Level.FINE, "Adding part number " + partIndex + " from payLoad");
                        }
                        writer.writeStartElement(WRAPPER_PART_QNAME);
                        DOMUtil.UTIL.writeChildren(payLoad, writer, ancestorNamespaceAttrs);
                        writer.writeEndElement();
                        //payLoad.next();
                        break;
                    case SOAP_ATTACHMENT_BINDING :
                        if(log) {
                            logger.log(Level.FINE, "Part number " + partIndex + " is an attachment");
                        }
                        break;
                }
            }
        }
        
        private boolean isSimpleType(Part part) {
            QName parttype = part.getTypeName();
            if (parttype != null) {
                String s = parttype.getNamespaceURI();
                if ( s != null && s.trim().equals("http://www.w3.org/2001/XMLSchema")) {
                    return true;
                }
            }
            return false;
        }
        
    }
    
    private void writeMessageAttributes(XMLStreamWriter sw,
            String wsdlMessageName) throws XMLStreamException {
        String prefix = wsdlMessageType.getPrefix();
        if (prefix == null || prefix.trim().length() == 0) {
            prefix = "msgns";
        }
        sw.writeAttribute(WRAPPER_ATTRIBUTE_VERSION, WRAPPER_ATTRIBUTE_VERSION_VALUE);
        if(wsdlMessageName != null) {
            sw.writeAttribute(WRAPPER_ATTRIBUTE_NAME, wsdlMessageName);
        }
        sw.writeAttribute(WRAPPER_ATTRIBUTE_TYPE, prefix + ":" + wsdlMessageType.getLocalPart());
        sw.writeAttribute("xmlns:" + prefix, wsdlMessageType.getNamespaceURI());
        sw.writeAttribute("xmlns:" + WRAPPER_DEFAULT_NAMESPACE_PREFIX, WRAPPER_DEFAULT_NAMESPACE);
    }
    
    public Source readPayloadAsSource() {
        return wrappedMessage;
    }
    
    public boolean isFault() {
        return abstractMessage.isFault();
    }
    
    public String getPayloadLocalPart() {
        return abstractMessage.getPayloadLocalPart();
    }
    
    public String getPayloadNamespaceURI() {
        return abstractMessage.getPayloadNamespaceURI();
    }
    
    public XMLStreamReader readPayload() throws XMLStreamException {
        return abstractMessage.readPayload();
    }
    
    public boolean hasPayload() {
        return abstractMessage.hasPayload();
    }
    
    public boolean hasHeaders() {
        return abstractMessage.hasHeaders();
    }
    
    public HeaderList getHeaders() {
        return abstractMessage.getHeaders();
    }
    
    public Message copy() {
        return null;
    }
    
    public Source readEnvelopeAsSource() {
        return abstractMessage.readEnvelopeAsSource();
    }
    
    public SOAPMessage readAsSOAPMessage() throws SOAPException {
        return abstractMessage.readAsSOAPMessage();
    }
    
    public <T> T readPayloadAsJAXB(Unmarshaller unmarshaller) throws JAXBException {
        return (T) abstractMessage.readPayloadAsJAXB(unmarshaller);
    }
    
    public <T> T readPayloadAsJAXB(Bridge<T> bridge) throws JAXBException {
        return abstractMessage.readPayloadAsJAXB(bridge);
    }
    
    public void writePayloadTo(XMLStreamWriter sw) throws XMLStreamException {
        try {
            sw.flush();
            abstractMessage.writePayloadTo(sw);
        } catch(Exception ex) {
            ex.printStackTrace();
            throw new XMLStreamException(ex);
        }
    }
    
    public void writeTo(ContentHandler contentHandler, ErrorHandler errorHandler) throws SAXException {
        abstractMessage.writeTo(contentHandler, errorHandler);
    }
    
    /**
     * Writes the wrapped abstractMessage into the stream writer.
     *
     * The fault is not wrapped to compensate a bug in HTTP SOAP BC.
     * It is assumed that the normal response abstractMessage has only one part.
     */
    public void writeTo(XMLStreamWriter sw) throws XMLStreamException {
        writePayloadTo(sw);
    }

    private Node getNodeValue(Document d, String localName) {
        NodeList nl = d.getElementsByTagNameNS("", localName);
        Node n = (nl != null && nl.getLength() > 0)
        ? nl.item(0).getFirstChild()
        : null;
        return n;
    }
    
    public void wrapFault(String operationName, EndpointMetaData emd) {
        
        if(JavaEEServiceEngineContext.getInstance().isServiceMix()) {
            if(log) {
                logger.log(Level.FINE, "Skipping the wrapping...");
            }
            wrappedMessage = abstractMessage.readPayloadAsSource();
            return;
        }
        
        DOMResult result = new DOMResult();
        try {
            Transformer t = TF.newTransformer();
            t.transform(abstractMessage.readPayloadAsSource(), result);
        } catch(Exception ex) {
            ex.printStackTrace();
            throw new RuntimeException(ex);
        }
        Document d = (Document)result.getNode();
        
        if(log) {
            logger.log(Level.FINE, "Fault received from JAX-WS : " +
                    UnWrappedMessage.toString(d));
        }
        
        Node faultDetail = getNodeValue(d, FAULT_DETAIL_ELEMENT);
        
        /*
         * Caused by RuntimeException thrown from JAX-WS.
         */
        if(faultDetail == null) {
            logger.log(Level.WARNING, 
                    "RuntimeException thrown from the JAX-WS. No <detail> found.");
            RuntimeException rt_ex = new RuntimeException(
                    "RuntimeException thrown from the JAX-WS. No details found.");
            // don't send service engine stack trace.
            rt_ex.setStackTrace(new StackTraceElement[]{}); 
            throw rt_ex;
        }
        
        javax.wsdl.Message wsdlMsg = null;
        try {
            wsdlMsg = emd.getFaultMessage(operationName,
                    faultDetail.getLocalName(), faultDetail.getNamespaceURI());
        } catch(Exception ex) {
            logger.log(Level.WARNING, ex.getMessage(), ex);
        }

        /*
         * Caused by RuntimeException thrown from the Application.
         */
        if(wsdlMsg == null) {
            logger.log(Level.WARNING, "RuntimeException thrown from the " +
                    "Application. Fault is not defined in the WSDL");
            
            Node n = getNodeValue(d, FAULT_STRING_ELEMENT);
            RuntimeException rt_ex = new RuntimeException(n.getTextContent());
            // don't send service engine stack trace.
            rt_ex.setStackTrace(new StackTraceElement[]{}); 
            throw rt_ex;
        }
        
        /*
         * Caused by the Checked exception thrown from the Application.
         */
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            XMLStreamWriter writer = XOF.createXMLStreamWriter(baos);
            writer.writeStartDocument();
            String wsdlMessageName = faultDetail.getLocalName();
            this.wsdlMessageType = wsdlMsg.getQName();
            writer.writeStartElement(WRAPPER_MESSAGE_QNAME);
            writeMessageAttributes(writer, wsdlMessageName);
            writer.writeStartElement(WRAPPER_PART_QNAME);
            writer.writeCharacters(""); // Force completion of open elems
            DOMUtil.UTIL.writeNode(faultDetail, writer);
            writer.writeEndElement();
            writer.writeEndElement();
            writer.writeEndDocument();
            writer.flush();
            setMessage(baos.toByteArray());
            
            if(log) {
                logger.log(Level.FINE, "\n\nWrapped fault = " + 
                        baos.toString() + "\n\n");
            }
        } catch(Exception ex) {
            ex.printStackTrace();
            throw new RuntimeException(ex);
        }
    }
    
    /*
    void setMessage(byte[] data) throws Exception {
        ByteArrayInputStream bais = new ByteArrayInputStream(data);
        DocumentBuilder db = dbf.newDocumentBuilder();
        wrappedMessage = new DOMSource(db.parse(bais));
    }
     */
}