~ubuntu-branches/ubuntu/raring/libjboss-remoting-java/raring

« back to all changes in this revision

Viewing changes to src/org/jboss/remoting/Client.java

  • Committer: Package Import Robot
  • Author(s): Torsten Werner
  • Date: 2011-09-09 14:01:03 UTC
  • mto: This revision was merged to the branch mainline in revision 9.
  • Revision ID: package-import@ubuntu.com-20110909140103-o8ucrolqt5g25k57
Tags: upstream-2.5.3.SP1
ImportĀ upstreamĀ versionĀ 2.5.3.SP1

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
* JBoss, Home of Professional Open Source
3
 
* Copyright 2005, JBoss Inc., and individual contributors as indicated
4
 
* by the @authors tag. See the copyright.txt in the distribution for a
5
 
* full listing of individual contributors.
6
 
*
7
 
* This is free software; you can redistribute it and/or modify it
8
 
* under the terms of the GNU Lesser General Public License as
9
 
* published by the Free Software Foundation; either version 2.1 of
10
 
* the License, or (at your option) any later version.
11
 
*
12
 
* This software is distributed in the hope that it will be useful,
13
 
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
 
* Lesser General Public License for more details.
16
 
*
17
 
* You should have received a copy of the GNU Lesser General Public
18
 
* License along with this software; if not, write to the Free
19
 
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20
 
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21
 
*/
22
 
 
23
 
package org.jboss.remoting;
24
 
 
25
 
import org.jboss.logging.Logger;
26
 
import org.jboss.remoting.callback.Callback;
27
 
import org.jboss.remoting.callback.CallbackPoller;
28
 
import org.jboss.remoting.callback.InvokerCallbackHandler;
29
 
import org.jboss.remoting.callback.ServerInvokerCallbackHandler;
30
 
import org.jboss.remoting.invocation.InternalInvocation;
31
 
import org.jboss.remoting.invocation.OnewayInvocation;
32
 
import org.jboss.remoting.marshal.Marshaller;
33
 
import org.jboss.remoting.marshal.UnMarshaller;
34
 
import org.jboss.remoting.security.SSLSocketBuilder;
35
 
import org.jboss.remoting.stream.StreamServer;
36
 
import org.jboss.remoting.transport.BidirectionalClientInvoker;
37
 
import org.jboss.remoting.transport.ClientInvoker;
38
 
import org.jboss.remoting.transport.Connector;
39
 
import org.jboss.remoting.transport.PortUtil;
40
 
import org.jboss.remoting.transport.local.LocalClientInvoker;
41
 
import org.jboss.remoting.util.SecurityUtility;
42
 
import org.jboss.util.id.GUID;
43
 
import org.jboss.util.threadpool.BasicThreadPool;
44
 
import org.jboss.util.threadpool.BlockingMode;
45
 
import org.jboss.util.threadpool.ThreadPool;
46
 
 
47
 
import javax.net.SocketFactory;
48
 
import java.io.Externalizable;
49
 
import java.io.IOException;
50
 
import java.io.InputStream;
51
 
import java.io.ObjectInput;
52
 
import java.io.ObjectOutput;
53
 
import java.io.StreamCorruptedException;
54
 
import java.lang.ref.WeakReference;
55
 
import java.net.InetAddress;
56
 
import java.net.SocketTimeoutException;
57
 
import java.net.UnknownHostException;
58
 
import java.rmi.MarshalException;
59
 
import java.security.AccessController;
60
 
import java.security.PrivilegedAction;
61
 
import java.security.PrivilegedActionException;
62
 
import java.security.PrivilegedExceptionAction;
63
 
import java.util.ArrayList;
64
 
import java.util.HashMap;
65
 
import java.util.HashSet;
66
 
import java.util.Iterator;
67
 
import java.util.List;
68
 
import java.util.Map;
69
 
import java.util.Set;
70
 
import java.util.Timer;
71
 
import java.util.TimerTask;
72
 
 
73
 
/**
74
 
 * Client is a convience class for invoking remote methods for a given subsystem. It is intended to
75
 
 * be the main user interface for making remote invocation on the client side.
76
 
 *
77
 
 * @author <a href="mailto:jhaynie@vocalocity.net">Jeff Haynie</a>
78
 
 * @author <a href="mailto:telrod@e2technologies.net">Tom Elrod</a>
79
 
 * @author <a href="mailto:ovidiu@jboss.org">Ovidiu Feodorov</a>
80
 
 *
81
 
 * @version $Revision: 5458 $
82
 
 */
83
 
public class Client implements Externalizable
84
 
