[jboss-svn-commits] JBL Code SVN: r19417 - in labs/jbosstm/trunk: ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/recovery and 10 other directories.

jboss-svn-commits at lists.jboss.org jboss-svn-commits at lists.jboss.org
Fri Apr 4 09:22:05 EDT 2008


Author: mmusgrov
Date: 2008-04-04 09:22:05 -0400 (Fri, 04 Apr 2008)
New Revision: 19417

Modified:
   labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/common/Environment.java
   labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/recovery/RecoveryManager.java
   labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/recovery/TransactionStatusManager.java
   labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/utils/Utility.java
   labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/recovery/PeriodicRecovery.java
   labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/recovery/RecoveryManagerImple.java
   labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/recovery/TransactionStatusManagerItem.java
   labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/utils/SocketProcessId.java
   labs/jbosstm/trunk/ArjunaCore/arjuna/etc/default-RecoveryManager-properties.xml
   labs/jbosstm/trunk/ArjunaCore/arjuna/etc/default-arjuna-properties.xml
   labs/jbosstm/trunk/ArjunaJTA/jta/etc/default-RecoveryManager-properties.xml
   labs/jbosstm/trunk/ArjunaJTS/jts/classes/com/arjuna/ats/internal/jts/orbspecific/jacorb/recoverycoordinators/JacOrbRCServiceInit.java
   labs/jbosstm/trunk/ArjunaJTS/jts/classes/com/arjuna/ats/jts/common/Environment.java
   labs/jbosstm/trunk/ArjunaJTS/jts/etc/default-RecoveryManager-properties.xml
   labs/jbosstm/trunk/ArjunaJTS/jts/etc/default-jts-properties.xml
   labs/jbosstm/trunk/atsintegration/classes/com/arjuna/ats/jbossatx/jta/TransactionManagerService.java
   labs/jbosstm/trunk/atsintegration/classes/com/arjuna/ats/jbossatx/jts/TransactionManagerService.java
Log:
Updated SocketProcessId such that the port is configurable via the properties
  com.arjuna.ats.internal.arjuna.utils.SocketProcessIdPort
  com.arjuna.ats.internal.arjuna.utils.SocketProcessIdMaxPorts

Added properties to configure the address used by the Recovery Manager and Transaction Status Manager and
Recovery Manager orb:
  com.arjuna.ats.arjuna.recovery.recoveryAddress
  com.arjuna.ats.arjuna.recovery.transactionStatusManagerAddress
  com.arjuna.ats.jts.recoveryManagerAddress

When running inside an AS these properties are ignored (and the AS listen address is used instead).

Resolves issues JBTM-348, JBTM-253 and JBTM-324


Modified: labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/common/Environment.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/common/Environment.java	2008-04-04 13:12:45 UTC (rev 19416)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/common/Environment.java	2008-04-04 13:22:05 UTC (rev 19417)
@@ -137,9 +137,20 @@
     public static final String OBJECTSTORE_SHARE = "com.arjuna.ats.arjuna.objectstore.share";
     public static final String OBJECTSTORE_HIERARCHY_RETRY = "com.arjuna.ats.arjuna.objectstore.hierarchyRetry";
     public static final String OBJECTSTORE_HIERARCHY_TIMEOUT = "com.arjuna.ats.arjuna.objectstore.hierarchyTimeout";
-    public static final String RECOVERY_MANAGER_PORT = "com.arjuna.ats.internal.arjuna.recovery.recoveryPort";
+    public static final String RECOVERY_MANAGER_PORT = "com.arjuna.ats.arjuna.recovery.recoveryPort";
+    public static final String RECOVERY_MANAGER_ADDRESS = "com.arjuna.ats.arjuna.recovery.recoveryAddress";
     public static final String XA_NODE_IDENTIFIER = "com.arjuna.ats.arjuna.xa.nodeIdentifier";
     public static final String DEFAULT_TIMEOUT = "com.arjuna.ats.arjuna.coordinator.defaultTimeout";
-    
+    public static final String TRANSACTION_STATUS_MANAGER_PORT = "com.arjuna.ats.arjuna.recovery.transactionStatusManagerPort";
+    public static final String TRANSACTION_STATUS_MANAGER_ADDRESS = "com.arjuna.ats.arjuna.recovery.transactionStatusManagerAddress";
+    public static final String SOCKET_PROCESS_ID_PORT= "com.arjuna.ats.internal.arjuna.utils.SocketProcessIdPort";
+    public static final String SOCKET_PROCESS_ID_MAX_PORTS= "com.arjuna.ats.internal.arjuna.utils.SocketProcessIdMaxPorts";
+
+    /**
+      * Constant that holds the name of the environment property
+      * for specifying the bind address for transaction services
+      *
+      */
+    public static final String SERVER_BIND_ADDRESS = "jbossts.bind.address";
 }
 

Modified: labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/recovery/RecoveryManager.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/recovery/RecoveryManager.java	2008-04-04 13:12:45 UTC (rev 19416)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/recovery/RecoveryManager.java	2008-04-04 13:22:05 UTC (rev 19417)
@@ -32,9 +32,24 @@
 package com.arjuna.ats.arjuna.recovery;
 
 import java.util.Vector;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+import java.io.IOException;
 
 import com.arjuna.ats.internal.arjuna.recovery.RecoveryManagerImple;
+import com.arjuna.ats.arjuna.utils.Utility;
+import com.arjuna.ats.arjuna.logging.tsLogger;
+import com.arjuna.common.util.propertyservice.PropertyManager;
+import com.arjuna.common.util.propertyservice.PropertyManagerFactory;
 
