天天看點

Hudson插件開發簡介

近期接觸到Hudson的插件開發,覺得還是比較好玩的,但目前這方面的資料而非常之少,于是将自己一些學習資料簡單歸納了一下,算是抛磚引玉吧

一、關于Hudson(又名Jenkins)

     簡單說,它就是一個純java實作開源的持續內建軟體,一般搭載在web容器上用,有單獨war包的形式,也有内嵌jetty伺服器的安裝包。在持續內建領域中相當出名,而其中最大的因素則源自其可伸縮的插件機制和強大的插件支援,目前已有超過400多款支援不同持續內建特性的免費可用插件。Hudson的插件機制允許開發者通過定制來做很多事情,包括自定義建構步驟、結果的展示方式、通知方式、與SCM系統的內建、測試和分析等等。

二、插件開發

    Hudson是基于maven的項目,其插件開發也離不開maven的支援,是以有必要稍微了解下maven是怎麼用的:

http://maven.apache.org/

 。此外,hudson提供了hpi插件來實作其插件開發。The Hudson HPI (Hudson Plug-in Interface) tool, 是一個Maven插件,可幫助開發者建立、建構、運作和調試Hudson插件項目

     安裝完maven之後便可以開始玩了:

    1    建立項目

           找一個幹淨的地方,執行一下:mvn  hpi:create   此時maven會檢查目前是否安裝了hpi插件(hudson插件開發的maven插件,全稱為hudson plugin Interface),如果沒有将先下載下傳安裝;如果報錯提示 無法識别 hpi指令或别名,那是maven找不到插件了,打開maven的setting.xml檔案,添加maven插件查找路徑:

<pluginGroups>
    <pluginGroup>org.jenkins-ci.tools</pluginGroup>
</pluginGroups>      

建立項目成功之後,一個helloworld的骨架項目結構如下:

pom.xml -       Maven POM file which is used to build your plugin
src/main/java - java源檔案
src/main/resources - 插件的Jelly 視圖檔案
src/main/webapp - 插件的靜态資源 such as images and HTML files.      

這是一标準的maven項目結構,緊接着執行一下打包試試: mvn package,在target目錄下發現插件打成了jar包,另外還有一個hpi檔案。而hpi檔案便是hudson的标準插件格式,可以直接安裝到已運作的hudson程式中(系統設定-插件-進階-上傳插件)

此後執行hpi:run 可以開啟一個test模式的hudson,其内置安裝了目前開發中的插件,通過localhost:8080可以通路。hpi:run 指令包含了幾個子task:啟動jetty伺服器,添加hudson為web項目、安裝目前插件。 

插件的work子目錄成為了目前Hudson的Home目錄,work/plugins子目錄則包含了一些hpi檔案(對應于目前hudson中的插件清單);仔細點可以發現目前的目錄中
有一個hpl為字尾的檔案,其對應了目前的helloworld插件項目;這是一個簡單的文本檔案,其内部描述了與目前項目建構相關的檔案(包括classes、jars和resources)每次執行hpi:run指令時,HPI工具都會生成該檔案,而Hudson解釋該檔案并直接加載該插件(而不需要把插件打成hpi的包)
此種方式也友善于部署期間的調試。
      

    2   擴充功能

         生成的helloworld項目預設添加了一個Builder的擴充類(名為HelloWorldBuilder)。Hudson的擴充機制與Eclipse有些相似,也有擴充點和擴充的概念,擴充點即是一組接口,其允許第三方開發者實作該接口(提供擴充實作)來增強系統的功能。下面的應用将圍繞HelloWorldBuilder進行說明:

一次建構過程通常包括:      
SCM checkout - check out出源碼
   Pre-build    - 預編譯
   Build wrapper  -準備建構的環境,設定環境變量等
   Builder runs   - 執行建構,比如調用calling Ant, Make 等等
   Recording    - 記錄輸出,如測試結果
   Notification    - 通知成員      

 jenkins建構器的擴充點通過Builder接口聲明,在預設情況下,jenkins自帶了Ant和Maven的builder擴充實作(建立一個job,可以添加ant build step...)

生成的HelloWorld類如下:

public class HelloWorldBuilder extends Builder {
   //建構的執行通過實作perform方法來進行自定義
    public boolean perform(AbstractBuild<?> ab, Launcher launcher, BuildListener bl) 
                                             throws InterruptedException, IOException;{
    ..}

