2016-06-16 17 views
26

rozwiązać: Dzięki below answer od S.Richmond. Musiałem zdezaktywować zapisane mapy typu typu groovy.json.internal.LazyMap, co oznaczało usunięcie po użyciu zmiennych envServers i.Jenkins Pipeline NotSerializableException: groovy.json.internal.LazyMap

Dodatkowe: Osoby poszukujące tego błędu może być zainteresowany w użyciu krok rurociągu Jenkins readJSON zamiast - znaleźć więcej informacji here.


Próbuję użyć Jenkins Pipeline do pobrania danych wejściowych od użytkownika, który jest przekazywany do zadania jako ciąg znaków json. Pipeline następnie analizuje to za pomocą slurpera i wybieram ważne informacje. Następnie użyje tych informacji, aby wielokrotnie uruchamiać 1 zadanie równolegle z różnymi parametrami zadania.

Do czasu dodania kodu poniżej "## Error when below here is added" skrypt będzie działał poprawnie. Nawet kod pod tym punktem będzie działał sam. Ale po połączeniu otrzymuję poniższy błąd.

Należy zauważyć, że wyzwalane zadanie jest wywoływane i działa poprawnie, ale występuje poniżej błąd i zawiedzie główne zadanie. Z tego powodu główne zadanie nie czeka na powrót wyzwalanej pracy. I Mogę próbować/złapać wokół build job: jednak chcę, aby główne zadanie czekać na zakończenie wyzwalanego zadania.

Czy ktoś może pomóc tutaj? Jeśli potrzebujesz więcej informacji, daj mi znać.

Cheers

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']] 

} 

Błąd:

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

Właśnie wpadł ten sam. Czy zrobiłeś jeszcze jakieś postępy? –

+2

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

Odpowiedz

21

wpadłem na ten sam dzisiaj i przez jakiś bruteforce Mam zorientowali się, zarówno jak go rozwiązać i potencjalnie dlaczego.

Prawdopodobnie najlepiej zacząć dlaczego:

Jenkins ma paradygmat gdzie wszystkie prace mogą zostać przerwane, wstrzymane i wznawiania po ponownym uruchomieniu serwera. Aby to osiągnąć, potok i jego dane muszą być w pełni przekształcalne do postaci cyfrowej - IE musi być w stanie uratować stan wszystkiego. Podobnie, musi być w stanie szeregować stan zmiennych globalnych między węzłami i pod-zadaniami w kompilacji, co moim zdaniem dzieje się dla ciebie i dla mnie, i dlatego, że występuje tylko wtedy, gdy dodasz dodatkowy krok kompilacji.

Z dowolnego powodu JSONObject nie są domyślnie serializowane. Nie jestem programistą Java, więc niestety nie mogę powiedzieć nic więcej na ten temat. Istnieje wiele odpowiedzi na temat tego, jak można to naprawić prawidłowo, chociaż nie wiem, jak mają one zastosowanie do Groovy'ego i Jenkinsa. See this post, aby uzyskać więcej informacji.

Jak to naprawić:

Jeśli wiesz, jak można ewentualnie zrobić JSONObject serializacji jakoś. W przeciwnym razie można go rozwiązać, upewniając się, że nie istnieją zmienne globalne tego typu.

Spróbuj odseparować zmienną object lub zawinąć ją w sposób, aby jej zasięg nie był globalny węzła.

+0

Dzięki, To jest wskazówka, którą muszę rozwiązać. Chociaż już wypróbowałem twoją sugestię. Sprawiłem, że znów spojrzałem i nie myślałem, że przechowuję części mapy w innych zmiennych - to one powodują błędy. Więc też musiałem ich rozbroić. Zmienię moje pytanie w celu uwzględnienia poprawnych zmian w kodzie. Pozdrawiam – Sunvic

+0

To jest oglądane ~ 8 razy dziennie. Czy chcielibyście przedstawić bardziej szczegółowy przykład wdrożenia tego rozwiązania? –

+0

Nie ma prostego rozwiązania, ponieważ zależy to od tego, co zrobiłeś. Informacje podane tutaj, a także rozwiązanie @Sunvic dodane na górze jego postu wystarczy, aby doprowadzić do rozwiązania własnego kodu. –

53

Zamiast tego należy użyć JsonSlurperClassic.

