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 }