{
85
 
   // Constants ------------------------------------------------------------------------------------
86
 
 
87
 
   /**
88
 
    * Key to be used to determine if invocation is to be oneway (async).
89
 
    */
90
 
   public static final String ONEWAY_FLAG = "oneway";
91
 
 
92
 
   /**
93
 
    * Key to be used when tracking callback listeners.
94
 
    */
95
 
   public static final String LISTENER_ID_KEY = "listenerId";
96
 
 
97
 
   /**
98
 
    * Specifies the default number of work threads in the pool for executing one way invocations on
99
 
    * the client. Value is 10.
100
 
    */
101
 
   public static final int MAX_NUM_ONEWAY_THREADS_DEFAULT = 10;
102
 
 
103
 
   /**
104
 
    * The key to use for the metadata Map passed when making a invoke() call and wish for the
105
 
    * invocation payload to be sent as is and not wrapped within a remoting invocation request
106
 
    * object. This should be used when want to make direct calls on systems outside of remoting
107
 
    * (e.g. making a http POST request to a web service).
108
 
    */
109
 
   public static final String RAW = "rawPayload";
110
 
 
111
 
   /**
112
 
    * Key for the configuration map passed to the Client constructor to indicate that client should
113
 
    * make initial request to establish lease with server. The value for this should be either a
114
 
    * String that java.lang.Boolean can evaluate or a java.lang.Boolean. Client leasing is turned
115
 
    * off by default, so would need to use this property to turn client leasing on.
116
 
    */
117
 
   public static final String ENABLE_LEASE = "enableLease";
118
 
 
119
 
   /**
120
 
    * Key for the configuration map passed to the Client constructor providing a ssl
121
 
    * javax.net.ssl.HandshakeCompletedListener implementation, which will be called on when ssl
122
 
    * handshake completed with server.
123
 
    */
124
 
   public static final String HANDSHAKE_COMPLETED_LISTENER = "handshakeCompletedListener";
125
 
 
126
 
   /**
127
 
    * Key for the configuration when adding a callback handler and internal callback server
128
 
    * connector is created.  The value should be the transport protocol to be used. By default will
129
 
    * use the same protocol as being used by this client (e.g. http, socket, rmi, multiplex, etc.)
130
 
    */
131
 
   public static final String CALLBACK_SERVER_PROTOCOL = "callbackServerProtocol";
132
 
 
133
 
   /**
134
 
    * Key for the configuration when adding a callback handler and internal callback server
135
 
    * connector is created.  The value should be the host name to be used. By default will use the
136
 
    * result of calling InetAddress.getLocalHost().getHostAddress().
137
 
    */
138
 
   public static final String CALLBACK_SERVER_HOST = "callbackServerHost";
139
 
 
140
 
   /**
141
 
    * Key for the configuration when adding a callback handler and internal callback server
142
 
    * connector is created.  The value should be the port to be used.  By default will find a random
143
 
    * unused port.
144
 
    */
145
 
   public static final String CALLBACK_SERVER_PORT = "callbackServerPort";
146
 
 
147
 
   /**
148
 
    * Key for the configuration map that determines the threadpool size for asynchrouous invocations.
149
 
    */
150
 
   public static final String MAX_NUM_ONEWAY_THREADS = "maxNumThreadsOneway";
151
 
 
152
 
   /**
153
 
    * Key for the configuration map that determines the queue size for waiting asynchronous
154
 
    * invocations.
155
 
    */
156
 
   public static final String MAX_ONEWAY_THREAD_POOL_QUEUE_SIZE = "maxOnewayThreadPoolQueueSize";
157
 
 
158
 
   /**
159
 
    * Default timeout period for network i/o in disconnect() and removeListener().
160
 
    * -1 indicates that no special per invocation timeout will be set.
161
 
    */
162
 
   public static final int DEFAULT_DISCONNECT_TIMEOUT = -1;
163
 
   
164
 
   /**
165
 
    * Key for setting delay before client invoker is destroyed by disconnect().
166
 
    */
167
 
   public static final String INVOKER_DESTRUCTION_DELAY = "invokerDestructionDelay";
168
 
   
169
 
   public static final String THROW_CALLBACK_EXCEPTION = "throwCallbackException";
170
 
   
171
 
   private static Map connectionValidators = new HashMap();
172
 
   private static Object connectionValidatorLock = new Object();
173
 
 
174
 
   static final String CLIENT = "client";
175
 
   static final String CONNECTION_LISTENER = "connectionListener";
176
 
   
177
 
   /** The key to use to specify that parameters for objects created by Client should be taken,
178
 
    *  in addition to the metadata map, from the InvokerLocator and from the Client's configuration map.
179
 
    */
180
 
   public static final String USE_ALL_PARAMS = "useAllParams";
181
 
   
182
 
   private static final Logger log = Logger.getLogger(Client.class);
183
 
   private static boolean trace = log.isTraceEnabled();
184
 
 
185
 
   private static final long serialVersionUID = 5679279425009837934L;
186
 
   
187
 
   private static Timer invokerDestructionTimer;
188
 
   private static Object invokerDestructionTimerLock = new Object();
189
 
 
190
 
   // Static ---------------------------------------------------------------------------------------
191
 
 
192
 
   // Attributes -----------------------------------------------------------------------------------
193
 
 
194
 
   /**
195
 
    * Indicated the max number of threads used within oneway thread pool.
196
 
    */
197
 
   private int maxNumberThreads = MAX_NUM_ONEWAY_THREADS_DEFAULT;
198
 
   private int maxOnewayThreadPoolQueueSize = -1;
199
 
   private ClientInvoker invoker;
200
 
   private ClassLoader classloader;
201
 
   private String subsystem;
202
 
   private String sessionId;
203
 
   private Object onewayThreadPoolLock = new Object();
204
 
   private ThreadPool onewayThreadPool;
205
 
   private InvokerLocator locator;
206
 
 
207
 
   private ConnectionValidator connectionValidator = null;
208
 
   private ConnectionValidatorKey connectionValidatorKey;
209
 
   private Map configuration = new HashMap();
210
 
 
211
 
   private Map callbackConnectors = new HashMap();
212
 
   private Map callbackPollers = new HashMap();
213
 
 
214
 
   private Map listeners = new HashMap();
215
 
 
216
 
   private SocketFactory socketFactory;
217
 
 
218
 
   private int disconnectTimeout = DEFAULT_DISCONNECT_TIMEOUT;
219
 
 
220
 
   private boolean connected = false;
221
 
   
222
 
   private int invokerDestructionDelay = 0;
223
 
 
224
 
   private Set connectionListeners = new HashSet();
225
 
   
226
 
   private boolean useClientConnectionIdentity;
227
 
   
228
 
   // Constructors ---------------------------------------------------------------------------------
229
 
 
230
 
   /**
231
 
    * PLEASE DO NOT USE THIS CONSTRUCTOR OR YOUR COMPUTER WILL BURST INTO FLAMES!!!
232
 
    * It is only here so can externalize object and will provide a dead object if invoker is not
233
 
    * explicitly set. Please use other contructors provided.
234
 
    */
235
 
   public Client()
236
 
   {
237
 
   }
238
 
 
239
 
   /**
240
 
    * Constructs a remoting client with intended target server specified via the locator, without
241
 
    * specifing a remote subsystem or including any metadata. Same as calling Client(locator, null,
242
 
    * null).
243
 
    */
244
 
   public Client(InvokerLocator locator) throws Exception
245
 
   {
246
 
      this(locator, null, null);
247
 
   }
248
 
 
249
 
   /**
250
 
    * Constructs a remoting client with intended target server specified via the locator and
251
 
    * configuration metadata.  The metadata supplied will be used when creating client invoker (in
252
 
    * the case specific data is required) and also for passing along additional data to connection
253
 
    * listeners on the server side in the case that the client fails, will be able to use this extra
254
 
    * information when notified.
255
 
    */
256
 
   public Client(InvokerLocator locator, Map configuration) throws Exception
257
 
   {
258
 
      this(locator, null, configuration);
259
 
   }
260
 
 
261
 
   /**
262
 
    * Constructs a remoting client with intended target server specified via the locator and
263
 
    * intended subsystem on server for invocations to be routed to.
264
 
    */
265
 
   public Client(InvokerLocator locator, String subsystem) throws Exception
266
 
   {
267
 
      this(locator, subsystem, null);
268
 
   }
269
 
 
270
 
   /**
271
 
    * Constructs a remoting client with intended target server specified via the locator, intended
272
 
    * subsystem on the server for invocations to be routed to, and configuration metadata. The
273
 
    * metadata supplied will be used when creating client invoker (in the case specific data is
274
 
    * required) and also for passing along additional data to connection listeners on the server
275
 
    * side in the case that the client fails, will be able to use this extra information when
276
 
    * notified.
277
 
    */
278
 
   public Client(InvokerLocator locator, String subsystem, Map configuration) throws Exception
279
 
   {
280
 
      this(null, locator, subsystem, configuration);
281
 
   }
282
 
 
283
 
   /**
284
 
    * Constructs a remoting client with intended target server specified via the locator, intended
285
 
    * subsystem on the server for invocations to be routed to, and configuration metadata. The
286
 
    * metadata supplied will be used when creating client invoker (in the case specific data is
287
 
    * required) and also for passing along additional data to connection listeners on the server
288
 
    * side in the case that the client fails, will be able to use this extra information when
289
 
    * notified (which will happen when connect() method is called.
290
 
    *
291
 
    * @param cl - the classloader that should be used by remoting.
292
 
    * @deprecated This constructor should not be used any more as will no longer take into account
293
 
    *             the classloader specified as a parameter.
294
 
    */
295
 
   public Client(ClassLoader cl, InvokerLocator locator, String subsystem, Map configuration)
296
 
         throws Exception
297
 
   {
298
 
      if (cl == null)
299
 
      {
300
 
         this.classloader = (ClassLoader) AccessController.doPrivileged( new PrivilegedAction()
301
 
         {
302
 
            public Object run()
303
 
            {
304
 
               return Thread.currentThread().getContextClassLoader();
305
 
            }
306
 
         });
307
 
      }
308
 
      else
309
 
      {
310
 
         this.classloader = cl;
311
 
      }
312
 
      this.locator = locator;
313
 
      this.subsystem = subsystem == null ? null : subsystem.toUpperCase();
314
 
      if (configuration != null)
315
 
      {
316
 
         this.configuration = new HashMap(configuration);
317
 
      }
318
 
      this.sessionId = new GUID().toString();
319
 
      processParameters();
320
 
   }
321
 
 
322
 
   /**
323
 
    * Constructs a remoting client with intended target server specified via the locator and
324
 
    * intended subsystem on server for invocations to be routed to.
325
 
    *
326
 
    * @deprecated This constructor should not be used any more as will no longer take into account
327
 
    *             the classloader specified as a parameter.
328
 
    */
329
 
   public Client(ClassLoader cl, ClientInvoker invoker, String subsystem) throws Exception
330
 
   {
331
 
      this.classloader = cl;
332
 
      this.subsystem = subsystem == null ? null : subsystem.toUpperCase();
333
 
      this.invoker = invoker;
334
 
      this.sessionId = new GUID().toString();
335
 
   }
336
 
 
337
 
   // Externalizable implementation ----------------------------------------------------------------
338
 
 
339
 
   public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
340
 
   {
341
 
      int version = in.readInt();
342
 
 
343
 
      switch (version)
344
 
      {
345
 
         case Version.VERSION_2:
346
 
         case Version.VERSION_2_2:
347
 
         {
348
 
            this.locator = (InvokerLocator) in.readObject();
349
 
            this.subsystem = (String) in.readObject();
350
 
            this.configuration = (Map) in.readObject();
351
 
            boolean wasConnected = in.readBoolean();
352
 
 
353
 
            this.classloader = (ClassLoader) AccessController.doPrivileged( new PrivilegedAction()
354
 
            {
355
 
               public Object run()
356
 
               {
357
 
                  return Thread.currentThread().getContextClassLoader();
358
 
               }
359
 
            });
360
 
            
361
 
            try
362
 
            {
363
 
               this.invoker = InvokerRegistry.createClientInvoker(locator, configuration);
364
 
               if(wasConnected)
365
 
               {
366
 
                  connect();
367
 
               }
368
 
            }
369
 
            catch (Exception e)
370
 
            {
371
 
               log.debug(e);
372
 
               throw new IOException(e.getMessage());
373
 
            }
374
 
 
375
 
            break;
376
 
         }
377
 
         default:
378
 
            throw new StreamCorruptedException("Unkown version seen: " + version);
379
 
      }
380
 
   }
381
 
 
382
 
   public void writeExternal(ObjectOutput out) throws IOException
383
 
   {
384
 
      out.writeInt(Version.getDefaultVersion());
385
 
      out.writeObject(invoker != null ? invoker.getLocator() : locator);
386
 
      out.writeObject(subsystem);
387
 
      out.writeObject(configuration);
388
 
      out.writeBoolean(isConnected());
389
 
      out.flush();
390
 
   }
391
 
 
392
 
   // Public ---------------------------------------------------------------------------------------
393
 
 
394
 
   /**
395
 
    * Adds a connection listener that will be notified if/when the connection to the server fails
396
 
    * while the client is idle (no calls being made). The default behavior is to ping for connection
397
 
    * every two seconds.
398
 
    */
399
 
   public void addConnectionListener(ConnectionListener listener)
400
 
   {
401
 
      HashMap metadata = new HashMap();
402
 
      if (configuration.get(ConnectionValidator.VALIDATOR_PING_PERIOD) == null && 
403
 
          locator.getParameters().get(ConnectionValidator.VALIDATOR_PING_PERIOD) == null)
404
 
      {
405
 
         String pingPeriod = Long.toString(ConnectionValidator.DEFAULT_PING_PERIOD);
406
 
         metadata.put(ConnectionValidator.VALIDATOR_PING_PERIOD, pingPeriod);
407
 
      }
408
 
      addConnectionListener(listener, metadata);
409
 
   }
410
 
 
411
 
   /**
412
 
    * Adds a connection listener that will be notified if/when the connection to the server fails
413
 
    * while the client is idle (no calls being made). The current behavior is to ping the server
414
 
    * periodically.  The time period is defined by the pingPeriod (which should be in milliseconds).
415
 
    */
416
 
   public void addConnectionListener(ConnectionListener listener, int pingPeriod)
417
 
   {
418
 
      HashMap metadata = new HashMap();
419
 
      metadata.put(ConnectionValidator.VALIDATOR_PING_PERIOD, Integer.toString(pingPeriod));
420
 
      addConnectionListener(listener, metadata);
421
 
   }
422
 
   
423
 
   /**
424
 
    * Adds a connection listener that will be notified if/when the connection to the server fails
425
 
    * while the client is idle (no calls being made). The current behavior is to ping the server
426
 
    * periodically.  Various parameters may be specified in metadata.
427
 
    * 
428
 
    * @see org.jboss.remoting.ConnectionValidator
429
 
    */
430
 
   public void addConnectionListener(ConnectionListener listener, Map metadata)
431
 
   {
432
 
      if (invoker == null)
433
 
      {
434
 
         throw new RuntimeException("Can not add connection listener to remoting client " +
435
 
                                    "while client is not connected.");
436
 
      }
437
 
      else
438
 
      {
439
 
         // if local, then no point in having connection listener
440
 
         if (invoker instanceof LocalClientInvoker)
441
 
         {
442
 
            return;
443
 
         }
444
 
      }
445
 
 
446
 
      synchronized (connectionValidatorLock)
447
 
      {
448
 
         if (trace) log.trace(this + " in addConnectionListener()");
449
 
         if (connectionValidator == null)
450
 
         {
451
 
            Map map = new HashMap(configuration);
452
 
            map.putAll(metadata);
453
 
            connectionValidatorKey = new ConnectionValidatorKey(invoker, map);
454
 
            WeakReference ref = (WeakReference) connectionValidators.get(connectionValidatorKey);
455
 
            if (ref == null)
456
 
            {
457
 
               connectionValidator = new ConnectionValidator(this, metadata);
458
 
               connectionValidators.put(connectionValidatorKey, new WeakReference(connectionValidator));
459
 
               connectionValidator.addConnectionListener(this, listener);
460
 
               if (trace) log.trace(this + ": created " + connectionValidator);
461
 
            }
462
 
            else
463
 
            {
464
 
               connectionValidator = (ConnectionValidator) ref.get();
465
 
               if (connectionValidator.addConnectionListener(this, listener))
466
 
               {
467
 
                  if (trace) log.trace(this + ": reusing from static table:  " + connectionValidator);                  
468
 
               }
469
 
               else
470
 
               {
471
 
                  if (trace) log.trace(this + ": unable to reuse existing ConnectionValidator in static map: " + connectionValidator);
472
 
                  connectionValidator = new ConnectionValidator(this, metadata);
473
 
                  connectionValidators.put(connectionValidatorKey, new WeakReference(connectionValidator));
474
 
                  connectionValidator.addConnectionListener(this, listener);
475
 
                  if (trace) log.trace(this + ": current ConnectionValidator is stopped: created " + connectionValidator);
476
 
               }
477
 
            }
478
 
         }
479
 
         else
480
 
         {
481
 
            if (connectionValidator.addConnectionListener(this, listener))
482
 
            {
483
 
               if (trace) log.trace(this + ": reusing from local reference: " + connectionValidator);                  
484
 
            }
485
 
            else
486
 
            {
487
 
               if (trace) log.trace(this + ": unable to reuse ConnectionValidator from local reference: " + connectionValidator);
488
 
               connectionValidator = new ConnectionValidator(this, metadata);
489
 
               connectionValidators.put(connectionValidatorKey, new WeakReference(connectionValidator));
490
 
               connectionValidator.addConnectionListener(this, listener);
491
 
               if (trace) log.trace(this + ": current ConnectionValidator is stopped: created " + connectionValidator);
492
 
            }
493
 
         }
494
 
         
495
 
         connectionListeners.add(listener);
496
 
      }
497
 
   }
498
 
 
499
 
   /**
500
 
    * Removes specified connection listener.  Will return true if it has already been registered,
501
 
    * false otherwise.
502
 
    */
503
 
   public boolean removeConnectionListener(ConnectionListener listener)
504
 
   {
505
 
      if (trace) log.trace(this + ".removeConnectionListener(" + listener + ")");
506
 
      boolean isRemoved = false;
507
 
      synchronized (connectionValidatorLock)
508
 
      {
509
 
         if (connectionValidator == null)
510
 
         {
511
 
            return false;
512
 
         }
513
 
         isRemoved = connectionValidator.removeConnectionListener(this, listener);
514
 
         if (connectionValidator.isStopped())
515
 
         {
516
 
            if (connectionValidators.remove(connectionValidatorKey) != null)
517
 
            {
518
 
               log.debug(this + ".removeConnectionListener() removed from static map: " + connectionValidator);
519
 
            }
520
 
            connectionValidator = null;
521
 
            connectionValidatorKey = null;
522
 
         }
523
 
         connectionListeners.remove(listener);
524
 
         if (connectionListeners.isEmpty())
525
 
         {
526
 
            connectionValidator = null;
527
 
            connectionValidatorKey = null;
528
 
         }
529
 
         if (connectionValidator == null)
530
 
         {
531
 
            if (trace) log.trace(this + " set connectionValidator to null");
532
 
         }
533
 
      }
534
 
      return isRemoved;
535
 
   }
536
 
 
537
 
   /**
538
 
    * This will set the session id used when making invocations on server invokers. There is a
539
 
    * default unique id automatically generated for each Client instance, so unless you have a good
540
 
    * reason to set this, do not set this.
541
 
    */
542
 
   public void setSessionId(String sessionId)
543
 
   {
544
 
      this.sessionId = sessionId;
545
 
   }
546
 
 
547
 
   /**
548
 
    * Gets the configuration map passed when constructing this object.
549
 
    */
550
 
   public Map getConfiguration()
551
 
   {
552
 
      return configuration;
553
 
   }
554
 
 
555
 
   /**
556
 
    * Gets the session id used when making invocations on server invokers. This is the id that will
557
 
    * be used for tracking client connections on the server side, to include client failures that
558
 
    * are sent to connection listeners on the server side.
559
 
    */
560
 
   public String getSessionId()
561
 
   {
562
 
      return this.sessionId;
563
 
   }
564
 
 
565
 
   /**
566
 
    * Indicates if the underlying transport has been connected to the target server.
567
 
    */
568
 
   public boolean isConnected()
569
 
   {
570
 
      return connected;
571
 
   }
572
 
 
573
 
   /**
574
 
    * Will cause the underlying transport to make connection to the target server.  This is
575
 
    * important for any stateful transports, like socket or multiplex. This is also when a client
576
 
    * lease with the server is started.
577
 
    */
578
 
   public void connect() throws Exception
579
 
   {
580
 
       connect(null, null);
581
 
   }
582
 
   
583
 
   /**
584
 
    * Will cause the underlying transport to make connection to the target server.  This is
585
 
    * important for any stateful transports, like socket or multiplex. This is also when a client
586
 
    * lease with the server is started.  If listener is not null, it will be registered to
587
 
    * receive a callback if the connection fails.
588
 
    */
589
 
   public void connect(ConnectionListener listener) throws Exception
590
 
   {
591
 
       connect(listener, null);
592
 
   }
593
 
   
594
 
   /**
595
 
    * Will cause the underlying transport to make connection to the target server.  This is
596
 
    * important for any stateful transports, like socket or multiplex. This is also when a client
597
 
    * lease with the server is started.  If listener is not null, it will be registered to
598
 
    * receive a callback if the connection fails.
599
 
    * <p>
600
 
    * 
601
 
    * If this version of connect() is used, and leasing is enabled, the concept of "connection
602
 
    * identity" is enforced.  That is, the ConnectionValidator used by this Client will be
603
 
    * tied to the LeasePinger currently used by the MicroRemoteClientInvoker created or reused
604
 
    * in this method, and that LeasePinger will be tied to this Client and its ConnectionValidator.
605
 
    * If the ConnectionValidator used by any of the Clients associated with the MicroRemoteClientInvoker
606
 
    * used by this Client detects a broken connection, it will shut down that LeasePinger.
607
 
    * Moreover, each ConnectionValidator associated with that LeasePinger will notify its
608
 
    * ConnectionListeners of the broken connection.  At that point, the LeasePinger will be
609
 
    * destroyed, and all of the associated Clients will be disconnected. 
610
 
    */
611
 
   public void connect(ConnectionListener listener, Map metadata) throws Exception
612
 
   {
613
 
      log.debug(this + ".connect(" + listener + ")");
614
 
      if (trace) log.trace(this + ": metadata = " + metadata);
615
 
      if (isConnected())
616
 
         return;
617
 
 
618
 
      if (locator == null)
619
 
      {
620
 
         throw new IllegalStateException("Cannot connect a client with a null locator");
621
 
      }
622
 
 
623
 
      if (invoker == null)
624
 
      {
625
 
         if (socketFactory != null)
626
 
         {
627
 
            configuration.put(Remoting.CUSTOM_SOCKET_FACTORY, socketFactory);
628
 
            this.socketFactory = null;
629
 
         }
630
 
         invoker = InvokerRegistry.createClientInvoker(locator, configuration);
631
 
      }
632
 
 
633
 
      connect(invoker, listener, metadata);
634
 
 
635
 
      connected = true;
636
 
      log.debug(this + " is connected");
637
 
   }
638
 
 
639
 
   /**
640
 
    * Disconnects the underlying transport from the target server. Also notifies the target server
641
 
    * to terminate client lease.  Is important that this method is called when no longer using the
642
 
    * remoting client.  Otherwise resource will not be cleaned up and if the target server requires
643
 
    * a lease, it will be maintained in the background.
644
 
    */
645
 
   public void disconnect()
646
 
   {
647
 
      if (trace) log.trace(this + " entering disconnect()");
648
 
      
649
 
      connected = false;
650
 
      
651
 
      if (invoker != null)
652
 
      {
653
 
         // this is a noop if no lease is active
654
 
         invoker.terminateLease(sessionId, disconnectTimeout);
655
 
         
656
 
         // Need to remove myself from registry so will not keep reference to me since I am of no
657
 
         // use now. Will have to create a new one.
658
 
 
659
 
         if (invokerDestructionDelay > 0)
660
 
         {
661
 
            synchronized (invokerDestructionTimerLock)
662
 
            {
663
 
               InvokerDestructionTimerTask task = new InvokerDestructionTimerTask(invoker);
664
 
               if (invokerDestructionTimer == null)
665
 
               {
666
 
                  invokerDestructionTimer = new Timer(true);
667
 
               }
668
 
 
669
 
               try
670
 
               {
671
 
                  invokerDestructionTimer.schedule(task, invokerDestructionDelay);
672
 
               }
673
 
               catch (IllegalStateException e)
674
 
               {
675
 
                  log.debug("Unable to schedule InvokerDestructionTimerTask on existing Timer", e);
676
 
                  invokerDestructionTimer = new Timer(true);
677
 
                  invokerDestructionTimer.schedule(task, invokerDestructionDelay);
678
 
               }
679
 
               
680
 
               if (trace) log.trace(this + " scheduled destruction of " + invoker);
681
 
            }
682
 
         }
683
 
         else
684
 
         {
685
 
            InvokerRegistry.destroyClientInvoker(invoker.getLocator(), configuration);
686
 
         }
687
 
         
688
 
         invoker = null;
689
 
      }
690
 
      
691
 
      synchronized (connectionValidatorLock)
692
 
      {
693
 
         if (connectionValidator != null)
694
 
         {
695
 
            Iterator it = connectionListeners.iterator();
696
 
            while (it.hasNext())
697
 
            {
698
 
               ConnectionListener listener = (ConnectionListener) it.next();
699
 
               connectionValidator.removeConnectionListener(this, listener);
700
 
            }
701
 
            if (connectionValidator.isStopped())
702
 
            {
703
 
               if (connectionValidators.remove(connectionValidatorKey) != null)
704
 
               {
705
 
                  if (trace) log.trace(this + ".disconnect() removed from static map: " + connectionValidator);
706
 
               }
707
 
            }
708
 
            
709
 
            connectionValidator = null;
710
 
            connectionValidatorKey = null;
711
 
         }
712
 
      }
713
 
      log.debug(this + " is disconnected");
714
 
   }
715
 
 
716
 
   /**
717
 
    * Get the client invoker (transport implementation).
718
 
    */
719
 
   public ClientInvoker getInvoker()
720
 
   {
721
 
      return invoker;
722
 
   }
723
 
 
724
 
   /**
725
 
    * Set the client invoker (transport implementation).
726
 
    */
727
 
   public void setInvoker(ClientInvoker invoker)
728
 
   {
729
 
      this.invoker = invoker;
730
 
   }
731
 
 
732
 
   /**
733
 
    * Gets the subsystem being used when routing invocation request on the server side.
734
 
    */
735
 
   public String getSubsystem()
736
 
   {
737
 
      return subsystem;
738
 
   }
739
 
 
740
 
   /**
741
 
    * Sets the subsystem being used when routing invocation requests on the server side.  Specifing
742
 
    * a subsystem is only needed when server has multiple handlers registered (which will each have
743
 
    * their own associated subsystem).
744
 
    */
745
 
   public void setSubsystem(String subsystem)
746
 
   {
747
 
      this.subsystem = subsystem;
748
 
   }
749
 
 
750
 
   /**
751
 
    * Invokes the server invoker handler with the payload parameter passed. Same as calling
752
 
    * invoke(param, null);
753
 
    */
754
 
   public Object invoke(Object param) throws Throwable
755
 
   {
756
 
      return invoke(param, null);
757
 
   }
758
 
 
759
 
   /**
760
 
    * Invoke the method remotely.
761
 
    *
762
 
    * @param param - payload for the server invoker handler.
763
 
    * @param metadata - any extra metadata that may be needed by the transport (i.e. GET or POST if
764
 
    *        using http invoker) or if need to pass along extra data to the server invoker handler.
765
 
    */
766
 
   public Object invoke(Object param, Map metadata) throws Throwable
767
 
   {
768
 
      return invoke(param, metadata, null);
769
 
   }
770
 
 
771
 
   /**
772
 
    * Will invoke a oneway call to server without a return object. This should be used when not
773
 
    * expecting a return value from the server and wish to achieve higher performance, since the
774
 
    * client will not wait for a return.
775
 
    * <b>
776
 
    * This is done one of two ways. The first is to pass true as the clientSide param.  This will
777
 
    * cause the execution of the remote call to be executed in a new thread on the client side and
778
 
    * will return the calling thread before making call to server side.
779
 
    * <p/>
780
 
    * The second, is to pass false as the clientSide param. This will allow the current calling
781
 
    * thread to make the call to the remote server, at which point, the server side processing of
782
 
    * the thread will be executed on the remote server in a new executing thread.
783
 
    * <p>
784
 
    * NOTE:  The treatment of server side oneway invocations may vary with the transport.  The
785
 
    * client side transport is not required to wait for a reply from the server.  In particular,
786
 
    * the socket and bisocket transports return immediately after writing the invocation.
787
 
    */
788
 
   public void invokeOneway(final Object param, final Map sendPayload, boolean clientSide) 
789
 
      throws Throwable
790
 
   {
791
 
      final Map internalSendPayload = sendPayload == null ? new HashMap() : sendPayload;
792
 
      internalSendPayload.put(ONEWAY_FLAG, "true");
793
 
 
794
 
      if (clientSide)
795
 
      {
796
 
         ThreadPool threadPool = getOnewayThreadPool();
797
 
         Runnable onewayRun = new Runnable()
798
 
         {
799
 
            public void run()
800
 
            {
801
 
               try
802
 
               {
803
 
                  invoke(param, internalSendPayload);
804
 
               }
805
 
               catch (Throwable e)
806
 
               {
807
 
                  // throw away exception since can't get it back to original caller
808
 
                  log.error("Error executing client oneway invocation request: " + param, e);
809
 
               }
810
 
            }
811
 
         };
812
 
         threadPool.run(onewayRun);
813
 
      }
814
 
      else
815
 
      {
816
 
         OnewayInvocation invocation = new OnewayInvocation(param);
817
 
         invoke(invocation, internalSendPayload);
818
 
      }
819
 
   }
820
 
 
821
 
   /**
822
 
    * Returns the callback Connectors with which callbackHandler is registered.
823
 
    */
824
 
   public Set getCallbackConnectors(InvokerCallbackHandler callbackHandler)
825
 
   {
826
 
      return (Set) callbackConnectors.get(callbackHandler);
827
 
   }
828
 
 
829
 
   /**
830
 
    * Gets the timeout used for network i/o in disconnect() and removeListener().
831
 
    */
832
 
   public int getDisconnectTimeout()
833
 
   {
834
 
      return disconnectTimeout;
835
 
   }
836
 
 
837
 
   /**
838
 
    * Sets the timeout used for network i/o in disconnect() and removeListener().
839
 
    */
840
 
   public void setDisconnectTimeout(int disconnectTimeout)
841
 
   {
842
 
      this.disconnectTimeout = disconnectTimeout;
843
 
   }
844
 
 
845
 
   /**
846
 
    * Sets the maximum queue size to use within client pool for one way invocations on the client
847
 
    * side (meaning oneway invocation is handled by thread in this pool and user's call returns
848
 
    * immediately). Default value is MAX_NUM_ONEWAY_THREADS.
849
 
    */
850
 
   public void setMaxOnewayThreadPoolQueueSize(int maxOnewayThreadPoolQueueSize)
851
 
   {
852
 
      this.maxOnewayThreadPoolQueueSize = maxOnewayThreadPoolQueueSize;
853
 
   }
854
 
 
855
 
   /**
856
 
    * Gets the maximum queue size to use within client pool for one way invocations on the client
857
 
    * side (meaning oneway invocation is handled by thread in this pool and user's call returns
858
 
    * immediately). Default value is MAX_NUM_ONEWAY_THREADS.
859
 
    */
860
 
   public int getMaxOnewayThreadPoolQueueSize()
861
 
   {
862
 
      return this.maxOnewayThreadPoolQueueSize;
863
 
   }
864
 
 
865
 
   /**
866
 
    * Sets the maximum number of threads to use within client pool for one way invocations on the
867
 
    * client side (meaning oneway invocation is handled by thread in this pool and user's call
868
 
    * returns immediately). Default value is MAX_NUM_ONEWAY_THREADS.
869
 
    */
870
 
   public void setMaxNumberOfThreads(int numOfThreads)
871
 
   {
872
 
      this.maxNumberThreads = numOfThreads;
873
 
   }
874
 
 
875
 
   /**
876
 
    * Gets the maximum number of threads to use within client pool for one way invocations on the
877
 
    * client side (meaning oneway invocation is handled by thread in this pool and user's call
878
 
    * returns immediately). Default value is MAX_NUM_ONEWAY_THREADS.
879
 
    */
880
 
   public int getMaxNumberOfThreads()
881
 
   {
882
 
      return this.maxNumberThreads;
883
 
   }
884
 
 
885
 
   /**
886
 
    * Gets the thread pool being used for making one way invocations on the client side. If one has
887
 
    * not be specifically set via configuration or call to set it, will always return instance of
888
 
    * org.jboss.util.threadpool.BasicThreadPool.
889
 
    */
890
 
   public ThreadPool getOnewayThreadPool()
891
 
   {
892
 
      synchronized (onewayThreadPoolLock)
893
 
      {
894
 
         if (onewayThreadPool == null)
895
 
         {
896
 
            BasicThreadPool pool = new BasicThreadPool("JBossRemoting Client Oneway");
897
 
            log.debug("created new thread pool: " + pool);
898
 
            Object param = configuration.get(MAX_NUM_ONEWAY_THREADS);
899
 
            if (param instanceof String)
900
 
            {
901
 
               try
902
 
               {
903
 
                  maxNumberThreads = Integer.parseInt((String) param);
904
 
               }
905
 
               catch (NumberFormatException  e)
906
 
               {
907
 
                  log.error("maxNumberThreads parameter has invalid format: " + param);
908
 
               }
909
 
            }
910
 
            else if (param != null)
911
 
            {
912
 
               log.error("maxNumberThreads parameter must be a string in integer format: " + param);
913
 
            }
914
 
 
915
 
            param = configuration.get(MAX_ONEWAY_THREAD_POOL_QUEUE_SIZE);
916
 
 
917
 
            if (param instanceof String)
918
 
            {
919
 
               try
920
 
               {
921
 
                  maxOnewayThreadPoolQueueSize = Integer.parseInt((String) param);
922
 
               }
923
 
               catch (NumberFormatException  e)
924
 
               {
925
 
                  log.error("maxOnewayThreadPoolQueueSize parameter has invalid format: " + param);
926
 
               }
927
 
            }
928
 
            else if (param != null)
929
 
            {
930
 
               log.error("maxOnewayThreadPoolQueueSize parameter must be a string in integer format: " + param);
931
 
            }
932
 
 
933
 
            pool.setMaximumPoolSize(maxNumberThreads);
934
 
 
935
 
            if (maxOnewayThreadPoolQueueSize > 0)
936
 
            {
937
 
               pool.setMaximumQueueSize(maxOnewayThreadPoolQueueSize);
938
 
            }
939
 
            pool.setBlockingMode(BlockingMode.RUN);
940
 
            onewayThreadPool = pool;
941
 
         }
942
 
      }
943
 
      return onewayThreadPool;
944
 
   }
945
 
 
946
 
   /**
947
 
    * Sets the thread pool to be used for making one way invocations on the client side.
948
 
    */
949
 
   public void setOnewayThreadPool(ThreadPool pool)
950
 
   {
951
 
      this.onewayThreadPool = pool;
952
 
   }
953
 
 
954
 
   /**
955
 
    * The socket factory can only be set on the Client before the connect() method has been called.
956
 
    * Otherwise, a runtime exception will be thrown.
957
 
    */
958
 
   public void setSocketFactory(SocketFactory socketFactory)
959
 
   {
960
 
      if(isConnected())
961
 
      {
962
 
         throw new RuntimeException("Cannot set socket factory on Client after " +
963
 
                                    "the connect() method has been called.");
964
 
      }
965
 
 
966
 
      if (invoker != null)
967
 
      {
968
 
         invoker.setSocketFactory(socketFactory);
969
 
      }
970
 
      else
971
 
      {
972
 
         this.socketFactory = socketFactory;
973
 
      }
974
 
   }
975
 
 
976
 
   public SocketFactory getSocketFactory()
977
 
   {
978
 
      if (invoker != null)
979
 
      {
980
 
         return invoker.getSocketFactory();
981
 
      }
982
 
      else
983
 
      {
984
 
         return socketFactory;
985
 
      }
986
 
   }
987
 
 
988
 
   /**
989
 
    * Same as calling invokeOneway(Object param, Map sendPayload, boolean clientSide) with
990
 
    * clientSide param being false and a null sendPayload.
991
 
    */
992
 
   public void invokeOneway(Object param) throws Throwable
993
 
   {
994
 
      invokeOneway(param, null);
995
 
   }
996
 
 
997
 
   /**
998
 
    * Same as calling invokeOneway(Object param, Map sendPayload, boolean clientSide) with
999
 
    * clientSide param being false.
1000
 
    */
1001
 
   public void invokeOneway(Object param, Map sendPayload) throws Throwable
1002
 
   {
1003
 
      invokeOneway(param, sendPayload, false);
1004
 
   }
1005
 
 
1006
 
   /**
1007
 
    * Adds the specified handler as a callback listener for push (async) callbacks. If the transport
1008
 
    * is uni-directional (e.g. http), remoting will automatically poll for callbacks from the server
1009
 
    * and deliver them to the callback handler. If the transport is bi-directional (e.g. multiplex),
1010
 
    * remoting will automatically create a callback server internally and receive and deliver to
1011
 
    * callback handler the callbacks as they are generated on the server. The metadata map passed
1012
 
    * will control configuration for how the callbacks are processed, such as the polling frequency.
1013
 
    */
1014
 
   public void addListener(InvokerCallbackHandler callbackhandler, Map metadata) throws Throwable
1015
 
   {
1016
 
      addListener(callbackhandler, metadata, null);
1017
 
   }
1018
 
 
1019
 
   /**
1020
 
    * Adds the specified handler as a callback listener for push (async) callbacks. If the transport
1021
 
    * is uni-directional (e.g. http), remoting will automatically poll for callbacks from the server
1022
 
    * and deliver them to the callback handler. If the transport is bi-directional (e.g. multiplex),
1023
 
    * remoting will automatically create a callback server internally and receive and deliver to
1024
 
    * callback handler the callbacks as they are generated on the server. The metadata map passed
1025
 
    * will control configuration for how the callbacks are processed, such as the polling frequency.
1026
 
    *
1027
 
    * @param callbackHandlerObject - this object will be included in the Callback object instance
1028
 
    *        passed to the InvokerCallbackHandler specified.
1029
 
    */
1030
 
   public void addListener(InvokerCallbackHandler callbackhandler, Map metadata,
1031
 
                           Object callbackHandlerObject) throws Throwable
1032
 
   {
1033
 
      addListener(callbackhandler, metadata, callbackHandlerObject, false);
1034
 
   }
1035
 
 
1036
 
   /**
1037
 
    * Adds the specific handler as a callback listener for async callbacks. If the transport
1038
 
    * supports bi-directional calls (meaning server can call back to client over same connection
1039
 
    * that was established by the client) or if the serverToClient flag is set to true, a callback
1040
 
    * server will be created internally and the target server will actually send callbacks to the
1041
 
    * client's internal server. Otherwise, the client will simulate push callbacks by internally
1042
 
    * polling for callbacks on the server and then deliver them to the callback handler.
1043
 
    *
1044
 
    * @param serverToClient - if true, will allow server to connect to the client directly (which
1045
 
    *        must be allowed by firewall in front of client unless transport is bi-directional, such
1046
 
    *        as the multiplex transport). If false (and not bi-directional transport), server will
1047
 
    *        not create any new connection to the client.
1048
 
    */
1049
 
   public void addListener(InvokerCallbackHandler callbackhandler, Map metadata,
1050
 
                           Object callbackHandlerObject, boolean serverToClient) throws Throwable
1051
 
   {
1052
 
      InvokerLocator callbackLocator = null;
1053
 
 
1054
 
      if (isConnected())
1055
 
      {
1056
 
         if (callbackhandler != null)
1057
 
         {
1058
 
            boolean isBidirectional = invoker instanceof BidirectionalClientInvoker;
1059
 
 
1060
 
            if (isBidirectional || serverToClient)
1061
 
            {
1062
 
               // setup callback server
1063
 
               String transport = null;
1064
 
               String host = null;
1065
 
               int port = -1;
1066
 
 
1067
 
               // look for config values
1068
 
               if (metadata != null)
1069
 
               {
1070
 
                  transport = (String) metadata.get(CALLBACK_SERVER_PROTOCOL);
1071
 
                  host = (String) metadata.get(CALLBACK_SERVER_HOST);
1072
 
                  String sPort = (String) metadata.get(CALLBACK_SERVER_PORT);
1073
 
                  if (sPort != null)
1074
 
                  {
1075
 
                     try
1076
 
                     {
1077
 
                        port = Integer.parseInt(sPort);
1078
 
                     }
1079
 
                     catch (NumberFormatException e)
1080
 
                     {
1081
 
                        log.warn("Could not set the internal callback server port as " +
1082
 
                                 "configuration value (" + sPort + ") is not a number.");
1083
 
                     }
1084
 
                  }
1085
 
               }
1086
 
               else
1087
 
               {
1088
 
                  metadata = new HashMap();
1089
 
               }
1090
 
               if (transport == null)
1091
 
               {
1092
 
                  transport = invoker.getLocator().getProtocol();
1093
 
                  metadata.put(CALLBACK_SERVER_PROTOCOL, transport);
1094
 
               }
1095
 
               if (host == null)
1096
 
               {
1097
 
                  host = getLocalHost().getHostAddress();
1098
 
                  metadata.put(CALLBACK_SERVER_HOST, host);
1099
 
               }
1100
 
               if (port == -1)
1101
 
               {
1102
 
                  port = PortUtil.findFreePort(host);
1103
 
                  metadata.put(CALLBACK_SERVER_PORT, String.valueOf(port));
1104
 
               }
1105
 
 
1106
 
               if(isBidirectional)
1107
 
               {
1108
 
                  callbackLocator =
1109
 
                     ((BidirectionalClientInvoker)invoker).getCallbackLocator(metadata);
1110
 
               }
1111
 
               else
1112
 
               {
1113
 
                  callbackLocator = new InvokerLocator(transport, host, port, null, metadata);
1114
 
               }
1115
 
               log.debug("starting callback Connector: " + callbackLocator);
1116
 
               Map callbackConfig = new HashMap(configuration);
1117
 
               
1118
 
               if (locator.getParameters() != null)
1119
 
               {
1120
 
                  callbackConfig.putAll(locator.getParameters());
1121
 
               }
1122
 
               
1123
 
               configureCallbackServerSocketFactory(callbackConfig);
1124
 
               Connector callbackServerConnector = new Connector(callbackLocator, callbackConfig);
1125
 
               
1126
 
               synchronized (callbackConnectors)
1127
 
               {
1128
 
                  Set connectors = (Set) callbackConnectors.get(callbackhandler);
1129
 
                  if (connectors == null)
1130
 
                  {
1131
 
                     connectors = new HashSet();
1132
 
                  }
1133
 
                  connectors.add(callbackServerConnector);
1134
 
                  callbackConnectors.put(callbackhandler, connectors);
1135
 
               }
1136
 
 
1137
 
               callbackServerConnector.start();
1138
 
               // have to use the locator from the server as can be modified internally
1139
 
               callbackLocator = callbackServerConnector.getServerInvoker().getLocator();
1140
 
               addCallbackListener(callbackhandler, metadata, callbackLocator, callbackHandlerObject);
1141
 
            }
1142
 
            else
1143
 
            {
1144
 
               if (callbackPollers.get(callbackhandler) != null)
1145
 
               {
1146
 
                  log.debug(callbackhandler + " already registered");
1147
 
                  return;
1148
 
               }
1149
 
               
1150
 
               //need to setup poller to get callbacks from the server
1151
 
               CallbackPoller poller =
1152
 
                  new CallbackPoller(this, callbackhandler, metadata, callbackHandlerObject);
1153
 
               callbackPollers.put(callbackhandler, poller);
1154
 
               addCallbackListener(callbackhandler, metadata, callbackLocator, callbackHandlerObject);
1155
 
               poller.start();
1156
 
            }
1157
 
         }
1158
 
         else
1159
 
         {
1160
 
            throw new NullPointerException("InvokerCallbackHandler to be added as " +
1161
 
                                           "a listener can not be null.");
1162
 
         }
1163
 
      }
1164
 
      else
1165
 
      {
1166
 
         throw new Exception("Can not add callback listener because " +
1167
 
                             "remoting client is not connected to server.");
1168
 
      }
1169
 
   }
1170
 
 
1171
 
   /**
1172
 
    * Adds the specified handler as a callback listener for pull (sync) callbacks. Using this method
1173
 
    * will require the programatic getting of callbacks from the server (they will not be pushed to
1174
 
    * the callback handler automatically).
1175
 
    */
1176
 
   public void addListener(InvokerCallbackHandler callbackHandler) throws Throwable
1177
 
   {
1178
 
      addListener(callbackHandler, (InvokerLocator) null);
1179
 
   }
1180
 
 
1181
 
   /**
1182
 
    * Adds the specified handler as a callback listener for push (async) callbacks. The invoker
1183
 
    * server will then callback on this handler (via the server invoker specified by the
1184
 
    * clientLocator) when it gets a callback from the server handler.
1185
 
    *
1186
 
    * Note: passing a null clientLocator will cause the client invoker's client locator to be set to
1187
 
    * null, which basically converts the mode to be pull (sync) where will require call to get
1188
 
    * callbacks (as will not automatically be pushed to callback handler).
1189
 
    */
1190
 
   public void addListener(InvokerCallbackHandler callbackHandler,
1191
 
                           InvokerLocator clientLocator) throws Throwable
1192
 
   {
1193
 
      addListener(callbackHandler, clientLocator, null);
1194
 
   }
1195
 
 
1196
 
   /**
1197
 
    * Adds the specified handler as a callback listener for push (async) callbacks. The invoker
1198
 
    * server will then callback on this handler (via the server invoker specified by the
1199
 
    * clientLocator) when it gets a callback from the server handler.
1200
 
    *
1201
 
    * Note: passing a null clientLocator will cause the client invoker's client locator to be set to
1202
 
    * null, which basically converts the mode to be pull (sync) where will require call to get
1203
 
    * callbacks (as will not automatically be pushed to callback handler).
1204
 
    *
1205
 
    * @param callbackHandlerObject will be included in the callback object passed upon callback.
1206
 
    */
1207
 
   public void addListener(InvokerCallbackHandler callbackHandler,
1208
 
                           InvokerLocator clientLocator, Object callbackHandlerObject)
1209
 
      throws Throwable
1210
 
   {
1211
 
      if (callbackHandler != null)
1212
 
      {
1213
 
         if (isConnected())
1214
 
         {
1215
 
            addCallbackListener(callbackHandler, null, clientLocator, callbackHandlerObject);
1216
 
         }
1217
 
         else
1218
 
         {
1219
 
            throw new Exception("Can not add callback listener as " +
1220
 
                                "remoting client is not connected to server.");
1221
 
         }
1222
 
      }
1223
 
      else
1224
 
      {
1225
 
         throw new NullPointerException("InvokerCallbackHandler to be added as " +
1226
 
                                        "a listener can not be null.");
1227
 
      }
1228
 
   }
1229
 
 
1230
 
   /**
1231
 
    * Removes callback handler as a callback listener from the server (and client in the case that
1232
 
    * it was setup to receive async callbacks). See addListener().
1233
 
    */
1234
 
   public void removeListener(InvokerCallbackHandler callbackHandler) throws Throwable
1235
 
   {
1236
 
      if (isConnected())
1237
 
      {
1238
 
         if (callbackHandler != null)
1239
 
         {
1240
 
            // first need to see if is push or pull callback (i.e. does have locator associated
1241
 
            // with it)
1242
 
            String listenerId = (String)listeners.get(callbackHandler);
1243
 
            if(listenerId != null)
1244
 
            {
1245
 
               // have a pull callback handler
1246
 
               // If disconnectTimeout == 0, skip network i/o.
1247
 
               if (disconnectTimeout != 0)
1248
 
               {
1249
 
                  Map metadata = new HashMap();
1250
 
                  metadata.put(LISTENER_ID_KEY, listenerId);
1251
 
                  
1252
 
                  if (disconnectTimeout > 0)
1253
 
                     metadata.put(ServerInvoker.TIMEOUT, Integer.toString(disconnectTimeout));
1254
 
 
1255
 
                  try
1256
 
                  {
1257
 
                     invoke(new InternalInvocation(InternalInvocation.REMOVELISTENER, null), metadata);
1258
 
                  }
1259
 
                  catch (Exception e)
1260
 
                  {
1261
 
                     log.debug("unable to remove remote callback handler", e);
1262
 
                  }
1263
 
               }
1264
 
 
1265
 
               // clean up callback poller if one exists
1266
 
               CallbackPoller callbackPoller = (CallbackPoller) callbackPollers.remove(callbackHandler);
1267
 
               if (callbackPoller != null)
1268
 
               {
1269
 
                  callbackPoller.stop();
1270
 
               }
1271
 
 
1272
 
               listeners.remove(callbackHandler);
1273
 
            }
1274
 
            else
1275
 
            {
1276
 
               // have a push callback handler
1277
 
               List holderList = invoker.getClientLocators(sessionId, callbackHandler);
1278
 
               if(holderList != null && holderList.size() > 0)
1279
 
               {
1280
 
                  for(int x = 0; x < holderList.size(); x++)
1281
 
                  {
1282
 
                     AbstractInvoker.CallbackLocatorHolder holder =
1283
 
                        (AbstractInvoker.CallbackLocatorHolder)holderList.get(x);
1284
 
                     listenerId = holder.getListenerId();
1285
 
                     InvokerLocator locator = holder.getLocator();
1286
 
                     Map metadata = new HashMap();
1287
 
                     metadata.put(LISTENER_ID_KEY, listenerId);
1288
 
 
1289
 
                     // If disconnectTimeout == 0, skip network i/o.
1290
 
                     if (disconnectTimeout != 0)
1291
 
                     {
1292
 
                        if (disconnectTimeout > 0)
1293
 
                           metadata.put(ServerInvoker.TIMEOUT, Integer.toString(disconnectTimeout));
1294
 
 
1295
 
                        try
1296
 
                        {
1297
 
                           // now call target server to remove listener
1298
 
                           InternalInvocation ii =
1299
 
                              new InternalInvocation(InternalInvocation.REMOVELISTENER, null);
1300
 
 
1301
 
                           invoke(ii, metadata);
1302
 
                        }
1303
 
                        catch (Exception e)
1304
 
                        {
1305
 
                           log.debug("unable to remove remote callback handler", e);
1306
 
                        }
1307
 
                     }
1308
 
 
1309
 
                     // call to callback server to remove listener
1310
 
                     Client client = new Client(locator, subsystem);
1311
 
                     client.setSessionId(getSessionId());
1312
 
                     client.connect();
1313
 
                     InternalInvocation ii =
1314
 
                        new InternalInvocation(InternalInvocation.REMOVECLIENTLISTENER,
1315
 
                              new Object[]{callbackHandler});
1316
 
 
1317
 
                     client.invoke(ii, metadata);
1318
 
                     client.disconnect();
1319
 
                  }
1320
 
               }
1321
 
            }
1322
 
 
1323
 
            // clean up callback server connectors if any exist
1324
 
            Set connectors = null;
1325
 
            synchronized (callbackConnectors)
1326
 
            {
1327
 
               connectors = (Set) callbackConnectors.remove(callbackHandler);
1328
 
            }
1329
 
 
1330
 
            if (connectors != null)
1331
 
            {
1332
 
               Iterator it = connectors.iterator();
1333
 
               while (it.hasNext())
1334
 
               {
1335
 
                  Connector callbackConnector = (Connector) it.next();
1336
 
                  callbackConnector.stop();
1337
 
                  callbackConnector.destroy();
1338
 
               }
1339
 
            }
1340
 
         }
1341
 
         else
1342
 
         {
1343
 
            throw new NullPointerException("Can not remove null InvokerCallbackHandler listener.");
1344
 
         }
1345
 
      }
1346
 
      else
1347
 
      {
1348
 
         throw new Exception("Can not remove callback listener as " +
1349
 
         "remoting client is not connected to server.");
1350
 
      }
1351
 
   }
1352
 
 
1353
 
   /**
1354
 
    * Gets the callbacks for specified callback handler. The handler is required because an id is
1355
 
    * generated for each handler.  So if have two callback handlers registered with the same server,
1356
 
    * no other way to know for which handler to get the callbacks for.
1357
 
    */
1358
 
   public List getCallbacks(InvokerCallbackHandler callbackHandler) throws Throwable
1359
 
   {
1360
 
      return getCallbacks(callbackHandler, null);
1361
 
   }
1362
 
   
1363
 
   /**
1364
 
    * Gets the callbacks for specified callback handler. The handler is required because an id is
1365
 
    * generated for each handler.  So if have two callback handlers registered with the same server,
1366
 
    * no other way to know for which handler to get the callbacks for.
1367
 
    * 
1368
 
    * The metadata map can be used to set callback blocking mode and blocking timeout
1369
 
    * value.
1370
 
    */
1371
 
   public List getCallbacks(InvokerCallbackHandler callbackHandler, Map metadata) throws Throwable
1372
 
   {
1373
 
      if (callbackHandler != null)
1374
 
      {
1375
 
         String listenerId = (String)listeners.get(callbackHandler);
1376
 
 
1377
 
         if(listenerId != null)
1378
 
         {
1379
 
            if (metadata == null)
1380
 
               metadata = new HashMap();
1381
 
            
1382
 
            metadata.put(LISTENER_ID_KEY, listenerId);
1383
 
            InternalInvocation invocation = new InternalInvocation(InternalInvocation.GETCALLBACKS, null);
1384
 
 
1385
 
            try
1386
 
            {
1387
 
               List response = (List) invoke(invocation, metadata);
1388
 
               return response;
1389
 
            }
1390
 
            catch (MarshalException e)
1391
 
            {
1392
 
               if (e.getCause() != null && e.getCause() instanceof SocketTimeoutException)
1393
 
               {
1394
 
                  if (trace) log.trace(this + ": getCallbacks() timed out: returning empty list");
1395
 
                  return new ArrayList();
1396
 
               }
1397
 
               throw e;
1398
 
            }
1399
 
            finally
1400
 
            {
1401
 
               metadata.remove(LISTENER_ID_KEY);
1402
 
            }
1403
 
         }
1404
 
         else
1405
 
         {
1406
 
            String errorMessage = "Could not find listener id for InvokerCallbackHandler (" +
1407
 
                                  callbackHandler +
1408
 
                                  "), please verify handler has been registered as listener.";
1409
 
 
1410
 
            String errorMode = (String) metadata.get(THROW_CALLBACK_EXCEPTION);
1411
 
            boolean throwError = Boolean.valueOf(errorMode).booleanValue();
1412
 
            
1413
 
            if (throwError)
1414
 
            {
1415
 
               throw new IOException(errorMessage);
1416
 
            }
1417
 
            else
1418
 
            {
1419
 
               log.error(errorMessage);
1420
 
               return null;
1421
 
            }
1422
 
         }
1423
 
      }
1424
 
      else
1425
 
      {
1426
 
         throw new NullPointerException("Can not remove null InvokerCallbackHandler listener.");
1427
 
      }
1428
 
   }
1429
 
 
1430
 
   public int acknowledgeCallback(InvokerCallbackHandler callbackHandler, Callback callback)
1431
 
      throws Throwable
1432
 
   {
1433
 
      return acknowledgeCallback(callbackHandler, callback, null);
1434
 
   }
1435
 
 
1436
 
   public int acknowledgeCallback(InvokerCallbackHandler callbackHandler, Callback callback,
1437
 
                                  Object response) throws Throwable
1438
 
   {
1439
 
      ArrayList callbacks = new ArrayList(1);
1440
 
      callbacks.add(callback);
1441
 
 
1442
 
      ArrayList responses = null;
1443
 
      if (response != null)
1444
 
      {
1445
 
         responses = new ArrayList(1);
1446
 
         responses.add(response);
1447
 
      }
1448
 
 
1449
 
      return acknowledgeCallbacks(callbackHandler, callbacks, responses);
1450
 
   }
1451
 
 
1452
 
   public int acknowledgeCallbacks(InvokerCallbackHandler callbackHandler, List callbacks)
1453
 
      throws Throwable
1454
 
   {
1455
 
      return acknowledgeCallbacks(callbackHandler, callbacks, null);
1456
 
   }
1457
 
 
1458
 
   public int acknowledgeCallbacks(InvokerCallbackHandler callbackHandler, List callbacks,
1459
 
                                   List responses) throws Throwable
1460
 
   {
1461
 
      if (callbackHandler == null)
1462
 
      {
1463
 
         throw new Exception("InvokerCallbackHandler parameter must not be null");
1464
 
      }
1465
 
 
1466
 
      if (callbacks == null)
1467
 
      {
1468
 
         throw new Exception("Callback List parameter must not be null");
1469
 
      }
1470
 
 
1471
 
      if (responses != null && responses.size() != callbacks.size())
1472
 
      {
1473
 
         throw new Exception("Callback response list must be (1) null " +
1474
 
                             "or (2) the same size as callback list");
1475
 
      }
1476
 
 
1477
 
      if (callbacks.size() == 0)
1478
 
      {
1479
 
         return 0;
1480
 
      }
1481
 
 
1482
 
      if (isConnected())
1483
 
      {
1484
 
         ArrayList callbackIds = new ArrayList(callbacks.size());
1485
 
         Iterator idsIterator = callbacks.iterator();
1486
 
         ArrayList responseList = null;
1487
 
         Iterator responseIterator = null;
1488
 
 
1489
 
         if (responses != null)
1490
 
         {
1491
 
            responseList = new ArrayList(responses.size());
1492
 
            responseIterator = responses.iterator();
1493
 
         }
1494
 
 
1495
 
         Callback callback = null;
1496
 
         Object response = null;
1497
 
         String listenerId = null;
1498
 
 
1499
 
         for (int i = 0; i < callbacks.size(); i++)
1500
 
         {
1501
 
            callback = (Callback) idsIterator.next();
1502
 
 
1503
 
            if (responseIterator != null)
1504
 
            {
1505
 
               response = responseIterator.next();
1506
 
            }
1507
 
 
1508
 
            Map returnPayload = callback.getReturnPayload();
1509
 
 
1510
 
            if (returnPayload != null)
1511
 
            {
1512
 
               Object callbackId = returnPayload.get(ServerInvokerCallbackHandler.CALLBACK_ID);
1513
 
               if (callbackId != null)
1514
 
               {
1515
 
                  callbackIds.add(callbackId);
1516
 
 
1517
 
                  if (responseIterator != null)
1518
 
                  {
1519
 
                     responseList.add(response);
1520
 
                  }
1521
 
 
1522
 
                  String nextListenerId = (String) returnPayload.get(LISTENER_ID_KEY);
1523
 
 
1524
 
                  if (nextListenerId == null)
1525
 
                  {
1526
 
                     throw new Exception("Cannot acknowledge callbacks: " +
1527
 
                                         "callback " + callbackId + " has null listener id");
1528
 
                  }
1529
 
 
1530
 
                  if (i == 0)
1531
 
                  {
1532
 
                     listenerId = nextListenerId;
1533
 
                  }
1534
 
                  else
1535
 
                  {
1536
 
                     if (!listenerId.equals(nextListenerId))
1537
 
                        throw new Exception("Cannot acknowledge callbacks: " +
1538
 
                                            "all must be from same server side callback handler");
1539
 
                  }
1540
 
               }
1541
 
               else
1542
 
               {
1543
 
                  log.error("Cannot acknowledge callback: callback id " +
1544
 
                            "is missing from return payload");
1545
 
               }
1546
 
            }
1547
 
            else
1548
 
            {
1549
 
               log.error("Cannot acknowledge callback: return payload is null");
1550
 
            }
1551
 
         }
1552
 
 
1553
 
         if (callbackIds.size() == 0)
1554
 
         {
1555
 
            return 0;
1556
 
         }
1557
 
 
1558
 
         Map metadata = new HashMap();
1559
 
         if(listenerId != null)
1560
 
         {
1561
 
            metadata.put(LISTENER_ID_KEY, listenerId);
1562
 
         }
1563
 
         else
1564
 
         {
1565
 
            throw new Exception("Could not find listener id for InvokerCallbackHandler (" +
1566
 
                                callbackHandler + "), please verify handler " +
1567
 
                                "has been registered as listener.");
1568
 
         }
1569
 
 
1570
 
         Object[] params = new Object[] {callbackIds, responseList};
1571
 
         InternalInvocation invocation =
1572
 
            new InternalInvocation(InternalInvocation.ACKNOWLEDGECALLBACK, params);
1573
 
         invoke(invocation, metadata);
1574
 
         return callbackIds.size();
1575
 
      }
1576
 
      else
1577
 
      {
1578
 
         throw new Exception("Can not acknowledge Callback due to not being connected to server.");
1579
 
      }
1580
 
   }
1581
 
 
1582
 
   /**
1583
 
    * Sets the marshaller implementation that should be used by the client invoker (transport). This
1584
 
    * overrides the client's default marshaller (or any set within configuration).
1585
 
    */
1586
 
   public void setMarshaller(Marshaller marshaller)
1587
 
   {
1588
 
      if (isConnected())
1589
 
      {
1590
 
         if (marshaller != null)
1591
 
         {
1592
 
            invoker.setMarshaller(marshaller);
1593
 
         }
1594
 
         else
1595
 
         {
1596
 
            throw new NullPointerException("Can not set Marshaller with a null value.");
1597
 
         }
1598
 
      }
1599
 
      else
1600
 
      {
1601
 
         throw new RuntimeException("Can not set remoting client Marshaller when not connected.");
1602
 
      }
1603
 
   }
1604
 
 
1605
 
   /**
1606
 
    * Sets the unmarshaller implementation that should be used by the client invoker (transport).
1607
 
    * This overrides the client's default unmarshaller (or any set within configuration).
1608
 
    */
1609
 
   public void setUnMarshaller(UnMarshaller unmarshaller)
1610
 
   {
1611
 
      if (isConnected())
1612
 
      {
1613
 
         if (unmarshaller != null)
1614
 
         {
1615
 
 
1616
 
            invoker.setUnMarshaller(unmarshaller);
1617
 
         }
1618
 
         else
1619
 
         {
1620
 
            throw new NullPointerException("Can not set UnMarshaller to null value.");
1621
 
         }
1622
 
      }
1623
 
      else
1624
 
      {
1625
 
         throw new RuntimeException("Can not set remoting client UnMarhshaller when not connected.");
1626
 
      }
1627
 
   }
1628
 
 
1629
 
   /**
1630
 
    * Takes an inputstream and wraps a server around. Then calls the target remoting server and
1631
 
    * passes a proxy for an inputstream to the server's handler. When the server handler calls on
1632
 
    * this proxy, it will call back on this server wrapped around this inputstream.
1633
 
    *
1634
 
    * @param param - invocation payload.
1635
 
    *
1636
 
    * @return the return value from the invocation.
1637
 
    * @throws Throwable
1638
 
    */
1639
 
   public Object invoke(InputStream inputStream, Object param) throws Throwable
1640
 
   {
1641
 
      StreamServer streamServer = new StreamServer(inputStream);
1642
 
      String locator = streamServer.getInvokerLocator();
1643
 
 
1644
 
      // now call on target server and pass locator for stream callbacks
1645
 
      InvocationRequest invocationRequest =
1646
 
         new InvocationRequest(sessionId, subsystem, param, null, null, null);
1647
 
      return invoke(new InternalInvocation(InternalInvocation.ADDSTREAMCALLBACK,
1648
 
                                           new Object[]{locator, invocationRequest}), null);
1649
 
   }
1650
 
 
1651
 
   /**
1652
 
    * Takes an inputstream and wraps a server around. Then calls the target remoting server and
1653
 
    * passes a proxy for an inputstream to the server's handler. When the server handler calls on
1654
 
    * this proxy, it will call back on this server wrapped around this inputstream. The Connector
1655
 
    * passed is expected to have already been started and will have the stream handler added with
1656
 
    * subsystem of 'stream'. Also note that the Connector passed will not be stopped when/if the
1657
 
    * server calls to close the input stream.
1658
 
    *
1659
 
    * @param param - invocation payload.
1660
 
    *
1661
 
    * @return the return value from the invocation
1662
 
    */
1663
 
   public Object invoke(InputStream inputStream, Object param, Connector streamConnector)
1664
 
      throws Throwable
1665
 
   {
1666
 
      StreamServer streamServer = new StreamServer(inputStream, streamConnector);
1667
 
      String locator = streamServer.getInvokerLocator();
1668
 
 
1669
 
      // now call on target server and pass locator for stream callbacks
1670
 
      InvocationRequest invocationRequest =
1671
 
         new InvocationRequest(sessionId, subsystem, param, null, null, null);
1672
 
 
1673
 
      return invoke(new InternalInvocation(InternalInvocation.ADDSTREAMCALLBACK,
1674
 
                                           new Object[]{locator, invocationRequest}), null);
1675
 
   }
1676
 
 
1677
 
   /**
1678
 
    * Takes an inputstream and wraps a server around. Then calls the target remoting server and
1679
 
    * passes proxy for an inputstream to the server's handler. When the server handle calls on this
1680
 
    * proxy, it will call back on this server wrapped around this inputstream. The InvokerLocator
1681
 
    * passed is used to create the internal Connector used to receive the calls from the server
1682
 
    * side.
1683
 
    */
1684
 
   public Object invoke(InputStream inputStream, Object param, InvokerLocator streamServerLocator)
1685
 
      throws Throwable
1686
 
   {
1687
 
      StreamServer streamServer = new StreamServer(inputStream, streamServerLocator);
1688
 
      String locator = streamServer.getInvokerLocator();
1689
 
 
1690
 
      // now call on target server and pass locator for stream callbacks
1691
 
      InvocationRequest invocationRequest =
1692
 
         new InvocationRequest(sessionId, subsystem, param, null, null, null);
1693
 
      return invoke(new InternalInvocation(InternalInvocation.ADDSTREAMCALLBACK,
1694
 
                                           new Object[]{locator, invocationRequest}), null);
1695
 
   }
1696
 
 
1697
 
   /**
1698
 
    * @return the ping period (in ms) this client's connection validator is configured with. If the
1699
 
    *         client doesn't ping (on account of connection validator not being installed, or
1700
 
    *         stopped), returns -1.
1701
 
    */
1702
 
   public long getPingPeriod()
1703
 
   {
1704
 
      if (connectionValidator == null)
1705
 
      {
1706
 
         return -1;
1707
 
      }
1708
 
 
1709
 
      return connectionValidator.getPingPeriod();
1710
 
   }
1711
 
 
1712
 
   /**
1713
 
    * @return the lease period (in ms) if the client has an active leasing mechanism with the server
1714
 
    *         or -1 otherwise.
1715
 
    */
1716
 
   public long getLeasePeriod()
1717
 
   {
1718
 
      if (invoker == null)
1719
 
      {
1720
 
         return -1;
1721
 
      }
1722
 
 
1723
 
      return invoker.getLeasePeriod(sessionId);
1724
 
   }
1725
 
   
1726
 
   /**
1727
 
    * Returns an InetAddress for the client machine as seen by the server machine.
1728
 
    * @return an InetAddress for the client machine as seen by the server machine.
1729
 
    * @throws Throwable
1730
 
    */
1731
 
   public InetAddress getAddressSeenByServer() throws Throwable
1732
 
   {
1733
 
      return (InetAddress) invoke("$GET_CLIENT_LOCAL_ADDRESS$");
1734
 
   }
1735
 
 
1736
 
   public String toString()
1737
 
   {
1738
 
      return "Client[" + System.identityHashCode(this) + ":" + sessionId + "]";
1739
 
   }
1740
 
 
1741
 
   // Package protected ----------------------------------------------------------------------------
1742
 
 
1743
 
   void notifyListeners()
1744
 
   {
1745
 
      synchronized (connectionValidatorLock)
1746
 
      {
1747
 
         log.debug(this + " entering notifyListeners(): " + connectionValidator);
1748
 
         if (connectionValidator != null)
1749
 
         {
1750
 
            synchronized (connectionValidator)
1751
 
            {
1752
 
               if (connectionValidator.isStopped())
1753
 
               {
1754
 
                  if (trace) log.trace(this + ": " + connectionValidator + " is stopped");
1755
 
               }
1756
 
               else
1757
 
               {
1758
 
                  if (trace) log.trace(this + ": " + connectionValidator + " is not stopped");
1759
 
                  if (trace) log.trace(this + " calling connectionValidator.notifyListeners()");
1760
 
                  connectionValidator.notifyListeners(new Exception("Could not connect to server!"));
1761
 
                  Iterator it = connectionListeners.iterator();
1762
 
                  while (it.hasNext())
1763
 
                  {
1764
 
                     ConnectionListener listener = (ConnectionListener) it.next();
1765
 
                     connectionValidator.removeConnectionListener(this, listener);
1766
 
                  }
1767
 
                  if (connectionValidators.remove(connectionValidatorKey) != null)
1768
 
                  {
1769
 
                     if (trace) log.trace(this + ".notifyAndDisconnect() removed from static map: " + connectionValidator);
1770
 
                  }
1771
 
               }
1772
 
            }
1773
 
            connectionValidator = null;
1774
 
            connectionValidatorKey = null;
1775
 
         }
1776
 
         
1777
 
         log.debug(this + " leaving notifyListeners()");
1778
 
      }
1779
 
   }
1780
 
   
1781
 
   // Protected ------------------------------------------------------------------------------------
1782
 
 
1783
 
   // Private --------------------------------------------------------------------------------------
1784
 
 
1785
 
   private void connect(ClientInvoker invoker, ConnectionListener listener, Map metadata)
1786
 
   {
1787
 
      if (invoker != null)
1788
 
      {
1789
 
         invoker.connect();
1790
 
         try
1791
 
         {
1792
 
            setupClientLease(invoker, listener, metadata);
1793
 
         }
1794
 
         catch (Throwable throwable)
1795
 
         {
1796
 
            RuntimeException e = new CannotConnectException("Error setting up client lease upon performing connect.");
1797
 
            e.initCause(throwable);
1798
 
            throw e;
1799
 
         }
1800
 
         log.debug(this + " connected to " + locator);
1801
 
      }
1802
 
      else
1803
 
      {
1804
 
         throw new RuntimeException("Client invoker is null (may have used void constructor " +
1805
 
                                    "for Client, which should only be used for Externalization.");
1806
 
      }
1807
 
   }
1808
 
 
1809
 
   private void setupClientLease(ClientInvoker invoker, ConnectionListener listener, Map metadata) throws Throwable
1810
 
   {
1811
 
      long leasePeriod = -1;
1812
 
      boolean enableLease = false;
1813
 
 
1814
 
      // start with checking the locator URL for hint as to if should do initial lease ping
1815
 
      if (invoker != null)
1816
 
      {
1817
 
         if (invoker instanceof LocalClientInvoker)
1818
 
         {
1819
 
            // no need to continue as won't do client lease when is local (JBREM-382)
1820
 
            return;
1821
 
         }
1822
 
 
1823
 
         InvokerLocator locator = invoker.getLocator();
1824
 
         Map locatorParams = locator.getParameters();
1825
 
         if (locatorParams != null)
1826
 
         {
1827
 
            String leaseValue = (String)locatorParams.get(InvokerLocator.CLIENT_LEASE);
1828
 
            if (leaseValue != null && leaseValue.length() > 0)
1829
 
            {
1830
 
               enableLease = Boolean.valueOf(leaseValue).booleanValue();
1831
 
            }
1832
 
 
1833
 
            String leasePeriodValue = (String)locatorParams.get(InvokerLocator.CLIENT_LEASE_PERIOD);
1834
 
            if (leasePeriodValue != null && leasePeriodValue.length() > 0)
1835
 
            {
1836
 
               try
1837
 
               {
1838
 
                  leasePeriod = Long.parseLong(leasePeriodValue);
1839
 
               }
1840
 
               catch (NumberFormatException e)
1841
 
               {
1842
 
                  log.warn("Could not convert client lease period value (" +
1843
 
                           leasePeriodValue + ") to a number.");
1844
 
               }
1845
 
            }
1846
 
         }
1847
 
      }
1848
 
      else
1849
 
      {
1850
 
         throw new RuntimeException("Can not set up client lease as client invoker is null.");
1851
 
      }
1852
 
 
1853
 
      if (configuration != null)
1854
 
      {
1855
 
         Object val = configuration.get(ENABLE_LEASE);
1856
 
 
1857
 
         if (val != null)
1858
 
         {
1859
 
            if (val instanceof Boolean)
1860
 
            {
1861
 
               enableLease = ((Boolean)val).booleanValue();
1862
 
            }
1863
 
            else if (val instanceof String)
1864
 
            {
1865
 
               enableLease = Boolean.valueOf((String)val).booleanValue();
1866
 
            }
1867
 
            else
1868
 
            {
1869
 
               log.warn("Can not evaluate " + ENABLE_LEASE + " value (" +
1870
 
                         val + ") as a boolean type.");
1871
 
            }
1872
 
         }
1873
 
 
1874
 
         String leasePeriodValue = (String)configuration.get(InvokerLocator.CLIENT_LEASE_PERIOD);
1875
 
 
1876
 
         if (leasePeriodValue != null && leasePeriodValue.length() > 0)
1877
 
         {
1878
 
            try
1879
 
            {
1880
 
               leasePeriod = Long.parseLong(leasePeriodValue);
1881
 
            }
1882
 
            catch (NumberFormatException e)
1883
 
            {
1884
 
               log.warn("Could not convert client lease period value (" +
1885
 
                         leasePeriodValue + ") to a number.");
1886
 
            }
1887
 
         }
1888
 
      }
1889
 
 
1890
 
      if (trace) log.trace(this + " enableLease: " + enableLease);
1891
 
      if (enableLease)
1892
 
      {
1893
 
         Map temp = new HashMap(configuration);
1894
 
         if (metadata != null)
1895
 
         {
1896
 
             temp.putAll(metadata);
1897
 
         }
1898
 
         if (useClientConnectionIdentity)
1899
 
         {
1900
 
            temp.put(CLIENT, this);
1901
 
            temp.put(CONNECTION_LISTENER, listener);
1902
 
         }
1903
 
         if (trace) log.trace(this + " calling MicroRemoteClientInvoker.establishLease()");
1904
 
         invoker.establishLease(sessionId, temp, leasePeriod);
1905
 
      }
1906
 
      else if (listener != null)
1907
 
      {
1908
 
          addConnectionListener(listener, metadata);
1909
 
      }
1910
 
   }
1911
 
 
1912
 
   private Object invoke(Object param, Map metadata, InvokerLocator callbackServerLocator)
1913
 
         throws Throwable
1914
 
   {
1915
 
      if (isConnected())
1916
 
      {
1917
 
         return invoker.invoke(new InvocationRequest(sessionId, subsystem, param,
1918
 
                                                     metadata, null, callbackServerLocator));
1919
 
      }
1920
 
      else
1921
 
      {
1922
 
         throw new Exception("Can not make remoting client invocation " +
1923
 
                             "due to not being connected to server.");
1924
 
      }
1925
 
   }
1926
 
 
1927
 
   private void addCallbackListener(InvokerCallbackHandler callbackhandler, Map metadata,
1928
 
                                    InvokerLocator callbackLocator, Object callbackHandlerObject)
1929
 
         throws Throwable
1930
 
   {
1931
 
      // if callback locator is null, then is pull callbacks and need to track callback handler
1932
 
      // per Client (not by client invoker).
1933
 
      if (callbackLocator == null)
1934
 
      {
1935
 
         String listenerId = generateListenerId(callbackhandler);
1936
 
 
1937
 
         // if listenerId is null, means this Client has already had the callbackhanler reference
1938
 
         // registered as a listener, so no need to add it again.
1939
 
         if (listenerId != null)
1940
 
         {
1941
 
            Map internalMetadata = new HashMap();
1942
 
            internalMetadata.put(LISTENER_ID_KEY, listenerId);
1943
 
            if(metadata != null)
1944
 
            {
1945
 
               internalMetadata.putAll(metadata);
1946
 
            }
1947
 
            // now call server to add listener
1948
 
            invoke(new InternalInvocation(InternalInvocation.ADDLISTENER, null),
1949
 
                   internalMetadata, callbackLocator);
1950
 
         }
1951
 
      }
1952
 
      else
1953
 
      {
1954
 
         // is going to be push callbacks which means callback server locator involved.
1955
 
         // will have to delegate to client invoker.
1956
 
         String listenerId = invoker.addClientLocator(sessionId, callbackhandler, callbackLocator);
1957
 
 
1958
 
         if (listenerId != null)
1959
 
         {
1960
 
 
1961
 
            Map internalMetadata = new HashMap();
1962
 
            internalMetadata.put(LISTENER_ID_KEY, listenerId);
1963
 
            if(metadata != null)
1964
 
            {
1965
 
               internalMetadata.putAll(metadata);
1966
 
            }
1967
 
 
1968
 
            Client client = new Client(callbackLocator, subsystem);
1969
 
            client.setSessionId(getSessionId());
1970
 
            client.connect();
1971
 
 
1972
 
            try
1973
 
            {
1974
 
               InternalInvocation i =
1975
 
                  new InternalInvocation(InternalInvocation.ADDCLIENTLISTENER,
1976
 
                                         new Object[]{callbackhandler, callbackHandlerObject});
1977
 
 
1978
 
               client.invoke(i, internalMetadata);
1979
 
            }
1980
 
            finally
1981
 
            {
1982
 
               client.disconnect();
1983
 
            }
1984
 
 
1985
 
            // now call server to add listener
1986
 
            invoke(new InternalInvocation(InternalInvocation.ADDLISTENER, null),
1987
 
                   internalMetadata, callbackLocator);
1988
 
         }
1989
 
      }
1990
 
   }
1991
 
 
1992
 
   private String generateListenerId(InvokerCallbackHandler callbackhandler)
1993
 
   {
1994
 
      String listenerId = null;
1995
 
      Object obj = listeners.get(callbackhandler);
1996
 
      if(obj == null)
1997
 
      {
1998
 
         listenerId = new GUID().toString();
1999
 
         listeners.put(callbackhandler, listenerId);
2000
 
      }
2001
 
      return listenerId;
2002
 
   }
2003
 
   
2004
 
   private void processParameters()
2005
 
   {
2006
 
      Map params = new HashMap();
2007
 
      if (configuration != null)
2008
 
         params.putAll(configuration);
2009
 
      if (locator.getParameters() != null)
2010
 
         params.putAll(locator.getParameters());
2011
 
      
2012
 
      Object param = params.get(INVOKER_DESTRUCTION_DELAY);
2013
 
      if (param instanceof String)
2014
 
      {
2015
 
         try
2016
 
         {
2017
 
            invokerDestructionDelay = Integer.parseInt((String) param);
2018
 
            log.debug(this + " setting invokerDestructionDelay to " + invokerDestructionDelay);
2019
 
         }
2020
 
         catch (NumberFormatException  e)
2021
 
         {
2022
 
            log.error("invokerDestructionDelay parameter has invalid format: " + param);
2023
 
         }
2024
 
      }
2025
 
      else if (param != null)
2026
 
      {
2027
 
         log.error("invokerDestructionDelay parameter must be a string in integer format: " + param);
2028
 
      }
2029
 
      
2030
 
      param = configuration.get(Remoting.USE_CLIENT_CONNECTION_IDENTITY);
2031
 
      if (param instanceof String)
2032
 
      {
2033
 
         useClientConnectionIdentity = Boolean.valueOf((String) param).booleanValue();
2034
 
      }
2035
 
      else if (param != null)
2036
 
      {
2037
 
         log.warn("value of " + Remoting.USE_CLIENT_CONNECTION_IDENTITY + " must be a String: " + param); 
2038
 
      }
2039
 
      else
2040
 
      {
2041
 
         if (locator.getParameters() != null)
2042
 
         {
2043
 
            param = locator.getParameters().get(Remoting.USE_CLIENT_CONNECTION_IDENTITY);
2044
 
            if (param != null)
2045
 
            {
2046
 
               useClientConnectionIdentity = Boolean.valueOf((String) param).booleanValue();
2047
 
               this.configuration.put(Remoting.USE_CLIENT_CONNECTION_IDENTITY, param);
2048
 
            }
2049
 
         }
2050
 
      }
2051
 
      
2052
 
      PortUtil.updateRange(params);
2053
 
   }
2054
 
 
2055
 
   private void configureCallbackServerSocketFactory(Map map) throws Exception
2056
 
   {
2057
 
      if (InvokerRegistry.isSSLSupported(locator.getProtocol()) &&
2058
 
          !map.containsKey(Remoting.CUSTOM_SERVER_SOCKET_FACTORY) &&
2059
 
          !map.containsKey(ServerInvoker.SERVER_SOCKET_FACTORY) &&
2060
 
          !map.containsKey(SSLSocketBuilder.REMOTING_SERVER_SOCKET_USE_CLIENT_MODE))
2061
 
         map.put(SSLSocketBuilder.REMOTING_SERVER_SOCKET_USE_CLIENT_MODE, "true");
2062
 
   }
2063
 
 
2064
 
   // Inner classes --------------------------------------------------------------------------------
2065
 
   class InvokerDestructionTimerTask extends TimerTask
2066
 
   {
2067
 
      private WeakReference ref;
2068
 
      
2069
 
      public InvokerDestructionTimerTask(ClientInvoker invoker)
2070
 
      {
2071
 
         ref = new WeakReference(invoker);
2072
 
      }
2073
 
      
2074
 
      public void run()
2075
 
      {
2076
 
         ClientInvoker invoker = (ClientInvoker) ref.get();
2077
 
         log.trace(this + " calling InvokerRegistry.destroyClientInvoker() for " + invoker);
2078
 
         InvokerRegistry.destroyClientInvoker(invoker.getLocator(), configuration);
2079
 
         ref.clear();
2080
 
         ref = null;
2081
 
      }
2082
 
   }
2083
 
   
2084
 
   static class ConnectionValidatorKey
2085
 
   {
2086
 
      private ClientInvoker invoker;
2087
 
      private Map metadata;
2088
 
      
2089
 
      ConnectionValidatorKey(ClientInvoker invoker, Map metadata)
2090
 
      {
2091
 
         this.invoker = invoker;
2092
 
         this.metadata = metadata;
2093
 
      }
2094
 
      
2095
 
      public boolean equals(Object o)
2096
 
      {
2097
 
         if (o == null)
2098
 
            return false;
2099
 
         if (! (o instanceof ConnectionValidatorKey))
2100
 
            return false;
2101
 
         ConnectionValidatorKey holder = (ConnectionValidatorKey) o;
2102
 
         boolean metadataEquals = (metadata == null && holder.metadata == null) || metadata.equals(holder.metadata); 
2103
 
         return invoker == holder.invoker && metadataEquals;
2104
 
      }
2105
 
      
2106
 
      public int hashCode()
2107
 
      {
2108
 
         return invoker.hashCode() * metadata.hashCode();
2109
 
      }
2110
 
   }
2111
 
   
2112
 
   static private InetAddress getLocalHost() throws UnknownHostException
2113
 
   {
2114
 
      if (SecurityUtility.skipAccessControl())
2115
 
      {
2116
 
         try
2117
 
         {
2118
 
            return InetAddress.getLocalHost();
2119
 
         }
2120
 
         catch (IOException e)
2121
 
         {
2122
 
            return InetAddress.getByName("127.0.0.1");
2123
 
         }
2124
 
      }
2125
 
 
2126
 
      try
2127
 
      {
2128
 
         return (InetAddress) AccessController.doPrivileged( new PrivilegedExceptionAction()
2129
 
         {
2130
 
            public Object run() throws IOException
2131
 
            {
2132
 
               try
2133
 
               {
2134
 
                  return InetAddress.getLocalHost();
2135
 
               }
2136
 
               catch (IOException e)
2137
 
               {
2138
 
                  return InetAddress.getByName("127.0.0.1");
2139
 
               }
2140
 
            }
2141
 
         });
2142
 
      }
2143
 
      catch (PrivilegedActionException e)
2144
 
      {
2145
 
         throw (UnknownHostException) e.getCause();
2146
 
      }
2147
 
   }
2148
 
}