You are viewing a plain text version of this content. The canonical link for it is here.
Posted to jdo-commits@db.apache.org by mb...@apache.org on 2005/05/22 20:01:48 UTC

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/

Added: incubator/jdo/trunk/runtime20/src/java/org/apache/jdo/impl/pm/CacheManagerImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/runtime20/src/java/org/apache/jdo/impl/pm/CacheManagerImpl.java?rev=171352&view=auto
==============================================================================
--- incubator/jdo/trunk/runtime20/src/java/org/apache/jdo/impl/pm/CacheManagerImpl.java (added)
+++ incubator/jdo/trunk/runtime20/src/java/org/apache/jdo/impl/pm/CacheManagerImpl.java Sun May 22 11:01:45 2005
@@ -0,0 +1,630 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ * 
+ * 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 
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software 
+ * distributed under the License is distributed on an "AS IS" BASIS, 
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+ * See the License for the specific language governing permissions and 
+ * 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 = null;
+
+    /**
+     * Collection of Persistent instances created and/or updated
+     * in this Transaction
+     */
+    private Collection _txCache = Collections.synchronizedSet(new HashSet());
+
+    /**
+     * Collection of Transient-transactional instances registered
+     * with this Transaction
+     */
+    private Collection _transientCache = new Vector();
+
+    /**
+     * Collection of Persistent instances that will require state
+     * change at the transaction completion
+     */
+    private ArrayList _flushedCache = new ArrayList();
+
+    /**
+     * Collection of StateManager instances that represent Persistent
+     * instances that had been made newly persistent in the current
+     * transaction.
+     */
+    private ArrayList _newInstances = new ArrayList();
+
+    /** 
+     * Weak Hashtable of Persistent instances accessed by this PersistenceManager
+     */
+    private WeakValueHashMap _weakCache = new WeakValueHashMap();
+
+    /**
+     * Logger instance
+     */
+    private static final Log logger = LogFactory.getFactory().getInstance(
+        "org.apache.jdo.impl.pm"); // NOI18N
+
+    /**
+     * I18N message handler
+     */
+    private final static I18NHelper msg = 
+        I18NHelper.getInstance(CacheManagerImpl.class);
+
+    /**
+     * Constructs new instnstance of CacheManagerImpl
+     * 
+     * @param pm calling instance of PersistenceManagerImpl
+     */
+    CacheManagerImpl(PersistenceManagerImpl pm) {
+        this.pm = 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. 
+       _weakCache.clear();
+       _txCache.clear();
+       _flushedCache.clear();
+       _transientCache.clear();
+    }
+
+    
+
+    /** This method locates a persistent instance in the cache of instances
+     * managed by this PersistenceManager.
+     *   
+     * <P>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 returned.
+     * If there is a transaction active, then
+     * a persistent clean instance is returned.
+     * <P>If the validate flag is false: If there is not already an instance
+     * 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 = this.getStateManager(oid, validate);
+        return ((StateManagerInternal)sm).getObject();
+    }
+
+    /**
+     * Returns StateManager instance associated with this instance of ObjectId
+     * Creates a Hollow instance of a PersistenceCapable object, if it cannot be 
+     * found in the cache
+     * @param oid an ObjectId
+     * @param pcClass Class of a Hollow instance to be created.
+     * @return the StateManagerInternal 
+     */
+    protected StateManagerInternal getStateManager (Object oid, Class pcClass) {
+        if (debugging())
+            debug ("getStateManager " + oid + " for: " + pcClass.getName()); // NOI18N
+
+        StateManagerInternal sm = 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 = _weakCache.get(oid);
+            if (o == null) {
+                // Nothing found
+                sm  = createNewSM(null, oid, pcClass);
+            } else {
+                // Prepare Hollow instance if its class type was not
+                // known before. 
+                sm = (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 = pm.findStateManager(pc);
+
+        Object oid = null;
+        if (_weakCache.containsValue(sm)) {
+            if (transactional)
+                oid = sm.getTransactionalObjectId(pc);
+            else
+                oid = 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 Class.
+     * 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 = pm.findStateManager(pc);
+        if (sm == null) {
+            sm = 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 = pm.findStateManager(pc);
+        if (sm == null) {
+            sm = 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 = pm.findStateManager(pc);
+        if (sm == 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 = pm.findStateManager(pc);
+        if (sm != null) {
+            sm.makeTransient();
+        }
+    }
+
+    /** Make persistent instance hollow in this PersistenceManager.
+     * @see javax.jdo.PersistenceManager#evict(Object pc)
+     */
+    protected void evict(PersistenceCapable pc) {
+        StateManagerInternal sm = pm.findStateManager(pc);
+        if (sm != null) {
+            sm.evictInstance();
+        }
+    }
+
+    /** Make all non-dirty persistent instances in the cache hollow in 
+     * this PersistenceManager.
+     * @see javax.jdo.PersistenceManager#evictAll()
+     */
+    protected void evictAll() {
+        StateManagerInternal sm = null;
+
+        Iterator it = _weakCache.entrySet().iterator();
+        while (it.hasNext()) {
+            sm = (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 = pm.findStateManager(pc);
+        if (sm != null) {
+            sm.retrieve();
+        }
+    }
+
+    /** Refresh dirty persistent instance in this PersistenceManager.
+     * @see javax.jdo.PersistenceManager#refresh(Object pc)
+     */
+    protected void refresh(PersistenceCapable pc) {
+        StateManagerInternal sm = pm.findStateManager(pc);
+        if (sm != null) {
+            sm.refreshInstance();
+        }
+    }
+
+    /** Refresh dirty persistent instances in the transactional cache 
+     * of this PersistenceManager. Called in an active transaction.
+     * @see javax.jdo.PersistenceManager#refreshAll()
+     */
+    protected void refreshAllTransactional() {
+        StateManagerInternal sm = null;
+
+        Iterator it = _txCache.iterator();
+        while(it.hasNext()) {
+            sm = (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 = null;
+
+        Iterator it = _weakCache.entrySet().iterator();
+        while (it.hasNext()) {
+            sm = (StateManagerInternal) ((Map.Entry)it.next()).getValue();
+            sm.refreshInstance();
+        }
+    }
+
+    /**
+     * Register transient instance in the transient cache
+     */
+    protected void registerTransient(StateManagerInternal sm) {
+        Iterator it = _transientCache.iterator();
+        while(it.hasNext()) {
+            Object o = ((WeakReference)it.next()).get();
+            if ((StateManagerInternal)o == 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, 
+            boolean transactional, boolean throwDuplicateException) {
+        if (oid == null) {
+            oid = sm.getInternalObjectId();
+        }
+
+        //register in both caches for transactional instances only
+
+        if (! _weakCache.containsKey(oid)) {
+            deregisterTransient(sm);
+            _weakCache.put(oid, sm);  
+
+        } 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); 
+            }
+            if (! _txCache.contains(sm)) {
+                _txCache.add(sm); 
+                if (sm.isNew()) 
+                    _newInstances.add(sm.getObject());
+            }
+        }
+
+        if (!transactional) {
+            // Remove from transactional caches if instance became
+            // nontransactional
+            _txCache.remove(sm); 
+            _flushedCache.remove(sm);
+        }
+    }
+
+    /**
+     * Remove transient instance from the transient cache
+     */
+    protected void deregisterTransient(Object sm) {
+        Iterator it = _transientCache.iterator();
+        while(it.hasNext()) {
+            WeakReference wr = (WeakReference)it.next();
+            if ((StateManagerInternal)wr.get() == sm) {
+                _transientCache.remove(wr);
+                break;
+            }
+        }
+    }
+
+    /**
+     * Remove persistent instance from all caches
+     */
+    protected void deregister(Object oid) {
+        if (oid != null) {
+            //deregister the instance from all the caches
+            Object o = _weakCache.remove(oid);
+
+            // No need to do anything outside an active transaction.
+            if (pm.currentTransaction().isActive()) {
+                _txCache.remove(o);
+                _flushedCache.remove(o);
+            }
+        }
+    }
+
+    /**  
+     * @see PersistenceManagerInternal#replaceObjectId(Object oldId,
+     * Object newId)
+     */  
+    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 = _weakCache.remove(oldId);
+            if (o == null) {
+                throw new JDOFatalInternalException(msg.msg(
+                    "EXC_ObjectIdNotExistsInCache", newId)); // NOI18N
+            }
+            _weakCache.put(newId, o);
+        }
+    }
+
+    /**  
+     * @see PersistenceManagerInternal#markAsFlushed(StateManagerInternal sm)
+     */  
+    protected void markAsFlushed(StateManagerInternal sm) { 
+        _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() 
+     * for each StateManager in it.
+     */
+    protected void flushInstances() {
+        StateManagerInternal sm = null;
+
+        Object[] e = _txCache.toArray();
+        boolean commit = pm.insideCommit();
+
+        for (int i = 0; i < e.length; i++) {
+            sm = (StateManagerInternal)e[i];
+
+            //
+            // NOTE: handleRelationships has the side-effect of adding
+            // more objects to the transaction cache.
+            //
+            sm.handleReachability(commit);
+        }
+
+        StoreManager srm = pm.getStoreManager();
+        Iterator it = _txCache.iterator();
+
+        srm.flush(it, pm);
+
+        _txCache.clear();
+    }
+
+    /**
+     * Called by Transaction commit() or rollback()
+     * cleans up transactional cache
+     * @param    abort 
+     */
+    protected void afterCompletion(boolean abort) {
+        boolean retainValues = pm.currentTransaction().getRetainValues();
+        boolean restoreValues = pm.currentTransaction().getRestoreValues();
+
+        // Need to process transient instances also
+        Iterator it = _transientCache.iterator();
+        while(it.hasNext()) {
+            Object o = ((WeakReference)it.next()).get();
+
+            if (o == null) { 
+                // It has been GC'd and should be removed from _transientCache. 
+                it.remove(); 
+            } else {
+                _flushedCache.add(o);
+            }
+        }
+
+        int len = _flushedCache.size();
+        for ( int i = 0; i < len; i++) {
+            StateManagerInternal sm = (StateManagerInternal)_flushedCache.get(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 ObjectId
+     * @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 validate) {
+
+        Object o = null;
+        StoreManager srm = pm.getStoreManager();
+        Class candidateClassType = srm.getPCClassForOid(oid, pm);
+        if (candidateClassType == null) {
+            // not found, report an error
+            throw new JDOUserException(msg.msg(
+                "EXC_NotOID"),// NOI18N
+                 oid);
+        }
+
+        Object internalOid = srm.getInternalObjectId(oid, pm);
+        if (debugging())
+            debug ("getStateManager internal oid: " + internalOid); // NOI18N
+
+        StateManagerInternal sm = null;
+
+        // Check weak cache to find SM:
+        synchronized (_weakCache) {
+            if((o = _weakCache.get(internalOid)) == null) {
+                // Nothing found
+                if (debugging())
+                    debug ("getStateManager oid not found."); // NOI18N
+
+                sm  = 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."); // NOI18N
+
+                sm = (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 created.
+     */
+    private StateManagerInternal createNewSM(Object UserOid, Object internalOid,
+                                                      Class candidateClassType) {
+        try {
+            return StateManagerFactory.newInstance(UserOid, internalOid, 
+                pm, candidateClassType);
+
+        } catch (JDOUserException e) {
+            throw e;
+            // XXX Possible jikes bug
+            //
+            // The following catch phrase causes jikes to complain (Caution:
+            // This try block cannot throw a "checked exception" (JLS section
+            // 14.7) that can be caught here. You may have intended to catch
+            // 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
+     */  
+    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/PersistenceManagerFactoryImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/runtime20/src/java/org/apache/jdo/impl/pm/PersistenceManagerFactoryImpl.java?rev=171352&view=auto
==============================================================================
--- incubator/jdo/trunk/runtime20/src/java/org/apache/jdo/impl/pm/PersistenceManagerFactoryImpl.java (added)
+++ incubator/jdo/trunk/runtime20/src/java/org/apache/jdo/impl/pm/PersistenceManagerFactoryImpl.java Sun May 22 11:01:45 2005
@@ -0,0 +1,1677 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ * 
+ * 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 
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software 
+ * distributed under the License is distributed on an "AS IS" BASIS, 
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+ * See the License for the specific language governing permissions and 
+ * limitations under the License.
+ */
+
+/*
+ * PersistenceManagerFactoryImpl.java
+ *
+ * Created on December 1, 2000
+ */
+ 
+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;
+
+/** 
+ * This is an abstract PersistenceManagerFactoryImpl class that provides the 
+ * StoreManager independent implementation of javax.jdo.PersistenceManager
+ * interface. 
+ * <p>
+ * Subclasses must override the following methods declared abstract:
+ * <ul>
+ * <li> {@link #getOptionArray()}
+ * <li> {@link #createPersistenceManager(String userid, String password)}
+ * <li> {@link #setPMFClassProperty (Properties props)}
+ * <li> {@link #encrypt(String s)}
+ * <li> {@link #decrypt(String s)}
+ * <li> {@link #setCFProperties(Properties p)}
+ * <li> {@link #getCFFromProperties(Properties p)}
+ * <li> {@link #isConnectionFactoryConfigured()}
+ * <li> and all methods from org.apache.jdo.pm.PersistenceManagerFactoryInternal.
+ * </ul> 
+ *
+ * @author  Marina Vatkina
+ * @version 0.1
+ */
+
+abstract public class PersistenceManagerFactoryImpl implements 
+    PersistenceManagerFactoryInternal {
+
+    //
+    // PersistenceManagerFactory properties
+    //
+    private String URL = null;
+    private String userName = null;
+    protected String password = null;
+    private String driverName = null;
+
+    private Object connectionFactory = null;
+    private String connectionFactoryName = null;
+
+    private Object connectionFactory2 = null;
+    private String connectionFactory2Name = null;
+
+    private boolean multithreaded = false;
+
+    private boolean optimistic = true;
+    private boolean retainValues = true;
+    private boolean restoreValues = true;
+    private boolean nontransactionalRead = true;
+    private boolean nontransactionalWrite = false;
+    private boolean ignoreCache = true;
+    
+    private int queryTimeout = 0;
+    private int updateTimeout = 0;
+
+    private int minPool = 1;
+    private int maxPool = 1;
+    private int msWait = 0;
+
+    /** Cached hashCode for this PMF.  Changes every time a property of this
+    * 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 = true;
+    
+
+    //
+    // The PMF is serialized in one of 3 forms, depending on how it is
+    // configured.
+    //
+    private static final int PERSIST_CF = 1;
+    private static final int PERSIST_CF_NAME = 2;
+    private static final int PERSIST_PROPS = 3;
+
+    /** These are used for implementing close().
+     */
+    protected boolean closed = false;
+    
+    /** The closeLock protects the close flag and pmSet.
+     */
+    protected Object closeLock = new Object();
+    
+    /** 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 = new HashSet();
+    
+    /**
+     * Logger instance
+     */
+    private static final Log logger = LogFactory.getFactory().getInstance(
+        "org.apache.jdo.impl.pm"); // NOI18N
+
+    /**
+     * I18N message handler
+     */
+    private final static I18NHelper msg = 
+        I18NHelper.getInstance("org.apache.jdo.impl.pm.Bundle"); // NOI18N
+
+    /**
+     * Transactional cache of PersistenceManager instances
+     */
+    private Hashtable pmCache = new Hashtable();
+
+    /** RuntimeJavaModelFactory. */
+    private static final RuntimeJavaModelFactory javaModelFactory =
+        (RuntimeJavaModelFactory) AccessController.doPrivileged(
+            new PrivilegedAction () {
+                public Object run () {
+                    return RuntimeJavaModelFactory.getInstance();
+                }
+            }
+        );
+ 
+    /** Collection of registered pmf instances. */
+    private static Collection registeredPMFs = new HashSet();
+    
+    /** Adds a JVM shutdown hook to close pmf instances left open by the
+     * user. 
+     */
+    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 <code>PersistenceManagerFactoryImpl</code> without
+     * any user info.
+     */
+    public PersistenceManagerFactoryImpl() { }
+
+    /**
+     * Creates new <code>PersistenceManagerFactoryImpl</code> with user info
+     * @param URL        URL for the data store connection
+     * @param userName    user name for the data store connection 
+     * @param password    password for the data store connection
+     * @param driverName    driver name for the data store connection
+     */
+    public PersistenceManagerFactoryImpl(
+            String URL, 
+            String userName, 
+            String password, 
+            String driverName) {
+        this.URL = URL;
+        this.userName = userName;
+        this.password = password;
+        this.driverName = driverName;
+        
+    }
+  
+    /** 
+     * 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 = userName;
+    }
+  
+    /**
+     * Get the user name for the data store connection.
+     * @return    the user name for the data store connection.
+     */
+    public String getConnectionUserName() {
+        return userName;
+    }
+  
+    /**
+     * 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 = password;
+    }
+  
+    /**
+     * Get the password for the data store connection.  Protected so 
+     * not just anybody can get the password.
+     * @return password the password for the data store connection.
+     */
+    protected String getConnectionPassword () {
+        return this.password;
+    }
+  
+    /**
+     * 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 = URL;
+    }
+  
+    /**
+     * 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 = driverName;
+    }
+  
+    /**
+     * 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 = connectionFactoryName;
+    }
+  
+    /**
+     * 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 = connectionFactory;
+    }
+  
+    /**
+     * Get the data store connection factory.
+     * @return the data store connection factory.
+     */
+    public Object getConnectionFactory() {
+        return connectionFactory;
+    }
+    
+    /** Set the name of the connection factory for non-transactional connections.
+     * @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 = connectionFactory2Name;
+    }
+    
+    /** Get the name of the connection factory for non-transactional connections.
+     * @see javax.jdo.PersistenceManagerFactory#getConnectionFactory2Name
+     * @return the name of the connection factory for 
+     * 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 = connectionFactory2;
+    }
+  
+    /** 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;
+    }
+  
+    /** 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 = flag;
+    }
+
+    /** Get the default Multithreaded setting for all
+     * PersistenceManager instances obtained from this factory.  
+     *
+     * @return the default Multithreaded setting.
+     */
+    public boolean getMultithreaded() {
+        return multithreaded;
+    }
+
+    /**
+     * Set the default Optimistic setting for all PersistenceManager instances
+     * 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 = flag;
+    }
+
+    /**
+     * Get the default Optimistic setting for all PersistenceManager instances
+     * obtained from this factory.
+     * @return the default Optimistic setting.
+     */
+    public boolean getOptimistic () {
+        return optimistic;
+    }
+
+
+    /**
+     * Set the default RetainValues setting for all PersistenceManager instances
+     * 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 = flag;    
+    }
+
+    /**
+     * Get the default RetainValues setting for all PersistenceManager instances
+     * obtained from this factory.
+     * @return the default RetainValues setting.
+     */
+     public boolean getRetainValues () {
+         return retainValues;
+     }
+
+    /**
+     * Set the default RestoreValues setting for all PersistenceManager instances
+     * 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 = flag;    
+    }
+
+    /**
+     * Get the default RestoreValues setting for all PersistenceManager instances
+     * 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.
+      */   
+     public void setNontransactionalRead (boolean flag) {
+         assertConfigurable();
+         nontransactionalRead = flag; 
+     }
+
+     /**
+      * Get the default NontransactionalRead setting for all
+      * PersistenceManager instances obtained from this factory.
+      * @return the default NontransactionalRead setting.
+      */   
+     public boolean getNontransactionalRead () {
+         return nontransactionalRead;
+     }
+
+     /**
+      * Set the default NontransactionalWrite setting for all
+      * PersistenceManager instances obtained from this factory.
+      * @param flag the default NontransactionalWrite setting.
+      */   
+     public void setNontransactionalWrite (boolean flag) {
+         assertConfigurable();
+         nontransactionalWrite = flag; 
+     }
+
+    /**
+     * Get the default NontransactionalWrite setting for all
+     * PersistenceManager instances obtained from this factory.
+     * @return the default NontransactionalWrite setting.
+     */   
+    public boolean getNontransactionalWrite () {
+        return nontransactionalWrite;
+    }
+
+
+    /**
+     * Set the default IgnoreCache setting for all PersistenceManager instances
+     * obtained from this factory.
+     * @param flag the default IgnoreCache setting.
+     */
+    public void setIgnoreCache (boolean flag) {
+        assertConfigurable();
+        ignoreCache = flag;
+    }
+
+    /**
+     * Get the default IgnoreCache setting for all PersistenceManager instances
+     * 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 = msWait;
+    }
+    
+    /** Get the default MsWait setting for all PersistenceManager instances
+     * obtained from this factory.
+     * @return the default MsWait setting.
+     */
+    public int getMsWait() {
+        return msWait;
+    }
+    
+    /** Set the default MinPool setting for all PersistenceManager instances
+     * obtained from this factory.
+     * @param minPool the default MinPool setting.
+     */
+    public void setMinPool(int minPool) {
+        assertConfigurable();
+        this.minPool = minPool;
+    }
+    
+    /** Get the default MinPool setting for all PersistenceManager instances
+     * obtained from this factory.
+     * @return the default MinPool setting.
+     */
+    public int getMinPool() {
+        return minPool;
+    }
+    
+    /** Set the default MaxPool setting for all PersistenceManager instances
+     * obtained from this factory.
+     * @param maxPool the default MaxPool setting.
+     */
+    public void setMaxPool(int maxPool) {
+        assertConfigurable();
+        this.maxPool = maxPool;
+    }
+    
+    /** Get the default MaxPool setting for all PersistenceManager instances
+     * obtained from this factory.
+     * @return the default MaxPool setting.
+     */
+    public int getMaxPool() {
+        return maxPool;
+    }
+    
+    /** Set the default QueryTimeout setting for all PersistenceManager instances
+     * obtained from this factory.
+     * @param queryTimeout the default QueryTimeout setting.
+     */
+    public void setQueryTimeout(int queryTimeout) {
+        assertConfigurable();
+        this.queryTimeout = queryTimeout;
+    }
+    
+    /** Get the default QueryTimeout setting for all PersistenceManager instances
+     * obtained from this factory.
+     * @return the default QueryTimeout setting.
+     */
+    public int getQueryTimeout() {
+        return queryTimeout;
+    }
+    
+    /** 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 = updateTimeout;
+    }
+    
+    /** Get the default UpdateTimeout setting for all PersistenceManager instances
+     * obtained from this factory.
+     * @return the default UpdateTimeout setting.
+     */
+    public int getUpdateTimeout() {
+        return updateTimeout;
+    }
+    
+    
+    /**
+     * Return "static" properties of this PersistenceManagerFactory.
+     * Properties with keys VendorName and VersionNumber are required.  Other
+     * keys are optional.
+     * @return the non-operational properties of this PersistenceManagerFactory.
+     */
+    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.
+     * <P>Each supported JDO optional feature is represented by a
+     * String with one of the following values:
+     *
+     * <P>javax.jdo.option.TransientTransactional
+     * <P>javax.jdo.option.NontransactionalRead
+     * <P>javax.jdo.option.NontransactionalWrite
+     * <P>javax.jdo.option.RetainValues
+     * <P>javax.jdo.option.Optimistic
+     * <P>javax.jdo.option.ApplicationIdentity
+     * <P>javax.jdo.option.DatastoreIdentity
+     * <P>javax.jdo.option.NonDatastoreIdentity
+     * <P>javax.jdo.option.ArrayList
+     * <P>javax.jdo.option.HashMap
+     * <P>javax.jdo.option.Hashtable
+     * <P>javax.jdo.option.LinkedList
+     * <P>javax.jdo.option.TreeMap
+     * <P>javax.jdo.option.TreeSet
+     * <P>javax.jdo.option.Vector
+     * <P>javax.jdo.option.Map
+     * <P>javax.jdo.option.List
+     * <P>javax.jdo.option.Array     
+     * <P>javax.jdo.option.NullCollection  
+     *
+     *<P>The standard JDO query language is represented by a String:
+     *<P>javax.jdo.query.JDOQL   
+     * @return the Set of String representing the supported Options
+     */    
+    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.
+     *
+     * <P>If pooling of PersistenceManager instances is supported by
+     * this factory, the instance might have been returned to the pool
+     * and is being reused. 
+     *
+     * <P>After the first use of getPersistenceManager, no "set" methods will
+     * succeed.
+     *
+     * @return a PersistenceManager instance with default options.
+     */
+    public PersistenceManager getPersistenceManager() {
+        return getPersistenceManager(null, null);
+    }
+        
+    /** 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.
+     *
+     * <P>If pooling of PersistenceManager instances is supported by
+     * this factory, the instance might have been returned to the pool
+     * and is being reused.
+     *
+     * <P>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 = configurable;
+
+        try {
+            if (wasConfigurable) {
+                
+                // 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. 
+                // This is a no-op in a non-managed environment, and
+                // if an appserver does not need any extra code here.
+                PersistenceManagerFactoryImpl pmf = 
+                    (PersistenceManagerFactoryImpl)EJBImplHelper.
+                        replacePersistenceManagerFactory(this);
+
+                if (pmf != this) {
+                    // Was replaced. Mark this PersistenceManagerFactory as 
+                    // configurable.
+                    setNonconfigured();
+                }
+                else {
+                    // register this PMF
+                    registeredPMFs.add(pmf);
+                }
+                
+                return pmf.getPersistenceManagerInternal(userid, password);
+            } 
+            // 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 = null;
+        javax.transaction.Transaction t = EJBImplHelper.getTransaction();
+ 
+        if (t != null) {
+            pm = (PersistenceManagerImpl)pmCache.get(t);
+            if (pm == null) {
+                // Not found
+                synchronized(pmCache) {
+                    pm = (PersistenceManagerImpl)pmCache.get(t);
+                    if (pm == null) {
+                        pm = 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.
+    
+                    Transaction tx = pm.currentTransaction();
+                    if (!tx.isActive()) {
+                        ((TransactionImpl)tx).begin(t);
+                    }
+                }
+            }      
+            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 = 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 = (PersistenceManagerImpl)pmCache.get(t);
+        if (pm1 == null) {
+            synchronized (pmCache) {
+                pm1 = (PersistenceManagerImpl)pmCache.get(t);
+                if (pm1 == null) {
+                    pmCache.put(t, pm);
+                    pm.setJTATransaction(t);
+                    return;
+                }
+            }
+        }
+
+        if (pm1 != 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 != null) {        // Managed environment
+            // Deregister 
+            PersistenceManagerImpl pm1 = (PersistenceManagerImpl)pmCache.get(t);
+            if (pm1 == null || pm1 != 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
+    //
+    
+    /**
+     * 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 = 
+        (PersistenceManagerImpl)createPersistenceManager(userid, password);
+        synchronized(closeLock) {
+            if (closed) {
+                throw new JDOUserException(
+                    msg.msg("EXC_PersistenceManagerFactoryClosed")); // NOI18N
+            }
+            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 PersistenceManagers.
+        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 that
+    // 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 = 0;
+        if (null != connectionFactory) {
+            rc = PERSIST_CF;
+        } else if (null != connectionFactoryName) {
+            rc = PERSIST_CF_NAME;
+        } else {
+            rc = 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 = 0;
+        if (null == props.get("javax.jdo.option.ConnectionURL")) { // NOI18N
+            rc = PERSIST_CF;
+        } else if (null != props.get("javax.jdo.option.ConnectionFactoryName")) { // NOI18N
+            rc = PERSIST_CF_NAME;
+        } else {
+            rc = 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
+     */    
+    protected void doWriteObject(java.io.ObjectOutputStream oos)
+        throws java.io.IOException {
+
+        writeObject(oos);
+    }
+    
+    private void writeObject(java.io.ObjectOutputStream oos)
+        throws java.io.IOException {
+        int kind = 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;
+        }                                        
+        oos.writeBoolean(multithreaded);
+        oos.writeBoolean(optimistic);
+        oos.writeBoolean(retainValues);
+        oos.writeBoolean(restoreValues);
+        oos.writeBoolean(nontransactionalRead);
+        oos.writeBoolean(nontransactionalWrite);
+        oos.writeBoolean(ignoreCache);
+        
+        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 loaded
+     */    
+    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 = ois.readInt();
+        switch (kind) {
+          case PERSIST_CF:
+              connectionFactory = ois.readObject();
+              break;
+              
+          case PERSIST_CF_NAME:
+              connectionFactoryName = ois.readUTF();
+              connectionFactory2Name = ois.readUTF();
+              break;
+              
+          case PERSIST_PROPS:
+              URL = (String)ois.readObject();
+              userName = (String)ois.readObject();
+              password = decrypt((String)ois.readObject());
+              driverName = (String)ois.readObject();
+              break;
+        }
+        multithreaded = ois.readBoolean();
+        optimistic = ois.readBoolean();
+        retainValues = ois.readBoolean();
+        restoreValues = ois.readBoolean();
+        nontransactionalRead = ois.readBoolean();
+        nontransactionalWrite = ois.readBoolean();
+        ignoreCache = ois.readBoolean();
+        
+        queryTimeout = ois.readInt();
+        updateTimeout = 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 {
+        
+        /** Returns a value from a PMF, turned into a String.
+         * @param pmf the PersistenceManagerFactory to get the property from
+         * @return  the property value associated with the Accessor key
+         */
+        public String get(PersistenceManagerFactoryImpl pmf);
+        
+        /** 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 from
+         * @return  the non-default property value associated with the
+         * Accessor key
+         */
+        public String getNonDefault(PersistenceManagerFactoryImpl pmf);
+        
+        /** Sets a value in a PMF, translating from String to the PMF's
+         * representation.
+         * @param pmf the PersistenceManagerFactory to set the property into
+         * @param s the property value associated with the Accessor key 
+         */
+        public void set(PersistenceManagerFactoryImpl pmf, String s);
+    }
+    
+    /**
+     * Tables which map from names to PMFAccessors.  The names are the same 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 = new HashMap(4);
+    
+    /**
+     *These PMFAccessors are for configuring non-connection properties.
+     */
+    protected static HashMap propsAccessors = new HashMap(10);
+    
+    /** Get JDO implementation-specific properties
+     * (not specified by JDO specification).
+     * @return a hashmap of accessors
+     */    
+    protected HashMap getLocalAccessors() {
+        return new HashMap();
+    }
+
+    /** Initialize the Accessor hashmaps for
+    * connection and non-connection properties.
+    * <br>
+    * XXX: Jikes bug
+    * <br>
+    * If this is protected, FOStorePMF.initPropsAccessors cannot invoke it,
+    * due to a bug in jikes
+    * (http://www-124.ibm.com/developerworks/bugs/?func=detailbug&bug_id=213&group_id=10)
+     */
+    //protected static void initPropsAccessors() {
+    public static void initPropsAccessors() {
+        if (pmfAccessors.size() != 0)
+            return;
+        synchronized (pmfAccessors) {
+            if (pmfAccessors.size() != 0)
+                return;
+            //
+            // PMF accessors
+            //
+
+            pmfAccessors.put(
+                "javax.jdo.option.ConnectionURL", // NOI18N
+                new PMFAccessor() {
+                public String get(PersistenceManagerFactoryImpl pmf) { return 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) { return 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) { return 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) { return 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) { return 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) { return 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) { return 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) { return 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) { return 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) { return 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) { return 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) { return pmf.getConnectionFactoryName(); }
+                public String getNonDefault(PersistenceManagerFactoryImpl pmf) { return (pmf.getConnectionFactoryName()==null)?null:pmf.getConnectionFactoryName(); }
+                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) { return pmf.getConnectionFactory2Name(); }
+                public String getNonDefault(PersistenceManagerFactoryImpl pmf) { return (pmf.getConnectionFactory2Name()==null)?null:pmf.getConnectionFactory2Name(); }
+                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 encounters
+     * a NumberFormatException.  If so, tell the user in the JDO-approved
+     * manner.
+     * @param s the input String
+     * @return the int representation of the String
+     */ 
+    protected static int toInt(String s) {
+        int rc = 0;
+        try {
+            rc = 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();
+    }
+
+    /** 
+     * Does not do assertConfigurable validation
+     * @see #getAsProperties()
+     */
+    protected Properties getAsPropertiesInternal() {
+        initPropsAccessors();
+        Properties p = new Properties();
+
+        int kind = 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 != 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;
+    }
+    
+    /** 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 value
+     * from the PMF and puts it in the given Properties object.
+     */
+    void setProps(Properties p, HashMap accessors) {
+        Set s = accessors.entrySet();
+        for (Iterator i = s.iterator(); i.hasNext();) {
+            Map.Entry e = (Map.Entry)i.next();
+            String key = (String)e.getKey();
+            PMFAccessor a = (PMFAccessor)e.getValue();
+            String value = (String)a.getNonDefault(this);
+            if (null != 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 = getSerializedForm (p);
+
+        switch (kind) {
+          case PERSIST_CF:
+              getCFFromProperties(p);
+              break;
+
+          case PERSIST_CF_NAME:
+              connectionFactoryName = p.getProperty(
+                  "javax.jdo.option.ConnectionFactoryName"); // NOI18N
+              connectionFactory2Name = 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 value
+     * from the Properties and sets that value in the PMF.
+     */
+    private void getProps(Properties p, HashMap accessors) {
+        Set s = accessors.entrySet();
+        for (Iterator i = s.iterator(); i.hasNext();) {
+            Map.Entry e = (Map.Entry)i.next();
+            String key = (String)e.getKey();
+            String value = p.getProperty(key);
+            if (null != value) {
+//                System.out.println("PersistenceManagerFactoryImpl setting property: " + key + " to: " + value); // NOI18N
+                PMFAccessor a = (PMFAccessor)e.getValue();
+                a.set(this, value);
+            }
+        }
+    }
+
+    /**
+     * Provides an encrypted version of the given string.
+     * <b>NOTE:</b>
+     * 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.
+     * <b>NOTE:</b>
+     * 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 implementation
+     * should set the PMF's connection factory with the newly created object.
+     * @param p Properties from which the ConnectionFactory is to be created.
+     */
+        // 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();
+    
+    /** The String representation of this PMF.
+     * @return the String representation of this PMF
+     */    
+    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" + // NOI18N
+            "nontransactionalWrite: " + nontransactionalWrite + "\n" + // NOI18N
+            "ignoreCache: " + ignoreCache + "\n" + // NOI18N
+            "queryTimeout: " + queryTimeout + "\n" + // NOI18N
+            "updateTimeout: " + updateTimeout + "\n"; // NOI18N
+    }
+    
+    /** 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.
+     */    
+    protected void verifyConfiguration() {
+        if ((!isConnectionFactoryConfigured()) &&
+            (connectionFactoryName == null) &&
+            (URL == null)) {
+            throw new JDOFatalUserException(msg.msg(
+                "EXC_IncompleteConfiguration")); // NOI18N
+        }
+    }
+        
+    /**
+     * 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 never
+     * change, except if setNonconfigured is called.
+     * @see #hashCode()
+     * @see #setNonconfigured()
+     */        
+    protected void setConfigured() {
+        configurable = false;
+        myHashCode = 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.
+     */        
+    protected void setNonconfigured() {
+        configurable = true;
+        myHashCode = 0;
+    }
+
+    /** Given an input Properties instance, add to the output Properties instance
+     * 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.
+     *
+     * <P>A properties instance will typically be filtered a number of times:
+     * once for the JDO standard PersistenceManagerFactory properties, another
+     * for the JDO implementation properties, and another for the implementation
+     * ConnectionFactory properties.
+     *
+     * <P>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 filtered,
+                                         Map accessors) {
+        Set s = accessors.entrySet();
+        for (Iterator i = s.iterator(); i.hasNext();) {
+            // for each accessor defined
+            Map.Entry e = (Map.Entry)i.next();
+            String key = (String)e.getKey();
+            // if the key in the accessor matches a property in the properties
+            String value = props.getProperty(key);
+            // and if the property is not null
+            if (null != value) {
+                Accessor a = (Accessor)e.getValue();
+                // and the value is not the default value for the accessor
+                if (a.getDefault() != value) {
+                    // set the property in the filtered properties
+                    filtered.setProperty (key, value);
+                }
+            }
+        }
+        return;
+    }
+
+    public synchronized boolean equals(Object o) {
+        if (o == 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 == myHashCode) {
+            return this.getAsPropertiesInternal().hashCode();
+        } else {
+            return myHashCode;
+        }
+    }
+    
+    /** Close this PersistenceManagerFactory. Check for
+     * JDOPermission("closePersistenceManagerFactory") and if not authorized,
+     * throw SecurityException.
+     * <P>If the authorization check succeeds, check to see that all
+     * PersistenceManager instances obtained from this PersistenceManagerFactory
+     * have no active transactions. If any PersistenceManager instances have
+     * an active transaction, throw a JDOUserException, with one nested
+     * JDOUserException for each PersistenceManager with an active Transaction.
+     * <P>If there are no active transactions, then close all PersistenceManager
+     * 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 = System.getSecurityManager();
+            if (secmgr != null) {
+                // checkPermission will throw SecurityException if not authorized
+                secmgr.checkPermission(JDOPermission.CLOSE_PERSISTENCE_MANAGER_FACTORY);
+            }
+            List activePersistenceManagers = getActivePersistenceManagers();
+            int size = activePersistenceManagers.size();
+            if (size != 0) {
+                Throwable[] thrown = new Throwable[size];
+                for (int i = 0; i < size; ++i) {
+                    PersistenceManagerImpl pm = 
+                        (PersistenceManagerImpl)activePersistenceManagers.get(i);
+                    thrown[i] = new JDOUserException(
+                        msg.msg("EXC_ActivePersistenceManager"), // NOI18N
+                        pm.getCurrentWrapper());
+                    }
+                throw new JDOUserException(
+                    msg.msg("EXC_ActivePersistenceManager"), thrown); // NOI18N
+            }
+            closeOpenPersistenceManagers();
+            // pmf is closed => remove it from collection of registered pmfs
+            registeredPMFs.remove(this);
+            setConfigured();
+            closed = true;
+        }
+    }
+    
+    /** 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 complete.
+     */
+    protected void assertNotClosed() {
+        synchronized(closeLock) {
+            if (closed) {
+                throw new JDOUserException(
+                    msg.msg("EXC_PersistenceManagerFactoryClosed")); // NOI18N
+            }
+        }
+    }
+    
+    /** Get all active PersistenceManagers.  This is all 
+     * PersistenceManagers that have active transactions.
+     */
+    protected List getActivePersistenceManagers() {
+        List pms = new ArrayList();
+        for (Iterator it=pmSet.iterator(); it.hasNext();) {
+            PersistenceManager pm = (PersistenceManager)it.next();
+            if (pm.currentTransaction().isActive()) {
+                pms.add(pm);
+            }
+        }
+        return pms;
+    }
+    
+    /** 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 = Arrays.asList(pmSet.toArray()); 
+        for (Iterator it=toClose.iterator(); it.hasNext();) {
+            PersistenceManagerImpl pm = (PersistenceManagerImpl)it.next();
+            pm.forceClose();
+        }
+    }
+    
+
+    /** Method called by the shudown hook to close pmf instances left open 
+     * when the JVM exits.
+     */
+    protected void shutdown() {
+        closeOpenPersistenceManagers();
+    }
+
+    /** Shutdown hook to close pmf instances left open when the JVM
+     * exits. 
+     */ 
+    static class ShutdownHook extends Thread {
+        public void run() {
+            for (Iterator i = registeredPMFs.iterator(); i.hasNext();) {
+                try {
+                    ((PersistenceManagerFactoryImpl)i.next()).shutdown();
+                }
+                catch (JDOException ex) {
+                    // ignore
+                }
+            }
+        }
+    }
+}
+