File D:\maven-qcr-plugin-testsunitaires\qcr4java\jenkins-master\target\site\qc\maven-plugin\src\main\java\hudson\maven\reporters\MavenArtifact.java.html

1 /*
2 * The MIT License
3 *
4 * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, CloudBees, Inc.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24 package hudson.maven.reporters;
25
26 import hudson.FilePath;
27 import hudson.Util;
28 import hudson.maven.MavenBuild;
29 import hudson.maven.MavenBuildProxy;
30 import hudson.model.Api;
31 import hudson.model.BuildListener;
32 import hudson.model.FingerprintMap;
33 import hudson.model.Run;
34 import hudson.util.LRUStringConverter;
35 import jenkins.model.Jenkins;
36
37 import hudson.util.HttpResponses;
38 import org.apache.maven.artifact.Artifact;
39 import org.apache.maven.artifact.factory.ArtifactFactory;
40 import org.apache.maven.artifact.handler.ArtifactHandler;
41 import org.apache.maven.artifact.handler.DefaultArtifactHandler;
42 import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
43
44 import com.google.common.collect.Maps;
45 import org.kohsuke.stapler.AncestorInPath;
46 import org.kohsuke.stapler.HttpResponse;
47 import org.kohsuke.stapler.export.Exported;
48 import org.kohsuke.stapler.export.ExportedBean;
49
50 import java.io.File;
51 import java.io.FileInputStream;
52 import java.io.IOException;
53 import java.io.Serializable;
54 import java.util.Map;
55 import java.util.logging.Logger;
56
57 /**
58 * Captures information about an artifact created by Maven and archived by
59 * Jenkins, so that we can later deploy it to repositories of our choice.
60 *
61 * <p>
62 * This object is created within the Maven process and sent back to the master,
63 * so it shouldn't contain anything non-serializable as fields.
64 *
65 * <p>
66 * Once it's constructed, the object should be considered final and immutable.
67 *
68 * @author Kohsuke Kawaguchi
69 * @since 1.189
70 */
71 @ExportedBean
72 public final class MavenArtifact implements Serializable {
73
74 static {
75 Run.XSTREAM.registerLocalConverter(MavenArtifact.class, "md5sum", new LRUStringConverter(5000));
76 }
77
78 /**
79 * Basic parameters of a Maven artifact.
80 */
81 @Exported
82 public final String groupId, artifactId, version, classifier, type;
83
84 /**
85 * File name (without directory portion) of this artifact in the Hudson archive.
86 * Remembered explicitly because some times this doesn't follow the
87 * standard naming convention, due to &lt;finalName> setting in POM.
88 *
89 * <p>
90 * This name is taken directly from the name of the file as used during the build
91 * (thus POM would be most likely just <tt>pom.xml</tt> and artifacts would
92 * use their <tt>finalName</tt> if one is configured.) This is often
93 * different from {@link #canonicalName}.
94 */
95 @Exported
96 public final String fileName;
97
98 /**
99 * The canonical artifact file name, used by Maven in the repository.
100 * This is <tt>artifactId-version[-classifier].extension</tt>.
101 *
102 * <p>
103 * The reason we persist this is that the extension is only available
104 * through {@link ArtifactHandler}.
105 */
106 @Exported
107 public final String canonicalName;
108
109 /**
110 * The md5sum for this artifact.
111 */
112 @Exported
113 public final String md5sum;
114
115 public MavenArtifact(Artifact a) throws IOException {
116 this.groupId = a.getGroupId();
117 this.artifactId = a.getArtifactId();
118 this.version = a.getVersion();
119 this.classifier = a.getClassifier();
120 this.type = a.getType();
121 this.fileName = a.getFile().getName();
122 this.md5sum = Util.getDigestOf(new FileInputStream(a.getFile()));
123 String extension;
124 if(a.getArtifactHandler()!=null) // don't know if this can be null, but just to be defensive.
125 extension = a.getArtifactHandler().getExtension();
126 else
127 extension = a.getType();
128
129 canonicalName = getSeed(extension);
130 }
131
132 public MavenArtifact(String groupId, String artifactId, String version, String classifier, String type, String fileName, String md5sum) {
133 this.groupId = groupId;
134 this.artifactId = artifactId;
135 this.version = version;
136 this.classifier = classifier;
137 this.type = type;
138 this.fileName = fileName;
139 this.canonicalName = getSeed(type);
140 this.md5sum = md5sum;
141 }
142
143 /**
144 * Convenience method to check if the given {@link Artifact} object contains
145 * enough information suitable for recording, and if so, create {@link MavenArtifact}.
146 */
147 public static MavenArtifact create(Artifact a) throws IOException {
148 File file = a.getFile();
149 if(file==null)
150 return null; // perhaps build failed and didn't leave an artifact
151 if(!file.isFile())
152 return null; // file doesn't exist or artifact points to a directory
153 return new MavenArtifact(a);
154 }
155
156 public boolean isPOM() {
157 return fileName.endsWith(".pom")||"pom.xml".equals(fileName); // hack
158 }
159
160 /**
161 * Creates a Maven {@link Artifact} back from the persisted data.
162 */
163 public Artifact toArtifact(ArtifactHandlerManager handlerManager, ArtifactFactory factory, MavenBuild build) throws IOException {
164 // Hack: presence of custom ArtifactHandler during builds could influence the file extension
165 // in the repository during deployment. So simulate that behavior if that's necessary.
166 final String canonicalExtension = canonicalName.substring(canonicalName.lastIndexOf('.')+1);
167 ArtifactHandler ah = handlerManager.getArtifactHandler(type);
168 Map<String,ArtifactHandler> handlers = Maps.newHashMap();
169
170 handlers.put( type, new DefaultArtifactHandler(type) {
171 public String getExtension() {
172 return canonicalExtension;
173 } } );
174 // Fix for HUDSON-3814 - changed from comparing against canonical extension to canonicalName.endsWith.
175 if(!canonicalName.endsWith(ah.getExtension())) {
176 handlerManager.addHandlers(handlers);
177 }
178
179 Artifact a = factory.createArtifactWithClassifier(groupId, artifactId, version, type, classifier);
180 a.setFile(getFile(build));
181
182 return a;
183 }
184
185 /**
186 * Computes the file name seed by taking &lt;finalName> POM entry into consideration.
187 */
188 private String getSeed(String extension) {
189 String name = artifactId+'-'+version;
190 if(Util.fixEmpty(classifier)!=null)
191 name += '-'+classifier;
192 name += '.'+extension;
193 return name;
194 }
195
196 /**
197 * Obtains the {@link File} representing the archived artifact.
198 */
199 public File getFile(MavenBuild build) throws IOException {
200 File f = new File(new File(new File(new File(build.getArtifactsDir(), groupId), artifactId), version), canonicalName);
201 if(!f.exists())
202 throw new IOException("Archived artifact is missing: "+f);
203 return f;
204 }
205
206 /**
207 * Serve the file.
208 *
209 * TODO: figure out how to make this URL more discoverable to the remote API.
210 */
211 public HttpResponse doFile(@AncestorInPath MavenArtifactRecord parent) throws IOException {
212 return HttpResponses.staticResource(getFile(parent.parent));
213 }
214
215 private FilePath getArtifactArchivePath(MavenBuildProxy build, String groupId, String artifactId, String version) {
216 return build.getArtifactsDir().child(groupId).child(artifactId).child(version).child(canonicalName);
217 }
218
219 /**
220 * Called from within Maven to archive an artifact in Hudson.
221 */
222 public void archive(MavenBuildProxy build, File file, BuildListener listener) throws IOException, InterruptedException {
223 if (build.isArchivingDisabled()) {
224 LOGGER.fine("Archiving disabled - not archiving " + file);
225 }
226 else {
227 FilePath target = getArtifactArchivePath(build,groupId,artifactId,version);
228 FilePath origin = new FilePath(file);
229 if (!target.exists()) {
230 listener.getLogger().println("[JENKINS] Archiving "+ file+" to "+target);
231 origin.copyTo(target);
232 } else if (!origin.digest().equals(target.digest())) {
233 listener.getLogger().println("[JENKINS] Re-archiving "+file);
234 origin.copyTo(target);
235 } else {
236 LOGGER.fine("Not actually archiving "+origin+" due to digest match");
237 }
238
239 /* debug probe to investigate "missing artifact" problem typically seen like this:
240
241 ERROR: Asynchronous execution failure
242 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
243 at hudson.remoting.Channel$1.adapt(Channel.java:423)
244 at hudson.remoting.Channel$1.adapt(Channel.java:418)
245 at hudson.remoting.FutureAdapter.get(FutureAdapter.java:32)
246 at hudson.maven.MavenBuilder.call(MavenBuilder.java:140)
247 at hudson.maven.MavenModuleSetBuild$Builder.call(MavenModuleSetBuild.java:476)
248 at hudson.maven.MavenModuleSetBuild$Builder.call(MavenModuleSetBuild.java:422)
249 at hudson.remoting.UserRequest.perform(UserRequest.java:69)
250 at hudson.remoting.UserRequest.perform(UserRequest.java:23)
251 at hudson.remoting.Request$2.run(Request.java:200)
252 at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:417)
253 at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:269)
254 at java.util.concurrent.FutureTask.run(FutureTask.java:123)
255 at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:650)
256 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:675)
257 at java.lang.Thread.run(Thread.java:595)
258 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
259 at hudson.maven.reporters.MavenArtifact.getFile(MavenArtifact.java:147)
260 at hudson.maven.reporters.MavenArtifact.toArtifact(MavenArtifact.java:126)
261 at hudson.maven.reporters.MavenArtifactRecord.install(MavenArtifactRecord.java:115)
262 at hudson.maven.reporters.MavenArtifactArchiver$1.call(MavenArtifactArchiver.java:81)
263 at hudson.maven.reporters.MavenArtifactArchiver$1.call(MavenArtifactArchiver.java:71)
264 at hudson.maven.MavenBuild$ProxyImpl.execute(MavenBuild.java:255)
265 at hudson.maven.MavenBuildProxy$Filter$AsyncInvoker.call(MavenBuildProxy.java:177)
266 at hudson.remoting.UserRequest.perform(UserRequest.java:69)
267 at hudson.remoting.UserRequest.perform(UserRequest.java:23)
268 at hudson.remoting.Request$2.run(Request.java:200)
269 at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:441)
270 at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
271 at java.util.concurrent.FutureTask.run(FutureTask.java:138)
272 at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:885)
273 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:907)
274 at java.lang.Thread.run(Thread.java:619)
275 */
276
277 if(!target.exists())
278 throw new AssertionError("Just copied "+file+" to "+target+" but now I can't find it");
279 }
280 }
281
282 /**
283 * Called from within the master to record fingerprint.
284 */
285 public void recordFingerprint(MavenBuild build) throws IOException {
286 FingerprintMap map = Jenkins.getInstance().getFingerprintMap();
287 map.getOrCreate(build,fileName,md5sum);
288 }
289
290 public Api getApi() {
291 return new Api(this);
292 }
293
294 private static final Logger LOGGER = Logger.getLogger(MavenArtifact.class.getName());
295
296 private static final long serialVersionUID = 1L;
297 }
298