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 java.util.ArrayList;
018import java.util.List;
019
020import org.apache.hivemind.ApplicationRuntimeException;
021import org.apache.hivemind.HiveMind;
022import org.apache.hivemind.PoolManageable;
023import org.apache.hivemind.events.RegistryShutdownListener;
024import org.apache.hivemind.impl.ConstructableServicePoint;
025import org.apache.hivemind.impl.ProxyUtils;
026import org.apache.hivemind.internal.Module;
027import org.apache.hivemind.service.ThreadCleanupListener;
028import org.apache.hivemind.service.ThreadEventNotifier;
029
030/**
031 * Similar to the
032 * {@link org.apache.hivemind.impl.servicemodel.ThreadedServiceModel threaded service model},
033 * except that, once created, services are pooled for later use.
034 * 
035 * @author Howard Lewis Ship
036 */
037public class PooledServiceModel extends AbstractServiceModelImpl
038{
039    /**
040     * Name of a method in the deferred proxy that is used to obtain the constructed service.
041     */
042    protected static final String SERVICE_ACCESSOR_METHOD_NAME = "_service";
043
044    private final Object _serviceProxy;
045
046    private final ThreadEventNotifier _notifier;
047
048    private final ThreadLocal _activeService = new ThreadLocal();
049
050    private final List _servicePool = new ArrayList();
051
052    /** @since 1.1 */
053
054    private Class _serviceInterface;
055
056    /**
057     * Shared, null implementation of PoolManageable.
058     */
059    private static final PoolManageable NULL_MANAGEABLE = new PoolManageable()
060    {
061        public void activateService()
062        {
063        }
064
065        public void passivateService()
066        {
067        }
068    };
069
070    private class PooledService implements ThreadCleanupListener
071    {
072        private Object _core;
073
074        private PoolManageable _managed;
075
076        /**
077         * @param service
078         *            the full service implementation, including any interceptors
079         * @param core
080         *            the core service implementation, which may optionally implement
081         *            {@link PoolManageable}
082         */
083        PooledService(Object core)
084        {
085            _core = core;
086
087            if (core instanceof PoolManageable)
088                _managed = (PoolManageable) core;
089            else
090                _managed = NULL_MANAGEABLE;
091        }
092
093        public void threadDidCleanup()
094        {
095            unbindPooledServiceFromCurrentThread(this);
096        }
097
098        void activate()
099        {
100            _managed.activateService();
101        }
102
103        void passivate()
104        {
105            _managed.passivateService();
106        }
107
108        /**
109         * Returns the configured service implementation.
110         */
111        public Object getService()
112        {
113            return _core;
114        }
115
116    }
117
118    public PooledServiceModel(ConstructableServicePoint servicePoint)
119    {
120        super(servicePoint);
121
122        _serviceInterface = servicePoint.getServiceInterface();
123
124        Module module = getServicePoint().getModule();
125
126        _notifier = (ThreadEventNotifier) module.getService(
127                HiveMind.THREAD_EVENT_NOTIFIER_SERVICE,
128                ThreadEventNotifier.class);
129
130        _serviceProxy = constructServiceProxy();
131    }
132
133    public Object getService()
134    {
135        return _serviceProxy;
136    }
137
138    /**
139     * Constructs the service proxy and returns it, wrapped in any interceptors.
140     */
141    private Object constructServiceProxy()
142    {
143        ConstructableServicePoint servicePoint = getServicePoint();
144
145        if (_log.isDebugEnabled())
146            _log.debug("Creating PooledProxy for service " + servicePoint.getExtensionPointId());
147
148        Object proxy = ProxyUtils.createDelegatingProxy(
149                "PooledProxy",
150                this,
151                "getServiceImplementationForCurrentThread",
152                servicePoint);
153
154        Object intercepted = addInterceptors(proxy);
155
156        RegistryShutdownListener outerProxy = ProxyUtils
157                .createOuterProxy(intercepted, servicePoint);
158
159        servicePoint.addRegistryShutdownListener(outerProxy);
160
161        return outerProxy;
162    }
163
164    public Object getServiceImplementationForCurrentThread()
165    {
166        PooledService pooled = (PooledService) _activeService.get();
167
168        if (pooled == null)
169        {
170            pooled = obtainPooledService();
171
172            pooled.activate();
173
174            _notifier.addThreadCleanupListener(pooled);
175            _activeService.set(pooled);
176        }
177
178        return pooled.getService();
179    }
180
181    private PooledService obtainPooledService()
182    {
183        PooledService result = getServiceFromPool();
184
185        if (result == null)
186            result = constructPooledService();
187
188        return result;
189    }
190
191    private synchronized PooledService getServiceFromPool()
192    {
193        int count = _servicePool.size();
194
195        if (count == 0)
196            return null;
197
198        return (PooledService) _servicePool.remove(count - 1);
199    }
200
201    private synchronized void returnServiceToPool(PooledService pooled)
202    {
203        _servicePool.add(pooled);
204    }
205
206    private PooledService constructPooledService()
207    {
208        try
209        {
210            Object core = constructCoreServiceImplementation();
211
212            // This is related to bean services.
213
214            if (!_serviceInterface.isInstance(core))
215                core = constructBridgeProxy(core);
216
217            registerWithShutdownCoordinator(core);
218
219            return new PooledService(core);
220        }
221        catch (Exception ex)
222        {
223            throw new ApplicationRuntimeException(ServiceModelMessages.unableToConstructService(
224                    getServicePoint(),
225                    ex), ex);
226        }
227    }
228
229    private void unbindPooledServiceFromCurrentThread(PooledService pooled)
230    {
231        _activeService.set(null);
232
233        pooled.passivate();
234
235        returnServiceToPool(pooled);
236    }
237
238    /**
239     * Invokes {@link #getServiceImplementationForCurrentThread()} to instantiate an instance of the
240     * service.
241     */
242    public void instantiateService()
243    {
244        getServiceImplementationForCurrentThread();
245    }
246
247}