Bring jetty-7 into line with new annotation parsing mechansim from jetty-8

git-svn-id: svn+ssh://dev.eclipse.org/svnroot/rt/org.eclipse.jetty/jetty/trunk@575 7e9141cc-0065-0410-87d8-b60c137991c4
This commit is contained in:
Jan Bartel 2009-07-22 00:51:14 +00:00
parent 640b20941a
commit 0358f29479
35 changed files with 2860 additions and 2132 deletions

View File

@ -0,0 +1,180 @@
// ========================================================================
// 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.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.WebAppContext;
import org.eclipse.jetty.webapp.WebXmlProcessor;
import org.eclipse.jetty.webapp.WebXmlProcessor.Descriptor;
import org.eclipse.jetty.webapp.WebInfConfiguration;
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 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");
//Convert from Resource to URI
ArrayList<URI> containerUris = new ArrayList<URI>();
List<Resource> jarResources = (List<Resource>)context.getAttribute(CONTAINER_JAR_RESOURCES);
for (Resource r : jarResources)
{
URI uri = r.getURI();
containerUris.add(uri);
}
parser.parse (containerUris.toArray(new URI[containerUris.size()]),
new ClassNameResolver ()
{
public boolean isExcluded (String name)
{
if (context.isSystemClass(name)) return false;
if (context.isServerClass(name)) return true;
return false;
}
public boolean shouldOverride (String name)
{
//looking at system classpath
if (context.isParentLoaderPriority())
return true;
return false;
}
});
}
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");
List<Descriptor> frags = webXmlProcessor.getFragments();
//Get the web-inf lib jars who have a web-fragment.xml that is not metadata-complete (or is not set)
ArrayList<URI> webInfUris = new ArrayList<URI>();
List<Resource> jarResources = (List<Resource>)context.getAttribute(WEB_INF_JAR_RESOURCES);
for (Resource r : jarResources)
{
URI uri = r.getURI();
Descriptor d = null;
for (Descriptor frag: frags)
{
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;
}
}
//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;
}
});
}
public void parseWebInfClasses (final WebAppContext context, final AnnotationParser parser)
throws Exception
{
Log.debug("Scanning classes in WEB-INF/classes");
parser.parse(context.getWebInf().addPath("classes/"),
new ClassNameResolver()
{
public boolean isExcluded (String name)
{
if (context.isSystemClass(name)) return true;
if (context.isServerClass(name)) return false;
return false;
}
public boolean shouldOverride (String name)
{
//looking at webapp classpath, found already-parsed class of same name - did it come from system or duplicate in webapp?
if (context.isParentLoaderPriority())
return false;
return true;
}
});
}
public void parse25Classes (final WebAppContext context, final AnnotationParser parser)
throws Exception
{
//only parse servlets, filters and listeners from web.xml
if (Log.isDebugEnabled()) Log.debug("Scanning only classes from web.xml");
ArrayList<String> classNames = (ArrayList<String>)context.getAttribute(WEBXML_CLASSNAMES);
for (String s : classNames)
{
Class clazz = Loader.loadClass(null, s);
parser.parse(clazz, 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;
}
}, true);
}
}
}

View File

@ -13,218 +13,83 @@
package org.eclipse.jetty.annotations;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.EventListener;
import java.util.List;
import org.eclipse.jetty.plus.servlet.ServletHandler;
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlet.FilterMapping;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.servlet.ServletMapping;
import org.eclipse.jetty.util.LazyList;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.webapp.WebInfConfiguration;
/**
* Configuration
*
*
*/
public class AnnotationConfiguration extends org.eclipse.jetty.plus.webapp.Configuration
public class AnnotationConfiguration extends AbstractConfiguration
{
public static final String JAR_RESOURCES = WebInfConfiguration.JAR_RESOURCES;
public static final String CLASS_INHERITANCE_MAP = "org.eclipse.jetty.classInheritanceMap";
/**
* @see org.eclipse.jetty.plus.webapp.AbstractConfiguration#parseAnnotations()
*/
public void parseAnnotations(final WebAppContext context) throws Exception
public void preConfigure(final WebAppContext context) throws Exception
{
/*
* TODO Need to also take account of hidden classes on system classpath that should never
* contribute annotations to a webapp (system and server classes):
*
* --- when scanning system classpath:
* + system classes : should always be scanned (subject to pattern)
* + server classes : always ignored
*
* --- when scanning webapp classpath:
* + system classes : always ignored
* + server classes : always scanned
*
*
* If same class is found in both container and in context then need to use
* webappcontext parentloaderpriority to work out which one contributes the
* annotation.
*/
AnnotationFinder finder = new AnnotationFinder();
//TODO change for servlet spec 3
parseContainerPath (context, finder);
parseWebInfLib (context, finder);
parseWebInfClasses (context, finder);
AnnotationProcessor processor = new AnnotationProcessor(context, finder);
processor.process();
List servlets = processor.getServlets();
List filters = processor.getFilters();
List servletMappings = processor.getServletMappings();
List filterMappings = processor.getFilterMappings();
List listeners = processor.getListeners();
ServletHandler servletHandler = (ServletHandler)context.getServletHandler();
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));
}
public void parseContainerPath (final WebAppContext context, final AnnotationFinder finder)
throws Exception
public void configure(WebAppContext context) throws Exception
{
//if no pattern for the container path is defined, then by default scan NOTHING
Log.debug("Scanning container jars");
//Get the container jar uris
ArrayList<URI> containerCandidateUris = findJars (context.getClassLoader().getParent(), true);
//Pick out the uris from JAR_RESOURCES that match those uris to be scanned
ArrayList<URI> containerUris = new ArrayList<URI>();
List<Resource> jarResources = (List<Resource>)context.getAttribute(JAR_RESOURCES);
for (Resource r : jarResources)
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)
{
URI uri = r.getURI();
if (containerCandidateUris.contains(uri))
{
containerUris.add(uri);
}
//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;
}
finder.find (containerUris.toArray(new URI[containerUris.size()]),
new ClassNameResolver ()
{
public boolean isExcluded (String name)
{
if (context.isSystemClass(name)) return false;
if (context.isServerClass(name)) return true;
return false;
}
public boolean shouldOverride (String name)
{
//looking at system classpath
if (context.isParentLoaderPriority())
return true;
return false;
}
});
}
public void parseWebInfLib (final WebAppContext context, final AnnotationFinder finder)
throws Exception
{
Log.debug("Scanning WEB-INF/lib jars");
//Get the uris of jars on the webapp classloader
ArrayList<URI> candidateUris = findJars(context.getClassLoader(), false);
//Pick out the uris from JAR_RESOURCES that match those to be scanned
ArrayList<URI> webInfUris = new ArrayList<URI>();
List<Resource> jarResources = (List<Resource>)context.getAttribute(JAR_RESOURCES);
for (Resource r : jarResources)
else
{
URI uri = r.getURI();
if (candidateUris.contains(uri))
{
webInfUris.add(uri);
}
}
//if no pattern for web-inf/lib is defined, then by default scan everything in it
finder.find(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;
}
//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();
public boolean shouldOverride (String name)
{
//looking at webapp classpath, found already-parsed class of same name - did it come from system or duplicate in webapp?
if (context.isParentLoaderPriority())
return false;
return true;
}
});
}
public void parseWebInfClasses (final WebAppContext context, final AnnotationFinder finder)
throws Exception
{
Log.debug("Scanning classes in WEB-INF/classes");
finder.find(context.getWebInf().addPath("classes/"),
new ClassNameResolver()
{
public boolean isExcluded (String name)
{
if (context.isSystemClass(name)) return true;
if (context.isServerClass(name)) return false;
return false;
}
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));
public boolean shouldOverride (String name)
ClassInheritanceHandler classHandler = new ClassInheritanceHandler();
parser.registerClassHandler(classHandler);
if (webxmlVersion >= 30 || context.isConfigurationDiscovered())
{
//looking at webapp classpath, found already-parsed class of same name - did it come from system or duplicate in webapp?
if (context.isParentLoaderPriority())
return false;
return true;
}
});
}
public ArrayList<URI> findJars (ClassLoader loader, boolean visitParent)
{
ArrayList<URI> uris = new ArrayList<URI>();
while (loader != null && (loader instanceof URLClassLoader))
{
URL[] urls = ((URLClassLoader)loader).getURLs();
if (urls != null)
{
for (URL u : urls)
{
try
{
uris.add(u.toURI());
}
catch (Exception e)
{
Log.warn(e);
}
}
}
if (visitParent)
loader = loader.getParent();
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
loader = null;
}
return uris;
{
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());
}
}
public void deconfigure(WebAppContext context) throws Exception
{
}
public void postConfigure(WebAppContext context) throws Exception
{
}
}

View File

@ -1,781 +0,0 @@
// ========================================================================
// Copyright (c) 2008-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.IOException;
import java.io.InputStream;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.regex.Pattern;
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.JarScanner;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.EmptyVisitor;
/**
* AnnotationFinder
*
*
* Scans class sources using asm to find annotations.
*
*
*/
public class AnnotationFinder
{
private Map<String,ParsedClass> parsedClasses = new HashMap<String, ParsedClass>();
public static String normalize (String name)
{
if (name==null)
return null;
if (name.startsWith("L") && name.endsWith(";"))
name = name.substring(1, name.length()-1);
if (name.endsWith(".class"))
name = name.substring(0, name.length()-".class".length());
name = name.replace('$', '.');
return name.replace('/', '.');
}
public static Class convertType (org.objectweb.asm.Type t)
throws Exception
{
if (t == null)
return (Class)null;
switch (t.getSort())
{
case Type.BOOLEAN:
{
return Boolean.TYPE;
}
case Type.ARRAY:
{
Class clazz = convertType(t.getElementType());
return Array.newInstance(clazz, 0).getClass();
}
case Type.BYTE:
{
return Byte.TYPE;
}
case Type.CHAR:
{
return Character.TYPE;
}
case Type.DOUBLE:
{
return Double.TYPE;
}
case Type.FLOAT:
{
return Float.TYPE;
}
case Type.INT:
{
return Integer.TYPE;
}
case Type.LONG:
{
return Long.TYPE;
}
case Type.OBJECT:
{
return (Loader.loadClass(null, t.getClassName()));
}
case Type.SHORT:
{
return Short.TYPE;
}
case Type.VOID:
{
return null;
}
default:
return null;
}
}
public static Class[] convertTypes (Type[] types)
throws Exception
{
if (types==null)
return new Class[0];
Class[] classArray = new Class[types.length];
for (int i=0; i<types.length; i++)
{
classArray[i] = convertType(types[i]);
}
return classArray;
}
/**
* AnnotatedStructure
*
* Annotations on an object such as a class, field or method.
*/
public static class AnnotatedStructure extends EmptyVisitor
{
Map<String, Map<String, Object>> annotations = new HashMap<String, Map<String,Object>>();
public AnnotationVisitor addAnnotation (final String name)
{
final HashMap<String,Object> annotationValues = new HashMap<String,Object>();
this.annotations.put(normalize(name), annotationValues);
return new AnnotationVisitor()
{
public void visit(String name, Object value)
{
annotationValues.put(name, value);
}
public AnnotationVisitor visitAnnotation(String name, String desc)
{
return null; //ignore nested annotations
}
public AnnotationVisitor visitArray(String arg0)
{
return null;//ignore array valued annotations
}
public void visitEnd()
{
}
public void visitEnum(String name, String desc, String value)
{
}
};
}
public Map<String, Map<String, Object>> getAnnotations ()
{
return annotations;
}
public String toString()
{
StringBuffer strbuff = new StringBuffer();
for (Map.Entry<String, Map<String,Object>> e: annotations.entrySet())
{
strbuff.append(e.getKey()+"\n");
for (Map.Entry<String,Object> v: e.getValue().entrySet())
{
strbuff.append("\t"+v.getKey()+"="+v.getValue()+", ");
}
}
return strbuff.toString();
}
}
/**
* ParsedClass
*
* A class that contains annotations.
*/
public static class ParsedClass extends AnnotatedStructure
{
String className;
String superClassName;
Class clazz;
List<ParsedMethod> methods = new ArrayList<ParsedMethod>();
List<ParsedField> fields = new ArrayList<ParsedField>();
public ParsedClass (String className, String superClassName)
{
this.className = normalize(className);
this.superClassName = normalize(superClassName);
}
public String getClassName()
{
return this.className;
}
public String getSuperClassName ()
{
return this.superClassName;
}
public Class toClass ()
throws ClassNotFoundException
{
if (clazz==null)
clazz = Loader.loadClass(null, className);
return clazz;
}
public List<ParsedMethod> getMethods ()
{
return methods;
}
public ParsedMethod getMethod(String name, String paramString)
{
Iterator<ParsedMethod> itor = methods.iterator();
ParsedMethod method = null;
while (itor.hasNext() && method==null)
{
ParsedMethod m = itor.next();
if (m.matches(name, paramString))
method = m;
}
return method;
}
public void addMethod (ParsedMethod m)
{
if (getMethod(m.methodName, m.paramString)!= null)
return;
methods.add(m);
}
public List<ParsedField> getFields()
{
return fields;
}
public ParsedField getField(String name)
{
Iterator<ParsedField> itor = fields.iterator();
ParsedField field = null;
while (itor.hasNext() && field==null)
{
ParsedField f = itor.next();
if (f.matches(name))
field=f;
}
return field;
}
public void addField (ParsedField f)
{
if (getField(f.fieldName) != null)
return;
fields.add(f);
}
public String toString ()
{
StringBuffer strbuff = new StringBuffer();
strbuff.append(this.className+"\n");
strbuff.append("Class annotations\n"+super.toString());
strbuff.append("\n");
strbuff.append("Method annotations\n");
for (ParsedMethod p:methods)
strbuff.append(p+"\n");
strbuff.append("\n");
strbuff.append("Field annotations\n");
for (ParsedField f:fields)
strbuff.append(f+"\n");
strbuff.append("\n");
return strbuff.toString();
}
}
/**
* ParsedMethod
*
* A class method that can contain annotations.
*/
public static class ParsedMethod extends AnnotatedStructure
{
ParsedClass pclass;
String methodName;
String paramString;
Method method;
public ParsedMethod(ParsedClass pclass, String name, String paramString)
{
this.pclass=pclass;
this.methodName=name;
this.paramString=paramString;
}
public AnnotationVisitor visitAnnotation(String desc, boolean visible)
{
this.pclass.addMethod(this);
return addAnnotation(desc);
}
public Method toMethod ()
throws Exception
{
if (method == null)
{
Type[] types = null;
if (paramString!=null)
types = Type.getArgumentTypes(paramString);
Class[] args = convertTypes(types);
method = pclass.toClass().getDeclaredMethod(methodName, args);
}
return method;
}
public boolean matches (String name, String paramString)
{
if (!methodName.equals(name))
return false;
if (this.paramString!=null && this.paramString.equals(paramString))
return true;
return (this.paramString == paramString);
}
public String toString ()
{
return pclass.getClassName()+"."+methodName+"\n\t"+super.toString();
}
}
/**
* ParsedField
*
* A class field that can contain annotations. Also implements the
* asm visitor for Annotations.
*/
public static class ParsedField extends AnnotatedStructure
{
ParsedClass pclass;
String fieldName;
Field field;
public ParsedField (ParsedClass pclass, String name)
{
this.pclass=pclass;
this.fieldName=name;
}
public AnnotationVisitor visitAnnotation(String desc, boolean visible)
{
this.pclass.addField(this);
return addAnnotation(desc);
}
public Field toField ()
throws Exception
{
if (field==null)
{
field=this.pclass.toClass().getDeclaredField(fieldName);
}
return field;
}
public boolean matches (String name)
{
if (fieldName.equals(name))
return true;
return false;
}
public String toString ()
{
return pclass.getClassName()+"."+fieldName+"\n\t"+super.toString();
}
}
/**
* MyClassVisitor
*
* ASM visitor for a class.
*/
public class MyClassVisitor extends EmptyVisitor
{
ParsedClass pclass;
public void visit (int version,
int access,
String name,
String signature,
String superName,
String[] interfaces)
{
pclass = new ParsedClass(name, superName);
}
public AnnotationVisitor visitAnnotation (String desc, boolean visible)
{
if (!parsedClasses.containsKey(pclass.getClassName()))
parsedClasses.put(pclass.getClassName(), pclass);
return pclass.addAnnotation(desc);
}
public MethodVisitor visitMethod (int access,
String name,
String desc,
String signature,
String[] exceptions)
{
if (!parsedClasses.values().contains(pclass))
parsedClasses.put(pclass.getClassName(),pclass);
ParsedMethod method = pclass.getMethod(name, desc);
if (method==null)
method = new ParsedMethod(pclass, name, desc);
return method;
}
public FieldVisitor visitField (int access,
String name,
String desc,
String signature,
Object value)
{
if (!parsedClasses.values().contains(pclass))
parsedClasses.put(pclass.getClassName(),pclass);
ParsedField field = pclass.getField(name);
if (field==null)
field = new ParsedField(pclass, name);
return field;
}
}
public void find (String className, ClassNameResolver resolver)
throws Exception
{
if (className == null)
return;
if (!resolver.isExcluded(className))
{
if ((parsedClasses.get(className) == null) || (resolver.shouldOverride(className)))
{
parsedClasses.remove(className);
className = className.replace('.', '/')+".class";
URL resource = Loader.getResource(this.getClass(), className, false);
if (resource!= null)
scanClass(resource.openStream());
}
}
}
public void find (String[] classNames, ClassNameResolver resolver)
throws Exception
{
if (classNames == null)
return;
find(Arrays.asList(classNames), resolver);
}
public void find (List<String> classNames, ClassNameResolver resolver)
throws Exception
{
for (String s:classNames)
{
if (!resolver.isExcluded(s))
{
if ((parsedClasses.get(s) == null) || (resolver.shouldOverride(s)))
{
parsedClasses.remove(s);
s = s.replace('.', '/')+".class";
URL resource = Loader.getResource(this.getClass(), s, false);
if (resource!= null)
scanClass(resource.openStream());
}
}
}
}
public void find (Resource dir, ClassNameResolver resolver)
throws Exception
{
if (!dir.isDirectory() || !dir.exists())
return;
String[] files=dir.list();
for (int f=0;files!=null && f<files.length;f++)
{
try
{
Resource res = dir.addPath(files[f]);
if (res.isDirectory())
find(res, resolver);
String name = res.getName();
if (name.endsWith(".class"))
{
if (!resolver.isExcluded(name))
{
if ((parsedClasses.get(name) == null) || (resolver.shouldOverride(name)))
{
parsedClasses.remove(name);
scanClass(res.getURL().openStream());
}
}
}
}
catch (Exception ex)
{
Log.warn(Log.EXCEPTION,ex);
}
}
}
/**
* Find annotations on classes in the supplied classloader.
* Only class files in jar files will be scanned.
* @param loader
* @param visitParents
* @param jarNamePattern
* @param nullInclusive
* @param resolver
* @throws Exception
*/
public void find (ClassLoader loader, boolean visitParents, boolean nullInclusive, final ClassNameResolver resolver)
throws Exception
{
if (loader==null)
return;
if (!(loader instanceof URLClassLoader))
return; //can't extract classes?
JarScanner scanner = new JarScanner()
{
public void processEntry(URI jarUri, JarEntry entry)
{
try
{
String name = entry.getName();
if (name.toLowerCase().endsWith(".class"))
{
String shortName = name.replace('/', '.').substring(0,name.length()-6);
if (!resolver.isExcluded(shortName))
{
if ((parsedClasses.get(shortName) == null) || (resolver.shouldOverride(shortName)))
{
parsedClasses.remove(shortName);
Resource clazz = Resource.newResource("jar:"+jarUri+"!/"+name);
scanClass(clazz.getInputStream());
}
}
}
}
catch (Exception e)
{
Log.warn("Problem processing jar entry "+entry, e);
}
}
};
scanner.scan(null, loader, nullInclusive, visitParents);
}
/**
* Find annotations in classes in the supplied url of jar files.
* @param uris
* @param resolver
* @throws Exception
*/
public void find (URI[] uris, final ClassNameResolver resolver)
throws Exception
{
if (uris==null)
return;
JarScanner scanner = new JarScanner()
{
public void processEntry(URI jarUri, JarEntry entry)
{
try
{
String name = entry.getName();
if (name.toLowerCase().endsWith(".class"))
{
String shortName = name.replace('/', '.').substring(0,name.length()-6);
if (!resolver.isExcluded(shortName))
{
if ((parsedClasses.get(shortName) == null) || (resolver.shouldOverride(shortName)))
{
parsedClasses.remove(shortName);
Resource clazz = Resource.newResource("jar:"+jarUri+"!/"+name);
scanClass(clazz.getInputStream());
}
}
}
}
catch (Exception e)
{
Log.warn("Problem processing jar entry "+entry, e);
}
}
};
scanner.scan(null, uris, true);
}
/** Exclude class by name
* Instances of {@link AnnotationFinder} can implement this method to exclude
* classes by name.
* @param name
* @return
*/
protected boolean excludeClass (String name)
{
return false;
}
public List<Class<?>> getClassesForAnnotation(Class<?> annotationClass)
throws Exception
{
List<Class<?>> classes = new ArrayList<Class<?>>();
for (Map.Entry<String, ParsedClass> e: parsedClasses.entrySet())
{
ParsedClass pc = e.getValue();
Map<String, Map<String,Object>> annotations = pc.getAnnotations();
for (String key:annotations.keySet())
{
if (key.equals(annotationClass.getName()))
{
classes.add(pc.toClass());
}
}
}
return classes;
}
public List<Method> getMethodsForAnnotation (Class<?> annotationClass)
throws Exception
{
List<Method> methods = new ArrayList<Method>();
for (Map.Entry<String, ParsedClass> e: parsedClasses.entrySet())
{
ParsedClass pc = e.getValue();
List<ParsedMethod> pmethods = pc.getMethods();
for (ParsedMethod p:pmethods)
{
for (String key:p.getAnnotations().keySet())
{
if (key.equals(annotationClass.getName()))
{
methods.add(p.toMethod());
}
}
}
}
return methods;
}
public List<Field> getFieldsForAnnotation (Class<?> annotation)
throws Exception
{
List<Field> fields = new ArrayList<Field>();
for (Map.Entry<String, ParsedClass> e: parsedClasses.entrySet())
{
ParsedClass pc = e.getValue();
List<ParsedField> pfields = pc.getFields();
for (ParsedField f:pfields)
{
for (String key:f.getAnnotations().keySet())
{
if (key.equals(annotation.getName()))
{
fields.add(f.toField());
}
}
}
}
return fields;
}
public String toString ()
{
StringBuffer strbuff = new StringBuffer();
for (Map.Entry<String, ParsedClass> e:parsedClasses.entrySet())
{
strbuff.append(e.getValue());
strbuff.append("\n");
}
return strbuff.toString();
}
private void scanClass (InputStream is)
throws IOException
{
ClassReader reader = new ClassReader(is);
reader.accept(new MyClassVisitor(), ClassReader.SKIP_CODE|ClassReader.SKIP_DEBUG|ClassReader.SKIP_FRAMES);
}
}

