KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > enterprise > deployment > annotation > handlers > AbstractEjbHandler


1 /*
2  * The contents of this file are subject to the terms
3  * of the Common Development and Distribution License
4  * (the License). You may not use this file except in
5  * compliance with the License.
6  *
7  * You can obtain a copy of the license at
8  * https://glassfish.dev.java.net/public/CDDLv1.0.html or
9  * glassfish/bootstrap/legal/CDDLv1.0.txt.
10  * See the License for the specific language governing
11  * permissions and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL
14  * Header Notice in each file and include the License file
15  * at glassfish/bootstrap/legal/CDDLv1.0.txt.
16  * If applicable, add the following below the CDDL Header,
17  * with the fields enclosed by brackets [] replaced by
18  * you own identifying information:
19  * "Portions Copyrighted [year] [name of copyright owner]"
20  *
21  * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
22  */

23 package com.sun.enterprise.deployment.annotation.handlers;
24
25 import java.lang.annotation.Annotation JavaDoc;
26 import java.lang.reflect.AnnotatedElement JavaDoc;
27 import java.lang.reflect.Method JavaDoc;
28
29 import java.util.Set JavaDoc;
30 import java.util.List JavaDoc;
31 import java.util.LinkedList JavaDoc;
32 import java.util.HashSet JavaDoc;
33 import java.util.logging.Level JavaDoc;
34 import javax.ejb.Timeout JavaDoc;
35 import javax.ejb.EJBHome JavaDoc;
36 import javax.ejb.EJBLocalHome JavaDoc;
37 import javax.ejb.Remote JavaDoc;
38 import javax.ejb.Local JavaDoc;
39 import javax.ejb.RemoteHome JavaDoc;
40 import javax.ejb.LocalHome JavaDoc;
41 import javax.ejb.Stateless JavaDoc;
42
43 import com.sun.enterprise.deployment.EjbBundleDescriptor;
44 import com.sun.enterprise.deployment.EjbDescriptor;
45 import com.sun.enterprise.deployment.EjbSessionDescriptor;
46 import com.sun.enterprise.deployment.MethodDescriptor;
47
48 import com.sun.enterprise.deployment.annotation.AnnotatedElementHandler;
49 import com.sun.enterprise.deployment.annotation.AnnotationInfo;
50 import com.sun.enterprise.deployment.annotation.AnnotationProcessorException;
51 import com.sun.enterprise.deployment.annotation.HandlerProcessingResult;
52 import com.sun.enterprise.deployment.annotation.context.EjbBundleContext;
53 import com.sun.enterprise.deployment.annotation.context.EjbContext;
54
55 /**
56  * This is an abstract class for EJB annotation handler.
57  * Concrete subclass handlers need to implements the following methods:
58  * public Class<? extends Annotation> getAnnotationType();
59  * protected String getAnnotatedName(Annotation annotation );
60  * protected boolean isValidEjbDescriptor(EjbDescriptor ejbDesc);
61  * Annotation annotation);
62  * protected EjbDescriptor createEjbDescriptor(String elementName,
63  * AnnotationInfo ainfo) throws AnnotationProcessorException;
64  * protected HandlerProcessingResult setEjbDescriptorInfo(
65  * EjbDescriptor ejbDesc, AnnotationInfo ainfo)
66  * throws AnnotationProcessorException;
67  *
68  * @author Shing Wai Chan
69  */

