जेनकिंस पाइपलाइन नोटसिरेबल एक्ससेप्शन: groovy.json.internal.LazyMap


80

हल : एस। रिचमंड से नीचे जवाब के लिए धन्यवाद । मुझे उस प्रकार के सभी संग्रहीत नक्शे को अनसेट करने की आवश्यकता थी , groovy.json.internal.LazyMapजिसका अर्थ था चर envServersका objectउपयोग करना और उपयोग के बाद।

अतिरिक्त : इस त्रुटि के लिए खोज करने वाले लोग इसकेreadJSON बजाय जेनकींस पाइपलाइन कदम का उपयोग करने के लिए इच्छुक हो सकते हैं - यहां अधिक जानकारी प्राप्त करें


मैं उपयोगकर्ता से इनपुट लेने के लिए जेनकिंस पाइपलाइन का उपयोग करने की कोशिश कर रहा हूं जो कि जॉन्स स्ट्रिंग के रूप में नौकरी में उत्तीर्ण है। पाइपलाइन तब गारा का उपयोग करके इसे पार्स करता है और मैं महत्वपूर्ण जानकारी निकालता हूं। फिर यह उस जानकारी का उपयोग अलग-अलग नौकरी मापदंडों के साथ समानांतर में 1 नौकरी को कई बार चलाने के लिए करेगा।

जब तक मैं "## 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

बस खुद इस में भाग गया। क्या आपने अभी तक कोई और प्रगति की है?
एस। रिचमंड

जवाबों:


71

मैं खुद आज इस में भाग गया और कुछ bruteforce के माध्यम से मुझे पता चला है कि दोनों इसे कैसे हल करें और संभवतः क्यों।

शायद क्यों शुरू करने के लिए सबसे अच्छा:

जेनकिंस का एक प्रतिमान है जहां सर्वर रिबूट के माध्यम से सभी नौकरियों को बाधित, रोका और फिर से शुरू किया जा सकता है। इसे प्राप्त करने के लिए पाइपलाइन और इसके डेटा को पूरी तरह से क्रमबद्ध होना चाहिए - IE इसे हर चीज की स्थिति को बचाने में सक्षम होना चाहिए। इसी तरह, यह निर्माण में नोड्स और उप-नौकरियों के बीच वैश्विक चर की स्थिति को क्रमबद्ध करने में सक्षम होने की आवश्यकता है, जो मुझे लगता है कि आपके लिए हो रहा है और मैं और यह केवल तब होता है जब आप उस अतिरिक्त बिल्ड कदम को जोड़ते हैं।

जो भी कारण JSONObject के डिफ़ॉल्ट रूप से क्रमिक नहीं हैं। मैं जावा देवता नहीं हूं इसलिए मैं इस विषय पर ज्यादा दुख की बात नहीं कह सकता। वहाँ के बारे में बहुत सारे उत्तर हैं कि कोई इसे कैसे ठीक कर सकता है हालांकि मुझे नहीं पता कि वे ग्रूवी और जेनकिंस के लिए कितने लागू हैं। इस पोस्ट को थोड़ी और जानकारी के लिए देखें

आप इसे कैसे ठीक करते हैं:

यदि आप जानते हैं कि कैसे, आप संभवतः JSONObject को किसी भी तरह से क्रमबद्ध बना सकते हैं। अन्यथा आप यह सुनिश्चित कर सकते हैं कि कोई वैश्विक वैरिएबल उस प्रकार का न हो।

अपने संस्करण को परेशान करने objectया इसे एक विधि में लपेटने का प्रयास करें ताकि इसका दायरा वैश्विक न हो।


2
धन्यवाद, यह मुझे इस हल करने के लिए आवश्यक सुराग है। जब तक मैंने आपकी शर्करा की कोशिश पहले ही कर ली थी, इसने मुझे फिर से देख लिया और मैंने यह नहीं माना कि मैं अन्य चर में नक्शे के कुछ हिस्सों को संग्रहीत कर रहा था - ये त्रुटियां पैदा कर रहे थे। इसलिए मुझे उन्हें भी परेशान करने की जरूरत थी। कोड में सही परिवर्तन शामिल करने के लिए मेरे प्रश्न में संशोधन करेंगे। चीयर्स
Sunvic

1
इसे दिन में ~ 8 बार देखा जाता है। क्या आप लोग इस समाधान को लागू करने के बारे में अधिक विस्तृत उदाहरण प्रदान करेंगे?
जॉर्डन स्टेफनेली

