Package org.apache.derbyTesting.junit

Source Code of org.apache.derbyTesting.junit.TestConfiguration

/*
*
* Derby - Class TestConfiguration
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
package org.apache.derbyTesting.junit;

import java.io.File;
import java.io.FileOutputStream;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.security.*;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
import java.util.ArrayList;
import java.util.Hashtable;

import org.apache.derby.drda.NetworkServerControl;

import junit.extensions.TestSetup;
import junit.framework.Assert;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;

/**
* Class which holds information about the configuration of a Test.
*
* A configuration manages the pool of databases in use
* in <code>usedDbNames</code> property. One of those databases
* is supposed to be the default database. A new default database
* is added to the pool by <code>singleUseDatabaseDecorator</code> function.
* <br>
* Additional databases may be added by <code>additionalDatabaseDecorator</code>
* function. Each of the additional databases has its logical and physical name.
* Physical database name is automatically generated as 'singleUse/oneuseXX'
* where 'XX' is unique number. The logical database name is used to establish
* a connection to the database using
* a <code>TestConfiguration::openConnection(String logicalDatabaseName)</code>
* function.
* <br>
* The database files are supposed to be local and they will be
* removed by <code>DropDatabaseSetup</code>.
*
*/
public class TestConfiguration {
    /**
     * Default values for configurations
     */
    private final static String DEFAULT_DBNAME = "wombat";
    private final static String DEFAULT_DBNAME_SQL = "dbsqlauth";
   
    private final static String DEFAULT_USER_NAME = "APP";
    private final static String DEFAULT_USER_PASSWORD = "APP";
    public final static int    DEFAULT_PORT = 1527;
    private final static String DEFAULT_FRAMEWORK = "embedded";
    public final static String DEFAULT_HOSTNAME = "localhost";
    public final static String DEFAULT_SSL = "off";
    public final static int DEFAULT_JMX_PORT = 9999; // 9999 is arbitrary

    public  final   static  String  TEST_DBO = "TEST_DBO";

    private FileOutputStream serverOutput;
   
    /** Sleep for 500 ms before pinging the network server (again) */
    private static final int SLEEP_TIME = 1000;
           
    /**
     * Keys to use to look up values in properties files.
     */
    private final static String KEY_DBNAME = "databaseName";
    private final static String KEY_FRAMEWORK = "framework";
    private final static String KEY_USER_PASSWORD = "password";
    private final static String KEY_USER_NAME = "user";
    private final static String KEY_HOSTNAME = "hostName";
    private final static String KEY_PORT = "port";
    private final static String KEY_VERBOSE = "derby.tests.debug";   
    private final static String KEY_TRACE = "derby.tests.trace";
    private final static String KEY_SSL = "ssl";
    private final static String KEY_JMX_PORT = "jmxPort";
   
    /**
     * Simple count to provide a unique number for database
     * names.
     */
    private static int uniqueDB;


    /**
     * Default Derby test configuration object based
     * upon system properties set by the old harness.
     */
    private static final TestConfiguration DERBY_HARNESS_CONFIG =
        new TestConfiguration(getSystemProperties());
   
    /**
     * Default configuration for standalone JUnit tests,
     * an embedded configuration.
     */
    private static final TestConfiguration JUNIT_CONFIG
        = new TestConfiguration();
   
    /**
     * The default configuration.
     */
    private static final TestConfiguration DEFAULT_CONFIG;
   
    /**
     * Are we running in the harness, assume so if framework
     * was set so the
     */
    private static final boolean runningInDerbyHarness;
   
    static {
        boolean assumeHarness = false;
       
        // In the harness if the default configuration according
        // to system properties is not embedded.
        if (!DERBY_HARNESS_CONFIG.getJDBCClient().isEmbedded())
            assumeHarness = true;
       
        // Assume harness if database name is not default
        if (!DERBY_HARNESS_CONFIG.getDefaultDatabaseName().equals(DEFAULT_DBNAME))
            assumeHarness = true;
       
        // Assume harness if user name is not default
        if (!DERBY_HARNESS_CONFIG.getUserName().equals(DEFAULT_USER_NAME))
            assumeHarness = true;
       
        // If derby.system.home set externally at startup assume
        // running in harness
        if (BaseTestCase.getSystemProperty("derby.system.home") != null)
            assumeHarness = true;

        DEFAULT_CONFIG = assumeHarness ? DERBY_HARNESS_CONFIG : JUNIT_CONFIG;
        runningInDerbyHarness = assumeHarness;
       
        if (!assumeHarness) {
            final   File dsh = new File("system");

            AccessController.doPrivileged
            (new java.security.PrivilegedAction(){
                public Object run(){
                    BaseTestCase.setSystemProperty("derby.system.home",
                                                   dsh.getAbsolutePath());
                    return null;
                }
            }
             );           
        }
     }
   
    /**
     * Current configuration is stored in a ThreadLocal to
     * allow the potential for multiple tests to be running
     * concurrently with different configurations.
     */
    private static final ThreadLocal CURRENT_CONFIG = new ThreadLocal() {
        protected Object initialValue() {
            return DEFAULT_CONFIG;
        }
    };
  
    /**
     * Get this Thread's current configuraiton for running
     * the tests.
     * Note this call must only be used while a test is
     * running, they make no sense when setting up a suite.
     * A suite itself sets up which test configurations
     * the fixtures will run in.

     * @return TestConfiguration to use.
     */
    public static TestConfiguration getCurrent() {
        return (TestConfiguration) CURRENT_CONFIG.get();
    }
   
    /**
     * WORK IN PROGRESS
     * Set this Thread's current configuration for running tests.
     * @param config Configuration to set it to.
     */
    static void setCurrent(TestConfiguration config)
    {
        CURRENT_CONFIG.set(config);
    }
   
   
    /**
     * Return a Test suite that contains all the test fixtures
     * for the passed in class running in embedded and the
     * default client server configuration.
     * <BR>
     * Each set of embedded and set of client server tests
     * is decorated with a CleanDatabaseTestSetup.
     * <BR>
     * The client server configuration is setup using clientServerSuite
     */
    public static Test defaultSuite(Class testClass)
    {
        return defaultSuite(testClass, true);
    }

