KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > avalon > phoenix > tools > verifier > SarVerifier


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

8 package org.apache.avalon.phoenix.tools.verifier;
9
10 import java.util.ArrayList JavaDoc;
11 import java.util.Stack JavaDoc;
12 import org.apache.avalon.excalibur.i18n.ResourceManager;
13 import org.apache.avalon.excalibur.i18n.Resources;
14 import org.apache.avalon.framework.activity.Disposable;
15 import org.apache.avalon.framework.activity.Initializable;
16 import org.apache.avalon.framework.activity.Startable;
17 import org.apache.avalon.framework.component.Composable;
18 import org.apache.avalon.framework.configuration.Configurable;
19 import org.apache.avalon.framework.context.Contextualizable;
20 import org.apache.avalon.framework.logger.AbstractLogEnabled;
21 import org.apache.avalon.framework.logger.LogEnabled;
22 import org.apache.avalon.framework.parameters.Parameterizable;
23 import org.apache.avalon.framework.service.Serviceable;
24 import org.apache.avalon.phoenix.Block;
25 import org.apache.avalon.phoenix.BlockListener;
26 import org.apache.avalon.phoenix.metadata.BlockListenerMetaData;
27 import org.apache.avalon.phoenix.metadata.BlockMetaData;
28 import org.apache.avalon.phoenix.metadata.DependencyMetaData;
29 import org.apache.avalon.phoenix.metadata.SarMetaData;
30 import org.apache.avalon.phoenix.metainfo.BlockInfo;
31 import org.apache.avalon.phoenix.metainfo.DependencyDescriptor;
32 import org.apache.avalon.phoenix.metainfo.ServiceDescriptor;
33
34 /**
35  * This Class verifies that Sars are valid. It performs a number
36  * of checks to make sure that the Sar represents a valid
37  * application and excluding runtime errors will start up validly.
38  * Some of the checks it performs include;
39  *
40  * <ul>
41  * <li>Verify names of Sar, Blocks and BlockListeners contain only
42  * letters, digits or the '_' character.</li>
43  * <li>Verify that the names of the Blocks and BlockListeners are
44  * unique to Sar.</li>
45  * <li>Verify that the dependendencies specified in assembly.xml
46  * correspond to dependencies specified in BlockInfo files.</li>
47  * <li>Verify that the inter-block dependendencies specified in
48  * assembly.xml are valid. This essentially means that if
49  * Block A requires Service S from Block B then Block B must
50  * provide Service S.</li>
51  * <li>Verify that there are no circular dependendencies between
52  * blocks.</li>
53  * <li>Verify that the Class objects for Blocks support the Block
54  * interface and any specified Services.</li>
55  * <li>Verify that the Class objects for BlockListeners support the
56  * BlockListener interface.</li>
57  * </ul>
58  *
59  * @author <a HREF="mailto:peter at apache.org">Peter Donald</a>
60  * @version $Revision: 1.28.2.1 $ $Date: 2002/09/09 11:30:19 $
61  */

