持续集成(CI)是将多个团队成员贡献的代码频繁持续的构建并给与反馈,而不必将寻找和修复缺陷的过程放在开发后期。在典型的持续集成周期中,代码首先被周期性的从版本控制服务器(如CVS或Subversion)中更新下来,随后调用自动化编译脚本(如Ant或Maven)编译,并运行所有测试用例,给出结果分析的报告。

java.net上的开源工具Hudson便是一款优秀的持续集成工具,目前的发展速度很快,并且在吸收了众多CI服务器的优点和长处。在自动构建工具支持方面,Hudson可以同Maven紧密集成,并基于Maven依赖图确定需要重新编译的项目。

作为CI服务器,在Hudson项目的官方站点下载War格式的安装包后,可以方便的部署在GlassFish或Tomcat容器之上。在Hudson中,配置一个新的项目也十分快速直观,在新建 Hudson工程时填写名称和描述信息、设定检查代码储存库的时间间隔、指定本地代码编译路径、指定储存库的访问路径和授权用户、填写工程和分支名称以及构建完成后的动作等,就完成了整个CI工程的配置工作。

在结果展示方面,用户可以在编译日志中查看Hudson通过不同颜色标记列出的信息。Hudson还提供了易用的报表功能,并拥有强大的插件支持,如具有能显示测试结果趋势等信息的插件,以及随时间轴跟踪Bugs并监控代码覆盖的插件。在通知机制方面,Hudson可以方便的与用户建立联系,Hudson 提供了电子邮件通知选项,还支持以RSS方式输出报错通知。

目前,包括NetBeans项目本身以及Ruby IDE在内的众多项目都在使用Hudson实现持续集成,更多Hudson的相关内容,可以在Hudson的Wiki中查看并了解详细的使用方式。

同时Hudson支持插件扩展,你可以通过其的插件管理功能从网络上下载你所需要的插件,也可以自己为所在的工作团队创建符合需要的插件。下面我就来介绍下如何开发一个Hudson的插件

首先你要有Maven 2和JDK1.6以上,这是必须的。然后在你的Maven 2的setting.xml 文件中加入下列代码

Xml代码 
  1. 1. <settings>
  2. 2. ..
  3. 3. <profiles>
  4. 4.   <profile>
  5. 5.     <id>hudson</id>
  6. 6.
  7. 7.     <activation>
  8. 8.       <activeByDefault />
  9. 9.     </activation>
  10. 10.
  11. 11.    <pluginRepositories>
  12. 12.      <pluginRepository>
  13. 13.        <id>m.g.o-public</id>
  14. 14.        <url>http://maven.glassfish.org/content/groups/public/</url>
  15. 15.      </pluginRepository>
  16. 16.    </pluginRepositories>
  17. 17.    <repositories>
  18. 18.      <repository>
  19. 19.        <id>m.g.o-public</id>
  20. 20.        <url>http://maven.glassfish.org/content/groups/public/</url>
  21. 21.      </repository>
  22. 22.    </repositories>
  23. 23.  </profile>
  24. 24.</profiles>
  25. 25.
  26. 26.<activeProfiles>
  27. 27.  <activeProfile>hudson</activeProfile>
  28. 28.</activeProfiles>
  29. 29.
  30. 30.<pluginGroups>
  31. 31.  <pluginGroup>org.jvnet.hudson.tools</pluginGroup>
  32. 32.</pluginGroups>
  33. 33...
  34. 34.</settings>

这样会将你的Maven指向有着Hudson-related Maven plugins的仓库,而且允许你使用Hudson Maven plugins的短名字来调用相关的插件(例如:hpi:create 代替org.jvnet.hudson.tools:maven-hpi-plugin:1.23:create)。

接着在CMD中输入

Java代码 
  1. 1. mvn hpi:create

之后会问你一些如groupId和artifactId之类的问题,按照需要来填写就好了。

完成后计算机会自动的创建了一个项目,里面有一些模板代码,可供你学习如何开始写一个Hudson的插件,后面的代码全部来自模版代码。如果你需要在Eclipse里编辑插件可以执行

