Skip to content

Commit

Permalink
Allow Feature and Dynamic feature as a JDK services (#4833)
Browse files Browse the repository at this point in the history
Signed-off-by: Maxim Nesen <maxim.nesen@oracle.com>
  • Loading branch information
senivam authored Aug 11, 2021
1 parent 68eae40 commit 1ec382f
Show file tree
Hide file tree
Showing 14 changed files with 409 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import org.glassfish.jersey.internal.BootstrapConfigurator;
import org.glassfish.jersey.internal.ContextResolverFactory;
import org.glassfish.jersey.internal.ExceptionMapperFactory;
import org.glassfish.jersey.internal.FeatureConfigurator;
import org.glassfish.jersey.internal.JaxrsProviders;
import org.glassfish.jersey.internal.ServiceFinder;
import org.glassfish.jersey.internal.inject.Bindings;
Expand All @@ -53,7 +54,6 @@
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.glassfish.jersey.message.internal.MessageBodyFactory;
import org.glassfish.jersey.model.internal.CommonConfig;
import org.glassfish.jersey.model.internal.ComponentBag;
import org.glassfish.jersey.model.internal.ManagedObjectsFinalizer;
Expand Down Expand Up @@ -429,7 +429,8 @@ private ClientRuntime initRuntime() {
new ExceptionMapperFactory.ExceptionMappersConfigurator(),
new JaxrsProviders.ProvidersConfigurator(),
new AutoDiscoverableConfigurator(RuntimeType.CLIENT),
new ClientComponentConfigurator());
new ClientComponentConfigurator(),
new FeatureConfigurator(RuntimeType.CLIENT));
bootstrapConfigurators.forEach(configurator -> configurator.init(injectionManager, bootstrapBag));

// AutoDiscoverable.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright (c) 2021 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.client;

import org.glassfish.jersey.CommonProperties;
import org.junit.Test;

import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.ClientRequestFilter;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.core.Feature;
import javax.ws.rs.core.FeatureContext;
import javax.ws.rs.core.Response;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;

