Merge branch 'master' into bug312752
diff --git a/org.eclipse.gemini.web.core/.classpath b/org.eclipse.gemini.web.core/.classpath
index dfeb4f0..3ef650f 100644
--- a/org.eclipse.gemini.web.core/.classpath
+++ b/org.eclipse.gemini.web.core/.classpath
@@ -21,10 +21,10 @@
 		</attributes>
 	</classpathentry>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-	<classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.junit/com.springsource.org.junit/4.7.0/com.springsource.org.junit-4.7.0.jar" sourcepath="/WEB_CONTAINER_IVY_CACHE/org.junit/com.springsource.org.junit/4.5.0/com.springsource.org.junit-sources-4.5.0.jar"/>
+	<classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.junit/com.springsource.org.junit/4.7.0/com.springsource.org.junit-4.7.0.jar" sourcepath="/WEB_CONTAINER_IVY_CACHE/org.junit/com.springsource.org.junit/4.7.0/com.springsource.org.junit-sources-4.7.0.jar"/>
 	<classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.eclipse.osgi/org.eclipse.osgi/3.5.1.R35x_v20091005/org.eclipse.osgi-3.5.1.R35x_v20091005.jar" sourcepath="/WEB_CONTAINER_IVY_CACHE/org.eclipse.osgi/org.eclipse.osgi/3.5.1.R35x_v20091005/org.eclipse.osgi-sources-3.5.1.R35x_v20091005.jar"/>
 	<classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.eclipse.virgo.util/org.eclipse.virgo.util.osgi/2.1.0.D-20100420091708/org.eclipse.virgo.util.osgi-2.1.0.D-20100420091708.jar" sourcepath="/WEB_CONTAINER_IVY_CACHE/org.eclispe.virgo.util/org.eclipse.virgo.util.osgi/2.0.0.RELEASE/org.eclipse.virgo.util.osgi-sources-2.0.0.RELEASE.jar"/>
-	<classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.eclipse.virgo.util/org.eclipse.virgo.util.io/2.1.0.D-20100420091708/org.eclipse.virgo.util.io-2.1.0.D-20100420091708.jar" sourcepath="/WEB_CONTAINER_IVY_CACHE/org.eclispe.virgo.util/org.eclipse.virgo.util.io/2.0.0.RELEASE/org.eclipse.virgo.util.io-sources-2.0.0.RELEASE.jar"/>
+	<classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.eclipse.virgo.util/org.eclipse.virgo.util.io/2.1.0.D-20100420091708/org.eclipse.virgo.util.io-2.1.0.D-20100420091708.jar" sourcepath="/WEB_CONTAINER_IVY_CACHE/org.eclipse.virgo.util/org.eclipse.virgo.util.io/2.1.0.D-20100420091708/org.eclipse.virgo.util.io-sources-2.1.0.D-20100420091708.jar"/>
 	<classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.eclipse.virgo.util/org.eclipse.virgo.util.common/2.1.0.D-20100420091708/org.eclipse.virgo.util.common-2.1.0.D-20100420091708.jar" sourcepath="/WEB_CONTAINER_IVY_CACHE/org.eclispe.virgo.util/org.eclipse.virgo.util.common/2.0.0.RELEASE/org.eclipse.virgo.util.common-sources-2.0.0.RELEASE.jar"/>
 	<classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.slf4j/com.springsource.slf4j.api/1.5.10/com.springsource.slf4j.api-1.5.10.jar" sourcepath="/WEB_CONTAINER_IVY_CACHE/org.slf4j/com.springsource.slf4j.api/1.5.10/com.springsource.slf4j.api-sources-1.5.10.jar"/>
 	<classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.apache.catalina.springsource/com.springsource.org.apache.catalina.springsource/6.0.20.S2-r5956/com.springsource.org.apache.catalina.springsource-6.0.20.S2-r5956.jar" sourcepath="/WEB_CONTAINER_IVY_CACHE/org.apache.catalina.springsource/com.springsource.org.apache.catalina.springsource/6.0.20.S2-r5956/com.springsource.org.apache.catalina.springsource-sources-6.0.20.S2-r5956.jar"/>
diff --git a/org.eclipse.gemini.web.test/src/test/java/org/eclipse/gemini/web/test/tomcat/TomcatServletContainerTests.java b/org.eclipse.gemini.web.test/src/test/java/org/eclipse/gemini/web/test/tomcat/TomcatServletContainerTests.java
index e5d0c6e..f5ed6c3 100644
--- a/org.eclipse.gemini.web.test/src/test/java/org/eclipse/gemini/web/test/tomcat/TomcatServletContainerTests.java
+++ b/org.eclipse.gemini.web.test/src/test/java/org/eclipse/gemini/web/test/tomcat/TomcatServletContainerTests.java
@@ -23,6 +23,8 @@
 import static org.junit.Assert.fail;
 
 import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileWriter;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
