2011-01-06 1 views
4

У меня проблема с JMS и транзакциями, которые я не совсем понимаю. Мое приложение имеет один ресурс JDBC и две очереди JMS. Производители сообщений для очередей создаются в одном и том же сеансовом компоненте без состояния из одного и того же объекта сеанса jms. Я использую очереди следующим образом: Создается entitiy, и его идентификатор сохраняется как свойство в сообщении JMS и отправляется в очередь. Создание объекта и представление в очередь происходят в одной транзакции. Мое сообщение управляемое bean-а затем извлекает сущность из базы данных через ID в сообщении JMS и обрабатывает ее.JMS сделка выпуск

Код выглядит примерно так:

public long doSomething(String message) { 
     SomeObject obj = new SomeObject(message); 
     entityManager.persist(obj) 

     // submit to JMS queue 
     try { 
      Message jmsMessage = session.createMessage(); 
      jmsMessage.setLongProperty("id", obj.getId()); 
      messageProducer.send(jmsMessage); 
     } catch (JMSException ex) { 
      Logger.getLogger(NotificationQueue.class.getName()).log(Level.SEVERE, null, ex); 
     } 

     return obj.getId(); 
} 

метод OnMessage ПДБ в:

public void onMessage(Message message) { 
     Long id; 
     try { 
      id = message.getLongProperty("id"); 
     } catch (Exception ex) { 
      Logger.getLogger(AlertMessageListener.class.getName()).log(Level.SEVERE, null, ex); 
      throw new EJBException(ex); 
     } 

     SomeObject obj = entityManager.find(SomeObject.class, id); 
     obj.process(); 
} 

Когда объект извлекается из базы данных в методе OnMessage(), следующее исключение составляет файл журнала:

FINE: ENTRY com.test.app.alert.control.AlertMessageListener onMessage 
FINE: ENTRY com.test.app.alert.control.MessageDao find 
FINER: client acquired: 2104888816 
FINER: TX binding to tx mgr, status=STATUS_ACTIVE 
FINER: acquire unit of work: 1368213481 
FINEST: Execute query ReadObjectQuery(name="readObject" referenceClass=Message sql="SELECT ID, DTYPE, MESSAGE, REPORTTIME, SENDER_USERNAME, ALERTSTATE, TIMERHANDLE, CATEGORY_ID, PRIORITY_PRIOLEVEL FROM MESSAGE WHERE (ID = ?)") 
SEVERE: prepareTransaction (XA) on JMSService:jmsdirect failed for connectionId:7979865462417759232 due to Unknown JMSService server error ERROR: com.sun.messaging.jmq.jmsserver.util.BrokerException: Bad transaction state transition. Cannot perform operation PREPARE_TRANSACTION(56) (XAFlag=null) on a transaction in state STARTED(1). 
WARNING: JTS5031: Exception [java.lang.RuntimeException: javax.transaction.xa.XAException] on Resource [prepare] operation. 
SEVERE: rollbackTransaction (XA) on JMSService:jmsdirect failed for connectionId:7979865462417759232:transactionId=7979865462479908608 due to Unknown JMSService server error ERROR: com.sun.messaging.jmq.jmsserver.util.BrokerException: Bad transaction state transition. Cannot perform operation ROLLBACK_TRANSACTION(48) (XAFlag=null) on a transaction in state STARTED(1). 
WARNING: JTS5068: Unexpected error occurred in rollback 
javax.transaction.xa.XAException 
     at com.sun.messaging.jms.ra.DirectXAResource.rollback(DirectXAResource.java:703) 
     at com.sun.jts.jta.TransactionState.rollback(TransactionState.java:193) 
     at com.sun.jts.jtsxa.OTSResourceImpl.rollback(OTSResourceImpl.java:333) 
     at com.sun.jts.CosTransactions.RegisteredResources.distributeRollback(RegisteredResources.java:1063) 
     at com.sun.jts.CosTransactions.TopCoordinator.rollback(TopCoordinator.java:2299) 
     at com.sun.jts.CosTransactions.CoordinatorTerm.commit(CoordinatorTerm.java:420) 
     at com.sun.jts.CosTransactions.TerminatorImpl.commit(TerminatorImpl.java:250) 
     at com.sun.jts.CosTransactions.CurrentImpl.commit(CurrentImpl.java:623) 
     at com.sun.jts.jta.TransactionManagerImpl.commit(TransactionManagerImpl.java:319) 
     at com.sun.enterprise.transaction.jts.JavaEETransactionManagerJTSDelegate.commitDistributedTransaction(JavaEETransactionManagerJTSDelegate.java:173) 
     at com.sun.enterprise.transaction.JavaEETransactionManagerSimplified.commit(JavaEETransactionManagerSimplified.java:873) 
     at com.sun.ejb.containers.BaseContainer.completeNewTx(BaseContainer.java:5115) 
     at com.sun.ejb.containers.BaseContainer.postInvokeTx(BaseContainer.java:4880) 
     at com.sun.ejb.containers.MessageBeanContainer.afterMessageDeliveryInternal(MessageBeanContainer.java:1207) 
     at com.sun.ejb.containers.MessageBeanContainer.afterMessageDelivery(MessageBeanContainer.java:1180) 
     at com.sun.ejb.containers.MessageBeanListenerImpl.afterMessageDelivery(MessageBeanListenerImpl.java:86) 
     at com.sun.enterprise.connectors.inbound.MessageEndpointInvocationHandler.invoke(MessageEndpointInvocationHandler.java:143) 
     at $Proxy260.afterDelivery(Unknown Source) 
     at com.sun.messaging.jms.ra.OnMessageRunner.run(OnMessageRunner.java:328) 
     at com.sun.enterprise.connectors.work.OneWork.doWork(OneWork.java:114) 
     at com.sun.corba.ee.impl.orbutil.threadpool.ThreadPoolImpl$WorkerThread.performWork(ThreadPoolImpl.java:496) 
     at com.sun.corba.ee.impl.orbutil.threadpool.ThreadPoolImpl$WorkerThread.run(ThreadPoolImpl.java:537) 
