Coverage Report - org.mule.transport.jdbc.JdbcMessageReceiver
 
Classes in this File Line Coverage Branch Coverage Complexity
JdbcMessageReceiver
0%
0/87
0%
0/62
0
 
 1  
 /*
 2  
  * $Id: JdbcMessageReceiver.java 20346 2010-11-25 10:14:22Z pablo.kraan $
 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.LinkedList;
 30  
 import java.util.List;
 31  
 
 32  
 /**
 33  
  * Implements {@link TransactedPollingMessageReceiver} reading data from a database.
 34  
  * Provides a way to acknowledge each read data using a SQL statement.
 35  
  */
 36  
 public class JdbcMessageReceiver extends TransactedPollingMessageReceiver
 37  
 {
 38  
 
 39  
     public static final String RECEIVE_MESSAGE_IN_TRANSCTION = "receiveMessageInTransaction";
 40  
     public static final String RECEIVE_MESSAGES_IN_XA_TRANSCTION = "receiveMessagesInXaTransaction";
 41  
     
 42  
     protected JdbcConnector connector;
 43  
     protected String readStmt;
 44  
     protected String ackStmt;
 45  
     protected List readParams;
 46  
     protected List ackParams;
 47  0
     public boolean receiveMessagesInXaTransaction = false;
 48  
     private volatile boolean aggregateResult;
 49  
     
 50  
     public JdbcMessageReceiver(Connector connector,
 51  
                                FlowConstruct flowConstruct,
 52  
                                InboundEndpoint endpoint,
 53  
                                String readStmt,
 54  
                                String ackStmt) throws CreateException
 55  
     {
 56  0
         super(connector, flowConstruct, endpoint);
 57  0
         this.setFrequency(((JdbcConnector) connector).getPollingFrequency());
 58  
 
 59  0
         boolean transactedEndpoint = endpoint.getTransactionConfig().isTransacted();
 60  0
         boolean xaTransactedEndpoint = (transactedEndpoint &&
 61  
             endpoint.getTransactionConfig().getFactory() instanceof XaTransactionFactory);
 62  
         
 63  0
         boolean receiveMessageInTransaction = MapUtils.getBooleanValue(endpoint.getProperties(),
 64  
             RECEIVE_MESSAGE_IN_TRANSCTION, false);
 65  0
         this.setReceiveMessagesInTransaction(receiveMessageInTransaction && transactedEndpoint);
 66  0
         if (receiveMessageInTransaction && !transactedEndpoint)
 67  
         {
 68  0
             logger.warn(JdbcMessages.forcePropertyNoTransaction(RECEIVE_MESSAGE_IN_TRANSCTION, "transaction"));
 69  0
             receiveMessageInTransaction = false;
 70  
         }
 71  
         
 72  0
         receiveMessagesInXaTransaction = MapUtils.getBooleanValue(endpoint.getProperties(),
 73  
             RECEIVE_MESSAGES_IN_XA_TRANSCTION, false);
 74  0
         if (receiveMessagesInXaTransaction && !receiveMessageInTransaction)
 75  
         {
 76  0
             logger.warn(JdbcMessages.forceProperty(RECEIVE_MESSAGES_IN_XA_TRANSCTION, RECEIVE_MESSAGE_IN_TRANSCTION));
 77  0
             receiveMessagesInXaTransaction = false;
 78  
         }
 79  0
         else if (receiveMessagesInXaTransaction && isReceiveMessagesInTransaction() && !xaTransactedEndpoint)
 80  
         {
 81  0
             logger.warn(JdbcMessages.forcePropertyNoTransaction(RECEIVE_MESSAGES_IN_XA_TRANSCTION, "XA transaction"));
 82  0
             receiveMessagesInXaTransaction = false;
 83  
         }
 84  
         
 85  0
         this.connector = (JdbcConnector) connector;
 86  0
         this.setReceiveMessagesInTransaction(endpoint.getTransactionConfig().isTransacted()
 87  
             && !this.connector.isTransactionPerMessage());
 88  
 
 89  0
         parseStatements(readStmt, ackStmt);
 90  0
     }
 91  
 
 92  
     /**
 93  
      * Parses the read and acknowledge SQL statements
 94  
      */
 95  
     protected void parseStatements(String readStmt, String ackStmt)
 96  
     {
 97  0
         this.readParams = new ArrayList();
 98  0
         this.readStmt = this.connector.parseStatement(readStmt, this.readParams);
 99  0
         this.ackParams = new ArrayList();
 100  0
         this.ackStmt = this.connector.parseStatement(ackStmt, this.ackParams);
 101  0
     }
 102  
 
 103  
     @Override
 104  
     protected void doDispose()
 105  
     {
 106  
         // template method
 107  0
     }
 108  
 
 109  
     @Override
 110  
     protected void doConnect() throws Exception
 111  
     {
 112  
         // template method
 113  0
     }
 114  
 
 115  
     @Override
 116  
     protected void doDisconnect() throws Exception
 117  
     {
 118  
         // noop
 119  0
     }
 120  
 
 121  
     @Override
 122  
     public void processMessage(Object message) throws Exception
 123  
     {
 124  0
         Connection con = null;
 125  0
         Transaction tx = TransactionCoordination.getInstance().getTransaction();
 126  
         try
 127  
         {
 128  0
             con = this.connector.getConnection();
 129  0
             MuleMessage muleMessage = createMuleMessage(message, endpoint.getEncoding());
 130  0
             if (hasAckStatement())
 131  
             {
 132  0
                 if (aggregateResult)
 133  
                 {
 134  0
                     List<MuleMessage> messages = createMuleMessages((List) message);
 135  0
                     int[] nbRows = executeBatchAckStatement(con, messages);
 136  
 
 137  0
                     if (nbRows[0] == 0)
 138  
                     {
 139  0
                         logger.warn(".ack statement did not update any rows");
 140  
                     }
 141  
                     // Reset this flag
 142  0
                     aggregateResult = false;
 143  0
                 }
 144  
                 else
 145  
                 {
 146  0
                     int nbRows = executeAckStatement(con, muleMessage);
 147  0
                     if (nbRows == 0)
 148  
                     {
 149  0
                         logger.warn(".ack statement did not update any rows");
 150  
                     }
 151  
                 }
 152  
             }
 153  0
             routeMessage(muleMessage);
 154  
 
 155  
         }
 156  0
         catch (Exception ex)
 157  
         {
 158  0
             if (tx != null)
 159  
             {
 160  0
                 tx.setRollbackOnly();
 161  
             }
 162  
 
 163  
             // rethrow
 164  0
             throw ex;
 165  
         }
 166  
         finally
 167  
         {
 168  0
             if (tx == null || tx.isXA())
 169  
             {
 170  
                 // We are running in an XA transaction.
 171  
                 // This call is required here for compatibility with strict XA
 172  
                 // DataSources
 173  
                 // implementations, as is the case for WebSphere AS and Weblogic.
 174  
                 // Failure to do it here may result in a connection leak.
 175  
                 // The close() call will NOT close the connection, neither will it
 176  
                 // return it to the pool.
 177  
                 // It will notify the XA driver's ConnectionEventListener that the XA
 178  
                 // connection
 179  
                 // is no longer used by the application and is ready for the 2PC
 180  
                 // commit.
 181  0
                 JdbcUtils.close(con);
 182  
             }
 183  
         }
 184  0
     }
 185  
 
 186  
     protected boolean hasAckStatement()
 187  
     {
 188  0
         return this.ackStmt != null;
 189  
     }
 190  
 
 191  
     /**
 192  
      * Creates a mule message per each data record.
 193  
      *
 194  
      * @param records data records used to created the payload of the new messages.
 195  
      * @return the created messages
 196  
      */
 197  
     protected List<MuleMessage> createMuleMessages(List<Object> records)
 198  
     {
 199  0
         List<MuleMessage> messages = new LinkedList<MuleMessage>();
 200  0
         for (Object record : records)
 201  
         {
 202  0
             messages.add(new DefaultMuleMessage(record, connector.getMuleContext()));
 203  
         }
 204  
 
 205  0
         return messages;
 206  
     }
 207  
 
 208  
     /**
 209  
      * Executes the acknowledge SQL statement for a given message.
 210  
      *
 211  
      * @param con         database connection to execute the statement
 212  
      * @param muleMessage message to been acknowledge
 213  
      * @return the number of updated rows by the SQL statement
 214  
      * @throws Exception
 215  
      */
 216  
     protected int executeAckStatement(Connection con, MuleMessage muleMessage)
 217  
             throws Exception
 218  
     {
 219  0
         Object[] paramValues = connector.getParams(endpoint, this.ackParams, muleMessage, this.endpoint.getEndpointURI().getAddress());
 220  0
         if (logger.isDebugEnabled())
 221  
         {
 222  0
             logger.debug("SQL UPDATE: " + ackStmt + ", params = " + ArrayUtils.toString(paramValues));
 223  
         }
 224  0
         int nbRows = connector.getQueryRunnerFor(endpoint).update(con, this.ackStmt, paramValues);
 225  0
         return nbRows;
 226  
     }
 227  
 
 228  
     /**
 229  
      * Executes the acknowledge SQL statement for a list of messages.
 230  
      *
 231  
      * @param con      database connection to execute the statement
 232  
      * @param messages messages to be acknowledge
 233  
      * @return the number of updated rows by each batched execution
 234  
      * @throws Exception
 235  
      */
 236  
     protected int[] executeBatchAckStatement(Connection con, List<MuleMessage> messages)
 237  
             throws Exception
 238  
     {
 239  0
         Object[][] paramValuesArray = new Object[messages.size()][];
 240  
 
 241  0
         for (int i = 0; i < messages.size(); i++)
 242  
         {
 243  0
             MuleMessage message = messages.get(i);
 244  0
             paramValuesArray[i] = connector.getParams(endpoint, this.ackParams, message, this.endpoint.getEndpointURI().getAddress());
 245  
         }
 246  
 
 247  0
         if (logger.isDebugEnabled())
 248  
         {
 249  0
             logger.debug("SQL UPDATE: " + ackStmt + ", params = " + ArrayUtils.toString(ackParams));
 250  
         }
 251  
 
 252  0
         int[] nbRows = connector.getQueryRunnerFor(endpoint).batch(con, this.ackStmt, paramValuesArray);
 253  
 
 254  0
         return nbRows;
 255  
     }
 256  
 
 257  
     public List getMessages() throws Exception
 258  
     {
 259  0
         Connection con = null;
 260  
         try
 261  
         {
 262  0
             con = this.connector.getConnection();
 263  
 
 264  0
             List resultList = executeReadStatement(con);
 265  0
             if (resultList != null && resultList.size() > 1 && isReceiveMessagesInTransaction() && !receiveMessagesInXaTransaction)
 266  
             {
 267  0
                 aggregateResult = true;
 268  0
                 logger.warn(JdbcMessages.moreThanOneMessageInTransaction(RECEIVE_MESSAGE_IN_TRANSCTION, RECEIVE_MESSAGES_IN_XA_TRANSCTION));
 269  0
                 List singleResultList = new ArrayList(1);
 270  0
                 singleResultList.add(resultList);
 271  0
                 return singleResultList;
 272  
             }
 273  
             
 274  0
             return resultList;
 275  
         }
 276  
         finally
 277  
         {
 278  0
             if (TransactionCoordination.getInstance().getTransaction() == null)
 279  
             {
 280  0
                 JdbcUtils.close(con);
 281  
             }
 282  
         }
 283  
     }
 284  
 
 285  
     /**
 286  
      * Executes the read SQL statement to get data from the database.
 287  
      *
 288  
      * @param con database connection to execute the statement
 289  
      * @return the list of read records
 290  
      * @throws Exception
 291  
      */
 292  
     protected List executeReadStatement(Connection con) throws Exception
 293  
     {
 294  0
         Object[] readParams = connector.getParams(endpoint, this.readParams, null, this.endpoint.getEndpointURI().getAddress());
 295  0
         if (logger.isDebugEnabled())
 296  
         {
 297  0
             logger.debug("SQL QUERY: " + readStmt + ", params = " + ArrayUtils.toString(readParams));
 298  
         }
 299  0
         Object results = connector.getQueryRunnerFor(endpoint).query(con, this.readStmt, readParams,
 300  
                 connector.getResultSetHandler());
 301  
 
 302  0
         return (List) results;
 303  
     }
 304  
 }