2016-06-16 105 views
26

已解决:感谢S.Richmond的below answer。我需要取消所有存储的groovy.json.internal.LazyMap类型的地图,这意味着在使用后取消变量envServersobjectJenkins管道NotSerializableException:groovy.json.internal.LazyMap

附加:人民搜索这个错误可能有兴趣使用詹金斯管道一步readJSON代替 - 获取更多here


我想使用詹金斯管道从作为json字符串传递给作业的用户采取输入。管道然后使用slurper解析这个,我挑选出重要的信息。然后,它将使用该信息与不同的作业参数并行运行多个作业。

直到我将下面的代码添加到"## Error when below here is added"脚本才能正常运行。即使低于该点的代码也会自行运行。但是当结合时,我得到了下面的错误。

我应该注意到,触发的作业被调用并且运行成功,但是发生了下面的错误,并且主作业失败。正因为如此,主要工作并不等待被触发工作的回归。我可能尝试/捕捉围绕build job:但是我希望主要作业等待触发作业完成。

任何人都可以在这里协助吗?如果您需要更多信息,请告诉我。

干杯

def slurpJSON() { 
return new groovy.json.JsonSlurper().parseText(BUILD_CHOICES); 
} 

node { 
    stage 'Prepare'; 
    echo 'Loading choices as build properties'; 
    def object = slurpJSON(); 

    def serverChoices = []; 
    def serverChoicesStr = ''; 

    for (env in object) { 
    envName = env.name; 
    envServers = env.servers; 

    for (server in envServers) { 
     if (server.Select) { 
      serverChoicesStr += server.Server; 
      serverChoicesStr += ','; 
     } 
    } 
    } 
    serverChoicesStr = serverChoicesStr[0..-2]; 

    println("Server choices: " + serverChoicesStr); 

    ## Error when below here is added 

    stage 'Jobs' 
    build job: 'Dummy Start App', parameters: [[$class: 'StringParameterValue', name: 'SERVER_NAME', value: 'TestServer'], [$class: 'StringParameterValue', name: 'SERVER_DOMAIN', value: 'domain.uk'], [$class: 'StringParameterValue', name: 'APP', value: 'application1']] 

} 

错误:

java.io.NotSerializableException: groovy.json.internal.LazyMap 
    at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:860) 
    at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:569) 
    at org.jboss.marshalling.river.BlockMarshaller.doWriteObject(BlockMarshaller.java:65) 
    at org.jboss.marshalling.river.BlockMarshaller.writeObject(BlockMarshaller.java:56) 
    at org.jboss.marshalling.MarshallerObjectOutputStream.writeObjectOverride(MarshallerObjectOutputStream.java:50) 
    at org.jboss.marshalling.river.RiverObjectOutputStream.writeObjectOverride(RiverObjectOutputStream.java:179) 
    at java.io.ObjectOutputStream.writeObject(Unknown Source) 
    at java.util.LinkedHashMap.internalWriteEntries(Unknown Source) 
    at java.util.HashMap.writeObject(Unknown Source) 
... 
... 
Caused by: an exception which occurred: 
    in field delegate 
    in field closures 
    in object [email protected] 
+0

恰好碰到了这个自己尴尬的嵌套构造函数)。你有没有取得进一步的进展? –

+2

https://github.com/jenkinsci/pipeline-plugin/blob/master/TUTORIAL.md#serializing-local-variables –

回答

21

我碰到了这个自己今天并通过一些暴力破解我已经想通了这两个如何解决它和潜在原因。

可能是最好下手的原因:

詹金斯已经在那里所有的工作可以被打断的范例,暂停并通过服务器重启可恢复。要实现这一点,管道及其数据必须完全可序列化 - IE需要能够保存所有事情的状态。同样,它需要能够序列化构建中节点和子作业之间的全局变量的状态,这是我认为正在发生的事情,以及为什么只有在添加额外的构建步骤时才会发生这种情况。

无论出于什么原因JSONObject的默认情况下都不可序列化。我不是一个Java开发人员,所以我不能就这个话题说更多的话。虽然我不知道他们对Groovy和Jenkins的适用性,但有很多关于如何正确解决这个问题的答案。 See this post了解更多信息。

你如何解决这个问题:

如果你知道你怎么样,都不可能使JSONObject的序列化莫名其妙。否则,你可以通过确保没有全局变量是这种类型来解决它。

尝试取消设置您的object var或将其封装在一个方法中,以使其范围不是节点全局的。

+0

谢谢,这是我需要解决这个问题的线索。虽然我已经尝试了你的建议,但它让我再次看起来并没有考虑到我将地图的一部分存储在其他变量中 - 这些都是导致错误的原因。所以我也需要解除它们。将修改我的问题以包含对代码的正确更改。欢呼声 – Sunvic

+0

这是每天〜8次。你们能介绍一下如何实施这个解决方案的更详细的例子吗? –

+0

