1
package org.jboss.remoting;
3
import org.jboss.logging.Logger;
4
import org.jboss.remoting.transport.ClientInvoker;
5
import java.util.HashMap;
6
import java.util.Iterator;
8
import java.util.Timer;
9
import java.util.TimerTask;
11
import java.util.concurrent.ConcurrentHashMap;
14
* Internal agent class to ping the remote server to keep lease alive.
16
* @author <a href="mailto:telrod@e2technologies.net">Tom Elrod</a>
17
* @author <a href="mailto:ovidiu@ejboss.org">Ovidiu Feodorov</a>
19
public class LeasePinger
21
// Constants ------------------------------------------------------------------------------------
23
private static final Logger log = Logger.getLogger(LeasePinger.class);
25
public static final long DEFAULT_LEASE_PERIOD = 5000;
26
public static final int DEFAULT_DISCONNECT_TIMEOUT = -1;
27
public static final String LEASE_PINGER_TIMEOUT = "leasePingerTimeout";
29
static final String LEASE_PINGER_ID = "leasePingerId";
30
static final String TIME_STAMP = "timeStamp";
32
// Static ---------------------------------------------------------------------------------------
34
private static boolean trace = log.isTraceEnabled();
36
private static Timer timer = new Timer(true);
38
// Attributes -----------------------------------------------------------------------------------
40
private long defaultPingPeriod = -1;
42
private ClientInvoker invoker = null;
43
private String invokerSessionID = null;
45
private Map clientSessionIds = new ConcurrentHashMap();
46
private Map clients = new ConcurrentHashMap();
47
private TimerTask timerTask = null;
49
private long pingPeriod = -1;
50
private int disconnectTimeout = DEFAULT_DISCONNECT_TIMEOUT;
51
private int leasePingerTimeout = -1;
53
// The following variables exist for testing purposes.
54
private boolean pingInvoked;
55
private boolean pingSucceeded;
57
private String leasePingerId;
58
private boolean useClientConnectionIdentity;
60
// Constructors ---------------------------------------------------------------------------------
62
public LeasePinger(ClientInvoker invoker, String invokerSessionID, long defaultLeasePeriod)
64
this(invoker, invokerSessionID, defaultLeasePeriod, null);
67
public LeasePinger(ClientInvoker invoker, String invokerSessionID, long defaultLeasePeriod, Map config)
69
this.invoker = invoker;
70
this.invokerSessionID = invokerSessionID;
71
this.pingPeriod = defaultLeasePeriod;
72
this.defaultPingPeriod = defaultLeasePeriod;
76
Object o = config.get(LEASE_PINGER_TIMEOUT);
79
if (o instanceof String)
83
leasePingerTimeout = Integer.valueOf((String) o).intValue();
85
catch (NumberFormatException e)
87
log.warn("leasePingerTimeout parameter must represent an int: " + o);
92
log.warn("leasePingerTimeout parameter must be a String representing an int");
98
// Public ---------------------------------------------------------------------------------------
100
public void startPing()
102
if(trace) { log.trace(this + " starting lease timer with ping period of " + pingPeriod); }
104
timerTask = new LeaseTimerTask(this);
108
timer.schedule(timerTask, pingPeriod, pingPeriod);
110
catch (IllegalStateException e)
112
log.debug("Unable to schedule TimerTask on existing Timer", e);
113
timer = new Timer(true);
114
timer.schedule(timerTask, pingPeriod, pingPeriod);
118
public void stopPing()
120
if(trace) { log.trace(this + " stopping lease timer"); }
122
if (timerTask != null)
127
if (useClientConnectionIdentity)
129
Iterator it = clients.values().iterator();
132
Client client = (Client) it.next();
133
if (trace) log.trace(this + " calling " + client + ".notifyAndDisconnect()");
134
client.notifyListeners();
141
// sending request map with no ClientHolders will indicate to server
142
// that is full disconnect (for client invoker)
143
HashMap metadata = null;
145
// If disconnectTimeout == 0, skip network i/o.
146
if (trace) log.trace(this + ": disconnectTimeout: " + disconnectTimeout);
147
if (disconnectTimeout != 0)
149
if (disconnectTimeout > 0)
151
metadata = new HashMap(1);
152
metadata.put(ServerInvoker.TIMEOUT, Integer.toString(disconnectTimeout));
154
InvocationRequest ir =
155
new InvocationRequest(invokerSessionID, null, "$DISCONNECT$", metadata, null, null);
159
catch (Throwable throwable)
161
RuntimeException e = new RuntimeException("Error tearing down lease with server.");
162
e.initCause(throwable);
168
log.trace(this + " shut down");
169
if (!clientSessionIds.isEmpty())
171
log.trace(this + " " + clientSessionIds.size() + " remaining clients:");
172
Iterator it = clientSessionIds.keySet().iterator();
175
log.trace(this + ": " + it.next());
177
clientSessionIds.clear();
181
log.trace(this + " No remaining clients");
187
public void addClient(String sessionID, Map configuration, long leasePeriod)
189
if (leasePeriod <= 0)
191
leasePeriod = defaultPingPeriod;
194
if(trace) { log.trace(this + " adding new client with session ID " + sessionID + " and lease period " + leasePeriod); }
196
if (useClientConnectionIdentity)
198
Client client = (Client) configuration.remove(Client.CLIENT);
201
clients.put(sessionID, client);
205
ClientHolder newClient = new ClientHolder(sessionID, configuration, leasePeriod);
206
clientSessionIds.put(sessionID, newClient);
214
log.debug(this + " failed to ping to server", t);
215
log.warn(this + " failed to ping to server: " + t.getMessage());
216
throw new RuntimeException(t);
218
// if new client lease period is less than the current ping period, need to refresh to new one
219
if (leasePeriod < pingPeriod)
221
pingPeriod = leasePeriod;
223
// don't want to call stopPing() as that will send disconnect for client invoker
224
if (timerTask != null)
233
public boolean removeClient(String sessionID)
235
boolean isLastClientLease = false;
237
if(trace) { log.trace(this + " removing client with session ID " + sessionID); }
239
// Don't remove holder until after client has been removed from server side Lease, to
240
// avoid a race with LeaseTimerTask sending a PING without the Client being removed.
241
ClientHolder holder = (ClientHolder)clientSessionIds.get(sessionID);
245
// send disconnect for this client
248
Map clientMap = new HashMap();
249
clientMap.put(ClientHolder.CLIENT_HOLDER_KEY, holder);
251
// If disconnectTimeout == 0, skip network i/o.
252
if (disconnectTimeout != 0)
254
if (disconnectTimeout > 0)
255
clientMap.put(ServerInvoker.TIMEOUT, Integer.toString(disconnectTimeout));
257
InvocationRequest ir = new InvocationRequest(invokerSessionID, null, "$DISCONNECT$",
258
clientMap, null, null);
261
if(trace) { log.trace(this + " sent out disconnect message to server for lease tied to client with session ID " + sessionID); }
264
catch (Throwable throwable)
266
log.debug(this + " failed sending disconnect for client lease for " +
267
"client with session ID " + sessionID);
270
clientSessionIds.remove(sessionID);
271
if (useClientConnectionIdentity)
273
clients.remove(sessionID);
278
log.debug(this + " tried to remove lease for client with session ID " + sessionID +
279
", but no such lease was found: probably it was registered with an older LeasePinger");
282
if (clientSessionIds.isEmpty())
284
isLastClientLease = true;
285
if(trace) { log.trace(this + " has no more client leases"); }
289
// now need to see if any of the other client holders have a lower lease period than
292
long tempPingPeriod = defaultPingPeriod;
294
for (Iterator i = clientSessionIds.values().iterator(); i.hasNext(); )
296
ClientHolder clientHolder = (ClientHolder)i.next();
297
long clientHolderLeasePeriod = clientHolder.getLeasePeriod();
298
if (clientHolderLeasePeriod > 0 && clientHolderLeasePeriod < tempPingPeriod)
300
tempPingPeriod = clientHolderLeasePeriod;
304
// was there a change in lease period?
305
if (tempPingPeriod != pingPeriod)
307
// need to update to new ping period and reset timer
308
pingPeriod = tempPingPeriod;
310
if (timerTask != null)
319
return isLastClientLease;
322
public long getLeasePeriod(String sessionID)
324
if (timerTask == null)
329
// look to see if the client is still amont those serviced by this lease pinger
330
if (clientSessionIds.containsKey(sessionID))
340
public String toString()
342
return "LeasePinger[" + leasePingerId + ":" + invoker + "(" + invokerSessionID + ")]";
345
// Package protected ----------------------------------------------------------------------------
347
// Protected ------------------------------------------------------------------------------------
350
protected int getDisconnectTimeout()
352
return disconnectTimeout;
355
protected void setDisconnectTimeout(int disconnectTimeout)
357
this.disconnectTimeout = disconnectTimeout;
358
if (trace) log.trace(this + " setting disconnect timeout to: " + disconnectTimeout);
361
protected String getLeasePingerId()
363
return leasePingerId;
366
protected void setLeasePingerId(String leasePingerId)
368
this.leasePingerId = leasePingerId;
371
boolean isUseClientConnectionIdentity()
373
return useClientConnectionIdentity;
376
void setUseClientConnectionIdentity(boolean useClientConnectionIdentity)
378
this.useClientConnectionIdentity = useClientConnectionIdentity;
381
// Private --------------------------------------------------------------------------------------
383
private void sendClientPing() throws Throwable
387
StringBuffer sb = new StringBuffer();
388
if(clientSessionIds != null)
390
for(Iterator i = clientSessionIds.values().iterator(); i.hasNext(); )
392
ClientHolder h = (ClientHolder)i.next();
393
sb.append(" ").append(h.getSessionId()).append('\n');
397
log.trace(this + " sending ping to server. Currently managing lease " +
398
"for following clients:\n" + sb.toString());
401
Map clientsClone = new ConcurrentHashMap(clientSessionIds);
402
Map requestClients = new ConcurrentHashMap();
403
requestClients.put(ClientHolder.CLIENT_HOLDER_KEY, clientsClone);
404
requestClients.put(LeasePinger.LEASE_PINGER_ID, leasePingerId);
405
requestClients.put(TIME_STAMP, Long.toString(System.currentTimeMillis()));
407
if (leasePingerTimeout >= 0)
409
requestClients.put(ServerInvoker.TIMEOUT, Integer.toString(leasePingerTimeout));
412
InvocationRequest ir = new InvocationRequest(invokerSessionID, null, "$PING$", requestClients, null, null);
414
pingSucceeded = false;
418
pingSucceeded = true;
420
if(trace) { log.trace(this + " successfully pinged the server"); }
423
// Inner classes --------------------------------------------------------------------------------
425
static private class LeaseTimerTask extends TimerTask
427
private LeasePinger pinger;
429
LeaseTimerTask(final LeasePinger pinger)
431
this.pinger = pinger;
436
final LeasePinger currentPinger;
439
currentPinger = pinger;
442
if (currentPinger != null)
446
currentPinger.sendClientPing();
450
log.debug(this + " failed to ping to server", t);
451
log.warn(this + " failed to ping to server: " + t.getMessage());
456
public boolean cancel()
462
return super.cancel();