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.server;
23
24 import java.util.ArrayList;
25 import java.util.Iterator;
26 import java.util.Map;
27 import java.util.HashMap;
28 import java.lang.reflect.Method;
29 import javax.management.ObjectName;
30 import javax.naming.InitialContext;
31
32 import org.jboss.invocation.InvokerInterceptor;
33 import org.jboss.invocation.Invocation;
34 import org.jboss.invocation.MarshalledInvocation;
35 import org.jboss.naming.Util;
36 import org.jboss.proxy.ClientMethodInterceptor;
37 import org.jboss.proxy.GenericProxyFactory;
38 import org.jboss.system.Registry;
39 import org.jboss.system.ServiceMBeanSupport;
40 import org.jboss.metadata.MetaData;
41 import org.w3c.dom.Element;
42
43 /** Create an interface proxy that uses RMI/JRMP to communicate with the server
44 * side object that exposes the corresponding JMX invoke operation. Requests
45 * make through the proxy are sent to the JRMPInvoker instance the proxy
46 * is bound to.
47 *
48 * @author Scott.Stark@jboss.org
49 * @version $Revision: 37459 $
50 */
51 public class JRMPProxyFactory extends ServiceMBeanSupport
52 implements JRMPProxyFactoryMBean
53 {
54 /** The server side JRMPInvoker mbean that will handle RMI/JRMP transport */
55 private ObjectName invokerName;
56 /** The server side mbean that exposes the invoke operation for the
57 exported interface */
58 private ObjectName targetName;
59 /** The Proxy object which uses the proxy as its handler */
60 protected Object theProxy;
61 /** The JNDI name under which the proxy will be bound */
62 private String jndiName;
63 /** The interface that the proxy implements */
64 private Class[] exportedInterfaces;
65 /** The optional definition */
66 private Element interceptorConfig;
67 /** The interceptor Classes defined in the interceptorConfig */
68 private ArrayList interceptorClasses = new ArrayList();
69 /** invoke target method */
70 private boolean invokeTargetMethod;
71 /** methods by their hash code */
72 private final Map methodMap = new HashMap();
73 /** signatures by method */
74 private final Map signatureMap = new HashMap();
75
76 public JRMPProxyFactory()
77 {
78 interceptorClasses.add(ClientMethodInterceptor.class);
79 interceptorClasses.add(InvokerInterceptor.class);
80 }
81
82 public ObjectName getInvokerName()
83 {
84 return invokerName;
85 }
86 public void setInvokerName(ObjectName invokerName)
87 {
88 this.invokerName = invokerName;
89 }
90
91 public ObjectName getTargetName()
92 {
93 return targetName;
94 }
95 public void setTargetName(ObjectName targetName)
96 {
97 this.targetName = targetName;
98 }
99
100 public String getJndiName()
101 {
102 return jndiName;
103 }
104 public void setJndiName(String jndiName)
105 {
106 this.jndiName = jndiName;
107 }
108
109 public Class getExportedInterface()
110 {
111 return exportedInterfaces[0];
112 }
113 public void setExportedInterface(Class exportedInterface)
114 {
115 this.exportedInterfaces = new Class[] {exportedInterface};
116 }
117
118 public Class[] getExportedInterfaces()
119 {
120 return exportedInterfaces;
121 }
122 public void setExportedInterfaces(Class[] exportedInterfaces)
123 {
124 this.exportedInterfaces = exportedInterfaces;
125 }
126
127 public boolean getInvokeTargetMethod()
128 {
129 return invokeTargetMethod;
130 }
131
132 public void setInvokeTargetMethod(boolean invokeTargetMethod)
133 {
134 this.invokeTargetMethod = invokeTargetMethod;
135 }
136
137 public Element getClientInterceptors()
138 {
139 return interceptorConfig;
140 }
141 public void setClientInterceptors(Element config) throws Exception
142 {
143 this.interceptorConfig = config;
144 Iterator interceptorElements = MetaData.getChildrenByTagName(interceptorConfig, "interceptor");
145 ClassLoader loader = Thread.currentThread().getContextClassLoader();
146 interceptorClasses.clear();
147 while( interceptorElements != null && interceptorElements.hasNext() )
148 {
149 Element ielement = (Element) interceptorElements.next();
150 String className = null;
151 className = MetaData.getElementContent(ielement);
152 Class clazz = loader.loadClass(className);
153 interceptorClasses.add(clazz);
154 log.debug("added interceptor type: "+clazz);
155 }
156 }
157
158 public Object getProxy()
159 {
160 return theProxy;
161 }
162
163 public Object invoke(Invocation mi) throws Exception
164 {
165 final boolean remoteInvocation = mi instanceof MarshalledInvocation;
166 if(remoteInvocation)
167 {
168 ((MarshalledInvocation)mi).setMethodMap(methodMap);
169 }
170
171 final Object result;
172 if(invokeTargetMethod)
173 {
174 String signature[] = (String[])signatureMap.get(mi.getMethod());
175 result = server.invoke(targetName, mi.getMethod().getName(), mi.getArguments(), signature);
176 }
177 else
178 {
179 result = server.invoke(targetName, "invoke", new Object[]{mi}, Invocation.INVOKE_SIGNATURE);
180 }
181
182 return result;
183 }
184
185 /** Initializes the servlet.
186 */
187 protected void startService() throws Exception
188 {
189 /* Create a binding between the invoker name hash and the jmx name
190 This is used by the JRMPInvoker to map from the Invocation ObjectName
191 hash value to the target JMX ObjectName.
192 */
193 Integer nameHash = new Integer(getServiceName().hashCode());
194 Registry.bind(nameHash, getServiceName());
195
196 // Create the service proxy
197 Object cacheID = null;
198 String proxyBindingName = null;
199 Class[] ifaces = exportedInterfaces;
200 ClassLoader loader = Thread.currentThread().getContextClassLoader();
201 createProxy(cacheID, proxyBindingName, loader, ifaces);
202 log.debug("Created JRMPPRoxy for service="+targetName
203 +", nameHash="+nameHash+", invoker="+invokerName);
204
205 if( jndiName != null )
206 {
207 InitialContext iniCtx = new InitialContext();
208 Util.bind(iniCtx, jndiName, theProxy);
209 log.debug("Bound proxy under jndiName="+jndiName);
210 }
211
212 for(int i = 0; i < exportedInterfaces.length; ++i)
213 {
214 final Method[] methods = exportedInterfaces[i].getMethods();
215 for(int j = 0; j < methods.length; ++j)
216 {
217 methodMap.put(new Long(MarshalledInvocation.calculateHash(methods[j])), methods[j]);
218
219 String signature[];
220 final Class[] types = methods[j].getParameterTypes();
221 if(types == null || types.length == 0)
222 {
223 signature = null;
224 }
225 else
226 {
227 signature = new String[types.length];
228 for(int typeInd = 0; typeInd < types.length; ++typeInd)
229 {
230 signature[typeInd] = types[typeInd].getName();
231 }
232 }
233 signatureMap.put(methods[j], signature);
234 }
235 }
236 }
237
238 protected void stopService() throws Exception
239 {
240 Integer nameHash = new Integer(getServiceName().hashCode());
241 Registry.unbind(nameHash);
242 if( jndiName != null )
243 {
244 InitialContext iniCtx = new InitialContext();
245 Util.unbind(iniCtx, jndiName);
246 }
247 this.theProxy = null;
248 }
249
250 protected void destroyService() throws Exception
251 {
252 interceptorClasses.clear();
253 }
254
255 protected void createProxy
256 (
257 Object cacheID,
258 String proxyBindingName,
259 ClassLoader loader,
260 Class[] ifaces
261 )
262 {
263 GenericProxyFactory proxyFactory = new GenericProxyFactory();
264 theProxy = proxyFactory.createProxy(cacheID, getServiceName(), invokerName,
265 jndiName, proxyBindingName, interceptorClasses, loader, ifaces);
266 }
267
268 protected void rebind() throws Exception
269 {
270 log.debug("(re-)Binding " + jndiName);
271 Util.rebind(new InitialContext(), jndiName, theProxy);
272 }
273
274 protected ArrayList getInterceptorClasses()
275 {
276 return interceptorClasses;
277 }
278 }