Index: java/org/apache/catalina/deploy/LocalStrings.properties =================================================================== --- java/org/apache/catalina/deploy/LocalStrings.properties (revision 1344406) +++ java/org/apache/catalina/deploy/LocalStrings.properties (working copy) @@ -50,3 +50,4 @@ namingResources.cleanupNoResource=Failed to retrieve JNDI resource [{0}] for container [{1}] so no cleanup was performed for that resource namingResources.mbeanCreateFail=Failed to create MBean for naming resource [{0}] namingResources.mbeanDestroyFail=Failed to destroy MBean for naming resource [{0}] +namingResources.indeterminateResourceType=Cannot determine the resource type for Resource [{0}] Index: java/org/apache/catalina/deploy/NamingResources.java =================================================================== --- java/org/apache/catalina/deploy/NamingResources.java (revision 1344406) +++ java/org/apache/catalina/deploy/NamingResources.java (working copy) @@ -22,10 +22,12 @@ import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.io.Serializable; +import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Hashtable; +import java.util.List; import javax.naming.NamingException; @@ -35,7 +37,9 @@ import org.apache.catalina.LifecycleException; import org.apache.catalina.LifecycleState; import org.apache.catalina.Server; +import org.apache.catalina.core.DefaultInstanceManager; import org.apache.catalina.mbeans.MBeanUtils; +import org.apache.catalina.startup.WebAnnotationSet; import org.apache.catalina.util.LifecycleMBeanBase; import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; @@ -251,6 +255,23 @@ } } + // 14.4 Deployment Descriptor Diagram + // If an injection-target is specified for the environment entry, + // the env-entry-type may be omitted or MUST match the type of the injection target. + // If no injection-target is specified, the env-entry-type is required. + try { + String type = processResourceType(environment, true); + if (type != null) { + environment.setType(type); + } else { + log.warn(sm.getString("namingResources.indeterminateResourceType", environment)); + return; + } + } catch (IllegalArgumentException e) { + log.warn(sm.getString("namingResources.indeterminateResourceType", environment), e); + return; + } + entries.put(environment.getName(), environment.getType()); synchronized (envs) { @@ -317,6 +338,22 @@ if (entries.containsKey(mdr.getName())) { return; } else { + // 14.4 Deployment Descriptor Diagram + // The message-destination-type MUST be specified unless an injection target is specified, + // in which case the type of the target is used. + // If both are specified, the type MUST be assignment compatible with the type of the injection target. + try { + String type = processResourceType(mdr, false); + if (type != null) { + mdr.setType(type); + } else { + log.warn(sm.getString("namingResources.indeterminateResourceType", mdr)); + return; + } + } catch (IllegalArgumentException e) { + log.warn(sm.getString("namingResources.indeterminateResourceType", mdr), e); + return; + } entries.put(mdr.getName(), mdr.getType()); } @@ -351,6 +388,18 @@ if (entries.containsKey(resource.getName())) { return; } else { + try { + String type = processResourceType(resource, false); + if (type != null) { + resource.setType(type); + } else { + log.warn(sm.getString("namingResources.indeterminateResourceType", resource)); + return; + } + } catch (IllegalArgumentException e) { + log.warn(sm.getString("namingResources.indeterminateResourceType", resource), e); + return; + } entries.put(resource.getName(), resource.getType()); } @@ -382,6 +431,22 @@ if (entries.containsKey(resource.getName())) { return; } else { + // 14.4 Deployment Descriptor Diagram + // The resource-env-ref-type MUST be supplied unless an injection target is specified, + // in which case the type of the target is used. + // If both are specified, the type MUST be assignment compatible with the type of the injection target. + try { + String type = processResourceType(resource, false); + if (type != null) { + resource.setType(type); + } else { + log.warn(sm.getString("namingResources.indeterminateResourceType", resource)); + return; + } + } catch (IllegalArgumentException e) { + log.warn(sm.getString("namingResources.indeterminateResourceType", resource), e); + return; + } entries.put(resource.getName(), resource.getType()); } @@ -907,7 +972,165 @@ } + /** + * Determines the resource type. + * + * @param resourceBase the resource + * @param exactMatch true if the resource type must match the type of the injection target, + * false if the type must be assignment compatible with the type of the injection target + * @return resource type + */ + private String processResourceType(ResourceBase resourceBase, boolean exactMatch) { + String type = resourceBase.getType(); + if (!(container instanceof Context)) { + return type; + } + Context context = (Context) container; + + if (context.getLoader() == null || context.getLoader().getClassLoader() == null) { + return type; + } + + Class typeClass = null; + if (type != null) { + typeClass = WebAnnotationSet.loadClass(context, type); + if (typeClass == null) { + // Cannot load the class + return type; + } + } + + Class injectionTargetType = getInjectionTargetType(context, resourceBase.getInjectionTargets(), resourceBase); + + if (typeClass != null) { + if (injectionTargetType != null) { + boolean typeMatches = (exactMatch) ? typeClass.equals(injectionTargetType) : injectionTargetType.isAssignableFrom(typeClass); + if(!typeMatches) { + throw new IllegalArgumentException("Resource " + resourceBase + " cannot be processed. " + + "Resource type '" + typeClass.getCanonicalName() + "' must match the type or assignment " + + "compatible with the type '" + injectionTargetType.getCanonicalName() + "' of the injection target."); + } + } + } else if (injectionTargetType != null) { + type = injectionTargetType.getCanonicalName(); + } else { + throw new IllegalArgumentException("Resource " + resourceBase + " cannot be processed. " + + "The resource type and the injection target are missing."); + } + + return type; + } + + /** + * An injection-target specifies a class and a name within that class into which a resource should be injected. + * The injection-target-class specifies the fully qualified class name that is the target of the injection. + * The injection-target-name specifies the target within the specified class. + * The target is first looked for as a JavaBean property name. If not found, the target is looked for as a field name. + * + * @param context context that will be use for loading injection target classes + * @param injectionTargets list with all injection targets specified for the resource. + * @param resourceBase the resource + * + * @return the type of the specified injection-target-name + */ + private Class getInjectionTargetType(Context context, List injectionTargets, ResourceBase resourceBase) { + Class type = null; + + if (injectionTargets != null && injectionTargets.size() > 0) { + for (InjectionTarget injectionTarget : injectionTargets) { + Class clazz = WebAnnotationSet.loadClass(context, injectionTarget.getTargetClass()); + if (clazz == null) { + continue; + } + + Class propertyType = getPropertyType(injectionTarget, clazz); + if (propertyType != null) { + propertyType = convertPrimitiveType(propertyType); + if (type == null) { + type = propertyType; + } else if (!type.equals(propertyType)) { + // The Servlet Specification does not describe + // how servlet container should handle multiple injection targets with different types + throw new IllegalArgumentException("Resource " + resourceBase + " cannot be processed. " + + "Resource type amongst injections targets is not the same."); + } + continue; + } + + Class fieldType = getFieldType(injectionTarget, clazz); + if (fieldType != null) { + fieldType = convertPrimitiveType(fieldType); + if (type == null) { + type = fieldType; + } else if (!type.equals(fieldType)) { + // The Servlet Specification does not describe + // how servlet container should handle multiple injection targets with different types + throw new IllegalArgumentException("Resource " + resourceBase + " cannot be processed. " + + "Resource type amongst injections targets is not the same."); + } + continue; + } + } + } + + return type; + } + + private Class getPropertyType(InjectionTarget injectionTarget, Class clazz) { + Method[] methods = WebAnnotationSet.getDeclaredMethods(clazz); + if (methods != null && methods.length > 0) { + for (Method method : methods) { + String methodName = method.getName(); + if (methodName.startsWith("set") + && methodName.length() > 3 + && method.getParameterTypes().length == 1 + && method.getReturnType().getName().equals("void")) { + String fieldName = DefaultInstanceManager.getName(method); + if (injectionTarget.getTargetName().equals(fieldName)) { + return (method.getParameterTypes()[0]); + } + } + } + } + return null; + } + + private Class getFieldType(InjectionTarget injectionTarget, Class clazz) { + Field[] fields = WebAnnotationSet.getDeclaredFields(clazz); + if (fields != null && fields.length > 0) { + for (Field field : fields) { + if (injectionTarget.getTargetName().equals(field.getName())) { + return field.getType(); + } + } + } + return null; + } + + private Class convertPrimitiveType(Class clazz) { + Class convertedClass = clazz; + if (clazz.equals(char.class)) { + convertedClass = Character.class; + } else if (clazz.equals(int.class)) { + convertedClass = Integer.class; + } else if (clazz.equals(boolean.class)) { + convertedClass = Boolean.class; + } else if (clazz.equals(double.class)) { + convertedClass = Double.class; + } else if (clazz.equals(byte.class)) { + convertedClass = Byte.class; + } else if (clazz.equals(short.class)) { + convertedClass = Short.class; + } else if (clazz.equals(long.class)) { + convertedClass = Long.class; + } else if (clazz.equals(float.class)) { + convertedClass = Float.class; + } + + return convertedClass; + } + // ------------------------------------------------------- Lifecycle methods @Override Index: java/org/apache/catalina/startup/WebAnnotationSet.java =================================================================== --- java/org/apache/catalina/startup/WebAnnotationSet.java (revision 1344406) +++ java/org/apache/catalina/startup/WebAnnotationSet.java (working copy) @@ -437,7 +437,7 @@ } - private static Field[] getDeclaredFields(Class classClass) { + public static Field[] getDeclaredFields(Class classClass) { Field[] fields = null; if (Globals.IS_SECURITY_ENABLED) { final Class clazz = classClass; @@ -455,7 +455,7 @@ } - private static Method[] getDeclaredMethods(Class classClass) { + public static Method[] getDeclaredMethods(Class classClass) { Method[] methods = null; if (Globals.IS_SECURITY_ENABLED) { final Class clazz = classClass; @@ -473,7 +473,7 @@ } - private static Class loadClass(Context context, String fileString) { + public static Class loadClass(Context context, String fileString) { ClassLoader classLoader = context.getLoader().getClassLoader(); Class classClass = null; try {