// ======================================================================== // Copyright 2006-2007 Sabre Holdings. // ------------------------------------------------------------------------ // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // http://www.apache.org/licenses/LICENSE-2.0 // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ======================================================================== package org.mortbay.jetty.ant; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.Iterator; import java.util.List; import javax.servlet.Servlet; import org.apache.tools.ant.AntClassLoader; import org.eclipse.jetty.annotations.AnnotationConfiguration; import org.eclipse.jetty.plus.webapp.EnvConfiguration; import org.eclipse.jetty.plus.webapp.PlusConfiguration; import org.eclipse.jetty.security.SecurityHandler; import org.eclipse.jetty.server.HandlerContainer; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.ContextHandlerCollection; import org.eclipse.jetty.server.handler.ErrorHandler; import org.eclipse.jetty.server.session.SessionHandler; import org.eclipse.jetty.servlet.ServletHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.webapp.Configuration; import org.eclipse.jetty.webapp.FragmentConfiguration; import org.eclipse.jetty.webapp.JettyWebXmlConfiguration; import org.eclipse.jetty.webapp.MetaInfConfiguration; import org.eclipse.jetty.webapp.TagLibConfiguration; import org.eclipse.jetty.webapp.WebAppClassLoader; import org.eclipse.jetty.webapp.WebAppContext; import org.eclipse.jetty.webapp.WebInfConfiguration; import org.eclipse.jetty.webapp.WebXmlConfiguration; import org.mortbay.jetty.ant.types.Attribute; import org.mortbay.jetty.ant.types.FileMatchingConfiguration; import org.mortbay.jetty.ant.utils.TaskLog; import org.mortbay.jetty.ant.utils.WebApplicationProxy; /** * An abstraction layer over Jetty WebAppContext. * * @author Jakub Pawlowicz */ public class WebApplicationProxyImpl implements WebApplicationProxy { /** Common root temp directory for all web applications. */ static File baseTempDirectory = new File("."); /** Name of this web application. */ private String name; /** Location of WAR file (either expanded or not). */ private File warFile; /** Application context path. */ private String contextPath; /** Location of web.xml file. */ private File webXmlFile; /** Location of jetty-env.xml file. */ private File jettyEnvXml; /** List of classpath files. */ private List classPathFiles; /** Jetty6 Web Application Context. */ private WebAppContext webAppContext; /** Extra scan targets. */ private FileMatchingConfiguration extraScanTargetsConfiguration; /** Extra context handlers. */ private List contextHandlers; private List attributes; Configuration[] configurations; private FileMatchingConfiguration librariesConfiguration; public static void setBaseTempDirectory(File tempDirectory) { baseTempDirectory = tempDirectory; } public static class AntServletHolder extends ServletHolder { public AntServletHolder() { super(); } public AntServletHolder(Class<? extends Servlet> servlet) { super(servlet); } public AntServletHolder(Servlet servlet) { super(servlet); } public AntServletHolder(String name, Class<? extends Servlet> servlet) { super(name, servlet); } public AntServletHolder(String name, Servlet servlet) { super(name, servlet); } @Override protected void initJspServlet() throws Exception { //super.initJspServlet(); ContextHandler ch = ((ContextHandler.Context)getServletHandler().getServletContext()).getContextHandler(); /* Set the webapp's classpath for Jasper */ ch.setAttribute("org.apache.catalina.jsp_classpath", ch.getClassPath()); /* Set the system classpath for Jasper */ String sysClassPath = getSystemClassPath(ch.getClassLoader().getParent()); setInitParameter("com.sun.appserv.jsp.classpath", sysClassPath); /* Set up other classpath attribute */ if ("?".equals(getInitParameter("classpath"))) { String classpath = ch.getClassPath(); if (classpath != null) setInitParameter("classpath", classpath); } } protected String getSystemClassPath (ClassLoader loader) throws Exception { StringBuilder classpath=new StringBuilder(); while (loader != null) { if (loader instanceof URLClassLoader) { URL[] urls = ((URLClassLoader)loader).getURLs(); if (urls != null) { for (int i=0;i<urls.length;i++) { Resource resource = Resource.newResource(urls[i]); File file=resource.getFile(); if (file!=null && file.exists()) { if (classpath.length()>0) classpath.append(File.pathSeparatorChar); classpath.append(file.getAbsolutePath()); } } } } else if (loader instanceof AntClassLoader) { classpath.append(((AntClassLoader)loader).getClasspath()); } loader = loader.getParent(); } return classpath.toString(); } } public static class AntServletHandler extends ServletHandler { @Override public ServletHolder newServletHolder() { return new AntServletHolder(); } @Override public ServletHolder newServletHolder(Class<? extends Servlet> servlet) { return new AntServletHolder(servlet); } } public static class AntWebAppContext extends WebAppContext { public AntWebAppContext() { super(); } public AntWebAppContext(HandlerContainer parent, String webApp, String contextPath) { super(parent, webApp, contextPath); } public AntWebAppContext(SessionHandler sessionHandler, SecurityHandler securityHandler, ServletHandler servletHandler, ErrorHandler errorHandler) { super(sessionHandler, securityHandler, servletHandler, errorHandler); } public AntWebAppContext(String webApp, String contextPath) { super(webApp, contextPath); } @Override protected ServletHandler newServletHandler() { return new AntServletHandler(); } } /** * Default constructor. Takes application name as an argument * * @param name web application name. */ public WebApplicationProxyImpl(String name) throws Exception { this.name = name; TaskLog.log("\nConfiguring Jetty for web application: " + name); this.configurations = new Configuration[] { new AntWebInfConfiguration(), new AntWebXmlConfiguration(), new MetaInfConfiguration(), new FragmentConfiguration(), new EnvConfiguration(), new PlusConfiguration(), new AnnotationConfiguration(), new JettyWebXmlConfiguration(), new TagLibConfiguration() }; } public List getClassPathFiles() { return classPathFiles; } public String getContextPath() { return contextPath; } public String getName() { return name; } public File getSourceDirectory() { return warFile; } public File getWebXmlFile() { return webXmlFile; } public void setSourceDirectory(File warFile) { this.warFile = warFile; TaskLog.log("Webapp source directory = " + warFile); } public void setContextPath(String contextPath) { if (!contextPath.startsWith("/")) { contextPath = "/" + contextPath; } this.contextPath = contextPath; TaskLog.log("Context path = " + contextPath); } public void setWebXml(File webXmlFile) { this.webXmlFile = webXmlFile; } public void setJettyEnvXml(File jettyEnvXml) { this.jettyEnvXml = jettyEnvXml; if (this.jettyEnvXml != null) { TaskLog.log("jetty-env.xml file: = " + jettyEnvXml.getAbsolutePath()); } } public void setClassPathFiles(List classPathFiles) { this.classPathFiles = classPathFiles; TaskLog.log("Classpath = " + classPathFiles); } /** * Checks if a given file is scanned according to the internal * configuration. This may be difficult due to use of 'includes' and * 'excludes' statements. * * @param pathToFile a fully qualified path to file. * @return true if file is being scanned, false otherwise. */ public boolean isFileScanned(String pathToFile) { return librariesConfiguration.isIncluded(pathToFile) || extraScanTargetsConfiguration.isIncluded(pathToFile); } public void setLibrariesConfiguration(FileMatchingConfiguration classesConfiguration) { TaskLog.log("Default scanned paths = " + classesConfiguration.getBaseDirectories()); this.librariesConfiguration = classesConfiguration; } public List getLibraries() { return librariesConfiguration.getBaseDirectories(); } public void setExtraScanTargetsConfiguration( FileMatchingConfiguration extraScanTargetsConfiguration) { this.extraScanTargetsConfiguration = extraScanTargetsConfiguration; TaskLog.log("Extra scan targets = " + extraScanTargetsConfiguration.getBaseDirectories()); } public void setAttributes(List attributes) { this.attributes = attributes; } public List getExtraScanTargets() { return extraScanTargetsConfiguration.getBaseDirectories(); } public List getContextHandlers() { return contextHandlers; } public void setContextHandlers(List contextHandlers) { this.contextHandlers = contextHandlers; } /** * @see WebApplicationProxy#getProxiedObject() */ public Object getProxiedObject() { return webAppContext; } /** * @see WebApplicationProxy#start() */ public void start() { try { TaskLog.logWithTimestamp("Starting web application " + name + " ...\n"); webAppContext.setShutdown(false); webAppContext.start(); } catch (Exception e) { TaskLog.log(e.toString()); } } /** * @see WebApplicationProxy#stop() */ public void stop() { try { TaskLog.logWithTimestamp("Stopping web application " + name + " ..."); webAppContext.setShutdown(true); Thread.currentThread().sleep(500L); webAppContext.stop(); } catch (InterruptedException e) { TaskLog.log(e.toString()); } catch (Exception e) { TaskLog.log(e.toString()); } } /** * @see WebApplicationProxy#createApplicationContext(org.eclipse.jetty.server.handler.ContextHandlerCollection) */ public void createApplicationContext(ContextHandlerCollection contexts) { webAppContext = new AntWebAppContext(contexts, warFile.getAbsolutePath(), contextPath); webAppContext.setDisplayName(name); configurePaths(); if ( !attributes.isEmpty() ) { for ( Iterator i = attributes.iterator(); i.hasNext(); ) { Attribute attr = (Attribute) i.next(); webAppContext.setAttribute(attr.getName(),attr.getValue()); } } configureHandlers(contexts); applyConfiguration(); } private void configureHandlers(ContextHandlerCollection contexts) { // adding extra context handlers Iterator handlersIterator = contextHandlers.iterator(); while (handlersIterator.hasNext()) { ContextHandler contextHandler = (ContextHandler) handlersIterator.next(); contexts.addHandler(contextHandler); } } private void configurePaths() { // configuring temp directory File tempDir = new File(baseTempDirectory, contextPath); if (!tempDir.exists()) { tempDir.mkdirs(); } webAppContext.setTempDirectory(tempDir); tempDir.deleteOnExit(); TaskLog.log("Temp directory = " + tempDir.getAbsolutePath()); // configuring WAR directory for packaged web applications if (warFile.isFile()) { warFile = new File(tempDir, "webapp"); webXmlFile = new File(new File(warFile, "WEB-INF"), "web.xml"); } } /** * Applies web application configuration at the end of configuration process * or after application restart. */ void applyConfiguration() { for (int i = 0; i < configurations.length; i++) { if (configurations[i] instanceof EnvConfiguration) { try { if (jettyEnvXml != null && jettyEnvXml.exists()) { ((EnvConfiguration) configurations[i]).setJettyEnvXml(Resource.toURL(jettyEnvXml)); } } catch (MalformedURLException e) { throw new RuntimeException(e); } } else if (configurations[i] instanceof AntWebXmlConfiguration) { ((AntWebXmlConfiguration) configurations[i]).setClassPathFiles(classPathFiles); ((AntWebXmlConfiguration) configurations[i]).setWebAppBaseDir(warFile); ((AntWebXmlConfiguration) configurations[i]).setWebXmlFile(webXmlFile); ((AntWebXmlConfiguration) configurations[i]).setWebDefaultXmlFile(webDefaultXmlFile); } } try { ClassLoader loader = new WebAppClassLoader(this.getClass().getClassLoader(), webAppContext); webAppContext.setParentLoaderPriority(true); webAppContext.setClassLoader(loader); if (webDefaultXmlFile != null) webAppContext.setDefaultsDescriptor(webDefaultXmlFile.getCanonicalPath()); } catch (IOException e) { TaskLog.log(e.toString()); } webAppContext.setConfigurations(configurations); } private File webDefaultXmlFile; public File getWebDefaultXmlFile() { return this.webDefaultXmlFile; } public void setWebDefaultXmlFile(File webDefaultXmlfile) { this.webDefaultXmlFile = webDefaultXmlfile; } }