Package org.mule.transport.jdbc

Source Code of org.mule.transport.jdbc.JdbcMessageReceiver

/*
* $Id: JdbcMessageReceiver.java 22263 2011-06-27 08:31:06Z dirk.olmes $
* --------------------------------------------------------------------------------------
* Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
*
* The software in this package is published under the terms of the CPAL v1.0
* license, a copy of which has been included with this distribution in the
* LICENSE.txt file.
*/

package org.mule.transport.jdbc;

import org.mule.DefaultMuleMessage;
import org.mule.api.MuleMessage;
import org.mule.api.construct.FlowConstruct;
import org.mule.api.endpoint.InboundEndpoint;
import org.mule.api.lifecycle.CreateException;
import org.mule.api.transaction.Transaction;
import org.mule.api.transport.Connector;
import org.mule.transaction.TransactionCoordination;
import org.mule.transaction.XaTransactionFactory;
import org.mule.transport.TransactedPollingMessageReceiver;
import org.mule.transport.jdbc.i18n.JdbcMessages;
import org.mule.util.ArrayUtils;
import org.mule.util.MapUtils;

import java.sql.Connection;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

/**
* Implements {@link TransactedPollingMessageReceiver} reading data from a database.
* Provides a way to acknowledge each read data using a SQL statement.
*/
public class JdbcMessageReceiver extends TransactedPollingMessageReceiver
{

    public static final String RECEIVE_MESSAGE_IN_TRANSCTION = "receiveMessageInTransaction";
    public static final String RECEIVE_MESSAGES_IN_XA_TRANSCTION = "receiveMessagesInXaTransaction";

    protected JdbcConnector connector;
    protected String readStmt;
    protected String ackStmt;
    protected List<?> readParams;
    protected List<?> ackParams;
    public boolean receiveMessagesInXaTransaction = false;
    private volatile boolean aggregateResult;

    public JdbcMessageReceiver(Connector connector,
                               FlowConstruct flowConstruct,
                               InboundEndpoint endpoint,
                               String readStmt,
                               String ackStmt) throws CreateException
    {
        super(connector, flowConstruct, endpoint);
        this.setFrequency(((JdbcConnector) connector).getPollingFrequency());

        boolean transactedEndpoint = endpoint.getTransactionConfig().isTransacted();
        boolean xaTransactedEndpoint = (transactedEndpoint &&
            endpoint.getTransactionConfig().getFactory() instanceof XaTransactionFactory);

        boolean receiveMessageInTransaction = MapUtils.getBooleanValue(endpoint.getProperties(),
            RECEIVE_MESSAGE_IN_TRANSCTION, false);
        this.setReceiveMessagesInTransaction(receiveMessageInTransaction && transactedEndpoint);
        if (receiveMessageInTransaction && !transactedEndpoint)
        {
            logger.warn(JdbcMessages.forcePropertyNoTransaction(RECEIVE_MESSAGE_IN_TRANSCTION, "transaction"));
            receiveMessageInTransaction = false;
        }

        receiveMessagesInXaTransaction = MapUtils.getBooleanValue(endpoint.getProperties(),
            RECEIVE_MESSAGES_IN_XA_TRANSCTION, false);
        if (receiveMessagesInXaTransaction && !receiveMessageInTransaction)
        {
            logger.warn(JdbcMessages.forceProperty(RECEIVE_MESSAGES_IN_XA_TRANSCTION, RECEIVE_MESSAGE_IN_TRANSCTION));
            receiveMessagesInXaTransaction = false;
        }
        else if (receiveMessagesInXaTransaction && isReceiveMessagesInTransaction() && !xaTransactedEndpoint)
        {
            logger.warn(JdbcMessages.forcePropertyNoTransaction(RECEIVE_MESSAGES_IN_XA_TRANSCTION, "XA transaction"));
            receiveMessagesInXaTransaction = false;
        }

        this.connector = (JdbcConnector) connector;
        this.setReceiveMessagesInTransaction(endpoint.getTransactionConfig().isTransacted()
            && !this.connector.isTransactionPerMessage());

        parseStatements(readStmt, ackStmt);
    }

    /**
     * Parses the read and acknowledge SQL statements
     */
    protected void parseStatements(String readStmt, String ackStmt)
    {
        this.readParams = new ArrayList<Object>();
        this.readStmt = this.connector.parseStatement(readStmt, this.readParams);
        this.ackParams = new ArrayList<Object>();
        this.ackStmt = this.connector.parseStatement(ackStmt, this.ackParams);
    }

    @Override
    protected void doDispose()
    {
        // template method
    }

    @Override
    protected void doConnect() throws Exception
    {
        // template method
    }

    @Override
    protected void doDisconnect() throws Exception
    {
        // noop
    }

