[MSHADE-363] add ReproducibleResourceTransformer to keep compatibility
diff --git a/src/main/java/org/apache/maven/plugins/shade/DefaultShader.java b/src/main/java/org/apache/maven/plugins/shade/DefaultShader.java
index 15712ad..6f3c4e6 100644
--- a/src/main/java/org/apache/maven/plugins/shade/DefaultShader.java
+++ b/src/main/java/org/apache/maven/plugins/shade/DefaultShader.java
@@ -49,6 +49,7 @@
 import org.apache.maven.plugins.shade.filter.Filter;
 import org.apache.maven.plugins.shade.relocation.Relocator;
 import org.apache.maven.plugins.shade.resource.ManifestResourceTransformer;
+import org.apache.maven.plugins.shade.resource.ReproducibleResourceTransformer;
 import org.apache.maven.plugins.shade.resource.ResourceTransformer;
 import org.codehaus.plexus.component.annotations.Component;
 import org.codehaus.plexus.logging.AbstractLogEnabled;
@@ -76,7 +77,7 @@
     {
         Set<String> resources = new HashSet<>();
 
-        ResourceTransformer manifestTransformer = null;
+        ManifestResourceTransformer manifestTransformer = null;
         List<ResourceTransformer> transformers =
             new ArrayList<>( shadeRequest.getResourceTransformers() );
         for ( Iterator<ResourceTransformer> it = transformers.iterator(); it.hasNext(); )
@@ -84,7 +85,7 @@
             ResourceTransformer transformer = it.next();
             if ( transformer instanceof ManifestResourceTransformer )
             {
-                manifestTransformer = transformer;
+                manifestTransformer = (ManifestResourceTransformer) transformer;
                 it.remove();
             }
         }
@@ -265,11 +266,11 @@
     }
 
     private void goThroughAllJarEntriesForManifestTransformer( ShadeRequest shadeRequest, Set<String> resources,
-                                                               ResourceTransformer resourceTransformer,
+                                                               ManifestResourceTransformer manifestTransformer,
                                                                JarOutputStream jos )
         throws IOException
     {
-        if ( resourceTransformer != null )
+        if ( manifestTransformer != null )
         {
             for ( File jar : shadeRequest.getJars() )
             {
@@ -279,22 +280,22 @@
                     {
                         JarEntry entry = en.nextElement();
                         String resource = entry.getName();
-                        if ( resourceTransformer.canTransformResource( resource ) )
+                        if ( manifestTransformer.canTransformResource( resource ) )
                         {
                             resources.add( resource );
                             try ( InputStream inputStream = jarFile.getInputStream( entry ) )
                             {
-                                resourceTransformer.processResource( resource, inputStream,
-                                        shadeRequest.getRelocators(), entry.getTime() );
+                                manifestTransformer.processResource( resource, inputStream,
+                                                                     shadeRequest.getRelocators(), entry.getTime() );
                             }
                             break;
                         }
                     }
                 }
             }
-            if ( resourceTransformer.hasTransformedResource() )
+            if ( manifestTransformer.hasTransformedResource() )
             {
-                resourceTransformer.modifyOutputStream( jos );
+                manifestTransformer.modifyOutputStream( jos );
             }
         }
     }