+/**
+ * @message com.arjuna.ats.arjuna.recovery.RecoveryManager_1 [com.arjuna.ats.arjuna.recovery.RecoveryManager_1] - Invalid recovery manager port specified {0}
+ * @message com.arjuna.ats.arjuna.recovery.RecoveryManager_2 [com.arjuna.ats.arjuna.recovery.RecoveryManager_2] - Invalid recovery manager host specified {0}
+ * @message com.arjuna.ats.arjuna.recovery.RecoveryManager_3 [com.arjuna.ats.arjuna.recovery.RecoveryManager_3] - Recovery manager bound to {0}:{1}
+ * @message com.arjuna.ats.arjuna.recovery.RecoveryManager_4 [com.arjuna.ats.arjuna.recovery.RecoveryManager_4] - Connected to recovery manager on {0}:{1}
+ * @message com.arjuna.ats.arjuna.recovery.RecoveryManager_5 [com.arjuna.ats.arjuna.recovery.RecoveryManager_5] - Invalid recovery manager port specified
+ */
 class ScanThread extends Thread
 {
 
@@ -147,7 +162,7 @@
      * and will return immediately. Notification of completion of the
      * scan is done through the RecoveryScan object.
      *
-     * @param RecoveryScan callback The callback mechanism used to
+     * @param callback callback The callback mechanism used to
      * inform users that the scan has completed. If this is <code>null</code>
      * then no callback will happen and asynchronous scanning will occur.
      */
@@ -207,7 +222,7 @@
     /**
      * Add a recovery module to the system.
      *
-     * @param RecoveryModule module The module to add.
+     * @param module module The module to add.
      */
 
     public final void addModule (RecoveryModule module)
@@ -248,6 +263,70 @@
     {
 	return _mode;
     }
+
+    public static InetAddress getRecoveryManagerHost(boolean useASBindAddress) throws UnknownHostException
+    {
+        PropertyManager pm = PropertyManagerFactory.getPropertyManager("com.arjuna.ats.propertymanager", "recoverymanager");
+
+        if ( pm == null )
+            return InetAddress.getLocalHost();
+
+        String hostPropName = com.arjuna.ats.arjuna.common.Environment.RECOVERY_MANAGER_ADDRESS;
+        String host = ((useASBindAddress) ? Utility.getServerBindAddress(pm, hostPropName) : pm.getProperty(hostPropName));
+
+        return Utility.hostNameToInetAddress(host, "com.arjuna.ats.arjuna.recovery.RecoveryManager_2");
+    }
+
+    public static int getRecoveryManagerPort()
+    {
+        PropertyManager pm = PropertyManagerFactory.getPropertyManager("com.arjuna.ats.propertymanager", "recoverymanager");
+
+        if (pm == null)
+            return 0;
+
+        String portPropName = com.arjuna.ats.arjuna.common.Environment.RECOVERY_MANAGER_PORT;
+        Integer port = Utility.lookupBoundedIntegerProperty(pm, portPropName, null,
+                    "com.arjuna.ats.arjuna.recovery.RecoveryManager_1",
+                    0, Utility.MAX_PORT);
+
+        if (port == null)
+        {
+            String portStr = pm.getProperty(portPropName);
+
+           /*
+            * if the property files specified a value for the port which is invalid throw a fatal error. An empty value or no value
+            * corresponds to any port
+            */
+            if (portStr == null || portStr.length() == 0)
+                port = 0;
+            else
+                throw new com.arjuna.ats.arjuna.exceptions.FatalError(tsLogger.log_mesg.getString("com.arjuna.ats.arjuna.recovery.RecoveryManager_5"));
+        }
+
+        return port;
+    }
+
+    /**
+     * Obtain a client connection to the recovery manager
+     * 
+     * @param useASBindAddress if true and the recovery manager is running within an appserver then
+     *  bind the socket to the same address that the AS is using. Otherwise use the environment config
+     *  to choose which address to bind to
+     * @return a bound client socket connection to the recovery manager
+     * @throws IOException
+     */
+    public static Socket getClientSocket (boolean useASBindAddress) throws IOException
+    {
+        Socket socket = new Socket(getRecoveryManagerHost(useASBindAddress), getRecoveryManagerPort());
+
+        if (tsLogger.arjLogger.isInfoEnabled())
+        {
+            tsLogger.arjLoggerI18N.info("com.arjuna.ats.arjuna.recovery.RecoveryManager_4",
+                    new Object[]{socket.getInetAddress().getHostAddress(), socket.getLocalPort()});
+        }
+
+        return socket;
+    }
     
     /**
      * Run the RecoveryManager. See Administration manual for details.

Modified: labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/recovery/TransactionStatusManager.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/recovery/TransactionStatusManager.java	2008-04-04 13:12:45 UTC (rev 19416)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/recovery/TransactionStatusManager.java	2008-04-04 13:22:05 UTC (rev 19417)
@@ -33,20 +33,16 @@
 
 import java.io.* ;
 import java.net.* ;
-import java.util.* ;
 
-import com.arjuna.common.util.propertyservice.PropertyManager;
 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.internal.arjuna.utils.SocketProcessId;
 import com.arjuna.ats.arjuna.common.arjPropertyManager;
 
 import com.arjuna.ats.arjuna.logging.tsLogger;
-import com.arjuna.ats.arjuna.logging.FacilityCode;
+import com.arjuna.common.util.propertyservice.PropertyManager;
+import com.arjuna.common.util.propertyservice.PropertyManagerFactory;
 
-import com.arjuna.common.util.logging.*;
-
 /**
  * This implementation is tied closely with the socket/port version of
  * getpid. If a pid is obtained via a port, then this class will obtain
@@ -60,39 +56,40 @@
  *
  * @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} with service {1}
+ * @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()
    {
-      int port = getTsmPort();
-
-      start( _defaultTsmService, port ) ;
+      start( _defaultTsmService, null, -1 ) ;
    }
    
    public TransactionStatusManager( int port )
    {
-      start( _defaultTsmService, port ) ;
+      start( _defaultTsmService, null, port ) ;
    }
     
    public TransactionStatusManager( String serviceName )
    {
-      int port = getTsmPort();
-       
-      start( serviceName, port ) ;
+      start( serviceName, null, -1 ) ;
    }
     
    public TransactionStatusManager( String serviceName, int port  )
    {
-      start( serviceName, port ) ;
+      start( serviceName, null, port ) ;
    }
    
    /**
@@ -134,32 +131,28 @@
 	   TransactionStatusManagerItem.removeThis( Utility.getProcessUid() ) ;
       } 
    }
-   
+
    /**
     * Create service and Transaction status manager item.
     */
-   private void start( String serviceName, int port )
+   private void start( String serviceName, String host, int port )
    {
-      int localPort = 0;
-
       try
       {
          Class serviceClass = Thread.currentThread().getContextClassLoader().loadClass( serviceName ) ;
    
          Service service = (Service) serviceClass.newInstance() ;
             
-         ServerSocket socketServer = getTsmServerSocket(port);
-         
-         localPort = socketServer.getLocalPort();
+         ServerSocket socketServer = getTsmServerSocket(host, port);
 
          addService( service, socketServer ) ;
    
-         TransactionStatusManagerItem.createAndSave( socketServer.getLocalPort() ) ;
+         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(localPort), serviceName});
+					  new Object[]{Integer.toString(socketServer.getLocalPort()), socketServer.getInetAddress().getHostAddress(), serviceName});
 	 }
       }
       catch ( ClassNotFoundException ex )
@@ -190,70 +183,100 @@
       {
 	  if (tsLogger.arjLoggerI18N.isWarnEnabled())
 	  {
-	      tsLogger.arjLoggerI18N.warn("com.arjuna.ats.arjuna.recovery.TransactionStatusManager_7", 
-					  new Object[]{Integer.toString(localPort)});
+          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"));
       }
    }
-   
+
     /**
-     * If the socket based version of getpid is being used, then it will
-     * have already assigned a port and created a ServerSocket. We should
-     * use this - don't want too many ports assigned to a given process,
-     * especially if they aren't used. In which case, the port parameter
-     * is not used, because we got it from the getpid class anyway.
+     * 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;
 
-private static final ServerSocket getTsmServerSocket (int port) throws IOException
+        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()
     {
-	ServerSocket socket = SocketProcessId.getSocket();
-	
-	return ((socket == null) ? new ServerSocket(port) : socket);
+        PropertyManager pm = PropertyManagerFactory.getPropertyManager("com.arjuna.ats.propertymanager", "recoverymanager");
+        //pm = arjPropertyManager.propertyManager;
+
+        return Utility.getServerBindAddress(pm, com.arjuna.ats.arjuna.common.Environment.TRANSACTION_STATUS_MANAGER_ADDRESS);
     }
-    
-   /**
-    * Return the port specified by the property
-    * com.arjuna.ats.arjuna.recovery.TransactionStatusManagerPort,
-    * otherwise return a default port.
-    *
-    * If the socket based version of getpid is being used, then it will
-    * already have assigned our port, so use that.
-    */
 
