-
-
Notifications
You must be signed in to change notification settings - Fork 86
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Rewrote the PropertiesProvider API to support initialization, close a…
…nd multithreading better
- Loading branch information
1 parent
feb0daa
commit 4860641
Showing
14 changed files
with
428 additions
and
348 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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(); | ||
} | ||
} | ||
|
||
/** | ||
|
@@ -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()); | ||
|
@@ -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; | ||
|
@@ -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); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 <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 | ||
} | ||
|
@@ -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> | ||
|
@@ -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(); | ||
} | ||
} |
Oops, something went wrong.