Skip to content

Commit

Permalink
Support for jakarta.inject.Provider
Browse files Browse the repository at this point in the history
Adds support for jakarta providers. These can be used interchangeably
with their native and javax.inject counterparts.
  • Loading branch information
hgschmie committed May 17, 2022
1 parent cc74577 commit 56de719
Show file tree
Hide file tree
Showing 26 changed files with 1,066 additions and 8 deletions.
2 changes: 1 addition & 1 deletion core/src/com/google/inject/Provider.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
* @param <T> the type of object this provides
* @author crazybob@google.com (Bob Lee)
*/
public interface Provider<T> extends javax.inject.Provider<T> {
public interface Provider<T> extends javax.inject.Provider<T>, jakarta.inject.Provider<T> {

/**
* Provides an instance of {@code T}.
Expand Down
21 changes: 21 additions & 0 deletions core/src/com/google/inject/binder/LinkedBindingBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,27 @@ ScopedBindingBuilder toProvider(
/** See the EDSL examples at {@link com.google.inject.Binder}. */
ScopedBindingBuilder toProvider(Key<? extends javax.inject.Provider<? extends T>> providerKey);

/** See the EDSL examples at {@link com.google.inject.Binder}. This method is called <code>toJeeProvider</code> to avoid clashes with {@link #toProvider(Provider)}. */
default ScopedBindingBuilder toJeeProvider(jakarta.inject.Provider<? extends T> provider) {
throw new UnsupportedOperationException("not implemented");
}

/** See the EDSL examples at {@link com.google.inject.Binder}. This method is called <code>toJeeProvider</code> to avoid clashes with {@link #toProvider(Class)}. */
default ScopedBindingBuilder toJeeProvider(Class<? extends jakarta.inject.Provider<? extends T>> providerType) {
return toJeeProvider(Key.get(providerType));
}

/** See the EDSL examples at {@link com.google.inject.Binder}. This method is called <code>toJeeProvider</code> to avoid clashes with {@link #toProvider(TypeLiteral)}. */
default ScopedBindingBuilder toJeeProvider(
TypeLiteral<? extends jakarta.inject.Provider<? extends T>> providerType) {
return toJeeProvider(Key.get(providerType));
}

/** See the EDSL examples at {@link com.google.inject.Binder}. This method is called <code>toJeeProvider</code> to avoid clashes with {@link #toProvider(Key)}. */
default ScopedBindingBuilder toJeeProvider(Key<? extends jakarta.inject.Provider<? extends T>> providerKey) {
throw new UnsupportedOperationException("not implemented");
}

/**
* See the EDSL examples at {@link com.google.inject.Binder}.
*
Expand Down
34 changes: 34 additions & 0 deletions core/src/com/google/inject/internal/BindingBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,40 @@ public BindingBuilder<T> toProvider(
return this;
}

@Override
public BindingBuilder<T> toJeeProvider(jakarta.inject.Provider<? extends T> provider) {
checkNotNull(provider, "provider");
checkNotTargetted();

// lookup the injection points, adding any errors to the binder's errors list
Set<InjectionPoint> injectionPoints;
try {
injectionPoints = InjectionPoint.forInstanceMethodsAndFields(provider.getClass());
} catch (ConfigurationException e) {
copyErrorsToBinder(e);
injectionPoints = e.getPartialValue();
}

BindingImpl<T> base = getBinding();
setBinding(
new JeeProviderInstanceBindingImpl<T>(
base.getSource(), base.getKey(), base.getScoping(), injectionPoints, provider));
return this;
}

@Override
public BindingBuilder<T> toJeeProvider(
Key<? extends jakarta.inject.Provider<? extends T>> providerKey) {
checkNotNull(providerKey, "providerKey");
checkNotTargetted();

BindingImpl<T> base = getBinding();
setBinding(
new LinkedJeeProviderBindingImpl<T>(
base.getSource(), base.getKey(), base.getScoping(), providerKey));
return this;
}

@Override
public <S extends T> ScopedBindingBuilder toConstructor(Constructor<S> constructor) {
return toConstructor(constructor, TypeLiteral.get(constructor.getDeclaringClass()));
Expand Down
67 changes: 64 additions & 3 deletions core/src/com/google/inject/internal/BindingProcessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
import com.google.inject.spi.ExposedBinding;
import com.google.inject.spi.InjectionPoint;
import com.google.inject.spi.InstanceBinding;
import com.google.inject.spi.JeeProviderInstanceBinding;
import com.google.inject.spi.JeeProviderKeyBinding;
import com.google.inject.spi.LinkedKeyBinding;
import com.google.inject.spi.PrivateElements;
import com.google.inject.spi.ProviderBinding;
Expand Down Expand Up @@ -53,9 +55,12 @@ final class BindingProcessor extends AbstractBindingProcessor {
public <T> Boolean visit(Binding<T> command) {
Class<?> rawType = command.getKey().getTypeLiteral().getRawType();
if (Void.class.equals(rawType)) {
if (command instanceof ProviderInstanceBinding
&& ((ProviderInstanceBinding) command).getUserSuppliedProvider()
instanceof ProviderMethod) {
if ((command instanceof ProviderInstanceBinding
&& ((ProviderInstanceBinding) command).getUserSuppliedProvider()
instanceof ProviderMethod)
|| (command instanceof JeeProviderInstanceBinding
&& ((JeeProviderInstanceBinding) command).getUserSuppliedProvider()
instanceof ProviderMethod)) {
errors.voidProviderMethod();
} else {
errors.missingConstantValues();
Expand Down Expand Up @@ -141,6 +146,35 @@ public Boolean visit(ProviderInstanceBinding<? extends T> binding) {
return true;
}

@Override
public Boolean visit(JeeProviderInstanceBinding<? extends T> binding) {
prepareBinding();
jakarta.inject.Provider<? extends T> provider = binding.getUserSuppliedProvider();
if (provider instanceof InternalProviderInstanceBindingImpl.Factory) {
@SuppressWarnings("unchecked")
InternalProviderInstanceBindingImpl.Factory<T> asProviderMethod =
(InternalProviderInstanceBindingImpl.Factory<T>) provider;
return visitInternalProviderInstanceBindingFactory(asProviderMethod);
}
Set<InjectionPoint> injectionPoints = binding.getInjectionPoints();
Initializable<? extends jakarta.inject.Provider<? extends T>> initializable =
initializer.<jakarta.inject.Provider<? extends T>>requestInjection(
injector, provider, null, source, injectionPoints);
// always visited with Binding<T>
@SuppressWarnings("unchecked")
InternalFactory<T> factory =
new InternalFactoryToJeeInitializableAdapter<T>(
initializable,
source,
injector.provisionListenerStore.get((JeeProviderInstanceBinding<T>) binding));
InternalFactory<? extends T> scopedFactory =
Scoping.scope(key, injector, factory, source, scoping);
putBinding(
new JeeProviderInstanceBindingImpl<T>(
injector, key, source, scopedFactory, scoping, provider, injectionPoints));
return true;
}

@Override
public Boolean visit(ProviderKeyBinding<? extends T> binding) {
prepareBinding();
Expand Down Expand Up @@ -169,6 +203,33 @@ public Boolean visit(ProviderKeyBinding<? extends T> binding) {
}

@Override
public Boolean visit(JeeProviderKeyBinding<? extends T> binding) {
prepareBinding();
Key<? extends jakarta.inject.Provider<? extends T>> providerKey =
binding.getProviderKey();
// always visited with Binding<T>
@SuppressWarnings("unchecked")
BoundJeeProviderFactory<T> boundProviderFactory =
new BoundJeeProviderFactory<>(
injector,
providerKey,
source,
injector.provisionListenerStore.get((JeeProviderKeyBinding<T>) binding));
processedBindingData.addCreationListener(boundProviderFactory);
InternalFactory<? extends T> scopedFactory =
Scoping.scope(
key,
injector,
boundProviderFactory,
source,
scoping);
putBinding(
new LinkedJeeProviderBindingImpl<>(
injector, key, source, scopedFactory, scoping, providerKey));
return true;
}

@Override
public Boolean visit(LinkedKeyBinding<? extends T> binding) {
prepareBinding();
Key<? extends T> linkedKey = binding.getLinkedKey();
Expand Down
85 changes: 85 additions & 0 deletions core/src/com/google/inject/internal/BoundJeeProviderFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* Copyright (C) 2022 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.inject.internal;

import com.google.inject.Key;
import com.google.inject.internal.InjectorImpl.JitLimitation;
import com.google.inject.spi.Dependency;
import jakarta.inject.Provider;

/**
* Delegates to a custom factory which is also bound in the injector.
*/
final class BoundJeeProviderFactory<T> extends JeeProviderInternalFactory<T> implements
CreationListener {

final Key<? extends Provider<? extends T>> providerKey;
private final ProvisionListenerStackCallback<T> provisionCallback;
private final InjectorImpl injector;
private InternalFactory<? extends Provider<? extends T>> providerFactory;

BoundJeeProviderFactory(
InjectorImpl injector,
Key<? extends Provider<? extends T>> providerKey,
Object source,
ProvisionListenerStackCallback<T> provisionCallback) {
super(source);
this.provisionCallback = provisionCallback;
this.injector = injector;
this.providerKey = providerKey;
}

@Override
public void notify(Errors errors) {
try {
providerFactory =
injector.getInternalFactory(
providerKey, errors.withSource(source), JitLimitation.NEW_OR_EXISTING_JIT);
} catch (ErrorsException e) {
errors.merge(e.getErrors());
}
}

@Override
public T get(InternalContext context, Dependency<?> dependency, boolean linked)
throws InternalProvisionException {
try {
Provider<? extends T> provider = providerFactory.get(context, dependency, true);
return circularGet(provider, context, dependency, provisionCallback);
} catch (InternalProvisionException ipe) {
throw ipe.addSource(providerKey);
}
}

@Override
protected T provision(
Provider<? extends T> provider,
Dependency<?> dependency,
ConstructionContext<T> constructionContext)
throws InternalProvisionException {
try {
return super.provision(provider, dependency, constructionContext);
} catch (RuntimeException userException) {
throw InternalProvisionException.errorInProvider(userException);
}
}

@Override
public String toString() {
return providerKey.toString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright (C) 2022 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.inject.internal;

import static com.google.common.base.Preconditions.checkNotNull;

import com.google.inject.spi.Dependency;
import com.google.inject.spi.ProviderInstanceBinding;

/**
* Adapts {@link ProviderInstanceBinding} providers, ensuring circular proxies fail (or proxy)
* properly.
*/
final class InternalFactoryToJeeInitializableAdapter<T> extends JeeProviderInternalFactory<T> {

private final ProvisionListenerStackCallback<T> provisionCallback;
private final Initializable<? extends jakarta.inject.Provider<? extends T>> initializable;

public InternalFactoryToJeeInitializableAdapter(
Initializable<? extends jakarta.inject.Provider<? extends T>> initializable,
Object source,
ProvisionListenerStackCallback<T> provisionCallback) {
super(source);
this.provisionCallback = provisionCallback;
this.initializable = checkNotNull(initializable, "provider");
}

@Override
public T get(InternalContext context, Dependency<?> dependency, boolean linked)
throws InternalProvisionException {
return circularGet(initializable.get(), context, dependency, provisionCallback);
}

@Override
protected T provision(
jakarta.inject.Provider<? extends T> provider,
Dependency<?> dependency,
ConstructionContext<T> constructionContext)
throws InternalProvisionException {
try {
return super.provision(provider, dependency, constructionContext);
} catch (RuntimeException userException) {
throw InternalProvisionException.errorInProvider(userException).addSource(source);
}
}

@Override
public String toString() {
return initializable.toString();
}
}
Loading

0 comments on commit 56de719

Please sign in to comment.