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.
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.
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.
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.
23
package org.jboss.remoting;
25
import java.util.HashMap;
26
import java.util.HashSet;
27
import java.util.Iterator;
30
import java.util.Timer;
31
import java.util.TimerTask;
33
import org.jboss.logging.Logger;
34
import org.jboss.remoting.transport.ClientInvoker;
35
import org.jboss.remoting.util.StoppableTimerTask;
36
import org.jboss.remoting.util.TimerUtil;
39
* @author <a href="mailto:tom.elrod@jboss.com">Tom Elrod</a>
40
* @author <a href="mailto:ovidiu@jboss.org">Ovidiu Feodorov</a>
41
* @author <a href="mailto:tlee@redhat.com">Trustin Lee</a>
43
public class ConnectionValidator extends TimerTask implements StoppableTimerTask
45
// Constants ------------------------------------------------------------------------------------
47
private static final Logger log = Logger.getLogger(ConnectionValidator.class.getName());
49
/** Configuration map key for ping period. */
50
public static final String VALIDATOR_PING_PERIOD = "validatorPingPeriod";
52
/** Default ping period. Value is 2 seconds. */
53
public static final long DEFAULT_PING_PERIOD = 2000;
55
/** Configuration map key for ping timeout. */
56
public static final String VALIDATOR_PING_TIMEOUT = "validatorPingTimeout";
58
/** Default ping timeout period. Value is 1 second. */
59
public static final String DEFAULT_PING_TIMEOUT = "1000";
61
/** Default ping timeout period. Value is 1 second. */
62
public static final int DEFAULT_PING_TIMEOUT_INT = 1000;
65
* Default number of ping retries. Value is 1.
66
* Currently implemented only on socket transport family.
68
public static final String DEFAULT_NUMBER_OF_PING_RETRIES = "1";
71
* Key to determine if ConnectionValidator should tie failure to presence
72
* of active lease on server side. Default value is "true".
74
public static final String TIE_TO_LEASE = "tieToLease";
77
* Key to determine whether to stop ConnectionValidator when PING fails.
78
* Default value is "true".
80
public static final String STOP_LEASE_ON_FAILURE = "stopLeaseOnFailure";
83
* Key to determine value of disconnectTimeout upon connection failure.
85
public static final String FAILURE_DISCONNECT_TIMEOUT = "failureDisconnectTimeout";
87
// Static ---------------------------------------------------------------------------------------
89
private static boolean trace = log.isTraceEnabled();
92
* Will make $PING$ invocation on server. If sucessful, will return true. Otherwise, will throw
95
* @param locator - locator for the server to ping
96
* @param config - any configuration needed for server
97
* @return true if alive, false if not
99
public static boolean checkConnection(InvokerLocator locator, Map config) throws Throwable
101
boolean pingWorked = false;
102
Map configMap = createPingConfig(config, null);
103
int pingTimeout = Integer.parseInt((String) configMap.get(ServerInvoker.TIMEOUT));
104
ClientInvoker innerClientInvoker = null;
108
innerClientInvoker = InvokerRegistry.createClientInvoker(locator, configMap);
110
if (!innerClientInvoker.isConnected())
112
if (trace) { log.trace("inner client invoker not connected, connecting ..."); }
113
innerClientInvoker.connect();
116
pingWorked = doCheckConnection(innerClientInvoker, pingTimeout);
118
catch (Throwable throwable)
120
log.debug("ConnectionValidator unable to connect to server " +
121
innerClientInvoker.getLocator().getProtocol() + "://" +
122
innerClientInvoker.getLocator().getHost() + ":" +
123
innerClientInvoker.getLocator().getPort(), throwable);
127
if (innerClientInvoker != null)
129
InvokerRegistry.destroyClientInvoker(locator, configMap);
136
private static boolean doCheckConnection(ClientInvoker clientInvoker, int pingTimeout) throws Throwable
138
boolean pingWorked = false;
142
// Sending null client id as don't want to trigger lease on server side. This also means
143
// that client connection validator will NOT impact client lease, so can not depend on it
144
// to maintain client lease with the server.
145
InvocationRequest ir;
146
ir = new InvocationRequest(null, Subsystem.SELF, "$PING$", null, null, null);
147
ConnectionCheckThread t = new ConnectionCheckThread(clientInvoker, ir);
149
Thread.sleep(pingTimeout);
150
pingWorked = t.isValid();
154
log.debug("ConnectionValidator failed to ping via " + clientInvoker, t);
160
private static Map createPingConfig(Map config, Map metadata)
162
Map localConfig = new HashMap();
163
localConfig.put("connection_checker", "true");
167
Object o = config.get(VALIDATOR_PING_TIMEOUT);
168
log.trace("config timeout: " + o);
173
Integer.parseInt((String) o);
174
localConfig.put(ServerInvoker.TIMEOUT, o);
176
catch (NumberFormatException e)
178
log.warn("Need integer for value of parameter " + VALIDATOR_PING_TIMEOUT +
179
". Using default value " + DEFAULT_PING_TIMEOUT);
183
o = config.get("NumberOfCallRetries");
186
localConfig.put("NumberOfCallRetries", o);
190
if (metadata != null)
192
metadata.remove(ServerInvoker.TIMEOUT);
193
localConfig.putAll(metadata);
194
Object o = metadata.get(VALIDATOR_PING_TIMEOUT);
199
Integer.parseInt((String) o);
200
localConfig.put(ServerInvoker.TIMEOUT, o);
202
catch (NumberFormatException e)
204
log.warn("Need integer for value of parameter " + VALIDATOR_PING_TIMEOUT +
205
". Using default value " + DEFAULT_PING_TIMEOUT);
210
if (localConfig.get(ServerInvoker.TIMEOUT) == null)
212
localConfig.put(ServerInvoker.TIMEOUT, DEFAULT_PING_TIMEOUT);
215
if (localConfig.get("NumberOfCallRetries") == null)
217
localConfig.put("NumberOfCallRetries", DEFAULT_NUMBER_OF_PING_RETRIES);
223
// Attributes -----------------------------------------------------------------------------------
225
private Client client;
226
private long pingPeriod;
227
private Map metadata;
228
private InvokerLocator locator;
229
private Map configMap;
230
private Map listeners;
231
private ClientInvoker clientInvoker;
232
private Object lock = new Object();
233
private Object notificationLock = new Object();
234
private boolean started;
235
private volatile boolean stopped;
236
private volatile boolean stopping;
237
private String invokerSessionId;
238
private boolean tieToLease = true;
239
private boolean stopLeaseOnFailure = true;
240
private int pingTimeout;
241
private int failureDisconnectTimeout = -1;
242
private boolean isValid;
244
private MicroRemoteClientInvoker sharedInvoker;
245
private LeasePinger leasePinger;
246
private boolean useClientConnectionIdentity;
248
// Constructors ---------------------------------------------------------------------------------
250
public ConnectionValidator(Client client)
252
this(client, DEFAULT_PING_PERIOD);
255
public ConnectionValidator(Client client, long pingPeriod)
257
this.client = client;
258
this.locator = client.getInvoker().getLocator();
259
this.pingPeriod = pingPeriod;
260
pingTimeout = DEFAULT_PING_TIMEOUT_INT;
261
listeners = new HashMap();
263
getParameters(client, new HashMap());
264
log.debug(this + " created");
267
public ConnectionValidator(Client client, Map metadata)
269
this.client = client;
270
this.locator = client.getInvoker().getLocator();
271
pingPeriod = DEFAULT_PING_PERIOD;
272
pingTimeout = DEFAULT_PING_TIMEOUT_INT;
273
listeners = new HashMap();
275
this.metadata = new HashMap(metadata);
276
getParameters(client, metadata);
277
log.debug(this + " created");
280
// StoppableTimerTask implementation ------------------------------------------------------------
292
// TimerTask overrides --------------------------------------------------------------------------
295
* The action to be performed by this timer task.
303
throw new IllegalStateException(
304
ConnectionValidator.class.getName() + ".run() should not be " +
305
"called directly; use " + ConnectionValidator.class.getName() +
306
".addConnectionListener() instead.");
314
TimerTask tt = new WaitOnConnectionCheckTimerTask();
318
timer.schedule(tt, 0);
320
catch (IllegalStateException e)
322
log.debug("Unable to schedule TimerTask on existing Timer", e);
323
timer = new Timer(true);
324
timer.schedule(tt, 0);
334
if (tieToLease && client.getLeasePeriod() > 0)
338
log.trace(this + " sending PING tied to lease");
340
isValid = doCheckConnectionWithLease();
344
if (trace) { log.trace(this + " pinging ..."); }
345
isValid = doCheckConnectionWithoutLease();
349
catch (Throwable thr)
351
log.debug(this + " got throwable while pinging", thr);
353
if (stopLeaseOnFailure)
355
log.debug(this + " detected connection failure: stopping");
361
synchronized (notificationLock)
363
notificationLock.notifyAll();
368
public boolean cancel()
373
// Public ---------------------------------------------------------------------------------------
375
public boolean addConnectionListener(Client client, ConnectionListener listener)
377
boolean doStart = false;
378
if (listener != null)
384
if (trace) log.trace(this + " is stopped. Cannot add ConnectionListener: " + listener + " for " + client);
387
if (listeners.size() == 0)
391
Set s = (Set) listeners.get(listener);
397
listeners.put(listener, s);
398
log.debug(this + " added ConnectionListener: " + listener + " for " + client);
409
public boolean removeConnectionListener(Client client, ConnectionListener listener)
411
if (listener == null)
413
if (trace) log.trace(this + " ConnectionListener is null");
420
if (trace) log.trace(this + " is stopped. It's too late to remove " + listener);
423
Set s = (Set) listeners.get(listener);
426
log.debug(this + ": " + listener + " is not registered");
429
if (s.remove(client))
431
log.debug(this + " removed ConnectionListener: " + listener + " for " + client);
435
log.debug(this + ": " + listener + " is not registered for " + client);
440
listeners.remove(listener);
442
if (listeners.size() == 0)
450
public long getPingPeriod()
460
public String toString()
462
return "ConnectionValidator[" + Integer.toHexString(System.identityHashCode(this)) + ":" + clientInvoker + ", pingPeriod=" + pingPeriod + " ms]";
465
public boolean isStopped()
470
// Package protected ----------------------------------------------------------------------------
472
void notifyListeners(Throwable thr)
474
final Throwable t = thr;
482
if (trace) log.trace(this + " is stopped. No more listeners will be accepted.");
484
Iterator itr = listeners.keySet().iterator();
485
while (itr.hasNext())
487
final ConnectionListener listener = (ConnectionListener) itr.next();
488
Set clients = (Set) listeners.get(listener);
489
Iterator itr2 = clients.iterator();
490
while (itr2.hasNext())
492
final Client client = (Client) itr2.next();
497
log.debug(ConnectionValidator.this + " calling " + listener + ".handleConnectionException() for " + client);
498
listener.handleConnectionException(t, client);
510
// Protected ------------------------------------------------------------------------------------
512
// Private --------------------------------------------------------------------------------------
514
private void getParameters(Client client, Map metadata)
516
if (checkUseParametersFromLocator(client, metadata))
518
getParametersFromMap(client.getInvoker().getLocator().getParameters());
520
getParametersFromMap(client.getConfiguration());
521
getParametersFromMap(metadata);
523
ClientInvoker clientInvoker = client.getInvoker();
524
if (clientInvoker instanceof MicroRemoteClientInvoker)
526
sharedInvoker = (MicroRemoteClientInvoker) clientInvoker;
527
invokerSessionId = sharedInvoker.getSessionId();
531
throw new RuntimeException("creating a ConnectionValidator on a local connection");
533
if (stopLeaseOnFailure)
535
if (sharedInvoker != null)
537
leasePinger = sharedInvoker.getLeasePinger();
540
if (trace) log.trace(this + ": sharedInvoker = " + sharedInvoker + ", leasePinger = " + leasePinger);
543
private boolean checkUseParametersFromLocator(Client client, Map metadata)
545
if (client.getInvoker() == null)
549
Object o = client.getInvoker().getLocator().getParameters().get(Client.USE_ALL_PARAMS);
552
if (o instanceof String)
554
return Boolean.valueOf(((String) o)).booleanValue();
558
log.warn(this + " could not convert " + Client.USE_ALL_PARAMS + " value" +
559
" in InvokerLocator to a boolean: must be a String");
562
o = client.getConfiguration().get(Client.USE_ALL_PARAMS);
565
if (o instanceof String)
567
return Boolean.valueOf(((String) o)).booleanValue();
571
log.warn(this + " could not convert " + Client.USE_ALL_PARAMS + " value" +
572
" in Client configuration map to a boolean: must be a String");
575
o = metadata.get(Client.USE_ALL_PARAMS);
578
if (o instanceof String)
580
return Boolean.valueOf(((String) o)).booleanValue();
584
log.warn(this + " could not convert " + Client.USE_ALL_PARAMS + " value" +
585
" in metadata map to a boolean: must be a String");
591
private void getParametersFromMap(Map config)
595
Object o = config.get(VALIDATOR_PING_PERIOD);
598
if (o instanceof String)
602
pingPeriod = Long.parseLong((String)o);
606
log.warn(this + " could not convert " + VALIDATOR_PING_PERIOD +
607
" value of " + o + " to a long value");
612
log.warn(this + " could not convert " + VALIDATOR_PING_PERIOD +
613
" value of " + o + " to a long value: must be a String");
617
o = config.get(VALIDATOR_PING_TIMEOUT);
620
if (o instanceof String)
624
pingTimeout = Integer.parseInt((String)o);
628
log.warn(this + " could not convert " + VALIDATOR_PING_TIMEOUT +
629
" value of " + o + " to a long value");
634
log.warn(this + " could not convert " + VALIDATOR_PING_TIMEOUT +
635
" value of " + o + " to a long value: must be a String");
639
o = config.get(TIE_TO_LEASE);
642
if (o instanceof String)
646
tieToLease = Boolean.valueOf(((String) o)).booleanValue();
650
log.warn(this + " could not convert " + TIE_TO_LEASE + " value" +
651
" to a boolean: " + o);
656
log.warn(this + " could not convert " + TIE_TO_LEASE + " value" +
657
" to a boolean: must be a String");
661
o = config.get(STOP_LEASE_ON_FAILURE);
664
if (o instanceof String)
668
stopLeaseOnFailure = Boolean.valueOf(((String) o)).booleanValue();
672
log.warn(this + " could not convert " + STOP_LEASE_ON_FAILURE + " value" +
673
" to a boolean: " + o);
678
log.warn(this + " could not convert " + STOP_LEASE_ON_FAILURE + " value" +
679
" to a boolean: must be a String");
683
o = config.get(FAILURE_DISCONNECT_TIMEOUT);
684
if (trace) log.trace(this + " \"failureDisconnectTimeout\" set to " + o);
687
if (o instanceof String)
691
failureDisconnectTimeout = Integer.valueOf(((String) o)).intValue();
692
if (trace) log.trace(this + " setting failureDisconnectTimeout to " + failureDisconnectTimeout);
696
log.warn(this + " could not convert " + FAILURE_DISCONNECT_TIMEOUT + " value" +
702
log.warn(this + " could not convert " + FAILURE_DISCONNECT_TIMEOUT + " value" +
703
" to an int: must be a String");
706
o = config.get(Remoting.USE_CLIENT_CONNECTION_IDENTITY);
709
if (o instanceof String)
713
useClientConnectionIdentity = Boolean.valueOf(((String) o)).booleanValue();
717
log.warn(this + " could not convert " + Remoting.USE_CLIENT_CONNECTION_IDENTITY + " value" +
718
" to a boolean: " + o);
723
log.warn(this + " could not convert " + Remoting.USE_CLIENT_CONNECTION_IDENTITY + " value" +
724
" to a boolean: must be a String");
732
metadata.put(ServerInvoker.TIMEOUT, Integer.toString(pingTimeout));
733
configMap = createPingConfig(client.getConfiguration(), metadata);
734
log.debug(this + ": pingPeriod: " + this.pingPeriod);
735
log.debug(this + ": pingTimeout: " + this.pingTimeout);
736
log.debug(this + ": ping retries: " + configMap.get("NumberOfCallRetries"));
740
clientInvoker = InvokerRegistry.createClientInvoker(locator, configMap);
744
log.debug("Unable to create client invoker for locator: " + locator);
745
throw new RuntimeException("Unable to create client invoker for locator: " + locator, e);
748
if (!clientInvoker.isConnected())
750
if (trace) { log.trace("inner client invoker not connected, connecting ..."); }
751
clientInvoker.connect();
755
timer = new Timer(true);
759
TimerUtil.schedule(this, pingPeriod);
763
log.error(this + " unable to schedule on TimerUtil", e);
768
log.debug(this + " started");
771
private boolean doCheckConnectionWithLease() throws Throwable
773
boolean pingWorked = false;
777
Map metadata = new HashMap();
778
metadata.put(ServerInvoker.INVOKER_SESSION_ID, invokerSessionId);
779
InvocationRequest ir =
780
new InvocationRequest(null, Subsystem.SELF, "$PING$", metadata, null, null);
782
if (trace) { log.trace("pinging, sending " + ir + " over " + clientInvoker); }
784
Object o = clientInvoker.invoke(ir);
785
if (o instanceof Boolean && !((Boolean) o).booleanValue())
787
// Server indicates lease has stopped.
788
throw new Exception();
791
if (trace) { log.trace("ConnectionValidator got successful ping using " + clientInvoker);}
797
log.debug("ConnectionValidator failed to ping via " + clientInvoker, t);
803
private boolean doCheckConnectionWithoutLease() throws Throwable
805
boolean pingWorked = false;
809
// Sending null client id as don't want to trigger lease on server side. This also means
810
// that client connection validator will NOT impact client lease, so can not depend on it
811
// to maintain client lease with the server.
812
InvocationRequest ir =
813
new InvocationRequest(null, Subsystem.SELF, "$PING$", null, null, null);
815
if (trace) { log.trace("pinging, sending " + ir + " over " + clientInvoker); }
817
clientInvoker.invoke(ir);
819
if (trace) { log.trace("ConnectionValidator got successful ping using " + clientInvoker);}
825
log.debug("ConnectionValidator failed to ping via " + clientInvoker, t);
831
private boolean doStop()
833
if (trace) log.trace("entering doStop()");
841
if (!listeners.isEmpty())
850
if (clientInvoker != null)
852
InvokerRegistry.destroyClientInvoker(locator, configMap);
855
TimerUtil.unschedule(this);
857
boolean result = super.cancel();
858
log.debug(this + " stopped, returning " + result);
862
// Inner classes --------------------------------------------------------------------------------
864
private class WaitOnConnectionCheckTimerTask extends TimerTask
868
long start = System.currentTimeMillis();
869
synchronized (notificationLock)
873
int elapsed = (int) (System.currentTimeMillis() - start);
874
int wait = pingTimeout - elapsed;
882
notificationLock.wait(wait);
885
catch (InterruptedException e)
894
log.debug(ConnectionValidator.this + "'s connection is invalid");
895
ConnectionValidator.super.cancel();
897
if (stopLeaseOnFailure)
899
if (trace) log.trace(ConnectionValidator.this + " detected connection failure: stopping LeasePinger");
900
if (leasePinger != null)
902
log.debug(ConnectionValidator.this + " shutting down lease pinger: " + leasePinger);
903
int disconnectTimeout = (failureDisconnectTimeout == -1) ? client.getDisconnectTimeout() : failureDisconnectTimeout;
904
if (trace) log.trace(ConnectionValidator.this + " disconnectTimeout: " + disconnectTimeout);
905
sharedInvoker.terminateLease(null, disconnectTimeout, leasePinger);
909
if (trace) log.trace(ConnectionValidator.this + ": lease pinger == null: perhaps leasing is not enabled for this connection");
910
notifyListeners(new Exception("Could not connect to server!"));
915
if (!useClientConnectionIdentity)
917
notifyListeners(new Exception("Could not connect to server!"));
923
private static class ConnectionCheckThread extends Thread
925
private InvocationRequest ir;
926
private ClientInvoker clientInvoker;
927
private boolean isValid;
929
public ConnectionCheckThread(ClientInvoker clientInvoker, InvocationRequest ir)
931
this.clientInvoker = clientInvoker;
940
if (trace) { log.trace("pinging, sending " + ir + " over " + clientInvoker); }
941
clientInvoker.invoke(ir);
943
if (trace) { log.trace("ConnectionValidator got successful ping using " + clientInvoker);}
947
log.debug("ConnectionValidator failed to ping via " + clientInvoker, t);
951
public boolean isValid()
b'\\ No newline at end of file'