View File

@ -0,0 +1,598 @@
// ========================================================================
// 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.annotations;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.jar.JarEntry;
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.JarScanner;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.commons.EmptyVisitor;
/**
* AnnotationParser
*
* Use asm to scan classes for annotations. A SAX-style parsing is done, with
* a handler being able to be registered to handle each annotation type.
*/
public class AnnotationParser
{
protected List<String> _parsedClassNames = new ArrayList<String>();
protected Map<String, AnnotationHandler> _annotationHandlers = new HashMap<String, AnnotationHandler>();
protected List<ClassHandler> _classHandlers = new ArrayList<ClassHandler>();
protected List<MethodHandler> _methodHandlers = new ArrayList<MethodHandler>();
protected List<FieldHandler> _fieldHandlers = new ArrayList<FieldHandler>();
public static String normalize (String name)
{
if (name==null)
return null;
if (name.startsWith("L") && name.endsWith(";"))
name = name.substring(1, name.length()-1);
if (name.endsWith(".class"))
name = name.substring(0, name.length()-".class".length());
return name.replace('/', '.');
}
public abstract class Value
{
String _name;
public Value (String name)
{
_name = name;
}
public String getName()
{
return _name;
}
public abstract Object getValue();
}
public class SimpleValue extends Value
{
Object _val;
public SimpleValue(String name)
{
super(name);
}
public void setValue(Object val)
{
_val=val;
}
public Object getValue()
{
return _val;
}
public String toString()
{
return "("+getName()+":"+_val+")";
}
}
public class ListValue extends Value
{
List<Value> _val;
public ListValue (String name)
{
super(name);
_val = new ArrayList<Value>();
}
public Object getValue()
{
return _val;
}
public List<Value> getList()
{
return _val;
}
public void addValue (Value v)
{
_val.add(v);
}
public int size ()
{
return _val.size();
}
public String toString()
{
StringBuffer buff = new StringBuffer();
buff.append("(");
buff.append(getName());
buff.append(":");
for (Value n: _val)
{
buff.append(" "+n.toString());
}
buff.append(")");
return buff.toString();
}
}
public interface AnnotationHandler
{
public void handleClass (String className, int version, int access,
String signature, String superName, String[] interfaces,
String annotation, List<Value>values);
public void handleMethod (String className, String methodName, int access,
String params, String signature,String[] exceptions,
String annotation, List<Value>values);
public void handleField (String className, String fieldName, int access,
String fieldType, String signature, Object value,
String annotation, List<Value>values);
}
public interface ClassHandler
{
public void handle (String className, int version, int access, String signature, String superName, String[] interfaces);
}
public interface MethodHandler
{
public void handle (String className, String methodName, int access, String params, String signature,String[] exceptions);
}
public interface FieldHandler
{
public void handle (String className, String fieldName, int access, String fieldType, String signature, Object value);
}
public class MyAnnotationVisitor implements AnnotationVisitor
{
List<Value> _annotationValues;
String _annotationName;
public MyAnnotationVisitor (String annotationName, List<Value> values)
{
_annotationValues = values;
_annotationName = annotationName;
}
public List<Value> getAnnotationValues()
{
return _annotationValues;
}
/**
* Visit a single-valued (name,value) pair for this annotation
* @see org.objectweb.asm.AnnotationVisitor#visit(java.lang.String, java.lang.Object)
*/
public void visit(String aname, Object avalue)
{
SimpleValue v = new SimpleValue(aname);
v.setValue(avalue);
_annotationValues.add(v);
}
/**
* Visit a (name,value) pair whose value is another Annotation
* @see org.objectweb.asm.AnnotationVisitor#visitAnnotation(java.lang.String, java.lang.String)
*/
public AnnotationVisitor visitAnnotation(String name, String desc)
{
String s = normalize(desc);
ListValue v = new ListValue(s);
_annotationValues.add(v);
MyAnnotationVisitor visitor = new MyAnnotationVisitor(s, v.getList());
return visitor;
}
/**
* Visit an array valued (name, value) pair for this annotation
* @see org.objectweb.asm.AnnotationVisitor#visitArray(java.lang.String)
*/
public AnnotationVisitor visitArray(String name)
{
ListValue v = new ListValue(name);
_annotationValues.add(v);
MyAnnotationVisitor visitor = new MyAnnotationVisitor(null, v.getList());
return visitor;
}
/**
* Visit a enum-valued (name,value) pair for this annotation
* @see org.objectweb.asm.AnnotationVisitor#visitEnum(java.lang.String, java.lang.String, java.lang.String)
*/
public void visitEnum(String name, String desc, String value)
{
//TODO
}
public void visitEnd()
{
}
}
/**
* MyClassVisitor
*
* ASM visitor for a class.
*/
public class MyClassVisitor extends EmptyVisitor
{
String _className;
int _access;
String _signature;
String _superName;
String[] _interfaces;
int _version;
public void visit (int version,
final int access,
final String name,
final String signature,
final String superName,
final String[] interfaces)
{
_className = normalize(name);
_access = access;
_signature = signature;
_superName = superName;
_interfaces = interfaces;
_version = version;
_parsedClassNames.add(_className);
//call all registered ClassHandlers
String[] normalizedInterfaces = null;
if (interfaces!= null)
{
normalizedInterfaces = new String[interfaces.length];
int i=0;
for (String s : interfaces)
normalizedInterfaces[i++] = normalize(s);
}
for (ClassHandler h : AnnotationParser.this._classHandlers)
{
h.handle(_className, _version, _access, _signature, normalize(_superName), normalizedInterfaces);
}
}
public AnnotationVisitor visitAnnotation (String desc, boolean visible)
{
MyAnnotationVisitor visitor = new MyAnnotationVisitor(normalize(desc), new ArrayList<Value>())
{
public void visitEnd()
{
super.visitEnd();
//call all AnnotationHandlers with classname, annotation name + values
AnnotationHandler handler = AnnotationParser.this._annotationHandlers.get(_annotationName);
if (handler != null)
{
handler.handleClass(_className, _version, _access, _signature, _superName, _interfaces, _annotationName, _annotationValues);
}
}
};
return visitor;
}
public MethodVisitor visitMethod (final int access,
final String name,
final String params,
final String signature,
final String[] exceptions)
{
return new EmptyVisitor ()
{
public AnnotationVisitor visitAnnotation(String desc, boolean visible)
{
MyAnnotationVisitor visitor = new MyAnnotationVisitor (normalize(desc), new ArrayList<Value>())
{
public void visitEnd()
{
super.visitEnd();
//call all AnnotationHandlers with classname, method, annotation name + values
AnnotationHandler handler = AnnotationParser.this._annotationHandlers.get(_annotationName);
if (handler != null)
{
handler.handleMethod(_className, name, access, params, signature, exceptions, _annotationName, _annotationValues);
}
}
};
return visitor;
}
};
}
public FieldVisitor visitField (final int access,
final String fieldName,
final String fieldType,
final String signature,
final Object value)
{
return new EmptyVisitor ()
{
public AnnotationVisitor visitAnnotation(String desc, boolean visible)
{
MyAnnotationVisitor visitor = new MyAnnotationVisitor(normalize(desc), new ArrayList<Value>())
{
public void visitEnd()
{
super.visitEnd();
AnnotationHandler handler = AnnotationParser.this._annotationHandlers.get(_annotationName);
if (handler != null)
{
handler.handleField(_className, fieldName, access, fieldType, signature, value, _annotationName, _annotationValues);
}
}
};
return visitor;
}
};
}
}
public void registerAnnotationHandler (String annotationName, AnnotationHandler handler)
{
_annotationHandlers.put(annotationName, handler);
}
public void registerClassHandler (ClassHandler handler)
{
_classHandlers.add(handler);
}
public boolean isParsed (String className)
{
return _parsedClassNames.contains(className);
}
public void parse (String className, ClassNameResolver resolver)
throws Exception
{
if (className == null)
return;
if (!resolver.isExcluded(className))
{
if (!isParsed(className) || resolver.shouldOverride(className))
{
className = className.replace('.', '/')+".class";
URL resource = Loader.getResource(this.getClass(), className, false);
if (resource!= null)
scanClass(resource.openStream());
}
}
}
public void parse (Class clazz, ClassNameResolver resolver, boolean visitSuperClasses)
throws Exception
{
Class cz = clazz;
while (cz != null)
{
if (!resolver.isExcluded(cz.getName()))
{
if (!isParsed(cz.getName()) || resolver.shouldOverride(cz.getName()))
{
String nameAsResource = cz.getName().replace('.', '/')+".class";
URL resource = Loader.getResource(this.getClass(), nameAsResource, false);
if (resource!= null)
scanClass(resource.openStream());
}
}
if (visitSuperClasses)
cz = cz.getSuperclass();
else
cz = null;
}
}
public void parse (String[] classNames, ClassNameResolver resolver)
throws Exception
{
if (classNames == null)
return;
parse(Arrays.asList(classNames), resolver);
}
public void parse (List<String> classNames, ClassNameResolver resolver)
throws Exception
{
for (String s:classNames)
{
if ((resolver == null) || (!resolver.isExcluded(s) && (!isParsed(s) || resolver.shouldOverride(s))))
{
s = s.replace('.', '/')+".class";
URL resource = Loader.getResource(this.getClass(), s, false);
if (resource!= null)
scanClass(resource.openStream());
}
}
}
public void parse (Resource dir, ClassNameResolver resolver)
throws Exception
{
if (!dir.isDirectory() || !dir.exists())
return;
String[] files=dir.list();
for (int f=0;files!=null && f<files.length;f++)
{
try
{
Resource res = dir.addPath(files[f]);
if (res.isDirectory())
parse(res, resolver);
String name = res.getName();
if (name.endsWith(".class"))
{
if ((resolver == null)|| (!resolver.isExcluded(name) && (!isParsed(name) || resolver.shouldOverride(name))))
scanClass(res.getURL().openStream());
}
}
catch (Exception ex)
{
Log.warn(Log.EXCEPTION,ex);
}
}
}
/**
* Find annotations on classes in the supplied classloader.
* Only class files in jar files will be scanned.
* @param loader
* @param visitParents
* @param jarNamePattern
* @param nullInclusive
* @param resolver
* @throws Exception
*/
public void parse (ClassLoader loader, boolean visitParents, boolean nullInclusive, final ClassNameResolver resolver)
throws Exception
{
if (loader==null)
return;
if (!(loader instanceof URLClassLoader))
return; //can't extract classes?
JarScanner scanner = new JarScanner()
{
public void processEntry(URI jarUri, JarEntry entry)
{
try
{
String name = entry.getName();
if (name.toLowerCase().endsWith(".class"))
{
String shortName = name.replace('/', '.').substring(0,name.length()-6);
if ((resolver == null)
||
(!resolver.isExcluded(shortName) && (!isParsed(shortName) || resolver.shouldOverride(shortName))))
{
Resource clazz = Resource.newResource("jar:"+jarUri+"!/"+name);
scanClass(clazz.getInputStream());
}
}
}
catch (Exception e)
{
Log.warn("Problem processing jar entry "+entry, e);
}
}
};
scanner.scan(null, loader, nullInclusive, visitParents);
}
/**
* Find annotations in classes in the supplied url of jar files.
* @param uris
* @param resolver
* @throws Exception
*/
public void parse (URI[] uris, final ClassNameResolver resolver)
throws Exception
{
if (uris==null)
return;
JarScanner scanner = new JarScanner()
{
public void processEntry(URI jarUri, JarEntry entry)
{
try
{
String name = entry.getName();
if (name.toLowerCase().endsWith(".class"))
{
String shortName = name.replace('/', '.').substring(0,name.length()-6);
if ((resolver == null)
||
(!resolver.isExcluded(shortName) && (!isParsed(shortName) || resolver.shouldOverride(shortName))))
{
Resource clazz = Resource.newResource("jar:"+jarUri+"!/"+name);
scanClass(clazz.getInputStream());
}
}
}
catch (Exception e)
{
Log.warn("Problem processing jar entry "+entry, e);
}
}
};
scanner.scan(null, uris, true);
}
private void scanClass (InputStream is)
throws IOException
{
ClassReader reader = new ClassReader(is);
reader.accept(new MyClassVisitor(), ClassReader.SKIP_CODE|ClassReader.SKIP_DEBUG|ClassReader.SKIP_FRAMES);
}
}