    /**
     * Does the work of "defaultSuite" as defined above.  Takes
     * a boolean argument to determine whether or not to "clean"
     * the test database before each suite.  If the resultant
     * suite is going to be wrapped inside a TestSetup that creates
     * database objects to be used throughout the tests, then the
     * cleanDB parameter should be "false" to prevent cleanup of the
     * database objects that TestSetup created.  For example, see
     * XMLBindingTest.suite().
     */
    public static Test defaultSuite(Class testClass, boolean cleanDB)
    {
         final TestSuite suite = new TestSuite(suiteName(testClass));
        
        if (cleanDB)
        {
            suite.addTest(new CleanDatabaseTestSetup(embeddedSuite(testClass)));
            suite.addTest(new CleanDatabaseTestSetup(clientServerSuite(testClass)));
        }
        else
        {
            suite.addTest(embeddedSuite(testClass));
            suite.addTest(clientServerSuite(testClass));
        }

        return (suite);
    }
    /**
     * Equivalent to "defaultSuite" as defined above, but assumes a server
     * has already been started.
     * <BR>
     * Does NOT decorate for running in embedded mode, only for running on
     * the already started server.
     * <BR>
     * Return a Test suite that contains all the test fixtures
     * for the passed in class running in client server configuration
     * on an already started server.
     * <BR>
     * The set of client server tests
     * is decorated with a CleanDatabaseTestSetup.
     * <BR>
     * The client server configuration is setup using clientExistingServerSuite
     */
    public static Test defaultExistingServerSuite(Class testClass)
    {
        return defaultExistingServerSuite(testClass, true);
    }
   
    /**
     * Does the work of "defaultExistingServerSuite" as defined above.  Takes
     * a boolean argument to determine whether or not to "clean"
     * the test database before each suite.  If the resultant
     * suite is going to be wrapped inside a TestSetup that creates
     * database objects to be used throughout the tests, then the
     * cleanDB parameter should be "false" to prevent cleanup of the
     * database objects that TestSetup created.
     * <BR>
     * Does NOT decorate for running in embedded mode, only for running on
     * an already started server.
     */
    public static Test defaultExistingServerSuite(Class testClass, boolean cleanDB)
    {
         final TestSuite suite = new TestSuite(suiteName(testClass));
        
        if (cleanDB)
        {
            suite.addTest(new CleanDatabaseTestSetup(clientExistingServerSuite(testClass)));
        }
        else
        {
            suite.addTest(clientExistingServerSuite(testClass));
        }

        return (suite);
    }

    /**
     * Return a Test suite that contains all the test fixtures
     * for the passed in class running in client server configuration
     * on an already started server on a given host and port number.
     * <BR>
     * Takes a boolean argument to determine whether or not to "clean"
     * the test database before each suite.  If the resultant
     * suite is going to be wrapped inside a TestSetup that creates
     * database objects to be used throughout the tests, then the
     * cleanDB parameter should be "false" to prevent cleanup of the
     * database objects that TestSetup created.
     * <BR>
     * Takes a String argument to specify which host the server runs on, and
     * takes an int argument to specify the port number to use.
     * <BR>
     * Does NOT decorate for running in embedded mode, only for running on
     * an already started server.
     * <BR>
     * The set of client server tests
     * is decorated with a CleanDatabaseTestSetup.
     * <BR>
     * The client server configuration is setup using clientExistingServerSuite
     */
    public static Test existingServerSuite(Class testClass,
            boolean cleanDB,
            String hostName,
            int portNumber)
    {
         final TestSuite suite = new TestSuite(suiteName(testClass));
        
        if (cleanDB)
        {
            suite.addTest(new CleanDatabaseTestSetup(
                    clientExistingServerSuite(testClass, hostName, portNumber)));
        }
        else
        {
            suite.addTest(clientExistingServerSuite(testClass, hostName, portNumber));
        }

        return (suite);
    }

    /**
     * Return a Test suite that contains all the test fixtures
     * for the passed in class running in embedded and client-
     * server *JDBC3* configurations.
     * <BR>
     * Each set of embedded and set of client server tests is
     * decorated with a CleanDatabaseTestSetup.
     * <BR>
     */
    public static Test forceJDBC3Suite(Class testClass)
    {
        final TestSuite suite = new TestSuite(suiteName(testClass));

        suite.addTest(
            new CleanDatabaseTestSetup(
                forceJDBC3Embedded(embeddedSuite(testClass))));

        suite.addTest(
            new CleanDatabaseTestSetup(
                forceJDBC3NetClient(clientServerSuite(testClass))));

        return (suite);
    }

    /**
     * Generate a suite name from a class name, taking
     * only the last element of the fully qualified class name.
     */
    private static String suiteName(Class testClass)
    {
        int lastDot = testClass.getName().lastIndexOf('.');
        String suiteName = testClass.getName();
        if (lastDot != -1)
            suiteName = suiteName.substring(lastDot + 1, suiteName.length());
       
        return suiteName;
    }
   
    /**
     * Create a suite for the passed test class that includes
     * all the default fixtures from the class.
      */
    public static Test embeddedSuite(Class testClass)
    {
        return new TestSuite(testClass,
                suiteName(testClass)+":embedded");
    }
   
    /**
     * Create a suite for the passed test class that includes
     * all the default fixtures from the class, wrapped in
     * a derbyClientServerDecorator.
     *
     */
    public static Test clientServerSuite(Class testClass)
    {          
        TestSuite suite = new TestSuite(testClass,
                suiteName(testClass)+":client");
        return clientServerDecorator(suite);
    }
    /**
     * Create a suite for the passed test class that includes
     * all the default fixtures from the class, wrapped in
     * a derbyClientServerDecorator with alternative port.
     *
     */

    public static Test clientServerSuiteWithAlternativePort(Class testClass) {
        TestSuite suite = new TestSuite(testClass, suiteName(testClass)
                + ":client");
        return clientServerDecoratorWithAlternativePort(suite);
    }

