Example #1
0
  // TODO: move these converters in converter package
  private Map<AdapterKey, Adapter<?, ?>> createJava8Converters(final MapperBuilder builder) {
    final Map<AdapterKey, Adapter<?, ?>> converters = new HashMap<>();

    final TimeZone timeZoneUTC = TimeZone.getTimeZone("UTC");
    final ZoneId zoneIDUTC = ZoneId.of("UTC");

    // built-in converters not in mapper
    converters.put(
        new AdapterKey(Period.class, String.class),
        new ConverterAdapter<>(
            new Converter<Period>() {
              @Override
              public String toString(final Period instance) {
                return instance.toString();
              }

              @Override
              public Period fromString(final String text) {
                return Period.parse(text);
              }
            }));
    converters.put(
        new AdapterKey(Duration.class, String.class),
        new ConverterAdapter<>(
            new Converter<Duration>() {
              @Override
              public String toString(final Duration instance) {
                return instance.toString();
              }

              @Override
              public Duration fromString(final String text) {
                return Duration.parse(text);
              }
            }));
    converters.put(
        new AdapterKey(Date.class, String.class),
        new ConverterAdapter<>(
            new Converter<Date>() {
              @Override
              public String toString(final Date instance) {
                return LocalDateTime.ofInstant(instance.toInstant(), zoneIDUTC).toString();
              }

              @Override
              public Date fromString(final String text) {
                return Date.from(LocalDateTime.parse(text).toInstant(ZoneOffset.UTC));
              }
            }));
    converters.put(
        new AdapterKey(Calendar.class, String.class),
        new ConverterAdapter<>(
            new Converter<Calendar>() {
              @Override
              public String toString(final Calendar instance) {
                return ZonedDateTime.ofInstant(instance.toInstant(), zoneIDUTC).toString();
              }

              @Override
              public Calendar fromString(final String text) {
                final Calendar calendar = Calendar.getInstance();
                calendar.setTimeZone(timeZoneUTC);
                calendar.setTimeInMillis(ZonedDateTime.parse(text).toInstant().toEpochMilli());
                return calendar;
              }
            }));
    converters.put(
        new AdapterKey(GregorianCalendar.class, String.class),
        new ConverterAdapter<>(
            new Converter<GregorianCalendar>() {
              @Override
              public String toString(final GregorianCalendar instance) {
                return instance.toZonedDateTime().toString();
              }

              @Override
              public GregorianCalendar fromString(final String text) {
                final GregorianCalendar calendar = new GregorianCalendar();
                calendar.setTimeZone(timeZoneUTC);
                calendar.setTimeInMillis(ZonedDateTime.parse(text).toInstant().toEpochMilli());
                return calendar;
              }
            }));
    converters.put(
        new AdapterKey(TimeZone.class, String.class),
        new ConverterAdapter<>(
            new Converter<TimeZone>() {
              @Override
              public String toString(final TimeZone instance) {
                return instance.getID();
              }

              @Override
              public TimeZone fromString(final String text) {
                logIfDeprecatedTimeZone(text);
                return TimeZone.getTimeZone(text);
              }
            }));
    converters.put(
        new AdapterKey(ZoneId.class, String.class),
        new ConverterAdapter<>(
            new Converter<ZoneId>() {
              @Override
              public String toString(final ZoneId instance) {
                return instance.getId();
              }

              @Override
              public ZoneId fromString(final String text) {
                return ZoneId.of(text);
              }
            }));
    converters.put(
        new AdapterKey(ZoneOffset.class, String.class),
        new ConverterAdapter<>(
            new Converter<ZoneOffset>() {
              @Override
              public String toString(final ZoneOffset instance) {
                return instance.getId();
              }

              @Override
              public ZoneOffset fromString(final String text) {
                return ZoneOffset.of(text);
              }
            }));
    converters.put(
        new AdapterKey(SimpleTimeZone.class, String.class),
        new ConverterAdapter<>(
            new Converter<SimpleTimeZone>() {
              @Override
              public String toString(final SimpleTimeZone instance) {
                return instance.getID();
              }

              @Override
              public SimpleTimeZone fromString(final String text) {
                logIfDeprecatedTimeZone(text);
                final TimeZone timeZone = TimeZone.getTimeZone(text);
                return new SimpleTimeZone(timeZone.getRawOffset(), timeZone.getID());
              }
            }));
    converters.put(
        new AdapterKey(Instant.class, String.class),
        new ConverterAdapter<>(
            new Converter<Instant>() {
              @Override
              public String toString(final Instant instance) {
                return instance.toString();
              }

              @Override
              public Instant fromString(final String text) {
                return Instant.parse(text);
              }
            }));
    converters.put(
        new AdapterKey(LocalDate.class, String.class),
        new ConverterAdapter<>(
            new Converter<LocalDate>() {
              @Override
              public String toString(final LocalDate instance) {
                return instance.toString();
              }

              @Override
              public LocalDate fromString(final String text) {
                return LocalDate.parse(text);
              }
            }));
    converters.put(
        new AdapterKey(LocalDateTime.class, String.class),
        new ConverterAdapter<>(
            new Converter<LocalDateTime>() {
              @Override
              public String toString(final LocalDateTime instance) {
                return instance.toString();
              }

              @Override
              public LocalDateTime fromString(final String text) {
                return LocalDateTime.parse(text);
              }
            }));
    converters.put(
        new AdapterKey(ZonedDateTime.class, String.class),
        new ConverterAdapter<>(
            new Converter<ZonedDateTime>() {
              @Override
              public String toString(final ZonedDateTime instance) {
                return instance.toString();
              }

              @Override
              public ZonedDateTime fromString(final String text) {
                return ZonedDateTime.parse(text);
              }
            }));
    converters.put(
        new AdapterKey(OffsetDateTime.class, String.class),
        new ConverterAdapter<>(
            new Converter<OffsetDateTime>() {
              @Override
              public String toString(final OffsetDateTime instance) {
                return instance.toString();
              }

              @Override
              public OffsetDateTime fromString(final String text) {
                return OffsetDateTime.parse(text);
              }
            }));
    converters.put(
        new AdapterKey(OffsetTime.class, String.class),
        new ConverterAdapter<>(
            new Converter<OffsetTime>() {
              @Override
              public String toString(final OffsetTime instance) {
                return instance.toString();
              }

              @Override
              public OffsetTime fromString(final String text) {
                return OffsetTime.parse(text);
              }
            }));
    addDateFormatConfigConverters(converters, zoneIDUTC);

    converters.forEach((k, v) -> builder.addAdapter(k.getFrom(), k.getTo(), v));
    return converters;
  }