-private static final int getTsmPort ()
-   {
-       if (SocketProcessId.getSocket() == null)
-       {
-	   int port = _defaultTsmPort ;
-	   
-	   // TODO these properties should be documented!!
+    /**
+     * 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;
+        }
 
-	   String tsmPortStr = arjPropertyManager.propertyManager.getProperty("com.arjuna.ats.arjuna.recovery.transactionStatusManagerPort" ) ;
-	   
-	   if ( tsmPortStr != null )
-	   {
-	       try
-	       {
-		   port = Integer.parseInt( tsmPortStr ) ;
-	       }
-	       catch ( Exception ex )
-	       {
-		   if (tsLogger.arjLoggerI18N.isWarnEnabled())
-		   {
-		       tsLogger.arjLoggerI18N.warn("com.arjuna.ats.arjuna.recovery.TransactionStatusManager_8", 
-						   new Object[]{ex});
-		   }
-	       }
-	   }
+        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"));
+        }
 
-	   return port ;
-       }
-       else
-	   return SocketProcessId.getSocket().getLocalPort();
-   }
+        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.
      */
@@ -263,17 +286,27 @@
      * Default service run on listener thread.
      */ 
     private static final String _defaultTsmService = "com.arjuna.ats.arjuna.recovery.ActionStatusService" ;
-    
+
     /**
-     * Default port is any free port.
-     */
-    private static final int _defaultTsmPort = 0 ;
-    
-    /**
      * 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;
 }
 
 

Modified: labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/utils/Utility.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/utils/Utility.java	2008-04-04 13:12:45 UTC (rev 19416)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/utils/Utility.java	2008-04-04 13:22:05 UTC (rev 19417)
@@ -35,16 +35,11 @@
 
 import com.arjuna.ats.arjuna.common.*;
 import com.arjuna.common.util.propertyservice.PropertyManager;
-import java.util.Properties;
-import java.io.*;
+
 import java.net.InetAddress;
 
-import com.arjuna.ats.arjuna.exceptions.FatalError;
 import java.net.UnknownHostException;
 import java.lang.NumberFormatException;
-import java.lang.StringIndexOutOfBoundsException;
-import java.io.IOException;
-import java.io.FileNotFoundException;
 
 /**
  * Various useful functions that we wrap in a single class.
@@ -193,6 +188,100 @@
     }
 
     /**
+     * If the transaction service is running within a JBoss instance return the jboss bind port
+     *
+     * @param hostPropName the hostname property to use if running standalone (must not be null)
+     * @return the host name
+     */
+    public static final String getServerBindAddress(PropertyManager pm, String hostPropName)
+    {
+        String host = System.getProperty(Environment.SERVER_BIND_ADDRESS);
+
+        if (host == null)
+        {
+            host = pm.getProperty(hostPropName);
+        }
+
+        return host;
+    }
+    
+    /**
+     * Convert a host name into an InetAddress object
+     *
+     * @param host if empty or null then the loopback address is used
+     * @param messageKey message key to a report warning if host is unknown
+     * @return an InetAddress structure corresponding the desired host name
+     * @throws UnknownHostException if the hostname cannot be found
+     */
+    public static InetAddress hostNameToInetAddress(String host, String messageKey) throws UnknownHostException
+    {
+        try {
+            if (host == null || host.length() == 0)
+                return InetAddress.getLocalHost();
+            else
+                return InetAddress.getByName(host);
+        }
+        catch (UnknownHostException ex)
+        {
+            /*
+             * The hostname is unknown
+             */
+            if (tsLogger.arjLoggerI18N.isWarnEnabled() && messageKey != null)
+                tsLogger.arjLoggerI18N.warn(messageKey,ex);
+
+            throw ex;
+        }
+    }
+
+    /**
+     * Lookup and valid a port number.
+     *
+     * @param intProperty property name of an integer valued property
+     * @param defValue a value to return if intProperty is invalid. If a null value is used and intProperty is invalid then @com.arjuna.ats.arjuna.exceptions.FatalError
+     *      is thrown
+     * @param warnMsgKey message key to report a warning if property values is invalid
+     * @param minValue minimum value for the integer propertry
+     * @param maxValue maximum value for the integer propertry
+     * @return the integer value of property or the default value if the property does not represent an integer
+     */
+    public static Integer lookupBoundedIntegerProperty(PropertyManager pm, String intProperty, Integer defValue, String warnMsgKey, int minValue, int maxValue)
+    {
+        String intStr = pm.getProperty(intProperty);
+
+        // if the propery is not found or empty just return the default
+        if (intStr == null || intStr.length() == 0)
+        {
+            return defValue;
+        }
+
+        try
+        {
+            int i = Integer.parseInt(intStr);
+
+            if (i < minValue || i > maxValue)
+            {
+                // the value is an invalid number
+                if (warnMsgKey != null && tsLogger.arjLoggerI18N.isWarnEnabled())
+                {
+                    tsLogger.arjLoggerI18N.warn(warnMsgKey, new Object[]{intStr});
+                }
+            }
+            else
+            {
+                return i;
+            }
+        }
+        catch (Exception ex)
+        {
+            // the value is not a number
+            if (warnMsgKey != null && tsLogger.arjLoggerI18N.isWarnEnabled())
+                tsLogger.arjLoggerI18N.warn(warnMsgKey,ex);
+        }
+
+        return defValue;
+    }
+
+    /**
      * @return the process id. This had better be unique between processes
      * on the same machine. If not we're in trouble!
      *
@@ -271,6 +360,14 @@
 private static final String hexStart = "0x";
 private static final String defaultProcessId = "com.arjuna.ats.internal.arjuna.utils.SocketProcessId";
 
- 
+    /**
+     * The maximum queue length for incoming connection indications (a request to connect)
+     */
+    public static final int BACKLOG = 50;
+
+    /**
+     * Maximum value for a socket port
+     */
+    public static final int MAX_PORT = 65535; 
 }
 

Modified: labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/recovery/PeriodicRecovery.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/recovery/PeriodicRecovery.java	2008-04-04 13:12:45 UTC (rev 19416)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/recovery/PeriodicRecovery.java	2008-04-04 13:22:05 UTC (rev 19417)
@@ -42,10 +42,12 @@
 
 import com.arjuna.ats.arjuna.recovery.RecoveryModule;
 import com.arjuna.ats.arjuna.recovery.RecoveryEnvironment;
+import com.arjuna.ats.arjuna.recovery.RecoveryManager;
 import com.arjuna.ats.arjuna.common.arjPropertyManager;
 
 import com.arjuna.ats.arjuna.logging.FacilityCode;
 import com.arjuna.ats.arjuna.logging.tsLogger;
+import com.arjuna.ats.arjuna.utils.Utility;
 
 import com.arjuna.common.util.logging.*;
 
@@ -70,6 +72,9 @@
  * @message com.arjuna.ats.internal.arjuna.recovery.PeriodicRecovery_8 [com.arjuna.ats.internal.arjuna.recovery.PeriodicRecovery_8] - Invalid port specified {0}
  * @message com.arjuna.ats.internal.arjuna.recovery.PeriodicRecovery_9 [com.arjuna.ats.internal.arjuna.recovery.PeriodicRecovery_9] - Could not create recovery listener {0}
  * @message com.arjuna.ats.internal.arjuna.recovery.PeriodicRecovery_10 [com.arjuna.ats.internal.arjuna.recovery.PeriodicRecovery_10] - Ignoring request to scan because RecoveryManager state is: {0}
+ * @message com.arjuna.ats.internal.arjuna.recovery.PeriodicRecovery_11 [com.arjuna.ats.internal.arjuna.recovery.PeriodicRecovery_11] - Invalid host specified {0}
+ * @message com.arjuna.ats.internal.arjuna.recovery.PeriodicRecovery_12 [com.arjuna.ats.internal.arjuna.recovery.PeriodicRecovery_12] - Could not create recovery listener
+ * @message com.arjuna.ats.internal.arjuna.recovery.PeriodicRecovery_13 [com.arjuna.ats.internal.arjuna.recovery.PeriodicRecovery_13] - Recovery manager listening on endpoint {0}:{1}
  */
 
 public class PeriodicRecovery extends Thread
@@ -151,6 +156,13 @@
 
 	  _listener = new Listener(getServerSocket(), _workerService);
 	  _listener.setDaemon(true);
+
+      if (tsLogger.arjLoggerI18N.isInfoEnabled())
+          tsLogger.arjLoggerI18N.info(
+                  "com.arjuna.ats.internal.arjuna.recovery.PeriodicRecovery_13",
+                  new Object[] {
+                          _socket.getInetAddress().getHostAddress(), _socket.getLocalPort()
+                  });
       }
       catch (Exception ex)
       {
@@ -294,40 +306,20 @@
        }
    }
 
