/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 * 
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License. You can obtain
 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
 * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
 * Sun designates this particular file as subject to the "Classpath" exception
 * as provided by Sun in the GPL Version 2 section of the License file that
 * accompanied this code.  If applicable, add the following below the License
 * Header, with the fields enclosed by brackets [] replaced by your own
 * identifying information: "Portions Copyrighted [year]
 * [name of copyright owner]"
 * 
 * Contributor(s):
 * 
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

/*
 * PersistenceManagerFactoryImpl.java
 *
 * Created on March 9, 2000
 */

package com.sun.jdo.spi.persistence.support.sqlstore.impl;

import java.util.ResourceBundle;
import java.util.Properties;
import java.io.PrintWriter;
import javax.sql.DataSource;

import com.sun.jdo.api.persistence.support.*;
import com.sun.jdo.spi.persistence.support.sqlstore.RuntimeVersion;
import com.sun.jdo.spi.persistence.utility.I18NHelper;
import com.sun.jdo.spi.persistence.support.sqlstore.ejb.EJBHelper;

/**
 *
 * @author  Marina Vatkina
 * @version 0.1
 */

public class PersistenceManagerFactoryImpl implements PersistenceManagerFactory
{
	/**
	 * PersistenceManagerFactory properties
	 */
	private String URL = null;
	private String userName = null;
	private String password = null;
	private String driverName = null;
	private ConnectionFactory connectionFactory = null;
	private Object dataSource = null;
	private String connectionFactoryName = null;
	private String identifier = null;
	private int connectionMaxPool = 0;
	private int connectionMinPool = 0;
	private int connectionMsInterval = 0;
	private int connectionLoginTimeout = 0;
	private int connectionMsWait = 0;
	private int txIsolation = -1;
	private PrintWriter connectionLogWriter;
	private boolean optimistic = true;
	private boolean retainValues = true;
	private boolean nontransactionalRead = true;
	private boolean ignoreCache = false;
	private int queryTimeout = 0;
	private int updateTimeout = 0;
	private int maxPool = 0;
	private int minPool = 0;
	private boolean supersedeDeletedInstance = true;
	private boolean requireCopyObjectId = true;
	private boolean requireTrackedSCO = true;

	private static final int NOT_SET = 0;
	private static final int SET_AS_CONNECTIONFACTORY = 1;
	private static final int SET_AS_DATASOURCE = 2;

	/** flag for ConnectionFactory setup
	 */
	private int providedConnectionFactory = 0;


	/**
	 * Reference to the internal PersistenceManagerFactory implementation
	 * to deligate actual stuff
	 */
	private transient SQLPersistenceManagerFactory pmFactory = null;

	/**
     	 * I18N message handler
     	 */
	private final static ResourceBundle messages = I18NHelper.loadBundle(
                                PersistenceManagerFactoryImpl.class);

	/**
	 * Creates new <code>PersistenceManagerFactoryImpl</code> without any user info
	 */
	public PersistenceManagerFactoryImpl() {
            EJBHelper.setPersistenceManagerFactoryDefaults(this);
	}

	/**
	 * Creates new <code>PersistenceManagerFactoryImpl</code> with user info
	 * @param URL		connection URL
	 * @param userName	database user
	 * @param password	database user password
	 * @param driverName	driver name
	 */
	public PersistenceManagerFactoryImpl(
			String URL,
			String userName,
			String password,
			String driverName)
	{
            EJBHelper.setPersistenceManagerFactoryDefaults(this);

		this.URL = URL;
		this.userName = userName;
		this.password = password;
		this.driverName = driverName;

	}

	/**
	 * Sets database user
	 * @param userName      database user
	 */
	public void setConnectionUserName (String userName) {
		assertNotConfigured();
		this.userName = userName;
	}

	/**
	 * Returns database user name
	 * @return	current database user name
	 */
	public String getConnectionUserName()
	{
		if (connectionFactory != null)
			return connectionFactory.getUserName();

		return userName;

	}

	/**
	 * Sets database user password
	 * @param password      database user password
	 */
	public void setConnectionPassword (String password) {
		assertNotConfigured();
		this.password = password;
	}


