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

4.x: Inject fixes #9515

Merged
merged 5 commits into from
Nov 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
Expand Up @@ -18,6 +18,7 @@

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
Expand Down Expand Up @@ -192,6 +193,11 @@ static void type(TypeName.BuilderBase<?, ?> builder, Type type) {
}
return;
}
if (reflectGenericType instanceof WildcardType) {
builder.className("?");
builder.wildcard(true);
return;
}

throw new IllegalArgumentException("We can only create a type from a class, GenericType, or a ParameterizedType,"
+ " but got: " + reflectGenericType.getClass().getName());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,13 @@ public final class TypeNames {
* Type name for {@link java.lang.annotation.Target}.
*/
public static final TypeName TARGET = TypeName.create(Target.class);
/**
* Wildcard type name, represented in code by {@code ?}.
*/
public static final TypeName WILDCARD = TypeName.builder()
.className("?")
.wildcard(true)
.build();

/*
Primitive types and their boxed counterparts
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -210,14 +210,21 @@ public void addContracts(Set<ResolvedType> contractSet,

if (!resolvedType.type().typeArguments().isEmpty()) {
// we also need to add a contract for the type it implements
// i.e. if this is Circle<Green>, we may want to add Circle<Color> as well
// i.e. if this is Circle<Green>, we may want to add Circle<Color> as well, and Circle<?>
typeInfoFactory.apply(withGenerics.genericTypeName())
.ifPresent(declaration -> {
TypeName tn = declaration.typeName();
for (int i = 0; i < withGenerics.typeArguments().size(); i++) {
TypeName declared = tn.typeArguments().get(i);
if (declared.generic()) {
// this is not ideal (this could be T extends Circle)
// Circle<?>
contractSet.add(ResolvedType.create(
TypeName.builder()
.from(withGenerics)
.typeArguments(List.of(TypeNames.WILDCARD))
.build()));

// Circle<Color>
var asString = declared.toString();
int index = asString.indexOf(" extends ");
if (index != -1) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,8 @@ default boolean matches(InjectServiceInfo serviceInfo) {
boolean matches = matches(serviceInfo.serviceType(), this.serviceType());
if (matches && this.serviceType().isEmpty()) {
matches = serviceInfo.contracts().containsAll(this.contracts())
|| this.contracts().contains(ResolvedType.create(serviceInfo.serviceType()));
|| this.contracts().contains(ResolvedType.create(serviceInfo.serviceType()))
|| serviceInfo.factoryContracts().containsAll(this.contracts());
}
return matches
&& matchesProviderTypes(factoryTypes(), serviceInfo.factoryType())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,18 @@ private Activators() {

@SuppressWarnings("unchecked")
static <T> Activator<T> create(ServiceProvider<T> provider, T instance) {
if (instance instanceof Supplier<?> supplier) {
return new FixedSupplierActivator<>(provider, (Supplier<T>) supplier);
} else {
return new Activators.FixedActivator<>(provider, instance);
}
return switch (provider.descriptor().factoryType()) {
case NONE, SERVICE, SUPPLIER -> {
if (instance instanceof Supplier<?> supplier) {
yield new FixedSupplierActivator<>(provider, (Supplier<T>) supplier);
} else {
yield new Activators.FixedActivator<>(provider, instance);
}
}
case SERVICES -> new Activators.FixedServicesFactoryActivator<>(provider, (Injection.ServicesFactory<T>) instance);
case INJECTION_POINT -> new Activators.FixedIpFactoryActivator<>(provider, (InjectionPointFactory<T>) instance);
case QUALIFIED -> new Activators.FixedQualifiedFactoryActivator<>(provider, (QualifiedFactory<T, ?>) instance);
};
}

static <T> Supplier<Activator<T>> create(InjectServiceRegistryImpl registry, ServiceProvider<T> provider) {
Expand All @@ -93,11 +100,11 @@ static <T> Supplier<Activator<T>> create(InjectServiceRegistryImpl registry, Ser
yield () -> new ActivatorsPerLookup.SingleServiceActivator<>(provider);
}
case SUPPLIER -> () -> new ActivatorsPerLookup.SupplierActivator<>(provider);
case SERVICES -> () -> new ActivatorsPerLookup.ServicesProviderActivator<>(provider);
case INJECTION_POINT -> () -> new ActivatorsPerLookup.IpProviderActivator<>(provider);
case SERVICES -> () -> new ActivatorsPerLookup.ServicesFactoryActivator<>(provider);
case INJECTION_POINT -> () -> new ActivatorsPerLookup.IpFactoryActivator<>(provider);
case QUALIFIED -> () ->
new ActivatorsPerLookup.QualifiedProviderActivator<>(provider,
(QualifiedFactoryDescriptor) descriptor);
new ActivatorsPerLookup.QualifiedFactoryActivator<>(provider,
(QualifiedFactoryDescriptor) descriptor);
};
} else {
return switch (descriptor.factoryType()) {
Expand All @@ -109,11 +116,11 @@ static <T> Supplier<Activator<T>> create(InjectServiceRegistryImpl registry, Ser
yield () -> new Activators.SingleServiceActivator<>(provider);
}
case SUPPLIER -> () -> new Activators.SupplierActivator<>(provider);
case SERVICES -> () -> new Activators.ServicesProviderActivator<>(provider);
case INJECTION_POINT -> () -> new Activators.IpProviderActivator<>(provider);
case SERVICES -> () -> new ServicesFactoryActivator<>(provider);
case INJECTION_POINT -> () -> new IpFactoryActivator<>(provider);
case QUALIFIED -> () ->
new Activators.QualifiedProviderActivator<>(provider,
(QualifiedFactoryDescriptor) descriptor);
new QualifiedFactoryActivator<>(provider,
(QualifiedFactoryDescriptor) descriptor);
};
}
}
Expand Down Expand Up @@ -283,8 +290,17 @@ protected boolean requestedProvider(Lookup lookup, FactoryType providerType) {
if (lookup.factoryTypes().contains(providerType)) {
return true;
}
if (lookup.contracts().size() == 1 && lookup.contracts().contains(ResolvedType.create(descriptor().serviceType()))) {
return true;
if (lookup.contracts().size() == 1) {
ResolvedType requestedContract = lookup.contracts().iterator().next();
if (requestedContract.equals(ResolvedType.create(descriptor().serviceType()))) {
// requested actual service
return true;
}
if (descriptor().factoryContracts().contains(requestedContract)
&& !descriptor().contracts().contains(requestedContract)) {
// requested a contract satisfied by the factory only
return true;
}
}
if (lookup.serviceType().isPresent() && lookup.serviceType().get().equals(descriptor().serviceType())) {
return true;
Expand Down Expand Up @@ -396,6 +412,31 @@ protected Optional<List<QualifiedInstance<T>>> targetInstances() {

}

static class FixedIpFactoryActivator<T> extends IpFactoryActivator<T> {

FixedIpFactoryActivator(ServiceProvider<T> provider,
InjectionPointFactory<T> instance) {
super(provider);
serviceInstance = InstanceHolder.create(instance);
}
}

static class FixedServicesFactoryActivator<T> extends ServicesFactoryActivator<T> {
FixedServicesFactoryActivator(ServiceProvider<T> provider,
Injection.ServicesFactory<T> factory) {
super(provider);
serviceInstance = InstanceHolder.create(factory);
}
}

static class FixedQualifiedFactoryActivator<T> extends QualifiedFactoryActivator<T> {
FixedQualifiedFactoryActivator(ServiceProvider<T> provider,
Injection.QualifiedFactory<T, ?> factory) {
super(provider, (QualifiedFactoryDescriptor) provider.descriptor());
serviceInstance = InstanceHolder.create(factory);
}
}

/**
* {@code MyService implements Contract}.
* Created for a service within each scope.
Expand Down Expand Up @@ -423,7 +464,10 @@ protected void construct(ActivationResult.Builder response) {
}
try {
lock.lock();
this.serviceInstance = InstanceHolder.create(provider, provider.injectionPlan());
if (serviceInstance == null) {
// it may have been set explicitly when creating registry
this.serviceInstance = InstanceHolder.create(provider, provider.injectionPlan());
}
this.serviceInstance.construct();
} finally {
lock.unlock();
Expand Down Expand Up @@ -497,14 +541,14 @@ protected void setTargetInstances() {
/**
* {@code MyService implements QualifiedProvider}.
*/
static class QualifiedProviderActivator<T> extends SingleServiceActivator<T> {
static class QualifiedFactoryActivator<T> extends SingleServiceActivator<T> {
static final GenericType<?> OBJECT_GENERIC_TYPE = GenericType.create(Object.class);

private final TypeName supportedQualifier;
private final Set<ResolvedType> supportedContracts;
private final boolean anyMatch;

QualifiedProviderActivator(ServiceProvider<T> provider, QualifiedFactoryDescriptor qpd) {
QualifiedFactoryActivator(ServiceProvider<T> provider, QualifiedFactoryDescriptor qpd) {
super(provider);
this.supportedQualifier = qpd.qualifierType();
this.supportedContracts = provider.descriptor()
Expand Down Expand Up @@ -560,8 +604,8 @@ private List<QualifiedInstance<T>> targetInstances(Lookup lookup, Qualifier qual
/**
* {@code MyService implements InjectionPointProvider}.
*/
static class IpProviderActivator<T> extends SingleServiceActivator<T> {
IpProviderActivator(ServiceProvider<T> provider) {
static class IpFactoryActivator<T> extends SingleServiceActivator<T> {
IpFactoryActivator(ServiceProvider<T> provider) {
super(provider);
}

Expand Down Expand Up @@ -596,8 +640,8 @@ protected Optional<List<QualifiedInstance<T>>> targetInstances(Lookup lookup) {
/**
* {@code MyService implements ServicesProvider}.
*/
static class ServicesProviderActivator<T> extends SingleServiceActivator<T> {
ServicesProviderActivator(ServiceProvider<T> provider) {
static class ServicesFactoryActivator<T> extends SingleServiceActivator<T> {
ServicesFactoryActivator(ServiceProvider<T> provider) {
super(provider);
}

Expand Down Expand Up @@ -806,48 +850,91 @@ static <T> QualifiedServiceInstance<T> create(ServiceProvider<T> provider, Servi
}
}

static class InstanceHolder<T> {
interface InstanceHolder<T> {
static <T> InstanceHolder<T> create(ServiceProvider<T> serviceProvider, Map<Dependency, IpPlan<?>> injectionPlan) {
// the same instance is returned for the lifetime of the service provider
return new InstanceHolderImpl<>(InjectionContext.create(injectionPlan),
serviceProvider.interceptionMetadata(),
serviceProvider.descriptor());
}

// we use the instance holder to hold either the actual instance,
// or the factory; this is a place for improvement
@SuppressWarnings("unchecked")
static <T> InstanceHolder<T> create(Object instance) {
return new FixedInstanceHolder<>((T) instance);
}

T get();

default void construct() {
}

default void inject() {
}

default void postConstruct() {
}

default void preDestroy() {
}
}

private static class FixedInstanceHolder<T> implements InstanceHolder<T> {
private final T instance;

private FixedInstanceHolder(T instance) {
this.instance = instance;
}

@Override
public T get() {
return instance;
}
}

private static class InstanceHolderImpl<T> implements InstanceHolder<T> {
private final DependencyContext ctx;
private final InterceptionMetadata interceptionMetadata;
private final InjectServiceDescriptor<T> source;

private volatile T instance;

private InstanceHolder(DependencyContext ctx,
private InstanceHolderImpl(DependencyContext ctx,
InterceptionMetadata interceptionMetadata,
InjectServiceDescriptor<T> source) {
this.ctx = ctx;
this.interceptionMetadata = interceptionMetadata;
this.source = source;
}

static <T> InstanceHolder<T> create(ServiceProvider<T> serviceProvider, Map<Dependency, IpPlan<?>> injectionPlan) {
// the same instance is returned for the lifetime of the service provider
return new InstanceHolder<>(InjectionContext.create(injectionPlan),
serviceProvider.interceptionMetadata(),
serviceProvider.descriptor());
}

T get() {

@Override
public T get() {
return instance;
}

@SuppressWarnings("unchecked")
void construct() {
@Override
public void construct() {
instance = (T) source.instantiate(ctx, interceptionMetadata);
}

void inject() {
@Override
public void inject() {
// using linked set, so we can see in debugging what was injected first
Set<String> injected = new LinkedHashSet<>();
source.inject(ctx, interceptionMetadata, injected, instance);
}

void postConstruct() {
@Override
public void postConstruct() {
source.postConstruct(instance);
}

void preDestroy() {
@Override
public void preDestroy() {
source.preDestroy(instance);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
import io.helidon.service.registry.ServiceInfo;
import io.helidon.service.registry.ServiceRegistryException;

import static io.helidon.service.inject.Activators.QualifiedProviderActivator.OBJECT_GENERIC_TYPE;
import static io.helidon.service.inject.Activators.QualifiedFactoryActivator.OBJECT_GENERIC_TYPE;
import static java.util.function.Predicate.not;

/*
Expand Down Expand Up @@ -138,12 +138,12 @@ protected Optional<List<QualifiedInstance<T>>> targetInstances() {
/**
* {@code MyService implements QualifiedProvider}.
*/
static class QualifiedProviderActivator<T> extends SingleServiceActivator<T> {
static class QualifiedFactoryActivator<T> extends SingleServiceActivator<T> {
private final TypeName supportedQualifier;
private final Set<ResolvedType> supportedContracts;
private final boolean anyMatch;

QualifiedProviderActivator(ServiceProvider<T> provider, GeneratedInjectService.QualifiedFactoryDescriptor qpd) {
QualifiedFactoryActivator(ServiceProvider<T> provider, GeneratedInjectService.QualifiedFactoryDescriptor qpd) {
super(provider);
this.supportedQualifier = qpd.qualifierType();
this.supportedContracts = provider.descriptor().contracts()
Expand Down Expand Up @@ -193,8 +193,8 @@ private List<QualifiedInstance<T>> targetInstances(Lookup lookup, Qualifier qual
/**
* {@code MyService implements InjectionPointProvider}.
*/
static class IpProviderActivator<T> extends SingleServiceActivator<T> {
IpProviderActivator(ServiceProvider<T> provider) {
static class IpFactoryActivator<T> extends SingleServiceActivator<T> {
IpFactoryActivator(ServiceProvider<T> provider) {
super(provider);
}

Expand Down Expand Up @@ -224,8 +224,8 @@ protected Optional<List<QualifiedInstance<T>>> targetInstances(Lookup lookup) {
/**
* {@code MyService implements ServicesProvider}.
*/
static class ServicesProviderActivator<T> extends SingleServiceActivator<T> {
ServicesProviderActivator(ServiceProvider<T> provider) {
static class ServicesFactoryActivator<T> extends SingleServiceActivator<T> {
ServicesFactoryActivator(ServiceProvider<T> provider) {
super(provider);
}

Expand Down
Loading