Package org.jboss.internal.soa.esb.couriers

Source Code of org.jboss.internal.soa.esb.couriers.SqlTableCourier$MessagePickupProspect

/*
* JBoss, Home of Professional Open Source
* Copyright 2006, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/

package org.jboss.internal.soa.esb.couriers;

import org.apache.log4j.Logger;
import org.jboss.internal.soa.esb.couriers.helpers.JDBCEprDBResourceFactory;
import org.jboss.internal.soa.esb.util.StreamUtils;
import org.jboss.soa.esb.addressing.Call;
import org.jboss.soa.esb.addressing.eprs.JDBCEpr;
import org.jboss.soa.esb.common.*;
import org.jboss.soa.esb.couriers.*;
import org.jboss.soa.esb.message.Message;
import org.jboss.soa.esb.util.Util;
import org.xml.sax.SAXException;
import org.jboss.soa.esb.client.ServiceInvoker;
import org.jboss.soa.esb.listeners.message.MessageDeliverException;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.Serializable;
import java.io.StringReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.sql.*;
import java.util.UUID;

import javax.xml.parsers.ParserConfigurationException;

public class SqlTableCourier implements PickUpOnlyCourier, DeliverOnlyCourier
{
    public static final String SQL_RETRY_COUNT = "org.jboss.soa.esb.sql.retry.count";

    protected long _pollLatency = 200;

    protected long _sleepForRetries = 3000; // milliseconds

    protected boolean deleteOnSuccess, deleteOnError;
    protected boolean _isReceiver;

    private boolean monitoringRetryCount;

    private int messageType = Types.OTHER ;

    private JDBCEprDBResourceFactory jdbcFactory;
    protected static final Logger _logger = Logger.getLogger(SqlTableCourier.class);

    /**
     * Dead letter channel ServiceInvoker. Messages are delivered to the DLQ after the retry limit for a
     * failed message has been exceeded.
     */
    private static ServiceInvoker dlQueueInvoker;

    /**
     * Redelivery retry limit.
     */
    private static int retryLimit;

    static {
        String retryLimitConfig = ModulePropertyManager.getPropertyManager(ModulePropertyManager.TRANSPORTS_MODULE).getProperty(Environment.SQL_RETRY_LIMIT, "5").trim();
        try {
            retryLimit = Integer.parseInt(retryLimitConfig);
        } catch (NumberFormatException e) {
            retryLimit = 5;
        }
    }

    /**
   * package protected constructor - Objects of Courier should only be
   * instantiated by the Factory
   *
   * @param epr
   */
  SqlTableCourier(JDBCEpr epr) throws CourierException
  {
    this(epr, false);
    }

  /**
   * package protected constructor - Objects of Courier should only be
   * instantiated by the Factory
   *
   * @param epr
   */
  SqlTableCourier(JDBCEpr epr, boolean isReceiver) throws CourierException
  {
    _isReceiver = isReceiver;
    _sleepForRetries = 3000// TODO magic number - configurable?

    deleteOnSuccess = Boolean.TRUE.equals(Boolean.valueOf(epr
            .getPostDelete()));
    deleteOnError = Boolean.TRUE.equals(Boolean.valueOf(epr
            .getErrorDelete()));

          jdbcFactory = new JDBCEprDBResourceFactory(epr);

        monitoringRetryCount = (epr.getRetryCountColumn() != null);
    }

  public void cleanup() {
  }

  /**
   * package the ESB message in a java.io.Serializable, and write it.
   * Delivery occurs within its own transaction if there is no
   * global transaction active.
   *
   * @param message
   *            Message - the message to deliverAsync
   * @return boolean - the result of the delivery
   * @throws CourierException -
   *             if problems were encountered
   */
 
  public boolean deliver(Message message) throws CourierException
  {
    if (_isReceiver)
      throw new CourierException("This is a read-only Courier");

    if (null == message)
      return false;

    String msgId;
    Call call = message.getHeader().getCall();
    if (null==call)
      message.getHeader().setCall(call=new Call());
    try
    {
      if (null==call.getMessageID())
        call.setMessageID(new URI(UUID.randomUUID().toString()));
      msgId = call.getMessageID().toString();
    }
    catch (URISyntaxException e)
    {
      throw new CourierException("Problems with message header ",e);
    }

        boolean transactional = isTransactional();

        Serializable serializedMessage;
        try {
            serializedMessage = Util.serialize(message);
        } catch (Exception e) {
            throw new CourierTransportException("Unable to serialize ESB Message.", e);
        }

        Connection connection = jdbcFactory.createConnection(transactional);
        try
        {
            PreparedStatement insertStatement = jdbcFactory.createInsertStatement(connection);
            try {
                insertStatement.setString(1, msgId);
               
                final int type = getMessageType(connection) ;
                switch(type)
                {
                case Types.BLOB:
                    final byte[] blobData =  serializedMessage.toString().getBytes() ;
                    final ByteArrayInputStream bais = new ByteArrayInputStream(blobData) ;
                    insertStatement.setBinaryStream(2, bais, blobData.length) ;
                    break ;
                case Types.BINARY:
                case Types.VARBINARY:
                case Types.LONGVARBINARY:
                    final byte[] data = serializedMessage.toString().getBytes() ;
                    insertStatement.setBytes(2, data) ;
                    break ;
                case Types.CLOB:
                    final String clobData = serializedMessage.toString() ;
                    final StringReader clobReader = new StringReader(clobData) ;
                    insertStatement.setCharacterStream(2, clobReader, clobData.length()) ;
                    break ;
                case Types.CHAR:
                case Types.VARCHAR:
                case Types.LONGVARCHAR:
                    insertStatement.setString(2, serializedMessage.toString()) ;
                    break ;
                default:
                    insertStatement.setObject(2, serializedMessage);
                }
                insertStatement.setString(3, State.Pending.getColumnValue());
                insertStatement.setLong(4, System.currentTimeMillis());

                insertStatement.executeUpdate();
            } finally {
                insertStatement.close();
            }

            if (!transactional) {
                connection.commit();
            }

            return true;
        }
        catch (SQLException e)
        {
            try
            {
                if (!transactional) {
                    connection.rollback();
                }
            }
            catch (Exception roll)
            {
                _logger.debug(roll);
            }

            _logger.debug("SQL exception during deliver", e);
            throw new CourierTransportException(e);
        } finally {
            try {
                if (!transactional) {
                    connection.close();
                }
            } catch (SQLException e) {
                _logger.error("Exception while closing DataSource connection.", e);
            }
        }
  }

    public Message pickup(long millis) throws CourierException, CourierTimeoutException
  {
    Message result = null;
    long limit = System.currentTimeMillis()
        + ((millis < 100) ? 100 : millis);

        do
        {
            boolean transactional = isTransactional();
            MessagePickupProspect pickupProspect;

            try {
                pickupProspect = getPickupProspect(transactional);
            } catch (Exception e) {
                _logger.warn("Exception while attempting to lookup message pickup prospect.", e);
                return null;
            }

            if(pickupProspect != null) {
                Connection connection = jdbcFactory.createConnection(transactional);
                try {
                    result = tryToPickup(pickupProspect, connection);

                    // We've successfully picked up a message, so we can commit on a
                    // non-transacted connection...
                    if (!transactional) {
                        connection.commit();
                    }

                    if (result != null) {
                        if(transactional && monitoringRetryCount && pickupProspect.sendToDQL) {
                            // Not going to deliver this message to the action pipeline
                            // because it has already failed (been retried) a number of
                            // time.  Sending to the DLQ...
                            deliverToDLQ(result);
                            return null;
                        } else {
                            return result;
                        }
                    }
                } catch (FaultMessageException e) {
                    // The picked up message was a fault, generating this exception
                    // in Factory.createExceptionFromFault.  Just rethrow...
                    throw e;
                } catch (Exception e) {
                    _logger.warn("Exception during pickup", e);
                    if (!transactional) {
                        try {
                            connection.rollback();
                        } catch (SQLException e1) {
                            _logger.warn("SQL Exception during rollback", e);
                        }
                    }
                    throw new CourierTransportException(e);
                } finally {
                    try {
                        connection.close();
                    } catch (SQLException e) {
                        _logger.warn("Error closing DataSource Connection.", e);
                    }
                }

                try {
                    long lSleep = limit - System.currentTimeMillis();
                    if (_pollLatency < lSleep)
                        lSleep = _pollLatency;
                    if (lSleep > 0)
                        Thread.sleep(lSleep);
                }
                catch (InterruptedException e) {
                    return null;
                }
            }
        } while (System.currentTimeMillis() <= limit);

        return null;
    }

    private MessagePickupProspect getPickupProspect(boolean transactional) throws TransactionStrategyException, CourierServiceBindException, SQLException, CourierTransportException {
        MessagePickupProspect prospect = null;

        // Attempt to read a prospect's details from the DB...
        Connection connection = jdbcFactory.createConnection(transactional);
        try {
            PreparedStatement listStatement = jdbcFactory.createListStatement(connection);
            try {
                ResultSet resultSet = listStatement.executeQuery();
                try {
                    if (resultSet.next()) {
                        prospect = new MessagePickupProspect();
                        prospect.messageId = resultSet.getString(1);
                        prospect.timestamp = resultSet.getLong(2);
                        prospect.status = resultSet.getString(3);

                        if (transactional && monitoringRetryCount) {
                            prospect.retryCount = resultSet.getInt(4);
                        }
                    }
                } finally {
                    resultSet.close();
                }
            } finally {
                listStatement.close();
            }
        } finally {
            connection.close();
        }

        // If we read a prospect...
        if(prospect != null && transactional && monitoringRetryCount) {
            if(prospect.retryCount >= retryLimit) {
                prospect.sendToDQL = true;
            }
            prospect = updateRetryCount(prospect);
        }

        return prospect;
    }

    private Message tryToPickup(MessagePickupProspect prospect, Connection connection) throws CourierException, SQLException
  {
        PreparedStatement selectPicukupMessageStatement = jdbcFactory.createSelectPickupMessage(connection);

        try {
            selectPicukupMessageStatement.setString(1, prospect.messageId);
            selectPicukupMessageStatement.setString(2, prospect.status);
            selectPicukupMessageStatement.setLong(3, prospect.timestamp);

            ResultSet resultSet = selectPicukupMessageStatement.executeQuery();
            try
            {
                if (resultSet.next())
                {
                    Message result = null;

                    try
                    {
                        final Serializable value ;
                        final int type = getMessageType(resultSet) ;
                        switch (type)
                        {
                        case Types.BLOB:
                            final Blob blob = resultSet.getBlob(1) ;
                            final byte[] blobData = ((blob != null) ? StreamUtils.readStream(blob.getBinaryStream()) : null);

                            if (blobData != null)
                                value = new String(blobData) ;
                            else
                                value = "";
                            break ;
                        case Types.BINARY:
                        case Types.VARBINARY:
                        case Types.LONGVARBINARY:
                            final byte[] binaryData = StreamUtils.readStream(resultSet.getBinaryStream(1)) ;
                            value = new String(binaryData) ;
                            break ;
                        case Types.CLOB:
                            final Clob clob = resultSet.getClob(1) ;

                            if (clob != null)
                                value = StreamUtils.readReader(clob.getCharacterStream());
                            else
                                value = "";
                            break ;
                        case Types.CHAR:
                        case Types.VARCHAR:
                        case Types.LONGVARCHAR:
                            value = resultSet.getString(1) ;
                            break ;
                        default:
                            value = (Serializable) resultSet.getObject(1);
                            break ;
                        }
                        result = Util.deserialize(value);
                    }
                    catch (IOException e) {} // ignore
                    catch (SAXException e) {} // ignore
                    catch (ParserConfigurationException e) {} // ignore
                    finally {
                        if (result == null && deleteOnError) {
                            deleteMsg(prospect.messageId, connection);
                        } else if (result != null && deleteOnSuccess) {
                            deleteMsg(prospect.messageId, connection);
                        } else if(result == null) {
                            changeStatus(prospect.messageId, State.Error, connection);
                        } else {
                            changeStatus(prospect.messageId, State.Done, connection);
                        }
                    }

                    return result;
                }
            }
            finally
            {
                try
                {
                    resultSet.close();
                } catch (final Exception ex) {
                    _logger.warn("Could not close ResultSet.", ex);
                }
            }
        } finally {
            selectPicukupMessageStatement.close();
        }

        return null;
  }

    private MessagePickupProspect updateRetryCount(MessagePickupProspect prospect) throws SQLException, CourierServiceBindException, CourierTransportException, TransactionStrategyException {
        TransactionStrategy txStrategy = TransactionStrategy.getTransactionStrategy(true);
        Object suspendedTX;

        suspendedTX = txStrategy.suspend();
        try {
            txStrategy.begin();

            try {
                Connection connection = jdbcFactory.createConnection(true);
                boolean updated = false;

                try {
                    PreparedStatement updateStatement = jdbcFactory.createUpdateRetryCountStatement(connection);

                    try {
                        long readTimestamp = prospect.timestamp;
                        long updateTimeout = System.currentTimeMillis();

                        if(prospect.sendToDQL) {
                            // Mark status as error...
                            prospect.status = SqlTableCourier.State.Error.getColumnValue();
                        }

                        updateStatement.setInt(1, prospect.retryCount + 1);
                        updateStatement.setString(2, prospect.status);
                        updateStatement.setLong(3, updateTimeout);
                        updateStatement.setString(4, prospect.messageId);
                        updateStatement.setLong(5, readTimestamp);

                        // Update the timestamp as the pickup will need this...
                        prospect.timestamp = updateTimeout;

                        updated = (updateStatement.executeUpdate() == 1);
                    } finally {
                        updateStatement.close();
                    }
                } finally {
                    connection.close();
                }
                txStrategy.terminate();

                // Only return the prospec if the retry count was successfully updated
                // and commited...
                if(updated) {
                    return prospect;
                }
            } catch (Exception e){
                txStrategy.rollbackOnly();
                txStrategy.terminate();
                _logger.debug("Error updating message retry count: " + e.getMessage());
            }
        } finally {
            txStrategy.resume(suspendedTX);
        }

        return null;
    }

    private void deleteMsg(String messageId, Connection connection) throws SQLException
  {
        PreparedStatement statement = jdbcFactory.createDeleteStatement(connection);

        try {
            statement.setString(1, messageId);
            statement.executeUpdate();
        }   finally {
            statement.close();
        }
    }

    private void changeStatus(String messageId, State to, Connection connection) throws SQLException
  {
        PreparedStatement statement = jdbcFactory.createUpdateStatusStatement(connection);

        try {
            statement.setString(1, to.getColumnValue());
            statement.setString(2, messageId);
            statement.executeUpdate();
        } finally {
            statement.close();
        }
    }

    public static enum State
  {
    Pending, WorkInProgress, Done, Error;

        public String getColumnValue()
    {
      return toString().substring(0, 1);
    }

    }

    public void setPollLatency(Long millis)
  {
    if (millis <= 200)
      _logger.warn("Poll latency must be >= 200 milliseconds - Keeping old value of "+_pollLatency);
    else
      _pollLatency = millis;
  }

    private boolean isTransactional() throws CourierException {
        boolean transactional;
        try
        {
            TransactionStrategy txStrategy = TransactionStrategy.getTransactionStrategy(true);
            Object txHandle = ((txStrategy == null) ? null : txStrategy.getTransaction());
            boolean isActive = ((txStrategy == null) ? false : txStrategy.isActive());

            transactional = (txHandle != null);

            /*
            * Make sure the current transaction is still active! If we
            * have previously slept, then the timeout may be longer than that
            * associated with the transaction.
            */

            if (transactional && !isActive)
            {
                throw new CourierException("Associated transaction is no longer active!");
            }
        }
        catch (TransactionStrategyException ex)
        {
            throw new CourierException(ex);
        }
        return transactional;
    }
   
    private synchronized int getMessageType(final Connection connection)
        throws SQLException
    {
        if (messageType != Types.OTHER)
        {
            return messageType ;
        }
       
        final PreparedStatement ps = jdbcFactory.createSelect4UpdateStatement(connection) ;
        try
        {
                ps.setString(1, "");
                ps.setString(2, State.Pending.getColumnValue());

                final ResultSet resultSet = ps.executeQuery();
                try
                {
                    return getMessageType(resultSet) ;
                }
                finally
                {
                    try
                    {
                        resultSet.close() ;
                    }
                    catch (final Throwable th) {} // ignore
                }
        }
        finally
        {
            try
            {
                ps.close() ;
            }
            catch (final Throwable th) {} // ignore
        }
    }
   
    private synchronized int getMessageType(final ResultSet resultSet)
        throws SQLException
    {
        if (messageType == Types.OTHER)
        {
            final ResultSetMetaData metaData = resultSet.getMetaData() ;
            messageType = metaData.getColumnType(1) ;
        }
        return messageType ;
    }

    /**
     * Deliver a message to the Dead Letter Channel Service.
     *
     * @throws org.jboss.soa.esb.listeners.message.MessageDeliverException Message delivery failure.
     */
    protected void deliverToDLQ(Message message) throws MessageDeliverException {
        if (!"true".equalsIgnoreCase(Configuration.getRedeliveryDlsOn())) {
            _logger.debug("org.jboss.soa.esb.dls.redeliver is turned off");
        } else {
           synchronized (ServiceInvoker.dlqService) {
             if (dlQueueInvoker == null) {
                        dlQueueInvoker = new ServiceInvoker(ServiceInvoker.dlqService);
               }
            }
            dlQueueInvoker.deliverAsync(message);
        }
    }

    private static class MessagePickupProspect {
        private String messageId;
        private String status;
        private long timestamp;
        public int retryCount;
        private boolean sendToDQL;
    }
}
TOP

Related Classes of org.jboss.internal.soa.esb.couriers.SqlTableCourier$MessagePickupProspect

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.