//========================================================================
//$Id$
//Copyright 2006 Mort Bay Consulting Pty. Ltd.
//------------------------------------------------------------------------
//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.plugin;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.eclipse.jetty.annotations.AnnotationConfiguration;
import org.eclipse.jetty.plus.webapp.EnvConfiguration;
import org.eclipse.jetty.util.URIUtil;
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.util.resource.ResourceCollection;
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.WebAppContext;
import org.eclipse.jetty.webapp.WebXmlConfiguration;
/**
* JettyWebAppContext
*
* Extends the WebAppContext to specialize for the maven environment.
* We pass in the list of files that should form the classpath for
* the webapp when executing in the plugin, and any jetty-env.xml file
* that may have been configured.
*
*/
public class JettyWebAppContext extends WebAppContext
{
private static final Logger LOG = Log.getLogger(JettyWebAppContext.class);
private static final String WEB_INF_CLASSES_PREFIX = "/WEB-INF/classes";
private static final String WEB_INF_LIB_PREFIX = "/WEB-INF/lib";
private File classes = null;
private File testClasses = null;
private final List<File> webInfClasses = new ArrayList<File>();
private final List<File> webInfJars = new ArrayList<File>();
private final Map<String, File> webInfJarMap = new HashMap<String, File>();
private final EnvConfiguration envConfig;
private List<File> classpathFiles; //webInfClasses+testClasses+webInfJars
private String jettyEnvXml;
private List<Resource> overlays;
/**
* @deprecated The value of this parameter will be ignored by the plugin. Overlays will always be unpacked.
*/
private boolean unpackOverlays;
/**
* @deprecated The value of this parameter will be ignored by the plugin. This option will be always disabled.
*/
private boolean copyWebInf;
private boolean baseAppFirst = true;
public JettyWebAppContext ()
throws Exception
{
super();
setConfigurations(new Configuration[]{
new MavenWebInfConfiguration(),
new WebXmlConfiguration(),
new MetaInfConfiguration(),
new FragmentConfiguration(),
envConfig = new EnvConfiguration(),
new AnnotationConfiguration(),
new org.eclipse.jetty.plus.webapp.PlusConfiguration(),
new JettyWebXmlConfiguration(),
new TagLibConfiguration()
});
// Turn off copyWebInf option as it is not applicable for plugin.
super.setCopyWebInf(false);
}
public boolean getUnpackOverlays()
{
return unpackOverlays;
}
public void setUnpackOverlays(boolean unpackOverlays)
{
this.unpackOverlays = unpackOverlays;
}
public List<File> getClassPathFiles()
{
return this.classpathFiles;
}
public void setOverlays (List<Resource> overlays)
{
this.overlays = overlays;
}
public List<Resource> getOverlays ()
{
return this.overlays;
}
public void setJettyEnvXml (String jettyEnvXml)
{
this.jettyEnvXml = jettyEnvXml;
}
public String getJettyEnvXml()
{
return this.jettyEnvXml;
}
public void setClasses(File dir)
{
classes = dir;
}
public File getClasses()
{
return classes;
}
public void setWebInfLib (List<File> jars)
{
webInfJars.addAll(jars);
}
public void setTestClasses (File dir)
{
testClasses = dir;
}
public File getTestClasses ()
{
return testClasses;
}
/* ------------------------------------------------------------ */
@Override
public void setCopyWebInf(boolean value)
{
copyWebInf = value;
}
/* ------------------------------------------------------------ */
@Override
public boolean isCopyWebInf()
{
return copyWebInf;
}
/* ------------------------------------------------------------ */
public void setBaseAppFirst(boolean value)
{
baseAppFirst = value;
}
/* ------------------------------------------------------------ */
public boolean getBaseAppFirst()
{
return baseAppFirst;
}
/* ------------------------------------------------------------ */
/**
* This method is provided as a convenience for jetty maven plugin configuration
* @param resourceBases Array of resources strings to set as a {@link ResourceCollection}. Each resource string may be a comma separated list of resources
* @see Resource
*/
public void setResourceBases(String[] resourceBases)
{
List<String> resources = new ArrayList<String>();
for (String rl:resourceBases)
{
String[] rs = rl.split(" *, *");
for (String r:rs)
resources.add(r);
}
setBaseResource(new ResourceCollection(resources.toArray(new String[resources.size()])));
}
public List<File> getWebInfLib()
{
return webInfJars;
}
public void doStart () throws Exception
{
//Set up the pattern that tells us where the jars are that need scanning for
//stuff like taglibs so we can tell jasper about it (see TagLibConfiguration)
String tmp = (String)getAttribute("org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern");
tmp = addPattern(tmp, ".*/.*jsp-api-[^/]*\\.jar$");
tmp = addPattern(tmp, ".*/.*jsp-[^/]*\\.jar$");
tmp = addPattern(tmp, ".*/.*taglibs[^/]*\\.jar$");
tmp = addPattern(tmp, ".*/.*jstl[^/]*\\.jar$");
tmp = addPattern(tmp, ".*/.*jsf-impl-[^/]*\\.jar$"); // add in 2 most popular jsf impls
tmp = addPattern(tmp, ".*/.*javax.faces-[^/]*\\.jar$");
tmp = addPattern(tmp, ".*/.*myfaces-impl-[^/]*\\.jar$");
setAttribute("org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern", tmp);
//Set up the classes dirs that comprises the equivalent of WEB-INF/classes
if (testClasses != null)
webInfClasses.add(testClasses);
if (classes != null)
webInfClasses.add(classes);
// Set up the classpath
classpathFiles = new ArrayList<File>();
classpathFiles.addAll(webInfClasses);
classpathFiles.addAll(webInfJars);
// Initialize map containing all jars in /WEB-INF/lib
webInfJarMap.clear();
for (File file : webInfJars)
{
// Return all jar files from class path
String fileName = file.getName();
if (fileName.endsWith(".jar"))
webInfJarMap.put(fileName, file);
}
if (this.jettyEnvXml != null)
envConfig.setJettyEnvXml(Resource.toURL(new File(this.jettyEnvXml)));
setShutdown(false);
super.doStart();
}
public void doStop () throws Exception
{
if (classpathFiles != null)
classpathFiles.clear();
classpathFiles = null;
classes = null;
testClasses = null;
if (webInfJarMap != null)
webInfJarMap.clear();
webInfClasses.clear();
webInfJars.clear();
setShutdown(true);
//just wait a little while to ensure no requests are still being processed
Thread.currentThread().sleep(500L);
super.doStop();
}
@Override
public Resource getResource(String uriInContext) throws MalformedURLException
{
Resource resource = null;
// Try to get regular resource
resource = super.getResource(uriInContext);
// If no regular resource exists check for access to /WEB-INF/lib or /WEB-INF/classes
if ((resource == null || !resource.exists()) && uriInContext != null && classes != null)
{
String uri = URIUtil.canonicalPath(uriInContext);
if (uri == null)
return null;
try
{
// Replace /WEB-INF/classes with candidates for the classpath
if (uri.startsWith(WEB_INF_CLASSES_PREFIX))
{
if (uri.equalsIgnoreCase(WEB_INF_CLASSES_PREFIX) || uri.equalsIgnoreCase(WEB_INF_CLASSES_PREFIX+"/"))
{
//exact match for a WEB-INF/classes, so preferentially return the resource matching the web-inf classes
//rather than the test classes
if (classes != null)
return Resource.newResource(classes);
else if (testClasses != null)
return Resource.newResource(testClasses);
}
else
{
//try matching
Resource res = null;
int i=0;
while (res == null && (i < webInfClasses.size()))
{
String newPath = uri.replace(WEB_INF_CLASSES_PREFIX, webInfClasses.get(i).getPath());
res = Resource.newResource(newPath);
if (!res.exists())
{
res = null;
i++;
}
}
return res;
}
}
else if (uri.startsWith(WEB_INF_LIB_PREFIX))
{
// Return the real jar file for all accesses to
// /WEB-INF/lib/*.jar
String jarName = uri.replace(WEB_INF_LIB_PREFIX, "");
if (jarName.startsWith("/") || jarName.startsWith("\\"))
jarName = jarName.substring(1);
if (jarName.length()==0)
return null;
File jarFile = webInfJarMap.get(jarName);
if (jarFile != null)
return Resource.newResource(jarFile.getPath());
return null;
}
}
catch (MalformedURLException e)
{
throw e;
}
catch (IOException e)
{
LOG.ignore(e);
}
}
return resource;
}
@Override
public Set<String> getResourcePaths(String path)
{
// Try to get regular resource paths - this will get appropriate paths from any overlaid wars etc
Set<String> paths = super.getResourcePaths(path);
if (path != null)
{
TreeSet<String> allPaths = new TreeSet<String>();
allPaths.addAll(paths);
//add in the dependency jars as a virtual WEB-INF/lib entry
if (path.startsWith(WEB_INF_LIB_PREFIX))
{
for (String fileName : webInfJarMap.keySet())
{
// Return all jar files from class path
allPaths.add(WEB_INF_LIB_PREFIX + "/" + fileName);
}
}
else if (path.startsWith(WEB_INF_CLASSES_PREFIX))
{
int i=0;
while (i < webInfClasses.size())
{
String newPath = path.replace(WEB_INF_CLASSES_PREFIX, webInfClasses.get(i).getPath());
allPaths.addAll(super.getResourcePaths(newPath));
i++;
}
}
return allPaths;
}
return paths;
}
public String addPattern (String s, String pattern)
{
if (s == null)
s = "";
else
s = s.trim();
if (!s.contains(pattern))
{
if (s.length() != 0)
s = s + "|";
s = s + pattern;
}
return s;
}
}