62 public class SarVerifier
63     extends AbstractLogEnabled
64 {
65     private static final Resources REZ =
66         ResourceManager.getPackageResources( SarVerifier.class );
67
68     private static final Class JavaDoc[] FRAMEWORK_CLASSES = new Class JavaDoc[]
69     {
70         LogEnabled.class,
71         Contextualizable.class,
72         Composable.class,
73         Serviceable.class,
74         Configurable.class,
75         Parameterizable.class,
76         Initializable.class,
77         Startable.class,
78         Disposable.class
79     };
80
81     /**
82      * Verify the specified {@link SarMetaData} object.
83      * The rules used to verify {@link SarMetaData} are specified
84      * in the Class javadocs.
85      *
86      * @param sar the SarMetaDat object
87      * @param classLoader the ClassLoader used to load types. This is used
88      * to verify that specified Class objects exist and
89      * implement the correct interfaces.
90      * @throws VerifyException if an error occurs
91      */

92     public void verifySar( final SarMetaData sar, final ClassLoader JavaDoc classLoader )
93         throws VerifyException
94     {
95         final BlockMetaData[] blocks = sar.getBlocks();
96         final BlockListenerMetaData[] listeners = sar.getListeners();
97
98         String JavaDoc message = null;
99
100         message = REZ.getString( "verify-valid-names" );
101         getLogger().info( message );
102         verifySarName( sar.getName() );
103         verifyValidNames( blocks );
104         verifyValidNames( listeners );
105
106         message = REZ.getString( "verify-unique-names" );
107         getLogger().info( message );
108         checkNamesUnique( blocks, listeners );
109
110         message = REZ.getString( "verify-dependencies-mapping" );
111         getLogger().info( message );
112         verifyValidDependencies( blocks );
113
114         message = REZ.getString( "verify-dependency-references" );
115         getLogger().info( message );
116         verifyDependencyReferences( blocks );
117
118         message = REZ.getString( "verify-nocircular-dependencies" );
119         getLogger().info( message );
120         verifyNoCircularDependencies( blocks );
121
122         message = REZ.getString( "verify-block-type" );
123         getLogger().info( message );
124         verifyBlocksType( blocks, classLoader );
125
126         message = REZ.getString( "verify-listener-type" );
127         getLogger().info( message );
128         verifyListenersType( listeners, classLoader );
129     }
130
131     /**
132      * Verfiy that all Blocks have the needed dependencies specified correctly.
133      *
134      * @param blocks the BlockMetaData objects for the blocks
135      * @throws VerifyException if an error occurs
136      */

137     private void verifyValidDependencies( final BlockMetaData[] blocks )
138         throws VerifyException
139     {
140         for( int i = 0; i < blocks.length; i++ )
141         {
142             verifyDependenciesMap( blocks[ i ] );
143         }
144     }
145
146     /**
147      * Verfiy that there are no circular references between Blocks.
148      *
149      * @param blocks the BlockMetaData objects for the blocks
150      * @throws VerifyException if an error occurs
151      */

152     private void verifyNoCircularDependencies( final BlockMetaData[] blocks )
153         throws VerifyException
154     {
155         for( int i = 0; i < blocks.length; i++ )
156         {
157             final BlockMetaData block = blocks[ i ];
158
159             final Stack JavaDoc stack = new Stack JavaDoc();
160             stack.push( block );
161             verifyNoCircularDependencies( block, blocks, stack );
162             stack.pop();
163         }
164     }
165
166     /**
167      * Verfiy that there are no circular references between Blocks.
168      *
169      * @param blocks the BlockMetaData objects for the blocks
170      * @throws VerifyException if an error occurs
171      */

172     private void verifyNoCircularDependencies( final BlockMetaData block,
173                                                final BlockMetaData[] blocks,
174                                                final Stack JavaDoc stack )
175         throws VerifyException
176     {
177         final BlockMetaData[] dependencies = getDependencies( block, blocks );
178
179         for( int i = 0; i < dependencies.length; i++ )
180         {
181             final BlockMetaData dependency = dependencies[ i ];
182             if( stack.contains( dependency ) )
183             {
184                 final String JavaDoc trace = getDependencyTrace( dependency, stack );
185                 final String JavaDoc message =
186                     REZ.getString( "dependency-circular", block.getName(), trace );
187                 throw new VerifyException( message );
188             }
189
190             stack.push( dependency );
191             verifyNoCircularDependencies( dependency, blocks, stack );
192             stack.pop();
193         }
194     }
195
196     /**
197      * Get a string defining path from top of stack till it reaches specified block.
198      *
199      * @param block the block
200      * @param stack the Stack
201      * @return the path of dependency
202      */

203     private String JavaDoc getDependencyTrace( final BlockMetaData block,
204                                        final Stack JavaDoc stack )
205     {
206         final StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
207         sb.append( "[ " );
208
209         final String JavaDoc name = block.getName();
210         final int size = stack.size();
211         final int top = size - 1;
212         for( int i = top; i >= 0; i-- )
213         {
214             final BlockMetaData other = (BlockMetaData)stack.get( i );
215             if( top != i )
216             {
217                 sb.append( ", " );
218             }
219             sb.append( other.getName() );
220
221             if( other.getName().equals( name ) )
222             {
223                 break;
224             }
225         }
226
227         sb.append( ", " );
228         sb.append( name );
229
230         sb.append( " ]" );
231         return sb.toString();
232     }
233
234     /**
235      * Get array of dependencies for specified Block from specified Block array.
236      *
237      * @param block the block to get dependencies of
238      * @param blocks the total set of blocks in application
239      * @return the dependencies of block
240      */

241     private BlockMetaData[] getDependencies( final BlockMetaData block,
242                                              final BlockMetaData[] blocks )
243     {
244         final ArrayList JavaDoc dependencies = new ArrayList JavaDoc();
245         final DependencyMetaData[] deps = block.getDependencies();
246
247         for( int i = 0; i < deps.length; i++ )
248         {
249             final String JavaDoc name = deps[ i ].getName();
250             final BlockMetaData other = getBlock( name, blocks );
251             dependencies.add( other );
252         }
253
254         return (BlockMetaData[])dependencies.toArray( new BlockMetaData[ 0 ] );
255     }
256
257     /**
258      * Verfiy that the inter-Block dependencies are valid.
259      *
260      * @param blocks the BlockMetaData objects for the blocks
261      * @throws VerifyException if an error occurs
262      */

263     private void verifyDependencyReferences( final BlockMetaData[] blocks )
264         throws VerifyException
265     {
266         for( int i = 0; i < blocks.length; i++ )
267         {
268             verifyDependencyReferences( blocks[ i ], blocks );
269         }
270     }
271
272     /**
273      * Verfiy that the inter-Block dependencies are valid for specified Block.
274      *
275      * @param block the BlockMetaData object for the block
276      * @param others the BlockMetaData objects for the other blocks
277      * @throws VerifyException if an error occurs
278      */

279     private void verifyDependencyReferences( final BlockMetaData block,
280                                              final BlockMetaData[] others )
281         throws VerifyException
282     {
283         final BlockInfo info = block.getBlockInfo();
284         final DependencyMetaData[] roles = block.getDependencies();
285
286         for( int i = 0; i < roles.length; i++ )
287         {
288             final String JavaDoc blockName = roles[ i ].getName();
289             final String JavaDoc roleName = roles[ i ].getRole();
290             final ServiceDescriptor service =
291                 info.getDependency( roleName ).getService();
292
293             //Get the other block that is providing service
294
final BlockMetaData other = getBlock( blockName, others );
295             if( null == other )
296             {
297                 final String JavaDoc message =
298                     REZ.getString( "dependency-noblock", blockName, block.getName() );
299                 throw new VerifyException( message );
300             }
301
302             //make sure that the block offers service
303
//that user expects it to be providing
304
final ServiceDescriptor[] services = other.getBlockInfo().getServices();
305             if( !hasMatchingService( service, services ) )
306             {
307                 final String JavaDoc message =
308                     REZ.getString( "dependency-noservice", blockName, service, block.getName() );
309                 throw new VerifyException( message );
310             }
311         }
312     }
313
314     /**
315      * Get Block with specified name from specified Block array.
316      *
317      * @param name the name of block to get
318      * @param blocks the array of Blocks to search
319      * @return the Block if found, else null
320      */

321     private BlockMetaData getBlock( final String JavaDoc name, final BlockMetaData[] blocks )
322     {
323         for( int i = 0; i < blocks.length; i++ )
324         {
325             if( blocks[ i ].getName().equals( name ) )
326             {
327                 return blocks[ i ];
328             }
329         }
330
331         return null;
332     }
333
334     /**
335      * Verfiy that all Blocks specify classes that implement the
336      * advertised interfaces.
337      *
338      * @param blocks the BlockMetaData objects for the blocks
339      * @throws VerifyException if an error occurs
340      */

341     private void verifyBlocksType( final BlockMetaData[] blocks, final ClassLoader JavaDoc classLoader )
342         throws VerifyException
343     {
344         for( int i = 0; i < blocks.length; i++ )
345         {
346             verifyBlockType( blocks[ i ], classLoader );
347         }
348     }
349
350     /**
351      * Verfiy that specified Block designate classes that implement the
352      * advertised interfaces.
353      *
354      * @param block the BlockMetaData object for the blocks
355      * @throws VerifyException if an error occurs
356      */

357     private void verifyBlockType( final BlockMetaData block, final ClassLoader JavaDoc classLoader )
358         throws VerifyException
359     {
360         final String JavaDoc name = block.getName();
361         final String JavaDoc classname = block.getImplementationKey();
362         Class JavaDoc clazz = null;
363         try
364         {
365             clazz = classLoader.loadClass( classname );
366         }
367         catch( final Exception JavaDoc e )
368         {
369             final String JavaDoc message = REZ.getString( "bad-block-class",
370                                                   name,
371                                                   classname,
372                                                   e.getMessage() );
373             throw new VerifyException( message );
374         }
375
376         final Class JavaDoc[] interfaces =
377             getServiceClasses( name,
378                                block.getBlockInfo().getServices(),
379                                classLoader );
380
381         for( int i = 0; i < interfaces.length; i++ )
382         {
383             if( !interfaces[ i ].isAssignableFrom( clazz ) )
384             {
385                 final String JavaDoc message = REZ.getString( "block-noimpl-service",
386                                                       name,
387                                                       classname,
388                                                       interfaces[ i ].getName() );
389                 throw new VerifyException( message );
390             }
391         }
392
393         if( Block.class.isAssignableFrom( clazz ) )
394         {
395             final String JavaDoc message =
396                 REZ.getString( "verifier.implements-block.error",
397                                name,
398                                classname );
399             getLogger().error( message );
400             System.err.println( message );
401         }
402     }
403
404     /**
405      * Verfiy that all listeners implement BlockListener.
406      *
407      * @param listeners the BlockListenerMetaData objects for the listeners
408      * @throws VerifyException if an error occurs
409      */

410     private void verifyListenersType( final BlockListenerMetaData[] listeners,
411                                       final ClassLoader JavaDoc classLoader )
412         throws VerifyException
413     {
414         for( int i = 0; i < listeners.length; i++ )
415         {
416             verifyListenerType( listeners[ i ], classLoader );
417         }
418     }
419
420     /**
421      * Verfiy that specified Listener class implements the BlockListener interface.
422      *
423      * @param listener the BlockListenerMetaData object for the listener
424      * @throws VerifyException if an error occurs
425      */

426     private void verifyListenerType( final BlockListenerMetaData listener,
427                                      final ClassLoader JavaDoc classLoader )
428         throws VerifyException
429     {
430         Class JavaDoc clazz = null;
431         try
432         {
433             clazz = classLoader.loadClass( listener.getClassname() );
434         }
435         catch( final Exception JavaDoc e )
436         {
437             final String JavaDoc message =
438                 REZ.getString( "bad-listener-class",
439                                listener.getName(),
440                                listener.getClassname(),
441                                e.getMessage() );
442             throw new VerifyException( message, e );
443         }
444
445         if( !BlockListener.class.isAssignableFrom( clazz ) )
446         {
447             final String JavaDoc message = REZ.getString( "listener-noimpl-listener",
448                                                   listener.getName(),
449                                                   listener.getClassname() );
450             throw new VerifyException( message );
451         }
452     }
453
454     /**
455      * Verify that the Sat name specified is valid.
456      *
457      * @param name the sar name
458      * @throws VerifyException if an error occurs
459      */

460     private void verifySarName( final String JavaDoc name )
461         throws VerifyException
462     {
463         if( !isValidName( name ) )
464         {
465             final String JavaDoc message = REZ.getString( "invalid-sar-name", name );
466             throw new VerifyException( message );
467         }
468     }
469
470     /**
471      * Verify that the names of the specified blocks are valid.
472      *
473      * @param blocks the Blocks
474      * @throws VerifyException if an error occurs
475      */

476     private void verifyValidNames( final BlockMetaData[] blocks )
477         throws VerifyException
478     {
479         for( int i = 0; i < blocks.length; i++ )
480         {
481             final String JavaDoc name = blocks[ i ].getName();
482             if( !isValidName( name ) )
483             {
484                 final String JavaDoc message = REZ.getString( "invalid-block-name", name );
485                 throw new VerifyException( message );
486             }
487         }
488     }
489
490     /**
491      * Verify that the names of the specified listeners are valid.
492      *
493      * @param listeners the listeners
494      * @throws VerifyException if an error occurs
495      */

496     private void verifyValidNames( final BlockListenerMetaData[] listeners )
497         throws VerifyException
498     {
499         for( int i = 0; i < listeners.length; i++ )
500         {
501             final String JavaDoc name = listeners[ i ].getName();
502             if( !isValidName( name ) )
503             {
504                 final String JavaDoc message = REZ.getString( "invalid-listener-name", name );
505                 throw new VerifyException( message );
506             }
507         }
508     }
509
510     /**
511      * Return true if specified name is valid.
512      * Valid names consist of letters, digits or the '-' & '.' characters.
513      *
514      * @param name the name to check
515      * @return true if valid, false otherwise
516      */

517     private boolean isValidName( final String JavaDoc name )
518     {
519         final int size = name.length();
520         for( int i = 0; i < size; i++ )
521         {
522             final char ch = name.charAt( i );
523
524             if( !Character.isLetterOrDigit( ch ) && '-' != ch && '.' != ch )
525             {
526                 return false;
527             }
528         }
529
530         return true;
531     }
532
533     /**
534      * Verify that the names of the specified blocks and listeners are unique.
535      * It is not valid for the same name to be used in multiple Blocks and or
536      * BlockListeners.
537      *
538      * @param blocks the Blocks
539      * @param listeners the listeners
540      * @throws VerifyException if an error occurs
541      */

542     private void checkNamesUnique( final BlockMetaData[] blocks,
543                                    final BlockListenerMetaData[] listeners )
544         throws VerifyException
545     {
546         for( int i = 0; i < blocks.length; i++ )
547         {
548             final String JavaDoc name = blocks[ i ].getName();
549             checkNameUnique( name, blocks, listeners, i, -1 );
550         }
551
552         for( int i = 0; i < listeners.length; i++ )
553         {
554             final String JavaDoc name = listeners[ i ].getName();
555             checkNameUnique( name, blocks, listeners, -1, i );
556         }
557     }
558
559     /**
560      * Verify that the specified name is unique among specified blocks
561      * and listeners except for those indexes specified.
562      *
563      * @param name the name to check for
564      * @param blocks the Blocks
565      * @param listeners the listeners
566      * @param blockIndex the index of block that is allowed to
567      * match in name (or -1 if name designates a listener)
568      * @param listenerIndex the index of listener that is allowed to
569      * match in name (or -1 if name designates a block)
570      * @throws VerifyException if an error occurs
571      */

572     private void checkNameUnique( final String JavaDoc name,
573                                   final BlockMetaData[] blocks,
574                                   final BlockListenerMetaData[] listeners,
575                                   final int blockIndex,
576                                   final int listenerIndex )
577         throws VerifyException
578     {
579         //Verify no blocks have the same name
580
for( int i = 0; i < blocks.length; i++ )
581         {
582             final String JavaDoc other = blocks[ i ].getName();
583             if( blockIndex != i && name.equals( other ) )
584             {
585                 final String JavaDoc message = REZ.getString( "duplicate-name", name );
586                 throw new VerifyException( message );
587             }
588         }
589
590         //Verify no listeners have the same name
591
for( int i = 0; i < listeners.length; i++ )
592         {
593             final String JavaDoc other = listeners[ i ].getName();
594             if( listenerIndex != i && name.equals( other ) )
595             {
596                 final String JavaDoc message = REZ.getString( "duplicate-name", name );
597                 throw new VerifyException( message );
598             }
599         }
600     }
601
602     /**
603      * Retrieve a list of DependencyMetaData objects for BlockMetaData
604      * and verify that there is a 1 to 1 map with dependencies specified
605      * in BlockInfo.
606      *
607      * @param block the BlockMetaData describing the block
608      * @throws VerifyException if an error occurs
609      */

610     private void verifyDependenciesMap( final BlockMetaData block )
611         throws VerifyException
612     {
613         //Make sure all role entries specified in config file are valid
614
final DependencyMetaData[] roles = block.getDependencies();
615         for( int i = 0; i < roles.length; i++ )
616         {
617             final String JavaDoc roleName = roles[ i ].getRole();
618             final DependencyDescriptor descriptor = block.getBlockInfo().getDependency( roleName );
619
620             //If there is no dependency descriptor in BlockInfo then
621
//user has specified an uneeded dependency.
622
if( null == descriptor )
623             {
624                 final String JavaDoc message = REZ.getString( "unknown-dependency",
625                                                       roles[ i ].getName(),
626                                                       roleName,
627                                                       block.getName() );
628                 throw new VerifyException( message );
629             }
630         }
631
632         //Make sure all dependencies in BlockInfo file are satisfied
633
final DependencyDescriptor[] dependencies = block.getBlockInfo().getDependencies();
634         for( int i = 0; i < dependencies.length; i++ )
635         {
636             final DependencyMetaData role = block.getDependency( dependencies[ i ].getRole() );
637
638             //If there is no Role then the user has failed
639
//to specify a needed dependency.
640
if( null == role )
641             {
642                 final String JavaDoc message = REZ.getString( "unspecified-dependency",
643                                                       dependencies[ i ].getRole(),
644                                                       block.getName() );
645                 throw new VerifyException( message );
646             }
647         }
648     }
649
650     /**
651      * Retrieve an array of Classes for all the services (+ the Block interface)
652      * that a Block offers. This method also makes sure all services offered are
653      * interfaces.
654      *
655      * @param name the name of block
656      * @param services the services the Block offers
657      * @param classLoader the classLoader
658      * @return an array of Classes for all the services
659      * @throws VerifyException if an error occurs
660      */

661     private Class JavaDoc[] getServiceClasses( final String JavaDoc name,
662                                        final ServiceDescriptor[] services,
663                                        final ClassLoader JavaDoc classLoader )
664         throws VerifyException
665     {
666         final Class JavaDoc[] classes = new Class JavaDoc[ services.length ];
667
668         for( int i = 0; i < services.length; i++ )
669         {
670             final String JavaDoc classname = services[ i ].getName();
671             try
672             {
673                 classes[ i ] = classLoader.loadClass( classname );
674             }
675             catch( final Throwable JavaDoc t )
676             {
677                 final String JavaDoc message =
678                     REZ.getString( "bad-service-class", name, classname, t.getMessage() );
679                 throw new VerifyException( message, t );
680             }
681
682             if( !classes[ i ].isInterface() )
683             {
684                 final String JavaDoc message =
685                     REZ.getString( "service-not-interface", name, classname );
686                 throw new VerifyException( message );
687             }
688
689             checkNotFrameworkInterface( name, classname, classes[ i ] );
690         }
691
692         return classes;
693     }
694
695     /**
696      * Warn the user if any of the service interfaces extend
697      * a Lifecycle interface (a generally unrecomended approach).
698      *
699      * @param name the name of block
700      * @param classname the classname of block
701      * @param clazz the service implemented by block
702      */

703     private void checkNotFrameworkInterface( final String JavaDoc name,
704                                              final String JavaDoc classname,
705                                              final Class JavaDoc clazz )
706     {
707         for( int i = 0; i < FRAMEWORK_CLASSES.length; i++ )
708         {
709             final Class JavaDoc lifecycle = FRAMEWORK_CLASSES[ i ];
710             if( lifecycle.isAssignableFrom( clazz ) )
711             {
712                 final String JavaDoc message =
713                     REZ.getString( "verifier.service-isa-lifecycle.error",
714                                    name,
715                                    classname,
716                                    clazz.getName(),
717                                    lifecycle.getName() );
718                 getLogger().warn( message );
719                 System.err.println( message );
720             }
721         }
722     }
723
724     /**
725      * Return true if specified service matches any of the
726      * candidate services.
727      *
728      * @param candidates an array of candidate services
729      * @param service the service
730      * @return true if candidate services contains a service that matches
731      * specified service, false otherwise
732      */

733     private boolean hasMatchingService( final ServiceDescriptor service,
734                                         final ServiceDescriptor[] candidates )
735     {
736         for( int i = 0; i < candidates.length; i++ )
737         {
738             if( service.matches( candidates[ i ] ) )
739             {
740                 return true;
741             }
742         }
743
744         return false;
745     }
746 }
747
Popular Tags