    /**
     * Equivalent to 'clientServerSuite' above, but assumes server is
     * already running.
     *
     */
    public static Test clientExistingServerSuite(Class testClass)
    {
        TestSuite suite = new TestSuite(testClass,
                suiteName(testClass)+":client");
        return defaultExistingServerDecorator(suite); // Will not start server and does not stop it when done!.
    }
   
    /**
     * Create a suite for the passed test class that includes
     * all the default fixtures from the class, wrapped in
     * a existingServerDecorator.
     * <BR>
     * Equivalent to 'clientServerSuite' above, but assumes server is
     * already running. Will also NOT shut down the server.
     *
     */
    public static Test clientExistingServerSuite(Class testClass, String hostName, int portNumber)
    {
        TestSuite suite = new TestSuite(testClass,
                suiteName(testClass)+":client");
        return existingServerDecorator(suite, hostName, portNumber);
               // Will not start server and does not stop it when done!.
    }

    /**
     * Return a decorator for the passed in tests that sets the
     * configuration for the client to be Derby's JDBC client
     * and to start the network server at setUp.
     * <BR>
     * The database configuration (name etc.) is based upon
     * the previous configuration.
     * <BR>
     * The previous TestConfiguration is restored at tearDown and
     * the network server is shutdown.
     * @param suite the suite to decorate
     */
    public static Test clientServerDecorator(Test suite)
    {
        Test test = new NetworkServerTestSetup(suite, false);
           
        return defaultServerDecorator(test);
    }
    /**
     * Wrapper to use the alternative port number.
     */
    public static Test clientServerDecoratorWithAlternativePort(Test suite) {
        Test test = new NetworkServerTestSetup(suite, false);

        return defaultServerDecoratorWithAlternativePort(test);
    }
    /**
     * Decorate a test to use suite's default host and port,
     * but assuming the server is already running.
     */
    public static Test defaultExistingServerDecorator(Test test)
    {
        // As defaultServerDecorator but assuming
        // server is already started.
        // Need to have client
        // and not running in J2ME (JSR169).
        if (!(Derby.hasClient())
                || JDBC.vmSupportsJSR169())
        {
            return new TestSuite("empty: no network server support in JSR169 (or derbyclient.jar missing).");
        }
       
        Test r =
                new ServerSetup(test, DEFAULT_HOSTNAME, DEFAULT_PORT);
        ((ServerSetup)r).setJDBCClient(JDBCClient.DERBYNETCLIENT);
       
        return r;
    }
  
    /**
     * Decorate a test to use suite's default host and port.
     */
    public static Test defaultServerDecorator(Test test)
    {
        // Need to have network server and client and not
        // running in J2ME (JSR169).
        if (!(Derby.hasClient() && Derby.hasServer())
                || JDBC.vmSupportsJSR169())
            return new TestSuite("empty: no network server support");

        //
        // This looks bogus to me. Shouldn't this get the hostname and port
        // which are specific to this test run (perhaps overridden on the
        // command line)?
        //
        return new ServerSetup(test, DEFAULT_HOSTNAME, DEFAULT_PORT);
    }
   /**
    * A variant of defaultServerDecorator allowing
    * non-default hostname and portnumber.
    */
    public static Test existingServerDecorator(Test test,
            String hostName, int PortNumber)
    {
        Test r =
                new ServerSetup(test, hostName, PortNumber);
        ((ServerSetup)r).setJDBCClient(JDBCClient.DERBYNETCLIENT);
        return r;
    }
  
    /**
     * Decorate a test to use suite's default host and Alternative port.
     */
    public static Test defaultServerDecoratorWithAlternativePort(Test test) {
        // Need to have network server and client and not
        // running in J2ME (JSR169).
        if (!(Derby.hasClient() && Derby.hasServer())
                || JDBC.vmSupportsJSR169())
            return new TestSuite("empty: no network server support");
        int port = getCurrent().getAlternativePort();

        //
        // This looks bogus to me. Shouldn't this get the hostname and port
        // which are specific to this test run (perhaps overridden on the
        // command line)?
        //
        return new ServerSetup(test, DEFAULT_HOSTNAME, port);
    }

    /**
     * Generate the unique database name for single use.
     */
    private static synchronized String generateUniqueDatabaseName()
    {
        // Forward slash is ok, Derby treats database names
        // as URLs and translates forward slash to the local
        // separator.
        String dbName = "singleUse/oneuse";
        dbName = dbName.concat(Integer.toHexString(uniqueDB++));
        return dbName;
    }

    /**
     * Decorate a test to use a new database that is created upon the
     * first connection request to the database and shutdown & deleted at
     * tearDown. The configuration differs only from the current configuration
     * by the list of used databases. The new database name
     * is generated automatically as 'singleUse/oneuseXX' where 'XX' is
     * the unique number. The generated database name is added at the end
     * of <code>usedDbNames</code> and assigned as a default database name.
     * This decorator expects the database file to be local so it
     * can be removed.
     * @param test Test to be decorated
     * @return decorated test.
     */
    public static TestSetup singleUseDatabaseDecorator(Test test)
    {
        String dbName = generateUniqueDatabaseName();

        return new DatabaseChangeSetup(new DropDatabaseSetup(test, dbName), dbName, dbName, true);
    }

    /**
     * Decorate a test to use a new database that is created upon the
     * first connection request to the database and deleted at
     * tearDown. In contrast to plain singleUseDatabaseDecorator, the
     * database is expected to be shutdown by the test.  The
     * configuration differs only from the current configuration by
     * the list of used databases. The new database name is generated
     * automatically as 'singleUse/oneuseXX' where 'XX' is the unique
     * number. The generated database name is added at the end of
     * <code>usedDbNames</code> and assigned as a default database
     * name.  This decorator expects the database file to be local so
     * it can be removed.
     * @param test Test to be decorated
     * @return decorated test.
     */
    public static TestSetup singleUseDatabaseDecoratorNoShutdown(Test test)
    {
        String dbName = generateUniqueDatabaseName();

        return new DatabaseChangeSetup(
            new DropDatabaseSetup(test, dbName)
            {
                protected void tearDown() throws Exception {
                    // test responsible for shutdown
                    removeDatabase();
                }
            },
            dbName, dbName, true);
    }

