| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.johnzon.jaxrs; |
| |
| import static java.util.Arrays.asList; |
| import static java.util.stream.Collectors.toMap; |
| import static java.util.stream.Collectors.toSet; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.lang.annotation.Annotation; |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Type; |
| import java.util.Comparator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.concurrent.atomic.AtomicReference; |
| import java.util.function.Function; |
| import java.util.stream.Stream; |
| |
| import javax.json.JsonReaderFactory; |
| import javax.json.stream.JsonGeneratorFactory; |
| import javax.ws.rs.Consumes; |
| import javax.ws.rs.Produces; |
| import javax.ws.rs.core.MediaType; |
| import javax.ws.rs.core.MultivaluedMap; |
| import javax.ws.rs.ext.MessageBodyReader; |
| import javax.ws.rs.ext.MessageBodyWriter; |
| |
| import org.apache.johnzon.mapper.MapperBuilder; |
| import org.apache.johnzon.mapper.SerializeValueFilter; |
| import org.apache.johnzon.mapper.access.AccessMode; |
| import org.apache.johnzon.mapper.access.BaseAccessMode; |
| |
| // @Provider // don't let it be scanned, it would conflict with JohnzonProvider |
| @Produces("application/json") |
| @Consumes("application/json") |
| public class ConfigurableJohnzonProvider<T> implements MessageBodyWriter<T>, MessageBodyReader<T> { |
| // build/configuration |
| private MapperBuilder builder = new MapperBuilder(); |
| private List<String> ignores; |
| |
| // runtime |
| private AtomicReference<JohnzonProvider<T>> delegate = new AtomicReference<JohnzonProvider<T>>(); |
| |
| private JohnzonProvider<T> instance() { |
| JohnzonProvider<T> instance; |
| do { |
| instance = delegate.get(); |
| if (builder != null && delegate.compareAndSet(null, new JohnzonProvider<T>(builder.build(), ignores))) { |
| // reset build instances |
| builder = null; |
| ignores = null; |
| } |
| } while (instance == null); |
| return instance; |
| } |
| |
| @Override |
| public boolean isReadable(final Class<?> rawType, final Type genericType, |
| final Annotation[] annotations, final MediaType mediaType) { |
| return instance().isReadable(rawType, genericType, annotations, mediaType); |
| } |
| |
| @Override |
| public T readFrom(final Class<T> rawType, final Type genericType, |
| final Annotation[] annotations, final MediaType mediaType, |
| final MultivaluedMap<String, String> httpHeaders, |
| final InputStream entityStream) throws IOException { |
| return instance().readFrom(rawType, genericType, annotations, mediaType, httpHeaders, entityStream); |
| } |
| |
| @Override |
| public long getSize(final T t, final Class<?> rawType, final Type genericType, |
| final Annotation[] annotations, final MediaType mediaType) { |
| return instance().getSize(t, rawType, genericType, annotations, mediaType); |
| } |
| |
| @Override |
| public boolean isWriteable(final Class<?> rawType, final Type genericType, |
| final Annotation[] annotations, final MediaType mediaType) { |
| return instance().isWriteable(rawType, genericType, annotations, mediaType); |
| } |
| |
| @Override |
| public void writeTo(final T t, final Class<?> rawType, final Type genericType, |
| final Annotation[] annotations, final MediaType mediaType, |
| final MultivaluedMap<String, Object> httpHeaders, |
| final OutputStream entityStream) throws IOException { |
| instance().writeTo(t, rawType, genericType, annotations, mediaType, httpHeaders, entityStream); |
| } |
| |
| public void setUseJsRange(final boolean value) { |
| builder.setUseJsRange(value); |
| } |
| |
| public void setUseBigDecimalForObjectNumbers(final boolean value) { |
| builder.setUseBigDecimalForObjectNumbers(value); |
| } |
| |
| // type=a,b,c|type2=d,e |
| public void setIgnoreFieldsForType(final String mapping) { |
| for (final String config : mapping.split(" *| *")) { |
| final String[] parts = config.split(" *= *"); |
| try { |
| final Class<?> type = Thread.currentThread().getContextClassLoader().loadClass(parts[0]); |
| if (parts.length == 1) { |
| builder.setIgnoreFieldsForType(type); |
| } else { |
| builder.setIgnoreFieldsForType(type, parts[1].split(" *, *")); |
| } |
| } catch (final ClassNotFoundException e) { |
| throw new IllegalArgumentException(e); |
| } |
| } |
| } |
| |
| public void setFailOnUnknownProperties(final boolean active) { |
| builder.setFailOnUnknownProperties(active); |
| } |
| |
| public void setPolymorphicSerializationPredicate(final String classes) { |
| final Set<Class<?>> set = asSet(classes); |
| builder.setPolymorphicSerializationPredicate(set::contains); |
| } |
| |
| public void setPolymorphicDeserializationPredicate(final String classes) { |
| final Set<Class<?>> set = asSet(classes); |
| builder.setPolymorphicDeserializationPredicate(set::contains); |
| } |
| |
| public void setPolymorphicDiscriminatorMapper(final Map<String, String> discriminatorMapper) { |
| final ClassLoader loader = Thread.currentThread().getContextClassLoader(); |
| final Map<Class<?>, String> map = discriminatorMapper.entrySet().stream() |
| .collect(toMap(e -> { |
| try { |
| return loader.loadClass(e.getKey().trim()); |
| } catch (final ClassNotFoundException ex) { |
| throw new IllegalArgumentException(ex); |
| } |
| }, Map.Entry::getValue)); |
| builder.setPolymorphicDiscriminatorMapper(map::get); |
| } |
| |
| public void setPolymorphicTypeLoader(final Map<String, String> aliasTypeMapping) { |
| final ClassLoader loader = Thread.currentThread().getContextClassLoader(); |
| final Map<String, Class<?>> map = aliasTypeMapping.entrySet().stream() |
| .collect(toMap(Map.Entry::getKey, e -> { |
| try { |
| return loader.loadClass(e.getValue().trim()); |
| } catch (final ClassNotFoundException ex) { |
| throw new IllegalArgumentException(ex); |
| } |
| })); |
| builder.setPolymorphicTypeLoader(map::get); |
| } |
| |
| public void setPolymorphicDiscriminator(final String value) { |
| builder.setPolymorphicDiscriminator(value); |
| } |
| |
| public void setSupportConstructors(final boolean supportConstructors) { |
| builder.setSupportConstructors(supportConstructors); |
| } |
| |
| public void setPretty(final boolean pretty) { |
| builder.setPretty(pretty); |
| } |
| |
| public void setSupportGetterForCollections(final boolean supportGetterForCollections) { |
| builder.setSupportGetterForCollections(supportGetterForCollections); |
| } |
| |
| public void setSupportsComments(final boolean supportsComments) { |
| builder.setSupportsComments(supportsComments); |
| } |
| |
| public void setIgnores(final String ignores) { |
| this.ignores = ignores == null ? null : asList(ignores.split(" *, *")); |
| } |
| |
| public void setAccessMode(final AccessMode mode) { |
| builder.setAccessMode(mode); |
| } |
| |
| public void setAccessModeName(final String mode) { |
| builder.setAccessModeName(mode); |
| } |
| |
| public void setAccessModeFieldFilteringStrategy(final BaseAccessMode.FieldFilteringStrategy strategy) { |
| builder.setAccessModeFieldFilteringStrategy(strategy); |
| } |
| |
| public void setInterfaceImplementationMapping(final Map<String, String> interfaceImplementationMapping) { |
| final ClassLoader loader = Thread.currentThread().getContextClassLoader(); |
| final Function<String, Class<?>> load = name -> { |
| try { |
| return loader.loadClass(name.trim()); |
| } catch (final ClassNotFoundException e) { |
| throw new IllegalArgumentException(e); |
| } |
| }; |
| builder.setInterfaceImplementationMapping(interfaceImplementationMapping.entrySet().stream() |
| .collect(toMap(it -> load.apply(it.getKey()), it -> load.apply(it.getValue())))); |
| } |
| |
| public void setAccessModeFieldFilteringStrategyName(final String mode) { |
| builder.setAccessModeFieldFilteringStrategyName(mode); |
| } |
| |
| public void setSupportHiddenAccess(final boolean supportHiddenAccess) { |
| builder.setSupportHiddenAccess(supportHiddenAccess); |
| } |
| |
| public void setAttributeOrder(final Comparator<String> attributeOrder) { |
| builder.setAttributeOrder(attributeOrder); |
| } |
| |
| public void setReaderFactory(final JsonReaderFactory readerFactory) { |
| builder.setReaderFactory(readerFactory); |
| } |
| |
| public void setGeneratorFactory(final JsonGeneratorFactory generatorFactory) { |
| builder.setGeneratorFactory(generatorFactory); |
| } |
| |
| public void setDoCloseOnStreams(final boolean doCloseOnStreams) { |
| builder.setDoCloseOnStreams(doCloseOnStreams); |
| } |
| |
| public void setVersion(final int version) { |
| builder.setVersion(version); |
| } |
| |
| public void setSkipNull(final boolean skipNull) { |
| builder.setSkipNull(skipNull); |
| } |
| |
| public void setSkipEmptyArray(final boolean skipEmptyArray) { |
| builder.setSkipEmptyArray(skipEmptyArray); |
| } |
| |
| public void setBufferSize(final int bufferSize) { |
| builder.setBufferSize(bufferSize); |
| } |
| |
| public void setBufferStrategy(final String bufferStrategy) { |
| builder.setBufferStrategy(bufferStrategy); |
| } |
| |
| public void setMaxSize(final int size) { |
| builder.setMaxSize(size); |
| } |
| |
| public void setTreatByteArrayAsBase64(final boolean treatByteArrayAsBase64) { |
| builder.setTreatByteArrayAsBase64(treatByteArrayAsBase64); |
| } |
| |
| public void setEncoding(final String encoding) { |
| builder.setEncoding(encoding); |
| } |
| |
| public void setReadAttributeBeforeWrite(final boolean rabw) { |
| builder.setReadAttributeBeforeWrite(rabw); |
| } |
| |
| public void setEnforceQuoteString(final boolean val) { |
| builder.setEnforceQuoteString(val); |
| } |
| |
| public void setPrimitiveConverters(final boolean val) { |
| builder.setPrimitiveConverters(val); |
| } |
| |
| public MapperBuilder setDeduplicateObjects(boolean deduplicateObjects) { |
| return builder.setDeduplicateObjects(deduplicateObjects); |
| } |
| |
| public void setSerializeValueFilter(final String val) { |
| try { |
| builder.setSerializeValueFilter(SerializeValueFilter.class.cast( |
| Thread.currentThread().getContextClassLoader().loadClass(val).getConstructor().newInstance())); |
| } catch (final InstantiationException | IllegalAccessException | NoSuchMethodException | ClassNotFoundException e) { |
| throw new IllegalArgumentException(e); |
| } catch (InvocationTargetException e) { |
| throw new IllegalArgumentException(e.getCause()); |
| } |
| } |
| |
| public void setUseBigDecimalForFloats(final boolean useBigDecimalForFloats) { |
| builder.setUseBigDecimalForFloats(useBigDecimalForFloats); |
| } |
| |
| public void setAutoAdjustStringBuffers(final boolean autoAdjustStringBuffers) { |
| builder.setAutoAdjustStringBuffers(autoAdjustStringBuffers); |
| } |
| |
| private Set<Class<?>> asSet(final String classes) { |
| final ClassLoader loader = Thread.currentThread().getContextClassLoader(); |
| return Stream.of(classes.split(" *, *")) |
| .map(n -> { |
| try { |
| return loader.loadClass(n.trim()); |
| } catch (final ClassNotFoundException ex) { |
| throw new IllegalArgumentException(ex); |
| } |
| }).collect(toSet()); |
| } |
| } |