View File

@ -1,645 +0,0 @@
// ========================================================================
// Copyright (c) 2008-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.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import javax.annotation.Resources;
import javax.annotation.security.RunAs;
import javax.naming.InitialContext;
import javax.naming.NameNotFoundException;
import javax.naming.NamingException;
import org.eclipse.jetty.plus.annotation.Injection;
import org.eclipse.jetty.plus.annotation.InjectionCollection;
import org.eclipse.jetty.plus.annotation.LifeCycleCallbackCollection;
import org.eclipse.jetty.plus.annotation.PostConstructCallback;
import org.eclipse.jetty.plus.annotation.PreDestroyCallback;
import org.eclipse.jetty.plus.annotation.RunAsCollection;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.util.IntrospectionUtil;
import org.eclipse.jetty.util.LazyList;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.webapp.WebAppContext;
/**
* AnnotationProcessor
*
* Act on the annotations discovered in the webapp.
*/
public class AnnotationProcessor
{
AnnotationFinder _finder;
ClassLoader _loader;
RunAsCollection _runAs;
InjectionCollection _injections;
LifeCycleCallbackCollection _callbacks;
List _servlets;
List _filters;
List _listeners;
List _servletMappings;
List _filterMappings;
WebAppContext _webApp;
private static Class[] __envEntryTypes =
new Class[] {String.class, Character.class, Integer.class, Boolean.class, Double.class, Byte.class, Short.class, Long.class, Float.class};
public AnnotationProcessor(WebAppContext webApp, AnnotationFinder finder)
{
if (webApp == null)
throw new IllegalStateException("No WebAppContext");
_webApp=webApp;
_finder=finder;
ServletHandler servletHandler = _webApp.getServletHandler();
_filters = LazyList.array2List(servletHandler.getFilters());
_filterMappings = LazyList.array2List(servletHandler.getFilterMappings());
_servlets = LazyList.array2List(servletHandler.getServlets());
_servletMappings = LazyList.array2List(servletHandler.getServletMappings());
_listeners = LazyList.array2List(_webApp.getEventListeners());
_runAs = (RunAsCollection)_webApp.getAttribute(RunAsCollection.RUNAS_COLLECTION);
_injections = (InjectionCollection)_webApp.getAttribute(InjectionCollection.INJECTION_COLLECTION);
_callbacks = (LifeCycleCallbackCollection)_webApp.getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION);
if (_runAs == null || _injections == null || _callbacks == null)
throw new IllegalStateException("RunAs, Injections or LifeCycleCallbacks is null");
}
public void process ()
throws Exception
{
processServlets();
processFilters();
processListeners();
processRunAsAnnotations();
processLifeCycleCallbackAnnotations();
processResourcesAnnotations();
processResourceAnnotations();
}
public void processServlets ()
throws Exception
{
}
public void processFilters ()
throws Exception
{
}
public void processListeners ()
throws Exception
{
}
public List getServlets ()
{
return _servlets;
}
public List getServletMappings ()
{
return _servletMappings;
}
public List getFilters ()
{
return _filters;
}
public List getFilterMappings ()
{
return _filterMappings;
}
public List getListeners()
{
return _listeners;
}
public void processRunAsAnnotations ()
throws Exception
{
for (Class clazz:_finder.getClassesForAnnotation(RunAs.class))
{
if (!javax.servlet.Servlet.class.isAssignableFrom(clazz))
{
Log.debug("Ignoring runAs notation on on-servlet class "+clazz.getName());
continue;
}
RunAs runAs = (RunAs)clazz.getAnnotation(RunAs.class);
if (runAs != null)
{
String role = runAs.value();
if (role != null)
{
org.eclipse.jetty.plus.annotation.RunAs ra = new org.eclipse.jetty.plus.annotation.RunAs();
ra.setTargetClass(clazz);
ra.setRoleName(role);
_runAs.add(ra);
}
}
}
}
public void processLifeCycleCallbackAnnotations()
throws Exception
{
processPostConstructAnnotations();
processPreDestroyAnnotations();
}
private void processPostConstructAnnotations ()
throws Exception
{
// TODO: check that the same class does not have more than one
for (Method m:_finder.getMethodsForAnnotation(PostConstruct.class))
{
if (!isServletType(m.getDeclaringClass()))
{
Log.debug("Ignoring "+m.getName()+" as non-servlet type");
continue;
}
if (m.getParameterTypes().length != 0)
throw new IllegalStateException(m+" has parameters");
if (m.getReturnType() != Void.TYPE)
throw new IllegalStateException(m+" is not void");
if (m.getExceptionTypes().length != 0)
throw new IllegalStateException(m+" throws checked exceptions");
if (Modifier.isStatic(m.getModifiers()))
throw new IllegalStateException(m+" is static");
PostConstructCallback callback = new PostConstructCallback();
callback.setTargetClass(m.getDeclaringClass());
callback.setTarget(m);
_callbacks.add(callback);
}
}
public void processPreDestroyAnnotations ()
throws Exception
{
//TODO: check that the same class does not have more than one
for (Method m: _finder.getMethodsForAnnotation(PreDestroy.class))
{
if (!isServletType(m.getDeclaringClass()))
{
Log.debug("Ignoring "+m.getName()+" as non-servlet type");
continue;
}
if (m.getParameterTypes().length != 0)
throw new IllegalStateException(m+" has parameters");
if (m.getReturnType() != Void.TYPE)
throw new IllegalStateException(m+" is not void");
if (m.getExceptionTypes().length != 0)
throw new IllegalStateException(m+" throws checked exceptions");
if (Modifier.isStatic(m.getModifiers()))
throw new IllegalStateException(m+" is static");
PreDestroyCallback callback = new PreDestroyCallback();
callback.setTargetClass(m.getDeclaringClass());
callback.setTarget(m);
_callbacks.add(callback);
}
}
/**
* Process @Resources annotation on classes
*/
public void processResourcesAnnotations ()
throws Exception
{
List<Class<?>> classes = _finder.getClassesForAnnotation(Resources.class);
for (Class<?> clazz:classes)
{
if (!isServletType(clazz))
{
Log.debug("Ignoring @Resources annotation on on-servlet type class "+clazz.getName());
continue;
}
//Handle Resources annotation - add namespace entries
Resources resources = (Resources)clazz.getAnnotation(Resources.class);
if (resources == null)
continue;
Resource[] resArray = resources.value();
if (resArray==null||resArray.length==0)
continue;
for (int j=0;j<resArray.length;j++)
{
String name = resArray[j].name();
String mappedName = resArray[j].mappedName();
Resource.AuthenticationType auth = resArray[j].authenticationType();
Class type = resArray[j].type();
boolean shareable = resArray[j].shareable();
if (name==null || name.trim().equals(""))
throw new IllegalStateException ("Class level Resource annotations must contain a name (Common Annotations Spec Section 2.3)");
try
{
//TODO don't ignore the shareable, auth etc etc
if (!org.eclipse.jetty.plus.jndi.NamingEntryUtil.bindToENC(_webApp, name, mappedName))
if (!org.eclipse.jetty.plus.jndi.NamingEntryUtil.bindToENC(_webApp.getServer(), name, mappedName))
throw new IllegalStateException("No resource bound at "+(mappedName==null?name:mappedName));
}
catch (NamingException e)
{
throw new IllegalStateException(e);
}
}
}
}
public void processResourceAnnotations ()
throws Exception
{
processClassResourceAnnotations();
processMethodResourceAnnotations();
processFieldResourceAnnotations();
}
/**
* Class level Resource annotations declare a name in the
* environment that will be looked up at runtime. They do
* not specify an injection.
*/
public void processClassResourceAnnotations ()
throws Exception
{
List<Class<?>> classes = _finder.getClassesForAnnotation(Resource.class);
for (Class<?> clazz:classes)
{
if (!isServletType(clazz))
{
Log.debug("Ignoring @Resource annotation on on-servlet type class "+clazz.getName());
continue;
}
//Handle Resource annotation - add namespace entries
Resource resource = (Resource)clazz.getAnnotation(Resource.class);
if (resource != null)
{
String name = resource.name();
String mappedName = resource.mappedName();
Resource.AuthenticationType auth = resource.authenticationType();
Class type = resource.type();
boolean shareable = resource.shareable();
if (name==null || name.trim().equals(""))
throw new IllegalStateException ("Class level Resource annotations must contain a name (Common Annotations Spec Section 2.3)");
try
{
//TODO don't ignore the shareable, auth etc etc
if (!org.eclipse.jetty.plus.jndi.NamingEntryUtil.bindToENC(_webApp, name,mappedName))
if (!org.eclipse.jetty.plus.jndi.NamingEntryUtil.bindToENC(_webApp.getServer(), name,mappedName))
throw new IllegalStateException("No resource at "+(mappedName==null?name:mappedName));
}
catch (NamingException e)
{
throw new IllegalStateException(e);
}
}
}
}
/**
* Process a Resource annotation on the Methods.
*
* This will generate a JNDI entry, and an Injection to be
* processed when an instance of the class is created.
* @param injections
*/
public void processMethodResourceAnnotations ()
throws Exception
{
//Get all methods that have a Resource annotation
List<Method> methods = _finder.getMethodsForAnnotation(javax.annotation.Resource.class);
for (Method m: methods)
{
if (!isServletType(m.getDeclaringClass()))
{
Log.debug("Ignoring @Resource annotation on on-servlet type method "+m.getName());
continue;
}
/*
* Commons Annotations Spec 2.3
* " The Resource annotation is used to declare a reference to a resource.
* It can be specified on a class, methods or on fields. When the
* annotation is applied on a field or method, the container will
* inject an instance of the requested resource into the application
* when the application is initialized... Even though this annotation
* is not marked Inherited, if used all superclasses MUST be examined
* to discover all uses of this annotation. All such annotation instances
* specify resources that are needed by the application. Note that this
* annotation may appear on private fields and methods of the superclasses.
* Injection of the declared resources needs to happen in these cases as
* well, even if a method with such an annotation is overridden by a subclass."
*
* Which IMHO, put more succinctly means "If you find a @Resource on any method
* or field, inject it!".
*/
Resource resource = (Resource)m.getAnnotation(Resource.class);
if (resource == null)
continue;
//JavaEE Spec 5.2.3: Method cannot be static
if (Modifier.isStatic(m.getModifiers()))
throw new IllegalStateException(m+" cannot be static");
// Check it is a valid javabean
if (!IntrospectionUtil.isJavaBeanCompliantSetter(m))
throw new IllegalStateException(m+" is not a java bean compliant setter method");
//default name is the javabean property name
String name = m.getName().substring(3);
name = name.substring(0,1).toLowerCase()+name.substring(1);
name = m.getDeclaringClass().getCanonicalName()+"/"+name;
//allow default name to be overridden
name = (resource.name()!=null && !resource.name().trim().equals("")? resource.name(): name);
//get the mappedName if there is one
String mappedName = (resource.mappedName()!=null && !resource.mappedName().trim().equals("")?resource.mappedName():null);
Class type = m.getParameterTypes()[0];
//get other parts that can be specified in @Resource
Resource.AuthenticationType auth = resource.authenticationType();
boolean shareable = resource.shareable();
//if @Resource specifies a type, check it is compatible with setter param
if ((resource.type() != null)
&&
!resource.type().equals(Object.class)
&&
(!IntrospectionUtil.isTypeCompatible(type, resource.type(), false)))
throw new IllegalStateException("@Resource incompatible type="+resource.type()+ " with method param="+type+ " for "+m);
//check if an injection has already been setup for this target by web.xml
Injection webXmlInjection = _injections.getInjection(m.getDeclaringClass(), m);
if (webXmlInjection == null)
{
try
{
//try binding name to environment
//try the webapp's environment first
boolean bound = org.eclipse.jetty.plus.jndi.NamingEntryUtil.bindToENC(_webApp, name, mappedName);
//try the server's environment
if (!bound)
bound = org.eclipse.jetty.plus.jndi.NamingEntryUtil.bindToENC(_webApp.getServer(), name, mappedName);
//try the jvm's environment
if (!bound)
bound = org.eclipse.jetty.plus.jndi.NamingEntryUtil.bindToENC(null, name, mappedName);
//TODO if it is an env-entry from web.xml it can be injected, in which case there will be no
//NamingEntry, just a value bound in java:comp/env
if (!bound)
{
try
{
InitialContext ic = new InitialContext();
String nameInEnvironment = (mappedName!=null?mappedName:name);
ic.lookup("java:comp/env/"+nameInEnvironment);
bound = true;
}
catch (NameNotFoundException e)
{
bound = false;
}
}
if (bound)
{
Log.debug("Bound "+(mappedName==null?name:mappedName) + " as "+ name);
// Make the Injection for it
Injection injection = new Injection();
injection.setTargetClass(m.getDeclaringClass());
injection.setJndiName(name);
injection.setMappingName(mappedName);
injection.setTarget(m);
_injections.add(injection);
}
else if (!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
throw new IllegalStateException("No resource at "+(mappedName==null?name:mappedName));
}
}
catch (NamingException e)
{
//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 (!isEnvEntryType(type))
throw new IllegalStateException(e);
}
}
else
{
//if an injection is already set up for this name, then the types must be compatible
//JavaEE spec sec 5.2.4
Object value = webXmlInjection.lookupInjectedValue();
if (!IntrospectionUtil.isTypeCompatible(type, value.getClass(), false))
throw new IllegalStateException("Type of field="+type+" is not compatible with Resource type="+value.getClass());
}
}
}
/**
* Process @Resource annotation for a Field. These will both set up a
* JNDI entry and generate an Injection. Or they can be the equivalent
* of env-entries with default values
*
* @param injections
*/
public void processFieldResourceAnnotations ()
throws Exception
{
//Get all fields that have a Resource annotation
List<Field> fields = _finder.getFieldsForAnnotation(Resource.class);
for (Field f: fields)
{
if (!isServletType(f.getDeclaringClass()))
{
Log.debug("Ignoring @Resource annotation on on-servlet type field "+f.getName());
continue;
}
Resource resource = (Resource)f.getAnnotation(Resource.class);
if (resource == null)
continue;
//JavaEE Spec 5.2.3: Field cannot be static
if (Modifier.isStatic(f.getModifiers()))
throw new IllegalStateException(f+" cannot be static");
//JavaEE Spec 5.2.3: Field cannot be final
if (Modifier.isFinal(f.getModifiers()))
throw new IllegalStateException(f+" cannot be final");
//work out default name
String name = f.getDeclaringClass().getCanonicalName()+"/"+f.getName();
//allow @Resource name= to override the field name
name = (resource.name()!=null && !resource.name().trim().equals("")? resource.name(): name);
//get the type of the Field
Class type = f.getType();
//if @Resource specifies a type, check it is compatible with field type
if ((resource.type() != null)
&&
!resource.type().equals(Object.class)
&&
(!IntrospectionUtil.isTypeCompatible(type, resource.type(), false)))
throw new IllegalStateException("@Resource incompatible type="+resource.type()+ " with field type ="+f.getType());
//get the mappedName if there is one
String mappedName = (resource.mappedName()!=null && !resource.mappedName().trim().equals("")?resource.mappedName():null);
//get other parts that can be specified in @Resource
Resource.AuthenticationType auth = resource.authenticationType();
boolean shareable = resource.shareable();
//check if an injection has already been setup for this target by web.xml
Injection webXmlInjection = _injections.getInjection(f.getDeclaringClass(), f);
if (webXmlInjection == null)
{
try
{
boolean bound = org.eclipse.jetty.plus.jndi.NamingEntryUtil.bindToENC(_webApp, name, mappedName);
if (!bound)
bound = org.eclipse.jetty.plus.jndi.NamingEntryUtil.bindToENC(_webApp.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
try
{
InitialContext ic = new InitialContext();
String nameInEnvironment = (mappedName!=null?mappedName:name);
ic.lookup("java:comp/env/"+nameInEnvironment);
bound = true;
}
catch (NameNotFoundException e)
{
bound = false;
}
}
//Check there is a JNDI entry for this annotation
if (bound)
{
Log.debug("Bound "+(mappedName==null?name:mappedName) + " as "+ name);
// Make the Injection for it if the binding succeeded
Injection injection = new Injection();
injection.setTargetClass(f.getDeclaringClass());
injection.setJndiName(name);
injection.setMappingName(mappedName);
injection.setTarget(f);
_injections.add(injection);
}
else if (!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
throw new IllegalStateException("No resource at "+(mappedName==null?name:mappedName));
}
}
catch (NamingException e)
{
//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 (!isEnvEntryType(type))
throw new IllegalStateException(e);
}
}
else
{
//if an injection is already set up for this name, then the types must be compatible
//JavaEE spec sec 5.2.4
Object value = webXmlInjection.lookupInjectedValue();
if (!IntrospectionUtil.isTypeCompatible(type, value.getClass(), false))
throw new IllegalStateException("Type of field="+type+" is not compatible with Resource type="+value.getClass());
}
}
}
/**
* Check if the presented method belongs to a class that is one
* of the classes with which a servlet container should be concerned.
* @param m
* @return
*/
private boolean isServletType (Class c)
{
boolean isServlet = false;
if (javax.servlet.Servlet.class.isAssignableFrom(c) ||
javax.servlet.Filter.class.isAssignableFrom(c) ||
javax.servlet.ServletContextListener.class.isAssignableFrom(c) ||
javax.servlet.ServletContextAttributeListener.class.isAssignableFrom(c) ||
javax.servlet.ServletRequestListener.class.isAssignableFrom(c) ||
javax.servlet.ServletRequestAttributeListener.class.isAssignableFrom(c) ||
javax.servlet.http.HttpSessionListener.class.isAssignableFrom(c) ||
javax.servlet.http.HttpSessionAttributeListener.class.isAssignableFrom(c))
isServlet=true;
return isServlet;
}
private static boolean isEnvEntryType (Class type)
{
boolean result = false;
for (int i=0;i<__envEntryTypes.length && !result;i++)
{
result = (type.equals(__envEntryTypes[i]));
}
return result;
}
protected static String normalizePattern(String p)
{
if (p!=null && p.length()>0 && !p.startsWith("/") && !p.startsWith("*"))
return "/"+p;
return p;
}
}

