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