/*
* Copyright 2011 the original author or authors.
*
* 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 org.gradle.plugins.signing;
import groovy.lang.Closure;
import org.gradle.api.InvalidUserDataException;
import org.gradle.api.Nullable;
import org.gradle.api.artifacts.PublishArtifact;
import org.gradle.api.internal.artifacts.publish.AbstractPublishArtifact;
import org.gradle.plugins.signing.signatory.Signatory;
import org.gradle.plugins.signing.type.SignatureType;
import java.io.File;
import java.util.Date;
import java.util.concurrent.Callable;
import static com.google.common.util.concurrent.Callables.returning;
import static org.gradle.util.GUtil.uncheckedCall;
/**
* A digital signature file artifact.
*
* <p>A signature file is always generated from another file, which may be a {@link PublishArtifact}.</p>
*/
public class Signature extends AbstractPublishArtifact {
/**
* The specification of how to generate the signature.
*/
private SignatureSpec signatureSpec;
/**
* The artifact that this signature is for, which may be {@code null} if this signature is not for an artifact.
*/
private PublishArtifact toSignArtifact;
/**
* The name of the signature artifact.
*
* @see #getName()
*/
private String name;
/**
* The extension of the signature artifact.
*
* @see #getExtension()
*/
private String extension;
/**
* The type of the signature artifact.
*
* @see #getType()
*/
private String type;
/**
* The classifier of the signature artifact.
*
* @see #getClassifier()
*/
private String classifier;
/**
* The date of the signature arifact.
*
* @see #getDate()
*/
private Date date;
/**
* The signature file.
*
* @see #getFile()
*/
private File file;
private Callable<File> toSignGenerator;
private Callable<String> classifierGenerator;
/**
* Creates a signature artifact for the given public artifact.
*
* <p>The file to sign will be the file of the given artifact and the classifier of this signature artifact will default to the classifier of the given artifact to sign.</p> <p> The artifact to
* sign may change after being used as the source for this signature.</p>
*
* @param toSign The artifact that is to be signed
* @param signatureSpec The specification of how the artifact is to be signed
* @param tasks The task(s) that will invoke {@link #generate()} on this signature (optional)
*/
public Signature(final PublishArtifact toSign, SignatureSpec signatureSpec, Object... tasks) {
super(tasks);
init(new Callable<File>() {
public File call() {
return toSign.getFile();
}
}, new Callable<String>() {
public String call() {
return toSign.getClassifier();
}
}, signatureSpec);
this.toSignArtifact = toSign;
}
/**
* Creates a signature artifact for the given file.
*
* @param toSign The file that is to be signed
* @param signatureSpec The specification of how the artifact is to be signed
* @param tasks The task(s) that will invoke {@link #generate()} on this signature (optional)
*/
public Signature(final File toSign, SignatureSpec signatureSpec, Object... tasks) {
super(tasks);
init(returning(toSign), null, signatureSpec);
}
/**
* Creates a signature artifact for the given file, with the given classifier.
*
* @param toSign The file that is to be signed
* @param classifier The classifier to assign to the signature (should match the files)
* @param signatureSpec The specification of how the artifact is to be signed
* @param tasks The task(s) that will invoke {@link #generate()} on this signature (optional)
*/
public Signature(final File toSign, final String classifier, SignatureSpec signatureSpec, Object... tasks) {
super(tasks);
init(returning(toSign), returning(classifier), signatureSpec);
}
/**
* Creates a signature artifact for the file returned by the {@code toSign} closure.
*
* <p>The closures will be “evaluated” on demand whenever the value is needed (e.g. at generation time)</p>
*
* @param toSign A closure that produces a File for the object to sign (non File return values will be used as the path to the file)
* @param classifier A closure that produces the classifier to assign to the signature artifact on demand
* @param signatureSpec The specification of how the artifact is to be signed
* @param tasks The task(s) that will invoke {@link #generate()} on this signature (optional)
*/
public Signature(Closure<File> toSign, Closure<String> classifier, SignatureSpec signatureSpec, Object... tasks) {
super(tasks);
this.toSignGenerator = toSign;
this.classifierGenerator = classifier;
this.signatureSpec = signatureSpec;
}
/**
* Creates a signature artifact for the file returned by the {@code toSign} closure.
*
* <p>The closures will be “evaluated” on demand whenever the value is needed (e.g. at generation time)</p>
*
* @param toSign A closure that produces a File for the object to sign (non File return values will be used as the path to the file)
* @param classifier A closure that produces the classifier to assign to the signature artifact on demand
* @param signatureSpec The specification of how the artifact is to be signed
* @param tasks The task(s) that will invoke {@link #generate()} on this signature (optional)
*/
public Signature(Callable<File> toSign, Callable<String> classifier, SignatureSpec signatureSpec, Object... tasks) {
super(tasks);
this.toSignGenerator = toSign;
this.classifierGenerator = classifier;
this.signatureSpec = signatureSpec;
}
private void init(Callable<File> toSign, Callable<String> classifier, SignatureSpec signatureSpec) {
this.toSignGenerator = toSign;
this.classifierGenerator = classifier;
this.signatureSpec = signatureSpec;
}
/**
* The file that is to be signed.
*
* @return The file. May be {@code null} if unknown at this time.
*/
public File getToSign() {
File toSign = uncheckedCall(toSignGenerator);
return toSign != null ? toSign : null;
}
public void setName(String name) {
this.name = name;
}
/**
* The name of the signature artifact.
*
* <p>Defaults to the name of the signature {@link #getFile() file}.
*
* @return The name. May be {@code null} if unknown at this time.
*/
public String getName() {
return name != null ? name : defaultName();
}
@Nullable
private String defaultName() {
return toSignArtifact != null ? toSignArtifact.getName() : fileName();
}
@Nullable
private String fileName() {
final File file = getFile();
return file != null ? file.getName() : null;
}
public void setExtension(String extension) {
this.extension = extension;
}
/**
* The extension of the signature artifact.
*
* <p>Defaults to the specified file extension of the {@link #getSignatureType() signature type}.</p>
*
* @return The extension. May be {@code null} if unknown at this time.
*/
public String getExtension() {
return extension != null ? extension : signatureTypeExtension();
}
@Nullable
private String signatureTypeExtension() {
SignatureType signatureType = getSignatureType();
return signatureType != null ? signatureType.getExtension() : null;
}
public void setType(String type) {
this.type = type;
}
/**
* The type of the signature artifact.
*
* <p>Defaults to the extension of the {@link #getToSign() file to sign} plus the extension of the {@link #getSignatureType() signature type}. For example, when signing the file ‘my.zip’ with a
* signature type with extension ‘sig’, the default type is ‘zip.sig’.</p>
*
* @return The type. May be {@code null} if the file to sign or signature type are unknown at this time.
*/
public String getType() {
return type != null ? type : defaultType();
}
@Nullable
private String defaultType() {
File toSign = getToSign();
SignatureType signatureType = getSignatureType();
return toSign != null && signatureType != null
? signatureType.combinedExtension(toSign)
: null;
}
public void setClassifier(String classifier) {
this.classifier = classifier;
}
/**
* The classifier of the signature artifact.
*
* <p>Defaults to the classifier of the source artifact (if signing an artifact) or the given classifier at construction (if given).</p>
*
* @return The classifier. May be {@code null} if unknown at this time.
*/
public String getClassifier() {
return classifier != null ? classifier : uncheckedCall(classifierGenerator);
}
public void setDate(Date date) {
this.date = date;
}
/**
* The date of the signature artifact.
*
* <p>Defaults to the last modified time of the {@link #getFile() signature file} (if exists)</p>
*
* @return The date of the signature. May be {@code null} if unknown at this time.
*/
public Date getDate() {
return date != null ? date : defaultDate();
}
@Nullable
private Date defaultDate() {
File file = getFile();
if (file == null) {
return null;
}
long modified = file.lastModified();
if (modified == 0L) {
return null;
}
return new Date(modified);
}
public void setFile(File file) {
this.file = file;
}
/**
* The file for the generated signature, which may not yet exist.
*
* <p>Defaults to a file alongside the {@link #getToSign() file to sign} with the extension of the {@link #getSignatureType() signature type}.</p>
*
* @return The signature file. May be {@code null} if unknown at this time.
*/
public File getFile() {
return file != null ? file : defaultFile();
}
@Nullable
private File defaultFile() {
File toSign = getToSign();
SignatureType signatureType = getSignatureType();
return toSign != null && signatureType != null
? signatureType.fileFor(toSign)
: null;
}
/**
* The signatory of this signature file.
*
* @return The signatory. May be {@code null} if unknown at this time.
*/
public Signatory getSignatory() {
return signatureSpec.getSignatory();
}
/**
* The file representation type of the signature.
*
* @return The signature type. May be {@code null} if unknown at this time.
*/
public SignatureType getSignatureType() {
return signatureSpec.getSignatureType();
}
public void setSignatureSpec(SignatureSpec signatureSpec) {
this.signatureSpec = signatureSpec;
}
public SignatureSpec getSignatureSpec() {
return signatureSpec;
}
public final PublishArtifact getToSignArtifact() {
return toSignArtifact;
}
/**
* Generates the signature file.
*
* <p>In order to generate the signature, the {@link #getToSign() file to sign}, {@link #getSignatory() signatory} and {@link #getSignatureType() signature type} must be known (i.e. non {@code
* null}).</p>
*
* @throws InvalidUserDataException if the there is insufficient information available to generate the signature.
*/
public void generate() {
File toSign = getToSign();
if (toSign == null) {
if (signatureSpec.isRequired()) {
throw new InvalidUserDataException("Unable to generate signature as the file to sign has not been specified");
} else {
return;
}
}
Signatory signatory = getSignatory();
if (signatory == null) {
if (signatureSpec.isRequired()) {
throw new InvalidUserDataException("Unable to generate signature for \'" + String.valueOf(toSign) + "\' as no signatory is available to sign");
} else {
return;
}
}
SignatureType signatureType = getSignatureType();
if (signatureType == null) {
if (signatureSpec.isRequired()) {
throw new InvalidUserDataException("Unable to generate signature for \'" + String.valueOf(toSign) + "\' as no signature type has been configured");
} else {
return;
}
}
signatureType.sign(signatory, toSign);
}
}