/*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license (CDDL), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
/**
* Manages the references to individual webapps within the container. This object handles
* the mapping of url-prefixes to webapps, and init and shutdown of any webapps it manages.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: HostConfiguration.java,v 1.8 2007/08/02 06:16:00 rickknowles Exp $
*/
public class HostConfiguration implements Runnable {
private static final long FLUSH_PERIOD = 60000L;
private static final String WEB_INF = "WEB-INF";
private static final String WEB_XML = "web.xml";
private String hostname;
private Map args;
private Map webapps;
private Cluster cluster;
private ObjectPool objectPool;
private ClassLoader commonLibCL;
private File commonLibCLPaths[];
private Thread thread;
public HostConfiguration(String hostname, Cluster cluster, ObjectPool objectPool, ClassLoader commonLibCL,
File commonLibCLPaths[], Map args, String webappsDirName) throws IOException {
this.hostname = hostname;
this.args = args;
this.webapps = new Hashtable();
this.cluster = cluster;
this.objectPool = objectPool;
this.commonLibCL = commonLibCL;
this.commonLibCLPaths = commonLibCLPaths;
// Is this the single or multiple configuration ? Check args
String warfile = (String) args.get("warfile");
String webroot = (String) args.get("webroot");
// If single-webapp mode
if ((webappsDirName == null) && ((warfile != null) || (webroot != null))) {
String prefix = (String) args.get("prefix");
if (prefix == null) {
prefix = "";
}
try {
this.webapps.put(prefix, initWebApp(prefix, getWebRoot(webroot, warfile), "webapp"));
} catch (Throwable err) {
Logger.log(Logger.ERROR, Launcher.RESOURCES, "HostConfig.WebappInitError", prefix, err);
}
}
// Otherwise multi-webapp mode
else {
initMultiWebappDir(webappsDirName);
}
Logger.log(Logger.DEBUG, Launcher.RESOURCES, "HostConfig.InitComplete",
new String[] {this.webapps.size() + "", this.webapps.keySet() + ""});
this.thread = new Thread(this, "WinstoneHostConfigurationMgmt:" + this.hostname);
this.thread.setDaemon(true);
this.thread.start();
}
public WebAppConfiguration getWebAppByURI(String uri) {
if (uri == null) {
return null;
} else if (uri.equals("/") || uri.equals("")) {
return (WebAppConfiguration) this.webapps.get("");
} else if (uri.startsWith("/")) {
String decoded = WinstoneRequest.decodeURLToken(uri);
String noLeadingSlash = decoded.substring(1);
int slashPos = noLeadingSlash.indexOf("/");
if (slashPos == -1) {
return (WebAppConfiguration) this.webapps.get(decoded);
} else {
return (WebAppConfiguration) this.webapps.get(
decoded.substring(0, slashPos + 1));
}
} else {
return null;
}
}
protected WebAppConfiguration initWebApp(String prefix, File webRoot,
String contextName) throws IOException {
Node webXMLParentNode = null;
File webInfFolder = new File(webRoot, WEB_INF);
if (webInfFolder.exists()) {
File webXmlFile = new File(webInfFolder, WEB_XML);
if (webXmlFile.exists()) {
Logger.log(Logger.DEBUG, Launcher.RESOURCES, "HostConfig.ParsingWebXml");
Document webXMLDoc = new WebXmlParser(this.commonLibCL)
.parseStreamToXML(webXmlFile);
if (webXMLDoc != null) {
webXMLParentNode = webXMLDoc.getDocumentElement();
Logger.log(Logger.DEBUG, Launcher.RESOURCES, "HostConfig.WebXmlParseComplete");
} else {
Logger.log(Logger.DEBUG, Launcher.RESOURCES, "HostConfig.WebXmlParseFailed");
}
}
}
// Instantiate the webAppConfig
return new WebAppConfiguration(this, this.cluster, webRoot
.getCanonicalPath(), prefix, this.objectPool, this.args,
webXMLParentNode, this.commonLibCL,
this.commonLibCLPaths, contextName);
}
public String getHostname() {
return this.hostname;
}
/**
* Destroy this webapp instance. Kills the webapps, plus any servlets,
* attributes, etc
*
* @param webApp The webapp to destroy
*/
private void destroyWebApp(String prefix) {
WebAppConfiguration webAppConfig = (WebAppConfiguration) this.webapps.get(prefix);
if (webAppConfig != null) {
webAppConfig.destroy();
this.webapps.remove(prefix);
}
}
public void destroy() {
Set prefixes = new HashSet(this.webapps.keySet());
for (Iterator i = prefixes.iterator(); i.hasNext(); ) {
destroyWebApp((String) i.next());
}
if (this.thread != null) {
this.thread.interrupt();
}
}
public void invalidateExpiredSessions() {
Set webapps = new HashSet(this.webapps.values());
for (Iterator i = webapps.iterator(); i.hasNext(); ) {
((WebAppConfiguration) i.next()).invalidateExpiredSessions();
}
}
public void run() {
boolean interrupted = false;
while (!interrupted) {
try {
Thread.sleep(FLUSH_PERIOD);
invalidateExpiredSessions();
} catch (InterruptedException err) {
interrupted = true;
}
}
this.thread = null;
}
public void reloadWebApp(String prefix) throws IOException {
WebAppConfiguration webAppConfig = (WebAppConfiguration) this.webapps.get(prefix);
if (webAppConfig != null) {
String webRoot = webAppConfig.getWebroot();
String contextName = webAppConfig.getContextName();
destroyWebApp(prefix);
try {
this.webapps.put(prefix, initWebApp(prefix, new File(webRoot), contextName));
} catch (Throwable err) {
Logger.log(Logger.ERROR, Launcher.RESOURCES, "HostConfig.WebappInitError", prefix, err);
}
} else {
throw new WinstoneException(Launcher.RESOURCES.getString("HostConfig.PrefixUnknown", prefix));
}
}
/**
* Setup the webroot. If a warfile is supplied, extract any files that the
* war file is newer than. If none is supplied, use the default temp
* directory.
*/
protected File getWebRoot(String requestedWebroot, String warfileName) throws IOException {
if (warfileName != null) {
Logger.log(Logger.INFO, Launcher.RESOURCES,
"HostConfig.BeginningWarExtraction");
// open the war file
File warfileRef = new File(warfileName);
if (!warfileRef.exists() || !warfileRef.isFile())
throw new WinstoneException(Launcher.RESOURCES.getString(
"HostConfig.WarFileInvalid", warfileName));
// Get the webroot folder (or a temp dir if none supplied)
File unzippedDir = null;
if (requestedWebroot != null) {
unzippedDir = new File(requestedWebroot);
} else {
File tempFile = File.createTempFile("dummy", "dummy");
String userName = System.getProperty("user.name");
unzippedDir = new File(tempFile.getParent(), "winstone/" +
(userName != null ? WinstoneResourceBundle.globalReplace(userName,
new String[][] {{"/", ""}, {"\\", ""}, {",", ""}}) + "/" : "") +
warfileRef.getName());
tempFile.delete();
}
if (unzippedDir.exists()) {
if (!unzippedDir.isDirectory()) {
throw new WinstoneException(Launcher.RESOURCES.getString(
"HostConfig.WebRootNotDirectory", unzippedDir.getPath()));
} else {
Logger.log(Logger.DEBUG, Launcher.RESOURCES,
"HostConfig.WebRootExists", unzippedDir.getCanonicalPath());
}
} else {
unzippedDir.mkdirs();
}
// Iterate through the files
JarFile warArchive = new JarFile(warfileRef);
for (Enumeration e = warArchive.entries(); e.hasMoreElements();) {
JarEntry element = (JarEntry) e.nextElement();
if (element.isDirectory()) {
continue;
}
String elemName = element.getName();
// If archive date is newer than unzipped file, overwrite
File outFile = new File(unzippedDir, elemName);
if (outFile.exists() && (outFile.lastModified() > warfileRef.lastModified())) {
continue;
}
outFile.getParentFile().mkdirs();
byte buffer[] = new byte[8192];
// Copy out the extracted file
InputStream inContent = warArchive.getInputStream(element);
OutputStream outStream = new FileOutputStream(outFile);
int readBytes = inContent.read(buffer);
while (readBytes != -1) {
outStream.write(buffer, 0, readBytes);
readBytes = inContent.read(buffer);
}
inContent.close();
outStream.close();
}
// Return webroot
return unzippedDir;
} else {
return new File(requestedWebroot);
}
}
protected void initMultiWebappDir(String webappsDirName) throws IOException {
if (webappsDirName == null) {
webappsDirName = "webapps";
}
File webappsDir = new File(webappsDirName);
if (!webappsDir.exists()) {
throw new WinstoneException(Launcher.RESOURCES.getString("HostConfig.WebAppDirNotFound", webappsDirName));
} else if (!webappsDir.isDirectory()) {
throw new WinstoneException(Launcher.RESOURCES.getString("HostConfig.WebAppDirIsNotDirectory", webappsDirName));
} else {
File children[] = webappsDir.listFiles();
for (int n = 0; n < children.length; n++) {
String childName = children[n].getName();
// Check any directories for warfiles that match, and skip: only deploy the war file
if (children[n].isDirectory()) {
File matchingWarFile = new File(webappsDir, children[n].getName() + ".war");
if (matchingWarFile.exists() && matchingWarFile.isFile()) {
Logger.log(Logger.DEBUG, Launcher.RESOURCES, "HostConfig.SkippingWarfileDir", childName);
} else {
String prefix = childName.equalsIgnoreCase("ROOT") ? "" : "/" + childName;
if (!this.webapps.containsKey(prefix)) {
try {
WebAppConfiguration webAppConfig = initWebApp(prefix, children[n], childName);
this.webapps.put(webAppConfig.getContextPath(), webAppConfig);
Logger.log(Logger.INFO, Launcher.RESOURCES, "HostConfig.DeployingWebapp", childName);
} catch (Throwable err) {
Logger.log(Logger.ERROR, Launcher.RESOURCES, "HostConfig.WebappInitError", prefix, err);
}
}
}
} else if (childName.endsWith(".war")) {
String outputName = childName.substring(0, childName.lastIndexOf(".war"));
String prefix = outputName.equalsIgnoreCase("ROOT") ? "" : "/" + outputName;
if (!this.webapps.containsKey(prefix)) {
File outputDir = new File(webappsDir, outputName);
outputDir.mkdirs();
try {
WebAppConfiguration webAppConfig = initWebApp(prefix,
getWebRoot(new File(webappsDir, outputName).getCanonicalPath(),
children[n].getCanonicalPath()), outputName);
this.webapps.put(webAppConfig.getContextPath(), webAppConfig);
Logger.log(Logger.INFO, Launcher.RESOURCES, "HostConfig.DeployingWebapp", childName);
} catch (Throwable err) {
Logger.log(Logger.ERROR, Launcher.RESOURCES, "HostConfig.WebappInitError", prefix, err);
}
}
}
}
}
}
public WebAppConfiguration getWebAppBySessionKey(String sessionKey) {
List allwebapps = new ArrayList(this.webapps.values());
for (Iterator i = allwebapps.iterator(); i.hasNext(); ) {
WebAppConfiguration webapp = (WebAppConfiguration) i.next();
WinstoneSession session = webapp.getSessionById(sessionKey, false);
if (session != null) {
return webapp;
}
}
return null;
}
}