Skip to content

Commit bbb7cbf

Browse files
authored
Merge pull request micronaut-projects#2511 from micronaut-projects/pr-2487
Modifications to allow further Jackson based media type support
2 parents f5668a8 + f51d89f commit bbb7cbf

File tree

11 files changed

+360
-180
lines changed

11 files changed

+360
-180
lines changed

aop/build.gradle

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ ext {
44
dependencies {
55
api project(':inject')
66
api project(':core')
7-
}
7+
}

benchmarks/build.gradle

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,4 @@ jmh {
2727
iterations = 3
2828
fork = 1
2929
// jvmArgs = ["-agentpath:/Applications/YourKit-Java-Profiler-2018.04.app/Contents/Resources/bin/mac/libyjpagent.jnilib"]
30-
}
30+
}

gradle/publishing.gradle

+1-1
Original file line numberDiff line numberDiff line change
@@ -279,4 +279,4 @@ if (!project.version.endsWith("-SNAPSHOT")) {
279279
// disable remote publish for non-snapshot versions
280280
// since releases are published to bintray
281281
publishMavenPublicationToMavenRepository.enabled = false
282-
}
282+
}

http-client/src/main/java/io/micronaut/http/client/interceptor/HttpClientIntroductionAdvice.java

+40-29
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
import io.micronaut.inject.qualifiers.Qualifiers;
6262
import io.micronaut.jackson.ObjectMapperFactory;
6363
import io.micronaut.jackson.annotation.JacksonFeatures;
64+
import io.micronaut.jackson.codec.JacksonMediaTypeCodec;
6465
import io.micronaut.jackson.codec.JsonMediaTypeCodec;
6566
import io.micronaut.runtime.ApplicationConfiguration;
6667
import io.reactivex.Completable;
@@ -225,7 +226,7 @@ public Object intercept(MethodInvocationContext<Object, Object> context) {
225226
configuration.getParameters()
226227
.forEach(parameter -> queryParams.put(parameter, version));
227228
});
228-
229+
229230
Map<String, Object> attributes = new LinkedHashMap<>(ATTRIBUTES_INITIAL_CAPACITY);
230231

