KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > invocation > jrmp > interfaces > JRMPInvokerProxyHA


1 /*
2   * JBoss, Home of Professional Open Source
3   * Copyright 2005, JBoss Inc., and individual contributors as indicated
4   * by the @authors tag. See the copyright.txt in the distribution for a
5   * full listing of individual contributors.
6   *
7   * This is free software; you can redistribute it and/or modify it
8   * under the terms of the GNU Lesser General Public License as
9   * published by the Free Software Foundation; either version 2.1 of
10   * the License, or (at your option) any later version.
11   *
12   * This software is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15   * Lesser General Public License for more details.
16   *
17   * You should have received a copy of the GNU Lesser General Public
18   * License along with this software; if not, write to the Free
19   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21   */

22 package org.jboss.invocation.jrmp.interfaces;
23
24 import java.io.Externalizable JavaDoc;
25 import java.io.IOException JavaDoc;
26 import java.io.ObjectInput JavaDoc;
27 import java.io.ObjectOutput JavaDoc;
28 import java.rmi.MarshalledObject JavaDoc;
29 import java.rmi.RemoteException JavaDoc;
30 import java.rmi.ServerException JavaDoc;
31 import java.util.ArrayList JavaDoc;
32 import java.util.HashSet JavaDoc;
33 import java.util.List JavaDoc;
34 import java.util.WeakHashMap JavaDoc;
35 import javax.transaction.TransactionRolledbackException JavaDoc;
36
37 import org.jboss.ha.framework.interfaces.ClusteringTargetsRepository;
38 import org.jboss.ha.framework.interfaces.FamilyClusterInfo;
39 import org.jboss.ha.framework.interfaces.GenericClusteringException;
40 import org.jboss.ha.framework.interfaces.HARMIResponse;
41 import org.jboss.ha.framework.interfaces.LoadBalancePolicy;
42 import org.jboss.invocation.Invocation;
43 import org.jboss.invocation.Invoker;
44 import org.jboss.invocation.InvokerInterceptor;
45 import org.jboss.invocation.InvokerProxyHA;
46 import org.jboss.invocation.MarshalledInvocation;
47 import org.jboss.invocation.PayloadKey;
48 import org.jboss.invocation.ServiceUnavailableException;
49 import org.jboss.logging.Logger;
50
51 /**
52  * An extension of the JRMPInvokerProxy that supports failover and load
53  * balancing among a
54  *
55  * @author <a HREF="mailto:marc.fleury@jboss.org">Marc Fleury</a>
56  * @author Scott.Stark@jboss.org
57  * @version $Revision: 56228 $
58  */

