KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > remoting > Client


1 /***************************************
2  * *
3  * JBoss: The OpenSource J2EE WebOS *
4  * *
5  * Distributable under LGPL license. *
6  * See terms of license at gnu.org. *
7  * *
8  ***************************************/

9 package org.jboss.remoting;
10
11 import java.io.InputStream JavaDoc;
12 import java.util.HashMap JavaDoc;
13 import java.util.List JavaDoc;
14 import java.util.Map JavaDoc;
15 import org.jboss.logging.Logger;
16 import org.jboss.remoting.callback.InvokerCallbackHandler;
17 import org.jboss.remoting.invocation.InternalInvocation;
18 import org.jboss.remoting.invocation.OnewayInvocation;
19 import org.jboss.remoting.marshal.Marshaller;
20 import org.jboss.remoting.marshal.UnMarshaller;
21 import org.jboss.remoting.stream.StreamServer;
22 import org.jboss.remoting.transport.ClientInvoker;
23 import org.jboss.util.id.GUID;
24 import org.jboss.util.threadpool.BasicThreadPool;
25 import org.jboss.util.threadpool.BlockingMode;
26 import org.jboss.util.threadpool.ThreadPool;
27
28 /**
29  * Client is a convience method for invoking remote methods for a given subsystem
30  *
31  * @author <a HREF="mailto:jhaynie@vocalocity.net">Jeff Haynie</a>
32  * @author <a HREF="mailto:telrod@e2technologies.net">Tom Elrod</a>
33  * @version $Revision: 1.14 $
34  */