    /**
     * Decorate a test to use a new database that is created upon the
     * first connection request to the database and shutdown & deleted at
     * tearDown. The configuration differs only from the current configuration
     * by the list of used databases.
     * The passed database name is mapped to the generated database
     * name 'singleUse/oneuseXX' where 'XX' is the unique number.
     * (by generateUniqueDatabaseName). The generated database name is added
     * at the end of <code>usedDbNames</code>.
     * This decorator expects the database file to be local so it
     * can be removed.
     * @param test Test to be decorated
     * @param logicalDbName The logical database name. This name is used to identify
     * the database in openConnection(String logicalDatabaseName) method calls.
     * @return decorated test.
     */
    public static TestSetup additionalDatabaseDecorator(Test test, String logicalDbName)
    {
        return new DatabaseChangeSetup(new DropDatabaseSetup(test, logicalDbName),
                                       logicalDbName,
                                       generateUniqueDatabaseName(),
                                       false);
    }
   
    /**
     * Similar to additionalDatabaseDecorator except the database will
     * not be shutdown, only deleted. It is the responsibility of the
     * test to shut it down.
     *
     * @param test Test to be decorated
     * @param logicalDbName The logical database name. This name is
     *                      used to identify the database in
     *                      openConnection(String logicalDatabaseName)
     *                      method calls.
     * @return decorated test.
     */
    public static TestSetup additionalDatabaseDecoratorNoShutdown(
        Test test,
        String logicalDbName)
    {
        return new DatabaseChangeSetup(
            new DropDatabaseSetup(test, logicalDbName)
            {
                protected void tearDown() throws Exception {
                    // the test is responsible for shutdown
                    removeDatabase();
                }
            },
            logicalDbName,
            generateUniqueDatabaseName(),
            false);
    }

    /**
     * Decorate a test changing the default user name and password.
     * Typically used along with DatabasePropertyTestSetup.builtinAuthentication.
     * The tearDown method resets the default user and password value to
     * their previous settings.
     *
     * @param test Test to decorate
     * @param user New default user
     * @param password New password
     * @return decorated test
     *
     * @see DatabasePropertyTestSetup#builtinAuthentication(Test, String[], String)
     */
    public static Test changeUserDecorator(Test test, String user, String password)
    {
        return new ChangeUserSetup(test, user, password);
    }  
   
    /**
     * Decorate a test to use the default database that has
     * was created in SQL authorization mode.
     * The tearDown reverts the configuration to the previous
     * configuration.
     *
     * The database owner of this default SQL authorization mode
     * database is TEST_DBO. This decorator sets the default user
     * to be TEST_DBO.
     *
     * Tests can use this in conjunction with
     * DatabasePropertyTestSetup.builtinAuthentication
     * to set up BUILTIN authentication and changeUserDecorator
     * to switch users. The database owner TEST_DBO must be included
     * in the list of users provided to builtinAuthentication.
     * This decorator must be the outer one in this mode.
     * <code>
     * test = DatabasePropertyTestSetup.builtinAuthentication(test,
                new String[] {TEST_DBO,"U1","U2",},
                "nh32ew");
       test = TestConfiguration.sqlAuthorizationDecorator(test);
     * </code>
     * A utility version of sqlAuthorizationDecorator is provided
     * that combines the two setups.
     *
     * @param test Test to be decorated
     * @return decorated test.
     *
     * @see DatabasePropertyTestSetup#builtinAuthentication(Test, String[], String)
     */
    public static Test sqlAuthorizationDecorator(Test test)
    {      
        // Set the SQL authorization mode as a database property
        // with a modified DatabasePropertyTestSetup that does not
        // reset it.
        final Properties sqlAuth = new Properties();
        sqlAuth.setProperty("derby.database.sqlAuthorization", "true");
        Test setSQLAuthMode = new DatabasePropertyTestSetup(test,
                sqlAuth, true) {
            protected void tearDown() {
            }
        };
       
        return changeUserDecorator(
            new DatabaseChangeSetup(setSQLAuthMode, DEFAULT_DBNAME_SQL, DEFAULT_DBNAME_SQL, true),
            TEST_DBO, "dummy"); // DRDA doesn't like empty pw
    }


    /**
     * Same as sqlAuthorizationDecorator, except that the database is dropped
     * at teardown and the test is responsible for shutting down the database.
     *
     * @param test Test to be decorated
     * @return decorated test.
     *
     * @see TestConfiguration#sqlAuthorizationDecorator(Test test)
     */
    public static Test sqlAuthorizationDecoratorSingleUse(Test test)
    {
        // Set the SQL authorization mode as a database property
        // with a modified DatabasePropertyTestSetup that does not
        // reset it.
        final Properties sqlAuth = new Properties();
        sqlAuth.setProperty("derby.database.sqlAuthorization", "true");
        Test setSQLAuthMode = new DatabasePropertyTestSetup(test,
                                                            sqlAuth, true) {
                protected void tearDown() { }
            };


        setSQLAuthMode = new DatabaseChangeSetup(
            new DropDatabaseSetup(setSQLAuthMode, DEFAULT_DBNAME_SQL) {
                protected void tearDown() throws Exception {
                    // test responsible for shutdown
                    removeDatabase();
                }
            },
            DEFAULT_DBNAME_SQL, DEFAULT_DBNAME_SQL, true);

        return changeUserDecorator(setSQLAuthMode,
                                   TEST_DBO,
                                   "dummy"); // DRDA doesn't like empty pw
    }
   