@@ -36,6 +38,7 @@
 import javax.servlet.ServletContext;
 
 import org.junit.Before;
+import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.osgi.framework.Bundle;
@@ -47,6 +50,7 @@
 import org.eclipse.gemini.web.core.spi.WebApplicationHandle;
 import org.eclipse.virgo.test.framework.OsgiTestRunner;
 import org.eclipse.virgo.test.framework.TestFrameworkUtils;
+import org.eclipse.virgo.util.io.IOUtils;
 import org.eclipse.virgo.util.io.PathReference;
 import org.eclipse.virgo.util.io.ZipUtils;
 
@@ -75,9 +79,20 @@
     private static final String LOCATION_WAR_WITH_TLD_IMPORT_SYSTEM_PACKAGES = LOCATION_PREFIX
         + "../org.eclipse.gemini.web.test/src/test/resources/war-with-tld-import-system-packages.war?Web-ContextPath=/war-with-tld-import-system-packages";
 
+    private static final String LOCATION_WAR_WITH_CONTEXT_XML_RESOURCES = LOCATION_PREFIX
+        + "../org.eclipse.gemini.web.test/src/test/resources/war-with-context-xml-resources.war?Web-ContextPath=/war-with-context-xml-resources";
+
+    private static final String LOCATION_WAR_WITH_CONTEXT_XML_CROSS_CONTEXT = LOCATION_PREFIX
+        + "../org.eclipse.gemini.web.test/src/test/resources/war-with-context-xml-cross-context.war?Web-ContextPath=/war-with-context-xml-cross-context";
+    
     private BundleContext bundleContext;
 
     private ServletContainer container;
+    
+    @BeforeClass
+    public static void beforeClass() throws Exception {
+        System.setProperty("org.eclipse.gemini.web.tomcat.config.path", "target/config/tomcat-server.xml");
+    }
 
     @Before
     public void before() throws Exception {
@@ -338,4 +353,65 @@
             this.container.stopWebApplication(handle);
         }
     }
+
+    @Test
+    public void testWarWithContextXml() throws Exception {
+        // Copy default context.xml
+        File defaultContextXml = new File("target/config/context.xml");
+        createFileWithContent(defaultContextXml, "<Context crossContext=\"true\"/>");
+
+        // Copy default context.xml.default
+        File defaultHostContextXml = new File("target/config/Catalina/localhost/context.xml.default");
+        String content = "<Context>"
+                + "<Resource name=\"mail/Session1\" auth=\"Container\" type=\"javax.mail.Session\" mail.smtp.host=\"localhost\"/>"
+                + "</Context>";
+        createFileWithContent(defaultHostContextXml, content);
+
+        File tomcatServerXml = new File("target/config/tomcat-server.xml");
+        createFileWithContent(tomcatServerXml, "");
+
+        String location1 = LOCATION_WAR_WITH_CONTEXT_XML_RESOURCES;
+        Bundle bundle1 = this.bundleContext.installBundle(location1);
+        bundle1.start();
+
+        String location2 = LOCATION_WAR_WITH_CONTEXT_XML_CROSS_CONTEXT;
+        Bundle bundle2 = this.bundleContext.installBundle(location2);
+        bundle2.start();
+
+        WebApplicationHandle handle1 = this.container.createWebApplication("/war-with-context-xml-resources", bundle1);
+        this.container.startWebApplication(handle1);
+
+        WebApplicationHandle handle2 = this.container.createWebApplication("/war-with-context-xml-cross-context",
+                bundle2);
+        this.container.startWebApplication(handle2);
+        try {
+            // tests JNDI resources
+            validateURL("http://localhost:8080/war-with-context-xml-resources/index.jsp");
+
+            // tests cross context functionality
+            validateURL("http://localhost:8080/war-with-context-xml-cross-context/index.jsp");
+        } finally {
+            this.container.stopWebApplication(handle1);
+            bundle1.uninstall();
+
+            this.container.stopWebApplication(handle2);
+            bundle2.uninstall();
+            
+            defaultContextXml.delete();
+            defaultHostContextXml.delete();
+            tomcatServerXml.delete();
+        }
+    }
+
+    private void createFileWithContent(File file, String content) throws Exception {
+        file.getParentFile().mkdirs();
+        FileWriter fWriter = null;
+        try {
+            fWriter = new FileWriter(file);
+            fWriter.write(content);
+            fWriter.flush();
+        } finally {
+            IOUtils.closeQuietly(fWriter);
+        }
+    }
 }