-   /**
-    * Return the port specified by the property
-    * com.arjuna.ats.internal.arjuna.recovery.recoveryPort,
-    * otherwise return a default port.
-    */
-
+    /**
+     *
+     * @return a bound server socket corresponding to the recovery manager
+     * @throws IOException if the host name is unknown or the endpoint has already been bound
+     */
     public static ServerSocket getServerSocket () throws IOException
     {
-	    if (_socket == null)
-	    {
-		// TODO these properties should be documented!!
+        synchronized (PeriodicRecovery._socketLock)
+        {
+            if (_socket == null)
+                _socket = new ServerSocket(RecoveryManager.getRecoveryManagerPort(), Utility.BACKLOG, RecoveryManager.getRecoveryManagerHost(true));
 
-		String tsmPortStr = arjPropertyManager.propertyManager.getProperty(com.arjuna.ats.arjuna.common.Environment.RECOVERY_MANAGER_PORT);
-		int port = 0;
-
-		if (tsmPortStr != null)
-		{
-		    try
-		    {
-			port = Integer.parseInt( tsmPortStr );
-		    }
-		    catch (Exception ex)
-		    {
-			if (tsLogger.arjLoggerI18N.isWarnEnabled())
-			{
-			    tsLogger.arjLoggerI18N.warn("com.arjuna.ats.internal.arjuna.recovery.PeriodicRecovery_8", new Object[]{ex});
-			}
-		    }
-		}
-
-		_socket = new ServerSocket(port);
-	    }
-
-	return _socket;
+            return _socket;
+        }
     }
 
    /**
@@ -1042,6 +1034,7 @@
      * socket used by listener worker thread
      */
     private static ServerSocket _socket = null;
+    private static final Object _socketLock = new Object();
 
     /**
      * listener thread running worker service

Modified: labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/recovery/RecoveryManagerImple.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/recovery/RecoveryManagerImple.java	2008-04-04 13:12:45 UTC (rev 19416)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/recovery/RecoveryManagerImple.java	2008-04-04 13:22:05 UTC (rev 19417)
@@ -40,6 +40,7 @@
 import com.arjuna.ats.arjuna.exceptions.FatalError;
 import com.arjuna.ats.arjuna.recovery.RecoveryConfiguration;
 import com.arjuna.ats.arjuna.recovery.RecoveryModule;
+import com.arjuna.ats.arjuna.recovery.RecoveryManager;
 import com.arjuna.ats.arjuna.logging.FacilityCode;
 import com.arjuna.ats.arjuna.logging.tsLogger;
 
@@ -74,6 +75,9 @@
 	 * @message com.arjuna.ats.internal.arjuna.recovery.ready
 	 *          [com.arjuna.ats.internal.arjuna.recovery.ready]
 	 *          RecoveryManagerImple is ready on port {0}
+     * @message com.arjuna.ats.internal.arjuna.recovery.fail
+	 *          [com.arjuna.ats.internal.arjuna.recovery.fail]
+	 *          RecoveryManagerImple: cannot bind to socket on address {0} and port {1}
 	 */
 
 	public RecoveryManagerImple (boolean threaded)
@@ -112,12 +116,34 @@
 
 		/*
 		 * Check whether there is a recovery daemon running - only allow one per
-		 * machine (currently!)
+		 * object store
 		 */
 
-		if (activeRecoveryManager())
+		if (isRecoveryManagerEndPointInUse())
 		{
-			throw new FatalError("Recovery manager already active!");
+            if (tsLogger.arjLoggerI18N.isFatalEnabled())
+            {
+                try
+                {
+                    tsLogger.arjLoggerI18N.fatal(
+                            "com.arjuna.ats.internal.arjuna.recovery.fail",
+                            new Object[] {
+                                    RecoveryManager.getRecoveryManagerHost(true), RecoveryManager.getRecoveryManagerPort()
+                            }
+                    );
+                }
+                catch (Throwable t)
+                {
+                    tsLogger.arjLoggerI18N.fatal(
+                            "com.arjuna.ats.internal.arjuna.recovery.fail",
+                            new Object[] {
+                                    "unknown", "unknown"
+                            }
+                    );
+                }
+            }
+
+            throw new FatalError("Recovery manager already active (or recovery port and address are in use)!");
 		}
 
 		// start the expiry scanners
@@ -221,28 +247,30 @@
 		stop(true);
 	}
 
-	private final boolean activeRecoveryManager ()
+    /**
+     * Test whether the recovery manager (RM) port and address are available - if not assume that another
+     * recovery manager is already active.
+     *
+     * Ideally this method needs to discover whether or not another RM is already monitoring the object store
+     *
+     * @return true if the RM port and address are in use
+     */
+    private final boolean isRecoveryManagerEndPointInUse ()
 	{
-		// we should be checking for the port in use or something!
+        try
+        {
+            /*
+             * attempt to create the server socket. If an exception is thrown then some other
+             * process is using the RM endpoint
+             */
+            PeriodicRecovery.getServerSocket();
 
-		SocketProcessId socket = null;
-		boolean active = false;
-
-		try
-		{
-			socket = new SocketProcessId();
-
-			if (socket.getpid() == -1) 
-				active = true;
-		}
-		catch (FatalError ex)
-		{
-			// already active on that port
-
-			active = true;
-		}
-
-		return active;
+            return false;
+        }
+        catch (Throwable e)
+        {
+            return true;
+        }
 	}
 
 }

