Thanks for this Benjamin.

Vincent

2008/8/29  <[EMAIL PROTECTED]>:
> Author: bentmann
> Date: Fri Aug 29 14:06:00 2008
> New Revision: 690389
>
> URL: http://svn.apache.org/viewvc?rev=690389&view=rev
> Log:
> [MNGSITE-48] Consolidate coding pitfalls into a standalone document
>
> Added:
>    maven/site/trunk/src/site/apt/plugin-developers/common-bugs.apt   (with 
> props)
> Modified:
>    
> maven/site/trunk/src/site/apt/guides/plugin/guide-java-plugin-development.apt
>    maven/site/trunk/src/site/apt/plugin-developers/index.apt
>
> Modified: 
> maven/site/trunk/src/site/apt/guides/plugin/guide-java-plugin-development.apt
> URL: 
> http://svn.apache.org/viewvc/maven/site/trunk/src/site/apt/guides/plugin/guide-java-plugin-development.apt?rev=690389&r1=690388&r2=690389&view=diff
> ==============================================================================
> --- 
> maven/site/trunk/src/site/apt/guides/plugin/guide-java-plugin-development.apt 
> (original)
> +++ 
> maven/site/trunk/src/site/apt/guides/plugin/guide-java-plugin-development.apt 
> Fri Aug 29 14:06:00 2008
> @@ -100,8 +100,9 @@
>   * The <<<getLog>>> method (defined in <<<AbstractMojo>>>) returns a
>     log4j-like logger object which allows plugins to create messages at levels
>     of "debug", "info", "warn", and "error".  This logger is the accepted 
> means
> -    to display information to the user. Please have a look at the section 
> about
> -    pitfalls for a hint on its proper usage.
> +    to display information to the user. Please have a look at the section
> +    
> {{{../../plugin-developers/common-bugs.html#Retrieving_the_Mojo_Logger}Retrieving
>  the Mojo Logger}} for a hint on
> +    its proper usage.
>
>   []
>
> @@ -642,135 +643,6 @@
>  <myObject>test</myObject>
>  +-----+
>
> -* Some Pitfalls
> -
> - The following are some subtle anti-patterns that plugin developers should 
> avoid or take care.
> -
> -** Retrieving the Mojo Logger
> -
> - Defining an instance field in your mojo to save the logger like the 
> following is bad:
> -
> -+-----+
> -public MyMojo extends AbstractMojo
> -{
> -    private Log log = getLog();
> -
> -    public void execute() throws MojoExecutionException
> -    {
> -        log.debug( "..." );
> -    }
> -}
> -+-----+
> -
> - <<Explanation>>: Getting the logger this way (i.e. during the construction 
> of the mojo) will retrieve some default logger. In contrast, calling
> - <<<getLog()>>> later in the <<<execute()>>> method will retrieve a logger 
> that has been injected by the plexus container.
> - This is easily noticed by the different output styles of the log messages 
> and the fact that one gets <<<[debug]>>> messages without having "-X" enabled
> - when using the wrong logger. Just call <<<getLog()>>> whenever you need it, 
> it is fast enough and needs not be saved in an instance field.
> -
> -** Using Relative File Path Parameters
> -
> - Defining a file path parameter of type <<<java.lang.String>>> is dangerous:
> -
> -+-----+
> -public MyMojo extends AbstractMojo
> -{
> -    /**
> -     * This parameter will take a file path (file paths are 
> platform-dependent...)
> -     *
> -     * @parameter
> -     */
> -    private String outputDirectory;
> -
> -    public void execute() throws MojoExecutionException
> -    {
> -        File outputDir = new File( outputDirectory ).getAbsoluteFile();
> -        outputDir.mkdirs();
> -    }
> -}
> -+-----+
> -
> -  <<Explanation>>: Users of your mojo will usually provide relative paths 
> for its parameters (i.e. outputDirectory = "target/something") and
> -  expect the mojo to resolve these paths against the base directory of the 
> project (i.e. outputDirectory = "$\{basedir\}/target/something").
> -  However, whenever you use <<<java.io.File>>> with a relative path to 
> access the file system, the relative path will be resolved against
> -  the current working directory. But the current working directory might be 
> anything and is <not> necessarily the project's base directory.
> -  This is especially true during a reactor build when the current working 
> directory is usually the base directory of the parent project
> -  and not the base directory of the currently executed sub module. For this 
> reason, mojo parameters taking paths should be of type
> -  <<<java.io.File>>>. The important difference compared to 
> <<<java.lang.String>>> is that Maven will automatically push properly 
> resolved paths
> -  into the mojo fields. If you really need to use <<<java.lang.String>>> for 
> the parameter type (e.g. to allow the user to alternatively specify
> -  a class path resource or URL), be sure to always resolve relative file 
> paths manually against the base directory of the project.
> -
> -** Creating Resource Bundles
> -
> - Omitting an explicit resource bundle for the default locale provided by the 
> base bundle is not allowed. For example, the following family
> - of resource bundles does not provide reliable internationalization for your 
> mojo:
> -
> -+-----+
> -mymojo-report.properties
> -mymojo-report_de.properties
> -+-----+
> -
> -  <<Explanation>>: As described in the method javadoc about
> -  
> <<<{{{http://java.sun.com/javase/6/docs/api/java/util/ResourceBundle.html#getBundle(java.lang.String,%20java.util.Locale,%20java.lang.ClassLoader)}ResourceBundle.getBundle(String,
>  Locale, ClassLoader)}}>>>,
> -  the lookup strategy to find a bundle for a specific locale will prefer the 
> bundle for the JVM's default locale over the base bundle.
> -  However, the JVM's default locale needs not to match the locale of the 
> base bundle. In the example above, a request for the locale "en" will
> -  retrieve the bundle <<<mymojo-report_de.properties>>> instead of 
> <<<mymojo-report.properties>>> if the JVM's default locale happens to be "de".
> -  Therefore, always provide a dedicated bundle for the default locale of 
> your bundle family (e.g. <<<mymojo-report_en.properties>>>). This
> -  bundle should be empty such that it retrieves strings via the parent chain 
> from the base bundle.
> -
> -** Getting the Output Directory for a Report Mojo
> -
> - A report mojo that subclasses <<<AbstractReportMojo>>> needs to implement 
> <<<AbstractReportMojo.getOutputDirectory()>>>. This method
> - should <never> be called during the generation of the report, i.e. the code 
> shown below is buggy:
> -
> -+-----+
> -public MyReportMojo extends AbstractReportMojo
> -{
> -    /**
> -     * The base directory of the report output. Only relevant if the mojo is 
> not run as part of a site generation.
> -     *
> -     * @parameter expression="${project.reporting.outputDirectory}"
> -     */
> -    private File outputDirectory;
> -
> -    /**
> -     * @see 
> org.apache.maven.reporting.AbstractMavenReport#getOutputDirectory()
> -     */
> -    protected String getOutputDirectory()
> -    {
> -        return outputDirectory.toString();
> -    }
> -
> -    /**
> -     * @see org.apache.maven.reporting.AbstractMavenReport#executeReport( 
> java.util.Locale )
> -     */
> -    public void executeReport( Locale locale )
> -    {
> -        // will fail during a site generation
> -        File outputFile = new File( getOutputDirectory(), "summary.txt" );
> -    }
> -}
> -+-----+
> -
> -  <<Explanation>>: There are in principal two situations in which a report 
> mojo could be invoked. The mojo might be run
> -  directly from the command line or it might be run indirectly as part of 
> the site generation along with other report mojos.
> -  The glaring difference between these two invocations is the way the output 
> directory is controlled. In the first case,
> -  the parameter <<<outputDirectory>>> from the mojo itself is used. In the 
> second case however, the Maven Site Plugin
> -  will set the output directory according to its own configuration by 
> calling <<<MavenReport.setReportOutputDirectory()>>>
> -  on the reports being generated. Therefore, developers should always use 
> <<<MavenReport.getReportOutputDirectory()>>> if they
> -  need to query the effective output directory for the report.
> -
> -** Using Plexus Utilities
> -
> -  If you depend on a version of plexus-utils later than 1.1, be sure to 
> properly specify your prerequisites on Maven:
> -
> -+-----+
> -  <prerequisites>
> -    <maven>2.0.6</maven>
> -  </prerequisites>
> -+-----+
> -
> -  <<Explanation>>: Before Maven 2.0.6, Maven always used plexus-utils 1.1 
> for the plugin class realm regardless of the
> -  version of plexus-utils that the plugin specified in its POM.
>
>  * Resources
>
> @@ -784,6 +656,6 @@
>
>     [[5]] {{{http://commons.apache.org/io/}Commons IO}}: Set of utilities 
> classes useful for file/path handling.
>
> -    [[6]] {{{http://www.nabble.com/Common-Bugs-td14783703s177.html}Common 
> Bugs Thread}}.
> +    [[6]] {{{../../plugin-developers/common-bugs.html}Common Bugs and 
> Pitfalls}}: Overview of problematic coding patterns.
>
>     []
>
> Added: maven/site/trunk/src/site/apt/plugin-developers/common-bugs.apt
> URL: 
> http://svn.apache.org/viewvc/maven/site/trunk/src/site/apt/plugin-developers/common-bugs.apt?rev=690389&view=auto
> ==============================================================================
> --- maven/site/trunk/src/site/apt/plugin-developers/common-bugs.apt (added)
> +++ maven/site/trunk/src/site/apt/plugin-developers/common-bugs.apt Fri Aug 
> 29 14:06:00 2008
> @@ -0,0 +1,458 @@
> + ------
> + Common Bugs and Pitfalls
> + ------
> + Benjamin Bentmann
> + ------
> + 2008-08-29
> + ------
> +
> +~~ Licensed to the Apache Software Foundation (ASF) under one
> +~~ or more contributor license agreements.  See the NOTICE file
> +~~ distributed with this work for additional information
> +~~ regarding copyright ownership.  The ASF licenses this file
> +~~ to you 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.
> +
> +~~ NOTE: For help with the syntax of this file, see:
> +~~ http://maven.apache.org/doxia/references/apt-format.html
> +
> +Common Bugs and Pitfalls
> +
> +  Maven is not the smallest project in terms of source code and has as such 
> already suffered from many bugs. Having a
> +  closer look at all the issues revealed some coding problems that had 
> widespread among the various subcomponents. This
> +  document lists these commonly occurring anti patterns in order to help the 
> Maven community to prevent rather than fix
> +  bugs. Note that the primary focus is on pointing out problems that are 
> subtle in their nature rather than giving a
> +  comprehensive guide for Java or Maven development.
> +
> +  * {{{Reading_and_Writing_Text_Files}Reading and Writing Text Files}}
> +
> +  * {{{Converting_between_URLs_and_Filesystem_Paths}Converting between URLs 
> and Filesystem Paths}}
> +
> +  * {{{Handling_Strings_Case-insensitively}Handling Strings 
> Case-insensitively}}
> +
> +  * {{{Creating_Resource_Bundle_Families}Creating Resource Bundle Families}}
> +
> +  * {{{Using_System_Properties}Using System Properties}}
> +
> +  * {{{Resolving_Relative_Paths}Resolving Relative Paths}}
> +
> +  * {{{Determining_the_Output_Directory_for_a_Site_Report}Determining the 
> Output Directory for a Site Report}}
> +
> +  * {{{Retrieving_the_Mojo_Logger}Retrieving the Mojo Logger}}
> +
> +  * {{{Depending_on_Plexus_Utilities_1.1}Depending on Plexus Utilities 1.1+}}
> +
> +* {Reading and Writing Text Files}
> +
> +  Textual content is composed of characters while file systems merely store 
> byte streams. A file encoding (aka charset)
> +  is used to convert between bytes and characters. The challenge is using 
> the right file encoding.
> +
> +  The JVM has this notion of a default encoding (given by the 
> <<<file.encoding>>> property) which it derives from a
> +  system's locale. While this might be a convenient feature sometimes, using 
> this default encoding for a project build
> +  is in general a bad idea: The build output will depend on the 
> machine/developer who runs the build. As such, usage
> +  of the default encoding threatens the dream of a reproducible build.
> +
> +  For example, if developer A has UTF-8 as default encoding while developer 
> B uses ISO-8859-1, text files are very
> +  likely to get messed up during resource filtering or similar tasks.
> +
> +  Therefore, plugin developers should avoid any direct or indirect usage of 
> the classes/methods that simply employ the
> +  platform's default encoding. For instance, <<<FileWriter>>> and 
> <<<FileReader>>> should usually be avoided:
> +
> ++---
> +/*
> + * FIXME: This assumes the source file is using the platform's default 
> encoding.
> + */
> +Reader reader = new FileReader( javaFile );
> ++---
> +
> +  Instead, the classes <<<OutputStreamWriter>>> and <<<OutputStreamReader>>> 
> can be used in combination with an
> +  explicit encoding value. This encoding value can be retrieved from a mojo 
> parameter such that the user can configure
> +  the plugin to fit his/her needs.
> +
> +  To save the user from configuring each plugin individually, conventions 
> have been established that allow a user to
> +  centrally configure the file encoding per POM. Plugin developers should 
> respect these conventions whereever possible:
> +
> +  * 
> {{{http://docs.codehaus.org/display/MAVENUSER/POM+Element+for+Source+File+Encoding}Source
>  File Encoding}}
> +
> +  * 
> {{{http://docs.codehaus.org/display/MAVEN/Reporting+Encoding+Configuration}Report
>  Output Encoding}}
> +
> +  []
> +
> +  Finally note that XML files require special handling because they are 
> equipped with an encoding declaration in the
> +  XML prolog. Reading or writing XML files with an encoding that does not 
> match their XML prolog's <<<encoding>>>
> +  attribute is a bad idea:
> +
> ++---
> +/*
> + * FIXME: This assumes the XML encoding declaration matches the platform's 
> default encoding.
> + */
> +Writer writer = new FileWriter( xmlFile );
> +writer.write( xmlContent );
> ++---
> +
> +  To ease the correct processing of XML files, developers are encouraged to 
> use
> +  
> <<<{{{http://plexus.codehaus.org/plexus-utils/apidocs/org/codehaus/plexus/util/ReaderFactory.html#newXmlReader(java.io.File)}ReaderFactory.newXmlReader()}}>>>
> +  and
> +  
> <<<{{{http://plexus.codehaus.org/plexus-utils/apidocs/org/codehaus/plexus/util/WriterFactory.html#newXmlWriter(java.io.File)}WriterFactory.newXmlWriter()}}>>>
> +  from the Plexus Utilities.
> +
> +* {Converting between URLs and Filesystem Paths}
> +
> +  URLs and filesystem paths are really two different things and converting 
> between them is not trivial. The main source
> +  of problems is that different encoding rules apply for the strings that 
> make up a URL or filesystem path. For example,
> +  consider the following code snippet and its associated console output:
> +
> ++---
> +File file = new File( "foo bar+foo" );
> +URL url = file.toURI().toURL();
> +
> +System.out.println( file.toURL() );
> +> file:/C:/temp/foo bar+foo
> +
> +System.out.println( url );
> +> file:/C:/temp/foo%20bar+foo
> +
> +System.out.println( url.getPath() );
> +> /C:/temp/foo%20bar+foo
> +
> +System.out.println( URLDecoder.decode( url.getPath(), "UTF-8" ) );
> +> /C:/temp/foo bar foo
> ++---
> +
> +  First of all, please note that
> +  
> <<<{{{http://java.sun.com/javase/6/docs/api/java/io/File.html#toURL()}File.toURL()}}>>>
>  does not escape the space
> +  character (and others). This yields an invalid URL, as per
> +  {{{http://www.faqs.org/rfcs/rfc2396.html}RFC 2396, section 2.4.3 "Excluded 
> US-ASCII Characters"}}. The class
> +  <<<java.net.URL>>> will silently accept such invalid URLs, in contrast 
> <<<java.net.URI>>> will not (see also
> +  
> <<<{{{http://java.sun.com/javase/6/docs/api/java/net/URL.html#toURI()}URL.toURI()}}>>>).
>  For this reason,
> +  <<<File.toURL()>>> has been deprecated and should be replaced with 
> <<<File.toURI().toURL()>>>.
> +
> +  Next, 
> <<<{{{http://java.sun.com/javase/6/docs/api/java/net/URL.html#getPath()}URL.getPath()}}>>>
>  does in general not
> +  return a string that can be used as a filesystem path. It returns a 
> substring of the URL and as such can contain
> +  escape sequences. The prominent example is the space character which will 
> show up as "%20". People sometimes hack
> +  around this by means of <<<replace("%20", " ")>>> but that does simply not 
> cover all cases. It's worth to mention
> +  that on the other hand the related method
> +  
> <<<{{{http://java.sun.com/javase/6/docs/api/java/net/URI.html#getPath()}URI.getPath()}}>>>
>  does decode escapes but
> +  still the result is not a filesystem path (compare the source for the 
> constructor <<<File(URI)>>>). To summarize,
> +  the following idiom is to be avoided:
> +
> ++---
> +URL url = new URL( "file:/C:/Program%20Files/Java/bin/java.exe" );
> +
> +/*
> + * FIXME: This does not decode percent encoded characters.
> + */
> +File path = new File( url.getPath() );
> ++---
> +
> +  To decode a URL, people sometimes also choose
> +  
> <<<{{{http://java.sun.com/javase/6/docs/api/java/net/URLDecoder.html}java.net.URLDecoder}}>>>.
>  The pitfall with this
> +  class is that is actually performs HTML form decoding which is yet another 
> encoding and not the same as the URL
> +  encoding (compare the last paragraph in class javadoc about
> +  
> <<<{{{http://java.sun.com/javase/6/docs/api/java/net/URL.html}java.net.URL}}>>>).
>  For instance, a <<<URLDecoder>>>
> +  will erroneously convert the character "+" into a space as illustrated by 
> the last sysout in the example above.
> +
> +  In an ideal world, code targetting JRE 1.4+ could easily avoid these 
> problems by using the constructor
> +  
> <<<{{{http://java.sun.com/javase/6/docs/api/java/io/File.html#File(java.net.URI)}File(URI)}}>>>
>  as suggested by the
> +  following snippet:
> +
> ++---
> +URL url = new URL( "file:/C:/Documents and Settings/user/.m2/settings.xml" );
> +
> +/*
> + * FIXME: This assumes the URL is fully compliant with RFC 3986.
> + */
> +File path = new File( new URI( url.toExternalForm() ) );
> ++---
> +
> +  The remaining source of frustration is the conversion from <<<URL>>> to 
> <<<URI>>>. As already said, the <<<URL>>>
> +  class accepts malformed URLs which will make the constructor of <<<URI>>> 
> throw an exception. And indeed, class
> +  loaders from Sun JREs up to Java 1.4 will deliver malformed URLs when 
> queried for a resource. Likewise, the class
> +  loaders employed by Maven 2.x deliver malformed resource URLs regardless 
> of the JRE version (see
> +  {{{http://jira.codehaus.org/browse/MNG-3607}MNG-3607}}).
> +
> +  For all these reasons, it is recommended to use
> +  
> <<<{{{http://commons.apache.org/io/api-release/org/apache/commons/io/FileUtils.html#toFile(java.net.URL)}FileUtils.toFile()}}>>>
> +  from Commons IO or
> +  
> <<<{{{http://plexus.codehaus.org/plexus-utils/apidocs/org/codehaus/plexus/util/FileUtils.html#toFile(java.net.URL)}FileUtils.toFile()}}>>>
> +  from a recent Plexus Utilities.
> +
> +* {Handling Strings Case-insensitively}
> +
> +  When developers need to compare strings without regard to case or want to 
> realize a map with case-insensitive string
> +  keys, they often employ
> +  
> <<<{{{http://java.sun.com/javase/6/docs/api/java/lang/String.html#toLowerCase()}String.toLowerCase()}}>>>
>  or
> +  
> <<<{{{http://java.sun.com/javase/6/docs/api/java/lang/String.html#toUpperCase()}String.toUpperCase()}}>>>
>  to create
> +  a "normalized" string before doing a simple <<<String.equals()>>>. Now, 
> the <<<to*Case()>>> methods are overloaded:
> +  One takes no arguments and one takes a <<<Locale>>> object.
> +
> +  The gotcha with the arg-less methods is that their output depends on the 
> default locale of the JVM but the default
> +  locale is out of control of the developer. That means the string expected 
> by the developer (who runs/tests his code
> +  in a JVM using locale <<<xy>>>) does not necessarily match the string seen 
> by another user (that runs a JVM with
> +  locale <<<ab>>>). For example, the comparison shown in the next code 
> snippet is likely to fail for systems with
> +  default locale Turkish because Turkish has unusual casing rules for the 
> characters "i" and "I":
> +
> ++---
> +/*
> + * FIXME: This assumes the casing rules of the current platform
> + * match the rules for the English locale.
> + */
> +if ( "info".equals( debugLevel.toLowerCase() ) )
> +    logger.info( message );
> ++---
> +
> +  For case-insensitive string comparisions which should be 
> locale-insensitive, the method
> +  
> <<<{{{http://java.sun.com/javase/6/docs/api/java/lang/String.html#equalsIgnoreCase(java.lang.String)}String.equalsIgnoreCase()}}>>>
> +  should be used instead. If only a substring like a prefix/suffix should be 
> compared, the method
> +  
> <<<{{{http://java.sun.com/javase/6/docs/api/java/lang/String.html#regionMatches(boolean,%20int,%20java.lang.String,%20int,%20int)}String.regionMatches()}}>>>
> +  can be used instead.
> +
> +  If the usage of <<<String.to*Case()>>> cannot be avoided, the overloaded 
> version taking a <<<Locale>>> object should
> +  be used, passing in
> +  
> <<<{{{http://java.sun.com/javase/6/docs/api/java/util/Locale.html#ENGLISH}Locale.ENGLISH}}>>>.
>  The resulting code
> +  will still run on Non-English systems, the parameter only locks down the 
> casing rules used for the string comparison
> +  such that the code delivers the same results on all platforms.
> +
> +* {Creating Resource Bundle Families}
> +
> +  Especially reporting plugins employ resource bundles to support 
> internationalization. One language (usually English)
> +  is provided as the fallback/default language in the base resource bundle. 
> Due to the lookup strategy performed by
> +  
> <<<{{{http://java.sun.com/javase/6/docs/api/java/util/ResourceBundle.html#getBundle(java.lang.String,%20java.util.Locale,%20java.lang.ClassLoader)}ResourceBundle.getBundle()}}>>>,
> +  one must always provide a dedicated resource bundle for this default 
> language, too. This bundle should be empty
> +  because it inherits the strings via the parent chain from the base bundle, 
> but it must exist.
> +
> +  The following example illustrates this requirement. Imagine the broken 
> resource bundle family shown below which is
> +  intended to provide localization for English, German and French:
> +
> ++---
> +src/
> ++- main/
> +   +- resources/
> +      +- mymojo-report.properties
> +      +- mymojo-report_de.properties
> +      +- mymojo-report_fr.properties
> ++---
> +
> +  Now, if a resource bundle is to be looked up for English on a JVM whose 
> default locale happens to be French, the
> +  bundle <<<mymojo-report_fr.properties>>> will be loaded instead of the 
> intended bundle <<<mymojo-report.properties>>>.
> +
> +  Reporting plugins that suffer from this bug can easily be detected by 
> executing <<<mvn site -D locales=xy,en>>> where
> +  <<<xy>>> denotes any other language code supported by the particular 
> plugin. Specifying <<<xy>>> as the first locale
> +  will have the Maven Site Plugin change the JVM's default locale to 
> <<<xy>>> which in turn causes the lookup for
> +  <<<en>>> to fail as outlined above unless the plugin has a dedicated 
> resource bundle for English.
> +
> +* {Using System Properties}
> +
> +  Maven's command line supports the definition of system properties via 
> arguments of the form <<<-D key=value>>>.
> +  While these properties are called system properties, plugins should never 
> use
> +  
> <<<{{{http://java.sun.com/javase/6/docs/api/java/lang/System.html#getProperty(java.lang.String)}System.getProperty()}}>>>
> +  and related methods to query these properties. For example, the following 
> code snippet will not work reliably when
> +  Maven is embedded, say into an IDE or a CI server:
> +
> ++---
> +public MyMojo extends AbstractMojo
> +{
> +    public void execute()
> +    {
> +        /*
> +         * FIXME: This prevents proper embedding into IDEs or CI systems.
> +         */
> +        String value = System.getProperty( "maven.test.skip" );
> +    }
> +}
> ++---
> +
> +  The problem is that the properties managed by the <<<System>>> class are 
> global, i.e. shared among all threads in the
> +  current JVM. To prevent conflicts with other code running in the same JVM, 
> Maven plugins should instead query the
> +  execution properties. These can be obtained from
> +  
> <<<{{{http://maven.apache.org/ref/current/maven-core/apidocs/org/apache/maven/execution/MavenSession.html#getExecutionProperties()}MavenSession.getExecutionProperties()}}>>>.
> +
> +* {Resolving Relative Paths}
> +
> +  It is common practice for users of Maven to specify relative paths in the 
> POM, not to mention that the Super POM does
> +  so, too. The intention is to resolve such relative paths against the base 
> directory of the current project. In other
> +  words, the paths <<<target/classes>>> and 
> <<<$\{basedir\}/target/classes>>> should resolve to the same directory for a
> +  given POM.
> +
> +  Unfortunately, the class 
> <<<{{{http://java.sun.com/javase/6/docs/api/java/io/File.html}java.io.File}}>>>
>  does not
> +  resolve relative paths against the project's base directory. As mentioned 
> in its class javadoc, it resolves relative
> +  paths against the current working directory. In plain English: Unless a 
> Maven component has complete control over the
> +  current working directory, any usage of <<<java.io.File>>> in combination 
> with a relative path is a bug.
> +
> +  At first glance, one might be tempted to argue that the project base 
> directory is equal to the current working
> +  directory. However, this assumption is generally not true. Consider the 
> following scenarios:
> +
> +  [[a]] Reactor Builds
> +
> +        When a child module is build during a reactor build, the current 
> working is usually the base directory of the
> +        parent project, not the base directory of the current module. That 
> is the most common scenario where users are
> +        faced with the bug.
> +
> +  [[b]] Embedded Maven Invocations
> +
> +        Other tools, most notably IDEs, that run Maven under the hood may 
> have set the current working directory to
> +        their installation folder or whatever they like.
> +
> +  [[c]] Maven Invocations using the <<<-f>>> Switch
> +
> +        While it is surely an uncommon use-case, the user is free to invoke 
> Maven from an arbitrary working directory
> +        by specifying an absolute path like <<<mvn -f 
> /home/me/projects/demo/pom.xml>>>.
> +
> +  []
> +
> +  Hence this example code is prone to misbehave:
> +
> ++---
> +public MyMojo extends AbstractMojo
> +{
> +    /**
> +     * @parameter
> +     */
> +    private String outputDirectory;
> +
> +    public void execute()
> +    {
> +        /*
> +         * FIXME: This will resolve relative paths like "target/classes" 
> against
> +         * the user's working directory instead of the project's base 
> directory.
> +         */
> +        File outputDir = new File( outputDirectory ).getAbsoluteFile();
> +    }
> +}
> ++---
> +
> +  In order to guarantee reliable builds, Maven and its plugins must manually 
> resolve relative paths against the
> +  project's base directory. A simple idiom like the following will do just 
> fine:
> +
> ++---
> +File file = new File( path );
> +if ( !file.isAbsolute() )
> +{
> +    file = new File( project.getBasedir(), file );
> +}
> ++---
> +
> +  Many Maven plugins can get this resolution automatically if they declare 
> their affected mojo parameters of type
> +  <<<java.io.File>>> instead of <<<java.lang.String>>>. This subtle 
> difference in parameter types will trigger a
> +  feature known as <path translation>, i.e. the Maven core will 
> automatically resolve relative paths when it pumps the
> +  XML configuration into a mojo.
> +
> +* {Determining the Output Directory for a Site Report}
> +
> +  Most reporting plugins inherit from <<<AbstractMavenReport>>>. In doing 
> so, they need to implement the inherited but
> +  abstract method <<<getOutputDirectory()>>>. To implement this method, 
> plugins usually declare a field named
> +  <<<outputDirectory>>> which they return in the method. Nothing wrong so 
> far.
> +
> +  Now, some plugins need to create additional files in the report output 
> directory that accompany the report generated
> +  via the sink interface. While it is tempting to use either the method 
> <<<getOutputDirectory()>>> or the field
> +  <<<outputDirectory>>> directly in order to setup a path for the output 
> files, this leads most likely to a bug. More
> +  precisely, those plugins will not properly output files when run by the 
> Maven Site Plugin as part of the site
> +  lifecycle. This is best noticed when the output directory for the site is 
> configured directly in the Maven Site Plugin
> +  such that it deviates from the expression 
> <<<$\{project.reporting.outputDirectory\}>>> that the plugins use by default.
> +  Multi-language site generation is another scenario to exploit this bug 
> which is illustrated below:
> +
> ++---
> +public MyReportMojo extends AbstractMavenReport
> +{
> +    /**
> +     * @parameter default-value="${project.reporting.outputDirectory}"
> +     */
> +    private File outputDirectory;
> +
> +    protected String getOutputDirectory()
> +    {
> +        return outputDirectory.getAbsolutePath();
> +    }
> +
> +    public void executeReport( Locale locale )
> +    {
> +        /*
> +         * FIXME: This assumes the mojo parameter reflects the effective
> +         * output directory as used by the Maven Site Plugin.
> +         */
> +        outputDirectory.mkdirs();
> +    }
> +}
> ++---
> +
> +  There are in principal two situations in which a report mojo could be 
> invoked. The mojo might be run directly from
> +  the command line or the default build lifecycle or it might be run 
> indirectly as part of the site generation along
> +  with other report mojos. The glaring difference between these two 
> invocations is the way the output directory is
> +  controlled. In the first case, the parameter <<<outputDirectory>>> from 
> the mojo itself is used. In the second case
> +  however, the Maven Site Plugin takes over control and will set the output 
> directory according to its own configuration
> +  by calling <<<MavenReport.setReportOutputDirectory()>>> on the reports 
> being generated.
> +
> +  Therefore, developers should always use 
> <<<MavenReport.getReportOutputDirectory()>>> if they need to query the
> +  effective output directory for the report. The implementation of 
> <<<AbstractMavenReport.getOutputDirectory()>>>
> +  is only intended as a fallback in case the mojo is not run as part of the 
> site generation.
> +
> +* {Retrieving the Mojo Logger}
> +
> +  Maven employs an IoC container named Plexus to setup a plugin's mojos 
> before their execution. In other words,
> +  components required by a mojo will be provided by means of dependency 
> injection, more precisely field injection. The
> +  important point to keep in mind is that this field injection happens 
> <after> the mojo's constructor has finished.
> +  This means that references to injected components are invalid during the 
> construction time of the mojo.
> +
> +  For example, the next snippet tries to retrieve the mojo logger during 
> construction time but the mojo logger is an
> +  injected component and as such has not been properly initialized yet:
> +
> ++---
> +public MyMojo extends AbstractMojo
> +{
> +    /*
> +     * FIXME: This will retrieve a wrong logger instead of the intended mojo 
> logger.
> +     */
> +    private Log log = getLog();
> +
> +    public void execute()
> +    {
> +        log.debug( "..." );
> +    }
> +}
> ++---
> +
> +  In case of the logger, the above mojo will simply use a default console 
> logger, i.e. the code defect is not
> +  immediately noticeable by a <<<NullPointerException>>>. This default 
> logger will however use a different message
> +  format for its output and also outputs debug messages even if Maven's 
> debug mode was not enabled. For this reason,
> +  developers must not try to cache the logger during construction time. The 
> method <<<getLog()>>> is fast enough and
> +  can simply be called whenever one needs it.
> +
> +* {Depending on Plexus Utilities 1.1+}
> +
> +  Up to Maven 2.0.5, version 1.1 of the artifact <<<plexus-utils>>> was 
> included in the Maven core class loader which
> +  is shared with the plugin class realm. This effectively prevented plugins 
> from using another/newer version of
> +  <<<plexus-utils>>>. This has been solved starting with Maven 2.0.6 by 
> shading (most of) the classes from
> +  <<<plexus-utils>>> (see 
> {{{http://jira.codehaus.org/browse/MNG-2892}MNG-2892}}).
> +
> +  In short, plugins that strictly require a newer version of 
> <<<plexus-utils>>> also require Maven 2.0.6 as a minimum.
> +  Hence, a POM snippet for a Maven plugin like shown below is misleading:
> +
> ++---
> +<project>
> +  <packaging>maven-plugin</packaging>
> +  ...
> +  <prerequisites>
> +    <!-- FIXME: This assumes the plugin works with plexus-utils:1.1, too -->
> +    <maven>2.0</maven>
> +  </prerequisites>
> +  ...
> +  <dependencies>
> +    <dependency>
> +      <groupId>org.codehaus.plexus</groupId>
> +      <artifactId>plexus-utils</artifactId>
> +      <version>1.5.6</version>
> +    </dependency>
> +  </dependencies>
> +  ...
> +</project>
> ++---
>
> Propchange: maven/site/trunk/src/site/apt/plugin-developers/common-bugs.apt
> ------------------------------------------------------------------------------
>    svn:eol-style = native
>
> Propchange: maven/site/trunk/src/site/apt/plugin-developers/common-bugs.apt
> ------------------------------------------------------------------------------
>    svn:keywords = Author Date Id Revision
>
> Modified: maven/site/trunk/src/site/apt/plugin-developers/index.apt
> URL: 
> http://svn.apache.org/viewvc/maven/site/trunk/src/site/apt/plugin-developers/index.apt?rev=690389&r1=690388&r2=690389&view=diff
> ==============================================================================
> --- maven/site/trunk/src/site/apt/plugin-developers/index.apt (original)
> +++ maven/site/trunk/src/site/apt/plugin-developers/index.apt Fri Aug 29 
> 14:06:00 2008
> @@ -26,6 +26,8 @@
>
>   * {{{./cookbook/index.html} Plugins Cookbook}} - Examples for how to 
> perform common tasks in plugins
>
> +  * {{{./common-bugs.html} Common Bugs and Pitfalls}} - Overview of 
> problematic coding patterns
> +
>   []
>
>  ~~TODO: trails
>
>
>

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to