KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > codehaus > dna > tools > verifier > ComponentVerifier


1 /*
2  * Copyright (C) The DNA Group. All rights reserved.
3  *
4  * This software is published under the terms of the DNA
5  * Software License version 1.1, a copy of which has been included
6  * with this distribution in the LICENSE.txt file.
7  */

8 package org.codehaus.dna.tools.verifier;
9
10 import java.lang.reflect.Method JavaDoc;
11 import java.lang.reflect.Modifier JavaDoc;
12 import java.net.URL JavaDoc;
13 import java.text.MessageFormat JavaDoc;
14 import java.util.ArrayList JavaDoc;
15 import java.util.List JavaDoc;
16 import java.util.ResourceBundle JavaDoc;
17
18 import org.codehaus.dna.Active;
19 import org.codehaus.dna.Composable;
20 import org.codehaus.dna.Configurable;
21 import org.codehaus.dna.Configuration;
22 import org.codehaus.dna.LogEnabled;
23 import org.codehaus.dna.ResourceLocator;
24 import org.codehaus.metaclass.Attributes;
25 import org.codehaus.metaclass.model.Attribute;
26
27 /**
28  * Utility class to help verify that component respects the
29  * rules of an DNA component.
30  *
31  * @author Peter Donald
32  * @version $Revision: 1.2 $ $Date: 2004/05/01 09:51:48 $
33  */

