View Javadoc

1   /*
2    * $Id: JdbcMessageReceiver.java 19191 2010-08-25 21:05:23Z tcarlson $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.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.MuleMessage;
15  import org.mule.api.construct.FlowConstruct;
16  import org.mule.api.endpoint.InboundEndpoint;
17  import org.mule.api.lifecycle.CreateException;
18  import org.mule.api.transaction.Transaction;
19  import org.mule.api.transport.Connector;
20  import org.mule.transaction.TransactionCoordination;
21  import org.mule.transaction.XaTransactionFactory;
22  import org.mule.transport.TransactedPollingMessageReceiver;
23  import org.mule.transport.jdbc.i18n.JdbcMessages;
24  import org.mule.util.ArrayUtils;
25  import org.mule.util.MapUtils;
26  
27  import java.sql.Connection;
28  import java.util.ArrayList;
29  import java.util.HashMap;
30  import java.util.List;
31  
32  /** TODO */
33  public class JdbcMessageReceiver extends TransactedPollingMessageReceiver
34  {
35  
36      public static final String RECEIVE_MESSAGE_IN_TRANSCTION = "receiveMessageInTransaction";
37      public static final String RECEIVE_MESSAGES_IN_XA_TRANSCTION = "receiveMessagesInXaTransaction";
38      
39      protected JdbcConnector connector;
40      protected String readStmt;
41      protected String ackStmt;
42      protected List readParams;
43      protected List ackParams;
44      public boolean receiveMessagesInXaTransaction = false;
45      private volatile boolean aggregateResult;
46      
47      public JdbcMessageReceiver(Connector connector,
48                                 FlowConstruct flowConstruct,
49                                 InboundEndpoint endpoint,
50                                 String readStmt,
51                                 String ackStmt) throws CreateException
52      {
53          super(connector, flowConstruct, endpoint);
54          this.setFrequency(((JdbcConnector) connector).getPollingFrequency());
55  
56          boolean transactedEndpoint = endpoint.getTransactionConfig().isTransacted();
57          boolean xaTransactedEndpoint = (transactedEndpoint &&
58              endpoint.getTransactionConfig().getFactory() instanceof XaTransactionFactory);
59          
60          boolean receiveMessageInTransaction = MapUtils.getBooleanValue(endpoint.getProperties(),
61              RECEIVE_MESSAGE_IN_TRANSCTION, false);
62          this.setReceiveMessagesInTransaction(receiveMessageInTransaction && transactedEndpoint);
63          if (receiveMessageInTransaction && !transactedEndpoint)
64          {
65              logger.warn(JdbcMessages.forcePropertyNoTransaction(RECEIVE_MESSAGE_IN_TRANSCTION, "transaction"));
66              receiveMessageInTransaction = false;
67          }
68          
69          receiveMessagesInXaTransaction = MapUtils.getBooleanValue(endpoint.getProperties(),
70              RECEIVE_MESSAGES_IN_XA_TRANSCTION, false);
71          if (receiveMessagesInXaTransaction && !receiveMessageInTransaction)
72          {
73              logger.warn(JdbcMessages.forceProperty(RECEIVE_MESSAGES_IN_XA_TRANSCTION, RECEIVE_MESSAGE_IN_TRANSCTION));
74              receiveMessagesInXaTransaction = false;
75          }
76          else if (receiveMessagesInXaTransaction && isReceiveMessagesInTransaction() && !xaTransactedEndpoint)
77          {
78              logger.warn(JdbcMessages.forcePropertyNoTransaction(RECEIVE_MESSAGES_IN_XA_TRANSCTION, "XA transaction"));
79              receiveMessagesInXaTransaction = false;
80          }
81          
82          this.connector = (JdbcConnector) connector;
83          this.setReceiveMessagesInTransaction(endpoint.getTransactionConfig().isTransacted()
84              && !this.connector.isTransactionPerMessage());
85          
86          this.readParams = new ArrayList();
87          this.readStmt = this.connector.parseStatement(readStmt, this.readParams);
88          this.ackParams = new ArrayList();
89          this.ackStmt = this.connector.parseStatement(ackStmt, this.ackParams);
90      }
91  
92      @Override
93      protected void doDispose()
94      {
95          // template method
96      }
97  
98      @Override
99      protected void doConnect() throws Exception
100     {
101         // template method
102     }
103 
104     @Override
105     protected void doDisconnect() throws Exception
106     {
107         // noop
108     }
109 
110     @Override
111     public void processMessage(Object message) throws Exception
112     {
113         Connection con = null;
114         Transaction tx = TransactionCoordination.getInstance().getTransaction();
115         try
116         {
117             con = this.connector.getConnection();
118             MuleMessage muleMessage = createMuleMessage(message, endpoint.getEncoding());
119             if (this.ackStmt != null)
120             {
121                 if (aggregateResult)
122                 {
123                     List rows = (List) message;
124                     Object[][] paramValuesArray = new Object[rows.size()][];
125 
126                     HashMap record;
127                     for (int i = 0; i <  rows.size(); i++)
128                     {
129                         record = (HashMap) rows.get(i);
130                         paramValuesArray[i] = connector.getParams(endpoint, this.ackParams, new DefaultMuleMessage(record, connector.getMuleContext()), this.endpoint.getEndpointURI().getAddress());
131                     }
132                     if (logger.isDebugEnabled())
133                     {
134                         logger.debug("SQL UPDATE: " + ackStmt + ", params = " + ArrayUtils.toString(ackParams));
135                     }
136                     int[] nbRows = connector.getQueryRunnerFor(endpoint).batch(con, this.ackStmt, paramValuesArray);
137                     if (nbRows[0] == 0)
138                     {
139                         logger.warn(".ack statement did not update any rows");
140                     }
141                     // Reset this flag
142                     aggregateResult = false;
143                 }
144                 else
145                 {
146                     Object[] paramValues = connector.getParams(endpoint, this.ackParams, muleMessage, this.endpoint.getEndpointURI().getAddress());
147                     if (logger.isDebugEnabled())
148                     {
149                         logger.debug("SQL UPDATE: " + ackStmt + ", params = " + ArrayUtils.toString(paramValues));
150                     }
151                     int nbRows = connector.getQueryRunnerFor(endpoint).update(con, this.ackStmt, paramValues);
152                     if (nbRows == 0)
153                     {
154                         logger.warn(".ack statement did not update any rows");
155                     }
156                 }
157             }
158             routeMessage(muleMessage);
159 
160         }
161         catch (Exception ex)
162         {
163             if (tx != null)
164             {
165                 tx.setRollbackOnly();
166             }
167 
168             // rethrow
169             throw ex;
170         }
171         finally
172         {
173             if (tx == null || tx.isXA())
174             {
175                 // We are running in an XA transaction.
176                 // This call is required here for compatibility with strict XA
177                 // DataSources
178                 // implementations, as is the case for WebSphere AS and Weblogic.
179                 // Failure to do it here may result in a connection leak.
180                 // The close() call will NOT close the connection, neither will it
181                 // return it to the pool.
182                 // It will notify the XA driver's ConnectionEventListener that the XA
183                 // connection
184                 // is no longer used by the application and is ready for the 2PC
185                 // commit.
186                 JdbcUtils.close(con);
187             }
188         }
189     }
190 
191     @Override
192     public List<MuleMessage> getMessages() throws Exception
193     {
194         Connection con = null;
195         try
196         {
197             con = this.connector.getConnection();
198 
199             Object[] readParams = connector.getParams(endpoint, this.readParams, null, this.endpoint.getEndpointURI().getAddress());
200             if (logger.isDebugEnabled())
201             {
202                 logger.debug("SQL QUERY: " + readStmt + ", params = " + ArrayUtils.toString(readParams));
203             }
204             Object results = connector.getQueryRunnerFor(endpoint).query(con, this.readStmt, readParams,
205                     connector.getResultSetHandler());
206 
207             List resultList = (List) results;
208             if (resultList != null && resultList.size() > 1 && isReceiveMessagesInTransaction() && !receiveMessagesInXaTransaction)
209             {
210                 aggregateResult = true;
211                 logger.warn(JdbcMessages.moreThanOneMessageInTransaction(RECEIVE_MESSAGE_IN_TRANSCTION, RECEIVE_MESSAGES_IN_XA_TRANSCTION));
212                 List singleResultList = new ArrayList(1);
213                 singleResultList.add(resultList);
214                 return singleResultList;
215             }
216             
217             return resultList;
218         }
219         finally
220         {
221             if (TransactionCoordination.getInstance().getTransaction() == null)
222             {
223                 JdbcUtils.close(con);
224             }
225         }
226     }
227 
228 }