Modified: labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/recovery/TransactionStatusManagerItem.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/recovery/TransactionStatusManagerItem.java	2008-04-04 13:12:45 UTC (rev 19416)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/recovery/TransactionStatusManagerItem.java	2008-04-04 13:22:05 UTC (rev 19417)
@@ -74,7 +74,20 @@
 	    }
 	return ret_status ;
     }
-    
+
+    public static boolean createAndSave(String hostAddress, int port )
+    {
+        boolean ret_status = true ;
+
+        if ( _singularItem == null )
+        {
+            _singularItem = new TransactionStatusManagerItem(hostAddress, port );
+
+            ret_status = _singularItem.saveThis();
+        }
+        return ret_status ;
+    }
+
     /**
      * Get a reference to the Object Store.
      */
@@ -343,8 +356,40 @@
 	    }
 	}
     }
-    
+
     /**
+     * Constructor which obtains the process uid for
+     * use with the specified host and port.
+     */
+    private TransactionStatusManagerItem (String host, int port )
+    {
+        _pidUid = Utility.getProcessUid() ;
+        _port = port ;
+
+        try
+        {
+            // make sure the passed in host is valid
+            Utility.hostNameToInetAddress(host, "com.arjuna.ats.internal.arjuna.recovery.TransactionStatusManagerItem_4");
+            _host = host;
+
+            if (tsLogger.arjLogger.isInfoEnabled())
+            {
+                tsLogger.arjLogger.info
+                        ( "TransactionStatusManagerItem - " + "host: " + _host +
+                                " port: " + _port ) ;
+            }
+        }
+        catch ( UnknownHostException ex )
+        {
+            if (tsLogger.arjLoggerI18N.isWarnEnabled())
+            {
+                tsLogger.arjLoggerI18N.warn("com.arjuna.ats.internal.arjuna.recovery.TransactionStatusManagerItem_4",
+                        new Object[]{ex});
+            }
+        }
+    }
+
+    /**
      * Used by a Recovery Manager to recreate a Transaction
     * status manager contact item.
     */

Modified: labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/utils/SocketProcessId.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/utils/SocketProcessId.java	2008-04-04 13:12:45 UTC (rev 19416)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/utils/SocketProcessId.java	2008-04-04 13:22:05 UTC (rev 19417)
@@ -32,21 +32,15 @@
 package com.arjuna.ats.internal.arjuna.utils;
 
 import com.arjuna.ats.arjuna.logging.tsLogger;
-import com.arjuna.common.util.propertyservice.PropertyManager;
+import com.arjuna.ats.arjuna.common.Environment;
 import com.arjuna.ats.arjuna.common.arjPropertyManager;
 
-import com.arjuna.ats.arjuna.utils.Process;
 import com.arjuna.ats.arjuna.utils.Utility;
 
-import java.io.*;
 import java.net.*;
 
 import com.arjuna.ats.arjuna.exceptions.FatalError;
-import java.net.UnknownHostException;
-import java.lang.NumberFormatException;
-import java.lang.StringIndexOutOfBoundsException;
 import java.io.IOException;
-import java.io.FileNotFoundException;
 
 /**
  * Obtains a unique value to represent the process id via sockets and
@@ -70,6 +64,7 @@
      *
      * @message com.arjuna.ats.internal.arjuna.utils.SocketProcessId_1 [com.arjuna.ats.internal.arjuna.utils.SocketProcessId_1]- Invalid port specified 
      * @message com.arjuna.ats.internal.arjuna.utils.SocketProcessId_2 [com.arjuna.ats.internal.arjuna.utils.SocketProcessId_2] - SocketProcessId.getpid could not get unique port.
+     * @message com.arjuna.ats.internal.arjuna.utils.SocketProcessId_3 [com.arjuna.ats.internal.arjuna.utils.SocketProcessId_3]- Invalid value for SocketProcessIdMaxPorts specified {0}
      */
     
     public int getpid ()
@@ -80,40 +75,32 @@
 	    {
 		if (_theSocket == null)
 		{
-		    int port = _defaultPort;
-      
-		    String portStr = arjPropertyManager.propertyManager.getProperty("com.arjuna.ats.arjuna.recovery.transactionStatusManagerPort");
+            Integer port = Utility.lookupBoundedIntegerProperty(arjPropertyManager.propertyManager, Environment.SOCKET_PROCESS_ID_PORT, _defaultPort,
+                        "com.arjuna.ats.internal.arjuna.utils.SocketProcessId_1",
+                        0, Utility.MAX_PORT);
+            Integer maxPorts = Utility.lookupBoundedIntegerProperty(arjPropertyManager.propertyManager, Environment.SOCKET_PROCESS_ID_MAX_PORTS, 1,
+                        "com.arjuna.ats.internal.arjuna.utils.SocketProcessId_3",
+                        0, Utility.MAX_PORT);
+            int maxPort;
+                          
+            if (maxPorts <= 1)
+            {
+                maxPort = port;
+            }
+            else if (Utility.MAX_PORT - maxPorts < port)
+            {
+                maxPort = Utility.MAX_PORT;
+            }
+            else
+            {
+                maxPort = port + maxPorts;
+            }
 
-		    if ( portStr != null )
-		    {
-			try
-			{
-			    port = Integer.parseInt(portStr);
-			}
-			catch (Exception ex)
-			{
-			    if (tsLogger.arjLoggerI18N.isWarnEnabled())
-				tsLogger.arjLoggerI18N.warn("com.arjuna.ats.internal.arjuna.utils.SocketProcessId_1",ex);
-			    
-			    port = -1;
-			}
-		    }
-			
-		    if (port != -1)
-		    {
-			try
-			{
-			    _theSocket = new ServerSocket(port);
-			    
-			    _thePort = _theSocket.getLocalPort();
-			}
-			catch (Exception ex)
-			{
-			    _thePort = -1;
-			}
-		    }
-		    else
-			_thePort = -1;
+            do {
+                _theSocket = createSocket(port);
+            } while (_theSocket == null && ++port < maxPort);
+
+	    	_thePort = ((_theSocket == null) ? -1 : _theSocket.getLocalPort());
 		}
 	    }
 	}
@@ -124,8 +111,20 @@
 	return _thePort;
     }
 
