2011-01-06 5 views
4

JMS와 관련된 문제점과 완전히 이해하지 못한 트랜잭션이 있습니다. 내 응용 프로그램에는 하나의 JDBC 자원과 두 개의 JMS 대기열이 있습니다. 대기열의 메시지 생성자는 동일한 jms 세션 객체의 동일한 stateless 세션 bean에서 작성됩니다. 대기열을 사용하는 방법은 다음과 같습니다. entitiy가 작성되고 해당 id가 JMS 메시지의 특성으로 저장되어 대기열로 보내집니다. 엔티티 생성과 대기열에 대한 제출은 동일한 트랜잭션에서 발생합니다. 내 메시지 구동 Bean은 JMS 메시지의 ID를 통해 데이터베이스에서 엔티티를 검색하고 처리합니다.JMS 트랜잭션 문제

이 코드는 다음과 거의 같습니다

엔티티가의 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(); 
} 

, 다음과 같은 예외가있는 다음 MDB의 onMessage 메소드는

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(); 
} 

로그 파일 :

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 

정확히 예외는 무엇을 의미합니까 : 나는 JPA 2.0 (및 아파치 더비)와 글래스 피시 버전 3.1-B35를 사용하고있어

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

표준 JMS 공급자는 GF와 함께 제공됩니다. 트랜잭션 설정은 JMS 자원 (EMBEDDED 모드)뿐만 아니라 기본값입니다. 트랜잭션은 컨테이너 관리됩니다. 어떤 아이디어가 잘못 되었습니까? 예외는 꽤 자주 발생합니다.

답변

8

나는 이것에 관해 오라클에 연락했다 : 그들은 Stateless Session Bean에 JMS 연결을 캐시하지 않을 것을 권장한다. 대신 연결은 주문형 (on-demand) 요청을 받아 즉시 릴리스되어야합니다. 연결 핸들은 실제 물리적 연결을 감싸는 얇은 래퍼 (Java EE Connector Architecture Specification의 6.4.3 절 참조)이기 때문에이를 수행 할 때 성능 오버 헤드가 없습니다. glassfish 메일 링리스트에있는 this thread을 참고하면 비슷한 문제가 발생할 수 있습니다.

+0

링크가 작동하지 않습니다. – dazito

3

문제는 트랜잭션 관리에 있습니다. 일단 JMS 목적지에 메시지를 제출하면 MDB는 자신의 트랜잭션에서 그 메시지를 선택합니다. 이것은 메시지를 제출 한 세션 빈이 아직 트랜잭션을 커밋하지 않았기 때문에 문제가됩니다. 이제 MDB가 존재하지 않는 엔티티를로드하려고합니다.

제출 코드를 자체 클래스로 분리하고 빈 관리 트랜잭션을 사용하십시오.

+0

이 경우였다 경우는, NullPointerException가 슬로우 될 것이다 MDB가 엔티티를 찾을 수 없기 때문입니다. JMS는 트랜잭션이 커밋 된 후에 보내지며 실제로 messageProducer.send()를 호출 한 후에는 보내지지 않습니다. – Theo

+1

나는 네가하는 말을 이해하지만 그게 어떻게 행동하는지 생각하지 않는다. 우리도 비슷한 문제가 있었고 거래와 관련이있었습니다. – Preston

+0

"STARTED (1) 상태의 트랜잭션에서 PREPARE_TRANSACTION (56) (XAFlag = null) 작업을 수행 할 수 없음"오류는 첫 번째 트랜잭션이 여러 트랜잭션이 동일한 세션에서 트랜잭션을 사용하려고 시도했음을 * 또는 * 나타냅니다. 하지 sync'd. 예상 된 널 포인터를 말할 수는 없지만 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()); 
     } 
    } 

}