/******************************************************************************* * Copyright (c) 2006-2010 eBay Inc. All Rights Reserved. * Licensed 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 *******************************************************************************/ package org.ebayopensource.turmeric.runtime.common.impl.internal.pipeline; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.SocketException; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.logging.Level; import javax.activation.DataHandler; import javax.xml.namespace.QName; import javax.xml.stream.XMLStreamWriter; import org.ebayopensource.turmeric.runtime.binding.DataBindingOptions; import org.ebayopensource.turmeric.runtime.binding.objectnode.ObjectNode; import org.ebayopensource.turmeric.runtime.binding.objectnode.impl.JavaObjectNodeImpl; import org.ebayopensource.turmeric.runtime.common.binding.DataBindingDesc; import org.ebayopensource.turmeric.runtime.common.binding.IProtobufSerializer; import org.ebayopensource.turmeric.runtime.common.binding.Serializer; import org.ebayopensource.turmeric.runtime.common.binding.SerializerFactory; import org.ebayopensource.turmeric.runtime.common.exceptions.ErrorDataFactory; import org.ebayopensource.turmeric.runtime.common.exceptions.ExceptionUtils; import org.ebayopensource.turmeric.runtime.common.exceptions.ServiceException; import org.ebayopensource.turmeric.runtime.common.exceptions.TransportException; import org.ebayopensource.turmeric.runtime.common.impl.attachment.BaseMessageAttachments; import org.ebayopensource.turmeric.runtime.common.impl.attachment.OutboundMessageAttachments; import org.ebayopensource.turmeric.runtime.common.impl.attachment.SOAMimeUtils; import org.ebayopensource.turmeric.runtime.common.impl.binding.jaxb.JAXBBasedSerializer; import org.ebayopensource.turmeric.runtime.common.impl.binding.protobuf.ProtobufSerializerFactory; import org.ebayopensource.turmeric.runtime.common.impl.internal.monitoring.SystemMetricDefs; import org.ebayopensource.turmeric.runtime.common.impl.internal.utils.OutboundRawDataRecorder; import org.ebayopensource.turmeric.runtime.common.impl.utils.LogManager; import org.ebayopensource.turmeric.runtime.common.pipeline.MessageProcessingStage; import org.ebayopensource.turmeric.runtime.common.pipeline.OutboundMessage; import org.ebayopensource.turmeric.runtime.common.pipeline.ProtocolProcessor; import org.ebayopensource.turmeric.runtime.common.service.ServiceOperationDesc; import org.ebayopensource.turmeric.runtime.common.types.Cookie; import org.ebayopensource.turmeric.runtime.common.types.G11nOptions; import org.ebayopensource.turmeric.runtime.common.types.SOAHeaders; import org.ebayopensource.turmeric.runtime.errorlibrary.ErrorConstants; public final class OutboundMessageImpl extends BaseMessageImpl implements OutboundMessage { private int m_maxBytesToRecord; private byte[] m_recordedData; private boolean m_isRest; private boolean m_isUnserializable; private String m_unserializableReason; private int m_maxURLLengthForREST; public OutboundMessageImpl(boolean isRequestMessage,String transportProtocol, DataBindingDesc dataBindingDesc, G11nOptions g11nOptions, Map<String,String> transportHeaders, Cookie[] cookies, Collection<ObjectNode> messageHeaders, BaseMessageAttachments attachment, ServiceOperationDesc operationDesc, boolean isREST, int maxURLLengthForREST, boolean bufferingMode) throws ServiceException { super(isRequestMessage, transportProtocol, dataBindingDesc, g11nOptions, transportHeaders, cookies, messageHeaders, attachment, operationDesc, bufferingMode); m_isRest = isREST; m_maxURLLengthForREST = maxURLLengthForREST; } public OutboundMessageImpl(boolean isRequestMessage, String transportProtocol, DataBindingDesc dataBindingDesc, G11nOptions g11nOptions, Map<String,String> transportHeaders, Cookie[] cookies, Collection<ObjectNode> messageHeaders, BaseMessageAttachments attachment, ServiceOperationDesc operationDesc, boolean isREST, int maxURLLengthForREST) throws ServiceException { this(isRequestMessage, transportProtocol, dataBindingDesc, g11nOptions, transportHeaders, cookies, messageHeaders, attachment, operationDesc, isREST, maxURLLengthForREST, false); } @Override protected void loadParamsImpl() throws ServiceException { List<Class> paramTypes = getParamTypes(); if (paramTypes != null && !paramTypes.isEmpty()) { m_params = new Object[paramTypes.size()]; } } public final boolean isErrorMessage() throws ServiceException { return (m_errorResponse != null); } @SuppressWarnings("unchecked") public Map<String,String> buildOutputHeaders() throws ServiceException { if (hasAttachment()) { ((OutboundMessageAttachments)m_attachments).addAttachmentHeaders(this); } Map<String,String> customHeaders = getTransportHeadersNoCopy(); Map<String,String> result = getContextImpl().buildOutputHeaders(customHeaders); return result; } public void recordPayload(int maxBytes) throws ServiceException { checkNotCleaned(); if (maxBytes > m_maxBytesToRecord) { m_maxBytesToRecord = maxBytes; } } public byte[] getRecordedData() throws ServiceException { if (m_recordedData != null) { byte result[] = new byte[m_recordedData.length]; System.arraycopy(m_recordedData, 0, result, 0, m_recordedData.length); return result; } return null; } public void serialize(OutputStream out) throws ServiceException { if (isUnserializable()) { throw new IllegalStateException("Message is not serializable"); } m_recordedData = null; OutboundRawDataRecorder recorder = null; if (m_maxBytesToRecord != 0) { recorder = new OutboundRawDataRecorder(out, false, m_maxBytesToRecord); out = recorder; } if (getContextImpl().isInboundRawMode()) { try { out.write(getByteBuffer().array()); } catch (IOException e) { throw new ServiceException(ErrorDataFactory.createErrorData(ErrorConstants.SVC_DATA_WRITE_ERROR, ErrorConstants.ERRORDOMAIN, new String[] {e.toString()}), e); } } else { // make sure to get m_params set, even if no values were specified by the app checkParams(); try { if (hasAttachment()) { serializeMessageWithAttachment(out); } else if(isBufferingMode()) { ByteArrayOutputStream baoutputStream = new ByteArrayOutputStream(); serializeMessage(baoutputStream); out.write(baoutputStream.toByteArray()); } else { serializeMessage(out); } } catch (Exception e) { try { filterException(e); } catch(ServiceException se) { try { out.write("Internal server error. Please check the server logs for details".getBytes()); } catch(IOException ioe) { // Something is seriously wrong with the state of output stream } throw se; } } } if (recorder != null) { m_recordedData = recorder.getRawByteData(); } } public void serializeMessageWithAttachment(OutputStream out) throws ServiceException { ByteArrayOutputStream bodyStream = new ByteArrayOutputStream(); serializeMessage(bodyStream); SOAMimeUtils.complete(out, bodyStream.toString(), ((OutboundMessageAttachments)m_attachments)); } public void serializeMessage(OutputStream out) throws ServiceException { BaseMessageContextImpl ctx = getContextImpl(); if (!isRequestMessage()) { MessageProcessingStage processingStage = ctx.getProcessingStage(); if (processingStage != MessageProcessingStage.RESPONSE_DISPATCH) { throw new IllegalStateException( "Server-side serialization is allowed during response dispatch only"); } } SerializerFactory serFactory = getDataBindingDesc().getSerializerFactory(); Serializer ser = serFactory.getSerializer(); List<Class> paramTypes; if (m_errorResponse != null) { paramTypes = getErrorParamTypeList(); } else { paramTypes = getParamTypes(); } long startTime = System.nanoTime(); try { if (serFactory instanceof ProtobufSerializerFactory) { try { IProtobufSerializer serializer = (IProtobufSerializer) ser; if (m_params != null) { for (int i = 0; i < m_params.length; ++i) { serializer.serialize(ctx, m_params[i], paramTypes.get(i), out); } } } finally { try { out.flush(); } catch (Exception e) { throw new ServiceException(ErrorDataFactory.createErrorData( ErrorConstants.SVC_DATA_WRITE_ERROR, ErrorConstants.ERRORDOMAIN, new String[] { e.toString() }), e); } } } else { XMLStreamWriter xmlStream = serFactory.getXMLStreamWriter(this, paramTypes, out); ProtocolProcessor protocolProcessor = ctx .getProtocolProcessor(); if (m_errorResponse != null) { // note that we use errorMessage's class here // in case when ErrorMapper fails, this could be our default // ErrorMessage // and not the one produced by ErrorMapper implementation protocolProcessor.preSerialize(this, xmlStream); QName xmlName = getMessageXMLName(getParamDesc(), m_errorResponse.getClass()); // If service version is not provided as http header by // client request, // we assume the request is using common types ErrorMessage // to pass error back. /* * 03/06/2009 Comment this code out because the migration is * over. if (shouldReturnCommonTypeErrorMessage()) { * convertToCommonTypeErrorMessage(); xmlName = new * QName(BindingConstants.SOA_COMMON_TYPES_NAMESPACE, * xmlName.getLocalPart(), * BindingConstants.SOA_COMMON_TYPES_PREFIX); } */ ser.serialize(this, m_errorResponse, xmlName, m_errorResponse.getClass(), xmlStream); protocolProcessor.postSerialize(this, xmlStream); } else { protocolProcessor.preSerialize(this, xmlStream); if (m_params != null) { for (int i = 0; i < m_params.length; i++) { Object value = m_params[i]; Class<?> clazz = paramTypes.get(i); QName xmlName = getMessageXMLName(getParamDesc(), clazz); ser.serialize(this, value, xmlName, clazz, xmlStream); } } protocolProcessor.postSerialize(this, xmlStream); } try { xmlStream.flush(); out.flush(); } catch (Exception e) { throw new ServiceException(ErrorDataFactory.createErrorData( ErrorConstants.SVC_DATA_WRITE_ERROR, ErrorConstants.ERRORDOMAIN, new String[] { e.toString() }), e); } } } finally { long duration = System.nanoTime() - startTime; ctx.updateSvcAndOpMetric(SystemMetricDefs.OP_TIME_SERIALIZATION, startTime, duration); } } public void serializeBody(OutputStream out) throws ServiceException { SerializerFactory serFactory = getDataBindingDesc().getSerializerFactory(); Serializer ser = serFactory.getSerializer(); List<Class> paramTypes; paramTypes = getParamTypes(); XMLStreamWriter xmlStream = serFactory.getXMLStreamWriter(this, paramTypes, out); if (m_params != null) { for (int i=0; i<m_params.length; i++) { Object value = m_params[i]; Class<?> clazz = paramTypes.get(i); QName xmlName = getMessageXMLName(getParamDesc(), clazz); ser.serialize(this, value, xmlName, clazz, xmlStream); } } } public void setErrorResponse(Object error) throws ServiceException { if (error == null) { throw new NullPointerException(); } markAsLoaded(); m_errorResponse = error; m_params = null; } // TODO - protection may need to be tightened up public void setG11nOptions(G11nOptions options) { m_g11nOptions = options; } public boolean isREST() { return m_isRest; } public int getMaxURLLengthForREST() { return m_maxURLLengthForREST; } public void addDataHandler(DataHandler dh, String id) { if (null == m_attachments) { return; } ((OutboundMessageAttachments)m_attachments).addDataHandler(dh, id); } public String getUnserializableReason() { return m_unserializableReason; } public boolean isUnserializable() { return m_isUnserializable; } public void setUnserializable(String reason) { m_isUnserializable = true; m_unserializableReason = reason; } public Collection<ObjectNode> getMessageHeaders() throws ServiceException { return m_messageHeaders; } public void addMessageHeaderAsJavaObject(Object headerJavaObject) throws ServiceException { m_messageHeaders.add(new JavaObjectNodeImpl(null, headerJavaObject)); } public void serializeHeader(XMLStreamWriter out) throws ServiceException { if (null == getMessageHeaders()) { return; } for (ObjectNode node : getMessageHeaders()) { //TODO: We should refactor this, possibly add a serialize method to ObjectNode. if (node instanceof JavaObjectNodeImpl) { serializeJavaObjectNode((JavaObjectNodeImpl)node, out); } else { throw new ServiceException(ErrorDataFactory.createErrorData(ErrorConstants.SVC_DATA_UNSUPPORTED_OBJECT_NODE_SERIALIZATION, ErrorConstants.ERRORDOMAIN, new Object[] {node.getClass().getSimpleName()})); } } } private void serializeJavaObjectNode(JavaObjectNodeImpl node, XMLStreamWriter out) throws ServiceException { Object obj = node.getNodeValue(); SerializerFactory serFactory = getDataBindingDesc().getSerializerFactory(); Serializer ser = serFactory.getSerializer(); if (obj instanceof String) { ser.serialize(this, obj, node.getNodeName(), obj.getClass(), out); } else { QName xmlName = getMessageXMLName(getHeaderParamDesc(), obj.getClass()); ser.serialize(this, obj, xmlName, obj.getClass(), out); } } public int getTransportErrorResponseIndicationCode() throws ServiceException { BaseMessageContextImpl ctx = getContextImpl(); ProtocolProcessor protocolProcessor = ctx.getProtocolProcessor(); int code = HttpURLConnection.HTTP_INTERNAL_ERROR; // Default. Will be overwritten. if (ctx.getRequestMessage().getTransportHeader(SOAHeaders.ALTERNATE_FAULT_STATUS) != null) { code = protocolProcessor.getAlternateTransportErrorResponseIndicationCode(); } else { code = protocolProcessor.getTransportErrorResponseIndicationCode(); } return code; } public void filterException(Exception e) throws ServiceException { SerializerFactory serFactory = getDataBindingDesc().getSerializerFactory(); Map<String, String> options = serFactory.getOptions(); boolean ignoreClientTimeout = DataBindingOptions.IgnoreClientTimeout.getBoolOption(options); ServiceException se = null; SocketException socketException = ExceptionUtils.getClientTimeoutException(e); if (socketException == null) {// not caused by socket exception if (e instanceof ServiceException) { se = (ServiceException) e; } else { se = new ServiceException(ErrorDataFactory.createErrorData( ErrorConstants.SVC_DATA_WRITE_ERROR, ErrorConstants.ERRORDOMAIN, new String[] { e .toString() }), e); } throw se; } // caused by socket exception se = new TransportException(ErrorDataFactory .createErrorData(ErrorConstants.SVC_DATA_WRITE_ERROR, ErrorConstants.ERRORDOMAIN, new String[] { socketException.toString() }), socketException); if (!ignoreClientTimeout) { se = new TransportException(ErrorDataFactory .createErrorData(ErrorConstants.SVC_DATA_WRITE_ERROR, ErrorConstants.ERRORDOMAIN, new String[] { socketException.toString() }), socketException); throw se; } LogManager.getInstance(JAXBBasedSerializer.class).log(Level.SEVERE, "Client timeout", se); } }