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.ejb.EJB;
025: import javax.ejb.EJBHome;
026: import javax.ejb.EJBLocalHome;
027: import javax.ejb.EJBs;
028: import javax.ejb.Local;
029: import javax.ejb.Remote;
030:
031: import org.apache.commons.logging.Log;
032: import org.apache.commons.logging.LogFactory;
033: import org.apache.geronimo.deployment.xmlbeans.XmlBeansUtil;
034: import org.apache.geronimo.xbeans.javaee.DescriptionType;
035: import org.apache.geronimo.xbeans.javaee.EjbLinkType;
036: import org.apache.geronimo.xbeans.javaee.EjbLocalRefType;
037: import org.apache.geronimo.xbeans.javaee.EjbRefNameType;
038: import org.apache.geronimo.xbeans.javaee.EjbRefType;
039: import org.apache.geronimo.xbeans.javaee.FullyQualifiedClassType;
040: import org.apache.geronimo.xbeans.javaee.InjectionTargetType;
041: import org.apache.geronimo.xbeans.javaee.JavaIdentifierType;
042: import org.apache.geronimo.xbeans.javaee.LocalType;
043: import org.apache.geronimo.xbeans.javaee.RemoteType;
044: import org.apache.geronimo.xbeans.javaee.XsdStringType;
045: import org.apache.xbean.finder.ClassFinder;
046:
047: /**
048: * Static helper class used to encapsulate all the functions related to the translation of
049: * <strong>@EJB</strong> and <strong>@EJBs</strong> annotations to deployment descriptor tags. The
050: * EJBAnnotationHelper class can be used as part of the deployment of a module into the Geronimo
051: * server. It performs the following major functions:
052: *
053: * <ol>
054: * <li>Translates annotations into corresponding deployment descriptor elements (so that the
055: * actual deployment descriptor in the module can be updated or even created if necessary)
056: * </ol>
057: *
058: * <p><strong>Note(s):</strong>
059: * <ul>
060: * <li>The user is responsible for invoking change to metadata-complete
061: * <li>This helper class will validate any changes it makes to the deployment descriptor. An
062: * exception will be thrown if it fails to parse
063: * </ul>
064: *
065: * <p><strong>Remaining ToDo(s):</strong>
066: * <ul>
067: * <li>Usage of mappedName
068: * </ul>
069: *
070: * @version $Rev: 534273 $ $Date: 2007-05-01 16:22:35 -0700 (Tue, 01 May 2007) $
071: * @since 02-2007
072: */
073:
074: public final class EJBAnnotationHelper {
075:
076: // Private instance variables
077: private static final Log log = LogFactory
078: .getLog(EJBAnnotationHelper.class);
079:
080: // Private constructor to prevent instantiation
081: private EJBAnnotationHelper() {
082: }
083:
084: /**
085: * Determine if there are any annotations present
086: *
087: * @return true or false
088: */
089: public static boolean annotationsPresent(ClassFinder classFinder) {
090: if (classFinder.isAnnotationPresent(EJB.class))
091: return true;
092: if (classFinder.isAnnotationPresent(EJBs.class))
093: return true;
094: return false;
095: }
096:
097: /**
098: * Process the annotations
099: *
100: * @return Updated deployment descriptor
101: * @throws Exception if parsing or validation error
102: */
103: public static void processAnnotations(AnnotatedApp annotatedApp,
104: ClassFinder classFinder) throws Exception {
105: if (annotatedApp != null) {
106: processEJBs(annotatedApp, classFinder);
107: processEJB(annotatedApp, classFinder);
108: }
109: }
110:
111: /**
112: * Process annotations
113: *
114: * @param annotatedApp
115: * @param classFinder
116: * @throws Exception
117: */
118: private static void processEJB(AnnotatedApp annotatedApp,
119: ClassFinder classFinder) throws Exception {
120: log.debug("processEJB(): Entry: AnnotatedApp: "
121: + annotatedApp.toString());
122:
123: List<Class> classesWithEJB = classFinder
124: .findAnnotatedClasses(EJB.class);
125: List<Method> methodsWithEJB = classFinder
126: .findAnnotatedMethods(EJB.class);
127: List<Field> fieldsWithEJB = classFinder
128: .findAnnotatedFields(EJB.class);
129:
130: // Class-level annotation
131: for (Class cls : classesWithEJB) {
132: EJB ejb = (EJB) cls.getAnnotation(EJB.class);
133: if (ejb != null) {
134: addEJB(annotatedApp, ejb, cls, null, null);
135: }
136: }
137:
138: // Method-level annotation
139: for (Method method : methodsWithEJB) {
140: EJB ejb = (EJB) method.getAnnotation(EJB.class);
141: if (ejb != null) {
142: addEJB(annotatedApp, ejb, null, method, null);
143: }
144: }
145:
146: // Field-level annotation
147: for (Field field : fieldsWithEJB) {
148: EJB ejb = (EJB) field.getAnnotation(EJB.class);
149: if (ejb != null) {
150: addEJB(annotatedApp, ejb, null, null, field);
151: }
152: }
153:
154: // Validate deployment descriptor to ensure it's still okay
155: validateDD(annotatedApp);
156:
157: log.debug("processEJB(): Exit: AnnotatedApp: "
158: + annotatedApp.toString());
159: }
160:
161: /**
162: * Process multiple annotations
163: *
164: * @param annotatedApp
165: * @param classFinder
166: * @throws Exception
167: */
168: private static void processEJBs(AnnotatedApp annotatedApp,
169: ClassFinder classFinder) throws Exception {
170: log.debug("processEJBs(): Entry");
171:
172: List<Class> classesWithEJBs = classFinder
173: .findAnnotatedClasses(EJBs.class);
174:
175: // Class-level annotation(s)
176: List<EJB> ejbList = new ArrayList<EJB>();
177: for (Class cls : classesWithEJBs) {
178: EJBs ejbs = (EJBs) cls.getAnnotation(EJBs.class);
179: if (ejbs != null) {
180: ejbList.addAll(Arrays.asList(ejbs.value()));
181: }
182: for (EJB ejb : ejbList) {
183: addEJB(annotatedApp, ejb, cls, null, null);
184: }
185: ejbList.clear();
186: }
187:
188: log.debug("processEJBs(): Exit");
189: }
190:
191: /**
192: * Add @EJB and @EJBs annotations to the deployment descriptor. XMLBeans are used to read and
193: * manipulate the deployment descriptor as necessary. The EJB annotation(s) will be converted to
194: * one of the following deployment descriptors if possible. Otherwise they will be listed as
195: * ambiguous and resolved in OpenEJB.
196: * <p/>
197: * <ol>
198: * <li><ejb-ref>
199: * <li><ejb-local-ref>
200: * </ol>
201: * <p/>
202: * <p><strong>Note(s):</strong>
203: * <ul>
204: * <li>The deployment descriptor is the authoritative source so this method ensures that
205: * existing elements in it are not overwritten by annoations
206: * </ul>
207: *
208: * @param annotatedApp
209: * @param annotation @EJB annotation
210: * @param cls Class name with the @EJB annoation
211: * @param method Method name with the @EJB annoation
212: * @param field Field name with the @EJB annoation
213: */
214: private static void addEJB(AnnotatedApp annotatedApp,
215: EJB annotation, Class cls, Method method, Field field) {
216: log.debug("addEJB( [annotatedApp] " + annotatedApp.toString()
217: + "," + '\n' + "[annotation] " + annotation.toString()
218: + "," + '\n' + "[cls] "
219: + (cls != null ? cls.getName() : null) + "," + '\n'
220: + "[method] "
221: + (method != null ? method.getName() : null) + ","
222: + '\n' + "[field] "
223: + (field != null ? field.getName() : null)
224: + " ): Entry");
225:
226: // First determine if the interface is "Local" or "Remote" (if we can--we may not be able to)
227: boolean localFlag = false;
228: boolean remoteFlag = false;
229: Class interfce = annotation.beanInterface();
230: if (interfce.equals(Object.class)) {
231: if (method != null) {
232: interfce = method.getParameterTypes()[0];
233: } else if (field != null) {
234: interfce = field.getType();
235: } else {
236: interfce = null;
237: }
238: }
239: log.debug("addEJB(): interfce: " + interfce);
240:
241: // Just in case local and/or remote homes are still being implemented (even though
242: // they are optional in EJB 3.0)
243: if (interfce != null && !interfce.equals(Object.class)) {
244: if (EJBHome.class.isAssignableFrom(interfce)) {
245: for (Method m : interfce.getMethods()) {
246: if (m.getName().startsWith("create")) {
247: interfce = m.getReturnType();
248: break;
249: }
250: }
251: remoteFlag = true;
252: } else if (EJBLocalHome.class.isAssignableFrom(interfce)) {
253: for (Method m : interfce.getMethods()) {
254: if (m.getName().startsWith("create")) {
255: interfce = m.getReturnType();
256: break;
257: }
258: }
259: localFlag = true;
260: } else {
261: if (interfce.getAnnotation(Local.class) != null) {
262: localFlag = true;
263: } else if (interfce.getAnnotation(Remote.class) != null) {
264: remoteFlag = true;
265: }
266: }
267: }
268: log.debug("addEJB(): localFlag: " + localFlag);
269: log.debug("addEJB(): remoteFlag: " + remoteFlag);
270:
271: //------------------------------------------------------------------------------------------
272: // 1. <ejb-local-ref>
273: //------------------------------------------------------------------------------------------
274: if (localFlag) {
275:
276: log.debug("addEJB(): <ejb-local-ref> found");
277:
278: String localRefName = annotation.name();
279: if (localRefName.equals("")) {
280: if (method != null) {
281: localRefName = method.getDeclaringClass().getName()
282: + "/" + method.getName().substring(3); // method should start with "set"
283: } else if (field != null) {
284: localRefName = field.getDeclaringClass().getName()
285: + "/" + field.getName();
286: }
287: }
288:
289: boolean exists = false;
290: EjbLocalRefType[] ejbLocalRefEntries = annotatedApp
291: .getEjbLocalRefArray();
292: for (EjbLocalRefType ejbLocalRefEntry : ejbLocalRefEntries) {
293: if (ejbLocalRefEntry.getEjbRefName().getStringValue()
294: .trim().equals(localRefName)) {
295: exists = true;
296: break;
297: }
298: }
299: if (!exists) {
300: try {
301:
302: log.debug("addEJB(): Does not exist in DD: "
303: + localRefName);
304:
305: // Doesn't exist in deployment descriptor -- add new
306: EjbLocalRefType ejbLocalRef = annotatedApp
307: .addNewEjbLocalRef();
308:
309: //------------------------------------------------------------------------------
310: // <ejb-local-ref> required elements:
311: //------------------------------------------------------------------------------
312:
313: // ejb-ref-name
314: EjbRefNameType ejbRefName = ejbLocalRef
315: .addNewEjbRefName();
316: ejbRefName.setStringValue(localRefName);
317: ejbLocalRef.setEjbRefName(ejbRefName);
318:
319: //------------------------------------------------------------------------------
320: // <ejb-local-ref> optional elements:
321: //------------------------------------------------------------------------------
322:
323: // local
324: if (interfce != null) {
325: String localAnnotation = interfce.getName();
326: if (!localAnnotation.equals("")) {
327: LocalType local = ejbLocalRef.addNewLocal();
328: local.setStringValue(localAnnotation);
329: ejbLocalRef.setLocal(local);
330: }
331: }
332:
333: // ejb-link
334: String beanName = annotation.beanName();
335: if (!beanName.equals("")) {
336: EjbLinkType ejbLink = ejbLocalRef
337: .addNewEjbLink();
338: ejbLink.setStringValue(beanName);
339: ejbLocalRef.setEjbLink(ejbLink);
340: }
341:
342: // mappedName
343: String mappdedNameAnnotation = annotation
344: .mappedName();
345: if (!mappdedNameAnnotation.equals("")) {
346: XsdStringType mappedName = ejbLocalRef
347: .addNewMappedName();
348: mappedName
349: .setStringValue(mappdedNameAnnotation);
350: ejbLocalRef.setMappedName(mappedName);
351: }
352:
353: // description
354: String descriptionAnnotation = annotation
355: .description();
356: if (!descriptionAnnotation.equals("")) {
357: DescriptionType description = ejbLocalRef
358: .addNewDescription();
359: description
360: .setStringValue(descriptionAnnotation);
361: }
362:
363: // injectionTarget
364: if (method != null || field != null) { // No class-level injection
365: InjectionTargetType injectionTarget = ejbLocalRef
366: .addNewInjectionTarget();
367: FullyQualifiedClassType qualifiedClass = injectionTarget
368: .addNewInjectionTargetClass();
369: JavaIdentifierType javaType = injectionTarget
370: .addNewInjectionTargetName();
371: if (method != null) {
372: qualifiedClass.setStringValue(method
373: .getDeclaringClass().getName());
374: javaType.setStringValue(method.getName()
375: .substring(3)); // method should start with "set"
376: injectionTarget
377: .setInjectionTargetClass(qualifiedClass);
378: injectionTarget
379: .setInjectionTargetName(javaType);
380: } else if (field != null) {
381: qualifiedClass.setStringValue(field
382: .getDeclaringClass().getName());
383: javaType.setStringValue(field.getName());
384: injectionTarget
385: .setInjectionTargetClass(qualifiedClass);
386: injectionTarget
387: .setInjectionTargetName(javaType);
388: }
389: }
390:
391: } catch (Exception anyException) {
392: log
393: .debug("EJBAnnotationHelper: Exception caught while processing <ejb-local-ref>");
394: anyException.printStackTrace();
395: }
396: }
397: } // end if local
398: else if (remoteFlag) { // remote
399:
400: //--------------------------------------------------------------------------------------
401: // 2. <ejb-ref>
402: //--------------------------------------------------------------------------------------
403:
404: log.debug("addEJB(): <ejb-ref> found");
405:
406: String remoteRefName = annotation.name();
407: if (remoteRefName.equals("")) {
408: if (method != null) {
409: remoteRefName = method.getDeclaringClass()
410: .getName()
411: + "/" + method.getName().substring(3); // method should start with "set"
412: } else if (field != null) {
413: remoteRefName = field.getDeclaringClass().getName()
414: + "/" + field.getName();
415: }
416: }
417:
418: boolean exists = false;
419: EjbRefType[] ejbRefEntries = annotatedApp.getEjbRefArray();
420: for (EjbRefType ejbRefEntry : ejbRefEntries) {
421: if (ejbRefEntry.getEjbRefName().getStringValue().trim()
422: .equals(remoteRefName)) {
423: exists = true;
424: break;
425: }
426: }
427: if (!exists) {
428: try {
429:
430: log.debug("addEJB(): Does not exist in DD: "
431: + remoteRefName);
432:
433: // Doesn't exist in deployment descriptor -- add new
434: EjbRefType ejbRef = annotatedApp.addNewEjbRef();
435:
436: //------------------------------------------------------------------------------
437: // <ejb-ref> required elements:
438: //------------------------------------------------------------------------------
439:
440: // ejb-ref-name
441: EjbRefNameType ejbRefName = ejbRef
442: .addNewEjbRefName();
443: ejbRefName.setStringValue(remoteRefName);
444: ejbRef.setEjbRefName(ejbRefName);
445:
446: //------------------------------------------------------------------------------
447: // <ejb-ref> optional elements:
448: //------------------------------------------------------------------------------
449:
450: // remote
451: if (interfce != null) {
452: String remoteAnnotation = interfce.getName();
453: if (!remoteAnnotation.equals("")) {
454: RemoteType remote = ejbRef.addNewRemote();
455: remote.setStringValue(remoteAnnotation);
456: ejbRef.setRemote(remote);
457: }
458: }
459:
460: // ejb-link
461: String beanName = annotation.beanName();
462: if (!beanName.equals("")) {
463: EjbLinkType ejbLink = ejbRef.addNewEjbLink();
464: ejbLink.setStringValue(beanName);
465: ejbRef.setEjbLink(ejbLink);
466: }
467:
468: // mappedName
469: String mappdedNameAnnotation = annotation
470: .mappedName();
471: if (!mappdedNameAnnotation.equals("")) {
472: XsdStringType mappedName = ejbRef
473: .addNewMappedName();
474: mappedName
475: .setStringValue(mappdedNameAnnotation);
476: ejbRef.setMappedName(mappedName);
477: }
478:
479: // description
480: String descriptionAnnotation = annotation
481: .description();
482: if (!descriptionAnnotation.equals("")) {
483: DescriptionType description = ejbRef
484: .addNewDescription();
485: description
486: .setStringValue(descriptionAnnotation);
487: }
488:
489: // injectionTarget
490: if (method != null || field != null) { // No class-level injection
491: InjectionTargetType injectionTarget = ejbRef
492: .addNewInjectionTarget();
493: FullyQualifiedClassType qualifiedClass = injectionTarget
494: .addNewInjectionTargetClass();
495: JavaIdentifierType javaType = injectionTarget
496: .addNewInjectionTargetName();
497: if (method != null) {
498: qualifiedClass.setStringValue(method
499: .getDeclaringClass().getName());
500: javaType.setStringValue(method.getName()
501: .substring(3)); // method should start with "set"
502: injectionTarget
503: .setInjectionTargetClass(qualifiedClass);
504: injectionTarget
505: .setInjectionTargetName(javaType);
506: } else if (field != null) {
507: qualifiedClass.setStringValue(field
508: .getDeclaringClass().getName());
509: javaType.setStringValue(field.getName());
510: injectionTarget
511: .setInjectionTargetClass(qualifiedClass);
512: injectionTarget
513: .setInjectionTargetName(javaType);
514: }
515: }
516:
517: } catch (Exception anyException) {
518: log
519: .debug("EJBAnnotationHelper: Exception caught while processing <ejb-ref>");
520: anyException.printStackTrace();
521: }
522: }
523: } // end if remote
524: else { // ambiguous
525:
526: //--------------------------------------------------------------------------------------
527: // 3. <UNKNOWN>
528: //--------------------------------------------------------------------------------------
529: log.debug("addEJB(): <UNKNOWN> found");
530:
531: String remoteRefName = annotation.name();
532: if (remoteRefName.equals("")) {
533: if (method != null) {
534: remoteRefName = method.getDeclaringClass()
535: .getName()
536: + "/" + method.getName().substring(3); // method should start with "set"
537: } else if (field != null) {
538: remoteRefName = field.getDeclaringClass().getName()
539: + "/" + field.getName();
540: }
541: }
542:
543: boolean exists = false;
544: EjbRefType[] ejbRefEntries = annotatedApp.getEjbRefArray();
545: for (EjbRefType ejbRefEntry : ejbRefEntries) {
546: if (ejbRefEntry.getEjbRefName().getStringValue().trim()
547: .equals(remoteRefName)) {
548: exists = true;
549: break;
550: }
551: }
552: if (!exists) {
553: try {
554:
555: log.debug("addEJB(): Does not exist in DD: "
556: + remoteRefName);
557:
558: // Doesn't exist in deployment descriptor -- add as an <ejb-ref> to the
559: // ambiguous list so that it can be resolved later
560: EjbRefType ejbRef = EjbRefType.Factory
561: .newInstance();
562: annotatedApp.getAmbiguousEjbRefs().add(ejbRef);
563:
564: //------------------------------------------------------------------------------
565: // <ejb-ref> required elements:
566: //------------------------------------------------------------------------------
567:
568: // ejb-ref-name
569: EjbRefNameType ejbRefName = ejbRef
570: .addNewEjbRefName();
571: ejbRefName.setStringValue(remoteRefName);
572: ejbRef.setEjbRefName(ejbRefName);
573:
574: //------------------------------------------------------------------------------
575: // <ejb-ref> optional elements:
576: //------------------------------------------------------------------------------
577:
578: // remote
579: if (interfce != null) {
580: String remoteAnnotation = interfce.getName();
581: if (!remoteAnnotation.equals("")) {
582: RemoteType remote = ejbRef.addNewRemote();
583: remote.setStringValue(remoteAnnotation);
584: ejbRef.setRemote(remote);
585: }
586: }
587:
588: // ejb-link
589: String beanName = annotation.beanName();
590: if (!beanName.equals("")) {
591: EjbLinkType ejbLink = ejbRef.addNewEjbLink();
592: ejbLink.setStringValue(beanName);
593: ejbRef.setEjbLink(ejbLink);
594: }
595:
596: // mappedName
597: String mappdedNameAnnotation = annotation
598: .mappedName();
599: if (!mappdedNameAnnotation.equals("")) {
600: XsdStringType mappedName = ejbRef
601: .addNewMappedName();
602: mappedName
603: .setStringValue(mappdedNameAnnotation);
604: ejbRef.setMappedName(mappedName);
605: }
606:
607: // description
608: String descriptionAnnotation = annotation
609: .description();
610: if (!descriptionAnnotation.equals("")) {
611: DescriptionType description = ejbRef
612: .addNewDescription();
613: description
614: .setStringValue(descriptionAnnotation);
615: }
616:
617: // injectionTarget
618: if (method != null || field != null) { // No class-level injection
619: InjectionTargetType injectionTarget = ejbRef
620: .addNewInjectionTarget();
621: FullyQualifiedClassType qualifiedClass = injectionTarget
622: .addNewInjectionTargetClass();
623: JavaIdentifierType javaType = injectionTarget
624: .addNewInjectionTargetName();
625: if (method != null) {
626: qualifiedClass.setStringValue(method
627: .getDeclaringClass().getName());
628: javaType.setStringValue(method.getName()
629: .substring(3)); // method should start with "set"
630: injectionTarget
631: .setInjectionTargetClass(qualifiedClass);
632: injectionTarget
633: .setInjectionTargetName(javaType);
634: } else if (field != null) {
635: qualifiedClass.setStringValue(field
636: .getDeclaringClass().getName());
637: javaType.setStringValue(field.getName());
638: injectionTarget
639: .setInjectionTargetClass(qualifiedClass);
640: injectionTarget
641: .setInjectionTargetName(javaType);
642: }
643: }
644:
645: } catch (Exception anyException) {
646: log
647: .debug("EJBAnnotationHelper: Exception caught while processing <UNKNOWN>");
648: anyException.printStackTrace();
649: }
650: }
651:
652: }
653: log.debug("addEJB(): Exit");
654: }
655:
656: /**
657: * Validate deployment descriptor
658: *
659: * @param annotatedApp
660: * @throws Exception thrown if deployment descriptor cannot be parsed
661: */
662: private static void validateDD(AnnotatedApp annotatedApp)
663: throws Exception {
664: log.debug("validateDD( " + annotatedApp.toString()
665: + " ): Entry");
666:
667: XmlBeansUtil.parse(annotatedApp.toString());
668:
669: log.debug("validateDD(): Exit");
670: }
671: }
|