有没有简单的解决方案,因为它取决于你所做的。这里提供的信息以及解决方案@Sunvic在其帖子的顶部添加了足够的信息,足以让他们为自己的代码提供解决方案。 –

53

改为使用JsonSlurperClassic

由于Groovy的2.3(注:詹金斯2.7.1使用的Groovy 2.4.7JsonSlurper返回LazyMap代替HashMap。这使得新的执行JsonSlurper不是线程安全和不是序列化。这使得流水线DSL脚本中的@NONDSL函数以外的内容无法使用。

但是,您可以回退到groovy.json.JsonSlurperClassic,它支持旧的behavior,并且可以在管道脚本中安全地使用它。

import groovy.json.JsonSlurperClassic 


@NonCPS 
def jsonParse(def json) { 
    new groovy.json.JsonSlurperClassic().parseText(json) 
} 

node('master') { 
    def config = jsonParse(readFile("config.json")) 

    def db = config["database"]["address"] 
    ... 
}  

PS。在被调用之前,您仍然需要批准JsonSlurperClassic

+2

你能告诉我如何批准'JsonSlurperClassic'吗? – mybecks

+5

詹金斯管理员将需要导航到管理詹金斯»进程内脚本批准。 – luka5z

+0

不幸的是我只得到'hudson.remoting.ProxyException:org.codehaus.groovy.control.MultipleCompilationErrorsException:启动失败: Script1.groovy:24:无法解决类groovy.json.JsonSlurperClassic' – dvtoever

7

编辑:正如注释中@Sunvic指出的那样,以下解决方案对于JSON数组不起作用。

我通过使用JsonSlurper来处理这个问题,然后从惰性结果中创建一个新的HashMapHashMapSerializable

我认为这需要白名单new HashMap(Map)JsonSlurper

@NonCPS 
def parseJsonText(String jsonText) { 
    final slurper = new JsonSlurper() 
    return new HashMap<>(slurper.parseText(jsonText)) 
} 
+1

没有工作 - 保持一个错误'无法找到匹配的构造函数:java.util.HashMap(java.util.ArrayList)'。文档建议它应该吐出一张清单或地图 - 你如何配置返回地图? – Sunvic

+0

@Sunvic良好的捕获,我们一直解析的数据总是对象,从来没有JSON数组。你想分析一个JSON数组吗? – mkobit

+0

啊,是的,这是一个JSON数组,就是这样。 – Sunvic

4

稍微更广义的从@mkobit答案,这将允许的形式以及阵列的解码的地图是:

import groovy.json.JsonSlurper 

@NonCPS 
def parseJsonText(String json) { 
    def object = new JsonSlurper().parseText(json) 
    if(object instanceof groovy.json.internal.LazyMap) { 
     return new HashMap<>(object) 
    } 
    return object 
} 

注:请注意,这只会顶层LazyMap对象转换为一个HashMap。任何嵌套的LazyMap对象仍然存在,并继续引发Jenkins问题。

0

我发现更简单的方法off docs for Jenkins pipeline

工作例如

import groovy.json.JsonSlurperClassic 


@NonCPS 
def jsonParse(def json) { 
    new groovy.json.JsonSlurperClassic().parseText(json) 
} 

@NonCPS 
def jobs(list) { 
    list 
     .grep { it.value == true } 
     .collect { [ name : it.key.toString(), 
         branch : it.value.toString() ] } 

} 

node { 
    def params = jsonParse(env.choice_app) 
    def forBuild = jobs(params) 
} 

Due to limitations in Workflow - i.e., JENKINS-26481 - it's not really possible to use Groovy closures or syntax that depends on closures, so you can't > do the Groovy standard of using .collectEntries on a list and generating the steps as values for the resulting entries. You also can't use the standard > Java syntax for For loops - i.e., "for (String s: strings)" - and instead have to use old school counter-based for loops.

+0

会建议使用詹金斯管道步骤readJSON代替 - 找到更多的信息[这里](https://jenkins.io/doc/pipeline/steps/pipeline-公用事业步骤/#代码readjson码-readjson从 - 文件 - 内式工作区)。 – Sunvic

0

这则讯息的其他的想法是有用的,但并不完全都是我一直在寻找的 - 所以我提取适合我需要的部分,并添加了我自己的一些魔术...

def jsonSlurpLaxWithoutSerializationTroubles(String jsonText) 
{ 
    return new JsonSlurperClassic().parseText(
     new JsonBuilder(
      new JsonSlurper() 
       .setType(JsonParserType.LAX) 
       .parseText(jsonText) 
     ) 
     .toString() 
    ) 
} 

是的,我在自己的Git指出提交代码,的“狂放ineffecient,但微小的系数:JSON啜食的解决方案”(这我可以接受的了)。 的方面,我需要解决:

  1. 完全摆脱了java.io.NotSerializableException问题了,即使JSON文本定义嵌套容器
  2. 两个地图和数组容器
  3. 支持LAX的解析工作(最重要的一部分,我的情况)
  4. 容易实现(甚至与消除@NonCPS