diff --git a/maven-plugin/src/main/java/org/apache/maven/plugin/util/scan/AbstractSourceInclusionScanner.java b/maven-plugin/src/main/java/org/apache/maven/plugin/util/scan/AbstractSourceInclusionScanner.java
new file mode 100644
index 0000000000..7fa7738497
--- /dev/null
+++ b/maven-plugin/src/main/java/org/apache/maven/plugin/util/scan/AbstractSourceInclusionScanner.java
@@ -0,0 +1,42 @@
+package org.apache.maven.plugin.util.scan;
+
+/*
+ * Copyright 2001-2005 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.
+ */
+
+import org.apache.maven.plugin.util.scan.mapping.SourceMapping;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author jdcasey
+ */
+public abstract class AbstractSourceInclusionScanner
+ implements SourceInclusionScanner
+{
+
+ private final List sourceMappings = new ArrayList();
+
+ public final void addSourceMapping( SourceMapping sourceMapping )
+ {
+ sourceMappings.add( sourceMapping );
+ }
+
+ protected final List getSourceMappings()
+ {
+ return Collections.unmodifiableList( sourceMappings );
+ }
+
+}
\ No newline at end of file
diff --git a/maven-plugin/src/main/java/org/apache/maven/plugin/util/scan/InclusionScanException.java b/maven-plugin/src/main/java/org/apache/maven/plugin/util/scan/InclusionScanException.java
new file mode 100644
index 0000000000..d69bf44dc2
--- /dev/null
+++ b/maven-plugin/src/main/java/org/apache/maven/plugin/util/scan/InclusionScanException.java
@@ -0,0 +1,36 @@
+package org.apache.maven.plugin.util.scan;
+
+/*
+ * Copyright 2001-2005 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.
+ */
+
+/**
+ * @author jdcasey
+ */
+public class InclusionScanException
+ extends Exception
+{
+
+ public InclusionScanException( String message )
+ {
+ super( message );
+ }
+
+ public InclusionScanException( String message, Throwable cause )
+ {
+ super( message, cause );
+ }
+
+}
diff --git a/maven-plugin/src/main/java/org/apache/maven/plugin/util/scan/SourceInclusionScanner.java b/maven-plugin/src/main/java/org/apache/maven/plugin/util/scan/SourceInclusionScanner.java
new file mode 100644
index 0000000000..f1bea56942
--- /dev/null
+++ b/maven-plugin/src/main/java/org/apache/maven/plugin/util/scan/SourceInclusionScanner.java
@@ -0,0 +1,32 @@
+package org.apache.maven.plugin.util.scan;
+
+import org.apache.maven.plugin.util.scan.mapping.SourceMapping;
+
+import java.io.File;
+import java.util.Set;
+
+/*
+ * Copyright 2001-2005 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.
+ */
+
+/**
+ * @author jdcasey
+ */
+public interface SourceInclusionScanner
+{
+
+ void addSourceMapping( SourceMapping sourceMapping );
+
+ Set getIncludedSources( File sourceDir, File targetDir ) throws InclusionScanException;
+
+}
\ No newline at end of file
diff --git a/maven-plugin/src/main/java/org/apache/maven/plugin/util/scan/StaleSourceScanner.java b/maven-plugin/src/main/java/org/apache/maven/plugin/util/scan/StaleSourceScanner.java
new file mode 100644
index 0000000000..309f5d5d9b
--- /dev/null
+++ b/maven-plugin/src/main/java/org/apache/maven/plugin/util/scan/StaleSourceScanner.java
@@ -0,0 +1,133 @@
+package org.apache.maven.plugin.util.scan;
+
+/*
+ * Copyright 2001-2005 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.
+ */
+
+import org.apache.maven.plugin.util.scan.mapping.SourceMapping;
+import org.codehaus.plexus.util.DirectoryScanner;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author jdcasey
+ */
+public class StaleSourceScanner
+ extends AbstractSourceInclusionScanner
+{
+
+ private final long lastUpdatedWithinMsecs;
+
+ private final Set sourceIncludes;
+
+ private final Set sourceExcludes;
+
+ public StaleSourceScanner( long lastUpdatedWithinMsecs )
+ {
+ this( lastUpdatedWithinMsecs, Collections.singleton("**/*"), Collections.EMPTY_SET );
+ }
+
+ public StaleSourceScanner()
+ {
+ this( 0, Collections.singleton("**/*"), Collections.EMPTY_SET );
+ }
+
+ public StaleSourceScanner( long lastUpdatedWithinMsecs, Set sourceIncludes, Set sourceExcludes )
+ {
+ this.lastUpdatedWithinMsecs = lastUpdatedWithinMsecs;
+
+ this.sourceIncludes = sourceIncludes;
+ this.sourceExcludes = sourceExcludes;
+ }
+
+ public Set getIncludedSources( File sourceDir, File targetDir )
+ throws InclusionScanException
+ {
+ Set matchingSources = new HashSet();
+
+ List srcMappings = getSourceMappings();
+
+ String[] potentialIncludes = scanForSources( sourceDir );
+ for ( int i = 0; i < potentialIncludes.length; i++ )
+ {
+ String path = potentialIncludes[i];
+
+ File sourceFile = new File( sourceDir, path );
+
+ staleSourceFileTesting: for ( Iterator patternIt = srcMappings.iterator(); patternIt.hasNext(); )
+ {
+ SourceMapping mapping = (SourceMapping) patternIt.next();
+
+ Set targetFiles = mapping.getTargetFiles( targetDir, path );
+
+ // never include files that don't have corresponding target mappings.
+ // the targets don't have to exist on the filesystem, but the
+ // mappers must tell us to look for them.
+ for ( Iterator targetIt = targetFiles.iterator(); targetIt.hasNext(); )
+ {
+ File targetFile = (File) targetIt.next();
+
+ if ( !targetFile.exists()
+ || ( targetFile.lastModified() + lastUpdatedWithinMsecs < sourceFile.lastModified() ) )
+ {
+ matchingSources.add( sourceFile );
+ break staleSourceFileTesting;
+ }
+ }
+ }
+ }
+
+ return matchingSources;
+ }
+
+ private String[] scanForSources( File sourceDir )
+ {
+ DirectoryScanner ds = new DirectoryScanner();
+ ds.setFollowSymlinks(true);
+ ds.setBasedir( sourceDir );
+
+ String[] includes = null;
+ if ( sourceIncludes.isEmpty() )
+ {
+ includes = new String[0];
+ }
+ else
+ {
+ includes = (String[]) sourceIncludes.toArray( new String[sourceIncludes.size()] );
+ }
+
+ ds.setIncludes( includes );
+
+ String[] excludes = null;
+ if ( sourceExcludes.isEmpty() )
+ {
+ excludes = new String[0];
+ }
+ else
+ {
+ excludes = (String[]) sourceExcludes.toArray( new String[sourceExcludes.size()] );
+ }
+
+ ds.setExcludes( excludes );
+
+ ds.scan();
+
+ return ds.getIncludedFiles();
+ }
+
+}
\ No newline at end of file
diff --git a/maven-plugin/src/main/java/org/apache/maven/plugin/util/scan/mapping/SourceMapping.java b/maven-plugin/src/main/java/org/apache/maven/plugin/util/scan/mapping/SourceMapping.java
new file mode 100644
index 0000000000..675e3690dc
--- /dev/null
+++ b/maven-plugin/src/main/java/org/apache/maven/plugin/util/scan/mapping/SourceMapping.java
@@ -0,0 +1,32 @@
+package org.apache.maven.plugin.util.scan.mapping;
+
+/*
+ * Copyright 2001-2005 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.
+ */
+
+import org.apache.maven.plugin.util.scan.InclusionScanException;
+
+import java.io.File;
+import java.util.Set;
+
+/**
+ * @author jdcasey
+ */
+public interface SourceMapping
+{
+
+ Set getTargetFiles(File targetDir, String source) throws InclusionScanException;
+
+}
diff --git a/maven-plugin/src/main/java/org/apache/maven/plugin/util/scan/mapping/SuffixMapping.java b/maven-plugin/src/main/java/org/apache/maven/plugin/util/scan/mapping/SuffixMapping.java
new file mode 100644
index 0000000000..9609285ea7
--- /dev/null
+++ b/maven-plugin/src/main/java/org/apache/maven/plugin/util/scan/mapping/SuffixMapping.java
@@ -0,0 +1,70 @@
+package org.apache.maven.plugin.util.scan.mapping;
+
+import org.apache.maven.plugin.util.scan.InclusionScanException;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+/*
+ * Copyright 2001-2005 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.
+ */
+
+/**
+ * @author jdcasey
+ */
+public final class SuffixMapping
+ implements SourceMapping
+{
+
+ private final String sourceSuffix;
+
+ private final Set targetSuffixes;
+
+ public SuffixMapping( String sourceSuffix, String targetSuffix )
+ {
+ this.sourceSuffix = sourceSuffix;
+
+ this.targetSuffixes = Collections.singleton( targetSuffix );
+ }
+
+ public SuffixMapping( String sourceSuffix, Set targetSuffixes )
+ {
+ this.sourceSuffix = sourceSuffix;
+
+ this.targetSuffixes = Collections.unmodifiableSet( targetSuffixes );
+ }
+
+ public Set getTargetFiles( File targetDir, String source )
+ throws InclusionScanException
+ {
+ Set targetFiles = new HashSet();
+
+ if(source.endsWith(sourceSuffix))
+ {
+ String base = source.substring( 0, source.length() - sourceSuffix.length() );
+
+ for ( Iterator it = targetSuffixes.iterator(); it.hasNext(); )
+ {
+ String suffix = (String) it.next();
+
+ targetFiles.add( new File( targetDir, base + suffix ) );
+ }
+ }
+
+ return targetFiles;
+ }
+
+}
\ No newline at end of file
diff --git a/maven-plugin/src/test/java/org/apache/maven/plugin/util/scan/StaleSourceScannerTest.java b/maven-plugin/src/test/java/org/apache/maven/plugin/util/scan/StaleSourceScannerTest.java
new file mode 100644
index 0000000000..8976c9416a
--- /dev/null
+++ b/maven-plugin/src/test/java/org/apache/maven/plugin/util/scan/StaleSourceScannerTest.java
@@ -0,0 +1,440 @@
+package org.apache.maven.plugin.util.scan;
+
+import org.apache.maven.plugin.util.scan.StaleSourceScanner;
+import org.apache.maven.plugin.util.scan.mapping.SuffixMapping;
+import org.codehaus.plexus.util.IOUtil;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Collections;
+import java.util.Set;
+
+import junit.framework.TestCase;
+
+/*
+ * Copyright 2001-2005 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.
+ */
+
+/**
+ * @author jdcasey
+ */
+public class StaleSourceScannerTest
+ extends TestCase
+{
+ private static final String TESTFILE_DEST_MARKER_FILE = StaleSourceScanner.class.getName().replace('.', '/') + "-testMarker.txt";
+
+ // test 1.
+ public void testWithDefaultConstructorShouldFindOneStaleSource() throws Exception
+ {
+ File base = new File(getTestBaseDir(), "test1");
+
+ long now = System.currentTimeMillis();
+
+ File targetFile = new File(base, "file.xml");
+
+ writeFile(targetFile);
+
+ targetFile.setLastModified(now - 60000);
+
+ File sourceFile = new File(base, "file.java");
+
+ writeFile(sourceFile);
+
+ sourceFile.setLastModified(now);
+
+ SuffixMapping mapping = new SuffixMapping(".java", ".xml");
+
+ StaleSourceScanner scanner = new StaleSourceScanner();
+
+ scanner.addSourceMapping(mapping);
+
+ Set result = scanner.getIncludedSources(base, base);
+
+ assertEquals("wrong number of stale sources returned.", 1, result.size());
+
+ assertTrue("expected stale source file not found in result", result.contains(sourceFile));
+ }
+
+ // test 2.
+ public void testWithDefaultConstructorShouldNotFindStaleSources() throws Exception
+ {
+ File base = new File(getTestBaseDir(), "test2");
+
+ long now = System.currentTimeMillis();
+
+ File sourceFile = new File(base, "file.java");
+
+ writeFile(sourceFile);
+
+ sourceFile.setLastModified(now - 60000);
+
+ File targetFile = new File(base, "file.xml");
+
+ writeFile(targetFile);
+
+ targetFile.setLastModified(now);
+
+ SuffixMapping mapping = new SuffixMapping(".java", ".xml");
+
+ StaleSourceScanner scanner = new StaleSourceScanner();
+
+ scanner.addSourceMapping(mapping);
+
+ Set result = scanner.getIncludedSources(base, base);
+
+ assertEquals("wrong number of stale sources returned.", 0, result.size());
+
+ assertFalse("expected stale source file not found in result", result.contains(sourceFile));
+ }
+
+ // test 3.
+ public void testWithDefaultConstructorShouldFindStaleSourcesBecauseOfMissingTargetFile() throws Exception
+ {
+ File base = new File(getTestBaseDir(), "test3");
+
+ long now = System.currentTimeMillis();
+
+ File sourceFile = new File(base, "file.java");
+
+ writeFile(sourceFile);
+
+ SuffixMapping mapping = new SuffixMapping(".java", ".xml");
+
+ StaleSourceScanner scanner = new StaleSourceScanner();
+
+ scanner.addSourceMapping(mapping);
+
+ Set result = scanner.getIncludedSources(base, base);
+
+ assertEquals("wrong number of stale sources returned.", 1, result.size());
+
+ assertTrue("expected stale source file not found in result", result.contains(sourceFile));
+ }
+
+ // test 4.
+ public void testWithDefaultConstructorShouldFindStaleSourcesOneBecauseOfMissingTargetAndOneBecauseOfStaleTarget() throws Exception
+ {
+ File base = new File(getTestBaseDir(), "test4");
+
+ long now = System.currentTimeMillis();
+
+ File targetFile = new File(base, "file2.xml");
+
+ writeFile(targetFile);
+
+ targetFile.setLastModified(now - 60000);
+
+ File sourceFile = new File(base, "file.java");
+
+ writeFile(sourceFile);
+
+ File sourceFile2 = new File(base, "file2.java");
+
+ writeFile(sourceFile2);
+
+ sourceFile2.setLastModified(now);
+
+ SuffixMapping mapping = new SuffixMapping(".java", ".xml");
+
+ StaleSourceScanner scanner = new StaleSourceScanner();
+
+ scanner.addSourceMapping(mapping);
+
+ Set result = scanner.getIncludedSources(base, base);
+
+ assertEquals("wrong number of stale sources returned.", 2, result.size());
+
+ assertTrue("expected stale source file not found in result", result.contains(sourceFile));
+
+ assertTrue("expected stale source file not found in result", result.contains(sourceFile2));
+ }
+
+ // test 5.
+ public void testWithDefaultConstructorShouldFindOneStaleSourcesWithStaleTargetAndOmitUpToDateSource() throws Exception
+ {
+ File base = new File(getTestBaseDir(), "test5");
+
+ long now = System.currentTimeMillis();
+
+ // target/source (1) should result in source being included.
+
+ // write the target file first, and set the lastmod to some time in the
+ // past to ensure this.
+ File targetFile = new File(base, "file.xml");
+
+ writeFile(targetFile);
+
+ targetFile.setLastModified(now - 60000);
+
+ // now write the source file, and set the lastmod to now.
+ File sourceFile = new File(base, "file.java");
+
+ writeFile(sourceFile);
+
+ sourceFile.setLastModified(now);
+
+ // target/source (2) should result in source being omitted.
+
+ // write the source file first, and set the lastmod to some time in the
+ // past to ensure this.
+ File sourceFile2 = new File(base, "file2.java");
+
+ writeFile(sourceFile2);
+
+ sourceFile2.setLastModified(now - 60000);
+
+ // now write the target file, with lastmod of now.
+ File targetFile2 = new File(base, "file2.xml");
+
+ writeFile(targetFile2);
+
+ targetFile2.setLastModified(now);
+
+ SuffixMapping mapping = new SuffixMapping(".java", ".xml");
+
+ StaleSourceScanner scanner = new StaleSourceScanner();
+
+ scanner.addSourceMapping(mapping);
+
+ Set result = scanner.getIncludedSources(base, base);
+
+ assertEquals("wrong number of stale sources returned.", 1, result.size());
+
+ assertTrue("expected stale source file not found in result", result.contains(sourceFile));
+ }
+
+ // test 6.
+ public void testConstructedWithMsecsShouldReturnOneSourceFileOfTwoDueToLastMod() throws Exception
+ {
+ File base = new File(getTestBaseDir(), "test6");
+
+ long now = System.currentTimeMillis();
+
+ File targetFile = new File(base, "file.xml");
+
+ writeFile(targetFile);
+
+ // should be within the threshold of lastMod for stale sources.
+ targetFile.setLastModified(now - 8000);
+
+ File sourceFile = new File(base, "file.java");
+
+ writeFile(sourceFile);
+
+ // modified 'now' for comparison with the above target file.
+ sourceFile.setLastModified(now);
+
+ File targetFile2 = new File(base, "file2.xml");
+
+ writeFile(targetFile2);
+
+ targetFile2.setLastModified(now - 12000);
+
+ File sourceFile2 = new File(base, "file2.java");
+
+ writeFile(sourceFile2);
+
+ // modified 'now' for comparison to above target file.
+ sourceFile2.setLastModified(now);
+
+ SuffixMapping mapping = new SuffixMapping(".java", ".xml");
+
+ StaleSourceScanner scanner = new StaleSourceScanner(10000);
+
+ scanner.addSourceMapping(mapping);
+
+ Set result = scanner.getIncludedSources(base, base);
+
+ assertEquals("wrong number of stale sources returned.", 1, result.size());
+
+ assertTrue("expected stale source file not found in result", result.contains(sourceFile2));
+
+ assertFalse("expected stale source file not found in result", result.contains(sourceFile));
+ }
+
+ // test 7.
+ public void testConstructedWithMsecsIncludesAndExcludesShouldReturnOneSourceFileOfThreeDueToIncludePattern() throws Exception
+ {
+ File base = new File(getTestBaseDir(), "test7");
+
+ long now = System.currentTimeMillis();
+
+ File targetFile = new File(base, "file.xml");
+
+ writeFile(targetFile);
+
+ // should be within the threshold of lastMod for stale sources.
+ targetFile.setLastModified(now - 12000);
+
+ File sourceFile = new File(base, "file.java");
+
+ writeFile(sourceFile);
+
+ // modified 'now' for comparison with the above target file.
+ sourceFile.setLastModified(now);
+
+ File targetFile2 = new File(base, "file2.xml");
+
+ writeFile(targetFile2);
+
+ targetFile2.setLastModified(now - 12000);
+
+ File sourceFile2 = new File(base, "file2.java");
+
+ writeFile(sourceFile2);
+
+ // modified 'now' for comparison to above target file.
+ sourceFile2.setLastModified(now);
+
+ File targetFile3 = new File(base, "file3.xml");
+
+ writeFile(targetFile3);
+
+ targetFile3.setLastModified(now - 12000);
+
+ File sourceFile3 = new File(base, "file3.java");
+
+ writeFile(sourceFile3);
+
+ // modified 'now' for comparison to above target file.
+ sourceFile3.setLastModified(now);
+
+ SuffixMapping mapping = new SuffixMapping(".java", ".xml");
+
+ StaleSourceScanner scanner = new StaleSourceScanner(0, Collections.singleton("*3.java"), Collections.EMPTY_SET);
+
+ scanner.addSourceMapping(mapping);
+
+ Set result = scanner.getIncludedSources(base, base);
+
+ assertEquals("wrong number of stale sources returned.", 1, result.size());
+
+ assertFalse("expected stale source file not found in result", result.contains(sourceFile));
+
+ assertFalse("unexpected stale source file found in result", result.contains(sourceFile2));
+
+ assertTrue("unexpected stale source file found in result", result.contains(sourceFile3));
+ }
+
+ // test 8.
+ public void testConstructedWithMsecsIncludesAndExcludesShouldReturnTwoSourceFilesOfThreeDueToExcludePattern() throws Exception
+ {
+ File base = new File(getTestBaseDir(), "test8");
+
+ long now = System.currentTimeMillis();
+
+ File targetFile = new File(base, "fileX.xml");
+
+ writeFile(targetFile);
+
+ // should be within the threshold of lastMod for stale sources.
+ targetFile.setLastModified(now - 12000);
+
+ File sourceFile = new File(base, "fileX.java");
+
+ writeFile(sourceFile);
+
+ // modified 'now' for comparison with the above target file.
+ sourceFile.setLastModified(now);
+
+ File targetFile2 = new File(base, "file2.xml");
+
+ writeFile(targetFile2);
+
+ targetFile2.setLastModified(now - 12000);
+
+ File sourceFile2 = new File(base, "file2.java");
+
+ writeFile(sourceFile2);
+
+ // modified 'now' for comparison to above target file.
+ sourceFile2.setLastModified(now);
+
+ File targetFile3 = new File(base, "file3.xml");
+
+ writeFile(targetFile3);
+
+ targetFile3.setLastModified(now - 12000);
+
+ File sourceFile3 = new File(base, "file3.java");
+
+ writeFile(sourceFile3);
+
+ // modified 'now' for comparison to above target file.
+ sourceFile3.setLastModified(now);
+
+ SuffixMapping mapping = new SuffixMapping(".java", ".xml");
+
+ StaleSourceScanner scanner = new StaleSourceScanner(0, Collections.singleton("**/*"), Collections.singleton("*X.*"));
+
+ scanner.addSourceMapping(mapping);
+
+ Set result = scanner.getIncludedSources(base, base);
+
+ assertEquals("wrong number of stale sources returned.", 2, result.size());
+
+ assertFalse("unexpected stale source file found in result", result.contains(sourceFile));
+
+ assertTrue("expected stale source not file found in result", result.contains(sourceFile2));
+
+ assertTrue("expected stale source not file found in result", result.contains(sourceFile3));
+ }
+
+ private File getTestBaseDir()
+ {
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();
+ URL markerResource = cl.getResource(TESTFILE_DEST_MARKER_FILE);
+
+ File basedir = null;
+ if(markerResource != null)
+ {
+ File marker = new File(markerResource.getPath());
+ basedir = marker.getParentFile().getAbsoluteFile();
+ }
+ else
+ {
+ // punt.
+ System.out.println("Cannot find marker file: \'" + TESTFILE_DEST_MARKER_FILE + "\' in classpath. Using '.' for basedir.");
+ basedir = new File(".").getAbsoluteFile();
+ }
+
+ return basedir;
+ }
+
+ private void writeFile(File file) throws IOException
+ {
+ FileWriter fWriter = null;
+ try
+ {
+ File parent = file.getParentFile();
+ if(!parent.exists())
+ {
+ parent.mkdirs();
+ }
+
+ file.deleteOnExit();
+
+ fWriter = new FileWriter(file);
+ fWriter.write("This is just a test file.");
+ }
+ finally
+ {
+ IOUtil.close(fWriter);
+ }
+ }
+
+}
diff --git a/maven-plugins/maven-compiler-plugin/src/main/java/org/apache/maven/plugin/CompilerMojo.java b/maven-plugins/maven-compiler-plugin/src/main/java/org/apache/maven/plugin/CompilerMojo.java
index 6ee5a5cbd1..394168539b 100644
--- a/maven-plugins/maven-compiler-plugin/src/main/java/org/apache/maven/plugin/CompilerMojo.java
+++ b/maven-plugins/maven-compiler-plugin/src/main/java/org/apache/maven/plugin/CompilerMojo.java
@@ -2,20 +2,22 @@
/*
* Copyright 2001-2005 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.
+ *
+ * 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.
*/
+import org.apache.maven.plugin.util.scan.InclusionScanException;
+import org.apache.maven.plugin.util.scan.SourceInclusionScanner;
+import org.apache.maven.plugin.util.scan.StaleSourceScanner;
+import org.apache.maven.plugin.util.scan.mapping.SuffixMapping;
import org.codehaus.plexus.compiler.Compiler;
import org.codehaus.plexus.compiler.CompilerConfiguration;
import org.codehaus.plexus.compiler.CompilerError;
@@ -23,42 +25,35 @@
import java.io.File;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import java.util.Set;
/**
- * @author Jason van Zyl
+ * @author Jason van Zyl
* @version $Id$
* @goal compile
* @requiresDependencyResolution
* @description Compiles application sources
- * @parameter name="compileSourceRoots"
- * type="java.util.List"
- * required="true"
- * validator=""
- * expression="#project.compileSourceRoots"
- * description=""
- * @parameter name="outputDirectory"
- * type="String"
- * required="true"
- * validator=""
- * expression="#project.build.outputDirectory"
- * description=""
- * @parameter name="classpathElements"
- * type="List"
- * required="true"
- * validator=""
- * expression="#project.compileClasspathElements"
- * description=""
- * @parameter name="debug"
- * type="boolean"
- * required="false"
- * validator=""
- * expression="#maven.compiler.debug"
- * description="Whether to include debugging information in the compiled class files; the default value is false"
+ * @parameter name="compileSourceRoots" type="java.util.List" required="true" validator=""
+ * expression="#project.compileSourceRoots" description=""
+ * @parameter name="outputDirectory" type="String" required="true" validator=""
+ * expression="#project.build.outputDirectory" description=""
+ * @parameter name="classpathElements" type="List" required="true" validator=""
+ * expression="#project.compileClasspathElements" description=""
+ * @parameter name="debug" type="boolean" required="false" validator=""
+ * expression="#maven.compiler.debug" description="Whether to include debugging
+ * information in the compiled class files; the default value is false"
* @todo change debug parameter type to Boolean
- * @parameter name="source" type="String" required="false" expression="#source" validator="" description="The -source argument for the Java compiler"
- * @parameter name="target" type="String" required="false" expression="#target" validator="" description="The -target argument for the Java compiler"
+ * @parameter name="source" type="String" required="false" expression="#source" validator=""
+ * description="The -source argument for the Java compiler"
+ * @parameter name="target" type="String" required="false" expression="#target" validator=""
+ * description="The -target argument for the Java compiler"
+ * @parameter name="staleMillis" type="long" required="false" expression="#lastModGranularityMs"
+ * validator="" description="The granularity in milliseconds of the last modification
+ * date for testing whether a source needs recompilation"
+ * @todo change staleMillis parameter type to Long
*/
public class CompilerMojo
@@ -79,6 +74,9 @@ public class CompilerMojo
private String target;
+ // TODO: Use long when supported
+ private String staleMillis = "0";
+
public void execute()
throws PluginExecutionException
{
@@ -99,11 +97,18 @@ public void execute()
compilerConfiguration.setClasspathEntries( classpathElements );
compilerConfiguration.setSourceLocations( compileSourceRoots );
+ Set staleSources = computeStaleSources();
+
+ if ( staleSources != null && !staleSources.isEmpty() )
+ {
+ compilerConfiguration.setSourceFiles( staleSources );
+ }
+
if ( source != null )
{
compilerConfiguration.addCompilerOption( "-source", source );
}
-
+
if ( target != null )
{
compilerConfiguration.addCompilerOption( "-target", target );
@@ -143,8 +148,55 @@ public void execute()
}
}
+ private Set computeStaleSources()
+ throws PluginExecutionException
+ {
+ long staleTime = 0;
+
+ if ( staleMillis != null && staleMillis.length() > 0 )
+ {
+ try
+ {
+ staleTime = Long.parseLong( staleMillis );
+ }
+ catch ( NumberFormatException e )
+ {
+ throw new PluginExecutionException( "Invalid staleMillis plugin parameter value: \'" + staleMillis
+ + "\'", e );
+ }
+
+ }
+ SuffixMapping mapping = new SuffixMapping( ".java", ".class" );
+
+ SourceInclusionScanner scanner = new StaleSourceScanner( staleTime );
+
+ File outDir = new File( outputDirectory );
+
+ Set staleSources = new HashSet();
+
+ for ( Iterator it = compileSourceRoots.iterator(); it.hasNext(); )
+ {
+ String sourceRoot = (String) it.next();
+
+ File rootFile = new File( sourceRoot );
+
+ try
+ {
+ staleSources.addAll( scanner.getIncludedSources( rootFile, outDir ) );
+ }
+ catch ( InclusionScanException e )
+ {
+ throw new PluginExecutionException( "Error scanning source root: \'" + sourceRoot
+ + "\' for stale files to recompile.", e );
+ }
+ }
+
+ return staleSources;
+ }
+
/**
- * @todo also in ant plugin. This should be resolved at some point so that it does not need to be calculated continuously - or should the plugins accept empty source roots as is?
+ * @todo also in ant plugin. This should be resolved at some point so that it does not need to
+ * be calculated continuously - or should the plugins accept empty source roots as is?
*/
private static List removeEmptyCompileSourceRoots( List compileSourceRootsList )
{
@@ -163,4 +215,4 @@ private static List removeEmptyCompileSourceRoots( List compileSourceRootsList )
}
return newCompileSourceRootsList;
}
-}
+}
\ No newline at end of file