/* * JBoss, Home of Professional Open Source. * Copyright 2012, Red Hat, Inc., and individual contributors * as indicated by the @author tags. See the copyright.txt file 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.as.protocol; import org.jboss.as.protocol.logging.ProtocolLogger; import org.jboss.remoting3.CloseHandler; import org.jboss.remoting3.Connection; import org.wildfly.common.Assert; import java.io.IOException; /** * A basic connection manager, notifying clients when the connection is closed or shutdown. The * {@code ProtocolConnectionManager.ConnectTask} can be used to implement different (re-)connection strategies. * * @author Emanuel Muckenhuber */ public final class ProtocolConnectionManager { private ConnectTask connectTask; private volatile boolean shutdown; private volatile Connection connection; private ProtocolConnectionManager(final ConnectTask initial) { Assert.checkNotNullParam("initial", initial); this.connectTask = initial; } /** * Check if connected. * * @return {@code true} if the connection is open, {@code false} otherwise */ public boolean isConnected() { return connection != null && !shutdown; } /** * Get the connection. If not connected, the {@code ConnectTask} will be used to establish a connection. * * @return the connection * @throws IOException */ public Connection connect() throws IOException { Connection connection; synchronized (this) { if(shutdown) throw ProtocolLogger.ROOT_LOGGER.channelClosed(); connection = this.connection; if(connection == null) { connection = connectTask.connect(); if(connection == null) { throw ProtocolLogger.ROOT_LOGGER.channelClosed(); } boolean ok = false; try { // Connection opened notification final ConnectionOpenHandler openHandler = connectTask.getConnectionOpenedHandler(); openHandler.connectionOpened(connection); ok = true; this.connection = connection; connection.addCloseHandler(new CloseHandler<Connection>() { @Override public void handleClose(Connection closed, IOException exception) { onConnectionClose(closed); } }); } finally { if(!ok) { StreamUtils.safeClose(connection); } } } } return connection; } /** * Get the connection. * * @return the connection */ public Connection getConnection() { return connection; } /** * Shutdown the connection manager. */ public void shutdown() { final Connection connection; synchronized (this) { if(shutdown) return; shutdown = true; connection = this.connection; if(connectTask != null) { connectTask.shutdown(); } } if (connection != null) { connection.closeAsync(); } } /** * Notification that a connection was closed. * * @param closed the closed connection */ private void onConnectionClose(final Connection closed) { synchronized (this) { if(connection == closed) { connection = null; if(shutdown) { connectTask = DISCONNECTED; return; } final ConnectTask previous = connectTask; connectTask = previous.connectionClosed(); } } } /** Handler for notifications that a connection has been opened */ public interface ConnectionOpenHandler { /** * Connection opened notification * * @param connection the connection * @throws IOException */ void connectionOpened(final Connection connection) throws IOException; } /** * Task used to establish the connection. */ public interface ConnectTask { /** * Get the connection opened handler. * * @return the connection opened handler */ ConnectionOpenHandler getConnectionOpenedHandler(); /** * Create a new connection * * @return the connection * @throws IOException */ Connection connect() throws IOException; /** * Notification when the channel is closed, but the manager not shutdown. * * @return the next connect connectTask */ ConnectTask connectionClosed(); /** * Notification when the connection manager gets shutdown. */ void shutdown(); } /** * Create a new connection manager, based on an existing connection. * * @param connection the existing connection * @param openHandler a connection open handler * @return the connected manager */ public static ProtocolConnectionManager create(final Connection connection, final ConnectionOpenHandler openHandler) { return create(new EstablishedConnection(connection, openHandler)); } /** * Create a new connection manager, which will try to connect using the protocol connection configuration. * * @param configuration the connection configuration * @param openHandler the connection open handler * @return the connection manager */ public static ProtocolConnectionManager create(final ProtocolConnectionConfiguration configuration, final ConnectionOpenHandler openHandler) { return create(new EstablishingConnection(configuration, openHandler)); } /** * Create a new connection manager, which will try to connect using the protocol connection configuration. * * @param configuration the connection configuration * @param openHandler the connection open handler * @param next the next connect connectTask used once disconnected * @return the connection manager */ public static ProtocolConnectionManager create(final ProtocolConnectionConfiguration configuration, final ConnectionOpenHandler openHandler, final ConnectTask next) { return create(new EstablishingConnection(configuration, openHandler, next)); } /** * Create a new connection manager. * * @param connectTask the connect connectTask * @return the connection manager */ public static ProtocolConnectionManager create(final ConnectTask connectTask) { return new ProtocolConnectionManager(connectTask); } private static class EstablishingConnection implements ConnectTask { private final ConnectTask next; private final ConnectionOpenHandler openHandler; private final ProtocolConnectionConfiguration configuration; protected EstablishingConnection(final ProtocolConnectionConfiguration configuration, final ConnectionOpenHandler openHandler) { this.configuration = configuration; this.openHandler = openHandler; this.next = this; } protected EstablishingConnection(final ProtocolConnectionConfiguration configuration, final ConnectionOpenHandler openHandler, final ConnectTask next) { this.configuration = configuration; this.openHandler = openHandler; this.next = next; } @Override public ConnectionOpenHandler getConnectionOpenedHandler() { return openHandler; } @Override public Connection connect() throws IOException { return ProtocolConnectionUtils.connectSync(configuration); } @Override public ConnectTask connectionClosed() { return next; } @Override public void shutdown() { // } } private static class EstablishedConnection implements ConnectTask { private final Connection connection; private final ConnectionOpenHandler openHandler; private EstablishedConnection(final Connection connection, final ConnectionOpenHandler openHandler) { this.connection = connection; this.openHandler = openHandler; } @Override public ConnectionOpenHandler getConnectionOpenedHandler() { return openHandler; } @Override public Connection connect() throws IOException { return connection; } @Override public ConnectTask connectionClosed() { return DISCONNECTED; } @Override public void shutdown() { // } } /** * A {@code ConnectTask} that can be returned from {@link ConnectTask#connectionClosed()} * to terminate further attempts to connect. */ public static final ConnectTask DISCONNECTED = new ConnectTask() { @Override public ConnectionOpenHandler getConnectionOpenedHandler() { return new ConnectionOpenHandler() { @Override public void connectionOpened(final Connection connection) throws IOException { // } }; } @Override public Connection connect() throws IOException { throw ProtocolLogger.ROOT_LOGGER.channelClosed(); } @Override public ConnectTask connectionClosed() { return this; } @Override public void shutdown() { // } }; }