Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow for having CDI on pure Jersey Client without Jersey Server #4695

Merged
merged 2 commits into from
Jan 26, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,24 @@ 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.
* @param componentProviders available component providers capable of registering the classes
*/
public static void bindProviders(ComponentBag componentBag,
RuntimeType constrainedTo,
Set<Class<?>> registeredClasses,
InjectionManager injectionManager) {
InjectionManager injectionManager,
Collection<ComponentProvider> componentProviders) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As this method is public it is possible that it is already referenced by user code. Shouldn't we duplicate this method with previous arguments?.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The general contract is that an internal package can be a subject of change. But yes, adding an extra method won't hurt.

Predicate<ContractProvider> filter = ComponentBag.EXCLUDE_EMPTY
.and(ComponentBag.excludeMetaProviders(injectionManager));

Expand Down Expand Up @@ -234,7 +237,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 +257,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