	/**
	 * Sets JDBC connection URL
	 * @param URL	connection URL
	 */
	public void setConnectionURL (String URL) {
		assertNotConfigured();
		this.URL = URL;
	}

	/**
	 * Returns connection URL
	 * @return	connection URL
	 */
	public String getConnectionURL()
	{
		if (connectionFactory != null)
                        return connectionFactory.getURL();

		return URL;

	}

	/**
	 * Sets JDBC driver name
	 * @param driverName	JDBC driver name
	 */
	public void setConnectionDriverName (String driverName) {
		assertNotConfigured();
		this.driverName = driverName;
	}

	/**
	 * Returns JDBC driver name
	 * @return	driver name
	 */
	public String getConnectionDriverName()
	{
		if (connectionFactory != null)
                        return connectionFactory.getDriverName();
		return driverName;

	}

	/**
	 * Sets ConnectionFactory that can be one of two types:
	 * <a href="ConnectionFactory.html">ConnectionFactory</a> or javax.sql.DataSource
	 * @param connectionFactory	as java.lang.Object
	 */
	public void setConnectionFactory (Object connectionFactory) {
		assertNotConfigured();
		if (connectionFactory == null) {
			this.connectionFactory = null;
			this.dataSource = null;
			providedConnectionFactory = NOT_SET;

		} else {
			if (EJBHelper.isManaged() || (connectionFactory instanceof DataSource)) {
				this.dataSource = connectionFactory;
				providedConnectionFactory = SET_AS_DATASOURCE;
			} else if (connectionFactory instanceof ConnectionFactory) {
				this.connectionFactory = (ConnectionFactory)connectionFactory;
				providedConnectionFactory = SET_AS_CONNECTIONFACTORY;
			} else {
				throw new JDOUserException(I18NHelper.getMessage( messages,
                                	"persistencemanagerfactoryimpl.wrongtype")); //NOI18N
			}
		}
	}

	/**
	 * Returns ConnectionFactory
	 * @return	ConnectionFactory
	 */
	public Object getConnectionFactory() {
		if (dataSource != null)
			return dataSource;

		return connectionFactory;
	}

        /**
         * Sets ConnectionFactory name
         * @param connectionFactoryName     ConnectionFactory name
         */
        public void setConnectionFactoryName (String connectionFactoryName)
	{
		assertNotConfigured();
		this.connectionFactoryName = connectionFactoryName;
	}

	/**
         * Returns ConnectionFactory name
         * @return      ConnectionFactoryName
         */
        public String getConnectionFactoryName ()
	{
		return connectionFactoryName;
	}

	/**
	 * Sets Identifier. An identifier is a string that user can use to identify
	 * the PersistenceManagerFactory in a given environment. Identifier can be
	 * particularly useful in an environment where multiple 
	 * PersistenceManagerFactories are initialized in a system.
	 * @param identifier
	 */
	public void setIdentifier(String identifier) {
		assertNotConfigured();
		this.identifier = identifier;
	}

	/**
	 * Gets Identifier. An identifier is a string that user can use to identify
	 * the PersistenceManagerFactory in a given environment. Identifier can be
	 * particularly useful in an environment where multiple 
	 * PersistenceManagerFactories are initialized in a system.
	 * @return identifier
	 */
	public String getIdentifier() {
		return identifier;
	}

    /**
         * Sets maximum number of connections in the connection pool
         * @param MaxPool	maximum number of connections
         */
	public  void setConnectionMaxPool (int MaxPool)
	{
		assertNotConfigured();
		this.connectionMaxPool = MaxPool;
	}


        /**
         * Returns maximum number of connections in the connection pool
         * @return      connectionMaxPool
         */
  	public int getConnectionMaxPool ()
	{
		if (connectionFactory != null)
                        return connectionFactory.getMaxPool();

		return connectionMaxPool;
	}

        /**
         * Sets minimum number of connections in the connection pool
         * @param MinPool	minimum number of connections
         */

	public  void setConnectionMinPool (int MinPool)
	{
		assertNotConfigured();
		this.connectionMinPool = MinPool;
	}


        /**
         * Returns minimum number of connections in the connection pool
         * @return      connectionMinPool
         */
  	public int getConnectionMinPool ()
	{
		if (connectionFactory != null)
                        return connectionFactory.getMinPool();
		return connectionMinPool;
	}


