You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@avalon.apache.org by do...@apache.org on 2002/06/06 03:55:21 UTC

cvs commit: jakarta-avalon-excalibur/containerkit/src/java/org/apache/excalibur/containerkit/verifier AssemblyVerifier.java Resources.properties

donaldp     2002/06/05 18:55:21

  Modified:    containerkit/src/java/org/apache/excalibur/containerkit/verifier
                        Resources.properties
  Added:       containerkit/src/java/org/apache/excalibur/containerkit/verifier
                        AssemblyVerifier.java
  Log:
  Move AssemblyVerifier to excalibur. Also updated all the i18n strings to conform to be in format "assembly.X.notice" or "assembly.X.error" and made them refer to generic Component assemblys rather than phoenix specifics
  
  Revision  Changes    Path
  1.3       +18 -0     jakarta-avalon-excalibur/containerkit/src/java/org/apache/excalibur/containerkit/verifier/Resources.properties
  
  Index: Resources.properties
  ===================================================================
  RCS file: /home/cvs/jakarta-avalon-excalibur/containerkit/src/java/org/apache/excalibur/containerkit/verifier/Resources.properties,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- Resources.properties	4 Jun 2002 08:33:53 -0000	1.2
  +++ Resources.properties	6 Jun 2002 01:55:21 -0000	1.3
  @@ -11,3 +11,21 @@
   verifier.incompat-serviceable.error=The implementation class {1} for component named "{0}" is both Serviceable and Composable (incompatible lifecycle interfaces).
   verifier.incompat-config.error=The implementation class {1} for component named "{0}" is both Configurable and Parameterizable (incompatible lifecycle interfaces).
   verifier.noimpl-service.error=The implementation class {1} for component named "{0}" does not implement the service interface {2} which it declares it supports.
  +
  +#Assembly Verifier
  +assembly.valid-names.notice=Verifying that the names specified for Component are valid.
  +assembly.unique-names.notice=Verifying that the names specified for the Components are unique.
  +assembly.dependencies-mapping.notice=Verifying that the dependency mapping is valid according to ComponentInfos.
  +assembly.dependency-references.notice=Verifying that the dependency mapping for every Component is valid with respect to other components.
  +assembly.nocircular-dependencies.notice=Verifying that there are no circular dependencies between Components.
  +assembly.component-type.notice=Verifying that the specified Components have valid types.
  +assembly.circular-dependency.error=Component named "{0}" has a circular dependency via path: {1}.
  +assembly.missing-dependency.error=Component "{1}" that satisfies the dependency with role "{0}" of Component "{2}" does not exist.
  +assembly.dependency-missing-service.error=Dependency "{0}" of Block "{2}" does not offer the required service "{1}".
  +assembly.bad-class.error=Unable to load class "{1}" for Component named "{0}". (Reason: {2}).
  +assembly.bad-name.error=The Component name "{0}" is invalid. Valid names contain only letters, digits and the '-' character.
  +assembly.duplicate-name.error=The name "{0}" is used by multiple Components in assembly.
  +assembly.unknown-dependency.error=Unknown dependency named "{0}" with role "{1}" declared for Component {2}.
  +assembly.unspecified-dependency.error=Dependency for role "{0}" not specified for the Component named "{1}".
  +assembly.bad-service-class.error=Unable to load service class "{1}" for Component named "{0}". (Reason: {2}).
  +assembly.service-not-interface.error=Service class "{1}" specified for Component named "{0}" is not an interface.
  
  
  
  1.1                  jakarta-avalon-excalibur/containerkit/src/java/org/apache/excalibur/containerkit/verifier/AssemblyVerifier.java
  
  Index: AssemblyVerifier.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included with this distribution in
   * the LICENSE.txt file.
   */
  package org.apache.excalibur.containerkit.verifier;
  
  import java.util.ArrayList;
  import java.util.Stack;
  import org.apache.avalon.excalibur.i18n.ResourceManager;
  import org.apache.avalon.excalibur.i18n.Resources;
  import org.apache.avalon.framework.logger.AbstractLogEnabled;
  import org.apache.avalon.framework.logger.Logger;
  import org.apache.excalibur.containerkit.metadata.ComponentMetaData;
  import org.apache.excalibur.containerkit.metadata.DependencyMetaData;
  import org.apache.excalibur.containerkit.metainfo.ComponentInfo;
  import org.apache.excalibur.containerkit.metainfo.DependencyDescriptor;
  import org.apache.excalibur.containerkit.metainfo.ServiceDescriptor;
  
  /**
   * This Class verifies that Sars are valid. It performs a number
   * of checks to make sure that the Sar represents a valid
   * application and excluding runtime errors will start up validly.
   * Some of the checks it performs include;
   *
   * <ul>
   *   <li>Verify names of Sar, Blocks and BlockListeners contain only
   *       letters, digits or the '_' character.</li>
   *   <li>Verify that the names of the Blocks and BlockListeners are
   *       unique to Sar.</li>
   *   <li>Verify that the dependendencies specified in assembly.xml
   *       correspond to dependencies specified in ComponentInfo files.</li>
   *   <li>Verify that the inter-block dependendencies specified in
   *       assembly.xml are valid. This essentially means that if
   *       Block A requires Service S from Block B then Block B must
   *       provide Service S.</li>
   *   <li>Verify that there are no circular dependendencies between
   *       blocks.</li>
   *   <li>Verify that the Class objects for Blocks support the Block
   *       interface and any specified Services.</li>
   *   <li>Verify that the Class objects for BlockListeners support the
   *       BlockListener interface.</li>
   * </ul>
   *
   * @author <a href="mailto:peter@apache.org">Peter Donald</a>
   * @version $Revision: 1.1 $ $Date: 2002/06/06 01:55:21 $
   * @todo redo documentation and all the i18n strings
   */
  public class AssemblyVerifier
      extends AbstractLogEnabled
  {
      private static final Resources REZ =
          ResourceManager.getPackageResources( AssemblyVerifier.class );
      private final Verifier m_verifier = new Verifier();
  
      public void enableLogging( Logger logger )
      {
          super.enableLogging( logger );
          setupLogger( m_verifier );
      }
  
      /**
       * Validate and Verify the specified assembly (ie organization
       * of components). See the Class Javadocs for the rules and
       * regulations of assembly.
       *
       * @param components the Components that make up assembly
       * @param classLoader the ClassLoader used to load types. This is used
       *                    to verify that specified Class objects exist and
       *                    implement the correct interfaces.
       * @throws VerifyException if an error occurs
       */
      public void verifySar( final ComponentMetaData[] components,
                             final ClassLoader classLoader )
          throws VerifyException
      {
          String message = null;
  
          message = REZ.getString( "assembly.valid-names.notice" );
          getLogger().info( message );
          verifyValidNames( components );
  
          message = REZ.getString( "assembly.unique-names.notice" );
          getLogger().info( message );
          checkNamesUnique( components );
  
          message = REZ.getString( "assembly.dependencies-mapping.notice" );
          getLogger().info( message );
          verifyValidDependencies( components );
  
          message = REZ.getString( "assembly.dependency-references.notice" );
          getLogger().info( message );
          verifyDependencyReferences( components );
  
          message = REZ.getString( "assembly.nocircular-dependencies.notice" );
          getLogger().info( message );
          verifyNoCircularDependencies( components );
  
          message = REZ.getString( "assembly.component-type.notice" );
          getLogger().info( message );
          verifyTypes( components, classLoader );
      }
  
      /**
       * Verfiy that all Blocks have the needed dependencies specified correctly.
       *
       * @param blocks the ComponentMetaData objects for the blocks
       * @throws VerifyException if an error occurs
       */
      private void verifyValidDependencies( final ComponentMetaData[] blocks )
          throws VerifyException
      {
          for( int i = 0; i < blocks.length; i++ )
          {
              verifyDependenciesMap( blocks[ i ] );
          }
      }
  
      /**
       * Verfiy that there are no circular references between Components.
       *
       * @param components the ComponentMetaData objects for the components
       * @throws VerifyException if an error occurs
       */
      private void verifyNoCircularDependencies( final ComponentMetaData[] components )
          throws VerifyException
      {
          for( int i = 0; i < components.length; i++ )
          {
              final ComponentMetaData component = components[ i ];
  
              final Stack stack = new Stack();
              stack.push( component );
              verifyNoCircularDependencies( component, components, stack );
              stack.pop();
          }
      }
  
      /**
       * Verfiy that there are no circular references between Components.
       *
       * @param components the ComponentMetaData objects for the components
       * @throws VerifyException if an error occurs
       */
      private void verifyNoCircularDependencies( final ComponentMetaData component,
                                                 final ComponentMetaData[] components,
                                                 final Stack stack )
          throws VerifyException
      {
          final ComponentMetaData[] dependencies = getDependencies( component, components );
  
          for( int i = 0; i < dependencies.length; i++ )
          {
              final ComponentMetaData dependency = dependencies[ i ];
              if( stack.contains( dependency ) )
              {
                  final String trace = getDependencyTrace( dependency, stack );
                  final String message =
                      REZ.getString( "assembly.circular-dependency.error",
                                     component.getName(),
                                     trace );
                  throw new VerifyException( message );
              }
  
              stack.push( dependency );
              verifyNoCircularDependencies( dependency, components, stack );
              stack.pop();
          }
      }
  
      /**
       * Get a string defining path from top of stack till
       * it reaches specified component.
       *
       * @param component the component
       * @param stack the Stack
       * @return the path of dependency
       */
      private String getDependencyTrace( final ComponentMetaData component,
                                         final Stack stack )
      {
          final StringBuffer sb = new StringBuffer();
          sb.append( "[ " );
  
          final String name = component.getName();
          final int size = stack.size();
          final int top = size - 1;
          for( int i = top; i >= 0; i-- )
          {
              final ComponentMetaData other = (ComponentMetaData)stack.get( i );
              if( top != i )
              {
                  sb.append( ", " );
              }
              sb.append( other.getName() );
  
              if( other.getName().equals( name ) )
              {
                  break;
              }
          }
  
          sb.append( ", " );
          sb.append( name );
  
          sb.append( " ]" );
          return sb.toString();
      }
  
      /**
       * Get array of dependencies for specified Component from specified
       * Component array.
       *
       * @param component the component to get dependencies of
       * @param components the total set of components in application
       * @return the dependencies of component
       */
      private ComponentMetaData[] getDependencies( final ComponentMetaData component,
                                                   final ComponentMetaData[] components )
      {
          final ArrayList dependencies = new ArrayList();
          final DependencyMetaData[] deps = component.getDependencies();
  
          for( int i = 0; i < deps.length; i++ )
          {
              final String name = deps[ i ].getName();
              final ComponentMetaData other = getComponentMetaData( name, components );
              dependencies.add( other );
          }
  
          return (ComponentMetaData[])dependencies.toArray( new ComponentMetaData[ 0 ] );
      }
  
      /**
       * Verfiy that the inter-Component dependencies are valid.
       *
       * @param components the ComponentMetaData objects for the components
       * @throws VerifyException if an error occurs
       */
      private void verifyDependencyReferences( final ComponentMetaData[] components )
          throws VerifyException
      {
          for( int i = 0; i < components.length; i++ )
          {
              verifyDependencyReferences( components[ i ], components );
          }
      }
  
      /**
       * Verfiy that the inter-Component dependencies are valid for specified Component.
       *
       * @param component the ComponentMetaData object for the component
       * @param others the ComponentMetaData objects for the other components
       * @throws VerifyException if an error occurs
       */
      private void verifyDependencyReferences( final ComponentMetaData component,
                                               final ComponentMetaData[] others )
          throws VerifyException
      {
          final ComponentInfo info = component.getComponentInfo();
          final DependencyMetaData[] roles = component.getDependencies();
  
          for( int i = 0; i < roles.length; i++ )
          {
              final String providerName = roles[ i ].getName();
              final String roleName = roles[ i ].getRole();
              final ServiceDescriptor service =
                  info.getDependency( roleName ).getService();
  
              //Get the other block that is providing service
              final ComponentMetaData provider = getComponentMetaData( providerName, others );
              if( null == provider )
              {
                  final String message =
                      REZ.getString( "assembly.missing-dependency.error",
                                     roleName,
                                     providerName,
                                     component.getName() );
                  throw new VerifyException( message );
              }
  
              //make sure that the block offers service
              //that user expects it to be providing
              final ServiceDescriptor[] services = provider.getComponentInfo().getServices();
              if( !hasMatchingService( service, services ) )
              {
                  final String message =
                      REZ.getString( "assembly.dependency-missing-service.error",
                                     providerName,
                                     service,
                                     component.getName() );
                  throw new VerifyException( message );
              }
          }
      }
  
      /**
       * Get Block with specified name from specified Block array.
       *
       * @param name the name of block to get
       * @param components the array of Blocks to search
       * @return the Block if found, else null
       */
      private ComponentMetaData getComponentMetaData( final String name,
                                                      final ComponentMetaData[] components )
      {
          for( int i = 0; i < components.length; i++ )
          {
              if( components[ i ].getName().equals( name ) )
              {
                  return components[ i ];
              }
          }
  
          return null;
      }
  
      /**
       * Verfiy that all Components specify classes that implement the
       * advertised interfaces.
       *
       * @param components the ComponentMetaData objects for the components
       * @throws VerifyException if an error occurs
       */
      private void verifyTypes( final ComponentMetaData[] components,
                                final ClassLoader classLoader )
          throws VerifyException
      {
          for( int i = 0; i < components.length; i++ )
          {
              verifyType( components[ i ], classLoader );
          }
      }
  
      /**
       * Verfiy that specified Block designate classes that implement the
       * advertised interfaces.
       *
       * @param block the ComponentMetaData object for the blocks
       * @throws VerifyException if an error occurs
       */
      private void verifyType( final ComponentMetaData block, final ClassLoader classLoader )
          throws VerifyException
      {
          final String name = block.getName();
          final String classname = block.getClassname();
          Class clazz = null;
          try
          {
              clazz = classLoader.loadClass( classname );
          }
          catch( final Exception e )
          {
              final String message =
                  REZ.getString( "assembly.bad-class.error",
                                 name,
                                 classname,
                                 e.toString() );
              throw new VerifyException( message );
          }
  
          final Class[] interfaces =
              getServiceClasses( name,
                                 block.getComponentInfo().getServices(),
                                 classLoader );
  
          m_verifier.verifyComponent( name, clazz, interfaces );
      }
  
      /**
       * Verify that the names of the specified Components are valid.
       *
       * @param components the Components metadata
       * @throws VerifyException if an error occurs
       */
      private void verifyValidNames( final ComponentMetaData[] components )
          throws VerifyException
      {
          for( int i = 0; i < components.length; i++ )
          {
              final String name = components[ i ].getName();
              if( !isValidName( name ) )
              {
                  final String message =
                      REZ.getString( "assembly.bad-name.error", name );
                  throw new VerifyException( message );
              }
          }
      }
  
      /**
       * Return true if specified name is valid.
       * Valid names consist of letters, digits or the '_' character.
       *
       * @param name the name to check
       * @return true if valid, false otherwise
       */
      private boolean isValidName( final String name )
      {
          final int size = name.length();
          for( int i = 0; i < size; i++ )
          {
              final char ch = name.charAt( i );
  
              if( !Character.isLetterOrDigit( ch ) && '-' != ch )
              {
                  return false;
              }
          }
  
          return true;
      }
  
      /**
       * Verify that the names of the specified blocks and listeners are unique.
       * It is not valid for the same name to be used in multiple Blocks and or
       * BlockListeners.
       *
       * @param components the Components
       * @throws VerifyException if an error occurs
       */
      private void checkNamesUnique( final ComponentMetaData[] components )
          throws VerifyException
      {
          for( int i = 0; i < components.length; i++ )
          {
              final String name = components[ i ].getName();
              verifyUniqueName( components, name, i );
          }
      }
  
      /**
       * Verfify that specified name is unique among the specified components.
       *
       * @param components the array of components to check
       * @param name the name of component
       * @param index the index of component in array (so we can skip it)
       * @throws VerifyException if names are not unique
       */
      private void verifyUniqueName( final ComponentMetaData[] components,
                                     final String name,
                                     final int index )
          throws VerifyException
      {
          for( int i = 0; i < components.length; i++ )
          {
              final String other = components[ i ].getName();
              if( index != i && other.equals( name ) )
              {
                  final String message =
                      REZ.getString( "assembly.duplicate-name.error", name );
                  throw new VerifyException( message );
              }
          }
      }
  
      /**
       * Retrieve a list of DependencyMetaData objects for ComponentMetaData
       * and verify that there is a 1 to 1 map with dependencies specified
       * in ComponentInfo.
       *
       * @param block the ComponentMetaData describing the block
       * @throws VerifyException if an error occurs
       */
      private void verifyDependenciesMap( final ComponentMetaData block )
          throws VerifyException
      {
          //Make sure all role entries specified in config file are valid
          final DependencyMetaData[] roles = block.getDependencies();
          for( int i = 0; i < roles.length; i++ )
          {
              final String roleName = roles[ i ].getRole();
              final DependencyDescriptor descriptor = block.getComponentInfo().getDependency( roleName );
  
              //If there is no dependency descriptor in ComponentInfo then
              //user has specified an uneeded dependency.
              if( null == descriptor )
              {
                  final String message =
                      REZ.getString( "assembly.unknown-dependency.error",
                                     roles[ i ].getName(),
                                     roleName,
                                     block.getName() );
                  throw new VerifyException( message );
              }
          }
  
          //Make sure all dependencies in ComponentInfo file are satisfied
          final DependencyDescriptor[] dependencies = block.getComponentInfo().getDependencies();
          for( int i = 0; i < dependencies.length; i++ )
          {
              final DependencyMetaData role = block.getDependency( dependencies[ i ].getRole() );
  
              //If there is no Role then the user has failed
              //to specify a needed dependency.
              if( null == role )
              {
                  final String message =
                      REZ.getString( "assembly.unspecified-dependency.error",
                                     dependencies[ i ].getRole(),
                                     block.getName() );
                  throw new VerifyException( message );
              }
          }
      }
  
      /**
       * Retrieve an array of Classes for all the services (+ the Block interface)
       * that a Block offers. This method also makes sure all services offered are
       * interfaces.
       *
       * @param name the name of block
       * @param services the services the Block offers
       * @param classLoader the classLoader
       * @return an array of Classes for all the services
       * @throws VerifyException if an error occurs
       */
      private Class[] getServiceClasses( final String name,
                                         final ServiceDescriptor[] services,
                                         final ClassLoader classLoader )
          throws VerifyException
      {
          final Class[] classes = new Class[ services.length ];
          for( int i = 0; i < services.length; i++ )
          {
              final String classname = services[ i ].getName();
              try
              {
                  classes[ i ] = classLoader.loadClass( classname );
              }
              catch( final Throwable t )
              {
                  final String message =
                      REZ.getString( "assembly.bad-service-class.error",
                                     name,
                                     classname,
                                     t.toString() );
                  throw new VerifyException( message, t );
              }
  
              if( !classes[ i ].isInterface() )
              {
                  final String message =
                      REZ.getString( "assembly.service-not-interface.error",
                                     name,
                                     classname );
                  throw new VerifyException( message );
              }
          }
  
          return classes;
      }
  
      /**
       * Return true if specified service matches any of the
       * candidate services.
       *
       * @param candidates an array of candidate services
       * @param service the service
       * @return true if candidate services contains a service that matches
       *         specified service, false otherwise
       */
      private boolean hasMatchingService( final ServiceDescriptor service,
                                          final ServiceDescriptor[] candidates )
      {
          for( int i = 0; i < candidates.length; i++ )
          {
              if( service.matches( candidates[ i ] ) )
              {
                  return true;
              }
          }
  
          return false;
      }
  }
  
  
  

--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>