View Javadoc

1   /*
2    * $Id: JdbcMessageDispatcher.java 12370 2008-07-17 13:11:17Z tcarlson $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSource, Inc.  All rights reserved.  http://www.mulesource.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  
11  package org.mule.transport.jdbc;
12  
13  import org.mule.DefaultMuleMessage;
14  import org.mule.api.MuleEvent;
15  import org.mule.api.MuleMessage;
16  import org.mule.api.endpoint.ImmutableEndpoint;
17  import org.mule.api.endpoint.OutboundEndpoint;
18  import org.mule.api.transaction.Transaction;
19  import org.mule.api.transport.MessageAdapter;
20  import org.mule.transaction.TransactionCoordination;
21  import org.mule.transport.AbstractMessageDispatcher;
22  import org.mule.util.ArrayUtils;
23  import org.mule.util.StringUtils;
24  
25  import java.sql.Connection;
26  import java.util.ArrayList;
27  import java.util.List;
28  
29  import org.apache.commons.logging.Log;
30  import org.apache.commons.logging.LogFactory;
31  
32  /**
33   * The Jdbc Message dispatcher is responsible for executing SQL queries against a
34   * database.
35   */
36  public class JdbcMessageDispatcher extends AbstractMessageDispatcher
37  {
38  
39      protected static Log staticLogger = LogFactory.getLog(AbstractMessageDispatcher.class);
40  
41      protected JdbcConnector connector;
42      protected static final String STORED_PROCEDURE_PREFIX = "{ ";
43      protected static final String STORED_PROCEDURE_SUFFIX = " }";
44  
45      public JdbcMessageDispatcher(OutboundEndpoint endpoint)
46      {
47          super(endpoint);
48          this.connector = (JdbcConnector) endpoint.getConnector();
49      }
50  
51      protected void doDispose()
52      {
53          // template method
54      }
55      
56      protected Object executeWriteStatement(MuleEvent event, String writeStmt) throws Exception
57      {
58          List paramNames = new ArrayList();
59          writeStmt = connector.parseStatement(writeStmt, paramNames);
60  
61          Object[] paramValues = connector.getParams(endpoint, paramNames, new DefaultMuleMessage(
62              event.transformMessage()), this.endpoint.getEndpointURI().getAddress());
63  
64          Transaction tx = TransactionCoordination.getInstance().getTransaction();
65          Connection con = null;
66          
67          MuleMessage message = event.getMessage();
68          
69          try
70          {
71              con = this.connector.getConnection();
72              
73              if ("call".equalsIgnoreCase(writeStmt.substring(0, 4)))
74              {
75                  writeStmt = STORED_PROCEDURE_PREFIX + writeStmt + STORED_PROCEDURE_SUFFIX;
76              }
77              
78              if (logger.isDebugEnabled())
79              {
80                  logger.debug("SQL UPDATE: " + writeStmt + ", params = " + ArrayUtils.toString(paramValues));
81              }
82              int nbRows = connector.getQueryRunner().update(con, writeStmt, paramValues);
83              if (nbRows != 1)
84              {
85                  logger.warn("Row count for write should be 1 and not " + nbRows);
86              }
87              if (tx == null)
88              {
89                  JdbcUtils.commitAndClose(con);
90              }
91              logger.debug("MuleEvent dispatched succesfuly");
92          }
93          catch (Exception e)
94          {
95              logger.debug("Error dispatching event: " + e.getMessage(), e);
96              if (tx == null)
97              {
98                  JdbcUtils.rollbackAndClose(con);
99              }
100             throw e;
101         }
102         
103         return message;
104     }
105     
106     protected String getStatement(ImmutableEndpoint endpoint)
107     {
108         String writeStmt = endpoint.getEndpointURI().getAddress();
109         String str;
110         if ((str = this.connector.getQuery(endpoint, writeStmt)) != null)
111         { 
112             writeStmt = str;
113         }
114         writeStmt = StringUtils.trimToEmpty(writeStmt);
115         if (StringUtils.isBlank(writeStmt))
116         {
117             throw new IllegalArgumentException("Missing statement");
118         }
119         
120         return writeStmt;
121     }
122     
123     protected boolean isWriteStatement(String writeStmt)
124     {
125         if (!"insert".equalsIgnoreCase(writeStmt.substring(0, 6))
126                         && !"update".equalsIgnoreCase(writeStmt.substring(0, 6))
127                         && !"delete".equalsIgnoreCase(writeStmt.substring(0, 6))
128                         && !"merge".equalsIgnoreCase(writeStmt.substring(0, 5))
129                         && !"call".equalsIgnoreCase(writeStmt.substring(0, 4)))
130         {
131             return false;
132         }
133         
134         return true;
135     }
136 
137     protected void doDispatch(MuleEvent event) throws Exception
138     {
139         if (logger.isDebugEnabled())
140         {
141             logger.debug("Dispatch event: " + event);
142         }
143         
144         String writeStmt = getStatement(event.getEndpoint());
145         
146         if (!isWriteStatement(writeStmt))
147         {
148             throw new IllegalArgumentException(
149                 "Write statement should be an insert / update / delete / merge sql statement, or a stored-procedure call");
150         }
151         
152         this.executeWriteStatement(event, writeStmt);
153         
154     }
155 
156     protected MuleMessage doSend(MuleEvent event) throws Exception
157     {
158         String statement = getStatement(event.getEndpoint());
159         
160         if (isWriteStatement(statement))
161         {
162             executeWriteStatement(event, statement);
163             return event.getMessage();
164         }
165         
166         return executeRequest(event.getTimeout(),event, connector, endpoint);
167         
168     }
169 
170     /**
171      * This does work for both dispatcher and requester
172      *
173      * @param timeout
174      * @param event
175      * @param connector
176      * @param endpoint
177      * @return
178      * @throws Exception
179      */
180     protected static MuleMessage executeRequest(long timeout, MuleEvent event,
181                                                JdbcConnector connector, ImmutableEndpoint endpoint) throws Exception
182     {
183         if (staticLogger.isDebugEnabled())
184         {
185             staticLogger.debug("Trying to receive a message with a timeout of " + timeout);
186         }
187 
188         String[] stmts = connector.getReadAndAckStatements(endpoint);
189         String readStmt = stmts[0];
190         String ackStmt = stmts[1];
191         List readParams = new ArrayList();
192         List ackParams = new ArrayList();
193         readStmt = connector.parseStatement(readStmt, readParams);
194         ackStmt = connector.parseStatement(ackStmt, ackParams);
195 
196         Connection con = null;
197         long t0 = System.currentTimeMillis();
198         Transaction tx  = TransactionCoordination.getInstance().getTransaction();
199         try
200         {
201             con = connector.getConnection();
202             if (timeout < 0)
203             {
204                 timeout = Long.MAX_VALUE;
205             }
206             Object result;
207             do
208             {
209                 Object[] params = connector.getParams(endpoint, readParams,
210                     event!=null ? event.getMessage() : null,
211                     endpoint.getEndpointURI().getAddress());
212                 if (staticLogger.isDebugEnabled())
213                 {
214                     staticLogger.debug("SQL QUERY: " + readStmt + ", params = " + ArrayUtils.toString(params));
215                 }
216                 result = connector.getQueryRunner().query(con, readStmt, params, connector.getResultSetHandler());
217                 if (result != null)
218                 {
219                     if (staticLogger.isDebugEnabled())
220                     {
221                         staticLogger.debug("Received: " + result);
222                     }
223                     break;
224                 }
225                 long sleep = Math.min(connector.getPollingFrequency(),
226                                       timeout - (System.currentTimeMillis() - t0));
227                 if (sleep > 0)
228                 {
229                     if (staticLogger.isDebugEnabled())
230                     {
231                         staticLogger.debug("No results, sleeping for " + sleep);
232                     }
233                     Thread.sleep(sleep);
234                 }
235                 else
236                 {
237                     staticLogger.debug("Timeout");
238                     JdbcUtils.rollbackAndClose(con);
239                     return null;
240                 }
241             }
242             while (true);
243             if (ackStmt != null)
244             {
245                 Object[] params = connector.getParams(endpoint, ackParams, result, ackStmt);
246                 if (staticLogger.isDebugEnabled())
247                 {
248                     staticLogger.debug("SQL UPDATE: " + ackStmt + ", params = " + ArrayUtils.toString(params));
249                 }
250                 int nbRows = connector.getQueryRunner().update(con, ackStmt, params);
251                 if (nbRows != 1)
252                 {
253                     staticLogger.warn("Row count for ack should be 1 and not " + nbRows);
254                 }
255             }
256             MessageAdapter msgAdapter = connector.getMessageAdapter(result);
257             MuleMessage message = new DefaultMuleMessage(msgAdapter);
258             if (tx == null)
259             {
260                 JdbcUtils.commitAndClose(con);
261             }
262             
263             return message;
264         }
265         catch (Exception e)
266         {
267             if (tx == null)
268             {
269                 JdbcUtils.rollbackAndClose(con);
270             }
271             throw e;
272         }
273 
274     }
275 
276 
277     protected void doConnect() throws Exception
278     {
279         // template method
280     }
281 
282     protected void doDisconnect() throws Exception
283     {
284         // template method
285     }
286 
287 }