Jelly基础

参考:https://wiki.jenkins-ci.org/display/JENKINS/Basic+guide+to+Jelly+usage+in+Jenkins

UI Samples Plugin

这个插件就是用来展示如何使用基于Stapler, Jelly, Groovy等技术的Jenkins UI 控件的。安装这个插件对学习Jenkins插件开发非常有用。

创建*.jelly文件

一个基本的jenkins插件结构包括以下几个部分:

  • pom.xml
  • src/main/java
  • src/main/resources
  • src/main/webapp

假如已经写好一个类  src/main/java/org/myorganization/MyAction.java,要为这个类定义一个Jelly文件。

  1. 在resources目录中创建一个与类名一一对应的目录:  src/main/resources/org/myorganization/MyAction
  2. 在这个目录中添加.jelly文件(不同的文件名会有不同的作用),如index.jelly。
  3. 然后就是编辑这个文件

预定义对象

一个目录下的所有Jelly文件都跟和该目录一一对应的某个类直接关联。这个对应的类对象会绑定到jelly文件中名字为 it 的变量上,jelly文件中可以通过it变量调用类中的方法,调用代码要用一对大括号包裹,前括号前加$。形式为:${insert code here}

例如在MyAction.java中定义了如下的方法

public String getMyString() {
    return "Hello Jenkins!";
}

在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">
    ${it.myString}
</j:jelly>

方法名开头的get被自动去掉了,剩余部分首字母变成小写。建议使用java约定的命名方式(如getter方法都是get开头的CamelCase形式)确保Jelly能够找到正确的方法。

Jenkins中预定义对象主要有

it

  与jelly文件直接关联的类实例

instance

  当前正在被配置的对象(在配置页面中的一个区域内),例如BuildStep。如果不是重新配置而是刚添加了一个新的实例的话这个变量为null。

app

  Jenkins(或Hudson)实例

descriptor

与instance对象所属类对应的Descriptor对象

h

hudson.Functions对象的实例,提供了很多非常有用的方法。

预定义URL变量

rootURL

Jenkins实例的URL

resURL

JavaScript, HTML等静态webapp资源(cf.  Jenkins.RESOURCE_PATH)

imagesURL

图标等图像资源路径 ,实际就是resURL/images

应尽可能使用resURL而不是rootURL,因为resURL允许浏览器缓存,可以提高响应速度减轻服务器压力。

只要在l:layout或l:ajax块内,这些URL就已经定义好了,也就是说在任何的Jenkins页面里都可以使用(如果页面是通过ajax加载的,需要添加l:ajax标签)。

