Example #1
0
 private Map<String, ?> generatorConfig() {
   final Map<String, Object> map = new HashMap<>();
   if (config == null) {
     return map;
   }
   config
       .getProperty(JsonGeneratorFactoryImpl.GENERATOR_BUFFER_LENGTH)
       .ifPresent(b -> map.put(JsonGeneratorFactoryImpl.GENERATOR_BUFFER_LENGTH, b));
   config
       .getProperty(AbstractJsonFactory.BUFFER_STRATEGY)
       .ifPresent(b -> map.put(AbstractJsonFactory.BUFFER_STRATEGY, b));
   config
       .getProperty(JsonbConfig.FORMATTING)
       .ifPresent(b -> map.put(JsonGenerator.PRETTY_PRINTING, b));
   return map;
 }
Example #2
0
 private JohnzonAdapterFactory findFactory() {
   if (getBeanManager() == NO_BM
       || config
           .getProperty("johnzon.skip-cdi")
           .map(s -> "true".equalsIgnoreCase(String.valueOf(s)))
           .orElse(false)) {
     return new SimpleJohnzonAdapterFactory();
   }
   try { // don't trigger CDI is not there
     return new org.apache.johnzon.jsonb.factory.CdiJohnzonAdapterFactory(beanManager);
   } catch (final NoClassDefFoundError | Exception e) {
     return new SimpleJohnzonAdapterFactory();
   }
 }
Example #3
0
  private void addDateFormatConfigConverters(
      final Map<AdapterKey, Adapter<?, ?>> converters, final ZoneId zoneIDUTC) {
    // config, override defaults
    config
        .getProperty(JsonbConfig.DATE_FORMAT)
        .map(String.class::cast)
        .ifPresent(
            dateFormat -> {
              final Optional<Locale> locale =
                  config.getProperty(JsonbConfig.LOCALE).map(Locale.class::cast);
              final DateTimeFormatter formatter =
                  locale.isPresent() ? ofPattern(dateFormat, locale.get()) : ofPattern(dateFormat);

              // Note: we try and fallback in the parsing cause we don't know if the date format
              // provided is
              // for date, datetime, time

              converters.put(
                  new AdapterKey(Date.class, String.class),
                  new ConverterAdapter<>(
                      new Converter<Date>() {
                        private volatile boolean useFormatter = true;

                        @Override
                        public String toString(final Date instance) {
                          return LocalDateTime.ofInstant(instance.toInstant(), zoneIDUTC)
                              .toString();
                        }

                        @Override
                        public Date fromString(final String text) {
                          if (useFormatter) {
                            try {
                              return Date.from(
                                  LocalDateTime.parse(text, formatter).toInstant(ZoneOffset.UTC));
                            } catch (final DateTimeParseException dpe) {
                              useFormatter = false;
                            }
                          }
                          return Date.from(LocalDateTime.parse(text).toInstant(ZoneOffset.UTC));
                        }
                      }));
              converters.put(
                  new AdapterKey(LocalDateTime.class, String.class),
                  new ConverterAdapter<>(
                      new Converter<LocalDateTime>() {
                        private volatile boolean useFormatter = true;

                        @Override
                        public String toString(final LocalDateTime instance) {
                          return instance.toString();
                        }

                        @Override
                        public LocalDateTime fromString(final String text) {
                          if (useFormatter) {
                            try {
                              return LocalDateTime.parse(text, formatter);
                            } catch (final DateTimeParseException dpe) {
                              useFormatter = false;
                            }
                          }
                          return LocalDateTime.parse(text);
                        }
                      }));
              converters.put(
                  new AdapterKey(LocalDate.class, String.class),
                  new ConverterAdapter<>(
                      new Converter<LocalDate>() {
                        private volatile boolean useFormatter = true;

                        @Override
                        public String toString(final LocalDate instance) {
                          return instance.toString();
                        }

                        @Override
                        public LocalDate fromString(final String text) {
                          if (useFormatter) {
                            try {
                              return LocalDate.parse(text, formatter);
                            } catch (final DateTimeParseException dpe) {
                              useFormatter = false;
                            }
                          }
                          return LocalDate.parse(text);
                        }
                      }));
              converters.put(
                  new AdapterKey(ZonedDateTime.class, String.class),
                  new ConverterAdapter<>(
                      new Converter<ZonedDateTime>() {
                        private volatile boolean useFormatter = true;

                        @Override
                        public String toString(final ZonedDateTime instance) {
                          return instance.toString();
                        }

                        @Override
                        public ZonedDateTime fromString(final String text) {
                          if (useFormatter) {
                            try {
                              return ZonedDateTime.parse(text, formatter);
                            } catch (final DateTimeParseException dpe) {
                              useFormatter = false;
                            }
                          }
                          return ZonedDateTime.parse(text);
                        }
                      }));
            });
  }
Example #4
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);
  }