jenkins 的一切 URL 请求,都托管给了 Stapler,要了解主页是如何渲染的,依然要从 Stapler 开始。用现在 Web 开发的目光来看 Stapler,它是一个纯粹的路由组件,即根据用户访问的 URL ,找到对应的处理代码。不过对于 Stapler 来说,“约定”优于“配置”,除了在 web.xml 中将 /* 交给 Stapler 处理,再也没有任何配置。免“配置”,对于开发来说,省了很多麻烦,只要在文档中详细说明“约定”,大家通读一遍文档,也就知道了。

Stapler 的文档里面记录了很多细节,与主页(Index)有关的,有“Index View”和“Index Action Method”这两个。不过 $JENKINS_SRC/core/src/main/resources/jenkins/model/Jenkins 目录下面,并没有 index.jelly 文件,jenkins.model.Jenkins 类里面,也没有 doIndex 方法。

Google 了一下,找到了一份PPT一篇文章,原来 jenkins.model.Jenkins 实现了 StaplerFallback 接口。Stapler 实在找不到处理方法的时候,会调用 getStaplerFallback 方法,jenkins.model.Jenkins 就是靠这个方法,实现了主页的渲染。

getStaplerFallback 方法返回了一个 hudson.model.View 对象,Stapler 会根据这个对象,再去找对应的模板,这是最后的方法了,如果还是找不到,就会返回 404。终于,在 $JENKINS_SRC/core/src/main/resources/hudson/model/View 目录下,找到了 index.jelly 文件,主页的模板!

来试着“劫持”一下 jenkins 的主页。修改 Jenkins.java 文件,加上下面的方法。

$JENKINS_SRC/core/src/main/java/jenkins/model/Jenkins.java

public void doIndex( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException {
    rsp.getWriter().println("Index hijack");
}

用浏览器访问 http://localhost:8080/ ,成功看到了“Index hijack”!

继续翻看 Stapler 的源码,找到了比文档更清楚的答案。

/org/kohsuke/stapler/Stapler.java

boolean tryInvoke(RequestImpl req, ResponseImpl rsp, Object node ) throws IOException, ServletException {
    //省略很多代码
    if(node instanceof StaplerFallback) {
        if(traceable())
            traceEval(req,rsp,node,"((StaplerFallback)",").getStaplerFallback()");
        Object n;
        try {
            n = ((StaplerFallback)node).getStaplerFallback();
        } catch (RuntimeException e) {
            if (Function.renderResponse(req,rsp,node,e))
                return true; // let the exception serve the request and we are done
            else
                throw e;     // unprocessed exception
        }
        if(n!=node && n!=null) {
            // delegate to the fallback object
            invoke(req,rsp,n);
            return true;
        }
   }
}