FELIX-4817 : Baseline plugin is not thread safe

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1663247 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/bundleplugin/pom.xml b/bundleplugin/pom.xml
index 168849b..f431070 100644
--- a/bundleplugin/pom.xml
+++ b/bundleplugin/pom.xml
@@ -109,6 +109,11 @@
    <version>3.0.10</version>
   </dependency>
   <dependency>
+   <groupId>org.sonatype.plexus</groupId>
+   <artifactId>plexus-build-api</artifactId>
+   <version>0.0.7</version>
+  </dependency>
+  <dependency>
    <groupId>org.apache.maven.doxia</groupId>
    <artifactId>doxia-sink-api</artifactId>
    <version>1.0</version>
diff --git a/bundleplugin/src/main/java/org/apache/felix/bundleplugin/baseline/AbstractBaselinePlugin.java b/bundleplugin/src/main/java/org/apache/felix/bundleplugin/baseline/AbstractBaselinePlugin.java
index 287f582..441a01b 100644
--- a/bundleplugin/src/main/java/org/apache/felix/bundleplugin/baseline/AbstractBaselinePlugin.java
+++ b/bundleplugin/src/main/java/org/apache/felix/bundleplugin/baseline/AbstractBaselinePlugin.java
@@ -162,6 +162,12 @@
     public final void execute()
         throws MojoExecutionException, MojoFailureException
     {
+        this.execute(null);
+    }
+
+    protected void execute( Object context)
+            throws MojoExecutionException, MojoFailureException
+    {
         if ( skip )
         {
             getLog().info( "Skipping Baseline execution" );
@@ -204,8 +210,7 @@
         }
 
         // go!
-
-        init();
+        context = this.init(context);
 
         String generationDate = new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm'Z'" ).format( new Date() );
         Reporter reporter = new Processor();
@@ -215,7 +220,7 @@
             Set<Info> infoSet = new Baseline( reporter, new DiffPluginImpl() )
                                 .baseline( currentBundle, previousBundle, packageFilters );
 
-            startBaseline( generationDate, project.getArtifactId(), project.getVersion(), previousArtifact.getVersion() );
+            startBaseline( context, generationDate, project.getArtifactId(), project.getVersion(), previousArtifact.getVersion() );
 
             final Info[] infos = infoSet.toArray( new Info[infoSet.size()] );
             Arrays.sort( infos, new InfoComparator() );
@@ -283,7 +288,7 @@
                 String suggestedVersionString = String.valueOf( ( info.suggestedVersion == null ) ? "-" : info.suggestedVersion );
                 Map<String,String> attributes = info.attributes;
 
-                startPackage( mismatch,
+                startPackage( context, mismatch,
                               packageName,
                               shortDelta,
                               deltaString,
@@ -295,13 +300,13 @@
 
                 if ( Delta.REMOVED != delta )
                 {
-                    doPackageDiff( packageDiff );
+                    doPackageDiff( context, packageDiff );
                 }
 
-                endPackage();
+                endPackage(context);
             }
 
-            endBaseline();
+            endBaseline(context);
         }
         catch ( Exception e )
         {
@@ -310,6 +315,7 @@
         finally
         {
             closeJars( currentBundle, previousBundle );
+            this.close(context);
         }
 
         // check if it has to fail if some error has been detected
@@ -354,7 +360,7 @@
         }
     }
 
-    private void doPackageDiff( Diff diff )
+    private void doPackageDiff( Object context, Diff diff )
     {
         int depth = 1;
 
@@ -362,38 +368,41 @@
         {
             if ( Delta.UNCHANGED != curDiff.getDelta() )
             {
-                doDiff( curDiff, depth );
+                doDiff( context, curDiff, depth );
             }
         }
     }
 
