目前Hudson和Jenkins基本上就是同一個東西,Hudson的插件能夠直接用於Jenkins。如下是參照Hudson wiki的extend plugin文檔和實際操做。 1、軟件 1. maven2 以上 2. JDK1.6 以上html
maven和JDK都須要加入到環境變量中,IDE能夠選eclipse
2、配置java
修改~/.m2/settings.xml或者maven/config/settings.xml <pluginGroups> <pluginGroup>org.eclipse.hudson.tools</pluginGroup> </pluginGroups> 這個主要做用是在咱們使用hudson的maven插件時使用縮略名(例如,使用hpi:create 代替org.jvnet.hudson.tools:maven-hpi-plugin:1.23:create)git
cmd或終端執行: mvn hpi:create 使用hudson hpi tool建立插件的主體結構,這過程當中maven須要下載相關的插件, 同時須要你輸入,groupId和artifactId這兩個字段 groupId是項目惟一的標識符,實際對應java的包的結構,是main目錄裏的java的目錄結構。 artifcatid 是項目惟一的標識符對應項目的名稱,就是項目根目錄的名稱 若是執行過程當中報錯,請按順序檢查下面幾項 a. 在~/.m2/settings.xml 文件中配置了pluginGroup org.eclipse.hudson.tools b. 在$M2_HOME/config/settings.xml中配置 pluginGroup org.eclipse.hudson.tools c. 本地 .m2/respoistory/org/eclipse/hudson/tools/maven-hpi-plugin 目錄移除就版本 作完以上步驟若是還失敗,能夠執行 mv org.eclipse.hudson.tools:maven-hpi-plugin:createweb
3、代碼目錄結構 pom.xml Maven Pom文件用於構建咱們的plugin src/main/java plugin的java源碼目錄 src/main/resources plugin jelly視圖(界面)源碼 src/main/webapp plugin的資源文件目錄,例如圖片和html文件shell
4、編譯和執行plugin hudson plugin雖然是個迷你的maven項目,但它是完整的。它能夠執行maven的全部功能。 mvn package 是構建項目命令。在第一次構建的時候會下載一堆依賴的包,目錄在~/.m2/repository 此命令先構建項目,而後打包成Hudson的插件安裝目錄,目標文件在工程target目錄下,.hpi結尾的jenkins插件和.jar結尾的jar包 mvn hpi:run 執行,一樣會下載一堆依賴包(不能連外網的能夠建立本地的maven倉庫,具體參照網絡上maven的文檔) 將當前開發的plugin安裝到Jetty容器中的Hudson, Hudson wiki的實例擴展的是build,因此當插件安裝後,在任務的構建步驟中會看到你的插件。數組
5、Hudson 擴展點 項目構建任務通常由如下幾步構成 1. SCM checkout 從代碼庫檢查代碼(CVS/SVN/git等) 2. Pre-build 構建開始前 3. Build wrapper 設置構建所需的環境 4. Builder runs 執行構建,例如執行Ant、Make、shell命令等 5. Recording 記錄構建結果和過程當中的輸出信息、測試結果等 6. Notification 發送構建結果通知,默認是郵件的方式網絡
Builder是負責構建的,Hudson提供的builder run step擴展點仍是叫Builder。Hudson默認提供Ant和Maven這兩個builders。 爲了讓Hudson識別一個class是擴展點,這個class必須從一個擴展點類繼承,必須實現擴展須要的抽象方法,在Hudson中定義這個class是一個擴展類。 代碼片斷: public class HelloWorkBuilder extends Builder{ HelloWorkBuilder從Builder類繼承,Builder類是擴展點Builder的接口。Builder類自己是從BuildStep類繼承的,BuildStep定義了perform抽象方法須要子類實現。 public boolean perform(AbstractBuild<?> ab,Launcher launcher,BuildListener bl)throws InterruptedException IOException; 下面的方法告訴Hudson這個類實現了某個擴展點,必須加上@Extension的註解 @Extension // this marker indicates Hudson that this is an implementation of an extension point. public static final class DescriptorImpl extends BuildStepDescriptor<Builder> { BuildStep.perform()這個抽象方法,提供三個訪問對象。 1. Build 這個表示job開始執行,通Build也能夠獲取三個對象 a. Project 這個Job對象 b. WorkSpace 構建的工做區 c. Result 構建步驟的執行結果 2. Launcher 用於啓動這個任務的構建,也能夠執行外部的可執行命令或程序 3. BuildListener 這是個接口,用於將構建步驟執行過程當中的信息在Hudson控制檯中顯示。 BuildListener.getLogger()方法從Build Listener 對象獲取將信息輸出到控制檯的Logger Hudson監控構建的執行和中止是經過下面兩個方法。 listener.started(buildStepCause); listener.finished(Result.SUCCESS); Hudson這樣設計的緣由是 1.Hudson須要將build構建過程信息輸出到控制檯 2.當構建失敗的時候Hudson必須中止後面構建的執行,並將這次構建標識爲FALIED。這是經過BuildListener向Hudson發送build的狀態消息實現的。 int r; r = launcher.launch().cmds(args).stdout(listener).join(); 執行外部命令並將執行信息輸出到控制檯。 Launcher能夠正確地啓動Master或Slave節點上的任務執行。經過Launcher返回的狀態碼能夠判斷執行是否成功。Launcher執行的標準輸出能夠被listener得到。
6、輸入擴展的配置信息 兩種方式配置擴展數據: 1. plugin局部配置 2. Hudson全局配置app
Hudson UI採用的是Jelly,Jelly是服務端的技術,經過解析將XML轉換爲客戶端的HTML/JavaScript和Ajax。Hudson內置了許多方便的Jelly tags。模型和tags屬性經過表達式語言Jexl綁定。 當將tags解析爲HTML和JavaScript的時,代碼中會包括模型對象的屬性字段。經過Jelly tags 就能夠避免書寫大量的HTML和JS代碼。Jelly文件已.jelly做爲擴展名,保存在插件的resources目錄中。Jelly文件的路徑和model類包的名稱要一致,這樣才能保證Hudson將模型和UI匹配上。 若是配置文件config.jelly就是plugin局部的配置。 若是配置文件global.jelly就是Hudson全局的配置。 字段的幫助信息經過help-{fieldName}.html 這是個純粹的HTML文件,fieldName要和jelly中字段名稱對應。 UI和Model字段的綁定 model中必需要有和UI中對應字段的屬性,例如 @DataBoundConstructor public HelloWorldBuilder(String name) { this.name = name; } @DataBoundConstructor 這個註解將屬性和UI的提交綁定,它的值只有經過頁面上的字段修改。 對應須要給一個getter方法用於在頁面展現 public String getName(){ return name; } UI字段值的校驗是經過doCheck+{nameOfTheField}做爲Ajax URL的一部分。
public FormValidation doCheckName(@QueryParameter String value) 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(); } FormValidation.error()返回錯誤信息。 FormValidation.warning()返回警告信息。eclipse
7、Hudson結構 代碼的根模塊就是Hudson,其餘子模塊都是從這裏開始的。Hudson全部的配置數據、構建記錄都是以文件的形式存儲在硬盤上的,默認的存儲路徑是$HUDSON_HOME若是沒有添加這個環境變量,默認存儲在當前用戶主目錄的.hudson目錄下。控制檯文件是以文本文件的格式存儲,項目的配置文件和構建記錄是以XML的格式存儲,其餘一些文件是以Java格式文件的形式存儲的。webapp
8、插件結構 plugin和jar的結構很類似
foo.hpi +- META-INF | +- MANIFEST.MF +- WEB-INF | +- classes | +- lib +- (static resources
插件的名稱以.hpi結尾,名稱要是獨一無二的以便和其餘plugin區別。這個結構也相似於war包,但沒有web.xml文件。
MANIFEST.MF文件主要包括的是插件的版本和插件編譯機器的一些信息。 WEB-INF/classes plugin的類文件和Jelly及Jelly用到tag文件 WEB-INF/lib 被plugin應用的.jar文件,CLassLoad加載的 static resources 存放HTML/CSS/JS/image等
9、插件開發新手常見問題 1. 如何執行shell命令 每一個構建都是經過調用perform(Build,Launcher,BuildListener)方法執行的。在這個方法中,經過Launcher能夠執行咱們定義的 命令。Launcher提供了不一樣參數的launch方法,詳細的參加API文檔。推薦使用 launcher.launch(cmd,env,out,workDir) cmd: 要執行的命令是個字符串 env: 環境變量是字符串數組,每一個元素是值鍵對("Varibale=Value") out: 命令的輸出,是OutputStream 輸出流。 workDir: 工做區目錄,FilePath
示例: launcher.launch("dir",new String[0],listener.getLogger(),build.getProject().getWorkspace()); Hudson構建成功或失敗是經過perform方法的返回值判斷的。大部分值是命令執行的返回碼或者拋出的異常。若是launch方法拋出 一個異常,意味執行出錯了將返回false構建失敗。若是launch方法沒有異常拋出,就要解析返回碼,獲取返回碼的方式是。 Proc proc = launcher.launch("dir",build.getEnvVars(),listener.getLogger(),build.getProject().getWorkspace()); int exitCode = proc.join(); 完成的代碼
public boolean perform(Build build, Launcher launcher, BuildListener listener) { try { Proc proc = launcher.launch("dir", build.getEnvVars(), listener.getLogger(),build.getProject().getWorkspace()); int exitCode = proc.join(); return exitCode == 0; } catch (IOException e) { e.printStackTrace(); listener.getLogger().println("IOException !"); return false; } catch (InterruptedException e) { e.printStackTrace(); listener.getLogger().println("InterruptedException!"); return false; } 2. 如何獲取圖片路徑,圖片存放的目錄是src/main/webapp public String getIconPath() { PluginWrapper wrapper = Hudson.getInstance().getPluginManager().getPlugin([YOUR-PLUGIN-MAIN-CLASS].class); return Hudson.getInstance().getRootUrl() + "plugin/"+ wrapper.getShortName()+"/"; }
3. HTML中添加空格 <st:nbsp/>
10、開發Hudson CLI 命令 有兩種方法在Hudson CLI中新增命令. 1. 經過在方法上加@CLIMethod註解定義新命令,能夠在註解中指定命令的名稱。定義CLI.command-name.shortDescription做爲命令的說明,經過CLICommand.getShortDescription()方法獲取。 示例代碼: public class AbstractItem { @CLIMethod(name="delete-job") public synchronized void delete() throws IOException, InterruptedException { performDelete();
if(this instanceof TopLevelItem) Hudson.getInstance().deleteJob((TopLevelItem)this); Hudson.getInstance().rebuildDependencyGraph(); } ...
} 這是方法的具體實現,不能判斷哪一個job會被刪除。經過定義CLI resolver,將它做爲參數的一部分,傳遞給調用此方法的對象實例。 @CLIResolver public static AbstractItem resolveForCLI( @Argument(required=true,metaVar="NAME",usage="Job name") String name) throws CmdLineException { AbstractItem item = Hudson.getInstance().getItemByFullName(name, AbstractItem.class); if (item==null) throw new CmdLineException(null,"No such job exists:"+name); return item; } 2. 擴展示有的CLICommand命令,做爲現有命令的子命令經過@Extension註解。詳細的參見javadoc of CLICommand