update to 4.4.2

This commit is contained in:
Jörg Prante 2019-12-09 15:32:54 +01:00
parent 70a8da3a4e
commit 79970ae966
221 changed files with 8310 additions and 10659 deletions

View file

@ -1,7 +1,3 @@
sudo: false
language: java
jdk:
- oraclejdk8
cache:
directories:
- $HOME/.m2
- openjdk11

View file

@ -17,7 +17,7 @@ original [Google Guice Core Library](https://github.com/google/guice):
- code is conforming to JRE `compact1` profile
- the extra AOP stuff was removed (aopalliance/cglib), no intercepting any more
- removed all logging, uses of java.util.logger.* stuff and logger default binding
- removed all java.io.Serializable dependent stuff
- removed all java.io.Serializable dependent stuff. Note: everything which relies to be serializable was removed.
- reformatted source by IntelliJ and cleaned up source of @author and @since tags
- target of compilation is Java 8, no support for Java 6/7
- introduced Gradle as build system

View file

@ -1,50 +1,43 @@
plugins {
id "org.sonarqube" version "2.2"
id "org.sonarqube" version "2.8"
id "io.codearte.nexus-staging" version "0.21.1"
}
apply plugin: 'java'
apply plugin: 'maven'
apply plugin: 'signing'
apply plugin: 'findbugs'
apply plugin: 'pmd'
apply plugin: 'checkstyle'
apply plugin: "jacoco"
repositories {
mavenCentral()
}
configurations {
wagon
}
dependencies {
compile "javax.inject:javax.inject:1"
compile 'org.xbib:jsr-305:1.0.0'
compile "com.google.guava:guava:21.0"
testCompile "junit:junit:4.12"
testCompile "org.apache.logging.log4j:log4j-slf4j-impl:2.7"
testCompile "org.apache.logging.log4j:log4j-core:2.7"
testCompile "javax.inject:javax.inject-tck:1"
testCompile "com.google.guava:guava-testlib:21.0"
wagon 'org.apache.maven.wagon:wagon-ssh-external:2.10'
compile "org.xbib:javax-inject:${project.property('javax-inject.version')}"
compile "org.xbib:guava:${project.property('guava.version')}"
testCompile "org.junit.jupiter:junit-jupiter-api:${project.property('junit.version')}"
testCompile "org.junit.jupiter:junit-jupiter-params:${project.property('junit.version')}"
testCompile "org.junit.jupiter:junit-jupiter-engine:${project.property('junit.version')}"
testCompile "org.junit.vintage:junit-vintage-engine:${project.property('junit.version')}"
testCompile "junit:junit:${project.property('junit4.version')}"
// Helper for com.google.common.testing.GcFinalization, com.google.common.testing.EqualsTester
testCompile "com.google.guava:guava-testlib:28.1-jre"
}
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
compileJava {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
compileTestJava {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
[compileJava, compileTestJava]*.options*.encoding = 'UTF-8'
tasks.withType(JavaCompile) {
options.compilerArgs << "-Xlint:all" << "-profile" << "compact1"
options.compilerArgs << "-Xlint:all,-fallthrough"
}
test {
exclude '*$*'
exclude '**/ErrorHandlingTest*'
exclude '**/OSGiContainerTest*'
exclude '**/ScopesTest*'
exclude '**/TypeConversionTest*'
useJUnitPlatform()
testLogging {
showStandardStreams = false
exceptionFormat = 'full'
@ -55,18 +48,145 @@ task sourcesJar(type: Jar, dependsOn: classes) {
classifier 'sources'
from sourceSets.main.allSource
}
task javadocJar(type: Jar, dependsOn: javadoc) {
classifier 'javadoc'
}
artifacts {
archives sourcesJar, javadocJar
}
if (project.hasProperty('signing.keyId')) {
signing {
sign configurations.archives
ext {
user = 'xbib'
projectName = 'guice'
projectDescription = 'Guice for Java'
scmUrl = 'https://github.com/xbib/guice'
scmConnection = 'scm:git:git://github.com/xbib/guice.git'
scmDeveloperConnection = 'scm:git:git://github.com/xbib/guice.git'
}
task sonatypeUpload(type: Upload) {
configuration = configurations.archives
uploadDescriptor = true
repositories {
if (project.hasProperty('ossrhUsername')) {
mavenDeployer {
beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
repository(url: uri(ossrhReleaseUrl)) {
authentication(userName: ossrhUsername, password: ossrhPassword)
}
snapshotRepository(url: uri(ossrhSnapshotUrl)) {
authentication(userName: ossrhUsername, password: ossrhPassword)
}
pom.project {
groupId project.group
artifactId project.name
version project.version
name project.name
description projectDescription
packaging 'jar'
inceptionYear '2016'
url scmUrl
organization {
name 'xbib'
url 'http://xbib.org'
}
developers {
developer {
id user
name 'Jörg Prante'
email 'joergprante@gmail.com'
url 'https://github.com/jprante'
}
}
scm {
url scmUrl
connection scmConnection
developerConnection scmDeveloperConnection
}
licenses {
license {
name 'The Apache License, Version 2.0'
url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
}
}
}
}
}
}
}
apply from: "${rootProject.projectDir}/gradle/ext.gradle"
apply from: "${rootProject.projectDir}/gradle/publish.gradle"
apply from: "${rootProject.projectDir}/gradle/sonarqube.gradle"
nexusStaging {
packageGroup = "org.xbib"
}
/*
spotbugs {
effort = "max"
reportLevel = "low"
}
tasks.withType(com.github.spotbugs.SpotBugsTask) {
ignoreFailures = true
reports {
xml.enabled = false
html.enabled = true
}
}
tasks.withType(Pmd) {
ignoreFailures = true
reports {
xml.enabled = true
html.enabled = true
}
}
tasks.withType(Checkstyle) {
ignoreFailures = true
reports {
xml.enabled = true
html.enabled = true
}
}
pmd {
toolVersion = '6.11.0'
ruleSets = ['category/java/bestpractices.xml']
}
checkstyle {
configFile = rootProject.file('config/checkstyle/checkstyle.xml')
ignoreFailures = true
showViolations = false
}
sonarqube {
properties {
property "sonar.projectName", "${project.group} ${project.name}"
property "sonar.sourceEncoding", "UTF-8"
property "sonar.tests", "src/test/java"
property "sonar.scm.provider", "git"
}
}
*/
afterEvaluate {
compileJava {
inputs.property("moduleName", 'org.xbib.guice')
doFirst {
options.compilerArgs = ['--module-path', classpath.asPath]
classpath = files()
}
}
javadoc {
inputs.property("moduleName", 'org.xbib.guice')
doFirst {
options.addStringOption('Xdoclint:none', '-quiet')
options.addStringOption('-module-path', classpath.asPath)
classpath = files()
}
}
}

View file

@ -1,3 +1,11 @@
group = org.xbib
name = guice
version = 4.0.4
version = 4.4.2
javax-inject.version = 1
guava.version = 28.1
# test
junit.version = 5.5.2
junit4.version = 4.12
log4j.version = 2.12.1

View file

@ -1,8 +0,0 @@
ext {
user = 'xbib'
projectName = 'guice'
projectDescription = 'Guice for Java'
scmUrl = 'https://github.com/xbib/guice'
scmConnection = 'scm:git:git://github.com/xbib/guice.git'
scmDeveloperConnection = 'scm:git:git://github.com/xbib/guice.git'
}

View file

@ -1,66 +0,0 @@
task xbibUpload(type: Upload) {
configuration = configurations.archives
uploadDescriptor = true
repositories {
if (project.hasProperty('xbibUsername')) {
mavenDeployer {
configuration = configurations.wagon
repository(url: uri('scpexe://xbib.org/repository')) {
authentication(userName: xbibUsername, privateKey: xbibPrivateKey)
}
}
}
}
}
task sonatypeUpload(type: Upload) {
configuration = configurations.archives
uploadDescriptor = true
repositories {
if (project.hasProperty('ossrhUsername')) {
mavenDeployer {
beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
repository(url: uri(ossrhReleaseUrl)) {
authentication(userName: ossrhUsername, password: ossrhPassword)
}
snapshotRepository(url: uri(ossrhSnapshotUrl)) {
authentication(userName: ossrhUsername, password: ossrhPassword)
}
pom.project {
groupId project.group
artifactId project.name
version project.version
name project.name
description projectDescription
packaging 'jar'
inceptionYear '2016'
url scmUrl
organization {
name 'xbib'
url 'http://xbib.org'
}
developers {
developer {
id user
name 'Jörg Prante'
email 'joergprante@gmail.com'
url 'https://github.com/jprante'
}
}
scm {
url scmUrl
connection scmConnection
developerConnection scmDeveloperConnection
}
licenses {
license {
name 'The Apache License, Version 2.0'
url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
}
}
}
}
}
}
}

View file

@ -1,41 +0,0 @@
tasks.withType(FindBugs) {
ignoreFailures = true
reports {
xml.enabled = true
html.enabled = false
}
}
tasks.withType(Pmd) {
ignoreFailures = true
reports {
xml.enabled = true
html.enabled = true
}
}
tasks.withType(Checkstyle) {
ignoreFailures = true
reports {
xml.enabled = true
html.enabled = true
}
}
jacocoTestReport {
reports {
xml.enabled true
csv.enabled false
xml.destination "${buildDir}/reports/jacoco-xml"
html.destination "${buildDir}/reports/jacoco-html"
}
}
sonarqube {
properties {
property "sonar.projectName", "${project.group} ${project.name}"
property "sonar.sourceEncoding", "UTF-8"
property "sonar.tests", "src/test/java"
property "sonar.scm.provider", "git"
property "sonar.java.coveragePlugin", "jacoco"
property "sonar.junit.reportsPath", "build/test-results/test/"
}
}

Binary file not shown.

View file

@ -1,6 +1,6 @@
#Fri Feb 03 12:38:43 CET 2017
#Wed Nov 20 21:27:17 CET 2019
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-bin.zip
zipStoreBase=GRADLE_USER_HOME

28
gradlew vendored
View file

@ -1,5 +1,21 @@
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# 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
#
# https://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.
#
##############################################################################
##
## Gradle start up script for UN*X
@ -28,16 +44,16 @@ APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
warn () {
echo "$*"
}
die ( ) {
die () {
echo
echo "$*"
echo
@ -109,8 +125,8 @@ if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
@ -155,7 +171,7 @@ if $cygwin ; then
fi
# Escape application args
save ( ) {
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}

18
gradlew.bat vendored
View file

@ -1,3 +1,19 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@ -14,7 +30,7 @@ set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome

View file

@ -36,6 +36,7 @@ public abstract class AbstractModule implements Module {
Binder binder;
@Override
public final synchronized void configure(Binder builder) {
checkState(this.binder == null, "Re-entry is not allowed.");
@ -63,8 +64,7 @@ public abstract class AbstractModule implements Module {
/**
* @see Binder#bindScope(Class, Scope)
*/
protected void bindScope(Class<? extends Annotation> scopeAnnotation,
Scope scope) {
protected void bindScope(Class<? extends Annotation> scopeAnnotation, Scope scope) {
binder().bindScope(scopeAnnotation, scope);
}

View file

@ -367,7 +367,7 @@ public interface Binder {
* their clients.
* @return a binder that shares its configuration with this binder.
*/
Binder skipSources(Class... classesToSkip);
Binder skipSources(Class<?>... classesToSkip);
/**
* Creates a new private child environment for bindings and other configuration. The returned

View file

@ -1,7 +1,7 @@
package com.google.inject;
import com.google.common.collect.ImmutableSet;
import com.google.inject.internal.Errors;
import com.google.inject.internal.Messages;
import com.google.inject.spi.Message;
import java.util.Collection;
@ -24,7 +24,7 @@ public final class ConfigurationException extends RuntimeException {
*/
public ConfigurationException(Iterable<Message> messages) {
this.messages = ImmutableSet.copyOf(messages);
initCause(Errors.getOnlyCause(this.messages));
initCause(Messages.getOnlyCause(this.messages));
}
/**
@ -59,6 +59,6 @@ public final class ConfigurationException extends RuntimeException {
@Override
public String getMessage() {
return Errors.format("Guice configuration errors", messages);
return Messages.formatMessages("Guice configuration errors", messages);
}
}

View file

@ -1,7 +1,7 @@
package com.google.inject;
import com.google.common.collect.ImmutableSet;
import com.google.inject.internal.Errors;
import com.google.inject.internal.Messages;
import com.google.inject.spi.Message;
import java.util.Collection;
@ -12,9 +12,9 @@ import static com.google.common.base.Preconditions.checkArgument;
* Thrown when errors occur while creating a {@link Injector}. Includes a list of encountered
* errors. Clients should catch this exception, log it, and stop execution.
*/
@SuppressWarnings("serial")
public class CreationException extends RuntimeException {
private static final long serialVersionUID = 0;
private final ImmutableSet<Message> messages;
/**
@ -23,7 +23,7 @@ public class CreationException extends RuntimeException {
public CreationException(Collection<Message> messages) {
this.messages = ImmutableSet.copyOf(messages);
checkArgument(!this.messages.isEmpty());
initCause(Errors.getOnlyCause(this.messages));
initCause(Messages.getOnlyCause(this.messages));
}
/**
@ -35,6 +35,6 @@ public class CreationException extends RuntimeException {
@Override
public String getMessage() {
return Errors.format("Unable to create injector, see the following errors", messages);
return Messages.formatMessages("Unable to create injector, see the following errors", messages);
}
}

View file

@ -76,11 +76,7 @@ public final class Guice {
* @throws CreationException if one or more errors occur during injector
* construction
*/
public static Injector createInjector(Stage stage,
Iterable<? extends Module> modules) {
return new InternalInjectorCreator()
.stage(stage)
.addModules(modules)
.build();
public static Injector createInjector(Stage stage, Iterable<? extends Module> modules) {
return new InternalInjectorCreator().stage(stage).addModules(modules).build();
}
}

View file

@ -1,5 +1,7 @@
package com.google.inject;
import com.google.inject.spi.Element;
import com.google.inject.spi.InjectionPoint;
import com.google.inject.spi.TypeConverterBinding;
import java.lang.annotation.Annotation;
@ -225,4 +227,37 @@ public interface Injector {
* <p>This method is part of the Guice SPI and is intended for use by tools and extensions.
*/
Set<TypeConverterBinding> getTypeConverterBindings();
/**
* Returns the elements that make up this injector. Note that not all kinds of elements are
* returned.
*
* <p>The returned list does not include elements inherited from a {@link #getParent() parent
* injector}, should one exist.
*
* <p>The returned list is immutable; it contains only the elements that were present when {@link
* #getElements} was invoked. Subsequent calls may return a list with additional elements.
*
* <p>The returned list does not include data inherited from a {@link #getParent() parent
* injector}, should one exist.
*
* <p>This method is part of the Guice SPI and is intended for use by tools and extensions.
*/
List<Element> getElements();
/**
* Returns the injection points created for calls to {@link #getMembersInjector} (either directly
* or indirectly, e.g. through {@link #injectMembers}.
*
* <p>This excludes any injection points from elements (which are accessible from each element via
* the SPI), unless {@link #getMembersInjector} or {@link #injectMembers} were also called for the
* same key.
*
* <p>The returned multimap does not include data inherited from a {@link #getParent() parent
* injector}, should one exist.
*
* <p>This method is part of the Guice SPI and is intended for use by tools and extensions.
*/
Map<TypeLiteral<?>, List<InjectionPoint>> getAllMembersInjectorInjectionPoints();
}

View file

@ -39,8 +39,13 @@ public class Key<T> {
private final AnnotationStrategy annotationStrategy;
private final TypeLiteral<T> typeLiteral;
private final int hashCode;
private final Supplier<String> toStringSupplier;
// This field is updated using the 'Data-Race-Ful' lazy intialization pattern
// See http://jeremymanson.blogspot.com/2008/12/benign-data-races-in-java.html for a detailed
// explanation.
private String toString;
/**
* Constructs a new key. Derives the type from this class's type parameter.
@ -60,7 +65,6 @@ public class Key<T> {
this.typeLiteral = MoreTypes.canonicalizeForKey(
(TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass()));
this.hashCode = computeHashCode();
this.toStringSupplier = createToStringSupplier();
}
/**
@ -82,7 +86,6 @@ public class Key<T> {
this.typeLiteral = MoreTypes.canonicalizeForKey(
(TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass()));
this.hashCode = computeHashCode();
this.toStringSupplier = createToStringSupplier();
}
/**
@ -102,7 +105,6 @@ public class Key<T> {
this.typeLiteral = MoreTypes.canonicalizeForKey(
(TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass()));
this.hashCode = computeHashCode();
this.toStringSupplier = createToStringSupplier();
}
/**
@ -113,7 +115,6 @@ public class Key<T> {
this.annotationStrategy = annotationStrategy;
this.typeLiteral = MoreTypes.canonicalizeForKey((TypeLiteral<T>) TypeLiteral.get(type));
this.hashCode = computeHashCode();
this.toStringSupplier = createToStringSupplier();
}
/**
@ -123,14 +124,12 @@ public class Key<T> {
this.annotationStrategy = annotationStrategy;
this.typeLiteral = MoreTypes.canonicalizeForKey(typeLiteral);
this.hashCode = computeHashCode();
this.toStringSupplier = createToStringSupplier();
}
/**
* Gets a key for an injection type and an annotation strategy.
*/
static <T> Key<T> get(Class<T> type,
AnnotationStrategy annotationStrategy) {
static <T> Key<T> get(Class<T> type, AnnotationStrategy annotationStrategy) {
return new Key<T>(type, annotationStrategy);
}
@ -144,8 +143,7 @@ public class Key<T> {
/**
* Gets a key for an injection type and an annotation type.
*/
public static <T> Key<T> get(Class<T> type,
Class<? extends Annotation> annotationType) {
public static <T> Key<T> get(Class<T> type, Class<? extends Annotation> annotationType) {
return new Key<T>(type, strategyFor(annotationType));
}
@ -166,8 +164,7 @@ public class Key<T> {
/**
* Gets a key for an injection type and an annotation type.
*/
public static Key<?> get(Type type,
Class<? extends Annotation> annotationType) {
public static Key<?> get(Type type, Class<? extends Annotation> annotationType) {
return new Key<Object>(type, strategyFor(annotationType));
}
@ -188,16 +185,14 @@ public class Key<T> {
/**
* Gets a key for an injection type and an annotation type.
*/
public static <T> Key<T> get(TypeLiteral<T> typeLiteral,
Class<? extends Annotation> annotationType) {
public static <T> Key<T> get(TypeLiteral<T> typeLiteral, Class<? extends Annotation> annotationType) {
return new Key<T>(typeLiteral, strategyFor(annotationType));
}
/**
* Gets a key for an injection type and an annotation.
*/
public static <T> Key<T> get(TypeLiteral<T> typeLiteral,
Annotation annotation) {
public static <T> Key<T> get(TypeLiteral<T> typeLiteral, Annotation annotation) {
return new Key<T>(typeLiteral, strategyFor(annotation));
}
@ -253,20 +248,6 @@ public class Key<T> {
return typeLiteral.hashCode() * 31 + annotationStrategy.hashCode();
}
/**
* @return a {@link Supplier} which memoizes the value for lazy initialization.
*/
private Supplier<String> createToStringSupplier() {
// The performance hit on access is acceptable since the intended use is for non-performance-
// critical applications.
return Suppliers.memoize(new Supplier<String>() {
@Override
public String get() {
return "Key[type=" + typeLiteral + ", annotation=" + annotationStrategy + "]";
}
});
}
/**
* Gets the key type.
*/
@ -333,7 +314,14 @@ public class Key<T> {
@Override
public final String toString() {
return toStringSupplier.get();
// Note: to not introduce dangerous data races the field should only be read once in this
// method.
String local = toString;
if (local == null) {
local = "Key[type=" + typeLiteral + ", annotation=" + annotationStrategy + "]";
toString = local;
}
return local;
}
/**
@ -375,21 +363,25 @@ public class Key<T> {
return new Key<T>(typeLiteral, annotationStrategy.withoutAttributes());
}
static enum NullAnnotationStrategy implements AnnotationStrategy {
enum NullAnnotationStrategy implements AnnotationStrategy {
INSTANCE;
@Override
public boolean hasAttributes() {
return false;
}
@Override
public AnnotationStrategy withoutAttributes() {
throw new UnsupportedOperationException("Key already has no attributes.");
}
@Override
public Annotation getAnnotation() {
return null;
}
@Override
public Class<? extends Annotation> getAnnotationType() {
return null;
}
@ -401,6 +393,7 @@ public class Key<T> {
}
interface AnnotationStrategy {
Annotation getAnnotation();
Class<? extends Annotation> getAnnotationType();
@ -419,18 +412,22 @@ public class Key<T> {
this.annotation = checkNotNull(annotation, "annotation");
}
@Override
public boolean hasAttributes() {
return true;
}
@Override
public AnnotationStrategy withoutAttributes() {
return new AnnotationTypeStrategy(getAnnotationType(), annotation);
}
@Override
public Annotation getAnnotation() {
return annotation;
}
@Override
public Class<? extends Annotation> getAnnotationType() {
return annotation.annotationType();
}
@ -469,18 +466,22 @@ public class Key<T> {
this.annotation = annotation;
}
@Override
public boolean hasAttributes() {
return false;
}
@Override
public AnnotationStrategy withoutAttributes() {
throw new UnsupportedOperationException("Key already has no attributes.");
}
@Override
public Annotation getAnnotation() {
return annotation;
}
@Override
public Class<? extends Annotation> getAnnotationType() {
return annotationType;
}

View file

@ -0,0 +1,22 @@
package com.google.inject;
/**
* Thrown from {@link Provider#get} when an attempt is made to access a scoped object while the
* scope in question is not currently active.
*
*/
@SuppressWarnings("serial")
public final class OutOfScopeException extends RuntimeException {
public OutOfScopeException(String message) {
super(message);
}
public OutOfScopeException(String message, Throwable cause) {
super(message, cause);
}
public OutOfScopeException(Throwable cause) {
super(cause);
}
}

View file

@ -31,5 +31,5 @@ public interface PrivateBinder extends Binder {
PrivateBinder withSource(Object source);
PrivateBinder skipSources(Class... classesToSkip);
PrivateBinder skipSources(Class<?>... classesToSkip);
}

View file

@ -2,6 +2,7 @@ package com.google.inject;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.inject.Provider;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

View file

@ -32,5 +32,6 @@ public interface Provider<T> extends javax.inject.Provider<T> {
* @throws ProvisionException if an instance cannot be provided. Such exceptions include messages
* and throwables to describe why provision failed.
*/
@Override
T get();
}

View file

@ -1,7 +1,7 @@
package com.google.inject;
import com.google.common.collect.ImmutableSet;
import com.google.inject.internal.Errors;
import com.google.inject.internal.Messages;
import com.google.inject.spi.Message;
import java.util.Collection;
@ -23,7 +23,7 @@ public final class ProvisionException extends RuntimeException {
public ProvisionException(Iterable<Message> messages) {
this.messages = ImmutableSet.copyOf(messages);
checkArgument(!this.messages.isEmpty());
initCause(Errors.getOnlyCause(this.messages));
initCause(Messages.getOnlyCause(this.messages));
}
public ProvisionException(String message, Throwable cause) {
@ -44,6 +44,6 @@ public final class ProvisionException extends RuntimeException {
@Override
public String getMessage() {
return Errors.format("Unable to provision, see the following errors", messages);
return Messages.formatMessages("Unable to provision, see the following errors", messages);
}
}

View file

@ -28,6 +28,7 @@ public class Scopes {
* this to "no scope" in your binding.
*/
public static final Scope NO_SCOPE = new Scope() {
@Override
public <T> Provider<T> scope(Key<T> key, Provider<T> unscoped) {
return unscoped;
}
@ -37,21 +38,26 @@ public class Scopes {
return "Scopes.NO_SCOPE";
}
};
private static final BindingScopingVisitor<Boolean> IS_SINGLETON_VISITOR
= new BindingScopingVisitor<Boolean>() {
= new BindingScopingVisitor<>() {
@Override
public Boolean visitNoScoping() {
return false;
}
@Override
public Boolean visitScopeAnnotation(Class<? extends Annotation> scopeAnnotation) {
return scopeAnnotation == Singleton.class
|| scopeAnnotation == javax.inject.Singleton.class;
}
@Override
public Boolean visitScope(Scope scope) {
return scope == Scopes.SINGLETON;
}
@Override
public Boolean visitEagerSingleton() {
return true;
}
@ -107,18 +113,22 @@ public class Scopes {
final Class<? extends Annotation> scopeAnnotation) {
do {
boolean matches = binding.acceptScopingVisitor(new BindingScopingVisitor<Boolean>() {
@Override
public Boolean visitNoScoping() {
return false;
}
@Override
public Boolean visitScopeAnnotation(Class<? extends Annotation> visitedAnnotation) {
return visitedAnnotation == scopeAnnotation;
}
@Override
public Boolean visitScope(Scope visitedScope) {
return visitedScope == scope;
}
@Override
public Boolean visitEagerSingleton() {
return false;
}

View file

@ -170,7 +170,7 @@ public class TypeLiteral<T> {
// this implementation is made a little more complicated in an attempt to avoid object-creation
while (true) {
if (toResolve instanceof TypeVariable) {
TypeVariable original = (TypeVariable) toResolve;
TypeVariable<?> original = (TypeVariable) toResolve;
toResolve = MoreTypes.resolveTypeVariable(type, rawType, original);
if (toResolve == original) {
return toResolve;

View file

@ -13,6 +13,7 @@ import com.google.inject.Provider;
import com.google.inject.TypeLiteral;
import com.google.inject.internal.Errors;
import com.google.inject.internal.ErrorsException;
import com.google.inject.internal.Messages;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.HasDependencies;
import com.google.inject.spi.Message;
@ -256,7 +257,7 @@ public class FactoryProvider<F> implements Provider<F>, HasDependencies {
}
private static ConfigurationException newConfigurationException(String format, Object... args) {
return new ConfigurationException(ImmutableSet.of(new Message(Errors.format(format, args))));
return new ConfigurationException(ImmutableSet.of(new Message(Messages.format(format, args))));
}
@Inject
@ -355,9 +356,9 @@ public class FactoryProvider<F> implements Provider<F>, HasDependencies {
};
@SuppressWarnings("unchecked") // we imprecisely treat the class literal of T as a Class<T>
Class<F> factoryRawType = (Class<F>) (Class<?>) factoryType.getRawType();
Class<F> factoryRawType = (Class<F>) factoryType.getRawType();
return factoryRawType.cast(Proxy.newProxyInstance(factoryRawType.getClassLoader(),
new Class[]{factoryRawType}, invocationHandler));
new Class<?>[]{factoryRawType}, invocationHandler));
}
@Override

View file

@ -96,7 +96,7 @@ final class FactoryProvider2<F> implements InvocationHandler,
@Override
public String toString() {
return "@" + Assisted.class.getName() + "(value=)";
return "@" + Assisted.class.getName() + "(value=\"\")";
}
};
/**
@ -555,7 +555,7 @@ final class FactoryProvider2<F> implements InvocationHandler,
+ "Stop injecting @Assisted Provider<T> (instead use @Assisted T) "
+ "or Injector to speed things up. (It will be a ~6500% speed bump!) "
+ "The exact offending deps are: {2}",
new Object[]{factoryType, implementation, badDeps});
new Object[] { factoryType, implementation, badDeps } );
return false;
}
return true;

View file

@ -51,20 +51,17 @@ public interface LinkedBindingBuilder<T> extends ScopedBindingBuilder {
/**
* See the EDSL examples at {@link com.google.inject.Binder}.
*/
ScopedBindingBuilder toProvider(
Class<? extends javax.inject.Provider<? extends T>> providerType);
ScopedBindingBuilder toProvider(Class<? extends javax.inject.Provider<? extends T>> providerType);
/**
* See the EDSL examples at {@link com.google.inject.Binder}.
*/
ScopedBindingBuilder toProvider(
TypeLiteral<? extends javax.inject.Provider<? extends T>> providerType);
ScopedBindingBuilder toProvider(TypeLiteral<? extends javax.inject.Provider<? extends T>> providerType);
/**
* See the EDSL examples at {@link com.google.inject.Binder}.
*/
ScopedBindingBuilder toProvider(
Key<? extends javax.inject.Provider<? extends T>> providerKey);
ScopedBindingBuilder toProvider(Key<? extends javax.inject.Provider<? extends T>> providerKey);
/**
* See the EDSL examples at {@link com.google.inject.Binder}.
@ -74,6 +71,5 @@ public interface LinkedBindingBuilder<T> extends ScopedBindingBuilder {
/**
* See the EDSL examples at {@link com.google.inject.Binder}.
*/
<S extends T> ScopedBindingBuilder toConstructor(
Constructor<S> constructor, TypeLiteral<? extends S> type);
<S extends T> ScopedBindingBuilder toConstructor(Constructor<S> constructor, TypeLiteral<? extends S> type);
}

View file

@ -18,14 +18,14 @@ import static com.google.common.base.Preconditions.checkNotNull;
public abstract class AbstractBindingBuilder<T> {
public static final String IMPLEMENTATION_ALREADY_SET = "Implementation is set more than once.";
public static final String SINGLE_INSTANCE_AND_SCOPE
= "Setting the scope is not permitted when binding to a single instance.";
public static final String SINGLE_INSTANCE_AND_SCOPE =
"Setting the scope is not permitted when binding to a single instance.";
public static final String SCOPE_ALREADY_SET = "Scope is set more than once.";
public static final String BINDING_TO_NULL = "Binding to null instances is not allowed. "
+ "Use toProvider(Providers.of(null)) if this is your intended behaviour.";
public static final String BINDING_TO_NULL = "Binding to null instances is not allowed. " +
"Use toProvider(Providers.of(null)) if this is your intended behaviour.";
public static final String CONSTANT_VALUE_ALREADY_SET = "Constant value is set more than once.";
public static final String ANNOTATION_ALREADY_SPECIFIED
= "More than one annotation is specified for this binding.";
public static final String ANNOTATION_ALREADY_SPECIFIED =
"More than one annotation is specified for this binding.";
protected static final Key<?> NULL_KEY = Key.get(Void.class);
protected final Binder binder;
@ -37,7 +37,7 @@ public abstract class AbstractBindingBuilder<T> {
this.binder = binder;
this.elements = elements;
this.position = elements.size();
this.binding = new UntargettedBindingImpl<T>(source, key, Scoping.UNSCOPED);
this.binding = new UntargettedBindingImpl<>(source, key, Scoping.UNSCOPED);
elements.add(position, this.binding);
}
@ -57,8 +57,7 @@ public abstract class AbstractBindingBuilder<T> {
protected BindingImpl<T> annotatedWithInternal(Class<? extends Annotation> annotationType) {
checkNotNull(annotationType, "annotationType");
checkNotAnnotated();
return setBinding(binding.withKey(
Key.get(this.binding.getKey().getTypeLiteral(), annotationType)));
return setBinding(binding.withKey(Key.get(this.binding.getKey().getTypeLiteral(), annotationType)));
}
/**
@ -67,8 +66,7 @@ public abstract class AbstractBindingBuilder<T> {
protected BindingImpl<T> annotatedWithInternal(Annotation annotation) {
checkNotNull(annotation, "annotation");
checkNotAnnotated();
return setBinding(binding.withKey(
Key.get(this.binding.getKey().getTypeLiteral(), annotation)));
return setBinding(binding.withKey(Key.get(this.binding.getKey().getTypeLiteral(), annotation)));
}
public void in(final Class<? extends Annotation> scopeAnnotation) {

View file

@ -24,7 +24,7 @@ abstract class AbstractBindingProcessor extends AbstractProcessor {
// It's unfortunate that we have to maintain a blacklist of specific
// classes, but we can't easily block the whole package because of
// all our unit tests.
private static final Set<Class<?>> FORBIDDEN_TYPES = ImmutableSet.<Class<?>>of(
private static final Set<Class<?>> FORBIDDEN_TYPES = ImmutableSet.of(
AbstractModule.class,
Binder.class,
Binding.class,
@ -93,7 +93,7 @@ abstract class AbstractBindingProcessor extends AbstractProcessor {
*/
private boolean isOkayDuplicate(BindingImpl<?> original, BindingImpl<?> binding, State state) {
if (original instanceof ExposedBindingImpl) {
ExposedBindingImpl exposed = (ExposedBindingImpl) original;
ExposedBindingImpl<?> exposed = (ExposedBindingImpl) original;
InjectorImpl exposedFrom = (InjectorImpl) exposed.getPrivateElements().getInjector();
return (exposedFrom == binding.getInjector());
} else {
@ -131,16 +131,28 @@ abstract class AbstractBindingProcessor extends AbstractProcessor {
scoping = Scoping.makeInjectable(scoping, injector, errors);
}
protected void scheduleInitialization(final BindingImpl<?> binding) {
bindingData.addUninitializedBinding(new Runnable() {
public void run() {
try {
binding.getInjector().initializeBinding(binding, errors.withSource(source));
} catch (ErrorsException e) {
errors.merge(e.getErrors());
}
}
});
/**
* Schedule initialization of this binding to occur immediately after all bindings have been
* initialially processed.
*/
protected void scheduleInitialization(BindingImpl<?> binding) {
bindingData.addUninitializedBinding(() -> initializeBinding(binding));
}
/**
* Schedule initialization for this binding to occur after all other static initialization of
* bindings.
*/
protected void scheduleDelayedInitialization(BindingImpl<?> binding) {
bindingData.addDelayedUninitializedBinding(() -> initializeBinding(binding));
}
private void initializeBinding(BindingImpl<?> binding) {
try {
binding.getInjector().initializeBinding(binding, errors.withSource(source));
} catch (ErrorsException e) {
errors.merge(e.getErrors());
}
}
}
}

View file

@ -1,5 +1,7 @@
package com.google.inject.internal;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Joiner.MapJoiner;
@ -43,14 +45,17 @@ public class Annotations {
return s.substring(1, s.length() - 1); // cut off brackets
}
};
private static final LoadingCache<Class<? extends Annotation>, Annotation> cache =
CacheBuilder.newBuilder().weakKeys().build(
new CacheLoader<Class<? extends Annotation>, Annotation>() {
CacheBuilder.newBuilder()
.weakKeys()
.build(new CacheLoader<>() {
@Override
public Annotation load(Class<? extends Annotation> input) {
return generateAnnotationImpl(input);
}
});
private static final AnnotationChecker scopeChecker = new AnnotationChecker(
Arrays.asList(ScopeAnnotation.class, javax.inject.Scope.class));
private static final AnnotationChecker bindingAnnotationChecker = new AnnotationChecker(
@ -90,21 +95,19 @@ public class Annotations {
return annotationType.cast(Proxy.newProxyInstance(
annotationType.getClassLoader(),
new Class<?>[]{annotationType},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Exception {
String name = method.getName();
if (name.equals("annotationType")) {
(proxy, method, args) -> {
String name = method.getName();
switch (name) {
case "annotationType":
return annotationType;
} else if (name.equals("toString")) {
case "toString":
return annotationToString(annotationType, members);
} else if (name.equals("hashCode")) {
case "hashCode":
return annotationHashCode(annotationType, members);
} else if (name.equals("equals")) {
case "equals":
return annotationEquals(annotationType, members, args[0]);
} else {
default:
return members.get(name);
}
}
}));
}
@ -207,6 +210,30 @@ public class Annotations {
return false;
}
private static final boolean QUOTE_MEMBER_VALUES = determineWhetherToQuote();
public static String memberValueString(String value) {
return QUOTE_MEMBER_VALUES ? "\"" + value + "\"" : value;
}
@Retention(RUNTIME)
private @interface TestAnnotation {
String value();
}
@TestAnnotation("determineWhetherToQuote")
private static boolean determineWhetherToQuote() {
try {
String annotation = Annotations.class
.getDeclaredMethod("determineWhetherToQuote")
.getAnnotation(TestAnnotation.class)
.toString();
return annotation.contains("\"determineWhetherToQuote\"");
} catch (NoSuchMethodException e) {
throw new AssertionError(e);
}
}
public static boolean isScopeAnnotation(Class<? extends Annotation> annotationType) {
return scopeChecker.hasAnnotations(annotationType);
}
@ -243,8 +270,7 @@ public class Annotations {
/**
* Returns the binding annotation on {@code member}, or null if there isn't one.
*/
public static Annotation findBindingAnnotation(
Errors errors, Member member, Annotation[] annotations) {
public static Annotation findBindingAnnotation(Errors errors, Member member, Annotation[] annotations) {
Annotation found = null;
for (Annotation annotation : annotations) {
@ -293,6 +319,24 @@ public class Annotations {
}
}
/**
* Returns the name the binding should use. This is based on the annotation. If the annotation has
* an instance and is not a marker annotation, we ask the annotation for its toString. If it was a
* marker annotation or just an annotation type, we use the annotation's name. Otherwise, the name
* is the empty string.
*/
public static String nameOf(Key<?> key) {
Annotation annotation = key.getAnnotation();
Class<? extends Annotation> annotationType = key.getAnnotationType();
if (annotation != null && !isMarker(annotationType)) {
return key.getAnnotation().toString();
} else if (key.getAnnotationType() != null) {
return "@" + key.getAnnotationType().getName();
} else {
return "";
}
}
/**
* Checks for the presence of annotations. Caches results because Android doesn't.
*/
@ -303,7 +347,8 @@ public class Annotations {
* Returns true if the given class has one of the desired annotations.
*/
private CacheLoader<Class<? extends Annotation>, Boolean> hasAnnotations =
new CacheLoader<Class<? extends Annotation>, Boolean>() {
new CacheLoader<>() {
@Override
public Boolean load(Class<? extends Annotation> annotationType) {
for (Annotation annotation : annotationType.getAnnotations()) {
if (annotationTypes.contains(annotation.annotationType())) {
@ -314,8 +359,8 @@ public class Annotations {
}
};
final LoadingCache<Class<? extends Annotation>, Boolean> cache = CacheBuilder.newBuilder().weakKeys()
.build(hasAnnotations);
final LoadingCache<Class<? extends Annotation>, Boolean> cache =
CacheBuilder.newBuilder().weakKeys().build(hasAnnotations);
/**
* Constructs a new checker that looks for annotations of the given types.

View file

@ -29,24 +29,29 @@ public class BindingBuilder<T> extends AbstractBindingBuilder<T>
super(binder, elements, source, key);
}
@Override
public BindingBuilder<T> annotatedWith(Class<? extends Annotation> annotationType) {
annotatedWithInternal(annotationType);
return this;
}
@Override
public BindingBuilder<T> annotatedWith(Annotation annotation) {
annotatedWithInternal(annotation);
return this;
}
@Override
public BindingBuilder<T> to(Class<? extends T> implementation) {
return to(Key.get(implementation));
}
@Override
public BindingBuilder<T> to(TypeLiteral<? extends T> implementation) {
return to(Key.get(implementation));
}
@Override
public BindingBuilder<T> to(Key<? extends T> linkedKey) {
checkNotNull(linkedKey, "linkedKey");
checkNotTargetted();
@ -56,6 +61,7 @@ public class BindingBuilder<T> extends AbstractBindingBuilder<T>
return this;
}
@Override
public void toInstance(T instance) {
checkNotTargetted();
@ -79,10 +85,12 @@ public class BindingBuilder<T> extends AbstractBindingBuilder<T>
}
@SuppressWarnings("unchecked")
@Override
public BindingBuilder<T> toProvider(Provider<? extends T> provider) {
return toProvider((javax.inject.Provider<T>) provider);
}
@Override
public BindingBuilder<T> toProvider(javax.inject.Provider<? extends T> provider) {
checkNotNull(provider, "provider");
checkNotTargetted();
@ -102,16 +110,19 @@ public class BindingBuilder<T> extends AbstractBindingBuilder<T>
return this;
}
@Override
public BindingBuilder<T> toProvider(
Class<? extends javax.inject.Provider<? extends T>> providerType) {
return toProvider(Key.get(providerType));
}
@Override
public BindingBuilder<T> toProvider(
TypeLiteral<? extends javax.inject.Provider<? extends T>> providerType) {
return toProvider(Key.get(providerType));
}
@Override
public BindingBuilder<T> toProvider(
Key<? extends javax.inject.Provider<? extends T>> providerKey) {
checkNotNull(providerKey, "providerKey");
@ -123,10 +134,12 @@ public class BindingBuilder<T> extends AbstractBindingBuilder<T>
return this;
}
@Override
public <S extends T> ScopedBindingBuilder toConstructor(Constructor<S> constructor) {
return toConstructor(constructor, TypeLiteral.get(constructor.getDeclaringClass()));
}
@Override
public <S extends T> ScopedBindingBuilder toConstructor(Constructor<S> constructor,
TypeLiteral<? extends S> type) {
checkNotNull(constructor, "constructor");

View file

@ -34,14 +34,17 @@ public abstract class BindingImpl<T> implements Binding<T> {
this.scoping = scoping;
}
@Override
public Key<T> getKey() {
return key;
}
@Override
public Object getSource() {
return source;
}
@Override
public Provider<T> getProvider() {
if (provider == null) {
if (injector == null) {
@ -69,10 +72,12 @@ public abstract class BindingImpl<T> implements Binding<T> {
return this instanceof InstanceBinding;
}
@Override
public <V> V acceptVisitor(ElementVisitor<V> visitor) {
return visitor.visit(this);
}
@Override
public <V> V acceptScopingVisitor(BindingScopingVisitor<V> visitor) {
return scoping.acceptVisitor(visitor);
}

View file

@ -49,7 +49,7 @@ final class BindingProcessor extends AbstractBindingProcessor {
return true;
}
return command.acceptTargetVisitor(new Processor<T, Boolean>((BindingImpl<T>) command) {
return command.acceptTargetVisitor(new Processor<>((BindingImpl<T>) command) {
@Override
public Boolean visit(ConstructorBinding<? extends T> binding) {
prepareBinding();
@ -72,13 +72,13 @@ final class BindingProcessor extends AbstractBindingProcessor {
T instance = binding.getInstance();
@SuppressWarnings("unchecked") // safe to cast to binding<T> because
// the processor was constructed w/ it
Initializable<T> ref = initializer.requestInjection(
Initializable<T> ref = initializer.requestInjection(
injector, instance, (Binding<T>) binding, source, injectionPoints);
ConstantFactory<? extends T> factory = new ConstantFactory<T>(ref);
InternalFactory<? extends T> scopedFactory
= Scoping.scope(key, injector, factory, source, scoping);
putBinding(new InstanceBindingImpl<T>(injector, key, source, scopedFactory, injectionPoints,
instance));
ConstantFactory<? extends T> factory = new ConstantFactory<>(ref);
InternalFactory<? extends T> scopedFactory =
Scoping.scope(key, injector, factory, source, scoping);
putBinding(new InstanceBindingImpl<>(injector, key, source,
scopedFactory, injectionPoints, instance));
return true;
}
@ -86,18 +86,22 @@ final class BindingProcessor extends AbstractBindingProcessor {
public Boolean visit(ProviderInstanceBinding<? extends T> binding) {
prepareBinding();
javax.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 javax.inject.Provider<? extends T>> initializable =
initializer.<javax.inject.Provider<? extends T>>requestInjection(
injector, provider, null, source, injectionPoints);
initializer.requestInjection(injector, provider, null, source, injectionPoints);
// always visited with Binding<T>
@SuppressWarnings("unchecked")
InternalFactory<T> factory = new InternalFactoryToInitializableAdapter<T>(
InternalFactory<T> factory = new InternalFactoryToInitializableAdapter<>(
initializable, source,
injector.provisionListenerStore.get((ProviderInstanceBinding<T>) binding));
InternalFactory<? extends T> scopedFactory
= Scoping.scope(key, injector, factory, source, scoping);
putBinding(new ProviderInstanceBindingImpl<T>(injector, key, source, scopedFactory, scoping,
InternalFactory<? extends T> scopedFactory = Scoping.scope(key, injector, factory, source, scoping);
putBinding(new ProviderInstanceBindingImpl<>(injector, key, source, scopedFactory, scoping,
provider, injectionPoints));
return true;
}
@ -108,13 +112,13 @@ final class BindingProcessor extends AbstractBindingProcessor {
Key<? extends javax.inject.Provider<? extends T>> providerKey = binding.getProviderKey();
// always visited with Binding<T>
@SuppressWarnings("unchecked")
BoundProviderFactory<T> boundProviderFactory = new BoundProviderFactory<T>(
BoundProviderFactory<T> boundProviderFactory = new BoundProviderFactory<>(
injector, providerKey, source,
injector.provisionListenerStore.get((ProviderKeyBinding<T>) binding));
bindingData.addCreationListener(boundProviderFactory);
InternalFactory<? extends T> scopedFactory = Scoping.scope(
key, injector, (InternalFactory<? extends T>) boundProviderFactory, source, scoping);
putBinding(new LinkedProviderBindingImpl<T>(
key, injector, boundProviderFactory, source, scoping);
putBinding(new LinkedProviderBindingImpl<>(
injector, key, source, scopedFactory, scoping, providerKey));
return true;
}
@ -126,13 +130,36 @@ final class BindingProcessor extends AbstractBindingProcessor {
if (key.equals(linkedKey)) {
errors.recursiveBinding();
}
FactoryProxy<T> factory = new FactoryProxy<T>(injector, key, linkedKey, source);
FactoryProxy<T> factory = new FactoryProxy<>(injector, key, linkedKey, source);
bindingData.addCreationListener(factory);
InternalFactory<? extends T> scopedFactory
= Scoping.scope(key, injector, factory, source, scoping);
putBinding(
new LinkedBindingImpl<T>(injector, key, source, scopedFactory, scoping, linkedKey));
InternalFactory<? extends T> scopedFactory =
Scoping.scope(key, injector, factory, source, scoping);
putBinding(new LinkedBindingImpl<>(injector, key, source, scopedFactory, scoping, linkedKey));
return true;
}
/** Handle ProviderMethods specially. */
private Boolean visitInternalProviderInstanceBindingFactory(
InternalProviderInstanceBindingImpl.Factory<T> provider) {
InternalProviderInstanceBindingImpl<T> binding =
new InternalProviderInstanceBindingImpl<>(
injector,
key,
source,
provider,
Scoping.scope(key, injector, provider, source, scoping),
scoping);
switch (binding.getInitializationTiming()) {
case DELAYED:
scheduleDelayedInitialization(binding);
break;
case EAGER:
scheduleInitialization(binding);
break;
default:
throw new AssertionError();
}
putBinding(binding);
return true;
}
@ -172,9 +199,9 @@ final class BindingProcessor extends AbstractBindingProcessor {
}
private <T> void bindExposed(PrivateElements privateElements, Key<T> key) {
ExposedKeyFactory<T> exposedKeyFactory = new ExposedKeyFactory<T>(key, privateElements);
ExposedKeyFactory<T> exposedKeyFactory = new ExposedKeyFactory<>(key, privateElements);
bindingData.addCreationListener(exposedKeyFactory);
putBinding(new ExposedBindingImpl<T>(
putBinding(new ExposedBindingImpl<>(
injector, privateElements.getExposedSource(key), key, exposedKeyFactory, privateElements));
}
}

View file

@ -6,16 +6,17 @@ import com.google.inject.spi.Dependency;
import javax.inject.Provider;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Delegates to a custom factory which is also bound in the injector.
*/
final class BoundProviderFactory<T> extends ProviderInternalFactory<T> implements CreationListener {
final Key<? extends javax.inject.Provider<? extends T>> providerKey;
private final Key<? extends javax.inject.Provider<? extends T>> providerKey;
private final ProvisionListenerStackCallback<T> provisionCallback;
private final InjectorImpl injector;
private InternalFactory<? extends javax.inject.Provider<? extends T>> providerFactory;
BoundProviderFactory(
@ -24,11 +25,12 @@ final class BoundProviderFactory<T> extends ProviderInternalFactory<T> implement
Object source,
ProvisionListenerStackCallback<T> provisionCallback) {
super(source);
this.provisionCallback = checkNotNull(provisionCallback, "provisionCallback");
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);
@ -37,25 +39,27 @@ final class BoundProviderFactory<T> extends ProviderInternalFactory<T> implement
}
}
public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
throws ErrorsException {
@Override
public T get(InternalContext context, Dependency<?> dependency, boolean linked)
throws InternalProvisionException {
context.pushState(providerKey, source);
try {
errors = errors.withSource(providerKey);
javax.inject.Provider<? extends T> provider = providerFactory.get(errors, context, dependency, true);
return circularGet(provider, errors, context, dependency, provisionCallback);
javax.inject.Provider<? extends T> provider = providerFactory.get(context, dependency, true);
return circularGet(provider, context, dependency, provisionCallback);
} catch (InternalProvisionException ipe) {
throw ipe.addSource(providerKey);
} finally {
context.popState();
}
}
@Override
protected T provision(Provider<? extends T> provider, Errors errors, Dependency<?> dependency,
ConstructionContext<T> constructionContext) throws ErrorsException {
protected T provision(Provider<? extends T> provider, Dependency<?> dependency,
ConstructionContext<T> constructionContext) throws InternalProvisionException {
try {
return super.provision(provider, errors, dependency, constructionContext);
return super.provision(provider, dependency, constructionContext);
} catch (RuntimeException userException) {
throw errors.errorInProvider(userException).toException();
throw InternalProvisionException.errorInProvider(userException);
}
}

View file

@ -6,7 +6,6 @@ import com.google.inject.Key;
import com.google.inject.binder.AnnotatedConstantBindingBuilder;
import com.google.inject.binder.ConstantBindingBuilder;
import com.google.inject.spi.Element;
import com.google.inject.spi.InjectionPoint;
import java.lang.annotation.Annotation;
import java.util.List;
@ -24,56 +23,69 @@ public final class ConstantBindingBuilderImpl<T>
super(binder, elements, source, (Key<T>) NULL_KEY);
}
@Override
public ConstantBindingBuilder annotatedWith(Class<? extends Annotation> annotationType) {
annotatedWithInternal(annotationType);
return this;
}
@Override
public ConstantBindingBuilder annotatedWith(Annotation annotation) {
annotatedWithInternal(annotation);
return this;
}
@Override
public void to(final String value) {
toConstant(String.class, value);
}
@Override
public void to(final int value) {
toConstant(Integer.class, value);
}
@Override
public void to(final long value) {
toConstant(Long.class, value);
}
@Override
public void to(final boolean value) {
toConstant(Boolean.class, value);
}
@Override
public void to(final double value) {
toConstant(Double.class, value);
}
@Override
public void to(final float value) {
toConstant(Float.class, value);
}
@Override
public void to(final short value) {
toConstant(Short.class, value);
}
@Override
public void to(final char value) {
toConstant(Character.class, value);
}
@Override
public void to(final byte value) {
toConstant(Byte.class, value);
}
@Override
public void to(final Class<?> value) {
toConstant(Class.class, value);
}
@Override
public <E extends Enum<E>> void to(final E value) {
toConstant(value.getDeclaringClass(), value);
}
@ -105,7 +117,7 @@ public final class ConstantBindingBuilderImpl<T>
}
setBinding(new InstanceBindingImpl<T>(
base.getSource(), key, base.getScoping(), ImmutableSet.<InjectionPoint>of(), instanceAsT));
base.getSource(), key, base.getScoping(), ImmutableSet.of(), instanceAsT));
}
@Override

View file

@ -7,15 +7,17 @@ final class ConstantFactory<T> implements InternalFactory<T> {
private final Initializable<T> initializable;
public ConstantFactory(Initializable<T> initializable) {
ConstantFactory(Initializable<T> initializable) {
this.initializable = initializable;
}
public T get(Errors errors, InternalContext context, Dependency dependency, boolean linked)
throws ErrorsException {
return initializable.get(errors);
@Override
public T get(InternalContext context, Dependency<?> dependency, boolean linked)
throws InternalProvisionException {
return initializable.get();
}
@Override
public String toString() {
return MoreObjects.toStringHelper(ConstantFactory.class)
.add("value", initializable)

View file

@ -41,26 +41,26 @@ final class ConstructionContext<T> {
invocationHandlers = null;
}
public Object createProxy(Errors errors, InjectorOptions injectorOptions,
Class<?> expectedType) throws ErrorsException {
public Object createProxy(InjectorOptions injectorOptions,
Class<?> expectedType) throws InternalProvisionException {
if (injectorOptions.disableCircularProxies) {
throw errors.circularProxiesDisabled(expectedType).toException();
throw InternalProvisionException.circularDependenciesDisabled(expectedType);
}
if (!expectedType.isInterface()) {
throw errors.cannotSatisfyCircularDependency(expectedType).toException();
throw InternalProvisionException.cannotProxyClass(expectedType);
}
if (invocationHandlers == null) {
invocationHandlers = new ArrayList<DelegatingInvocationHandler<T>>();
invocationHandlers = new ArrayList<>();
}
DelegatingInvocationHandler<T> invocationHandler = new DelegatingInvocationHandler<T>();
DelegatingInvocationHandler<T> invocationHandler = new DelegatingInvocationHandler<>();
invocationHandlers.add(invocationHandler);
ClassLoader classLoader = expectedType.getClass().getClassLoader() != null ?
expectedType.getClass().getClassLoader() : ClassLoader.getSystemClassLoader();
return expectedType.cast(Proxy.newProxyInstance(classLoader,
new Class[]{expectedType, CircularDependencyProxy.class}, invocationHandler));
new Class<?>[]{expectedType, CircularDependencyProxy.class}, invocationHandler));
}
public void setProxyDelegates(T delegate) {

View file

@ -26,10 +26,15 @@ final class ConstructorBindingImpl<T> extends BindingImpl<T>
implements ConstructorBinding<T>, DelayedInitialize {
private final Factory<T> factory;
private final InjectionPoint constructorInjectionPoint;
private ConstructorBindingImpl(InjectorImpl injector, Key<T> key, Object source,
InternalFactory<? extends T> scopedFactory, Scoping scoping, Factory<T> factory,
private ConstructorBindingImpl(InjectorImpl injector,
Key<T> key,
Object source,
InternalFactory<? extends T> scopedFactory,
Scoping scoping,
Factory<T> factory,
InjectionPoint constructorInjectionPoint) {
super(injector, key, source, scopedFactory, scoping);
this.factory = factory;
@ -53,7 +58,10 @@ final class ConstructorBindingImpl<T> extends BindingImpl<T>
* only succeed if retrieved from a linked binding
*/
static <T> ConstructorBindingImpl<T> create(InjectorImpl injector, Key<T> key,
InjectionPoint constructorInjector, Object source, Scoping scoping, Errors errors,
InjectionPoint constructorInjector,
Object source,
Scoping scoping,
Errors errors,
boolean failIfNotLinked, boolean failIfNotExplicit)
throws ErrorsException {
int numErrors = errors.size();
@ -65,7 +73,7 @@ final class ConstructorBindingImpl<T> extends BindingImpl<T>
// We can't inject abstract classes.
if (Modifier.isAbstract(rawType.getModifiers())) {
errors.missingImplementation(key);
errors.missingImplementationWithHint(key, injector);
}
// Error: Inner class.
@ -110,7 +118,7 @@ final class ConstructorBindingImpl<T> extends BindingImpl<T>
/**
* Returns true if the inject annotation is on the constructor.
*/
private static boolean hasAtInject(Constructor cxtor) {
private static boolean hasAtInject(Constructor<?> cxtor) {
return cxtor.isAnnotationPresent(Inject.class)
|| cxtor.isAnnotationPresent(javax.inject.Inject.class);
}
@ -162,21 +170,25 @@ final class ConstructorBindingImpl<T> extends BindingImpl<T>
return Dependency.forInjectionPoints(builder.build());
}
@Override
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
checkState(factory.constructorInjector != null, "not initialized");
return visitor.visit(this);
}
@Override
public InjectionPoint getConstructor() {
checkState(factory.constructorInjector != null, "Binding is not ready");
return factory.constructorInjector.getConstructionProxy().getInjectionPoint();
}
@Override
public Set<InjectionPoint> getInjectableMembers() {
checkState(factory.constructorInjector != null, "Binding is not ready");
return factory.constructorInjector.getInjectableMembers();
}
@Override
public Set<Dependency<?>> getDependencies() {
return Dependency.forInjectionPoints(new ImmutableSet.Builder<InjectionPoint>()
.add(getConstructor())
@ -241,18 +253,19 @@ final class ConstructorBindingImpl<T> extends BindingImpl<T>
}
@SuppressWarnings("unchecked")
public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
throws ErrorsException {
checkState(constructorInjector != null, "Constructor not ready");
if (failIfNotLinked && !linked) {
throw errors.jitDisabled(key).toException();
public T get(InternalContext context, Dependency<?> dependency, boolean linked)
throws InternalProvisionException {
ConstructorInjector<T> localInjector = constructorInjector;
if (localInjector == null) {
throw new IllegalStateException("Constructor not ready");
}
if (!linked && failIfNotLinked) {
throw InternalProvisionException.jitDisabled(key);
}
// This may not actually be safe because it could return a super type of T (if that's all the
// client needs), but it should be OK in practice thanks to the wonders of erasure.
return (T) constructorInjector.construct(errors, context,
dependency.getKey().getTypeLiteral().getRawType(), provisionCallback);
return (T) localInjector.construct(context, dependency, provisionCallback);
}
}
}

View file

@ -2,6 +2,7 @@ package com.google.inject.internal;
import com.google.common.collect.ImmutableSet;
import com.google.inject.internal.ProvisionListenerStackCallback.ProvisionCallback;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.InjectionPoint;
import java.lang.reflect.InvocationTargetException;
@ -40,37 +41,39 @@ final class ConstructorInjector<T> {
* Construct an instance. Returns {@code Object} instead of {@code T} because
* it may return a proxy.
*/
Object construct(final Errors errors, final InternalContext context,
Class<?> expectedType,
Object construct(final InternalContext context,
Dependency<?> dependency,
ProvisionListenerStackCallback<T> provisionCallback)
throws ErrorsException {
throws InternalProvisionException {
final ConstructionContext<T> constructionContext = context.getConstructionContext(this);
// We have a circular reference between constructors. Return a proxy.
if (constructionContext.isConstructing()) {
// TODO (crazybob): if we can't proxy this object, can we proxy the other object?
return constructionContext.createProxy(
errors, context.getInjectorOptions(), expectedType);
context.getInjectorOptions(), dependency.getKey().getTypeLiteral().getRawType());
}
// If we're re-entering this factory while injecting fields or methods,
// return the same instance. This prevents infinite loops.
T t = constructionContext.getCurrentReference();
if (t != null) {
return t;
if (context.getInjectorOptions().disableCircularProxies) {
throw InternalProvisionException.circularDependenciesDisabled(
dependency.getKey().getTypeLiteral().getRawType());
} else {
return t;
}
}
constructionContext.startConstruction();
try {
// Optimization: Don't go through the callback stack if we have no listeners.
if (!provisionCallback.hasListeners()) {
return provision(errors, context, constructionContext);
if (provisionCallback == null) {
return provision(context, constructionContext);
} else {
return provisionCallback.provision(errors, context, new ProvisionCallback<T>() {
public T call() throws ErrorsException {
return provision(errors, context, constructionContext);
}
});
return provisionCallback.provision(context, () ->
provision(context, constructionContext));
}
} finally {
constructionContext.finishConstruction();
@ -80,12 +83,12 @@ final class ConstructorInjector<T> {
/**
* Provisions a new T.
*/
private T provision(Errors errors, InternalContext context,
ConstructionContext<T> constructionContext) throws ErrorsException {
private T provision(InternalContext context, ConstructionContext<T> constructionContext)
throws InternalProvisionException {
try {
T t;
try {
Object[] parameters = SingleParameterInjector.getAll(errors, context, parameterInjectors);
Object[] parameters = SingleParameterInjector.getAll(context, parameterInjectors);
t = constructionProxy.newInstance(parameters);
constructionContext.setProxyDelegates(t);
} finally {
@ -94,17 +97,17 @@ final class ConstructorInjector<T> {
// Store reference. If an injector re-enters this factory, they'll get the same reference.
constructionContext.setCurrentReference(t);
membersInjector.injectMembers(t, errors, context, false);
membersInjector.notifyListeners(t, errors);
MembersInjectorImpl<T> localMembersInjector = membersInjector;
localMembersInjector.injectMembers(t, context, false);
localMembersInjector.notifyListeners(t);
return t;
} catch (InvocationTargetException userException) {
Throwable cause = userException.getCause() != null
? userException.getCause()
: userException;
throw errors.withSource(constructionProxy.getInjectionPoint())
.errorInjectingConstructor(cause).toException();
throw InternalProvisionException.errorInjectingConstructor(cause)
.addSource(constructionProxy.getInjectionPoint());
} finally {
constructionContext.removeCurrentReference();
}

View file

@ -8,8 +8,8 @@ import com.google.inject.spi.InjectionPoint;
final class ConstructorInjectorStore {
private final InjectorImpl injector;
private final FailableCache<InjectionPoint, ConstructorInjector<?>> cache
= new FailableCache<InjectionPoint, ConstructorInjector<?>>() {
private final FailableCache<InjectionPoint, ConstructorInjector<?>> cache =
new FailableCache<>() {
@Override
protected ConstructorInjector<?> create(InjectionPoint constructorInjector, Errors errors)
throws ErrorsException {
@ -46,12 +46,12 @@ final class ConstructorInjectorStore {
throws ErrorsException {
int numErrorsBefore = errors.size();
SingleParameterInjector<?>[] constructorParameterInjectors
= injector.getParametersInjectors(injectionPoint.getDependencies(), errors);
SingleParameterInjector<?>[] constructorParameterInjectors =
injector.getParametersInjectors(injectionPoint.getDependencies(), errors);
@SuppressWarnings("unchecked") // the injector type agrees with the injection point type
MembersInjectorImpl<T> membersInjector = (MembersInjectorImpl<T>) injector.membersInjectorStore
.get(injectionPoint.getDeclaringType(), errors);
MembersInjectorImpl<T> membersInjector = (MembersInjectorImpl<T>)
injector.membersInjectorStore.get(injectionPoint.getDeclaringType(), errors);
ConstructionProxyFactory<T> factory = new DefaultConstructionProxyFactory<T>(injectionPoint);

View file

@ -1,5 +0,0 @@
package com.google.inject.internal;
interface ContextualCallable<T> {
T call(InternalContext context) throws ErrorsException;
}

View file

@ -1,7 +1,6 @@
package com.google.inject.internal;
import com.google.common.base.Preconditions;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.ListMultimap;
@ -12,7 +11,6 @@ import com.google.common.collect.Multimaps;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@ -43,7 +41,7 @@ interface CycleDetectingLock<ID> {
* In case no cycle is detected performance is O(threads creating singletons),
* in case cycle is detected performance is O(singleton locks).
*/
ListMultimap<Long, ID> lockOrDetectPotentialLocksCycle();
ListMultimap<Thread, ID> lockOrDetectPotentialLocksCycle();
/**
* Unlocks previously locked lock.
@ -82,7 +80,7 @@ interface CycleDetectingLock<ID> {
*
* Guarded by {@code this}.
*/
private final Multimap<Long, ReentrantCycleDetectingLock> locksOwnedByThread =
private static final Multimap<Thread, ReentrantCycleDetectingLock<?>> locksOwnedByThread =
LinkedHashMultimap.create();
/**
* Specifies lock that thread is currently waiting on to own it.
@ -100,22 +98,22 @@ interface CycleDetectingLock<ID> {
*
* Guarded by {@code this}.
*/
private Map<Long, ReentrantCycleDetectingLock> lockThreadIsWaitingOn = Maps.newHashMap();
private static Map<Thread, ReentrantCycleDetectingLock<?>> lockThreadIsWaitingOn = Maps.newHashMap();
/**
* Creates new lock within this factory context. We can guarantee that locks created by
* the same factory would not deadlock.
*
* @param newLockId lock id that would be used to report lock cycles if detected
* @param userLockId lock id that would be used to report lock cycles if detected
*/
CycleDetectingLock<ID> create(ID newLockId) {
return new ReentrantCycleDetectingLock(newLockId, new ReentrantLock());
CycleDetectingLock<ID> create(ID userLockId) {
return new ReentrantCycleDetectingLock<>(this, userLockId, new ReentrantLock());
}
/**
* The implementation for {@link CycleDetectingLock}.
*/
class ReentrantCycleDetectingLock implements CycleDetectingLock<ID> {
static class ReentrantCycleDetectingLock<ID> implements CycleDetectingLock<ID> {
/**
* Underlying lock used for actual waiting when no potential deadlocks are detected.
@ -125,50 +123,56 @@ interface CycleDetectingLock<ID> {
* User id for this lock.
*/
private final ID userLockId;
/** Factory that was used to create this lock. */
private final CycleDetectingLockFactory<ID> lockFactory;
/**
* Thread id for the thread that owned this lock. Nullable.
* Guarded by {@code CycleDetectingLockFactory.this}.
*/
private Long lockOwnerThreadId = null;
private Thread lockOwnerThread = null;
/**
* Number of times that thread owned this lock.
* Guarded by {@code CycleDetectingLockFactory.this}.
*/
private int lockReentranceCount = 0;
ReentrantCycleDetectingLock(ID userLockId, Lock lockImplementation) {
ReentrantCycleDetectingLock(
CycleDetectingLockFactory<ID> lockFactory, ID userLockId, Lock lockImplementation) {
this.lockFactory = lockFactory;
this.userLockId = Preconditions.checkNotNull(userLockId, "userLockId");
this.lockImplementation = Preconditions.checkNotNull(
lockImplementation, "lockImplementation");
}
@Override
public ListMultimap<Long, ID> lockOrDetectPotentialLocksCycle() {
final long currentThreadId = Thread.currentThread().getId();
synchronized (CycleDetectingLockFactory.this) {
public ListMultimap<Thread, ID> lockOrDetectPotentialLocksCycle() {
final Thread currentThread = Thread.currentThread();
synchronized (CycleDetectingLockFactory.class) {
checkState();
ListMultimap<Long, ID> locksInCycle = detectPotentialLocksCycle();
// Add this lock to the waiting map to ensure it is included in any reported lock cycle.
lockThreadIsWaitingOn.put(currentThread, this);
ListMultimap<Thread, ID> locksInCycle = detectPotentialLocksCycle();
if (!locksInCycle.isEmpty()) {
// We aren't actually going to wait for this lock, so remove it from the map.
lockThreadIsWaitingOn.remove(currentThread);
// potential deadlock is found, we don't try to take this lock
return locksInCycle;
}
lockThreadIsWaitingOn.put(currentThreadId, this);
}
// this may be blocking, but we don't expect it to cause a deadlock
lockImplementation.lock();
synchronized (CycleDetectingLockFactory.this) {
synchronized (CycleDetectingLockFactory.class) {
// current thread is no longer waiting on this lock
lockThreadIsWaitingOn.remove(currentThreadId);
lockThreadIsWaitingOn.remove(currentThread);
checkState();
// mark it as owned by us
lockOwnerThreadId = currentThreadId;
lockOwnerThread = currentThread;
lockReentranceCount++;
// add this lock to the list of locks owned by a current thread
locksOwnedByThread.put(currentThreadId, this);
locksOwnedByThread.put(currentThread, this);
}
// no deadlock is found, locking successful
return ImmutableListMultimap.of();
@ -176,12 +180,12 @@ interface CycleDetectingLock<ID> {
@Override
public void unlock() {
final long currentThreadId = Thread.currentThread().getId();
synchronized (CycleDetectingLockFactory.this) {
final Thread currentThread = Thread.currentThread();
synchronized (CycleDetectingLockFactory.class) {
checkState();
Preconditions.checkState(lockOwnerThreadId != null,
Preconditions.checkState(lockOwnerThread != null,
"Thread is trying to unlock a lock that is not locked");
Preconditions.checkState(lockOwnerThreadId == currentThreadId,
Preconditions.checkState(lockOwnerThread == currentThread,
"Thread is trying to unlock a lock owned by another thread");
// releasing underlying lock
@ -191,12 +195,12 @@ interface CycleDetectingLock<ID> {
lockReentranceCount--;
if (lockReentranceCount == 0) {
// we no longer own this lock
lockOwnerThreadId = null;
Preconditions.checkState(locksOwnedByThread.remove(currentThreadId, this),
lockOwnerThread = null;
Preconditions.checkState(locksOwnedByThread.remove(currentThread, this),
"Internal error: Can not find this lock in locks owned by a current thread");
if (locksOwnedByThread.get(currentThreadId).isEmpty()) {
if (locksOwnedByThread.get(currentThread).isEmpty()) {
// clearing memory
locksOwnedByThread.removeAll(currentThreadId);
locksOwnedByThread.removeAll(currentThread);
}
}
}
@ -206,14 +210,14 @@ interface CycleDetectingLock<ID> {
* Check consistency of an internal state.
*/
void checkState() throws IllegalStateException {
final long currentThreadId = Thread.currentThread().getId();
Preconditions.checkState(!lockThreadIsWaitingOn.containsKey(currentThreadId),
final Thread currentThread = Thread.currentThread();
Preconditions.checkState(!lockThreadIsWaitingOn.containsKey(currentThread),
"Internal error: Thread should not be in a waiting thread on a lock now");
if (lockOwnerThreadId != null) {
if (lockOwnerThread != null) {
// check state of a locked lock
Preconditions.checkState(lockReentranceCount >= 0,
"Internal error: Lock ownership and reentrance count internal states do not match");
Preconditions.checkState(locksOwnedByThread.get(lockOwnerThreadId).contains(this),
Preconditions.checkState(locksOwnedByThread.get(lockOwnerThread).contains(this),
"Internal error: Set of locks owned by a current thread and lock "
+ "ownership status do not match");
} else {
@ -233,74 +237,80 @@ interface CycleDetectingLock<ID> {
*
* @see CycleDetectingLock#lockOrDetectPotentialLocksCycle()
*/
private ListMultimap<Long, ID> detectPotentialLocksCycle() {
final long currentThreadId = Thread.currentThread().getId();
if (lockOwnerThreadId == null || lockOwnerThreadId == currentThreadId) {
private ListMultimap<Thread, ID> detectPotentialLocksCycle() {
final Thread currentThread = Thread.currentThread();
if (lockOwnerThread == null || lockOwnerThread == currentThread) {
// if nobody owns this lock, lock cycle is impossible
// if a current thread owns this lock, we let Guice to handle it
return ImmutableListMultimap.of();
}
ListMultimap<Long, ID> potentialLocksCycle = Multimaps.newListMultimap(
new LinkedHashMap<Long, Collection<ID>>(),
new Supplier<List<ID>>() {
@Override
public List<ID> get() {
return Lists.newArrayList();
}
});
ListMultimap<Thread, ID> potentialLocksCycle = Multimaps.newListMultimap(
new LinkedHashMap<>(), Lists::newArrayList);
// lock that is a part of a potential locks cycle, starts with current lock
ReentrantCycleDetectingLock lockOwnerWaitingOn = this;
ReentrantCycleDetectingLock<?> lockOwnerWaitingOn = this;
// try to find a dependency path between lock's owner thread and a current thread
while (lockOwnerWaitingOn != null && lockOwnerWaitingOn.lockOwnerThreadId != null) {
Long threadOwnerThreadWaits = lockOwnerWaitingOn.lockOwnerThreadId;
while (lockOwnerWaitingOn != null && lockOwnerWaitingOn.lockOwnerThread != null) {
Thread threadOwnerThreadWaits = lockOwnerWaitingOn.lockOwnerThread;
// in case locks cycle exists lock we're waiting for is part of it
potentialLocksCycle.putAll(threadOwnerThreadWaits,
getAllLockIdsAfter(threadOwnerThreadWaits, lockOwnerWaitingOn));
if (threadOwnerThreadWaits == currentThreadId) {
lockOwnerWaitingOn =
addAllLockIdsAfter(threadOwnerThreadWaits, lockOwnerWaitingOn, potentialLocksCycle);
if (threadOwnerThreadWaits == currentThread) {
// owner thread depends on current thread, cycle detected
return potentialLocksCycle;
}
// going for the next thread we wait on indirectly
lockOwnerWaitingOn = lockThreadIsWaitingOn.get(threadOwnerThreadWaits);
}
// no dependency path from an owner thread to a current thread
return ImmutableListMultimap.of();
}
/**
* Return locks owned by a thread after a lock specified, inclusive.
* Adds all locks held by the given thread that are after the given lock and then returns the
* lock the thread is currently waiting on, if any
*/
private List<ID> getAllLockIdsAfter(long threadId, ReentrantCycleDetectingLock lock) {
List<ID> ids = Lists.newArrayList();
private ReentrantCycleDetectingLock<?> addAllLockIdsAfter(
Thread thread,
ReentrantCycleDetectingLock<?> lock,
ListMultimap<Thread, ID> potentialLocksCycle) {
boolean found = false;
Collection<ReentrantCycleDetectingLock> ownedLocks = locksOwnedByThread.get(threadId);
Collection<ReentrantCycleDetectingLock<?>> ownedLocks = locksOwnedByThread.get(thread);
Preconditions.checkNotNull(ownedLocks,
"Internal error: No locks were found taken by a thread");
for (ReentrantCycleDetectingLock ownedLock : ownedLocks) {
for (ReentrantCycleDetectingLock<?> ownedLock : ownedLocks) {
if (ownedLock == lock) {
found = true;
}
if (found) {
ids.add(ownedLock.userLockId);
if (found && ownedLock.lockFactory == this.lockFactory) {
// All locks are stored in a shared map therefore there is no way to
// enforce type safety. We know that our cast is valid as we check for a lock's
// factory. If the lock was generated by the
// same factory it has to have same type as the current lock.
@SuppressWarnings("unchecked")
ID userLockId = (ID) ownedLock.userLockId;
potentialLocksCycle.put(thread, userLockId);
}
}
Preconditions.checkState(found, "Internal error: We can not find locks that "
+ "created a cycle that we detected");
return ids;
Preconditions.checkState(found,
"Internal error: We can not find locks that created a cycle that we detected");
ReentrantCycleDetectingLock<?> unownedLock = lockThreadIsWaitingOn.get(thread);
// If this thread is waiting for a lock add it to the cycle and return it
if (unownedLock != null && unownedLock.lockFactory == this.lockFactory) {
@SuppressWarnings("unchecked")
ID typed = (ID) unownedLock.userLockId;
potentialLocksCycle.put(thread, typed);
}
return unownedLock;
}
@Override
public String toString() {
// copy is made to prevent a data race
// no synchronization is used, potentially stale data, should be good enough
Long localLockOwnerThreadId = this.lockOwnerThreadId;
if (localLockOwnerThreadId != null) {
return String.format("CycleDetectingLock[%s][locked by %s]",
userLockId, localLockOwnerThreadId);
Thread thread = this.lockOwnerThread;
if (thread != null) {
return String.format("%s[%s][locked by %s]", super.toString(), userLockId, thread);
} else {
return String.format("CycleDetectingLock[%s][unlocked]", userLockId);
return String.format("%s[%s][unlocked]", super.toString(), userLockId);
}
}
}

View file

@ -0,0 +1,81 @@
package com.google.inject.internal;
import com.google.common.collect.ComparisonChain;
import com.google.common.collect.Ordering;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* Utility class for retrieving declared fields or methods in a particular order, because the JVM
* doesn't guarantee ordering for listing declared methods. We don't externally guarantee an
* ordering, but having a consistent ordering allows deterministic behavior and simpler tests.
*/
public final class DeclaredMembers {
private DeclaredMembers() {}
public static Field[] getDeclaredFields(Class<?> type) {
Field[] fields = type.getDeclaredFields();
Arrays.sort(fields, FIELD_ORDERING);
return fields;
}
public static Method[] getDeclaredMethods(Class<?> type) {
Method[] methods = type.getDeclaredMethods();
Arrays.sort(methods, METHOD_ORDERING);
return methods;
}
/**
* An ordering suitable for comparing two classes if they are loaded by the same classloader
*
* <p>Within a single classloader there can only be one class with a given name, so we just
* compare the names.
*/
private static final Ordering<Class<?>> CLASS_ORDERING =
new Ordering<>() {
@Override
public int compare(Class<?> o1, Class<?> o2) {
return o1.getName().compareTo(o2.getName());
}
};
/**
* An ordering suitable for comparing two fields if they are owned by the same class.
*
* <p>Within a single class it is sufficent to compare the non-generic field signature which
* consists of the field name and type.
*/
private static final Ordering<Field> FIELD_ORDERING =
new Ordering<>() {
@Override
public int compare(Field left, Field right) {
return ComparisonChain.start()
.compare(left.getName(), right.getName())
.compare(left.getType(), right.getType(), CLASS_ORDERING)
.result();
}
};
/**
* An ordering suitable for comparing two methods if they are owned by the same class.
*
* <p>Within a single class it is sufficient to compare the non-generic method signature which
* consists of the name, return type and parameter types.
*/
private static final Ordering<Method> METHOD_ORDERING =
new Ordering<>() {
@Override
public int compare(Method left, Method right) {
return ComparisonChain.start()
.compare(left.getName(), right.getName())
.compare(left.getReturnType(), right.getReturnType(), CLASS_ORDERING)
.compare(
Arrays.asList(left.getParameterTypes()),
Arrays.asList(right.getParameterTypes()),
CLASS_ORDERING.lexicographical())
.result();
}
};
}

View file

@ -24,13 +24,8 @@ final class DefaultConstructionProxyFactory<T> implements ConstructionProxyFacto
@SuppressWarnings("unchecked") // the injection point is for a constructor of T
final Constructor<T> constructor = (Constructor<T>) injectionPoint.getMember();
// Use FastConstructor if the constructor is public.
if (Modifier.isPublic(constructor.getModifiers())) {
Class<T> classToConstruct = constructor.getDeclaringClass();
if (!Modifier.isPublic(classToConstruct.getModifiers())) {
constructor.setAccessible(true);
}
} else {
if (!Modifier.isPublic(constructor.getDeclaringClass().getModifiers()) ||
!Modifier.isPublic(constructor.getModifiers())) {
constructor.setAccessible(true);
}
@ -38,17 +33,17 @@ final class DefaultConstructionProxyFactory<T> implements ConstructionProxyFacto
public T newInstance(Object... arguments) throws InvocationTargetException {
try {
return constructor.newInstance(arguments);
} catch (InstantiationException e) {
} catch (InstantiationException | IllegalAccessException e) {
throw new AssertionError(e); // shouldn't happen, we know this is a concrete type
} catch (IllegalAccessException e) {
throw new AssertionError(e); // a security manager is blocking us, we're hosed
}
}
@Override
public InjectionPoint getInjectionPoint() {
return injectionPoint;
}
@Override
public Constructor<T> getConstructor() {
return constructor;
}

View file

@ -32,13 +32,13 @@ final class DeferredLookups implements Lookups {
}
public <T> Provider<T> getProvider(Key<T> key) {
ProviderLookup<T> lookup = new ProviderLookup<T>(key, key);
ProviderLookup<T> lookup = new ProviderLookup<>(key, key);
lookups.add(lookup);
return lookup.getProvider();
}
public <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> type) {
MembersInjectorLookup<T> lookup = new MembersInjectorLookup<T>(type, type);
MembersInjectorLookup<T> lookup = new MembersInjectorLookup<>(type, type);
lookups.add(lookup);
return lookup.getMembersInjector();
}

View file

@ -1,4 +1,4 @@
package com.google.inject.multibindings;
package com.google.inject.internal;
import com.google.inject.BindingAnnotation;
@ -25,7 +25,6 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
enum Type {
MAPBINDER,
MULTIBINDER,
OPTIONALBINDER;
MULTIBINDER;
}
}

View file

@ -43,6 +43,7 @@ final class EncounterImpl<T> implements TypeEncounter<T> {
: ImmutableSet.copyOf(injectionListeners);
}
@Override
public void register(MembersInjector<? super T> membersInjector) {
checkState(valid, "Encounters may not be used after hear() returns.");
@ -53,6 +54,7 @@ final class EncounterImpl<T> implements TypeEncounter<T> {
membersInjectors.add(membersInjector);
}
@Override
public void register(InjectionListener<? super T> injectionListener) {
checkState(valid, "Encounters may not be used after hear() returns.");
@ -63,36 +65,43 @@ final class EncounterImpl<T> implements TypeEncounter<T> {
injectionListeners.add(injectionListener);
}
@Override
public void addError(String message, Object... arguments) {
checkState(valid, "Encounters may not be used after hear() returns.");
errors.addMessage(message, arguments);
}
@Override
public void addError(Throwable t) {
checkState(valid, "Encounters may not be used after hear() returns.");
errors.errorInUserCode(t, "An exception was caught and reported. Message: %s", t.getMessage());
}
@Override
public void addError(Message message) {
checkState(valid, "Encounters may not be used after hear() returns.");
errors.addMessage(message);
}
@Override
public <T> Provider<T> getProvider(Key<T> key) {
checkState(valid, "Encounters may not be used after hear() returns.");
return lookups.getProvider(key);
}
@Override
public <T> Provider<T> getProvider(Class<T> type) {
return getProvider(Key.get(type));
}
@Override
public <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> typeLiteral) {
checkState(valid, "Encounters may not be used after hear() returns.");
return lookups.getMembersInjector(typeLiteral);
}
@Override
public <T> MembersInjector<T> getMembersInjector(Class<T> type) {
return getMembersInjector(TypeLiteral.get(type));
}
}
}

View file

@ -4,41 +4,33 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Ordering;
import com.google.common.primitives.Primitives;
import com.google.inject.Binding;
import com.google.inject.ConfigurationException;
import com.google.inject.CreationException;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.MembersInjector;
import com.google.inject.Provider;
import com.google.inject.Provides;
import com.google.inject.ProvisionException;
import com.google.inject.Scope;
import com.google.inject.TypeLiteral;
import com.google.inject.internal.util.Classes;
import com.google.inject.internal.util.SourceProvider;
import com.google.inject.internal.util.StackTraceElements;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.ElementSource;
import com.google.inject.spi.InjectionListener;
import com.google.inject.spi.InjectionPoint;
import com.google.inject.spi.Message;
import com.google.inject.spi.ScopeBinding;
import com.google.inject.spi.TypeConverterBinding;
import com.google.inject.spi.TypeListenerBinding;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Formatter;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* A collection of error messages. If this type is passed as a method parameter, the method is
@ -56,43 +48,40 @@ import java.util.concurrent.ConcurrentHashMap;
*/
public final class Errors {
private static final Set<Dependency<?>> warnedDependencies =
Collections.newSetFromMap(new ConcurrentHashMap<Dependency<?>, Boolean>());
private static final String CONSTRUCTOR_RULES =
"Classes must have either one (and only one) constructor "
+ "annotated with @Inject or a zero-argument constructor that is not private.";
private static final Collection<Converter<?>> converters = ImmutableList.of(
new Converter<Class>(Class.class) {
@Override
public String toString(Class c) {
return c.getName();
}
},
new Converter<Member>(Member.class) {
@Override
public String toString(Member member) {
return Classes.toString(member);
}
},
new Converter<Key>(Key.class) {
@Override
public String toString(Key key) {
if (key.getAnnotationType() != null) {
return key.getTypeLiteral() + " annotated with "
+ (key.getAnnotation() != null ? key.getAnnotation() : key.getAnnotationType());
} else {
return key.getTypeLiteral().toString();
}
}
});
/**
* The root errors object. Used to access the list of error messages.
*/
/** When a binding is not found, show at most this many bindings with the same type */
private static final int MAX_MATCHING_TYPES_REPORTED = 3;
/** When a binding is not found, show at most this many bindings that have some similarities */
private static final int MAX_RELATED_TYPES_REPORTED = 3;
static <T> T checkNotNull(T reference, String name) {
if (reference != null) {
return reference;
}
NullPointerException npe = new NullPointerException(name);
throw new ConfigurationException(ImmutableSet.of(new Message(npe.toString(), npe)));
}
static void checkConfiguration(boolean condition, String format, Object... args) {
if (condition) {
return;
}
throw new ConfigurationException(ImmutableSet.of(new Message(Messages.format(format, args))));
}
private static final ImmutableSet<Class<?>> COMMON_AMBIGUOUS_TYPES =
ImmutableSet.<Class<?>>builder()
.add(Object.class)
.add(String.class)
.addAll(Primitives.allWrapperTypes())
.build();
/** The root errors object. Used to access the list of error messages. */
private final Errors root;
/**
* The parent errors object. Used to obtain the chain of source objects.
*/
/** The parent errors object. Used to obtain the chain of source objects. */
private final Errors parent;
/**
* The leaf source for errors added here.
*/
@ -120,246 +109,101 @@ public final class Errors {
this.source = source;
}
public static Collection<Message> getMessagesFromThrowable(Throwable throwable) {
if (throwable instanceof ProvisionException) {
return ((ProvisionException) throwable).getErrorMessages();
} else if (throwable instanceof ConfigurationException) {
return ((ConfigurationException) throwable).getErrorMessages();
} else if (throwable instanceof CreationException) {
return ((CreationException) throwable).getErrorMessages();
} else {
return ImmutableSet.of();
}
}
public static String format(String messageFormat, Object... arguments) {
for (int i = 0; i < arguments.length; i++) {
arguments[i] = Errors.convert(arguments[i]);
}
return String.format(messageFormat, arguments);
}
/**
* Returns the formatted message for an exception with the specified messages.
*/
public static String format(String heading, Collection<Message> errorMessages) {
Formatter fmt = new Formatter().format(heading).format(":%n%n");
int index = 1;
boolean displayCauses = getOnlyCause(errorMessages) == null;
for (Message errorMessage : errorMessages) {
fmt.format("%s) %s%n", index++, errorMessage.getMessage());
List<Object> dependencies = errorMessage.getSources();
for (int i = dependencies.size() - 1; i >= 0; i--) {
Object source = dependencies.get(i);
formatSource(fmt, source);
}
Throwable cause = errorMessage.getCause();
if (displayCauses && cause != null) {
StringWriter writer = new StringWriter();
cause.printStackTrace(new PrintWriter(writer));
fmt.format("Caused by: %s", writer.getBuffer());
}
fmt.format("%n");
}
if (errorMessages.size() == 1) {
fmt.format("1 error");
} else {
fmt.format("%s errors", errorMessages.size());
}
return fmt.toString();
}
/**
* Returns the cause throwable if there is exactly one cause in {@code messages}. If there are
* zero or multiple messages with causes, null is returned.
*/
public static Throwable getOnlyCause(Collection<Message> messages) {
Throwable onlyCause = null;
for (Message message : messages) {
Throwable messageCause = message.getCause();
if (messageCause == null) {
continue;
}
if (onlyCause != null) {
return null;
}
onlyCause = messageCause;
}
return onlyCause;
}
public static Object convert(Object o) {
ElementSource source = null;
if (o instanceof ElementSource) {
source = (ElementSource) o;
o = source.getDeclaringSource();
}
return convert(o, source);
}
public static Object convert(Object o, ElementSource source) {
for (Converter<?> converter : converters) {
if (converter.appliesTo(o)) {
return appendModules(converter.convert(o), source);
}
}
return appendModules(o, source);
}
private static Object appendModules(Object source, ElementSource elementSource) {
String modules = moduleSourceString(elementSource);
if (modules.length() == 0) {
return source;
} else {
return source + modules;
}
}
private static String moduleSourceString(ElementSource elementSource) {
// if we only have one module (or don't know what they are), then don't bother
// reporting it, because the source already is going to report exactly that module.
if (elementSource == null) {
return "";
}
List<String> modules = Lists.newArrayList(elementSource.getModuleClassNames());
// Insert any original element sources w/ module info into the path.
while (elementSource.getOriginalElementSource() != null) {
elementSource = elementSource.getOriginalElementSource();
modules.addAll(0, elementSource.getModuleClassNames());
}
if (modules.size() <= 1) {
return "";
}
// Ideally we'd do:
// return Joiner.on(" -> ")
// .appendTo(new StringBuilder(" (via modules: "), Lists.reverse(modules))
// .append(")").toString();
// ... but for some reason we can't find Lists.reverse, so do it the boring way.
StringBuilder builder = new StringBuilder(" (via modules: ");
for (int i = modules.size() - 1; i >= 0; i--) {
builder.append(modules.get(i));
if (i != 0) {
builder.append(" -> ");
}
}
builder.append(")");
return builder.toString();
}
public static void formatSource(Formatter formatter, Object source) {
ElementSource elementSource = null;
if (source instanceof ElementSource) {
elementSource = (ElementSource) source;
source = elementSource.getDeclaringSource();
}
formatSource(formatter, source, elementSource);
}
public static void formatSource(Formatter formatter, Object source, ElementSource elementSource) {
String modules = moduleSourceString(elementSource);
if (source instanceof Dependency) {
Dependency<?> dependency = (Dependency<?>) source;
InjectionPoint injectionPoint = dependency.getInjectionPoint();
if (injectionPoint != null) {
formatInjectionPoint(formatter, dependency, injectionPoint, elementSource);
} else {
formatSource(formatter, dependency.getKey(), elementSource);
}
} else if (source instanceof InjectionPoint) {
formatInjectionPoint(formatter, null, (InjectionPoint) source, elementSource);
} else if (source instanceof Class) {
formatter.format(" at %s%s%n", StackTraceElements.forType((Class<?>) source), modules);
} else if (source instanceof Member) {
formatter.format(" at %s%s%n", StackTraceElements.forMember((Member) source), modules);
} else if (source instanceof TypeLiteral) {
formatter.format(" while locating %s%s%n", source, modules);
} else if (source instanceof Key) {
Key<?> key = (Key<?>) source;
formatter.format(" while locating %s%n", convert(key, elementSource));
} else if (source instanceof Thread) {
formatter.format(" in thread %s%n", source);
} else {
formatter.format(" at %s%s%n", source, modules);
}
}
public static void formatInjectionPoint(Formatter formatter, Dependency<?> dependency,
InjectionPoint injectionPoint, ElementSource elementSource) {
Member member = injectionPoint.getMember();
Class<? extends Member> memberType = Classes.memberType(member);
if (memberType == Field.class) {
dependency = injectionPoint.getDependencies().get(0);
formatter.format(" while locating %s%n", convert(dependency.getKey(), elementSource));
formatter.format(" for field at %s%n", StackTraceElements.forMember(member));
} else if (dependency != null) {
formatter.format(" while locating %s%n", convert(dependency.getKey(), elementSource));
formatter.format(" for parameter %s at %s%n",
dependency.getParameterIndex(), StackTraceElements.forMember(member));
} else {
formatSource(formatter, injectionPoint.getMember());
}
}
/**
* Returns an instance that uses {@code source} as a reference point for newly added errors.
*/
/** Returns an instance that uses {@code source} as a reference point for newly added errors. */
public Errors withSource(Object source) {
return source == this.source || source == SourceProvider.UNKNOWN_SOURCE
? this
: new Errors(this, source);
}
/**
* We use a fairly generic error message here. The motivation is to share the
* same message for both bind time errors:
* <pre><code>Guice.createInjector(new AbstractModule() {
* public void configure() {
* bind(Runnable.class);
* }
* }</code></pre>
* ...and at provide-time errors:
* <pre><code>Guice.createInjector().getInstance(Runnable.class);</code></pre>
* Otherwise we need to know who's calling when resolving a just-in-time
* binding, which makes things unnecessarily complex.
*/
public Errors missingImplementation(Key key) {
public Errors missingImplementation(Key<?> key) {
return addMessage("No implementation for %s was bound.", key);
}
public Errors jitDisabled(Key key) {
/** Within guice's core, allow for better missing binding messages */
<T> Errors missingImplementationWithHint(Key<T> key, Injector injector) {
StringBuilder sb = new StringBuilder();
sb.append(Messages.format("No implementation for %s was bound.", key));
// Keys which have similar strings as the desired key
List<String> possibleMatches = new ArrayList<>();
// Check for other keys that may have the same type,
// but not the same annotation
TypeLiteral<T> type = key.getTypeLiteral();
List<Binding<T>> sameTypes = injector.findBindingsByType(type);
if (!sameTypes.isEmpty()) {
sb.append(Messages.format("%n Did you mean?"));
int howMany = Math.min(sameTypes.size(), MAX_MATCHING_TYPES_REPORTED);
for (int i = 0; i < howMany; ++i) {
// TODO: Look into a better way to prioritize suggestions. For example, possbily
// use levenshtein distance of the given annotation vs actual annotation.
sb.append(Messages.format("%n * %s", sameTypes.get(i).getKey()));
}
int remaining = sameTypes.size() - MAX_MATCHING_TYPES_REPORTED;
if (remaining > 0) {
String plural = (remaining == 1) ? "" : "s";
sb.append(Messages.format("%n %d more binding%s with other annotations.", remaining, plural));
}
} else {
// For now, do a simple substring search for possibilities. This can help spot
// issues when there are generics being used (such as a wrapper class) and the
// user has forgotten they need to bind based on the wrapper, not the underlying
// class. In the future, consider doing a strict in-depth type search.
// TODO: Look into a better way to prioritize suggestions. For example, possbily
// use levenshtein distance of the type literal strings.
String want = type.toString();
Map<Key<?>, Binding<?>> bindingMap = injector.getAllBindings();
for (Key<?> bindingKey : bindingMap.keySet()) {
String have = bindingKey.getTypeLiteral().toString();
if (have.contains(want) || want.contains(have)) {
Formatter fmt = new Formatter();
Messages.formatSource(fmt, bindingMap.get(bindingKey).getSource());
String match = String.format("%s bound%s", Messages.convert(bindingKey), fmt.toString());
possibleMatches.add(match);
// TODO: Consider a check that if there are more than some number of results,
// don't suggest any.
if (possibleMatches.size() > MAX_RELATED_TYPES_REPORTED) {
// Early exit if we have found more than we need.
break;
}
}
}
if ((possibleMatches.size() > 0) && (possibleMatches.size() <= MAX_RELATED_TYPES_REPORTED)) {
sb.append(Messages.format("%n Did you mean?"));
for (String possibleMatch : possibleMatches) {
sb.append(Messages.format("%n %s", possibleMatch));
}
}
}
// If where are no possibilities to suggest, then handle the case of missing
// annotations on simple types. This is usually a bad idea.
if (sameTypes.isEmpty()
&& possibleMatches.isEmpty()
&& key.getAnnotationType() == null
&& COMMON_AMBIGUOUS_TYPES.contains(key.getTypeLiteral().getRawType())) {
// We don't recommend using such simple types without annotations.
sb.append(Messages.format("%nThe key seems very generic, did you forget an annotation?"));
}
return addMessage(sb.toString());
}
public Errors jitDisabled(Key<?> key) {
return addMessage("Explicit bindings are required and %s is not explicitly bound.", key);
}
public Errors jitDisabledInParent(Key<?> key) {
return addMessage(
"Explicit bindings are required and %s would be bound in a parent injector.%n"
return addMessage("Explicit bindings are required and %s would be bound in a parent injector.%n"
+ "Please add an explicit binding for it, either in the child or the parent.",
key);
}
public Errors atInjectRequired(Class clazz) {
return addMessage(
"Explicit @Inject annotations are required on constructors,"
public Errors atInjectRequired(Class<?> clazz) {
return addMessage("Explicit @Inject annotations are required on constructors,"
+ " but %s has no constructors annotated with @Inject.",
clazz);
}
@ -368,7 +212,7 @@ public final class Errors {
TypeLiteral<?> type, TypeConverterBinding typeConverterBinding) {
return addMessage("Received null converting '%s' (bound at %s) to %s%n"
+ " using %s.",
stringValue, convert(source), type, typeConverterBinding);
stringValue, Messages.convert(source), type, typeConverterBinding);
}
public Errors conversionTypeError(String stringValue, Object source, TypeLiteral<?> type,
@ -376,7 +220,7 @@ public final class Errors {
return addMessage("Type mismatch converting '%s' (bound at %s) to %s%n"
+ " using %s.%n"
+ " Converter returned %s.",
stringValue, convert(source), type, typeConverterBinding, converted);
stringValue, Messages.convert(source), type, typeConverterBinding, converted);
}
public Errors conversionError(String stringValue, Object source,
@ -384,7 +228,7 @@ public final class Errors {
return errorInUserCode(cause, "Error converting '%s' (bound at %s) to %s%n"
+ " using %s.%n"
+ " Reason: %s",
stringValue, convert(source), type, typeConverterBinding, cause);
stringValue, Messages.convert(source), type, typeConverterBinding, cause);
}
public Errors ambiguousTypeConversion(String stringValue, Object source, TypeLiteral<?> type,
@ -393,18 +237,13 @@ public final class Errors {
+ " %s and%n"
+ " %s.%n"
+ " Please adjust your type converter configuration to avoid overlapping matches.",
stringValue, convert(source), type, a, b);
stringValue, Messages.convert(source), type, a, b);
}
public Errors bindingToProvider() {
return addMessage("Binding to Provider is not allowed.");
}
public Errors subtypeNotProvided(Class<? extends Provider<?>> providerType,
Class<?> type) {
return addMessage("%s doesn't provide instances of %s.", providerType, type);
}
public Errors notASubtype(Class<?> implementationType, Class<?> type) {
return addMessage("%s doesn't extend %s.", implementationType, type);
}
@ -418,14 +257,14 @@ public final class Errors {
}
public Errors missingRuntimeRetention(Class<? extends Annotation> annotation) {
return addMessage(format("Please annotate %s with @Retention(RUNTIME).", annotation));
return addMessage(Messages.format("Please annotate %s with @Retention(RUNTIME).", annotation));
}
public Errors missingScopeAnnotation(Class<? extends Annotation> annotation) {
return addMessage(format("Please annotate %s with @ScopeAnnotation.", annotation));
return addMessage(Messages.format("Please annotate %s with @ScopeAnnotation.", annotation));
}
public Errors optionalConstructor(Constructor constructor) {
public Errors optionalConstructor(Constructor<?> constructor) {
return addMessage("%s is annotated @Inject(optional=true), "
+ "but constructors cannot be optional.", constructor);
}
@ -441,7 +280,7 @@ public final class Errors {
public Errors scopeAnnotationOnAbstractType(
Class<? extends Annotation> scopeAnnotation, Class<?> type, Object source) {
return addMessage("%s is annotated with %s, but scope annotations are not supported "
+ "for abstract types.%n Bound at %s.", type, scopeAnnotation, convert(source));
+ "for abstract types.%n Bound at %s.", type, scopeAnnotation, Messages.convert(source));
}
public Errors misplacedBindingAnnotation(Member member, Annotation bindingAnnotation) {
@ -449,14 +288,27 @@ public final class Errors {
+ "to its parameters instead.", member, bindingAnnotation);
}
public Errors missingConstructor(Class<?> implementation) {
return addMessage("Could not find a suitable constructor in %s. " + CONSTRUCTOR_RULES,
implementation);
private static final String CONSTRUCTOR_RULES =
"Injectable classes must have either one (and only one) constructor annotated with @Inject"
+ " or a zero-argument constructor that is not private.";
public Errors missingConstructor(TypeLiteral<?> type) {
// Don't bother including the type in the message twice, unless the type is generic (i.e. the
// type has generics that the raw class loses)
String typeString = type.toString();
String rawTypeString = MoreTypes.getRawType(type.getType()).getName();
return addMessage(
"No implementation for %s (with no qualifier annotation) was bound, and could not find an"
+ " injectable constructor%s. %s",
typeString,
typeString.equals(rawTypeString) ? "" : " in " + rawTypeString,
CONSTRUCTOR_RULES);
}
public Errors tooManyConstructors(Class<?> implementation) {
return addMessage("%s has more than one constructor annotated with @Inject. "
+ CONSTRUCTOR_RULES, implementation);
return addMessage(
"%s has more than one constructor annotated with @Inject. %s",
implementation, CONSTRUCTOR_RULES);
}
public Errors constructorNotDefinedByType(Constructor<?> constructor, TypeLiteral<?> type) {
@ -518,7 +370,7 @@ public final class Errors {
}
public Errors bindingAlreadySet(Key<?> key, Object source) {
return addMessage("A binding to %s was already configured at %s.", key, convert(source));
return addMessage("A binding to %s was already configured at %s.", key, Messages.convert(source));
}
public Errors jitBindingAlreadySet(Key<?> key) {
@ -534,24 +386,19 @@ public final class Errors {
allSources.format("%n bound at %s", source);
}
}
Errors errors = addMessage(
return addMessage(
"Unable to create binding for %s."
+ " It was already configured on one or more child injectors or private modules"
+ "%s%n"
+ " If it was in a PrivateModule, did you forget to expose the binding?",
key, allSources.out());
return errors;
}
public Errors errorCheckingDuplicateBinding(Key<?> key, Object source, Throwable t) {
return addMessage(
"A binding to %s was already configured at %s and an error was thrown "
+ "while checking duplicate bindings. Error: %s",
key, convert(source), t);
}
public Errors errorInjectingMethod(Throwable cause) {
return errorInUserCode(cause, "Error injecting method, %s", cause);
key, Messages.convert(source), t);
}
public Errors errorNotifyingTypeListener(TypeListenerBinding listener,
@ -559,28 +406,7 @@ public final class Errors {
return errorInUserCode(cause,
"Error notifying TypeListener %s (bound at %s) of %s.%n"
+ " Reason: %s",
listener.getListener(), convert(listener.getSource()), type, cause);
}
public Errors errorInjectingConstructor(Throwable cause) {
return errorInUserCode(cause, "Error injecting constructor, %s", cause);
}
public Errors errorInProvider(RuntimeException runtimeException) {
Throwable unwrapped = unwrap(runtimeException);
return errorInUserCode(unwrapped, "Error in custom provider, %s", unwrapped);
}
public Errors errorInUserInjector(
MembersInjector<?> listener, TypeLiteral<?> type, RuntimeException cause) {
return errorInUserCode(cause, "Error injecting %s using %s.%n"
+ " Reason: %s", type, listener, cause);
}
public Errors errorNotifyingInjectionListener(
InjectionListener<?> listener, TypeLiteral<?> type, RuntimeException cause) {
return errorInUserCode(cause, "Error notifying InjectionListener %s of %s.%n"
+ " Reason: %s", listener, type, cause);
listener.getListener(), Messages.convert(listener.getSource()), type, cause);
}
public Errors exposedButNotBound(Key<?> key) {
@ -595,6 +421,18 @@ public final class Errors {
return errorInUserCode(cause, "Unable to method intercept: %s", clazz);
}
public static Collection<Message> getMessagesFromThrowable(Throwable throwable) {
if (throwable instanceof ProvisionException) {
return ((ProvisionException) throwable).getErrorMessages();
} else if (throwable instanceof ConfigurationException) {
return ((ConfigurationException) throwable).getErrorMessages();
} else if (throwable instanceof CreationException) {
return ((CreationException) throwable).getErrorMessages();
} else {
return ImmutableSet.of();
}
}
public Errors errorInUserCode(Throwable cause, String messageFormat, Object... arguments) {
Collection<Message> messages = getMessagesFromThrowable(cause);
@ -605,13 +443,6 @@ public final class Errors {
}
}
private Throwable unwrap(RuntimeException runtimeException) {
if (runtimeException instanceof Exceptions.UnhandledCheckedUserException) {
return runtimeException.getCause();
} else {
return runtimeException;
}
}
public Errors cannotInjectRawProvider() {
return addMessage("Cannot inject a Provider that has no type parameter");
@ -629,23 +460,10 @@ public final class Errors {
return addMessage("Cannot inject a TypeLiteral that has no type parameter");
}
public Errors cannotSatisfyCircularDependency(Class<?> expectedType) {
return addMessage(
"Tried proxying %s to support a circular dependency, but it is not an interface.",
expectedType);
}
public Errors circularProxiesDisabled(Class<?> expectedType) {
return addMessage(
"Tried proxying %s to support a circular dependency, but circular proxies are disabled.",
expectedType);
}
public void throwCreationExceptionIfErrorsExist() {
if (!hasErrors()) {
return;
}
throw new CreationException(getMessages());
}
@ -653,28 +471,13 @@ public final class Errors {
if (!hasErrors()) {
return;
}
throw new ConfigurationException(getMessages());
}
public void throwProvisionExceptionIfErrorsExist() {
if (!hasErrors()) {
return;
}
throw new ProvisionException(getMessages());
}
private Message merge(Message message) {
List<Object> sources = Lists.newArrayList();
sources.addAll(getSources());
sources.addAll(message.getSources());
return new Message(sources, message.getMessage(), message.getCause());
}
public Errors merge(Collection<Message> messages) {
List<Object> sources = getSources();
for (Message message : messages) {
addMessage(merge(message));
addMessage(Messages.mergeSources(sources, message));
}
return this;
}
@ -683,11 +486,15 @@ public final class Errors {
if (moreErrors.root == root || moreErrors.root.errors == null) {
return this;
}
merge(moreErrors.root.errors);
return this;
}
public Errors merge(InternalProvisionException ipe) {
merge(ipe.getErrors());
return this;
}
public List<Object> getSources() {
List<Object> sources = Lists.newArrayList();
for (Errors e = this; e != null; e = e.parent) {
@ -719,8 +526,7 @@ public final class Errors {
}
private Errors addMessage(Throwable cause, String messageFormat, Object... arguments) {
String message = format(messageFormat, arguments);
addMessage(new Message(getSources(), message, cause));
addMessage(Messages.create(cause, getSources(), messageFormat, arguments));
return this;
}
@ -736,7 +542,6 @@ public final class Errors {
if (root.errors == null) {
return ImmutableList.of();
}
return new Ordering<Message>() {
@Override
public int compare(Message a, Message b) {
@ -745,73 +550,7 @@ public final class Errors {
}.sortedCopy(root.errors);
}
/**
* Returns {@code value} if it is non-null allowed to be null. Otherwise a message is added and
* an {@code ErrorsException} is thrown.
*/
public <T> T checkForNull(T value, Object source, Dependency<?> dependency)
throws ErrorsException {
if (value != null || dependency.isNullable()) {
return value;
}
// Hack to allow null parameters to @Provides methods, for backwards compatibility.
if (dependency.getInjectionPoint().getMember() instanceof Method) {
Method annotated = (Method) dependency.getInjectionPoint().getMember();
if (annotated.isAnnotationPresent(Provides.class)) {
switch (InternalFlags.getNullableProvidesOption()) {
case ERROR:
break; // break out & let the below exception happen
case IGNORE:
return value; // user doesn't care about injecting nulls to non-@Nullables.
case WARN:
// Warn only once, otherwise we spam logs too much.
if (!warnedDependencies.add(dependency)) {
return value;
}
/*logger.log(Level.WARNING,
"Guice injected null into parameter {0} of {1} (a {2}), please mark it @Nullable."
+ " Use -Dguice_check_nullable_provides_params=ERROR to turn this into an"
+ " error.",
new Object[]{
dependency.getParameterIndex(),
convert(dependency.getInjectionPoint().getMember()),
convert(dependency.getKey())});*/
return null; // log & exit.
}
}
}
int parameterIndex = dependency.getParameterIndex();
String parameterName = (parameterIndex != -1)
? "parameter " + parameterIndex + " of "
: "";
addMessage("null returned by binding at %s%n but %s%s is not @Nullable",
source, parameterName, dependency.getInjectionPoint().getMember());
throw toException();
}
public int size() {
return root.errors == null ? 0 : root.errors.size();
}
private static abstract class Converter<T> {
final Class<T> type;
Converter(Class<T> type) {
this.type = type;
}
boolean appliesTo(Object o) {
return o != null && type.isAssignableFrom(o.getClass());
}
String convert(Object o) {
return toString(type.cast(o));
}
abstract String toString(T t);
}
}

View file

@ -8,6 +8,9 @@ package com.google.inject.internal;
@SuppressWarnings("serial")
public class ErrorsException extends Exception {
// NOTE: this is used by Gin which is abandoned. So changing this API will prevent Gin users from
// upgrading Guice version.
private final Errors errors;
public ErrorsException(Errors errors) {

View file

@ -1,46 +0,0 @@
package com.google.inject.internal;
/**
* Rethrows user-code exceptions in wrapped exceptions so that Errors can target the correct
* exception.
*/
class Exceptions {
/**
* Rethrows the exception (or it's cause, if it has one) directly if possible.
* If it was a checked exception, this wraps the exception in a stack trace
* with no frames, so that the exception is shown immediately with no frames
* above it.
*/
public static RuntimeException rethrowCause(Throwable throwable) {
Throwable cause = throwable;
if (cause.getCause() != null) {
cause = cause.getCause();
}
return rethrow(cause);
}
/**
* Rethrows the exception.
*/
public static RuntimeException rethrow(Throwable throwable) {
if (throwable instanceof RuntimeException) {
throw (RuntimeException) throwable;
} else if (throwable instanceof Error) {
throw (Error) throwable;
} else {
throw new UnhandledCheckedUserException(throwable);
}
}
/**
* A marker exception class that we look for in order to unwrap the exception
* into the user exception, to provide a cleaner stack trace.
*/
@SuppressWarnings("serial")
static class UnhandledCheckedUserException extends RuntimeException {
public UnhandledCheckedUserException(Throwable cause) {
super(cause);
}
}
}

View file

@ -22,14 +22,17 @@ public final class ExposedBindingImpl<T> extends BindingImpl<T> implements Expos
this.privateElements = privateElements;
}
@Override
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
return visitor.visit(this);
}
@Override
public Set<Dependency<?>> getDependencies() {
return ImmutableSet.<Dependency<?>>of(Dependency.get(Key.get(Injector.class)));
}
@Override
public PrivateElements getPrivateElements() {
return privateElements;
}

View file

@ -18,6 +18,7 @@ final class ExposedKeyFactory<T> implements InternalFactory<T>, CreationListener
this.privateElements = privateElements;
}
@Override
public void notify(Errors errors) {
InjectorImpl privateInjector = (InjectorImpl) privateElements.getInjector();
BindingImpl<T> explicitBinding = privateInjector.state.getExplicitBinding(key);
@ -33,8 +34,8 @@ final class ExposedKeyFactory<T> implements InternalFactory<T>, CreationListener
this.delegate = explicitBinding;
}
public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
throws ErrorsException {
return delegate.getInternalFactory().get(errors, context, dependency, linked);
public T get(InternalContext context, Dependency<?> dependency, boolean linked)
throws InternalProvisionException {
return delegate.getInternalFactory().get(context, dependency, linked);
}
}

View file

@ -27,12 +27,14 @@ public class ExposureBuilder<T> implements AnnotatedElementBuilder {
}
}
@Override
public void annotatedWith(Class<? extends Annotation> annotationType) {
Preconditions.checkNotNull(annotationType, "annotationType");
checkNotAnnotated();
key = Key.get(key.getTypeLiteral(), annotationType);
}
@Override
public void annotatedWith(Annotation annotation) {
Preconditions.checkNotNull(annotation, "annotation");
checkNotAnnotated();

View file

@ -25,6 +25,7 @@ final class FactoryProxy<T> implements InternalFactory<T>, CreationListener {
this.source = source;
}
@Override
public void notify(final Errors errors) {
try {
targetFactory = injector.getInternalFactory(targetKey, errors.withSource(source), JitLimitation.NEW_OR_EXISTING_JIT);
@ -33,11 +34,15 @@ final class FactoryProxy<T> implements InternalFactory<T>, CreationListener {
}
}
public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
throws ErrorsException {
context.pushState(targetKey, source);
@Override
public T get(InternalContext context, Dependency<?> dependency, boolean linked)
throws InternalProvisionException {
Key<? extends T> localTargetKey = targetKey;
context.pushState(localTargetKey, source);
try {
return targetFactory.get(errors.withSource(targetKey), context, dependency, true);
return targetFactory.get(context, dependency, true);
} catch (InternalProvisionException ipe) {
throw ipe.addSource(localTargetKey);
} finally {
context.popState();
}

View file

@ -3,6 +3,9 @@ package com.google.inject.internal;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import java.util.Map;
/**
* Lazily creates (and caches) values for keys. If creating the value fails (with errors), an
@ -10,8 +13,8 @@ import com.google.common.cache.LoadingCache;
*/
public abstract class FailableCache<K, V> {
private final LoadingCache<K, Object> delegate = CacheBuilder.newBuilder().build(
new CacheLoader<K, Object>() {
private final LoadingCache<K, Object> delegate = CacheBuilder.newBuilder()
.build(new CacheLoader<>() {
public Object load(K key) {
Errors errors = new Errors();
V result = null;
@ -33,7 +36,7 @@ public abstract class FailableCache<K, V> {
throw errors.toException();
} else {
@SuppressWarnings("unchecked") // create returned a non-error result, so this is safe
V result = (V) resultOrError;
V result = (V) resultOrError;
return result;
}
}
@ -41,4 +44,14 @@ public abstract class FailableCache<K, V> {
boolean remove(K key) {
return delegate.asMap().remove(key) != null;
}
Map<K, V> asMap() {
return Maps.transformValues(Maps.filterValues(ImmutableMap.copyOf(delegate.asMap()),
resultOrError -> !(resultOrError instanceof Errors)),
resultOrError -> {
@SuppressWarnings("unchecked") // create returned a non-error result, so this is safe
V result = (V) resultOrError;
return result;
});
}
}

View file

@ -1,4 +1,4 @@
package com.google.inject.multibindings;
package com.google.inject.internal;
import com.google.common.base.Objects;
import com.google.inject.Binding;
@ -23,17 +23,22 @@ import java.lang.annotation.Annotation;
/**
* Visits bindings to return a {@code IndexedBinding} that can be used to emulate the binding
* deduplication that Guice internally performs.
*
* <p>Note: simply using equals/hashCode on the BindingImpls doesn't work because they all have
* unique annotations. This works around that by reimplementing equality semantics that ignores
* {@link Element#uniqueId()}. A better solution might be to introduce the idea of an 'anonymous'
* binding to guice, that might support this usecase directly.
*/
class Indexer extends DefaultBindingTargetVisitor<Object, Indexer.IndexedBinding>
public class Indexer extends DefaultBindingTargetVisitor<Object, Indexer.IndexedBinding>
implements BindingScopingVisitor<Object> {
private static final Object EAGER_SINGLETON = new Object();
final Injector injector;
private final Injector injector;
Indexer(Injector injector) {
public Indexer(Injector injector) {
this.injector = injector;
}
boolean isIndexable(Binding<?> binding) {
public boolean isIndexable(Binding<?> binding) {
return binding.getKey().getAnnotation() instanceof Element;
}
@ -126,7 +131,7 @@ class Indexer extends DefaultBindingTargetVisitor<Object, Indexer.IndexedBinding
PROVIDED_BY,
}
static class IndexedBinding {
public static class IndexedBinding {
final String annotationName;
final Element.Type annotationType;
final TypeLiteral<?> typeLiteral;
@ -134,7 +139,7 @@ class Indexer extends DefaultBindingTargetVisitor<Object, Indexer.IndexedBinding
final BindingType type;
final Object extraEquality;
IndexedBinding(Binding<?> binding, BindingType type, Object scope, Object extraEquality) {
public IndexedBinding(Binding<?> binding, BindingType type, Object scope, Object extraEquality) {
this.scope = scope;
this.type = type;
this.extraEquality = extraEquality;

View file

@ -3,17 +3,23 @@ package com.google.inject.internal;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.inject.Binding;
import com.google.inject.Key;
import com.google.inject.Scope;
import com.google.inject.TypeLiteral;
import com.google.inject.spi.InjectionRequest;
import com.google.inject.spi.MembersInjectorLookup;
import com.google.inject.spi.ModuleAnnotatedMethodScannerBinding;
import com.google.inject.spi.ProviderLookup;
import com.google.inject.spi.ProvisionListenerBinding;
import com.google.inject.spi.ScopeBinding;
import com.google.inject.spi.StaticInjectionRequest;
import com.google.inject.spi.TypeConverterBinding;
import com.google.inject.spi.TypeListenerBinding;
import java.lang.annotation.Annotation;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@ -27,9 +33,13 @@ final class InheritingState implements State {
// Must be a linked hashmap in order to preserve order of bindings in Modules.
private final Map<Key<?>, Binding<?>> explicitBindingsMutable = Maps.newLinkedHashMap();
private final Map<Key<?>, Binding<?>> explicitBindings
= Collections.unmodifiableMap(explicitBindingsMutable);
private final Map<Key<?>, Binding<?>> explicitBindings =
Collections.unmodifiableMap(explicitBindingsMutable);
private final Map<Class<? extends Annotation>, ScopeBinding> scopes = Maps.newHashMap();
private final Set<ProviderLookup<?>> providerLookups = Sets.newLinkedHashSet();
private final Set<StaticInjectionRequest> staticInjectionRequests = Sets.newLinkedHashSet();
private final Set<MembersInjectorLookup<?>> membersInjectorLookups = Sets.newLinkedHashSet();
private final Set<InjectionRequest<?>> injectionRequests = Sets.newLinkedHashSet();
private final List<TypeConverterBinding> converters = Lists.newArrayList();
private final List<TypeListenerBinding> typeListenerBindings = Lists.newArrayList();
private final List<ProvisionListenerBinding> provisionListenerBindings = Lists.newArrayList();
@ -43,41 +53,95 @@ final class InheritingState implements State {
this.blacklistedKeys = new WeakKeySet(lock);
}
@Override
public State parent() {
return parent;
}
@SuppressWarnings("unchecked") // we only put in BindingImpls that match their key types
@Override
public <T> BindingImpl<T> getExplicitBinding(Key<T> key) {
Binding<?> binding = explicitBindings.get(key);
return binding != null ? (BindingImpl<T>) binding : parent.getExplicitBinding(key);
}
@Override
public Map<Key<?>, Binding<?>> getExplicitBindingsThisLevel() {
return explicitBindings;
}
@Override
public void putBinding(Key<?> key, BindingImpl<?> binding) {
explicitBindingsMutable.put(key, binding);
}
@Override
public void putProviderLookup(ProviderLookup<?> lookup) {
providerLookups.add(lookup);
}
@Override
public Set<ProviderLookup<?>> getProviderLookupsThisLevel() {
return providerLookups;
}
@Override
public void putStaticInjectionRequest(StaticInjectionRequest staticInjectionRequest) {
staticInjectionRequests.add(staticInjectionRequest);
}
@Override
public Set<StaticInjectionRequest> getStaticInjectionRequestsThisLevel() {
return staticInjectionRequests;
}
@Override
public void putInjectionRequest(InjectionRequest<?> injectionRequest) {
injectionRequests.add(injectionRequest);
}
@Override
public Set<InjectionRequest<?>> getInjectionRequestsThisLevel() {
return injectionRequests;
}
@Override
public void putMembersInjectorLookup(MembersInjectorLookup<?> membersInjectorLookup) {
membersInjectorLookups.add(membersInjectorLookup);
}
@Override
public Set<MembersInjectorLookup<?>> getMembersInjectorLookupsThisLevel() {
return membersInjectorLookups;
}
@Override
public ScopeBinding getScopeBinding(Class<? extends Annotation> annotationType) {
ScopeBinding scopeBinding = scopes.get(annotationType);
return scopeBinding != null ? scopeBinding : parent.getScopeBinding(annotationType);
}
@Override
public void putScopeBinding(Class<? extends Annotation> annotationType, ScopeBinding scope) {
scopes.put(annotationType, scope);
}
@Override
public Collection<ScopeBinding> getScopeBindingsThisLevel() {
return scopes.values();
}
@Override
public Iterable<TypeConverterBinding> getConvertersThisLevel() {
return converters;
}
@Override
public void addConverter(TypeConverterBinding typeConverterBinding) {
converters.add(typeConverterBinding);
}
@Override
public TypeConverterBinding getConverter(
String stringValue, TypeLiteral<?> type, Errors errors, Object source) {
TypeConverterBinding matchingConverter = null;
@ -94,10 +158,12 @@ final class InheritingState implements State {
return matchingConverter;
}
@Override
public void addTypeListener(TypeListenerBinding listenerBinding) {
typeListenerBindings.add(listenerBinding);
}
@Override
public List<TypeListenerBinding> getTypeListenerBindings() {
List<TypeListenerBinding> parentBindings = parent.getTypeListenerBindings();
List<TypeListenerBinding> result =
@ -107,10 +173,17 @@ final class InheritingState implements State {
return result;
}
@Override
public List<TypeListenerBinding> getTypeListenerBindingsThisLevel() {
return typeListenerBindings;
}
@Override
public void addProvisionListener(ProvisionListenerBinding listenerBinding) {
provisionListenerBindings.add(listenerBinding);
}
@Override
public List<ProvisionListenerBinding> getProvisionListenerBindings() {
List<ProvisionListenerBinding> parentBindings = parent.getProvisionListenerBindings();
List<ProvisionListenerBinding> result =
@ -120,10 +193,17 @@ final class InheritingState implements State {
return result;
}
@Override
public List<ProvisionListenerBinding> getProvisionListenerBindingsThisLevel() {
return provisionListenerBindings;
}
@Override
public void addScanner(ModuleAnnotatedMethodScannerBinding scanner) {
scannerBindings.add(scanner);
}
@Override
public List<ModuleAnnotatedMethodScannerBinding> getScannerBindings() {
List<ModuleAnnotatedMethodScannerBinding> parentBindings = parent.getScannerBindings();
List<ModuleAnnotatedMethodScannerBinding> result =
@ -133,23 +213,33 @@ final class InheritingState implements State {
return result;
}
@Override
public List<ModuleAnnotatedMethodScannerBinding> getScannerBindingsThisLevel() {
return scannerBindings;
}
@Override
public void blacklist(Key<?> key, State state, Object source) {
parent.blacklist(key, state, source);
blacklistedKeys.add(key, state, source);
}
@Override
public boolean isBlacklisted(Key<?> key) {
return blacklistedKeys.contains(key);
}
@Override
public Set<Object> getSourcesForBlacklistedKey(Key<?> key) {
return blacklistedKeys.getSources(key);
}
@Override
public Object lock() {
return lock;
}
@Override
public Map<Class<? extends Annotation>, Scope> getScopes() {
ImmutableMap.Builder<Class<? extends Annotation>, Scope> builder = ImmutableMap.builder();
for (Map.Entry<Class<? extends Annotation>, ScopeBinding> entry : scopes.entrySet()) {

View file

@ -8,5 +8,5 @@ interface Initializable<T> {
/**
* Ensures the reference is initialized, then returns it.
*/
T get(Errors errors) throws ErrorsException;
T get() throws InternalProvisionException;
}

View file

@ -6,8 +6,8 @@ final class Initializables {
* Returns an initializable for an instance that requires no initialization.
*/
static <T> Initializable<T> of(final T instance) {
return new Initializable<T>() {
public T get(Errors errors) throws ErrorsException {
return new Initializable<>() {
public T get() {
return instance;
}

View file

@ -3,15 +3,16 @@ package com.google.inject.internal;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.inject.Binding;
import com.google.inject.Key;
import com.google.inject.Stage;
import com.google.inject.TypeLiteral;
import com.google.inject.spi.InjectionPoint;
import java.util.Map;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import static com.google.common.base.Preconditions.checkNotNull;
@ -23,27 +24,30 @@ import static com.google.common.base.Preconditions.checkNotNull;
*/
final class Initializer {
/**
* the only thread that we'll use to inject members.
*/
private final Thread creatingThread = Thread.currentThread();
/** Is set to true once {@link #validateOustandingInjections} is called. */
private volatile boolean validationStarted = false;
/**
* zero means everything is injected.
* Allows us to detect circular dependencies. It's only used during injectable reference
* initialization. After initialization direct access through volatile field is used.
*/
private final CountDownLatch ready = new CountDownLatch(1);
private final CycleDetectingLock.CycleDetectingLockFactory<Class<?>> cycleDetectingLockFactory =
new CycleDetectingLock.CycleDetectingLockFactory<>();
/**
* Maps from instances that need injection to the MembersInjector that will inject them.
* Instances that need injection during injector creation to a source that registered them. New
* references added before {@link #validateOustandingInjections}. Cleared up in {@link
* #injectAll}.
*/
private final Map<Object, MembersInjectorImpl<?>> pendingMembersInjectors =
private final List<InjectableReference<?>> pendingInjections = Lists.newArrayList();
/**
* Map that guarantees that no instance would get two references. New references added before
* {@link #validateOustandingInjections}. Cleared up in {@link #validateOustandingInjections}.
*/
private final IdentityHashMap<Object, InjectableReference<?>> initializablesCache =
Maps.newIdentityHashMap();
/**
* Maps instances that need injection to a source that registered them
*/
private final Map<Object, InjectableReference<?>> pendingInjection = Maps.newIdentityHashMap();
/**
* Registers an instance for member injection when that step is performed.
*
@ -55,21 +59,35 @@ final class Initializer {
<T> Initializable<T> requestInjection(InjectorImpl injector, T instance, Binding<T> binding,
Object source, Set<InjectionPoint> injectionPoints) {
checkNotNull(source);
Preconditions.checkState(
!validationStarted, "Member injection could not be requested after validation is started");
ProvisionListenerStackCallback<T> provisionCallback =
binding == null ? null : injector.provisionListenerStore.get(binding);
// short circuit if the object has no injections or listeners.
if (instance == null || (injectionPoints.isEmpty()
&& !injector.membersInjectorStore.hasTypeListeners()
&& (provisionCallback == null || !provisionCallback.hasListeners()))) {
&& (provisionCallback == null))) {
return Initializables.of(instance);
}
InjectableReference<T> initializable = new InjectableReference<T>(
injector, instance, binding == null ? null : binding.getKey(), provisionCallback, source);
pendingInjection.put(instance, initializable);
return initializable;
if (initializablesCache.containsKey(instance)) {
@SuppressWarnings("unchecked") // Map from T to InjectableReference<T>
Initializable<T> cached = (Initializable<T>) initializablesCache.get(instance);
return cached;
}
InjectableReference<T> injectableReference =
new InjectableReference<T>(
injector,
instance,
binding == null ? null : binding.getKey(),
provisionCallback,
source,
cycleDetectingLockFactory.create(instance.getClass()));
initializablesCache.put(instance, injectableReference);
pendingInjections.add(injectableReference);
return injectableReference;
}
/**
@ -77,9 +95,11 @@ final class Initializer {
* on the injected instances.
*/
void validateOustandingInjections(Errors errors) {
for (InjectableReference<?> reference : pendingInjection.values()) {
validationStarted = true;
initializablesCache.clear();
for (InjectableReference<?> reference : pendingInjections) {
try {
pendingMembersInjectors.put(reference.instance, reference.validate(errors));
reference.validate(errors);
} catch (ErrorsException e) {
errors.merge(e.getErrors());
}
@ -92,85 +112,122 @@ final class Initializer {
* instances are codependent (directly or transitively), ordering of injection is arbitrary.
*/
void injectAll(final Errors errors) {
// loop over a defensive copy since ensureInjected() mutates the set. Unfortunately, that copy
// is made complicated by a bug in IBM's JDK, wherein entrySet().toArray(Object[]) doesn't work
for (InjectableReference<?> reference : Lists.newArrayList(pendingInjection.values())) {
Preconditions.checkState(validationStarted, "Validation should be done before injection");
for (InjectableReference<?> reference : pendingInjections) {
try {
reference.get(errors);
} catch (ErrorsException e) {
errors.merge(e.getErrors());
reference.get();
} catch (InternalProvisionException ipe) {
errors.merge(ipe);
}
}
if (!pendingInjection.isEmpty()) {
throw new AssertionError("Failed to satisfy " + pendingInjection);
}
ready.countDown();
pendingInjections.clear();
}
private class InjectableReference<T> implements Initializable<T> {
private enum InjectableReferenceState {
NEW,
VALIDATED,
INJECTING,
READY
}
private static class InjectableReference<T> implements Initializable<T> {
private volatile InjectableReferenceState state = InjectableReferenceState.NEW;
private volatile MembersInjectorImpl<T> membersInjector = null;
private final InjectorImpl injector;
private final T instance;
private final Object source;
private final Key<T> key;
private final ProvisionListenerStackCallback<T> provisionCallback;
private final CycleDetectingLock<?> lock;
public InjectableReference(InjectorImpl injector, T instance, Key<T> key,
ProvisionListenerStackCallback<T> provisionCallback, Object source) {
ProvisionListenerStackCallback<T> provisionCallback,
Object source,
CycleDetectingLock<?> lock) {
this.injector = injector;
this.key = key; // possibly null!
this.provisionCallback = provisionCallback; // possibly null!
this.instance = checkNotNull(instance, "instance");
this.source = checkNotNull(source, "source");
this.lock = checkNotNull(lock, "lock");
}
public MembersInjectorImpl<T> validate(Errors errors) throws ErrorsException {
public void validate(Errors errors) throws ErrorsException {
@SuppressWarnings("unchecked") // the type of 'T' is a TypeLiteral<T>
TypeLiteral<T> type = TypeLiteral.get((Class<T>) instance.getClass());
return injector.membersInjectorStore.get(type, errors.withSource(source));
TypeLiteral<T> type = TypeLiteral.get((Class<T>) instance.getClass());
membersInjector = injector.membersInjectorStore.get(type, errors.withSource(source));
Preconditions.checkNotNull(
membersInjector,
"No membersInjector available for instance: %s, from key: %s",
instance,
key);
state = InjectableReferenceState.VALIDATED;
}
/**
* Reentrant. If {@code instance} was registered for injection at injector-creation time, this
* method will ensure that all its members have been injected before returning.
*/
public T get(Errors errors) throws ErrorsException {
if (ready.getCount() == 0) {
@Override
public T get() throws InternalProvisionException {
// skipping acquiring lock if initialization is already finished
if (state == InjectableReferenceState.READY) {
return instance;
}
// acquire lock for current binding to initialize an instance
Multimap<?, ?> lockCycle = lock.lockOrDetectPotentialLocksCycle();
if (!lockCycle.isEmpty()) {
// Potential deadlock detected and creation lock is not taken.
// According to injectAll()'s contract return non-initialized instance.
// just wait for everything to be injected by another thread
if (Thread.currentThread() != creatingThread) {
try {
ready.await();
return instance;
} catch (InterruptedException e) {
// Give up, since we don't know if our injection is ready
throw new RuntimeException(e);
}
// This condition should not be possible under the current Guice implementation.
// This clause exists for defensive programming purposes.
// Reasoning:
// get() is called either directly from injectAll(), holds no locks and can not create
// a cycle, or it is called through a singleton scope, which resolves deadlocks by itself.
// Before calling get() object has to be requested for injection.
// Initializer.requestInjection() is called either for constant object bindings, which wrap
// creation into a Singleton scope, or from Binder.requestInjection(), which
// has to use Singleton scope to reuse the same InjectableReference to potentially
// create a lock cycle.
return instance;
}
// toInject needs injection, do it right away. we only do this once, even if it fails
if (pendingInjection.remove(instance) != null) {
// safe because we only insert a members injector for the appropriate instance
@SuppressWarnings("unchecked")
MembersInjectorImpl<T> membersInjector =
(MembersInjectorImpl<T>) pendingMembersInjectors.remove(instance);
Preconditions.checkState(membersInjector != null,
"No membersInjector available for instance: %s, from key: %s", instance, key);
try {
// lock acquired, current thread owns this instance initialization
switch (state) {
case READY:
return instance;
// When instance depends on itself in the same thread potential dead lock
// is not detected. We have to prevent a stack overflow and we use
// an "injecting" stage to short-circuit a call.
case INJECTING:
return instance;
case VALIDATED:
state = InjectableReferenceState.INJECTING;
break;
case NEW:
throw new IllegalStateException("InjectableReference is not validated yet");
default:
throw new IllegalStateException("Unknown state: " + state);
}
// if in Stage.TOOL, we only want to inject & notify toolable injection points.
// (otherwise we'll inject all of them)
membersInjector.injectAndNotify(instance,
errors.withSource(source),
key,
provisionCallback,
source,
injector.options.stage == Stage.TOOL);
try {
membersInjector.injectAndNotify(
instance, key, provisionCallback, source, injector.options.stage == Stage.TOOL);
} catch (InternalProvisionException ipe) {
throw ipe.addSource(source);
}
// mark instance as ready to skip a lock on subsequent calls
state = InjectableReferenceState.READY;
return instance;
} finally {
// always release our creation lock, even on failures
lock.unlock();
}
return instance;
}
@Override

View file

@ -98,21 +98,19 @@ final class InjectionRequestProcessor extends AbstractProcessor {
}
void injectMembers() {
try {
injector.callInContext(new ContextualCallable<Void>() {
public Void call(InternalContext context) {
for (SingleMemberInjector memberInjector : memberInjectors) {
// Run injections if we're not in tool stage (ie, PRODUCTION or DEV),
// or if we are in tool stage and the injection point is toolable.
if (injector.options.stage != Stage.TOOL || memberInjector.getInjectionPoint().isToolable()) {
memberInjector.inject(errors, context, null);
}
try (InternalContext context = injector.enterContext()) {
boolean isStageTool = injector.options.stage == Stage.TOOL;
for (SingleMemberInjector memberInjector : memberInjectors) {
// Run injections if we're not in tool stage (ie, PRODUCTION or DEV),
// or if we are in tool stage and the injection point is toolable.
if (!isStageTool || memberInjector.getInjectionPoint().isToolable()) {
try {
memberInjector.inject(context, null);
} catch (InternalProvisionException e) {
errors.merge(e);
}
return null;
}
});
} catch (ErrorsException e) {
throw new AssertionError();
}
}
}
}

View file

@ -1,12 +1,16 @@
package com.google.inject.internal;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimaps;
import com.google.common.collect.Sets;
import com.google.inject.Binder;
import com.google.inject.Binding;
@ -18,7 +22,6 @@ import com.google.inject.MembersInjector;
import com.google.inject.Module;
import com.google.inject.ProvidedBy;
import com.google.inject.Provider;
import com.google.inject.ProvisionException;
import com.google.inject.Scope;
import com.google.inject.Stage;
import com.google.inject.TypeLiteral;
@ -26,8 +29,10 @@ import com.google.inject.internal.util.SourceProvider;
import com.google.inject.spi.BindingTargetVisitor;
import com.google.inject.spi.ConvertedConstantBinding;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.Element;
import com.google.inject.spi.HasDependencies;
import com.google.inject.spi.InjectionPoint;
import com.google.inject.spi.InstanceBinding;
import com.google.inject.spi.ProviderBinding;
import com.google.inject.spi.TypeConverterBinding;
import com.google.inject.util.Providers;
@ -42,45 +47,46 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
/**
* Default {@link Injector} implementation.
*
*/
final class InjectorImpl implements Injector, Lookups {
public static final TypeLiteral<String> STRING_TYPE = TypeLiteral.get(String.class);
/**
* Synchronization: map value is modified only for the current thread,
* it's ok to read map values of other threads. It can change between your
* calls.
*
* @see #getGlobalInternalContext
*/
private static final ConcurrentMap<Thread, InternalContext> globalInternalContext =
Maps.newConcurrentMap();
final State state;
final InjectorImpl parent;
final BindingsMultimap bindingsMultimap = new BindingsMultimap();
final ListMultimap<TypeLiteral<?>, Binding<?>> bindingsMultimap = ArrayListMultimap.create();
final InjectorOptions options;
/**
* Just-in-time binding cache. Guarded by state.lock()
*/
final Map<Key<?>, BindingImpl<?>> jitBindings = Maps.newHashMap();
/**
* Cache of Keys that we were unable to create JIT bindings for, so we don't
* keep trying. Also guarded by state.lock().
*/
final Set<Key<?>> failedJitBindings = Sets.newHashSet();
Lookups lookups = new DeferredLookups(this);
/** The set of types passed to {@link #getMembersInjector} and {@link #injectMembers}. */
final Set<TypeLiteral<?>> userRequestedMembersInjectorTypes = Sets.newConcurrentHashSet();
/**
* Cached constructor injectors for each type
*/
final ConstructorInjectorStore constructors = new ConstructorInjectorStore(this);
/**
* @see #getGlobalInternalContext
*/
private final ThreadLocal<Object[]> localContext;
Lookups lookups = new DeferredLookups(this);
/**
* Cached field and method injectors for each type.
*/
@ -98,56 +104,32 @@ final class InjectorImpl implements Injector, Lookups {
if (parent != null) {
localContext = parent.localContext;
} else {
localContext = new ThreadLocal<Object[]>();
// No ThreadLocal.initialValue(), as that would cause classloader leaks. See
// https://github.com/google/guice/issues/288#issuecomment-48216933,
// https://github.com/google/guice/issues/288#issuecomment-48216944
localContext = new ThreadLocal<>();
}
}
/**
* Returns true if the key type is Provider (but not a subclass of Provider).
*/
private static boolean isProvider(Key<?> key) {
return key.getTypeLiteral().getRawType().equals(Provider.class);
/** Only to be called by the {@link SingletonScope} provider. */
InternalContext getLocalContext() {
return (InternalContext) localContext.get()[0];
}
private static boolean isTypeLiteral(Key<?> key) {
return key.getTypeLiteral().getRawType().equals(TypeLiteral.class);
}
private static <T> Key<T> getProvidedKey(Key<Provider<T>> key, Errors errors) throws ErrorsException {
Type providerType = key.getTypeLiteral().getType();
// If the Provider has no type parameter (raw Provider)...
if (!(providerType instanceof ParameterizedType)) {
throw errors.cannotInjectRawProvider().toException();
InternalContext enterContext() {
Object[] reference = localContext.get();
if (reference == null) {
reference = new Object[1];
localContext.set(reference);
}
Type entryType = ((ParameterizedType) providerType).getActualTypeArguments()[0];
@SuppressWarnings("unchecked") // safe because T came from Key<Provider<T>>
Key<T> providedKey = (Key<T>) key.ofType(entryType);
return providedKey;
}
/**
* Returns true if the key type is MembersInjector (but not a subclass of MembersInjector).
*/
private static boolean isMembersInjector(Key<?> key) {
return key.getTypeLiteral().getRawType().equals(MembersInjector.class)
&& key.getAnnotationType() == null;
}
/**
* Provides access to the internal context for the current injector of all threads.
* One does not need to use this from Guice source code as context could be passed on the stack.
* It is required for custom scopes which are called from Guice and sometimes do require
* access to current internal context, but it is not passed in. Contrary to {@link #localContext}
* it is not used to store injector-specific state, but to provide easy access to the current
* state.
*
* @return unmodifiable map
*/
static Map<Thread, InternalContext> getGlobalInternalContext() {
return Collections.unmodifiableMap(globalInternalContext);
InternalContext ctx = (InternalContext) reference[0];
if (ctx == null) {
reference[0] = ctx = new InternalContext(options, reference);
} else {
ctx.enter();
}
return ctx;
}
/**
@ -155,23 +137,23 @@ final class InjectorImpl implements Injector, Lookups {
*/
void index() {
for (Binding<?> binding : state.getExplicitBindingsThisLevel().values()) {
index(binding);
bindingsMultimap.put(binding.getKey().getTypeLiteral(), binding);
}
}
<T> void index(Binding<T> binding) {
bindingsMultimap.put(binding.getKey().getTypeLiteral(), binding);
}
@Override
public <T> List<Binding<T>> findBindingsByType(TypeLiteral<T> type) {
return bindingsMultimap.getAll(type);
@SuppressWarnings("unchecked") // safe because we only put matching entries into the map
List<Binding<T>> list = (List<Binding<T>>) (List) bindingsMultimap.get(checkNotNull(type, "type"));
return Collections.unmodifiableList(list);
}
/**
* Returns the binding for {@code key}
*/
@Override
public <T> BindingImpl<T> getBinding(Key<T> key) {
Errors errors = new Errors(key);
Errors errors = new Errors(checkNotNull(key, "key"));
try {
BindingImpl<T> result = getBindingOrThrow(key, errors, JitLimitation.EXISTING_JIT);
errors.throwConfigurationExceptionIfErrorsExist();
@ -181,6 +163,7 @@ final class InjectorImpl implements Injector, Lookups {
}
}
@Override
public <T> BindingImpl<T> getExistingBinding(Key<T> key) {
// Check explicit bindings, i.e. bindings created by modules.
BindingImpl<T> explicitBinding = state.getExplicitBinding(key);
@ -235,21 +218,22 @@ final class InjectorImpl implements Injector, Lookups {
return getJustInTimeBinding(key, errors, jitType);
}
@Override
public <T> Binding<T> getBinding(Class<T> type) {
return getBinding(Key.get(type));
return getBinding(Key.get(checkNotNull(type, "type")));
}
@Override
public Injector getParent() {
return parent;
}
@Override
public Injector createChildInjector(Iterable<? extends Module> modules) {
return new InternalInjectorCreator()
.parentInjector(this)
.addModules(modules)
.build();
return new InternalInjectorCreator().parentInjector(this).addModules(modules).build();
}
@Override
public Injector createChildInjector(Module... modules) {
return createChildInjector(ImmutableList.copyOf(modules));
}
@ -267,7 +251,7 @@ final class InjectorImpl implements Injector, Lookups {
// first try to find a JIT binding that we've already created
for (InjectorImpl injector = this; injector != null; injector = injector.parent) {
@SuppressWarnings("unchecked") // we only store bindings that match their key
BindingImpl<T> binding = (BindingImpl<T>) injector.jitBindings.get(key);
BindingImpl<T> binding = (BindingImpl<T>) injector.jitBindings.get(key);
if (binding != null) {
// If we found a JIT binding and we don't allow them,
@ -304,6 +288,40 @@ final class InjectorImpl implements Injector, Lookups {
} // end synchronized(state.lock())
}
/**
* Returns true if the key type is Provider (but not a subclass of Provider).
*/
private static boolean isProvider(Key<?> key) {
return key.getTypeLiteral().getRawType().equals(Provider.class);
}
private static boolean isTypeLiteral(Key<?> key) {
return key.getTypeLiteral().getRawType().equals(TypeLiteral.class);
}
private static <T> Key<T> getProvidedKey(Key<Provider<T>> key, Errors errors)
throws ErrorsException {
Type providerType = key.getTypeLiteral().getType();
// If the Provider has no type parameter (raw Provider)...
if (!(providerType instanceof ParameterizedType)) {
throw errors.cannotInjectRawProvider().toException();
}
Type entryType = ((ParameterizedType) providerType).getActualTypeArguments()[0];
@SuppressWarnings("unchecked") // safe because T came from Key<Provider<T>>
Key<T> providedKey = (Key<T>) key.ofType(entryType);
return providedKey;
}
/** Returns true if the key type is MembersInjector (but not a subclass of MembersInjector). */
private static boolean isMembersInjector(Key<?> key) {
return key.getTypeLiteral().getRawType().equals(MembersInjector.class)
&& key.getAnnotationType() == null;
}
private <T> BindingImpl<MembersInjector<T>> createMembersInjectorBinding(
Key<MembersInjector<T>> key, Errors errors) throws ErrorsException {
Type membersInjectorType = key.getTypeLiteral().getType();
@ -312,16 +330,15 @@ final class InjectorImpl implements Injector, Lookups {
}
@SuppressWarnings("unchecked") // safe because T came from Key<MembersInjector<T>>
TypeLiteral<T> instanceType = (TypeLiteral<T>) TypeLiteral.get(
TypeLiteral<T> instanceType = (TypeLiteral<T>) TypeLiteral.get(
((ParameterizedType) membersInjectorType).getActualTypeArguments()[0]);
MembersInjector<T> membersInjector = membersInjectorStore.get(instanceType, errors);
InternalFactory<MembersInjector<T>> factory = new ConstantFactory<MembersInjector<T>>(
Initializables.of(membersInjector));
InternalFactory<MembersInjector<T>> factory =
new ConstantFactory<>(Initializables.of(membersInjector));
return new InstanceBindingImpl<MembersInjector<T>>(this, key, SourceProvider.UNKNOWN_SOURCE,
factory, ImmutableSet.<InjectionPoint>of(), membersInjector);
return new InstanceBindingImpl<>(this, key, SourceProvider.UNKNOWN_SOURCE,
factory, ImmutableSet.of(), membersInjector);
}
/**
@ -350,7 +367,11 @@ final class InjectorImpl implements Injector, Lookups {
return null;
}
String stringValue = stringBinding.getProvider().get();
// We can't call getProvider().get() because this InstanceBinding may not have been inintialized
// yet (because we may have been called during InternalInjectorCreator.initializeStatically and
// instance binding validation hasn't happened yet.)
@SuppressWarnings("unchecked")
String stringValue = ((InstanceBinding<String>) stringBinding).getInstance();
Object source = stringBinding.getSource();
// Find a matching type converter.
@ -365,7 +386,7 @@ final class InjectorImpl implements Injector, Lookups {
// Try to convert the string. A failed conversion results in an error.
try {
@SuppressWarnings("unchecked") // This cast is safe because we double check below.
T converted = (T) typeConverterBinding.getTypeConverter().convert(stringValue, type);
T converted = (T) typeConverterBinding.getTypeConverter().convert(stringValue, type);
if (converted == null) {
throw errors.converterReturnedNull(stringValue, source, type, typeConverterBinding)
@ -379,8 +400,6 @@ final class InjectorImpl implements Injector, Lookups {
return new ConvertedConstantBindingImpl<T>(this, key, converted, stringBinding,
typeConverterBinding);
} catch (ErrorsException e) {
throw e;
} catch (RuntimeException e) {
throw errors.conversionError(stringValue, source, type, typeConverterBinding, e)
.toException();
@ -411,7 +430,7 @@ final class InjectorImpl implements Injector, Lookups {
// so that cached exceptions while constructing it get stored.
// See TypeListenerTest#testTypeListenerThrows
removeFailedJitBinding(binding, null);
cleanup(binding, new HashSet<Key>());
cleanup(binding, new HashSet<>());
}
}
}
@ -423,18 +442,18 @@ final class InjectorImpl implements Injector, Lookups {
* optimistically added to allow circular dependency support, so dependencies may pass where they
* should have failed.
*/
private boolean cleanup(BindingImpl<?> binding, Set<Key> encountered) {
private boolean cleanup(BindingImpl<?> binding, Set<Key<?>> encountered) {
boolean bindingFailed = false;
Set<Dependency<?>> deps = getInternalDependencies(binding);
for (Dependency dep : deps) {
for (Dependency<?> dep : deps) {
Key<?> depKey = dep.getKey();
InjectionPoint ip = dep.getInjectionPoint();
if (encountered.add(depKey)) { // only check if we haven't looked at this key yet
BindingImpl depBinding = jitBindings.get(depKey);
BindingImpl<?> depBinding = jitBindings.get(depKey);
if (depBinding != null) { // if the binding still exists, validate
boolean failed = cleanup(depBinding, encountered); // if children fail, we fail
if (depBinding instanceof ConstructorBindingImpl) {
ConstructorBindingImpl ctorBinding = (ConstructorBindingImpl) depBinding;
ConstructorBindingImpl<?> ctorBinding = (ConstructorBindingImpl) depBinding;
ip = ctorBinding.getInternalConstructor();
if (!ctorBinding.isInitialized()) {
failed = true;
@ -493,13 +512,13 @@ final class InjectorImpl implements Injector, Lookups {
// Don't try to inject arrays or enums annotated with @ImplementedBy.
if (rawType.isArray() || (rawType.isEnum() && implementedBy != null)) {
throw errors.missingImplementation(key).toException();
throw errors.missingImplementationWithHint(key, this).toException();
}
// Handle TypeLiteral<T> by binding the inner type
if (rawType == TypeLiteral.class) {
@SuppressWarnings("unchecked") // we have to fudge the inner type as Object
BindingImpl<T> binding = (BindingImpl<T>) createTypeLiteralBinding(
BindingImpl<T> binding = (BindingImpl<T>) createTypeLiteralBinding(
(Key<TypeLiteral<Object>>) key, errors);
return binding;
}
@ -551,11 +570,10 @@ final class InjectorImpl implements Injector, Lookups {
}
@SuppressWarnings("unchecked") // by definition, innerType == T, so this is safe
TypeLiteral<T> value = (TypeLiteral<T>) TypeLiteral.get(innerType);
InternalFactory<TypeLiteral<T>> factory = new ConstantFactory<TypeLiteral<T>>(
Initializables.of(value));
return new InstanceBindingImpl<TypeLiteral<T>>(this, key, SourceProvider.UNKNOWN_SOURCE,
factory, ImmutableSet.<InjectionPoint>of(), value);
TypeLiteral<T> value = (TypeLiteral<T>) TypeLiteral.get(innerType);
InternalFactory<TypeLiteral<T>> factory = new ConstantFactory<>(Initializables.of(value));
return new InstanceBindingImpl<>(this, key, SourceProvider.UNKNOWN_SOURCE,
factory, ImmutableSet.of(), value);
}
/**
@ -564,7 +582,7 @@ final class InjectorImpl implements Injector, Lookups {
<T> BindingImpl<T> createProvidedByBinding(Key<T> key, Scoping scoping,
ProvidedBy providedBy, Errors errors) throws ErrorsException {
Class<?> rawType = key.getTypeLiteral().getRawType();
Class<? extends Provider<?>> providerType = providedBy.value();
Class<? extends javax.inject.Provider<?>> providerType = providedBy.value();
// Make sure it's not the same type. TODO: Can we check for deeper loops?
if (providerType == rawType) {
@ -576,12 +594,11 @@ final class InjectorImpl implements Injector, Lookups {
Key<? extends Provider<T>> providerKey = (Key<? extends Provider<T>>) Key.get(providerType);
ProvidedByInternalFactory<T> internalFactory =
new ProvidedByInternalFactory<T>(rawType, providerType, providerKey);
Object source = rawType;
BindingImpl<T> binding = LinkedProviderBindingImpl.createWithInitializer(
this,
key,
source,
Scoping.<T>scope(key, this, internalFactory, source, scoping),
rawType,
Scoping.<T>scope(key, this, internalFactory, rawType, scoping),
scoping,
providerKey,
internalFactory);
@ -613,27 +630,14 @@ final class InjectorImpl implements Injector, Lookups {
// Look up the target binding.
final Key<? extends T> targetKey = Key.get(subclass);
final BindingImpl<? extends T> targetBinding = getBindingOrThrow(targetKey, errors, JitLimitation.NEW_OR_EXISTING_JIT);
InternalFactory<T> internalFactory = new InternalFactory<T>() {
public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
throws ErrorsException {
context.pushState(targetKey, targetBinding.getSource());
try {
return targetBinding.getInternalFactory().get(
errors.withSource(targetKey), context, dependency, true);
} finally {
context.popState();
}
}
};
Object source = rawType;
FactoryProxy<T> factory = new FactoryProxy<>(this, key, targetKey, rawType);
factory.notify(errors); // causes the factory to initialize itself internally
return new LinkedBindingImpl<T>(
this,
key,
source,
Scoping.<T>scope(key, this, internalFactory, source, scoping),
rawType,
Scoping.<T>scope(key, this, factory, rawType, scoping),
scoping,
targetKey);
}
@ -725,9 +729,7 @@ final class InjectorImpl implements Injector, Lookups {
return convertedBinding;
}
if (!isTypeLiteral(key)
&& jitDisabled
&& jitType != JitLimitation.NEW_OR_EXISTING_JIT) {
if (!isTypeLiteral(key) && jitDisabled && jitType != JitLimitation.NEW_OR_EXISTING_JIT) {
throw errors.jitDisabled(key).toException();
}
@ -757,10 +759,12 @@ final class InjectorImpl implements Injector, Lookups {
return getBindingOrThrow(key, errors, jitType).getInternalFactory();
}
@Override
public Map<Key<?>, Binding<?>> getBindings() {
return state.getExplicitBindingsThisLevel();
}
@Override
public Map<Key<?>, Binding<?>> getAllBindings() {
synchronized (state.lock()) {
return new ImmutableMap.Builder<Key<?>, Binding<?>>()
@ -770,14 +774,47 @@ final class InjectorImpl implements Injector, Lookups {
}
}
@Override
public Map<Class<? extends Annotation>, Scope> getScopeBindings() {
return ImmutableMap.copyOf(state.getScopes());
}
@Override
public Set<TypeConverterBinding> getTypeConverterBindings() {
return ImmutableSet.copyOf(state.getConvertersThisLevel());
}
@Override
public List<Element> getElements() {
ImmutableList.Builder<Element> elements = ImmutableList.builder();
elements.addAll(getAllBindings().values());
elements.addAll(state.getProviderLookupsThisLevel());
elements.addAll(state.getConvertersThisLevel());
elements.addAll(state.getScopeBindingsThisLevel());
elements.addAll(state.getTypeListenerBindingsThisLevel());
elements.addAll(state.getProvisionListenerBindingsThisLevel());
elements.addAll(state.getScannerBindingsThisLevel());
elements.addAll(state.getStaticInjectionRequestsThisLevel());
elements.addAll(state.getMembersInjectorLookupsThisLevel());
elements.addAll(state.getInjectionRequestsThisLevel());
return elements.build();
}
@SuppressWarnings("unchecked")
@Override
public Map<TypeLiteral<?>, List<InjectionPoint>> getAllMembersInjectorInjectionPoints() {
// Note, this is a safe cast per the ListMultimap javadocs.
// We could use Multimaps.asMap to avoid the cast, but unfortunately it's a @Beta method.
return (Map<TypeLiteral<?>, List<InjectionPoint>>)
(Map<TypeLiteral<?>, ?>)
ImmutableListMultimap.copyOf(
Multimaps.filterKeys(
membersInjectorStore.getAllInjectionPoints(),
userRequestedMembersInjectorTypes::contains))
.asMap();
}
/**
* Returns parameter injectors, or {@code null} if there are no parameters.
*/
@ -808,13 +845,18 @@ final class InjectorImpl implements Injector, Lookups {
return new SingleParameterInjector<T>(dependency, binding);
}
@SuppressWarnings("unchecked") // the members injector type is consistent with instance's type
@SuppressWarnings({"unchecked","rawtypes"}) // the members injector type is consistent with instance's type
@Override
public void injectMembers(Object instance) {
MembersInjector membersInjector = getMembersInjector(instance.getClass());
membersInjector.injectMembers(instance);
}
@Override
public <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> typeLiteral) {
checkNotNull(typeLiteral, "typeLiteral");
userRequestedMembersInjectorTypes.add(typeLiteral);
Errors errors = new Errors(typeLiteral);
try {
return membersInjectorStore.get(typeLiteral, errors);
@ -823,47 +865,45 @@ final class InjectorImpl implements Injector, Lookups {
}
}
@Override
public <T> MembersInjector<T> getMembersInjector(Class<T> type) {
return getMembersInjector(TypeLiteral.get(type));
}
@Override
public <T> Provider<T> getProvider(Class<T> type) {
return getProvider(Key.get(type));
return getProvider(Key.get(checkNotNull(type, "type")));
}
<T> Provider<T> getProviderOrThrow(final Dependency<T> dependency, Errors errors) throws ErrorsException {
final Key<T> key = dependency.getKey();
final BindingImpl<? extends T> binding = getBindingOrThrow(key, errors, JitLimitation.NO_JIT);
return new Provider<T>() {
final InternalFactory<? extends T> internalFactory = binding.getInternalFactory();
final Object source = binding.getSource();
return new Provider<>() {
public T get() {
final Errors errors = new Errors(dependency);
InternalContext currentContext = enterContext();
Dependency<?> previous = currentContext.pushDependency(dependency, source);
try {
T t = callInContext(new ContextualCallable<T>() {
public T call(InternalContext context) throws ErrorsException {
Dependency previous = context.pushDependency(dependency, binding.getSource());
try {
return binding.getInternalFactory().get(errors, context, dependency, false);
} finally {
context.popStateAndSetDependency(previous);
}
}
});
errors.throwIfNewErrors(0);
return t;
} catch (ErrorsException e) {
throw new ProvisionException(errors.merge(e.getErrors()).getMessages());
return internalFactory.get(currentContext, dependency, false);
} catch (InternalProvisionException e) {
throw e.addSource(dependency).toProvisionException();
} finally {
currentContext.popStateAndSetDependency(previous);
currentContext.close();
}
}
@Override
public String toString() {
return binding.getInternalFactory().toString();
return internalFactory.toString();
}
};
}
@Override
public <T> Provider<T> getProvider(final Key<T> key) {
checkNotNull(key, "key");
Errors errors = new Errors(key);
try {
Provider<T> result = getProviderOrThrow(Dependency.get(key), errors);
@ -874,50 +914,16 @@ final class InjectorImpl implements Injector, Lookups {
}
}
@Override
public <T> T getInstance(Key<T> key) {
return getProvider(key).get();
}
@Override
public <T> T getInstance(Class<T> type) {
return getProvider(type).get();
}
/**
* Looks up thread local context. Creates (and removes) a new context if necessary.
*/
<T> T callInContext(ContextualCallable<T> callable) throws ErrorsException {
Object[] reference = localContext.get();
if (reference == null) {
reference = new Object[1];
localContext.set(reference);
}
Thread currentThread = Thread.currentThread();
if (reference[0] == null) {
reference[0] = new InternalContext(options);
globalInternalContext.put(currentThread, (InternalContext) reference[0]);
try {
return callable.call((InternalContext) reference[0]);
} finally {
// Only clear contexts if this call created them.
reference[0] = null;
globalInternalContext.remove(currentThread);
}
} else {
Object previousGlobalInternalContext = globalInternalContext.get(currentThread);
globalInternalContext.put(currentThread, (InternalContext) reference[0]);
try {
// Someone else will clean up this local context.
return callable.call((InternalContext) reference[0]);
} finally {
if (previousGlobalInternalContext != null) {
globalInternalContext.put(currentThread, (InternalContext) previousGlobalInternalContext);
} else {
globalInternalContext.remove(currentThread);
}
}
}
}
@Override
public String toString() {
return MoreObjects.toStringHelper(Injector.class)
@ -994,21 +1000,20 @@ final class InjectorImpl implements Injector, Lookups {
static <T> InternalFactory<Provider<T>> createInternalFactory(Binding<T> providedBinding) {
final Provider<T> provider = providedBinding.getProvider();
return new InternalFactory<Provider<T>>() {
public Provider<T> get(Errors errors, InternalContext context, Dependency dependency, boolean linked) {
return provider;
}
};
return (context, dependency, linked) -> provider;
}
@Override
public Key<? extends T> getProvidedKey() {
return providedBinding.getKey();
}
@Override
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super Provider<T>, V> visitor) {
return visitor.visit(this);
}
@Override
public void applyTo(Binder binder) {
throw new UnsupportedOperationException("This element represents a synthetic binding.");
}
@ -1021,6 +1026,7 @@ final class InjectorImpl implements Injector, Lookups {
.toString();
}
@Override
public Set<Dependency<?>> getDependencies() {
return ImmutableSet.<Dependency<?>>of(Dependency.get(getProvidedKey()));
}
@ -1050,9 +1056,11 @@ final class InjectorImpl implements Injector, Lookups {
final Binding<String> originalBinding;
final TypeConverterBinding typeConverterBinding;
ConvertedConstantBindingImpl(
InjectorImpl injector, Key<T> key, T value, Binding<String> originalBinding,
TypeConverterBinding typeConverterBinding) {
ConvertedConstantBindingImpl(InjectorImpl injector,
Key<T> key,
T value,
Binding<String> originalBinding,
TypeConverterBinding typeConverterBinding) {
super(injector, key, originalBinding.getSource(),
new ConstantFactory<T>(Initializables.of(value)), Scoping.UNSCOPED);
this.value = value;
@ -1066,26 +1074,32 @@ final class InjectorImpl implements Injector, Lookups {
return provider;
}
@Override
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
return visitor.visit(this);
}
@Override
public T getValue() {
return value;
}
@Override
public TypeConverterBinding getTypeConverterBinding() {
return typeConverterBinding;
}
@Override
public Key<String> getSourceKey() {
return originalBinding.getKey();
}
@Override
public Set<Dependency<?>> getDependencies() {
return ImmutableSet.<Dependency<?>>of(Dependency.get(getSourceKey()));
}
@Override
public void applyTo(Binder binder) {
throw new UnsupportedOperationException("This element represents a synthetic binding.");
}
@ -1116,27 +1130,4 @@ final class InjectorImpl implements Injector, Lookups {
return Objects.hashCode(getKey(), getScoping(), value);
}
}
private static class BindingsMultimap {
final Map<TypeLiteral<?>, List<Binding<?>>> multimap = Maps.newHashMap();
<T> void put(TypeLiteral<T> type, Binding<T> binding) {
List<Binding<?>> bindingsForType = multimap.get(type);
if (bindingsForType == null) {
bindingsForType = Lists.newArrayList();
multimap.put(type, bindingsForType);
}
bindingsForType.add(binding);
}
@SuppressWarnings("unchecked")
// safe because we only put matching entries into the map
<T> List<Binding<T>> getAll(TypeLiteral<T> type) {
List<Binding<?>> bindings = multimap.get(type);
return bindings != null
? Collections.<Binding<T>>unmodifiableList((List) multimap.get(type))
: ImmutableList.<Binding<T>>of();
}
}
}

View file

@ -15,7 +15,6 @@ import com.google.inject.internal.util.Stopwatch;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.Element;
import com.google.inject.spi.Elements;
import com.google.inject.spi.InjectionPoint;
import com.google.inject.spi.ModuleAnnotatedMethodScannerBinding;
import com.google.inject.spi.PrivateElements;
import com.google.inject.spi.ProvisionListenerBinding;
@ -35,11 +34,20 @@ final class InjectorShell {
private final List<Element> elements;
private final InjectorImpl injector;
private InjectorShell(Builder builder, List<Element> elements, InjectorImpl injector) {
private InjectorShell(List<Element> elements, InjectorImpl injector) {
this.elements = elements;
this.injector = injector;
}
InjectorImpl getInjector() {
return injector;
}
List<Element> getElements() {
return elements;
}
/**
* The Injector is a special case because we allow both parent and child injectors to both have
* a binding for that key.
@ -48,44 +56,23 @@ final class InjectorShell {
Key<Injector> key = Key.get(Injector.class);
InjectorFactory injectorFactory = new InjectorFactory(injector);
injector.state.putBinding(key,
new ProviderInstanceBindingImpl<Injector>(injector, key, SourceProvider.UNKNOWN_SOURCE,
new ProviderInstanceBindingImpl<>(injector, key, SourceProvider.UNKNOWN_SOURCE,
injectorFactory, Scoping.UNSCOPED, injectorFactory,
ImmutableSet.<InjectionPoint>of()));
ImmutableSet.of()));
}
/**
* The Logger is a special case because it knows the injection point of the injected member. It's
* the only binding that does this.
*/
/*private static void bindLogger(InjectorImpl injector) {
Key<Logger> key = Key.get(Logger.class);
LoggerFactory loggerFactory = new LoggerFactory();
injector.state.putBinding(key,
new ProviderInstanceBindingImpl<Logger>(injector, key,
SourceProvider.UNKNOWN_SOURCE, loggerFactory, Scoping.UNSCOPED,
loggerFactory, ImmutableSet.<InjectionPoint>of()));
}*/
private static void bindStage(InjectorImpl injector, Stage stage) {
Key<Stage> key = Key.get(Stage.class);
InstanceBindingImpl<Stage> stageBinding = new InstanceBindingImpl<Stage>(
InstanceBindingImpl<Stage> stageBinding = new InstanceBindingImpl<>(
injector,
key,
SourceProvider.UNKNOWN_SOURCE,
new ConstantFactory<Stage>(Initializables.of(stage)),
ImmutableSet.<InjectionPoint>of(),
new ConstantFactory<>(Initializables.of(stage)),
ImmutableSet.of(),
stage);
injector.state.putBinding(key, stageBinding);
}
InjectorImpl getInjector() {
return injector;
}
List<Element> getElements() {
return elements;
}
static class Builder {
private final List<Element> elements = Lists.newArrayList();
private final List<Module> modules = Lists.newArrayList();
@ -213,7 +200,7 @@ final class InjectorShell {
stopwatch.resetAndLog("Module annotated method scanners creation");
List<InjectorShell> injectorShells = Lists.newArrayList();
injectorShells.add(new InjectorShell(this, elements, injector));
injectorShells.add(new InjectorShell(elements, injector));
// recursively build child shells
PrivateElementProcessor processor = new PrivateElementProcessor(errors);
@ -241,11 +228,12 @@ final class InjectorShell {
this.injector = injector;
}
public Injector get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
throws ErrorsException {
@Override
public Injector get(InternalContext context, Dependency<?> dependency, boolean linked) {
return injector;
}
@Override
public Injector get() {
return injector;
}
@ -255,24 +243,8 @@ final class InjectorShell {
}
}
/*private static class LoggerFactory implements InternalFactory<Logger>, Provider<Logger> {
public Logger get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked) {
InjectionPoint injectionPoint = dependency.getInjectionPoint();
return injectionPoint == null
? Logger.getAnonymousLogger()
: Logger.getLogger(injectionPoint.getMember().getDeclaringClass().getName());
}
public Logger get() {
return Logger.getAnonymousLogger();
}
public String toString() {
return "Provider<Logger>";
}
}*/
private static class RootModule implements Module {
@Override
public void configure(Binder binder) {
binder = binder.withSource(SourceProvider.UNKNOWN_SOURCE);
binder.bindScope(Singleton.class, SINGLETON);
@ -287,6 +259,7 @@ final class InjectorShell {
this.state = state;
}
@Override
public void configure(Binder binder) {
for (ModuleAnnotatedMethodScannerBinding binding : state.getScannerBindings()) {
binding.applyTo(binder);

View file

@ -5,20 +5,17 @@ import com.google.common.base.Objects;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Binder;
import com.google.inject.Key;
import com.google.inject.Provider;
import com.google.inject.spi.BindingTargetVisitor;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.HasDependencies;
import com.google.inject.spi.InjectionPoint;
import com.google.inject.spi.InstanceBinding;
import com.google.inject.util.Providers;
import java.util.Set;
final class InstanceBindingImpl<T> extends BindingImpl<T> implements InstanceBinding<T> {
final T instance;
final Provider<T> provider;
final ImmutableSet<InjectionPoint> injectionPoints;
public InstanceBindingImpl(InjectorImpl injector, Key<T> key, Object source,
@ -27,7 +24,6 @@ final class InstanceBindingImpl<T> extends BindingImpl<T> implements InstanceBin
super(injector, key, source, internalFactory, Scoping.EAGER_SINGLETON);
this.injectionPoints = ImmutableSet.copyOf(injectionPoints);
this.instance = instance;
this.provider = Providers.of(instance);
}
public InstanceBindingImpl(Object source, Key<T> key, Scoping scoping,
@ -35,40 +31,41 @@ final class InstanceBindingImpl<T> extends BindingImpl<T> implements InstanceBin
super(source, key, scoping);
this.injectionPoints = ImmutableSet.copyOf(injectionPoints);
this.instance = instance;
this.provider = Providers.of(instance);
}
@Override
public Provider<T> getProvider() {
return this.provider;
}
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
return visitor.visit(this);
}
@Override
public T getInstance() {
return instance;
}
@Override
public Set<InjectionPoint> getInjectionPoints() {
return injectionPoints;
}
@Override
public Set<Dependency<?>> getDependencies() {
return instance instanceof HasDependencies
? ImmutableSet.copyOf(((HasDependencies) instance).getDependencies())
: Dependency.forInjectionPoints(injectionPoints);
}
@Override
public BindingImpl<T> withScoping(Scoping scoping) {
return new InstanceBindingImpl<T>(getSource(), getKey(), scoping, injectionPoints, instance);
}
@Override
public BindingImpl<T> withKey(Key<T> key) {
return new InstanceBindingImpl<T>(getSource(), key, getScoping(), injectionPoints, instance);
}
@Override
public void applyTo(Binder binder) {
// instance bindings aren't scoped
binder.withSource(getSource()).bind(getKey()).toInstance(instance);

View file

@ -1,37 +1,76 @@
package com.google.inject.internal;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;
import com.google.inject.Key;
import com.google.inject.internal.InjectorImpl.InjectorOptions;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.DependencyAndSource;
import java.util.Arrays;
import java.util.List;
import java.util.IdentityHashMap;
import java.util.Map;
/**
* Internal context. Used to coordinate injections and support circular
* dependencies.
*/
final class InternalContext {
final class InternalContext implements AutoCloseable {
private final InjectorOptions options;
/**
* Keeps track of the hierarchy of types needed during injection.
*/
private final DependencyStack state = new DependencyStack();
private Map<Object, ConstructionContext<?>> constructionContexts = Maps.newHashMap();
private final Map<Object, ConstructionContext<?>> constructionContexts =
new IdentityHashMap<>();
/**
* Keeps track of the type that is currently being requested for injection.
*/
private Dependency<?> dependency;
InternalContext(InjectorOptions options) {
/**
* Keeps track of the hierarchy of types needed during injection.
*
* <p>This is a pairwise combination of dependencies and sources, with dependencies or keys on
* even indices, and sources on odd indices. This structure is to avoid the memory overhead of
* DependencyAndSource objects, which can add to several tens of megabytes in large applications.
*/
private Object[] dependencyStack = new Object[16];
private int dependencyStackSize = 0;
/**
* The number of times {@link #enter()} has been called + 1 for initial construction.
*/
private int enterCount;
/**
* A single element array to clear when the {@link #enterCount} hits {@code 0}.
*
* <p>This is the value stored in the {@code InjectorImpl.localContext} thread local.
*/
private final Object[] toClear;
InternalContext(InjectorOptions options, Object[] toClear) {
this.options = options;
this.toClear = toClear;
this.enterCount = 1;
}
/** Should only be called by InjectorImpl.enterContext(). */
void enter() {
enterCount++;
}
/** Should be called any any method that received an instance via InjectorImpl.enterContext(). */
@Override
public void close() {
int newCount = --enterCount;
if (newCount < 0) {
throw new IllegalStateException("Called close() too many times");
}
if (newCount == 0) {
toClear[0] = null;
}
}
public InjectorOptions getInjectorOptions() {
return options;
}
@ -57,7 +96,7 @@ final class InternalContext {
public Dependency<?> pushDependency(Dependency<?> dependency, Object source) {
Dependency<?> previous = this.dependency;
this.dependency = dependency;
state.add(dependency, source);
doPushState(dependency, source);
return previous;
}
@ -65,7 +104,7 @@ final class InternalContext {
* Pops the current state & sets the new dependency.
*/
public void popStateAndSetDependency(Dependency<?> newDependency) {
state.pop();
popState();
this.dependency = newDependency;
}
@ -73,64 +112,29 @@ final class InternalContext {
* Adds to the state without setting the dependency.
*/
public void pushState(Key<?> key, Object source) {
state.add(key, source);
doPushState(key, source);
}
private void doPushState(Object dependencyOrKey, Object source) {
int localSize = dependencyStackSize;
Object[] localStack = dependencyStack;
if (localStack.length < localSize + 2) {
localStack = dependencyStack =
java.util.Arrays.copyOf(localStack, (localStack.length * 3) / 2 + 2);
}
localStack[localSize++] = dependencyOrKey;
localStack[localSize++] = source;
dependencyStackSize = localSize;
}
/**
* Pops from the state without setting a dependency.
*/
public void popState() {
state.pop();
}
/**
* Returns the current dependency chain (all the state).
*/
public List<DependencyAndSource> getDependencyChain() {
ImmutableList.Builder<DependencyAndSource> builder = ImmutableList.builder();
for (int i = 0; i < state.size(); i += 2) {
Object evenEntry = state.get(i);
Dependency<?> dependency;
if (evenEntry instanceof Key) {
dependency = Dependency.get((Key<?>) evenEntry);
} else {
dependency = (Dependency<?>) evenEntry;
}
builder.add(new DependencyAndSource(dependency, state.get(i + 1)));
}
return builder.build();
}
/**
* Keeps track of the hierarchy of types needed during injection.
*
* <p>This is a pairwise combination of dependencies and sources, with dependencies or keys on
* even indices, and sources on odd indices. This structure is to avoid the memory overhead of
* DependencyAndSource objects, which can add to several tens of megabytes in large applications.
*/
private static final class DependencyStack {
private Object[] elements = new Object[16];
private int size = 0;
public void add(Object dependencyOrKey, Object source) {
if (elements.length < size + 2) {
elements = Arrays.copyOf(elements, (elements.length * 3) / 2 + 2);
}
elements[size++] = dependencyOrKey;
elements[size++] = source;
}
public void pop() {
elements[--size] = null;
elements[--size] = null;
}
public Object get(int i) {
return elements[i];
}
public int size() {
return size;
}
// N.B. we don't null out the array entries. It isn't necessary since all the objects in the
// array (Key, Dependency, or Binding source objects) are all tied to the lifetime of the
// injector, which is greater than the lifetime of this object. So removing them from the array
// doesn't matter.
dependencyStackSize -= 2;
}
}

View file

@ -12,9 +12,9 @@ interface InternalFactory<T> {
*
* @param context of this injection
* @param linked true if getting as a result of a linked binding
* @return instance to be injected
* @throws com.google.inject.internal.ErrorsException if a value cannot be provided
* @return instance that was created
* @throws InternalProvisionException if a value cannot be provided
*/
T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
throws ErrorsException;
T get(InternalContext context, Dependency<?> dependency, boolean linked)
throws InternalProvisionException;
}

View file

@ -14,7 +14,7 @@ final class InternalFactoryToInitializableAdapter<T> extends ProviderInternalFac
private final ProvisionListenerStackCallback<T> provisionCallback;
private final Initializable<? extends javax.inject.Provider<? extends T>> initializable;
public InternalFactoryToInitializableAdapter(
InternalFactoryToInitializableAdapter(
Initializable<? extends javax.inject.Provider<? extends T>> initializable,
Object source, ProvisionListenerStackCallback<T> provisionCallback) {
super(source);
@ -22,19 +22,20 @@ final class InternalFactoryToInitializableAdapter<T> extends ProviderInternalFac
this.initializable = checkNotNull(initializable, "provider");
}
public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
throws ErrorsException {
return circularGet(initializable.get(errors), errors, context, dependency,
provisionCallback);
@Override
public T get(InternalContext context, Dependency<?> dependency, boolean linked)
throws InternalProvisionException {
return circularGet(initializable.get(), context, dependency, provisionCallback);
}
@Override
protected T provision(javax.inject.Provider<? extends T> provider, Errors errors,
Dependency<?> dependency, ConstructionContext<T> constructionContext) throws ErrorsException {
protected T provision(javax.inject.Provider<? extends T> provider,
Dependency<?> dependency,
ConstructionContext<T> constructionContext) throws InternalProvisionException {
try {
return super.provision(provider, errors, dependency, constructionContext);
return super.provision(provider, dependency, constructionContext);
} catch (RuntimeException userException) {
throw errors.withSource(source).errorInProvider(userException).toException();
throw InternalProvisionException.errorInProvider(userException).addSource(source);
}
}

View file

@ -10,18 +10,22 @@ final class InternalFactoryToProviderAdapter<T> implements InternalFactory<T> {
private final Provider<? extends T> provider;
private final Object source;
public InternalFactoryToProviderAdapter(Provider<? extends T> provider, Object source) {
InternalFactoryToProviderAdapter(Provider<? extends T> provider, Object source) {
this.provider = checkNotNull(provider, "provider");
this.source = checkNotNull(source, "source");
}
public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
throws ErrorsException {
// TODO(sameb): Does this need to push state into the context?
@Override
public T get(InternalContext context, Dependency<?> dependency, boolean linked)
throws InternalProvisionException {
try {
return errors.checkForNull(provider.get(), source, dependency);
T t = provider.get();
if (t == null && !dependency.isNullable()) {
InternalProvisionException.onNullInjectedIntoNonNullableDependency(source, dependency);
}
return t;
} catch (RuntimeException userException) {
throw errors.withSource(source).errorInProvider(userException).toException();
throw InternalProvisionException.errorInProvider(userException).addSource(source);
}
}

View file

@ -66,19 +66,13 @@ public class InternalFlags {
private static <T extends Enum<T>> T getSystemOption(final String name, T defaultValue,
T secureValue) {
Class<T> enumType = defaultValue.getDeclaringClass();
String value = null;
try {
value = AccessController.doPrivileged(new PrivilegedAction<String>() {
public String run() {
return System.getProperty(name);
}
});
String value = AccessController.doPrivileged((PrivilegedAction<String>) ()
-> System.getProperty(name));
return (value != null && value.length() > 0) ? Enum.valueOf(enumType, value) : defaultValue;
} catch (SecurityException e) {
return secureValue;
} catch (IllegalArgumentException e) {
//logger.warning(value + " is not a valid flag value for " + name + ". "
// + " Values must be one of " + Arrays.asList(enumType.getEnumConstants()));
return defaultValue;
}
}

View file

@ -1,7 +1,5 @@
package com.google.inject.internal;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.inject.Binding;
import com.google.inject.Injector;
import com.google.inject.Key;
@ -13,9 +11,12 @@ import com.google.inject.Stage;
import com.google.inject.TypeLiteral;
import com.google.inject.internal.util.Stopwatch;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.Element;
import com.google.inject.spi.InjectionPoint;
import com.google.inject.spi.TypeConverterBinding;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@ -64,7 +65,7 @@ public final class InternalInjectorCreator {
* Sets the parent of the injector to-be-constructed. As a side effect, this sets this injector's
* stage to the stage of {@code parent}.
*/
public InternalInjectorCreator parentInjector(InjectorImpl parent) {
InternalInjectorCreator parentInjector(InjectorImpl parent) {
shellBuilder.parent(parent);
return this;
}
@ -102,36 +103,32 @@ public final class InternalInjectorCreator {
private void initializeStatically() {
bindingData.initializeBindings();
stopwatch.resetAndLog("Binding initialization");
for (InjectorShell shell : shells) {
shell.getInjector().index();
}
stopwatch.resetAndLog("Binding indexing");
injectionRequestProcessor.process(shells);
stopwatch.resetAndLog("Collecting injection requests");
bindingData.runCreationListeners(errors);
stopwatch.resetAndLog("Binding validation");
injectionRequestProcessor.validate();
stopwatch.resetAndLog("Static validation");
initializer.validateOustandingInjections(errors);
stopwatch.resetAndLog("Instance member validation");
new LookupProcessor(errors).process(shells);
for (InjectorShell shell : shells) {
((DeferredLookups) shell.getInjector().lookups).initialize(errors);
}
stopwatch.resetAndLog("Provider verification");
// This needs to come late since some user bindings rely on requireBinding calls to create
// jit bindings during the LookupProcessor.
bindingData.initializeDelayedBindings();
stopwatch.resetAndLog("Delayed Binding initialization");
for (InjectorShell shell : shells) {
if (!shell.getElements().isEmpty()) {
throw new AssertionError("Failed to execute " + shell.getElements());
}
}
errors.throwCreationExceptionIfErrorsExist();
}
@ -170,31 +167,25 @@ public final class InternalInjectorCreator {
*/
void loadEagerSingletons(InjectorImpl injector, Stage stage, final Errors errors) {
@SuppressWarnings("unchecked") // casting Collection<Binding> to Collection<BindingImpl> is safe
Iterable<BindingImpl<?>> candidateBindings = ImmutableList.copyOf(Iterables.concat(
(Collection) injector.state.getExplicitBindingsThisLevel().values(),
injector.jitBindings.values()));
for (final BindingImpl<?> binding : candidateBindings) {
if (isEagerSingleton(injector, binding, stage)) {
try {
injector.callInContext(new ContextualCallable<Void>() {
Dependency<?> dependency = Dependency.get(binding.getKey());
public Void call(InternalContext context) {
Dependency previous = context.pushDependency(dependency, binding.getSource());
Errors errorsForBinding = errors.withSource(dependency);
try {
binding.getInternalFactory().get(errorsForBinding, context, dependency, false);
} catch (ErrorsException e) {
errorsForBinding.merge(e.getErrors());
} finally {
context.popStateAndSetDependency(previous);
}
return null;
}
});
} catch (ErrorsException e) {
throw new AssertionError();
Collection<BindingImpl<?>> bindingsAtThisLevel =
(Collection) injector.state.getExplicitBindingsThisLevel().values();
List<BindingImpl<?>> candidateBindings = new ArrayList<>(bindingsAtThisLevel);
synchronized (injector.state.lock()) {
// jit bindings must be accessed while holding the lock.
candidateBindings.addAll(injector.jitBindings.values());
}
try (InternalContext context = injector.enterContext()) {
for (BindingImpl<?> binding : candidateBindings) {
if (isEagerSingleton(injector, binding, stage)) {
Dependency<?> dependency = Dependency.get(binding.getKey());
Dependency<?> previous = context.pushDependency(dependency, binding.getSource());
try {
binding.getInternalFactory().get(context, dependency, false);
} catch (InternalProvisionException e) {
errors.withSource(dependency).merge(e);
} finally {
context.popStateAndSetDependency(previous);
}
}
}
}
@ -207,10 +198,11 @@ public final class InternalInjectorCreator {
// handle a corner case where a child injector links to a binding in a parent injector, and
// that binding is singleton. We won't catch this otherwise because we only iterate the child's
// bindings.
// bindings. This only applies if the linked binding is not itself scoped.
if (binding instanceof LinkedBindingImpl) {
Key<?> linkedBinding = ((LinkedBindingImpl<?>) binding).getLinkedKey();
return isEagerSingleton(injector, injector.getBinding(linkedBinding), stage);
return binding.getScoping().isNoScope() &&
isEagerSingleton(injector, injector.getBinding(linkedBinding), stage);
}
return false;
@ -226,80 +218,108 @@ public final class InternalInjectorCreator {
this.delegateInjector = delegateInjector;
}
@Override
public void injectMembers(Object o) {
throw new UnsupportedOperationException(
"Injector.injectMembers(Object) is not supported in Stage.TOOL");
}
@Override
public Map<Key<?>, Binding<?>> getBindings() {
return this.delegateInjector.getBindings();
}
@Override
public Map<Key<?>, Binding<?>> getAllBindings() {
return this.delegateInjector.getAllBindings();
}
@Override
public <T> Binding<T> getBinding(Key<T> key) {
return this.delegateInjector.getBinding(key);
}
@Override
public <T> Binding<T> getBinding(Class<T> type) {
return this.delegateInjector.getBinding(type);
}
@Override
public <T> Binding<T> getExistingBinding(Key<T> key) {
return this.delegateInjector.getExistingBinding(key);
}
@Override
public <T> List<Binding<T>> findBindingsByType(TypeLiteral<T> type) {
return this.delegateInjector.findBindingsByType(type);
}
@Override
public Injector getParent() {
return delegateInjector.getParent();
}
@Override
public Injector createChildInjector(Iterable<? extends Module> modules) {
return delegateInjector.createChildInjector(modules);
}
@Override
public Injector createChildInjector(Module... modules) {
return delegateInjector.createChildInjector(modules);
}
@Override
public Map<Class<? extends Annotation>, Scope> getScopeBindings() {
return delegateInjector.getScopeBindings();
}
@Override
public Set<TypeConverterBinding> getTypeConverterBindings() {
return delegateInjector.getTypeConverterBindings();
}
@Override
public List<Element> getElements() {
return delegateInjector.getElements();
}
@Override
public Map<TypeLiteral<?>, List<InjectionPoint>> getAllMembersInjectorInjectionPoints() {
return delegateInjector.getAllMembersInjectorInjectionPoints();
}
@Override
public <T> Provider<T> getProvider(Key<T> key) {
throw new UnsupportedOperationException(
"Injector.getProvider(Key<T>) is not supported in Stage.TOOL");
}
@Override
public <T> Provider<T> getProvider(Class<T> type) {
throw new UnsupportedOperationException(
"Injector.getProvider(Class<T>) is not supported in Stage.TOOL");
}
@Override
public <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> typeLiteral) {
throw new UnsupportedOperationException(
"Injector.getMembersInjector(TypeLiteral<T>) is not supported in Stage.TOOL");
}
@Override
public <T> MembersInjector<T> getMembersInjector(Class<T> type) {
throw new UnsupportedOperationException(
"Injector.getMembersInjector(Class<T>) is not supported in Stage.TOOL");
}
@Override
public <T> T getInstance(Key<T> key) {
throw new UnsupportedOperationException(
"Injector.getInstance(Key<T>) is not supported in Stage.TOOL");
}
@Override
public <T> T getInstance(Class<T> type) {
throw new UnsupportedOperationException(
"Injector.getInstance(Class<T>) is not supported in Stage.TOOL");

View file

@ -0,0 +1,179 @@
package com.google.inject.internal;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Key;
import com.google.inject.Provider;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.HasDependencies;
import com.google.inject.spi.ProviderWithExtensionVisitor;
/**
* A {@link ProviderInstanceBindingImpl} for implementing 'native' guice extensions.
*
* <p>Beyond the normal binding contract that is mostly handled by our baseclass, this also
* implements {@link DelayedInitialize} in order to initialize factory state.
*/
final class InternalProviderInstanceBindingImpl<T> extends ProviderInstanceBindingImpl<T>
implements DelayedInitialize {
enum InitializationTiming {
/** This factory can be initialized eagerly. This should be the case for most things. */
EAGER,
/**
* Initialization of this factory should be delayed until after all other static initialization
* completes. This will be useful for factories that need to call {@link
* InjectorImpl#getExistingBinding(Key)} to not create jit bindings, but also want to be able to
* conditionally consume jit bindings created by other other bindings.
*/
DELAYED
}
private final Factory<T> originalFactory;
InternalProviderInstanceBindingImpl(
InjectorImpl injector,
Key<T> key,
Object source,
Factory<T> originalFactory,
InternalFactory<? extends T> scopedFactory,
Scoping scoping) {
super(
injector,
key,
source,
scopedFactory,
scoping,
originalFactory,
ImmutableSet.of());
this.originalFactory = originalFactory;
}
InitializationTiming getInitializationTiming() {
return originalFactory.initializationTiming;
}
@Override
public void initialize(final InjectorImpl injector, final Errors errors) throws ErrorsException {
originalFactory.source = getSource();
originalFactory.provisionCallback = injector.provisionListenerStore.get(this);
// For these kinds of providers, the 'user supplied provider' is really 'guice supplied'
// So make our user supplied provider just delegate to the guice supplied one.
originalFactory.delegateProvider = getProvider();
originalFactory.initialize(injector, errors);
}
/**
* A base factory implementation. Any Factories that delegate to other bindings should use the
* {@code CyclicFactory} subclass, but trivial factories can use this one.
*/
abstract static class Factory<T> implements InternalFactory<T>, Provider<T>, HasDependencies {
private final InitializationTiming initializationTiming;
private Object source;
private Provider<T> delegateProvider;
ProvisionListenerStackCallback<T> provisionCallback;
Factory(InitializationTiming initializationTiming) {
this.initializationTiming = initializationTiming;
}
/**
* The binding source.
*
* <p>May be useful for augmenting runtime error messages.
*
* <p>Note: this will return {#code null} until {@link #initialize(InjectorImpl, Errors)} has
* already been called.
*/
final Object getSource() {
return source;
}
/**
* A callback that allows for implementations to fetch dependencies on other bindings.
*
* <p>Will be called exactly once, prior to any call to {@link #doProvision}.
*/
abstract void initialize(InjectorImpl injector, Errors errors) throws ErrorsException;
@Override
public final T get() {
Provider<T> local = delegateProvider;
if (local == null) {
throw new IllegalStateException(
"This Provider cannot be used until the Injector has been created.");
}
return local.get();
}
@Override
public T get(final InternalContext context, final Dependency<?> dependency, boolean linked)
throws InternalProvisionException {
if (provisionCallback == null) {
return doProvision(context, dependency);
} else {
return provisionCallback.provision(context, () -> doProvision(context, dependency));
}
}
/**
* Creates an object to be injected.
*
* @throws com.google.inject.internal.InternalProvisionException if a value cannot be provided
* @return instance to be injected
*/
protected abstract T doProvision(InternalContext context, Dependency<?> dependency)
throws InternalProvisionException;
}
/**
* An base factory implementation that can be extended to provide a specialized implementation of
* a {@link ProviderWithExtensionVisitor} and also implements {@link InternalFactory}
*/
abstract static class CyclicFactory<T> extends Factory<T> {
CyclicFactory(InitializationTiming initializationTiming) {
super(initializationTiming);
}
@Override
public final T get(
final InternalContext context, final Dependency<?> dependency, boolean linked)
throws InternalProvisionException {
final ConstructionContext<T> constructionContext = context.getConstructionContext(this);
// We have a circular reference between bindings. Return a proxy.
if (constructionContext.isConstructing()) {
Class<?> expectedType = dependency.getKey().getTypeLiteral().getRawType();
@SuppressWarnings("unchecked")
T proxyType =
(T) constructionContext.createProxy(context.getInjectorOptions(), expectedType);
return proxyType;
}
// Optimization: Don't go through the callback stack if no one's listening.
constructionContext.startConstruction();
try {
if (provisionCallback == null) {
return provision(dependency, context, constructionContext);
} else {
return provisionCallback.provision(context, () -> provision(dependency, context, constructionContext));
}
} finally {
constructionContext.removeCurrentReference();
constructionContext.finishConstruction();
}
}
private T provision(
Dependency<?> dependency,
InternalContext context,
ConstructionContext<T> constructionContext)
throws InternalProvisionException {
try {
T t = doProvision(context, dependency);
constructionContext.setProxyDelegates(t);
return t;
} catch (InternalProvisionException ipe) {
throw ipe.addSource(getSource());
} catch (Throwable t) {
throw InternalProvisionException.errorInProvider(t).addSource(getSource());
}
}
}
}

View file

@ -0,0 +1,216 @@
package com.google.inject.internal;
import static com.google.common.base.Preconditions.checkArgument;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.inject.Guice;
import com.google.inject.Key;
import com.google.inject.MembersInjector;
import com.google.inject.Provides;
import com.google.inject.ProvisionException;
import com.google.inject.TypeLiteral;
import com.google.inject.internal.util.SourceProvider;
import com.google.inject.internal.util.StackTraceElements;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.InjectionListener;
import com.google.inject.spi.Message;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* A checked exception for provisioning errors.
*
* <p>This is the internal dual of {@link ProvisionException}, similar to the relationship between
* {@link com.google.inject.ConfigurationException} and {@link ErrorsException}. This is useful for
* several reasons:
*
* <ul>
* <li>Since it is a checked exception, we get some assistance from the java compiler in ensuring
* that we correctly handle it everywhere. ProvisionException is unchecked.
* <li>Since this is an internal package, we can add useful construction and mutation APIs that
* would be undesirable in a public supported API.
* </ul>
*
* <p>This exception will be thrown when errors are encountered during provisioning, ErrorsException
* will continue to be used for errors that are encountered during provisioning and both make use of
* the {@link Message} as the core model.
*
* <p>NOTE: this object stores a list of messages but in the most common case the cardinality will
* be 1. The only time that multiple errors might be reported via this mechanism is when {@link
* #errorInUserCode} is called with an exception that holds multiple errors (like
* ProvisionException).
*/
@SuppressWarnings("serial")
public final class InternalProvisionException extends Exception {
private static final Logger logger = Logger.getLogger(Guice.class.getName());
private static final Set<Dependency<?>> warnedDependencies =
Collections.newSetFromMap(new ConcurrentHashMap<>());
public static InternalProvisionException circularDependenciesDisabled(Class<?> expectedType) {
return create(
"Found a circular dependency involving %s, and circular dependencies are disabled.",
expectedType);
}
public static InternalProvisionException cannotProxyClass(Class<?> expectedType) {
return create(
"Tried proxying %s to support a circular dependency, but it is not an interface.",
expectedType);
}
public static InternalProvisionException create(String format, Object... arguments) {
return new InternalProvisionException(Messages.create(format, arguments));
}
public static InternalProvisionException errorInUserCode(
Throwable cause, String messageFormat, Object... arguments) {
Collection<Message> messages = Errors.getMessagesFromThrowable(cause);
if (!messages.isEmpty()) {
// TODO(lukes): it seems like we are dropping some valuable context here..
// consider eliminating this special case
return new InternalProvisionException(messages);
} else {
return new InternalProvisionException(Messages.create(cause, messageFormat, arguments));
}
}
public static InternalProvisionException subtypeNotProvided(
Class<? extends javax.inject.Provider<?>> providerType, Class<?> type) {
return create("%s doesn't provide instances of %s.", providerType, type);
}
public static InternalProvisionException errorInProvider(Throwable cause) {
return errorInUserCode(cause, "Error in custom provider, %s", cause);
}
public static InternalProvisionException errorInjectingMethod(Throwable cause) {
return errorInUserCode(cause, "Error injecting method, %s", cause);
}
public static InternalProvisionException errorInjectingConstructor(Throwable cause) {
return errorInUserCode(cause, "Error injecting constructor, %s", cause);
}
public static InternalProvisionException errorInUserInjector(
MembersInjector<?> listener, TypeLiteral<?> type, RuntimeException cause) {
return errorInUserCode(
cause, "Error injecting %s using %s.%n Reason: %s", type, listener, cause);
}
public static InternalProvisionException jitDisabled(Key<?> key) {
return create("Explicit bindings are required and %s is not explicitly bound.", key);
}
public static InternalProvisionException errorNotifyingInjectionListener(
InjectionListener<?> listener, TypeLiteral<?> type, RuntimeException cause) {
return errorInUserCode(
cause, "Error notifying InjectionListener %s of %s.%n Reason: %s", listener, type, cause);
}
/**
* Returns {@code value} if it is non-null or allowed to be null. Otherwise a message is added and
* an {@code InternalProvisionException} is thrown.
*/
static void onNullInjectedIntoNonNullableDependency(Object source, Dependency<?> dependency)
throws InternalProvisionException {
// Hack to allow null parameters to @Provides methods, for backwards compatibility.
if (dependency.getInjectionPoint().getMember() instanceof Method) {
Method annotated = (Method) dependency.getInjectionPoint().getMember();
if (annotated.isAnnotationPresent(Provides.class)) {
switch (InternalFlags.getNullableProvidesOption()) {
case ERROR:
break; // break out & let the below exception happen
case IGNORE:
return; // user doesn't care about injecting nulls to non-@Nullables.
case WARN:
// Warn only once, otherwise we spam logs too much.
if (warnedDependencies.add(dependency)) {
logger.log(
Level.WARNING,
"Guice injected null into {0} (a {1}), please mark it @Nullable."
+ " Use -Dguice_check_nullable_provides_params=ERROR to turn this into an"
+ " error.",
new Object[] {
Messages.formatParameter(dependency), Messages.convert(dependency.getKey())
});
}
return;
}
}
}
Object formattedDependency =
(dependency.getParameterIndex() != -1)
? Messages.formatParameter(dependency)
: StackTraceElements.forMember(dependency.getInjectionPoint().getMember());
throw InternalProvisionException.create(
"null returned by binding at %s%n but %s is not @Nullable", source, formattedDependency)
.addSource(source);
}
private final List<Object> sourcesToPrepend = new ArrayList<>();
private final ImmutableList<Message> errors;
private InternalProvisionException(Message error) {
this(ImmutableList.of(error));
}
private InternalProvisionException(Iterable<Message> errors) {
this.errors = ImmutableList.copyOf(errors);
checkArgument(!this.errors.isEmpty(), "Can't create a provision exception with no errors");
}
/**
* Prepends the given {@code source} to the stack of binding sources for the errors reported in
* this exception.
*
* <p>See {@link Errors#withSource(Object)}
*
* <p>It is expected that this method is called as the exception propagates up the stack.
*
* @param source
* @return {@code this}
*/
InternalProvisionException addSource(Object source) {
if (source == SourceProvider.UNKNOWN_SOURCE) {
return this;
}
int sz = sourcesToPrepend.size();
if (sz > 0 && sourcesToPrepend.get(sz - 1) == source) {
// This is for when there are two identical sources added in a row. This behavior is copied
// from Errors.withSource where it can happen when an constructor/provider method throws an
// exception
return this;
}
sourcesToPrepend.add(source);
return this;
}
ImmutableList<Message> getErrors() {
ImmutableList.Builder<Message> builder = ImmutableList.builder();
// reverse them since sources are added as the exception propagates (so the first source is the
// last one added)
List<Object> newSources = Lists.reverse(sourcesToPrepend);
for (Message error : errors) {
builder.add(Messages.mergeSources(newSources, error));
}
return builder.build();
}
/** Returns this exception convered to a ProvisionException. */
public ProvisionException toProvisionException() {
return new ProvisionException(getErrors());
}
}

View file

@ -15,8 +15,9 @@ import java.util.Set;
final class LinkedProviderBindingImpl<T>
extends BindingImpl<T> implements ProviderKeyBinding<T>, HasDependencies, DelayedInitialize {
final Key<? extends javax.inject.Provider<? extends T>> providerKey;
final DelayedInitialize delayedInitializer;
private final Key<? extends javax.inject.Provider<? extends T>> providerKey;
private final DelayedInitialize delayedInitializer;
private LinkedProviderBindingImpl(InjectorImpl injector, Key<T> key, Object source,
InternalFactory<? extends T> internalFactory, Scoping scoping,
@ -27,7 +28,7 @@ final class LinkedProviderBindingImpl<T>
this.delayedInitializer = delayedInitializer;
}
public LinkedProviderBindingImpl(InjectorImpl injector, Key<T> key, Object source,
LinkedProviderBindingImpl(InjectorImpl injector, Key<T> key, Object source,
InternalFactory<? extends T> internalFactory, Scoping scoping,
Key<? extends javax.inject.Provider<? extends T>> providerKey) {
this(injector, key, source, internalFactory, scoping, providerKey, null);
@ -44,36 +45,43 @@ final class LinkedProviderBindingImpl<T>
Object source, InternalFactory<? extends T> internalFactory, Scoping scoping,
Key<? extends javax.inject.Provider<? extends T>> providerKey,
DelayedInitialize delayedInitializer) {
return new LinkedProviderBindingImpl<T>(injector, key, source, internalFactory, scoping,
return new LinkedProviderBindingImpl<>(injector, key, source, internalFactory, scoping,
providerKey, delayedInitializer);
}
@Override
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
return visitor.visit(this);
}
@Override
public Key<? extends javax.inject.Provider<? extends T>> getProviderKey() {
return providerKey;
}
@Override
public void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
if (delayedInitializer != null) {
delayedInitializer.initialize(injector, errors);
}
}
@Override
public Set<Dependency<?>> getDependencies() {
return ImmutableSet.<Dependency<?>>of(Dependency.get(providerKey));
return ImmutableSet.of(Dependency.get(providerKey));
}
@Override
public BindingImpl<T> withScoping(Scoping scoping) {
return new LinkedProviderBindingImpl<T>(getSource(), getKey(), scoping, providerKey);
return new LinkedProviderBindingImpl<>(getSource(), getKey(), scoping, providerKey);
}
@Override
public BindingImpl<T> withKey(Key<T> key) {
return new LinkedProviderBindingImpl<T>(getSource(), key, getScoping(), providerKey);
return new LinkedProviderBindingImpl<>(getSource(), key, getScoping(), providerKey);
}
@Override
public void applyTo(Binder binder) {
getScoping().applyTo(binder.withSource(getSource())
.bind(getKey()).toProvider(getProviderKey()));

View file

@ -13,68 +13,68 @@ import com.google.inject.spi.InjectionPoint;
* Injects members of instances of a given type.
*/
final class MembersInjectorImpl<T> implements MembersInjector<T> {
private final TypeLiteral<T> typeLiteral;
private final InjectorImpl injector;
private final ImmutableList<SingleMemberInjector> memberInjectors;
private final ImmutableSet<MembersInjector<? super T>> userMembersInjectors;
private final ImmutableSet<InjectionListener<? super T>> injectionListeners;
MembersInjectorImpl(InjectorImpl injector, TypeLiteral<T> typeLiteral,
EncounterImpl<T> encounter, ImmutableList<SingleMemberInjector> memberInjectors) {
private final TypeLiteral<T> typeLiteral;
private final InjectorImpl injector;
private final ImmutableList<SingleMemberInjector> memberInjectors;
private final ImmutableList<MembersInjector<? super T>> userMembersInjectors;
private final ImmutableList<InjectionListener<? super T>> injectionListeners;
MembersInjectorImpl(InjectorImpl injector,
TypeLiteral<T> typeLiteral,
EncounterImpl<T> encounter,
ImmutableList<SingleMemberInjector> memberInjectors) {
this.injector = injector;
this.typeLiteral = typeLiteral;
this.memberInjectors = memberInjectors;
this.userMembersInjectors = encounter.getMembersInjectors();
this.injectionListeners = encounter.getInjectionListeners();
this.memberInjectors = memberInjectors.isEmpty() ? null : memberInjectors;
this.userMembersInjectors =
encounter.getMembersInjectors().isEmpty() ? null : encounter.getMembersInjectors().asList();
this.injectionListeners =
encounter.getInjectionListeners().isEmpty()
? null
: encounter.getInjectionListeners().asList();
}
public ImmutableList<SingleMemberInjector> getMemberInjectors() {
return memberInjectors;
return memberInjectors == null ? ImmutableList.of() : memberInjectors;
}
public void injectMembers(T instance) {
Errors errors = new Errors(typeLiteral);
TypeLiteral<T> localTypeLiteral = typeLiteral;
try {
injectAndNotify(instance, errors, null, null, typeLiteral, false);
} catch (ErrorsException e) {
errors.merge(e.getErrors());
injectAndNotify(instance, null, null, localTypeLiteral, false);
} catch (InternalProvisionException ipe) {
throw ipe.addSource(localTypeLiteral).toProvisionException();
}
errors.throwProvisionExceptionIfErrorsExist();
}
void injectAndNotify(final T instance,
final Errors errors,
final Key<T> key, // possibly null!
final ProvisionListenerStackCallback<T> provisionCallback, // possibly null!
final Object source,
final boolean toolableOnly) throws ErrorsException {
final boolean toolableOnly) throws InternalProvisionException {
if (instance == null) {
return;
}
injector.callInContext(new ContextualCallable<Void>() {
@Override
public Void call(final InternalContext context) throws ErrorsException {
context.pushState(key, source);
try {
if (provisionCallback != null && provisionCallback.hasListeners()) {
provisionCallback.provision(errors, context, new ProvisionCallback<T>() {
@Override
public T call() {
injectMembers(instance, errors, context, toolableOnly);
return instance;
}
final InternalContext context = injector.enterContext();
context.pushState(key, source);
try {
if (provisionCallback != null && provisionCallback.hasListeners()) {
provisionCallback.provision(context, () -> {
injectMembers(instance, context, toolableOnly);
return instance;
});
} else {
injectMembers(instance, errors, context, toolableOnly);
}
} finally {
context.popState();
}
return null;
} else {
injectMembers(instance, context, toolableOnly);
}
});
} finally {
context.popState();
context.close();
}
// TODO: We *could* notify listeners too here,
// but it's not clear if we want to. There's no way to know
@ -85,38 +85,54 @@ final class MembersInjectorImpl<T> implements MembersInjector<T> {
// the above callInContext could return 'true' if it injected
// anything.)
if (!toolableOnly) {
notifyListeners(instance, errors);
notifyListeners(instance);
}
}
void notifyListeners(T instance, Errors errors) throws ErrorsException {
int numErrorsBefore = errors.size();
for (InjectionListener<? super T> injectionListener : injectionListeners) {
void notifyListeners(T instance) throws InternalProvisionException {
ImmutableList<InjectionListener<? super T>> localInjectionListeners = injectionListeners;
if (localInjectionListeners == null) {
// no listeners
return;
}
// optimization: use manual for/each to save allocating an iterator here
for (int i = 0; i < localInjectionListeners.size(); i++) {
InjectionListener<? super T> injectionListener = localInjectionListeners.get(i);
try {
injectionListener.afterInjection(instance);
} catch (RuntimeException e) {
errors.errorNotifyingInjectionListener(injectionListener, typeLiteral, e);
throw InternalProvisionException.errorNotifyingInjectionListener(
injectionListener, typeLiteral, e);
}
}
errors.throwIfNewErrors(numErrorsBefore);
}
void injectMembers(T t, Errors errors, InternalContext context, boolean toolableOnly) {
// optimization: use manual for/each to save allocating an iterator here
for (int i = 0, size = memberInjectors.size(); i < size; i++) {
SingleMemberInjector injector = memberInjectors.get(i);
if (!toolableOnly || injector.getInjectionPoint().isToolable()) {
injector.inject(errors, context, t);
void injectMembers(T t, InternalContext context, boolean toolableOnly)
throws InternalProvisionException {
ImmutableList<SingleMemberInjector> localMembersInjectors = memberInjectors;
if (localMembersInjectors != null) {
// optimization: use manual for/each to save allocating an iterator here
for (int i = 0, size = localMembersInjectors.size(); i < size; i++) {
SingleMemberInjector injector = localMembersInjectors.get(i);
if (!toolableOnly || injector.getInjectionPoint().isToolable()) {
injector.inject(context, t);
}
}
}
// TODO: There's no way to know if a user's MembersInjector wants toolable injections.
if (!toolableOnly) {
for (MembersInjector<? super T> userMembersInjector : userMembersInjectors) {
try {
userMembersInjector.injectMembers(t);
} catch (RuntimeException e) {
errors.errorInUserInjector(userMembersInjector, typeLiteral, e);
ImmutableList<MembersInjector<? super T>> localUsersMembersInjectors = userMembersInjectors;
if (localUsersMembersInjectors != null) {
// optimization: use manual for/each to save allocating an iterator here
for (int i = 0; i < localUsersMembersInjectors.size(); i++) {
MembersInjector<? super T> userMembersInjector = localUsersMembersInjectors.get(i);
try {
userMembersInjector.injectMembers(t);
} catch (RuntimeException e) {
throw InternalProvisionException.errorInUserInjector(
userMembersInjector, typeLiteral, e);
}
}
}
}
@ -128,11 +144,15 @@ final class MembersInjectorImpl<T> implements MembersInjector<T> {
}
public ImmutableSet<InjectionPoint> getInjectionPoints() {
ImmutableSet.Builder<InjectionPoint> builder = ImmutableSet.builder();
for (SingleMemberInjector memberInjector : memberInjectors) {
builder.add(memberInjector.getInjectionPoint());
ImmutableList<SingleMemberInjector> localMemberInjectors = memberInjectors;
if (localMemberInjectors != null) {
ImmutableSet.Builder<InjectionPoint> builder = ImmutableSet.builder();
for (SingleMemberInjector memberInjector : localMemberInjectors) {
builder.add(memberInjector.getInjectionPoint());
}
return builder.build();
}
return builder.build();
return ImmutableSet.of();
}
}

View file

@ -1,6 +1,9 @@
package com.google.inject.internal;
import static com.google.common.collect.ImmutableListMultimap.flatteningToImmutableListMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.inject.ConfigurationException;
@ -11,24 +14,25 @@ import com.google.inject.spi.TypeListenerBinding;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Members injectors by type.
*/
final class MembersInjectorStore {
private final InjectorImpl injector;
private final ImmutableList<TypeListenerBinding> typeListenerBindings;
private final FailableCache<TypeLiteral<?>, MembersInjectorImpl<?>> cache
= new FailableCache<TypeLiteral<?>, MembersInjectorImpl<?>>() {
@Override
protected MembersInjectorImpl<?> create(TypeLiteral<?> type, Errors errors)
throws ErrorsException {
return createWithListeners(type, errors);
}
};
private final FailableCache<TypeLiteral<?>, MembersInjectorImpl<?>> cache = new FailableCache<>() {
@Override
protected MembersInjectorImpl<?> create(TypeLiteral<?> type, Errors errors)
throws ErrorsException {
return createWithListeners(type, errors);
}
};
MembersInjectorStore(InjectorImpl injector,
List<TypeListenerBinding> typeListenerBindings) {
this.injector = injector;
@ -39,7 +43,7 @@ final class MembersInjectorStore {
* Returns true if any type listeners are installed. Other code may take shortcuts when there
* aren't any type listeners.
*/
public boolean hasTypeListeners() {
boolean hasTypeListeners() {
return !typeListenerBindings.isEmpty();
}
@ -81,7 +85,7 @@ final class MembersInjectorStore {
ImmutableList<SingleMemberInjector> injectors = getInjectors(injectionPoints, errors);
errors.throwIfNewErrors(numErrorsBefore);
EncounterImpl<T> encounter = new EncounterImpl<T>(errors, injector.lookups);
EncounterImpl<T> encounter = new EncounterImpl<>(errors, injector.lookups);
Set<TypeListener> alreadySeenListeners = Sets.newHashSet();
for (TypeListenerBinding binding : typeListenerBindings) {
TypeListener typeListener = binding.getListener();
@ -97,7 +101,7 @@ final class MembersInjectorStore {
encounter.invalidate();
errors.throwIfNewErrors(numErrorsBefore);
return new MembersInjectorImpl<T>(injector, type, encounter, injectors);
return new MembersInjectorImpl<>(injector, type, encounter, injectors);
}
/**
@ -121,4 +125,11 @@ final class MembersInjectorStore {
}
return ImmutableList.copyOf(injectors);
}
ImmutableListMultimap<TypeLiteral<?>, InjectionPoint> getAllInjectionPoints() {
return cache.asMap().entrySet().stream()
.collect(
flatteningToImmutableListMultimap(
Map.Entry::getKey, entry -> entry.getValue().getInjectionPoints().stream()));
}
}

View file

@ -12,20 +12,8 @@ final class MessageProcessor extends AbstractProcessor {
super(errors);
}
public static String getRootMessage(Throwable t) {
Throwable cause = t.getCause();
return cause == null ? t.toString() : getRootMessage(cause);
}
@Override
public Boolean visit(Message message) {
if (message.getCause() != null) {
String rootMessage = getRootMessage(message.getCause());
/*logger.log(Level.INFO,
"An exception was caught and reported. Message: " + rootMessage,
message.getCause());*/
}
errors.addMessage(message);
return true;
}

View file

@ -0,0 +1,391 @@
package com.google.inject.internal;
import static com.google.common.base.Preconditions.checkArgument;
import com.google.common.base.Equivalence;
import com.google.common.base.Objects;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.inject.Key;
import com.google.inject.TypeLiteral;
import com.google.inject.internal.util.Classes;
import com.google.inject.internal.util.StackTraceElements;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.ElementSource;
import com.google.inject.spi.InjectionPoint;
import com.google.inject.spi.Message;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.util.Arrays;
import java.util.Collection;
import java.util.Formatter;
import java.util.List;
import java.util.Map;
/**
* Utility methods for {@link Message} objects
*/
public final class Messages {
private Messages() {
}
/**
* Prepends the list of sources to the given {@link Message}
*/
static Message mergeSources(List<Object> sources, Message message) {
List<Object> messageSources = message.getSources();
// It is possible that the end of getSources() and the beginning of message.getSources() are
// equivalent, in this case we should drop the repeated source when joining the lists. The
// most likely scenario where this would happen is when a scoped binding throws an exception,
// due to the fact that InternalFactoryToProviderAdapter applies the binding source when
// merging errors.
if (!sources.isEmpty() && !messageSources.isEmpty() &&
Objects.equal(messageSources.get(0), sources.get(sources.size() - 1))) {
messageSources = messageSources.subList(1, messageSources.size());
}
return new Message(ImmutableList.builder().addAll(sources).addAll(messageSources).build(),
message.getMessage(), message.getCause());
}
/**
* Calls {@link String#format} after converting the arguments using some standard guice formatting
* for {@link Key}, {@link Class} and {@link Member} objects.
*/
public static String format(String messageFormat, Object... arguments) {
for (int i = 0; i < arguments.length; i++) {
arguments[i] = convert(arguments[i]);
}
return String.format(messageFormat, arguments);
}
/**
* Returns the formatted message for an exception with the specified messages.
*/
public static String formatMessages(String heading, Collection<Message> errorMessages) {
Formatter fmt = new Formatter().format(heading).format(":%n%n");
int index = 1;
boolean displayCauses = getOnlyCause(errorMessages) == null;
Map<Equivalence.Wrapper<Throwable>, Integer> causes = Maps.newHashMap();
for (Message errorMessage : errorMessages) {
int thisIdx = index++;
fmt.format("%s) %s%n", thisIdx, errorMessage.getMessage());
List<Object> dependencies = errorMessage.getSources();
for (int i = dependencies.size() - 1; i >= 0; i--) {
Object source = dependencies.get(i);
formatSource(fmt, source);
}
Throwable cause = errorMessage.getCause();
if (displayCauses && cause != null) {
Equivalence.Wrapper<Throwable> causeEquivalence = ThrowableEquivalence.INSTANCE.wrap(cause);
if (!causes.containsKey(causeEquivalence)) {
causes.put(causeEquivalence, thisIdx);
fmt.format("Caused by: %s", Throwables.getStackTraceAsString(cause));
} else {
int causeIdx = causes.get(causeEquivalence);
fmt.format(
"Caused by: %s (same stack trace as error #%s)",
cause.getClass().getName(), causeIdx);
}
}
fmt.format("%n");
}
if (errorMessages.size() == 1) {
fmt.format("1 error");
} else {
fmt.format("%s errors", errorMessages.size());
}
return fmt.toString();
}
/**
* Creates a new Message without a cause.
*
* @param messageFormat Format string
* @param arguments format string arguments
*/
public static Message create(String messageFormat, Object... arguments) {
return create(null, messageFormat, arguments);
}
/**
* Creates a new Message with the given cause.
*
* @param cause The exception that caused the error
* @param messageFormat Format string
* @param arguments format string arguments
*/
public static Message create(Throwable cause, String messageFormat, Object... arguments) {
return create(cause, ImmutableList.of(), messageFormat, arguments);
}
/**
* Creates a new Message with the given cause and a binding source stack.
*
* @param cause The exception that caused the error
* @param sources The binding sources for the source stack
* @param messageFormat Format string
* @param arguments format string arguments
*/
public static Message create(Throwable cause, List<Object> sources, String messageFormat, Object... arguments) {
String message = format(messageFormat, arguments);
return new Message(sources, message, cause);
}
/**
* Formats an object in a user friendly way.
*/
public static Object convert(Object o) {
ElementSource source = null;
if (o instanceof ElementSource) {
source = (ElementSource) o;
o = source.getDeclaringSource();
}
return convert(o, source);
}
public static Object convert(Object o, ElementSource source) {
for (Converter<?> converter : converters) {
if (converter.appliesTo(o)) {
return appendModules(converter.convert(o), source);
}
}
return appendModules(o, source);
}
private static Object appendModules(Object source, ElementSource elementSource) {
String modules = moduleSourceString(elementSource);
if (modules.length() == 0) {
return source;
} else {
return source + modules;
}
}
private static String moduleSourceString(ElementSource elementSource) {
// if we only have one module (or don't know what they are), then don't bother
// reporting it, because the source already is going to report exactly that module.
if (elementSource == null) {
return "";
}
List<String> modules = Lists.newArrayList(elementSource.getModuleClassNames());
// Insert any original element sources w/ module info into the path.
while (elementSource.getOriginalElementSource() != null) {
elementSource = elementSource.getOriginalElementSource();
modules.addAll(0, elementSource.getModuleClassNames());
}
if (modules.size() <= 1) {
return "";
}
// Ideally we'd do:
// return Joiner.on(" -> ")
// .appendTo(new StringBuilder(" (via modules: "), Lists.reverse(modules))
// .append(")").toString();
// ... but for some reason we can't find Lists.reverse, so do it the boring way.
StringBuilder builder = new StringBuilder(" (via modules: ");
for (int i = modules.size() - 1; i >= 0; i--) {
builder.append(modules.get(i));
if (i != 0) {
builder.append(" -> ");
}
}
builder.append(")");
return builder.toString();
}
static void formatSource(Formatter formatter, Object source) {
ElementSource elementSource = null;
if (source instanceof ElementSource) {
elementSource = (ElementSource) source;
source = elementSource.getDeclaringSource();
}
formatSource(formatter, source, elementSource);
}
static void formatSource(Formatter formatter, Object source, ElementSource elementSource) {
String modules = moduleSourceString(elementSource);
if (source instanceof Dependency) {
Dependency<?> dependency = (Dependency<?>) source;
InjectionPoint injectionPoint = dependency.getInjectionPoint();
if (injectionPoint != null) {
formatInjectionPoint(formatter, dependency, injectionPoint, elementSource);
} else {
formatSource(formatter, dependency.getKey(), elementSource);
}
} else if (source instanceof InjectionPoint) {
formatInjectionPoint(formatter, null, (InjectionPoint) source, elementSource);
} else if (source instanceof Class) {
formatter.format(" at %s%s%n", StackTraceElements.forType((Class<?>) source), modules);
} else if (source instanceof Member) {
formatter.format(" at %s%s%n", StackTraceElements.forMember((Member) source), modules);
} else if (source instanceof TypeLiteral) {
formatter.format(" while locating %s%s%n", source, modules);
} else if (source instanceof Key) {
Key<?> key = (Key<?>) source;
formatter.format(" while locating %s%n", convert(key, elementSource));
} else if (source instanceof Thread) {
formatter.format(" in thread %s%n", source);
} else {
formatter.format(" at %s%s%n", source, modules);
}
}
private static void formatInjectionPoint(
Formatter formatter,
Dependency<?> dependency,
InjectionPoint injectionPoint,
ElementSource elementSource) {
Member member = injectionPoint.getMember();
Class<? extends Member> memberType = Classes.memberType(member);
if (memberType == Field.class) {
dependency = injectionPoint.getDependencies().get(0);
formatter.format(" while locating %s%n", convert(dependency.getKey(), elementSource));
formatter.format(" for field at %s%n", StackTraceElements.forMember(member));
} else if (dependency != null) {
formatter.format(" while locating %s%n", convert(dependency.getKey(), elementSource));
formatter.format(" for %s%n", formatParameter(dependency));
} else {
formatSource(formatter, injectionPoint.getMember());
}
}
static String formatParameter(Dependency<?> dependency) {
int ordinal = dependency.getParameterIndex() + 1;
return String.format(
"the %s%s parameter of %s",
ordinal,
getOrdinalSuffix(ordinal),
StackTraceElements.forMember(dependency.getInjectionPoint().getMember()));
}
/**
* Maps {@code 1} to the string {@code "1st"} ditto for all non-negative numbers
*
* @see <a href="https://en.wikipedia.org/wiki/English_numerals#Ordinal_numbers">
* https://en.wikipedia.org/wiki/English_numerals#Ordinal_numbers</a>
*/
private static String getOrdinalSuffix(int ordinal) {
// negative ordinals don't make sense, we allow zero though because we are programmers
checkArgument(ordinal >= 0);
if ((ordinal / 10) % 10 == 1) {
// all the 'teens' are weird
return "th";
} else {
// could use a lookup table? any better?
switch (ordinal % 10) {
case 1:
return "st";
case 2:
return "nd";
case 3:
return "rd";
default:
return "th";
}
}
}
private abstract static class Converter<T> {
final Class<T> type;
Converter(Class<T> type) {
this.type = type;
}
boolean appliesTo(Object o) {
return o != null && type.isAssignableFrom(o.getClass());
}
String convert(Object o) {
return toString(type.cast(o));
}
abstract String toString(T t);
}
@SuppressWarnings({"rawtypes"}) // rawtypes aren't avoidable
private static final Collection<Converter<?>> converters =
ImmutableList.of(
new Converter<>(Class.class) {
@Override
public String toString(Class c) {
return c.getName();
}
},
new Converter<>(Member.class) {
@Override
public String toString(Member member) {
return Classes.toString(member);
}
},
new Converter<>(Key.class) {
@Override
public String toString(Key key) {
if (key.getAnnotationType() != null) {
return key.getTypeLiteral()
+ " annotated with "
+ (key.getAnnotation() != null ? key.getAnnotation() : key.getAnnotationType());
} else {
return key.getTypeLiteral().toString();
}
}
});
/**
* Returns the cause throwable if there is exactly one cause in {@code messages}. If there are
* zero or multiple messages with causes, null is returned.
*/
public static Throwable getOnlyCause(Collection<Message> messages) {
Throwable onlyCause = null;
for (Message message : messages) {
Throwable messageCause = message.getCause();
if (messageCause == null) {
continue;
}
if (onlyCause != null && !ThrowableEquivalence.INSTANCE.equivalent(onlyCause, messageCause)) {
return null;
}
onlyCause = messageCause;
}
return onlyCause;
}
private static final class ThrowableEquivalence extends Equivalence<Throwable> {
static final ThrowableEquivalence INSTANCE = new ThrowableEquivalence();
@Override
protected boolean doEquivalent(Throwable a, Throwable b) {
return a.getClass().equals(b.getClass())
&& Objects.equal(a.getMessage(), b.getMessage())
&& Arrays.equals(a.getStackTrace(), b.getStackTrace())
&& equivalent(a.getCause(), b.getCause());
}
@Override
protected int doHash(Throwable t) {
return Objects.hashCode(t.getClass().hashCode(), t.getMessage(), hash(t.getCause()));
}
}
}

View file

@ -264,7 +264,7 @@ public class MoreTypes {
// we skip searching through interfaces if unknown is an interface
if (toResolve.isInterface()) {
Class[] interfaces = rawType.getInterfaces();
Class<?>[] interfaces = rawType.getInterfaces();
for (int i = 0, length = interfaces.length; i < length; i++) {
if (interfaces[i] == toResolve) {
return rawType.getGenericInterfaces()[i];
@ -291,7 +291,7 @@ public class MoreTypes {
return toResolve;
}
public static Type resolveTypeVariable(Type type, Class<?> rawType, TypeVariable unknown) {
public static Type resolveTypeVariable(Type type, Class<?> rawType, TypeVariable<?> unknown) {
Class<?> declaredByRaw = declaringClassOf(unknown);
// we can't reduce this further
@ -321,7 +321,7 @@ public class MoreTypes {
* Returns the declaring class of {@code typeVariable}, or {@code null} if it was not declared by
* a class.
*/
private static Class<?> declaringClassOf(TypeVariable typeVariable) {
private static Class<?> declaringClassOf(TypeVariable<?> typeVariable) {
GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration();
return genericDeclaration instanceof Class
? (Class<?>) genericDeclaration
@ -365,7 +365,7 @@ public class MoreTypes {
private static void ensureOwnerType(Type ownerType, Type rawType) {
if (rawType instanceof Class<?>) {
Class rawTypeAsClass = (Class) rawType;
Class<?> rawTypeAsClass = (Class) rawType;
checkArgument(ownerType != null || rawTypeAsClass.getEnclosingClass() == null,
"No owner type for enclosed %s", rawType);
checkArgument(ownerType == null || rawTypeAsClass.getEnclosingClass() != null,
@ -373,18 +373,22 @@ public class MoreTypes {
}
}
@Override
public Type[] getActualTypeArguments() {
return typeArguments.clone();
}
@Override
public Type getRawType() {
return rawType;
}
@Override
public Type getOwnerType() {
return ownerType;
}
@Override
public boolean isFullySpecified() {
if (ownerType != null && !MoreTypes.isFullySpecified(ownerType)) {
return false;
@ -441,10 +445,12 @@ public class MoreTypes {
this.componentType = canonicalize(componentType);
}
@Override
public Type getGenericComponentType() {
return componentType;
}
@Override
public boolean isFullySpecified() {
return MoreTypes.isFullySpecified(componentType);
}
@ -495,14 +501,17 @@ public class MoreTypes {
}
}
@Override
public Type[] getUpperBounds() {
return new Type[]{upperBound};
}
@Override
public Type[] getLowerBounds() {
return lowerBound != null ? new Type[]{lowerBound} : EMPTY_TYPE_ARRAY;
}
@Override
public boolean isFullySpecified() {
return MoreTypes.isFullySpecified(upperBound)
&& (lowerBound == null || MoreTypes.isFullySpecified(lowerBound));

View file

@ -25,7 +25,7 @@ final class PrivateElementProcessor extends AbstractProcessor {
return true;
}
public List<InjectorShell.Builder> getInjectorShellBuilders() {
List<InjectorShell.Builder> getInjectorShellBuilders() {
return injectorShellBuilders;
}
}

View file

@ -49,10 +49,12 @@ public final class PrivateElementsImpl implements PrivateElements {
this.source = checkNotNull(source, "source");
}
@Override
public Object getSource() {
return source;
}
@Override
public List<Element> getElements() {
if (elements == null) {
elements = ImmutableList.copyOf(elementsMutable);
@ -62,15 +64,17 @@ public final class PrivateElementsImpl implements PrivateElements {
return elements;
}
@Override
public Injector getInjector() {
return injector;
}
public void initInjector(Injector injector) {
void initInjector(Injector injector) {
checkState(this.injector == null, "injector already initialized");
this.injector = checkNotNull(injector, "injector");
}
@Override
public Set<Key<?>> getExposedKeys() {
if (exposedKeysToSources == null) {
Map<Key<?>, Object> exposedKeysToSourcesMutable = Maps.newLinkedHashMap();
@ -84,6 +88,7 @@ public final class PrivateElementsImpl implements PrivateElements {
return exposedKeysToSources.keySet();
}
@Override
public <T> T acceptVisitor(ElementVisitor<T> visitor) {
return visitor.visit(this);
}
@ -96,6 +101,7 @@ public final class PrivateElementsImpl implements PrivateElements {
exposureBuilders.add(exposureBuilder);
}
@Override
public void applyTo(Binder binder) {
PrivateBinder privateBinder = binder.withSource(source).newPrivateBinder();
@ -109,6 +115,7 @@ public final class PrivateElementsImpl implements PrivateElements {
}
}
@Override
public Object getExposedSource(Key<?> key) {
getExposedKeys(); // ensure exposedKeysToSources is populated
Object source = exposedKeysToSources.get(key);

View file

@ -11,8 +11,11 @@ import java.util.List;
class ProcessedBindingData {
private final List<CreationListener> creationListeners = Lists.newArrayList();
private final List<Runnable> uninitializedBindings = Lists.newArrayList();
private final List<Runnable> delayedUninitializedBindings = Lists.newArrayList();
void addCreationListener(CreationListener listener) {
creationListeners.add(listener);
}
@ -21,6 +24,10 @@ class ProcessedBindingData {
uninitializedBindings.add(runnable);
}
void addDelayedUninitializedBinding(Runnable runnable) {
delayedUninitializedBindings.add(runnable);
}
void initializeBindings() {
for (Runnable initializer : uninitializedBindings) {
initializer.run();
@ -33,4 +40,14 @@ class ProcessedBindingData {
}
}
/**
* Initialized bindings that need to be delayed until after all injection points and other
* bindings are processed. The main current usecase for this is resolving Optional dependencies
* for OptionalBinder bindings.
*/
void initializeDelayedBindings() {
for (Runnable initializer : delayedUninitializedBindings) {
initializer.run();
}
}
}

View file

@ -2,11 +2,9 @@ package com.google.inject.internal;
import com.google.inject.Key;
import com.google.inject.ProvidedBy;
import com.google.inject.Provider;
import com.google.inject.internal.InjectorImpl.JitLimitation;
import com.google.inject.spi.Dependency;
import static com.google.common.base.Preconditions.checkState;
import javax.inject.Provider;
/**
* An {@link InternalFactory} for {@literal @}{@link ProvidedBy} bindings.
@ -34,40 +32,48 @@ class ProvidedByInternalFactory<T> extends ProviderInternalFactory<T>
provisionCallback = listener;
}
@Override
public void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
providerBinding =
injector.getBindingOrThrow(providerKey, errors, JitLimitation.NEW_OR_EXISTING_JIT);
}
public T get(Errors errors, InternalContext context, Dependency dependency, boolean linked)
throws ErrorsException {
checkState(providerBinding != null, "not initialized");
@Override
public T get(InternalContext context, Dependency<?> dependency, boolean linked)
throws InternalProvisionException {
BindingImpl<? extends Provider<T>> localProviderBinding = providerBinding;
if (localProviderBinding == null) {
throw new IllegalStateException("not initialized");
}
Key<? extends Provider<T>> localProviderKey = providerKey;
context.pushState(localProviderKey, localProviderBinding.getSource());
context.pushState(providerKey, providerBinding.getSource());
try {
errors = errors.withSource(providerKey);
Provider<? extends T> provider = providerBinding.getInternalFactory().get(
errors, context, dependency, true);
return circularGet(provider, errors, context, dependency, provisionCallback);
Provider<? extends T> provider =
localProviderBinding.getInternalFactory().get(context, dependency, true);
return circularGet(provider, context, dependency, provisionCallback);
} catch (InternalProvisionException ipe) {
throw ipe.addSource(localProviderKey);
} finally {
context.popState();
}
}
@Override
protected T provision(javax.inject.Provider<? extends T> provider, Errors errors,
Dependency<?> dependency, ConstructionContext<T> constructionContext)
throws ErrorsException {
protected T provision(javax.inject.Provider<? extends T> provider,
Dependency<?> dependency,
ConstructionContext<T> constructionContext)
throws InternalProvisionException {
try {
Object o = super.provision(provider, errors, dependency, constructionContext);
Object o = super.provision(provider, dependency, constructionContext);
if (o != null && !rawType.isInstance(o)) {
throw errors.subtypeNotProvided(providerType, rawType).toException();
throw InternalProvisionException.subtypeNotProvided(providerType, rawType);
}
@SuppressWarnings("unchecked") // protected by isInstance() check above
T t = (T) o;
T t = (T) o;
return t;
} catch (RuntimeException e) {
throw errors.errorInProvider(e).toException();
throw InternalProvisionException.errorInProvider(e).addSource(source);
}
}
}

View file

@ -14,13 +14,13 @@ import com.google.inject.spi.ProviderWithExtensionVisitor;
import java.util.Set;
final class ProviderInstanceBindingImpl<T> extends BindingImpl<T>
implements ProviderInstanceBinding<T> {
class ProviderInstanceBindingImpl<T> extends BindingImpl<T> implements ProviderInstanceBinding<T> {
final javax.inject.Provider<? extends T> providerInstance;
final ImmutableSet<InjectionPoint> injectionPoints;
private final javax.inject.Provider<? extends T> providerInstance;
public ProviderInstanceBindingImpl(InjectorImpl injector, Key<T> key,
private final ImmutableSet<InjectionPoint> injectionPoints;
ProviderInstanceBindingImpl(InjectorImpl injector, Key<T> key,
Object source, InternalFactory<? extends T> internalFactory, Scoping scoping,
javax.inject.Provider<? extends T> providerInstance,
Set<InjectionPoint> injectionPoints) {
@ -29,7 +29,7 @@ final class ProviderInstanceBindingImpl<T> extends BindingImpl<T>
this.injectionPoints = ImmutableSet.copyOf(injectionPoints);
}
public ProviderInstanceBindingImpl(Object source, Key<T> key, Scoping scoping,
ProviderInstanceBindingImpl(Object source, Key<T> key, Scoping scoping,
Set<InjectionPoint> injectionPoints, javax.inject.Provider<? extends T> providerInstance) {
super(source, key, scoping);
this.injectionPoints = ImmutableSet.copyOf(injectionPoints);
@ -61,13 +61,11 @@ final class ProviderInstanceBindingImpl<T> extends BindingImpl<T>
}
public BindingImpl<T> withScoping(Scoping scoping) {
return new ProviderInstanceBindingImpl<T>(
getSource(), getKey(), scoping, injectionPoints, providerInstance);
return new ProviderInstanceBindingImpl<>(getSource(), getKey(), scoping, injectionPoints, providerInstance);
}
public BindingImpl<T> withKey(Key<T> key) {
return new ProviderInstanceBindingImpl<T>(
getSource(), key, getScoping(), injectionPoints, providerInstance);
return new ProviderInstanceBindingImpl<>(getSource(), key, getScoping(), injectionPoints, providerInstance);
}
public void applyTo(Binder binder) {

View file

@ -1,6 +1,5 @@
package com.google.inject.internal;
import com.google.inject.internal.ProvisionListenerStackCallback.ProvisionCallback;
import com.google.inject.spi.Dependency;
import javax.inject.Provider;
@ -20,33 +19,28 @@ abstract class ProviderInternalFactory<T> implements InternalFactory<T> {
this.source = checkNotNull(source, "source");
}
protected T circularGet(final Provider<? extends T> provider, final Errors errors,
InternalContext context, final Dependency<?> dependency,
protected T circularGet(final Provider<? extends T> provider,
InternalContext context,
final Dependency<?> dependency,
ProvisionListenerStackCallback<T> provisionCallback)
throws ErrorsException {
throws InternalProvisionException {
final ConstructionContext<T> constructionContext = context.getConstructionContext(this);
// We have a circular reference between constructors. Return a proxy.
if (constructionContext.isConstructing()) {
Class<?> expectedType = dependency.getKey().getTypeLiteral().getRawType();
// TODO: if we can't proxy this object, can we proxy the other object?
@SuppressWarnings("unchecked")
T proxyType = (T) constructionContext.createProxy(
errors, context.getInjectorOptions(), expectedType);
T proxyType = (T) constructionContext.createProxy(context.getInjectorOptions(), expectedType);
return proxyType;
}
// Optimization: Don't go through the callback stack if no one's listening.
constructionContext.startConstruction();
try {
if (!provisionCallback.hasListeners()) {
return provision(provider, errors, dependency, constructionContext);
return provision(provider, dependency, constructionContext);
} else {
return provisionCallback.provision(errors, context, new ProvisionCallback<T>() {
public T call() throws ErrorsException {
return provision(provider, errors, dependency, constructionContext);
}
});
return provisionCallback.provision(context, () ->
provision(provider, dependency, constructionContext));
}
} finally {
constructionContext.removeCurrentReference();
@ -58,9 +52,12 @@ abstract class ProviderInternalFactory<T> implements InternalFactory<T> {
* Provisions a new instance. Subclasses should override this to catch
* exceptions & rethrow as ErrorsExceptions.
*/
protected T provision(Provider<? extends T> provider, Errors errors, Dependency<?> dependency,
ConstructionContext<T> constructionContext) throws ErrorsException {
T t = errors.checkForNull(provider.get(), source, dependency);
protected T provision(Provider<? extends T> provider, Dependency<?> dependency,
ConstructionContext<T> constructionContext) throws InternalProvisionException {
T t = provider.get();
if (t == null && !dependency.isNullable()) {
InternalProvisionException.onNullInjectedIntoNonNullableDependency(source, dependency);
}
constructionContext.setProxyDelegates(t);
return t;
}

View file

@ -6,7 +6,6 @@ import com.google.inject.Binder;
import com.google.inject.Exposed;
import com.google.inject.Key;
import com.google.inject.PrivateBinder;
import com.google.inject.Provider;
import com.google.inject.Provides;
import com.google.inject.internal.util.StackTraceElements;
import com.google.inject.spi.BindingTargetVisitor;
@ -21,36 +20,50 @@ import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.Set;
/**
* A provider that invokes a method and returns its result.
*
*/
public abstract class ProviderMethod<T> implements ProviderWithExtensionVisitor<T>, HasDependencies,
ProvidesMethodBinding<T> {
public abstract class ProviderMethod<T>
extends InternalProviderInstanceBindingImpl.CyclicFactory<T>
implements HasDependencies, ProviderWithExtensionVisitor<T>, ProvidesMethodBinding<T> {
protected final Object instance;
protected final Method method;
private final Key<T> key;
private final Class<? extends Annotation> scopeAnnotation;
private final ImmutableSet<Dependency<?>> dependencies;
private final List<Provider<?>> parameterProviders;
private final boolean exposed;
private final Annotation annotation;
/**
* Set by {@link #initialize(InjectorImpl, Errors)} so it is always available prior to injection.
*/
private SingleParameterInjector<?>[] parameterInjectors;
/**
* @param method the method to invoke. It's return type must be the same type as {@code key}.
*/
private ProviderMethod(Key<T> key, Method method, Object instance,
ImmutableSet<Dependency<?>> dependencies, List<Provider<?>> parameterProviders,
Class<? extends Annotation> scopeAnnotation, Annotation annotation) {
private ProviderMethod(Key<T> key,
Method method,
Object instance,
ImmutableSet<Dependency<?>> dependencies,
Class<? extends Annotation> scopeAnnotation,
Annotation annotation) {
super(InternalProviderInstanceBindingImpl.InitializationTiming.EAGER);
this.key = key;
this.scopeAnnotation = scopeAnnotation;
this.instance = instance;
this.dependencies = dependencies;
this.method = method;
this.parameterProviders = parameterProviders;
this.exposed = method.isAnnotationPresent(Exposed.class);
this.annotation = annotation;
}
@ -58,24 +71,19 @@ public abstract class ProviderMethod<T> implements ProviderWithExtensionVisitor<
/**
* Creates a {@link ProviderMethod}.
*/
static <T> ProviderMethod<T> create(Key<T> key, Method method, Object instance,
ImmutableSet<Dependency<?>> dependencies, List<Provider<?>> parameterProviders,
Class<? extends Annotation> scopeAnnotation, boolean skipFastClassGeneration,
static <T> ProviderMethod<T> create(Key<T> key,
Method method,
Object instance,
ImmutableSet<Dependency<?>> dependencies,
Class<? extends Annotation> scopeAnnotation,
boolean skipFastClassGeneration,
Annotation annotation) {
int modifiers = method.getModifiers();
if (!Modifier.isPublic(modifiers) ||
!Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
method.setAccessible(true);
}
return new ReflectionProviderMethod<T>(key,
method,
instance,
dependencies,
parameterProviders,
scopeAnnotation,
annotation);
return new ReflectionProviderMethod<>(key, method, instance, dependencies, scopeAnnotation, annotation);
}
@Override
@ -120,27 +128,31 @@ public abstract class ProviderMethod<T> implements ProviderWithExtensionVisitor<
}
@Override
public T get() {
Object[] parameters = new Object[parameterProviders.size()];
for (int i = 0; i < parameters.length; i++) {
parameters[i] = parameterProviders.get(i).get();
}
void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
parameterInjectors = injector.getParametersInjectors(dependencies.asList(), errors);
}
@Override
protected T doProvision(InternalContext context, Dependency<?> dependency)
throws InternalProvisionException {
try {
@SuppressWarnings({"unchecked", "UnnecessaryLocalVariable"})
T result = (T) doProvision(parameters);
return result;
T t = doProvision(SingleParameterInjector.getAll(context, parameterInjectors));
if (t == null && !dependency.isNullable()) {
InternalProvisionException.onNullInjectedIntoNonNullableDependency(getMethod(), dependency);
}
return t;
} catch (IllegalAccessException e) {
throw new AssertionError(e);
} catch (InvocationTargetException e) {
throw Exceptions.rethrowCause(e);
} catch (InvocationTargetException userException) {
Throwable cause = userException.getCause() != null ? userException.getCause() : userException;
throw InternalProvisionException.errorInProvider(cause).addSource(getSource());
}
}
/**
* Extension point for our subclasses to implement the provisioning strategy.
*/
abstract Object doProvision(Object[] parameters)
abstract T doProvision(Object[] parameters)
throws IllegalAccessException, InvocationTargetException;
@Override
@ -199,22 +211,21 @@ public abstract class ProviderMethod<T> implements ProviderWithExtensionVisitor<
Method method,
Object instance,
ImmutableSet<Dependency<?>> dependencies,
List<Provider<?>> parameterProviders,
Class<? extends Annotation> scopeAnnotation,
Annotation annotation) {
super(key,
method,
instance,
dependencies,
parameterProviders,
scopeAnnotation,
annotation);
}
@SuppressWarnings("unchecked")
@Override
Object doProvision(Object[] parameters) throws IllegalAccessException,
T doProvision(Object[] parameters) throws IllegalAccessException,
InvocationTargetException {
return method.invoke(instance, parameters);
return (T) method.invoke(instance, parameters);
}
}
}

View file

@ -1,17 +1,14 @@
package com.google.inject.internal;
import com.google.common.base.Optional;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.inject.Binder;
import com.google.inject.Key;
import com.google.inject.Module;
import com.google.inject.Provider;
import com.google.inject.Provides;
import com.google.inject.TypeLiteral;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.InjectionPoint;
import com.google.inject.spi.Message;
import com.google.inject.spi.ModuleAnnotatedMethodScanner;
@ -21,11 +18,12 @@ import java.lang.annotation.Annotation;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
/**
* Creates bindings to methods annotated with {@literal @}{@link Provides}. Use the scope and
@ -34,23 +32,12 @@ import static com.google.common.base.Preconditions.checkNotNull;
*/
public final class ProviderMethodsModule implements Module {
private static ModuleAnnotatedMethodScanner PROVIDES_BUILDER =
new ModuleAnnotatedMethodScanner() {
@Override
public <T> Key<T> prepareMethod(
Binder binder, Annotation annotation, Key<T> key, InjectionPoint injectionPoint) {
return key;
}
@Override
public Set<? extends Class<? extends Annotation>> annotationClasses() {
return ImmutableSet.of(Provides.class);
}
};
private final Object delegate;
private final TypeLiteral<?> typeLiteral;
private final boolean skipFastClassGeneration;
private final ModuleAnnotatedMethodScanner scanner;
private ProviderMethodsModule(Object delegate, boolean skipFastClassGeneration,
@ -65,7 +52,7 @@ public final class ProviderMethodsModule implements Module {
* Returns a module which creates bindings for provider methods from the given module.
*/
public static Module forModule(Module module) {
return forObject(module, false, PROVIDES_BUILDER);
return forObject(module, false, ProvidesMethodScanner.INSTANCE);
}
/**
@ -83,7 +70,7 @@ public final class ProviderMethodsModule implements Module {
* are only interested in Module metadata.
*/
public static Module forObject(Object object) {
return forObject(object, true, PROVIDES_BUILDER);
return forObject(object, true, ProvidesMethodScanner.INSTANCE);
}
private static Module forObject(Object object, boolean skipFastClassGeneration,
@ -92,143 +79,143 @@ public final class ProviderMethodsModule implements Module {
if (object instanceof ProviderMethodsModule) {
return Modules.EMPTY_MODULE;
}
return new ProviderMethodsModule(object, skipFastClassGeneration, scanner);
}
/**
* Returns true if a overrides b, assumes that the signatures match
*/
private static boolean overrides(Method a, Method b) {
// See JLS section 8.4.8.1
int modifiers = b.getModifiers();
if (Modifier.isPublic(modifiers) || Modifier.isProtected(modifiers)) {
return true;
public Class<?> getDelegateModuleClass() {
return isStaticModule() ? (Class<?>) delegate : delegate.getClass();
}
@Override
public void configure(Binder binder) {
for (ProviderMethod<?> providerMethod : getProviderMethods(binder)) {
providerMethod.configure(binder);
}
if (Modifier.isPrivate(modifiers)) {
return false;
}
// b must be package-private
return a.getDeclaringClass().getPackage().equals(b.getDeclaringClass().getPackage());
}
private boolean isStaticModule() {
return delegate instanceof Class;
}
public Object getDelegateModule() {
return delegate;
}
@Override
public synchronized void configure(Binder binder) {
for (ProviderMethod<?> providerMethod : getProviderMethods(binder)) {
providerMethod.configure(binder);
}
}
public List<ProviderMethod<?>> getProviderMethods(Binder binder) {
List<ProviderMethod<?>> result = Lists.newArrayList();
Multimap<Signature, Method> methodsBySignature = HashMultimap.create();
for (Class<?> c = delegate.getClass(); c != Object.class; c = c.getSuperclass()) {
for (Method method : c.getDeclaredMethods()) {
// private/static methods cannot override or be overridden by other methods, so there is no
// point in indexing them.
// Skip synthetic methods and bridge methods since java will automatically generate
// synthetic overrides in some cases where we don't want to generate an error (e.g.
// increasing visibility of a subclass).
if (((method.getModifiers() & (Modifier.PRIVATE | Modifier.STATIC)) == 0)
&& !method.isBridge() && !method.isSynthetic()) {
methodsBySignature.put(new Signature(method), method);
}
Optional<Annotation> annotation = isProvider(binder, method);
if (annotation.isPresent()) {
result.add(createProviderMethod(binder, method, annotation.get()));
List<ProviderMethod<?>> result = null;
List<MethodAndAnnotation> methodsAndAnnotations = null;
// The highest class in the type hierarchy that contained a provider method definition.
Class<?> superMostClass = getDelegateModuleClass();
for (Class<?> c = superMostClass; c != Object.class && c != null; c = c.getSuperclass()) {
for (Method method : DeclaredMembers.getDeclaredMethods(c)) {
Annotation annotation = getAnnotation(binder, method);
if (annotation != null) {
if (isStaticModule()
&& !Modifier.isStatic(method.getModifiers())
&& !Modifier.isAbstract(method.getModifiers())) {
binder
.skipSources(ProviderMethodsModule.class)
.addError(
"%s is an instance method, but a class literal was passed. Make this method"
+ " static or pass an instance of the module instead.",
method);
continue;
}
if (result == null) {
result = new ArrayList<>();
methodsAndAnnotations = new ArrayList<>();
}
ProviderMethod<Object> providerMethod = createProviderMethod(binder, method, annotation);
if (providerMethod != null) {
result.add(providerMethod);
}
methodsAndAnnotations.add(new MethodAndAnnotation(method, annotation));
superMostClass = c;
}
}
}
// we have found all the providers and now need to identify if any were overridden
// In the worst case this will have O(n^2) in the number of @Provides methods, but that is only
// assuming that every method is an override, in general it should be very quick.
for (ProviderMethod<?> provider : result) {
Method method = provider.getMethod();
for (Method matchingSignature : methodsBySignature.get(new Signature(method))) {
// matching signature is in the same class or a super class, therefore method cannot be
// overridding it.
if (matchingSignature.getDeclaringClass().isAssignableFrom(method.getDeclaringClass())) {
continue;
if (result == null) {
// We didn't find anything
return ImmutableList.of();
}
// We have found some provider methods, now we need to check if any were overridden.
// We do this as a separate pass to avoid calculating all the signatures when there are no
// provides methods, or when all provides methods are defined in a single class.
Multimap<Signature, Method> methodsBySignature = null;
// We can stop scanning when we see superMostClass, since no superclass method can override
// a method in a subclass. Corrollary, if superMostClass == getDelegateModuleClass(), there can
// be no overrides of a provides method.
for (Class<?> c = getDelegateModuleClass(); c != superMostClass; c = c.getSuperclass()) {
for (Method method : c.getDeclaredMethods()) {
if (((method.getModifiers() & (Modifier.PRIVATE | Modifier.STATIC)) == 0)
&& !method.isBridge()
&& !method.isSynthetic()) {
if (methodsBySignature == null) {
methodsBySignature = HashMultimap.create();
}
methodsBySignature.put(new Signature(typeLiteral, method), method);
}
// now we know matching signature is in a subtype of method.getDeclaringClass()
if (overrides(matchingSignature, method)) {
String annotationString = provider.getAnnotation().annotationType() == Provides.class
? "@Provides" : "@" + provider.getAnnotation().annotationType().getCanonicalName();
binder.addError(
"Overriding " + annotationString + " methods is not allowed."
+ "\n\t" + annotationString + " method: %s\n\toverridden by: %s",
method,
matchingSignature);
break;
}
}
if (methodsBySignature != null) {
// we have found all the signatures and now need to identify if any were overridden
// In the worst case this will have O(n^2) in the number of @Provides methods, but that is
// only assuming that every method is an override, in general it should be very quick.
for (MethodAndAnnotation methodAndAnnotation : methodsAndAnnotations) {
Method method = methodAndAnnotation.method;
Annotation annotation = methodAndAnnotation.annotation;
for (Method matchingSignature :
methodsBySignature.get(new Signature(typeLiteral, method))) {
// matching signature is in the same class or a super class, therefore method cannot be
// overridding it.
if (matchingSignature.getDeclaringClass().isAssignableFrom(method.getDeclaringClass())) {
continue;
}
// now we know matching signature is in a subtype of method.getDeclaringClass()
if (overrides(matchingSignature, method)) {
String annotationString =
annotation.annotationType() == Provides.class
? "@Provides"
: "@" + annotation.annotationType().getCanonicalName();
binder.addError(
"Overriding "
+ annotationString
+ " methods is not allowed."
+ "\n\t"
+ annotationString
+ " method: %s\n\toverridden by: %s",
method,
matchingSignature);
break;
}
}
}
}
return result;
}
/**
* Returns true if the method is a provider.
*
* Synthetic bridge methods are excluded. Starting with JDK 8, javac copies annotations onto
* bridge methods (which always have erased signatures).
*/
private Optional<Annotation> isProvider(Binder binder, Method method) {
/** Returns the annotation that is claimed by the scanner, or null if there is none. */
private Annotation getAnnotation(Binder binder, Method method) {
if (method.isBridge() || method.isSynthetic()) {
return Optional.absent();
return null;
}
Annotation annotation = null;
for (Class<? extends Annotation> annotationClass : scanner.annotationClasses()) {
Annotation foundAnnotation = method.getAnnotation(annotationClass);
if (foundAnnotation != null) {
if (annotation != null) {
binder.addError("More than one annotation claimed by %s on method %s."
binder.addError(
"More than one annotation claimed by %s on method %s."
+ " Methods can only have one annotation claimed per scanner.",
scanner, method);
return Optional.absent();
return null;
}
annotation = foundAnnotation;
}
}
return Optional.fromNullable(annotation);
}
private <T> ProviderMethod<T> createProviderMethod(Binder binder, Method method,
Annotation annotation) {
binder = binder.withSource(method);
Errors errors = new Errors(method);
// prepare the parameter providers
InjectionPoint point = InjectionPoint.forMethod(method, typeLiteral);
List<Dependency<?>> dependencies = point.getDependencies();
List<Provider<?>> parameterProviders = Lists.newArrayList();
for (Dependency<?> dependency : point.getDependencies()) {
parameterProviders.add(binder.getProvider(dependency));
}
@SuppressWarnings("unchecked") // Define T as the method's return type.
TypeLiteral<T> returnType = (TypeLiteral<T>) typeLiteral.getReturnType(method);
Key<T> key = getKey(errors, returnType, method, method.getAnnotations());
try {
key = scanner.prepareMethod(binder, annotation, key, point);
} catch (Throwable t) {
binder.addError(t);
}
Class<? extends Annotation> scopeAnnotation
= Annotations.findScopeAnnotation(errors, method.getAnnotations());
for (Message message : errors.getMessages()) {
binder.addError(message);
}
return ProviderMethod.create(key, method, delegate, ImmutableSet.copyOf(dependencies),
parameterProviders, scopeAnnotation, skipFastClassGeneration, annotation);
}
<T> Key<T> getKey(Errors errors, TypeLiteral<T> type, Member member, Annotation[] annotations) {
Annotation bindingAnnotation = Annotations.findBindingAnnotation(errors, member, annotations);
return bindingAnnotation == null ? Key.get(type) : Key.get(type, bindingAnnotation);
return annotation;
}
@Override
@ -243,12 +230,23 @@ public final class ProviderMethodsModule implements Module {
return delegate.hashCode();
}
private final class Signature {
private static class MethodAndAnnotation {
final Method method;
final Annotation annotation;
MethodAndAnnotation(Method method, Annotation annotation) {
this.method = method;
this.annotation = annotation;
}
}
private static class Signature {
final Class<?>[] parameters;
final String name;
final int hashCode;
Signature(Method method) {
Signature(TypeLiteral<?> typeLiteral, Method method) {
this.name = method.getName();
// We need to 'resolve' the parameters against the actual class type in case this method uses
// type parameters. This is so we can detect overrides of generic superclass methods where
@ -278,4 +276,75 @@ public final class ProviderMethodsModule implements Module {
return hashCode;
}
}
/** Returns true if a overrides b, assumes that the signatures match */
private static boolean overrides(Method a, Method b) {
// See JLS section 8.4.8.1
int modifiers = b.getModifiers();
if (Modifier.isPublic(modifiers) || Modifier.isProtected(modifiers)) {
return true;
}
if (Modifier.isPrivate(modifiers)) {
return false;
}
// b must be package-private
return a.getDeclaringClass().getPackage().equals(b.getDeclaringClass().getPackage());
}
private <T> ProviderMethod<T> createProviderMethod(
Binder binder, Method method, Annotation annotation) {
binder = binder.withSource(method);
Errors errors = new Errors(method);
// prepare the parameter providers
InjectionPoint point = InjectionPoint.forMethod(method, typeLiteral);
@SuppressWarnings("unchecked") // Define T as the method's return type.
TypeLiteral<T> returnType = (TypeLiteral<T>) typeLiteral.getReturnType(method);
Key<T> key = getKey(errors, returnType, method, method.getAnnotations());
try {
key = scanner.prepareMethod(binder, annotation, key, point);
} catch (Throwable t) {
binder.addError(t);
}
if (Modifier.isAbstract(method.getModifiers())) {
checkState(
key == null,
"%s returned a non-null key (%s) for %s. prepareMethod() must return null for abstract"
+ " methods",
scanner,
key,
method);
return null;
} else {
checkState(
key != null,
"%s returned a null key for %s. prepareMethod() can only return null for abstract"
+ " methods",
scanner,
method);
}
Class<? extends Annotation> scopeAnnotation =
Annotations.findScopeAnnotation(errors, method.getAnnotations());
for (Message message : errors.getMessages()) {
binder.addError(message);
}
return ProviderMethod.create(
key,
method,
isStaticModule() ? null : delegate,
ImmutableSet.copyOf(point.getDependencies()),
scopeAnnotation,
skipFastClassGeneration,
annotation);
}
<T> Key<T> getKey(Errors errors, TypeLiteral<T> type, Member member, Annotation[] annotations) {
Annotation bindingAnnotation = Annotations.findBindingAnnotation(errors, member, annotations);
return bindingAnnotation == null ? Key.get(type) : Key.get(type, bindingAnnotation);
}
}

View file

@ -1,12 +1,11 @@
package com.google.inject.internal;
import com.google.inject.Provider;
import com.google.inject.ProvisionException;
import com.google.inject.spi.Dependency;
final class ProviderToInternalFactoryAdapter<T> implements Provider<T> {
private final InjectorImpl injector;
private final InternalFactory<? extends T> internalFactory;
public ProviderToInternalFactoryAdapter(InjectorImpl injector,
@ -15,25 +14,23 @@ final class ProviderToInternalFactoryAdapter<T> implements Provider<T> {
this.internalFactory = internalFactory;
}
@Override
public T get() {
final Errors errors = new Errors();
try {
T t = injector.callInContext(new ContextualCallable<T>() {
public T call(InternalContext context) throws ErrorsException {
Dependency dependency = context.getDependency();
// Always pretend that we are a linked binding, to support
// scoping implicit bindings. If we are not actually a linked
// binding, we'll fail properly elsewhere in the chain.
return internalFactory.get(errors, context, dependency, true);
}
});
errors.throwIfNewErrors(0);
return t;
} catch (ErrorsException e) {
throw new ProvisionException(errors.merge(e.getErrors()).getMessages());
try (InternalContext context = injector.enterContext()) {
// Always pretend that we are a linked binding, to support
// scoping implicit bindings. If we are not actually a linked
// binding, we'll fail properly elsewhere in the chain.
return internalFactory.get(context, context.getDependency(), true);
} catch (InternalProvisionException e) {
throw e.toProvisionException();
}
}
/** Exposed for SingletonScope. */
InjectorImpl getInjector() {
return injector;
}
@Override
public String toString() {
return internalFactory.toString();

View file

@ -0,0 +1,159 @@
package com.google.inject.internal;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Binder;
import com.google.inject.Key;
import com.google.inject.Provides;
import com.google.inject.TypeLiteral;
import com.google.inject.multibindings.MapKey;
import com.google.inject.multibindings.ProvidesIntoMap;
import com.google.inject.multibindings.ProvidesIntoOptional;
import com.google.inject.multibindings.ProvidesIntoSet;
import com.google.inject.spi.InjectionPoint;
import com.google.inject.spi.ModuleAnnotatedMethodScanner;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Set;
/**
* A {@link ModuleAnnotatedMethodScanner} that handles the {@link Provides}, {@link ProvidesIntoSet},
* {@link ProvidesIntoMap} and {@link ProvidesIntoOptional} annotations.
*
* <p>This is the default scanner used by ProviderMethodsModule and handles all the built in
* annotations.
*/
final class ProvidesMethodScanner extends ModuleAnnotatedMethodScanner {
static final ProvidesMethodScanner INSTANCE = new ProvidesMethodScanner();
private static final ImmutableSet<Class<? extends Annotation>> ANNOTATIONS =
ImmutableSet.of(Provides.class, ProvidesIntoSet.class, ProvidesIntoMap.class, ProvidesIntoOptional.class);
private ProvidesMethodScanner() {}
@Override
public Set<? extends Class<? extends Annotation>> annotationClasses() {
return ANNOTATIONS;
}
@SuppressWarnings({"unchecked", "rawtypes"}) // mapKey doesn't know its key type
@Override
public <T> Key<T> prepareMethod(
Binder binder, Annotation annotation, Key<T> key, InjectionPoint injectionPoint) {
Method method = (Method) injectionPoint.getMember();
AnnotationOrError mapKey = findMapKeyAnnotation(binder, method);
if (annotation instanceof Provides) {
if (mapKey.annotation != null) {
binder.addError("Found a MapKey annotation on non map binding at %s.", method);
}
// no key rewriting for plain old @Provides
return key;
}
if (annotation instanceof ProvidesIntoSet) {
if (mapKey.annotation != null) {
binder.addError("Found a MapKey annotation on non map binding at %s.", method);
}
return RealMultibinder.newRealSetBinder(binder, key).getKeyForNewItem();
} else if (annotation instanceof ProvidesIntoMap) {
if (mapKey.error) {
// Already failed on the MapKey, don't bother doing more work.
return key;
}
if (mapKey.annotation == null) {
// If no MapKey, make an error and abort.
binder.addError("No MapKey found for map binding at %s.", method);
return key;
}
TypeAndValue typeAndValue = typeAndValueOfMapKey(mapKey.annotation);
return RealMapBinder.newRealMapBinder(binder, typeAndValue.type, key)
.getKeyForNewValue(typeAndValue.value);
} else if (annotation instanceof ProvidesIntoOptional) {
if (mapKey.annotation != null) {
binder.addError("Found a MapKey annotation on non map binding at %s.", method);
}
switch (((ProvidesIntoOptional) annotation).value()) {
case DEFAULT:
return RealOptionalBinder.newRealOptionalBinder(binder, key).getKeyForDefaultBinding();
case ACTUAL:
return RealOptionalBinder.newRealOptionalBinder(binder, key).getKeyForActualBinding();
}
}
throw new IllegalStateException("Invalid annotation: " + annotation);
}
private static class AnnotationOrError {
final Annotation annotation;
final boolean error;
AnnotationOrError(Annotation annotation, boolean error) {
this.annotation = annotation;
this.error = error;
}
static AnnotationOrError forPossiblyNullAnnotation(Annotation annotation) {
return new AnnotationOrError(annotation, false);
}
static AnnotationOrError forError() {
return new AnnotationOrError(null, true);
}
}
private static AnnotationOrError findMapKeyAnnotation(Binder binder, Method method) {
Annotation foundAnnotation = null;
for (Annotation annotation : method.getAnnotations()) {
MapKey mapKey = annotation.annotationType().getAnnotation(MapKey.class);
if (mapKey != null) {
if (foundAnnotation != null) {
binder.addError("Found more than one MapKey annotations on %s.", method);
return AnnotationOrError.forError();
}
if (mapKey.unwrapValue()) {
try {
// validate there's a declared method called "value"
Method valueMethod = annotation.annotationType().getDeclaredMethod("value");
if (valueMethod.getReturnType().isArray()) {
binder.addError(
"Array types are not allowed in a MapKey with unwrapValue=true: %s",
annotation.annotationType());
return AnnotationOrError.forError();
}
} catch (NoSuchMethodException invalid) {
binder.addError(
"No 'value' method in MapKey with unwrapValue=true: %s",
annotation.annotationType());
return AnnotationOrError.forError();
}
}
foundAnnotation = annotation;
}
}
return AnnotationOrError.forPossiblyNullAnnotation(foundAnnotation);
}
@SuppressWarnings({"unchecked", "rawtypes"})
private static TypeAndValue<?> typeAndValueOfMapKey(Annotation mapKeyAnnotation) {
if (!mapKeyAnnotation.annotationType().getAnnotation(MapKey.class).unwrapValue()) {
return new TypeAndValue(TypeLiteral.get(mapKeyAnnotation.annotationType()), mapKeyAnnotation);
} else {
try {
Method valueMethod = mapKeyAnnotation.annotationType().getDeclaredMethod("value");
valueMethod.setAccessible(true);
TypeLiteral<?> returnType =
TypeLiteral.get(mapKeyAnnotation.annotationType()).getReturnType(valueMethod);
return new TypeAndValue(returnType, valueMethod.invoke(mapKeyAnnotation));
} catch (NoSuchMethodException | SecurityException | IllegalAccessException | InvocationTargetException e) {
throw new IllegalStateException(e);
}
}
}
private static class TypeAndValue<T> {
final TypeLiteral<T> type;
final T value;
TypeAndValue(TypeLiteral<T> type, T value) {
this.type = type;
this.value = value;
}
}
}

View file

@ -29,9 +29,9 @@ final class ProvisionListenerCallbackStore {
private final ImmutableList<ProvisionListenerBinding> listenerBindings;
private final LoadingCache<KeyBinding, ProvisionListenerStackCallback<?>> cache
= CacheBuilder.newBuilder().build(
new CacheLoader<KeyBinding, ProvisionListenerStackCallback<?>>() {
private final LoadingCache<KeyBinding, ProvisionListenerStackCallback<?>> cache = CacheBuilder.newBuilder().build(
new CacheLoader<>() {
@Override
public ProvisionListenerStackCallback<?> load(KeyBinding key) {
return create(key.binding);
}

View file

@ -3,8 +3,6 @@ package com.google.inject.internal;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
import com.google.inject.Binding;
import com.google.inject.ProvisionException;
import com.google.inject.spi.DependencyAndSource;
import com.google.inject.spi.ProvisionListener;
import java.util.List;
@ -16,7 +14,8 @@ import java.util.Set;
*/
final class ProvisionListenerStackCallback<T> {
private static final ProvisionListener EMPTY_LISTENER[] = new ProvisionListener[0];
private static final ProvisionListener[] EMPTY_LISTENER = new ProvisionListener[0];
@SuppressWarnings({"rawtypes", "unchecked"})
private static final ProvisionListenerStackCallback<?> EMPTY_CALLBACK =
new ProvisionListenerStackCallback(null /* unused, so ok */, ImmutableList.of());
@ -43,9 +42,9 @@ final class ProvisionListenerStackCallback<T> {
return listeners.length > 0;
}
public T provision(Errors errors, InternalContext context, ProvisionCallback<T> callable)
throws ErrorsException {
Provision provision = new Provision(errors, context, callable);
public T provision(InternalContext context, ProvisionCallback<T> callable)
throws InternalProvisionException {
Provision provision = new Provision(context, callable);
RuntimeException caught = null;
try {
provision.provision();
@ -56,12 +55,14 @@ final class ProvisionListenerStackCallback<T> {
if (provision.exceptionDuringProvision != null) {
throw provision.exceptionDuringProvision;
} else if (caught != null) {
Object listener = provision.erredListener != null ?
provision.erredListener.getClass() : "(unknown)";
throw errors
.errorInUserCode(caught, "Error notifying ProvisionListener %s of %s.%n"
+ " Reason: %s", listener, binding.getKey(), caught)
.toException();
Object listener =
provision.erredListener != null ? provision.erredListener.getClass() : "(unknown)";
throw InternalProvisionException.errorInUserCode(
caught,
"Error notifying ProvisionListener %s of %s.%n Reason: %s",
listener,
binding.getKey(),
caught);
} else {
return provision.result;
}
@ -69,25 +70,21 @@ final class ProvisionListenerStackCallback<T> {
// TODO(sameb): Can this be more InternalFactory-like?
public interface ProvisionCallback<T> {
T call() throws ErrorsException;
T call() throws InternalProvisionException;
}
private class Provision extends ProvisionListener.ProvisionInvocation<T> {
final Errors errors;
final int numErrorsBefore;
final InternalContext context;
final ProvisionCallback<T> callable;
int index = -1;
T result;
ErrorsException exceptionDuringProvision;
InternalProvisionException exceptionDuringProvision;
ProvisionListener erredListener;
public Provision(Errors errors, InternalContext context, ProvisionCallback<T> callable) {
public Provision(InternalContext context, ProvisionCallback<T> callable) {
this.callable = callable;
this.context = context;
this.errors = errors;
this.numErrorsBefore = errors.size();
}
@Override
@ -96,12 +93,9 @@ final class ProvisionListenerStackCallback<T> {
if (index == listeners.length) {
try {
result = callable.call();
// Make sure we don't return the provisioned object if there were any errors
// injecting its field/method dependencies.
errors.throwIfNewErrors(numErrorsBefore);
} catch (ErrorsException ee) {
exceptionDuringProvision = ee;
throw new ProvisionException(errors.merge(ee.getErrors()).getMessages());
} catch (InternalProvisionException ipe) {
exceptionDuringProvision = ipe;
throw ipe.toProvisionException();
}
} else if (index < listeners.length) {
int currentIdx = index;
@ -128,10 +122,5 @@ final class ProvisionListenerStackCallback<T> {
// if someone calls that they'll get strange errors.
return binding;
}
@Override
public List<DependencyAndSource> getDependencyChain() {
return context.getDependencyChain();
}
}
}

View file

@ -1,7 +1,6 @@
package com.google.inject.multibindings;
package com.google.inject.internal;
import com.google.inject.Key;
import com.google.inject.internal.Annotations;
import java.lang.annotation.Annotation;
import java.util.concurrent.atomic.AtomicInteger;

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,611 @@
package com.google.inject.internal;
import static com.google.inject.internal.Element.Type.MULTIBINDER;
import static com.google.inject.internal.Errors.checkConfiguration;
import static com.google.inject.internal.Errors.checkNotNull;
import static com.google.inject.name.Names.named;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.inject.AbstractModule;
import com.google.inject.Binder;
import com.google.inject.Binding;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Module;
import com.google.inject.Provider;
import com.google.inject.TypeLiteral;
import com.google.inject.binder.LinkedBindingBuilder;
import com.google.inject.internal.InternalProviderInstanceBindingImpl.InitializationTiming;
import com.google.inject.multibindings.MultibinderBinding;
import com.google.inject.multibindings.MultibindingsTargetVisitor;
import com.google.inject.spi.BindingTargetVisitor;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.ProviderInstanceBinding;
import com.google.inject.spi.ProviderWithExtensionVisitor;
import com.google.inject.util.Types;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.List;
import java.util.Set;
/**
* The actual multibinder plays several roles:
*
* <p>As a Multibinder, it acts as a factory for LinkedBindingBuilders for each of the set's
* elements. Each binding is given an annotation that identifies it as a part of this set.
*
* <p>As a Module, it installs the binding to the set itself. As a module, this implements equals()
* and hashcode() in order to trick Guice into executing its configure() method only once. That
* makes it so that multiple multibinders can be created for the same target collection, but only
* one is bound. Since the list of bindings is retrieved from the injector itself (and not the
* multibinder), each multibinder has access to all contributions from all multibinders.
*
* <p>As a Provider, this constructs the set instances.
*
* <p>We use a subclass to hide 'implements Module, Provider' from the public API.
*/
public final class RealMultibinder<T> implements Module {
/** Implementation of newSetBinder. */
public static <T> RealMultibinder<T> newRealSetBinder(Binder binder, Key<T> key) {
binder = binder.skipSources(RealMultibinder.class);
RealMultibinder<T> result = new RealMultibinder<>(binder, key);
binder.install(result);
return result;
}
@SuppressWarnings("unchecked") // wrapping a T in a Set safely returns a Set<T>
static <T> TypeLiteral<Set<T>> setOf(TypeLiteral<T> elementType) {
Type type = Types.setOf(elementType.getType());
return (TypeLiteral<Set<T>>) TypeLiteral.get(type);
}
@SuppressWarnings("unchecked")
static <T> TypeLiteral<Collection<Provider<T>>> collectionOfProvidersOf(
TypeLiteral<T> elementType) {
Type providerType = Types.providerOf(elementType.getType());
Type type = Types.collectionOf(providerType);
return (TypeLiteral<Collection<Provider<T>>>) TypeLiteral.get(type);
}
@SuppressWarnings("unchecked")
static <T> TypeLiteral<Collection<javax.inject.Provider<T>>> collectionOfJavaxProvidersOf(
TypeLiteral<T> elementType) {
Type providerType =
Types.newParameterizedType(javax.inject.Provider.class, elementType.getType());
Type type = Types.collectionOf(providerType);
return (TypeLiteral<Collection<javax.inject.Provider<T>>>) TypeLiteral.get(type);
}
private final BindingSelection<T> bindingSelection;
private final Binder binder;
RealMultibinder(Binder binder, Key<T> key) {
this.binder = checkNotNull(binder, "binder");
this.bindingSelection = new BindingSelection<>(key);
}
@Override
public void configure(Binder binder) {
checkConfiguration(!bindingSelection.isInitialized(), "Multibinder was already initialized");
binder
.bind(bindingSelection.getSetKey())
.toProvider(new RealMultibinderProvider<T>(bindingSelection));
Provider<Collection<Provider<T>>> collectionOfProvidersProvider =
new RealMultibinderCollectionOfProvidersProvider<T>(bindingSelection);
binder
.bind(bindingSelection.getCollectionOfProvidersKey())
.toProvider(collectionOfProvidersProvider);
// The collection this exposes is internally an ImmutableList, so it's OK to massage
// the guice Provider to javax Provider in the value (since the guice Provider implements
// javax Provider).
@SuppressWarnings("unchecked")
Provider<Collection<javax.inject.Provider<T>>> javaxProvider =
(Provider) collectionOfProvidersProvider;
binder.bind(bindingSelection.getCollectionOfJavaxProvidersKey()).toProvider(javaxProvider);
}
public void permitDuplicates() {
binder.install(new PermitDuplicatesModule(bindingSelection.getPermitDuplicatesKey()));
}
/** Adds a new entry to the set and returns the key for it. */
Key<T> getKeyForNewItem() {
checkConfiguration(!bindingSelection.isInitialized(), "Multibinder was already initialized");
return Key.get(
bindingSelection.getElementTypeLiteral(),
new RealElement(bindingSelection.getSetName(), MULTIBINDER, ""));
}
public LinkedBindingBuilder<T> addBinding() {
return binder.bind(getKeyForNewItem());
}
// These methods are used by RealMapBinder
Key<Set<T>> getSetKey() {
return bindingSelection.getSetKey();
}
TypeLiteral<T> getElementTypeLiteral() {
return bindingSelection.getElementTypeLiteral();
}
String getSetName() {
return bindingSelection.getSetName();
}
boolean permitsDuplicates(Injector injector) {
return bindingSelection.permitsDuplicates(injector);
}
boolean containsElement(com.google.inject.spi.Element element) {
return bindingSelection.containsElement(element);
}
private static final class RealMultibinderProvider<T>
extends InternalProviderInstanceBindingImpl.Factory<Set<T>>
implements ProviderWithExtensionVisitor<Set<T>>, MultibinderBinding<Set<T>> {
private final BindingSelection<T> bindingSelection;
private List<Binding<T>> bindings;
private SingleParameterInjector<T>[] injectors;
private boolean permitDuplicates;
RealMultibinderProvider(BindingSelection<T> bindingSelection) {
// While Multibinders only depend on bindings created in modules so we could theoretically
// initialize eagerly, they also depend on
// 1. findBindingsByType returning results
// 2. being able to call BindingImpl.acceptTargetVisitor
// neither of those is available during eager initialization, so we use DELAYED
super(InitializationTiming.DELAYED);
this.bindingSelection = bindingSelection;
}
@Override
public Set<Dependency<?>> getDependencies() {
return bindingSelection.getDependencies();
}
@Override
void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
bindingSelection.initialize(injector, errors);
this.bindings = bindingSelection.getBindings();
this.injectors = bindingSelection.getParameterInjectors();
this.permitDuplicates = bindingSelection.permitsDuplicates();
}
@Override
protected Set<T> doProvision(InternalContext context, Dependency<?> dependency)
throws InternalProvisionException {
SingleParameterInjector<T>[] localInjectors = injectors;
if (localInjectors == null) {
// if localInjectors == null, then we have no bindings so return the empty set.
return ImmutableSet.of();
}
// Ideally we would just add to an ImmutableSet.Builder, but if we did that and there were
// duplicates we wouldn't be able to tell which one was the duplicate. So to manage this we
// first put everything into an array and then construct the set. This way if something gets
// dropped we can figure out what it is.
@SuppressWarnings("unchecked")
T[] values = (T[]) new Object[localInjectors.length];
for (int i = 0; i < localInjectors.length; i++) {
SingleParameterInjector<T> parameterInjector = localInjectors[i];
T newValue = parameterInjector.inject(context);
if (newValue == null) {
throw newNullEntryException(i);
}
values[i] = newValue;
}
ImmutableSet<T> set = ImmutableSet.copyOf(values);
// There are fewer items in the set than the array. Figure out which one got dropped.
if (!permitDuplicates && set.size() < values.length) {
throw newDuplicateValuesException(set, values);
}
return set;
}
private InternalProvisionException newNullEntryException(int i) {
return InternalProvisionException.create(
"Set injection failed due to null element bound at: %s", bindings.get(i).getSource());
}
@SuppressWarnings("unchecked")
@Override
public <B, V> V acceptExtensionVisitor(
BindingTargetVisitor<B, V> visitor, ProviderInstanceBinding<? extends B> binding) {
if (visitor instanceof MultibindingsTargetVisitor) {
return ((MultibindingsTargetVisitor<Set<T>, V>) visitor).visit(this);
} else {
return visitor.visit(binding);
}
}
private InternalProvisionException newDuplicateValuesException(
ImmutableSet<T> set, T[] values) {
// TODO(lukes): consider reporting all duplicate values, the easiest way would be to rebuild
// a new set and detect dupes as we go
// Find the duplicate binding
// To do this we take advantage of the fact that set, values and bindings all have the same
// ordering for a non-empty prefix of the set.
// First we scan for the first item dropped from the set.
int newBindingIndex = 0;
for (T item : set) {
if (item != values[newBindingIndex]) {
break;
}
newBindingIndex++;
}
// once we exit the loop newBindingIndex will point at the first item in values that was
// dropped.
Binding<T> newBinding = bindings.get(newBindingIndex);
T newValue = values[newBindingIndex];
// Now we scan again to find the index of the value, we are guaranteed to find it.
int oldBindingIndex = set.asList().indexOf(newValue);
T oldValue = values[oldBindingIndex];
Binding<T> duplicateBinding = bindings.get(oldBindingIndex);
String oldString = oldValue.toString();
String newString = newValue.toString();
if (Objects.equal(oldString, newString)) {
// When the value strings match, just show the source of the bindings
return InternalProvisionException.create(
"Set injection failed due to duplicated element \"%s\""
+ "\n Bound at %s\n Bound at %s",
newValue, duplicateBinding.getSource(), newBinding.getSource());
} else {
// When the value strings don't match, include them both as they may be useful for debugging
return InternalProvisionException.create(
"Set injection failed due to multiple elements comparing equal:"
+ "\n \"%s\"\n bound at %s"
+ "\n \"%s\"\n bound at %s",
oldValue, duplicateBinding.getSource(), newValue, newBinding.getSource());
}
}
@Override
public boolean equals(Object obj) {
return obj instanceof RealMultibinderProvider
&& bindingSelection.equals(((RealMultibinderProvider<?>) obj).bindingSelection);
}
@Override
public int hashCode() {
return bindingSelection.hashCode();
}
@Override
public Key<Set<T>> getSetKey() {
return bindingSelection.getSetKey();
}
@Override
public Set<Key<?>> getAlternateSetKeys() {
return ImmutableSet.of(
(Key<?>) bindingSelection.getCollectionOfProvidersKey(),
(Key<?>) bindingSelection.getCollectionOfJavaxProvidersKey());
}
@Override
public TypeLiteral<?> getElementTypeLiteral() {
return bindingSelection.getElementTypeLiteral();
}
@Override
public List<Binding<?>> getElements() {
return bindingSelection.getElements();
}
@Override
public boolean permitsDuplicates() {
return bindingSelection.permitsDuplicates();
}
@Override
public boolean containsElement(com.google.inject.spi.Element element) {
return bindingSelection.containsElement(element);
}
}
private static final class BindingSelection<T> {
// prior to initialization we declare just a dependency on the injector, but as soon as we are
// initialized we swap to dependencies on the elements.
private static final ImmutableSet<Dependency<?>> MODULE_DEPENDENCIES =
ImmutableSet.<Dependency<?>>of(Dependency.get(Key.get(Injector.class)));
private final TypeLiteral<T> elementType;
private final Key<Set<T>> setKey;
// these are all lazily allocated
private String setName;
private Key<Collection<Provider<T>>> collectionOfProvidersKey;
private Key<Collection<javax.inject.Provider<T>>> collectionOfJavaxProvidersKey;
private Key<Boolean> permitDuplicatesKey;
private boolean isInitialized;
/* a binding for each element in the set. null until initialization, non-null afterwards */
private ImmutableList<Binding<T>> bindings;
// Starts out as Injector and gets set up properly after initialization
private ImmutableSet<Dependency<?>> dependencies = MODULE_DEPENDENCIES;
private ImmutableSet<Dependency<?>> providerDependencies = MODULE_DEPENDENCIES;
/** whether duplicates are allowed. Possibly configured by a different instance */
private boolean permitDuplicates;
private SingleParameterInjector<T>[] parameterinjectors;
BindingSelection(Key<T> key) {
this.setKey = key.ofType(setOf(key.getTypeLiteral()));
this.elementType = key.getTypeLiteral();
}
void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
// This will be called multiple times, once by each Factory. We only want
// to do the work to initialize everything once, so guard this code with
// isInitialized.
if (isInitialized) {
return;
}
List<Binding<T>> bindings = Lists.newArrayList();
Set<Indexer.IndexedBinding> index = Sets.newHashSet();
Indexer indexer = new Indexer(injector);
List<Dependency<?>> dependencies = Lists.newArrayList();
List<Dependency<?>> providerDependencies = Lists.newArrayList();
for (Binding<?> entry : injector.findBindingsByType(elementType)) {
if (keyMatches(entry.getKey())) {
@SuppressWarnings("unchecked") // protected by findBindingsByType()
Binding<T> binding = (Binding<T>) entry;
if (index.add(binding.acceptTargetVisitor(indexer))) {
// TODO(lukes): most of these are linked bindings since user bindings are linked to
// a user binding through the @Element annotation. Since this is an implementation
// detail we could 'dereference' the @Element if it is a LinkedBinding and avoid
// provisioning through the FactoryProxy at runtime.
// Ditto for OptionalBinder/MapBinder
bindings.add(binding);
Key<T> key = binding.getKey();
// TODO(lukes): we should mark this as a non-nullable dependency since we don't accept
// null.
// Add a dependency on Key<T>
dependencies.add(Dependency.get(key));
// and add a dependency on Key<Provider<T>>
providerDependencies.add(
Dependency.get(key.ofType(Types.providerOf(key.getTypeLiteral().getType()))));
}
}
}
this.bindings = ImmutableList.copyOf(bindings);
this.dependencies = ImmutableSet.copyOf(dependencies);
this.providerDependencies = ImmutableSet.copyOf(providerDependencies);
this.permitDuplicates = permitsDuplicates(injector);
// This is safe because all our dependencies are assignable to T and we never assign to
// elements of this array.
@SuppressWarnings("unchecked")
SingleParameterInjector<T>[] typed =
(SingleParameterInjector<T>[]) injector.getParametersInjectors(dependencies, errors);
this.parameterinjectors = typed;
isInitialized = true;
}
boolean permitsDuplicates(Injector injector) {
return injector.getBindings().containsKey(getPermitDuplicatesKey());
}
ImmutableList<Binding<T>> getBindings() {
checkConfiguration(isInitialized, "not initialized");
return bindings;
}
SingleParameterInjector<T>[] getParameterInjectors() {
checkConfiguration(isInitialized, "not initialized");
return parameterinjectors;
}
ImmutableSet<Dependency<?>> getDependencies() {
return dependencies;
}
ImmutableSet<Dependency<?>> getProviderDependencies() {
return providerDependencies;
}
String getSetName() {
// lazily initialized since most selectors don't survive module installation.
if (setName == null) {
setName = Annotations.nameOf(setKey);
}
return setName;
}
Key<Boolean> getPermitDuplicatesKey() {
Key<Boolean> local = permitDuplicatesKey;
if (local == null) {
local =
permitDuplicatesKey = Key.get(Boolean.class, named(toString() + " permits duplicates"));
}
return local;
}
Key<Collection<Provider<T>>> getCollectionOfProvidersKey() {
Key<Collection<Provider<T>>> local = collectionOfProvidersKey;
if (local == null) {
local = collectionOfProvidersKey = setKey.ofType(collectionOfProvidersOf(elementType));
}
return local;
}
Key<Collection<javax.inject.Provider<T>>> getCollectionOfJavaxProvidersKey() {
Key<Collection<javax.inject.Provider<T>>> local = collectionOfJavaxProvidersKey;
if (local == null) {
local =
collectionOfJavaxProvidersKey =
setKey.ofType(collectionOfJavaxProvidersOf(elementType));
}
return local;
}
boolean isInitialized() {
return isInitialized;
}
// MultibinderBinding API methods
TypeLiteral<T> getElementTypeLiteral() {
return elementType;
}
Key<Set<T>> getSetKey() {
return setKey;
}
@SuppressWarnings("unchecked")
List<Binding<?>> getElements() {
if (isInitialized()) {
return (List<Binding<?>>) (List<?>) bindings; // safe because bindings is immutable.
} else {
throw new UnsupportedOperationException("getElements() not supported for module bindings");
}
}
boolean permitsDuplicates() {
if (isInitialized()) {
return permitDuplicates;
} else {
throw new UnsupportedOperationException(
"permitsDuplicates() not supported for module bindings");
}
}
boolean containsElement(com.google.inject.spi.Element element) {
if (element instanceof Binding) {
Binding<?> binding = (Binding<?>) element;
return keyMatches(binding.getKey())
|| binding.getKey().equals(getPermitDuplicatesKey())
|| binding.getKey().equals(setKey)
|| binding.getKey().equals(collectionOfProvidersKey)
|| binding.getKey().equals(collectionOfJavaxProvidersKey);
} else {
return false;
}
}
private boolean keyMatches(Key<?> key) {
return key.getTypeLiteral().equals(elementType)
&& key.getAnnotation() instanceof Element
&& ((Element) key.getAnnotation()).setName().equals(getSetName())
&& ((Element) key.getAnnotation()).type() == MULTIBINDER;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof BindingSelection) {
return setKey.equals(((BindingSelection<?>) obj).setKey);
}
return false;
}
@Override
public int hashCode() {
return setKey.hashCode();
}
@Override
public String toString() {
return (getSetName().isEmpty() ? "" : getSetName() + " ")
+ "Multibinder<"
+ elementType
+ ">";
}
}
@Override
public boolean equals(Object o) {
return o instanceof RealMultibinder
&& ((RealMultibinder<?>) o).bindingSelection.equals(bindingSelection);
}
@Override
public int hashCode() {
return bindingSelection.hashCode();
}
private static final class RealMultibinderCollectionOfProvidersProvider<T>
extends InternalProviderInstanceBindingImpl.Factory<Collection<Provider<T>>> {
private final BindingSelection<T> bindingSelection;
private ImmutableList<Provider<T>> collectionOfProviders;
RealMultibinderCollectionOfProvidersProvider(BindingSelection<T> bindingSelection) {
super(InitializationTiming.DELAYED); // See comment in RealMultibinderProvider
this.bindingSelection = bindingSelection;
}
@Override
void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
bindingSelection.initialize(injector, errors);
ImmutableList.Builder<Provider<T>> providers = ImmutableList.builder();
for (Binding<T> binding : bindingSelection.getBindings()) {
providers.add(binding.getProvider());
}
this.collectionOfProviders = providers.build();
}
@Override
protected Collection<Provider<T>> doProvision(
InternalContext context, Dependency<?> dependency) {
return collectionOfProviders;
}
@Override
public Set<Dependency<?>> getDependencies() {
return bindingSelection.getProviderDependencies();
}
@Override
public boolean equals(Object obj) {
return obj instanceof RealMultibinderCollectionOfProvidersProvider
&& bindingSelection.equals(
((RealMultibinderCollectionOfProvidersProvider<?>) obj).bindingSelection);
}
@Override
public int hashCode() {
return bindingSelection.hashCode();
}
}
/**
* We install the permit duplicates configuration as its own binding, all by itself. This way, if
* only one of a multibinder's users remember to call permitDuplicates(), they're still permitted.
*
* <p>This is like setting a global variable in the injector so that each instance of the
* multibinder will have the same value for permitDuplicates, even if it is only set on one of
* them.
*/
private static class PermitDuplicatesModule extends AbstractModule {
private final Key<Boolean> key;
PermitDuplicatesModule(Key<Boolean> key) {
this.key = key;
}
@Override
protected void configure() {
bind(key).toInstance(true);
}
@Override
public boolean equals(Object o) {
return o instanceof PermitDuplicatesModule && ((PermitDuplicatesModule) o).key.equals(key);
}
@Override
public int hashCode() {
return getClass().hashCode() ^ key.hashCode();
}
}
}

View file

@ -0,0 +1,793 @@
package com.google.inject.internal;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.inject.internal.Errors.checkConfiguration;
import static com.google.inject.util.Types.newParameterizedType;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.inject.Binder;
import com.google.inject.Binding;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Module;
import com.google.inject.Provider;
import com.google.inject.TypeLiteral;
import com.google.inject.binder.LinkedBindingBuilder;
import com.google.inject.internal.InternalProviderInstanceBindingImpl.InitializationTiming;
import com.google.inject.multibindings.MultibindingsTargetVisitor;
import com.google.inject.multibindings.OptionalBinderBinding;
import com.google.inject.spi.BindingTargetVisitor;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.Element;
import com.google.inject.spi.ProviderInstanceBinding;
import com.google.inject.spi.ProviderWithExtensionVisitor;
import com.google.inject.util.Types;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.reflect.Type;
import java.util.Set;
import javax.inject.Qualifier;
/**
* The actual OptionalBinder plays several roles. It implements Module to hide that fact from the
* public API, and installs the various bindings that are exposed to the user.
*/
public final class RealOptionalBinder<T> implements Module {
public static <T> RealOptionalBinder<T> newRealOptionalBinder(Binder binder, Key<T> type) {
binder = binder.skipSources(RealOptionalBinder.class);
RealOptionalBinder<T> optionalBinder = new RealOptionalBinder<>(binder, type);
binder.install(optionalBinder);
return optionalBinder;
}
@SuppressWarnings("unchecked")
static <T> TypeLiteral<Optional<T>> optionalOf(TypeLiteral<T> type) {
return (TypeLiteral<Optional<T>>)
TypeLiteral.get(Types.newParameterizedType(Optional.class, type.getType()));
}
@SuppressWarnings("unchecked")
static <T> TypeLiteral<java.util.Optional<T>> javaOptionalOf(TypeLiteral<T> type) {
return (TypeLiteral<java.util.Optional<T>>)
TypeLiteral.get(Types.newParameterizedType(java.util.Optional.class, type.getType()));
}
@SuppressWarnings("unchecked")
static <T> TypeLiteral<Optional<javax.inject.Provider<T>>> optionalOfJavaxProvider(
TypeLiteral<T> type) {
return (TypeLiteral<Optional<javax.inject.Provider<T>>>)
TypeLiteral.get(
Types.newParameterizedType(
Optional.class, newParameterizedType(javax.inject.Provider.class, type.getType())));
}
@SuppressWarnings("unchecked")
static <T> TypeLiteral<java.util.Optional<javax.inject.Provider<T>>> javaOptionalOfJavaxProvider(
TypeLiteral<T> type) {
return (TypeLiteral<java.util.Optional<javax.inject.Provider<T>>>)
TypeLiteral.get(
Types.newParameterizedType(
java.util.Optional.class,
newParameterizedType(javax.inject.Provider.class, type.getType())));
}
@SuppressWarnings("unchecked")
static <T> TypeLiteral<Optional<Provider<T>>> optionalOfProvider(TypeLiteral<T> type) {
return (TypeLiteral<Optional<Provider<T>>>)
TypeLiteral.get(
Types.newParameterizedType(
Optional.class, newParameterizedType(Provider.class, type.getType())));
}
@SuppressWarnings("unchecked")
static <T> TypeLiteral<java.util.Optional<Provider<T>>> javaOptionalOfProvider(
TypeLiteral<T> type) {
return (TypeLiteral<java.util.Optional<Provider<T>>>)
TypeLiteral.get(
Types.newParameterizedType(
java.util.Optional.class, newParameterizedType(Provider.class, type.getType())));
}
@SuppressWarnings("unchecked")
static <T> Key<Provider<T>> providerOf(Key<T> key) {
Type providerT = Types.providerOf(key.getTypeLiteral().getType());
return (Key<Provider<T>>) key.ofType(providerT);
}
enum Source {
DEFAULT,
ACTUAL
}
@Retention(RUNTIME)
@Qualifier
@interface Default {
String value();
}
@Retention(RUNTIME)
@Qualifier
@interface Actual {
String value();
}
private final BindingSelection<T> bindingSelection;
private final Binder binder;
private RealOptionalBinder(Binder binder, Key<T> typeKey) {
this.bindingSelection = new BindingSelection<>(typeKey);
this.binder = binder;
}
/**
* Adds a binding for T. Multiple calls to this are safe, and will be collapsed as duplicate
* bindings.
*/
private void addDirectTypeBinding(Binder binder) {
binder
.bind(bindingSelection.getDirectKey())
.toProvider(new RealDirectTypeProvider<T>(bindingSelection));
}
/**
* Returns the key to use for the default binding.
*
* <p>As a side effect this installs support for the 'direct type', so a binding for {@code T}
* will be made available.
*/
Key<T> getKeyForDefaultBinding() {
bindingSelection.checkNotInitialized();
addDirectTypeBinding(binder);
return bindingSelection.getKeyForDefaultBinding();
}
public LinkedBindingBuilder<T> setDefault() {
return binder.bind(getKeyForDefaultBinding());
}
/**
* Returns the key to use for the actual binding, overrides the default if set.
*
* <p>As a side effect this installs support for the 'direct type', so a binding for {@code T}
* will be made available.
*/
Key<T> getKeyForActualBinding() {
bindingSelection.checkNotInitialized();
addDirectTypeBinding(binder);
return bindingSelection.getKeyForActualBinding();
}
public LinkedBindingBuilder<T> setBinding() {
return binder.bind(getKeyForActualBinding());
}
@Override
public void configure(Binder binder) {
bindingSelection.checkNotInitialized();
Key<T> key = bindingSelection.getDirectKey();
TypeLiteral<T> typeLiteral = key.getTypeLiteral();
// Every OptionalBinder gets the following types bound
// * {cgcb,ju}.Optional<Provider<T>>
// * {cgcb,ju}.Optional<javax.inject.Provider<T>>
// * {cgcb,ju}.Optional<T>
// If setDefault() or setBinding() is called then also
// * T is bound
// cgcb.Optional<Provider<T>>
InternalProviderInstanceBindingImpl.Factory<Optional<Provider<T>>> optionalProviderFactory =
new RealOptionalProviderProvider<>(bindingSelection);
binder.bind(key.ofType(optionalOfProvider(typeLiteral))).toProvider(optionalProviderFactory);
// ju.Optional<Provider<T>>
InternalProviderInstanceBindingImpl.Factory<java.util.Optional<Provider<T>>>
javaOptionalProviderFactory = new JavaOptionalProviderProvider<>(bindingSelection);
binder
.bind(key.ofType(javaOptionalOfProvider(typeLiteral)))
.toProvider(javaOptionalProviderFactory);
// Provider is assignable to javax.inject.Provider and the provider that the factory contains
// cannot be modified so we can use some rawtypes hackery to share the same implementation.
// cgcb.Optional<ji.Provider<T>>
@SuppressWarnings("unchecked")
InternalProviderInstanceBindingImpl.Factory<Optional<javax.inject.Provider<T>>>
optionalJavaxProviderFactory =
(InternalProviderInstanceBindingImpl.Factory) optionalProviderFactory;
binder
.bind(key.ofType(optionalOfJavaxProvider(typeLiteral)))
.toProvider(optionalJavaxProviderFactory);
// ju.Optional<ji.Provider<T>>
@SuppressWarnings("unchecked")
InternalProviderInstanceBindingImpl.Factory<java.util.Optional<javax.inject.Provider<T>>>
javaOptionalJavaxProviderFactory =
(InternalProviderInstanceBindingImpl.Factory) javaOptionalProviderFactory;
binder
.bind(key.ofType(javaOptionalOfJavaxProvider(typeLiteral)))
.toProvider(javaOptionalJavaxProviderFactory);
// cgcb.Optional<T>
Key<Optional<T>> optionalKey = key.ofType(optionalOf(typeLiteral));
binder
.bind(optionalKey)
.toProvider(new RealOptionalKeyProvider<>(bindingSelection, optionalKey));
// ju.Optional<T>
Key<java.util.Optional<T>> javaOptionalKey = key.ofType(javaOptionalOf(typeLiteral));
binder
.bind(javaOptionalKey)
.toProvider(new JavaOptionalProvider<>(bindingSelection, javaOptionalKey));
}
/** Provides the binding for java.util.Optional<T>. */
private static final class JavaOptionalProvider<T>
extends RealOptionalBinderProviderWithDependencies<T, java.util.Optional<T>>
implements ProviderWithExtensionVisitor<java.util.Optional<T>>,
OptionalBinderBinding<java.util.Optional<T>> {
private final Key<java.util.Optional<T>> optionalKey;
private Dependency<?> targetDependency;
private InternalFactory<? extends T> target;
JavaOptionalProvider(
BindingSelection<T> bindingSelection, Key<java.util.Optional<T>> optionalKey) {
super(bindingSelection);
this.optionalKey = optionalKey;
}
@Override
void doInitialize() {
if (bindingSelection.getBinding() != null) {
target = bindingSelection.getBinding().getInternalFactory();
targetDependency = bindingSelection.getDependency();
}
}
@Override
protected java.util.Optional<T> doProvision(
InternalContext context, Dependency<?> currentDependency)
throws InternalProvisionException {
InternalFactory<? extends T> local = target;
if (local == null) {
return java.util.Optional.empty();
}
Dependency<?> localDependency = targetDependency;
T result;
Dependency<?> previous = context.pushDependency(localDependency, getSource());
try {
// See comments in RealOptionalKeyProvider, about how localDependency may be more specific
// than what we actually need.
result = local.get(context, localDependency, false);
} catch (InternalProvisionException ipe) {
throw ipe.addSource(localDependency);
} finally {
context.popStateAndSetDependency(previous);
}
return java.util.Optional.ofNullable(result);
}
@Override
public Set<Dependency<?>> getDependencies() {
return bindingSelection.dependencies;
}
@SuppressWarnings("unchecked")
@Override
public <B, R> R acceptExtensionVisitor(
BindingTargetVisitor<B, R> visitor, ProviderInstanceBinding<? extends B> binding) {
if (visitor instanceof MultibindingsTargetVisitor) {
return ((MultibindingsTargetVisitor<java.util.Optional<T>, R>) visitor).visit(this);
} else {
return visitor.visit(binding);
}
}
@Override
public boolean containsElement(Element element) {
return bindingSelection.containsElement(element);
}
@Override
public Binding<?> getActualBinding() {
return bindingSelection.getActualBinding();
}
@Override
public Binding<?> getDefaultBinding() {
return bindingSelection.getDefaultBinding();
}
@Override
public Key<java.util.Optional<T>> getKey() {
return optionalKey;
}
@Override
public Set<Key<?>> getAlternateKeys() {
Key<?> key = bindingSelection.getDirectKey();
TypeLiteral<?> typeLiteral = key.getTypeLiteral();
return ImmutableSet.of(
(Key<?>) key.ofType(javaOptionalOfProvider(typeLiteral)),
(Key<?>) key.ofType(javaOptionalOfJavaxProvider(typeLiteral)));
}
}
/** Provides the binding for java.util.Optional<Provider<T>>. */
private static final class JavaOptionalProviderProvider<T>
extends RealOptionalBinderProviderWithDependencies<T, java.util.Optional<Provider<T>>> {
private java.util.Optional<Provider<T>> value;
JavaOptionalProviderProvider(BindingSelection<T> bindingSelection) {
super(bindingSelection);
}
@Override
void doInitialize() {
if (bindingSelection.getBinding() == null) {
value = java.util.Optional.empty();
} else {
value = java.util.Optional.of(bindingSelection.getBinding().getProvider());
}
}
@Override
protected java.util.Optional<Provider<T>> doProvision(
InternalContext context, Dependency<?> dependency) {
return value;
}
@Override
public Set<Dependency<?>> getDependencies() {
return bindingSelection.providerDependencies();
}
}
/** Provides the binding for T, conditionally installed by calling setBinding/setDefault. */
private static final class RealDirectTypeProvider<T>
extends RealOptionalBinderProviderWithDependencies<T, T> {
private Key<? extends T> targetKey;
private Object targetSource;
private InternalFactory<? extends T> targetFactory;
RealDirectTypeProvider(BindingSelection<T> bindingSelection) {
super(bindingSelection);
}
@Override
void doInitialize() {
BindingImpl<T> targetBinding = bindingSelection.getBinding();
// we only install this factory if they call setBinding()/setDefault() so we know that
// targetBinding will be non-null.
this.targetKey = targetBinding.getKey();
this.targetSource = targetBinding.getSource();
this.targetFactory = targetBinding.getInternalFactory();
}
@Override
protected T doProvision(InternalContext context, Dependency<?> dependency)
throws InternalProvisionException {
// This is what linked bindings do (see FactoryProxy), and we are pretty similar.
context.pushState(targetKey, targetSource);
try {
return targetFactory.get(context, dependency, true);
} catch (InternalProvisionException ipe) {
throw ipe.addSource(targetKey);
} finally {
context.popState();
}
}
@Override
public Set<Dependency<?>> getDependencies() {
return bindingSelection.dependencies;
}
}
/** Provides the binding for Optional<Provider<T>>. */
private static final class RealOptionalProviderProvider<T>
extends RealOptionalBinderProviderWithDependencies<T, Optional<Provider<T>>> {
private Optional<Provider<T>> value;
RealOptionalProviderProvider(BindingSelection<T> bindingSelection) {
super(bindingSelection);
}
@Override
void doInitialize() {
if (bindingSelection.getBinding() == null) {
value = Optional.absent();
} else {
value = Optional.of(bindingSelection.getBinding().getProvider());
}
}
@Override
protected Optional<Provider<T>> doProvision(InternalContext context, Dependency<?> dependency) {
return value;
}
@Override
public Set<Dependency<?>> getDependencies() {
return bindingSelection.providerDependencies();
}
}
/** Provides the binding for Optional<T>. */
private static final class RealOptionalKeyProvider<T>
extends RealOptionalBinderProviderWithDependencies<T, Optional<T>>
implements ProviderWithExtensionVisitor<Optional<T>>, OptionalBinderBinding<Optional<T>> {
private final Key<Optional<T>> optionalKey;
// These are assigned to non-null values during initialization if and only if we have a binding
// to delegate to.
private Dependency<?> targetDependency;
private InternalFactory<? extends T> delegate;
RealOptionalKeyProvider(BindingSelection<T> bindingSelection, Key<Optional<T>> optionalKey) {
super(bindingSelection);
this.optionalKey = optionalKey;
}
@Override
void doInitialize() {
if (bindingSelection.getBinding() != null) {
delegate = bindingSelection.getBinding().getInternalFactory();
targetDependency = bindingSelection.getDependency();
}
}
@Override
protected Optional<T> doProvision(InternalContext context, Dependency<?> currentDependency)
throws InternalProvisionException {
InternalFactory<? extends T> local = delegate;
if (local == null) {
return Optional.absent();
}
Dependency<?> localDependency = targetDependency;
T result;
Dependency<?> previous = context.pushDependency(localDependency, getSource());
try {
// currentDependency is Optional<? super T>, so we really just need to set the target
// dependency to ? super T, but we are currently setting it to T. We could hypothetically
// make it easier for our delegate to generate proxies by modifying the dependency, but that
// would also require us to rewrite the key on each call. So for now we don't do it.
result = local.get(context, localDependency, false);
} catch (InternalProvisionException ipe) {
throw ipe.addSource(localDependency);
} finally {
context.popStateAndSetDependency(previous);
}
return Optional.fromNullable(result);
}
@Override
public Set<Dependency<?>> getDependencies() {
return bindingSelection.dependencies();
}
@SuppressWarnings("unchecked")
@Override
public <B, R> R acceptExtensionVisitor(
BindingTargetVisitor<B, R> visitor, ProviderInstanceBinding<? extends B> binding) {
if (visitor instanceof MultibindingsTargetVisitor) {
return ((MultibindingsTargetVisitor<Optional<T>, R>) visitor).visit(this);
} else {
return visitor.visit(binding);
}
}
@Override
public Key<Optional<T>> getKey() {
return optionalKey;
}
@Override
public Set<Key<?>> getAlternateKeys() {
Key<?> key = bindingSelection.getDirectKey();
TypeLiteral<?> typeLiteral = key.getTypeLiteral();
return ImmutableSet.of(
(Key<?>) key.ofType(optionalOfProvider(typeLiteral)),
(Key<?>) key.ofType(optionalOfJavaxProvider(typeLiteral)));
}
@Override
public Binding<?> getActualBinding() {
return bindingSelection.getActualBinding();
}
@Override
public Binding<?> getDefaultBinding() {
return bindingSelection.getDefaultBinding();
}
@Override
public boolean containsElement(Element element) {
return bindingSelection.containsElement(element);
}
}
/**
* A helper object that implements the core logic for deciding what the implementation of the
* binding will be.
*
* <p>This also implements the main OptionalBinderBinding logic.
*/
private static final class BindingSelection<T> {
private static final ImmutableSet<Dependency<?>> MODULE_DEPENDENCIES =
ImmutableSet.<Dependency<?>>of(Dependency.get(Key.get(Injector.class)));
/*@Nullable */ BindingImpl<T> actualBinding;
/*@Nullable */ BindingImpl<T> defaultBinding;
/*@Nullable */ BindingImpl<T> binding;
private boolean initialized;
private final Key<T> key;
// Until the injector initializes us, we don't know what our dependencies are,
// so initialize to the whole Injector (like Multibinder, and MapBinder indirectly).
private ImmutableSet<Dependency<?>> dependencies = MODULE_DEPENDENCIES;
private ImmutableSet<Dependency<?>> providerDependencies = MODULE_DEPENDENCIES;
/** lazily allocated, by {@link #getBindingName}. */
private String bindingName;
/** lazily allocated, by {@link #getKeyForDefaultBinding}. */
private Key<T> defaultBindingKey;
/** lazily allocated, by {@link #getKeyForActualBinding}. */
private Key<T> actualBindingKey;
BindingSelection(Key<T> key) {
this.key = key;
}
void checkNotInitialized() {
checkConfiguration(!initialized, "already initialized");
}
void initialize(InjectorImpl injector) {
// Every one of our providers will call this method, so only execute the logic once.
if (initialized) {
return;
}
actualBinding = injector.getExistingBinding(getKeyForActualBinding());
defaultBinding = injector.getExistingBinding(getKeyForDefaultBinding());
// We should never create Jit bindings, but we can use them if some other binding created it.
BindingImpl<T> userBinding = injector.getExistingBinding(key);
if (actualBinding != null) {
// TODO(sameb): Consider exposing an option that will allow
// ACTUAL to fallback to DEFAULT if ACTUAL's provider returns null.
// Right now, an ACTUAL binding can convert from present -> absent
// if it's bound to a provider that returns null.
binding = actualBinding;
} else if (defaultBinding != null) {
binding = defaultBinding;
} else if (userBinding != null) {
// If neither the actual or default is set, then we fallback
// to the value bound to the type itself and consider that the
// "actual binding" for the SPI.
binding = userBinding;
actualBinding = userBinding;
}
if (binding != null) {
dependencies = ImmutableSet.<Dependency<?>>of(Dependency.get(binding.getKey()));
providerDependencies =
ImmutableSet.<Dependency<?>>of(Dependency.get(providerOf(binding.getKey())));
} else {
dependencies = ImmutableSet.of();
providerDependencies = ImmutableSet.of();
}
initialized = true;
}
Key<T> getKeyForDefaultBinding() {
if (defaultBindingKey == null) {
defaultBindingKey = Key.get(key.getTypeLiteral(), new DefaultImpl(getBindingName()));
}
return defaultBindingKey;
}
Key<T> getKeyForActualBinding() {
if (actualBindingKey == null) {
actualBindingKey = Key.get(key.getTypeLiteral(), new ActualImpl(getBindingName()));
}
return actualBindingKey;
}
Key<T> getDirectKey() {
return key;
}
private String getBindingName() {
// Lazily allocated, most instantiations will never need this because they are deduped during
// module installation.
if (bindingName == null) {
bindingName = Annotations.nameOf(key);
}
return bindingName;
}
BindingImpl<T> getBinding() {
return binding;
}
// Provide default implementations for most of the OptionalBinderBinding interface
BindingImpl<T> getDefaultBinding() {
return defaultBinding;
}
BindingImpl<T> getActualBinding() {
return actualBinding;
}
ImmutableSet<Dependency<?>> providerDependencies() {
return providerDependencies;
}
ImmutableSet<Dependency<?>> dependencies() {
return dependencies;
}
/**
* Returns the Dependency for the target binding, throws NoSuchElementException if no target
* exists.
*
* <p>Calls to this method should typically be guarded by checking if {@link #getBinding()}
* returns {@code null}.
*/
Dependency<?> getDependency() {
return Iterables.getOnlyElement(dependencies);
}
/** Implementation of {@link OptionalBinderBinding#containsElement}. */
boolean containsElement(Element element) {
// All of our bindings are ProviderInstanceBindings whose providers extend
// RealOptionalBinderProviderWithDependencies and have 'this' as its binding selection.
if (element instanceof ProviderInstanceBinding) {
javax.inject.Provider<?> providerInstance =
((ProviderInstanceBinding<?>) element).getUserSuppliedProvider();
if (providerInstance instanceof RealOptionalBinderProviderWithDependencies) {
return ((RealOptionalBinderProviderWithDependencies<?, ?>) providerInstance)
.bindingSelection.equals(this);
}
}
if (element instanceof Binding) {
Key<?> elementKey = ((Binding) element).getKey();
// if it isn't one of the things we bound directly it might be an actual or default key
return elementKey.equals(getKeyForActualBinding())
|| elementKey.equals(getKeyForDefaultBinding());
}
return false; // cannot match;
}
@Override
public boolean equals(Object o) {
return o instanceof BindingSelection && ((BindingSelection) o).key.equals(key);
}
@Override
public int hashCode() {
return key.hashCode();
}
}
@Override
public boolean equals(Object o) {
return o instanceof RealOptionalBinder
&& ((RealOptionalBinder<?>) o).bindingSelection.equals(bindingSelection);
}
@Override
public int hashCode() {
return bindingSelection.hashCode();
}
/** A base class for ProviderWithDependencies that need equality based on a specific object. */
private abstract static class RealOptionalBinderProviderWithDependencies<T, P>
extends InternalProviderInstanceBindingImpl.Factory<P> {
protected final BindingSelection<T> bindingSelection;
RealOptionalBinderProviderWithDependencies(BindingSelection<T> bindingSelection) {
// We need delayed initialization so we can detect jit bindings created by other bindings
// while not also creating jit bindings ourselves. This ensures we only pick up user bindings
// if the binding would have existed in the injector statically.
super(InitializationTiming.DELAYED);
this.bindingSelection = bindingSelection;
}
@Override
final void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
bindingSelection.initialize(injector);
doInitialize();
}
/**
* Initialize the factory. BindingSelection is guaranteed to be initialized at this point and
* this will be called prior to any provisioning.
*/
abstract void doInitialize();
@Override
public boolean equals(Object obj) {
return obj != null
&& this.getClass() == obj.getClass()
&& bindingSelection.equals(
((RealOptionalBinderProviderWithDependencies<?, ?>) obj).bindingSelection);
}
@Override
public int hashCode() {
return bindingSelection.hashCode();
}
}
@SuppressWarnings("serial")
static class DefaultImpl extends BaseAnnotation implements Default {
public DefaultImpl(String value) {
super(Default.class, value);
}
}
@SuppressWarnings("serial")
static class ActualImpl extends BaseAnnotation implements Actual {
public ActualImpl(String value) {
super(Actual.class, value);
}
}
abstract static class BaseAnnotation implements Serializable, Annotation {
private final String value;
private final Class<? extends Annotation> clazz;
BaseAnnotation(Class<? extends Annotation> clazz, String value) {
this.clazz = checkNotNull(clazz, "clazz");
this.value = checkNotNull(value, "value");
}
public String value() {
return this.value;
}
@Override
public int hashCode() {
// This is specified in java.lang.Annotation.
return (127 * "value".hashCode()) ^ value.hashCode();
}
@Override
public boolean equals(Object o) {
// We check against each annotation type instead of BaseAnnotation
// so that we can compare against generated annotation implementations.
if (o instanceof Actual && clazz == Actual.class) {
Actual other = (Actual) o;
return value.equals(other.value());
} else if (o instanceof Default && clazz == Default.class) {
Default other = (Default) o;
return value.equals(other.value());
}
return false;
}
@Override
public String toString() {
return "@" + clazz.getName() + (value.isEmpty() ? "" : "(value=" + value + ")");
}
@Override
public Class<? extends Annotation> annotationType() {
return clazz;
}
private static final long serialVersionUID = 0;
}
}

View file

@ -184,8 +184,7 @@ public abstract class Scoping {
Scope scope = scoping.getScopeInstance();
Provider<T> scoped
= scope.scope(key, new ProviderToInternalFactoryAdapter<T>(injector, creator));
Provider<T> scoped = scope.scope(key, new ProviderToInternalFactoryAdapter<T>(injector, creator));
return new InternalFactoryToProviderAdapter<T>(scoped, source);
}

View file

@ -10,35 +10,38 @@ import java.lang.reflect.Field;
* Sets an injectable field.
*/
final class SingleFieldInjector implements SingleMemberInjector {
final Field field;
final InjectionPoint injectionPoint;
final Dependency<?> dependency;
final BindingImpl<?> binding;
public SingleFieldInjector(InjectorImpl injector, InjectionPoint injectionPoint, Errors errors)
private final Field field;
private final InjectionPoint injectionPoint;
private final Dependency<?> dependency;
private final BindingImpl<?> binding;
SingleFieldInjector(InjectorImpl injector, InjectionPoint injectionPoint, Errors errors)
throws ErrorsException {
this.injectionPoint = injectionPoint;
this.field = (Field) injectionPoint.getMember();
this.dependency = injectionPoint.getDependencies().get(0);
// Ewwwww...
field.setAccessible(true);
binding = injector.getBindingOrThrow(dependency.getKey(), errors, JitLimitation.NO_JIT);
}
@Override
public InjectionPoint getInjectionPoint() {
return injectionPoint;
}
public void inject(Errors errors, InternalContext context, Object o) {
errors = errors.withSource(dependency);
Dependency previous = context.pushDependency(dependency, binding.getSource());
@Override
public void inject(InternalContext context, Object o) throws InternalProvisionException {
Dependency<?> previous = context.pushDependency(dependency, binding.getSource());
try {
Object value = binding.getInternalFactory().get(errors, context, dependency, false);
Object value = binding.getInternalFactory().get(context, dependency, false);
field.set(o, value);
} catch (ErrorsException e) {
errors.withSource(injectionPoint).merge(e.getErrors());
} catch (InternalProvisionException e) {
throw e.addSource(dependency);
} catch (IllegalAccessException e) {
throw new AssertionError(e); // a security manager is blocking us, we're hosed
} finally {

View file

@ -6,7 +6,7 @@ import com.google.inject.spi.InjectionPoint;
* Injects a field or method of a given object.
*/
interface SingleMemberInjector {
void inject(Errors errors, InternalContext context, Object o);
void inject(InternalContext context, Object o) throws InternalProvisionException;
InjectionPoint getInjectionPoint();
}

View file

@ -29,37 +29,23 @@ final class SingleMethodInjector implements SingleMemberInjector {
!Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
method.setAccessible(true);
}
return new MethodInvoker() {
public Object invoke(Object target, Object... parameters)
throws IllegalAccessException, InvocationTargetException {
return method.invoke(target, parameters);
}
};
return method::invoke;
}
public InjectionPoint getInjectionPoint() {
return injectionPoint;
}
public void inject(Errors errors, InternalContext context, Object o) {
Object[] parameters;
try {
parameters = SingleParameterInjector.getAll(errors, context, parameterInjectors);
} catch (ErrorsException e) {
errors.merge(e.getErrors());
return;
}
@Override
public void inject(InternalContext context, Object o) throws InternalProvisionException {
Object[] parameters = SingleParameterInjector.getAll(context, parameterInjectors);
try {
methodInvoker.invoke(o, parameters);
} catch (IllegalAccessException e) {
throw new AssertionError(e); // a security manager is blocking us, we're hosed
} catch (InvocationTargetException userException) {
Throwable cause = userException.getCause() != null
? userException.getCause()
: userException;
errors.withSource(injectionPoint).errorInjectingMethod(cause);
Throwable cause = userException.getCause() != null ? userException.getCause() : userException;
throw InternalProvisionException.errorInjectingMethod(cause).addSource(injectionPoint);
}
}
}

View file

@ -6,48 +6,47 @@ import com.google.inject.spi.Dependency;
* Resolves a single parameter, to be used in a constructor or method invocation.
*/
final class SingleParameterInjector<T> {
private static final Object[] NO_ARGUMENTS = {};
private final Dependency<T> dependency;
private final BindingImpl<? extends T> binding;
private final Object source;
private final InternalFactory<? extends T> factory;
SingleParameterInjector(Dependency<T> dependency, BindingImpl<? extends T> binding) {
this.dependency = dependency;
this.binding = binding;
this.source = binding.getSource();
this.factory = binding.getInternalFactory();
}
/**
* Returns an array of parameter values.
*/
static Object[] getAll(Errors errors, InternalContext context,
SingleParameterInjector<?>[] parameterInjectors) throws ErrorsException {
static Object[] getAll(InternalContext context,
SingleParameterInjector<?>[] parameterInjectors) throws InternalProvisionException {
if (parameterInjectors == null) {
return NO_ARGUMENTS;
}
int numErrorsBefore = errors.size();
int size = parameterInjectors.length;
Object[] parameters = new Object[size];
// optimization: use manual for/each to save allocating an iterator here
for (int i = 0; i < size; i++) {
SingleParameterInjector<?> parameterInjector = parameterInjectors[i];
try {
parameters[i] = parameterInjector.inject(errors, context);
} catch (ErrorsException e) {
errors.merge(e.getErrors());
}
parameters[i] = parameterInjectors[i].inject(context);
}
errors.throwIfNewErrors(numErrorsBefore);
return parameters;
}
private T inject(Errors errors, InternalContext context) throws ErrorsException {
Dependency previous = context.pushDependency(dependency, binding.getSource());
T inject(InternalContext context) throws InternalProvisionException {
Dependency<T> localDependency = dependency;
Dependency<?> previous = context.pushDependency(localDependency, source);
try {
return binding.getInternalFactory().get(errors.withSource(dependency), context, dependency, false);
return factory.get(context, localDependency, false);
} catch (InternalProvisionException ipe) {
throw ipe.addSource(localDependency);
} finally {
context.popStateAndSetDependency(previous);
}

Some files were not shown because too many files have changed in this diff Show more