root/trunk/src/java/org/jcoderz/commons/taskdefs/LuntBuildTask.java

Revision 1011, 13.3 kB (checked in by amandel, 4 years ago)

Aligned svn keyword settings.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
Line 
1/*
2 * $Id$
3 *
4 * Copyright 2006, The jCoderZ.org Project. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 *    * Redistributions of source code must retain the above copyright
11 *      notice, this list of conditions and the following disclaimer.
12 *    * Redistributions in binary form must reproduce the above
13 *      copyright notice, this list of conditions and the following
14 *      disclaimer in the documentation and/or other materials
15 *      provided with the distribution.
16 *    * Neither the name of the jCoderZ.org Project nor the names of
17 *      its contributors may be used to endorse or promote products
18 *      derived from this software without specific prior written
19 *      permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS
25 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
31 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34package org.jcoderz.commons.taskdefs;
35
36import java.io.File;
37import java.io.FileNotFoundException;
38import java.io.FileOutputStream;
39import java.io.IOException;
40import java.io.InputStream;
41import java.io.OutputStream;
42import java.net.HttpURLConnection;
43import java.net.MalformedURLException;
44import java.net.URL;
45import java.util.ArrayList;
46import java.util.Iterator;
47import java.util.List;
48
49import org.apache.tools.ant.BuildException;
50import org.apache.tools.ant.Project;
51import org.apache.tools.ant.Task;
52import org.jcoderz.commons.util.IoUtil;
53
54import com.caucho.hessian.client.HessianProxyFactory;
55import com.luntsys.luntbuild.facades.BuildParams;
56import com.luntsys.luntbuild.facades.Constants;
57import com.luntsys.luntbuild.facades.ILuntbuild;
58import com.luntsys.luntbuild.facades.lb12.BuildFacade;
59import com.luntsys.luntbuild.facades.lb12.ScheduleFacade;
60
61/**
62 * Ant task to trigger a build on the Luntbuild system and download results
63 * afterwards.
64 *
65 * @author Albrecht Messner
66 */
67public class LuntBuildTask
68  extends Task
69{
70  /**
71   * Schedule start policy: allows multiple schedules to be running
72   * simultaneously.
73   */
74  public static final String START_MULTIPLE = "startMultiple";
75  /**
76   * Schedule start policy: skips execution if schedule is currently running.
77   */
78  public static final String SKIP_IF_RUNNING = "skipIfRunning";
79  /**
80   * Schedule start policy: fails this task if schedule is currently running.
81   */
82  public static final String FAIL_IF_RUNNING = "failIfRunning";
83 
84  private static final List POLICY_LIST = new ArrayList();
85  static
86  {
87    POLICY_LIST.add(START_MULTIPLE);
88    POLICY_LIST.add(SKIP_IF_RUNNING);
89    POLICY_LIST.add(FAIL_IF_RUNNING);
90  }
91 
92  /** Wait time between kicking off schedule and start polling status
93   *  and between schedule termination and log retrieval. */
94  private static final int WAIT_PERIOD = 5000;
95  /** Interval to poll build server. */
96  private static final int POLL_INTERVAL = 2000;
97
98  private String mLuntUrl;
99  private String mUserName;
100  private String mPassword;
101  private String mProjectName;
102  private String mScheduleName;
103  private String mStartPolicy = FAIL_IF_RUNNING;
104  private boolean mWaitForSchedule = true;
105  private String mToDir;
106  private final List mArtifacts = new ArrayList();
107
108  private ILuntbuild mLuntServer;
109 
110  /**
111   * @param luntUrl The luntUrl to set.
112   */
113  public void setLuntUrl (String luntUrl)
114  {
115    mLuntUrl = luntUrl;
116  }
117
118  /**
119   * @param userName The userName to set.
120   */
121  public void setUserName (String userName)
122  {
123    mUserName = userName;
124  }
125 
126  /**
127   * @param password The password to set.
128   */
129  public void setPassword (String password)
130  {
131    mPassword = password;
132  }
133
134  /**
135   * @param projectName The projectName to set.
136   */
137  public void setProjectName (String projectName)
138  {
139    mProjectName = projectName;
140  }
141 
142  /**
143   * @param scheduleName The scheduleName to set.
144   */
145  public void setScheduleName (String scheduleName)
146  {
147    mScheduleName = scheduleName;
148  }
149 
150  /**
151   * @param startPolicy The startPolicy to set.
152   */
153  public void setStartPolicy (String startPolicy)
154  {
155    if (!POLICY_LIST.contains(startPolicy))
156    {
157      throw new BuildException("Invalid start policy " + startPolicy
158           + ", must be one of " + POLICY_LIST);
159    }
160    mStartPolicy = startPolicy;
161  }
162 
163  /**
164   * @param waitForSchedule The waitForSchedule to set.
165   */
166  public void setWaitForSchedule (boolean waitForSchedule)
167  {
168    mWaitForSchedule = waitForSchedule;
169  }
170
171  /**
172   * @param toDir The toDir to set.
173   */
174  public void setToDir (String toDir)
175  {
176    mToDir = toDir;
177  }
178
179  /**
180   * Adds an artifact for retrieval.
181   * @param artifact the artifact to add
182   */
183  public void addArtifact (Artifact artifact)
184  {
185    mArtifacts.add(artifact);
186  }
187
188  /**
189   * Execute this ant task.
190   * @throws BuildException if a build error occurs
191   */
192  public void execute () throws BuildException
193  {
194    checkParameters();
195    try
196    {
197     
198      final ScheduleFacade schedule = getSchedule();
199      final boolean startSchedule;
200      if (schedule.getStatus() == Constants.SCHEDULE_STATUS_RUNNING)
201      {
202        if (mStartPolicy.equals(START_MULTIPLE))
203        {
204          startSchedule = true;
205        }
206        else if (mStartPolicy.equals(SKIP_IF_RUNNING))
207        {
208          startSchedule = false; 
209        }
210        else
211        {
212          throw new BuildException(
213            "Can't start build because schedule is already running");
214        }
215      }
216      else
217      {
218        startSchedule = true;
219      }
220     
221      if (startSchedule)
222      {
223        startSchedule();
224      }
225     
226    }
227    catch (BuildException x)
228    {
229      throw x;
230    }
231    catch (Exception x)
232    {
233      throw new BuildException(x);
234    }
235  }
236
237  private void startSchedule () throws InterruptedException, IOException
238  {
239    log("Starting build for " + mProjectName + "/" + mScheduleName
240        + " on server " + mLuntUrl, Project.MSG_INFO);
241    getLuntServer().triggerBuild(mProjectName, mScheduleName, getBuildParams());
242
243    if (mWaitForSchedule)
244    {
245      waitForSchedule();
246    }
247  }
248
249  private void waitForSchedule () throws InterruptedException, IOException
250  {
251    Thread.sleep(WAIT_PERIOD);
252    log("Waiting for build "
253        + getLuntServer().getLastBuild(mProjectName, mScheduleName).getVersion()
254        + " to finish");
255 
256    while (getSchedule().getStatus() == Constants.SCHEDULE_STATUS_RUNNING)
257    {
258      log("Schedule running", Project.MSG_VERBOSE);
259      Thread.sleep(POLL_INTERVAL);
260    }
261    final int termStatus = getSchedule().getStatus();
262    switch (termStatus)
263    {
264      case Constants.SCHEDULE_STATUS_SUCCESS:
265        log("LuntBuild schedule " + mProjectName + "/" + mScheduleName
266            + " succeeded");
267        dumpLogFile();
268        retrieveArtifacts();
269        break;
270      case Constants.SCHEDULE_STATUS_FAILED:
271        log("LuntBuild schedule " + mProjectName + "/" + mScheduleName
272            + " FAILED");
273        dumpLogFile();
274        throw new BuildException("LuntBuild schedule " + mProjectName + "/"
275            + mScheduleName + " FAILED");
276      default:
277        throw new BuildException("Unexpected status for schedule "
278            + mProjectName + "/" + mScheduleName + ": " + termStatus);
279    }
280  }
281
282  private ScheduleFacade getSchedule () throws MalformedURLException
283  {
284    return getLuntServer().getScheduleByName(mProjectName, mScheduleName);
285  }
286
287  /**
288   * @throws IOException
289   *
290   */
291  private void retrieveArtifacts () throws IOException
292  {
293    final BuildFacade currentBuild
294      = getLuntServer().getLastBuild(mProjectName, mScheduleName);
295    final String buildLogUrl = currentBuild.getBuildLogUrl();
296    final String path = buildLogUrl.substring(0, buildLogUrl.lastIndexOf('/'));
297    final String artifactsBaseUrl = path + "/artifacts/";
298    log("Artifacts base URL: " + artifactsBaseUrl, Project.MSG_VERBOSE);
299    HttpURLConnection.setFollowRedirects(true);
300   
301    for (final Iterator it = mArtifacts.iterator(); it.hasNext(); )
302    {
303      final String artifactName = ((Artifact) it.next()).getName();
304      final File outputFile = new File(new File(mToDir), artifactName);
305      if (outputFile.exists())
306      {
307        throw new BuildException("Output file " + outputFile
308            + " already exists");
309      }
310      final String artifactUrl = artifactsBaseUrl + artifactName;
311      log("Retrieving artifact " + artifactName);
312      log("Retrieving from URL: " + artifactUrl, Project.MSG_VERBOSE);
313      log("Writing to file: " + mToDir + File.separator + outputFile,
314          Project.MSG_VERBOSE);
315      final HttpURLConnection con
316          = (HttpURLConnection) new URL(artifactUrl).openConnection();
317      con.setDoOutput(true);
318      con.addRequestProperty("Keep-alive", "false");
319     
320      con.connect();
321      if (con.getResponseCode() != HttpURLConnection.HTTP_OK)
322      {
323        throw new BuildException("Failed while retrieving artifact "
324            + artifactUrl + ": " + con.getResponseMessage());
325      }
326     
327      writeArtifactToFile(outputFile, con);
328    }
329  }
330
331  private void writeArtifactToFile (
332      final File outputFile, final HttpURLConnection con)
333    throws IOException, FileNotFoundException
334  {
335    InputStream artifactInput = null;
336    OutputStream artifactOutput = null;
337    try
338    {
339      artifactInput = con.getInputStream();
340      artifactOutput = new FileOutputStream(outputFile);
341     
342      IoUtil.copy(artifactInput, artifactOutput);
343    }
344    finally
345    {
346      IoUtil.close(artifactInput);
347      IoUtil.close(artifactOutput);
348    }
349  }
350
351  /**
352   * @throws InterruptedException
353   * @throws IOException
354   *
355   */
356  private void dumpLogFile () throws InterruptedException, IOException
357  {
358    Thread.sleep(WAIT_PERIOD);
359    final BuildFacade currentBuild
360      = getLuntServer().getLastBuild(mProjectName, mScheduleName);
361
362    final String buildLogUrlHtml = currentBuild.getBuildLogUrl();
363    log("Build log URL (HTML format): " + buildLogUrlHtml, Project.MSG_VERBOSE);
364    final String buildLogUrlTxt = buildLogUrlHtml.substring(0,
365        buildLogUrlHtml.lastIndexOf(".html")) + ".txt";
366    log("Build log URL (Text format): " + buildLogUrlTxt, Project.MSG_VERBOSE);
367
368    final URL buildLog = new URL(buildLogUrlTxt);
369    final HttpURLConnection con = (HttpURLConnection) buildLog.openConnection();
370    log("Got HTTP code " + con.getResponseCode(), Project.MSG_VERBOSE);
371
372    InputStream is = null;
373    try
374    {
375      is = con.getInputStream(); 
376      final byte[] data = IoUtil.readFully(is);
377      final String buildLogData = new String(data);
378      log("===== START Build log =====", Project.MSG_INFO);
379      log(buildLogData, Project.MSG_INFO);
380      log("===== END Build log =====", Project.MSG_INFO);
381    }
382    finally
383    {
384      IoUtil.close(is);
385    }
386  }
387
388  private BuildParams getBuildParams ()
389  {
390    final BuildParams params = new BuildParams();
391   
392    params.setBuildNecessaryCondition("always");
393    params.setBuildType(Constants.BUILD_TYPE_CLEAN);
394    params.setLabelStrategy(Constants.LABEL_NONE);
395    params.setNotifyStrategy(Constants.NOTIFY_NONE);
396    params.setPostbuildStrategy(Constants.POSTBUILD_NONE);
397    // params.setScheduleId()
398    params.setTriggerDependencyStrategy(
399        Constants.TRIGGER_NONE_DEPENDENT_SCHEDULES);
400   
401    return params;
402  }
403 
404  /**
405   * @param luntUrl
406   */
407  private void checkNotNull (Object obj, String name) 
408  {
409    if (obj == null)
410    {
411      throw new BuildException("Parameter " + name + " missing");
412    }
413  }
414
415  /**
416   *
417   */
418  private void checkParameters ()
419  {
420    checkNotNull(mLuntUrl, "luntUrl");
421    checkNotNull(mUserName, "userName");
422    checkNotNull(mPassword, "password");
423    checkNotNull(mProjectName, "projectName");
424    checkNotNull(mScheduleName, "scheduleName");
425    if (mArtifacts.size() > 0)
426    {
427      if (mToDir == null)
428      {
429        throw new BuildException("'toDir' must be set if artifacts are set");
430      }
431      else
432      {
433        AntTaskUtil.ensureDirectory(new File(mToDir));
434      }
435      if (!mWaitForSchedule)
436      {
437        throw new BuildException(
438          "Can't retrieve artifacts when waitForBuild == false");
439      }
440    }
441  }
442
443  private ILuntbuild getLuntServer () throws MalformedURLException
444  {
445    if (mLuntServer == null)
446    {
447      final HessianProxyFactory factory = new HessianProxyFactory();
448      factory.setUser(mUserName);
449      factory.setPassword(mPassword);
450      mLuntServer = (ILuntbuild) factory.create(ILuntbuild.class, mLuntUrl);
451    }
452    return mLuntServer;
453  }
454 
455  /**
456   * Represents an artifact to retrieve. This class basically consists of a
457   * name.
458   */
459  public static final class Artifact
460  {
461    private String mName;
462   
463    /**
464     * @param name The name to set.
465     */
466    public void setName (String name)
467    {
468      mName = name;
469    }
470   
471    /**
472     * @return Returns the name.
473     */
474    public String getName ()
475    {
476      return mName;
477    }
478  }
479}
Note: See TracBrowser for help on using the browser.