001    package org.apache.ojb.broker.core;
002    
003    /* Copyright 2003-2005 The Apache Software Foundation
004     *
005     * Licensed under the Apache License, Version 2.0 (the "License");
006     * you may not use this file except in compliance with the License.
007     * You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    import java.util.Properties;
019    
020    import org.apache.commons.lang.builder.ToStringBuilder;
021    import org.apache.commons.lang.builder.ToStringStyle;
022    import org.apache.commons.pool.KeyedObjectPool;
023    import org.apache.commons.pool.KeyedPoolableObjectFactory;
024    import org.apache.commons.pool.impl.GenericKeyedObjectPool;
025    import org.apache.ojb.broker.PBFactoryException;
026    import org.apache.ojb.broker.PBKey;
027    import org.apache.ojb.broker.PBState;
028    import org.apache.ojb.broker.PersistenceBroker;
029    import org.apache.ojb.broker.PersistenceBrokerInternal;
030    import org.apache.ojb.broker.util.BrokerHelper;
031    import org.apache.ojb.broker.util.logging.Logger;
032    import org.apache.ojb.broker.util.logging.LoggerFactory;
033    
034    /**
035     * This is the default implementation of the {@link PersistenceBrokerFactoryIF}
036     * interface.
037     * <p>
038     * This implementation use a pool of {@link org.apache.ojb.broker.PersistenceBroker}
039     * instances [abbr. PB]. Each pooled PB instance (the implementation class was specified
040     * in OJB configuration file) is wrapped by {@link PoolablePersistenceBroker} class
041     * before add to pool.
042     * </p>
043     * <p>
044     * When calling {@link #createPersistenceBroker} or {@link #defaultPersistenceBroker} the pooled-PB
045     * instance (<tt>PoolablePersistenceBroker</tt>) on its part was wrapped with {@link PersistenceBrokerHandle}
046     * handle.
047     * </p>
048     * <p>
049     * When a client do a PB.close() call on the handle the wrapped <tt>PoolablePersistenceBroker</tt> will
050     * be closed and returned to pool. All further method calls on the handle
051     * (except <tt>PB.isClosed()</tt> and <tt>PB.isInTransaction()</tt>) result in an exception.
052     * </p>
053     * Each different {@link org.apache.ojb.broker.PBKey} (based on <code>PBKey.equals(...)</code> method)
054     * get its own PB-pool.
055     *
056     * @see PersistenceBrokerFactoryBaseImpl
057     *
058     * @author <a href="mailto:thma@apache.org">Thomas Mahler<a>
059     * @author <a href="mailto:armin@codeAuLait.de">Armin Waibel</a>
060     * @version $Id: PersistenceBrokerFactoryDefaultImpl.java,v 1.1 2007-08-24 22:17:35 ewestfal Exp $
061     */
062    public class PersistenceBrokerFactoryDefaultImpl extends PersistenceBrokerFactoryBaseImpl
063    {
064        private static Logger log = LoggerFactory.getLogger(PersistenceBrokerFactoryDefaultImpl.class);
065        private GenericKeyedObjectPool brokerPool;
066        private PBPoolInfo poolConfig;
067    
068        public PersistenceBrokerFactoryDefaultImpl()
069        {
070            super();
071            // get PB-pool configuration properties from OJB.properties
072            poolConfig = new PBPoolInfo();
073            // setup pool for PB instances
074            brokerPool = this.createPool();
075            log.info("Create PersistenceBroker instance pool, pool configuration was " + getPoolConfiguration());
076        }
077    
078        /**
079         * Return broker instance from pool. If given {@link PBKey} was not found in pool
080         * a new pool for given
081         * @param pbKey
082         * @return
083         * @throws PBFactoryException
084         */
085        public PersistenceBrokerInternal createPersistenceBroker(PBKey pbKey) throws PBFactoryException
086        {
087            if (log.isDebugEnabled()) log.debug("Obtain broker from pool, used PBKey is " + pbKey);
088            PersistenceBrokerInternal broker = null;
089    
090            /*
091            try to find a valid PBKey, if given key does not full match
092            */
093            pbKey = BrokerHelper.crossCheckPBKey(pbKey);
094    
095            try
096            {
097                /*
098                get a pooled PB instance, the pool is reponsible to create new
099                PB instances if not found in pool
100                */
101                broker = ((PersistenceBrokerInternal) brokerPool.borrowObject(pbKey));
102                /*
103                now warp pooled PB instance with a handle to avoid PB corruption
104                of closed PB instances.
105                */
106                broker = wrapRequestedBrokerInstance(broker);
107    
108            }
109            catch (Exception e)
110            {
111                try
112                {
113                    // if something going wrong, tryto close broker
114                    if(broker != null) broker.close();
115                }
116                catch (Exception ignore)
117                {
118                    //ignore it
119                }
120                throw new PBFactoryException("Borrow broker from pool failed, using PBKey " + pbKey, e);
121            }
122            return broker;
123        }
124    
125        /**
126         * Each real pooled {@link PersistenceBroker} instance was wrapped by a
127         * pooling handle when a new instance was created.
128         *
129         * @see PoolablePersistenceBroker
130         * @param broker real {@link PersistenceBroker} instance
131         * @param pool use {@link KeyedObjectPool}
132         * @return wrapped broker instance
133         */
134        protected PersistenceBrokerInternal wrapBrokerWithPoolingHandle(PersistenceBrokerInternal broker, KeyedObjectPool pool)
135        {
136            return new PoolablePersistenceBroker(broker, pool);
137        }
138    
139        /**
140         * Wraps the requested pooled broker instance. The returned handle
141         * warps a pooled broker instance to avoid corruption
142         * of already closed broker instances.
143         *
144         * @see PersistenceBrokerHandle
145         * @param broker
146         * @return The broker handle.
147         */
148        protected PersistenceBrokerInternal wrapRequestedBrokerInstance(PersistenceBrokerInternal broker)
149        {
150            return new PersistenceBrokerHandle(broker);
151        }
152    
153        /**
154         * @see PersistenceBrokerFactoryIF#releaseAllInstances()
155         */
156        public synchronized void releaseAllInstances()
157        {
158            log.warn("Release all instances referenced by this object");
159            super.releaseAllInstances();
160            try
161            {
162                brokerPool.close();
163                brokerPool = this.createPool();
164            }
165            catch (Exception e)
166            {
167                log.error("Error while release all pooled broker instances and refresh pool", e);
168            }
169        }
170    
171        public void shutdown()
172        {
173            try
174            {
175                brokerPool.close();
176                brokerPool = null;
177            }
178            catch(Exception e)
179            {
180                log.error("Error while shutdown of broker pool", e);
181            }
182            super.shutdown();
183        }
184    
185        public int activePersistenceBroker()
186        {
187            return brokerPool.getNumActive();
188        }
189    
190        /**
191         * could be used for monitoring
192         * TODO: is this useful?
193         */
194        public Properties getPoolConfiguration()
195        {
196            return poolConfig;
197        }
198    
199        /**
200         * could be used for runtime configuration
201         * TODO: is this useful?
202         */
203        public void setPoolConfiguration(Properties prop)
204        {
205            poolConfig = new PBPoolInfo(prop);
206            log.info("Change pooling configuration properties: " + poolConfig.getKeyedObjectPoolConfig());
207            brokerPool.setConfig(poolConfig.getKeyedObjectPoolConfig());
208        }
209    
210    
211        /**
212         * Create the {@link org.apache.commons.pool.KeyedObjectPool}, pooling
213         * the {@link PersistenceBroker} instances - override this method to
214         * implement your own pool and {@link org.apache.commons.pool.KeyedPoolableObjectFactory}.
215         */
216        private GenericKeyedObjectPool createPool()
217        {
218            GenericKeyedObjectPool.Config conf = poolConfig.getKeyedObjectPoolConfig();
219            if (log.isDebugEnabled())
220                log.debug("PersistenceBroker pool will be setup with the following configuration " +
221                        ToStringBuilder.reflectionToString(conf, ToStringStyle.MULTI_LINE_STYLE));
222            GenericKeyedObjectPool pool = new GenericKeyedObjectPool(null, conf);
223            pool.setFactory(new PersistenceBrokerFactoryDefaultImpl.PBKeyedPoolableObjectFactory(this, pool));
224            return pool;
225        }
226    
227    //**************************************************************************************
228    // Inner classes
229    //**************************************************************************************
230    //
231    
232        /**
233         * This is a {@link org.apache.commons.pool.KeyedPoolableObjectFactory} implementation,
234         * manage the life-cycle of {@link PersistenceBroker} instances
235         * hold in an {@link org.apache.commons.pool.KeyedObjectPool}.
236         *
237         * @author <a href="mailto:armin@codeAuLait.de">Armin Waibel</a>
238         */
239        class PBKeyedPoolableObjectFactory implements KeyedPoolableObjectFactory
240        {
241            private PersistenceBrokerFactoryDefaultImpl pbf;
242            private KeyedObjectPool pool;
243    
244            public PBKeyedPoolableObjectFactory(PersistenceBrokerFactoryDefaultImpl pbf, KeyedObjectPool pool)
245            {
246                this.pbf = pbf;
247                this.pool = pool;
248            }
249    
250            public Object makeObject(Object key) throws Exception
251            {
252                return wrapBrokerWithPoolingHandle(pbf.createNewBrokerInstance((PBKey) key), pool);
253            }
254    
255            /**
256             * Do all cleanup stuff here.
257             */
258            public void destroyObject(Object key, Object obj) throws Exception
259            {
260                PoolablePersistenceBroker pb = (PoolablePersistenceBroker) obj;
261                PersistenceBroker broker = pb.getInnermostDelegate();
262                if (broker instanceof PersistenceBrokerImpl)
263                {
264                    log.info("Destroy PersistenceBroker instance " + obj);
265                    ((PersistenceBrokerImpl) broker).destroy();
266                }
267                pb.destroy();
268            }
269    
270            /**
271             * Check if the given PersistenceBroker instance
272             * was already in transaction.
273             * Was called when
274             * {@link PBPoolInfo#init}
275             * method does set <code>testOnBorrow(true)</code>.
276             * (Default was false, thus this method wasn't called)
277             * See documentation jakarta-connons-pool api.
278             */
279            public boolean validateObject(Object key, Object obj)
280            {
281                // here we could validate the PB instance
282                // if corresponding configuration properties are set
283                if (((PersistenceBroker) obj).isInTransaction())
284                {
285                    log.error("Illegal broker state! This broker instance was already in transaction.");
286                    return false;
287                }
288                return true;
289            }
290    
291            /**
292             * Called before borrow object from pool.
293             */
294            public void activateObject(Object key, Object obj) throws Exception
295            {
296                ((PBState) obj).setClosed(false);
297            }
298    
299            /**
300             * Called before return object to pool.
301             */
302            public void passivateObject(Object key, Object obj) throws Exception
303            {
304                ((PBState) obj).setClosed(true);
305            }
306        }
307    }