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