Ensure MetaInfConfiguration finds all candidate jars from the container path and also from web-inf, then applies inclusion patterns to them to do an initial ordering. Jetty-8 will modify this ordering based on web-fragment ordering. The list of jars is passed around as a context attribute. The inclusion patterns are specified as context attributes. The ContextDeployer and WebAppDeployer can be configured with context attributes (such as the default inclusion patterns) that are set on every context they deploy. The list of jars are passed around as a List of Resources, and the pattern matching is done based on the URI (ie as a name string) rather than as a jar URL.

git-svn-id: svn+ssh://dev.eclipse.org/svnroot/rt/org.eclipse.jetty/jetty/trunk@310 7e9141cc-0065-0410-87d8-b60c137991c4
This commit is contained in:
Jan Bartel 2009-05-28 03:51:44 +00:00
parent 0ef6db092a
commit 4675e38841
20 changed files with 479 additions and 285 deletions

View File

@ -13,6 +13,10 @@
package org.eclipse.jetty.annotations;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.EventListener;
import java.util.List;
@ -25,6 +29,7 @@ import org.eclipse.jetty.util.LazyList;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.webapp.WebInfConfiguration;
/**
* Configuration
@ -33,8 +38,7 @@ import org.eclipse.jetty.webapp.WebAppContext;
*/
public class AnnotationConfiguration extends org.eclipse.jetty.plus.webapp.Configuration
{
public static final String __web_inf_pattern = "org.eclipse.jetty.server.webapp.WebInfIncludeAnnotationJarPattern";
public static final String __container_pattern = "org.eclipse.jetty.server.webapp.ContainerIncludeAnnotationJarPattern";
public static final String JAR_RESOURCES = WebInfConfiguration.JAR_RESOURCES;
@ -89,7 +93,25 @@ public class AnnotationConfiguration extends org.eclipse.jetty.plus.webapp.Confi
{
//if no pattern for the container path is defined, then by default scan NOTHING
Log.debug("Scanning container jars");
parseAnnotationsFromJars(context, finder, context.getClassLoader().getParent(), context.getInitParameter(__container_pattern),true, false,
//Get the container jar uris
ArrayList<URI> containerCandidateUris = findJars (context.getClassLoader().getParent(), true);
//Pick out the uris from JAR_RESOURCES that match those uris to be scanned
ArrayList<URI> containerUris = new ArrayList<URI>();
List<Resource> jarResources = (List<Resource>)context.getAttribute(JAR_RESOURCES);
for (Resource r : jarResources)
{
URI uri = r.getURI();
if (containerCandidateUris.contains(uri))
{
containerUris.add(uri);
}
}
finder.find (containerUris.toArray(new URI[containerUris.size()]),
new ClassNameResolver ()
{
public boolean isExcluded (String name)
@ -114,8 +136,23 @@ public class AnnotationConfiguration extends org.eclipse.jetty.plus.webapp.Confi
throws Exception
{
Log.debug("Scanning WEB-INF/lib jars");
//Get the uris of jars on the webapp classloader
ArrayList<URI> candidateUris = findJars(context.getClassLoader(), false);
//Pick out the uris from JAR_RESOURCES that match those to be scanned
ArrayList<URI> webInfUris = new ArrayList<URI>();
List<Resource> jarResources = (List<Resource>)context.getAttribute(JAR_RESOURCES);
for (Resource r : jarResources)
{
URI uri = r.getURI();
if (candidateUris.contains(uri))
{
webInfUris.add(uri);
}
}
//if no pattern for web-inf/lib is defined, then by default scan everything in it
parseAnnotationsFromJars (context, finder, context.getClassLoader(), context.getInitParameter(__web_inf_pattern), false, true,
finder.find(webInfUris.toArray(new URI[webInfUris.size()]),
new ClassNameResolver()
{
public boolean isExcluded (String name)
@ -140,37 +177,54 @@ public class AnnotationConfiguration extends org.eclipse.jetty.plus.webapp.Confi
throws Exception
{
Log.debug("Scanning classes in WEB-INF/classes");
parseAnnotationsFromDir (context, finder, context.getWebInf().addPath("classes/"),
new ClassNameResolver()
finder.find(context.getWebInf().addPath("classes/"),
new ClassNameResolver()
{
public boolean isExcluded (String name)
{
if (context.isSystemClass(name)) return true;
if (context.isServerClass(name)) return false;
return false;
}
public boolean shouldOverride (String name)
{
//looking at webapp classpath, found already-parsed class of same name - did it come from system or duplicate in webapp?
if (context.isParentLoaderPriority())
return false;
return true;
}
});
}
public ArrayList<URI> findJars (ClassLoader loader, boolean visitParent)
{
ArrayList<URI> uris = new ArrayList<URI>();
while (loader != null && (loader instanceof URLClassLoader))
{
URL[] urls = ((URLClassLoader)loader).getURLs();
if (urls != null)
{
for (URL u : urls)
{
public boolean isExcluded (String name)
try
{
if (context.isSystemClass(name)) return true;
if (context.isServerClass(name)) return false;
return false;
uris.add(u.toURI());
}
public boolean shouldOverride (String name)
catch (Exception e)
{
//looking at webapp classpath, found already-parsed class of same name - did it come from system or duplicate in webapp?
if (context.isParentLoaderPriority())
return false;
return true;
Log.warn(e);
}
});
}
public void parseAnnotationsFromJars (final WebAppContext context, final AnnotationFinder finder, final ClassLoader classloader, final String pattern, boolean visitParents, boolean isNullInclusive, ClassNameResolver resolver)
throws Exception
{
finder.find (classloader, visitParents, pattern, isNullInclusive,resolver);
}
public void parseAnnotationsFromDir (final WebAppContext context, final AnnotationFinder finder, final Resource dir, ClassNameResolver resolver)
throws Exception
{
finder.find(dir, resolver);
}
}
if (visitParent)
loader = loader.getParent();
else
loader = null;
}
return uris;
}
}

View File

@ -18,6 +18,7 @@ import java.io.InputStream;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
@ -586,7 +587,7 @@ public class AnnotationFinder
* @param resolver
* @throws Exception
*/
public void find (ClassLoader loader, boolean visitParents, String jarNamePattern, boolean nullInclusive, final ClassNameResolver resolver)
public void find (ClassLoader loader, boolean visitParents, boolean nullInclusive, final ClassNameResolver resolver)
throws Exception
{
if (loader==null)
@ -597,7 +598,7 @@ public class AnnotationFinder
JarScanner scanner = new JarScanner()
{
public void processEntry(URL jarUrl, JarEntry entry)
public void processEntry(URI jarUri, JarEntry entry)
{
try
{
@ -610,7 +611,7 @@ public class AnnotationFinder
if ((parsedClasses.get(shortName) == null) || (resolver.shouldOverride(shortName)))
{
parsedClasses.remove(shortName);
Resource clazz = Resource.newResource("jar:"+jarUrl+"!/"+name);
Resource clazz = Resource.newResource("jar:"+jarUri+"!/"+name);
scanClass(clazz.getInputStream());
}
}
@ -623,32 +624,26 @@ public class AnnotationFinder
}
};
Pattern pattern = null;
if (jarNamePattern!=null)
pattern = Pattern.compile(jarNamePattern);
scanner.scan(pattern, loader, nullInclusive, visitParents);
scanner.scan(null, loader, nullInclusive, visitParents);
}
/**
* Find annotations in classes in classes in the supplied url of jar files.
* @param urls
* @param visitParents
* @param jarNamePattern
* @param nullInclusive
* Find annotations in classes in the supplied url of jar files.
* @param uris
* @param resolver
* @throws Exception
*/
public void find (URL[] urls, boolean visitParents, String jarNamePattern, boolean nullInclusive, final ClassNameResolver resolver)
public void find (URI[] uris, final ClassNameResolver resolver)
throws Exception
{
if (urls==null)
if (uris==null)
return;
JarScanner scanner = new JarScanner()
{
public void processEntry(URL jarUrl, JarEntry entry)
public void processEntry(URI jarUri, JarEntry entry)
{
try
{
@ -661,7 +656,7 @@ public class AnnotationFinder
if ((parsedClasses.get(shortName) == null) || (resolver.shouldOverride(shortName)))
{
parsedClasses.remove(shortName);
Resource clazz = Resource.newResource("jar:"+jarUrl+"!/"+name);
Resource clazz = Resource.newResource("jar:"+jarUri+"!/"+name);
scanClass(clazz.getInputStream());
}
}
@ -673,12 +668,8 @@ public class AnnotationFinder
}
}
};
Pattern pattern = null;
if (jarNamePattern!=null)
pattern = Pattern.compile(jarNamePattern);
scanner.scan(pattern, urls, nullInclusive);
};
scanner.scan(null, uris, true);
}

View File

@ -22,6 +22,7 @@ import java.util.Map;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.util.AttributesMap;
import org.eclipse.jetty.util.Scanner;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.log.Log;
@ -73,7 +74,8 @@ public class ContextDeployer extends AbstractLifeCycle
private ContextHandlerCollection _contexts;
private ConfigurationManager _configMgr;
private boolean _recursive = false;
private AttributesMap _contextAttributes = new AttributesMap();
/* ------------------------------------------------------------ */
protected class ScannerListener implements Scanner.DiscreteListener
{
@ -255,6 +257,39 @@ public class ContextDeployer extends AbstractLifeCycle
{
return _recursive;
}
/**
* Set a contextAttribute that will be set for every Context deployed by this deployer.
* @param name
* @param value
*/
public void setAttribute (String name, Object value)
{
_contextAttributes.setAttribute(name,value);
}
/**
* Get a contextAttribute that will be set for every Context deployed by this deployer.
* @param name
* @return
*/
public Object getAttribute (String name)
{
return _contextAttributes.getAttribute(name);
}
/**
* Remove a contextAttribute that will be set for every Context deployed by this deployer.
* @param name
*/
public void removeAttribute(String name)
{
_contextAttributes.removeAttribute(name);
}
/* ------------------------------------------------------------ */
private void deploy(String filename) throws Exception
{
@ -365,6 +400,7 @@ public class ContextDeployer extends AbstractLifeCycle
xmlConfiguration.setProperties(properties);
ContextHandler context=(ContextHandler)xmlConfiguration.configure();
context.setAttributes(new AttributesMap(_contextAttributes));
return context;
}

View File

@ -16,10 +16,10 @@ package org.eclipse.jetty.deploy;
import java.util.ArrayList;
import org.eclipse.jetty.server.Handler;
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.HandlerCollection;
import org.eclipse.jetty.util.AttributesMap;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.resource.Resource;
@ -51,6 +51,7 @@ public class WebAppDeployer extends AbstractLifeCycle
private boolean _parentLoaderPriority;
private boolean _allowDuplicates;
private ArrayList _deployed;
private AttributesMap _contextAttributes = new AttributesMap();
public String[] getConfigurationClasses()
{
@ -126,6 +127,38 @@ public class WebAppDeployer extends AbstractLifeCycle
_allowDuplicates=allowDuplicates;
}
/**
* Set a contextAttribute that will be set for every Context deployed by this deployer.
* @param name
* @param value
*/
public void setAttribute (String name, Object value)
{
_contextAttributes.setAttribute(name,value);
}
/**
* Get a contextAttribute that will be set for every Context deployed by this deployer.
* @param name
* @return
*/
public Object getAttribute (String name)
{
return _contextAttributes.getAttribute(name);
}
/**
* Remove a contextAttribute that will be set for every Context deployed by this deployer.
* @param name
*/
public void removeAttribute(String name)
{
_contextAttributes.removeAttribute(name);
}
/* ------------------------------------------------------------ */
/**
* @throws Exception
@ -234,6 +267,10 @@ public class WebAppDeployer extends AbstractLifeCycle
wah.setExtractWAR(_extract);
wah.setWar(app.toString());
wah.setParentLoaderPriority(_parentLoaderPriority);
//set up any contextAttributes
wah.setAttributes(_contextAttributes);
// add it
_contexts.addHandler(wah);
_deployed.add(wah);

View File

@ -201,7 +201,6 @@ public abstract class NamingEntry
objectName.addAll(0, prefix);
objectNameString = objectName.toString();
NamingUtil.bind(ic, objectNameString, objectToBind);
System.err.println("Bound: "+objectName.toString()+" to "+objectToBind);
}
}

View File

@ -411,7 +411,7 @@ public abstract class AbstractConfiguration implements Configuration
{
bindUserTransaction(context);
WebXmlProcessor webXmlProcessor = (WebXmlProcessor)context.getAttribute(WebXmlProcessor.__web_processor);
WebXmlProcessor webXmlProcessor = (WebXmlProcessor)context.getAttribute(WebXmlProcessor.WEB_PROCESSOR);
if (webXmlProcessor == null)
throw new IllegalStateException ("No processor for web xml");

View File

@ -62,8 +62,6 @@ public class Configuration extends AbstractConfiguration
EnvEntry ee = (EnvEntry)ne;
bound = ee.isOverrideWebXml();
}
System.err.println("Checked for envEntry already bound for "+name+" and got: "+ne+", bound="+bound);
}
catch (NameNotFoundException e)
{
@ -75,7 +73,6 @@ public class Configuration extends AbstractConfiguration
//either nothing was bound or the value from web.xml should override
Context envCtx = (Context)ic.lookup("java:comp/env");
NamingUtil.bind(envCtx, name, value);
System.err.println("Bound "+name+"to "+value+" for java:comp/env");
}
}
@ -186,7 +183,8 @@ public class Configuration extends AbstractConfiguration
*/
public void parseAnnotations(WebAppContext context) throws Exception
{
//see org.eclipse.jetty.annotations.Configuration instead
//Noop unless you want to do annotation discovery.
//Use org.eclipse.jetty.annotations.Configuration instead.
}
/**
@ -220,32 +218,26 @@ public class Configuration extends AbstractConfiguration
//if we found a mapping, get out name it is mapped to in the environment
nameInEnvironment = (String)((Link)ne).getObjectToBind();
Link l = (Link)ne;
System.err.println("Link, with nameInEnvironment="+nameInEnvironment);
}
//try finding that mapped name in the webapp's environment first
System.err.println("Trying to find "+nameInEnvironment+" in webapp scope");
scope = context;
bound = NamingEntryUtil.bindToENC(scope, name, nameInEnvironment);
if (bound)
return;
System.err.println("Trying to find "+nameInEnvironment+" in server scope");
//try the server's environment
scope = context.getServer();
bound = NamingEntryUtil.bindToENC(scope, name, nameInEnvironment);
if (bound)
return;
System.err.println("Trying to find "+nameInEnvironment+" in jvm scope");
//try the jvm environment
bound = NamingEntryUtil.bindToENC(null, name, nameInEnvironment);
if (bound)
return;
System.err.println("Didn't find "+nameInEnvironment+" anywhere - looking for "+typeClass.getName()+"/default in server or jvm scope");
//There is no matching resource so try a default name.
//The default name syntax is: the [res-type]/default
//eg javax.sql.DataSource/default

View File

@ -40,9 +40,6 @@ import org.eclipse.jetty.xml.XmlConfiguration;
*/
public class EnvConfiguration implements Configuration
{
private Context compCtx;
private Context envCtx;
private URL jettyEnvXmlUrl;
@ -96,7 +93,6 @@ public class EnvConfiguration implements Configuration
//apply the jetty-env.xml file
if (jettyEnvXmlUrl != null)
{
System.err.println("Applying "+jettyEnvXmlUrl);
XmlConfiguration configuration = new XmlConfiguration(jettyEnvXmlUrl);
configuration.configure(context);
}
@ -122,8 +118,17 @@ public class EnvConfiguration implements Configuration
//get rid of any bindings for comp/env for webapp
ClassLoader oldLoader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(context.getClassLoader());
compCtx.destroySubcontext("env");
try
{
Context ic = new InitialContext();
Context compCtx = (Context)ic.lookup ("java:comp");
compCtx.destroySubcontext("env");
}
catch (NameNotFoundException e)
{
Log.warn(e);
}
//unbind any NamingEntries that were configured in this webapp's name space
try
{
@ -149,6 +154,8 @@ public class EnvConfiguration implements Configuration
throws NamingException
{
Log.debug("Binding env entries from the jvm scope");
InitialContext ic = new InitialContext();
Context envCtx = (Context)ic.lookup("java:comp/env");
Object scope = null;
List list = NamingEntryUtil.lookupNamingEntries(scope, EnvEntry.class);
Iterator itor = list.iterator();
@ -190,9 +197,7 @@ public class EnvConfiguration implements Configuration
throws NamingException
{
Context context = new InitialContext();
compCtx = (Context)context.lookup ("java:comp");
envCtx = compCtx.createSubcontext("env");
System.err.println("Created comp/env");
Context compCtx = (Context)context.lookup ("java:comp");
compCtx.createSubcontext("env");
}
}

View File

@ -110,6 +110,10 @@
<Set name="contexts"><Ref id="Contexts"/></Set>
<Set name="configurationDir"><SystemProperty name="jetty.home" default="."/>/contexts</Set>
<Set name="scanInterval">5</Set>
<Call name="setAttribute">
<Arg>org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern</Arg>
<Arg>.*/jsp-api-[^/]*\.jar$|.*/jsp-[^/]*\.jar$</Arg>
</Call>
</New>
</Arg>
</Call>
@ -136,6 +140,10 @@
<Set name="extract">true</Set>
<Set name="allowDuplicates">false</Set>
<Set name="defaultsDescriptor"><SystemProperty name="jetty.home" default="."/>/etc/webdefault.xml</Set>
<Call name="setAttribute">
<Arg>org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern</Arg>
<Arg>.*/jsp-api-[^/]*\.jar$|.*/jsp-[^/]*\.jar$</Arg>
</Call>
</New>
</Arg>
</Call>

View File

@ -41,6 +41,11 @@ public class AttributesMap implements Attributes
_map=map;
}
public AttributesMap(AttributesMap map)
{
_map=new HashMap(map._map);
}
/* ------------------------------------------------------------ */
/*
* @see org.eclipse.jetty.util.Attributes#removeAttribute(java.lang.String)

View File

@ -0,0 +1,86 @@
package org.eclipse.jetty.util;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
public abstract class PatternMatcher
{
public abstract void matched (URI uri) throws Exception;
/**
* Find jar names from the provided list matching a pattern.
*
* If the pattern is null and isNullInclusive is true, then
* all jar names will match.
*
* A pattern is a set of acceptable jar names. Each acceptable
* jar name is a regex. Each regex can be separated by either a
* "," or a "|". If you use a "|" this or's together the jar
* name patterns. This means that ordering of the matches is
* unimportant to you. If instead, you want to match particular
* jar names, and you want to match them in order, you should
* separate the regexs with "," instead.
*
* Eg "aaa-.*\\.jar|bbb-.*\\.jar"
* Will iterate over the jar names and match
* in any order.
*
* Eg "aaa-*\\.jar,bbb-.*\\.jar"
* Will iterate over the jar names, matching
* all those starting with "aaa-" first, then "bbb-".
*
* @param pattern
* @param loader
* @param isNullInclusive if true, an empty pattern means all names match, if false, none match
* @throws Exception
*/
public void match (Pattern pattern, URI[] uris, boolean isNullInclusive)
throws Exception
{
if (uris!=null)
{
String[] patterns = (pattern==null?null:pattern.pattern().split(","));
List<Pattern> subPatterns = new ArrayList<Pattern>();
for (int i=0; patterns!=null && i<patterns.length;i++)
{
subPatterns.add(Pattern.compile(patterns[i]));
}
if (subPatterns.isEmpty())
subPatterns.add(pattern);
if (subPatterns.isEmpty())
{
matchPatterns(null, uris, isNullInclusive);
}
else
{
//for each subpattern, iterate over all the urls, processing those that match
for (Pattern p : subPatterns)
{
matchPatterns(p, uris, isNullInclusive);
}
}
}
}
public void matchPatterns (Pattern pattern, URI[] uris, boolean isNullInclusive)
throws Exception
{
for (int i=0; i<uris.length;i++)
{
URI uri = uris[i];
String s = uri.toString();
if ((pattern == null && isNullInclusive)
||
(pattern!=null && pattern.matcher(s).matches()))
{
matched(uris[i]);
}
}
}
}

View File

@ -51,6 +51,7 @@ public abstract class AbstractLifeCycle implements LifeCycle
{
if (_state == __STARTED || _state == __STARTING)
return;
Log.debug("Starting {}",this);
setStarting();
doStart();
Log.debug(STARTED+" {}",this);

View File

@ -50,24 +50,6 @@
</context-param>
-->
<!-- control which jars from the container's classpath will be scanned for .tlds -->
<context-param>
<param-name>org.eclipse.jetty.server.webapp.ContainerIncludeTLDJarPattern</param-name>
<param-value>jsp-api-.*\.jar|jsp-.*\.jar</param-value>
</context-param>
<!-- control which jars from the container's classpath will be scanned for -->
<!-- annotations. If this property is empty, NO JARS from the container -->
<!-- classpath will be scanned. This can be overridden by a webapp by -->
<!-- defining this context-param in their web.xml file. Individual webapps -->
<!-- can also control which jars in their WEB-INF/lib will be scanned by -->
<!-- setting a context-param in web.xml called: -->
<!-- org.eclipse.jetty.server.webapp.WebInfIncludeAnnotationPattern -->
<context-param>
<param-name>org.eclipse.jetty.server.webapp.ContainerIncludeAnnotationJarPattern</param-name>
<param-value></param-value>
</context-param>
<!-- ==================================================================== -->
<!-- The default servlet. -->

View File

@ -35,33 +35,36 @@ public class FragmentConfiguration implements Configuration
if (!context.isConfigurationDiscovered())
return;
WebXmlProcessor processor = (WebXmlProcessor)context.getAttribute(WebXmlProcessor.__web_processor);
WebXmlProcessor processor = (WebXmlProcessor)context.getAttribute(WebXmlProcessor.WEB_PROCESSOR);
if (processor == null)
{
processor = new WebXmlProcessor (context);
context.setAttribute(WebXmlProcessor.__web_processor, processor);
context.setAttribute(WebXmlProcessor.WEB_PROCESSOR, processor);
}
//parse web-fragment.xmls
parseWebFragments(context, processor);
//TODO for jetty-8/servletspec 3 we will need to merge the parsed web fragments into the
//effective pom in this preConfigure step
}
public void configure(WebAppContext context) throws Exception
{
{
if (!context.isConfigurationDiscovered())
return;
//TODO for jetty-8/servletspec3 the fragments will not be separately processed here, but
//will be done by webXmlConfiguration when it processes the effective merged web.xml
WebXmlProcessor processor = (WebXmlProcessor)context.getAttribute(WebXmlProcessor.__web_processor);
WebXmlProcessor processor = (WebXmlProcessor)context.getAttribute(WebXmlProcessor.WEB_PROCESSOR);
if (processor == null)
{
processor = new WebXmlProcessor (context);
context.setAttribute(WebXmlProcessor.__web_processor, processor);
context.setAttribute(WebXmlProcessor.WEB_PROCESSOR, processor);
}
processor.processFragments();
processor.processFragments();
}
public void deconfigure(WebAppContext context) throws Exception
@ -83,11 +86,7 @@ public class FragmentConfiguration implements Configuration
*/
public void parseWebFragments (final WebAppContext context, final WebXmlProcessor processor) throws Exception
{
// Check to see if a specific search pattern has been set.
String tmp = (String) context.getInitParameter("org.eclipse.jetty.webapp.WebXmlFragmentPattern");
Pattern webFragPattern = (tmp == null ? null : Pattern.compile(tmp));
List<Resource> frags = (List<Resource>)context.getAttribute(MetaInfConfiguration.METAINF_FRAGMENTS);
List<Resource> frags = (List<Resource>)context.getAttribute(FRAGMENT_RESOURCES);
if (frags!=null)
{
for (Resource frag : frags)

View File

@ -15,6 +15,7 @@
package org.eclipse.jetty.webapp;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
@ -38,10 +39,10 @@ import org.eclipse.jetty.util.resource.Resource;
* method to handle entries in jar files whose names match the supplied
* pattern.
*/
public abstract class JarScanner
public abstract class JarScanner extends org.eclipse.jetty.util.PatternMatcher
{
public abstract void processEntry (URL jarUrl, JarEntry entry);
public abstract void processEntry (URI jarUri, JarEntry entry);
/**
* Find jar names from the provided list matching a pattern.
@ -70,32 +71,10 @@ public abstract class JarScanner
* @param isNullInclusive if true, an empty pattern means all names match, if false, none match
* @throws Exception
*/
public void scan (Pattern pattern, URL[] urls, boolean isNullInclusive)
public void scan (Pattern pattern, URI[] uris, boolean isNullInclusive)
throws Exception
{
if (urls!=null)
{
String[] patterns = (pattern==null?null:pattern.pattern().split(","));
List<Pattern> subPatterns = new ArrayList<Pattern>();
for (int i=0; patterns!=null && i<patterns.length;i++)
subPatterns.add(Pattern.compile(patterns[i]));
if (subPatterns.isEmpty())
subPatterns.add(pattern);
if (subPatterns.isEmpty())
{
processJars(null, urls, isNullInclusive);
}
else
{
//for each subpattern, iterate over all the urls, processing those that match
for (Pattern p : subPatterns)
{
processJars(p, urls, isNullInclusive);
}
}
}
super.match(pattern, uris, isNullInclusive);
}
/**
@ -133,35 +112,18 @@ public abstract class JarScanner
public void scan (Pattern pattern, ClassLoader loader, boolean isNullInclusive, boolean visitParent)
throws Exception
{
String[] patterns = (pattern==null?null:pattern.pattern().split(","));
List<Pattern> subPatterns = new ArrayList<Pattern>();
for (int i=0; patterns!=null && i<patterns.length;i++)
subPatterns.add(Pattern.compile(patterns[i]));
if (subPatterns.isEmpty())
subPatterns.add(pattern);
while (loader!=null)
{
if (loader instanceof URLClassLoader)
{
URL[] urls = ((URLClassLoader)loader).getURLs();
if (urls!=null)
if (urls != null)
{
if (subPatterns.isEmpty())
{
processJars(null, urls, isNullInclusive);
}
else
{
//for each subpattern, iterate over all the urls, processing those that match
for (Pattern p : subPatterns)
{
processJars(p, urls, isNullInclusive);
}
}
URI[] uris = new URI[urls.length];
int i=0;
for (URL u : urls)
uris[i++] = u.toURI();
scan (pattern, uris, isNullInclusive);
}
}
if (visitParent)
@ -172,50 +134,31 @@ public abstract class JarScanner
}
public void processJars (Pattern pattern, URL[] urls, boolean isNullInclusive)
public void matched (URI uri)
throws Exception
{
for (int i=0; i<urls.length;i++)
Log.debug("Search of {}",uri);
if (uri.toString().toLowerCase().endsWith(".jar"))
{
if (urls[i].toString().toLowerCase().endsWith(".jar"))
{
String jar = urls[i].toString();
int slash=jar.lastIndexOf('/');
jar=jar.substring(slash+1);
if ((pattern == null && isNullInclusive)
||
(pattern!=null && pattern.matcher(jar).matches()))
InputStream in = Resource.newResource(uri).getInputStream();
if (in==null)
return;
JarInputStream jar_in = new JarInputStream(in);
try
{
JarEntry entry = jar_in.getNextJarEntry();
while (entry!=null)
{
processJar(urls[i]);
processEntry(uri, entry);
entry = jar_in.getNextJarEntry();
}
}
}
}
public void processJar (URL url)
throws Exception
{
Log.debug("Search of {}",url);
InputStream in = Resource.newResource(url).getInputStream();
if (in==null)
return;
JarInputStream jar_in = new JarInputStream(in);
try
{
JarEntry entry = jar_in.getNextJarEntry();
while (entry!=null)
finally
{
processEntry(url, entry);
entry = jar_in.getNextJarEntry();
}
jar_in.close();
}
}
finally
{
jar_in.close();
}
}
}

View File

@ -14,10 +14,12 @@
package org.eclipse.jetty.webapp;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.regex.Pattern;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.resource.Resource;
@ -37,19 +39,19 @@ public class MetaInfConfiguration implements Configuration
public static final String METAINF_TLDS = TagLibConfiguration.TLD_RESOURCES;
public static final String METAINF_FRAGMENTS = FragmentConfiguration.FRAGMENT_RESOURCES;
public static final String METAINF_RESOURCES = WebInfConfiguration.RESOURCE_URLS;
public static final String JAR_RESOURCES = WebInfConfiguration.JAR_RESOURCES;
public void preConfigure(final WebAppContext context) throws Exception
{
//Find all jars in WEB-INF
List<URL> urls = findJars(context);
JarScanner fragScanner = new JarScanner()
JarScanner scanner = new JarScanner()
{
public void processEntry(URL jarUrl, JarEntry entry)
public void processEntry(URI jarUri, JarEntry entry)
{
try
{
MetaInfConfiguration.this.processEntry(context,jarUrl,entry);
MetaInfConfiguration.this.processEntry(context,jarUri,entry);
}
catch (Exception e)
{
@ -57,26 +59,36 @@ public class MetaInfConfiguration implements Configuration
}
}
};
fragScanner.scan(null, urls.toArray(new URL[urls.size()]), true);
List<Resource> jarResources = (List<Resource>)context.getAttribute(JAR_RESOURCES);
//Scan jars for META-INF information
if (jarResources != null)
{
URI[] uris = new URI[jarResources.size()];
int i=0;
for (Resource r : jarResources)
{
uris[i++] = r.getURI();
}
scanner.scan(null, uris, true);
}
}
public void configure(WebAppContext context) throws Exception
{
// TODO Auto-generated method stub
}
public void deconfigure(WebAppContext context) throws Exception
{
// TODO Auto-generated method stub
}
public void postConfigure(WebAppContext context) throws Exception
{
// TODO Auto-generated method stub
}
public void addResource (WebAppContext context, String attribute, Resource jar)
@ -92,74 +104,35 @@ public class MetaInfConfiguration implements Configuration
}
protected void processEntry(WebAppContext context, URL jarUrl, JarEntry entry)
protected void processEntry(WebAppContext context, URI jarUri, JarEntry entry)
{
String name = entry.getName();
if (!name.startsWith("META-INF/"))
return;
try
{
if (name.equals("META-INF/web-fragment.xml") && context.isConfigurationDiscovered())
{
addResource(context,METAINF_FRAGMENTS,Resource.newResource(jarUrl));
addResource(context,METAINF_FRAGMENTS,Resource.newResource(jarUri));
}
else if (name.equals("META-INF/resources/") && context.isConfigurationDiscovered())
{
addResource(context,METAINF_RESOURCES,Resource.newResource("jar:"+jarUrl+"!/META-INF/resources"));
addResource(context,METAINF_RESOURCES,Resource.newResource("jar:"+jarUri+"!/META-INF/resources"));
}
else
{
String lcname = name.toLowerCase();
if (lcname.endsWith(".tld"))
{
addResource(context,METAINF_TLDS,Resource.newResource("jar:"+jarUrl+"!/"+name));
addResource(context,METAINF_TLDS,Resource.newResource("jar:"+jarUri+"!/"+name));
}
}
}
catch(Exception e)
{
context.getServletContext().log(jarUrl+"!/"+name,e);
context.getServletContext().log(jarUri+"!/"+name,e);
}
}
/**
* Look for jars in WEB-INF/lib
* @param context
* @return
* @throws Exception
*/
protected List<URL> findJars (WebAppContext context)
throws Exception
{
List<URL> urls = new ArrayList<URL>();
Resource web_inf = context.getWebInf();
Resource web_inf_lib = web_inf.addPath("/lib");
if (web_inf_lib.exists() && web_inf_lib.isDirectory())
{
String[] files=web_inf_lib.list();
for (int f=0;files!=null && f<files.length;f++)
{
try
{
Resource file = web_inf_lib.addPath(files[f]);
String fnlc = file.getName().toLowerCase();
int dot = fnlc.lastIndexOf('.');
String extension = (dot < 0 ? null : fnlc.substring(dot));
if (extension != null && (extension.equals(".jar") || extension.equals(".zip")))
{
urls.add(file.getURL());
}
}
catch (Exception ex)
{
Log.warn(Log.EXCEPTION,ex);
}
}
}
return urls;
}
}

View File

@ -35,12 +35,12 @@ import org.eclipse.jetty.xml.XmlParser;
/** TagLibConfiguration.
*
* The class searches for TLD descriptors found in web.xml, in WEB-INF/*.tld files of the web app
* or *.tld files withing jars found in WEB-INF/lib of the webapp. Any listeners defined in these
* or *.tld files within jars found in WEB-INF/lib of the webapp. Any listeners defined in these
* tld's are added to the context.
*
* &lt;bile&gt;This is total rubbish special case for JSPs! If there was a general use-case for web app
* frameworks to register listeners directly, then a generic mechanism could have been added to the servlet
* spec. Instead some special purpose JSP support is required that breaks all sorts of encapsualtion rules as
* spec. Instead some special purpose JSP support is required that breaks all sorts of encapsulation rules as
* the servlet container must go searching for and then parsing the descriptors for one particular framework.
* It only appears to be used by JSF, which is being developed by the same developer who implemented this
* feature in the first place!
@ -242,26 +242,8 @@ public class TagLibConfiguration implements Configuration
}
}
// Look for tlds in any jars
//Use an opt-in style:
//
//org.eclipse.jetty.server.server.webapp.WebInfIncludeTLDJarPattern and
//org.eclipse.jetty.server.server.webapp.ContainerIncludeTLDJarPattern
//
//When examining jars in WEB-INF/lib:
// if WebInfIncludeTLDJarPattern is null
// examine ALL for tlds
// else
// examine only files matching pattern
//
//When examining jars in parent loaders:
// If IncludeTLDJarPattern is null
// examine none
// else
// examine only files matching pattern
//
// Add in tlds found in META-INF of jars
Collection<Resource> tld_resources=(Collection<Resource>)context.getAttribute(TLD_RESOURCES);
if (tld_resources!=null)
tlds.addAll(tld_resources);

View File

@ -2,11 +2,16 @@ package org.eclipse.jetty.webapp;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.PatternMatcher;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.resource.JarResource;
@ -16,6 +21,9 @@ import org.eclipse.jetty.util.resource.ResourceCollection;
public class WebInfConfiguration implements Configuration
{
public static final String TEMPDIR_CREATED = "org.eclipse.jetty.tmpdirCreated";
public static final String JAR_RESOURCES = "org.eclipse.jetty.jarList";
public static final String CONTAINER_JAR_PATTERN = "org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern";
public static final String WEBINF_JAR_PATTERN = "org.eclipse.jetty.server.webapp.WebInfIncludeJarPattern";
/**
* If set, to a list of URLs, these resources are added to the context
@ -23,7 +31,10 @@ public class WebInfConfiguration implements Configuration
*/
public static final String RESOURCE_URLS = "org.eclipse.jetty.resources";
public void preConfigure(WebAppContext context) throws Exception
public void preConfigure(final WebAppContext context) throws Exception
{
//Make a temp directory for the webapp if one is not already set
resolveTempDirectory(context);
@ -34,6 +45,59 @@ public class WebInfConfiguration implements Configuration
File work = findWorkDirectory(context);
if (work != null)
makeTempDirectory(work, context, false);
//Apply an initial ordering to the jars which governs which will be scanned for META-INF
//info and annotations. The ordering is based on inclusion patterns.
String tmp = (String)context.getAttribute(WEBINF_JAR_PATTERN);
Pattern webInfPattern = (tmp==null?null:Pattern.compile(tmp));
tmp = (String)context.getAttribute(CONTAINER_JAR_PATTERN);
Pattern containerPattern = (tmp==null?null:Pattern.compile(tmp));
final ArrayList jarResources = new ArrayList<Resource>();
context.setAttribute(JAR_RESOURCES, jarResources);
PatternMatcher jarNameMatcher = new PatternMatcher ()
{
public void matched(URI uri) throws Exception
{
jarResources.add(Resource.newResource(uri));
}
};
//Apply ordering to container jars
ClassLoader loader = context.getClassLoader();
while (loader != null && (loader instanceof URLClassLoader))
{
URL[] urls = ((URLClassLoader)loader).getURLs();
if (urls != null)
{
URI[] containerUris = new URI[urls.length];
int i=0;
for (URL u : urls)
{
containerUris[i++] = u.toURI();
}
jarNameMatcher.match(containerPattern, containerUris, false);
}
loader = loader.getParent();
}
//Apply ordering to WEB-INF/lib jars
//Find all jars in WEB-INF
List<Resource> jars = findJars(context);
//Convert to uris for matching
URI[] uris = null;
if (jars != null)
{
uris = new URI[jars.size()];
int i=0;
for (Resource r: jars)
{
uris[i++] = r.getURI();
}
}
jarNameMatcher.match(webInfPattern, uris, true); //null is inclusive, no pattern == all jars match
}
@ -501,4 +565,43 @@ public class WebInfConfiguration implements Configuration
return canonicalName.toString();
}
/**
* Look for jars in WEB-INF/lib
* @param context
* @return
* @throws Exception
*/
protected List<Resource> findJars (WebAppContext context)
throws Exception
{
List<Resource> jarResources = new ArrayList<Resource>();
Resource web_inf = context.getWebInf();
Resource web_inf_lib = web_inf.addPath("/lib");
if (web_inf_lib.exists() && web_inf_lib.isDirectory())
{
String[] files=web_inf_lib.list();
for (int f=0;files!=null && f<files.length;f++)
{
try
{
Resource file = web_inf_lib.addPath(files[f]);
String fnlc = file.getName().toLowerCase();
int dot = fnlc.lastIndexOf('.');
String extension = (dot < 0 ? null : fnlc.substring(dot));
if (extension != null && (extension.equals(".jar") || extension.equals(".zip")))
{
jarResources.add(file);
}
}
catch (Exception ex)
{
Log.warn(Log.EXCEPTION,ex);
}
}
}
return jarResources;
}
}

View File

@ -57,11 +57,11 @@ public class WebXmlConfiguration implements Configuration
}
//Get or create a processor to handle webdefaults, web.xml and the fragments
WebXmlProcessor processor = (WebXmlProcessor)context.getAttribute(WebXmlProcessor.__web_processor);
WebXmlProcessor processor = (WebXmlProcessor)context.getAttribute(WebXmlProcessor.WEB_PROCESSOR);
if (processor == null)
{
processor = new WebXmlProcessor (context);
context.setAttribute(WebXmlProcessor.__web_processor, processor);
context.setAttribute(WebXmlProcessor.WEB_PROCESSOR, processor);
}
//handle webdefault.xml
@ -74,8 +74,6 @@ public class WebXmlConfiguration implements Configuration
processor.parseDefaults (dftResource.getURL());
processor.processDefaults();
}
}
/* ------------------------------------------------------------------------------- */
@ -93,11 +91,11 @@ public class WebXmlConfiguration implements Configuration
return;
}
WebXmlProcessor processor = (WebXmlProcessor)context.getAttribute(WebXmlProcessor.__web_processor);
WebXmlProcessor processor = (WebXmlProcessor)context.getAttribute(WebXmlProcessor.WEB_PROCESSOR);
if (processor == null)
{
processor = new WebXmlProcessor (context);
context.setAttribute(WebXmlProcessor.__web_processor, processor);
context.setAttribute(WebXmlProcessor.WEB_PROCESSOR, processor);
}
//process web.xml
@ -118,7 +116,6 @@ public class WebXmlConfiguration implements Configuration
processor.parseOverride(orideResource.getURL());
processor.processOverride();
}
}

View File

@ -55,7 +55,7 @@ import org.eclipse.jetty.xml.XmlParser;
*/
public class WebXmlProcessor
{
public static final String __web_processor = "org.eclipse.jetty.webProcessor";
public static final String WEB_PROCESSOR = "org.eclipse.jetty.webProcessor";
protected WebAppContext _context;
protected XmlParser _xmlParser;
@ -297,6 +297,7 @@ public class WebXmlProcessor
public void process (XmlParser.Node config)
throws Exception
{
//Get the current objects from the context
_servletHandler = _context.getServletHandler();
_securityHandler = (SecurityHandler)_context.getSecurityHandler();
@ -315,8 +316,8 @@ public class WebXmlProcessor
_roles.addAll(((ConstraintAware) _securityHandler).getRoles());
}
}
_errorPages = _context.getErrorHandler() instanceof ErrorPageErrorHandler ? ((ErrorPageErrorHandler)_context.getErrorHandler()).getErrorPages() : null;
_errorPages = _context.getErrorHandler() instanceof ErrorPageErrorHandler ? ((ErrorPageErrorHandler)_context.getErrorHandler()).getErrorPages() : null;
Iterator iter = config.iterator();
XmlParser.Node node = null;
while (iter.hasNext())