Od Groovy 2.3 (uwaga: Jenkins 2.7.1 używa Groovy 2.4.7) JsonSlurper powraca LazyMap zamiast HashMap. Dzięki temu możliwa jest seryjna nowa implementacja bezpiecznego wątku i nie jest możliwa To sprawia, że ​​nie nadaje się do użytku poza funkcjami @NonDSL w skryptowych skryptach DSL.

Można jednak cofnąć się do numeru groovy.json.JsonSlurperClassic, który obsługuje stary kod behavior i można go bezpiecznie używać w skryptach potoku.

Przykład

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. Nadal musisz zatwierdzić JsonSlurperClassic, zanim będzie można go wywołać.

+2

Czy możesz mi powiedzieć, jak zatwierdzić 'JsonSlurperClassic'? – mybecks

+5

Administrator Jenkins będzie musiał przejść do Zarządzaj Jenkins »W trakcie procesu zatwierdzania skryptu. – luka5z

+0

Niestety tylko dostać 'hudson.remoting.ProxyException: org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed: Script1.groovy: 24: w stanie rozwiązać klasę groovy.json.JsonSlurperClassic' – dvtoever

7

EDIT: Jak podkreślił @Sunvic w komentarzach, poniżej rozwiązanie nie działa, jak jest dla JSON tablic.

Zajmowałem się tym przy użyciu JsonSlurper, a następnie tworząc nowy HashMap z leniwych wyników. HashMap jest Serializable.

Uważam, że wymagało to białej listy zarówno new HashMap(Map), jak i JsonSlurper.

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

Nie działa mnie - ciągle pojawia się błąd 'Nie można znaleźć zgodnego konstruktora dla: java.util.HashMap (java.util.ArrayList)'. Dokumentacja sugeruje, że powinna wypluć listę lub mapę - jak skonfigurować, aby zwrócić mapę? – Sunvic

+0

@Sunvic Dobry catch, dane, które analizowaliśmy, są zawsze obiektami, nigdy tablicami JSON. Czy próbujesz parsować tablicę JSON? – mkobit

+0

Ach tak, to jest tablica JSON, to będzie to. – Sunvic

1

Sposób wtyczki rurociąg został wdrożony ma dość poważne konsekwencje dla nietrywialne kodu Groovy. Ten link wyjaśnia, w jaki sposób, aby uniknąć ewentualnych problemów: https://github.com/jenkinsci/pipeline-plugin/blob/master/TUTORIAL.md#serializing-local-variables

w Twoim konkretnym przypadku Pomyślę dodawanie adnotacji do slurpJSON@NonCPS i powrocie MAP-of-map zamiast obiektu JSON. Nie tylko kod wygląda czysto, ale jest także bardziej wydajny, zwłaszcza jeśli ten JSON jest złożony.

4

Nieco bardziej uogólniona postać odpowiedź od @mkobit która pozwoliłaby dekodowanie tablic, jak również mapy byłoby:

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 
} 

UWAGA: Należy pamiętać, że będzie to tylko konwertować obiekt LazyMap do najwyższego poziomu a HashMap. Wszelkie zagnieżdżone obiekty LazyMap nadal będą istniały i nadal będą powodować problemy z Jenkinsem.

0

znalazłem łatwiejszy sposób off docs for Jenkins pipeline

przykład Work

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

Zaleca się użycie kroku potoku Jenkinsa zamiast readJSON - znajdź więcej informacji [tutaj] (https://jenkins.io/doc/pipeline/steps/pipeline- narzędzie-steps/# code-readjson-code-read-json-from-files-in-the-workspace). – Sunvic

0

drugiej pomysłów w tym poście były pomocne, ale nie do końca wszystko szukałem - więc ekstrakcji części, które pasują do mojej potrzeby i dodałem trochę mojego magika ...

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

Tak, jak już zauważono w moim git commit kodu, „dziko-ineffecient, ale bardzo mały współczynnik: JSON rozwiązanie slurp” (co jestem w porządku z tym celu). Aspekty Musiałem rozwiązać:

  1. całkowicie uciec od problemu java.io.NotSerializableException, nawet gdy tekst JSON określa zagnieżdżonych pojemników
  2. Praca dla obu map i tablic pojemników
  3. LAX Pomoc dekodowaniu (najważniejszy część, dla mojej sytuacji)
  4. Łatwy do wdrożenia (nawet z nietypowej zagnieżdżonych konstruktorów, że wyeliminowanie @NonCPS)
Powiązane problemy