    /**
     * Utility version of sqlAuthorizationDecorator that also sets
     * up authentication. A combination of
     * DatabasePropertyTestSetup.builtinAuthentication wrapped in
     * sqlAuthorizationDecorator.
     * <BR>
     * The database owner of this default SQL authorization mode
     * database is TEST_DBO. This decorator sets the default user
     * to be TEST_DBO.
     * <BR>
     * Assumption is that no authentication is enabled on the default
     * SQL authorization database on entry.
     *
     * @param users Set of users excluding the database owner, that will
     * be added by this decorator.
     */
    public static Test sqlAuthorizationDecorator(Test test,
            String[] users, String passwordToken)
    {
        String[] usersWithDBO = new String[users.length + 1];
        usersWithDBO[0] = TEST_DBO;
        System.arraycopy(users, 0, usersWithDBO, 1, users.length);
        return sqlAuthorizationDecorator(
            DatabasePropertyTestSetup.builtinAuthentication(test,
                    usersWithDBO, passwordToken));
    }
   
    /**
     * Return a decorator that changes the configuration to obtain
     * connections from a ConnectionPoolDataSource using
     * <code>getPooledConnection().getConnection()</code>
     * <p>
     * Note that statement pooling is enabled in the data source and in all the
     * connections created from it.
     * <p>
     * The tearDown reverts the configuration to the previous
     * configuration.
     *
     * @param test the test/suite to decorate
     * @return A test setup with the requested decorator.
     */
    public static TestSetup connectionCPDecorator(Test test)
    {
        return new ConnectorSetup(test,
             "org.apache.derbyTesting.junit.ConnectionPoolDataSourceConnector");
    }

    /**
     * Return a decorator that changes the configuration to obtain
     * connections from an XADataSource using
     * <code>
     * getXAConnection().getConnection()
     * </code>
     * The connection is not connected to any global transaction,
     * thus it is in local connection mode.
     * The tearDown reverts the configuration to the previous
     * configuration.
     */
    public static TestSetup connectionXADecorator(Test test)
    {
        return new ConnectorSetup(test,
                "org.apache.derbyTesting.junit.XADataSourceConnector");
    }
    /**
     * Return a decorator that changes the configuration to obtain
     * connections from a standard DataSource using
     * <code>
     * getConnection()
     * </code>
     * The tearDown reverts the configuration to the previous
     * configuration.
     */
    public static TestSetup connectionDSDecorator(Test test)
    {
        return new ConnectorSetup(test,
            "org.apache.derbyTesting.junit.DataSourceConnector");
    }
   
    /**
     * Returns a decorator that forces the JDBC 3 embedded client  in
     * a Java SE 6/JDBC 4 environment. The only difference is that
     * the DataSource class names will be the "old" JDBC 3 versions
     * and not the JDBC 4 specific ones.
     * that
     * @param test
     */
    public static Test forceJDBC3Embedded(Test test)
    {
        if (JDBC.vmSupportsJDBC4()) {
            test = new JDBCClientSetup(test, JDBCClient.EMBEDDED_30);
        }
        return test;
    }
   
    /**
     * Returns a decorator that forces the JDBC 3 network client in
     * a Java SE 6/JDBC 4 environment. The only difference is that
     * the DataSource class names will be the "old" JDBC 3 versions
     * and not the JDBC 4 specific ones.
     *
     * Assumption is that the received Test is an instance of ServerSetup,
     * which is the decorator for client server tests.  If that is not
     * the case then this method is a no-op.
     *
     * @param test Test around which to wrap the JDBC 3 network client
     *  configuration.
     */
    public static Test forceJDBC3NetClient(Test test)
    {
        if (JDBC.vmSupportsJDBC4() && (test instanceof ServerSetup))
            ((ServerSetup)test).setJDBCClient(JDBCClient.DERBYNETCLIENT_30);
        return test;
    }
   
    /**
     * Decorate a test changing the default ssl mode.
     * The tearDown method resets the default user and password value to
     * their previous settings.
     *
     * @param test Test to decorate
     * @param ssl New ssl mode
     * @return decorated test
     */
    public static Test changeSSLDecorator(Test test, String ssl)
    {
        return new ChangeSSLSetup(test, ssl);
    }  
   
    /**
     * Default embedded configuration
     *
     */
    private TestConfiguration() {
        this.defaultDbName = DEFAULT_DBNAME;
        usedDbNames.add(DEFAULT_DBNAME);
        logicalDbMapping.put(DEFAULT_DBNAME, DEFAULT_DBNAME);
        this.userName = DEFAULT_USER_NAME;
        this.userPassword = DEFAULT_USER_PASSWORD;
        this.hostName = null;
        this.port = -1;
        this.isVerbose = Boolean.valueOf(
            getSystemProperties().getProperty(KEY_VERBOSE)).
            booleanValue();
        this.doTrace = Boolean.valueOf(
            getSystemProperties().getProperty(KEY_TRACE)).
            booleanValue();
       
        this.jdbcClient = JDBCClient.getDefaultEmbedded();
        this.ssl = null;
        this.jmxPort = DEFAULT_JMX_PORT;
        url = createJDBCUrlWithDatabaseName(defaultDbName);
        initConnector(null);
    }

    /**
     * Obtain a new configuration identical to the passed one.
     */
    TestConfiguration(TestConfiguration copy)
    {
        this.defaultDbName = copy.defaultDbName;
        this.usedDbNames.addAll(copy.usedDbNames);
        logicalDbMapping.putAll(copy.logicalDbMapping);
        this.userName = copy.userName;
        this.userPassword = copy.userPassword;

        this.isVerbose = copy.isVerbose;
        this.doTrace = copy.doTrace;
        this.port = copy.port;
        this.jmxPort = copy.jmxPort;
       
        this.jdbcClient = copy.jdbcClient;
        this.hostName = copy.hostName;
       
        this.ssl = copy.ssl;

        this.url = copy.url;
        initConnector(copy.connector);
    }

    TestConfiguration(TestConfiguration copy, JDBCClient client,
            String hostName, int port)
    {
        this.defaultDbName = copy.defaultDbName;
        this.usedDbNames.addAll(copy.usedDbNames);       
        logicalDbMapping.putAll(copy.logicalDbMapping);
        this.userName = copy.userName;
        this.userPassword = copy.userPassword;

        this.isVerbose = copy.isVerbose;
        this.doTrace = copy.doTrace;
        this.port = port;
        this.jmxPort = copy.jmxPort;
       
        this.jdbcClient = client;
        this.hostName = hostName;

        this.ssl = copy.ssl;
       
        this.url = createJDBCUrlWithDatabaseName(defaultDbName);
        initConnector(copy.connector);
    }

