You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@avalon.apache.org by mc...@apache.org on 2004/02/17 12:06:17 UTC

cvs commit: avalon/repository/main/src/java/org/apache/avalon/repository/main DefaultInitialContextFactory.java AbstractBuilder.java DefaultBuilder.java DefaultInitialContext.java

mcconnell    2004/02/17 03:06:17

  Modified:    repository/main/src/java/org/apache/avalon/repository/main
                        AbstractBuilder.java DefaultBuilder.java
                        DefaultInitialContext.java
  Added:       repository/main/src/java/org/apache/avalon/repository/main
                        DefaultInitialContextFactory.java
  Log:
  Add support for an initial context factory that exposes the application root directory. This provides sufficent info for dealing with properties management with an IDE.
  
  Revision  Changes    Path
  1.7       +2 -2      avalon/repository/main/src/java/org/apache/avalon/repository/main/AbstractBuilder.java
  
  Index: AbstractBuilder.java
  ===================================================================
  RCS file: /home/cvs/avalon/repository/main/src/java/org/apache/avalon/repository/main/AbstractBuilder.java,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- AbstractBuilder.java	31 Jan 2004 13:29:50 -0000	1.6
  +++ AbstractBuilder.java	17 Feb 2004 11:06:16 -0000	1.7
  @@ -86,7 +86,7 @@
       * </ul>
       * 
       * @param classloader the classloader
  -    * @param factory the the factory classname
  +    * @param clazz the the factory class
       * @param context the inital repository context
       * @return the instantiated factory
       * @exception RepositoryException if a factory creation error occurs
  
  
  
  1.13      +2 -2      avalon/repository/main/src/java/org/apache/avalon/repository/main/DefaultBuilder.java
  
  Index: DefaultBuilder.java
  ===================================================================
  RCS file: /home/cvs/avalon/repository/main/src/java/org/apache/avalon/repository/main/DefaultBuilder.java,v
  retrieving revision 1.12
  retrieving revision 1.13
  diff -u -r1.12 -r1.13
  --- DefaultBuilder.java	31 Jan 2004 13:29:50 -0000	1.12
  +++ DefaultBuilder.java	17 Feb 2004 11:06:16 -0000	1.13
  @@ -314,7 +314,7 @@
       * @return the delegate factory
       * @exception RepositoryRuntimeException if the declared class does 
       *    not implement the factory interface
  -    * @see getFactoryClass
  +    * @see #getFactoryClass
       */
       public Factory getFactory()
       {
  
  
  
  1.18      +112 -29   avalon/repository/main/src/java/org/apache/avalon/repository/main/DefaultInitialContext.java
  
  Index: DefaultInitialContext.java
  ===================================================================
  RCS file: /home/cvs/avalon/repository/main/src/java/org/apache/avalon/repository/main/DefaultInitialContext.java,v
  retrieving revision 1.17
  retrieving revision 1.18
  diff -u -r1.17 -r1.18
  --- DefaultInitialContext.java	2 Feb 2004 00:41:24 -0000	1.17
  +++ DefaultInitialContext.java	17 Feb 2004 11:06:16 -0000	1.18
  @@ -234,6 +234,8 @@
       /**
        * Creates an initial repository context.
        *
  +     * @param base the base working directory
  +     * @param loader the parent classloader
        * @param artifact an artifact referencing the default implementation
        * @param cache the cache directory
        * @param hosts a set of initial remote repository addresses 
  @@ -244,14 +246,8 @@
         throws RepositoryException
       {
           m_base = setupBaseDirectory( base );
  -        Properties avalonSystem = 
  -          getLocalProperties( getAvalonHome(), AVALON_PROPERTIES );
  -        Properties avalonHome = 
  -          getLocalProperties( USER_HOME, AVALON_PROPERTIES );
  -        Properties avalonWork = 
  -          getLocalProperties( m_base, AVALON_PROPERTIES );
  -        m_cache = setupCache( cache, avalonSystem, avalonHome, avalonWork );
  -        m_hosts = setupHosts( hosts, avalonSystem, avalonHome, avalonWork );
  +        m_cache = setupCache( cache, base );
  +        m_hosts = setupHosts( hosts, base );
   
           Artifact implementation = setupImplementation( artifact );
           ClassLoader parent = setupClassLoader( loader );
  @@ -326,6 +322,88 @@
               throw new RepositoryException( buffer.toString(), e );
           }
       }
  +
  +    /**
  +     * Creates an initial repository context.
  +     *
  +     * @param artifact an artifact referencing the default implementation
  +     * @param cache the cache directory
  +     * @param hosts a set of initial remote repository addresses 
  +     * @throws RepositoryException if an error occurs during establishment
  +     */
  +    DefaultInitialContext( 
  +      ClassLoader parent, Artifact artifact, File base, File cache, String[] hosts ) 
  +      throws RepositoryException
  +    {
  +        if( null == base ) throw new NullPointerException( "base" ); 
  +        if( null == parent ) throw new NullPointerException( "parent" ); 
  +        if( null == artifact ) throw new NullPointerException( "artifact" ); 
  +        if( null == cache ) throw new NullPointerException( "cache" ); 
  +        if( null == hosts ) throw new NullPointerException( "hosts" ); 
  +
  +        m_base = base;
  +        m_cache = cache;
  +        m_hosts = hosts;
  +
  +        Attributes attributes = loadAttributes( m_cache, m_hosts, artifact );
  +        FactoryDescriptor descriptor = new FactoryDescriptor( attributes );
  +        String factory = descriptor.getFactory();
  +        if( null == factory ) 
  +        {
  +            final String error = 
  +              "Required property 'avalon.artifact.factory' not present in artifact: "
  +              + artifact + " under the active cache: [" + m_cache + "] using the "
  +              + "attribute sequence: " + attributes;
  +            throw new IllegalArgumentException( error );
  +        }
  +
  +        //
  +        // Grab all of the dependents in one hit because this is 
  +        // the implementation so we can ignore api/spi spread.
  +        //
  +
  +        Artifact[] dependencies = descriptor.getDependencies();
  +
  +        int n = dependencies.length;
  +        URL[] urls = new URL[ n + 1];
  +        for( int i=0; i<n; i++ )
  +        {
  +            urls[i] = LoaderUtils.getResource( 
  +              dependencies[i], m_hosts, m_cache, true );
  +        }
  +
  +        urls[ n ] = LoaderUtils.getResource( 
  +            artifact, m_hosts, m_cache, true );
  +
  +        //
  +        // create the classloader
  +        //
  +        
  +        ClassLoader classloader = new URLClassLoader( urls, parent );
  +        Class clazz = loadFactoryClass( classloader, factory );
  +
  +        //
  +        // load the actual repository implementation 
  +        //
  +
  +        try
  +        {
  +            m_factory = createDelegate( classloader, clazz, this );
  +        }
  +        catch( Throwable e )
  +        {
  +            final String error = 
  +              "Unable to establish a factory for the supplied artifact:";
  +            StringBuffer buffer = new StringBuffer( error );
  +            buffer.append( "\n artifact: " + artifact );
  +            buffer.append( "\n build: " + descriptor.getBuild() );
  +            buffer.append( "\n factory: " + descriptor.getFactory() );
  +            buffer.append( "\n source: " 
  +              + clazz.getProtectionDomain().getCodeSource().getLocation() );
  +            buffer.append( "\n cache: " + m_cache );
  +            throw new RepositoryException( buffer.toString(), e );
  +        }
  +    }
     
       // ------------------------------------------------------------------------
       // InitialContext
  @@ -395,7 +473,7 @@
       }
   
      /**
  -    * Install a block archive into the repository.
  +    * Install a block archive into the repository cache.
       * @param url the block archive url
       * @return the block manifest
       */
  @@ -567,18 +645,16 @@
           return DefaultInitialContext.class.getClassLoader();
       }
   
  -    private File setupCache( 
  -      File file, Properties system, Properties home, Properties work )
  +    private File setupCache( File cache, File base )
       {
  -        if( null != file ) return file;
  -        return setupDefaultCache( system, home, work );
  +        if( null != cache ) return cache;
  +        return setupDefaultCache( base );
       }
   
  -    private String[] setupHosts( 
  -      String[] hosts, Properties system, Properties home, Properties work )
  +    private String[] setupHosts( String[] hosts, File base )
       {
           if( null != hosts ) return RepositoryUtils.getCleanPaths( hosts );
  -        return setupDefaultHosts( system, home, work );
  +        return setupDefaultHosts( base );
       }
   
       private Artifact setupImplementation( Artifact artifact )
  @@ -622,7 +698,8 @@
       private static Artifact getDefaultImplementation()
       {
           Properties properties = createDefaultProperties();
  -        String spec = properties.getProperty( "avalon.repository.implementation" );
  +        String spec = properties.getProperty( 
  +          InitialContext.IMPLEMENTATION_KEY );
           if( null == spec )
           {
               final String error =
  @@ -638,23 +715,19 @@
           return getBaseDirectory();
       }
   
  -    private String[] setupDefaultHosts(
  -      Properties system, Properties home, Properties work )
  +    private String[] setupDefaultHosts( File base )
       {
  -        String systemValue = system.getProperty( HOSTS_KEY );
  -        String homeValue = home.getProperty( HOSTS_KEY, systemValue );
  -        String workValue = work.getProperty( HOSTS_KEY, homeValue );
  +        String homeValue = getUserProperties().getProperty( HOSTS_KEY );
  +        String workValue = getWorkProperties( base ).getProperty( HOSTS_KEY, homeValue );
           String value = System.getProperty( HOSTS_KEY , workValue );
           if( null == value ) return DEFAULT_INITIAL_HOSTS;
           return expandHosts( value );
       }
   
  -    private static File setupDefaultCache( 
  -      Properties system, Properties home, Properties work )
  +    private static File setupDefaultCache( File base )
       {
  -        String systemValue = system.getProperty( CACHE_KEY );
  -        String homeValue = home.getProperty( CACHE_KEY, systemValue );
  -        String workValue = work.getProperty( CACHE_KEY, homeValue );
  +        String homeValue = getUserProperties().getProperty( CACHE_KEY );
  +        String workValue = getWorkProperties( base ).getProperty( CACHE_KEY, homeValue );
           String value = System.getProperty( CACHE_KEY , workValue );
           if( null != value ) return new File( value  );
           return getDefaultCache();
  @@ -675,7 +748,7 @@
           return new File( System.getProperty( "user.dir" ) );
       }
   
  -    private Properties getLocalProperties( 
  +    private static Properties getLocalProperties( 
         File dir, String filename ) 
       {
           Properties properties = new Properties();
  @@ -705,5 +778,15 @@
               list.add( tokenizer.nextToken() );
           }
           return (String[]) list.toArray( new String[0] );
  +    }
  +
  +    private static Properties getUserProperties()
  +    {
  +        return getLocalProperties( USER_HOME, AVALON_PROPERTIES );
  +    }
  +
  +    private static Properties getWorkProperties( File base )
  +    {
  +        return getLocalProperties( base, AVALON_PROPERTIES );
       }
   }
  
  
  
  1.1                  avalon/repository/main/src/java/org/apache/avalon/repository/main/DefaultInitialContextFactory.java
  
  Index: DefaultInitialContextFactory.java
  ===================================================================
  /* 
   * Copyright 2004 Apache Software Foundation
   * Licensed  under the  Apache License,  Version 2.0  (the "License");
   * you may not use  this file  except in  compliance with the License.
   * You may obtain a copy of the License at 
   * 
   *   http://www.apache.org/licenses/LICENSE-2.0
   * 
   * Unless required by applicable law or agreed to in writing, software
   * distributed  under the  License is distributed on an "AS IS" BASIS,
   * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or
   * implied.
   * 
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package org.apache.avalon.repository.main;
  
  
  import java.io.File;
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.FileInputStream;
  import java.io.FileOutputStream;
  import java.lang.reflect.Constructor;
  import java.lang.NoSuchMethodException;
  import java.net.URL;
  import java.net.URLConnection;
  import java.net.URLClassLoader;
  import java.net.HttpURLConnection;
  import java.net.MalformedURLException;
  import java.net.JarURLConnection;
  import java.util.ArrayList;
  import java.util.Enumeration;
  import java.util.Map;
  import java.util.Properties;
  import java.text.ParseException;
  import java.util.StringTokenizer;
  import java.util.jar.Manifest;
  import java.util.jar.JarFile;
  import java.util.zip.ZipEntry;
  
  import javax.naming.NamingException;
  import javax.naming.NamingEnumeration;
  import javax.naming.directory.Attributes;
  
  import org.apache.avalon.repository.Artifact;
  import org.apache.avalon.repository.Repository;
  import org.apache.avalon.repository.RepositoryException;
  import org.apache.avalon.repository.RepositoryRuntimeException;
  import org.apache.avalon.repository.meta.FactoryDescriptor;
  import org.apache.avalon.repository.provider.Factory;
  import org.apache.avalon.repository.provider.InitialContext;
  import org.apache.avalon.repository.provider.InitialContextFactory;
  import org.apache.avalon.repository.provider.Builder;
  import org.apache.avalon.repository.util.LoaderUtils;
  import org.apache.avalon.repository.util.RepositoryUtils;
  
  import org.apache.avalon.util.env.Env;
  import org.apache.avalon.util.env.EnvAccessException;
  import org.apache.avalon.util.exception.ExceptionHelper;
  import org.apache.avalon.util.defaults.DefaultsBuilder;
  
  
  /**
   * A utility class used to establish a new {@link InitialContext}
   * instance. An initial context is normally created by simply
   * instantiating the factory using a application key and a working
   * directory. 
   *
   * <pre>
   * final String key = "demo";
   * final File work = new File( System.getProperty( "user.dir" ) );
   * final InitialContextFactory factory = 
   *   new DefaultInitialContextFactory( key, work );
   * InitialContext context = factory.createInitialContext();
   * </pre>
   * 
   * @author <a href="mailto:mcconnell@apache.org">Stephen McConnell</a>
   * @version $Revision: 1.1 $
   */
  public class DefaultInitialContextFactory implements InitialContextFactory
  {
      //------------------------------------------------------------------
      // private static 
      //------------------------------------------------------------------
  
     /**
      * The name of a properties resource contained within the repository 
      * bootstrap jar file.  Property values contained in this resource
      * consitute the most primative static default values.
      */
      private static final String AVALON_PROPERTIES = "avalon.properties";
  
      //------------------------------------------------------------------
      // immutable state 
      //------------------------------------------------------------------
  
      private final String m_key;
  
      private final File m_work;
  
      private final DefaultsBuilder m_defaults;
  
      private final Properties m_properties;
  
      //------------------------------------------------------------------
      // mutable state 
      //------------------------------------------------------------------
  
      private File m_cache;
  
      private Artifact m_artifact;
  
      private ClassLoader m_classloader;
  
      private String[] m_hosts;
  
      // ------------------------------------------------------------------------
      // constructor
      // ------------------------------------------------------------------------
  
      /**
       * <p>Creates an initial repository context factory.  The supplied 
       * key is used to establish the application root directory and 
       * property files at application, user and working directory 
       * levels.  A key such as 'merlin' will be transformed to the 
       * environment symbol 'MERLIN_HOME' (i.e. uppercase of key plus 
       * _HOME) and resolved to a value.  If the symbol is undefined, 
       * the application home directory defaults to a file path 
       * ${user.home}/.[key] (so for example, if MERLIN_HOME is 
       * undefined the default application home for Merlin is
       * ${user.home}/.merlin.  Based on the application root directory,
       * a set of property files with the name [key].properties are 
       * resolved from the following locations:</p>
       * 
       * <ul>
       *  <li>the current working directory</li>
       *  <li>user's home directory</li>
       *  <li>application home directory</li>
       * </ul>
       * 
       * <p>The order in which properties are evaluated in in accordance
       * the above list.  The current working directory properties take 
       * precedence over properties defined in the user's home directory
       * which in turn take precedence over properties defined under the 
       * application home directory.  System properties take precedence
       * over all properties.</p>
       *
       * @param key the application key
       * @param work the working directory
       * @throws IOException if an error occurs during establishment
       */
      public DefaultInitialContextFactory( String key, File work ) 
        throws IOException
      {
          m_key = key;
          m_work = work;
          m_defaults = new DefaultsBuilder( key, work );
          Properties defaults = getDefaultProperties();
          m_properties = 
            m_defaults.getConsolidatedProperties(  
              defaults, 
              KEYS );
  
          String spec = m_properties.getProperty( 
            InitialContext.IMPLEMENTATION_KEY );
          if( null != spec )
          {
              m_artifact = Artifact.createArtifact( spec );
          }
          else
          {
              final String error =
                "Required implementation key [" 
                + InitialContext.IMPLEMENTATION_KEY 
                + "] not found.";
              throw new IllegalStateException( error );
          }
      }
  
      // ------------------------------------------------------------------------
      // InitialContextFactory
      // ------------------------------------------------------------------------
  
     /**
      * Set the parent classloader.  If not defined, the default 
      * classloader is the classloader holding this class. 
      *
      * @param classloader the parent classloader
      */
      public void setParentClassLoader( ClassLoader classloader )
      {
          m_classloader = classloader;
      }
  
     /**
      * The initial context factory support the establishment of an 
      * initial context which is associated with a repository cache 
      * manager implementation.  A client can override the default
      * repository cache manager implementation by declaring an 
      * artifact referencing a compliant factory (not normally 
      * required).
      *
      * @param artifact the repository cache manager artifact
      */
      public void setImplementation( Artifact artifact )
      {
          m_artifact = artifact;
      }
  
     /**
      * The cache directory is the directory into which resources 
      * such as jar files are loaded by a repository cache manager.
      *
      * @param cache the repository cache directory
      */
      public void setCacheDirectory( File cache )
      {
          m_cache = cache;
      }
  
     /**
      * Set the initial hosts to be used by a repository cache manager 
      * implementation and the initial context implementation when 
      * resolving dependent resources.  If is resource is not present
      * in a local cache, remote hosts are checked in the order presented
      * in the supplied list. A host may be a file url or a http url.
      *
      * @param hosts a sequence of remote host urls
      */
      public void setHosts( String[] hosts )
      {
          m_hosts = hosts;
      }
  
     /**
      * Creation of an inital context based on the system and working 
      * directory, parent classloader, repository cache manager implementation
      * artifact, cache directory, and remote hosts sequence supplied to the 
      * factory.
      *
      * @return a new initial context
      */
      public InitialContext createInitialContext() 
      {
          try
          {
              return new DefaultInitialContext(
                getParentClassLoader(),
                getImplementation(),
                getWorkingDirectory(),
                getCacheDirectory(),
                getHosts() );
          }
          catch( Throwable e )
          {
              final String error =
                "Could not create initial context.";
              throw new RepositoryRuntimeException( error, e );
          }
      }
  
     /**
      * Return the home directory value direved from the application key.
      * @return the home directory.
      */
      public File getHomeDirectory()
      {
          return m_defaults.getHomeDirectory();
      }
  
     /**
      * Return the working directory value.
      * @return the working directory.
      */
      public File getWorkingDirectory()
      {
          return m_work;
      }
  
     /**
      * Return the parent classloader. The default classloader returned
      * from this operation is the classloader containing this class.
      * 
      * @return the parent classloader
      */
      public ClassLoader getParentClassLoader()
      {
          if( null != m_classloader ) return m_classloader;
          return DefaultInitialContext.class.getClassLoader();
      }
  
     /**
      * Return the implementation artifact.  If not overriden, a default
      * artifact referencing avalon-repository-impl will be returned.
      *
      * @return the implementation artifact
      */
      public Artifact getImplementation()
      {
          return m_artifact;
      }
  
     /** 
      * Return the assigned or default cache directory. If undefined
      * the cache directory shall default to ${avalon.home}/repository.
      *
      * @return the cache directory
      */
      public File getCacheDirectory()
      {
          if( null != m_cache ) return m_cache;
          String value = m_properties.getProperty( InitialContext.CACHE_KEY );
          if( null != value ) return new File( value );
          return new File( getHomeDirectory(), "repository" );
      }
  
     /**
      * Return the assigned or default host sequence.
      * @return the remote host url sequence
      */
      public String[] getHosts()
      {
          if( null != m_hosts ) return m_hosts;
          String value = m_properties.getProperty( InitialContext.HOSTS_KEY );
          if( null == value ) return new String[0];
          return expandHosts( value );
      }
  
      // ------------------------------------------------------------------------
      // implementation
      // ------------------------------------------------------------------------
  
      private Properties getDefaultProperties() throws IOException
      {
          Properties properties = new Properties();
          ClassLoader classloader = 
            DefaultInitialContextFactory.class.getClassLoader();
          InputStream input = 
            classloader.getResourceAsStream( AVALON_PROPERTIES );
          if( input == null ) 
          {
              final String error = 
                "Missing resource: [" + AVALON_PROPERTIES + "]";
              throw new Error( error );
          }
          properties.load( input );
          return properties;
      }
  
      private static String[] expandHosts( String arg )
      {
          ArrayList list = new ArrayList();
          StringTokenizer tokenizer = new StringTokenizer( arg, "," );
          while( tokenizer.hasMoreTokens() )
          {
              list.add( tokenizer.nextToken() );
          }
          return (String[]) list.toArray( new String[0] );
      }
  }
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: cvs-unsubscribe@avalon.apache.org
For additional commands, e-mail: cvs-help@avalon.apache.org