/**
 *  Derby - Class org.apache.derbyTesting.functionTests.tests.lang.AggregateClassLoadingTest
 *  
 * 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.functionTests.tests.lang;

import java.net.URL;
import java.net.URLClassLoader;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import junit.framework.Test;
import org.apache.derbyTesting.junit.BaseJDBCTestCase;
import org.apache.derbyTesting.junit.BaseTestSuite;
import org.apache.derbyTesting.junit.CleanDatabaseTestSetup;
import org.apache.derbyTesting.junit.JDBC;
import org.apache.derbyTesting.junit.SecurityManagerSetup;

/**
 * Test for ensuring the aggregate implementation classes are loaded correctly,
 * even when the context class loader loads Derby engine classes as well. This
 * is a typical situation we have seen with J2EE servers where Derby may be in
 * the application WAR and provided as a system service by the container. <BR>
 * Jira issue DERBY-997 <BR>
 * Assumes embedded and only needs to be run in embedded, since all class
 * loading happens on the engine side.
 */
public class AggregateClassLoadingTest extends BaseJDBCTestCase {
	
	/**
	 * Basic constructor.
	 */	
	public AggregateClassLoadingTest(String name) {
		super(name);
	}
	
	/**
	 * Sets the auto commit to false.
	 */
	protected void initializeConnection(Connection conn) throws SQLException {
		conn.setAutoCommit(false);
	}
	
	/**
	 * Returns the implemented tests.
	 * 
	 * @return An instance of <code>Test</code> with the implemented tests to
	 *         run.
	 */
	public static Test suite() {
		
		
		/* this test creates a class loader, adding that permission to
		 * derbyTesting.jar would mean that permission was granted all
		 * the way up the stack to the derby engine. Thus increasing
		 * the chance that incorrectly a privileged block could be dropped
		 * but the tests continue to pass. 
		 */		
		return SecurityManagerSetup.noSecurityManager(
            new CleanDatabaseTestSetup(
                new BaseTestSuite(AggregateClassLoadingTest.class,
                                   "AggregateClassLoadingTest")) {
                            
                            /**
                             * Save the class loader upon entry to the
                             * suite, some JVM's install the main loader
                             * as the context loader.
                             */
                            private ClassLoader originalLoader;
                            protected void setUp() throws Exception {                    
                                originalLoader = Thread.currentThread().getContextClassLoader();
                                super.setUp();
                            }
							protected void tearDown() throws Exception {
								Thread.currentThread().setContextClassLoader(originalLoader);
								super.tearDown();
							}

							/**
							 * @see org.apache.derbyTesting.junit.CleanDatabaseTestSetup#decorateSQL(java.sql.Statement)
							 */
							protected void decorateSQL(Statement s)
									throws SQLException {
								s.execute("create table t (i int)");
								s.execute("insert into t values 1,2,3,4,5,6,null,4,5,456,2,4,6,7,2144,44,2,-2,4");

								/*
								 * Find the location of the code for the Derby
								 * connection. The rest of the engine will be at
								 * the same location!
								 */
								URL derbyURL = s.getConnection().getClass().getProtectionDomain().getCodeSource()
										.getLocation();

								/*
								 * Create a new loader that loads from the same
								 * location as the engine. Create it without a
								 * parent, otherwise the parent will be the
								 * class loader of this class which is most
								 * likely the same as the engine. Since the
								 * class loader delegates to its parent first
								 * the bug would not show, as all the derby
								 * engine classes would be from a single loader.
								 */
                                URLClassLoader cl = new URLClassLoader(new URL[] { derbyURL }, null);
								Thread.currentThread().setContextClassLoader(cl);

								super.decorateSQL(s);
							}
						});
		
	}		
		
	public void testAggregateMAX() throws SQLException {
		testAggregate("select MAX(i) from t");
	}
	
	public void testAggregateMIN() throws SQLException {
		testAggregate("select MIN(i) from t");
	}
	
	public void testAggregateAVG() throws SQLException {
		testAggregate("select AVG(i) from t");
	}
		
	public void testAggregateCOUNT() throws SQLException {
		testAggregate("select COUNT(i) from t");
	}
	
	public void testAggregateCOUNT2() throws SQLException {
		testAggregate("select COUNT(*) from t");
	}
	
    /**
     * Just run and display the aggregates result.
     * 
     * Test some aggregates, their generated class will attempt
	 * to load the internal aggregate through the context loader
	 * first, and then any remaining loader.
     */
    private void testAggregate(String query) throws SQLException {
		Statement s = createStatement();
        
        JDBC.assertDrainResults(s.executeQuery(query), 1);

        s.close();
   }	
}
