diff --git a/license-maven-plugin-git/src/main/java/com/mycila/maven/plugin/license/git/CopyrightAuthorProvider.java b/license-maven-plugin-git/src/main/java/com/mycila/maven/plugin/license/git/CopyrightAuthorProvider.java index 6d6f4c7be..035f43095 100644 --- a/license-maven-plugin-git/src/main/java/com/mycila/maven/plugin/license/git/CopyrightAuthorProvider.java +++ b/license-maven-plugin-git/src/main/java/com/mycila/maven/plugin/license/git/CopyrightAuthorProvider.java @@ -18,26 +18,42 @@ import com.mycila.maven.plugin.license.AbstractLicenseMojo; import com.mycila.maven.plugin.license.PropertiesProvider; import com.mycila.maven.plugin.license.document.Document; + +import java.io.IOException; +import java.io.UncheckedIOException; import java.util.Collections; import java.util.HashMap; import java.util.Map; -import java.util.Properties; /** * An implementation of {@link PropertiesProvider} that adds {@value #COPYRIGHT_CREATION_AUTHOR_NAME_KEY} and * {@value #COPYRIGHT_CREATION_AUTHOR_EMAIL_KEY} values - see - * {@link #getAdditionalProperties(AbstractLicenseMojo, Properties, Document)}. + * {@link #adjustProperties(AbstractLicenseMojo, Map, Document)}. * * @author masakimu */ -public class CopyrightAuthorProvider extends GitPropertiesProvider implements PropertiesProvider { +public class CopyrightAuthorProvider implements PropertiesProvider { public static final String COPYRIGHT_CREATION_AUTHOR_NAME_KEY = "license.git.CreationAuthorName"; public static final String COPYRIGHT_CREATION_AUTHOR_EMAIL_KEY = "license.git.CreationAuthorEmail"; + private GitLookup gitLookup; + + @Override + public void init(AbstractLicenseMojo mojo, Map currentProperties) { + gitLookup = GitLookup.create(mojo.defaultBasedir, currentProperties); + + // One-time warning for shallow repo + if (mojo.warnIfShallow && gitLookup.isShallowRepository()) { + mojo.warn("Shallow git repository detected. Author property values may not be accurate."); + } + } - public CopyrightAuthorProvider() { - super(); + @Override + public void close() { + if (gitLookup != null) { + gitLookup.close(); + } } /** @@ -49,21 +65,15 @@ public CopyrightAuthorProvider() { *
  • {@value #COPYRIGHT_CREATION_AUTHOR_EMAIL_KEY} key stores the author's email address of the first git commit. * */ - public Map getAdditionalProperties(AbstractLicenseMojo mojo, Properties properties, - Document document) { - + @Override + public Map adjustProperties(AbstractLicenseMojo mojo, Map properties, Document document) { try { Map result = new HashMap<>(3); - GitLookup gitLookup = getGitLookup(mojo, document.getFile(), properties); - result.put(COPYRIGHT_CREATION_AUTHOR_NAME_KEY, gitLookup.getAuthorNameOfCreation(document.getFile())); result.put(COPYRIGHT_CREATION_AUTHOR_EMAIL_KEY, gitLookup.getAuthorEmailOfCreation(document.getFile())); return Collections.unmodifiableMap(result); - } catch (Exception e) { - throw new RuntimeException("Could not compute the year of the last git commit for file " - + document.getFile().getAbsolutePath(), e); + } catch (IOException e) { + throw new UncheckedIOException("CopyrightAuthorProvider error on file: " + document.getFile().getAbsolutePath() + ": " + e.getMessage(), e); } } - - } diff --git a/license-maven-plugin-git/src/main/java/com/mycila/maven/plugin/license/git/CopyrightRangeProvider.java b/license-maven-plugin-git/src/main/java/com/mycila/maven/plugin/license/git/CopyrightRangeProvider.java index 1223b8909..f69be5b11 100644 --- a/license-maven-plugin-git/src/main/java/com/mycila/maven/plugin/license/git/CopyrightRangeProvider.java +++ b/license-maven-plugin-git/src/main/java/com/mycila/maven/plugin/license/git/CopyrightRangeProvider.java @@ -18,19 +18,21 @@ import com.mycila.maven.plugin.license.AbstractLicenseMojo; import com.mycila.maven.plugin.license.PropertiesProvider; import com.mycila.maven.plugin.license.document.Document; +import org.eclipse.jgit.api.errors.GitAPIException; + +import java.io.IOException; import java.util.Collections; import java.util.HashMap; import java.util.Map; -import java.util.Properties; /** * An implementation of {@link PropertiesProvider} that adds {@value #COPYRIGHT_LAST_YEAR_KEY} and * {@value #COPYRIGHT_YEARS_KEY} values - see - * {@link #getAdditionalProperties(AbstractLicenseMojo, Properties, Document)}. + * {@link #adjustProperties(AbstractLicenseMojo, Map, Document)}. * * @author Peter Palaga */ -public class CopyrightRangeProvider extends GitPropertiesProvider implements PropertiesProvider { +public class CopyrightRangeProvider implements PropertiesProvider { public static final String COPYRIGHT_LAST_YEAR_KEY = "license.git.copyrightLastYear"; public static final String COPYRIGHT_CREATION_YEAR_KEY = "license.git.copyrightCreationYear"; @@ -38,9 +40,23 @@ public class CopyrightRangeProvider extends GitPropertiesProvider implements Pro public static final String COPYRIGHT_YEARS_KEY = "license.git.copyrightYears"; public static final String INCEPTION_YEAR_KEY = "project.inceptionYear"; + private GitLookup gitLookup; + + @Override + public void init(AbstractLicenseMojo mojo, Map currentProperties) { + gitLookup = GitLookup.create(mojo.defaultBasedir, currentProperties); - public CopyrightRangeProvider() { - super(); + // One-time warning for shallow repo + if (mojo.warnIfShallow && gitLookup.isShallowRepository()) { + mojo.warn("Shallow git repository detected. Year property values may not be accurate."); + } + } + + @Override + public void close() { + if (gitLookup != null) { + gitLookup.close(); + } } /** @@ -54,16 +70,16 @@ public CopyrightRangeProvider() { * returned; otherwise, the two values are combined using dash, so that the result is e.g. {@code "2000-2010"}.
  • *
  • {@value #COPYRIGHT_CREATION_YEAR_KEY} key stores the year from the committer date of the first git commit for * the supplied {@code document}.
  • - *
  • {@value #COPYRIGHT_EXISTENCE_YEARS_KEY} key stores the range from {@value #COPYRIGHT_CREATION_YEAR_KEY} value to + *
  • {@value #COPYRIGHT_EXISTENCE_YEARS_KEY} key stores the range from {@value #COPYRIGHT_CREATION_YEAR_KEY} value to * {@value #COPYRIGHT_LAST_YEAR_KEY} value. If both values are equal only the {@value #COPYRIGHT_CREATION_YEAR_KEY} is returned; * otherwise, the two values are combined using dash, so that the result is e.g. {@code "2005-2010"}.
  • * * The {@value #INCEPTION_YEAR_KEY} value is read from the supplied properties and it must available. Otherwise a * {@link RuntimeException} is thrown. */ - public Map getAdditionalProperties(AbstractLicenseMojo mojo, Properties properties, - Document document) { - String inceptionYear = properties.getProperty(INCEPTION_YEAR_KEY); + @Override + public Map adjustProperties(AbstractLicenseMojo mojo, Map properties, Document document) { + String inceptionYear = properties.get(INCEPTION_YEAR_KEY); if (inceptionYear == null) { throw new RuntimeException("'" + INCEPTION_YEAR_KEY + "' must have a value for file " + document.getFile().getAbsolutePath()); @@ -77,7 +93,7 @@ public Map getAdditionalProperties(AbstractLicenseMojo mojo, Pro } try { Map result = new HashMap<>(4); - GitLookup gitLookup = getGitLookup(mojo, document.getFile(), properties); + int copyrightEnd = gitLookup.getYearOfLastChange(document.getFile()); result.put(COPYRIGHT_LAST_YEAR_KEY, Integer.toString(copyrightEnd)); final String copyrightYears; @@ -90,19 +106,18 @@ public Map getAdditionalProperties(AbstractLicenseMojo mojo, Pro int copyrightStart = gitLookup.getYearOfCreation(document.getFile()); result.put(COPYRIGHT_CREATION_YEAR_KEY, Integer.toString(copyrightStart)); - + final String copyrightExistenceYears; if (copyrightStart >= copyrightEnd) { - copyrightExistenceYears = Integer.toString(copyrightStart); + copyrightExistenceYears = Integer.toString(copyrightStart); } else { - copyrightExistenceYears = copyrightStart + "-" + copyrightEnd; + copyrightExistenceYears = copyrightStart + "-" + copyrightEnd; } result.put(COPYRIGHT_EXISTENCE_YEARS_KEY, copyrightExistenceYears); - + return Collections.unmodifiableMap(result); - } catch (Exception e) { - throw new RuntimeException("Could not compute the year of the last git commit for file " - + document.getFile().getAbsolutePath(), e); + } catch (IOException | GitAPIException e) { + throw new RuntimeException("CopyrightRangeProvider error on file: " + document.getFile().getAbsolutePath() + ": " + e.getMessage(), e); } } } diff --git a/license-maven-plugin-git/src/main/java/com/mycila/maven/plugin/license/git/GitLookup.java b/license-maven-plugin-git/src/main/java/com/mycila/maven/plugin/license/git/GitLookup.java index 7c05097d6..5cac8a138 100644 --- a/license-maven-plugin-git/src/main/java/com/mycila/maven/plugin/license/git/GitLookup.java +++ b/license-maven-plugin-git/src/main/java/com/mycila/maven/plugin/license/git/GitLookup.java @@ -15,13 +15,6 @@ */ package com.mycila.maven.plugin.license.git; -import java.io.File; -import java.io.IOException; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Date; -import java.util.Iterator; -import java.util.TimeZone; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.Status; import org.eclipse.jgit.api.errors.GitAPIException; @@ -39,14 +32,32 @@ import org.eclipse.jgit.treewalk.filter.PathFilter; import org.eclipse.jgit.treewalk.filter.TreeFilter; +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Date; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.TimeZone; + /** * A jGit library wrapper to query the date of the last commit. * * @author Peter Palaga */ -public class GitLookup { +public class GitLookup implements Closeable { public static final TimeZone DEFAULT_ZONE = TimeZone.getTimeZone("GMT"); + public static final String MAX_COMMITS_LOOKUP_KEY = "license.git.maxCommitsLookup"; + // keep for compatibility + private static final String COPYRIGHT_LAST_YEAR_MAX_COMMITS_LOOKUP_KEY = "license.git.copyrightLastYearMaxCommitsLookup"; + public static final String COPYRIGHT_LAST_YEAR_SOURCE_KEY = "license.git.copyrightLastYearSource"; + public static final String COPYRIGHT_LAST_YEAR_TIME_ZONE_KEY = "license.git.copyrightLastYearTimeZone"; + public enum DateSource { AUTHOR, COMMITER } @@ -58,6 +69,46 @@ public enum DateSource { private final TimeZone timeZone; private final boolean shallow; + /** + * Lazily initializes #gitLookup assuming that all subsequent calls to this method will be related to the same + * git repository. + */ + public static GitLookup create(File file, Map props) { + String dateSourceString = props.get(COPYRIGHT_LAST_YEAR_SOURCE_KEY); + if (dateSourceString == null) { + dateSourceString = GitLookup.DateSource.AUTHOR.name(); + } + GitLookup.DateSource dateSource = GitLookup.DateSource.valueOf(dateSourceString.toUpperCase(Locale.US)); + String checkCommitsCountString = props.get(MAX_COMMITS_LOOKUP_KEY); + // Backwads compatibility + if (checkCommitsCountString == null) { + checkCommitsCountString = props.get(COPYRIGHT_LAST_YEAR_MAX_COMMITS_LOOKUP_KEY); + } + int checkCommitsCount = Integer.MAX_VALUE; + if (checkCommitsCountString != null) { + checkCommitsCountString = checkCommitsCountString.trim(); + checkCommitsCount = Integer.parseInt(checkCommitsCountString); + } + final TimeZone timeZone; + String tzString = props.get(COPYRIGHT_LAST_YEAR_TIME_ZONE_KEY); + switch (dateSource) { + case COMMITER: + timeZone = tzString == null ? GitLookup.DEFAULT_ZONE : TimeZone.getTimeZone(tzString); + break; + case AUTHOR: + if (tzString != null) { + throw new RuntimeException(COPYRIGHT_LAST_YEAR_TIME_ZONE_KEY + " must not be set with " + + COPYRIGHT_LAST_YEAR_SOURCE_KEY + " = " + GitLookup.DateSource.AUTHOR.name() + + " because git author name already contains time zone information."); + } + timeZone = null; + break; + default: + throw new IllegalStateException("Unexpected " + GitLookup.DateSource.class.getName() + " " + dateSource); + } + return new GitLookup(file, dateSource, timeZone, checkCommitsCount); + } + /** * Creates a new {@link GitLookup} for a repository that is detected from the supplied {@code anyFile}. *

    @@ -70,30 +121,37 @@ public enum DateSource { * @param checkCommitsCount * @throws IOException */ - public GitLookup(File anyFile, DateSource dateSource, TimeZone timeZone, int checkCommitsCount) throws IOException { - super(); - this.repository = new FileRepositoryBuilder().findGitDir(anyFile).build(); - /* A workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=457961 */ - // Also contains contents of .git/shallow and can detect shallow repo - this.shallow = !this.repository.getObjectDatabase().newReader().getShallowCommits().isEmpty(); - - this.pathResolver = new GitPathResolver(repository.getWorkTree().getAbsolutePath()); - this.dateSource = dateSource; - switch (dateSource) { - case COMMITER: - this.timeZone = timeZone == null ? DEFAULT_ZONE : timeZone; - break; - case AUTHOR: - if (timeZone != null) { - throw new IllegalArgumentException("Time zone must be null with dateSource " + DateSource.AUTHOR.name() - + " because git author name already contrains time zone information."); - } - this.timeZone = null; - break; - default: - throw new IllegalStateException("Unexpected " + DateSource.class.getName() + " " + dateSource); + private GitLookup(File anyFile, DateSource dateSource, TimeZone timeZone, int checkCommitsCount) { + try { + this.repository = new FileRepositoryBuilder().findGitDir(anyFile).build(); + /* A workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=457961 */ + // Also contains contents of .git/shallow and can detect shallow repo + // the line below reads and caches the entries in the FileObjectDatabase of the repository to + // avoid concurrent modifications during RevWalk + // Closing the repository will close the FileObjectDatabase. + // Here the newReader() is a WindowCursor which delegates the getShallowCommits() to the FileObjectDatabase. + this.shallow = !this.repository.getObjectDatabase().newReader().getShallowCommits().isEmpty(); + + this.pathResolver = new GitPathResolver(repository.getWorkTree().getAbsolutePath()); + this.dateSource = dateSource; + switch (dateSource) { + case COMMITER: + this.timeZone = timeZone == null ? DEFAULT_ZONE : timeZone; + break; + case AUTHOR: + if (timeZone != null) { + throw new IllegalArgumentException("Time zone must be null with dateSource " + DateSource.AUTHOR.name() + + " because git author name already contains time zone information."); + } + this.timeZone = null; + break; + default: + throw new IllegalStateException("Unexpected " + DateSource.class.getName() + " " + dateSource); + } + this.checkCommitsCount = checkCommitsCount; + } catch (IOException e) { + throw new UncheckedIOException(e); } - this.checkCommitsCount = checkCommitsCount; } /** @@ -148,12 +206,12 @@ int getYearOfCreation(File file) throws IOException, GitAPIException { commitYear = getYearFromCommit(commit); } walk.dispose(); - + // If we couldn't find a creation year from Git assume newly created file if (commitYear == 0) { - return getCurrentYear(); - } - + return getCurrentYear(); + } + return commitYear; } @@ -182,7 +240,7 @@ String getAuthorEmailOfCreation(File file) throws IOException { walk.dispose(); return authorEmail; } - + boolean isShallowRepository() { return this.shallow; } @@ -245,4 +303,9 @@ private String getAuthorEmailFromCommit(RevCommit commit) { PersonIdent id = commit.getAuthorIdent(); return id.getEmailAddress(); } + + @Override + public void close() { + repository.close(); + } } diff --git a/license-maven-plugin-git/src/main/java/com/mycila/maven/plugin/license/git/GitPropertiesProvider.java b/license-maven-plugin-git/src/main/java/com/mycila/maven/plugin/license/git/GitPropertiesProvider.java deleted file mode 100644 index 18a201125..000000000 --- a/license-maven-plugin-git/src/main/java/com/mycila/maven/plugin/license/git/GitPropertiesProvider.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2008-2021 Mycila (mathieu.carbou@gmail.com) - * - * 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. - */ -package com.mycila.maven.plugin.license.git; - -import com.mycila.maven.plugin.license.AbstractLicenseMojo; -import java.io.File; -import java.io.IOException; -import java.util.Locale; -import java.util.Properties; -import java.util.TimeZone; - -/** - * @author Peter Palaga - */ -public class GitPropertiesProvider { - - private GitLookup gitLookup; - public static final String MAX_COMMITS_LOOKUP_KEY = "license.git.maxCommitsLookup"; - // keep for compatibility - private static final String COPYRIGHT_LAST_YEAR_MAX_COMMITS_LOOKUP_KEY = "license.git.copyrightLastYearMaxCommitsLookup"; - public static final String COPYRIGHT_LAST_YEAR_SOURCE_KEY = "license.git.copyrightLastYearSource"; - public static final String COPYRIGHT_LAST_YEAR_TIME_ZONE_KEY = "license.git.copyrightLastYearTimeZone"; - - public GitPropertiesProvider() {} - - ; - - /** - * Lazily initializes #gitLookup assuming that all subsequent calls to this method will be related to the same - * git repository. - * - * @param file - * @return - * @throws IOException - */ - GitLookup getGitLookup(AbstractLicenseMojo mojo, File file, Properties props) throws IOException { - if (gitLookup == null) { - synchronized (this) { - if (gitLookup == null) { - String dateSourceString = props.getProperty(COPYRIGHT_LAST_YEAR_SOURCE_KEY, - GitLookup.DateSource.AUTHOR.name()); - GitLookup.DateSource dateSource = GitLookup.DateSource.valueOf(dateSourceString.toUpperCase(Locale.US)); - String checkCommitsCountString = props.getProperty(MAX_COMMITS_LOOKUP_KEY); - // Backwads compatibility - if (checkCommitsCountString == null) { - checkCommitsCountString = props.getProperty(COPYRIGHT_LAST_YEAR_MAX_COMMITS_LOOKUP_KEY); - } - int checkCommitsCount = Integer.MAX_VALUE; - if (checkCommitsCountString != null) { - checkCommitsCountString = checkCommitsCountString.trim(); - checkCommitsCount = Integer.parseInt(checkCommitsCountString); - } - final TimeZone timeZone; - String tzString = props.getProperty(COPYRIGHT_LAST_YEAR_TIME_ZONE_KEY); - switch (dateSource) { - case COMMITER: - timeZone = tzString == null ? GitLookup.DEFAULT_ZONE : TimeZone.getTimeZone(tzString); - break; - case AUTHOR: - if (tzString != null) { - throw new RuntimeException(COPYRIGHT_LAST_YEAR_TIME_ZONE_KEY + " must not be set with " - + COPYRIGHT_LAST_YEAR_SOURCE_KEY + " = " + GitLookup.DateSource.AUTHOR.name() - + " because git author name already contrains time zone information."); - } - timeZone = null; - break; - default: - throw new IllegalStateException("Unexpected " + GitLookup.DateSource.class.getName() + " " + dateSource); - } - gitLookup = new GitLookup(file, dateSource, timeZone, checkCommitsCount); - // One-time warning for shallow repo - if (mojo.warnIfShallow && gitLookup.isShallowRepository()) { - mojo.warn("Shallow git repository detected. Year and author property values may not be accurate."); - } - } - } - } - return gitLookup; - } -} diff --git a/license-maven-plugin-git/src/test/java/com/mycila/maven/plugin/license/git/CopyrightAuthorProviderTest.java b/license-maven-plugin-git/src/test/java/com/mycila/maven/plugin/license/git/CopyrightAuthorProviderTest.java index 5c7811100..38ec4d6f1 100644 --- a/license-maven-plugin-git/src/test/java/com/mycila/maven/plugin/license/git/CopyrightAuthorProviderTest.java +++ b/license-maven-plugin-git/src/test/java/com/mycila/maven/plugin/license/git/CopyrightAuthorProviderTest.java @@ -17,6 +17,11 @@ import com.mycila.maven.plugin.license.LicenseCheckMojo; import com.mycila.maven.plugin.license.document.Document; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + import java.io.File; import java.io.IOException; import java.net.URL; @@ -24,11 +29,6 @@ import java.nio.file.Paths; import java.util.HashMap; import java.util.Map; -import java.util.Properties; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; /** * @author Peter Palaga @@ -44,19 +44,31 @@ class CopyrightAuthorProviderTest { void copyrightAuthor() { CopyrightAuthorProvider provider = new CopyrightAuthorProvider(); - assertAuthor(provider, "dir1/file1.txt", "Peter Palaga", "ppalaga@redhat.com"); + Map props = new HashMap<>(); + final LicenseCheckMojo mojo = new LicenseCheckMojo(); + mojo.defaultBasedir = gitRepoRoot.toFile(); + + try { + provider.init(mojo, props); + + String path = "dir1/file1.txt"; + + + Document document = newDocument(path); + Map actual = provider.adjustProperties(mojo, props, document); + + HashMap expected = new HashMap(); + expected.put(CopyrightAuthorProvider.COPYRIGHT_CREATION_AUTHOR_NAME_KEY, "Peter Palaga"); + expected.put(CopyrightAuthorProvider.COPYRIGHT_CREATION_AUTHOR_EMAIL_KEY, "ppalaga@redhat.com"); + Assertions.assertEquals(expected, actual, "for file '" + path + "': "); + + } finally { + provider.close(); + } } private void assertAuthor(CopyrightAuthorProvider provider, String path, String copyrightAuthorName, String copyrightAuthorEmail) { - Properties props = new Properties(); - - Document document = newDocument(path); - Map actual = provider.getAdditionalProperties(new LicenseCheckMojo(), props, document); - HashMap expected = new HashMap(); - expected.put(CopyrightAuthorProvider.COPYRIGHT_CREATION_AUTHOR_NAME_KEY, copyrightAuthorName); - expected.put(CopyrightAuthorProvider.COPYRIGHT_CREATION_AUTHOR_EMAIL_KEY, copyrightAuthorEmail); - Assertions.assertEquals(expected, actual, "for file '" + path + "': "); } diff --git a/license-maven-plugin-git/src/test/java/com/mycila/maven/plugin/license/git/CopyrightRangeProviderTest.java b/license-maven-plugin-git/src/test/java/com/mycila/maven/plugin/license/git/CopyrightRangeProviderTest.java index f8bad6e0c..59956e99b 100644 --- a/license-maven-plugin-git/src/test/java/com/mycila/maven/plugin/license/git/CopyrightRangeProviderTest.java +++ b/license-maven-plugin-git/src/test/java/com/mycila/maven/plugin/license/git/CopyrightRangeProviderTest.java @@ -17,6 +17,11 @@ import com.mycila.maven.plugin.license.LicenseCheckMojo; import com.mycila.maven.plugin.license.document.Document; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + import java.io.File; import java.io.IOException; import java.net.URL; @@ -24,11 +29,6 @@ import java.nio.file.Paths; import java.util.HashMap; import java.util.Map; -import java.util.Properties; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; /** * @author Peter Palaga @@ -44,19 +44,28 @@ class CopyrightRangeProviderTest { void copyrightRange() { CopyrightRangeProvider provider = new CopyrightRangeProvider(); - assertRange(provider, "dir1/file1.txt", "2000", "2006", "1999-2006", "2000-2006"); - assertRange(provider, "dir2/file2.txt", "2007", "2007", "1999-2007", "2007"); - assertRange(provider, "dir1/file3.txt", "2009", "2009", "1999-2009", "2009"); - assertRange(provider, "dir2/file4.txt", "1999", "1999", "1999", "1999"); - - /* The last change of file4.txt in git history is in 1999 - * but the inception year is 2000 - * and we do not want the range to go back (2000-1999) - * so in this case we expect just 2000 - * However for existence years always report the actual year regardless - * of the inception year so expect 1999 for that */ - assertRange(provider, "dir2/file4.txt", "2000", "1999", "1999", "2000", "1999"); - + Map props = new HashMap<>(); + final LicenseCheckMojo mojo = new LicenseCheckMojo(); + mojo.defaultBasedir = gitRepoRoot.toFile(); + try { + provider.init(mojo, props); + + assertRange(provider, "dir1/file1.txt", "2000", "2006", "1999-2006", "2000-2006"); + assertRange(provider, "dir2/file2.txt", "2007", "2007", "1999-2007", "2007"); + assertRange(provider, "dir1/file3.txt", "2009", "2009", "1999-2009", "2009"); + assertRange(provider, "dir2/file4.txt", "1999", "1999", "1999", "1999"); + + /* The last change of file4.txt in git history is in 1999 + * but the inception year is 2000 + * and we do not want the range to go back (2000-1999) + * so in this case we expect just 2000 + * However for existence years always report the actual year regardless + * of the inception year so expect 1999 for that */ + assertRange(provider, "dir2/file4.txt", "2000", "1999", "1999", "2000", "1999"); + + } finally { + provider.close(); + } } private void assertRange(CopyrightRangeProvider provider, String path, String copyrightStart, String copyrightEnd, String copyrightRange, String copyrightExistence) { @@ -65,11 +74,11 @@ private void assertRange(CopyrightRangeProvider provider, String path, String co private void assertRange(CopyrightRangeProvider provider, String path, String inceptionYear, String copyrightStart, String copyrightEnd, String copyrightRange, String copyrightExistence) { - Properties props = new Properties(); + Map props = new HashMap<>(); props.put(CopyrightRangeProvider.INCEPTION_YEAR_KEY, inceptionYear); Document document = newDocument(path); - Map actual = provider.getAdditionalProperties(new LicenseCheckMojo(), props, document); + Map actual = provider.adjustProperties(new LicenseCheckMojo(), props, document); HashMap expected = new HashMap(); expected.put(CopyrightRangeProvider.COPYRIGHT_CREATION_YEAR_KEY, copyrightStart); diff --git a/license-maven-plugin-git/src/test/java/com/mycila/maven/plugin/license/git/GitLookupTest.java b/license-maven-plugin-git/src/test/java/com/mycila/maven/plugin/license/git/GitLookupTest.java index 58403207f..4a657f5bc 100644 --- a/license-maven-plugin-git/src/test/java/com/mycila/maven/plugin/license/git/GitLookupTest.java +++ b/license-maven-plugin-git/src/test/java/com/mycila/maven/plugin/license/git/GitLookupTest.java @@ -16,6 +16,12 @@ package com.mycila.maven.plugin.license.git; import com.mycila.maven.plugin.license.git.GitLookup.DateSource; +import org.eclipse.jgit.api.errors.GitAPIException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; @@ -26,14 +32,11 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.Calendar; +import java.util.HashMap; +import java.util.Map; import java.util.TimeZone; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; -import org.eclipse.jgit.api.errors.GitAPIException; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; /** * @author Peter Palaga @@ -155,10 +158,10 @@ void reuseProvider() throws GitAPIException, IOException { @Test void timezone() throws GitAPIException, IOException { try { - new GitLookup(gitRepoRoot.toFile(), DateSource.AUTHOR, TimeZone.getTimeZone("GMT"), 10); + GitLookup.create(gitRepoRoot.toFile(), buildProps(DateSource.AUTHOR, "GMT", "10")); Assertions.fail("RuntimeException expected"); } catch (RuntimeException e) { - if (e.getMessage().startsWith("Time zone must be null with dateSource " + DateSource.AUTHOR.name() + "")) { + if (e.getMessage().contains("license.git.copyrightLastYearTimeZone must not be set with license.git.copyrightLastYearSource = AUTHOR because git author name already contains time zone information.")) { /* expected */ } else { throw e; @@ -166,28 +169,42 @@ void timezone() throws GitAPIException, IOException { } /* null is GMT */ - GitLookup nullTzLookup = new GitLookup(gitRepoRoot.toFile(), DateSource.COMMITER, null, 10); + GitLookup nullTzLookup = GitLookup.create(gitRepoRoot.toFile(), buildProps(DateSource.COMMITER, null, "10")); assertLastChange(nullTzLookup, "dir1/file3.txt", 2010); /* explicit GMT */ - GitLookup gmtLookup = new GitLookup(gitRepoRoot.toFile(), DateSource.COMMITER, TimeZone.getTimeZone("GMT"), 10); + GitLookup gmtLookup = GitLookup.create(gitRepoRoot.toFile(), buildProps(DateSource.COMMITER, "GMT", "10")); assertLastChange(gmtLookup, "dir1/file3.txt", 2010); /* * explicit non-GMT zome. Note that the relevant commit's (GMT) time stamp is 2010-12-31T23:30:00 which yealds * 2011 in the CET (+01:00) time zone */ - GitLookup cetLookup = new GitLookup(gitRepoRoot.toFile(), DateSource.COMMITER, TimeZone.getTimeZone("CET"), 10); + GitLookup cetLookup = GitLookup.create(gitRepoRoot.toFile(), buildProps(DateSource.COMMITER, "CET", "10")); assertLastChange(cetLookup, "dir1/file3.txt", 2011); } - private GitLookup newAuthorLookup() throws IOException { - return new GitLookup(gitRepoRoot.toFile(), DateSource.AUTHOR, null, 10); + private Map buildProps(DateSource ds, String tz, String history) { + Map props = new HashMap<>(); + if (history != null) { + props.put(GitLookup.MAX_COMMITS_LOOKUP_KEY, history); + } + if (tz != null) { + props.put(GitLookup.COPYRIGHT_LAST_YEAR_TIME_ZONE_KEY, tz); + } + if (ds != null) { + props.put(GitLookup.COPYRIGHT_LAST_YEAR_SOURCE_KEY, ds.name()); + } + return props; + } + + private GitLookup newAuthorLookup() { + return GitLookup.create(gitRepoRoot.toFile(), buildProps(DateSource.AUTHOR, null, "10")); } - private GitLookup newCommitterLookup() throws IOException { - return new GitLookup(gitRepoRoot.toFile(), DateSource.COMMITER, null, 10); + private GitLookup newCommitterLookup() { + return GitLookup.create(gitRepoRoot.toFile(), buildProps(DateSource.COMMITER, null, "10")); } private void assertLastChange(GitLookup provider, String relativePath, int expected) throws diff --git a/license-maven-plugin-svn/src/main/java/com/mycila/maven/plugin/license/svn/SVNPropertiesProvider.java b/license-maven-plugin-svn/src/main/java/com/mycila/maven/plugin/license/svn/SVNPropertiesProvider.java index 87b9d600e..7fa97c537 100755 --- a/license-maven-plugin-svn/src/main/java/com/mycila/maven/plugin/license/svn/SVNPropertiesProvider.java +++ b/license-maven-plugin-svn/src/main/java/com/mycila/maven/plugin/license/svn/SVNPropertiesProvider.java @@ -19,12 +19,6 @@ import com.mycila.maven.plugin.license.Credentials; import com.mycila.maven.plugin.license.PropertiesProvider; import com.mycila.maven.plugin.license.document.Document; -import java.io.File; -import java.text.SimpleDateFormat; -import java.util.GregorianCalendar; -import java.util.HashMap; -import java.util.Map; -import java.util.Properties; import org.tmatesoft.svn.core.ISVNLogEntryHandler; import org.tmatesoft.svn.core.SVNDepth; import org.tmatesoft.svn.core.SVNException; @@ -34,6 +28,15 @@ import org.tmatesoft.svn.core.wc.SVNInfo; import org.tmatesoft.svn.core.wc.SVNRevision; +import java.io.File; +import java.text.SimpleDateFormat; +import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.Map; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicBoolean; + /** * An implementation of {@link PropertiesProvider} that uses SVN to retrieve * year information of last modification of files. @@ -41,25 +44,40 @@ * @author Matthieu Brouillard */ public class SVNPropertiesProvider implements PropertiesProvider { - ThreadLocal svnClientThreadLocal = new ThreadLocal<>(); - ThreadLocal sdfTimestampThreadLocal = new ThreadLocal() { - @Override - protected SimpleDateFormat initialValue() { - return new SimpleDateFormat("yyyyMMdd-HH:mm:ss"); - } - }; + public static final String SVN_COPYRIGHT_LASTCHANGE_YEAR_KEY = "license.svn.lastchange.year"; public static final String SVN_COPYRIGHT_LASTCHANGE_DATE_KEY = "license.svn.lastchange.date"; public static final String SVN_COPYRIGHT_LASTCHANGE_TIMESTAMP_KEY = "license.svn.lastchange.timestamp"; public static final String SVN_COPYRIGHT_LASTCHANGE_REVISION_KEY = "license.svn.lastchange.revision"; public static final String SVN_COPYRIGHT_YEARS_RANGE_KEY = "license.svn.years.range"; - public static final String INCEPTION_YEAR_KEY = "project.inceptionYear"; - public static final String SVN_SERVER_ID_PLUGIN_KEY = "license.svn.serverId"; - private volatile boolean initialized = false; + private Credentials svnCredentials; + + private final AtomicBoolean warnedIfShallow = new AtomicBoolean(); + private final Queue clients = new ConcurrentLinkedQueue<>(); + private final ThreadLocal sdfTimestampThreadLocal = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyyMMdd-HH:mm:ss")); + private final ThreadLocal svnClientThreadLocal = ThreadLocal.withInitial(() -> { + SVNClientManager svnClientManager = svnCredentials == null ? + SVNClientManager.newInstance(new DefaultSVNOptions()) : + SVNClientManager.newInstance(new DefaultSVNOptions(), svnCredentials.getLogin(), svnCredentials.getPassword()); + clients.offer(svnClientManager); + return svnClientManager; + }); + + @Override + public void init(AbstractLicenseMojo mojo, Map currentProperties) { + svnCredentials = mojo.findCredentials(currentProperties.get(SVN_SERVER_ID_PLUGIN_KEY)); + } + + @Override + public void close() { + while (!clients.isEmpty()) { + clients.poll().dispose(); + } + } /** * Provides information on the given document. The information is put in the @@ -72,15 +90,10 @@ protected SimpleDateFormat initialValue() { * @return a non null Map containing the added entries */ @Override - public Map getAdditionalProperties(final AbstractLicenseMojo mojo, Properties currentProperties, Document document) { + public Map adjustProperties(final AbstractLicenseMojo mojo, Map currentProperties, Document document) { final Map newProperties = new HashMap<>(); final File documentFile = document.getFile(); - - initThreadLocalObjects(mojo, currentProperties.getProperty(SVN_SERVER_ID_PLUGIN_KEY)); - - SVNClientManager clientManager = svnClientThreadLocal.get(); - - final String inceptionYear = currentProperties.getProperty(INCEPTION_YEAR_KEY); + final SVNClientManager svnClientManager = svnClientThreadLocal.get(); ISVNLogEntryHandler lastChangeDateLogEntryHandler = new ISVNLogEntryHandler() { @Override @@ -90,6 +103,7 @@ public void handleLogEntry(SVNLogEntry logEntry) throws SVNException { final String timestamp = sdfTimestampThreadLocal.get().format(logEntry.getDate()); final String year = timestamp.substring(0, 4); + final String inceptionYear = currentProperties.get(INCEPTION_YEAR_KEY); newProperties.put(SVN_COPYRIGHT_LASTCHANGE_TIMESTAMP_KEY, timestamp); newProperties.put(SVN_COPYRIGHT_LASTCHANGE_DATE_KEY, timestamp.substring(0, 8)); @@ -97,6 +111,7 @@ public void handleLogEntry(SVNLogEntry logEntry) throws SVNException { newProperties.put(SVN_COPYRIGHT_LASTCHANGE_YEAR_KEY, year); mojo.getLog().debug("found " + logEntry.getDate() + " as last modified date for file: " + documentFile); + if (year.equals(inceptionYear) || inceptionYear == null) { newProperties.put(SVN_COPYRIGHT_YEARS_RANGE_KEY, year); } else { @@ -107,21 +122,17 @@ public void handleLogEntry(SVNLogEntry logEntry) throws SVNException { }; try { - if (!this.initialized) { - synchronized (this) { - if (!this.initialized) { - this.initialized = true; - // One-time warning for shallow repo - if (mojo.warnIfShallow) { - SVNInfo info = clientManager.getWCClient().doInfo(documentFile, SVNRevision.HEAD); - if (info.getDepth() != SVNDepth.INFINITY) { - mojo.warn("Sparse svn repository detected. Year and author property values may not be accurate."); - } - } + // One-time warning for shallow repo + if (mojo.warnIfShallow && !warnedIfShallow.get()) { + SVNInfo info = svnClientManager.getWCClient().doInfo(documentFile, SVNRevision.HEAD); + if (info.getDepth() != SVNDepth.INFINITY) { + if (warnedIfShallow.compareAndSet(false, true)) { + mojo.warn("Sparse svn repository detected. Year and author property values may not be accurate."); } } } - clientManager.getLogClient().doLog(new File[]{documentFile}, SVNRevision.HEAD, SVNRevision.create(0), true, true, 1, lastChangeDateLogEntryHandler); + + svnClientManager.getLogClient().doLog(new File[]{documentFile}, SVNRevision.HEAD, SVNRevision.create(0), true, true, 1, lastChangeDateLogEntryHandler); } catch (SVNException ex) { IllegalStateException ise = new IllegalStateException("cannot query SVN latest date information for file: " + documentFile, ex); throw ise; @@ -129,23 +140,4 @@ public void handleLogEntry(SVNLogEntry logEntry) throws SVNException { return newProperties; } - - private void setSVNClientManager(AbstractLicenseMojo mojo, String svnServerID) { - SVNClientManager c = svnClientThreadLocal.get(); - if (c == null) { - Credentials svnCredentials = mojo.findCredentials(svnServerID); - - if (svnCredentials == null) { - c = SVNClientManager.newInstance(new DefaultSVNOptions()); - } else { - c = SVNClientManager.newInstance(new DefaultSVNOptions(), svnCredentials.getLogin(), svnCredentials.getPassword()); - } - - svnClientThreadLocal.set(c); - } - } - - private void initThreadLocalObjects(AbstractLicenseMojo mojo, String serverID) { - setSVNClientManager(mojo, serverID); - } } diff --git a/license-maven-plugin/src/main/java/com/mycila/maven/plugin/license/AbstractLicenseMojo.java b/license-maven-plugin/src/main/java/com/mycila/maven/plugin/license/AbstractLicenseMojo.java index 0009d5b4c..677106675 100755 --- a/license-maven-plugin/src/main/java/com/mycila/maven/plugin/license/AbstractLicenseMojo.java +++ b/license-maven-plugin/src/main/java/com/mycila/maven/plugin/license/AbstractLicenseMojo.java @@ -15,12 +15,6 @@ */ package com.mycila.maven.plugin.license; -import static com.mycila.maven.plugin.license.document.DocumentType.defaultMapping; -import static com.mycila.maven.plugin.license.util.FileUtils.asPath; -import static java.lang.String.format; -import static java.util.Arrays.asList; -import static java.util.Arrays.deepToString; - import com.mycila.maven.plugin.license.dependencies.LicenseMessage; import com.mycila.maven.plugin.license.dependencies.LicensePolicy; import com.mycila.maven.plugin.license.document.Document; @@ -35,44 +29,54 @@ import com.mycila.maven.plugin.license.util.Selection; import com.mycila.maven.plugin.license.util.resource.ResourceFinder; import com.mycila.xmltool.XMLDoc; +import org.apache.maven.artifact.DependencyResolutionRequiredException; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.model.Organization; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.Component; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.ProjectBuilder; +import org.apache.maven.settings.Server; +import org.apache.maven.settings.Settings; +import org.apache.maven.settings.crypto.DefaultSettingsDecryptionRequest; +import org.apache.maven.settings.crypto.SettingsDecrypter; +import org.apache.maven.settings.crypto.SettingsDecryptionRequest; +import org.apache.maven.settings.crypto.SettingsDecryptionResult; +import org.apache.maven.shared.dependency.graph.DependencyGraphBuilder; +import org.xml.sax.InputSource; + import java.io.File; import java.io.IOException; import java.time.Clock; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Properties; +import java.util.Objects; import java.util.ServiceLoader; import java.util.Set; +import java.util.TreeMap; import java.util.concurrent.CompletionService; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import org.apache.maven.artifact.DependencyResolutionRequiredException; -import org.apache.maven.execution.MavenSession; -import org.apache.maven.model.Organization; -import org.apache.maven.plugin.AbstractMojo; -import org.apache.maven.plugin.MojoExecutionException; -import org.apache.maven.plugin.MojoFailureException; -import org.apache.maven.plugins.annotations.Component; -import org.apache.maven.plugins.annotations.Parameter; -import org.apache.maven.project.MavenProject; -import org.apache.maven.project.ProjectBuilder; -import org.apache.maven.settings.Server; -import org.apache.maven.settings.Settings; -import org.apache.maven.settings.crypto.DefaultSettingsDecryptionRequest; -import org.apache.maven.settings.crypto.SettingsDecrypter; -import org.apache.maven.settings.crypto.SettingsDecryptionRequest; -import org.apache.maven.settings.crypto.SettingsDecryptionResult; -import org.apache.maven.shared.dependency.graph.DependencyGraphBuilder; -import org.xml.sax.InputSource; +import java.util.stream.Collectors; + +import static com.mycila.maven.plugin.license.document.DocumentType.defaultMapping; +import static com.mycila.maven.plugin.license.util.FileUtils.asPath; +import static java.lang.String.format; +import static java.util.Arrays.asList; +import static java.util.Arrays.deepToString; /** * Date: 18-Feb-2008
    Author: Mathieu Carbou @@ -303,7 +307,7 @@ public abstract class AbstractLicenseMojo extends AbstractMojo { */ @Parameter(property = "license.warnIfShallow", defaultValue = "true") public boolean warnIfShallow = true; - + /** * If you do not want to see the list of file having a missing header, you * can add the quiet flag that will shorten the output @@ -587,52 +591,72 @@ private void executeForLicenseSet(final LicenseSet licenseSet, final Callback ca validHeaders.add(new Header(validHeaderSource, licenseSet.headerSections)); } - final List propertiesProviders = new LinkedList(); - for (final PropertiesProvider provider : ServiceLoader.load(PropertiesProvider.class, Thread.currentThread().getContextClassLoader())) { - propertiesProviders.add(provider); - } - final DocumentPropertiesLoader propertiesLoader = new DocumentPropertiesLoader() { - @Override - public Properties load(final Document document) { - final Properties props = new Properties(); + Map globalProperties = getDefaultProperties(); - for (final Map.Entry entry : mergeProperties(licenseSet, document).entrySet()) { - if (entry.getValue() != null) { - props.setProperty(entry.getKey(), entry.getValue()); - } else { - props.remove(entry.getKey()); - } - } - for (final PropertiesProvider provider : propertiesProviders) { - try { - final Map providerProperties = provider.getAdditionalProperties(AbstractLicenseMojo.this, props, document); - if (getLog().isDebugEnabled()) { - getLog().debug("provider: " + provider.getClass() + " brought new properties\n" + providerProperties); - } - for (Map.Entry entry : providerProperties.entrySet()) { - if (entry.getValue() != null) { - props.setProperty(entry.getKey(), entry.getValue()); - } else { - props.remove(entry.getKey()); - } - } - } catch (Exception e) { - getLog().warn("failure occurred while calling " + provider.getClass(), e); - } + // we override by properties in the licenseSet + if (licenseSet.properties != null) { + for (Map.Entry entry : licenseSet.properties.entrySet()) { + if (!System.getProperties().contains(entry.getKey())) { + globalProperties.put(entry.getKey(), entry.getValue()); } - return props; } - }; + } - final DocumentFactory documentFactory = new DocumentFactory(firstNonNull(licenseSet.basedir, defaultBasedir), buildMapping(), buildHeaderDefinitions(licenseSet, finder), encoding, licenseSet.keywords, propertiesLoader); + if (getLog().isDebugEnabled()) { + getLog().debug("global properties:\n - " + globalProperties.entrySet().stream().map(Objects::toString).collect(Collectors.joining("\n - "))); + } + final List propertiesProviders = new LinkedList<>(); int nThreads = getNumberOfExecutorThreads(); ExecutorService executorService = Executors.newFixedThreadPool(nThreads); - CompletionService completionService = new ExecutorCompletionService<>(executorService); - int count = 0; - debug("Number of execution threads: %s", nThreads); try { + + for (final PropertiesProvider provider : ServiceLoader.load(PropertiesProvider.class, Thread.currentThread().getContextClassLoader())) { + propertiesProviders.add(provider); + } + + final DocumentPropertiesLoader perDocumentProperties = new DocumentPropertiesLoader() { + @Override + public Map load(final Document document) { + // then add per document properties + Map perDoc = new LinkedHashMap<>(globalProperties); + perDoc.put("file.name", document.getFile().getName()); + + Map readOnly = Collections.unmodifiableMap(perDoc); + + for (final PropertiesProvider provider : propertiesProviders) { + try { + final Map adjustments = provider.adjustProperties(AbstractLicenseMojo.this, readOnly, document); + if (getLog().isDebugEnabled()) { + getLog().debug("provider: " + provider.getClass() + " adjusted these properties:\n" + adjustments); + } + for (Map.Entry entry : adjustments.entrySet()) { + if (entry.getValue() != null) { + perDoc.put(entry.getKey(), entry.getValue()); + } else { + perDoc.remove(entry.getKey()); + } + } + } catch (Exception e) { + getLog().warn("failure occurred while calling " + provider.getClass(), e); + } + } + + if (getLog().isDebugEnabled()) { + getLog().debug("properties for " + document + ":\n - " + perDoc.entrySet().stream().map(Objects::toString).collect(Collectors.joining("\n - "))); + } + + return perDoc; + } + }; + + final DocumentFactory documentFactory = new DocumentFactory(firstNonNull(licenseSet.basedir, defaultBasedir), buildMapping(), buildHeaderDefinitions(licenseSet, finder), encoding, licenseSet.keywords, perDocumentProperties); + + CompletionService completionService = new ExecutorCompletionService<>(executorService); + int count = 0; + debug("Number of execution threads: %s", nThreads); + for (final String file : listSelectedFiles(licenseSet)) { completionService.submit(() -> { Document document = documentFactory.createDocuments(file); @@ -685,6 +709,7 @@ public Properties load(final Document document) { } finally { executorService.shutdownNow(); + propertiesProviders.forEach(PropertiesProvider::close); } } @@ -702,9 +727,9 @@ private int getNumberOfExecutorThreads() { Math.max(1, (int) (Runtime.getRuntime().availableProcessors() * concurrencyFactor)); } - private Map mergeProperties(final LicenseSet licenseSet, final Document document) { + private Map getDefaultProperties() { // first put system environment - Map props = new LinkedHashMap(System.getenv()); + Map props = new TreeMap<>(System.getenv()); // treemap just to have nice debug logs // then add ${project.XYZ} properties props.put("project.groupId", project.getGroupId()); props.put("project.artifactId", project.getArtifactId()); @@ -720,23 +745,17 @@ private Map mergeProperties(final LicenseSet licenseSet, final D props.put("project.organization.name", org.getName()); props.put("project.organization.url", org.getUrl()); } - // then add per document properties - props.put("file.name", document.getFile().getName()); // we override by properties in the POM if (this.defaultProperties != null) { props.putAll(this.defaultProperties); } - // we override by properties in the licenseSet - if (licenseSet.properties != null) { - props.putAll(licenseSet.properties); - } - // then we override by java system properties (command-line -D...) for (String key : System.getProperties().stringPropertyNames()) { props.put(key, System.getProperty(key)); } + return props; } @@ -858,6 +877,10 @@ List getDecryptedServers() { * @return */ public Credentials findCredentials(String serverID) { + if (serverID == null) { + return null; + } + List decryptedServers = getDecryptedServers(); for (Server ds : decryptedServers) { diff --git a/license-maven-plugin/src/main/java/com/mycila/maven/plugin/license/PropertiesProvider.java b/license-maven-plugin/src/main/java/com/mycila/maven/plugin/license/PropertiesProvider.java index ae81f54fd..528bce802 100755 --- a/license-maven-plugin/src/main/java/com/mycila/maven/plugin/license/PropertiesProvider.java +++ b/license-maven-plugin/src/main/java/com/mycila/maven/plugin/license/PropertiesProvider.java @@ -16,6 +16,9 @@ package com.mycila.maven.plugin.license; import com.mycila.maven.plugin.license.document.Document; + +import java.io.Closeable; +import java.util.Collections; import java.util.Map; import java.util.Properties; @@ -23,6 +26,24 @@ * @author Mathieu Carbou (mathieu.carbou@gmail.com) * 2013-08-27 */ -public interface PropertiesProvider { - Map getAdditionalProperties(AbstractLicenseMojo mojo, Properties currentProperties, Document document); +public interface PropertiesProvider extends Closeable { + + default void init(AbstractLicenseMojo mojo, Map currentProperties) {} + + default Map adjustProperties(AbstractLicenseMojo mojo, Map currentProperties, Document document) { + Properties properties = new Properties(); + properties.putAll(currentProperties); + return getAdditionalProperties(mojo, properties, document); + } + + /** + * @deprecated Use instead {@link #adjustProperties(AbstractLicenseMojo, Map, Document)} + */ + @Deprecated + default Map getAdditionalProperties(AbstractLicenseMojo mojo, Properties currentProperties, Document document) { + return Collections.emptyMap(); + } + + @Override + default void close() {} } diff --git a/license-maven-plugin/src/main/java/com/mycila/maven/plugin/license/document/DocumentPropertiesLoader.java b/license-maven-plugin/src/main/java/com/mycila/maven/plugin/license/document/DocumentPropertiesLoader.java index e197bf9fe..af3aea837 100755 --- a/license-maven-plugin/src/main/java/com/mycila/maven/plugin/license/document/DocumentPropertiesLoader.java +++ b/license-maven-plugin/src/main/java/com/mycila/maven/plugin/license/document/DocumentPropertiesLoader.java @@ -15,12 +15,12 @@ */ package com.mycila.maven.plugin.license.document; -import java.util.Properties; +import java.util.Map; /** * @author Mathieu Carbou (mathieu.carbou@gmail.com) * 2013-08-27 */ public interface DocumentPropertiesLoader { - Properties load(Document d); + Map load(Document d); } diff --git a/license-maven-plugin/src/main/java/com/mycila/maven/plugin/license/document/PropertyPlaceholderResolver.java b/license-maven-plugin/src/main/java/com/mycila/maven/plugin/license/document/PropertyPlaceholderResolver.java index d06c55bed..b13822fac 100644 --- a/license-maven-plugin/src/main/java/com/mycila/maven/plugin/license/document/PropertyPlaceholderResolver.java +++ b/license-maven-plugin/src/main/java/com/mycila/maven/plugin/license/document/PropertyPlaceholderResolver.java @@ -16,6 +16,7 @@ package com.mycila.maven.plugin.license.document; import java.util.HashSet; +import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.function.Function; @@ -38,8 +39,8 @@ class PropertyPlaceholderResolver { private final String placeholderPrefix = "${"; private final String placeholderSuffix = "}"; - public String replacePlaceholders(String value, final Properties properties) { - return replacePlaceholders(value, properties::getProperty); + public String replacePlaceholders(String value, final Map properties) { + return replacePlaceholders(value, properties::get); } private String replacePlaceholders(String value, Function placeholderResolver) { diff --git a/license-maven-plugin/src/test/java/MyPropertiesProvider.java b/license-maven-plugin/src/test/java/MyPropertiesProvider.java index 0cd8ca2ef..236012e25 100755 --- a/license-maven-plugin/src/test/java/MyPropertiesProvider.java +++ b/license-maven-plugin/src/test/java/MyPropertiesProvider.java @@ -1,9 +1,9 @@ import com.mycila.maven.plugin.license.AbstractLicenseMojo; import com.mycila.maven.plugin.license.PropertiesProvider; import com.mycila.maven.plugin.license.document.Document; + import java.util.HashMap; import java.util.Map; -import java.util.Properties; /** * @author Mathieu Carbou (mathieu.carbou@gmail.com) @@ -11,7 +11,15 @@ */ public final class MyPropertiesProvider implements PropertiesProvider { @Override - public Map getAdditionalProperties(AbstractLicenseMojo mojo, Properties currentProperties, Document document) { + public void init(AbstractLicenseMojo mojo, Map currentProperties) { + } + + @Override + public void close() { + } + + @Override + public Map adjustProperties(AbstractLicenseMojo mojo, Map currentProperties, Document document) { Map map = new HashMap(); map.put("my-custom-property", "my-custom-value"); return map; diff --git a/license-maven-plugin/src/test/java/com/mycila/maven/plugin/license/document/DocumentTest.java b/license-maven-plugin/src/test/java/com/mycila/maven/plugin/license/document/DocumentTest.java index 44e9d2a97..344dcbf16 100755 --- a/license-maven-plugin/src/test/java/com/mycila/maven/plugin/license/document/DocumentTest.java +++ b/license-maven-plugin/src/test/java/com/mycila/maven/plugin/license/document/DocumentTest.java @@ -18,13 +18,15 @@ import com.mycila.maven.plugin.license.header.Header; import com.mycila.maven.plugin.license.header.HeaderSource.UrlHeaderSource; import com.mycila.maven.plugin.license.util.FileUtils; -import java.io.File; -import java.io.IOException; -import java.util.Properties; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + /** * @author Mathieu Carbou (mathieu.carbou@gmail.com) */ @@ -33,8 +35,8 @@ class DocumentTest { static Header header; static DocumentPropertiesLoader loader = new DocumentPropertiesLoader() { @Override - public Properties load(Document d) { - Properties props = new Properties(); + public Map load(Document d) { + Map props = new HashMap<>(); props.put("year", "2008"); return props; }