使用EJB 3发送JMS消息时的事务划分

时间:2013-09-02 10:47:59

标签: logging ejb jms ejb-3.0 message-driven-bean

在我的Java EE应用程序中,我已经将异步数据库记录器实现为MDB,它通过JMS接收XML消息并将它们写入数据库。

在另一个MDB中,我创建了一条日志消息,并使用以下代码将它们发送到记录器MDB的输入队列:

    public static void log(String correlId, String message, String data) throws Exception{

            SysLogEntry sysLogEntry = new SysLogEntry();
            sysLogEntry.setCorrelId(correlId);
            sysLogEntry.setDatetimeCreate(new Date());
            sysLogEntry.setMessage(message);
            sysLogEntry.setData(data);

            ConnectionFactory jmsConnectionFactory = (ConnectionFactory)initialContext.lookup(JMS_CONNECTION_FACTORY_JNDI_NAME);
            Destination logEventDestination = (Destination) initialContext.lookup(LOG_EVENT_DESTINATION_JNDI_NAME);

            JmsUtils.sendMsgToDestination(JaxbUtils.toString(sysLogEntry, jaxbContext), jmsConnectionFactory, logEventDestination, false, Session.AUTO_ACKNOWLEDGE);

        }

public static void sendMsgToDestination(String payload, ConnectionFactory connFactory, Destination destination, boolean sessionTransacted, int acknowledgeMode) throws JMSException{
        if(payload == null)
            throw new IllegalArgumentException("Message payload is null");
        if(connFactory == null)
            throw new IllegalArgumentException("Connection factory is null");
        if(destination == null)
            throw new IllegalArgumentException("Message destination is null");

        Connection connection = null;
        try{
            connection = connFactory.createConnection();
            Session session = connection.createSession(sessionTransacted, acknowledgeMode);
            MessageProducer messageProducer = session.createProducer(destination);
            TextMessage textMessage = session.createTextMessage();
            textMessage.setText(payload);
            messageProducer.send(textMessage);
        } finally {
            if(connection != null){
                try{
                    connection.close();
                } catch (JMSException ignore){

                }
            }
        }
    }

其中

  • SysLogEntry是一个带有JAXB注释的类,我用它来序列化
  • JMS_CONNECTION_FACTORY_JNDI_NAME是XA连接工厂的JNDI名称

但是每次回滚创建日志消息的事务时,日志消息都不会被放入记录器输入队列。

有人能告诉我我的代码有什么问题吗?我曾考虑使用一个单独的无状态会话bean来启动一个新的事务来生成日志消息,但这种方法有一个缺点:在EJB内部我可以很容易地让容器注入logger bean,但我也有非EJB我希望在其中进行异步日志记录的对象。

我的应用服务器是Weblogic 10.3.3

3 个答案:

答案 0 :(得分:1)

您可以将包含静态日志方法的类的静态成员分配给无状态EJB代理的实例。代理本身只是将调用路由到实例池中的空闲bean,因此可以静态共享。

但是......为了逃避当前的交易而开始新的交易听起来很浪费。特别是如果经常登录此异步记录器,您可能希望采用不同的方式。

使用带有几个线程的简单Java SE执行器池,只需向其提交工作(Runnable)。在此runnable中,您仍然可以发送JMS消息,保持现有代码不变。当来自池中的线程接收到工作时,事务上下文将丢失,但这正是您首先需要的。

答案 1 :(得分:1)

如果出现系统异常(或sessionContext.setRollbackOnly),则容器将回滚全局事务(如果使用容器管理的事务)。 这意味着将回滚在全局事务中登记的XA感知资源上的任何操作。

这包括您要发送给记录器的待处理邮件。这个被删除,因为事务被回滚。


在您的情况下,如果您使用非XA 连接工厂,那就足够了。这样就可以立即发送消息(如果记录器有帮助的话)。因此,仅在跨越JMS发送任务的本地事务中发送运行。

对于非EJB对象:您可以使用非XA连接工厂将发送功能提取到普通Java类(=非EJB)中,并从EJB和普通类中使用它。

答案 2 :(得分:0)

我会创建一个单独的BEAN来记录Message并且会 注释sendMessage以启动自己的独立事务:

@Stateless
public class SenderBean  {

@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public sendMsgToDestination(String payload, ConnectionFactory connFactory, Destination destination, boolean sessionTransacted, int acknowledgeMode) throws JMSException{
        ...
        Connection connection = null;
        try(
           connection = connFactory.createConnection();
           Session session = connection.createSession(sessionTransacted, acknowledgeMode);
        ){
            MessageProducer messageProducer = session.createProducer(destination);
            TextMessage textMessage = session.createTextMessage();
            textMessage.setText(payload);
            messageProducer.send(textMessage);
        }
    }

然后我会使用这个BEAN进行日志记录,即使你的调用者事务被回滚,它也会将消息写入队列