   /*
     Build參數是描述了目前任務的一次建構,通過它可以通路到一些比較重要的模型對象如:
        1 Project    目前項目的對象
       2 workspace  建構的工作空間
        3 Result    目前建構步驟的結果
     Launcher 用于啟動建構
     BuildListener  該接口用于檢查建構過程的狀态(開始、失敗、成功..)
        通過它可以在建構過程中發送一些控制台資訊給Hudson
    */
     perform方法的傳回值告訴jenkins目前步驟是否成功,如果失敗了Hudson将放棄後續的步驟。
     此外有一個内部靜态類,該類通過@Extension聲明告訴Hudson,這是一個擴充實作
     @Extension // This indicates to Jenkins that this is an implementation of an extension point.
      public static final class DescriptorImpl extends BuildStepDescriptor<Builder> {
            public boolean isApplicable(Class<? extends AbstractProject> aClass) {
            // 是否對所有項目類型可用
            return true;
             }
/**          
* builder的顯示名.
*/         
    public String getDisplayName() {             
        return "Say hello world";         
    } 
}
}      

 關于建構方法(Perform)的一個實作樣例:

List<Cause> buildStepCause = new ArrayList();
 buildStepCause.add(new Cause() {
   public String getShortDescription() {
     return "Build Step started by Hello Builder";
   }
 });
 listener.started(buildStepCause);     //向hudson控制台輸出日志
 
 ArgumentListBuilder args = new ArgumentListBuilder();
 if (launcher.isUnix()) {
   args.add("/bin/ls");
   args.add("-la");
 } else {
   args.add("dir"); //Windows
 }
 String homeDir = System.getProperty("user.home");
 args.add(homeDir);
 try {
   int r;
   //調用外部指令,cmds傳入指令和參數;stdout方法将标準輸出重定向到listener的流中(輸出到hudson的web控制台),join等待完成并傳回結果
   //可以看到launcher是相當強大的.. 
   r = launcher.launch().cmds(args).stdout(listener).join();

   if (r != 0) {
     listener.finished(Result.FAILURE);
     return false;
   }
 } catch (IOException ioe) {
   ioe.printStackTrace(listener.fatalError("Execution" + args + "failed"));  //列印異常,标記結果
   listener.finished(Result.FAILURE);           
   return false;
 } catch (InterruptedException ie) {
   ie.printStackTrace(listener.fatalError("Execution" + args + "failed"));
   listener.finished(Result.FAILURE);
   return false;
 }

 listener.finished(Result.SUCCESS);      

     3   添加配置

     Jenkins使用了Jelly頁面渲染技術,這是一個基于XML的服務端頁面渲染引擎,其将基于Jelly的xml标簽轉換為對應的Html标簽并輸出到用戶端。模型對象的資訊通過Jexl表達式被傳遞到頁面上(相當于Jsp的JSTL)。jelly檔案以.jelly為字尾,在hudson中使用類全名的形式來查找模型類對應的jelly頁面檔案,如名為org.sample.hudson.HelloWorldBuilder的類,其對應的頁面檔案應該存在于resource目錄的以下位置中(以classpath為根)

org/sample/hudson/HelloWordBuilder
      

此外hudson通過固定的命名方式來确定頁面檔案屬于局部配置還是全局配置:config.jelly提供局部配置;global.jelly提供全局配置

     A  局部配置詳解

         config.jelly 的内容将被包含在擴充功能的配置中

                以HelloWorldBuilder為例,其擴充的是一個Hudson Job的建構步驟,那麼config.jelly  提供的便是該建構步驟對應的配置内容

   樣例說明:

<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" 
xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
   <f:entry title="名稱" field="name">
         <f:textbox />
  </f:entry>
</j:jelly>      

   entry 表示用于互動的html表單域,title将作為表單域label的值

   textbox 表示簡單的渲染一個text

   允許為表單域增加幫助說明(在頁面上對應于文本框後面出現問号按鈕,一點選可出現提示):

   在同名目錄下建立help-{fileName}.html,在該檔案中添加幫助内容;幫助内容允許是動态的,即可以從模型中拉取資訊進行顯示,這需要将html字尾改為jelly,而檔案的形式大緻如下:

<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define">
  <div>
   Welcome to ${app.displayName}. //應用的Display名稱(一般就是Hudson)
   Enter your name in the Field.
  </div>
</j:jelly>      

jelly字尾的檔案在渲染時會交給jelly引擎執行,因而支援動态顯示能力。