        /**
         * Sets the number of milliseconds to wait for an available connection
	 * from the connection pool before throwing an exception
         * @param MsWait	number in milliseconds
         */

  	public void setConnectionMsWait (int MsWait)
	{
		assertNotConfigured();
		this.connectionMsWait = MsWait;
	}


        /**
         * Returns the number of milliseconds to wait for an available connection
         * from the connection pool before throwing an exception
         * @return      number in milliseconds
         */
  	public int getConnectionMsWait ()
	{
		if (connectionFactory != null)
                        return connectionFactory.getMsWait();

		return connectionMsWait;
	}

        /**
         * Returns maximum number of PersistenceManager instances in the pool
         * @return maxPool
         */
	public  int getMaxPool () {
		return maxPool;
	}


        /**
         * Sets maximum number of PersistenceManager instances in the pool
         * @param MaxPool	maximum number of PersistenceManager instances
         */
	public  void setMaxPool (int MaxPool) {
		assertNotConfigured();
		this.maxPool = MaxPool;
	}

        /**
         * Returns minimum number of PersistenceManager instances in the pool
         * @return minPool
         */
	public  int getMinPool () {
		return minPool;
	}


        /**
         * Sets minimum number of PersistenceManager instances in the pool
         * @param MinPool	minimum number of PersistenceManager instances
         */
	public  void setMinPool (int MinPool) {
		assertNotConfigured();
		this.minPool = MinPool;
	}



        /**
         * Sets the amount of time, in milliseconds, between the connection
         * manager's attempts to get a pooled connection.
         * @param MsInterval	the interval between attempts to get a database
	 *			connection, in milliseconds.

         */
  	public void setConnectionMsInterval (int MsInterval)
	{
		assertNotConfigured();
		this.connectionMsInterval = MsInterval;
	}


        /**
         * Returns the amount of time, in milliseconds, between the connection
         * manager's attempts to get a pooled connection.
         * @return      the length of the interval between tries in milliseconds
         */
  	public int getConnectionMsInterval ()
	{
		if (connectionFactory != null)
                        return connectionFactory.getMsInterval();

		return connectionMsInterval;
	}


        /**
         * Sets the number of seconds to wait for a new connection to be
	 * established to the data source
         * @param LoginTimeout		 wait time in seconds
         */

  	public void setConnectionLoginTimeout (int LoginTimeout)
	{
		assertNotConfigured();
		this.connectionLoginTimeout = LoginTimeout;
	}


        /**
         * Returns the number of seconds to wait for a new connection to be
         * established to the data source
         * @return      wait time in seconds
         */
  	public int getConnectionLoginTimeout ()
	{
		if (connectionFactory != null) {
                        return connectionFactory.getLoginTimeout();
/*
		} else if (dataSource != null) {
                        return dataSource.getLoginTimeout();
*/
		} else {
			return connectionLoginTimeout;
		}
	}


	/**
         * Sets the LogWriter to which messages should be sent
	 * @param pw 		LogWriter
	 */
	public  void setConnectionLogWriter(PrintWriter pw)
	{
		assertNotConfigured();
		this.connectionLogWriter = pw;
	}

	/**
         * Returns the LogWriter to which messages should be sent
	 * @return      LogWriter
	 */
        public PrintWriter getConnectionLogWriter ()
	{
		return connectionLogWriter;
	}

    /**
     * Sets the queryTimeout for all PersistenceManagers
     * @param timeout the timout to be set
     */
    public void setQueryTimeout(String timeout)
    {
        setQueryTimeout(Integer.parseInt(timeout));
    }

	/**
   	 * Sets the number of seconds to wait for a query statement
   	 * to execute in the datastore associated with this PersistenceManagerFactory.
   	 * @param timeout          new timout value in seconds; zero means unlimited
   	 */
  	public void setQueryTimeout (int timeout)
	{
		assertNotConfigured();
		this.queryTimeout = timeout;
	}

  	/**
   	 * Gets the number of seconds to wait for a query statement
   	 * to execute in the datastore associated with this PersistenceManagerFactory.
   	 * @return      timout value in seconds; zero means unlimited
   	 */
  	public int getQueryTimeout ()
	{
		return queryTimeout;
	}