1
कोई सरल उपाय नहीं है क्योंकि यह इस बात पर निर्भर करता है कि आपने क्या किया है। यहां दी गई जानकारी के साथ-साथ समाधान @Sunvic ने अपनी पोस्ट के शीर्ष पर जोड़ा aught अपने स्वयं के कोड के लिए एक समाधान का नेतृत्व करने के लिए पर्याप्त होने के लिए।
एस। रिचमंड

1
नीचे दिए गए समाधान, JsonSlurperClassic का उपयोग करके मेरे पास ठीक उसी मुद्दे को निर्धारित किया गया है, जिसे शायद यहां अनुमोदित विकल्प होना चाहिए। इस उत्तर में योग्यता है, लेकिन यह इस विशेष समस्या का सही समाधान नहीं है।
क्वार्ट्ज

@JordanStefanelli मैंने अपने समाधान के लिए कोड पोस्ट किया। देखें मेरा उत्तर नीचे
निल्स अल Himoud

127

JsonSlurperClassicइसके बजाय उपयोग करें ।

ग्रूवी 2.3 ( नोट: जेनकिंस 2.7.1 ग्रूवी 2.4.7 का उपयोग करता है ) के बजाय JsonSlurperरिटर्न । इस नए कार्यान्वयन के बनाता नहीं सुरक्षित और थ्रेड नहीं serializable। यह पाइपलाइन DSL स्क्रिप्ट्स में @NonDSL फ़ंक्शन के बाहर अनुपयोगी बनाता है।LazyMapHashMapJsonSlurper

हालाँकि, आप 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"]
    ...
}    

ps। JsonSlurperClassicयह कहने से पहले आपको अभी भी अनुमोदन करने की आवश्यकता होगी ।


2
क्या आप मुझे बता सकते हैं कि कैसे मंजूर करें JsonSlurperClassic?
जूल

7
जेनकींस प्रशासक को जेनकिन्स »इन-प्रोसेस स्क्रिप्ट अनुमोदन को प्रबंधित करने के लिए नेविगेट करने की आवश्यकता होगी।
लुका 5z

दुर्भाग्य से मुझे केवल मिलता हैhudson.remoting.ProxyException: org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed: Script1.groovy: 24: unable to resolve class groovy.json.JsonSlurperClassic
dvtoever

13
JsonSluperClassic .. यह नाम सॉफ्टवेयर विकास की वर्तमान स्थिति के बारे में बहुत कुछ बताता है
मार्कोस ब्रिगेंट

1
इस विस्तृत विवरण के लिए आपका बहुत-बहुत धन्यवाद। आपने मेरा बहुत समय बचाया। यह समाधान मेरी जेनकींस पाइपलाइन में एक आकर्षण की तरह काम करता है।
सतीश प्रकाशम

16

संपादित करें: जैसा कि @Sunvic द्वारा टिप्पणियों में बताया गया है , नीचे दिया गया समाधान JSON Arrays के लिए काम नहीं करता है।

मैंने इसका उपयोग करके JsonSlurperऔर फिर HashMapआलसी परिणामों से एक नया निर्माण किया । HashMapहै Serializable

मेरा मानना ​​है कि यह आवश्यक है और दोनों की श्वेत सूची में new HashMap(Map)है JsonSlurper

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

कुल मिलाकर, मैं सिर्फ पाइपलाइन उपयोगिता कदम प्लगइन का उपयोग करने की सलाह दूंगा , क्योंकि इसमें एक readJSONकदम है जो कार्यक्षेत्र या पाठ में फ़ाइलों का समर्थन कर सकता है।


1
मुझे काम नहीं किया - एक त्रुटि हो रही है Could not find matching constructor for: java.util.HashMap(java.util.ArrayList)। दस्तावेज़ीकरण से पता चलता है कि यह एक सूची या एक नक्शे को थूकना चाहिए - मानचित्र को वापस करने के लिए आप कैसे कॉन्फ़िगर करते हैं?
Sunvic

@Sunvic अच्छी पकड़, हम जो डेटा पार्स कर रहे हैं वह हमेशा ऑब्जेक्ट्स हैं, कभी भी JSON एरेज़ नहीं। क्या आप JSON सरणी पार्स करने का प्रयास कर रहे हैं?
mkobit

आह हाँ, यह एक JSON सरणी है, यह हो जाएगा।
Sunvic

यह जवाब और नीचे, जेनकिंस पर दोनों ने अस्वीकार कर दिया, क्योंकि जेनकींस सैंडबॉक्स में ग्रूवी चलाता है
यिवेन

@ मुझे पता चला कि इसके लिए प्रशासक श्वेतसूची की आवश्यकता है, लेकिन शायद इसका उत्तर स्पष्ट किया जा सकता है कि इसका क्या मतलब है?
mkobit

8

