简体   繁体   English

创建Jenkins插件以添加参数

[英]Creating Jenkins Plugin to add parameters

I am writing a Jenkins plugin (for the first time) that connects to a nexus repository to pull back a list of artifacts based on a GAV search. 我正在编写一个Jenkins插件(第一次),该插件连接到一个关系存储库,以基于GAV搜索拉回工件列表。 The config.jelly allows the user to specify which groups to pull artifacts back from and then the index.jelly populates separate select boxes for groupId, artifactId, and version when the user actually does the build. config.jelly允许用户指定从中拉出工件的组,然后index.jelly在用户实际进行构建时为groupId,artifactId和版本填充单独的选择框。 The idea is that when the user clicks the "Build" button, those artifact coordinates are available during the build process. 这样的想法是,当用户单击“构建”按钮时,这些工件坐标在构建过程中可用。 When I click the "Build" button I get the following error: 当我单击“生成”按钮时,出现以下错误:

javax.servlet.ServletException: java.lang.ClassCastException: net.sf.json.JSONNull cannot be cast to net.sf.json.JSONObject
at org.kohsuke.stapler.Stapler.tryInvoke(Stapler.java:778)
at org.kohsuke.stapler.Stapler.invoke(Stapler.java:858)
at org.kohsuke.stapler.MetaClass$6.doDispatch(MetaClass.java:248)
at org.kohsuke.stapler.NameBasedDispatcher.dispatch(NameBasedDispatcher.java:53)
at org.kohsuke.stapler.Stapler.tryInvoke(Stapler.java:728)
at org.kohsuke.stapler.Stapler.invoke(Stapler.java:858)
at org.kohsuke.stapler.Stapler.invoke(Stapler.java:631)
at org.kohsuke.stapler.Stapler.service(Stapler.java:225)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:848)
at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:686)
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1494)
at hudson.util.PluginServletFilter$1.doFilter(PluginServletFilter.java:96)
at hudson.util.PluginServletFilter.doFilter(PluginServletFilter.java:88)
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1482)
at hudson.security.csrf.CrumbFilter.doFilter(CrumbFilter.java:48)
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1482)
at hudson.security.ChainedServletFilter$1.doFilter(ChainedServletFilter.java:84)
at hudson.security.ChainedServletFilter.doFilter(ChainedServletFilter.java:76)
at hudson.security.HudsonFilter.doFilter(HudsonFilter.java:164)
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1482)
at org.kohsuke.stapler.compression.CompressionFilter.doFilter(CompressionFilter.java:46)
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1482)
at hudson.util.CharacterEncodingFilter.doFilter(CharacterEncodingFilter.java:81)
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1474)
at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:499)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:137)
at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:533)
at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:231)
at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1086)
at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:428)
at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:193)
at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1020)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:135)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:116)
at org.eclipse.jetty.server.Server.handle(Server.java:370)
at org.eclipse.jetty.server.AbstractHttpConnection.handleRequest(AbstractHttpConnection.java:489)
at org.eclipse.jetty.server.AbstractHttpConnection.content(AbstractHttpConnection.java:960)
at org.eclipse.jetty.server.AbstractHttpConnection$RequestHandler.content(AbstractHttpConnection.java:1021)
at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:865)
at org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:240)
at org.eclipse.jetty.server.AsyncHttpConnection.handle(AsyncHttpConnection.java:82)
at org.eclipse.jetty.io.nio.SelectChannelEndPoint.handle(SelectChannelEndPoint.java:668)
at org.eclipse.jetty.io.nio.SelectChannelEndPoint$1.run(SelectChannelEndPoint.java:52)
at winstone.BoundedExecutorService$1.run(BoundedExecutorService.java:77)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.ClassCastException: net.sf.json.JSONNull cannot be cast to net.sf.json.JSONObject
at hudson.model.ParametersDefinitionProperty._doBuild(ParametersDefinitionProperty.java:132)
at hudson.model.AbstractProject.doBuild(AbstractProject.java:1849)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.kohsuke.stapler.Function$InstanceFunction.invoke(Function.java:298)
at org.kohsuke.stapler.Function.bindAndInvoke(Function.java:161)
at org.kohsuke.stapler.Function.bindAndInvokeAndServeResponse(Function.java:96)
at org.kohsuke.stapler.MetaClass$1.doDispatch(MetaClass.java:120)
at org.kohsuke.stapler.NameBasedDispatcher.dispatch(NameBasedDispatcher.java:53)
at org.kohsuke.stapler.Stapler.tryInvoke(Stapler.java:728)
... 46 more