Note that until 1.505 rootURL will be empty in the typical case of Jenkins running in development mode with no context path (http://localhost:8080/); it is used for creating server-absolute paths such as /some/path for direct links. As of 1.505 it will be /jenkins in test mode, to help remind you to use it! In the rare case that you need to pass a complete URL to an external service for some reason, use app.rootUrl instead (which takes into account “Jenkins URL” from the system /configure screen). Hyperlinks in HTML has a fuller treatment of this topic.

大多数情况下,jelly文件修改后不需要重启Jenkins服务就能看到效果。修改文件然后重新请求页面,修改后的jelly文件就会被加载了。

带Descriptors的对象(如Publisher)

  1. 定义一个不可变的类将所有的配置选项作为构造器的参数。然后给这个构造器加上@DataBoundConstructor注解,Jenkins通过这个注解就能知道如何实例化这个类的对象。
  2. 为配置选项字段添加getter方法,或者将配置选项字段改为public final。Jelly脚本中就可以读取这些字段值到配置页面中。
  3. 编写Jelly片段文件(一般会叫config.jelly,可以查看基类的javadoc文档看看可以使用哪些jelly文件名字),在jelly文件中展示所有的配置选项。最基本的形式如下(jelly元素的field属性即是属性的名字,也是构造器参数的名字,这个例子中jelly文件对应的类需要提供getPort方法或public port字段)。
<j:jelly xmlns:j="jelly:core" xmlns:f="/lib/form">
  <f:entry title="${%Port}" field="port">
    <f:textbox />
  </f:entry>
  <f:entry title="${%Host}" field="host">
    <f:textbox />
  </f:entry>
</j:jelly>

${%...}为国际化标记(https://wiki.jenkins-ci.org/display/JENKINS/Internationalization)。

Help文件

插件可以使用src/main/resources/path/to/plugin/PluginName目录中的help-FIELD.htmlhelp-FIELD.jelly文件。Jenkins会在对应的Filed元素后添加一个问号图标,点击图标就会将帮助文件在配置页面中内联的渲染出来。help.htmlhelp.jelly会作为插件顶级的帮助文件。

表单验证

在descriptor类中添加的doCheckFIELD方法,会被用于表单验证逻辑。示例如下:

public FormValidation doCheckPort(@QueryParameter String value) {
  if(looksOk(value))  return FormValidation.ok();
  else                return FormValidation.error("There's a problem here");
}

这个方法中可以有多个指定了具体Filed的@QueryParameter("FIELD")注解参数,同时获得多个参数值。可以表单field之间相互依赖关系的验证。更详细的内容可以查询stapler的文档。

默认值

可以使用页面元素的default属性为表单域设置默认值。即可以用字面值,也可以编程式的方式计算默认值(使用jelly预定义的变量,调用这些对象的方法)。示例如下:

<j:jelly xmlns:j="jelly:core" xmlns:f="/lib/form">
  <f:entry title="${%Port}" field="port">
    <f:textbox default="80" />
  </f:entry>
  <f:entry title="${%Host}" field="host">
    <f:textbox default="${descriptor.defaultHost()}/>
  </f:entry>
</j:jelly>

无Descriptor的对象(如ComputerListener)

参考插件:https://github.com/jenkinsci/sidebar-link-plugin

如果插件用了无Descriptor的对象,如ComputerListener。可以按下面的步骤,在配置页面中增加一个插件类(Plugin的子类)可读取的文本框。

  1. 重写 plugin类的configure(StaplerRequest req, JSONObject formData)方法。这个方法会在用户点击配置页面的保存按钮是被调用。
  2. 在configure方法里,调用formData的提取数据方法获取配置值,如optInt("port",3141),将获取的数据保存在Plugin类的成员字段中。
  3. 在configure方法里,调用save()方法,将plugin类的可序列化字段持久化存储。
  4. 在plugin类的start方法中,调用load(),插件启动时重新加载持久化存储的数据。
  5. 在与plugin类对应的位置(src/main/resources/path/to/plugin/className/config.jelly)创建config.jelly文件,编写配置页面视图。在插件类的config.jelly中,用变量it代替变量instance,it.port可以引用当前使用的port参数值。help属性使用创建插件时pom.xml中指定的artifactId引用插件的资源。
    <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:section title="My Plugin">
        <f:entry title="${%Port}" help="/plugin/ARTIFACT_ID_GOES_HERE/help-projectConfig.html">
          <f:textbox name="port" value="${it.port}"/>
        </f:entry>
      </f:section>
    </j:jelly>
  6. 在src/main/webapp中创建help文件help-projectConfig.html。
  7. 正常启动Jenkins(mvn hpi:run),查看效果。

HTML中的超链接

https://wiki.jenkins-ci.org/display/JENKINS/Hyperlinks+in+HTML

规则1:超链接不要使用绝对URL

  HTML中的超链接应该使用相对URL(如../foo/bar,  build/5/changes)或应用相对URL(以/开头,如/job/foo/5, /manage)。但是前面不要用绝对路径(如http://jenkins/foo/1)。让浏览器将相对路径绝对化。

  这条规则的原因是,经过反向代理等过程后,web应用无法知道客户端用来访问服务器的URL。但是反向代理都会保证URL中的路径部分(context path+path info)是原封不动的,使用应用内部的相对路径不会有任何影响。

  在JavaEE中,URL的路径部分可分为两段,先是context path,后面跟着Path Info。例如http://server.example.com/jenkins/job/foo/5,/jenkins为context path,/job/foo/5为path info。Jenkins开发者可以控制后者。

  • getUrl()方法可以在整个Jenkins中返回域对象的path info部分,如Buiild.getUrl()返回job/foo/32/(开头没有/,结尾的/不同版本并不一致)。
  • getAbsoluteUrl()在整个Jenkins中返回域对象的绝对路径。不要用这个方法渲染超链接。
  • 在Groovy/Jelly/JavaScript中,rootURL变量代表了context path。生成相对的超链接格式为:<a href="${rootURL}/${it.url}">...</a> (in Jelly) 或 a(href:"${rootURL}/${my.url}", ...) (for Groovy)

规则2:使用静态资源前缀URL访问静态资源

  图片,JavaScript脚本,样式表等静态资源在Jenkins运行中不会变化,应该使用静态资源前缀的URL(/static/XXXXX)访问,如/static/907dbcb0/plugin/foobar/abc.png。XXXXX部分是Jenkins为运行中的Jenkins Session持续期而生成的随机字符串。请求的路由与不带前缀的相同,但是带了前缀后头信息中会设置很长的过期时间,让浏览器使用缓存。随机数字是为了防止plugins/core更新后浏览器使用旧数据。

在Java中: Functions.getResourcePath()作为URL的前缀。在Jelly中使用resURL变量。

Functions.getResourcePath()+"/plugin/git/icons/git-32x32.png";

<img src="${resURL}/plugin/translation/flags.png" />

规则3:为email,IM等生成绝对路径

如果以HTTP响应以为的方式提供服务数据,需要生成绝对路径。可用Jenkins.getInstance().getRootUrl()方法,将返回管理员配置的绝对路径。这是在Jenkins实例中准确获取绝对路径的唯一方法。

规则4:ConsoleNote必须为Path-Info

通过HyperlinkNote向控制台输出中嵌入超链接时,使用/开头的URL创建便携的超链接(如果URL以/开头,就会相对于context path处理)。这样就算控制台note创建以后,Jenkins URL变了,超链接依然能正确渲染。

void foo(TaskListener listener) {
  listener.getLogger().println(
    HyperlinkNote.encodeTo("/configure","Please configure your Jenkins"));
}

在Jenkins轻易地创建指向不同对象的超链接,可以查阅ModelHyperlinkNote类(http://javadoc.jenkins-ci.org/?hudson/console/ModelHyperlinkNote.html)。

反向代理和Https

如果反向代理终止了HTTPS,Jenkins无法利用HttpServletRequest#isSecure()检查传输是否安全。Jenkins.isRootUrlSecure()可以检查管理员配置的Jenkins URL是否是Https。

其他与路径有关的主题

https://wiki.jenkins-ci.org/display/JENKINS/Jenkins+says+my+reverse+proxy+setup+is+broken

https://wiki.jenkins-ci.org/display/JENKINS/Running+Jenkins+behind+Apache

https://wiki.jenkins-ci.org/display/JENKINS/Running+Jenkins+behind+Nginx

国际化

https://wiki.jenkins-ci.org/display/JENKINS/Internationalization

properties文件默认编码为ISO-8859-1。静态资源文件编码为UTF-8。

Messages类

  Jenkins使用Localizer(https://java.net/projects/localizer/)生成Messages类,能够以类型安全的方式访问Message资源。src/main/resources/**/Messages.properties匹配的所以文件都会生成一个对应的Messages类。如过IDE找不到这些类,需要手动将target/generated-sources/localizer目录加入源码的根目录。返回用于显示的字符串的代码(如Descriptor.getDisplayName())可以使用Messages类获取本地化的消息。在运行时,适当的locale会被自动选择。

  典型的工作流如下:

  1. 确定需要本地化的messages
  2. 将消息写入MEssages.properties文件。即可以为每个package编写一个,也可以整个module/plugin只用一个。
  3. 运行 mvn compile,生成Messages.java
  4. 更新代码,使用最新生成的消息格式方法。

  如果消息中包含单引号('),需要再用一个单引号昨晚转义('')。如果想使用英文撇号(’),可以用unicode字符 U+2019,在properties文件中写成\u2019。

在Jelly文件中标记消息

 ${%xxxx} 标记指示stapler查找本地化资源, 如果没有找到对应资源,就原样输出xxxx。

<h1>${%Output}</h1>

带参数消息

要渲染出的结果:

 <p>Click <a href="${obj.someMethod(a,b,c)}" >here</a></p>

foo.properties文件中内容为:

click.here.link = Click <a href="{0}" >here</a>

jelly中消息引用标记:

<p>${%click.here.link(obj.someMethod(a,b,c))}</p>

因为参数使用了的括号()包裹,所以普通文本中不能包含括号。

<h1>${%Path to file (.ipa or .apk)}</h1>     解析会出错

这种情况下可以通过拆分文本,将括号排除在{% }标记外部

<h1>${%Path to file} (${%.ipa or .apk})</h1>    

多个参数

多个参数之间用 , 分隔(类似方法调用)。在properties文件中按参数位置引用 {0}为第一个参数,{1}为第二个参数......(按MessageFormat解析,参考:http://docs.oracle.com/javase/6/docs/api/java/text/MessageFormat.html

<p>${h.ifThenElse(x,"no error","error")}</p>

参数中再标记消息,不需要大括号{}包裹

<p>${h.ifThenElse(x,"%no error","%error")}</p>

help.html和help-FILED.html文件的本地化

默认help.html为English。然后再为xx语言创建help_xx.html文件即可。

翻译

Translation Tool :命令行资源文件翻译工具(https://wiki.jenkins-ci.org/display/JENKINS/Translation+Tool)

Translation Assistance Plugin :Adds a dialog box in Jenkins to translate and send missing keys.

Stapler plugin for IntelliJ IDEA: 利用反射提取java代码中的资源到properties文件(mvn compile编译之前会显示错误)。

NetBeans plugin for Stapler

要翻译的内容

  • Messages.properties
  • jelly文件。可以先调用mvn stapler:i18n -Dlocal=fr,生成一个骨架文件*_fr.properties,所以条目值都为空。如果文件已经存在则添加之前不存在的新条目。留空的条目会回退到默认的locale。
  • 静态HTML资源。foo_xx.html

翻译完成度报告

http://www.simonwiest.de/glottr/report/

Jenkins中Jelly基础、超链接、国际化的更多相关文章

  1. Jenkins中Jelly邮件模板的配置

    [链接]Jenkins中Jelly邮件模板的配置http://blog.csdn.net/hwhua1986/article/details/47975237

  2. Jenkins中集成Gcov代码覆盖率报告

    最近终于把gcov代码覆盖报告集成到jenkins中了,总算是完成工作,写篇博客总结下. 我循序渐进地用了三个工具:gcov, lcov, gcovr 这三个工具原理(其实gcovr依赖于GNU的gc ...

  3. 新建项目到Jenkins中

    在以Jenkins为镜像创建Docker容器时,我们在jenkins的dockerfile文件中写明了要安装Docker Compose,目的也是在Jenkins容器中借助Docker Compose ...

  4. Jenkins中的邮件配置

    摘自http://blog.csdn.net/fullbug/article/details/53024562 Jenkins是一个很受欢迎的CI持续集成工具,能够实现项目的自动构建.打包.测试.发布 ...

  5. jenkins中Email Extersion Plugin插件使用说明点

    在jenkins中使用第3方邮件插件Email Extersion Plugin时,根据网上教程,发现每次都没有生成模板 再次查看,发现 $HOME_jenkins下没有templeate文件夹,查阅 ...

  6. Jenkins中构建Testcomplete项目的方法介绍

    Jenkins的部署在上一篇随笔中已经和大家介绍了,下面我们介绍一下再Jenkins中构建testcomplete项目.我这里使用的是Testcomplete11,下面详细介绍一下构建步骤. 1.Je ...

  7. 在Jenkins中获取GitHub对应Repository的Resource Code

    1):Install Jenkins 请看如下链接: https://wiki.jenkins-ci.org/display/JENKINS/Installing+Jenkins 2):Install ...

  8. [.net 面向对象编程基础] (3) 基础中的基础——数据类型

    [.net 面向对象编程基础] (3) 基础中的基础——数据类型 关于数据类型,这是基础中的基础. 基础..基础..基础.基本功必须要扎实. 首先,从使用电脑开始,再到编程,电脑要存储数据,就要按类型 ...

  9. [.net 面向对象编程基础] (4) 基础中的基础——数据类型转换

    [.net面向对象编程基础] (4)基础中的基础——数据类型转换 1.为什么要进行数据转换? 首先,为什么要进行数据转换,拿值类型例子说明一下, 比如:我们要把23角零钱,换成2.30元,就需要把整形 ...

随机推荐

  1. jboss配置数据源

    配置的是mysql的数据源 找到jboss-.GA\docs\examples\jca\mysql-ds.xml 复制一份到jboss-.GA\server\default\deploy目录下 然后修 ...

  2. LeetCode-62-Unique Paths

    A robot is located at the top-left corner of a m x n grid (marked 'Start' in the diagram below). The ...

  3. bzoj4404: [Neerc2015]Binary vs Decimal

    WC结束了,来补一下这题的题解 首先感谢SC神犇YYY(第一个AC此题的神犇)教我做法 再感谢教YYY做法的Claris大爷 首先,我们发现一个性质,一个合法的数的后缀必定是合法的,所以我们就可以bf ...

  4. 利用VBA查找excel中一行某列第一次不为空与最后一列不为空的列数

    昨日同事有需求,想知道每个商品第一次销售的月份,以及最后一次销售的月份. 本想通过什么excel函数来解决,但是找了半天也没找到合适的,最后还是通过VBA来解决吧. 使用方法: Excel工具-宏-V ...

  5. mybatis for .net

    MyBatis For .NET学习笔记:开篇 http://chenkai.blog.51cto.com/2023960/763806 MyBatis For .NET学习笔记[2]:配置环境 ht ...

  6. 关于MySQL的commit非规律性失败案例的深入分析

    案例描述: 一个普通的事务提交,在应用里面会提示commit超时,失败. 一.理论知识 1.关于commit原理,事务提交过程 1.寻找修改的数据页: 1.如果该数据页在内存中,则直接是内存读: 2. ...

  7. linux学习(一)认识、安装Linux

    一.什么是Linux linux是一种操作系统,我们用的android和ios就是分别是linux操作系统和类unix操作系统. linux也是我们经常说的服务器.我们看的网站,游戏,app背后都是服 ...

  8. git pull与git fetch的区别

    git pull: 取回远程主机某个分支的更新,再与本地的指定分支合并. 用法: git pull <远程仓库> <远程分支名>:<本地分支名> // 如 git ...

  9. MongoDB存储引擎(下)——In-Memory

    前两篇文章分别介绍了MMAPv1和WiredTiger,这两个存储引擎都是会将数据持久化存储到硬盘的,除此之外,MongoDB也有只将数据存储在内存的存储引擎,那就是In-Memory. In-Mem ...

  10. 并发编程(四)—— ThreadLocal源码分析及内存泄露预防

    今天我们一起探讨下ThreadLocal的实现原理和源码分析.首先,本文先谈一下对ThreadLocal的理解,然后根据ThreadLocal类的源码分析了其实现原理和使用需要注意的地方,最后给出了两 ...