目前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