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