dion 2003/08/18 21:28:50
Modified: src/java/org/apache/maven/plugin PluginManager.java
Added: src/java/org/apache/maven/plugin PluginCacheManager.java
Removed: src/java/org/apache/maven/plugin JellyScriptHousing.java
GoalToJellyScriptHousingMapper.java
JellyPlugin.java
Log:
Switch stable branch back to HEAD
Revision Changes Path
1.63 +655 -518 maven/src/java/org/apache/maven/plugin/PluginManager.java
Index: PluginManager.java
===================================================================
RCS file: /home/cvs/maven/src/java/org/apache/maven/plugin/PluginManager.java,v
retrieving revision 1.62
retrieving revision 1.63
diff -u -r1.62 -r1.63
--- PluginManager.java 4 Aug 2003 06:27:56 -0000 1.62
+++ PluginManager.java 19 Aug 2003 04:28:50 -0000 1.63
@@ -57,51 +57,67 @@
*/
import com.werken.forehead.Forehead;
-import com.werken.forehead.ForeheadClassLoader;
import com.werken.werkz.Goal;
import com.werken.werkz.Session;
-import com.werken.werkz.WerkzProject;
import com.werken.werkz.jelly.JellySession;
-import org.apache.commons.grant.GrantProject;
-import org.apache.commons.jelly.Script;
-import org.apache.commons.jelly.XMLOutput;
-import org.apache.commons.jelly.tags.ant.AntTagLibrary;
+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.GoalException;
-import org.apache.maven.Maven;
import org.apache.maven.MavenConstants;
import org.apache.maven.MavenException;
-import org.apache.maven.jelly.JellyBuildListener;
-import org.apache.maven.jelly.JellyPropsHandler;
+import org.apache.maven.MavenSession;
+import org.apache.maven.MavenUtils;
+import org.apache.maven.NoGoalException;
+import org.apache.maven.UnknownGoalException;
import org.apache.maven.jelly.JellyUtils;
import org.apache.maven.jelly.MavenJellyContext;
-import org.apache.maven.jelly.tags.werkz.MavenAttainGoalListener;
-import org.apache.maven.project.Dependency;
import org.apache.maven.project.Project;
-import org.apache.maven.repository.Artifact;
import org.apache.maven.util.Expand;
-import org.apache.tools.ant.types.Path;
import java.io.File;
import java.io.FileInputStream;
-import java.io.FileReader;
-import java.io.InputStream;
-import java.io.OutputStreamWriter;
-import java.io.PrintStream;
-import java.io.Writer;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.HashMap;
+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.Map;
+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.
+
+*/
+
/**
- * Plugin manager for Maven. <p>
+ * Plugin manager for MavenSession. <p>
*
* The <code>PluginManager</code> deals with all aspects of a plugins lifecycle.
* </p>
@@ -111,27 +127,24 @@
*
* @version $Id$
*
- * @todo Goal inheritance
*/
public class PluginManager
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 );
- /** */
- public static final String PLUGIN_MANAGER = "maven.pluginManager";
- /** */
- public static final String BASE_CONTEXT = "maven.goalAttainmentContext";
- /** */
- public static String JELLY_CONTEXT = "mavenGoalTag.jellyContext";
- /** */
- public static String XML_OUTPUT = "mavenGoalTag.xmlOutput";
/**
* The variable key that holds an implementation of the
- * <code>com.werken.werkz.Session</code> in the parent scope pluginContext.
+ * <code>com.werken.werkz.Session</code> in the parent scope context.
*/
- public static final String GLOBAL_SESSION_KEY = "maven.session.global";
+ private static final String GLOBAL_SESSION_KEY = "maven.session.global";
/** The directory where plugin jars reside under Maven's home. */
private File pluginsDir;
@@ -139,31 +152,23 @@
/** The directory where the plugin jars are unpacked to. */
private File unpackedPluginsDir;
+ /**
+ * 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 Maven maven;
-
- private Map jellyScriptHousings;
-
- /** map of loaded plugins by id */
- private Map loadedPlugins;
-
- /** map of plugins by id */
- private Map plugins;
-
- /** rootClassLoader classloader. */
- private ForeheadClassLoader rootClassLoader;
+ private MavenSession mavenSession;
- /** maven.rootClassLoader classloader. */
- private ForeheadClassLoader mavenRootClassLoader;
+ /** Plugin cache manager. */
+ private PluginCacheManager cacheManager;
- /** Goal to Plugins mapper. */
- private GoalToJellyScriptHousingMapper mapper;
-
- /** Context in which to compile Jelly scripts. */
- private MavenJellyContext compileScriptContext;
+ /** Cache manager for user jelly scripts. */
+ private PluginCacheManager transientCacheManager;
/**
* Default constructor.
@@ -171,150 +176,50 @@
* @param mavenSession The MavenSession this plugin manager will use
* until Maven shuts down.
*/
- public PluginManager( Maven mavenSession )
+ public PluginManager( MavenSession mavenSession )
{
- this.maven = mavenSession;
-
- jellyScriptHousings = new HashMap();
- loadedPlugins = new HashMap();
- plugins = new HashMap();
-
- mapper = new GoalToJellyScriptHousingMapper();
+ this.mavenSession = mavenSession;
- // FIXME: Why are there two hard coded class loaders?
- rootClassLoader = Forehead.getInstance().getClassLoader( "root" );
- mavenRootClassLoader = Forehead.getInstance().getClassLoader( "root.maven"
);
-
- compileScriptContext = new MavenJellyContext();
+ loadedPlugins = new HashSet();
+ cacheManager = new PluginCacheManager( true );
+ transientCacheManager = new PluginCacheManager( true );
}
// ----------------------------------------------------------------------
- // Accessors
+ // A C C E S S O R S
// ----------------------------------------------------------------------
- private Maven getMaven()
- {
- return maven;
- }
-
- private void setMaven( Maven maven )
- {
- this.maven = maven;
- }
-
- /**
- * Expand the plugin jars if needed
- * @throws MavenException
- */
- private void expandPlugins() throws MavenException
- {
- File[] files = getPluginsDir().listFiles();
-
- // First we expand any JARs that contain plugins to the unpack directory.
- // This will be a different directory than the plugin jars if
- // MAVEN_HOME_LOCAL / maven.home.local was set to a different directory
- // than MAVEN_HOME / maven.home.
- Expand unzipper = new Expand();
- for ( int i = 0; i < files.length; ++i )
- {
- processPluginFile(unzipper, files[i]);
- }
- }
-
/**
- * Load all plugins in the unpacked plugins dir
- * @throws Exception
+ * Retrieve the set of all goal names.
+ *
+ * @return The set of <code>String</code> goal names.
*/
- private void loadPlugins() throws Exception
- {
- File[] files = getUnpackedPluginsDir().listFiles();
-
- // Process each of the directorties.
- for ( int i = 0; i < files.length; ++i )
- {
- if ( files[i].isDirectory() )
- {
- String pluginId = files[i].getName();
- if ( !isLoaded( pluginId ) )
- {
- loadPlugin( pluginId );
- }
- }
- }
- }
-
- private void processPluginFile(Expand unzipper, File pluginFile)
- throws MavenException
+ public Set getGoalNames()
{
- // Only unpack the JAR if it hasn't been already, or is newer
- // than the plugin directory
- if ( pluginFile.getName().endsWith( ".jar" ) )
- {
- // this is where JellyPlugins should be determined and created
- JellyPlugin plugin = new JellyPlugin();
- plugin.setJarFile( pluginFile );
- String directory = pluginFile.getName();
- directory = directory.substring( 0, directory.indexOf( ".jar" ) );
- plugin.setId( directory );
- plugin.setUnpackDirectory( getUnpackedPluginsDir() );
- plugin.unpack();
- plugins.put(plugin.getId(), plugin);
- }
+ return cacheManager.getGoalCache().keySet();
}
/**
- * Load the specified plugin.
+ * Retrieve a goal description by the goal name.
*
- * @param name The name of the plugin to load.
+ * @param name The goal name.
*
- * @throws Exception If an error occurs while initializing the plugin.
+ * @return The description or <code>null</code> if no
+ * description has been set.
*/
- private void loadPlugin( String name )
- throws Exception
+ public String getGoalDescription( String name )
{
- System.err.println( "Loading plugin '" + name + "'" );
- JellyPlugin plugin = (JellyPlugin)plugins.get(name);
- if (plugin == null)
- {
- plugin = new JellyPlugin();
- plugin.setId(name);
- }
-
- plugin.setUnpackDirectory( getUnpackedPluginsDir() );
- plugin.setParentClassLoader( mavenRootClassLoader );
- Project pluginProject = getMaven().getProject( plugin.getDescriptor() ,
false );
- plugin.setProject( pluginProject );
- plugin.processDependencies();
+ String value = cacheManager.getGoalCache().getProperty( name );
- if ( plugin.hasScript() )
+ // Must check for "null" in case it was loaded from cache that way
+ if ( value == null || value.startsWith("null>"))
{
- JellyScriptHousing jellyScriptHousing = null;
- try
- {
- // Create the PluginHousing
- jellyScriptHousing = createJellyScriptHousing( plugin );
- }
- catch ( Exception e )
- {
- System.err.println( "Error loading plugin '" + name + "'" );
- throw e;
- }
-
- // Store the plugin housing for future use.
- jellyScriptHousings.put( name, jellyScriptHousing );
- mapper.parse( new FileReader( plugin.getScriptFile() ),
jellyScriptHousing );
+ return null;
}
- loadedPlugins.put(plugin.getId(), plugin);
- }
+ String description = value.substring( 0, value.indexOf( ">" ) );
- /**
- * @todo Why is this method public?
- * @return
- */
- public GoalToJellyScriptHousingMapper getMapper()
- {
- return mapper;
+ return description;
}
// ----------------------------------------------------------------------
@@ -325,7 +230,6 @@
* Initialize all plugins.
*
* @throws Exception If an error occurs while initializing any plugin.
- * @todo why is this method public?
*/
public void initialize()
throws Exception
@@ -335,487 +239,646 @@
return;
}
- setPluginsDir( new File( getMaven().getProperty( MavenConstants.MAVEN_HOME
), "plugins" ) );
- setUnpackedPluginsDir( new File( maven.getProperty(
MavenConstants.MAVEN_UNPACKED_PLUGINS_DIR ) ) );
+ if( log.isDebugEnabled() )
+ {
+ log.debug( "Initializing Plugins!" );
+ }
+ setPluginsDir( new File( mavenSession.getRootContext().getMavenHome(),
"plugins" ) );
+ setUnpackedPluginsDir( new File(
mavenSession.getRootContext().getUnpackedPluginsDir() ) );
+ if( log.isDebugEnabled() )
+ {
+ log.debug( "Set plugin source directory to "
+ + getPluginsDir().getAbsolutePath() );
+ log.debug( "Set plugin cache directory to "
+ + getUnpackedPluginsDir().getAbsolutePath() );
+ }
+ cacheManager.loadCache();
+
+ if( log.isDebugEnabled() )
+ {
+ log.debug( "Unpacking plugins from directory --> "
+ + getPluginsDir().getAbsolutePath() );
+ }
- expandPlugins();
+ expandPluginJars();
- loadPlugins();
+ cachePlugins();
initialized = true;
- log.info( "Finished initializing Plugins!" );
+ log.debug( "Finished initializing Plugins!" );
}
/**
+ * For any plugin that is not already cached according to the cache manager
+ * we cache it.
+ * @throws Exception FIXME. When anything goes wrong
+ */
+ private void cachePlugins() throws Exception
+ {
+ // We need to get the directory listing again so that we
+ // can process plugins that were just unpacked by the
+ // above process. This time we're looking at the unpacked plugins.
+ File[] files = getUnpackedPluginsDir().listFiles();
+
+ if (log.isDebugEnabled())
+ {
+ log.debug( "Processing unpacked plugins in "
+ + getUnpackedPluginsDir().getAbsolutePath() );
+ }
+
+ // 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();
+ }
+
+ /**
+ * Expand the plugin jars if needed
+ * @throws MavenException
+ */
+ private void expandPluginJars() throws MavenException
+ {
+ File[] files = getPluginsDir().listFiles();
+
+ // First we expand any JARs that contain plugins to the unpack directory.
+ // This will be a different directory than the plugin jars
+ // MAVEN_HOME_LOCAL / maven.home.local was set to a different directory
+ // than MAVEN_HOME / maven.home.
+ 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( getUnpackedPluginsDir(), directory );
+
+ // if there's no directory, or the jar is newer, expand the jar
+ if ( unzipDir.exists() == false
+ || files[i].lastModified() > unzipDir.lastModified() )
+ {
+ if( log.isDebugEnabled() )
+ {
+ log.debug( "Unpacking '" + directory
+ + "' plugin to directory --> "
+ + unzipDir.getAbsolutePath() );
+ }
+ 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);
+ }
+ }
+ }
+ }
+ }
+
+
+ /**
* 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, List goals )
+ public void attainGoals( Project project )
throws GoalException, Exception
{
- MavenJellyContext baseContext = createBaseContext( project );
-
// Before attempting to attain the goals verify the project
// if desired.
- // FIXME: From attainGoals angle, how does it know the project needs to
- // to be verified, or that the project object hasn't been used before
project.verifyDependencies();
+ project.processDependencies();
- // Set up the ant project.
- buildAntProject( project, baseContext );
-
- Session session = getJellySession(baseContext);
- // add the global session to the pluginContext so that it can be used by
tags
- baseContext.setVariable( GLOBAL_SESSION_KEY, session );
-
- WerkzProject werkzProject = buildWerkzProject( baseContext );
-
- //
-------------------------------------------------------------------------------------------------------------
- // Execution of the Jelly scripts:
- //
- // We run the Jelly scripts in the following order:
- //
- // 1) driver.jelly - doesn't exist anymore
- // 2) project's maven.xml
- // 3) parent's maven.xml (if it exists)
- // 4) plugin.jelly
- //
- // The Maven version of the <goal/> Werkz tag has been constructed so that
the first
- // definition of a goal wins.
- //
- // We run them in this order because we do not know before hand which
plugin goals a project
- // may wish to override so we guarantee precedence of the goals by running
the jelly scripts
- // in the above mentioned order.
- //
-------------------------------------------------------------------------------------------------------------
-
- // driver.jelly
- //InputStream driver =
PluginManager.class.getClassLoader().getResourceAsStream( "driver.jelly" );
- //JellyScriptHousing driverHousing = createJellyScriptHousing( project,
null, driver );
- //driverHousing.setSource( "driver.jelly" );
- //mapper.parse( new InputStreamReader( driver ), driverHousing );
- //runJellyScriptHousing( driverHousing, baseContext );
-
- // FIXME: Part of this belongs as a method on Project, e.g. the name and
- // construction of maven.xml
- // Project's Jelly script
- if ( project.hasMavenXml() )
- {
- File mavenXml = project.getMavenXml();
- //!!! This is the cause of the classloader problems!!
- JellyScriptHousing jellyScriptHousing = createJellyScriptHousing(
project, null, mavenXml );
- mapper.parse( new FileReader( mavenXml ), jellyScriptHousing );
- runJellyScriptHousing( jellyScriptHousing, baseContext );
- }
-
- // Parent's Jelly script
- // FIXME: What about further up the chain?
- if ( project.hasParent() && project.getParent().hasMavenXml() )
+ // 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() )
{
- // FIXME: this is a badly named method
- File f = project.parentMavenXml();
+ project.loadJellyScript( project.parentMavenXml() );
+ }
- if ( f.exists() )
- {
- JellyScriptHousing jellyScriptHousing = createJellyScriptHousing(
project, null, f );
- mapper.parse( new FileReader( f ), jellyScriptHousing );
- runJellyScriptHousing( jellyScriptHousing, baseContext );
- }
+ // 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(),
+ MavenConstants.BUILD_FILE_NAME );
+ loadJellyScript( mavenXmlFile, project );
}
- // ----------------------------------------------------------------------
- // Default goal handling
- // ----------------------------------------------------------------------
+ // 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 ( goals.size() == 0 )
+ //if ( goalNames.size() == 1 )
+ if ( project.getGoalNames().size() == 0)
{
- String defaultGoalName = mapper.getDefaultGoalName();
-
- if ( defaultGoalName != null )
+ if (project.getContext().getWerkzProject() == null)
{
- goals.add( defaultGoalName );
+ throw new NoGoalException("No goal specified and no project.xml
found.");
+ }
+ else
+ {
+ String defaultGoalName =
project.getContext().getWerkzProject().getDefaultGoalName();
+
+ if ( defaultGoalName != null )
+ {
+ project.getGoalNames().add( defaultGoalName );
+ }
}
}
- // Plugin Jelly scripts
- for ( Iterator i = goals.iterator(); i.hasNext(); )
+ //project.getGoalNames().add( BUILD_END_GOAL );
+
+ for ( Iterator i = project.getGoalNames().iterator(); i.hasNext(); )
{
String goalName = (String) i.next();
+ prepForGoal( goalName, project );
+ Goal eachGoal = project.getContext().getWerkzProject().getGoal(
goalName );
- // Now at this point we should be able to use the name of a goal to
lookup all
- // the plugins (that are stored in the plugin housings) that need to be
executed
- // in order to satisfy all the required preconditions for successful
goal attainment.
-
- Set plugins = mapper.resolveJellyScriptHousings( goalName );
-
- for ( Iterator j = plugins.iterator(); j.hasNext(); )
+ if ( eachGoal == null )
{
- JellyScriptHousing jellyScriptHousing = (JellyScriptHousing)
j.next();
+ throw new UnknownGoalException( goalName );
+ }
+ }
+ Session session = new JellySession( project.getContext().getXMLOutput() );
+ Thread.currentThread().setContextClassLoader( null );
- //!!! Not sure why the housing is null.
- if ( jellyScriptHousing != null )
- {
- runJellyScriptHousing( jellyScriptHousing, baseContext );
- }
- }
+ // add the global session to the context so that it can be used by tags
+ project.getContext().setVariable(GLOBAL_SESSION_KEY, session);
- Goal goal = baseContext.getWerkzProject().getGoal( goalName );
- goal.attain( session );
+ for ( Iterator i = project.getGoalNames().iterator(); i.hasNext(); )
+ {
+ String eachGoalName = (String) i.next();
+ Goal eachGoal = project.getContext().getWerkzProject().getGoal(
eachGoalName );
+ eachGoal.attain( session );
}
}
/**
- *
- * @param context the base context for the werkz project
- * @return a configured werkz project
- */
- private WerkzProject buildWerkzProject(MavenJellyContext context)
- {
- // We put in our listener to frob the session.
- MavenAttainGoalListener listener = new MavenAttainGoalListener();
- listener.setBaseContext( context );
- listener.setPluginManager( this );
- WerkzProject werkzProject = new WerkzProject();
- werkzProject.addAttainGoalListener( listener );
- context.setWerkzProject( werkzProject );
- return werkzProject;
- }
-
- /**
- * Get the Werkz Session
- * FIXME: Describe what it's for?
- * @param baseContext the maven context the session should use
- * @return A Werkz Session
- * @throws Exception FIXME For anything
+ * 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.
*/
- private Session getJellySession(MavenJellyContext baseContext) throws Exception
- {
- // Create the Jelly session
- Session session = new JellySession( getJellyOutputSink() );
- session.setAttribute( XML_OUTPUT, getJellyOutputSink() );
- session.setAttribute( PLUGIN_MANAGER, this );
- session.setAttribute( BASE_CONTEXT, baseContext );
- return session;
- }
-
- private void runJellyScriptHousing( JellyScriptHousing jellyScriptHousing,
MavenJellyContext context )
+ public void loadJellyScript( File jellyScript, Project project )
throws Exception
{
- //!!! I'm not sure how a plugin housing is coming out as null but some
- // are. I need to add more tests and clean up the pre-parser.
+ if ( jellyScript.exists() == false )
+ {
+ return;
+ }
- if ( jellyScriptHousing != null )
+ Set originalGoals = new HashSet(
project.getContext().getWerkzProject().getGoals() );
+
+ for ( Iterator i = originalGoals.iterator(); i.hasNext(); )
{
- Script s = jellyScriptHousing.getScript();
+ Goal eachGoal = (Goal) i.next();
- if ( s != null )
+ if ( eachGoal.getAction() == null )
{
- try
- {
- // DG HACK 1
- ClassLoader loader =
Thread.currentThread().getContextClassLoader();
- Thread.currentThread().setContextClassLoader(
jellyScriptHousing.getClassLoader() );
- // END DG HACK 1
- jellyScriptHousing.getScript().run( context,
getJellyOutputSink() );
- // DG HACK 2
- Thread.currentThread().setContextClassLoader( loader );
- // END DG HACK 2
- }
- catch ( Exception e )
- {
- System.err.println( "Error running '" +
jellyScriptHousing.getSource() + "'" );
- throw e;
- }
+ i.remove();
}
}
- }
- public MavenJellyContext createPluginContext( String goalName,
MavenJellyContext parent )
- {
- JellyScriptHousing jellyScriptHousing = mapper.getPluginHousing( goalName );
+ transientCacheManager.setPluginScript( jellyScript );
+ transientCacheManager.parse();
- return createPluginContext( jellyScriptHousing, parent );
+ for ( Iterator i = transientCacheManager.getDynaTagLibDecls().iterator();
i.hasNext();)
+ {
+ prepDynamicTagLib( (String) i.next(), project );
+ }
+
+ // 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() );
}
- public MavenJellyContext createPluginContext( JellyScriptHousing
jellyScriptHousing, MavenJellyContext parent )
+ /**
+ * Perform any required initialization to enable a goal
+ * to be met.
+ *
+ * @param initialGoalToPrep The initial goal to prepare for. There may
+ * also be additional goals to prep for once prequisite goals
+ * are taken into consideration.
+ *
+ * @throws Exception If an error occurs while attempting to
+ * prepare for the goal.
+ */
+ public void prepForGoal( String initialGoalToPrep, Project project )
+ throws Exception
{
- // Now we have to modify the context here or create a new one
- // so that we can take the following into consideration:
- //
- // ${plugin}
- // ${plugin.dir}
- // ${plugin.resources}
-
- Project project = jellyScriptHousing.getProject();
- File projectFile = project.getFile();
- File unpackedPluginDir = projectFile.getParentFile();
-
- MavenJellyContext pluginContext = new MavenJellyContext( parent );
- pluginContext.setVariable( "plugin", jellyScriptHousing.getProject() );
- pluginContext.setVariable( "plugin.dir", unpackedPluginDir );
- pluginContext.setVariable( "plugin.resources", new File( unpackedPluginDir,
"plugin-resources" ) );
+ LinkedList goalsToPrep = new LinkedList();
+ Set seen = new HashSet();
+ String goalToPrep = null;
- // Now I need to merge the properties of the project with the plugin
properties so
- // that plugin properties can be overriden by the project.
+ goalsToPrep.add( initialGoalToPrep );
- // The JellyScriptHousing contains the project for this plugin and
therefore contains the
- // properties. We want to make these available in the plugin as simple
${foo} references.
- //pluginContext.getVariables().putAll(
jellyScriptHousing.getProject().getProjectProperties() );
+ while ( goalsToPrep.isEmpty() == false )
+ {
+ goalToPrep = (String) goalsToPrep.removeFirst();
- //System.out.println( "jellyScriptHousing = " +
jellyScriptHousing.getProject().getName() );
- //System.out.println( "jellyScriptHousing = " +
jellyScriptHousing.getProject().getProjectProperty( "maven.compile.target") );
+ if ( seen.contains( goalToPrep ) )
+ {
+ continue;
+ }
- return pluginContext;
- }
+ seen.add( goalToPrep );
- private MavenJellyContext createBaseContext( Project project )
- {
- // We are making the compileScriptContext the parent so that the werkz
- // project is present
- MavenJellyContext baseContext = new MavenJellyContext( compileScriptContext
);
- baseContext.setInherit( true );
+ // We check to see if the goalToPrep has been defined in any user
+ // jelly script. This allows any goal specified in a plugin to be
+ // overriden if a user desires.
+ String spec = transientCacheManager.getPluginCache().getProperty(
goalToPrep );
- // Set the created context, and put the project itself in the context. This
- // is how we get the ${pom} reference in the project.xml file to work.
- baseContext.setProject( project );
+ if ( spec == null )
+ {
+ spec = cacheManager.getPluginCache().getProperty( goalToPrep );
- // Now we have to setup the classloaders correctly
+ if ( spec != null )
+ {
+ prepForCallbacks( goalToPrep, project );
+ loadPlugin( spec, project );
+ }
+ }
- return baseContext;
+ // Find any prerequisite goals and add them to the list of
+ // goals to prepare.
+ List prereqs = getPrereqs( goalToPrep );
+ goalsToPrep.addAll( prereqs );
+ }
}
- /** XML output goes to System.out*/
- private XMLOutput output;
+ // ----------------------------------------------------------------------
+ // P R E P A R A T I O N M E T H O D S
+ // ----------------------------------------------------------------------
- private XMLOutput getJellyOutputSink()
+ /**
+ * Prepare and load plugins based upon callback dependencies.
+ *
+ * @param goalName The goal name.
+ *
+ * @throws Exception If an error occurs while attempting to preprare
+ * callback dependencies.
+ */
+ void prepForCallbacks( String goalName, Project project )
throws Exception
{
- if ( output == null )
+ if ( cacheManager.getCallbackCache().containsKey( goalName + ".pre" ) )
{
- PrintStream consoleOut = System.out;
- //PrintStream consoleErr = System.err;
-
- Writer writer = new OutputStreamWriter( consoleOut );
- output = XMLOutput.createXMLOutput( writer, false );
+ loadPlugins( cacheManager.getCallbackCache().getProperty( goalName +
".pre" ), project );
}
- return output;
+ if ( cacheManager.getCallbackCache().containsKey( goalName + ".post" ) )
+ {
+ loadPlugins( cacheManager.getCallbackCache().getProperty( goalName +
".post" ), project );
+ }
}
/**
- * FIXME: Not sure why building an Ant project is the plugin manager's
responisbility
- *
- * @param project a maven project
- * @param context a maven context
- * @return an Ant project
- * @throws Exception When any error occurs. FIXME this is bad.
+ * Prepare the tag libary for use.
+ *
+ * @param pluginName URI of the tag library to prepare.
+ * @param project Project to load the dyna tag libs into.
+ *
+ * @throws Exception if an error occurs while preparing dyna tag libs.
*/
- private GrantProject buildAntProject( Project project, MavenJellyContext
context )
+ void prepDynaTagLibs( String pluginName, Project project )
throws Exception
{
- // Create the build listener.
- JellyBuildListener buildListener = new JellyBuildListener(
getJellyOutputSink() );
- if ( System.getProperty( MavenConstants.DEBUG_ON ).equals( "true" ) )
- {
- buildListener.setDebug( true );
- }
+ String depPlugins = cacheManager.getPluginDynaTagDepsCache().getProperty(
pluginName );
- if ( System.getProperty( MavenConstants.EMACS_MODE_ON ).equals( "true" ) )
+ if ( depPlugins == null )
{
- buildListener.setEmacsMode( true );
+ return;
}
- // Create our ant project.
- GrantProject antProject = new GrantProject();
- antProject.setPropsHandler( new JellyPropsHandler( context ) );
- antProject.init();
- antProject.setBaseDir( project.getFile().getParentFile() );
- antProject.addBuildListener( buildListener );
-
- context.setAntProject( antProject );
- AntTagLibrary.setProject( context, antProject );
+ String[] list = StringUtils.split( depPlugins, "," );
- Path p = new Path( antProject );
- p.setPath( project.getDependencyClasspath() );
- antProject.addReference( MavenConstants.DEPENDENCY_CLASSPATH, p );
-
- return antProject;
+ for ( int i = 0; i < list.length; i++ )
+ {
+ prepDynamicTagLib( list[i], project );
+ }
}
-
- private JellyScriptHousing createJellyScriptHousing( Project project, File
classesDirectory, File jelly )
+ /**
+ * Prepare an individual dyna tag lib for use.
+ *
+ * @param uri URI of the dyna tag.
+ * @param project Project to load the dyna tag lib into.
+ *
+ * @throws Exception If an error occurs preparing the dynamic tag library.
+ */
+ void prepDynamicTagLib( String uri, Project project )
throws Exception
{
- InputStream is = null;
+ String depPlugin = cacheManager.getDynaTagLibCache().getProperty( uri );
- if ( jelly.exists() )
+ // Some of the may be currently null because some dyna tag libs aren't
+ // in plugins but in the driver.jelly file. This needs to be unified where
+ // everything comes from a plugin.
+ if ( depPlugin == null )
{
- is = new FileInputStream( jelly );
+ return;
}
- JellyScriptHousing jellyScriptHousing = createJellyScriptHousing( project,
classesDirectory, is );
- jellyScriptHousing.setSource( jelly.getPath() );
-
- return jellyScriptHousing;
+ loadPlugin( depPlugin, project );
}
/**
- * @param plugin the plugin to create a housing for
- * @return a housing
- * @throws MalformedURLException
- * @throws Exception
+ * Retrieve the prerequisites for a goal.
+ *
+ * @param name The goal name.
+ *
+ * @return A list of <code>String</code> prerequisite goal names.
*/
- private JellyScriptHousing createJellyScriptHousing(JellyPlugin plugin)
- throws MalformedURLException, Exception
+ List getPrereqs( String name )
{
- JellyScriptHousing jellyScriptHousing = new JellyScriptHousing();
- jellyScriptHousing.setId( plugin.getId() );
+ String spec = transientCacheManager.getGoalCache().getProperty( name );
+
+ if ( spec == null )
+ {
+ spec = cacheManager.getGoalCache().getProperty( name );
+ }
+
+ if ( spec == null )
+ {
+ return Collections.EMPTY_LIST;
+ }
+
+ int splitLoc = spec.indexOf( ">" );
- // Now before we even _compile_ the jelly script you must set the
classloader because
- // the whole process needs to be able to resolve the tags. I thought a
StaticTag was
- // supposed to be created and the search deferred but this is _not_ the
case.
- jellyScriptHousing.setClassLoader( plugin.getClassLoader() );
- jellyScriptHousing.setProject( plugin.getProject() );
- jellyScriptHousing.setScript(
plugin.getCompiledScriptFor(compileScriptContext) );
+ if ( ( splitLoc < 0 )
+ ||
+ ( splitLoc + 1 ) == spec.length() )
+ {
+ return Collections.EMPTY_LIST;
+ }
+
+ String prereqSpec = spec.substring( splitLoc + 1 );
- return jellyScriptHousing;
+ StringTokenizer tokens = new StringTokenizer( prereqSpec, "," );
+
+ List prereqs = new ArrayList();
+
+ while ( tokens.hasMoreTokens() )
+ {
+ prereqs.add( tokens.nextToken() );
+ }
+
+ return prereqs;
}
- private JellyScriptHousing createJellyScriptHousing( Project project, File
unpackedPluginDirectory, InputStream jelly )
+ // ----------------------------------------------------------------------
+ // 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.
+ */
+ public void loadPlugin( String name, Project project )
throws Exception
{
- JellyScriptHousing jellyScriptHousing = new JellyScriptHousing();
+ if ( isLoaded( project, name ) )
+ {
+ return;
+ }
- // Now we are going to create a ClassLoader for the plugins classes and the
JARs
- // that it depends on for executing.
- ForeheadClassLoader classLoader = new ForeheadClassLoader(
mavenRootClassLoader, project.getId() );
+ File pluginScript = getPluginScript( name );
- if ( unpackedPluginDirectory != null )
+ if ( pluginScript.exists() == false )
{
- // We will add the plugin classes to the plugin class loader.
- classLoader.addURL( unpackedPluginDirectory.toURL() );
+ return;
}
- // We will add the JARs that have been instructed to be place in the class
loader
- // for use in the plugin in the plugin class loader.
- processDependencies( project, classLoader );
+ // We need to make sure all the dyna tag libs are initialized for this
+ // plugin.
+ prepDynaTagLibs( name, project );
- //displayClassLoaderContents( project, classLoader );
+ File unpackedPluginDir = getUnpackedPluginDir( name );
- // Now before we even _compile_ the jelly script you must set the
classloader because
- // the whole process needs to be able to resolve the tags. I thought a
StaticTag was
- // supposed to be created and the search deferred but this is _not_ the
case.
- compileScriptContext.setClassLoader( classLoader );
+ // Use classworlds
+ Forehead.getInstance().getClassLoader( "root.maven" )
+ .addURL( unpackedPluginDir.toURL() );
- // Now lets compile the script once so we can use it repeatedly.
- Script script = null;
+ Project pluginProject =
+ MavenUtils.getProject( new File( unpackedPluginDir,
+ "project.xml" ),
+ project.getContext(),
+ false );
- // Projects may not have a maven.xml file.
- if ( jelly != null )
+ if ( isPluginProcessed( name ) == false )
{
- try
- {
- script = JellyUtils.compileScript( jelly, compileScriptContext );
- }
- catch ( Exception e )
- {
- e.printStackTrace();
- }
+ pluginProject.verifyDependencies();
+
+ // Mark the plugin as processed.
+ FileUtils.fileWrite( getPluginProcessedMarker( name ).getPath(),
+ "plugin has been processed.");
}
- jellyScriptHousing.setClassLoader( classLoader );
- jellyScriptHousing.setProject( project );
- jellyScriptHousing.setScript( script );
- jellyScriptHousing.setId( project.getId() );
+ // place dependencies on the right classloaders
+ 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( unpackedPluginDir ),
+ pluginContext );
+ pluginContext.setVariable( "plugin", pluginProject );
+ pluginContext.setVariable( "plugin.dir", unpackedPluginDir );
- return jellyScriptHousing;
- }
+ // Set a context variable that points to the plugin resources directory
+ pluginContext.setVariable( "plugin.resources",
+ new File( unpackedPluginDir, "plugin-resources" ) );
- private void displayClassLoaderContents( Project project, ForeheadClassLoader
classLoader )
- {
- URL[] urls = classLoader.getURLs();
+ project.addPluginContext( pluginProject.getId(), pluginContext );
- System.out.println( "project.getName() = " + project.getName() );
+ JellyUtils.runScript( pluginScript,
+ unpackedPluginDir.toURL(),
+ pluginContext,
+ pluginContext.getXMLOutput() );
- for ( int i = 0; i < urls.length; i++ )
+ // 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 )
{
- System.out.println( "urls[" + i + "] = " + urls[i] );
+ project.getContext().setWerkzProject( pluginContext.getWerkzProject() );
}
-
- ClassLoader parent = classLoader.getParent();
- if (parent != null && parent instanceof ForeheadClassLoader)
+
+ // 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() )
{
- System.out.println("Displaying Parent classloader: ");
- displayClassLoaderContents(project,
(ForeheadClassLoader)classLoader.getParent());
+ loadPlugin( tokens.nextToken(), project );
}
}
+ // ----------------------------------------------------------------------
+ // C A C H I N G
+ // ----------------------------------------------------------------------
+
/**
- * process the dependencies for this project
+ * Cache a plugin.
+ *
+ * @param name The plugin name.
+ *
+ * @throws Exception If an error occurs while attempting to analyze
+ * and cache plugin information.
*/
- public static void processDependencies( Project project, ForeheadClassLoader
classLoader )
- throws MalformedURLException
+ void cachePlugin( String name )
+ throws Exception
{
- if ( project.getArtifacts() == null )
+ log.debug( "Processing Plugin: " + name );
+
+ 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() )
{
- return;
+ cacheManager.setPluginScript( pluginScript );
+ cacheManager.parse();
}
+ }
- for ( Iterator i = project.getArtifacts().iterator(); i.hasNext(); )
+ /**
+ * 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(); )
{
- Artifact artifact = (Artifact) i.next();
- Dependency dependency = artifact.getDependency();
- String classloaderProperty = dependency.getProperty( "classloader" );
-
- // Only add compile type dependencies to classloader
- // what about ejbs etc
- if ( dependency.getType().equals( "jar" ) )
+ String eachGoal = (String) i.next();
+
+ if ( cacheManager.getPluginCache().getProperty( eachGoal ).equals(
pluginName ) )
{
- // We have the jar and the classloader to push it into so
- // lets do it!
- if ( artifact.exists() )
- {
- if ( classloaderProperty != null )
- {
- ForeheadClassLoader otherClassLoader =
-
Forehead.getInstance().getClassLoader(classloaderProperty);
- if (otherClassLoader != null)
- {
- otherClassLoader.addURL( artifact.getFile().toURL() );
- }
- }
- else
- {
- classLoader.addURL( artifact.getFile().toURL() );
- }
- }
+ 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 );
}
/**
- * Load plugins specified in a whitespace delimited string.
+ * Determine if a goal has been cached.
*
- * @param names The whitespace delimited string of plugin names.
+ * @param name The name of the goal to test.
*
- * @throws Exception If an error occurs while attempting to load
- * the plugins.
+ * @return <code>true</code> if the goal has already been cached,
+ * otherwise <code>false</code>.
*/
- void loadPlugins( String names, Project project )
- throws Exception
+ boolean isGoalCached( String name )
{
- StringTokenizer tokens = new StringTokenizer( names, "," );
+ return cacheManager.getGoalCache().containsKey( name );
+ }
- while ( tokens.hasMoreTokens() )
- {
- loadPlugin( tokens.nextToken() );
- }
+ Set getTagLibsCache()
+ {
+ return cacheManager.getDynaTagLibCache().keySet();
}
/**
@@ -828,7 +891,7 @@
File getPluginProcessedMarker( String pluginName )
{
return new File( new File( getUnpackedPluginsDir(), pluginName ),
- ".processed" );
+ ".processed" );
}
/**
@@ -851,11 +914,44 @@
* @return <code>true</code> if the plugin has been loaded,
* otherwise <code>false</code>.
*/
- boolean isLoaded( String id )
+ boolean isLoaded( Project project, String name )
+ {
+ return loadedPlugins.contains( project.hashCode() + name );
+ }
+
+ /**
+ * Retrieve the plugin's default properties.
+ *
+ * @param unpackedPluginDir The location of the unpacked 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 unpackedPluginDir )
+ throws IOException
{
- return loadedPlugins.get( id ) != null;
+ File propsFile = new File( unpackedPluginDir, 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 where the specified plugin is unpacked.
*
@@ -869,6 +965,20 @@
}
/**
+ * 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( getUnpackedPluginDir( pluginName ),
+ PLUGIN_SCRIPT_NAME );
+ }
+
+ /**
* Sets the pluginsDir attribute of the PluginManager object
*
* @param pluginsDir The maven plugin directory.
@@ -896,6 +1006,7 @@
void setUnpackedPluginsDir( File unpackedPluginsDir )
{
this.unpackedPluginsDir = unpackedPluginsDir;
+ cacheManager.setUnpackedPluginsDir( unpackedPluginsDir );
}
/**
@@ -906,5 +1017,31 @@
File getUnpackedPluginsDir()
{
return unpackedPluginsDir;
+ }
+
+ /**
+ * Load and install a plugin
+ * @todo should check if it's already installed.
+ * @todo I'm not sure if caching needs to be called.
+ * @param file
+ */
+ public void installPlugin(Project project, File file) throws Exception
+ {
+ // copy the file to the unpacked plugins dir
+ FileUtils.copyFileToDirectory(file, getPluginsDir());
+ String pluginName = file.getCanonicalFile().getName();
+ pluginName = pluginName.substring(0, pluginName.indexOf(".jar"));
+ String newFileName = getPluginsDir().getCanonicalPath()
+ + File.separator + file.getCanonicalFile().getName();
+ // expand it
+ Expand unzipper = new Expand();
+ unzipper.setSrc( new File(newFileName));
+ File unzipDir = new File( getUnpackedPluginsDir(), pluginName);
+ unzipper.setDest( unzipDir );
+ unzipper.execute();
+ // load it
+ loadPlugin(pluginName, project);
+ cachePlugin(pluginName);
+ // FIXME: Does it need caching too?
}
}
1.14 +2 -2 maven/src/java/org/apache/maven/plugin/PluginCacheManager.java
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]