Caused by: com.sun.messaging.jmq.jmsservice.JMSServiceException: rollbackTransaction: rollback transaction failed. Connection ID: 7979865462417759232, Transaction ID: 7979865462479908608, XID: null 
     at com.sun.messaging.jmq.jmsserver.service.imq.IMQDirectService.rollbackTransaction(IMQDirectService.java:1827) 
     at com.sun.messaging.jms.ra.DirectXAResource.rollback(DirectXAResource.java:672) 
     ... 21 more 
Caused by: com.sun.messaging.jmq.jmsserver.util.BrokerException: Bad transaction state transition. Cannot perform operation ROLLBACK_TRANSACTION(48) (XAFlag=null) on a transaction in state STARTED(1). 
     at com.sun.messaging.jmq.jmsserver.data.TransactionState.nextState(TransactionState.java:449) 
     at com.sun.messaging.jmq.jmsserver.data.handlers.TransactionHandler.preRollback(TransactionHandler.java:1586) 
     at com.sun.messaging.jmq.jmsserver.data.protocol.ProtocolImpl.rollbackTransaction(ProtocolImpl.java:777) 
     at com.sun.messaging.jmq.jmsserver.service.imq.IMQDirectService.rollbackTransaction(IMQDirectService.java:1816) 
     ... 22 more 

FINER: TX afterCompletion callback, status=ROLLEDBACK 
FINER: release unit of work 
FINER: client released 
FINEST: Register the existing object [email protected] 
FINER: end unit of work commit 
FINEST: Register the existing object Mailserver 
FINEST: Register the existing object Low 
FINEST: Register the existing object u0 u0 (u0) 
FINEST: Register the existing object Sankt Augustin 
FINE: RETURN com.test.app.alert.control.MessageDao find 
... 
WARNING: javax.ejb.EJBException 
javax.ejb.EJBException: Transaction aborted 
     at com.sun.ejb.containers.BaseContainer.completeNewTx(BaseContainer.java:5121) 
     at com.sun.ejb.containers.BaseContainer.postInvokeTx(BaseContainer.java:4880) 
     at com.sun.ejb.containers.MessageBeanContainer.afterMessageDeliveryInternal(MessageBeanContainer.java:1207) 
     at com.sun.ejb.containers.MessageBeanContainer.afterMessageDelivery(MessageBeanContainer.java:1180) 
     at com.sun.ejb.containers.MessageBeanListenerImpl.afterMessageDelivery(MessageBeanListenerImpl.java:86) 
     at com.sun.enterprise.connectors.inbound.MessageEndpointInvocationHandler.invoke(MessageEndpointInvocationHandler.java:143) 
     at $Proxy260.afterDelivery(Unknown Source) 
     at com.sun.messaging.jms.ra.OnMessageRunner.run(OnMessageRunner.java:328) 
     at com.sun.enterprise.connectors.work.OneWork.doWork(OneWork.java:114) 
     at com.sun.corba.ee.impl.orbutil.threadpool.ThreadPoolImpl$WorkerThread.performWork(ThreadPoolImpl.java:496) 
     at com.sun.corba.ee.impl.orbutil.threadpool.ThreadPoolImpl$WorkerThread.run(ThreadPoolImpl.java:537) 
