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