blob: edcf545c75f04bcdfd7340dcb55bf3869801f1f2 [file] [log] [blame]
/*
* 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());
}
}