[json] Jenkins 파이프 라인 NotSerializableException : groovy.json.internal.LazyMap

해결 : S. Richmond의 답변에 감사드립니다 . 내가 설정 해제하는 데 필요한 모든 의 저장지도 groovy.json.internal.LazyMap변수 무효 의미 유형 envServersobject사용 후를.

추가 :이 오류를 검색하는 사람들은 readJSON대신 Jenkins 파이프 라인 단계를 사용하는 데 관심이있을 수 있습니다 . 여기에서 자세한 정보를 찾으 십시오 .


Jenkins Pipeline을 사용하여 json 문자열로 작업에 전달되는 사용자로부터 입력을 받으려고합니다. 그런 다음 Pipeline은 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 org.jenkinsci.plugins.workflow.cps.CpsThreadGroup@5288c



답변

나는 오늘 직접 이것을 만났고 일부 무차별 대입을 통해 그것을 해결하는 방법과 잠재적 인 이유를 모두 알아 냈습니다.

그 이유부터 시작하는 것이 가장 좋습니다.

Jenkins에는 서버 재부팅을 통해 모든 작업을 중단, 일시 중지 및 재개 할 수있는 패러다임이 있습니다. 이를 위해서는 파이프 라인과 데이터가 완전히 직렬화되어야합니다. IE는 모든 상태를 저장할 수 있어야합니다. 마찬가지로 빌드에서 노드와 하위 작업 사이의 전역 변수 상태를 직렬화 할 수 있어야합니다. 이것이 제가 여러분과 저에게 일어나고 있다고 생각하며 추가 빌드 단계를 추가 할 때만 발생하는 이유입니다.

어떤 이유로 든 JSONObject는 기본적으로 직렬화 할 수 없습니다. 나는 자바 개발자가 아니기 때문에 슬프게도 주제에 대해 더 이상 말할 수 없습니다. Groovy와 Jenkins에 얼마나 적용 가능한지 모르겠지만 어떻게 올바르게 수정할 수 있는지에 대한 답변이 많이 있습니다. 자세한 정보는 이 게시물참조하십시오 .

해결 방법 :

방법을 안다면 어떻게 든 JSONObject를 직렬화 할 수 있습니다. 그렇지 않으면 전역 변수가 해당 유형이 아닌지 확인하여 해결할 수 있습니다.

objectvar 설정을 해제 하거나 메서드로 래핑하여 범위가 전역 노드가되지 않도록하십시오.


답변

JsonSlurperClassic대신 사용하십시오 .

그루비 2.3 이후 ( 참고 : 젠킨스 2.7.1 그루비 2.4.7을 사용 ) JsonSlurper반환 LazyMap대신에 HashMap. 이것은 스레드로부터 안전 하지 않고 직렬화 할 수 JsonSlurper 없는 새로운 구현을 만듭니다 . 이로 인해 파이프 라인 DSL 스크립트의 @NonDSL 함수 외부에서 사용할 수 없습니다.

그러나 groovy.json.JsonSlurperClassic이전 동작 을 지원 하고 파이프 라인 스크립트 내에서 안전하게 사용할 수있는 대체 방법이 있습니다.

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"]
    ...
}

추신. JsonSlurperClassic호출하기 전에 승인이 필요합니다 .


답변

편집 : 주석에서 @Sunvic이 지적했듯이 아래 솔루션은 JSON 배열에 대해있는 그대로 작동하지 않습니다.

나는 이것을 사용 JsonSlurper하고 HashMap게으른 결과에서 새로운 것을 만들어서 다루었습니다 . HashMap입니다 Serializable.

이 작업에는 new HashMap(Map)JsonSlurper.

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

전반적으로 Pipeline Utility Steps 플러그인을 사용하는 것이 좋습니다 . 작업 공간의 파일이나 텍스트를 지원할 수 있는 readJSON단계 가 있기 때문입니다.


답변

답변 중 하나를 찬성하고 싶습니다. 작업 공간 또는 텍스트에서 파일을 지원할 수있는 readJSON 단계가 있으므로 Pipeline Utility Steps 플러그인을 사용하는 것이 좋습니다. https://jenkins.io/doc/pipeline/steps / pipeline-utility-steps / # readjson-read-json-from-files-in-the-workspace

script{
  def foo_json = sh(returnStdout:true, script: "aws --output json XXX").trim()
  def foo = readJSON text: foo_json
}

허용 목록이나 추가 항목이 필요하지 않습니다.


답변

이것은 요청 된 자세한 답변입니다.

설정되지 않은 것은 나를 위해 일했습니다.

String res = sh(script: "curl --header 'X-Vault-Token: ${token}' --request POST --data '${payload}' ${url}", returnStdout: true)
def response = new JsonSlurper().parseText(res)
String value1 = response.data.value1
String value2 = response.data.value2

// unset response because it's not serializable and Jenkins throws NotSerializableException.
response = null

파싱 ​​된 응답에서 값을 읽고 더 이상 개체가 필요하지 않으면 설정을 해제합니다.


답변

배열과 맵의 디코딩을 허용하는 @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에 계속 문제를 일으 킵니다.


답변

파이프 라인 플러그인이 구현 된 방식은 사소하지 않은 Groovy 코드에 상당히 심각한 영향을 미칩니다. 이 링크는 가능한 문제를 피하는 방법을 설명합니다 : https://github.com/jenkinsci/pipeline-plugin/blob/master/TUTORIAL.md#serializing-local-variables

귀하의 특정 경우 에는 JSON 객체 대신 맵 맵에 @NonCPS주석을 추가 slurpJSON하고 반환 하는 것을 고려할 것 입니다. 코드가 깔끔해 보일뿐만 아니라 특히 JSON이 복잡한 경우 더 효율적입니다.