FileDocCategorySizeDatePackage
JRMPInvokerProxyHA.javaAPI DocJBoss 4.2.112271Fri Jul 13 20:52:40 BST 2007org.jboss.invocation.jrmp.interfaces

JRMPInvokerProxyHA

public class JRMPInvokerProxyHA extends JRMPInvokerProxy implements org.jboss.invocation.InvokerProxyHA, Externalizable
An extension of the JRMPInvokerProxy that supports failover and load balancing among a
author
Marc Fleury
author
Scott.Stark@jboss.org
version
$Revision: 57188 $

Fields Summary
private static final long
serialVersionUID
The serialVersionUID
private static final Logger
log
public static final WeakHashMap
txFailoverAuthorizations
protected org.jboss.ha.framework.interfaces.LoadBalancePolicy
loadBalancePolicy
protected String
proxyFamilyName
org.jboss.ha.framework.interfaces.FamilyClusterInfo
familyClusterInfo
protected transient boolean
trace
Trace level logging flag only set when the proxy is created or read from JNDI
Constructors Summary
public JRMPInvokerProxyHA()


     
public JRMPInvokerProxyHA(ArrayList targets, org.jboss.ha.framework.interfaces.LoadBalancePolicy policy, String proxyFamilyName, long viewId)

      this.familyClusterInfo = ClusteringTargetsRepository.initTarget (proxyFamilyName, targets, viewId);
      this.loadBalancePolicy = policy;
      this.proxyFamilyName = proxyFamilyName;
      this.trace = log.isTraceEnabled();
      if( trace )
         log.trace("Init, cluterInfo: "+familyClusterInfo+", policy="+loadBalancePolicy);
   
Methods Summary
public java.lang.ObjectgetRemoteTarget()

      return getRemoteTarget(null);
   
public java.lang.ObjectgetRemoteTarget(org.jboss.invocation.Invocation invocationBasedRouting)

      return loadBalancePolicy.chooseTarget(this.familyClusterInfo, invocationBasedRouting);
   
public voidinvocationHasReachedAServer(org.jboss.invocation.Invocation invocation)

      javax.transaction.Transaction tx = invocation.getTransaction();
      if (tx != null)
      {
         synchronized (tx)
         {
            txFailoverAuthorizations.put (tx, null);               
         }
      }
   
public java.lang.Objectinvoke(org.jboss.invocation.Invocation invocation)
The invocation on the delegate, calls the right invoker. Remote if we are remote, local if we are local.

      // we give the opportunity, to any server interceptor, to know if this a
      // first invocation to a node or if it is a failovered call
      //
      int failoverCounter = 0;
      invocation.setValue ("FAILOVER_COUNTER", new Integer(failoverCounter), PayloadKey.AS_IS);

      // We are going to go through a Remote invocation, switch to a Marshalled Invocation
      MarshalledInvocation mi = new MarshalledInvocation(invocation);

      // Set the transaction propagation context
      mi.setTransactionPropagationContext(getTransactionPropagationContext());
      mi.setValue("CLUSTER_VIEW_ID", new Long(this.familyClusterInfo.getCurrentViewId ()));
      Invoker target = (Invoker)getRemoteTarget(invocation);
      
      boolean failoverAuthorized = true;
         Exception lastException = null;
      while (target != null && failoverAuthorized)
      {                        
         boolean definitivlyRemoveNodeOnFailure = true;
         try
         {
            if( trace )
               log.trace("Invoking on target="+target);
            Object rtnObj = target.invoke(mi);
            HARMIResponse rsp = null;
            if (rtnObj instanceof MarshalledObject)
            {
               rsp = (HARMIResponse)((MarshalledObject)rtnObj).get();
            }
            else
            {
               rsp = (HARMIResponse)rtnObj;
            }
            if (rsp.newReplicants != null)
            {
               if( trace )
               {
                  log.trace("newReplicants: "+rsp.newReplicants);
               }
               updateClusterInfo (rsp.newReplicants, rsp.currentViewId);
            }
            //else System.out.println("Static set of replicants: " + this.familyClusterInfo.getCurrentViewId () + " (me = " + this + ")");
            
            invocationHasReachedAServer (invocation);

            return rsp.response;
         }
         catch (java.net.ConnectException e)
         {
            lastException = e;
         }
         catch (java.net.UnknownHostException e)
         {
            lastException = e;
         }
         catch (java.rmi.ConnectException e)
         {
            lastException = e;
         }
         catch (java.rmi.ConnectIOException e)
         {
            lastException = e;
         }
         catch (java.rmi.NoSuchObjectException e)
         {
            lastException = e;
         }
         catch (java.rmi.UnknownHostException e)
         {
            lastException = e;
         }
         catch (GenericClusteringException e)
         {
            lastException = e;
            // this is a generic clustering exception that contain the
            // completion status: usefull to determine if we are authorized
            // to re-issue a query to another node
            //
            if (e.getCompletionStatus () == GenericClusteringException.COMPLETED_NO)
            {
               // we don't want to remove the node from the list of failed
               // node UNLESS there is a risk to indefinitively loop
               //
               if (totalNumberOfTargets() >= failoverCounter)
               {
                  if (!e.isDefinitive ())
                     definitivlyRemoveNodeOnFailure = false;
               }
            }
            else
            {
               invocationHasReachedAServer (invocation);
               throw new ServerException("Clustering error", e);
            }
         }
         catch (ServerException e)
         {
            //Why do NoSuchObjectExceptions get ignored for a retry here
            //unlike in the non-HA case?
            invocationHasReachedAServer (invocation);
            if (e.detail instanceof TransactionRolledbackException)
            {                  
               throw (TransactionRolledbackException) e.detail;
            }
            if (e.detail instanceof RemoteException)
            {
               throw (RemoteException) e.detail;
            }
            throw e;
         }
         catch (Exception e)
         {
            lastException = e;
            invocationHasReachedAServer (invocation);
            throw e;
         }

         if( trace )
            log.trace("Invoke failed, target="+target, lastException);

         // If we reach here, this means that we must fail-over
         remoteTargetHasFailed(target);
         if (!definitivlyRemoveNodeOnFailure)
         {
            resetView ();
         }

         failoverAuthorized = txContextAllowsFailover (invocation);            
         target = (Invoker)getRemoteTarget(invocation);

         failoverCounter++;
         mi.setValue ("FAILOVER_COUNTER", new Integer(failoverCounter), PayloadKey.AS_IS);
      }
      // if we get here this means list was exhausted
      String msg = "Service unavailable.";
      if (failoverAuthorized == false)
      {
         msg = "Service unavailable (failover not possible inside a user transaction).";
      }
      throw new ServiceUnavailableException(msg, lastException);
   
