o Added support to RewritePhase of repoclean to copy -SNAPSHOT.version.txt and -RELEASE.version.txt (or whatever it's called) using ArtifactMetadata, where these files exist in the source repo.

o Reformatted mojo annotation doco to tabular format.
o Committed a start to the plugin-overview (developer's PoV on this)...it's just an outline with an incomplete introduction for now, but it's still a start.

git-svn-id: https://svn.apache.org/repos/asf/maven/components/trunk@169873 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
John Dennis Casey 2005-05-12 18:59:54 +00:00
parent ccb9ae4b97
commit 8833620278
3 changed files with 680 additions and 360 deletions

View File

@ -91,13 +91,15 @@
</lt>
<li>
<code>
void setLog( org.apache.maven.monitor.logging.Log )
void setLog( org.apache.maven.monitor.logging.Log )
</code>
<p>Inject a standard Maven logging mechanism to allow this Mojo
to communicate events and feedback to the user.</p>
</li>
<li>
<code>void execute() throws org.apache.maven.plugin.MojoExecutionException</code>
<code>
void execute() throws org.apache.maven.plugin.MojoExecutionException
</code>
<p>Perform whatever build-process behavior this Mojo implements.
This is the main trigger for the Mojo inside the Maven system,
and allows the Mojo to communicate fatal errors by throwing an
@ -128,7 +130,7 @@
</lt>
<li>
<code>
public void setLog( org.apache.maven.monitor.logging.Log )
public void setLog( org.apache.maven.monitor.logging.Log )
</code>
<p>
<b>[IMPLEMENTED]</b>
@ -146,7 +148,7 @@
</li>
<li>
<code>
void execute() throws org.apache.maven.plugin.MojoExecutionException
void execute() throws org.apache.maven.plugin.MojoExecutionException
</code>
<p>
<b>[ABSTRACT]</b>
@ -178,7 +180,7 @@
</li>
<li>
<code>
void debug( java.lang.CharSequence, java.lang.Throwable )
void debug( java.lang.CharSequence, java.lang.Throwable )
</code>
<p>Send a message (and accompanying exception) to the user in the
<b>debug</b> error level. The error's stacktrace will be output
@ -196,7 +198,7 @@
</li>
<li>
<code>
void info( java.lang.CharSequence, java.lang.Throwable )
void info( java.lang.CharSequence, java.lang.Throwable )
</code>
<p>Send a message (and accompanying exception) to the user in the
<b>info</b> error level. The error's stacktrace will be output
@ -214,7 +216,7 @@
</li>
<li>
<code>
void warn( java.lang.CharSequence, java.lang.Throwable )
void warn( java.lang.CharSequence, java.lang.Throwable )
</code>
<p>Send a message (and accompanying exception) to the user in the
<b>warn</b> error level. The error's stacktrace will be output
@ -232,7 +234,7 @@
</li>
<li>
<code>
void error( java.lang.CharSequence, java.lang.Throwable )
void error( java.lang.CharSequence, java.lang.Throwable )
</code>
<p>Send a message (and accompanying exception) to the user in the
<b>error</b> error level. The error's stacktrace will be output
@ -262,71 +264,127 @@
element name along with a javadoc annotation (if applicable) supporting
that piece of the plugin descriptor. A couple of examples are:
<b>someElement
(@annotation parameterName="parameterValue")</b> or
(@annotation parameterName="parameterValue")</b> or
<b>someOtherElement (@annotation &lt;rawAnnotationValue&gt;)</b>.</p>
<p>The plugin descriptor must be provided in a jar resource with the
path: <code>META-INF/maven/plugin.xml</code>, and it must contain the
following:</p>
<p>
<ul>
<li><b>mojos</b> - Descriptors for each Mojo provided by
the plugin, each inside a <b>mojo</b> sub-element. Mojo descriptors
are covered in detail below.</li>
<li><b>dependencies</b> - A set of dependencies which the
plugin requires in order to function. Each dependency is provided
inside a <b>dependency</b> sub-element. Dependency specifications
are covered below. <i>Using the plugin toolset, these dependencies
can be extracted from the POM's dependencies.</i></li>
</ul>
<table>
<tr>
<th>Descriptor Element</th>
<th>Required?</th>
<th>Notes</th>
</tr>
<tr>
<td>mojos</td>
<td>Yes</td>
<td>Descriptors for each Mojo provided by the plugin, each inside a
<b>mojo</b> sub-element. Mojo descriptors are covered in detail
below. Obviously, a plugin without any declared mojos doesn't
make sense, so the <b>mojos</b> element is required, along with
at least one <b>mojo</b> sub-element.</td>
</tr>
<tr>
<td>dependencies</td>
<td>Yes</td>
<td>A set of dependencies which the plugin requires in order to
function. Each dependency is provided inside a <b>dependency</b>
sub-element. Dependency specifications are covered below. Since
all plugins must have a dependency on
<code>maven-plugin-api</code>, this element is effectively
required. <i>Using the plugin toolset, these dependencies can be
extracted from the POM's dependencies.</i></td>
</tr>
</table>
</p>
<p>Each Mojo specified inside a plugin descriptor must provide the
following:</p>
following (annotations specified here are at the class level):</p>
<p>
<ul>
<li><b>goal. (@goal &lt;goalName&gt;)</b> - The name for
the Mojo that users will reference from the command line to execute
the Mojo directly, or inside a POM in order to provide
Mojo-specific configuration.</li>
<li><b>implementation (detected)</b> - The
Mojo's fully-qualified class name.</li>
<li><b>language (detected)</b> - The implementation
language for this Mojo (marmalade, java, etc.). <i>Optional,
default is java.</i></li>
<li><b>configurator (detected)</b> - The configurator type
used to inject parameters into the Mojo. <i>Optional; will only be
used in very special cases, with a highly controlled vocabulary of
possible values. (Elements like this are why it's a good idea to
use the descriptor tools.)</i></li>
<li><b>phase
(@phase &lt;phaseName&gt;)</b> - Specifies to which
phase in the standard build lifecycle this Mojo binds. <i>Optional;
only required if this Mojo participates in the standard build
process.</i></li>
<li><b>executePhase
(@executePhase &lt;phaseName&gt;)</b> - Specifies
the last phase in the standard build lifecycle to execute before
executing this Mojo, when the Mojo's goal is invoked directly.
<i>Optional.</i></li>
<li><b>instantiationStrategy
(@instantiationStrategy &lt;strategyName&gt;)</b> - Specifies
the method Maven should use in instantiating this Mojo before it is
executed, which has implications for the Mojo's state and instance
reuse. <i>Optional. Currently, only "per-lookup" is suppported.
(<b>Brett:</b> Can we deprecate this before alpha-2
release?)</i></li>
<li><b>requiresDependencyResolution
(@requiresDependencyResolution &lt;requiredScope&gt;)</b> - Flags
this Mojo as requiring the specified scope of dependencies to be
resolved before it can execute. <i>Optional; Currently supports
"compile", "runtime", and "test" scopes.</i></li>
<li><b>description (detected)</b> - The description of the
Mojo's functionality. <i>Using the toolset, this will be the
class-level javadoc description provided.</i></li>
<li><b>parameters</b> - Specifications for the parameters
which this Mojo uses. Each parameter is specified in a
<b>parameter</b> sub-element. Parameters are discusses in
detail below.</li>
</ul>
<table>
<tr>
<th>Descriptor Element</th>
<th>Annotation</th>
<th>Required?</th>
<th>Notes</th>
</tr>
<tr>
<td>goal</td>
<td>@goal &lt;goalName&gt;</td>
<td>Yes</td>
<td>The name for the Mojo that users will reference from the
command line to execute the Mojo directly, or inside a POM in
order to provide Mojo-specific configuration.</td>
</tr>
<tr>
<td>implementation</td>
<td>none (detected)</td>
<td>Yes</td>
<td>The Mojo's fully-qualified class name (or script path in
the case of non-java mojos).</td>
</tr>
<tr>
<td>language</td>
<td>none (detected)</td>
<td>No. Default: <code>java</code></td>
<td>The implementation language for this Mojo (marmalade, java,
beanshell, etc.).</td>
</tr>
<tr>
<td>configurator</td>
<td>none (detected)</td>
<td>No</td>
<td>The configurator type to use when injecting parameter values
into this Mojo. <i>NOTE: This will only be used in very special
cases, using a highly controlled vocabulary of possible values.
(Elements like this are why it's a good idea to use the
descriptor tools.)</i></td>
</tr>
<tr>
<td>phase</td>
<td>@phase &lt;phaseName&gt;</td>
<td>No</td>
<td>Binds this Mojo to a particular phase of the standard build
lifecycle, if specified. <i>NOTE: This is only required if this
Mojo is to participate in the standard build process.</i></td>
</tr>
<tr>
<td>executePhase</td>
<td>@executePhase &lt;phaseName&gt;</td>
<td>No</td>
<td>When this Mojo's goal is invoked directly from the command
line, <b>executePhase</b> specifies the last phase in the
standard build lifecycle to execute before this Mojo
executes.</td>
</tr>
<tr>
<td>requiresDependencyResolution</td>
<td>@requiresDependencyResolution &lt;requiredScope&gt;</td>
<td>No</td>
<td>Flags this Mojo as requiring the dependencies in the specified
scope (or an implied scope) to be resolved before it can execute.
<i>NOTE: Currently supports <b>compile</b>,
<b>runtime</b>, and <b>test</b> scopes.</i></td>
</tr>
<tr>
<td>description</td>
<td>none (detected)</td>
<td>No</td>
<td>The description of this Mojo's functionality. <i>Using the
toolset, this will be the class-level javadoc description
provided. NOTE: While this is not a required part of the mojo
specification, it SHOULD be provided to enable future
tool support for browsing, etc. and for clarity.</i></td>
</tr>
<tr>
<td>parameters</td>
<td>N/A</td>
<td>No</td>
<td>Specifications for the parameters which this Mojo uses will be
provided in <b>parameter</b> sub-elements in this section.
<i>NOTE: Parameters are discussed in more detail below.</i></td>
</tr>
</table>
</p>
<p>Each Mojo specifies the parameters that it expects to be able to work
with. These parameters are the Mojo's link to the outside world, and
@ -344,63 +402,104 @@
<p>Each parameter for a Mojo must be specified in the
plugin descriptor as follows:</p>
<p>
<ul>
<li><b>name (detected)</b> - The name of the parameter, to
be used in configuring this parameter from the Mojo's declared
defaults (discussed below) or from the POM. <i>Using the toolset,
this is detected as the java field name. It cannot be
specified in an annotation.</i></li>
<li><b>alias (@parameter alias="myAlias")</b> - Specifies
an alias which can be used to configure this parameter from the
POM. This is primarily useful to improve user-friendliness, where
Mojo field names are not intuitive to the user.
<i>Optional.</i></li>
<li><b>type (detected)</b> - The type of this parameter.
This is used to validate the result of any expressions used to
calculate the value which should be injected into the Mojo for this
parameter. <i>Using the toolset, this is detected as the class of
this parameter's java field. It cannot be specified in an
annotation.</i></li>
<li><b>required (@required)</b> - Whether this parameter is
required for the Mojo to function. This is used to validate the
configuration for a Mojo before it is injected, and before the Mojo
is executed from some half-state. <i>Optional. Specification of
this annotation flags the parameter as required; there is
no true/false value.</i></li>
<li><b>editable (@readonly)</b> - Specifies that this
parameter cannot be configured directly by the user (as in the case
of POM-specified configuration). This is useful when you want to
force the user to use common POM elements rather than plugin
configurations, as in the case where you want to use the artifact's
final name as a parameter. In this case, you want the user to
modify &lt;build&gt;&lt;finalName/&gt;&lt;/build&gt; rather than
specifying a value for finalName directly in the plugin
configuration section. It is also useful to ensure that - for
example - a List-typed parameter which expects items of type
Artifact doesn't get a List full of Strings. <i>Optional.
Specification of this annotation flags the parameter as
non-editable; there is no true/false value.</i></li>
<li><b>description (detected)</b> - The description for
what this parameter is used for inside the Mojo. <i>Using the
toolset, this is detected as the javadoc description for
the field. It cannot be specified as an annotation.</i></li>
<li><b>expression
(@parameter expression="${someExpression}")</b> - Specifies
the expression used to calculate the value to be injected into this
parameter of the Mojo at buildtime. This is commonly used to refer
to specific elements in the POM, such as
${project.build.resources}, which refers to the List of resources
meant to accompany the classes in the resulting jar file.
<i>Optional. If not specified, an expression of ${&lt;name&gt;} is
assumed, which can only be satisfied from POM configuration or
System properties. The use of '${' and '}' is required to
delimit actual expressions which may be evaluated.</i></li>
<li><b>deprecated (@deprecated)</b> - Marks a parameter as
deprecated. The rules on deprecation are the same as normal java
with language elements. This will trigger a warning when a user
tries to configure a parameter marked as deprecated.
<i>Optional.</i></li>
</ul>
<table>
<tr>
<th>Descriptor Element</th>
<th>Annotation</th>
<th>Required?</th>
<th>Notes</th>
</tr>
<tr>
<td>name</td>
<td>none (detected)</td>
<td>Yes</td>
<td>The name of the parameter, to be used in configuring this
parameter from the Mojo's declared defaults (discussed below) or
from the POM. <i>Using the toolset, this is detected as the
java field name.</i></td>
</tr>
<tr>
<td>alias</td>
<td>@parameter alias="myAlias"</td>
<td>No</td>
<td>Specifies an alias which can be used to configure this
parameter from the POM. This is primarily useful to improve
user-friendliness, where Mojo field names are not intuitive to
the user or are otherwise not conducive to configuration via
the POM.</td>
</tr>
<tr>
<td>type</td>
<td>none (detected)</td>
<td>Yes</td>
<td>The java type for this parameter. This is used to validate the
result of any expressions used to calculate the value which
should be injected into the Mojo for this parameter. <i>Using the
toolset, this is detected as the class of the java field
corresponding to this parameter.</i></td>
</tr>
<tr>
<td>required</td>
<td>@required</td>
<td>No</td>
<td>Whether this parameter is required for the Mojo to function.
This is used to validate the configuration for a Mojo before it
is injected, and before the Mojo is executed from some
half-state. <i>NOTE: Specification of this annotation flags
the parameter as required; there is no true/false value.</i></td>
</tr>
<tr>
<td>editable</td>
<td>@readonly</td>
<td>No</td>
<td>Specifies that this parameter cannot be configured directly by
the user (as in the case of POM-specified configuration). This is
useful when you want to force the user to use common POM elements
rather than plugin configurations, as in the case where you want
to use the artifact's final name as a parameter. In this case,
you want the user to modify
&lt;build&gt;&lt;finalName/&gt;&lt/build&gt; rather than
specifying a value for finalName directly in the plugin
configuration section. It is also useful to ensure that - for
example - a List-typed parameter which expects items of type
Artifact doesn't get a List full of Strings. <i>NOTE:
Specification of this annotation flags the parameter as
non-editable; there is no true/false value.</i></td>
</tr>
<tr>
<td>description</td>
<td>none (detected)</td>
<td>No</td>
<td>The description of this parameter's use inside the Mojo.
<i>Using the toolset, this is detected as the javadoc description
for the field. NOTE: While this is not a required part of the
parameter specification, it SHOULD be provided to enable future
tool support for browsing, etc. and for clarity.</i></td>
</tr>
<tr>
<td>expression</td>
<td>@parameter expression="${someExpression}"</td>
<td>No</td>
<td>Specifies the expression used to calculate the value to be
injected into this parameter of the Mojo at buildtime. This is
commonly used to refer to specific elements in the POM, such as
${project.build.resources}, which refers to the List of resources
meant to accompany the classes in the resulting jar file.
<i>NOTE: If not specified, an expression of ${&lt;name&gt;} is
assumed, which can only be satisfied from POM configuration or
System properties. The use of '${' and '}' is required to delimit
actual expressions which may be evaluated.</i></td>
</tr>
<tr>
<td>deprecated</td>
<td>@deprecated</td>
<td>No</td>
<td>Marks a parameter as deprecated. The rules on deprecation are
the same as normal java with language elements. This will trigger
a warning when a user tries to configure a parameter marked as
deprecated. </td>
</tr>
</table>
</p>
<p>The final component of a plugin descriptor is the dependencies. This
enables the plugin to function independently of it's POM (or at least
@ -413,14 +512,17 @@
</section>
<section name="Plugin Tools">
<p>By now, we've mentioned the plugin tools several times without telling
you what they are or how to use them. instead of writing (and
maintaining) this metadata file by hand, Maven 2.0 ships with some
tools to aid developers in this task. In fact, the only thing a plugin
developer needs to do is declare his project to be a plugin from within
the POM. Once this is done, Maven will call the appropriate descriptor
you what they are or how to use them. Instead of manually writing (and
maintaining) the metadata detailed above, Maven 2.0 ships with some
tools to aid in this task. In fact, the only thing a plugin developer
needs to do is declare his project to be a plugin from within the POM.
Once this is done, Maven will call the appropriate descriptor
generators, etc. to produce an artifact that is ready for use within
Maven builds. The section below describes the changes to the POM
which are necessary to create plugin artifacts.</p>
Maven builds. Optional metadata can be injected via javadoc annotation
(and possibly jdk5 annotations in the future) as described above,
enabling richer interactions between the Mojo and the user. The
section below describes the changes to the POM which are necessary to
create plugin artifacts.</p>
</section>
<section name="Project Descriptor (POM) Requirements">
<p>From the POM, Maven plugin projects look quite similar to any other
@ -428,30 +530,52 @@
for script-based plugins. The following details the POM elements
which are necessary to build a Maven plugin artifact.</p>
<p>
<ul>
<li><b>packaging Element.</b> The POM must declare a packaging
element which describes this project as a Maven plugin project. The
element is specified as:
<code>&lt;packaging&gt;maven-plugin&lt;/packaging&gt;</code></li>
<li><b>scriptSourceDirectory Element.</b> In the case of script-based
Mojos (which are not covered in detail within this document), the
POM must include an additional element to distinguish script
sources from (optional) java supporting classes. This element is
the scriptSourceDirectory, inside the build section. It is
specified as:
<code>&lt;scriptSourceDirectory&gt;src/main/scripts&lt;/scriptSourceDirectory&gt;</code>.
This directory is included in the list of resources which accompany
any compiled code in the resulting artifact. It is specified
separately from the resources in the build section to denote its
special status as an alternate source directory for scripts.</li>
</ul>
<table>
<tr>
<th>POM Element</th>
<th>Required for Java Mojos?</th>
<th>Sample Declaration</th>
<th>Notes</th>
</tr>
<tr>
<td>packaging</td>
<td>Yes</td>
<td><code>&lt;packaging&gt;maven-plugin&lt;/packaging&gt;</code></td>
<td>The POM must declare a packaging element which describes this
project as a Maven plugin project.</td>
</tr>
<tr>
<td>scriptSourceDirectory</td>
<td>No</td>
<td><code>&lt;scriptSourceDirectory&gt;src/main/scripts&lt;/scriptSourceDirectory&gt;</code></td>
<td>In the case of script-based Mojos (which are not covered in
detail within this document), the POM must include an additional
element to distinguish script sources from (optional) java
supporting classes. This element is the scriptSourceDirectory,
inside the build section. This directory is included in the list
of resources which accompany any compiled code in the resulting
artifact. It is specified separately from the resources in the
build section to denote its special status as an alternate source
directory for scripts.</td>
</tr>
</table>
</p>
<p>After making the changes above, the developer can simply call <code>m2
install</code> to install the plugin to the local repository. (Any of
the other standard lifecycle targets like package, deploy, etc. are
also available in like fashion.)</p>
<p>After making the changes above, the developer can simply call
<br/><br/><code>m2 install</code><br/><br/> to install the plugin to
the local repository. (Any of the other standard lifecycle targets like
package, deploy, etc. are also available in like fashion.)</p>
</section>
<section name="Resources">
<p>This section simply gives a listing of pointers for more
information.</p>
<p>
<ul>
<li>M2 Plugin Overview [<a
href="plugin-overview.html">link</a>]</li>
<li>QDox Project (javadoc annotations) [<a
href="http://qdox.codehaus.org">link</a>]</li>
</ul>
</p>
</section>
</body>
</document>

View File

@ -0,0 +1,130 @@
<?xml version="1.0"?>
<!--
/*
* Copyright 2001-2004 The 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.
*/
-->
<document>
<properties>
<title>Maven 2.0 Plugin Development</title>
<author email="jdcasey@apache.org">John Casey</author>
</properties>
<body>
<section name="Introduction">
<subsection name="Similarities to Maven 1.x">
<p> Maven 2.0 is similar to its predecessor in that it has two main
functions. First, it organizes project data into a coherent whole,
and exposes this data for use within the build process. Second, Maven
marshals a set of plugins to do the heavy lifting and execute the
actual steps of the build. </p>
<p> Many things in Maven 2 will have at least superficial familiarity
to users of Maven 1, and the plugin system is no exception. Maven 2
plugins appear to behave much as their 1.x counterparts do. Like 1.x
plugins, they use both project information and custom-defined
configurations to perform their work. Also, Maven 2 plugins are
organized and executed in a coherent way by the build engine itself -
that is to say, the engine is still responsible for organizing and
fulfilling a plugin's requirements before executing the plugin
itself. </p>
<p>Operationally, Maven 2.0 should feel very much like a more
performant big brother of Maven 1.x. While the POM has definitely
changed, it has the same basic layout and features (with notable
additions). However, this is where the similarity ends. Maven 2.0 is
a complete redesign and reimplementation of the Maven build concept.
As such, it has a much different - and more evolved, at least to
our minds ;-) - architecture.</p>
</subsection>
<subsection name="Differences from Maven 1.x">
<p> However similar the architectures may seem, Maven 2 offers a much
richer environment for its plugins than Maven 1 ever did. The new
architecture offers a managed lifecycle, multiple implementation
languages, reusability outside of the build system, and many more
advantages. Arguably the biggest advantage is the ability to write
Maven plugins entirely in Java, which allows developers to tap into a
rich landscape of development and testing tools to aid in their
efforts. </p>
<p> Prior to Maven 2.0, the build system organized relevant plugins
into a loosely defined lifecycle, which was determined based on goal
prerequisites and decoration via preGoals and postGoals. That
experience was critical for the Maven community. It taught us that
even though there may be a million different build scenarios out
there, most of the activities in those builds fit into just a few
broad categories. Moreover, the category to which a goal fits serves
as an accurate predictor for where in the build process the goal
should execute. Drawing on this experience, Maven 2.0 defines a
lifecycle within which plugins are managed according to their
relative position within this lifecycle </p>
<p> Starting with Maven 2.0, plugins implemented in different
programming or scripting languages can coexist within the same build
process. This removes the requirement that plugin developers learn a
particular scripting language in order to interact with Maven. It
also reduced the risk associated with the stability or richness of
any particular scripting language. </p>
<p>Also starting with Maven 2.0 is an effort to integrate multiproject
builds directly into the core architecture. In Maven 1.x, many large
projects were fragmented into smaller builds to sidestep issues such
as conditional compilation of a subset of classes; separation
of client-server code; or cyclical dependencies between
distinct application libraries.</p>
</subsection>
<subsection name="Why Change the Plugin Architecture?">
</subsection>
</section>
<section name="Definitions">
<subsection name="What is a Plugin?">
</subsection>
<subsection name="What is a Mojo (And Why the H--- is it Named 'Mojo')?">
</subsection>
<subsection name="What is the Build Lifecycle?">
</subsection>
</section>
<section name="Building a Plugin (in Java)">
<subsection name="Write the Mojo">
</subsection>
<subsection name="Annotate the Mojo">
</subsection>
<subsection name="Build the Plugin (Including POM Modifications)">
</subsection>
</section>
<section name="Plugins in the Build">
<subsection name="I Want My preReqs!">
</subsection>
<subsection name="Decorating Existing Builds (What About pre/postGoals?)">
</subsection>
<subsection
name="Getting Information into the Plugin (Plugin Configuration)">
</subsection>
</section>
<section name="Plugins in the Wild">
<subsection name="Why Might a Plugin Be Maven-Centric?">
</subsection>
<subsection name="Curing Dependence on Maven">
</subsection>
<subsection name="Injecting Configuration into a Plugin">
</subsection>
</section>
<section name="Resources">
<ul>
<li>
<a href="../plugin-management.html">Managing plugin configuration in
large projects</a>
</li>
</ul>
</section>
</body>
</document>

