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.impl;
016    
017    import java.lang.reflect.Constructor;
018    import java.lang.reflect.Modifier;
019    
020    import org.apache.hivemind.ApplicationRuntimeException;
021    import org.apache.hivemind.events.RegistryShutdownListener;
022    import org.apache.hivemind.internal.ServiceModel;
023    import org.apache.hivemind.internal.ServicePoint;
024    import org.apache.hivemind.service.BodyBuilder;
025    import org.apache.hivemind.service.ClassFab;
026    import org.apache.hivemind.service.ClassFabUtils;
027    import org.apache.hivemind.service.MethodSignature;
028    import org.apache.hivemind.util.ConstructorUtils;
029    
030    /**
031     * Contains some common code used to create proxies that defer to a service model method for thier
032     * service.
033     * 
034     * @author Howard Lewis Ship
035     */
036    public final class ProxyUtils
037    {
038        public static final String SERVICE_ACCESSOR_METHOD_NAME = "_service";
039    
040        public static final String DELEGATE_ACCESSOR_METHOD_NAME = "_delegate";
041    
042        private ProxyUtils()
043        {
044            // Prevent instantiation
045        }
046    
047        /**
048         * Creates a class that implements the service interface. Implements a private synchronized
049         * method, _service(), that constructs the service as needed, and has each service interface
050         * method re-invoke on _service(). Adds a toString() method if the service interface does not
051         * define toString().
052         */
053        public static Object createDelegatingProxy(String type, ServiceModel serviceModel,
054                String delegationMethodName, ServicePoint servicePoint)
055        {
056            ProxyBuilder builder = new ProxyBuilder(type, servicePoint.getModule(),
057                    servicePoint.getServiceInterface(), servicePoint.getDeclaredInterface(), false);
058    
059            ClassFab classFab = builder.getClassFab();
060    
061            addConstructor(classFab, serviceModel);
062    
063            addServiceAccessor(classFab, delegationMethodName, servicePoint);
064    
065            builder.addServiceMethods(SERVICE_ACCESSOR_METHOD_NAME + "()");
066    
067            Class proxyClass = classFab.createClass();
068    
069            try
070            {
071                Constructor c = proxyClass.getConstructor(new Class[]
072                { String.class, serviceModel.getClass() });
073    
074                return c.newInstance(new Object[]
075                { servicePoint.getExtensionPointId(), serviceModel });
076            }
077            catch (Exception ex)
078            {
079                throw new ApplicationRuntimeException(ex);
080            }
081        }
082    
083        /**
084         * Constructs an outer proxy (for the threaded or pooled service). The outer proxy listens to
085         * the shutdown coordinator, and delegates from the declared interface (which may in fact be a
086         * bean) to the service interface.
087         * <p>
088         * The outer proxy is a {@link RegistryShutdownListener}; it can be registered for
089         * notifications and will respond by throwing an exception when service methods are invoked.
090         * 
091         * @param delegate
092         *            An object, implementing the service interface, that the proxy should delegate to.
093         * @param servicePoint
094         *            for which the proxy is being constructed
095         * @since 1.1
096         */
097    
098        public static RegistryShutdownListener createOuterProxy(Object delegate,
099                ServicePoint servicePoint)
100        {
101            ProxyBuilder builder = new ProxyBuilder("OuterProxy", servicePoint.getModule(),
102                    servicePoint.getServiceInterface(), servicePoint.getDeclaredInterface(), true);
103    
104            ClassFab classFab = builder.getClassFab();
105    
106            addDelegateAccessor(classFab, servicePoint, delegate);
107    
108            builder.addServiceMethods(DELEGATE_ACCESSOR_METHOD_NAME + "()");
109    
110            Class proxyClass = classFab.createClass();
111    
112            try
113            {
114                return (RegistryShutdownListener) ConstructorUtils.invokeConstructor(
115                        proxyClass,
116                        new Object[]
117                        { servicePoint.getExtensionPointId(), delegate });
118            }
119            catch (Exception ex)
120            {
121                throw new ApplicationRuntimeException(ex);
122            }
123        }
124    
125        /** @since 1.1 */
126    
127        private static void addDelegateAccessor(ClassFab classFab, ServicePoint servicePoint,
128                Object delegate)
129        {
130            classFab.addField("_shutdown", boolean.class);
131    
132            Class delegateClass = ClassFabUtils.getInstanceClass(classFab, delegate, servicePoint
133                    .getServiceInterface());
134    
135            classFab.addField("_delegate", delegateClass);
136    
137            classFab.addConstructor(new Class[]
138            { String.class, delegateClass }, null, "{ this($1); _delegate = $2; }");
139    
140            classFab.addInterface(RegistryShutdownListener.class);
141            if( RegistryShutdownListener.class.isAssignableFrom( delegateClass ) )
142            {
143                    classFab.addMethod(Modifier.PUBLIC | Modifier.FINAL, new MethodSignature(void.class,
144                        "registryDidShutdown", null, null), "{ _delegate.registryDidShutdown(); _delegate = null; _shutdown = true; }");
145            }
146            else
147            {
148                classFab.addMethod(Modifier.PUBLIC | Modifier.FINAL, new MethodSignature(void.class,
149                        "registryDidShutdown", null, null), "{ _delegate = null; _shutdown = true; }");
150            }
151            BodyBuilder builder = new BodyBuilder();
152    
153            builder.begin();
154    
155            builder.addln("if (_shutdown)");
156            builder.addln("  throw org.apache.hivemind.HiveMind#createRegistryShutdownException();");
157    
158            builder.add("return _delegate;");
159    
160            builder.end();
161    
162            classFab.addMethod(Modifier.FINAL | Modifier.PRIVATE, new MethodSignature(delegateClass,
163                    DELEGATE_ACCESSOR_METHOD_NAME, null, null), builder.toString());
164        }
165    
166        /**
167         * Adds a field, _serviceExtensionPoint, whose type matches this class, and a constructor which
168         * sets the field.
169         */
170        private static void addConstructor(ClassFab classFab, ServiceModel model)
171        {
172            Class modelClass = model.getClass();
173    
174            classFab.addField("_serviceModel", modelClass);
175    
176            classFab.addConstructor(new Class[]
177            { String.class, modelClass }, null, "{ this($1); _serviceModel = $2; }");
178        }
179    
180        /**
181         * We construct a method that always goes through this service model's
182         * {@link #getServiceImplementationForCurrentThread()} method.
183         */
184        private static void addServiceAccessor(ClassFab classFab, String serviceModelMethodName,
185                ServicePoint servicePoint)
186        {
187            Class serviceInterface = servicePoint.getServiceInterface();
188    
189            classFab.addField(SERVICE_ACCESSOR_METHOD_NAME, serviceInterface);
190    
191            BodyBuilder builder = new BodyBuilder();
192            builder.begin();
193    
194            builder.add("return (");
195            builder.add(serviceInterface.getName());
196            builder.add(") _serviceModel.");
197            builder.add(serviceModelMethodName);
198            builder.add("();");
199    
200            builder.end();
201    
202            classFab.addMethod(Modifier.PRIVATE | Modifier.FINAL, new MethodSignature(serviceInterface,
203                    SERVICE_ACCESSOR_METHOD_NAME, null, null), builder.toString());
204        }
205    }