You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@maven.apache.org by jv...@apache.org on 2003/02/12 11:09:40 UTC

cvs commit: jakarta-turbine-maven/src/java/org/apache/maven/plugin DefaultPluginCacheManager.java DefaultPluginManager.java

jvanzyl     2003/02/12 02:09:40

  Added:       src/java/org/apache/maven/plugin
                        DefaultPluginCacheManager.java
                        DefaultPluginManager.java
  Log:
  o reimplementing the caching and plugin manager after too much diddling.
    This is not wired in as I would like to spend the morning testing it.
    There are 9 mock plugins with some diabolical linking but I'm going
    to keep adding tests before I try to bootstrap with it in place.
  
  Revision  Changes    Path
  1.1                  jakarta-turbine-maven/src/java/org/apache/maven/plugin/DefaultPluginCacheManager.java
  
  Index: DefaultPluginCacheManager.java
  ===================================================================
  package org.apache.maven.plugin;
  
  /* ====================================================================
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2001 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Apache" and "Apache Software Foundation" and
   *    "Apache Maven" must not be used to endorse or promote products
   *    derived from this software without prior written permission. For
   *    written permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    "Apache Maven", nor may "Apache" appear in their name, without
   *    prior written permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  import com.werken.werkz.CyclicGoalChainException;
  import com.werken.werkz.Goal;
  import com.werken.werkz.NoSuchGoalException;
  import com.werken.werkz.WerkzProject;
  import org.apache.commons.lang.StringUtils;
  import org.xml.sax.Attributes;
  import org.xml.sax.InputSource;
  import org.xml.sax.SAXParseException;
  import org.xml.sax.helpers.DefaultHandler;
  
  import javax.xml.parsers.SAXParser;
  import javax.xml.parsers.SAXParserFactory;
  import java.io.File;
  import java.io.FileInputStream;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.Iterator;
  import java.util.LinkedList;
  import java.util.Set;
  
  /**
   * This is the process that we use:
   *
   * - Start with goal X.
   * - Gather prereqs for X.
   *
   * We want to find all goals that will be executed due to X being executed
   * we don't care about ordering here because when werkz executes it will sort
   * that out for us. We just need to make sure all the appropriate plugins have
   * been loaded so that werkz can find the goals it needs.
   *
   * For a single goal we will collect the set of goals that will be executed with
   * it.
   *
   */
  public class DefaultPluginCacheManager
      extends DefaultHandler
  {
      /** Goals with a pointer to plugins with callbacks. */
      private HashMap callbackPeersMap = new HashMap();
  
      /** dyna tag -> plugin map. Where dyna tags live. */
      private HashMap dynaTagPluginMap = new HashMap();
  
      /** A plugins dyna tag dependencies. */
      private HashMap pluginDynaTagDepsMap = new HashMap();
  
      /** Goal -> plugin map. */
      private HashMap goalPluginMap = new HashMap();
  
      /** Currently executing plugin script. */
      private File pluginScript;
  
      /** Parent directory of the currently executing plugin script. */
      private String pluginScriptDirectory;
  
      private boolean insideGoal;
      private Goal currentGoal;
      private boolean insideCallback;
      private String currentCallbackGoalName;
  
      private WerkzProject goalProject = new WerkzProject();
  
      /** Default constructor. */
      public DefaultPluginCacheManager()
      {
      }
  
      // ----------------------------------------------------------------------
      // A C C E S S O R S
      // ----------------------------------------------------------------------
  
      /**
       *
       * @param pluginScript
       */
      public void setPluginScript( File pluginScript )
      {
          this.pluginScript = pluginScript;
  
          pluginScriptDirectory = pluginScript.getParentFile().getName();
      }
  
      /**
       *
       * @return
       */
      public File getPluginScript()
      {
          return pluginScript;
      }
  
      private File pluginsDir;
  
      /**
       * Set the pluginsDir attribute.
       *
       * @param pluginsDir
       */
      public void setPluginsDir( File pluginsDir )
      {
          this.pluginsDir = pluginsDir;
      }
  
      /**
       * Get the pluginsDir attribute.
       *
       * @return The
       */
      public File getPluginsDir()
      {
          return pluginsDir;
      }
  
      // ----------------------------------------------------------------------
      // I M P L E M E N T A T I O N
      // ----------------------------------------------------------------------
  
      /**
       */
      public void parse()
      {
          try
          {
              SAXParserFactory saxFactory = SAXParserFactory.newInstance();
              saxFactory.setNamespaceAware( true );
              SAXParser parser = saxFactory.newSAXParser();
              InputSource is = new InputSource( new FileInputStream( getPluginScript() ) );
              parser.parse( is, this );
          }
          catch ( Exception e )
          {
              e.printStackTrace();
          }
      }
  
      public void saveCache()
          throws Exception
      {
      }
  
      /**
       *  Load on-disk cache information, if possible.
       */
      void loadCache()
      {
      }
  
      private HashSet getPluginDynaTagDeps( String pluginName )
      {
          HashSet pluginDynaTagDeps = (HashSet) pluginDynaTagDepsMap.get( pluginName );
  
          if ( pluginDynaTagDeps == null )
          {
              pluginDynaTagDeps = new HashSet();
              pluginDynaTagDepsMap.put( pluginName, pluginDynaTagDeps );
          }
  
          return pluginDynaTagDeps;
      }
  
  
      private HashSet getCallbackPeers( String goalName )
      {
          HashSet callbackPeers = (HashSet) callbackPeersMap.get( goalName );
  
          if ( callbackPeers == null )
          {
              callbackPeers = new HashSet();
              callbackPeersMap.put( goalName, callbackPeers );
          }
  
          return callbackPeers;
      }
  
      /**
       * We are looking for namespace declarations like the following:
       *
       * xmlns:doc="doc"
       *
       * Here we know that the given plugin.jelly script (or any jelly script used
       * within Maven) has a dependency on the dyna tag lib named 'doc'. We need to
       * make sure that this dyna tag lib is ready for use when the plugin.jelly
       * script is run.
       *
       * @param prefix Prefix to be used in the jelly script.
       * @param uri Uri of the dyna tag lib.
       */
      public void startPrefixMapping( String prefix, String uri )
      {
          if ( uri.startsWith( "jelly:" ) == false
              && uri.startsWith( "dummy" ) == false
              && uri.equals( "" ) == false )
          {
              getPluginDynaTagDeps( pluginScriptDirectory ).add( uri );
          }
      }
  
      /**
       * Handles opening elements of the xml file.
       */
      public void startElement( String uri, String localName, String rawName, Attributes attributes )
      {
          if ( rawName.equals( "goal" ) )
          {
              String name = StringUtils.deleteWhitespace( attributes.getValue( "name" ) );
  
              Goal goal = goalProject.getGoal( name, true );
              goalProject.addGoal( goal );
  
              // Add to the goal -> plugin map. */
              goalPluginMap.put( name, pluginScriptDirectory );
  
              // Set the current goal.
              insideGoal = true;
  
              String prereqs = attributes.getValue( "prereqs" );
              String description = attributes.getValue( "description" );
              String goalProperty = description + ">";
  
              if ( prereqs != null )
              {
                  String[] s = StringUtils.split( prereqs, "," );
  
                  for ( int i = 0; i < s.length; i++ )
                  {
                      try
                      {
                          Goal prereq = goalProject.getGoal( s[i], true );
                          goal.addPrecursor( prereq );
                      }
                      catch ( CyclicGoalChainException e )
                      {
                          // do nothing.
                      }
                  }
              }
  
              currentGoal = goal;
          }
          else if ( rawName.equals( "preGoal" ) )
          {
              String name = attributes.getValue( "name" );
              getCallbackPeers( name ).add( pluginScriptDirectory );
              insideCallback = true;
              currentCallbackGoalName = name;
          }
          else if ( rawName.equals( "postGoal" ) )
          {
              String name = attributes.getValue( "name" );
              getCallbackPeers( name ).add( pluginScriptDirectory );
              insideCallback = true;
              currentCallbackGoalName = name;
          }
          else if ( rawName.equals( "attainGoal" ) )
          {
              String name = attributes.getValue( "name" );
  
              if ( insideGoal )
              {
                  try
                  {
                      currentGoal.addPrecursor( goalProject.getGoal( name, true ) );
                  }
                  catch ( CyclicGoalChainException e )
                  {
                      // do nothing.
                  }
              }
              else if ( insideCallback )
              {
                  try
                  {
                      Goal g = goalProject.getGoal( currentCallbackGoalName );
                      g.addPrecursor( goalProject.getGoal( name, true ) );
                  }
                  catch ( CyclicGoalChainException e )
                  {
                      // do nothing.
                  }
              }
          }
          else if ( localName.equals( "taglib" )
              && uri.equals( "jelly:define" ) )
          {
              String tagLibUri = attributes.getValue( "uri" );
              dynaTagPluginMap.put( tagLibUri, pluginScriptDirectory );
          }
      }
  
      public void endElement( String uri, String localName, String rawName )
      {
          if ( rawName.equals( "goal" ) )
          {
              insideGoal = false;
          }
          else if ( rawName.equals( "preGoal" ) )
          {
              insideCallback = false;
          }
          else if ( rawName.equals( "postGoal" ) )
          {
              insideCallback = false;
          }
      }
  
      /**
       *
       * @param goal
       */
      public Set resolve( String goal )
          throws NoSuchGoalException
      {
          HashSet plugins = new HashSet();
  
          Goal[] chain = goalProject.getExecutionChain( goal );
  
          for ( int i = 0; i < chain.length; i++ )
          {
              Goal g = chain[i];
              String plugin = (String) goalPluginMap.get( g.getName() );
              plugins.add( plugin );
              plugins.addAll( getCallbackPeers( goal ) );
          }
  
          Set a = resolveDynaTagPlugins( plugins );
          plugins.addAll( a );
  
          return plugins;
      }
  
      private Set resolveDynaTagPlugins( Set plugins )
      {
          Set resolvedDynaTagPlugins = new HashSet();
  
          for ( Iterator i = plugins.iterator(); i.hasNext(); )
          {
              String plugin = (String) i.next();
              Set dynaTagDeps = getPluginDynaTagDeps( plugin );
  
              for ( Iterator j = dynaTagDeps.iterator(); j.hasNext(); )
              {
                  LinkedList dynaTagUris = new LinkedList();
                  Set seen = new HashSet();
  
                  dynaTagUris.add( j.next() );
  
                  while ( dynaTagUris.isEmpty() == false )
                  {
                      String dynaTagUri = (String) dynaTagUris.removeFirst();
  
                      if ( seen.contains( dynaTagUri ) )
                      {
                          continue;
                      }
  
                      seen.add( dynaTagUri );
  
                      String dynaTagPluginHome = (String) dynaTagPluginMap.get( dynaTagUri );
                      dynaTagUris.addAll( getPluginDynaTagDeps( dynaTagPluginHome ) );
                      resolvedDynaTagPlugins.add( dynaTagPluginHome );
                  }
              }
          }
  
          return resolvedDynaTagPlugins;
      }
  
      /**
       * Warning callback.
       *
       * @param spe The parse exception that caused the callback to be invoked.
       */
      public void warning( SAXParseException spe )
      {
          printParseError( "Warning", spe );
      }
  
      /**
       * Error callback.
       *
       * @param spe The parse exception that caused the callback to be invoked.
       */
      public void error( SAXParseException spe )
      {
          printParseError( "Error", spe );
      }
  
      /**
       * Fatal error callback.
       *
       * @param spe The parse exception that caused the callback to be invoked.
       */
      public void fatalError( SAXParseException spe )
      {
          printParseError( "Fatal Error", spe );
      }
  
      /**
       * Description of the Method
       */
      private final void printParseError( String type, SAXParseException spe )
      {
          System.err.println( type + " [line " + spe.getLineNumber() +
                              ", row " + spe.getColumnNumber() + "]: " +
                              spe.getMessage() );
      }
  }
  
  
  
  1.1                  jakarta-turbine-maven/src/java/org/apache/maven/plugin/DefaultPluginManager.java
  
  Index: DefaultPluginManager.java
  ===================================================================
  package org.apache.maven.plugin;
  
  /* ====================================================================
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2001 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Apache" and "Apache Software Foundation" and
   *    "Apache Maven" must not be used to endorse or promote products
   *    derived from this software without prior written permission. For
   *    written permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    "Apache Maven", nor may "Apache" appear in their name, without
   *    prior written permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   * ====================================================================
   */
  
  import com.werken.forehead.Forehead;
  import com.werken.werkz.Goal;
  import com.werken.werkz.Session;
  import com.werken.werkz.jelly.JellySession;
  import org.apache.commons.io.FileUtils;
  import org.apache.commons.lang.StringUtils;
  import org.apache.commons.logging.Log;
  import org.apache.commons.logging.LogFactory;
  import org.apache.maven.AbstractMavenComponent;
  import org.apache.maven.MavenException;
  import org.apache.maven.MavenSession;
  import org.apache.maven.MavenUtils;
  import org.apache.maven.UnknownGoalException;
  import org.apache.maven.jelly.JellyUtils;
  import org.apache.maven.jelly.MavenJellyContext;
  import org.apache.maven.project.Project;
  import org.apache.maven.util.Expand;
  
  import java.io.File;
  import java.io.FileInputStream;
  import java.io.IOException;
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.HashSet;
  import java.util.Iterator;
  import java.util.LinkedList;
  import java.util.List;
  import java.util.Properties;
  import java.util.Set;
  import java.util.StringTokenizer;
  
  /*
  
  NOTES:
  
  We initialize the plugin with an empty context. When we are finished initializing the
  plugins we will have a context with a werkz project object that has been created
  that will contain all the goals present within the plugins.
  
  The result of this initialization is the creation of all the cache files which
  give us an outline of what is required to attain a particular goal.
  
  When a particular project needs to attain one of these goals we need to
  load the plugin and in doing so we run the plugin.jelly script for the required
  plugins against the projects context.
  
  Possibly eventually we could make this more efficient storing the plugin.jelly
  in an accessible templated form which when required for a particular project
  can be executed against the project's context.
  
  IMPLEMENTATION NOTES
  
  - We need to know the goals to attain
  - We need to keep track of plugins that have been loaded for a particular project.
  
  */
  
  /**
   * This is the process that we use:
   *
   * - Start with goal X.
   * - Gather prereqs for X.
   * - Gather all preGoals for X.
   * - Gather all postGoals for X.
   *
   * The <code>PluginManager</code> deals with all aspects of a plugins lifecycle.
   * </p>
   *
   * @author <a href="mailto:jason@zenplex.com">Jason van Zyl</a>
   * @author <a href="mailto:bob@eng.werken.com">bob mcwhirter</a>
   *
   * @version $Id: DefaultPluginManager.java,v 1.1 2003/02/12 10:09:39 jvanzyl Exp $
   *
   */
  public class DefaultPluginManager
      extends AbstractMavenComponent
  {
      /** Plug-in main script name. */
      public static final String PLUGIN_SCRIPT_NAME = "plugin.jelly";
  
      /** Plug-in default properties name. */
      public static final String PLUGIN_PROPERTIES_NAME = "plugin.properties";
  
      /** Logger */
      private static final Log log = LogFactory.getLog( PluginManager.class );
  
      /** ${maven.home}/bin/plugins directory. */
      private File pluginsDir;
  
      /**
       * This contains a set of sets where each set contains a list of
       * plugins that have been loaded for a particular project.
       */
      private Set loadedPlugins;
  
      /** Is the plugin manager initialized. */
      private boolean initialized = false;
  
      /** Maven session reference. */
      private MavenSession mavenSession;
  
      /** Plugin cache manager. */
      private PluginCacheManager cacheManager;
  
      /** Cache manager for user jelly scripts. */
      private PluginCacheManager transientCacheManager;
      ;
  
      /**
       *  Default constructor.
       *
       *  @param mavenSession The MavenSession this plugin manager will use
       *         until Maven shuts down.
       */
      public DefaultPluginManager( MavenSession mavenSession )
      {
          this.mavenSession = mavenSession;
  
          loadedPlugins = new HashSet();
          cacheManager = new PluginCacheManager( true );
          transientCacheManager = new PluginCacheManager( true );
      }
  
      // ----------------------------------------------------------------------
      // A C C E S S O R S
      // ----------------------------------------------------------------------
  
      /**
       *  Retrieve the set of all goal names.
       *
       *  @return The set of <code>String</code> goal names.
       */
      public Set getGoalNames()
      {
          return cacheManager.getGoalCache().keySet();
      }
  
      /**
       *  Retrieve a goal description by the goal name.
       *
       *  @param name THe goal name.
       *
       *  @return The description or <code>null</code> if no
       *          description has been set.
       */
      public String getGoalDescription( String name )
      {
          String value = cacheManager.getGoalCache().getProperty( name );
  
          if ( value == null )
          {
              return null;
          }
  
          String description = value.substring( 0, value.indexOf( ">" ) );
  
          return description;
      }
  
      // ----------------------------------------------------------------------
      // P U B L I C  A P I
      // ----------------------------------------------------------------------
  
      /**
       *  Initialize all plugins.
       *
       *  @throws Exception If an error occurs while initializing any plugin.
       */
      public void initialize()
          throws Exception
      {
          if ( initialized )
          {
              return;
          }
  
          log.info( "Initializing Plugins!" );
  
          setPluginsDir( new File( mavenSession.getRootContext().getMavenHome(), "plugins" ) );
  
          cacheManager.setPluginsDir( getPluginsDir() );
          cacheManager.loadCache();
  
          File[] files = getPluginsDir().listFiles();
  
          // First we expand any JARs that contain plugins.
          for ( int i = 0; i < files.length; ++i )
          {
              // Only unpack the JAR if it hasn't been already, or is newer
              // than the plugin directory
              if ( files[i].getName().endsWith( ".jar" ) )
              {
                  String directory = files[i].getName();
                  directory = directory.substring( 0, directory.indexOf( ".jar" ) );
                  File unzipDir = new File( getPluginsDir(), directory );
  
                  // if there's no directory, or the jar is newer, expand the jar
                  if (   unzipDir.exists() == false
                      || files[i].lastModified() > unzipDir.lastModified() )
                  {
                      invalidateCache( directory );
  
                      File processed = getPluginProcessedMarker( unzipDir.getName() );
                      if ( processed.exists() )
                      {
                          processed.delete();
                      }
  
                      try
                      {
                          Expand unzipper = new Expand();
                          unzipper.setSrc( files[i] );
                          unzipper.setDest( unzipDir );
                          unzipper.execute();
                      }
                      catch (IOException e)
                      {
                          throw new MavenException("Unable to extract plugin: " + files[i], e);
                      }
                  }
              }
          }
  
          // We need to get the directory listing again so that we
          // can process plugins that were just unpacked by the
          // above process.
          files = pluginsDir.listFiles();
  
          // Process each of the directorties.
          for ( int i = 0; i < files.length; ++i )
          {
              if ( files[i].isDirectory() )
              {
                  String directory = files[i].getName();
  
                  // If we haven't cached (or previous cache data has become invalid)
                  // the plugin, then do so now.
                  if ( isCached( directory ) == false )
                  {
                      try
                      {
                          cachePlugin( files[i].getName() );
                      }
                      catch ( Exception e )
                      {
                          log.error( getMessage( "plugin.loading.error", files[i].getName() ) );
                          e.printStackTrace();
                      }
                  }
              }
          }
  
          saveCache();
  
          initialized = true;
  
          log.info( "Finished initializing Plugins!" );
      }
  
  
      /**
       *  Attain the goals.
       *
       *  @throws org.apache.maven.UnknownGoalException If one of the specified
       *          goals refers to an non-existent goal.
       *  @throws Exception If an exception occurs while running a goal.
       */
      public void attainGoals( Project project )
          throws UnknownGoalException, Exception
      {
          // Before attempting to attain the goals verify the project
          // if desired.
          project.verifyDependencies();
  
          // If this project has a parent then we will load it's maven.xml
          // file into this project so any goals the parent specifies in
          // its maven.xml file are available to the child. We load this
          // before the child's maven.xml so that the child can override
          // any desired goals.
          if ( project.hasParent() )
          {
              project.loadJellyScript( project.parentMavenXml() );
          }
  
          // Currently we will not attempt to load a maven.xml file when the project.xml
          // file is not present.
          if ( project.getFile() != null )
          {
              File mavenXmlFile = new File( project.getFile().getParentFile(), "maven.xml" );
              loadJellyScript( mavenXmlFile, project );
          }
  
          // There will always be at least one goal present which is the build:start
          // goal. If this is the only goal present then we want to add either the
          // default goal specified in the project.xml file, or the default goal
          // specified in the driver.properties file.
  
          //if ( goalNames.size() == 1 )
          if ( project.getGoalNames().size() == 0 )
          {
              String defaultGoalName = project.getContext().getWerkzProject().getDefaultGoalName();
  
              if ( defaultGoalName != null )
              {
                  project.getGoalNames().add( defaultGoalName );
              }
          }
  
          //project.getGoalNames().add( BUILD_END_GOAL );
  
          for ( Iterator i = project.getGoalNames().iterator(); i.hasNext(); )
          {
              String goalName = (String) i.next();
              Goal eachGoal = project.getContext().getWerkzProject().getGoal( goalName );
  
              if ( eachGoal == null )
              {
                  throw new UnknownGoalException( goalName );
              }
          }
  
          Session session = new JellySession( project.getContext().getXMLOutput() );
          Thread.currentThread().setContextClassLoader( null );
  
          for ( Iterator i = project.getGoalNames().iterator(); i.hasNext(); )
          {
              String eachGoalName = (String) i.next();
              Goal eachGoal = project.getContext().getWerkzProject().getGoal( eachGoalName );
              eachGoal.attain( session );
          }
      }
  
      /**
       *  Load a user jelly script. This may be a maven.xml file for a project or
       *  a shared jelly script used by the reactor.
       *
       *  @param jellyScript The jelly.
       *
       *  @throws Exception If an error occurs while attempting to load the file.
       */
      public void loadJellyScript( File jellyScript, Project project )
          throws Exception
      {
          if ( jellyScript.exists() == false )
          {
              return;
          }
  
          Set originalGoals = new HashSet( project.getContext().getWerkzProject().getGoals() );
  
          for ( Iterator i = originalGoals.iterator(); i.hasNext(); )
          {
              Goal eachGoal = (Goal) i.next();
  
              if ( eachGoal.getAction() == null )
              {
                  i.remove();
              }
          }
  
          transientCacheManager.setPluginScript( jellyScript );
          transientCacheManager.parse();
  
          // Now we have put the werkz project that belong to the plugin manager into the
          // the context so any goals with the same name encountered in the maven.xml file
          // should override goals in the plugin manager werkz project.
          JellyUtils.runScript( jellyScript,
                                project.getFile().getParentFile().toURL(),
                                project.getContext(),
                                project.getContext().getXMLOutput() );
      }
  
      // ----------------------------------------------------------------------
      // P L U G I N  L O A D I N G
      // ----------------------------------------------------------------------
  
      /**
       *  Load the specified plugin.
       *
       *  @param name The name of the plugin to load.
       *
       *  @throws Exception If an error occurs while initializing the plugin.
       */
      void loadPlugin( String name, Project project )
          throws Exception
      {
          if ( isLoaded( project, name ) )
          {
              return;
          }
  
          File pluginScript = getPluginScript( name );
  
          if ( pluginScript.exists() == false )
          {
              return;
          }
  
          File pluginDir = getPluginDir( name );
  
          // Use classworlds
          Forehead.getInstance().getClassLoader( "root.maven" ).addURL( pluginDir.toURL() );
  
          Project pluginProject = MavenUtils.getProject( new File( pluginDir, "project.xml" ),
                                                         project.getContext(),
                                                         false );
  
          if ( isPluginProcessed( name ) == false )
          {
              pluginProject.verifyDependencies();
  
              // Mark the plugin as processed.
              FileUtils.fileWrite( getPluginProcessedMarker( name ).getPath(), "plugin has been processed.");
          }
  
          pluginProject.processDependencies();
  
          // We need to create a separate context for the plugin.jelly script to run against
          // because we need our values of "plugin" and "plugin.dir" to have distinct values.
          // Everything else can be taken from the project's context. We also want project
          // properties to override any default plugin properties. When we make the
          // pluginContext, context inheritance is on so when we integrate the plugin
          // properties if the project has already defined an overriding value it
          // won't get clobbered. The plugin defaults will only be used if the
          // project hasn't provided a value.
          MavenJellyContext pluginContext = new MavenJellyContext( project.getContext() );
          MavenUtils.integrateMapInContext( getPluginProperties( pluginDir ), pluginContext );
          pluginContext.setVariable( "plugin", pluginProject );
          pluginContext.setVariable( "plugin.dir", pluginDir );
  
          // Set a context variable that points to the plugin resources directory
          pluginContext.setVariable( "plugin.resources", new File( pluginDir, "plugin-resources" ) );
  
          project.addPluginContext( pluginProject.getId(), pluginContext );
  
          JellyUtils.runScript( pluginScript,
                                getPluginDir( name ).toURL(),
                                pluginContext,
                                pluginContext.getXMLOutput() );
  
          // The project werkz project seems to come up null when there is no maven.xml
          // that is processed first. An inheritance bug in the context it seems.
          if ( project.getContext().getWerkzProject() == null )
          {
              project.getContext().setWerkzProject( pluginContext.getWerkzProject() );
          }
  
          // The plugin has now been loaded for use for a particular project
          // So add it to the list.
          loadedPlugins.add( project.hashCode() + name );
      }
  
      /**
       *  Load plugins specified in a whitespace delimited string.
       *
       *  @param names The whitespace delimited string of plugin names.
       *
       *  @throws Exception If an error occurs while attempting to load
       *          the plugins.
       */
      void loadPlugins( String names, Project project )
          throws Exception
      {
          StringTokenizer tokens = new StringTokenizer( names, "," );
  
          while ( tokens.hasMoreTokens() )
          {
              loadPlugin( tokens.nextToken(), project );
          }
      }
  
      // ----------------------------------------------------------------------
      // C A C H I N G
      // ----------------------------------------------------------------------
  
      /**
       *  Cache a plugin.
       *
       *  @param name The plugin name.
       *
       *  @throws Exception If an error occurs while attempting toa analyze
       *          and cache plugin information.
       */
      void cachePlugin( String name )
          throws Exception
      {
          File pluginScript = getPluginScript( name );
  
          // There are some plugins that don't have plugin.jelly scripts like
          // the examples plugin. This certainly isn't the norm but we want
          // avoid useless error messages filling up the console.
          if ( pluginScript.exists() )
          {
              cacheManager.setPluginScript( pluginScript );
              cacheManager.parse();
          }
      }
  
      /**
       *  Save cache information to disk.
       *
       *  @throws Exception If an error occurs while saving the cache.
       */
      void saveCache()
          throws Exception
      {
          cacheManager.saveCache();
      }
  
      /**
       * Invalidate cache information for a single plugin.
       *
       * @param pluginName The name of the plugin to invalid cache entries.
       */
      void invalidateCache( String pluginName )
      {
          for ( Iterator i = cacheManager.getGoalCache().keySet().iterator(); i.hasNext(); )
          {
              String eachGoal = (String) i.next();
  
              if ( cacheManager.getPluginCache().getProperty( eachGoal ).equals( pluginName ) )
              {
                  i.remove();
                  cacheManager.getPluginCache().remove( eachGoal );
                  cacheManager.getCallbackCache().remove( eachGoal + ".pre" );
                  cacheManager.getCallbackCache().remove( eachGoal + ".post" );
              }
          }
      }
  
      /**
       *  Determine if a plugin has been cached.
       *
       *  @param pluginName The name of the plugin to test.
       *
       *  @return <code>true</code> if the plugin has already been cached,
       *          otherwise <code>false</code>.
       */
      boolean isCached( String pluginName )
      {
          return cacheManager.getPluginCache().contains( pluginName );
      }
  
      /**
       *  Determine if a goal has been cached.
       *
       *  @param name The name of the goal to test.
       *
       *  @return <code>true</code> if the goal has already been cached,
       *          otherwise <code>false</code>.
       */
      boolean isGoalCached( String name )
      {
          return cacheManager.getGoalCache().containsKey( name );
      }
  
      Set getTagLibsCache()
      {
          return cacheManager.getDynaTagLibCache().keySet();
      }
  
      /**
       *  Retrieve the plugin processed marker.
       *
       *  @param pluginName Name of the plugin for which to return the processed marker.
       *
       *  @return File The file which indicates the plugin has been processed.
       */
      File getPluginProcessedMarker( String pluginName )
      {
          return new File( new File( getPluginsDir(), pluginName ), ".processed" );
      }
  
      /**
       *  Plugin processed state.
       *
       *  @param pluginName Name of the plugin for which to check for processing.
       *
       *  @return boolean Processed state of plugin.
       */
      boolean isPluginProcessed( String pluginName )
      {
          return getPluginProcessedMarker( pluginName ).exists();
      }
  
      /**
       *  Determine if a plugin has been loaded.
       *
       *  @param name The plugin name.
       *
       *  @return <code>true</code> if the plugin has been loaded,
       *          otherwise <code>false</code>.
       */
      boolean isLoaded( Project project, String name )
      {
          return loadedPlugins.contains( project.hashCode() + name );
      }
  
      /**
       *  Retrieve the plugin's default properties.
       *
       *  @param pluginDir The name of the plugin.
       *
       *  @return The default properties file for the plugin, or <code>null</code>
       *          if no such plugin.
       *
       *  @throws IOException If an IO error occurs while attempting to read the
       *       plugin's properties.
       */
      Properties getPluginProperties( File pluginDir )
          throws IOException
      {
          File propsFile = new File( pluginDir, PLUGIN_PROPERTIES_NAME );
  
          if ( propsFile.exists() == false )
          {
              return null;
          }
  
          Properties props = new Properties();
  
          FileInputStream in = new FileInputStream( propsFile );
  
          props.load( in );
  
          in.close();
  
          return props;
      }
  
  
      /**
       *  Retrieve the directory for the specified plugin.
       *
       *  @param pluginName The name of the plugin.
       *
       *  @return The directory for the plugin, or <code>null</code> if no such
       *          plugin.
       */
      File getPluginDir( String pluginName )
      {
          return new File( getPluginsDir(), pluginName );
      }
  
      /**
       *  Retrieve the plugin's entry-point jelly script.
       *
       *  @param pluginName The name of the plugin.
       *
       *  @return The entry-point script for the plugin, or <code>null</code> if no
       *          such plugin.
       */
      File getPluginScript( String pluginName )
      {
          return new File( getPluginDir( pluginName ), PLUGIN_SCRIPT_NAME );
      }
  
      /**
       *  Sets the pluginsDir attribute of the PluginManager object
       *
       *  @param pluginsDir The maven plugin directory.
       */
      void setPluginsDir( File pluginsDir )
      {
          this.pluginsDir = pluginsDir;
      }
  
      /**
       *  Retrieve the directory containing all plugins.
       *
       *  @return The directory containing all plugins.
       */
      File getPluginsDir()
      {
          return pluginsDir;
      }
  }