argoproj / argo-workflows

Workflow Engine for Kubernetes
https://argo-workflows.readthedocs.io/
Apache License 2.0
15.08k stars 3.2k forks source link

v3.3.9 still has serialization problems #9692

Open kodjo-anipah opened 2 years ago

kodjo-anipah commented 2 years ago
    After I used v3.3.9, I found that there was still a serialization problem. It was not the original DateTime problem but the java.time.Instant problem. I created a new package` io.argoproj.workflow` in my project, which added `JSON.java`, then copy `io.argoproj.workflow.JSON` in the sdk source code and modify it in my `JSON.java`, and modify the JSON constructor as follows:
public class JSON {
//add
private InstantTypeAdapter instantTypeAdapter = new InstantTypeAdapter();
//edit
public JSON() {
        gson = createGson()
                .registerTypeAdapter(Date.class, dateTypeAdapter)
                .registerTypeAdapter(Instant.class, instantTypeAdapter)
                .registerTypeAdapter(java.sql.Date.class, sqlDateTypeAdapter)
                .registerTypeAdapter(OffsetDateTime.class, offsetDateTimeTypeAdapter)
                .registerTypeAdapter(LocalDate.class, localDateTypeAdapter)
                .registerTypeAdapter(byte[].class, byteArrayAdapter)
                .create();
    }
//add 
public static class InstantTypeAdapter extends TypeAdapter<Instant> {

        private Instant dateFormat;

        public InstantTypeAdapter() {
        }

        public InstantTypeAdapter(Instant instant) {
            this.dateFormat = dateFormat;
        }

        public void setFormat(Instant instant) {
            this.dateFormat = dateFormat;
        }

        @Override
        public void write(JsonWriter out, Instant date) throws IOException {
            if (date == null) {
                out.nullValue();
            } else {
                out.value(FORMATTER.format(date));
            }
        }

        @Override
        public Instant read(JsonReader in) throws IOException {
            try {
                switch (in.peek()) {
                    case NULL:
                        in.nextNull();
                        return null;
                    default:
                        String date = in.nextString();
                        if (date == null) {
                            return null;
                        }
                        return FORMATTER.parse(date, Instant::from);

                }
            } catch (IllegalArgumentException e) {
                throw new JsonParseException(e);
            }
        }
    }

}

}

Originally posted by @sanqiuli in https://github.com/argoproj/argo-workflows/issues/9148#issuecomment-1250563985

Creating a new issue for more visibility

sarabala1979 commented 2 years ago

@kodjo-anipah Do you like to fix this issue?

stale[bot] commented 2 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. If this is a mentoring request, please provide an update here. Thank you for your contributions.

jreynard-code commented 2 years ago

Hello guys,

Tested with argo-client-java:v3.4.3 same problem... Would it be fixed it in next version?

Best regards


If you want to see the stack trace error:

com.google.gson.JsonIOException: Failed making field 'java.time.Instant#seconds' accessible; either change its visibility or write a custom TypeAdapter for its declaring type
        at com.google.gson.internal.reflect.ReflectionHelper.makeAccessible(ReflectionHelper.java:23)
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:203)
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:112)
        at com.google.gson.Gson.getDelegateAdapter(Gson.java:614)
        at io.gsonfire.gson.WrapTypeAdapterFactory.create(WrapTypeAdapterFactory.java:26)
        at com.google.gson.Gson.getAdapter(Gson.java:531)
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.createBoundField(ReflectiveTypeAdapterFactory.java:137)
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:211)
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:112)
        at com.google.gson.Gson.getDelegateAdapter(Gson.java:614)
        at io.gsonfire.gson.WrapTypeAdapterFactory.create(WrapTypeAdapterFactory.java:26)
        at com.google.gson.Gson.getAdapter(Gson.java:531)
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.createBoundField(ReflectiveTypeAdapterFactory.java:137)
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:211)
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:112)
        at com.google.gson.Gson.getDelegateAdapter(Gson.java:614)
        at io.gsonfire.gson.WrapTypeAdapterFactory.create(WrapTypeAdapterFactory.java:26)
        at com.google.gson.Gson.getAdapter(Gson.java:531)
        at com.google.gson.Gson.fromJson(Gson.java:1057)
        at com.google.gson.Gson.fromJson(Gson.java:1016)
        at com.google.gson.Gson.fromJson(Gson.java:959)
        at io.argoproj.workflow.JSON.deserialize(JSON.java:146)
        at io.argoproj.workflow.ApiClient.deserialize(ApiClient.java:795)
        at io.argoproj.workflow.ApiClient.handleResponse(ApiClient.java:1001)
        at io.argoproj.workflow.ApiClient.execute(ApiClient.java:925)
        at io.argoproj.workflow.apis.ArchivedWorkflowServiceApi.archivedWorkflowServiceGetArchivedWorkflowWithHttpInfo(ArchivedWorkflowServiceApi.java:271)
        at io.argoproj.workflow.apis.ArchivedWorkflowServiceApi.archivedWorkflowServiceGetArchivedWorkflow(ArchivedWorkflowServiceApi.java:251)
        ...
Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make field private final long java.time.Instant.seconds accessible: module java.base does not "opens java.time" to unnamed module @3e07d849
        at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354)
        at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297)
        at java.base/java.lang.reflect.Field.checkCanSetAccessible(Field.java:178)
        at java.base/java.lang.reflect.Field.setAccessible(Field.java:172)
        at com.google.gson.internal.reflect.ReflectionHelper.makeAccessible(ReflectionHelper.java:20)
        ... 168 common frames omitted
vcarluer commented 1 year ago

Found a way to go around since problem until it is solved: ApiClient exposes get/setJSON() and JSON exposes get/setgson(). So when creating the ApiClient you can

Example in kotlin:


  private val apiClient: ApiClient by lazy {
    Configuration.getDefaultApiClient().apply {
      isVerifyingSsl = false
      basePath = csmPlatformProperties.argo.baseUri
      httpClient = unsafeOkHttpClient
      isDebugging = logger.isTraceEnabled
      setUserAgent("com.cosmotech/cosmotech-api $apiVersion")
      json =
          json.apply {
            gson =
                gson
                    .newBuilder()
                    .registerTypeAdapter(java.time.Instant::class.java, InstantTypeAdapter())
                    .create()
          }
    }
  }

InstantTypeAdapter.kt


// Copyright (c) Cosmo Tech.
// Licensed under the MIT license.
package com.cosmotech.scenariorun.workflow.argo

import com.google.gson.TypeAdapter
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonToken
import com.google.gson.stream.JsonWriter
import java.time.Instant

class InstantTypeAdapter : TypeAdapter<Instant>() {
  override fun write(out: JsonWriter, value: Instant?) {
    if (value == null) {
      out.nullValue()
    } else {
      out.value(value.toString())
    }
  }

  override fun read(`in`: JsonReader): Instant? {
    return if (`in`.peek() == JsonToken.NULL) {
      `in`.nextNull()
      null
    } else {
      Instant.parse(`in`.nextString())
    }
  }
}
liuyix commented 1 year ago

After serveral hours hunting, I may have found a solution for this issue.

stale[bot] commented 1 year ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. If this is a mentoring request, please provide an update here. Thank you for your contributions.

pmbrull commented 1 year ago

Seeing the same happening in 3.4.5 for the Java SDK

RyanDevlin commented 1 year ago

I can confirm that this bug is also in the v.3.4.7 client and the TypeAdapter above fixes the issue.

stale[bot] commented 1 year ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. If this is a mentoring request, please provide an update here. Thank you for your contributions.