-    public static final ServerSocket getSocket ()
+    private static ServerSocket createSocket(int port)
     {
+        try
+        {
+            return new ServerSocket(port);
+        }
+        catch (IOException e)
+        {
+            return null;
+        }
+    }
+
+    public static ServerSocket getSocket ()
+    {
 	synchronized (SocketProcessId._lock)
 	{
 	    return _theSocket;
@@ -134,7 +133,7 @@
     
     private static int          _thePort = 0;
     private static ServerSocket _theSocket = null;
-    private static Object       _lock = new Object();
+    private static final Object       _lock = new Object();
 
     /**
      * Default port is any free port.

Modified: labs/jbosstm/trunk/ArjunaCore/arjuna/etc/default-RecoveryManager-properties.xml
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/etc/default-RecoveryManager-properties.xml	2008-04-04 13:12:45 UTC (rev 19416)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/etc/default-RecoveryManager-properties.xml	2008-04-04 13:22:05 UTC (rev 19417)
@@ -79,7 +79,19 @@
       <property
         name="com.arjuna.ats.arjuna.recovery.expiryScanInterval"
         value="12"/>
+
       <!--
+        The address and port number on which the recovery manager listens
+        If running within an AS then the address the AS is bound to (jboss.bind.address) takes precedence
+      -->
+      <property
+        name="com.arjuna.ats.arjuna.recovery.recoveryPort"
+        value="4712"/>
+      <property
+        name="com.arjuna.ats.arjuna.recovery.recoveryAddress"
+        value=""/>
+
+      <!--
         Age, in hours, for removal of transaction status manager item.
         This should be longer than any ts-using process will remain running.
         Zero = Never removed.  Default is 12.
@@ -94,5 +106,15 @@
       <property
         name="com.arjuna.ats.arjuna.recovery.transactionStatusManagerPort"
         value="0"/>
+
+      <!--
+        Use this to fix the address on which the TransactionStatusManager binds,
+        The default behaviour is to use the loopback address (ie localhost).
+        If running within an AS then the address the AS is bound to (jboss.bind.address) takes precedence
+      -->
+      <property
+        name="com.arjuna.ats.arjuna.recovery.transactionStatusManagerAddress"
+        value=""/>
+
     </properties>
 </transaction-service>

Modified: labs/jbosstm/trunk/ArjunaCore/arjuna/etc/default-arjuna-properties.xml
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/etc/default-arjuna-properties.xml	2008-04-04 13:12:45 UTC (rev 19416)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/etc/default-arjuna-properties.xml	2008-04-04 13:22:05 UTC (rev 19417)
@@ -123,6 +123,28 @@
         name="com.arjuna.ats.arjuna.xa.nodeIdentifier"
         value="1"/>
 
+      <!--
+        Base port number for determining a unique number to associate with an instance of the transaction service
+        (which is needed in order to support multiple instances on the same machine).
+        Use the value 0 to allow the system to select the first available port number.
+        If the port number is non-zero and the port is in use then the value will be incremented until either a successful binding
+        to the loopback address is created or until the the maximum number of ports (specified by the
+        com.arjuna.ats.internal.arjuna.utils.SocketProcessIdMaxPorts property) have been tried or until the port number
+        reaches the maximum possible port number.
+      -->
+      <property
+        name="com.arjuna.ats.internal.arjuna.utils.SocketProcessIdPort"
+        value="0"/>
+
+      <!--
+        The maximum number of ports to try starting from the value specified by the property
+        com.arjuna.ats.internal.arjuna.utils.SocketProcessIdPort. Any non-numeric or value less than 1 will
+        defautl to 1.
+      -->
+      <property
+        name="com.arjuna.ats.internal.arjuna.utils.SocketProcessIdMaxPorts"
+        value="1"/>
+
       <!-- property
         name="com.arjuna.ats.arjuna.coordinator.actionStore"
 		value="HashedActionStore"

Modified: labs/jbosstm/trunk/ArjunaJTA/jta/etc/default-RecoveryManager-properties.xml
===================================================================
--- labs/jbosstm/trunk/ArjunaJTA/jta/etc/default-RecoveryManager-properties.xml	2008-04-04 13:12:45 UTC (rev 19416)
+++ labs/jbosstm/trunk/ArjunaJTA/jta/etc/default-RecoveryManager-properties.xml	2008-04-04 13:22:05 UTC (rev 19417)
@@ -50,6 +50,20 @@
         name="com.arjuna.ats.arjuna.recovery.recoveryBackoffPeriod"
         value="10"/>
       <!--
+        The port number on which the recovery manager listens.
+      -->
+      <property
+        name="com.arjuna.ats.arjuna.recovery.recoveryPort"
+        value="4712"/>
+      <!--
+        The address on which the recovery manager listens.
+        If running within an AS then the address the AS is bound to (jboss.bind.address) takes precedence
+      -->
+      <property
+        name="com.arjuna.ats.arjuna.recovery.recoveryAddress"
+        value=""/>
+
+      <!--
         Periodic recovery modules to use.  Invoked in sort-order of names.
       -->
       <property
@@ -99,5 +113,14 @@
         name="com.arjuna.ats.arjuna.recovery.transactionStatusManagerPort"
         value="0"/>
 
+      <!--
+        Use this to fix the address on which the TransactionStatusManager binds,
+        The default behaviour is to use the loopback address (ie localhost).
+        If running within an AS then the address the AS is bound to (jboss.bind.address) takes precedence
+      -->
+      <property
+        name="com.arjuna.ats.arjuna.recovery.transactionStatusManagerAddress"
+        value=""/>
+
     </properties>
 </transaction-service>

Modified: labs/jbosstm/trunk/ArjunaJTS/jts/classes/com/arjuna/ats/internal/jts/orbspecific/jacorb/recoverycoordinators/JacOrbRCServiceInit.java
===================================================================
--- labs/jbosstm/trunk/ArjunaJTS/jts/classes/com/arjuna/ats/internal/jts/orbspecific/jacorb/recoverycoordinators/JacOrbRCServiceInit.java	2008-04-04 13:12:45 UTC (rev 19416)
+++ labs/jbosstm/trunk/ArjunaJTS/jts/classes/com/arjuna/ats/internal/jts/orbspecific/jacorb/recoverycoordinators/JacOrbRCServiceInit.java	2008-04-04 13:22:05 UTC (rev 19417)
@@ -55,6 +55,8 @@
 import com.arjuna.ats.arjuna.coordinator.TxControl;
 import com.arjuna.ats.arjuna.objectstore.ObjectStore;
 import com.arjuna.ats.arjuna.state.*;
+import com.arjuna.ats.arjuna.utils.Utility;
+
 import java.io.IOException;
 import java.util.Properties;
 
@@ -70,7 +72,7 @@
  * @message com.arjuna.ats.internal.jts.orbspecific.jacorb.recoverycoordinators.JacOrbRCServiceInit_3 [com.arjuna.ats.internal.jts.orbspecific.jacorb.recoverycoordinators.JacOrbRCServiceInit_3] - JacOrbRCServiceInit - Failed to start RC service
  * @message com.arjuna.ats.internal.jts.orbspecific.jacorb.recoverycoordinators.JacOrbRCServiceInit_4 [com.arjuna.ats.internal.jts.orbspecific.jacorb.recoverycoordinators.JacOrbRCServiceInit_4] - Unable to create file ObjectId
  * @message com.arjuna.ats.internal.jts.orbspecific.jacorb.recoverycoordinators.JacOrbRCServiceInit_5 [com.arjuna.ats.internal.jts.orbspecific.jacorb.recoverycoordinators.JacOrbRCServiceInit_5] - Unable to create file ObjectId - security problems
- * @message com.arjuna.ats.internal.jts.orbspecific.jacorb.recoverycoordinators.JacOrbRCServiceInit_6 [com.arjuna.ats.internal.jts.orbspecific.jacorb.recoverycoordinators.JacOrbRCServiceInit_6] - Staring RecoveryServer ORB on port {0}
+ * @message com.arjuna.ats.internal.jts.orbspecific.jacorb.recoverycoordinators.JacOrbRCServiceInit_6 [com.arjuna.ats.internal.jts.orbspecific.jacorb.recoverycoordinators.JacOrbRCServiceInit_6] - Starting RecoveryServer ORB on port {0} and address {1}
  * @message com.arjuna.ats.internal.jts.orbspecific.jacorb.recoverycoordinators.JacOrbRCServiceInit_6a [com.arjuna.ats.internal.jts.orbspecific.jacorb.recoverycoordinators.JacOrbRCServiceInit_6a] - Sharing RecoveryServer ORB on port {0}
  * @message com.arjuna.ats.internal.jts.orbspecific.jacorb.recoverycoordinators.JacOrbRCServiceInit_7 [com.arjuna.ats.internal.jts.orbspecific.jacorb.recoverycoordinators.JacOrbRCServiceInit_7] - Failed to create orb and poa for transactional objects {1}
  */
@@ -105,19 +107,26 @@
 	    String poaName = POA_NAME_PREFIX + rcServiceName+domainName;
 	    boolean oaInit = true;
 	    String oaPort = "OAPort";
-	    String oldPort = System.getProperty(oaPort, "");
+        String oaAddr = "OAIAddr";
+        String oldPort = System.getProperty(oaPort, "");
+        String oldAddr = System.getProperty(oaAddr, "");
 
-	    /** If the ORB Manager hasn't been initialised then create our own ORB **/
+        /** If the ORB Manager hasn't been initialised then create our own ORB **/
 
 	    if ( !RecoveryORBManager.isInitialised() )
 	    {
 		_orb = com.arjuna.orbportability.internal.InternalORB.getInstance("RecoveryServer");
 		String[] params = null;
 		String recoveryManagerPort = jtsPropertyManager.propertyManager.getProperty(com.arjuna.ats.jts.common.Environment.RECOVERY_MANAGER_PORT, "4711");
+        String recoveryManagerAddr = Utility.getServerBindAddress(jtsPropertyManager.propertyManager, com.arjuna.ats.jts.common.Environment.RECOVERY_MANAGER_ADDRESS);
 
-		if (jtsLogger.loggerI18N.isInfoEnabled())
+        if (recoveryManagerAddr == null)
+            recoveryManagerAddr = "";
+
+        if (jtsLogger.loggerI18N.isInfoEnabled())
 		{
-		    jtsLogger.loggerI18N.info("com.arjuna.ats.internal.jts.orbspecific.jacorb.recoverycoordinators.JacOrbRCServiceInit_6", new java.lang.Object[]{recoveryManagerPort});
+		    jtsLogger.loggerI18N.info("com.arjuna.ats.internal.jts.orbspecific.jacorb.recoverycoordinators.JacOrbRCServiceInit_6",
+                    new java.lang.Object[]{recoveryManagerPort, recoveryManagerAddr});
 		}
 
 		final Properties p = new Properties();
@@ -139,15 +148,22 @@
             }
         }
 		p.setProperty(oaPort, recoveryManagerPort);
-		_orb.initORB(params, p);
+
+        if (recoveryManagerAddr.length() != 0)
+        {
+            p.setProperty(oaAddr, recoveryManagerAddr);
+            System.setProperty(oaAddr, oldAddr);
+        }
+            
+        _orb.initORB(params, p);
 		_oa = OA.getRootOA(_orb);
 
 		if (oldPort == null)
 		    oldPort = "";
-			
-		System.setProperty(oaPort, oldPort);	// Remove property that JacORB added so future ORB's work.
 
-		RecoveryORBManager.setORB(_orb);
+        System.setProperty(oaPort, oldPort);	// Remove property that JacORB added so future ORB's work.
+
+        RecoveryORBManager.setORB(_orb);
 		RecoveryORBManager.setPOA(_oa);
 	    }
 	    else

