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