在我的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
答案 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进行日志记录,即使你的调用者事务被回滚,它也会将消息写入队列