From c7b3881f02afdb9aae9a73ef87cf5d12958fd696 Mon Sep 17 00:00:00 2001 From: Jan Supol Date: Sat, 9 Feb 2019 01:08:41 +0100 Subject: [PATCH 1/3] Enable @ConstrainedTo on Features Signed-off-by: Jan Supol --- .../jersey/model/internal/CommonConfig.java | 38 +++- .../inject/WebTargetValueParamProvider.java | 4 +- .../e2e/common/FeatureConstraintTest.java | 192 ++++++++++++++++++ 3 files changed, 223 insertions(+), 11 deletions(-) create mode 100644 tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/FeatureConstraintTest.java diff --git a/core-common/src/main/java/org/glassfish/jersey/model/internal/CommonConfig.java b/core-common/src/main/java/org/glassfish/jersey/model/internal/CommonConfig.java index 9dedb807c0..ae0e7d363f 100644 --- a/core-common/src/main/java/org/glassfish/jersey/model/internal/CommonConfig.java +++ b/core-common/src/main/java/org/glassfish/jersey/model/internal/CommonConfig.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2019 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 @@ -106,15 +106,20 @@ private static final class FeatureRegistration { private final Class featureClass; private final Feature feature; + private final RuntimeType runtimeType; private FeatureRegistration(final Class featureClass) { this.featureClass = featureClass; this.feature = null; + final ConstrainedTo runtimeTypeConstraint = featureClass.getAnnotation(ConstrainedTo.class); + this.runtimeType = runtimeTypeConstraint == null ? null : runtimeTypeConstraint.value(); } private FeatureRegistration(final Feature feature) { this.featureClass = feature.getClass(); this.feature = feature; + final ConstrainedTo runtimeTypeConstraint = featureClass.getAnnotation(ConstrainedTo.class); + this.runtimeType = runtimeTypeConstraint == null ? null : runtimeTypeConstraint.value(); } /** @@ -122,7 +127,7 @@ private FeatureRegistration(final Feature feature) { * * @return registered feature class. */ - Class getFeatureClass() { + private Class getFeatureClass() { return featureClass; } @@ -133,10 +138,21 @@ Class getFeatureClass() { * @return the registered feature instance or {@code null} if this is a * class based feature registration. */ - public Feature getFeature() { + private Feature getFeature() { return feature; } + /** + * Get the {@code RuntimeType} constraint given by {@code ConstrainedTo} annotated + * the Feature or {@code null} if not annotated. + * + * @return the {@code RuntimeType} constraint given by {@code ConstrainedTo} annotated + * the Feature or {@code null} if not annotated. + */ + private RuntimeType getFeatureRuntimeType() { + return runtimeType; + } + @Override public boolean equals(final Object obj) { if (this == obj) { @@ -674,13 +690,17 @@ private void configureFeatures(InjectionManager injectionManager, // init lazily featureContextWrapper = new FeatureContextWrapper(this, injectionManager); } - final boolean success = feature.configure(featureContextWrapper); - if (success) { - processed.add(registration); - configureFeatures(injectionManager, processed, resetRegistrations(), managedObjectsFinalizer); - enabledFeatureClasses.add(registration.getFeatureClass()); - enabledFeatures.add(feature); + final RuntimeType runtimeTypeConstraint = registration.getFeatureRuntimeType(); + if (runtimeTypeConstraint == null || type.equals(runtimeTypeConstraint)) { + final boolean success = feature.configure(featureContextWrapper); + + if (success) { + processed.add(registration); + configureFeatures(injectionManager, processed, resetRegistrations(), managedObjectsFinalizer); + enabledFeatureClasses.add(registration.getFeatureClass()); + enabledFeatures.add(feature); + } } } } diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/WebTargetValueParamProvider.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/WebTargetValueParamProvider.java index 69aba9bc1c..472408c704 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/WebTargetValueParamProvider.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/WebTargetValueParamProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2019 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 @@ -287,7 +287,7 @@ private void copyProviders(Configuration source, Configurable target) { for (Object o : source.getInstances()) { Class c = o.getClass(); if (!targetConfig.isRegistered(o)) { - target.register(c, source.getContracts(c)); + target.register(o, source.getContracts(c)); } } } diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/FeatureConstraintTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/FeatureConstraintTest.java new file mode 100644 index 0000000000..d3f23ec1f5 --- /dev/null +++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/FeatureConstraintTest.java @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2019 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.tests.e2e.common; + +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.server.Uri; +import org.glassfish.jersey.test.JerseyTest; +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import javax.ws.rs.ConstrainedTo; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.RuntimeType; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.Application; +import javax.ws.rs.core.Configuration; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.Feature; +import javax.ws.rs.core.FeatureContext; +import java.util.concurrent.atomic.AtomicInteger; + +public class FeatureConstraintTest extends JerseyTest { + + public static AtomicInteger clientEnvironmentHitCount = new AtomicInteger(0); + public static AtomicInteger serverEnvironmentHitCount = new AtomicInteger(0); + public static AtomicInteger clientServerEnvironmentHitCount = new AtomicInteger(0); + + @Override + protected Application configure() { + return new ResourceConfig(PropagatedConfigResource.class, ServerConstrainedClassFeature.class, + ClientConstrainedClassFeature.class, ClientServerConstrainedClassFeature.class) + .register(new ServerConstrainedInstanceFeature()) + .register(new ClientConstrainedInstanceFeature()) + .register(new ClientServerConstrainedInstanceFeature()); + } + + @ConstrainedTo(RuntimeType.SERVER) + public static class ServerConstrainedClassFeature implements Feature { + protected int increment = 10; + @Override + public boolean configure(FeatureContext context) { + if (context.getConfiguration().getRuntimeType().equals(RuntimeType.CLIENT)) { + clientEnvironmentHitCount.addAndGet(increment); + } + return true; + } + } + + @ConstrainedTo(RuntimeType.SERVER) + public static class ServerConstrainedInstanceFeature extends ServerConstrainedClassFeature { + { + increment = 100; + } + }; + + @ConstrainedTo(RuntimeType.CLIENT) + public static class ClientConstrainedClassFeature implements Feature { + protected int increment = 10; + @Override + public boolean configure(FeatureContext context) { + if (context.getConfiguration().getRuntimeType().equals(RuntimeType.SERVER)) { + serverEnvironmentHitCount.addAndGet(increment); + } + return true; + } + } + + @ConstrainedTo(RuntimeType.CLIENT) + public static class ClientConstrainedInstanceFeature extends ClientConstrainedClassFeature { + { + increment = 100; + } + }; + + public static class ClientServerConstrainedClassFeature implements Feature { + protected int increment = 10; + @Override + public boolean configure(FeatureContext context) { + if (context.getConfiguration().getRuntimeType().equals(RuntimeType.SERVER)) { + clientServerEnvironmentHitCount.addAndGet(increment); + } + if (context.getConfiguration().getRuntimeType().equals(RuntimeType.CLIENT)) { + clientServerEnvironmentHitCount.addAndGet(100 * increment); + } + return true; + } + } + + public static class ClientServerConstrainedInstanceFeature extends ClientServerConstrainedClassFeature { + { + increment = 100; + } + }; + + @Path("/") + public static class PropagatedConfigResource { + @Uri("/isRegistered") + WebTarget target; + + @Path("isRegistered") + @GET + public boolean isRegisteredOnServer(@Context Configuration config) { + return config.isRegistered(ServerConstrainedClassFeature.class) + && config.isRegistered(ServerConstrainedInstanceFeature.class) + && config.isRegistered(ClientConstrainedClassFeature.class) + && config.isRegistered(ClientConstrainedInstanceFeature.class) + && config.isRegistered(ClientServerConstrainedClassFeature.class) + && config.isRegistered(ClientServerConstrainedInstanceFeature.class); + } + + @Path("isInherited") + @GET + public boolean isInheritedInInjectedClientConfig() { + final Configuration config = target.getConfiguration(); + return isRegisteredOnServer(config); + } + + @Path("featureConfigurationNotInvoked") + @GET + public boolean featureConfigurationNotInvoked() { + return target + .register(ServerConstrainedClassFeature.class) + .register(new ServerConstrainedInstanceFeature()) + .register(ClientConstrainedClassFeature.class) + .register(new ClientConstrainedInstanceFeature()) + .register(ClientServerConstrainedClassFeature.class) + .register(new ClientServerConstrainedInstanceFeature()) + .request().get().readEntity(boolean.class); + } + } + + @Test + public void test() { + assertThat("*Constrained*Feature must be registered in a server configuration", + target("isRegistered") + .register(ServerConstrainedClassFeature.class) + .register(new ServerConstrainedInstanceFeature()) + .register(ClientConstrainedClassFeature.class) + .register(new ClientConstrainedInstanceFeature()) + .register(ClientServerConstrainedClassFeature.class) + .register(new ClientServerConstrainedInstanceFeature()) + .request().get().readEntity(boolean.class), + is(true)); + + assertThat("Server Features should not have been configured the Client", clientEnvironmentHitCount.get(), is(0)); + assertThat("Client Features should not have been configured the Server", serverEnvironmentHitCount.get(), is(0)); + assertThat("ClientSever Features should have been configured", clientServerEnvironmentHitCount.get(), is(11110)); + clientServerEnvironmentHitCount.set(0); //reset configuration invoked on a server, it won't happen again + + assertThat("*Constrained*Feature must be in an application classes set", + target("isInherited").request().get().readEntity(boolean.class), + is(true)); + + assertThat("Server Features should not have been configured the Client", clientEnvironmentHitCount.get(), is(0)); + assertThat("Client Features should not have been configured the Server", serverEnvironmentHitCount.get(), is(0)); + assertThat("ClientSever Features should not have been configured", clientServerEnvironmentHitCount.get(), is(0)); + + assertThat("ServerConstrainedFeature must be in an application classes set", + target("featureConfigurationNotInvoked") + .register(ServerConstrainedClassFeature.class) + .register(new ServerConstrainedInstanceFeature()) + .register(ClientConstrainedClassFeature.class) + .register(new ClientConstrainedInstanceFeature()) + .register(ClientServerConstrainedClassFeature.class) + .register(new ClientServerConstrainedInstanceFeature()) + .request().get().readEntity(boolean.class), + is(true)); + + assertThat("Server Features should not have been configured the Client", clientEnvironmentHitCount.get(), is(0)); + assertThat("Client Features should not have been configured the Server", serverEnvironmentHitCount.get(), is(0)); + assertThat("ClientSever Features should have been configured", clientServerEnvironmentHitCount.get(), is(22000)); + } + + +} From ed7beb73b80d4c926c24b32aaecd224247a37096 Mon Sep 17 00:00:00 2001 From: Jan Supol Date: Mon, 18 Feb 2019 15:40:27 +0100 Subject: [PATCH 2/3] Apply @ConstrainedTo even before Feature class is instantiated Signed-off-by: Jan Supol --- .../jersey/model/internal/CommonConfig.java | 23 +++++++++++-------- .../jersey/internal/localization.properties | 1 + .../e2e/common/FeatureConstraintTest.java | 12 +++++----- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/core-common/src/main/java/org/glassfish/jersey/model/internal/CommonConfig.java b/core-common/src/main/java/org/glassfish/jersey/model/internal/CommonConfig.java index ae0e7d363f..a979b37a96 100644 --- a/core-common/src/main/java/org/glassfish/jersey/model/internal/CommonConfig.java +++ b/core-common/src/main/java/org/glassfish/jersey/model/internal/CommonConfig.java @@ -669,6 +669,13 @@ private void configureFeatures(InjectionManager injectionManager, continue; } + final RuntimeType runtimeTypeConstraint = registration.getFeatureRuntimeType(); + if (runtimeTypeConstraint != null && !type.equals(runtimeTypeConstraint)) { + LOGGER.config(LocalizationMessages.FEATURE_CONSTRAINED_TO_IGNORED( + registration.getFeatureClass(), registration.runtimeType, type)); + continue; + } + Feature feature = registration.getFeature(); if (feature == null) { feature = injectionManager.createAndInitialize(registration.getFeatureClass()); @@ -690,17 +697,13 @@ private void configureFeatures(InjectionManager injectionManager, // init lazily featureContextWrapper = new FeatureContextWrapper(this, injectionManager); } + final boolean success = feature.configure(featureContextWrapper); - final RuntimeType runtimeTypeConstraint = registration.getFeatureRuntimeType(); - if (runtimeTypeConstraint == null || type.equals(runtimeTypeConstraint)) { - final boolean success = feature.configure(featureContextWrapper); - - if (success) { - processed.add(registration); - configureFeatures(injectionManager, processed, resetRegistrations(), managedObjectsFinalizer); - enabledFeatureClasses.add(registration.getFeatureClass()); - enabledFeatures.add(feature); - } + if (success) { + processed.add(registration); + configureFeatures(injectionManager, processed, resetRegistrations(), managedObjectsFinalizer); + enabledFeatureClasses.add(registration.getFeatureClass()); + enabledFeatures.add(feature); } } } diff --git a/core-common/src/main/resources/org/glassfish/jersey/internal/localization.properties b/core-common/src/main/resources/org/glassfish/jersey/internal/localization.properties index 791997da3f..9fa7e82ef7 100644 --- a/core-common/src/main/resources/org/glassfish/jersey/internal/localization.properties +++ b/core-common/src/main/resources/org/glassfish/jersey/internal/localization.properties @@ -70,6 +70,7 @@ errors.and.warnings.detected=Following issues have been detected: {0} exception.caught.while.loading.spi.providers=Exception caught while loading SPI providers. exception.mapper.supported.type.unknown=Unable to retrieve the supported exception type for a registered exception mapper service class "{0}". feature.has.already.been.processed=Feature [{0}] has already been processed. +feature.constrainedTo.ignored=Feature {0} registered in {2} runtime is constrained to {1} runtime and is ignored. hint.msg=HINT: {0} hints.detected=The following hints have been detected: {0} http.header.comments.not.allowed=Comments are not allowed. diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/FeatureConstraintTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/FeatureConstraintTest.java index d3f23ec1f5..76a47dd5d8 100644 --- a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/FeatureConstraintTest.java +++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/FeatureConstraintTest.java @@ -159,8 +159,8 @@ public void test() { .request().get().readEntity(boolean.class), is(true)); - assertThat("Server Features should not have been configured the Client", clientEnvironmentHitCount.get(), is(0)); - assertThat("Client Features should not have been configured the Server", serverEnvironmentHitCount.get(), is(0)); + assertThat("Server Features should not have been configured on Client", clientEnvironmentHitCount.get(), is(0)); + assertThat("Client Features should not have been configured on Server", serverEnvironmentHitCount.get(), is(0)); assertThat("ClientSever Features should have been configured", clientServerEnvironmentHitCount.get(), is(11110)); clientServerEnvironmentHitCount.set(0); //reset configuration invoked on a server, it won't happen again @@ -168,8 +168,8 @@ public void test() { target("isInherited").request().get().readEntity(boolean.class), is(true)); - assertThat("Server Features should not have been configured the Client", clientEnvironmentHitCount.get(), is(0)); - assertThat("Client Features should not have been configured the Server", serverEnvironmentHitCount.get(), is(0)); + assertThat("Server Features should not have been configured on Client", clientEnvironmentHitCount.get(), is(0)); + assertThat("Client Features should not have been configured on Server", serverEnvironmentHitCount.get(), is(0)); assertThat("ClientSever Features should not have been configured", clientServerEnvironmentHitCount.get(), is(0)); assertThat("ServerConstrainedFeature must be in an application classes set", @@ -183,8 +183,8 @@ public void test() { .request().get().readEntity(boolean.class), is(true)); - assertThat("Server Features should not have been configured the Client", clientEnvironmentHitCount.get(), is(0)); - assertThat("Client Features should not have been configured the Server", serverEnvironmentHitCount.get(), is(0)); + assertThat("Server Features should not have been configured on Client", clientEnvironmentHitCount.get(), is(0)); + assertThat("Client Features should not have been configured on Server", serverEnvironmentHitCount.get(), is(0)); assertThat("ClientSever Features should have been configured", clientServerEnvironmentHitCount.get(), is(22000)); } From b000b0d46d4195bf937c0348b68951a9853b8ecb Mon Sep 17 00:00:00 2001 From: Jan Supol Date: Mon, 18 Feb 2019 15:45:58 +0100 Subject: [PATCH 3/3] Updated copyright year Signed-off-by: Jan Supol --- .../org/glassfish/jersey/internal/localization.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core-common/src/main/resources/org/glassfish/jersey/internal/localization.properties b/core-common/src/main/resources/org/glassfish/jersey/internal/localization.properties index 9fa7e82ef7..110b7d3ca8 100644 --- a/core-common/src/main/resources/org/glassfish/jersey/internal/localization.properties +++ b/core-common/src/main/resources/org/glassfish/jersey/internal/localization.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2012, 2019 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