JMS, MDB, EJB problem

Hi everybody,

I have a question and want to get help from you.

one stateless bean and one mdb bean on the same container.

Expected stateless bean send message to the queue and mdb get respond by onMessage call simultaneous (ie. one prefer situation send one message and retreive one message).

However, the output is stateless bean send message, the mdb will not get message until the stateless bean close the session.

Thanks all you help first.

[481 byte] By [TaurusLeea] at [2007-11-27 7:52:56]
# 1
I hope that i have understood your question correctly, but i guess, you need to commit the message on to queue so that the MDB can pick it up.If this is not answering your question, please post more details.
jitenba at 2007-7-12 19:34:11 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...
# 2

Hi jitenb,

Thanks you for your reply.

I try to add commit() on the code. But "Cannot perform commit or rollback on an XASession" error and halt.

Attached cllient., producer and consumer code for your reference.

client.java

import javax.ejb.EJB;

import javax.ejb.NoSuchEJBException;

public class client{

@EJB(name="ProducerRemote")

private static ProducerRemote producer;

public static void main(String[] args){

client clt=new client();

clt.run();

System.exit(0);

}

public void run(){

try{

producer.run(100);

}catch(Exception exp){

exp.printStackTrace();

}

}

}

ProducerRemote.java

import javax.ejb.Remote;

@Remote

public interface ProducerRemote{

public void run(int numberoftry);

}

ProducerBeam.java

import java.util.logging.Logger;

import javax.ejb.*;

import javax.jms.*;

import javax.annotation.*;

@Stateless

@Remote({ProducerRemote.class})

public class ProducerBean implements ProducerRemote{

static final Logger logger=Logger.getLogger("ProducerBean");

Connection connection=null;

@Resource(mappedName="jms/ConnectionFactory")

private ConnectionFactory connectionFactory;

@Resource

private SessionContext sc;

@Resource(mappedName="jms/Topic")

private Topic topic;

public ProducerBean(){}

@PostConstruct

public void makeConnection(){

try{

connection=connectionFactory.createConnection();

}catch(Throwable thw){

logger.severe("ProducerBean.makeConnection:Exception:"+thw.toString());

}

}

public void run(int numberoftry){

Session session=null;

MessageProducer producer=null;

TextMessage msg=null;

int counter=0;

try{

session=connection.createSession(true,0);

producer=session.createProducer(topic);

msg=session.createTextMessage();

while(counter<numberoftry){

msg.setText("Item:"+counter);

logger.info("PRODUCER: Setting message text to :"+msg.getText());

producer.send(msg);

session.commit();

counter++;

}

}catch(Throwable thw){

logger.severe("ProducerBean.run:Excepton:"+thw.toString());

sc.setRollbackOnly();

}finally{

if(session!=null){

try{

session.close();

}catch(JMSException jmsexp){

}

}

}

}

@PreDestroy

public void endConnection() throws RuntimeException{

if(connection!=null){

try{

connection.close();

}catch(Exception exp){

exp.printStackTrace();

}

}

}

}

ConsumerBean.java

import javax.ejb.*;

import javax.jms.*;

import javax.annotation.Resource;

import java.util.logging.Logger;

@MessageDriven(mappedName="jms/Topic",activationConfig={

@ActivationConfigProperty(propertyName="subscriptionDurability",propertyValue="Durable"),

@ActivationConfigProperty(propertyName="clientId",propertyValue="MyID"),

@ActivationConfigProperty(propertyName="subscriptionName",propertyValue="MySub")})

public class ConsumerBean implements MessageListener{

static final Logger logger=Logger.getLogger("ConsumerBean");

@Resource

public MessageDrivenContext mdc;

public ConsumerBean(){}

public void onMessage(Message inMessage){

TextMessage msg=null;

try{

if(inMessage instanceof TextMessage){

msg=(TextMessage)inMessage;

logger.info("CONSUMER BEAN: Message received: "+msg.getText());

}else{

logger.warning("Message of wrong type: "+inMessage.getClass().getName());

}

}catch(JMSException jmsexp){

logger.severe("ConsumerBean.onMessage: JMSExcepton: "+jmsexp.toString());

jmsexp.printStackTrace();

mdc.setRollbackOnly();

}catch(Throwable thrw){

logger.severe("ConsumerBean.onMessage:Exception:"+thrw.toString());

thrw.printStackTrace();

}

}

}>

TaurusLeea at 2007-7-12 19:34:11 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...
# 3

When JMS resources are used within a global transaction there is special transactional behavior

associated with message production and consumption.When the code calls send() the message

is not actually sent until the transaction commits.Because EJB 3.0 business methods run in a

container-managed transaction by default, that means the messages produced in the session bean

run() method will not be sent to the MDB until the run() method completes.

If you want to force the message send to happen before the run() method completes, you'll need

to use bean-managed transactions or not use transactions at all. If you issue the send outside the

context of a transaction, it will be sent to the destination immediately.

--ken

ksaksa at 2007-7-12 19:34:11 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...
# 4

Hi Ken,

Thank you your reply.

So there are two way to do.

1. not use transaction at all.

but I read the document said "When you create a session in an enterprise bean, the container ignores the arguments you specify, because it manages all transactional properties for enterprise beans.".

this means even change session=connection.createSession(false,...) will not help?

2. use bean-managed transactions.

where can I set it?

TaurusLeea at 2007-7-12 19:34:11 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...
# 5

> 1. not use transaction at all.

> but I read the document said "When you create a

> session in an enterprise bean, the container ignores

> the arguments you specify, because it manages all

> transactional properties for enterprise beans.".

> this means even change

> session=connection.createSession(false,...) will not

> help?

Right, don't change that part of the code. What you need to change is the

EJB transaction properties.By default every Session/MDB has

container-managed transactions and transaction attribute REQUIRED.

You can change that by using the TransactionManagement and TransactionAttribute

annotations.One way is to just mark the specific business method with

@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)

That means there are no global transactions used in the method.

> . use bean-managed transactions.

>where can I set it?

To use bean-managed transactions, add the following to the class-level of the bean :

@TransactionManagement(TransactionManagementType.BEAN)

Then you can use the UserTransaction object to begin,commit, and rollback transactions

within the method.You can acquire UserTransaction by injecting it :

@Resource private UserTransaction ut;

ksaksa at 2007-7-12 19:34:11 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...
# 6
Hi,I just add TransactionAttributeType.NOT_SUPPORTED.But return SessionContext not found on sc.setRollbackOnly error.After that, I remove sc.setRollbackOnly. Another error say "Unknown transaction ID -1.
TaurusLeea at 2007-7-12 19:34:11 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...
# 7

setRollbackOnly is only used when you have a container-managed transaction. If your method is

configured with a tx attribute of NOT_SUPPORTED, the code should not be called setRollbackOnly.

Also make sure to remove the JMS session.commit() call.

If you continue to see errors, please post the entire stack trace.

ksaksa at 2007-7-12 19:34:11 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...