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 org.apache.hivemind.ApplicationRuntimeException;
018    import org.apache.hivemind.Discardable;
019    import org.apache.hivemind.HiveMind;
020    import org.apache.hivemind.events.RegistryShutdownListener;
021    import org.apache.hivemind.impl.ConstructableServicePoint;
022    import org.apache.hivemind.impl.ProxyUtils;
023    import org.apache.hivemind.internal.Module;
024    import org.apache.hivemind.service.ThreadCleanupListener;
025    import org.apache.hivemind.service.ThreadEventNotifier;
026    
027    /**
028     * Like {@link org.apache.hivemind.impl.servicemodel.SingletonServiceModel}, this method returns a
029     * proxy (implementing the service interface); unlike SingletonServiceModel, it <em>always</em>
030     * returns the proxy. Invoking a service method on the proxy constructs a service implementation and
031     * binds it to the current thread.
032     * 
033     * @author Howard Lewis Ship
034     */
035    public final class ThreadedServiceModel extends AbstractServiceModelImpl
036    {
037        /**
038         * Name of a method in the deferred proxy that is used to obtain the constructed service.
039         */
040        protected static final String SERVICE_ACCESSOR_METHOD_NAME = "_service";
041    
042        private final Object _serviceProxy;
043    
044    
045        private final ThreadEventNotifier _notifier;
046    
047        /**
048         * Used to store the active service for the current thread.
049         */
050        private final ThreadLocal _activeService = new ThreadLocal();
051    
052        /** @since 1.1 */
053    
054        private Class _serviceInterface;
055    
056        public ThreadedServiceModel(ConstructableServicePoint servicePoint)
057        {
058            super(servicePoint);
059    
060            _serviceInterface = servicePoint.getServiceInterface();
061    
062            Module module = getServicePoint().getModule();
063    
064            _notifier = (ThreadEventNotifier) module.getService(
065                    HiveMind.THREAD_EVENT_NOTIFIER_SERVICE,
066                    ThreadEventNotifier.class);
067    
068            _serviceProxy = createServiceProxy();
069        }
070    
071        class CleanupListener implements ThreadCleanupListener
072        {
073            // The core itself
074            private final Object _core;
075    
076            CleanupListener(Object core)
077            {
078                _core = core;
079            }
080    
081            public void threadDidCleanup()
082            {
083                unbindServiceFromCurrentThread();
084    
085                if (_core instanceof Discardable)
086                {
087                    Discardable d = (Discardable) _core;
088    
089                    d.threadDidDiscardService();
090                }
091            }
092        }
093    
094        /**
095         * Always returns the service proxy.
096         */
097        public Object getService()
098        {
099            // In 1.1 and earlier, we would lazily create the _serviceProxy here; but that meant the
100            // method had to be synchronized, which created a choke point.
101    
102            // The result is an interceptor stack, where the final (most deeply nested) object
103            // is the serviceProxy. The serviceProxy obtains the instance for the current thread
104            // and delegates to it. This is a little bit different than SingletonServiceModel, which
105            // creates a pair of proxies so as to defer creation of the interceptors as well. In both
106            // cases, the interceptors are only created once.
107    
108            return _serviceProxy;
109        }
110    
111        /**
112         * Creates a proxy instance for the service, and returns it, wrapped in any interceptors for the
113         * service.
114         */
115        private Object createServiceProxy()
116        {
117            ConstructableServicePoint servicePoint = getServicePoint();
118    
119            if (_log.isDebugEnabled())
120                _log.debug("Creating ThreadedProxy for service " + servicePoint.getExtensionPointId());
121    
122            Object proxy = ProxyUtils.createDelegatingProxy(
123                    "ThreadedProxy",
124                    this,
125                    "getServiceImplementationForCurrentThread",
126                    servicePoint);
127    
128            Object intercepted = addInterceptors(proxy);
129    
130            RegistryShutdownListener outerProxy = ProxyUtils
131                    .createOuterProxy(intercepted, servicePoint);
132    
133            servicePoint.addRegistryShutdownListener(outerProxy);
134    
135            return outerProxy;
136        }
137    
138        /**
139         * Invoked by the proxy to return the active service impl for this thread, constructing it as
140         * necessary.
141         */
142        public Object getServiceImplementationForCurrentThread()
143        {
144            Object result = _activeService.get();
145    
146            if (result == null)
147                result = constructInstanceForCurrentThread();
148    
149            return result;
150        }
151    
152        private Object constructInstanceForCurrentThread()
153        {
154            try
155            {
156                Object core = constructCoreServiceImplementation();
157    
158                if (core instanceof RegistryShutdownListener)
159                    _log.error(ServiceModelMessages.registryCleanupIgnored(getServicePoint()));
160    
161                _notifier.addThreadCleanupListener(new CleanupListener(core));
162    
163                // Once more ... with bean services, its possible that
164                // the factory generated bean does not implement the (synthetic) service
165                // interface, so create a bridge to it.
166    
167                if (!_serviceInterface.isInstance(core))
168                    core = constructBridgeProxy(core);
169    
170                _activeService.set(core);
171    
172                return core;
173            }
174            catch (Exception ex)
175            {
176                throw new ApplicationRuntimeException(ServiceModelMessages.unableToConstructService(
177                        getServicePoint(),
178                        ex), ex);
179            }
180        }
181    
182        private void unbindServiceFromCurrentThread()
183        {
184            _activeService.set(null);
185        }
186    
187        /**
188         * Invokes {@link #getServiceImplementationForCurrentThread()} to force the creation of the
189         * service implementation.
190         */
191    
192        public void instantiateService()
193        {
194            getServiceImplementationForCurrentThread();
195        }
196    
197    }