Java代码 
  1. 1. mvn -DdownloadSources=true eclipse:eclipse

然后你就可以在Eclipse中导入这个项目并开始开发了。

经过前一篇文章的步骤,在我们的指定目录下已经生成了一个Hudson 插件的项目文件夹。在Eclipse中导入这个项目,我们可以看见项目有如下的结构:

+ src 
    + main 
        + java 
             +  full.package.name 
                    +- HelloWorldBuilder.java 
                    +- PluginImpl.java 
+resources 
             +  full.package.name 
                    +- config.jelly 
                    +- global.jelly 
                +- index.jelly 
        + webapp 
            +- help-globalConfig.html 
            +- help-projectConfig.html

PluginImpl.java: 
这个类是用于插件注册扩展点(Extension points)的一个类,在这里,我们注册了一个Builder。其实在Hudson 中有很多不同种类的扩展点,比如Publisher、Recorder 等等。详细的说明可以参考Hudson 的网站。

Java代码 
  1. 1. public class PluginImpl extends Plugin {
  2. 2.     public void start() throws Exception {
  3. 3.
  4. 4.         BuildStep.BUILDERS.add(HelloWorldBuilder.DESCRIPTOR);
  5. 5.     }
  6. 6. }
HelloWorldBuilder.java 

这个类就是具体实现某一扩展点的一个类,在这里由于要扩展Builder这个扩展点,所以继承了 Builder 这个类

Java代码 
  1. 1. public class HelloWorldBuilder extends Builder {
  2. 2.
  3. 3.     private final String name;
  4. 4.
  5. 5.     @DataBoundConstructor
  6. 6.     public HelloWorldBuilder(String name) {
  7. 7.         this.name = name;
  8. 8.     }
  9. 9.
  10. 10.    /**
  11. 11.     * We'll use this from the <tt>config.jelly</tt>.
  12. 12.     */
  13. 13.    public String getName() {
  14. 14.        return name;
  15. 15.    }
  16. 16.
  17. 17.    public boolean perform(Build build, Launcher launcher, BuildListener listener) {
  18. 18.        // this is where you 'build' the project
  19. 19.        // since this is a dummy, we just say 'hello world' and call that a build
  20. 20.
  21. 21.        // this also shows how you can consult the global configuration of the builder
  22. 22.        if(DESCRIPTOR.useFrench())
  23. 23.            listener.getLogger().println("Bonjour, "+name+"!");
  24. 24.        else
  25. 25.            listener.getLogger().println("Hello, "+name+"!");
  26. 26.        return true;
  27. 27.    }
  28. 28.
  29. 29.    public Descriptor<Builder> getDescriptor() {
  30. 30.        // see Descriptor javadoc for more about what a descriptor is.
  31. 31.        return DESCRIPTOR;
  32. 32.    }
  33. 33.
  34. 34.    /**
  35. 35.     * Descriptor should be singleton.
  36. 36.     */
  37. 37.    public static final DescriptorImpl DESCRIPTOR = new DescriptorImpl();
  38. 38.
  39. 39.    /**
  40. 40.     * Descriptor for {@link HelloWorldBuilder}. Used as a singleton.
  41. 41.     * The class is marked as public so that it can be accessed from views.
  42. 42.     *
  43. 43.     * <p>
  44. 44.     * See <tt>views/hudson/plugins/hello_world/HelloWorldBuilder/*.jelly</tt>
  45. 45.     * for the actual HTML fragment for the configuration screen.
  46. 46.     */
  47. 47.    public static final class DescriptorImpl extends Descriptor<Builder> {
  48. 48.        /**
  49. 49.         * To persist global configuration information,
  50. 50.         * simply store it in a field and call save().
  51. 51.         *
  52. 52.         * <p>
  53. 53.         * If you don't want fields to be persisted, use <tt>transient</tt>.
  54. 54.         */
  55. 55.        private boolean useFrench;
  56. 56.
  57. 57.        DescriptorImpl() {
  58. 58.            super(HelloWorldBuilder.class);
  59. 59.        }
  60. 60.
  61. 61.        /**
  62. 62.         * Performs on-the-fly validation of the form field 'name'.
  63. 63.         *
  64. 64.         * @param value
  65. 65.         *      This receives the current value of the field.
  66. 66.         */
  67. 67.        public void doCheckName(StaplerRequest req, StaplerResponse rsp, @QueryParameter final String value) throws IOException, ServletException {
  68. 68.            new FormFieldValidator(req,rsp,null) {
  69. 69.                /**
  70. 70.                 * The real check goes here. In the end, depending on which
  71. 71.                 * method you call, the browser shows text differently.
  72. 72.                 */
  73. 73.                protected void check() throws IOException, ServletException {
  74. 74.                    if(value.length()==0)
  75. 75.                        error("Please set a name");
  76. 76.                    else
  77. 77.                    if(value.length()<4)
  78. 78.                        warning("Isn't the name too short?");
  79. 79.                    else
  80. 80.                        ok();
  81. 81.
  82. 82.                }
  83. 83.            }.process();
  84. 84.        }
  85. 85.
  86. 86.        /**
  87. 87.         * This human readable name is used in the configuration screen.
  88. 88.         */
  89. 89.        public String getDisplayName() {
  90. 90.            return "Say hello world";
  91. 91.        }
  92. 92.
  93. 93.        public boolean configure(StaplerRequest req, JSONObject o) throws FormException {
  94. 94.            // to persist global configuration information,
  95. 95.            // set that to properties and call save().
  96. 96.            useFrench = o.getBoolean("useFrench");
  97. 97.            save();
  98. 98.            return super.configure(req);
  99. 99.        }
  100. 100.
  101. 101.              /**
  102. 102.               * This method returns true if the global configuration says we should speak French.
  103. 103.               */
  104. 104.              public boolean useFrench() {
  105. 105.                  return useFrench;
  106. 106.              }
  107. 107.          }
  108. 108.      }