    /**
     * Sets the updateTimeout for all PersistenceManagers
     * @param timeout the timout to be set
     */
    public void setUpdateTimeout(String timeout)
    {
        setUpdateTimeout(Integer.parseInt(timeout));
    }

	/**
         * Sets the number of seconds to wait for an update statement
         * to execute in the datastore associated with this PersistenceManagerFactory.
         * @param timeout          new timout value in seconds; zero means unlimited
         */
        public void setUpdateTimeout (int timeout)
        {
                assertNotConfigured();
                this.updateTimeout = timeout;
        }

        /**
         * Gets the number of seconds to wait for an update statement
         * to execute in the datastore associated with this PersistenceManagerFactory.
         * @return      timout value in seconds; zero means unlimited
         */
        public int getUpdateTimeout()
        {
                return updateTimeout;
        }


	/**
   	 * Sets transaction isolation level for all connections of this PersistenceManagerFactory.
   	 * All validation is done by java.sql.Connection itself, so e.g. while Oracle
   	 * will not allow to set solation level to TRANSACTION_REPEATABLE_READ, this method
   	 * does not have any explicit restrictions
   	 *
   	 * @param level - one of the java.sql.Connection.TRANSACTION_* isolation values
   	 */
  	public void setConnectionTransactionIsolation (int level)
	{
		assertNotConfigured();
		txIsolation = level;
	}

  	/**
	 * Returns current transaction isolation level for connections of this PersistenceManagerFactory.
   	 * @return      the current transaction isolation mode value as java.sql.Connection.TRANSACTION_*
   	 */
  	public int getConnectionTransactionIsolation ()
	{
		if (connectionFactory != null)
                        return connectionFactory.getTransactionIsolation();

		return txIsolation;
	}

        /**
         * Sets the optimistic flag for all PersistenceManagers
	 * @param flag		String optimistic flag
	 */
	public void setOptimistic (String flag)
	{
		setOptimistic(Boolean.parseBoolean(flag));
	}

        /**
         * Sets the optimistic flag for all PersistenceManagers
	 * @param flag		boolean optimistic flag
	 */
	public void setOptimistic (boolean flag)
	{
		assertNotConfigured();
		optimistic = flag;

		// Adjust depending flags
		if (flag)
			 nontransactionalRead = flag;
	}

	/**
         * Returns the boolean value of the optimistic flag for all PersistenceManagers
	 * @return      boolean optimistic flag
	 */
  	public boolean getOptimistic ()
	{
		return optimistic;
	}

	/**
         * Sets the RetainValues flag for all PersistenceManagers
         * @param flag          String RetainValues flag
         */
        public void setRetainValues (String flag)
        {
                setRetainValues(Boolean.parseBoolean(flag));
        }

	/**
         * Sets flag that will not cause the eviction of persistent instances after transaction completion.
	 * @param flag          boolean flag passed
         */
  	public void setRetainValues (boolean flag)
	{
		assertNotConfigured();
		retainValues = flag;

		// Adjust depending flags
		if (flag) {
                        nontransactionalRead = flag;
		}
	}

        /**
         * Returns the boolean value for the flag that will not cause the eviction of persistent
	 * instances after transaction completion.
         * @return      boolean setting for the flag
         */
  	public boolean getRetainValues ()
	{
		return retainValues;
	}

        /**
         * Sets the NontransactionalRead flag for all PersistenceManagers
         * @param flag          String NontransactionalRead flag
         */
        public void setNontransactionalRead (String flag)
        {
                setNontransactionalRead(Boolean.parseBoolean(flag));
        }

        /**
         * Sets the flag that allows non-transactional instances to be managed in the cache.
         * @param flag          boolean flag passed
         */
  	public void setNontransactionalRead (boolean flag)
	{
		assertNotConfigured();
		nontransactionalRead = flag;

		// Adjust depending flags
		if (flag == false)
		{
			retainValues = flag;
			optimistic = flag;
		}
	}

        /**
         * Returns the boolean value for the flag that allows non-transactional instances
	 * to be managed in the cache.
         * @return      boolean setting for the flag
         */
  	public boolean getNontransactionalRead ()
	{
		return nontransactionalRead;
	}