View File

@ -2,10 +2,13 @@
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.metadata.ArtifactMetadata;
import org.apache.maven.artifact.metadata.ReleaseArtifactMetadata;
import org.apache.maven.artifact.metadata.SnapshotArtifactMetadata;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout;
import org.apache.maven.tools.repoclean.RepositoryCleanerConfiguration;
import org.apache.maven.tools.repoclean.artifact.metadata.ProjectMetadata;
import org.apache.maven.tools.repoclean.digest.DigestException;
import org.apache.maven.tools.repoclean.digest.DigestVerifier;
import org.apache.maven.tools.repoclean.report.FileReporter;
import org.apache.maven.tools.repoclean.report.ReportWriteException;
@ -15,6 +18,7 @@
import org.apache.maven.tools.repoclean.transaction.RollbackException;
import org.codehaus.plexus.PlexusConstants;
import org.codehaus.plexus.PlexusContainer;
import org.codehaus.plexus.component.repository.exception.ComponentLifecycleException;
import org.codehaus.plexus.context.Context;
import org.codehaus.plexus.context.ContextException;
import org.codehaus.plexus.logging.AbstractLogEnabled;
@ -70,238 +74,313 @@ public List execute( List artifacts, ArtifactRepository sourceRepo, ArtifactRepo
{
Logger logger = getLogger();
ArtifactPomRewriter artifactPomRewriter = null;
List rewritten = new ArrayList();
try
File sourceBase = new File( new URL( sourceRepo.getUrl() ).getPath() );
File targetBase = new File( new URL( targetRepo.getUrl() ).getPath() );
for ( Iterator it = artifacts.iterator(); it.hasNext(); )
{
File sourceBase = new File( new URL( sourceRepo.getUrl() ).getPath() );
Artifact artifact = (Artifact) it.next();
File targetBase = new File( new URL( targetRepo.getUrl() ).getPath() );
RewriteTransaction transaction = new RewriteTransaction( artifact );
for ( Iterator it = artifacts.iterator(); it.hasNext(); )
String artifactReportPath = buildArtifactReportPath( artifact );
FileReporter artifactReporter = null;
try
{
Artifact artifact = (Artifact) it.next();
artifactReporter = new FileReporter( reportsBase, artifactReportPath );
RewriteTransaction transaction = new RewriteTransaction( artifact );
boolean errorOccurred = false;
String artifactReportPath = buildArtifactReportPath( artifact );
File artifactSource = new File( sourceRepo.getBasedir(), sourceRepo.pathOf( artifact ) );
File artifactTarget = new File( targetRepo.getBasedir(), targetRepo.pathOf( artifact ).replace( '+',
'-' ) );
FileReporter artifactReporter = null;
try
transaction.addFile( artifactTarget );
artifact.setFile( artifactSource );
boolean targetMissingOrOlder = !artifactTarget.exists()
|| artifactTarget.lastModified() < artifactSource.lastModified();
if ( artifactSource.exists() && ( configuration.force() || targetMissingOrOlder ) )
{
artifactReporter = new FileReporter( reportsBase, artifactReportPath );
boolean errorOccurred = false;
File artifactSource = new File( sourceRepo.getBasedir(), sourceRepo.pathOf( artifact ) );
File artifactTarget = new File( targetRepo.getBasedir(), targetRepo.pathOf( artifact )
.replace( '+', '-' ) );
transaction.addFile( artifactTarget );
artifact.setFile( artifactSource );
boolean targetMissingOrOlder = !artifactTarget.exists()
|| artifactTarget.lastModified() < artifactSource.lastModified();
if ( artifactSource.exists() && ( configuration.force() || targetMissingOrOlder ) )
try
{
transaction.addFile( artifactTarget );
try
if ( !configuration.reportOnly() )
{
if ( !configuration.reportOnly() )
if ( logger.isDebugEnabled() )
{
if ( logger.isDebugEnabled() )
{
logger.debug( "sourceRepo basedir is: \'" + sourceRepo.getBasedir() + "\'" );
logger.debug( "targetRepo basedir is: \'" + targetRepo.getBasedir() + "\'" );
}
File targetParent = artifactTarget.getParentFile();
if ( !targetParent.exists() )
{
targetParent.mkdirs();
}
if ( logger.isDebugEnabled() )
{
logger.debug( "Copying artifact[" + artifact.getId() + "] from \'" + artifactSource
+ "\' to \'" + artifactTarget + "\'." );
}
copyArtifact( artifact, artifactTarget, artifactReporter );
}
}
catch ( Exception e )
{
repoReporter.error( "Error transferring artifact[" + artifact.getId()
+ "] to the target repository.", e );
throw e;
}
if ( logger.isDebugEnabled() )
{
logger.debug( "working on digest for artifact[" + artifact.getId() + "] with groupId: \'"
+ artifact.getGroupId() + "\'" );
}
try
{
digestVerifier.verifyDigest( artifactSource, artifactTarget, transaction, artifactReporter,
configuration.reportOnly() );
}
catch ( Exception e )
{
repoReporter.error( "Error verifying digest for artifact[" + artifact.getId() + "]", e );
throw e;
}
ArtifactMetadata pom = new ProjectMetadata( artifact );
File sourcePom = new File( sourceBase, sourceRepo.pathOfMetadata( pom ) );
String pomContents = null;
String pomVersion = ArtifactPomRewriter.V3_POM;
if ( sourcePom.exists() )
{
pomContents = readPomContents( sourcePom );
if ( pomContents.indexOf( "modelVersion" ) > -1 )
{
pomVersion = ArtifactPomRewriter.V4_POM;
}
}
artifactPomRewriter = (ArtifactPomRewriter) container.lookup( ArtifactPomRewriter.ROLE,
pomVersion );
File targetPom = new File( targetBase, targetRepo.pathOfMetadata( pom ).replace( '+', '-' ) );
transaction.addFile( targetPom );
File bridgedTargetPom = new File( targetBase, bridgingLayout.pathOfMetadata( pom )
.replace( '+', '-' ) );
transaction.addFile( bridgedTargetPom );
try
{
File targetPomParent = targetPom.getParentFile();
if ( !targetPomParent.exists() )
{
targetPomParent.mkdirs();
logger.debug( "sourceRepo basedir is: \'" + sourceRepo.getBasedir() + "\'" );
logger.debug( "targetRepo basedir is: \'" + targetRepo.getBasedir() + "\'" );
}
FileWriter to = null;
try
File targetParent = artifactTarget.getParentFile();
if ( !targetParent.exists() )
{
StringReader from = null;
if ( pomContents != null )
{
from = new StringReader( pomContents );
}
to = new FileWriter( targetPom );
artifactPomRewriter.rewrite( artifact, from, to, artifactReporter, configuration
.reportOnly() );
}
finally
{
IOUtil.close( to );
targetParent.mkdirs();
}
boolean wroteBridge = bridgePomLocations( targetPom, bridgedTargetPom, artifactReporter );
digestVerifier.verifyDigest( sourcePom, targetPom, transaction, artifactReporter,
configuration.reportOnly() );
if ( wroteBridge )
if ( logger.isDebugEnabled() )
{
digestVerifier.verifyDigest( sourcePom, bridgedTargetPom, transaction,
artifactReporter, configuration.reportOnly() );
logger.debug( "Copying artifact[" + artifact.getId() + "] from \'" + artifactSource
+ "\' to \'" + artifactTarget + "\'." );
}
copyArtifact( artifact, artifactTarget, artifactReporter );
}
catch ( Exception e )
{
repoReporter.error( "Error rewriting POM for artifact[" + artifact.getId()
+ "] into the target repository.\n Error message: " + e.getMessage() );
throw e;
}
rewritten.add( artifact );
}
else if ( !targetMissingOrOlder )
catch ( Exception e )
{
artifactReporter.warn( "Target file for artifact is present and not stale. (Artifact: \'"
+ artifact.getId() + "\' in path: \'" + artifactSource + "\' with target path: "
+ artifactTarget + ")." );
}
else
{
artifactReporter.error( "Cannot find source file for artifact: \'" + artifact.getId()
+ "\' under path: \'" + artifactSource + "\'" );
repoReporter.error( "Error transferring artifact[" + artifact.getId()
+ "] to the target repository.", e );
throw e;
}
if ( artifactReporter.hasError() )
if ( logger.isDebugEnabled() )
{
repoReporter.warn( "Error(s) occurred while rewriting artifact: \'" + artifact.getId()
+ "\' or its POM." );
logger.debug( "working on digest for artifact[" + artifact.getId() + "] with groupId: \'"
+ artifact.getGroupId() + "\'" );
}
try
{
digestVerifier.verifyDigest( artifactSource, artifactTarget, transaction, artifactReporter,
configuration.reportOnly() );
}
catch ( Exception e )
{
repoReporter.error( "Error verifying digest for artifact[" + artifact.getId() + "]", e );
throw e;
}
try
{
rewriteMetadata( artifact, transaction, sourceBase, sourceRepo, targetBase, targetRepo,
artifactReporter, configuration.reportOnly() );
}
catch ( Exception e )
{
repoReporter.error( "Error rewriting POM for artifact[" + artifact.getId()
+ "] into the target repository.\n Error message: " + e.getMessage() );
throw e;
}
rewritten.add( artifact );
}
else if ( !targetMissingOrOlder )
{
artifactReporter.warn( "Target file for artifact is present and not stale. (Artifact: \'"
+ artifact.getId() + "\' in path: \'" + artifactSource + "\' with target path: "
+ artifactTarget + ")." );
}
else
{
artifactReporter.error( "Cannot find source file for artifact: \'" + artifact.getId()
+ "\' under path: \'" + artifactSource + "\'" );
}
if ( artifactReporter.hasError() )
{
repoReporter.warn( "Error(s) occurred while rewriting artifact: \'" + artifact.getId()
+ "\' or its POM." );
}
}
catch ( Exception e )
{
if ( !configuration.force() )
{
repoReporter.warn( "Rolling back conversion for: " + artifact );
try
{
transaction.rollback();
}
catch ( RollbackException re )
{
repoReporter.error( "Error rolling back conversion transaction.", re );
}
}
else
{
repoReporter.warn( "NOT Rolling back conversion for: " + artifact + "; we are in --force mode." );
}
artifactReporter.error( "Error while rewriting file or POM for artifact: \'" + artifact.getId()
+ "\'. See report at: \'" + artifactReportPath + "\'.", e );
}
finally
{
if ( artifactReporter != null )
{
artifactReporter.close();
}
}
}
logger.info( "Actual number of artifacts rewritten: " + rewritten.size() + " (" + ( rewritten.size() * 2 )
+ " including POMs)." );
return rewritten;
}
private void rewriteMetadata( Artifact artifact, RewriteTransaction transaction, File sourceBase,
ArtifactRepository sourceRepo, File targetBase, ArtifactRepository targetRepo,
Reporter artifactReporter, boolean reportOnly )
throws Exception
{
// SNAPSHOT metadata
ArtifactMetadata snapshot = new SnapshotArtifactMetadata( artifact );
File snapshotSource = new File( sourceBase, sourceRepo.pathOfMetadata( snapshot ) );
File snapshotTarget = new File( targetBase, targetRepo.pathOfMetadata( snapshot ) );
freshenSupplementalMetadata( snapshot, snapshotSource, snapshotTarget, transaction, artifactReporter,
reportOnly );
// RELEASE metadata
ArtifactMetadata release = new ReleaseArtifactMetadata( artifact );
File releaseSource = new File( sourceBase, sourceRepo.pathOfMetadata( release ) );
File releaseTarget = new File( targetBase, targetRepo.pathOfMetadata( release ) );
freshenSupplementalMetadata( release, releaseSource, releaseTarget, transaction, artifactReporter, reportOnly );
// The rest is for POM metadata - translation and bridging of locations in the target repo may be required.
ArtifactMetadata pom = new ProjectMetadata( artifact );
File sourcePom = new File( sourceBase, sourceRepo.pathOfMetadata( pom ) );
File targetPom = new File( targetBase, targetRepo.pathOfMetadata( pom ).replace( '+', '-' ) );
String pomContents = null;
boolean pomNeedsRewriting = true;
if ( sourcePom.exists() )
{
pomContents = readPomContents( sourcePom );
if ( pomContents.indexOf( "modelVersion" ) > -1 )
{
pomNeedsRewriting = false;
freshenSupplementalMetadata( pom, sourcePom, targetPom, transaction, artifactReporter, reportOnly );
}
}
if ( pomNeedsRewriting )
{
ArtifactPomRewriter artifactPomRewriter = null;
try
{
artifactPomRewriter = (ArtifactPomRewriter) container.lookup( ArtifactPomRewriter.ROLE,
ArtifactPomRewriter.V3_POM );
transaction.addFile( targetPom );
File bridgedTargetPom = new File( targetBase, bridgingLayout.pathOfMetadata( pom ).replace( '+', '-' ) );
transaction.addFile( bridgedTargetPom );
try
{
File targetPomParent = targetPom.getParentFile();
if ( !targetPomParent.exists() )
{
targetPomParent.mkdirs();
}
FileWriter to = null;
try
{
StringReader from = null;
if ( pomContents != null )
{
from = new StringReader( pomContents );
}
to = new FileWriter( targetPom );
artifactPomRewriter.rewrite( artifact, from, to, artifactReporter, reportOnly );
}
finally
{
IOUtil.close( to );
}
boolean wroteBridge = bridgePomLocations( pom, targetPom, bridgedTargetPom, artifactReporter,
transaction, reportOnly );
digestVerifier.verifyDigest( sourcePom, targetPom, transaction, artifactReporter, reportOnly );
if ( wroteBridge )
{
digestVerifier.verifyDigest( sourcePom, bridgedTargetPom, transaction, artifactReporter,
reportOnly );
}
}
catch ( Exception e )
{
if ( !configuration.force() )
{
repoReporter.warn( "Rolling back conversion for: " + artifact );
try
{
transaction.rollback();
}
catch ( RollbackException re )
{
repoReporter.error( "Error rolling back conversion transaction.", re );
}
}
else
{
repoReporter
.warn( "NOT Rolling back conversion for: " + artifact + "; we are in --force mode." );
}
artifactReporter.error( "Error while rewriting file or POM for artifact: \'" + artifact.getId()
+ "\'. See report at: \'" + artifactReportPath + "\'.", e );
}
finally
{
if ( artifactReporter != null )
{
artifactReporter.close();
}
throw e;
}
}
logger.info( "Actual number of artifacts rewritten: " + rewritten.size() + " (" + ( rewritten.size() * 2 )
+ " including POMs)." );
}
finally
{
if ( artifactPomRewriter != null )
finally
{
container.release( artifactPomRewriter );
if ( artifactPomRewriter != null )
{
try
{
container.release( artifactPomRewriter );
}
catch ( ComponentLifecycleException e )
{
}
}
}
}
}
return rewritten;
private void freshenSupplementalMetadata( ArtifactMetadata metadata, File source, File target,
RewriteTransaction transaction, Reporter artifactReporter,
boolean reportOnly )
throws IOException, DigestException, ReportWriteException
{
if ( source.exists() )
{
File targetParent = target.getParentFile();
if ( !targetParent.exists() )
{
targetParent.mkdirs();
}
FileReader reader = null;
FileWriter writer = null;
try
{
reader = new FileReader( source );
writer = new FileWriter( target );
IOUtil.copy( reader, writer );
}
finally
{
IOUtil.close( reader );
IOUtil.close( writer );
}
digestVerifier.verifyDigest( source, target, transaction, artifactReporter, reportOnly );
}
}
private String readPomContents( File sourcePom )
@ -377,8 +456,9 @@ private void copyArtifact( Artifact artifact, File artifactTarget, FileReporter
}
}
private boolean bridgePomLocations( File targetPom, File bridgedTargetPom, Reporter reporter )
throws IOException, ReportWriteException
private boolean bridgePomLocations( ArtifactMetadata pom, File targetPom, File bridgedTargetPom, Reporter reporter,
RewriteTransaction transaction, boolean reportOnly )
throws IOException, ReportWriteException, DigestException
{
if ( targetPom.equals( bridgedTargetPom ) )
{
@ -388,21 +468,7 @@ private boolean bridgePomLocations( File targetPom, File bridgedTargetPom, Repor
return false;
}
FileInputStream in = null;
FileOutputStream out = null;
try
{
in = new FileInputStream( targetPom );
out = new FileOutputStream( bridgedTargetPom );
IOUtil.copy( in, out );
}
finally
{
IOUtil.close( in );
IOUtil.close( out );
}
freshenSupplementalMetadata( pom, targetPom, bridgedTargetPom, transaction, reporter, reportOnly );
return true;
}