View File

@ -0,0 +1,63 @@
// ========================================================================
// 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.List;
import org.eclipse.jetty.annotations.AnnotationParser.ClassHandler;
import org.eclipse.jetty.util.MultiMap;
import org.eclipse.jetty.util.log.Log;
/**
* ClassInheritanceHandler
*
* As asm scans for classes, remember the type hierarchy.
*/
public class ClassInheritanceHandler implements ClassHandler
{
MultiMap _inheritanceMap = new MultiMap();
public ClassInheritanceHandler()
{
}
public void handle(String className, int version, int access, String signature, String superName, String[] interfaces)
{
try
{
for (int i=0; interfaces != null && i<interfaces.length;i++)
{
_inheritanceMap.add (interfaces[i], className);
}
//To save memory, we don't record classes that only extend Object, as that can be assumed
if (!"java.lang.Object".equals(superName))
_inheritanceMap.add(superName, className);
}
catch (Exception e)
{
Log.warn(e);
}
}
public List getClassNamesExtendingOrImplementing (String className)
{
return _inheritanceMap.getValues(className);
}
public MultiMap getMap ()
{
return _inheritanceMap;
}
}

View File

@ -0,0 +1,86 @@
// ========================================================================
// 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.annotations;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.List;
import org.eclipse.jetty.annotations.AnnotationParser.AnnotationHandler;
import org.eclipse.jetty.annotations.AnnotationParser.Value;
import org.eclipse.jetty.plus.annotation.LifeCycleCallbackCollection;
import org.eclipse.jetty.plus.annotation.PostConstructCallback;
import org.eclipse.jetty.util.Loader;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.webapp.WebAppContext;
public class PostConstructAnnotationHandler implements AnnotationHandler
{
protected WebAppContext _wac;
public PostConstructAnnotationHandler (WebAppContext wac)
{
_wac = wac;
}
public void handleClass(String className, int version, int access, String signature, String superName, String[] interfaces, String annotation,
List<Value> 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<Value> values)
{
Log.warn("@PostConstruct annotation not applicable to fields: "+className+"."+fieldName);
}
public void handleMethod(String className, String methodName, int access, String params, String signature, String[] exceptions, String annotation,
List<Value> values)
{
LifeCycleCallbackCollection callbacks = (LifeCycleCallbackCollection)_wac.getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION);
Class clazz = null;
try
{
clazz = Loader.loadClass(null, className);
Method m = clazz.getDeclaredMethod(methodName, Util.convertTypes(params));
if (!Util.isServletType(m.getDeclaringClass()))
{
Log.debug("Ignoring "+m.getName()+" as non-servlet type");
return;
}
if (m.getParameterTypes().length != 0)
throw new IllegalStateException(m+" has parameters");
if (m.getReturnType() != Void.TYPE)
throw new IllegalStateException(m+" is not void");
if (m.getExceptionTypes().length != 0)
throw new IllegalStateException(m+" throws checked exceptions");
if (Modifier.isStatic(m.getModifiers()))
throw new IllegalStateException(m+" is static");
PostConstructCallback callback = new PostConstructCallback();
callback.setTargetClass(m.getDeclaringClass());
callback.setTarget(m);
callbacks.add(callback);
}
catch (Exception e)
{
Log.warn(e);
}
}
}

View File

@ -0,0 +1,84 @@
// ========================================================================
// 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.annotations;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.List;
import org.eclipse.jetty.annotations.AnnotationParser.AnnotationHandler;
import org.eclipse.jetty.annotations.AnnotationParser.Value;
import org.eclipse.jetty.plus.annotation.LifeCycleCallbackCollection;
import org.eclipse.jetty.plus.annotation.PreDestroyCallback;
import org.eclipse.jetty.util.Loader;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.webapp.WebAppContext;
public class PreDestroyAnnotationHandler implements AnnotationHandler
{
WebAppContext _wac;
public PreDestroyAnnotationHandler (WebAppContext wac)
{
_wac = wac;
}
public void handleClass(String className, int version, int access, String signature, String superName, String[] interfaces, String annotation,
List<Value> 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<Value> values)
{
Log.warn("@PreDestroy annotation not applicable for fields: "+className+"."+fieldName);
}
public void handleMethod(String className, String methodName, int access, String params, String signature, String[] exceptions, String annotation,
List<Value> values)
{
LifeCycleCallbackCollection callbacks = (LifeCycleCallbackCollection)_wac.getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION);
Class clazz = null;
try
{
clazz = Loader.loadClass(null, className);
Method m = clazz.getDeclaredMethod(methodName, Util.convertTypes(params));
if (!Util.isServletType(m.getDeclaringClass()))
{
Log.debug("Ignored "+m.getName()+" as non-servlet type");
return;
}
if (m.getParameterTypes().length != 0)
throw new IllegalStateException(m+" has parameters");
if (m.getReturnType() != Void.TYPE)
throw new IllegalStateException(m+" is not void");
if (m.getExceptionTypes().length != 0)
throw new IllegalStateException(m+" throws checked exceptions");
if (Modifier.isStatic(m.getModifiers()))
throw new IllegalStateException(m+" is static");
PreDestroyCallback callback = new PreDestroyCallback();
callback.setTargetClass(m.getDeclaringClass());
callback.setTarget(m);
callbacks.add(callback);
}
catch (Exception e)
{
Log.warn(e);
}
}
}

View File

@ -0,0 +1,369 @@
// ========================================================================
// 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.annotations;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.List;
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.plus.annotation.Injection;
import org.eclipse.jetty.plus.annotation.InjectionCollection;
import org.eclipse.jetty.util.IntrospectionUtil;
import org.eclipse.jetty.util.Loader;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.webapp.WebAppContext;
public class ResourceAnnotationHandler implements AnnotationHandler
{
protected WebAppContext _wac;
public ResourceAnnotationHandler (WebAppContext wac)
{
_wac = wac;
}
/**
* Class level Resource annotations declare a name in the
* 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<Value> values)
{
Class clazz = null;
try
{
clazz = Loader.loadClass(null, className);
}
catch (Exception e)
{
Log.warn(e);
}
if (!Util.isServletType(clazz))
{
Log.debug("Ignoring @Resource annotation on on-servlet type class "+clazz.getName());
return;
}
//Handle Resource annotation - add namespace entries
Resource resource = (Resource)clazz.getAnnotation(Resource.class);
if (resource != null)
{
String name = resource.name();
String mappedName = resource.mappedName();
Resource.AuthenticationType auth = resource.authenticationType();
Class type = resource.type();
boolean shareable = resource.shareable();
if (name==null || name.trim().equals(""))
throw new IllegalStateException ("Class level Resource annotations must contain a name (Common Annotations Spec Section 2.3)");
try
{
//TODO don't ignore the shareable, auth etc etc
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);
}
}
}
public void handleField(String className, String fieldName, int access, String fieldType, String signature, Object value, String annotation,
List<Value> values)
{
InjectionCollection injections = (InjectionCollection)_wac.getAttribute(InjectionCollection.INJECTION_COLLECTION);
Class clazz = null;
try
{
clazz = Loader.loadClass(null, className);
Field f = clazz.getDeclaredField(fieldName);
if (!Util.isServletType(clazz))
{
Log.debug("Ignoring @Resource annotation on on-servlet type field "+fieldName);
return;
}
Resource resource = (Resource)f.getAnnotation(Resource.class);
if (resource == null)
return;
//JavaEE Spec 5.2.3: Field cannot be static
if (Modifier.isStatic(f.getModifiers()))
throw new IllegalStateException(f+" cannot be static");
//JavaEE Spec 5.2.3: Field cannot be final
if (Modifier.isFinal(f.getModifiers()))
throw new IllegalStateException(f+" cannot be final");
//work out default name
String name = f.getDeclaringClass().getCanonicalName()+"/"+f.getName();
//allow @Resource name= to override the field name
name = (resource.name()!=null && !resource.name().trim().equals("")? resource.name(): name);
//get the type of the Field
Class type = f.getType();
//if @Resource specifies a type, check it is compatible with field type
if ((resource.type() != null)
&&
!resource.type().equals(Object.class)
&&
(!IntrospectionUtil.isTypeCompatible(type, resource.type(), false)))
throw new IllegalStateException("@Resource incompatible type="+resource.type()+ " with field type ="+f.getType());
//get the mappedName if there is one
String mappedName = (resource.mappedName()!=null && !resource.mappedName().trim().equals("")?resource.mappedName():null);
//get other parts that can be specified in @Resource
Resource.AuthenticationType auth = resource.authenticationType();
boolean shareable = resource.shareable();
//check if an injection has already been setup for this target by web.xml
Injection webXmlInjection = injections.getInjection(f.getDeclaringClass(), f);
if (webXmlInjection == null)
{
try
{
boolean bound = org.eclipse.jetty.plus.jndi.NamingEntryUtil.bindToENC(_wac, name, mappedName);
if (!bound)
bound = org.eclipse.jetty.plus.jndi.NamingEntryUtil.bindToENC(_wac.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
try
{
InitialContext ic = new InitialContext();
String nameInEnvironment = (mappedName!=null?mappedName:name);
ic.lookup("java:comp/env/"+nameInEnvironment);
bound = true;
}
catch (NameNotFoundException e)
{
bound = false;
}
}
//Check there is a JNDI entry for this annotation
if (bound)
{
Log.debug("Bound "+(mappedName==null?name:mappedName) + " as "+ name);
// Make the Injection for it if the binding succeeded
Injection injection = new Injection();
injection.setTargetClass(f.getDeclaringClass());
injection.setJndiName(name);
injection.setMappingName(mappedName);
injection.setTarget(f);
injections.add(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
throw new IllegalStateException("No resource at "+(mappedName==null?name:mappedName));
}
}
catch (NamingException e)
{
//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(type))
throw new IllegalStateException(e);
}
}
else
{
//if an injection is already set up for this name, then the types must be compatible
//JavaEE spec sec 5.2.4
Object val = webXmlInjection.lookupInjectedValue();
if (!IntrospectionUtil.isTypeCompatible(type, value.getClass(), false))
throw new IllegalStateException("Type of field="+type+" is not compatible with Resource type="+val.getClass());
}
}
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.
* @param injections
*/
public void handleMethod(String className, String methodName, int access, String params, String signature, String[] exceptions, String annotation,
List<Value> values)
{
InjectionCollection injections = (InjectionCollection)_wac.getAttribute(InjectionCollection.INJECTION_COLLECTION);
Class clazz = null;
try
{
clazz = Loader.loadClass(null, className);
Class[] args = Util.convertTypes(params);
Method m = clazz.getDeclaredMethod(methodName, args);
if (!Util.isServletType(m.getDeclaringClass()))
{
Log.debug("Ignoring @Resource annotation on on-servlet type method "+m.getName());
return;
}
/*
* Commons Annotations Spec 2.3
* " The Resource annotation is used to declare a reference to a resource.
* It can be specified on a class, methods or on fields. When the
* annotation is applied on a field or method, the container will
* inject an instance of the requested resource into the application
* when the application is initialized... Even though this annotation
* is not marked Inherited, if used all superclasses MUST be examined
* to discover all uses of this annotation. All such annotation instances
* specify resources that are needed by the application. Note that this
* annotation may appear on private fields and methods of the superclasses.
* Injection of the declared resources needs to happen in these cases as
* well, even if a method with such an annotation is overridden by a subclass."
*
* Which IMHO, put more succinctly means "If you find a @Resource on any method
* or field, inject it!".
*/
Resource resource = (Resource)m.getAnnotation(Resource.class);
if (resource == null)
return;
//JavaEE Spec 5.2.3: Method cannot be static
if (Modifier.isStatic(m.getModifiers()))
throw new IllegalStateException(m+" cannot be static");
// Check it is a valid javabean
if (!IntrospectionUtil.isJavaBeanCompliantSetter(m))
throw new IllegalStateException(m+" is not a java bean compliant setter method");
//default name is the javabean property name
String name = m.getName().substring(3);
name = name.substring(0,1).toLowerCase()+name.substring(1);
name = m.getDeclaringClass().getCanonicalName()+"/"+name;
//allow default name to be overridden
name = (resource.name()!=null && !resource.name().trim().equals("")? resource.name(): name);
//get the mappedName if there is one
String mappedName = (resource.mappedName()!=null && !resource.mappedName().trim().equals("")?resource.mappedName():null);
Class type = m.getParameterTypes()[0];
//get other parts that can be specified in @Resource
Resource.AuthenticationType auth = resource.authenticationType();
boolean shareable = resource.shareable();
//if @Resource specifies a type, check it is compatible with setter param
if ((resource.type() != null)
&&
!resource.type().equals(Object.class)
&&
(!IntrospectionUtil.isTypeCompatible(type, resource.type(), false)))
throw new IllegalStateException("@Resource incompatible type="+resource.type()+ " with method param="+type+ " for "+m);
//check if an injection has already been setup for this target by web.xml
Injection webXmlInjection = injections.getInjection(m.getDeclaringClass(), m);
if (webXmlInjection == 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);
//try the server's environment
if (!bound)
bound = org.eclipse.jetty.plus.jndi.NamingEntryUtil.bindToENC(_wac.getServer(), name, mappedName);
//try the jvm's environment
if (!bound)
bound = org.eclipse.jetty.plus.jndi.NamingEntryUtil.bindToENC(null, name, mappedName);
//TODO if it is an env-entry from web.xml it can be injected, in which case there will be no
//NamingEntry, just a value bound in java:comp/env
if (!bound)
{
try
{
InitialContext ic = new InitialContext();
String nameInEnvironment = (mappedName!=null?mappedName:name);
ic.lookup("java:comp/env/"+nameInEnvironment);
bound = true;
}
catch (NameNotFoundException e)
{
bound = false;
}
}
if (bound)
{
Log.debug("Bound "+(mappedName==null?name:mappedName) + " as "+ name);
// Make the Injection for it
Injection injection = new Injection();
injection.setTargetClass(m.getDeclaringClass());
injection.setJndiName(name);
injection.setMappingName(mappedName);
injection.setTarget(m);
injections.add(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
throw new IllegalStateException("No resource at "+(mappedName==null?name:mappedName));
}
}
catch (NamingException e)
{
//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(type))
throw new IllegalStateException(e);
}
}
else
{
//if an injection is already set up for this name, then the types must be compatible
//JavaEE spec sec 5.2.4
Object value = webXmlInjection.lookupInjectedValue();
if (!IntrospectionUtil.isTypeCompatible(type, value.getClass(), false))
throw new IllegalStateException("Type of field="+type+" is not compatible with Resource type="+value.getClass());
}
}
catch (Exception e)
{
Log.warn(e);
}
}
}

