Skip to content

Commit

Permalink
Allow to configure Jackson's JaxRSFeature on Jersey DefaultJacksonJax…
Browse files Browse the repository at this point in the history
…bJsonProvider (#5816)

Signed-off-by: jansupol <jan.supol@oracle.com>
  • Loading branch information
jansupol authored Dec 17, 2024
1 parent c59a1bc commit c312e20
Show file tree
Hide file tree
Showing 6 changed files with 284 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
Expand Down Expand Up @@ -29,8 +29,10 @@
import org.glassfish.jersey.jackson.internal.DefaultJacksonJaxbJsonProvider;
import org.glassfish.jersey.jackson.internal.FilteringJacksonJaxbJsonProvider;
import org.glassfish.jersey.jackson.internal.JacksonFilteringFeature;
import org.glassfish.jersey.jackson.internal.JaxrsFeatureBag;
import org.glassfish.jersey.jackson.internal.jackson.jaxrs.base.JsonMappingExceptionMapper;
import org.glassfish.jersey.jackson.internal.jackson.jaxrs.base.JsonParseExceptionMapper;
import org.glassfish.jersey.jackson.internal.jackson.jaxrs.cfg.JaxRSFeature;
import org.glassfish.jersey.jackson.internal.jackson.jaxrs.json.JacksonJaxbJsonProvider;
import org.glassfish.jersey.message.MessageProperties;
import org.glassfish.jersey.message.filtering.EntityFilteringFeature;
Expand All @@ -41,7 +43,7 @@
* @author Stepan Kopriva
* @author Michal Gajdos
*/
public class JacksonFeature implements Feature {
public class JacksonFeature extends JaxrsFeatureBag<JacksonFeature> implements Feature {

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

/**
* Register {@link JaxRSFeature} with the Jackson providers.
* @param feature the {@link JaxRSFeature} to be enabled or disabled.
* @param state {@code true} for enabling the feature, {@code false} for disabling.
* @return JacksonFeature with {@link JaxRSFeature} registered to be set on a created Jackson provider.
*/
public JacksonFeature jaxrsFeature(JaxRSFeature feature, boolean state) {
return super.jaxrsFeature(feature, state);
}

private static final String JSON_FEATURE = JacksonFeature.class.getSimpleName();

@Override
Expand Down Expand Up @@ -138,6 +150,10 @@ public boolean configure(final FeatureContext context) {
context.property(MessageProperties.JSON_MAX_STRING_LENGTH, maxStringLength);
}

if (hasJaxrsFeature()) {
context.property(JaxrsFeatureBag.JAXRS_FEATURE, this);
}

return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/

package org.glassfish.jersey.jackson;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.glassfish.jersey.jackson.internal.AbstractObjectMapper;
import org.glassfish.jersey.jackson.internal.jackson.jaxrs.cfg.JaxRSFeature;


/**
* The Jackson {@link ObjectMapper} supporting {@link JaxRSFeature}s.
*/
public class JaxRSFeatureObjectMapper extends AbstractObjectMapper {

public JaxRSFeatureObjectMapper() {
super();
}

/**
* Method for changing state of an on/off {@link org.glassfish.jersey.jackson.internal.jackson.jaxrs.cfg.JaxRSFeature}
* features.
*/
public ObjectMapper configure(JaxRSFeature f, boolean state) {
jaxrsFeatureBag.jaxrsFeature(f, state);
return this;
}

/**
* Method for enabling specified {@link org.glassfish.jersey.jackson.internal.jackson.jaxrs.cfg.JaxRSFeature}s
* for parser instances this object mapper creates.
*/
public ObjectMapper enable(JaxRSFeature... features) {
if (features != null) {
for (JaxRSFeature f : features) {
jaxrsFeatureBag.jaxrsFeature(f, true);
}
}
return this;
}

/**
* Method for disabling specified {@link org.glassfish.jersey.jackson.internal.jackson.jaxrs.cfg.JaxRSFeature}s
* for parser instances this object mapper creates.
*/
public ObjectMapper disable(JaxRSFeature... features) {
if (features != null) {
for (JaxRSFeature f : features) {
jaxrsFeatureBag.jaxrsFeature(f, false);
}
}
return this;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/

package org.glassfish.jersey.jackson.internal;

import com.fasterxml.jackson.databind.ObjectMapper;

/**
* Internal ObjectMapper with {@link JaxrsFeatureBag}.
*/
public abstract class AbstractObjectMapper extends ObjectMapper {
protected AbstractObjectMapper() {

}
protected JaxrsFeatureBag jaxrsFeatureBag = new JaxrsFeatureBag();
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.StreamReadConstraints;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.core.json.PackageVersion;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.Module;
Expand All @@ -41,6 +40,7 @@
import javax.inject.Singleton;
import javax.ws.rs.core.Configuration;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.ext.Providers;

/**
Expand All @@ -53,8 +53,7 @@ public class DefaultJacksonJaxbJsonProvider extends JacksonJaxbJsonProvider {

@Inject
public DefaultJacksonJaxbJsonProvider(@Context Providers providers, @Context Configuration config) {
this.commonConfig = config;
_providers = providers;
this(providers, config, DEFAULT_ANNOTATIONS);
}

//do not register JaxbAnnotationModule because it brakes default annotations processing
Expand All @@ -64,6 +63,20 @@ public DefaultJacksonJaxbJsonProvider(Providers providers, Configuration config,
super(annotationsToUse);
this.commonConfig = config;
_providers = providers;

Object jaxrsFeatureBag = config.getProperty(JaxrsFeatureBag.JAXRS_FEATURE);
if (jaxrsFeatureBag != null && (JaxrsFeatureBag.class.isInstance(jaxrsFeatureBag))) {
((JaxrsFeatureBag) jaxrsFeatureBag).configureJaxrsFeatures(this);
}
}

@Override
protected ObjectMapper _locateMapperViaProvider(Class<?> type, MediaType mediaType) {
ObjectMapper mapper = super._locateMapperViaProvider(type, mediaType);
if (AbstractObjectMapper.class.isInstance(mapper)) {
((AbstractObjectMapper) mapper).jaxrsFeatureBag.configureJaxrsFeatures(this);
}
return mapper;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/

package org.glassfish.jersey.jackson.internal;

import org.glassfish.jersey.jackson.internal.jackson.jaxrs.base.ProviderBase;
import org.glassfish.jersey.jackson.internal.jackson.jaxrs.cfg.JaxRSFeature;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

/**
* Internal holder class for {@link JaxRSFeature} settings and their values.
*/
public class JaxrsFeatureBag<T extends JaxrsFeatureBag> {
protected static final String JAXRS_FEATURE = "jersey.config.jackson.jaxrs.feature";

private static class JaxRSFeatureState {
/* package */ final JaxRSFeature feature;
/* package */ final boolean state;
public JaxRSFeatureState(JaxRSFeature feature, boolean state) {
this.feature = feature;
this.state = state;
}
}

private Optional<List<JaxRSFeatureState>> jaxRSFeature = Optional.empty();

public T jaxrsFeature(JaxRSFeature feature, boolean state) {
if (!jaxRSFeature.isPresent()) {
jaxRSFeature = Optional.of(new ArrayList<>());
}
jaxRSFeature.ifPresent(list -> list.add(new JaxrsFeatureBag.JaxRSFeatureState(feature, state)));
return (T) this;
}

protected boolean hasJaxrsFeature() {
return jaxRSFeature.isPresent();
}

/* package */ void configureJaxrsFeatures(ProviderBase providerBase) {
jaxRSFeature.ifPresent(list -> list.stream().forEach(state -> providerBase.configure(state.feature, state.state)));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/

package org.glassfish.jersey.jackson.internal;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.glassfish.jersey.jackson.JacksonFeature;
import org.glassfish.jersey.jackson.JaxRSFeatureObjectMapper;
import org.glassfish.jersey.jackson.internal.jackson.jaxrs.cfg.JaxRSFeature;
import org.hamcrest.MatcherAssert;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.Test;

import javax.inject.Inject;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.ClientRequestContext;
import javax.ws.rs.client.ClientRequestFilter;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Providers;
import java.io.ByteArrayInputStream;
import java.io.IOException;

public class JaxRSFeatureTest {
@Test
public void testJaxrsFeatureOnJacksonFeature() {
Client client = ClientBuilder.newClient()
.register(new JacksonFeature().jaxrsFeature(JaxRSFeature.READ_FULL_STREAM, false))
.register(JaxrsFeatureFilter.class);

try (Response r = client.target("http://xxx.yyy").request().get()) {
MatcherAssert.assertThat(r.getStatus(), Matchers.is(200));
}
}

@Test
public void testJaxrsFeatureOnContextResolver() {
Client client = ClientBuilder.newClient()
.register(JacksonFeature.class)
.register(JaxrsFetureContextResolver.class)
.register(JaxrsFeatureFilter.class);

try (Response r = client.target("http://xxx.yyy").request().get()) {
MatcherAssert.assertThat(r.getStatus(), Matchers.is(200));
}
}


public static class JaxrsFeatureFilter implements ClientRequestFilter {
private final DefaultJacksonJaxbJsonProvider jacksonProvider;
@Inject
public JaxrsFeatureFilter(Providers allProviders) {
jacksonProvider = (DefaultJacksonJaxbJsonProvider)
allProviders.getMessageBodyReader(Object.class, Object.class, null, MediaType.APPLICATION_JSON_TYPE);
try {
jacksonProvider.readFrom(Object.class, Object.class, null, MediaType.APPLICATION_JSON_TYPE, null,
new ByteArrayInputStream("{}".getBytes()));
} catch (IOException e) {
throw new RuntimeException(e);
}
};

@Override
public void filter(ClientRequestContext requestContext) throws IOException {
Response.Status status = jacksonProvider.isEnabled(JaxRSFeature.READ_FULL_STREAM)
? Response.Status.FORBIDDEN
: Response.Status.OK;
requestContext.abortWith(Response.status(status).build());
}
}

public static class JaxrsFetureContextResolver implements ContextResolver<ObjectMapper> {

@Override
public ObjectMapper getContext(Class<?> type) {
JaxRSFeatureObjectMapper objectMapper = new JaxRSFeatureObjectMapper();
objectMapper.disable(JaxRSFeature.READ_FULL_STREAM);
return objectMapper;
}
}
}

0 comments on commit c312e20

Please sign in to comment.