/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.synapse.format.hessian; import org.apache.axiom.om.OMElement; import org.apache.axiom.om.OMNode; import org.apache.axiom.om.OMOutputFormat; import org.apache.axiom.om.OMText; import org.apache.axiom.soap.SOAPFault; import org.apache.axis2.AxisFault; import org.apache.axis2.Constants; import org.apache.axis2.context.MessageContext; import org.apache.axis2.transport.MessageFormatter; import org.apache.axis2.transport.http.util.URLTemplatingUtil; import org.apache.commons.io.IOUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.synapse.util.SynapseBinaryDataSource; import javax.activation.DataHandler; import java.io.BufferedOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URL; import java.util.Iterator; /** * Enables a message encoded using the Hessian binary protocol to be written to transport by * axis2/synapse and this formats the HessianDataSource to a Hessian message. * * @see org.apache.axis2.transport.MessageFormatter * @see org.apache.synapse.util.SynapseBinaryDataSource */ public class HessianMessageFormatter implements MessageFormatter { private static final Log log = LogFactory.getLog(HessianMessageFormatter.class); /** * Formats the content type to be written in to the transport * * @param msgCtxt message of which the content type has to be formatted * @param format fomat of the expected formatted message * @param soapActionString soap action of the message * * @return contentType formatted content type as a String */ public String getContentType(MessageContext msgCtxt, OMOutputFormat format, String soapActionString) { String contentType = (String) msgCtxt.getProperty(Constants.Configuration.CONTENT_TYPE); if (contentType == null) { contentType = HessianConstants.HESSIAN_CONTENT_TYPE; } String encoding = format.getCharSetEncoding(); if (encoding != null) { contentType += "; charset=" + encoding; } return contentType; } /** * Extract Hessian bytes from the received SOAP message and write it onto the wire * * @param msgCtx message from which the Hessian message has to be extracted * @param format message format to be written * @param out stream to which the message is written * @param preserve whether to preserve the indentations * * @throws AxisFault in case of a failure in writing the message to the provided stream */ public void writeTo(MessageContext msgCtx, OMOutputFormat format, OutputStream out, boolean preserve) throws AxisFault { if (log.isDebugEnabled()) { log.debug("Start writing the Hessian message to OutputStream"); } // Check whether the message to be written is a fault message if (msgCtx.getFLOW() == MessageContext.OUT_FAULT_FLOW || msgCtx.getEnvelope().hasFault()) { SOAPFault soapFault = msgCtx.getEnvelope().getBody().getFault(); convertAndWriteHessianFault(soapFault, out); } else { // no differentiation between normal reply and fault (pass the original message through) writeHessianMessage(msgCtx, out); } if (log.isDebugEnabled()) { log.debug("Writing message as a Hessian message is successful"); } } /** * This method is not supported because of large file handling limitations * * @param msgCtxt message which contains the Hessian message inside the HessianDataSource * @param format message format to be written * * @return Hessian binary bytes of the message * * @throws AxisFault for any invocation */ public byte[] getBytes(MessageContext msgCtxt, OMOutputFormat format) throws AxisFault { throw new AxisFault("Method not supported. Use the " + "HessianMessageFormatter#writeTo method instead"); } /** * {@inheritDoc} * * Simply returns the soapAction unchanged. */ public String formatSOAPAction(MessageContext messageContext, OMOutputFormat format, String soapAction) { return soapAction; } /** * {@inheritDoc} * * @return A templated URL based on the given target URL. */ public URL getTargetAddress(MessageContext messageContext, OMOutputFormat format, URL targetURL) throws AxisFault { return URLTemplatingUtil.getTemplatedURL(targetURL, messageContext, false); } /** * Writes the Hessian message contained in the message context to the provided output stream. * * @param msgCtxt the message context containing the Hessian message * @param out the provided output stream to which the message shall be written * * @throws AxisFault if an error occurs writing to the output stream */ private void writeHessianMessage(MessageContext msgCtxt, OutputStream out) throws AxisFault { OMElement omElement = msgCtxt.getEnvelope().getBody().getFirstElement(); SynapseBinaryDataSource synapseBinaryDataSource = extractSynapseBinaryDataSource(omElement); if (synapseBinaryDataSource != null) { InputStream inputStream = null; try { inputStream = synapseBinaryDataSource.getInputStream(); IOUtils.copy(inputStream, out); } catch (IOException e) { handleException("Couldn't get the bytes from the HessianDataSource", e); } finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException ignore) { log.warn("Error closing input stream.", ignore); } } if (out != null) { try { out.close(); } catch (IOException ignore) { log.warn("Error closing output stream.", ignore); } } } } else { handleException("Unable to find the Hessian content in the payload"); } } /** * Tries to extract the binary data source containing the Hessian message. * * @param omElement * * @return the binary data source containing the Hessian message or null, if the OMElement * does not contain a binary datasource. */ private SynapseBinaryDataSource extractSynapseBinaryDataSource(OMElement omElement) { SynapseBinaryDataSource synapseBinaryDataSource = null; Iterator it = omElement.getChildren(); while (it.hasNext() && synapseBinaryDataSource == null) { OMNode hessianElement = (OMNode) it.next(); if (hessianElement instanceof OMText) { OMText tempNode = (OMText) hessianElement; if (tempNode.getDataHandler() != null && ((DataHandler) tempNode.getDataHandler()).getDataSource() instanceof SynapseBinaryDataSource) { synapseBinaryDataSource = (SynapseBinaryDataSource) ((DataHandler) tempNode .getDataHandler()).getDataSource(); } } } return synapseBinaryDataSource; } /** * Reads details from the SOAPFault and creates a new Hessian fault using those details and * writes it to the output stream. * * @param soapFault the SOAP fault to convert and write as a Hessian fault * @param out the output stream to write the Hessian fault to * * @throws AxisFault if an error occurs writing the message to the output stream */ private void convertAndWriteHessianFault(SOAPFault soapFault, OutputStream out) throws AxisFault { BufferedOutputStream faultOutStream = new BufferedOutputStream(out); try { String hessianFaultCode = "500"; String hessianFaultMessage = ""; String hessianFaultDetail = ""; if (soapFault.getCode() != null) { hessianFaultCode = soapFault.getCode().getText(); } if (soapFault.getReason() != null) { hessianFaultMessage = soapFault.getReason().getText(); } if (soapFault.getDetail() != null) { hessianFaultDetail = soapFault.getDetail().getText(); } HessianUtils.writeFault(hessianFaultCode, hessianFaultMessage, hessianFaultDetail, faultOutStream); faultOutStream.flush(); } catch (IOException e) { handleException("Unalbe to write the fault as a Hessian message", e); } finally { try { if (faultOutStream != null) { faultOutStream.close(); } } catch (IOException ignore) { log.warn("Error closing output stream.", ignore); } } } /** * Logs the original exception, wrappes it in an AxisFault and rethrows it. * * @param msg the error message * @param e the original exception * * @throws AxisFault */ private void handleException(String msg, Exception e) throws AxisFault { log.error(msg, e); throw new AxisFault(msg, e); } /** * Logs an error message and throws a newly created AxisFault. * * @param msg the error message * * @throws AxisFault */ private void handleException(String msg) throws AxisFault { log.error(msg); throw new AxisFault(msg); } }