jexl表達式替換模型資料的規則:${modelName.attrName}  調用對應子產品的get**方法獲得值

關于内置模型對象的說明:

1  app  Hudson應用程式對象 如上面的displayName例子
2  it   目前UI所屬的模型對象,在上面的Builder擴充例子中則對應于HelloWorldBuilder對象
       ${it.name} 對應于builder的getName()方法
3  h   一個全局的工具類,提供靜态工具方法         

    通過Job配置界面儲存之後,hudson會建立builder對象,并将表單值通過構造器注入,構造器聲明如下:

@DataBoundConstructor
public HelloWorldBuilder(String name) {
   this.name = name;
}      

而builder必須提供getName方法,這樣可将配置到config.xml中(hudson使用xml存儲配置資訊)在重新打開job配置時可自動填值

  關于表單值的校驗,以文本域name為例:

  jelly在渲染時自動增加了ajax校驗的功能腳本,于是文本框失去焦點時會往服務端發送校驗請求:

GET /job/TestProject/descriptorByName/org.sample.hudson.HelloWorldBuilder/checkName?value=xy HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:6.0.1) Gecko/20100101 Firefox/6.0.1
Accept: text/javascript, text/html, application/xml, text/xml, */*      

此後Hudson找到HelloWorldBuilder,查找doCheckName方法(在Descriptor類中查找),如果沒有找到則忽略該請求

否則傳回檢查結果在前端展示。doCheckName方法的樣例實作:

public FormValidation doCheckName(@QueryParameter String value)  //@QueryParameter注解表示注入http請求參數
                                      throws IOException, ServletException {
        if (value.length() == 0) {
            return FormValidation.error("Please set a name");
        }
        if (value.length() < 4) {
            return FormValidation.warning("Isn't the name too short?");
        }
        return FormValidation.ok();
}      

  B  全局配置詳解

         如上所述,global.jelly 為全局配置頁面,示例:

<f:section title="Hello World Builder">
   <f:entry title="French" description="Check if we should say hello in French"
     help="/plugin/javaone-sample/help-globalConfig.html">
      <f:checkbox name="hello_world.useFrench" checked="${descriptor.useFrench()}" />
   </f:entry>
</f:section>      
//在jenkins的系統設定中可以找到相應的配置段落。
      

其中${descriptor.useFrench()} 調用builder的(getDescriptor)得到Descriptor對象,調用其useFrench方法進行取值;

help聲明了幫助内容文檔位置;

在每次儲存全局配置時,jenkins都會調用調用該descriptor對象,并調用其configure方法,可以實作該方法并提供自己的定制:

public boolean configure(StaplerRequest req, JSONObject formData) throws FormException {
       useFrench = formData.getBoolean("useFrench");
       save();
       return super.configure(req,formData);
 }      
//save方法用于将目前Descriptor所提供的配置持久化(通過get**方法)
//load方法用于将持久化的資訊注入到目前Descriptor中(通過set**方法)
是以為了使save和load能正常工作,需要提供配置項的get/set方法
使用場景:在構造器方法中執行load,将全局配置諸如到目前對象中;配置檔案通過表達式調用descriptor的get**方法顯示到前端;在儲存系統配置時,configure方法中将配置讀入目前對象,并持久化。      

三、其他資料

Hudson的擴充點JavaDoc:

http://wiki.jenkins-ci.org/display/JENKINS/Extension+points

Hudson插件開發簡單介紹:

https://wiki.jenkins-ci.org/display/~martino/2011/10/27/The+JenkinsPluginTotallySimpelGuide

實作報告釋出擴充(Publisher)的介紹:

http://www.theserverlabs.com/blog/2008/09/24/developing-custom-hudson-plugins-integrate-with-your-own-applications/

一個Html報告釋出擴充的例子(基于Selenium的報告釋出擴充):

https://github.com/jenkinsci/seleniumhtmlreport/blob/master/src/main/java/org/jvnet/hudson/plugins/seleniumhtmlreport/SeleniumHtmlReportPublisher.java

Hudson插件大全介紹 -

http://wiki.hudson-ci.org/display/HUDSON/All+Plugins+by+Topic

原文位址:

http://blog.csdn.net/littleatp2008/article/details/7001793
Hudson插件開發簡介

作者:

zale

出處:

http://www.cnblogs.com/littleatp/

, 如果喜歡我的文章,請

關注我的公衆号

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出

原文連結

 如有問題, 可留言咨詢.