2

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

在另一个 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 中,我可以轻松地让容器注入记录器 bean,但我也有非 EJB我想在其中进行异步日志记录的对象。

我的应用服务器是 Weblogic 10.3.3

4

3 回答 3

1

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

在您的情况下,这包括您要发送给记录器的待处理消息。这个被删除了,因为事务被回滚了。


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

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

于 2013-09-02T18:47:11.220 回答
1

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

但是……为了逃避当前的交易而开始新的交易听起来很浪费。特别是如果频繁地记录到这个异步记录器,您可能希望以不同的方式进行。

使用带有几个线程的简单 Java SE 执行器池,然后将工作提交给它(Runnable)。在这个可运行文件中,您仍然可以发送 JMS 消息并保持现有代码不变。当池中的线程接手工作时,事务上下文会丢失,但这正是您首先需要的。

于 2013-09-02T17:41:23.037 回答
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 进行日志记录,即使您的调用方事务回滚,它也会将消息写入队列

于 2017-02-07T10:13:57.993 回答