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                Constructor co = proxyClass.getConstructor(new Class[] { String.class });
112    
113                Object result = co.newInstance(new Object[] { getServicePoint().getExtensionPointId() });
114                
115                // The inner proxy's construct invokes a method on the
116                // outer proxy to connect the two.
117    
118                Constructor c = innerProxyClass.getConstructor(new Class[]
119                { String.class, proxyClass, getClass() });
120    
121                _innerProxy = (SingletonInnerProxy) c.newInstance(new Object[]
122                { getServicePoint().getExtensionPointId(), result, this });
123    
124                RegistryShutdownListener asListener = (RegistryShutdownListener) result;
125    
126                getServicePoint().addRegistryShutdownListener(asListener);
127    
128                return result;
129            }
130            catch (Exception ex)
131            {
132                throw new ApplicationRuntimeException(ex);
133            }
134    
135        }
136    
137        /**
138         * Creates a class that implements the service interface. Implements a private synchronized
139         * method, _service(), that constructs the service as needed, and has each service interface
140         * method re-invoke on _service(). Adds a toString() method if the service interface does not
141         * define toString().
142         */
143        private Class createSingletonProxyClass()
144        {
145            ConstructableServicePoint servicePoint = getServicePoint();
146    
147            ProxyBuilder proxyBuilder = new ProxyBuilder("SingletonProxy", servicePoint.getModule(),
148                    servicePoint.getServiceInterface(), servicePoint.getDeclaredInterface(), true);
149    
150            ClassFab classFab = proxyBuilder.getClassFab();
151    
152            Class serviceInterface = servicePoint.getServiceInterface();
153    
154            // This will initally be the inner proxy, then switch over to the
155            // service implementation.
156    
157            classFab.addField("_inner", serviceInterface);
158            classFab.addField("_shutdown", boolean.class);
159            if (!RegistryShutdownListener.class.isAssignableFrom(serviceInterface))
160            {
161                classFab.addInterface(RegistryShutdownListener.class);
162    
163                classFab.addMethod(Modifier.PUBLIC | Modifier.FINAL, new MethodSignature(void.class,
164                        "registryDidShutdown", null, null), "{ _shutdown = true; }");
165            }
166            classFab.addMethod(
167                    Modifier.PUBLIC | Modifier.SYNCHRONIZED | Modifier.FINAL,
168                    new MethodSignature(void.class, "_setInner", new Class[]
169                    { serviceInterface }, null),
170                    "{ _inner = $1; }");
171    
172            BodyBuilder builder = new BodyBuilder();
173            builder.begin();
174            builder.addln("if (_shutdown)");
175            builder.begin();
176            builder.addln("_inner = null;");
177            builder.addln("throw org.apache.hivemind.HiveMind#createRegistryShutdownException();");
178            builder.end();
179    
180            builder.addln("return _inner;");
181            builder.end();
182    
183            classFab.addMethod(Modifier.PRIVATE, new MethodSignature(serviceInterface, "_getInner",
184                    null, null), builder.toString());
185    
186            proxyBuilder.addServiceMethods("_getInner()");
187    
188            return classFab.createClass();
189        }
190    
191        private Class createInnerProxyClass(Class deferredProxyClass)
192        {
193            ServicePoint servicePoint = getServicePoint();
194    
195            Class serviceInterface = servicePoint.getServiceInterface();
196            
197            ProxyBuilder builder = new ProxyBuilder("InnerProxy", servicePoint.getModule(),
198                    serviceInterface, servicePoint.getDeclaredInterface(), false);
199    
200            ClassFab classFab = builder.getClassFab();
201    
202            classFab.addField("_deferredProxy", deferredProxyClass);
203            classFab.addField("_service", serviceInterface);
204            classFab.addField("_serviceModel", getClass());
205    
206            BodyBuilder body = new BodyBuilder();
207    
208            // The constructor remembers the outer proxy and registers itself
209            // with the outer proxy.
210    
211            body.begin();
212    
213            body.addln("this($1);");
214            body.addln("_deferredProxy = $2;");
215            body.addln("_serviceModel = $3;");
216            body.addln("_deferredProxy._setInner(this);");
217    
218            body.end();
219    
220            classFab.addConstructor(new Class[]
221            { String.class, deferredProxyClass, getClass() }, null, body.toString());
222    
223            // Method _service() will look up the service implementation,
224            // then update the deferred proxy to go directly to the
225            // service implementation, bypassing itself!
226    
227            body.clear();
228            body.begin();
229    
230            body.add("if (_service == null)");
231            body.begin();
232    
233            body.add("_service = (");
234            body.add(serviceInterface.getName());
235            body.addln(") _serviceModel.getActualServiceImplementation();");
236    
237            body.add("_deferredProxy._setInner(_service);");
238    
239            body.end();
240    
241            body.add("return _service;");
242    
243            body.end();
244    
245            classFab.addMethod(
246                    Modifier.PRIVATE | Modifier.FINAL | Modifier.SYNCHRONIZED,
247                    new MethodSignature(serviceInterface, "_service", null, null),
248                    body.toString());
249    
250            builder.addServiceMethods("_service()");
251    
252            // Build the implementation of interface SingletonInnerProxy
253    
254            body.clear();
255            body.begin();
256    
257            body.add("_service();");
258    
259            body.end();
260    
261            classFab.addMethod(Modifier.PUBLIC | Modifier.FINAL, new MethodSignature(void.class,
262                    "_instantiateServiceImplementation", null, null), body.toString());
263    
264            classFab.addInterface(SingletonInnerProxy.class);
265    
266            return classFab.createClass();
267        }
268    
269        public void instantiateService()
270        {
271            // Ensure that the outer and inner proxies have been created
272    
273            getService();
274    
275            // Force the inner proxy to resolve the service and install the result into
276            // the outer proxy.
277    
278            _innerProxy._instantiateServiceImplementation();
279        }
280    
281    }