@@ -544,7 +545,14 @@
             {
                 getLogger().debug( "Transforming " + name + " using " + transformer.getClass().getName() );
 
-                transformer.processResource( name, is, relocators, time );
+                if ( transformer instanceof ReproducibleResourceTransformer )
+                {
+                    ( (ReproducibleResourceTransformer) transformer ).processResource( name, is, relocators, time );
+                }
+                else
+                {
+                    transformer.processResource( name, is, relocators );
+                }
 
                 resourceTransformed = true;
 
diff --git a/src/main/java/org/apache/maven/plugins/shade/resource/AbstractCompatibilityTransformer.java b/src/main/java/org/apache/maven/plugins/shade/resource/AbstractCompatibilityTransformer.java
new file mode 100644
index 0000000..392ddb9
--- /dev/null
+++ b/src/main/java/org/apache/maven/plugins/shade/resource/AbstractCompatibilityTransformer.java
@@ -0,0 +1,39 @@
+package org.apache.maven.plugins.shade.resource;
+
+/*
+ * 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.
+ */
+
+import org.apache.maven.plugins.shade.relocation.Relocator;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+
+/**
+ * An abstract class to implement once the old non-reproducible ResourceTransformer API.
+ */
+abstract class AbstractCompatibilityTransformer
+    implements ReproducibleResourceTransformer
+{
+    public final void processResource( String resource, InputStream is, List<Relocator> relocators )
+        throws IOException
+    {
+        processResource( resource, is, relocators, 0 );
+    }
+}
diff --git a/src/main/java/org/apache/maven/plugins/shade/resource/ApacheLicenseResourceTransformer.java b/src/main/java/org/apache/maven/plugins/shade/resource/ApacheLicenseResourceTransformer.java
index 6fd7931..524ffdd 100644
--- a/src/main/java/org/apache/maven/plugins/shade/resource/ApacheLicenseResourceTransformer.java
+++ b/src/main/java/org/apache/maven/plugins/shade/resource/ApacheLicenseResourceTransformer.java
@@ -30,7 +30,7 @@
  * Prevents duplicate copies of the license
  */
 public class ApacheLicenseResourceTransformer
-    implements ResourceTransformer
+    extends AbstractCompatibilityTransformer
 {
     private static final String LICENSE_PATH = "META-INF/LICENSE";
 
diff --git a/src/main/java/org/apache/maven/plugins/shade/resource/ApacheNoticeResourceTransformer.java b/src/main/java/org/apache/maven/plugins/shade/resource/ApacheNoticeResourceTransformer.java
index 173d1d9..e75062b 100644
--- a/src/main/java/org/apache/maven/plugins/shade/resource/ApacheNoticeResourceTransformer.java
+++ b/src/main/java/org/apache/maven/plugins/shade/resource/ApacheNoticeResourceTransformer.java
@@ -43,7 +43,7 @@
  * Merges <code>META-INF/NOTICE.TXT</code> files.
  */
 public class ApacheNoticeResourceTransformer
-    implements ResourceTransformer
+    extends AbstractCompatibilityTransformer
 {
     Set<String> entries = new LinkedHashSet<>();
 
diff --git a/src/main/java/org/apache/maven/plugins/shade/resource/AppendingTransformer.java b/src/main/java/org/apache/maven/plugins/shade/resource/AppendingTransformer.java
index 61556eb..c3cc937 100644
--- a/src/main/java/org/apache/maven/plugins/shade/resource/AppendingTransformer.java
+++ b/src/main/java/org/apache/maven/plugins/shade/resource/AppendingTransformer.java
@@ -33,7 +33,7 @@
  * A resource processor that appends content for a resource, separated by a newline.
  */
 public class AppendingTransformer
-    implements ResourceTransformer
+    extends AbstractCompatibilityTransformer
 {
     String resource;
 
diff --git a/src/main/java/org/apache/maven/plugins/shade/resource/ComponentsXmlResourceTransformer.java b/src/main/java/org/apache/maven/plugins/shade/resource/ComponentsXmlResourceTransformer.java
index b82b0d7..9f83618 100644
--- a/src/main/java/org/apache/maven/plugins/shade/resource/ComponentsXmlResourceTransformer.java
+++ b/src/main/java/org/apache/maven/plugins/shade/resource/ComponentsXmlResourceTransformer.java
@@ -42,7 +42,7 @@
  * A resource processor that aggregates plexus <code>components.xml</code> files.
  */
 public class ComponentsXmlResourceTransformer
-    implements ResourceTransformer
+    extends AbstractCompatibilityTransformer
 {
     private Map<String, Xpp3Dom> components = new LinkedHashMap<>();
 
diff --git a/src/main/java/org/apache/maven/plugins/shade/resource/DontIncludeResourceTransformer.java b/src/main/java/org/apache/maven/plugins/shade/resource/DontIncludeResourceTransformer.java
index bfd1f0e..e1b7bc2 100644
--- a/src/main/java/org/apache/maven/plugins/shade/resource/DontIncludeResourceTransformer.java
+++ b/src/main/java/org/apache/maven/plugins/shade/resource/DontIncludeResourceTransformer.java
@@ -32,7 +32,7 @@
  * resource into the shaded JAR.
  */
 public class DontIncludeResourceTransformer
-    implements ResourceTransformer
+    extends AbstractCompatibilityTransformer
 {
     String resource;
     
diff --git a/src/main/java/org/apache/maven/plugins/shade/resource/GroovyResourceTransformer.java b/src/main/java/org/apache/maven/plugins/shade/resource/GroovyResourceTransformer.java
index f54c0cb..1a8d61d 100644
--- a/src/main/java/org/apache/maven/plugins/shade/resource/GroovyResourceTransformer.java
+++ b/src/main/java/org/apache/maven/plugins/shade/resource/GroovyResourceTransformer.java
@@ -36,7 +36,7 @@
  * Aggregate Apache Groovy extension modules descriptors
  */
 public class GroovyResourceTransformer
-    implements ResourceTransformer
+    extends AbstractCompatibilityTransformer
 {
 
     static final String EXT_MODULE_NAME_LEGACY = "META-INF/services/org.codehaus.groovy.runtime.ExtensionModule";
diff --git a/src/main/java/org/apache/maven/plugins/shade/resource/IncludeResourceTransformer.java b/src/main/java/org/apache/maven/plugins/shade/resource/IncludeResourceTransformer.java
index ab360d1..dfe3326 100644
--- a/src/main/java/org/apache/maven/plugins/shade/resource/IncludeResourceTransformer.java
+++ b/src/main/java/org/apache/maven/plugins/shade/resource/IncludeResourceTransformer.java
@@ -35,7 +35,7 @@
  * content into the shaded JAR.
  */
 public class IncludeResourceTransformer
-    implements ResourceTransformer
+    extends AbstractCompatibilityTransformer
 {
     File file;
 
diff --git a/src/main/java/org/apache/maven/plugins/shade/resource/ManifestResourceTransformer.java b/src/main/java/org/apache/maven/plugins/shade/resource/ManifestResourceTransformer.java
index 6bf711b..b66c73a 100644
--- a/src/main/java/org/apache/maven/plugins/shade/resource/ManifestResourceTransformer.java
+++ b/src/main/java/org/apache/maven/plugins/shade/resource/ManifestResourceTransformer.java
@@ -41,7 +41,7 @@
  * @since 1.2
  */
 public class ManifestResourceTransformer
-    implements ResourceTransformer
+    extends AbstractCompatibilityTransformer
 {
     private final List<String> defaultAttributes = Arrays.asList( "Export-Package",
                                                                   "Import-Package",
diff --git a/src/main/java/org/apache/maven/plugins/shade/resource/PluginXmlResourceTransformer.java b/src/main/java/org/apache/maven/plugins/shade/resource/PluginXmlResourceTransformer.java
index 9392b53..0cbb3fa 100644
--- a/src/main/java/org/apache/maven/plugins/shade/resource/PluginXmlResourceTransformer.java
+++ b/src/main/java/org/apache/maven/plugins/shade/resource/PluginXmlResourceTransformer.java
@@ -44,7 +44,7 @@
  * @since 3.0
  */
 public class PluginXmlResourceTransformer
-    implements ResourceTransformer
+    extends AbstractCompatibilityTransformer
 {
     private List<Xpp3Dom> mojos = new ArrayList<>();
 
diff --git a/src/main/java/org/apache/maven/plugins/shade/resource/ReproducibleResourceTransformer.java b/src/main/java/org/apache/maven/plugins/shade/resource/ReproducibleResourceTransformer.java
new file mode 100644
index 0000000..eb66032
--- /dev/null
+++ b/src/main/java/org/apache/maven/plugins/shade/resource/ReproducibleResourceTransformer.java
@@ -0,0 +1,49 @@
+package org.apache.maven.plugins.shade.resource;
+
+/*
+ * 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.
+ */
+
+import org.apache.maven.plugins.shade.relocation.Relocator;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+
+/**
+ * Transform resource ensuring reproducible output: that requires to get the timestamp of
+ * the initial resources to define in a reproducible way the timestamp of the transformed
+ * resource.
+ *
+ * @author Hervé Boutemy
+ * @since 3.2.4
+ */
+public interface ReproducibleResourceTransformer
+    extends ResourceTransformer
+{
+    /**
+     * Transform an individual resource
+     * @param resource The resource name
+     * @param is An input stream for the resource, the implementation should *not* close this stream
+     * @param relocators  A list of relocators
+     * @param time the time of the resource to process
+     * @throws IOException When the IO blows up
+     */
+    void processResource( String resource, InputStream is, List<Relocator> relocators, long time )
+        throws IOException;
+}
diff --git a/src/main/java/org/apache/maven/plugins/shade/resource/ResourceBundleAppendingTransformer.java b/src/main/java/org/apache/maven/plugins/shade/resource/ResourceBundleAppendingTransformer.java
index 77223ee..5ea56cf 100644
--- a/src/main/java/org/apache/maven/plugins/shade/resource/ResourceBundleAppendingTransformer.java
+++ b/src/main/java/org/apache/maven/plugins/shade/resource/ResourceBundleAppendingTransformer.java
@@ -39,7 +39,7 @@
  * @since 3.0.0
  */
 public class ResourceBundleAppendingTransformer
-    implements ResourceTransformer
+    extends AbstractCompatibilityTransformer
 {
     private Map<String, ByteArrayOutputStream>  dataMap = new HashMap<>();
     
diff --git a/src/main/java/org/apache/maven/plugins/shade/resource/ResourceTransformer.java b/src/main/java/org/apache/maven/plugins/shade/resource/ResourceTransformer.java
index bb4400a..7b76666 100644
--- a/src/main/java/org/apache/maven/plugins/shade/resource/ResourceTransformer.java
+++ b/src/main/java/org/apache/maven/plugins/shade/resource/ResourceTransformer.java
@@ -36,10 +36,10 @@
      * @param resource The resource name
      * @param is An input stream for the resource, the implementation should *not* close this stream
      * @param relocators  A list of relocators
-     * @param time the time of the resource to process
      * @throws IOException When the IO blows up
+     * @deprecated prefer ReproducibleResourceTransformer
      */
-    void processResource( String resource, InputStream is, List<Relocator> relocators, long time )
+    void processResource( String resource, InputStream is, List<Relocator> relocators )
         throws IOException;
 
     boolean hasTransformedResource();
diff --git a/src/main/java/org/apache/maven/plugins/shade/resource/ServicesResourceTransformer.java b/src/main/java/org/apache/maven/plugins/shade/resource/ServicesResourceTransformer.java
index 929c5fa..b2f5dd6 100644
--- a/src/main/java/org/apache/maven/plugins/shade/resource/ServicesResourceTransformer.java
+++ b/src/main/java/org/apache/maven/plugins/shade/resource/ServicesResourceTransformer.java
@@ -46,7 +46,7 @@
  * shading process.
  */
 public class ServicesResourceTransformer
-    implements ResourceTransformer
+    extends AbstractCompatibilityTransformer
 {
 
     private static final String SERVICES_PATH = "META-INF/services";
diff --git a/src/main/java/org/apache/maven/plugins/shade/resource/XmlAppendingTransformer.java b/src/main/java/org/apache/maven/plugins/shade/resource/XmlAppendingTransformer.java
index 608c1a2..f5f6ac8 100644
--- a/src/main/java/org/apache/maven/plugins/shade/resource/XmlAppendingTransformer.java
+++ b/src/main/java/org/apache/maven/plugins/shade/resource/XmlAppendingTransformer.java
@@ -44,7 +44,7 @@
  * Appends multiple occurrences of some XML file.
  */
 public class XmlAppendingTransformer
-    implements ResourceTransformer
+    extends AbstractCompatibilityTransformer
 {
     public static final String XSI_NS = "http://www.w3.org/2001/XMLSchema-instance";
 
diff --git a/src/main/java/org/apache/maven/plugins/shade/resource/properties/PropertiesTransformer.java b/src/main/java/org/apache/maven/plugins/shade/resource/properties/PropertiesTransformer.java
index 8040923..9b92797 100644
--- a/src/main/java/org/apache/maven/plugins/shade/resource/properties/PropertiesTransformer.java
+++ b/src/main/java/org/apache/maven/plugins/shade/resource/properties/PropertiesTransformer.java
@@ -32,7 +32,7 @@
 import java.util.jar.JarOutputStream;
 
 import org.apache.maven.plugins.shade.relocation.Relocator;
-import org.apache.maven.plugins.shade.resource.ResourceTransformer;
+import org.apache.maven.plugins.shade.resource.ReproducibleResourceTransformer;
 import org.apache.maven.plugins.shade.resource.properties.io.NoCloseOutputStream;
 import org.apache.maven.plugins.shade.resource.properties.io.SkipPropertiesDateLineWriter;
 
@@ -42,7 +42,7 @@
  * @since 3.2.2
  */
 public class PropertiesTransformer
-    implements ResourceTransformer
+    implements ReproducibleResourceTransformer
 {
     private String resource;
     private String alreadyMergedKey;
@@ -74,6 +74,13 @@
     }
 
     @Override
+    public final void processResource( String resource, InputStream is, List<Relocator> relocators )
+        throws IOException
+    {
+        processResource( resource, is, relocators, 0 );
+    }
+
+    @Override
     public void processResource( final String resource, final InputStream is, final List<Relocator> relocators,
                                  long time )
             throws IOException
diff --git a/src/test/java/org/apache/maven/plugins/shade/resource/rule/TransformerTesterRule.java b/src/test/java/org/apache/maven/plugins/shade/resource/rule/TransformerTesterRule.java
index 1eaf2b1..c159841 100644
--- a/src/test/java/org/apache/maven/plugins/shade/resource/rule/TransformerTesterRule.java
+++ b/src/test/java/org/apache/maven/plugins/shade/resource/rule/TransformerTesterRule.java
@@ -39,7 +39,7 @@
 import java.util.jar.JarOutputStream;
 
 import org.apache.maven.plugins.shade.relocation.Relocator;
-import org.apache.maven.plugins.shade.resource.ResourceTransformer;
+import org.apache.maven.plugins.shade.resource.ReproducibleResourceTransformer;
 import org.codehaus.plexus.component.configurator.ComponentConfigurationException;
 import org.codehaus.plexus.component.configurator.converters.ConfigurationConverter;
 import org.codehaus.plexus.component.configurator.converters.lookup.ConverterLookup;
@@ -51,7 +51,8 @@
 import org.junit.runner.Description;
 import org.junit.runners.model.Statement;
 
-public class TransformerTesterRule implements TestRule
+public class TransformerTesterRule
+    implements TestRule
 {
     @Override
     public Statement apply( final Statement base, final Description description )
@@ -71,9 +72,9 @@
                 final Map<String, String> jar;
                 try
                 {
-                    final ResourceTransformer transformer = createTransformer(spec);
-                    visit(spec, transformer);
-                    jar = captureOutput(transformer);
+                    final ReproducibleResourceTransformer transformer = createTransformer( spec );
+                    visit( spec, transformer );
+                    jar = captureOutput( transformer );
                 }
                 catch ( final Exception ex )
                 {
@@ -110,7 +111,8 @@
         }
     }
 
-    private Map<String, String> captureOutput(final ResourceTransformer transformer ) throws IOException
+    private Map<String, String> captureOutput( final ReproducibleResourceTransformer transformer )
+        throws IOException
     {
         final ByteArrayOutputStream out = new ByteArrayOutputStream();
         try ( final JarOutputStream jar = new JarOutputStream( out ) )
@@ -130,11 +132,12 @@
         return created;
     }
 
-    private void visit( final TransformerTest spec, final ResourceTransformer transformer ) throws IOException
+    private void visit( final TransformerTest spec, final ReproducibleResourceTransformer transformer )
+        throws IOException
     {
         for ( final Resource resource : spec.visited() )
         {
-            if ( transformer.canTransformResource( resource.path() ))
+            if ( transformer.canTransformResource( resource.path() ) )
             {
                 transformer.processResource(
                         resource.path(),
@@ -156,7 +159,7 @@
         return builder.toString();
     }
 
-    private ResourceTransformer createTransformer(final TransformerTest spec)
+    private ReproducibleResourceTransformer createTransformer(final TransformerTest spec)
     {
         final ConverterLookup lookup = new DefaultConverterLookup();
         try
@@ -167,7 +170,7 @@
             {
                 configuration.addChild( property.name(), property.value() );
             }
-            return ResourceTransformer.class.cast(
+            return ReproducibleResourceTransformer.class.cast(
                     converter.fromConfiguration( lookup, configuration,  spec.transformer(), spec.transformer(),
                         Thread.currentThread().getContextClassLoader(),
                         new DefaultExpressionEvaluator() ) );