Modified: labs/jbosstm/trunk/ArjunaJTS/jts/classes/com/arjuna/ats/jts/common/Environment.java
===================================================================
--- labs/jbosstm/trunk/ArjunaJTS/jts/classes/com/arjuna/ats/jts/common/Environment.java	2008-04-04 13:12:45 UTC (rev 19416)
+++ labs/jbosstm/trunk/ArjunaJTS/jts/classes/com/arjuna/ats/jts/common/Environment.java	2008-04-04 13:22:05 UTC (rev 19417)
@@ -56,5 +56,6 @@
     public static final String PROPAGATE_TERMINATOR = "com.arjuna.ats.jts.propagateTerminator";
     public static final String CONTEXT_PROP_MODE = "com.arjuna.ats.jts.contextPropMode";
     public static final String RECOVERY_MANAGER_PORT = "com.arjuna.ats.jts.recoveryManagerPort";
+    public static final String RECOVERY_MANAGER_ADDRESS = "com.arjuna.ats.jts.recoveryManagerAddress";
     public static final String OTS_1_0_TIMEOUT_PROPAGATION = "com.arjuna.ats.jts.ots_1_0.timeoutPropagation";
 }

Modified: labs/jbosstm/trunk/ArjunaJTS/jts/etc/default-RecoveryManager-properties.xml
===================================================================
--- labs/jbosstm/trunk/ArjunaJTS/jts/etc/default-RecoveryManager-properties.xml	2008-04-04 13:12:45 UTC (rev 19416)
+++ labs/jbosstm/trunk/ArjunaJTS/jts/etc/default-RecoveryManager-properties.xml	2008-04-04 13:22:05 UTC (rev 19417)
@@ -116,7 +116,22 @@
       <property
         name="com.arjuna.ats.arjuna.recovery.transactionStatusManagerExpiryTime"
         value="12"/>
+
       <!--
+        The port number on which the recovery manager listens.
+      -->
+      <property
+        name="com.arjuna.ats.arjuna.recovery.recoveryPort"
+        value="4712"/>
+      <!--
+        The address on which the recovery manager listens.
+        If running within an AS then the address the AS is bound to (jboss.bind.address) takes precedence
+      -->
+      <property
+        name="com.arjuna.ats.arjuna.recovery.recoveryAddress"
+        value=""/>
+
+      <!--
         Use this to fix the port on which the TransactionStatusManager listens,
         The default behaviour is to use any free port.
       -->
@@ -124,5 +139,14 @@
         name="com.arjuna.ats.arjuna.recovery.transactionStatusManagerPort"
         value="0"/>
 
+      <!--
+        Use this to fix the address on which the TransactionStatusManager binds,
+        The default behaviour is to use the loopback address (ie localhost).
+        If running within an AS then the address the AS is bound to (jboss.bind.address) takes precedence
+      -->
+      <property
+        name="com.arjuna.ats.arjuna.recovery.transactionStatusManagerAddress"
+        value=""/>
+
     </properties>
 </transaction-service>

Modified: labs/jbosstm/trunk/ArjunaJTS/jts/etc/default-jts-properties.xml
===================================================================
--- labs/jbosstm/trunk/ArjunaJTS/jts/etc/default-jts-properties.xml	2008-04-04 13:12:45 UTC (rev 19416)
+++ labs/jbosstm/trunk/ArjunaJTS/jts/etc/default-jts-properties.xml	2008-04-04 13:22:05 UTC (rev 19417)
@@ -137,5 +137,13 @@
                 <property
                   name="com.arjuna.ats.jts.recoveryManagerPort"
                   value="4711"/>
