KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > hudson > maven > MavenBuild


1 package hudson.maven;
2
3 import hudson.FilePath;
4 import hudson.Util;
5 import hudson.maven.agent.*;
6 import hudson.model.AbstractBuild;
7 import hudson.model.AbstractProject;
8 import hudson.model.BuildListener;
9 import hudson.model.DependencyGraph;
10 import hudson.model.Hudson;
11 import hudson.model.Result;
12 import hudson.model.Run;
13 import hudson.model.Slave;
14 import hudson.remoting.Callable;
15 import hudson.remoting.Channel;
16 import hudson.remoting.Launcher;
17 import hudson.remoting.Which;
18 import hudson.scm.ChangeLogSet;
19 import hudson.scm.ChangeLogSet.Entry;
20 import hudson.tasks.Maven.MavenInstallation;
21 import hudson.tasks.test.AbstractTestResultAction;
22 import hudson.util.ArgumentListBuilder;
23 import hudson.util.IOException2;
24 import org.codehaus.classworlds.NoSuchRealmException;
25 import org.apache.maven.lifecycle.LifecycleExecutorInterceptor;
26
27 import java.io.File JavaDoc;
28 import java.io.FilenameFilter JavaDoc;
29 import java.io.IOException JavaDoc;
30 import java.io.Serializable JavaDoc;
31 import java.lang.reflect.InvocationTargetException JavaDoc;
32 import java.util.ArrayList JavaDoc;
33 import java.util.Calendar JavaDoc;
34 import java.util.List JavaDoc;
35 import java.util.Set JavaDoc;
36
37 /**
38  * {@link Run} for {@link MavenModule}.
39  *
40  * @author Kohsuke Kawaguchi
41  */

