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.events.RegistryShutdownListener;
024    import org.apache.hivemind.impl.ConstructableServicePoint;
025    import org.apache.hivemind.impl.ProxyUtils;
026    import org.apache.hivemind.internal.Module;
027    import org.apache.hivemind.service.ThreadCleanupListener;
028    import 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     */
037    public 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    }