    /**
     * Obtain a new configuration identical to the passed in
     * one except for the default user and password.
     * @param copy Configuration to copy.
     * @param user New default user
     * @param password New default password.
     */
    TestConfiguration(TestConfiguration copy, String user,
            String password, String passwordToken)
    {
        this.defaultDbName = copy.defaultDbName;
        this.usedDbNames.addAll(copy.usedDbNames);
        logicalDbMapping.putAll(copy.logicalDbMapping);
        this.userName = user;
        this.userPassword = password;
        this.passwordToken = passwordToken == null ?
                copy.passwordToken : passwordToken;

        this.isVerbose = copy.isVerbose;
        this.doTrace = copy.doTrace;
        this.port = copy.port;
        this.jmxPort = copy.jmxPort;
       
        this.jdbcClient = copy.jdbcClient;
        this.hostName = copy.hostName;

        this.ssl = copy.ssl;
       
        this.url = copy.url;
        initConnector(copy.connector);
    }

    /**
     * Obtain a new configuration identical to the passed in
     * one except for the default user and password.
     * @param copy Configuration to copy.
     * @param ssl New default ssl mode
     */
    TestConfiguration(TestConfiguration copy, String ssl)
    {
        this(copy);
        this.ssl = ssl;
    }

    /**
     * Obtain a new configuration identical to the passed in
     * one except for the database name. The passed database name
     * is added at the end of the list of used databases.
     * If the <code>defaulDb</code> parameter is <code>true</code>
     * the new database name is used as a default database.
     * @param copy Configuration to copy.
     * @param dbName New database name
     * @param defaultDb Indicates that the passed <code>dbName</code> is supposed
     * to be used as the default database name.
     */
    TestConfiguration(TestConfiguration copy, String logicalDbName,
                      String dbName, boolean defaultDb)
    {
        this.usedDbNames.addAll(copy.usedDbNames);
        this.usedDbNames.add(dbName);
        logicalDbMapping.putAll(copy.logicalDbMapping);

        // Can not use the same logical name for different database.
        // If this assert will make failures it might be safely removed
        // since having more physical databases accessible throught the same
        // logical database name will access only the last physical database
        Assert.assertTrue(logicalDbMapping.put(logicalDbName, dbName) == null);

        if (defaultDb) {
            this.defaultDbName = dbName;
        } else {
            this.defaultDbName = copy.defaultDbName;
        }
       
        this.userName = copy.userName;
        this.userPassword = copy.userPassword;

        this.isVerbose = copy.isVerbose;
        this.doTrace = copy.doTrace;
        this.port = copy.port;
        this.jmxPort = copy.jmxPort;
       
        this.jdbcClient = copy.jdbcClient;
        this.hostName = copy.hostName;

        this.ssl = copy.ssl;
       
        this.url = createJDBCUrlWithDatabaseName(this.defaultDbName);
        initConnector(copy.connector);
    }
 
    /**
     * This constructor creates a TestConfiguration from a Properties object.
     *
     * @throws NumberFormatException if the port specification is not an integer.
     */
    private TestConfiguration(Properties props)
        throws NumberFormatException {

        defaultDbName = props.getProperty(KEY_DBNAME, DEFAULT_DBNAME);
        usedDbNames.add(defaultDbName);
        logicalDbMapping.put(defaultDbName, defaultDbName);
        userName = props.getProperty(KEY_USER_NAME, DEFAULT_USER_NAME);
        userPassword = props.getProperty(KEY_USER_PASSWORD,
                                         DEFAULT_USER_PASSWORD);
        hostName = props.getProperty(KEY_HOSTNAME, DEFAULT_HOSTNAME);
        isVerbose = Boolean.valueOf(props.getProperty(KEY_VERBOSE)).booleanValue();
        doTrace =  Boolean.valueOf(props.getProperty(KEY_TRACE)).booleanValue();
        String portStr = props.getProperty(KEY_PORT);
        if (portStr != null) {
            try {
                port = Integer.parseInt(portStr);
            } catch (NumberFormatException nfe) {
                // We lose stacktrace here, but it is not important.
                throw new NumberFormatException(
                        "Port number must be an integer. Value: " + portStr);
            }
        } else {
            port = DEFAULT_PORT;
        }
        String jmxPortStr = props.getProperty(KEY_JMX_PORT);
        if (jmxPortStr != null) {
            try {
                jmxPort = Integer.parseInt(jmxPortStr);
            } catch (NumberFormatException nfe) {
                // We lose stacktrace here, but it is not important.
                throw new NumberFormatException(
                        "JMX Port number must be an integer. Value: "
                        + jmxPortStr);
            }
        } else {
            jmxPort = DEFAULT_JMX_PORT;
        }

        ssl = props.getProperty(KEY_SSL);
       
        String framework = props.getProperty(KEY_FRAMEWORK, DEFAULT_FRAMEWORK);
       
        if ("DerbyNetClient".equals(framework)) {
            jdbcClient = JDBCClient.DERBYNETCLIENT;
        } else if ("DerbyNet".equals(framework)) {
            jdbcClient = JDBCClient.DB2CLIENT;
        } else {
            jdbcClient = JDBCClient.getDefaultEmbedded();
        }
        url = createJDBCUrlWithDatabaseName(defaultDbName);
        initConnector(null);
    }

    /**
     * Get the system properties in a privileged block.
     *
     * @return the system properties.
     */
    private static final Properties getSystemProperties() {
        // Fetch system properties in a privileged block.
        Properties sysProps = (Properties)AccessController.doPrivileged(
                new PrivilegedAction() {
                    public Object run() {
                        return System.getProperties();
                    }
                });
        return sysProps;
    }

    /**
     * Create JDBC connection url, including the name of the database.
     *
     * @return JDBC connection url, without attributes.
     */
    private String createJDBCUrlWithDatabaseName(String name) {
        if (JDBC.vmSupportsJDBC2())
        {
            String url;
           if (jdbcClient.isEmbedded()) {
               url = jdbcClient.getUrlBase();
           } else {
               url = jdbcClient.getUrlBase() + hostName + ":" + port + "/";
           }
           return url.concat(name);
        }
        // No DriverManager support so no URL support.
        return null;
    }
   