        /**
         * Sets the IgnoreCache flag for all PersistenceManagers
         * @param flag          String IgnoreCache flag
         */
        public void setIgnoreCache (String flag)
        {
                setIgnoreCache(Boolean.parseBoolean(flag));
        }

	/**
         * Sets the flag that allows the user to request that queries be optimized to return
	 * approximate results by ignoring changed values in the cache.
         * @param flag          boolean flag passed
         */
  	public void setIgnoreCache (boolean flag)
	{
		assertNotConfigured();
		throw new JDOUnsupportedOptionException(I18NHelper.getMessage(messages,
                            "persistencemanagerfactoryimpl.notsupported")); //NOI18N
		//ignoreCache = flag;
	}

	/**
         * Returns the boolean value for the flag that allows the user to request that queries
	 * be optimized to return approximate results by ignoring changed values in the cache.
	 * @return      boolean setting for the flag
         */
  	public boolean getIgnoreCache ()
	{
		return ignoreCache;
	}


        /**
         * Returns non-operational properties to be available to the application via a Properties instance.
         * @return      Properties object
         */
  	public Properties getProperties ()
	{
		if (pmFactory != null)
		{
			return pmFactory.getProperties();
		}
		return RuntimeVersion.getVendorProperties(
			"/com/sun/jdo/spi/persistence/support/sqlstore/sys.properties"); //NOI18N
	}



	/**
	 * Creates new <a href="PersistenceManager.html">PersistenceManager</a> without extra info
	 * @return	the persistence manager
	 */
	public PersistenceManager getPersistenceManager() {
		return getPersistenceManager(null, null);
	}

    /**
     * Returns the boolean value of the supersedeDeletedInstance flag
     * for all PersistenceManagers. If set to true, deleted instances are
     * allowed to be replaced with persistent-new instances with the equal
     * Object Id.
     * @return      boolean supersedeDeletedInstance flag
     */
    public boolean getSupersedeDeletedInstance () {
        return supersedeDeletedInstance;
    }


    /**
     * Sets the supersedeDeletedInstance flag for all PersistenceManagers.
     * @param flag          boolean supersedeDeletedInstance flag
     */
    public void setSupersedeDeletedInstance (boolean flag) {
        assertNotConfigured();
        supersedeDeletedInstance = flag;
    }

    /**
      * Returns the default value of the requireCopyObjectId flag
      * for this PersistenceManagerFactoryImpl. If set to false, the PersistenceManager
      * will not create a copy of an ObjectId for <code>PersistenceManager.getObjectId(Object pc)</code>
      * and <code>PersistenceManager.getObjectById(Object oid)</code> requests.
      *
      * @see PersistenceManager#getObjectId(Object pc)
      * @see PersistenceManager#getObjectById(Object oid)
      * @return      boolean requireCopyObjectId flag
      */
     public boolean getRequireCopyObjectId() {
        return requireCopyObjectId;
    }


     /**
      * Sets the default value of the requireCopyObjectId flag.
      * If set to false, by default a PersistenceManager will not create a copy of
      * an ObjectId for <code>PersistenceManager.getObjectId(Object pc)</code>
      * and <code>PersistenceManager.getObjectById(Object oid)</code> requests.
      *
      * @see PersistenceManager#getObjectId(Object pc)
      * @see PersistenceManager#getObjectById(Object oid)
      * @param flag          boolean requireCopyObjectId flag
      */
     public void setRequireCopyObjectId (boolean flag) {
        assertNotConfigured();
        requireCopyObjectId = flag;
    }

    /**
     * Returns the boolean value of the requireTrackedSCO flag.
     * If set to false, by default the PersistenceManager will not create
     * tracked SCO instances for new persistent instances at commit with
     * retainValues set to true and while retrieving data from a datastore.
     *
     * @return      boolean requireTrackedSCO flag
     */
    public boolean getRequireTrackedSCO() {
        return requireTrackedSCO;
    }

    /**
     * Sets the requireTrackedSCO flag for this PersistenceManagerFactory.
     * If set to false, by default the PersistenceManager will not create tracked
     * SCO instances for new persistent instances at commit with retainValues set to true
     * and while retrieving data from a datastore.
     *
     * @param flag          boolean requireTrackedSCO flag
     */
    public void setRequireTrackedSCO (boolean flag) {
        assertNotConfigured();
        requireTrackedSCO = flag;
    }