35 public class Client
36 {
37    public static final String JavaDoc LISTENER_ID_KEY = "listenerId";
38
39    /**
40     * Specifies the default number of work threads in the pool for
41     * executing one way invocations on the client.
42     * Value is 10.
43     */

44    public static final int MAX_NUM_ONEWAY_THREADS = 10;
45
46    /**
47     * Indicated the max number of threads used within oneway thread pool.
48     */

49    private int maxNumberThreads = MAX_NUM_ONEWAY_THREADS;
50    private static final Logger log = Logger.getLogger(Client.class);
51    private ClientInvoker invoker;
52    private ClassLoader JavaDoc classloader;
53    private String JavaDoc subsystem;
54    private String JavaDoc sessionId = new GUID().toString();
55    private ThreadPool onewayThreadPool;
56    public static final String JavaDoc RAW = "RAW_PAYLOAD";
57
58    private ConnectionValidator connectionValidator = null;
59
60    public Client(InvokerLocator locator) throws Exception JavaDoc
61    {
62       this(locator, null);
63    }
64
65    public Client(InvokerLocator locator, String JavaDoc subsystem)
66          throws Exception JavaDoc
67    {
68       this(Thread.currentThread().getContextClassLoader(), locator, subsystem);
69    }
70
71    public Client(ClassLoader JavaDoc cl, InvokerLocator locator, String JavaDoc subsystem)
72          throws Exception JavaDoc
73    {
74       this(cl, InvokerRegistry.createClientInvoker(locator), subsystem);
75    }
76
77    public Client(ClassLoader JavaDoc cl, ClientInvoker invoker, String JavaDoc subsystem)
78          throws Exception JavaDoc
79    {
80       this.classloader = cl;
81       this.subsystem = subsystem == null ? null : subsystem.toUpperCase();
82       this.invoker = invoker;
83    }
84
85    /**
86     * Adds a connection listener that will be notified if/when the connection
87     * to the server fails while the client is idle (no calls being made).
88     *
89     * @param listener
90     */

91    public void addConnectionListener(ConnectionListener listener)
92    {
93       if(connectionValidator == null)
94       {
95          connectionValidator = new ConnectionValidator(this);
96       }
97       connectionValidator.addConnectionListener(listener);
98    }
99
100    /**
101     * Removes specified connection listener. Will return true if it has
102     * already been registered, false otherwise.
103     *
104     * @param listener
105     * @return
106     */

107    public boolean removeConnectionListener(ConnectionListener listener)
108    {
109       return connectionValidator.removeConnectionListener(listener);
110    }
111
112    /**
113     * This will set the session id used when making invocations on
114     * server invokers. There is a default unique id automatically
115     * generated for each Client instance, so unless you have a good reason to set
116     * this, do not set this.
117     *
118     * @param sessionId
119     */

120    public void setSessionId(String JavaDoc sessionId)
121    {
122       this.sessionId = sessionId;
123    }
124
125    public String JavaDoc getSessionId()
126    {
127       return this.sessionId;
128    }
129
130    public boolean isConnected()
131    {
132       return (this.invoker != null && this.invoker.isConnected());
133    }
134
135    public void connect() throws Exception JavaDoc
136    {
137       this.invoker.connect();
138    }
139
140    public void disconnect()
141    {
142       this.invoker.disconnect();
143    }
144
145    public ClientInvoker getInvoker()
146    {
147       return invoker;
148    }
149
150    public void setInvoker(ClientInvoker invoker)
151    {
152       this.invoker = invoker;
153    }
154
155    public String JavaDoc getSubsystem()
156    {
157       return subsystem;
158    }
159
160    public void setSubsystem(String JavaDoc subsystem)
161    {
162       this.subsystem = subsystem;
163    }
164
165    /**
166     * Invokes the server invoker handler with the payload parameter passed.
167     *
168     * @param param
169     * @return
170     * @throws Throwable
171     */

172    public Object JavaDoc invoke(Object JavaDoc param) throws Throwable JavaDoc
173    {
174       return invoke(param, null);
175    }
176
177    /**
178     * invoke the method remotely
179     *
180     * @param param - payload for the server invoker handler
181     * @param metadata - any extra metadata that may be needed by the transport (i.e. GET or POST if using
182     * http invoker) or if need to pass along extra data to the server invoker handler.
183     * @return
184     * @throws Throwable
185     */

186    public Object JavaDoc invoke(Object JavaDoc param, Map JavaDoc metadata)
187          throws Throwable JavaDoc
188    {
189       return invoke(param, metadata, null);
190    }
191
192    private Object JavaDoc invoke(Object JavaDoc param, Map JavaDoc metadata, InvokerLocator callbackServerLocator)
193          throws Throwable JavaDoc
194    {
195       /**
196        * Using a local variable for the invoker as work around so don't have
197        * to sync method (and take performance hit)
198        * Although this may cause having multiple instances of invoker in existance at
199        * one time, should avoid having reference changed by another thread while in
200        * execution path for method.
201        */

202       ClientInvoker localInvoker = invoker;
203
204       if(localInvoker != null)
205       {
206          /**
207           * This is here due to way the InvokerRegistry works. Since it will cache references to
208           * client invokers it creates based on locator, it is possible that this instance will
209           * have a reference to the same instance of the invoker as another client. So is possible
210           * that client will disconnect, which will cause the invoker to be disconnected. Therefore,
211           * if this were to happen, we would create a new one by calling the createClientInvoker() method.
212           */

213          if(localInvoker.isConnected() == false)
214          {
215             log.debug("invoke called, but our invoker is disconnected, discarding and fetching another fresh invoker for: " + invoker.getLocator());
216
217             localInvoker = InvokerRegistry.createClientInvoker(localInvoker.getLocator());
218             localInvoker.connect();
219          }
220       }
221       else
222       {
223          throw new Exception JavaDoc("Can not perform invoke because invoker is null.");
224       }
225
226       Object JavaDoc ret = localInvoker.invoke(new InvocationRequest(sessionId, subsystem, param, metadata, null, callbackServerLocator));
227       this.invoker = localInvoker;
228       return ret;
229    }
230
231    /**
232     * Will invoke a oneway call to server without a return object. This should be used when not expecting a
233     * return value from the server and wish to achieve higher performance, since the client will not wait for
234     * a return.
235     * <b>
236     * This is done one of two ways. The first is to pass true as the clientSide param. This will cause the
237     * execution of the remote call to be excuted in a new thread on the client side and will return the calling thread
238     * before making call to server side. Although, this is optimal for performance, will not know about any problems
239     * contacting server.
240     * <p/>
241     * The second, is to pass false as the clientSide param. This will allow the current calling thread to make
242     * the call to the remote server, at which point, the server side processing of the thread will be executed on
243     * the remote server in a new executing thread and the client thread will return. This is a little slower, but
244     * will know that the call made it to the server.
245     *
246     * @param param
247     * @param sendPayload
248     * @param clientSide
249     */

250    public void invokeOneway(final Object JavaDoc param, final Map JavaDoc sendPayload, boolean clientSide) throws Throwable JavaDoc
251    {
252       if(clientSide)
253       {
254          ThreadPool threadPool = getOnewayThreadPool();
255          Runnable JavaDoc onewayRun = new Runnable JavaDoc()
256          {
257             public void run()
258             {
259                try
260                {
261                   invoke(param, sendPayload);
262                }
263                catch(Throwable JavaDoc e)
264                {
265                   // throw away exception since can't get it back to original caller
266
log.error("Error executing client oneway invocation request: " + param, e);
267                }
268             }
269          };
270          threadPool.run(onewayRun);
271       }
272       else
273       {
274          OnewayInvocation invocation = new OnewayInvocation(param);
275          invoke(invocation, sendPayload);
276       }
277    }
278
279    public void setMaxNumberOfThreads(int numOfThreads)
280    {
281       this.maxNumberThreads = numOfThreads;
282    }
283
284    public int getMaxNumberOfThreads()
285    {
286       return this.maxNumberThreads;
287    }
288
289    public ThreadPool getOnewayThreadPool()
290    {
291       if(onewayThreadPool == null)
292       {
293          BasicThreadPool pool = new BasicThreadPool("JBossRemoting Client Oneway");
294          pool.setMaximumPoolSize(maxNumberThreads);
295          pool.setBlockingMode(BlockingMode.WAIT);
296          onewayThreadPool = pool;
297       }
298       return onewayThreadPool;
299    }
300
301    public void setOnewayThreadPool(ThreadPool pool)
302    {
303       this.onewayThreadPool = pool;
304    }
305
306
307    /**
308     * Same as calling invokeOneway(Object param, Map sendPayload, boolean clientSide) with
309     * clientSide param being false. Therefore, client thread will not return till it has made
310     * remote call.
311     *
312     * @param param
313     * @param sendPayload
314     */

315    public void invokeOneway(Object JavaDoc param, Map JavaDoc sendPayload) throws Throwable JavaDoc
316    {
317       invokeOneway(param, sendPayload, false);
318    }
319
320    /**
321     * Adds the specified handler as a callback listener for pull (sync) callbacks.
322     * The invoker server will then collect the callbacks for this specific handler.
323     * The callbacks can be retrieved by calling the getCallbacks() method.
324     * Note: this will cause the client invoker's client locator to be set to null.
325     *
326     * @param callbackHandler
327     * @throws Throwable
328     */

329    public void addListener(InvokerCallbackHandler callbackHandler) throws Throwable JavaDoc
330    {
331       addListener(callbackHandler, null);
332    }
333
334    /**
335     * Adds the specified handler as a callback listener for push (async) callbacks.
336     * The invoker server will then callback on this handler (via the server invoker
337     * specified by the clientLocator) when it gets a callback from the server handler.
338     * Note: passing a null clientLocator will cause the client invoker's client
339     * locator to be set to null.
340     *
341     * @param callbackHandler
342     * @param clientLocator
343     * @throws Throwable
344     */

345    public void addListener(InvokerCallbackHandler callbackHandler,
346                            InvokerLocator clientLocator) throws Throwable JavaDoc
347    {
348       addListener(callbackHandler, clientLocator, null);
349    }
350
351    /**
352     * Adds the specified handler as a callback listener for push (async) callbacks.
353     * The invoker server will then callback on this handler (via the server invoker
354     * specified by the clientLocator) when it gets a callback from the server handler.
355     * Note: passing a null clientLocator will cause the client invoker's client
356     * locator to be set to null.
357     *
358     * @param callbackHandler interface to call on with callback
359     * @param clientLocator locator for callback server to callback on
360     * @param callbackHandlerObject will be included in the callback object passed upon callback
361     * @throws Throwable
362     */

363    public void addListener(InvokerCallbackHandler callbackHandler,
364                            InvokerLocator clientLocator, Object JavaDoc callbackHandlerObject) throws Throwable JavaDoc
365    {
366       if(callbackHandler != null)
367       {
368          Map JavaDoc metadata = createListenerMetadata(callbackHandler);
369          String JavaDoc listenerId = (String JavaDoc) metadata.get(LISTENER_ID_KEY);
370          invoker.addClientLocator(listenerId, clientLocator);
371          if(clientLocator != null)
372          {
373             Client client = new Client(clientLocator, subsystem);
374             client.setSessionId(getSessionId());
375             client.connect();
376
377             client.invoke(new InternalInvocation(InternalInvocation.ADDCLIENTLISTENER,
378                                                  new Object JavaDoc[]{callbackHandler, callbackHandlerObject}),
379                           metadata);
380             client.disconnect();
381          }
382          // now call server to add listener
383
invoke(new InternalInvocation(InternalInvocation.ADDLISTENER, null), metadata, clientLocator);
384       }
385       else
386       {
387          throw new NullPointerException JavaDoc("InvokerCallbackHandler to be added as a listener can not be null.");
388       }
389    }
390
391    private Map JavaDoc createListenerMetadata(InvokerCallbackHandler callbackHandler)
392    {
393       String JavaDoc listenerId = String.valueOf(callbackHandler.hashCode());
394       Map JavaDoc metadata = new HashMap JavaDoc();
395       metadata.put(LISTENER_ID_KEY, listenerId);
396       return metadata;
397    }
398
399    /**
400     * Removes callback handler as a callback listener from the server (and client in
401     * the case that it was setup to receive async callbacks). See addListener().
402     *
403     * @param callbackHandler
404     * @throws Throwable
405     */

406    public void removeListener(InvokerCallbackHandler callbackHandler) throws Throwable JavaDoc
407    {
408       if(callbackHandler != null)
409       {
410          Map JavaDoc metadata = createListenerMetadata(callbackHandler);
411          String JavaDoc listenerId = (String JavaDoc) metadata.get(LISTENER_ID_KEY);
412          // connect to the given client locator and remove handler as listener
413
InvokerLocator locator = invoker.getClientLocator(listenerId);
414          if(locator != null) // async callback
415
{
416             Client client = new Client(locator, subsystem);
417             client.setSessionId(getSessionId());
418             client.connect();
419             client.invoke(new InternalInvocation(InternalInvocation.REMOVECLIENTLISTENER,
420                                                  new Object JavaDoc[]{callbackHandler}),
421                           metadata);
422             client.disconnect();
423          }
424          // now call server to remove listener
425
invoke(new InternalInvocation(InternalInvocation.REMOVELISTENER, null), metadata);
426       }
427       else
428       {
429          throw new NullPointerException JavaDoc("Can not remove null InvokerCallbackHandler listener.");
430       }
431    }
432
433    /**
434     * Gets the callbacks for specified callback handler. The handler is required because an id is generated
435     * for each handler. So if have two callback handlers registered with the same server, no other way to know
436     * for which handler to get the callbacks for.
437     *
438     * @param callbackHandler
439     * @return
440     * @throws Throwable
441     */

442    public List JavaDoc getCallbacks(InvokerCallbackHandler callbackHandler) throws Throwable JavaDoc
443    {
444       if(callbackHandler != null)
445       {
446          Map JavaDoc metadata = createListenerMetadata(callbackHandler);
447          return (List JavaDoc) invoke(new InternalInvocation(InternalInvocation.GETCALLBACKS, null), metadata);
448       }
449       else
450       {
451          throw new NullPointerException JavaDoc("Can not remove null InvokerCallbackHandler listener.");
452       }
453    }
454
455    public void setMarshaller(Marshaller marshaller)
456    {
457       if(invoker != null && marshaller != null)
458       {
459          invoker.setMarshaller(marshaller);
460       }
461    }
462
463    public void setUnMarshaller(UnMarshaller unmarshaller)
464    {
465       if(invoker != null && unmarshaller != null)
466       {
467          invoker.setUnMarshaller(unmarshaller);
468       }
469    }
470
471    /**
472     * Takes an inputstream and wraps a server around. Then calls the target
473     * remoting server and passes a proxy for an inputstream to the server's handler.
474     * When the server handler calls on this proxy, it will call back on this server
475     * wrapped around this inputstream.
476     *
477     * @param inputStream
478     * @param param invocation payload
479     * @return the return value from the invocation
480     * @throws Throwable
481     */

482    public Object JavaDoc invoke(InputStream JavaDoc inputStream, Object JavaDoc param) throws Throwable JavaDoc
483    {
484       StreamServer streamServer = new StreamServer(inputStream);
485       String JavaDoc locator = streamServer.getInvokerLocator();
486
487       // now call on target server and pass locator for stream callbacks
488
return invoke(new InternalInvocation(InternalInvocation.ADDSTREAMCALLBACK, new Object JavaDoc[]{locator, param}), null);
489    }
490 }
491
Popular Tags