View File

@ -0,0 +1,104 @@
// ========================================================================
// 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.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.Value;
import org.eclipse.jetty.util.Loader;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.webapp.WebAppContext;
public class ResourcesAnnotationHandler implements AnnotationHandler
{
protected WebAppContext _wac;
public ResourcesAnnotationHandler (WebAppContext wac)
{
_wac = wac;
}
public void handleClass(String className, int version, int access, String signature, String superName, String[] interfaces, String annotation,
List<Value> values)
{
Class clazz = null;
try
{
clazz = Loader.loadClass(null,className);
}
catch (Exception e)
{
Log.warn(e);
return;
}
if (!Util.isServletType(clazz))
{
Log.debug("@Resources annotation ignored on on-servlet type class "+clazz.getName());
return;
}
//Handle Resources annotation - add namespace entries
Resources resources = (Resources)clazz.getAnnotation(Resources.class);
if (resources == null)
return;
Resource[] resArray = resources.value();
if (resArray==null||resArray.length==0)
return;
for (int j=0;j<resArray.length;j++)
{
String name = resArray[j].name();
String mappedName = resArray[j].mappedName();
Resource.AuthenticationType auth = resArray[j].authenticationType();
Class type = resArray[j].type();
boolean shareable = resArray[j].shareable();
if (name==null || name.trim().equals(""))
{
Log.warn ("@Resource annotations on classes must contain a name (Common Annotations Spec Section 2.3)");
break;
}
try
{
//TODO don't ignore the shareable, auth etc etc
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 bound at "+(mappedName==null?name:mappedName));
}
catch (NamingException e)
{
Log.warn(e);
}
}
}
public void handleField(String className, String fieldName, int access, String fieldType, String signature, Object value, String annotation,
List<Value> 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<Value> values)
{
Log.warn ("@Resources not applicable for methods: "+className+"."+methodName);
}
}

View File

@ -0,0 +1,81 @@
// ========================================================================
// 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.annotations;
import java.util.List;
import javax.annotation.security.RunAs;
import org.eclipse.jetty.annotations.AnnotationParser.AnnotationHandler;
import org.eclipse.jetty.annotations.AnnotationParser.Value;
import org.eclipse.jetty.plus.annotation.RunAsCollection;
import org.eclipse.jetty.util.Loader;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.webapp.WebAppContext;
public class RunAsAnnotationHandler implements AnnotationHandler
{
protected WebAppContext _wac;
public RunAsAnnotationHandler (WebAppContext wac)
{
_wac = wac;
}
public void handleClass(String className, int version, int access, String signature, String superName, String[] interfaces, String annotation,
List<Value> values)
{
RunAsCollection runAsCollection = (RunAsCollection)_wac.getAttribute(RunAsCollection.RUNAS_COLLECTION);
Class clazz = null;
try
{
clazz = Loader.loadClass(null, className);
if (!javax.servlet.Servlet.class.isAssignableFrom(clazz))
{
Log.debug("@RunAs annotation ignored on on-servlet class "+clazz.getName());
return;
}
RunAs runAs = (RunAs)clazz.getAnnotation(RunAs.class);
if (runAs != null)
{
String role = runAs.value();
if (role != null)
{
org.eclipse.jetty.plus.annotation.RunAs ra = new org.eclipse.jetty.plus.annotation.RunAs();
ra.setTargetClass(clazz);
ra.setRoleName(role);
runAsCollection.add(ra);
}
}
}
catch (Exception e)
{
Log.warn(e);
}
}
public void handleField(String className, String fieldName, int access, String fieldType, String signature, Object value, String annotation,
List<Value> values)
{
Log.warn ("@RunAs annotation not applicable for fields: "+className+"."+fieldName);
}
public void handleMethod(String className, String methodName, int access, String params, String signature, String[] exceptions, String annotation,
List<Value> values)
{
Log.warn("@RunAs annotation ignored on method: "+className+"."+methodName+" "+signature);
}
}

View File

