Я пишу плагин 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, который я переопределяю?