title: Specify a custom web.xml to an embedded tomcat
body: <p>Is there a way to specify a different <code>web.xml</code> from the standard <code>WEB-INF/web.xml</code> when using an embedded tomcat instance?</p>
  
  <p>I would like to put a <code>web.xml</code> in my <code>src/test/resources</code> (or some other area) and refer to that <code>web.xml</code> when starting the embedded tomcat.</p>
  
  <p>Here is my existing code to start the tomcat instance</p>
  
  <pre><code>tomcat = new Tomcat();
  String baseDir = ".";
  tomcat.setPort(8080);
  tomcat.setBaseDir(baseDir);
  tomcat.getHost().setAppBase(baseDir);
  tomcat.getHost().setAutoDeploy(true);
  tomcat.enableNaming();
  
  Context ctx = tomcat.addWebApp(tomcat.getHost(), "/sandbox-web", "src\\main\\webapp");
  File configFile = new File("src\\main\\webapp\\META-INF\\context.xml");
  ctx.setConfigFile(configFile.toURI().toURL());
  
  tomcat.start();
  </code></pre>
  
  <p>I am starting this server from a tomcat instance and I would like to do the following when running unit tests</p>
  
  <ul>
  <li>turn off the <code>contextConfigLocation</code> </li>
  <li>specify a custom <code>ContextLoaderListener</code> that sets the parent <code>ApplicationContext</code> of the embedded tomcat.</li>
  </ul>
  
  <p>This file might be specified like so:</p>
  
  <pre><code>File webXmlFile = new File("src\\test\\resources\\embedded-web.xml");
  </code></pre>
  
  <h1>Edit</h1>
  
  <p>After much frustration I realized that no matter what I do, I cannot persuade tomcat from looking in <code>WEB-INF</code> for <code>web.xml</code>.  It appears that I must ignore the <code>web.xml</code> altogether and set the items in the <code>web.xml</code> programmatically.</p>
  
  <p>I ended up with this configuration:</p>
  
  <h2>cucumber.xml for configuring tests</h2>
  
  <pre><code>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
  &lt;beans xmlns="http://www.springframework.org/schema/beans"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns:context="http://www.springframework.org/schema/context"
         xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"&gt;
  
      &lt;bean id="applicationContextProvider" class="ca.statcan.icos.sandbox.ApplicationContextProvider"/&gt;
  
      &lt;bean id="sandBoxDataSource" class="org.apache.tomcat.dbcp.dbcp.BasicDataSource" destroy-method="close"&gt;
          &lt;property name="driverClassName" value="org.hsqldb.jdbcDriver" /&gt;
          &lt;property name="url" value="jdbc:hsqldb:mem:testdb;shutdown=true;" /&gt;
          &lt;property name="username" value="SA" /&gt;
          &lt;property name="password" value="" /&gt;
      &lt;/bean&gt;
  
      &lt;!-- Support for JPA related annotation support (@PersistenceUnit and @PersistenceContext) --&gt;
      &lt;bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" /&gt;
  
      &lt;!-- JTA Configuration --&gt;
      &lt;bean id="jtaTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager"
          init-method="init" destroy-method="close"&gt;
          &lt;property name="forceShutdown"&gt;&lt;value&gt;true&lt;/value&gt;&lt;/property&gt;
      &lt;/bean&gt;
  
      &lt;bean id="jtaUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp" /&gt;
  
      &lt;bean id="springTransactionManager"
          class="org.springframework.transaction.jta.JtaTransactionManager"&gt;
          &lt;property name="transactionManager" ref="jtaTransactionManager" /&gt;
          &lt;property name="userTransaction" ref="jtaUserTransaction" /&gt;
      &lt;/bean&gt;
  
      &lt;!-- JPA Entity Manager configuration --&gt;
      &lt;bean id="entityManagerFactory"
          class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
          lazy-init="true"&gt;
          &lt;property name="persistenceUnitName" value="sandBox" /&gt;    
          &lt;property name="dataSource" ref="sandBoxDataSource" /&gt;
          &lt;property name="jpaVendorAdapter"&gt;
              &lt;bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"&gt;
                  &lt;property name="database" value="SQL_SERVER" /&gt;
                  &lt;property name="showSql" value="true" /&gt;
                  &lt;property name="generateDdl" value="true" /&gt;
              &lt;/bean&gt;
          &lt;/property&gt;
          &lt;property name="jpaPropertyMap"&gt;
              &lt;props&gt;
                  &lt;prop key="hibernate.archive.autodetection"&gt;class&lt;/prop&gt;
                  &lt;prop key="hibernate.cache.use_second_level_cache"&gt;false&lt;/prop&gt;
                  &lt;prop key="hibernate.cache.use_query_cache"&gt;false&lt;/prop&gt;
                  &lt;!--  Second Level Cache : EHCache in dev
                  &lt;prop key="hibernate.cache.provider_class"&gt;net.sf.ehcache.hibernate.EhCacheProvider&lt;/prop&gt; --&gt;
                  &lt;prop key="hibernate.hbm2ddl.auto"&gt;create&lt;/prop&gt;
               &lt;/props&gt;
          &lt;/property&gt;
      &lt;/bean&gt;
  
      &lt;bean class="org.springframework.orm.jpa.JpaTransactionManager"&gt;
          &lt;property name="entityManagerFactory" ref="entityManagerFactory" /&gt;
      &lt;/bean&gt;
  
      &lt;import resource="classpath:META-INF/applicationContext-core.xml" /&gt;
      &lt;import resource="classpath:META-INF/applicationContext-web.xml" /&gt;
  
  &lt;/beans&gt;
  </code></pre>
  
  <h2>applicationContext-core.xml - where the services are configured</h2>
  
  <pre><code>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
  &lt;beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
      xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
      xmlns:jee="http://www.springframework.org/schema/jee"
      xsi:schemaLocation="
      http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
      http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
      http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
      http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
      http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd"
      default-autowire="byName"&gt;
  
      &lt;bean id="propertyPlaceholderConfigurer"
          class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"&gt;
          &lt;property name="locations" value="classpath*:META-INF/fms-local.properties" /&gt;
          &lt;property name="systemPropertiesModeName"&gt;
              &lt;value&gt;SYSTEM_PROPERTIES_MODE_OVERRIDE&lt;/value&gt;
          &lt;/property&gt;
      &lt;/bean&gt;
  
      &lt;!-- 
      Classpath scanning to load all the service classes
    --&gt;
      &lt;context:component-scan base-package="ca.statcan"
          use-default-filters="false"&gt;
          &lt;context:include-filter type="regex" expression="ca\.statcan\.icos.*\.service\..*Service" /&gt;
          &lt;context:include-filter type="regex" expression="ca\.statcan\.icos.*\.builders\..*Builder" /&gt;
      &lt;/context:component-scan&gt;
  
          &lt;!-- 
      Spring TransactionManager
    --&gt;
      &lt;tx:advice id="txAdvice" transaction-manager="springTransactionManager"&gt;
          &lt;tx:attributes&gt;
              &lt;!-- all methods starting with 'get' are read-only --&gt;
              &lt;tx:method name="get*" read-only="true" propagation="SUPPORTS" isolation="DEFAULT"/&gt;
              &lt;tx:method name="find*" read-only="true" propagation="SUPPORTS" isolation="DEFAULT"/&gt;
              &lt;!-- other methods use the default transaction settings --&gt;
              &lt;tx:method name="*" read-only="false" propagation="REQUIRED" isolation="DEFAULT"/&gt;
          &lt;/tx:attributes&gt;
      &lt;/tx:advice&gt;
  
      &lt;!-- 
      AOP Weaving for all Service methods
    --&gt;
      &lt;aop:config proxy-target-class="true"&gt;
          &lt;aop:pointcut id="icosServiceMethods" expression="execution(* ca.statcan.icos..*.service.*.*(..))" /&gt;
          &lt;aop:advisor advice-ref="txAdvice" pointcut-ref="icosServiceMethods" /&gt;
      &lt;/aop:config&gt;
  
  &lt;/beans&gt;
  </code></pre>
  
  <h2>Custom ContextLoaderListener</h2>
  
  <pre><code>public class EmbeddedContextLoaderListener extends ContextLoaderListener {
  
      @Override
      protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
          GenericWebApplicationContext context = new GenericWebApplicationContext(sc);
          context.setParent(ApplicationContextProvider.getApplicationContext());
          return context;
      }
  
      @Override
      protected ApplicationContext loadParentContext(ServletContext servletContext) {
          return ApplicationContextProvider.getApplicationContext();
      }
  }
  </code></pre>
  
  <p><strong>Modified Embedded Tomcat Wrapper</strong>  </p>
  
  <pre><code>public class EmbeddedTomcat {
      /** Log4j logger for this class. */
      @SuppressWarnings("unused")
      private static final Logger LOG = LoggerFactory.getLogger(EmbeddedTomcat.class);
  
      private Tomcat tomcat;
  
      public void start() {
          try {
              tomcat = new Tomcat();
              String baseDir = ".";
              tomcat.setPort(8080);
              tomcat.setBaseDir(baseDir);
              tomcat.getHost().setAppBase(baseDir);
              tomcat.getHost().setDeployOnStartup(true);
              tomcat.getHost().setAutoDeploy(true);
              tomcat.enableNaming();
  
              Context context = tomcat.addContext("/sandbox-web", "src\\main\\webapp");
              Tomcat.initWebappDefaults(context);
              configureSimulatedWebXml(context); 
  
              LOG.info("Starting tomcat in: " + new File(tomcat.getHost().getAppBase()).getAbsolutePath());
  
              tomcat.start();
          } catch (LifecycleException e) {
              throw new RuntimeException(e);
          }
      }
  
      public void stop() {
          try {
              tomcat.stop();
              tomcat.destroy();
              FileUtils.deleteDirectory(new File("work"));
              FileUtils.deleteDirectory(new File("tomcat.8080"));
          } catch (LifecycleException e) {
              throw new RuntimeException(e);
          } catch (IOException e) {
              throw new RuntimeException(e);
          }
      }
  
      public void deploy(String appName) {
          tomcat.addWebapp(tomcat.getHost(), "/" + appName, "src\\main\\webapp");
      }
  
      public String getApplicationUrl(String appName) {
          return String.format("http://%s:%d/%s", tomcat.getHost().getName(),
                  tomcat.getConnector().getLocalPort(), appName);
      }
  
      public boolean isRunning() {
          return tomcat != null;
      }
  
      private void configureSimulatedWebXml(final Context context) {
          // Programmatically configure the web.xml here
  
          context.setDisplayName("Sandbox Web Application");
  
          context.addParameter("org.apache.tiles.impl.BasicTilesContainer.DEFINITIONS_CONFIG", "/WEB-INF/tiles-defs.xml,/WEB-INF/tiles-sandbox.xml");
  
          final FilterDef struts2Filter = new FilterDef();
          struts2Filter.setFilterName("struts2");
          struts2Filter.setFilterClass("org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter");
          struts2Filter.addInitParameter("actionPackages", "ca.statcan.icos.sandbox.web");
          context.addFilterDef(struts2Filter);    
  
          final FilterMap struts2FilterMapping = new FilterMap();
          struts2FilterMapping.setFilterName("struts2");
          struts2FilterMapping.addURLPattern("/*");
          context.addFilterMap(struts2FilterMapping);
  
          context.addApplicationListener("org.apache.tiles.web.startup.TilesListener");
          context.addApplicationListener("ca.statcan.icos.sandbox.EmbeddedContextLoaderListener");
  
          context.addWelcomeFile("index.jsp");
      }
  }
  </code></pre>
  
  <h2>Step definitions</h2>
  
  <pre><code>public class StepDefs {
  
      @Autowired
      protected EmployeeEntityService employeeEntityService;
  
      @Given("^the following divisions exist$")
      public void the_following_divisions_exist(DataTable arg1) throws Throwable {
          final Employee employee = new Employee(3, "Third", "John", null, "613-222-2223");
          employeeEntityService.persistEmployee(employee);
      }
  
      @Given("^there are no existing surveys$")
      public void there_are_no_existing_surveys() throws Throwable {
      }
  
      @When("^I register a new survey with the following information$")
      public void I_register_a_new_survey_with_the_following_information(DataTable arg1) throws Throwable {
          Capabilities capabilities = DesiredCapabilities.htmlUnit();
          final HtmlUnitDriver driver = new HtmlUnitDriver(capabilities);
  
          driver.get("http://localhost:8080/sandbox-web/myFirst");
      }
  
      @Then("^the surveys are created$")
      public void the_surveys_are_created() throws Throwable {
          // Express the Regexp above with the code you wish you had
          throw new PendingException();
      }
  
      @Then("^a confirmation message is displayed saying: \"([^\"]*)\"$")
      public void a_confirmation_message_is_displayed_saying(String arg1) throws Throwable {
          // Express the Regexp above with the code you wish you had
          throw new PendingException();
      }
  }
  </code></pre>
  
  <h2>Action class</h2>
  
  <pre><code>@Results({ @Result(name = "success", location = "myFirst.page", type = "tiles") })
  @ParentPackage("default")
  @Breadcrumb(labelKey = "ca.statcan.icos.sandbox.firstAction")
  @SuppressWarnings("serial")
  public class MyFirstAction extends HappyfActionSupport {
  
      private List&lt;Employee&gt; employees;
  
      @Autowired
      private EmployeeEntityService employeeEntityService;
  
      @Override
      public String execute() {
          employees = employeeEntityService.getAllEmployee();
          if (employees.size() == 0) {
  
              // persist data in memory
              final Employee employee1 = new Employee(1, "First", "John", null, "613-222-2222");
              employeeEntityService.persistEmployee(employee1);
  
              final Employee employee2 = new Employee(2, "Second", "John", null, "613-222-2223");
              employeeEntityService.persistEmployee(employee2);
  
              employees = employeeEntityService.getAllEmployee();
          }
          return SUCCESS;
      }
  
      public List&lt;Employee&gt; getEmployees() {
          return employees;
      }
  
  }
  </code></pre>
  
  <p>With this, the embedded tomcat starts correctly and all seems to go well until I try to navigate to a web page.  In the <code>StepDefs</code> class, the <code>EmployeeEntityService</code> is injected correctly.  However, in the <code>Action</code> class, <code>EmployeeEntityservice</code> is not injected (it remains <code>null</code>).</p>
  
  <p>According to my knowledge, I am setting the parent <code>ApplicationContext</code> for the embedded Tomcat correctly.  So why isn't the server using the parent context to get the <code>EmployeeEntityService</code>?</p>