001 // Copyright 2004, 2005 The Apache Software Foundation
002 //
003 // Licensed under the Apache License, Version 2.0 (the "License");
004 // you may not use this file except in compliance with the License.
005 // You may obtain a copy of the License at
006 //
007 // http://www.apache.org/licenses/LICENSE-2.0
008 //
009 // Unless required by applicable law or agreed to in writing, software
010 // distributed under the License is distributed on an "AS IS" BASIS,
011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012 // See the License for the specific language governing permissions and
013 // limitations under the License.
014
015 package org.apache.hivemind.lib.impl;
016
017 import java.lang.reflect.Constructor;
018 import java.lang.reflect.Modifier;
019 import java.rmi.RemoteException;
020
021 import org.apache.hivemind.ApplicationRuntimeException;
022 import org.apache.hivemind.ServiceImplementationFactory;
023 import org.apache.hivemind.ServiceImplementationFactoryParameters;
024 import org.apache.hivemind.impl.BaseLocatable;
025 import org.apache.hivemind.internal.Module;
026 import org.apache.hivemind.lib.NameLookup;
027 import org.apache.hivemind.lib.RemoteExceptionCoordinator;
028 import org.apache.hivemind.service.BodyBuilder;
029 import org.apache.hivemind.service.ClassFab;
030 import org.apache.hivemind.service.ClassFabUtils;
031 import org.apache.hivemind.service.ClassFactory;
032 import org.apache.hivemind.service.MethodIterator;
033 import org.apache.hivemind.service.MethodSignature;
034
035 /**
036 * An implementation of {@link org.apache.hivemind.ServiceImplementationFactory}
037 * that can create a proxy to a stateless session EJB. Using this factory, it is
038 * easy to create a HiveMind service wrapper around the actual EJB.
039 *
040 * <p>
041 * The parameters for the factory are used to identify the JNDI name of the
042 * session EJB's home interface.
043 *
044 * @author Howard Lewis Ship
045 */
046 public class EJBProxyFactory extends BaseLocatable implements ServiceImplementationFactory
047 {
048 private NameLookup _nameLookup;
049 private RemoteExceptionCoordinator _coordinator;
050 private ClassFactory _classFactory;
051
052 public Object createCoreServiceImplementation(ServiceImplementationFactoryParameters factoryParameters)
053 {
054 EJBProxyParameters proxyParameters = (EJBProxyParameters) factoryParameters.getParameters().get(0);
055 String jndiName = proxyParameters.getJndiName();
056 String homeInterfaceClassName = proxyParameters.getHomeInterfaceClassName();
057
058 // The service interface is the remote interface.
059
060 Module module = factoryParameters.getInvokingModule();
061 Class serviceInterface = factoryParameters.getServiceInterface();
062
063 Class homeInterface = module.resolveType(homeInterfaceClassName);
064
065 String proxyClassName = ClassFabUtils.generateClassName(serviceInterface);
066
067 ClassFab classFab =
068 _classFactory.newClass(
069 proxyClassName,
070 AbstractEJBProxy.class);
071
072 classFab.addInterface(serviceInterface);
073
074 classFab.addField("_remote", serviceInterface);
075
076 addClearCachedMethod(classFab);
077
078 addLookupMethod(classFab, homeInterface, serviceInterface, jndiName);
079
080 addServiceMethods(classFab, serviceInterface, factoryParameters.getServiceId(), jndiName);
081
082 addConstructor(classFab);
083
084 Class proxyClass = classFab.createClass();
085
086 return invokeConstructor(proxyClass, proxyParameters.getNameLookup(_nameLookup));
087 }
088
089 private void addClearCachedMethod(ClassFab classFab)
090 {
091 classFab.addMethod(
092 Modifier.PROTECTED,
093 new MethodSignature(void.class, "_clearCachedReferences", null, null),
094 "_remote = null;");
095 }
096
097 private void addLookupMethod(
098 ClassFab classFab,
099 Class homeInterface,
100 Class remoteInterface,
101 String jndiName)
102 {
103 String homeInterfaceName = homeInterface.getName();
104
105 BodyBuilder builder = new BodyBuilder();
106
107 builder.begin();
108
109 builder.addln("if (_remote != null)");
110 builder.addln(" return _remote;");
111
112 builder.add(homeInterfaceName);
113 builder.add(" home = (");
114 builder.add(homeInterfaceName);
115 builder.add(") _lookup(");
116 builder.addQuoted(jndiName);
117 builder.addln(");");
118
119 builder.add("try");
120 builder.begin();
121 builder.add("_remote = home.create();");
122 builder.end();
123 builder.add("catch (javax.ejb.CreateException ex)");
124 builder.begin();
125 builder.add("throw new java.rmi.RemoteException(ex.getMessage(), ex);");
126 builder.end();
127
128 builder.add("return _remote;");
129
130 builder.end();
131
132 classFab.addMethod(
133 Modifier.SYNCHRONIZED + Modifier.PRIVATE,
134 new MethodSignature(
135 remoteInterface,
136 "_lookupRemote",
137 null,
138 new Class[] { RemoteException.class }),
139 builder.toString());
140
141 }
142
143 private void addServiceMethods(
144 ClassFab classFab,
145 Class serviceInterface,
146 String serviceId,
147 String jndiName)
148 {
149 MethodIterator mi = new MethodIterator(serviceInterface);
150
151 while (mi.hasNext())
152 {
153 addServiceMethod(classFab, mi.next());
154 }
155
156 if (!mi.getToString())
157 addToStringMethod(classFab, serviceInterface, serviceId, jndiName);
158 }
159
160 private void addServiceMethod(ClassFab classFab, MethodSignature sig)
161 {
162 String methodName = sig.getName();
163
164 boolean isVoid = sig.getReturnType().equals(Void.TYPE);
165
166 BodyBuilder builder = new BodyBuilder();
167
168 builder.begin();
169
170 builder.addln("boolean first = true;");
171 builder.add("while (true)");
172 builder.begin();
173
174 builder.add("try");
175 builder.begin();
176
177 if (!isVoid)
178 builder.add("return ");
179
180 builder.add("_lookupRemote().");
181 builder.add(methodName);
182 builder.addln("($$);");
183
184 if (isVoid)
185 builder.addln("return;");
186
187 builder.end(); // try
188
189 builder.add("catch (java.rmi.RemoteException ex)");
190 builder.begin();
191
192 builder.addln("if (first)");
193 builder.begin();
194
195 builder.addln("_handleRemoteException(ex);");
196 builder.addln("first = false;");
197
198 builder.end(); // if
199 builder.addln("else");
200 builder.add(" throw ex;");
201 builder.end(); // catch
202 builder.end(); // while
203 builder.end();
204
205 classFab.addMethod(Modifier.PUBLIC, sig, builder.toString());
206 }
207
208 private void addToStringMethod(
209 ClassFab classFab,
210 Class serviceInterface,
211 String serviceId,
212 String jndiName)
213 {
214 ClassFabUtils.addToStringMethod(
215 classFab,
216 ImplMessages.ejbProxyDescription(serviceId, serviceInterface, jndiName));
217 }
218
219 private void addConstructor(ClassFab classFab)
220 {
221 classFab.addConstructor(
222 new Class[] { NameLookup.class, RemoteExceptionCoordinator.class },
223 null,
224 "super($1, $2);");
225 }
226
227 private Object invokeConstructor(Class proxyClass, NameLookup nameLookup)
228 {
229 try
230 {
231 Constructor c =
232 proxyClass.getConstructor(
233 new Class[] { NameLookup.class, RemoteExceptionCoordinator.class });
234
235 return c.newInstance(new Object[] { nameLookup, _coordinator });
236 }
237 catch (Exception ex)
238 {
239 throw new ApplicationRuntimeException(ex);
240 }
241 }
242
243 public void setClassFactory(ClassFactory factory)
244 {
245 _classFactory = factory;
246 }
247
248 public void setCoordinator(RemoteExceptionCoordinator coordinator)
249 {
250 _coordinator = coordinator;
251 }
252
253 public void setNameLookup(NameLookup lookup)
254 {
255 _nameLookup = lookup;
256 }
257
258 }