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; }
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(); } }
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); } })); }); }
@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); }