package org.jboss.invocation.http.interfaces;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.rmi.ServerException;
import java.util.ArrayList;
import org.jboss.ha.framework.interfaces.ClusteringTargetsRepository;
import org.jboss.ha.framework.interfaces.GenericClusteringException;
import org.jboss.ha.framework.interfaces.HARMIResponse;
import org.jboss.ha.framework.interfaces.LoadBalancePolicy;
import org.jboss.ha.framework.interfaces.FamilyClusterInfo;
import org.jboss.invocation.Invocation;
import org.jboss.invocation.InvocationException;
import org.jboss.invocation.Invoker;
import org.jboss.invocation.InvokerProxyHA;
import org.jboss.invocation.MarshalledInvocation;
import org.jboss.invocation.PayloadKey;
import org.jboss.logging.Logger;
public class HttpInvokerProxyHA
implements InvokerProxyHA, Invoker, Externalizable
{
private static Logger log = Logger.getLogger(HttpInvokerProxyHA.class);
private static final long serialVersionUID = -7081220026780794383L;
protected LoadBalancePolicy loadBalancePolicy;
protected String proxyFamilyName = null;
protected FamilyClusterInfo familyClusterInfo = null;
protected transient boolean trace = false;
public HttpInvokerProxyHA()
{
}
public HttpInvokerProxyHA(ArrayList targets, long viewId, LoadBalancePolicy policy,
String proxyFamilyName)
{
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);
}
public void updateClusterInfo (ArrayList targets, long viewId)
{
if (familyClusterInfo != null)
this.familyClusterInfo.updateClusterInfo (targets, viewId);
}
public String getServerHostName() throws Exception
{
return null;
}
public FamilyClusterInfo getClusterInfo()
{
return familyClusterInfo;
}
public Object getRemoteTarget()
{
return getRemoteTarget(null);
}
public Object getRemoteTarget(Invocation invocationBasedRouting)
{
Object target = loadBalancePolicy.chooseTarget(this.familyClusterInfo, invocationBasedRouting);
if( trace )
log.trace("Choose remoteTarget: "+target);
return target;
}
public void remoteTargetHasFailed(Object target)
{
removeDeadTarget(target);
}
protected int totalNumberOfTargets ()
{
int size = 0;
if( familyClusterInfo != null )
size = familyClusterInfo.getTargets().size();
return size;
}
protected void removeDeadTarget(Object target)
{
if( familyClusterInfo != null )
{
ArrayList targets = familyClusterInfo.removeDeadTarget(target);
if( trace )
{
log.trace("removeDeadTarget("+target+"), targets.size="+targets.size());
}
}
}
protected void resetView ()
{
familyClusterInfo.resetView();
}
public Object invoke(Invocation invocation)
throws Exception
{
int failoverCounter = 0;
MarshalledInvocation mi = new MarshalledInvocation(invocation);
mi.setValue("CLUSTER_VIEW_ID", new Long(familyClusterInfo.getCurrentViewId()));
String target = (String) getRemoteTarget(invocation);
URL externalURL = Util.resolveURL(target);
Exception lastException = null;
while( externalURL != null )
{
boolean definitivlyRemoveNodeOnFailure = true;
invocation.setValue("FAILOVER_COUNTER", new Integer(failoverCounter), PayloadKey.AS_IS);
try
{
if( trace )
log.trace("Invoking on target="+externalURL);
Object rtn = Util.invoke(externalURL, mi);
HARMIResponse rsp = (HARMIResponse) rtn;
if (rsp.newReplicants != null)
updateClusterInfo(rsp.newReplicants, rsp.currentViewId);
return rsp.response;
}
catch(GenericClusteringException e)
{
if( e.getCompletionStatus() != e.COMPLETED_NO )
{
if (totalNumberOfTargets() >= failoverCounter)
{
if( e.isDefinitive() == false )
definitivlyRemoveNodeOnFailure = false;
}
}
else
{
throw new ServerException("Cannot proceed beyond target="+externalURL, e);
}
}
catch(InvocationException e)
{
Throwable cause = e.getTargetException();
if( cause instanceof Exception )
throw (Exception) cause;
else if (cause instanceof Error)
throw (Error) cause;
throw new InvocationTargetException(cause);
}
catch(IOException e)
{
if( trace )
log.trace("Invoke failed, target="+externalURL, e);
lastException = e;
}
catch(Exception e)
{
throw e;
}
remoteTargetHasFailed(target);
if( definitivlyRemoveNodeOnFailure )
resetView();
target = (String) getRemoteTarget(invocation);
externalURL = Util.resolveURL(target);
failoverCounter ++;
}
throw new ServerException("Service unavailable last exception:", lastException);
}
public void writeExternal(final ObjectOutput out)
throws IOException
{
out.writeObject(this.familyClusterInfo.getTargets());
out.writeLong(this.familyClusterInfo.getCurrentViewId ());
out.writeObject(this.loadBalancePolicy);
out.writeObject(this.proxyFamilyName);
}
public void readExternal(final ObjectInput in)
throws IOException, ClassNotFoundException
{
ArrayList targets = (ArrayList) in.readObject();
long vid = in.readLong ();
this.loadBalancePolicy = (LoadBalancePolicy) in.readObject();
this.proxyFamilyName = (String)in.readObject();
this.trace = log.isTraceEnabled();
this.familyClusterInfo = ClusteringTargetsRepository.initTarget(this.proxyFamilyName, targets, vid);
if( trace )
log.trace("Init, clusterInfo: "+familyClusterInfo+", policy="+loadBalancePolicy);
}
}