hudson.maven.reporters.MavenArtifact Maven / Gradle / Ivy
/*
* The MIT License
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package hudson.maven.reporters;
import hudson.FilePath;
import hudson.Util;
import hudson.maven.MavenBuild;
import hudson.maven.MavenBuildProxy;
import hudson.model.BuildListener;
import hudson.model.FingerprintMap;
import hudson.model.Hudson;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.factory.ArtifactFactory;
import org.apache.maven.artifact.handler.ArtifactHandler;
import org.apache.maven.artifact.handler.DefaultArtifactHandler;
import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
import com.google.common.collect.Maps;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.Serializable;
import java.util.Map;
import java.util.logging.Logger;
/**
* Captures information about an artifact created by Maven and archived by
* Hudson, so that we can later deploy it to repositories of our choice.
*
*
* This object is created within the Maven process and sent back to the master,
* so it shouldn't contain anything non-serializable as fields.
*
*
* Once it's constructed, the object should be considered final and immutable.
*
* @author Kohsuke Kawaguchi
* @since 1.189
*/
public final class MavenArtifact implements Serializable {
/**
* Basic parameters of a Maven artifact.
*/
public final String groupId, artifactId, version, classifier, type;
/**
* File name (without directory portion) of this artifact in the Hudson archive.
* Remembered explicitly because some times this doesn't follow the
* standard naming convention, due to <finalName> setting in POM.
*
*
* This name is taken directly from the name of the file as used during the build
* (thus POM would be most likely just pom.xml and artifacts would
* use their finalName if one is configured.) This is often
* different from {@link #canonicalName}.
*/
public final String fileName;
/**
* The canonical artifact file name, used by Maven in the repository.
* This is artifactId-version[-classifier].extension.
*
*
* The reason we persist this is that the extension is only available
* through {@link ArtifactHandler}.
*/
public final String canonicalName;
/**
* The md5sum for this artifact.
*/
public final String md5sum;
public MavenArtifact(Artifact a) throws IOException {
this.groupId = a.getGroupId();
this.artifactId = a.getArtifactId();
this.version = a.getVersion();
this.classifier = a.getClassifier();
this.type = a.getType();
// TODO: on archive we need to follow the same format as Maven repository
this.fileName = a.getFile().getName();
this.md5sum = Util.getDigestOf(new FileInputStream(a.getFile()));
String extension;
if(a.getArtifactHandler()!=null) // don't know if this can be null, but just to be defensive.
extension = a.getArtifactHandler().getExtension();
else
extension = a.getType();
canonicalName = getSeed(extension);
}
public MavenArtifact(String groupId, String artifactId, String version, String classifier, String type, String fileName, String md5sum) {
this.groupId = groupId;
this.artifactId = artifactId;
this.version = version;
this.classifier = classifier;
this.type = type;
this.fileName = fileName;
this.canonicalName = getSeed(type);
this.md5sum = md5sum;
}
/**
* Convenience method to check if the given {@link Artifact} object contains
* enough information suitable for recording, and if so, create {@link MavenArtifact}.
*/
public static MavenArtifact create(Artifact a) throws IOException {
File file = a.getFile();
if(file==null)
return null; // perhaps build failed and didn't leave an artifact
if(!file.exists() || file.isDirectory())
return null; // during a build maven sets a class folder instead of a jar file as artifact. ignore.
return new MavenArtifact(a);
}
public boolean isPOM() {
return fileName.endsWith(".pom"); // hack
}
/**
* Creates a Maven {@link Artifact} back from the persisted data.
*/
public Artifact toArtifact(ArtifactHandlerManager handlerManager, ArtifactFactory factory, MavenBuild build) throws IOException {
// Hack: presence of custom ArtifactHandler during builds could influence the file extension
// in the repository during deployment. So simulate that behavior if that's necessary.
final String canonicalExtension = canonicalName.substring(canonicalName.lastIndexOf('.')+1);
ArtifactHandler ah = handlerManager.getArtifactHandler(type);
Map handlers = Maps.newHashMap();
handlers.put( type, new DefaultArtifactHandler(type) {
public String getExtension() {
return canonicalExtension;
} } );
// Fix for HUDSON-3814 - changed from comparing against canonical extension to canonicalName.endsWith.
if(!canonicalName.endsWith(ah.getExtension())) {
handlerManager.addHandlers(handlers);
}
Artifact a = factory.createArtifactWithClassifier(groupId, artifactId, version, type, classifier);
a.setFile(getFile(build));
return a;
}
/**
* Computes the file name seed by taking <finalName> POM entry into consideration.
*/
private String getSeed(String extension) {
String name = artifactId+'-'+version;
if(classifier!=null)
name += '-'+classifier;
name += '.'+extension;
return name;
}
/**
* Obtains the {@link File} representing the archived artifact.
*/
protected File getFile(MavenBuild build) throws IOException {
File f = new File(new File(new File(new File(build.getArtifactsDir(), groupId), artifactId), version), fileName);
if(!f.exists())
throw new IOException("Archived artifact is missing: "+f);
return f;
}
private FilePath getArtifactArchivePath(MavenBuildProxy build, String groupId, String artifactId, String version) {
return build.getArtifactsDir().child(groupId).child(artifactId).child(version).child(fileName);
}
/**
* Called from within Maven to archive an artifact in Hudson.
*/
public void archive(MavenBuildProxy build, File file, BuildListener listener) throws IOException, InterruptedException {
if (build.isArchivingDisabled()) {
listener.getLogger().println("[HUDSON] Archiving disabled - not archiving " + file);
}
else {
FilePath target = getArtifactArchivePath(build,groupId,artifactId,version);
FilePath origin = new FilePath(file);
if (!target.exists()) {
listener.getLogger().println("[HUDSON] Archiving "+ file+" to "+target);
origin.copyTo(target);
} else if (!origin.digest().equals(target.digest())) {
listener.getLogger().println("[HUDSON] Re-archiving "+file);
origin.copyTo(target);
} else {
LOGGER.fine("Not actually archiving "+origin+" due to digest match");
}
/* debug probe to investigate "missing artifact" problem typically seen like this:
ERROR: Asynchronous execution failure
java.util.concurrent.ExecutionException: java.io.IOException: Archived artifact is missing: /files/hudson/server/jobs/glassfish-v3/modules/org.glassfish.build$maven-glassfish-extension/builds/2008-04-02_10-17-15/archive/org.glassfish.build/maven-glassfish-extension/1.0-SNAPSHOT/maven-glassfish-extension-1.0-SNAPSHOT.jar
at hudson.remoting.Channel$1.adapt(Channel.java:423)
at hudson.remoting.Channel$1.adapt(Channel.java:418)
at hudson.remoting.FutureAdapter.get(FutureAdapter.java:32)
at hudson.maven.MavenBuilder.call(MavenBuilder.java:140)
at hudson.maven.MavenModuleSetBuild$Builder.call(MavenModuleSetBuild.java:476)
at hudson.maven.MavenModuleSetBuild$Builder.call(MavenModuleSetBuild.java:422)
at hudson.remoting.UserRequest.perform(UserRequest.java:69)
at hudson.remoting.UserRequest.perform(UserRequest.java:23)
at hudson.remoting.Request$2.run(Request.java:200)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:417)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:269)
at java.util.concurrent.FutureTask.run(FutureTask.java:123)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:650)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:675)
at java.lang.Thread.run(Thread.java:595)
Caused by: java.io.IOException: Archived artifact is missing: /files/hudson/server/jobs/glassfish-v3/modules/org.glassfish.build$maven-glassfish-extension/builds/2008-04-02_10-17-15/archive/org.glassfish.build/maven-glassfish-extension/1.0-SNAPSHOT/maven-glassfish-extension-1.0-SNAPSHOT.jar
at hudson.maven.reporters.MavenArtifact.getFile(MavenArtifact.java:147)
at hudson.maven.reporters.MavenArtifact.toArtifact(MavenArtifact.java:126)
at hudson.maven.reporters.MavenArtifactRecord.install(MavenArtifactRecord.java:115)
at hudson.maven.reporters.MavenArtifactArchiver$1.call(MavenArtifactArchiver.java:81)
at hudson.maven.reporters.MavenArtifactArchiver$1.call(MavenArtifactArchiver.java:71)
at hudson.maven.MavenBuild$ProxyImpl.execute(MavenBuild.java:255)
at hudson.maven.MavenBuildProxy$Filter$AsyncInvoker.call(MavenBuildProxy.java:177)
at hudson.remoting.UserRequest.perform(UserRequest.java:69)
at hudson.remoting.UserRequest.perform(UserRequest.java:23)
at hudson.remoting.Request$2.run(Request.java:200)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:441)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
at java.util.concurrent.FutureTask.run(FutureTask.java:138)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:885)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:907)
at java.lang.Thread.run(Thread.java:619)
*/
if(!target.exists())
throw new AssertionError("Just copied "+file+" to "+target+" but now I can't find it");
}
}
/**
* Called from within the master to record fingerprint.
*/
public void recordFingerprint(MavenBuild build) throws IOException {
FingerprintMap map = Hudson.getInstance().getFingerprintMap();
map.getOrCreate(build,fileName,md5sum);
}
private static final Logger LOGGER = Logger.getLogger(MavenArtifact.class.getName());
private static final long serialVersionUID = 1L;
}