package org.resthub.test; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.DefaultServlet; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.resthub.web.Client; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter; import org.springframework.web.context.ContextLoaderListener; import org.springframework.web.servlet.DispatcherServlet; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import javax.servlet.DispatcherType; import javax.servlet.ServletException; import java.util.EnumSet; /** * Base class for your webservice tests, based on Jetty and with preconfigured Spring configuration. By default, a single * Jetty server is started for all tests so you should take care of cleanup your data after your tests. * <p/> * If you want restart Jetty server after each test class, set startServerOnce to false and eventually customize port (in order to * avoid Jetty server listening on the same port conflict) in the constructor. * <p/> * By default it use XML context configuration. In order to use @Configuration class, you should set this.annotationBasedConfig = true * and this.contextLocations = "org.mypackage"; in your class constructor. * <p/> * Usually you will have to redefine the Spring profiles used by your application by calling AbstractWebTest super constructor */ public abstract class AbstractWebTest { /** * HTTP port used for running web tests, default is 9797 */ protected int port = 9797; /** * Specify if the embedded Jetty server should run once (true, default value) or for each class (false) */ protected Boolean startServerOnce = true; /** * Jetty reusable embedded server that will run your application */ protected static Server reusableServer; /** * Class specific Jetty embedded server that will run your application */ protected Server server; /** * RESThub http client used to send test requests */ protected Client client; /** * Default rootUrl used to send requests */ protected String rootUrl; /** * Default Spring contexts imported. You should not have to customize this one frequently, prefer using Spring 3.1 * profiles in order to control configuration activation. * <p/> * If you use JavaConfig based configuration, use it to specify the package where belong your @Configuration classes */ protected String contextLocations = "classpath*:resthubContext.xml classpath*:applicationContext.xml"; /** * List of Spring profiles (use comma separator) to activate * For example "resthub-web-server,resthub-jpa" */ protected String activeProfiles = ""; /** * When set to true, activate Spring 3.1 JavaConfig based configuration, by setting context param contextClass to org.springframework.web.context.support.AnnotationConfigWebApplicationContext */ protected Boolean annotationBasedConfig = false; /** * Default constructor */ public AbstractWebTest() { client = new Client(); } /** * Constructor allowing to specify Spring active profiles * * @param activeProfiles coma separated list of profiles */ public AbstractWebTest(String activeProfiles) { this(); this.activeProfiles = activeProfiles; } /** * Constructor allowing to specify HTTP port used to run the server * * @param port HTTP port used for running web tests, default is 9797 */ public AbstractWebTest(int port) { this(); this.port = port; } /** * Constructor allowing to specify Spring active profiles and HTTP port used to run the server * * @param activeProfiles coma separated list of profiles * @param port HTTP port used for running web tests, default is 9797 */ public AbstractWebTest(String activeProfiles, int port) { this(port); this.activeProfiles = activeProfiles; } /** * Define in OpenEntityManagerInViewFilter should be activated or not. Default to false */ protected Boolean useOpenEntityManagerInViewFilter = false; protected int servletContextHandlerOption = ServletContextHandler.SESSIONS; protected static final Logger logger = LoggerFactory.getLogger(AbstractWebTest.class); /** * You should override it in order to customize the servlet context handler */ protected ServletContextHandler customizeContextHandler(ServletContextHandler context) throws ServletException { return context; } /** * Send a request to the embedded test webserver * * @param urlSuffix The end of the request without starting /, for example "todo" urlSuffix will send a request to "http://localhost:9797/todo" * @return The requestHolder that will allow you to build and send the request */ public Client.RequestHolder request(String urlSuffix) { return this.client.url("http://localhost:" + this.port + "/" + urlSuffix); } @BeforeClass public void beforeClass() throws Exception { if ((!this.startServerOnce) || (reusableServer == null)) { server = new Server(port); // Add a context for authorization service ServletContextHandler context = new ServletContextHandler(servletContextHandlerOption); if (this.annotationBasedConfig) { context.getInitParams().put("contextClass", "org.springframework.web.context.support.AnnotationConfigWebApplicationContext"); } context.getInitParams().put("contextConfigLocation", contextLocations); context.getInitParams().put("spring.profiles.active", activeProfiles); ServletHolder defaultServletHolder = new ServletHolder(DefaultServlet.class); defaultServletHolder.setName("default"); // Add default servlet in order to make <mvc:default-servlet-handler /> work in unit tests. // "/" servlet mapping will be overriden by dispatcher, but default servlet will stay in the context context.addServlet(defaultServletHolder, "/"); ServletHolder dispatcherServletHolder = new ServletHolder(DispatcherServlet.class); dispatcherServletHolder.setName("dispatcher"); dispatcherServletHolder.setInitOrder(1); // Reuse beans detected by ContextLoaderListener so we configure an // empty contextConfigLocation dispatcherServletHolder.setInitParameter("contextConfigLocation", ""); context.addServlet(dispatcherServletHolder, "/"); context.addEventListener(new ContextLoaderListener()); if (useOpenEntityManagerInViewFilter) { context.addFilter(OpenEntityManagerInViewFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST)); } // Customize context then start the server. server.setHandler(customizeContextHandler(context)); server.start(); if (this.startServerOnce) { reusableServer = server; } } } @AfterClass public void afterClass() { if ((server != null) && (!this.startServerOnce)) { try { server.stop(); } catch (Exception e) { logger.error("Error while trying to stop embedded Jetty: " + e); } } } }