@ -13,37 +13,143 @@
package org.eclipse.jetty.annotations;
import java.lang.reflect.Array;
import java.util.List;
import org.eclipse.jetty.util.Loader;
import org.objectweb.asm.Type;
/**
* Util
*
*
*/
public class Util
{
{
private static Class[] __envEntryTypes =
new Class[] {String.class, Character.class, Integer.class, Boolean.class, Double.class, Byte.class, Short.class, Long.class, Float.class};
/**
* Check if the presented method belongs to a class that is one
* of the classes with which a servlet container should be concerned.
* @param m
* @return
*/
public static boolean isServletType (Class c)
{
boolean isServlet = false;
if (javax.servlet.Servlet.class.isAssignableFrom(c) ||
javax.servlet.Filter.class.isAssignableFrom(c) ||
javax.servlet.ServletContextListener.class.isAssignableFrom(c) ||
javax.servlet.ServletContextAttributeListener.class.isAssignableFrom(c) ||
javax.servlet.ServletRequestListener.class.isAssignableFrom(c) ||
javax.servlet.ServletRequestAttributeListener.class.isAssignableFrom(c) ||
javax.servlet.http.HttpSessionListener.class.isAssignableFrom(c) ||
javax.servlet.http.HttpSessionAttributeListener.class.isAssignableFrom(c))
isServlet=true;
return isServlet;
}
public static boolean isEnvEntryType (Class type)
{
boolean result = false;
for (int i=0;i<__envEntryTypes.length && !result;i++)
{
result = (type.equals(__envEntryTypes[i]));
}
return result;
}
public static String normalizePattern(String p)
{
if (p!=null && p.length()>0 && !p.startsWith("/") && !p.startsWith("*"))
return "/"+p;
return p;
}
/**
* Find all annotations directly and inherited on a class.
*
* @param clazz
* @return
* @throws Exception
*/
public List findMethodAnnotations (Class clazz)
public static Class[] convertTypes (String params)
throws Exception
{
//TODO
return null;
return convertTypes(Type.getArgumentTypes(params));
}
public List findFieldAnnotations (Class clazz)
public static Class[] convertTypes (Type[] types)
throws Exception
{
//TODO
if (types==null)
return new Class[0];
return null;
Class[] classArray = new Class[types.length];
for (int i=0; i<types.length; i++)
{
classArray[i] = convertType(types[i]);
}
return classArray;
}
public static Class convertType (Type t)
throws Exception
{
if (t == null)
return (Class)null;
switch (t.getSort())
{
case Type.BOOLEAN:
{
return Boolean.TYPE;
}
case Type.ARRAY:
{
Class clazz = convertType(t.getElementType());
return Array.newInstance(clazz, 0).getClass();
}
case Type.BYTE:
{
return Byte.TYPE;
}
case Type.CHAR:
{
return Character.TYPE;
}
case Type.DOUBLE:
{
return Double.TYPE;
}
case Type.FLOAT:
{
return Float.TYPE;
}
case Type.INT:
{
return Integer.TYPE;
}
case Type.LONG:
{
return Long.TYPE;
}
case Type.OBJECT:
{
return (Loader.loadClass(null, t.getClassName()));
}
case Type.SHORT:
{
return Short.TYPE;
}
case Type.VOID:
{
return null;
}
default:
return null;
}
}
}

View File

@ -21,11 +21,13 @@ package org.eclipse.jetty.annotations;
*
*/
@Sample(value=50)
public class ClassB extends ClassA
@Multi({"do", "re", "mi"})
public class ClassB extends ClassA implements InterfaceD
{
//test override of public scope method
@Sample(value=51)
@Multi({"fa", "so", "la"})
public void a()
{
System.err.println("ClassB.public");

View File

@ -18,15 +18,20 @@ import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import javax.annotation.security.RunAs;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
@RunAs("admin")
public class ClassC
public class FilterC implements Filter
{
@Resource (mappedName="foo")
private Double foo;
@ -42,23 +47,25 @@ public class ClassC
{
}
public void anything (HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
response.setContentType("text/html");
response.getWriter().println("<h1>Pojo Servlet</h1>");
response.getWriter().println("Acting like a Servlet.");
}
public void doFilter (HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws java.io.IOException, javax.servlet.ServletException
public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2)
throws IOException, ServletException
{
HttpServletRequest request = (HttpServletRequest)arg0;
HttpServletResponse response = (HttpServletResponse)arg1;
HttpSession session = request.getSession(true);
String val = request.getParameter("action");
if (val!=null)
session.setAttribute("action", val);
chain.doFilter(request, response);
arg2.doFilter(request, response);
}
public void destroy()
{
}
public void init(FilterConfig arg0) throws ServletException
{
}
}

View File

@ -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.annotations;
/**
* InterfaceD
*
*
*/
public interface InterfaceD
{
}

View File

@ -0,0 +1,33 @@
// ========================================================================
// 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.annotations;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class ListenerC implements ServletContextListener
{
public void contextDestroyed(ServletContextEvent arg0)
{
}
public void contextInitialized(ServletContextEvent arg0)
{
}
}

View File

@ -0,0 +1,15 @@
package org.eclipse.jetty.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
public @interface Multi
{
String[] value();
}

View File

@ -0,0 +1,54 @@
// ========================================================================
// Copyright (c) 2008-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.IOException;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import javax.annotation.security.RunAs;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@RunAs("admin")
public class ServletC extends HttpServlet
{
@Resource (mappedName="foo")
private Double foo;
@PreDestroy
public void pre ()
{
}
@PostConstruct
public void post()
{
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
response.setContentType("text/html");
response.getWriter().println("<h1>Annotated Servlet</h1>");
response.getWriter().println("An annotated Servlet.");
}
}

View File

@ -1,105 +0,0 @@
// ========================================================================
// Copyright (c) 2008-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.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
import junit.framework.TestCase;
public class TestAnnotationFinder extends TestCase
{
public void testNormalize()
{
assertEquals("org.eclipse.test.Foo", AnnotationFinder.normalize("Lorg/eclipse/test/Foo;"));
assertEquals("org.eclipse.test.Foo.Bar", AnnotationFinder.normalize("org/eclipse/test/Foo$Bar.class"));
}
public void testConvertType ()
throws Exception
{
}
public void testSampleAnnotation ()
throws Exception
{
long start = System.currentTimeMillis();
String[] classNames = new String[]{"org.eclipse.jetty.annotations.ClassA"};
AnnotationFinder finder = new AnnotationFinder();
finder.find(classNames, 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)));
start = System.currentTimeMillis();
List<Class<?>> classes = finder.getClassesForAnnotation(Sample.class);
end = System.currentTimeMillis();
System.err.println("Time to find classes matching annotation: "+((end-start)));
assertNotNull(classes);
assertEquals(1, classes.size());
assertTrue(classes.contains(org.eclipse.jetty.annotations.ClassA.class));
start = System.currentTimeMillis();
List<Method> methods = finder.getMethodsForAnnotation(Sample.class);
end = System.currentTimeMillis();
System.err.println("Time to find methods matching annotation : "+((end-start)));
assertNotNull(methods);
assertEquals (5, methods.size());
Method a = ClassA.class.getDeclaredMethod("a", new Class[]{Array.newInstance(Integer.class, 0).getClass()});
Method b = ClassA.class.getDeclaredMethod("b", new Class[]{Array.newInstance(ClassA.Foo.class, 0).getClass()});
Method c = ClassA.class.getDeclaredMethod("c", new Class[]{Array.newInstance(Integer.TYPE, 0).getClass()});
Method d = ClassA.class.getDeclaredMethod("d", new Class[]{Integer.TYPE, String.class});
Method l = ClassA.class.getDeclaredMethod("l", new Class[]{});
assertTrue(methods.contains(a));
assertTrue(methods.contains(b));
assertTrue(methods.contains(c));
assertTrue(methods.contains(d));
assertTrue(methods.contains(l));
start = System.currentTimeMillis();
List<Field> fields = finder.getFieldsForAnnotation(Sample.class);
end = System.currentTimeMillis();
System.err.println("Time to find fields matching annotation : "+((end-start)));
assertNotNull(fields);
assertEquals(1, fields.size());
Field m = ClassA.class.getDeclaredField("m");
assertTrue(fields.contains(m));
}
}

View File

@ -18,6 +18,7 @@ import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import javax.annotation.Resources;
@ -26,6 +27,8 @@ import javax.naming.InitialContext;
import junit.framework.TestCase;
import org.eclipse.jetty.annotations.AnnotationParser.AnnotationHandler;
import org.eclipse.jetty.annotations.AnnotationParser.Value;
import org.eclipse.jetty.annotations.resources.ResourceA;
import org.eclipse.jetty.annotations.resources.ResourceB;
import org.eclipse.jetty.plus.annotation.Injection;
@ -33,6 +36,8 @@ 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.server.Server;
import org.eclipse.jetty.util.Loader;
import org.eclipse.jetty.util.MultiMap;
import org.eclipse.jetty.webapp.WebAppContext;
/**
@ -43,6 +48,32 @@ import org.eclipse.jetty.webapp.WebAppContext;
public class TestAnnotationInheritance extends TestCase
{
List<String> classNames = new ArrayList<String>();
class SampleHandler implements AnnotationHandler
{
public final List<String> annotatedClassNames = new ArrayList<String>();
public final List<String> annotatedMethods = new ArrayList<String>();
public final List<String> annotatedFields = new ArrayList<String>();
public void handleClass(String className, int version, int access, String signature, String superName, String[] interfaces, String annotation,
List<Value> values)
{
annotatedClassNames.add(className);
}
public void handleField(String className, String fieldName, int access, String fieldType, String signature, Object value, String annotation,
List<Value> values)
{
annotatedFields.add(className+"."+fieldName);
}
public void handleMethod(String className, String methodName, int access, String params, String signature, String[] exceptions, String annotation,
List<Value> values)
{
annotatedMethods.add(className+"."+methodName);
}
}
public void tearDown () throws Exception
@ -53,14 +84,17 @@ public class TestAnnotationInheritance extends TestCase
comp.destroySubcontext("env");
}
public void testInheritance ()
public void testParseClassNames ()
throws Exception
{
classNames.add(ClassA.class.getName());
classNames.add(ClassB.class.getName());
AnnotationFinder finder = new AnnotationFinder();
finder.find(classNames, new ClassNameResolver ()
SampleHandler handler = new SampleHandler();
AnnotationParser parser = new AnnotationParser();
parser.registerAnnotationHandler("org.eclipse.jetty.annotations.Sample", handler);
parser.parse(classNames, new ClassNameResolver ()
{
public boolean isExcluded(String name)
{
@ -72,24 +106,73 @@ public class TestAnnotationInheritance extends TestCase
return false;
}
});
List<Class<?>> classes = finder.getClassesForAnnotation(Sample.class);
assertEquals(2, classes.size());
//check we got 2 class annotations
assertEquals(2, handler.annotatedClassNames.size());
//check methods
//List methods = collection.getMethods();
List<Method> methods = finder.getMethodsForAnnotation(Sample.class);
//check we got all annotated methods on each class
assertEquals (7, handler.annotatedMethods.size());
assertTrue (handler.annotatedMethods.contains("org.eclipse.jetty.annotations.ClassA.a"));
assertTrue (handler.annotatedMethods.contains("org.eclipse.jetty.annotations.ClassA.b"));
assertTrue (handler.annotatedMethods.contains("org.eclipse.jetty.annotations.ClassA.c"));
assertTrue (handler.annotatedMethods.contains("org.eclipse.jetty.annotations.ClassA.d"));
assertTrue (handler.annotatedMethods.contains("org.eclipse.jetty.annotations.ClassA.l"));
assertTrue (handler.annotatedMethods.contains("org.eclipse.jetty.annotations.ClassB.a"));
assertTrue (handler.annotatedMethods.contains("org.eclipse.jetty.annotations.ClassB.c"));
//check we got all annotated fields on each class
assertEquals(1, handler.annotatedFields.size());
assertEquals("org.eclipse.jetty.annotations.ClassA.m", handler.annotatedFields.get(0));
}
public void testParseClass ()
throws Exception
{
SampleHandler handler = new SampleHandler();
AnnotationParser parser = new AnnotationParser();
parser.registerAnnotationHandler("org.eclipse.jetty.annotations.Sample", handler);
parser.parse(ClassB.class, new ClassNameResolver ()
{
public boolean isExcluded(String name)
{
return false;
}
public boolean shouldOverride(String name)
{
return false;
}
}, true);
assertTrue(methods!=null);
assertFalse(methods.isEmpty());
//check we got 2 class annotations
assertEquals(2, handler.annotatedClassNames.size());
//check we got all annotated methods on each class
assertEquals (7, handler.annotatedMethods.size());
assertTrue (handler.annotatedMethods.contains("org.eclipse.jetty.annotations.ClassA.a"));
assertTrue (handler.annotatedMethods.contains("org.eclipse.jetty.annotations.ClassA.b"));
assertTrue (handler.annotatedMethods.contains("org.eclipse.jetty.annotations.ClassA.c"));
assertTrue (handler.annotatedMethods.contains("org.eclipse.jetty.annotations.ClassA.d"));
assertTrue (handler.annotatedMethods.contains("org.eclipse.jetty.annotations.ClassA.l"));
assertTrue (handler.annotatedMethods.contains("org.eclipse.jetty.annotations.ClassB.a"));
assertTrue (handler.annotatedMethods.contains("org.eclipse.jetty.annotations.ClassB.c"));
//check we got all annotated fields on each class
assertEquals(1, handler.annotatedFields.size());
assertEquals("org.eclipse.jetty.annotations.ClassA.m", handler.annotatedFields.get(0));
}
public void testExclusions()
throws Exception
{
AnnotationFinder finder = new AnnotationFinder();
finder.find(ClassA.class.getName(), new ClassNameResolver()
AnnotationParser parser = new AnnotationParser();
SampleHandler handler = new SampleHandler();
parser.registerAnnotationHandler("org.eclipse.jetty.annotations.Sample", handler);
parser.parse(ClassA.class.getName(), new ClassNameResolver()
{
public boolean isExcluded(String name)
{
@ -101,9 +184,15 @@ public class TestAnnotationInheritance extends TestCase
return false;
}
});
assertTrue(finder.getClassesForAnnotation(Sample.class).isEmpty());
finder.find (ClassA.class.getName(), new ClassNameResolver()
assertEquals (0, handler.annotatedClassNames.size());
assertEquals (0, handler.annotatedFields.size());
assertEquals (0, handler.annotatedMethods.size());
handler.annotatedClassNames.clear();
handler.annotatedFields.clear();
handler.annotatedMethods.clear();
parser.parse (ClassA.class.getName(), new ClassNameResolver()
{
public boolean isExcluded(String name)
{
@ -115,136 +204,45 @@ public class TestAnnotationInheritance extends TestCase
return false;
}
});
assertEquals(1, finder.getClassesForAnnotation(Sample.class).size());
assertEquals (1, handler.annotatedClassNames.size());
}
public void testResourceAnnotations ()
public void testTypeInheritanceHandling ()
throws Exception
{
Server server = new Server();
WebAppContext wac = new WebAppContext();
wac.setServer(server);
AnnotationParser parser = new AnnotationParser();
ClassInheritanceHandler handler = new ClassInheritanceHandler();
parser.registerClassHandler(handler);
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);
classNames.add(ResourceA.class.getName());
classNames.add(ResourceB.class.getName());
AnnotationFinder finder = new AnnotationFinder();
finder.find(classNames, new ClassNameResolver()
class Foo implements InterfaceD
{
public boolean isExcluded(String name)
{
return false;
}
public boolean shouldOverride(String name)
{
return false;
}
});
List<Class<?>> resourcesClasses = finder.getClassesForAnnotation(Resources.class);
assertNotNull(resourcesClasses);
assertEquals(1, resourcesClasses.size());
List<Class<?>> annotatedClasses = finder.getClassesForAnnotation(Resource.class);
List<Method> annotatedMethods = finder.getMethodsForAnnotation(Resource.class);
List<Field> annotatedFields = finder.getFieldsForAnnotation(Resource.class);
assertNotNull(annotatedClasses);
assertEquals(0, annotatedClasses.size());
assertEquals(3, annotatedMethods.size());
assertEquals(6, annotatedFields.size());
InjectionCollection injections = new InjectionCollection();
wac.setAttribute(InjectionCollection.INJECTION_COLLECTION, injections);
LifeCycleCallbackCollection callbacks = new LifeCycleCallbackCollection();
wac.setAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION, callbacks);
RunAsCollection runAses = new RunAsCollection();
wac.setAttribute(RunAsCollection.RUNAS_COLLECTION, runAses);
AnnotationProcessor processor = new AnnotationProcessor(wac, finder);
//process with all the specific annotations turned into injections, callbacks etc
processor.process();
//processing classA should give us these jndi name bindings:
// java:comp/env/myf
// java:comp/env/org.eclipse.jetty.annotations.resources.ResourceA/g
// java:comp/env/mye
// java:comp/env/org.eclipse.jetty.annotations.resources.ResourceA/h
// java:comp/env/resA
// java:comp/env/org.eclipse.jetty.annotations.resources.ResourceB/f
// java:comp/env/org.eclipse.jetty.annotations.resources.ResourceA/n
//
assertEquals(resourceB.getObjectToBind(), env.lookup("myf"));
assertEquals(resourceA.getObjectToBind(), env.lookup("mye"));
assertEquals(resourceA.getObjectToBind(), env.lookup("resA"));
assertEquals(resourceA.getObjectToBind(), env.lookup("org.eclipse.jetty.annotations.resources.ResourceA/g"));
assertEquals(resourceA.getObjectToBind(), env.lookup("org.eclipse.jetty.annotations.resources.ResourceA/h"));
assertEquals(resourceB.getObjectToBind(), env.lookup("org.eclipse.jetty.annotations.resources.ResourceB/f"));
assertEquals(resourceB.getObjectToBind(), env.lookup("org.eclipse.jetty.annotations.resources.ResourceA/n"));
//we should have Injections
assertNotNull(injections);
List<Injection> fieldInjections = injections.getFieldInjections(ResourceB.class);
assertNotNull(fieldInjections);
Iterator itor = fieldInjections.iterator();
System.err.println("Field injections:");
while (itor.hasNext())
{
System.err.println(itor.next());
}
//only 1 field injection because the other has no Resource mapping
assertEquals(1, fieldInjections.size());
fieldInjections = injections.getFieldInjections(ResourceA.class);
assertNotNull(fieldInjections);
assertEquals(4, fieldInjections.size());
classNames.clear();
classNames.add(ClassA.class.getName());
classNames.add(ClassB.class.getName());
classNames.add(InterfaceD.class.getName());
classNames.add(Foo.class.getName());
List<Injection> methodInjections = injections.getMethodInjections(ResourceB.class);
itor = methodInjections.iterator();
System.err.println("Method injections:");
while (itor.hasNext())
System.err.println(itor.next());
parser.parse(classNames, null);
assertNotNull(methodInjections);
assertEquals(0, methodInjections.size());
methodInjections = injections.getMethodInjections(ResourceA.class);
assertNotNull(methodInjections);
assertEquals(3, methodInjections.size());
//test injection
ResourceB binst = new ResourceB();
injections.inject(binst);
//check injected values
Field f = ResourceB.class.getDeclaredField ("f");
f.setAccessible(true);
assertEquals(resourceB.getObjectToBind() , f.get(binst));
//@Resource(mappedName="resA") //test the default naming scheme but using a mapped name from the environment
f = ResourceA.class.getDeclaredField("g");
f.setAccessible(true);
assertEquals(resourceA.getObjectToBind(), f.get(binst));
//@Resource(name="resA") //test using the given name as the name from the environment
f = ResourceA.class.getDeclaredField("j");
f.setAccessible(true);
assertEquals(resourceA.getObjectToBind(), f.get(binst));
//@Resource(mappedName="resB") //test using the default name on an inherited field
f = ResourceA.class.getDeclaredField("n");
f.setAccessible(true);
assertEquals(resourceB.getObjectToBind(), f.get(binst));
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]);
}
}

View File

@ -0,0 +1,135 @@
// ========================================================================
// 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.annotations;
import java.util.Arrays;
import java.util.List;
import junit.framework.TestCase;
import org.eclipse.jetty.annotations.AnnotationParser.AnnotationHandler;
import org.eclipse.jetty.annotations.AnnotationParser.Value;
public class TestAnnotationParser extends TestCase
{
public void testSampleAnnotation ()
throws Exception
{
String[] classNames = new String[]{"org.eclipse.jetty.annotations.ClassA"};
AnnotationParser parser = new AnnotationParser();
class SampleAnnotationHandler implements AnnotationHandler
{
List<String> methods = Arrays.asList("a", "b", "c", "d", "l");
public void handleClass(String className, int version, int access, String signature, String superName, String[] interfaces, String annotation,
List<Value> values)
{
assertEquals ("org.eclipse.jetty.annotations.ClassA", className);
}
public void handleField(String className, String fieldName, int access, String fieldType, String signature, Object value, String annotation,
List<Value> values)
{
assertEquals ("m", fieldName);
assertEquals (org.objectweb.asm.Type.OBJECT, org.objectweb.asm.Type.getType(fieldType).getSort());
assertEquals (1, values.size());
Value anv1 = values.get(0);
assertEquals ("value", anv1.getName());
assertEquals (7, anv1.getValue());
}
public void handleMethod(String className, String methodName, int access, String params, String signature, String[] exceptions, String annotation,
List<Value> values)
{
assertEquals("org.eclipse.jetty.annotations.ClassA", className);
assertTrue(methods.contains(methodName));
assertEquals("org.eclipse.jetty.annotations.Sample", annotation);
}
}
parser.registerAnnotationHandler("org.eclipse.jetty.annotations.Sample", new SampleAnnotationHandler());
long start = System.currentTimeMillis();
parser.parse(classNames, 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 testMultiAnnotation ()
throws Exception
{
String[] classNames = new String[]{"org.eclipse.jetty.annotations.ClassB"};
AnnotationParser parser = new AnnotationParser();
class MultiAnnotationHandler implements AnnotationHandler
{
public void handleClass(String className, int version, int access, String signature, String superName, String[] interfaces, String annotation,
List<Value> values)
{
assertTrue("org.eclipse.jetty.annotations.ClassB".equals(className));
for (Value anv: values)
{
System.err.println(anv.toString());
}
}
public void handleField(String className, String fieldName, int access, String fieldType, String signature, Object value, String annotation,
List<Value> values)
{
//there should not be any
fail();
}
public void handleMethod(String className, String methodName, int access, String params, String signature, String[] exceptions, String annotation,
List<Value> values)
{
assertTrue("org.eclipse.jetty.annotations.ClassB".equals(className));
assertTrue("a".equals(methodName));
for (Value anv: values)
{
System.err.println(anv.toString());
}
}
}
parser.registerAnnotationHandler("org.eclipse.jetty.annotations.Multi", new MultiAnnotationHandler());
parser.parse(classNames, null);
}
}

View File

@ -0,0 +1,152 @@
// ========================================================================
// 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 junit.framework.TestCase;
import org.eclipse.jetty.annotations.AnnotationParser.AnnotationHandler;
import org.eclipse.jetty.annotations.AnnotationParser.Value;
/**
* TestServletAnnotations
*
*
*/
public class TestServletAnnotations extends TestCase
{
public void testServletAnnotation()
throws Exception
{
List<String> classes = new ArrayList<String>();
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<Value> values)
{}
public void handleField(String className, String fieldName, int access, String fieldType, String signature, Object value, String annotation,
List<Value> values)
{
assertEquals ("org.eclipse.jetty.annotations.ServletC", className);
assertEquals ("foo",fieldName);
assertNotNull (values);
assertNotNull (annotation);
assertTrue (annotation.endsWith("Resource"));
assertEquals (1, values.size());
Value anv = values.get(0);
assertEquals ("mappedName", anv.getName());
assertEquals ("foo", anv.getValue());
System.err.print(annotation+": ");
System.err.println(anv);
}
public void handleMethod(String className, String methodName, int access, String params, String signature, String[] exceptions, String annotation,
List<Value> values)
{}
}
class CallbackAnnotationHandler implements AnnotationHandler
{
public void handleClass(String className, int version, int access, String signature, String superName, String[] interfaces, String annotation,
List<Value> values)
{}
public void handleMethod(String className, String methodName, int access, String params, String signature, String[] exceptions, String annotation,
List<Value> 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());
}
System.err.println(annotation+": "+methodName);
}
public void handleField(String className, String fieldName, int access, String fieldType, String signature, Object value, String annotation,
List<Value> values)
{}
}
class RunAsAnnotationHandler implements AnnotationHandler
{
public void handleClass(String className, int version, int access, String signature, String superName, String[] interfaces, String annotation,
List<Value> values)
{
assertNotNull (values);
assertEquals(1, values.size());
Value anv = values.get(0);
assertEquals("value", anv.getName());
assertEquals("admin", anv.getValue());
System.err.print(annotation+": ");
System.err.println(anv);
}
public void handleMethod(String className, String methodName, int access, String params, String signature, String[] exceptions, String annotation,
List<Value> values)
{}
public void handleField(String className, String fieldName, int access, String fieldType, String signature, Object value, String annotation,
List<Value> values)
{}
}
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)));
}
}

View File

