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 feb0daa commit 4860641
Show file tree
Hide file tree
Showing 14 changed files with 428 additions and 348 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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<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.");
}
}

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

/**
Expand All @@ -49,21 +65,15 @@ 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()));
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,45 @@
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 <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;

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

/**
Expand All @@ -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"}.</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 @@ -77,7 +93,7 @@ public Map<String, String> getAdditionalProperties(AbstractLicenseMojo mojo, Pro
}
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 +106,18 @@ 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,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;
Expand All @@ -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 <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 @@ -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<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:
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}.
* <p>
Expand All @@ -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;
}

/**
Expand Down Expand Up @@ -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;
}

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

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

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

0 comments on commit 4860641

Please sign in to comment.