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 }