-    private void doDiff( Diff diff, int depth )
+    private void doDiff( Object context, Diff diff, int depth )
     {
         String type = StringUtils.lowerCase( String.valueOf( diff.getType() ) );
         String shortDelta = getShortDelta( diff.getDelta() );
         String delta = StringUtils.lowerCase( String.valueOf( diff.getDelta() ) );
         String name = diff.getName();
 
-        startDiff( depth, type, name, delta, shortDelta );
+        startDiff( context, depth, type, name, delta, shortDelta );
 
         for ( Diff curDiff : diff.getChildren() )
         {
             if ( Delta.UNCHANGED != curDiff.getDelta() )
             {
-                doDiff( curDiff, depth + 1 );
+                doDiff( context, curDiff, depth + 1 );
             }
         }
 
-        endDiff( depth );
+        endDiff( context, depth );
     }
 
     // extensions APIs
 
-    protected abstract void init();
+    protected abstract Object init(final Object initialContext);
 
-    protected abstract void startBaseline( String generationDate, String bundleName, String currentVersion, String previousVersion );
+    protected abstract void close(final Object context);
 
-    protected abstract void startPackage( boolean mismatch,
+    protected abstract void startBaseline( final Object context, String generationDate, String bundleName, String currentVersion, String previousVersion );
+
+    protected abstract void startPackage( final Object context,
+            boolean mismatch,
                                           String name,
                                           String shortDelta,
                                           String delta,
@@ -403,17 +412,18 @@
                                           DiffMessage diffMessage,
                                           Map<String,String> attributes );
 
-    protected abstract void startDiff( int depth,
+    protected abstract void startDiff( final Object context,
+                                       int depth,
                                        String type,
                                        String name,
                                        String delta,
                                        String shortDelta );
 
-    protected abstract void endDiff( int depth );
+    protected abstract void endDiff( final Object context, int depth );
 
-    protected abstract void endPackage();
+    protected abstract void endPackage(final Object context);
 
-    protected abstract void endBaseline();
+    protected abstract void endBaseline(final Object context);
 
     // internals
 
diff --git a/bundleplugin/src/main/java/org/apache/felix/bundleplugin/baseline/BaselinePlugin.java b/bundleplugin/src/main/java/org/apache/felix/bundleplugin/baseline/BaselinePlugin.java
index 0a46a26..722e0b6 100644
--- a/bundleplugin/src/main/java/org/apache/felix/bundleplugin/baseline/BaselinePlugin.java
+++ b/bundleplugin/src/main/java/org/apache/felix/bundleplugin/baseline/BaselinePlugin.java
@@ -24,8 +24,9 @@
 import java.util.Map;
 import java.util.Map.Entry;
 
-import org.codehaus.plexus.util.IOUtil;
 import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter;
+import org.codehaus.plexus.util.xml.XMLWriter;
+import org.sonatype.plexus.build.incremental.BuildContext;
 
 /**
  * BND Baseline check between two bundles.
@@ -55,32 +56,60 @@
      */
     private boolean logResults;
 
-    private FileWriter xmlFileWriter;
+    /**
+     * @component
+     */
+    private BuildContext buildContext;
 
-    private PrettyPrintXMLWriter xmlWriter;
+    private static final class Context {
+        public FileWriter writer;
+        public XMLWriter xmlWriter;
+    }
 
-    protected void init()
+    @Override
+    protected Object init(final Object noContext)
     {
         if ( xmlOutputFile != null )
         {
             xmlOutputFile.getParentFile().mkdirs();
             try
             {
-                xmlFileWriter = new FileWriter( xmlOutputFile );
-                xmlWriter = new PrettyPrintXMLWriter( xmlFileWriter );
+                final Context ctx = new Context();
+                ctx.writer = new FileWriter( xmlOutputFile );
+                ctx.xmlWriter = new PrettyPrintXMLWriter( ctx.writer );
+                return ctx;
             }
             catch ( IOException e )
             {
                 getLog().warn( "No XML report will be produced, cannot write data to " + xmlOutputFile, e );
             }
         }
+        return null;
     }
 
-    protected void startBaseline( String generationDate,
+    @Override
+    protected void close(final Object writer)
+    {
+        if ( writer != null )
+        {
+            try {
+
+                ((Context)writer).writer.close();
+            }
+            catch (IOException e)
+            {
+                // ignore
+            }
+        }
+    }
+    @Override
+    protected void startBaseline( Object context,
+                                  String generationDate,
                                   String bundleName,
                                   String currentVersion,
                                   String previousVersion )
     {
+        final XMLWriter xmlWriter = context == null ? null : ((Context)context).xmlWriter;
         if ( isLoggingResults() )
         {
             log( "Baseline Report - Generated by Apache Felix Maven Bundle Plugin on %s based on Bnd - see http://www.aqute.biz/Bnd/Bnd",
@@ -107,7 +136,7 @@
                  "==========" );
         }
 
-        if ( isProducingXml() )
+        if ( xmlWriter != null )
         {
             xmlWriter.startElement( "baseline" );
             xmlWriter.addAttribute( "version", "1.0.0" );
@@ -124,7 +153,9 @@
         }
     }
 
-    protected void startPackage( boolean mismatch,
+    @Override
+    protected void startPackage( Object context,
+                                 boolean mismatch,
                                  String name,
                                  String shortDelta,
                                  String delta,
@@ -134,6 +165,7 @@
                                  DiffMessage diffMessage,
                                  Map<String,String> attributes )
     {
+        final XMLWriter xmlWriter = context == null ? null : ((Context)context).xmlWriter;
         if ( isLoggingResults() )
         {
             log( TABLE_PATTERN,
@@ -147,19 +179,19 @@
                  attributes );
         }
 
-        if ( isProducingXml() )
+        if ( xmlWriter != null )
         {
             xmlWriter.startElement( "package" );
             xmlWriter.addAttribute( "name", name );
             xmlWriter.addAttribute( "delta", delta );
-            simpleElement( "mismatch", String.valueOf( mismatch ) );
-            simpleElement( "newerVersion", newerVersion );
-            simpleElement( "olderVersion", olderVersion );
-            simpleElement( "suggestedVersion", suggestedVersion );
+            simpleElement( xmlWriter, "mismatch", String.valueOf( mismatch ) );
+            simpleElement( xmlWriter, "newerVersion", newerVersion );
+            simpleElement( xmlWriter, "olderVersion", olderVersion );
+            simpleElement( xmlWriter, "suggestedVersion", suggestedVersion );
 
             if ( diffMessage != null )
             {
-                simpleElement( diffMessage.getType().name(), diffMessage.getMessage() );
+                simpleElement( xmlWriter, diffMessage.getType().name(), diffMessage.getMessage() );
             }
 
             xmlWriter.startElement( "attributes" );
@@ -183,8 +215,10 @@
         }
     }
 
-    protected void startDiff( int depth, String type, String name, String delta, String shortDelta )
+    @Override
+    protected void startDiff( Object context, int depth, String type, String name, String delta, String shortDelta )
     {
+        final XMLWriter xmlWriter = context == null ? null : ((Context)context).xmlWriter;
         if ( isLoggingResults() )
         {
             log( "%-" + (depth * 4) + "s %s %s %s",
@@ -194,7 +228,7 @@
                  name );
         }
 
-        if ( isProducingXml() )
+        if ( xmlWriter != null )
         {
             xmlWriter.startElement( type );
             xmlWriter.addAttribute( "name", name );
@@ -202,39 +236,39 @@
         }
     }
 
-    protected void endDiff( int depth )
+    @Override
+    protected void endDiff( Object context, int depth )
     {
-        if ( isProducingXml() )
+        final XMLWriter xmlWriter = context == null ? null : ((Context)context).xmlWriter;
+        if ( xmlWriter != null )
         {
             xmlWriter.endElement();
         }
     }
 
-    protected void endPackage()
+    @Override
+    protected void endPackage(Object context)
     {
+        final XMLWriter xmlWriter = context == null ? null : ((Context)context).xmlWriter;
         if ( isLoggingResults() )
         {
             log( "-----------------------------------------------------------------------------------------------------------" );
         }
 
-        if ( isProducingXml() )
-        {
-            xmlWriter.endElement();
-        }
-    }
-
-    protected void endBaseline()
-    {
         if ( xmlWriter != null )
         {
             xmlWriter.endElement();
-            IOUtil.close( xmlFileWriter );
         }
     }
 
-    private boolean isProducingXml()
+    @Override
+    protected void endBaseline(Object context)
     {
-        return xmlFileWriter!= null && xmlWriter != null;
+        final XMLWriter xmlWriter = context == null ? null : ((Context)context).xmlWriter;
+        if ( xmlWriter != null )
+        {
+            xmlWriter.endElement();
+        }
     }
 
     private boolean isLoggingResults()
@@ -247,7 +281,7 @@
         getLog().info( String.format( format, args ) );
     }
 
-    private void simpleElement( String name, String value )
+    private void simpleElement( XMLWriter xmlWriter, String name, String value )
     {
         xmlWriter.startElement( name );
         xmlWriter.writeText( value );
diff --git a/bundleplugin/src/main/java/org/apache/felix/bundleplugin/baseline/BaselineReport.java b/bundleplugin/src/main/java/org/apache/felix/bundleplugin/baseline/BaselineReport.java
index 39e9e42..b25f1ad 100644
--- a/bundleplugin/src/main/java/org/apache/felix/bundleplugin/baseline/BaselineReport.java
+++ b/bundleplugin/src/main/java/org/apache/felix/bundleplugin/baseline/BaselineReport.java
@@ -53,16 +53,18 @@
      */
     private File outputDirectory;
 
-    private Sink sink;
+    private static final class Context {
+        public Sink sink;
 
-    private Locale locale;
+        public Locale locale;
 
-    private int currentDepth = 0;
+        public int currentDepth = 0;
+    }
 
     // AbstractBaselinePlugin events
 
     @Override
-    protected void init()
+    protected Object init(final Object context)
     {
         failOnError = false;
         failOnWarning = false;
@@ -109,14 +111,25 @@
                 IOUtil.close( target );
             }
         }
+        return context;
     }
 
-    protected void startBaseline( String generationDate, String bundleName, String currentVersion, String previousVersion )
+    @Override
+    protected void close(final Object context)
     {
+        // nothing to do
+    }
+
+    @Override
+    protected void startBaseline( final Object context, String generationDate, String bundleName, String currentVersion, String previousVersion )
+    {
+        final Context ctx = (Context)context;
+        final Sink sink = ctx.sink;
+
         sink.head();
         sink.title();
 
-        String title = getBundle( locale ).getString( "report.baseline.title" );
+        String title = getBundle( ctx.locale ).getString( "report.baseline.title" );
         sink.text( title );
         sink.title_();
         sink.head_();
@@ -129,7 +142,7 @@
         sink.sectionTitle1_();
 
         sink.paragraph();
-        sink.text( getBundle( locale ).getString( "report.baseline.bndlink" ) + " " );
+        sink.text( getBundle( ctx.locale ).getString( "report.baseline.bndlink" ) + " " );
         sink.link( "http://www.aqute.biz/Bnd/Bnd" );
         sink.text( "Bnd" );
         sink.link_();
@@ -137,7 +150,7 @@
         sink.paragraph_();
 
         sink.paragraph();
-        sink.text( getBundle( locale ).getString( "report.baseline.bundle" ) + " " );
+        sink.text( getBundle( ctx.locale ).getString( "report.baseline.bundle" ) + " " );
         sink.figure();
         sink.figureGraphics( "images/baseline/bundle.gif" );
         sink.figure_();
@@ -148,21 +161,21 @@
         sink.listItem_();
 
         sink.paragraph();
-        sink.text( getBundle( locale ).getString( "report.baseline.version.current" ) + " " );
+        sink.text( getBundle( ctx.locale ).getString( "report.baseline.version.current" ) + " " );
         sink.bold();
         sink.text( currentVersion );
         sink.bold_();
         sink.paragraph_();
 
         sink.paragraph();
-        sink.text( getBundle( locale ).getString( "report.baseline.version.comparison" ) + " " );
+        sink.text( getBundle( ctx.locale ).getString( "report.baseline.version.comparison" ) + " " );
         sink.bold();
         sink.text( comparisonVersion );
         sink.bold_();
         sink.paragraph_();
 
         sink.paragraph();
-        sink.text( getBundle( locale ).getString( "report.baseline.generationdate" ) + " " );
+        sink.text( getBundle( ctx.locale ).getString( "report.baseline.generationdate" ) + " " );
         sink.bold();
         sink.text( generationDate );
         sink.bold_();
@@ -171,7 +184,9 @@
         sink.section1_();
     }
 
-    protected void startPackage( boolean mismatch,
+    @Override
+    protected void startPackage( final Object context,
+                                 boolean mismatch,
                                  String packageName,
                                  String shortDelta,
                                  String delta,
@@ -181,6 +196,9 @@
                                  DiffMessage diffMessage,
                                  Map<String,String> attributes )
     {
+        final Context ctx = (Context)context;
+        final Sink sink = ctx.sink;
+
         sink.list();
 
         sink.listItem();
@@ -219,18 +237,23 @@
         }
     }
 
-    protected void startDiff( int depth,
+    @Override
+    protected void startDiff( final Object context,
+                              int depth,
                               String type,
                               String name,
                               String delta,
                               String shortDelta )
     {
-        if ( currentDepth < depth )
+        final Context ctx = (Context)context;
+        final Sink sink = ctx.sink;
+
+        if ( ctx.currentDepth < depth )
         {
             sink.list();
         }
 
-        currentDepth = depth;
+        ctx.currentDepth = depth;
 
         sink.listItem();
         sink.figure();
@@ -249,35 +272,44 @@
         sink.italic_();
     }
 
-    protected void endDiff( int depth )
+    @Override
+    protected void endDiff( final Object context, int depth )
     {
+        final Context ctx = (Context)context;
+        final Sink sink = ctx.sink;
+
         sink.listItem_();
 
-        if ( currentDepth > depth )
+        if ( ctx.currentDepth > depth )
         {
             sink.list_();
         }
 
-        currentDepth = depth;
+        ctx.currentDepth = depth;
     }
 
-    protected void endPackage()
+    @Override
+    protected void endPackage(final Object context)
     {
-        if ( currentDepth > 0 )
+        final Context ctx = (Context)context;
+        final Sink sink = ctx.sink;
+        if ( ctx.currentDepth > 0 )
         {
             sink.list_();
-            currentDepth = 0;
+            ctx.currentDepth = 0;
         }
 
         sink.listItem_();
         sink.list_();
     }
 
-    protected void endBaseline()
+    @Override
+    protected void endBaseline(final Object context)
     {
-        sink.body_();
-        sink.flush();
-        sink.close();
+        final Context ctx = (Context)context;
+        ctx.sink.body_();
+        ctx.sink.flush();
+        ctx.sink.close();
     }
 
     // MavenReport methods
@@ -290,12 +322,13 @@
     public void generate( @SuppressWarnings( "deprecation" ) org.codehaus.doxia.sink.Sink sink, Locale locale )
         throws MavenReportException
     {
-        this.sink = sink;
-        this.locale = locale;
+        final Context ctx = new Context();
+        ctx.sink = sink;
+        ctx.locale = locale;
 
         try
         {
-            execute();
+            execute(ctx);
         }
         catch ( Exception e )
         {