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