Skip to content

Commit c312e20

Browse files
authored
Allow to configure Jackson's JaxRSFeature on Jersey DefaultJacksonJaxbJsonProvider (#5816)
Signed-off-by: jansupol <jan.supol@oracle.com>
1 parent c59a1bc commit c312e20

File tree

6 files changed

+284
-5
lines changed

6 files changed

+284
-5
lines changed

media/json-jackson/src/main/java/org/glassfish/jersey/jackson/JacksonFeature.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2012, 2024 Oracle and/or its affiliates. All rights reserved.
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -29,8 +29,10 @@
2929
import org.glassfish.jersey.jackson.internal.DefaultJacksonJaxbJsonProvider;
3030
import org.glassfish.jersey.jackson.internal.FilteringJacksonJaxbJsonProvider;
3131
import org.glassfish.jersey.jackson.internal.JacksonFilteringFeature;
32+
import org.glassfish.jersey.jackson.internal.JaxrsFeatureBag;
3233
import org.glassfish.jersey.jackson.internal.jackson.jaxrs.base.JsonMappingExceptionMapper;
3334
import org.glassfish.jersey.jackson.internal.jackson.jaxrs.base.JsonParseExceptionMapper;
35+
import org.glassfish.jersey.jackson.internal.jackson.jaxrs.cfg.JaxRSFeature;
3436
import org.glassfish.jersey.jackson.internal.jackson.jaxrs.json.JacksonJaxbJsonProvider;
3537
import org.glassfish.jersey.message.MessageProperties;
3638
import org.glassfish.jersey.message.filtering.EntityFilteringFeature;
@@ -41,7 +43,7 @@
4143
* @author Stepan Kopriva
4244
* @author Michal Gajdos
4345
*/
44-
public class JacksonFeature implements Feature {
46+
public class JacksonFeature extends JaxrsFeatureBag<JacksonFeature> implements Feature {
4547

4648
/**
4749
* Define whether to use Jackson's exception mappers ore not
@@ -100,6 +102,16 @@ public JacksonFeature maxStringLength(int maxStringLength) {
100102
return this;
101103
}
102104

105+
/**
106+
* Register {@link JaxRSFeature} with the Jackson providers.
107+
* @param feature the {@link JaxRSFeature} to be enabled or disabled.
108+
* @param state {@code true} for enabling the feature, {@code false} for disabling.
109+
* @return JacksonFeature with {@link JaxRSFeature} registered to be set on a created Jackson provider.
110+
*/
111+
public JacksonFeature jaxrsFeature(JaxRSFeature feature, boolean state) {
112+
return super.jaxrsFeature(feature, state);
113+
}
114+
103115
private static final String JSON_FEATURE = JacksonFeature.class.getSimpleName();
104116

105117
@Override
@@ -138,6 +150,10 @@ public boolean configure(final FeatureContext context) {
138150
context.property(MessageProperties.JSON_MAX_STRING_LENGTH, maxStringLength);
139151
}
140152

153+
if (hasJaxrsFeature()) {
154+
context.property(JaxrsFeatureBag.JAXRS_FEATURE, this);
155+
}
156+
141157
return true;
142158
}
143159
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License v. 2.0, which is available at
6+
* http://www.eclipse.org/legal/epl-2.0.
7+
*
8+
* This Source Code may also be made available under the following Secondary
9+
* Licenses when the conditions for such availability set forth in the
10+
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
11+
* version 2 with the GNU Classpath Exception, which is available at
12+
* https://www.gnu.org/software/classpath/license.html.
13+
*
14+
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15+
*/
16+
17+
package org.glassfish.jersey.jackson;
18+
19+
import com.fasterxml.jackson.databind.ObjectMapper;
20+
import org.glassfish.jersey.jackson.internal.AbstractObjectMapper;
21+
import org.glassfish.jersey.jackson.internal.jackson.jaxrs.cfg.JaxRSFeature;
22+
23+
24+
/**
25+
* The Jackson {@link ObjectMapper} supporting {@link JaxRSFeature}s.
26+
*/
27+
public class JaxRSFeatureObjectMapper extends AbstractObjectMapper {
28+
29+
public JaxRSFeatureObjectMapper() {
30+
super();
31+
}
32+
33+
/**
34+
* Method for changing state of an on/off {@link org.glassfish.jersey.jackson.internal.jackson.jaxrs.cfg.JaxRSFeature}
35+
* features.
36+
*/
37+
public ObjectMapper configure(JaxRSFeature f, boolean state) {
38+
jaxrsFeatureBag.jaxrsFeature(f, state);
39+
return this;
40+
}
41+
42+
/**
43+
* Method for enabling specified {@link org.glassfish.jersey.jackson.internal.jackson.jaxrs.cfg.JaxRSFeature}s
44+
* for parser instances this object mapper creates.
45+
*/
46+
public ObjectMapper enable(JaxRSFeature... features) {
47+
if (features != null) {
48+
for (JaxRSFeature f : features) {
49+
jaxrsFeatureBag.jaxrsFeature(f, true);
50+
}
51+
}
52+
return this;
53+
}
54+
55+
/**
56+
* Method for disabling specified {@link org.glassfish.jersey.jackson.internal.jackson.jaxrs.cfg.JaxRSFeature}s
57+
* for parser instances this object mapper creates.
58+
*/
59+
public ObjectMapper disable(JaxRSFeature... features) {
60+
if (features != null) {
61+
for (JaxRSFeature f : features) {
62+
jaxrsFeatureBag.jaxrsFeature(f, false);
63+
}
64+
}
65+
return this;
66+
}
67+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License v. 2.0, which is available at
6+
* http://www.eclipse.org/legal/epl-2.0.
7+
*
8+
* This Source Code may also be made available under the following Secondary
9+
* Licenses when the conditions for such availability set forth in the
10+
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
11+
* version 2 with the GNU Classpath Exception, which is available at
12+
* https://www.gnu.org/software/classpath/license.html.
13+
*
14+
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15+
*/
16+
17+
package org.glassfish.jersey.jackson.internal;
18+
19+
import com.fasterxml.jackson.databind.ObjectMapper;
20+
21+
/**
22+
* Internal ObjectMapper with {@link JaxrsFeatureBag}.
23+
*/
24+
public abstract class AbstractObjectMapper extends ObjectMapper {
25+
protected AbstractObjectMapper() {
26+
27+
}
28+
protected JaxrsFeatureBag jaxrsFeatureBag = new JaxrsFeatureBag();
29+
}

media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/DefaultJacksonJaxbJsonProvider.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818

1919
import com.fasterxml.jackson.core.JsonFactory;
2020
import com.fasterxml.jackson.core.StreamReadConstraints;
21-
import com.fasterxml.jackson.core.Version;
2221
import com.fasterxml.jackson.core.json.PackageVersion;
2322
import com.fasterxml.jackson.databind.ObjectMapper;
2423
import com.fasterxml.jackson.databind.Module;
@@ -41,6 +40,7 @@
4140
import javax.inject.Singleton;
4241
import javax.ws.rs.core.Configuration;
4342
import javax.ws.rs.core.Context;
43+
import javax.ws.rs.core.MediaType;
4444
import javax.ws.rs.ext.Providers;
4545

4646
/**
@@ -53,8 +53,7 @@ public class DefaultJacksonJaxbJsonProvider extends JacksonJaxbJsonProvider {
5353

5454
@Inject
5555
public DefaultJacksonJaxbJsonProvider(@Context Providers providers, @Context Configuration config) {
56-
this.commonConfig = config;
57-
_providers = providers;
56+
this(providers, config, DEFAULT_ANNOTATIONS);
5857
}
5958

6059
//do not register JaxbAnnotationModule because it brakes default annotations processing
@@ -64,6 +63,20 @@ public DefaultJacksonJaxbJsonProvider(Providers providers, Configuration config,
6463
super(annotationsToUse);
6564
this.commonConfig = config;
6665
_providers = providers;
66+
67+
Object jaxrsFeatureBag = config.getProperty(JaxrsFeatureBag.JAXRS_FEATURE);
68+
if (jaxrsFeatureBag != null && (JaxrsFeatureBag.class.isInstance(jaxrsFeatureBag))) {
69+
((JaxrsFeatureBag) jaxrsFeatureBag).configureJaxrsFeatures(this);
70+
}
71+
}
72+
73+
@Override
74+
protected ObjectMapper _locateMapperViaProvider(Class<?> type, MediaType mediaType) {
75+
ObjectMapper mapper = super._locateMapperViaProvider(type, mediaType);
76+
if (AbstractObjectMapper.class.isInstance(mapper)) {
77+
((AbstractObjectMapper) mapper).jaxrsFeatureBag.configureJaxrsFeatures(this);
78+
}
79+
return mapper;
6780
}
6881

6982
@Override
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License v. 2.0, which is available at
6+
* http://www.eclipse.org/legal/epl-2.0.
7+
*
8+
* This Source Code may also be made available under the following Secondary
9+
* Licenses when the conditions for such availability set forth in the
10+
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
11+
* version 2 with the GNU Classpath Exception, which is available at
12+
* https://www.gnu.org/software/classpath/license.html.
13+
*
14+
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15+
*/
16+
17+
package org.glassfish.jersey.jackson.internal;
18+
19+
import org.glassfish.jersey.jackson.internal.jackson.jaxrs.base.ProviderBase;
20+
import org.glassfish.jersey.jackson.internal.jackson.jaxrs.cfg.JaxRSFeature;
21+
22+
import java.util.ArrayList;
23+
import java.util.List;
24+
import java.util.Optional;
25+
26+
/**
27+
* Internal holder class for {@link JaxRSFeature} settings and their values.
28+
*/
29+
public class JaxrsFeatureBag<T extends JaxrsFeatureBag> {
30+
protected static final String JAXRS_FEATURE = "jersey.config.jackson.jaxrs.feature";
31+
32+
private static class JaxRSFeatureState {
33+
/* package */ final JaxRSFeature feature;
34+
/* package */ final boolean state;
35+
public JaxRSFeatureState(JaxRSFeature feature, boolean state) {
36+
this.feature = feature;
37+
this.state = state;
38+
}
39+
}
40+
41+
private Optional<List<JaxRSFeatureState>> jaxRSFeature = Optional.empty();
42+
43+
public T jaxrsFeature(JaxRSFeature feature, boolean state) {
44+
if (!jaxRSFeature.isPresent()) {
45+
jaxRSFeature = Optional.of(new ArrayList<>());
46+
}
47+
jaxRSFeature.ifPresent(list -> list.add(new JaxrsFeatureBag.JaxRSFeatureState(feature, state)));
48+
return (T) this;
49+
}
50+
51+
protected boolean hasJaxrsFeature() {
52+
return jaxRSFeature.isPresent();
53+
}
54+
55+
/* package */ void configureJaxrsFeatures(ProviderBase providerBase) {
56+
jaxRSFeature.ifPresent(list -> list.stream().forEach(state -> providerBase.configure(state.feature, state.state)));
57+
}
58+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/*
2+
* Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License v. 2.0, which is available at
6+
* http://www.eclipse.org/legal/epl-2.0.
7+
*
8+
* This Source Code may also be made available under the following Secondary
9+
* Licenses when the conditions for such availability set forth in the
10+
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
11+
* version 2 with the GNU Classpath Exception, which is available at
12+
* https://www.gnu.org/software/classpath/license.html.
13+
*
14+
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15+
*/
16+
17+
package org.glassfish.jersey.jackson.internal;
18+
19+
import com.fasterxml.jackson.databind.ObjectMapper;
20+
import org.glassfish.jersey.jackson.JacksonFeature;
21+
import org.glassfish.jersey.jackson.JaxRSFeatureObjectMapper;
22+
import org.glassfish.jersey.jackson.internal.jackson.jaxrs.cfg.JaxRSFeature;
23+
import org.hamcrest.MatcherAssert;
24+
import org.hamcrest.Matchers;
25+
import org.junit.jupiter.api.Test;
26+
27+
import javax.inject.Inject;
28+
import javax.ws.rs.client.Client;
29+
import javax.ws.rs.client.ClientBuilder;
30+
import javax.ws.rs.client.ClientRequestContext;
31+
import javax.ws.rs.client.ClientRequestFilter;
32+
import javax.ws.rs.core.MediaType;
33+
import javax.ws.rs.core.Response;
34+
import javax.ws.rs.ext.ContextResolver;
35+
import javax.ws.rs.ext.Providers;
36+
import java.io.ByteArrayInputStream;
37+
import java.io.IOException;
38+
39+
public class JaxRSFeatureTest {
40+
@Test
41+
public void testJaxrsFeatureOnJacksonFeature() {
42+
Client client = ClientBuilder.newClient()
43+
.register(new JacksonFeature().jaxrsFeature(JaxRSFeature.READ_FULL_STREAM, false))
44+
.register(JaxrsFeatureFilter.class);
45+
46+
try (Response r = client.target("http://xxx.yyy").request().get()) {
47+
MatcherAssert.assertThat(r.getStatus(), Matchers.is(200));
48+
}
49+
}
50+
51+
@Test
52+
public void testJaxrsFeatureOnContextResolver() {
53+
Client client = ClientBuilder.newClient()
54+
.register(JacksonFeature.class)
55+
.register(JaxrsFetureContextResolver.class)
56+
.register(JaxrsFeatureFilter.class);
57+
58+
try (Response r = client.target("http://xxx.yyy").request().get()) {
59+
MatcherAssert.assertThat(r.getStatus(), Matchers.is(200));
60+
}
61+
}
62+
63+
64+
public static class JaxrsFeatureFilter implements ClientRequestFilter {
65+
private final DefaultJacksonJaxbJsonProvider jacksonProvider;
66+
@Inject
67+
public JaxrsFeatureFilter(Providers allProviders) {
68+
jacksonProvider = (DefaultJacksonJaxbJsonProvider)
69+
allProviders.getMessageBodyReader(Object.class, Object.class, null, MediaType.APPLICATION_JSON_TYPE);
70+
try {
71+
jacksonProvider.readFrom(Object.class, Object.class, null, MediaType.APPLICATION_JSON_TYPE, null,
72+
new ByteArrayInputStream("{}".getBytes()));
73+
} catch (IOException e) {
74+
throw new RuntimeException(e);
75+
}
76+
};
77+
78+
@Override
79+
public void filter(ClientRequestContext requestContext) throws IOException {
80+
Response.Status status = jacksonProvider.isEnabled(JaxRSFeature.READ_FULL_STREAM)
81+
? Response.Status.FORBIDDEN
82+
: Response.Status.OK;
83+
requestContext.abortWith(Response.status(status).build());
84+
}
85+
}
86+
87+
public static class JaxrsFetureContextResolver implements ContextResolver<ObjectMapper> {
88+
89+
@Override
90+
public ObjectMapper getContext(Class<?> type) {
91+
JaxRSFeatureObjectMapper objectMapper = new JaxRSFeatureObjectMapper();
92+
objectMapper.disable(JaxRSFeature.READ_FULL_STREAM);
93+
return objectMapper;
94+
}
95+
}
96+
}

0 commit comments

Comments
 (0)