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    }