मैं उत्तर में से एक को उभारना चाहता हूं: मैं सिर्फ पाइपलाइन उपयोगिता स्टेप्स प्लगइन का उपयोग करने की सिफारिश करूंगा, क्योंकि इसमें एक रीडजोन चरण है जो कार्यक्षेत्र या पाठ में फ़ाइलों का समर्थन कर सकता है: https://jenkins.io/doc/pipeline-steps / पाइपलाइन-उपयोगिता-चरण / # readjson-read-json-from-files-of-the-workspace

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

इसके लिए किसी भी श्वेतसूची या अतिरिक्त सामान की आवश्यकता नहीं होती है।


6

यह विस्तृत उत्तर है जो पूछा गया था।

अशांत ने मेरे लिए काम किया:

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

मैंने पार्स की गई प्रतिक्रिया से मूल्यों को पढ़ा और जब मुझे ऑब्जेक्ट की आवश्यकता नहीं होती है तो मैं इसे अनसेट करता हूं।


5

@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 ऑब्जेक्ट्स अभी भी वहाँ रहेंगे और जेनकींस के साथ मुद्दों का कारण बनेंगे।


2

जिस तरह से पाइपलाइन प्लगइन को लागू किया गया है उसमें गैर-तुच्छ ग्रूवी कोड के लिए काफी गंभीर निहितार्थ हैं। यह लिंक बताता है कि संभावित समस्याओं से कैसे बचा जाए: https://github.com/jenkinsci/pipeline-plugin/blob/master/TUTORIAL.md#serializing-local-variables

आपके विशिष्ट मामले में मैं JSON ऑब्जेक्ट के बजाय @NonCPSएनोटेशन को जोड़ने slurpJSONऔर मैप-ऑफ़-मैप्स वापस करने पर विचार करूंगा । न केवल कोड साफ दिखता है, बल्कि यह अधिक कुशल भी है, खासकर यदि JSON जटिल है।


2

जेनकिंस ब्लॉग पर पोस्ट की गई सर्वोत्तम प्रथाओं ( पाइप लाइन स्केलेबिलिटी बेस्ट प्रैक्टिस ) के अनुसार, इस तरह के काम के लिए कमांड-लाइन टूल या स्क्रिप्ट का उपयोग करने की दृढ़ता से सिफारिश की जाती है :

Gotcha: विशेष रूप से Groovy के XmlSlurper और JsonSlurper का उपयोग करके पाइपलाइन XML या JSON पार्सिंग से बचें! कमांड लाइन टूल या स्क्रिप्ट को मजबूती से प्राथमिकता दें।

मैं। ग्रूवी कार्यान्वयन जटिल हैं और परिणामस्वरूप पाइपलाइन उपयोग में अधिक भंगुर हैं।

ii। XmlSlurper और JsonSlurper पाइपलाइनों में एक उच्च मेमोरी और CPU लागत ले सकते हैं

iii। xmllint और xmlstartlet xpath के माध्यम से XML निष्कर्षण की पेशकश करने वाले कमांड-लाइन टूल हैं

iv। jq JSON के लिए समान कार्यक्षमता प्रदान करता है

v। इन निष्कर्षण साधनों को HTTP API से जानकारी प्राप्त करने के लिए कर्ल या wget में जोड़ा जा सकता है

इस प्रकार, यह बताता है कि क्यों इस पृष्ठ पर प्रस्तावित अधिकांश समाधानों को डिफ़ॉल्ट रूप से जेनकिंस सुरक्षा स्क्रिप्ट प्लगइन के सैंडबॉक्स द्वारा अवरुद्ध किया जाता है।

ग्रूवी का भाषा दर्शन पायथन या जावा की तुलना में बैश के करीब है। इसके अलावा, इसका मतलब है कि देशी ग्रूवी में जटिल और भारी काम करना स्वाभाविक नहीं है।

यह देखते हुए, मैंने व्यक्तिगत रूप से निम्नलिखित का उपयोग करने का निर्णय लिया है:

sh('jq <filters_and_options> file.json')

अधिक सहायता के लिए jq stackoverflow पोस्ट के साथ jq मैन्युअल और सेलेक्ट ऑब्जेक्ट देखें ।

यह थोड़ा काउंटर सहज है क्योंकि ग्रूवी कई सामान्य तरीके प्रदान करता है जो डिफ़ॉल्ट श्वेतसूची में नहीं हैं।

