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.persistence.PersistenceUnit; 027 import javax.persistence.PersistenceUnits; 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.geronimo.xbeans.javaee.DescriptionType; 033 import org.apache.geronimo.xbeans.javaee.FullyQualifiedClassType; 034 import org.apache.geronimo.xbeans.javaee.InjectionTargetType; 035 import org.apache.geronimo.xbeans.javaee.JndiNameType; 036 import org.apache.geronimo.xbeans.javaee.PersistenceUnitRefType; 037 import org.apache.geronimo.xbeans.javaee.XsdAnyURIType; 038 import org.apache.xbean.finder.ClassFinder; 039 040 041 /** 042 * Static helper class used to encapsulate all the functions related to the translation of 043 * <strong>@PersistenceUnit</strong> and <strong>@PersistenceUnits</strong> annotations to deployment 044 * descriptor tags. The PersistenceUnitAnnotationHelper class can be used as part of the deployment of 045 * a module into the Geronimo server. It performs the following major functions: 046 * <p/> 047 * <ol> 048 * <li>Translates annotations into corresponding deployment descriptor elements (so that the 049 * actual deployment descriptor in the module can be updated or even created if necessary) 050 * </ol> 051 * <p/> 052 * <p><strong>Note(s):</strong> 053 * <ul> 054 * <li>The user is responsible for invoking change to metadata-complete 055 * <li>This helper class will validate any changes it makes to the deployment descriptor. An 056 * exception will be thrown if it fails to parse 057 * </ul> 058 * <p/> 059 * <p><strong>Remaining ToDo(s):</strong> 060 * <ul> 061 * <li>None 062 * </ul> 063 * 064 * @version $Rev $Date 065 * @since 04-2007 066 */ 067 public final class PersistenceUnitAnnotationHelper extends AnnotationHelper { 068 069 // Private instance variables 070 private static final Log log = LogFactory.getLog(PersistenceUnitAnnotationHelper.class); 071 072 // Private constructor to prevent instantiation 073 private PersistenceUnitAnnotationHelper() { 074 } 075 076 /** 077 * Update the deployment descriptor from the PersistenceUnit and PersistenceUnits annotations 078 * 079 * @param annotatedApp Access to the spec dd 080 * @param classFinder Access to the classes of interest 081 * @throws DeploymentException if parsing or validation error 082 */ 083 public static void processAnnotations(AnnotatedApp annotatedApp, ClassFinder classFinder) throws DeploymentException { 084 if (annotatedApp != null) { 085 if (classFinder.isAnnotationPresent(PersistenceUnits.class)) { 086 processPersistenceUnits(annotatedApp, classFinder); 087 } 088 if (classFinder.isAnnotationPresent(PersistenceUnit.class)) { 089 processPersistenceUnit(annotatedApp, classFinder); 090 } 091 } 092 } 093 094 095 /** 096 * Process annotations 097 * 098 * @param annotatedApp Access to the spec dd 099 * @param classFinder Access to the classes of interest 100 * @throws DeploymentException if parsing or validation error 101 */ 102 private static void processPersistenceUnit(AnnotatedApp annotatedApp, ClassFinder classFinder) throws DeploymentException { 103 log.debug("processPersistenceUnit(): Entry: AnnotatedApp: " + annotatedApp.toString()); 104 105 List<Class> classeswithPersistenceUnit = classFinder.findAnnotatedClasses(PersistenceUnit.class); 106 List<Method> methodswithPersistenceUnit = classFinder.findAnnotatedMethods(PersistenceUnit.class); 107 List<Field> fieldswithPersistenceUnit = classFinder.findAnnotatedFields(PersistenceUnit.class); 108 109 // Class-level annotation 110 for (Class cls : classeswithPersistenceUnit) { 111 PersistenceUnit persistenceUnit = (PersistenceUnit) cls.getAnnotation(PersistenceUnit.class); 112 if (persistenceUnit != null) { 113 addPersistenceUnit(annotatedApp, persistenceUnit, cls, null, null); 114 } 115 } 116 117 // Method-level annotation 118 for (Method method : methodswithPersistenceUnit) { 119 PersistenceUnit persistenceUnit = method.getAnnotation(PersistenceUnit.class); 120 if (persistenceUnit != null) { 121 addPersistenceUnit(annotatedApp, persistenceUnit, null, method, null); 122 } 123 } 124 125 // Field-level annotation 126 for (Field field : fieldswithPersistenceUnit) { 127 PersistenceUnit persistenceUnit = field.getAnnotation(PersistenceUnit.class); 128 if (persistenceUnit != null) { 129 addPersistenceUnit(annotatedApp, persistenceUnit, null, null, field); 130 } 131 } 132 133 // Validate deployment descriptor to ensure it's still okay 134 validateDD(annotatedApp); 135 136 log.debug("processPersistenceUnit(): Exit: AnnotatedApp: " + annotatedApp.toString()); 137 } 138 139 140 /** 141 * Process multiple annotations 142 * 143 * @param annotatedApp Access to the spec dd 144 * @param classFinder Access to the classes of interest 145 * @throws DeploymentException if parsing or validation error 146 */ 147 private static void processPersistenceUnits(AnnotatedApp annotatedApp, ClassFinder classFinder) throws DeploymentException { 148 log.debug("processPersistenceUnits(): Entry"); 149 150 List<Class> classeswithPersistenceUnits = classFinder.findAnnotatedClasses(PersistenceUnits.class); 151 152 // Class-level annotation(s) 153 List<PersistenceUnit> persistenceUnitList = new ArrayList<PersistenceUnit>(); 154 for (Class cls : classeswithPersistenceUnits) { 155 PersistenceUnits persistenceUnits = (PersistenceUnits) cls.getAnnotation(PersistenceUnits.class); 156 if (persistenceUnits != null) { 157 persistenceUnitList.addAll(Arrays.asList(persistenceUnits.value())); 158 } 159 for (PersistenceUnit persistenceUnit : persistenceUnitList) { 160 addPersistenceUnit(annotatedApp, persistenceUnit, cls, null, null); 161 } 162 persistenceUnitList.clear(); 163 } 164 165 log.debug("processPersistenceUnits(): Exit"); 166 } 167 168 169 /** 170 * Add @PersistenceUnit and @PersistenceUnits annotations to the deployment descriptor. XMLBeans are used to 171 * read and manipulate the deployment descriptor as necessary. The PersistenceUnit annotation(s) will be 172 * converted to one of the following deployment descriptors: 173 * 174 * <ol> 175 * <li><persistence-unit-ref> -- Describes a single entity manager factory reference for the 176 * persistence unit 177 * </ol> 178 * 179 * <p><strong>Note(s):</strong> 180 * <ul> 181 * <li>The deployment descriptor is the authoritative source so this method ensures that 182 * existing elements in it are not overwritten by annoations 183 * </ul> 184 * 185 * @param annotation @PersistenceUnit annotation 186 * @param cls Class name with the @PersistenceUnit annoation 187 * @param method Method name with the @PersistenceUnit annoation 188 * @param field Field name with the @PersistenceUnit annoation 189 * @param annotatedApp Access to the specc dd 190 */ 191 private static void addPersistenceUnit(AnnotatedApp annotatedApp, PersistenceUnit annotation, Class cls, Method method, Field field) { 192 log.debug("addPersistenceUnit( [annotatedApp] " + annotatedApp.toString() + "," + '\n' + 193 "[annotation] " + annotation.toString() + "," + '\n' + 194 "[cls] " + (cls != null ? cls.getName() : null) + "," + '\n' + 195 "[method] " + (method != null ? method.getName() : null) + "," + '\n' + 196 "[field] " + (field != null ? field.getName() : null) + " ): Entry"); 197 198 //------------------------------------------------------------------------------------------ 199 // PersistenceUnitRef name: 200 // -- When annotation is applied on a class: Name must be provided (cannot be inferred) 201 // -- When annotation is applied on a method: Name is JavaBeans property name qualified 202 // by the class (or as provided on the 203 // annotation) 204 // -- When annotation is applied on a field: Name is the field name qualified by the 205 // class (or as provided on the annotation) 206 //------------------------------------------------------------------------------------------ 207 String persistenceUnitRefName = annotation.name(); 208 if (persistenceUnitRefName.equals("")) { 209 if (method != null) { 210 StringBuilder stringBuilder = new StringBuilder(method.getName().substring(3)); 211 stringBuilder.setCharAt(0, Character.toLowerCase(stringBuilder.charAt(0))); 212 persistenceUnitRefName = method.getDeclaringClass().getName() + "/" + stringBuilder.toString(); 213 } else if (field != null) { 214 persistenceUnitRefName = field.getDeclaringClass().getName() + "/" + field.getName(); 215 } 216 } 217 log.debug("addPersistenceUnit(): persistenceUnitRefName: " + persistenceUnitRefName); 218 219 // If there is already xml for the persistence unit ref, just add injection targets and return. 220 PersistenceUnitRefType[] persistenceUnitRefs = annotatedApp.getPersistenceUnitRefArray(); 221 for (PersistenceUnitRefType persistenceUnitRef : persistenceUnitRefs) { 222 if (persistenceUnitRef.getPersistenceUnitRefName().getStringValue().trim().equals(persistenceUnitRefName)) { 223 if (method != null || field != null) { 224 InjectionTargetType[] targets = persistenceUnitRef.getInjectionTargetArray(); 225 if (!hasTarget(method, field, targets)) { 226 configureInjectionTarget(persistenceUnitRef.addNewInjectionTarget(), method, field); 227 } 228 } 229 return; 230 } 231 } 232 233 // Doesn't exist in deployment descriptor -- add new 234 PersistenceUnitRefType persistenceUnitRef = annotatedApp.addNewPersistenceUnitRef(); 235 236 //------------------------------------------------------------------------------ 237 // <persistence-unit-ref> required elements: 238 //------------------------------------------------------------------------------ 239 240 // persistence-unit-ref-name 241 JndiNameType unitRefName = persistenceUnitRef.addNewPersistenceUnitRefName(); 242 unitRefName.setStringValue(persistenceUnitRefName); 243 244 //------------------------------------------------------------------------------ 245 // <persistence-unit-ref> optional elements: 246 //------------------------------------------------------------------------------ 247 248 // persistence-unit-name 249 String unitNameAnnotation = annotation.unitName(); 250 if (!unitNameAnnotation.equals("")) { 251 org.apache.geronimo.xbeans.javaee.String persistenceUnitName = persistenceUnitRef.addNewPersistenceUnitName(); 252 persistenceUnitName.setStringValue(unitNameAnnotation); 253 } 254 255 // injection targets 256 if (method != null || field != null) { 257 configureInjectionTarget(persistenceUnitRef.addNewInjectionTarget(), method, field); 258 } 259 260 } 261 262 }