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
+ }
+ }
+ }
+ }
+}
+