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