public voidreadExternal(java.io.ObjectInput in)
Un-externalize this instance. We check timestamps of the interfaces to see if the instance is in the original VM of creation

      ArrayList targets = (ArrayList)in.readObject();
      this.loadBalancePolicy = (LoadBalancePolicy)in.readObject();
      this.proxyFamilyName = (String)in.readObject();
      long vid = in.readLong ();

      // keep a reference on our family object
      //
      this.familyClusterInfo = ClusteringTargetsRepository.initTarget (this.proxyFamilyName, targets, vid);
      this.trace = log.isTraceEnabled();
      if( trace )
         log.trace("Init, clusterInfo: "+familyClusterInfo+", policy="+loadBalancePolicy);
   
public voidremoteTargetHasFailed(java.lang.Object target)

      removeDeadTarget(target);
   
protected voidremoveDeadTarget(java.lang.Object target)

      //System.out.println("Removing a dead target: Size before : " + Integer.toString(this.familyClusterInfo.getTargets ().size()));
      if (this.familyClusterInfo != null)
         this.familyClusterInfo.removeDeadTarget (target);
   
protected voidresetView()

      this.familyClusterInfo.resetView ();
   
protected inttotalNumberOfTargets()

      if (this.familyClusterInfo != null)
         return this.familyClusterInfo.getTargets ().size ();
      else
         return 0;
   
public booleantxContextAllowsFailover(org.jboss.invocation.Invocation invocation)

      javax.transaction.Transaction tx = invocation.getTransaction();
      if (tx != null)
      {
         synchronized (tx)
         {
            return ! txFailoverAuthorizations.containsKey (tx);               
         }
      }
      else
      {
         return true;
      }
   
public voidupdateClusterInfo(java.util.ArrayList targets, long viewId)

      if (familyClusterInfo != null)
         this.familyClusterInfo.updateClusterInfo (targets, viewId);
   
public voidwriteExternal(java.io.ObjectOutput out)
Externalize this instance. If this instance lives in a different VM than its container invoker, the remote interface of the container invoker is not externalized.

 
      // JBAS-2071 - sync on FCI to ensure targets and vid are consistent
      ArrayList targets = null;
      long vid = 0;
      synchronized (this.familyClusterInfo)
      {
         targets = this.familyClusterInfo.getTargets ();
         vid = this.familyClusterInfo.getCurrentViewId ();
      }
      out.writeObject(targets);
      out.writeObject(this.loadBalancePolicy);
      out.writeObject (this.proxyFamilyName);
      out.writeLong (vid);