From jdo-commits-return-506-apmail-db-jdo-commits-archive=www.apache.org@db.apache.org Sun May 22 18:02:13 2005 Return-Path: Delivered-To: apmail-db-jdo-commits-archive@www.apache.org Received: (qmail 43640 invoked from network); 22 May 2005 18:02:13 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199) by minotaur.apache.org with SMTP; 22 May 2005 18:02:13 -0000 Received: (qmail 9307 invoked by uid 500); 22 May 2005 18:02:12 -0000 Mailing-List: contact jdo-commits-help@db.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: jdo-dev@db.apache.org Delivered-To: mailing list jdo-commits@db.apache.org Received: (qmail 9167 invoked by uid 99); 22 May 2005 18:02:10 -0000 X-ASF-Spam-Status: No, hits=-9.8 required=10.0 tests=ALL_TRUSTED,NO_REAL_NAME X-Spam-Check-By: apache.org Received: from minotaur.apache.org (HELO minotaur.apache.org) (209.237.227.194) by apache.org (qpsmtpd/0.28) with SMTP; Sun, 22 May 2005 11:02:04 -0700 Received: (qmail 42733 invoked by uid 65534); 22 May 2005 18:02:01 -0000 Message-ID: <20050522180201.42731.qmail@minotaur.apache.org> Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Subject: svn commit: r171352 [2/11] - in /incubator/jdo/trunk/runtime20: ./ src/ src/conf/ src/java/ src/java/org/ src/java/org/apache/ src/java/org/apache/jdo/ src/java/org/apache/jdo/ejb/ src/java/org/apache/jdo/impl/ src/java/org/apache/jdo/impl/model/ src/java/org/apache/jdo/impl/model/java/ src/java/org/apache/jdo/impl/model/java/runtime/ src/java/org/apache/jdo/impl/model/jdo/ src/java/org/apache/jdo/impl/model/jdo/xml/ src/java/org/apache/jdo/impl/pm/ src/java/org/apache/jdo/impl/sco/ src/java/org/apache/jdo/impl/state/ src/java/org/apache/jdo/pm/ src/java/org/apache/jdo/query/ src/java/org/apache/jdo/sco/ src/java/org/apache/jdo/state/ src/java/org/apache/jdo/store/ Date: Sun, 22 May 2005 18:01:48 -0000 To: jdo-commits@db.apache.org From: mbo@apache.org X-Mailer: svnmailer-1.0.0-dev X-Virus-Checked: Checked X-Spam-Rating: minotaur.apache.org 1.6.2 0/1000/N Added: incubator/jdo/trunk/runtime20/src/java/org/apache/jdo/impl/pm/CacheM= anagerImpl.java URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/runtime20/src/java/o= rg/apache/jdo/impl/pm/CacheManagerImpl.java?rev=3D171352&view=3Dauto =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D --- incubator/jdo/trunk/runtime20/src/java/org/apache/jdo/impl/pm/CacheMana= gerImpl.java (added) +++ incubator/jdo/trunk/runtime20/src/java/org/apache/jdo/impl/pm/CacheMana= gerImpl.java Sun May 22 11:01:45 2005 @@ -0,0 +1,630 @@ +/* + * Copyright 2005 The Apache Software Foundation. + *=20 + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at=20 + *=20 + * http://www.apache.org/licenses/LICENSE-2.0 + *=20 + * Unless required by applicable law or agreed to in writing, software=20 + * distributed under the License is distributed on an "AS IS" BASIS,=20 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied= .=20 + * See the License for the specific language governing permissions and=20 + * limitations under the License. + */ + +/* + * CacheManagerImpl.java + * + * Created on December 1, 2000 + */ + +package org.apache.jdo.impl.pm; + +import java.util.*; +import java.lang.reflect.Field; +import java.lang.reflect.Constructor; +import java.lang.ref.WeakReference; + +import javax.jdo.*; +import javax.jdo.spi.*; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.apache.jdo.impl.state.StateManagerFactory; +import org.apache.jdo.pm.PersistenceManagerInternal; +import org.apache.jdo.state.StateManagerInternal; +import org.apache.jdo.store.StoreManager; +import org.apache.jdo.util.I18NHelper; +import org.apache.jdo.util.WeakValueHashMap; + +/* + * This is the cache manager that is responsible for operation of + * all types of caches (weak, transactional, flushed, transient) + * associated with the referenced instance of a PersistenceManager. + * + * @author Marina Vatkina + */ +class CacheManagerImpl { + + // Reference to the corresponding PersistenceManagerImpl. + PersistenceManagerImpl pm =3D null; + + /** + * Collection of Persistent instances created and/or updated + * in this Transaction + */ + private Collection _txCache =3D Collections.synchronizedSet(new HashSe= t()); + + /** + * Collection of Transient-transactional instances registered + * with this Transaction + */ + private Collection _transientCache =3D new Vector(); + + /** + * Collection of Persistent instances that will require state + * change at the transaction completion + */ + private ArrayList _flushedCache =3D new ArrayList(); + + /** + * Collection of StateManager instances that represent Persistent + * instances that had been made newly persistent in the current + * transaction. + */ + private ArrayList _newInstances =3D new ArrayList(); + + /**=20 + * Weak Hashtable of Persistent instances accessed by this Persistence= Manager + */ + private WeakValueHashMap _weakCache =3D new WeakValueHashMap(); + + /** + * Logger instance + */ + private static final Log logger =3D LogFactory.getFactory().getInstanc= e( + "org.apache.jdo.impl.pm"); // NOI18N + + /** + * I18N message handler + */ + private final static I18NHelper msg =3D=20 + I18NHelper.getInstance(CacheManagerImpl.class); + + /** + * Constructs new instnstance of CacheManagerImpl + *=20 + * @param pm calling instance of PersistenceManagerImpl + */ + CacheManagerImpl(PersistenceManagerImpl pm) { + this.pm =3D pm; + } + + /** + * close the CacheManagerImpl + */ + protected void close() { + // RELEASE THE CACHE... + // Nothing should be in _txCache and/or _flushedCache because + // PersistenceManager verified that transaction is not + // active. _transientCache can have transient transactional + // instances, but it is OK to clear them.=20 + _weakCache.clear(); + _txCache.clear(); + _flushedCache.clear(); + _transientCache.clear(); + } + + =20 + + /** This method locates a persistent instance in the cache of instances + * managed by this PersistenceManager. + * =20 + *

If the validate flag is true: This method verifies that there + * is an instance in the data store with the same oid, constructs an + * instance, and returns it. If there is no transaction active, then + * a hollow instance or persistent non-transactional instance is retur= ned. + * If there is a transaction active, then + * a persistent clean instance is returned. + *

If the validate flag is false: If there is not already an instan= ce + * in the cache with the same oid, then an instance is constructed and + * returned. If the instance does not exist + * in the data store, then this method will + * not fail. However, a request to access fields of the instance will + * throw an exception. + * @return the PersistenceCapable instance with the specified + * ObjectId + * @param oid an ObjectId + * @param validate if the existence of the instance is to be validated + */ + protected Object getObjectById (Object oid, boolean validate) { + if (debugging()) + debug ("getObjectById"); // NOI18N + + StateManagerInternal sm =3D this.getStateManager(oid, validate); + return ((StateManagerInternal)sm).getObject(); + } + + /** + * Returns StateManager instance associated with this instance of Obje= ctId + * Creates a Hollow instance of a PersistenceCapable object, if it can= not be=20 + * found in the cache + * @param oid an ObjectId + * @param pcClass Class of a Hollow instance to be created. + * @return the StateManagerInternal=20 + */ + protected StateManagerInternal getStateManager (Object oid, Class pcCl= ass) { + if (debugging()) + debug ("getStateManager " + oid + " for: " + pcClass.getName()= ); // NOI18N + + StateManagerInternal sm =3D null; + // Check weak cache to find SM: + synchronized (_weakCache) { + // Need to keep a reference to the value in the cache as it is= a weak + // cache and the value might be removed otherwise. + Object o =3D _weakCache.get(oid); + if (o =3D=3D null) { + // Nothing found + sm =3D createNewSM(null, oid, pcClass); + } else { + // Prepare Hollow instance if its class type was not + // known before.=20 + sm =3D (StateManagerInternal)o; + sm.setPCClass(pcClass); + } + } + + if (debugging()) + debug ("return from getStateManager: " + sm); // NOI18N + + return sm; + } + + /** + * The ObjectId returned by this method represents the JDO identity of + * the instance. The ObjectId is a copy (clone) of the internal state + * of the instance, and changing it does not affect the JDO identity of + * the instance. + * Delegates actual execution to the internal method. + * @param pc the PersistenceCapable instance + * @param transactional true if transactional Id is requested + * @return the ObjectId of the instance + */ + protected Object getExternalObjectId (PersistenceCapable pc, + boolean transactional) { + StateManagerInternal sm =3D pm.findStateManager(pc); + + Object oid =3D null; + if (_weakCache.containsValue(sm)) { + if (transactional) + oid =3D sm.getTransactionalObjectId(pc); + else + oid =3D sm.getExternalObjectId(); + } + + return oid; + } + + /** Make the transient instance persistent in this PersistenceManager. + * This method must be called in an active transaction. + * The PersistenceManager assigns an ObjectId to the instance and + * transitions it to persistent-new. + * The instance will be managed in the Extent associated with its Clas= s=2E + * The instance will be put into the data store at commit. + * @param pc a transient instance of a Class that implements + * PersistenceCapable + */ + protected void makePersistent (PersistenceCapable pc) { + + StateManagerInternal sm =3D pm.findStateManager(pc); + if (sm =3D=3D null) { + sm =3D StateManagerFactory.newInstance(pc, pm); + } + + sm.makePersistent(); + } + + /** Make the transient or persistent instance transactional in + * this PersistenceManager. + * @see javax.jdo.PersistenceManager#makeTransactional(Object pc) + */ + protected void makeTransactional(PersistenceCapable pc) { + StateManagerInternal sm =3D pm.findStateManager(pc); + if (sm =3D=3D null) { + sm =3D StateManagerFactory.newInstance(pc, pm); + } + sm.makeTransactional(); + } + + + /** Make the transient or persistent instance transactional in + * this PersistenceManager. + * @see javax.jdo.PersistenceManager#makeNontransactional(Object pc) + */ + protected void makeNontransactional(PersistenceCapable pc) { + StateManagerInternal sm =3D pm.findStateManager(pc); + if (sm =3D=3D null) { + throw new JDOUserException(msg.msg( + "EXC_NonTransactional")); // NOI18N + } + sm.makeNontransactional(); + } + + + /** Make the persistent instance transient in this PersistenceManager. + * @see javax.jdo.PersistenceManager#makeTransient(Object pc) + */ + protected void makeTransient(PersistenceCapable pc) { + StateManagerInternal sm =3D pm.findStateManager(pc); + if (sm !=3D null) { + sm.makeTransient(); + } + } + + /** Make persistent instance hollow in this PersistenceManager. + * @see javax.jdo.PersistenceManager#evict(Object pc) + */ + protected void evict(PersistenceCapable pc) { + StateManagerInternal sm =3D pm.findStateManager(pc); + if (sm !=3D null) { + sm.evictInstance(); + } + } + + /** Make all non-dirty persistent instances in the cache hollow in=20 + * this PersistenceManager. + * @see javax.jdo.PersistenceManager#evictAll() + */ + protected void evictAll() { + StateManagerInternal sm =3D null; + + Iterator it =3D _weakCache.entrySet().iterator(); + while (it.hasNext()) { + sm =3D (StateManagerInternal) ((Map.Entry)it.next()).getValue(= ); + sm.evictInstance(); + } + } + + /** Retrieve Hollow persistent instance in this PersistenceManager. + * @see javax.jdo.PersistenceManager#retrieve(Object pc) + */ + protected void retrieve(PersistenceCapable pc) { + StateManagerInternal sm =3D pm.findStateManager(pc); + if (sm !=3D null) { + sm.retrieve(); + } + } + + /** Refresh dirty persistent instance in this PersistenceManager. + * @see javax.jdo.PersistenceManager#refresh(Object pc) + */ + protected void refresh(PersistenceCapable pc) { + StateManagerInternal sm =3D pm.findStateManager(pc); + if (sm !=3D null) { + sm.refreshInstance(); + } + } + + /** Refresh dirty persistent instances in the transactional cache=20 + * of this PersistenceManager. Called in an active transaction. + * @see javax.jdo.PersistenceManager#refreshAll() + */ + protected void refreshAllTransactional() { + StateManagerInternal sm =3D null; + + Iterator it =3D _txCache.iterator(); + while(it.hasNext()) { + sm =3D (StateManagerInternal)it.next(); + sm.refreshInstance(); + } + } + + /** Refresh nontransactional instances in the weak cache + * of this PersistenceManager. Called outside an active transaction. + * @see javax.jdo.PersistenceManager#refreshAll() + */ + protected void refreshAllNontransactional() { + StateManagerInternal sm =3D null; + + Iterator it =3D _weakCache.entrySet().iterator(); + while (it.hasNext()) { + sm =3D (StateManagerInternal) ((Map.Entry)it.next()).getValue(= ); + sm.refreshInstance(); + } + } + + /** + * Register transient instance in the transient cache + */ + protected void registerTransient(StateManagerInternal sm) { + Iterator it =3D _transientCache.iterator(); + while(it.hasNext()) { + Object o =3D ((WeakReference)it.next()).get(); + if ((StateManagerInternal)o =3D=3D sm) { + // The same SM is found - nothing to do. + return; + } + } + _transientCache.add(new WeakReference(sm)); + } + + /** + * Register persistent instance in the transactional cache + */ + protected void register(StateManagerInternal sm, Object oid,=20 + boolean transactional, boolean throwDuplicateException) { + if (oid =3D=3D null) { + oid =3D sm.getInternalObjectId(); + } + + //register in both caches for transactional instances only + + if (! _weakCache.containsKey(oid)) { + deregisterTransient(sm); + _weakCache.put(oid, sm); =20 + + } else if (throwDuplicateException) { + throw new JDOUserException(msg.msg( + "EXC_ObjectExistsInCache")); // NOI18N + } + + if (pm.currentTransaction().isActive() && transactional) { + // Register in both caches for convenience. + if (! _flushedCache.contains(sm)) { + _flushedCache.add(sm);=20 + } + if (! _txCache.contains(sm)) { + _txCache.add(sm);=20 + if (sm.isNew())=20 + _newInstances.add(sm.getObject()); + } + } + + if (!transactional) { + // Remove from transactional caches if instance became + // nontransactional + _txCache.remove(sm);=20 + _flushedCache.remove(sm); + } + } + + /** + * Remove transient instance from the transient cache + */ + protected void deregisterTransient(Object sm) { + Iterator it =3D _transientCache.iterator(); + while(it.hasNext()) { + WeakReference wr =3D (WeakReference)it.next(); + if ((StateManagerInternal)wr.get() =3D=3D sm) { + _transientCache.remove(wr); + break; + } + } + } + + /** + * Remove persistent instance from all caches + */ + protected void deregister(Object oid) { + if (oid !=3D null) { + //deregister the instance from all the caches + Object o =3D _weakCache.remove(oid); + + // No need to do anything outside an active transaction. + if (pm.currentTransaction().isActive()) { + _txCache.remove(o); + _flushedCache.remove(o); + } + } + } + + /** =20 + * @see PersistenceManagerInternal#replaceObjectId(Object oldId, + * Object newId) + */ =20 + protected void replaceObjectId(Object oldId, Object newId) { + if (debugging()) + debug ("replaceObjectId"); // NOI18N + + synchronized(_weakCache) { + if (_weakCache.containsKey(newId)) { + throw new JDOFatalInternalException(msg.msg( + "EXC_ObjectIdExistsInCache", newId)); // NOI18N + } + Object o =3D _weakCache.remove(oldId); + if (o =3D=3D null) { + throw new JDOFatalInternalException(msg.msg( + "EXC_ObjectIdNotExistsInCache", newId)); // NOI18N + } + _weakCache.put(newId, o); + } + } + + /** =20 + * @see PersistenceManagerInternal#markAsFlushed(StateManagerInternal = sm) + */ =20 + protected void markAsFlushed(StateManagerInternal sm) {=20 + _txCache.remove(sm); + } + + /** + * Called by Transaction#commit(), Transaction#beforeCompletion(), or + * Transaction#internalFlush(). + * Processes instances for the reachability algorithm, then calls + * StoreManager to iterate over transactional cache and to call flush(= )=20 + * for each StateManager in it. + */ + protected void flushInstances() { + StateManagerInternal sm =3D null; + + Object[] e =3D _txCache.toArray(); + boolean commit =3D pm.insideCommit(); + + for (int i =3D 0; i < e.length; i++) { + sm =3D (StateManagerInternal)e[i]; + + // + // NOTE: handleRelationships has the side-effect of adding + // more objects to the transaction cache. + // + sm.handleReachability(commit); + } + + StoreManager srm =3D pm.getStoreManager(); + Iterator it =3D _txCache.iterator(); + + srm.flush(it, pm); + + _txCache.clear(); + } + + /** + * Called by Transaction commit() or rollback() + * cleans up transactional cache + * @param abort=20 + */ + protected void afterCompletion(boolean abort) { + boolean retainValues =3D pm.currentTransaction().getRetainValues(); + boolean restoreValues =3D pm.currentTransaction().getRestoreValues= (); + + // Need to process transient instances also + Iterator it =3D _transientCache.iterator(); + while(it.hasNext()) { + Object o =3D ((WeakReference)it.next()).get(); + + if (o =3D=3D null) {=20 + // It has been GC'd and should be removed from _transientC= ache.=20 + it.remove();=20 + } else { + _flushedCache.add(o); + } + } + + int len =3D _flushedCache.size(); + for ( int i =3D 0; i < len; i++) { + StateManagerInternal sm =3D (StateManagerInternal)_flushedCach= e=2Eget(i); + sm.afterCompletion(abort, retainValues, restoreValues); + } + + // Now clean the flushed cache + _flushedCache.clear(); + _newInstances.clear(); + + // Just in case beforeCompletion failed or it was a rollback + _txCache.clear(); + } + + /** + * Returns a Collection of instances that has been made persistent + * or become persistent through persistence-by-reachability + * algorithm in this transaction. Called by the Extent.iterator. + * @see PersistenceManagerInternal#getInsertedInstances + * @return Collection of Persistent-New instances. + */ + protected Collection getInsertedInstances() { + if (debugging()) + debug("getInsertedInstances"); // NOI18N + + return _newInstances; + } + + /** --------------Private Methods-------------- */ + + /** + * Returns StateManager instance associated with this instance of Obje= ctId + * @see #getObjectById(Object oid, boolean validate) + * @param oid an ObjectId + * @param validate if the existence of the instance is to be validated + */ + private StateManagerInternal getStateManager (Object oid, boolean vali= date) { + + Object o =3D null; + StoreManager srm =3D pm.getStoreManager(); + Class candidateClassType =3D srm.getPCClassForOid(oid, pm); + if (candidateClassType =3D=3D null) { + // not found, report an error + throw new JDOUserException(msg.msg( + "EXC_NotOID"),// NOI18N + oid); + } + + Object internalOid =3D srm.getInternalObjectId(oid, pm); + if (debugging()) + debug ("getStateManager internal oid: " + internalOid); // NOI= 18N + + StateManagerInternal sm =3D null; + + // Check weak cache to find SM: + synchronized (_weakCache) { + if((o =3D _weakCache.get(internalOid)) =3D=3D null) { + // Nothing found + if (debugging()) + debug ("getStateManager oid not found."); // NOI18N + + sm =3D createNewSM(oid, internalOid, candidateClassType); + // Always reload from the DB to resolve actual classType + if (validate || !srm.hasActualPCClass(internalOid)) + sm.reload(); + return sm; + + } else if (validate && !_flushedCache.contains(o)) { + // Found but NOT in the transactional cache. Reload. + if (debugging()) + debug ("getStateManager oid found - reload."); // NOI1= 8N + + sm =3D (StateManagerInternal)o; + sm.reload(); + return sm; + } + } + return (StateManagerInternal)o; + } + + /** + * Creates new StateManager instance associated with this instance + * of ObjectId. + * @see #getObjectById(Object oid, boolean validate) + * @param UserOid a user provided ObjectId + * @param internalOid an internal ObjectId + * @param candidateClassType super class of a Hollow instance to be cr= eated. + */ + private StateManagerInternal createNewSM(Object UserOid, Object intern= alOid, + Class candidateClass= Type) { + try { + return StateManagerFactory.newInstance(UserOid, internalOid,=20 + pm, candidateClassType); + + } catch (JDOUserException e) { + throw e; + // XXX Possible jikes bug + // + // The following catch phrase causes jikes to complain (Cautio= n: + // This try block cannot throw a "checked exception" (JLS sect= ion + // 14.7) that can be caught here. You may have intended to cat= ch + // a RuntimeException instead of an Exception.) But this try + // block is *not* here throwing any checked exceptions! That's + // why I think it's a jikes bug (Sun's javac does not complain= .) + } catch (Exception e) { + throw new JDOUserException(msg.msg("EXC_NotOID"), e, UserOid);= // NOI18N + } + } + + /** + * Tracing method + * @param msg String to display + */ =20 + private void debug(String msg) { + logger.debug("In CacheManagerImpl " + msg); // NOI18N + } + /** + * Verifies if debugging is enabled. + * @return true if debugging is enabled. + */ + private boolean debugging() { + return logger.isDebugEnabled(); + } + +} Added: incubator/jdo/trunk/runtime20/src/java/org/apache/jdo/impl/pm/Persis= tenceManagerFactoryImpl.java URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/runtime20/src/java/o= rg/apache/jdo/impl/pm/PersistenceManagerFactoryImpl.java?rev=3D171352&view= =3Dauto =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D --- incubator/jdo/trunk/runtime20/src/java/org/apache/jdo/impl/pm/Persisten= ceManagerFactoryImpl.java (added) +++ incubator/jdo/trunk/runtime20/src/java/org/apache/jdo/impl/pm/Persisten= ceManagerFactoryImpl.java Sun May 22 11:01:45 2005 @@ -0,0 +1,1677 @@ +/* + * Copyright 2005 The Apache Software Foundation. + *=20 + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at=20 + *=20 + * http://www.apache.org/licenses/LICENSE-2.0 + *=20 + * Unless required by applicable law or agreed to in writing, software=20 + * distributed under the License is distributed on an "AS IS" BASIS,=20 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied= .=20 + * See the License for the specific language governing permissions and=20 + * limitations under the License. + */ + +/* + * PersistenceManagerFactoryImpl.java + * + * Created on December 1, 2000 + */ +=20 +package org.apache.jdo.impl.pm; + +import java.io.IOException; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; + +import javax.jdo.JDOException; +import javax.jdo.JDOFatalInternalException; +import javax.jdo.JDOFatalUserException; +import javax.jdo.JDOUserException; +import javax.jdo.PersistenceManager; +import javax.jdo.Transaction; +import javax.jdo.spi.JDOPermission; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.jdo.ejb.EJBImplHelper; +import org.apache.jdo.impl.model.java.runtime.RuntimeJavaModelFactory; +import org.apache.jdo.pm.Accessor; +import org.apache.jdo.pm.PersistenceManagerFactoryInternal; +import org.apache.jdo.util.I18NHelper; +import org.apache.jdo.util.JDORIVersion; + +/**=20 + * This is an abstract PersistenceManagerFactoryImpl class that provides t= he=20 + * StoreManager independent implementation of javax.jdo.PersistenceManager + * interface.=20 + *

+ * Subclasses must override the following methods declared abstract: + *

    + *
  • {@link #getOptionArray()} + *
  • {@link #createPersistenceManager(String userid, String password)} + *
  • {@link #setPMFClassProperty (Properties props)} + *
  • {@link #encrypt(String s)} + *
  • {@link #decrypt(String s)} + *
  • {@link #setCFProperties(Properties p)} + *
  • {@link #getCFFromProperties(Properties p)} + *
  • {@link #isConnectionFactoryConfigured()} + *
  • and all methods from org.apache.jdo.pm.PersistenceManagerFactoryIn= ternal. + *
=20 + * + * @author Marina Vatkina + * @version 0.1 + */ + +abstract public class PersistenceManagerFactoryImpl implements=20 + PersistenceManagerFactoryInternal { + + // + // PersistenceManagerFactory properties + // + private String URL =3D null; + private String userName =3D null; + protected String password =3D null; + private String driverName =3D null; + + private Object connectionFactory =3D null; + private String connectionFactoryName =3D null; + + private Object connectionFactory2 =3D null; + private String connectionFactory2Name =3D null; + + private boolean multithreaded =3D false; + + private boolean optimistic =3D true; + private boolean retainValues =3D true; + private boolean restoreValues =3D true; + private boolean nontransactionalRead =3D true; + private boolean nontransactionalWrite =3D false; + private boolean ignoreCache =3D true; + =20 + private int queryTimeout =3D 0; + private int updateTimeout =3D 0; + + private int minPool =3D 1; + private int maxPool =3D 1; + private int msWait =3D 0; + + /** Cached hashCode for this PMF. Changes every time a property of th= is + * PMF is changed to a non-default value. Fixed after setConfigured() + * (mostly). + * @see #setConfigured() + * @see #setNonconfigured() + */ + private int myHashCode; + + // + // Once false, attempts to change properties above will fail (see + // assertConfigurable). + // + private boolean configurable =3D true; + =20 + + // + // The PMF is serialized in one of 3 forms, depending on how it is + // configured. + // + private static final int PERSIST_CF =3D 1; + private static final int PERSIST_CF_NAME =3D 2; + private static final int PERSIST_PROPS =3D 3; + + /** These are used for implementing close(). + */ + protected boolean closed =3D false; + =20 + /** The closeLock protects the close flag and pmSet. + */ + protected Object closeLock =3D new Object(); + =20 + /** The set of all PersistenceManagers that are not closed. In order + * for this to work, it is important that PersistenceManager implement + * equals to be equivalent to Object.equals. + */ + protected Set pmSet =3D new HashSet(); + =20 + /** + * Logger instance + */ + private static final Log logger =3D LogFactory.getFactory().getInstanc= e( + "org.apache.jdo.impl.pm"); // NOI18N + + /** + * I18N message handler + */ + private final static I18NHelper msg =3D=20 + I18NHelper.getInstance("org.apache.jdo.impl.pm.Bundle"); // NOI18N + + /** + * Transactional cache of PersistenceManager instances + */ + private Hashtable pmCache =3D new Hashtable(); + + /** RuntimeJavaModelFactory. */ + private static final RuntimeJavaModelFactory javaModelFactory =3D + (RuntimeJavaModelFactory) AccessController.doPrivileged( + new PrivilegedAction () { + public Object run () { + return RuntimeJavaModelFactory.getInstance(); + } + } + ); +=20 + /** Collection of registered pmf instances. */ + private static Collection registeredPMFs =3D new HashSet(); + =20 + /** Adds a JVM shutdown hook to close pmf instances left open by the + * user.=20 + */ + static { + AccessController.doPrivileged(new PrivilegedAction () { + public Object run () { + try { + Runtime.getRuntime().addShutdownHook(new ShutdownHook(= )); + return null; + } + catch (SecurityException ex) { + throw new JDOFatalUserException(msg.msg( + "EXC_CannotAddShutdownHook"), ex); // NOI18N + } + }}); + } + + /** + * Creates new PersistenceManagerFactoryImpl without + * any user info. + */ + public PersistenceManagerFactoryImpl() { } + + /** + * Creates new PersistenceManagerFactoryImpl with user in= fo + * @param URL URL for the data store connection + * @param userName user name for the data store connection=20 + * @param password password for the data store connection + * @param driverName driver name for the data store connection + */ + public PersistenceManagerFactoryImpl( + String URL,=20 + String userName,=20 + String password,=20 + String driverName) { + this.URL =3D URL; + this.userName =3D userName; + this.password =3D password; + this.driverName =3D driverName; + =20 + } + =20 + /**=20 + * Set the user name for the data store connection. + * @param userName the user name for the data store connection. + */ + public void setConnectionUserName (String userName) { + assertConfigurable(); + this.userName =3D userName; + } + =20 + /** + * Get the user name for the data store connection. + * @return the user name for the data store connection. + */ + public String getConnectionUserName() { + return userName; + } + =20 + /** + * Set the password for the data store connection. + * @param password the password for the data store connection. + */ + public void setConnectionPassword (String password) { + assertConfigurable(); + this.password =3D password; + } + =20 + /** + * Get the password for the data store connection. Protected so=20 + * not just anybody can get the password. + * @return password the password for the data store connection. + */ + protected String getConnectionPassword () { + return this.password; + } + =20 + /** + * Set the URL for the data store connection. + * @param URL the URL for the data store connection. + */ + public void setConnectionURL (String URL) { + assertConfigurable(); + this.URL =3D URL; + } + =20 + /** + * Get the URL for the data store connection. + * @return the URL for the data store connection. + */ + public String getConnectionURL() { + return URL; + } + + /** + * Set the driver name for the data store connection. + * @param driverName the driver name for the data store connection. + */ + public void setConnectionDriverName (String driverName) { + assertConfigurable(); + this.driverName =3D driverName; + } + =20 + /** + * Get the driver name for the data store connection. + * @return the driver name for the data store connection. + */ + public String getConnectionDriverName() { + return driverName; + } + + /** + * Set the name for the data store connection factory. + * @param connectionFactoryName the name of the data store + * connection factory. + */ + public void setConnectionFactoryName (String connectionFactoryName) { + assertConfigurable(); + this.connectionFactoryName =3D connectionFactoryName; + } + =20 + /** + * Get the name for the data store connection factory. + * @return the name of the data store connection factory. + */ + public String getConnectionFactoryName () { + return connectionFactoryName; + } + + /** + * Set the data store connection factory. JDO implementations + * will support specific connection factories. The connection + * factory interfaces are not part of the JDO specification. + * @param connectionFactory the data store connection factory. + */ + public void setConnectionFactory (Object connectionFactory) { + assertConfigurable(); + this.connectionFactory =3D connectionFactory; + } + =20 + /** + * Get the data store connection factory. + * @return the data store connection factory. + */ + public Object getConnectionFactory() { + return connectionFactory; + } + =20 + /** Set the name of the connection factory for non-transactional conne= ctions. + * @see javax.jdo.PersistenceManagerFactory#setConnectionFactory2Name + * @param connectionFactoryName the name of the connection factory + * for non-transactional connections. + */ + public void setConnectionFactory2Name(String connectionFactoryName) = { + assertConfigurable(); + this.connectionFactory2Name =3D connectionFactory2Name; + } + =20 + /** Get the name of the connection factory for non-transactional conne= ctions. + * @see javax.jdo.PersistenceManagerFactory#getConnectionFactory2Name + * @return the name of the connection factory for=20 + * non-transactional connections. + */ + public String getConnectionFactory2Name() { + return connectionFactory2Name; + } + + /** Set the non-transactional connection factory + * for optimistic transactions. + * @see javax.jdo.PersistenceManagerFactory#setConnectionFactory2 + * @param connectionFactory the non-transactional connection factory. + */ + public void setConnectionFactory2(Object connectionFactory) { + assertConfigurable(); + this.connectionFactory2 =3D connectionFactory2; + } + =20 + /** Return the non-transactional connection factory + * for optimistic transactions. + * @see javax.jdo.PersistenceManagerFactory#getConnectionFactory2 + * @return the non-transactional connection factory for optimistic + * transactions + */ + public Object getConnectionFactory2() { + return connectionFactory2; + } + =20 + /** Set the default Multithreaded setting for all + * PersistenceManager instances obtained from this factory. + * + * @param flag the default Multithreaded setting. + */ + public void setMultithreaded (boolean flag) { + assertConfigurable(); + multithreaded =3D flag; + } + + /** Get the default Multithreaded setting for all + * PersistenceManager instances obtained from this factory. =20 + * + * @return the default Multithreaded setting. + */ + public boolean getMultithreaded() { + return multithreaded; + } + + /** + * Set the default Optimistic setting for all PersistenceManager insta= nces + * obtained from this factory. Setting Optimistic to true also sets + * NontransactionalRead to true. + * @param flag the default Optimistic setting. + */ + public void setOptimistic (boolean flag) { + assertConfigurable(); + optimistic =3D flag; + } + + /** + * Get the default Optimistic setting for all PersistenceManager insta= nces + * obtained from this factory. + * @return the default Optimistic setting. + */ + public boolean getOptimistic () { + return optimistic; + } + + + /** + * Set the default RetainValues setting for all PersistenceManager ins= tances + * obtained from this factory. Setting RetainValues to true also sets + * NontransactionalRead to true. + * @param flag the default RetainValues setting. + */ + public void setRetainValues (boolean flag) { + assertConfigurable(); + retainValues =3D flag; =20 + } + + /** + * Get the default RetainValues setting for all PersistenceManager ins= tances + * obtained from this factory. + * @return the default RetainValues setting. + */ + public boolean getRetainValues () { + return retainValues; + } + + /** + * Set the default RestoreValues setting for all PersistenceManager in= stances + * obtained from this factory. Setting RestoreValues to true also sets + * NontransactionalRead to true. + * @param flag the default RestoreValues setting. + */ + public void setRestoreValues (boolean flag) { + assertConfigurable(); + restoreValues =3D flag; =20 + } + + /** + * Get the default RestoreValues setting for all PersistenceManager in= stances + * obtained from this factory. + * @return the default RestoreValues setting. + */ + public boolean getRestoreValues () { + return restoreValues; + } + + + /** + * Set the default NontransactionalRead setting for all + * PersistenceManager instances obtained from this factory. + * @param flag the default NontransactionalRead setting. + */ =20 + public void setNontransactionalRead (boolean flag) { + assertConfigurable(); + nontransactionalRead =3D flag;=20 + } + + /** + * Get the default NontransactionalRead setting for all + * PersistenceManager instances obtained from this factory. + * @return the default NontransactionalRead setting. + */ =20 + public boolean getNontransactionalRead () { + return nontransactionalRead; + } + + /** + * Set the default NontransactionalWrite setting for all + * PersistenceManager instances obtained from this factory. + * @param flag the default NontransactionalWrite setting. + */ =20 + public void setNontransactionalWrite (boolean flag) { + assertConfigurable(); + nontransactionalWrite =3D flag;=20 + } + + /** + * Get the default NontransactionalWrite setting for all + * PersistenceManager instances obtained from this factory. + * @return the default NontransactionalWrite setting. + */ =20 + public boolean getNontransactionalWrite () { + return nontransactionalWrite; + } + + + /** + * Set the default IgnoreCache setting for all PersistenceManager inst= ances + * obtained from this factory. + * @param flag the default IgnoreCache setting. + */ + public void setIgnoreCache (boolean flag) { + assertConfigurable(); + ignoreCache =3D flag; + } + + /** + * Get the default IgnoreCache setting for all PersistenceManager inst= ances + * obtained from this factory. + * @return the default IngoreCache setting. + */ + public boolean getIgnoreCache () { + return ignoreCache; + } + + /** Set the default MsWait setting for all PersistenceManager instances + * obtained from this factory. + * @param msWait the default MsWait setting. + */ + public void setMsWait(int msWait) { + assertConfigurable(); + this.msWait =3D msWait; + } + =20 + /** Get the default MsWait setting for all PersistenceManager instances + * obtained from this factory. + * @return the default MsWait setting. + */ + public int getMsWait() { + return msWait; + } + =20 + /** Set the default MinPool setting for all PersistenceManager instanc= es + * obtained from this factory. + * @param minPool the default MinPool setting. + */ + public void setMinPool(int minPool) { + assertConfigurable(); + this.minPool =3D minPool; + } + =20 + /** Get the default MinPool setting for all PersistenceManager instanc= es + * obtained from this factory. + * @return the default MinPool setting. + */ + public int getMinPool() { + return minPool; + } + =20 + /** Set the default MaxPool setting for all PersistenceManager instanc= es + * obtained from this factory. + * @param maxPool the default MaxPool setting. + */ + public void setMaxPool(int maxPool) { + assertConfigurable(); + this.maxPool =3D maxPool; + } + =20 + /** Get the default MaxPool setting for all PersistenceManager instanc= es + * obtained from this factory. + * @return the default MaxPool setting. + */ + public int getMaxPool() { + return maxPool; + } + =20 + /** Set the default QueryTimeout setting for all PersistenceManager in= stances + * obtained from this factory. + * @param queryTimeout the default QueryTimeout setting. + */ + public void setQueryTimeout(int queryTimeout) { + assertConfigurable(); + this.queryTimeout =3D queryTimeout; + } + =20 + /** Get the default QueryTimeout setting for all PersistenceManager in= stances + * obtained from this factory. + * @return the default QueryTimeout setting. + */ + public int getQueryTimeout() { + return queryTimeout; + } + =20 + /** Set the default UpdateTimeout setting for all + * PersistenceManager instances obtained from this factory. + * @param updateTimeout the default UpdateTimeout setting. + */ + public void setUpdateTimeout(int updateTimeout) { + assertConfigurable(); + this.updateTimeout =3D updateTimeout; + } + =20 + /** Get the default UpdateTimeout setting for all PersistenceManager i= nstances + * obtained from this factory. + * @return the default UpdateTimeout setting. + */ + public int getUpdateTimeout() { + return updateTimeout; + } + =20 + =20 + /** + * Return "static" properties of this PersistenceManagerFactory. + * Properties with keys VendorName and VersionNumber are required. Ot= her + * keys are optional. + * @return the non-operational properties of this PersistenceManagerFa= ctory. + */ + public Properties getProperties () { + return JDORIVersion.getVendorProperties(); + } + + /** The application can determine from the results of this + * method which optional features are supported by the + * JDO implementation. + *

Each supported JDO optional feature is represented by a + * String with one of the following values: + * + *

javax.jdo.option.TransientTransactional + *

javax.jdo.option.NontransactionalRead + *

javax.jdo.option.NontransactionalWrite + *

javax.jdo.option.RetainValues + *

javax.jdo.option.Optimistic + *

javax.jdo.option.ApplicationIdentity + *

javax.jdo.option.DatastoreIdentity + *

javax.jdo.option.NonDatastoreIdentity + *

javax.jdo.option.ArrayList + *

javax.jdo.option.HashMap + *

javax.jdo.option.Hashtable + *

javax.jdo.option.LinkedList + *

javax.jdo.option.TreeMap + *

javax.jdo.option.TreeSet + *

javax.jdo.option.Vector + *

javax.jdo.option.Map + *

javax.jdo.option.List + *

javax.jdo.option.Array =20 + *

javax.jdo.option.NullCollection =20 + * + *

The standard JDO query language is represented by a String: + *

javax.jdo.query.JDOQL =20 + * @return the Set of String representing the supported Options + */ =20 + public Collection supportedOptions() { + return Collections.unmodifiableList(Arrays.asList(getOptionArray()= )); + } + + /** + * Returns an array of Strings indicating which options are supported = by + * this PersistenceManagerFactory. + * @return the option array. + */ + abstract protected String[] getOptionArray(); + + /** Creates a new instance of PersistenceManager from this factory. + * Called by getPersistenceManager(String userid, String password)) + * if there is no pooled instance that satisfies the request. + * + * @return a PersistenceManager instance with default options. + * @param userid The user id of the connection factory. + * @param password The password of the connection factory. + */ + protected abstract PersistenceManager createPersistenceManager( + String userid, String password); + + /** Get an instance of PersistenceManager from this factory. The + * instance has default values for options. + * + *

If pooling of PersistenceManager instances is supported by + * this factory, the instance might have been returned to the pool + * and is being reused.=20 + * + *

After the first use of getPersistenceManager, no "set" methods w= ill + * succeed. + * + * @return a PersistenceManager instance with default options. + */ + public PersistenceManager getPersistenceManager() { + return getPersistenceManager(null, null); + } + =20 + /** Get an instance of PersistenceManager from this factory. The + * instance has default values for options. The parameters userid + * and password are used when obtaining datastore connections from + * the connection pool. + * + *

If pooling of PersistenceManager instances is supported by + * this factory, the instance might have been returned to the pool + * and is being reused. + * + *

After the first use of getPersistenceManager, no "set" + * methods will succeed. + * + * @return a PersistenceManager instance with default options. + * @param userid The user id of the connection factory. + * @param password The password of the connection factory. + */ + public PersistenceManager getPersistenceManager( + String userid, String password){ + + if (debugging()) + debug("getPersistenceManager"); // NOI18N + + if (configurable) { + verifyConfiguration(); + } + + // Remember if it was configurable. We will need to restore it if + // it was and createPersistenceManager failed. + boolean wasConfigurable =3D configurable; + + try { + if (wasConfigurable) { + =20 + // if successful, the state of this PMF becomes configured + setConfigured(); + + // Replace this PersistenceManagerFactory with the one + // known to the appserver, if it is the first request + // to getPersistenceManager in this instance of the + // PersistenceManagerFactory.=20 + // This is a no-op in a non-managed environment, and + // if an appserver does not need any extra code here. + PersistenceManagerFactoryImpl pmf =3D=20 + (PersistenceManagerFactoryImpl)EJBImplHelper. + replacePersistenceManagerFactory(this); + + if (pmf !=3D this) { + // Was replaced. Mark this PersistenceManagerFactory a= s=20 + // configurable. + setNonconfigured(); + } + else { + // register this PMF + registeredPMFs.add(pmf); + } + =20 + return pmf.getPersistenceManagerInternal(userid, password); + }=20 + // This PersistenceManagerFactory has been already configured. + return getPersistenceManagerInternal(userid, password); + + } catch (javax.jdo.JDOException e) { + if (wasConfigurable) { + setNonconfigured(); + } + throw e; + } + } + + /** + * Returns PersistenceManager instance with default options. + * @see #getPersistenceManager(String userid, String password) + */ + private PersistenceManager getPersistenceManagerInternal( + String userid, String password){ + + if (debugging()) + debug("getPersistenceManagerInternal"); // NOI18N + + // Check if we are in managed environment and + // PersistenceManager is cached + PersistenceManagerImpl pm =3D null; + javax.transaction.Transaction t =3D EJBImplHelper.getTransaction(); +=20 + if (t !=3D null) { + pm =3D (PersistenceManagerImpl)pmCache.get(t); + if (pm =3D=3D null) { + // Not found + synchronized(pmCache) { + pm =3D (PersistenceManagerImpl)pmCache.get(t); + if (pm =3D=3D null) { + pm =3D getFromPool(userid, password); + pmCache.put(t, pm); + pm.setJTATransaction(t); + } + + // We know we are in the managed environment and + // JTA transaction is active. We need to start + // JDO Transaction internally if it is not active. + =20 + Transaction tx =3D pm.currentTransaction(); + if (!tx.isActive()) { + ((TransactionImpl)tx).begin(t); + } + } + } =20 + if (!(pm.verify(userid, password))) { + throw new JDOUserException(msg.msg( + "EXC_WrongUsernamePassword")); //NOI18N + } + } else { + // We don't know if we are in the managed environment or not + // If Yes, it is BMT with JDO Transaction and it will register + // itself when user calls begin(). + pm =3D getFromPool(userid, password); + } + + // Always return a wrapper + return new PersistenceManagerWrapper(pm); + } + + + /** + * Registers PersistenceManager in the transactional cache in + * managed environment in case of BMT with JDO Transaction. + * There is no javax.transaction.Transaction + * available before the user starts the transaction. + * @param pm the PersistenceManager + * @param t the Transaction used as the hashmap key + */ + protected void registerPersistenceManager( + PersistenceManagerImpl pm, + Object t) { + + if (debugging()) + debug("registerPersistenceManager"); // NOI18N + + PersistenceManagerImpl pm1 =3D (PersistenceManagerImpl)pmCache.get= (t); + if (pm1 =3D=3D null) { + synchronized (pmCache) { + pm1 =3D (PersistenceManagerImpl)pmCache.get(t); + if (pm1 =3D=3D null) { + pmCache.put(t, pm); + pm.setJTATransaction(t); + return; + } + } + } + + if (pm1 !=3D pm){ + throw new JDOFatalInternalException(msg.msg( + "EXC_WrongJTATransaction")); //NOI18N + + } else { + // do nothing ??? + } + } + + /** Deregisters PersistenceManager that is not associated with + * a JTA transaction any more. + * @param pm the PersistenceManager + * @param t the Transaction used as the hashmap key + */ + protected void deregisterPersistenceManager(PersistenceManagerImpl pm, + Object t) { + if (debugging()) + debug("deregisterPersistenceManager"); // NOI18N + + if (t !=3D null) { // Managed environment + // Deregister=20 + PersistenceManagerImpl pm1 =3D (PersistenceManagerImpl)pmCache= .get(t); + if (pm1 =3D=3D null || pm1 !=3D pm) { + throw new JDOFatalInternalException(msg.msg( + "EXC_WrongJTATransaction")); //NOI18N + } else { + pmCache.remove(t); + } + } + } + + /** Releases closed PersistenceManager that is not in use + * @param pm the PersistenceManager + * @param t the Transaction used as the hashmap key + */ + protected void releasePersistenceManager(PersistenceManagerImpl pm, + Object t) { + if (debugging()) + debug("releasePersistenceManager"); // NOI18N + + deregisterPersistenceManager(pm, t); + releaseStoreManager(pm); + returnToPool(pm); + } + + // + // Internal methods + // + =20 + /** + * Finds PersistenceManager for this combination of userid and password + * in the free pool, or creates new one if not found. + */ + private synchronized PersistenceManagerImpl getFromPool( + String userid, String password) { + + if (debugging()) + debug("getFromPool"); // NOI18N + + // We do not have pooling yet... + + // create new PersistenceManager object and set its atributes + PersistenceManagerImpl pm =3D=20 + (PersistenceManagerImpl)createPersistenceManager(userid, password); + synchronized(closeLock) { + if (closed) { + throw new JDOUserException( + msg.msg("EXC_PersistenceManagerFactoryClosed")); // NO= I18N + } + pmSet.add(pm); + } + + return pm; + } + + /** + * Returns unused PersistenceManager to the free pool + */ + private void returnToPool(PersistenceManagerImpl pm) { + if (debugging()) + debug("returnToPool"); // NOI18N + + // do nothing for now except remove from set of PersistenceManager= s=2E + synchronized(closeLock) { + pmSet.remove(pm); + } + } + + /** + * Asserts that change to the property is allowed + */ + private void assertConfigurable() { + synchronized(closeLock) { + if (!configurable) { + throw new JDOUserException (msg.msg("EXC_NotConfigurable")= ); // NOI18N + } + } + } + + /** + * Tracing method + * @param msg String to display + */ + private void debug(String msg) { + logger.debug("In PersistenceManagerFactoryImpl " + msg); //NOI18N + } + + /** + * Verifies if debugging is enabled. + * @return true if debugging is enabled. + */ + private boolean debugging() { + return logger.isDebugEnabled(); + } + + + // + // Explicit {read, write}Object support for java.io.Serializable so th= at + // we can en/de-crypt the password + // + + // The PMF is serialized in one of 3 forms, depending on how it is + // configured. + private int getSerializedForm() { + int rc =3D 0; + if (null !=3D connectionFactory) { + rc =3D PERSIST_CF; + } else if (null !=3D connectionFactoryName) { + rc =3D PERSIST_CF_NAME; + } else { + rc =3D PERSIST_PROPS; + } + return rc; + } + + /**The PMF is serialized in one of 3 forms, depending on how it is + * configured. This method examines a properties instance to determine + * which form it is. + */ + private int getSerializedForm(Properties props) { + int rc =3D 0; + if (null =3D=3D props.get("javax.jdo.option.ConnectionURL")) { // = NOI18N + rc =3D PERSIST_CF; + } else if (null !=3D props.get("javax.jdo.option.ConnectionFactory= Name")) { // NOI18N + rc =3D PERSIST_CF_NAME; + } else { + rc =3D PERSIST_PROPS; + } + return rc; + } + + /** + * Write this object to a stream. This method is provided so it + * can be called from outside the class (explicitly by a subclass). + * @param oos the ObjectOutputStream + * @throws IOException on errors writing to the stream + */ =20 + protected void doWriteObject(java.io.ObjectOutputStream oos) + throws java.io.IOException { + + writeObject(oos); + } + =20 + private void writeObject(java.io.ObjectOutputStream oos) + throws java.io.IOException { + int kind =3D getSerializedForm(); + oos.writeInt(kind); + + switch(kind) { + case PERSIST_CF: + oos.writeObject(connectionFactory); + break; + + case PERSIST_CF_NAME: + oos.writeUTF(connectionFactoryName); + oos.writeUTF(connectionFactory2Name); + break; + + case PERSIST_PROPS: + oos.writeObject(URL); + oos.writeObject(userName); + oos.writeObject(encrypt(password)); + oos.writeObject(driverName); + break; + } =20 + oos.writeBoolean(multithreaded); + oos.writeBoolean(optimistic); + oos.writeBoolean(retainValues); + oos.writeBoolean(restoreValues); + oos.writeBoolean(nontransactionalRead); + oos.writeBoolean(nontransactionalWrite); + oos.writeBoolean(ignoreCache); + =20 + oos.writeInt(queryTimeout); + oos.writeInt(updateTimeout); + } + + /** + * Read this object from a stream. This method is provided so it + * can be called from outside the class (explicitly by a subclass). + * @param ois the ObjectInputStream + * @throws IOException on errors reading from the stream + * @throws ClassNotFoundException if a referenced class cannot be load= ed + */ =20 + protected void doReadObject(java.io.ObjectInputStream ois) + throws java.io.IOException, ClassNotFoundException { + + readObject(ois); + } + + private void readObject(java.io.ObjectInputStream ois) + throws java.io.IOException, ClassNotFoundException { + + int kind =3D ois.readInt(); + switch (kind) { + case PERSIST_CF: + connectionFactory =3D ois.readObject(); + break; + =20 + case PERSIST_CF_NAME: + connectionFactoryName =3D ois.readUTF(); + connectionFactory2Name =3D ois.readUTF(); + break; + =20 + case PERSIST_PROPS: + URL =3D (String)ois.readObject(); + userName =3D (String)ois.readObject(); + password =3D decrypt((String)ois.readObject()); + driverName =3D (String)ois.readObject(); + break; + } + multithreaded =3D ois.readBoolean(); + optimistic =3D ois.readBoolean(); + retainValues =3D ois.readBoolean(); + restoreValues =3D ois.readBoolean(); + nontransactionalRead =3D ois.readBoolean(); + nontransactionalWrite =3D ois.readBoolean(); + ignoreCache =3D ois.readBoolean(); + =20 + queryTimeout =3D ois.readInt(); + updateTimeout =3D ois.readInt(); + } + + /** + * The preferred way of getting & restoring a PMF in JNDI is to do so = via + * a Properties object. + * + * Accessor instances allow copying values to/from a PMF and a + * Properties. They do the proper type translation too. + * The PMFAccessor extends the Accessor interface which provides only + * the getDefault method which is type-independent. The PMFAccessor + * provides type-specific accessor properties. + */ + public interface PMFAccessor extends Accessor { + =20 + /** Returns a value from a PMF, turned into a String. + * @param pmf the PersistenceManagerFactory to get the property fr= om + * @return the property value associated with the Accessor key + */ + public String get(PersistenceManagerFactoryImpl pmf); + =20 + /** Returns a value from a PMF, turned into a String, only if the + * current value is not the default. + * @param pmf the PersistenceManagerFactory to get the property fr= om + * @return the non-default property value associated with the + * Accessor key + */ + public String getNonDefault(PersistenceManagerFactoryImpl pmf); + =20 + /** Sets a value in a PMF, translating from String to the PMF's + * representation. + * @param pmf the PersistenceManagerFactory to set the property in= to + * @param s the property value associated with the Accessor key=20 + */ + public void set(PersistenceManagerFactoryImpl pmf, String s); + } + =20 + /** + * Tables which map from names to PMFAccessors. The names are the sam= e as + * the PMF's property names. + * + * These PMFAccessors are particular to the case when the connection + * properties are configured as PersistenceManagerFactory properties; + * neither a connection + * factory nor connection factory name has been configured. + */ + protected static HashMap pmfAccessors =3D new HashMap(4); + =20 + /** + *These PMFAccessors are for configuring non-connection properties. + */ + protected static HashMap propsAccessors =3D new HashMap(10); + =20 + /** Get JDO implementation-specific properties + * (not specified by JDO specification). + * @return a hashmap of accessors + */ =20 + protected HashMap getLocalAccessors() { + return new HashMap(); + } + + /** Initialize the Accessor hashmaps for + * connection and non-connection properties. + *
+ * XXX: Jikes bug + *
+ * If this is protected, FOStorePMF.initPropsAccessors cannot invoke it, + * due to a bug in jikes + * (http://www-124.ibm.com/developerworks/bugs/?func=3Ddetailbug&bug_id= =3D213&group_id=3D10) + */ + //protected static void initPropsAccessors() { + public static void initPropsAccessors() { + if (pmfAccessors.size() !=3D 0) + return; + synchronized (pmfAccessors) { + if (pmfAccessors.size() !=3D 0) + return; + // + // PMF accessors + // + + pmfAccessors.put( + "javax.jdo.option.ConnectionURL", // NOI18N + new PMFAccessor() { + public String get(PersistenceManagerFactoryImpl pmf) { ret= urn pmf.getConnectionURL(); } + public String getNonDefault(PersistenceManagerFactoryImpl = pmf) { return pmf.getConnectionURL(); } + public String getDefault() {return null;} + public void set(PersistenceManagerFactoryImpl pmf, String = s) { pmf.setConnectionURL(s); } + }); + pmfAccessors.put( + "javax.jdo.option.ConnectionUserName", // NOI18N + new PMFAccessor() { + public String get(PersistenceManagerFactoryImpl pmf) { ret= urn pmf.getConnectionUserName(); } + public String getNonDefault(PersistenceManagerFactoryImpl = pmf) { return pmf.getConnectionUserName(); } + public String getDefault() {return null;} + public void set(PersistenceManagerFactoryImpl pmf, String = s) { pmf.setConnectionUserName(s); } + }); + pmfAccessors.put( + "javax.jdo.option.ConnectionPassword", // NOI18N + new PMFAccessor() { + public String get(PersistenceManagerFactoryImpl pmf) { ret= urn pmf.encrypt(pmf.getConnectionPassword()); } + public String getNonDefault(PersistenceManagerFactoryImpl = pmf) { return pmf.encrypt(pmf.getConnectionPassword()); } + public String getDefault() {return null;} + public void set(PersistenceManagerFactoryImpl pmf, String = s) { pmf.setConnectionPassword(pmf.decrypt(s)); } + }); + pmfAccessors.put( + "javax.jdo.option.ConnectionDriverName", // NOI18N + new PMFAccessor() { + public String get(PersistenceManagerFactoryImpl pmf) { ret= urn pmf.getConnectionDriverName(); } + public String getNonDefault(PersistenceManagerFactoryImpl = pmf) { return pmf.getConnectionDriverName(); } + public String getDefault() {return null;} + public void set(PersistenceManagerFactoryImpl pmf, String = s) { pmf.setConnectionDriverName(s); } + }); + + // + // Props accessors + // + + propsAccessors.put( + "javax.jdo.option.Multithreaded", // NOI18N + new PMFAccessor() { + public String get(PersistenceManagerFactoryImpl pmf) { ret= urn new Boolean(pmf.getMultithreaded()).toString(); } + public String getNonDefault(PersistenceManagerFactoryImpl = pmf) { return (!pmf.getMultithreaded())?null:"true"; } // NOI18N + public String getDefault() { return "false"; } // NOI18N + public void set(PersistenceManagerFactoryImpl pmf, String = s) { pmf.setMultithreaded(Boolean.valueOf(s).booleanValue()); } + }); + propsAccessors.put( + "javax.jdo.option.Optimistic", // NOI18N + new PMFAccessor() { + public String get(PersistenceManagerFactoryImpl pmf) { ret= urn new Boolean(pmf.getOptimistic()).toString(); } + public String getNonDefault(PersistenceManagerFactoryImpl = pmf) { return (pmf.getOptimistic())?null:"false"; } // NOI18N + public String getDefault() { return "true"; } // NOI18N + public void set(PersistenceManagerFactoryImpl pmf, String = s) { pmf.setOptimistic(Boolean.valueOf(s).booleanValue()); } + }); + propsAccessors.put( + "javax.jdo.option.RetainValues", // NOI18N + new PMFAccessor() { + public String get(PersistenceManagerFactoryImpl pmf) { ret= urn new Boolean(pmf.getRetainValues()).toString(); } + public String getNonDefault(PersistenceManagerFactoryImpl = pmf) { return (pmf.getRetainValues())?null:"false"; } // NOI18N + public String getDefault() { return "true"; } // NOI18N + public void set(PersistenceManagerFactoryImpl pmf, String = s) { pmf.setRetainValues(Boolean.valueOf(s).booleanValue()); } + }); + propsAccessors.put( + "javax.jdo.option.RestoreValues", // NOI18N + new PMFAccessor() { + public String get(PersistenceManagerFactoryImpl pmf) { ret= urn new Boolean(pmf.getRestoreValues()).toString(); } + public String getNonDefault(PersistenceManagerFactoryImpl = pmf) { return (pmf.getRestoreValues())?null:"false"; } // NOI18N + public String getDefault() { return "true"; } // NOI18N + public void set(PersistenceManagerFactoryImpl pmf, String = s) { pmf.setRestoreValues(Boolean.valueOf(s).booleanValue()); } + }); + propsAccessors.put( + "javax.jdo.option.NontransactionalRead", // NOI18N + new PMFAccessor() { + public String get(PersistenceManagerFactoryImpl pmf) { ret= urn new Boolean(pmf.getNontransactionalRead()).toString(); } + public String getNonDefault(PersistenceManagerFactoryImpl = pmf) { return (pmf.getNontransactionalRead())?null:"false"; } // NOI18N + public String getDefault() { return "true"; } // NOI18N + public void set(PersistenceManagerFactoryImpl pmf, String = s) { pmf.setNontransactionalRead(Boolean.valueOf(s).booleanValue()); } + }); + propsAccessors.put( + "javax.jdo.option.NontransactionalWrite", // NOI18N + new PMFAccessor() { + public String get(PersistenceManagerFactoryImpl pmf) { ret= urn new Boolean(pmf.getNontransactionalWrite()).toString(); } + public String getNonDefault(PersistenceManagerFactoryImpl = pmf) { return (!pmf.getNontransactionalWrite())?null:"true"; } // NOI18N + public String getDefault() { return "false"; } // NOI18N + public void set(PersistenceManagerFactoryImpl pmf, String = s) { pmf.setNontransactionalWrite(Boolean.valueOf(s).booleanValue()); } + }); + propsAccessors.put( + "javax.jdo.option.IgnoreCache", // NOI18N + new PMFAccessor() { + public String get(PersistenceManagerFactoryImpl pmf) { ret= urn new Boolean(pmf.getIgnoreCache()).toString(); } + public String getNonDefault(PersistenceManagerFactoryImpl = pmf) { return (pmf.getIgnoreCache())?null:"false"; } // NOI18N + public String getDefault() { return "true"; } // NOI18N + public void set(PersistenceManagerFactoryImpl pmf, String = s) { pmf.setIgnoreCache(Boolean.valueOf(s).booleanValue()); } + }); + propsAccessors.put( + "javax.jdo.option.ConnectionFactoryName", // NOI18N + new PMFAccessor() { + public String get(PersistenceManagerFactoryImpl pmf) { ret= urn pmf.getConnectionFactoryName(); } + public String getNonDefault(PersistenceManagerFactoryImpl = pmf) { return (pmf.getConnectionFactoryName()=3D=3Dnull)?null:pmf.getConnec= tionFactoryName(); } + public String getDefault() { return null; } + public void set(PersistenceManagerFactoryImpl pmf, String = s) { pmf.setConnectionFactoryName(s); } + }); + propsAccessors.put( + "javax.jdo.option.ConnectionFactory2Name", // NOI18N + new PMFAccessor() { + public String get(PersistenceManagerFactoryImpl pmf) { ret= urn pmf.getConnectionFactory2Name(); } + public String getNonDefault(PersistenceManagerFactoryImpl = pmf) { return (pmf.getConnectionFactory2Name()=3D=3Dnull)?null:pmf.getConne= ctionFactory2Name(); } + public String getDefault() { return null; } + public void set(PersistenceManagerFactoryImpl pmf, String = s) { pmf.setConnectionFactory2Name(s); } + }); + } + } + + /** + * It should *never* be the case that our translation process encounte= rs + * a NumberFormatException. If so, tell the user in the JDO-approved + * manner. + * @param s the input String + * @return the int representation of the String + */=20 + protected static int toInt(String s) { + int rc =3D 0; + try { + rc =3D Integer.parseInt(s); + } catch (NumberFormatException ex) { + throw new JDOFatalInternalException(msg.msg( + "EXC_IntegerInInvalidFormat")); // NOI18N + } + return rc; + } + + /** + * Returns a Properties representation of this PMF. + * Only allow Properties representation if the caller configured + * this PersistenceManagerFactory. Otherwise, this is a security + * exposure. + * @return the Properties representing the non-default properties + */ + public Properties getAsProperties() { + assertConfigurable(); + return getAsPropertiesInternal(); + } + + /**=20 + * Does not do assertConfigurable validation + * @see #getAsProperties() + */ + protected Properties getAsPropertiesInternal() { + initPropsAccessors(); + Properties p =3D new Properties(); + + int kind =3D getSerializedForm(); + + switch (kind) { + case PERSIST_CF: + // XXX need to handle the case of ConnectionFactory2 + setCFProperties(p); + break; + + case PERSIST_CF_NAME: + p.setProperty ("javax.jdo.option.ConnectionFactoryName", + connectionFactoryName); // NOI18N + if (connectionFactory2Name !=3D null) { + p.setProperty ("javax.jdo.option.ConnectionFactory2Name", + connectionFactory2Name); // NOI18N + } + break; + + case PERSIST_PROPS: + setProps(p, pmfAccessors); + break; + } + setProps(p, propsAccessors); + setPMFClassProperty(p); + // add the properties from the implementation class + setProps(p, getLocalAccessors()); + return p; + } + =20 + /** Set the PMF class property for this PMF. + * @param props the Properties to which to add the PMF class property + */ + abstract protected void setPMFClassProperty (Properties props); + + /** + * For each PMFAccessor in the given HashMap, gets the corresponding v= alue + * from the PMF and puts it in the given Properties object. + */ + void setProps(Properties p, HashMap accessors) { + Set s =3D accessors.entrySet(); + for (Iterator i =3D s.iterator(); i.hasNext();) { + Map.Entry e =3D (Map.Entry)i.next(); + String key =3D (String)e.getKey(); + PMFAccessor a =3D (PMFAccessor)e.getValue(); + String value =3D (String)a.getNonDefault(this); + if (null !=3D value) { + p.setProperty (key, value); + } + } + } + + /** + * Configures a PMF from the given Properties. + * @param p the Properties used to configure this PMF + */ + public void setFromProperties(Properties p) { + initPropsAccessors(); + assertConfigurable(); + int kind =3D getSerializedForm (p); + + switch (kind) { + case PERSIST_CF: + getCFFromProperties(p); + break; + + case PERSIST_CF_NAME: + connectionFactoryName =3D p.getProperty( + "javax.jdo.option.ConnectionFactoryName"); // NOI18N + connectionFactory2Name =3D p.getProperty( + "javax.jdo.option.ConnectionFactory2Name"); // NOI18N + break; + + case PERSIST_PROPS: + getProps(p, pmfAccessors); + break; + } + getProps(p, propsAccessors); + getProps(p, getLocalAccessors()); + } + + /** + * For each PMFAccessor in the given HashMap, gets the corresponding v= alue + * from the Properties and sets that value in the PMF. + */ + private void getProps(Properties p, HashMap accessors) { + Set s =3D accessors.entrySet(); + for (Iterator i =3D s.iterator(); i.hasNext();) { + Map.Entry e =3D (Map.Entry)i.next(); + String key =3D (String)e.getKey(); + String value =3D p.getProperty(key); + if (null !=3D value) { +// System.out.println("PersistenceManagerFactoryImpl settin= g property: " + key + " to: " + value); // NOI18N + PMFAccessor a =3D (PMFAccessor)e.getValue(); + a.set(this, value); + } + } + } + + /** + * Provides an encrypted version of the given string. + * NOTE: + * Be very sure that you implement this method using the kind of + * security that is appropriate for your JDO implementation!!! + * Note that this method is not static, because it must be overridden + * by the specialized subclass. But it should be written as if it were + * static. That is, it should not use any state in the + * PersistenceManagerFactoryImpl instance. + * @param s the String to be encrypted + * @return the encrypted String + */ + abstract protected String encrypt(String s); + + /** + * Provides a decrypted version of the given (encrypted) string. + * NOTE: + * Be very sure that you implement this method using the kind of + * security that is appropriate for your JDO implementation!!! + * @param s the String to be decrypted + * @return the decrypted String + */ + abstract protected String decrypt(String s); + + /** + * Set the PMF-specific ConnectionFactory's properties. + * @param p Properties object in which the PMF's ConnectioFactory's + * properties are to be set. + */ + abstract protected void setCFProperties(Properties p); + + /** + * Create a ConnectionFactory for this PMF. The method's implementati= on + * should set the PMF's connection factory with the newly created obje= ct. + * @param p Properties from which the ConnectionFactory is to be creat= ed. + */ + // XXX The method name contains "get" but this does not "get" + // anything. It should be changed to "setup" or ??? + abstract protected void getCFFromProperties(Properties p); + + /** + * Returns if a connection factory is configured for this + * PersistenceManagerFactory. This is used to determine whether + * this PersistenceManagerFactory has been configured with a + * ConnectionFactory, a ConnectionFactoryName, or a ConnectionURL. + * @return if a connection factory is configured + */ + abstract protected boolean isConnectionFactoryConfigured(); + =20 + /** The String representation of this PMF. + * @return the String representation of this PMF + */ =20 + public String toString() { + return "" + // NOI18N + "URL: " + URL + "\n" + // NOI18N + "userName: " + userName + "\n" + // NOI18N + "password: " + password + "\n" + // NOI18N + "driverName: " + driverName + "\n" + // NOI18N + + "connectionFactory: " + connectionFactory + "\n" + // NOI18N + "connectionFactoryName: " + connectionFactoryName + "\n" + // = NOI18N + + "connectionFactory2: " + connectionFactory2 + "\n" + // NOI18N + "connectionFactory2Name: " + connectionFactory2Name + "\n" + /= / NOI18N + + "multithreaded: " + multithreaded + "\n" + // NOI18N + "optimistic: " + optimistic + "\n" + // NOI18N + "retainValues: " + retainValues + "\n" + // NOI18N + "restoreValues: " + restoreValues + "\n" + // NOI18N + "nontransactionalRead: " + nontransactionalRead + "\n" + // NO= I18N + "nontransactionalWrite: " + nontransactionalWrite + "\n" + // = NOI18N + "ignoreCache: " + ignoreCache + "\n" + // NOI18N + "queryTimeout: " + queryTimeout + "\n" + // NOI18N + "updateTimeout: " + updateTimeout + "\n"; // NOI18N + } + =20 + /** Verify that the connection URL has been configured. + * This might be done by the PMF property ConnectionURL, + * or by the connection factory property URL, or + * by configuring a connection factory name. + */ =20 + protected void verifyConfiguration() { + if ((!isConnectionFactoryConfigured()) && + (connectionFactoryName =3D=3D null) && + (URL =3D=3D null)) { + throw new JDOFatalUserException(msg.msg( + "EXC_IncompleteConfiguration")); // NOI18N + } + } + =20 + /** + * Set the configurable flag false so this + * PersistenceManagerFactory can no longer be configured. No value is + * provided, because a PersistenceManagerFactory can never become + * re-configurable. Once invoked, the hashCode() of this PMF will nev= er + * change, except if setNonconfigured is called. + * @see #hashCode() + * @see #setNonconfigured() + */ =20 + protected void setConfigured() { + configurable =3D false; + myHashCode =3D hashCode(); + } + + /** + * Set the configurable flag true so this + * PersistenceManagerFactory can be again configured. Called only + * if the action caused change to be non-configurable failed. + */ =20 + protected void setNonconfigured() { + configurable =3D true; + myHashCode =3D 0; + } + + /** Given an input Properties instance, add to the output Properties i= nstance + * only the non-default entries of the input Properties, based on the + * Accessor map provided. The output instance can be used as the key + * for the PersistenceManagerFactory hashMap. + * + *

A properties instance will typically be filtered a number of tim= es: + * once for the JDO standard PersistenceManagerFactory properties, ano= ther + * for the JDO implementation properties, and another for the implemen= tation + * ConnectionFactory properties. + * + *

A properties accessor map is passed as an argument. The map + * contains the PMFAccessors, keyed by property name. + * @param props the input Properties + * @param filtered the output properties + * @param accessors the hashmap of accessors to filter for + */ + public static void filterProperties (Properties props, Properties filt= ered, + Map accessors) { + Set s =3D accessors.entrySet(); + for (Iterator i =3D s.iterator(); i.hasNext();) { + // for each accessor defined + Map.Entry e =3D (Map.Entry)i.next(); + String key =3D (String)e.getKey(); + // if the key in the accessor matches a property in the proper= ties + String value =3D props.getProperty(key); + // and if the property is not null + if (null !=3D value) { + Accessor a =3D (Accessor)e.getValue(); + // and the value is not the default value for the accessor + if (a.getDefault() !=3D value) { + // set the property in the filtered properties + filtered.setProperty (key, value); + } + } + } + return; + } + + public synchronized boolean equals(Object o) { + if (o =3D=3D this) + return true; + + if (!(o instanceof PersistenceManagerFactoryImpl)) + return false; + + return (this.getAsPropertiesInternal().equals( + ((PersistenceManagerFactoryImpl)o).getAsPropertiesInternal())); + } + + /** The returned value can change before this PMF is configured. Once + * configured it will never change (well...) + * @see #setConfigured() + * @see #setNonconfigured() + */ + public synchronized int hashCode() { + if (0 =3D=3D myHashCode) { + return this.getAsPropertiesInternal().hashCode(); + } else { + return myHashCode; + } + } + =20 + /** Close this PersistenceManagerFactory. Check for + * JDOPermission("closePersistenceManagerFactory") and if not authoriz= ed, + * throw SecurityException. + *

If the authorization check succeeds, check to see that all + * PersistenceManager instances obtained from this PersistenceManagerF= actory + * have no active transactions. If any PersistenceManager instances ha= ve + * an active transaction, throw a JDOUserException, with one nested + * JDOUserException for each PersistenceManager with an active Transac= tion. + *

If there are no active transactions, then close all PersistenceM= anager + * instances obtained from this PersistenceManagerFactory, mark this + * PersistenceManagerFactory as closed, disallow getPersistenceManager + * methods, and allow all other get methods. If a set method or + * getPersistenceManager method is called after close, then + * JDOUserException is thrown. + */ + public void close() { + synchronized(closeLock) { + if (closed) { + return; + } + SecurityManager secmgr =3D System.getSecurityManager(); + if (secmgr !=3D null) { + // checkPermission will throw SecurityException if not aut= horized + secmgr.checkPermission(JDOPermission.CLOSE_PERSISTENCE_MAN= AGER_FACTORY); + } + List activePersistenceManagers =3D getActivePersistenceManager= s(); + int size =3D activePersistenceManagers.size(); + if (size !=3D 0) { + Throwable[] thrown =3D new Throwable[size]; + for (int i =3D 0; i < size; ++i) { + PersistenceManagerImpl pm =3D=20 + (PersistenceManagerImpl)activePersistenceManagers.= get(i); + thrown[i] =3D new JDOUserException( + msg.msg("EXC_ActivePersistenceManager"), // NOI18N + pm.getCurrentWrapper()); + } + throw new JDOUserException( + msg.msg("EXC_ActivePersistenceManager"), thrown); // N= OI18N + } + closeOpenPersistenceManagers(); + // pmf is closed =3D> remove it from collection of registered = pmfs + registeredPMFs.remove(this); + setConfigured(); + closed =3D true; + } + } + =20 + /** Assert that this PersistenceManagerFactory is not closed. This + * assertion precedes all getPersistenceManager calls. "set" methods + * are already protected by the configured flag. + * This method is synchronized so if another thread is calling + * close at the same time, this thread will wait for the close to comp= lete. + */ + protected void assertNotClosed() { + synchronized(closeLock) { + if (closed) { + throw new JDOUserException( + msg.msg("EXC_PersistenceManagerFactoryClosed")); // NO= I18N + } + } + } + =20 + /** Get all active PersistenceManagers. This is all=20 + * PersistenceManagers that have active transactions. + */ + protected List getActivePersistenceManagers() { + List pms =3D new ArrayList(); + for (Iterator it=3DpmSet.iterator(); it.hasNext();) { + PersistenceManager pm =3D (PersistenceManager)it.next(); + if (pm.currentTransaction().isActive()) { + pms.add(pm); + } + } + return pms; + } + =20 + /** Close all open PersistenceManagers. Only the PersistenceManagers + * in the non-transactional set are considered; there cannot be any + * inactive PersistenceManagers in the transactional cache. + * We do forceClose because we don't care if there are active wrappers. + */ + protected void closeOpenPersistenceManagers() { + // copy to avoid concurrent modification; forceClose changes pmSet. + List toClose =3D Arrays.asList(pmSet.toArray());=20 + for (Iterator it=3DtoClose.iterator(); it.hasNext();) { + PersistenceManagerImpl pm =3D (PersistenceManagerImpl)it.next(= ); + pm.forceClose(); + } + } + =20 + + /** Method called by the shudown hook to close pmf instances left open=20 + * when the JVM exits. + */ + protected void shutdown() { + closeOpenPersistenceManagers(); + } + + /** Shutdown hook to close pmf instances left open when the JVM + * exits.=20 + */=20 + static class ShutdownHook extends Thread { + public void run() { + for (Iterator i =3D registeredPMFs.iterator(); i.hasNext();) { + try { + ((PersistenceManagerFactoryImpl)i.next()).shutdown(); + } + catch (JDOException ex) { + // ignore + } + } + } + } +} +