/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 2010-2012 Oracle and/or its affiliates. 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 * http://glassfish.java.net/public/CDDL+GPL_1_1.html * or packager/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 packager/legal/LICENSE.txt. * * GPL Classpath Exception: * Oracle designates this particular file as subject to the "Classpath" * exception as provided by Oracle in the GPL Version 2 section of the License * file that accompanied this code. * * Modifications: * If applicable, add the following below the License Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyright [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. */ package com.sun.jersey.test.framework; import java.net.URI; import java.util.logging.Logger; import javax.ws.rs.core.UriBuilder; import com.sun.jersey.api.client.Client; import com.sun.jersey.api.client.WebResource; import com.sun.jersey.api.client.config.ClientConfig; import com.sun.jersey.api.client.filter.LoggingFilter; import com.sun.jersey.spi.service.ServiceFinder; import com.sun.jersey.test.framework.spi.client.ClientFactory; import com.sun.jersey.test.framework.spi.container.TestContainer; import com.sun.jersey.test.framework.spi.container.TestContainerException; import com.sun.jersey.test.framework.spi.container.TestContainerFactory; import org.junit.After; import org.junit.Before; /** * An abstract JUnit 4.x-based unit test class for testing JAX-RS and * Jersey-based applications. * <p> * At construction this class will obtain a test container factory, of type * {@link TestContainerFactory}, and use that to obtain a configured test * container, of type {@link TestContainer}. * <p> * Before a test method, in an extending class, is run the * {@link TestContainer#start() } method is invoked. After the test method has * run the {@link TestContainer#stop() } method is invoked. * The test method can invoke the {@link #resource() } to obtain a * {@link WebResource} from which requests may be sent to and responses received * from the Web application under test. * <p> * If a test container factory is not explicitly declared using the appropriate * constructor (see {@link #JerseyTest(TestContainerFactory) }) then a default * test container factory will be obtained as follows. * If the system property {@literal jersey.test.containerFactory} is set and the * value is a fully qualified class name of a class that extends from * {@link TestContainerFactory} then the default test container factory will * be an instance of that class. The exception {@link TestContainerException} * will be thrown if the class cannot be loaded or instantiated. * If the system property {@literal jersey.test.containerFactory} is not set then * the default test container factory will be an instance of * {@value JerseyTest#DEFAULT_TEST_CONTAINER_FACTORY_CLASS_NAME}. * The exception {@link TestContainerException} will be thrown if this class * cannot be loaded or instantiated. * <p> * The test container is configured from an application descriptor, of type * {@link AppDescriptor}. The exception {@link TestContainerException} * will be thrown if the test container cannot support the application * descriptor. * An application descriptor is built from an application descriptor builder. * Two application descriptor builders are provided: * <ol> * <li>A low-level builder, of type {@link LowLevelAppDescriptor.Builder}, * compatible with low-level test containers that do not support Servlets.</li> * <li>A web-based builder, of type {@link WebAppDescriptor.Builder}, * compatible with web-based test containers that support Servlets.</li> * </ol> * An application descriptor of type {@link WebAppDescriptor} may be * transformed to an application descriptor of type {@link LowLevelAppDescriptor} * if the state of the former is compatible with a low-level description. * <p> * The following low-level test container factories are provided: * <ul> * <li>{@link GrizzlyTestContainerFactory} for testing with the low-level * Grizzly HTTP container.</li> * <li>{@link HTTPContainerFactory} for testing with the Light Weight HTTP * server distributed with Java SE 6.</li> * <li>{@link InMemoryTestContainerFactory} for testing in memory without * using underlying HTTP client and server side functionality * to send requests and receive responses.</li> * </ul> * The following Web-based test container factories are provided: * <ul> * <li>{@link GrizzlyWebTestContainerFactory} for testing with the Grizzly * Web container and Servlet support.</li> * <li>{@link EmbeddedGlassFishTestContainerFactory} for testing with * embedded GlassFish.</li> * <li>{@link ExternalTestContainerFactory} for testing when the Web * application is independently deployed in a separate JVM to that of the * tests. For example, the application may be deployed to the * Glassfish v2 or v3 application server.</li> * </ul> * * @author Paul Sandoz (paul.sandoz at oracle.com) * @author Srinivas Bhimisetty */ public abstract class JerseyTest { /** * Holds the default test container factory class to be used for running the * tests. */ private static Class<? extends TestContainerFactory> defaultTestContainerFactoryClass; /** * The test container factory which creates an instance of the test container * on which the tests would be run. */ private TestContainerFactory testContainerFactory; private ClientFactory clientFactory; /** * The test container on which the tests would be run. */ private final TestContainer tc; /** * Client instance for creating {@link WebResource} instances and configuring * the properties of connections and requests. */ private final Client client; /** * An extending class must implement the {@link #configure()} method to * provide an application descriptor. * * @throws TestContainerException if the default test container factory * cannot be obtained, or the application descriptor is not * supported by the test container factory. */ public JerseyTest() throws TestContainerException { AppDescriptor ad = configure(); this.tc = getContainer(ad, getTestContainerFactory()); this.client = getClient(tc, ad); } /** * Construct a new instance with a test container factory. * <p> * An extending class must implement the {@link #configure()} method to * provide an application descriptor. * * @param testContainerFactory the test container factory to use for testing. * @throws TestContainerException if the application descriptor is not * supported by the test container factory. */ public JerseyTest(TestContainerFactory testContainerFactory) { setTestContainerFactory(testContainerFactory); AppDescriptor ad = configure(); this.tc = getContainer(ad, getTestContainerFactory()); this.client = getClient(tc, ad); } /** * Return an application descriptor that defines how the test container * is configured. * <p> * If a constructor is utilized that does not supply an application * descriptor then this method must be overridden to return an application * descriptor, otherwise an {@link UnsupportedOperationException} exception * will be thrown. * <p> * If a constructor is utilized that does supply an application descriptor * then this method does not require to be overridden and will not be * invoked. * * @return the application descriptor. */ protected AppDescriptor configure() { throw new UnsupportedOperationException( "The configure method must be implemented by the extending class"); } /** * Construct a new instance with an application descriptor that defines * how the test container is configured. * * @param ad an application descriptor describing how to configure the * test container. * @throws TestContainerException if the default test container factory * cannot be obtained, or the application descriptor is not * supported by the test container factory. */ public JerseyTest(AppDescriptor ad) throws TestContainerException { this.tc = getContainer(ad, getTestContainerFactory()); this.client = getClient(tc, ad); } /** * Construct a new instance with an array or a colon separated * list of package names which contain resource and provider classes. * <p> * This constructor builds an instance of {@link WebAppDescriptor} passing * the package names to the constructor. * * @param packages array or a colon separated list of package names which * contain resource and provider classes. * @throws TestContainerException if the default test container factory * cannot be obtained, or the built application descriptor is not * supported by the test container factory. */ public JerseyTest(String... packages) throws TestContainerException { this(new WebAppDescriptor.Builder(packages).build()); } /** * Sets the test container factory to to be used for testing. * * @param testContainerFactory the test container factory to to be used for * testing. */ protected void setTestContainerFactory(TestContainerFactory testContainerFactory) { this.testContainerFactory = testContainerFactory; } /** * Get the test container factory. * <p> * If the test container factory has not been explicit set with * {@link #setTestContainerFactory(TestContainerFactory) } then * the default test container factory will be obtained. * <p> * If the system property {@literal jersey.test.containerFactory} is set and the * value is a fully qualified class name of a class that extends from * {@link TestContainerFactory} then the default test container factory will * be an instance of that class. The exception {@link TestContainerException} * will be thrown if the class cannot be loaded or instantiated. * If the system property {@literal jersey.test.containerFactory} is not set then * the default test container factory will be an instance of * {@value JerseyTest#DEFAULT_TEST_CONTAINER_FACTORY_CLASS_NAME}. * The exception {@link TestContainerException} will be thrown if this class * cannot be loaded or instantiated. * * * @return the test container factory. * @throws TestContainerException if the default test container factory * cannot be obtained. */ protected TestContainerFactory getTestContainerFactory() throws TestContainerException { if (testContainerFactory == null) testContainerFactory = getDefaultTestContainerFactory(); return testContainerFactory; } /** * Create a web resource whose URI refers to the base URI the Web * application is deployed at. * * @return the created web resource */ public WebResource resource() { return client.resource(tc.getBaseUri()); } /** * Get the client that is configured for this test. * * @return the configured client. */ public Client client() { return client; } /** * Set up the test by invoking {@link TestContainer#start() } on * the test container obtained from the test container factory. * * @throws Exception */ @Before public void setUp() throws Exception { tc.start(); } /** * Tear down the test by invoking {@link TestContainer#stop() } on * the test container obtained from the test container factory. * * @throws Exception */ @After public void tearDown() throws Exception { tc.stop(); } /** * Creates an instance of {@link TestContainer} from the passed instances of * {@link AppDescriptor} and {@link TestContainerFactory}. If the test container * factory doesn't support the application descriptor, a {@link TestContainerException} * is thrown. * @param ad An instance of {@link AppDescriptor} * @param tcf An instance of {@link TestContainerFactory} * @return An instance of {@link TestContainer} */ private TestContainer getContainer(AppDescriptor ad, TestContainerFactory tcf) { if (ad == null) throw new IllegalArgumentException("The application descriptor cannot be null"); Class<? extends AppDescriptor> adType = tcf.supports(); if (adType == LowLevelAppDescriptor.class && ad.getClass() == WebAppDescriptor.class) { ad = LowLevelAppDescriptor.transform((WebAppDescriptor)ad); } else if (adType != ad.getClass()) { throw new TestContainerException("The application descriptor type, " + ad.getClass() + ", is not supported by the test container factory, " + tcf); } return tcf.create(getBaseURI(), ad); } /** * Creates an instance of the default test container factory. * * @return An instance of {@link TestContainerFactory} */ private static TestContainerFactory getDefaultTestContainerFactory() { if (defaultTestContainerFactoryClass == null) { if ((System.getProperty(TEST_CONTAINER_FACTORY_PROPERTY_NAME) == null) && (System.getProperty(TEST_CONTAINER_FACTORY_PROPERTY_NAME_LEGACY) == null)) { TestContainerFactory[] resultArray = ServiceFinder.find(TestContainerFactory.class).toArray(); if (resultArray.length >= 1) { // if default factory is present, use it. for (int i = 0; i < resultArray.length; i++) { if (resultArray[i].getClass().getName().equals(DEFAULT_TEST_CONTAINER_FACTORY_CLASS_NAME)) { Logger.getLogger(TestContainerFactory.class.getName()).config( "Found multiple TestContainerFactory implementations, using default " + resultArray[i].getClass().getName() ); defaultTestContainerFactoryClass = resultArray[i].getClass(); // is this necessary? return resultArray[i]; } } if (resultArray.length != 1) Logger.getLogger(TestContainerFactory.class.getName()).warning( "Found multiple TestContainerFactory implementations, using " + resultArray[0].getClass().getName() ); defaultTestContainerFactoryClass = resultArray[0].getClass(); return resultArray[0]; } } else { String tcfClassName = System.getProperty(TEST_CONTAINER_FACTORY_PROPERTY_NAME); if(tcfClassName == null) tcfClassName = System.getProperty(TEST_CONTAINER_FACTORY_PROPERTY_NAME_LEGACY, DEFAULT_TEST_CONTAINER_FACTORY_CLASS_NAME); try { defaultTestContainerFactoryClass = Class.forName(tcfClassName).asSubclass(TestContainerFactory.class); } catch (ClassNotFoundException ex) { throw new TestContainerException( "The default test container factory class name, " + tcfClassName + ", cannot be loaded", ex); } catch (ClassCastException ex) { throw new TestContainerException( "The default test container factory class, " + tcfClassName + ", is not an instance of TestContainerFactory", ex); } } } try { return defaultTestContainerFactoryClass.newInstance(); } catch (Exception ex) { throw new TestContainerException( "The default test container factory, " + defaultTestContainerFactoryClass + ", could not be instantiated", ex); } } // Refer to the class name rather than the class so we do not introduce // a required runtime dependency for those that do not want to utilize // Grizzly. private static final String DEFAULT_TEST_CONTAINER_FACTORY_CLASS_NAME = "com.sun.jersey.test.framework.spi.container.grizzly2.web.GrizzlyWebTestContainerFactory"; @Deprecated private static final String TEST_CONTAINER_FACTORY_PROPERTY_NAME_LEGACY = "test.containerFactory"; private static final String TEST_CONTAINER_FACTORY_PROPERTY_NAME = "jersey.test.containerFactory"; /** * Creates an instance of {@link Client}. * * Checks whether TestContainer provides client instance and * if not, ClientFactory obtained through getClientFactory() * will be used to create new client instance. * * This method is called exactly once when JerseyTest is created. * * @param tc instance of {@link TestContainer} * @param ad instance of {@link AppDescriptor} * @return A Client instance. */ protected Client getClient(TestContainer tc, AppDescriptor ad) { Client c = tc.getClient(); if (c != null) { return c; } else { c = getClientFactory().create(ad.getClientConfig()); } //check if logging is required boolean enableLogging = System.getProperty("enableLogging") != null; if (enableLogging) { c.addFilter(new LoggingFilter()); } return c; } /** * Get the ClientFactory. * <p> * If the client factory has not been explicit set with * {@link #setClientFactory(ClientFactory) } then * the default client factory will be obtained. * * @return ClientFactory instance */ protected ClientFactory getClientFactory() { if(clientFactory == null) clientFactory = getDefaultClientFactory(); return clientFactory; } private static ClientFactory getDefaultClientFactory() { return new ClientFactory() { @Override public Client create(ClientConfig cc) { return Client.create(cc); } }; } protected void setClientFactory(ClientFactory clientFactory) { this.clientFactory = clientFactory; } /** * Returns the base URI of the application. * @return The base URI of the application */ protected URI getBaseURI() { return UriBuilder.fromUri("http://localhost/") .port(getPort(9998)).build(); } /** * Returns the port to be used in the base URI. * @param defaultPort default port * @return The HTTP port of the URI */ protected int getPort(int defaultPort) { String port = System.getProperty("jersey.test.port"); if (null != port) { try { return Integer.parseInt(port); } catch (NumberFormatException e) { throw new TestContainerException("jersey.test.port with a " + "value of \"" + port +"\" is not a valid integer.", e); } } port = System.getProperty("JERSEY_HTTP_PORT"); if (null != port) { try { return Integer.parseInt(port); } catch (NumberFormatException e) { throw new TestContainerException("JERSEY_HTTP_PORT with a " + "value of \"" + port +"\" is not a valid integer.", e); } } return defaultPort; } }