From 17c65991e8d4c4d4bac835e6867294e0a7784076 Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Tue, 27 Jul 2021 12:43:11 +0200 Subject: [PATCH] [jOOQ/jOOQ#12262] Delay initialisation of Jackson, Gson, JAXB classes in Convert --- jOOQ/src/main/java/org/jooq/impl/Convert.java | 157 ++++++++++-------- 1 file changed, 84 insertions(+), 73 deletions(-) diff --git a/jOOQ/src/main/java/org/jooq/impl/Convert.java b/jOOQ/src/main/java/org/jooq/impl/Convert.java index db44ccaff2..922d2d9195 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Convert.java +++ b/jOOQ/src/main/java/org/jooq/impl/Convert.java @@ -148,26 +148,6 @@ final class Convert { */ private static final Pattern UUID_PATTERN = Pattern.compile("(\\p{XDigit}{8})-?(\\p{XDigit}{4})-?(\\p{XDigit}{4})-?(\\p{XDigit}{4})-?(\\p{XDigit}{12})"); - /** - * The Jackson ObjectMapper or Gson instance, if available. - */ - private static final Object JSON_MAPPER; - - /** - * The Jackson ObjectMapper::readValue or Gson::fromJson method, if available. - */ - private static final Method JSON_READ_METHOD; - - /** - * The Jackson ObjectMapper::writeValueToString or Gson::toJson method, if available. - */ - private static final Method JSON_WRITE_METHOD; - - /** - * Whether a JAXB implementation is available. - */ - private static final boolean JAXB_AVAILABLE; - static { Set trueValues = new HashSet<>(); Set falseValues = new HashSet<>(); @@ -204,62 +184,93 @@ final class Convert { TRUE_VALUES = Collections.unmodifiableSet(trueValues); FALSE_VALUES = Collections.unmodifiableSet(falseValues); + } - Object jsonMapper = null; - Method jsonReadMethod = null; - Method jsonWriteMethod = null; - boolean jaxbAvailable = false; + private static final class _JSON { - try { - Class klass = Class.forName("com.fasterxml.jackson.databind.ObjectMapper"); + /** + * The Jackson ObjectMapper or Gson instance, if available. + */ + private static final Object JSON_MAPPER; + + /** + * The Jackson ObjectMapper::readValue or Gson::fromJson method, if available. + */ + private static final Method JSON_READ_METHOD; + + /** + * The Jackson ObjectMapper::writeValueToString or Gson::toJson method, if available. + */ + private static final Method JSON_WRITE_METHOD; + + static { + Object jsonMapper = null; + Method jsonReadMethod = null; + Method jsonWriteMethod = null; try { - Class kotlin = Class.forName("com.fasterxml.jackson.module.kotlin.ExtensionsKt"); - jsonMapper = kotlin.getMethod("jacksonObjectMapper").invoke(kotlin); - log.debug("Jackson kotlin module is available"); + Class klass = Class.forName("com.fasterxml.jackson.databind.ObjectMapper"); + + try { + Class kotlin = Class.forName("com.fasterxml.jackson.module.kotlin.ExtensionsKt"); + jsonMapper = kotlin.getMethod("jacksonObjectMapper").invoke(kotlin); + log.debug("Jackson kotlin module is available"); + } + catch (Exception e) { + jsonMapper = klass.getDeclaredConstructor().newInstance(); + log.debug("Jackson kotlin module is not available"); + } + + jsonReadMethod = klass.getMethod("readValue", String.class, Class.class); + jsonWriteMethod = klass.getMethod("writeValueAsString", Object.class); + log.debug("Jackson is available"); } - catch (Exception e) { - jsonMapper = klass.getDeclaredConstructor().newInstance(); - log.debug("Jackson kotlin module is not available"); + catch (Exception e1) { + log.debug("Jackson not available", e1.getMessage()); + + try { + Class klass = Class.forName("com.google.gson.Gson"); + + jsonMapper = klass.getDeclaredConstructor().newInstance(); + jsonReadMethod = klass.getMethod("fromJson", String.class, Class.class); + jsonWriteMethod = klass.getMethod("toJson", Object.class); + log.debug("Gson is available"); + } + catch (Exception e2) { + log.debug("Gson not available", e2.getMessage()); + } } - jsonReadMethod = klass.getMethod("readValue", String.class, Class.class); - jsonWriteMethod = klass.getMethod("writeValueAsString", Object.class); - log.debug("Jackson is available"); + JSON_MAPPER = jsonMapper; + JSON_READ_METHOD = jsonReadMethod; + JSON_WRITE_METHOD = jsonWriteMethod; } - catch (Exception e1) { - log.debug("Jackson not available", e1.getMessage()); + } + + private static final class _XML { + + /** + * Whether a JAXB implementation is available. + */ + private static final boolean JAXB_AVAILABLE; + + static { + boolean jaxbAvailable = false; try { - Class klass = Class.forName("com.google.gson.Gson"); - - jsonMapper = klass.getDeclaredConstructor().newInstance(); - jsonReadMethod = klass.getMethod("fromJson", String.class, Class.class); - jsonWriteMethod = klass.getMethod("toJson", Object.class); - log.debug("Gson is available"); + JAXB.marshal(new InformationSchema(), new StringWriter()); + jaxbAvailable = true; + log.debug("JAXB is available"); } - catch (Exception e2) { - log.debug("Gson not available", e2.getMessage()); + + // [#10145] Depending on whether jOOQ is modularised or not, this can also + // be a NoClassDefFoundError. + catch (Throwable t) { + log.debug("JAXB not available", t.getMessage()); } + + JAXB_AVAILABLE = jaxbAvailable; } - - JSON_MAPPER = jsonMapper; - JSON_READ_METHOD = jsonReadMethod; - JSON_WRITE_METHOD = jsonWriteMethod; - - try { - JAXB.marshal(new InformationSchema(), new StringWriter()); - jaxbAvailable = true; - log.debug("JAXB is available"); - } - - // [#10145] Depending on whether jOOQ is modularised or not, this can also - // be a NoClassDefFoundError. - catch (Throwable t) { - log.debug("JAXB not available", t.getMessage()); - } - - JAXB_AVAILABLE = jaxbAvailable; } /** @@ -1083,9 +1094,9 @@ final class Convert { } // [#10072] Out of the box Jackson JSON mapping support - else if (fromClass == JSON.class && JSON_MAPPER != null) { + else if (fromClass == JSON.class && _JSON.JSON_MAPPER != null) { try { - return (U) JSON_READ_METHOD.invoke(JSON_MAPPER, ((JSON) from).data(), toClass); + return (U) _JSON.JSON_READ_METHOD.invoke(_JSON.JSON_MAPPER, ((JSON) from).data(), toClass); } catch (Exception e) { throw new DataTypeException("Error while mapping JSON to POJO using Jackson", e); @@ -1093,9 +1104,9 @@ final class Convert { } // [#10072] Out of the box Jackson JSON mapping support - else if (fromClass == JSONB.class && JSON_MAPPER != null) { + else if (fromClass == JSONB.class && _JSON.JSON_MAPPER != null) { try { - return (U) JSON_READ_METHOD.invoke(JSON_MAPPER, ((JSONB) from).data(), toClass); + return (U) _JSON.JSON_READ_METHOD.invoke(_JSON.JSON_MAPPER, ((JSONB) from).data(), toClass); } catch (Exception e) { throw new DataTypeException("Error while mapping JSON to POJO using Jackson", e); @@ -1104,9 +1115,9 @@ final class Convert { // [#11213] Workaround for a problem when Jackson or Gson do not know // the generic List type because toClass has its generics erased - else if (Map.class.isAssignableFrom(fromClass) && JSON_MAPPER != null) { + else if (Map.class.isAssignableFrom(fromClass) && _JSON.JSON_MAPPER != null) { try { - return (U) JSON_READ_METHOD.invoke(JSON_MAPPER, JSON_WRITE_METHOD.invoke(JSON_MAPPER, from), toClass); + return (U) _JSON.JSON_READ_METHOD.invoke(_JSON.JSON_MAPPER, _JSON.JSON_WRITE_METHOD.invoke(_JSON.JSON_MAPPER, from), toClass); } catch (Exception e) { throw new DataTypeException("Error while mapping JSON to POJO using Jackson", e); @@ -1114,7 +1125,7 @@ final class Convert { } // [#10072] Out of the box JAXB mapping support - else if (fromClass == XML.class && JAXB_AVAILABLE) { + else if (fromClass == XML.class && _XML.JAXB_AVAILABLE) { try { return JAXB.unmarshal(new StringReader(((XML) from).data()), toClass); } @@ -1318,13 +1329,13 @@ final class Convert { return UUID.fromString(UUID_PATTERN.matcher(string).replaceAll("$1-$2-$3-$4-$5")); } - private static DataTypeException fail(Object from, Class toClass) { + private static final DataTypeException fail(Object from, Class toClass) { String message = "Cannot convert from " + from + " (" + from.getClass() + ") to " + toClass; // [#10072] [#11023] Some mappings may not have worked because of badly set up classpaths - if ((from instanceof JSON || from instanceof JSONB) && JSON_MAPPER == null) + if ((from instanceof JSON || from instanceof JSONB) && _JSON.JSON_MAPPER == null) return new DataTypeException(message + ". Check your classpath to see if Jackson or Gson is available to jOOQ."); - else if (from instanceof XML && !JAXB_AVAILABLE) + else if (from instanceof XML && !_XML.JAXB_AVAILABLE) return new DataTypeException(message + ". Check your classpath to see if JAXB is available to jOOQ."); else return new DataTypeException(message);