Merge 'upstream/2.x' into 'upstream/3.0'
Signed-off-by: Maxim Nesen <maxim.nesen@oracle.com>
diff --git a/core-client/src/main/java/org/glassfish/jersey/client/ClientProperties.java b/core-client/src/main/java/org/glassfish/jersey/client/ClientProperties.java
index ae46be8..267664d 100644
--- a/core-client/src/main/java/org/glassfish/jersey/client/ClientProperties.java
+++ b/core-client/src/main/java/org/glassfish/jersey/client/ClientProperties.java
@@ -241,10 +241,33 @@
public static final String OUTBOUND_CONTENT_LENGTH_BUFFER = CommonProperties.OUTBOUND_CONTENT_LENGTH_BUFFER_CLIENT;
/**
+ * If {@code true} then disable configuration of Json Binding (JSR-367)
+ * feature on client.
+ * <p>
+ * By default, Json Binding on client is automatically enabled if global
+ * property
+ * {@value org.glassfish.jersey.CommonProperties#JSON_BINDING_FEATURE_DISABLE}
+ * is not disabled. If set then the client property value overrides the
+ * global property value.
+ * <p>
+ * The default value is {@code false}.
+ * </p>
+ * <p>
+ * The name of the configuration property is <tt>{@value}</tt>.
+ * </p>
+ * <p>This constant is an alias for {@link CommonProperties#JSON_BINDING_FEATURE_DISABLE_CLIENT}.</p>
+ *
+ * @see org.glassfish.jersey.CommonProperties#JSON_BINDING_FEATURE_DISABLE
+ * @since 2.45
+ */
+ @PropertyAlias
+ public static final String JSON_BINDING_FEATURE_DISABLE = CommonProperties.JSON_BINDING_FEATURE_DISABLE_CLIENT;
+
+ /**
* If {@code true} then disable configuration of Json Processing (JSR-353)
* feature on client.
* <p>
- * By default Json Processing on client is automatically enabled if global
+ * By default, Json Processing on client is automatically enabled if global
* property
* {@value org.glassfish.jersey.CommonProperties#JSON_PROCESSING_FEATURE_DISABLE}
* is not disabled. If set then the client property value overrides the
@@ -265,7 +288,7 @@
/**
* If {@code true} then disable META-INF/services lookup on client.
* <p>
- * By default Jersey looks up SPI implementations described by {@code META-INF/services/*} files.
+ * By default, Jersey looks up SPI implementations described by {@code META-INF/services/*} files.
* Then you can register appropriate provider classes by {@link jakarta.ws.rs.core.Application}.
* </p>
* <p>
diff --git a/core-common/src/main/java/org/glassfish/jersey/ApplicationSupplier.java b/core-common/src/main/java/org/glassfish/jersey/ApplicationSupplier.java
new file mode 100644
index 0000000..6ef17b9
--- /dev/null
+++ b/core-common/src/main/java/org/glassfish/jersey/ApplicationSupplier.java
@@ -0,0 +1,33 @@
+/*
+ * 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;
+
+import jakarta.ws.rs.core.Application;
+
+/**
+ * Implementation of this interface is capable of returning {@link Application}.
+ */
+public interface ApplicationSupplier {
+ /**
+ * Get Application.
+ *
+ * @return Application.
+ */
+ Application getApplication();
+
+}
diff --git a/core-common/src/main/java/org/glassfish/jersey/CommonProperties.java b/core-common/src/main/java/org/glassfish/jersey/CommonProperties.java
index d7f5985..d3a9d47 100644
--- a/core-common/src/main/java/org/glassfish/jersey/CommonProperties.java
+++ b/core-common/src/main/java/org/glassfish/jersey/CommonProperties.java
@@ -71,7 +71,7 @@
/**
* If {@code true} then disable feature auto discovery globally on client/server.
* <p>
- * By default auto discovery is automatically enabled. The value of this property may be overridden by the client/server
+ * By default, auto discovery is automatically enabled. The value of this property may be overridden by the client/server
* variant of this property.
* <p>
* The default value is {@code false}.
@@ -98,10 +98,55 @@
*/
public static final String FEATURE_AUTO_DISCOVERY_DISABLE_SERVER = "jersey.config.server.disableAutoDiscovery";
+
+ /**
+ * If {@code true} then disable configuration of Json Binding (JSR-367) feature.
+ * <p>
+ * By default, Json Binding is automatically enabled. The value of this property may be overridden by the client/server
+ * variant of this property.
+ * <p>
+ * The default value is {@code false}.
+ * </p>
+ * <p>
+ * The name of the configuration property is <tt>{@value}</tt>.
+ * </p>
+ * @since 2.45
+ */
+ public static final String JSON_BINDING_FEATURE_DISABLE = "jersey.config.disableJsonBinding";
+
+ /**
+ * Client-specific version of {@link CommonProperties#JSON_BINDING_FEATURE_DISABLE}.
+ *
+ * If present, it overrides the generic one for the client environment.
+ * @since 2.45
+ */
+ public static final String JSON_BINDING_FEATURE_DISABLE_CLIENT = "jersey.config.client.disableJsonBinding";
+
+ /**
+ * Server-specific version of {@link CommonProperties#JSON_BINDING_FEATURE_DISABLE}.
+ *
+ * If present, it overrides the generic one for the server environment.
+ * @since 2.45
+ */
+ public static final String JSON_BINDING_FEATURE_DISABLE_SERVER = "jersey.config.server.disableJsonBinding";
+
+ /**
+ * Disables configuration of Json Binding (JSR-367) feature for {@link jakarta.ws.rs.core.Application} subclasses whose
+ * package names are specified as a value. The value is comma-separated string defining prefixes of the application
+ * package names.
+ * <p>
+ * By default, Json Binding is automatically enabled.
+ * <p>
+ * The name of the configuration property is <tt>{@value}</tt>.
+ * </p>
+ * @since 2.45
+ */
+ public static final String JSON_BINDING_FEATURE_DISABLE_APPLICATION = "jersey.config.application.disableJsonBinding";
+
/**
* If {@code true} then disable configuration of Json Processing (JSR-353) feature.
* <p>
- * By default Json Processing is automatically enabled. The value of this property may be overridden by the client/server
+ * By default, Json Processing is automatically enabled. The value of this property may be overridden by the client/server
* variant of this property.
* <p>
* The default value is {@code false}.
@@ -131,7 +176,7 @@
/**
* If {@code true} then disable META-INF/services lookup globally on client/server.
* <p>
- * By default Jersey looks up SPI implementations described by META-INF/services/* files.
+ * By default, Jersey looks up SPI implementations described by META-INF/services/* files.
* Then you can register appropriate provider classes by {@link jakarta.ws.rs.core.Application}.
* </p>
* <p>
@@ -164,7 +209,7 @@
/**
* If {@code true} then disable configuration of MOXy Json feature.
* <p>
- * By default MOXy Json is automatically enabled. The value of this property may be overridden by the client/server
+ * By default, MOXy Json is automatically enabled. The value of this property may be overridden by the client/server
* variant of this property.
* <p>
* The default value is {@code false}.
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/util/PropertiesHelper.java b/core-common/src/main/java/org/glassfish/jersey/internal/util/PropertiesHelper.java
index 5500e54..97dee37 100644
--- a/core-common/src/main/java/org/glassfish/jersey/internal/util/PropertiesHelper.java
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/util/PropertiesHelper.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2022 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
@@ -390,6 +390,27 @@
}
/**
+ * Converts the property value to {@code boolean} and checks it is {@code true} or empty.
+ * Returns {@code true} if the value is {@code true} or empty but not {@code null}.
+ *
+ * <p>
+ * The rationale behind this is that system property {@code -Dprop=true} is the same as {@code -Dprop}.
+ * The property {@code -Dprop=false} behaves as if the {@code -Dprop} is not set at all.
+ * </p>
+ *
+ * @param value property value.
+ * @return {@code boolean} property value or {@code true} if the property value is not set or {@code false} if the property
+ * is otherwise not convertible.
+ */
+ public static boolean isPropertyOrNotSet(final Object value) {
+ if (value instanceof Boolean) {
+ return Boolean.class.cast(value);
+ } else {
+ return value != null && ("".equals(value.toString()) || Boolean.parseBoolean(value.toString()));
+ }
+ }
+
+ /**
* Faster replacement of {@code RuntimeType#name().toLowerCase(Locale.ROOT)}
* @param runtimeType The runtime type to lower case
* @return the lower-cased variant of the {@link RuntimeType}.
diff --git a/core-common/src/test/java/org/glassfish/jersey/internal/util/PropertiesHelperTest.java b/core-common/src/test/java/org/glassfish/jersey/internal/util/PropertiesHelperTest.java
index 67df369..237e749 100644
--- a/core-common/src/test/java/org/glassfish/jersey/internal/util/PropertiesHelperTest.java
+++ b/core-common/src/test/java/org/glassfish/jersey/internal/util/PropertiesHelperTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 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
@@ -199,4 +199,15 @@
}
+ @Test
+ public void isPropertyOrNotSetTest() {
+ assertEquals(false, PropertiesHelper.isPropertyOrNotSet((Boolean) null));
+ assertEquals(true, PropertiesHelper.isPropertyOrNotSet(Boolean.TRUE));
+ assertEquals(false, PropertiesHelper.isPropertyOrNotSet(Boolean.FALSE));
+ assertEquals(false, PropertiesHelper.isPropertyOrNotSet((String) null));
+ assertEquals(true, PropertiesHelper.isPropertyOrNotSet(""));
+ assertEquals(false, PropertiesHelper.isPropertyOrNotSet("treu")); // false for non-boolean values
+ assertEquals(true, PropertiesHelper.isPropertyOrNotSet("TRUE"));
+ assertEquals(false, PropertiesHelper.isPropertyOrNotSet("false"));
+ }
}
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/ResourceConfig.java b/core-server/src/main/java/org/glassfish/jersey/server/ResourceConfig.java
index 291b76f..5575335 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/ResourceConfig.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/ResourceConfig.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2021 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
@@ -39,6 +39,7 @@
import jakarta.ws.rs.core.Configuration;
import jakarta.ws.rs.core.Feature;
+import org.glassfish.jersey.ApplicationSupplier;
import org.glassfish.jersey.internal.Errors;
import org.glassfish.jersey.internal.config.ExternalPropertiesConfigurationFactory;
import org.glassfish.jersey.internal.inject.Binder;
@@ -70,7 +71,7 @@
* @author Michal Gajdos
* @author Marek Potociar
*/
-public class ResourceConfig extends Application implements Configurable<ResourceConfig>, ServerConfig {
+public class ResourceConfig extends Application implements Configurable<ResourceConfig>, ServerConfig, ApplicationSupplier {
private static final Logger LOGGER = Logger.getLogger(ResourceConfig.class.getName());
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/ServerProperties.java b/core-server/src/main/java/org/glassfish/jersey/server/ServerProperties.java
index 4c06c5c..6414204 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/ServerProperties.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/ServerProperties.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2021 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
@@ -302,7 +302,7 @@
/**
* If {@code true} then disable auto discovery on server.
*
- * By default auto discovery is automatically enabled if global property
+ * By default, auto discovery is automatically enabled if global property
* {@value org.glassfish.jersey.CommonProperties#FEATURE_AUTO_DISCOVERY_DISABLE} is not disabled. If set then the server
* property value overrides the global property value.
* <p>
@@ -343,9 +343,28 @@
public static final String OUTBOUND_CONTENT_LENGTH_BUFFER = CommonProperties.OUTBOUND_CONTENT_LENGTH_BUFFER_SERVER;
/**
+ * If {@code true} then disable configuration of Json Binding (JSR-367) feature on server.
+ *
+ * By default, Json Binding is automatically enabled if global property
+ * {@value org.glassfish.jersey.CommonProperties#JSON_BINDING_FEATURE_DISABLE} is not disabled. If set then the server
+ * property value overrides the global property value.
+ * <p>
+ * The default value is {@code false}.
+ * </p>
+ * <p>
+ * The name of the configuration property is <tt>{@value}</tt>.
+ * </p>
+ * <p>This constant is an alias for {@link CommonProperties#JSON_BINDING_FEATURE_DISABLE_SERVER}</p>
+ *
+ * @see org.glassfish.jersey.CommonProperties#JSON_BINDING_FEATURE_DISABLE
+ */
+ @PropertyAlias
+ public static final String JSON_BINDING_FEATURE_DISABLE = CommonProperties.JSON_BINDING_FEATURE_DISABLE_SERVER;
+
+ /**
* If {@code true} then disable configuration of Json Processing (JSR-353) feature on server.
*
- * By default Json Processing is automatically enabled if global property
+ * By default, Json Processing is automatically enabled if global property
* {@value org.glassfish.jersey.CommonProperties#JSON_PROCESSING_FEATURE_DISABLE} is not disabled. If set then the server
* property value overrides the global property value.
* <p>
@@ -364,7 +383,7 @@
/**
* If {@code true} then disable META-INF/services lookup on server.
*
- * By default Jersey looks up SPI implementations described by META-INF/services/* files.
+ * By default, Jersey looks up SPI implementations described by META-INF/services/* files.
* Then you can register appropriate provider classes by {@link jakarta.ws.rs.core.Application}.
* <p>
* The default value is {@code false}.
@@ -383,7 +402,7 @@
/**
* If {@code true} then disable configuration of MOXy Json feature on server.
*
- * By default MOXy Json is automatically enabled if global property
+ * By default, MOXy Json is automatically enabled if global property
* {@value org.glassfish.jersey.CommonProperties#MOXY_JSON_FEATURE_DISABLE} is not disabled. If set then the server
* property value overrides the global property value.
* <p>
diff --git a/docs/src/main/docbook/appendix-properties.xml b/docs/src/main/docbook/appendix-properties.xml
index b1a04c9..c53de04 100644
--- a/docs/src/main/docbook/appendix-properties.xml
+++ b/docs/src/main/docbook/appendix-properties.xml
@@ -72,6 +72,40 @@
</entry>
</row>
<row>
+ <entry>&jersey.common.CommonProperties.JSON_BINDING_FEATURE_DISABLE; /
+ &jersey.common.CommonProperties.JSON_BINDING_FEATURE_DISABLE_CLIENT; /
+ &jersey.common.CommonProperties.JSON_BINDING_FEATURE_DISABLE_SERVER;</entry>
+ <entry><literal>jersey.config.disableJsonBinding</literal> /
+ <literal>jersey.config.client.disableJsonBinding</literal> /
+ <literal>jersey.config.server.disableJsonBinding</literal></entry>
+ <entry>
+ <para>
+ Disables configuration of Json Binding (JSR-367) feature. Default value is <literal>false</literal>.
+ Can also be set as a system property.
+ </para>
+ <para>
+ Since 2.45.
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>&jersey.common.CommonProperties.JSON_BINDING_FEATURE_DISABLE_APPLICATION; </entry>
+ <entry><literal>jersey.config.application.disableJsonBinding</literal></entry>
+ <entry>
+ <para>
+ Disables configuration of Json Binding (JSR-367) feature for <literal>jakarta.ws.rs.core.Application</literal>
+ subclasses whose package names are specified as a value. The value is comma-separated string
+ defining prefixes of the application package names. Can also be set as a system property.
+ </para>
+ <para>
+ By default, Json Binding is automatically enabled.
+ </para>
+ <para>
+ Since 2.45.
+ </para>
+ </entry>
+ </row>
+ <row>
<entry>&jersey.common.CommonProperties.JSON_PROCESSING_FEATURE_DISABLE; /
&jersey.common.CommonProperties.JSON_PROCESSING_FEATURE_DISABLE_CLIENT; /
&jersey.common.CommonProperties.JSON_PROCESSING_FEATURE_DISABLE_SERVER;</entry>
@@ -416,6 +450,15 @@
</entry>
</row>
<row>
+ <entry>&jersey.server.ServerProperties.JSON_BINDING_FEATURE_DISABLE;</entry>
+ <entry><literal>jersey.config.server.disableJsonBinding</literal></entry>
+ <entry>
+ <para>
+ Disables configuration of Json Processing (JSR-353) feature. Default value is <literal>false</literal>.
+ </para>
+ </entry>
+ </row>
+ <row>
<entry>&jersey.server.ServerProperties.JSON_PROCESSING_FEATURE_DISABLE;</entry>
<entry><literal>jersey.config.server.disableJsonProcessing</literal></entry>
<entry>
@@ -1034,6 +1077,15 @@
</entry>
</row>
<row>
+ <entry>&jersey.client.ClientProperties.JSON_BINDING_FEATURE_DISABLE;</entry>
+ <entry><literal>jersey.config.client.disableJsonBinding</literal></entry>
+ <entry>
+ <para>
+ Disables configuration of Json Binding (JSR-367) feature. Default value is <literal>false</literal>.
+ </para>
+ </entry>
+ </row>
+ <row>
<entry>&jersey.client.ClientProperties.JSON_PROCESSING_FEATURE_DISABLE;</entry>
<entry><literal>jersey.config.client.disableJsonProcessing</literal></entry>
<entry>
diff --git a/docs/src/main/docbook/jersey.ent b/docs/src/main/docbook/jersey.ent
index 4981502..cc94282 100644
--- a/docs/src/main/docbook/jersey.ent
+++ b/docs/src/main/docbook/jersey.ent
@@ -346,6 +346,7 @@
<!ENTITY jersey.client.ClientProperties.DEFAULT_CHUNK_SIZE "<link xlink:href='&jersey.javadoc.uri.prefix;/client/ClientProperties.html#DEFAULT_CHUNK_SIZE'>ClientProperties.DEFAULT_CHUNK_SIZE</link>" >
<!ENTITY jersey.client.ClientProperties.FEATURE_AUTO_DISCOVERY_DISABLE "<link xlink:href='&jersey.javadoc.uri.prefix;/client/ClientProperties.html#FEATURE_AUTO_DISCOVERY_DISABLE'>ClientProperties.FEATURE_AUTO_DISCOVERY_DISABLE</link>" >
<!ENTITY jersey.client.ClientProperties.FOLLOW_REDIRECTS "<link xlink:href='&jersey.javadoc.uri.prefix;/client/ClientProperties.html#FOLLOW_REDIRECTS'>ClientProperties.FOLLOW_REDIRECTS</link>" >
+<!ENTITY jersey.client.ClientProperties.JSON_BINDING_FEATURE_DISABLE "<link xlink:href='&jersey.javadoc.uri.prefix;/client/ClientProperties.html#JSON_BINDING_FEATURE_DISABLE'>ClientProperties.JSON_BINDING_FEATURE_DISABLE</link>" >
<!ENTITY jersey.client.ClientProperties.JSON_PROCESSING_FEATURE_DISABLE "<link xlink:href='&jersey.javadoc.uri.prefix;/client/ClientProperties.html#JSON_PROCESSING_FEATURE_DISABLE'>ClientProperties.JSON_PROCESSING_FEATURE_DISABLE</link>" >
<!ENTITY jersey.client.ClientProperties.METAINF_SERVICES_LOOKUP_DISABLE "<link xlink:href='&jersey.javadoc.uri.prefix;/client/ClientProperties.html#METAINF_SERVICES_LOOKUP_DISABLE'>ClientProperties.METAINF_SERVICES_LOOKUP_DISABLE</link>" >
<!ENTITY jersey.client.ClientProperties.MOXY_JSON_FEATURE_DISABLE "<link xlink:href='&jersey.javadoc.uri.prefix;/client/ClientProperties.html#MOXY_JSON_FEATURE_DISABLE'>ClientProperties.MOXY_JSON_FEATURE_DISABLE</link>" >
@@ -390,6 +391,10 @@
<!ENTITY jersey.common.CommonProperties.FEATURE_AUTO_DISCOVERY_DISABLE "<link xlink:href='&jersey.javadoc.uri.prefix;/CommonProperties.html#FEATURE_AUTO_DISCOVERY_DISABLE'>CommonProperties.FEATURE_AUTO_DISCOVERY_DISABLE</link>" >
<!ENTITY jersey.common.CommonProperties.FEATURE_AUTO_DISCOVERY_DISABLE_CLIENT "<link xlink:href='&jersey.javadoc.uri.prefix;/CommonProperties.html#FEATURE_AUTO_DISCOVERY_DISABLE_CLIENT'>CommonProperties.FEATURE_AUTO_DISCOVERY_DISABLE_CLIENT</link>" >
<!ENTITY jersey.common.CommonProperties.FEATURE_AUTO_DISCOVERY_DISABLE_SERVER "<link xlink:href='&jersey.javadoc.uri.prefix;/CommonProperties.html#FEATURE_AUTO_DISCOVERY_DISABLE_SERVER'>CommonProperties.FEATURE_AUTO_DISCOVERY_DISABLE_SERVER</link>" >
+<!ENTITY jersey.common.CommonProperties.JSON_BINDING_FEATURE_DISABLE "<link xlink:href='&jersey.javadoc.uri.prefix;/CommonProperties.html#JSON_BINDING_FEATURE_DISABLE'>CommonProperties.JSON_BINDING_FEATURE_DISABLE</link>" >
+<!ENTITY jersey.common.CommonProperties.JSON_BINDING_FEATURE_DISABLE_CLIENT "<link xlink:href='&jersey.javadoc.uri.prefix;/CommonProperties.html#JSON_BINDING_FEATURE_DISABLE_CLIENT'>CommonProperties.JSON_BINDING_FEATURE_DISABLE_CLIENT</link>" >
+<!ENTITY jersey.common.CommonProperties.JSON_BINDING_FEATURE_DISABLE_SERVER "<link xlink:href='&jersey.javadoc.uri.prefix;/CommonProperties.html#JSON_BINDING_FEATURE_DISABLE_SERVER'>CommonProperties.JSON_BINDING_FEATURE_DISABLE_SERVER</link>" >
+<!ENTITY jersey.common.CommonProperties.JSON_BINDING_FEATURE_DISABLE_APPLICATION "<link xlink:href='&jersey.javadoc.uri.prefix;/CommonProperties.html#JSON_BINDING_FEATURE_DISABLE_APPLICATION'>CommonProperties.JSON_BINDING_FEATURE_DISABLE_APPLICATION</link>" >
<!ENTITY jersey.common.CommonProperties.JSON_PROCESSING_FEATURE_DISABLE "<link xlink:href='&jersey.javadoc.uri.prefix;/CommonProperties.html#JSON_PROCESSING_FEATURE_DISABLE'>CommonProperties.JSON_PROCESSING_FEATURE_DISABLE</link>" >
<!ENTITY jersey.common.CommonProperties.JSON_PROCESSING_FEATURE_DISABLE_CLIENT "<link xlink:href='&jersey.javadoc.uri.prefix;/CommonProperties.html#JSON_PROCESSING_FEATURE_DISABLE_CLIENT'>CommonProperties.JSON_PROCESSING_FEATURE_DISABLE_CLIENT</link>" >
<!ENTITY jersey.common.CommonProperties.JSON_PROCESSING_FEATURE_DISABLE_SERVER "<link xlink:href='&jersey.javadoc.uri.prefix;/CommonProperties.html#JSON_PROCESSING_FEATURE_DISABLE_SERVER'>CommonProperties.JSON_PROCESSING_FEATURE_DISABLE_SERVER</link>" >
@@ -605,6 +610,7 @@
<!ENTITY jersey.server.ServerProperties.BV_SEND_ERROR_IN_RESPONSE "<link xlink:href='&jersey.javadoc.uri.prefix;/server/ServerProperties.html#BV_SEND_ERROR_IN_RESPONSE'>ServerProperties.BV_SEND_ERROR_IN_RESPONSE</link>" >
<!ENTITY jersey.server.ServerProperties.FEATURE_AUTO_DISCOVERY_DISABLE "<link xlink:href='&jersey.javadoc.uri.prefix;/server/ServerProperties.html#FEATURE_AUTO_DISCOVERY_DISABLE'>ServerProperties.FEATURE_AUTO_DISCOVERY_DISABLE</link>" >
<!ENTITY jersey.server.ServerProperties.HTTP_METHOD_OVERRIDE "<link xlink:href='&jersey.javadoc.uri.prefix;/server/ServerProperties.html#HTTP_METHOD_OVERRIDE'>ServerProperties.HTTP_METHOD_OVERRIDE</link>" >
+<!ENTITY jersey.server.ServerProperties.JSON_BINDING_FEATURE_DISABLE "<link xlink:href='&jersey.javadoc.uri.prefix;/server/ServerProperties.html#JSON_BINDING_FEATURE_DISABLE'>ServerProperties.JSON_BINDING_FEATURE_DISABLE</link>" >
<!ENTITY jersey.server.ServerProperties.JSON_PROCESSING_FEATURE_DISABLE "<link xlink:href='&jersey.javadoc.uri.prefix;/server/ServerProperties.html#JSON_PROCESSING_FEATURE_DISABLE'>ServerProperties.JSON_PROCESSING_FEATURE_DISABLE</link>" >
<!ENTITY jersey.server.ServerProperties.LANGUAGE_MAPPINGS "<link xlink:href='&jersey.javadoc.uri.prefix;/server/ServerProperties.html#LANGUAGE_MAPPINGS'>ServerProperties.LANGUAGE_MAPPINGS</link>" >
<!ENTITY jersey.server.ServerProperties.MEDIA_TYPE_MAPPINGS "<link xlink:href='&jersey.javadoc.uri.prefix;/server/ServerProperties.html#MEDIA_TYPE_MAPPINGS'>ServerProperties.MEDIA_TYPE_MAPPINGS</link>" >
diff --git a/examples/extended-wadl-webapp/README.MD b/examples/extended-wadl-webapp/README.MD
index d7a59d1..6c4f676 100644
--- a/examples/extended-wadl-webapp/README.MD
+++ b/examples/extended-wadl-webapp/README.MD
@@ -15,9 +15,6 @@
Contents
--------
-The description of what's done here you'll find in the [jersey 1
-wiki](https://wikis.oracle.com/display/Jersey/HowToConfigureExtendedWADL).
-
The difference in configuration against jersey 1.x is in property
configuring the custom WadlGeneratorConfig. Instead of property key
'com.sun.jersey.config.property.WadlGeneratorConfig' use the property
@@ -48,6 +45,8 @@
instructions at [the module README file](../../README.html) in order to
deploy the example.
+NOTE: the example must be run with a JDK prior to JDK 13 otherwise javadoc plugin won't work properly.
+
Otherwise, you can run the example using embedded GlassFish as follows:
You can run the example using Grizzly as follows:
diff --git a/examples/extended-wadl-webapp/pom.xml b/examples/extended-wadl-webapp/pom.xml
index 0b5f9f2..ecd9e5a 100644
--- a/examples/extended-wadl-webapp/pom.xml
+++ b/examples/extended-wadl-webapp/pom.xml
@@ -150,7 +150,7 @@
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxb2-maven-plugin</artifactId>
- <version>2.5.0</version>
+ <version>3.2.0</version>
<executions>
<execution>
<id>xjc</id>
@@ -290,6 +290,69 @@
<profiles>
<profile>
+ <id>testsJdk11</id>
+ <activation>
+ <jdk>[11,)</jdk>
+ </activation>
+ <dependencies>
+ <dependency>
+ <groupId>com.sun.activation</groupId>
+ <artifactId>jakarta.activation</artifactId>
+ </dependency>
+ </dependencies>
+ </profile>
+ <profile>
+ <id>javadocAndTestsSkipJDK13</id>
+ <activation>
+ <jdk>[13,)</jdk>
+ </activation>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ <plugin>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <executions>
+ <execution>
+ <phase>generate-resources</phase>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ <configuration>
+ <target>
+ <echo>****************************************************</echo>
+ <echo>****THIS EXAMPLE WORKS ONLY ON JDKs PRIOR to 13!****</echo>
+ <echo>********CURRENT JDK IS NOT SUPPORTED!***************</echo>
+ <echo>****************************************************</echo>
+ </target>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.jvnet.jaxb2.maven2</groupId>
+ <artifactId>maven-jaxb2-plugin</artifactId>
+ <version>0.14.0</version>
+ <configuration>
+ <verbose>false</verbose>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ <profile>
<id>pre-release</id>
<build>
<plugins>
diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/BeanHelper.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/BeanHelper.java
index c5c4cfe..3e3da09 100644
--- a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/BeanHelper.java
+++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/BeanHelper.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 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
@@ -129,13 +129,7 @@
* CDI does not provide sufficient support for ThreadScoped Supplier
*/
if (binding.getScope() == PerThread.class) {
- BeanManagerImpl manager;
- if (beanManager instanceof BeanManagerProxy) {
- manager = ((BeanManagerProxy) beanManager).unwrap();
- } else {
- manager = (BeanManagerImpl) beanManager;
- }
- abd.addBean(new InitializableSupplierThreadScopeBean(runtimeType, binding, manager));
+ abd.addBean(new InitializableSupplierThreadScopeBean(runtimeType, binding, beanManagerImpl(beanManager)));
} else {
abd.addBean(new InitializableSupplierInstanceBean<>(runtimeType, binding));
abd.addBean(new InitializableSupplierInstanceBeanBridge<>(runtimeType, binding));
@@ -163,12 +157,18 @@
InjectionTarget<Supplier<T>> jit = getJerseyInjectionTarget(supplierClass, injectionTarget, supplierBean, resolvers);
supplierBean.setInjectionTarget(jit);
- final SupplierBeanBridge supplierBeanBridge = new SupplierBeanBridge(runtimeType, binding, beanManager);
-
- abd.addBean(supplierBean);
- abd.addBean(supplierBeanBridge);
-
- return new BindingBeanPair(binding, supplierBean, supplierBeanBridge);
+ /*
+ * CDI does not provide sufficient support for ThreadScoped Supplier
+ */
+ if (binding.getScope() == PerThread.class) {
+ abd.addBean(new SupplierThreadScopeClassBean(runtimeType, binding, supplierBean, beanManagerImpl(beanManager)));
+ return null;
+ } else {
+ final SupplierBeanBridge supplierBeanBridge = new SupplierBeanBridge(runtimeType, binding, beanManager);
+ abd.addBean(supplierBean);
+ abd.addBean(supplierBeanBridge);
+ return new BindingBeanPair(binding, supplierBean, supplierBeanBridge);
+ }
}
/**
@@ -255,6 +255,14 @@
return null;
}
+ private static BeanManagerImpl beanManagerImpl(BeanManager beanManager) {
+ if (beanManager instanceof BeanManagerProxy) {
+ return ((BeanManagerProxy) beanManager).unwrap();
+ } else {
+ return (BeanManagerImpl) beanManager;
+ }
+ }
+
private static <T> InjectionTarget<T> getJerseyInjectionTarget(Class<T> clazz, InjectionTarget<T> injectionTarget,
Bean<T> bean, Collection<InjectionResolver> resolvers) {
BasicInjectionTarget<T> it = (BasicInjectionTarget<T>) injectionTarget;
diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/InitializableSupplierThreadScopeBean.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/InitializableSupplierThreadScopeBean.java
index c5f211e..3415b46 100644
--- a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/InitializableSupplierThreadScopeBean.java
+++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/InitializableSupplierThreadScopeBean.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 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
@@ -111,31 +111,4 @@
ProxyFactory<T> factory = new ProxyFactory<>(contextId, getBeanClass(), getTypes(), this);
return factory.create(beanInstance);
}
-
- private static class ThreadScopeBeanInstance<T> extends ContextBeanInstance<T> {
-
- private final WeakHashMap<Thread, Object> instances = new WeakHashMap<>();
-
- private final Supplier<T> supplier;
-
- /**
- * Creates a new invocation handler with supplier which provides a current injected value in proper scope.
- *
- * @param supplier provider of the value.
- */
- private ThreadScopeBeanInstance(Supplier<T> supplier, Bean<T> bean, String contextId) {
- super(bean, new StringBeanIdentifier(((PassivationCapable) bean).getId()), contextId);
- this.supplier = supplier;
- }
-
- @Override
- public Object invoke(Object obj, Method method, Object... arguments) throws Throwable {
- Object instance = instances.computeIfAbsent(Thread.currentThread(), thread -> supplier.get());
- return super.invoke(instance, method, arguments);
- }
-
- public void dispose() {
- this.instances.clear();
- }
- }
}
diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/SupplierThreadScopeClassBean.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/SupplierThreadScopeClassBean.java
new file mode 100644
index 0000000..fe3e8b4
--- /dev/null
+++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/SupplierThreadScopeClassBean.java
@@ -0,0 +1,114 @@
+/*
+ * 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.inject.weld.internal.bean;
+
+import org.glassfish.jersey.internal.inject.SupplierClassBinding;
+import org.glassfish.jersey.internal.inject.SupplierInstanceBinding;
+import org.glassfish.jersey.internal.util.collection.LazyValue;
+import org.glassfish.jersey.internal.util.collection.Value;
+import org.glassfish.jersey.internal.util.collection.Values;
+import org.jboss.weld.bean.proxy.BeanInstance;
+import org.jboss.weld.bean.proxy.ProxyFactory;
+import org.jboss.weld.manager.BeanManagerImpl;
+
+import jakarta.enterprise.context.Dependent;
+import jakarta.enterprise.context.spi.CreationalContext;
+import jakarta.ws.rs.RuntimeType;
+import java.lang.annotation.Annotation;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
+
+
+/**
+ * Creates an implementation of {@link jakarta.enterprise.inject.spi.Bean} interface using Jersey's {@link SupplierInstanceBinding}.
+ * Binding provides the information about the bean also called {@link jakarta.enterprise.inject.spi.BeanAttributes} information.
+ * The {@code Bean} does not use {@link org.glassfish.jersey.inject.weld.internal.injector.JerseyInjectionTarget} because serves already
+ * created proxy, therefore the create operation just return provided instance without any other contextual operation
+ * (produce, inject, destroy).
+ * <p>
+ * This bean is special and is used only for service registered as a {@link org.glassfish.jersey.internal.inject.PerThread} and
+ * works through the proxy which serves the correct instance per the given thread.
+ * <p>
+ * Register example:
+ * <pre>
+ * AbstractBinder {
+ * @Override
+ * protected void configure() {
+ * bindFactory(MyFactoryInjectionSupplier.class)
+ * .to(MyBean.class)
+ * .in(PerThread.class);
+ * }
+ * }
+ * </pre>
+ * Inject example:
+ * <pre>
+ * @Path("/")
+ * public class MyResource {
+ * @Inject
+ * private MyBean myBean;
+ * }
+ * </pre>
+ */
+class SupplierThreadScopeClassBean extends JerseyBean<Object> {
+ private final LazyValue<ThreadScopeBeanInstance<Object>> beanInstance;
+ private final SupplierClassBinding binding;
+ private final LazyValue<Object> proxy;
+ private final AtomicReference<CreationalContext> creationalContextAtomicReference = new AtomicReference<>();
+
+ SupplierThreadScopeClassBean(RuntimeType runtimeType,
+ SupplierClassBinding binding,
+ SupplierClassBean supplierClassBean,
+ BeanManagerImpl beanManager) {
+ super(runtimeType, binding);
+ this.binding = binding;
+ this.beanInstance = Values.lazy((Value<ThreadScopeBeanInstance<Object>>) () -> {
+ Supplier supplierInstance = supplierClassBean.create(creationalContextAtomicReference.get());
+ ThreadScopeBeanInstance scopeBeanInstance =
+ new ThreadScopeBeanInstance(supplierInstance, this, beanManager.getContextId());
+ return scopeBeanInstance;
+ });
+ this.proxy = Values.lazy((Value<Object>) () -> createClientProxy(beanInstance.get(), beanManager.getContextId()));
+ }
+
+ @Override
+ public Class<? extends Annotation> getScope() {
+ return Dependent.class;
+ }
+
+ @Override
+ public Object create(CreationalContext<Object> ctx) {
+ creationalContextAtomicReference.set(ctx);
+ return proxy.get();
+ }
+
+ @Override
+ public void destroy(Object instance, CreationalContext<Object> creationalContext) {
+ if (beanInstance.isInitialized()) {
+ this.beanInstance.get().dispose();
+ }
+ }
+
+ @Override
+ public Class<?> getBeanClass() {
+ return (Class<?>) this.binding.getContracts().iterator().next();
+ }
+
+ private <T> T createClientProxy(BeanInstance beanInstance, String contextId) {
+ ProxyFactory<T> factory = new ProxyFactory<>(contextId, getBeanClass(), getTypes(), this);
+ return factory.create(beanInstance);
+ }
+}
diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/ThreadScopeBeanInstance.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/ThreadScopeBeanInstance.java
new file mode 100644
index 0000000..73ac651
--- /dev/null
+++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/ThreadScopeBeanInstance.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2021, 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.inject.weld.internal.bean;
+
+import org.jboss.weld.bean.StringBeanIdentifier;
+import org.jboss.weld.bean.proxy.ContextBeanInstance;
+
+import jakarta.enterprise.inject.spi.Bean;
+import jakarta.enterprise.inject.spi.PassivationCapable;
+import java.lang.reflect.Method;
+import java.util.WeakHashMap;
+import java.util.function.Supplier;
+
+/**
+ * {@link org.glassfish.jersey.internal.inject.PerThread} scope bean instance used from
+ * {@link InitializableSupplierThreadScopeBean} and {@link SupplierThreadScopeClassBean}.
+ *
+ * @param <T> Typed of the bean supplied by a {@code Supplier}.
+ */
+class ThreadScopeBeanInstance<T> extends ContextBeanInstance<T> {
+
+ private final WeakHashMap<Thread, Object> instances = new WeakHashMap<>();
+
+ private final Supplier<T> supplier;
+
+ /**
+ * Creates a new invocation handler with supplier which provides a current injected value in proper scope.
+ *
+ * @param supplier provider of the value.
+ */
+ ThreadScopeBeanInstance(Supplier<T> supplier, Bean<T> bean, String contextId) {
+ super(bean, new StringBeanIdentifier(((PassivationCapable) bean).getId()), contextId);
+ this.supplier = supplier;
+ }
+
+ @Override
+ public Object invoke(Object obj, Method method, Object... arguments) throws Throwable {
+ Object instance = instances.computeIfAbsent(Thread.currentThread(), thread -> supplier.get());
+ return super.invoke(instance, method, arguments);
+ }
+
+ public void dispose() {
+ this.instances.clear();
+ }
+}
diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/managed/BinderRegisterExtension.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/managed/BinderRegisterExtension.java
index 60d48e4..8917bb0 100644
--- a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/managed/BinderRegisterExtension.java
+++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/managed/BinderRegisterExtension.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 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
@@ -347,8 +347,10 @@
}
BindingBeanPair pair = BeanHelper.registerSupplier(
runtimeType, (SupplierClassBinding<?>) binding, abd, injectionResolvers, beanManager);
- for (Type contract : ((SupplierClassBinding<?>) binding).getContracts()) {
- supplierClassBindings.add(contract, pair);
+ if (pair != null) {
+ for (Type contract : ((SupplierClassBinding<?>) binding).getContracts()) {
+ supplierClassBindings.add(contract, pair);
+ }
}
} else if (InitializableInstanceBinding.class.isAssignableFrom(binding.getClass())) {
if (RuntimeType.SERVER == runtimeType
diff --git a/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/CzechGreeting.java b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/CzechGreeting.java
index 68b57d9..14bd268 100644
--- a/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/CzechGreeting.java
+++ b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/CzechGreeting.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 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
@@ -26,9 +26,11 @@
static final String GREETING = "Ahoj";
+ private String greeting = GREETING + "#" + Thread.currentThread().getName();
+
@Override
public String getGreeting() {
- return GREETING + "#" + Thread.currentThread().getName();
+ return greeting;
}
@Override
diff --git a/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/TestPreinitialization.java b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/TestPreinitialization.java
index b7806c1..97d4a49 100644
--- a/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/TestPreinitialization.java
+++ b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/TestPreinitialization.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 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
@@ -97,6 +97,13 @@
binder.bindFactory(new ThreadScopeTest.SupplierGreeting2(ThreadScopeTest.EnglishGreeting2.GREETING))
.to(ThreadScopeTest.EnglishGreeting2.class)
.in(PerThread.class);
+
+ //testSupplierClassBindingThreadScopedInSingletonScope
+ binder.bindAsContract(ThreadScopeTest.SingletonObject3.class)
+ .in(Singleton.class);
+ binder.bindFactory(ThreadScopeTest.SupplierGreeting3.class)
+ .to(ThreadScopeTest.Greeting3.class)
+ .in(PerThread.class);
}
//ClientInstanceInjectionTest
diff --git a/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/ThreadScopeTest.java b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/ThreadScopeTest.java
index 144c333..7c959fe 100644
--- a/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/ThreadScopeTest.java
+++ b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/ThreadScopeTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 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
@@ -18,6 +18,7 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
import jakarta.enterprise.context.RequestScoped;
@@ -181,7 +182,7 @@
}
@Test
- public void testThreadScopedInSingletonScope() {
+ public void testThreadScopedInSingletonScope() throws InterruptedException {
// InjectionManager injectionManager = BindingTestHelper.createInjectionManager();
// BindingTestHelper.bind(injectionManager, binder -> {
// binder.bindAsContract(SingletonObject.class)
@@ -202,6 +203,52 @@
assertNotNull(greeting2);
assertEquals(greeting1, greeting2);
+
+ final AtomicReference<String> greetingAtomicReference = new AtomicReference<>();
+ Runnable runnable = () ->
+ greetingAtomicReference.set(injectionManager.getInstance(SingletonObject.class).getGreeting().getGreeting());
+
+ Thread newThread = new Thread(runnable);
+ newThread.start();
+ newThread.join();
+
+ assertEquals(greeting1.getGreeting(), greeting2.getGreeting());
+ assertNotEquals(greeting1.getGreeting(), greetingAtomicReference.get());
+ }
+
+ @Test
+ public void testSupplierClassBindingThreadScopedInSingletonScope() throws InterruptedException {
+// InjectionManager injectionManager = BindingTestHelper.createInjectionManager();
+// BindingTestHelper.bind(injectionManager, binder -> {
+// binder.bindAsContract(SingletonObject.class)
+// .in(Singleton.class);
+//
+// binder.bindFactory(SupplierGreeting.class)
+// .to(Greeting.class)
+// .in(PerThread.class);
+// });
+
+ SingletonObject3 instance1 = injectionManager.getInstance(SingletonObject3.class);
+ Greeting3 greeting1 = instance1.getGreeting();
+ assertNotNull(greeting1);
+
+ // Precisely the same object
+ SingletonObject3 instance2 = injectionManager.getInstance(SingletonObject3.class);
+ Greeting3 greeting2 = instance2.getGreeting();
+ assertNotNull(greeting2);
+
+ assertEquals(greeting1, greeting2);
+
+ final AtomicReference<String> greetingAtomicReference = new AtomicReference<>();
+ Runnable runnable = () ->
+ greetingAtomicReference.set(injectionManager.getInstance(SingletonObject3.class).getGreeting().getGreeting());
+
+ Thread newThread = new Thread(runnable);
+ newThread.start();
+ newThread.join();
+
+ assertEquals(greeting1.getGreeting(), greeting2.getGreeting());
+ assertNotEquals(greeting1.getGreeting(), greetingAtomicReference.get());
}
@RequestScoped
@@ -227,6 +274,17 @@
}
@Singleton
+ public static class SingletonObject3 {
+
+ @Inject
+ Greeting3 greeting;
+
+ public Greeting3 getGreeting() {
+ return greeting;
+ }
+ }
+
+ @Singleton
public static class SingletonObject {
@Inject
@@ -260,6 +318,14 @@
}
@Vetoed
+ static class SupplierGreeting3 implements Supplier<Greeting3> {
+ @Override
+ public Greeting3 get() {
+ return new CzechGreeting3();
+ }
+ }
+
+ @Vetoed
static class SupplierGreeting2 implements Supplier<Greeting2> {
private final String greetingType;
@@ -291,13 +357,33 @@
}
@Vetoed
+ static class CzechGreeting3 implements Greeting3 {
+
+ static final String GREETING = "Ahoj";
+
+ private String greeting = GREETING + "#" + Thread.currentThread().getName();
+
+ @Override
+ public String getGreeting() {
+ return greeting;
+ }
+
+ @Override
+ public String toString() {
+ return "CzechGreeting";
+ }
+ }
+
+ @Vetoed
static class CzechGreeting2 implements Greeting2 {
static final String GREETING = "Ahoj";
+ private String greeting = GREETING + "#" + Thread.currentThread().getName();
+
@Override
public String getGreeting() {
- return GREETING + "#" + Thread.currentThread().getName();
+ return greeting;
}
@Override
@@ -374,9 +460,11 @@
static final String GREETING = "Ahoj";
+ private String greeting = GREETING + "#" + Thread.currentThread().getName();
+
@Override
public String getGreeting() {
- return GREETING + "#" + Thread.currentThread().getName();
+ return greeting;
}
@Override
@@ -386,6 +474,11 @@
}
@FunctionalInterface
+ static interface Greeting3 {
+ String getGreeting();
+ }
+
+ @FunctionalInterface
static interface Greeting2 {
String getGreeting();
}
diff --git a/inject/cdi2-se/src/main/java/org/glassfish/jersey/inject/cdi/se/bean/BeanHelper.java b/inject/cdi2-se/src/main/java/org/glassfish/jersey/inject/cdi/se/bean/BeanHelper.java
index 2ae0ebc..aff3f91 100644
--- a/inject/cdi2-se/src/main/java/org/glassfish/jersey/inject/cdi/se/bean/BeanHelper.java
+++ b/inject/cdi2-se/src/main/java/org/glassfish/jersey/inject/cdi/se/bean/BeanHelper.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 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
@@ -116,18 +116,11 @@
* @param <T> type of the instance which is registered.
*/
public static <T> void registerSupplier(SupplierInstanceBinding<T> binding, AfterBeanDiscovery abd, BeanManager beanManager) {
- BeanManagerImpl manager;
- if (beanManager instanceof BeanManagerProxy) {
- manager = ((BeanManagerProxy) beanManager).unwrap();
- } else {
- manager = (BeanManagerImpl) beanManager;
- }
-
/*
* CDI does not provide sufficient support for ThreadScoped Supplier
*/
if (binding.getScope() == PerThread.class) {
- abd.addBean(new SupplierThreadScopeBean(binding, manager));
+ abd.addBean(new SupplierThreadScopeBean(binding, beanManagerImpl(beanManager)));
} else {
abd.addBean(new SupplierInstanceBean<>(binding));
abd.addBean(new SupplierInstanceBeanBridge<>(binding));
@@ -155,8 +148,23 @@
InjectionTarget<Supplier<T>> jit = getJerseyInjectionTarget(supplierClass, injectionTarget, supplierBean, resolvers);
supplierBean.setInjectionTarget(jit);
- abd.addBean(supplierBean);
- abd.addBean(new SupplierBeanBridge(binding, beanManager));
+ /*
+ * CDI does not provide sufficient support for ThreadScoped Supplier
+ */
+ if (binding.getScope() == PerThread.class) {
+ abd.addBean(new SupplierThreadScopeClassBean(binding, supplierBean, beanManagerImpl(beanManager)));
+ } else {
+ abd.addBean(supplierBean);
+ abd.addBean(new SupplierBeanBridge(binding, beanManager));
+ }
+ }
+
+ private static BeanManagerImpl beanManagerImpl(BeanManager beanManager) {
+ if (beanManager instanceof BeanManagerProxy) {
+ return ((BeanManagerProxy) beanManager).unwrap();
+ } else {
+ return (BeanManagerImpl) beanManager;
+ }
}
private static <T> InjectionTarget<T> getJerseyInjectionTarget(Class<T> clazz, InjectionTarget<T> injectionTarget,
diff --git a/inject/cdi2-se/src/main/java/org/glassfish/jersey/inject/cdi/se/bean/SupplierThreadScopeBean.java b/inject/cdi2-se/src/main/java/org/glassfish/jersey/inject/cdi/se/bean/SupplierThreadScopeBean.java
index a0c0d34..b95ae0a 100644
--- a/inject/cdi2-se/src/main/java/org/glassfish/jersey/inject/cdi/se/bean/SupplierThreadScopeBean.java
+++ b/inject/cdi2-se/src/main/java/org/glassfish/jersey/inject/cdi/se/bean/SupplierThreadScopeBean.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 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
@@ -17,20 +17,13 @@
package org.glassfish.jersey.inject.cdi.se.bean;
import java.lang.annotation.Annotation;
-import java.lang.reflect.Method;
-import java.util.WeakHashMap;
-import java.util.function.Supplier;
import jakarta.enterprise.context.Dependent;
import jakarta.enterprise.context.spi.CreationalContext;
-import jakarta.enterprise.inject.spi.Bean;
-import jakarta.enterprise.inject.spi.PassivationCapable;
import org.glassfish.jersey.internal.inject.SupplierInstanceBinding;
-import org.jboss.weld.bean.StringBeanIdentifier;
import org.jboss.weld.bean.proxy.BeanInstance;
-import org.jboss.weld.bean.proxy.ContextBeanInstance;
import org.jboss.weld.bean.proxy.ProxyFactory;
import org.jboss.weld.manager.BeanManagerImpl;
@@ -107,31 +100,4 @@
ProxyFactory<T> factory = new ProxyFactory<>(contextId, getBeanClass(), getTypes(), this);
return factory.create(beanInstance);
}
-
- private static class ThreadScopeBeanInstance<T> extends ContextBeanInstance<T> {
-
- private final WeakHashMap<Thread, Object> instances = new WeakHashMap<>();
-
- private final Supplier<T> supplier;
-
- /**
- * Creates a new invocation handler with supplier which provides a current injected value in proper scope.
- *
- * @param supplier provider of the value.
- */
- private ThreadScopeBeanInstance(Supplier<T> supplier, Bean<T> bean, String contextId) {
- super(bean, new StringBeanIdentifier(((PassivationCapable) bean).getId()), contextId);
- this.supplier = supplier;
- }
-
- @Override
- public Object invoke(Object obj, Method method, Object... arguments) throws Throwable {
- Object instance = instances.computeIfAbsent(Thread.currentThread(), thread -> supplier.get());
- return super.invoke(instance, method, arguments);
- }
-
- public void dispose() {
- this.instances.clear();
- }
- }
}
diff --git a/inject/cdi2-se/src/main/java/org/glassfish/jersey/inject/cdi/se/bean/SupplierThreadScopeClassBean.java b/inject/cdi2-se/src/main/java/org/glassfish/jersey/inject/cdi/se/bean/SupplierThreadScopeClassBean.java
new file mode 100644
index 0000000..698bfb0
--- /dev/null
+++ b/inject/cdi2-se/src/main/java/org/glassfish/jersey/inject/cdi/se/bean/SupplierThreadScopeClassBean.java
@@ -0,0 +1,111 @@
+/*
+ * 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.inject.cdi.se.bean;
+
+import java.lang.annotation.Annotation;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
+
+import jakarta.enterprise.context.Dependent;
+import jakarta.enterprise.context.spi.CreationalContext;
+
+import org.glassfish.jersey.internal.inject.SupplierClassBinding;
+import org.glassfish.jersey.internal.inject.SupplierInstanceBinding;
+import org.glassfish.jersey.internal.util.collection.LazyValue;
+import org.glassfish.jersey.internal.util.collection.Value;
+import org.glassfish.jersey.internal.util.collection.Values;
+import org.jboss.weld.bean.proxy.BeanInstance;
+import org.jboss.weld.bean.proxy.ProxyFactory;
+import org.jboss.weld.manager.BeanManagerImpl;
+
+
+/**
+ * Creates an implementation of {@link jakarta.enterprise.inject.spi.Bean} interface using Jersey's {@link SupplierInstanceBinding}.
+ * Binding provides the information about the bean also called {@link jakarta.enterprise.inject.spi.BeanAttributes} information.
+ * The {@code Bean} does not use {@link org.glassfish.jersey.inject.cdi.se.injector.JerseyInjectionTarget} because serves already
+ * created proxy, therefore the create operation just return provided instance without any other contextual operation
+ * (produce, inject, destroy).
+ * <p>
+ * This bean is special and is used only for service registered as a {@link org.glassfish.jersey.internal.inject.PerThread} and
+ * works through the proxy which serves the correct instance per the given thread.
+ * <p>
+ * Register example:
+ * <pre>
+ * AbstractBinder {
+ * @Override
+ * protected void configure() {
+ * bindFactory(MyFactoryInjectionSupplier.class)
+ * .to(MyBean.class)
+ * .in(PerThread.class);
+ * }
+ * }
+ * </pre>
+ * Inject example:
+ * <pre>
+ * @Path("/")
+ * public class MyResource {
+ * @Inject
+ * private MyBean myBean;
+ * }
+ * </pre>
+ */
+class SupplierThreadScopeClassBean extends JerseyBean<Object> {
+ private final LazyValue<ThreadScopeBeanInstance<Object>> beanInstance;
+ private final SupplierClassBinding binding;
+ private final LazyValue<Object> proxy;
+ private final AtomicReference<CreationalContext> creationalContextAtomicReference = new AtomicReference<>();
+
+ SupplierThreadScopeClassBean(SupplierClassBinding binding, SupplierClassBean supplierClassBean, BeanManagerImpl beanManager) {
+ super(binding);
+ this.binding = binding;
+ this.beanInstance = Values.lazy((Value<ThreadScopeBeanInstance<Object>>) () -> {
+ Supplier supplierInstance = supplierClassBean.create(creationalContextAtomicReference.get());
+ ThreadScopeBeanInstance scopeBeanInstance =
+ new ThreadScopeBeanInstance(supplierInstance, this, beanManager.getContextId());
+ return scopeBeanInstance;
+ });
+ this.proxy = Values.lazy((Value<Object>) () -> createClientProxy(beanInstance.get(), beanManager.getContextId()));
+ }
+
+ @Override
+ public Class<? extends Annotation> getScope() {
+ return Dependent.class;
+ }
+
+ @Override
+ public Object create(CreationalContext<Object> ctx) {
+ creationalContextAtomicReference.set(ctx);
+ return proxy.get();
+ }
+
+ @Override
+ public void destroy(Object instance, CreationalContext<Object> creationalContext) {
+ if (beanInstance.isInitialized()) {
+ this.beanInstance.get().dispose();
+ }
+ }
+
+ @Override
+ public Class<?> getBeanClass() {
+ return (Class<?>) this.binding.getContracts().iterator().next();
+ }
+
+ private <T> T createClientProxy(BeanInstance beanInstance, String contextId) {
+ ProxyFactory<T> factory = new ProxyFactory<>(contextId, getBeanClass(), getTypes(), this);
+ return factory.create(beanInstance);
+ }
+}
diff --git a/inject/cdi2-se/src/main/java/org/glassfish/jersey/inject/cdi/se/bean/ThreadScopeBeanInstance.java b/inject/cdi2-se/src/main/java/org/glassfish/jersey/inject/cdi/se/bean/ThreadScopeBeanInstance.java
new file mode 100644
index 0000000..a200960
--- /dev/null
+++ b/inject/cdi2-se/src/main/java/org/glassfish/jersey/inject/cdi/se/bean/ThreadScopeBeanInstance.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2017, 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.inject.cdi.se.bean;
+
+import org.jboss.weld.bean.StringBeanIdentifier;
+import org.jboss.weld.bean.proxy.ContextBeanInstance;
+
+import java.lang.reflect.Method;
+import java.util.WeakHashMap;
+import java.util.function.Supplier;
+
+import jakarta.enterprise.inject.spi.Bean;
+import jakarta.enterprise.inject.spi.PassivationCapable;
+
+/**
+ * {@link org.glassfish.jersey.internal.inject.PerThread} scope bean instance used from
+ * {@link SupplierThreadScopeBean} and {@link SupplierThreadScopeClassBean}.
+ *
+ * @param <T> Typed of the bean supplied by a {@code Supplier}.
+ */
+class ThreadScopeBeanInstance<T> extends ContextBeanInstance<T> {
+
+ private final WeakHashMap<Thread, Object> instances = new WeakHashMap<>();
+
+ private final Supplier<T> supplier;
+
+ /**
+ * Creates a new invocation handler with supplier which provides a current injected value in proper scope.
+ *
+ * @param supplier provider of the value.
+ */
+ ThreadScopeBeanInstance(Supplier<T> supplier, Bean<T> bean, String contextId) {
+ super(bean, new StringBeanIdentifier(((PassivationCapable) bean).getId()), contextId);
+ this.supplier = supplier;
+ }
+
+ @Override
+ public Object invoke(Object obj, Method method, Object... arguments) throws Throwable {
+ Object instance = instances.computeIfAbsent(Thread.currentThread(), thread -> supplier.get());
+ return super.invoke(instance, method, arguments);
+ }
+
+ public void dispose() {
+ this.instances.clear();
+ }
+}
diff --git a/inject/cdi2-se/src/test/java/org/glassfish/jersey/inject/cdi/se/CzechGreeting.java b/inject/cdi2-se/src/test/java/org/glassfish/jersey/inject/cdi/se/CzechGreeting.java
index fd4ae86..3f4f924 100644
--- a/inject/cdi2-se/src/test/java/org/glassfish/jersey/inject/cdi/se/CzechGreeting.java
+++ b/inject/cdi2-se/src/test/java/org/glassfish/jersey/inject/cdi/se/CzechGreeting.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 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
@@ -26,9 +26,11 @@
static final String GREETING = "Ahoj";
+ private String greeting = GREETING + "#" + Thread.currentThread().getName();
+
@Override
public String getGreeting() {
- return GREETING + "#" + Thread.currentThread().getName();
+ return greeting;
}
@Override
diff --git a/inject/cdi2-se/src/test/java/org/glassfish/jersey/inject/cdi/se/ThreadScopeTest.java b/inject/cdi2-se/src/test/java/org/glassfish/jersey/inject/cdi/se/ThreadScopeTest.java
index cf377c1..1fe627a 100644
--- a/inject/cdi2-se/src/test/java/org/glassfish/jersey/inject/cdi/se/ThreadScopeTest.java
+++ b/inject/cdi2-se/src/test/java/org/glassfish/jersey/inject/cdi/se/ThreadScopeTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 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
@@ -17,6 +17,7 @@
package org.glassfish.jersey.inject.cdi.se;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicReference;
import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Inject;
@@ -167,7 +168,7 @@
}
@Test
- public void testThreadScopedInSingletonScope() {
+ public void testThreadScopedInSingletonScope() throws InterruptedException {
InjectionManager injectionManager = BindingTestHelper.createInjectionManager();
BindingTestHelper.bind(injectionManager, binder -> {
binder.bindAsContract(SingletonObject.class)
@@ -188,6 +189,52 @@
assertNotNull(greeting2);
assertEquals(greeting1, greeting2);
+
+ final AtomicReference<String> greetingAtomicReference = new AtomicReference<>();
+ Runnable runnable = () ->
+ greetingAtomicReference.set(injectionManager.getInstance(SingletonObject.class).getGreeting().getGreeting());
+
+ Thread newThread = new Thread(runnable);
+ newThread.start();
+ newThread.join();
+
+ assertEquals(greeting1.getGreeting(), greeting2.getGreeting());
+ assertNotEquals(greeting1.getGreeting(), greetingAtomicReference.get());
+ }
+
+ @Test
+ public void testSupplierClassBindingThreadScopedInSingletonScope() throws InterruptedException {
+ InjectionManager injectionManager = BindingTestHelper.createInjectionManager();
+ BindingTestHelper.bind(injectionManager, binder -> {
+ binder.bindAsContract(SingletonObject.class)
+ .in(Singleton.class);
+
+ binder.bindFactory(SupplierGreeting.class)
+ .to(Greeting.class)
+ .in(PerThread.class);
+ });
+
+ SingletonObject instance1 = injectionManager.getInstance(SingletonObject.class);
+ Greeting greeting1 = instance1.getGreeting();
+ assertNotNull(greeting1);
+
+ // Precisely the same object
+ SingletonObject instance2 = injectionManager.getInstance(SingletonObject.class);
+ Greeting greeting2 = instance2.getGreeting();
+ assertNotNull(greeting2);
+
+ assertEquals(greeting1, greeting2);
+
+ final AtomicReference<String> greetingAtomicReference = new AtomicReference<>();
+ Runnable runnable = () ->
+ greetingAtomicReference.set(injectionManager.getInstance(SingletonObject.class).getGreeting().getGreeting());
+
+ Thread newThread = new Thread(runnable);
+ newThread.start();
+ newThread.join();
+
+ assertEquals(greeting1.getGreeting(), greeting2.getGreeting());
+ assertNotEquals(greeting1.getGreeting(), greetingAtomicReference.get());
}
@RequestScoped
diff --git a/media/json-binding/src/main/java/org/glassfish/jersey/jsonb/JsonBindingFeature.java b/media/json-binding/src/main/java/org/glassfish/jersey/jsonb/JsonBindingFeature.java
index b80ac9e..a41afb0 100644
--- a/media/json-binding/src/main/java/org/glassfish/jersey/jsonb/JsonBindingFeature.java
+++ b/media/json-binding/src/main/java/org/glassfish/jersey/jsonb/JsonBindingFeature.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 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
@@ -16,16 +16,25 @@
package org.glassfish.jersey.jsonb;
+import jakarta.ws.rs.RuntimeType;
+import jakarta.ws.rs.core.Application;
import jakarta.ws.rs.core.Configuration;
import jakarta.ws.rs.core.Feature;
import jakarta.ws.rs.core.FeatureContext;
+import org.glassfish.jersey.ApplicationSupplier;
import org.glassfish.jersey.CommonProperties;
import org.glassfish.jersey.internal.InternalProperties;
+import org.glassfish.jersey.internal.inject.InjectionManagerSupplier;
import org.glassfish.jersey.internal.util.PropertiesHelper;
import org.glassfish.jersey.jsonb.internal.JsonBindingAutoDiscoverable;
import org.glassfish.jersey.jsonb.internal.JsonBindingProvider;
+import java.security.AccessController;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.logging.Logger;
+
/**
* Feature used to register JSON-B providers.
* <p>
@@ -50,12 +59,85 @@
*/
public class JsonBindingFeature implements Feature {
+ private static final Logger LOGGER = Logger.getLogger(JsonBindingFeature.class.getName());
private static final String JSON_FEATURE = JsonBindingFeature.class.getSimpleName();
@Override
public boolean configure(final FeatureContext context) {
final Configuration config = context.getConfiguration();
+ // ---- Allow to disable for compatibility with Pre JAX-RS 2.1 Jersey.
+
+ /* Either system properties */
+ final String bindingDisabledBySystemProperty = AccessController.doPrivileged(
+ PropertiesHelper.getSystemProperty(CommonProperties.JSON_BINDING_FEATURE_DISABLE));
+
+ final String bindingDisabledBySystemPropertyClient = AccessController.doPrivileged(
+ PropertiesHelper.getSystemProperty(CommonProperties.JSON_BINDING_FEATURE_DISABLE_CLIENT));
+
+ final String bindingDisabledBySystemPropertyServer = AccessController.doPrivileged(
+ PropertiesHelper.getSystemProperty(CommonProperties.JSON_BINDING_FEATURE_DISABLE_SERVER));
+
+ final RuntimeType runtimeType = config.getRuntimeType();
+
+ boolean bindingDisabledBySystem = PropertiesHelper.isPropertyOrNotSet(bindingDisabledBySystemProperty)
+ || (runtimeType == RuntimeType.CLIENT
+ && PropertiesHelper.isPropertyOrNotSet(bindingDisabledBySystemPropertyClient))
+ || (runtimeType == RuntimeType.SERVER
+ && PropertiesHelper.isPropertyOrNotSet(bindingDisabledBySystemPropertyServer));
+
+ /* Or config property */
+ final Boolean bindingDisabled = CommonProperties.getValue(config.getProperties(), runtimeType,
+ CommonProperties.JSON_BINDING_FEATURE_DISABLE, Boolean.class);
+
+ /* Config property takes precedence */
+ if ((bindingDisabledBySystem && !Boolean.FALSE.equals(bindingDisabled)) || Boolean.TRUE.equals(bindingDisabled)) {
+ return false;
+ }
+
+ final Set<String> disabledPackageNames = new HashSet<>();
+
+ /* Only a certain package names */
+ final String bindingDisabledPackageBySystemProperty = RuntimeType.SERVER == runtimeType
+ ? AccessController.doPrivileged(PropertiesHelper.getSystemProperty(
+ CommonProperties.JSON_BINDING_FEATURE_DISABLE_APPLICATION))
+ : null;
+
+ final String bindingDisabledPackage = RuntimeType.SERVER == runtimeType
+ ? CommonProperties.getValue(config.getProperties(), runtimeType,
+ CommonProperties.JSON_BINDING_FEATURE_DISABLE_APPLICATION, String.class)
+ : null;
+
+ separatePackageNames(disabledPackageNames, bindingDisabledPackageBySystemProperty);
+ separatePackageNames(disabledPackageNames, bindingDisabledPackage);
+
+ if (!disabledPackageNames.isEmpty() && !Boolean.FALSE.equals(bindingDisabled)) {
+ try {
+ Application app = null;
+ if (InjectionManagerSupplier.class.isInstance(context)) {
+ app = ((InjectionManagerSupplier) context).getInjectionManager().getInstance(Application.class);
+ if (app != null) {
+ while (ApplicationSupplier.class.isInstance(app) && ((ApplicationSupplier) app).getApplication() != app) {
+ app = ((ApplicationSupplier) app).getApplication();
+ }
+ for (String disabledPackageName : disabledPackageNames) {
+ if (app.getClass().getName().startsWith(disabledPackageName)) {
+ return false;
+ }
+ }
+ }
+ }
+ if (app == null) {
+ LOGGER.warning(LocalizationMessages.ERROR_JSONB_DETECTING_APPLICATION(
+ LocalizationMessages.ERROR_JSONB_APPLICATION_NOT_FOUND()));
+ }
+ } catch (Throwable throwable) {
+ LOGGER.warning(LocalizationMessages.ERROR_JSONB_DETECTING_APPLICATION(throwable.getMessage()));
+ }
+ }
+
+ // ---- End of disabling for compatibility with Pre JAX-RS 2.1 Jersey.
+
final String jsonFeature = CommonProperties.getValue(
config.getProperties(),
config.getRuntimeType(),
@@ -74,4 +156,12 @@
return true;
}
+
+ private static void separatePackageNames(Set<String> set, String packages) {
+ if (packages != null) {
+ for (String packageName : packages.split(",")) {
+ set.add(packageName.trim());
+ }
+ }
+ }
}
diff --git a/media/json-binding/src/main/resources/org/glassfish/jersey/jsonb/localization.properties b/media/json-binding/src/main/resources/org/glassfish/jersey/jsonb/localization.properties
index 4eee493..371e641 100644
--- a/media/json-binding/src/main/resources/org/glassfish/jersey/jsonb/localization.properties
+++ b/media/json-binding/src/main/resources/org/glassfish/jersey/jsonb/localization.properties
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2017, 2019 Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2017, 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
@@ -14,6 +14,8 @@
# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
#
+error.jsonb.application.not.found=Application not found.
+error.jsonb.detecting.application=JSON-B could not detect the application name: {0}.
error.jsonb.serialization=Error writing JSON-B serialized object.
error.jsonb.deserialization=Error deserializing object from entity stream.
error.jsonb.emptystream=JSON-B cannot parse empty input stream.
diff --git a/media/json-binding/src/test/java/org/glassfish/jersey/jsonb/internal/JsonbDisabledTest.java b/media/json-binding/src/test/java/org/glassfish/jersey/jsonb/internal/JsonbDisabledTest.java
new file mode 100644
index 0000000..7dc8a3f
--- /dev/null
+++ b/media/json-binding/src/test/java/org/glassfish/jersey/jsonb/internal/JsonbDisabledTest.java
@@ -0,0 +1,213 @@
+/*
+ * 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.jsonb.internal;
+
+import org.glassfish.jersey.CommonProperties;
+import org.glassfish.jersey.internal.inject.AbstractBinder;
+import org.glassfish.jersey.internal.inject.InjectionManager;
+import org.glassfish.jersey.internal.inject.InjectionManagerSupplier;
+import org.glassfish.jersey.internal.inject.Injections;
+import org.glassfish.jersey.jsonb.JsonBindingFeature;
+import org.glassfish.jersey.model.internal.CommonConfig;
+import org.glassfish.jersey.model.internal.ComponentBag;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.RuntimeType;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.FeatureContext;
+import java.lang.reflect.Proxy;
+import java.util.concurrent.atomic.AtomicReference;
+
+public class JsonbDisabledTest {
+ @Test
+ public void testDisabled() {
+ AtomicReference<CommonConfig> configReference = new AtomicReference<>();
+ FeatureContext featureContext1 = featureContextForConfig(configReference);
+
+ CommonConfig config1 = new CommonConfig(RuntimeType.SERVER, ComponentBag.INCLUDE_ALL);
+ configReference.set(config1);
+ Assertions.assertTrue(new JsonBindingFeature().configure(featureContext1));
+
+ CommonConfig config2 = new CommonConfig(RuntimeType.SERVER, ComponentBag.INCLUDE_ALL);
+ config2.property(CommonProperties.JSON_BINDING_FEATURE_DISABLE, true);
+ configReference.set(config2);
+ Assertions.assertFalse(new JsonBindingFeature().configure(featureContext1));
+ }
+
+ @Test
+ public void testDisabledBySystemProperty() {
+ AtomicReference<CommonConfig> configReference = new AtomicReference<>();
+ FeatureContext featureContext1 = featureContextForConfig(configReference);
+
+ CommonConfig config1 = new CommonConfig(RuntimeType.SERVER, ComponentBag.INCLUDE_ALL);
+ configReference.set(config1);
+ Assertions.assertTrue(new JsonBindingFeature().configure(featureContext1));
+
+ CommonConfig config2 = new CommonConfig(RuntimeType.SERVER, ComponentBag.INCLUDE_ALL);
+ System.setProperty(CommonProperties.JSON_BINDING_FEATURE_DISABLE_SERVER, "true");
+ configReference.set(config2);
+ Assertions.assertFalse(new JsonBindingFeature().configure(featureContext1));
+ System.clearProperty(CommonProperties.JSON_BINDING_FEATURE_DISABLE_SERVER);
+ }
+
+ @Test
+ public void testDisabledBySystemPropertyOverridenByConfigProperty() {
+ AtomicReference<CommonConfig> configReference = new AtomicReference<>();
+ FeatureContext featureContext1 = featureContextForConfig(configReference);
+
+ CommonConfig config1 = new CommonConfig(RuntimeType.SERVER, ComponentBag.INCLUDE_ALL);
+ configReference.set(config1);
+ Assertions.assertTrue(new JsonBindingFeature().configure(featureContext1));
+
+ CommonConfig config2 = new CommonConfig(RuntimeType.SERVER, ComponentBag.INCLUDE_ALL);
+ System.setProperty(CommonProperties.JSON_BINDING_FEATURE_DISABLE, "true");
+ config2.property(CommonProperties.JSON_BINDING_FEATURE_DISABLE, false);
+ configReference.set(config2);
+ Assertions.assertTrue(new JsonBindingFeature().configure(featureContext1));
+ System.clearProperty(CommonProperties.JSON_BINDING_FEATURE_DISABLE);
+ }
+
+ @Test
+ public void testDisabledByPropertyApplicationPackage() {
+ Application application = new Application() {
+ };
+ InjectionManager injectionManager = Injections.createInjectionManager();
+ injectionManager.register(new AbstractBinder() {
+ @Override
+ protected void configure() {
+ bind(application).to(Application.class);
+ }
+ });
+ AtomicReference<InjectionManager> injectionManagerReference = new AtomicReference<>(injectionManager);
+ AtomicReference<CommonConfig> configReference = new AtomicReference<>();
+ FeatureContext featureContext1 = featureContextForConfig(configReference, injectionManagerReference);
+
+ CommonConfig config1 = new CommonConfig(RuntimeType.SERVER, ComponentBag.INCLUDE_ALL);
+ configReference.set(config1);
+ Assertions.assertTrue(new JsonBindingFeature().configure(featureContext1));
+
+ System.setProperty(CommonProperties.JSON_BINDING_FEATURE_DISABLE_APPLICATION,
+ "some.does.not.matter, org.glassfish.jersey.jsonb.internal");
+ CommonConfig config2 = new CommonConfig(RuntimeType.SERVER, ComponentBag.INCLUDE_ALL);
+ configReference.set(config2);
+ Assertions.assertFalse(new JsonBindingFeature().configure(featureContext1));
+ System.clearProperty(CommonProperties.JSON_BINDING_FEATURE_DISABLE_APPLICATION);
+ }
+
+ @Test
+ public void disableOnClientOnlyTest() {
+ AtomicReference<CommonConfig> configReference = new AtomicReference<>();
+ FeatureContext featureContext1 = featureContextForConfig(configReference);
+
+ CommonConfig config1 = new CommonConfig(RuntimeType.CLIENT, ComponentBag.INCLUDE_ALL);
+ configReference.set(config1);
+ Assertions.assertTrue(new JsonBindingFeature().configure(featureContext1));
+
+ config1.property(CommonProperties.JSON_BINDING_FEATURE_DISABLE_SERVER, true);
+ Assertions.assertTrue(new JsonBindingFeature().configure(featureContext1));
+
+ config1.property(CommonProperties.JSON_BINDING_FEATURE_DISABLE_CLIENT, true);
+ Assertions.assertFalse(new JsonBindingFeature().configure(featureContext1));
+
+ CommonConfig config2 = new CommonConfig(RuntimeType.CLIENT, ComponentBag.INCLUDE_ALL);
+ System.setProperty(CommonProperties.JSON_BINDING_FEATURE_DISABLE_SERVER, "true");
+ configReference.set(config2);
+ Assertions.assertTrue(new JsonBindingFeature().configure(featureContext1));
+
+ System.setProperty(CommonProperties.JSON_BINDING_FEATURE_DISABLE_CLIENT, "true");
+ Assertions.assertFalse(new JsonBindingFeature().configure(featureContext1));
+
+ System.clearProperty(CommonProperties.JSON_BINDING_FEATURE_DISABLE_SERVER);
+ System.clearProperty(CommonProperties.JSON_BINDING_FEATURE_DISABLE_CLIENT);
+ }
+
+ @Test
+ public void testDisabledBySystemPropertyApplicationPackage() {
+ Application application = new Application() {
+ };
+ InjectionManager injectionManager = Injections.createInjectionManager();
+ injectionManager.register(new AbstractBinder() {
+ @Override
+ protected void configure() {
+ bind(application).to(Application.class);
+ }
+ });
+ AtomicReference<InjectionManager> injectionManagerReference = new AtomicReference<>(injectionManager);
+ AtomicReference<CommonConfig> configReference = new AtomicReference<>();
+ FeatureContext featureContext1 = featureContextForConfig(configReference, injectionManagerReference);
+
+ System.setProperty(CommonProperties.JSON_BINDING_FEATURE_DISABLE_APPLICATION,
+ "some.does.not.matter, org.glassfish.jersey.jsonb.internal");
+
+ CommonConfig config1 = new CommonConfig(RuntimeType.SERVER, ComponentBag.INCLUDE_ALL);
+ configReference.set(config1);
+ Assertions.assertFalse(new JsonBindingFeature().configure(featureContext1));
+
+ CommonConfig config2 = new CommonConfig(RuntimeType.SERVER, ComponentBag.INCLUDE_ALL);
+ configReference.set(config2);
+ Assertions.assertFalse(new JsonBindingFeature().configure(featureContext1));
+ System.clearProperty(CommonProperties.JSON_BINDING_FEATURE_DISABLE_APPLICATION);
+ }
+
+ @Test
+ public void testDisabledBySystemPropertyApplicationPackageEnabledByProperty() {
+ Application application = new Application() {
+ };
+ InjectionManager injectionManager = Injections.createInjectionManager();
+ injectionManager.register(new AbstractBinder() {
+ @Override
+ protected void configure() {
+ bind(application).to(Application.class);
+ }
+ });
+ AtomicReference<InjectionManager> injectionManagerReference = new AtomicReference<>(injectionManager);
+ AtomicReference<CommonConfig> configReference = new AtomicReference<>();
+ FeatureContext featureContext1 = featureContextForConfig(configReference, injectionManagerReference);
+
+ CommonConfig config1 = new CommonConfig(RuntimeType.SERVER, ComponentBag.INCLUDE_ALL);
+ configReference.set(config1);
+ Assertions.assertTrue(new JsonBindingFeature().configure(featureContext1));
+
+ System.setProperty(CommonProperties.JSON_BINDING_FEATURE_DISABLE_APPLICATION,
+ "some.does.not.matter, org.glassfish.jersey.jsonb.internal");
+
+ CommonConfig config2 = new CommonConfig(RuntimeType.SERVER, ComponentBag.INCLUDE_ALL);
+ config2.property(CommonProperties.JSON_BINDING_FEATURE_DISABLE, Boolean.FALSE);
+ configReference.set(config2);
+ Assertions.assertTrue(new JsonBindingFeature().configure(featureContext1));
+ System.clearProperty(CommonProperties.JSON_BINDING_FEATURE_DISABLE_APPLICATION);
+ }
+
+ private static FeatureContext featureContextForConfig(AtomicReference<CommonConfig> configReference) {
+ return featureContextForConfig(configReference, new AtomicReference<>());
+ }
+ private static FeatureContext featureContextForConfig(AtomicReference<CommonConfig> configReference,
+ AtomicReference<InjectionManager> injectionManager) {
+ return (FeatureContext) Proxy.newProxyInstance(
+ JsonbDisabledTest.class.getClassLoader(),
+ new Class[] {FeatureContext.class, InjectionManagerSupplier.class}, (proxy, method, args) -> {
+ switch (method.getName()) {
+ case "getConfiguration":
+ return configReference.get();
+ case "getInjectionManager":
+ return injectionManager.get();
+ }
+ return null;
+ });
+ }
+}