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.server;
023:
024: import java.lang.ref.SoftReference;
025: import java.lang.reflect.Method;
026: import java.lang.reflect.Proxy;
027: import java.net.InetAddress;
028: import java.rmi.server.RMIClientSocketFactory;
029: import java.rmi.server.RMIServerSocketFactory;
030: import java.rmi.server.RemoteStub;
031: import java.rmi.server.UnicastRemoteObject;
032: import java.util.ArrayList;
033: import java.util.HashMap;
034: import java.util.List;
035: import java.util.Map;
036:
037: import org.jboss.ha.framework.interfaces.HAPartition;
038: import org.jboss.ha.framework.interfaces.HARMIClient;
039: import org.jboss.ha.framework.interfaces.HARMIProxy;
040: import org.jboss.ha.framework.interfaces.HARMIResponse;
041: import org.jboss.ha.framework.interfaces.HARMIServer;
042: import org.jboss.ha.framework.interfaces.LoadBalancePolicy;
043: import org.jboss.invocation.MarshalledInvocation;
044: import org.jboss.logging.Logger;
045: import org.jboss.net.sockets.DefaultSocketFactory;
046:
047: /**
048: * This class is a <em>server-side</em> proxy for replicated RMI objects.
049: *
050: * @author bill@jboss.org
051: * @author sacha.labourey@jboss.org
052: * @author Scott.Stark@jboss.org
053: * @version $Revision: 57188 $
054: */
055: public class HARMIServerImpl implements HARMIServer {
056: protected Object handler;
057: protected Map invokerMap = new HashMap();
058: protected org.jboss.logging.Logger log;
059: protected RemoteStub rmistub;
060: protected Object stub;
061: protected String key;
062: protected Class intf;
063: protected RefreshProxiesHATarget target;
064:
065: public HARMIServerImpl(HAPartition partition, String replicantName,
066: Class intf, Object handler, int port,
067: RMIClientSocketFactory csf, RMIServerSocketFactory ssf)
068: throws Exception {
069: this (partition, replicantName, intf, handler, port, csf, ssf,
070: null);
071:
072: }
073:
074: public HARMIServerImpl(HAPartition partition, String replicantName,
075: Class intf, Object handler, int port,
076: RMIClientSocketFactory clientSocketFactory,
077: RMIServerSocketFactory serverSocketFactory,
078: InetAddress bindAddress) throws Exception {
079: this .handler = handler;
080: this .log = Logger.getLogger(this .getClass());
081: this .intf = intf;
082: this .key = partition.getPartitionName() + "/" + replicantName;
083:
084: // Obtain the hashes for the supported handler interfaces
085: Class[] ifaces = handler.getClass().getInterfaces();
086: for (int i = 0; i < ifaces.length; i++) {
087: Map tmp = MarshalledInvocation.methodToHashesMap(ifaces[i]);
088: invokerMap.putAll(tmp);
089: }
090:
091: if (bindAddress != null) {
092: // If there is no serverSocketFactory use a default
093: if (serverSocketFactory == null)
094: serverSocketFactory = new DefaultSocketFactory(
095: bindAddress);
096: else {
097: // See if the server socket supports setBindAddress(String)
098: try {
099: Class[] parameterTypes = { String.class };
100: Class ssfClass = serverSocketFactory.getClass();
101: Method m = ssfClass.getMethod("setBindAddress",
102: parameterTypes);
103: Object[] args = { bindAddress.getHostAddress() };
104: m.invoke(serverSocketFactory, args);
105: } catch (NoSuchMethodException e) {
106: log
107: .warn("Socket factory does not support setBindAddress(String)");
108: // Go with default address
109: } catch (Exception e) {
110: log.warn("Failed to setBindAddress=" + bindAddress
111: + " on socket factory", e);
112: // Go with default address
113: }
114: }
115: }
116:
117: this .rmistub = (RemoteStub) UnicastRemoteObject.exportObject(
118: this , port, clientSocketFactory, serverSocketFactory);// casting is necessary because interface has changed in JDK>=1.2
119: this .target = new RefreshProxiesHATarget(partition,
120: replicantName, rmistub, HATarget.ENABLE_INVOCATIONS);
121:
122: HARMIServer.rmiServers.put(key, this );
123: }
124:
125: /**
126: * Create a new HARMIServer implementation that will act as a RMI end-point for a specific server.
127: *
128: * @param partition {@link HAPartition} that will determine the cluster member
129: * @param replicantName Name of the service using this HARMIServer
130: * @param intf Class type under which should appear the RMI server dynamically built
131: * @param handler Target object to which calls will be forwarded
132: * @throws Exception Thrown if any exception occurs during call forwarding
133: */
134: public HARMIServerImpl(HAPartition partition, String replicantName,
135: Class intf, Object handler) throws Exception {
136: this (partition, replicantName, intf, handler, 0, null, null);
137: }
138:
139: /**
140: * Once a HARMIServer implementation exists, it is possible to ask for a stub that can, for example,
141: * be bound in JNDI for client use. Each client stub may incorpore a specific load-balancing
142: * policy.
143: *
144: * @param policy {@link org.jboss.ha.framework.interfaces.LoadBalancePolicy} implementation to ues on the client.
145: * @return
146: */
147: public Object createHAStub(LoadBalancePolicy policy) {
148: HARMIClient client = new HARMIClient(target.getReplicants(),
149: target.getCurrentViewId(), policy, key, handler);
150: this .target.addProxy(client);
151: return Proxy.newProxyInstance(intf.getClassLoader(),
152: new Class[] { intf, HARMIProxy.class }, client);
153: }
154:
155: public void destroy() {
156: try {
157: target.destroy();
158: HARMIServer.rmiServers.remove(key);
159: UnicastRemoteObject.unexportObject(this , true);
160:
161: } catch (Exception e) {
162: log.error("failed to destroy", e);
163: }
164: }
165:
166: // HARMIServer implementation ----------------------------------------------
167:
168: public HARMIResponse invoke(long clientViewId,
169: MarshalledInvocation mi) throws Exception {
170: mi.setMethodMap(invokerMap);
171: Method method = mi.getMethod();
172:
173: try {
174: HARMIResponse rsp = new HARMIResponse();
175: if (clientViewId != target.getCurrentViewId()) {
176: rsp.newReplicants = new ArrayList(target
177: .getReplicants());
178: rsp.currentViewId = target.getCurrentViewId();
179: }
180:
181: rsp.response = method.invoke(handler, mi.getArguments());
182: return rsp;
183: } catch (IllegalAccessException iae) {
184: throw iae;
185: } catch (IllegalArgumentException iae) {
186: throw iae;
187: } catch (java.lang.reflect.InvocationTargetException ite) {
188: throw (Exception) ite.getTargetException();
189: }
190: }
191:
192: public List getReplicants() throws Exception {
193: return target.getReplicants();
194: }
195:
196: public Object getLocal() throws Exception {
197: return handler;
198: }
199:
200: public class RefreshProxiesHATarget extends HATarget {
201: protected ArrayList generatedProxies;
202:
203: public RefreshProxiesHATarget(HAPartition partition,
204: String replicantName, java.io.Serializable target,
205: int allowInvocations) throws Exception {
206: super (partition, replicantName, target, allowInvocations);
207: }
208:
209: public void init() throws Exception {
210: super .init();
211: generatedProxies = new ArrayList();
212: }
213:
214: public synchronized void addProxy(HARMIClient client) {
215: SoftReference ref = new SoftReference(client);
216: generatedProxies.add(ref);
217: }
218:
219: public synchronized void replicantsChanged(String key,
220: List newReplicants, int newReplicantsViewId) {
221: super .replicantsChanged(key, newReplicants,
222: newReplicantsViewId);
223:
224: // we now update all generated proxies
225: //
226: int max = generatedProxies.size();
227: ArrayList trash = new ArrayList();
228: for (int i = 0; i < max; i++) {
229: SoftReference ref = (SoftReference) generatedProxies
230: .get(i);
231: HARMIClient proxy = (HARMIClient) ref.get();
232: if (proxy == null) {
233: trash.add(ref);
234: } else {
235: proxy.updateClusterInfo(this .replicants,
236: this .clusterViewId);
237: }
238: }
239:
240: if (trash.size() > 0)
241: generatedProxies.removeAll(trash);
242:
243: }
244: }
245: }
|