यदि आप अपने अधिकांश काम के लिए वैसे भी ग्रूवी भाषा का उपयोग करने का निर्णय लेते हैं, तो सैंडबॉक्स सक्षम और स्वच्छ (जो आसान नहीं है, क्योंकि प्राकृतिक नहीं है), मैं आपको अपने सुरक्षा स्क्रिप्ट प्लगइन के संस्करण के लिए श्वेत सूची की जांच करने की सलाह देता हूं ताकि यह पता चल सके कि आपकी संभावनाएं क्या हैं: स्क्रिप्ट सुरक्षा प्लगइन श्वेतसूची


2

आप LazzMap को एक नियमित लिंक्डहैशमैप में परिवर्तित करने के लिए निम्नलिखित फ़ंक्शन का उपयोग कर सकते हैं (यह मूल डेटा का क्रम बनाए रखेगा):

LinkedHashMap nonLazyMap (Map lazyMap) {
    LinkedHashMap res = new LinkedHashMap()
    lazyMap.each { key, value ->
        if (value instanceof Map) {
            res.put (key, nonLazyMap(value))
        } else if (value instanceof List) {
            res.put (key, value.stream().map { it instanceof Map ? nonLazyMap(it) : it }.collect(Collectors.toList()))
        } else {
            res.put (key, value)
        }
    }
    return res
}

... 

LazyMap lazyMap = new JsonSlurper().parseText (jsonText)
Map serializableMap = nonLazyMap(lazyMap);

या पहले की टिप्पणियों में देखा गया एक readJSON कदम का बेहतर उपयोग करें:

Map serializableMap = readJSON text: jsonText

1

इस पोस्ट में अन्य विचार सहायक थे, लेकिन बिल्कुल नहीं जिसकी मुझे तलाश थी - इसलिए मैंने उन हिस्सों को निकाला जो मेरी ज़रूरत के हिसाब से फिट होते हैं और अपने कुछ मैजिक्स जोड़े हैं ...

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

हां, जैसा कि मैंने कोड के अपने स्वयं के गिट कमिट में उल्लेख किया है, "बेतहाशा अक्षम, लेकिन छोटे गुणांक: JSON घोल समाधान" (जो मैं इस उद्देश्य के लिए ठीक हूं)। मुझे जिन पहलुओं को हल करने की आवश्यकता थी:

  1. पूरी तरह से java.io.NotSerializableExceptionसमस्या से दूर हो जाते हैं , तब भी जब JSON पाठ नेस्टेड कंटेनरों को परिभाषित करता है
  2. मैप और एरे कंटेनर दोनों के लिए काम करें
  3. LAX पार्सिंग का समर्थन करें (मेरी स्थिति के लिए सबसे महत्वपूर्ण हिस्सा)
  4. लागू करने के लिए आसान (यहां तक ​​कि अजीब नेस्टेड निर्माणकर्ताओं के साथ जो कम हो जाते हैं @NonCPS)

1

मेरी ओर से नोब गलती। एक पुराने पाइपलाइन प्लगइन, jenkins 1.6 से किसी के कोड को स्थानांतरित किया? नवीनतम 2.x जेनकींस चलाने वाले सर्वर पर।

इस कारण से विफल: "java.io.NotSerializableException: groovy.lang.IntRange" मैं उपरोक्त त्रुटि के लिए कई बार इस पोस्ट को पढ़ता और पढ़ता रहा। साकार: (संख्या में 1..numSlaves) {IntRange - गैर-धारावाहिक वस्तु प्रकार।

सरल रूप में फिर से लिखिए: (num = 1; num <= numSlaves; num ++)

दुनिया के साथ सब अच्छा है।

मैं बहुत बार जावा या ग्रूवी का उपयोग नहीं करता हूं।

धन्यवाद दोस्तों।


0

मुझे जेनकिंस पाइपलाइन के लिए डॉक्स ऑफ में अधिक आसान तरीका मिला

काम का उदाहरण

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)
}

वर्कफ़्लो में सीमाओं के कारण - यानी, जेनकेनएस -26481 - ग्रूवी क्लोजर या सिंटैक्स का उपयोग करना वास्तव में संभव नहीं है जो क्लोजर पर निर्भर करता है, इसलिए आप सूची में .collectEntries का उपयोग करने और मान के रूप में चरणों को उत्पन्न करने का ग्रूवी मानक नहीं कर सकते। परिणामी प्रविष्टियों के लिए। आप फॉर लूप्स के लिए मानक> जावा सिंटैक्स का उपयोग नहीं कर सकते हैं - यानी, (स्ट्रिंग s: स्ट्रिंग्स के लिए) "- और इसके बजाय पुराने स्कूल काउंटर-आधारित लूप का उपयोग करना होगा।


1
इसके बजाय जेनकींस पाइपलाइन स्टेप रीडजोन का उपयोग करने की सिफारिश करेंगे - यहां अधिक जानकारी प्राप्त करें
Sunvic
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.