|
Jetty example source code file (AnnotationCollection.java)
The Jetty AnnotationCollection.java source code//======================================================================== //$Id: AnnotationCollection.java 1667 2007-03-16 08:29:46Z janb $ //Copyright 2006 Mort Bay Consulting Pty. Ltd. //------------------------------------------------------------------------ //Licensed under the Apache License, Version 2.0 (the "License"); //you may not use this file except in compliance with the License. //You may obtain a copy of the License at //http://www.apache.org/licenses/LICENSE-2.0 //Unless required by applicable law or agreed to in writing, software //distributed under the License is distributed on an "AS IS" BASIS, //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. //See the License for the specific language governing permissions and //limitations under the License. //======================================================================== package org.mortbay.jetty.annotations; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.List; import javax.annotation.Resource; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import javax.annotation.Resources; import javax.annotation.security.RunAs; import javax.naming.NameNotFoundException; import javax.naming.NamingException; import javax.servlet.Servlet; import javax.transaction.UserTransaction; import org.mortbay.jetty.plus.annotation.Injection; import org.mortbay.jetty.plus.annotation.InjectionCollection; import org.mortbay.jetty.plus.annotation.LifeCycleCallbackCollection; import org.mortbay.jetty.plus.annotation.PostConstructCallback; import org.mortbay.jetty.plus.annotation.PreDestroyCallback; import org.mortbay.jetty.plus.annotation.RunAsCollection; import org.mortbay.jetty.plus.naming.EnvEntry; import org.mortbay.jetty.plus.naming.Transaction; import org.mortbay.jetty.servlet.Holder; import org.mortbay.jetty.servlet.ServletHolder; import org.mortbay.log.Log; import org.mortbay.util.IntrospectionUtil; import org.mortbay.util.Loader; /** * AnnotationCollection * * An AnnotationCollection represents all of the annotated classes, methods and fields in the * inheritance hierarchy for a class. NOTE that methods and fields in this collection are NOT * just the ones that are inherited by the class, but represent ALL annotations that must be * processed for a single instance of a given class. * * The class to which this collection pertains is obtained by calling * getTargetClass(). * * Using the list of annotated classes, methods and fields, the collection will generate * the appropriate JNDI entries and the appropriate Injection and LifeCycleCallback objects * to be later applied to instances of the getTargetClass(). */ public class AnnotationCollection { private Class _targetClass; //the most derived class to which this collection pertains private List _methods = new ArrayList(); //list of methods relating to the _targetClass which have annotations private List _fields = new ArrayList(); //list of fields relating to the _targetClass which have annotations private List _classes = new ArrayList();//list of classes in the inheritance hierarchy that have annotations private static Class[] __envEntryTypes = new Class[] {String.class, Character.class, Integer.class, Boolean.class, Double.class, Byte.class, Short.class, Long.class, Float.class}; /** * Get the class which is the subject of these annotations * @return the clazz */ public Class getTargetClass() { return _targetClass; } /** * Set the class to which this collection pertains * @param clazz the clazz to set */ public void setTargetClass(Class clazz) { _targetClass=clazz; } public void addClass (Class clazz) { if (clazz.getDeclaredAnnotations().length==0) return; _classes.add(clazz); } public void addMethod (Method method) { if (method.getDeclaredAnnotations().length==0) return; _methods.add(method); } public void addField(Field field) { if (field.getDeclaredAnnotations().length==0) return; _fields.add(field); } public List getClasses() { return _classes; } public List getMethods () { return _methods; } public List getFields() { return _fields; } public void processRunAsAnnotations (RunAsCollection runAsCollection) { for (int i=0; i<_classes.size();i++) { Class clazz = (Class)_classes.get(i); //if this implements javax.servlet.Servlet check for run-as if (Servlet.class.isAssignableFrom(clazz)) { RunAs runAs = (RunAs)clazz.getAnnotation(RunAs.class); if (runAs != null) { String role = runAs.value(); if (role != null) { org.mortbay.jetty.plus.annotation.RunAs ra = new org.mortbay.jetty.plus.annotation.RunAs(); ra.setTargetClass(clazz); ra.setRoleName(role); runAsCollection.add(ra); } } } } } /** * Process @Resource annotations at the class, method and field level. * @return */ public InjectionCollection processResourceAnnotations(InjectionCollection injections) { processClassResourceAnnotations(); processMethodResourceAnnotations(injections); processFieldResourceAnnotations(injections); return injections; } /** * Process @PostConstruct and @PreDestroy annotations. * @return */ public LifeCycleCallbackCollection processLifeCycleCallbackAnnotations(LifeCycleCallbackCollection callbacks) { processPostConstructAnnotations(callbacks); processPreDestroyAnnotations(callbacks); return callbacks; } /** * Process @Resources annotation on classes */ public void processResourcesAnnotations () { for (int i=0; i<_classes.size();i++) { Class clazz = (Class)_classes.get(i); Resources resources = (Resources)clazz.getAnnotation(Resources.class); if (resources != null) { 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 //make it optional to use the mappedName to represent the JNDI name of the resource in //the runtime environment. If present the mappedName would represent the JNDI name set //for a Resource entry in jetty.xml or jetty-env.xml. if (type!=null && isEnvEntryType(type)) org.mortbay.jetty.plus.naming.NamingEntry.bindToENC((mappedName==null?name:mappedName), name, org.mortbay.jetty.plus.naming.EnvEntry.class); else { //try all types of naming resources to see what the name has been bound as try { //try a non-EnvEntry, non-Transaction type first org.mortbay.jetty.plus.naming.NamingEntry.bindToENC((mappedName==null?name:mappedName), name, org.mortbay.jetty.plus.naming.Resource.class); } catch (NameNotFoundException e) { //try an EnvEntry try { org.mortbay.jetty.plus.naming.NamingEntry.bindToENC((mappedName==null?name:mappedName), name, org.mortbay.jetty.plus.naming.EnvEntry.class); } catch (NameNotFoundException x) { //try a Transaction type org.mortbay.jetty.plus.naming.NamingEntry.bindToENC((mappedName==null?name:mappedName), name, org.mortbay.jetty.plus.naming.Transaction.class); } } } } catch (NamingException e) { throw new IllegalStateException(e); } } } } } /** * Class level Resource annotations declare a name in the * environment that will be looked up at runtime. They do * not specify an injection. */ private void processClassResourceAnnotations () { for (int i=0; i<_classes.size();i++) { Class clazz = (Class)_classes.get(i); 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 //make it optional to use the mappedName to represent the JNDI name of the resource in //the runtime environment. If present the mappedName would represent the JNDI name set //for a Resource entry in jetty.xml or jetty-env.xml. org.mortbay.jetty.plus.naming.NamingEntry.bindToENC((mappedName==null?name:mappedName), name, org.mortbay.jetty.plus.naming.Resource.class); } 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 */ private void processMethodResourceAnnotations(InjectionCollection webXmlInjections) { //Get the method level Resource annotations for (int i=0;i<_methods.size();i++) { Method m = (Method)_methods.get(i); Resource resource = (Resource)m.getAnnotation(Resource.class); if (resource != null) { //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 = webXmlInjections.getInjection(getTargetClass(), m); if (webXmlInjection == null) { try { org.mortbay.jetty.plus.naming.NamingEntry.bindToENC((mappedName==null?name:mappedName), name, getNamingEntryType(type)); Log.debug("Bound "+(mappedName==null?name:mappedName) + " as "+ name); // Make the Injection for it Injection injection = new Injection(); injection.setTargetClass(getTargetClass()); injection.setJndiName(name); injection.setMappingName(mappedName); injection.setTarget(m); webXmlInjections.add(injection); } 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 try { 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 (NamingException e) { throw new IllegalStateException(e); } } } } } /** * 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 */ private void processFieldResourceAnnotations (InjectionCollection webXmlInjections) { for (int i=0;i<_fields.size();i++) { Field f = (Field)_fields.get(i); Resource resource = (Resource)f.getAnnotation(Resource.class); if (resource != null) { //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 = webXmlInjections.getInjection(getTargetClass(), f); if (webXmlInjection == null) { try { //Check there is a JNDI entry for this annotation org.mortbay.jetty.plus.naming.NamingEntry.bindToENC((mappedName==null?name:mappedName), name, getNamingEntryType(type)); Log.debug("Bound "+(mappedName==null?name:mappedName) + " as "+ name); // Make the Injection for it if the binding succeeded Injection injection = new Injection(); injection.setTargetClass(getTargetClass()); injection.setJndiName(name); injection.setMappingName(mappedName); injection.setTarget(f); webXmlInjections.add(injection); } 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 try { 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 (NamingException e) { throw new IllegalStateException(e); } } } } } /** * Find @PostConstruct annotations. * * The spec says (Common Annotations Sec 2.5) that only ONE method * may be adorned with the PostConstruct annotation, however this does * not clarify how this works with inheritance. * * TODO work out what to do with inherited PostConstruct annotations * * @param callbacks */ private void processPostConstructAnnotations (LifeCycleCallbackCollection callbacks) { // TODO: check that the same class does not have more than one for (int i=0; i<_methods.size(); i++) { Method m = (Method)_methods.get(i); if (m.isAnnotationPresent(PostConstruct.class)) { 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(getTargetClass()); callback.setTarget(m); callbacks.add(callback); } } } /** * Find @PreDestroy annotations. * * The spec says (Common Annotations Sec 2.5) that only ONE method * may be adorned with the PreDestroy annotation, however this does * not clarify how this works with inheritance. * * TODO work out what to do with inherited PreDestroy annotations * @param callbacks */ private void processPreDestroyAnnotations (LifeCycleCallbackCollection callbacks) { //TODO: check that the same class does not have more than one for (int i=0; i<_methods.size(); i++) { Method m = (Method)_methods.get(i); if (m.isAnnotationPresent(PreDestroy.class)) { 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(getTargetClass()); callback.setTarget(m); callbacks.add(callback); } } } 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; } private static Class getNamingEntryType (Class type) { if (type==null) return null; if (isEnvEntryType(type)) return EnvEntry.class; if (type.getName().equals("javax.transaction.UserTransaction")) return Transaction.class; else return org.mortbay.jetty.plus.naming.Resource.class; } } Other Jetty examples (source code examples)Here is a short list of links related to this Jetty AnnotationCollection.java source code file: |
... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.