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
015package org.apache.hivemind.impl.servicemodel;
016
017import org.apache.hivemind.ApplicationRuntimeException;
018import org.apache.hivemind.Discardable;
019import org.apache.hivemind.HiveMind;
020import org.apache.hivemind.events.RegistryShutdownListener;
021import org.apache.hivemind.impl.ConstructableServicePoint;
022import org.apache.hivemind.impl.ProxyUtils;
023import org.apache.hivemind.internal.Module;
024import org.apache.hivemind.service.ThreadCleanupListener;
025import 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 */
035public 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}