public class JaxRsFeatureRegistrationClientTest {

private static final String REGISTERED_FEATURE_RESPONSE = "RegisteredFeature";

private static final ClientRequestFilter component =
requestContext -> requestContext
.abortWith(Response.status(400).entity(REGISTERED_FEATURE_RESPONSE).build());

public static class FeatureClientImpl implements Feature {

@Override
public boolean configure(FeatureContext context) {
if ("true".equals(context.getConfiguration().getProperty("runWithJaxRsClient"))) {
context.register(component);
}
return true;
}
}

@Test
public void featureRegistrationTest() {
final ClientConfig config = new ClientConfig().property("runWithJaxRsClient", "true");
final Client client = ClientBuilder.newClient(config);
final Invocation.Builder request = client.target("").request();

assertEquals(REGISTERED_FEATURE_RESPONSE, request.get().readEntity(String.class));

client.close();
}

@Test
public void featureNotRegistrationTest() {
final ClientConfig config = new ClientConfig()
.property(CommonProperties.JAXRS_SERVICE_LOADING_ENABLE, false);
final Client client = ClientBuilder.newClient(config);
final Invocation.Builder request = client.target("").request();

assertFalse(client.getConfiguration().isRegistered(FeatureClientImpl.class));

client.close();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
org.glassfish.jersey.client.JaxRsFeatureRegistrationClientTest$FeatureClientImpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* Copyright (c) 2021 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 org.glassfish.jersey.internal.AbstractServiceFinderConfigurator;
import org.glassfish.jersey.internal.BootstrapBag;
import org.glassfish.jersey.internal.ServiceFinder;
import org.glassfish.jersey.internal.spi.AutoDiscoverable;
import org.glassfish.jersey.internal.util.PropertiesHelper;

import javax.ws.rs.RuntimeType;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public abstract class AbstractFeatureConfigurator<T> extends AbstractServiceFinderConfigurator<T> {
/**
* Create a new configurator.
*
* @param contract contract of the service providers bound by this binder.
* @param runtimeType runtime (client or server) where the service finder binder is used.
*/
protected AbstractFeatureConfigurator(Class contract, RuntimeType runtimeType) {
super(contract, runtimeType);
}

/**
* Specification specific implementation which allows find classes by specified classloader
*
* @param applicationProperties map of properties to check if search is allowed
* @param loader specific classloader (must not be NULL)
* @return list of found classes
*/
protected List<Class<T>> loadImplementations(Map<String, Object> applicationProperties, ClassLoader loader) {
if (PropertiesHelper.isMetaInfServicesEnabled(applicationProperties, getRuntimeType())) {
return Stream.of(ServiceFinder.find(getContract(), loader, true).toClassArray())
.collect(Collectors.toList());
}
return Collections.emptyList();
}

/**
* Allows feature registration as part of autoDiscoverables list
*
* @param features list of features to be registered
* @param bootstrapBag place where features are being registered
*/
protected void registerFeatures(List<Class<T>> features,
BootstrapBag bootstrapBag) {
final List<AutoDiscoverable> autoDiscoverables = new ArrayList<>();

features.forEach(feature -> autoDiscoverables.add(registerClass(feature)));

bootstrapBag.getAutoDiscoverables().addAll(autoDiscoverables);
}

/**
* Register particular feature as an autoDiscoverable
*
* @param classToRegister class to be registered
* @param <T> type of class which is being registered
* @return initialized autoDiscoverable
*/
private static <T> AutoDiscoverable registerClass(Class<T> classToRegister) {
return context -> {
if (!context.getConfiguration().isRegistered(classToRegister)) {
context.register(classToRegister, AutoDiscoverable.DEFAULT_PRIORITY);
}
};
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2021 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
Expand Down Expand Up @@ -245,6 +245,16 @@ public final class CommonProperties {
*/
public static final String PROVIDER_DEFAULT_DISABLE = "jersey.config.disableDefaultProvider";


/**
* Allows API services loading. If absent or true JAXRS services loading is allowed.
* Services shall implement Feature or DynamicFeature interface and be listed as SPI
* in user's application.
*
* @since 2.35
*/
public static final String JAXRS_SERVICE_LOADING_ENABLE = "jakarta.ws.rs.loadServices";

/**
* Prevent instantiation.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2019 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2017, 2021 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
Expand Down Expand Up @@ -65,4 +65,22 @@ protected List<Class<T>> loadImplementations(Map<String, Object> applicationProp
}
return Collections.emptyList();
}

/**
* Mainly aimed to provide runtime type to abstract classes which extends this finder
*
* @return runtime type
*/
protected RuntimeType getRuntimeType() {
return runtimeType;
}

/**
* Mainly aimed to provide contract class to abstract classes which extends this finder
*
* @return contract class
*/
protected Class<T> getContract() {
return contract;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright (c) 2021 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.internal;

import org.glassfish.jersey.AbstractFeatureConfigurator;
import org.glassfish.jersey.internal.inject.InjectionManager;
import org.glassfish.jersey.internal.util.PropertiesHelper;

import javax.ws.rs.RuntimeType;
import javax.ws.rs.container.DynamicFeature;
import java.util.List;
import java.util.Map;

/**
* Registers JAX-RS {@link DynamicFeature} which are listed as SPIs for registration.
* Also checks if JAX-RS service loading is enabled by the jakarta.ws.rs.loadServices property. In order for
* registration to proceed the property shall be true (or null).
*
* Configurator is used only at Server side.
*
* This configurator's instance shall be after {@link AutoDiscoverableConfigurator}
* in the list of configurators due to same list of {@link org.glassfish.jersey.internal.spi.AutoDiscoverable}
* used in the {@link BootstrapBag} to register discovered features.
*/
public class DynamicFeatureConfigurator extends AbstractFeatureConfigurator<DynamicFeature> {

/**
* Create a new configurator.
*
* Must be used at server side only (takes no effect as a client).
*/
public DynamicFeatureConfigurator() {
super(DynamicFeature.class, RuntimeType.SERVER);
}

@Override
public void init(InjectionManager injectionManager, BootstrapBag bootstrapBag) {
final Map<String, Object> properties = bootstrapBag.getConfiguration().getProperties();
if (PropertiesHelper.isJaxRsServiceLoadingEnabled(properties)) {
final List<Class<DynamicFeature>> dynamicFeatures = loadImplementations(properties);
dynamicFeatures.addAll(loadImplementations(properties, DynamicFeature.class.getClassLoader()));

registerFeatures(dynamicFeatures, bootstrapBag);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright (c) 2021 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.internal;

import org.glassfish.jersey.AbstractFeatureConfigurator;
import org.glassfish.jersey.internal.inject.InjectionManager;
import org.glassfish.jersey.internal.util.PropertiesHelper;

import javax.ws.rs.RuntimeType;
import javax.ws.rs.core.Feature;
import java.util.List;
import java.util.Map;

/**
* Registers JAX-RS {@link Feature} which are listed as SPIs for registration.
* Also checks if JAX-RS service loading is enabled by the jakarta.ws.rs.loadServices property. In order for
* registration to proceed the property shall be true (or null).
*
* This configurator's instance shall be the last (or at least after {@link AutoDiscoverableConfigurator})
* in the list of configurators due to same list of {@link org.glassfish.jersey.internal.spi.AutoDiscoverable}
* used in the {@link BootstrapBag} to register discovered features.
*/
public class FeatureConfigurator extends AbstractFeatureConfigurator<Feature> {

public FeatureConfigurator(RuntimeType runtimeType) {
super(Feature.class, runtimeType);
}

@Override
public void init(InjectionManager injectionManager, BootstrapBag bootstrapBag) {
final Map<String, Object> properties = bootstrapBag.getConfiguration().getProperties();
if (PropertiesHelper.isJaxRsServiceLoadingEnabled(properties)) {
final List<Class<Feature>> features = loadImplementations(properties);
features.addAll(loadImplementations(properties, Feature.class.getClassLoader()));

registerFeatures(features, bootstrapBag);
}
}
}
Loading

0 comments on commit 1ec382f

Please sign in to comment.