KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > hudson > model > AbstractBuild


1 package hudson.model;
2
3 import hudson.Launcher;
4 import hudson.Proc.LocalProc;
5 import hudson.Util;
6 import hudson.tasks.Fingerprinter.FingerprintAction;
7 import hudson.tasks.test.AbstractTestResultAction;
8 import hudson.maven.MavenBuild;
9 import static hudson.model.Hudson.isWindows;
10 import hudson.model.listeners.SCMListener;
11 import hudson.model.Fingerprint.RangeSet;
12 import hudson.model.Fingerprint.BuildPtr;
13 import hudson.scm.CVSChangeLogParser;
14 import hudson.scm.ChangeLogParser;
15 import hudson.scm.ChangeLogSet;
16 import hudson.scm.ChangeLogSet.Entry;
17 import hudson.scm.SCM;
18 import org.kohsuke.stapler.StaplerRequest;
19 import org.kohsuke.stapler.StaplerResponse;
20 import org.xml.sax.SAXException JavaDoc;
21
22 import javax.servlet.ServletException JavaDoc;
23 import java.io.File JavaDoc;
24 import java.io.IOException JavaDoc;
25 import java.io.PrintStream JavaDoc;
26 import java.util.Calendar JavaDoc;
27 import java.util.Map JavaDoc;
28 import java.util.HashMap JavaDoc;
29 import java.util.Collections JavaDoc;
30
31 /**
32  * Base implementation of {@link Run}s that build software.
33  *
34  * For now this is primarily the common part of {@link Build} and {@link MavenBuild}.
35  *
36  * @author Kohsuke Kawaguchi
37  * @see AbstractProject
38  */

