-
Notifications
You must be signed in to change notification settings - Fork 14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[MJARSIGNER-72] Parallel signing for increased speed #18
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,6 +21,13 @@ | |
import java.io.File; | ||
import java.io.IOException; | ||
import java.time.Duration; | ||
import java.util.List; | ||
import java.util.concurrent.Callable; | ||
import java.util.concurrent.ExecutionException; | ||
import java.util.concurrent.ExecutorService; | ||
import java.util.concurrent.Executors; | ||
import java.util.concurrent.Future; | ||
import java.util.stream.Collectors; | ||
|
||
import org.apache.maven.plugin.MojoExecutionException; | ||
import org.apache.maven.plugins.annotations.LifecyclePhase; | ||
|
@@ -115,6 +122,17 @@ public class JarsignerSignMojo extends AbstractJarsignerMojo { | |
@Parameter(property = "jarsigner.maxRetryDelaySeconds", defaultValue = "0") | ||
private int maxRetryDelaySeconds; | ||
|
||
/** | ||
* Maximum number of parallel threads to use when signing jar files. Increases performance when signing multiple jar | ||
* files, especially when network operations are used during signing, for example when using a Time Stamp Authority | ||
* or network based PKCS11 HSM solution for storing code signing keys. Note: the logging from the signing process | ||
* will be interleaved, and harder to read, when using many threads. | ||
* | ||
* @since 3.1.0 | ||
*/ | ||
@Parameter(property = "jarsigner.threadCount", defaultValue = "1") | ||
private int threadCount; | ||
|
||
/** Current WaitStrategy, to allow for sleeping after a signing failure. */ | ||
private WaitStrategy waitStrategy = this::defaultWaitStrategy; | ||
|
||
|
@@ -156,6 +174,11 @@ protected void validateParameters() throws MojoExecutionException { | |
getLog().warn(getMessage("invalidMaxRetryDelaySeconds", maxRetryDelaySeconds)); | ||
maxRetryDelaySeconds = 0; | ||
} | ||
|
||
if (threadCount < 1) { | ||
getLog().warn(getMessage("invalidThreadCount", threadCount)); | ||
threadCount = 1; | ||
} | ||
} | ||
|
||
/** | ||
|
@@ -174,12 +197,42 @@ protected JarSignerRequest createRequest(File archive) throws MojoExecutionExcep | |
return request; | ||
} | ||
|
||
/** | ||
* {@inheritDoc} Processing of files may be parallelized for increased performance. | ||
*/ | ||
@Override | ||
protected void processArchives(List<File> archives) throws MojoExecutionException { | ||
ExecutorService executor = Executors.newFixedThreadPool(threadCount); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should this be the minimum of threadCount and the number of archives? No reason to spawn ten threads with only 5 files to sign There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice observation! I could, but I'm not sure I should, because it would make the code a bit harder to read. In the documentation for https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ThreadPoolExecutor.html there is this text:
That is, for this specific case the number of threads will in practice be limited to the number of jobs I submit to the thread pool (number of jar files). Due to how Executors.newFixedThreadPool() work you might end up with a few more than needed, if the first jobs terminate very fast before the last has been submitted. But the number of threads will never be more than the amount of submitted jobs. I have tested and verified that this behavior holds! Using |
||
List<Future<Void>> futures = archives.stream() | ||
.map(file -> executor.submit((Callable<Void>) () -> { | ||
processArchive(file); | ||
return null; | ||
})) | ||
.collect(Collectors.toList()); | ||
try { | ||
for (Future<Void> future : futures) { | ||
future.get(); // Wait for completion. Result ignored, but may raise any Exception | ||
} | ||
} catch (InterruptedException e) { | ||
Thread.currentThread().interrupt(); | ||
throw new MojoExecutionException("Thread interrupted while waiting for jarsigner to complete", e); | ||
} catch (ExecutionException e) { | ||
if (e.getCause() instanceof MojoExecutionException) { | ||
throw (MojoExecutionException) e.getCause(); | ||
} | ||
throw new MojoExecutionException("Error processing archives", e); | ||
} finally { | ||
// Shutdown of thread pool. If an Exception occurred, remaining threads will be aborted "best effort" | ||
executor.shutdownNow(); | ||
} | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
* | ||
* Will retry signing up to maxTries times if it fails. | ||
* | ||
* @throws MojoExecutionException If all signing attempts fail. | ||
* @throws MojoExecutionException if all signing attempts fail | ||
*/ | ||
@Override | ||
protected void executeJarSigner(JarSigner jarSigner, JarSignerRequest request) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Once again the diff on GitHub displays like I have changed the entire function. View the diff from an IDE to easier see what rows I have only re-indented.