是不是看得有点头晕,呵呵,下面我来逐步分析这些代码

Java代码 
  1. 1. @DataBoundConstructor
  2. 2.     public HelloWorldBuilder(String name) {
  3. 3.         this.name = name;
  4. 4.     }
  5. 5.
  6. 6.     /**
  7. 7.      * We'll use this from the <tt>config.jelly</tt>.
  8. 8.      */
  9. 9.     public String getName() {
  10. 10.        return name;
  11. 11.    }

这段代码用于构造这个Bulider并且从相应的config.jelly中获取相应的参数。Hudson使用了一种叫structured form submission的技术,使得可以使用这种方式活动相应的参数。

Java代码 
  1. 1. public boolean perform(Build build, Launcher launcher, BuildListener listener) {
  2. 2.         // this is where you 'build' the project
  3. 3.         // since this is a dummy, we just say 'hello world' and call that a build
  4. 4.
  5. 5.         // this also shows how you can consult the global configuration of the builder
  6. 6.         if(DESCRIPTOR.useFrench())
  7. 7.             listener.getLogger().println("Bonjour, "+name+"!");
  8. 8.         else
  9. 9.             listener.getLogger().println("Hello, "+name+"!");
  10. 10.        return true;
  11. 11.    }

方法perform()是个很重要的方法,当插件运行的的时候这个方法会被调用。相应的业务逻辑也可以在这里实现。比如这个perform()方法就实现了怎么说 “Hello”

接下来,在HelloBuilder 这个类里面有一个叫 DescriptorImpl 的内部类,它继承了Descriptor。在Hudson 的官方说明文档里说Descriptor包含了一个配置实例的元数据。打个比方,我们在工程配置那里对插件进行了配置,这样就相当于创建了一个插脚的实例,这时候就需要一个类来存储插件的配置数据,这个类就是Descriptor。

Java代码 
  1. 1. public String getDisplayName() {
  2. 2.             return "Say hello world";
  3. 3.         }

如上面的代码,可以在Descriptor的这个方法下设置插件在工程配置页面下现实的名字

