/**
* JBoss, Home of Professional Open Source
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.jms.client.remoting;
import javax.jms.ExceptionListener;
import javax.jms.JMSException;
import org.jboss.jms.client.container.ConnectionFailureListener;
import org.jboss.jms.client.state.ConnectionState;
import org.jboss.logging.Logger;
import org.jboss.remoting.Client;
import org.jboss.remoting.ConnectionListener;
/**
* The ONLY remoting connection listener for a JMS connection's underlying remoting connection.
* Added to the remoting connection when the JMS connection is created, and removed when the
* JMS connection is closed. Any second tier listeners (the JMS connection ExceptionListener,
* and the HA's connection failure detector) are registered with this consolidated listener and not
* with the remoting connection directly.
*
* @author <a href="mailto:ovidiu@feodorov.com">Ovidiu Feodorov</a>
* @version <tt>$Revision: 8498 $</tt>
*
* $Id: ConsolidatedRemotingConnectionListener.java 8498 2012-02-09 10:51:07Z gaohoward $
*/
public class ConsolidatedRemotingConnectionListener implements ConnectionListener
{
// Constants ------------------------------------------------------------------------------------
private static final Logger log = Logger.getLogger(ConsolidatedRemotingConnectionListener.class);
// Static ---------------------------------------------------------------------------------------
// Attributes -----------------------------------------------------------------------------------
private ConnectionState state;
private ExceptionListener jmsExceptionListener;
private ConnectionFailureListener remotingListener;
private boolean started;
private JMSException jmsException;
private Object jmsExceptionLock = new Object();
private Object exceptionLock = new Object();
private boolean beingHandled = false;
// Constructors ---------------------------------------------------------------------------------
public ConsolidatedRemotingConnectionListener()
{
}
// ConnectionListener implementation ------------------------------------------------------------
//for simplicity, we changed from partial synchronization to full synchronization. This
//method is not supposed to have high concurrent access.
public synchronized void handleConnectionException(Throwable throwable, Client client)
{
if (!started)
{
return;
}
//if the connection is already closed then there is no point handling it again.
if (!setBeingHandled())
{
return;
}
// forward the exception to delegate listener and JMS ExceptionListeners;
boolean forwardToJMSListener = true;
//save a copy. This is because if the failover failed the listener will be cleaned up.
ExceptionListener jmsListenerCopy = jmsExceptionListener;
if (remotingListener != null)
{
try
{
log.trace(this + " forwarding remoting failure \"" + throwable + "\" to " + remotingListener);
//We only forward to the JMS listener if failover did not successfully handle the exception
//If failover handled the exception transparently then there is effectively no problem
//with the logical connection that the client needs to be aware of
forwardToJMSListener = !remotingListener.handleConnectionException(throwable, client);
}
catch(Exception e)
{
log.warn("Failed to forward " + throwable + " to " + remotingListener, e);
}
}
if (forwardToJMSListener)
{
// if a listener already set, notify it instantly and forget the exception.
synchronized (jmsExceptionLock)
{
// the connection is broken, we need to remember this
if (throwable instanceof Error)
{
final String msg = "Caught Error on underlying remoting connection";
log.error(this + ": " + msg, throwable);
jmsException = new JMSException(msg + ": " + throwable.getMessage());
}
else if (throwable instanceof Exception)
{
Exception e = (Exception)throwable;
jmsException = new JMSException("Failure on underlying remoting connection");
jmsException.setLinkedException(e);
}
else
{
// Some other Throwable subclass
final String msg = "Caught Throwable on underlying remoting connection";
log.error(this + ": " + msg, throwable);
jmsException = new JMSException(msg + ": " + throwable.getMessage());
}
if (jmsListenerCopy != null)
{
jmsListenerCopy.onException(jmsException);
jmsException = null;
}
}
}
}
// Public ---------------------------------------------------------------------------------------
public synchronized void setDelegateListener(ConnectionFailureListener l)
{
log.trace(this + " setting delegate listener " + l);
if (remotingListener != null)
{
throw new IllegalStateException("There is already a connection listener for the connection");
}
remotingListener = l;
}
public synchronized void addJMSExceptionListener(ExceptionListener listener)
{
log.trace(this + " adding JMS exception listener " + listener + " current exception: " + jmsException);
synchronized (jmsExceptionLock)
{
this.jmsExceptionListener = listener;
// if a connection failure event already happened, we notify it.
// https://jira.jboss.org/jira/browse/JBMESSAGING-1776
if (jmsException != null)
{
final JMSException copy = jmsException;
jmsException = null;
notifyJMSExceptionListener(listener, copy);
}
}
}
//Called when the listener is added after the connection exception.
private void notifyJMSExceptionListener(final ExceptionListener l, final JMSException ex)
{
new Thread()
{
public void run()
{
l.onException(ex);
}
}.start();
}
public ExceptionListener getJMSExceptionListener()
{
synchronized (jmsExceptionLock)
{
return jmsExceptionListener;
}
}
/**
* Clears all delegate listeners
*/
public synchronized void clear()
{
clearJmsExceptionListener();
remotingListener = null;
log.trace(this + " cleared");
}
//https://issues.jboss.org/browse/JBMESSAGING-1912
public boolean setBeingHandled()
{
synchronized (exceptionLock)
{
if (beingHandled)
{
return false;
}
beingHandled = true;
return true;
}
}
private void clearJmsExceptionListener()
{
synchronized (jmsExceptionLock)
{
jmsExceptionListener = null;
}
}
public void setConnectionState(ConnectionState state)
{
this.state = state;
}
public String toString()
{
if (state == null)
{
return "ConsolidatedListener(UNINITIALIZED)";
}
return state + ".ConsolidatedListener";
}
public void start()
{
started = true;
}
// Package protected ----------------------------------------------------------------------------
// Protected ------------------------------------------------------------------------------------
// Private --------------------------------------------------------------------------------------
// Inner classes --------------------------------------------------------------------------------
}