  /**
   * Creates new <a href="PersistenceManager.html">PersistenceManager</a> with specific
   * username and password. Used to call ConnectionFactory.getConnection(String, String)
   * @param       username      datasource user
   * @param       passwd      datasource user password
   * @return      the persistence manager
   */
  public PersistenceManager getPersistenceManager (String username, String passwd){
	    synchronized (this) {

		if (pmFactory == null) {
		    // Nothing there yet. Check and create
			if (providedConnectionFactory == NOT_SET) {

			    if (connectionFactoryName == null) {

				// Validate that MsWait/MsInterval are correct
				assertConnectionWait();

				// only PMFactory was configured
				// Create a default ConnectionFactoryImpl and
				// set all the parameters. With 1st connection
				// which happens during configuration od SqlStore
				// the actaul connectionManager will be created
				connectionFactory = new ConnectionFactoryImpl();

				connectionFactory.setURL(URL);
				connectionFactory.setUserName(userName);
				connectionFactory.setPassword(password);
                                connectionFactory.setDriverName(driverName);
                                connectionFactory.setMinPool(connectionMinPool);
                                connectionFactory.setMaxPool(connectionMaxPool);

				// MsWait MUST be set BEFORE MsInterval
				connectionFactory.setMsWait(this.connectionMsWait);
				connectionFactory.setMsInterval(this.connectionMsInterval);
				connectionFactory.setLogWriter(this.connectionLogWriter);
				connectionFactory.setLoginTimeout(this.connectionLoginTimeout);
				if (txIsolation > 0)
					connectionFactory.setTransactionIsolation(txIsolation);
			    } else {
				// Do JNDI lookup
				try {
					javax.naming.InitialContext ctx =
						(javax.naming.InitialContext) Class.forName("javax.naming.InitialContext").newInstance(); //NOI18N
                                	Object o = ctx.lookup(connectionFactoryName);
					if (EJBHelper.isManaged() || (o instanceof DataSource))
                                		dataSource = o;
					else if (o instanceof ConnectionFactory)
                                		connectionFactory = (ConnectionFactory) o;
					else
						throw new JDOUserException(I18NHelper.getMessage(
							messages,
                                                "persistencemanagerfactoryimpl.wrongtype")); //NOI18N

				} catch (JDOException e) {
					throw e; 	// rethrow it.

				} catch (ClassNotFoundException e) {
					throw new JDOUserException(I18NHelper.getMessage(messages,
                                                "persistencemanagerfactoryimpl.initialcontext")); //NOI18N

				} catch (Exception e) {
					throw new JDOUserException(I18NHelper.getMessage(messages,
                                		"persistencemanagerfactoryimpl.lookup"), e); //NOI18N
				}
			    }
			}

			//If identifier is not yet set, set it to name of connection factory
			if(getIdentifier() == null) {
				setIdentifier(getConnectionFactoryName());
			}
			// create new
			pmFactory = new SQLPersistenceManagerFactory(this);
	    		// Check EJBHelper
			pmFactory =
                            (SQLPersistenceManagerFactory)EJBHelper.replaceInternalPersistenceManagerFactory(pmFactory);

		    }
		} // end synchronized (this)

		// Should not be called
		if (username != null && connectionFactory != null) {
			throw new JDOUnsupportedOptionException(I18NHelper.getMessage(messages,
				"persistencemanagerfactoryimpl.notsupported")); //NOI18N
		}

		return pmFactory.getPersistenceManager(username, passwd);

	}

    	/**
      	 * Sets default value of a known boolean property.
         *
      	 * @param name the name of the property to be set.
      	 * @param value the default boolean value.
         */
        public void setBooleanProperty(String name, boolean value) {
           // These if-else statements will be replaced by the JDO implementation...
           if (name.equals("optimistic")) { // NOI18N
               setOptimistic(value);

           } else if (name.equals("retainValues")) { // NOI18N
               setRetainValues(value);

           } else if (name.equals("nontransactionalRead")) { // NOI18N
               setNontransactionalRead(value);

           } else if (name.equals("ignoreCache")) { // NOI18N
               setIgnoreCache(value);

           } else if (name.equals("supersedeDeletedInstance")) { // NOI18N
               setSupersedeDeletedInstance(value);

           } else if (name.equals("requireCopyObjectId")) { // NOI18N
               setRequireCopyObjectId(value);

           } else if (name.equals("requireTrackedSCO")) { // NOI18N
               setRequireTrackedSCO(value);

           } // else ignore it.

	}

