001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     *  Unless required by applicable law or agreed to in writing, software
012     *  distributed under the License is distributed on an "AS IS" BASIS,
013     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     *  See the License for the specific language governing permissions and
015     *  limitations under the License.
016     */
017    
018    package org.apache.geronimo.j2ee.deployment.annotation;
019    
020    import java.lang.reflect.Field;
021    import java.lang.reflect.Method;
022    import java.util.ArrayList;
023    import java.util.Arrays;
024    import java.util.List;
025    
026    import javax.annotation.Resource;
027    import javax.annotation.Resources;
028    
029    import org.apache.commons.logging.Log;
030    import org.apache.commons.logging.LogFactory;
031    import org.apache.geronimo.common.DeploymentException;
032    import org.apache.xbean.finder.ClassFinder;
033    
034    
035    /**
036     * Static helper class used to encapsulate all the functions related to the translation of
037     * <strong>@Resource</strong> and <strong>@Resources</strong> annotations to deployment descriptor
038     * tags. The ResourceAnnotationHelper class can be used as part of the deployment of a module into
039     * the Geronimo server. It performs the following major functions:
040     * <p/>
041     * <ol>
042     * <li>Translates annotations into corresponding deployment descriptor elements (so that the
043     * actual deployment descriptor in the module can be updated or even created if necessary)
044     * </ol>
045     * <p/>
046     * <p><strong>Note(s):</strong>
047     * <ul>
048     * <li>The user is responsible for invoking change to metadata-complete
049     * <li>This helper class will validate any changes it makes to the deployment descriptor. An
050     * exception will be thrown if it fails to parse
051     * </ul>
052     * <p/>
053     * <p><strong>Remaining ToDo(s):</strong>
054     * <ul>
055     * <li>Usage of mappedName
056     * </ul>
057     *
058     * @version $Rev: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $
059     * @since 02-2007
060     */
061    public final class ResourceAnnotationHelper extends AnnotationHelper {
062    
063        // Private instance variables
064        private static final Log log = LogFactory.getLog(ResourceAnnotationHelper.class);
065    
066        // Private constructor to prevent instantiation
067        private ResourceAnnotationHelper() {
068        }
069    
070        /**
071         * Update the deployment descriptor from Resource and Resources annotations
072         * @throws Exception if parsing or validation error
073         */
074        public static void processAnnotations(AnnotatedApp annotatedApp, ClassFinder classFinder, ResourceProcessor resourceProcessor) throws Exception {
075            if (annotatedApp != null) {
076                if (classFinder.isAnnotationPresent(Resources.class)) {
077                    processResources(annotatedApp, classFinder, resourceProcessor);
078                }
079                if (classFinder.isAnnotationPresent(Resource.class)) {
080                    processResource(annotatedApp, classFinder, resourceProcessor);
081                }
082            }
083        }
084    
085    
086        /**
087         * Process annotations
088         *
089         * @param annotatedApp
090         * @param classFinder
091         * @param resourceProcessor
092         * @throws Exception
093         */
094        private static void processResource(AnnotatedApp annotatedApp, ClassFinder classFinder, ResourceProcessor resourceProcessor) throws Exception {
095            log.debug("processResource(): Entry: AnnotatedApp: " + annotatedApp.toString());
096    
097            List<Class> classeswithResource = classFinder.findAnnotatedClasses(Resource.class);
098            List<Method> methodswithResource = classFinder.findAnnotatedMethods(Resource.class);
099            List<Field> fieldswithResource = classFinder.findAnnotatedFields(Resource.class);
100    
101            // Class-level annotation
102            for (Class cls : classeswithResource) {
103                Resource resource = (Resource) cls.getAnnotation(Resource.class);
104                if (resource != null) {
105                    resourceProcessor.processResource(annotatedApp, resource, cls, null, null);
106                }
107            }
108    
109            // Method-level annotation
110            for (Method method : methodswithResource) {
111                Resource resource = (Resource) method.getAnnotation(Resource.class);
112                if (resource != null) {
113                    resourceProcessor.processResource(annotatedApp, resource, null, method, null);
114                }
115            }
116    
117            // Field-level annotation
118            for (Field field : fieldswithResource) {
119                Resource resource = (Resource) field.getAnnotation(Resource.class);
120                if (resource != null) {
121                    resourceProcessor.processResource(annotatedApp, resource, null, null, field);
122                }
123            }
124    
125            // Validate deployment descriptor to ensure it's still okay
126            validateDD(annotatedApp);
127    
128            log.debug("processResource(): Exit: AnnotatedApp: " + annotatedApp.toString());
129        }
130    
131    
132        /**
133         * Process multiple annotations
134         *
135         * @param annotatedApp
136         * @param classFinder
137         * @param resourceProcessor
138         * @throws Exception
139         */
140        private static void processResources(AnnotatedApp annotatedApp, ClassFinder classFinder, ResourceProcessor resourceProcessor) throws Exception {
141            log.debug("processResources(): Entry");
142    
143            List<Class> classeswithResources = classFinder.findAnnotatedClasses(Resources.class);
144    
145            // Class-level annotation(s)
146            List<Resource> resourceList = new ArrayList<Resource>();
147            for (Class cls : classeswithResources) {
148                Resources resources = (Resources) cls.getAnnotation(Resources.class);
149                if (resources != null) {
150                    resourceList.addAll(Arrays.asList(resources.value()));
151                }
152                for (Resource resource : resourceList) {
153                    resourceProcessor.processResource(annotatedApp, resource, cls, null, null);
154                }
155                resourceList.clear();
156            }
157    
158            log.debug("processResources(): Exit");
159        }
160    
161        public abstract static class ResourceProcessor extends AnnotationHelper {
162    
163            public abstract boolean processResource(AnnotatedApp annotatedApp, Resource annotation, Class cls, Method method, Field field) throws DeploymentException;
164    
165            /**
166             * Resource name:
167             * -- When annotation is applied on a class:    Name must be provided (cannot be inferred)
168             * -- When annotation is applied on a method:   Name is JavaBeans property name qualified
169             * by the class (or as provided on the annotation)
170             * -- When annotation is applied on a field:    Name is the field name qualified by the
171             * class (or as provided on the annotation)
172             *
173             * @param annotation
174             * @param method
175             * @param field
176             * @return
177             */
178            protected static String getResourceName(Resource annotation, Method method, Field field) {
179                String resourceName = annotation.name();
180                if (resourceName.equals("")) {
181                    if (method != null) {
182                        StringBuilder stringBuilder = new StringBuilder(method.getName().substring(3));
183                        stringBuilder.setCharAt(0, Character.toLowerCase(stringBuilder.charAt(0)));
184                        resourceName = method.getDeclaringClass().getName() + "/" + stringBuilder.toString();
185                    } else if (field != null) {
186                        resourceName = field.getDeclaringClass().getName() + "/" + field.getName();
187                    }
188                }
189                return resourceName;
190            }
191    
192            protected static String getResourceType(Resource annotation, Method method, Field field) {
193                //------------------------------------------------------------------------------------------
194                // Resource type:
195                // -- When annotation is applied on a class:    Type must be provided (cannot be inferred)
196                // -- When annotation is applied on a method:   Type is the JavaBeans property type (or as
197                //                                              provided on the annotation)
198                // -- When annotation is applied on a field:    Type is the field type (or as provided on
199                //                                              the annotation)
200                //------------------------------------------------------------------------------------------
201                String resourceType = annotation.type().getCanonicalName();
202                if (resourceType.equals("") || resourceType.equals(Object.class.getName())) {
203                    if (method != null) {
204                        resourceType = method.getParameterTypes()[0].getCanonicalName();
205                    } else if (field != null) {
206                        resourceType = field.getType().getName();
207                    }
208                }
209                return resourceType;
210            }
211        }
212    
213    }