blob: 0caef5de5b01d146f06496005584460507b757aa [file] [log] [blame]
/*******************************************************************************
* 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.IOException;
import java.net.JarURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
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.osgi.framework.Bundle;
public class WebappConfigLocator {
static final String DEFAULT_CONFIG_DIRECTORY = "config";
private static final String DEFAULT_CONTEXT_XML = "context.xml";
private static final String DEFAULT_WEB_XML = "web.xml";
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 = '#';
static final String JAR_SCHEMA = "jar:";
static final String JAR_TO_ENTRY_SEPARATOR = "!/";
static final String EMPTY_STRING = "";
/**
* 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 defaultContextXml.getAbsolutePath();
} else {
return null;
}
}
/**
* Resolves the default web.xml and returns an absolute 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. Note: If a default web.xml is not found then web-embed.xml will be used
* as default web.xml. (web-embed.xml is provided by this bundle)
*
* @param configLocation the main Tomcat's configuration directory
* @return an absolute path to default web.xml file, if it exists in the main Tomcat's configuration directory,
* otherwise returns <code>null</code>.
*/
public static String resolveDefaultWebXml(File configLocation) {
if (configLocation == null) {
return null;
}
File defaultWebXml = new File(configLocation, DEFAULT_WEB_XML);
if (defaultWebXml.exists()) {
return defaultWebXml.getAbsolutePath();
}
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
* @param bundle the corresponding web application bundle
* @return the context.xml if it is found following the algorithm above, otherwise <code>null</code>
* @throws MalformedURLException
*/
public static URL resolveWebappContextXml(String path, String docBase, File configLocation, Bundle bundle) throws MalformedURLException {
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.toURI().toURL();
}
// Try to find the context.xml in docBase
if (EMPTY_STRING.equals(docBase)) {
if (bundle != null) {
return resolveWebappContextXmlFromJarURLConnection(bundle);
} else {
return null;
}
}
File docBaseFile = new File(docBase);
if (docBaseFile.isDirectory()) {
contextXml = new File(docBaseFile, CONTEXT_XML);
if (contextXml.exists()) {
return contextXml.toURI().toURL();
}
} else {
JarFile jar = null;
try {
jar = new JarFile(docBaseFile);
ZipEntry contextXmlEntry = jar.getEntry(CONTEXT_XML);
if (contextXmlEntry != null) {
return new URL(JAR_SCHEMA + docBaseFile.toURI().toString() + JAR_TO_ENTRY_SEPARATOR + CONTEXT_XML);
}
} catch (IOException e) {
throw new ServletContainerException("Cannot open for reading " + docBaseFile.getAbsolutePath(), e);
} finally {
if (jar != null) {
try {
jar.close();
} catch (IOException _) {
}
}
}
}
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
* @param host host
* @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 URL resolveWebappContextXmlFromJarURLConnection(Bundle bundle) {
URL bundleUrl;
try {
bundleUrl = new URL(JAR_SCHEMA + bundle.getLocation() + JAR_TO_ENTRY_SEPARATOR + CONTEXT_XML);
} catch (MalformedURLException e) {
return null;
}
JarFile jarFile = null;
try {
URLConnection connection = bundleUrl.openConnection();
if (connection instanceof JarURLConnection) {
JarURLConnection jarURLConnection = (JarURLConnection) connection;
jarURLConnection.setUseCaches(false);
jarFile = jarURLConnection.getJarFile();
String entryName = jarURLConnection.getEntryName();
if (entryName != null && jarFile != null && jarFile.getEntry(entryName) != null) {
return bundleUrl;
}
}
} catch (IOException e) {
return null;
} finally {
if (jarFile != null) {
try {
jarFile.close();
} catch (IOException _) {
}
}
}
return null;
}
}