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