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 }