    @Override
    public void processMessage(Object message) throws Exception
    {
        Connection con = null;
        Transaction tx = TransactionCoordination.getInstance().getTransaction();
        try
        {
            con = this.connector.getConnection();
            MuleMessage muleMessage = createMuleMessage(message, endpoint.getEncoding());
            routeMessage(muleMessage);
            if (hasAckStatement())
            {
                if (aggregateResult)
                {
                    List<MuleMessage> messages = createMuleMessages((List) message);
                    int[] nbRows = executeBatchAckStatement(con, messages);

                    if (nbRows[0] == 0)
                    {
                        logger.warn(".ack statement did not update any rows");
                    }
                    // Reset this flag
                    aggregateResult = false;
                }
                else
                {
                    int nbRows = executeAckStatement(con, muleMessage);
                    if (nbRows == 0)
                    {
                        logger.warn(".ack statement did not update any rows");
                    }
                }
            }
        }
        catch (Exception ex)
        {
            if (tx != null)
            {
                tx.setRollbackOnly();
            }

            // rethrow
            throw ex;
        }
        finally
        {
            if (tx == null || tx.isXA())
            {
                // We are running in an XA transaction.
                // This call is required here for compatibility with strict XA
                // DataSources
                // implementations, as is the case for WebSphere AS and Weblogic.
                // Failure to do it here may result in a connection leak.
                // The close() call will NOT close the connection, neither will it
                // return it to the pool.
                // It will notify the XA driver's ConnectionEventListener that the XA
                // connection
                // is no longer used by the application and is ready for the 2PC
                // commit.
                JdbcUtils.close(con);
            }
        }
    }

    protected boolean hasAckStatement()
    {
        return this.ackStmt != null;
    }

    /**
     * Creates a mule message per each data record.
     *
     * @param records data records used to created the payload of the new messages.
     * @return the created messages
     */
    protected List<MuleMessage> createMuleMessages(List<Object> records)
    {
        List<MuleMessage> messages = new LinkedList<MuleMessage>();
        for (Object record : records)
        {
            messages.add(new DefaultMuleMessage(record, connector.getMuleContext()));
        }

        return messages;
    }

    /**
     * Executes the acknowledge SQL statement for a given message.
     *
     * @param con         database connection to execute the statement
     * @param muleMessage message to been acknowledge
     * @return the number of updated rows by the SQL statement
     * @throws Exception
     */
    protected int executeAckStatement(Connection con, MuleMessage muleMessage)
            throws Exception
    {
        Object[] paramValues = connector.getParams(endpoint, this.ackParams, muleMessage, this.endpoint.getEndpointURI().getAddress());
        if (logger.isDebugEnabled())
        {
            logger.debug("SQL UPDATE: " + ackStmt + ", params = " + ArrayUtils.toString(paramValues));
        }
        int nbRows = connector.getQueryRunnerFor(endpoint).update(con, this.ackStmt, paramValues);
        return nbRows;
    }

    /**
     * Executes the acknowledge SQL statement for a list of messages.
     *
     * @param con      database connection to execute the statement
     * @param messages messages to be acknowledge
     * @return the number of updated rows by each batched execution
     * @throws Exception
     */
    protected int[] executeBatchAckStatement(Connection con, List<MuleMessage> messages)
            throws Exception
    {
        Object[][] paramValuesArray = new Object[messages.size()][];

        for (int i = 0; i < messages.size(); i++)
        {
            MuleMessage message = messages.get(i);
            paramValuesArray[i] = connector.getParams(endpoint, this.ackParams, message, this.endpoint.getEndpointURI().getAddress());
        }

        if (logger.isDebugEnabled())
        {
            logger.debug("SQL UPDATE: " + ackStmt + ", params = " + ArrayUtils.toString(ackParams));
        }

        int[] nbRows = connector.getQueryRunnerFor(endpoint).batch(con, this.ackStmt, paramValuesArray);

        return nbRows;
    }

    @Override
    public List getMessages() throws Exception
    {
        Connection con = null;
        try
        {
            con = this.connector.getConnection();

            List resultList = executeReadStatement(con);
            if (resultList != null && resultList.size() > 1 && isReceiveMessagesInTransaction() && !receiveMessagesInXaTransaction)
            {
                aggregateResult = true;
                logger.warn(JdbcMessages.moreThanOneMessageInTransaction(RECEIVE_MESSAGE_IN_TRANSCTION, RECEIVE_MESSAGES_IN_XA_TRANSCTION));
                List singleResultList = new ArrayList(1);
                singleResultList.add(resultList);
                return singleResultList;
            }

            return resultList;
        }
        finally
        {
            if (TransactionCoordination.getInstance().getTransaction() == null)
            {
                JdbcUtils.close(con);
            }
        }
    }

    /**
     * Executes the read SQL statement to get data from the database.
     *
     * @param con database connection to execute the statement
     * @return the list of read records
     * @throws Exception
     */
    protected List executeReadStatement(Connection con) throws Exception
    {
        Object[] readParams = connector.getParams(endpoint, this.readParams, null, this.endpoint.getEndpointURI().getAddress());
        if (logger.isDebugEnabled())
        {
            logger.debug("SQL QUERY: " + readStmt + ", params = " + ArrayUtils.toString(readParams));
        }
        Object results = connector.getQueryRunnerFor(endpoint).query(con, this.readStmt, readParams,
                connector.getResultSetHandler());

        return (List) results;
    }
}
TOP

Related Classes of org.mule.transport.jdbc.JdbcMessageReceiver

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.