70 abstract class AbstractEjbHandler extends AbstractHandler {
71     /**
72      * Return the name attribute of given annotation.
73      * @param annotation
74      * @return name
75      */

76     protected abstract String JavaDoc getAnnotatedName(Annotation JavaDoc annotation);
77
78     /*
79      * check if the given EjbDescriptor matches the given Annotation.
80      * @param ejbDesc
81      * @param annotation
82      * @return boolean check for validity of EjbDescriptor
83      */

84     protected abstract boolean isValidEjbDescriptor(EjbDescriptor ejbDesc,
85             Annotation JavaDoc annotation);
86
87     /**
88      * Create a new EjbDescriptor for a given elementName and AnnotationInfo.
89      * @param elementName
90      * @param ainfo
91      * @return a new EjbDescriptor
92      */

93     protected abstract EjbDescriptor createEjbDescriptor(String JavaDoc elementName,
94             AnnotationInfo ainfo) throws AnnotationProcessorException;
95
96     /**
97      * Set Annotation information to Descriptor.
98      * This method will also be invoked for an existing descriptor with
99      * annotation as user may not specific a complete xml.
100      * @param ejbDesc
101      * @param ainfo
102      * @return HandlerProcessingResult
103      */

104     protected abstract HandlerProcessingResult setEjbDescriptorInfo(
105             EjbDescriptor ejbDesc, AnnotationInfo ainfo)
106             throws AnnotationProcessorException;
107
108     /**
109      * Process a particular annotation which type is the same as the
110      * one returned by @see getAnnotationType(). All information
111      * pertinent to the annotation and its context is encapsulated
112      * in the passed AnnotationInfo instance.
113      * This is a method in interface AnnotationHandler.
114      *
115      * @param ainfo the annotation information
116      */

117     public HandlerProcessingResult processAnnotation(AnnotationInfo ainfo)
118             throws AnnotationProcessorException {
119
120         Class JavaDoc ejbClass = (Class JavaDoc) ainfo.getAnnotatedElement();
121         Annotation JavaDoc annotation = ainfo.getAnnotation();
122         if (logger.isLoggable(Level.FINER)) {
123             logger.finer("@ process ejb annotation " +
124                 annotation + " in " + ejbClass);
125         }
126         AnnotatedElementHandler aeHandler =
127                 ainfo.getProcessingContext().getHandler();
128         if (aeHandler != null && aeHandler instanceof EjbContext) {
129             EjbContext context = (EjbContext)aeHandler;
130             EjbDescriptor desc = context.getDescriptor();
131             if (isValidEjbDescriptor(desc, annotation)) {
132                 return getDefaultProcessedResult();
133             } else {
134                 log(Level.SEVERE, ainfo,
135                     localStrings.getLocalString(
136                     "enterprise.deployment.annotation.handlers.notcompsuperclass",
137                     "The annotation symbol defined in super-class is not compatible with {0} ejb {1}.",
138                     new Object JavaDoc[] { desc.getType(), desc.getName() }));
139                 return getDefaultFailedResult();
140             }
141         } else if (aeHandler == null || !(aeHandler instanceof EjbBundleContext)) {
142             return getInvalidAnnotatedElementHandlerResult(
143                 ainfo.getProcessingContext().getHandler(), ainfo);
144         }
145
146         EjbBundleContext ctx = (EjbBundleContext)aeHandler;
147
148         if (logger.isLoggable(Level.FINE)) {
149             logger.fine("My context is " + ctx);
150         }
151         
152         String JavaDoc elementName = getAnnotatedName(annotation);
153         if (elementName.length() == 0) {
154             elementName = ejbClass.getSimpleName();
155         }
156
157         EjbBundleDescriptor currentBundle = ctx.getDescriptor();
158         EjbDescriptor ejbDesc = null;
159         try {
160             ejbDesc = currentBundle.getEjbByName(elementName);
161         } catch(IllegalArgumentException JavaDoc ex) {
162             //getEjbByName throws IllegalArgumentException when no ejb is found
163
}
164
165         if (ejbDesc != null) {
166             // element has already been defined in the standard DDs,
167
// overriding rules applies
168
if (logger.isLoggable(Level.FINE)) {
169                 logger.fine("Overriding rules apply for " + ejbClass.getName());
170             }
171
172             // don't allow ejb-jar.xml overwrite ejb type
173
if (!isValidEjbDescriptor(ejbDesc, annotation)) {
174                 // this is an error
175
log(Level.SEVERE, ainfo,
176                     localStrings.getLocalString(
177                     "enterprise.deployment.annotation.handlers.wrongejbtype",
178                     "Wrong annotation symbol for ejb {1}",
179                     new Object JavaDoc[] { ejbDesc }));
180                 return getDefaultFailedResult();
181             }
182
183             // <ejb-class> is optional if a component-defining
184
// annotation is used. If present, <ejb-class> element
185
// must match the class on which the component defining annotation
186
// appears.
187
String JavaDoc descriptorEjbClass = ejbDesc.getEjbClassName();
188             if( descriptorEjbClass == null ) {
189                 ejbDesc.setEjbClassName(ejbClass.getName());
190                 ejbDesc.applyDefaultClassToLifecycleMethods();
191             } else if( !descriptorEjbClass.equals(ejbClass.getName()) ) {
192                 log(Level.SEVERE, ainfo,
193                     localStrings.getLocalString(
194                     "enterprise.deployment.annotation.handlers.ejbclsmismatch",
195                     "",
196                     new Object JavaDoc[] { descriptorEjbClass, elementName,
197                                    ejbClass.getName() }));
198                 return getDefaultFailedResult();
199             }
200
201
202         } else {
203             if (logger.isLoggable(Level.FINE)) {
204                 logger.fine("Creating a new descriptor for "
205                     + ejbClass.getName());
206             }
207
208             ejbDesc = createEjbDescriptor(elementName, ainfo);
209             currentBundle.addEjb(ejbDesc);
210             if (logger.isLoggable(Level.FINE)) {
211                 logger.fine("New " +
212                     getAnnotationType().getName() + " bean " + elementName);
213             }
214         }
215
216         HandlerProcessingResult procResult = setEjbDescriptorInfo(ejbDesc, ainfo);
217         doTimedObjectProcessing(ejbClass, ejbDesc);
218
219         EjbContext ejbContext = new EjbContext(ejbDesc, ejbClass);
220         // we push the new context on the stack...
221
ctx.getProcessingContext().pushHandler(ejbContext);
222         
223         return procResult;
224     }
225
226     /**
227      * Process TimedObject and @Timeout annotation. It's better to do it
228      * when processing the initial bean type since Timeout method is a
229      * business method that should be included in any tx processing defaulting
230      * that takes place.
231      */

232     private void doTimedObjectProcessing(Class JavaDoc ejbClass,
233                                          EjbDescriptor ejbDesc) {
234         
235         // Timeout methods can be declared on the bean class or any
236
// super-class and can be public, protected, private, or
237
// package access. There can be at most one timeout method for
238
// the entire bean class hierarchy, so we start from the bean
239
// class and go up, stopping when we find the first one.
240

241         MethodDescriptor timeoutMethodDesc = null;
242         Class JavaDoc nextClass = ejbClass;
243         while((nextClass != Object JavaDoc.class) && (nextClass != null)
244               && (timeoutMethodDesc == null) ) {
245             Method JavaDoc[] methods = nextClass.getDeclaredMethods();
246             for(Method JavaDoc m : methods) {
247                 if( (m.getAnnotation(Timeout JavaDoc.class) != null) ) {
248                     timeoutMethodDesc =
249                         new MethodDescriptor(m, MethodDescriptor.EJB_BEAN);
250                     break;
251                 }
252             }
253             nextClass = nextClass.getSuperclass();
254         }
255
256         if( (timeoutMethodDesc == null) &&
257             javax.ejb.TimedObject JavaDoc.class.isAssignableFrom(ejbClass) ) {
258             // If the class implements the TimedObject interface, it must
259
// be ejbTimeout.
260
timeoutMethodDesc = new MethodDescriptor
261                 ("ejbTimeout", "@Timeout method",
262                  new String JavaDoc[] { "javax.ejb.Timer" },
263                  MethodDescriptor.EJB_BEAN);
264         }
265
266         if( timeoutMethodDesc != null ) {
267             ejbDesc.setEjbTimeoutMethod(timeoutMethodDesc);
268         }
269
270         return;
271     }
272
273     /**
274      * MessageDriven bean does not need to invoke this API.
275      * @param ejbDesc
276      * @param ainfo for error handling
277      * @return HandlerProcessingResult
278      */

279     protected HandlerProcessingResult setBusinessAndHomeInterfaces(
280             EjbDescriptor ejbDesc, AnnotationInfo ainfo)
281             throws AnnotationProcessorException {
282
283         Set JavaDoc<String JavaDoc> localIntfNames = new HashSet JavaDoc<String JavaDoc>();
284         Set JavaDoc<String JavaDoc> remoteIntfNames = new HashSet JavaDoc<String JavaDoc>();
285
286         Class JavaDoc ejbClass = (Class JavaDoc)ainfo.getAnnotatedElement();
287
288         // First check for annotations specifying remote/local business intfs.
289
// We analyze them here because they are needed during the
290
// implements clause processing for beans that specify
291
// @Stateless/@Stateful. In addition, they should *not* be processed
292
// if there is no @Stateful/@Stateless annotation.
293

294         Remote JavaDoc remoteBusAnn = (Remote JavaDoc) ejbClass.getAnnotation(Remote JavaDoc.class);
295         boolean emptyRemoteBusAnn = false;
296         if( remoteBusAnn != null ) {
297             for(Class JavaDoc next : remoteBusAnn.value()) {
298                 remoteIntfNames.add(next.getName());
299             }
300             emptyRemoteBusAnn = remoteIntfNames.isEmpty();
301         }
302
303         Local JavaDoc localBusAnn = (Local JavaDoc) ejbClass.getAnnotation(Local JavaDoc.class);
304         if( localBusAnn != null ) {
305             for(Class JavaDoc next : localBusAnn.value()) {
306                 localIntfNames.add(next.getName());
307             }
308         }
309
310         List JavaDoc<Class JavaDoc> eligibleInterfaces = new LinkedList JavaDoc<Class JavaDoc>();
311         for(Class JavaDoc next : ejbClass.getInterfaces()) {
312             if( !excludedFromImplementsClause(next) ) {
313                 eligibleInterfaces.add(next);
314             }
315         }
316
317         // total number of local/remote business interfaces declared
318
// outside of the implements clause
319
int nonImplementsClauseBusinessInterfaceCount =
320             remoteIntfNames.size() + localIntfNames.size() +
321             ejbDesc.getRemoteBusinessClassNames().size() +
322             ejbDesc.getLocalBusinessClassNames().size();
323         
324         for(Class JavaDoc next : eligibleInterfaces) {
325             String JavaDoc nextIntfName = next.getName();
326
327             if( remoteIntfNames.contains(nextIntfName)
328                 ||
329                 localIntfNames.contains(nextIntfName)
330                 ||
331                 ejbDesc.getRemoteBusinessClassNames().contains(nextIntfName)
332                 ||
333                 ejbDesc.getLocalBusinessClassNames().contains(nextIntfName)){
334                 
335                 // Interface has already been identified as a Remote/Local
336
// business interface, so ignore.
337

338             } else if( next.getAnnotation(Local JavaDoc.class) != null ) {
339
340                 localIntfNames.add(nextIntfName);
341
342             } else if( next.getAnnotation(Remote JavaDoc.class) != null ) {
343                 
344                 remoteIntfNames.add(nextIntfName);
345
346             } else {
347
348                 if( nonImplementsClauseBusinessInterfaceCount == 0 ) {
349
350                     // If there's an empty @Remote annotation on the class,
351
// it's treated as a remote business interface. Otherwise,
352
// it's treated as a local business interface.
353
if( emptyRemoteBusAnn ) {
354                         remoteIntfNames.add(nextIntfName);
355                     } else {
356                         localIntfNames.add(nextIntfName);
357                     }
358
359                 } else {
360                     
361                     // Since the component has at least one other business
362
// interface, each implements clause interface that cannot
363
// be identified as business interface via the deployment
364
// descriptor or a @Remote/@Local annotation is ignored.
365

366                 }
367             }
368         }
369
370         if (localIntfNames.size() > 0) {
371             for(String JavaDoc next : localIntfNames) {
372                 ejbDesc.addLocalBusinessClassName(next);
373             }
374         }
375         if (remoteIntfNames.size() > 0) {
376             for(String JavaDoc next : remoteIntfNames) {
377                 ejbDesc.addRemoteBusinessClassName(next);
378             }
379         }
380
381         // Do Adapted @Home / Adapted @LocalHome processing here too since
382
// they are logically part of the structural @Stateless/@Stateful info.
383
RemoteHome JavaDoc remoteHomeAnn = (RemoteHome JavaDoc)
384             ejbClass.getAnnotation(RemoteHome JavaDoc.class);
385
386         if( remoteHomeAnn != null ) {
387             Class JavaDoc remoteHome = remoteHomeAnn.value();
388             Class JavaDoc remoteIntf = getComponentIntfFromHome(remoteHome);
389             if( EJBHome JavaDoc.class.isAssignableFrom(remoteHome) &&
390                 (remoteIntf != null) ) {
391
392                 ejbDesc.setHomeClassName(remoteHome.getName());
393                 ejbDesc.setRemoteClassName(remoteIntf.getName());
394
395             } else {
396                 log(Level.SEVERE, ainfo,
397                     localStrings.getLocalString(
398                     "enterprise.deployment.annotation.handlers.invalidremotehome",
399                     "Encountered invalid @RemoteHome interface {0}.",
400                     new Object JavaDoc[] { remoteHome }));
401                 return getDefaultFailedResult();
402             }
403         }
404
405         LocalHome JavaDoc localHomeAnn = (LocalHome JavaDoc)
406             ejbClass.getAnnotation(LocalHome JavaDoc.class);
407
408         if( localHomeAnn != null ) {
409             Class JavaDoc localHome = localHomeAnn.value();
410             Class JavaDoc localIntf = getComponentIntfFromHome(localHome);
411             if( EJBLocalHome JavaDoc.class.isAssignableFrom(localHome) &&
412                 (localIntf != null) ) {
413
414                 ejbDesc.setLocalHomeClassName(localHome.getName());
415                 ejbDesc.setLocalClassName(localIntf.getName());
416
417             } else {
418                 log(Level.SEVERE, ainfo,
419                     localStrings.getLocalString(
420                     "enterprise.deployment.annotation.handlers.invalidlocalhome",
421                     "Encountered invalid @LocalHome interface {0}.",
422                     new Object JavaDoc[] { localHome }));
423                 return getDefaultFailedResult();
424             }
425         }
426
427         return getDefaultProcessedResult();
428     }
429
430     private Class JavaDoc getComponentIntfFromHome(Class JavaDoc homeIntf) {
431
432         Class JavaDoc componentIntf = null;
433
434         Method JavaDoc[] methods = homeIntf.getMethods();
435         for (Method JavaDoc m : methods) {
436             if (m.getName().startsWith("create")) {
437                 componentIntf = m.getReturnType();
438                 break;
439             }
440         }
441
442         return componentIntf;
443     }
444
445     protected boolean excludedFromImplementsClause(Class JavaDoc intf) {
446         return ( (intf == java.io.Serializable JavaDoc.class) ||
447                  (intf == java.io.Externalizable JavaDoc.class) ||
448                  ( (intf.getPackage() != null) &&
449                    intf.getPackage().getName().equals("javax.ejb")) );
450     }
451
452     protected void doDescriptionProcessing(String JavaDoc description,
453                                            EjbDescriptor ejbDescriptor) {
454         // Since there are multiple descriptions allowed in the deployment
455
// descriptor, there are no overriding issues here. If the
456
// component-defining annotation contains a description, it will
457
// always be added to the list of descriptions for the bean.
458
if( (description != null) && !description.equals("") ) {
459             ejbDescriptor.setDescription(description);
460         }
461
462     }
463
464     protected void doMappedNameProcessing(String JavaDoc mappedName,
465                                           EjbDescriptor ejbDesc) {
466         
467         // Set mappedName() if a value has been given in the annotation and
468
// it hasn't already been set on the descriptor via the .xml.
469
if( ejbDesc.getMappedName().equals("") ) {
470             if( !mappedName.equals("") ) {
471                 ejbDesc.setMappedName(mappedName);
472             }
473         }
474         
475         
476     }
477
478 }
479
Popular Tags