diff --git a/org.eclipse.gemini.web.test/src/test/resources/war-with-context-xml-cross-context.war b/org.eclipse.gemini.web.test/src/test/resources/war-with-context-xml-cross-context.war
new file mode 100755
index 0000000..26fecfc
--- /dev/null
+++ b/org.eclipse.gemini.web.test/src/test/resources/war-with-context-xml-cross-context.war
Binary files differ
diff --git a/org.eclipse.gemini.web.test/src/test/resources/war-with-context-xml-resources.war b/org.eclipse.gemini.web.test/src/test/resources/war-with-context-xml-resources.war
new file mode 100755
index 0000000..102bc64
--- /dev/null
+++ b/org.eclipse.gemini.web.test/src/test/resources/war-with-context-xml-resources.war
Binary files differ
diff --git a/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/Tomcat.java b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/Tomcat.java
index e1c5b94..78709de 100644
--- a/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/Tomcat.java
+++ b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/Tomcat.java
@@ -58,8 +58,11 @@
     private final ExtendCatalina catalina = new ExtendCatalina();
 
     private final JarScanner jarScanner;
+    
+    private BundleContext bundleContext;
 
     Tomcat(BundleContext context, PackageAdmin packageAdmin) {
+        this.bundleContext = context;
         JarScanner bundleDependenciesJarScanner = new BundleDependenciesJarScanner(new PackageAdminBundleDependencyDeterminer(context, packageAdmin),
             BundleFileResolverFactory.createBundleFileResolver());
         JarScanner defaultJarScanner = new DefaultJarScanner();
@@ -136,16 +139,36 @@
         if (LOGGER.isDebugEnabled()) {
             LOGGER.debug("Creating context '" + path + "' with docBase '" + docBase + "'");
         }
-
+        
         StandardContext context = new ExtendedStandardContext();
+        
+        ExtendedContextConfig config = new ExtendedContextConfig();
 
+        // Allocate the tomcat's configuration directory
+        File configDir = TomcatConfigLocator.resolveConfigDir(bundleContext);
+        config.setConfigBase(configDir);
+
+        // If default context.xml is existing, set it to the ContextConfig
+        String defaultContextXml = WebappConfigLocator.resolveDefaultContextXml(configDir);
+        if (defaultContextXml != null) {
+            config.setDefaultContextXml(defaultContextXml);
+        }
+
+        // Allocate the web application's configuration directory
+        File configLocation = WebappConfigLocator.resolveWebappConfigDir(configDir, findHost());
+
+        // If web application's context.xml is existing, set it to the
+        // StandardContext
+        File contextXml = WebappConfigLocator.resolveWebappContextXml(path, docBase, configLocation);
+        if (contextXml != null) {
+            context.setConfigFile(contextXml.getAbsolutePath());
+        }
+        
         context.setDocBase(docBase);        
         context.setPath(path.equals(ROOT_PATH) ? ROOT_CONTEXT_PATH : path);
 
         context.setJarScanner(this.jarScanner);
 
-        ContextConfig config = new ExtendedContextConfig();
-
         config.setCustomAuthenticators(this.authenticators);
         ((Lifecycle) context).addLifecycleListener(config);
 
@@ -210,5 +233,29 @@
      * 
      */
     private static class ExtendedContextConfig extends ContextConfig {
+        private File configDir;
+
+        /**
+         * If there is not configuration directory, return custom configuration
+         * directory. It is used to resolve the context.xml.default
+         */
+        @Override
+        protected File getConfigBase() {
+            File configBase = super.getConfigBase();
+            if (configBase != null) {
+                return configBase;
+            }
+
+            if (configDir != null) {
+                return configDir;
+            }
+
+            return null;
+        }
+
+        protected void setConfigBase(File configDir) {
+            this.configDir = configDir;
+        }
     }
+
 }
diff --git a/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/TomcatConfigLocator.java b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/TomcatConfigLocator.java
index 837a099..0126e30 100644
--- a/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/TomcatConfigLocator.java
+++ b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/TomcatConfigLocator.java
@@ -48,7 +48,7 @@
     private static final Logger LOGGER = LoggerFactory.getLogger(TomcatConfigLocator.class);
 
     static final String CONFIG_PATH_FRAMEWORK_PROPERTY = "org.eclipse.gemini.web.tomcat.config.path";
-    
+
     static final String DEFAULT_CONFIG_FILE_PATH = "config" + File.separator + "tomcat-server.xml";
 
     static final String CONFIG_PATH = "META-INF/tomcat";
@@ -68,15 +68,63 @@
         return is;
     }
 