@ -0,0 +1,173 @@
package org.eclipse.jetty.annotations.resources;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import javax.naming.Context;
import javax.naming.InitialContext;
import org.eclipse.jetty.annotations.AnnotationParser;
import org.eclipse.jetty.annotations.ClassNameResolver;
import org.eclipse.jetty.annotations.ResourceAnnotationHandler;
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.webapp.WebAppContext;
import junit.framework.TestCase;
public class TestResourceAnnotations extends TestCase
{
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);
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);
ArrayList<String> classNames = new ArrayList<String>();
classNames.add(ResourceA.class.getName());
classNames.add(ResourceB.class.getName());
AnnotationParser parser = new AnnotationParser();
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;
}
});
//processing classA should give us these jndi name bindings:
// java:comp/env/myf
// java:comp/env/org.eclipse.jetty.annotations.resources.ResourceA/g
// java:comp/env/mye
// java:comp/env/org.eclipse.jetty.annotations.resources.ResourceA/h
// java:comp/env/resA
// java:comp/env/org.eclipse.jetty.annotations.resources.ResourceB/f
// java:comp/env/org.eclipse.jetty.annotations.resources.ResourceA/n
//
assertEquals(resourceB.getObjectToBind(), env.lookup("myf"));
assertEquals(resourceA.getObjectToBind(), env.lookup("mye"));
assertEquals(resourceA.getObjectToBind(), env.lookup("resA"));
assertEquals(resourceA.getObjectToBind(), env.lookup("org.eclipse.jetty.annotations.resources.ResourceA/g"));
assertEquals(resourceA.getObjectToBind(), env.lookup("org.eclipse.jetty.annotations.resources.ResourceA/h"));
assertEquals(resourceB.getObjectToBind(), env.lookup("org.eclipse.jetty.annotations.resources.ResourceB/f"));
assertEquals(resourceB.getObjectToBind(), env.lookup("org.eclipse.jetty.annotations.resources.ResourceA/n"));
//we should have Injections
assertNotNull(injections);
List<Injection> fieldInjections = injections.getFieldInjections(ResourceB.class);
assertNotNull(fieldInjections);
//only 1 field injection because the other has no Resource mapping
assertEquals(1, fieldInjections.size());
Injection fi = fieldInjections.get(0);
assertEquals ("f", fi.getTarget().getName());
fieldInjections = injections.getFieldInjections(ResourceA.class);
assertNotNull(fieldInjections);
assertEquals(4, fieldInjections.size());
//no method injections on class ResourceB
List<Injection> methodInjections = injections.getMethodInjections(ResourceB.class);
assertNotNull(methodInjections);
assertEquals(0, methodInjections.size());
//3 method injections on class ResourceA
methodInjections = injections.getMethodInjections(ResourceA.class);
assertNotNull(methodInjections);
assertEquals(3, methodInjections.size());
//test injection
ResourceB binst = new ResourceB();
injections.inject(binst);
//check injected values
Field f = ResourceB.class.getDeclaredField ("f");
f.setAccessible(true);
assertEquals(resourceB.getObjectToBind() , f.get(binst));
//@Resource(mappedName="resA") //test the default naming scheme but using a mapped name from the environment
f = ResourceA.class.getDeclaredField("g");
f.setAccessible(true);
assertEquals(resourceA.getObjectToBind(), f.get(binst));
//@Resource(name="resA") //test using the given name as the name from the environment
f = ResourceA.class.getDeclaredField("j");
f.setAccessible(true);
assertEquals(resourceA.getObjectToBind(), f.get(binst));
//@Resource(mappedName="resB") //test using the default name on an inherited field
f = ResourceA.class.getDeclaredField("n");
f.setAccessible(true);
assertEquals(resourceB.getObjectToBind(), f.get(binst));
comp.destroySubcontext("env");
}
public void testResourcesAnnotation ()
throws Exception
{
Server server = new Server();
WebAppContext wac = new WebAppContext();
wac.setServer(server);
InjectionCollection injections = new InjectionCollection();
wac.setAttribute(InjectionCollection.INJECTION_COLLECTION, injections);
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);
ArrayList<String> classNames = new ArrayList<String>();
classNames.add(ResourceA.class.getName());
classNames.add(ResourceB.class.getName());
AnnotationParser parser = new AnnotationParser();
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;
}
});
assertEquals(resourceA.getObjectToBind(), env.lookup("fluff"));
assertEquals(resourceB.getObjectToBind(), env.lookup("stuff"));
}
}

View File

@ -34,6 +34,7 @@ import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.webapp.Configuration;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.webapp.WebXmlProcessor;
import org.eclipse.jetty.webapp.WebXmlProcessor.Descriptor;
import org.eclipse.jetty.xml.XmlParser;
@ -54,8 +55,6 @@ public abstract class AbstractConfiguration implements Configuration
public abstract void bindUserTransaction (WebAppContext context) throws Exception;
public abstract void bindMessageDestinationRef (WebAppContext context, String name, Class type) throws Exception;
protected abstract void parseAnnotations (WebAppContext context) throws Exception;
public class PlusWebXmlProcessor
@ -66,7 +65,13 @@ public abstract class AbstractConfiguration implements Configuration
{
_context = context;
}
public void process (Descriptor d)
throws Exception
{
if (d != null)
process(d.getRoot());
}
public void process (XmlParser.Node root)
throws Exception
@ -403,7 +408,7 @@ public abstract class AbstractConfiguration implements Configuration
InjectionCollection injections = new InjectionCollection();
context.setAttribute(InjectionCollection.INJECTION_COLLECTION, injections);
RunAsCollection runAsCollection = new RunAsCollection();
context.setAttribute(RunAsCollection.RUNAS_COLLECTION, runAsCollection);
context.setAttribute(RunAsCollection.RUNAS_COLLECTION, runAsCollection);
}
public void configure (WebAppContext context)
@ -420,20 +425,16 @@ public abstract class AbstractConfiguration implements Configuration
PlusWebXmlProcessor plusProcessor = new PlusWebXmlProcessor(context);
plusProcessor.process(webXmlProcessor.getWebDefault());
plusProcessor.process(webXmlProcessor.getWebXml());
//TODO: can web-fragment contain resource-ref and injection-targets?
for (XmlParser.Node frag: webXmlProcessor.getFragments())
//Process plus-elements of each descriptor
for (Descriptor frag: webXmlProcessor.getFragments())
{
plusProcessor.process(frag);
}
//process the override-web.xml descriptor
plusProcessor.process(webXmlProcessor.getOverrideWeb());
//parse classes for annotations, if necessary
if (!webXmlProcessor.isMetaDataComplete())
{
if (Log.isDebugEnabled()) Log.debug("Processing annotations");
parseAnnotations(context);
}
//configure injections and callbacks to be called by the FilterHolder and ServletHolder
//when they lazily instantiate the Filter/Servlet.

View File

@ -143,7 +143,7 @@ public class Configuration extends AbstractConfiguration
//lock this webapp's java:comp namespace as per J2EE spec
ClassLoader oldLoader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(context.getClassLoader());
lockCompEnv();
lockCompEnv(context);
Thread.currentThread().setContextClassLoader(oldLoader);
}
@ -151,42 +151,52 @@ public class Configuration extends AbstractConfiguration
{
ClassLoader oldLoader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(context.getClassLoader());
unlockCompEnv();
unlockCompEnv(context);
_key = null;
Thread.currentThread().setContextClassLoader(oldLoader);
super.deconfigure (context);
}
protected void lockCompEnv ()
protected void lockCompEnv (WebAppContext wac)
throws Exception
{
Random random = new Random ();
_key = new Integer(random.nextInt());
Context context = new InitialContext();
Context compCtx = (Context)context.lookup("java:comp");
compCtx.addToEnvironment("org.eclipse.jndi.lock", _key);
ClassLoader old_loader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(wac.getClassLoader());
try
{
Random random = new Random ();
_key = new Integer(random.nextInt());
Context context = new InitialContext();
Context compCtx = (Context)context.lookup("java:comp");
compCtx.addToEnvironment("org.eclipse.jndi.lock", _key);
}
finally
{
Thread.currentThread().setContextClassLoader(old_loader);
}
}
protected void unlockCompEnv ()
protected void unlockCompEnv (WebAppContext wac)
throws Exception
{
if (_key!=null)
{ClassLoader old_loader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(wac.getClassLoader());
try
{
Context context = new InitialContext();
Context compCtx = (Context)context.lookup("java:comp");
compCtx.addToEnvironment("org.eclipse.jndi.unlock", _key);
}
finally
{
Thread.currentThread().setContextClassLoader(old_loader);
}
}
}
/**
* @see org.eclipse.jetty.plus.webapp.AbstractConfiguration#parseAnnotations()
*/
public void parseAnnotations(WebAppContext context) throws Exception
{
//Noop unless you want to do annotation discovery.
//Use org.eclipse.jetty.annotations.Configuration instead.
}
/**
* 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

View File

@ -58,7 +58,8 @@ public class EnvConfiguration implements Configuration
*/
public void preConfigure (WebAppContext context) throws Exception
{
//create a java:comp/env
createEnvContext(context);
}
/**
@ -66,9 +67,6 @@ public class EnvConfiguration implements Configuration
*/
public void configure (WebAppContext context) throws Exception
{
//create a java:comp/env - do this here instead of preConfigure because it needs the
//webapp classloader set up, which only happens in WebInfConfiguration.configure() step.
createEnvContext();
if (Log.isDebugEnabled())
Log.debug("Created java:comp/env for webapp "+context.getContextPath());
@ -96,7 +94,7 @@ public class EnvConfiguration implements Configuration
XmlConfiguration configuration = new XmlConfiguration(jettyEnvXmlUrl);
configuration.configure(context);
}
//add java:comp/env entries for any EnvEntries that have been defined so far
bindEnvEntries(context);
}
@ -193,11 +191,20 @@ public class EnvConfiguration implements Configuration
}
}
protected void createEnvContext ()
protected void createEnvContext (WebAppContext wac)
throws NamingException
{
Context context = new InitialContext();
Context compCtx = (Context)context.lookup ("java:comp");
compCtx.createSubcontext("env");
ClassLoader old_loader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(wac.getClassLoader());
try
{
Context context = new InitialContext();
Context compCtx = (Context)context.lookup ("java:comp");
compCtx.createSubcontext("env");
}
finally
{
Thread.currentThread().setContextClassLoader(old_loader);
}
}
}

View File

@ -21,6 +21,7 @@ import org.eclipse.jetty.plus.jndi.EnvEntry;
import org.eclipse.jetty.plus.jndi.NamingEntry;
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;
public class TestConfiguration extends TestCase
@ -37,84 +38,96 @@ public class TestConfiguration extends TestCase
public void testIt ()
throws Exception
{
InitialContext ic = new InitialContext();
Server server = new Server();
WebAppContext wac = new MyWebAppContext();
wac.setServer(server);
//bind some EnvEntrys at the server level
EnvEntry ee1 = new EnvEntry(server, "xxx/a", "100", true);
EnvEntry ee2 = new EnvEntry(server, "yyy/b", "200", false);
EnvEntry ee3 = new EnvEntry(server, "zzz/c", "300", false);
EnvEntry ee4 = new EnvEntry(server, "zzz/d", "400", false);
EnvEntry ee5 = new EnvEntry(server, "zzz/f", "500", true);
//bind some EnvEntrys at the webapp level
EnvEntry ee6 = new EnvEntry(wac, "xxx/a", "900", true);
EnvEntry ee7 = new EnvEntry(wac, "yyy/b", "910", true);
EnvEntry ee8 = new EnvEntry(wac, "zzz/c", "920", false);
EnvEntry ee9 = new EnvEntry(wac, "zzz/e", "930", false);
assertNotNull(NamingEntryUtil.lookupNamingEntry(server, "xxx/a"));
assertNotNull(NamingEntryUtil.lookupNamingEntry(server, "yyy/b"));
assertNotNull(NamingEntryUtil.lookupNamingEntry(server, "zzz/c"));
assertNotNull(NamingEntryUtil.lookupNamingEntry(server, "zzz/d"));
assertNotNull(NamingEntryUtil.lookupNamingEntry(wac, "xxx/a"));
assertNotNull(NamingEntryUtil.lookupNamingEntry(wac, "yyy/b"));
assertNotNull(NamingEntryUtil.lookupNamingEntry(wac, "zzz/c"));
assertNotNull(NamingEntryUtil.lookupNamingEntry(wac, "zzz/e"));
Configuration config = new Configuration();
EnvConfiguration envConfig = new EnvConfiguration();
envConfig.preConfigure(wac);
envConfig.configure(wac);
envConfig.bindEnvEntries(wac);
String val = (String)ic.lookup("java:comp/env/xxx/a");
assertEquals("900", val); //webapp naming overrides server
val = (String)ic.lookup("java:comp/env/yyy/b");
assertEquals("910", val);//webapp overrides server
val = (String)ic.lookup("java:comp/env/zzz/c");
assertEquals("920",val);//webapp overrides server
val = (String)ic.lookup("java:comp/env/zzz/d");
assertEquals("400", val);//from server naming
val = (String)ic.lookup("java:comp/env/zzz/e");
assertEquals("930", val);//from webapp naming
NamingEntry ne = (NamingEntry)ic.lookup("java:comp/env/"+NamingEntry.__contextName+"/xxx/a");
assertNotNull(ne);
ne = (NamingEntry)ic.lookup("java:comp/env/"+NamingEntry.__contextName+"/yyy/b");
assertNotNull(ne);
ne = (NamingEntry)ic.lookup("java:comp/env/"+NamingEntry.__contextName+"/zzz/c");
assertNotNull(ne);
ne = (NamingEntry)ic.lookup("java:comp/env/"+NamingEntry.__contextName+"/zzz/d");
assertNotNull(ne);
ne = (NamingEntry)ic.lookup("java:comp/env/"+NamingEntry.__contextName+"/zzz/e");
assertNotNull(ne);
config.bindEnvEntry(wac, "foo", "99");
assertEquals("99",ic.lookup( "java:comp/env/foo"));
config.bindEnvEntry(wac, "xxx/a", "7");
assertEquals("900", ic.lookup("java:comp/env/xxx/a")); //webapp overrides web.xml
config.bindEnvEntry(wac, "yyy/b", "7");
assertEquals("910", ic.lookup("java:comp/env/yyy/b"));//webapp overrides web.xml
config.bindEnvEntry(wac,"zzz/c", "7");
assertEquals("7", ic.lookup("java:comp/env/zzz/c"));//webapp does NOT override web.xml
config.bindEnvEntry(wac,"zzz/d", "7");
assertEquals("7", ic.lookup("java:comp/env/zzz/d"));//server does NOT override web.xml
config.bindEnvEntry(wac,"zzz/e", "7");
assertEquals("7", ic.lookup("java:comp/env/zzz/e"));//webapp does NOT override web.xml
config.bindEnvEntry(wac,"zzz/f", "7");
assertEquals("500", ic.lookup("java:comp/env/zzz/f"));//server overrides web.xml
((Context)ic.lookup("java:comp")).destroySubcontext("env");
ic.destroySubcontext("xxx");
ic.destroySubcontext("yyy");
ic.destroySubcontext("zzz");
ClassLoader old_loader = Thread.currentThread().getContextClassLoader();
try
{
InitialContext ic = new InitialContext();
Server server = new Server();
WebAppContext wac = new MyWebAppContext();
wac.setServer(server);
wac.setClassLoader(new WebAppClassLoader(Thread.currentThread().getContextClassLoader(), wac));
//bind some EnvEntrys at the server level
EnvEntry ee1 = new EnvEntry(server, "xxx/a", "100", true);
EnvEntry ee2 = new EnvEntry(server, "yyy/b", "200", false);
EnvEntry ee3 = new EnvEntry(server, "zzz/c", "300", false);
EnvEntry ee4 = new EnvEntry(server, "zzz/d", "400", false);
EnvEntry ee5 = new EnvEntry(server, "zzz/f", "500", true);
//bind some EnvEntrys at the webapp level
EnvEntry ee6 = new EnvEntry(wac, "xxx/a", "900", true);
EnvEntry ee7 = new EnvEntry(wac, "yyy/b", "910", true);
EnvEntry ee8 = new EnvEntry(wac, "zzz/c", "920", false);
EnvEntry ee9 = new EnvEntry(wac, "zzz/e", "930", false);
assertNotNull(NamingEntryUtil.lookupNamingEntry(server, "xxx/a"));
assertNotNull(NamingEntryUtil.lookupNamingEntry(server, "yyy/b"));
assertNotNull(NamingEntryUtil.lookupNamingEntry(server, "zzz/c"));
assertNotNull(NamingEntryUtil.lookupNamingEntry(server, "zzz/d"));
assertNotNull(NamingEntryUtil.lookupNamingEntry(wac, "xxx/a"));
assertNotNull(NamingEntryUtil.lookupNamingEntry(wac, "yyy/b"));
assertNotNull(NamingEntryUtil.lookupNamingEntry(wac, "zzz/c"));
assertNotNull(NamingEntryUtil.lookupNamingEntry(wac, "zzz/e"));
Configuration config = new Configuration();
EnvConfiguration envConfig = new EnvConfiguration();
Thread.currentThread().setContextClassLoader(wac.getClassLoader());
envConfig.preConfigure(wac);
envConfig.configure(wac);
envConfig.bindEnvEntries(wac);
String val = (String)ic.lookup("java:comp/env/xxx/a");
assertEquals("900", val); //webapp naming overrides server
val = (String)ic.lookup("java:comp/env/yyy/b");
assertEquals("910", val);//webapp overrides server
val = (String)ic.lookup("java:comp/env/zzz/c");
assertEquals("920",val);//webapp overrides server
val = (String)ic.lookup("java:comp/env/zzz/d");
assertEquals("400", val);//from server naming
val = (String)ic.lookup("java:comp/env/zzz/e");
assertEquals("930", val);//from webapp naming
NamingEntry ne = (NamingEntry)ic.lookup("java:comp/env/"+NamingEntry.__contextName+"/xxx/a");
assertNotNull(ne);
ne = (NamingEntry)ic.lookup("java:comp/env/"+NamingEntry.__contextName+"/yyy/b");
assertNotNull(ne);
ne = (NamingEntry)ic.lookup("java:comp/env/"+NamingEntry.__contextName+"/zzz/c");
assertNotNull(ne);
ne = (NamingEntry)ic.lookup("java:comp/env/"+NamingEntry.__contextName+"/zzz/d");
assertNotNull(ne);
ne = (NamingEntry)ic.lookup("java:comp/env/"+NamingEntry.__contextName+"/zzz/e");
assertNotNull(ne);
config.bindEnvEntry(wac, "foo", "99");
assertEquals("99",ic.lookup( "java:comp/env/foo"));
config.bindEnvEntry(wac, "xxx/a", "7");
assertEquals("900", ic.lookup("java:comp/env/xxx/a")); //webapp overrides web.xml
config.bindEnvEntry(wac, "yyy/b", "7");
assertEquals("910", ic.lookup("java:comp/env/yyy/b"));//webapp overrides web.xml
config.bindEnvEntry(wac,"zzz/c", "7");
assertEquals("7", ic.lookup("java:comp/env/zzz/c"));//webapp does NOT override web.xml
config.bindEnvEntry(wac,"zzz/d", "7");
assertEquals("7", ic.lookup("java:comp/env/zzz/d"));//server does NOT override web.xml
config.bindEnvEntry(wac,"zzz/e", "7");
assertEquals("7", ic.lookup("java:comp/env/zzz/e"));//webapp does NOT override web.xml
config.bindEnvEntry(wac,"zzz/f", "7");
assertEquals("500", ic.lookup("java:comp/env/zzz/f"));//server overrides web.xml
((Context)ic.lookup("java:comp")).destroySubcontext("env");
ic.destroySubcontext("xxx");
ic.destroySubcontext("yyy");
ic.destroySubcontext("zzz");
}
finally
{
Thread.currentThread().setContextClassLoader(old_loader);
}
}
}