Java代码 
  1. 1. public boolean configure(StaplerRequest req, JSONObject o) throws FormException {
  2. 2.             // to persist global configuration information,
  3. 3.             // set that to properties and call save().
  4. 4.             useFrench = o.getBoolean("useFrench");
  5. 5.             save();
  6. 6.             return super.configure(req);
  7. 7.         }

如同注释属所说,这个方法用于将全局配置存储到项目中

注意点:

HUDSON_HOME:

Hudson需要一个位置来进行每次构建,保留相关的配置信息,以及保存测试的结果,这就是在部署好了Hudson环境以后,系统就会自动在当前用户下新建一个.hudson,在linux下如:~/.hudson,我们有三种方式来改变这个路径:

1.  在启动servlet容器之前,设置:“HUDSON_HOME”环境变量,指向你需要设定的目录

2.  在servlet容器之中,设定系统属性

3.  设置一个JNDI的环境实体<env-entry>“HUDSON_HOME”指向您所想要设定的目录

目前我们在glassfish中设置jvm-option的方式属于第二种。

当我们设置好这个变量以后想要换个目录,但又不想丢掉以前的配置怎么办,很简单,关闭Hudson,将目录A的内容拷贝的目录B中去,然后重新设定“HUDSON_HOME”的值,然后重启,你会发现之前你所作的所有配置都完好的保留了下来

1、Hudson-home的目录结构:

HUDSON_HOME

+- config.xml     (hudson的基本配置文件,如:jdk的安装路径)

+- *.xml          (其他系统相关的配置文件,比如:旺旺插件的全局配置信息)

+- fingerprints   (储存文件版本跟踪记录信息)

+- plugins        (存放插件)

+- jobs

+- [JOBNAME]      (任务名称,对应页面上的project name)

+- config.xml     (任务配置文件,类似于CC的config.xml中各个项目的配置)

+- workspace      (SCM所用到的目录,hudson所下载代码默认存放在这个目录)

+- builds

+- [BUILD_ID]     (每一次构建的序号)

+- build.xml      (构建结果汇总)

+- log            (运行日志)

+- changelog.xml  (SCM修改日志)

小提示:如果你使用了e-mail或者旺旺通知来接受测试消息,并且hudson的迁移设计到不同ip地址机器的迁移的话,可能需要去Hudson的主配置中修改一下Hudson的访问地址

workspace:

刚才在hudson-home的目录结构中已经看到了workspce,假设当前hudson-home为/home/hudson-home,那么当我们在hudson上配置一个项目demo的时候,就会在新建一个目录/home/hudson-home/demo,在第一次运行之前,在jobs下并没有demo这个文件夹,只有当第一次运行以后才会在jobs目录下创建demo目录,当代码顺利从svn上下载下来时才会创建workspace文件夹,所有从svn下载下来的代码都会存放在这个目录下。

1、相对路径:

项目配置过程中,Hudson使用的是相对路径,对于Hudson,在我们新建一个项目比如demo后,假设workspace的目录结构为:

workspace

+- demo

+- pom.xml

+- src

