/*
* JBoss, Home of Professional Open Source
* Copyright 2006, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License, v. 2.1.
* This program is distributed in the hope that it will be useful, but WITHOUT A
* 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,
* v.2.1 along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
* (C) 2005-2006,
* @author JBoss Inc.
*/
/*
* Copyright (C) 1999-2001 by HP Bluestone Software, Inc. All rights Reserved.
*
* HP Arjuna Labs,
* Newcastle upon Tyne,
* Tyne and Wear,
* UK.
*
* $Id: Listener.java 2342 2006-03-30 13:06:17Z $
*/
package com.arjuna.ats.internal.arjuna.recovery;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.LinkedList;
import java.util.List;
import com.arjuna.ats.arjuna.common.recoveryPropertyManager;
import com.arjuna.ats.arjuna.logging.tsLogger;
import com.arjuna.ats.arjuna.recovery.Service;
public class Listener extends Thread
{
/**
* Creates a listener thread on the specified port
* for the specified service to run.
*/
public Listener( int port, Service service )
throws IOException
{
super( "Listener:" + port );
_listener_port = port;
_listener_service = service;
_listener_socket = new ServerSocket( _listener_port );
if (Listener.setTimeout)
_listener_socket.setSoTimeout( _listener_socket_timeout_in_msecs );
connections = new LinkedList<Socket>();
}
/**
* Creates a listener thread on the specified server socket
* for the specified service to run.
*/
public Listener( ServerSocket serverSocket, Service service )
throws IOException
{
super( "Listener:" + serverSocket.getLocalPort() );
_listener_port = serverSocket.getLocalPort();
_listener_service = service;
_listener_socket = serverSocket;
if (Listener.setTimeout)
_listener_socket.setSoTimeout( _listener_socket_timeout_in_msecs );
connections = new LinkedList<Socket>();
}
/*
* Close down the socket.
*
* this is pointless because this instance is a thread so never gets garbage collected until it has stopped running.
* but that means shutdown will have been called making the call to close in this method redundant.
public void finalize()
{
stopListener();
try
{
_listener_socket.close();
}
catch ( IOException ex )
{
tsLogger.arjLoggerI18N.warn("com.arjuna.ats.internal.arjuna.recovery.Listener_1");
}
}
*/
/**
* Loops waiting for connection requests from client,
* creates a new Connection object for each connection.
*/
public void run()
{
while ( !stopRequested() )
{
try
{
final Socket conn = _listener_socket.accept();
// n.b. add may not occur because a shutdown was requested
if (addConnection(conn)) {
// ok the connection is in the list -- ensure it clears itself out
Connection.Callback callback = new Connection.Callback() {
private Socket _conn = conn;
public void run() {
removeConnection(_conn);
}
};
Connection new_conn = new Connection( conn, _listener_service, callback );
if (tsLogger.logger.isDebugEnabled()) {
tsLogger.logger.debug("Connected to " + conn.getInetAddress().getHostAddress() +
" on port " + conn.getPort() + " on listener port " +
conn.getLocalPort() + " for service " +
_listener_service.getClass().getName());
}
new_conn.start();
}
}
catch ( final InterruptedIOException ex )
{
// timeout on the listener socket expired.
}
catch (final SocketException ex)
{
// we get this if the socket is closed under a call to shutdown
if (tsLogger.logger.isDebugEnabled()) {
tsLogger.logger.debug("Recovery listener existing " +
_listener_service.getClass().getName());
}
}
catch ( final IOException ex )
{
if (tsLogger.logger.isDebugEnabled())
tsLogger.logger.debug("Listener - IOException"+" "+ex);
}
catch (final Exception ex)
{
}
}
}
public synchronized boolean addConnection(Socket conn)
{
if (!_stop_listener) {
connections.add(conn);
return true;
} else {
// a close down request got in between the connection create and the
// call to this method. it will have closed all the other connections
// and will be waiting on this (listener) thread. so close this connection
// before returning false
try {
conn.close();
} catch (Exception e) {
// ignore
}
return false;
}
}
public synchronized void removeConnection(Socket conn)
{
connections.remove(conn);
notifyAll();
}
/**
* Halts running of the listener thread.
*/
public synchronized void stopListener()
{
_stop_listener = true;
try
{
_listener_socket.close(); // in case we're still in accept
}
catch (final Exception ex)
{
}
// there is no need for this as the close will interrupt any i/o that is in progress
// this.interrupt();
// ok, closing a connection socket will cause the connection thread to remove it from the list as it
// exits so we keep on closing them and waiting until the list is empty
while(connections.size() > 0) {
Socket conn = connections.get(0);
try {
conn.close();
} catch (Exception e) {
// ignore
}
try {
wait();
} catch (InterruptedException e) {
// ignore
}
}
// make sure this listener thread has exited before we return
try {
this.join();
} catch (InterruptedException ie) {
}
}
private synchronized boolean stopRequested()
{
return _stop_listener;
}
// Socket & port which client(RecoveryManager) connects to.
private ServerSocket _listener_socket;
private int _listener_port;
// Flag to indicate when to shutdown the listener thread.
private boolean _stop_listener = false;
// Timeout used on for accept.
private int _listener_socket_timeout_in_msecs = 1500;
// The work item to execute.
private Service _listener_service;
private List<Socket> connections;
private static final boolean setTimeout = recoveryPropertyManager.getRecoveryEnvironmentBean().isTimeoutSocket();
}