Enterprise JavaAn Architecture for Making Asynchronous EJB Calls Using JMS
- By by Tyler Jewell
- April 18, 2000
Listing 3. SpellCheckBean.java.
1 package asynch;
2
3 import java.rmi.*;
4 import java.util.*;
5 import javax.ejb.*;
6 import javax.naming.*;
7 import javax.jms.*;
8 import java.lang.reflect.*;
9
10 public class SpellCheckBean implements SessionBean {
11 private transient SessionContext context;
12 public Boolean checkSpelling(String word, Destination answer) {
13 // If the destination passed in as a parameter is
14 // not null, then the caller of this method wants
15 // the method called asynchronously.
16 if (answer != null) {
17 // For asynchronous execution, we'll inject a message
18 // to an asynchronous method handler that will call
19 // this method synchronously. After we send the message
20 // we'll immediately return giving the caller the illusion
21 // of asynchronous handling.
22 Hashtable objectsToSend = new Hashtable();
23 objectsToSend.put("ResponseDestination", answer);
24
25 // We need to get a reference to this particular EJB.
26 // Since this reference is going to be sent to a client
27 // over a network connection, we need to send a transportable
28 // reference. Handle instances represent transportable
29 // references to EJBs.
30 try {
31 EJBObject thisEJB = context.getEJBObject();
32 Handle thisHandle = thisEJB.getHandle();
33 objectsToSend.put("EJBToInvoke", thisHandle);
34
35 // VERY IMPORTANT SECTION!!!!
36 // The EJB has to send an instance of Method which contains
37 // the information that the handler will need to invoke
38 // this method. Unfortunately, since Method is not
// serializable
39 // we will have to send the elements individually.
40 //
41 // NOTE: The reflection API does not have any extensions for
42 // dynamically determining the name of the current
43 // method. You must type the name of the method
44 // and the object name of each input parameter on a
// per-method
45 // basis.
46 //
47 // NOTE: Instances of Method are not serializable.
// This means that all of the parameters and the method
48 // name must be sent individually in the
49 // ObjectMessage. We cannot send the
50 // java.reflect.Method instance directly.
51 //
52 // NOTE: To truly have the best asynchronous behavior,
// your EJB needs to be of a stateful nature.
53 // Even though the EJB must be declared to be
54 // stateful so that it can remember the JMS and
55 // JNDI connections, declaring this EJB to
56 // have stateless behavior will allow the handler
57 // to create a new instance of this EJB and to make the
58 // synchronous method invocation on a separate bean.
59 // This is crucial since the handler thread will not lock
60 // up this EJB when it is making its invocation. If this
61 // EJB is locked by a handler, then the client program
62 // will not be able to use it. So, if the EJB does have
63 // stateless behavior, set the "HasStatelessBehavior"
64 // flag to be true.
65 objectsToSend.put("MethodNameToInvoke", "checkSpelling");
66 objectsToSend.put("NumMethodParameters", "2");
67 objectsToSend.put("Param1", word);
68 objectsToSend.put("Param1Class", String.class);
69 objectsToSend.put("Param2", answer);
70 objectsToSend.put("Param2Class", Destination.class);
71 objectsToSend.put("HasStatelessBehavior", new Boolean(true));
72
73 sendAsynchRequestMSG(objectsToSend);
74 } catch (Exception e) {
75 System.out.println("Error with inserting objects into "hashtable. + e);
76 }
77
78 // Immediately return. We are currently in the asynchronous
// block.
79 // We need to immediately return to the client. The client should
80 // ignore this return value because the client is expecting the
// "real"
81 // response to be returned in a JMS message.
82 return new Boolean(false);
83 }
84
85 // If execution reaches this point, it means that this method was
86 // called synchronously. Perform the business logic intended for
87 // this method here.
88
89 // We want to simulate the spell checking of words. We could hit
90 // against a text file checking to see if the word passed in was
91 // spelled correctly. However, to make things simple, we'll just
92 // give a word a 50% chance of being spelled correctly. Also, so
93 // that the client program can better see the affects of the
94 // asynchronous behavior, we'll have this EJB sleep anywhere from
95 // 100-5000 milliseconds to simulate the processing time needed
96 // to check the spelling of the word. The client should see
97 // some words immediately returned while others take longer.
98 int sleepTime = (int) (Math.random() * 5000);
99
100 try {
101 // You should never, ever sleep or block in an EJB! We are just
102 // doing this to demonstrate processing time.
103 Thread.sleep(sleepTime);
104 } catch (InterruptedException e) {}
105
106 double num = Math.random();
107 boolean spelledCorrectly = false;
108 if (num < 0.5)
109 spelledCorrectly = true;
110
111 return new Boolean(spelledCorrectly);
112
113 }
114
115 // Helper method
116 private void sendAsynchRequestMSG(Hashtable props) {
117 try {
118
119 // Set up the JNDI connection that this EJB will use
120 // to the Naming Service.
121 Hashtable env = new Hashtable();
122 env.put(Context.INITIAL_CONTEXT_FACTORY,
"weblogic.jndi.WLInitialContextFactory");
123 env.put(Context.PROVIDER_URL, "t3://localhost:7001");
124 InitialContext ctx = new InitialContext(env);
125
126 // Set up the JMS connection that this EJB will use
127 // to send asynchronous message requests. The destination
128 // of the asynchronous message handler is not determined
129 // by the client.
130
131 // Lookup a connection factory and create a connection.
132 QueueConnectionFactory qconFactory =
(QueueConnectionFactory)
133 ctx.lookup("AsynchMethodHandlerQCF");
134 QueueConnection qcon =
qconFactory.createQueueConnection();
135
136 // Create a session that is NON transacted.
137 // We don't need to buffer messages.
138 QueueSession qsession = qcon.createQueueSession
(false, Session.AUTO_ACKNOWLEDGE);
139
140 // Create the asynchronous destination object.
141 // If the object is already bound in JNDI, we'll use the one
// bound
142 // there. If not, we'll create the object and bind it for future
// use.
143 String queueName = "AsynchMethodHandlerQueue";
144 Queue queue = null;
145 try {
146 queue = (Queue) ctx.lookup(queueName);
147 } catch (NamingException ne) {
148 queue = qsession.createQueue(queueName);
149 ctx.bind(queueName, queue);
150 }
151
152 // Create a sender and the message we want to send.
153 QueueSender qsender = qsession.createSender(queue);
154 qcon.start();
155
156
157 // We have to put everything a handler will need to
158 // know to invoke this method into the message that
159 // we send to the handler. We must give the handler:
160 // 1) The location where the client wants the response
161 // 2) A reference to this EJB
162 // 3) A reflection API Method instance so that it knows
163 // which method to invoke on this EJB.
164 ObjectMessage msg = qsession.createObjectMessage();
165
166 msg.setObject(props);
167 qsender.send(msg);
168
169 // After the message has been sent, close everything and
// immediately
170 // return to the client.
171 ctx.close();
172 qsender.close();
173 qsession.close();
174 qcon.close();
175
176 } catch (Exception e) {
177 System.out.println("Error working with EJB or JMS messages.");
178 System.out.println(e);
179
180 }
181 }
182
183 public void ejbActivate() throws RemoteException {
184
185 }
186 public void ejbCreate() {
187 }
188 public void ejbPassivate() throws RemoteException {
189 }
190 public void ejbRemove() throws RemoteException {
191
192 }
193 public void setSessionContext(SessionContext arg1) throws RemoteException {
194 this.context = arg1;
195 }
196 }