View Javadoc
1   /*
2    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
3    * The software in this package is published under the terms of the CPAL v1.0
4    * license, a copy of which has been included with this distribution in the
5    * LICENSE.txt file.
6    */
7   package org.mule.transport.tcp;
8   
9   import org.mule.DefaultMuleMessage;
10  import org.mule.api.MuleEvent;
11  import org.mule.api.MuleMessage;
12  import org.mule.api.endpoint.ImmutableEndpoint;
13  import org.mule.api.endpoint.OutboundEndpoint;
14  import org.mule.api.retry.RetryContext;
15  import org.mule.api.transformer.TransformerException;
16  import org.mule.transport.AbstractMessageDispatcher;
17  import org.mule.transport.NullPayload;
18  
19  import java.io.BufferedInputStream;
20  import java.io.BufferedOutputStream;
21  import java.io.DataInputStream;
22  import java.io.IOException;
23  import java.net.Socket;
24  import java.net.SocketTimeoutException;
25  
26  /**
27   * Send transformed Mule events over TCP.
28   */
29  public class TcpMessageDispatcher extends AbstractMessageDispatcher
30  {
31  
32      private final TcpConnector connector;
33  
34      public TcpMessageDispatcher(OutboundEndpoint endpoint)
35      {
36          super(endpoint);
37          this.connector = (TcpConnector) endpoint.getConnector();
38      }
39  
40      @Override
41      protected synchronized void doDispatch(MuleEvent event) throws Exception
42      {
43          Socket socket = connector.getSocket(event.getEndpoint());
44          try 
45          {
46              dispatchToSocket(socket, event);
47          }
48          finally 
49          {
50              connector.releaseSocket(socket, event.getEndpoint());
51          }
52      }
53  
54      private void doDispatchToSocket(Socket socket, MuleEvent event) throws Exception
55      {
56          try
57          {
58              dispatchToSocket(socket, event);
59          }
60          catch(Exception e)
61          {
62              connector.releaseSocket(socket, event.getEndpoint());
63              throw new Exception(e);
64          }
65      }
66  
67      @Override
68      protected synchronized MuleMessage doSend(MuleEvent event) throws Exception
69      {
70          Socket socket = connector.getSocket(event.getEndpoint());
71          doDispatchToSocket(socket, event);
72          try
73          {
74              if (returnResponse(event))
75              {
76                  try
77                  {
78                      Object result = receiveFromSocket(socket, event.getTimeout(), endpoint);
79                      if (result == null)
80                      {
81                          return new DefaultMuleMessage(NullPayload.getInstance(), connector.getMuleContext());
82                      }
83                      
84                      if (result instanceof MuleMessage)
85                      {
86                          return (MuleMessage) result;
87                      }
88                      
89                      return createMuleMessage(result, endpoint.getEncoding());
90                  }
91                  catch (SocketTimeoutException e)
92                  {
93                      // we don't necessarily expect to receive a response here
94                      logger.info("Socket timed out normally while doing a synchronous receive on endpointUri: "
95                          + event.getEndpoint().getEndpointURI());
96                      return new DefaultMuleMessage(NullPayload.getInstance(), connector.getMuleContext());
97                  }
98              }
99              else
100             {
101                 return new DefaultMuleMessage(NullPayload.getInstance(), connector.getMuleContext());
102             }
103         }
104         finally
105         {
106             if (!returnResponse(event))
107             {
108                 connector.releaseSocket(socket, endpoint);
109             }
110         }
111         
112     }
113 
114     // Socket management (get and release) is handled outside this method
115     private void dispatchToSocket(Socket socket, MuleEvent event) throws Exception
116     {
117         Object payload = event.getMessage().getPayload();
118         write(socket, payload);
119     }
120 
121     private void write(Socket socket, Object data) throws IOException, TransformerException
122     {
123         BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
124         connector.getTcpProtocol().write(bos, data);
125         bos.flush();
126     }
127 
128     protected static Object receiveFromSocket(final Socket socket, int timeout, final ImmutableEndpoint endpoint)
129             throws IOException
130     {
131         final TcpConnector connector = (TcpConnector) endpoint.getConnector();
132         DataInputStream underlyingIs = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
133         TcpInputStream tis = new TcpInputStream(underlyingIs)
134         {
135             @Override
136             public void close() throws IOException
137             {
138                 try
139                 {
140                     connector.releaseSocket(socket, endpoint);
141                 }
142                 catch (IOException e)
143                 {
144                    throw e;
145                 }
146                 catch (Exception e)
147                 {
148                     IOException e2 = new IOException();
149                     e2.initCause(e);
150                     throw e2;
151                 }
152             }
153 
154         };
155 
156         int soTimeout = endpoint.getResponseTimeout() != 0 ? endpoint.getResponseTimeout() : timeout;
157         if (soTimeout >= 0)
158         {
159             socket.setSoTimeout(soTimeout);
160         }
161 
162         try
163         {
164             return connector.getTcpProtocol().read(tis);
165         }
166         finally
167         {
168             if (!tis.isStreaming())
169             {
170                 tis.close();
171             }
172         }
173     }
174 
175     @Override
176     protected synchronized void doDispose()
177     {
178         try
179         {
180             doDisconnect();
181         }
182         catch (Exception e)
183         {
184             logger.error("Failed to shutdown the dispatcher.", e);
185         }
186     }
187 
188     @Override
189     protected void doConnect() throws Exception
190     {
191         // nothing, there is an optional validation in validateConnection()
192     }
193 
194     @Override
195     protected void doDisconnect() throws Exception
196     {
197         //nothing to do
198     }
199 
200     @Override
201     public RetryContext validateConnection(RetryContext retryContext)
202     {
203         Socket socket = null;
204         try
205         {
206             socket = connector.getSocket(endpoint);
207 
208             retryContext.setOk();
209         }
210         catch (Exception ex)
211         {
212             retryContext.setFailed(ex);
213         }
214         finally
215         {
216             if (socket != null)
217             {
218                 try
219                 {
220                     connector.releaseSocket(socket, endpoint);
221                 }
222                 catch (Exception e)
223                 {
224                     if (logger.isDebugEnabled())
225                     {
226                         logger.debug("Failed to release a socket " + socket, e);
227                     }
228                 }
229             }
230         }
231         
232         return retryContext;
233     }
234 }