blob: 7512581eb45fdef5139b0253730dbf6d78ed460c [file] [log] [blame]
//
// ========================================================================
// Copyright (c) 1995-2012 Sabre Holdings.
// ------------------------------------------------------------------------
// 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
//
// 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.
// ========================================================================
//
package org.eclipse.jetty.ant;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.CodeSource;
import java.security.PermissionCollection;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.EventListener;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.jar.Manifest;
import javax.servlet.Servlet;
import org.apache.tools.ant.AntClassLoader;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.types.FileSet;
import org.eclipse.jetty.annotations.AnnotationConfiguration;
import org.eclipse.jetty.ant.types.Attribute;
import org.eclipse.jetty.ant.types.Attributes;
import org.eclipse.jetty.ant.types.FileMatchingConfiguration;
import org.eclipse.jetty.ant.utils.TaskLog;
import org.eclipse.jetty.plus.webapp.EnvConfiguration;
import org.eclipse.jetty.plus.webapp.PlusConfiguration;
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlet.FilterMapping;
import org.eclipse.jetty.servlet.Holder;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.servlet.ServletMapping;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
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.WebAppClassLoader;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.webapp.WebInfConfiguration;
import org.eclipse.jetty.webapp.WebXmlConfiguration;
import org.eclipse.jetty.xml.XmlConfiguration;
/**
* Extension of WebAppContext to allow configuration via Ant environment.
*/
public class AntWebAppContext extends WebAppContext
{
private static final Logger LOG = Log.getLogger(WebAppContext.class);
public final AntWebInfConfiguration antWebInfConfiguration = new AntWebInfConfiguration();
public final WebXmlConfiguration webXmlConfiguration = new WebXmlConfiguration();
public final MetaInfConfiguration metaInfConfiguration = new MetaInfConfiguration();
public final FragmentConfiguration fragmentConfiguration = new FragmentConfiguration();
public final EnvConfiguration envConfiguration = new EnvConfiguration();
public final PlusConfiguration plusConfiguration = new PlusConfiguration();
public final AnnotationConfiguration annotationConfiguration = new AnnotationConfiguration();
public final JettyWebXmlConfiguration jettyWebXmlConfiguration = new JettyWebXmlConfiguration();
public final Configuration[] DEFAULT_CONFIGURATIONS =
{
antWebInfConfiguration,
webXmlConfiguration,
metaInfConfiguration,
fragmentConfiguration,
envConfiguration,
plusConfiguration,
annotationConfiguration,
jettyWebXmlConfiguration
};
public final static String DEFAULT_CONTAINER_INCLUDE_JAR_PATTERN =
".*/.*jsp-api-[^/]*\\.jar$|.*/.*jsp-[^/]*\\.jar$|.*/.*taglibs[^/]*\\.jar$|.*/.*jstl[^/]*\\.jar$|.*/.*jsf-impl-[^/]*\\.jar$|.*/.*javax.faces-[^/]*\\.jar$|.*/.*myfaces-impl-[^/]*\\.jar$";
/** Location of jetty-env.xml file. */
private File jettyEnvXml;
/** List of web application libraries. */
private List libraries = new ArrayList();
/** List of web application class directories. */
private List classes = new ArrayList();
/** context xml file to apply to the webapp */
private File contextXml;
/** List of extra scan targets for this web application. */
private FileSet scanTargets;
/** context attributes to set **/
private Attributes attributes;
private Project project;
private List<File> scanFiles;
/** Extra scan targets. */
private FileMatchingConfiguration extraScanTargetsConfiguration;
private FileMatchingConfiguration librariesConfiguration;
public static void dump(ClassLoader loader)
{
while (loader != null)
{
System.err.println(loader);
if (loader instanceof URLClassLoader)
{
URL[] urls = ((URLClassLoader)loader).getURLs();
if (urls != null)
{
for (URL u:urls)
System.err.println("\t"+u+"\n");
}
}
loader = loader.getParent();
}
}
/**
* AntURLClassLoader
*
* Adapt the AntClassLoader which is not a URLClassLoader - this is needed for
* jsp to be able to search the classpath.
*/
public static class AntURLClassLoader extends URLClassLoader
{
private AntClassLoader antLoader;
public AntURLClassLoader(AntClassLoader antLoader)
{
super(new URL[] {}, antLoader);
this.antLoader = antLoader;
}
@Override
public InputStream getResourceAsStream(String name)
{
return super.getResourceAsStream(name);
}
@Override
public void close() throws IOException
{
super.close();
}
@Override
protected void addURL(URL url)
{
super.addURL(url);
}
@Override
public URL[] getURLs()
{
Set<URL> urls = new HashSet<URL>();
//convert urls from antLoader
String[] paths = antLoader.getClasspath().split(new String(new char[]{File.pathSeparatorChar}));
if (paths != null)
{
for (String p:paths)
{
File f = new File(p);
try
{
urls.add(f.toURI().toURL());
}
catch (Exception e)
{
LOG.ignore(e);
}
}
}
//add in any that may have been added to us as a URL directly
URL[] ourURLS = super.getURLs();
if (ourURLS != null)
{
for (URL u:ourURLS)
urls.add(u);
}
return urls.toArray(new URL[urls.size()]);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException
{
return super.findClass(name);
}
@Override
protected Package definePackage(String name, Manifest man, URL url) throws IllegalArgumentException
{
return super.definePackage(name, man, url);
}
@Override
public URL findResource(String name)
{
return super.findResource(name);
}
@Override
public Enumeration<URL> findResources(String name) throws IOException
{
return super.findResources(name);
}
@Override
protected PermissionCollection getPermissions(CodeSource codesource)
{
return super.getPermissions(codesource);
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException
{
return super.loadClass(name);
}
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
{
return super.loadClass(name, resolve);
}
@Override
protected Object getClassLoadingLock(String className)
{
return super.getClassLoadingLock(className);
}
@Override
public URL getResource(String name)
{
return super.getResource(name);
}
@Override
public Enumeration<URL> getResources(String name) throws IOException
{
return super.getResources(name);
}
@Override
protected Package definePackage(String name, String specTitle, String specVersion, String specVendor, String implTitle, String implVersion,
String implVendor, URL sealBase) throws IllegalArgumentException
{
return super.definePackage(name, specTitle, specVersion, specVendor, implTitle, implVersion, implVendor, sealBase);
}
@Override
protected Package getPackage(String name)
{
return super.getPackage(name);
}
@Override
protected Package[] getPackages()
{
return super.getPackages();
}
@Override
protected String findLibrary(String libname)
{
return super.findLibrary(libname);
}
@Override
public void setDefaultAssertionStatus(boolean enabled)
{
super.setDefaultAssertionStatus(enabled);
}
@Override
public void setPackageAssertionStatus(String packageName, boolean enabled)
{
super.setPackageAssertionStatus(packageName, enabled);
}
@Override
public void setClassAssertionStatus(String className, boolean enabled)
{
super.setClassAssertionStatus(className, enabled);
}
@Override
public void clearAssertionStatus()
{
super.clearAssertionStatus();
}
}
/**
* AntServletHolder
*
*
*/
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);
}
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();
}
}
/**
* AntServletHandler
*
*
*/
public static class AntServletHandler extends ServletHandler
{
@Override
public ServletHolder newServletHolder(Holder.Source source)
{
return new AntServletHolder();
}
}
/**
* Default constructor. Takes project as an argument
*
* @param project the project.
* @throws Exception if unable to create webapp context
*/
public AntWebAppContext(Project project) throws Exception
{
super();
this.project = project;
setConfigurations(DEFAULT_CONFIGURATIONS);
setAttribute(WebInfConfiguration.CONTAINER_JAR_PATTERN, DEFAULT_CONTAINER_INCLUDE_JAR_PATTERN);
setParentLoaderPriority(true);
}
/**
* Adds a new Ant's attributes tag object if it have not been created yet.
* @param atts the attributes
*/
public void addAttributes(Attributes atts)
{
if (this.attributes != null)
{
throw new BuildException("Only one <attributes> tag is allowed!");
}
this.attributes = atts;
}
public void addLib(FileSet lib)
{
libraries.add(lib);
}
public void addClasses(FileSet classes)
{
this.classes.add(classes);
}
@Override
protected ServletHandler newServletHandler()
{
return new AntServletHandler();
}
public void setJettyEnvXml(File jettyEnvXml)
{
this.jettyEnvXml = jettyEnvXml;
TaskLog.log("jetty-env.xml file: = " + (jettyEnvXml == null ? null : jettyEnvXml.getAbsolutePath()));
}
public File getJettyEnvXml ()
{
return this.jettyEnvXml;
}
public List getLibraries()
{
return librariesConfiguration.getBaseDirectories();
}
public void addScanTargets(FileSet scanTargets)
{
if (this.scanTargets != null)
{
throw new BuildException("Only one <scanTargets> tag is allowed!");
}
this.scanTargets = scanTargets;
}
public List getScanTargetFiles ()
{
if (this.scanTargets == null)
return null;
FileMatchingConfiguration configuration = new FileMatchingConfiguration();
configuration.addDirectoryScanner(scanTargets.getDirectoryScanner(project));
return configuration.getBaseDirectories();
}
public List<File> getScanFiles()
{
if (scanFiles == null)
scanFiles = initScanFiles();
return scanFiles;
}
public boolean isScanned (File file)
{
List<File> files = getScanFiles();
if (files == null || files.isEmpty())
return false;
return files.contains(file);
}
public List<File> initScanFiles ()
{
List<File> scanList = new ArrayList<File>();
if (getDescriptor() != null)
{
try (Resource r = Resource.newResource(getDescriptor());)
{
scanList.add(r.getFile());
}
catch (IOException e)
{
throw new BuildException(e);
}
}
if (getJettyEnvXml() != null)
{
try (Resource r = Resource.newResource(getJettyEnvXml());)
{
scanList.add(r.getFile());
}
catch (IOException e)
{
throw new BuildException("Problem configuring scanner for jetty-env.xml", e);
}
}
if (getDefaultsDescriptor() != null)
{
try (Resource r = Resource.newResource(getDefaultsDescriptor());)
{
if (!WebAppContext.WEB_DEFAULTS_XML.equals(getDefaultsDescriptor()))
{
scanList.add(r.getFile());
}
}
catch (IOException e)
{
throw new BuildException("Problem configuring scanner for webdefaults.xml", e);
}
}
if (getOverrideDescriptor() != null)
{
try
{
Resource r = Resource.newResource(getOverrideDescriptor());
scanList.add(r.getFile());
}
catch (IOException e)
{
throw new BuildException("Problem configuring scanner for webdefaults.xml", e);
}
}
//add any extra classpath and libs
List<File> cpFiles = getClassPathFiles();
if (cpFiles != null)
scanList.addAll(cpFiles);
//any extra scan targets
@SuppressWarnings("unchecked")
List<File> scanFiles = (List<File>)getScanTargetFiles();
if (scanFiles != null)
scanList.addAll(scanFiles);
return scanList;
}
@Override
public void setWar(String path)
{
super.setWar(path);
try
{
Resource war = Resource.newResource(path);
if (war.exists() && war.isDirectory() && getDescriptor() == null)
{
Resource webXml = war.addPath("WEB-INF/web.xml");
setDescriptor(webXml.toString());
}
}
catch (IOException e)
{
throw new BuildException(e);
}
}
/**
*
*/
public void doStart()
{
try
{
TaskLog.logWithTimestamp("Starting web application "+this.getDescriptor());
if (jettyEnvXml != null && jettyEnvXml.exists())
envConfiguration.setJettyEnvXml(Resource.toURL(jettyEnvXml));
ClassLoader parentLoader = this.getClass().getClassLoader();
if (parentLoader instanceof AntClassLoader)
parentLoader = new AntURLClassLoader((AntClassLoader)parentLoader);
setClassLoader(new WebAppClassLoader(parentLoader, this));
if (attributes != null && attributes.getAttributes() != null)
{
for (Attribute a:attributes.getAttributes())
setAttribute(a.getName(), a.getValue());
}
//apply a context xml file if one was supplied
if (contextXml != null)
{
XmlConfiguration xmlConfiguration = new XmlConfiguration(Resource.toURL(contextXml));
TaskLog.log("Applying context xml file "+contextXml);
xmlConfiguration.configure(this);
}
super.doStart();
}
catch (Exception e)
{
TaskLog.log(e.toString());
}
}
public void doStop()
{
try
{
scanFiles = null;
TaskLog.logWithTimestamp("Stopping web application "+this);
Thread.currentThread().sleep(500L);
super.doStop();
//remove all filters, servlets and listeners. They will be recreated
//either via application of a context xml file or web.xml or annotation or servlet api
setEventListeners(new EventListener[0]);
getServletHandler().setFilters(new FilterHolder[0]);
getServletHandler().setFilterMappings(new FilterMapping[0]);
getServletHandler().setServlets(new ServletHolder[0]);
getServletHandler().setServletMappings(new ServletMapping[0]);
}
catch (InterruptedException e)
{
TaskLog.log(e.toString());
}
catch (Exception e)
{
TaskLog.log(e.toString());
}
}
/**
* @return a list of classpath files (libraries and class directories).
*/
public List<File> getClassPathFiles()
{
List<File> classPathFiles = new ArrayList<File>();
Iterator classesIterator = classes.iterator();
while (classesIterator.hasNext())
{
FileSet clazz = (FileSet) classesIterator.next();
classPathFiles.add(clazz.getDirectoryScanner(project).getBasedir());
}
Iterator iterator = libraries.iterator();
while (iterator.hasNext())
{
FileSet library = (FileSet) iterator.next();
String[] includedFiles = library.getDirectoryScanner(project).getIncludedFiles();
File baseDir = library.getDirectoryScanner(project).getBasedir();
for (int i = 0; i < includedFiles.length; i++)
{
classPathFiles.add(new File(baseDir, includedFiles[i]));
}
}
return classPathFiles;
}
/**
* @return a <code>FileMatchingConfiguration</code> object describing the
* configuration of all libraries added to this particular web app
* (both classes and libraries).
*/
public FileMatchingConfiguration getLibrariesConfiguration()
{
FileMatchingConfiguration config = new FileMatchingConfiguration();
Iterator classesIterator = classes.iterator();
while (classesIterator.hasNext())
{
FileSet clazz = (FileSet) classesIterator.next();
config.addDirectoryScanner(clazz.getDirectoryScanner(project));
}
Iterator librariesIterator = libraries.iterator();
while (librariesIterator.hasNext())
{
FileSet library = (FileSet) librariesIterator.next();
config.addDirectoryScanner(library.getDirectoryScanner(project));
}
return config;
}
public File getContextXml()
{
return contextXml;
}
public void setContextXml(File contextXml)
{
this.contextXml = contextXml;
}
}