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.servicemodel;
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.impl.ConstructableServicePoint;
023import org.apache.hivemind.impl.ProxyBuilder;
024import org.apache.hivemind.internal.ServicePoint;
025import org.apache.hivemind.service.BodyBuilder;
026import org.apache.hivemind.service.ClassFab;
027import org.apache.hivemind.service.MethodSignature;
028
029/**
030 * Subclass of {@link org.apache.hivemind.impl.servicemodel.AbstractServiceModelImpl} which supports
031 * creation of a singleton service proxy (deferring the actual construction of the service until
032 * absolutely necessary). This is used with the singleton service type, which is the default.
033 * 
034 * @author Howard Lewis Ship
035 */
036public final class SingletonServiceModel extends AbstractServiceModelImpl
037{
038    /**
039     * Name of a method in the deferred proxy that is used to obtain the constructed service.
040     */
041    protected static final String SERVICE_ACCESSOR_METHOD_NAME = "_service";
042
043    private Object _serviceProxy;
044
045    private SingletonInnerProxy _innerProxy;
046
047    private Object _constructedService;
048
049    public SingletonServiceModel(ConstructableServicePoint servicePoint)
050    {
051        super(servicePoint);
052    }
053
054    public synchronized Object getService()
055    {
056        if (_serviceProxy == null)
057            _serviceProxy = createSingletonProxy();
058
059        return _serviceProxy;
060    }
061
062    /**
063     * This is invoked by the proxy to create the actual implementation.
064     */
065    public synchronized Object getActualServiceImplementation()
066    {
067        if (_constructedService == null)
068        {
069            _constructedService = constructServiceImplementation();
070            registerWithShutdownCoordinator(_constructedService);
071        }
072
073        // The inner proxy needs the service to implement the service interface.
074        // For bean services (not interface services) with no interceptors,
075        // the implementation may be the bean provided by the factory ... which
076        // does not implement the service interface (which was created at runtime).
077        // So we introduce a "bridge" between the two.
078
079        Class serviceInterface = getServicePoint().getServiceInterface();
080
081        if (!serviceInterface.isInstance(_constructedService))
082            _constructedService = constructBridgeProxy(_constructedService);
083
084        return _constructedService;
085    }
086
087    /**
088     * Creates a proxy class for the service and then construct the class itself.
089     */
090    private Object createSingletonProxy()
091    {
092        if (_log.isDebugEnabled())
093            _log.debug("Creating SingletonProxy for service "
094                    + getServicePoint().getExtensionPointId());
095
096        try
097        {
098
099            // Create the outer proxy, the one visible to client code (including
100            // other services). It is dependent on an inner proxy.
101
102            Class proxyClass = createSingletonProxyClass();
103
104            // Create the inner proxy, whose job is to replace itself
105            // when the first service method is invoked.
106
107            Class innerProxyClass = createInnerProxyClass(proxyClass);
108
109            // Create the outer proxy.
110
111            Object result = proxyClass.newInstance();
112
113            // The inner proxy's construct invokes a method on the
114            // outer proxy to connect the two.
115
116            Constructor c = innerProxyClass.getConstructor(new Class[]
117            { proxyClass, getClass() });
118
119            _innerProxy = (SingletonInnerProxy) c.newInstance(new Object[]
120            { result, this });
121
122            RegistryShutdownListener asListener = (RegistryShutdownListener) result;
123
124            getServicePoint().addRegistryShutdownListener(asListener);
125
126            return result;
127        }
128        catch (Exception ex)
129        {
130            throw new ApplicationRuntimeException(ex);
131        }
132
133    }
134
135    /**
136     * Creates a class that implements the service interface. Implements a private synchronized
137     * method, _service(), that constructs the service as needed, and has each service interface
138     * method re-invoke on _service(). Adds a toString() method if the service interface does not
139     * define toString().
140     */
141    private Class createSingletonProxyClass()
142    {
143        ConstructableServicePoint servicePoint = getServicePoint();
144
145        ProxyBuilder proxyBuilder = new ProxyBuilder("SingletonProxy", servicePoint, true);
146
147        ClassFab classFab = proxyBuilder.getClassFab();
148
149        Class serviceInterface = servicePoint.getServiceInterface();
150
151        // This will initally be the inner proxy, then switch over to the
152        // service implementation.
153
154        classFab.addField("_inner", serviceInterface);
155        classFab.addField("_shutdown", boolean.class);
156        if (!RegistryShutdownListener.class.isAssignableFrom(serviceInterface))
157        {
158            classFab.addInterface(RegistryShutdownListener.class);
159
160            classFab.addMethod(Modifier.PUBLIC | Modifier.FINAL, new MethodSignature(void.class,
161                    "registryDidShutdown", null, null), "{ _shutdown = true; }");
162        }
163        classFab.addMethod(
164                Modifier.PUBLIC | Modifier.SYNCHRONIZED | Modifier.FINAL,
165                new MethodSignature(void.class, "_setInner", new Class[]
166                { serviceInterface }, null),
167                "{ _inner = $1; }");
168
169        BodyBuilder builder = new BodyBuilder();
170        builder.begin();
171        builder.addln("if (_shutdown)");
172        builder.begin();
173        builder.addln("_inner = null;");
174        builder.addln("throw org.apache.hivemind.HiveMind#createRegistryShutdownException();");
175        builder.end();
176
177        builder.addln("return _inner;");
178        builder.end();
179
180        classFab.addMethod(Modifier.PRIVATE, new MethodSignature(serviceInterface, "_getInner",
181                null, null), builder.toString());
182
183        proxyBuilder.addServiceMethods("_getInner()");
184
185        return classFab.createClass();
186    }
187
188    private Class createInnerProxyClass(Class deferredProxyClass)
189    {
190        ServicePoint servicePoint = getServicePoint();
191
192        Class serviceInterface = servicePoint.getServiceInterface();
193        ProxyBuilder builder = new ProxyBuilder("InnerProxy", servicePoint);
194
195        ClassFab classFab = builder.getClassFab();
196
197        classFab.addField("_deferredProxy", deferredProxyClass);
198        classFab.addField("_service", serviceInterface);
199        classFab.addField("_serviceModel", getClass());
200
201        BodyBuilder body = new BodyBuilder();
202
203        // The constructor remembers the outer proxy and registers itself
204        // with the outer proxy.
205
206        body.begin();
207
208        body.addln("super();");
209        body.addln("_deferredProxy = $1;");
210        body.addln("_serviceModel = $2;");
211        body.addln("_deferredProxy._setInner(this);");
212
213        body.end();
214
215        classFab.addConstructor(new Class[]
216        { deferredProxyClass, getClass() }, null, body.toString());
217
218        // Method _service() will look up the service implementation,
219        // then update the deferred proxy to go directly to the
220        // service implementation, bypassing itself!
221
222        body.clear();
223        body.begin();
224
225        body.add("if (_service == null)");
226        body.begin();
227
228        body.add("_service = (");
229        body.add(serviceInterface.getName());
230        body.addln(") _serviceModel.getActualServiceImplementation();");
231
232        body.add("_deferredProxy._setInner(_service);");
233
234        body.end();
235
236        body.add("return _service;");
237
238        body.end();
239
240        classFab.addMethod(
241                Modifier.PRIVATE | Modifier.FINAL | Modifier.SYNCHRONIZED,
242                new MethodSignature(serviceInterface, "_service", null, null),
243                body.toString());
244
245        builder.addServiceMethods("_service()");
246
247        // Build the implementation of interface SingletonInnerProxy
248
249        body.clear();
250        body.begin();
251
252        body.add("_service();");
253
254        body.end();
255
256        classFab.addMethod(Modifier.PUBLIC | Modifier.FINAL, new MethodSignature(void.class,
257                "_instantiateServiceImplementation", null, null), body.toString());
258
259        classFab.addInterface(SingletonInnerProxy.class);
260
261        return classFab.createClass();
262    }
263
264    public void instantiateService()
265    {
266        // Ensure that the outer and inner proxies have been created
267
268        getService();
269
270        // Force the inner proxy to resolve the service and install the result into
271        // the outer proxy.
272
273        _innerProxy._instantiateServiceImplementation();
274    }
275
276}