59 public class JRMPInvokerProxyHA
60    extends JRMPInvokerProxy
61    implements InvokerProxyHA, Externalizable JavaDoc
62 {
63    // Public --------------------------------------------------------
64
/** The serialVersionUID
65     * @since 1.7.2.8
66     */

67    private static final long serialVersionUID = -967671822225981666L;
68    private static final Logger log = Logger.getLogger(JRMPInvokerProxyHA.class);
69    public static final HashSet JavaDoc colocation = new HashSet JavaDoc();
70    public static final WeakHashMap JavaDoc txFailoverAuthorizations = new WeakHashMap JavaDoc();
71
72    protected LoadBalancePolicy loadBalancePolicy;
73    protected String JavaDoc proxyFamilyName = null;
74
75    FamilyClusterInfo familyClusterInfo = null;
76    //protected transient long currentViewId = 0;
77
/** Trace level logging flag only set when the proxy is created or read from JNDI */
78    protected transient boolean trace = false;
79
80    public JRMPInvokerProxyHA() {}
81
82    public JRMPInvokerProxyHA(List JavaDoc targets, LoadBalancePolicy policy,
83                              String JavaDoc proxyFamilyName, long viewId)
84    {
85       this.familyClusterInfo = ClusteringTargetsRepository.initTarget (proxyFamilyName, targets, viewId);
86       this.loadBalancePolicy = policy;
87       this.proxyFamilyName = proxyFamilyName;
88       this.trace = log.isTraceEnabled();
89       if( trace )
90          log.trace("Init, cluterInfo: "+familyClusterInfo+", policy="+loadBalancePolicy);
91    }
92
93    public void updateClusterInfo (ArrayList JavaDoc targets, long viewId)
94    {
95       if (familyClusterInfo != null)
96          this.familyClusterInfo.updateClusterInfo (targets, viewId);
97    }
98
99    public Object JavaDoc getRemoteTarget()
100    {
101       return getRemoteTarget(null);
102    }
103    public Object JavaDoc getRemoteTarget(Invocation invocationBasedRouting)
104    {
105       return loadBalancePolicy.chooseTarget(this.familyClusterInfo, invocationBasedRouting);
106    }
107
108    public void remoteTargetHasFailed(Object JavaDoc target)
109    {
110       removeDeadTarget(target);
111    }
112
113    protected void removeDeadTarget(Object JavaDoc target)
114    {
115       //System.out.println("Removing a dead target: Size before : " + Integer.toString(this.familyClusterInfo.getTargets ().size()));
116
if (this.familyClusterInfo != null)
117          this.familyClusterInfo.removeDeadTarget (target);
118    }
119
120    protected int totalNumberOfTargets ()
121    {
122       if (this.familyClusterInfo != null)
123          return this.familyClusterInfo.getTargets ().size ();
124       else
125          return 0;
126    }
127
128    protected void resetView ()
129    {
130       this.familyClusterInfo.resetView ();
131    }
132
133    /**
134    * Returns wether we are local to the originating container or not.
135    */

136    public boolean isLocal(Invocation invocation)
137    {
138       return colocation.contains(invocation.getObjectName());
139    }
140    
141    public boolean txContextAllowsFailover (Invocation invocation)
142    {
143       javax.transaction.Transaction JavaDoc tx = invocation.getTransaction();
144       if (tx != null)
145       {
146          synchronized (tx)
147          {
148             return ! txFailoverAuthorizations.containsKey (tx);
149          }
150       }
151       else
152       {
153          return true;
154       }
155    }
156    
157    public void invocationHasReachedAServer (Invocation invocation)
158    {
159       javax.transaction.Transaction JavaDoc tx = invocation.getTransaction();
160       if (tx != null)
161       {
162          synchronized (tx)
163          {
164             txFailoverAuthorizations.put (tx, null);
165          }
166       }
167    }
168
169    /**
170    * The invocation on the delegate, calls the right invoker. Remote if we are remote, local if we
171    * are local.
172    */

173    public Object JavaDoc invoke(Invocation invocation)
174       throws Exception JavaDoc
175    {
176       // we give the opportunity, to any server interceptor, to know if this a
177
// first invocation to a node or if it is a failovered call
178
//
179
int failoverCounter = 0;
180       invocation.setValue ("FAILOVER_COUNTER", new Integer JavaDoc(failoverCounter), PayloadKey.AS_IS);
181
182       // optimize if calling another bean in same EJB-application
183
if (isLocal(invocation))
184       {
185          return InvokerInterceptor.getLocal().invoke(invocation);
186       }
187       else
188       {
189          // We are going to go through a Remote invocation, switch to a Marshalled Invocation
190
MarshalledInvocation mi = new MarshalledInvocation(invocation);
191
192          // Set the transaction propagation context
193
mi.setTransactionPropagationContext(getTransactionPropagationContext());
194          mi.setValue("CLUSTER_VIEW_ID", new Long JavaDoc(this.familyClusterInfo.getCurrentViewId ()));
195          Invoker target = (Invoker)getRemoteTarget(invocation);
196          
197          boolean failoverAuthorized = true;
198          Exception JavaDoc lastException = null;
199          while (target != null && failoverAuthorized)
200          {
201             boolean definitivlyRemoveNodeOnFailure = true;
202             try
203             {
204                if( trace )
205                   log.trace("Invoking on target="+target);
206                Object JavaDoc rtnObj = target.invoke(mi);
207                HARMIResponse rsp = null;
208                if (rtnObj instanceof MarshalledObject JavaDoc)
209                {
210                   rsp = (HARMIResponse)((MarshalledObject JavaDoc)rtnObj).get();
211                }
212                else
213                {
214                   rsp = (HARMIResponse)rtnObj;
215                }
216                if (rsp.newReplicants != null)
217                {
218                   if( trace )
219                   {
220                      log.trace("newReplicants: "+rsp.newReplicants);
221                   }
222                   updateClusterInfo (rsp.newReplicants, rsp.currentViewId);
223                }
224                //else System.out.println("Static set of replicants: " + this.familyClusterInfo.getCurrentViewId () + " (me = " + this + ")");
225

226                invocationHasReachedAServer (invocation);
227
228                return rsp.response;
229             }
230             catch (java.net.ConnectException JavaDoc e)
231             {
232                lastException = e;
233             }
234             catch (java.net.UnknownHostException JavaDoc e)
235             {
236                lastException = e;
237             }
238             catch (java.rmi.ConnectException JavaDoc e)
239             {
240                lastException = e;
241             }
242             catch (java.rmi.ConnectIOException JavaDoc e)
243             {
244                lastException = e;
245             }
246             catch (java.rmi.NoSuchObjectException JavaDoc e)
247             {
248                lastException = e;
249             }
250             catch (java.rmi.UnknownHostException JavaDoc e)
251             {
252                lastException = e;
253             }
254             catch (GenericClusteringException e)
255             {
256                lastException = e;
257                // this is a generic clustering exception that contain the
258
// completion status: usefull to determine if we are authorized
259
// to re-issue a query to another node
260
//
261
if (e.getCompletionStatus () == GenericClusteringException.COMPLETED_NO)
262                {
263                   // we don't want to remove the node from the list of failed
264
// node UNLESS there is a risk to indefinitively loop
265
//
266
if (totalNumberOfTargets() >= failoverCounter)
267                   {
268                      if (!e.isDefinitive ())
269                         definitivlyRemoveNodeOnFailure = false;
270                   }
271                }
272                else
273                {
274                   invocationHasReachedAServer (invocation);
275                   throw new ServerException JavaDoc("Clustering error", e);
276                }
277             }
278             catch (ServerException JavaDoc e)
279             {
280                //Why do NoSuchObjectExceptions get ignored for a retry here
281
//unlike in the non-HA case?
282
invocationHasReachedAServer (invocation);
283                if (e.detail instanceof TransactionRolledbackException JavaDoc)
284                {
285                   throw (TransactionRolledbackException JavaDoc) e.detail;
286                }
287                if (e.detail instanceof RemoteException JavaDoc)
288                {
289                   throw (RemoteException JavaDoc) e.detail;
290                }
291                throw e;
292             }
293             catch (Exception JavaDoc e)
294             {
295                lastException = e;
296                invocationHasReachedAServer (invocation);
297                throw e;
298             }
299
300             if( trace )
301                log.trace("Invoke failed, target="+target, lastException);
302
303             // If we reach here, this means that we must fail-over
304
remoteTargetHasFailed(target);
305             if (!definitivlyRemoveNodeOnFailure)
306             {
307                resetView ();
308             }
309
310             failoverAuthorized = txContextAllowsFailover (invocation);
311             target = (Invoker)getRemoteTarget(invocation);
312
313             failoverCounter++;
314             mi.setValue ("FAILOVER_COUNTER", new Integer JavaDoc(failoverCounter), PayloadKey.AS_IS);
315          }
316          // if we get here this means list was exhausted
317
String JavaDoc msg = "Service unavailable.";
318          if (failoverAuthorized == false)
319          {
320             msg = "Service unavailable (failover not possible inside a user transaction).";
321          }
322          throw new ServiceUnavailableException(msg, lastException);
323       }
324    }
325
326    /**
327    * Externalize this instance.
328    *
329    * If this instance lives in a different VM than its container
330    * invoker, the remote interface of the container invoker is
331    * not externalized.
332    */

333    public void writeExternal(final ObjectOutput JavaDoc out)
334       throws IOException JavaDoc
335    {
336       // JBAS-2071 - sync on FCI to ensure targets and vid are consistent
337
List JavaDoc targets = null;
338       long vid = 0;
339       synchronized (this.familyClusterInfo)
340       {
341          targets = this.familyClusterInfo.getTargets ();
342          vid = this.familyClusterInfo.getCurrentViewId ();
343       }
344       out.writeObject(targets);
345       out.writeObject(this.loadBalancePolicy);
346       out.writeObject (this.proxyFamilyName);
347       out.writeLong (vid);
348    }
349
350    /**
351    * Un-externalize this instance.
352    *
353    * We check timestamps of the interfaces to see if the instance is in the original VM of creation
354    */

355    public void readExternal(final ObjectInput JavaDoc in)
356    throws IOException JavaDoc, ClassNotFoundException JavaDoc
357    {
358       List JavaDoc targets = (List JavaDoc)in.readObject();
359       this.loadBalancePolicy = (LoadBalancePolicy)in.readObject();
360       this.proxyFamilyName = (String JavaDoc)in.readObject();
361       long vid = in.readLong ();
362
363       // keep a reference on our family object
364
//
365
this.familyClusterInfo = ClusteringTargetsRepository.initTarget (this.proxyFamilyName, targets, vid);
366       this.trace = log.isTraceEnabled();
367       if( trace )
368          log.trace("Init, clusterInfo: "+familyClusterInfo+", policy="+loadBalancePolicy);
369    }
370
371    // Private -------------------------------------------------------
372

373    // Inner classes -------------------------------------------------
374
}
375
Popular Tags