    	/**
      	 * Determines whether obj is a PersistenceManagerFactoryImpl with the same configuration
      	 *
      	 * @param obj The possibly null object to check.
      	 * @return true if obj is equal to this PersistenceManagerFactoryImpl; false otherwise.
      	 */
	public boolean equals(Object obj) {
       		if ((obj == null) || !(obj instanceof PersistenceManagerFactoryImpl)) {
			return false;
		}
       		PersistenceManagerFactoryImpl pmf = (PersistenceManagerFactoryImpl)obj;

		if (pmf.providedConnectionFactory == this.providedConnectionFactory) {
			if (pmf.providedConnectionFactory == SET_AS_CONNECTIONFACTORY) {
	  	 		return (pmf.connectionFactory.equals(this.connectionFactory) &&
                                       equalBooleanProperties(pmf));

			} else if (pmf.providedConnectionFactory == SET_AS_DATASOURCE) {
				return (pmf.dataSource.equals(this.dataSource) &&
                                       equalBooleanProperties(pmf));

			} else if (pmf.connectionFactoryName != null) {
				return (pmf.connectionFactoryName.equals(this.connectionFactoryName) &&
                                       equalBooleanProperties(pmf));

			}
			return (pmf.URL.equals(this.URL) && pmf.userName.equals(this.userName) &&
				pmf.password.equals(this.password) &&
				pmf.driverName.equals(this.driverName) &&
                                       equalBooleanProperties(pmf));
		}
		return false;
    	}

    	/**
         * Computes the hash code of this PersistenceManagerFactory.
         *
         * @return A hash code of the owning PersistenceManagerFactory as an int.
         */
        public int hashCode() {
                if (providedConnectionFactory == SET_AS_CONNECTIONFACTORY) {
			return connectionFactory.hashCode();
                } else if (providedConnectionFactory == SET_AS_DATASOURCE) {
			return dataSource.hashCode();
		} else if (connectionFactoryName != null) {
			return connectionFactoryName.hashCode();
                }
		return URL.hashCode() + userName.hashCode() + password.hashCode() + driverName.hashCode();
        }

	/**
	 * INTERNAL
	 * Asserts that change to the property is allowed
	 */
	private void assertNotConfigured() {
		if ( pmFactory != null) {
			throw new JDOUnsupportedOptionException(I18NHelper.getMessage(messages,
                                "persistencemanagerfactoryimpl.configured")); //NOI18N
		}
	}


	/**
	 * INTERNAL
	 * Asserts that MsWait and MsInterval are properly configured
	 */
	private void assertConnectionWait() {
		if ( connectionMsWait < 0 )
		{
			throw new JDOUserException(I18NHelper.getMessage(messages,
                                             "connection.connectionmanager.mswaitvalue")); // NOI18N
		}
		else if ( connectionMsInterval < 0 ||
			connectionMsInterval > connectionMsWait ||
			(connectionMsWait > 0 && connectionMsInterval == 0) )
		{
				throw new JDOUserException(I18NHelper.getMessage(messages,
                                             "connection.connectionmanager.msintervalvalue")); // NOI18N
		}
	}

        /**
         * Compares boolean setting on 2 PersistenceManagerFactory instances.
         *
         * @param pmf the PersistenceManagerFactory instance to compare with this instance.
         */
        private boolean equalBooleanProperties(PersistenceManagerFactory pmf) {
            return (pmf.getOptimistic() == optimistic &&
                       pmf.getRetainValues() == retainValues &&
                       pmf.getNontransactionalRead() == nontransactionalRead &&
                       pmf.getIgnoreCache() == ignoreCache &&
                       pmf.getSupersedeDeletedInstance() == supersedeDeletedInstance &&
                       pmf.getRequireCopyObjectId() == requireCopyObjectId &&
                       pmf.getRequireTrackedSCO() == requireTrackedSCO
                   );

        }
}
