Enterprise JavaAn Architecture for Making Asynchronous EJB Calls Using JMS

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 }