Caused by: javax.transaction.RollbackException 
     at com.sun.jts.jta.TransactionManagerImpl.commit(TransactionManagerImpl.java:321) 
     at com.sun.enterprise.transaction.jts.JavaEETransactionManagerJTSDelegate.commitDistributedTransaction(JavaEETransactionManagerJTSDelegate.java:173) 
     at com.sun.enterprise.transaction.JavaEETransactionManagerSimplified.commit(JavaEETransactionManagerSimplified.java:873) 
     at com.sun.ejb.containers.BaseContainer.completeNewTx(BaseContainer.java:5115) 
     ... 10 more 

Следующая информация была в файле журнала немного раньше е (после того, как был создан объект):

INFO: DXAR:start():Warning:Received diff Xid for open txnId:switching transactionId: 
DXAR Xid=(GlobalTransactionID=[[email protected], BranchQualifier=[[email protected]) 
DXAR TXid=7979865462479908608 
got Xid=(GlobalTransactionID=[[email protected], BranchQualifier=[[email protected]) 
got TXid=7979865462480472064 

Что именно это исключение в виду:

Bad transaction state transition. Cannot perform operation ROLLBACK_TRANSACTION(48) (XAFlag=null) on a transaction in state STARTED(1). 

Я использую Glassfish v3.1-B35 с JPA 2.0 (и Apache Derby) и стандартный поставщик JMS, поставляемый с GF. Параметры транзакций - по умолчанию, а также ресурсы JMS (режим EMBEDDED). Транзакции управляются контейнерами. Любые идеи, что здесь происходит не так? Исключение случается довольно часто.

ответ

8

Я связался с Oracle по этому поводу: они рекомендуют не кэшировать JMS-соединения в сеансовом компоненте без состояния. Вместо этого соединения должны быть приобретены по требованию и немедленно отправлены. При выполнении этого нет служебных накладных расходов, поскольку дескриптор соединения представляет собой тонкую оболочку вокруг фактического физического соединения (см. Раздел 6.4.3 Спецификации архитектуры соединителя Java EE). Вы также можете обратиться к this thread в список рассылки по стеклянным очкам для аналогичной проблемы.

+0

Ссылка мертва. – dazito

3

Ваша проблема с управлением транзакциями. Когда вы отправляете свое сообщение в адрес JMS, MDB выбирает его в своей собственной транзакции. Это становится проблемой, потому что ваш сеансовый компонент, отправляющий сообщение, еще не совершил транзакции. Теперь MDB пытается загрузить сущность, которая не существует.

Отделите код отправки в свой класс и используйте транзакцию, управляемую компонентом.

+0

Если бы это было так, то NullPointerException будет брошен , поскольку MDB не смог найти объект. JMS отправляется после совершения транзакции, а НЕ после фактического вызова messageProducer.send(). – Theo

+1

Я понимаю, о чем вы говорите, но я не думаю, что так оно и происходит. У нас была аналогичная проблема, и это было связано с транзакциями. – Preston

+0

Ошибка «Невозможно выполнить операцию PREPARE_TRANSACTION (56) (XAFlag = null) в транзакции в состоянии STARTED (1)» означает, что первая транзакция не была выполнена * или *, что несколько потоков пытаются использовать транзакции на одном сеансе и не синхронизирован. Я не могу говорить с ожидаемым нулевым указателем, но Preston прав насчет того, что это связано с транзакциями. –

0

У меня также была эта проблема с JMS, где транзакции сущности были отброшены назад. Мне удалось исправить это с запросом на новую транзакцию, используя:

@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) 

На EJB, который создает сообщения в очередь.

Так что мой дизайн что-то вроде:

продюсер:

@Stateless 
@LocalBean 
// Note here that a new transaction is required for this bean 
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) 
public class ProducerBean { 

    @Resource(mappedName = "jms/theQueue") 
    private Queue theQueue; 

    @Inject 
    private JMSContext jmsContext; 


    private static final Logger logger = LoggerFactory.getLogger(ProducerBean.class); 

    public void event(TheEvent theEvent) { 
     // Place the message in the Queue 
     try { 
      jmsContext.createProducer().send(theQueue, theEvent); 
      logger.info("send event|eventName:{}", theEvent.getEventName()); 
     } catch (Exception ex) { 
      logger.error("Could not send the event|eventName:{}|error:{}", theEvent.getEventName(), ex.getMessage()); 
     } 
    } 
} 

И мой потребитель что-то вроде:

@MessageDriven(name = "TheConsumer", activationConfig = { 
    @ActivationConfigProperty(propertyName = "destinationLookup", propertyValue = "jms/theQueue"), 
    @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"), 
    @ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge") 
}) 
public class TheConsumer implements MessageListener { 

    private static final Logger logger = LoggerFactory.getLogger(TheConsumer.class); 

    @Override 
    public void onMessage(Message message) { 

     try { 
      doSomething(); 

     } catch (JMSException ex) { 
      logger.error("JMSException|could not retrieve the object from the message body - reason: {}", ex.getMessage()); 
     } 
    } 

}