001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.ha.framework.interfaces;
023:
024: import java.io.IOException;
025: import java.io.ObjectInputStream;
026: import java.io.ObjectOutputStream;
027: import java.lang.reflect.Method;
028: import java.util.ArrayList;
029:
030: import org.jboss.invocation.MarshalledInvocation;
031: import org.jboss.logging.Logger;
032:
033: /**
034: *
035: *
036: * @author <a href="mailto:sacha.labourey@cogito-info.ch">Sacha Labourey</a>
037: * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
038: * @version $Revision: 57188 $
039: */
040: public class HARMIClient implements HARMIProxy,
041: java.lang.reflect.InvocationHandler, java.io.Serializable {
042: // Constants -----------------------------------------------------
043: /** The serialVersionUID
044: * @since
045: */
046: private static final long serialVersionUID = -1227816478666532463L;
047: private static final Logger log = Logger
048: .getLogger(HARMIClient.class);
049:
050: /** {@link Object#toString} method reference. */
051: protected static final Method TO_STRING;
052:
053: /** {@link Object#hashCode} method reference. */
054: protected static final Method HASH_CODE;
055:
056: /** {@link Object#equals} method reference. */
057: protected static final Method EQUALS;
058:
059: static {
060: try {
061: final Class[] empty = {};
062: final Class type = Object.class;
063:
064: TO_STRING = type.getMethod("toString", empty);
065: HASH_CODE = type.getMethod("hashCode", empty);
066: EQUALS = type.getMethod("equals", new Class[] { type });
067: } catch (Exception e) {
068: e.printStackTrace();
069: throw new ExceptionInInitializerError(e);
070: }
071: }
072:
073: // Attributes ----------------------------------------------------
074:
075: protected String key = null;
076: //protected ArrayList targets = null;
077: protected LoadBalancePolicy loadBalancePolicy;
078: //protected transient long currentViewId = 0;
079: protected transient Object local = null;
080: protected transient boolean trace;
081: FamilyClusterInfo familyClusterInfo = null;
082:
083: // Static --------------------------------------------------------
084:
085: // Constructors --------------------------------------------------
086:
087: public HARMIClient() {
088: }
089:
090: public HARMIClient(ArrayList targets, LoadBalancePolicy policy,
091: String key) {
092: this (targets, 0, policy, key, null);
093: }
094:
095: public HARMIClient(ArrayList targets, long initViewId,
096: LoadBalancePolicy policy, String key, Object local) {
097: this .familyClusterInfo = ClusteringTargetsRepository
098: .initTarget(key, targets, initViewId);
099:
100: //this.targets = targets;
101: this .loadBalancePolicy = policy;
102: this .loadBalancePolicy.init(this );
103: this .key = key;
104: this .local = local;
105: this .trace = log.isTraceEnabled();
106: if (trace)
107: log.trace("Init, cluterInfo: " + familyClusterInfo
108: + ", policy=" + loadBalancePolicy);
109: }
110:
111: // Public --------------------------------------------------------
112: /*
113: public ArrayList getTargets()
114: {
115: return targets;
116: }
117:
118: public void setTargets(ArrayList newTargets)
119: {
120: synchronized(targets)
121: {
122: targets.clear();
123: targets.addAll(newTargets);
124: }
125: }
126: */
127: public void updateClusterInfo(ArrayList targets, long viewId) {
128: if (familyClusterInfo != null)
129: this .familyClusterInfo.updateClusterInfo(targets, viewId);
130: }
131:
132: public Object getRemoteTarget() {
133: // System.out.println("number of targets: " + targets.size());
134: return loadBalancePolicy.chooseTarget(this .familyClusterInfo,
135: null); // legacy, no Invocation object in raw HA-RMI
136: }
137:
138: public void remoteTargetHasFailed(Object target) {
139: removeDeadTarget(target);
140: }
141:
142: public Method findLocalMethod(Method method, Object[] args)
143: throws Exception {
144: return method;
145: }
146:
147: public Object invokeRemote(Object proxy, Method method,
148: Object[] args) throws Throwable {
149: boolean trace = log.isTraceEnabled();
150: HARMIServer target = (HARMIServer) getRemoteTarget();
151: while (target != null) {
152: Exception lastException = null;
153: try {
154: if (trace)
155: log.trace("Invoking on target=" + target);
156: MarshalledInvocation mi = new MarshalledInvocation(
157: null, method, args, null, null, null);
158: mi.setObjectName(""); //FIXME: Fake value! Bill's optimisations regarding MI make the hypothesis
159: // that ObjectName is always here otherwise the writeExternal code of MI
160: // "out.writeInt(payload.size() - 3);" is wrong
161: HARMIResponse rsp = target.invoke(
162: this .familyClusterInfo.getCurrentViewId(), mi);
163: if (rsp.newReplicants != null) {
164: if (trace) {
165: log
166: .trace("newReplicants: "
167: + rsp.newReplicants);
168: }
169: updateClusterInfo(rsp.newReplicants,
170: rsp.currentViewId);
171: //setTargets(rsp.newReplicants);
172: //currentViewId = rsp.currentViewId;
173: }
174:
175: return rsp.response;
176: } catch (java.rmi.ConnectException e) {
177: lastException = e;
178: } catch (java.rmi.ConnectIOException e) {
179: lastException = e;
180: } catch (java.rmi.NoSuchObjectException e) {
181: lastException = e;
182: } catch (java.rmi.UnmarshalException e) {
183: lastException = e;
184: } catch (java.rmi.UnknownHostException e) {
185: lastException = e;
186: }
187: if (trace)
188: log.trace("Invoke failed, target=" + target,
189: lastException);
190: // If we reach here, this means that we must fail-over
191: remoteTargetHasFailed(target);
192: target = (HARMIServer) getRemoteTarget();
193: }
194: // if we get here this means list was exhausted
195: throw new java.rmi.RemoteException("Service unavailable.");
196:
197: }
198:
199: // HARMIProxy implementation ----------------------------------------------
200:
201: public boolean isLocal() {
202: return local != null;
203: }
204:
205: // InvocationHandler implementation ----------------------------------------------
206:
207: public Object invoke(Object proxy, Method method, Object[] args)
208: throws Throwable {
209: // The isLocal call is handled by the proxy
210: String name = method.getName();
211: if (method.equals(TO_STRING)) {
212: StringBuffer tmp = new StringBuffer(super .toString());
213: tmp.append('(');
214: tmp.append(familyClusterInfo);
215: tmp.append(')');
216: return tmp.toString();
217: } else if (name.equals("equals")) {
218: return method.invoke(this , args);
219: } else if (name.equals("hashCode")) {
220: return method.invoke(this , args);
221: } else if (name.equals("isLocal")
222: && (args == null || args.length == 0)) {
223: return method.invoke(this , args);
224: }
225:
226: // we try to optimize the call locally first
227: //
228: if (local != null) {
229: try {
230: Method localMethod = findLocalMethod(method, args);
231: return localMethod.invoke(local, args);
232: } catch (java.lang.reflect.InvocationTargetException ite) {
233: throw ite.getTargetException();
234: }
235: } else {
236: return invokeRemote(null, method, args);
237: }
238: }
239:
240: // Package protected ---------------------------------------------
241:
242: // Protected -----------------------------------------------------
243:
244: protected void removeDeadTarget(Object target) {
245: //System.out.println("Size before : " + Integer.toString(targets.length));
246: if (this .familyClusterInfo != null)
247: this .familyClusterInfo.removeDeadTarget(target);
248: }
249:
250: // Private -------------------------------------------------------
251:
252: private void readObject(ObjectInputStream stream)
253: throws IOException, ClassNotFoundException {
254: this .key = stream.readUTF();
255: ArrayList targets = (ArrayList) stream.readObject();
256: long vid = stream.readLong();
257: this .loadBalancePolicy = (LoadBalancePolicy) stream
258: .readObject();
259: HARMIServer server = (HARMIServer) HARMIServer.rmiServers
260: .get(key);
261:
262: // keep a reference on our family object
263: //
264: this .familyClusterInfo = ClusteringTargetsRepository
265: .initTarget(this .key, targets, vid);
266:
267: this .loadBalancePolicy.init(this );
268:
269: if (server != null) {
270: synchronized (targets) {
271: try {
272: targets = (ArrayList) server.getReplicants();
273: local = server.getLocal();
274: } catch (Exception ignored) {
275: }
276: }
277: }
278: this .trace = log.isTraceEnabled();
279: if (trace)
280: log.trace("Init, clusterInfo: " + familyClusterInfo
281: + ", policy=" + loadBalancePolicy);
282: }
283:
284: private void writeObject(ObjectOutputStream stream)
285: throws IOException {
286: // JBAS-2071 - sync on FCI to ensure targets and vid are consistent
287: ArrayList currentTargets = null;
288: long vid = 0;
289: synchronized (this.familyClusterInfo) {
290: currentTargets = this.familyClusterInfo.getTargets();
291: vid = this.familyClusterInfo.getCurrentViewId();
292: }
293: stream.writeUTF(key);
294: stream.writeObject(currentTargets);
295: stream.writeLong(vid);
296: stream.writeObject(loadBalancePolicy);
297:
298: }
299:
300: }
|