+
+	<!--
+	  This property controls the address on which the Recovery ORB binds - defaults to the loopback connection
+          If running within an AS then the address the AS is bound to (jboss.bind.address) takes precedence
+	-->
+                <property
+                  name="com.arjuna.ats.jts.recoveryManagerAddress"
+                  value=""/>
     </properties>
 </transaction-service>

Modified: labs/jbosstm/trunk/atsintegration/classes/com/arjuna/ats/jbossatx/jta/TransactionManagerService.java
===================================================================
--- labs/jbosstm/trunk/atsintegration/classes/com/arjuna/ats/jbossatx/jta/TransactionManagerService.java	2008-04-04 13:12:45 UTC (rev 19416)
+++ labs/jbosstm/trunk/atsintegration/classes/com/arjuna/ats/jbossatx/jta/TransactionManagerService.java	2008-04-04 13:22:05 UTC (rev 19417)
@@ -33,6 +33,7 @@
 import org.jboss.mx.util.ObjectNameFactory;
 import org.jboss.system.ServiceMBeanSupport;
 import org.jboss.system.server.Server;
+import org.jboss.system.server.ServerConfig;
 import org.jboss.tm.JBossXATerminator;
 import org.jboss.tm.LastResource;
 import org.jboss.tm.XAExceptionFormatter;
@@ -133,7 +134,8 @@
         System.setProperty(com.arjuna.ats.tsmx.TransactionServiceMX.AGENT_IMPLEMENTATION_PROPERTY,
                 com.arjuna.ats.internal.jbossatx.agent.LocalJBossAgentImpl.class.getName());
         System.setProperty(Environment.LAST_RESOURCE_OPTIMISATION_INTERFACE, LastResource.class.getName()) ;
-
+        System.setProperty(com.arjuna.ats.arjuna.common.Environment.SERVER_BIND_ADDRESS, System.getProperty(ServerConfig.SERVER_BIND_ADDRESS));
+        
         if (timeout != 0)
         {
             TxControl.setDefaultTimeout(timeout);
@@ -219,39 +221,17 @@
     private boolean isRecoveryManagerRunning() throws Exception
     {
         boolean active = false;
-        int port = 0;
         PropertyManager pm = PropertyManagerFactory.getPropertyManager("com.arjuna.ats.propertymanager", "recoverymanager");
 
         if ( pm != null )
         {
-            String portStr = pm.getProperty(com.arjuna.ats.arjuna.common.Environment.RECOVERY_MANAGER_PORT);
-
-            if (portStr != null)
-            {
-                try
-                {
-                    port = Integer.parseInt(portStr);
-                }
-                catch (Exception ex)
-                {
-                    port = -1;
-                }
-            }
-            else
-            {
-                throw new Exception("The transaction status manager port is not set - please refer to the JBossTS documentation");
-            }
-
-
             BufferedReader in = null;
             PrintStream out = null;
 
             try
             {
-                getLog().info("Connecting to recovery manager on port "+port);
+                Socket sckt = RecoveryManager.getClientSocket(getRunInVMRecoveryManager());
 
-                Socket sckt = new Socket(InetAddress.getLocalHost(),port);
-
                 in = new BufferedReader(new InputStreamReader(sckt.getInputStream()));
                 out = new PrintStream(sckt.getOutputStream());
 

Modified: labs/jbosstm/trunk/atsintegration/classes/com/arjuna/ats/jbossatx/jts/TransactionManagerService.java
===================================================================
--- labs/jbosstm/trunk/atsintegration/classes/com/arjuna/ats/jbossatx/jts/TransactionManagerService.java	2008-04-04 13:12:45 UTC (rev 19416)
+++ labs/jbosstm/trunk/atsintegration/classes/com/arjuna/ats/jbossatx/jts/TransactionManagerService.java	2008-04-04 13:22:05 UTC (rev 19417)
@@ -32,6 +32,7 @@
 
 import org.jboss.system.ServiceMBeanSupport;
 import org.jboss.system.server.Server;
+import org.jboss.system.server.ServerConfig;
 import org.jboss.iiop.CorbaORBService;
 import org.jboss.mx.util.ObjectNameFactory;
 import org.jboss.tm.JBossXATerminator;
@@ -69,6 +70,7 @@
 import javax.transaction.UserTransaction;
 import java.net.Socket;
 import java.net.InetAddress;
+import java.net.UnknownHostException;
 import java.io.BufferedReader;
 import java.io.InputStreamReader;
 import java.io.PrintStream;
@@ -137,6 +139,8 @@
                 com.arjuna.ats.internal.jbossatx.agent.LocalJBossAgentImpl.class.getName());
         System.setProperty(Environment.LAST_RESOURCE_OPTIMISATION_INTERFACE, LastResource.class.getName()) ;
 
+        System.setProperty(com.arjuna.ats.arjuna.common.Environment.SERVER_BIND_ADDRESS, System.getProperty(ServerConfig.SERVER_BIND_ADDRESS));
+
         final String alwaysPropagateProperty = alwaysPropagateContext ? "YES" : "NO" ;
         System.setProperty(com.arjuna.ats.jts.common.Environment.ALWAYS_PROPAGATE_CONTEXT, alwaysPropagateProperty);
 
@@ -235,38 +239,17 @@
     private boolean isRecoveryManagerRunning() throws Exception
     {
         boolean active = false;
-        int port = 0;
         PropertyManager pm = PropertyManagerFactory.getPropertyManager("com.arjuna.ats.propertymanager", "recoverymanager");
 
         if ( pm != null )
         {
-            String portStr = pm.getProperty(com.arjuna.ats.arjuna.common.Environment.RECOVERY_MANAGER_PORT);
-
-            if (portStr != null)
-            {
-                try
-                {
-                    port = Integer.parseInt(portStr);
-                }
-                catch (Exception ex)
-                {
-                    port = -1;
-                }
-            }
-            else
-            {
-                throw new Exception("The transaction status manager port is not set - please refer to the JBossTS documentation");
-            }
-
             BufferedReader in = null;
             PrintStream out = null;
 
             try
             {
-                getLog().info("Connecting to recovery manager on port "+port);
+                Socket sckt = RecoveryManager.getClientSocket(getRunInVMRecoveryManager());
 
-                Socket sckt = new Socket(InetAddress.getLocalHost(),port);
-
                 in = new BufferedReader(new InputStreamReader(sckt.getInputStream()));
                 out = new PrintStream(sckt.getOutputStream());
 
@@ -280,7 +263,18 @@
             }
             catch (Exception ex)
             {
-                getLog().error("Failed to connect to recovery manager", ex);
+                try
+                {
+                    InetAddress host = RecoveryManager.getRecoveryManagerHost(getRunInVMRecoveryManager());
+                    int port = RecoveryManager.getRecoveryManagerPort();
+
+                    getLog().error("Failed to connect to recovery manager on " + host.getHostAddress() + ':' + port);
+                }
+                catch (UnknownHostException e)
+                {
+                    getLog().error("Failed to connect to recovery manager", ex);
+                }
+
                 active = false;
             }
             finally




More information about the jboss-svn-commits mailing list