那么测试报告的路径就为demo/target/surefire-reports/*.xml,系统会自动去当前项目的workspace中去寻找这个路径

mvn package  -- 完成代码开发之后执行,按照pom.xml 中的配置信息将会打包为hpi 格式的插件文件,这个就是你最终可以拿来上传给你的hudson 平台的玩意

mvn hpi:run   -- 在本地的Jetty 中运行你的hudson 插件,调试专用,当然可以使用Debug 模式,执行之后,在本地访问http://localhost:8080/ 即可见(注意不要占用8080 端口)

mvnDebug hup:run ,debug调试模式

hudson插件开发入门相关推荐

  1. Hudson插件开发入门体验

    持续集成(CI)将软件项目流程的各个阶段进行自动化部署,从build, deploy, test automation到coverage分析,全部实现自动完成,而不需要每天的手工操作. 在敏捷开发过程 ...

  2. 【直播回顾】蚂蚁金服高级开发工程师萧恺:IDEA 插件开发入门教程

    主讲人:萧恺(蚂蚁金服-支付宝事业群-高级开发工程师) 本名:肖汉松 讲师介绍: 热爱阅读,喜欢挑战,热衷尝试新的技术,关注技术背后的原理. 关注领域:Java 服务端开发,分布式系统 关注语言:Ja ...

  3. xposed模块编写教程_Xposed插件开发入门详解,

    Xposed插件开发入门详解, 前言 Xposed的用处不必言说,能hook任意java写的代码,修改替换apk内部的资源文件. 至于如何开发一个XPosed的插件,官方给出的答案如下: https: ...

  4. 视频教程-FastAdmin插件开发入门-PHP

    FastAdmin插件开发入门 2009年4月创办 淄博日诺网络科技有限公司 法人总经理 2016年负责 中国传媒大学凤凰学院 网站开发 项目负责人 2017年 参与负责 用友软件理财项目开发 郭孟涛 ...

  5. Maya插件开发入门

    Maya插件开发入门      这里笔者给出一个创建简单的MAYA C++插件的流程.      笔者给出MEL和C++插件两个版本,实现了同样的功能. 这个插件的背景是这样的,大多人听说MAYA8. ...

  6. 基于jquery插件开发入门教程

    鉴于最近要使用大量的jquery,所以总有一种捣鼓文字来抒发一下情绪的冲动.思前想后就来篇jquery插件开发入门教程吧,毕竟如果不想开发插件,那自己无论用别人的插件多牛逼,最多只是js的使用者,作为 ...

  7. carbide.c++ 插件开发入门(转)

    carbide.c++ 插件开发入门(仅仅入门) 热10已有 229 次阅读  2010-07-25 20:39   标签:  carbide  入门  插件  开发 symbian开发,最不爽的就是 ...

  8. QT浏览器插件开发入门

    QT浏览器插件开发入门_百度文库 http://wenku.baidu.com/view/427755d13186bceb19e8bbbf.html

  9. Hudson插件开发简介

    转:http://blog.csdn.net/littleatp2008/article/details/7001793 版权声明:本文为博主原创文章,未经博主允许不得转载. 近期接触到Hudson的 ...

最新文章

  1. linux 内核编译错误 gcc: error: elf_i386: No such file or directory
  2. 制表符补全位数在idea和eclipse中的区别
  3. 从tabBarController的一个item上的控制器跳转到另一个item上的控制器
  4. e.printstacktrace()为什么没有输出信息_不输入内容,能不能直接输出内容?
  5. 【七】Java面向对象
  6. 计算色光叠加的RGB颜色的alpha值
  7. 震惊!Faker.js作者删库,理由竟然是 拒绝被“白嫖”~
  8. 解决 vim 报错:the imp module is deprecated in favour of importlib
  9. Repast Statecharts
  10. Intellij中Maven项目html页面乱码解决方案
  11. “如何写好一篇学术论文?”这大概是最详实的一则攻略了!
  12. oracle访问syno,[Oracle]同义词(synonym)
  13. linux 复制文件加后缀,linux shell 取文件名后缀
  14. 计算机移动监控,AI技术和5G技术赋能移动监控领域,加速移动监控产品的迭代...
  15. 免费PBootCMS采集支持聚合文章采集插件
  16. OS X键盘快捷键Cheatsheet
  17. text/template与html/template的区别
  18. 基于ARM、linux的MF RC522射频读卡器
  19. 如何在uni-app 平台快速实现一对一音视频通话应用
  20. 海康摄像头web集成播放,ffmpeg+nginx方案

热门文章

  1. 电商项目抢购压测实例
  2. 如何解决svn is not a working copy
  3. 豌豆淘谈护肤:豌豆淘讲解肤质分类,不了解肤质谈何护肤?
  4. Stack Overflow 上 250W 浏览量的一个问题:你对象丢了
  5. Apple Watch开发
  6. 如何选择移动广告平台,对比参数详细罗列
  7. 腾讯音乐被“锤”后,“网文霸主”阅文如何突破版权垄断?
  8. 「镁客早报」华为余承东欢迎苹果使用5G芯片;三星首款折叠手机本月开卖
  9. 人机工程学座椅设计_人机工程学椅子的尺寸和设计
  10. 破解数字化转型困局,企业分析协同场景案例解析