+    /**
+     * Returns the directory where the Tomcat configuration files resides.
+     * 
+     * The location algorithm is as follows:
+     * <ol>
+     * <li>Check for <code>org.eclipse.gemini.web.tomcat.config.path</code> framework property, use if found</li>
+     * <li>Check for <code>config/tomcat-server.xml</code> in the current working directory, use if found</li>
+     * <li>If the previous checks do not return a result, return <code>null</code></li>
+     * </ol>
+     * 
+     * @param context the bundle context
+     * @return the directory where the Tomcat configuration files resides.
+     */
+    public static File resolveConfigDir(BundleContext context) {
+        File configFile = null;
+
+        /*
+         * Search for the framework property 'org.eclipse.gemini.web.tomcat.config.path'
+         * 
+         * Note: this is supposed to search framework and system properties but appears to ignore system properties which
+         * are set after the framework has initialised. See https://bugs.eclipse.org/bugs/show_bug.cgi?id=319679.
+         */
+        String path = context.getProperty(TomcatConfigLocator.CONFIG_PATH_FRAMEWORK_PROPERTY);
+        if (path != null) {
+            configFile = new File(path);
+            if (configFile.exists()) {
+                return configFile.getParentFile();
+            }
+        }
+
+        // Search for the system property 'org.eclipse.gemini.web.tomcat.config.path'
+        path = System.getProperty(TomcatConfigLocator.CONFIG_PATH_FRAMEWORK_PROPERTY);
+        if (path != null) {
+            configFile = new File(path);
+            if (configFile.exists()) {
+                return configFile.getParentFile();
+            }
+        }
+
+        // Search for the 'config' directory
+        configFile = new File(TomcatConfigLocator.DEFAULT_CONFIG_FILE_PATH);
+        if (configFile.exists()) {
+            return configFile.getParentFile();
+        }
+
+        return null;
+    }
+
     private static InputStream lookupConfigInFileSystem(BundleContext context) {
         InputStream result = null;
 
         String path = context.getProperty(CONFIG_PATH_FRAMEWORK_PROPERTY);
-        if(path != null) {
+        if (path != null) {
             result = tryGetStreamForFilePath(path);
         }
-        
-        if(result == null) {
+
+        if (result == null) {
             result = tryGetStreamForFilePath(DEFAULT_CONFIG_FILE_PATH);
         }
         return result;
@@ -111,7 +159,7 @@
             entry = bundle.getEntry(DEFAULT_CONFIG_PATH);
             if (entry == null) {
                 throw new IllegalStateException("Unable to locate default Tomcat configuration. Is the '" + bundle + "' bundle corrupt?");
-            } else if(LOGGER.isInfoEnabled()) {
+            } else if (LOGGER.isInfoEnabled()) {
                 LOGGER.info("Configuring Tomcat from default config file");
             }
         }
diff --git a/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/WebappConfigLocator.java b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/WebappConfigLocator.java
new file mode 100755
index 0000000..6e3da32
--- /dev/null
+++ b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/WebappConfigLocator.java
@@ -0,0 +1,180 @@
+/*******************************************************************************
+ * Copyright (c) 2010 SAP AG
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution. 
+ * The Eclipse Public License is available at
+ *   http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at 
+ *   http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.  
+ *
+ * Contributors:
+ *   Violeta Georgieva - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.tomcat.internal;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.jar.JarFile;
+import java.util.zip.ZipEntry;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Engine;
+import org.apache.catalina.Host;
+import org.eclipse.gemini.web.core.spi.ServletContainerException;
+import org.eclipse.virgo.util.io.IOUtils;
+import org.eclipse.virgo.util.io.PathReference;
+
+public class WebappConfigLocator {
+
+    static final String DEFAULT_CONFIG_DIRECTORY = "config";
+
+    private static final String DEFAULT_CONTEXT_XML = "context.xml";
+
+    private static final String CONTEXT_XML = "META-INF/context.xml";
+
+    private static final String XML_EXTENSION = ".xml";
+
+    private static final String ROOT_PATH = "/";
+
+    private static final String ROOT_CONTEXT_FILE = "ROOT";
+
+    private static final char SLASH_SEPARATOR = '/';
+
+    private static final char HASH_SEPARATOR = '#';
+
+    /**
+     * Resolves the default context.xml and returns a relative path to it, if it exists in the main Tomcat's
+     * configuration directory, otherwise returns <code>null</code>. The method returns <code>null</code> also in case
+     * main Tomcat's configuration directory does not exists.
+     * 
+     * @param configLocation the main Tomcat's configuration directory
+     * @return a relative path to default context.xml file, if it exists in the main Tomcat's configuration directory,
+     *         otherwise returns <code>null</code>.
+     */
+    public static String resolveDefaultContextXml(File configLocation) {
+        if (configLocation == null) {
+            return null;
+        }
+
+        File defaultContextXml = new File(configLocation, DEFAULT_CONTEXT_XML);
+        if (defaultContextXml.exists()) {
+            return getRelativePath(defaultContextXml);
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Resolves the web application's specific context.xml The algorithm is the following:
+     * <ol>
+     * <li>The composite context path i.e. /foo/bar are formated. The "/" are replaced with "#" i.e. foo#bar</li>
+     * <li>Check for <code><formated-web-app-context-path>.xml</code> file in the main Tomcat Host's configuration
+     * directory, use if found</li>
+     * <li>If <code>docBase</code> is directory check for <code>META-INF/context.xml</code> file, use if found</li>
+     * <li>If <code>docBase</code> is an archive check for <code>META-INF/context.xml</code> entry. If it exists, copy
+     * it to the main Tomcat Host's configuration directory and use it. The next time this copy will be used instead of
+     * the one in the archive.</li>
+     * <li>Return <code>null</code> in other cases.</li>
+     * </ol>
+     * 
+     * @param path the context path
+     * @param docBase the root directory/file for the web application
+     * @param configLocation Host's configuration directory
+     * @return the context.xml if it is found following the algorithm above, otherwise <code>null</code>
+     */
+    public static File resolveWebappContextXml(String path, String docBase, File configLocation) {
+        path = formatContextPath(path);
+
+        // Try to find the context.xml in the Tomcat's configuration directory
+        File contextXml = new File(configLocation, path + XML_EXTENSION);
+        if (contextXml.exists()) {
+            return contextXml;
+        }
+
+        // Try to find the context.xml in docBase
+        File docBaseFile = new File(docBase);
+        if (docBaseFile.isDirectory()) {
+            contextXml = new File(docBaseFile, CONTEXT_XML);
+            if (contextXml.exists()) {
+                return contextXml;
+            }
+        } else {
+            JarFile jar;
+            try {
+                jar = new JarFile(docBaseFile);
+            } catch (IOException e) {
+                throw new ServletContainerException("Cannot open for reading " + docBaseFile.getAbsolutePath(), e);
+            }
+            ZipEntry contextXmlEntry = jar.getEntry(CONTEXT_XML);
+            if (contextXmlEntry != null) {
+                File destination = new File(configLocation, path + XML_EXTENSION);
+                try {
+                    copyFile(jar.getInputStream(contextXmlEntry), destination);
+                } catch (IOException e) {
+                    throw new ServletContainerException("Cannot copy " + contextXml.getAbsolutePath() + " to " + destination.getAbsolutePath(), e);
+                }
+                return destination;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Resolves the directory where the web application' context.xml files are placed. Typically this is the Host's
+     * configuration directory.
+     * 
+     * @param mainConfigDir the main Tomcat's configuration directory
+     * @return the directory where the web applications' context.xml files are placed.
+     */
+    public static File resolveWebappConfigDir(File mainConfigDir, Host host) {
+        mainConfigDir = mainConfigDir != null ? mainConfigDir : new File(DEFAULT_CONFIG_DIRECTORY);
+
+        File configLocation = mainConfigDir;
+
+        Container parent = host.getParent();
+        if ((parent != null) && (parent instanceof Engine)) {
+            configLocation = new File(configLocation, parent.getName());
+        }
+
+        return new File(configLocation, host.getName());
+    }
+
+    private static String formatContextPath(String contextPath) {
+        // Multi-level context paths may be defined using #, e.g. foo#bar.xml for a context path of /foo/bar.
+        if (contextPath.equals(ROOT_PATH)) {
+            contextPath = ROOT_CONTEXT_FILE;
+        } else if (SLASH_SEPARATOR == contextPath.charAt(0)) {
+            contextPath = contextPath.substring(1);
+        }
+        return contextPath.replace(SLASH_SEPARATOR, HASH_SEPARATOR);
+    }
+
+    private static void copyFile(InputStream source, File destination) throws IOException {
+        PathReference destinationRef = new PathReference(destination);
+        destinationRef.getParent().createDirectory();
+
+        OutputStream outputStream = null;
+        try {
+            outputStream = new FileOutputStream(destination);
+            byte[] buffer = new byte[1024];
+            int read;
+            while ((read = source.read(buffer)) > 0) {
+                outputStream.write(buffer, 0, read);
+            }
+        } finally {
+            IOUtils.closeQuietly(source);
+            IOUtils.closeQuietly(outputStream);
+        }
+    }
+
+    private static String getRelativePath(File file) {
+        return new File(".").toURI().relativize(file.toURI()).toString();
+    }
+}
diff --git a/org.eclipse.gemini.web.tomcat/src/test/java/org/eclipse/gemini/web/tomcat/internal/TomcatConfigLocatorTests.java b/org.eclipse.gemini.web.tomcat/src/test/java/org/eclipse/gemini/web/tomcat/internal/TomcatConfigLocatorTests.java
index eba96a5..004129a 100644
--- a/org.eclipse.gemini.web.tomcat/src/test/java/org/eclipse/gemini/web/tomcat/internal/TomcatConfigLocatorTests.java
+++ b/org.eclipse.gemini.web.tomcat/src/test/java/org/eclipse/gemini/web/tomcat/internal/TomcatConfigLocatorTests.java
@@ -21,7 +21,10 @@
 import static org.easymock.EasyMock.expect;
 import static org.easymock.EasyMock.replay;
 import static org.easymock.EasyMock.verify;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
 
+import java.io.File;
 import java.net.URL;
 import java.util.Vector;
 
@@ -78,6 +81,27 @@
 
         TomcatConfigLocator.resolveConfigFile(mockContext);
     }
+    
+    @Test
+    public void testResolveConfigDir() throws Exception {
+        URL existingUrl = new URL("file:src/test/resources/server.xml");
+        URL nonexistingUrl = new URL("file:src/test/resources/server1.xml");
+
+        BundleContext mockContext = createMock(BundleContext.class);
+        expect(mockContext.getProperty(TomcatConfigLocator.CONFIG_PATH_FRAMEWORK_PROPERTY)).andReturn(
+                (new File(existingUrl.getPath())).getAbsolutePath()).andReturn(
+                (new File(nonexistingUrl.getPath())).getAbsolutePath()).andReturn(null);
+
+        replay(mockContext);
+
+        String result = "src" + File.separator + "test" + File.separator + "resources";
+
+        assertTrue(TomcatConfigLocator.resolveConfigDir(mockContext).getAbsolutePath().endsWith(result));
+        assertEquals(null, TomcatConfigLocator.resolveConfigDir(mockContext));
+        assertEquals(null, TomcatConfigLocator.resolveConfigDir(mockContext));
+
+        verify(mockContext);
+    }
 
     private BundleContext createMockBundleContext(Bundle mockBundle) {
         BundleContext mockContext = createNiceMock(BundleContext.class);
diff --git a/org.eclipse.gemini.web.tomcat/src/test/java/org/eclipse/gemini/web/tomcat/internal/WebappConfigLocatorTest.java b/org.eclipse.gemini.web.tomcat/src/test/java/org/eclipse/gemini/web/tomcat/internal/WebappConfigLocatorTest.java
new file mode 100755
index 0000000..7243e74
--- /dev/null
+++ b/org.eclipse.gemini.web.tomcat/src/test/java/org/eclipse/gemini/web/tomcat/internal/WebappConfigLocatorTest.java
@@ -0,0 +1,209 @@
+/*******************************************************************************
+ * Copyright (c) 2010 SAP AG
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution. 
+ * The Eclipse Public License is available at
+ *   http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at 
+ *   http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.  
+ *
+ * Contributors:
+ *   Violeta Georgieva - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.tomcat.internal;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.net.URL;
+import java.util.jar.JarEntry;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Engine;
+import org.apache.catalina.Host;
+import org.eclipse.gemini.web.core.spi.ServletContainerException;
+import org.eclipse.virgo.util.io.IOUtils;
+import org.junit.Before;
+import org.junit.Test;
+
+public class WebappConfigLocatorTest {
+    private static final String CONTEXT_PATH_1 = "test";
+    private static final String CONTEXT_PATH_2 = "test1";
+    private static final String CONTEXT_PATH_3 = "/";
+    private static final String CONTEXT_PATH_4 = "/test/test";
+    private static final String CONTEXT_PATH_5 = "test/test";
+
+    private static final String CONFIG_FILE_LOCATION_1 = "file:src/test/resources/test.xml";
+    private static final String CONFIG_FILE_LOCATION_2 = "file:src/test/resources/META-INF/context.xml";
+    private static final String CONFIG_FILE_LOCATION_3 = "file:target/test-classes/test1.xml";
+    private static final String CONFIG_FILE_LOCATION_4 = "file:target/test-classes/ROOT.xml";
+    private static final String CONFIG_FILE_LOCATION_5 = "file:target/test-classes/test%23test.xml";
+
+    private static final String CONFIG_DIR_LOCATION_1 = "file:src/test/resources";
+    private static final String CONFIG_DIR_LOCATION_2 = "file:src/test";
+    private static final String CONFIG_DIR_LOCATION_3 = "file:target/test-classes";
+
+    private static final String CORRUPTED_JAR_NAME = "file:target/test-classes/corrupted.jar";
+    private static final String JAR_NAME_1 = "file:target/test-classes/test1.jar";
+    private static final String JAR_NAME_2 = "file:target/test-classes/test2.jar";
+    private static final String JAR_ENTRY_NAME = "META-INF/context.xml";
+
+    private static final String HOST_NAME = "localhost";
+    private static final String ENGINE_NAME = "Catalina";
+
+    @Before
+    public void setUp() throws Exception {
+        URL urlFile2 = new URL(CONFIG_FILE_LOCATION_2);
+        URL jarFile1 = new URL(JAR_NAME_1);
+        URL jarFile2 = new URL(JAR_NAME_2);
+        URL corruptedJarFile = new URL(CORRUPTED_JAR_NAME);
+        new File(corruptedJarFile.getPath()).createNewFile();
+
+        byte[] buffer = new byte[1024];
+        int bytesRead;
+
+        FileOutputStream stream = null;
+        JarOutputStream out = null;
+        FileInputStream file = null;
+        try {
+            stream = new FileOutputStream(jarFile1.getPath());
+            out = new JarOutputStream(stream, new Manifest());
+            file = new FileInputStream(new File(urlFile2.getPath()));
+            JarEntry jarAdd = new JarEntry(JAR_ENTRY_NAME);
+            out.putNextEntry(jarAdd);
+            while ((bytesRead = file.read(buffer)) != -1) {
+                out.write(buffer, 0, bytesRead);
+            }
+            out.closeEntry();
+        } finally {
+            IOUtils.closeQuietly(file);
+            IOUtils.closeQuietly(out);
+            IOUtils.closeQuietly(stream);
+        }
+
+        try {
+            stream = new FileOutputStream(jarFile2.getPath());
+            out = new JarOutputStream(stream, new Manifest());
+        } finally {
+            IOUtils.closeQuietly(out);
+            IOUtils.closeQuietly(stream);
+        }
+    }
+
+    @Test
+    public void testResolveWebappContextXml() throws Exception {
+        URL urlFile1 = new URL(CONFIG_FILE_LOCATION_1);
+        URL urlDir1 = new URL(CONFIG_DIR_LOCATION_1);
+        // context.xml exists in the configuration directory
+        assertEquals(new File(urlFile1.getPath()), WebappConfigLocator.resolveWebappContextXml(CONTEXT_PATH_1, null,
+                new File(urlDir1.getPath())));
+
+        URL urlFile2 = new URL(CONFIG_FILE_LOCATION_2);
+        URL urlDir2 = new URL(CONFIG_DIR_LOCATION_2);
+        // context.xml does not exist in the configuration directory, but exists
+        // in doc base
+        // doc base is directory
+        assertEquals(new File(urlFile2.getPath()), WebappConfigLocator.resolveWebappContextXml(CONTEXT_PATH_2, urlDir1
+                .getPath(), new File(urlDir1.getPath())));
+        // context.xml does not exist in the configuration directory and in doc
+        // base
+        // doc base is directory
+        assertEquals(null, WebappConfigLocator.resolveWebappContextXml(CONTEXT_PATH_2, urlDir2.getPath(), new File(
+                urlDir1.getPath())));
+
+        try {
+            // context.xml does not exist in the configuration directory, doc
+            // base is jar and does not exist
+            URL corruptedJarFile = new URL(CORRUPTED_JAR_NAME);
+            WebappConfigLocator.resolveWebappContextXml(CONTEXT_PATH_2, corruptedJarFile.getPath(), new File(urlDir1
+                    .getPath()));
+        } catch (ServletContainerException e) {
+            assertTrue(e.getCause() instanceof IOException);
+        }
+
+        URL jarFile1 = new URL(JAR_NAME_1);
+        URL jarFile2 = new URL(JAR_NAME_2);
+        URL urlDir3 = new URL(CONFIG_DIR_LOCATION_3);
+        URL urlFile3 = new URL(CONFIG_FILE_LOCATION_3);
+        // context.xml does not exist in the configuration directory, but exists
+        // in doc base
+        // doc base is jar
+        // copy will be performed
+        assertEquals(new File(urlFile3.getPath()), WebappConfigLocator.resolveWebappContextXml(CONTEXT_PATH_2, jarFile1
+                .getPath(), new File(urlDir3.getPath())));
+        // context.xml does not exist in the configuration directory and in doc
+        // base
+        // doc base is jar
+        assertEquals(null, WebappConfigLocator.resolveWebappContextXml(CONTEXT_PATH_2, jarFile2.getPath(), new File(
+                urlDir1.getPath())));
+
+        URL urlFile4 = new URL(CONFIG_FILE_LOCATION_4);
+        URL urlFile5 = new URL(CONFIG_FILE_LOCATION_5);
+        // different types of context path
+        assertEquals(new File(urlFile4.getPath()), WebappConfigLocator.resolveWebappContextXml(CONTEXT_PATH_3, jarFile1
+                .getPath(), new File(urlDir3.getPath())));
+        assertEquals(new File(urlFile5.toURI().getSchemeSpecificPart()), WebappConfigLocator.resolveWebappContextXml(
+                CONTEXT_PATH_4, jarFile1.getPath(), new File(urlDir3.getPath())));
+        assertEquals(new File(urlFile5.toURI().getSchemeSpecificPart()), WebappConfigLocator.resolveWebappContextXml(
+                CONTEXT_PATH_5, jarFile1.getPath(), new File(urlDir3.getPath())));
+    }
+
+    @Test
+    public void testResolveWebappConfigDir() {
+        Host mockHost = createMock(Host.class);
+        Engine mockEngine = createMock(Engine.class);
+        Container mockContainer = createMock(Container.class);
+        expect(mockHost.getParent()).andReturn(mockEngine).andReturn(mockContainer).andReturn(null).andReturn(
+                mockEngine).andReturn(mockContainer).andReturn(null);
+        expect(mockHost.getName()).andReturn(HOST_NAME).times(6);
+        expect(mockEngine.getName()).andReturn(ENGINE_NAME).times(2);
+
+        replay(mockHost, mockEngine);
+
+        File configDir = new File("");
+        File expected = new File(configDir, ENGINE_NAME);
+        expected = new File(expected, HOST_NAME);
+        assertEquals(expected, WebappConfigLocator.resolveWebappConfigDir(configDir, mockHost));
+
+        expected = new File(configDir, HOST_NAME);
+        assertEquals(expected, WebappConfigLocator.resolveWebappConfigDir(configDir, mockHost));
+        assertEquals(expected, WebappConfigLocator.resolveWebappConfigDir(configDir, mockHost));
+
+        configDir = new File(WebappConfigLocator.DEFAULT_CONFIG_DIRECTORY);
+        expected = new File(configDir, ENGINE_NAME);
+        expected = new File(expected, HOST_NAME);
+        assertEquals(expected, WebappConfigLocator.resolveWebappConfigDir(null, mockHost));
+
+        expected = new File(configDir, HOST_NAME);
+        assertEquals(expected, WebappConfigLocator.resolveWebappConfigDir(null, mockHost));
+        assertEquals(expected, WebappConfigLocator.resolveWebappConfigDir(null, mockHost));
+
+        verify(mockHost, mockEngine);
+    }
+
+    @Test
+    public void testResolveDefaultContextXml() throws Exception {
+        assertEquals(null, WebappConfigLocator.resolveDefaultContextXml(null));
+
+        URL urlFile = new URL(CONFIG_FILE_LOCATION_2);
+        assertEquals(urlFile.getPath(), WebappConfigLocator.resolveDefaultContextXml(new File(urlFile.getPath())
+                .getParentFile()));
+
+        assertEquals(null, WebappConfigLocator.resolveDefaultContextXml(new File(urlFile.getPath()).getParentFile()
+                .getParentFile()));
+    }
+}
diff --git a/org.eclipse.gemini.web.tomcat/src/test/resources/META-INF/context.xml b/org.eclipse.gemini.web.tomcat/src/test/resources/META-INF/context.xml
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/org.eclipse.gemini.web.tomcat/src/test/resources/META-INF/context.xml
diff --git a/org.eclipse.gemini.web.tomcat/src/test/resources/test.xml b/org.eclipse.gemini.web.tomcat/src/test/resources/test.xml
new file mode 100755
index 0000000..e3e359d
--- /dev/null
+++ b/org.eclipse.gemini.web.tomcat/src/test/resources/test.xml
@@ -0,0 +1,5 @@
+<Context crossContext="true" 
+         privileged="true"
+         reloadable="true"
+         useHTTPOnly="true"
+/>
\ No newline at end of file
diff --git a/org.eclipse.gemini.web.tomcat/template.mf b/org.eclipse.gemini.web.tomcat/template.mf
index 6d62570..1abab2a 100644
--- a/org.eclipse.gemini.web.tomcat/template.mf
+++ b/org.eclipse.gemini.web.tomcat/template.mf
@@ -26,6 +26,7 @@
  javax.xml.transform.*;version="0",
  javax.xml.validation.*;version="0",
  org.w3c.dom.*;version="0"
-Import-Package: org.apache.catalina.deploy;version="6.0.20.S2-r5956"
+Import-Package: org.apache.catalina.deploy;version="6.0.20.S2-r5956",
+ org.apache.catalina.session;version="6.0.20.S2-r5956"
 Bundle-Activator: org.eclipse.gemini.web.tomcat.internal.Activator
 Excluded-Exports: *.internal.*