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
015package org.apache.hivemind.lib.impl;
016
017import java.lang.reflect.Constructor;
018import java.lang.reflect.Modifier;
019import java.rmi.RemoteException;
020
021import org.apache.hivemind.ApplicationRuntimeException;
022import org.apache.hivemind.ServiceImplementationFactory;
023import org.apache.hivemind.ServiceImplementationFactoryParameters;
024import org.apache.hivemind.impl.BaseLocatable;
025import org.apache.hivemind.internal.Module;
026import org.apache.hivemind.lib.NameLookup;
027import org.apache.hivemind.lib.RemoteExceptionCoordinator;
028import org.apache.hivemind.service.BodyBuilder;
029import org.apache.hivemind.service.ClassFab;
030import org.apache.hivemind.service.ClassFabUtils;
031import org.apache.hivemind.service.ClassFactory;
032import org.apache.hivemind.service.MethodIterator;
033import 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 */
046public 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}