Example #2
0
  @Override
  public Jsonb build() {
    if (jsonp != null) {
      builder.setGeneratorFactory(jsonp.createGeneratorFactory(generatorConfig()));
      builder.setReaderFactory(jsonp.createReaderFactory(emptyMap()));
    }
    final Supplier<JsonParserFactory> parserFactoryProvider =
        new Supplier<JsonParserFactory>() { // thread safety is not mandatory
          private final AtomicReference<JsonParserFactory> ref = new AtomicReference<>();

          @Override
          public JsonParserFactory get() {
            JsonParserFactory factory = ref.get();
            if (factory == null) {
              factory = doCreate();
              if (!ref.compareAndSet(null, factory)) {
                factory = ref.get();
              }
            }
            return factory;
          }

          private JsonParserFactory doCreate() {
            return (jsonp == null ? JsonProvider.provider() : jsonp)
                .createParserFactory(emptyMap());
          }
        };

    if (config == null) {
      config = new JsonbConfig();
    }

    if (config.getProperty(JsonbConfig.FORMATTING).map(Boolean.class::cast).orElse(false)) {
      builder.setPretty(true);
    }

    config
        .getProperty(JsonbConfig.ENCODING)
        .ifPresent(encoding -> builder.setEncoding(String.valueOf(encoding)));
    config
        .getProperty(JsonbConfig.NULL_VALUES)
        .ifPresent(serNulls -> builder.setSkipNull(!Boolean.class.cast(serNulls)));

    final Optional<Object> namingStrategyValue =
        config.getProperty(JsonbConfig.PROPERTY_NAMING_STRATEGY);

    final PropertyNamingStrategy propertyNamingStrategy =
        new PropertyNamingStrategyFactory(namingStrategyValue.orElse(IDENTITY)).create();
    final String orderValue =
        config
            .getProperty(JsonbConfig.PROPERTY_ORDER_STRATEGY)
            .map(String::valueOf)
            .orElse(LEXICOGRAPHICAL);
    final PropertyVisibilityStrategy visibilityStrategy =
        config
            .getProperty(JsonbConfig.PROPERTY_VISIBILITY_STRATEGY)
            .map(PropertyVisibilityStrategy.class::cast)
            .orElse(
                new PropertyVisibilityStrategy() {
                  private final ConcurrentMap<Class<?>, PropertyVisibilityStrategy> strategies =
                      new ConcurrentHashMap<>();

                  @Override
                  public boolean isVisible(final Field field) {
                    final PropertyVisibilityStrategy strategy =
                        strategies.computeIfAbsent(
                            field.getDeclaringClass(), this::visibilityStrategy);
                    return strategy == this
                        ? Modifier.isPublic(field.getModifiers())
                        : strategy.isVisible(field);
                  }

                  @Override
                  public boolean isVisible(final Method method) {
                    final PropertyVisibilityStrategy strategy =
                        strategies.computeIfAbsent(
                            method.getDeclaringClass(), this::visibilityStrategy);
                    return strategy == this
                        ? Modifier.isPublic(method.getModifiers())
                        : strategy.isVisible(method);
                  }

                  private PropertyVisibilityStrategy visibilityStrategy(
                      final Class<?> type) { // can be cached
                    Package p = type.getPackage();
                    while (p != null) {
                      final JsonbVisibility visibility = p.getAnnotation(JsonbVisibility.class);
                      if (visibility != null) {
                        try {
                          return visibility.value().newInstance();
                        } catch (final InstantiationException | IllegalAccessException e) {
                          throw new IllegalArgumentException(e);
                        }
                      }
                      final String name = p.getName();
                      final int end = name.lastIndexOf('.');
                      if (end < 0) {
                        break;
                      }
                      p = Package.getPackage(name.substring(0, end));
                    }
                    return this;
                  }
                });

    config
        .getProperty("johnzon.attributeOrder")
        .ifPresent(comp -> builder.setAttributeOrder(Comparator.class.cast(comp)));
    config
        .getProperty("johnzon.enforceQuoteString")
        .map(
            v ->
                !Boolean.class.isInstance(v)
                    ? Boolean.parseBoolean(v.toString())
                    : Boolean.class.cast(v))
        .ifPresent(builder::setEnforceQuoteString);
    config
        .getProperty("johnzon.primitiveConverters")
        .map(
            v ->
                !Boolean.class.isInstance(v)
                    ? Boolean.parseBoolean(v.toString())
                    : Boolean.class.cast(v))
        .ifPresent(builder::setPrimitiveConverters);

    final Map<AdapterKey, Adapter<?, ?>> defaultConverters = createJava8Converters(builder);

    final JohnzonAdapterFactory factory =
        config
            .getProperty("johnzon.factory")
            .map(
                val -> {
                  if (JohnzonAdapterFactory.class.isInstance(val)) {
                    return JohnzonAdapterFactory.class.cast(val);
                  }
                  if (String.class.isInstance(val)) {
                    try {
                      return JohnzonAdapterFactory.class.cast(
                          tccl().loadClass(val.toString()).newInstance());
                    } catch (final InstantiationException
                        | ClassNotFoundException
                        | IllegalAccessException e) {
                      throw new IllegalArgumentException(e);
                    }
                  }
                  if (Class.class.isInstance(val)) {
                    try {
                      return JohnzonAdapterFactory.class.cast(Class.class.cast(val).newInstance());
                    } catch (final InstantiationException | IllegalAccessException e) {
                      throw new IllegalArgumentException(e);
                    }
                  }
                  throw new IllegalArgumentException("Unsupported factory: " + val);
                })
            .orElseGet(this::findFactory);
    final JsonbAccessMode accessMode =
        new JsonbAccessMode(
            propertyNamingStrategy,
            orderValue,
            visibilityStrategy,
            !namingStrategyValue.orElse("").equals(PropertyNamingStrategy.CASE_INSENSITIVE),
            defaultConverters,
            factory,
            parserFactoryProvider);
    builder.setAccessMode(accessMode);

    // user adapters
    config
        .getProperty(JsonbConfig.ADAPTERS)
        .ifPresent(
            adapters ->
                Stream.of(JsonbAdapter[].class.cast(adapters))
                    .forEach(
                        adapter -> {
                          final ParameterizedType pt =
                              ParameterizedType.class.cast(
                                  Stream.of(adapter.getClass().getGenericInterfaces())
                                      .filter(
                                          i ->
                                              ParameterizedType.class.isInstance(i)
                                                  && ParameterizedType.class.cast(i).getRawType()
                                                      == JsonbAdapter.class)
                                      .findFirst()
                                      .orElse(null));
                          if (pt == null) {
                            throw new IllegalArgumentException(
                                adapter + " doesn't implement JsonbAdapter");
                          }
                          final Type[] args = pt.getActualTypeArguments();
                          builder.addAdapter(
                              args[0], args[1], new JohnzonJsonbAdapter(adapter, args[0], args[1]));
                        }));

    config
        .getProperty(JsonbConfig.STRICT_IJSON)
        .map(Boolean.class::cast)
        .ifPresent(
            ijson -> {
              // no-op: https://tools.ietf.org/html/rfc7493 the only MUST of the spec should be fine
              // by default
            });

    config
        .getProperty(JsonbConfig.BINARY_DATA_STRATEGY)
        .map(String.class::cast)
        .ifPresent(
            bin -> {
              switch (bin) {
                case BinaryDataStrategy.BYTE:
                  // no-op: our default
                  break;
                case BinaryDataStrategy.BASE_64:
                  builder.setTreatByteArrayAsBase64(true);
                  break;
                case BinaryDataStrategy.BASE_64_URL: // needs j8
                  builder.addConverter(
                      byte[].class,
                      new Converter<byte[]>() {
                        @Override
                        public String toString(final byte[] instance) {
                          return Base64.getUrlEncoder().encodeToString(instance);
                        }

                        @Override
                        public byte[] fromString(final String text) {
                          return Base64.getUrlDecoder()
                              .decode(text.getBytes(StandardCharsets.UTF_8));
                        }
                      });
                  break;
                default:
                  throw new IllegalArgumentException("Unsupported binary configuration: " + bin);
              }
            });

    getBeanManager(); // force detection

    builder.setReadAttributeBeforeWrite(
        config
            .getProperty("johnzon.readAttributeBeforeWrite")
            .map(Boolean.class::cast)
            .orElse(false));

    config
        .getProperty(JsonbConfig.SERIALIZERS)
        .map(JsonbSerializer[].class::cast)
        .ifPresent(
            serializers -> {
              Stream.of(serializers)
                  .forEach(
                      s -> {
                        final ParameterizedType pt = findPT(s, JsonbSerializer.class);
                        if (pt == null) {
                          throw new IllegalArgumentException(
                              s + " doesn't implement JsonbSerializer");
                        }
                        final Type[] args = pt.getActualTypeArguments();
                        // TODO: support PT in ObjectConverter (list)
                        if (args.length != 1 || !Class.class.isInstance(args[0])) {
                          throw new IllegalArgumentException(
                              "We only support serializer on Class for now");
                        }
                        builder.addObjectConverter(
                            Class.class.cast(args[0]),
                            (ObjectConverter.Writer)
                                (instance, jsonbGenerator) ->
                                    s.serialize(
                                        instance,
                                        jsonbGenerator.getJsonGenerator(),
                                        new JohnzonSerializationContext(jsonbGenerator)));
                      });
            });
    config
        .getProperty(JsonbConfig.DESERIALIZERS)
        .map(JsonbDeserializer[].class::cast)
        .ifPresent(
            deserializers -> {
              Stream.of(deserializers)
                  .forEach(
                      d -> {
                        final ParameterizedType pt = findPT(d, JsonbDeserializer.class);
                        if (pt == null) {
                          throw new IllegalArgumentException(
                              d + " doesn't implement JsonbDeserializer");
                        }
                        final Type[] args = pt.getActualTypeArguments();
                        if (args.length != 1 || !Class.class.isInstance(args[0])) {
                          throw new IllegalArgumentException(
                              "We only support deserializer on Class for now");
                        }
                        // TODO: support PT in ObjectConverter (list)
                        builder.addObjectConverter(
                            Class.class.cast(args[0]),
                            (ObjectConverter.Reader)
                                (jsonObject, targetType, parser) ->
                                    d.deserialize(
                                        parserFactoryProvider.get().createParser(jsonObject),
                                        new JohnzonDeserializationContext(parser),
                                        targetType));
                      });
            });

    final boolean useCdi =
        cdiIntegration != null
            && cdiIntegration.isCanWrite()
            && config
                .getProperty("johnzon.cdi.activated")
                .map(Boolean.class::cast)
                .orElse(Boolean.TRUE);
    final Mapper mapper = builder.addCloseable(accessMode).build();

    return useCdi
        ? new JohnsonJsonb(mapper) {
          {
            cdiIntegration.track(this);
          }

          @Override
          public void close() {
            try {
              super.close();
            } finally {
              if (cdiIntegration.isCanWrite()) {
                cdiIntegration.untrack(this);
              }
            }
          }
        }
        : new JohnsonJsonb(mapper);
  }