I realize that JSONNull is not a subclass of JSONObject, but I don't understand why there is a JSONNull in the first place. 我意识到JSONNull不是JSONObject的子类,但我不明白为什么首先要有JSONNull。 I haven't gotten Eclipse to bind to the debug port, so I'm just using the jenkins.out.log and jenkins.err.log logs as a way of tracing through the plugin as it runs. 我还没有将Eclipse绑定到调试端口,所以我只是使用jenkins.out.log和jenkins.err.log日志作为在插件运行时进行跟踪的一种方式。 Using Fiddler I captured the following transaction with a response code of 500 (Internal server error): 使用Fiddler,我捕获了以下事务,响应代码为500(内部服务器错误):

POST http://localhost:8080/job/asdf/build?delay=0sec HTTP/1.1
Host: localhost:8080

Connection: keep-alive
Content-Length: 316
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Origin: http://localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.107 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Referer: http://localhost:8080/job/asdf/build?delay=0sec
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Cookie: screenResolution=1280x1024; JSESSIONID=fd6xjf3cxhat1mwlhv2vzt17g

groupId=org.test&artifactId=test-service&version=0.0.1-SNAPSHOT&statusCode=303&redirectTo=.&json=%7B%22groupId%22%3A+%22org.test%22%2C+%22artifactId%22%3A+%22test-service%22%2C+%22version%22%3A+%220.0.1-SNAPSHOT%22%2C+%22statusCode%22%3A+%22303%22%2C+%22redirectTo%22%3A+%22.%22%7D&Submit=Build

In my ClassParameterDefinition I am overriding the following method, with the expectation that a "Build" button click will come here: 在我的ClassParameterDefinition中,我重写了以下方法,期望在这里单击“ Build”按钮:

@Override
    public ParameterValue createValue(StaplerRequest req, JSONObject jo) {
        System.out.println(new Date().toString() + " createValue(" + jo.toString() + ")");
        ClassParameterValue value = req.bindJSON(ClassParameterValue.class, jo);

        value.setGroupId(this.groupId);
        value.setArtifactId(this.artifactId);
        value.setVersion(this.version);
        return value;
    }

and my index.jelly file looks like this: 我的index.jelly文件如下所示:

