/*
* JBoss, Home of Professional Open Source
* Copyright 2005, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.remoting;
import org.jboss.logging.Logger;
import org.jboss.remoting.transport.ClientInvoker;
import org.jboss.remoting.util.TimerUtil;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.TimerTask;
/**
* @author <a href="mailto:tom.elrod@jboss.com">Tom Elrod</a>
*/
public class ConnectionValidator extends TimerTask
{
private List listeners = new ArrayList();
private Client client = null;
private long pingPeriod = DEFAULT_PING_PERIOD;
protected static final Logger log = Logger.getLogger(ConnectionValidator.class.getName());
/**
* Default ping period. Value is 2 seconds.
*/
public static final long DEFAULT_PING_PERIOD = 2000;
public ConnectionValidator(Client client)
{
this.client = client;
}
public ConnectionValidator(Client client, int pingPeriod)
{
this.client = client;
this.pingPeriod = pingPeriod;
}
private void start()
{
TimerUtil.schedule(this, pingPeriod);
}
private void stop()
{
cancel();
}
public void addConnectionListener(ConnectionListener listener)
{
if (listener != null)
{
synchronized (listener)
{
if (listeners.size() == 0)
{
start();
}
listeners.add(listener);
}
}
}
public boolean removeConnectionListener(ConnectionListener listener)
{
boolean isRemoved = false;
if (listener != null)
{
synchronized (listeners)
{
isRemoved = listeners.remove(listener);
if (listeners.size() == 0)
{
stop();
}
}
}
return isRemoved;
}
/**
* The action to be performed by this timer task.
*/
public void run()
{
try
{
boolean isValid = checkConnection(client.getInvoker().getLocator(), client.getConfiguration());
if (!isValid)
{
notifyListeners(new Exception("Could not connect to server."));
}
}
catch (Throwable thr)
{
notifyListeners(thr);
}
}
private void notifyListeners(Throwable thr)
{
final Throwable t = thr;
synchronized (listeners)
{
ListIterator itr = listeners.listIterator();
while (itr.hasNext())
{
final ConnectionListener listener = (ConnectionListener) itr.next();
new Thread()
{
public void run()
{
listener.handleConnectionException(t, client);
}
}.start();
}
}
stop();
listeners.clear();
}
/**
* Will make $PING$ invocation on server. If sucessful, will return true. Otherwise,
* will throw an exception.
*
* @param locator - locator for the server to ping
* @param config - any configuration needed for server
* @return true if alive, false if not
* @throws Throwable
*/
public static boolean checkConnection(InvokerLocator locator, Map config) throws Throwable
{
boolean pingWorked = false;
Map configMap = config;
if (configMap == null)
{
configMap = new HashMap();
}
configMap.put("connection_checker", "true");
configMap.put("timeout", "1000");
configMap.put("NumberOfRetries", "1");
ClientInvoker innerClientInvoker = null;
try
{
innerClientInvoker = InvokerRegistry.createClientInvoker(locator, configMap);
if (!innerClientInvoker.isConnected())
{
innerClientInvoker.connect();
}
/**
* Sending null client id as don't want to trigger lease on server side.
* This also means that client connection validator will NOT impact client
* lease, so can not depend on it to maintain client lease with the server.
*/
Object o = innerClientInvoker.invoke(new InvocationRequest(null, Subsystem.SELF,
"$PING$", null, null, null));
pingWorked = true;
}
catch (Throwable throwable)
{
log.debug("ConnectionValidator could not successfully ping server (" + innerClientInvoker.getLocator());
}
finally
{
if (innerClientInvoker != null)
{
InvokerRegistry.destroyClientInvoker(locator, configMap);
}
}
return pingWorked;
}
}