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