001: /*
002: * Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
003: * Distributed under the terms of either:
004: * - the common development and distribution license (CDDL), v1.0; or
005: * - the GNU Lesser General Public License, v2.1 or later
006: */
007: package winstone;
008:
009: import java.io.File;
010: import java.io.FileOutputStream;
011: import java.io.IOException;
012: import java.io.InputStream;
013: import java.io.OutputStream;
014: import java.util.ArrayList;
015: import java.util.Enumeration;
016: import java.util.HashSet;
017: import java.util.Hashtable;
018: import java.util.Iterator;
019: import java.util.List;
020: import java.util.Map;
021: import java.util.Set;
022: import java.util.jar.JarEntry;
023: import java.util.jar.JarFile;
024:
025: import org.w3c.dom.Document;
026: import org.w3c.dom.Node;
027:
028: /**
029: * Manages the references to individual webapps within the container. This object handles
030: * the mapping of url-prefixes to webapps, and init and shutdown of any webapps it manages.
031: *
032: * @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
033: * @version $Id: HostConfiguration.java,v 1.8 2007/08/02 06:16:00 rickknowles Exp $
034: */
035: public class HostConfiguration implements Runnable {
036:
037: private static final long FLUSH_PERIOD = 60000L;
038:
039: private static final String WEB_INF = "WEB-INF";
040: private static final String WEB_XML = "web.xml";
041:
042: private String hostname;
043: private Map args;
044: private Map webapps;
045: private Cluster cluster;
046: private ObjectPool objectPool;
047: private ClassLoader commonLibCL;
048: private File commonLibCLPaths[];
049:
050: private Thread thread;
051:
052: public HostConfiguration(String hostname, Cluster cluster,
053: ObjectPool objectPool, ClassLoader commonLibCL,
054: File commonLibCLPaths[], Map args, String webappsDirName)
055: throws IOException {
056: this .hostname = hostname;
057: this .args = args;
058: this .webapps = new Hashtable();
059: this .cluster = cluster;
060: this .objectPool = objectPool;
061: this .commonLibCL = commonLibCL;
062: this .commonLibCLPaths = commonLibCLPaths;
063:
064: // Is this the single or multiple configuration ? Check args
065: String warfile = (String) args.get("warfile");
066: String webroot = (String) args.get("webroot");
067:
068: // If single-webapp mode
069: if ((webappsDirName == null)
070: && ((warfile != null) || (webroot != null))) {
071: String prefix = (String) args.get("prefix");
072: if (prefix == null) {
073: prefix = "";
074: }
075: try {
076: this .webapps.put(prefix, initWebApp(prefix, getWebRoot(
077: webroot, warfile), "webapp"));
078: } catch (Throwable err) {
079: Logger.log(Logger.ERROR, Launcher.RESOURCES,
080: "HostConfig.WebappInitError", prefix, err);
081: }
082: }
083: // Otherwise multi-webapp mode
084: else {
085: initMultiWebappDir(webappsDirName);
086: }
087: Logger.log(Logger.DEBUG, Launcher.RESOURCES,
088: "HostConfig.InitComplete", new String[] {
089: this .webapps.size() + "",
090: this .webapps.keySet() + "" });
091:
092: this .thread = new Thread(this , "WinstoneHostConfigurationMgmt:"
093: + this .hostname);
094: this .thread.setDaemon(true);
095: this .thread.start();
096: }
097:
098: public WebAppConfiguration getWebAppByURI(String uri) {
099: if (uri == null) {
100: return null;
101: } else if (uri.equals("/") || uri.equals("")) {
102: return (WebAppConfiguration) this .webapps.get("");
103: } else if (uri.startsWith("/")) {
104: String decoded = WinstoneRequest.decodeURLToken(uri);
105: String noLeadingSlash = decoded.substring(1);
106: int slashPos = noLeadingSlash.indexOf("/");
107: if (slashPos == -1) {
108: return (WebAppConfiguration) this .webapps.get(decoded);
109: } else {
110: return (WebAppConfiguration) this .webapps.get(decoded
111: .substring(0, slashPos + 1));
112: }
113: } else {
114: return null;
115: }
116: }
117:
118: protected WebAppConfiguration initWebApp(String prefix,
119: File webRoot, String contextName) throws IOException {
120: Node webXMLParentNode = null;
121: File webInfFolder = new File(webRoot, WEB_INF);
122: if (webInfFolder.exists()) {
123: File webXmlFile = new File(webInfFolder, WEB_XML);
124: if (webXmlFile.exists()) {
125: Logger.log(Logger.DEBUG, Launcher.RESOURCES,
126: "HostConfig.ParsingWebXml");
127: Document webXMLDoc = new WebXmlParser(this .commonLibCL)
128: .parseStreamToXML(webXmlFile);
129: if (webXMLDoc != null) {
130: webXMLParentNode = webXMLDoc.getDocumentElement();
131: Logger.log(Logger.DEBUG, Launcher.RESOURCES,
132: "HostConfig.WebXmlParseComplete");
133: } else {
134: Logger.log(Logger.DEBUG, Launcher.RESOURCES,
135: "HostConfig.WebXmlParseFailed");
136: }
137: }
138: }
139:
140: // Instantiate the webAppConfig
141: return new WebAppConfiguration(this , this .cluster, webRoot
142: .getCanonicalPath(), prefix, this .objectPool,
143: this .args, webXMLParentNode, this .commonLibCL,
144: this .commonLibCLPaths, contextName);
145: }
146:
147: public String getHostname() {
148: return this .hostname;
149: }
150:
151: /**
152: * Destroy this webapp instance. Kills the webapps, plus any servlets,
153: * attributes, etc
154: *
155: * @param webApp The webapp to destroy
156: */
157: private void destroyWebApp(String prefix) {
158: WebAppConfiguration webAppConfig = (WebAppConfiguration) this .webapps
159: .get(prefix);
160: if (webAppConfig != null) {
161: webAppConfig.destroy();
162: this .webapps.remove(prefix);
163: }
164: }
165:
166: public void destroy() {
167: Set prefixes = new HashSet(this .webapps.keySet());
168: for (Iterator i = prefixes.iterator(); i.hasNext();) {
169: destroyWebApp((String) i.next());
170: }
171: if (this .thread != null) {
172: this .thread.interrupt();
173: }
174: }
175:
176: public void invalidateExpiredSessions() {
177: Set webapps = new HashSet(this .webapps.values());
178: for (Iterator i = webapps.iterator(); i.hasNext();) {
179: ((WebAppConfiguration) i.next())
180: .invalidateExpiredSessions();
181: }
182: }
183:
184: public void run() {
185: boolean interrupted = false;
186: while (!interrupted) {
187: try {
188: Thread.sleep(FLUSH_PERIOD);
189: invalidateExpiredSessions();
190: } catch (InterruptedException err) {
191: interrupted = true;
192: }
193: }
194: this .thread = null;
195: }
196:
197: public void reloadWebApp(String prefix) throws IOException {
198: WebAppConfiguration webAppConfig = (WebAppConfiguration) this .webapps
199: .get(prefix);
200: if (webAppConfig != null) {
201: String webRoot = webAppConfig.getWebroot();
202: String contextName = webAppConfig.getContextName();
203: destroyWebApp(prefix);
204: try {
205: this .webapps.put(prefix, initWebApp(prefix, new File(
206: webRoot), contextName));
207: } catch (Throwable err) {
208: Logger.log(Logger.ERROR, Launcher.RESOURCES,
209: "HostConfig.WebappInitError", prefix, err);
210: }
211: } else {
212: throw new WinstoneException(Launcher.RESOURCES.getString(
213: "HostConfig.PrefixUnknown", prefix));
214: }
215: }
216:
217: /**
218: * Setup the webroot. If a warfile is supplied, extract any files that the
219: * war file is newer than. If none is supplied, use the default temp
220: * directory.
221: */
222: protected File getWebRoot(String requestedWebroot,
223: String warfileName) throws IOException {
224: if (warfileName != null) {
225: Logger.log(Logger.INFO, Launcher.RESOURCES,
226: "HostConfig.BeginningWarExtraction");
227:
228: // open the war file
229: File warfileRef = new File(warfileName);
230: if (!warfileRef.exists() || !warfileRef.isFile())
231: throw new WinstoneException(Launcher.RESOURCES
232: .getString("HostConfig.WarFileInvalid",
233: warfileName));
234:
235: // Get the webroot folder (or a temp dir if none supplied)
236: File unzippedDir = null;
237: if (requestedWebroot != null) {
238: unzippedDir = new File(requestedWebroot);
239: } else {
240: File tempFile = File.createTempFile("dummy", "dummy");
241: String userName = System.getProperty("user.name");
242: unzippedDir = new File(
243: tempFile.getParent(),
244: "winstone/"
245: + (userName != null ? WinstoneResourceBundle
246: .globalReplace(userName,
247: new String[][] {
248: { "/", "" },
249: { "\\", "" },
250: { ",", "" } })
251: + "/"
252: : "") + warfileRef.getName());
253: tempFile.delete();
254: }
255: if (unzippedDir.exists()) {
256: if (!unzippedDir.isDirectory()) {
257: throw new WinstoneException(Launcher.RESOURCES
258: .getString(
259: "HostConfig.WebRootNotDirectory",
260: unzippedDir.getPath()));
261: } else {
262: Logger.log(Logger.DEBUG, Launcher.RESOURCES,
263: "HostConfig.WebRootExists", unzippedDir
264: .getCanonicalPath());
265: }
266: } else {
267: unzippedDir.mkdirs();
268: }
269:
270: // Iterate through the files
271: JarFile warArchive = new JarFile(warfileRef);
272: for (Enumeration e = warArchive.entries(); e
273: .hasMoreElements();) {
274: JarEntry element = (JarEntry) e.nextElement();
275: if (element.isDirectory()) {
276: continue;
277: }
278: String elemName = element.getName();
279:
280: // If archive date is newer than unzipped file, overwrite
281: File outFile = new File(unzippedDir, elemName);
282: if (outFile.exists()
283: && (outFile.lastModified() > warfileRef
284: .lastModified())) {
285: continue;
286: }
287: outFile.getParentFile().mkdirs();
288: byte buffer[] = new byte[8192];
289:
290: // Copy out the extracted file
291: InputStream inContent = warArchive
292: .getInputStream(element);
293: OutputStream outStream = new FileOutputStream(outFile);
294: int readBytes = inContent.read(buffer);
295: while (readBytes != -1) {
296: outStream.write(buffer, 0, readBytes);
297: readBytes = inContent.read(buffer);
298: }
299: inContent.close();
300: outStream.close();
301: }
302:
303: // Return webroot
304: return unzippedDir;
305: } else {
306: return new File(requestedWebroot);
307: }
308: }
309:
310: protected void initMultiWebappDir(String webappsDirName)
311: throws IOException {
312: if (webappsDirName == null) {
313: webappsDirName = "webapps";
314: }
315: File webappsDir = new File(webappsDirName);
316: if (!webappsDir.exists()) {
317: throw new WinstoneException(Launcher.RESOURCES.getString(
318: "HostConfig.WebAppDirNotFound", webappsDirName));
319: } else if (!webappsDir.isDirectory()) {
320: throw new WinstoneException(Launcher.RESOURCES.getString(
321: "HostConfig.WebAppDirIsNotDirectory",
322: webappsDirName));
323: } else {
324: File children[] = webappsDir.listFiles();
325: for (int n = 0; n < children.length; n++) {
326: String childName = children[n].getName();
327:
328: // Check any directories for warfiles that match, and skip: only deploy the war file
329: if (children[n].isDirectory()) {
330: File matchingWarFile = new File(webappsDir,
331: children[n].getName() + ".war");
332: if (matchingWarFile.exists()
333: && matchingWarFile.isFile()) {
334: Logger.log(Logger.DEBUG, Launcher.RESOURCES,
335: "HostConfig.SkippingWarfileDir",
336: childName);
337: } else {
338: String prefix = childName
339: .equalsIgnoreCase("ROOT") ? "" : "/"
340: + childName;
341: if (!this .webapps.containsKey(prefix)) {
342: try {
343: WebAppConfiguration webAppConfig = initWebApp(
344: prefix, children[n], childName);
345: this .webapps
346: .put(webAppConfig
347: .getContextPath(),
348: webAppConfig);
349: Logger.log(Logger.INFO,
350: Launcher.RESOURCES,
351: "HostConfig.DeployingWebapp",
352: childName);
353: } catch (Throwable err) {
354: Logger.log(Logger.ERROR,
355: Launcher.RESOURCES,
356: "HostConfig.WebappInitError",
357: prefix, err);
358: }
359: }
360: }
361: } else if (childName.endsWith(".war")) {
362: String outputName = childName.substring(0,
363: childName.lastIndexOf(".war"));
364: String prefix = outputName.equalsIgnoreCase("ROOT") ? ""
365: : "/" + outputName;
366:
367: if (!this .webapps.containsKey(prefix)) {
368: File outputDir = new File(webappsDir,
369: outputName);
370: outputDir.mkdirs();
371: try {
372: WebAppConfiguration webAppConfig = initWebApp(
373: prefix,
374: getWebRoot(new File(webappsDir,
375: outputName)
376: .getCanonicalPath(),
377: children[n]
378: .getCanonicalPath()),
379: outputName);
380: this .webapps.put(webAppConfig
381: .getContextPath(), webAppConfig);
382: Logger.log(Logger.INFO, Launcher.RESOURCES,
383: "HostConfig.DeployingWebapp",
384: childName);
385: } catch (Throwable err) {
386: Logger.log(Logger.ERROR,
387: Launcher.RESOURCES,
388: "HostConfig.WebappInitError",
389: prefix, err);
390: }
391: }
392: }
393: }
394: }
395: }
396:
397: public WebAppConfiguration getWebAppBySessionKey(String sessionKey) {
398: List allwebapps = new ArrayList(this .webapps.values());
399: for (Iterator i = allwebapps.iterator(); i.hasNext();) {
400: WebAppConfiguration webapp = (WebAppConfiguration) i.next();
401: WinstoneSession session = webapp.getSessionById(sessionKey,
402: false);
403: if (session != null) {
404: return webapp;
405: }
406: }
407: return null;
408: }
409: }
|