Skip to content

Commit

Permalink
Allow for having CDI on pure Jersey Client without Jersey Server (#4695)
Browse files Browse the repository at this point in the history
* Allow for having CDI on pure Jersey Client without Jersey Server jars dependencies

Signed-off-by: jansupol <[email protected]>
  • Loading branch information
jansupol committed Jan 26, 2021
1 parent 0056615 commit 7bf9e5d
Show file tree
Hide file tree
Showing 30 changed files with 1,362 additions and 343 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2018 Payara Foundation and/or its affiliates. All rights reserved.
* Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 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 All @@ -19,6 +19,11 @@

import org.glassfish.jersey.internal.BootstrapBag;
import org.glassfish.jersey.client.inject.ParameterUpdaterProvider;
import org.glassfish.jersey.internal.util.collection.LazyValue;
import org.glassfish.jersey.spi.ComponentProvider;

import javax.ws.rs.core.GenericType;
import java.util.Collection;

/**
* {@inheritDoc}
Expand All @@ -30,6 +35,7 @@
public class ClientBootstrapBag extends BootstrapBag {

private ParameterUpdaterProvider parameterUpdaterProvider;
private LazyValue<Collection<ComponentProvider>> componentProviders;

public ParameterUpdaterProvider getParameterUpdaterProvider() {
requireNonNull(parameterUpdaterProvider, ParameterUpdaterProvider.class);
Expand All @@ -39,4 +45,23 @@ public ParameterUpdaterProvider getParameterUpdaterProvider() {
public void setParameterUpdaterProvider(ParameterUpdaterProvider provider) {
this.parameterUpdaterProvider = provider;
}

/**
* Gets a lazy initialized collection of {@link ComponentProvider}s.
*
* @return A lazy initialized collection of {@code ComponentProvider}s.
*/
LazyValue<Collection<ComponentProvider>> getComponentProviders() {
requireNonNull(componentProviders, new GenericType<LazyValue<Collection<ComponentProvider>>>() {}.getType());
return componentProviders;
}

/**
* Sets a lazy initialized collection of {@link ComponentProvider}s.
*
* @param componentProviders A lazy initialized collection of {@code ComponentProvider}s.
*/
void setComponentProviders(LazyValue<Collection<ComponentProvider>> componentProviders) {
this.componentProviders = componentProviders;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* 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.internal.BootstrapBag;
import org.glassfish.jersey.internal.BootstrapConfigurator;
import org.glassfish.jersey.internal.ServiceConfigurationError;
import org.glassfish.jersey.internal.ServiceFinder;
import org.glassfish.jersey.internal.inject.InjectionManager;
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.model.internal.RankedComparator;
import org.glassfish.jersey.model.internal.RankedProvider;
import org.glassfish.jersey.spi.ComponentProvider;

import java.util.Collection;
import java.util.Comparator;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

class ClientComponentConfigurator implements BootstrapConfigurator {
private static final Comparator<RankedProvider<ComponentProvider>> RANKED_COMPARATOR =
new RankedComparator<>(RankedComparator.Order.DESCENDING);

@Override
public void init(InjectionManager injectionManager, BootstrapBag bootstrapBag) {
// There are situation in which ComponentProviders are not needed therefore their entire initialization is wrapped
// into a lazy block.
LazyValue<Collection<ComponentProvider>> componentProviders =
Values.lazy((Value<Collection<ComponentProvider>>) () -> getRankedComponentProviders().stream()
.map(RankedProvider::getProvider)
.peek(provider -> provider.initialize(injectionManager))
.collect(Collectors.toList()));
((ClientBootstrapBag) bootstrapBag).setComponentProviders(componentProviders);
}

@Override
public void postInit(InjectionManager injectionManager, BootstrapBag bootstrapBag) {
((ClientBootstrapBag) bootstrapBag).getComponentProviders().get().forEach(ComponentProvider::done);
}

private static Collection<RankedProvider<ComponentProvider>> getRankedComponentProviders() throws ServiceConfigurationError {
return StreamSupport.stream(ServiceFinder.find(ComponentProvider.class).spliterator(), false)
.map(RankedProvider::new)
.sorted(RANKED_COMPARATOR)
.collect(Collectors.toList());
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018 Payara Foundation and/or its affiliates.
*
* This program and the accompanying materials are made available under the
Expand Down Expand Up @@ -59,6 +59,7 @@
import org.glassfish.jersey.model.internal.ManagedObjectsFinalizer;
import org.glassfish.jersey.process.internal.RequestScope;
import org.glassfish.jersey.internal.inject.ParamConverterConfigurator;
import org.glassfish.jersey.spi.ComponentProvider;

/**
* Jersey externalized implementation of client-side JAX-RS {@link javax.ws.rs.core.Configurable
Expand Down Expand Up @@ -412,7 +413,7 @@ private ClientRuntime initRuntime() {
InjectionManager injectionManager = Injections.createInjectionManager();
injectionManager.register(new ClientBinder(runtimeCfgState.getProperties()));

BootstrapBag bootstrapBag = new ClientBootstrapBag();
final ClientBootstrapBag bootstrapBag = new ClientBootstrapBag();
bootstrapBag.setManagedObjectsFinalizer(new ManagedObjectsFinalizer(injectionManager));

final ClientMessageBodyFactory.MessageBodyWorkersConfigurator messageBodyWorkersConfigurator =
Expand All @@ -427,7 +428,8 @@ private ClientRuntime initRuntime() {
messageBodyWorkersConfigurator,
new ExceptionMapperFactory.ExceptionMappersConfigurator(),
new JaxrsProviders.ProvidersConfigurator(),
new AutoDiscoverableConfigurator(RuntimeType.CLIENT));
new AutoDiscoverableConfigurator(RuntimeType.CLIENT),
new ClientComponentConfigurator());
bootstrapConfigurators.forEach(configurator -> configurator.init(injectionManager, bootstrapBag));

// AutoDiscoverable.
Expand All @@ -442,7 +444,10 @@ private ClientRuntime initRuntime() {
runtimeCfgState.configureMetaProviders(injectionManager, bootstrapBag.getManagedObjectsFinalizer());

// Bind providers.
ProviderBinder.bindProviders(runtimeCfgState.getComponentBag(), RuntimeType.CLIENT, null, injectionManager);
final Collection<ComponentProvider> componentProviders = bootstrapBag.getComponentProviders().get();
ProviderBinder.bindProviders(
runtimeCfgState.getComponentBag(), RuntimeType.CLIENT, null, injectionManager, componentProviders
);

ClientExecutorProvidersConfigurator executorProvidersConfigurator =
new ClientExecutorProvidersConfigurator(runtimeCfgState.getComponentBag(),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2010, 2019 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2010, 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 @@ -34,6 +34,7 @@

import org.glassfish.jersey.model.ContractProvider;
import org.glassfish.jersey.model.internal.ComponentBag;
import org.glassfish.jersey.spi.ComponentProvider;

/**
* Class used for registration of the custom providers into injection manager.
Expand Down Expand Up @@ -189,22 +190,41 @@ protected void configure() {
* @param injectionManager injection manager the binder will use to bind the providers into.
*/
public static void bindProviders(final ComponentBag componentBag, final InjectionManager injectionManager) {
bindProviders(componentBag, null, Collections.emptySet(), injectionManager);
bindProviders(componentBag, null, null, injectionManager, null);
}

/**
* Bind all providers contained in {@code providerBag} (classes and instances) using injection manager. Configuration is
* also committed.
*
* @param componentBag bag of provider classes and instances.
* @param constrainedTo current runtime (client or server).
* @param registeredClasses classes which are manually registered by the user (not found by the classpath scanning).
* @param injectionManager injection manager the binder will use to bind the providers into.
* @param componentBag bag of provider classes and instances.
* @param constrainedTo current runtime (client or server).
* @param registeredClasses classes which are manually registered by the user (not found by the classpath scanning).
* @param injectionManager injection manager the binder will use to bind the providers into.
*/
@Deprecated // backward compatibility until JPMS
public static void bindProviders(ComponentBag componentBag,
RuntimeType constrainedTo,
Set<Class<?>> registeredClasses,
InjectionManager injectionManager) {
bindProviders(componentBag, constrainedTo, registeredClasses, injectionManager, null);
}

/**
* Bind all providers contained in {@code providerBag} (classes and instances) using injection manager. Configuration is
* also committed.
*
* @param componentBag bag of provider classes and instances.
* @param constrainedTo current runtime (client or server).
* @param registeredClasses classes which are manually registered by the user (not found by the classpath scanning).
* @param injectionManager injection manager the binder will use to bind the providers into.
* @param componentProviders available component providers capable of registering the classes
*/
public static void bindProviders(ComponentBag componentBag,
RuntimeType constrainedTo,
Set<Class<?>> registeredClasses,
InjectionManager injectionManager,
Collection<ComponentProvider> componentProviders) {
Predicate<ContractProvider> filter = ComponentBag.EXCLUDE_EMPTY
.and(ComponentBag.excludeMetaProviders(injectionManager));

Expand Down Expand Up @@ -234,7 +254,9 @@ public static void bindProviders(ComponentBag componentBag,
}
for (final Class<?> providerClass : classes) {
final ContractProvider model = componentBag.getModel(providerClass);
binderToRegister.addAll(createProviderBinders(providerClass, model));
if (componentProviders == null || !bindWithComponentProvider(providerClass, model, componentProviders)) {
binderToRegister.addAll(createProviderBinders(providerClass, model));
}
}

// Bind provider instances except for pure meta-providers and providers with empty contract models (e.g. resources)
Expand All @@ -252,6 +274,16 @@ public static void bindProviders(ComponentBag componentBag,
injectionManager.register(CompositeBinder.wrap(binderToRegister));
}

private static boolean bindWithComponentProvider(
Class<?> component, ContractProvider providerModel, Iterable<ComponentProvider> componentProviders) {
for (ComponentProvider provider : componentProviders) {
if (provider.bind(component, providerModel)) {
return true;
}
}
return false;
}

@SuppressWarnings("unchecked")
private static <T> Collection<Binder> createInstanceBinders(T instance) {
Function<Class, Binder> binderFunction = contract ->
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* 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.spi;

import java.util.Collections;
import java.util.Set;

import org.glassfish.jersey.internal.inject.InjectionManager;
import org.glassfish.jersey.model.ContractProvider;

/**
* Component provider interface to allow custom management of 3rd party
* components life-cycle and dependency injection.
* <p />
* An implementation (a component-provider) identifies itself by placing a provider-configuration
* file (if not already present), {@code org.glassfish.jersey.spi.ComponentProvider}
* in the resource directory <tt>META-INF/services</tt>, and adding the fully
* qualified service-provider-class of the implementation in the file.
*
* Jersey will not even try to inject component provider instances with Jersey artifacts.
* The SPI providers should be designed so that no dependency injection is needed at the bind time phase.
* @author Jakub Podlesak
*/
public interface ComponentProvider {


/**
* Initializes the component provider with a reference to a injection manager
* instance, which will get used in the application to manage individual components.
* Providers should keep a reference to the injection manager for later use.
* This method will be invoked prior to any bind method calls.
* The injection manager parameter will not be fully initialized at the time of invocation
* and should be used as a reference only.
*
* @param injectionManager an injection manager.
*/
void initialize(final InjectionManager injectionManager);

/**
* Jersey will invoke this method before binding of each component class internally
* during initialization of it's injection manager.
*
* If the component provider wants to bind the component class
* itself, it must do so and return true. In that case, Jersey will not
* bind the component and rely on the component provider in this regard.
*
* @param component a component (resource/provider) class.
* @param providerContracts provider contracts implemented by given component.
* @return true if the component class has been bound by the provider, false otherwise
*/
boolean bind(final Class<?> component, Set<Class<?>> providerContracts);

/**
* Jersey will invoke this method before binding of each component class internally
* during initialization of it's injection manager.
*
* If the component provider wants to bind the component class
* itself, it must do so and return true. In that case, Jersey will not
* bind the component and rely on the component provider in this regard.
*
* @param component a component (resource/provider) class.
* @param contractProvider optional registered {@link ContractProvider} of the component.
* @return true if the component class has been bound by the provider, false otherwise
*/
default boolean bind(final Class<?> component, ContractProvider contractProvider) {
final Set<Class<?>> contracts = contractProvider == null ? Collections.emptySet() : contractProvider.getContracts();
return bind(component, contracts);
}

/**
* Jersey will invoke this method after all component classes have been bound.
*
* If the component provider wants to do some actions after it has seen all component classes
* registered with the application, this is the right place for the corresponding code.
*/
void done();
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2018 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 @@ -189,9 +189,8 @@ private void bindProvidersAndResources(

private boolean bindWithComponentProvider(
Class<?> component, ContractProvider providerModel, Iterable<ComponentProvider> componentProviders) {
Set<Class<?>> contracts = providerModel == null ? Collections.emptySet() : providerModel.getContracts();
for (ComponentProvider provider : componentProviders) {
if (provider.bind(component, contracts)) {
if (provider.bind(component, providerModel)) {
return true;
}
}
Expand Down
Loading

0 comments on commit 7bf9e5d

Please sign in to comment.