    /**
     * Initialize the connection factory.
     * Defaults to the DriverManager implementation
     * if running JDBC 2.0 or higher, otherwise a
     * DataSource implementation for JSR 169.
     *
     */
    private void initConnector(Connector oldConnector)
    {
        if (oldConnector != null)
        {
            // Use the same type of connector as the
            // configuration we are copying from.
           
            try {
                connector = (Connector) Class.forName(
                  oldConnector.getClass().getName()).newInstance();
            } catch (Exception e) {
                Assert.fail(e.getMessage());
            }           
        }
        else if (JDBC.vmSupportsJDBC2())
        {
            try {
                connector = (Connector) Class.forName(
                  "org.apache.derbyTesting.junit.DriverManagerConnector").newInstance();
            } catch (Exception e) {
                Assert.fail(e.getMessage());
            }
           
        } else {
            connector = new DataSourceConnector();
        }
        connector.setConfiguration(this);
    }

    /**
     * Get configured JDBCClient object.
     *
     * @return JDBCClient
     */
    public JDBCClient getJDBCClient() {
        return jdbcClient;
    }
   
   
    /**
     * Return the jdbc url for connecting to the default database.
     *
     * @return JDBC url.
     */
    public String getJDBCUrl() {
        return url;
    }

    /**
     * Return the jdbc url for a connecting to the database.
     *
     * @param databaseName name of database.
     * @return JDBC connection url, including database name.
     */
    public String getJDBCUrl(String databaseName) {
        return createJDBCUrlWithDatabaseName(databaseName);
    }
   
    /**
     * Return the default database name.
     *
     * @return default database name.
     */
    public String getDefaultDatabaseName() {
        return defaultDbName;
    }
   
    /**
     * Return the physical name for a database
     * given its logical name.
     *
     * @return Physical name of the database.
     */
    String getPhysicalDatabaseName(String logicalName) {
        return (String) logicalDbMapping.get(logicalName);
    }

    /**
     * Return the user name.
     *
     * @return user name.
     */
    public String getUserName() {
        return userName;
    }
   
    /**
     * Return the user password.
     *
     * @return user password.
     */
    public String getUserPassword() {
        return userPassword;
    }

    /**
     * Return the host name for the network server.
     *
     * @return host name.
     */
    public String getHostName() {
        return hostName;
    }

    /**
     * Get port number for network server.
     *
     * @return port number.
     */
    public int getPort() {
        return port;
    }
    /**
     * Get an alternative port number for network server.
     *
     * @return port number.
     */

    public int getAlternativePort() {
        int possiblePort = getPort();
        if (!(possiblePort > 0))
            possiblePort = 1528;
        else
            possiblePort = getPort() + 1;
        return possiblePort;
    }
   
    /**
     * Gets the value of the port number that may be used for "remote"
     * JMX monitoring and management.
     * @return the port number on which the JMX MBean server is listening for
     *         connections
     */
    public int getJmxPort() {
        return jmxPort;
    }

    /**
     * Get ssl mode for network server
     *
     * @return ssl mode
     */
    public String getSsl() {
        return ssl;
    }

   
    /**
     * Open connection to the default database.
     * If the database does not exist, it will be created.
     * A default username and password will be used for the connection.
     *
     * @return connection to default database.
     */
    Connection openDefaultConnection()
        throws SQLException {
        return connector.openConnection();
    }
   
    /**
     * Open connection to the default database.
     * If the database does not exist, it will be created.
     *
     * @return connection to default database.
     */
    Connection openDefaultConnection(String user, String password)
        throws SQLException {
        return connector.openConnection(user, password);
    }

    /**
     * Open connection to the specified database.
     * If the database does not exist, it will be created.
     * A default username and password will be used for the connection.
     * Requires that the test has been decorated with
     * additionalDatabaseDecorator with the matching name.
     * The physical database name may differ.
     * @param logicalDatabaseName A logical database name as passed
     * to <code>additionalDatabaseDecorator</code> function.
     * @return connection to specified database.
     */
    Connection openConnection(String logicalDatabaseName)
        throws SQLException {
        String databaseName = getPhysicalDatabaseName(logicalDatabaseName);
        if (usedDbNames.contains(databaseName))
            return connector.openConnection(databaseName);
        else
            throw new SQLException("Database name \"" + logicalDatabaseName
                      + "\" is not in a list of used databases."
                      + "Use method TestConfiguration.additionalDatabaseDecorator first.");
    }

    /**
     * Shutdown the database for this configuration
     * assuming it is booted.
     *
     */
    public void shutdownDatabase()
    {
        try {
            connector.shutDatabase();
            Assert.fail("Database failed to shut down");
        } catch (SQLException e) {
             BaseJDBCTestCase.assertSQLState("Database shutdown", "08006", e);
        }
    }
   
    /**
     * Shutdown the engine for this configuration
     * assuming it is booted.
     * This method can only be called when the engine
     * is running embedded in this JVM.
     *
     */
    public void shutdownEngine()
    {
        try {
            connector.shutEngine();
            Assert.fail("Engine failed to shut down");
        } catch (SQLException e) {
             BaseJDBCTestCase.assertSQLState("Engine shutdown", "XJ015", e);
        }
    }   

   /**
     * stops the Network server for this configuration.
     *
     */
    public void stopNetworkServer() {
        try {
            NetworkServerControl networkServer =
                NetworkServerTestSetup.getNetworkServerControl();

            networkServer.shutdown();
            if (serverOutput != null) {
                serverOutput.close();
            }
        } catch(Exception e) {
            SQLException se = new SQLException("Error shutting down server");
            se.initCause(e);
        }
    }