View File

@ -6,8 +6,8 @@
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-webapp</artifactId>
<name>Jetty :: Web Applications</name>
<description>Jetty web application</description>
<name>Jetty :: Webapp Application Support</name>
<description>Jetty web application support</description>
<build>
<resources>
<resource><directory>src/main/resources</directory></resource>

View File

@ -91,7 +91,7 @@ public class FragmentConfiguration implements Configuration
{
for (Resource frag : frags)
{
processor.parseFragment("jar:"+frag.getURL()+"!/META-INF/web-fragment.xml");
processor.parseFragment(Resource.newResource("jar:"+frag.getURL()+"!/META-INF/web-fragment.xml"));
}
}
}

View File

@ -15,11 +15,9 @@ package org.eclipse.jetty.webapp;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.regex.Pattern;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.resource.Resource;
@ -39,12 +37,17 @@ public class MetaInfConfiguration implements Configuration
public static final String METAINF_TLDS = TagLibConfiguration.TLD_RESOURCES;
public static final String METAINF_FRAGMENTS = FragmentConfiguration.FRAGMENT_RESOURCES;
public static final String METAINF_RESOURCES = WebInfConfiguration.RESOURCE_URLS;
public static final String JAR_RESOURCES = WebInfConfiguration.JAR_RESOURCES;
public static final String CONTAINER_JAR_RESOURCES = WebInfConfiguration.CONTAINER_JAR_RESOURCES;
public static final String WEB_INF_JAR_RESOURCES = WebInfConfiguration.WEB_INF_JAR_RESOURCES;
public void preConfigure(final WebAppContext context) throws Exception
{
//Merge all container and webinf lib jars to look for META-INF resources
ArrayList<Resource> jars = new ArrayList<Resource>();
jars.addAll((List<Resource>)context.getAttribute(CONTAINER_JAR_RESOURCES));
jars.addAll((List<Resource>)context.getAttribute(WEB_INF_JAR_RESOURCES));
JarScanner scanner = new JarScanner()
{
public void processEntry(URI jarUri, JarEntry entry)
@ -60,14 +63,13 @@ public class MetaInfConfiguration implements Configuration
}
};
List<Resource> jarResources = (List<Resource>)context.getAttribute(JAR_RESOURCES);
//Scan jars for META-INF information
if (jarResources != null)
if (jars != null)
{
URI[] uris = new URI[jarResources.size()];
URI[] uris = new URI[jars.size()];
int i=0;
for (Resource r : jarResources)
for (Resource r : jars)
{
uris[i++] = r.getURI();
}

View File

@ -50,9 +50,6 @@ public class TagLibConfiguration implements Configuration
{
public static final String TLD_RESOURCES = "org.eclipse.jetty.tlds";
// TODO support patterns
private static final String __web_inf_pattern = "org.eclipse.jetty.webapp.WebInfIncludeTLDJarPattern";
private static final String __container_pattern = "org.eclipse.jetty.server.webapp.ContainerIncludeTLDJarPattern";
public class TldProcessor
{
@ -240,7 +237,9 @@ public class TagLibConfiguration implements Configuration
}
// Add in tlds found in META-INF of jars
// Add in tlds found in META-INF of jars. The jars that will be scanned are controlled by
// the patterns defined in the context attributes: org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern,
// and org.eclipse.jetty.server.webapp.WebInfIncludeJarPattern
Collection<Resource> tld_resources=(Collection<Resource>)context.getAttribute(TLD_RESOURCES);
if (tld_resources!=null)
tlds.addAll(tld_resources);

View File

@ -21,10 +21,6 @@ import java.util.EventListener;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSessionActivationListener;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingListener;
@ -39,13 +35,11 @@ import org.eclipse.jetty.server.session.SessionHandler;
import org.eclipse.jetty.servlet.ErrorPageErrorHandler;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.LazyList;
import org.eclipse.jetty.util.Loader;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.resource.JarResource;
import org.eclipse.jetty.util.resource.Resource;
/* ------------------------------------------------------------ */
@ -115,7 +109,7 @@ public class WebAppContext extends ServletContextHandler
private Map _resourceAliases;
private boolean _ownClassLoader=false;
private boolean _configurationDiscovered=false;
private boolean _configurationDiscovered=true;
public static ContextHandler getCurrentWebAppContext()
{
@ -338,6 +332,8 @@ public class WebAppContext extends ServletContextHandler
}
}
// Prepare for configuration
for (int i=0;i<_configurations.length;i++)
_configurations[i].preConfigure(this);

View File

@ -21,7 +21,8 @@ import org.eclipse.jetty.util.resource.ResourceCollection;
public class WebInfConfiguration implements Configuration
{
public static final String TEMPDIR_CREATED = "org.eclipse.jetty.tmpdirCreated";
public static final String JAR_RESOURCES = "org.eclipse.jetty.jarList";
public static final String CONTAINER_JAR_RESOURCES = "org.eclipse.jetty.containerJars";
public static final String WEB_INF_JAR_RESOURCES = "org.eclipse.jetty.webInfJars";
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";
@ -53,19 +54,18 @@ public class WebInfConfiguration implements Configuration
tmp = (String)context.getAttribute(CONTAINER_JAR_PATTERN);
Pattern containerPattern = (tmp==null?null:Pattern.compile(tmp));
final ArrayList jarResources = new ArrayList<Resource>();
context.setAttribute(JAR_RESOURCES, jarResources);
PatternMatcher jarNameMatcher = new PatternMatcher ()
final ArrayList containerJarResources = new ArrayList<Resource>();
context.setAttribute(CONTAINER_JAR_RESOURCES, containerJarResources);
//Apply ordering to container jars - if no pattern is specified, we won't
//match any of the container jars
PatternMatcher containerJarNameMatcher = new PatternMatcher ()
{
public void matched(URI uri) throws Exception
{
jarResources.add(Resource.newResource(uri));
}
containerJarResources.add(Resource.newResource(uri));
}
};
//Apply ordering to container jars
ClassLoader loader = context.getClassLoader();
while (loader != null && (loader instanceof URLClassLoader))
{
@ -78,13 +78,21 @@ public class WebInfConfiguration implements Configuration
{
containerUris[i++] = u.toURI();
}
jarNameMatcher.match(containerPattern, containerUris, false);
containerJarNameMatcher.match(containerPattern, containerUris, false);
}
loader = loader.getParent();
}
//Apply ordering to WEB-INF/lib jars
//Find all jars in WEB-INF
final ArrayList webInfJarResources = new ArrayList<Resource>();
context.setAttribute(WEB_INF_JAR_RESOURCES, webInfJarResources);
PatternMatcher webInfJarNameMatcher = new PatternMatcher ()
{
public void matched(URI uri) throws Exception
{
webInfJarResources.add(Resource.newResource(uri));
}
};
List<Resource> jars = findJars(context);
//Convert to uris for matching
URI[] uris = null;
@ -97,7 +105,7 @@ public class WebInfConfiguration implements Configuration
uris[i++] = r.getURI();
}
}
jarNameMatcher.match(webInfPattern, uris, true); //null is inclusive, no pattern == all jars match
webInfJarNameMatcher.match(webInfPattern, uris, true); //null is inclusive, no pattern == all jars match
}

View File

@ -17,7 +17,6 @@ import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collections;
import org.eclipse.jetty.security.ConstraintAware;
import org.eclipse.jetty.security.ConstraintMapping;
import org.eclipse.jetty.security.SecurityHandler;
@ -71,9 +70,16 @@ public class WebXmlConfiguration implements Configuration
Resource dftResource = Resource.newSystemResource(defaultsDescriptor);
if (dftResource == null)
dftResource = context.newResource(defaultsDescriptor);
processor.parseDefaults (dftResource.getURL());
processor.parseDefaults (dftResource);
processor.processDefaults();
}
//parse, but don't process web.xml
Resource webxml = findWebXml(context);
if (webxml != null)
{
processor.parseWebXml(webxml);
}
}
/* ------------------------------------------------------------------------------- */
@ -98,13 +104,9 @@ public class WebXmlConfiguration implements Configuration
context.setAttribute(WebXmlProcessor.WEB_PROCESSOR, processor);
}
//process web.xml
URL webxml = findWebXml(context);
if (webxml != null)
{
processor.parseWebXml(webxml);
processor.processWebXml();
}
//process web.xml (the effective web.xml???)
processor.processWebXml();
//process override-web.xml
String overrideDescriptor = context.getOverrideDescriptor();
@ -113,7 +115,7 @@ public class WebXmlConfiguration implements Configuration
Resource orideResource = Resource.newSystemResource(overrideDescriptor);
if (orideResource == null)
orideResource = context.newResource(overrideDescriptor);
processor.parseOverride(orideResource.getURL());
processor.parseOverride(orideResource);
processor.processOverride();
}
}
@ -128,13 +130,13 @@ public class WebXmlConfiguration implements Configuration
/* ------------------------------------------------------------------------------- */
protected URL findWebXml(WebAppContext context) throws IOException, MalformedURLException
protected Resource findWebXml(WebAppContext context) throws IOException, MalformedURLException
{
String descriptor = context.getDescriptor();
if (descriptor != null)
{
Resource web = context.newResource(descriptor);
if (web.exists() && !web.isDirectory()) return web.getURL();
if (web.exists() && !web.isDirectory()) return web;
}
Resource web_inf = context.getWebInf();
@ -142,7 +144,7 @@ public class WebXmlConfiguration implements Configuration
{
// do web.xml file
Resource web = web_inf.addPath("web.xml");
if (web.exists()) return web.getURL();
if (web.exists()) return web;
Log.debug("No WEB-INF/web.xml in " + context.getWar() + ". Serving files and default/dynamic servlets only");
}
return null;

View File

@ -56,15 +56,16 @@ import org.eclipse.jetty.xml.XmlParser;
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 XmlParser.Node _webDefaultsRoot;
protected XmlParser.Node _webXmlRoot;
protected List<XmlParser.Node> _webFragmentRoots = new ArrayList<XmlParser.Node>();
protected XmlParser.Node _webOverrideRoot;
protected int _version;
protected boolean _metaDataComplete = false;
protected Descriptor _webDefaultsRoot;
protected Descriptor _webXmlRoot;
protected List<Descriptor> _webFragmentRoots = new ArrayList<Descriptor>();
protected Descriptor _webOverrideRoot;
protected ServletHandler _servletHandler;
protected SecurityHandler _securityHandler;
@ -83,6 +84,126 @@ public class WebXmlProcessor
protected boolean _defaultWelcomeFileList;
public class Descriptor
{
protected Resource _xml;
protected XmlParser.Node _root;
protected boolean _metadataComplete;
protected boolean _hasOrdering;
protected int _version;
protected ArrayList<String> _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<String>();
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<String> getClassNames ()
{
return _classNames;
}
}
public static XmlParser webXmlParser() throws ClassNotFoundException
@ -171,124 +292,89 @@ public class WebXmlProcessor
_xmlParser = webXmlParser();
}
public int getVersion ()
{
return _version;
}
public boolean isMetaDataComplete ()
{
return _metaDataComplete;
}
public void processForVersion (XmlParser.Node config)
{
String version = config.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) config.getAttribute("metadata-complete", "false")).booleanValue();
Log.debug("Calculated metadatacomplete = " + _metaDataComplete + " with version=" + version);
_context.setAttribute("metadata-complete", String.valueOf(_metaDataComplete));
}
public XmlParser.Node parseDefaults (URL webDefaults)
public void parseDefaults (Resource webDefaults)
throws Exception
{
_webDefaultsRoot = _xmlParser.parse(webDefaults.toString());
return _webDefaultsRoot;
_webDefaultsRoot = new Descriptor(webDefaults);
_webDefaultsRoot.parse();
}
public XmlParser.Node parseWebXml (URL webXml)
public void parseWebXml (Resource webXml)
throws Exception
{
_webXmlRoot = _xmlParser.parse(webXml.toString());
processForVersion(_webXmlRoot);
return _webXmlRoot;
_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 XmlParser.Node parseFragment (String fragment)
public void parseFragment (Resource fragment)
throws Exception
{
if (isMetaDataComplete())
return null; //do not process anything else if main web.xml file is complete
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
XmlParser.Node root = _xmlParser.parse(fragment);
_webFragmentRoots.add(root);
return root;
Descriptor frag = new Descriptor(fragment);
frag.parse();
_webFragmentRoots.add(frag);
}
public XmlParser.Node parseOverride (URL override)
public void parseOverride (Resource override)
throws Exception
{
_xmlParser.setValidating(false);
_webOverrideRoot = _xmlParser.parse(override.toString());
return _webOverrideRoot;
_webOverrideRoot = new Descriptor(override);
_webOverrideRoot.parse();
}
public void processDefaults ()
throws Exception
{
process (_webDefaultsRoot);
_webDefaultsRoot.process();
_defaultWelcomeFileList = _context.getWelcomeFiles() != null;
}
public void processWebXml ()
throws Exception
{
process (_webXmlRoot);
_webXmlRoot.process();
}
public void processFragments ()
throws Exception
{
for (XmlParser.Node frag : _webFragmentRoots)
for (Descriptor frag : _webFragmentRoots)
{
process (frag);
frag.process();
}
}
public void processOverride ()
throws Exception
{
process(_webOverrideRoot);
_webOverrideRoot.process();
}
public XmlParser.Node getWebXml ()
public Descriptor getWebXml ()
{
return _webXmlRoot;
}
public XmlParser.Node getOverrideWeb ()
public Descriptor getOverrideWeb ()
{
return _webOverrideRoot;
}
public XmlParser.Node getWebDefault ()
public Descriptor getWebDefault ()
{
return _webDefaultsRoot;
}
public List<XmlParser.Node> getFragments ()
public List<Descriptor> getFragments ()
{
return _webFragmentRoots;
}
@ -469,9 +555,12 @@ public class WebXmlProcessor
holder.setInitParameter(pname, pvalue);
}
String async=node.getString("async-support",false,true);
String async=node.getString("async-supported",false,true);
if (async!=null)
holder.setAsyncSupported(Boolean.valueOf(async));
holder.setAsyncSupported(async.length()==0||Boolean.valueOf(async));
String timeout=node.getString("async-timeout",false,true);
// TODO set it
}
/* ------------------------------------------------------------ */
@ -640,9 +729,12 @@ public class WebXmlProcessor
holder.setRunAsRole(roleName);
}
String async=node.getString("async-support",false,true);
String async=node.getString("async-supported",false,true);
if (async!=null)
holder.setAsyncSupported(Boolean.valueOf(async));
holder.setAsyncSupported(async.length()==0||Boolean.valueOf(async));
String timeout=node.getString("async-timeout",false,true);
// TODO set it
}
/* ------------------------------------------------------------ */