42 public class MavenBuild extends AbstractBuild<MavenModule,MavenBuild> {
43     /**
44      * {@link MavenReporter}s that will contribute project actions.
45      * Can be null if there's none.
46      */

47     /*package*/ List JavaDoc<MavenReporter> projectActionReporters;
48
49     public MavenBuild(MavenModule job) throws IOException JavaDoc {
50         super(job);
51     }
52
53     public MavenBuild(MavenModule job, Calendar JavaDoc timestamp) {
54         super(job, timestamp);
55     }
56
57     public MavenBuild(MavenModule project, File JavaDoc buildDir) throws IOException JavaDoc {
58         super(project, buildDir);
59     }
60
61     /**
62      * Gets the {@link MavenModuleSetBuild} that has the same build number.
63      *
64      * @return
65      * null if no such build exists, which happens when the module build
66      * is manually triggered.
67      */

68     public MavenModuleSetBuild getParentBuild() {
69         return getParent().getParent().getBuildByNumber(getNumber());
70     }
71
72     @Override JavaDoc
73     public ChangeLogSet<? extends Entry> getChangeSet() {
74         return new FilteredChangeLogSet(this);
75     }
76
77     /**
78      * We always get the changeset from {@link MavenModuleSetBuild}.
79      */

80     @Override JavaDoc
81     public boolean hasChangeSetComputed() {
82         return true;
83     }
84
85     @Override JavaDoc
86     public AbstractTestResultAction getTestResultAction() {
87         return getAction(AbstractTestResultAction.class);
88     }
89
90     public void registerAsProjectAction(MavenReporter reporter) {
91         if(projectActionReporters==null)
92             projectActionReporters = new ArrayList JavaDoc<MavenReporter>();
93         projectActionReporters.add(reporter);
94     }
95     
96     @Override JavaDoc
97     public void run() {
98         run(new RunnerImpl());
99         getProject().updateTransientActions();
100     }
101
102     /**
103      * Runs Maven and builds the project.
104      */

105     private static final class Builder implements Callable<Result,IOException JavaDoc> {
106         private final BuildListener listener;
107         private final MavenBuildProxy buildProxy;
108         private final MavenReporter[] reporters;
109         private final List JavaDoc<String JavaDoc> goals;
110
111         public Builder(BuildListener listener,MavenBuildProxy buildProxy,MavenReporter[] reporters, List JavaDoc<String JavaDoc> goals) {
112             this.listener = listener;
113             this.buildProxy = buildProxy;
114             this.reporters = reporters;
115             this.goals = goals;
116         }
117
118         /**
119          * This code is executed inside the maven jail process.
120          */

121         public Result call() throws IOException JavaDoc {
122             try {
123                 PluginManagerInterceptor pmi = new PluginManagerInterceptor(buildProxy, reporters, listener);
124                 hudson.maven.agent.PluginManagerInterceptor.setListener(pmi);
125                 LifecycleExecutorInterceptor.setListener(pmi);
126
127                 int r = Main.launch(goals.toArray(new String JavaDoc[goals.size()]));
128                 return r==0 ? Result.SUCCESS : Result.FAILURE;
129             } catch (NoSuchMethodException JavaDoc e) {
130                 throw new IOException2(e);
131             } catch (IllegalAccessException JavaDoc e) {
132                 throw new IOException2(e);
133             } catch (NoSuchRealmException e) {
134                 throw new IOException2(e);
135             } catch (InvocationTargetException JavaDoc e) {
136                 throw new IOException2(e);
137             } catch (ClassNotFoundException JavaDoc e) {
138                 throw new IOException2(e);
139             }
140         }
141     }
142
143     /**
144      * {@link MavenBuildProxy} implementation.
145      */

146     private class ProxyImpl implements MavenBuildProxy, Serializable JavaDoc {
147         public <V, T extends Throwable JavaDoc> V execute(BuildCallable<V, T> program) throws T, IOException JavaDoc, InterruptedException JavaDoc {
148             return program.call(MavenBuild.this);
149         }
150
151         public FilePath getRootDir() {
152             return new FilePath(MavenBuild.this.getRootDir());
153         }
154
155         public FilePath getProjectRootDir() {
156             return new FilePath(MavenBuild.this.getParent().getRootDir());
157         }
158
159         public FilePath getArtifactsDir() {
160             return new FilePath(MavenBuild.this.getArtifactsDir());
161         }
162
163         public void setResult(Result result) {
164             MavenBuild.this.setResult(result);
165         }
166
167         public void registerAsProjectAction(MavenReporter reporter) {
168             MavenBuild.this.registerAsProjectAction(reporter);
169         }
170
171         private Object JavaDoc writeReplace() {
172             return Channel.current().export(MavenBuildProxy.class, new ProxyImpl());
173         }
174     }
175
176     private static final class getJavaExe implements Callable<String JavaDoc,IOException JavaDoc> {
177         public String JavaDoc call() throws IOException JavaDoc {
178             return new File JavaDoc(new File JavaDoc(System.getProperty("java.home")),"bin/java").getPath();
179         }
180     }
181
182     private class RunnerImpl extends AbstractRunner {
183         protected Result doRun(BuildListener listener) throws Exception JavaDoc {
184             // pick up a list of reporters to run
185
List JavaDoc<MavenReporter> reporters = new ArrayList JavaDoc<MavenReporter>();
186             getProject().getReporters().addAllTo(reporters);
187             for (MavenReporterDescriptor d : MavenReporters.LIST) {
188                 if(getProject().getReporters().contains(d))
189                     continue; // already configured
190
MavenReporter auto = d.newAutoInstance(getProject());
191                 if(auto!=null)
192                     reporters.add(auto);
193             }
194
195             // start maven process
196
ArgumentListBuilder args = buildMavenCmdLine(listener);
197
198             Channel channel = launcher.launchChannel(args.toCommandArray(),
199                 listener.getLogger(), getProject().getModuleRoot());
200
201             // Maven started.
202

203             ArgumentListBuilder margs = new ArgumentListBuilder();
204             margs.add("-N");
205             margs.addTokenized(getProject().getGoals());
206
207             try {
208                 return channel.call(new Builder(
209                     listener,new ProxyImpl(),
210                     reporters.toArray(new MavenReporter[0]), margs.toList()));
211             } finally {
212                 channel.close();
213             }
214         }
215
216         // UGLY....
217
private ArgumentListBuilder buildMavenCmdLine(BuildListener listener) throws IOException JavaDoc, InterruptedException JavaDoc {
218             MavenInstallation mvn = getParent().getParent().getMaven();
219             if(mvn==null) {
220                 listener.error("Maven version is not configured for this project. Can't determine which Maven to run");
221                 throw new RunnerAbortedException();
222             }
223
224             // find classworlds.jar
225
File JavaDoc bootDir = new File JavaDoc(mvn.getHomeDir(), "core/boot");
226             File JavaDoc[] classworlds = bootDir.listFiles(new FilenameFilter JavaDoc() {
227                 public boolean accept(File JavaDoc dir, String JavaDoc name) {
228                     return name.startsWith("classworlds") && name.endsWith(".jar");
229                 }
230             });
231             if(classworlds==null || classworlds.length==0) {
232                 listener.error("No classworlds*.jar found in "+bootDir+" -- Is this a valid maven2 directory?");
233                 throw new RunnerAbortedException();
234             }
235
236             boolean isMaster = getCurrentNode()==Hudson.getInstance();
237             FilePath slaveRoot=null;
238             if(!isMaster)
239                 slaveRoot = ((Slave)getCurrentNode()).getFilePath();
240
241             ArgumentListBuilder args = new ArgumentListBuilder();
242             args.add(launcher.getChannel().call(new getJavaExe()));
243
244             //args.add("-Xrunjdwp:transport=dt_socket,server=y,address=8002");
245

246             args.add("-cp");
247             args.add(
248                 (isMaster?Which.jarFile(Main.class).getAbsolutePath():slaveRoot.child("maven-agent.jar").getRemote())+
249                 (launcher.isUnix()?":":";")+
250                 classworlds[0].getAbsolutePath());
251             args.add(Main.class.getName());
252
253             // M2_HOME
254
args.add(mvn.getMavenHome());
255
256             // remoting.jar
257
args.add(Which.jarFile(Launcher.class).getPath());
258             // interceptor.jar
259
args.add(isMaster?
260                 Which.jarFile(hudson.maven.agent.PluginManagerInterceptor.class).getAbsolutePath():
261                 slaveRoot.child("maven-interceptor.jar").getRemote());
262             return args;
263         }
264
265         public void post(BuildListener listener) {
266             if(!getResult().isWorseThan(Result.UNSTABLE)) {
267                 // trigger dependency builds
268
DependencyGraph graph = Hudson.getInstance().getDependencyGraph();
269                 for( AbstractProject<?,?> down : getParent().getDownstreamProjects()) {
270                     if(graph.hasIndirectDependencies(getParent(),down))
271                         // if there's a longer dependency path to this project,
272
// then scheduling the build now is going to be a waste,
273
// so don't do that.
274
// let the longer path eventually trigger this build
275
continue;
276
277                     // if the downstream module depends on multiple modules,
278
// only trigger them when all the upstream dependencies are updated.
279
boolean trigger = true;
280                     
281                     AbstractBuild<?,?> dlb = down.getLastBuild(); // can be null.
282
for (MavenModule up : Util.filter(down.getUpstreamProjects(),MavenModule.class)) {
283                         MavenBuild ulb;
284                         if(up==getProject()) {
285                             // the current build itself is not registered as lastSuccessfulBuild
286
// at this point, so we have to take that into account. ugly.
287
if(getResult()==null || !getResult().isWorseThan(Result.UNSTABLE))
288                                 ulb = MavenBuild.this;
289                             else
290                                 ulb = up.getLastSuccessfulBuild();
291                         } else
292                             ulb = up.getLastSuccessfulBuild();
293                         if(ulb==null) {
294                             // if no usable build is available from the upstream,
295
// then we have to wait at least until this build is ready
296
trigger = false;
297                             break;
298                         }
299
300                         // if no record of the relationship in the last build
301
// is available, we'll just have to assume that the condition
302
// for the new build is met, or else no build will be fired forever.
303
if(dlb==null) continue;
304                         int n = dlb.getUpstreamRelationship(up);
305                         if(n==-1) continue;
306
307                         assert ulb.getNumber()>=n;
308
309                         if(ulb.getNumber()==n) {
310                             // there's no new build of this upstream since the last build
311
// of the downstream, and the upstream build is in progress.
312
// The new downstream build should wait until this build is started
313
if(isUpstreamBuilding(graph,up)) {
314                                 trigger = false;
315                                 break;
316                             }
317                         }
318                     }
319
320                     if(trigger) {
321                         listener.getLogger().println("Triggering a new build of "+down.getName());
322                         down.scheduleBuild();
323                     }
324                 }
325             }
326         }
327
328         /**
329          * Returns true if any of the upstream project (or itself) is either
330          * building or is in the queue.
331          * <p>
332          * This means eventually there will be an automatic triggering of
333          * the given project (provided that all builds went smoothly.)
334          */

335         private boolean isUpstreamBuilding(DependencyGraph graph, AbstractProject project) {
336             Set<AbstractProject> tups = graph.getTransitiveUpstream(project);
337             tups.add(project);
338             for (AbstractProject tup : tups) {
339                 if(tup.isBuilding() || tup.isInQueue())
340                     return true;
341             }
342             return false;
343         }
344     }
345 }
346
Popular Tags