231232
List<AnnotationValue<RequestAttribute>> attributeAnnotations = context.getAnnotationValuesByType(RequestAttribute.class);
@@ -646,11 +647,11 @@ private HttpClient getClient(MethodInvocationContext<Object, Object> context, An
646647

647648
return clients.computeIfAbsent(clientKey, integer -> {
648649
HttpClient clientBean = beanContext.findBean(HttpClient.class, Qualifiers.byName(NameUtils.hyphenate(clientId))).orElse(null);
649-
AnnotationValue<JacksonFeatures> jacksonFeatures = context.findAnnotation(JacksonFeatures.class).orElse(null);
650+
AnnotationValue<JacksonFeatures> jacksonFeaturesAnn = context.findAnnotation(JacksonFeatures.class).orElse(null);
650651
Optional<Class<?>> configurationClass = clientAnn.classValue("configuration");
651652

652653
if (null != clientBean) {
653-
if (path == null && jacksonFeatures == null && !configurationClass.isPresent()) {
654+
if (path == null && jacksonFeaturesAnn == null && !configurationClass.isPresent()) {
654655
return clientBean;
655656
}
656657
}
@@ -688,60 +689,70 @@ private HttpClient getClient(MethodInvocationContext<Object, Object> context, An
688689
DefaultHttpClient defaultClient = (DefaultHttpClient) client;
689690
defaultClient.setClientIdentifiers(clientId);
690691

691-
if (jacksonFeatures != null) {
692-
Optional<MediaTypeCodec> existingCodec = defaultClient.getMediaTypeCodecRegistry().findCodec(MediaType.APPLICATION_JSON_TYPE);
693-
ObjectMapper objectMapper = null;
694-
if (existingCodec.isPresent()) {
695-
MediaTypeCodec existing = existingCodec.get();
696-
if (existing instanceof JsonMediaTypeCodec) {
697-
objectMapper = ((JsonMediaTypeCodec) existing).getObjectMapper().copy();
698-
}
699-
}
700-
if (objectMapper == null) {
701-
objectMapper = new ObjectMapperFactory().objectMapper(null, null);
702-
}
692+
if (jacksonFeaturesAnn != null) {
693+
io.micronaut.jackson.codec.JacksonFeatures jacksonFeatures = new io.micronaut.jackson.codec.JacksonFeatures();
703694

704-
SerializationFeature[] enabledSerializationFeatures = jacksonFeatures.get("enabledSerializationFeatures", SerializationFeature[].class).orElse(null);
695+
696+
SerializationFeature[] enabledSerializationFeatures = jacksonFeaturesAnn.get("enabledSerializationFeatures", SerializationFeature[].class).orElse(null);
705697
if (enabledSerializationFeatures != null) {
706698
for (SerializationFeature serializationFeature : enabledSerializationFeatures) {
707-
objectMapper.configure(serializationFeature, true);
699+
jacksonFeatures.addFeature(serializationFeature, true);
708700
}
709701
}
710702

711-
DeserializationFeature[] enabledDeserializationFeatures = jacksonFeatures.get("enabledDeserializationFeatures", DeserializationFeature[].class).orElse(null);
703+
DeserializationFeature[] enabledDeserializationFeatures = jacksonFeaturesAnn.get("enabledDeserializationFeatures", DeserializationFeature[].class).orElse(null);
712704

713705
if (enabledDeserializationFeatures != null) {
714-
for (DeserializationFeature serializationFeature : enabledDeserializationFeatures) {
715-
objectMapper.configure(serializationFeature, true);
706+
for (DeserializationFeature deserializationFeature : enabledDeserializationFeatures) {
707+
jacksonFeatures.addFeature(deserializationFeature, true);
716708
}
717709
}
718710

719-
SerializationFeature[] disabledSerializationFeatures = jacksonFeatures.get("disabledSerializationFeatures", SerializationFeature[].class).orElse(null);
711+
SerializationFeature[] disabledSerializationFeatures = jacksonFeaturesAnn.get("disabledSerializationFeatures", SerializationFeature[].class).orElse(null);
720712
if (disabledSerializationFeatures != null) {
721713
for (SerializationFeature serializationFeature : disabledSerializationFeatures) {
722-
objectMapper.configure(serializationFeature, false);
714+
jacksonFeatures.addFeature(serializationFeature, false);
723715
}
724716
}
725717

726-
DeserializationFeature[] disabledDeserializationFeatures = jacksonFeatures.get("disabledDeserializationFeatures", DeserializationFeature[].class).orElse(null);
718+
DeserializationFeature[] disabledDeserializationFeatures = jacksonFeaturesAnn.get("disabledDeserializationFeatures", DeserializationFeature[].class).orElse(null);
727719

728720
if (disabledDeserializationFeatures != null) {
729721
for (DeserializationFeature feature : disabledDeserializationFeatures) {
730-
objectMapper.configure(feature, false);
722+
jacksonFeatures.addFeature(feature, false);
731723
}
732724
}
733725

734-
defaultClient.setMediaTypeCodecRegistry(
735-
MediaTypeCodecRegistry.of(
736-
new JsonMediaTypeCodec(objectMapper,
737-
beanContext.getBean(ApplicationConfiguration.class),
738-
beanContext.findBean(CodecConfiguration.class, Qualifiers.byName(JsonMediaTypeCodec.CONFIGURATION_QUALIFIER)).orElse(null))));
726+
List<MediaTypeCodec> codecs = new ArrayList<>(2);
727+
MediaTypeCodecRegistry codecRegistry = defaultClient.getMediaTypeCodecRegistry();
728+
for (MediaTypeCodec codec: codecRegistry.getCodecs()) {
729+
if (codec instanceof JacksonMediaTypeCodec) {
730+
codecs.add(((JacksonMediaTypeCodec) codec).cloneWithFeatures(jacksonFeatures));
731+
} else {
732+
codecs.add(codec);
733+
}
734+
}
735+
if (!codecRegistry.findCodec(MediaType.APPLICATION_JSON_TYPE).isPresent()) {
736+
codecs.add(createNewJsonCodec(beanContext, jacksonFeatures));
737+
}
738+
defaultClient.setMediaTypeCodecRegistry(MediaTypeCodecRegistry.of(codecs));
739739
}
740740
}
741741
return client;
742742
});
743743
}
744744

745+
private static MediaTypeCodec createNewJsonCodec(BeanContext beanContext, io.micronaut.jackson.codec.JacksonFeatures jacksonFeatures) {
746+
ObjectMapper objectMapper = new ObjectMapperFactory().objectMapper(null, null);
747+
748+
jacksonFeatures.getDeserializationFeatures().forEach(objectMapper::configure);
749+
jacksonFeatures.getSerializationFeatures().forEach(objectMapper::configure);
750+
751+
return new JsonMediaTypeCodec(objectMapper,
752+
beanContext.getBean(ApplicationConfiguration.class),
753+
beanContext.findBean(CodecConfiguration.class, Qualifiers.byName(JsonMediaTypeCodec.CONFIGURATION_QUALIFIER)).orElse(null));
754+
}
755+
745756
private String getClientId(AnnotationValue<Client> clientAnn) {
746757
String clientId = clientAnn.stringValue().orElse(null);
747758
if (clientId == null) {

http-server-netty/src/main/java/io/micronaut/http/server/netty/jackson/JsonViewMediaTypeCodecFactory.java

+2-3
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
import javax.annotation.Nullable;
3030
import javax.inject.Named;
3131
import javax.inject.Singleton;
32-
3332
import java.util.Map;
3433
import java.util.concurrent.ConcurrentHashMap;
3534

@@ -61,8 +60,8 @@ public class JsonViewMediaTypeCodecFactory implements JsonViewCodecResolver {
6160
* @param codecConfiguration The configuration for the codec
6261
*/
6362
protected JsonViewMediaTypeCodecFactory(ObjectMapper objectMapper,
64-
ApplicationConfiguration applicationConfiguration,
65-
@Named(CONFIGURATION_QUALIFIER) @Nullable CodecConfiguration codecConfiguration) {
63+
ApplicationConfiguration applicationConfiguration,
64+
@Named(CONFIGURATION_QUALIFIER) @Nullable CodecConfiguration codecConfiguration) {
6665
this.objectMapper = objectMapper;
6766
this.applicationConfiguration = applicationConfiguration;
6867
this.codecConfiguration = codecConfiguration;

runtime/src/main/java/io/micronaut/jackson/ObjectMapperFactory.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
import javax.annotation.Nullable;
2828
import javax.inject.Inject;
29+
import javax.inject.Named;
2930
import javax.inject.Singleton;
3031
import java.text.SimpleDateFormat;
3132
import java.util.Locale;
@@ -75,6 +76,7 @@ public class ObjectMapperFactory {
7576
*/
7677
@Singleton
7778
@Primary
79+
@Named("json")
7880
@BootstrapContextCompatible
7981
public ObjectMapper objectMapper(@Nullable JacksonConfiguration jacksonConfiguration,
8082
@Nullable JsonFactory jsonFactory) {
@@ -146,7 +148,7 @@ public ObjectMapper objectMapper(@Nullable JacksonConfiguration jacksonConfigura
146148

147149
ObjectMapper.DefaultTyping defaultTyping = jacksonConfiguration.getDefaultTyping();
148150
if (defaultTyping != null) {
149-
objectMapper.enableDefaultTyping(defaultTyping);
151+
objectMapper.activateDefaultTyping(objectMapper.getPolymorphicTypeValidator(), defaultTyping);
150152
}
151153

152154
JsonInclude.Include include = jacksonConfiguration.getSerializationInclusion();
@@ -169,7 +171,7 @@ public ObjectMapper objectMapper(@Nullable JacksonConfiguration jacksonConfigura
169171
if (propertyNamingStrategy != null) {
170172
objectMapper.setPropertyNamingStrategy(propertyNamingStrategy);
171173
}
172-
174+
173175
jacksonConfiguration.getSerializationSettings().forEach(objectMapper::configure);
174176
jacksonConfiguration.getDeserializationSettings().forEach(objectMapper::configure);
175177
jacksonConfiguration.getMapperSettings().forEach(objectMapper::configure);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* Copyright 2017-2019 original authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.micronaut.jackson.codec;
17+
18+
import java.util.HashMap;
19+
import java.util.Map;
20+
21+
import com.fasterxml.jackson.databind.DeserializationFeature;
22+
import com.fasterxml.jackson.databind.SerializationFeature;
23+
import io.micronaut.core.annotation.Internal;
24+
25+
/**
26+
* Stores features to later configure an {@link com.fasterxml.jackson.databind.ObjectMapper}.
27+
* Features are supplied through the {@link io.micronaut.jackson.annotation.JacksonFeatures} annotation.
28+
*
29+
* @author svishnyakov
30+
* @since 1.3.0
31+
*/
32+
@Internal
33+
public final class JacksonFeatures {
34+
35+
private final Map<SerializationFeature, Boolean> serializationFeatures;
36+
private final Map<DeserializationFeature, Boolean> deserializationFeatures;
37+
38+
/**
39+
* Empty jackson features.
40+
*/
41+
public JacksonFeatures() {
42+
this.serializationFeatures = new HashMap<>();
43+
this.deserializationFeatures = new HashMap<>();
44+
}
45+
46+
/**
47+
* Add a serialization feature.
48+
*
49+
* @param serializationFeature serialization feature to enable/disable
50+
* @param isEnabled whether you want to turn feature on/off
51+
* @return This object.
52+
*/
53+
public JacksonFeatures addFeature(SerializationFeature serializationFeature, boolean isEnabled) {
54+
serializationFeatures.put(serializationFeature, isEnabled);
55+
return this;
56+
}
57+
58+
/**
59+
* Add a deserialization feature.
60+
*
61+
* @param deserializationFeature deserialization feature to enable/disable
62+
* @param isEnabled whether you want to turn feature on/off
63+
* @return This object.
64+
*/
65+
public JacksonFeatures addFeature(DeserializationFeature deserializationFeature, boolean isEnabled) {
66+
deserializationFeatures.put(deserializationFeature, isEnabled);
67+
return this;
68+
}
69+
70+
/**
71+
* Serialization features.
72+
*
73+
* @return Serialization features or empty map if none available.
74+
*/
75+
public Map<SerializationFeature, Boolean> getSerializationFeatures() {
76+
return this.serializationFeatures;
77+
}
78+
79+
/**
80+
* Deserialization features.
81+
*
82+
* @return Deserialization features or empty map if none available.
83+
*/
84+
public Map<DeserializationFeature, Boolean> getDeserializationFeatures() {
85+
return this.deserializationFeatures;
86+
}
87+
}

0 commit comments

Comments
 (0)