/*
* 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: TransactionStatusManager.java 2342 2006-03-30 13:06:17Z $
*/
package com.arjuna.ats.arjuna.recovery ;
import java.io.* ;
import java.net.* ;
import com.arjuna.ats.arjuna.utils.Utility ;
import com.arjuna.ats.internal.arjuna.recovery.Listener ;
import com.arjuna.ats.internal.arjuna.recovery.TransactionStatusManagerItem ;
import com.arjuna.ats.arjuna.logging.tsLogger;
import com.arjuna.common.util.propertyservice.PropertyManager;
import com.arjuna.common.util.propertyservice.PropertyManagerFactory;
/**
*
* @author Dave Elsworthy (david_elsworthy@hp.com)
* @version $Id: TransactionStatusManager.java 2342 2006-03-30 13:06:17Z $
* @since HPTS 3.0.
*
* @message com.arjuna.ats.arjuna.recovery.TransactionStatusManager_1 [com.arjuna.ats.arjuna.recovery.TransactionStatusManager_1] - Starting service {0} on port {1}
* @message com.arjuna.ats.arjuna.recovery.TransactionStatusManager_2 [com.arjuna.ats.arjuna.recovery.TransactionStatusManager_2] - Listener failed
* @message com.arjuna.ats.arjuna.recovery.TransactionStatusManager_3 [com.arjuna.ats.arjuna.recovery.TransactionStatusManager_3] - TransactionStatusManager started on port {0} and host {1} with service {2}
* @message com.arjuna.ats.arjuna.recovery.TransactionStatusManager_4 [com.arjuna.ats.arjuna.recovery.TransactionStatusManager_4] - Class not found: {0}
* @message com.arjuna.ats.arjuna.recovery.TransactionStatusManager_5 [com.arjuna.ats.arjuna.recovery.TransactionStatusManager_5] - Failed to instantiate service class: {0}
* @message com.arjuna.ats.arjuna.recovery.TransactionStatusManager_6 [com.arjuna.ats.arjuna.recovery.TransactionStatusManager_6] - Illegal access to service class: {0}
* @message com.arjuna.ats.arjuna.recovery.TransactionStatusManager_7 [com.arjuna.ats.arjuna.recovery.TransactionStatusManager_7] - Failed to create server socket on port: {0}
* @message com.arjuna.ats.arjuna.recovery.TransactionStatusManager_8 [com.arjuna.ats.arjuna.recovery.TransactionStatusManager_8] - Invalid port specified {0}
* @message com.arjuna.ats.arjuna.recovery.TransactionStatusManager_9 [com.arjuna.ats.arjuna.recovery.TransactionStatusManager_9] - Could not get unique port.
* @message com.arjuna.ats.arjuna.recovery.TransactionStatusManager_10 [com.arjuna.ats.arjuna.recovery.TransactionStatusManager_10] - Unknown host {0}
* @message com.arjuna.ats.arjuna.recovery.TransactionStatusManager_11 [com.arjuna.ats.arjuna.recovery.TransactionStatusManager_11] - Invalid port specified
* @message com.arjuna.ats.arjuna.recovery.TransactionStatusManager_12 [com.arjuna.ats.arjuna.recovery.TransactionStatusManager_12] - Unknown host specified
* @message com.arjuna.ats.arjuna.recovery.TransactionStatusManager_13 [com.arjuna.ats.arjuna.recovery.TransactionStatusManager_13] - Invalid host or port
* @message com.arjuna.ats.arjuna.recovery.TransactionStatusManager_14 [com.arjuna.ats.arjuna.recovery.TransactionStatusManager_14] - Failed to create server socket on address {0} and port: {1}
*/
public class TransactionStatusManager
{
public TransactionStatusManager()
{
start( _defaultTsmService, null, -1 ) ;
}
public TransactionStatusManager( int port )
{
start( _defaultTsmService, null, port ) ;
}
public TransactionStatusManager( String serviceName )
{
start( serviceName, null, -1 ) ;
}
public TransactionStatusManager( String serviceName, int port )
{
start( serviceName, null, port ) ;
}
/**
* The work item to be executed.
*/
public void addService( Service service, ServerSocket serverSocket )
{
try
{
_listener = new Listener( serverSocket, service );
_listener.setDaemon(true);
if (tsLogger.arjLoggerI18N.isInfoEnabled())
{
tsLogger.arjLoggerI18N.info("com.arjuna.ats.arjuna.recovery.TransactionStatusManager_1",
new Object[]{service.getClass().getName(),
Integer.toString(serverSocket.getLocalPort())});
}
_listener.start() ;
}
catch ( IOException ex )
{
tsLogger.arjLoggerI18N.warn("com.arjuna.ats.arjuna.recovery.TransactionStatusManager_2");
}
}
/**
* Removes the TransactionStatusManager from the object store
* and closes down the listener thread.
*/
// TODO consider adding a shutdown operation (signature change)
public void finalize()
{
if ( ! _finalizeCalled )
{
_finalizeCalled = true ;
_listener.stopListener() ;
TransactionStatusManagerItem.removeThis( Utility.getProcessUid() ) ;
}
}
/**
* Create service and Transaction status manager item.
*/
private void start( String serviceName, String host, int port )
{
try
{
Class serviceClass = Thread.currentThread().getContextClassLoader().loadClass( serviceName ) ;
Service service = (Service) serviceClass.newInstance() ;
ServerSocket socketServer = getTsmServerSocket(host, port);
addService( service, socketServer ) ;
TransactionStatusManagerItem.createAndSave(socketServer.getInetAddress().getHostAddress(), socketServer.getLocalPort() ) ;
if (tsLogger.arjLoggerI18N.isInfoEnabled())
{
tsLogger.arjLoggerI18N.info("com.arjuna.ats.arjuna.recovery.TransactionStatusManager_3",
new Object[]{Integer.toString(socketServer.getLocalPort()), socketServer.getInetAddress().getHostAddress(), serviceName});
}
}
catch ( ClassNotFoundException ex )
{
if (tsLogger.arjLoggerI18N.isWarnEnabled())
{
tsLogger.arjLoggerI18N.warn("com.arjuna.ats.arjuna.recovery.TransactionStatusManager_4",
new Object[]{serviceName});
}
}
catch ( InstantiationException ex )
{
if (tsLogger.arjLoggerI18N.isWarnEnabled())
{
tsLogger.arjLoggerI18N.warn("com.arjuna.ats.arjuna.recovery.TransactionStatusManager_5",
new Object[]{serviceName});
}
}
catch ( IllegalAccessException ex )
{
if (tsLogger.arjLoggerI18N.isWarnEnabled())
{
tsLogger.arjLoggerI18N.warn("com.arjuna.ats.arjuna.recovery.TransactionStatusManager_6",
new Object[]{serviceName});
}
}
catch ( IOException ex )
{
if (tsLogger.arjLoggerI18N.isWarnEnabled())
{
tsLogger.arjLoggerI18N.warn("com.arjuna.ats.arjuna.recovery.TransactionStatusManager_14",
new Object[]{getListenerHostName(), getListenerPort(-1)});
}
throw new com.arjuna.ats.arjuna.exceptions.FatalError(tsLogger.log_mesg.getString("com.arjuna.ats.arjuna.recovery.TransactionStatusManager_9"));
}
}
/**
* Lookup the listener port for the transaction manager
* @param defValue the value to use if no valid port number can be found
* @return the listener port
*/
private int getListenerPort(Integer defValue)
{
// has the port already been bound
if (_port > 0)
return _port;
PropertyManager pm = PropertyManagerFactory.getPropertyManager("com.arjuna.ats.propertymanager", "recoverymanager");
//pm = arjPropertyManager.propertyManager;
String portStr = pm.getProperty(com.arjuna.ats.arjuna.common.Environment.TRANSACTION_STATUS_MANAGER_PORT);
if ( portStr == null || portStr.length() == 0)
{
return DEFAULT_TMS_PORT;
}
else
{
Integer port = Utility.lookupBoundedIntegerProperty(pm, com.arjuna.ats.arjuna.common.Environment.TRANSACTION_STATUS_MANAGER_PORT, defValue,
"com.arjuna.ats.arjuna.recovery.TransactionStatusManager_8",
0, Utility.MAX_PORT);
if (port == null)
throw new com.arjuna.ats.arjuna.exceptions.FatalError(tsLogger.log_mesg.getString("com.arjuna.ats.arjuna.recovery.TransactionStatusManager_11"));
return port;
}
}
private String getListenerHostName()
{
PropertyManager pm = PropertyManagerFactory.getPropertyManager("com.arjuna.ats.propertymanager", "recoverymanager");
//pm = arjPropertyManager.propertyManager;
return pm.getProperty(com.arjuna.ats.arjuna.common.Environment.TRANSACTION_STATUS_MANAGER_ADDRESS);
}
/**
* Create a new listener socket. If the input paramters are invalid use the config properties
* to choose the desired address and port to bind the listener to. A port value of -1 is considered
* invalid.
*
* @param hostNameOverride override the config property for the hostname
* @param portOverride override the config property for the port
* @return a socket bound to the appropriate host and port
* @throws IOException if the host name is unknown
*/
private ServerSocket getTsmServerSocket (String hostNameOverride, int portOverride) throws IOException
{
if (_socket != null)
{
// the socket has already been created
return _socket;
}
if (_port == -1)
{
// a previous attempt to create the socket failed
throw new com.arjuna.ats.arjuna.exceptions.FatalError(tsLogger.log_mesg.getString("com.arjuna.ats.arjuna.recovery.TransactionStatusManager_13"));
}
try
{
String host = hostNameOverride == null ? getListenerHostName() : hostNameOverride;
InetAddress bindAddress = Utility.hostNameToInetAddress(host, "com.arjuna.ats.arjuna.recovery.TransactionStatusManager_10");
_port = portOverride == -1 ? getListenerPort(null) : portOverride;
_socket = new ServerSocket(_port, Utility.BACKLOG, bindAddress);
_port = _socket.getLocalPort();
}
catch (UnknownHostException ex)
{
_port = -1;
throw ex;
}
return _socket;
}
/**
* Listener thread.
*/
private Listener _listener ;
/**
* Default service run on listener thread.
*/
private static final String _defaultTsmService = "com.arjuna.ats.arjuna.recovery.ActionStatusService" ;
/**
* Flag used to ensure finalize gets called just once.
*/
private boolean _finalizeCalled = false ;
/**
* The listener socket
*/
private ServerSocket _socket;
/**
* Bound port for listener socket
* A value of -1 means that the attempt to create the socket failed
*/
private int _port = 0;
/**
* Default bind port is any port
*/
private int DEFAULT_TMS_PORT = 0;
}