   /**
     * starts the Networs server for this configuration.
     *
     */
    public void startNetworkServer() throws SQLException
    {
        Exception failException = null;
        try {
           
            NetworkServerControl networkServer =
                NetworkServerTestSetup.getNetworkServerControl();
     
       serverOutput = (FileOutputStream)
            AccessController.doPrivileged(new PrivilegedAction() {
                public Object run() {
                    File logs = new File("logs");
                    logs.mkdir();
                    File console = new File(logs, "serverConsoleOutput.log");
                    FileOutputStream fos = null;
                    try {
                        fos = new FileOutputStream(console.getPath(), true);
                    } catch (FileNotFoundException ex) {
                        ex.printStackTrace();
                    }
                    return fos;
                }
            });

            networkServer.start(new PrintWriter(serverOutput));

            // Wait for the network server to start
            boolean started = false;
            int retries = 10;         // Max retries = max seconds to wait

            while (!started && retries > 0) {
                try {
                    // Sleep 1 second and then ping the network server
                    Thread.sleep(SLEEP_TIME);
                    networkServer.ping();

                    // If ping does not throw an exception the server has started
                    started = true;
                } catch(Exception e) {      
                    retries--;
                    failException = e;
                 }
               
             }

            // Check if we got a reply on ping
            if (!started) {
                 throw failException;
            }
        } catch (Exception e) {
            SQLException se = new SQLException("Error starting network  server");
            se.initCause(failException);
            throw se;
      }
    }
    /**
     * Set the verbosity, i.e., whether debug statements print.
     */
    public void  setVerbosity( boolean isChatty )  { isVerbose = isChatty; }
    /**
     * Set JUnit test method tracing.
     */
    public void setTrace( boolean isChatty )    { doTrace = isChatty; }
   
    /**
     * Return verbose flag.
     *
     * @return verbose flag.
     */
    public boolean isVerbose() {
        return isVerbose;
    }
    /**
     * Return JUnit test method trace flag.
     *
     * @return JUnit test method trace flag.
     */
    public boolean doTrace() {
        return doTrace;
    }

  /**
   * <p>
   * Return true if we classes are being loaded from jar files. For the time
   * being, this simply tests that the JVMInfo class (common to the client and
   * the server) comes out of a jar file.
   * </p>
   */
  public static boolean loadingFromJars()
  {
        return SecurityManagerSetup.isJars;
  }
   
    /**
     * Returns true if this JUnit test being run by the old harness.
     * Temp method to ease the switch over by allowing
     * suites to alter their behaviour based upon the
     * need to still run under the old harness.
     */
    public static boolean runningInDerbyHarness()
    {
        return runningInDerbyHarness;
    }
   
    /**
     * Get a folder already created where a test can
     * write its failure information. The name of the folder,
     * relative to ${user.dir} is:
     * <BR>
     * <code>
     * fail/client/testclass/testname
     * <code>
     * <UL>
     * <LI> client - value of JDBCClient.getName() for the test's configuration
     * <LI> testclass - last element of the class name
     * <LI> testname - value of test.getName()
     *  </UL>
     */
    File getFailureFolder(TestCase test){
       
        StringBuffer sb = new StringBuffer();
     
        sb.append("fail");
        sb.append(File.separatorChar);
        sb.append(getJDBCClient().getName());
        sb.append(File.separatorChar);
       
        String className = test.getClass().getName();
        int lastDot = className.lastIndexOf('.');
        if (lastDot != -1)
            className = className.substring(lastDot+1, className.length());
       
        sb.append(className);
        sb.append(File.separatorChar);
        sb.append(test.getName());
       
        String base = sb.toString().intern();
        final File folder = new File(base);
       
        // Create the folder
        // TODO: Dump this configuration in some human readable format
        synchronized (base) {
           
            AccessController.doPrivileged
            (new java.security.PrivilegedAction(){
                public Object run(){
                    if (folder.exists()) {
                        // do something
                    }           
                    return new Boolean(folder.mkdirs());
                }
            }
             );           
        }
              
        return folder;
       
    }
   
    /*
     * Immutable data members in test configuration
     */
   
    /** The default database name for tests. */
    private final String defaultDbName;
    /** Holds the names of all other databases used in a test to perform a proper cleanup.
     * The <code>defaultDbName</code> is also contained here.  */
    private final ArrayList usedDbNames = new ArrayList();
    /** Contains the mapping of logical database names to physical database names. */
    private final Hashtable logicalDbMapping = new Hashtable();
    private final String url;
    private final String userName;
    private final String userPassword;
    private final int port;
    private final String hostName;
    private final JDBCClient jdbcClient;
    private final int jmxPort;
    private boolean isVerbose;
    private boolean doTrace;
    private String ssl;
   
    /**
     * Password token used by the builtin authentication decorators.
     * Default simple scheme is the password is a function
     * of the user and a password token. password token
     * is set by DatabasePropertyTestSetup.builtinAuthentication
     */
    private String passwordToken = "";
   
    /**
     * Indirection for obtaining connections based upon
     * this configuration.
     */
    Connector connector;
   
    /*
     * SecurityManager related configuration.
     */
   
    /**
     * Install the default security manager setup,
     * for the current configuration.
     * @throws PrivilegedActionException
     */
    boolean defaultSecurityManagerSetup() {
     
      // Testing with the DB2 client has not been performed
      // under the security manager since it's not part
      // of Derby so no real interest in tracking down issues.
      if (jdbcClient.isDB2Client()) {
        SecurityManagerSetup.noSecurityManager();
        return false;
      } else {
            if ("<NONE>".equals(
                    BaseTestCase.getSystemProperty("java.security.policy")))
            {
                // Explict setting of no security manager
                return false;
            }
        SecurityManagerSetup.installSecurityManager();
        return true;
      }
    }
   
   
    /*
    ** BUILTIN password handling.
    */
   
    /**
     * Get the password that is a function of the user
     * name and the passed in token.
     */
    static final String getPassword(String user, String token)
    {
        return user.concat(token);
    }
   
    /**
     * Get the password that is a function of the user
     * name and the token for the current configuration.
     */
    final String getPassword(String user)
    {
        return getPassword(user, passwordToken);
    }
}
TOP

Related Classes of org.apache.derbyTesting.junit.TestConfiguration

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.