2014-02-12 4 views
0

Я пишу плагин Jenkins (впервые), который подключается к репозиторию nexus, чтобы отбросить список артефактов на основе поиска GAV. Config.jelly позволяет пользователю указывать, какие группы извлекать артефакты назад, а затем index.jelly заполняет отдельные поля выбора для groupId, artifactId и версии, когда пользователь действительно выполняет сборку. Идея заключается в том, что когда пользователь нажимает кнопку «Создать», эти артефактные координаты доступны во время процесса сборки. При нажатии на кнопку «Построить» Я получаю следующее сообщение об ошибке:Создание плагина Jenkins для добавления параметров

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 

Я понимаю, что JSONNull не подкласс JSONObject, но я не понимаю, почему есть JSONNull в первую очередь , Я не получил Eclipse для привязки к порту отладки, поэтому я просто использую журналы jenkins.out.log и jenkins.err.log как способ отслеживания через плагин при его запуске. С помощью 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 

В моей 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; 
    } 

и мой файл 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> 
  • Почему я г etting объект JSONNull, созданный, когда кнопка «Создать» отправляет форму?
  • Действительно ли код пытается перейти к методу createValue, который я переопределяю?

ответ

0

После довольно многого чтения плагинов и проб и ошибок я нашел проблему. Надеюсь, это помогает кому-то другому.

В файле index.jelly отсутствует поле имени, и мой ClassParameterDescription ожидал его в конструкторе и вызывал super (name);

Это говорит о том, что я смог сделать параметры пригодными для использования, и я уверен, что есть лучшие способы сделать это, но поскольку это мой первый плагин, я доволен рабочим продуктом на данный момент, был добавьте следующий раздел в мой 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> 

значения должны были быть обновлены, а в выбранном методе OnChange.

Затем переменные были помещены в переменные среды класса ClassParameterValue.java, и они были доступны с помощью $ groupId, $ artifactId и $ version. Или на окнах пакетного файла в% идентификатор_группе%,% артефакт% и% версия%

Я получил подсказку от этикетки плагин для параметра Дженкинс исходного кода (https://wiki.jenkins-ci.org/display/JENKINS/NodeLabel+Parameter+Plugin)

Смежные вопросы