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.servicemodel;
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.impl.ConstructableServicePoint;
023    import org.apache.hivemind.impl.ProxyBuilder;
024    import org.apache.hivemind.internal.ServicePoint;
025    import org.apache.hivemind.service.BodyBuilder;
026    import org.apache.hivemind.service.ClassFab;
027    import 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     */
036    public 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    }