39 public abstract class AbstractBuild<P extends AbstractProject<P,R>,R extends AbstractBuild<P,R>> extends Run<P,R> implements Runnable JavaDoc {
40
41     /**
42      * PluginName of the slave this project was built on.
43      * Null or "" if built by the master. (null happens when we read old record that didn't have this information.)
44      */

45     private String JavaDoc builtOn;
46
47     /**
48      * SCM used for this build.
49      * Maybe null, for historical reason, in which case CVS is assumed.
50      */

51     private ChangeLogParser scm;
52
53     /**
54      * Changes in this build.
55      */

56     private volatile transient ChangeLogSet<? extends Entry> changeSet;
57
58     protected AbstractBuild(P job) throws IOException JavaDoc {
59         super(job);
60     }
61
62     protected AbstractBuild(P job, Calendar JavaDoc timestamp) {
63         super(job, timestamp);
64     }
65
66     protected AbstractBuild(P project, File JavaDoc buildDir) throws IOException JavaDoc {
67         super(project, buildDir);
68     }
69
70     public final P getProject() {
71         return getParent();
72     }
73
74     /**
75      * Returns a {@link Slave} on which this build was done.
76      */

77     public Node getBuiltOn() {
78         if(builtOn==null)
79             return Hudson.getInstance();
80         else
81             return Hudson.getInstance().getSlave(builtOn);
82     }
83
84     /**
85      * Returns the name of the slave it was built on, or null if it was the master.
86      */

87     public String JavaDoc getBuiltOnStr() {
88         return builtOn;
89     }
90
91     protected abstract class AbstractRunner implements Runner {
92         /**
93          * Since configuration can be changed while a build is in progress,
94          * stick to one launcher and use it.
95          */

96         protected Launcher launcher;
97
98         /**
99          * Returns the current {@link Node} on which we are buildling.
100          */

101         protected final Node getCurrentNode() {
102             return Executor.currentExecutor().getOwner().getNode();
103         }
104
105         public Result run(BuildListener listener) throws Exception JavaDoc {
106             Node node = Executor.currentExecutor().getOwner().getNode();
107             assert builtOn==null;
108             builtOn = node.getNodeName();
109
110             launcher = node.createLauncher(listener);
111             if(node instanceof Slave)
112                 listener.getLogger().println("Building remotely on "+node.getNodeName());
113
114             if(checkout(listener))
115                 return Result.FAILURE;
116
117             Result result = doRun(listener);
118             if(result!=null)
119                 return result; // abort here
120

121             if(getResult()==null || getResult()==Result.SUCCESS)
122                 createLastSuccessfulLink(listener);
123
124             return Result.SUCCESS;
125         }
126
127         private void createLastSuccessfulLink(BuildListener listener) {
128             if(!isWindows()) {
129                 try {
130                     // ignore a failure.
131
new LocalProc(new String JavaDoc[]{"rm","-f","../lastSuccessful"},new String JavaDoc[0],listener.getLogger(),getProject().getBuildDir()).join();
132
133                     int r = new LocalProc(new String JavaDoc[]{
134                         "ln","-s","builds/"+getId()/*ugly*/,"../lastSuccessful"},
135                         new String JavaDoc[0],listener.getLogger(),getProject().getBuildDir()).join();
136                     if(r!=0)
137                         listener.getLogger().println("ln failed: "+r);
138                 } catch (IOException JavaDoc e) {
139                     PrintStream JavaDoc log = listener.getLogger();
140                     log.println("ln failed");
141                     Util.displayIOException(e,listener);
142                     e.printStackTrace( log );
143                 }
144             }
145         }
146
147         private boolean checkout(BuildListener listener) throws Exception JavaDoc {
148             if(!project.checkout(AbstractBuild.this,launcher,listener,new File JavaDoc(getRootDir(),"changelog.xml")))
149                 return true;
150
151             SCM scm = project.getScm();
152
153             AbstractBuild.this.scm = scm.createChangeLogParser();
154             AbstractBuild.this.changeSet = AbstractBuild.this.calcChangeSet();
155
156             for (SCMListener l : Hudson.getInstance().getSCMListeners())
157                 l.onChangeLogParsed(AbstractBuild.this,listener,changeSet);
158             return false;
159         }
160
161         /**
162          * The portion of a build that is specific to a subclass of {@link AbstractBuild}
163          * goes here.
164          *
165          * @return
166          * null to continue the build normally (that means the doRun method
167          * itself run successfully)
168          * Return a non-null value to abort the build right there with the specified result code.
169          */

170         protected abstract Result doRun(BuildListener listener) throws Exception JavaDoc;
171     }
172
173     /**
174      * Gets the changes incorporated into this build.
175      *
176      * @return never null.
177      */

178     public ChangeLogSet<? extends Entry> getChangeSet() {
179         if(scm==null)
180             scm = new CVSChangeLogParser();
181
182         if(changeSet==null) // cached value
183
changeSet = calcChangeSet();
184         return changeSet;
185     }
186
187     /**
188      * Returns true if the changelog is already computed.
189      */

190     public boolean hasChangeSetComputed() {
191         File JavaDoc changelogFile = new File JavaDoc(getRootDir(), "changelog.xml");
192         return changelogFile.exists();
193     }
194
195     private ChangeLogSet<? extends Entry> calcChangeSet() {
196         File JavaDoc changelogFile = new File JavaDoc(getRootDir(), "changelog.xml");
197         if(!changelogFile.exists())
198             return ChangeLogSet.createEmpty(this);
199
200         try {
201             return scm.parse(this,changelogFile);
202         } catch (IOException JavaDoc e) {
203             e.printStackTrace();
204         } catch (SAXException JavaDoc e) {
205             e.printStackTrace();
206         }
207         return ChangeLogSet.createEmpty(this);
208     }
209
210     @Override JavaDoc
211     public Map JavaDoc<String JavaDoc,String JavaDoc> getEnvVars() {
212         Map JavaDoc<String JavaDoc,String JavaDoc> env = super.getEnvVars();
213
214         JDK jdk = project.getJDK();
215         if(jdk !=null)
216             jdk.buildEnvVars(env);
217         project.getScm().buildEnvVars(env);
218
219         return env;
220     }
221
222     public Calendar JavaDoc due() {
223         return timestamp;
224     }
225
226     /**
227      * Gets {@link AbstractTestResultAction} associated with this build if any.
228      * <p>
229      */

230     public abstract AbstractTestResultAction getTestResultAction();
231
232     /**
233      * Invoked by {@link Executor} to performs a build.
234      */

235     public abstract void run();
236
237 //
238
//
239
// fingerprint related stuff
240
//
241
//
242

243     @Override JavaDoc
244     public String JavaDoc getWhyKeepLog() {
245         // if any of the downstream project is configured with 'keep dependency component',
246
// we need to keep this log
247
for (Map.Entry JavaDoc<AbstractProject, RangeSet> e : getDownstreamBuilds().entrySet()) {
248             AbstractProject<?,?> p = e.getKey();
249             if(!p.isKeepDependencies()) continue;
250
251             // is there any active build that depends on us?
252
for (AbstractBuild build : p.getBuilds()) {
253                 if(e.getValue().includes(build.getNumber()))
254                     return "kept because of "+build;
255             }
256         }
257
258         return super.getWhyKeepLog();
259     }
260
261     /**
262      * Gets the dependency relationship from this build (as the source)
263      * and that project (as the sink.)
264      *
265      * @return
266      * range of build numbers that represent which downstream builds are using this build.
267      * The range will be empty if no build of that project matches this.
268      */

269     public RangeSet getDownstreamRelationship(AbstractProject that) {
270         RangeSet rs = new RangeSet();
271
272         FingerprintAction f = getAction(FingerprintAction.class);
273         if(f==null) return rs;
274
275         // look for fingerprints that point to this build as the source, and merge them all
276
for (Fingerprint e : f.getFingerprints().values()) {
277             BuildPtr o = e.getOriginal();
278             if(o!=null && o.is(this))
279                 rs.add(e.getRangeSet(that));
280         }
281
282         return rs;
283     }
284
285     /**
286      * Gets the dependency relationship from this build (as the sink)
287      * and that project (as the source.)
288      *
289      * @return
290      * Build number of the upstream build that feed into this build,
291      * or -1 if no record is available.
292      */

293     public int getUpstreamRelationship(AbstractProject that) {
294         FingerprintAction f = getAction(FingerprintAction.class);
295         if(f==null) return -1;
296
297         int n = -1;
298
299         // look for fingerprints that point to the given project as the source, and merge them all
300
for (Fingerprint e : f.getFingerprints().values()) {
301             BuildPtr o = e.getOriginal();
302             if(o!=null && o.is(that))
303                 n = Math.max(n,o.getNumber());
304         }
305
306         return n;
307     }
308
309     /**
310      * Gets the downstream builds of this build, which are the builds of the
311      * downstream projects that use artifacts of this build.
312      *
313      * @return
314      * For each project with fingerprinting enabled, returns the range
315      * of builds (which can be empty if no build uses the artifact from this build.)
316      */

317     public Map JavaDoc<AbstractProject,RangeSet> getDownstreamBuilds() {
318         Map JavaDoc<AbstractProject,RangeSet> r = new HashMap JavaDoc<AbstractProject,RangeSet>();
319         for (AbstractProject p : getParent().getDownstreamProjects()) {
320             if(p.isFingerprintConfigured())
321                 r.put(p,getDownstreamRelationship(p));
322         }
323         return r;
324     }
325     
326     /**
327      * Gets the upstream builds of this build, which are the builds of the
328      * upstream projects whose artifacts feed into this build.
329      */

330     public Map JavaDoc<AbstractProject,Integer JavaDoc> getUpstreamBuilds() {
331         Map JavaDoc<AbstractProject,Integer JavaDoc> r = new HashMap JavaDoc<AbstractProject,Integer JavaDoc>();
332         for (AbstractProject p : getParent().getUpstreamProjects()) {
333             int n = getUpstreamRelationship(p);
334             if(n>=0)
335                 r.put(p,n);
336         }
337         return r;
338     }
339
340     /**
341      * Gets the changes in the dependency between the given build and this build.
342      */

343     public Map JavaDoc<AbstractProject,DependencyChange> getDependencyChanges(AbstractBuild from) {
344         if(from==null) return Collections.emptyMap(); // make it easy to call this from views
345
FingerprintAction n = this.getAction(FingerprintAction.class);
346         FingerprintAction o = from.getAction(FingerprintAction.class);
347         if(n==null || o==null) return Collections.emptyMap();
348
349         Map JavaDoc<AbstractProject,Integer JavaDoc> ndep = n.getDependencies();
350         Map JavaDoc<AbstractProject,Integer JavaDoc> odep = o.getDependencies();
351
352         Map JavaDoc<AbstractProject,DependencyChange> r = new HashMap JavaDoc<AbstractProject,DependencyChange>();
353
354         for (Map.Entry JavaDoc<AbstractProject,Integer JavaDoc> entry : odep.entrySet()) {
355             AbstractProject p = entry.getKey();
356             Integer JavaDoc oldNumber = entry.getValue();
357             Integer JavaDoc newNumber = ndep.get(p);
358             if(newNumber!=null && oldNumber.compareTo(newNumber)<0) {
359                 r.put(p,new DependencyChange(p,oldNumber,newNumber));
360             }
361         }
362
363         return r;
364     }
365
366     /**
367      * Represents a change in the dependency.
368      */

369     public static final class DependencyChange {
370         /**
371          * The dependency project.
372          */

373         public final AbstractProject project;
374         /**
375          * Version of the dependency project used in the previous build.
376          */

377         public final int fromId;
378         /**
379          * {@link Build} object for {@link #fromId}. Can be null if the log is gone.
380          */

381         public final AbstractBuild from;
382         /**
383          * Version of the dependency project used in this build.
384          */

385         public final int toId;
386
387         public final AbstractBuild to;
388
389         public DependencyChange(AbstractProject<?,?> project, int fromId, int toId) {
390             this.project = project;
391             this.fromId = fromId;
392             this.toId = toId;
393             this.from = project.getBuildByNumber(fromId);
394             this.to = project.getBuildByNumber(toId);
395         }
396     }
397     
398
399 //
400
//
401
// web methods
402
//
403
//
404
/**
405      * Stops this build if it's still going.
406      *
407      * If we use this/executor/stop URL, it causes 404 if the build is already killed,
408      * as {@link #getExecutor()} returns null.
409      */

410     public synchronized void doStop( StaplerRequest req, StaplerResponse rsp ) throws IOException JavaDoc, ServletException JavaDoc {
411         Executor e = getExecutor();
412         if(e!=null)
413             e.doStop(req,rsp);
414         else
415             // nothing is building
416
rsp.forwardToPreviousPage(req);
417     }
418 }
419
Popular Tags