1 /*
2 * JBoss, Home of Professional Open Source
3 * Copyright 2005, JBoss Inc., and individual contributors as indicated
4 * by the @authors tag. See the copyright.txt in the distribution for a
5 * full listing of individual contributors.
6 *
7 * This is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * This software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this software; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21 */
22 package org.jboss.verifier.strategy;
23
24 // standard imports
25
26 import org.jboss.logging.Logger;
27 import org.jboss.metadata.BeanMetaData;
28 import org.jboss.metadata.EntityMetaData;
29 import org.jboss.metadata.MessageDrivenMetaData;
30 import org.jboss.util.Classes;
31 import org.jboss.verifier.Section;
32 import org.jboss.verifier.event.VerificationEvent;
33 import org.jboss.verifier.factory.DefaultEventFactory;
34 import org.jboss.verifier.factory.VerificationEventFactory;
35
36 import java.lang.reflect.Constructor;
37 import java.lang.reflect.Field;
38 import java.lang.reflect.Member;
39 import java.lang.reflect.Method;
40 import java.lang.reflect.Modifier;
41 import java.net.URL;
42 import java.net.URLClassLoader;
43 import java.rmi.RemoteException;
44 import java.util.Arrays;
45 import java.util.Iterator;
46 import java.util.LinkedList;
47 import java.util.List;
48
49 import javax.jms.Message;
50
51
52 /**
53 * Abstract superclass for verifiers containing a bunch of useful methods.
54 *
55 * @see org.jboss.verifier.strategy.VerificationStrategy
56 *
57 * @author <a href="mailto:juha.lindfors@jboss.org">Juha Lindfors</a>
58 * @author Aaron Mulder (ammulder@alumni.princeton.edu)
59 * @author Vinay Menon (menonv@cpw.co.uk)
60 * @author <a href="mailto:andreas@jboss.org">Andreas Schaefer</a>
61 * @author <a href="mailto:luke@mkeym.com">Luke Taylor</a>
62 * @author <a href="mailto:jwalters@computer.org">Jay Walters</a>
63 * @author Scott.Stark@jboss.org
64 *
65 * @version $Revision: 68983 $
66 * @since JDK 1.3
67 */
68 public abstract class AbstractVerifier
69 implements VerificationStrategy
70 {
71 static final Logger log = Logger.getLogger(AbstractVerifier.class);
72
73 protected final static String EJB_OBJECT_INTERFACE =
74 "javax.ejb.EJBObject";
75
76 protected final static String EJB_HOME_INTERFACE =
77 "javax.ejb.EJBHome";
78
79 protected final static String EJB_LOCAL_OBJECT_INTERFACE =
80 "javax.ejb.EJBLocalObject";
81
82 protected final static String EJB_LOCAL_HOME_INTERFACE =
83 "javax.ejb.EJBLocalHome";
84
85 /**
86 * The application classloader. This can be provided by the context
87 * directly via {@link VerificationContext#getClassLoader} method, or
88 * constructed by this object by creating a classloader to the URL
89 * returned by {@link VerificationContext#getJarLocation} method. <p>
90 *
91 * Initialized in the constructor.
92 */
93 protected ClassLoader classloader = null;
94
95 /**
96 * Factory for generating the verifier events. <p>
97 *
98 * Initialized in the constructor.
99 *
100 * @see org.jboss.verifier.factory.DefaultEventFactory
101 */
102 private VerificationEventFactory factory = null;
103
104 /**
105 * Context is used for retrieving application level information,
106 * such as the application meta data, location of the jar file, etc.
107 * <p>
108 *
109 * Initialized in the constructor.
110 */
111 private VerificationContext context = null;
112
113 /**************************************************************************
114 *
115 * CONSTRUCTORS
116 *
117 **************************************************************************/
118 public AbstractVerifier(VerificationContext context)
119 {
120 this.context = context;
121 this.classloader = context.getClassLoader();
122 this.factory = new DefaultEventFactory(getMessageBundle());
123
124 if (this.classloader == null)
125 {
126 URL[] list = {context.getJarLocation()};
127
128 ClassLoader parent = Thread.currentThread().getContextClassLoader();
129 this.classloader = new URLClassLoader(list, parent);
130 }
131 }
132
133
134 /*
135 *************************************************************************
136 *
137 * PUBLIC INSTANCE METHODS
138 *
139 *************************************************************************
140 */
141
142 public boolean isAssignableFrom(String className, Class assignableFromClass)
143 {
144 try
145 {
146 Class clazz = this.classloader.loadClass(className);
147 return clazz.isAssignableFrom(assignableFromClass);
148 }
149 catch (ClassNotFoundException e)
150 {
151 log.warn("Failed to find class: " + className, e);
152 }
153 return false;
154 }
155
156 public boolean isAssignableFrom(Class clazz, String assignableFromClassName)
157 {
158 try
159 {
160 Class assignableFromClass = this.classloader.loadClass(assignableFromClassName);
161 return clazz.isAssignableFrom(assignableFromClass);
162 }
163 catch (ClassNotFoundException e)
164 {
165 log.warn("Failed to find class: " + assignableFromClassName, e);
166 }
167 return false;
168 }
169
170 public abstract String getMessageBundle();
171
172 public abstract boolean isCreateMethod(Method m);
173
174 public abstract boolean isEjbCreateMethod(Method m);
175
176 public boolean hasLegalRMIIIOPArguments(Method method)
177 {
178 Class[] params = method.getParameterTypes();
179
180 for (int i = 0; i < params.length; ++i)
181 {
182 if (!isRMIIIOPType(params[i]))
183 return false;
184 }
185
186 return true;
187 }
188
189 public boolean hasLegalRMIIIOPReturnType(Method method)
190 {
191 return isRMIIIOPType(method.getReturnType());
192 }
193
194
195 public boolean hasLegalRMIIIOPExceptionTypes(Method method)
196 {
197 /*
198 * All checked exception classes used in method declarations
199 * (other than java.rmi.RemoteException) MUST be conforming
200 * RMI/IDL exception types.
201 *
202 * Spec 28.2.3 (4)
203 */
204 Iterator it = Arrays.asList(method.getExceptionTypes()).iterator();
205 while (it.hasNext())
206 {
207 Class exception = (Class)it.next();
208
209 if (!isRMIIDLExceptionType(exception))
210 return false;
211 }
212
213 return true;
214 }
215
216 /**
217 * Checks if the method includes java.rmi.RemoteException or its
218 * subclass in its throws clause.
219 *
220 * See bug report #434739 and #607805
221 */
222 public boolean throwsRemoteException(Method method)
223 {
224 Class[] exception = method.getExceptionTypes();
225
226 for (int i = 0; i < exception.length; ++i)
227 {
228 // Fix for bug #607805: an IOException is OK for local interfaces
229 // Fix for bug #626430: java.lang.Exception is also OK
230 if (exception[i].equals(java.io.IOException.class)
231 || exception[i].equals(java.lang.Exception.class))
232 {
233 continue;
234 }
235 // Not true see bug report #434739
236 // if (java.rmi.RemoteException.class.isAssignableFrom(exception[i]))
237 // According to the RMI spec. a remote interface must throw an RemoteException
238 // or any of its super classes therefore the check must be done vice versa
239
240 if (isAssignableFrom(exception[i], "java.rmi.RemoteException"))
241 {
242 return true;
243 }
244 }
245
246 return false;
247 }
248
249 /**
250 * checks if the method accepts a single parameter of a specified type.
251 */
252 public boolean hasSingleArgument(Method method, Class argClass)
253 {
254 Class[] params = method.getParameterTypes();
255 if (params.length == 1)
256 {
257 if (params[0].equals(argClass))
258 return true;
259 }
260
261 return false;
262 }
263
264 /**
265 * checks if the method accepts any parameters.
266 */
267 public boolean hasNoArguments(Method method)
268 {
269 Class[] params = method.getParameterTypes();
270 return (params.length == 0) ? true : false;
271 }
272
273 /**
274 * checks if the method throws no checked exceptions in its throws clause.
275 */
276 public boolean throwsNoException(Method method)
277 {
278 boolean hasCheckedException = false;
279 Class[] exceptions = method.getExceptionTypes();
280 for (int e = 0; e < exceptions.length; e++)
281 {
282 Class ex = exceptions[e];
283 boolean isError = Error.class.isAssignableFrom(ex);
284 boolean isRuntimeException = RuntimeException.class.isAssignableFrom(ex);
285 boolean isRemoteException = RemoteException.class.isAssignableFrom(ex);
286 if (isError == false && isRuntimeException == false && isRemoteException == false)
287 hasCheckedException = true;
288 }
289 return hasCheckedException == false;
290 }
291
292 /**
293 * checks if the method includes java.ejb.CreateException in its
294 * throws clause.
295 */
296 public boolean throwsCreateException(Method method)
297 {
298 Class[] exception = method.getExceptionTypes();
299 for (int i = 0; i < exception.length; ++i)
300 {
301 if (isAssignableFrom("javax.ejb.CreateException", exception[i]))
302 return true;
303 }
304
305 return false;
306 }
307
308 /**
309 * checks if the methods includes javax.ejb.FinderException in its
310 * throws clause.
311 */
312 public boolean throwsFinderException(Method method)
313 {
314 Class[] exception = method.getExceptionTypes();
315
316 for (int i = 0; i < exception.length; ++i)
317 {
318 if (isAssignableFrom("javax.ejb.FinderException", exception[i]))
319 return true;
320 }
321
322 return false;
323 }
324
325 /**
326 * checks if a class's member (method, constructor or field) has a
327 * <code>static</code> modifier.
328 */
329 public boolean isStatic(Member member)
330 {
331 return (Modifier.isStatic(member.getModifiers()));
332 }
333
334 /**
335 * checks if the given class is declared as static (inner classes only)
336 */
337 public boolean isStatic(Class c)
338 {
339 return (Modifier.isStatic(c.getModifiers()));
340 }
341
342 /**
343 * checks if a class's member (method, constructor or field) has a
344 * <code>final</code> modifier.
345 */
346 public boolean isFinal(Member member)
347 {
348 return (Modifier.isFinal(member.getModifiers()));
349 }
350
351 /**
352 * checks if the given class is declared as final
353 */
354 public boolean isFinal(Class c)
355 {
356 return (Modifier.isFinal(c.getModifiers()));
357 }
358
359 /**
360 * checks if a class's member (method, constructor or field) has a
361 * <code>public</code> modifier.
362 */
363 public boolean isPublic(Member member)
364 {
365 return (Modifier.isPublic(member.getModifiers()));
366 }
367
368 /**
369 * checks if the given class is declared as <code>public</code>
370 */
371 public boolean isPublic(Class c)
372 {
373 return (Modifier.isPublic(c.getModifiers()));
374 }
375
376 /**
377 * Checks whether all the fields in the class are declared as public.
378 */
379 public boolean isAllFieldsPublic(Class c)
380 {
381 try
382 {
383 Field list[] = c.getFields();
384 for (int i = 0; i < list.length; i++)
385 {
386 if (!Modifier.isPublic(list[i].getModifiers()))
387 return false;
388 }
389 }
390 catch (Exception e)
391 {
392 return false;
393 }
394
395 return true;
396 }
397
398 /**
399 * checks if the given class is declared as abstract
400 */
401 public boolean isAbstract(Class c)
402 {
403 return (Modifier.isAbstract(c.getModifiers()));
404 }
405
406 /**
407 * checks if the given method is declared as abstract
408 */
409 public boolean isAbstract(Method m)
410 {
411 return (Modifier.isAbstract(m.getModifiers()));
412 }
413
414 /**
415 * checks if finder returns the primary key type
416 */
417 public boolean isSingleObjectFinder(EntityMetaData entity,
418 Method finder)
419 {
420 return hasPrimaryKeyReturnType(entity, finder);
421 }
422
423 /**
424 * checks if finder method returns either Collection or Enumeration
425 */
426 public boolean isMultiObjectFinder(Method f)
427 {
428 return (java.util.Collection.class.isAssignableFrom(f.getReturnType())
429 || java.util.Enumeration.class.isAssignableFrom(f.getReturnType()));
430 }
431
432 /**
433 * checks the return type of method matches the bean's remote interface
434 */
435 public boolean hasRemoteReturnType(BeanMetaData bean, Method m)
436 {
437 try
438 {
439 Class clazz = classloader.loadClass(bean.getRemote());
440 return m.getReturnType().isAssignableFrom(clazz);
441 }
442 catch (Exception e)
443 {
444 // e.printStackTrace();
445 return false;
446 }
447 }
448
449 /**
450 * checks the return type of method matches the bean's local interface
451 */
452 public boolean hasLocalReturnType(BeanMetaData bean, Method m)
453 {
454 try
455 {
456 Class clazz = classloader.loadClass(bean.getLocal());
457 return m.getReturnType().isAssignableFrom(clazz);
458 }
459 catch (Exception e)
460 {
461 // e.printStackTrace();
462 return false;
463 }
464 }
465
466 /**
467 * checks if a method has a void return type
468 */
469 public boolean hasVoidReturnType(Method method)
470 {
471 return (method.getReturnType() == Void.TYPE);
472 }
473
474 /**
475 * Finds java.ejb.MessageDrivenBean interface from the class
476 */
477 public boolean hasMessageDrivenBeanInterface(Class c)
478 {
479 return isAssignableFrom("javax.ejb.MessageDrivenBean", c);
480 }
481
482 /**
483 * Finds javax.jms.MessageListener interface from the class
484 */
485 public boolean hasMessageListenerInterface(Class c)
486 {
487 return isAssignableFrom("javax.jms.MessageListener", c);
488 }
489
490 /**
491 * Finds java.ejb.SessionBean interface from the class
492 */
493 public boolean hasSessionBeanInterface(Class c)
494 {
495 return isAssignableFrom("javax.ejb.SessionBean", c);
496 }
497
498 /**
499 * Finds java.ejb.EntityBean interface from the class
500 */
501 public boolean hasEntityBeanInterface(Class c)
502 {
503 return isAssignableFrom("javax.ejb.EntityBean", c);
504 }
505
506 /**
507 * Finds java.ejb.EJBObject interface from the class
508 */
509 public boolean hasEJBObjectInterface(Class c)
510 {
511 return isAssignableFrom("javax.ejb.EJBObject", c);
512 }
513
514 /**
515 * Finds java.ejb.EJBLocalObject interface from the class
516 */
517 public boolean hasEJBLocalObjectInterface(Class c)
518 {
519 return isAssignableFrom("javax.ejb.EJBLocalObject", c);
520 }
521
522 /**
523 * Finds javax.ejb.EJBHome interface from the class or its
524 * superclasses
525 */
526 public boolean hasEJBHomeInterface(Class c)
527 {
528 return isAssignableFrom("javax.ejb.EJBHome", c);
529 }
530
531 /**
532 * Finds javax.ejb.EJBLocalHome interface from the class or its
533 * superclasses
534 */
535 public boolean hasEJBLocalHomeInterface(Class c)
536 {
537 return isAssignableFrom("javax.ejb.EJBLocalHome", c);
538 }
539
540 /**
541 * Finds javax.ejb.SessionSynchronization interface from the class
542 */
543 public boolean hasSessionSynchronizationInterface(Class c)
544 {
545 return isAssignableFrom("javax.ejb.SessionSynchronization", c);
546 }
547
548 /**
549 * Checks if a class has a default (no args) constructor
550 */
551 public boolean hasDefaultConstructor(Class c)
552 {
553 try
554 {
555 Constructor ctr = c.getConstructor(new Class[0]);
556 }
557 catch (NoSuchMethodException e)
558 {
559 if( log.isTraceEnabled() )
560 {
561 StringBuffer tmp = new StringBuffer("hasDefaultConstructor(");
562 tmp.append(") failure, ");
563 Classes.displayClassInfo(c, tmp);
564 log.trace(tmp.toString(), e);
565 }
566 return false;
567 }
568
569 return true;
570 }
571
572 /**
573 * Checks of the class defines a finalize() method
574 */
575 public boolean hasFinalizer(Class c)
576 {
577 try
578 {
579 Method finalizer = c.getDeclaredMethod(FINALIZE_METHOD, new Class[0]);
580 }
581 catch (NoSuchMethodException e)
582 {
583 return false;
584 }
585
586 return true;
587 }
588
589 /**
590 * check if a class has one or more finder methods
591 */
592 public boolean hasFinderMethod(Class c)
593 {
594 Method[] method = c.getMethods();
595 for (int i = 0; i < method.length; ++i)
596 {
597 if (method[i].getName().startsWith("ejbFind"))
598 return true;
599 }
600
601 return false;
602 }
603
604 /**
605 * Check if this is a finder method
606 */
607 public boolean isFinderMethod(Method m)
608 {
609 return (m.getName().startsWith("find"));
610 }
611
612 /**
613 * Check if the given message is the onMessage() method
614 */
615 public boolean isOnMessageMethod(Method m)
616 {
617 if ("onMessage".equals(m.getName()))
618 {
619 Class[] paramTypes = m.getParameterTypes();
620 if (paramTypes.length == 1)
621 {
622 if (Message.class.equals(paramTypes[0]))
623 return true;
624 }
625 }
626 return false;
627 }
628
629 /**
630 * Checks for at least one non-static field.
631 */
632 public boolean hasANonStaticField(Class c)
633 {
634 try
635 {
636 Field list[] = c.getFields();
637 for (int i = 0; i < list.length; i++)
638 {
639 if (!Modifier.isStatic(list[i].getModifiers()))
640 return true;
641 }
642 }
643 catch (Exception ignored)
644 {
645 }
646
647 return false;
648 }
649
650 /**
651 * Searches for an instance of a public onMessage method from the class
652 */
653 public boolean hasOnMessageMethod(Class c)
654 {
655 Method[] method = c.getMethods();
656 for (int i = 0; i < method.length; ++i)
657 {
658 if (isOnMessageMethod(method[i]))
659 return true;
660 }
661
662 return false;
663 }
664
665 /**
666 * Searches for an instance of a public create method from the class
667 */
668 public boolean hasCreateMethod(Class c)
669 {
670 Method[] method = c.getMethods();
671
672 for (int i = 0; i < method.length; ++i)
673 {
674 if (isCreateMethod(method[i]))
675 return true;
676 }
677
678 return false;
679 }
680
681 /**
682 * Searches for an instance of a public ejbCreate method from the class
683 */
684 public boolean hasEJBCreateMethod(Class c, boolean isSession)
685 {
686 Method[] method = c.getMethods();
687 for (int i = 0; i < method.length; ++i)
688 {
689 if (isEjbCreateMethod(method[i]))
690 {
691 if (!isStatic(method[i]) && !isFinal(method[i])
692 && ((isSession && hasVoidReturnType(method[i]))
693 || (!isSession))
694 )
695 {
696 return true;
697 }
698 }
699 }
700
701 return false;
702 }
703
704 /**
705 * Searches the class or interface, and its superclass or superinterface
706 * for a create() method that takes no arguments
707 */
708 public boolean hasDefaultCreateMethod(Class home)
709 {
710 Method[] method = home.getMethods();
711
712 for (int i = 0; i < method.length; ++i)
713 {
714 if (isCreateMethod(method[i]))
715 {
716 Class[] params = method[i].getParameterTypes();
717 if (params.length == 0)
718 return true;
719 }
720 }
721
722 return false;
723 }
724
725 /**
726 * checks if the class has an ejbFindByPrimaryKey method
727 */
728 public boolean hasEJBFindByPrimaryKey(Class c)
729 {
730 Method[] method = c.getMethods();
731 for (int i = 0; i < method.length; ++i)
732 {
733 if (method[i].getName().equals(EJB_FIND_BY_PRIMARY_KEY))
734 return true;
735 }
736
737 return false;
738 }
739
740 /**
741 * checks the return type of method matches the entity's primary key
742 * class or is a super class of the primary key class
743 */
744 public boolean hasPrimaryKeyReturnType(EntityMetaData entity, Method m)
745 {
746 try
747 {
748 return
749 m.getReturnType().isAssignableFrom(classloader.loadClass(entity.getPrimaryKeyClass()));
750 }
751 catch (ClassNotFoundException cnfe)
752 {
753 // Only check equality
754 return
755 m.getReturnType().getName().equals(entity.getPrimaryKeyClass());
756 }
757 }
758
759 /**
760 * @return Returns the default create method or <code>null</code>
761 * if none is found
762 */
763 public Method getDefaultCreateMethod(Class c)
764 {
765 Method method = null;
766
767 try
768 {
769 method = c.getMethod(CREATE_METHOD, null);
770 }
771 catch (NoSuchMethodException ignored)
772 {
773 }
774
775 return method;
776 }
777
778 /**
779 * Returns the ejbFindByPrimaryKey method
780 */
781 public Method getEJBFindByPrimaryKey(Class c)
782 {
783 Method[] method = c.getMethods();
784 for (int i = 0; i < method.length; ++i)
785 {
786 if (method[i].getName().equals(EJB_FIND_BY_PRIMARY_KEY))
787 return method[i];
788 }
789
790 return null;
791 }
792
793 /**
794 * returns the ejbFind<METHOD> methods of a bean
795 */
796 public Iterator getEJBFindMethods(Class c)
797 {
798 List finders = new LinkedList();
799 Method[] method = c.getMethods();
800 for (int i = 0; i < method.length; ++i)
801 {
802 if (method[i].getName().startsWith("ejbFind"))
803 finders.add(method[i]);
804 }
805
806 return finders.iterator();
807 }
808
809
810 /**
811 * returns the finder methods of a home interface
812 */
813 public Iterator getFinderMethods(Class home)
814 {
815 List finders = new LinkedList();
816 Method[] method = home.getMethods();
817
818 for (int i = 0; i < method.length; ++i)
819 {
820 if (method[i].getName().startsWith("find"))
821 finders.add(method[i]);
822 }
823
824 return finders.iterator();
825 }
826
827 /**
828 * Returns the onMessage(...) method of a bean
829 */
830 public Iterator getOnMessageMethods(Class c)
831 {
832 List onMessages = new LinkedList();
833 Method[] method = c.getMethods();
834
835 for (int i = 0; i < method.length; ++i)
836 {
837 if (isOnMessageMethod(method[i]))
838 onMessages.add(method[i]);
839 }
840
841 return onMessages.iterator();
842 }
843
844 /**
845 * Returns the ejbCreate(...) methods of a bean
846 */
847 public Iterator getEJBCreateMethods(Class c)
848 {
849 List ejbCreates = new LinkedList();
850 Method[] method = c.getMethods();
851
852 for (int i = 0; i < method.length; ++i)
853 {
854 if (isEjbCreateMethod(method[i]))
855 ejbCreates.add(method[i]);
856 }
857
858 return ejbCreates.iterator();
859 }
860
861 /**
862 * Return all create methods of a class
863 */
864 public Iterator getCreateMethods(Class c)
865 {
866 List creates = new LinkedList();
867 Method[] method = c.getMethods();
868
869 for (int i = 0; i < method.length; ++i)
870 {
871 if (isCreateMethod(method[i]))
872 creates.add(method[i]);
873 }
874
875 return creates.iterator();
876 }
877
878 /**
879 * Check whether a class has more than one create method
880 */
881 public boolean hasMoreThanOneCreateMethods(Class c)
882 {
883 int count = 0;
884 Method[] method = c.getMethods();
885 for (int i = 0; i < method.length; ++i)
886 {
887 if (isCreateMethod(method[i]))
888 {
889 ++count;
890 }
891 }
892
893 return (count > 1);
894 }
895
896 /**
897 * Check whether two given methods declare the same Exceptions
898 */
899 public boolean hasMatchingExceptions(Method source, Method target)
900 {
901 // target must be a superset of source
902 Class[] a = source.getExceptionTypes();
903 Class[] b = target.getExceptionTypes();
904 Class rteClass = null;
905 Class errorClass = null;
906
907 try
908 {
909 rteClass = classloader.loadClass("java.lang.RuntimeException");
910 errorClass = classloader.loadClass("java.lang.Error");
911 }
912 catch (ClassNotFoundException cnfe)
913 {
914 // Ignored, if this happens we have more serious problems :)
915 }
916
917 for (int i = 0; i < a.length; ++i)
918 {
919 if (rteClass.isAssignableFrom(a[i])
920 || errorClass.isAssignableFrom(a[i]))
921 {
922 // Skip over subclasses of java.lang.RuntimeException and
923 // java.lang.Error
924 continue;
925 }
926
927 boolean found = false;
928 for (int j = 0; j < b.length; ++j)
929 {
930 if (b[j].isAssignableFrom(a[i]))
931 {
932 found = true;
933 break;
934 }
935 }
936
937 if (!found)
938 {
939 return false;
940 }
941 }
942
943 return true;
944 }
945
946 /**
947 * Check if a class (or its superclasses) declare a given method
948 */
949 public boolean hasMatchingMethod(Class bean, Method method)
950 {
951 try
952 {
953 bean.getMethod(method.getName(), method.getParameterTypes());
954 return true;
955 }
956 catch (NoSuchMethodException e)
957 {
958 if( log.isTraceEnabled() )
959 {
960 StringBuffer tmp = new StringBuffer("hasMatchingMethod(");
961 tmp.append(method.toString());
962 tmp.append(") failure, ");
963 Classes.displayClassInfo(bean, tmp);
964 log.trace(tmp.toString(), e);
965 }
966 return false;
967 }
968 }
969
970 /**
971 * Check whether two methods have the same return type
972 */
973 public boolean hasMatchingReturnType(Method a, Method b)
974 {
975 return (a.getReturnType() == b.getReturnType());
976 }
977
978 /**
979 * Check whether a bean has a matching ejbPostCreate methods for
980 * a given ejbCreate method
981 */
982 public boolean hasMatchingEJBPostCreate(Class bean, Method create)
983 {
984 try
985 {
986 return (bean.getMethod(getMatchingEJBPostCreateName(create.getName()),
987 create.getParameterTypes()) != null);
988 }
989 catch (NoSuchMethodException e)
990 {
991 if( log.isTraceEnabled() )
992 {
993 StringBuffer tmp = new StringBuffer("hasMatchingEJBPostCreate(");
994 tmp.append(create.toString());
995 tmp.append(") failure, ");
996 Classes.displayClassInfo(bean, tmp);
997 log.trace(tmp.toString(), e);
998 }
999 return false;
1000 }
1001 }
1002
1003 public boolean hasMatchingEJBCreate(Class bean, Method create)
1004 {
1005 try
1006 {
1007 return (bean.getMethod(getMatchingEJBCreateName(create.getName()), create.getParameterTypes()) != null);
1008 }
1009 catch (NoSuchMethodException e)
1010 {
1011 if( log.isTraceEnabled() )
1012 {
1013 StringBuffer tmp = new StringBuffer("hasMatchingEJBCreate(");
1014 tmp.append(create.toString());
1015 tmp.append(") failure, ");
1016 Classes.displayClassInfo(bean, tmp);
1017 log.trace(tmp.toString(), e);
1018 }
1019 return false;
1020 }
1021 }
1022
1023 public Method getMatchingEJBPostCreate(Class bean, Method create)
1024 {
1025 try
1026 {
1027 return bean.getMethod(getMatchingEJBPostCreateName(create.getName()), create.getParameterTypes());
1028 }
1029 catch (NoSuchMethodException e)
1030 {
1031 return null;
1032 }
1033 }
1034
1035 public Method getMatchingEJBCreate(Class bean, Method create)
1036 {
1037 try
1038 {
1039 return bean.getMethod(getMatchingEJBCreateName(create.getName()), create.getParameterTypes());
1040 }
1041 catch (NoSuchMethodException e)
1042 {
1043 return null;
1044 }
1045 }
1046
1047 public boolean hasMatchingEJBFind(Class bean, Method finder)
1048 {
1049 try
1050 {
1051 String methodName = "ejbF" + finder.getName().substring(1);
1052 return (bean.getMethod(methodName, finder.getParameterTypes()) != null);
1053 }
1054 catch (NoSuchMethodException e)
1055 {
1056 if( log.isTraceEnabled() )
1057 {
1058 StringBuffer tmp = new StringBuffer("hasMatchingEJBFind(");
1059 tmp.append(finder.toString());
1060 tmp.append(") failure, ");
1061 Classes.displayClassInfo(bean, tmp);
1062 log.trace(tmp.toString(), e);
1063 }
1064 return false;
1065 }
1066 }
1067
1068 public Method getMatchingEJBFind(Class bean, Method finder)
1069 {
1070 try
1071 {
1072 String methodName = "ejbF" + finder.getName().substring(1);
1073 return bean.getMethod(methodName, finder.getParameterTypes());
1074 }
1075 catch (NoSuchMethodException e)
1076 {
1077 return null;
1078 }
1079 }
1080
1081 public boolean hasMatchingEJBHome(Class bean, Method home)
1082 {
1083 try
1084 {
1085 return (bean.getMethod(getMatchingEJBHomeName(home.getName()), home.getParameterTypes()) != null);
1086 }
1087 catch (NoSuchMethodException e)
1088 {
1089 if( log.isTraceEnabled() )
1090 {
1091 StringBuffer tmp = new StringBuffer("hasMatchingEJBHome(");
1092 tmp.append(home.toString());
1093 tmp.append(") failure, ");
1094 Classes.displayClassInfo(bean, tmp);
1095 log.trace(tmp.toString(), e);
1096 }
1097 return false;
1098 }
1099 }
1100
1101
1102 /*
1103 *************************************************************************
1104 *
1105 * PROTECTED INSTANCE METHODS
1106 *
1107 *************************************************************************
1108 */
1109
1110 protected void fireSpecViolationEvent(BeanMetaData bean, Section section)
1111 {
1112 fireSpecViolationEvent(bean, null /* method */, section);
1113 }
1114
1115 protected void fireSpecViolationEvent(BeanMetaData bean, Method method,
1116 Section section)
1117 {
1118 VerificationEvent event = factory.createSpecViolationEvent(context,
1119 section);
1120 event.setName(bean.getEjbName());
1121 event.setMethod(method);
1122
1123 context.fireSpecViolation(event);
1124 }
1125
1126 protected final void fireBeanVerifiedEvent(BeanMetaData bean)
1127 {
1128 fireBeanVerifiedEvent(bean, null);
1129 }
1130
1131 protected final void fireBeanVerifiedEvent(BeanMetaData bean, String msg)
1132 {
1133 VerificationEvent event = factory.createBeanVerifiedEvent(context);
1134 event.setName(bean.getEjbName());
1135
1136 if (msg != null)
1137 {
1138 event.setMessage(msg);
1139 }
1140
1141 context.fireBeanChecked(event);
1142 }
1143
1144 /*
1145 *************************************************************************
1146 *
1147 * IMPLEMENTS VERIFICATIONSTRATEGY INTERFACE
1148 *
1149 *************************************************************************
1150 */
1151
1152 /**
1153 * Provides an empty default implementation for EJB 1.1 verifier (message
1154 * beans are for EJB 2.0 and greater only).
1155 *
1156 * @param bean the message bean to verify
1157 */
1158 public void checkMessageBean(MessageDrivenMetaData bean)
1159 {
1160 }
1161
1162 /**
1163 * Returns the context object reference for this strategy implementation.
1164 *
1165 * @return the client object using this algorithm implementation
1166 */
1167 public VerificationContext getContext()
1168 {
1169 return context;
1170 }
1171
1172
1173 /*
1174 *************************************************************************
1175 *
1176 * PRIVATE INSTANCE METHODS
1177 *
1178 *************************************************************************
1179 */
1180
1181 protected boolean isRMIIIOPType(Class type)
1182 {
1183 /*
1184 * Java Language to IDL Mapping
1185 *
1186 * ftp://ftp.omg.org/pub/docs/ptc/99-03-09.pdf
1187 *
1188 * A conforming RMI/IDL type is a Java type whose values may be
1189 * transmitted across an RMI/IDL remote interface at run-time.
1190 * A Java data type is a conforming RMI/IDL type if it is:
1191 *
1192 * - one of the Java primitive types (see Primitive Types on
1193 * page 28-2).
1194 * - a conforming remote interface (as defined in RMI/IDL
1195 * Remote Interfaces on page 28-2).
1196 * - a conforming value type (as defined in RMI/IDL Value Types
1197 * on page 28-4).
1198 * - an array of conforming RMI/IDL types (see RMI/IDL Arrays on
1199 * page 28-5).
1200 * - a conforming exception type (see RMI/IDL Exception Types on
1201 * page 28-5).
1202 * - a conforming CORBA object reference type (see CORBA Object
1203 * Reference Types on page 28-6).
1204 * - a conforming IDL entity type see IDL Entity Types on page
1205 * 28-6).
1206 */
1207
1208 /*
1209 * Primitive types.
1210 *
1211 * Spec 28.2.2
1212 */
1213 if (type.isPrimitive())
1214 return true;
1215
1216 /*
1217 * Conforming array.
1218 *
1219 * Spec 28.2.5
1220 */
1221 if (type.isArray())
1222 return isRMIIIOPType(type.getComponentType());
1223
1224 /*
1225 * Conforming CORBA reference type
1226 *
1227 * Spec 28.2.7
1228 */
1229 if (org.omg.CORBA.Object.class.isAssignableFrom(type))
1230 return true;
1231
1232 /*
1233 * Conforming IDL Entity type
1234 *
1235 * Spec 28.2.8
1236 */
1237 if (org.omg.CORBA.portable.IDLEntity.class.isAssignableFrom(type))
1238 return true;
1239
1240 /*
1241 * Conforming remote interface.
1242 *
1243 * Spec 28.2.3
1244 */
1245 if (isRMIIDLRemoteInterface(type))
1246 return true;
1247
1248 /*
1249 * Conforming exception.
1250 *
1251 * Spec 28.2.6
1252 */
1253 if (isRMIIDLExceptionType(type))
1254 return true;
1255
1256 /*
1257 * Conforming value type.
1258 *
1259 * Spec 28.2.4
1260 */
1261 if (isRMIIDLValueType(type))
1262 return true;
1263
1264 return false;
1265 }
1266
1267 private boolean isRMIIDLRemoteInterface(Class type)
1268 {
1269 /*
1270 * If does not implement java.rmi.Remote, cannot be valid RMI-IDL
1271 * remote interface.
1272 */
1273
1274 if (!java.rmi.Remote.class.isAssignableFrom(type))
1275 return false;
1276
1277 Iterator methodIterator = Arrays.asList(type.getMethods()).iterator();
1278 while (methodIterator.hasNext())
1279 {
1280 Method m = (Method)methodIterator.next();
1281
1282 /*
1283 * All methods in the interface MUST throw
1284 * java.rmi.RemoteException or its subclass.
1285 *
1286 * Spec 28.2.3 (2)
1287 */
1288 if (!throwsRemoteException(m))
1289 return false;
1290
1291 /*
1292 * All checked exception classes used in method declarations
1293 * (other than java.rmi.RemoteException) MUST be conforming
1294 * RMI/IDL exception types.
1295 *
1296 * Spec 28.2.3 (4)
1297 */
1298 Iterator it = Arrays.asList(m.getExceptionTypes()).iterator();
1299 while (it.hasNext())
1300 {
1301 Class exception = (Class)it.next();
1302 if (!isRMIIDLExceptionType(exception))
1303 return false;
1304 }
1305 }
1306
1307 /*
1308 * The constant values defined in the interface MUST be
1309 * compile-time types of RMI/IDL primitive types or String.
1310 *
1311 * Spec 28.2.3 (6)
1312 */
1313 Iterator fieldIterator = Arrays.asList(type.getFields()).iterator();
1314 while (fieldIterator.hasNext())
1315 {
1316 Field f = (Field)fieldIterator.next();
1317
1318 if (f.getType().isPrimitive())
1319 continue;
1320
1321 if (f.getType().equals(java.lang.String.class))
1322 continue;
1323
1324 return false;
1325 }
1326
1327 return true;
1328 }
1329
1330 private boolean isRMIIDLExceptionType(Class type)
1331 {
1332 /*
1333 * A conforming RMI/IDL Exception class MUST be a checked
1334 * exception class and MUST be a valid RMI/IDL value type.
1335 *
1336 * Spec 28.2.6
1337 */
1338 if (!Throwable.class.isAssignableFrom(type))
1339 return false;
1340
1341 if (Error.class.isAssignableFrom(type))
1342 return false;
1343
1344 // 28.3.4.4 (6) java.rmi.RemoteException and its subclasses, and unchecked
1345 // exception classes, are assumed to be mapped to the implicit
1346 // CORBA system exception, and are therefore not explicitly
1347 // declared in OMG IDL.
1348 //
1349 // if (RuntimeException.class.isAssignableFrom(type))
1350 // return false;
1351
1352 if (!isRMIIDLValueType(type))
1353 return false;
1354
1355 return true;
1356 }
1357
1358 protected boolean isRMIIDLValueType(Class type)
1359 {
1360 /*
1361 * A value type MUST NOT either directly or indirectly implement the
1362 * java.rmi.Remote interface.
1363 *
1364 * Spec 28.2.4 (4)
1365 */
1366 if (java.rmi.Remote.class.isAssignableFrom(type))
1367 return false;
1368
1369 /*
1370 * If class is a non-static inner class then its containing class must
1371 * also be a conforming RMI/IDL value type.
1372 *
1373 * Spec 28.2.4 (3)
1374 */
1375 if (type.getDeclaringClass() != null && !isStatic(type))
1376 {
1377 if (!isRMIIDLValueType(type.getDeclaringClass()))
1378 return false;
1379 }
1380
1381 return true;
1382 }
1383
1384 private String getMatchingEJBHomeName(String homeName)
1385 {
1386 return "ejbHome" + homeName.substring(0, 1).toUpperCase() +
1387 homeName.substring(1);
1388 }
1389
1390 private String getMatchingEJBCreateName(String createName)
1391 {
1392 return "ejb" + createName.substring(0, 1).toUpperCase() +
1393 createName.substring(1);
1394 }
1395
1396 private String getMatchingEJBPostCreateName(String createName)
1397 {
1398 int createIdx = createName.indexOf("Create");
1399 return "ejbPost" + createName.substring(createIdx >= 0 ? createIdx : 0);
1400 }
1401
1402 /*
1403 *************************************************************************
1404 *
1405 * STRING CONSTANTS
1406 *
1407 *************************************************************************
1408 */
1409
1410 /*
1411 * Ejb-jar DTD
1412 */
1413 public final static String BEAN_MANAGED_TX =
1414 "Bean";
1415
1416 public final static String CONTAINER_MANAGED_TX =
1417 "Container";
1418
1419 public final static String STATEFUL_SESSION =
1420 "Stateful";
1421
1422 public final static String STATELESS_SESSION =
1423 "Stateless";
1424
1425 /*
1426 * method names
1427 */
1428 private final static String EJB_FIND_BY_PRIMARY_KEY =
1429 "ejbFindByPrimaryKey";
1430
1431 protected final static String EJB_CREATE_METHOD =
1432 "ejbCreate";
1433
1434 protected final static String EJB_REMOVE_METHOD =
1435 "ejbRemove";
1436
1437 private final static String EJB_POST_CREATE_METHOD =
1438 "ejbPostCreate";
1439
1440 protected final static String CREATE_METHOD =
1441 "create";
1442
1443 protected final static String EJB_HOME_METHOD =
1444 "ejbHome";
1445
1446 protected final static String EJB_SELECT_METHOD =
1447 "ejbSelect";
1448
1449 private final static String FINALIZE_METHOD =
1450 "finalize";
1451
1452 private final static String REMOVE_METHOD =
1453 "remove";
1454
1455 private final static String GET_HOME_HANDLE_METHOD =
1456 "getHomeHandle";
1457
1458 private final static String GET_EJB_METADATA_METHOD =
1459 "getEJBMetaData";
1460 }
1461
1462 /*
1463 vim:ts=3:sw=3:et
1464 */