<?jelly escape-by-default='true'?>
<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"
    xmlns:i="jelly:fmt" xmlns:p="/lib/hudson/project">

    <!-- This is the input form when a user does a build. -->

    <j:invokeStatic className="org.test.ClassParameterDefinition" method="getFullArtifactList" var="fullArtifactList" />
    <j:invokeStatic className="org.test.ClassParameterDefinition" method="fetchGroupList" var="groupList" />

    <![CDATA[
    <script type="text/javascript">
        var artifactList = [];
        var versionList = [];

        function createArtifactList(fullArtifactList, groupId) {
            artifactList = [];
            for(var a = 0; a < fullArtifactList.length; a++) {
                var found = false;
                for(var l = 0; l < artifactList.length; l++) {
                    if(fullArtifactList[a].groupId == groupId) {
                        if(fullArtifactList[a].artifactId == artifactList[l]) {
                            found = true
                            break
                        }
                    }
                }
                if(fullArtifactList[a].groupId == groupId) {
                    if(!found) {
                        artifactList.push(fullArtifactList[a].artifactId)
                    }
                }
            }
            artifactList.sort(function (a, b) {
                return a.toLowerCase().localeCompare(b.toLowerCase())
            })
            populateArtifactSelect(artifactList)
        }

        function populateArtifactSelect(artifactList) {
            console.log('Populating the artifactId drop down')
            document.getElementById('artifactIdSelect').innerHTML = ""
            for(var a = 0; a < artifactList.length; a++) {
                opt = document.createElement("option")
                opt.value = artifactList[a]
                opt.text = artifactList[a]
                document.getElementById('artifactIdSelect').appendChild(opt)
            }
            createVersionList(${fullArtifactList}, document.getElementById('groupIdSelect').value, document.getElementById('artifactIdSelect').value)
        }

        function createVersionList(fullArtifactList, groupId, artifactId) {
            versionList = [];
            for(var a = 0; a < fullArtifactList.length; a++) {
                if(fullArtifactList[a].groupId == groupId) {
                    if(fullArtifactList[a].artifactId == artifactId) {
                        versionList.push(fullArtifactList[a].version)
                    }
                }
            }
            populateVersionSelect(versionList)
        }

        function populateVersionSelect(versionList) {
            console.log('Populating the version drop down')
            document.getElementById('versionIdSelect').innerHTML = ""
            for(var v = 0; v < versionList.length; v++) {
                opt = document.createElement("option")
                opt.value = versionList[v]
                opt.text = versionList[v]
                document.getElementById('versionIdSelect').appendChild(opt)
            }
        }
    </script>
    ]]>

    <j:set var="it" value="${it.build}" />
    <f:entry title="Group ID" description="${%group.id.description}" field="groupId">
        <select id="groupIdSelect" name="groupId" onchange="createArtifactList(${fullArtifactList}, this.value)">
            <j:forEach var="group" items="${groupList}">
                <option value="${group}">${group}</option>
            </j:forEach>
        </select>
    </f:entry>

    <f:entry title="Artifact ID" description="${%artifact.id.description}" field="artifactId">
        <select id="artifactIdSelect" name="artifactId" onchange="createVersionList(${fullArtifactList}, document.getElementById('groupIdSelect').value, this.value)"></select>
    </f:entry>

    <f:entry title="Version" description="${%version.description}" field="version">
        <select name="version" id="versionIdSelect"></select>
    </f:entry>

    <![CDATA[
        <script type="text/javascript">
            createArtifactList(${fullArtifactList}, document.getElementById('groupIdSelect').value)
        </script>
    ]]>

</j:jelly>
  • Why am I getting a JSONNull object created when the "Build" button submits the form? 为什么在“构建”按钮提交表单时得到一个创建的JSONNull对象?
  • Is the code in fact trying to go to the createValue method I am overriding? 实际上,代码是否在尝试使用我要覆盖的createValue方法?

After quite a bit of reading through plugins and trial and error I found the issue. 经过大量阅读插件和反复试验后,我发现了问题所在。 Hope this helps someone else. 希望这对其他人有帮助。

My index.jelly file was missing a name field, and my ClassParameterDescription was expecting it in the constructor and calling super(name); 我的index.jelly文件缺少名称字段,而我的ClassParameterDescription在构造函数中期望它并调用super(name);

That said the only way I was able to make the parameters usable, and I'm sure there are better ways to do this but since it's my first plugin I'm satisfied with a working product at the moment, was to add the following section into my index.jelly: 那就是我能够使参数可用的唯一方法,并且我敢肯定有更好的方法可以做到这一点,但是由于这是我的第一个插件,我对目前可以使用的产品感到满意,因此请添加以下部分进入我的index.jelly:

<div name="parameter">
        <input type="hidden" name="name" value="${it.name}" />
        <input type="hidden" id="groupIdValue" name="groupId" value="document.getElementById('groupIdSelect').value" />
        <input type="hidden" id="artifactIdValue" name="artifactId" value="document.getElementById('artifactIdSelect').value" />
        <input type="hidden" id="versionValue" name="version" value="document.getElementById('versionSelect').value" />
    </div>

The values had to be updated as well in the select onchange method. 这些值也必须在select onchange方法中进行更新。

The variables were then put into the environment variables in the ClassParameterValue.java class and they were accessible by $groupId, $artifactId, and $version. 然后将变量放入ClassParameterValue.java类中的环境变量中,并且可以通过$ groupId,$ artifactId和$ version访问它们。 Or on the windows batch file as %groupId%, %artifactId%, and %version% 或在Windows批处理文件上以%groupId%,%artifactId%和%version%

I got the hint from the Label Parameter plugin for Jenkins source code ( https://wiki.jenkins-ci.org/display/JENKINS/NodeLabel+Parameter+Plugin ) 我从Jenkins源代码的标签参数插件获得提示( https://wiki.jenkins-ci.org/display/JENKINS/NodeLabel+Parameter+Plugin

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM