/* * 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.axis2.transport.tcp; import org.apache.axiom.soap.SOAPEnvelope; import org.apache.axis2.AxisFault; import org.apache.axis2.Constants; import org.apache.axis2.transport.TransportUtils; import org.apache.axis2.context.MessageContext; import org.apache.axis2.engine.AxisEngine; import org.apache.axis2.util.MessageContextBuilder; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.StreamTokenizer; import java.net.Socket; import java.util.Arrays; import javax.xml.parsers.FactoryConfigurationError; import javax.xml.stream.XMLStreamException; /** * This Class is the work hoarse of the TCP request, this process the incomming SOAP Message. */ public class TCPWorker implements Runnable { private static final Log log = LogFactory.getLog(TCPWorker.class); private TCPEndpoint endpoint; private Socket socket; public TCPWorker(TCPEndpoint endpoint, Socket socket) { this.endpoint = endpoint; this.socket = socket; } public void run() { MessageContext msgContext = null; try { msgContext = endpoint.createMessageContext(); msgContext.setIncomingTransportName(Constants.TRANSPORT_TCP); TCPOutTransportInfo outInfo = new TCPOutTransportInfo(); outInfo.setSocket(socket); outInfo.setClientResponseRequired(endpoint.isClientResponseRequired()); outInfo.setContentType(endpoint.getContentType()); String delimiter = endpoint.getRecordDelimiter(); int recordLength = endpoint.getRecordLength(); String inputType = endpoint.getInputType(); String delimiterType = endpoint.getRecordDelimiterType(); outInfo.setDelimiter(delimiter); outInfo.setDelimiterType(delimiterType); msgContext.setProperty(Constants.OUT_TRANSPORT_INFO, outInfo); // create the SOAP Envelope InputStream input = socket.getInputStream(); boolean handled = false; if (recordLength > -1) { this.handleRecordLength(msgContext, input, recordLength); handled = true; } if (!delimiter.isEmpty() && !handled) { if (inputType != null) { if (TCPConstants.STRING_INPUT_TYPE.equalsIgnoreCase(inputType)) { if(TCPConstants.BYTE_DELIMITER_TYPE.equalsIgnoreCase(delimiterType)) { int delimiterVal = Integer.parseInt(delimiter.split("0x")[1], 16); this.handleCharacterRecordDelimiterStringStream(msgContext, input, delimiterVal); } else if(TCPConstants.STRING_DELIMITER_TYPE.equalsIgnoreCase(delimiterType)) { this.handleStringRecordDelimiterStringStream(msgContext, input, delimiter); } else { this.handleCharacterRecordDelimiterStringStream(msgContext, input, delimiter.charAt(0)); } } else { if(TCPConstants.BYTE_DELIMITER_TYPE.equalsIgnoreCase(delimiterType)) { int delimiterVal = Integer.parseInt(delimiter.split("0x")[1], 16); this.handleCharacterRecordDelimiterBinaryStream(msgContext, input, delimiterVal); } else if(TCPConstants.STRING_DELIMITER_TYPE.equalsIgnoreCase(delimiterType)) { this.handleStringRecordDelimiterBinaryStream(msgContext, input, delimiter); } else { this.handleCharacterRecordDelimiterBinaryStream(msgContext, input, delimiter.charAt(0)); } } } handled = true; } if (!handled) { SOAPEnvelope envelope = TransportUtils.createSOAPMessage(msgContext, input, endpoint.getContentType()); msgContext.setEnvelope(envelope); AxisEngine.receive(msgContext); } } catch (Exception e) { sendFault(msgContext, e); } finally { if (!endpoint.isClientResponseRequired()) { try { socket.close(); } catch (IOException e) { log.error("Error while closing a TCP socket", e); } } } } /** * Handling record delimiter character type for string stream * * @param msgContext the messahe contenxt * @param input socket input stream * @param delimiter character value to delimit incoming message * @throws XMLStreamException if xml parsing error occurred * @throws FactoryConfigurationError if configuration issue occurred */ private void handleCharacterRecordDelimiterStringStream(MessageContext msgContext, InputStream input, int delimiter) throws AxisFault { if(log.isDebugEnabled()) { log.debug("Handle message with character delimiter type string stream"); } StreamTokenizer tokenizer = new StreamTokenizer(new InputStreamReader(input)); tokenizer.resetSyntax(); tokenizer.wordChars('\u0000', (char) (delimiter - 1)); tokenizer.wordChars((char) (delimiter + 1), '\u00ff'); tokenizer.whitespaceChars('\n', '\n'); tokenizer.whitespaceChars(delimiter, delimiter); tokenizer.eolIsSignificant(true); int type = 0; // Stores the value returned by nextToken() try { type = tokenizer.nextToken(); while (type != StreamTokenizer.TT_EOF) { if (type == StreamTokenizer.TT_WORD) { handleEnvelope(msgContext, tokenizer.sval.getBytes()); } else { assert false; // We only expect words } type = tokenizer.nextToken(); } } catch (IOException e) { sendFault(msgContext, e); } } /** * Handling record delimiter character type in binary stream * * @param msgContext the message context * @param input socket input stream * @param delimiter character value to delimit incoming message * @throws AxisFault if error occurred while processing */ private void handleCharacterRecordDelimiterBinaryStream(MessageContext msgContext, InputStream input, int delimiter) throws AxisFault { if(log.isDebugEnabled()) { log.debug("Handle message with character delimiter type binary stream"); } ByteArrayOutputStream bos = null; try { int next = input.read(); while (next > -1) { if (next == delimiter && bos != null) { try { bos.flush(); } catch (IOException e) { sendFault(msgContext, e); } byte[] result = bos.toByteArray(); handleEnvelope(msgContext, result); bos.close(); bos = null; next = input.read(); continue; } if (bos == null) { bos = new ByteArrayOutputStream(); } if (next != delimiter) { bos.write(next); } next = input.read(); } if (bos != null) { handleEnvelope(msgContext, bos.toByteArray()); } } catch (IOException e) { sendFault(msgContext, e); } finally { if (bos != null) { try { bos.close(); } catch (IOException e) { sendFault(msgContext, e); } } } } /** * Handling record string type delimiter in string stream * * @param msgContext the message context * @param input socket input stream * @param delimiter delimiter string * @throws AxisFault if error occurred while processing */ private void handleStringRecordDelimiterStringStream(MessageContext msgContext, InputStream input, String delimiter) throws AxisFault { if(log.isDebugEnabled()) { log.debug("Handle message with string delimiter type string stream"); } ByteArrayOutputStream bos = new ByteArrayOutputStream(); try { int next = input.read(); while(next > -1) { bos.write(next); next = input.read(); if(input.available() <= 0) { if(next > -1) { bos.write(next); } String[] segments = bos.toString().split(delimiter); for(String s : segments) { handleEnvelope(msgContext, s.getBytes()); } bos = new ByteArrayOutputStream(); next = input.read(); } } } catch (IOException e) { sendFault(msgContext, e); } finally { try { bos.close(); } catch (IOException e) { sendFault(msgContext, e); } } } /** * Handling record delimiter string type delimiter in binary stream * * @param msgContext the message context * @param input socket input stream * @param delimiter delimiter string * @throws AxisFault if error occurred while processing */ private void handleStringRecordDelimiterBinaryStream(MessageContext msgContext, InputStream input, String delimiter) throws AxisFault { if(log.isDebugEnabled()) { log.debug("Handle message with string delimiter type binary stream"); } ByteArrayOutputStream bos = new ByteArrayOutputStream(); ByteArrayOutputStream chunk = new ByteArrayOutputStream(); try { byte[] delimiterBytes = delimiter.getBytes(); int next = input.read(); while(next > -1) { bos.write(next); next = input.read(); if(input.available() <= 0) { if (next > -1) { bos.write(next); } byte[] contents = bos.toByteArray(); for(int i = 0; i< (contents.length - delimiterBytes.length + 1); i++) { byte[] temp = new byte[delimiterBytes.length]; int count = 0; for(int j = i; j < (i + delimiterBytes.length); j++) { temp[count] = contents[j]; count++; } if(Arrays.equals(delimiterBytes, temp)) { handleEnvelope(msgContext, chunk.toByteArray()); chunk = new ByteArrayOutputStream(); i = i + delimiterBytes.length - 1; } else { chunk.write(contents[i]); } } bos =new ByteArrayOutputStream(); next = input.read(); } } } catch (IOException e) { sendFault(msgContext, e); } finally { try { bos.close(); chunk.close(); } catch (IOException e) { sendFault(msgContext, e); } } } /** * Handling record length delimiter * @param msgContext the message context * @param input the socket input stream * @param recordLength the record length to split the message * @throws AxisFault if error occurred while processing */ private void handleRecordLength(MessageContext msgContext, InputStream input, int recordLength) throws AxisFault { ByteArrayOutputStream baos = null; ByteArrayInputStream bais = null; byte[] bytes = new byte[recordLength]; try { for (int len; (len = input.read(bytes)) > 0;) { baos = new ByteArrayOutputStream(); baos.write(bytes, 0, len); bais = new ByteArrayInputStream(baos.toByteArray()); SOAPEnvelope envelope = TransportUtils.createSOAPMessage(msgContext, bais, endpoint.getContentType()); msgContext.setEnvelope(envelope); AxisEngine.receive(msgContext); } } catch (IOException e) { sendFault(msgContext, e); } catch (XMLStreamException e) { sendFault(msgContext, e); } finally { if(baos != null) { try { baos.close(); } catch (IOException e) { sendFault(msgContext, e); } if(bais != null) { try { bais.close(); } catch (IOException e) { sendFault(msgContext, e); } } } } } private void handleEnvelope(MessageContext msgContext, byte [] value) throws AxisFault { ByteArrayInputStream bais = null; try { bais = new ByteArrayInputStream(value); SOAPEnvelope envelope = TransportUtils.createSOAPMessage(msgContext, bais, endpoint.getContentType()); msgContext.setEnvelope(envelope); AxisEngine.receive(msgContext); } catch (IOException e) { sendFault(msgContext, e); } catch (XMLStreamException e) { sendFault(msgContext, e); } finally { if(bais != null) { try { bais.close(); } catch (IOException e) { sendFault(msgContext, e); } } } } private void sendFault(MessageContext msgContext, Exception fault) { log.error("Error while processing TCP request through the Axis2 engine", fault); try { if (msgContext != null) { msgContext.setProperty(MessageContext.TRANSPORT_OUT, socket.getOutputStream()); MessageContext faultContext = MessageContextBuilder.createFaultMessageContext(msgContext, fault); AxisEngine.sendFault(faultContext); } } catch (Exception e) { log.error("Error while sending the fault response", e); } } }