/* * JBoss, Home of Professional Open Source Copyright 2005-2008, Red Hat Middleware LLC, and individual contributors 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.messaging.core.remoting.impl; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import org.jboss.messaging.core.config.Configuration; import org.jboss.messaging.core.config.TransportConfiguration; import org.jboss.messaging.core.exception.MessagingException; import org.jboss.messaging.core.logging.Logger; import org.jboss.messaging.core.remoting.Channel; import org.jboss.messaging.core.remoting.ChannelHandler; import org.jboss.messaging.core.remoting.Interceptor; import org.jboss.messaging.core.remoting.RemotingConnection; import org.jboss.messaging.core.remoting.RemotingService; import org.jboss.messaging.core.remoting.spi.Acceptor; import org.jboss.messaging.core.remoting.spi.AcceptorFactory; import org.jboss.messaging.core.remoting.spi.BufferHandler; import org.jboss.messaging.core.remoting.spi.Connection; import org.jboss.messaging.core.remoting.spi.ConnectionLifeCycleListener; import org.jboss.messaging.core.remoting.spi.MessagingBuffer; import org.jboss.messaging.core.server.MessagingServer; import org.jboss.messaging.core.server.impl.MessagingServerPacketHandler; /** * @author <a href="mailto:jmesnil@redhat.com">Jeff Mesnil</a> * @author <a href="mailto:ataylor@redhat.com">Andy Taylor</a> * @author <a href="mailto:tim.fox@jboss.com">Tim Fox</a> * @version <tt>$Revision$</tt> */ public class RemotingServiceImpl implements RemotingService, ConnectionLifeCycleListener { // Constants ----------------------------------------------------- private static final Logger log = Logger.getLogger(RemotingServiceImpl.class); // Attributes ---------------------------------------------------- private volatile boolean started = false; private final Set<TransportConfiguration> transportConfigs; private final List<Interceptor> interceptors = new ArrayList<Interceptor>(); private final Set<Acceptor> acceptors = new HashSet<Acceptor>(); private final long callTimeout; private final Map<Object, RemotingConnection> connections = new ConcurrentHashMap<Object, RemotingConnection>(); private final Timer failedConnectionTimer = new Timer(true); private TimerTask failedConnectionsTask; private final long connectionScanPeriod; private final BufferHandler bufferHandler = new DelegatingBufferHandler(); private volatile boolean backup; private volatile MessagingServer server; private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(true); // Static -------------------------------------------------------- // Constructors -------------------------------------------------- public RemotingServiceImpl(final Configuration config) { transportConfigs = config.getAcceptorConfigurations(); ClassLoader loader = Thread.currentThread().getContextClassLoader(); for (String interceptorClass : config.getInterceptorClassNames()) { try { Class<?> clazz = loader.loadClass(interceptorClass); interceptors.add((Interceptor)clazz.newInstance()); } catch (Exception e) { log.warn("Error instantiating interceptor \"" + interceptorClass + "\"", e); } } callTimeout = config.getCallTimeout(); connectionScanPeriod = config.getConnectionScanPeriod(); backup = config.isBackup(); } // RemotingService implementation ------------------------------- public synchronized void start() throws Exception { if (started) { return; } ClassLoader loader = Thread.currentThread().getContextClassLoader(); for (TransportConfiguration info : transportConfigs) { try { Class<?> clazz = loader.loadClass(info.getFactoryClassName()); AcceptorFactory factory = (AcceptorFactory)clazz.newInstance(); Acceptor acceptor = factory.createAcceptor(info.getParams(), bufferHandler, this); acceptors.add(acceptor); } catch (Exception e) { log.warn("Error instantiating acceptor \"" + info.getFactoryClassName() + "\"", e); } } for (Acceptor a : acceptors) { a.start(); } failedConnectionsTask = new FailedConnectionsTask(); failedConnectionTimer.schedule(failedConnectionsTask, connectionScanPeriod, connectionScanPeriod); started = true; } public synchronized void stop() { if (!started) { return; } if (failedConnectionsTask != null) { failedConnectionsTask.cancel(); failedConnectionsTask = null; } for (Acceptor acceptor : acceptors) { acceptor.stop(); } started = false; } public boolean isStarted() { return started; } public Set<Acceptor> getAcceptors() { return acceptors; } public RemotingConnection getConnection(final Object remotingConnectionID) { return connections.get(remotingConnectionID); } public synchronized Set<RemotingConnection> getConnections() { return new HashSet<RemotingConnection>(connections.values()); } public void setMessagingServer(final MessagingServer server) { this.server = server; } public void setBackup(final boolean backup) { this.backup = backup; } // ConnectionLifeCycleListener implementation ----------------------------------- public void connectionCreated(final Connection connection) { if (server == null) { throw new IllegalStateException("Unable to create connection, server hasn't finished starting up"); } RemotingConnection replicatingConnection = server.getReplicatingConnection(); RemotingConnection rc = new RemotingConnectionImpl(connection, callTimeout, -1, null, interceptors, replicatingConnection, !backup, readWriteLock); Channel channel1 = rc.getChannel(1, -1, false); ChannelHandler handler = new MessagingServerPacketHandler(server, channel1, rc); channel1.setHandler(handler); Object id = connection.getID(); connections.put(id, rc); } public void connectionDestroyed(final Object connectionID) { RemotingConnection conn = connections.remove(connectionID); if (conn != null) { conn.destroy(); } } public void connectionException(final Object connectionID, final MessagingException me) { RemotingConnection rc = connections.remove(connectionID); if (rc != null) { rc.fail(me); } } public void addInterceptor(final Interceptor interceptor) { interceptors.add(interceptor); } public boolean removeInterceptor(final Interceptor interceptor) { return interceptors.remove(interceptor); } // Public -------------------------------------------------------- // Package protected --------------------------------------------- // Protected ----------------------------------------------------- // Private ------------------------------------------------------- // Inner classes ------------------------------------------------- private class FailedConnectionsTask extends TimerTask { private boolean cancelled; @Override public synchronized void run() { if (cancelled) { return; } Set<RemotingConnection> failedConnections = new HashSet<RemotingConnection>(); long now = System.currentTimeMillis(); for (RemotingConnection conn : connections.values()) { if (conn.isExpired(now)) { failedConnections.add(conn); } } for (RemotingConnection conn : failedConnections) { MessagingException me = new MessagingException(MessagingException.CONNECTION_TIMEDOUT, "Did not receive ping on connection. It is likely a client has exited or crashed without " + "closing its connection, or the network between the server and client has failed. The connection will now be closed."); conn.fail(me); } } @Override public synchronized boolean cancel() { cancelled = true; return super.cancel(); } } private class DelegatingBufferHandler extends AbstractBufferHandler { public void bufferReceived(final Object connectionID, final MessagingBuffer buffer) { RemotingConnection conn = connections.get(connectionID); if (conn != null) { conn.bufferReceived(connectionID, buffer); } } } }