Skip to content

Commit

Permalink
Rewrote the PropertiesProvider API to support initialization, close a…
Browse files Browse the repository at this point in the history
…nd multithreading better
  • Loading branch information
mathieucarbou committed Mar 28, 2022
1 parent ce1ae61 commit 4b2cf41
Show file tree
Hide file tree
Showing 14 changed files with 437 additions and 320 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,26 +18,41 @@
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)}.
* An implementation of {@link PropertiesProvider} that adds {@value
* #COPYRIGHT_CREATION_AUTHOR_NAME_KEY} and {@value #COPYRIGHT_CREATION_AUTHOR_EMAIL_KEY} values -
* see {@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;

public CopyrightAuthorProvider() {
super();
@Override
public void init(AbstractLicenseMojo mojo, Map<String, String> 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.");
}
}

@Override
public void close() {
if (gitLookup != null) {
gitLookup.close();
}
}

/**
Expand All @@ -49,21 +64,20 @@ public CopyrightAuthorProvider() {
* <li>{@value #COPYRIGHT_CREATION_AUTHOR_EMAIL_KEY} key stores the author's email address of the first git commit.
* </ul>
*/
public Map<String, String> getAdditionalProperties(AbstractLicenseMojo mojo, Properties properties,
Document document) {

@Override
public Map<String, String> adjustProperties(AbstractLicenseMojo mojo,
Map<String, String> properties, Document document) {
try {
Map<String, String> 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()));
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);
}
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -18,29 +18,44 @@
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.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import org.eclipse.jgit.api.errors.GitAPIException;

/**
* An implementation of {@link PropertiesProvider} that adds {@value #COPYRIGHT_LAST_YEAR_KEY} and
* {@value #COPYRIGHT_YEARS_KEY} values - see
* {@link #getAdditionalProperties(AbstractLicenseMojo, Properties, Document)}.
* {@value #COPYRIGHT_YEARS_KEY} values - see {@link #adjustProperties(AbstractLicenseMojo, Map,
* Document)}.
*
* @author <a href="mailto:[email protected]">Peter Palaga</a>
*/
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";
public static final String COPYRIGHT_EXISTENCE_YEARS_KEY = "license.git.copyrightExistenceYears";
public static final String COPYRIGHT_YEARS_KEY = "license.git.copyrightYears";
public static final String INCEPTION_YEAR_KEY = "project.inceptionYear";

private GitLookup gitLookup;

public CopyrightRangeProvider() {
super();
@Override
public void init(AbstractLicenseMojo mojo, Map<String, String> currentProperties) {
gitLookup = GitLookup.create(mojo.defaultBasedir, currentProperties);

// 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();
}
}

/**
Expand All @@ -54,16 +69,17 @@ public CopyrightRangeProvider() {
* returned; otherwise, the two values are combined using dash, so that the result is e.g. {@code "2000-2010"}.</li>
* <li>{@value #COPYRIGHT_CREATION_YEAR_KEY} key stores the year from the committer date of the first git commit for
* the supplied {@code document}.</li>
* <li>{@value #COPYRIGHT_EXISTENCE_YEARS_KEY} key stores the range from {@value #COPYRIGHT_CREATION_YEAR_KEY} value to
* <li>{@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"}.</li>
* </ul>
* The {@value #INCEPTION_YEAR_KEY} value is read from the supplied properties and it must available. Otherwise a
* {@link RuntimeException} is thrown.
*/
public Map<String, String> getAdditionalProperties(AbstractLicenseMojo mojo, Properties properties,
Document document) {
String inceptionYear = properties.getProperty(INCEPTION_YEAR_KEY);
@Override
public Map<String, String> adjustProperties(AbstractLicenseMojo mojo,
Map<String, String> 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());
Expand All @@ -72,12 +88,13 @@ public Map<String, String> getAdditionalProperties(AbstractLicenseMojo mojo, Pro
try {
inceptionYearInt = Integer.parseInt(inceptionYear);
} catch (NumberFormatException e1) {
throw new RuntimeException("'" + INCEPTION_YEAR_KEY + "' must be an integer ; found = " + inceptionYear + " file: "
+ document.getFile().getAbsolutePath());
throw new RuntimeException(
"'" + INCEPTION_YEAR_KEY + "' must be an integer ; found = " + inceptionYear + " file: "
+ document.getFile().getAbsolutePath());
}
try {
Map<String, String> 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;
Expand All @@ -90,19 +107,20 @@ public Map<String, String> 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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,16 @@
*/
package com.mycila.maven.plugin.license.git;

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;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.Status;
Expand All @@ -44,9 +48,16 @@
*
* @author <a href="mailto:[email protected]">Peter Palaga</a>
*/
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
}
Expand All @@ -59,41 +70,94 @@ public enum DateSource {
private final boolean shallow;

/**
* Creates a new {@link GitLookup} for a repository that is detected from the supplied {@code anyFile}.
* <p>
* Note on time zones:
*
* @param anyFile - any path from the working tree of the git repository to consider in all subsequent calls to
* {@link #getYearOfLastChange(File)}
* @param dateSource where to read the commit dates from - committer date or author date
* @param timeZone the time zone if {@code dateSource} is {@link DateSource#COMMITER}; otherwise must be {@code null}.
* @param checkCommitsCount
* @throws IOException
* Lazily initializes #gitLookup assuming that all subsequent calls to this method will be related
* to the same git repository.
*/
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;
public static GitLookup create(File file, Map<String, String> 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:
this.timeZone = timeZone == null ? DEFAULT_ZONE : timeZone;
timeZone = tzString == null ? GitLookup.DEFAULT_ZONE : TimeZone.getTimeZone(tzString);
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.");
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.");
}
this.timeZone = null;
timeZone = null;
break;
default:
throw new IllegalStateException("Unexpected " + DateSource.class.getName() + " " + dateSource);
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}.
* <p>
* Note on time zones:
*
* @param anyFile - any path from the working tree of the git repository to consider in
* all subsequent calls to {@link #getYearOfLastChange(File)}
* @param dateSource where to read the commit dates from - committer date or author date
* @param timeZone the time zone if {@code dateSource} is {@link DateSource#COMMITER};
* otherwise must be {@code null}.
* @param checkCommitsCount
* @throws IOException
*/
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;
}

/**
Expand Down Expand Up @@ -148,12 +212,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;
}

Expand Down Expand Up @@ -182,7 +246,7 @@ String getAuthorEmailOfCreation(File file) throws IOException {
walk.dispose();
return authorEmail;
}

boolean isShallowRepository() {
return this.shallow;
}
Expand Down Expand Up @@ -245,4 +309,9 @@ private String getAuthorEmailFromCommit(RevCommit commit) {
PersonIdent id = commit.getAuthorIdent();
return id.getEmailAddress();
}

@Override
public void close() {
repository.close();
}
}
Loading

0 comments on commit 4b2cf41

Please sign in to comment.