diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AbstractConfiguration.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AbstractConfiguration.java index 1858611ec44..f245642b9d5 100644 --- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AbstractConfiguration.java +++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AbstractConfiguration.java @@ -17,30 +17,36 @@ import java.net.URI; import java.util.ArrayList; import java.util.List; -import org.eclipse.jetty.util.Loader; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.webapp.Configuration; +import org.eclipse.jetty.webapp.Descriptor; +import org.eclipse.jetty.webapp.DiscoveredAnnotation; +import org.eclipse.jetty.webapp.FragmentDescriptor; +import org.eclipse.jetty.webapp.MetaData; import org.eclipse.jetty.webapp.WebAppContext; import org.eclipse.jetty.webapp.WebInfConfiguration; -import org.eclipse.jetty.webapp.WebXmlProcessor; -import org.eclipse.jetty.webapp.WebXmlProcessor.Descriptor; +import org.eclipse.jetty.webapp.Descriptor.MetaDataComplete; public abstract class AbstractConfiguration implements Configuration { public static final String CONTAINER_JAR_RESOURCES = WebInfConfiguration.CONTAINER_JAR_RESOURCES; public static final String WEB_INF_JAR_RESOURCES = WebInfConfiguration.WEB_INF_JAR_RESOURCES; - public static final String WEBXML_VERSION = WebXmlProcessor.WEBXML_VERSION; - public static final String METADATA_COMPLETE = WebXmlProcessor.METADATA_COMPLETE; - public static final String WEBXML_CLASSNAMES = WebXmlProcessor.WEBXML_CLASSNAMES; - + public static final String WEB_INF_ORDERED_JAR_RESOURCES = WebInfConfiguration.WEB_INF_ORDERED_JAR_RESOURCES; + public static final String METADATA_COMPLETE = MetaData.METADATA_COMPLETE; + public static final String WEBXML_CLASSNAMES = MetaData.WEBXML_CLASSNAMES; + public static final String DISCOVERED_ANNOTATIONS = "org.eclipse.jetty.discoveredAnnotations"; + public void parseContainerPath (final WebAppContext context, final AnnotationParser parser) throws Exception { //if no pattern for the container path is defined, then by default scan NOTHING Log.debug("Scanning container jars"); - + List discoveredAnnotations = new ArrayList(); + context.setAttribute(DISCOVERED_ANNOTATIONS, discoveredAnnotations); + + //Convert from Resource to URI ArrayList containerUris = new ArrayList(); List jarResources = (List)context.getAttribute(CONTAINER_JAR_RESOURCES); @@ -68,60 +74,71 @@ public abstract class AbstractConfiguration implements Configuration return false; } }); + MetaData metaData = (MetaData)context.getAttribute(MetaData.METADATA); + if (metaData == null) + throw new IllegalStateException ("No metadata"); + + metaData.addDiscoveredAnnotations((List)context.getAttribute(DISCOVERED_ANNOTATIONS)); + context.removeAttribute(DISCOVERED_ANNOTATIONS); } public void parseWebInfLib (final WebAppContext context, final AnnotationParser parser) throws Exception { - WebXmlProcessor webXmlProcessor = (WebXmlProcessor)context.getAttribute(WebXmlProcessor.WEB_PROCESSOR); - if (webXmlProcessor == null) - throw new IllegalStateException ("No processor for web xml"); + MetaData metaData = (MetaData)context.getAttribute(MetaData.METADATA); + if (metaData == null) + throw new IllegalStateException ("No metadata"); - List frags = webXmlProcessor.getFragments(); + List frags = metaData.getFragments(); - //Get the web-inf lib jars who have a web-fragment.xml that is not metadata-complete (or is not set) + //email from Rajiv Mordani jsrs 315 7 April 2010 + //jars that do not have a web-fragment.xml are still considered fragments + //they have to participate in the ordering ArrayList webInfUris = new ArrayList(); - List jarResources = (List)context.getAttribute(WEB_INF_JAR_RESOURCES); - for (Resource r : jarResources) + List jars = (List)context.getAttribute(WEB_INF_ORDERED_JAR_RESOURCES); + + //No ordering just use the jars in any order + if (jars == null || jars.isEmpty()) + jars = (List)context.getAttribute(WEB_INF_JAR_RESOURCES); + + List discoveredAnnotations = new ArrayList(); + context.setAttribute(DISCOVERED_ANNOTATIONS, discoveredAnnotations); + + for (Resource r : jars) { + discoveredAnnotations.clear(); //start fresh for each jar URI uri = r.getURI(); - Descriptor d = null; - for (Descriptor frag: frags) + FragmentDescriptor f = getFragmentFromJar(r, frags); + + //if a jar has no web-fragment.xml we scan it (because it is not exluded by the ordering) + //or if it has a fragment we scan it if it is not metadata complete + if (f == null || !isMetaDataComplete(f)) { - Resource fragResource = frag.getResource(); //eg jar:file:///a/b/c/foo.jar!/META-INF/web-fragment.xml - if (Resource.isContainedIn(fragResource,r)) - { - d = frag; - break; - } + parser.parse(uri, + 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; + } + }); + + metaData.addDiscoveredAnnotations(r, discoveredAnnotations); } - - //if there was no web-fragment.xml for the jar, or there was one - //and its metadata is NOT complete, we want to exame it for annotations - if (d == null || (d != null && !d.isMetaDataComplete())) - webInfUris.add(uri); } - - parser.parse(webInfUris.toArray(new URI[webInfUris.size()]), - 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; - } - }); + context.removeAttribute(DISCOVERED_ANNOTATIONS); } public void parseWebInfClasses (final WebAppContext context, final AnnotationParser parser) @@ -133,6 +150,13 @@ public abstract class AbstractConfiguration implements Configuration Resource classesDir = context.getWebInf().addPath("classes/"); if (classesDir.exists()) { + MetaData metaData = (MetaData)context.getAttribute(MetaData.METADATA); + if (metaData == null) + throw new IllegalStateException ("No metadata"); + + List discoveredAnnotations = new ArrayList(); + context.setAttribute(DISCOVERED_ANNOTATIONS, discoveredAnnotations); + parser.parse(classesDir, new ClassNameResolver() { @@ -151,37 +175,36 @@ public abstract class AbstractConfiguration implements Configuration return true; } }); + + //TODO - where to set the annotations discovered from WEB-INF/classes? + metaData.addDiscoveredAnnotations (discoveredAnnotations); + context.removeAttribute(DISCOVERED_ANNOTATIONS); } } } - public void parse25Classes (final WebAppContext context, final AnnotationParser parser) + + + public FragmentDescriptor getFragmentFromJar (Resource jar, List frags) throws Exception { - //only parse servlets, filters and listeners from web.xml - if (Log.isDebugEnabled()) Log.debug("Scanning only classes from web.xml"); - ArrayList classNames = (ArrayList)context.getAttribute(WEBXML_CLASSNAMES); - for (String s : classNames) + //check if the jar has a web-fragment.xml + FragmentDescriptor d = null; + for (FragmentDescriptor frag: frags) { - Class clazz = Loader.loadClass(null, s); - parser.parse(clazz, new ClassNameResolver() + Resource fragResource = frag.getResource(); //eg jar:file:///a/b/c/foo.jar!/META-INF/web-fragment.xml + if (Resource.isContainedIn(fragResource,jar)) { - 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; - } - }, true); + d = frag; + break; + } } - + return d; + } + + + public boolean isMetaDataComplete (Descriptor d) + { + return (d!=null && d.getMetaDataComplete() == MetaDataComplete.True); } } diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationConfiguration.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationConfiguration.java index a26915b5c66..a742d87e2ae 100644 --- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationConfiguration.java +++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationConfiguration.java @@ -13,17 +13,18 @@ package org.eclipse.jetty.annotations; -import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.webapp.WebAppContext; /** - * Configuration + * Configuration for Annotations * * */ public class AnnotationConfiguration extends AbstractConfiguration { public static final String CLASS_INHERITANCE_MAP = "org.eclipse.jetty.classInheritanceMap"; + + public void preConfigure(final WebAppContext context) throws Exception { @@ -31,53 +32,9 @@ public class AnnotationConfiguration extends AbstractConfiguration public void configure(WebAppContext context) throws Exception - { - Boolean b = (Boolean)context.getAttribute(METADATA_COMPLETE); - boolean metadataComplete = (b != null && b.booleanValue()); - Integer i = (Integer)context.getAttribute(WEBXML_VERSION); - int webxmlVersion = (i == null? 0 : i.intValue()); - - if (metadataComplete) - { - //Never scan any jars or classes for annotations if metadata is complete - if (Log.isDebugEnabled()) Log.debug("Metadata-complete==true, not processing annotations for context "+context); - return; - } - else - { - //Only scan jars and classes if metadata is not complete and the web app is version 3.0, or - //a 2.5 version webapp that has specifically asked to discover annotations - if (Log.isDebugEnabled()) Log.debug("parsing annotations"); - - AnnotationParser parser = new AnnotationParser(); - - parser.registerAnnotationHandler("javax.annotation.Resource", new ResourceAnnotationHandler (context)); - parser.registerAnnotationHandler("javax.annotation.Resources", new ResourcesAnnotationHandler (context)); - parser.registerAnnotationHandler("javax.annotation.PostConstruct", new PostConstructAnnotationHandler(context)); - parser.registerAnnotationHandler("javax.annotation.PreDestroy", new PreDestroyAnnotationHandler(context)); - parser.registerAnnotationHandler("javax.annotation.security.RunAs", new RunAsAnnotationHandler(context)); - parser.registerAnnotationHandler("javax.annotation.security.DeclareRoles", new DeclareRolesAnnotationHandler(context)); - - ClassInheritanceHandler classHandler = new ClassInheritanceHandler(); - parser.registerClassHandler(classHandler); - - - if (webxmlVersion >= 30 || context.isConfigurationDiscovered()) - { - if (Log.isDebugEnabled()) Log.debug("Scanning all classes for annotations: webxmlVersion="+webxmlVersion+" configurationDiscovered="+context.isConfigurationDiscovered()); - parseContainerPath(context, parser); - parseWebInfLib (context, parser); - parseWebInfClasses(context, parser); - } - else - { - if (Log.isDebugEnabled()) Log.debug("Scanning only classes in web.xml for annotations"); - parse25Classes(context, parser); - } - - //save the type inheritance map created by the parser for later reference - context.setAttribute(CLASS_INHERITANCE_MAP, classHandler.getMap()); - } + { + WebAppDecoratorWrapper wrapper = new WebAppDecoratorWrapper(context, context.getDecorator()); + context.setDecorator(wrapper); } @@ -92,7 +49,5 @@ public class AnnotationConfiguration extends AbstractConfiguration public void postConfigure(WebAppContext context) throws Exception { - context.setAttribute(CLASS_INHERITANCE_MAP, null); } - } diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationIntrospector.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationIntrospector.java new file mode 100644 index 00000000000..0374ce1eb8e --- /dev/null +++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationIntrospector.java @@ -0,0 +1,102 @@ +// ======================================================================== +// Copyright (c) 2006-2009 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// 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.annotations; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jetty.util.log.Log; + +/** + * AnnotationIntrospector + * + * + */ +public class AnnotationIntrospector +{ + protected List _handlers = new ArrayList(); + + + /** + * IntrospectableAnnotationHandler + * + * Interface for all handlers that wish to introspect a class to find a particular annotation + */ + public interface IntrospectableAnnotationHandler + { + public void handle(Class clazz); + } + + + + /** + * AbstractIntrospectableAnnotationHandler + * + * Base class for handlers that introspect a class to find a particular annotation. + * A handler can optionally introspect the parent hierarchy of a class. + */ + public static abstract class AbstractIntrospectableAnnotationHandler implements IntrospectableAnnotationHandler + { + private boolean _introspectAncestors; + + public abstract void doHandle(Class clazz); + + + public AbstractIntrospectableAnnotationHandler(boolean introspectAncestors) + { + _introspectAncestors = introspectAncestors; + } + + public void handle(Class clazz) + { + Class c = clazz; + + //process the whole inheritance hierarchy for the class + while (c!=null && (!c.equals(Object.class))) + { + doHandle(clazz); + if (!_introspectAncestors) + break; + + c = c.getSuperclass(); + } + } + } + + public void registerHandler (IntrospectableAnnotationHandler handler) + { + _handlers.add(handler); + } + + public void introspect (Class clazz) + { + if (_handlers == null) + return; + if (clazz == null) + return; + + for (IntrospectableAnnotationHandler handler:_handlers) + { + try + { + handler.handle(clazz); + } + catch (Exception e) + { + Log.warn(e); + } + } + + } +} diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationParser.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationParser.java index 5c1338c66f8..85b0d039cf1 100644 --- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationParser.java +++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationParser.java @@ -43,7 +43,7 @@ import org.objectweb.asm.commons.EmptyVisitor; public class AnnotationParser { protected List _parsedClassNames = new ArrayList(); - protected Map _annotationHandlers = new HashMap(); + protected Map> _annotationHandlers = new HashMap>(); protected List _classHandlers = new ArrayList(); protected List _methodHandlers = new ArrayList(); protected List _fieldHandlers = new ArrayList(); @@ -157,7 +157,7 @@ public class AnnotationParser - public interface AnnotationHandler + public interface DiscoverableAnnotationHandler { public void handleClass (String className, int version, int access, String signature, String superName, String[] interfaces, @@ -310,12 +310,15 @@ public class AnnotationParser public void visitEnd() { super.visitEnd(); - + //call all AnnotationHandlers with classname, annotation name + values - AnnotationHandler handler = AnnotationParser.this._annotationHandlers.get(_annotationName); - if (handler != null) + List handlers = AnnotationParser.this._annotationHandlers.get(_annotationName); + if (handlers != null) { - handler.handleClass(_className, _version, _access, _signature, _superName, _interfaces, _annotationName, _annotationValues); + for (DiscoverableAnnotationHandler h:handlers) + { + h.handleClass(_className, _version, _access, _signature, _superName, _interfaces, _annotationName, _annotationValues); + } } } }; @@ -340,10 +343,13 @@ public class AnnotationParser { super.visitEnd(); //call all AnnotationHandlers with classname, method, annotation name + values - AnnotationHandler handler = AnnotationParser.this._annotationHandlers.get(_annotationName); - if (handler != null) + List handlers = AnnotationParser.this._annotationHandlers.get(_annotationName); + if (handlers != null) { - handler.handleMethod(_className, name, access, methodDesc, signature, exceptions, _annotationName, _annotationValues); + for (DiscoverableAnnotationHandler h:handlers) + { + h.handleMethod(_className, name, access, methodDesc, signature, exceptions, _annotationName, _annotationValues); + } } } }; @@ -369,10 +375,13 @@ public class AnnotationParser public void visitEnd() { super.visitEnd(); - AnnotationHandler handler = AnnotationParser.this._annotationHandlers.get(_annotationName); - if (handler != null) + List handlers = AnnotationParser.this._annotationHandlers.get(_annotationName); + if (handlers != null) { - handler.handleField(_className, fieldName, access, fieldType, signature, value, _annotationName, _annotationValues); + for (DiscoverableAnnotationHandler h:handlers) + { + h.handleField(_className, fieldName, access, fieldType, signature, value, _annotationName, _annotationValues); + } } } }; @@ -383,9 +392,22 @@ public class AnnotationParser } - public void registerAnnotationHandler (String annotationName, AnnotationHandler handler) + /** + * Register a handler that will be called back when the named annotation is + * encountered on a class. + * + * @param annotationName + * @param handler + */ + public void registerAnnotationHandler (String annotationName, DiscoverableAnnotationHandler handler) { - _annotationHandlers.put(annotationName, handler); + List handlers = _annotationHandlers.get(annotationName); + if (handlers == null) + { + handlers = new ArrayList(); + _annotationHandlers.put(annotationName, handlers); + } + handlers.add(handler); } public void registerClassHandler (ClassHandler handler) @@ -587,6 +609,14 @@ public class AnnotationParser scanner.scan(null, uris, true); } + public void parse (URI uri, final ClassNameResolver resolver) + throws Exception + { + if (uri == null) + return; + URI[] uris = {uri}; + parse(uris, resolver); + } private void scanClass (InputStream is) throws IOException diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/DeclareRolesAnnotationHandler.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/DeclareRolesAnnotationHandler.java index 50c5bdcd2f8..e224855d694 100644 --- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/DeclareRolesAnnotationHandler.java +++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/DeclareRolesAnnotationHandler.java @@ -1,5 +1,5 @@ // ======================================================================== -// Copyright (c) 2010 Mort Bay Consulting Pty. Ltd. +// Copyright (c) 2006-2010 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 @@ -11,79 +11,58 @@ // You may elect to redistribute this code under either of these licenses. // ======================================================================== - package org.eclipse.jetty.annotations; +import java.util.Arrays; import java.util.HashSet; -import java.util.List; import java.util.Set; +import javax.servlet.Servlet; -import org.eclipse.jetty.annotations.AnnotationParser.AnnotationHandler; -import org.eclipse.jetty.annotations.AnnotationParser.ListValue; -import org.eclipse.jetty.annotations.AnnotationParser.SimpleValue; -import org.eclipse.jetty.annotations.AnnotationParser.Value; +import javax.annotation.security.DeclareRoles; +import org.eclipse.jetty.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler; import org.eclipse.jetty.security.ConstraintSecurityHandler; -import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.webapp.WebAppContext; -public class DeclareRolesAnnotationHandler implements AnnotationHandler +/** + * DeclaresRolesAnnotationHandler + * + * + */ +public class DeclareRolesAnnotationHandler extends AbstractIntrospectableAnnotationHandler { - protected WebAppContext _wac; + + protected WebAppContext _context; - public DeclareRolesAnnotationHandler (WebAppContext wac) + /** + * @param introspectAncestors + */ + public DeclareRolesAnnotationHandler(WebAppContext context) { - _wac = wac; + super(false); + _context = context; } + /** - * A DeclareRoles annotation is equivalent to a in web.xml. - * - * @see org.eclipse.jetty.annotations.AnnotationParser.AnnotationHandler#handleClass(java.lang.String, int, int, java.lang.String, java.lang.String, java.lang.String[], java.lang.String, java.util.List) + * @see org.eclipse.jetty.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler#doHandle(java.lang.Class) */ - public void handleClass(String className, int version, int access, String signature, String superName, String[] interfaces, String annotation, - List values) + public void doHandle(Class clazz) { - //Add the role names to the list of roles for the webapp - Set roles = new HashSet(); + if (!Servlet.class.isAssignableFrom(clazz)) + return; //only applicable on javax.servlet.Servlet derivatives - try - { - Set existing = ((ConstraintSecurityHandler)_wac.getSecurityHandler()).getRoles(); - roles.addAll(existing); - - if (values != null && values.size() == 1) - { - Value v = values.get(0); - if (v instanceof SimpleValue) - { - roles.add((String)((SimpleValue)v).getValue()); - } - else if (v instanceof ListValue) - { - for (Value vv:((ListValue)v).getList()) - { - roles.add((String)((SimpleValue)vv).getValue()); - } - } - } - } - catch (Exception e) - { - Log.warn(e); - } - } + DeclareRoles declareRoles = (DeclareRoles) clazz.getAnnotation(DeclareRoles.class); + if (declareRoles == null) + return; + + String[] roles = declareRoles.value(); - public void handleField(String className, String fieldName, int access, String fieldType, String signature, Object value, String annotation, - List values) - { - Log.warn ("@DeclareRoles annotation not applicable for field: "+className+"."+fieldName); - } - - public void handleMethod(String className, String methodName, int access, String desc, String signature, String[] exceptions, String annotation, - List values) - { - Log.warn ("@DeclareRoles annotation not applicable for method: "+className+"."+methodName); + if (roles != null && roles.length > 0) + { + for (String r:roles) + ((ConstraintSecurityHandler)_context.getSecurityHandler()).addRole(r); + } } } diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/PostConstructAnnotationHandler.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/PostConstructAnnotationHandler.java index 01d0ae8ac7d..357cd4e39fe 100644 --- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/PostConstructAnnotationHandler.java +++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/PostConstructAnnotationHandler.java @@ -13,77 +13,65 @@ package org.eclipse.jetty.annotations; -import java.util.List; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; -import org.eclipse.jetty.annotations.AnnotationParser.AnnotationHandler; -import org.eclipse.jetty.annotations.AnnotationParser.Value; +import javax.annotation.PostConstruct; + +import org.eclipse.jetty.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler; import org.eclipse.jetty.plus.annotation.LifeCycleCallbackCollection; import org.eclipse.jetty.plus.annotation.PostConstructCallback; -import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.webapp.MetaData; import org.eclipse.jetty.webapp.WebAppContext; -public class PostConstructAnnotationHandler implements AnnotationHandler +public class PostConstructAnnotationHandler extends AbstractIntrospectableAnnotationHandler { - protected WebAppContext _wac; + protected WebAppContext _context; protected LifeCycleCallbackCollection _callbacks; public PostConstructAnnotationHandler (WebAppContext wac) { - _wac = wac; - _callbacks = (LifeCycleCallbackCollection)_wac.getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION); + super(true); + _context = wac; + _callbacks = (LifeCycleCallbackCollection)_context.getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION); } - public void handleClass(String className, int version, int access, String signature, String superName, String[] interfaces, String annotation, - List values) - { - Log.warn ("@PostConstruct annotation not applicable to classes: "+className); - } - - public void handleField(String className, String fieldName, int access, String fieldType, String signature, Object value, String annotation, - List values) - { - Log.warn("@PostConstruct annotation not applicable to fields: "+className+"."+fieldName); - } - - public void handleMethod(String className, String methodName, int access, String desc, String signature, String[] exceptions, String annotation, - List values) + public void doHandle(Class clazz) { - try + //Check that the PostConstruct is on a class that we're interested in + if (Util.isServletType(clazz)) { - org.objectweb.asm.Type[] args = org.objectweb.asm.Type.getArgumentTypes(desc); - - if (args.length != 0) + Method[] methods = clazz.getDeclaredMethods(); + for (int i=0; i 0) - { - Log.warn("Skipping PostConstruct annotation on "+className+"."+methodName+": is static"); - return; - } - - PostConstructCallback callback = new PostConstructCallback(); - callback.setTarget(className, methodName); - _callbacks.add(callback); - } - catch (Exception e) - { - Log.warn(e); } } - } diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/PreDestroyAnnotationHandler.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/PreDestroyAnnotationHandler.java index 4b1bd57e03d..3b593f5ca77 100644 --- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/PreDestroyAnnotationHandler.java +++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/PreDestroyAnnotationHandler.java @@ -13,75 +13,64 @@ package org.eclipse.jetty.annotations; -import java.util.List; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; -import org.eclipse.jetty.annotations.AnnotationParser.AnnotationHandler; -import org.eclipse.jetty.annotations.AnnotationParser.Value; +import javax.annotation.PreDestroy; + +import org.eclipse.jetty.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler; import org.eclipse.jetty.plus.annotation.LifeCycleCallbackCollection; import org.eclipse.jetty.plus.annotation.PreDestroyCallback; -import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.webapp.MetaData; import org.eclipse.jetty.webapp.WebAppContext; -public class PreDestroyAnnotationHandler implements AnnotationHandler +public class PreDestroyAnnotationHandler extends AbstractIntrospectableAnnotationHandler { - WebAppContext _wac; + WebAppContext _context; LifeCycleCallbackCollection _callbacks; public PreDestroyAnnotationHandler (WebAppContext wac) { - _wac = wac; - _callbacks = (LifeCycleCallbackCollection)_wac.getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION); + super(true); + _context = wac; + _callbacks = (LifeCycleCallbackCollection)_context.getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION); } - public void handleClass(String className, int version, int access, String signature, String superName, String[] interfaces, String annotation, - List values) - { - Log.warn("@PreDestroy annotation not applicable for classes: "+className); - } - - public void handleField(String className, String fieldName, int access, String fieldType, String signature, Object value, String annotation, - List values) - { - Log.warn("@PreDestroy annotation not applicable for fields: "+className+"."+fieldName); - } - - public void handleMethod(String className, String methodName, int access, String desc, String signature, String[] exceptions, String annotation, - List values) - { - try + public void doHandle(Class clazz) + { + //Check that the PreDestroy is on a class that we're interested in + if (Util.isServletType(clazz)) { - org.objectweb.asm.Type[] args = org.objectweb.asm.Type.getArgumentTypes(desc); - - if (args.length != 0) + Method[] methods = clazz.getDeclaredMethods(); + for (int i=0; i 0) - { - Log.warn("Skipping PreDestroy annotation on "+className+"."+methodName+": is static"); - return; - } - - PreDestroyCallback callback = new PreDestroyCallback(); - callback.setTarget(className, methodName); - _callbacks.add(callback); - } - catch (Exception e) - { - Log.warn(e); } } } diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ResourceAnnotationHandler.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ResourceAnnotationHandler.java index b506dac71fa..c3e55481463 100644 --- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ResourceAnnotationHandler.java +++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ResourceAnnotationHandler.java @@ -13,28 +13,33 @@ package org.eclipse.jetty.annotations; -import java.util.List; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import javax.annotation.Resource; import javax.naming.InitialContext; import javax.naming.NameNotFoundException; import javax.naming.NamingException; -import org.eclipse.jetty.annotations.AnnotationParser.AnnotationHandler; -import org.eclipse.jetty.annotations.AnnotationParser.Value; +import org.eclipse.jetty.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler; import org.eclipse.jetty.plus.annotation.Injection; import org.eclipse.jetty.plus.annotation.InjectionCollection; import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.webapp.MetaData; import org.eclipse.jetty.webapp.WebAppContext; +import org.eclipse.jetty.webapp.MetaData.Origin; -public class ResourceAnnotationHandler implements AnnotationHandler +public class ResourceAnnotationHandler extends AbstractIntrospectableAnnotationHandler { - protected WebAppContext _wac; + protected WebAppContext _context; protected InjectionCollection _injections; public ResourceAnnotationHandler (WebAppContext wac) { - _wac = wac; - _injections = (InjectionCollection)_wac.getAttribute(InjectionCollection.INJECTION_COLLECTION); + super(true); + _context = wac; + _injections = (InjectionCollection)_context.getAttribute(InjectionCollection.INJECTION_COLLECTION); } @@ -43,95 +48,104 @@ public class ResourceAnnotationHandler implements AnnotationHandler * environment that will be looked up at runtime. They do * not specify an injection. */ - public void handleClass(String className, int version, int access, String signature, String superName, String[] interfaces, String annotation, - List values) + public void doHandle(Class clazz) { - String name = null; - String mappedName = null; - String resourceType = null; - if (values != null) + if (Util.isServletType(clazz)) { - for (Value v : values) - { - if ("name".equals(v.getName())) - name = (String)v.getValue(); - else if ("mappedName".equals(v.getName())) - mappedName = (String)v.getValue(); - else if ("type".equals(v.getName())) - resourceType = (String)v.getValue(); - } - - if (name==null || name.trim().equals("")) - throw new IllegalStateException ("Class level Resource annotations must contain a name (Common Annotations Spec Section 2.3)"); - - try - { - if (!org.eclipse.jetty.plus.jndi.NamingEntryUtil.bindToENC(_wac, name,mappedName)) - if (!org.eclipse.jetty.plus.jndi.NamingEntryUtil.bindToENC(_wac.getServer(), name,mappedName)) - throw new IllegalStateException("No resource at "+(mappedName==null?name:mappedName)); - } - catch (NamingException e) - { - Log.warn(e); - } + handleClass(clazz); + + Method[] methods = clazz.getDeclaredMethods(); + for (int i=0; i values) + public void handleField(Class clazz, Field field) { - try + Resource resource = (Resource)field.getAnnotation(Resource.class); + if (resource != null) { //JavaEE Spec 5.2.3: Field cannot be static - if ((access & org.objectweb.asm.Opcodes.ACC_STATIC) > 0) + if (Modifier.isStatic(field.getModifiers())) { - Log.warn("Skipping Resource annotation on "+className+"."+fieldName+": cannot be static"); + Log.warn("Skipping Resource annotation on "+clazz.getName()+"."+field.getName()+": cannot be static"); return; } //JavaEE Spec 5.2.3: Field cannot be final - if ((access & org.objectweb.asm.Opcodes.ACC_FINAL) > 0) + if (Modifier.isFinal(field.getModifiers())) { - Log.warn("Skipping Resource annotation on "+className+"."+fieldName+": cannot be final"); + Log.warn("Skipping Resource annotation on "+clazz.getName()+"."+field.getName()+": cannot be final"); return; } //work out default name - String name = className+"/"+fieldName; - String mappedName = null; - org.objectweb.asm.Type resourceType = null; - if (values != null) + String name = clazz.getCanonicalName()+"/"+field.getName(); + + //allow @Resource name= to override the field name + name = (resource.name()!=null && !resource.name().trim().equals("")? resource.name(): name); + String mappedName = (resource.mappedName()!=null && !resource.mappedName().trim().equals("")?resource.mappedName():null); + //get the type of the Field + Class type = field.getType(); + + //Servlet Spec 3.0 p. 76 + //If a descriptor has specified at least 1 injection target for this + //resource, then it overrides this annotation + MetaData metaData = ((MetaData)_context.getAttribute(MetaData.METADATA)); + if (metaData.getOriginDescriptor("resource-ref."+name+".injection") != null) { - for (Value val :values) - { - //allow @Resource name= to override the field name - if (val.getName().equals("name") && !"".equals((String)val.getValue())) - name = (String)(val.getValue()); - //get the mappedName if there is one - else if (val.getName().equals("mappedName") && !"".equals((String)val.getValue())) - mappedName = (String)val.getValue(); - //save @Resource type, so we can check it is compatible with field type later - else if (val.getName().equals("type")) - { - resourceType = (org.objectweb.asm.Type)(val.getValue()); - } - } + //at least 1 injection was specified for this resource by a descriptor, so + //it overrides this annotation + return; } - - //check if an injection has already been setup for this target by web.xml - Injection webXmlInjection = _injections.getInjection(name, className, fieldName); - if (webXmlInjection == null) + + //No injections for this resource in any descriptors, so we can add it + //Does the injection already exist? + Injection injection = _injections.getInjection(name, clazz, field); + if (injection == null) { + //No injection has been specified, add it try { - boolean bound = org.eclipse.jetty.plus.jndi.NamingEntryUtil.bindToENC(_wac, name, mappedName); + boolean bound = org.eclipse.jetty.plus.jndi.NamingEntryUtil.bindToENC(_context, name, mappedName); if (!bound) - bound = org.eclipse.jetty.plus.jndi.NamingEntryUtil.bindToENC(_wac.getServer(), name, mappedName); + bound = org.eclipse.jetty.plus.jndi.NamingEntryUtil.bindToENC(_context.getServer(), name, mappedName); if (!bound) bound = org.eclipse.jetty.plus.jndi.NamingEntryUtil.bindToENC(null, name, mappedName); if (!bound) { - //see if there is an env-entry value been bound from web.xml + //see if there is an env-entry value been bound try { InitialContext ic = new InitialContext(); @@ -149,16 +163,17 @@ public class ResourceAnnotationHandler implements AnnotationHandler { Log.debug("Bound "+(mappedName==null?name:mappedName) + " as "+ name); // Make the Injection for it if the binding succeeded - Injection injection = new Injection(); - injection.setTarget(className, fieldName, Util.asCanonicalName(resourceType)); + injection = new Injection(); + injection.setTarget(clazz, field, type); injection.setJndiName(name); injection.setMappingName(mappedName); _injections.add(injection); - } - else if (!Util.isEnvEntryType(fieldType)) - { - System.err.println(fieldType); + //TODO - an @Resource is equivalent to a resource-ref, resource-env-ref, message-destination + metaData.setOrigin("resource-ref."+name+".injection"); + } + else if (!Util.isEnvEntryType(type)) + { //if this is an env-entry type resource and there is no value bound for it, it isn't //an error, it just means that perhaps the code will use a default value instead // JavaEE Spec. sec 5.4.1.3 @@ -171,29 +186,26 @@ public class ResourceAnnotationHandler implements AnnotationHandler //if this is an env-entry type resource and there is no value bound for it, it isn't //an error, it just means that perhaps the code will use a default value instead // JavaEE Spec. sec 5.4.1.3 - if (!Util.isEnvEntryType(fieldType)) + if (!Util.isEnvEntryType(type)) throw new IllegalStateException(e); } } } - catch (Exception e) - { - Log.warn(e); - } } + /** * Process a Resource annotation on a Method. * * This will generate a JNDI entry, and an Injection to be * processed when an instance of the class is created. */ - public void handleMethod(String className, String methodName, int access, String desc, String signature, String[] exceptions, String annotation, - List values) + public void handleMethod(Class clazz, Method method) { - try + + Resource resource = (Resource)method.getAnnotation(Resource.class); + if (resource != null) { - /* * Commons Annotations Spec 2.3 * " The Resource annotation is used to declare a reference to a resource. @@ -212,70 +224,68 @@ public class ResourceAnnotationHandler implements AnnotationHandler * or field, inject it!". */ //JavaEE Spec 5.2.3: Method cannot be static - if ((access & org.objectweb.asm.Opcodes.ACC_STATIC) > 0) + if (Modifier.isStatic(method.getModifiers())) { - Log.warn("Skipping Resource annotation on "+className+"."+methodName+": cannot be static"); + Log.warn("Skipping Resource annotation on "+clazz.getName()+"."+method.getName()+": cannot be static"); return; } // Check it is a valid javabean: must be void return type, the name must start with "set" and it must have // only 1 parameter - if (!methodName.startsWith("set")) + if (!method.getName().startsWith("set")) { - Log.warn("Skipping Resource annotation on "+className+"."+methodName+": invalid java bean, does not start with 'set'"); + Log.warn("Skipping Resource annotation on "+clazz.getName()+"."+method.getName()+": invalid java bean, does not start with 'set'"); return; } - org.objectweb.asm.Type[] args = org.objectweb.asm.Type.getArgumentTypes(desc); - if (args == null || args.length != 1) + + if (method.getParameterTypes().length != 1) { - Log.warn("Skipping Resource annotation on "+className+"."+methodName+": invalid java bean, not single argument to method"); + Log.warn("Skipping Resource annotation on "+clazz.getName()+"."+method.getName()+": invalid java bean, not single argument to method"); return; } - org.objectweb.asm.Type retVal = org.objectweb.asm.Type.getReturnType(desc); - if (!org.objectweb.asm.Type.VOID_TYPE.equals(retVal)) + + if (Void.TYPE != method.getReturnType()) { - Log.warn("Skipping Resource annotation on "+className+"."+methodName+": invalid java bean, not void"); + Log.warn("Skipping Resource annotation on "+clazz.getName()+"."+method.getName()+": invalid java bean, not void"); return; } - + //default name is the javabean property name - String name = methodName.substring(3); + String name = method.getName().substring(3); name = name.substring(0,1).toLowerCase()+name.substring(1); - name = className+"/"+name; - String mappedName = null; - org.objectweb.asm.Type resourceType = null; - if (values != null) - { - for (Value v : values) - { - //allow default name to be overridden - if ("name".equals(v.getName())) - name = (String)(v.getValue()); - //get the mappedName if there is one - else if ("mappedName".equals(v.getName()) && !"".equals((String)(v.getValue()))) - mappedName = (String)(v.getValue()); - else if ("type".equals(v.getName())) - { - resourceType = (org.objectweb.asm.Type)(v.getValue()); - } - //TODO: authentication and shareable - } - } + name = clazz.getCanonicalName()+"/"+name; + name = (resource.name()!=null && !resource.name().trim().equals("")? resource.name(): name); + String mappedName = (resource.mappedName()!=null && !resource.mappedName().trim().equals("")?resource.mappedName():null); + Class paramType = method.getParameterTypes()[0]; + + Class resourceType = resource.type(); + + //Servlet Spec 3.0 p. 76 + //If a descriptor has specified at least 1 injection target for this + //resource, then it overrides this annotation + MetaData metaData = ((MetaData)_context.getAttribute(MetaData.METADATA)); + if (metaData.getOriginDescriptor("resource-ref."+name+".injection") != null) + { + //at least 1 injection was specified for this resource by a descriptor, so + //it overrides this annotation + return; + } + //check if an injection has already been setup for this target by web.xml - Injection webXmlInjection = _injections.getInjection(name, className, methodName, Util.asCanonicalName(args[0])); - if (webXmlInjection == null) + Injection injection = _injections.getInjection(name, clazz, method, paramType); + if (injection == null) { try { //try binding name to environment //try the webapp's environment first - boolean bound = org.eclipse.jetty.plus.jndi.NamingEntryUtil.bindToENC(_wac, name, mappedName); + boolean bound = org.eclipse.jetty.plus.jndi.NamingEntryUtil.bindToENC(_context, name, mappedName); //try the server's environment if (!bound) - bound = org.eclipse.jetty.plus.jndi.NamingEntryUtil.bindToENC(_wac.getServer(), name, mappedName); + bound = org.eclipse.jetty.plus.jndi.NamingEntryUtil.bindToENC(_context.getServer(), name, mappedName); //try the jvm's environment if (!bound) @@ -302,13 +312,15 @@ public class ResourceAnnotationHandler implements AnnotationHandler { Log.debug("Bound "+(mappedName==null?name:mappedName) + " as "+ name); // Make the Injection for it - Injection injection = new Injection(); - injection.setTarget(className, methodName, Util.asCanonicalName(args[0]), Util.asCanonicalName(resourceType)); + injection = new Injection(); + injection.setTarget(clazz, method,paramType,resourceType); injection.setJndiName(name); injection.setMappingName(mappedName); _injections.add(injection); + //TODO - an @Resource is equivalent to a resource-ref, resource-env-ref, message-destination + metaData.setOrigin("resource-ref."+name+".injection"); } - else if (!Util.isEnvEntryType(args[0].getDescriptor())) + else if (!Util.isEnvEntryType(paramType)) { //if this is an env-entry type resource and there is no value bound for it, it isn't @@ -322,14 +334,11 @@ public class ResourceAnnotationHandler implements AnnotationHandler //if this is an env-entry type resource and there is no value bound for it, it isn't //an error, it just means that perhaps the code will use a default value instead // JavaEE Spec. sec 5.4.1.3 - if (!Util.isEnvEntryType(args[0].getDescriptor())) + if (!Util.isEnvEntryType(paramType)) throw new IllegalStateException(e); } } - } - catch (Exception e) - { - Log.warn(e); + } } } diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ResourcesAnnotationHandler.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ResourcesAnnotationHandler.java index 0b7455449e4..90e71ced785 100644 --- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ResourcesAnnotationHandler.java +++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ResourcesAnnotationHandler.java @@ -13,78 +13,63 @@ package org.eclipse.jetty.annotations; -import java.util.List; - +import javax.annotation.Resource; +import javax.annotation.Resources; import javax.naming.NamingException; -import org.eclipse.jetty.annotations.AnnotationParser.AnnotationHandler; -import org.eclipse.jetty.annotations.AnnotationParser.ListValue; -import org.eclipse.jetty.annotations.AnnotationParser.Value; +import org.eclipse.jetty.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.webapp.WebAppContext; -public class ResourcesAnnotationHandler implements AnnotationHandler +public class ResourcesAnnotationHandler extends AbstractIntrospectableAnnotationHandler { protected WebAppContext _wac; public ResourcesAnnotationHandler (WebAppContext wac) { + super(true); _wac = wac; } - public void handleClass(String className, int version, int access, String signature, String superName, String[] interfaces, String annotation, - List values) + public void doHandle (Class clazz) { - if (values != null && values.size() == 1) + Resources resources = (Resources)clazz.getAnnotation(Resources.class); + if (resources != null) { - List list = (List)(values.get(0).getValue()); - for (ListValue resource : list) + Resource[] resArray = resources.value(); + if (resArray==null||resArray.length==0) { - List resourceValues = resource.getList(); - String name = null; - String mappedName = null; - for (Value v:resourceValues) + Log.warn ("Skipping empty or incorrect Resources annotation on "+clazz.getName()); + return; + } + + for (int j=0;j values) - { - Log.warn ("@Resources not applicable for fields: "+className+"."+fieldName); - } - - public void handleMethod(String className, String methodName, int access, String params, String signature, String[] exceptions, String annotation, - List values) - { - Log.warn ("@Resources not applicable for methods: "+className+"."+methodName); - } + } } diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/RunAsAnnotationHandler.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/RunAsAnnotationHandler.java index 4bd72384ec2..cf849771d7d 100644 --- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/RunAsAnnotationHandler.java +++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/RunAsAnnotationHandler.java @@ -15,47 +15,63 @@ package org.eclipse.jetty.annotations; import java.util.List; -import org.eclipse.jetty.annotations.AnnotationParser.AnnotationHandler; +import javax.servlet.Servlet; + +import org.eclipse.jetty.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler; import org.eclipse.jetty.annotations.AnnotationParser.Value; import org.eclipse.jetty.plus.annotation.RunAsCollection; +import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.webapp.Descriptor; +import org.eclipse.jetty.webapp.MetaData; import org.eclipse.jetty.webapp.WebAppContext; -public class RunAsAnnotationHandler implements AnnotationHandler +public class RunAsAnnotationHandler extends AbstractIntrospectableAnnotationHandler { - protected WebAppContext _wac; + protected WebAppContext _context; public RunAsAnnotationHandler (WebAppContext wac) { - _wac = wac; + //Introspect only the given class for a RunAs annotation, as it is a class level annotation, + //and according to Common Annotation Spec p2-6 a class-level annotation is not inheritable. + super(false); + _context = wac; } - public void handleClass(String className, int version, int access, String signature, String superName, String[] interfaces, String annotation, - List values) + public void doHandle (Class clazz) { - RunAsCollection runAsCollection = (RunAsCollection)_wac.getAttribute(RunAsCollection.RUNAS_COLLECTION); - - try + RunAsCollection runAsCollection = (RunAsCollection)_context.getAttribute(RunAsCollection.RUNAS_COLLECTION); + + if (!Servlet.class.isAssignableFrom(clazz)) + return; + + javax.annotation.security.RunAs runAs = (javax.annotation.security.RunAs)clazz.getAnnotation(javax.annotation.security.RunAs.class); + if (runAs != null) { - if (values != null && values.size() == 1) + String role = runAs.value(); + if (role != null) { - String role = (String)values.get(0).getValue(); - if (role != null) + ServletHolder holder = getServletHolderForClass(clazz); + if (holder != null) { - org.eclipse.jetty.plus.annotation.RunAs ra = new org.eclipse.jetty.plus.annotation.RunAs(); - ra.setTargetClassName(className); - ra.setRoleName(role); - runAsCollection.add(ra); + MetaData metaData = ((MetaData)_context.getAttribute(MetaData.METADATA)); + Descriptor d = metaData.getOriginDescriptor(holder.getName()+".servlet.run-as"); + //if a descriptor has already set the value for run-as, do not + //let the annotation override it + if (d == null) + { + metaData.setOrigin(holder.getName()+".servlet.run-as"); + org.eclipse.jetty.plus.annotation.RunAs ra = new org.eclipse.jetty.plus.annotation.RunAs(); + ra.setTargetClassName(clazz.getCanonicalName()); + ra.setRoleName(role); + runAsCollection.add(ra); + } } } else - Log.warn("Bad value for @RunAs annotation on class "+className); + Log.warn("Bad value for @RunAs annotation on class "+clazz.getName()); } - catch (Exception e) - { - Log.warn(e); - } - + } public void handleField(String className, String fieldName, int access, String fieldType, String signature, Object value, String annotation, @@ -70,4 +86,20 @@ public class RunAsAnnotationHandler implements AnnotationHandler Log.warn("@RunAs annotation ignored on method: "+className+"."+methodName+" "+signature); } + private ServletHolder getServletHolderForClass (Class clazz) + { + ServletHolder holder = null; + ServletHolder[] holders = _context.getServletHandler().getServlets(); + if (holders != null) + { + for (ServletHolder h : holders) + { + if (h.getClassName().equals(clazz.getName())) + { + holder = h; + } + } + } + return holder; + } } diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebAppDecoratorWrapper.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebAppDecoratorWrapper.java new file mode 100644 index 00000000000..c1f96731c5d --- /dev/null +++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebAppDecoratorWrapper.java @@ -0,0 +1,164 @@ +// ======================================================================== +// Copyright (c) 2006-2009 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// 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.annotations; + +import java.util.EventListener; + +import javax.servlet.Filter; +import javax.servlet.Servlet; +import javax.servlet.ServletException; + +import org.eclipse.jetty.servlet.FilterHolder; +import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.servlet.ServletContextHandler.Decorator; +import org.eclipse.jetty.webapp.WebAppContext; + +/** + * WebAppDecoratorWrapper + * + * + */ +public class WebAppDecoratorWrapper implements Decorator +{ + Decorator _wrappedDecorator; + AnnotationIntrospector _introspector = new AnnotationIntrospector(); + + /** + * @param context + */ + public WebAppDecoratorWrapper(WebAppContext context, Decorator wrappedDecorator) + { + _wrappedDecorator = wrappedDecorator; + _introspector.registerHandler(new ResourceAnnotationHandler(context)); + _introspector.registerHandler(new ResourcesAnnotationHandler(context)); + _introspector.registerHandler(new RunAsAnnotationHandler(context)); + _introspector.registerHandler(new PostConstructAnnotationHandler(context)); + _introspector.registerHandler(new PreDestroyAnnotationHandler(context)); + _introspector.registerHandler(new DeclareRolesAnnotationHandler(context)); + } + + /* ------------------------------------------------------------ */ + /** + * @param filter + * @throws ServletException + * @see org.eclipse.jetty.servlet.ServletContextHandler.Decorator#decorateFilterHolder(org.eclipse.jetty.servlet.FilterHolder) + */ + public void decorateFilterHolder(FilterHolder filter) throws ServletException + { + _wrappedDecorator.decorateFilterHolder(filter); + } + + /* ------------------------------------------------------------ */ + /** + * @param + * @param filter + * @return + * @throws ServletException + * @see org.eclipse.jetty.servlet.ServletContextHandler.Decorator#decorateFilterInstance(javax.servlet.Filter) + */ + public T decorateFilterInstance(T filter) throws ServletException + { + introspect(filter); + return _wrappedDecorator.decorateFilterInstance(filter); + } + + /* ------------------------------------------------------------ */ + /** + * @param + * @param listener + * @return + * @throws ServletException + * @see org.eclipse.jetty.servlet.ServletContextHandler.Decorator#decorateListenerInstance(java.util.EventListener) + */ + public T decorateListenerInstance(T listener) throws ServletException + { + introspect(listener); + return _wrappedDecorator.decorateListenerInstance(listener); + } + + /* ------------------------------------------------------------ */ + /** + * @param servlet + * @throws ServletException + * @see org.eclipse.jetty.servlet.ServletContextHandler.Decorator#decorateServletHolder(org.eclipse.jetty.servlet.ServletHolder) + */ + public void decorateServletHolder(ServletHolder servlet) throws ServletException + { + _wrappedDecorator.decorateServletHolder(servlet); + } + + /* ------------------------------------------------------------ */ + /** + * @param + * @param servlet + * @return + * @throws ServletException + * @see org.eclipse.jetty.servlet.ServletContextHandler.Decorator#decorateServletInstance(javax.servlet.Servlet) + */ + public T decorateServletInstance(T servlet) throws ServletException + { + introspect(servlet); + return _wrappedDecorator.decorateServletInstance(servlet); + } + + /* ------------------------------------------------------------ */ + /** + * @param f + * @see org.eclipse.jetty.servlet.ServletContextHandler.Decorator#destroyFilterInstance(javax.servlet.Filter) + */ + public void destroyFilterInstance(Filter f) + { + _wrappedDecorator.destroyFilterInstance(f); + } + + /* ------------------------------------------------------------ */ + /** + * @param s + * @see org.eclipse.jetty.servlet.ServletContextHandler.Decorator#destroyServletInstance(javax.servlet.Servlet) + */ + public void destroyServletInstance(Servlet s) + { + _wrappedDecorator.destroyServletInstance(s); + } + + + + + + /* ------------------------------------------------------------ */ + /** + * @param f + * @see org.eclipse.jetty.servlet.ServletContextHandler.Decorator#destroyListenerInstance(java.util.EventListener) + */ + public void destroyListenerInstance(EventListener f) + { + _wrappedDecorator.destroyListenerInstance(f); + } + + /** + * Look for annotations that can be discovered with introspection: + *
    + *
  • Resource + *
  • Resources + *
  • PostConstruct + *
  • PreDestroy + *
  • ServletSecurity? + *
+ * @param o + */ + protected void introspect (Object o) + { + _introspector.introspect(o.getClass()); + } +} diff --git a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/FilterC.java b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/FilterC.java index 2b28fe2a532..4c85ee713ca 100644 --- a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/FilterC.java +++ b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/FilterC.java @@ -28,8 +28,6 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; - - @RunAs("admin") public class FilterC implements Filter { diff --git a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/ListenerC.java b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/ListenerC.java index e67c4a3ad82..d839c47c715 100644 --- a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/ListenerC.java +++ b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/ListenerC.java @@ -15,8 +15,6 @@ package org.eclipse.jetty.annotations; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; - - public class ListenerC implements ServletContextListener { diff --git a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/ServletC.java b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/ServletC.java index 4d931f77034..87cad8bc608 100644 --- a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/ServletC.java +++ b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/ServletC.java @@ -1,5 +1,5 @@ // ======================================================================== -// Copyright (c) 2008-2009 Mort Bay Consulting Pty. Ltd. +// Copyright (c) 2008-2010 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 @@ -17,7 +17,7 @@ import java.io.IOException; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import javax.annotation.Resource; -import javax.annotation.Resources; +import javax.annotation.security.DeclareRoles; import javax.annotation.security.RunAs; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; @@ -26,11 +26,7 @@ import javax.servlet.http.HttpServletResponse; - -@Resources({ - @Resource(name="apple", mappedName="foo"), - @Resource(name="banana", mappedName="foo") -}) +@DeclareRoles({"alice"}) @RunAs("admin") public class ServletC extends HttpServlet { diff --git a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationConfiguration.java b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationConfiguration.java new file mode 100644 index 00000000000..f3e08e1beb3 --- /dev/null +++ b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationConfiguration.java @@ -0,0 +1,84 @@ +// ======================================================================== +// Copyright (c) 2006-2009 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// 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.annotations; + +import java.io.File; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +import junit.framework.TestCase; + +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.webapp.FragmentDescriptor; +import org.eclipse.jetty.webapp.WebAppContext; + +/** + * TestAnnotationConfiguration + * + * + */ +public class TestAnnotationConfiguration extends TestCase +{ + + + + + public void testGetFragmentFromJar () + throws Exception + { + String dir = System.getProperty("basedir", "."); + File file = new File(dir); + file=new File(file.getCanonicalPath()); + URL url=file.toURL(); + + Resource jar1 = Resource.newResource(url+"file.jar"); + + AbstractConfiguration config = new AbstractConfiguration() + { + + 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 preConfigure(WebAppContext context) throws Exception + { + // TODO Auto-generated method stub + + } + + }; + WebAppContext wac = new WebAppContext(); + + List frags = new ArrayList(); + frags.add(new FragmentDescriptor(Resource.newResource("jar:"+url+"file.jar!/fooa.props"), null)); + frags.add(new FragmentDescriptor(Resource.newResource("jar:"+url+"file2.jar!/foob.props"), null)); + + assertNotNull(config.getFragmentFromJar(jar1, frags)); + } +} diff --git a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationInheritance.java b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationInheritance.java index 0a5766e2f18..e7f482de01f 100644 --- a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationInheritance.java +++ b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationInheritance.java @@ -13,31 +13,29 @@ package org.eclipse.jetty.annotations; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + import java.util.ArrayList; import java.util.List; -import java.util.Map; + import javax.naming.Context; import javax.naming.InitialContext; -import org.eclipse.jetty.annotations.AnnotationParser.AnnotationHandler; +import org.eclipse.jetty.annotations.AnnotationParser.DiscoverableAnnotationHandler; import org.eclipse.jetty.annotations.AnnotationParser.Value; -import org.eclipse.jetty.util.MultiMap; import org.junit.After; import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - /** * */ public class TestAnnotationInheritance { - private List classNames = new ArrayList(); - - private class SampleHandler implements AnnotationHandler + List classNames = new ArrayList(); + + + class SampleHandler implements DiscoverableAnnotationHandler { public final List annotatedClassNames = new ArrayList(); public final List annotatedMethods = new ArrayList(); @@ -188,40 +186,4 @@ public class TestAnnotationInheritance }); assertEquals (1, handler.annotatedClassNames.size()); } - - @Test - public void testTypeInheritanceHandling() throws Exception - { - AnnotationParser parser = new AnnotationParser(); - ClassInheritanceHandler handler = new ClassInheritanceHandler(); - parser.registerClassHandler(handler); - - class Foo implements InterfaceD - { - } - - classNames.clear(); - classNames.add(ClassA.class.getName()); - classNames.add(ClassB.class.getName()); - classNames.add(InterfaceD.class.getName()); - classNames.add(Foo.class.getName()); - - parser.parse(classNames, null); - - MultiMap map = handler.getMap(); - assertNotNull(map); - assertFalse(map.isEmpty()); - assertEquals(2, map.size()); - Map stringArrayMap = map.toStringArrayMap(); - assertTrue (stringArrayMap.keySet().contains("org.eclipse.jetty.annotations.ClassA")); - assertTrue (stringArrayMap.keySet().contains("org.eclipse.jetty.annotations.InterfaceD")); - String[] classes = (String[])stringArrayMap.get("org.eclipse.jetty.annotations.ClassA"); - assertEquals(1, classes.length); - assertEquals ("org.eclipse.jetty.annotations.ClassB", classes[0]); - - classes = (String[])stringArrayMap.get("org.eclipse.jetty.annotations.InterfaceD"); - assertEquals(2, classes.length); - assertEquals ("org.eclipse.jetty.annotations.ClassB", classes[0]); - assertEquals(Foo.class.getName(), classes[1]); - } } diff --git a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationParser.java b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationParser.java index 95cfef341fb..bef8867a7bb 100644 --- a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationParser.java +++ b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationParser.java @@ -15,7 +15,7 @@ package org.eclipse.jetty.annotations; import java.util.Arrays; import java.util.List; -import org.eclipse.jetty.annotations.AnnotationParser.AnnotationHandler; +import org.eclipse.jetty.annotations.AnnotationParser.DiscoverableAnnotationHandler; import org.eclipse.jetty.annotations.AnnotationParser.Value; import org.junit.Test; @@ -31,7 +31,7 @@ public class TestAnnotationParser String[] classNames = new String[]{"org.eclipse.jetty.annotations.ClassA"}; AnnotationParser parser = new AnnotationParser(); - class SampleAnnotationHandler implements AnnotationHandler + class SampleAnnotationHandler implements DiscoverableAnnotationHandler { private List methods = Arrays.asList("a", "b", "c", "d", "l"); @@ -104,7 +104,7 @@ public class TestAnnotationParser String[] classNames = new String[]{"org.eclipse.jetty.annotations.ClassB"}; AnnotationParser parser = new AnnotationParser(); - class MultiAnnotationHandler implements AnnotationHandler + class MultiAnnotationHandler implements DiscoverableAnnotationHandler { public void handleClass(String className, int version, int access, String signature, String superName, String[] interfaces, String annotation, List values) diff --git a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestServletAnnotations.java b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestServletAnnotations.java index 8c57200e8e2..08dbc745b50 100644 --- a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestServletAnnotations.java +++ b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestServletAnnotations.java @@ -13,17 +13,15 @@ package org.eclipse.jetty.annotations; -import java.util.ArrayList; -import java.util.List; - -import org.eclipse.jetty.annotations.AnnotationParser.AnnotationHandler; -import org.eclipse.jetty.annotations.AnnotationParser.Value; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; + +import java.util.Arrays; +import java.util.HashSet; + +import org.eclipse.jetty.security.ConstraintSecurityHandler; +import org.eclipse.jetty.webapp.MetaData; +import org.eclipse.jetty.webapp.WebAppContext; +import org.junit.Test; /** * TestServletAnnotations @@ -32,172 +30,20 @@ import static org.junit.Assert.fail; */ public class TestServletAnnotations { + @Test - public void testServletAnnotation() throws Exception - { - List classes = new ArrayList(); - classes.add("org.eclipse.jetty.annotations.ServletC"); - AnnotationParser parser = new AnnotationParser(); - - class ResourceAnnotationHandler implements AnnotationHandler - { - public void handleClass(String className, int version, int access, String signature, String superName, String[] interfaces, String annotation, - List values) - {} - - public void handleField(String className, String fieldName, int access, String fieldType, String signature, Object value, String annotation, - List values) - { - assertEquals ("org.eclipse.jetty.annotations.ServletC", className); - - assertEquals ("foo",fieldName); - assertNotNull (values); - assertNotNull (annotation); - assertTrue (annotation.endsWith("Resource")); - - for (Value v :values) - { - if (v.getName().equals("mappedName")) - assertEquals ("foo", v.getValue()); - else if (v.getName().equals("type")) - { - try - { - assertEquals(fieldType, ((org.objectweb.asm.Type)v.getValue()).getDescriptor()); - } - catch (Exception e) - { - fail(e.getMessage()); - } - } - } - } - - public void handleMethod(String className, String methodName, int access, String params, String signature, String[] exceptions, String annotation, - List values) - { - } - } - - class CallbackAnnotationHandler implements AnnotationHandler - { - public void handleClass(String className, int version, int access, String signature, String superName, String[] interfaces, String annotation, - List values) - { - } - - public void handleMethod(String className, String methodName, int access, String desc, String signature, String[] exceptions, String annotation, - List values) - { - assertEquals ("org.eclipse.jetty.annotations.ServletC", className); - assertNotNull(methodName); - if (methodName.endsWith("pre")) - { - assertTrue(annotation.endsWith("PreDestroy")); - assertTrue(values.isEmpty()); - } - else if (methodName.endsWith("post")) - { - assertTrue(annotation.endsWith("PostConstruct")); - assertTrue(values.isEmpty()); - } - - assertEquals (org.objectweb.asm.Type.VOID_TYPE, org.objectweb.asm.Type.getReturnType(desc)); - assertEquals(0, org.objectweb.asm.Type.getArgumentTypes(desc).length); - int isstatic = access & org.objectweb.asm.Opcodes.ACC_STATIC; - - assertTrue (isstatic == 0); - } - - public void handleField(String className, String fieldName, int access, String fieldType, String signature, Object value, String annotation, - List values) - { - } - } - - class RunAsAnnotationHandler implements AnnotationHandler - { - public void handleClass(String className, int version, int access, String signature, String superName, String[] interfaces, String annotation, - List values) - { - assertNotNull (values); - assertEquals(1, values.size()); - Value anv = values.get(0); - assertEquals("value", anv.getName()); - assertEquals("admin", anv.getValue()); - } - - public void handleMethod(String className, String methodName, int access, String params, String signature, String[] exceptions, String annotation, - List values) - { - } - - public void handleField(String className, String fieldName, int access, String fieldType, String signature, Object value, String annotation, - List values) - { - } - } - - class ResourcesAnnotationHandler implements AnnotationHandler - { - public void handleClass (String className, int version, int access, String signature, String superName, String[] interfaces, String annotation, - List values) - { - assertNotNull (values); - for (Value v : values) - { - List list = (List)(v.getValue()); - for (Object o : list) - { - AnnotationParser.ListValue lv = (AnnotationParser.ListValue)o; - List theValues = lv.getList(); - for (Value vv : theValues) - { - if ("name".equals((String)vv.getName())) - { - if (!"apple".equals((String)vv.getValue()) && !"banana".equals((String)vv.getValue())) - fail("Wrong name "+vv.getName()); - } - else if ("mappedName".equals((String)vv.getName())) - assertEquals("foo", (String)vv.getValue()); - - } - } - } - } - - public void handleMethod(String className, String methodName, int access, String params, String signature, String[] exceptions, String annotation, - List values) - { - } - - public void handleField(String className, String fieldName, int access, String fieldType, String signature, Object value, String annotation, - List values) - { - } - } - - parser.registerAnnotationHandler("javax.annotation.Resources", new ResourcesAnnotationHandler()); - parser.registerAnnotationHandler("javax.annotation.Resource", new ResourceAnnotationHandler ()); - parser.registerAnnotationHandler("javax.annotation.PostConstruct", new CallbackAnnotationHandler()); - parser.registerAnnotationHandler("javax.annotation.PreDestroy", new CallbackAnnotationHandler()); - parser.registerAnnotationHandler("javax.annotation.security.RunAs", new RunAsAnnotationHandler()); - - long start = System.currentTimeMillis(); - parser.parse(classes, new ClassNameResolver () - { - public boolean isExcluded(String name) - { - return false; - } - - public boolean shouldOverride(String name) - { - return false; - } - }); - long end = System.currentTimeMillis(); - - System.err.println("Time to parse class: "+((end-start))); + public void testDeclareRoles () + throws Exception + { + WebAppContext wac = new WebAppContext(); + wac.setAttribute(MetaData.METADATA, new MetaData(wac)); + ConstraintSecurityHandler sh = new ConstraintSecurityHandler(); + wac.setSecurityHandler(sh); + sh.setRoles(new HashSet(Arrays.asList(new String[]{"humpty", "dumpty"}))); + DeclareRolesAnnotationHandler handler = new DeclareRolesAnnotationHandler(wac); + handler.doHandle(ServletC.class); + assertTrue(sh.getRoles().contains("alice")); + assertTrue(sh.getRoles().contains("humpty")); + assertTrue(sh.getRoles().contains("dumpty")); } } diff --git a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/resources/TestResourceAnnotations.java b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/resources/TestResourceAnnotations.java index 6e1e3c79b48..6fdef5830c3 100644 --- a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/resources/TestResourceAnnotations.java +++ b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/resources/TestResourceAnnotations.java @@ -6,6 +6,7 @@ import java.util.List; import javax.naming.Context; import javax.naming.InitialContext; +import org.eclipse.jetty.annotations.AnnotationIntrospector; import org.eclipse.jetty.annotations.AnnotationParser; import org.eclipse.jetty.annotations.ClassNameResolver; import org.eclipse.jetty.annotations.ResourceAnnotationHandler; @@ -13,6 +14,8 @@ import org.eclipse.jetty.annotations.ResourcesAnnotationHandler; import org.eclipse.jetty.plus.annotation.Injection; import org.eclipse.jetty.plus.annotation.InjectionCollection; import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.webapp.MetaData; import org.eclipse.jetty.webapp.WebAppContext; import org.junit.Test; @@ -21,40 +24,29 @@ import static org.junit.Assert.assertNotNull; public class TestResourceAnnotations { + @Test - public void testResourceAnnotations() throws Exception + public void testResourceAnnotations () + throws Exception { Server server = new Server(); WebAppContext wac = new WebAppContext(); wac.setServer(server); InjectionCollection injections = new InjectionCollection(); wac.setAttribute(InjectionCollection.INJECTION_COLLECTION, injections); + wac.setAttribute(MetaData.METADATA, new MetaData(wac)); InitialContext ic = new InitialContext(); Context comp = (Context)ic.lookup("java:comp"); Context env = comp.createSubcontext("env"); + + org.eclipse.jetty.plus.jndi.EnvEntry resourceA = new org.eclipse.jetty.plus.jndi.EnvEntry(server, "resA", new Integer(1000), false); + org.eclipse.jetty.plus.jndi.EnvEntry resourceB = new org.eclipse.jetty.plus.jndi.EnvEntry(server, "resB", new Integer(2000), false); - org.eclipse.jetty.plus.jndi.EnvEntry resourceA = new org.eclipse.jetty.plus.jndi.EnvEntry(server, "resA", 1000, false); - org.eclipse.jetty.plus.jndi.EnvEntry resourceB = new org.eclipse.jetty.plus.jndi.EnvEntry(server, "resB", 2000, false); - - ArrayList classNames = new ArrayList(); - classNames.add(ResourceA.class.getName()); - classNames.add(ResourceB.class.getName()); - - AnnotationParser parser = new AnnotationParser(); + AnnotationIntrospector parser = new AnnotationIntrospector(); ResourceAnnotationHandler handler = new ResourceAnnotationHandler(wac); - parser.registerAnnotationHandler("javax.annotation.Resource", handler); - parser.parse(classNames, new ClassNameResolver() - { - public boolean isExcluded(String name) - { - return false; - } - - public boolean shouldOverride(String name) - { - return false; - } - }); + parser.registerHandler(handler); + parser.introspect(ResourceA.class); + parser.introspect(ResourceB.class); //processing classA should give us these jndi name bindings: // java:comp/env/myf @@ -82,7 +74,7 @@ public class TestResourceAnnotations //only 1 field injection because the other has no Resource mapping assertEquals(1, resBInjections.size()); Injection fi = resBInjections.get(0); - assertEquals ("f", fi.getFieldName()); + assertEquals ("f", fi.getTarget().getName()); //3 method injections on class ResourceA, 4 field injections List resAInjections = injections.getInjections(ResourceA.class.getCanonicalName()); @@ -126,9 +118,11 @@ public class TestResourceAnnotations comp.destroySubcontext("env"); } - + + @Test - public void testResourcesAnnotation() throws Exception + public void testResourcesAnnotation () + throws Exception { Server server = new Server(); WebAppContext wac = new WebAppContext(); @@ -138,29 +132,15 @@ public class TestResourceAnnotations InitialContext ic = new InitialContext(); Context comp = (Context)ic.lookup("java:comp"); Context env = comp.createSubcontext("env"); - org.eclipse.jetty.plus.jndi.EnvEntry resourceA = new org.eclipse.jetty.plus.jndi.EnvEntry(server, "resA", 1000, false); - org.eclipse.jetty.plus.jndi.EnvEntry resourceB = new org.eclipse.jetty.plus.jndi.EnvEntry(server, "resB", 2000, false); - - ArrayList classNames = new ArrayList(); - classNames.add(ResourceA.class.getName()); - classNames.add(ResourceB.class.getName()); - - AnnotationParser parser = new AnnotationParser(); + org.eclipse.jetty.plus.jndi.EnvEntry resourceA = new org.eclipse.jetty.plus.jndi.EnvEntry(server, "resA", new Integer(1000), false); + org.eclipse.jetty.plus.jndi.EnvEntry resourceB = new org.eclipse.jetty.plus.jndi.EnvEntry(server, "resB", new Integer(2000), false); + + AnnotationIntrospector introspector = new AnnotationIntrospector(); ResourcesAnnotationHandler handler = new ResourcesAnnotationHandler(wac); - parser.registerAnnotationHandler("javax.annotation.Resources", handler); - parser.parse(classNames, new ClassNameResolver() - { - public boolean isExcluded(String name) - { - return false; - } - - public boolean shouldOverride(String name) - { - return false; - } - }); - + introspector.registerHandler(handler); + introspector.introspect(ResourceA.class); + introspector.introspect(ResourceB.class); + assertEquals(resourceA.getObjectToBind(), env.lookup("peach")); assertEquals(resourceB.getObjectToBind(), env.lookup("pear")); } diff --git a/jetty-plus/src/main/config/etc/jetty-plus.xml b/jetty-plus/src/main/config/etc/jetty-plus.xml index c9ea6fd8297..fd6cca33bfb 100644 --- a/jetty-plus/src/main/config/etc/jetty-plus.xml +++ b/jetty-plus/src/main/config/etc/jetty-plus.xml @@ -58,7 +58,6 @@ - diff --git a/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/Injection.java b/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/Injection.java index 4b9c83ac1b7..79a80e264fd 100644 --- a/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/Injection.java +++ b/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/Injection.java @@ -39,12 +39,9 @@ public class Injection private String _jndiName; private String _mappingName; private Member _target; - private String _className; - private String _fieldName; - private String _methodName; - private String _paramCanonicalName; - private String _annotationResourceType; - + private Class _paramClass; + private Class _resourceClass; + public Injection () {} @@ -58,35 +55,24 @@ public class Injection return _targetClass; } - - public String getTargetClassName() + public Class getParamClass () { - return _className; + return _paramClass; } - - public String getFieldName () + + public Class getResourceClass () { - return _fieldName; - } - - public String getMethodName () - { - return _methodName; - } - - public String getParamCanonicalName () - { - return _paramCanonicalName; + return _resourceClass; } public boolean isField () { - return (_fieldName != null); + return (_target != null && _target instanceof Field); } public boolean isMethod () { - return (_methodName != null); + return (_target != null && _target instanceof Method); } /** @@ -126,59 +112,51 @@ public class Injection return _target; } - - - /** - * Set up an injection target that is a field - * @param className - * @param fieldName - */ - public void setTarget (String className, String fieldName, String annotationResourceType) + + public void setTarget(Class clazz, Field field, Class resourceType) { - _className = className; - _fieldName = fieldName; - _annotationResourceType = annotationResourceType; + _targetClass = clazz; + _target = field; + _resourceClass = resourceType; + } + public void setTarget(Class clazz, Method method, Class arg, Class resourceType) + { + _targetClass = clazz; + _target = method; + _resourceClass = resourceType; + _paramClass = arg; } - public void setTarget (String className, String methodName, String paramCanonicalName, String annotationResourceType) - { - _className = className; - _methodName = methodName; - _paramCanonicalName = paramCanonicalName; - _annotationResourceType = annotationResourceType; - } - - - public void setTarget (Class clazz, String targetName, Class targetType) + + public void setTarget (Class clazz, String target, Class resourceType) { + _targetClass = clazz; + _resourceClass = resourceType; + //first look for a javabeans style setter matching the targetName - String setter = "set"+targetName.substring(0,1).toUpperCase()+targetName.substring(1); + String setter = "set"+target.substring(0,1).toUpperCase()+target.substring(1); try { - Log.debug("Looking for method for setter: "+setter+" with arg "+targetType); - _target = IntrospectionUtil.findMethod(clazz, setter, new Class[] {targetType}, true, false); + Log.debug("Looking for method for setter: "+setter+" with arg "+_resourceClass); + _target = IntrospectionUtil.findMethod(clazz, setter, new Class[] {_resourceClass}, true, false); _targetClass = clazz; - _className = clazz.getCanonicalName(); - _methodName = targetName; - _paramCanonicalName = targetType.getCanonicalName(); + _paramClass = _resourceClass; } catch (NoSuchMethodException me) { //try as a field try { - _target = IntrospectionUtil.findField(clazz, targetName, targetType, true, false); - _targetClass = clazz; - _className = clazz.getCanonicalName(); - _fieldName = targetName; + _target = IntrospectionUtil.findField(clazz, target, resourceType, true, false); + _targetClass = clazz; } catch (NoSuchFieldException fe) { - throw new IllegalArgumentException("No such field or method "+targetName+" on class "+_targetClass); + throw new IllegalArgumentException("No such field or method "+target+" on class "+_targetClass); } } - } + } /** * Inject a value for a Resource from JNDI into an object @@ -186,19 +164,6 @@ public class Injection */ public void inject (Object injectable) { - try - { - if (_target == null) - loadField(); - - if (_target == null) - loadMethod(); - } - catch (Exception e) - { - throw new IllegalStateException (e); - } - if (_target != null) { if (_target instanceof Field) @@ -223,7 +188,7 @@ public class Injection return context.lookup("java:comp/env/"+getJndiName()); } - + /** * Inject value from jndi into a field of an instance @@ -231,24 +196,19 @@ public class Injection * @param injectable */ protected void injectField (Field field, Object injectable) - { - if (validateInjection()) + { + try { - try - { - boolean accessibility = field.isAccessible(); - field.setAccessible(true); - field.set(injectable, lookupInjectedValue()); - field.setAccessible(accessibility); - } - catch (Exception e) - { - Log.warn(e); - throw new IllegalStateException("Inject failed for field "+field.getName()); - } + boolean accessibility = field.isAccessible(); + field.setAccessible(true); + field.set(injectable, lookupInjectedValue()); + field.setAccessible(accessibility); + } + catch (Exception e) + { + Log.warn(e); + throw new IllegalStateException("Inject failed for field "+field.getName()); } - else - throw new IllegalStateException ("Invalid injection for "+_className+"."+_fieldName); } /** @@ -258,100 +218,18 @@ public class Injection */ protected void injectMethod (Method method, Object injectable) { - if (validateInjection()) + try { - try - { - boolean accessibility = method.isAccessible(); - method.setAccessible(true); - method.invoke(injectable, new Object[] {lookupInjectedValue()}); - method.setAccessible(accessibility); - } - catch (Exception e) - { - Log.warn(e); - throw new IllegalStateException("Inject failed for method "+method.getName()); - } + boolean accessibility = method.isAccessible(); + method.setAccessible(true); + method.invoke(injectable, new Object[] {lookupInjectedValue()}); + method.setAccessible(accessibility); + } + catch (Exception e) + { + Log.warn(e); + throw new IllegalStateException("Inject failed for method "+method.getName()); } - else - throw new IllegalStateException("Invalid injection for "+_className+"."+_methodName); } - - - - protected void loadField() - throws ClassNotFoundException, NoSuchFieldException - { - if (_fieldName == null || _className == null) - return; - - if (_targetClass == null) - _targetClass = Loader.loadClass(null, _className); - - _target = _targetClass.getDeclaredField(_fieldName); - } - - - /** - * Load the target class and method. - * A valid injection target method only has 1 argument. - * @throws ClassNotFoundException - * @throws NoSuchMethodException - */ - protected void loadMethod () - throws ClassNotFoundException, NoSuchMethodException - { - if (_methodName == null || _className == null) - return; - - if (_targetClass == null) - _targetClass = Loader.loadClass(null, _className); - - Class arg = TypeUtil.fromName(_paramCanonicalName); - - if (arg == null) - arg = Loader.loadClass(null, _paramCanonicalName); - - _target = _targetClass.getDeclaredMethod(_methodName, new Class[] {arg}); - } - - - private boolean validateInjection () - { - //check that if the injection came from an annotation, the type specified in the annotation - //is compatible with the field or method to inject - //JavaEE spec sec 5.2.4 - if (_annotationResourceType != null) - { - if (_target == null) - return false; - - try - { - Class annotationType = TypeUtil.fromName(_annotationResourceType); - if (annotationType == null) - annotationType = Loader.loadClass(null, _annotationResourceType); - - if (_target instanceof Field) - { - return ((Field)_target).getType().isAssignableFrom(annotationType); - } - else if (_target instanceof Method) - { - Class[] args = ((Method)_target).getParameterTypes(); - return args[0].isAssignableFrom(annotationType); - } - - return false; - } - catch (Exception e) - { - Log.warn("Unable to verify injection for "+_className+"."+ (_fieldName==null?_methodName:_fieldName)); - return false; - } - } - else - return true; - } } diff --git a/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/InjectionCollection.java b/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/InjectionCollection.java index 0d7bd771a33..7d87b80e77e 100644 --- a/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/InjectionCollection.java +++ b/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/InjectionCollection.java @@ -13,6 +13,8 @@ package org.eclipse.jetty.plus.annotation; +import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; @@ -30,22 +32,22 @@ public class InjectionCollection public static final String INJECTION_COLLECTION = "org.eclipse.jetty.injectionCollection"; private HashMap> _injectionMap = new HashMap>();//map of classname to injections + public void add (Injection injection) { - if ((injection==null) || injection.getTargetClassName()==null) + if ((injection==null) || injection.getTargetClass()==null) return; if (Log.isDebugEnabled()) - Log.debug("Adding injection for class="+(injection.getTargetClassName()+ " on a "+(injection.isField()?injection.getFieldName():injection.getMethodName()))); + Log.debug("Adding injection for class="+(injection.getTargetClass()+ " on a "+(injection.getTarget().getName()))); - List injections = (List)_injectionMap.get(injection.getTargetClassName()); + List injections = (List)_injectionMap.get(injection.getTargetClass().getCanonicalName()); if (injections==null) { injections = new ArrayList(); - _injectionMap.put(injection.getTargetClassName(), injections); + _injectionMap.put(injection.getTargetClass().getCanonicalName(), injections); } - injections.add(injection); } @@ -57,14 +59,15 @@ public class InjectionCollection return _injectionMap.get(className); } + - public Injection getInjection (String jndiName, String className, String fieldName) + public Injection getInjection (String jndiName, Class clazz, Field field) { - if (fieldName == null || className == null) + if (field == null || clazz == null) return null; - List injections = getInjections(className); + List injections = getInjections(clazz.getCanonicalName()); if (injections == null) return null; Iterator itor = injections.iterator(); @@ -72,19 +75,19 @@ public class InjectionCollection while (itor.hasNext() && injection == null) { Injection i = itor.next(); - if (fieldName.equals(i.getFieldName())) + if (i.isField() && field.getName().equals(i.getTarget().getName())) injection = i; } return injection; } - public Injection getInjection (String jndiName, String className, String methodName, String paramCanonicalName) + public Injection getInjection (String jndiName, Class clazz, Method method, Class paramClass) { - if (className == null || methodName == null || paramCanonicalName == null) + if (clazz == null || method == null || paramClass == null) return null; - List injections = getInjections(className); + List injections = getInjections(clazz.getCanonicalName()); if (injections == null) return null; Iterator itor = injections.iterator(); @@ -92,7 +95,7 @@ public class InjectionCollection while (itor.hasNext() && injection == null) { Injection i = itor.next(); - if (methodName.equals(i.getMethodName()) && paramCanonicalName.equals(i.getParamCanonicalName())) + if (i.isMethod() && i.getTarget().getName().equals(method.getName()) && paramClass.equals(i.getParamClass())) injection = i; } @@ -101,7 +104,6 @@ public class InjectionCollection public void inject (Object injectable) - throws Exception { if (injectable==null) return; diff --git a/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/LifeCycleCallback.java b/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/LifeCycleCallback.java index d0df01730b2..fc75dd4b3fd 100644 --- a/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/LifeCycleCallback.java +++ b/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/LifeCycleCallback.java @@ -13,6 +13,7 @@ package org.eclipse.jetty.plus.annotation; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -93,8 +94,8 @@ public abstract class LifeCycleCallback - public void callback (Object instance) - throws Exception + public void callback (Object instance) + throws SecurityException, NoSuchMethodException, ClassNotFoundException, IllegalArgumentException, IllegalAccessException, InvocationTargetException { if (_target == null) { diff --git a/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/LifeCycleCallbackCollection.java b/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/LifeCycleCallbackCollection.java index 89e370cb0cb..1d9a22981fe 100644 --- a/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/LifeCycleCallbackCollection.java +++ b/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/LifeCycleCallbackCollection.java @@ -101,14 +101,16 @@ public class LifeCycleCallbackCollection Class clazz = o.getClass(); List callbacks = postConstructCallbacksMap.get(clazz.getName()); - + if (callbacks == null) return; - + for (int i=0;i element, because you can get - //type from the element, but what to do if there is more - //than one element, do you just pick the type - //of the first one? - - //check for elements - initInjection (node, name, TypeUtil.fromName(type)); - - //bind the entry into jndi - Object value = TypeUtil.valueOf(type,valueStr); - bindEnvEntry(_context, name, value); - - } - - - /** - * Common Annotations Spec section 2.3: - * resource-ref is for: - * - javax.sql.DataSource - * - javax.jms.ConnectionFactory - * - javax.jms.QueueConnectionFactory - * - javax.jms.TopicConnectionFactory - * - javax.mail.Session - * - java.net.URL - * - javax.resource.cci.ConnectionFactory - * - org.omg.CORBA_2_3.ORB - * - any other connection factory defined by a resource adapter - * @param node - * @throws Exception - */ - protected void initResourceRef (XmlParser.Node node) - throws Exception - { - String jndiName = node.getString("res-ref-name",false,true); - String type = node.getString("res-type", false, true); - String auth = node.getString("res-auth", false, true); - String shared = node.getString("res-sharing-scope", false, true); - - //check for elements - Class typeClass = TypeUtil.fromName(type); - if (typeClass==null) - typeClass = _context.loadClass(type); - initInjection (node, jndiName, typeClass); - - bindResourceRef(_context, jndiName, typeClass); - } - - - /** - * Common Annotations Spec section 2.3: - * resource-env-ref is for: - * - javax.transaction.UserTransaction - * - javax.resource.cci.InteractionSpec - * - anything else that is not a connection factory - * @param node - * @throws Exception - */ - protected void initResourceEnvRef (XmlParser.Node node) - throws Exception - { - String jndiName = node.getString("resource-env-ref-name",false,true); - String type = node.getString("resource-env-ref-type", false, true); - - //check for elements - - //JavaEE Spec sec 5.7.1.3 says the resource-env-ref-type - //is mandatory, but the schema says it is optional! - Class typeClass = TypeUtil.fromName(type); - if (typeClass==null) - typeClass = _context.loadClass(type); - initInjection (node, jndiName, typeClass); - - bindResourceEnvRef(_context, jndiName, typeClass); - } - - - /** - * Common Annotations Spec section 2.3: - * message-destination-ref is for: - * - javax.jms.Queue - * - javax.jms.Topic - * @param node - * @throws Exception - */ - protected void initMessageDestinationRef (XmlParser.Node node) - throws Exception - { - String jndiName = node.getString("message-destination-ref-name",false,true); - String type = node.getString("message-destination-type",false,true); - String usage = node.getString("message-destination-usage",false,true); - - Class typeClass = TypeUtil.fromName(type); - if (typeClass==null) - typeClass = _context.loadClass(type); - initInjection(node, jndiName, typeClass); - - bindMessageDestinationRef(_context, jndiName, typeClass); - } - - - - /** - * Process <post-construct> - * @param node - */ - protected void initPostConstruct(XmlParser.Node node) - { - String className = node.getString("lifecycle-callback-class", false, true); - String methodName = node.getString("lifecycle-callback-method", false, true); - - if (className==null || className.equals("")) - { - Log.warn("No lifecycle-callback-class specified"); - return; - } - if (methodName==null || methodName.equals("")) - { - Log.warn("No lifecycle-callback-method specified for class "+className); - return; - } - - LifeCycleCallbackCollection callbacks = (LifeCycleCallbackCollection)_context.getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION); - try - { - Class clazz = _context.loadClass(className); - LifeCycleCallback callback = new PostConstructCallback(); - callback.setTarget(clazz, methodName); - callbacks.add(callback); - } - catch (ClassNotFoundException e) - { - Log.warn("Couldn't load post-construct target class "+className); - } - } - - - /** - * Process <pre-destroy> - * @param node - */ - protected void initPreDestroy(XmlParser.Node node) - { - String className = node.getString("lifecycle-callback-class", false, true); - String methodName = node.getString("lifecycle-callback-method", false, true); - if (className==null || className.equals("")) - { - Log.warn("No lifecycle-callback-class specified for pre-destroy"); - return; - } - if (methodName==null || methodName.equals("")) - { - Log.warn("No lifecycle-callback-method specified for pre-destroy class "+className); - return; - } - LifeCycleCallbackCollection callbacks = (LifeCycleCallbackCollection)_context.getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION); - try - { - Class clazz = _context.loadClass(className); - LifeCycleCallback callback = new PreDestroyCallback(); - callback.setTarget(clazz, methodName); - callbacks.add(callback); - } - catch (ClassNotFoundException e) - { - Log.warn("Couldn't load pre-destory target class "+className); - } - } - - - /** - * Iterate over the <injection-target> entries for a node - * - * @param node - * @param jndiName - * @param valueClass - */ - protected void initInjection (XmlParser.Node node, String jndiName, Class valueClass) - { - Iterator itor = node.iterator("injection-target"); - - while(itor.hasNext()) - { - XmlParser.Node injectionNode = (XmlParser.Node)itor.next(); - String targetClassName = injectionNode.getString("injection-target-class", false, true); - String targetName = injectionNode.getString("injection-target-name", false, true); - if ((targetClassName==null) || targetClassName.equals("")) - { - Log.warn("No classname found in injection-target"); - continue; - } - if ((targetName==null) || targetName.equals("")) - { - Log.warn("No field or method name in injection-target"); - continue; - } - - InjectionCollection injections = (InjectionCollection)_context.getAttribute(InjectionCollection.INJECTION_COLLECTION); - // comments in the javaee_5.xsd file specify that the targetName is looked - // for first as a java bean property, then if that fails, as a field - try - { - Class clazz = _context.loadClass(targetClassName); - Injection injection = new Injection(); - injection.setJndiName(jndiName); - injection.setTarget(clazz, targetName, valueClass); - injections.add(injection); - } - catch (ClassNotFoundException e) - { - Log.warn("Couldn't load injection target class "+targetClassName); - } - } - } - } - - - - public void preConfigure (WebAppContext context) - throws Exception - { - //set up our special ServletHandler to remember injections and lifecycle callbacks - ServletHandler servletHandler = new ServletHandler(); - SecurityHandler securityHandler = context.getSecurityHandler(); - org.eclipse.jetty.servlet.ServletHandler existingHandler = context.getServletHandler(); - servletHandler.setFilters(existingHandler.getFilters()); - servletHandler.setFilterMappings(existingHandler.getFilterMappings()); - servletHandler.setServlets(existingHandler.getServlets()); - servletHandler.setServletMappings(existingHandler.getServletMappings()); - context.setServletHandler(servletHandler); - securityHandler.setHandler(servletHandler); - - LifeCycleCallbackCollection callbacks = new LifeCycleCallbackCollection(); - context.setAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION, callbacks); - InjectionCollection injections = new InjectionCollection(); - context.setAttribute(InjectionCollection.INJECTION_COLLECTION, injections); - RunAsCollection runAsCollection = new RunAsCollection(); - context.setAttribute(RunAsCollection.RUNAS_COLLECTION, runAsCollection); - } - - public void postConfigure(WebAppContext context) throws Exception - { - context.setAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION, null); - context.setAttribute(InjectionCollection.INJECTION_COLLECTION, null); - context.setAttribute(RunAsCollection.RUNAS_COLLECTION, null); - } - - public void configure (WebAppContext context) - throws Exception - { - bindUserTransaction(context); - - WebXmlProcessor webXmlProcessor = (WebXmlProcessor)context.getAttribute(WebXmlProcessor.WEB_PROCESSOR); - if (webXmlProcessor == null) - throw new IllegalStateException ("No processor for web xml"); - - //TODO: When webdefaults.xml, web.xml, fragments and web-override.xml are merged into an effective web.xml this - //will change - PlusWebXmlProcessor plusProcessor = new PlusWebXmlProcessor(context); - plusProcessor.process(webXmlProcessor.getWebDefault()); - plusProcessor.process(webXmlProcessor.getWebXml()); - - //Process plus-elements of each descriptor - for (Descriptor frag: webXmlProcessor.getFragments()) - { - plusProcessor.process(frag); - } - - //process the override-web.xml descriptor - plusProcessor.process(webXmlProcessor.getOverrideWeb()); - - - //configure injections and callbacks to be called by the FilterHolder and ServletHolder - //when they lazily instantiate the Filter/Servlet. - ((ServletHandler)context.getServletHandler()).setInjections((InjectionCollection)context.getAttribute(InjectionCollection.INJECTION_COLLECTION)); - ((ServletHandler)context.getServletHandler()).setCallbacks((LifeCycleCallbackCollection)context.getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION)); - - //do any injects on the listeners that were created and then - //also callback any postConstruct lifecycle methods - injectAndCallPostConstructCallbacks(context); - } - - public void deconfigure (WebAppContext context) - throws Exception - { - //call any preDestroy methods on the listeners - callPreDestroyCallbacks(context); - } - - - - protected void injectAndCallPostConstructCallbacks(WebAppContext context) - throws Exception - { - InjectionCollection injections = (InjectionCollection)context.getAttribute(InjectionCollection.INJECTION_COLLECTION); - RunAsCollection runAsCollection = (RunAsCollection)context.getAttribute(RunAsCollection.RUNAS_COLLECTION); - LifeCycleCallbackCollection callbacks = (LifeCycleCallbackCollection)context.getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION); - SecurityHandler securityHandler = context.getSecurityHandler(); - - //look thru the servlets to apply any runAs annotations - //NOTE: that any run-as in web.xml will already have been applied - if (runAsCollection != null) - { - ServletHolder[] holders = context.getServletHandler().getServlets(); - for (int i=0;holders!=null && i element, because you can get + //type from the element, but what to do if there is more + //than one element, do you just pick the type + //of the first one? + addInjections (descriptor, node, name, TypeUtil.fromName(type)); + Object value = TypeUtil.valueOf(type,valueStr); + bindEnvEntry(name, value); + break; + } + case WebXml: + case WebDefaults: + case WebOverride: + { + //ServletSpec 3.0 p75. web.xml (or web-override/web-defaults) declared + //the env-entry. A fragment is not allowed to change that, except unless + //the web.xml did not declare any injections. + if (!(descriptor instanceof FragmentDescriptor)) + { + //We're processing web-defaults, web.xml or web-override. Any of them can + //set or change the env-entry. + _metaData.setOrigin("env-entry."+name, descriptor); + addInjections (descriptor, node, name, TypeUtil.fromName(type)); + Object value = TypeUtil.valueOf(type,valueStr); + bindEnvEntry(name, value); + } + else + { + //A web.xml declared the env-entry. Check to see if any injections have been + //declared for it. If it was declared in web.xml then don't merge any injections. + //If it was declared in a web-fragment, then we can keep merging fragments. + Descriptor d = _metaData.getOriginDescriptor("env-entry."+name+".injection"); + if (d==null || d instanceof FragmentDescriptor) + addInjections(descriptor, node, name, TypeUtil.fromName(type)); + } + break; + } + case WebFragment: + { + //ServletSpec p.75. No declaration in web.xml, but in multiple web-fragments. Error. + throw new IllegalStateException("Conflicting env-entry "+name+" in "+descriptor.getResource()); + } + } + } + + + /** + * Common Annotations Spec section 2.3: + * resource-ref is for: + * - javax.sql.DataSource + * - javax.jms.ConnectionFactory + * - javax.jms.QueueConnectionFactory + * - javax.jms.TopicConnectionFactory + * - javax.mail.Session + * - java.net.URL + * - javax.resource.cci.ConnectionFactory + * - org.omg.CORBA_2_3.ORB + * - any other connection factory defined by a resource adapter + * + * TODO + * If web.xml contains a resource-ref with injection targets, all resource-ref entries + * of the same name are ignored in web fragments. If web.xml does not contain any + * injection-targets, then they are merged from all the fragments. + * If web.xml does not contain a resource-ref element of same name, but 2 fragments + * declare the same name it is an error. + * resource-ref entries are ONLY for connection factories + * the resource-ref says how the app will reference the jndi lookup relative + * to java:comp/env, but it is up to the deployer to map this reference to + * a real resource in the environment. At the moment, we insist that the + * jetty.xml file name of the resource has to be exactly the same as the + * name in web.xml deployment descriptor, but it shouldn't have to be + * + * @param node + * @throws Exception + */ + public void visitResourceRef (Descriptor descriptor, XmlParser.Node node) + throws Exception + { + String jndiName = node.getString("res-ref-name",false,true); + String type = node.getString("res-type", false, true); + String auth = node.getString("res-auth", false, true); + String shared = node.getString("res-sharing-scope", false, true); + + Origin o = _metaData.getOrigin("resource-ref."+jndiName); + switch (o) + { + case NotSet: + { + //No descriptor or annotation previously declared a resource-ref of this name. + _metaData.setOrigin("resource-ref."+jndiName, descriptor); + + //check for elements + Class typeClass = TypeUtil.fromName(type); + if (typeClass==null) + typeClass = _context.loadClass(type); + addInjections(descriptor, node, jndiName, typeClass); + bindResourceRef(jndiName, typeClass); + break; + } + case WebXml: + case WebDefaults: + case WebOverride: + { + //A web xml previously declared the resource-ref. + if (!(descriptor instanceof FragmentDescriptor)) + { + //We're processing web-defaults, web.xml or web-override. Any of them can + //set or change the resource-ref. + _metaData.setOrigin("resource-ref."+jndiName, descriptor); + + //check for elements + Class typeClass = TypeUtil.fromName(type); + if (typeClass==null) + typeClass = _context.loadClass(type); + + addInjections(descriptor, node, jndiName, typeClass); + + //bind the entry into jndi + bindResourceRef(jndiName, typeClass); + } + else + { + //A web xml declared the resource-ref and we're processing a + //web-fragment. Check to see if any injections were declared for it by web.xml. + //If any injection was declared in web.xml then don't merge any injections. + //If it was declared in a web-fragment, then we can keep merging fragments. + Descriptor d = _metaData.getOriginDescriptor("resource-ref."+jndiName+".injection"); + if (d==null || d instanceof FragmentDescriptor) + { + Class typeClass = TypeUtil.fromName(type); + if (typeClass==null) + typeClass = _context.loadClass(type); + addInjections(descriptor, node, jndiName, TypeUtil.fromName(type)); + } + } + break; + } + case WebFragment: + { + //ServletSpec p.75. No declaration of resource-ref in web xml, but in multiple web-fragments. Error. + throw new IllegalStateException("Conflicting resource-ref "+jndiName+" in "+descriptor.getResource()); + } + } + + } + + + /** + * Common Annotations Spec section 2.3: + * resource-env-ref is for: + * - javax.transaction.UserTransaction + * - javax.resource.cci.InteractionSpec + * - anything else that is not a connection factory + * + * @param node + * @throws Exception + */ + public void visitResourceEnvRef (Descriptor descriptor, XmlParser.Node node) + throws Exception + { + String jndiName = node.getString("resource-env-ref-name",false,true); + String type = node.getString("resource-env-ref-type", false, true); + + Origin o = _metaData.getOrigin("resource-env-ref."+jndiName); + switch (o) + { + case NotSet: + { + //First declaration of resource-env-ref with this jndiName + //JavaEE Spec sec 5.7.1.3 says the resource-env-ref-type + //is mandatory, but the schema says it is optional! + Class typeClass = TypeUtil.fromName(type); + if (typeClass==null) + typeClass = _context.loadClass(type); + addInjections (descriptor, node, jndiName, typeClass); + bindResourceEnvRef(jndiName, typeClass); + break; + } + case WebXml: + case WebDefaults: + case WebOverride: + { + //A resource-env-ref of this name has been declared first in a web xml. + //Only allow other web-default, web.xml, web-override to change it. + if (!(descriptor instanceof FragmentDescriptor)) + { + //We're processing web-defaults, web.xml or web-override. Any of them can + //set or change the resource-env-ref. + _metaData.setOrigin("resource-env-ref."+jndiName, descriptor); + Class typeClass = TypeUtil.fromName(type); + if (typeClass==null) + typeClass = _context.loadClass(type); + addInjections (descriptor, node, jndiName, typeClass); + bindResourceEnvRef(jndiName, typeClass); + } + else + { + //We're processing a web-fragment. It can only contribute injections if the + //there haven't been any injections declared yet, or they weren't declared in a WebXml file. + Descriptor d = _metaData.getOriginDescriptor("resource-env-ref."+jndiName+".injection"); + if (d == null || d instanceof FragmentDescriptor) + { + Class typeClass = TypeUtil.fromName(type); + if (typeClass==null) + typeClass = _context.loadClass(type); + addInjections (descriptor, node, jndiName, typeClass); + } + } + break; + } + case WebFragment: + { + //ServletSpec p.75. No declaration of resource-env-ref in web xml, but in multiple web-fragments. Error. + throw new IllegalStateException("Conflicting resource-env-ref "+jndiName+" in "+descriptor.getResource()); + } + } + } + + + /** + * Common Annotations Spec section 2.3: + * message-destination-ref is for: + * - javax.jms.Queue + * - javax.jms.Topic + * @param node + * @throws Exception + */ + public void visitMessageDestinationRef (Descriptor descriptor, XmlParser.Node node) + throws Exception + { + String jndiName = node.getString("message-destination-ref-name",false,true); + String type = node.getString("message-destination-type",false,true); + String usage = node.getString("message-destination-usage",false,true); + + Origin o = _metaData.getOrigin("message-destination-ref."+jndiName); + switch (o) + { + case NotSet: + { + //A message-destination-ref of this name has not been previously declared + Class typeClass = TypeUtil.fromName(type); + if (typeClass==null) + typeClass = _context.loadClass(type); + addInjections(descriptor, node, jndiName, typeClass); + bindMessageDestinationRef(jndiName, typeClass); + _metaData.setOrigin("message-destination-ref."+jndiName, descriptor); + break; + } + case WebXml: + case WebDefaults: + case WebOverride: + { + //A message-destination-ref of this name has been declared first in a web xml. + //Only allow other web-default, web.xml, web-override to change it. + if (!(descriptor instanceof FragmentDescriptor)) + { + Class typeClass = TypeUtil.fromName(type); + if (typeClass==null) + typeClass = _context.loadClass(type); + addInjections(descriptor, node, jndiName, typeClass); + bindMessageDestinationRef(jndiName, typeClass); + _metaData.setOrigin("message-destination-ref."+jndiName, descriptor); + } + else + { + //A web-fragment has declared a message-destination-ref with the same name as a web xml. + //It can only contribute injections, and only if the web xml didn't declare any. + Descriptor d = _metaData.getOriginDescriptor("message-destination-ref."+jndiName+".injection"); + if (d == null || d instanceof FragmentDescriptor) + { + Class typeClass = TypeUtil.fromName(type); + if (typeClass==null) + typeClass = _context.loadClass(type); + addInjections(descriptor, node, jndiName, typeClass); + } + } + break; + } + case WebFragment: + { + //ServletSpec p.75. No declaration of message-destination-ref in web xml, but in multiple web-fragments. Error. + throw new IllegalStateException("Conflicting message-destination-ref "+jndiName+" in "+descriptor.getResource()); + } + } + + } + + + + /** + * + * TODO + * If web.xml has at least 1 post-construct, then all post-constructs in fragments + * are ignored. Otherwise, post-constructs from fragments are merged. + * post-construct is the name of a class and method to call after all + * resources have been setup but before the class is put into use + * @param node + */ + public void visitPostConstruct(Descriptor descriptor, XmlParser.Node node) + { + String className = node.getString("lifecycle-callback-class", false, true); + String methodName = node.getString("lifecycle-callback-method", false, true); + + if (className==null || className.equals("")) + { + Log.warn("No lifecycle-callback-class specified"); + return; + } + if (methodName==null || methodName.equals("")) + { + Log.warn("No lifecycle-callback-method specified for class "+className); + return; + } + LifeCycleCallbackCollection callbacks = (LifeCycleCallbackCollection)_context.getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION); + //ServletSpec 3.0 p80 If web.xml declares a post-construct then all post-constructs + //in fragments must be ignored. Otherwise, they are additive. + Origin o = _metaData.getOrigin("post-construct"); + switch (o) + { + case NotSet: + { + //No post-constructs have been declared previously. + _metaData.setOrigin("post-construct", descriptor); + + try + { + Class clazz = _context.loadClass(className); + LifeCycleCallback callback = new PostConstructCallback(); + callback.setTarget(clazz, methodName); + callbacks.add(callback); + } + catch (ClassNotFoundException e) + { + Log.warn("Couldn't load post-construct target class "+className); + } + break; + } + case WebXml: + case WebDefaults: + case WebOverride: + { + //A web xml first declared a post-construct. Only allow other web xml files (web-defaults, web-overrides etc) + //to add to it + if (!(descriptor instanceof FragmentDescriptor)) + { + try + { + Class clazz = _context.loadClass(className); + LifeCycleCallback callback = new PostConstructCallback(); + callback.setTarget(clazz, methodName); + callbacks.add(callback); + } + catch (ClassNotFoundException e) + { + Log.warn("Couldn't load post-construct target class "+className); + } + } + break; + } + case WebFragment: + { + //A web-fragment first declared a post-construct. Allow all other web-fragments to merge in their post-constructs + try + { + Class clazz = _context.loadClass(className); + LifeCycleCallback callback = new PostConstructCallback(); + callback.setTarget(clazz, methodName); + callbacks.add(callback); + } + catch (ClassNotFoundException e) + { + Log.warn("Couldn't load post-construct target class "+className); + } + break; + } + } + + } + + + /** + * + * pre-destroy is the name of a class and method to call just as + * the instance is being destroyed + * @param node + */ + public void visitPreDestroy(Descriptor descriptor, XmlParser.Node node) + { + String className = node.getString("lifecycle-callback-class", false, true); + String methodName = node.getString("lifecycle-callback-method", false, true); + if (className==null || className.equals("")) + { + Log.warn("No lifecycle-callback-class specified for pre-destroy"); + return; + } + if (methodName==null || methodName.equals("")) + { + Log.warn("No lifecycle-callback-method specified for pre-destroy class "+className); + return; + } + LifeCycleCallbackCollection callbacks = (LifeCycleCallbackCollection)_context.getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION); + + Origin o = _metaData.getOrigin("pre-destroy"); + switch(o) + { + case NotSet: + { + //No pre-destroys have been declared previously. Record this descriptor + //as the first declarer. + _metaData.setOrigin("pre-destroy", descriptor); + try + { + Class clazz = _context.loadClass(className); + LifeCycleCallback callback = new PreDestroyCallback(); + callback.setTarget(clazz, methodName); + callbacks.add(callback); + } + catch (ClassNotFoundException e) + { + Log.warn("Couldn't load pre-destory target class "+className); + } + break; + } + case WebXml: + case WebDefaults: + case WebOverride: + { + //A web xml file previously declared a pre-destroy. Only allow other web xml files + //(not web-fragments) to add to them. + if (!(descriptor instanceof FragmentDescriptor)) + { + try + { + Class clazz = _context.loadClass(className); + LifeCycleCallback callback = new PreDestroyCallback(); + callback.setTarget(clazz, methodName); + callbacks.add(callback); + } + catch (ClassNotFoundException e) + { + Log.warn("Couldn't load pre-destory target class "+className); + } + } + break; + } + case WebFragment: + { + //No pre-destroys in web xml, so allow all fragments to merge their pre-destroys. + try + { + Class clazz = _context.loadClass(className); + LifeCycleCallback callback = new PreDestroyCallback(); + callback.setTarget(clazz, methodName); + callbacks.add(callback); + } + catch (ClassNotFoundException e) + { + Log.warn("Couldn't load pre-destory target class "+className); + } + break; + } + } + } + + + /** + * Iterate over the <injection-target> entries for a node + * + * @param descriptor + * @param node + * @param jndiName + * @param valueClass + * @return + */ + public void addInjections (Descriptor descriptor, XmlParser.Node node, String jndiName, Class valueClass) + { + Iterator itor = node.iterator("injection-target"); + + while(itor.hasNext()) + { + XmlParser.Node injectionNode = (XmlParser.Node)itor.next(); + String targetClassName = injectionNode.getString("injection-target-class", false, true); + String targetName = injectionNode.getString("injection-target-name", false, true); + if ((targetClassName==null) || targetClassName.equals("")) + { + Log.warn("No classname found in injection-target"); + continue; + } + if ((targetName==null) || targetName.equals("")) + { + Log.warn("No field or method name in injection-target"); + continue; + } + + InjectionCollection injections = (InjectionCollection)_context.getAttribute(InjectionCollection.INJECTION_COLLECTION); + // comments in the javaee_5.xsd file specify that the targetName is looked + // for first as a java bean property, then if that fails, as a field + try + { + Class clazz = _context.loadClass(targetClassName); + Injection injection = new Injection(); + injection.setJndiName(jndiName); + injection.setTarget(clazz, targetName, valueClass); + injections.add(injection); + + //Record which was the first descriptor to declare an injection for this name + if (_metaData.getOriginDescriptor(node.getTag()+"."+jndiName+".injection") == null) + _metaData.setOrigin(node.getTag()+"."+jndiName+".injection", descriptor); + } + catch (ClassNotFoundException e) + { + Log.warn("Couldn't load injection target class "+targetClassName); + } + } + } + + + + + /** + * @see org.eclipse.jetty.plus.webapp.AbstractConfiguration#bindEnvEntry(java.lang.String, java.lang.String) + * @param name + * @param value + * @throws Exception + */ + public void bindEnvEntry(String name, Object value) throws Exception + { + InitialContext ic = null; + boolean bound = false; + //check to see if we bound a value and an EnvEntry with this name already + //when we processed the server and the webapp's naming environment + //@see EnvConfiguration.bindEnvEntries() + ic = new InitialContext(); + try + { + NamingEntry ne = (NamingEntry)ic.lookup("java:comp/env/"+NamingEntryUtil.makeNamingEntryName(ic.getNameParser(""), name)); + if (ne!=null && ne instanceof EnvEntry) + { + EnvEntry ee = (EnvEntry)ne; + bound = ee.isOverrideWebXml(); + } + } + catch (NameNotFoundException e) + { + bound = false; + } + + if (!bound) + { + //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); + } + } + + /** + * Bind a resource reference. + * + * If a resource reference with the same name is in a jetty-env.xml + * file, it will already have been bound. + * + * @see org.eclipse.jetty.plus.webapp.AbstractConfiguration#bindResourceRef(java.lang.String) + * @param name + * @throws Exception + */ + public void bindResourceRef(String name, Class typeClass) + throws Exception + { + bindEntry(name, typeClass); + } + + /** + * @see org.eclipse.jetty.plus.webapp.AbstractConfiguration#bindResourceEnvRef(java.lang.String) + * @param name + * @throws Exception + */ + public void bindResourceEnvRef(String name, Class typeClass) + throws Exception + { + bindEntry(name, typeClass); + } + + + public void bindMessageDestinationRef(String name, Class typeClass) + throws Exception + { + bindEntry(name, typeClass); + } + + + /** + * Bind a resource with the given name from web.xml of the given type + * with a jndi resource from either the server or the webapp's naming + * environment. + * + * As the servlet spec does not cover the mapping of names in web.xml with + * names from the execution environment, jetty uses the concept of a Link, which is + * a subclass of the NamingEntry class. A Link defines a mapping of a name + * from web.xml with a name from the execution environment (ie either the server or the + * webapp's naming environment). + * + * @param name name of the resource from web.xml + * @param typeClass + * @throws Exception + */ + protected void bindEntry (String name, Class typeClass) + throws Exception + { + String nameInEnvironment = name; + boolean bound = false; + + //check if the name in web.xml has been mapped to something else + //check a context-specific naming environment first + Object scope = _context; + NamingEntry ne = NamingEntryUtil.lookupNamingEntry(scope, name); + + if (ne!=null && (ne instanceof Link)) + { + //if we found a mapping, get out name it is mapped to in the environment + nameInEnvironment = (String)((Link)ne).getObjectToBind(); + Link l = (Link)ne; + } + + //try finding that mapped name in the webapp's environment first + scope = _context; + bound = NamingEntryUtil.bindToENC(scope, name, nameInEnvironment); + + if (bound) + return; + + //try the server's environment + scope = _context.getServer(); + bound = NamingEntryUtil.bindToENC(scope, name, nameInEnvironment); + if (bound) + return; + + //try the jvm environment + bound = NamingEntryUtil.bindToENC(null, name, nameInEnvironment); + if (bound) + return; + + //There is no matching resource so try a default name. + //The default name syntax is: the [res-type]/default + //eg javax.sql.DataSource/default + nameInEnvironment = typeClass.getName()+"/default"; + //First try the server scope + NamingEntry defaultNE = NamingEntryUtil.lookupNamingEntry(_context.getServer(), nameInEnvironment); + if (defaultNE==null) + defaultNE = NamingEntryUtil.lookupNamingEntry(null, nameInEnvironment); + + if (defaultNE!=null) + defaultNE.bindToENC(name); + else + throw new IllegalStateException("Nothing to bind for name "+nameInEnvironment); + } + + +} diff --git a/jetty-plus/src/main/java/org/eclipse/jetty/plus/webapp/WebAppDecorator.java b/jetty-plus/src/main/java/org/eclipse/jetty/plus/webapp/WebAppDecorator.java new file mode 100644 index 00000000000..d499dafe7ea --- /dev/null +++ b/jetty-plus/src/main/java/org/eclipse/jetty/plus/webapp/WebAppDecorator.java @@ -0,0 +1,164 @@ +// ======================================================================== +// Copyright (c) 2006-2009 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// 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.plus.webapp; + +import java.util.EventListener; + +import javax.servlet.Filter; +import javax.servlet.Servlet; +import javax.servlet.ServletException; + +import org.eclipse.jetty.plus.annotation.InjectionCollection; +import org.eclipse.jetty.plus.annotation.LifeCycleCallbackCollection; +import org.eclipse.jetty.plus.annotation.RunAsCollection; +import org.eclipse.jetty.servlet.FilterHolder; +import org.eclipse.jetty.servlet.Holder; +import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.servlet.ServletContextHandler.Decorator; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.webapp.WebAppContext; + +/** + * WebAppDecorator + * + * + */ +public class WebAppDecorator implements Decorator +{ + private WebAppContext _wac; + + public WebAppDecorator (WebAppContext context) + { + _wac = context; + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.servlet.ServletContextHandler.Decorator#decorateFilterHolder(org.eclipse.jetty.servlet.FilterHolder) + */ + public void decorateFilterHolder(FilterHolder filter) throws ServletException + { + // TODO Auto-generated method stub + + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.servlet.ServletContextHandler.Decorator#decorateFilterInstance(javax.servlet.Filter) + */ + public T decorateFilterInstance(T filter) throws ServletException + { + decorate(filter); + return filter; + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.servlet.ServletContextHandler.Decorator#decorateListenerInstance(java.util.EventListener) + */ + public T decorateListenerInstance(T listener) throws ServletException + { + decorate(listener); + return listener; + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.servlet.ServletContextHandler.Decorator#decorateServletHolder(org.eclipse.jetty.servlet.ServletHolder) + */ + public void decorateServletHolder(ServletHolder holder) throws ServletException + { + decorate(holder); + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.servlet.ServletContextHandler.Decorator#decorateServletInstance(javax.servlet.Servlet) + */ + public T decorateServletInstance(T servlet) throws ServletException + { + decorate(servlet); + return servlet; + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.servlet.ServletContextHandler.Decorator#destroyFilterInstance(javax.servlet.Filter) + */ + public void destroyFilterInstance(Filter f) + { + destroy(f); + } + + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.servlet.ServletContextHandler.Decorator#destroyServletInstance(javax.servlet.Servlet) + */ + public void destroyServletInstance(Servlet s) + { + destroy(s); + } + + /** + * @see org.eclipse.jetty.servlet.ServletContextHandler.Decorator#destroyListenerInstance(java.util.EventListener) + */ + public void destroyListenerInstance(EventListener l) + { + destroy(l); + } + + + protected void decorate (Object o) + throws ServletException + { + InjectionCollection injections = (InjectionCollection)_wac.getAttribute(InjectionCollection.INJECTION_COLLECTION); + LifeCycleCallbackCollection callbacks = (LifeCycleCallbackCollection)_wac.getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION); + RunAsCollection runAses = (RunAsCollection)_wac.getAttribute(RunAsCollection.RUNAS_COLLECTION); + + if (runAses != null) + runAses.setRunAs(o); + + if (injections != null) + injections.inject(o); + + if (callbacks != null) + { + try + { + callbacks.callPostConstructCallback(o); + } + catch (Exception e) + { + throw new ServletException(e); + } + } + } + + protected void destroy (Object o) + { + LifeCycleCallbackCollection callbacks = (LifeCycleCallbackCollection)_wac.getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION); + if (callbacks != null) + { + try + { + callbacks.callPreDestroyCallback(o); + } + catch (Exception e) + { + Log.warn("Destroying instance of "+o.getClass(), e); + } + } + } +} diff --git a/jetty-plus/src/test/java/org/eclipse/jetty/plus/webapp/TestConfiguration.java b/jetty-plus/src/test/java/org/eclipse/jetty/plus/webapp/TestConfiguration.java index 0abd5e57263..e96b69008d6 100644 --- a/jetty-plus/src/test/java/org/eclipse/jetty/plus/webapp/TestConfiguration.java +++ b/jetty-plus/src/test/java/org/eclipse/jetty/plus/webapp/TestConfiguration.java @@ -21,6 +21,7 @@ import org.eclipse.jetty.plus.jndi.NamingEntryUtil; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.webapp.WebAppClassLoader; import org.eclipse.jetty.webapp.WebAppContext; +import org.eclipse.jetty.webapp.MetaData; import org.junit.Test; import static org.junit.Assert.assertEquals; @@ -50,6 +51,10 @@ public class TestConfiguration WebAppContext wac = new MyWebAppContext(); wac.setServer(server); wac.setClassLoader(new WebAppClassLoader(Thread.currentThread().getContextClassLoader(), wac)); + + MetaData metaData = new MetaData(wac); + + PlusDescriptorProcessor plusProcessor = new PlusDescriptorProcessor(); //bind some EnvEntrys at the server level EnvEntry ee1 = new EnvEntry(server, "xxx/a", "100", true); @@ -73,8 +78,7 @@ public class TestConfiguration assertNotNull(NamingEntryUtil.lookupNamingEntry(wac, "zzz/c")); assertNotNull(NamingEntryUtil.lookupNamingEntry(wac, "zzz/e")); - Configuration config = new Configuration(); - + //make a new env configuration EnvConfiguration envConfig = new EnvConfiguration(); @@ -105,20 +109,20 @@ public class TestConfiguration ne = (NamingEntry)ic.lookup("java:comp/env/"+NamingEntry.__contextName+"/zzz/e"); assertNotNull(ne); - config.bindEnvEntry(wac, "foo", "99"); + plusProcessor.bindEnvEntry("foo", "99"); assertEquals("99",ic.lookup( "java:comp/env/foo")); - config.bindEnvEntry(wac, "xxx/a", "7"); + plusProcessor.bindEnvEntry("xxx/a", "7"); assertEquals("900", ic.lookup("java:comp/env/xxx/a")); //webapp overrides web.xml - config.bindEnvEntry(wac, "yyy/b", "7"); + plusProcessor.bindEnvEntry("yyy/b", "7"); assertEquals("910", ic.lookup("java:comp/env/yyy/b"));//webapp overrides web.xml - config.bindEnvEntry(wac,"zzz/c", "7"); + plusProcessor.bindEnvEntry("zzz/c", "7"); assertEquals("7", ic.lookup("java:comp/env/zzz/c"));//webapp does NOT override web.xml - config.bindEnvEntry(wac,"zzz/d", "7"); + plusProcessor.bindEnvEntry("zzz/d", "7"); assertEquals("7", ic.lookup("java:comp/env/zzz/d"));//server does NOT override web.xml - config.bindEnvEntry(wac,"zzz/e", "7"); + plusProcessor.bindEnvEntry("zzz/e", "7"); assertEquals("7", ic.lookup("java:comp/env/zzz/e"));//webapp does NOT override web.xml - config.bindEnvEntry(wac,"zzz/f", "7"); + plusProcessor.bindEnvEntry("zzz/f", "7"); assertEquals("500", ic.lookup("java:comp/env/zzz/f"));//server overrides web.xml ((Context)ic.lookup("java:comp")).destroySubcontext("env"); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java index c69732ebf96..50fd577e449 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java @@ -1269,7 +1269,14 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. _localeEncodingMap=new HashMap(); _localeEncodingMap.put(locale, encoding); } - + + public String getLocaleEncoding (String locale) + { + if (_localeEncodingMap==null) + return null; + String encoding = _localeEncodingMap.get(locale); + return encoding; + } /* ------------------------------------------------------------ */ /** * Get the character encoding for a locale. The full locale name is first diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/FilterHolder.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/FilterHolder.java index b52c698c623..9208b5f46f2 100644 --- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/FilterHolder.java +++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/FilterHolder.java @@ -13,8 +13,17 @@ package org.eclipse.jetty.servlet; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.EnumSet; +import java.util.List; + +import org.eclipse.jetty.server.DispatcherType; import javax.servlet.Filter; import javax.servlet.FilterConfig; +import org.eclipse.jetty.servlet.api.FilterRegistration; +import javax.servlet.ServletException; import org.eclipse.jetty.util.log.Log; @@ -22,25 +31,26 @@ import org.eclipse.jetty.util.log.Log; /** * */ -public class FilterHolder extends Holder +public class FilterHolder extends Holder { /* ------------------------------------------------------------ */ private transient Filter _filter; private transient Config _config; - + /* ---------------------------------------------------------------- */ - /** Constructor for Serialization. + /** Constructor */ public FilterHolder() { } + /* ---------------------------------------------------------------- */ - /** Constructor for Serialization. + /** Constructor */ - public FilterHolder(Class filter) + public FilterHolder(Class filter) { - super (filter); + setHeldClass(filter); } /* ---------------------------------------------------------------- */ @@ -67,9 +77,22 @@ public class FilterHolder extends Holder } if (_filter==null) - _filter=(Filter)newInstance(); + { + try + { + _filter=((ServletContextHandler.Context)_servletHandler.getServletContext()).createFilter(getHeldClass()); + } + catch (ServletException se) + { + Throwable cause = se.getRootCause(); + if (cause instanceof InstantiationException) + throw (InstantiationException)cause; + if (cause instanceof IllegalAccessException) + throw (IllegalAccessException)cause; + throw se; + } + } - _filter = getServletHandler().customizeFilter(_filter); _config=new Config(); _filter.init(_config); } @@ -100,13 +123,13 @@ public class FilterHolder extends Holder /* ------------------------------------------------------------ */ @Override public void destroyInstance (Object o) - throws Exception + throws Exception { if (o==null) return; Filter f = (Filter)o; f.destroy(); - getServletHandler().customizeFilterDestroy(f); + getServletHandler().destroyFilter(f); } /* ------------------------------------------------------------ */ @@ -132,6 +155,74 @@ public class FilterHolder extends Holder return getName(); } + /* ------------------------------------------------------------ */ + public FilterRegistration.Dynamic getRegistration() + { + return new Registration(); + } + + /* ------------------------------------------------------------ */ + /* ------------------------------------------------------------ */ + /* ------------------------------------------------------------ */ + protected class Registration extends HolderRegistration implements FilterRegistration.Dynamic + { + public void addMappingForServletNames(EnumSet dispatcherTypes, boolean isMatchAfter, String... servletNames) + { + illegalStateIfContextStarted(); + FilterMapping mapping = new FilterMapping(); + mapping.setFilterHolder(FilterHolder.this); + mapping.setServletNames(servletNames); + mapping.setDispatcherTypes(dispatcherTypes); + if (isMatchAfter) + _servletHandler.addFilterMapping(mapping); + else + _servletHandler.prependFilterMapping(mapping); + } + + public void addMappingForUrlPatterns(EnumSet dispatcherTypes, boolean isMatchAfter, String... urlPatterns) + { + illegalStateIfContextStarted(); + FilterMapping mapping = new FilterMapping(); + mapping.setFilterHolder(FilterHolder.this); + mapping.setPathSpecs(urlPatterns); + mapping.setDispatcherTypes(dispatcherTypes); + if (isMatchAfter) + _servletHandler.addFilterMapping(mapping); + else + _servletHandler.prependFilterMapping(mapping); + } + + public Collection getServletNameMappings() + { + FilterMapping[] mappings =_servletHandler.getFilterMappings(); + List names=new ArrayList(); + for (FilterMapping mapping : mappings) + { + if (mapping.getFilterHolder()!=FilterHolder.this) + continue; + String[] servlets=mapping.getServletNames(); + if (servlets!=null && servlets.length>0) + names.addAll(Arrays.asList(servlets)); + } + return names; + } + + public Collection getUrlPatternMappings() + { + FilterMapping[] mappings =_servletHandler.getFilterMappings(); + List patterns=new ArrayList(); + for (FilterMapping mapping : mappings) + { + if (mapping.getFilterHolder()!=FilterHolder.this) + continue; + String[] specs=mapping.getPathSpecs(); + if (specs!=null && specs.length>0) + patterns.addAll(Arrays.asList(specs)); + } + return patterns; + } + } + /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/FilterMapping.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/FilterMapping.java index b0bb2fedc9a..cb1345d9652 100644 --- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/FilterMapping.java +++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/FilterMapping.java @@ -16,8 +16,9 @@ package org.eclipse.jetty.servlet; import java.util.Arrays; import java.util.EnumSet; -import org.eclipse.jetty.http.PathMap; import org.eclipse.jetty.server.DispatcherType; + +import org.eclipse.jetty.http.PathMap; import org.eclipse.jetty.server.Handler; @@ -36,18 +37,18 @@ public class FilterMapping /* ------------------------------------------------------------ */ /** Dispatch type from name */ - public static int dispatch(String type) + public static DispatcherType dispatch(String type) { if ("request".equalsIgnoreCase(type)) - return REQUEST; + return DispatcherType.REQUEST; if ("forward".equalsIgnoreCase(type)) - return FORWARD; + return DispatcherType.FORWARD; if ("include".equalsIgnoreCase(type)) - return INCLUDE; + return DispatcherType.INCLUDE; if ("error".equalsIgnoreCase(type)) - return ERROR; + return DispatcherType.ERROR; if ("async".equalsIgnoreCase(type)) - return ASYNC; + return DispatcherType.ASYNC; throw new IllegalArgumentException(type); } @@ -71,28 +72,6 @@ public class FilterMapping } throw new IllegalArgumentException(type.toString()); } - - - /* ------------------------------------------------------------ */ - /** Dispatch type from name - */ - public static DispatcherType dispatch(int type) - { - switch(type) - { - case REQUEST: - return DispatcherType.REQUEST; - case ASYNC: - return DispatcherType.ASYNC; - case FORWARD: - return DispatcherType.FORWARD; - case INCLUDE: - return DispatcherType.INCLUDE; - case ERROR: - return DispatcherType.ERROR; - } - throw new IllegalArgumentException(""+type); - } /* ------------------------------------------------------------ */ @@ -140,15 +119,6 @@ public class FilterMapping return (_dispatches&type)!=0; } - /* ------------------------------------------------------------ */ - /** - * @return Returns the dispatches. - */ - public int getDispatches() - { - return _dispatches; - } - /* ------------------------------------------------------------ */ /** * @return Returns the filterName. @@ -180,16 +150,23 @@ public class FilterMapping public void setDispatcherTypes(EnumSet dispatcherTypes) { _dispatches=DEFAULT; - if (dispatcherTypes.contains(DispatcherType.ERROR)) - _dispatches|=ERROR; - if (dispatcherTypes.contains(DispatcherType.FORWARD)) - _dispatches|=FORWARD; - if (dispatcherTypes.contains(DispatcherType.INCLUDE)) - _dispatches|=INCLUDE; - if (dispatcherTypes.contains(DispatcherType.REQUEST)) - _dispatches|=REQUEST; + if (dispatcherTypes!=null) + { + if (dispatcherTypes.contains(DispatcherType.ERROR)) + _dispatches|=ERROR; + if (dispatcherTypes.contains(DispatcherType.FORWARD)) + _dispatches|=FORWARD; + if (dispatcherTypes.contains(DispatcherType.INCLUDE)) + _dispatches|=INCLUDE; + if (dispatcherTypes.contains(DispatcherType.REQUEST)) + _dispatches|=REQUEST; + if (dispatcherTypes.contains(DispatcherType.ASYNC)) + _dispatches|=ASYNC; + } } + + /* ------------------------------------------------------------ */ /** * @param dispatches The dispatches to set. diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/Holder.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/Holder.java index 8a9172725a8..8a8e709194d 100644 --- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/Holder.java +++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/Holder.java @@ -16,8 +16,11 @@ package org.eclipse.jetty.servlet; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; +import org.eclipse.jetty.servlet.api.Registration; import javax.servlet.ServletContext; import javax.servlet.UnavailableException; @@ -31,10 +34,10 @@ import org.eclipse.jetty.util.log.Log; /** * */ -public class Holder extends AbstractLifeCycle +public class Holder extends AbstractLifeCycle { + protected transient Class _class; protected final Map _initParams=new HashMap(3); - protected transient Class _class; protected String _className; protected String _displayName; protected boolean _extInstance; @@ -44,22 +47,20 @@ public class Holder extends AbstractLifeCycle protected String _name; protected ServletHandler _servletHandler; + /* ---------------------------------------------------------------- */ protected Holder() { - } - /* ---------------------------------------------------------------- */ - protected Holder(Class held) + /* ------------------------------------------------------------ */ + /** + * @return True if this holder was created for a specific instance. + */ + public boolean isInstance() { - _class=held; - if (held!=null) - { - _className=held.getName(); - _name=held.getName()+"-"+this.hashCode(); - } + return _extInstance; } - + /* ------------------------------------------------------------ */ public void doStart() throws Exception @@ -85,6 +86,7 @@ public class Holder extends AbstractLifeCycle } /* ------------------------------------------------------------ */ + @Override public void doStop() throws Exception { @@ -99,7 +101,7 @@ public class Holder extends AbstractLifeCycle } /* ------------------------------------------------------------ */ - public Class getHeldClass() + public Class getHeldClass() { return _class; } @@ -127,7 +129,7 @@ public class Holder extends AbstractLifeCycle } /* ---------------------------------------------------------------- */ - public Map getInitParameters() + public Map getInitParameters() { return _initParams; } @@ -147,16 +149,6 @@ public class Holder extends AbstractLifeCycle return _servletHandler; } - /* ------------------------------------------------------------ */ - public synchronized Object newInstance() - throws InstantiationException, - IllegalAccessException - { - if (_class==null) - throw new InstantiationException("!"+_className); - return _class.newInstance(); - } - /* ------------------------------------------------------------ */ public void destroyInstance(Object instance) throws Exception @@ -177,10 +169,15 @@ public class Holder extends AbstractLifeCycle /** * @param held The class to hold */ - public void setHeldClass(Class held) + public void setHeldClass(Class held) { _class=held; - _className = held!=null?held.getName():null; + if (held!=null) + { + _className=held.getName(); + if (_name==null) + _name=held.getName()+"-"+this.hashCode(); + } } /* ------------------------------------------------------------ */ @@ -276,6 +273,72 @@ public class Holder extends AbstractLifeCycle return Holder.this.getInitParameterNames(); } } + + /* -------------------------------------------------------- */ + /* -------------------------------------------------------- */ + /* -------------------------------------------------------- */ + protected class HolderRegistration implements Registration.Dynamic + { + public void setAsyncSupported(boolean isAsyncSupported) + { + illegalStateIfContextStarted(); + Holder.this.setAsyncSupported(isAsyncSupported); + } + + public void setDescription(String description) + { + if (Log.isDebugEnabled()) + Log.debug(this+" is "+description); + } + + public String getClassName() + { + return Holder.this.getClassName(); + } + + public String getInitParameter(String name) + { + return Holder.this.getInitParameter(name); + } + + public Map getInitParameters() + { + return Holder.this.getInitParameters(); + } + + public String getName() + { + return Holder.this.getName(); + } + + public boolean setInitParameter(String name, String value) + { + illegalStateIfContextStarted(); + if (Holder.this.getInitParameter(name)!=null) + return false; + Holder.this.setInitParameter(name,value); + return true; + } + + public Set setInitParameters(Map initParameters) + { + illegalStateIfContextStarted(); + Set clash=null; + for (String name : initParameters.keySet()) + { + if (Holder.this.getInitParameter(name)!=null) + { + if (clash==null) + clash=new HashSet(); + clash.add(name); + } + } + if (clash!=null) + return clash; + Holder.this.setInitParameters(initParameters); + return Collections.emptySet(); + }; + } } diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletContextHandler.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletContextHandler.java index 36cbc575a40..d76f9e277d8 100644 --- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletContextHandler.java +++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletContextHandler.java @@ -13,13 +13,26 @@ package org.eclipse.jetty.servlet; +import java.util.Arrays; +import java.util.Collections; import java.util.EnumSet; +import java.util.EventListener; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import javax.servlet.Filter; import javax.servlet.RequestDispatcher; import javax.servlet.Servlet; import javax.servlet.ServletContext; +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; +import javax.servlet.ServletException; +import org.eclipse.jetty.security.ConstraintAware; +import org.eclipse.jetty.security.ConstraintSecurityHandler; import org.eclipse.jetty.security.SecurityHandler; import org.eclipse.jetty.server.Dispatcher; import org.eclipse.jetty.server.DispatcherType; @@ -29,8 +42,10 @@ import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.ErrorHandler; import org.eclipse.jetty.server.handler.HandlerCollection; import org.eclipse.jetty.server.handler.HandlerWrapper; -import org.eclipse.jetty.server.handler.ScopedHandler; import org.eclipse.jetty.server.session.SessionHandler; +import org.eclipse.jetty.servlet.api.FilterRegistration; +import org.eclipse.jetty.servlet.api.ServletRegistration; +import org.eclipse.jetty.util.Loader; /* ------------------------------------------------------------ */ @@ -56,6 +71,10 @@ public class ServletContextHandler extends ContextHandler protected SecurityHandler _securityHandler; protected ServletHandler _servletHandler; protected int _options; + protected Decorator _decorator; + protected Object _restrictedContextListeners; + + protected final Set _created = Collections.newSetFromMap(new ConcurrentHashMap()); /* ------------------------------------------------------------ */ public ServletContextHandler() @@ -115,6 +134,42 @@ public class ServletContextHandler extends ContextHandler ((HandlerCollection)parent).addHandler(this); } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.server.handler.ContextHandler#doStart() + */ + @Override + protected void doStart() throws Exception + { + _created.clear(); + super.doStart(); + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.server.handler.ContextHandler#doStop() + */ + @Override + protected void doStop() throws Exception + { + super.doStop(); + _created.clear(); + } + + /* ------------------------------------------------------------ */ + /** + * Check if instance was created by a call to {@link ServletContext#createFilter(Class)}, + * {@link ServletContext#createServlet(Class)} or {@link ServletContext#createListener(Class)} + * @param instance Instance of {@link Servlet}, {@link Filter} or {@link EventListener} + * @return True if the instance was created by a call to {@link ServletContext#createFilter(Class)}, + * {@link ServletContext#createServlet(Class)} or {@link ServletContext#createListener(Class)} + */ + public boolean isCreatedInstance(Object instance) + { + return _created.contains(instance); + } + /* ------------------------------------------------------------ */ /** Get the defaultSecurityHandlerClass. * @return the defaultSecurityHandlerClass @@ -201,7 +256,19 @@ public class ServletContextHandler extends ContextHandler // OK to Initialize servlet handler now if (_servletHandler != null && _servletHandler.isStarted()) + { + if (_decorator!=null) + { + if (_servletHandler.getFilters()!=null) + for (FilterHolder holder:_servletHandler.getFilters()) + _decorator.decorateFilterHolder(holder); + if(_servletHandler.getServlets()!=null) + for (ServletHolder holder:_servletHandler.getServlets()) + _decorator.decorateServletHolder(holder); + } + _servletHandler.initialize(); + } } /* ------------------------------------------------------------ */ @@ -262,6 +329,31 @@ public class ServletContextHandler extends ContextHandler getServletHandler().addServletWithMapping(servlet, pathSpec); } + /* ------------------------------------------------------------ */ + /** conveniance method to add a filter + */ + public void addFilter(FilterHolder holder,String pathSpec,EnumSet dispatches) + { + getServletHandler().addFilterWithMapping(holder,pathSpec,dispatches); + } + + /* ------------------------------------------------------------ */ + /** convenience method to add a filter + */ + public FilterHolder addFilter(Class filterClass,String pathSpec,EnumSet dispatches) + { + return getServletHandler().addFilterWithMapping(filterClass,pathSpec,dispatches); + } + + /* ------------------------------------------------------------ */ + /** convenience method to add a filter + */ + public FilterHolder addFilter(String filterClass,String pathSpec,EnumSet dispatches) + { + return getServletHandler().addFilterWithMapping(filterClass,pathSpec,dispatches); + } + + /* ------------------------------------------------------------ */ /** conveniance method to add a filter */ @@ -286,6 +378,21 @@ public class ServletContextHandler extends ContextHandler return getServletHandler().addFilterWithMapping(filterClass,pathSpec,dispatches); } + + + public void callContextInitialized(ServletContextListener l, ServletContextEvent e) + { + l.contextInitialized(e); + } + + + public void callContextDestroyed(ServletContextListener l, ServletContextEvent e) + { + l.contextDestroyed(e); + } + + + /* ------------------------------------------------------------ */ /** * @param sessionHandler The sessionHandler to set. @@ -322,14 +429,46 @@ public class ServletContextHandler extends ContextHandler _servletHandler = servletHandler; } + /* ------------------------------------------------------------ */ + /** + * @return The decorator used to resource inject new Filters, Servlets and EventListeners + */ + public Decorator getDecorator() + { + return _decorator; + } + + /* ------------------------------------------------------------ */ + /** + * @param decorator The inject used to resource inject new Filters, Servlets and EventListeners + */ + public void setDecorator(Decorator decorator) + { + _decorator = decorator; + } + + /* ------------------------------------------------------------ */ + void destroyServlet(Servlet servlet) + { + if (_decorator!=null) + _decorator.destroyServletInstance(servlet); + } + + /* ------------------------------------------------------------ */ + void destroyFilter(Filter filter) + { + if (_decorator!=null) + _decorator.destroyFilterInstance(filter); + } + /* ------------------------------------------------------------ */ public class Context extends ContextHandler.Context { - /* ------------------------------------------------------------ */ /* * @see javax.servlet.ServletContext#getNamedDispatcher(java.lang.String) */ + @Override public RequestDispatcher getNamedDispatcher(String name) { ContextHandler context=org.eclipse.jetty.servlet.ServletContextHandler.this; @@ -337,33 +476,307 @@ public class ServletContextHandler extends ContextHandler return null; return new Dispatcher(context, name); } - - + /* ------------------------------------------------------------ */ - public void addFilterMappingForServletNames(String filterName, EnumSet dispatcherTypes, boolean isMatchAfter, String... servletNames) + /** + * @see javax.servlet.ServletContext#addFilter(java.lang.String, java.lang.Class) + */ + public FilterRegistration.Dynamic addFilter(String filterName, Class filterClass) { if (isStarted()) throw new IllegalStateException(); - ServletHandler handler = ServletContextHandler.this.getServletHandler(); - FilterMapping mapping = new FilterMapping(); - mapping.setFilterName(filterName); - mapping.setServletNames(servletNames); - mapping.setDispatcherTypes(dispatcherTypes); - handler.addFilterMapping(mapping); + + final ServletHandler handler = ServletContextHandler.this.getServletHandler(); + final FilterHolder holder= handler.newFilterHolder(); + holder.setName(filterName); + holder.setHeldClass(filterClass); + handler.addFilter(holder); + return holder.getRegistration(); } /* ------------------------------------------------------------ */ - public void addServletMapping(String servletName, String[] urlPatterns) + /** + * @see javax.servlet.ServletContext#addFilter(java.lang.String, java.lang.String) + */ + public FilterRegistration.Dynamic addFilter(String filterName, String className) { if (isStarted()) throw new IllegalStateException(); - ServletHandler handler = ServletContextHandler.this.getServletHandler(); - ServletHolder holder= handler.newServletHolder(); - holder.setName(servletName); - handler.addServlet(holder); + + final ServletHandler handler = ServletContextHandler.this.getServletHandler(); + final FilterHolder holder= handler.newFilterHolder(); + holder.setName(filterName); + holder.setClassName(className); + handler.addFilter(holder); + return holder.getRegistration(); + } + + + /* ------------------------------------------------------------ */ + /** + * @see javax.servlet.ServletContext#addFilter(java.lang.String, javax.servlet.Filter) + */ + public FilterRegistration.Dynamic addFilter(String filterName, Filter filter) + { + if (isStarted()) + throw new IllegalStateException(); + + final ServletHandler handler = ServletContextHandler.this.getServletHandler(); + final FilterHolder holder= handler.newFilterHolder(); + holder.setName(filterName); + holder.setFilter(filter); + handler.addFilter(holder); + return holder.getRegistration(); } + /* ------------------------------------------------------------ */ + /** + * @see javax.servlet.ServletContext#addServlet(java.lang.String, java.lang.Class) + */ + public ServletRegistration.Dynamic addServlet(String servletName, Class servletClass) + { + if (!isStarting()) + throw new IllegalStateException(); + final ServletHandler handler = ServletContextHandler.this.getServletHandler(); + final ServletHolder holder= handler.newServletHolder(); + holder.setName(servletName); + holder.setHeldClass(servletClass); + handler.addServlet(holder); + return holder.getRegistration(); + } + /* ------------------------------------------------------------ */ + /** + * @see javax.servlet.ServletContext#addServlet(java.lang.String, java.lang.String) + */ + public ServletRegistration.Dynamic addServlet(String servletName, String className) + { + if (!isStarting()) + throw new IllegalStateException(); + + final ServletHandler handler = ServletContextHandler.this.getServletHandler(); + final ServletHolder holder= handler.newServletHolder(); + holder.setName(servletName); + holder.setClassName(className); + handler.addServlet(holder); + return holder.getRegistration(); + } + + /* ------------------------------------------------------------ */ + public ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet) + { + if (!isStarting()) + throw new IllegalStateException(); + + final ServletHandler handler = ServletContextHandler.this.getServletHandler(); + final ServletHolder holder= handler.newServletHolder(); + holder.setName(servletName); + holder.setServlet(servlet); + handler.addServlet(holder); + return holder.getRegistration(); + } + + /* ------------------------------------------------------------ */ + public boolean setInitParameter(String name, String value) + { + // TODO other started conditions + if (!isStarting()) + throw new IllegalStateException(); + + return super.setInitParameter(name,value); + } + + /* ------------------------------------------------------------ */ + public T createFilter(Class c) throws ServletException + { + try + { + T f = c.newInstance(); + if (_decorator!=null) + f=_decorator.decorateFilterInstance(f); + _created.add(f); + return f; + } + catch (InstantiationException e) + { + throw new ServletException(e); + } + catch (IllegalAccessException e) + { + throw new ServletException(e); + } + } + + /* ------------------------------------------------------------ */ + public T createServlet(Class c) throws ServletException + { + try + { + T s = c.newInstance(); + if (_decorator!=null) + s=_decorator.decorateServletInstance(s); + _created.add(s); + return s; + } + catch (InstantiationException e) + { + throw new ServletException(e); + } + catch (IllegalAccessException e) + { + throw new ServletException(e); + } + } + + public FilterRegistration getFilterRegistration(String filterName) + { + final FilterHolder holder=ServletContextHandler.this.getServletHandler().getFilter(filterName); + return (holder==null)?null:holder.getRegistration(); + } + + + public Map getFilterRegistrations() + { + HashMap registrations = new HashMap(); + ServletHandler handler=ServletContextHandler.this.getServletHandler(); + FilterHolder[] holders=handler.getFilters(); + if (holders!=null) + { + for (FilterHolder holder : holders) + registrations.put(holder.getName(),holder.getRegistration()); + } + return registrations; + } + + + public ServletRegistration getServletRegistration(String servletName) + { + final ServletHolder holder=ServletContextHandler.this.getServletHandler().getServlet(servletName); + return (holder==null)?null:holder.getRegistration(); + } + + + public Map getServletRegistrations() + { + HashMap registrations = new HashMap(); + ServletHandler handler=ServletContextHandler.this.getServletHandler(); + ServletHolder[] holders=handler.getServlets(); + if (holders!=null) + { + for (ServletHolder holder : holders) + registrations.put(holder.getName(),holder.getRegistration()); + } + return registrations; + } + + + public void addListener(String className) + { + // TODO other started conditions + if (!isStarting()) + throw new IllegalStateException(); + try + { + Class clazz = getClassLoader()==null?Loader.loadClass(ContextHandler.class,className):getClassLoader().loadClass(className); + addListener(clazz); + } + catch (ClassNotFoundException e) + { + throw new IllegalArgumentException(e); + } + } + + + public void addListener(T t) + { + if (!isStarting()) + throw new IllegalStateException(); + + ServletContextHandler.this.addEventListener(t); + } + + + public void addListener(Class listenerClass) + { + if (!isStarting()) + throw new IllegalStateException(); + + try + { + EventListener l = createListener(listenerClass); + ServletContextHandler.this.addEventListener(l); + } + catch (ServletException e) + { + throw new IllegalStateException(e); + } + } + + + public T createListener(Class clazz) throws ServletException + { + try + { + T l = null; + try + { + l = clazz.newInstance(); + } + catch (InstantiationException e) + { + throw new ServletException(e); + } + catch (IllegalAccessException e) + { + throw new ServletException(e); + } + + if (_decorator!=null) + l=_decorator.decorateListenerInstance(l); + _created.add(l); + return l; + } + catch(ServletException e) + { + throw e; + } + catch(Exception e) + { + throw new ServletException(e); + } + } + + + public void declareRoles(String... roleNames) + { + if (!isStarting()) + throw new IllegalStateException(); + + //Get a reference to the SecurityHandler, which must be ConstraintAware + if (_securityHandler != null && _securityHandler instanceof ConstraintAware) + { + HashSet union = new HashSet(); + Set existing = ((ConstraintAware)_securityHandler).getRoles(); + if (existing != null) + union.addAll(existing); + union.addAll(Arrays.asList(roleNames)); + ((ConstraintSecurityHandler)_securityHandler).setRoles(union); + } + } + } + + public interface Decorator + { + T decorateFilterInstance(T filter) throws ServletException; + T decorateServletInstance(T servlet) throws ServletException; + T decorateListenerInstance(T listener) throws ServletException; + + void decorateFilterHolder(FilterHolder filter) throws ServletException; + void decorateServletHolder(ServletHolder servlet) throws ServletException; + + void destroyServletInstance(Servlet s); + void destroyFilterInstance(Filter f); + void destroyListenerInstance(EventListener f); } } diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java index cbb65017634..a0abf33d177 100644 --- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java +++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java @@ -16,12 +16,15 @@ package org.eclipse.jetty.servlet; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; +import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import org.eclipse.jetty.server.DispatcherType; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.RequestDispatcher; @@ -42,7 +45,6 @@ import org.eclipse.jetty.io.RuntimeIOException; import org.eclipse.jetty.security.IdentityService; import org.eclipse.jetty.security.SecurityHandler; import org.eclipse.jetty.server.Dispatcher; -import org.eclipse.jetty.server.DispatcherType; import org.eclipse.jetty.server.HttpConnection; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Server; @@ -73,7 +75,7 @@ public class ServletHandler extends ScopedHandler public static final String __DEFAULT_SERVLET="default"; /* ------------------------------------------------------------ */ - private ContextHandler _contextHandler; + private ServletContextHandler _contextHandler; private ContextHandler.Context _servletContext; private FilterHolder[] _filters; private FilterMapping[] _filterMappings; @@ -85,14 +87,14 @@ public class ServletHandler extends ScopedHandler private ServletHolder[] _servlets; private ServletMapping[] _servletMappings; - private transient Map _filterNameMap= new HashMap(); - private transient List _filterPathMappings; - private transient MultiMap _filterNameMappings; + private final Map _filterNameMap= new HashMap(); + private List _filterPathMappings; + private MultiMap _filterNameMappings; - private transient Map _servletNameMap=new HashMap(); - private transient PathMap _servletPathMap; + private final Map _servletNameMap=new HashMap(); + private PathMap _servletPathMap; - protected transient ConcurrentHashMap _chainCache[]; + protected ConcurrentHashMap _chainCache[]; /* ------------------------------------------------------------ */ @@ -130,7 +132,7 @@ public class ServletHandler extends ScopedHandler throws Exception { _servletContext=ContextHandler.getCurrentContext(); - _contextHandler=_servletContext==null?null:_servletContext.getContextHandler(); + _contextHandler=(ServletContextHandler)(_servletContext==null?null:_servletContext.getContextHandler()); if (_contextHandler!=null) { @@ -269,6 +271,7 @@ public class ServletHandler extends ScopedHandler { return _servletContext; } + /* ------------------------------------------------------------ */ /** * @return Returns the servletMappings. @@ -277,6 +280,30 @@ public class ServletHandler extends ScopedHandler { return _servletMappings; } + + /* ------------------------------------------------------------ */ + /** + * @return Returns the servletMappings. + */ + public ServletMapping getServletMapping(String pattern) + { + if (_servletMappings!=null) + { + for (ServletMapping m:_servletMappings) + { + String[] paths=m.getPathSpecs(); + if (paths!=null) + { + for (String path:paths) + { + if (pattern.equals(path)) + return m; + } + } + } + } + return null; + } /* ------------------------------------------------------------ */ /** Get Servlets. @@ -726,9 +753,9 @@ public class ServletHandler extends ScopedHandler */ public ServletHolder addServletWithMapping (Class servlet,String pathSpec) { - ServletHolder holder = newServletHolder(servlet); + ServletHolder holder = newServletHolder(); + holder.setHeldClass(servlet); setServlets((ServletHolder[])LazyList.addToArray(getServlets(), holder, ServletHolder.class)); - addServletWithMapping(holder,pathSpec); return holder; @@ -797,11 +824,82 @@ public class ServletHandler extends ScopedHandler return new FilterHolder(); } + /* ------------------------------------------------------------ */ public FilterHolder getFilter(String name) { return (FilterHolder)_filterNameMap.get(name); } + + + /* ------------------------------------------------------------ */ + /** Convenience method to add a filter. + * @param filter class of filter to create + * @param pathSpec filter mappings for filter + * @param dispatches see {@link FilterMapping#setDispatches(int)} + * @return The filter holder. + */ + public FilterHolder addFilterWithMapping (Class filter,String pathSpec,EnumSet dispatches) + { + FilterHolder holder = newFilterHolder(); + holder.setHeldClass(filter); + addFilterWithMapping(holder,pathSpec,dispatches); + + return holder; + } + + /* ------------------------------------------------------------ */ + /** Convenience method to add a filter. + * @param className of filter + * @param pathSpec filter mappings for filter + * @param dispatches see {@link FilterMapping#setDispatches(int)} + * @return The filter holder. + */ + public FilterHolder addFilterWithMapping (String className,String pathSpec,EnumSet dispatches) + { + FilterHolder holder = newFilterHolder(); + holder.setName(className+"-"+holder.hashCode()); + holder.setClassName(className); + + addFilterWithMapping(holder,pathSpec,dispatches); + return holder; + } + + /* ------------------------------------------------------------ */ + /** Convenience method to add a filter. + * @param holder filter holder to add + * @param pathSpec filter mappings for filter + * @param dispatches see {@link FilterMapping#setDispatches(int)} + */ + public void addFilterWithMapping (FilterHolder holder,String pathSpec,EnumSet dispatches) + { + FilterHolder[] holders = getFilters(); + if (holders!=null) + holders = (FilterHolder[])holders.clone(); + + try + { + setFilters((FilterHolder[])LazyList.addToArray(holders, holder, FilterHolder.class)); + + FilterMapping mapping = new FilterMapping(); + mapping.setFilterName(holder.getName()); + mapping.setPathSpec(pathSpec); + mapping.setDispatcherTypes(dispatches); + setFilterMappings((FilterMapping[])LazyList.addToArray(getFilterMappings(), mapping, FilterMapping.class)); + } + catch (RuntimeException e) + { + setFilters(holders); + throw e; + } + catch (Error e) + { + setFilters(holders); + throw e; + } + + } + /* ------------------------------------------------------------ */ /** Convenience method to add a filter. @@ -870,6 +968,20 @@ public class ServletHandler extends ScopedHandler } + + /* ------------------------------------------------------------ */ + /** Convenience method to add a filter with a mapping + * @param className + * @param pathSpec + * @param dispatches + * @return the filter holder created + * @deprecated use {@link #addFilterWithMapping(Class, String, int)} instead + */ + public FilterHolder addFilter (String className,String pathSpec,EnumSet dispatches) + { + return addFilterWithMapping(className, pathSpec, dispatches); + } + /* ------------------------------------------------------------ */ /** * convenience method to add a filter and mapping @@ -1282,55 +1394,18 @@ public class ServletHandler extends ScopedHandler } /* ------------------------------------------------------------ */ - /** - * Customize a servlet. - * - * Called before the servlet goes into service. - * Subclasses of ServletHandler should override - * this method. - * - * @param servlet - * @return the potentially customized servlet - * @throws Exception - */ - public Servlet customizeServlet (Servlet servlet) - throws Exception + void destroyServlet(Servlet servlet) { - return servlet; + _contextHandler.destroyServlet(servlet); } - + /* ------------------------------------------------------------ */ - public Servlet customizeServletDestroy (Servlet servlet) - throws Exception + void destroyFilter(Filter filter) { - return servlet; - } - - /* ------------------------------------------------------------ */ - /** - * Customize a Filter. - * - * Called before the Filter goes into service. - * Subclasses of ServletHandler should override - * this method. - * - * @param filter The filter to customize. - * @return the potentially customized filter - * @throws Exception If there was a problem - */ - public Filter customizeFilter (Filter filter) - throws Exception - { - return filter; - } - - /* ------------------------------------------------------------ */ - public Filter customizeFilterDestroy (Filter filter) - throws Exception - { - return filter; + _contextHandler.destroyFilter(filter); } + /* ------------------------------------------------------------ */ @Override diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java index f90d5799785..fe684d16904 100644 --- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java +++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java @@ -14,19 +14,26 @@ package org.eclipse.jetty.servlet; import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; +import java.util.List; import java.util.Map; +import java.util.Set; import java.util.Stack; + import javax.servlet.Servlet; import javax.servlet.ServletConfig; import javax.servlet.ServletException; +import org.eclipse.jetty.servlet.api.ServletRegistration; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.SingleThreadModel; import javax.servlet.UnavailableException; - import org.eclipse.jetty.security.IdentityService; import org.eclipse.jetty.security.RunAsToken; import org.eclipse.jetty.server.Request; @@ -45,7 +52,7 @@ import org.eclipse.jetty.util.log.Log; * * */ -public class ServletHolder extends Holder implements UserIdentity.Scope, Comparable +public class ServletHolder extends Holder implements UserIdentity.Scope, Comparable { /* ---------------------------------------------------------------- */ private int _initOrder; @@ -67,7 +74,9 @@ public class ServletHolder extends Holder implements UserIdentity.Scope, Compara /** Constructor . */ public ServletHolder() - {} + { + } + /* ---------------------------------------------------------------- */ /** Constructor for existing servlet. @@ -80,9 +89,9 @@ public class ServletHolder extends Holder implements UserIdentity.Scope, Compara /* ---------------------------------------------------------------- */ /** Constructor for existing servlet. */ - public ServletHolder(Class servlet) + public ServletHolder(Class servlet) { - super(servlet); + setHeldClass(servlet); } /* ---------------------------------------------------------------- */ @@ -124,6 +133,11 @@ public class ServletHolder extends Holder implements UserIdentity.Scope, Compara _initOnStartup=true; _initOrder = order; } + + public boolean isSetInitOrder() + { + return _initOnStartup; + } /* ------------------------------------------------------------ */ /** Comparitor by init order. @@ -293,7 +307,7 @@ public class ServletHolder extends Holder implements UserIdentity.Scope, Compara return; Servlet servlet = ((Servlet)o); servlet.destroy(); - getServletHandler().customizeServletDestroy(servlet); + getServletHandler().destroyServlet(servlet); } /* ------------------------------------------------------------ */ @@ -404,13 +418,9 @@ public class ServletHolder extends Holder implements UserIdentity.Scope, Compara try { if (_servlet==null) - _servlet=(Servlet)newInstance(); + _servlet=newInstance(); if (_config==null) _config=new Config(); - - //handle any cusomizations of the servlet, such as @postConstruct - if (!(_servlet instanceof SingleThreadedWrapper)) - _servlet = getServletHandler().customizeServlet(_servlet); // Handle run as if (_identityService!=null) @@ -468,22 +478,6 @@ public class ServletHolder extends Holder implements UserIdentity.Scope, Compara return _roleMap; } - /* ------------------------------------------------------------ */ - public String getRunAsRole() - { - return _runAsRole; - } - - /* ------------------------------------------------------------ */ - /** - * Set the run-as role for this servlet - * @param role run-as role for this servlet - */ - public void setRunAsRole(String role) - { - _runAsRole=role; - } - /* ------------------------------------------------------------ */ /** Service a request with this servlet. */ @@ -560,19 +554,92 @@ public class ServletHolder extends Holder implements UserIdentity.Scope, Compara } + /* -------------------------------------------------------- */ + /* -------------------------------------------------------- */ + /* -------------------------------------------------------- */ + public class Registration extends HolderRegistration implements ServletRegistration.Dynamic + { + public Set addMapping(String... urlPatterns) + { + illegalStateIfContextStarted(); + Set clash=null; + for (String pattern : urlPatterns) + { + if (_servletHandler.getServletMapping(pattern)!=null) + { + if (clash==null) + clash=new HashSet(); + clash.add(pattern); + } + } + + if (clash!=null) + return clash; + + ServletMapping mapping = new ServletMapping(); + mapping.setServletName(ServletHolder.this.getName()); + mapping.setPathSpecs(urlPatterns); + _servletHandler.addServletMapping(mapping); + + return Collections.emptySet(); + } + + public Collection getMappings() + { + ServletMapping[] mappings =_servletHandler.getServletMappings(); + List patterns=new ArrayList(); + for (ServletMapping mapping : mappings) + { + if (!mapping.getServletName().equals(getName())) + continue; + String[] specs=mapping.getPathSpecs(); + if (specs!=null && specs.length>0) + patterns.addAll(Arrays.asList(specs)); + } + return patterns; + } + + public String getRunAsRole() + { + return _runAsRole; + } + + public void setLoadOnStartup(int loadOnStartup) + { + illegalStateIfContextStarted(); + ServletHolder.this.setInitOrder(loadOnStartup); + } + + public int getInitOrder() + { + return ServletHolder.this.getInitOrder(); + } + + + public void setRunAsRole(String role) + { + _runAsRole = role; + } + } + + public ServletRegistration.Dynamic getRegistration() + { + return new Registration(); + } + /* -------------------------------------------------------- */ /* -------------------------------------------------------- */ /* -------------------------------------------------------- */ private class SingleThreadedWrapper implements Servlet { - Stack _stack=new Stack(); + Stack _stack=new Stack(); public void destroy() { synchronized(this) { while(_stack.size()>0) - try { ((Servlet)_stack.pop()).destroy(); } catch (Exception e) { Log.warn(e); } + try { (_stack.pop()).destroy(); } catch (Exception e) { Log.warn(e); } } } @@ -594,8 +661,7 @@ public class ServletHolder extends Holder implements UserIdentity.Scope, Compara { try { - Servlet s = (Servlet) newInstance(); - s = getServletHandler().customizeServlet(s); + Servlet s = newInstance(); s.init(config); _stack.push(s); } @@ -622,18 +688,13 @@ public class ServletHolder extends Holder implements UserIdentity.Scope, Compara { try { - s = (Servlet) newInstance(); - s = getServletHandler().customizeServlet(s); + s = newInstance(); s.init(_config); } catch (ServletException e) { throw e; } - catch (IOException e) - { - throw e; - } catch (Exception e) { throw new ServletException(e); @@ -653,7 +714,30 @@ public class ServletHolder extends Holder implements UserIdentity.Scope, Compara } } } - + } + + /* ------------------------------------------------------------ */ + /** + * @return + * @throws ServletException + * @throws IllegalAccessException + * @throws InstantiationException + */ + protected Servlet newInstance() throws ServletException, IllegalAccessException, InstantiationException + { + try + { + return ((ServletContextHandler.Context)getServletHandler().getServletContext()).createServlet(getHeldClass()); + } + catch (ServletException se) + { + Throwable cause = se.getRootCause(); + if (cause instanceof InstantiationException) + throw (InstantiationException)cause; + if (cause instanceof IllegalAccessException) + throw (IllegalAccessException)cause; + throw se; + } } } diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/api/FilterRegistration.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/api/FilterRegistration.java new file mode 100644 index 00000000000..6ff1b97afd5 --- /dev/null +++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/api/FilterRegistration.java @@ -0,0 +1,27 @@ +package org.eclipse.jetty.servlet.api; + +import java.util.Collection; +import java.util.EnumSet; + +import org.eclipse.jetty.server.DispatcherType; + +/** + * FilterRegistration + * + * Mimics the javax.servlet.FilterRegistration class to ease + * jetty-7/jetty-8 compatibility + */ +public interface FilterRegistration +{ + public void addMappingForServletNames(EnumSet dispatcherTypes, boolean isMatchAfter, String... servletNames); + + public Collection getServletNameMappings(); + + public void addMappingForUrlPatterns(EnumSet dispatcherTypes, boolean isMatchAfter, String... urlPatterns); + + public Collection getUrlPatternMappings(); + + interface Dynamic extends FilterRegistration, Registration.Dynamic + { + } +} diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/api/Registration.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/api/Registration.java new file mode 100644 index 00000000000..d83bd2ceb34 --- /dev/null +++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/api/Registration.java @@ -0,0 +1,25 @@ +package org.eclipse.jetty.servlet.api; + +import java.util.Map; +import java.util.Set; + +public interface Registration +{ + + public String getName(); + + public String getClassName(); + + public boolean setInitParameter(String name, String value); + + public String getInitParameter(String name); + + public Set setInitParameters(Map initParameters); + + public Map getInitParameters(); + + interface Dynamic extends Registration + { + public void setAsyncSupported(boolean isAsyncSupported); + } +} diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/api/ServletRegistration.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/api/ServletRegistration.java new file mode 100644 index 00000000000..c6f231dff2f --- /dev/null +++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/api/ServletRegistration.java @@ -0,0 +1,21 @@ +package org.eclipse.jetty.servlet.api; + +import java.util.Collection; +import java.util.Set; + +public interface ServletRegistration +{ + public Set addMapping(String... urlPatterns); + + public Collection getMappings(); + + public String getRunAsRole(); + + interface Dynamic extends ServletRegistration, Registration.Dynamic + { + public void setLoadOnStartup(int loadOnStartup); + + public void setRunAsRole(String roleName); + } + +} diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/DefaultsDescriptor.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/DefaultsDescriptor.java new file mode 100644 index 00000000000..e7331f6972a --- /dev/null +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/DefaultsDescriptor.java @@ -0,0 +1,29 @@ +// ======================================================================== +// Copyright (c) 2006-2010 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// 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.webapp; + +import org.eclipse.jetty.util.resource.Resource; + +/** + * DefaultsDescriptor + * + * + */ +public class DefaultsDescriptor extends Descriptor +{ + public DefaultsDescriptor(Resource xml, MetaData processor) + { + super(xml, processor); + } +} diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/Descriptor.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/Descriptor.java new file mode 100644 index 00000000000..e6aca3c819e --- /dev/null +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/Descriptor.java @@ -0,0 +1,290 @@ +// ======================================================================== +// Copyright (c) 2006-2010 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// 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.webapp; + +import java.net.URL; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import javax.servlet.Servlet; + +import org.eclipse.jetty.util.Loader; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.xml.XmlParser; + + + +/** + * Descriptor + * + * A web descriptor (web.xml/web-defaults.xml/web-overrides.xml). + */ +public class Descriptor +{ + protected static XmlParser _parser; + public enum MetaDataComplete {NotSet, True, False}; + protected Resource _xml; + protected XmlParser.Node _root; + protected MetaDataComplete _metaDataComplete; + protected int _majorVersion = 3; //default to container version + protected int _minorVersion = 0; + protected ArrayList _classNames = new ArrayList(); + protected boolean _distributable; + protected boolean _validating; + protected MetaData _metaData; + protected boolean _isOrdered = false; + protected List _ordering = new ArrayList(); + + + + + public static XmlParser newParser() + throws ClassNotFoundException + { + XmlParser xmlParser=new XmlParser(); + //set up cache of DTDs and schemas locally + URL dtd22=Loader.getResource(Servlet.class,"javax/servlet/resources/web-app_2_2.dtd",true); + URL dtd23=Loader.getResource(Servlet.class,"javax/servlet/resources/web-app_2_3.dtd",true); + URL j2ee14xsd=Loader.getResource(Servlet.class,"javax/servlet/resources/j2ee_1_4.xsd",true); + URL webapp24xsd=Loader.getResource(Servlet.class,"javax/servlet/resources/web-app_2_4.xsd",true); + URL webapp25xsd=Loader.getResource(Servlet.class,"javax/servlet/resources/web-app_2_5.xsd",true); + URL webapp30xsd=Loader.getResource(Servlet.class,"javax/servlet/resources/web-app_3_0.xsd",true); + URL webcommon30xsd=Loader.getResource(Servlet.class,"javax/servlet/resources/web-common_3_0.xsd",true); + URL webfragment30xsd=Loader.getResource(Servlet.class,"javax/servlet/resources/web-fragment_3_0.xsd",true); + URL schemadtd=Loader.getResource(Servlet.class,"javax/servlet/resources/XMLSchema.dtd",true); + URL xmlxsd=Loader.getResource(Servlet.class,"javax/servlet/resources/xml.xsd",true); + URL webservice11xsd=Loader.getResource(Servlet.class,"javax/servlet/resources/j2ee_web_services_client_1_1.xsd",true); + URL webservice12xsd=Loader.getResource(Servlet.class,"javax/servlet/resources/javaee_web_services_client_1_2.xsd",true); + URL datatypesdtd=Loader.getResource(Servlet.class,"javax/servlet/resources/datatypes.dtd",true); + + URL jsp20xsd = null; + URL jsp21xsd = null; + + try + { + Class jsp_page = Loader.loadClass(WebXmlConfiguration.class, "javax.servlet.jsp.JspPage"); + jsp20xsd = jsp_page.getResource("/javax/servlet/resources/jsp_2_0.xsd"); + jsp21xsd = jsp_page.getResource("/javax/servlet/resources/jsp_2_1.xsd"); + } + catch (Exception e) + { + Log.ignore(e); + } + finally + { + if (jsp20xsd == null) jsp20xsd = Loader.getResource(Servlet.class, "javax/servlet/resources/jsp_2_0.xsd", true); + if (jsp21xsd == null) jsp21xsd = Loader.getResource(Servlet.class, "javax/servlet/resources/jsp_2_1.xsd", true); + } + + redirect(xmlParser,"web-app_2_2.dtd",dtd22); + redirect(xmlParser,"-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN",dtd22); + redirect(xmlParser,"web.dtd",dtd23); + redirect(xmlParser,"web-app_2_3.dtd",dtd23); + redirect(xmlParser,"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN",dtd23); + redirect(xmlParser,"XMLSchema.dtd",schemadtd); + redirect(xmlParser,"http://www.w3.org/2001/XMLSchema.dtd",schemadtd); + redirect(xmlParser,"-//W3C//DTD XMLSCHEMA 200102//EN",schemadtd); + redirect(xmlParser,"jsp_2_0.xsd",jsp20xsd); + redirect(xmlParser,"http://java.sun.com/xml/ns/j2ee/jsp_2_0.xsd",jsp20xsd); + redirect(xmlParser,"http://java.sun.com/xml/ns/javaee/jsp_2_1.xsd",jsp21xsd); + redirect(xmlParser,"j2ee_1_4.xsd",j2ee14xsd); + redirect(xmlParser,"http://java.sun.com/xml/ns/j2ee/j2ee_1_4.xsd",j2ee14xsd); + redirect(xmlParser,"web-app_2_4.xsd",webapp24xsd); + redirect(xmlParser,"http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd",webapp24xsd); + redirect(xmlParser,"web-app_2_5.xsd",webapp25xsd); + redirect(xmlParser,"http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd",webapp25xsd); + redirect(xmlParser,"web-app_3_0.xsd",webapp30xsd); + redirect(xmlParser,"http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd",webapp30xsd); + redirect(xmlParser,"web-common_3_0.xsd",webcommon30xsd); + redirect(xmlParser,"http://java.sun.com/xml/ns/javaee/web-common_3_0.xsd",webcommon30xsd); + redirect(xmlParser,"web-fragment_3_0.xsd",webfragment30xsd); + redirect(xmlParser,"http://java.sun.com/xml/ns/javaee/web-fragment_3_0.xsd",webfragment30xsd); + redirect(xmlParser,"xml.xsd",xmlxsd); + redirect(xmlParser,"http://www.w3.org/2001/xml.xsd",xmlxsd); + redirect(xmlParser,"datatypes.dtd",datatypesdtd); + redirect(xmlParser,"http://www.w3.org/2001/datatypes.dtd",datatypesdtd); + redirect(xmlParser,"j2ee_web_services_client_1_1.xsd",webservice11xsd); + redirect(xmlParser,"http://www.ibm.com/webservices/xsd/j2ee_web_services_client_1_1.xsd",webservice11xsd); + redirect(xmlParser,"javaee_web_services_client_1_2.xsd",webservice12xsd); + redirect(xmlParser,"http://www.ibm.com/webservices/xsd/javaee_web_services_client_1_2.xsd",webservice12xsd); + return xmlParser; + } + + + protected static void redirect(XmlParser parser, String resource, URL source) + { + if (source != null) parser.redirectEntity(resource, source); + } + + + + public Descriptor (Resource xml, MetaData md) + { + _xml = xml; + _metaData = md; + } + + public void parse () + throws Exception + { + if (_parser == null) + _parser = newParser(); + + if (_root == null) + { + //boolean oldValidating = _processor.getParser().getValidating(); + //_processor.getParser().setValidating(_validating); + _root = _parser.parse(_xml.getURL().toString()); + processVersion(); + processOrdering(); + //_processor.getParser().setValidating(oldValidating); + } + } + + public MetaDataComplete getMetaDataComplete() + { + return _metaDataComplete; + } + + + public XmlParser.Node getRoot () + { + return _root; + } + + public int getMajorVersion () + { + return _majorVersion; + } + + public int getMinorVersion() + { + return _minorVersion; + } + + public Resource getResource () + { + return _xml; + } + + public MetaData getMetaData() + { + return _metaData; + } + + public void processVersion () + { + String version = _root.getAttribute("version", "DTD"); + if ("DTD".equals(version)) + { + _majorVersion = 2; + _minorVersion = 3; + String dtd = _parser.getDTD(); + if (dtd != null && dtd.indexOf("web-app_2_2") >= 0) + { + _majorVersion = 2; + _minorVersion = 2; + } + } + else + { + int dot = version.indexOf("."); + if (dot > 0) + { + _majorVersion = Integer.parseInt(version.substring(0,dot)); + _minorVersion = Integer.parseInt(version.substring(dot+1)); + } + } + + if (_majorVersion < 2 && _minorVersion < 5) + _metaDataComplete = MetaDataComplete.True; // does not apply before 2.5 + else + { + String s = (String)_root.getAttribute("metadata-complete"); + if (s == null) + _metaDataComplete = MetaDataComplete.NotSet; + else + _metaDataComplete = Boolean.valueOf(s).booleanValue()?MetaDataComplete.True:MetaDataComplete.False; + } + + Log.debug(_xml.toString()+": Calculated metadatacomplete = " + _metaDataComplete + " with version=" + version); + } + + public void processOrdering () + { + //Process the web.xml's optional element + XmlParser.Node ordering = _root.get("absolute-ordering"); + if (ordering == null) + return; + + _isOrdered = true; + //If an absolute-ordering was already set, then ignore it in favour of this new one + // _processor.setOrdering(new AbsoluteOrdering()); + + Iterator iter = ordering.iterator(); + XmlParser.Node node = null; + while (iter.hasNext()) + { + Object o = iter.next(); + if (!(o instanceof XmlParser.Node)) continue; + node = (XmlParser.Node) o; + + if (node.getTag().equalsIgnoreCase("others")) + //((AbsoluteOrdering)_processor.getOrdering()).addOthers(); + _ordering.add("others"); + else if (node.getTag().equalsIgnoreCase("name")) + //((AbsoluteOrdering)_processor.getOrdering()).add(node.toString(false,true)); + _ordering.add(node.toString(false,true)); + } + } + + public void addClassName (String className) + { + if (!_classNames.contains(className)) + _classNames.add(className); + } + + public ArrayList getClassNames () + { + return _classNames; + } + + public void setDistributable (boolean distributable) + { + _distributable = distributable; + } + + public boolean isDistributable() + { + return _distributable; + } + + public void setValidating (boolean validating) + { + _validating = validating; + } + + + public boolean isOrdered() + { + return _isOrdered; + } + + public List getOrdering() + { + return _ordering; + } +} diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/DescriptorProcessor.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/DescriptorProcessor.java new file mode 100644 index 00000000000..88a0e143d52 --- /dev/null +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/DescriptorProcessor.java @@ -0,0 +1,24 @@ +// ======================================================================== +// Copyright (c) 2006-2009 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// 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.webapp; + +/** + * DescriptorProcessor + * + * + */ +public interface DescriptorProcessor +{ + public void process (Descriptor descriptor) throws Exception; +} diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/DiscoveredAnnotation.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/DiscoveredAnnotation.java new file mode 100644 index 00000000000..2e1a1535b19 --- /dev/null +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/DiscoveredAnnotation.java @@ -0,0 +1,68 @@ +// ======================================================================== +// Copyright (c) 2010 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// 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.webapp; + +import org.eclipse.jetty.util.Loader; +import org.eclipse.jetty.util.log.Log; + +/** + * DiscoveredAnnotation + * + * Represents an annotation that has been discovered + * by scanning source code of WEB-INF/classes and WEB-INF/lib jars. + * + */ +public abstract class DiscoveredAnnotation +{ + protected WebAppContext _context; + protected String _className; + protected Class _clazz; + + public abstract void apply(); + + public DiscoveredAnnotation (WebAppContext context, String className) + { + _context = context; + _className = className; + } + + + public Class getTargetClass() + { + if (_clazz != null) + return _clazz; + + loadClass(); + + return _clazz; + } + + private void loadClass () + { + if (_clazz != null) + return; + + if (_className == null) + return; + + try + { + _clazz = Loader.loadClass(null, _className); + } + catch (Exception e) + { + Log.warn(e); + } + } +} diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/FragmentConfiguration.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/FragmentConfiguration.java index 47a2d9f81cc..4828738c94f 100644 --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/FragmentConfiguration.java +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/FragmentConfiguration.java @@ -21,7 +21,7 @@ import org.eclipse.jetty.util.resource.Resource; /** * FragmentConfiguration * - * This configuration supports some Servlet 3.0 features in jetty-7. + * * * Process web-fragments in jars */ @@ -33,20 +33,14 @@ public class FragmentConfiguration implements Configuration { if (!context.isConfigurationDiscovered()) return; + + MetaData metaData = (MetaData)context.getAttribute(MetaData.METADATA); + if (metaData == null) + throw new IllegalStateException("No metadata"); + + //find all web-fragment.xmls + findWebFragments(context, metaData); - WebXmlProcessor processor = (WebXmlProcessor)context.getAttribute(WebXmlProcessor.WEB_PROCESSOR); - if (processor == null) - { - processor = new WebXmlProcessor (context); - 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 @@ -54,16 +48,12 @@ public class FragmentConfiguration implements Configuration 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); - if (processor == null) - { - processor = new WebXmlProcessor (context); - context.setAttribute(WebXmlProcessor.WEB_PROCESSOR, processor); - } - - processor.processFragments(); + MetaData metaData = (MetaData)context.getAttribute(MetaData.METADATA); + if (metaData == null) + throw new IllegalStateException("No metadata"); + + //order the fragments + metaData.orderFragments(); } public void deconfigure(WebAppContext context) throws Exception @@ -78,20 +68,19 @@ public class FragmentConfiguration implements Configuration /* ------------------------------------------------------------------------------- */ /** - * Look for any web.xml fragments in META-INF of jars in WEB-INF/lib + * Look for any web-fragment.xml fragments in META-INF of jars in WEB-INF/lib * * @throws Exception */ - public void parseWebFragments (final WebAppContext context, final WebXmlProcessor processor) throws Exception + public void findWebFragments (final WebAppContext context, final MetaData metaData) throws Exception { List frags = (List)context.getAttribute(FRAGMENT_RESOURCES); if (frags!=null) { for (Resource frag : frags) { - processor.parseFragment(Resource.newResource("jar:"+frag.getURL()+"!/META-INF/web-fragment.xml")); + metaData.addFragment(frag, Resource.newResource("jar:"+frag.getURL()+"!/META-INF/web-fragment.xml")); } } } - } diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/FragmentDescriptor.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/FragmentDescriptor.java new file mode 100644 index 00000000000..75c36de57a7 --- /dev/null +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/FragmentDescriptor.java @@ -0,0 +1,162 @@ +// ======================================================================== +// Copyright (c) 2006-2010 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// 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.webapp; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.xml.XmlParser; + + +/** + * Fragment + * + * A web-fragment.xml descriptor. + */ +public class FragmentDescriptor extends Descriptor +{ + public static final String NAMELESS = "@@-NAMELESS-@@"; //prefix for nameless Fragments + public enum OtherType {None, Before, After}; + + protected int _counter = 0; + protected OtherType _otherType = OtherType.None; + + + protected List _befores = new ArrayList(); + protected List _afters = new ArrayList(); + protected String _name; + + + public FragmentDescriptor (Resource xml, MetaData processor) + throws Exception + { + super (xml, processor); + } + + public String getName () + { + return _name; + } + + public void parse () + throws Exception + { + super.parse(); + processName(); + } + + public void processName () + { + XmlParser.Node root = getRoot(); + XmlParser.Node nameNode = root.get("name"); + _name = NAMELESS+(_counter++); + if (nameNode != null) + { + String tmp = nameNode.toString(false,true); + if (tmp!=null && tmp.length()>0) + _name = tmp; + } + } + public void processOrdering () + { + //Process a fragment jar's web-fragment.xml elements + XmlParser.Node root = getRoot(); + + XmlParser.Node ordering = root.get("ordering"); + if (ordering == null) + return; //No ordering for this fragment + + _isOrdered = true; + + processBefores(ordering); + processAfters(ordering); + } + + + public void processBefores (XmlParser.Node ordering) + { + //Process the elements, looking for an clause and all of the clauses + XmlParser.Node before = ordering.get("before"); + if (before == null) + return; + + Iterator iter = before.iterator(); + XmlParser.Node node = null; + while (iter.hasNext()) + { + Object o = iter.next(); + if (!(o instanceof XmlParser.Node)) continue; + node = (XmlParser.Node) o; + if (node.getTag().equalsIgnoreCase("others")) + { + if (_otherType != OtherType.None) + throw new IllegalStateException("Duplicate clause detected in "+_xml.getURI()); + + _otherType = OtherType.Before; + } + else if (node.getTag().equalsIgnoreCase("name")) + _befores.add(node.toString(false,true)); + } + } + + public void processAfters (XmlParser.Node ordering) + { + //Process the elements, look for an clause and all of the clauses + XmlParser.Node after = ordering.get("after"); + if (after == null) + return; + + Iterator iter = after.iterator(); + XmlParser.Node node = null; + while (iter.hasNext()) + { + Object o = iter.next(); + if (!(o instanceof XmlParser.Node)) continue; + node = (XmlParser.Node) o; + if (node.getTag().equalsIgnoreCase("others")) + { + if (_otherType != OtherType.After) + throw new IllegalStateException("Duplicate clause detected in "+_xml.getURI()); + + _otherType = OtherType.After; + + } + else if (node.getTag().equalsIgnoreCase("name")) + _afters.add(node.toString(false,true)); + } + } + + public List getBefores() + { + return Collections.unmodifiableList(_befores); + } + + public List getAfters() + { + return Collections.unmodifiableList(_afters); + } + + public OtherType getOtherType () + { + return _otherType; + } + + public List getOrdering() + { + return null; //only used for absolute-ordering in Descriptor + } +} \ No newline at end of file diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/IterativeDescriptorProcessor.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/IterativeDescriptorProcessor.java new file mode 100644 index 00000000000..fd1507649e5 --- /dev/null +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/IterativeDescriptorProcessor.java @@ -0,0 +1,84 @@ +// ======================================================================== +// Copyright (c) 2006-2009 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// 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.webapp; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.eclipse.jetty.xml.XmlParser; + +/** + * IterativeDescriptorProcessor + * + * + */ +public abstract class IterativeDescriptorProcessor implements DescriptorProcessor +{ + public static final Class[] __signature = new Class[]{Descriptor.class, XmlParser.Node.class}; + protected Map _visitors = new HashMap(); + public abstract void start(Descriptor descriptor); + public abstract void end(Descriptor descriptor); + + /** + * Register a method to be called back when visiting the node with the given name. + * The method must exist on a subclass of this class, and must have the signature: + * public void method (Descriptor descriptor, XmlParser.Node node) + * @param nodeName + * @param m + */ + public void registerVisitor(String nodeName, Method m) + { + _visitors.put(nodeName, m); + } + + + /** + * @see org.eclipse.jetty.webapp.DescriptorProcessor#process(org.eclipse.jetty.webapp.Descriptor) + */ + public void process(Descriptor descriptor) + throws Exception + { + if (descriptor == null) + return; + + start(descriptor); + + XmlParser.Node root = descriptor.getRoot(); + Iterator iter = root.iterator(); + XmlParser.Node node = null; + while (iter.hasNext()) + { + Object o = iter.next(); + if (!(o instanceof XmlParser.Node)) continue; + node = (XmlParser.Node) o; + visit(descriptor, node); + } + + end(descriptor); + } + + + protected void visit (final Descriptor descriptor, final XmlParser.Node node) + throws Exception + { + String name = node.getTag(); + Method m = _visitors.get(name); + if (m != null) + m.invoke(this, new Object[]{descriptor, node}); + } +} diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/JettyWebXmlConfiguration.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/JettyWebXmlConfiguration.java index 27aa287e17e..10bcfad5a98 100644 --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/JettyWebXmlConfiguration.java +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/JettyWebXmlConfiguration.java @@ -25,7 +25,7 @@ import org.eclipse.jetty.xml.XmlConfiguration; * * JettyWebConfiguration. * - * Looks for Xmlconfiguration files in WEB-INF. Searches in order for the first of jetty6-web.xml, jetty-web.xml or web-jetty.xml + * Looks for Xmlconfiguration files in WEB-INF. Searches in order for the first of jettyX-web.xml, jetty-web.xml or web-jetty.xml * * * diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/MetaData.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/MetaData.java new file mode 100644 index 00000000000..b240b190d75 --- /dev/null +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/MetaData.java @@ -0,0 +1,911 @@ +// ======================================================================== +// Copyright (c) 2009 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// 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.webapp; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import javax.servlet.ServletContext; + +import org.eclipse.jetty.util.resource.Resource; + + + + + +/** + * MetaData + * + * + */ +public class MetaData +{ + public static final String METADATA = "org.eclipse.jetty.metaData"; + public static final String METADATA_COMPLETE = "org.eclipse.jetty.metadataComplete"; + public static final String WEBXML_MAJOR_VERSION = "org.eclipse.jetty.webXmlMajorVersion"; + public static final String WEBXML_MINOR_VERSION = "org.eclipse.jetty.webXmlMinorVersion"; + public static final String WEBXML_CLASSNAMES = "org.eclipse.jetty.webXmlClassNames"; + public static final String ORDERED_LIBS = "javax.servlet.context.orderedLibs"; + + public enum Origin {NotSet, WebXml, WebDefaults, WebOverride, WebFragment, Annotation}; + + protected WebAppContext _context; + protected Map _origins = new HashMap(); + protected Descriptor _webDefaultsRoot; + protected Descriptor _webXmlRoot; + protected Descriptor _webOverrideRoot; + protected List _annotations = new ArrayList(); + protected List _descriptorProcessors = new ArrayList(); + protected List _webFragmentRoots = new ArrayList(); + protected Map _webFragmentNameMap = new HashMap(); + protected Map _webFragmentResourceMap = new HashMap(); + protected Map> _webFragmentAnnotations = new HashMap>(); + protected List _orderedResources; + protected Ordering _ordering;//can be set to RelativeOrdering by web-default.xml, web.xml, web-override.xml + protected StandardDescriptorProcessor _standardDescriptorProcessor; + + + public static class OriginInfo + { + protected String name; + protected Origin origin; + protected Descriptor descriptor; + + public OriginInfo (String n, Descriptor d) + { + name = n; + descriptor = d; + if (d == null) + throw new IllegalArgumentException("No descriptor"); + if (d instanceof FragmentDescriptor) + origin = Origin.WebFragment; + if (d instanceof OverrideDescriptor) + origin = Origin.WebOverride; + if (d instanceof DefaultsDescriptor) + origin = Origin.WebDefaults; + origin = Origin.WebXml; + } + + public OriginInfo (String n) + { + name = n; + origin = Origin.Annotation; + } + + public OriginInfo(String n, Origin o) + { + name = n; + origin = o; + } + + public String getName() + { + return name; + } + + public Origin getOriginType() + { + return origin; + } + + public Descriptor getDescriptor() + { + return descriptor; + } + } + + /** + * Ordering + * + * + */ + public interface Ordering + { + public List order(List fragments); + public boolean isAbsolute (); + public boolean hasOther(); + } + + /** + * AbsoluteOrdering + * + * An element in web.xml + */ + public class AbsoluteOrdering implements Ordering + { + public static final String OTHER = "@@-OTHER-@@"; + protected List _order = new ArrayList(); + protected boolean _hasOther = false; + + /** + * Order the list of jars in WEB-INF/lib according to the ordering declarations in the descriptors + * @see org.eclipse.jetty.webapp.MetaData.Ordering#order(java.util.List) + */ + public List order(List jars) + { + List orderedList = new ArrayList(); + List tmp = new ArrayList(jars); + + //1. put everything into the list of named others, and take the named ones out of there, + //assuming we will want to use the clause + Map others = new HashMap(getNamedFragments()); + + //2. for each name, take out of the list of others, add to tail of list + int index = -1; + for (String item:_order) + { + if (!item.equals(OTHER)) + { + FragmentDescriptor f = others.remove(item); + if (f != null) + { + Resource jar = getJarForFragment(item); + orderedList.add(jar); //take from others and put into final list in order, ignoring duplicate names + //remove resource from list for resource matching name of descriptor + tmp.remove(jar); + } + } + else + index = orderedList.size(); //remember the index at which we want to add in all the others + } + + //3. if was specified, insert rest of the fragments + if (_hasOther) + { + orderedList.addAll((index < 0? 0: index), tmp); + } + + return orderedList; + } + + public boolean isAbsolute() + { + return true; + } + + public void add (String name) + { + _order.add(name); + } + + public void addOthers () + { + if (_hasOther) + throw new IllegalStateException ("Duplicate element in absolute ordering"); + + _hasOther = true; + _order.add(OTHER); + } + + public boolean hasOther () + { + return _hasOther; + } + } + + + /** + * RelativeOrdering + * + * A set of elements in web-fragment.xmls. + */ + public class RelativeOrdering implements Ordering + { + protected LinkedList _beforeOthers = new LinkedList(); + protected LinkedList _afterOthers = new LinkedList(); + protected LinkedList _noOthers = new LinkedList(); + + /** + * Order the list of jars according to the ordering declared + * in the various web-fragment.xml files. + * @see org.eclipse.jetty.webapp.MetaData.Ordering#order(java.util.List) + */ + public List order(List jars) + { + //for each jar, put it into the ordering according to the fragment ordering + for (Resource jar:jars) + { + //check if the jar has a fragment descriptor + FragmentDescriptor descriptor = _webFragmentResourceMap.get(jar); + if (descriptor != null) + { + switch (descriptor.getOtherType()) + { + case None: + { + ((RelativeOrdering)_ordering).addNoOthers(jar); + break; + } + case Before: + { + ((RelativeOrdering)_ordering).addBeforeOthers(jar); + break; + } + case After: + { + ((RelativeOrdering)_ordering).addAfterOthers(jar); + break; + } + } + } + else + { + //jar fragment has no descriptor, but there is a relative ordering in place, so it must be part of the others + ((RelativeOrdering)_ordering).addNoOthers(jar); + } + } + + //now apply the ordering + List orderedList = new ArrayList(); + int maxIterations = 2; + boolean done = false; + do + { + //1. order the before-others according to any explicit before/after relationships + boolean changesBefore = orderList(_beforeOthers); + + //2. order the after-others according to any explicit before/after relationships + boolean changesAfter = orderList(_afterOthers); + + //3. order the no-others according to their explicit before/after relationships + boolean changesNone = orderList(_noOthers); + + //we're finished on a clean pass through with no ordering changes + done = (!changesBefore && !changesAfter && !changesNone); + } + while (!done && (--maxIterations >0)); + + //4. merge before-others + no-others +after-others + if (!done) + throw new IllegalStateException("Circular references for fragments"); + + for (Resource r: _beforeOthers) + orderedList.add(r); + for (Resource r: _noOthers) + orderedList.add(r); + for(Resource r: _afterOthers) + orderedList.add(r); + + return orderedList; + } + + public boolean isAbsolute () + { + return false; + } + + public boolean hasOther () + { + return !_beforeOthers.isEmpty() || !_afterOthers.isEmpty(); + } + + public void addBeforeOthers (Resource r) + { + _beforeOthers.addLast(r); + } + + public void addAfterOthers (Resource r) + { + _afterOthers.addLast(r); + } + + public void addNoOthers (Resource r) + { + _noOthers.addLast(r); + } + + protected boolean orderList (LinkedList list) + { + //Take a copy of the list so we can iterate over it and at the same time do random insertions + boolean changes = false; + List iterable = new ArrayList(list); + Iterator itor = iterable.iterator(); + + while (itor.hasNext()) + { + Resource r = itor.next(); + FragmentDescriptor f = getFragment(r); + if (f == null) + { + //no fragment for this resource so cannot have any ordering directives + continue; + } + + //Handle any explicit relationships for the fragment we're considering + List befores = f.getBefores(); + if (befores != null && !befores.isEmpty()) + { + for (String b: befores) + { + //Fragment we're considering must be before b + //Check that we are already before it, if not, move us so that we are. + //If the name does not exist in our list, then get it out of the no-other list + if (!isBefore(list, f.getName(), b)) + { + //b is not already before name, move it so that it is + int idx1 = getIndexOf(list, f.getName()); + int idx2 = getIndexOf(list, b); + + //if b is not in the same list + if (idx2 < 0) + { + changes = true; + // must be in the noOthers list or it would have been an error + Resource bResource = getJarForFragment(b); + if (bResource != null) + { + //If its in the no-others list, insert into this list so that we are before it + if (_noOthers.remove(bResource)) + { + insert(list, idx1+1, b); + + } + } + } + else + { + //b is in the same list but b is before name, so swap it around + list.remove(idx1); + insert(list, idx2, f.getName()); + changes = true; + } + } + } + } + + //Handle any explicit relationships + List afters = f.getAfters(); + if (afters != null && !afters.isEmpty()) + { + for (String a: afters) + { + //Check that fragment we're considering is after a, moving it if possible if its not + if (!isAfter(list, f.getName(), a)) + { + //name is not after a, move it + int idx1 = getIndexOf(list, f.getName()); + int idx2 = getIndexOf(list, a); + + //if a is not in the same list as name + if (idx2 < 0) + { + changes = true; + //take it out of the noOthers list and put it in the right place in this list + Resource aResource = getJarForFragment(a); + if (aResource != null) + { + if (_noOthers.remove(aResource)) + { + insert(list,idx1, aResource); + } + } + } + else + { + //a is in the same list as name, but in the wrong place, so move it + list.remove(idx2); + insert(list,idx1, a); + changes = true; + } + } + //Name we're considering must be after this name + //Check we're already after it, if not, move us so that we are. + //If the name does not exist in our list, then get it out of the no-other list + } + } + } + + return changes; + } + + /** + * Is fragment with name a before fragment with name b? + * @param list + * @param fragNameA + * @param fragNameB + * @return + */ + protected boolean isBefore (List list, String fragNameA, String fragNameB) + { + //check if a and b are already in the same list, and b is already + //before a + int idxa = getIndexOf(list, fragNameA); + int idxb = getIndexOf(list, fragNameB); + + + if (idxb >=0 && idxb < idxa) + { + //a and b are in the same list but a is not before b + return false; + } + + if (idxb < 0) + { + //a and b are not in the same list, but it is still possible that a is before + //b, depending on which list we're examining + if (list == _beforeOthers) + { + //The list we're looking at is the beforeOthers.If b is in the _afterOthers or the _noOthers, then by + //definition a is before it + return true; + } + else if (list == _afterOthers) + { + //The list we're looking at is the afterOthers, then a will be the tail of + //the final list. If b is in the beforeOthers list, then b will be before a and an error. + if (_beforeOthers.contains(fragNameB)) + throw new IllegalStateException("Incorrect relationship: "+fragNameA+" before "+fragNameB); + else + return false; //b could be moved to the list + } + } + + //a and b are in the same list and a is already before b + return true; + } + + + /** + * Is fragment name "a" after fragment name "b"? + * @param list + * @param fragNameA + * @param fragNameB + * @return + */ + protected boolean isAfter(List list, String fragNameA, String fragNameB) + { + int idxa = getIndexOf(list, fragNameA); + int idxb = getIndexOf(list, fragNameB); + + if (idxb >=0 && idxa < idxb) + { + //a and b are both in the same list, but a is before b + return false; + } + + if (idxb < 0) + { + //a and b are in different lists. a could still be after b depending on which list it is in. + + if (list == _afterOthers) + { + //The list we're looking at is the afterOthers. If b is in the beforeOthers or noOthers then + //by definition a is after b because a is in the afterOthers list. + return true; + } + else if (list == _beforeOthers) + { + //The list we're looking at is beforeOthers, and contains a and will be before + //everything else in the final ist. If b is in the afterOthers list, then a cannot be before b. + if (_afterOthers.contains(fragNameB)) + throw new IllegalStateException("Incorrect relationship: "+fragNameB+" after "+fragNameA); + else + return false; //b could be moved from noOthers list + } + } + + return true; //a and b in the same list, a is after b + } + + /** + * Insert the resource matching the fragName into the list of resources + * at the location indicated by index. + * + * @param list + * @param index + * @param fragName + */ + protected void insert(List list, int index, String fragName) + { + Resource jar = getJarForFragment(fragName); + if (jar == null) + throw new IllegalStateException("No jar for insertion"); + + insert(list, index, jar); + } + + protected void insert(List list, int index, Resource resource) + { + if (list == null) + throw new IllegalStateException("List is null for insertion"); + + //add it at the end + if (index > list.size()) + list.add(resource); + else + list.add(index, resource); + } + + protected void remove (List resources, Resource r) + { + if (resources == null) + return; + resources.remove(r); + } + + protected int getIndexOf(List resources, String fragmentName) + { + FragmentDescriptor fd = getFragment(fragmentName); + if (fd == null) + return -1; + + + Resource r = getJarForFragment(fragmentName); + if (r == null) + return -1; + + return resources.indexOf(r); + } + } + + + + public MetaData (WebAppContext context) throws ClassNotFoundException + { + _context = context; + + } + + public WebAppContext getContext() + { + return _context; + } + + + + public void setDefaults (Resource webDefaults) + throws Exception + { + _webDefaultsRoot = new DefaultsDescriptor(webDefaults, this); + _webDefaultsRoot.parse(); + if (_webDefaultsRoot.isOrdered()) + { + if (_ordering == null) + _ordering = new AbsoluteOrdering(); + + List order = _webDefaultsRoot.getOrdering(); + for (String s:order) + { + if (s.equalsIgnoreCase("others")) + ((AbsoluteOrdering)_ordering).addOthers(); + else + ((AbsoluteOrdering)_ordering).add(s); + } + } + } + + public void setWebXml (Resource webXml) + throws Exception + { + _webXmlRoot = new Descriptor(webXml, this); + _webXmlRoot.parse(); + if (_webXmlRoot.getMetaDataComplete() == Descriptor.MetaDataComplete.True) + _context.setAttribute(METADATA_COMPLETE, Boolean.TRUE); + else + _context.setAttribute(METADATA_COMPLETE, Boolean.FALSE); + + _context.setAttribute(WEBXML_CLASSNAMES, _webXmlRoot.getClassNames()); + + if (_webXmlRoot.isOrdered()) + { + if (_ordering == null) + _ordering = new AbsoluteOrdering(); + + List order = _webXmlRoot.getOrdering(); + for (String s:order) + { + if (s.equalsIgnoreCase("others")) + ((AbsoluteOrdering)_ordering).addOthers(); + else + ((AbsoluteOrdering)_ordering).add(s); + } + } + } + + public void setOverride (Resource override) + throws Exception + { + _webOverrideRoot = new OverrideDescriptor(override, this); + _webOverrideRoot.setValidating(false); + _webOverrideRoot.parse(); + if (_webOverrideRoot.getMetaDataComplete() == Descriptor.MetaDataComplete.True) + _context.setAttribute(METADATA_COMPLETE, Boolean.TRUE); + else if (_webOverrideRoot.getMetaDataComplete() == Descriptor.MetaDataComplete.False) + _context.setAttribute(METADATA_COMPLETE, Boolean.FALSE); + + if (_webOverrideRoot.isOrdered()) + { + if (_ordering == null) + _ordering = new AbsoluteOrdering(); + + List order = _webOverrideRoot.getOrdering(); + for (String s:order) + { + if (s.equalsIgnoreCase("others")) + ((AbsoluteOrdering)_ordering).addOthers(); + else + ((AbsoluteOrdering)_ordering).add(s); + } + } + } + + + /** + * Add a web-fragment.xml + * + * @param jarResource the jar the fragment is contained in + * @param xmlResource the resource representing the xml file + * @throws Exception + */ + public void addFragment (Resource jarResource, Resource xmlResource) + throws Exception + { + Boolean metaComplete = (Boolean)_context.getAttribute(METADATA_COMPLETE); + if (metaComplete != null && metaComplete.booleanValue()) + return; //do not process anything else if web.xml/web-override.xml set metadata-complete + + //Metadata-complete is not set, or there is no web.xml + FragmentDescriptor descriptor = new FragmentDescriptor(xmlResource, this); + _webFragmentResourceMap.put(jarResource, descriptor); + _webFragmentRoots.add(descriptor); + + descriptor.parse(); + + if (descriptor.getName() != null) + _webFragmentNameMap.put(descriptor.getName(), descriptor); + + //If web.xml has specified an absolute ordering, ignore any relative ordering in the fragment + if (_ordering != null && _ordering.isAbsolute()) + return; + + if (_ordering == null && descriptor.isOrdered()) + _ordering = new RelativeOrdering(); + } + + /** + * Annotations not associated with a WEB-INF/lib fragment jar. + * These are from WEB-INF/classes or the ??container path?? + * @param annotations + */ + public void addDiscoveredAnnotations(List annotations) + { + _annotations.addAll(annotations); + } + + public void addDiscoveredAnnotations(Resource resource, List annotations) + { + _webFragmentAnnotations.put(resource, new ArrayList(annotations)); + } + + public void addDescriptorProcessor(DescriptorProcessor p) + { + _descriptorProcessors.add(p); + } + + public void orderFragments () + { + //if we have already ordered them don't do it again + if (_orderedResources != null) + return; + + if (_ordering != null) + { + //Get the jars in WEB-INF/lib according to the order specified + _orderedResources = _ordering.order((List)_context.getAttribute(WebInfConfiguration.WEB_INF_JAR_RESOURCES)); + + _context.setAttribute(WebInfConfiguration.WEB_INF_ORDERED_JAR_RESOURCES, _orderedResources); + List orderedJars = new ArrayList(); + + for (Resource webInfJar:_orderedResources) + { + //get just the name of the jar file + String fullname = webInfJar.getName(); + int i = fullname.indexOf(".jar"); + int j = fullname.lastIndexOf("/", i); + orderedJars.add(fullname.substring(j+1,i+4)); + } + + _context.setAttribute(ORDERED_LIBS, orderedJars); + } + else + _orderedResources = new ArrayList((List)_context.getAttribute(WebInfConfiguration.WEB_INF_JAR_RESOURCES)); + } + + + /** + * Resolve all servlet/filter/listener metadata from all sources: descriptors and annotations. + * + */ + public void resolve () + throws Exception + { + //TODO - apply all descriptors and annotations in order: + //apply descriptorProcessors to web-defaults.xml + //apply descriptorProcessors to web.xml + //apply descriptorProcessors to web-override.xml + //apply discovered annotations from container path + //apply discovered annotations from WEB-INF/classes + //for the ordering of the jars in WEB-INF/lib: + // +apply descriptorProcessors to web-fragment.xml + // +apply discovered annotations + + for (DescriptorProcessor p:_descriptorProcessors) + { + p.process(getWebDefault()); + p.process(getWebXml()); + p.process(getOverrideWeb()); + } + + for (DiscoveredAnnotation a:_annotations) + a.apply(); + + + List resources = getOrderedResources(); + for (Resource r:resources) + { + FragmentDescriptor fd = _webFragmentResourceMap.get(r); + if (fd != null) + { + for (DescriptorProcessor p:_descriptorProcessors) + { + p.process(fd); + } + } + + List fragAnnotations = _webFragmentAnnotations.get(r); + if (fragAnnotations != null) + { + for (DiscoveredAnnotation a:fragAnnotations) + a.apply(); + } + } + } + + public boolean isDistributable () + { + boolean distributable = ( + (_webDefaultsRoot != null && _webDefaultsRoot.isDistributable()) + || (_webXmlRoot != null && _webXmlRoot.isDistributable()) + || (_webOverrideRoot != null && _webOverrideRoot.isDistributable())); + + List orderedResources = getOrderedResources(); + for (Resource r: orderedResources) + { + FragmentDescriptor d = _webFragmentResourceMap.get(r); + if (d!=null) + distributable = distributable && d.isDistributable(); + } + return distributable; + } + + + public Descriptor getWebXml () + { + return _webXmlRoot; + } + + public Descriptor getOverrideWeb () + { + return _webOverrideRoot; + } + + public Descriptor getWebDefault () + { + return _webDefaultsRoot; + } + + public List getFragments () + { + return _webFragmentRoots; + } + + public List getOrderedResources () + { + return _orderedResources; + } + + public List getOrderedFragments () + { + List list = new ArrayList(); + if (_orderedResources == null) + return list; + + for (Resource r:_orderedResources) + { + FragmentDescriptor fd = _webFragmentResourceMap.get(r); + if (fd != null) + list.add(fd); + } + return list; + } + + public Ordering getOrdering() + { + return _ordering; + } + + public void setOrdering (Ordering o) + { + _ordering = o; + } + + public FragmentDescriptor getFragment (Resource jar) + { + return _webFragmentResourceMap.get(jar); + } + + public FragmentDescriptor getFragment(String name) + { + return _webFragmentNameMap.get(name); + } + + public Resource getJarForFragment (String name) + { + FragmentDescriptor f = getFragment(name); + if (f == null) + return null; + + Resource jar = null; + for (Resource r: _webFragmentResourceMap.keySet()) + { + if (_webFragmentResourceMap.get(r).equals(f)) + jar = r; + } + return jar; + } + + public Map getNamedFragments () + { + return Collections.unmodifiableMap(_webFragmentNameMap); + } + + + public Origin getOrigin (String name) + { + OriginInfo x = _origins.get(name); + if (x == null) + return Origin.NotSet; + + return x.getOriginType(); + } + + + public Descriptor getOriginDescriptor (String name) + { + OriginInfo o = _origins.get(name); + if (o == null) + return null; + return o.getDescriptor(); + } + + public void setOrigin (String name, Descriptor d) + { + OriginInfo x = new OriginInfo (name, d); + _origins.put(name, x); + } + + public void setOrigin (String name) + { + if (name == null) + return; + + OriginInfo x = new OriginInfo (name, Origin.Annotation); + _origins.put(name, x); + } +} diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/OverrideDescriptor.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/OverrideDescriptor.java new file mode 100644 index 00000000000..fe7adb35994 --- /dev/null +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/OverrideDescriptor.java @@ -0,0 +1,29 @@ +// ======================================================================== +// Copyright (c) 2006-2010 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// 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.webapp; + +import org.eclipse.jetty.util.resource.Resource; + +/** + * OverrideDescriptor + * + * + */ +public class OverrideDescriptor extends Descriptor +{ + public OverrideDescriptor(Resource xml, MetaData processor) + { + super(xml, processor); + } +} diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/StandardDescriptorProcessor.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/StandardDescriptorProcessor.java new file mode 100644 index 00000000000..952f8da4ee0 --- /dev/null +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/StandardDescriptorProcessor.java @@ -0,0 +1,1335 @@ +// ======================================================================== +// Copyright (c) 2006-2010 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// 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.webapp; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.EventListener; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.jetty.server.DispatcherType; +import javax.servlet.ServletException; +import org.eclipse.jetty.servlet.api.ServletRegistration; + + +import org.eclipse.jetty.http.security.Constraint; +import org.eclipse.jetty.security.ConstraintAware; +import org.eclipse.jetty.security.ConstraintMapping; +import org.eclipse.jetty.security.SecurityHandler; +import org.eclipse.jetty.security.authentication.FormAuthenticator; +import org.eclipse.jetty.servlet.ErrorPageErrorHandler; +import org.eclipse.jetty.servlet.FilterHolder; +import org.eclipse.jetty.servlet.FilterMapping; +import org.eclipse.jetty.servlet.Holder; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.servlet.ServletMapping; +import org.eclipse.jetty.util.LazyList; +import org.eclipse.jetty.util.Loader; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.webapp.MetaData.Origin; +import org.eclipse.jetty.xml.XmlParser; + +/** + * StandardDescriptorProcessor + * + * + */ +public class StandardDescriptorProcessor extends IterativeDescriptorProcessor +{ + public static final String STANDARD_PROCESSOR = "org.eclipse.jetty.standardDescriptorProcessor"; + protected WebAppContext _context; + + //the shared configuration operated on by web-default.xml, web.xml, web-override.xml and all web-fragment.xml + protected ServletHandler _servletHandler; + protected SecurityHandler _securityHandler; + protected Object _filters; + protected Object _filterMappings; + protected Object _servlets; + protected Object _servletMappings; + protected Object _listeners; + protected Object _listenerClassNames; + protected Object _welcomeFiles; + protected Set _roles = new HashSet(); + protected List _constraintMappings = new ArrayList(); + protected Map _errorPages; + protected boolean _hasJSP; + protected String _jspServletName; + protected String _jspServletClass; + protected boolean _defaultWelcomeFileList; + protected MetaData _metaData; + + + + public StandardDescriptorProcessor () + { + + try + { + registerVisitor("context-param", this.getClass().getDeclaredMethod("visitContextParam", __signature)); + registerVisitor("display-name", this.getClass().getDeclaredMethod("visitDisplayName", __signature)); + registerVisitor("servlet", this.getClass().getDeclaredMethod("visitServlet", __signature)); + registerVisitor("servlet-mapping", this.getClass().getDeclaredMethod("visitServletMapping", __signature)); + registerVisitor("session-config", this.getClass().getDeclaredMethod("visitSessionConfig", __signature)); + registerVisitor("mime-mapping", this.getClass().getDeclaredMethod("visitMimeMapping", __signature)); + registerVisitor("welcome-file-list", this.getClass().getDeclaredMethod("visitWelcomeFileList", __signature)); + registerVisitor("locale-encoding-mapping-list", this.getClass().getDeclaredMethod("visitLocaleEncodingList", __signature)); + registerVisitor("error-page", this.getClass().getDeclaredMethod("visitErrorPage", __signature)); + registerVisitor("taglib", this.getClass().getDeclaredMethod("visitTagLib", __signature)); + registerVisitor("jsp-config", this.getClass().getDeclaredMethod("visitJspConfig", __signature)); + registerVisitor("security-constraint", this.getClass().getDeclaredMethod("visitSecurityConstraint", __signature)); + registerVisitor("login-config", this.getClass().getDeclaredMethod("visitLoginConfig", __signature)); + registerVisitor("security-role", this.getClass().getDeclaredMethod("visitSecurityRole", __signature)); + registerVisitor("filter", this.getClass().getDeclaredMethod("visitFilter", __signature)); + registerVisitor("filter-mapping", this.getClass().getDeclaredMethod("visitFilterMapping", __signature)); + registerVisitor("listener", this.getClass().getDeclaredMethod("visitListener", __signature)); + registerVisitor("distributable", this.getClass().getDeclaredMethod("visitDistributable", __signature)); + } + catch (Exception e) + { + throw new IllegalStateException(e); + } + } + + + + /** + * @see org.eclipse.jetty.webapp.IterativeDescriptorProcessor#start() + */ + public void start(Descriptor descriptor) + { + _metaData = descriptor.getMetaData(); + _context = _metaData.getContext(); + + //Get the current objects from the context + _servletHandler = _context.getServletHandler(); + _securityHandler = (SecurityHandler)_context.getSecurityHandler(); + _filters = LazyList.array2List(_servletHandler.getFilters()); + _filterMappings = LazyList.array2List(_servletHandler.getFilterMappings()); + _servlets = LazyList.array2List(_servletHandler.getServlets()); + _servletMappings = LazyList.array2List(_servletHandler.getServletMappings()); + _listeners = LazyList.array2List(_context.getEventListeners()); + _welcomeFiles = LazyList.array2List(_context.getWelcomeFiles()); + if (_securityHandler instanceof ConstraintAware) + { + _constraintMappings.addAll(((ConstraintAware) _securityHandler).getConstraintMappings()); + if (((ConstraintAware) _securityHandler).getRoles() != null) + { + _roles.addAll(((ConstraintAware) _securityHandler).getRoles()); + } + } + _errorPages = _context.getErrorHandler() instanceof ErrorPageErrorHandler ? ((ErrorPageErrorHandler)_context.getErrorHandler()).getErrorPages() : null; + } + + + + /** + * @see org.eclipse.jetty.webapp.IterativeDescriptorProcessor#end() + */ + public void end(Descriptor descriptor) + { + //Set the context with the results of the processing + _servletHandler.setFilters((FilterHolder[]) LazyList.toArray(_filters, FilterHolder.class)); + _servletHandler.setFilterMappings((FilterMapping[]) LazyList.toArray(_filterMappings, FilterMapping.class)); + _servletHandler.setServlets((ServletHolder[]) LazyList.toArray(_servlets, ServletHolder.class)); + _servletHandler.setServletMappings((ServletMapping[]) LazyList.toArray(_servletMappings, ServletMapping.class)); + _context.setEventListeners((EventListener[]) LazyList.toArray(_listeners, EventListener.class)); + _context.setWelcomeFiles((String[]) LazyList.toArray(_welcomeFiles, String.class)); + // TODO jaspi check this + if (_securityHandler instanceof ConstraintAware) + { + for (ConstraintMapping m:_constraintMappings) + ((ConstraintAware) _securityHandler).addConstraintMapping(m); + for (String r:_roles) + ((ConstraintAware) _securityHandler).addRole(r); + } + + if (_errorPages != null && _context.getErrorHandler() instanceof ErrorPageErrorHandler) + ((ErrorPageErrorHandler)_context.getErrorHandler()).setErrorPages(_errorPages); + + _roles.clear(); + _constraintMappings.clear(); + _metaData = null; + _context = null; + } + + public void visitContextParam (Descriptor descriptor, XmlParser.Node node) + { + String name = node.getString("param-name", false, true); + String value = node.getString("param-value", false, true); + Origin o = _metaData.getOrigin("context-param."+name); + switch (o) + { + case NotSet: + { + //just set it + _context.getInitParams().put(name, value); + _metaData.setOrigin("context-param."+name, descriptor); + break; + } + case WebXml: + case WebDefaults: + case WebOverride: + { + //previously set by a web xml, allow other web xml files to override + if (!(descriptor instanceof FragmentDescriptor)) + { + _context.getInitParams().put(name, value); + _metaData.setOrigin("context-param."+name, descriptor); + } + break; + } + case WebFragment: + { + //previously set by a web-fragment, this fragment's value must be the same + if (descriptor instanceof FragmentDescriptor) + { + if (!((String)_context.getInitParams().get(name)).equals(value)) + throw new IllegalStateException("Conflicting context-param "+name+"="+value+" in "+descriptor.getResource()); + } + break; + } + } + if (Log.isDebugEnabled()) Log.debug("ContextParam: " + name + "=" + value); + + } + + + /* ------------------------------------------------------------ */ + protected void visitDisplayName(Descriptor descriptor, XmlParser.Node node) + { + //Servlet Spec 3.0 p. 74 Ignore from web-fragments + if (!(descriptor instanceof FragmentDescriptor)) + { + _context.setDisplayName(node.toString(false, true)); + _metaData.setOrigin("display-name", descriptor); + } + } + + protected void visitServlet(Descriptor descriptor, XmlParser.Node node) + { + String id = node.getAttribute("id"); + + // initialize holder + String servlet_name = node.getString("servlet-name", false, true); + ServletHolder holder = _servletHandler.getServlet(servlet_name); + + /* + * If servlet of that name does not already exist, create it. + */ + if (holder == null) + { + holder = _servletHandler.newServletHolder(); + holder.setName(servlet_name); + _servlets = LazyList.add(_servlets, holder); + } + ServletRegistration.Dynamic registration = holder.getRegistration(); + + // init params + Iterator iParamsIter = node.iterator("init-param"); + while (iParamsIter.hasNext()) + { + XmlParser.Node paramNode = (XmlParser.Node) iParamsIter.next(); + String pname = paramNode.getString("param-name", false, true); + String pvalue = paramNode.getString("param-value", false, true); + + Origin origin = _metaData.getOrigin(servlet_name+".servlet.init-param."+pname); + + switch (origin) + { + case NotSet: + { + //init-param not already set, so set it + + registration.setInitParameter(pname, pvalue); + _metaData.setOrigin(servlet_name+".servlet.init-param."+pname, descriptor); + break; + } + case WebXml: + case WebDefaults: + case WebOverride: + { + //previously set by a web xml descriptor, if we're parsing another web xml descriptor allow override + //otherwise just ignore it + if (!(descriptor instanceof FragmentDescriptor)) + { + registration.setInitParameter(pname, pvalue); + _metaData.setOrigin(servlet_name+".servlet.init-param."+pname, descriptor); + } + break; + } + case WebFragment: + { + //previously set by a web-fragment, make sure that the value matches, otherwise its an error + if (!registration.getInitParameter(pname).equals(pvalue)) + throw new IllegalStateException("Mismatching init-param "+pname+"="+pvalue+" in "+descriptor.getResource()); + break; + } + } + } + + String servlet_class = node.getString("servlet-class", false, true); + + // Handle JSP + if (id != null && id.equals("jsp")) + { + _jspServletName = servlet_name; + _jspServletClass = servlet_class; + try + { + Loader.loadClass(this.getClass(), servlet_class); + _hasJSP = true; + } + catch (ClassNotFoundException e) + { + Log.info("NO JSP Support for {}, did not find {}", _context.getContextPath(), servlet_class); + _hasJSP = false; + _jspServletClass = servlet_class = "org.eclipse.jetty.servlet.NoJspServlet"; + } + if (registration.getInitParameter("scratchdir") == null) + { + File tmp = _context.getTempDirectory(); + File scratch = new File(tmp, "jsp"); + if (!scratch.exists()) scratch.mkdir(); + registration.setInitParameter("scratchdir", scratch.getAbsolutePath()); + + if ("?".equals(registration.getInitParameter("classpath"))) + { + String classpath = _context.getClassPath(); + Log.debug("classpath=" + classpath); + if (classpath != null) + registration.setInitParameter("classpath", classpath); + } + } + + /* Set the webapp's classpath for Jasper */ + _context.setAttribute("org.apache.catalina.jsp_classpath", _context.getClassPath()); + /* Set the system classpath for Jasper */ + registration.setInitParameter("com.sun.appserv.jsp.classpath", getSystemClassPath()); + } + + //Set the servlet-class + if (servlet_class != null) + { + descriptor.addClassName(servlet_class); + + Origin o = _metaData.getOrigin(servlet_name+".servlet.servlet-class"); + switch (o) + { + case NotSet: + { + //the class of the servlet has not previously been set, so set it + holder.setClassName(servlet_class); + _metaData.setOrigin(servlet_name+".servlet.servlet-class", descriptor); + break; + } + case WebXml: + case WebDefaults: + case WebOverride: + { + //the class of the servlet was set by a web xml file, only allow web-override/web-default to change it + if (!(descriptor instanceof FragmentDescriptor)) + { + holder.setClassName(servlet_class); + _metaData.setOrigin(servlet_name+".servlet.servlet-class", descriptor); + } + break; + } + case WebFragment: + { + //the class was set by another fragment, ensure this fragment's value is the same + if (!servlet_class.equals(holder.getClassName())) + throw new IllegalStateException("Conflicting servlet-class "+servlet_class+" in "+descriptor.getResource()); + break; + } + } + } + + // Handler JSP file + String jsp_file = node.getString("jsp-file", false, true); + if (jsp_file != null) + { + holder.setForcedPath(jsp_file); + holder.setClassName(_jspServletClass); + } + + // handle load-on-startup + XmlParser.Node startup = node.get("load-on-startup"); + if (startup != null) + { + String s = startup.toString(false, true).toLowerCase(); + int order = 0; + if (s.startsWith("t")) + { + Log.warn("Deprecated boolean load-on-startup. Please use integer"); + order = 1; + } + else + { + try + { + if (s != null && s.trim().length() > 0) order = Integer.parseInt(s); + } + catch (Exception e) + { + Log.warn("Cannot parse load-on-startup " + s + ". Please use integer"); + Log.ignore(e); + } + } + + Origin o = _metaData.getOrigin(servlet_name+".servlet.load-on-startup"); + switch (o) + { + case NotSet: + { + //not already set, so set it now + registration.setLoadOnStartup(order); + _metaData.setOrigin(servlet_name+".servlet.load-on-startup", descriptor); + break; + } + case WebXml: + case WebDefaults: + case WebOverride: + { + //if it was already set by a web xml descriptor and we're parsing another web xml descriptor, then override it + if (!(descriptor instanceof FragmentDescriptor)) + { + registration.setLoadOnStartup(order); + _metaData.setOrigin(servlet_name+".servlet.load-on-startup", descriptor); + } + break; + } + case WebFragment: + { + //it was already set by another fragment, if we're parsing a fragment, the values must match + if (order != holder.getInitOrder()) + throw new IllegalStateException("Conflicting load-on-startup value in "+descriptor.getResource()); + break; + } + } + } + + Iterator sRefsIter = node.iterator("security-role-ref"); + while (sRefsIter.hasNext()) + { + XmlParser.Node securityRef = (XmlParser.Node) sRefsIter.next(); + String roleName = securityRef.getString("role-name", false, true); + String roleLink = securityRef.getString("role-link", false, true); + if (roleName != null && roleName.length() > 0 && roleLink != null && roleLink.length() > 0) + { + if (Log.isDebugEnabled()) Log.debug("link role " + roleName + " to " + roleLink + " for " + this); + Origin o = _metaData.getOrigin(servlet_name+".servlet.role-name."+roleName); + switch (o) + { + case NotSet: + { + //set it + holder.setUserRoleLink(roleName, roleLink); + _metaData.setOrigin(servlet_name+".servlet.role-name."+roleName, descriptor); + break; + } + case WebXml: + case WebDefaults: + case WebOverride: + { + //only another web xml descriptor (web-default,web-override web.xml) can override an already set value + if (!(descriptor instanceof FragmentDescriptor)) + { + holder.setUserRoleLink(roleName, roleLink); + _metaData.setOrigin(servlet_name+".servlet.role-name."+roleName, descriptor); + } + break; + } + case WebFragment: + { + if (!holder.getUserRoleLink(roleName).equals(roleLink)) + throw new IllegalStateException("Conflicting role-link for role-name "+roleName+" for servlet "+servlet_name+" in "+descriptor.getResource()); + break; + } + } + } + else + { + Log.warn("Ignored invalid security-role-ref element: " + "servlet-name=" + holder.getName() + ", " + securityRef); + } + } + + + XmlParser.Node run_as = node.get("run-as"); + if (run_as != null) + { + String roleName = run_as.getString("role-name", false, true); + + if (roleName != null) + { + Origin o = _metaData.getOrigin(servlet_name+".servlet.run-as"); + switch (o) + { + case NotSet: + { + //run-as not set, so set it + registration.setRunAsRole(roleName); + _metaData.setOrigin(servlet_name+".servlet.run-as", descriptor); + break; + } + case WebXml: + case WebDefaults: + case WebOverride: + { + //run-as was set by a web xml, only allow it to be changed if we're currently parsing another web xml(override/default) + if (!(descriptor instanceof FragmentDescriptor)) + { + registration.setRunAsRole(roleName); + _metaData.setOrigin(servlet_name+".servlet.run-as", descriptor); + } + break; + } + case WebFragment: + { + //run-as was set by another fragment, this fragment must show the same value + if (!registration.getRunAsRole().equals(roleName)) + throw new IllegalStateException("Conflicting run-as role "+roleName+" for servlet "+servlet_name+" in "+descriptor.getResource()); + break; + } + } + } + } + } + + protected void visitServletMapping(Descriptor descriptor, XmlParser.Node node) + { + //Servlet Spec 3.0, p74 + //servlet-mappings are always additive, whether from web xml descriptors (web.xml/web-default.xml/web-override.xml) or web-fragments. + String servlet_name = node.getString("servlet-name", false, true); + ServletMapping mapping = new ServletMapping(); + mapping.setServletName(servlet_name); + + if (_metaData.getOrigin(servlet_name+".servlet.mappings") == Origin.NotSet) + _metaData.setOrigin(servlet_name+".servlet.mappings", descriptor); + + ArrayList paths = new ArrayList(); + Iterator iter = node.iterator("url-pattern"); + while (iter.hasNext()) + { + String p = ((XmlParser.Node) iter.next()).toString(false, true); + p = normalizePattern(p); + paths.add(p); + } + mapping.setPathSpecs((String[]) paths.toArray(new String[paths.size()])); + _servletMappings = LazyList.add(_servletMappings, mapping); + } + + + protected void visitSessionConfig(Descriptor descriptor, XmlParser.Node node) + { + XmlParser.Node tNode = node.get("session-timeout"); + if (tNode != null) + { + int timeout = Integer.parseInt(tNode.toString(false, true)); + _context.getSessionHandler().getSessionManager().setMaxInactiveInterval(timeout * 60); + } + } + + protected void visitMimeMapping(Descriptor descriptor, XmlParser.Node node) + { + String extension = node.getString("extension", false, true); + if (extension != null && extension.startsWith(".")) + extension = extension.substring(1); + String mimeType = node.getString("mime-type", false, true); + if (extension != null) + { + Origin o = _metaData.getOrigin("extension."+extension); + switch (o) + { + case NotSet: + { + //no mime-type set for the extension yet + _context.getMimeTypes().addMimeMapping(extension, mimeType); + _metaData.setOrigin("extension."+extension, descriptor); + break; + } + case WebXml: + case WebDefaults: + case WebOverride: + { + //a mime-type was set for the extension in a web xml, only allow web-default/web-override to change + if (!(descriptor instanceof FragmentDescriptor)) + { + _context.getMimeTypes().addMimeMapping(extension, mimeType); + _metaData.setOrigin("extension."+extension, descriptor); + } + break; + } + case WebFragment: + { + //a web-fragment set the value, all web-fragments must have the same value + if (!_context.getMimeTypes().getMimeByExtension("."+extension).equals(_context.getMimeTypes().CACHE.lookup(mimeType))) + throw new IllegalStateException("Conflicting mime-type "+mimeType+" for extension "+extension+" in "+descriptor.getResource()); + break; + } + } + } + } + + protected void visitWelcomeFileList(Descriptor descriptor, XmlParser.Node node) + { + Origin o = _metaData.getOrigin("welcome-file-list"); + switch (o) + { + case NotSet: + { + _metaData.setOrigin("welcome-file-list", descriptor); + addWelcomeFiles(node); + break; + } + case WebXml: + { + //web.xml set the welcome-file-list, all other descriptors then just merge in + addWelcomeFiles(node); + break; + } + case WebDefaults: + { + //if web-defaults set the welcome-file-list first and + //we're processing web.xml then reset the welcome-file-list + if (!(descriptor instanceof DefaultsDescriptor) && !(descriptor instanceof OverrideDescriptor) && !(descriptor instanceof FragmentDescriptor)) + { + _welcomeFiles = null; + } + addWelcomeFiles(node); + break; + } + case WebOverride: + { + //web-override set the list, all other descriptors just merge in + addWelcomeFiles(node); + break; + } + case WebFragment: + { + //A web-fragment first set the welcome-file-list. Other descriptors just add. + addWelcomeFiles(node); + break; + } + } + } + + protected void visitLocaleEncodingList(Descriptor descriptor, XmlParser.Node node) + { + Iterator iter = node.iterator("locale-encoding-mapping"); + while (iter.hasNext()) + { + XmlParser.Node mapping = (XmlParser.Node) iter.next(); + String locale = mapping.getString("locale", false, true); + String encoding = mapping.getString("encoding", false, true); + + if (encoding != null) + { + Origin o = _metaData.getOrigin("locale-encoding."+locale); + switch (o) + { + case NotSet: + { + //no mapping for the locale yet, so set it + _context.addLocaleEncoding(locale, encoding); + _metaData.setOrigin("locale-encoding."+locale, descriptor); + break; + } + case WebXml: + case WebDefaults: + case WebOverride: + { + //a value was set in a web descriptor, only allow another web descriptor to change it (web-default/web-override) + if (!(descriptor instanceof FragmentDescriptor)) + { + _context.addLocaleEncoding(locale, encoding); + _metaData.setOrigin("locale-encoding."+locale, descriptor); + } + break; + } + case WebFragment: + { + //a value was set by a web-fragment, all fragments must have the same value + if (!encoding.equals(_context.getLocaleEncoding(locale))) + throw new IllegalStateException("Conflicting locale-encoding mapping for locale "+locale+" in "+descriptor.getResource()); + break; + } + } + } + } + } + + protected void visitErrorPage(Descriptor descriptor, XmlParser.Node node) + { + String error = node.getString("error-code", false, true); + if (error == null || error.length() == 0) error = node.getString("exception-type", false, true); + String location = node.getString("location", false, true); + + if (_errorPages == null) + _errorPages = new HashMap(); + + Origin o = _metaData.getOrigin("error."+error); + switch (o) + { + case NotSet: + { + //no error page setup for this code or exception yet + _errorPages.put(error, location); + _metaData.setOrigin("error."+error, descriptor); + break; + } + case WebXml: + case WebDefaults: + case WebOverride: + { + //an error page setup was set in web.xml, only allow other web xml descriptors to override it + if (!(descriptor instanceof FragmentDescriptor)) + { + _errorPages.put(error, location); + _metaData.setOrigin("error."+error, descriptor); + } + break; + } + case WebFragment: + { + //another web fragment set the same error code or exception, if its different its an error + if (!_errorPages.get(error).equals(location)) + throw new IllegalStateException("Conflicting error-code or exception-type "+error+" in "+descriptor.getResource()); + break; + } + } + + } + + protected void addWelcomeFiles (XmlParser.Node node) + { + Iterator iter = node.iterator("welcome-file"); + while (iter.hasNext()) + { + XmlParser.Node indexNode = (XmlParser.Node) iter.next(); + String welcome = indexNode.toString(false, true); + + //Servlet Spec 3.0 p. 74 welcome files are additive + _welcomeFiles = LazyList.add(_welcomeFiles, welcome); + } + } + + protected void visitTagLib(Descriptor descriptor, XmlParser.Node node) + { + //Additive across web.xml and web-fragment.xml + String uri = node.getString("taglib-uri", false, true); + String location = node.getString("taglib-location", false, true); + + _context.setResourceAlias(uri, location); + } + + protected void visitJspConfig(Descriptor descriptor, XmlParser.Node node) + { + for (int i = 0; i < node.size(); i++) + { + Object o = node.get(i); + if (o instanceof XmlParser.Node && "taglib".equals(((XmlParser.Node) o).getTag())) + visitTagLib(descriptor, (XmlParser.Node) o); + } + + // Map URLs from jsp property groups to JSP servlet. + // this is more JSP stupidness creaping into the servlet spec + Iterator iter = node.iterator("jsp-property-group"); + Object paths = null; + while (iter.hasNext()) + { + XmlParser.Node group = (XmlParser.Node) iter.next(); + Iterator iter2 = group.iterator("url-pattern"); + while (iter2.hasNext()) + { + String url = ((XmlParser.Node) iter2.next()).toString(false, true); + url = normalizePattern(url); + paths = LazyList.add(paths, url); + } + } + + if (LazyList.size(paths) > 0) + { + String jspName = getJSPServletName(); + if (jspName != null) + { + ServletMapping mapping = new ServletMapping(); + mapping.setServletName(jspName); + mapping.setPathSpecs(LazyList.toStringArray(paths)); + _servletMappings = LazyList.add(_servletMappings, mapping); + } + } + } + + protected void visitSecurityConstraint(Descriptor descriptor, XmlParser.Node node) + { + Constraint scBase = new Constraint(); + + //ServletSpec 3.0, p74 security-constraints, as minOccurs > 1, are additive + //across fragments + try + { + XmlParser.Node auths = node.get("auth-constraint"); + + if (auths != null) + { + scBase.setAuthenticate(true); + // auth-constraint + Iterator iter = auths.iterator("role-name"); + Object roles = null; + while (iter.hasNext()) + { + String role = ((XmlParser.Node) iter.next()).toString(false, true); + roles = LazyList.add(roles, role); + } + scBase.setRoles(LazyList.toStringArray(roles)); + } + + XmlParser.Node data = node.get("user-data-constraint"); + if (data != null) + { + data = data.get("transport-guarantee"); + String guarantee = data.toString(false, true).toUpperCase(); + if (guarantee == null || guarantee.length() == 0 || "NONE".equals(guarantee)) + scBase.setDataConstraint(Constraint.DC_NONE); + else if ("INTEGRAL".equals(guarantee)) + scBase.setDataConstraint(Constraint.DC_INTEGRAL); + else if ("CONFIDENTIAL".equals(guarantee)) + scBase.setDataConstraint(Constraint.DC_CONFIDENTIAL); + else + { + Log.warn("Unknown user-data-constraint:" + guarantee); + scBase.setDataConstraint(Constraint.DC_CONFIDENTIAL); + } + } + Iterator iter = node.iterator("web-resource-collection"); + while (iter.hasNext()) + { + XmlParser.Node collection = (XmlParser.Node) iter.next(); + String name = collection.getString("web-resource-name", false, true); + Constraint sc = (Constraint) scBase.clone(); + sc.setName(name); + + Iterator iter2 = collection.iterator("url-pattern"); + while (iter2.hasNext()) + { + String url = ((XmlParser.Node) iter2.next()).toString(false, true); + url = normalizePattern(url); + + Iterator iter3 = collection.iterator("http-method"); + if (iter3.hasNext()) + { + while (iter3.hasNext()) + { + String method = ((XmlParser.Node) iter3.next()).toString(false, true); + ConstraintMapping mapping = new ConstraintMapping(); + mapping.setMethod(method); + mapping.setPathSpec(url); + mapping.setConstraint(sc); + _constraintMappings.add(mapping); + } + } + else + { + ConstraintMapping mapping = new ConstraintMapping(); + mapping.setPathSpec(url); + mapping.setConstraint(sc); + _constraintMappings.add(mapping); + } + } + } + } + catch (CloneNotSupportedException e) + { + Log.warn(e); + } + } + + protected void visitLoginConfig(Descriptor descriptor, XmlParser.Node node) throws Exception + { + //ServletSpec 3.0 p74 says elements present 0/1 time if specified in web.xml take + //precendece over any web-fragment. If not specified in web.xml, then if specified + //in a web-fragment must be the same across all web-fragments. + XmlParser.Node method = node.get("auth-method"); + if (method != null) + { + //handle auth-method merge + Origin o = _metaData.getOrigin("auth-method"); + switch (o) + { + case NotSet: + { + //not already set, so set it now + _securityHandler.setAuthMethod(method.toString(false, true)); + _metaData.setOrigin("auth-method", descriptor); + break; + } + case WebXml: + case WebDefaults: + case WebOverride: + { + //if it was already set by a web xml descriptor and we're parsing another web xml descriptor, then override it + if (!(descriptor instanceof FragmentDescriptor)) + { + _securityHandler.setAuthMethod(method.toString(false, true)); + _metaData.setOrigin("auth-method", descriptor); + } + break; + } + case WebFragment: + { + //it was already set by another fragment, if we're parsing a fragment, the values must match + if (!_securityHandler.getAuthMethod().equals(method.toString(false, true))) + throw new IllegalStateException("Conflicting auth-method value in "+descriptor.getResource()); + break; + } + } + + //handle realm-name merge + XmlParser.Node name = node.get("realm-name"); + String nameStr = (name == null ? "default" : name.toString(false, true)); + o = _metaData.getOrigin("realm-name"); + switch (o) + { + case NotSet: + { + //no descriptor has set the realm-name yet, so set it + _securityHandler.setRealmName(nameStr); + _metaData.setOrigin("realm-name", descriptor); + break; + } + case WebXml: + case WebDefaults: + case WebOverride: + { + //set by a web xml file (web.xml/web-default.xm/web-override.xml), only allow it to be changed by another web xml file + if (!(descriptor instanceof FragmentDescriptor)) + { + _securityHandler.setRealmName(nameStr); + _metaData.setOrigin("realm-name", descriptor); + } + break; + } + case WebFragment: + { + //a fragment set it, and we must be parsing another fragment, so the values must match + if (!_securityHandler.getRealmName().equals(nameStr)) + throw new IllegalStateException("Conflicting realm-name value in "+descriptor.getResource()); + break; + } + } + + if (Constraint.__FORM_AUTH.equals(_securityHandler.getAuthMethod())) + { + XmlParser.Node formConfig = node.get("form-login-config"); + if (formConfig != null) + { + String loginPageName = null; + XmlParser.Node loginPage = formConfig.get("form-login-page"); + if (loginPage != null) + loginPageName = loginPage.toString(false, true); + String errorPageName = null; + XmlParser.Node errorPage = formConfig.get("form-error-page"); + if (errorPage != null) + errorPageName = errorPage.toString(false, true); + + //handle form-login-page + o = _metaData.getOrigin("form-login-page"); + switch (o) + { + case NotSet: + { + //Never been set before, so accept it + _securityHandler.setInitParameter(FormAuthenticator.__FORM_LOGIN_PAGE,loginPageName); + _metaData.setOrigin("form-login-page",descriptor); + break; + } + case WebXml: + case WebDefaults: + case WebOverride: + { + //a web xml descriptor previously set it, only allow another one to change it (web.xml/web-default.xml/web-override.xml) + if (!(descriptor instanceof FragmentDescriptor)) + { + _securityHandler.setInitParameter(FormAuthenticator.__FORM_LOGIN_PAGE,loginPageName); + _metaData.setOrigin("form-login-page",descriptor); + } + break; + } + case WebFragment: + { + //a web-fragment previously set it. We must be parsing yet another web-fragment, so the values must agree + if (!_securityHandler.getInitParameter(FormAuthenticator.__FORM_LOGIN_PAGE).equals(loginPageName)) + throw new IllegalStateException("Conflicting form-login-page value in "+descriptor.getResource()); + break; + } + } + + //handle form-error-page + o = _metaData.getOrigin("form-error-page"); + switch (o) + { + case NotSet: + { + //Never been set before, so accept it + _securityHandler.setInitParameter(FormAuthenticator.__FORM_ERROR_PAGE,errorPageName); + _metaData.setOrigin("form-error-page",descriptor); + break; + } + case WebXml: + case WebDefaults: + case WebOverride: + { + //a web xml descriptor previously set it, only allow another one to change it (web.xml/web-default.xml/web-override.xml) + if (!(descriptor instanceof FragmentDescriptor)) + { + _securityHandler.setInitParameter(FormAuthenticator.__FORM_ERROR_PAGE,errorPageName); + _metaData.setOrigin("form-error-page",descriptor); + } + break; + } + case WebFragment: + { + //a web-fragment previously set it. We must be parsing yet another web-fragment, so the values must agree + if (!_securityHandler.getInitParameter(FormAuthenticator.__FORM_ERROR_PAGE).equals(errorPageName)) + throw new IllegalStateException("Conflicting form-error-page value in "+descriptor.getResource()); + break; + } + } + } + else + { + throw new IllegalStateException("!form-login-config"); + } + } + } + } + + protected void visitSecurityRole(Descriptor descriptor, XmlParser.Node node) + { + //ServletSpec 3.0, p74 elements with multiplicity >1 are additive when merged + XmlParser.Node roleNode = node.get("role-name"); + String role = roleNode.toString(false, true); + _roles.add(role); + } + + + protected void visitFilter(Descriptor descriptor, XmlParser.Node node) + { + String name = node.getString("filter-name", false, true); + FilterHolder holder = _servletHandler.getFilter(name); + if (holder == null) + { + holder = _servletHandler.newFilterHolder(); + holder.setName(name); + _filters = LazyList.add(_filters, holder); + } + + String filter_class = node.getString("filter-class", false, true); + if (filter_class != null) + { + descriptor.addClassName(filter_class); + + Origin o = _metaData.getOrigin(name+".filter.filter-class"); + switch (o) + { + case NotSet: + { + //no class set yet + holder.setClassName(filter_class); + _metaData.setOrigin(name+".filter.filter-class", descriptor); + break; + } + case WebXml: + case WebDefaults: + case WebOverride: + { + //filter class was set in web.xml, only allow other web xml descriptors (override/default) to change it + if (!(descriptor instanceof FragmentDescriptor)) + { + holder.setClassName(filter_class); + _metaData.setOrigin(name+".filter.filter-class", descriptor); + } + break; + } + case WebFragment: + { + //the filter class was set up by a web fragment, all fragments must be the same + if (!holder.getClassName().equals(filter_class)) + throw new IllegalStateException("Conflicting filter-class for filter "+name+" in "+descriptor.getResource()); + break; + } + } + + } + + Iterator iter = node.iterator("init-param"); + while (iter.hasNext()) + { + XmlParser.Node paramNode = (XmlParser.Node) iter.next(); + String pname = paramNode.getString("param-name", false, true); + String pvalue = paramNode.getString("param-value", false, true); + + Origin origin = _metaData.getOrigin(name+".filter.init-param."+pname); + switch (origin) + { + case NotSet: + { + //init-param not already set, so set it + holder.setInitParameter(pname, pvalue); + _metaData.setOrigin(name+".filter.init-param."+pname, descriptor); + break; + } + case WebXml: + case WebDefaults: + case WebOverride: + { + //previously set by a web xml descriptor, if we're parsing another web xml descriptor allow override + //otherwise just ignore it + if (!(descriptor instanceof FragmentDescriptor)) + { + holder.setInitParameter(pname, pvalue); + _metaData.setOrigin(name+".filter.init-param."+pname, descriptor); + } + break; + } + case WebFragment: + { + //previously set by a web-fragment, make sure that the value matches, otherwise its an error + if (!holder.getInitParameter(pname).equals(pvalue)) + throw new IllegalStateException("Mismatching init-param "+pname+"="+pvalue+" in "+descriptor.getResource()); + break; + } + } + } + + String async=node.getString("async-supported",false,true); + if (async!=null) + holder.setAsyncSupported(async.length()==0||Boolean.valueOf(async)); + if (async!=null) + { + boolean val = async.length()==0||Boolean.valueOf(async); + Origin o = _metaData.getOrigin(name+".filter.async-supported"); + switch (o) + { + case NotSet: + { + //set it + holder.setAsyncSupported(val); + _metaData.setOrigin(name+".filter.async-supported", descriptor); + break; + } + case WebXml: + case WebDefaults: + case WebOverride: + { + //async-supported set by previous web xml descriptor, only allow override if we're parsing another web descriptor(web.xml/web-override.xml/web-default.xml) + if (!(descriptor instanceof FragmentDescriptor)) + { + holder.setAsyncSupported(val); + _metaData.setOrigin(name+".filter.async-supported", descriptor); + } + break; + } + case WebFragment: + { + //async-supported set by another fragment, this fragment's value must match + if (holder.isAsyncSupported() != val) + throw new IllegalStateException("Conflicting async-supported="+async+" for filter "+name+" in "+descriptor.getResource()); + break; + } + } + } + + } + + protected void visitFilterMapping(Descriptor descriptor, XmlParser.Node node) + { + //Servlet Spec 3.0, p74 + //filter-mappings are always additive, whether from web xml descriptors (web.xml/web-default.xml/web-override.xml) or web-fragments. + + String filter_name = node.getString("filter-name", false, true); + + FilterMapping mapping = new FilterMapping(); + + mapping.setFilterName(filter_name); + + ArrayList paths = new ArrayList(); + Iterator iter = node.iterator("url-pattern"); + while (iter.hasNext()) + { + String p = ((XmlParser.Node) iter.next()).toString(false, true); + p = normalizePattern(p); + paths.add(p); + } + mapping.setPathSpecs((String[]) paths.toArray(new String[paths.size()])); + + ArrayList names = new ArrayList(); + iter = node.iterator("servlet-name"); + while (iter.hasNext()) + { + String n = ((XmlParser.Node) iter.next()).toString(false, true); + names.add(n); + } + mapping.setServletNames((String[]) names.toArray(new String[names.size()])); + + + List dispatches = new ArrayList(); + iter=node.iterator("dispatcher"); + while(iter.hasNext()) + { + String d=((XmlParser.Node)iter.next()).toString(false,true); + dispatches.add(FilterMapping.dispatch(d)); + } + + if (dispatches.size()>0) + mapping.setDispatcherTypes(EnumSet.copyOf(dispatches)); + + _filterMappings = LazyList.add(_filterMappings, mapping); + } + + + protected void visitListener(Descriptor descriptor, XmlParser.Node node) + { + String className = node.getString("listener-class", false, true); + Object listener = null; + try + { + if (className != null && className.length()> 0) + { + descriptor.addClassName(className); + + //Servlet Spec 3.0 p 74 + //Duplicate listener declarations don't result in duplicate listener instances + if (!LazyList.contains(_listenerClassNames, className)) + { + LazyList.add(_listenerClassNames, className); + Class listenerClass = _context.loadClass(className); + listener = newListenerInstance(listenerClass); + if (!(listener instanceof EventListener)) + { + Log.warn("Not an EventListener: " + listener); + return; + } + _metaData.setOrigin(className+".listener", descriptor); + _listeners = LazyList.add(_listeners, listener); + } + } + } + catch (Exception e) + { + Log.warn("Could not instantiate listener " + className, e); + return; + } + } + + protected void visitDistributable(Descriptor descriptor, XmlParser.Node node) + { + // the element has no content, so its simple presence + // indicates that the webapp is distributable... + //Servlet Spec 3.0 p.74 distributable only if all fragments are distributable + descriptor.setDistributable(true); + } + + protected Object newListenerInstance(Class clazz) throws ServletException, InstantiationException, IllegalAccessException + { + try + { + return ((ServletContextHandler.Context)_context.getServletContext()).createListener(clazz); + } + catch (ServletException se) + { + Throwable cause = se.getRootCause(); + if (cause instanceof InstantiationException) + throw (InstantiationException)cause; + if (cause instanceof IllegalAccessException) + throw (IllegalAccessException)cause; + throw se; + } + } + + protected String getJSPServletName() + { + if (_jspServletName == null) + { + Map.Entry entry = _context.getServletHandler().getHolderEntry("test.jsp"); + if (entry != null) + { + ServletHolder holder = (ServletHolder) entry.getValue(); + _jspServletName = holder.getName(); + } + } + return _jspServletName; + } + + protected String normalizePattern(String p) + { + if (p != null && p.length() > 0 && !p.startsWith("/") && !p.startsWith("*")) return "/" + p; + return p; + } + + /** + * Generate the classpath (as a string) of all classloaders + * above the webapp's classloader. + * + * This is primarily used for jasper. + * @return + */ + protected String getSystemClassPath () + { + ClassLoader loader = _context.getClassLoader(); + if (loader.getParent() != null) + loader = loader.getParent(); + + StringBuilder classpath=new StringBuilder(); + while (loader != null && (loader instanceof URLClassLoader)) + { + URL[] urls = ((URLClassLoader)loader).getURLs(); + if (urls != null) + { + for (int i=0;i0) + classpath.append(File.pathSeparatorChar); + classpath.append(file.getAbsolutePath()); + } + } + catch (IOException e) + { + Log.debug(e); + } + } + } + loader = loader.getParent(); + } + return classpath.toString(); + } +} diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/TagLibConfiguration.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/TagLibConfiguration.java index d5b349b35cf..f5dd88f39ef 100644 --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/TagLibConfiguration.java +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/TagLibConfiguration.java @@ -45,7 +45,8 @@ import org.eclipse.jetty.xml.XmlParser; * </bile> * * - * + * TODO - this has been superceded by the new TldScanner in jasper which uses ServletContainerInitializer to + * find all the listeners in tag libs and register them. */ public class TagLibConfiguration implements Configuration { diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java index cea44168f26..139d8681297 100644 --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java @@ -371,11 +371,16 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL - // Prepare for configuration + // Prepare for configuration + //Make a new MetaData to hold descriptor and annotation metadata + MetaData metadata = new MetaData(this); + setAttribute(MetaData.METADATA, metadata); + for (int i=0;i<_configurations.length;i++) _configurations[i].preConfigure(this); super.doStart(); + // Clean up after configuration for (int i=0;i<_configurations.length;i++) @@ -986,12 +991,12 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL protected void startContext() throws Exception { - - // Configure webapp for (int i=0;i<_configurations.length;i++) _configurations[i].configure(this); - + + //resolve the metadata + ((MetaData)getAttribute(MetaData.METADATA)).resolve(); super.startContext(); } diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java index 922952ac150..a4fd810f3f6 100644 --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java @@ -23,6 +23,7 @@ public class WebInfConfiguration implements Configuration public static final String TEMPDIR_CREATED = "org.eclipse.jetty.tmpdirCreated"; public static final String CONTAINER_JAR_RESOURCES = "org.eclipse.jetty.containerJars"; public static final String WEB_INF_JAR_RESOURCES = "org.eclipse.jetty.webInfJars"; + public static final String WEB_INF_ORDERED_JAR_RESOURCES = "org.eclipse.jetty.webInfOrderedJars"; 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"; @@ -96,6 +97,7 @@ public class WebInfConfiguration implements Configuration } }; List jars = findJars(context); + //Convert to uris for matching URI[] uris = null; if (jars != null) diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebXmlConfiguration.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebXmlConfiguration.java index f392ab188e9..abb952d479c 100644 --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebXmlConfiguration.java +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebXmlConfiguration.java @@ -1,5 +1,5 @@ // ======================================================================== -// Copyright (c) 2003-2009 Mort Bay Consulting Pty. Ltd. +// Copyright (c) 2003-2010 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 @@ -34,52 +34,50 @@ public class WebXmlConfiguration implements Configuration /* ------------------------------------------------------------------------------- */ /** - * Process webdefaults.xml + * * * */ public void preConfigure (WebAppContext context) throws Exception { - // cannot configure if the context is already started - if (context.isStarted()) - { - if (Log.isDebugEnabled()) - { - Log.debug("Cannot configure webapp after it is started"); - } - return; - } - - //Get or create a processor to handle webdefaults, web.xml and the fragments - WebXmlProcessor processor = (WebXmlProcessor)context.getAttribute(WebXmlProcessor.WEB_PROCESSOR); - if (processor == null) - { - processor = new WebXmlProcessor (context); - context.setAttribute(WebXmlProcessor.WEB_PROCESSOR, processor); - } - - //handle webdefault.xml + + MetaData metaData = (MetaData)context.getAttribute(MetaData.METADATA); + if (metaData == null) + throw new IllegalStateException("No metadata"); + + + //parse webdefault.xml String defaultsDescriptor = context.getDefaultsDescriptor(); if (defaultsDescriptor != null && defaultsDescriptor.length() > 0) { Resource dftResource = Resource.newSystemResource(defaultsDescriptor); if (dftResource == null) dftResource = context.newResource(defaultsDescriptor); - processor.parseDefaults (dftResource); - processor.processDefaults(); + metaData.setDefaults (dftResource); + } //parse, but don't process web.xml Resource webxml = findWebXml(context); if (webxml != null) { - processor.parseWebXml(webxml); + metaData.setWebXml(webxml); + } + + //parse but don't process override-web.xml + String overrideDescriptor = context.getOverrideDescriptor(); + if (overrideDescriptor != null && overrideDescriptor.length() > 0) + { + Resource orideResource = Resource.newSystemResource(overrideDescriptor); + if (orideResource == null) + orideResource = context.newResource(overrideDescriptor); + metaData.setOverride(orideResource); } } /* ------------------------------------------------------------------------------- */ /** - * Process web.xml, web-fragment.xml(s), override-web.xml + * Process web-default.xml, web.xml, override-web.xml * */ public void configure (WebAppContext context) throws Exception @@ -90,35 +88,35 @@ public class WebXmlConfiguration implements Configuration if (Log.isDebugEnabled()) Log.debug("Cannot configure webapp after it is started"); return; } - - WebXmlProcessor processor = (WebXmlProcessor)context.getAttribute(WebXmlProcessor.WEB_PROCESSOR); - if (processor == null) - { - processor = new WebXmlProcessor (context); - context.setAttribute(WebXmlProcessor.WEB_PROCESSOR, processor); - } - //process web.xml (the effective web.xml???) - processor.processWebXml(); + MetaData metaData = (MetaData)context.getAttribute(MetaData.METADATA); + if (metaData == null) + throw new IllegalStateException("No metadata"); + + metaData.addDescriptorProcessor(new StandardDescriptorProcessor()); - //process override-web.xml - String overrideDescriptor = context.getOverrideDescriptor(); - if (overrideDescriptor != null && overrideDescriptor.length() > 0) + /* + StandardDescriptorProcessor descriptorProcessor = (StandardDescriptorProcessor)context.getAttribute(StandardDescriptorProcessor.STANDARD_PROCESSOR); + if (descriptorProcessor == null) { - Resource orideResource = Resource.newSystemResource(overrideDescriptor); - if (orideResource == null) - orideResource = context.newResource(overrideDescriptor); - processor.parseOverride(orideResource); - processor.processOverride(); + descriptorProcessor = new StandardDescriptorProcessor(metaData); + context.setAttribute(StandardDescriptorProcessor.STANDARD_PROCESSOR, descriptorProcessor); } + + //process web-default.xml + descriptorProcessor.process(metaData.getWebDefault()); + + //process web.xml + descriptorProcessor.process(metaData.getWebXml()); + + //process override-web.xml + descriptorProcessor.process(metaData.getOverrideWeb()); + */ } public void postConfigure(WebAppContext context) throws Exception { - context.setAttribute(WebXmlProcessor.WEB_PROCESSOR, null); - context.setAttribute(WebXmlProcessor.METADATA_COMPLETE, null); - context.setAttribute(WebXmlProcessor.WEBXML_VERSION, null); - context.setAttribute(WebXmlProcessor.WEBXML_CLASSNAMES, null); + context.setAttribute(MetaData.WEBXML_CLASSNAMES, null); } /* ------------------------------------------------------------------------------- */ diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebXmlProcessor.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebXmlProcessor.java deleted file mode 100644 index 44bdb9f9199..00000000000 --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebXmlProcessor.java +++ /dev/null @@ -1,1060 +0,0 @@ -// ======================================================================== -// Copyright (c) 2009 Mort Bay Consulting Pty. Ltd. -// ------------------------------------------------------------------------ -// 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.webapp; - -import java.io.File; -import java.lang.reflect.Method; -import java.net.URL; -import java.util.ArrayList; -import java.util.EventListener; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javax.servlet.Servlet; -import javax.servlet.UnavailableException; - -import org.eclipse.jetty.http.security.Constraint; -import org.eclipse.jetty.security.ConstraintAware; -import org.eclipse.jetty.security.ConstraintMapping; -import org.eclipse.jetty.security.SecurityHandler; -import org.eclipse.jetty.security.authentication.FormAuthenticator; -import org.eclipse.jetty.servlet.ErrorPageErrorHandler; -import org.eclipse.jetty.servlet.FilterHolder; -import org.eclipse.jetty.servlet.FilterMapping; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.eclipse.jetty.servlet.ServletMapping; -import org.eclipse.jetty.util.LazyList; -import org.eclipse.jetty.util.Loader; -import org.eclipse.jetty.util.log.Log; -import org.eclipse.jetty.util.resource.Resource; -import org.eclipse.jetty.xml.XmlParser; - - - -/** - * WebXmlProcessor - * - */ -public class WebXmlProcessor -{ - public static final String WEB_PROCESSOR = "org.eclipse.jetty.webProcessor"; - public static final String METADATA_COMPLETE = "org.eclipse.jetty.metadataComplete"; - public static final String WEBXML_VERSION = "org.eclipse.jetty.webXmlVersion"; - public static final String WEBXML_CLASSNAMES = "org.eclipse.jetty.webXmlClassNames"; - - protected WebAppContext _context; - protected XmlParser _xmlParser; - protected Descriptor _webDefaultsRoot; - protected Descriptor _webXmlRoot; - protected List _webFragmentRoots = new ArrayList(); - protected Descriptor _webOverrideRoot; - - protected ServletHandler _servletHandler; - protected SecurityHandler _securityHandler; - protected Object _filters; - protected Object _filterMappings; - protected Object _servlets; - protected Object _servletMappings; - protected Object _listeners; - protected Object _welcomeFiles; - protected Set _newRoles = new HashSet(); - protected List _newConstraintMappings; - protected Map _errorPages; - protected boolean _hasJSP; - protected String _jspServletName; - protected String _jspServletClass; - protected boolean _defaultWelcomeFileList; - - - public class Descriptor - { - protected Resource _xml; - protected XmlParser.Node _root; - protected boolean _metadataComplete; - protected boolean _hasOrdering; - protected int _version; - protected ArrayList _classNames; - - public Descriptor (Resource xml) - { - _xml = xml; - } - - public void parse () - throws Exception - { - if (_root == null) - { - _root = _xmlParser.parse(_xml.getURL().toString()); - processVersion(); - processOrdering(); - } - } - - public boolean isMetaDataComplete() - { - return _metadataComplete; - } - - - public XmlParser.Node getRoot () - { - return _root; - } - - public int getVersion () - { - return _version; - } - - public Resource getResource () - { - return _xml; - } - - public void process() - throws Exception - { - WebXmlProcessor.this.process(_root); - } - - private void processVersion () - { - String version = _root.getAttribute("version", "DTD"); - if ("2.5".equals(version)) - _version = 25; - else if ("2.4".equals(version)) - _version = 24; - else if ("3.0".equals(version)) - _version = 30; - else if ("DTD".equals(version)) - { - _version = 23; - String dtd = _xmlParser.getDTD(); - if (dtd != null && dtd.indexOf("web-app_2_2") >= 0) _version = 22; - } - - if (_version < 25) - _metadataComplete = true; // does not apply before 2.5 - else - _metadataComplete = Boolean.valueOf((String)_root.getAttribute("metadata-complete", "false")).booleanValue(); - - Log.debug(_xml.toString()+": Calculated metadatacomplete = " + _metadataComplete + " with version=" + version); - } - - private void processOrdering () - { - //TODO - } - - private void processClassNames () - { - _classNames = new ArrayList(); - Iterator iter = _root.iterator(); - - while (iter.hasNext()) - { - Object o = iter.next(); - if (!(o instanceof XmlParser.Node)) continue; - XmlParser.Node node = (XmlParser.Node) o; - String name = node.getTag(); - if ("servlet".equals(name)) - { - String className = node.getString("servlet-class", false, true); - if (className != null) - _classNames.add(className); - } - else if ("filter".equals(name)) - { - String className = node.getString("filter-class", false, true); - if (className != null) - _classNames.add(className); - } - else if ("listener".equals(name)) - { - String className = node.getString("listener-class", false, true); - if (className != null) - _classNames.add(className); - } - } - } - - public ArrayList getClassNames () - { - return _classNames; - } - } - - - - - public static XmlParser webXmlParser() throws ClassNotFoundException - { - XmlParser xmlParser=new XmlParser(); - //set up cache of DTDs and schemas locally - URL dtd22=Loader.getResource(Servlet.class,"javax/servlet/resources/web-app_2_2.dtd",true); - URL dtd23=Loader.getResource(Servlet.class,"javax/servlet/resources/web-app_2_3.dtd",true); - URL j2ee14xsd=Loader.getResource(Servlet.class,"javax/servlet/resources/j2ee_1_4.xsd",true); - URL webapp24xsd=Loader.getResource(Servlet.class,"javax/servlet/resources/web-app_2_4.xsd",true); - URL webapp25xsd=Loader.getResource(Servlet.class,"javax/servlet/resources/web-app_2_5.xsd",true); - URL webapp30xsd=Loader.getResource(Servlet.class,"javax/servlet/resources/web-app_3_0.xsd",true); - URL webcommon30xsd=Loader.getResource(Servlet.class,"javax/servlet/resources/web-common_3_0.xsd",true); - URL webfragment30xsd=Loader.getResource(Servlet.class,"javax/servlet/resources/web-fragment_3_0.xsd",true); - URL schemadtd=Loader.getResource(Servlet.class,"javax/servlet/resources/XMLSchema.dtd",true); - URL xmlxsd=Loader.getResource(Servlet.class,"javax/servlet/resources/xml.xsd",true); - URL webservice11xsd=Loader.getResource(Servlet.class,"javax/servlet/resources/j2ee_web_services_client_1_1.xsd",true); - URL webservice12xsd=Loader.getResource(Servlet.class,"javax/servlet/resources/javaee_web_services_client_1_2.xsd",true); - URL datatypesdtd=Loader.getResource(Servlet.class,"javax/servlet/resources/datatypes.dtd",true); - - URL jsp20xsd = null; - URL jsp21xsd = null; - - try - { - Class jsp_page = Loader.loadClass(WebXmlConfiguration.class, "javax.servlet.jsp.JspPage"); - jsp20xsd = jsp_page.getResource("/javax/servlet/resources/jsp_2_0.xsd"); - jsp21xsd = jsp_page.getResource("/javax/servlet/resources/jsp_2_1.xsd"); - } - catch (Exception e) - { - Log.ignore(e); - } - finally - { - if (jsp20xsd == null) jsp20xsd = Loader.getResource(Servlet.class, "javax/servlet/resources/jsp_2_0.xsd", true); - if (jsp21xsd == null) jsp21xsd = Loader.getResource(Servlet.class, "javax/servlet/resources/jsp_2_1.xsd", true); - } - - redirect(xmlParser,"web-app_2_2.dtd",dtd22); - redirect(xmlParser,"-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN",dtd22); - redirect(xmlParser,"web.dtd",dtd23); - redirect(xmlParser,"web-app_2_3.dtd",dtd23); - redirect(xmlParser,"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN",dtd23); - redirect(xmlParser,"XMLSchema.dtd",schemadtd); - redirect(xmlParser,"http://www.w3.org/2001/XMLSchema.dtd",schemadtd); - redirect(xmlParser,"-//W3C//DTD XMLSCHEMA 200102//EN",schemadtd); - redirect(xmlParser,"jsp_2_0.xsd",jsp20xsd); - redirect(xmlParser,"http://java.sun.com/xml/ns/j2ee/jsp_2_0.xsd",jsp20xsd); - redirect(xmlParser,"jsp_2_1.xsd",jsp21xsd); - redirect(xmlParser,"http://java.sun.com/xml/ns/javaee/jsp_2_1.xsd",jsp21xsd); - redirect(xmlParser,"j2ee_1_4.xsd",j2ee14xsd); - redirect(xmlParser,"http://java.sun.com/xml/ns/j2ee/j2ee_1_4.xsd",j2ee14xsd); - redirect(xmlParser,"web-app_2_4.xsd",webapp24xsd); - redirect(xmlParser,"http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd",webapp24xsd); - redirect(xmlParser,"web-app_2_5.xsd",webapp25xsd); - redirect(xmlParser,"http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd",webapp25xsd); - redirect(xmlParser,"web-app_3_0.xsd",webapp30xsd); - redirect(xmlParser,"http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd",webapp30xsd); - redirect(xmlParser,"web-common_3_0.xsd",webcommon30xsd); - redirect(xmlParser,"http://java.sun.com/xml/ns/javaee/web-common_3_0.xsd",webcommon30xsd); - redirect(xmlParser,"web-fragment_3_0.xsd",webfragment30xsd); - redirect(xmlParser,"http://java.sun.com/xml/ns/javaee/web-fragment_3_0.xsd",webfragment30xsd); - redirect(xmlParser,"xml.xsd",xmlxsd); - redirect(xmlParser,"http://www.w3.org/2001/xml.xsd",xmlxsd); - redirect(xmlParser,"datatypes.dtd",datatypesdtd); - redirect(xmlParser,"http://www.w3.org/2001/datatypes.dtd",datatypesdtd); - redirect(xmlParser,"j2ee_web_services_client_1_1.xsd",webservice11xsd); - redirect(xmlParser,"http://www.ibm.com/webservices/xsd/j2ee_web_services_client_1_1.xsd",webservice11xsd); - redirect(xmlParser,"javaee_web_services_client_1_2.xsd",webservice12xsd); - redirect(xmlParser,"http://www.ibm.com/webservices/xsd/javaee_web_services_client_1_2.xsd",webservice12xsd); - - return xmlParser; - } - - /* ------------------------------------------------------------------------------- */ - protected static void redirect(XmlParser parser, String resource, URL source) - { - if (source != null) parser.redirectEntity(resource, source); - } - - - public WebXmlProcessor (WebAppContext context) throws ClassNotFoundException - { - _context = context; - _xmlParser = webXmlParser(); - } - - public void parseDefaults (Resource webDefaults) - throws Exception - { - _webDefaultsRoot = new Descriptor(webDefaults); - _webDefaultsRoot.parse(); - } - - public void parseWebXml (Resource webXml) - throws Exception - { - _webXmlRoot = new Descriptor(webXml); - _webXmlRoot.parse(); - _webXmlRoot.processClassNames(); - _context.setAttribute(METADATA_COMPLETE, Boolean.valueOf(_webXmlRoot.isMetaDataComplete())); - _context.setAttribute(WEBXML_VERSION, Integer.valueOf(_webXmlRoot.getVersion())); - _context.setAttribute(WEBXML_CLASSNAMES, _webXmlRoot.getClassNames()); - } - - public void parseFragment (Resource fragment) - throws Exception - { - if (_webXmlRoot.isMetaDataComplete()) - return; //do not process anything else if main web.xml file is complete - - //Metadata-complete is not set, or there is no web.xml - Descriptor frag = new Descriptor(fragment); - frag.parse(); - _webFragmentRoots.add(frag); - } - - public void parseOverride (Resource override) - throws Exception - { - _xmlParser.setValidating(false); - _webOverrideRoot = new Descriptor(override); - _webOverrideRoot.parse(); - } - - - public void processDefaults () - throws Exception - { - _webDefaultsRoot.process(); - _defaultWelcomeFileList = _context.getWelcomeFiles() != null; - } - - public void processWebXml () - throws Exception - { - if (_webXmlRoot!=null) - _webXmlRoot.process(); - } - - public void processFragments () - throws Exception - { - for (Descriptor frag : _webFragmentRoots) - { - frag.process(); - } - } - - public void processOverride () - throws Exception - { - _webOverrideRoot.process(); - } - - public Descriptor getWebXml () - { - return _webXmlRoot; - } - - public Descriptor getOverrideWeb () - { - return _webOverrideRoot; - } - - public Descriptor getWebDefault () - { - return _webDefaultsRoot; - } - - public List getFragments () - { - return _webFragmentRoots; - } - - - public void process (XmlParser.Node config) - throws Exception - { - - //Get the current objects from the context - _servletHandler = _context.getServletHandler(); - _securityHandler = (SecurityHandler)_context.getSecurityHandler(); - _filters = LazyList.array2List(_servletHandler.getFilters()); - _filterMappings = LazyList.array2List(_servletHandler.getFilterMappings()); - _servlets = LazyList.array2List(_servletHandler.getServlets()); - _servletMappings = LazyList.array2List(_servletHandler.getServletMappings()); - _listeners = LazyList.array2List(_context.getEventListeners()); - _welcomeFiles = LazyList.array2List(_context.getWelcomeFiles()); - - _newConstraintMappings=new ArrayList(); - _newRoles=new HashSet(); - - _errorPages = _context.getErrorHandler() instanceof ErrorPageErrorHandler ? ((ErrorPageErrorHandler)_context.getErrorHandler()).getErrorPages() : null; - - Iterator iter = config.iterator(); - XmlParser.Node node = null; - while (iter.hasNext()) - { - try - { - Object o = iter.next(); - if (!(o instanceof XmlParser.Node)) continue; - node = (XmlParser.Node) o; - String name = node.getTag(); - initWebXmlElement(name, node); - } - catch (ClassNotFoundException e) - { - throw e; - } - catch (Exception e) - { - Log.warn("Configuration problem at " + node, e); - throw new UnavailableException("Configuration problem"); - } - } - - //Set the context with the results of the processing - _servletHandler.setFilters((FilterHolder[]) LazyList.toArray(_filters, FilterHolder.class)); - _servletHandler.setFilterMappings((FilterMapping[]) LazyList.toArray(_filterMappings, FilterMapping.class)); - _servletHandler.setServlets((ServletHolder[]) LazyList.toArray(_servlets, ServletHolder.class)); - _servletHandler.setServletMappings((ServletMapping[]) LazyList.toArray(_servletMappings, ServletMapping.class)); - _context.setEventListeners((EventListener[]) LazyList.toArray(_listeners, EventListener.class)); - _context.setWelcomeFiles((String[]) LazyList.toArray(_welcomeFiles, String.class)); - // TODO jaspi check this - if (_securityHandler instanceof ConstraintAware) - { - for (String role : _newRoles) - ((ConstraintAware)_securityHandler).addRole(role); - for (ConstraintMapping mapping : _newConstraintMappings) - ((ConstraintAware)_securityHandler).addConstraintMapping(mapping); - } - - if (_errorPages != null && _context.getErrorHandler() instanceof ErrorPageErrorHandler) - ((ErrorPageErrorHandler)_context.getErrorHandler()).setErrorPages(_errorPages); - } - - - - - /* ------------------------------------------------------------ */ - /** - * Handle web.xml element. This method is called for each top level element - * within the web.xml file. It may be specialized by derived WebAppHandlers - * to provide additional configuration and handling. - * - * @param element The element name - * @param node The node containing the element. - */ - protected void initWebXmlElement(String element, XmlParser.Node node) throws Exception - { - if ("display-name".equals(element)) - initDisplayName(node); - else if ("description".equals(element)) - { - } - else if ("context-param".equals(element)) - initContextParam(node); - else if ("servlet".equals(element)) - initServlet(node); - else if ("servlet-mapping".equals(element)) - initServletMapping(node); - else if ("session-config".equals(element)) - initSessionConfig(node); - else if ("mime-mapping".equals(element)) - initMimeConfig(node); - else if ("welcome-file-list".equals(element)) - initWelcomeFileList(node); - else if ("locale-encoding-mapping-list".equals(element)) - initLocaleEncodingList(node); - else if ("error-page".equals(element)) - initErrorPage(node); - else if ("taglib".equals(element)) - initTagLib(node); - else if ("jsp-config".equals(element)) - initJspConfig(node); - else if ("resource-ref".equals(element)) - { - if (Log.isDebugEnabled()) Log.debug("No implementation: " + node); - } - else if ("security-constraint".equals(element)) - initSecurityConstraint(node); - else if ("login-config".equals(element)) - initLoginConfig(node); - else if ("security-role".equals(element)) - initSecurityRole(node); - else if ("filter".equals(element)) - initFilter(node); - else if ("filter-mapping".equals(element)) - initFilterMapping(node); - else if ("listener".equals(element)) - initListener(node); - else if ("distributable".equals(element)) - initDistributable(node); - else if ("web-fragment".equals(element)) - { - } - else - { - if (Log.isDebugEnabled()) - { - Log.debug("Element {} not handled in {}", element, this); - Log.debug(node.toString()); - } - } - } - - /* ------------------------------------------------------------ */ - protected void initDisplayName(XmlParser.Node node) - { - _context.setDisplayName(node.toString(false, true)); - } - - /* ------------------------------------------------------------ */ - protected void initContextParam(XmlParser.Node node) - { - String name = node.getString("param-name", false, true); - String value = node.getString("param-value", false, true); - if (Log.isDebugEnabled()) Log.debug("ContextParam: " + name + "=" + value); - _context.getInitParams().put(name, value); - } - - /* ------------------------------------------------------------ */ - protected void initFilter(XmlParser.Node node) - { - String name = node.getString("filter-name", false, true); - FilterHolder holder = _servletHandler.getFilter(name); - if (holder == null) - { - holder = _servletHandler.newFilterHolder(); - holder.setName(name); - _filters = LazyList.add(_filters, holder); - } - - String filter_class = node.getString("filter-class", false, true); - if (filter_class != null) holder.setClassName(filter_class); - - Iterator iter = node.iterator("init-param"); - while (iter.hasNext()) - { - XmlParser.Node paramNode = (XmlParser.Node) iter.next(); - String pname = paramNode.getString("param-name", false, true); - String pvalue = paramNode.getString("param-value", false, true); - holder.setInitParameter(pname, pvalue); - } - - String async=node.getString("async-supported",false,true); - if (async!=null) - holder.setAsyncSupported(async.length()==0||Boolean.valueOf(async)); - - String timeout=node.getString("async-timeout",false,true); - // TODO set it - } - - /* ------------------------------------------------------------ */ - protected void initFilterMapping(XmlParser.Node node) - { - String filter_name = node.getString("filter-name", false, true); - - FilterMapping mapping = new FilterMapping(); - - mapping.setFilterName(filter_name); - - ArrayList paths = new ArrayList(); - Iterator iter = node.iterator("url-pattern"); - while (iter.hasNext()) - { - String p = ((XmlParser.Node) iter.next()).toString(false, true); - p = normalizePattern(p); - paths.add(p); - } - mapping.setPathSpecs((String[]) paths.toArray(new String[paths.size()])); - - ArrayList names = new ArrayList(); - iter = node.iterator("servlet-name"); - while (iter.hasNext()) - { - String n = ((XmlParser.Node) iter.next()).toString(false, true); - names.add(n); - } - mapping.setServletNames((String[]) names.toArray(new String[names.size()])); - - int dispatcher=FilterMapping.DEFAULT; - iter=node.iterator("dispatcher"); - while(iter.hasNext()) - { - String d=((XmlParser.Node)iter.next()).toString(false,true); - dispatcher|=FilterMapping.dispatch(d); - } - mapping.setDispatches(dispatcher); - - _filterMappings = LazyList.add(_filterMappings, mapping); - } - - /* ------------------------------------------------------------ */ - protected String normalizePattern(String p) - { - if (p != null && p.length() > 0 && !p.startsWith("/") && !p.startsWith("*")) return "/" + p; - return p; - } - - /* ------------------------------------------------------------ */ - protected void initServlet(XmlParser.Node node) - { - String id = node.getAttribute("id"); - - // initialize holder - String servlet_name = node.getString("servlet-name", false, true); - ServletHolder holder = _servletHandler.getServlet(servlet_name); - if (holder == null) - { - holder = _servletHandler.newServletHolder(); - holder.setName(servlet_name); - _servlets = LazyList.add(_servlets, holder); - } - - // init params - Iterator iParamsIter = node.iterator("init-param"); - while (iParamsIter.hasNext()) - { - XmlParser.Node paramNode = (XmlParser.Node) iParamsIter.next(); - String pname = paramNode.getString("param-name", false, true); - String pvalue = paramNode.getString("param-value", false, true); - holder.setInitParameter(pname, pvalue); - } - - String servlet_class = node.getString("servlet-class", false, true); - - // Handle JSP - if (id != null && id.equals("jsp")) - { - _jspServletName = servlet_name; - _jspServletClass = servlet_class; - try - { - Loader.loadClass(this.getClass(), servlet_class); - _hasJSP = true; - } - catch (ClassNotFoundException e) - { - Log.info("NO JSP Support for {}, did not find {}", _context.getContextPath(), servlet_class); - _hasJSP = false; - _jspServletClass = servlet_class = "org.eclipse.jetty.servlet.NoJspServlet"; - } - - // set the JSP log - if (_hasJSP) - { - try - { - Class logFactory = Loader.loadClass(this.getClass(),"org.eclipse.jetty.jsp.JettyLog"); - Method init = logFactory.getMethod("init"); - Log.debug("Init JSP loggging "+init); - init.invoke(null); - } - catch (Exception e) - { - Log.warn(e.toString()); - Log.ignore(e); - } - } - - if (holder.getInitParameter("scratchdir") == null) - { - File tmp = _context.getTempDirectory(); - File scratch = new File(tmp, "jsp"); - if (!scratch.exists()) scratch.mkdir(); - holder.setInitParameter("scratchdir", scratch.getAbsolutePath()); - - if ("?".equals(holder.getInitParameter("classpath"))) - { - String classpath = _context.getClassPath(); - Log.debug("classpath=" + classpath); - if (classpath != null) holder.setInitParameter("classpath", classpath); - } - } - } - if (servlet_class != null) holder.setClassName(servlet_class); - - // Handler JSP file - String jsp_file = node.getString("jsp-file", false, true); - if (jsp_file != null) - { - holder.setForcedPath(jsp_file); - holder.setClassName(_jspServletClass); - } - - // handle startup - XmlParser.Node startup = node.get("load-on-startup"); - if (startup != null) - { - String s = startup.toString(false, true).toLowerCase(); - if (s.startsWith("t")) - { - Log.warn("Deprecated boolean load-on-startup. Please use integer"); - holder.setInitOrder(1); - } - else - { - int order = 0; - try - { - if (s != null && s.trim().length() > 0) order = Integer.parseInt(s); - } - catch (Exception e) - { - Log.warn("Cannot parse load-on-startup " + s + ". Please use integer"); - Log.ignore(e); - } - holder.setInitOrder(order); - } - } - - Iterator sRefsIter = node.iterator("security-role-ref"); - while (sRefsIter.hasNext()) - { - XmlParser.Node securityRef = (XmlParser.Node) sRefsIter.next(); - String roleName = securityRef.getString("role-name", false, true); - String roleLink = securityRef.getString("role-link", false, true); - if (roleName != null && roleName.length() > 0 && roleLink != null && roleLink.length() > 0) - { - if (Log.isDebugEnabled()) Log.debug("link role " + roleName + " to " + roleLink + " for " + this); - holder.setUserRoleLink(roleName, roleLink); - } - else - { - Log.warn("Ignored invalid security-role-ref element: " + "servlet-name=" + holder.getName() + ", " + securityRef); - } - } - - XmlParser.Node run_as = node.get("run-as"); - if (run_as != null) - { - String roleName = run_as.getString("role-name", false, true); - if (roleName != null) - holder.setRunAsRole(roleName); - } - - String async=node.getString("async-supported",false,true); - if (async!=null) - holder.setAsyncSupported(async.length()==0||Boolean.valueOf(async)); - - String timeout=node.getString("async-timeout",false,true); - // TODO set it - } - - /* ------------------------------------------------------------ */ - protected void initServletMapping(XmlParser.Node node) - { - String servlet_name = node.getString("servlet-name", false, true); - ServletMapping mapping = new ServletMapping(); - mapping.setServletName(servlet_name); - - ArrayList paths = new ArrayList(); - Iterator iter = node.iterator("url-pattern"); - while (iter.hasNext()) - { - String p = ((XmlParser.Node) iter.next()).toString(false, true); - p = normalizePattern(p); - paths.add(p); - } - mapping.setPathSpecs((String[]) paths.toArray(new String[paths.size()])); - - _servletMappings = LazyList.add(_servletMappings, mapping); - } - - /* ------------------------------------------------------------ */ - protected void initListener(XmlParser.Node node) - { - String className = node.getString("listener-class", false, true); - Object listener = null; - try - { - Class listenerClass = _context.loadClass(className); - listener = newListenerInstance(listenerClass); - if (!(listener instanceof EventListener)) - { - Log.warn("Not an EventListener: " + listener); - return; - } - _listeners = LazyList.add(_listeners, listener); - } - catch (Exception e) - { - Log.warn("Could not instantiate listener " + className, e); - return; - } - } - - /* ------------------------------------------------------------ */ - protected Object newListenerInstance(Class clazz) throws InstantiationException, IllegalAccessException - { - return clazz.newInstance(); - } - - /* ------------------------------------------------------------ */ - protected void initDistributable(XmlParser.Node node) - { - // the element has no content, so its simple presence - // indicates that the webapp is distributable... - if (!_context.isDistributable()) - _context.setDistributable(true); - } - - /* ------------------------------------------------------------ */ - protected void initSessionConfig(XmlParser.Node node) - { - XmlParser.Node tNode = node.get("session-timeout"); - if (tNode != null) - { - int timeout = Integer.parseInt(tNode.toString(false, true)); - _context.getSessionHandler().getSessionManager().setMaxInactiveInterval(timeout * 60); - } - } - - /* ------------------------------------------------------------ */ - protected void initMimeConfig(XmlParser.Node node) - { - String extension = node.getString("extension", false, true); - if (extension != null && extension.startsWith(".")) extension = extension.substring(1); - String mimeType = node.getString("mime-type", false, true); - _context.getMimeTypes().addMimeMapping(extension, mimeType); - } - - /* ------------------------------------------------------------ */ - protected void initWelcomeFileList(XmlParser.Node node) - { - if (_defaultWelcomeFileList) - _welcomeFiles = null; // erase welcome files from default web.xml - - _defaultWelcomeFileList = false; - Iterator iter = node.iterator("welcome-file"); - while (iter.hasNext()) - { - XmlParser.Node indexNode = (XmlParser.Node) iter.next(); - String welcome = indexNode.toString(false, true); - _welcomeFiles = LazyList.add(_welcomeFiles, welcome); - } - } - - /* ------------------------------------------------------------ */ - protected void initLocaleEncodingList(XmlParser.Node node) - { - Iterator iter = node.iterator("locale-encoding-mapping"); - while (iter.hasNext()) - { - XmlParser.Node mapping = (XmlParser.Node) iter.next(); - String locale = mapping.getString("locale", false, true); - String encoding = mapping.getString("encoding", false, true); - _context.addLocaleEncoding(locale, encoding); - } - } - - /* ------------------------------------------------------------ */ - protected void initErrorPage(XmlParser.Node node) - { - String error = node.getString("error-code", false, true); - if (error == null || error.length() == 0) error = node.getString("exception-type", false, true); - String location = node.getString("location", false, true); - - if (_errorPages == null) - _errorPages = new HashMap(); - _errorPages.put(error, location); - } - - /* ------------------------------------------------------------ */ - protected void initTagLib(XmlParser.Node node) - { - String uri = node.getString("taglib-uri", false, true); - String location = node.getString("taglib-location", false, true); - - _context.setResourceAlias(uri, location); - } - - /* ------------------------------------------------------------ */ - protected void initJspConfig(XmlParser.Node node) - { - for (int i = 0; i < node.size(); i++) - { - Object o = node.get(i); - if (o instanceof XmlParser.Node && "taglib".equals(((XmlParser.Node) o).getTag())) initTagLib((XmlParser.Node) o); - } - - // Map URLs from jsp property groups to JSP servlet. - // this is more JSP stupidness creaping into the servlet spec - Iterator iter = node.iterator("jsp-property-group"); - Object paths = null; - while (iter.hasNext()) - { - XmlParser.Node group = (XmlParser.Node) iter.next(); - Iterator iter2 = group.iterator("url-pattern"); - while (iter2.hasNext()) - { - String url = ((XmlParser.Node) iter2.next()).toString(false, true); - url = normalizePattern(url); - paths = LazyList.add(paths, url); - } - } - - if (LazyList.size(paths) > 0) - { - String jspName = getJSPServletName(); - if (jspName != null) - { - ServletMapping mapping = new ServletMapping(); - mapping.setServletName(jspName); - mapping.setPathSpecs(LazyList.toStringArray(paths)); - _servletMappings = LazyList.add(_servletMappings, mapping); - } - } - } - - /* ------------------------------------------------------------ */ - protected void initSecurityConstraint(XmlParser.Node node) - { - Constraint scBase = new Constraint(); - - try - { - XmlParser.Node auths = node.get("auth-constraint"); - - if (auths != null) - { - scBase.setAuthenticate(true); - // auth-constraint - Iterator iter = auths.iterator("role-name"); - Object roles = null; - while (iter.hasNext()) - { - String role = ((XmlParser.Node) iter.next()).toString(false, true); - roles = LazyList.add(roles, role); - } - scBase.setRoles(LazyList.toStringArray(roles)); - } - - XmlParser.Node data = node.get("user-data-constraint"); - if (data != null) - { - data = data.get("transport-guarantee"); - String guarantee = data.toString(false, true).toUpperCase(); - if (guarantee == null || guarantee.length() == 0 || "NONE".equals(guarantee)) - scBase.setDataConstraint(Constraint.DC_NONE); - else if ("INTEGRAL".equals(guarantee)) - scBase.setDataConstraint(Constraint.DC_INTEGRAL); - else if ("CONFIDENTIAL".equals(guarantee)) - scBase.setDataConstraint(Constraint.DC_CONFIDENTIAL); - else - { - Log.warn("Unknown user-data-constraint:" + guarantee); - scBase.setDataConstraint(Constraint.DC_CONFIDENTIAL); - } - } - Iterator iter = node.iterator("web-resource-collection"); - while (iter.hasNext()) - { - XmlParser.Node collection = (XmlParser.Node) iter.next(); - String name = collection.getString("web-resource-name", false, true); - Constraint sc = (Constraint) scBase.clone(); - sc.setName(name); - - Iterator iter2 = collection.iterator("url-pattern"); - while (iter2.hasNext()) - { - String url = ((XmlParser.Node) iter2.next()).toString(false, true); - url = normalizePattern(url); - - Iterator iter3 = collection.iterator("http-method"); - if (iter3.hasNext()) - { - while (iter3.hasNext()) - { - String method = ((XmlParser.Node) iter3.next()).toString(false, true); - ConstraintMapping mapping = new ConstraintMapping(); - mapping.setMethod(method); - mapping.setPathSpec(url); - mapping.setConstraint(sc); - _newConstraintMappings.add(mapping); - } - } - else - { - ConstraintMapping mapping = new ConstraintMapping(); - mapping.setPathSpec(url); - mapping.setConstraint(sc); - _newConstraintMappings.add(mapping); - } - } - } - } - catch (CloneNotSupportedException e) - { - Log.warn(e); - } - - } - - /* ------------------------------------------------------------ */ - protected void initLoginConfig(XmlParser.Node node) throws Exception - { - XmlParser.Node method = node.get("auth-method"); - if (method != null) - { - XmlParser.Node name = node.get("realm-name"); - _securityHandler.setRealmName(name == null ? "default" : name.toString(false, true)); - _securityHandler.setAuthMethod(method.toString(false, true)); - - - if (Constraint.__FORM_AUTH.equals(_securityHandler.getAuthMethod())) - { - XmlParser.Node formConfig = node.get("form-login-config"); - if (formConfig != null) - { - String loginPageName = null; - XmlParser.Node loginPage = formConfig.get("form-login-page"); - if (loginPage != null) - loginPageName = loginPage.toString(false, true); - String errorPageName = null; - XmlParser.Node errorPage = formConfig.get("form-error-page"); - if (errorPage != null) - errorPageName = errorPage.toString(false, true); - _securityHandler.setInitParameter(FormAuthenticator.__FORM_LOGIN_PAGE,loginPageName); - _securityHandler.setInitParameter(FormAuthenticator.__FORM_ERROR_PAGE,errorPageName); - } - else - { - throw new IllegalArgumentException("!form-login-config"); - } - } - } - } - - /* ------------------------------------------------------------ */ - protected void initSecurityRole(XmlParser.Node node) - { - XmlParser.Node roleNode = node.get("role-name"); - String role = roleNode.toString(false, true); - _newRoles.add(role); - } - - /* ------------------------------------------------------------ */ - protected String getJSPServletName() - { - if (_jspServletName == null) - { - Map.Entry entry = _context.getServletHandler().getHolderEntry("test.jsp"); - if (entry != null) - { - ServletHolder holder = (ServletHolder) entry.getValue(); - _jspServletName = holder.getName(); - } - } - return _jspServletName; - } -} diff --git a/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/OrderingTest.java b/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/OrderingTest.java new file mode 100644 index 00000000000..7e20c21da2c --- /dev/null +++ b/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/OrderingTest.java @@ -0,0 +1,939 @@ +// ======================================================================== +// Copyright (c) 2006-2009 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// 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.webapp; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +import junit.framework.TestCase; + +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.webapp.MetaData.AbsoluteOrdering; +import org.eclipse.jetty.webapp.MetaData.RelativeOrdering; + +/** + * OrderingTest + * + * + */ +public class OrderingTest extends TestCase +{ + public class TestResource extends Resource + { + public String _name; + + public TestResource (String name) + { + _name =name; + } + + /** + * @see org.eclipse.jetty.util.resource.Resource#addPath(java.lang.String) + */ + public Resource addPath(String path) throws IOException, MalformedURLException + { + // TODO Auto-generated method stub + return null; + } + + /** + * @see org.eclipse.jetty.util.resource.Resource#delete() + */ + public boolean delete() throws SecurityException + { + // TODO Auto-generated method stub + return false; + } + + /** + * @see org.eclipse.jetty.util.resource.Resource#exists() + */ + public boolean exists() + { + // TODO Auto-generated method stub + return false; + } + + /** + * @see org.eclipse.jetty.util.resource.Resource#getFile() + */ + public File getFile() throws IOException + { + // TODO Auto-generated method stub + return null; + } + + /** + * @see org.eclipse.jetty.util.resource.Resource#getInputStream() + */ + public InputStream getInputStream() throws IOException + { + // TODO Auto-generated method stub + return null; + } + + /** + * @see org.eclipse.jetty.util.resource.Resource#getName() + */ + public String getName() + { + return _name; + } + + /** + * @see org.eclipse.jetty.util.resource.Resource#getOutputStream() + */ + public OutputStream getOutputStream() throws IOException, SecurityException + { + // TODO Auto-generated method stub + return null; + } + + /** + * @see org.eclipse.jetty.util.resource.Resource#getURL() + */ + public URL getURL() + { + // TODO Auto-generated method stub + return null; + } + + /** + * @see org.eclipse.jetty.util.resource.Resource#isContainedIn(org.eclipse.jetty.util.resource.Resource) + */ + public boolean isContainedIn(Resource r) throws MalformedURLException + { + // TODO Auto-generated method stub + return false; + } + + /** + * @see org.eclipse.jetty.util.resource.Resource#isDirectory() + */ + public boolean isDirectory() + { + // TODO Auto-generated method stub + return false; + } + + /** + * @see org.eclipse.jetty.util.resource.Resource#lastModified() + */ + public long lastModified() + { + // TODO Auto-generated method stub + return 0; + } + + /** + * @see org.eclipse.jetty.util.resource.Resource#length() + */ + public long length() + { + // TODO Auto-generated method stub + return 0; + } + + /** + * @see org.eclipse.jetty.util.resource.Resource#list() + */ + public String[] list() + { + // TODO Auto-generated method stub + return null; + } + + /** + * @see org.eclipse.jetty.util.resource.Resource#release() + */ + public void release() + { + // TODO Auto-generated method stub + + } + + /** + * @see org.eclipse.jetty.util.resource.Resource#renameTo(org.eclipse.jetty.util.resource.Resource) + */ + public boolean renameTo(Resource dest) throws SecurityException + { + // TODO Auto-generated method stub + return false; + } + + } + + + public void testRelativeOrdering0 () + throws Exception + { + //Example from ServletSpec p.70 + WebAppContext wac = new WebAppContext(); + MetaData metaData = new MetaData(wac); + List resources = new ArrayList(); + metaData._ordering = metaData.new RelativeOrdering(); + + //A: after others, after C + TestResource jar1 = new TestResource("A"); + resources.add(jar1); + TestResource r1 = new TestResource("A/web-fragment.xml"); + FragmentDescriptor f1 = new FragmentDescriptor(r1, metaData); + f1._name = "A"; + metaData._webFragmentNameMap.put(f1._name, f1); + metaData._webFragmentResourceMap.put(jar1, f1); + f1._otherType = FragmentDescriptor.OtherType.After; + //((RelativeOrdering)metaData._ordering).addAfterOthers(r1); + f1._afters.add("C"); + + //B: before others + TestResource jar2 = new TestResource("B"); + resources.add(jar2); + TestResource r2 = new TestResource("B/web-fragment.xml"); + FragmentDescriptor f2 = new FragmentDescriptor(r2, metaData); + f2._name="B"; + metaData._webFragmentNameMap.put(f2._name, f2); + metaData._webFragmentResourceMap.put(jar2, f2); + f2._otherType = FragmentDescriptor.OtherType.Before; + //((RelativeOrdering)metaData._ordering).addBeforeOthers(r2); + + //C: after others + TestResource jar3 = new TestResource("C"); + resources.add(jar3); + TestResource r3 = new TestResource("C/web-fragment.xml"); + FragmentDescriptor f3 = new FragmentDescriptor(r3, metaData); + f3._name="C"; + metaData._webFragmentNameMap.put(f3._name, f3); + metaData._webFragmentResourceMap.put(jar3, f3); + f3._otherType = FragmentDescriptor.OtherType.After; + //((RelativeOrdering)metaData._ordering).addAfterOthers(r3); + + //D: no ordering + TestResource jar4 = new TestResource("D"); + resources.add(jar4); + TestResource r4 = new TestResource("D/web-fragment.xml"); + FragmentDescriptor f4 = new FragmentDescriptor(r4, metaData); + f4._name="D"; + metaData._webFragmentNameMap.put(f4._name, f4); + metaData._webFragmentResourceMap.put(jar4, f4); + f4._otherType = FragmentDescriptor.OtherType.None; + //((RelativeOrdering)metaData._ordering).addNoOthers(r4); + + //E: no ordering + TestResource jar5 = new TestResource("E"); + resources.add(jar5); + TestResource r5 = new TestResource("E/web-fragment.xml"); + FragmentDescriptor f5 = new FragmentDescriptor(r5, metaData); + f5._name="E"; + metaData._webFragmentNameMap.put(f5._name, f5); + metaData._webFragmentResourceMap.put(jar5, f5); + f5._otherType = FragmentDescriptor.OtherType.None; + //((RelativeOrdering)metaData._ordering).addNoOthers(r5); + + //F: before others, before B + TestResource jar6 = new TestResource("F"); + resources.add(jar6); + TestResource r6 = new TestResource("F/web-fragment.xml"); + FragmentDescriptor f6 = new FragmentDescriptor(r6, metaData); + f6._name="F"; + metaData._webFragmentNameMap.put(f6._name, f6); + metaData._webFragmentResourceMap.put(jar6,f6); + f6._otherType = FragmentDescriptor.OtherType.Before; + //((RelativeOrdering)metaData._ordering).addBeforeOthers(r6); + f6._befores.add("B"); + + // + // p.70 outcome: F, B, D, E, C, A + // + String[] outcomes = {"FBDECA"}; + List orderedList = metaData._ordering.order(resources); + + String result = ""; + for (Resource r:orderedList) + result+=(((TestResource)r)._name); + + if (!checkResult(result, outcomes)) + fail("No outcome matched "+result); + } + + + + + public void testRelativeOrdering1 () + throws Exception + { + List resources = new ArrayList(); + WebAppContext wac = new WebAppContext(); + MetaData metaData = new MetaData(wac); + metaData._ordering = metaData.new RelativeOrdering(); + + //Example from ServletSpec p.70-71 + //No name: after others, before C + TestResource jar1 = new TestResource("plain"); + resources.add(jar1); + TestResource r1 = new TestResource("plain/web-fragment.xml"); + FragmentDescriptor f1 = new FragmentDescriptor(r1, metaData); + f1._name = FragmentDescriptor.NAMELESS+"1"; + metaData._webFragmentNameMap.put(f1._name, f1); + metaData._webFragmentResourceMap.put(jar1,f1); + f1._otherType = FragmentDescriptor.OtherType.After; + //((RelativeOrdering)metaData._ordering).addAfterOthers(f1); + f1._befores.add("C"); + + //B: before others + TestResource jar2 = new TestResource("B"); + resources.add(jar2); + TestResource r2 = new TestResource("B/web-fragment.xml"); + FragmentDescriptor f2 = new FragmentDescriptor(r2, metaData); + f2._name="B"; + metaData._webFragmentNameMap.put(f2._name, f2); + metaData._webFragmentResourceMap.put(jar2,f2); + f2._otherType = FragmentDescriptor.OtherType.Before; + //((RelativeOrdering)metaData._ordering).addBeforeOthers(f2); + + //C: no ordering + TestResource jar3 = new TestResource("C"); + resources.add(jar3); + TestResource r3 = new TestResource("C/web-fragment.xml"); + FragmentDescriptor f3 = new FragmentDescriptor(r3, metaData); + f3._name="C"; + metaData._webFragmentNameMap.put(f3._name, f3); + metaData._webFragmentResourceMap.put(jar3,f3); + //((RelativeOrdering)metaData._ordering).addNoOthers(f3); + f3._otherType = FragmentDescriptor.OtherType.None; + + //D: after others + TestResource jar4 = new TestResource("D"); + resources.add(jar4); + TestResource r4 = new TestResource("D/web-fragment.xml"); + FragmentDescriptor f4 = new FragmentDescriptor(r4, metaData); + f4._name="D"; + metaData._webFragmentNameMap.put(f4._name, f4); + metaData._webFragmentResourceMap.put(jar4,f4); + //((RelativeOrdering)metaData._ordering).addAfterOthers(f4); + f4._otherType = FragmentDescriptor.OtherType.After; + + //E: before others + TestResource jar5 = new TestResource("E"); + resources.add(jar5); + TestResource r5 = new TestResource("E/web-fragment.xml"); + FragmentDescriptor f5 = new FragmentDescriptor(r5, metaData); + f5._name="E"; + metaData._webFragmentNameMap.put(f5._name, f5); + metaData._webFragmentResourceMap.put(jar5,f5); + //((RelativeOrdering)metaData._ordering).addBeforeOthers(f5); + f5._otherType = FragmentDescriptor.OtherType.Before; + + //F: no ordering + TestResource jar6 = new TestResource("F"); + resources.add(jar6); + TestResource r6 = new TestResource("F/web-fragment.xml"); + FragmentDescriptor f6 = new FragmentDescriptor(r6, metaData); + f6._name="F"; + metaData._webFragmentNameMap.put(f6._name, f6); + metaData._webFragmentResourceMap.put(jar6,f6); + //((RelativeOrdering)metaData._ordering).addNoOthers(f6); + f6._otherType = FragmentDescriptor.OtherType.None; + + List orderedList = metaData._ordering.order(resources); + + // p.70-71 Possible outcomes are: + // B, E, F, noname, C, D + // B, E, F, noname, D, C + // E, B, F, noname, C, D + // E, B, F, noname, D, C + // E, B, F, D, noname, C + // + String[] outcomes = {"BEFplainCD", + "BEFplainDC", + "EBFplainCD", + "EBFplainDC", + "EBFDplain"}; + + String orderedNames = ""; + for (Resource r:orderedList) + orderedNames+=(((TestResource)r)._name); + + if (!checkResult(orderedNames, outcomes)) + fail("No outcome matched "+orderedNames); + } + + + public void testRelativeOrdering2 () + throws Exception + { + List resources = new ArrayList(); + WebAppContext wac = new WebAppContext(); + MetaData metaData = new MetaData(wac); + metaData._ordering = metaData.new RelativeOrdering(); + + //Example from Spec p. 71-72 + + //A: after B + TestResource jar1 = new TestResource("A"); + resources.add(jar1); + TestResource r1 = new TestResource("A/web-fragment.xml"); + FragmentDescriptor f1 = new FragmentDescriptor(r1, metaData); + f1._name = "A"; + metaData._webFragmentNameMap.put(f1._name, f1); + metaData._webFragmentResourceMap.put(jar1, f1); + //((RelativeOrdering)metaData._ordering).addNoOthers(f1); + f1._otherType = FragmentDescriptor.OtherType.None; + f1._afters.add("B"); + + //B: no order + TestResource jar2 = new TestResource("B"); + resources.add(jar2); + TestResource r2 = new TestResource("B/web-fragment.xml"); + FragmentDescriptor f2 = new FragmentDescriptor(r2, metaData); + f2._name="B"; + metaData._webFragmentNameMap.put(f2._name, f2); + metaData._webFragmentResourceMap.put(jar2, f2); + //((RelativeOrdering)metaData._ordering).addNoOthers(f2); + f2._otherType = FragmentDescriptor.OtherType.None; + + //C: before others + TestResource jar3 = new TestResource("C"); + resources.add(jar3); + TestResource r3 = new TestResource("C/web-fragment.xml"); + FragmentDescriptor f3 = new FragmentDescriptor(r3, metaData); + f3._name="C"; + metaData._webFragmentNameMap.put(f3._name, f3); + metaData._webFragmentResourceMap.put(jar3,f3); + //((RelativeOrdering)metaData._ordering).addBeforeOthers(f3); + f3._otherType = FragmentDescriptor.OtherType.Before; + + //D: no order + TestResource jar4 = new TestResource("D"); + resources.add(jar4); + TestResource r4 = new TestResource("D/web-fragment.xml"); + FragmentDescriptor f4 = new FragmentDescriptor(r4, metaData); + f4._name="D"; + metaData._webFragmentNameMap.put(f4._name, f4); + metaData._webFragmentResourceMap.put(jar4, f4); + //((RelativeOrdering)metaData._ordering).addNoOthers(f4); + f4._otherType = FragmentDescriptor.OtherType.None; + // + // p.71-72 possible outcomes are: + // C,B,D,A + // C,D,B,A + // C,B,A,D + // + String[] outcomes = {"CBDA", + "CDBA", + "CBAD"}; + + + List orderedList = metaData._ordering.order(resources); + String result = ""; + for (Resource r:orderedList) + result+=(((TestResource)r)._name); + + if (!checkResult(result, outcomes)) + fail ("No outcome matched "+result); + } + + + public void testRelativeOrdering3 () + throws Exception + { + List resources = new ArrayList(); + WebAppContext wac = new WebAppContext(); + MetaData metaData = new MetaData(wac); + metaData._ordering = metaData.new RelativeOrdering(); + + //A: after others, before C + TestResource jar1 = new TestResource("A"); + resources.add(jar1); + TestResource r1 = new TestResource("A/web-fragment.xml"); + FragmentDescriptor f1 = new FragmentDescriptor(r1, metaData); + f1._name = "A"; + metaData._webFragmentNameMap.put(f1._name, f1); + metaData._webFragmentResourceMap.put(jar1, f1); + //((RelativeOrdering)metaData._ordering).addAfterOthers(f1); + f1._otherType = FragmentDescriptor.OtherType.After; + f1._befores.add("C"); + + //B: before others, before C + TestResource jar2 = new TestResource("B"); + resources.add(jar2); + TestResource r2 = new TestResource("B/web-fragment.xml"); + FragmentDescriptor f2 = new FragmentDescriptor(r2, metaData); + f2._name="B"; + metaData._webFragmentNameMap.put(f2._name, f2); + metaData._webFragmentResourceMap.put(jar2,f2); + //((RelativeOrdering)metaData._ordering).addBeforeOthers(f2); + f2._otherType = FragmentDescriptor.OtherType.Before; + f2._befores.add("C"); + + //C: no ordering + TestResource jar3 = new TestResource("C"); + resources.add(jar3); + TestResource r3 = new TestResource("C/web-fragment.xml"); + FragmentDescriptor f3 = new FragmentDescriptor(r3, metaData); + f3._name="C"; + metaData._webFragmentNameMap.put(f3._name, f3); + metaData._webFragmentResourceMap.put(jar3,f3); + //((RelativeOrdering)metaData._ordering).addNoOthers(f3); + f3._otherType = FragmentDescriptor.OtherType.None; + + //result: BAC + String[] outcomes = {"BAC"}; + + List orderedList = metaData._ordering.order(resources); + String result = ""; + for (Resource r:orderedList) + result+=(((TestResource)r)._name); + + if (!checkResult(result, outcomes)) + fail ("No outcome matched "+result); + } + + public void testCircular1 () + throws Exception + { + + //A: after B + //B: after A + List resources = new ArrayList(); + WebAppContext wac = new WebAppContext(); + MetaData metaData = new MetaData(wac); + metaData._ordering = metaData.new RelativeOrdering(); + + //A: after B + TestResource jar1 = new TestResource("A"); + resources.add(jar1); + TestResource r1 = new TestResource("A/web-fragment.xml"); + FragmentDescriptor f1 = new FragmentDescriptor(r1, metaData); + f1._name = "A"; + metaData._webFragmentNameMap.put(f1._name, f1); + metaData._webFragmentResourceMap.put(jar1, f1); + //((RelativeOrdering)metaData._ordering).addNoOthers(f1); + f1._otherType = FragmentDescriptor.OtherType.None; + f1._afters.add("B"); + + //B: after A + TestResource jar2 = new TestResource("B"); + resources.add(jar2); + TestResource r2 = new TestResource("B/web-fragment.xml"); + FragmentDescriptor f2 = new FragmentDescriptor(r2, metaData); + f2._name="B"; + metaData._webFragmentNameMap.put(f2._name, f2); + metaData._webFragmentResourceMap.put(jar2, f2); + //((RelativeOrdering)metaData._ordering).addNoOthers(f2); + f2._otherType = FragmentDescriptor.OtherType.None; + f2._afters.add("A"); + + try + { + List orderedList = metaData._ordering.order(resources); + fail("No circularity detected"); + } + catch (Exception e) + { + assertTrue (e instanceof IllegalStateException); + } + } + + + + + public void testInvalid1 () + throws Exception + { + List resources = new ArrayList(); + WebAppContext wac = new WebAppContext(); + MetaData metaData = new MetaData(wac); + metaData._ordering = metaData.new RelativeOrdering(); + + //A: after others, before C + TestResource jar1 = new TestResource("A"); + resources.add(jar1); + TestResource r1 = new TestResource("A/web-fragment.xml"); + FragmentDescriptor f1 = new FragmentDescriptor(r1, metaData); + f1._name = "A"; + metaData._webFragmentNameMap.put(f1._name, f1); + metaData._webFragmentResourceMap.put(jar1,f1); + //((RelativeOrdering)metaData._ordering).addAfterOthers(r1); + f1._otherType = FragmentDescriptor.OtherType.After; + f1._befores.add("C"); + + //B: before others, after C + TestResource jar2 = new TestResource("B"); + resources.add(jar2); + TestResource r2 = new TestResource("B/web-fragment.xml"); + FragmentDescriptor f2 = new FragmentDescriptor(r2, metaData); + f2._name="B"; + metaData._webFragmentNameMap.put(f2._name, f2); + metaData._webFragmentResourceMap.put(jar2,f2); + //((RelativeOrdering)metaData._ordering).addBeforeOthers(r2); + f2._otherType = FragmentDescriptor.OtherType.Before; + f2._afters.add("C"); + + //C: no ordering + TestResource jar3 = new TestResource("C"); + resources.add(jar3); + TestResource r3 = new TestResource("C/web-fragment.xml"); + FragmentDescriptor f3 = new FragmentDescriptor(r3, metaData); + f3._name="C"; + metaData._webFragmentNameMap.put(f3._name, f3); + metaData._webFragmentResourceMap.put(jar3,f3); + //((RelativeOrdering)metaData._ordering).addNoOthers(r3); + f3._otherType = FragmentDescriptor.OtherType.None; + + try + { + List orderedList = metaData._ordering.order(resources); + String result = ""; + for (Resource r:orderedList) + result +=((TestResource)r)._name; + System.err.println("Invalid Result = "+result); + fail("A and B have an impossible relationship to C"); + } + catch (Exception e) + { + assertTrue (e instanceof IllegalStateException); + } + } + + + public void testAbsoluteOrdering1 () + throws Exception + { + // + // A,B,C,others + // + List resources = new ArrayList(); + WebAppContext wac = new WebAppContext(); + MetaData metaData = new MetaData(wac); + metaData._ordering = metaData.new AbsoluteOrdering(); + ((AbsoluteOrdering)metaData._ordering).add("A"); + ((AbsoluteOrdering)metaData._ordering).add("B"); + ((AbsoluteOrdering)metaData._ordering).add("C"); + ((AbsoluteOrdering)metaData._ordering).addOthers(); + + TestResource jar1 = new TestResource("A"); + resources.add(jar1); + TestResource r1 = new TestResource("A/web-fragment.xml"); + FragmentDescriptor f1 = new FragmentDescriptor(r1, metaData); + f1._name = "A"; + metaData._webFragmentNameMap.put(f1._name, f1); + metaData._webFragmentResourceMap.put(jar1,f1); + + TestResource jar2 = new TestResource("B"); + resources.add(jar2); + TestResource r2 = new TestResource("B/web-fragment.xml"); + FragmentDescriptor f2 = new FragmentDescriptor(r2, metaData); + f2._name="B"; + metaData._webFragmentNameMap.put(f2._name, f2); + metaData._webFragmentResourceMap.put(jar2, f2); + + TestResource jar3 = new TestResource("C"); + resources.add(jar3); + TestResource r3 = new TestResource("C/web-fragment.xml"); + FragmentDescriptor f3 = new FragmentDescriptor(r3, metaData); + f3._name="C"; + metaData._webFragmentNameMap.put(f3._name, f3); + metaData._webFragmentResourceMap.put(jar3, f3); + + TestResource jar4 = new TestResource("D"); + resources.add(jar4); + TestResource r4 = new TestResource("D/web-fragment.xml"); + FragmentDescriptor f4 = new FragmentDescriptor((Resource)null, metaData); + f4._name="D"; + metaData._webFragmentNameMap.put(f4._name, f4); + metaData._webFragmentResourceMap.put(jar4, f4); + + TestResource jar5 = new TestResource("E"); + resources.add(jar5); + TestResource r5 = new TestResource("E/web-fragment.xml"); + FragmentDescriptor f5 = new FragmentDescriptor((Resource)null, metaData); + f5._name="E"; + metaData._webFragmentNameMap.put(f5._name, f5); + metaData._webFragmentResourceMap.put(jar5, f5); + + TestResource jar6 = new TestResource("plain"); + resources.add(jar6); + TestResource r6 = new TestResource ("plain/web-fragment.xml"); + FragmentDescriptor f6 = new FragmentDescriptor((Resource)null, metaData); + f6._name=FragmentDescriptor.NAMELESS+"1"; + metaData._webFragmentNameMap.put(f6._name, f6); + metaData._webFragmentResourceMap.put(jar6, f6); + + List list = metaData._ordering.order(resources); + + String[] outcomes = {"ABCDEplain"}; + String result = ""; + for (Resource r:list) + result += ((TestResource)r)._name; + + if (!checkResult(result, outcomes)) + fail("No outcome matched "+result); + } + + + public void testAbsoluteOrdering2 () + throws Exception + { + // C,B,A + List resources = new ArrayList(); + + WebAppContext wac = new WebAppContext(); + MetaData metaData = new MetaData(wac); + metaData._ordering = metaData.new AbsoluteOrdering(); + ((AbsoluteOrdering)metaData._ordering).add("C"); + ((AbsoluteOrdering)metaData._ordering).add("B"); + ((AbsoluteOrdering)metaData._ordering).add("A"); + + TestResource jar1 = new TestResource("A"); + resources.add(jar1); + TestResource r1 = new TestResource("A/web-fragment.xml"); + FragmentDescriptor f1 = new FragmentDescriptor(r1, metaData); + f1._name = "A"; + metaData._webFragmentNameMap.put(f1._name, f1); + metaData._webFragmentResourceMap.put(jar1,f1); + + TestResource jar2 = new TestResource("B"); + resources.add(jar2); + TestResource r2 = new TestResource("B/web-fragment.xml"); + FragmentDescriptor f2 = new FragmentDescriptor(r2, metaData); + f2._name="B"; + metaData._webFragmentNameMap.put(f2._name, f2); + metaData._webFragmentResourceMap.put(jar2,f2); + + TestResource jar3 = new TestResource("C"); + resources.add(jar3); + TestResource r3 = new TestResource("C/web-fragment.xml"); + FragmentDescriptor f3 = new FragmentDescriptor(r3, metaData); + f3._name="C"; + metaData._webFragmentNameMap.put(f3._name, f3); + metaData._webFragmentResourceMap.put(jar3,f3); + + TestResource jar4 = new TestResource("D"); + resources.add(jar4); + TestResource r4 = new TestResource("D/web-fragment.xml"); + FragmentDescriptor f4 = new FragmentDescriptor(r4, metaData); + f4._name="D"; + metaData._webFragmentNameMap.put(f4._name, f4); + metaData._webFragmentResourceMap.put(jar4,f4); + + TestResource jar5 = new TestResource("E"); + resources.add(jar5); + TestResource r5 = new TestResource("E/web-fragment.xml"); + FragmentDescriptor f5 = new FragmentDescriptor(r5, metaData); + f5._name="E"; + metaData._webFragmentNameMap.put(f5._name, f5); + metaData._webFragmentResourceMap.put(jar5,f5); + + TestResource jar6 = new TestResource("plain"); + resources.add(jar6); + TestResource r6 = new TestResource("plain/web-fragment.xml"); + FragmentDescriptor f6 = new FragmentDescriptor(r6, metaData); + f6._name=FragmentDescriptor.NAMELESS+"1"; + metaData._webFragmentNameMap.put(f6._name, f6); + metaData._webFragmentResourceMap.put(jar6,f6); + + List list = metaData._ordering.order(resources); + String[] outcomes = {"CBA"}; + String result = ""; + for (Resource r:list) + result += ((TestResource)r)._name; + + + if (!checkResult(result, outcomes)) + fail("No outcome matched "+result); + } + + public void testAbsoluteOrdering3 () + throws Exception + { + //empty + + WebAppContext wac = new WebAppContext(); + MetaData metaData = new MetaData(wac); + metaData._ordering = metaData.new AbsoluteOrdering(); + List resources = new ArrayList(); + + resources.add(new TestResource("A")); + resources.add(new TestResource("B")); + + List list = metaData._ordering.order(resources); + assertTrue(list.isEmpty()); + } + + + public void testRelativeOrderingWithPlainJars () + throws Exception + { + //B,A,C other jars with no fragments + List resources = new ArrayList(); + WebAppContext wac = new WebAppContext(); + MetaData metaData = new MetaData(wac); + metaData._ordering = metaData.new RelativeOrdering(); + + //A: after others, before C + TestResource jar1 = new TestResource("A"); + resources.add(jar1); + TestResource r1 = new TestResource("A/web-fragment.xml"); + FragmentDescriptor f1 = new FragmentDescriptor(r1, metaData); + f1._name = "A"; + metaData._webFragmentNameMap.put(f1._name, f1); + metaData._webFragmentResourceMap.put(jar1, f1); + //((RelativeOrdering)metaData._ordering).addAfterOthers(f1); + f1._otherType = FragmentDescriptor.OtherType.After; + f1._befores.add("C"); + + //B: before others, before C + TestResource jar2 = new TestResource("B"); + resources.add(jar2); + TestResource r2 = new TestResource("B/web-fragment.xml"); + FragmentDescriptor f2 = new FragmentDescriptor(r2, metaData); + f2._name="B"; + metaData._webFragmentNameMap.put(f2._name, f2); + metaData._webFragmentResourceMap.put(jar2,f2); + //((RelativeOrdering)metaData._ordering).addBeforeOthers(f2); + f2._otherType = FragmentDescriptor.OtherType.Before; + f2._befores.add("C"); + + //C: after A + TestResource jar3 = new TestResource("C"); + resources.add(jar3); + TestResource r3 = new TestResource("C/web-fragment.xml"); + FragmentDescriptor f3 = new FragmentDescriptor(r3, metaData); + f3._name="C"; + metaData._webFragmentNameMap.put(f3._name, f3); + metaData._webFragmentResourceMap.put(jar3,f3); + //((RelativeOrdering)metaData._ordering).addNoOthers(f3); + f3._otherType = FragmentDescriptor.OtherType.None; + f3._afters.add("A"); + + //No fragment jar 1 + TestResource r4 = new TestResource("plain1"); + resources.add(r4); + + //No fragment jar 2 + TestResource r5 = new TestResource("plain2"); + resources.add(r5); + + //result: BAC + String[] outcomes = {"Bplain1plain2AC"}; + + List orderedList = metaData._ordering.order(resources); + String result = ""; + for (Resource r:orderedList) + result+=(((TestResource)r)._name); + + if (!checkResult(result, outcomes)) + fail ("No outcome matched "+result); + } + + public void testAbsoluteOrderingWithPlainJars() + throws Exception + { + // + // A,B,C,others + // + List resources = new ArrayList(); + WebAppContext wac = new WebAppContext(); + MetaData metaData = new MetaData(wac); + metaData._ordering = metaData.new AbsoluteOrdering(); + ((AbsoluteOrdering)metaData._ordering).add("A"); + ((AbsoluteOrdering)metaData._ordering).add("B"); + ((AbsoluteOrdering)metaData._ordering).add("C"); + ((AbsoluteOrdering)metaData._ordering).addOthers(); + + TestResource jar1 = new TestResource("A"); + resources.add(jar1); + TestResource r1 = new TestResource("A/web-fragment.xml"); + FragmentDescriptor f1 = new FragmentDescriptor(r1, metaData); + f1._name = "A"; + metaData._webFragmentNameMap.put(f1._name, f1); + metaData._webFragmentResourceMap.put(jar1,f1); + + TestResource jar2 = new TestResource("B"); + resources.add(jar2); + TestResource r2 = new TestResource("B/web-fragment.xml"); + FragmentDescriptor f2 = new FragmentDescriptor(r2, metaData); + f2._name="B"; + metaData._webFragmentNameMap.put(f2._name, f2); + metaData._webFragmentResourceMap.put(jar2, f2); + + TestResource jar3 = new TestResource("C"); + resources.add(jar3); + TestResource r3 = new TestResource("C/web-fragment.xml"); + FragmentDescriptor f3 = new FragmentDescriptor(r3, metaData); + f3._name="C"; + metaData._webFragmentNameMap.put(f3._name, f3); + metaData._webFragmentResourceMap.put(jar3, f3); + + TestResource jar4 = new TestResource("D"); + resources.add(jar4); + TestResource r4 = new TestResource("D/web-fragment.xml"); + FragmentDescriptor f4 = new FragmentDescriptor((Resource)null, metaData); + f4._name="D"; + metaData._webFragmentNameMap.put(f4._name, f4); + metaData._webFragmentResourceMap.put(jar4, f4); + + TestResource jar5 = new TestResource("E"); + resources.add(jar5); + TestResource r5 = new TestResource("E/web-fragment.xml"); + FragmentDescriptor f5 = new FragmentDescriptor((Resource)null, metaData); + f5._name="E"; + metaData._webFragmentNameMap.put(f5._name, f5); + metaData._webFragmentResourceMap.put(jar5, f5); + + TestResource jar6 = new TestResource("plain"); + resources.add(jar6); + TestResource r6 = new TestResource("plain/web-fragment.xml"); + FragmentDescriptor f6 = new FragmentDescriptor((Resource)null, metaData); + f6._name=FragmentDescriptor.NAMELESS+"1"; + metaData._webFragmentNameMap.put(f6._name, f6); + metaData._webFragmentResourceMap.put(jar6, f6); + + //plain jar + TestResource r7 = new TestResource("plain1"); + resources.add(r7); + + TestResource r8 = new TestResource("plain2"); + resources.add(r8); + + List list = metaData._ordering.order(resources); + + String[] outcomes = {"ABCDEplainplain1plain2"}; + String result = ""; + for (Resource r:list) + result += ((TestResource)r)._name; + + if (!checkResult(result, outcomes)) + fail("No outcome matched "+result); + } + + + + public boolean checkResult (String result, String[] outcomes) + { + boolean matched = false; + for (String s:outcomes) + { + if (s.equals(result)) + matched = true; + } + return matched; + } +}