34 public class ComponentVerifier
35 {
36     /**
37      * Key used to look up ResourceBundle.
38      */

39     private static final String JavaDoc BASE_NAME =
40         ComponentVerifier.class.getName() + "Resources";
41
42     /**
43      * The resource bundle.
44      */

45     private static final ResourceBundle JavaDoc BUNDLE =
46         ResourceBundle.getBundle( BASE_NAME );
47
48     /**
49      * Constant for array of 0 classes. Saves recreating array everytime
50      * look up constructor with no args.
51      */

52     private static final Class JavaDoc[] EMPTY_TYPES = new Class JavaDoc[ 0 ];
53
54     /**
55      * The interfaces representing lifecycle stages.
56      */

57     private static final Class JavaDoc[] FRAMEWORK_CLASSES = new Class JavaDoc[]
58     {
59         LogEnabled.class,
60         Composable.class,
61         Configurable.class,
62         Active.class
63     };
64
65     /**
66      * The name of the Configurable.configure() method.
67      */

68     private static final String JavaDoc CONFIGURE_METHOD_NAME = "configure";
69
70     /**
71      * The parameter types of the Configurable.configure() method.
72      */

73     private static final Class JavaDoc[] CONFIGURE_PARAMETER_TYPES = new Class JavaDoc[]{Configuration.class};
74
75     /**
76      * The name of the Composable.compose() method.
77      */

78     private static final String JavaDoc COMPOSE_METHOD_NAME = "compose";
79
80     /**
81      * The parameter types of the Composable.compose() method.
82      */

83     private static final Class JavaDoc[] COMPOSE_PARAMETER_TYPES = new Class JavaDoc[]{ResourceLocator.class};
84
85     /**
86      * Verfiy that specified components designate classes that implement the
87      * advertised interfaces.
88      *
89      * @param type the component type
90      * @return an array of issues
91      */

92     public VerifyIssue[] verifyType( final Class JavaDoc type )
93     {
94         final List JavaDoc issues = new ArrayList JavaDoc();
95         verifyMetaData( type, issues );
96         verifyDependencyMetaData( type, issues );
97         verifyConfigurationMetaData( type, issues );
98
99         final Class JavaDoc[] interfaces = getServiceClasses( type, issues );
100
101         verifyClass( type, issues );
102         verifyImplementsServices( type, interfaces, issues );
103         for( int i = 0; i < interfaces.length; i++ )
104         {
105             verifyService( interfaces[ i ], issues );
106         }
107
108         return (VerifyIssue[])issues.
109             toArray( new VerifyIssue[ issues.size() ] );
110     }
111
112     /**
113      * Verify that the component is annotated with the
114      * dna.component metadata.
115      *
116      * @param type the type
117      * @param issues the list of issues
118      */

119     void verifyMetaData( final Class JavaDoc type, final List JavaDoc issues )
120     {
121         final Attribute attribute =
122             Attributes.getAttribute( type, "dna.component" );
123         if( null == attribute )
124         {
125             final String JavaDoc message = getMessage( "CV001" );
126             final VerifyIssue issue =
127                 new VerifyIssue( VerifyIssue.ERROR, message );
128             issues.add( issue );
129         }
130     }
131
132     /**
133      * Verify that the configuration metadata for component is valid.
134      *
135      * @param type the type
136      * @param issues the list of issues
137      */

138     void verifyConfigurationMetaData( final Class JavaDoc type, final List JavaDoc issues )
139     {
140         Attribute attribute = getConfigurationMetaData( type );
141         if( null != attribute )
142         {
143             final String JavaDoc location = attribute.getParameter( "location" );
144             if( null == location )
145             {
146                 final Object JavaDoc[] args = new Object JavaDoc[]{"type"};
147                 final String JavaDoc message = getMessage( "CV019", args );
148                 final VerifyIssue issue =
149                     new VerifyIssue( VerifyIssue.ERROR, message );
150                 issues.add( issue );
151             }
152             else
153             {
154                 verifyLocation( type, location, issues );
155             }
156         }
157     }
158
159     /**
160      * Return the configuration metadata for component if any.
161      * Protected to allow overiding in subclasses.
162      *
163      * @param type the component type
164      * @return the metadata if any
165      */

166     protected Attribute getConfigurationMetaData( final Class JavaDoc type )
167     {
168         try
169         {
170             final Method JavaDoc method = getConfigurationMethod( type );
171             return Attributes.getAttribute( method, "dna.configuration" );
172         }
173         catch( final NoSuchMethodException JavaDoc nsme )
174         {
175             return null;
176         }
177     }
178
179     /**
180      * Return the method used to pass configuration to component.
181      *
182      * @param type the components type
183      * @return the method
184      * @throws NoSuchMethodException if unable to locate method
185      */

186     protected Method JavaDoc getConfigurationMethod( final Class JavaDoc type )
187         throws NoSuchMethodException JavaDoc
188     {
189         if( !Configurable.class.isAssignableFrom( type ) )
190         {
191             throw new NoSuchMethodException JavaDoc();
192         }
193         return type.getMethod( CONFIGURE_METHOD_NAME,
194                                CONFIGURE_PARAMETER_TYPES );
195     }
196
197     /**
198      * Verify that the location specified for the schema actually exists.
199      *
200      * @param type the component type
201      * @param location the location of schmea
202      * @param issues the list of issues
203      */

204     void verifyLocation( final Class JavaDoc type,
205                          final String JavaDoc location,
206                          final List JavaDoc issues )
207     {
208         final URL JavaDoc url = type.getResource( location );
209         if( null == url )
210         {
211             final Object JavaDoc[] args = new Object JavaDoc[]{location};
212             final String JavaDoc message = getMessage( "CV020", args );
213             final VerifyIssue issue =
214                 new VerifyIssue( VerifyIssue.ERROR, message );
215             issues.add( issue );
216         }
217     }
218
219     /**
220      * Verify that the dependency metadata for component is valid.
221      *
222      * @param type the type
223      * @param issues the list of issues
224      */

225     void verifyDependencyMetaData( final Class JavaDoc type, final List JavaDoc issues )
226     {
227         final Attribute[] attributes = getDependencyAttributes( type );
228         for( int i = 0; i < attributes.length; i++ )
229         {
230             final Attribute attribute = attributes[ i ];
231             verifyDependencyMetaData( type, attribute, issues );
232         }
233     }
234
235     /**
236      * Return the dependency attributes for component.
237      * Made protected so that it is possible to overload
238      * in subclasses.
239      *
240      * @param type the component type
241      * @return the dependency attributes
242      */

243     protected Attribute[] getDependencyAttributes( final Class JavaDoc type )
244     {
245         try
246         {
247             final Method JavaDoc method = getComposeMethod( type );
248             return Attributes.getAttributes( method, "dna.dependency" );
249         }
250         catch( NoSuchMethodException JavaDoc e )
251         {
252             return Attribute.EMPTY_SET;
253         }
254     }
255
256     /**
257      * Return the method via which component is passed services.
258      * This method is protected so can overload in subclasses
259      * and get alternative methods.
260      *
261      * @param type the components type
262      * @return the method
263      * @throws NoSuchMethodException if can not locate method
264      */

265     protected Method JavaDoc getComposeMethod( final Class JavaDoc type )
266         throws NoSuchMethodException JavaDoc
267     {
268         if( !Composable.class.isAssignableFrom( type ) )
269         {
270             throw new NoSuchMethodException JavaDoc();
271         }
272         return type.getMethod( COMPOSE_METHOD_NAME, COMPOSE_PARAMETER_TYPES );
273     }
274
275     /**
276      * Verify that the dependency metadata tag is valid.
277      *
278      * @param type the component type
279      * @param attribute the metadata tag
280      * @param issues the list of issues
281      */

282     void verifyDependencyMetaData( final Class JavaDoc type,
283                                    final Attribute attribute,
284                                    final List JavaDoc issues )
285     {
286         final String JavaDoc optional = attribute.getParameter( "optional" );
287         verifyOptionalParameter( optional, issues );
288
289         final String JavaDoc typeName = attribute.getParameter( "type" );
290         if( null == typeName )
291         {
292             final Object JavaDoc[] args = new Object JavaDoc[]{"type"};
293             final String JavaDoc message = getMessage( "CV015", args );
294             final VerifyIssue issue =
295                 new VerifyIssue( VerifyIssue.ERROR, message );
296             issues.add( issue );
297         }
298         else
299         {
300             verifyDependencyType( type, typeName, issues );
301             final String JavaDoc key = attribute.getParameter( "key" );
302             if( null == key )
303             {
304                 final Object JavaDoc[] args = new Object JavaDoc[]{"key"};
305                 final String JavaDoc message = getMessage( "CV015", args );
306                 final VerifyIssue issue =
307                     new VerifyIssue( VerifyIssue.ERROR, message );
308                 issues.add( issue );
309             }
310             else
311             {
312                 verifyDependencyKeyConforms( typeName, key, issues );
313             }
314         }
315     }
316
317     /**
318      * Verify optional parameter for dependency metadata.
319      *
320      * @param optional the value of parameter
321      * @param issues the list of issues
322      */

323     void verifyOptionalParameter( final String JavaDoc optional, final List JavaDoc issues )
324     {
325         if( null == optional )
326         {
327             final Object JavaDoc[] args = new Object JavaDoc[]{"optional"};
328             final String JavaDoc message = getMessage( "CV015", args );
329             final VerifyIssue issue =
330                 new VerifyIssue( VerifyIssue.ERROR, message );
331             issues.add( issue );
332         }
333         else
334         {
335             verifyDependencyOptionalValid( optional, issues );
336         }
337     }
338
339     /**
340      * Verify that value of optional value is valid.
341      *
342      * @param optional the optional value
343      * @param issues the list of issues
344      */

345     void verifyDependencyOptionalValid( final String JavaDoc optional,
346                                         final List JavaDoc issues )
347     {
348         if( !optional.equals( "true" ) && !optional.equals( "false" ) )
349         {
350             final Object JavaDoc[] args = new Object JavaDoc[]{optional};
351             final String JavaDoc message = getMessage( "CV018", args );
352             final VerifyIssue issue =
353                 new VerifyIssue( VerifyIssue.ERROR, message );
354             issues.add( issue );
355         }
356     }
357
358     /**
359      * Verify that the key conforms to the expectation
360      * of being (type)[/(qualifier)]
361      *
362      * @param typeName the name of dependency type
363      * @param key the dependency key
364      * @param issues the list of issues
365      */

366     void verifyDependencyKeyConforms( final String JavaDoc typeName,
367                                       final String JavaDoc key,
368                                       final List JavaDoc issues )
369     {
370         final int typeLength = typeName.length();
371         final int keyLength = key.length();
372         final String JavaDoc prefix;
373         if( typeLength == keyLength )
374         {
375             prefix = typeName;
376         }
377         else
378         {
379             prefix = typeName + "/";
380         }
381         if( !key.startsWith( prefix ) )
382         {
383             final Object JavaDoc[] args = new Object JavaDoc[]{key};
384             final String JavaDoc message = getMessage( "CV017", args );
385             final VerifyIssue issue =
386                 new VerifyIssue( VerifyIssue.NOTICE, message );
387             issues.add( issue );
388         }
389     }
390
391     /**
392      * Verify that the type specified dependency can be loaded.
393      *
394      * @param type the component type
395      * @param typeName the type of dependency
396      * @param issues the list of issues
397      */

398     void verifyDependencyType( final Class JavaDoc type,
399                                final String JavaDoc typeName,
400                                final List JavaDoc issues )
401     {
402         try
403         {
404             type.getClassLoader().loadClass( typeName );
405         }
406         catch( final Throwable JavaDoc t )
407         {
408             final Object JavaDoc[] args = new Object JavaDoc[]{typeName, t};
409             final String JavaDoc message = getMessage( "CV016", args );
410             final VerifyIssue issue =
411                 new VerifyIssue( VerifyIssue.ERROR, message );
412             issues.add( issue );
413         }
414     }
415
416     /**
417      * Verify that the supplied implementation implements the specified
418      * services.
419      *
420      * @param implementation the class representign component
421      * @param services the services that the implementation must provide
422      * @param issues the list of issues
423      */

424     void verifyImplementsServices( final Class JavaDoc implementation,
425                                    final Class JavaDoc[] services,
426                                    final List JavaDoc issues )
427     {
428         for( int i = 0; i < services.length; i++ )
429         {
430             if( !services[ i ].isAssignableFrom( implementation ) )
431             {
432                 final Object JavaDoc[] args = new Object JavaDoc[]{services[ i ].getName()};
433                 final String JavaDoc message = getMessage( "CV002", args );
434                 final VerifyIssue issue =
435                     new VerifyIssue( VerifyIssue.ERROR, message );
436                 issues.add( issue );
437             }
438         }
439     }
440
441     /**
442      * Verify that the supplied class is a valid type for
443      * a component.
444      *
445      * @param type the class representing component
446      * @param issues the list of issues
447      */

448     void verifyClass( final Class JavaDoc type, final List JavaDoc issues )
449     {
450         verifyNoArgConstructor( type, issues );
451         verifyNonAbstract( type, issues );
452         verifyNonArray( type, issues );
453         verifyNonInterface( type, issues );
454         verifyNonPrimitive( type, issues );
455         verifyPublic( type, issues );
456     }
457
458     /**
459      * Verify that the supplied class is a valid class for
460      * a service.
461      *
462      * @param clazz the class representign service
463      * @param issues the list of issues
464      */

465     void verifyService( final Class JavaDoc clazz, final List JavaDoc issues )
466     {
467         verifyServiceIsaInterface( clazz, issues );
468         verifyServiceIsPublic( clazz, issues );
469         verifyServiceNotALifecycle( clazz, issues );
470     }
471
472     /**
473      * Verify that the service implemented by
474      * specified component is an interface.
475      *
476      * @param clazz the class representign service
477      * @param issues the list of issues
478      */

479     void verifyServiceIsaInterface( final Class JavaDoc clazz, final List JavaDoc issues )
480     {
481         if( !clazz.isInterface() )
482         {
483             final Object JavaDoc[] args = new Object JavaDoc[]{clazz.getName()};
484             final String JavaDoc message = getMessage( "CV004", args );
485             final VerifyIssue issue =
486                 new VerifyIssue( VerifyIssue.ERROR, message );
487             issues.add( issue );
488         }
489     }
490
491     /**
492      * Verify that the service implemented by
493      * specified component is public.
494      *
495      * @param clazz the class representign service
496      * @param issues the list of issues
497      */

498     void verifyServiceIsPublic( final Class JavaDoc clazz, final List JavaDoc issues )
499     {
500         final boolean isPublic =
501             Modifier.isPublic( clazz.getModifiers() );
502         if( !isPublic )
503         {
504             final Object JavaDoc[] args = new Object JavaDoc[]{clazz.getName()};
505             final String JavaDoc message = getMessage( "CV005", args );
506             final VerifyIssue issue =
507                 new VerifyIssue( VerifyIssue.ERROR, message );
508             issues.add( issue );
509         }
510     }
511
512     /**
513      * Verify that the service implemented by
514      * specified component does not extend any lifecycle interfaces.
515      *
516      * @param clazz the class representign service
517      * @param issues the list of issues
518      */

519     void verifyServiceNotALifecycle( final Class JavaDoc clazz, final List JavaDoc issues )
520     {
521         for( int i = 0; i < FRAMEWORK_CLASSES.length; i++ )
522         {
523             final Class JavaDoc lifecycle = FRAMEWORK_CLASSES[ i ];
524             if( lifecycle.isAssignableFrom( clazz ) )
525             {
526                 final Object JavaDoc[] args =
527                     new Object JavaDoc[]{clazz.getName(), lifecycle.getName()};
528                 final String JavaDoc message = getMessage( "CV006", args );
529                 final VerifyIssue issue =
530                     new VerifyIssue( VerifyIssue.ERROR, message );
531                 issues.add( issue );
532             }
533         }
534     }
535
536     /**
537      * Verify that the component has a no-arg aka default
538      * constructor.
539      *
540      * @param clazz the class representign component
541      * @param issues the list of issues
542      */

543     void verifyNoArgConstructor( final Class JavaDoc clazz, final List JavaDoc issues )
544     {
545         try
546         {
547             clazz.getConstructor( EMPTY_TYPES );
548         }
549         catch( final NoSuchMethodException JavaDoc nsme )
550         {
551             final String JavaDoc message = getMessage( "CV008" );
552             final VerifyIssue issue =
553                 new VerifyIssue( VerifyIssue.ERROR, message );
554             issues.add( issue );
555         }
556     }
557
558     /**
559      * Verify that the component is not represented by
560      * abstract class.
561      *
562      * @param clazz the class representign component
563      * @param issues the list of issues
564      */

565     void verifyNonAbstract( final Class JavaDoc clazz, final List JavaDoc issues )
566     {
567         final boolean isAbstract =
568             Modifier.isAbstract( clazz.getModifiers() );
569         if( isAbstract )
570         {
571             final String JavaDoc message = getMessage( "CV009" );
572             final VerifyIssue issue =
573                 new VerifyIssue( VerifyIssue.ERROR, message );
574             issues.add( issue );
575         }
576     }
577
578     /**
579      * Verify that the component is not represented by
580      * abstract class.
581      *
582      * @param clazz the class representign component
583      * @param issues the list of issues
584      */

585     void verifyPublic( final Class JavaDoc clazz, final List JavaDoc issues )
586     {
587         final boolean isPublic =
588             Modifier.isPublic( clazz.getModifiers() );
589         if( !isPublic )
590         {
591             final String JavaDoc message = getMessage( "CV010" );
592             final VerifyIssue issue =
593                 new VerifyIssue( VerifyIssue.ERROR, message );
594             issues.add( issue );
595         }
596     }
597
598     /**
599      * Verify that the component is not represented by
600      * primitive class.
601      *
602      * @param clazz the class representign component
603      * @param issues the list of issues
604      */

605     void verifyNonPrimitive( final Class JavaDoc clazz, final List JavaDoc issues )
606     {
607         if( clazz.isPrimitive() )
608         {
609             final String JavaDoc message = getMessage( "CV011" );
610             final VerifyIssue issue =
611                 new VerifyIssue( VerifyIssue.ERROR, message );
612             issues.add( issue );
613         }
614     }
615
616     /**
617      * Verify that the component is not represented by
618      * interface class.
619      *
620      * @param clazz the class representign component
621      * @param issues the list of issues
622      */

623     void verifyNonInterface( final Class JavaDoc clazz, final List JavaDoc issues )
624     {
625         if( clazz.isInterface() )
626         {
627             final String JavaDoc message = getMessage( "CV012" );
628             final VerifyIssue issue =
629                 new VerifyIssue( VerifyIssue.ERROR, message );
630             issues.add( issue );
631         }
632     }
633
634     /**
635      * Verify that the component is not represented by
636      * an array class.
637      *
638      * @param clazz the class representign component
639      * @param issues the list of issues
640      */

641     void verifyNonArray( final Class JavaDoc clazz, final List JavaDoc issues )
642     {
643         if( clazz.isArray() )
644         {
645             final String JavaDoc message = getMessage( "CV013" );
646             final VerifyIssue issue =
647                 new VerifyIssue( VerifyIssue.ERROR, message );
648             issues.add( issue );
649         }
650     }
651
652     /**
653      * Retrieve an array of Classes for all the services that a Component
654      * offers. This method also makes sure all services offered are
655      * interfaces.
656      *
657      * @param type the component type
658      * @param issues the list of issues
659      * @return an array of Classes for all the services
660      */

661     Class JavaDoc[] getServiceClasses( final Class JavaDoc type, final List JavaDoc issues )
662     {
663         final List JavaDoc services = new ArrayList JavaDoc();
664         final ClassLoader JavaDoc classLoader = type.getClassLoader();
665         final Attribute[] attributes =
666             Attributes.getAttributes( type, "dna.service" );
667         for( int i = 0; i < attributes.length; i++ )
668         {
669             final String JavaDoc classname = attributes[ i ].getParameter( "type" );
670             try
671             {
672                 final Class JavaDoc clazz = classLoader.loadClass( classname );
673                 services.add( clazz );
674             }
675             catch( final Throwable JavaDoc t )
676             {
677                 final Object JavaDoc[] args = new Object JavaDoc[]{classname, t};
678                 final String JavaDoc message = getMessage( "CV014", args );
679                 final VerifyIssue issue =
680                     new VerifyIssue( VerifyIssue.ERROR, message );
681                 issues.add( issue );
682             }
683         }
684
685         return (Class JavaDoc[])services.toArray( new Class JavaDoc[ services.size() ] );
686     }
687
688     /**
689      * Get message out of resource bundle with specified key.
690      *
691      * @param key the key
692      * @return the message
693      */

694     String JavaDoc getMessage( final String JavaDoc key )
695     {
696         return BUNDLE.getString( key );
697     }
698
699     /**
700      * Get message out of resource bungle with specified key
701      * and format wit specified arguments.
702      *
703      * @param key the keys
704      * @param args the arguments
705      * @return the message
706      */

707     String JavaDoc getMessage( final String JavaDoc key, final Object JavaDoc[] args )
708     {
709         final String JavaDoc pattern = getMessage( key );
710         return MessageFormat.format( pattern, args );
711     }
712 }
713
Popular Tags