Is there a way to specify a different web.xml from the standard WEB-INF/web.xml when using an embedded tomcat instance?
I would like to put a web.xml in my src/test/resources (or some other area) and refer to that web.xml when starting the embedded tomcat.
Here is my existing code to start the tomcat instance
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();
I am starting this server from a tomcat instance and I would like to do the following when running unit tests
contextConfigLocation ContextLoaderListener that sets the parent ApplicationContext of the embedded tomcat.This file might be specified like so:
File webXmlFile = new File("src\\test\\resources\\embedded-web.xml");
After much frustration I realized that no matter what I do, I cannot persuade tomcat from looking in WEB-INF for web.xml.  It appears that I must ignore the web.xml altogether and set the items in the web.xml programmatically.
I ended up with this configuration:
<?xml version="1.0" encoding="UTF-8"?>
<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">
    <bean id="applicationContextProvider" class="ca.statcan.icos.sandbox.ApplicationContextProvider"/>
    <bean id="sandBoxDataSource" class="org.apache.tomcat.dbcp.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="org.hsqldb.jdbcDriver" />
        <property name="url" value="jdbc:hsqldb:mem:testdb;shutdown=true;" />
        <property name="username" value="SA" />
        <property name="password" value="" />
    </bean>
    <!-- Support for JPA related annotation support (@PersistenceUnit and @PersistenceContext) -->
    <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
    <!-- JTA Configuration -->
    <bean id="jtaTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager"
        init-method="init" destroy-method="close">
        <property name="forceShutdown"><value>true</value></property>
    </bean>
    <bean id="jtaUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp" />
    <bean id="springTransactionManager"
        class="org.springframework.transaction.jta.JtaTransactionManager">
        <property name="transactionManager" ref="jtaTransactionManager" />
        <property name="userTransaction" ref="jtaUserTransaction" />
    </bean>
    <!-- JPA Entity Manager configuration -->
    <bean id="entityManagerFactory"
        class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
        lazy-init="true">
        <property name="persistenceUnitName" value="sandBox" />    
        <property name="dataSource" ref="sandBoxDataSource" />
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="database" value="SQL_SERVER" />
                <property name="showSql" value="true" />
                <property name="generateDdl" value="true" />
            </bean>
        </property>
        <property name="jpaPropertyMap">
            <props>
                <prop key="hibernate.archive.autodetection">class</prop>
                <prop key="hibernate.cache.use_second_level_cache">false</prop>
                <prop key="hibernate.cache.use_query_cache">false</prop>
                <!--  Second Level Cache : EHCache in dev
                <prop key="hibernate.cache.provider_class">net.sf.ehcache.hibernate.EhCacheProvider</prop> -->
                <prop key="hibernate.hbm2ddl.auto">create</prop>
             </props>
        </property>
    </bean>
    <bean class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>
    <import resource="classpath:META-INF/applicationContext-core.xml" />
    <import resource="classpath:META-INF/applicationContext-web.xml" />
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<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">
    <bean id="propertyPlaceholderConfigurer"
        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations" value="classpath*:META-INF/fms-local.properties" />
        <property name="systemPropertiesModeName">
            <value>SYSTEM_PROPERTIES_MODE_OVERRIDE</value>
        </property>
    </bean>
    <!-- 
    Classpath scanning to load all the service classes
  -->
    <context:component-scan base-package="ca.statcan"
        use-default-filters="false">
        <context:include-filter type="regex" expression="ca\.statcan\.icos.*\.service\..*Service" />
        <context:include-filter type="regex" expression="ca\.statcan\.icos.*\.builders\..*Builder" />
    </context:component-scan>
        <!-- 
    Spring TransactionManager
  -->
    <tx:advice id="txAdvice" transaction-manager="springTransactionManager">
        <tx:attributes>
            <!-- all methods starting with 'get' are read-only -->
            <tx:method name="get*" read-only="true" propagation="SUPPORTS" isolation="DEFAULT"/>
            <tx:method name="find*" read-only="true" propagation="SUPPORTS" isolation="DEFAULT"/>
            <!-- other methods use the default transaction settings -->
            <tx:method name="*" read-only="false" propagation="REQUIRED" isolation="DEFAULT"/>
        </tx:attributes>
    </tx:advice>
    <!-- 
    AOP Weaving for all Service methods
  -->
    <aop:config proxy-target-class="true">
        <aop:pointcut id="icosServiceMethods" expression="execution(* ca.statcan.icos..*.service.*.*(..))" />
        <aop:advisor advice-ref="txAdvice" pointcut-ref="icosServiceMethods" />
    </aop:config>
</beans>
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();
    }
}
Modified Embedded Tomcat Wrapper
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");
    }
}
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();
    }
}
@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<Employee> 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<Employee> getEmployees() {
        return employees;
    }
}
With this, the embedded tomcat starts correctly and all seems to go well until I try to navigate to a web page.  In the StepDefs class, the EmployeeEntityService is injected correctly.  However, in the Action class, EmployeeEntityservice is not injected (it remains null).
According to my knowledge, I am setting the parent ApplicationContext for the embedded Tomcat correctly.  So why isn't the server using the parent context to get the EmployeeEntityService?
xml . As with the other dm Server configuration files, the tomcat-server. xml file is located in the $SERVER_HOME/config directory.
In this pom. xml file, we also include dependencies for embedded Tomcat JSP container: tomcat-jasper , tomcat-jasper-el , and tomcat-jsp-api . This is a simple JSP file to be served by embedded Tomcat server. The application serves a JSP file.
An embedded Tomcat server consists of a single Java web application along with a full Tomcat server distribution, packaged together and compressed into a single JAR, WAR or ZIP file.
Anyway, the tomcat-embed-jasper is marked as provided , so indicates that you expect the JDK or a container to provide the dependency at runtime. This scope is only available on the compilation and test classpath, and is not transitive.
I was stuck with a similar problem and the solution for using an alternative web.xml is simpler than one would dare to think: 
2 lines:
Context webContext = tomcat.addWebapp("/yourContextPath", "/web/app/docroot/");
webContext.getServletContext().setAttribute(Globals.ALT_DD_ATTR, "/path/to/custom/web.xml");
Voila! The magic happens in org.apache.catalina.startup.ContextConfig#getWebXmlSource
Disclaimer: Tested on Tomcat 7.0.42
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With