How do I issue the close() method within an onMessage()
method call and what are the semantics of the close()
method?
If you wish to issue the close() method within an
onMessage() method call, the system administrator must
select the Allow Close In OnMessage check box when
configuring the connection factory. For more
information, see JMS Connection Factories in the
Administration Console Online Help. If this check box is
not selected and you issue the close() method within an
onMessage() method call, the call will hang.
The close() method performs the following steps to
execute an orderly shutdown:
* Terminates the receipt of all pending messages.
Applications may return a message or null if a message
was not available at the time of the close.
* Waits until all message listeners that are currently
processing messages have completed (except for the
message listener from which the close() method is being
called).
* Rolls back in-process transactions on its transacted
sessions (unless such transactions are part of an
external JTA user transaction).
* Does not force an acknowledge of client-acknowledged
sessions. By not forcing an acknowledge, no messages are
lost for queues and durable subscriptions that require
reliable processing.
When you close a connection, all associated objects are
also closed. You can continue to use the message objects
created or received via the connection, except the
received message's acknowledge() method. Closing a
closed connection has no effect.
Note: Attempting to acknowledge a received message from
a closed connection's session throws an
IllegalStateException.
When you close a session, all associated producers and
consumers are also closed.
For more information about the impact of the close()
method for each object, see the appropriate javax.jms
javadoc.
How do I publish an XML message?
Follow these steps:
1. Generate XML from the DOM document tree.
2. Serialize the generated DOM document to a
StringWriter.
3. Call toString on the StringWriter and pass it into
message.setText.
4. Publish the message.
A client wants to preserve the reference to the EJBhome
object of an enterprise bean instance and use it later.
Which of the following can be serialized for this
purpose ?
a. home
b. Handle
c. homeHandle
d. EJBhomeHandle
e. homeObject
Choice C is correct. Once a client has obtained the
EJBhome object for an EJB instance, it can create a
reference to the home object by calling gethomeHandle().
gethomeHandle() returns a homeHandle object, which can
be used to obtain the home interface to the same EJB
instance at a later time.
A client can pass the homeHandle object as arguments to
another client, and the receiving client can use the
handle to obtain a reference to the same EJBhome object.
Clients can also serialize the homeHandle and store it
in a file for later use. The homeHandle interface has
only one method getEJBhome(), which returns the EJBhome
reference.
Is it possible to send or receive a message from within
a message listener?
Yes. You can send to or receive from any queue or topic
from within in a message listener.
If it's not an MDB, you can use the same Connection or
Session that the onMessage() is part of to do this. When
you create your message listener, you pass in a session
in your constructor. Then you have access to the session
in your onMessage method and you would be able to make
synchronous, not asynchronous, calls from within the
onMessage method. Do not use another Session that is
servicing another onMessage() because that would
multi-thread that Session and Sessions don't support
multi-threading.
When things are done non-transactionally, there can be
duplicates or lost messages (assuming your onMessage()
code is attempting to forward messages):
1. If you call acknowledge after the publish() and the
acknowledge fails for whatever reason (network/server
failure), then you will see the message again and will
end up publishing twice (possible duplicate semantics).
You can try to keep track of sequence numbers to detect
duplicates but this is not easy.
2. If you call acknowledge before the publish(), you get
at-most-once semantics. If the publish() fails, you
don't know if the failure occurred before or after the
message reached the server.
If you want exactly once, transactional semantics using
onMessage, you must use transactional MDBs. The
onMessage() for a transactional MDB starts the
transaction, includes the WebLogic Server JMS message
received within that transaction and the publish() would
also be in the same transaction. The following code
sends a response to each message that it receives. It
creates the connection, etc. in the ejbCreate method so
that it doesn't need to create it every time onMessage
is called. The QueueSender is anonymous (null Queue)
since we don't know to whom we will have to reply. The
ejbRemove method cleans up by closing the connection.
This same approach can be used to create a receiver,
subscriber or publisher.
import javax.ejb.CreateException;
import javax.ejb.EJBContext;
import javax.naming.*;
import javax.naming.directory.*;
import java.util.Hashtable;
import javax.ejb.MessageDrivenBean;
import javax.ejb.MessageDrivenContext;
import javax.jms.*;
public class MDB
implements MessageDrivenBean, MessageListener {
public static final String WLSqcf =
"javax.jms.QueueConnectionFactory";
public static final String WLSqname =
"jms.queue.TestQueue1";
public static final String WLSurl =
"t3://localhost:7001";
public static final String WLSJNDIfactory =
"weblogic.jndi.WLInitialContextFactory";
private MessageDrivenContext context;
private QueueSession session;
private QueueConnection connection = null;
private QueueConnectionFactory factory;
private InitialContext ctx;
private QueueSender QueueSender;
// Required - public constructor with no argument
public MDB() {}
// Required - ejbActivate
public void ejbActivate() {}
// Required - ejbRemove
public void ejbRemove() {
context = null;
if (connection != null) {
try {
connection.close();
} catch(Exception e) {}
connection = null;
}
}
// Required - ejbPassivate
public void ejbPassivate() {}
public void setMessageDrivenContext(
MessageDrivenContext mycontext) {
context = mycontext;
}
// Required - ejbCreate() with no arguments
public void ejbCreate () throws CreateException {
try {
// Get the initial context
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, WLSJNDIfactory);
env.put(Context.PROVIDER_URL, WLSurl);
env.put(Context.REFERRAL, "throw");
ctx = new InitialContext(env);
factory = (QueueConnectionFactory)ctx.lookup(WLSqcf);
// Create a QueueConnection, QueueSession, QueueSender
connection = factory.createQueueConnection();
session = connection.createQueueSession(false,
Session.AUTO_ACKNOWLEDGE);
queueSender = session.createSender(null);
connection.start();
} catch (Exception e) {
throw(new CreateException(e.toString()));
}
}
// Implementation of MessageListener
// Throws no exceptions
public void onMessage(Message msg) {
try {
System.out.println("MDB: " +
((TextMessage)msg).getText());
msg.clearBody();
((TextMessage)msg).setText("reply message");
queueSender.send((Queue)msg.getJMSReplyTo(), msg);
}
catch(Exception e) { // Catch any exception
e.printStackTrace();
}
}
}
This approach creates a connection per EJB/MDB instance,
so you might want to create a producer pool that is
shared by the EJB instances. This is done by writing a
class that populates a static pool with producers (see
the next question for a sample producer pool). The
onMessage call grabs a producer when needed. Since
Sessions must be single threaded, make sure there is
only one producer per session within the producer pool.
Page Numbers :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18