Compare commits
10 commits
240983e9ec
...
372b5be77e
Author | SHA1 | Date | |
---|---|---|---|
372b5be77e | |||
7e942c813e | |||
9f72318aae | |||
7970fce620 | |||
79970ae966 | |||
70a8da3a4e | |||
5de2c31cbe | |||
e485bbd9e5 | |||
1e7e644d44 | |||
16f09e2dc4 |
276 changed files with 45357 additions and 33007 deletions
|
@ -1,7 +0,0 @@
|
||||||
sudo: false
|
|
||||||
language: java
|
|
||||||
jdk:
|
|
||||||
- oraclejdk8
|
|
||||||
cache:
|
|
||||||
directories:
|
|
||||||
- $HOME/.m2
|
|
19
README.md
19
README.md
|
@ -1,24 +1,25 @@
|
||||||
# Guice
|
= Guice - simplified and reduced version
|
||||||
|
|
||||||
This is a xbib Guice, a build of Guice with the following differences to the
|
This is xbib Guice, a build of Google Guice with the following differences to the
|
||||||
original [Google Guice Core Library](https://github.com/google/guice):
|
original [Google Guice Core Library](https://github.com/google/guice):
|
||||||
|
|
||||||
- only external dependencies are
|
- only external dependencies are
|
||||||
- javax.inject:javax.inject:1
|
- javax.inject:javax.inject:1
|
||||||
- javax.annotation:javax.annotation-api:1.2
|
- org.xbib:jsr-305:1.0.0
|
||||||
- com.google.guava:guava:19.0
|
- com.google.guava:guava:30.1
|
||||||
|
|
||||||
- the extra AOP stuff was removed (aopalliance/cglib), no intercepting any more
|
- 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 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
|
- 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
|
- target of compilation is Java 11, no support for Java 6/7
|
||||||
- introduced Gradle as build system
|
- introduced Gradle as build system
|
||||||
- junit tests made compatible to junit 4.12
|
- junit tests made compatible to junit 4
|
||||||
- assistedinject and multibindings extension included
|
- assistedinject and multibindings extensions are included
|
||||||
|
|
||||||
All credits belong to the original authors, especially Bob Lee http://blog.crazybob.org/
|
All credits belong to the original authors, especially Bob Lee http://blog.crazybob.org/
|
||||||
|
|
||||||
## License
|
== License
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
|
124
build.gradle
124
build.gradle
|
@ -1,85 +1,51 @@
|
||||||
|
plugins {
|
||||||
group = 'org.xbib'
|
//id "checkstyle"
|
||||||
version = '4.0'
|
//id "pmd"
|
||||||
|
id 'maven-publish'
|
||||||
apply plugin: 'java'
|
id 'signing'
|
||||||
apply plugin: 'maven'
|
id "io.github.gradle-nexus.publish-plugin" version "1.3.0"
|
||||||
|
//id "com.github.spotbugs" version "5.0.14"
|
||||||
repositories {
|
id "org.cyclonedx.bom" version "1.7.2"
|
||||||
mavenCentral()
|
//id "org.xbib.gradle.plugin.asciidoctor" version "3.0.0"
|
||||||
mavenLocal()
|
|
||||||
jcenter()
|
|
||||||
maven {
|
|
||||||
url "http://xbib.org/repository"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
configurations {
|
wrapper {
|
||||||
wagon
|
gradleVersion = libs.versions.gradle.get()
|
||||||
|
distributionType = Wrapper.DistributionType.ALL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ext {
|
||||||
|
user = 'joerg'
|
||||||
|
name = 'guice'
|
||||||
|
description = 'Guice implementation with named modules for Java 11+'
|
||||||
|
inceptionYear = '2012'
|
||||||
|
url = 'https://xbib.org/' + user + '/' + name
|
||||||
|
scmUrl = 'https://xbib.org/' + user + '/' + name
|
||||||
|
scmConnection = 'scm:git:git://xbib.org/' + user + '/' + name + '.git'
|
||||||
|
scmDeveloperConnection = 'scm:git:ssh://forgejo@xbib.org:' + user + '/' + name + '.git'
|
||||||
|
issueManagementSystem = 'Github'
|
||||||
|
issueManagementUrl = ext.scmUrl + '/issues'
|
||||||
|
licenseName = 'The Apache License, Version 2.0'
|
||||||
|
licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'java-library'
|
||||||
|
apply from: rootProject.file('gradle/init/banner.gradle')
|
||||||
|
apply from: rootProject.file('gradle/ide/idea.gradle')
|
||||||
|
apply from: rootProject.file('gradle/repositories/maven.gradle')
|
||||||
|
apply from: rootProject.file('gradle/compile/java.gradle')
|
||||||
|
apply from: rootProject.file('gradle/test/junit5.gradle')
|
||||||
|
apply from: rootProject.file('gradle/quality/cyclonedx.gradle')
|
||||||
|
//apply from: rootProject.file('gradle/quality/spotbugs.gradle')
|
||||||
|
//apply from: rootProject.file('gradle/quality/checkstyle.gradle')
|
||||||
|
//apply from: rootProject.file('gradle/quality/pmd.gradle')
|
||||||
|
apply from: rootProject.file('gradle/publish/sonatype.gradle')
|
||||||
|
apply from: rootProject.file('gradle/publish/forgejo.gradle')
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile "javax.inject:javax.inject:1"
|
api libs.javax.inject
|
||||||
compile 'javax.annotation:javax.annotation-api:1.2'
|
api libs.guava
|
||||||
compile "com.google.guava:guava:19.0"
|
testImplementation libs.junit4
|
||||||
testCompile "junit:junit:4.12"
|
// Helper for com.google.common.testing.GcFinalization, com.google.common.testing.EqualsTester
|
||||||
testCompile "org.apache.logging.log4j:log4j-slf4j-impl:2.5"
|
testImplementation libs.guava.testlib
|
||||||
testCompile "org.apache.logging.log4j:log4j-core:2.5"
|
|
||||||
testCompile "javax.inject:javax.inject-tck:1"
|
|
||||||
testCompile "com.google.guava:guava-testlib:19.0"
|
|
||||||
wagon 'org.apache.maven.wagon:wagon-ssh-external:2.10'
|
|
||||||
}
|
|
||||||
|
|
||||||
sourceCompatibility = 1.8
|
|
||||||
targetCompatibility = 1.8
|
|
||||||
|
|
||||||
[compileJava, compileTestJava]*.options.collect { options ->
|
|
||||||
options.encoding = 'UTF-8'
|
|
||||||
options.compilerArgs << '-Xlint:-serial,-path,-rawtypes,-unchecked'
|
|
||||||
}
|
|
||||||
|
|
||||||
test {
|
|
||||||
exclude '*$*'
|
|
||||||
exclude '**/ErrorHandlingTest*'
|
|
||||||
exclude '**/OSGiContainerTest*'
|
|
||||||
exclude '**/ScopesTest*'
|
|
||||||
exclude '**/TypeConversionTest*'
|
|
||||||
testLogging {
|
|
||||||
showStandardStreams = false
|
|
||||||
exceptionFormat = 'full'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
task sourcesJar(type: Jar, dependsOn: classes) {
|
|
||||||
from sourceSets.main.allSource
|
|
||||||
classifier 'sources'
|
|
||||||
}
|
|
||||||
|
|
||||||
artifacts {
|
|
||||||
archives sourcesJar
|
|
||||||
}
|
|
||||||
|
|
||||||
uploadArchives {
|
|
||||||
repositories {
|
|
||||||
if (project.hasProperty("xbibUsername")) {
|
|
||||||
mavenDeployer {
|
|
||||||
configuration = configurations.wagon
|
|
||||||
repository(
|
|
||||||
id: 'xbib.org',
|
|
||||||
url: uri('scpexe://xbib.org/repository'),
|
|
||||||
authentication: [userName: xbibUsername, privateKey: xbibPrivateKey]
|
|
||||||
)
|
|
||||||
pom.project {
|
|
||||||
inceptionYear '2016'
|
|
||||||
licenses {
|
|
||||||
license {
|
|
||||||
name 'The Apache Software License, Version 2.0'
|
|
||||||
url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
|
|
||||||
distribution 'repo'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
5
gradle.properties
Normal file
5
gradle.properties
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
group = org.xbib
|
||||||
|
name = guice
|
||||||
|
version = 5.0.1.0
|
||||||
|
|
||||||
|
org.gradle.warning.mode = ALL
|
33
gradle/compile/java.gradle
Normal file
33
gradle/compile/java.gradle
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
|
||||||
|
apply plugin: 'java-library'
|
||||||
|
|
||||||
|
java {
|
||||||
|
modularity.inferModulePath.set(true)
|
||||||
|
withSourcesJar()
|
||||||
|
withJavadocJar()
|
||||||
|
}
|
||||||
|
|
||||||
|
compileJava {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_17
|
||||||
|
targetCompatibility = JavaVersion.VERSION_17
|
||||||
|
}
|
||||||
|
|
||||||
|
compileTestJava {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_17
|
||||||
|
targetCompatibility = JavaVersion.VERSION_17
|
||||||
|
}
|
||||||
|
|
||||||
|
jar {
|
||||||
|
manifest {
|
||||||
|
attributes('Implementation-Version': project.version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.withType(JavaCompile) {
|
||||||
|
options.encoding('UTF-8')
|
||||||
|
options.compilerArgs << '-Xlint:all'
|
||||||
|
}
|
||||||
|
|
||||||
|
javadoc {
|
||||||
|
options.addStringOption('Xdoclint:none', '-quiet')
|
||||||
|
}
|
55
gradle/documentation/asciidoc.gradle
Normal file
55
gradle/documentation/asciidoc.gradle
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
apply plugin: 'org.xbib.gradle.plugin.asciidoctor'
|
||||||
|
|
||||||
|
configurations {
|
||||||
|
asciidoclet
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
asciidoclet "org.asciidoctor:asciidoclet:${project.property('asciidoclet.version')}"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
asciidoctor {
|
||||||
|
backends 'html5'
|
||||||
|
outputDir = file("${rootProject.projectDir}/docs")
|
||||||
|
separateOutputDirs = false
|
||||||
|
attributes 'source-highlighter': 'coderay',
|
||||||
|
idprefix: '',
|
||||||
|
idseparator: '-',
|
||||||
|
toc: 'left',
|
||||||
|
doctype: 'book',
|
||||||
|
icons: 'font',
|
||||||
|
encoding: 'utf-8',
|
||||||
|
sectlink: true,
|
||||||
|
sectanchors: true,
|
||||||
|
linkattrs: true,
|
||||||
|
imagesdir: 'img',
|
||||||
|
stylesheet: "${projectDir}/src/docs/asciidoc/css/foundation.css"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*javadoc {
|
||||||
|
options.docletpath = configurations.asciidoclet.files.asType(List)
|
||||||
|
options.doclet = 'org.asciidoctor.Asciidoclet'
|
||||||
|
//options.overview = "src/docs/asciidoclet/overview.adoc"
|
||||||
|
options.addStringOption "-base-dir", "${projectDir}"
|
||||||
|
options.addStringOption "-attribute",
|
||||||
|
"name=${project.name},version=${project.version},title-link=https://github.com/xbib/${project.name}"
|
||||||
|
configure(options) {
|
||||||
|
noTimestamp = true
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
|
||||||
|
/*javadoc {
|
||||||
|
options.docletpath = configurations.asciidoclet.files.asType(List)
|
||||||
|
options.doclet = 'org.asciidoctor.Asciidoclet'
|
||||||
|
options.overview = "${rootProject.projectDir}/src/docs/asciidoclet/overview.adoc"
|
||||||
|
options.addStringOption "-base-dir", "${projectDir}"
|
||||||
|
options.addStringOption "-attribute",
|
||||||
|
"name=${project.name},version=${project.version},title-link=https://github.com/xbib/${project.name}"
|
||||||
|
options.destinationDirectory(file("${projectDir}/docs/javadoc"))
|
||||||
|
configure(options) {
|
||||||
|
noTimestamp = true
|
||||||
|
}
|
||||||
|
}*/
|
12
gradle/ide/idea.gradle
Normal file
12
gradle/ide/idea.gradle
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
apply plugin: 'idea'
|
||||||
|
|
||||||
|
idea {
|
||||||
|
module {
|
||||||
|
outputDir file('build/classes/java/main')
|
||||||
|
testOutputDir file('build/classes/java/test')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clean {
|
||||||
|
delete 'out'
|
||||||
|
}
|
14
gradle/init/banner.gradle
Normal file
14
gradle/init/banner.gradle
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
gradle.projectsLoaded { g ->
|
||||||
|
printf "Host: %s\nOS: %s %s %s\nJVM: %s %s %s %s\nGradle: %s Groovy: %s Java: %s\n" +
|
||||||
|
"Build: group: %s name: %s version: %s\n",
|
||||||
|
InetAddress.getLocalHost(),
|
||||||
|
System.getProperty("os.name"),
|
||||||
|
System.getProperty("os.arch"),
|
||||||
|
System.getProperty("os.version"),
|
||||||
|
System.getProperty("java.version"),
|
||||||
|
System.getProperty("java.vm.version"),
|
||||||
|
System.getProperty("java.vm.vendor"),
|
||||||
|
System.getProperty("java.vm.name"),
|
||||||
|
gradle.gradleVersion, GroovySystem.getVersion(), JavaVersion.current(),
|
||||||
|
gradle.rootProject.group, gradle.rootProject.name, gradle.rootProject.version
|
||||||
|
}
|
16
gradle/publish/forgejo.gradle
Normal file
16
gradle/publish/forgejo.gradle
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
if (project.hasProperty('forgeJoToken')) {
|
||||||
|
publishing {
|
||||||
|
repositories {
|
||||||
|
maven {
|
||||||
|
url 'https://xbib.org/api/packages/joerg/maven'
|
||||||
|
credentials(HttpHeaderCredentials) {
|
||||||
|
name = "Authorization"
|
||||||
|
value = "token ${project.property('forgeJoToken')}"
|
||||||
|
}
|
||||||
|
authentication {
|
||||||
|
header(HttpHeaderAuthentication)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
27
gradle/publish/ivy.gradle
Normal file
27
gradle/publish/ivy.gradle
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
apply plugin: 'ivy-publish'
|
||||||
|
|
||||||
|
publishing {
|
||||||
|
repositories {
|
||||||
|
ivy {
|
||||||
|
url = "https://xbib.org/repo"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
publications {
|
||||||
|
ivy(IvyPublication) {
|
||||||
|
from components.java
|
||||||
|
descriptor {
|
||||||
|
license {
|
||||||
|
name = 'The Apache License, Version 2.0'
|
||||||
|
url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
|
||||||
|
}
|
||||||
|
author {
|
||||||
|
name = 'Jörg Prante'
|
||||||
|
url = 'https://xbib.org/joerg'
|
||||||
|
}
|
||||||
|
descriptor.description {
|
||||||
|
text = rootProject.ext.description
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
51
gradle/publish/maven.gradle
Normal file
51
gradle/publish/maven.gradle
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
|
||||||
|
publishing {
|
||||||
|
publications {
|
||||||
|
"${project.name}"(MavenPublication) {
|
||||||
|
from components.java
|
||||||
|
pom {
|
||||||
|
artifactId = project.name
|
||||||
|
name = project.name
|
||||||
|
description = rootProject.ext.description
|
||||||
|
url = rootProject.ext.url
|
||||||
|
inceptionYear = rootProject.ext.inceptionYear
|
||||||
|
packaging = 'jar'
|
||||||
|
organization {
|
||||||
|
name = 'xbib'
|
||||||
|
url = 'https://xbib.org'
|
||||||
|
}
|
||||||
|
developers {
|
||||||
|
developer {
|
||||||
|
id = 'joerg'
|
||||||
|
name = 'Jörg Prante'
|
||||||
|
email = 'joergprante@gmail.com'
|
||||||
|
url = 'https://xbib.org/joerg'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scm {
|
||||||
|
url = rootProject.ext.scmUrl
|
||||||
|
connection = rootProject.ext.scmConnection
|
||||||
|
developerConnection = rootProject.ext.scmDeveloperConnection
|
||||||
|
}
|
||||||
|
issueManagement {
|
||||||
|
system = rootProject.ext.issueManagementSystem
|
||||||
|
url = rootProject.ext.issueManagementUrl
|
||||||
|
}
|
||||||
|
licenses {
|
||||||
|
license {
|
||||||
|
name = rootProject.ext.licenseName
|
||||||
|
url = rootProject.ext.licenseUrl
|
||||||
|
distribution = 'repo'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (project.hasProperty("signing.keyId")) {
|
||||||
|
apply plugin: 'signing'
|
||||||
|
signing {
|
||||||
|
sign publishing.publications."${project.name}"
|
||||||
|
}
|
||||||
|
}
|
11
gradle/publish/sonatype.gradle
Normal file
11
gradle/publish/sonatype.gradle
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
if (project.hasProperty('ossrhUsername') && project.hasProperty('ossrhPassword')) {
|
||||||
|
nexusPublishing {
|
||||||
|
repositories {
|
||||||
|
sonatype {
|
||||||
|
username = project.property('ossrhUsername')
|
||||||
|
password = project.property('ossrhPassword')
|
||||||
|
packageGroup = "org.xbib"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
19
gradle/quality/checkstyle.gradle
Normal file
19
gradle/quality/checkstyle.gradle
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
|
||||||
|
apply plugin: 'checkstyle'
|
||||||
|
|
||||||
|
tasks.withType(Checkstyle) {
|
||||||
|
ignoreFailures = true
|
||||||
|
reports {
|
||||||
|
xml.getRequired().set(true)
|
||||||
|
html.getRequired().set(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
checkstyle {
|
||||||
|
configFile = rootProject.file('gradle/quality/checkstyle.xml')
|
||||||
|
ignoreFailures = true
|
||||||
|
showViolations = true
|
||||||
|
checkstyleMain {
|
||||||
|
source = sourceSets.main.allSource
|
||||||
|
}
|
||||||
|
}
|
333
gradle/quality/checkstyle.xml
Normal file
333
gradle/quality/checkstyle.xml
Normal file
|
@ -0,0 +1,333 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE module PUBLIC
|
||||||
|
"-//Puppy Crawl//DTD Check Configuration 1.3//EN"
|
||||||
|
"http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
|
||||||
|
|
||||||
|
<!-- This is a checkstyle configuration file. For descriptions of
|
||||||
|
what the following rules do, please see the checkstyle configuration
|
||||||
|
page at http://checkstyle.sourceforge.net/config.html -->
|
||||||
|
|
||||||
|
<module name="Checker">
|
||||||
|
|
||||||
|
<module name="BeforeExecutionExclusionFileFilter">
|
||||||
|
<property name="fileNamePattern" value=".*(Example|Test|module-info)(\$.*)?"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="FileTabCharacter">
|
||||||
|
<!-- Checks that there are no tab characters in the file.
|
||||||
|
-->
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="NewlineAtEndOfFile">
|
||||||
|
<property name="lineSeparator" value="lf"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="RegexpSingleline">
|
||||||
|
<!-- Checks that FIXME is not used in comments. TODO is preferred.
|
||||||
|
-->
|
||||||
|
<property name="format" value="((//.*)|(\*.*))FIXME" />
|
||||||
|
<property name="message" value='TODO is preferred to FIXME. e.g. "TODO(johndoe): Refactor when v2 is released."' />
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="RegexpSingleline">
|
||||||
|
<!-- Checks that TODOs are named. (Actually, just that they are followed
|
||||||
|
by an open paren.)
|
||||||
|
-->
|
||||||
|
<property name="format" value="((//.*)|(\*.*))TODO[^(]" />
|
||||||
|
<property name="message" value='All TODOs should be named. e.g. "TODO(johndoe): Refactor when v2 is released."' />
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="JavadocPackage">
|
||||||
|
<!-- Checks that each Java package has a Javadoc file used for commenting.
|
||||||
|
Only allows a package-info.java, not package.html. -->
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<!-- All Java AST specific tests live under TreeWalker module. -->
|
||||||
|
<module name="TreeWalker">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
|
||||||
|
IMPORT CHECKS
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
<module name="RedundantImport">
|
||||||
|
<!-- Checks for redundant import statements. -->
|
||||||
|
<property name="severity" value="error"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="ImportOrder">
|
||||||
|
<!-- Checks for out of order import statements. -->
|
||||||
|
|
||||||
|
<property name="severity" value="warning"/>
|
||||||
|
<!-- <property name="tokens" value="IMPORT, STATIC_IMPORT"/> -->
|
||||||
|
<property name="separated" value="false"/>
|
||||||
|
<property name="groups" value="*"/>
|
||||||
|
<!-- <property name="option" value="above"/> -->
|
||||||
|
<property name="sortStaticImportsAlphabetically" value="true"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="CustomImportOrder">
|
||||||
|
<!-- <property name="customImportOrderRules" value="THIRD_PARTY_PACKAGE###SPECIAL_IMPORTS###STANDARD_JAVA_PACKAGE###STATIC"/> -->
|
||||||
|
<!-- <property name="specialImportsRegExp" value="^javax\."/> -->
|
||||||
|
<!-- <property name="standardPackageRegExp" value="^java\."/> -->
|
||||||
|
<property name="sortImportsInGroupAlphabetically" value="true"/>
|
||||||
|
<property name="separateLineBetweenGroups" value="false"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
|
||||||
|
JAVADOC CHECKS
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!-- Checks for Javadoc comments. -->
|
||||||
|
<!-- See http://checkstyle.sf.net/config_javadoc.html -->
|
||||||
|
<module name="JavadocMethod">
|
||||||
|
<property name="accessModifiers" value="protected"/>
|
||||||
|
<property name="severity" value="warning"/>
|
||||||
|
<property name="allowMissingParamTags" value="true"/>
|
||||||
|
<property name="allowMissingReturnTag" value="true"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="JavadocType">
|
||||||
|
<property name="scope" value="protected"/>
|
||||||
|
<property name="severity" value="error"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="JavadocStyle">
|
||||||
|
<property name="severity" value="warning"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
|
||||||
|
NAMING CHECKS
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!-- Item 38 - Adhere to generally accepted naming conventions -->
|
||||||
|
|
||||||
|
<module name="PackageName">
|
||||||
|
<!-- Validates identifiers for package names against the
|
||||||
|
supplied expression. -->
|
||||||
|
<!-- Here the default checkstyle rule restricts package name parts to
|
||||||
|
seven characters, this is not in line with common practice at Google.
|
||||||
|
-->
|
||||||
|
<property name="format" value="^[a-z]+(\.[a-z][a-z0-9]{1,})*$"/>
|
||||||
|
<property name="severity" value="warning"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="TypeNameCheck">
|
||||||
|
<!-- Validates static, final fields against the
|
||||||
|
expression "^[A-Z][a-zA-Z0-9]*$". -->
|
||||||
|
<metadata name="altname" value="TypeName"/>
|
||||||
|
<property name="severity" value="warning"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="ConstantNameCheck">
|
||||||
|
<!-- Validates non-private, static, final fields against the supplied
|
||||||
|
public/package final fields "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$". -->
|
||||||
|
<metadata name="altname" value="ConstantName"/>
|
||||||
|
<property name="applyToPublic" value="true"/>
|
||||||
|
<property name="applyToProtected" value="true"/>
|
||||||
|
<property name="applyToPackage" value="true"/>
|
||||||
|
<property name="applyToPrivate" value="false"/>
|
||||||
|
<property name="format" value="^([A-Z][A-Z0-9]*(_[A-Z0-9]+)*|FLAG_.*)$"/>
|
||||||
|
<message key="name.invalidPattern"
|
||||||
|
value="Variable ''{0}'' should be in ALL_CAPS (if it is a constant) or be private (otherwise)."/>
|
||||||
|
<property name="severity" value="warning"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="StaticVariableNameCheck">
|
||||||
|
<!-- Validates static, non-final fields against the supplied
|
||||||
|
expression "^[a-z][a-zA-Z0-9]*_?$". -->
|
||||||
|
<metadata name="altname" value="StaticVariableName"/>
|
||||||
|
<property name="applyToPublic" value="true"/>
|
||||||
|
<property name="applyToProtected" value="true"/>
|
||||||
|
<property name="applyToPackage" value="true"/>
|
||||||
|
<property name="applyToPrivate" value="true"/>
|
||||||
|
<property name="format" value="^[a-z][a-zA-Z0-9]*_?$"/>
|
||||||
|
<property name="severity" value="warning"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="MemberNameCheck">
|
||||||
|
<!-- Validates non-static members against the supplied expression. -->
|
||||||
|
<metadata name="altname" value="MemberName"/>
|
||||||
|
<property name="applyToPublic" value="true"/>
|
||||||
|
<property name="applyToProtected" value="true"/>
|
||||||
|
<property name="applyToPackage" value="true"/>
|
||||||
|
<property name="applyToPrivate" value="true"/>
|
||||||
|
<property name="format" value="^[a-z][a-zA-Z0-9]*$"/>
|
||||||
|
<property name="severity" value="warning"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="MethodNameCheck">
|
||||||
|
<!-- Validates identifiers for method names. -->
|
||||||
|
<metadata name="altname" value="MethodName"/>
|
||||||
|
<property name="format" value="^[a-z][a-zA-Z0-9]*(_[a-zA-Z0-9]+)*$"/>
|
||||||
|
<property name="severity" value="warning"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="ParameterName">
|
||||||
|
<!-- Validates identifiers for method parameters against the
|
||||||
|
expression "^[a-z][a-zA-Z0-9]*$". -->
|
||||||
|
<property name="severity" value="warning"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="LocalFinalVariableName">
|
||||||
|
<!-- Validates identifiers for local final variables against the
|
||||||
|
expression "^[a-z][a-zA-Z0-9]*$". -->
|
||||||
|
<property name="severity" value="warning"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="LocalVariableName">
|
||||||
|
<!-- Validates identifiers for local variables against the
|
||||||
|
expression "^[a-z][a-zA-Z0-9]*$". -->
|
||||||
|
<property name="severity" value="warning"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
|
||||||
|
<!--
|
||||||
|
|
||||||
|
LENGTH and CODING CHECKS
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
|
||||||
|
<module name="LeftCurly">
|
||||||
|
<!-- Checks for placement of the left curly brace ('{'). -->
|
||||||
|
<property name="severity" value="warning"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="RightCurly">
|
||||||
|
<!-- Checks right curlies on CATCH, ELSE, and TRY blocks are on
|
||||||
|
the same line. e.g., the following example is fine:
|
||||||
|
<pre>
|
||||||
|
if {
|
||||||
|
...
|
||||||
|
} else
|
||||||
|
</pre>
|
||||||
|
-->
|
||||||
|
<!-- This next example is not fine:
|
||||||
|
<pre>
|
||||||
|
if {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
else
|
||||||
|
</pre>
|
||||||
|
-->
|
||||||
|
<property name="option" value="same"/>
|
||||||
|
<property name="severity" value="warning"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<!-- Checks for braces around if and else blocks -->
|
||||||
|
<module name="NeedBraces">
|
||||||
|
<property name="severity" value="warning"/>
|
||||||
|
<property name="tokens" value="LITERAL_IF, LITERAL_ELSE, LITERAL_FOR, LITERAL_WHILE, LITERAL_DO"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="UpperEll">
|
||||||
|
<!-- Checks that long constants are defined with an upper ell.-->
|
||||||
|
<property name="severity" value="error"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="FallThrough">
|
||||||
|
<!-- Warn about falling through to the next case statement. Similar to
|
||||||
|
javac -Xlint:fallthrough, but the check is suppressed if a single-line comment
|
||||||
|
on the last non-blank line preceding the fallen-into case contains 'fall through' (or
|
||||||
|
some other variants which we don't publicized to promote consistency).
|
||||||
|
-->
|
||||||
|
<property name="reliefPattern"
|
||||||
|
value="fall through|Fall through|fallthru|Fallthru|falls through|Falls through|fallthrough|Fallthrough|No break|NO break|no break|continue on"/>
|
||||||
|
<property name="severity" value="error"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
|
||||||
|
<!--
|
||||||
|
|
||||||
|
MODIFIERS CHECKS
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
<module name="ModifierOrder">
|
||||||
|
<!-- Warn if modifier order is inconsistent with JLS3 8.1.1, 8.3.1, and
|
||||||
|
8.4.3. The prescribed order is:
|
||||||
|
public, protected, private, abstract, static, final, transient, volatile,
|
||||||
|
synchronized, native, strictfp
|
||||||
|
-->
|
||||||
|
</module>
|
||||||
|
|
||||||
|
|
||||||
|
<!--
|
||||||
|
|
||||||
|
WHITESPACE CHECKS
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
<module name="WhitespaceAround">
|
||||||
|
<!-- Checks that various tokens are surrounded by whitespace.
|
||||||
|
This includes most binary operators and keywords followed
|
||||||
|
by regular or curly braces.
|
||||||
|
-->
|
||||||
|
<property name="tokens" value="ASSIGN, BAND, BAND_ASSIGN, BOR,
|
||||||
|
BOR_ASSIGN, BSR, BSR_ASSIGN, BXOR, BXOR_ASSIGN, COLON, DIV, DIV_ASSIGN,
|
||||||
|
EQUAL, GE, GT, LAND, LE, LITERAL_CATCH, LITERAL_DO, LITERAL_ELSE,
|
||||||
|
LITERAL_FINALLY, LITERAL_FOR, LITERAL_IF, LITERAL_RETURN,
|
||||||
|
LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_WHILE, LOR, LT, MINUS,
|
||||||
|
MINUS_ASSIGN, MOD, MOD_ASSIGN, NOT_EQUAL, PLUS, PLUS_ASSIGN, QUESTION,
|
||||||
|
SL, SL_ASSIGN, SR_ASSIGN, STAR, STAR_ASSIGN"/>
|
||||||
|
<property name="severity" value="error"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="WhitespaceAfter">
|
||||||
|
<!-- Checks that commas, semicolons and typecasts are followed by
|
||||||
|
whitespace.
|
||||||
|
-->
|
||||||
|
<property name="tokens" value="COMMA, SEMI, TYPECAST"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="NoWhitespaceAfter">
|
||||||
|
<!-- Checks that there is no whitespace after various unary operators.
|
||||||
|
Linebreaks are allowed.
|
||||||
|
-->
|
||||||
|
<property name="tokens" value="BNOT, DEC, DOT, INC, LNOT, UNARY_MINUS,
|
||||||
|
UNARY_PLUS"/>
|
||||||
|
<property name="allowLineBreaks" value="true"/>
|
||||||
|
<property name="severity" value="error"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="NoWhitespaceBefore">
|
||||||
|
<!-- Checks that there is no whitespace before various unary operators.
|
||||||
|
Linebreaks are allowed.
|
||||||
|
-->
|
||||||
|
<property name="tokens" value="SEMI, DOT, POST_DEC, POST_INC"/>
|
||||||
|
<property name="allowLineBreaks" value="true"/>
|
||||||
|
<property name="severity" value="error"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="ParenPad">
|
||||||
|
<!-- Checks that there is no whitespace before close parens or after
|
||||||
|
open parens.
|
||||||
|
-->
|
||||||
|
<property name="severity" value="warning"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="LineLength">
|
||||||
|
<!-- Checks if a line is too long. -->
|
||||||
|
<property name="max" value="${com.puppycrawl.tools.checkstyle.checks.sizes.LineLength.max}" default="128"/>
|
||||||
|
<property name="severity" value="error"/>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
The default ignore pattern exempts the following elements:
|
||||||
|
- import statements
|
||||||
|
- long URLs inside comments
|
||||||
|
-->
|
||||||
|
|
||||||
|
<property name="ignorePattern"
|
||||||
|
value="${com.puppycrawl.tools.checkstyle.checks.sizes.LineLength.ignorePattern}"
|
||||||
|
default="^(package .*;\s*)|(import .*;\s*)|( *(\*|//).*https?://.*)$"/>
|
||||||
|
</module>
|
||||||
|
</module>
|
||||||
|
|
11
gradle/quality/cyclonedx.gradle
Normal file
11
gradle/quality/cyclonedx.gradle
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
cyclonedxBom {
|
||||||
|
includeConfigs = [ 'runtimeClasspath' ]
|
||||||
|
skipConfigs = [ 'compileClasspath', 'testCompileClasspath' ]
|
||||||
|
projectType = "library"
|
||||||
|
schemaVersion = "1.4"
|
||||||
|
destination = file("build/reports")
|
||||||
|
outputName = "bom"
|
||||||
|
outputFormat = "json"
|
||||||
|
includeBomSerialNumber = true
|
||||||
|
componentVersion = "2.0.0"
|
||||||
|
}
|
17
gradle/quality/pmd.gradle
Normal file
17
gradle/quality/pmd.gradle
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
|
||||||
|
apply plugin: 'pmd'
|
||||||
|
|
||||||
|
tasks.withType(Pmd) {
|
||||||
|
ignoreFailures = true
|
||||||
|
reports {
|
||||||
|
xml.getRequired().set(true)
|
||||||
|
html.getRequired().set(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pmd {
|
||||||
|
ignoreFailures = true
|
||||||
|
consoleOutput = false
|
||||||
|
toolVersion = "6.51.0"
|
||||||
|
ruleSetFiles = rootProject.files('gradle/quality/pmd/category/java/bestpractices.xml')
|
||||||
|
}
|
1650
gradle/quality/pmd/category/java/bestpractices.xml
Normal file
1650
gradle/quality/pmd/category/java/bestpractices.xml
Normal file
File diff suppressed because it is too large
Load diff
10
gradle/quality/pmd/category/java/categories.properties
Normal file
10
gradle/quality/pmd/category/java/categories.properties
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
|
||||||
|
rulesets.filenames=\
|
||||||
|
category/java/bestpractices.xml,\
|
||||||
|
category/java/codestyle.xml,\
|
||||||
|
category/java/design.xml,\
|
||||||
|
category/java/documentation.xml,\
|
||||||
|
category/java/errorprone.xml,\
|
||||||
|
category/java/multithreading.xml,\
|
||||||
|
category/java/performance.xml,\
|
||||||
|
category/java/security.xml
|
2176
gradle/quality/pmd/category/java/codestyle.xml
Normal file
2176
gradle/quality/pmd/category/java/codestyle.xml
Normal file
File diff suppressed because it is too large
Load diff
1657
gradle/quality/pmd/category/java/design.xml
Normal file
1657
gradle/quality/pmd/category/java/design.xml
Normal file
File diff suppressed because it is too large
Load diff
144
gradle/quality/pmd/category/java/documentation.xml
Normal file
144
gradle/quality/pmd/category/java/documentation.xml
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
|
||||||
|
<ruleset name="Documentation"
|
||||||
|
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd">
|
||||||
|
|
||||||
|
<description>
|
||||||
|
Rules that are related to code documentation.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<rule name="CommentContent"
|
||||||
|
since="5.0"
|
||||||
|
message="Invalid words or phrases found"
|
||||||
|
class="net.sourceforge.pmd.lang.java.rule.documentation.CommentContentRule"
|
||||||
|
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_documentation.html#commentcontent">
|
||||||
|
<description>
|
||||||
|
A rule for the politically correct... we don't want to offend anyone.
|
||||||
|
</description>
|
||||||
|
<priority>3</priority>
|
||||||
|
<example>
|
||||||
|
<![CDATA[
|
||||||
|
//OMG, this is horrible, Bob is an idiot !!!
|
||||||
|
]]>
|
||||||
|
</example>
|
||||||
|
</rule>
|
||||||
|
|
||||||
|
<rule name="CommentRequired"
|
||||||
|
since="5.1"
|
||||||
|
message="Comment is required"
|
||||||
|
class="net.sourceforge.pmd.lang.java.rule.documentation.CommentRequiredRule"
|
||||||
|
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_documentation.html#commentrequired">
|
||||||
|
<description>
|
||||||
|
Denotes whether comments are required (or unwanted) for specific language elements.
|
||||||
|
</description>
|
||||||
|
<priority>3</priority>
|
||||||
|
<example>
|
||||||
|
<![CDATA[
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @author Jon Doe
|
||||||
|
*/
|
||||||
|
]]>
|
||||||
|
</example>
|
||||||
|
</rule>
|
||||||
|
|
||||||
|
<rule name="CommentSize"
|
||||||
|
since="5.0"
|
||||||
|
message="Comment is too large"
|
||||||
|
class="net.sourceforge.pmd.lang.java.rule.documentation.CommentSizeRule"
|
||||||
|
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_documentation.html#commentsize">
|
||||||
|
<description>
|
||||||
|
Determines whether the dimensions of non-header comments found are within the specified limits.
|
||||||
|
</description>
|
||||||
|
<priority>3</priority>
|
||||||
|
<example>
|
||||||
|
<![CDATA[
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* too many lines!
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
]]>
|
||||||
|
</example>
|
||||||
|
</rule>
|
||||||
|
|
||||||
|
<rule name="UncommentedEmptyConstructor"
|
||||||
|
language="java"
|
||||||
|
since="3.4"
|
||||||
|
message="Document empty constructor"
|
||||||
|
class="net.sourceforge.pmd.lang.rule.XPathRule"
|
||||||
|
typeResolution="true"
|
||||||
|
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_documentation.html#uncommentedemptyconstructor">
|
||||||
|
<description>
|
||||||
|
Uncommented Empty Constructor finds instances where a constructor does not
|
||||||
|
contain statements, but there is no comment. By explicitly commenting empty
|
||||||
|
constructors it is easier to distinguish between intentional (commented)
|
||||||
|
and unintentional empty constructors.
|
||||||
|
</description>
|
||||||
|
<priority>3</priority>
|
||||||
|
<properties>
|
||||||
|
<property name="xpath">
|
||||||
|
<value>
|
||||||
|
<![CDATA[
|
||||||
|
//ConstructorDeclaration[@Private='false']
|
||||||
|
[count(BlockStatement) = 0 and ($ignoreExplicitConstructorInvocation = 'true' or not(ExplicitConstructorInvocation)) and @containsComment = 'false']
|
||||||
|
[not(../Annotation/MarkerAnnotation/Name[pmd-java:typeIs('javax.inject.Inject')])]
|
||||||
|
]]>
|
||||||
|
</value>
|
||||||
|
</property>
|
||||||
|
<property name="ignoreExplicitConstructorInvocation" type="Boolean" description="Ignore explicit constructor invocation when deciding whether constructor is empty or not" value="false"/>
|
||||||
|
</properties>
|
||||||
|
<example>
|
||||||
|
<![CDATA[
|
||||||
|
public Foo() {
|
||||||
|
// This constructor is intentionally empty. Nothing special is needed here.
|
||||||
|
}
|
||||||
|
]]>
|
||||||
|
</example>
|
||||||
|
</rule>
|
||||||
|
|
||||||
|
<rule name="UncommentedEmptyMethodBody"
|
||||||
|
language="java"
|
||||||
|
since="3.4"
|
||||||
|
message="Document empty method body"
|
||||||
|
class="net.sourceforge.pmd.lang.rule.XPathRule"
|
||||||
|
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_documentation.html#uncommentedemptymethodbody">
|
||||||
|
<description>
|
||||||
|
Uncommented Empty Method Body finds instances where a method body does not contain
|
||||||
|
statements, but there is no comment. By explicitly commenting empty method bodies
|
||||||
|
it is easier to distinguish between intentional (commented) and unintentional
|
||||||
|
empty methods.
|
||||||
|
</description>
|
||||||
|
<priority>3</priority>
|
||||||
|
<properties>
|
||||||
|
<property name="xpath">
|
||||||
|
<value>
|
||||||
|
<![CDATA[
|
||||||
|
//MethodDeclaration/Block[count(BlockStatement) = 0 and @containsComment = 'false']
|
||||||
|
]]>
|
||||||
|
</value>
|
||||||
|
</property>
|
||||||
|
</properties>
|
||||||
|
<example>
|
||||||
|
<![CDATA[
|
||||||
|
public void doSomething() {
|
||||||
|
}
|
||||||
|
]]>
|
||||||
|
</example>
|
||||||
|
</rule>
|
||||||
|
|
||||||
|
</ruleset>
|
3383
gradle/quality/pmd/category/java/errorprone.xml
Normal file
3383
gradle/quality/pmd/category/java/errorprone.xml
Normal file
File diff suppressed because it is too large
Load diff
393
gradle/quality/pmd/category/java/multithreading.xml
Normal file
393
gradle/quality/pmd/category/java/multithreading.xml
Normal file
|
@ -0,0 +1,393 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
|
||||||
|
<ruleset name="Multithreading"
|
||||||
|
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd">
|
||||||
|
|
||||||
|
<description>
|
||||||
|
Rules that flag issues when dealing with multiple threads of execution.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<rule name="AvoidSynchronizedAtMethodLevel"
|
||||||
|
language="java"
|
||||||
|
since="3.0"
|
||||||
|
message="Use block level rather than method level synchronization"
|
||||||
|
class="net.sourceforge.pmd.lang.rule.XPathRule"
|
||||||
|
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_multithreading.html#avoidsynchronizedatmethodlevel">
|
||||||
|
<description>
|
||||||
|
Method-level synchronization can cause problems when new code is added to the method.
|
||||||
|
Block-level synchronization helps to ensure that only the code that needs synchronization
|
||||||
|
gets it.
|
||||||
|
</description>
|
||||||
|
<priority>3</priority>
|
||||||
|
<properties>
|
||||||
|
<property name="xpath">
|
||||||
|
<value>//MethodDeclaration[@Synchronized='true']</value>
|
||||||
|
</property>
|
||||||
|
</properties>
|
||||||
|
<example>
|
||||||
|
<![CDATA[
|
||||||
|
public class Foo {
|
||||||
|
// Try to avoid this:
|
||||||
|
synchronized void foo() {
|
||||||
|
}
|
||||||
|
// Prefer this:
|
||||||
|
void bar() {
|
||||||
|
synchronized(this) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to avoid this for static methods:
|
||||||
|
static synchronized void fooStatic() {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prefer this:
|
||||||
|
static void barStatic() {
|
||||||
|
synchronized(Foo.class) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]]>
|
||||||
|
</example>
|
||||||
|
</rule>
|
||||||
|
|
||||||
|
<rule name="AvoidThreadGroup"
|
||||||
|
language="java"
|
||||||
|
since="3.6"
|
||||||
|
message="Avoid using java.lang.ThreadGroup; it is not thread safe"
|
||||||
|
class="net.sourceforge.pmd.lang.rule.XPathRule"
|
||||||
|
typeResolution="true"
|
||||||
|
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_multithreading.html#avoidthreadgroup">
|
||||||
|
<description>
|
||||||
|
Avoid using java.lang.ThreadGroup; although it is intended to be used in a threaded environment
|
||||||
|
it contains methods that are not thread-safe.
|
||||||
|
</description>
|
||||||
|
<priority>3</priority>
|
||||||
|
<properties>
|
||||||
|
<property name="xpath">
|
||||||
|
<value>
|
||||||
|
<![CDATA[
|
||||||
|
//AllocationExpression/ClassOrInterfaceType[pmd-java:typeIs('java.lang.ThreadGroup')]|
|
||||||
|
//PrimarySuffix[contains(@Image, 'getThreadGroup')]
|
||||||
|
]]>
|
||||||
|
</value>
|
||||||
|
</property>
|
||||||
|
</properties>
|
||||||
|
<example>
|
||||||
|
<![CDATA[
|
||||||
|
public class Bar {
|
||||||
|
void buz() {
|
||||||
|
ThreadGroup tg = new ThreadGroup("My threadgroup");
|
||||||
|
tg = new ThreadGroup(tg, "my thread group");
|
||||||
|
tg = Thread.currentThread().getThreadGroup();
|
||||||
|
tg = System.getSecurityManager().getThreadGroup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]]>
|
||||||
|
</example>
|
||||||
|
</rule>
|
||||||
|
|
||||||
|
<rule name="AvoidUsingVolatile"
|
||||||
|
language="java"
|
||||||
|
since="4.1"
|
||||||
|
class="net.sourceforge.pmd.lang.rule.XPathRule"
|
||||||
|
message="Use of modifier volatile is not recommended."
|
||||||
|
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_multithreading.html#avoidusingvolatile">
|
||||||
|
<description>
|
||||||
|
Use of the keyword 'volatile' is generally used to fine tune a Java application, and therefore, requires
|
||||||
|
a good expertise of the Java Memory Model. Moreover, its range of action is somewhat misknown. Therefore,
|
||||||
|
the volatile keyword should not be used for maintenance purpose and portability.
|
||||||
|
</description>
|
||||||
|
<priority>2</priority>
|
||||||
|
<properties>
|
||||||
|
<property name="xpath">
|
||||||
|
<value>//FieldDeclaration[contains(@Volatile,'true')]</value>
|
||||||
|
</property>
|
||||||
|
</properties>
|
||||||
|
<example>
|
||||||
|
<![CDATA[
|
||||||
|
public class ThrDeux {
|
||||||
|
private volatile String var1; // not suggested
|
||||||
|
private String var2; // preferred
|
||||||
|
}
|
||||||
|
]]>
|
||||||
|
</example>
|
||||||
|
</rule>
|
||||||
|
|
||||||
|
<rule name="DoNotUseThreads"
|
||||||
|
language="java"
|
||||||
|
since="4.1"
|
||||||
|
class="net.sourceforge.pmd.lang.rule.XPathRule"
|
||||||
|
message="To be compliant to J2EE, a webapp should not use any thread."
|
||||||
|
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_multithreading.html#donotusethreads">
|
||||||
|
<description>
|
||||||
|
The J2EE specification explicitly forbids the use of threads.
|
||||||
|
</description>
|
||||||
|
<priority>3</priority>
|
||||||
|
<properties>
|
||||||
|
<property name="xpath">
|
||||||
|
<value>//ClassOrInterfaceType[@Image = 'Thread' or @Image = 'Runnable']</value>
|
||||||
|
</property>
|
||||||
|
</properties>
|
||||||
|
<example>
|
||||||
|
<![CDATA[
|
||||||
|
// This is not allowed
|
||||||
|
public class UsingThread extends Thread {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Neither this,
|
||||||
|
public class OtherThread implements Runnable {
|
||||||
|
// Nor this ...
|
||||||
|
public void methode() {
|
||||||
|
Runnable thread = new Thread(); thread.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]]>
|
||||||
|
</example>
|
||||||
|
</rule>
|
||||||
|
|
||||||
|
<rule name="DontCallThreadRun"
|
||||||
|
language="java"
|
||||||
|
since="4.3"
|
||||||
|
message="Don't call Thread.run() explicitly, use Thread.start()"
|
||||||
|
class="net.sourceforge.pmd.lang.rule.XPathRule"
|
||||||
|
typeResolution="true"
|
||||||
|
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_multithreading.html#dontcallthreadrun">
|
||||||
|
<description>
|
||||||
|
Explicitly calling Thread.run() method will execute in the caller's thread of control. Instead, call Thread.start() for the intended behavior.
|
||||||
|
</description>
|
||||||
|
<priority>4</priority>
|
||||||
|
<properties>
|
||||||
|
<property name="xpath">
|
||||||
|
<value>
|
||||||
|
<![CDATA[
|
||||||
|
//StatementExpression/PrimaryExpression
|
||||||
|
[
|
||||||
|
PrimaryPrefix
|
||||||
|
[
|
||||||
|
./Name[ends-with(@Image, '.run') or @Image = 'run']
|
||||||
|
and substring-before(Name/@Image, '.') =//VariableDeclarator/VariableDeclaratorId/@Image
|
||||||
|
[../../../Type/ReferenceType/ClassOrInterfaceType[pmd-java:typeIs('java.lang.Thread')]]
|
||||||
|
or (./AllocationExpression/ClassOrInterfaceType[pmd-java:typeIs('java.lang.Thread')]
|
||||||
|
and ../PrimarySuffix[@Image = 'run'])
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]]>
|
||||||
|
</value>
|
||||||
|
</property>
|
||||||
|
</properties>
|
||||||
|
<example>
|
||||||
|
<![CDATA[
|
||||||
|
Thread t = new Thread();
|
||||||
|
t.run(); // use t.start() instead
|
||||||
|
new Thread().run(); // same violation
|
||||||
|
]]>
|
||||||
|
</example>
|
||||||
|
</rule>
|
||||||
|
|
||||||
|
<rule name="DoubleCheckedLocking"
|
||||||
|
language="java"
|
||||||
|
since="1.04"
|
||||||
|
message="Double checked locking is not thread safe in Java."
|
||||||
|
class="net.sourceforge.pmd.lang.java.rule.multithreading.DoubleCheckedLockingRule"
|
||||||
|
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_multithreading.html#doublecheckedlocking">
|
||||||
|
<description>
|
||||||
|
Partially created objects can be returned by the Double Checked Locking pattern when used in Java.
|
||||||
|
An optimizing JRE may assign a reference to the baz variable before it calls the constructor of the object the
|
||||||
|
reference points to.
|
||||||
|
|
||||||
|
Note: With Java 5, you can make Double checked locking work, if you declare the variable to be `volatile`.
|
||||||
|
|
||||||
|
For more details refer to: <http://www.javaworld.com/javaworld/jw-02-2001/jw-0209-double.html>
|
||||||
|
or <http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html>
|
||||||
|
</description>
|
||||||
|
<priority>1</priority>
|
||||||
|
<example>
|
||||||
|
<![CDATA[
|
||||||
|
public class Foo {
|
||||||
|
/*volatile */ Object baz = null; // fix for Java5 and later: volatile
|
||||||
|
Object bar() {
|
||||||
|
if (baz == null) { // baz may be non-null yet not fully created
|
||||||
|
synchronized(this) {
|
||||||
|
if (baz == null) {
|
||||||
|
baz = new Object();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return baz;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]]>
|
||||||
|
</example>
|
||||||
|
</rule>
|
||||||
|
|
||||||
|
<rule name="NonThreadSafeSingleton"
|
||||||
|
since="3.4"
|
||||||
|
message="Singleton is not thread safe"
|
||||||
|
class="net.sourceforge.pmd.lang.java.rule.multithreading.NonThreadSafeSingletonRule"
|
||||||
|
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_multithreading.html#nonthreadsafesingleton">
|
||||||
|
<description>
|
||||||
|
Non-thread safe singletons can result in bad state changes. Eliminate
|
||||||
|
static singletons if possible by instantiating the object directly. Static
|
||||||
|
singletons are usually not needed as only a single instance exists anyway.
|
||||||
|
Other possible fixes are to synchronize the entire method or to use an
|
||||||
|
[initialize-on-demand holder class](https://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom).
|
||||||
|
|
||||||
|
Refrain from using the double-checked locking pattern. The Java Memory Model doesn't
|
||||||
|
guarantee it to work unless the variable is declared as `volatile`, adding an uneeded
|
||||||
|
performance penalty. [Reference](http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html)
|
||||||
|
|
||||||
|
See Effective Java, item 48.
|
||||||
|
</description>
|
||||||
|
<priority>3</priority>
|
||||||
|
<example>
|
||||||
|
<![CDATA[
|
||||||
|
private static Foo foo = null;
|
||||||
|
|
||||||
|
//multiple simultaneous callers may see partially initialized objects
|
||||||
|
public static Foo getFoo() {
|
||||||
|
if (foo==null) {
|
||||||
|
foo = new Foo();
|
||||||
|
}
|
||||||
|
return foo;
|
||||||
|
}
|
||||||
|
]]>
|
||||||
|
</example>
|
||||||
|
</rule>
|
||||||
|
|
||||||
|
<rule name="UnsynchronizedStaticDateFormatter"
|
||||||
|
since="3.6"
|
||||||
|
deprecated="true"
|
||||||
|
message="Static DateFormatter objects should be accessed in a synchronized manner"
|
||||||
|
class="net.sourceforge.pmd.lang.java.rule.multithreading.UnsynchronizedStaticDateFormatterRule"
|
||||||
|
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_multithreading.html#unsynchronizedstaticdateformatter">
|
||||||
|
<description>
|
||||||
|
SimpleDateFormat instances are not synchronized. Sun recommends using separate format instances
|
||||||
|
for each thread. If multiple threads must access a static formatter, the formatter must be
|
||||||
|
synchronized either on method or block level.
|
||||||
|
|
||||||
|
This rule has been deprecated in favor of the rule {% rule UnsynchronizedStaticFormatter %}.
|
||||||
|
</description>
|
||||||
|
<priority>3</priority>
|
||||||
|
<example>
|
||||||
|
<![CDATA[
|
||||||
|
public class Foo {
|
||||||
|
private static final SimpleDateFormat sdf = new SimpleDateFormat();
|
||||||
|
void bar() {
|
||||||
|
sdf.format(); // poor, no thread-safety
|
||||||
|
}
|
||||||
|
synchronized void foo() {
|
||||||
|
sdf.format(); // preferred
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]]>
|
||||||
|
</example>
|
||||||
|
</rule>
|
||||||
|
|
||||||
|
<rule name="UnsynchronizedStaticFormatter"
|
||||||
|
since="6.11.0"
|
||||||
|
message="Static Formatter objects should be accessed in a synchronized manner"
|
||||||
|
class="net.sourceforge.pmd.lang.java.rule.multithreading.UnsynchronizedStaticFormatterRule"
|
||||||
|
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_multithreading.html#unsynchronizedstaticformatter">
|
||||||
|
<description>
|
||||||
|
Instances of `java.text.Format` are generally not synchronized.
|
||||||
|
Sun recommends using separate format instances for each thread.
|
||||||
|
If multiple threads must access a static formatter, the formatter must be
|
||||||
|
synchronized either on method or block level.
|
||||||
|
</description>
|
||||||
|
<priority>3</priority>
|
||||||
|
<example>
|
||||||
|
<![CDATA[
|
||||||
|
public class Foo {
|
||||||
|
private static final SimpleDateFormat sdf = new SimpleDateFormat();
|
||||||
|
void bar() {
|
||||||
|
sdf.format(); // poor, no thread-safety
|
||||||
|
}
|
||||||
|
synchronized void foo() {
|
||||||
|
sdf.format(); // preferred
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]]>
|
||||||
|
</example>
|
||||||
|
</rule>
|
||||||
|
|
||||||
|
<rule name="UseConcurrentHashMap"
|
||||||
|
language="java"
|
||||||
|
minimumLanguageVersion="1.5"
|
||||||
|
since="4.2.6"
|
||||||
|
message="If you run in Java5 or newer and have concurrent access, you should use the ConcurrentHashMap implementation"
|
||||||
|
class="net.sourceforge.pmd.lang.rule.XPathRule"
|
||||||
|
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_multithreading.html#useconcurrenthashmap">
|
||||||
|
<description>
|
||||||
|
Since Java5 brought a new implementation of the Map designed for multi-threaded access, you can
|
||||||
|
perform efficient map reads without blocking other threads.
|
||||||
|
</description>
|
||||||
|
<priority>3</priority>
|
||||||
|
<properties>
|
||||||
|
<property name="xpath">
|
||||||
|
<value>
|
||||||
|
<![CDATA[
|
||||||
|
//Type[../VariableDeclarator/VariableInitializer//AllocationExpression/ClassOrInterfaceType[@Image != 'ConcurrentHashMap']]
|
||||||
|
/ReferenceType/ClassOrInterfaceType[@Image = 'Map']
|
||||||
|
]]>
|
||||||
|
</value>
|
||||||
|
</property>
|
||||||
|
</properties>
|
||||||
|
<example>
|
||||||
|
<![CDATA[
|
||||||
|
public class ConcurrentApp {
|
||||||
|
public void getMyInstance() {
|
||||||
|
Map map1 = new HashMap(); // fine for single-threaded access
|
||||||
|
Map map2 = new ConcurrentHashMap(); // preferred for use with multiple threads
|
||||||
|
|
||||||
|
// the following case will be ignored by this rule
|
||||||
|
Map map3 = someModule.methodThatReturnMap(); // might be OK, if the returned map is already thread-safe
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]]>
|
||||||
|
</example>
|
||||||
|
</rule>
|
||||||
|
|
||||||
|
<rule name="UseNotifyAllInsteadOfNotify"
|
||||||
|
language="java"
|
||||||
|
since="3.0"
|
||||||
|
message="Call Thread.notifyAll() rather than Thread.notify()"
|
||||||
|
class="net.sourceforge.pmd.lang.rule.XPathRule"
|
||||||
|
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_multithreading.html#usenotifyallinsteadofnotify">
|
||||||
|
<description>
|
||||||
|
Thread.notify() awakens a thread monitoring the object. If more than one thread is monitoring, then only
|
||||||
|
one is chosen. The thread chosen is arbitrary; thus its usually safer to call notifyAll() instead.
|
||||||
|
</description>
|
||||||
|
<priority>3</priority>
|
||||||
|
<properties>
|
||||||
|
<property name="xpath">
|
||||||
|
<value>
|
||||||
|
<![CDATA[
|
||||||
|
//StatementExpression/PrimaryExpression
|
||||||
|
[PrimarySuffix/Arguments[@ArgumentCount = '0']]
|
||||||
|
[
|
||||||
|
PrimaryPrefix[
|
||||||
|
./Name[@Image='notify' or ends-with(@Image,'.notify')]
|
||||||
|
or ../PrimarySuffix/@Image='notify'
|
||||||
|
or (./AllocationExpression and ../PrimarySuffix[@Image='notify'])
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]]>
|
||||||
|
</value>
|
||||||
|
</property>
|
||||||
|
</properties>
|
||||||
|
<example>
|
||||||
|
<![CDATA[
|
||||||
|
void bar() {
|
||||||
|
x.notify();
|
||||||
|
// If many threads are monitoring x, only one (and you won't know which) will be notified.
|
||||||
|
// use instead:
|
||||||
|
x.notifyAll();
|
||||||
|
}
|
||||||
|
]]>
|
||||||
|
</example>
|
||||||
|
</rule>
|
||||||
|
|
||||||
|
</ruleset>
|
1006
gradle/quality/pmd/category/java/performance.xml
Normal file
1006
gradle/quality/pmd/category/java/performance.xml
Normal file
File diff suppressed because it is too large
Load diff
65
gradle/quality/pmd/category/java/security.xml
Normal file
65
gradle/quality/pmd/category/java/security.xml
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
|
||||||
|
<ruleset name="Security" xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd">
|
||||||
|
|
||||||
|
<description>
|
||||||
|
Rules that flag potential security flaws.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<rule name="HardCodedCryptoKey"
|
||||||
|
since="6.4.0"
|
||||||
|
message="Do not use hard coded encryption keys"
|
||||||
|
class="net.sourceforge.pmd.lang.java.rule.security.HardCodedCryptoKeyRule"
|
||||||
|
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_security.html#hardcodedcryptokey">
|
||||||
|
<description>
|
||||||
|
Do not use hard coded values for cryptographic operations. Please store keys outside of source code.
|
||||||
|
</description>
|
||||||
|
<priority>3</priority>
|
||||||
|
<example>
|
||||||
|
<![CDATA[
|
||||||
|
public class Foo {
|
||||||
|
void good() {
|
||||||
|
SecretKeySpec secretKeySpec = new SecretKeySpec(Properties.getKey(), "AES");
|
||||||
|
}
|
||||||
|
|
||||||
|
void bad() {
|
||||||
|
SecretKeySpec secretKeySpec = new SecretKeySpec("my secret here".getBytes(), "AES");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]]>
|
||||||
|
</example>
|
||||||
|
</rule>
|
||||||
|
|
||||||
|
<rule name="InsecureCryptoIv"
|
||||||
|
since="6.3.0"
|
||||||
|
message="Do not use hard coded initialization vector in crypto operations"
|
||||||
|
class="net.sourceforge.pmd.lang.java.rule.security.InsecureCryptoIvRule"
|
||||||
|
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_security.html#insecurecryptoiv">
|
||||||
|
<description>
|
||||||
|
Do not use hard coded initialization vector in cryptographic operations. Please use a randomly generated IV.
|
||||||
|
</description>
|
||||||
|
<priority>3</priority>
|
||||||
|
<example>
|
||||||
|
<![CDATA[
|
||||||
|
public class Foo {
|
||||||
|
void good() {
|
||||||
|
SecureRandom random = new SecureRandom();
|
||||||
|
byte iv[] = new byte[16];
|
||||||
|
random.nextBytes(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bad() {
|
||||||
|
byte[] iv = new byte[] { 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, };
|
||||||
|
}
|
||||||
|
|
||||||
|
void alsoBad() {
|
||||||
|
byte[] iv = "secret iv in here".getBytes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]]>
|
||||||
|
</example>
|
||||||
|
</rule>
|
||||||
|
|
||||||
|
</ruleset>
|
37
gradle/quality/sonarqube.gradle
Normal file
37
gradle/quality/sonarqube.gradle
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
|
||||||
|
subprojects {
|
||||||
|
|
||||||
|
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.junit.reportsPath", "build/test-results/test/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
tasks.withType(Pmd) {
|
||||||
|
ignoreFailures = true
|
||||||
|
reports {
|
||||||
|
xml.enabled = true
|
||||||
|
html.enabled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
spotbugs {
|
||||||
|
effort = "max"
|
||||||
|
reportLevel = "low"
|
||||||
|
//includeFilter = file("findbugs-exclude.xml")
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.withType(com.github.spotbugs.SpotBugsTask) {
|
||||||
|
ignoreFailures = true
|
||||||
|
reports {
|
||||||
|
xml.enabled = false
|
||||||
|
html.enabled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
15
gradle/quality/spotbugs.gradle
Normal file
15
gradle/quality/spotbugs.gradle
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
|
||||||
|
apply plugin: 'com.github.spotbugs'
|
||||||
|
|
||||||
|
spotbugs {
|
||||||
|
effort = "max"
|
||||||
|
reportLevel = "low"
|
||||||
|
ignoreFailures = true
|
||||||
|
}
|
||||||
|
|
||||||
|
spotbugsMain {
|
||||||
|
reports {
|
||||||
|
xml.getRequired().set(false)
|
||||||
|
html.getRequired().set(true)
|
||||||
|
}
|
||||||
|
}
|
4
gradle/repositories/maven.gradle
Normal file
4
gradle/repositories/maven.gradle
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
repositories {
|
||||||
|
mavenLocal()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
28
gradle/test/junit5.gradle
Normal file
28
gradle/test/junit5.gradle
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
|
||||||
|
def junitVersion = project.hasProperty('junit.version')?project.property('junit.version'):'5.7.1'
|
||||||
|
def hamcrestVersion = project.hasProperty('hamcrest.version')?project.property('hamcrest.version'):'2.2'
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
testImplementation "org.junit.jupiter:junit-jupiter-api:${junitVersion}"
|
||||||
|
testImplementation "org.junit.jupiter:junit-jupiter-params:${junitVersion}"
|
||||||
|
testImplementation "org.hamcrest:hamcrest-library:${hamcrestVersion}"
|
||||||
|
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junitVersion}"
|
||||||
|
testRuntimeOnly "org.junit.vintage:junit-vintage-engine:${junitVersion}"
|
||||||
|
}
|
||||||
|
|
||||||
|
test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
failFast = false
|
||||||
|
testLogging {
|
||||||
|
events 'STARTED', 'PASSED', 'FAILED', 'SKIPPED'
|
||||||
|
}
|
||||||
|
afterSuite { desc, result ->
|
||||||
|
if (!desc.parent) {
|
||||||
|
println "\nTest result: ${result.resultType}"
|
||||||
|
println "Test summary: ${result.testCount} tests, " +
|
||||||
|
"${result.successfulTestCount} succeeded, " +
|
||||||
|
"${result.failedTestCount} failed, " +
|
||||||
|
"${result.skippedTestCount} skipped"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-all.zip
|
||||||
|
networkTimeout=10000
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
245
gradlew
vendored
Executable file
245
gradlew
vendored
Executable file
|
@ -0,0 +1,245 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright © 2015-2021 the original 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 POSIX generated by Gradle.
|
||||||
|
#
|
||||||
|
# Important for running:
|
||||||
|
#
|
||||||
|
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||||
|
# noncompliant, but you have some other compliant shell such as ksh or
|
||||||
|
# bash, then to run this script, type that shell name before the whole
|
||||||
|
# command line, like:
|
||||||
|
#
|
||||||
|
# ksh Gradle
|
||||||
|
#
|
||||||
|
# Busybox and similar reduced shells will NOT work, because this script
|
||||||
|
# requires all of these POSIX shell features:
|
||||||
|
# * functions;
|
||||||
|
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||||
|
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||||
|
# * compound commands having a testable exit status, especially «case»;
|
||||||
|
# * various built-in commands including «command», «set», and «ulimit».
|
||||||
|
#
|
||||||
|
# Important for patching:
|
||||||
|
#
|
||||||
|
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||||
|
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||||
|
#
|
||||||
|
# The "traditional" practice of packing multiple parameters into a
|
||||||
|
# space-separated string is a well documented source of bugs and security
|
||||||
|
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||||
|
# options in "$@", and eventually passing that to Java.
|
||||||
|
#
|
||||||
|
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||||
|
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||||
|
# see the in-line comments for details.
|
||||||
|
#
|
||||||
|
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||||
|
# Darwin, MinGW, and NonStop.
|
||||||
|
#
|
||||||
|
# (3) This script is generated from the Groovy template
|
||||||
|
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
|
# within the Gradle project.
|
||||||
|
#
|
||||||
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
app_path=$0
|
||||||
|
|
||||||
|
# Need this for daisy-chained symlinks.
|
||||||
|
while
|
||||||
|
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||||
|
[ -h "$app_path" ]
|
||||||
|
do
|
||||||
|
ls=$( ls -ld "$app_path" )
|
||||||
|
link=${ls#*' -> '}
|
||||||
|
case $link in #(
|
||||||
|
/*) app_path=$link ;; #(
|
||||||
|
*) app_path=$APP_HOME$link ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# This is normally unused
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
APP_BASE_NAME=${0##*/}
|
||||||
|
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD=maximum
|
||||||
|
|
||||||
|
warn () {
|
||||||
|
echo "$*"
|
||||||
|
} >&2
|
||||||
|
|
||||||
|
die () {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
} >&2
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
nonstop=false
|
||||||
|
case "$( uname )" in #(
|
||||||
|
CYGWIN* ) cygwin=true ;; #(
|
||||||
|
Darwin* ) darwin=true ;; #(
|
||||||
|
MSYS* | MINGW* ) msys=true ;; #(
|
||||||
|
NONSTOP* ) nonstop=true ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
# Determine the Java command to use to start the JVM.
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||||
|
else
|
||||||
|
JAVACMD=$JAVA_HOME/bin/java
|
||||||
|
fi
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD=java
|
||||||
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||||
|
case $MAX_FD in #(
|
||||||
|
max*)
|
||||||
|
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||||
|
# shellcheck disable=SC3045
|
||||||
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
|
warn "Could not query maximum file descriptor limit"
|
||||||
|
esac
|
||||||
|
case $MAX_FD in #(
|
||||||
|
'' | soft) :;; #(
|
||||||
|
*)
|
||||||
|
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||||
|
# shellcheck disable=SC3045
|
||||||
|
ulimit -n "$MAX_FD" ||
|
||||||
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Collect all arguments for the java command, stacking in reverse order:
|
||||||
|
# * args from the command line
|
||||||
|
# * the main class name
|
||||||
|
# * -classpath
|
||||||
|
# * -D...appname settings
|
||||||
|
# * --module-path (only if needed)
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||||
|
|
||||||
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
|
if "$cygwin" || "$msys" ; then
|
||||||
|
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||||
|
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||||
|
|
||||||
|
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||||
|
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
for arg do
|
||||||
|
if
|
||||||
|
case $arg in #(
|
||||||
|
-*) false ;; # don't mess with options #(
|
||||||
|
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||||
|
[ -e "$t" ] ;; #(
|
||||||
|
*) false ;;
|
||||||
|
esac
|
||||||
|
then
|
||||||
|
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||||
|
fi
|
||||||
|
# Roll the args list around exactly as many times as the number of
|
||||||
|
# args, so each arg winds up back in the position where it started, but
|
||||||
|
# possibly modified.
|
||||||
|
#
|
||||||
|
# NB: a `for` loop captures its iteration list before it begins, so
|
||||||
|
# changing the positional parameters here affects neither the number of
|
||||||
|
# iterations, nor the values presented in `arg`.
|
||||||
|
shift # remove old arg
|
||||||
|
set -- "$@" "$arg" # push replacement arg
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
|
# Collect all arguments for the java command;
|
||||||
|
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||||
|
# shell script including quotes and variable substitutions, so put them in
|
||||||
|
# double quotes to make sure that they get re-expanded; and
|
||||||
|
# * put everything else in single quotes, so that it's not re-expanded.
|
||||||
|
|
||||||
|
set -- \
|
||||||
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
|
-classpath "$CLASSPATH" \
|
||||||
|
org.gradle.wrapper.GradleWrapperMain \
|
||||||
|
"$@"
|
||||||
|
|
||||||
|
# Stop when "xargs" is not available.
|
||||||
|
if ! command -v xargs >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
die "xargs is not available"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Use "xargs" to parse quoted args.
|
||||||
|
#
|
||||||
|
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||||
|
#
|
||||||
|
# In Bash we could simply go:
|
||||||
|
#
|
||||||
|
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||||
|
# set -- "${ARGS[@]}" "$@"
|
||||||
|
#
|
||||||
|
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||||
|
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||||
|
# character that might be a shell metacharacter, then use eval to reverse
|
||||||
|
# that process (while maintaining the separation between arguments), and wrap
|
||||||
|
# the whole thing up as a single "set" statement.
|
||||||
|
#
|
||||||
|
# This will of course break if any of these variables contains a newline or
|
||||||
|
# an unmatched quote.
|
||||||
|
#
|
||||||
|
|
||||||
|
eval "set -- $(
|
||||||
|
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||||
|
xargs -n1 |
|
||||||
|
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||||
|
tr '\n' ' '
|
||||||
|
)" '"$@"'
|
||||||
|
|
||||||
|
exec "$JAVACMD" "$@"
|
92
gradlew.bat
vendored
Normal file
92
gradlew.bat
vendored
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
@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
|
||||||
|
@rem Gradle startup script for Windows
|
||||||
|
@rem
|
||||||
|
@rem ##########################################################################
|
||||||
|
|
||||||
|
@rem Set local scope for the variables with windows NT shell
|
||||||
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
|
set DIRNAME=%~dp0
|
||||||
|
if "%DIRNAME%"=="" set DIRNAME=.
|
||||||
|
@rem This is normally unused
|
||||||
|
set APP_BASE_NAME=%~n0
|
||||||
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||||
|
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||||
|
|
||||||
|
@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="-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
|
@rem Find java.exe
|
||||||
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
|
set JAVA_EXE=java.exe
|
||||||
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
|
if %ERRORLEVEL% equ 0 goto execute
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
|
|
||||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
@rem Execute Gradle
|
||||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||||
|
|
||||||
|
:fail
|
||||||
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
|
rem the _cmd.exe /c_ return code!
|
||||||
|
set EXIT_CODE=%ERRORLEVEL%
|
||||||
|
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||||
|
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||||
|
exit /b %EXIT_CODE%
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
|
@ -1 +1,49 @@
|
||||||
rootProject.name = 'guice'
|
pluginManagement {
|
||||||
|
repositories {
|
||||||
|
mavenLocal()
|
||||||
|
mavenCentral {
|
||||||
|
metadataSources {
|
||||||
|
mavenPom()
|
||||||
|
artifact()
|
||||||
|
ignoreGradleMetadataRedirection()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gradlePluginPortal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencyResolutionManagement {
|
||||||
|
versionCatalogs {
|
||||||
|
libs {
|
||||||
|
version('gradle', '8.1.1')
|
||||||
|
version('groovy', '3.0.10')
|
||||||
|
version('junit', '5.9.2')
|
||||||
|
library('junit-jupiter-api', 'org.junit.jupiter', 'junit-jupiter-api').versionRef('junit')
|
||||||
|
library('junit-jupiter-params', 'org.junit.jupiter', 'junit-jupiter-params').versionRef('junit')
|
||||||
|
library('junit-jupiter-engine', 'org.junit.jupiter', 'junit-jupiter-engine').versionRef('junit')
|
||||||
|
library('hamcrest', 'org.hamcrest', 'hamcrest-library').version('2.2')
|
||||||
|
library('junit4', 'junit', 'junit').version('4.13.2')
|
||||||
|
library('javax.inject', 'org.xbib', 'javax-inject').version('1')
|
||||||
|
library('guava', 'org.xbib', 'guava').version('30.1')
|
||||||
|
library('guava.testlib', 'com.google.guava', 'guava-testlib').version('30.1-jre')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
gradle.wrapper.version = 6.6.1
|
||||||
|
javax-inject.version = 1
|
||||||
|
guava.version = 30.1
|
||||||
|
# test
|
||||||
|
junit.version = 5.7.2
|
||||||
|
junit4.version = 4.13.2
|
||||||
|
log4j.version = 2.14.1
|
||||||
|
|
||||||
|
api "org.xbib:javax-inject:${project.property('javax-inject.version')}"
|
||||||
|
api "org.xbib:guava:${project.property('guava.version')}"
|
||||||
|
testImplementation "junit:junit:${project.property('junit4.version')}"
|
||||||
|
// Helper for com.google.common.testing.GcFinalization, com.google.common.testing.EqualsTester
|
||||||
|
testImplementation "com.google.guava:guava-testlib:30.1-jre"
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
|
@ -36,6 +36,7 @@ public abstract class AbstractModule implements Module {
|
||||||
|
|
||||||
Binder binder;
|
Binder binder;
|
||||||
|
|
||||||
|
@Override
|
||||||
public final synchronized void configure(Binder builder) {
|
public final synchronized void configure(Binder builder) {
|
||||||
checkState(this.binder == null, "Re-entry is not allowed.");
|
checkState(this.binder == null, "Re-entry is not allowed.");
|
||||||
|
|
||||||
|
@ -63,8 +64,7 @@ public abstract class AbstractModule implements Module {
|
||||||
/**
|
/**
|
||||||
* @see Binder#bindScope(Class, Scope)
|
* @see Binder#bindScope(Class, Scope)
|
||||||
*/
|
*/
|
||||||
protected void bindScope(Class<? extends Annotation> scopeAnnotation,
|
protected void bindScope(Class<? extends Annotation> scopeAnnotation, Scope scope) {
|
||||||
Scope scope) {
|
|
||||||
binder().bindScope(scopeAnnotation, scope);
|
binder().bindScope(scopeAnnotation, scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -100,7 +100,7 @@ import java.lang.reflect.Proxy;
|
||||||
* contribute their own custom scopes for use here as well.
|
* contribute their own custom scopes for use here as well.
|
||||||
*
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* bind(new TypeLiteral<PaymentService<CreditCard>>() {})
|
* bind(new TypeLiteral<PaymentService<CreditCard>>() {})
|
||||||
* .to(CreditCardPaymentService.class);</pre>
|
* .to(CreditCardPaymentService.class);</pre>
|
||||||
*
|
*
|
||||||
* This admittedly odd construct is the way to bind a parameterized type. It
|
* This admittedly odd construct is the way to bind a parameterized type. It
|
||||||
|
@ -159,7 +159,7 @@ import java.lang.reflect.Proxy;
|
||||||
* your application.
|
* your application.
|
||||||
*
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* Constructor<T> loneCtor = getLoneCtorFromServiceImplViaReflection();
|
* Constructor<T> loneCtor = getLoneCtorFromServiceImplViaReflection();
|
||||||
* bind(ServiceImpl.class)
|
* bind(ServiceImpl.class)
|
||||||
* .toConstructor(loneCtor);</pre>
|
* .toConstructor(loneCtor);</pre>
|
||||||
*
|
*
|
||||||
|
@ -367,7 +367,7 @@ public interface Binder {
|
||||||
* their clients.
|
* their clients.
|
||||||
* @return a binder that shares its configuration with this binder.
|
* @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
|
* Creates a new private child environment for bindings and other configuration. The returned
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package com.google.inject;
|
package com.google.inject;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
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 com.google.inject.spi.Message;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
@ -24,7 +24,7 @@ public final class ConfigurationException extends RuntimeException {
|
||||||
*/
|
*/
|
||||||
public ConfigurationException(Iterable<Message> messages) {
|
public ConfigurationException(Iterable<Message> messages) {
|
||||||
this.messages = ImmutableSet.copyOf(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
|
@Override
|
||||||
public String getMessage() {
|
public String getMessage() {
|
||||||
return Errors.format("Guice configuration errors", messages);
|
return Messages.formatMessages("Guice configuration errors", messages);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
package com.google.inject;
|
package com.google.inject;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
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 com.google.inject.spi.Message;
|
||||||
|
|
||||||
import java.util.Collection;
|
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
|
* 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.
|
* errors. Clients should catch this exception, log it, and stop execution.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("serial")
|
||||||
public class CreationException extends RuntimeException {
|
public class CreationException extends RuntimeException {
|
||||||
|
|
||||||
private static final long serialVersionUID = 0;
|
|
||||||
private final ImmutableSet<Message> messages;
|
private final ImmutableSet<Message> messages;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -23,7 +23,7 @@ public class CreationException extends RuntimeException {
|
||||||
public CreationException(Collection<Message> messages) {
|
public CreationException(Collection<Message> messages) {
|
||||||
this.messages = ImmutableSet.copyOf(messages);
|
this.messages = ImmutableSet.copyOf(messages);
|
||||||
checkArgument(!this.messages.isEmpty());
|
checkArgument(!this.messages.isEmpty());
|
||||||
initCause(Errors.getOnlyCause(this.messages));
|
initCause(Messages.getOnlyCause(this.messages));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -35,6 +35,6 @@ public class CreationException extends RuntimeException {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getMessage() {
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,11 +76,7 @@ public final class Guice {
|
||||||
* @throws CreationException if one or more errors occur during injector
|
* @throws CreationException if one or more errors occur during injector
|
||||||
* construction
|
* construction
|
||||||
*/
|
*/
|
||||||
public static Injector createInjector(Stage stage,
|
public static Injector createInjector(Stage stage, Iterable<? extends Module> modules) {
|
||||||
Iterable<? extends Module> modules) {
|
return new InternalInjectorCreator().stage(stage).addModules(modules).build();
|
||||||
return new InternalInjectorCreator()
|
|
||||||
.stage(stage)
|
|
||||||
.addModules(modules)
|
|
||||||
.build();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package com.google.inject;
|
package com.google.inject;
|
||||||
|
|
||||||
|
import com.google.inject.spi.Element;
|
||||||
|
import com.google.inject.spi.InjectionPoint;
|
||||||
import com.google.inject.spi.TypeConverterBinding;
|
import com.google.inject.spi.TypeConverterBinding;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
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.
|
* <p>This method is part of the Guice SPI and is intended for use by tools and extensions.
|
||||||
*/
|
*/
|
||||||
Set<TypeConverterBinding> getTypeConverterBindings();
|
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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +1,20 @@
|
||||||
package com.google.inject;
|
package com.google.inject;
|
||||||
|
|
||||||
import com.google.common.base.Supplier;
|
|
||||||
import com.google.common.base.Suppliers;
|
|
||||||
import com.google.inject.internal.Annotations;
|
|
||||||
import com.google.inject.internal.MoreTypes;
|
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
import java.lang.reflect.Type;
|
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkArgument;
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
import static com.google.inject.internal.Annotations.generateAnnotation;
|
import static com.google.inject.internal.Annotations.generateAnnotation;
|
||||||
import static com.google.inject.internal.Annotations.isAllDefaultMethods;
|
import static com.google.inject.internal.Annotations.isAllDefaultMethods;
|
||||||
|
|
||||||
|
import com.google.inject.internal.Annotations;
|
||||||
|
import com.google.inject.internal.MoreTypes;
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Binding key consisting of an injection type and an optional annotation.
|
* Guice uses Key objects to identify a dependency that can be resolved by the Guice {@link
|
||||||
* Matches the type and annotation at a point of injection.
|
* Injector}. A Guice key consists of an injection type and an optional annotation.
|
||||||
*
|
*
|
||||||
* <p>For example, {@code Key.get(Service.class, Transactional.class)} will
|
* <p>For example, {@code Key.get(Service.class, Transactional.class)} will match:
|
||||||
* match:
|
|
||||||
*
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* {@literal @}Inject
|
* {@literal @}Inject
|
||||||
|
@ -27,12 +23,13 @@ import static com.google.inject.internal.Annotations.isAllDefaultMethods;
|
||||||
* }
|
* }
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* <p>{@code Key} supports generic types via subclassing just like {@link
|
* <p>{@code Key} supports generic types via subclassing just like {@link TypeLiteral}.
|
||||||
* TypeLiteral}.
|
|
||||||
*
|
*
|
||||||
* <p>Keys do not differentiate between primitive types (int, char, etc.) and
|
* <p>Keys do not differentiate between primitive types (int, char, etc.) and their corresponding
|
||||||
* their corresponding wrapper types (Integer, Character, etc.). Primitive
|
* wrapper types (Integer, Character, etc.). Primitive types will be replaced with their wrapper
|
||||||
* types will be replaced with their wrapper types when keys are created.
|
* types when keys are created.
|
||||||
|
*
|
||||||
|
* @author crazybob@google.com (Bob Lee)
|
||||||
*/
|
*/
|
||||||
public class Key<T> {
|
public class Key<T> {
|
||||||
|
|
||||||
|
@ -40,38 +37,37 @@ public class Key<T> {
|
||||||
|
|
||||||
private final TypeLiteral<T> typeLiteral;
|
private final TypeLiteral<T> typeLiteral;
|
||||||
private final int hashCode;
|
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.
|
* Constructs a new key. Derives the type from this class's type parameter.
|
||||||
*
|
*
|
||||||
* <p>Clients create an empty anonymous subclass. Doing so embeds the type
|
* <p>Clients create an empty anonymous subclass. Doing so embeds the type parameter in the
|
||||||
* parameter in the anonymous class's type hierarchy so we can reconstitute it
|
* anonymous class's type hierarchy so we can reconstitute it at runtime despite erasure.
|
||||||
* at runtime despite erasure.
|
|
||||||
*
|
*
|
||||||
* <p>Example usage for a binding of type {@code Foo} annotated with
|
* <p>Example usage for a binding of type {@code Foo} annotated with {@code @Bar}:
|
||||||
* {@code @Bar}:
|
|
||||||
*
|
*
|
||||||
* <p>{@code new Key<Foo>(Bar.class) {}}.
|
* <p>{@code new Key<Foo>(Bar.class) {}}.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
protected Key(Class<? extends Annotation> annotationType) {
|
protected Key(Class<? extends Annotation> annotationType) {
|
||||||
this.annotationStrategy = strategyFor(annotationType);
|
this.annotationStrategy = strategyFor(annotationType);
|
||||||
this.typeLiteral = MoreTypes.canonicalizeForKey(
|
this.typeLiteral =
|
||||||
|
MoreTypes.canonicalizeForKey(
|
||||||
(TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass()));
|
(TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass()));
|
||||||
this.hashCode = computeHashCode();
|
this.hashCode = computeHashCode();
|
||||||
this.toStringSupplier = createToStringSupplier();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new key. Derives the type from this class's type parameter.
|
* Constructs a new key. Derives the type from this class's type parameter.
|
||||||
*
|
*
|
||||||
* <p>Clients create an empty anonymous subclass. Doing so embeds the type
|
* <p>Clients create an empty anonymous subclass. Doing so embeds the type parameter in the
|
||||||
* parameter in the anonymous class's type hierarchy so we can reconstitute it
|
* anonymous class's type hierarchy so we can reconstitute it at runtime despite erasure.
|
||||||
* at runtime despite erasure.
|
|
||||||
*
|
*
|
||||||
* <p>Example usage for a binding of type {@code Foo} annotated with
|
* <p>Example usage for a binding of type {@code Foo} annotated with {@code @Bar}:
|
||||||
* {@code @Bar}:
|
|
||||||
*
|
*
|
||||||
* <p>{@code new Key<Foo>(new Bar()) {}}.
|
* <p>{@code new Key<Foo>(new Bar()) {}}.
|
||||||
*/
|
*/
|
||||||
|
@ -79,18 +75,17 @@ public class Key<T> {
|
||||||
protected Key(Annotation annotation) {
|
protected Key(Annotation annotation) {
|
||||||
// no usages, not test-covered
|
// no usages, not test-covered
|
||||||
this.annotationStrategy = strategyFor(annotation);
|
this.annotationStrategy = strategyFor(annotation);
|
||||||
this.typeLiteral = MoreTypes.canonicalizeForKey(
|
this.typeLiteral =
|
||||||
|
MoreTypes.canonicalizeForKey(
|
||||||
(TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass()));
|
(TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass()));
|
||||||
this.hashCode = computeHashCode();
|
this.hashCode = computeHashCode();
|
||||||
this.toStringSupplier = createToStringSupplier();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new key. Derives the type from this class's type parameter.
|
* Constructs a new key. Derives the type from this class's type parameter.
|
||||||
*
|
*
|
||||||
* <p>Clients create an empty anonymous subclass. Doing so embeds the type
|
* <p>Clients create an empty anonymous subclass. Doing so embeds the type parameter in the
|
||||||
* parameter in the anonymous class's type hierarchy so we can reconstitute it
|
* anonymous class's type hierarchy so we can reconstitute it at runtime despite erasure.
|
||||||
* at runtime despite erasure.
|
|
||||||
*
|
*
|
||||||
* <p>Example usage for a binding of type {@code Foo}:
|
* <p>Example usage for a binding of type {@code Foo}:
|
||||||
*
|
*
|
||||||
|
@ -99,191 +94,51 @@ public class Key<T> {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
protected Key() {
|
protected Key() {
|
||||||
this.annotationStrategy = NullAnnotationStrategy.INSTANCE;
|
this.annotationStrategy = NullAnnotationStrategy.INSTANCE;
|
||||||
this.typeLiteral = MoreTypes.canonicalizeForKey(
|
this.typeLiteral =
|
||||||
|
MoreTypes.canonicalizeForKey(
|
||||||
(TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass()));
|
(TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass()));
|
||||||
this.hashCode = computeHashCode();
|
this.hashCode = computeHashCode();
|
||||||
this.toStringSupplier = createToStringSupplier();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Unsafe. Constructs a key from a manually specified type. */
|
||||||
* Unsafe. Constructs a key from a manually specified type.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private Key(Type type, AnnotationStrategy annotationStrategy) {
|
private Key(Type type, AnnotationStrategy annotationStrategy) {
|
||||||
this.annotationStrategy = annotationStrategy;
|
this.annotationStrategy = annotationStrategy;
|
||||||
this.typeLiteral = MoreTypes.canonicalizeForKey((TypeLiteral<T>) TypeLiteral.get(type));
|
this.typeLiteral = MoreTypes.canonicalizeForKey((TypeLiteral<T>) TypeLiteral.get(type));
|
||||||
this.hashCode = computeHashCode();
|
this.hashCode = computeHashCode();
|
||||||
this.toStringSupplier = createToStringSupplier();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Constructs a key from a manually specified type. */
|
||||||
* Constructs a key from a manually specified type.
|
|
||||||
*/
|
|
||||||
private Key(TypeLiteral<T> typeLiteral, AnnotationStrategy annotationStrategy) {
|
private Key(TypeLiteral<T> typeLiteral, AnnotationStrategy annotationStrategy) {
|
||||||
this.annotationStrategy = annotationStrategy;
|
this.annotationStrategy = annotationStrategy;
|
||||||
this.typeLiteral = MoreTypes.canonicalizeForKey(typeLiteral);
|
this.typeLiteral = MoreTypes.canonicalizeForKey(typeLiteral);
|
||||||
this.hashCode = computeHashCode();
|
this.hashCode = computeHashCode();
|
||||||
this.toStringSupplier = createToStringSupplier();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Computes the hash code for this key. */
|
||||||
* Gets a key for an injection type and an annotation strategy.
|
|
||||||
*/
|
|
||||||
static <T> Key<T> get(Class<T> type,
|
|
||||||
AnnotationStrategy annotationStrategy) {
|
|
||||||
return new Key<T>(type, annotationStrategy);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a key for an injection type.
|
|
||||||
*/
|
|
||||||
public static <T> Key<T> get(Class<T> type) {
|
|
||||||
return new Key<T>(type, NullAnnotationStrategy.INSTANCE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a key for an injection type and an annotation type.
|
|
||||||
*/
|
|
||||||
public static <T> Key<T> get(Class<T> type,
|
|
||||||
Class<? extends Annotation> annotationType) {
|
|
||||||
return new Key<T>(type, strategyFor(annotationType));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a key for an injection type and an annotation.
|
|
||||||
*/
|
|
||||||
public static <T> Key<T> get(Class<T> type, Annotation annotation) {
|
|
||||||
return new Key<T>(type, strategyFor(annotation));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a key for an injection type.
|
|
||||||
*/
|
|
||||||
public static Key<?> get(Type type) {
|
|
||||||
return new Key<Object>(type, NullAnnotationStrategy.INSTANCE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a key for an injection type and an annotation type.
|
|
||||||
*/
|
|
||||||
public static Key<?> get(Type type,
|
|
||||||
Class<? extends Annotation> annotationType) {
|
|
||||||
return new Key<Object>(type, strategyFor(annotationType));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a key for an injection type and an annotation.
|
|
||||||
*/
|
|
||||||
public static Key<?> get(Type type, Annotation annotation) {
|
|
||||||
return new Key<Object>(type, strategyFor(annotation));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a key for an injection type.
|
|
||||||
*/
|
|
||||||
public static <T> Key<T> get(TypeLiteral<T> typeLiteral) {
|
|
||||||
return new Key<T>(typeLiteral, NullAnnotationStrategy.INSTANCE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a key for an injection type and an annotation type.
|
|
||||||
*/
|
|
||||||
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) {
|
|
||||||
return new Key<T>(typeLiteral, strategyFor(annotation));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the strategy for an annotation.
|
|
||||||
*/
|
|
||||||
static AnnotationStrategy strategyFor(Annotation annotation) {
|
|
||||||
checkNotNull(annotation, "annotation");
|
|
||||||
Class<? extends Annotation> annotationType = annotation.annotationType();
|
|
||||||
ensureRetainedAtRuntime(annotationType);
|
|
||||||
ensureIsBindingAnnotation(annotationType);
|
|
||||||
|
|
||||||
if (Annotations.isMarker(annotationType)) {
|
|
||||||
return new AnnotationTypeStrategy(annotationType, annotation);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new AnnotationInstanceStrategy(Annotations.canonicalizeIfNamed(annotation));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the strategy for an annotation type.
|
|
||||||
*/
|
|
||||||
static AnnotationStrategy strategyFor(Class<? extends Annotation> annotationType) {
|
|
||||||
annotationType = Annotations.canonicalizeIfNamed(annotationType);
|
|
||||||
if (isAllDefaultMethods(annotationType)) {
|
|
||||||
return strategyFor(generateAnnotation(annotationType));
|
|
||||||
}
|
|
||||||
|
|
||||||
checkNotNull(annotationType, "annotation type");
|
|
||||||
ensureRetainedAtRuntime(annotationType);
|
|
||||||
ensureIsBindingAnnotation(annotationType);
|
|
||||||
return new AnnotationTypeStrategy(annotationType, null);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ensureRetainedAtRuntime(
|
|
||||||
Class<? extends Annotation> annotationType) {
|
|
||||||
checkArgument(Annotations.isRetainedAtRuntime(annotationType),
|
|
||||||
"%s is not retained at runtime. Please annotate it with @Retention(RUNTIME).",
|
|
||||||
annotationType.getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ensureIsBindingAnnotation(Class<? extends Annotation> annotationType) {
|
|
||||||
checkArgument(Annotations.isBindingAnnotation(annotationType),
|
|
||||||
"%s is not a binding annotation. Please annotate it with @BindingAnnotation.",
|
|
||||||
annotationType.getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Computes the hash code for this key.
|
|
||||||
*/
|
|
||||||
private int computeHashCode() {
|
private int computeHashCode() {
|
||||||
return typeLiteral.hashCode() * 31 + annotationStrategy.hashCode();
|
return typeLiteral.hashCode() * 31 + annotationStrategy.hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Gets the key type. */
|
||||||
* @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.
|
|
||||||
*/
|
|
||||||
public final TypeLiteral<T> getTypeLiteral() {
|
public final TypeLiteral<T> getTypeLiteral() {
|
||||||
return typeLiteral;
|
return typeLiteral;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Gets the annotation type. Will be {@code null} if this key lacks an annotation. */
|
||||||
* Gets the annotation type.
|
|
||||||
*/
|
|
||||||
public final Class<? extends Annotation> getAnnotationType() {
|
public final Class<? extends Annotation> getAnnotationType() {
|
||||||
return annotationStrategy.getAnnotationType();
|
return annotationStrategy.getAnnotationType();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the annotation.
|
* Gets the annotation instance if available. Will be {@code null} if this key lacks an annotation
|
||||||
|
* <i>or</i> the key was constructed with a {@code Class<Annotation>}.
|
||||||
|
*
|
||||||
|
* <p><b>Warning:</b> this can return null even if this key is annotated. To check whether a
|
||||||
|
* {@code Key} has an annotation use {@link #hasAnnotationType} instead.
|
||||||
*/
|
*/
|
||||||
|
// TODO(diamondm) consider deprecating this in favor of a method that ISEs if hasAnnotationType()
|
||||||
|
// is true but this would return null.
|
||||||
public final Annotation getAnnotation() {
|
public final Annotation getAnnotation() {
|
||||||
return annotationStrategy.getAnnotation();
|
return annotationStrategy.getAnnotation();
|
||||||
}
|
}
|
||||||
|
@ -306,9 +161,7 @@ public class Key<T> {
|
||||||
return typeLiteral.getRawType();
|
return typeLiteral.getRawType();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Gets the key of this key's provider. */
|
||||||
* Gets the key of this key's provider.
|
|
||||||
*/
|
|
||||||
Key<Provider<T>> providerKey() {
|
Key<Provider<T>> providerKey() {
|
||||||
return ofType(typeLiteral.providerType());
|
return ofType(typeLiteral.providerType());
|
||||||
}
|
}
|
||||||
|
@ -333,73 +186,136 @@ public class Key<T> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final String toString() {
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Gets a key for an injection type and an annotation strategy. */
|
||||||
* Returns a new key of the specified type with the same annotation as this
|
static <T> Key<T> get(Class<T> type, AnnotationStrategy annotationStrategy) {
|
||||||
* key.
|
|
||||||
*/
|
|
||||||
public <T> Key<T> ofType(Class<T> type) {
|
|
||||||
return new Key<T>(type, annotationStrategy);
|
return new Key<T>(type, annotationStrategy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Gets a key for an injection type. */
|
||||||
|
public static <T> Key<T> get(Class<T> type) {
|
||||||
|
return new Key<T>(type, NullAnnotationStrategy.INSTANCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gets a key for an injection type and an annotation type. */
|
||||||
|
public static <T> Key<T> get(Class<T> type, Class<? extends Annotation> annotationType) {
|
||||||
|
return new Key<T>(type, strategyFor(annotationType));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gets a key for an injection type and an annotation. */
|
||||||
|
public static <T> Key<T> get(Class<T> type, Annotation annotation) {
|
||||||
|
return new Key<T>(type, strategyFor(annotation));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gets a key for an injection type. */
|
||||||
|
public static Key<?> get(Type type) {
|
||||||
|
return new Key<>(type, NullAnnotationStrategy.INSTANCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gets a key for an injection type and an annotation type. */
|
||||||
|
public static Key<?> get(Type type, Class<? extends Annotation> annotationType) {
|
||||||
|
return new Key<>(type, strategyFor(annotationType));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gets a key for an injection type and an annotation. */
|
||||||
|
public static Key<?> get(Type type, Annotation annotation) {
|
||||||
|
return new Key<>(type, strategyFor(annotation));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gets a key for an injection type. */
|
||||||
|
public static <T> Key<T> get(TypeLiteral<T> typeLiteral) {
|
||||||
|
return new Key<T>(typeLiteral, NullAnnotationStrategy.INSTANCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gets a key for an injection type and an annotation type. */
|
||||||
|
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) {
|
||||||
|
return new Key<T>(typeLiteral, strategyFor(annotation));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a new key of the specified type with the same annotation as this
|
* Returns a new key of the specified type with the same annotation as this key.
|
||||||
* key.
|
*
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
|
public <U> Key<U> ofType(Class<U> type) {
|
||||||
|
return new Key<>(type, annotationStrategy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new key of the specified type with the same annotation as this key.
|
||||||
|
*
|
||||||
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
public Key<?> ofType(Type type) {
|
public Key<?> ofType(Type type) {
|
||||||
return new Key<Object>(type, annotationStrategy);
|
return new Key<>(type, annotationStrategy);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a new key of the specified type with the same annotation as this
|
* Returns a new key of the specified type with the same annotation as this key.
|
||||||
* key.
|
*
|
||||||
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
public <T> Key<T> ofType(TypeLiteral<T> type) {
|
public <U> Key<U> ofType(TypeLiteral<U> type) {
|
||||||
return new Key<T>(type, annotationStrategy);
|
return new Key<U>(type, annotationStrategy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new key of the same type with the specified annotation.
|
||||||
|
*
|
||||||
|
* <p>This is equivalent to {@code Key.get(key.getTypeLiteral(), annotation)} but may be more
|
||||||
|
* convenient to use in certain cases.
|
||||||
|
*
|
||||||
|
* @since 5.0
|
||||||
|
*/
|
||||||
|
public Key<T> withAnnotation(Class<? extends Annotation> annotationType) {
|
||||||
|
return new Key<T>(typeLiteral, strategyFor(annotationType));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new key of the same type with the specified annotation.
|
||||||
|
*
|
||||||
|
* <p>This is equivalent to {@code Key.get(key.getTypeLiteral(), annotation)} but may be more
|
||||||
|
* convenient to use in certain cases.
|
||||||
|
*
|
||||||
|
* @since 5.0
|
||||||
|
*/
|
||||||
|
public Key<T> withAnnotation(Annotation annotation) {
|
||||||
|
return new Key<T>(typeLiteral, strategyFor(annotation));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if this key has annotation attributes.
|
* Returns true if this key has annotation attributes.
|
||||||
|
*
|
||||||
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
public boolean hasAttributes() {
|
public boolean hasAttributes() {
|
||||||
return annotationStrategy.hasAttributes();
|
return annotationStrategy.hasAttributes();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns this key without annotation attributes, i.e. with only the
|
* Returns this key without annotation attributes, i.e. with only the annotation type.
|
||||||
* annotation type.
|
*
|
||||||
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
public Key<T> withoutAttributes() {
|
public Key<T> withoutAttributes() {
|
||||||
return new Key<T>(typeLiteral, annotationStrategy.withoutAttributes());
|
return new Key<T>(typeLiteral, annotationStrategy.withoutAttributes());
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum NullAnnotationStrategy implements AnnotationStrategy {
|
|
||||||
INSTANCE;
|
|
||||||
|
|
||||||
public boolean hasAttributes() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AnnotationStrategy withoutAttributes() {
|
|
||||||
throw new UnsupportedOperationException("Key already has no attributes.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public Annotation getAnnotation() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Class<? extends Annotation> getAnnotationType() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "[none]";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface AnnotationStrategy {
|
interface AnnotationStrategy {
|
||||||
Annotation getAnnotation();
|
Annotation getAnnotation();
|
||||||
|
|
||||||
|
@ -410,6 +326,76 @@ public class Key<T> {
|
||||||
AnnotationStrategy withoutAttributes();
|
AnnotationStrategy withoutAttributes();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Gets the strategy for an annotation. */
|
||||||
|
static AnnotationStrategy strategyFor(Annotation annotation) {
|
||||||
|
checkNotNull(annotation, "annotation");
|
||||||
|
Class<? extends Annotation> annotationType = annotation.annotationType();
|
||||||
|
ensureRetainedAtRuntime(annotationType);
|
||||||
|
ensureIsBindingAnnotation(annotationType);
|
||||||
|
|
||||||
|
if (Annotations.isMarker(annotationType)) {
|
||||||
|
return new AnnotationTypeStrategy(annotationType, annotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new AnnotationInstanceStrategy(Annotations.canonicalizeIfNamed(annotation));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gets the strategy for an annotation type. */
|
||||||
|
static AnnotationStrategy strategyFor(Class<? extends Annotation> annotationType) {
|
||||||
|
annotationType = Annotations.canonicalizeIfNamed(annotationType);
|
||||||
|
if (isAllDefaultMethods(annotationType)) {
|
||||||
|
return strategyFor(generateAnnotation(annotationType));
|
||||||
|
}
|
||||||
|
|
||||||
|
checkNotNull(annotationType, "annotation type");
|
||||||
|
ensureRetainedAtRuntime(annotationType);
|
||||||
|
ensureIsBindingAnnotation(annotationType);
|
||||||
|
return new AnnotationTypeStrategy(annotationType, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ensureRetainedAtRuntime(Class<? extends Annotation> annotationType) {
|
||||||
|
checkArgument(
|
||||||
|
Annotations.isRetainedAtRuntime(annotationType),
|
||||||
|
"%s is not retained at runtime. Please annotate it with @Retention(RUNTIME).",
|
||||||
|
annotationType.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ensureIsBindingAnnotation(Class<? extends Annotation> annotationType) {
|
||||||
|
checkArgument(
|
||||||
|
Annotations.isBindingAnnotation(annotationType),
|
||||||
|
"%s is not a binding annotation. Please annotate it with @BindingAnnotation.",
|
||||||
|
annotationType.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
static 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "[none]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// this class not test-covered
|
// this class not test-covered
|
||||||
static class AnnotationInstanceStrategy implements AnnotationStrategy {
|
static class AnnotationInstanceStrategy implements AnnotationStrategy {
|
||||||
|
|
||||||
|
@ -419,18 +405,22 @@ public class Key<T> {
|
||||||
this.annotation = checkNotNull(annotation, "annotation");
|
this.annotation = checkNotNull(annotation, "annotation");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean hasAttributes() {
|
public boolean hasAttributes() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public AnnotationStrategy withoutAttributes() {
|
public AnnotationStrategy withoutAttributes() {
|
||||||
return new AnnotationTypeStrategy(getAnnotationType(), annotation);
|
return new AnnotationTypeStrategy(getAnnotationType(), annotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Annotation getAnnotation() {
|
public Annotation getAnnotation() {
|
||||||
return annotation;
|
return annotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Class<? extends Annotation> getAnnotationType() {
|
public Class<? extends Annotation> getAnnotationType() {
|
||||||
return annotation.annotationType();
|
return annotation.annotationType();
|
||||||
}
|
}
|
||||||
|
@ -463,24 +453,27 @@ public class Key<T> {
|
||||||
// Keep the instance around if we have it so the client can request it.
|
// Keep the instance around if we have it so the client can request it.
|
||||||
final Annotation annotation;
|
final Annotation annotation;
|
||||||
|
|
||||||
AnnotationTypeStrategy(Class<? extends Annotation> annotationType,
|
AnnotationTypeStrategy(Class<? extends Annotation> annotationType, Annotation annotation) {
|
||||||
Annotation annotation) {
|
|
||||||
this.annotationType = checkNotNull(annotationType, "annotation type");
|
this.annotationType = checkNotNull(annotationType, "annotation type");
|
||||||
this.annotation = annotation;
|
this.annotation = annotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean hasAttributes() {
|
public boolean hasAttributes() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public AnnotationStrategy withoutAttributes() {
|
public AnnotationStrategy withoutAttributes() {
|
||||||
throw new UnsupportedOperationException("Key already has no attributes.");
|
throw new UnsupportedOperationException("Key already has no attributes.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Annotation getAnnotation() {
|
public Annotation getAnnotation() {
|
||||||
return annotation;
|
return annotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Class<? extends Annotation> getAnnotationType() {
|
public Class<? extends Annotation> getAnnotationType() {
|
||||||
return annotationType;
|
return annotationType;
|
||||||
}
|
}
|
||||||
|
|
22
src/main/java/com/google/inject/OutOfScopeException.java
Normal file
22
src/main/java/com/google/inject/OutOfScopeException.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -31,5 +31,5 @@ public interface PrivateBinder extends Binder {
|
||||||
|
|
||||||
PrivateBinder withSource(Object source);
|
PrivateBinder withSource(Object source);
|
||||||
|
|
||||||
PrivateBinder skipSources(Class... classesToSkip);
|
PrivateBinder skipSources(Class<?>... classesToSkip);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package com.google.inject;
|
||||||
|
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
import javax.inject.Provider;
|
||||||
|
|
||||||
import static java.lang.annotation.ElementType.TYPE;
|
import static java.lang.annotation.ElementType.TYPE;
|
||||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
|
|
|
@ -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
|
* @throws ProvisionException if an instance cannot be provided. Such exceptions include messages
|
||||||
* and throwables to describe why provision failed.
|
* and throwables to describe why provision failed.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
T get();
|
T get();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package com.google.inject;
|
package com.google.inject;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
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 com.google.inject.spi.Message;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
@ -23,7 +23,7 @@ public final class ProvisionException extends RuntimeException {
|
||||||
public ProvisionException(Iterable<Message> messages) {
|
public ProvisionException(Iterable<Message> messages) {
|
||||||
this.messages = ImmutableSet.copyOf(messages);
|
this.messages = ImmutableSet.copyOf(messages);
|
||||||
checkArgument(!this.messages.isEmpty());
|
checkArgument(!this.messages.isEmpty());
|
||||||
initCause(Errors.getOnlyCause(this.messages));
|
initCause(Messages.getOnlyCause(this.messages));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProvisionException(String message, Throwable cause) {
|
public ProvisionException(String message, Throwable cause) {
|
||||||
|
@ -44,6 +44,6 @@ public final class ProvisionException extends RuntimeException {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getMessage() {
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
120
src/main/java/com/google/inject/RestrictedBindingSource.java
Normal file
120
src/main/java/com/google/inject/RestrictedBindingSource.java
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
package com.google.inject;
|
||||||
|
|
||||||
|
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
|
||||||
|
import static java.lang.annotation.ElementType.TYPE;
|
||||||
|
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.Inherited;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Annotation restricting the binding of the target type to permitted sources.
|
||||||
|
*
|
||||||
|
* <p>Bindings restricted by this annotation may only be created by sources annotated with a permit
|
||||||
|
* from {@link #permits} -- otherwise, an error message including the {@link #explanation} is
|
||||||
|
* issued.
|
||||||
|
*
|
||||||
|
* <p>There are two kinds of binding source:
|
||||||
|
*
|
||||||
|
* <ol>
|
||||||
|
* <li>Module: a module is the source of a binding if it creates it (either directly, or
|
||||||
|
* indirectly by installing another module). For example: if module A creates restricted
|
||||||
|
* binding X, and module C installs module B that installs A; then all 3 modules C,B,A are
|
||||||
|
* sources of X, and it's enough for any one of them to be annotated with a permit from X's
|
||||||
|
* restriction.
|
||||||
|
* <li>Method Scanner ({@code ModuleAnnotatedMethodScanner}): If a binding was created by a
|
||||||
|
* scanner, then that scanner is also a source of the binding (in addition to the module
|
||||||
|
* sources) and a permit may be given to the scanner by annotating its class.
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* <p>Bindings with qualifier annotations are restricted solely by the annotation on their qualifier
|
||||||
|
* (restrictions on the type are ignored for qualified bindings). Unqualified bindings are
|
||||||
|
* restricted by the annotation on their type.
|
||||||
|
*
|
||||||
|
* <p>This allows libraries to prevent their clients from binding their keys, similar to how
|
||||||
|
* declaring a class final prevents subtyping. For example, a library may want to prevent users from
|
||||||
|
* creating mock bindings for tests, using the {@link #explanation} - included in the error message
|
||||||
|
* - to point them to a supported testing module.
|
||||||
|
*
|
||||||
|
* <p>Example usage:
|
||||||
|
*
|
||||||
|
* <pre>{@code
|
||||||
|
* @RestrictedBindingSource.Permit
|
||||||
|
* @Retention(RetentionPolicy.RUNTIME)
|
||||||
|
* @interface NetworkPermit {}
|
||||||
|
*
|
||||||
|
* @RestrictedBindingSource(
|
||||||
|
* explanation = "Only NetworkModule can create network bindings.",
|
||||||
|
* permits = {NetworkPermit.class})
|
||||||
|
* @Qualifier
|
||||||
|
* @Retention(RetentionPolicy.RUNTIME)
|
||||||
|
* public @interface GatewayIpAdress {}
|
||||||
|
*
|
||||||
|
* @NetworkPermit
|
||||||
|
* public final class NetworkModule extends AbstractModule {
|
||||||
|
* @Provides
|
||||||
|
* @GatewayIpAdress // Allowed because the module is annotated with @NetworkPermit.
|
||||||
|
* int provideGatewayIp() { ... }
|
||||||
|
* }
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* @author vzm@google.com (Vladimir Makaric)
|
||||||
|
* @since 5.0
|
||||||
|
*/
|
||||||
|
@Inherited
|
||||||
|
@Retention(RUNTIME)
|
||||||
|
@Target(TYPE)
|
||||||
|
public @interface RestrictedBindingSource {
|
||||||
|
/**
|
||||||
|
* Explanation of why binding this target type is restricted.
|
||||||
|
*
|
||||||
|
* <p>Will appear as the error message if the target type is bound by non-allowed modules.
|
||||||
|
*/
|
||||||
|
String explanation();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Meta-annotation indicating that the target annotation is a permit for binding restricted
|
||||||
|
* bindings. Annotating a binding source (defined in top-level javadoc) with a permit gives it
|
||||||
|
* permission to bind the restricted bindings guarded by the permit (see {@link #permits}).
|
||||||
|
*
|
||||||
|
* @since 5.0
|
||||||
|
*/
|
||||||
|
@Documented
|
||||||
|
@Retention(RUNTIME)
|
||||||
|
@Target(ANNOTATION_TYPE)
|
||||||
|
public @interface Permit {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of {@code Permit} annotations (must be non-empty), one of which has has to be present on a
|
||||||
|
* restricted binding's source (defined in top-level javadoc).
|
||||||
|
*/
|
||||||
|
Class<? extends Annotation>[] permits();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exempt modules whose fully qualified class names match this regex.
|
||||||
|
*
|
||||||
|
* <p>If any module on the binding's module stack matches this regex, the binding is allowed (no
|
||||||
|
* permit necessary). No module is exempt by default (empty string).
|
||||||
|
*
|
||||||
|
* <p>Inteded to be used when retrofitting a binding with this restriction. When restricting an
|
||||||
|
* existing binding, it's often practical to first restrict with exemptions for existing
|
||||||
|
* violations (to prevent new violations), before updating the code in violation to use the
|
||||||
|
* permitted module(s).
|
||||||
|
*/
|
||||||
|
String exemptModules() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Level of restriction. Determines how violations are handled.
|
||||||
|
*
|
||||||
|
* @since 5.0
|
||||||
|
*/
|
||||||
|
public static enum RestrictionLevel {
|
||||||
|
WARNING,
|
||||||
|
ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
RestrictionLevel restrictionLevel() default RestrictionLevel.ERROR;
|
||||||
|
}
|
|
@ -28,6 +28,7 @@ public class Scopes {
|
||||||
* this to "no scope" in your binding.
|
* this to "no scope" in your binding.
|
||||||
*/
|
*/
|
||||||
public static final Scope NO_SCOPE = new Scope() {
|
public static final Scope NO_SCOPE = new Scope() {
|
||||||
|
@Override
|
||||||
public <T> Provider<T> scope(Key<T> key, Provider<T> unscoped) {
|
public <T> Provider<T> scope(Key<T> key, Provider<T> unscoped) {
|
||||||
return unscoped;
|
return unscoped;
|
||||||
}
|
}
|
||||||
|
@ -37,21 +38,26 @@ public class Scopes {
|
||||||
return "Scopes.NO_SCOPE";
|
return "Scopes.NO_SCOPE";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private static final BindingScopingVisitor<Boolean> IS_SINGLETON_VISITOR
|
private static final BindingScopingVisitor<Boolean> IS_SINGLETON_VISITOR
|
||||||
= new BindingScopingVisitor<Boolean>() {
|
= new BindingScopingVisitor<>() {
|
||||||
|
@Override
|
||||||
public Boolean visitNoScoping() {
|
public Boolean visitNoScoping() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Boolean visitScopeAnnotation(Class<? extends Annotation> scopeAnnotation) {
|
public Boolean visitScopeAnnotation(Class<? extends Annotation> scopeAnnotation) {
|
||||||
return scopeAnnotation == Singleton.class
|
return scopeAnnotation == Singleton.class
|
||||||
|| scopeAnnotation == javax.inject.Singleton.class;
|
|| scopeAnnotation == javax.inject.Singleton.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Boolean visitScope(Scope scope) {
|
public Boolean visitScope(Scope scope) {
|
||||||
return scope == Scopes.SINGLETON;
|
return scope == Scopes.SINGLETON;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Boolean visitEagerSingleton() {
|
public Boolean visitEagerSingleton() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -107,18 +113,22 @@ public class Scopes {
|
||||||
final Class<? extends Annotation> scopeAnnotation) {
|
final Class<? extends Annotation> scopeAnnotation) {
|
||||||
do {
|
do {
|
||||||
boolean matches = binding.acceptScopingVisitor(new BindingScopingVisitor<Boolean>() {
|
boolean matches = binding.acceptScopingVisitor(new BindingScopingVisitor<Boolean>() {
|
||||||
|
@Override
|
||||||
public Boolean visitNoScoping() {
|
public Boolean visitNoScoping() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Boolean visitScopeAnnotation(Class<? extends Annotation> visitedAnnotation) {
|
public Boolean visitScopeAnnotation(Class<? extends Annotation> visitedAnnotation) {
|
||||||
return visitedAnnotation == scopeAnnotation;
|
return visitedAnnotation == scopeAnnotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Boolean visitScope(Scope visitedScope) {
|
public Boolean visitScope(Scope visitedScope) {
|
||||||
return visitedScope == scope;
|
return visitedScope == scope;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Boolean visitEagerSingleton() {
|
public Boolean visitEagerSingleton() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -170,7 +170,7 @@ public class TypeLiteral<T> {
|
||||||
// this implementation is made a little more complicated in an attempt to avoid object-creation
|
// this implementation is made a little more complicated in an attempt to avoid object-creation
|
||||||
while (true) {
|
while (true) {
|
||||||
if (toResolve instanceof TypeVariable) {
|
if (toResolve instanceof TypeVariable) {
|
||||||
TypeVariable original = (TypeVariable) toResolve;
|
TypeVariable<?> original = (TypeVariable) toResolve;
|
||||||
toResolve = MoreTypes.resolveTypeVariable(type, rawType, original);
|
toResolve = MoreTypes.resolveTypeVariable(type, rawType, original);
|
||||||
if (toResolve == original) {
|
if (toResolve == original) {
|
||||||
return toResolve;
|
return toResolve;
|
||||||
|
|
|
@ -5,14 +5,15 @@ import com.google.inject.Key;
|
||||||
import com.google.inject.Module;
|
import com.google.inject.Module;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import com.google.inject.TypeLiteral;
|
import com.google.inject.TypeLiteral;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides a factory that combines the caller's arguments with injector-supplied values to
|
* Provides a factory that combines the caller's arguments with injector-supplied values to
|
||||||
* construct objects.
|
* construct objects.
|
||||||
*
|
*
|
||||||
* <h3>Defining a factory</h3>
|
* <h3>Defining a factory</h3>
|
||||||
|
*
|
||||||
* Create an interface whose methods return the constructed type, or any of its supertypes. The
|
* Create an interface whose methods return the constructed type, or any of its supertypes. The
|
||||||
* method's parameters are the arguments required to build the constructed type.
|
* method's parameters are the arguments required to build the constructed type.
|
||||||
*
|
*
|
||||||
|
@ -24,6 +25,7 @@ import java.lang.annotation.Annotation;
|
||||||
* or <i>newPayment</i>.
|
* or <i>newPayment</i>.
|
||||||
*
|
*
|
||||||
* <h3>Creating a type that accepts factory parameters</h3>
|
* <h3>Creating a type that accepts factory parameters</h3>
|
||||||
|
*
|
||||||
* {@code constructedType} is a concrete class with an {@literal @}{@link com.google.inject.Inject
|
* {@code constructedType} is a concrete class with an {@literal @}{@link com.google.inject.Inject
|
||||||
* Inject}-annotated constructor. In addition to injector-supplied parameters, the constructor
|
* Inject}-annotated constructor. In addition to injector-supplied parameters, the constructor
|
||||||
* should have parameters that match each of the factory method's parameters. Each factory-supplied
|
* should have parameters that match each of the factory method's parameters. Each factory-supplied
|
||||||
|
@ -42,10 +44,10 @@ import java.lang.annotation.Annotation;
|
||||||
* }</pre>
|
* }</pre>
|
||||||
*
|
*
|
||||||
* <h3>Multiple factory methods for the same type</h3>
|
* <h3>Multiple factory methods for the same type</h3>
|
||||||
|
*
|
||||||
* If the factory contains many methods that return the same type, you can create multiple
|
* If the factory contains many methods that return the same type, you can create multiple
|
||||||
* constructors in your concrete class, each constructor marked with with
|
* constructors in your concrete class, each constructor marked with with {@literal @}{@link
|
||||||
* {@literal @}{@link AssistedInject}, in order to match the different parameters types of the
|
* AssistedInject}, in order to match the different parameters types of the factory methods.
|
||||||
* factory methods.
|
|
||||||
*
|
*
|
||||||
* <pre>public interface PaymentFactory {
|
* <pre>public interface PaymentFactory {
|
||||||
* Payment create(Date startDate, Money amount);
|
* Payment create(Date startDate, Money amount);
|
||||||
|
@ -72,8 +74,8 @@ import java.lang.annotation.Annotation;
|
||||||
* }</pre>
|
* }</pre>
|
||||||
*
|
*
|
||||||
* <h3>Configuring simple factories</h3>
|
* <h3>Configuring simple factories</h3>
|
||||||
* In your {@link Module module}, install a {@code FactoryModuleBuilder} that creates the
|
*
|
||||||
* factory:
|
* In your {@link Module module}, install a {@code FactoryModuleBuilder} that creates the factory:
|
||||||
*
|
*
|
||||||
* <pre>install(new FactoryModuleBuilder()
|
* <pre>install(new FactoryModuleBuilder()
|
||||||
* .implement(Payment.class, RealPayment.class)
|
* .implement(Payment.class, RealPayment.class)
|
||||||
|
@ -83,8 +85,9 @@ import java.lang.annotation.Annotation;
|
||||||
* factory cannot be used until the injector has been initialized.
|
* factory cannot be used until the injector has been initialized.
|
||||||
*
|
*
|
||||||
* <h3>Configuring complex factories</h3>
|
* <h3>Configuring complex factories</h3>
|
||||||
* Factories can create an arbitrary number of objects, one per each method. Each factory
|
*
|
||||||
* method can be configured using <code>.implement</code>.
|
* Factories can create an arbitrary number of objects, one per each method. Each factory method can
|
||||||
|
* be configured using <code>.implement</code>.
|
||||||
*
|
*
|
||||||
* <pre>public interface OrderFactory {
|
* <pre>public interface OrderFactory {
|
||||||
* Payment create(Date startDate, Money amount);
|
* Payment create(Date startDate, Money amount);
|
||||||
|
@ -100,11 +103,13 @@ import java.lang.annotation.Annotation;
|
||||||
* // will be 'Shipment' itself, which is legal if it's not an interface.
|
* // will be 'Shipment' itself, which is legal if it's not an interface.
|
||||||
* .implement(Receipt.class, RealReceipt.class)
|
* .implement(Receipt.class, RealReceipt.class)
|
||||||
* .build(OrderFactory.class));</pre>
|
* .build(OrderFactory.class));</pre>
|
||||||
|
*
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* <h3>Using the factory</h3>
|
* <h3>Using the factory</h3>
|
||||||
* Inject your factory into your application classes. When you use the factory, your arguments
|
*
|
||||||
* will be combined with values from the injector to construct an instance.
|
* Inject your factory into your application classes. When you use the factory, your arguments will
|
||||||
|
* be combined with values from the injector to construct an instance.
|
||||||
*
|
*
|
||||||
* <pre>public class PaymentAction {
|
* <pre>public class PaymentAction {
|
||||||
* {@literal @}Inject private PaymentFactory paymentFactory;
|
* {@literal @}Inject private PaymentFactory paymentFactory;
|
||||||
|
@ -116,9 +121,10 @@ import java.lang.annotation.Annotation;
|
||||||
* }</pre>
|
* }</pre>
|
||||||
*
|
*
|
||||||
* <h3>Making parameter types distinct</h3>
|
* <h3>Making parameter types distinct</h3>
|
||||||
* The types of the factory method's parameters must be distinct. To use multiple parameters of
|
*
|
||||||
* the same type, use a named {@literal @}{@link Assisted} annotation to disambiguate the
|
* The types of the factory method's parameters must be distinct. To use multiple parameters of the
|
||||||
* parameters. The names must be applied to the factory method's parameters:
|
* same type, use a named {@literal @}{@link Assisted} annotation to disambiguate the parameters.
|
||||||
|
* The names must be applied to the factory method's parameters:
|
||||||
*
|
*
|
||||||
* <pre>public interface PaymentFactory {
|
* <pre>public interface PaymentFactory {
|
||||||
* Payment create(
|
* Payment create(
|
||||||
|
@ -142,15 +148,17 @@ import java.lang.annotation.Annotation;
|
||||||
* }</pre>
|
* }</pre>
|
||||||
*
|
*
|
||||||
* <h3>Values are created by Guice</h3>
|
* <h3>Values are created by Guice</h3>
|
||||||
|
*
|
||||||
* Returned factories use child injectors to create values. The values are eligible for method
|
* Returned factories use child injectors to create values. The values are eligible for method
|
||||||
* interception. In addition, {@literal @}{@literal Inject} members will be injected before they are
|
* interception. In addition, {@literal @}{@literal Inject} members will be injected before they are
|
||||||
* returned.
|
* returned.
|
||||||
*
|
*
|
||||||
* <h3>More configuration options</h3>
|
* <h3>More configuration options</h3>
|
||||||
|
*
|
||||||
* In addition to simply specifying an implementation class for any returned type, factories' return
|
* In addition to simply specifying an implementation class for any returned type, factories' return
|
||||||
* values can be automatic or can be configured to use annotations:
|
* values can be automatic or can be configured to use annotations:
|
||||||
* <p/>
|
*
|
||||||
* If you just want to return the types specified in the factory, do not configure any
|
* <p>If you just want to return the types specified in the factory, do not configure any
|
||||||
* implementations:
|
* implementations:
|
||||||
*
|
*
|
||||||
* <pre>public interface FruitFactory {
|
* <pre>public interface FruitFactory {
|
||||||
|
@ -162,8 +170,8 @@ import java.lang.annotation.Annotation;
|
||||||
* }</pre>
|
* }</pre>
|
||||||
*
|
*
|
||||||
* Note that any type returned by the factory in this manner needs to be an implementation class.
|
* Note that any type returned by the factory in this manner needs to be an implementation class.
|
||||||
* <p/>
|
*
|
||||||
* To return two different implementations for the same interface from your factory, use binding
|
* <p>To return two different implementations for the same interface from your factory, use binding
|
||||||
* annotations on your return types:
|
* annotations on your return types:
|
||||||
*
|
*
|
||||||
* <pre>interface CarFactory {
|
* <pre>interface CarFactory {
|
||||||
|
@ -179,142 +187,138 @@ import java.lang.annotation.Annotation;
|
||||||
* }</pre>
|
* }</pre>
|
||||||
*
|
*
|
||||||
* <h3>Implementation limitations</h3>
|
* <h3>Implementation limitations</h3>
|
||||||
* As a limitation of the implementation, it is prohibited to declare a factory method that
|
*
|
||||||
* accepts a {@code Provider} as one of its arguments.
|
* As a limitation of the implementation, it is prohibited to declare a factory method that accepts
|
||||||
|
* a {@code Provider} as one of its arguments.
|
||||||
|
*
|
||||||
|
* @since 3.0
|
||||||
|
* @author schmitt@google.com (Peter Schmitt)
|
||||||
*/
|
*/
|
||||||
public final class FactoryModuleBuilder {
|
public final class FactoryModuleBuilder {
|
||||||
|
|
||||||
private final BindingCollector bindings = new BindingCollector();
|
private final BindingCollector bindings = new BindingCollector();
|
||||||
|
private MethodHandles.Lookup lookups;
|
||||||
|
|
||||||
/**
|
/** See the factory configuration examples at {@link FactoryModuleBuilder}. */
|
||||||
* See the factory configuration examples at {@link FactoryModuleBuilder}.
|
|
||||||
*/
|
|
||||||
public <T> FactoryModuleBuilder implement(Class<T> source, Class<? extends T> target) {
|
public <T> FactoryModuleBuilder implement(Class<T> source, Class<? extends T> target) {
|
||||||
return implement(source, TypeLiteral.get(target));
|
return implement(source, TypeLiteral.get(target));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** See the factory configuration examples at {@link FactoryModuleBuilder}. */
|
||||||
* See the factory configuration examples at {@link FactoryModuleBuilder}.
|
|
||||||
*/
|
|
||||||
public <T> FactoryModuleBuilder implement(Class<T> source, TypeLiteral<? extends T> target) {
|
public <T> FactoryModuleBuilder implement(Class<T> source, TypeLiteral<? extends T> target) {
|
||||||
return implement(TypeLiteral.get(source), target);
|
return implement(TypeLiteral.get(source), target);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** See the factory configuration examples at {@link FactoryModuleBuilder}. */
|
||||||
* See the factory configuration examples at {@link FactoryModuleBuilder}.
|
|
||||||
*/
|
|
||||||
public <T> FactoryModuleBuilder implement(TypeLiteral<T> source, Class<? extends T> target) {
|
public <T> FactoryModuleBuilder implement(TypeLiteral<T> source, Class<? extends T> target) {
|
||||||
return implement(source, TypeLiteral.get(target));
|
return implement(source, TypeLiteral.get(target));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** See the factory configuration examples at {@link FactoryModuleBuilder}. */
|
||||||
* See the factory configuration examples at {@link FactoryModuleBuilder}.
|
public <T> FactoryModuleBuilder implement(
|
||||||
*/
|
TypeLiteral<T> source, TypeLiteral<? extends T> target) {
|
||||||
public <T> FactoryModuleBuilder implement(TypeLiteral<T> source,
|
|
||||||
TypeLiteral<? extends T> target) {
|
|
||||||
return implement(Key.get(source), target);
|
return implement(Key.get(source), target);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** See the factory configuration examples at {@link FactoryModuleBuilder}. */
|
||||||
* See the factory configuration examples at {@link FactoryModuleBuilder}.
|
public <T> FactoryModuleBuilder implement(
|
||||||
*/
|
Class<T> source, Annotation annotation, Class<? extends T> target) {
|
||||||
public <T> FactoryModuleBuilder implement(Class<T> source, Annotation annotation,
|
|
||||||
Class<? extends T> target) {
|
|
||||||
return implement(source, annotation, TypeLiteral.get(target));
|
return implement(source, annotation, TypeLiteral.get(target));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** See the factory configuration examples at {@link FactoryModuleBuilder}. */
|
||||||
* See the factory configuration examples at {@link FactoryModuleBuilder}.
|
public <T> FactoryModuleBuilder implement(
|
||||||
*/
|
Class<T> source, Annotation annotation, TypeLiteral<? extends T> target) {
|
||||||
public <T> FactoryModuleBuilder implement(Class<T> source, Annotation annotation,
|
|
||||||
TypeLiteral<? extends T> target) {
|
|
||||||
return implement(TypeLiteral.get(source), annotation, target);
|
return implement(TypeLiteral.get(source), annotation, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** See the factory configuration examples at {@link FactoryModuleBuilder}. */
|
||||||
* See the factory configuration examples at {@link FactoryModuleBuilder}.
|
public <T> FactoryModuleBuilder implement(
|
||||||
*/
|
TypeLiteral<T> source, Annotation annotation, Class<? extends T> target) {
|
||||||
public <T> FactoryModuleBuilder implement(TypeLiteral<T> source, Annotation annotation,
|
|
||||||
Class<? extends T> target) {
|
|
||||||
return implement(source, annotation, TypeLiteral.get(target));
|
return implement(source, annotation, TypeLiteral.get(target));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** See the factory configuration examples at {@link FactoryModuleBuilder}. */
|
||||||
* See the factory configuration examples at {@link FactoryModuleBuilder}.
|
public <T> FactoryModuleBuilder implement(
|
||||||
*/
|
TypeLiteral<T> source, Annotation annotation, TypeLiteral<? extends T> target) {
|
||||||
public <T> FactoryModuleBuilder implement(TypeLiteral<T> source, Annotation annotation,
|
|
||||||
TypeLiteral<? extends T> target) {
|
|
||||||
return implement(Key.get(source, annotation), target);
|
return implement(Key.get(source, annotation), target);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** See the factory configuration examples at {@link FactoryModuleBuilder}. */
|
||||||
* See the factory configuration examples at {@link FactoryModuleBuilder}.
|
public <T> FactoryModuleBuilder implement(
|
||||||
*/
|
Class<T> source, Class<? extends Annotation> annotationType, Class<? extends T> target) {
|
||||||
public <T> FactoryModuleBuilder implement(Class<T> source,
|
|
||||||
Class<? extends Annotation> annotationType, Class<? extends T> target) {
|
|
||||||
return implement(source, annotationType, TypeLiteral.get(target));
|
return implement(source, annotationType, TypeLiteral.get(target));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** See the factory configuration examples at {@link FactoryModuleBuilder}. */
|
||||||
* See the factory configuration examples at {@link FactoryModuleBuilder}.
|
public <T> FactoryModuleBuilder implement(
|
||||||
*/
|
Class<T> source,
|
||||||
public <T> FactoryModuleBuilder implement(Class<T> source,
|
Class<? extends Annotation> annotationType,
|
||||||
Class<? extends Annotation> annotationType, TypeLiteral<? extends T> target) {
|
TypeLiteral<? extends T> target) {
|
||||||
return implement(TypeLiteral.get(source), annotationType, target);
|
return implement(TypeLiteral.get(source), annotationType, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** See the factory configuration examples at {@link FactoryModuleBuilder}. */
|
||||||
* See the factory configuration examples at {@link FactoryModuleBuilder}.
|
public <T> FactoryModuleBuilder implement(
|
||||||
*/
|
TypeLiteral<T> source,
|
||||||
public <T> FactoryModuleBuilder implement(TypeLiteral<T> source,
|
Class<? extends Annotation> annotationType,
|
||||||
Class<? extends Annotation> annotationType, Class<? extends T> target) {
|
Class<? extends T> target) {
|
||||||
return implement(source, annotationType, TypeLiteral.get(target));
|
return implement(source, annotationType, TypeLiteral.get(target));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** See the factory configuration examples at {@link FactoryModuleBuilder}. */
|
||||||
* See the factory configuration examples at {@link FactoryModuleBuilder}.
|
public <T> FactoryModuleBuilder implement(
|
||||||
*/
|
TypeLiteral<T> source,
|
||||||
public <T> FactoryModuleBuilder implement(TypeLiteral<T> source,
|
Class<? extends Annotation> annotationType,
|
||||||
Class<? extends Annotation> annotationType, TypeLiteral<? extends T> target) {
|
TypeLiteral<? extends T> target) {
|
||||||
return implement(Key.get(source, annotationType), target);
|
return implement(Key.get(source, annotationType), target);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** See the factory configuration examples at {@link FactoryModuleBuilder}. */
|
||||||
* See the factory configuration examples at {@link FactoryModuleBuilder}.
|
|
||||||
*/
|
|
||||||
public <T> FactoryModuleBuilder implement(Key<T> source, Class<? extends T> target) {
|
public <T> FactoryModuleBuilder implement(Key<T> source, Class<? extends T> target) {
|
||||||
return implement(source, TypeLiteral.get(target));
|
return implement(source, TypeLiteral.get(target));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** See the factory configuration examples at {@link FactoryModuleBuilder}. */
|
||||||
* See the factory configuration examples at {@link FactoryModuleBuilder}.
|
|
||||||
*/
|
|
||||||
public <T> FactoryModuleBuilder implement(Key<T> source, TypeLiteral<? extends T> target) {
|
public <T> FactoryModuleBuilder implement(Key<T> source, TypeLiteral<? extends T> target) {
|
||||||
bindings.addBinding(source, target);
|
bindings.addBinding(source, target);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See the factory configuration examples at {@link FactoryModuleBuilder}.
|
* Typically called via {@code withLookups(MethodHandles.lookup())}. Sets the MethodHandles.Lookup
|
||||||
|
* that the factory implementation will use to call default methods on the factory interface.
|
||||||
|
* While this is not always required, it is always OK to set it. It is required if the factory
|
||||||
|
* passed to {@link #build} is non-public and javac generated default methods while compiling it
|
||||||
|
* (which javac can sometimes do if the factory uses generic types).
|
||||||
|
*
|
||||||
|
* <p>Guice will try to work properly even if this method is not called (or called with a lookups
|
||||||
|
* that doesn't have access to the factory), but doing so requires reflection into the JDK, which
|
||||||
|
* may break at any time (and trigger unsafe access warnings).
|
||||||
|
*
|
||||||
|
* @since 5.0
|
||||||
*/
|
*/
|
||||||
|
public <T> FactoryModuleBuilder withLookups(MethodHandles.Lookup lookups) {
|
||||||
|
this.lookups = lookups;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** See the factory configuration examples at {@link FactoryModuleBuilder}. */
|
||||||
public <F> Module build(Class<F> factoryInterface) {
|
public <F> Module build(Class<F> factoryInterface) {
|
||||||
return build(TypeLiteral.get(factoryInterface));
|
return build(TypeLiteral.get(factoryInterface));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** See the factory configuration examples at {@link FactoryModuleBuilder}. */
|
||||||
* See the factory configuration examples at {@link FactoryModuleBuilder}.
|
|
||||||
*/
|
|
||||||
public <F> Module build(TypeLiteral<F> factoryInterface) {
|
public <F> Module build(TypeLiteral<F> factoryInterface) {
|
||||||
return build(Key.get(factoryInterface));
|
return build(Key.get(factoryInterface));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public <F> Module build(final Key<F> factoryInterface) {
|
public <F> Module build(final Key<F> factoryInterface) {
|
||||||
return new AbstractModule() {
|
return new AbstractModule() {
|
||||||
@Override
|
@Override
|
||||||
protected void configure() {
|
protected void configure() {
|
||||||
Provider<F> provider = new FactoryProvider2<F>(factoryInterface, bindings);
|
Provider<F> provider = new FactoryProvider2<>(factoryInterface, bindings, lookups);
|
||||||
bind(factoryInterface).toProvider(provider);
|
binder().skipSources(this.getClass()).bind(factoryInterface).toProvider(provider);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package com.google.inject.assistedinject;
|
package com.google.inject.assistedinject;
|
||||||
|
|
||||||
|
import static com.google.inject.internal.Annotations.getKey;
|
||||||
|
|
||||||
import com.google.common.base.Objects;
|
import com.google.common.base.Objects;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
@ -16,7 +18,6 @@ import com.google.inject.internal.ErrorsException;
|
||||||
import com.google.inject.spi.Dependency;
|
import com.google.inject.spi.Dependency;
|
||||||
import com.google.inject.spi.HasDependencies;
|
import com.google.inject.spi.HasDependencies;
|
||||||
import com.google.inject.spi.Message;
|
import com.google.inject.spi.Message;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.InvocationHandler;
|
import java.lang.reflect.InvocationHandler;
|
||||||
|
@ -27,8 +28,6 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import static com.google.inject.internal.Annotations.getKey;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <strong>Obsolete.</strong> Prefer {@link FactoryModuleBuilder} for its more concise API and
|
* <strong>Obsolete.</strong> Prefer {@link FactoryModuleBuilder} for its more concise API and
|
||||||
* additional capability.
|
* additional capability.
|
||||||
|
@ -37,20 +36,25 @@ import static com.google.inject.internal.Annotations.getKey;
|
||||||
* construct objects.
|
* construct objects.
|
||||||
*
|
*
|
||||||
* <h3>Defining a factory</h3>
|
* <h3>Defining a factory</h3>
|
||||||
|
*
|
||||||
* Create an interface whose methods return the constructed type, or any of its supertypes. The
|
* Create an interface whose methods return the constructed type, or any of its supertypes. The
|
||||||
* method's parameters are the arguments required to build the constructed type.
|
* method's parameters are the arguments required to build the constructed type.
|
||||||
|
*
|
||||||
* <pre>public interface PaymentFactory {
|
* <pre>public interface PaymentFactory {
|
||||||
* Payment create(Date startDate, Money amount);
|
* Payment create(Date startDate, Money amount);
|
||||||
* }</pre>
|
* }</pre>
|
||||||
|
*
|
||||||
* You can name your factory methods whatever you like, such as <i>create</i>, <i>createPayment</i>
|
* You can name your factory methods whatever you like, such as <i>create</i>, <i>createPayment</i>
|
||||||
* or <i>newPayment</i>.
|
* or <i>newPayment</i>.
|
||||||
*
|
*
|
||||||
* <h3>Creating a type that accepts factory parameters</h3>
|
* <h3>Creating a type that accepts factory parameters</h3>
|
||||||
|
*
|
||||||
* {@code constructedType} is a concrete class with an {@literal @}{@link Inject}-annotated
|
* {@code constructedType} is a concrete class with an {@literal @}{@link Inject}-annotated
|
||||||
* constructor. In addition to injector-supplied parameters, the constructor should have
|
* constructor. In addition to injector-supplied parameters, the constructor should have parameters
|
||||||
* parameters that match each of the factory method's parameters. Each factory-supplied parameter
|
* that match each of the factory method's parameters. Each factory-supplied parameter requires an
|
||||||
* requires an {@literal @}{@link Assisted} annotation. This serves to document that the parameter
|
* {@literal @}{@link Assisted} annotation. This serves to document that the parameter is not bound
|
||||||
* is not bound by your application's modules.
|
* by your application's modules.
|
||||||
|
*
|
||||||
* <pre>public class RealPayment implements Payment {
|
* <pre>public class RealPayment implements Payment {
|
||||||
* {@literal @}Inject
|
* {@literal @}Inject
|
||||||
* public RealPayment(
|
* public RealPayment(
|
||||||
|
@ -61,19 +65,25 @@ import static com.google.inject.internal.Annotations.getKey;
|
||||||
* ...
|
* ...
|
||||||
* }
|
* }
|
||||||
* }</pre>
|
* }</pre>
|
||||||
|
*
|
||||||
* Any parameter that permits a null value should also be annotated {@code @Nullable}.
|
* Any parameter that permits a null value should also be annotated {@code @Nullable}.
|
||||||
*
|
*
|
||||||
* <h3>Configuring factories</h3>
|
* <h3>Configuring factories</h3>
|
||||||
|
*
|
||||||
* In your {@link com.google.inject.Module module}, bind the factory interface to the returned
|
* In your {@link com.google.inject.Module module}, bind the factory interface to the returned
|
||||||
* factory:
|
* factory:
|
||||||
|
*
|
||||||
* <pre>bind(PaymentFactory.class).toProvider(
|
* <pre>bind(PaymentFactory.class).toProvider(
|
||||||
* FactoryProvider.newFactory(PaymentFactory.class, RealPayment.class));</pre>
|
* FactoryProvider.newFactory(PaymentFactory.class, RealPayment.class));</pre>
|
||||||
|
*
|
||||||
* As a side-effect of this binding, Guice will inject the factory to initialize it for use. The
|
* As a side-effect of this binding, Guice will inject the factory to initialize it for use. The
|
||||||
* factory cannot be used until the injector has been initialized.
|
* factory cannot be used until the injector has been initialized.
|
||||||
*
|
*
|
||||||
* <h3>Using the factory</h3>
|
* <h3>Using the factory</h3>
|
||||||
* Inject your factory into your application classes. When you use the factory, your arguments
|
*
|
||||||
* will be combined with values from the injector to construct an instance.
|
* Inject your factory into your application classes. When you use the factory, your arguments will
|
||||||
|
* be combined with values from the injector to construct an instance.
|
||||||
|
*
|
||||||
* <pre>public class PaymentAction {
|
* <pre>public class PaymentAction {
|
||||||
* {@literal @}Inject private PaymentFactory paymentFactory;
|
* {@literal @}Inject private PaymentFactory paymentFactory;
|
||||||
*
|
*
|
||||||
|
@ -84,9 +94,10 @@ import static com.google.inject.internal.Annotations.getKey;
|
||||||
* }</pre>
|
* }</pre>
|
||||||
*
|
*
|
||||||
* <h3>Making parameter types distinct</h3>
|
* <h3>Making parameter types distinct</h3>
|
||||||
* The types of the factory method's parameters must be distinct. To use multiple parameters of
|
*
|
||||||
* the same type, use a named {@literal @}{@link Assisted} annotation to disambiguate the
|
* The types of the factory method's parameters must be distinct. To use multiple parameters of the
|
||||||
* parameters. The names must be applied to the factory method's parameters:
|
* same type, use a named {@literal @}{@link Assisted} annotation to disambiguate the parameters.
|
||||||
|
* The names must be applied to the factory method's parameters:
|
||||||
*
|
*
|
||||||
* <pre>public interface PaymentFactory {
|
* <pre>public interface PaymentFactory {
|
||||||
* Payment create(
|
* Payment create(
|
||||||
|
@ -94,7 +105,9 @@ import static com.google.inject.internal.Annotations.getKey;
|
||||||
* <strong>{@literal @}Assisted("dueDate")</strong> Date dueDate,
|
* <strong>{@literal @}Assisted("dueDate")</strong> Date dueDate,
|
||||||
* Money amount);
|
* Money amount);
|
||||||
* } </pre>
|
* } </pre>
|
||||||
|
*
|
||||||
* ...and to the concrete type's constructor parameters:
|
* ...and to the concrete type's constructor parameters:
|
||||||
|
*
|
||||||
* <pre>public class RealPayment implements Payment {
|
* <pre>public class RealPayment implements Payment {
|
||||||
* {@literal @}Inject
|
* {@literal @}Inject
|
||||||
* public RealPayment(
|
* public RealPayment(
|
||||||
|
@ -108,23 +121,27 @@ import static com.google.inject.internal.Annotations.getKey;
|
||||||
* }</pre>
|
* }</pre>
|
||||||
*
|
*
|
||||||
* <h3>Values are created by Guice</h3>
|
* <h3>Values are created by Guice</h3>
|
||||||
|
*
|
||||||
* Returned factories use child injectors to create values. The values are eligible for method
|
* Returned factories use child injectors to create values. The values are eligible for method
|
||||||
* interception. In addition, {@literal @}{@literal Inject} members will be injected before they are
|
* interception. In addition, {@literal @}{@literal Inject} members will be injected before they are
|
||||||
* returned.
|
* returned.
|
||||||
*
|
*
|
||||||
* <h3>Backwards compatibility using {@literal @}AssistedInject</h3>
|
* <h3>Backwards compatibility using {@literal @}AssistedInject</h3>
|
||||||
|
*
|
||||||
* Instead of the {@literal @}Inject annotation, you may annotate the constructed classes with
|
* Instead of the {@literal @}Inject annotation, you may annotate the constructed classes with
|
||||||
* {@literal @}{@link AssistedInject}. This triggers a limited backwards-compatability mode.
|
* {@literal @}{@link AssistedInject}. This triggers a limited backwards-compatability mode.
|
||||||
*
|
*
|
||||||
* <p>Instead of matching factory method arguments to constructor parameters using their names, the
|
* <p>Instead of matching factory method arguments to constructor parameters using their names, the
|
||||||
* <strong>parameters are matched by their order</strong>. The first factory method argument is
|
* <strong>parameters are matched by their order</strong>. The first factory method argument is used
|
||||||
* used for the first {@literal @}Assisted constructor parameter, etc.. Annotation names have no
|
* for the first {@literal @}Assisted constructor parameter, etc.. Annotation names have no effect.
|
||||||
* effect.
|
|
||||||
*
|
*
|
||||||
* <p>Returned values are <strong>not created by Guice</strong>. These types are not eligible for
|
* <p>Returned values are <strong>not created by Guice</strong>. These types are not eligible for
|
||||||
* method interception. They do receive post-construction member injection.
|
* method interception. They do receive post-construction member injection.
|
||||||
*
|
*
|
||||||
* @param <F> The factory interface
|
* @param <F> The factory interface
|
||||||
|
* @author jmourits@google.com (Jerome Mourits)
|
||||||
|
* @author jessewilson@google.com (Jesse Wilson)
|
||||||
|
* @author dtm@google.com (Daniel Martin)
|
||||||
* @deprecated use {@link FactoryModuleBuilder} instead.
|
* @deprecated use {@link FactoryModuleBuilder} instead.
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
|
@ -135,19 +152,11 @@ public class FactoryProvider<F> implements Provider<F>, HasDependencies {
|
||||||
* to factory methods. The new child injector implementation lives in FactoryProvider2.
|
* to factory methods. The new child injector implementation lives in FactoryProvider2.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
private Injector injector;
|
||||||
|
|
||||||
private final TypeLiteral<F> factoryType;
|
private final TypeLiteral<F> factoryType;
|
||||||
private final TypeLiteral<?> implementationType;
|
private final TypeLiteral<?> implementationType;
|
||||||
private final Map<Method, AssistedConstructor<?>> factoryMethodToConstructor;
|
private final Map<Method, AssistedConstructor<?>> factoryMethodToConstructor;
|
||||||
private Injector injector;
|
|
||||||
|
|
||||||
private FactoryProvider(TypeLiteral<F> factoryType,
|
|
||||||
TypeLiteral<?> implementationType,
|
|
||||||
Map<Method, AssistedConstructor<?>> factoryMethodToConstructor) {
|
|
||||||
this.factoryType = factoryType;
|
|
||||||
this.implementationType = implementationType;
|
|
||||||
this.factoryMethodToConstructor = factoryMethodToConstructor;
|
|
||||||
checkDeclaredExceptionsMatch();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <F> Provider<F> newFactory(Class<F> factoryType, Class<?> implementationType) {
|
public static <F> Provider<F> newFactory(Class<F> factoryType, Class<?> implementationType) {
|
||||||
return newFactory(TypeLiteral.get(factoryType), TypeLiteral.get(implementationType));
|
return newFactory(TypeLiteral.get(factoryType), TypeLiteral.get(implementationType));
|
||||||
|
@ -155,8 +164,8 @@ public class FactoryProvider<F> implements Provider<F>, HasDependencies {
|
||||||
|
|
||||||
public static <F> Provider<F> newFactory(
|
public static <F> Provider<F> newFactory(
|
||||||
TypeLiteral<F> factoryType, TypeLiteral<?> implementationType) {
|
TypeLiteral<F> factoryType, TypeLiteral<?> implementationType) {
|
||||||
Map<Method, AssistedConstructor<?>> factoryMethodToConstructor
|
Map<Method, AssistedConstructor<?>> factoryMethodToConstructor =
|
||||||
= createMethodMapping(factoryType, implementationType);
|
createMethodMapping(factoryType, implementationType);
|
||||||
|
|
||||||
if (!factoryMethodToConstructor.isEmpty()) {
|
if (!factoryMethodToConstructor.isEmpty()) {
|
||||||
return new FactoryProvider<F>(factoryType, implementationType, factoryMethodToConstructor);
|
return new FactoryProvider<F>(factoryType, implementationType, factoryMethodToConstructor);
|
||||||
|
@ -170,8 +179,8 @@ public class FactoryProvider<F> implements Provider<F>, HasDependencies {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (Method method : factoryType.getRawType().getMethods()) {
|
for (Method method : factoryType.getRawType().getMethods()) {
|
||||||
Key<?> returnType = getKey(factoryType.getReturnType(method), method,
|
Key<?> returnType =
|
||||||
method.getAnnotations(), errors);
|
getKey(factoryType.getReturnType(method), method, method.getAnnotations(), errors);
|
||||||
if (!implementationKey.equals(returnType)) {
|
if (!implementationKey.equals(returnType)) {
|
||||||
collector.addBinding(returnType, implementationType);
|
collector.addBinding(returnType, implementationType);
|
||||||
}
|
}
|
||||||
|
@ -180,83 +189,18 @@ public class FactoryProvider<F> implements Provider<F>, HasDependencies {
|
||||||
throw new ConfigurationException(e.getErrors().getMessages());
|
throw new ConfigurationException(e.getErrors().getMessages());
|
||||||
}
|
}
|
||||||
|
|
||||||
return new FactoryProvider2<F>(Key.get(factoryType), collector);
|
return new FactoryProvider2<F>(Key.get(factoryType), collector, /* userLookups= */ null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Map<Method, AssistedConstructor<?>> createMethodMapping(
|
private FactoryProvider(
|
||||||
TypeLiteral<?> factoryType, TypeLiteral<?> implementationType) {
|
TypeLiteral<F> factoryType,
|
||||||
List<AssistedConstructor<?>> constructors = Lists.newArrayList();
|
TypeLiteral<?> implementationType,
|
||||||
|
Map<Method, AssistedConstructor<?>> factoryMethodToConstructor) {
|
||||||
for (Constructor<?> constructor : implementationType.getRawType().getDeclaredConstructors()) {
|
this.factoryType = factoryType;
|
||||||
if (constructor.isAnnotationPresent(AssistedInject.class)) {
|
this.implementationType = implementationType;
|
||||||
AssistedConstructor<?> assistedConstructor = AssistedConstructor.create(
|
this.factoryMethodToConstructor = factoryMethodToConstructor;
|
||||||
constructor, implementationType.getParameterTypes(constructor));
|
checkDeclaredExceptionsMatch();
|
||||||
constructors.add(assistedConstructor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (constructors.isEmpty()) {
|
|
||||||
return ImmutableMap.of();
|
|
||||||
}
|
|
||||||
|
|
||||||
Method[] factoryMethods = factoryType.getRawType().getMethods();
|
|
||||||
|
|
||||||
if (constructors.size() != factoryMethods.length) {
|
|
||||||
throw newConfigurationException("Constructor mismatch: %s has %s @AssistedInject "
|
|
||||||
+ "constructors, factory %s has %s creation methods", implementationType,
|
|
||||||
constructors.size(), factoryType, factoryMethods.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<ParameterListKey, AssistedConstructor<?>> paramsToConstructor = Maps.newHashMap();
|
|
||||||
|
|
||||||
for (AssistedConstructor<?> c : constructors) {
|
|
||||||
if (paramsToConstructor.containsKey(c.getAssistedParameters())) {
|
|
||||||
throw new RuntimeException("Duplicate constructor, " + c);
|
|
||||||
}
|
|
||||||
paramsToConstructor.put(c.getAssistedParameters(), c);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<Method, AssistedConstructor<?>> result = Maps.newHashMap();
|
|
||||||
for (Method method : factoryMethods) {
|
|
||||||
if (!method.getReturnType().isAssignableFrom(implementationType.getRawType())) {
|
|
||||||
throw newConfigurationException("Return type of method %s is not assignable from %s",
|
|
||||||
method, implementationType);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Type> parameterTypes = Lists.newArrayList();
|
|
||||||
for (TypeLiteral<?> parameterType : factoryType.getParameterTypes(method)) {
|
|
||||||
parameterTypes.add(parameterType.getType());
|
|
||||||
}
|
|
||||||
ParameterListKey methodParams = new ParameterListKey(parameterTypes);
|
|
||||||
|
|
||||||
if (!paramsToConstructor.containsKey(methodParams)) {
|
|
||||||
throw newConfigurationException("%s has no @AssistInject constructor that takes the "
|
|
||||||
+ "@Assisted parameters %s in that order. @AssistInject constructors are %s",
|
|
||||||
implementationType, methodParams, paramsToConstructor.values());
|
|
||||||
}
|
|
||||||
|
|
||||||
method.getParameterAnnotations();
|
|
||||||
for (Annotation[] parameterAnnotations : method.getParameterAnnotations()) {
|
|
||||||
for (Annotation parameterAnnotation : parameterAnnotations) {
|
|
||||||
if (parameterAnnotation.annotationType() == Assisted.class) {
|
|
||||||
throw newConfigurationException("Factory method %s has an @Assisted parameter, which "
|
|
||||||
+ "is incompatible with the deprecated @AssistedInject annotation. Please replace "
|
|
||||||
+ "@AssistedInject with @Inject on the %s constructor.",
|
|
||||||
method, implementationType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AssistedConstructor<?> matchingConstructor = paramsToConstructor.remove(methodParams);
|
|
||||||
|
|
||||||
result.put(method, matchingConstructor);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ConfigurationException newConfigurationException(String format, Object... args) {
|
|
||||||
return new ConfigurationException(ImmutableSet.of(new Message(Errors.format(format, args))));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
|
@ -268,8 +212,10 @@ public class FactoryProvider<F> implements Provider<F>, HasDependencies {
|
||||||
// this is lame - we're not using the proper mechanism to add an
|
// this is lame - we're not using the proper mechanism to add an
|
||||||
// error to the injector. Throughout this class we throw exceptions
|
// error to the injector. Throughout this class we throw exceptions
|
||||||
// to add errors, which isn't really the best way in Guice
|
// to add errors, which isn't really the best way in Guice
|
||||||
throw newConfigurationException("Parameter of type '%s' is not injectable or annotated "
|
throw newConfigurationException(
|
||||||
+ "with @Assisted for Constructor '%s'", p, c);
|
"Parameter of type '%s' is not injectable or annotated "
|
||||||
|
+ "with @Assisted for Constructor '%s'",
|
||||||
|
p, c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -280,8 +226,10 @@ public class FactoryProvider<F> implements Provider<F>, HasDependencies {
|
||||||
for (Class<?> constructorException : entry.getValue().getDeclaredExceptions()) {
|
for (Class<?> constructorException : entry.getValue().getDeclaredExceptions()) {
|
||||||
if (!isConstructorExceptionCompatibleWithFactoryExeception(
|
if (!isConstructorExceptionCompatibleWithFactoryExeception(
|
||||||
constructorException, entry.getKey().getExceptionTypes())) {
|
constructorException, entry.getKey().getExceptionTypes())) {
|
||||||
throw newConfigurationException("Constructor %s declares an exception, but no compatible "
|
throw newConfigurationException(
|
||||||
+ "exception is thrown by the factory method %s", entry.getValue(), entry.getKey());
|
"Constructor %s declares an exception, but no compatible "
|
||||||
|
+ "exception is thrown by the factory method %s",
|
||||||
|
entry.getValue(), entry.getKey());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -301,6 +249,82 @@ public class FactoryProvider<F> implements Provider<F>, HasDependencies {
|
||||||
return parameter.isBound(injector);
|
return parameter.isBound(injector);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Map<Method, AssistedConstructor<?>> createMethodMapping(
|
||||||
|
TypeLiteral<?> factoryType, TypeLiteral<?> implementationType) {
|
||||||
|
List<AssistedConstructor<?>> constructors = Lists.newArrayList();
|
||||||
|
|
||||||
|
for (Constructor<?> constructor : implementationType.getRawType().getDeclaredConstructors()) {
|
||||||
|
if (constructor.isAnnotationPresent(AssistedInject.class)) {
|
||||||
|
AssistedConstructor<?> assistedConstructor =
|
||||||
|
AssistedConstructor.create(
|
||||||
|
constructor, implementationType.getParameterTypes(constructor));
|
||||||
|
constructors.add(assistedConstructor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (constructors.isEmpty()) {
|
||||||
|
return ImmutableMap.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
Method[] factoryMethods = factoryType.getRawType().getMethods();
|
||||||
|
|
||||||
|
if (constructors.size() != factoryMethods.length) {
|
||||||
|
throw newConfigurationException(
|
||||||
|
"Constructor mismatch: %s has %s @AssistedInject "
|
||||||
|
+ "constructors, factory %s has %s creation methods",
|
||||||
|
implementationType, constructors.size(), factoryType, factoryMethods.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<ParameterListKey, AssistedConstructor<?>> paramsToConstructor = Maps.newHashMap();
|
||||||
|
|
||||||
|
for (AssistedConstructor<?> c : constructors) {
|
||||||
|
if (paramsToConstructor.containsKey(c.getAssistedParameters())) {
|
||||||
|
throw new RuntimeException("Duplicate constructor, " + c);
|
||||||
|
}
|
||||||
|
paramsToConstructor.put(c.getAssistedParameters(), c);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<Method, AssistedConstructor<?>> result = Maps.newHashMap();
|
||||||
|
for (Method method : factoryMethods) {
|
||||||
|
if (!method.getReturnType().isAssignableFrom(implementationType.getRawType())) {
|
||||||
|
throw newConfigurationException(
|
||||||
|
"Return type of method %s is not assignable from %s", method, implementationType);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Type> parameterTypes = Lists.newArrayList();
|
||||||
|
for (TypeLiteral<?> parameterType : factoryType.getParameterTypes(method)) {
|
||||||
|
parameterTypes.add(parameterType.getType());
|
||||||
|
}
|
||||||
|
ParameterListKey methodParams = new ParameterListKey(parameterTypes);
|
||||||
|
|
||||||
|
if (!paramsToConstructor.containsKey(methodParams)) {
|
||||||
|
throw newConfigurationException(
|
||||||
|
"%s has no @AssistInject constructor that takes the "
|
||||||
|
+ "@Assisted parameters %s in that order. @AssistInject constructors are %s",
|
||||||
|
implementationType, methodParams, paramsToConstructor.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
method.getParameterAnnotations();
|
||||||
|
for (Annotation[] parameterAnnotations : method.getParameterAnnotations()) {
|
||||||
|
for (Annotation parameterAnnotation : parameterAnnotations) {
|
||||||
|
if (parameterAnnotation.annotationType() == Assisted.class) {
|
||||||
|
throw newConfigurationException(
|
||||||
|
"Factory method %s has an @Assisted parameter, which is incompatible with the"
|
||||||
|
+ " deprecated @AssistedInject annotation. Please replace @AssistedInject with"
|
||||||
|
+ " @Inject on the %s constructor.",
|
||||||
|
method, implementationType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AssistedConstructor<?> matchingConstructor = paramsToConstructor.remove(methodParams);
|
||||||
|
|
||||||
|
result.put(method, matchingConstructor);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Set<Dependency<?>> getDependencies() {
|
public Set<Dependency<?>> getDependencies() {
|
||||||
List<Dependency<?>> dependencies = Lists.newArrayList();
|
List<Dependency<?>> dependencies = Lists.newArrayList();
|
||||||
for (AssistedConstructor<?> constructor : factoryMethodToConstructor.values()) {
|
for (AssistedConstructor<?> constructor : factoryMethodToConstructor.values()) {
|
||||||
|
@ -313,9 +337,13 @@ public class FactoryProvider<F> implements Provider<F>, HasDependencies {
|
||||||
return ImmutableSet.copyOf(dependencies);
|
return ImmutableSet.copyOf(dependencies);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public F get() {
|
public F get() {
|
||||||
InvocationHandler invocationHandler = new InvocationHandler() {
|
InvocationHandler invocationHandler =
|
||||||
public Object invoke(Object proxy, Method method, Object[] creationArgs) throws Throwable {
|
new InvocationHandler() {
|
||||||
|
@Override
|
||||||
|
public Object invoke(Object proxy, Method method, Object[] creationArgs)
|
||||||
|
throws Throwable {
|
||||||
// pass methods from Object.class to the proxy
|
// pass methods from Object.class to the proxy
|
||||||
if (method.getDeclaringClass().equals(Object.class)) {
|
if (method.getDeclaringClass().equals(Object.class)) {
|
||||||
if ("equals".equals(method.getName())) {
|
if ("equals".equals(method.getName())) {
|
||||||
|
@ -335,8 +363,7 @@ public class FactoryProvider<F> implements Provider<F>, HasDependencies {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object[] gatherArgsForConstructor(
|
public Object[] gatherArgsForConstructor(
|
||||||
AssistedConstructor<?> constructor,
|
AssistedConstructor<?> constructor, Object[] factoryArgs) {
|
||||||
Object[] factoryArgs) {
|
|
||||||
int numParams = constructor.getAllParameters().size();
|
int numParams = constructor.getAllParameters().size();
|
||||||
int argPosition = 0;
|
int argPosition = 0;
|
||||||
Object[] result = new Object[numParams];
|
Object[] result = new Object[numParams];
|
||||||
|
@ -356,8 +383,9 @@ public class FactoryProvider<F> implements Provider<F>, HasDependencies {
|
||||||
|
|
||||||
@SuppressWarnings("unchecked") // we imprecisely treat the class literal of T as a Class<T>
|
@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>) (Class<?>) factoryType.getRawType();
|
||||||
return factoryRawType.cast(Proxy.newProxyInstance(factoryRawType.getClassLoader(),
|
return factoryRawType.cast(
|
||||||
new Class[]{factoryRawType}, invocationHandler));
|
Proxy.newProxyInstance(
|
||||||
|
factoryRawType.getClassLoader(), new Class<?>[] {factoryRawType}, invocationHandler));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -374,4 +402,8 @@ public class FactoryProvider<F> implements Provider<F>, HasDependencies {
|
||||||
return factoryType.equals(other.factoryType)
|
return factoryType.equals(other.factoryType)
|
||||||
&& implementationType.equals(other.implementationType);
|
&& implementationType.equals(other.implementationType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ConfigurationException newConfigurationException(String format, Object... args) {
|
||||||
|
return new ConfigurationException(ImmutableSet.of(new Message(Errors.format(format, args))));
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,9 @@
|
||||||
package com.google.inject.assistedinject;
|
package com.google.inject.assistedinject;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkState;
|
||||||
|
import static com.google.common.collect.Iterables.getOnlyElement;
|
||||||
|
|
||||||
|
import com.google.common.base.MoreObjects;
|
||||||
import com.google.common.base.Objects;
|
import com.google.common.base.Objects;
|
||||||
import com.google.common.collect.HashMultimap;
|
import com.google.common.collect.HashMultimap;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
@ -35,11 +39,12 @@ import com.google.inject.spi.ProviderInstanceBinding;
|
||||||
import com.google.inject.spi.ProviderWithExtensionVisitor;
|
import com.google.inject.spi.ProviderWithExtensionVisitor;
|
||||||
import com.google.inject.spi.Toolable;
|
import com.google.inject.spi.Toolable;
|
||||||
import com.google.inject.util.Providers;
|
import com.google.inject.util.Providers;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.invoke.MethodHandle;
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
|
import java.lang.invoke.MethodType;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.InvocationHandler;
|
import java.lang.reflect.InvocationHandler;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.lang.reflect.Proxy;
|
import java.lang.reflect.Proxy;
|
||||||
|
@ -50,35 +55,46 @@ import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkState;
|
|
||||||
import static com.google.common.collect.Iterables.getOnlyElement;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The newer implementation of factory provider. This implementation uses a child injector to
|
* The newer implementation of factory provider. This implementation uses a child injector to create
|
||||||
* create values.
|
* values.
|
||||||
|
*
|
||||||
|
* @author jessewilson@google.com (Jesse Wilson)
|
||||||
|
* @author dtm@google.com (Daniel Martin)
|
||||||
|
* @author schmitt@google.com (Peter Schmitt)
|
||||||
|
* @author sameb@google.com (Sam Berlin)
|
||||||
*/
|
*/
|
||||||
final class FactoryProvider2<F> implements InvocationHandler,
|
final class FactoryProvider2<F>
|
||||||
ProviderWithExtensionVisitor<F>, HasDependencies, AssistedInjectBinding<F> {
|
implements InvocationHandler,
|
||||||
|
ProviderWithExtensionVisitor<F>,
|
||||||
|
HasDependencies,
|
||||||
|
AssistedInjectBinding<F> {
|
||||||
|
|
||||||
/**
|
/** A constant annotation to denote the return value, instead of creating a new one each time. */
|
||||||
* A constant annotation to denote the return value, instead of creating a new one each time.
|
|
||||||
*/
|
|
||||||
static final Annotation RETURN_ANNOTATION = UniqueAnnotations.create();
|
static final Annotation RETURN_ANNOTATION = UniqueAnnotations.create();
|
||||||
|
|
||||||
// use the logger under a well-known name, not FactoryProvider2
|
// use the logger under a well-known name, not FactoryProvider2
|
||||||
static final Logger logger = Logger.getLogger(AssistedInject.class.getName());
|
static final Logger logger = Logger.getLogger(AssistedInject.class.getName());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* if a factory method parameter isn't annotated, it gets this annotation.
|
* A constant that determines if we allow fallback to reflection. Typically always true, but
|
||||||
|
* reflectively set to false in tests.
|
||||||
*/
|
*/
|
||||||
static final Assisted DEFAULT_ANNOTATION = new Assisted() {
|
private static boolean allowLookupReflection = true;
|
||||||
|
|
||||||
|
/** if a factory method parameter isn't annotated, it gets this annotation. */
|
||||||
|
static final Assisted DEFAULT_ANNOTATION =
|
||||||
|
new Assisted() {
|
||||||
|
@Override
|
||||||
public String value() {
|
public String value() {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Class<? extends Annotation> annotationType() {
|
public Class<? extends Annotation> annotationType() {
|
||||||
return Assisted.class;
|
return Assisted.class;
|
||||||
}
|
}
|
||||||
|
@ -95,40 +111,117 @@ final class FactoryProvider2<F> implements InvocationHandler,
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "@" + Assisted.class.getName() + "(value=)";
|
return "@"
|
||||||
|
+ Assisted.class.getName()
|
||||||
|
+ "("
|
||||||
|
+ Annotations.memberValueString("value", "")
|
||||||
|
+ ")";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
/**
|
|
||||||
* Mapping from method to the data about how the method will be assisted.
|
/** All the data necessary to perform an assisted inject. */
|
||||||
*/
|
private static class AssistData implements AssistedMethod {
|
||||||
|
/** the constructor the implementation is constructed with. */
|
||||||
|
final Constructor<?> constructor;
|
||||||
|
/** the return type in the factory method that the constructor is bound to. */
|
||||||
|
final Key<?> returnType;
|
||||||
|
/** the parameters in the factory method associated with this data. */
|
||||||
|
final ImmutableList<Key<?>> paramTypes;
|
||||||
|
/** the type of the implementation constructed */
|
||||||
|
final TypeLiteral<?> implementationType;
|
||||||
|
|
||||||
|
/** All non-assisted dependencies required by this method. */
|
||||||
|
final Set<Dependency<?>> dependencies;
|
||||||
|
/** The factory method associated with this data */
|
||||||
|
final Method factoryMethod;
|
||||||
|
|
||||||
|
/** true if {@link #isValidForOptimizedAssistedInject} returned true. */
|
||||||
|
final boolean optimized;
|
||||||
|
/** the list of optimized providers, empty if not optimized. */
|
||||||
|
final List<ThreadLocalProvider> providers;
|
||||||
|
/** used to perform optimized factory creations. */
|
||||||
|
volatile Binding<?> cachedBinding; // TODO: volatile necessary?
|
||||||
|
|
||||||
|
AssistData(
|
||||||
|
Constructor<?> constructor,
|
||||||
|
Key<?> returnType,
|
||||||
|
ImmutableList<Key<?>> paramTypes,
|
||||||
|
TypeLiteral<?> implementationType,
|
||||||
|
Method factoryMethod,
|
||||||
|
Set<Dependency<?>> dependencies,
|
||||||
|
boolean optimized,
|
||||||
|
List<ThreadLocalProvider> providers) {
|
||||||
|
this.constructor = constructor;
|
||||||
|
this.returnType = returnType;
|
||||||
|
this.paramTypes = paramTypes;
|
||||||
|
this.implementationType = implementationType;
|
||||||
|
this.factoryMethod = factoryMethod;
|
||||||
|
this.dependencies = dependencies;
|
||||||
|
this.optimized = optimized;
|
||||||
|
this.providers = providers;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return MoreObjects.toStringHelper(getClass())
|
||||||
|
.add("ctor", constructor)
|
||||||
|
.add("return type", returnType)
|
||||||
|
.add("param type", paramTypes)
|
||||||
|
.add("implementation type", implementationType)
|
||||||
|
.add("dependencies", dependencies)
|
||||||
|
.add("factory method", factoryMethod)
|
||||||
|
.add("optimized", optimized)
|
||||||
|
.add("providers", providers)
|
||||||
|
.add("cached binding", cachedBinding)
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Dependency<?>> getDependencies() {
|
||||||
|
return dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Method getFactoryMethod() {
|
||||||
|
return factoryMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Constructor<?> getImplementationConstructor() {
|
||||||
|
return constructor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TypeLiteral<?> getImplementationType() {
|
||||||
|
return implementationType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Mapping from method to the data about how the method will be assisted. */
|
||||||
private final ImmutableMap<Method, AssistData> assistDataByMethod;
|
private final ImmutableMap<Method, AssistData> assistDataByMethod;
|
||||||
/**
|
|
||||||
* Mapping from method to method handle, for generated default methods.
|
/** Mapping from method to method handle, for generated default methods. */
|
||||||
*/
|
private final ImmutableMap<Method, MethodHandle> methodHandleByMethod;
|
||||||
private final ImmutableMap<Method, MethodHandleWrapper> methodHandleByMethod;
|
|
||||||
/**
|
/** the hosting injector, or null if we haven't been initialized yet */
|
||||||
* the factory interface, implemented and provided
|
|
||||||
*/
|
|
||||||
private final F factory;
|
|
||||||
/**
|
|
||||||
* The key that this is bound to.
|
|
||||||
*/
|
|
||||||
private final Key<F> factoryKey;
|
|
||||||
/**
|
|
||||||
* The binding collector, for equality/hashing purposes.
|
|
||||||
*/
|
|
||||||
private final BindingCollector collector;
|
|
||||||
/**
|
|
||||||
* the hosting injector, or null if we haven't been initialized yet
|
|
||||||
*/
|
|
||||||
private Injector injector;
|
private Injector injector;
|
||||||
|
|
||||||
|
/** the factory interface, implemented and provided */
|
||||||
|
private final F factory;
|
||||||
|
|
||||||
|
/** The key that this is bound to. */
|
||||||
|
private final Key<F> factoryKey;
|
||||||
|
|
||||||
|
/** The binding collector, for equality/hashing purposes. */
|
||||||
|
private final BindingCollector collector;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param factoryKey a key for a Java interface that defines one or more create methods.
|
* @param factoryKey a key for a Java interface that defines one or more create methods.
|
||||||
* @param collector binding configuration that maps method return types to
|
* @param collector binding configuration that maps method return types to implementation types.
|
||||||
* implementation types.
|
* @param userLookups user provided lookups, optional.
|
||||||
*/
|
*/
|
||||||
FactoryProvider2(Key<F> factoryKey, BindingCollector collector) {
|
FactoryProvider2(
|
||||||
|
Key<F> factoryKey, BindingCollector collector, MethodHandles.Lookup userLookups) {
|
||||||
this.factoryKey = factoryKey;
|
this.factoryKey = factoryKey;
|
||||||
this.collector = collector;
|
this.collector = collector;
|
||||||
|
|
||||||
|
@ -148,6 +241,11 @@ final class FactoryProvider2<F> implements InvocationHandler,
|
||||||
ImmutableMap.Builder<Method, AssistData> assistDataBuilder = ImmutableMap.builder();
|
ImmutableMap.Builder<Method, AssistData> assistDataBuilder = ImmutableMap.builder();
|
||||||
// TODO: also grab methods from superinterfaces
|
// TODO: also grab methods from superinterfaces
|
||||||
for (Method method : factoryRawType.getMethods()) {
|
for (Method method : factoryRawType.getMethods()) {
|
||||||
|
// Skip static methods
|
||||||
|
if (Modifier.isStatic(method.getModifiers())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Skip default methods that java8 may have created.
|
// Skip default methods that java8 may have created.
|
||||||
if (isDefault(method) && (method.isBridge() || method.isSynthetic())) {
|
if (isDefault(method) && (method.isBridge() || method.isSynthetic())) {
|
||||||
// Even synthetic default methods need the return type validation...
|
// Even synthetic default methods need the return type validation...
|
||||||
|
@ -161,7 +259,8 @@ final class FactoryProvider2<F> implements InvocationHandler,
|
||||||
TypeLiteral<?> returnTypeLiteral = factoryType.getReturnType(method);
|
TypeLiteral<?> returnTypeLiteral = factoryType.getReturnType(method);
|
||||||
Key<?> returnType;
|
Key<?> returnType;
|
||||||
try {
|
try {
|
||||||
returnType = Annotations.getKey(returnTypeLiteral, method, method.getAnnotations(), errors);
|
returnType =
|
||||||
|
Annotations.getKey(returnTypeLiteral, method, method.getAnnotations(), errors);
|
||||||
} catch (ConfigurationException ce) {
|
} catch (ConfigurationException ce) {
|
||||||
// If this was an error due to returnTypeLiteral not being specified, rephrase
|
// If this was an error due to returnTypeLiteral not being specified, rephrase
|
||||||
// it as our factory not being specified, so it makes more sense to users.
|
// it as our factory not being specified, so it makes more sense to users.
|
||||||
|
@ -181,7 +280,8 @@ final class FactoryProvider2<F> implements InvocationHandler,
|
||||||
Class<?> underlylingType = paramKey.getTypeLiteral().getRawType();
|
Class<?> underlylingType = paramKey.getTypeLiteral().getRawType();
|
||||||
if (underlylingType.equals(Provider.class)
|
if (underlylingType.equals(Provider.class)
|
||||||
|| underlylingType.equals(javax.inject.Provider.class)) {
|
|| underlylingType.equals(javax.inject.Provider.class)) {
|
||||||
errors.addMessage("A Provider may not be a type in a factory method of an AssistedInject."
|
errors.addMessage(
|
||||||
|
"A Provider may not be a type in a factory method of an AssistedInject."
|
||||||
+ "\n Offending instance is parameter [%s] with key [%s] on method [%s]",
|
+ "\n Offending instance is parameter [%s] with key [%s] on method [%s]",
|
||||||
p, paramKey, method);
|
p, paramKey, method);
|
||||||
}
|
}
|
||||||
|
@ -197,7 +297,8 @@ final class FactoryProvider2<F> implements InvocationHandler,
|
||||||
Class<? extends Annotation> scope =
|
Class<? extends Annotation> scope =
|
||||||
Annotations.findScopeAnnotation(errors, implementation.getRawType());
|
Annotations.findScopeAnnotation(errors, implementation.getRawType());
|
||||||
if (scope != null) {
|
if (scope != null) {
|
||||||
errors.addMessage("Found scope annotation [%s] on implementation class "
|
errors.addMessage(
|
||||||
|
"Found scope annotation [%s] on implementation class "
|
||||||
+ "[%s] of AssistedInject factory [%s].\nThis is not allowed, please"
|
+ "[%s] of AssistedInject factory [%s].\nThis is not allowed, please"
|
||||||
+ " remove the scope annotation.",
|
+ " remove the scope annotation.",
|
||||||
scope, implementation.getRawType(), factoryType);
|
scope, implementation.getRawType(), factoryType);
|
||||||
|
@ -206,7 +307,8 @@ final class FactoryProvider2<F> implements InvocationHandler,
|
||||||
InjectionPoint ctorInjectionPoint;
|
InjectionPoint ctorInjectionPoint;
|
||||||
try {
|
try {
|
||||||
ctorInjectionPoint =
|
ctorInjectionPoint =
|
||||||
findMatchingConstructorInjectionPoint(method, returnType, implementation, immutableParamList);
|
findMatchingConstructorInjectionPoint(
|
||||||
|
method, returnType, implementation, immutableParamList);
|
||||||
} catch (ErrorsException ee) {
|
} catch (ErrorsException ee) {
|
||||||
errors.merge(ee.getErrors());
|
errors.merge(ee.getErrors());
|
||||||
continue;
|
continue;
|
||||||
|
@ -230,7 +332,9 @@ final class FactoryProvider2<F> implements InvocationHandler,
|
||||||
optimized = true;
|
optimized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
AssistData data = new AssistData(constructor,
|
AssistData data =
|
||||||
|
new AssistData(
|
||||||
|
constructor,
|
||||||
returnType,
|
returnType,
|
||||||
immutableParamList,
|
immutableParamList,
|
||||||
implementation,
|
implementation,
|
||||||
|
@ -241,25 +345,61 @@ final class FactoryProvider2<F> implements InvocationHandler,
|
||||||
assistDataBuilder.put(method, data);
|
assistDataBuilder.put(method, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
factory = factoryRawType.cast(Proxy.newProxyInstance(
|
factory =
|
||||||
|
factoryRawType.cast(
|
||||||
|
Proxy.newProxyInstance(
|
||||||
factoryRawType.getClassLoader(), new Class<?>[] {factoryRawType}, this));
|
factoryRawType.getClassLoader(), new Class<?>[] {factoryRawType}, this));
|
||||||
|
|
||||||
// Now go back through default methods. Try to use MethodHandles to make things
|
// Now go back through default methods. Try to use MethodHandles to make things
|
||||||
// work. If that doesn't work, fallback to trying to find compatible method
|
// work. If that doesn't work, fallback to trying to find compatible method
|
||||||
// signatures.
|
// signatures.
|
||||||
Map<Method, AssistData> dataSoFar = assistDataBuilder.build();
|
Map<Method, AssistData> dataSoFar = assistDataBuilder.build();
|
||||||
ImmutableMap.Builder<Method, MethodHandleWrapper> methodHandleBuilder = ImmutableMap.builder();
|
ImmutableMap.Builder<Method, MethodHandle> methodHandleBuilder = ImmutableMap.builder();
|
||||||
|
boolean warnedAboutUserLookups = false;
|
||||||
for (Map.Entry<String, Method> entry : defaultMethods.entries()) {
|
for (Map.Entry<String, Method> entry : defaultMethods.entries()) {
|
||||||
|
if (!warnedAboutUserLookups
|
||||||
|
&& userLookups == null
|
||||||
|
&& !Modifier.isPublic(factory.getClass().getModifiers())) {
|
||||||
|
warnedAboutUserLookups = true;
|
||||||
|
logger.log(
|
||||||
|
Level.WARNING,
|
||||||
|
"AssistedInject factory {0} is non-public and has javac-generated default methods. "
|
||||||
|
+ " Please pass a `MethodHandles.lookups()` with"
|
||||||
|
+ " FactoryModuleBuilder.withLookups when using this factory so that Guice can"
|
||||||
|
+ " properly call the default methods. Guice will try to work around the"
|
||||||
|
+ " problem, but doing so requires reflection into the JDK and may break at any"
|
||||||
|
+ " time.",
|
||||||
|
new Object[] {factoryType});
|
||||||
|
}
|
||||||
|
|
||||||
Method defaultMethod = entry.getValue();
|
Method defaultMethod = entry.getValue();
|
||||||
MethodHandleWrapper handle = MethodHandleWrapper.create(defaultMethod, factory);
|
MethodHandle handle = null;
|
||||||
|
try {
|
||||||
|
// Note: this can return null if we fallback to reflecting the private lookup cxtor and it
|
||||||
|
// fails. In that case, we try the super hacky workaround below (w/ 'foundMatch').
|
||||||
|
// It can throw an exception if we're not doing private reflection, or if unreflectSpecial
|
||||||
|
// _still_ fails.
|
||||||
|
handle = superMethodHandle(defaultMethod, factory, userLookups);
|
||||||
|
} catch (ReflectiveOperationException roe) {
|
||||||
|
errors.addMessage(
|
||||||
|
new Message(
|
||||||
|
"Unable to use factory "
|
||||||
|
+ factoryRawType.getName()
|
||||||
|
+ ". Did you call FactoryModuleBuilder.withLookups(MethodHandles.lookups())"
|
||||||
|
+ " (with a lookups that has access to the factory)?"));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (handle != null) {
|
if (handle != null) {
|
||||||
methodHandleBuilder.put(defaultMethod, handle);
|
methodHandleBuilder.put(defaultMethod, handle);
|
||||||
} else {
|
} else {
|
||||||
|
// TODO: remove this workaround when Java8 support is dropped
|
||||||
boolean foundMatch = false;
|
boolean foundMatch = false;
|
||||||
for (Method otherMethod : otherMethods.get(defaultMethod.getName())) {
|
for (Method otherMethod : otherMethods.get(defaultMethod.getName())) {
|
||||||
if (dataSoFar.containsKey(otherMethod) && isCompatible(defaultMethod, otherMethod)) {
|
if (dataSoFar.containsKey(otherMethod) && isCompatible(defaultMethod, otherMethod)) {
|
||||||
if (foundMatch) {
|
if (foundMatch) {
|
||||||
errors.addMessage("Generated default method %s with parameters %s is"
|
errors.addMessage(
|
||||||
|
"Generated default method %s with parameters %s is"
|
||||||
+ " signature-compatible with more than one non-default method."
|
+ " signature-compatible with more than one non-default method."
|
||||||
+ " Unable to create factory. As a workaround, remove the override"
|
+ " Unable to create factory. As a workaround, remove the override"
|
||||||
+ " so javac stops generating a default method.",
|
+ " so javac stops generating a default method.",
|
||||||
|
@ -276,7 +416,8 @@ final class FactoryProvider2<F> implements InvocationHandler,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we generated any errors (from finding matching constructors, for instance), throw an exception.
|
// If we generated any errors (from finding matching constructors, for instance), throw an
|
||||||
|
// exception.
|
||||||
if (errors.hasErrors()) {
|
if (errors.hasErrors()) {
|
||||||
throw errors.toException();
|
throw errors.toException();
|
||||||
}
|
}
|
||||||
|
@ -296,23 +437,6 @@ final class FactoryProvider2<F> implements InvocationHandler,
|
||||||
== Modifier.PUBLIC;
|
== Modifier.PUBLIC;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if {@code thrown} can be thrown by {@code invoked} without wrapping.
|
|
||||||
*/
|
|
||||||
static boolean canRethrow(Method invoked, Throwable thrown) {
|
|
||||||
if (thrown instanceof Error || thrown instanceof RuntimeException) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Class<?> declared : invoked.getExceptionTypes()) {
|
|
||||||
if (declared.isInstance(thrown)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isCompatible(Method src, Method dst) {
|
private boolean isCompatible(Method src, Method dst) {
|
||||||
if (!src.getReturnType().isAssignableFrom(dst.getReturnType())) {
|
if (!src.getReturnType().isAssignableFrom(dst.getReturnType())) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -330,32 +454,37 @@ final class FactoryProvider2<F> implements InvocationHandler,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public F get() {
|
public F get() {
|
||||||
return factory;
|
return factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Set<Dependency<?>> getDependencies() {
|
public Set<Dependency<?>> getDependencies() {
|
||||||
Set<Dependency<?>> combinedDeps = new HashSet<Dependency<?>>();
|
Set<Dependency<?>> combinedDeps = new HashSet<>();
|
||||||
for (AssistData data : assistDataByMethod.values()) {
|
for (AssistData data : assistDataByMethod.values()) {
|
||||||
combinedDeps.addAll(data.dependencies);
|
combinedDeps.addAll(data.dependencies);
|
||||||
}
|
}
|
||||||
return ImmutableSet.copyOf(combinedDeps);
|
return ImmutableSet.copyOf(combinedDeps);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Key<F> getKey() {
|
public Key<F> getKey() {
|
||||||
return factoryKey;
|
return factoryKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Safe cast because values are typed to AssistedData, which is an AssistedMethod, and
|
// Safe cast because values are typed to AssistedData, which is an AssistedMethod, and
|
||||||
// the collection is immutable.
|
// the collection is immutable.
|
||||||
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public Collection<AssistedMethod> getAssistedMethods() {
|
public Collection<AssistedMethod> getAssistedMethods() {
|
||||||
return (Collection<AssistedMethod>) (Collection<?>) assistDataByMethod.values();
|
return (Collection<AssistedMethod>) (Collection<?>) assistDataByMethod.values();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public <T, V> V acceptExtensionVisitor(BindingTargetVisitor<T, V> visitor,
|
public <T, V> V acceptExtensionVisitor(
|
||||||
ProviderInstanceBinding<? extends T> binding) {
|
BindingTargetVisitor<T, V> visitor, ProviderInstanceBinding<? extends T> binding) {
|
||||||
if (visitor instanceof AssistedInjectTargetVisitor) {
|
if (visitor instanceof AssistedInjectTargetVisitor) {
|
||||||
return ((AssistedInjectTargetVisitor<T, V>) visitor).visit((AssistedInjectBinding<T>) this);
|
return ((AssistedInjectTargetVisitor<T, V>) visitor).visit((AssistedInjectBinding<T>) this);
|
||||||
}
|
}
|
||||||
|
@ -365,7 +494,8 @@ final class FactoryProvider2<F> implements InvocationHandler,
|
||||||
private void validateFactoryReturnType(Errors errors, Class<?> returnType, Class<?> factoryType) {
|
private void validateFactoryReturnType(Errors errors, Class<?> returnType, Class<?> factoryType) {
|
||||||
if (Modifier.isPublic(factoryType.getModifiers())
|
if (Modifier.isPublic(factoryType.getModifiers())
|
||||||
&& !Modifier.isPublic(returnType.getModifiers())) {
|
&& !Modifier.isPublic(returnType.getModifiers())) {
|
||||||
errors.addMessage("%s is public, but has a method that returns a non-public type: %s. "
|
errors.addMessage(
|
||||||
|
"%s is public, but has a method that returns a non-public type: %s. "
|
||||||
+ "Due to limitations with java.lang.reflect.Proxy, this is not allowed. "
|
+ "Due to limitations with java.lang.reflect.Proxy, this is not allowed. "
|
||||||
+ "Please either make the factory non-public or the return type public.",
|
+ "Please either make the factory non-public or the return type public.",
|
||||||
factoryType, returnType);
|
factoryType, returnType);
|
||||||
|
@ -379,8 +509,8 @@ final class FactoryProvider2<F> implements InvocationHandler,
|
||||||
private boolean isTypeNotSpecified(TypeLiteral<?> typeLiteral, ConfigurationException ce) {
|
private boolean isTypeNotSpecified(TypeLiteral<?> typeLiteral, ConfigurationException ce) {
|
||||||
Collection<Message> messages = ce.getErrorMessages();
|
Collection<Message> messages = ce.getErrorMessages();
|
||||||
if (messages.size() == 1) {
|
if (messages.size() == 1) {
|
||||||
Message msg = Iterables.getOnlyElement(
|
Message msg =
|
||||||
new Errors().keyNotFullySpecified(typeLiteral).getMessages());
|
Iterables.getOnlyElement(new Errors().keyNotFullySpecified(typeLiteral).getMessages());
|
||||||
return msg.getMessage().equals(Iterables.getOnlyElement(messages).getMessage());
|
return msg.getMessage().equals(Iterables.getOnlyElement(messages).getMessage());
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
@ -390,9 +520,9 @@ final class FactoryProvider2<F> implements InvocationHandler,
|
||||||
/**
|
/**
|
||||||
* Finds a constructor suitable for the method. If the implementation contained any constructors
|
* Finds a constructor suitable for the method. If the implementation contained any constructors
|
||||||
* marked with {@link AssistedInject}, this requires all {@link Assisted} parameters to exactly
|
* marked with {@link AssistedInject}, this requires all {@link Assisted} parameters to exactly
|
||||||
* match the parameters (in any order) listed in the method. Otherwise, if no
|
* match the parameters (in any order) listed in the method. Otherwise, if no {@link
|
||||||
* {@link AssistedInject} constructors exist, this will default to looking for an
|
* AssistedInject} constructors exist, this will default to looking for an {@literal @}{@link
|
||||||
* {@literal @}{@link Inject} constructor.
|
* Inject} constructor.
|
||||||
*/
|
*/
|
||||||
private <T> InjectionPoint findMatchingConstructorInjectionPoint(
|
private <T> InjectionPoint findMatchingConstructorInjectionPoint(
|
||||||
Method method, Key<?> returnType, TypeLiteral<T> implementation, List<Key<?>> paramList)
|
Method method, Key<?> returnType, TypeLiteral<T> implementation, List<Key<?>> paramList)
|
||||||
|
@ -428,8 +558,7 @@ final class FactoryProvider2<F> implements InvocationHandler,
|
||||||
anyAssistedInjectConstructors = true;
|
anyAssistedInjectConstructors = true;
|
||||||
if (constructorHasMatchingParams(implementation, constructor, paramList, errors)) {
|
if (constructorHasMatchingParams(implementation, constructor, paramList, errors)) {
|
||||||
if (matchingConstructor != null) {
|
if (matchingConstructor != null) {
|
||||||
errors
|
errors.addMessage(
|
||||||
.addMessage(
|
|
||||||
"%s has more than one constructor annotated with @AssistedInject"
|
"%s has more than one constructor annotated with @AssistedInject"
|
||||||
+ " that matches the parameters in method %s. Unable to create "
|
+ " that matches the parameters in method %s. Unable to create "
|
||||||
+ "AssistedInject factory.",
|
+ "AssistedInject factory.",
|
||||||
|
@ -443,7 +572,7 @@ final class FactoryProvider2<F> implements InvocationHandler,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!anyAssistedInjectConstructors) {
|
if (!anyAssistedInjectConstructors) {
|
||||||
// If none existed, use @Inject.
|
// If none existed, use @Inject or a no-arg constructor.
|
||||||
try {
|
try {
|
||||||
return InjectionPoint.forConstructorOf(implementation);
|
return InjectionPoint.forConstructorOf(implementation);
|
||||||
} catch (ConfigurationException e) {
|
} catch (ConfigurationException e) {
|
||||||
|
@ -455,7 +584,8 @@ final class FactoryProvider2<F> implements InvocationHandler,
|
||||||
if (matchingConstructor != null) {
|
if (matchingConstructor != null) {
|
||||||
// safe because we got the constructor from this implementation.
|
// safe because we got the constructor from this implementation.
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
InjectionPoint ip = InjectionPoint.forConstructor(
|
InjectionPoint ip =
|
||||||
|
InjectionPoint.forConstructor(
|
||||||
(Constructor<? super T>) matchingConstructor, implementation);
|
(Constructor<? super T>) matchingConstructor, implementation);
|
||||||
return ip;
|
return ip;
|
||||||
} else {
|
} else {
|
||||||
|
@ -469,21 +599,19 @@ final class FactoryProvider2<F> implements InvocationHandler,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Matching logic for constructors annotated with AssistedInject.
|
* Matching logic for constructors annotated with AssistedInject. This returns true if and only if
|
||||||
* This returns true if and only if all @Assisted parameters in the
|
* all @Assisted parameters in the constructor exactly match (in any order) all @Assisted
|
||||||
* constructor exactly match (in any order) all @Assisted parameters
|
* parameters the method's parameter.
|
||||||
* the method's parameter.
|
|
||||||
*/
|
*/
|
||||||
private boolean constructorHasMatchingParams(TypeLiteral<?> type,
|
private boolean constructorHasMatchingParams(
|
||||||
Constructor<?> constructor, List<Key<?>> paramList, Errors errors)
|
TypeLiteral<?> type, Constructor<?> constructor, List<Key<?>> paramList, Errors errors)
|
||||||
throws ErrorsException {
|
throws ErrorsException {
|
||||||
List<TypeLiteral<?>> params = type.getParameterTypes(constructor);
|
List<TypeLiteral<?>> params = type.getParameterTypes(constructor);
|
||||||
Annotation[][] paramAnnotations = constructor.getParameterAnnotations();
|
Annotation[][] paramAnnotations = constructor.getParameterAnnotations();
|
||||||
int p = 0;
|
int p = 0;
|
||||||
List<Key<?>> constructorKeys = Lists.newArrayList();
|
List<Key<?>> constructorKeys = Lists.newArrayList();
|
||||||
for (TypeLiteral<?> param : params) {
|
for (TypeLiteral<?> param : params) {
|
||||||
Key<?> paramKey = Annotations.getKey(param, constructor, paramAnnotations[p++],
|
Key<?> paramKey = Annotations.getKey(param, constructor, paramAnnotations[p++], errors);
|
||||||
errors);
|
|
||||||
constructorKeys.add(paramKey);
|
constructorKeys.add(paramKey);
|
||||||
}
|
}
|
||||||
// Require that every key exist in the constructor to match up exactly.
|
// Require that every key exist in the constructor to match up exactly.
|
||||||
|
@ -503,10 +631,9 @@ final class FactoryProvider2<F> implements InvocationHandler,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Calculates all dependencies required by the implementation and constructor. */
|
||||||
* Calculates all dependencies required by the implementation and constructor.
|
private Set<Dependency<?>> getDependencies(
|
||||||
*/
|
InjectionPoint ctorPoint, TypeLiteral<?> implementation) {
|
||||||
private Set<Dependency<?>> getDependencies(InjectionPoint ctorPoint, TypeLiteral<?> implementation) {
|
|
||||||
ImmutableSet.Builder<Dependency<?>> builder = ImmutableSet.builder();
|
ImmutableSet.Builder<Dependency<?>> builder = ImmutableSet.builder();
|
||||||
builder.addAll(ctorPoint.getDependencies());
|
builder.addAll(ctorPoint.getDependencies());
|
||||||
if (!implementation.getRawType().isInterface()) {
|
if (!implementation.getRawType().isInterface()) {
|
||||||
|
@ -517,9 +644,7 @@ final class FactoryProvider2<F> implements InvocationHandler,
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Return all non-assisted dependencies. */
|
||||||
* Return all non-assisted dependencies.
|
|
||||||
*/
|
|
||||||
private Set<Dependency<?>> removeAssistedDeps(Set<Dependency<?>> deps) {
|
private Set<Dependency<?>> removeAssistedDeps(Set<Dependency<?>> deps) {
|
||||||
ImmutableSet.Builder<Dependency<?>> builder = ImmutableSet.builder();
|
ImmutableSet.Builder<Dependency<?>> builder = ImmutableSet.builder();
|
||||||
for (Dependency<?> dep : deps) {
|
for (Dependency<?> dep : deps) {
|
||||||
|
@ -537,8 +662,8 @@ final class FactoryProvider2<F> implements InvocationHandler,
|
||||||
* the assisted bindings are immediately provided. This looks for hints that the values may be
|
* the assisted bindings are immediately provided. This looks for hints that the values may be
|
||||||
* lazily retrieved, by looking for injections of Injector or a Provider for the assisted values.
|
* lazily retrieved, by looking for injections of Injector or a Provider for the assisted values.
|
||||||
*/
|
*/
|
||||||
private boolean isValidForOptimizedAssistedInject(Set<Dependency<?>> dependencies,
|
private boolean isValidForOptimizedAssistedInject(
|
||||||
Class<?> implementation, TypeLiteral<?> factoryType) {
|
Set<Dependency<?>> dependencies, Class<?> implementation, TypeLiteral<?> factoryType) {
|
||||||
Set<Dependency<?>> badDeps = null; // optimization: create lazily
|
Set<Dependency<?>> badDeps = null; // optimization: create lazily
|
||||||
for (Dependency<?> dep : dependencies) {
|
for (Dependency<?> dep : dependencies) {
|
||||||
if (isInjectorOrAssistedProvider(dep)) {
|
if (isInjectorOrAssistedProvider(dep)) {
|
||||||
|
@ -549,7 +674,9 @@ final class FactoryProvider2<F> implements InvocationHandler,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (badDeps != null && !badDeps.isEmpty()) {
|
if (badDeps != null && !badDeps.isEmpty()) {
|
||||||
logger.log(Level.WARNING, "AssistedInject factory {0} will be slow "
|
logger.log(
|
||||||
|
Level.WARNING,
|
||||||
|
"AssistedInject factory {0} will be slow "
|
||||||
+ "because {1} has assisted Provider dependencies or injects the Injector. "
|
+ "because {1} has assisted Provider dependencies or injects the Injector. "
|
||||||
+ "Stop injecting @Assisted Provider<T> (instead use @Assisted T) "
|
+ "Stop injecting @Assisted Provider<T> (instead use @Assisted T) "
|
||||||
+ "or Injector to speed things up. (It will be a ~6500% speed bump!) "
|
+ "or Injector to speed things up. (It will be a ~6500% speed bump!) "
|
||||||
|
@ -561,33 +688,43 @@ final class FactoryProvider2<F> implements InvocationHandler,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the dependency is for {@link Injector} or if the dependency
|
* Returns true if the dependency is for {@link Injector} or if the dependency is a {@link
|
||||||
* is a {@link Provider} for a parameter that is {@literal @}{@link Assisted}.
|
* Provider} for a parameter that is {@literal @}{@link Assisted}.
|
||||||
*/
|
*/
|
||||||
private boolean isInjectorOrAssistedProvider(Dependency<?> dependency) {
|
private boolean isInjectorOrAssistedProvider(Dependency<?> dependency) {
|
||||||
Class<?> annotationType = dependency.getKey().getAnnotationType();
|
Class<?> annotationType = dependency.getKey().getAnnotationType();
|
||||||
if (annotationType != null && annotationType.equals(Assisted.class)) { // If it's assisted..
|
if (annotationType != null && annotationType.equals(Assisted.class)) { // If it's assisted..
|
||||||
if (dependency.getKey().getTypeLiteral().getRawType().equals(Provider.class)) { // And a Provider...
|
if (dependency
|
||||||
|
.getKey()
|
||||||
|
.getTypeLiteral()
|
||||||
|
.getRawType()
|
||||||
|
.equals(Provider.class)) { // And a Provider...
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else if (dependency.getKey().getTypeLiteral().getRawType().equals(Injector.class)) { // If it's the Injector...
|
} else if (dependency
|
||||||
|
.getKey()
|
||||||
|
.getTypeLiteral()
|
||||||
|
.getRawType()
|
||||||
|
.equals(Injector.class)) { // If it's the Injector...
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a key similar to {@code key}, but with an {@literal @}Assisted binding annotation.
|
* Returns a key similar to {@code key}, but with an {@literal @}Assisted binding annotation. This
|
||||||
* This fails if another binding annotation is clobbered in the process. If the key already has
|
* fails if another binding annotation is clobbered in the process. If the key already has the
|
||||||
* the {@literal @}Assisted annotation, it is returned as-is to preserve any String value.
|
* {@literal @}Assisted annotation, it is returned as-is to preserve any String value.
|
||||||
*/
|
*/
|
||||||
private <T> Key<T> assistKey(Method method, Key<T> key, Errors errors) throws ErrorsException {
|
private <T> Key<T> assistKey(Method method, Key<T> key, Errors errors) throws ErrorsException {
|
||||||
if (key.getAnnotationType() == null) {
|
if (key.getAnnotationType() == null) {
|
||||||
return Key.get(key.getTypeLiteral(), DEFAULT_ANNOTATION);
|
return key.withAnnotation(DEFAULT_ANNOTATION);
|
||||||
} else if (key.getAnnotationType() == Assisted.class) {
|
} else if (key.getAnnotationType() == Assisted.class) {
|
||||||
return key;
|
return key;
|
||||||
} else {
|
} else {
|
||||||
errors.withSource(method).addMessage(
|
errors
|
||||||
|
.withSource(method)
|
||||||
|
.addMessage(
|
||||||
"Only @Assisted is allowed for factory parameters, but found @%s",
|
"Only @Assisted is allowed for factory parameters, but found @%s",
|
||||||
key.getAnnotationType());
|
key.getAnnotationType());
|
||||||
throw errors.toException();
|
throw errors.toException();
|
||||||
|
@ -595,14 +732,17 @@ final class FactoryProvider2<F> implements InvocationHandler,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* At injector-creation time, we initialize the invocation handler. At this time we make sure
|
* At injector-creation time, we initialize the invocation handler. At this time we make sure all
|
||||||
* all factory methods will be able to build the target types.
|
* factory methods will be able to build the target types.
|
||||||
*/
|
*/
|
||||||
@Inject
|
@Inject
|
||||||
@Toolable
|
@Toolable
|
||||||
void initialize(Injector injector) {
|
void initialize(Injector injector) {
|
||||||
if (this.injector != null) {
|
if (this.injector != null) {
|
||||||
throw new ConfigurationException(ImmutableList.of(new Message(FactoryProvider2.class,
|
throw new ConfigurationException(
|
||||||
|
ImmutableList.of(
|
||||||
|
new Message(
|
||||||
|
FactoryProvider2.class,
|
||||||
"Factories.create() factories may only be used in one Injector!")));
|
"Factories.create() factories may only be used in one Injector!")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -618,7 +758,8 @@ final class FactoryProvider2<F> implements InvocationHandler,
|
||||||
} else {
|
} else {
|
||||||
args = null; // won't be used -- instead will bind to data.providers.
|
args = null; // won't be used -- instead will bind to data.providers.
|
||||||
}
|
}
|
||||||
getBindingFromNewInjector(method, args, data); // throws if the binding isn't properly configured
|
getBindingFromNewInjector(
|
||||||
|
method, args, data); // throws if the binding isn't properly configured
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -627,7 +768,8 @@ final class FactoryProvider2<F> implements InvocationHandler,
|
||||||
*/
|
*/
|
||||||
public Binding<?> getBindingFromNewInjector(
|
public Binding<?> getBindingFromNewInjector(
|
||||||
final Method method, final Object[] args, final AssistData data) {
|
final Method method, final Object[] args, final AssistData data) {
|
||||||
checkState(injector != null,
|
checkState(
|
||||||
|
injector != null,
|
||||||
"Factories.create() factories cannot be used until they're initialized by Guice.");
|
"Factories.create() factories cannot be used until they're initialized by Guice.");
|
||||||
|
|
||||||
final Key<?> returnType = data.returnType;
|
final Key<?> returnType = data.returnType;
|
||||||
|
@ -635,17 +777,21 @@ final class FactoryProvider2<F> implements InvocationHandler,
|
||||||
// We ignore any pre-existing binding annotation.
|
// We ignore any pre-existing binding annotation.
|
||||||
final Key<?> returnKey = Key.get(returnType.getTypeLiteral(), RETURN_ANNOTATION);
|
final Key<?> returnKey = Key.get(returnType.getTypeLiteral(), RETURN_ANNOTATION);
|
||||||
|
|
||||||
Module assistedModule = new AbstractModule() {
|
Module assistedModule =
|
||||||
|
new AbstractModule() {
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings({
|
@SuppressWarnings({
|
||||||
"unchecked", "rawtypes"}) // raw keys are necessary for the args array and return value
|
"unchecked",
|
||||||
|
"rawtypes"
|
||||||
|
}) // raw keys are necessary for the args array and return value
|
||||||
protected void configure() {
|
protected void configure() {
|
||||||
Binder binder = binder().withSource(method);
|
Binder binder = binder().withSource(method);
|
||||||
|
|
||||||
int p = 0;
|
int p = 0;
|
||||||
if (!data.optimized) {
|
if (!data.optimized) {
|
||||||
for (Key<?> paramKey : data.paramTypes) {
|
for (Key<?> paramKey : data.paramTypes) {
|
||||||
// Wrap in a Provider to cover null, and to prevent Guice from injecting the parameter
|
// Wrap in a Provider to cover null, and to prevent Guice from injecting the
|
||||||
|
// parameter
|
||||||
binder.bind((Key) paramKey).toProvider(Providers.of(args[p++]));
|
binder.bind((Key) paramKey).toProvider(Providers.of(args[p++]));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -660,7 +806,8 @@ final class FactoryProvider2<F> implements InvocationHandler,
|
||||||
// but if it isn't, we'll end up throwing a fairly good error
|
// but if it isn't, we'll end up throwing a fairly good error
|
||||||
// message for the user.
|
// message for the user.
|
||||||
if (constructor != null) {
|
if (constructor != null) {
|
||||||
binder.bind(returnKey)
|
binder
|
||||||
|
.bind(returnKey)
|
||||||
.toConstructor(constructor, (TypeLiteral) data.implementationType)
|
.toConstructor(constructor, (TypeLiteral) data.implementationType)
|
||||||
.in(Scopes.NO_SCOPE); // make sure we erase any scope on the implementation type
|
.in(Scopes.NO_SCOPE); // make sure we erase any scope on the implementation type
|
||||||
}
|
}
|
||||||
|
@ -680,6 +827,7 @@ final class FactoryProvider2<F> implements InvocationHandler,
|
||||||
* When a factory method is invoked, we create a child injector that binds all parameters, then
|
* When a factory method is invoked, we create a child injector that binds all parameters, then
|
||||||
* use that to get an instance of the return type.
|
* use that to get an instance of the return type.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public Object invoke(Object proxy, final Method method, final Object[] args) throws Throwable {
|
public Object invoke(Object proxy, final Method method, final Object[] args) throws Throwable {
|
||||||
// If we setup a method handle earlier for this method, call it.
|
// If we setup a method handle earlier for this method, call it.
|
||||||
// This is necessary for default methods that java8 creates, so we
|
// This is necessary for default methods that java8 creates, so we
|
||||||
|
@ -748,93 +896,19 @@ final class FactoryProvider2<F> implements InvocationHandler,
|
||||||
return factoryKey.equals(other.factoryKey) && Objects.equal(collector, other.collector);
|
return factoryKey.equals(other.factoryKey) && Objects.equal(collector, other.collector);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Returns true if {@code thrown} can be thrown by {@code invoked} without wrapping. */
|
||||||
* All the data necessary to perform an assisted inject.
|
static boolean canRethrow(Method invoked, Throwable thrown) {
|
||||||
*/
|
if (thrown instanceof Error || thrown instanceof RuntimeException) {
|
||||||
private static class AssistData implements AssistedMethod {
|
return true;
|
||||||
/**
|
|
||||||
* the constructor the implementation is constructed with.
|
|
||||||
*/
|
|
||||||
final Constructor<?> constructor;
|
|
||||||
/**
|
|
||||||
* the return type in the factory method that the constructor is bound to.
|
|
||||||
*/
|
|
||||||
final Key<?> returnType;
|
|
||||||
/**
|
|
||||||
* the parameters in the factory method associated with this data.
|
|
||||||
*/
|
|
||||||
final ImmutableList<Key<?>> paramTypes;
|
|
||||||
/**
|
|
||||||
* the type of the implementation constructed
|
|
||||||
*/
|
|
||||||
final TypeLiteral<?> implementationType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* All non-assisted dependencies required by this method.
|
|
||||||
*/
|
|
||||||
final Set<Dependency<?>> dependencies;
|
|
||||||
/**
|
|
||||||
* The factory method associated with this data
|
|
||||||
*/
|
|
||||||
final Method factoryMethod;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* true if {@link #isValidForOptimizedAssistedInject} returned true.
|
|
||||||
*/
|
|
||||||
final boolean optimized;
|
|
||||||
/**
|
|
||||||
* the list of optimized providers, empty if not optimized.
|
|
||||||
*/
|
|
||||||
final List<ThreadLocalProvider> providers;
|
|
||||||
/**
|
|
||||||
* used to perform optimized factory creations.
|
|
||||||
*/
|
|
||||||
volatile Binding<?> cachedBinding; // TODO: volatile necessary?
|
|
||||||
|
|
||||||
AssistData(Constructor<?> constructor, Key<?> returnType, ImmutableList<Key<?>> paramTypes,
|
|
||||||
TypeLiteral<?> implementationType, Method factoryMethod,
|
|
||||||
Set<Dependency<?>> dependencies,
|
|
||||||
boolean optimized, List<ThreadLocalProvider> providers) {
|
|
||||||
this.constructor = constructor;
|
|
||||||
this.returnType = returnType;
|
|
||||||
this.paramTypes = paramTypes;
|
|
||||||
this.implementationType = implementationType;
|
|
||||||
this.factoryMethod = factoryMethod;
|
|
||||||
this.dependencies = dependencies;
|
|
||||||
this.optimized = optimized;
|
|
||||||
this.providers = providers;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
for (Class<?> declared : invoked.getExceptionTypes()) {
|
||||||
public String toString() {
|
if (declared.isInstance(thrown)) {
|
||||||
return Objects.toStringHelper(getClass())
|
return true;
|
||||||
.add("ctor", constructor)
|
}
|
||||||
.add("return type", returnType)
|
|
||||||
.add("param type", paramTypes)
|
|
||||||
.add("implementation type", implementationType)
|
|
||||||
.add("dependencies", dependencies)
|
|
||||||
.add("factory method", factoryMethod)
|
|
||||||
.add("optimized", optimized)
|
|
||||||
.add("providers", providers)
|
|
||||||
.add("cached binding", cachedBinding)
|
|
||||||
.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<Dependency<?>> getDependencies() {
|
return false;
|
||||||
return dependencies;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Method getFactoryMethod() {
|
|
||||||
return factoryMethod;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Constructor<?> getImplementationConstructor() {
|
|
||||||
return constructor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TypeLiteral<?> getImplementationType() {
|
|
||||||
return implementationType;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// not <T> because we'll never know and this is easier than suppressing warnings.
|
// not <T> because we'll never know and this is easier than suppressing warnings.
|
||||||
|
@ -847,86 +921,106 @@ final class FactoryProvider2<F> implements InvocationHandler,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private static MethodHandle superMethodHandle(
|
||||||
* Wrapper around MethodHandles/MethodHandle, so we can compile+run on java6.
|
Method method, Object proxy, MethodHandles.Lookup userLookups)
|
||||||
*/
|
throws ReflectiveOperationException {
|
||||||
private static class MethodHandleWrapper {
|
MethodHandles.Lookup lookup = userLookups == null ? MethodHandles.lookup() : userLookups;
|
||||||
static final int ALL_MODES = Modifier.PRIVATE
|
MethodHandle handle = SUPER_METHOD_LOOKUP.get().superMethodHandle(method, lookup);
|
||||||
| Modifier.STATIC /* package */
|
return handle != null ? handle.bindTo(proxy) : null;
|
||||||
| Modifier.PUBLIC
|
}
|
||||||
| Modifier.PROTECTED;
|
|
||||||
|
|
||||||
static final Method unreflectSpecial;
|
// begin by trying unreflectSpecial to find super method handles; this should work on Java14+
|
||||||
static final Method bindTo;
|
private static final AtomicReference<SuperMethodLookup> SUPER_METHOD_LOOKUP =
|
||||||
static final Method invokeWithArguments;
|
new AtomicReference<>(SuperMethodLookup.UNREFLECT_SPECIAL);
|
||||||
static final Constructor<?> lookupCxtor;
|
|
||||||
static final boolean valid;
|
|
||||||
|
|
||||||
static {
|
private static enum SuperMethodLookup {
|
||||||
Method unreflectSpecialTmp = null;
|
UNREFLECT_SPECIAL {
|
||||||
Method bindToTmp = null;
|
@Override
|
||||||
Method invokeWithArgumentsTmp = null;
|
MethodHandle superMethodHandle(Method method, MethodHandles.Lookup lookup)
|
||||||
boolean validTmp = false;
|
throws ReflectiveOperationException {
|
||||||
Constructor<?> lookupCxtorTmp = null;
|
|
||||||
try {
|
try {
|
||||||
Class<?> lookupClass = Class.forName("java.lang.invoke.MethodHandles$Lookup");
|
return lookup.unreflectSpecial(method, method.getDeclaringClass());
|
||||||
unreflectSpecialTmp = lookupClass.getMethod("unreflectSpecial", Method.class, Class.class);
|
} catch (ReflectiveOperationException e) {
|
||||||
Class<?> methodHandleClass = Class.forName("java.lang.invoke.MethodHandle");
|
// fall back to findSpecial which should work on Java9+; use that for future lookups
|
||||||
bindToTmp = methodHandleClass.getMethod("bindTo", Object.class);
|
SUPER_METHOD_LOOKUP.compareAndSet(this, FIND_SPECIAL);
|
||||||
invokeWithArgumentsTmp = methodHandleClass.getMethod("invokeWithArguments", Object[].class);
|
return SUPER_METHOD_LOOKUP.get().superMethodHandle(method, lookup);
|
||||||
lookupCxtorTmp = lookupClass.getDeclaredConstructor(Class.class, int.class);
|
|
||||||
lookupCxtorTmp.setAccessible(true);
|
|
||||||
validTmp = true;
|
|
||||||
} catch (Exception invalid) {
|
|
||||||
// Ignore the exception, store the values & exit early in create(..) if invalid.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store refs to later.
|
|
||||||
valid = validTmp;
|
|
||||||
unreflectSpecial = unreflectSpecialTmp;
|
|
||||||
bindTo = bindToTmp;
|
|
||||||
invokeWithArguments = invokeWithArgumentsTmp;
|
|
||||||
lookupCxtor = lookupCxtorTmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
final Object handle;
|
|
||||||
|
|
||||||
MethodHandleWrapper(Object handle) {
|
|
||||||
this.handle = handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
static MethodHandleWrapper create(Method method, Object proxy) {
|
|
||||||
if (!valid) {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
FIND_SPECIAL {
|
||||||
|
@Override
|
||||||
|
MethodHandle superMethodHandle(Method method, MethodHandles.Lookup lookup)
|
||||||
|
throws ReflectiveOperationException {
|
||||||
try {
|
try {
|
||||||
Class<?> declaringClass = method.getDeclaringClass();
|
Class<?> declaringClass = method.getDeclaringClass();
|
||||||
// Note: this isn't a public API, but we need to use it in order to call default methods.
|
// use findSpecial to workaround https://bugs.openjdk.java.net/browse/JDK-8209005
|
||||||
Object lookup = lookupCxtor.newInstance(declaringClass, ALL_MODES);
|
return lookup.findSpecial(
|
||||||
method.setAccessible(true);
|
declaringClass,
|
||||||
// These are part of the public API, but we use reflection since we run on java6
|
method.getName(),
|
||||||
// and they were introduced in java7.
|
MethodType.methodType(method.getReturnType(), method.getParameterTypes()),
|
||||||
lookup = unreflectSpecial.invoke(lookup, method, declaringClass);
|
declaringClass);
|
||||||
Object handle = bindTo.invoke(lookup, proxy);
|
} catch (ReflectiveOperationException e) {
|
||||||
return new MethodHandleWrapper(handle);
|
if (!allowLookupReflection) {
|
||||||
} catch (InvocationTargetException ite) {
|
throw e;
|
||||||
return null;
|
}
|
||||||
} catch (IllegalAccessException iae) {
|
// fall back to private Lookup which should work on Java8; use that for future lookups
|
||||||
return null;
|
SUPER_METHOD_LOOKUP.compareAndSet(this, PRIVATE_LOOKUP);
|
||||||
} catch (InstantiationException ie) {
|
return SUPER_METHOD_LOOKUP.get().superMethodHandle(method, lookup);
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
Object invokeWithArguments(Object[] args) throws Exception {
|
PRIVATE_LOOKUP {
|
||||||
// We must cast the args to an object so the Object[] is the first param,
|
|
||||||
// as opposed to each individual varargs param.
|
|
||||||
return invokeWithArguments.invoke(handle, (Object) args);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
MethodHandle superMethodHandle(Method method, MethodHandles.Lookup unused)
|
||||||
return handle.toString();
|
throws ReflectiveOperationException {
|
||||||
|
return PrivateLookup.superMethodHandle(method);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
abstract MethodHandle superMethodHandle(Method method, MethodHandles.Lookup lookup)
|
||||||
|
throws ReflectiveOperationException;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Note: this isn't a public API, but we need to use it in order to call default methods on (or
|
||||||
|
// with) non-public types. If it doesn't exist, the code falls back to a less precise check.
|
||||||
|
static class PrivateLookup {
|
||||||
|
PrivateLookup() {}
|
||||||
|
|
||||||
|
private static final int ALL_MODES =
|
||||||
|
Modifier.PRIVATE | Modifier.STATIC /* package */ | Modifier.PUBLIC | Modifier.PROTECTED;
|
||||||
|
|
||||||
|
private static final Constructor<MethodHandles.Lookup> privateLookupCxtor =
|
||||||
|
findPrivateLookupCxtor();
|
||||||
|
|
||||||
|
private static Constructor<MethodHandles.Lookup> findPrivateLookupCxtor() {
|
||||||
|
try {
|
||||||
|
Constructor<MethodHandles.Lookup> cxtor;
|
||||||
|
try {
|
||||||
|
cxtor = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);
|
||||||
|
} catch (NoSuchMethodException ignored) {
|
||||||
|
cxtor =
|
||||||
|
MethodHandles.Lookup.class.getDeclaredConstructor(
|
||||||
|
Class.class, Class.class, int.class);
|
||||||
|
}
|
||||||
|
cxtor.setAccessible(true);
|
||||||
|
return cxtor;
|
||||||
|
} catch (ReflectiveOperationException | SecurityException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static MethodHandle superMethodHandle(Method method) throws ReflectiveOperationException {
|
||||||
|
if (privateLookupCxtor == null) {
|
||||||
|
return null; // fall back to assistDataBuilder workaround
|
||||||
|
}
|
||||||
|
Class<?> declaringClass = method.getDeclaringClass();
|
||||||
|
MethodHandles.Lookup lookup;
|
||||||
|
if (privateLookupCxtor.getParameterCount() == 2) {
|
||||||
|
lookup = privateLookupCxtor.newInstance(declaringClass, ALL_MODES);
|
||||||
|
} else {
|
||||||
|
lookup = privateLookupCxtor.newInstance(declaringClass, null, ALL_MODES);
|
||||||
|
}
|
||||||
|
return lookup.unreflectSpecial(method, declaringClass);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,20 +51,17 @@ public interface LinkedBindingBuilder<T> extends ScopedBindingBuilder {
|
||||||
/**
|
/**
|
||||||
* See the EDSL examples at {@link com.google.inject.Binder}.
|
* See the EDSL examples at {@link com.google.inject.Binder}.
|
||||||
*/
|
*/
|
||||||
ScopedBindingBuilder toProvider(
|
ScopedBindingBuilder toProvider(Class<? extends javax.inject.Provider<? extends T>> providerType);
|
||||||
Class<? extends javax.inject.Provider<? extends T>> providerType);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See the EDSL examples at {@link com.google.inject.Binder}.
|
* See the EDSL examples at {@link com.google.inject.Binder}.
|
||||||
*/
|
*/
|
||||||
ScopedBindingBuilder toProvider(
|
ScopedBindingBuilder toProvider(TypeLiteral<? extends javax.inject.Provider<? extends T>> providerType);
|
||||||
TypeLiteral<? extends javax.inject.Provider<? extends T>> providerType);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See the EDSL examples at {@link com.google.inject.Binder}.
|
* See the EDSL examples at {@link com.google.inject.Binder}.
|
||||||
*/
|
*/
|
||||||
ScopedBindingBuilder toProvider(
|
ScopedBindingBuilder toProvider(Key<? extends javax.inject.Provider<? extends T>> providerKey);
|
||||||
Key<? extends javax.inject.Provider<? extends T>> providerKey);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See the EDSL examples at {@link com.google.inject.Binder}.
|
* 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}.
|
* See the EDSL examples at {@link com.google.inject.Binder}.
|
||||||
*/
|
*/
|
||||||
<S extends T> ScopedBindingBuilder toConstructor(
|
<S extends T> ScopedBindingBuilder toConstructor(Constructor<S> constructor, TypeLiteral<? extends S> type);
|
||||||
Constructor<S> constructor, TypeLiteral<? extends S> type);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,14 +18,14 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
public abstract class AbstractBindingBuilder<T> {
|
public abstract class AbstractBindingBuilder<T> {
|
||||||
|
|
||||||
public static final String IMPLEMENTATION_ALREADY_SET = "Implementation is set more than once.";
|
public static final String IMPLEMENTATION_ALREADY_SET = "Implementation is set more than once.";
|
||||||
public static final String SINGLE_INSTANCE_AND_SCOPE
|
public static final String SINGLE_INSTANCE_AND_SCOPE =
|
||||||
= "Setting the scope is not permitted when binding to a single instance.";
|
"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 SCOPE_ALREADY_SET = "Scope is set more than once.";
|
||||||
public static final String BINDING_TO_NULL = "Binding to null instances is not allowed. "
|
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.";
|
"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 CONSTANT_VALUE_ALREADY_SET = "Constant value is set more than once.";
|
||||||
public static final String ANNOTATION_ALREADY_SPECIFIED
|
public static final String ANNOTATION_ALREADY_SPECIFIED =
|
||||||
= "More than one annotation is specified for this binding.";
|
"More than one annotation is specified for this binding.";
|
||||||
|
|
||||||
protected static final Key<?> NULL_KEY = Key.get(Void.class);
|
protected static final Key<?> NULL_KEY = Key.get(Void.class);
|
||||||
protected final Binder binder;
|
protected final Binder binder;
|
||||||
|
@ -37,7 +37,7 @@ public abstract class AbstractBindingBuilder<T> {
|
||||||
this.binder = binder;
|
this.binder = binder;
|
||||||
this.elements = elements;
|
this.elements = elements;
|
||||||
this.position = elements.size();
|
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);
|
elements.add(position, this.binding);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,8 +57,7 @@ public abstract class AbstractBindingBuilder<T> {
|
||||||
protected BindingImpl<T> annotatedWithInternal(Class<? extends Annotation> annotationType) {
|
protected BindingImpl<T> annotatedWithInternal(Class<? extends Annotation> annotationType) {
|
||||||
checkNotNull(annotationType, "annotationType");
|
checkNotNull(annotationType, "annotationType");
|
||||||
checkNotAnnotated();
|
checkNotAnnotated();
|
||||||
return setBinding(binding.withKey(
|
return setBinding(binding.withKey(Key.get(this.binding.getKey().getTypeLiteral(), annotationType)));
|
||||||
Key.get(this.binding.getKey().getTypeLiteral(), annotationType)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -67,8 +66,7 @@ public abstract class AbstractBindingBuilder<T> {
|
||||||
protected BindingImpl<T> annotatedWithInternal(Annotation annotation) {
|
protected BindingImpl<T> annotatedWithInternal(Annotation annotation) {
|
||||||
checkNotNull(annotation, "annotation");
|
checkNotNull(annotation, "annotation");
|
||||||
checkNotAnnotated();
|
checkNotAnnotated();
|
||||||
return setBinding(binding.withKey(
|
return setBinding(binding.withKey(Key.get(this.binding.getKey().getTypeLiteral(), annotation)));
|
||||||
Key.get(this.binding.getKey().getTypeLiteral(), annotation)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void in(final Class<? extends Annotation> scopeAnnotation) {
|
public void in(final Class<? extends Annotation> scopeAnnotation) {
|
||||||
|
|
|
@ -14,17 +14,16 @@ import com.google.inject.Stage;
|
||||||
import com.google.inject.TypeLiteral;
|
import com.google.inject.TypeLiteral;
|
||||||
import com.google.inject.spi.DefaultBindingTargetVisitor;
|
import com.google.inject.spi.DefaultBindingTargetVisitor;
|
||||||
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Guarantees that processing of Binding elements happens in a sane way.
|
* Guarantees that processing of Binding elements happens in a sane way.
|
||||||
*/
|
*/
|
||||||
abstract class AbstractBindingProcessor extends AbstractProcessor {
|
abstract class AbstractBindingProcessor extends AbstractProcessor {
|
||||||
|
|
||||||
// It's unfortunate that we have to maintain a blacklist of specific
|
// It's unfortunate that we have to maintain a list of specific
|
||||||
// classes, but we can't easily block the whole package because of
|
// classes, but we can't easily block the whole package because of
|
||||||
// all our unit tests.
|
// all our unit tests.
|
||||||
private static final Set<Class<?>> FORBIDDEN_TYPES = ImmutableSet.<Class<?>>of(
|
private static final ImmutableSet<Class<?>> FORBIDDEN_TYPES =
|
||||||
|
ImmutableSet.<Class<?>>of(
|
||||||
AbstractModule.class,
|
AbstractModule.class,
|
||||||
Binder.class,
|
Binder.class,
|
||||||
Binding.class,
|
Binding.class,
|
||||||
|
@ -37,11 +36,11 @@ abstract class AbstractBindingProcessor extends AbstractProcessor {
|
||||||
Stage.class,
|
Stage.class,
|
||||||
TypeLiteral.class);
|
TypeLiteral.class);
|
||||||
|
|
||||||
protected final ProcessedBindingData bindingData;
|
protected final ProcessedBindingData processedBindingData;
|
||||||
|
|
||||||
AbstractBindingProcessor(Errors errors, ProcessedBindingData bindingData) {
|
AbstractBindingProcessor(Errors errors, ProcessedBindingData processedBindingData) {
|
||||||
super(errors);
|
super(errors);
|
||||||
this.bindingData = bindingData;
|
this.processedBindingData = processedBindingData;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected <T> UntargettedBindingImpl<T> invalidBinding(
|
protected <T> UntargettedBindingImpl<T> invalidBinding(
|
||||||
|
@ -61,10 +60,10 @@ abstract class AbstractBindingProcessor extends AbstractProcessor {
|
||||||
BindingImpl<?> original = injector.getExistingBinding(key);
|
BindingImpl<?> original = injector.getExistingBinding(key);
|
||||||
if (original != null) {
|
if (original != null) {
|
||||||
// If it failed because of an explicit duplicate binding...
|
// If it failed because of an explicit duplicate binding...
|
||||||
if (injector.state.getExplicitBinding(key) != null) {
|
if (injector.getBindingData().getExplicitBinding(key) != null) {
|
||||||
try {
|
try {
|
||||||
if (!isOkayDuplicate(original, binding, injector.state)) {
|
if (!isOkayDuplicate(original, binding, injector.getBindingData())) {
|
||||||
errors.bindingAlreadySet(key, original.getSource());
|
errors.bindingAlreadySet(binding, original);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
|
@ -80,27 +79,34 @@ abstract class AbstractBindingProcessor extends AbstractProcessor {
|
||||||
}
|
}
|
||||||
|
|
||||||
// prevent the parent from creating a JIT binding for this key
|
// prevent the parent from creating a JIT binding for this key
|
||||||
injector.state.parent().blacklist(key, injector.state, binding.getSource());
|
injector
|
||||||
injector.state.putBinding(key, binding);
|
.getJitBindingData()
|
||||||
|
.banKeyInParent(key, injector.getBindingData(), binding.getSource());
|
||||||
|
injector.getBindingData().putBinding(key, binding);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We tolerate duplicate bindings if one exposes the other or if the two bindings
|
* We tolerate duplicate bindings if one exposes the other or if the two bindings are considered
|
||||||
* are considered duplicates.
|
* duplicates (see {@link Bindings#areDuplicates(BindingImpl, BindingImpl)}.
|
||||||
*
|
*
|
||||||
* @param original the binding in the parent injector (candidate for an exposing binding)
|
* @param original the binding in the parent injector (candidate for an exposing binding)
|
||||||
* @param binding the binding to check (candidate for the exposed binding)
|
* @param binding the binding to check (candidate for the exposed binding)
|
||||||
*/
|
*/
|
||||||
private boolean isOkayDuplicate(BindingImpl<?> original, BindingImpl<?> binding, State state) {
|
private static boolean isOkayDuplicate(
|
||||||
|
BindingImpl<?> original, BindingImpl<?> binding, InjectorBindingData bindingData) {
|
||||||
if (original instanceof ExposedBindingImpl) {
|
if (original instanceof ExposedBindingImpl) {
|
||||||
ExposedBindingImpl exposed = (ExposedBindingImpl) original;
|
ExposedBindingImpl<?> exposed = (ExposedBindingImpl<?>) original;
|
||||||
InjectorImpl exposedFrom = (InjectorImpl) exposed.getPrivateElements().getInjector();
|
InjectorImpl exposedFrom = (InjectorImpl) exposed.getPrivateElements().getInjector();
|
||||||
return (exposedFrom == binding.getInjector());
|
return (exposedFrom == binding.getInjector());
|
||||||
} else {
|
} else {
|
||||||
original = (BindingImpl<?>) state.getExplicitBindingsThisLevel().get(binding.getKey());
|
original = (BindingImpl<?>) bindingData.getExplicitBindingsThisLevel().get(binding.getKey());
|
||||||
// If no original at this level, the original was on a parent, and we don't
|
// If no original at this level, the original was on a parent, and we don't
|
||||||
// allow deduplication between parents & children.
|
// allow deduplication between parents & children.
|
||||||
return original != null && original.equals(binding);
|
if (original == null) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return original.equals(binding);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,8 +116,8 @@ abstract class AbstractBindingProcessor extends AbstractProcessor {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Processor for visiting bindings. Each overriden method that wants to
|
* Processor for visiting bindings. Each overriden method that wants to actually process the
|
||||||
* actually process the binding should call prepareBinding first.
|
* binding should call prepareBinding first.
|
||||||
*/
|
*/
|
||||||
abstract class Processor<T, V> extends DefaultBindingTargetVisitor<T, V> {
|
abstract class Processor<T, V> extends DefaultBindingTargetVisitor<T, V> {
|
||||||
final Object source;
|
final Object source;
|
||||||
|
@ -131,16 +137,28 @@ abstract class AbstractBindingProcessor extends AbstractProcessor {
|
||||||
scoping = Scoping.makeInjectable(scoping, injector, errors);
|
scoping = Scoping.makeInjectable(scoping, injector, errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void scheduleInitialization(final BindingImpl<?> binding) {
|
/**
|
||||||
bindingData.addUninitializedBinding(new Runnable() {
|
* Schedule initialization of this binding to occur immediately after all bindings have been
|
||||||
public void run() {
|
* initialially processed.
|
||||||
|
*/
|
||||||
|
protected void scheduleInitialization(BindingImpl<?> binding) {
|
||||||
|
processedBindingData.addUninitializedBinding(() -> initializeBinding(binding));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedule initialization for this binding to occur after all other static initialization of
|
||||||
|
* bindings.
|
||||||
|
*/
|
||||||
|
protected void scheduleDelayedInitialization(BindingImpl<?> binding) {
|
||||||
|
processedBindingData.addDelayedUninitializedBinding(() -> initializeBinding(binding));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeBinding(BindingImpl<?> binding) {
|
||||||
try {
|
try {
|
||||||
binding.getInjector().initializeBinding(binding, errors.withSource(source));
|
binding.getInjector().initializeBinding(binding, errors.withSource(source));
|
||||||
} catch (ErrorsException e) {
|
} catch (ErrorsException e) {
|
||||||
errors.merge(e.getErrors());
|
errors.merge(e.getErrors());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package com.google.inject.internal;
|
package com.google.inject.internal;
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
|
|
||||||
import com.google.common.base.Joiner;
|
import com.google.common.base.Joiner;
|
||||||
import com.google.common.base.Joiner.MapJoiner;
|
import com.google.common.base.Joiner.MapJoiner;
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
|
@ -16,8 +17,6 @@ import com.google.inject.TypeLiteral;
|
||||||
import com.google.inject.internal.util.Classes;
|
import com.google.inject.internal.util.Classes;
|
||||||
import com.google.inject.name.Named;
|
import com.google.inject.name.Named;
|
||||||
import com.google.inject.name.Names;
|
import com.google.inject.name.Names;
|
||||||
|
|
||||||
import javax.inject.Qualifier;
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
@ -28,37 +27,16 @@ import java.lang.reflect.Proxy;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import javax.inject.Qualifier;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Annotation utilities.
|
* Annotation utilities.
|
||||||
*
|
*
|
||||||
|
* @author crazybob@google.com (Bob Lee)
|
||||||
*/
|
*/
|
||||||
public class Annotations {
|
public class Annotations {
|
||||||
|
|
||||||
private static final MapJoiner JOINER = Joiner.on(", ").withKeyValueSeparator("=");
|
/** Returns {@code true} if the given annotation type has no attributes. */
|
||||||
private static final Function<Object, String> DEEP_TO_STRING_FN = new Function<Object, String>() {
|
|
||||||
@Override
|
|
||||||
public String apply(Object arg) {
|
|
||||||
String s = Arrays.deepToString(new Object[]{arg});
|
|
||||||
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>() {
|
|
||||||
@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(
|
|
||||||
Arrays.asList(BindingAnnotation.class, Qualifier.class));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns {@code true} if the given annotation type has no attributes.
|
|
||||||
*/
|
|
||||||
public static boolean isMarker(Class<? extends Annotation> annotationType) {
|
public static boolean isMarker(Class<? extends Annotation> annotationType) {
|
||||||
return annotationType.getDeclaredMethods().length == 0;
|
return annotationType.getDeclaredMethods().length == 0;
|
||||||
}
|
}
|
||||||
|
@ -74,11 +52,22 @@ public class Annotations {
|
||||||
return hasMethods;
|
return hasMethods;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final LoadingCache<Class<? extends Annotation>, Annotation> cache =
|
||||||
|
CacheBuilder.newBuilder()
|
||||||
|
.weakKeys()
|
||||||
|
.build(
|
||||||
|
new CacheLoader<Class<? extends Annotation>, Annotation>() {
|
||||||
|
@Override
|
||||||
|
public Annotation load(Class<? extends Annotation> input) {
|
||||||
|
return generateAnnotationImpl(input);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates an Annotation for the annotation class. Requires that the annotation is all
|
* Generates an Annotation for the annotation class. Requires that the annotation is all
|
||||||
* optionals.
|
* optionals.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked") // Safe because generateAnnotationImpl returns T for Class<T>
|
||||||
public static <T extends Annotation> T generateAnnotation(Class<T> annotationType) {
|
public static <T extends Annotation> T generateAnnotation(Class<T> annotationType) {
|
||||||
Preconditions.checkState(
|
Preconditions.checkState(
|
||||||
isAllDefaultMethods(annotationType), "%s is not all default methods", annotationType);
|
isAllDefaultMethods(annotationType), "%s is not all default methods", annotationType);
|
||||||
|
@ -87,7 +76,8 @@ public class Annotations {
|
||||||
|
|
||||||
private static <T extends Annotation> T generateAnnotationImpl(final Class<T> annotationType) {
|
private static <T extends Annotation> T generateAnnotationImpl(final Class<T> annotationType) {
|
||||||
final Map<String, Object> members = resolveMembers(annotationType);
|
final Map<String, Object> members = resolveMembers(annotationType);
|
||||||
return annotationType.cast(Proxy.newProxyInstance(
|
return annotationType.cast(
|
||||||
|
Proxy.newProxyInstance(
|
||||||
annotationType.getClassLoader(),
|
annotationType.getClassLoader(),
|
||||||
new Class<?>[] {annotationType},
|
new Class<?>[] {annotationType},
|
||||||
new InvocationHandler() {
|
new InvocationHandler() {
|
||||||
|
@ -118,11 +108,10 @@ public class Annotations {
|
||||||
return result.build();
|
return result.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Implements {@link Annotation#equals}. */
|
||||||
* Implements {@link Annotation#equals}.
|
private static boolean annotationEquals(
|
||||||
*/
|
Class<? extends Annotation> type, Map<String, Object> members, Object other)
|
||||||
private static boolean annotationEquals(Class<? extends Annotation> type,
|
throws Exception {
|
||||||
Map<String, Object> members, Object other) throws Exception {
|
|
||||||
if (!type.isInstance(other)) {
|
if (!type.isInstance(other)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -136,11 +125,9 @@ public class Annotations {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Implements {@link Annotation#hashCode}. */
|
||||||
* Implements {@link Annotation#hashCode}.
|
private static int annotationHashCode(
|
||||||
*/
|
Class<? extends Annotation> type, Map<String, Object> members) throws Exception {
|
||||||
private static int annotationHashCode(Class<? extends Annotation> type,
|
|
||||||
Map<String, Object> members) throws Exception {
|
|
||||||
int result = 0;
|
int result = 0;
|
||||||
for (Method method : type.getDeclaredMethods()) {
|
for (Method method : type.getDeclaredMethods()) {
|
||||||
String name = method.getName();
|
String name = method.getName();
|
||||||
|
@ -150,36 +137,38 @@ public class Annotations {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private static final MapJoiner JOINER = Joiner.on(", ").withKeyValueSeparator("=");
|
||||||
* Implements {@link Annotation#toString}.
|
|
||||||
*/
|
/** Implements {@link Annotation#toString}. */
|
||||||
private static String annotationToString(Class<? extends Annotation> type,
|
private static String annotationToString(
|
||||||
Map<String, Object> members) throws Exception {
|
Class<? extends Annotation> type, Map<String, Object> members) throws Exception {
|
||||||
StringBuilder sb = new StringBuilder().append("@").append(type.getName()).append("(");
|
StringBuilder sb = new StringBuilder().append("@").append(type.getName()).append("(");
|
||||||
JOINER.appendTo(sb, Maps.transformValues(members, DEEP_TO_STRING_FN));
|
JOINER.appendTo(
|
||||||
|
sb,
|
||||||
|
Maps.transformValues(
|
||||||
|
members,
|
||||||
|
arg -> {
|
||||||
|
String s = Arrays.deepToString(new Object[] {arg});
|
||||||
|
return s.substring(1, s.length() - 1); // cut off brackets
|
||||||
|
}));
|
||||||
return sb.append(")").toString();
|
return sb.append(")").toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Returns true if the given annotation is retained at runtime. */
|
||||||
* Returns true if the given annotation is retained at runtime.
|
|
||||||
*/
|
|
||||||
public static boolean isRetainedAtRuntime(Class<? extends Annotation> annotationType) {
|
public static boolean isRetainedAtRuntime(Class<? extends Annotation> annotationType) {
|
||||||
Retention retention = annotationType.getAnnotation(Retention.class);
|
Retention retention = annotationType.getAnnotation(Retention.class);
|
||||||
return retention != null && retention.value() == RetentionPolicy.RUNTIME;
|
return retention != null && retention.value() == RetentionPolicy.RUNTIME;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Returns the scope annotation on {@code type}, or null if none is specified. */
|
||||||
* Returns the scope annotation on {@code type}, or null if none is specified.
|
|
||||||
*/
|
|
||||||
public static Class<? extends Annotation> findScopeAnnotation(
|
public static Class<? extends Annotation> findScopeAnnotation(
|
||||||
Errors errors, Class<?> implementation) {
|
Errors errors, Class<?> implementation) {
|
||||||
return findScopeAnnotation(errors, implementation.getAnnotations());
|
return findScopeAnnotation(errors, implementation.getAnnotations());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Returns the scoping annotation, or null if there isn't one. */
|
||||||
* Returns the scoping annotation, or null if there isn't one.
|
public static Class<? extends Annotation> findScopeAnnotation(
|
||||||
*/
|
Errors errors, Annotation[] annotations) {
|
||||||
public static Class<? extends Annotation> findScopeAnnotation(Errors errors, Annotation[] annotations) {
|
|
||||||
Class<? extends Annotation> found = null;
|
Class<? extends Annotation> found = null;
|
||||||
|
|
||||||
for (Annotation annotation : annotations) {
|
for (Annotation annotation : annotations) {
|
||||||
|
@ -207,13 +196,115 @@ public class Annotations {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class AnnotationToStringConfig {
|
||||||
|
final boolean quote;
|
||||||
|
final boolean includeMemberName;
|
||||||
|
|
||||||
|
AnnotationToStringConfig(boolean quote, boolean includeMemberName) {
|
||||||
|
this.quote = quote;
|
||||||
|
this.includeMemberName = includeMemberName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final AnnotationToStringConfig ANNOTATION_TO_STRING_CONFIG =
|
||||||
|
determineAnnotationToStringConfig();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns {@code value}, quoted if annotation implementations quote their member values. In Java
|
||||||
|
* 9, annotations quote their string members.
|
||||||
|
*/
|
||||||
|
public static String memberValueString(String value) {
|
||||||
|
return ANNOTATION_TO_STRING_CONFIG.quote ? "\"" + value + "\"" : value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns string representation of the annotation memeber.
|
||||||
|
*
|
||||||
|
* <p>The value of the member is prefixed with `memberName=` unless the runtime omits the member
|
||||||
|
* name. The value of the member is quoted if annotation implementations quote their member values
|
||||||
|
* and the value type is String.
|
||||||
|
*
|
||||||
|
* <p>In Java 9, annotations quote their string members and in Java 15, the member name is
|
||||||
|
* omitted.
|
||||||
|
*/
|
||||||
|
public static String memberValueString(String memberName, Object value) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
boolean quote = ANNOTATION_TO_STRING_CONFIG.quote;
|
||||||
|
boolean includeMemberName = ANNOTATION_TO_STRING_CONFIG.includeMemberName;
|
||||||
|
if (includeMemberName) {
|
||||||
|
sb.append(memberName).append('=');
|
||||||
|
}
|
||||||
|
if (quote && (value instanceof String)) {
|
||||||
|
sb.append('"').append(value).append('"');
|
||||||
|
} else {
|
||||||
|
sb.append(value);
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Retention(RUNTIME)
|
||||||
|
private @interface TestAnnotation {
|
||||||
|
String value();
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestAnnotation("determineAnnotationToStringConfig")
|
||||||
|
private static AnnotationToStringConfig determineAnnotationToStringConfig() {
|
||||||
|
try {
|
||||||
|
String annotation =
|
||||||
|
Annotations.class
|
||||||
|
.getDeclaredMethod("determineAnnotationToStringConfig")
|
||||||
|
.getAnnotation(TestAnnotation.class)
|
||||||
|
.toString();
|
||||||
|
boolean quote = annotation.contains("\"determineAnnotationToStringConfig\"");
|
||||||
|
boolean includeMemberName = annotation.contains("value=");
|
||||||
|
return new AnnotationToStringConfig(quote, includeMemberName);
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Checks for the presence of annotations. Caches results because Android doesn't. */
|
||||||
|
static class AnnotationChecker {
|
||||||
|
private final Collection<Class<? extends Annotation>> annotationTypes;
|
||||||
|
|
||||||
|
/** 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>() {
|
||||||
|
@Override
|
||||||
|
public Boolean load(Class<? extends Annotation> annotationType) {
|
||||||
|
for (Annotation annotation : annotationType.getAnnotations()) {
|
||||||
|
if (annotationTypes.contains(annotation.annotationType())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
final LoadingCache<Class<? extends Annotation>, Boolean> cache =
|
||||||
|
CacheBuilder.newBuilder().weakKeys().build(hasAnnotations);
|
||||||
|
|
||||||
|
/** Constructs a new checker that looks for annotations of the given types. */
|
||||||
|
AnnotationChecker(Collection<Class<? extends Annotation>> annotationTypes) {
|
||||||
|
this.annotationTypes = annotationTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns true if the given type has one of the desired annotations. */
|
||||||
|
boolean hasAnnotations(Class<? extends Annotation> annotated) {
|
||||||
|
return cache.getUnchecked(annotated);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final AnnotationChecker scopeChecker =
|
||||||
|
new AnnotationChecker(Arrays.asList(ScopeAnnotation.class, javax.inject.Scope.class));
|
||||||
|
|
||||||
public static boolean isScopeAnnotation(Class<? extends Annotation> annotationType) {
|
public static boolean isScopeAnnotation(Class<? extends Annotation> annotationType) {
|
||||||
return scopeChecker.hasAnnotations(annotationType);
|
return scopeChecker.hasAnnotations(annotationType);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds an error if there is a misplaced annotations on {@code type}. Scoping
|
* Adds an error if there is a misplaced annotations on {@code type}. Scoping annotations are not
|
||||||
* annotations are not allowed on abstract classes or interfaces.
|
* allowed on abstract classes or interfaces.
|
||||||
*/
|
*/
|
||||||
public static void checkForMisplacedScopeAnnotations(
|
public static void checkForMisplacedScopeAnnotations(
|
||||||
Class<?> type, Object source, Errors errors) {
|
Class<?> type, Object source, Errors errors) {
|
||||||
|
@ -229,20 +320,20 @@ public class Annotations {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// NOTE: getKey/findBindingAnnotation are used by Gin which is abandoned. So changing this API
|
||||||
* Gets a key for the given type, member and annotations.
|
// will prevent Gin users from upgrading Guice version.
|
||||||
*/
|
|
||||||
public static Key<?> getKey(TypeLiteral<?> type, Member member, Annotation[] annotations,
|
/** Gets a key for the given type, member and annotations. */
|
||||||
Errors errors) throws ErrorsException {
|
public static Key<?> getKey(
|
||||||
|
TypeLiteral<?> type, Member member, Annotation[] annotations, Errors errors)
|
||||||
|
throws ErrorsException {
|
||||||
int numErrorsBefore = errors.size();
|
int numErrorsBefore = errors.size();
|
||||||
Annotation found = findBindingAnnotation(errors, member, annotations);
|
Annotation found = findBindingAnnotation(errors, member, annotations);
|
||||||
errors.throwIfNewErrors(numErrorsBefore);
|
errors.throwIfNewErrors(numErrorsBefore);
|
||||||
return found == null ? Key.get(type) : Key.get(type, found);
|
return found == null ? Key.get(type) : Key.get(type, found);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Returns the binding annotation on {@code member}, or null if there isn't one. */
|
||||||
* Returns the binding annotation on {@code member}, or null if there isn't one.
|
|
||||||
*/
|
|
||||||
public static Annotation findBindingAnnotation(
|
public static Annotation findBindingAnnotation(
|
||||||
Errors errors, Member member, Annotation[] annotations) {
|
Errors errors, Member member, Annotation[] annotations) {
|
||||||
Annotation found = null;
|
Annotation found = null;
|
||||||
|
@ -261,9 +352,10 @@ public class Annotations {
|
||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private static final AnnotationChecker bindingAnnotationChecker =
|
||||||
* Returns true if annotations of the specified type are binding annotations.
|
new AnnotationChecker(Arrays.asList(BindingAnnotation.class, Qualifier.class));
|
||||||
*/
|
|
||||||
|
/** Returns true if annotations of the specified type are binding annotations. */
|
||||||
public static boolean isBindingAnnotation(Class<? extends Annotation> annotationType) {
|
public static boolean isBindingAnnotation(Class<? extends Annotation> annotationType) {
|
||||||
return bindingAnnotationChecker.hasAnnotations(annotationType);
|
return bindingAnnotationChecker.hasAnnotations(annotationType);
|
||||||
}
|
}
|
||||||
|
@ -294,41 +386,20 @@ public class Annotations {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks for the presence of annotations. Caches results because Android doesn't.
|
* 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.
|
||||||
*/
|
*/
|
||||||
static class AnnotationChecker {
|
public static String nameOf(Key<?> key) {
|
||||||
private final Collection<Class<? extends Annotation>> annotationTypes;
|
Annotation annotation = key.getAnnotation();
|
||||||
|
Class<? extends Annotation> annotationType = key.getAnnotationType();
|
||||||
/**
|
if (annotation != null && !isMarker(annotationType)) {
|
||||||
* Returns true if the given class has one of the desired annotations.
|
return key.getAnnotation().toString();
|
||||||
*/
|
} else if (key.getAnnotationType() != null) {
|
||||||
private CacheLoader<Class<? extends Annotation>, Boolean> hasAnnotations =
|
return "@" + key.getAnnotationType().getName();
|
||||||
new CacheLoader<Class<? extends Annotation>, Boolean>() {
|
} else {
|
||||||
public Boolean load(Class<? extends Annotation> annotationType) {
|
return "";
|
||||||
for (Annotation annotation : annotationType.getAnnotations()) {
|
|
||||||
if (annotationTypes.contains(annotation.annotationType())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
final LoadingCache<Class<? extends Annotation>, Boolean> cache = CacheBuilder.newBuilder().weakKeys()
|
|
||||||
.build(hasAnnotations);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a new checker that looks for annotations of the given types.
|
|
||||||
*/
|
|
||||||
AnnotationChecker(Collection<Class<? extends Annotation>> annotationTypes) {
|
|
||||||
this.annotationTypes = annotationTypes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the given type has one of the desired annotations.
|
|
||||||
*/
|
|
||||||
boolean hasAnnotations(Class<? extends Annotation> annotated) {
|
|
||||||
return cache.getUnchecked(annotated);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.inject.Binding;
|
||||||
|
import com.google.inject.spi.ErrorDetail;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Formatter;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/** Error reported by Guice when a key is bound at multiple places the injector. */
|
||||||
|
final class BindingAlreadySetError extends InternalErrorDetail<BindingAlreadySetError> {
|
||||||
|
private final Binding<?> binding;
|
||||||
|
private final Binding<?> original;
|
||||||
|
|
||||||
|
BindingAlreadySetError(Binding<?> binding, Binding<?> original, List<Object> sources) {
|
||||||
|
super(
|
||||||
|
ErrorId.BINDING_ALREADY_SET,
|
||||||
|
String.format("%s was bound multiple times.", Messages.convert(binding.getKey())),
|
||||||
|
sources,
|
||||||
|
null);
|
||||||
|
this.binding = binding;
|
||||||
|
this.original = original;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isMergeable(ErrorDetail<?> otherError) {
|
||||||
|
return otherError instanceof BindingAlreadySetError
|
||||||
|
&& ((BindingAlreadySetError) otherError).binding.getKey().equals(binding.getKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void formatDetail(List<ErrorDetail<?>> mergeableErrors, Formatter formatter) {
|
||||||
|
List<List<Object>> sourcesList = new ArrayList<>();
|
||||||
|
sourcesList.add(ImmutableList.of(original.getSource()));
|
||||||
|
sourcesList.add(ImmutableList.of(binding.getSource()));
|
||||||
|
sourcesList.addAll(
|
||||||
|
mergeableErrors.stream()
|
||||||
|
.map(e -> ((BindingAlreadySetError) e).binding.getSource())
|
||||||
|
.map(ImmutableList::of)
|
||||||
|
.collect(Collectors.toList()));
|
||||||
|
formatter.format("%n%s%n", Messages.bold("Bound at:"));
|
||||||
|
for (int i = 0; i < sourcesList.size(); i++) {
|
||||||
|
ErrorFormatter.formatSources(i + 1, sourcesList.get(i), formatter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BindingAlreadySetError withSources(List<Object> newSources) {
|
||||||
|
return new BindingAlreadySetError(binding, original, newSources);
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,24 +29,29 @@ public class BindingBuilder<T> extends AbstractBindingBuilder<T>
|
||||||
super(binder, elements, source, key);
|
super(binder, elements, source, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public BindingBuilder<T> annotatedWith(Class<? extends Annotation> annotationType) {
|
public BindingBuilder<T> annotatedWith(Class<? extends Annotation> annotationType) {
|
||||||
annotatedWithInternal(annotationType);
|
annotatedWithInternal(annotationType);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public BindingBuilder<T> annotatedWith(Annotation annotation) {
|
public BindingBuilder<T> annotatedWith(Annotation annotation) {
|
||||||
annotatedWithInternal(annotation);
|
annotatedWithInternal(annotation);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public BindingBuilder<T> to(Class<? extends T> implementation) {
|
public BindingBuilder<T> to(Class<? extends T> implementation) {
|
||||||
return to(Key.get(implementation));
|
return to(Key.get(implementation));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public BindingBuilder<T> to(TypeLiteral<? extends T> implementation) {
|
public BindingBuilder<T> to(TypeLiteral<? extends T> implementation) {
|
||||||
return to(Key.get(implementation));
|
return to(Key.get(implementation));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public BindingBuilder<T> to(Key<? extends T> linkedKey) {
|
public BindingBuilder<T> to(Key<? extends T> linkedKey) {
|
||||||
checkNotNull(linkedKey, "linkedKey");
|
checkNotNull(linkedKey, "linkedKey");
|
||||||
checkNotTargetted();
|
checkNotTargetted();
|
||||||
|
@ -56,6 +61,7 @@ public class BindingBuilder<T> extends AbstractBindingBuilder<T>
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void toInstance(T instance) {
|
public void toInstance(T instance) {
|
||||||
checkNotTargetted();
|
checkNotTargetted();
|
||||||
|
|
||||||
|
@ -79,10 +85,12 @@ public class BindingBuilder<T> extends AbstractBindingBuilder<T>
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
public BindingBuilder<T> toProvider(Provider<? extends T> provider) {
|
public BindingBuilder<T> toProvider(Provider<? extends T> provider) {
|
||||||
return toProvider((javax.inject.Provider<T>) provider);
|
return toProvider((javax.inject.Provider<T>) provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public BindingBuilder<T> toProvider(javax.inject.Provider<? extends T> provider) {
|
public BindingBuilder<T> toProvider(javax.inject.Provider<? extends T> provider) {
|
||||||
checkNotNull(provider, "provider");
|
checkNotNull(provider, "provider");
|
||||||
checkNotTargetted();
|
checkNotTargetted();
|
||||||
|
@ -102,16 +110,19 @@ public class BindingBuilder<T> extends AbstractBindingBuilder<T>
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public BindingBuilder<T> toProvider(
|
public BindingBuilder<T> toProvider(
|
||||||
Class<? extends javax.inject.Provider<? extends T>> providerType) {
|
Class<? extends javax.inject.Provider<? extends T>> providerType) {
|
||||||
return toProvider(Key.get(providerType));
|
return toProvider(Key.get(providerType));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public BindingBuilder<T> toProvider(
|
public BindingBuilder<T> toProvider(
|
||||||
TypeLiteral<? extends javax.inject.Provider<? extends T>> providerType) {
|
TypeLiteral<? extends javax.inject.Provider<? extends T>> providerType) {
|
||||||
return toProvider(Key.get(providerType));
|
return toProvider(Key.get(providerType));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public BindingBuilder<T> toProvider(
|
public BindingBuilder<T> toProvider(
|
||||||
Key<? extends javax.inject.Provider<? extends T>> providerKey) {
|
Key<? extends javax.inject.Provider<? extends T>> providerKey) {
|
||||||
checkNotNull(providerKey, "providerKey");
|
checkNotNull(providerKey, "providerKey");
|
||||||
|
@ -123,10 +134,12 @@ public class BindingBuilder<T> extends AbstractBindingBuilder<T>
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public <S extends T> ScopedBindingBuilder toConstructor(Constructor<S> constructor) {
|
public <S extends T> ScopedBindingBuilder toConstructor(Constructor<S> constructor) {
|
||||||
return toConstructor(constructor, TypeLiteral.get(constructor.getDeclaringClass()));
|
return toConstructor(constructor, TypeLiteral.get(constructor.getDeclaringClass()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public <S extends T> ScopedBindingBuilder toConstructor(Constructor<S> constructor,
|
public <S extends T> ScopedBindingBuilder toConstructor(Constructor<S> constructor,
|
||||||
TypeLiteral<? extends S> type) {
|
TypeLiteral<? extends S> type) {
|
||||||
checkNotNull(constructor, "constructor");
|
checkNotNull(constructor, "constructor");
|
||||||
|
|
|
@ -34,14 +34,17 @@ public abstract class BindingImpl<T> implements Binding<T> {
|
||||||
this.scoping = scoping;
|
this.scoping = scoping;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Key<T> getKey() {
|
public Key<T> getKey() {
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Object getSource() {
|
public Object getSource() {
|
||||||
return source;
|
return source;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Provider<T> getProvider() {
|
public Provider<T> getProvider() {
|
||||||
if (provider == null) {
|
if (provider == null) {
|
||||||
if (injector == null) {
|
if (injector == null) {
|
||||||
|
@ -69,10 +72,12 @@ public abstract class BindingImpl<T> implements Binding<T> {
|
||||||
return this instanceof InstanceBinding;
|
return this instanceof InstanceBinding;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public <V> V acceptVisitor(ElementVisitor<V> visitor) {
|
public <V> V acceptVisitor(ElementVisitor<V> visitor) {
|
||||||
return visitor.visit(this);
|
return visitor.visit(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public <V> V acceptScopingVisitor(BindingScopingVisitor<V> visitor) {
|
public <V> V acceptScopingVisitor(BindingScopingVisitor<V> visitor) {
|
||||||
return scoping.acceptVisitor(visitor);
|
return scoping.acceptVisitor(visitor);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,6 @@ import com.google.inject.spi.ProviderBinding;
|
||||||
import com.google.inject.spi.ProviderInstanceBinding;
|
import com.google.inject.spi.ProviderInstanceBinding;
|
||||||
import com.google.inject.spi.ProviderKeyBinding;
|
import com.google.inject.spi.ProviderKeyBinding;
|
||||||
import com.google.inject.spi.UntargettedBinding;
|
import com.google.inject.spi.UntargettedBinding;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -25,8 +24,9 @@ final class BindingProcessor extends AbstractBindingProcessor {
|
||||||
|
|
||||||
private final Initializer initializer;
|
private final Initializer initializer;
|
||||||
|
|
||||||
BindingProcessor(Errors errors, Initializer initializer, ProcessedBindingData bindingData) {
|
BindingProcessor(
|
||||||
super(errors, bindingData);
|
Errors errors, Initializer initializer, ProcessedBindingData processedBindingData) {
|
||||||
|
super(errors, processedBindingData);
|
||||||
this.initializer = initializer;
|
this.initializer = initializer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,13 +49,22 @@ final class BindingProcessor extends AbstractBindingProcessor {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return command.acceptTargetVisitor(new Processor<T, Boolean>((BindingImpl<T>) command) {
|
return command.acceptTargetVisitor(
|
||||||
|
new Processor<T, Boolean>((BindingImpl<T>) command) {
|
||||||
@Override
|
@Override
|
||||||
public Boolean visit(ConstructorBinding<? extends T> binding) {
|
public Boolean visit(ConstructorBinding<? extends T> binding) {
|
||||||
prepareBinding();
|
prepareBinding();
|
||||||
try {
|
try {
|
||||||
ConstructorBindingImpl<T> onInjector = ConstructorBindingImpl.create(injector, key,
|
ConstructorBindingImpl<T> onInjector =
|
||||||
binding.getConstructor(), source, scoping, errors, false, false);
|
ConstructorBindingImpl.create(
|
||||||
|
injector,
|
||||||
|
key,
|
||||||
|
binding.getConstructor(),
|
||||||
|
source,
|
||||||
|
scoping,
|
||||||
|
errors,
|
||||||
|
false,
|
||||||
|
false);
|
||||||
scheduleInitialization(onInjector);
|
scheduleInitialization(onInjector);
|
||||||
putBinding(onInjector);
|
putBinding(onInjector);
|
||||||
} catch (ErrorsException e) {
|
} catch (ErrorsException e) {
|
||||||
|
@ -72,13 +81,15 @@ final class BindingProcessor extends AbstractBindingProcessor {
|
||||||
T instance = binding.getInstance();
|
T instance = binding.getInstance();
|
||||||
@SuppressWarnings("unchecked") // safe to cast to binding<T> because
|
@SuppressWarnings("unchecked") // safe to cast to binding<T> because
|
||||||
// the processor was constructed w/ it
|
// the processor was constructed w/ it
|
||||||
Initializable<T> ref = initializer.requestInjection(
|
Initializable<T> ref =
|
||||||
|
initializer.requestInjection(
|
||||||
injector, instance, (Binding<T>) binding, source, injectionPoints);
|
injector, instance, (Binding<T>) binding, source, injectionPoints);
|
||||||
ConstantFactory<? extends T> factory = new ConstantFactory<T>(ref);
|
ConstantFactory<? extends T> factory = new ConstantFactory<>(ref);
|
||||||
InternalFactory<? extends T> scopedFactory
|
InternalFactory<? extends T> scopedFactory =
|
||||||
= Scoping.scope(key, injector, factory, source, scoping);
|
Scoping.scope(key, injector, factory, source, scoping);
|
||||||
putBinding(new InstanceBindingImpl<T>(injector, key, source, scopedFactory, injectionPoints,
|
putBinding(
|
||||||
instance));
|
new InstanceBindingImpl<T>(
|
||||||
|
injector, key, source, scopedFactory, injectionPoints, instance));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,35 +97,54 @@ final class BindingProcessor extends AbstractBindingProcessor {
|
||||||
public Boolean visit(ProviderInstanceBinding<? extends T> binding) {
|
public Boolean visit(ProviderInstanceBinding<? extends T> binding) {
|
||||||
prepareBinding();
|
prepareBinding();
|
||||||
javax.inject.Provider<? extends T> provider = binding.getUserSuppliedProvider();
|
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();
|
Set<InjectionPoint> injectionPoints = binding.getInjectionPoints();
|
||||||
Initializable<? extends javax.inject.Provider<? extends T>> initializable =
|
Initializable<? extends javax.inject.Provider<? extends T>> initializable =
|
||||||
initializer.<javax.inject.Provider<? extends T>>requestInjection(
|
initializer.<javax.inject.Provider<? extends T>>requestInjection(
|
||||||
injector, provider, null, source, injectionPoints);
|
injector, provider, null, source, injectionPoints);
|
||||||
// always visited with Binding<T>
|
// always visited with Binding<T>
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
InternalFactory<T> factory = new InternalFactoryToInitializableAdapter<T>(
|
InternalFactory<T> factory =
|
||||||
initializable, source,
|
new InternalFactoryToInitializableAdapter<T>(
|
||||||
|
initializable,
|
||||||
|
source,
|
||||||
injector.provisionListenerStore.get((ProviderInstanceBinding<T>) binding));
|
injector.provisionListenerStore.get((ProviderInstanceBinding<T>) binding));
|
||||||
InternalFactory<? extends T> scopedFactory
|
InternalFactory<? extends T> scopedFactory =
|
||||||
= Scoping.scope(key, injector, factory, source, scoping);
|
Scoping.scope(key, injector, factory, source, scoping);
|
||||||
putBinding(new ProviderInstanceBindingImpl<T>(injector, key, source, scopedFactory, scoping,
|
putBinding(
|
||||||
provider, injectionPoints));
|
new ProviderInstanceBindingImpl<T>(
|
||||||
|
injector, key, source, scopedFactory, scoping, provider, injectionPoints));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Boolean visit(ProviderKeyBinding<? extends T> binding) {
|
public Boolean visit(ProviderKeyBinding<? extends T> binding) {
|
||||||
prepareBinding();
|
prepareBinding();
|
||||||
Key<? extends javax.inject.Provider<? extends T>> providerKey = binding.getProviderKey();
|
Key<? extends javax.inject.Provider<? extends T>> providerKey =
|
||||||
|
binding.getProviderKey();
|
||||||
// always visited with Binding<T>
|
// always visited with Binding<T>
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
BoundProviderFactory<T> boundProviderFactory = new BoundProviderFactory<T>(
|
BoundProviderFactory<T> boundProviderFactory =
|
||||||
injector, providerKey, source,
|
new BoundProviderFactory<T>(
|
||||||
|
injector,
|
||||||
|
providerKey,
|
||||||
|
source,
|
||||||
injector.provisionListenerStore.get((ProviderKeyBinding<T>) binding));
|
injector.provisionListenerStore.get((ProviderKeyBinding<T>) binding));
|
||||||
bindingData.addCreationListener(boundProviderFactory);
|
processedBindingData.addCreationListener(boundProviderFactory);
|
||||||
InternalFactory<? extends T> scopedFactory = Scoping.scope(
|
InternalFactory<? extends T> scopedFactory =
|
||||||
key, injector, (InternalFactory<? extends T>) boundProviderFactory, source, scoping);
|
Scoping.scope(
|
||||||
putBinding(new LinkedProviderBindingImpl<T>(
|
key,
|
||||||
|
injector,
|
||||||
|
(InternalFactory<? extends T>) boundProviderFactory,
|
||||||
|
source,
|
||||||
|
scoping);
|
||||||
|
putBinding(
|
||||||
|
new LinkedProviderBindingImpl<T>(
|
||||||
injector, key, source, scopedFactory, scoping, providerKey));
|
injector, key, source, scopedFactory, scoping, providerKey));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -124,18 +154,44 @@ final class BindingProcessor extends AbstractBindingProcessor {
|
||||||
prepareBinding();
|
prepareBinding();
|
||||||
Key<? extends T> linkedKey = binding.getLinkedKey();
|
Key<? extends T> linkedKey = binding.getLinkedKey();
|
||||||
if (key.equals(linkedKey)) {
|
if (key.equals(linkedKey)) {
|
||||||
errors.recursiveBinding();
|
// TODO: b/168656899 check for transitive recursive binding
|
||||||
|
errors.recursiveBinding(key, linkedKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
FactoryProxy<T> factory = new FactoryProxy<T>(injector, key, linkedKey, source);
|
FactoryProxy<T> factory = new FactoryProxy<>(injector, key, linkedKey, source);
|
||||||
bindingData.addCreationListener(factory);
|
processedBindingData.addCreationListener(factory);
|
||||||
InternalFactory<? extends T> scopedFactory
|
InternalFactory<? extends T> scopedFactory =
|
||||||
= Scoping.scope(key, injector, factory, source, scoping);
|
Scoping.scope(key, injector, factory, source, scoping);
|
||||||
putBinding(
|
putBinding(
|
||||||
new LinkedBindingImpl<T>(injector, key, source, scopedFactory, scoping, linkedKey));
|
new LinkedBindingImpl<T>(injector, key, source, scopedFactory, scoping, linkedKey));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Handle ProviderMethods specially. */
|
||||||
|
private Boolean visitInternalProviderInstanceBindingFactory(
|
||||||
|
InternalProviderInstanceBindingImpl.Factory<T> provider) {
|
||||||
|
InternalProviderInstanceBindingImpl<T> binding =
|
||||||
|
new InternalProviderInstanceBindingImpl<T>(
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Boolean visit(UntargettedBinding<? extends T> untargetted) {
|
public Boolean visit(UntargettedBinding<? extends T> untargetted) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -172,9 +228,14 @@ final class BindingProcessor extends AbstractBindingProcessor {
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T> void bindExposed(PrivateElements privateElements, Key<T> key) {
|
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);
|
processedBindingData.addCreationListener(exposedKeyFactory);
|
||||||
putBinding(new ExposedBindingImpl<T>(
|
putBinding(
|
||||||
injector, privateElements.getExposedSource(key), key, exposedKeyFactory, privateElements));
|
new ExposedBindingImpl<T>(
|
||||||
|
injector,
|
||||||
|
privateElements.getExposedSource(key),
|
||||||
|
key,
|
||||||
|
exposedKeyFactory,
|
||||||
|
privateElements));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,16 +6,17 @@ import com.google.inject.spi.Dependency;
|
||||||
|
|
||||||
import javax.inject.Provider;
|
import javax.inject.Provider;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delegates to a custom factory which is also bound in the injector.
|
* Delegates to a custom factory which is also bound in the injector.
|
||||||
*/
|
*/
|
||||||
final class BoundProviderFactory<T> extends ProviderInternalFactory<T> implements CreationListener {
|
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 ProvisionListenerStackCallback<T> provisionCallback;
|
||||||
|
|
||||||
private final InjectorImpl injector;
|
private final InjectorImpl injector;
|
||||||
|
|
||||||
private InternalFactory<? extends javax.inject.Provider<? extends T>> providerFactory;
|
private InternalFactory<? extends javax.inject.Provider<? extends T>> providerFactory;
|
||||||
|
|
||||||
BoundProviderFactory(
|
BoundProviderFactory(
|
||||||
|
@ -24,11 +25,12 @@ final class BoundProviderFactory<T> extends ProviderInternalFactory<T> implement
|
||||||
Object source,
|
Object source,
|
||||||
ProvisionListenerStackCallback<T> provisionCallback) {
|
ProvisionListenerStackCallback<T> provisionCallback) {
|
||||||
super(source);
|
super(source);
|
||||||
this.provisionCallback = checkNotNull(provisionCallback, "provisionCallback");
|
this.provisionCallback = provisionCallback;
|
||||||
this.injector = injector;
|
this.injector = injector;
|
||||||
this.providerKey = providerKey;
|
this.providerKey = providerKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void notify(Errors errors) {
|
public void notify(Errors errors) {
|
||||||
try {
|
try {
|
||||||
providerFactory = injector.getInternalFactory(providerKey, errors.withSource(source), JitLimitation.NEW_OR_EXISTING_JIT);
|
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)
|
@Override
|
||||||
throws ErrorsException {
|
public T get(InternalContext context, Dependency<?> dependency, boolean linked)
|
||||||
|
throws InternalProvisionException {
|
||||||
context.pushState(providerKey, source);
|
context.pushState(providerKey, source);
|
||||||
try {
|
try {
|
||||||
errors = errors.withSource(providerKey);
|
javax.inject.Provider<? extends T> provider = providerFactory.get(context, dependency, true);
|
||||||
javax.inject.Provider<? extends T> provider = providerFactory.get(errors, context, dependency, true);
|
return circularGet(provider, context, dependency, provisionCallback);
|
||||||
return circularGet(provider, errors, context, dependency, provisionCallback);
|
} catch (InternalProvisionException ipe) {
|
||||||
|
throw ipe.addSource(providerKey);
|
||||||
} finally {
|
} finally {
|
||||||
context.popState();
|
context.popState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected T provision(Provider<? extends T> provider, Errors errors, Dependency<?> dependency,
|
protected T provision(Provider<? extends T> provider, Dependency<?> dependency,
|
||||||
ConstructionContext<T> constructionContext) throws ErrorsException {
|
ConstructionContext<T> constructionContext) throws InternalProvisionException {
|
||||||
try {
|
try {
|
||||||
return super.provision(provider, errors, dependency, constructionContext);
|
return super.provision(provider, dependency, constructionContext);
|
||||||
} catch (RuntimeException userException) {
|
} catch (RuntimeException userException) {
|
||||||
throw errors.errorInProvider(userException).toException();
|
throw InternalProvisionException.errorInProvider(userException);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.Streams;
|
||||||
|
import com.google.inject.Key;
|
||||||
|
import com.google.inject.spi.ErrorDetail;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Formatter;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error reported by Guice when a key is already bound in one or more child injectors or private
|
||||||
|
* modules.
|
||||||
|
*/
|
||||||
|
final class ChildBindingAlreadySetError extends InternalErrorDetail<ChildBindingAlreadySetError> {
|
||||||
|
private final Key<?> key;
|
||||||
|
private final ImmutableList<Object> existingSources;
|
||||||
|
|
||||||
|
ChildBindingAlreadySetError(Key<?> key, Iterable<Object> existingSoruces, List<Object> sources) {
|
||||||
|
super(
|
||||||
|
ErrorId.CHILD_BINDING_ALREADY_SET,
|
||||||
|
String.format(
|
||||||
|
"Unable to create binding for %s because it was already configured on one or more"
|
||||||
|
+ " child injectors or private modules.",
|
||||||
|
Messages.convert(key)),
|
||||||
|
sources,
|
||||||
|
null);
|
||||||
|
this.key = key;
|
||||||
|
// Can't use ImmutableList.toImmutableList here because of b/156759807.
|
||||||
|
this.existingSources =
|
||||||
|
ImmutableList.copyOf(
|
||||||
|
Streams.stream(existingSoruces)
|
||||||
|
.map(source -> source == null ? "" : source)
|
||||||
|
.collect(Collectors.toList()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isMergeable(ErrorDetail<?> otherError) {
|
||||||
|
return otherError instanceof ChildBindingAlreadySetError
|
||||||
|
&& ((ChildBindingAlreadySetError) otherError).key.equals(this.key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void formatDetail(List<ErrorDetail<?>> mergeableErrors, Formatter formatter) {
|
||||||
|
formatter.format("%n%s%n", Messages.bold("Bound at:"));
|
||||||
|
int index = 1;
|
||||||
|
for (Object source : existingSources) {
|
||||||
|
formatter.format("%-2s: ", index++);
|
||||||
|
if (source.equals("")) {
|
||||||
|
formatter.format("as a just-in-time binding%n");
|
||||||
|
} else {
|
||||||
|
new SourceFormatter(source, formatter, /* omitPreposition= */ true).format();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
List<List<Object>> sourcesList = new ArrayList<>();
|
||||||
|
sourcesList.add(getSources());
|
||||||
|
mergeableErrors.forEach(error -> sourcesList.add(error.getSources()));
|
||||||
|
|
||||||
|
List<List<Object>> filteredSources =
|
||||||
|
sourcesList.stream()
|
||||||
|
.map(this::trimSource)
|
||||||
|
.filter(list -> !list.isEmpty())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
if (!filteredSources.isEmpty()) {
|
||||||
|
formatter.format("%n%s%n", Messages.bold("Requested by:"));
|
||||||
|
for (int i = 0; i < sourcesList.size(); i++) {
|
||||||
|
ErrorFormatter.formatSources(i + 1, sourcesList.get(i), formatter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(b/151482394): Detect if the key was bound in any PrivateModule and suggest exposing the
|
||||||
|
// key in those cases.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChildBindingAlreadySetError withSources(List<Object> newSources) {
|
||||||
|
return new ChildBindingAlreadySetError(key, existingSources, newSources);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Omit the key itself in the source list since the information is redundant. */
|
||||||
|
private List<Object> trimSource(List<Object> sources) {
|
||||||
|
return sources.stream().filter(source -> !source.equals(this.key)).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,7 +6,6 @@ import com.google.inject.Key;
|
||||||
import com.google.inject.binder.AnnotatedConstantBindingBuilder;
|
import com.google.inject.binder.AnnotatedConstantBindingBuilder;
|
||||||
import com.google.inject.binder.ConstantBindingBuilder;
|
import com.google.inject.binder.ConstantBindingBuilder;
|
||||||
import com.google.inject.spi.Element;
|
import com.google.inject.spi.Element;
|
||||||
import com.google.inject.spi.InjectionPoint;
|
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -24,56 +23,69 @@ public final class ConstantBindingBuilderImpl<T>
|
||||||
super(binder, elements, source, (Key<T>) NULL_KEY);
|
super(binder, elements, source, (Key<T>) NULL_KEY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public ConstantBindingBuilder annotatedWith(Class<? extends Annotation> annotationType) {
|
public ConstantBindingBuilder annotatedWith(Class<? extends Annotation> annotationType) {
|
||||||
annotatedWithInternal(annotationType);
|
annotatedWithInternal(annotationType);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public ConstantBindingBuilder annotatedWith(Annotation annotation) {
|
public ConstantBindingBuilder annotatedWith(Annotation annotation) {
|
||||||
annotatedWithInternal(annotation);
|
annotatedWithInternal(annotation);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void to(final String value) {
|
public void to(final String value) {
|
||||||
toConstant(String.class, value);
|
toConstant(String.class, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void to(final int value) {
|
public void to(final int value) {
|
||||||
toConstant(Integer.class, value);
|
toConstant(Integer.class, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void to(final long value) {
|
public void to(final long value) {
|
||||||
toConstant(Long.class, value);
|
toConstant(Long.class, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void to(final boolean value) {
|
public void to(final boolean value) {
|
||||||
toConstant(Boolean.class, value);
|
toConstant(Boolean.class, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void to(final double value) {
|
public void to(final double value) {
|
||||||
toConstant(Double.class, value);
|
toConstant(Double.class, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void to(final float value) {
|
public void to(final float value) {
|
||||||
toConstant(Float.class, value);
|
toConstant(Float.class, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void to(final short value) {
|
public void to(final short value) {
|
||||||
toConstant(Short.class, value);
|
toConstant(Short.class, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void to(final char value) {
|
public void to(final char value) {
|
||||||
toConstant(Character.class, value);
|
toConstant(Character.class, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void to(final byte value) {
|
public void to(final byte value) {
|
||||||
toConstant(Byte.class, value);
|
toConstant(Byte.class, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void to(final Class<?> value) {
|
public void to(final Class<?> value) {
|
||||||
toConstant(Class.class, value);
|
toConstant(Class.class, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public <E extends Enum<E>> void to(final E value) {
|
public <E extends Enum<E>> void to(final E value) {
|
||||||
toConstant(value.getDeclaringClass(), value);
|
toConstant(value.getDeclaringClass(), value);
|
||||||
}
|
}
|
||||||
|
@ -105,7 +117,7 @@ public final class ConstantBindingBuilderImpl<T>
|
||||||
}
|
}
|
||||||
|
|
||||||
setBinding(new InstanceBindingImpl<T>(
|
setBinding(new InstanceBindingImpl<T>(
|
||||||
base.getSource(), key, base.getScoping(), ImmutableSet.<InjectionPoint>of(), instanceAsT));
|
base.getSource(), key, base.getScoping(), ImmutableSet.of(), instanceAsT));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -7,15 +7,17 @@ final class ConstantFactory<T> implements InternalFactory<T> {
|
||||||
|
|
||||||
private final Initializable<T> initializable;
|
private final Initializable<T> initializable;
|
||||||
|
|
||||||
public ConstantFactory(Initializable<T> initializable) {
|
ConstantFactory(Initializable<T> initializable) {
|
||||||
this.initializable = initializable;
|
this.initializable = initializable;
|
||||||
}
|
}
|
||||||
|
|
||||||
public T get(Errors errors, InternalContext context, Dependency dependency, boolean linked)
|
@Override
|
||||||
throws ErrorsException {
|
public T get(InternalContext context, Dependency<?> dependency, boolean linked)
|
||||||
return initializable.get(errors);
|
throws InternalProvisionException {
|
||||||
|
return initializable.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return MoreObjects.toStringHelper(ConstantFactory.class)
|
return MoreObjects.toStringHelper(ConstantFactory.class)
|
||||||
.add("value", initializable)
|
.add("value", initializable)
|
||||||
|
|
|
@ -41,26 +41,26 @@ final class ConstructionContext<T> {
|
||||||
invocationHandlers = null;
|
invocationHandlers = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object createProxy(Errors errors, InjectorOptions injectorOptions,
|
public Object createProxy(InjectorOptions injectorOptions,
|
||||||
Class<?> expectedType) throws ErrorsException {
|
Class<?> expectedType) throws InternalProvisionException {
|
||||||
if (injectorOptions.disableCircularProxies) {
|
if (injectorOptions.disableCircularProxies) {
|
||||||
throw errors.circularProxiesDisabled(expectedType).toException();
|
throw InternalProvisionException.circularDependenciesDisabled(expectedType);
|
||||||
}
|
}
|
||||||
if (!expectedType.isInterface()) {
|
if (!expectedType.isInterface()) {
|
||||||
throw errors.cannotSatisfyCircularDependency(expectedType).toException();
|
throw InternalProvisionException.cannotProxyClass(expectedType);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (invocationHandlers == null) {
|
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);
|
invocationHandlers.add(invocationHandler);
|
||||||
|
|
||||||
ClassLoader classLoader = expectedType.getClass().getClassLoader() != null ?
|
ClassLoader classLoader = expectedType.getClass().getClassLoader() != null ?
|
||||||
expectedType.getClass().getClassLoader() : ClassLoader.getSystemClassLoader();
|
expectedType.getClass().getClassLoader() : ClassLoader.getSystemClassLoader();
|
||||||
return expectedType.cast(Proxy.newProxyInstance(classLoader,
|
return expectedType.cast(Proxy.newProxyInstance(classLoader,
|
||||||
new Class[]{expectedType, CircularDependencyProxy.class}, invocationHandler));
|
new Class<?>[]{expectedType, CircularDependencyProxy.class}, invocationHandler));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setProxyDelegates(T delegate) {
|
public void setProxyDelegates(T delegate) {
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
package com.google.inject.internal;
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkState;
|
||||||
|
import static com.google.inject.internal.Annotations.findScopeAnnotation;
|
||||||
|
import static com.google.inject.internal.GuiceInternal.GUICE_INTERNAL;
|
||||||
|
import static com.google.inject.spi.Elements.withTrustedSource;
|
||||||
|
|
||||||
import com.google.common.base.MoreObjects;
|
import com.google.common.base.MoreObjects;
|
||||||
import com.google.common.base.Objects;
|
import com.google.common.base.Objects;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.inject.Binder;
|
import com.google.inject.Binder;
|
||||||
import com.google.inject.ConfigurationException;
|
import com.google.inject.ConfigurationException;
|
||||||
import com.google.inject.Inject;
|
|
||||||
import com.google.inject.Key;
|
import com.google.inject.Key;
|
||||||
import com.google.inject.TypeLiteral;
|
import com.google.inject.TypeLiteral;
|
||||||
import com.google.inject.internal.util.Classes;
|
import com.google.inject.internal.util.Classes;
|
||||||
|
@ -13,59 +17,70 @@ import com.google.inject.spi.BindingTargetVisitor;
|
||||||
import com.google.inject.spi.ConstructorBinding;
|
import com.google.inject.spi.ConstructorBinding;
|
||||||
import com.google.inject.spi.Dependency;
|
import com.google.inject.spi.Dependency;
|
||||||
import com.google.inject.spi.InjectionPoint;
|
import com.google.inject.spi.InjectionPoint;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkState;
|
|
||||||
import static com.google.inject.internal.Annotations.findScopeAnnotation;
|
|
||||||
|
|
||||||
final class ConstructorBindingImpl<T> extends BindingImpl<T>
|
final class ConstructorBindingImpl<T> extends BindingImpl<T>
|
||||||
implements ConstructorBinding<T>, DelayedInitialize {
|
implements ConstructorBinding<T>, DelayedInitialize {
|
||||||
|
|
||||||
private final Factory<T> factory;
|
private final Factory<T> factory;
|
||||||
private final InjectionPoint constructorInjectionPoint;
|
private final InjectionPoint constructorInjectionPoint;
|
||||||
|
|
||||||
private ConstructorBindingImpl(InjectorImpl injector, Key<T> key, Object source,
|
private ConstructorBindingImpl(
|
||||||
InternalFactory<? extends T> scopedFactory, Scoping scoping, Factory<T> factory,
|
InjectorImpl injector,
|
||||||
|
Key<T> key,
|
||||||
|
Object source,
|
||||||
|
InternalFactory<? extends T> scopedFactory,
|
||||||
|
Scoping scoping,
|
||||||
|
Factory<T> factory,
|
||||||
InjectionPoint constructorInjectionPoint) {
|
InjectionPoint constructorInjectionPoint) {
|
||||||
super(injector, key, source, scopedFactory, scoping);
|
super(injector, key, source, scopedFactory, scoping);
|
||||||
this.factory = factory;
|
this.factory = factory;
|
||||||
this.constructorInjectionPoint = constructorInjectionPoint;
|
this.constructorInjectionPoint = constructorInjectionPoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConstructorBindingImpl(Key<T> key, Object source, Scoping scoping,
|
public ConstructorBindingImpl(
|
||||||
InjectionPoint constructorInjectionPoint, Set<InjectionPoint> injectionPoints) {
|
Key<T> key,
|
||||||
|
Object source,
|
||||||
|
Scoping scoping,
|
||||||
|
InjectionPoint constructorInjectionPoint,
|
||||||
|
Set<InjectionPoint> injectionPoints) {
|
||||||
super(source, key, scoping);
|
super(source, key, scoping);
|
||||||
this.factory = new Factory<T>(false, key);
|
this.factory = new Factory<>(false, key);
|
||||||
ConstructionProxy<T> constructionProxy
|
ConstructionProxy<T> constructionProxy =
|
||||||
= new DefaultConstructionProxyFactory<T>(constructorInjectionPoint).create();
|
new DefaultConstructionProxyFactory<T>(constructorInjectionPoint).create();
|
||||||
this.constructorInjectionPoint = constructorInjectionPoint;
|
this.constructorInjectionPoint = constructorInjectionPoint;
|
||||||
factory.constructorInjector = new ConstructorInjector<T>(
|
factory.constructorInjector =
|
||||||
injectionPoints, constructionProxy, null, null);
|
new ConstructorInjector<T>(injectionPoints, constructionProxy, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param constructorInjector the constructor to use, or {@code null} to use the default.
|
* @param constructorInjector the constructor to use, or {@code null} to use the default.
|
||||||
* @param failIfNotLinked true if this ConstructorBindingImpl's InternalFactory should
|
* @param failIfNotLinked true if this ConstructorBindingImpl's InternalFactory should only
|
||||||
* only succeed if retrieved from a linked binding
|
* succeed if retrieved from a linked binding
|
||||||
*/
|
*/
|
||||||
static <T> ConstructorBindingImpl<T> create(InjectorImpl injector, Key<T> key,
|
static <T> ConstructorBindingImpl<T> create(
|
||||||
InjectionPoint constructorInjector, Object source, Scoping scoping, Errors errors,
|
InjectorImpl injector,
|
||||||
boolean failIfNotLinked, boolean failIfNotExplicit)
|
Key<T> key,
|
||||||
|
InjectionPoint constructorInjector,
|
||||||
|
Object source,
|
||||||
|
Scoping scoping,
|
||||||
|
Errors errors,
|
||||||
|
boolean failIfNotLinked,
|
||||||
|
boolean atInjectRequired)
|
||||||
throws ErrorsException {
|
throws ErrorsException {
|
||||||
int numErrors = errors.size();
|
int numErrors = errors.size();
|
||||||
|
|
||||||
@SuppressWarnings("unchecked") // constructorBinding guarantees type is consistent
|
Class<?> rawType =
|
||||||
Class<? super T> rawType = constructorInjector == null
|
constructorInjector == null
|
||||||
? key.getTypeLiteral().getRawType()
|
? key.getTypeLiteral().getRawType()
|
||||||
: (Class) constructorInjector.getDeclaringType().getRawType();
|
: constructorInjector.getDeclaringType().getRawType();
|
||||||
|
|
||||||
// We can't inject abstract classes.
|
// We can't inject abstract classes.
|
||||||
if (Modifier.isAbstract(rawType.getModifiers())) {
|
if (Modifier.isAbstract(rawType.getModifiers())) {
|
||||||
errors.missingImplementation(key);
|
errors.missingImplementationWithHint(key, injector);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error: Inner class.
|
// Error: Inner class.
|
||||||
|
@ -78,10 +93,8 @@ final class ConstructorBindingImpl<T> extends BindingImpl<T>
|
||||||
// Find a constructor annotated @Inject
|
// Find a constructor annotated @Inject
|
||||||
if (constructorInjector == null) {
|
if (constructorInjector == null) {
|
||||||
try {
|
try {
|
||||||
constructorInjector = InjectionPoint.forConstructorOf(key.getTypeLiteral());
|
constructorInjector =
|
||||||
if (failIfNotExplicit && !hasAtInject((Constructor) constructorInjector.getMember())) {
|
InjectionPoint.forConstructorOf(key.getTypeLiteral(), atInjectRequired);
|
||||||
errors.atInjectRequired(rawType);
|
|
||||||
}
|
|
||||||
} catch (ConfigurationException e) {
|
} catch (ConfigurationException e) {
|
||||||
throw errors.merge(e.getErrorMessages()).toException();
|
throw errors.merge(e.getErrorMessages()).toException();
|
||||||
}
|
}
|
||||||
|
@ -92,47 +105,36 @@ final class ConstructorBindingImpl<T> extends BindingImpl<T>
|
||||||
Class<?> annotatedType = constructorInjector.getMember().getDeclaringClass();
|
Class<?> annotatedType = constructorInjector.getMember().getDeclaringClass();
|
||||||
Class<? extends Annotation> scopeAnnotation = findScopeAnnotation(errors, annotatedType);
|
Class<? extends Annotation> scopeAnnotation = findScopeAnnotation(errors, annotatedType);
|
||||||
if (scopeAnnotation != null) {
|
if (scopeAnnotation != null) {
|
||||||
scoping = Scoping.makeInjectable(Scoping.forAnnotation(scopeAnnotation),
|
scoping =
|
||||||
injector, errors.withSource(rawType));
|
Scoping.makeInjectable(
|
||||||
|
Scoping.forAnnotation(scopeAnnotation), injector, errors.withSource(rawType));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
errors.throwIfNewErrors(numErrors);
|
errors.throwIfNewErrors(numErrors);
|
||||||
|
|
||||||
Factory<T> factoryFactory = new Factory<T>(failIfNotLinked, key);
|
Factory<T> factoryFactory = new Factory<>(failIfNotLinked, key);
|
||||||
InternalFactory<? extends T> scopedFactory
|
InternalFactory<? extends T> scopedFactory =
|
||||||
= Scoping.scope(key, injector, factoryFactory, source, scoping);
|
Scoping.scope(key, injector, factoryFactory, source, scoping);
|
||||||
|
|
||||||
return new ConstructorBindingImpl<T>(
|
return new ConstructorBindingImpl<T>(
|
||||||
injector, key, source, scopedFactory, scoping, factoryFactory, constructorInjector);
|
injector, key, source, scopedFactory, scoping, factoryFactory, constructorInjector);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Returns true if the inject annotation is on the constructor.
|
|
||||||
*/
|
|
||||||
private static boolean hasAtInject(Constructor cxtor) {
|
|
||||||
return cxtor.isAnnotationPresent(Inject.class)
|
|
||||||
|| cxtor.isAnnotationPresent(javax.inject.Inject.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked") // the result type always agrees with the ConstructorInjector type
|
@SuppressWarnings("unchecked") // the result type always agrees with the ConstructorInjector type
|
||||||
public void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
|
public void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
|
||||||
factory.constructorInjector =
|
factory.constructorInjector =
|
||||||
(ConstructorInjector<T>) injector.constructors.get(constructorInjectionPoint, errors);
|
(ConstructorInjector<T>) injector.constructors.get(constructorInjectionPoint, errors);
|
||||||
factory.provisionCallback =
|
factory.provisionCallback = injector.provisionListenerStore.get(this);
|
||||||
injector.provisionListenerStore.get(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** True if this binding has been initialized and is ready for use. */
|
||||||
* True if this binding has been initialized and is ready for use.
|
|
||||||
*/
|
|
||||||
boolean isInitialized() {
|
boolean isInitialized() {
|
||||||
return factory.constructorInjector != null;
|
return factory.constructorInjector != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Returns an injection point that can be used to clean up the constructor store. */
|
||||||
* Returns an injection point that can be used to clean up the constructor store.
|
|
||||||
*/
|
|
||||||
InjectionPoint getInternalConstructor() {
|
InjectionPoint getInternalConstructor() {
|
||||||
if (factory.constructorInjector != null) {
|
if (factory.constructorInjector != null) {
|
||||||
return factory.constructorInjector.getConstructionProxy().getInjectionPoint();
|
return factory.constructorInjector.getConstructionProxy().getInjectionPoint();
|
||||||
|
@ -141,44 +143,48 @@ final class ConstructorBindingImpl<T> extends BindingImpl<T>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Returns a set of dependencies that can be iterated over to clean up stray JIT bindings. */
|
||||||
* Returns a set of dependencies that can be iterated over to clean up stray JIT bindings.
|
|
||||||
*/
|
|
||||||
Set<Dependency<?>> getInternalDependencies() {
|
Set<Dependency<?>> getInternalDependencies() {
|
||||||
ImmutableSet.Builder<InjectionPoint> builder = ImmutableSet.builder();
|
ImmutableSet.Builder<InjectionPoint> builder = ImmutableSet.builder();
|
||||||
if (factory.constructorInjector == null) {
|
if (factory.constructorInjector == null) {
|
||||||
builder.add(constructorInjectionPoint);
|
builder.add(constructorInjectionPoint);
|
||||||
// If the below throws, it's OK -- we just ignore those dependencies, because no one
|
|
||||||
// could have used them anyway.
|
|
||||||
try {
|
try {
|
||||||
builder.addAll(InjectionPoint.forInstanceMethodsAndFields(constructorInjectionPoint.getDeclaringType()));
|
builder.addAll(
|
||||||
|
InjectionPoint.forInstanceMethodsAndFields(
|
||||||
|
constructorInjectionPoint.getDeclaringType()));
|
||||||
} catch (ConfigurationException ignored) {
|
} catch (ConfigurationException ignored) {
|
||||||
|
// This is OK -- we just ignore those dependencies, because no one could have used them
|
||||||
|
// anyway.
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
builder.add(getConstructor())
|
builder.add(getConstructor()).addAll(getInjectableMembers());
|
||||||
.addAll(getInjectableMembers());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Dependency.forInjectionPoints(builder.build());
|
return Dependency.forInjectionPoints(builder.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
|
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
|
||||||
checkState(factory.constructorInjector != null, "not initialized");
|
checkState(factory.constructorInjector != null, "not initialized");
|
||||||
return visitor.visit(this);
|
return visitor.visit(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public InjectionPoint getConstructor() {
|
public InjectionPoint getConstructor() {
|
||||||
checkState(factory.constructorInjector != null, "Binding is not ready");
|
checkState(factory.constructorInjector != null, "Binding is not ready");
|
||||||
return factory.constructorInjector.getConstructionProxy().getInjectionPoint();
|
return factory.constructorInjector.getConstructionProxy().getInjectionPoint();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Set<InjectionPoint> getInjectableMembers() {
|
public Set<InjectionPoint> getInjectableMembers() {
|
||||||
checkState(factory.constructorInjector != null, "Binding is not ready");
|
checkState(factory.constructorInjector != null, "Binding is not ready");
|
||||||
return factory.constructorInjector.getInjectableMembers();
|
return factory.constructorInjector.getInjectableMembers();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Set<Dependency<?>> getDependencies() {
|
public Set<Dependency<?>> getDependencies() {
|
||||||
return Dependency.forInjectionPoints(new ImmutableSet.Builder<InjectionPoint>()
|
return Dependency.forInjectionPoints(
|
||||||
|
new ImmutableSet.Builder<InjectionPoint>()
|
||||||
.add(getConstructor())
|
.add(getConstructor())
|
||||||
.addAll(getInjectableMembers())
|
.addAll(getInjectableMembers())
|
||||||
.build());
|
.build());
|
||||||
|
@ -196,11 +202,17 @@ final class ConstructorBindingImpl<T> extends BindingImpl<T>
|
||||||
null, key, getSource(), factory, getScoping(), factory, constructorInjectionPoint);
|
null, key, getSource(), factory, getScoping(), factory, constructorInjectionPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
@SuppressWarnings("unchecked") // the raw constructor member and declaring type always agree
|
@SuppressWarnings("unchecked") // the raw constructor member and declaring type always agree
|
||||||
public void applyTo(Binder binder) {
|
public void applyTo(Binder binder) {
|
||||||
InjectionPoint constructor = getConstructor();
|
InjectionPoint constructor = getConstructor();
|
||||||
getScoping().applyTo(binder.withSource(getSource()).bind(getKey()).toConstructor(
|
getScoping()
|
||||||
(Constructor) getConstructor().getMember(), (TypeLiteral) constructor.getDeclaringType()));
|
.applyTo(
|
||||||
|
withTrustedSource(GUICE_INTERNAL, binder, getSource())
|
||||||
|
.bind(getKey())
|
||||||
|
.toConstructor(
|
||||||
|
(Constructor) getConstructor().getMember(),
|
||||||
|
(TypeLiteral) constructor.getDeclaringType()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -240,19 +252,22 @@ final class ConstructorBindingImpl<T> extends BindingImpl<T>
|
||||||
this.key = key;
|
this.key = key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
|
public T get(InternalContext context, Dependency<?> dependency, boolean linked)
|
||||||
throws ErrorsException {
|
throws InternalProvisionException {
|
||||||
checkState(constructorInjector != null, "Constructor not ready");
|
ConstructorInjector<T> localInjector = constructorInjector;
|
||||||
|
if (localInjector == null) {
|
||||||
|
throw new IllegalStateException("Constructor not ready");
|
||||||
|
}
|
||||||
|
|
||||||
if (failIfNotLinked && !linked) {
|
if (!linked && failIfNotLinked) {
|
||||||
throw errors.jitDisabled(key).toException();
|
throw InternalProvisionException.jitDisabled(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This may not actually be safe because it could return a super type of T (if that's all the
|
// 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.
|
// client needs), but it should be OK in practice thanks to the wonders of erasure.
|
||||||
return (T) constructorInjector.construct(errors, context,
|
return (T) localInjector.construct(context, dependency, provisionCallback);
|
||||||
dependency.getKey().getTypeLiteral().getRawType(), provisionCallback);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package com.google.inject.internal;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.inject.internal.ProvisionListenerStackCallback.ProvisionCallback;
|
import com.google.inject.internal.ProvisionListenerStackCallback.ProvisionCallback;
|
||||||
|
import com.google.inject.spi.Dependency;
|
||||||
import com.google.inject.spi.InjectionPoint;
|
import com.google.inject.spi.InjectionPoint;
|
||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
@ -40,37 +41,39 @@ final class ConstructorInjector<T> {
|
||||||
* Construct an instance. Returns {@code Object} instead of {@code T} because
|
* Construct an instance. Returns {@code Object} instead of {@code T} because
|
||||||
* it may return a proxy.
|
* it may return a proxy.
|
||||||
*/
|
*/
|
||||||
Object construct(final Errors errors, final InternalContext context,
|
Object construct(final InternalContext context,
|
||||||
Class<?> expectedType,
|
Dependency<?> dependency,
|
||||||
ProvisionListenerStackCallback<T> provisionCallback)
|
ProvisionListenerStackCallback<T> provisionCallback)
|
||||||
throws ErrorsException {
|
throws InternalProvisionException {
|
||||||
final ConstructionContext<T> constructionContext = context.getConstructionContext(this);
|
final ConstructionContext<T> constructionContext = context.getConstructionContext(this);
|
||||||
|
|
||||||
// We have a circular reference between constructors. Return a proxy.
|
// We have a circular reference between constructors. Return a proxy.
|
||||||
if (constructionContext.isConstructing()) {
|
if (constructionContext.isConstructing()) {
|
||||||
// TODO (crazybob): if we can't proxy this object, can we proxy the other object?
|
// TODO (crazybob): if we can't proxy this object, can we proxy the other object?
|
||||||
return constructionContext.createProxy(
|
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,
|
// If we're re-entering this factory while injecting fields or methods,
|
||||||
// return the same instance. This prevents infinite loops.
|
// return the same instance. This prevents infinite loops.
|
||||||
T t = constructionContext.getCurrentReference();
|
T t = constructionContext.getCurrentReference();
|
||||||
if (t != null) {
|
if (t != null) {
|
||||||
|
if (context.getInjectorOptions().disableCircularProxies) {
|
||||||
|
throw InternalProvisionException.circularDependenciesDisabled(
|
||||||
|
dependency.getKey().getTypeLiteral().getRawType());
|
||||||
|
} else {
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
constructionContext.startConstruction();
|
constructionContext.startConstruction();
|
||||||
try {
|
try {
|
||||||
// Optimization: Don't go through the callback stack if we have no listeners.
|
// Optimization: Don't go through the callback stack if we have no listeners.
|
||||||
if (!provisionCallback.hasListeners()) {
|
if (provisionCallback == null) {
|
||||||
return provision(errors, context, constructionContext);
|
return provision(context, constructionContext);
|
||||||
} else {
|
} else {
|
||||||
return provisionCallback.provision(errors, context, new ProvisionCallback<T>() {
|
return provisionCallback.provision(context, () ->
|
||||||
public T call() throws ErrorsException {
|
provision(context, constructionContext));
|
||||||
return provision(errors, context, constructionContext);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
constructionContext.finishConstruction();
|
constructionContext.finishConstruction();
|
||||||
|
@ -80,12 +83,12 @@ final class ConstructorInjector<T> {
|
||||||
/**
|
/**
|
||||||
* Provisions a new T.
|
* Provisions a new T.
|
||||||
*/
|
*/
|
||||||
private T provision(Errors errors, InternalContext context,
|
private T provision(InternalContext context, ConstructionContext<T> constructionContext)
|
||||||
ConstructionContext<T> constructionContext) throws ErrorsException {
|
throws InternalProvisionException {
|
||||||
try {
|
try {
|
||||||
T t;
|
T t;
|
||||||
try {
|
try {
|
||||||
Object[] parameters = SingleParameterInjector.getAll(errors, context, parameterInjectors);
|
Object[] parameters = SingleParameterInjector.getAll(context, parameterInjectors);
|
||||||
t = constructionProxy.newInstance(parameters);
|
t = constructionProxy.newInstance(parameters);
|
||||||
constructionContext.setProxyDelegates(t);
|
constructionContext.setProxyDelegates(t);
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -94,17 +97,17 @@ final class ConstructorInjector<T> {
|
||||||
|
|
||||||
// Store reference. If an injector re-enters this factory, they'll get the same reference.
|
// Store reference. If an injector re-enters this factory, they'll get the same reference.
|
||||||
constructionContext.setCurrentReference(t);
|
constructionContext.setCurrentReference(t);
|
||||||
|
MembersInjectorImpl<T> localMembersInjector = membersInjector;
|
||||||
membersInjector.injectMembers(t, errors, context, false);
|
localMembersInjector.injectMembers(t, context, false);
|
||||||
membersInjector.notifyListeners(t, errors);
|
localMembersInjector.notifyListeners(t);
|
||||||
|
|
||||||
return t;
|
return t;
|
||||||
} catch (InvocationTargetException userException) {
|
} catch (InvocationTargetException userException) {
|
||||||
Throwable cause = userException.getCause() != null
|
Throwable cause = userException.getCause() != null
|
||||||
? userException.getCause()
|
? userException.getCause()
|
||||||
: userException;
|
: userException;
|
||||||
throw errors.withSource(constructionProxy.getInjectionPoint())
|
throw InternalProvisionException.errorInjectingConstructor(cause)
|
||||||
.errorInjectingConstructor(cause).toException();
|
.addSource(constructionProxy.getInjectionPoint());
|
||||||
} finally {
|
} finally {
|
||||||
constructionContext.removeCurrentReference();
|
constructionContext.removeCurrentReference();
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,8 @@ import com.google.inject.spi.InjectionPoint;
|
||||||
final class ConstructorInjectorStore {
|
final class ConstructorInjectorStore {
|
||||||
private final InjectorImpl injector;
|
private final InjectorImpl injector;
|
||||||
|
|
||||||
private final FailableCache<InjectionPoint, ConstructorInjector<?>> cache
|
private final FailableCache<InjectionPoint, ConstructorInjector<?>> cache =
|
||||||
= new FailableCache<InjectionPoint, ConstructorInjector<?>>() {
|
new FailableCache<>() {
|
||||||
@Override
|
@Override
|
||||||
protected ConstructorInjector<?> create(InjectionPoint constructorInjector, Errors errors)
|
protected ConstructorInjector<?> create(InjectionPoint constructorInjector, Errors errors)
|
||||||
throws ErrorsException {
|
throws ErrorsException {
|
||||||
|
@ -46,12 +46,12 @@ final class ConstructorInjectorStore {
|
||||||
throws ErrorsException {
|
throws ErrorsException {
|
||||||
int numErrorsBefore = errors.size();
|
int numErrorsBefore = errors.size();
|
||||||
|
|
||||||
SingleParameterInjector<?>[] constructorParameterInjectors
|
SingleParameterInjector<?>[] constructorParameterInjectors =
|
||||||
= injector.getParametersInjectors(injectionPoint.getDependencies(), errors);
|
injector.getParametersInjectors(injectionPoint.getDependencies(), errors);
|
||||||
|
|
||||||
@SuppressWarnings("unchecked") // the injector type agrees with the injection point type
|
@SuppressWarnings("unchecked") // the injector type agrees with the injection point type
|
||||||
MembersInjectorImpl<T> membersInjector = (MembersInjectorImpl<T>) injector.membersInjectorStore
|
MembersInjectorImpl<T> membersInjector = (MembersInjectorImpl<T>)
|
||||||
.get(injectionPoint.getDeclaringType(), errors);
|
injector.membersInjectorStore.get(injectionPoint.getDeclaringType(), errors);
|
||||||
|
|
||||||
ConstructionProxyFactory<T> factory = new DefaultConstructionProxyFactory<T>(injectionPoint);
|
ConstructionProxyFactory<T> factory = new DefaultConstructionProxyFactory<T>(injectionPoint);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
package com.google.inject.internal;
|
|
||||||
|
|
||||||
interface ContextualCallable<T> {
|
|
||||||
T call(InternalContext context) throws ErrorsException;
|
|
||||||
}
|
|
|
@ -1,7 +1,6 @@
|
||||||
package com.google.inject.internal;
|
package com.google.inject.internal;
|
||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.base.Supplier;
|
|
||||||
import com.google.common.collect.ImmutableListMultimap;
|
import com.google.common.collect.ImmutableListMultimap;
|
||||||
import com.google.common.collect.LinkedHashMultimap;
|
import com.google.common.collect.LinkedHashMultimap;
|
||||||
import com.google.common.collect.ListMultimap;
|
import com.google.common.collect.ListMultimap;
|
||||||
|
@ -12,7 +11,6 @@ import com.google.common.collect.Multimaps;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.locks.Lock;
|
import java.util.concurrent.locks.Lock;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
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 no cycle is detected performance is O(threads creating singletons),
|
||||||
* in case cycle is detected performance is O(singleton locks).
|
* in case cycle is detected performance is O(singleton locks).
|
||||||
*/
|
*/
|
||||||
ListMultimap<Long, ID> lockOrDetectPotentialLocksCycle();
|
ListMultimap<Thread, ID> lockOrDetectPotentialLocksCycle();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unlocks previously locked lock.
|
* Unlocks previously locked lock.
|
||||||
|
@ -82,7 +80,7 @@ interface CycleDetectingLock<ID> {
|
||||||
*
|
*
|
||||||
* Guarded by {@code this}.
|
* Guarded by {@code this}.
|
||||||
*/
|
*/
|
||||||
private final Multimap<Long, ReentrantCycleDetectingLock> locksOwnedByThread =
|
private static final Multimap<Thread, ReentrantCycleDetectingLock<?>> locksOwnedByThread =
|
||||||
LinkedHashMultimap.create();
|
LinkedHashMultimap.create();
|
||||||
/**
|
/**
|
||||||
* Specifies lock that thread is currently waiting on to own it.
|
* Specifies lock that thread is currently waiting on to own it.
|
||||||
|
@ -100,22 +98,22 @@ interface CycleDetectingLock<ID> {
|
||||||
*
|
*
|
||||||
* Guarded by {@code this}.
|
* 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
|
* Creates new lock within this factory context. We can guarantee that locks created by
|
||||||
* the same factory would not deadlock.
|
* 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) {
|
CycleDetectingLock<ID> create(ID userLockId) {
|
||||||
return new ReentrantCycleDetectingLock(newLockId, new ReentrantLock());
|
return new ReentrantCycleDetectingLock<>(this, userLockId, new ReentrantLock());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The implementation for {@link CycleDetectingLock}.
|
* 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.
|
* Underlying lock used for actual waiting when no potential deadlocks are detected.
|
||||||
|
@ -125,50 +123,56 @@ interface CycleDetectingLock<ID> {
|
||||||
* User id for this lock.
|
* User id for this lock.
|
||||||
*/
|
*/
|
||||||
private final ID userLockId;
|
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.
|
* Thread id for the thread that owned this lock. Nullable.
|
||||||
* Guarded by {@code CycleDetectingLockFactory.this}.
|
* Guarded by {@code CycleDetectingLockFactory.this}.
|
||||||
*/
|
*/
|
||||||
private Long lockOwnerThreadId = null;
|
private Thread lockOwnerThread = null;
|
||||||
/**
|
/**
|
||||||
* Number of times that thread owned this lock.
|
* Number of times that thread owned this lock.
|
||||||
* Guarded by {@code CycleDetectingLockFactory.this}.
|
* Guarded by {@code CycleDetectingLockFactory.this}.
|
||||||
*/
|
*/
|
||||||
private int lockReentranceCount = 0;
|
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.userLockId = Preconditions.checkNotNull(userLockId, "userLockId");
|
||||||
this.lockImplementation = Preconditions.checkNotNull(
|
this.lockImplementation = Preconditions.checkNotNull(
|
||||||
lockImplementation, "lockImplementation");
|
lockImplementation, "lockImplementation");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ListMultimap<Long, ID> lockOrDetectPotentialLocksCycle() {
|
public ListMultimap<Thread, ID> lockOrDetectPotentialLocksCycle() {
|
||||||
final long currentThreadId = Thread.currentThread().getId();
|
final Thread currentThread = Thread.currentThread();
|
||||||
synchronized (CycleDetectingLockFactory.this) {
|
synchronized (CycleDetectingLockFactory.class) {
|
||||||
checkState();
|
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()) {
|
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
|
// potential deadlock is found, we don't try to take this lock
|
||||||
return locksInCycle;
|
return locksInCycle;
|
||||||
}
|
}
|
||||||
|
|
||||||
lockThreadIsWaitingOn.put(currentThreadId, this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// this may be blocking, but we don't expect it to cause a deadlock
|
// this may be blocking, but we don't expect it to cause a deadlock
|
||||||
lockImplementation.lock();
|
lockImplementation.lock();
|
||||||
|
|
||||||
synchronized (CycleDetectingLockFactory.this) {
|
synchronized (CycleDetectingLockFactory.class) {
|
||||||
// current thread is no longer waiting on this lock
|
// current thread is no longer waiting on this lock
|
||||||
lockThreadIsWaitingOn.remove(currentThreadId);
|
lockThreadIsWaitingOn.remove(currentThread);
|
||||||
checkState();
|
checkState();
|
||||||
|
|
||||||
// mark it as owned by us
|
// mark it as owned by us
|
||||||
lockOwnerThreadId = currentThreadId;
|
lockOwnerThread = currentThread;
|
||||||
lockReentranceCount++;
|
lockReentranceCount++;
|
||||||
// add this lock to the list of locks owned by a current thread
|
// 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
|
// no deadlock is found, locking successful
|
||||||
return ImmutableListMultimap.of();
|
return ImmutableListMultimap.of();
|
||||||
|
@ -176,12 +180,12 @@ interface CycleDetectingLock<ID> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void unlock() {
|
public void unlock() {
|
||||||
final long currentThreadId = Thread.currentThread().getId();
|
final Thread currentThread = Thread.currentThread();
|
||||||
synchronized (CycleDetectingLockFactory.this) {
|
synchronized (CycleDetectingLockFactory.class) {
|
||||||
checkState();
|
checkState();
|
||||||
Preconditions.checkState(lockOwnerThreadId != null,
|
Preconditions.checkState(lockOwnerThread != null,
|
||||||
"Thread is trying to unlock a lock that is not locked");
|
"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");
|
"Thread is trying to unlock a lock owned by another thread");
|
||||||
|
|
||||||
// releasing underlying lock
|
// releasing underlying lock
|
||||||
|
@ -191,12 +195,12 @@ interface CycleDetectingLock<ID> {
|
||||||
lockReentranceCount--;
|
lockReentranceCount--;
|
||||||
if (lockReentranceCount == 0) {
|
if (lockReentranceCount == 0) {
|
||||||
// we no longer own this lock
|
// we no longer own this lock
|
||||||
lockOwnerThreadId = null;
|
lockOwnerThread = null;
|
||||||
Preconditions.checkState(locksOwnedByThread.remove(currentThreadId, this),
|
Preconditions.checkState(locksOwnedByThread.remove(currentThread, this),
|
||||||
"Internal error: Can not find this lock in locks owned by a current thread");
|
"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
|
// clearing memory
|
||||||
locksOwnedByThread.removeAll(currentThreadId);
|
locksOwnedByThread.removeAll(currentThread);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -206,14 +210,14 @@ interface CycleDetectingLock<ID> {
|
||||||
* Check consistency of an internal state.
|
* Check consistency of an internal state.
|
||||||
*/
|
*/
|
||||||
void checkState() throws IllegalStateException {
|
void checkState() throws IllegalStateException {
|
||||||
final long currentThreadId = Thread.currentThread().getId();
|
final Thread currentThread = Thread.currentThread();
|
||||||
Preconditions.checkState(!lockThreadIsWaitingOn.containsKey(currentThreadId),
|
Preconditions.checkState(!lockThreadIsWaitingOn.containsKey(currentThread),
|
||||||
"Internal error: Thread should not be in a waiting thread on a lock now");
|
"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
|
// check state of a locked lock
|
||||||
Preconditions.checkState(lockReentranceCount >= 0,
|
Preconditions.checkState(lockReentranceCount >= 0,
|
||||||
"Internal error: Lock ownership and reentrance count internal states do not match");
|
"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 "
|
"Internal error: Set of locks owned by a current thread and lock "
|
||||||
+ "ownership status do not match");
|
+ "ownership status do not match");
|
||||||
} else {
|
} else {
|
||||||
|
@ -233,74 +237,80 @@ interface CycleDetectingLock<ID> {
|
||||||
*
|
*
|
||||||
* @see CycleDetectingLock#lockOrDetectPotentialLocksCycle()
|
* @see CycleDetectingLock#lockOrDetectPotentialLocksCycle()
|
||||||
*/
|
*/
|
||||||
private ListMultimap<Long, ID> detectPotentialLocksCycle() {
|
private ListMultimap<Thread, ID> detectPotentialLocksCycle() {
|
||||||
final long currentThreadId = Thread.currentThread().getId();
|
final Thread currentThread = Thread.currentThread();
|
||||||
if (lockOwnerThreadId == null || lockOwnerThreadId == currentThreadId) {
|
if (lockOwnerThread == null || lockOwnerThread == currentThread) {
|
||||||
// if nobody owns this lock, lock cycle is impossible
|
// if nobody owns this lock, lock cycle is impossible
|
||||||
// if a current thread owns this lock, we let Guice to handle it
|
// if a current thread owns this lock, we let Guice to handle it
|
||||||
return ImmutableListMultimap.of();
|
return ImmutableListMultimap.of();
|
||||||
}
|
}
|
||||||
|
|
||||||
ListMultimap<Long, ID> potentialLocksCycle = Multimaps.newListMultimap(
|
ListMultimap<Thread, ID> potentialLocksCycle = Multimaps.newListMultimap(
|
||||||
new LinkedHashMap<Long, Collection<ID>>(),
|
new LinkedHashMap<>(), Lists::newArrayList);
|
||||||
new Supplier<List<ID>>() {
|
|
||||||
@Override
|
|
||||||
public List<ID> get() {
|
|
||||||
return Lists.newArrayList();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// lock that is a part of a potential locks cycle, starts with current lock
|
// 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
|
// try to find a dependency path between lock's owner thread and a current thread
|
||||||
while (lockOwnerWaitingOn != null && lockOwnerWaitingOn.lockOwnerThreadId != null) {
|
while (lockOwnerWaitingOn != null && lockOwnerWaitingOn.lockOwnerThread != null) {
|
||||||
Long threadOwnerThreadWaits = lockOwnerWaitingOn.lockOwnerThreadId;
|
Thread threadOwnerThreadWaits = lockOwnerWaitingOn.lockOwnerThread;
|
||||||
// in case locks cycle exists lock we're waiting for is part of it
|
// in case locks cycle exists lock we're waiting for is part of it
|
||||||
potentialLocksCycle.putAll(threadOwnerThreadWaits,
|
lockOwnerWaitingOn =
|
||||||
getAllLockIdsAfter(threadOwnerThreadWaits, lockOwnerWaitingOn));
|
addAllLockIdsAfter(threadOwnerThreadWaits, lockOwnerWaitingOn, potentialLocksCycle);
|
||||||
|
if (threadOwnerThreadWaits == currentThread) {
|
||||||
if (threadOwnerThreadWaits == currentThreadId) {
|
|
||||||
// owner thread depends on current thread, cycle detected
|
// owner thread depends on current thread, cycle detected
|
||||||
return potentialLocksCycle;
|
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
|
// no dependency path from an owner thread to a current thread
|
||||||
return ImmutableListMultimap.of();
|
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) {
|
private ReentrantCycleDetectingLock<?> addAllLockIdsAfter(
|
||||||
List<ID> ids = Lists.newArrayList();
|
Thread thread,
|
||||||
|
ReentrantCycleDetectingLock<?> lock,
|
||||||
|
ListMultimap<Thread, ID> potentialLocksCycle) {
|
||||||
boolean found = false;
|
boolean found = false;
|
||||||
Collection<ReentrantCycleDetectingLock> ownedLocks = locksOwnedByThread.get(threadId);
|
Collection<ReentrantCycleDetectingLock<?>> ownedLocks = locksOwnedByThread.get(thread);
|
||||||
Preconditions.checkNotNull(ownedLocks,
|
Preconditions.checkNotNull(ownedLocks,
|
||||||
"Internal error: No locks were found taken by a thread");
|
"Internal error: No locks were found taken by a thread");
|
||||||
for (ReentrantCycleDetectingLock ownedLock : ownedLocks) {
|
for (ReentrantCycleDetectingLock<?> ownedLock : ownedLocks) {
|
||||||
if (ownedLock == lock) {
|
if (ownedLock == lock) {
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
if (found) {
|
if (found && ownedLock.lockFactory == this.lockFactory) {
|
||||||
ids.add(ownedLock.userLockId);
|
// 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 "
|
Preconditions.checkState(found,
|
||||||
+ "created a cycle that we detected");
|
"Internal error: We can not find locks that created a cycle that we detected");
|
||||||
return ids;
|
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
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
// copy is made to prevent a data race
|
// copy is made to prevent a data race
|
||||||
// no synchronization is used, potentially stale data, should be good enough
|
// no synchronization is used, potentially stale data, should be good enough
|
||||||
Long localLockOwnerThreadId = this.lockOwnerThreadId;
|
Thread thread = this.lockOwnerThread;
|
||||||
if (localLockOwnerThreadId != null) {
|
if (thread != null) {
|
||||||
return String.format("CycleDetectingLock[%s][locked by %s]",
|
return String.format("%s[%s][locked by %s]", super.toString(), userLockId, thread);
|
||||||
userLockId, localLockOwnerThreadId);
|
|
||||||
} else {
|
} else {
|
||||||
return String.format("CycleDetectingLock[%s][unlocked]", userLockId);
|
return String.format("%s[%s][unlocked]", super.toString(), userLockId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
|
@ -24,13 +24,8 @@ final class DefaultConstructionProxyFactory<T> implements ConstructionProxyFacto
|
||||||
@SuppressWarnings("unchecked") // the injection point is for a constructor of T
|
@SuppressWarnings("unchecked") // the injection point is for a constructor of T
|
||||||
final Constructor<T> constructor = (Constructor<T>) injectionPoint.getMember();
|
final Constructor<T> constructor = (Constructor<T>) injectionPoint.getMember();
|
||||||
|
|
||||||
// Use FastConstructor if the constructor is public.
|
if (!Modifier.isPublic(constructor.getDeclaringClass().getModifiers()) ||
|
||||||
if (Modifier.isPublic(constructor.getModifiers())) {
|
!Modifier.isPublic(constructor.getModifiers())) {
|
||||||
Class<T> classToConstruct = constructor.getDeclaringClass();
|
|
||||||
if (!Modifier.isPublic(classToConstruct.getModifiers())) {
|
|
||||||
constructor.setAccessible(true);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
constructor.setAccessible(true);
|
constructor.setAccessible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,17 +33,17 @@ final class DefaultConstructionProxyFactory<T> implements ConstructionProxyFacto
|
||||||
public T newInstance(Object... arguments) throws InvocationTargetException {
|
public T newInstance(Object... arguments) throws InvocationTargetException {
|
||||||
try {
|
try {
|
||||||
return constructor.newInstance(arguments);
|
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
|
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() {
|
public InjectionPoint getInjectionPoint() {
|
||||||
return injectionPoint;
|
return injectionPoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Constructor<T> getConstructor() {
|
public Constructor<T> getConstructor() {
|
||||||
return constructor;
|
return constructor;
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,13 +32,13 @@ final class DeferredLookups implements Lookups {
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> Provider<T> getProvider(Key<T> key) {
|
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);
|
lookups.add(lookup);
|
||||||
return lookup.getProvider();
|
return lookup.getProvider();
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> type) {
|
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);
|
lookups.add(lookup);
|
||||||
return lookup.getMembersInjector();
|
return lookup.getMembersInjector();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,127 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableMultimap;
|
||||||
|
import com.google.inject.Binding;
|
||||||
|
import com.google.inject.Key;
|
||||||
|
import com.google.inject.spi.Dependency;
|
||||||
|
import com.google.inject.spi.ErrorDetail;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Formatter;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error reported by Guice when duplicate elements are found in a {@link Multibinder} that does not
|
||||||
|
* permit duplicates.
|
||||||
|
*/
|
||||||
|
final class DuplicateElementError<T> extends InternalErrorDetail<DuplicateElementError<T>> {
|
||||||
|
private final Key<Set<T>> setKey;
|
||||||
|
private final ImmutableMultimap<T, Element<T>> elements;
|
||||||
|
|
||||||
|
DuplicateElementError(
|
||||||
|
Key<Set<T>> setKey, List<Binding<T>> bindings, T[] values, List<Object> sources) {
|
||||||
|
this(setKey, indexElements(bindings, values), sources);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DuplicateElementError(
|
||||||
|
Key<Set<T>> setKey, ImmutableMultimap<T, Element<T>> elements, List<Object> sources) {
|
||||||
|
super(
|
||||||
|
ErrorId.DUPLICATE_ELEMENT,
|
||||||
|
String.format("Duplicate elements found in Multibinder %s.", Messages.convert(setKey)),
|
||||||
|
sources,
|
||||||
|
null);
|
||||||
|
this.setKey = setKey;
|
||||||
|
this.elements = elements;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void formatDetail(List<ErrorDetail<?>> others, Formatter formatter) {
|
||||||
|
formatter.format("%n%s%n", Messages.bold("Duplicates:"));
|
||||||
|
int duplicateIndex = 1;
|
||||||
|
for (Map.Entry<T, Collection<Element<T>>> entry : elements.asMap().entrySet()) {
|
||||||
|
formatter.format("%-2s: ", duplicateIndex++);
|
||||||
|
if (entry.getValue().size() > 1) {
|
||||||
|
Set<String> valuesAsString =
|
||||||
|
entry.getValue().stream()
|
||||||
|
.map(element -> element.value.toString())
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
if (valuesAsString.size() == 1) {
|
||||||
|
// String representation of the duplicates elements are the same, so only print out one.
|
||||||
|
formatter.format("Element: %s%n", Messages.redBold(valuesAsString.iterator().next()));
|
||||||
|
formatter.format(" Bound at:%n");
|
||||||
|
int index = 1;
|
||||||
|
for (Element<T> element : entry.getValue()) {
|
||||||
|
formatter.format(" %-2s: ", index++);
|
||||||
|
formatElement(element, formatter);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Print out all elements as string when there are different string representations of the
|
||||||
|
// elements. To keep the logic simple, same strings are not grouped together unless all
|
||||||
|
// elements have the same string represnetation. This means some strings may be printed
|
||||||
|
// out multiple times.
|
||||||
|
// There is no indentation for the first duplicate element.
|
||||||
|
boolean indent = false;
|
||||||
|
for (Element<T> element : entry.getValue()) {
|
||||||
|
if (indent) {
|
||||||
|
formatter.format(" ");
|
||||||
|
} else {
|
||||||
|
indent = true;
|
||||||
|
}
|
||||||
|
formatter.format("Element: %s%n", Messages.redBold(element.value.toString()));
|
||||||
|
formatter.format(" Bound at: ");
|
||||||
|
formatElement(element, formatter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
formatter.format("%n%s%n", Messages.bold("Multibinder declared at:"));
|
||||||
|
// Multibinder source includes the key of the set. Filter it out since it's not useful in the
|
||||||
|
// printed error stack.
|
||||||
|
List<Object> filteredSource =
|
||||||
|
getSources().stream()
|
||||||
|
.filter(
|
||||||
|
source -> {
|
||||||
|
if (source instanceof Dependency) {
|
||||||
|
return !((Dependency<?>) source).getKey().equals(setKey);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
ErrorFormatter.formatSources(filteredSource, formatter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void formatElement(Element<T> element, Formatter formatter) {
|
||||||
|
Object source = element.binding.getSource();
|
||||||
|
new SourceFormatter(
|
||||||
|
source,
|
||||||
|
formatter,
|
||||||
|
/** omitPreposition= */
|
||||||
|
true)
|
||||||
|
.format();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DuplicateElementError<T> withSources(List<Object> newSources) {
|
||||||
|
return new DuplicateElementError<>(setKey, elements, newSources);
|
||||||
|
}
|
||||||
|
|
||||||
|
static <T> ImmutableMultimap<T, Element<T>> indexElements(List<Binding<T>> bindings, T[] values) {
|
||||||
|
ImmutableMultimap.Builder<T, Element<T>> map = ImmutableMultimap.builder();
|
||||||
|
for (int i = 0; i < values.length; i++) {
|
||||||
|
map.put(values[i], new Element<T>(values[i], bindings.get(i)));
|
||||||
|
}
|
||||||
|
return map.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
static class Element<T> {
|
||||||
|
T value;
|
||||||
|
Binding<T> binding;
|
||||||
|
|
||||||
|
Element(T value, Binding<T> binding) {
|
||||||
|
this.value = value;
|
||||||
|
this.binding = binding;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.common.collect.Multimap;
|
||||||
|
import com.google.inject.Binding;
|
||||||
|
import com.google.inject.Key;
|
||||||
|
import com.google.inject.spi.ErrorDetail;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Formatter;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error reported by Guice when a duplicate key is found in a {@link MapBinder} that does not permit
|
||||||
|
* duplicates.
|
||||||
|
*/
|
||||||
|
final class DuplicateMapKeyError<K, V> extends InternalErrorDetail<DuplicateMapKeyError<K, V>> {
|
||||||
|
private final Key<Map<K, V>> mapKey;
|
||||||
|
private final Multimap<K, Binding<V>> duplicates;
|
||||||
|
|
||||||
|
DuplicateMapKeyError(
|
||||||
|
Key<Map<K, V>> mapKey, Multimap<K, Binding<V>> duplicates, List<Object> sources) {
|
||||||
|
super(ErrorId.DUPLICATE_MAP_KEY, getDuplicateKeysMessage(mapKey, duplicates), sources, null);
|
||||||
|
this.mapKey = mapKey;
|
||||||
|
this.duplicates = duplicates;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final void formatDetail(List<ErrorDetail<?>> others, Formatter formatter) {
|
||||||
|
formatter.format("%n%s%n", Messages.bold("Duplicates:"));
|
||||||
|
|
||||||
|
for (Map.Entry<K, Collection<Binding<V>>> entry : duplicates.asMap().entrySet()) {
|
||||||
|
formatter.format(" Key: \"%s\"%n", Messages.redBold(entry.getKey().toString()));
|
||||||
|
formatter.format(" Bound at:%n");
|
||||||
|
int index = 1;
|
||||||
|
for (Binding<V> binding : entry.getValue()) {
|
||||||
|
formatter.format(" %-2s: ", index++);
|
||||||
|
new SourceFormatter(
|
||||||
|
binding.getSource(),
|
||||||
|
formatter,
|
||||||
|
/** omitPreposition= */
|
||||||
|
true)
|
||||||
|
.format();
|
||||||
|
}
|
||||||
|
formatter.format("%n");
|
||||||
|
}
|
||||||
|
|
||||||
|
formatter.format("%s%n", Messages.bold("MapBinder declared at:"));
|
||||||
|
ErrorFormatter.formatSources(getSources(), formatter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DuplicateMapKeyError<K, V> withSources(List<Object> newSources) {
|
||||||
|
return new DuplicateMapKeyError<>(mapKey, duplicates, newSources);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <K, V> String getDuplicateKeysMessage(
|
||||||
|
Key<Map<K, V>> mapKey, Multimap<K, Binding<V>> duplicates) {
|
||||||
|
Set<K> duplicateKeys = duplicates.keySet();
|
||||||
|
String mapBinderKey = Messages.convert(mapKey).toString();
|
||||||
|
String firstDuplicateKey = duplicateKeys.iterator().next().toString();
|
||||||
|
if (duplicateKeys.size() == 1) {
|
||||||
|
return String.format("Duplicate key \"%s\" found in %s.", firstDuplicateKey, mapBinderKey);
|
||||||
|
} else {
|
||||||
|
return String.format(
|
||||||
|
"\"%s\" and %s other duplicate keys found in %s.",
|
||||||
|
firstDuplicateKey, duplicateKeys.size() - 1, mapBinderKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package com.google.inject.multibindings;
|
package com.google.inject.internal;
|
||||||
|
|
||||||
import com.google.inject.BindingAnnotation;
|
import com.google.inject.BindingAnnotation;
|
||||||
|
|
||||||
|
@ -25,7 +25,6 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
|
|
||||||
enum Type {
|
enum Type {
|
||||||
MAPBINDER,
|
MAPBINDER,
|
||||||
MULTIBINDER,
|
MULTIBINDER;
|
||||||
OPTIONALBINDER;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -43,6 +43,7 @@ final class EncounterImpl<T> implements TypeEncounter<T> {
|
||||||
: ImmutableSet.copyOf(injectionListeners);
|
: ImmutableSet.copyOf(injectionListeners);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void register(MembersInjector<? super T> membersInjector) {
|
public void register(MembersInjector<? super T> membersInjector) {
|
||||||
checkState(valid, "Encounters may not be used after hear() returns.");
|
checkState(valid, "Encounters may not be used after hear() returns.");
|
||||||
|
|
||||||
|
@ -53,6 +54,7 @@ final class EncounterImpl<T> implements TypeEncounter<T> {
|
||||||
membersInjectors.add(membersInjector);
|
membersInjectors.add(membersInjector);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void register(InjectionListener<? super T> injectionListener) {
|
public void register(InjectionListener<? super T> injectionListener) {
|
||||||
checkState(valid, "Encounters may not be used after hear() returns.");
|
checkState(valid, "Encounters may not be used after hear() returns.");
|
||||||
|
|
||||||
|
@ -63,35 +65,42 @@ final class EncounterImpl<T> implements TypeEncounter<T> {
|
||||||
injectionListeners.add(injectionListener);
|
injectionListeners.add(injectionListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void addError(String message, Object... arguments) {
|
public void addError(String message, Object... arguments) {
|
||||||
checkState(valid, "Encounters may not be used after hear() returns.");
|
checkState(valid, "Encounters may not be used after hear() returns.");
|
||||||
errors.addMessage(message, arguments);
|
errors.addMessage(message, arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void addError(Throwable t) {
|
public void addError(Throwable t) {
|
||||||
checkState(valid, "Encounters may not be used after hear() returns.");
|
checkState(valid, "Encounters may not be used after hear() returns.");
|
||||||
errors.errorInUserCode(t, "An exception was caught and reported. Message: %s", t.getMessage());
|
errors.errorInUserCode(t, "An exception was caught and reported. Message: %s", t.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void addError(Message message) {
|
public void addError(Message message) {
|
||||||
checkState(valid, "Encounters may not be used after hear() returns.");
|
checkState(valid, "Encounters may not be used after hear() returns.");
|
||||||
errors.addMessage(message);
|
errors.addMessage(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public <T> Provider<T> getProvider(Key<T> key) {
|
public <T> Provider<T> getProvider(Key<T> key) {
|
||||||
checkState(valid, "Encounters may not be used after hear() returns.");
|
checkState(valid, "Encounters may not be used after hear() returns.");
|
||||||
return lookups.getProvider(key);
|
return lookups.getProvider(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public <T> Provider<T> getProvider(Class<T> type) {
|
public <T> Provider<T> getProvider(Class<T> type) {
|
||||||
return getProvider(Key.get(type));
|
return getProvider(Key.get(type));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> typeLiteral) {
|
public <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> typeLiteral) {
|
||||||
checkState(valid, "Encounters may not be used after hear() returns.");
|
checkState(valid, "Encounters may not be used after hear() returns.");
|
||||||
return lookups.getMembersInjector(typeLiteral);
|
return lookups.getMembersInjector(typeLiteral);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public <T> MembersInjector<T> getMembersInjector(Class<T> type) {
|
public <T> MembersInjector<T> getMembersInjector(Class<T> type) {
|
||||||
return getMembersInjector(TypeLiteral.get(type));
|
return getMembersInjector(TypeLiteral.get(type));
|
||||||
}
|
}
|
||||||
|
|
33
src/main/java/com/google/inject/internal/ErrorFormatter.java
Normal file
33
src/main/java/com/google/inject/internal/ErrorFormatter.java
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import java.util.Formatter;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/** Helper for formatting Guice errors. */
|
||||||
|
final class ErrorFormatter {
|
||||||
|
private ErrorFormatter() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format a list of sources to the given {@code formatter}, prefixed by the give {@code index}.
|
||||||
|
*/
|
||||||
|
static void formatSources(int index, List<Object> sources, Formatter formatter) {
|
||||||
|
for (int i = 0; i < sources.size(); i++) {
|
||||||
|
Object source = sources.get(i);
|
||||||
|
if (i == 0) {
|
||||||
|
formatter.format("%-3s: ", index);
|
||||||
|
} else {
|
||||||
|
formatter.format(SourceFormatter.INDENT);
|
||||||
|
}
|
||||||
|
new SourceFormatter(source, formatter, i == 0).format();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Format a list of sources to the given {@code formatter}. */
|
||||||
|
static void formatSources(List<Object> sources, Formatter formatter) {
|
||||||
|
for (int i = 0; i < sources.size(); i++) {
|
||||||
|
Object source = sources.get(i);
|
||||||
|
formatter.format(" ");
|
||||||
|
new SourceFormatter(source, formatter, i == 0).format();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
65
src/main/java/com/google/inject/internal/ErrorId.java
Normal file
65
src/main/java/com/google/inject/internal/ErrorId.java
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
/** Enum used to identify a specific Guice error. */
|
||||||
|
public enum ErrorId {
|
||||||
|
AMBIGUOUS_TYPE_CONVERSION,
|
||||||
|
AOP_DISABLED,
|
||||||
|
AT_INJECT_REQUIRED,
|
||||||
|
AT_TARGET_IS_MISSING_PARAMETER,
|
||||||
|
BINDING_ALREADY_SET,
|
||||||
|
BINDING_TO_GUICE_TYPE,
|
||||||
|
BINDING_TO_PROVIDER,
|
||||||
|
CAN_NOT_PROXY_CLASS,
|
||||||
|
CHILD_BINDING_ALREADY_SET,
|
||||||
|
CIRCULAR_PROXY_DISABLED,
|
||||||
|
CONSTRUCTOR_NOT_DEFINED_BY_TYPE,
|
||||||
|
CONVERSION_TYPE_ERROR,
|
||||||
|
CONVERTER_RETURNED_NULL,
|
||||||
|
DUPLICATE_BINDING_ANNOTATIONS,
|
||||||
|
DUPLICATE_ELEMENT,
|
||||||
|
DUPLICATE_MAP_KEY,
|
||||||
|
DUPLICATE_SCOPES,
|
||||||
|
DUPLICATE_SCOPE_ANNOTATIONS,
|
||||||
|
ERROR_ENHANCING_CLASS,
|
||||||
|
ERROR_INJECTING_CONSTRUCTOR,
|
||||||
|
ERROR_INJECTING_METHOD,
|
||||||
|
ERROR_IN_CUSTOM_PROVIDER,
|
||||||
|
ERROR_IN_USER_CODE,
|
||||||
|
ERROR_IN_USER_INJECTOR,
|
||||||
|
ERROR_NOTIFYING_TYPE_LISTENER,
|
||||||
|
EXPOSED_BUT_NOT_BOUND,
|
||||||
|
INJECT_ABSTRACT_METHOD,
|
||||||
|
INJECT_FINAL_FIELD,
|
||||||
|
INJECT_INNER_CLASS,
|
||||||
|
INJECT_METHOD_WITH_TYPE_PARAMETER,
|
||||||
|
INJECT_RAW_MEMBERS_INJECTOR,
|
||||||
|
INJECT_RAW_PROVIDER,
|
||||||
|
INJECT_RAW_TYPE_LITERAL,
|
||||||
|
JIT_BINDING_ALREADY_SET,
|
||||||
|
JIT_DISABLED,
|
||||||
|
JIT_DISABLED_IN_PARENT,
|
||||||
|
KEY_NOT_FULLY_SPECIFIED,
|
||||||
|
MISPLACED_BINDING_ANNOTATION,
|
||||||
|
MISSING_CONSTANT_VALUES,
|
||||||
|
MISSING_CONSTRUCTOR,
|
||||||
|
MISSING_IMPLEMENTATION,
|
||||||
|
MISSING_RUNTIME_RETENTION,
|
||||||
|
MISSING_SCOPE_ANNOTATION,
|
||||||
|
NOT_A_SUBTYPE,
|
||||||
|
NULL_ELEMENT_IN_SET,
|
||||||
|
NULL_INJECTED_INTO_NON_NULLABLE,
|
||||||
|
NULL_VALUE_IN_MAP,
|
||||||
|
OPTIONAL_CONSTRUCTOR,
|
||||||
|
RECURSIVE_BINDING,
|
||||||
|
RECURSIVE_IMPLEMENTATION_TYPE,
|
||||||
|
RECURSIVE_PROVIDER_TYPE,
|
||||||
|
SCOPE_ANNOTATION_ON_ABSTRACT_TYPE,
|
||||||
|
SCOPE_NOT_FOUND,
|
||||||
|
STATIC_INJECTION_ON_INTERFACE,
|
||||||
|
SUBTYPE_NOT_PROVIDED,
|
||||||
|
TOO_MANY_CONSTRUCTORS,
|
||||||
|
VOID_PROVIDER_METHOD,
|
||||||
|
|
||||||
|
// All other uncommon type of errors
|
||||||
|
OTHER;
|
||||||
|
}
|
File diff suppressed because it is too large
Load diff
|
@ -8,6 +8,9 @@ package com.google.inject.internal;
|
||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
public class ErrorsException extends Exception {
|
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;
|
private final Errors errors;
|
||||||
|
|
||||||
public ErrorsException(Errors errors) {
|
public ErrorsException(Errors errors) {
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -22,14 +22,17 @@ public final class ExposedBindingImpl<T> extends BindingImpl<T> implements Expos
|
||||||
this.privateElements = privateElements;
|
this.privateElements = privateElements;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
|
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
|
||||||
return visitor.visit(this);
|
return visitor.visit(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Set<Dependency<?>> getDependencies() {
|
public Set<Dependency<?>> getDependencies() {
|
||||||
return ImmutableSet.<Dependency<?>>of(Dependency.get(Key.get(Injector.class)));
|
return ImmutableSet.<Dependency<?>>of(Dependency.get(Key.get(Injector.class)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public PrivateElements getPrivateElements() {
|
public PrivateElements getPrivateElements() {
|
||||||
return privateElements;
|
return privateElements;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,9 +18,10 @@ final class ExposedKeyFactory<T> implements InternalFactory<T>, CreationListener
|
||||||
this.privateElements = privateElements;
|
this.privateElements = privateElements;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void notify(Errors errors) {
|
public void notify(Errors errors) {
|
||||||
InjectorImpl privateInjector = (InjectorImpl) privateElements.getInjector();
|
InjectorImpl privateInjector = (InjectorImpl) privateElements.getInjector();
|
||||||
BindingImpl<T> explicitBinding = privateInjector.state.getExplicitBinding(key);
|
BindingImpl<T> explicitBinding = privateInjector.getBindingData().getExplicitBinding(key);
|
||||||
|
|
||||||
// validate that the child injector has its own factory. If the getInternalFactory() returns
|
// validate that the child injector has its own factory. If the getInternalFactory() returns
|
||||||
// this, then that child injector doesn't have a factory (and getExplicitBinding has returned
|
// this, then that child injector doesn't have a factory (and getExplicitBinding has returned
|
||||||
|
@ -33,8 +34,10 @@ final class ExposedKeyFactory<T> implements InternalFactory<T>, CreationListener
|
||||||
this.delegate = explicitBinding;
|
this.delegate = explicitBinding;
|
||||||
}
|
}
|
||||||
|
|
||||||
public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
|
@Override
|
||||||
throws ErrorsException {
|
public T get(InternalContext context, Dependency<?> dependency, boolean linked)
|
||||||
return delegate.getInternalFactory().get(errors, context, dependency, linked);
|
throws InternalProvisionException {
|
||||||
|
// TODO(lukes): add a source to the thrown exception?
|
||||||
|
return delegate.getInternalFactory().get(context, dependency, linked);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,12 +27,14 @@ public class ExposureBuilder<T> implements AnnotatedElementBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void annotatedWith(Class<? extends Annotation> annotationType) {
|
public void annotatedWith(Class<? extends Annotation> annotationType) {
|
||||||
Preconditions.checkNotNull(annotationType, "annotationType");
|
Preconditions.checkNotNull(annotationType, "annotationType");
|
||||||
checkNotAnnotated();
|
checkNotAnnotated();
|
||||||
key = Key.get(key.getTypeLiteral(), annotationType);
|
key = Key.get(key.getTypeLiteral(), annotationType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void annotatedWith(Annotation annotation) {
|
public void annotatedWith(Annotation annotation) {
|
||||||
Preconditions.checkNotNull(annotation, "annotation");
|
Preconditions.checkNotNull(annotation, "annotation");
|
||||||
checkNotAnnotated();
|
checkNotAnnotated();
|
||||||
|
|
|
@ -25,6 +25,7 @@ final class FactoryProxy<T> implements InternalFactory<T>, CreationListener {
|
||||||
this.source = source;
|
this.source = source;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void notify(final Errors errors) {
|
public void notify(final Errors errors) {
|
||||||
try {
|
try {
|
||||||
targetFactory = injector.getInternalFactory(targetKey, errors.withSource(source), JitLimitation.NEW_OR_EXISTING_JIT);
|
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)
|
@Override
|
||||||
throws ErrorsException {
|
public T get(InternalContext context, Dependency<?> dependency, boolean linked)
|
||||||
context.pushState(targetKey, source);
|
throws InternalProvisionException {
|
||||||
|
Key<? extends T> localTargetKey = targetKey;
|
||||||
|
context.pushState(localTargetKey, source);
|
||||||
try {
|
try {
|
||||||
return targetFactory.get(errors.withSource(targetKey), context, dependency, true);
|
return targetFactory.get(context, dependency, true);
|
||||||
|
} catch (InternalProvisionException ipe) {
|
||||||
|
throw ipe.addSource(localTargetKey);
|
||||||
} finally {
|
} finally {
|
||||||
context.popState();
|
context.popState();
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,9 @@ package com.google.inject.internal;
|
||||||
import com.google.common.cache.CacheBuilder;
|
import com.google.common.cache.CacheBuilder;
|
||||||
import com.google.common.cache.CacheLoader;
|
import com.google.common.cache.CacheLoader;
|
||||||
import com.google.common.cache.LoadingCache;
|
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
|
* 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> {
|
public abstract class FailableCache<K, V> {
|
||||||
|
|
||||||
private final LoadingCache<K, Object> delegate = CacheBuilder.newBuilder().build(
|
private final LoadingCache<K, Object> delegate = CacheBuilder.newBuilder()
|
||||||
new CacheLoader<K, Object>() {
|
.build(new CacheLoader<>() {
|
||||||
public Object load(K key) {
|
public Object load(K key) {
|
||||||
Errors errors = new Errors();
|
Errors errors = new Errors();
|
||||||
V result = null;
|
V result = null;
|
||||||
|
@ -41,4 +44,14 @@ public abstract class FailableCache<K, V> {
|
||||||
boolean remove(K key) {
|
boolean remove(K key) {
|
||||||
return delegate.asMap().remove(key) != null;
|
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;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.inject.spi.ErrorDetail;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Formatter;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/** Generic error message representing a Guice internal error. */
|
||||||
|
public final class GenericErrorDetail extends InternalErrorDetail<GenericErrorDetail>
|
||||||
|
implements Serializable {
|
||||||
|
public GenericErrorDetail(
|
||||||
|
ErrorId errorId, String message, List<Object> sources, Throwable cause) {
|
||||||
|
super(errorId, checkNotNull(message, "message"), sources, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void formatDetail(List<ErrorDetail<?>> mergeableErrors, Formatter formatter) {
|
||||||
|
Preconditions.checkArgument(mergeableErrors.isEmpty(), "Unexpected mergeable errors");
|
||||||
|
List<Object> dependencies = getSources();
|
||||||
|
for (Object source : Lists.reverse(dependencies)) {
|
||||||
|
formatter.format(" ");
|
||||||
|
new SourceFormatter(source, formatter, /* omitPreposition= */ false).format();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GenericErrorDetail withSources(List<Object> newSources) {
|
||||||
|
return new GenericErrorDetail(errorId, getMessage(), newSources, getCause());
|
||||||
|
}
|
||||||
|
}
|
14
src/main/java/com/google/inject/internal/GuiceInternal.java
Normal file
14
src/main/java/com/google/inject/internal/GuiceInternal.java
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class used for restricting APIs in other packages to only be used by this package.
|
||||||
|
*
|
||||||
|
* <p>Other packages can reference this class but only this package can reference an instance of it,
|
||||||
|
* so adding this class as a method param ensures that only this package can call it (provided null
|
||||||
|
* is disallowed).
|
||||||
|
*/
|
||||||
|
public final class GuiceInternal {
|
||||||
|
static final GuiceInternal GUICE_INTERNAL = new GuiceInternal();
|
||||||
|
|
||||||
|
private GuiceInternal() {}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package com.google.inject.multibindings;
|
package com.google.inject.internal;
|
||||||
|
|
||||||
import com.google.common.base.Objects;
|
import com.google.common.base.Objects;
|
||||||
import com.google.inject.Binding;
|
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
|
* Visits bindings to return a {@code IndexedBinding} that can be used to emulate the binding
|
||||||
* deduplication that Guice internally performs.
|
* 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> {
|
implements BindingScopingVisitor<Object> {
|
||||||
private static final Object EAGER_SINGLETON = new 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;
|
this.injector = injector;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isIndexable(Binding<?> binding) {
|
public boolean isIndexable(Binding<?> binding) {
|
||||||
return binding.getKey().getAnnotation() instanceof Element;
|
return binding.getKey().getAnnotation() instanceof Element;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,7 +131,7 @@ class Indexer extends DefaultBindingTargetVisitor<Object, Indexer.IndexedBinding
|
||||||
PROVIDED_BY,
|
PROVIDED_BY,
|
||||||
}
|
}
|
||||||
|
|
||||||
static class IndexedBinding {
|
public static class IndexedBinding {
|
||||||
final String annotationName;
|
final String annotationName;
|
||||||
final Element.Type annotationType;
|
final Element.Type annotationType;
|
||||||
final TypeLiteral<?> typeLiteral;
|
final TypeLiteral<?> typeLiteral;
|
||||||
|
@ -134,7 +139,7 @@ class Indexer extends DefaultBindingTargetVisitor<Object, Indexer.IndexedBinding
|
||||||
final BindingType type;
|
final BindingType type;
|
||||||
final Object extraEquality;
|
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.scope = scope;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.extraEquality = extraEquality;
|
this.extraEquality = extraEquality;
|
|
@ -1,160 +0,0 @@
|
||||||
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.inject.Binding;
|
|
||||||
import com.google.inject.Key;
|
|
||||||
import com.google.inject.Scope;
|
|
||||||
import com.google.inject.TypeLiteral;
|
|
||||||
import com.google.inject.spi.ModuleAnnotatedMethodScannerBinding;
|
|
||||||
import com.google.inject.spi.ProvisionListenerBinding;
|
|
||||||
import com.google.inject.spi.ScopeBinding;
|
|
||||||
import com.google.inject.spi.TypeConverterBinding;
|
|
||||||
import com.google.inject.spi.TypeListenerBinding;
|
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
|
||||||
|
|
||||||
final class InheritingState implements State {
|
|
||||||
|
|
||||||
private final State parent;
|
|
||||||
|
|
||||||
// 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<Class<? extends Annotation>, ScopeBinding> scopes = Maps.newHashMap();
|
|
||||||
private final List<TypeConverterBinding> converters = Lists.newArrayList();
|
|
||||||
private final List<TypeListenerBinding> typeListenerBindings = Lists.newArrayList();
|
|
||||||
private final List<ProvisionListenerBinding> provisionListenerBindings = Lists.newArrayList();
|
|
||||||
private final List<ModuleAnnotatedMethodScannerBinding> scannerBindings = Lists.newArrayList();
|
|
||||||
private final WeakKeySet blacklistedKeys;
|
|
||||||
private final Object lock;
|
|
||||||
|
|
||||||
InheritingState(State parent) {
|
|
||||||
this.parent = checkNotNull(parent, "parent");
|
|
||||||
this.lock = (parent == State.NONE) ? this : parent.lock();
|
|
||||||
this.blacklistedKeys = new WeakKeySet(lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
public State parent() {
|
|
||||||
return parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked") // we only put in BindingImpls that match their key types
|
|
||||||
public <T> BindingImpl<T> getExplicitBinding(Key<T> key) {
|
|
||||||
Binding<?> binding = explicitBindings.get(key);
|
|
||||||
return binding != null ? (BindingImpl<T>) binding : parent.getExplicitBinding(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<Key<?>, Binding<?>> getExplicitBindingsThisLevel() {
|
|
||||||
return explicitBindings;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void putBinding(Key<?> key, BindingImpl<?> binding) {
|
|
||||||
explicitBindingsMutable.put(key, binding);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ScopeBinding getScopeBinding(Class<? extends Annotation> annotationType) {
|
|
||||||
ScopeBinding scopeBinding = scopes.get(annotationType);
|
|
||||||
return scopeBinding != null ? scopeBinding : parent.getScopeBinding(annotationType);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void putScopeBinding(Class<? extends Annotation> annotationType, ScopeBinding scope) {
|
|
||||||
scopes.put(annotationType, scope);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Iterable<TypeConverterBinding> getConvertersThisLevel() {
|
|
||||||
return converters;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addConverter(TypeConverterBinding typeConverterBinding) {
|
|
||||||
converters.add(typeConverterBinding);
|
|
||||||
}
|
|
||||||
|
|
||||||
public TypeConverterBinding getConverter(
|
|
||||||
String stringValue, TypeLiteral<?> type, Errors errors, Object source) {
|
|
||||||
TypeConverterBinding matchingConverter = null;
|
|
||||||
for (State s = this; s != State.NONE; s = s.parent()) {
|
|
||||||
for (TypeConverterBinding converter : s.getConvertersThisLevel()) {
|
|
||||||
if (converter.getTypeMatcher().matches(type)) {
|
|
||||||
if (matchingConverter != null) {
|
|
||||||
errors.ambiguousTypeConversion(stringValue, source, type, matchingConverter, converter);
|
|
||||||
}
|
|
||||||
matchingConverter = converter;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return matchingConverter;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addTypeListener(TypeListenerBinding listenerBinding) {
|
|
||||||
typeListenerBindings.add(listenerBinding);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<TypeListenerBinding> getTypeListenerBindings() {
|
|
||||||
List<TypeListenerBinding> parentBindings = parent.getTypeListenerBindings();
|
|
||||||
List<TypeListenerBinding> result =
|
|
||||||
Lists.newArrayListWithCapacity(parentBindings.size() + typeListenerBindings.size());
|
|
||||||
result.addAll(parentBindings);
|
|
||||||
result.addAll(typeListenerBindings);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addProvisionListener(ProvisionListenerBinding listenerBinding) {
|
|
||||||
provisionListenerBindings.add(listenerBinding);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<ProvisionListenerBinding> getProvisionListenerBindings() {
|
|
||||||
List<ProvisionListenerBinding> parentBindings = parent.getProvisionListenerBindings();
|
|
||||||
List<ProvisionListenerBinding> result =
|
|
||||||
Lists.newArrayListWithCapacity(parentBindings.size() + provisionListenerBindings.size());
|
|
||||||
result.addAll(parentBindings);
|
|
||||||
result.addAll(provisionListenerBindings);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addScanner(ModuleAnnotatedMethodScannerBinding scanner) {
|
|
||||||
scannerBindings.add(scanner);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<ModuleAnnotatedMethodScannerBinding> getScannerBindings() {
|
|
||||||
List<ModuleAnnotatedMethodScannerBinding> parentBindings = parent.getScannerBindings();
|
|
||||||
List<ModuleAnnotatedMethodScannerBinding> result =
|
|
||||||
Lists.newArrayListWithCapacity(parentBindings.size() + scannerBindings.size());
|
|
||||||
result.addAll(parentBindings);
|
|
||||||
result.addAll(scannerBindings);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void blacklist(Key<?> key, State state, Object source) {
|
|
||||||
parent.blacklist(key, state, source);
|
|
||||||
blacklistedKeys.add(key, state, source);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isBlacklisted(Key<?> key) {
|
|
||||||
return blacklistedKeys.contains(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Set<Object> getSourcesForBlacklistedKey(Key<?> key) {
|
|
||||||
return blacklistedKeys.getSources(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object lock() {
|
|
||||||
return lock;
|
|
||||||
}
|
|
||||||
|
|
||||||
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()) {
|
|
||||||
builder.put(entry.getKey(), entry.getValue().getScope());
|
|
||||||
}
|
|
||||||
return builder.build();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -8,5 +8,5 @@ interface Initializable<T> {
|
||||||
/**
|
/**
|
||||||
* Ensures the reference is initialized, then returns it.
|
* Ensures the reference is initialized, then returns it.
|
||||||
*/
|
*/
|
||||||
T get(Errors errors) throws ErrorsException;
|
T get() throws InternalProvisionException;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,8 @@ final class Initializables {
|
||||||
* Returns an initializable for an instance that requires no initialization.
|
* Returns an initializable for an instance that requires no initialization.
|
||||||
*/
|
*/
|
||||||
static <T> Initializable<T> of(final T instance) {
|
static <T> Initializable<T> of(final T instance) {
|
||||||
return new Initializable<T>() {
|
return new Initializable<>() {
|
||||||
public T get(Errors errors) throws ErrorsException {
|
public T get() {
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,15 +3,16 @@ package com.google.inject.internal;
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
|
import com.google.common.collect.Multimap;
|
||||||
import com.google.inject.Binding;
|
import com.google.inject.Binding;
|
||||||
import com.google.inject.Key;
|
import com.google.inject.Key;
|
||||||
import com.google.inject.Stage;
|
import com.google.inject.Stage;
|
||||||
import com.google.inject.TypeLiteral;
|
import com.google.inject.TypeLiteral;
|
||||||
import com.google.inject.spi.InjectionPoint;
|
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.Set;
|
||||||
import java.util.concurrent.CountDownLatch;
|
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
@ -23,27 +24,30 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
*/
|
*/
|
||||||
final class Initializer {
|
final class Initializer {
|
||||||
|
|
||||||
/**
|
/** Is set to true once {@link #validateOustandingInjections} is called. */
|
||||||
* the only thread that we'll use to inject members.
|
private volatile boolean validationStarted = false;
|
||||||
*/
|
|
||||||
private final Thread creatingThread = Thread.currentThread();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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.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.
|
* 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,
|
<T> Initializable<T> requestInjection(InjectorImpl injector, T instance, Binding<T> binding,
|
||||||
Object source, Set<InjectionPoint> injectionPoints) {
|
Object source, Set<InjectionPoint> injectionPoints) {
|
||||||
checkNotNull(source);
|
checkNotNull(source);
|
||||||
|
Preconditions.checkState(
|
||||||
|
!validationStarted, "Member injection could not be requested after validation is started");
|
||||||
ProvisionListenerStackCallback<T> provisionCallback =
|
ProvisionListenerStackCallback<T> provisionCallback =
|
||||||
binding == null ? null : injector.provisionListenerStore.get(binding);
|
binding == null ? null : injector.provisionListenerStore.get(binding);
|
||||||
|
|
||||||
// short circuit if the object has no injections or listeners.
|
// short circuit if the object has no injections or listeners.
|
||||||
if (instance == null || (injectionPoints.isEmpty()
|
if (instance == null || (injectionPoints.isEmpty()
|
||||||
&& !injector.membersInjectorStore.hasTypeListeners()
|
&& !injector.membersInjectorStore.hasTypeListeners()
|
||||||
&& (provisionCallback == null || !provisionCallback.hasListeners()))) {
|
&& (provisionCallback == null))) {
|
||||||
return Initializables.of(instance);
|
return Initializables.of(instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
InjectableReference<T> initializable = new InjectableReference<T>(
|
if (initializablesCache.containsKey(instance)) {
|
||||||
injector, instance, binding == null ? null : binding.getKey(), provisionCallback, source);
|
@SuppressWarnings("unchecked") // Map from T to InjectableReference<T>
|
||||||
pendingInjection.put(instance, initializable);
|
Initializable<T> cached = (Initializable<T>) initializablesCache.get(instance);
|
||||||
return initializable;
|
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.
|
* on the injected instances.
|
||||||
*/
|
*/
|
||||||
void validateOustandingInjections(Errors errors) {
|
void validateOustandingInjections(Errors errors) {
|
||||||
for (InjectableReference<?> reference : pendingInjection.values()) {
|
validationStarted = true;
|
||||||
|
initializablesCache.clear();
|
||||||
|
for (InjectableReference<?> reference : pendingInjections) {
|
||||||
try {
|
try {
|
||||||
pendingMembersInjectors.put(reference.instance, reference.validate(errors));
|
reference.validate(errors);
|
||||||
} catch (ErrorsException e) {
|
} catch (ErrorsException e) {
|
||||||
errors.merge(e.getErrors());
|
errors.merge(e.getErrors());
|
||||||
}
|
}
|
||||||
|
@ -92,85 +112,122 @@ final class Initializer {
|
||||||
* instances are codependent (directly or transitively), ordering of injection is arbitrary.
|
* instances are codependent (directly or transitively), ordering of injection is arbitrary.
|
||||||
*/
|
*/
|
||||||
void injectAll(final Errors errors) {
|
void injectAll(final Errors errors) {
|
||||||
// loop over a defensive copy since ensureInjected() mutates the set. Unfortunately, that copy
|
Preconditions.checkState(validationStarted, "Validation should be done before injection");
|
||||||
// is made complicated by a bug in IBM's JDK, wherein entrySet().toArray(Object[]) doesn't work
|
for (InjectableReference<?> reference : pendingInjections) {
|
||||||
for (InjectableReference<?> reference : Lists.newArrayList(pendingInjection.values())) {
|
|
||||||
try {
|
try {
|
||||||
reference.get(errors);
|
reference.get();
|
||||||
} catch (ErrorsException e) {
|
} catch (InternalProvisionException ipe) {
|
||||||
errors.merge(e.getErrors());
|
errors.merge(ipe);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pendingInjections.clear();
|
||||||
|
}
|
||||||
|
|
||||||
if (!pendingInjection.isEmpty()) {
|
private enum InjectableReferenceState {
|
||||||
throw new AssertionError("Failed to satisfy " + pendingInjection);
|
NEW,
|
||||||
|
VALIDATED,
|
||||||
|
INJECTING,
|
||||||
|
READY
|
||||||
}
|
}
|
||||||
|
|
||||||
ready.countDown();
|
private static class InjectableReference<T> implements Initializable<T> {
|
||||||
}
|
private volatile InjectableReferenceState state = InjectableReferenceState.NEW;
|
||||||
|
private volatile MembersInjectorImpl<T> membersInjector = null;
|
||||||
|
|
||||||
private class InjectableReference<T> implements Initializable<T> {
|
|
||||||
private final InjectorImpl injector;
|
private final InjectorImpl injector;
|
||||||
private final T instance;
|
private final T instance;
|
||||||
private final Object source;
|
private final Object source;
|
||||||
private final Key<T> key;
|
private final Key<T> key;
|
||||||
private final ProvisionListenerStackCallback<T> provisionCallback;
|
private final ProvisionListenerStackCallback<T> provisionCallback;
|
||||||
|
private final CycleDetectingLock<?> lock;
|
||||||
|
|
||||||
public InjectableReference(InjectorImpl injector, T instance, Key<T> key,
|
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.injector = injector;
|
||||||
this.key = key; // possibly null!
|
this.key = key; // possibly null!
|
||||||
this.provisionCallback = provisionCallback; // possibly null!
|
this.provisionCallback = provisionCallback; // possibly null!
|
||||||
this.instance = checkNotNull(instance, "instance");
|
this.instance = checkNotNull(instance, "instance");
|
||||||
this.source = checkNotNull(source, "source");
|
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>
|
@SuppressWarnings("unchecked") // the type of 'T' is a TypeLiteral<T>
|
||||||
TypeLiteral<T> type = TypeLiteral.get((Class<T>) instance.getClass());
|
TypeLiteral<T> type = TypeLiteral.get((Class<T>) instance.getClass());
|
||||||
return injector.membersInjectorStore.get(type, errors.withSource(source));
|
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
|
* 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.
|
* method will ensure that all its members have been injected before returning.
|
||||||
*/
|
*/
|
||||||
public T get(Errors errors) throws ErrorsException {
|
@Override
|
||||||
if (ready.getCount() == 0) {
|
public T get() throws InternalProvisionException {
|
||||||
|
// skipping acquiring lock if initialization is already finished
|
||||||
|
if (state == InjectableReferenceState.READY) {
|
||||||
return instance;
|
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
|
// This condition should not be possible under the current Guice implementation.
|
||||||
if (Thread.currentThread() != creatingThread) {
|
// 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;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
ready.await();
|
// lock acquired, current thread owns this instance initialization
|
||||||
|
switch (state) {
|
||||||
|
case READY:
|
||||||
return instance;
|
return instance;
|
||||||
} catch (InterruptedException e) {
|
// When instance depends on itself in the same thread potential dead lock
|
||||||
// Give up, since we don't know if our injection is ready
|
// is not detected. We have to prevent a stack overflow and we use
|
||||||
throw new RuntimeException(e);
|
// 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);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
// if in Stage.TOOL, we only want to inject & notify toolable injection points.
|
// if in Stage.TOOL, we only want to inject & notify toolable injection points.
|
||||||
// (otherwise we'll inject all of them)
|
// (otherwise we'll inject all of them)
|
||||||
membersInjector.injectAndNotify(instance,
|
try {
|
||||||
errors.withSource(source),
|
membersInjector.injectAndNotify(
|
||||||
key,
|
instance, key, provisionCallback, source, injector.options.stage == Stage.TOOL);
|
||||||
provisionCallback,
|
} catch (InternalProvisionException ipe) {
|
||||||
source,
|
throw ipe.addSource(source);
|
||||||
injector.options.stage == Stage.TOOL);
|
|
||||||
}
|
}
|
||||||
|
// mark instance as ready to skip a lock on subsequent calls
|
||||||
|
state = InjectableReferenceState.READY;
|
||||||
return instance;
|
return instance;
|
||||||
|
} finally {
|
||||||
|
// always release our creation lock, even on failures
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -98,21 +98,19 @@ final class InjectionRequestProcessor extends AbstractProcessor {
|
||||||
}
|
}
|
||||||
|
|
||||||
void injectMembers() {
|
void injectMembers() {
|
||||||
try {
|
try (InternalContext context = injector.enterContext()) {
|
||||||
injector.callInContext(new ContextualCallable<Void>() {
|
boolean isStageTool = injector.options.stage == Stage.TOOL;
|
||||||
public Void call(InternalContext context) {
|
|
||||||
for (SingleMemberInjector memberInjector : memberInjectors) {
|
for (SingleMemberInjector memberInjector : memberInjectors) {
|
||||||
// Run injections if we're not in tool stage (ie, PRODUCTION or DEV),
|
// 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.
|
// or if we are in tool stage and the injection point is toolable.
|
||||||
if (injector.options.stage != Stage.TOOL || memberInjector.getInjectionPoint().isToolable()) {
|
if (!isStageTool || memberInjector.getInjectionPoint().isToolable()) {
|
||||||
memberInjector.inject(errors, context, null);
|
try {
|
||||||
}
|
memberInjector.inject(context, null);
|
||||||
}
|
} catch (InternalProvisionException e) {
|
||||||
return null;
|
errors.merge(e);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
} catch (ErrorsException e) {
|
}
|
||||||
throw new AssertionError();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,231 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.common.collect.ArrayListMultimap;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.common.collect.ListMultimap;
|
||||||
|
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;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A container that stores an injector's binding data. This excludes JIT binding data, which is
|
||||||
|
* stored in {@link InjectorJitBindingData}.
|
||||||
|
*/
|
||||||
|
class InjectorBindingData {
|
||||||
|
|
||||||
|
// The parent injector's InjectorBindingData, if the parent injector exists.
|
||||||
|
private final Optional<InjectorBindingData> parent;
|
||||||
|
|
||||||
|
// 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<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();
|
||||||
|
private final List<ModuleAnnotatedMethodScannerBinding> scannerBindings = Lists.newArrayList();
|
||||||
|
// The injector's explicit bindings, indexed by the binding's type.
|
||||||
|
private final ListMultimap<TypeLiteral<?>, Binding<?>> indexedExplicitBindings =
|
||||||
|
ArrayListMultimap.create();
|
||||||
|
|
||||||
|
InjectorBindingData(Optional<InjectorBindingData> parent) {
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<InjectorBindingData> parent() {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked") // we only put in BindingImpls that match their key types
|
||||||
|
public <T> BindingImpl<T> getExplicitBinding(Key<T> key) {
|
||||||
|
Binding<?> binding = explicitBindings.get(key);
|
||||||
|
if (binding == null && parent.isPresent()) {
|
||||||
|
return parent.get().getExplicitBinding(key);
|
||||||
|
}
|
||||||
|
return (BindingImpl<T>) binding;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<Key<?>, Binding<?>> getExplicitBindingsThisLevel() {
|
||||||
|
return explicitBindings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putBinding(Key<?> key, BindingImpl<?> binding) {
|
||||||
|
explicitBindingsMutable.put(key, binding);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putProviderLookup(ProviderLookup<?> lookup) {
|
||||||
|
providerLookups.add(lookup);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<ProviderLookup<?>> getProviderLookupsThisLevel() {
|
||||||
|
return providerLookups;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putStaticInjectionRequest(StaticInjectionRequest staticInjectionRequest) {
|
||||||
|
staticInjectionRequests.add(staticInjectionRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<StaticInjectionRequest> getStaticInjectionRequestsThisLevel() {
|
||||||
|
return staticInjectionRequests;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putInjectionRequest(InjectionRequest<?> injectionRequest) {
|
||||||
|
injectionRequests.add(injectionRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<InjectionRequest<?>> getInjectionRequestsThisLevel() {
|
||||||
|
return injectionRequests;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putMembersInjectorLookup(MembersInjectorLookup<?> membersInjectorLookup) {
|
||||||
|
membersInjectorLookups.add(membersInjectorLookup);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<MembersInjectorLookup<?>> getMembersInjectorLookupsThisLevel() {
|
||||||
|
return membersInjectorLookups;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScopeBinding getScopeBinding(Class<? extends Annotation> annotationType) {
|
||||||
|
ScopeBinding scopeBinding = scopes.get(annotationType);
|
||||||
|
if (scopeBinding == null && parent.isPresent()) {
|
||||||
|
return parent.get().getScopeBinding(annotationType);
|
||||||
|
}
|
||||||
|
return scopeBinding;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putScopeBinding(Class<? extends Annotation> annotationType, ScopeBinding scope) {
|
||||||
|
scopes.put(annotationType, scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<ScopeBinding> getScopeBindingsThisLevel() {
|
||||||
|
return scopes.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iterable<TypeConverterBinding> getConvertersThisLevel() {
|
||||||
|
return converters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addConverter(TypeConverterBinding typeConverterBinding) {
|
||||||
|
converters.add(typeConverterBinding);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TypeConverterBinding getConverter(
|
||||||
|
String stringValue, TypeLiteral<?> type, Errors errors, Object source) {
|
||||||
|
TypeConverterBinding matchingConverter = null;
|
||||||
|
InjectorBindingData b = this;
|
||||||
|
while (b != null) {
|
||||||
|
for (TypeConverterBinding converter : b.getConvertersThisLevel()) {
|
||||||
|
if (converter.getTypeMatcher().matches(type)) {
|
||||||
|
if (matchingConverter != null) {
|
||||||
|
errors.ambiguousTypeConversion(stringValue, source, type, matchingConverter, converter);
|
||||||
|
}
|
||||||
|
matchingConverter = converter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b = b.parent().orElse(null);
|
||||||
|
}
|
||||||
|
return matchingConverter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addTypeListener(TypeListenerBinding listenerBinding) {
|
||||||
|
typeListenerBindings.add(listenerBinding);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImmutableList<TypeListenerBinding> getTypeListenerBindings() {
|
||||||
|
if (parent.isPresent()) {
|
||||||
|
return new ImmutableList.Builder<TypeListenerBinding>()
|
||||||
|
.addAll(parent.get().getTypeListenerBindings())
|
||||||
|
.addAll(typeListenerBindings)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
return ImmutableList.copyOf(typeListenerBindings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImmutableList<TypeListenerBinding> getTypeListenerBindingsThisLevel() {
|
||||||
|
return ImmutableList.copyOf(typeListenerBindings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addProvisionListener(ProvisionListenerBinding listenerBinding) {
|
||||||
|
provisionListenerBindings.add(listenerBinding);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImmutableList<ProvisionListenerBinding> getProvisionListenerBindings() {
|
||||||
|
if (parent.isPresent()) {
|
||||||
|
return new ImmutableList.Builder<ProvisionListenerBinding>()
|
||||||
|
.addAll(parent.get().getProvisionListenerBindings())
|
||||||
|
.addAll(provisionListenerBindings)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
return ImmutableList.copyOf(provisionListenerBindings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImmutableList<ProvisionListenerBinding> getProvisionListenerBindingsThisLevel() {
|
||||||
|
return ImmutableList.copyOf(provisionListenerBindings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addScanner(ModuleAnnotatedMethodScannerBinding scanner) {
|
||||||
|
scannerBindings.add(scanner);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImmutableList<ModuleAnnotatedMethodScannerBinding> getScannerBindings() {
|
||||||
|
if (parent.isPresent()) {
|
||||||
|
return new ImmutableList.Builder<ModuleAnnotatedMethodScannerBinding>()
|
||||||
|
.addAll(parent.get().getScannerBindings())
|
||||||
|
.addAll(scannerBindings)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
return ImmutableList.copyOf(scannerBindings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImmutableList<ModuleAnnotatedMethodScannerBinding> getScannerBindingsThisLevel() {
|
||||||
|
return ImmutableList.copyOf(scannerBindings);
|
||||||
|
}
|
||||||
|
|
||||||
|
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()) {
|
||||||
|
builder.put(entry.getKey(), entry.getValue().getScope());
|
||||||
|
}
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Once the injector's explicit bindings are finalized, this method is called to index all
|
||||||
|
* explicit bindings by their return type.
|
||||||
|
*/
|
||||||
|
void indexBindingsByType() {
|
||||||
|
for (Binding<?> binding : getExplicitBindingsThisLevel().values()) {
|
||||||
|
indexedExplicitBindings.put(binding.getKey().getTypeLiteral(), binding);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ListMultimap<TypeLiteral<?>, Binding<?>> getIndexedExplicitBindings() {
|
||||||
|
return indexedExplicitBindings;
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,106 @@
|
||||||
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
import com.google.inject.Key;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A container for most just-in-time (JIT) binding data corresponding to an Injector. It
|
||||||
|
* includes @Inject constructor bindings. It does not include {@link MembersInjectorStore} or {@link
|
||||||
|
* ProvisionListenerCallbackStore}, which are still stored in {@link InjectorImpl}.
|
||||||
|
*/
|
||||||
|
final class InjectorJitBindingData {
|
||||||
|
/** Just-in-time binding cache. Guarded by {@link #lock}. */
|
||||||
|
private 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. Guarded
|
||||||
|
* by {@link #lock}.
|
||||||
|
*/
|
||||||
|
private final Set<Key<?>> failedJitBindings = Sets.newHashSet();
|
||||||
|
|
||||||
|
// The set of JIT binding keys that are banned for this particular injector, because a binding
|
||||||
|
// already exists in a child injector. Guarded by {@link #lock}.
|
||||||
|
private final WeakKeySet bannedKeys;
|
||||||
|
|
||||||
|
// The InjectorJitBindingData corresponding to the Injector's parent, if it exists.
|
||||||
|
private final Optional<InjectorJitBindingData> parent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This lock is needed for threadsafe InjectorJitBindingData accesses. It corresponds to this
|
||||||
|
* InjectorJitBindingData's highest ancestor.
|
||||||
|
*/
|
||||||
|
private final Object lock;
|
||||||
|
|
||||||
|
InjectorJitBindingData(Optional<InjectorJitBindingData> parent) {
|
||||||
|
this.parent = parent;
|
||||||
|
this.lock = parent.isPresent() ? parent.get().lock() : this;
|
||||||
|
this.bannedKeys = new WeakKeySet(lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<Key<?>, BindingImpl<?>> getJitBindings() {
|
||||||
|
return Collections.unmodifiableMap(jitBindings);
|
||||||
|
}
|
||||||
|
|
||||||
|
BindingImpl<?> getJitBinding(Key<?> key) {
|
||||||
|
return jitBindings.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
void putJitBinding(Key<?> key, BindingImpl<?> binding) {
|
||||||
|
jitBindings.put(key, binding);
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeJitBinding(Key<?> key) {
|
||||||
|
jitBindings.remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isFailedJitBinding(Key<?> key) {
|
||||||
|
return failedJitBindings.contains(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addFailedJitBinding(Key<?> key) {
|
||||||
|
failedJitBindings.add(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forbids the corresponding injector and its ancestors from creating a binding to {@code key}.
|
||||||
|
* Child injectors ban their bound keys on their parent injectors to prevent just-in-time bindings
|
||||||
|
* on the parent injector that would conflict, and pass along their InjectorBindingData to control
|
||||||
|
* the banned key's lifetime.
|
||||||
|
*/
|
||||||
|
void banKey(Key<?> key, InjectorBindingData injectorBindingData, Object source) {
|
||||||
|
banKeyInParent(key, injectorBindingData, source);
|
||||||
|
bannedKeys.add(key, injectorBindingData, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Similar to {@link #banKey(Key, InjectorBindingData, Object)} but we only begin banning the
|
||||||
|
* binding at the parent level. This is used to prevent JIT bindings in the parent injector from
|
||||||
|
* overriding explicit bindings declared in a child injector.
|
||||||
|
*/
|
||||||
|
void banKeyInParent(Key<?> key, InjectorBindingData injectorBindingData, Object source) {
|
||||||
|
if (parent.isPresent()) {
|
||||||
|
parent.get().banKey(key, injectorBindingData, source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if {@code key} is forbidden from being bound in the injector corresponding to this
|
||||||
|
* data object. This indicates that one of the injector's children has bound the key.
|
||||||
|
*/
|
||||||
|
boolean isBannedKey(Key<?> key) {
|
||||||
|
return bannedKeys.contains(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the source of a banned key. */
|
||||||
|
Set<Object> getSourcesForBannedKey(Key<?> key) {
|
||||||
|
return bannedKeys.getSources(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object lock() {
|
||||||
|
return lock;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,9 @@
|
||||||
package com.google.inject.internal;
|
package com.google.inject.internal;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkState;
|
||||||
|
import static com.google.inject.Scopes.SINGLETON;
|
||||||
|
import static com.google.inject.internal.GuiceInternal.GUICE_INTERNAL;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.inject.Binder;
|
import com.google.inject.Binder;
|
||||||
|
@ -10,8 +14,9 @@ import com.google.inject.Provider;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
import com.google.inject.Stage;
|
import com.google.inject.Stage;
|
||||||
import com.google.inject.internal.InjectorImpl.InjectorOptions;
|
import com.google.inject.internal.InjectorImpl.InjectorOptions;
|
||||||
|
import com.google.inject.internal.util.ContinuousStopwatch;
|
||||||
import com.google.inject.internal.util.SourceProvider;
|
import com.google.inject.internal.util.SourceProvider;
|
||||||
import com.google.inject.internal.util.Stopwatch;
|
import com.google.inject.spi.BindingSourceRestriction;
|
||||||
import com.google.inject.spi.Dependency;
|
import com.google.inject.spi.Dependency;
|
||||||
import com.google.inject.spi.Element;
|
import com.google.inject.spi.Element;
|
||||||
import com.google.inject.spi.Elements;
|
import com.google.inject.spi.Elements;
|
||||||
|
@ -20,64 +25,34 @@ import com.google.inject.spi.ModuleAnnotatedMethodScannerBinding;
|
||||||
import com.google.inject.spi.PrivateElements;
|
import com.google.inject.spi.PrivateElements;
|
||||||
import com.google.inject.spi.ProvisionListenerBinding;
|
import com.google.inject.spi.ProvisionListenerBinding;
|
||||||
import com.google.inject.spi.TypeListenerBinding;
|
import com.google.inject.spi.TypeListenerBinding;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
import static com.google.common.base.Preconditions.checkState;
|
import java.util.logging.Logger;
|
||||||
import static com.google.inject.Scopes.SINGLETON;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A partially-initialized injector. See {@link InternalInjectorCreator}, which
|
* InjectorShell is used by {@link InternalInjectorCreator} to recursively create a tree of
|
||||||
* uses this to build a tree of injectors in batch.
|
* uninitialized {@link Injector}s. Each InjectorShell corresponds to either the top-level root
|
||||||
|
* injector, or a private child injector.
|
||||||
|
*
|
||||||
|
* <p>The root InjectorShell extracts elements from its list of modules and processes these elements
|
||||||
|
* to aggregate data that is used to populate its injector's fields. Child injectors are constructed
|
||||||
|
* similarly, but using {@link PrivateElements} instead of modules.
|
||||||
|
*
|
||||||
|
* <p>It is necessary to create the root and child injectors in a single batch because there can be
|
||||||
|
* bidirectional parent <-> child injector dependencies that require the entire tree of injectors to
|
||||||
|
* be initialized together in the {@link InternalInjectorCreator}.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
final class InjectorShell {
|
final class InjectorShell {
|
||||||
|
|
||||||
private final List<Element> elements;
|
private final List<Element> elements;
|
||||||
private final InjectorImpl injector;
|
private final InjectorImpl injector;
|
||||||
|
|
||||||
private InjectorShell(Builder builder, List<Element> elements, InjectorImpl injector) {
|
private InjectorShell(List<Element> elements, InjectorImpl injector) {
|
||||||
this.elements = elements;
|
this.elements = elements;
|
||||||
this.injector = injector;
|
this.injector = injector;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* The Injector is a special case because we allow both parent and child injectors to both have
|
|
||||||
* a binding for that key.
|
|
||||||
*/
|
|
||||||
private static void bindInjector(InjectorImpl injector) {
|
|
||||||
Key<Injector> key = Key.get(Injector.class);
|
|
||||||
InjectorFactory injectorFactory = new InjectorFactory(injector);
|
|
||||||
injector.state.putBinding(key,
|
|
||||||
new ProviderInstanceBindingImpl<Injector>(injector, key, SourceProvider.UNKNOWN_SOURCE,
|
|
||||||
injectorFactory, Scoping.UNSCOPED, injectorFactory,
|
|
||||||
ImmutableSet.<InjectionPoint>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>(
|
|
||||||
injector,
|
|
||||||
key,
|
|
||||||
SourceProvider.UNKNOWN_SOURCE,
|
|
||||||
new ConstantFactory<Stage>(Initializables.of(stage)),
|
|
||||||
ImmutableSet.<InjectionPoint>of(),
|
|
||||||
stage);
|
|
||||||
injector.state.putBinding(key, stageBinding);
|
|
||||||
}
|
|
||||||
|
|
||||||
InjectorImpl getInjector() {
|
InjectorImpl getInjector() {
|
||||||
return injector;
|
return injector;
|
||||||
}
|
}
|
||||||
|
@ -90,18 +65,15 @@ final class InjectorShell {
|
||||||
private final List<Element> elements = Lists.newArrayList();
|
private final List<Element> elements = Lists.newArrayList();
|
||||||
private final List<Module> modules = Lists.newArrayList();
|
private final List<Module> modules = Lists.newArrayList();
|
||||||
|
|
||||||
/**
|
// lazily constructed fields
|
||||||
* lazily constructed
|
private InjectorBindingData bindingData;
|
||||||
*/
|
private InjectorJitBindingData jitBindingData;
|
||||||
private State state;
|
|
||||||
|
|
||||||
private InjectorImpl parent;
|
private InjectorImpl parent;
|
||||||
private InjectorOptions options;
|
private InjectorOptions options;
|
||||||
private Stage stage;
|
private Stage stage;
|
||||||
|
|
||||||
/**
|
/** null unless this exists in a {@link Binder#newPrivateBinder private environment} */
|
||||||
* null unless this exists in a {@link Binder#newPrivateBinder private environment}
|
|
||||||
*/
|
|
||||||
private PrivateElementsImpl privateElements;
|
private PrivateElementsImpl privateElements;
|
||||||
|
|
||||||
Builder stage(Stage stage) {
|
Builder stage(Stage stage) {
|
||||||
|
@ -111,7 +83,8 @@ final class InjectorShell {
|
||||||
|
|
||||||
Builder parent(InjectorImpl parent) {
|
Builder parent(InjectorImpl parent) {
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.state = new InheritingState(parent.state);
|
this.jitBindingData = new InjectorJitBindingData(Optional.of(parent.getJitBindingData()));
|
||||||
|
this.bindingData = new InjectorBindingData(Optional.of(parent.getBindingData()));
|
||||||
this.options = parent.options;
|
this.options = parent.options;
|
||||||
this.stage = options.stage;
|
this.stage = options.stage;
|
||||||
return this;
|
return this;
|
||||||
|
@ -124,22 +97,24 @@ final class InjectorShell {
|
||||||
}
|
}
|
||||||
|
|
||||||
void addModules(Iterable<? extends Module> modules) {
|
void addModules(Iterable<? extends Module> modules) {
|
||||||
if (modules != null) {
|
|
||||||
for (Module module : modules) {
|
for (Module module : modules) {
|
||||||
this.modules.add(module);
|
this.modules.add(module);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Stage getStage() {
|
Stage getStage() {
|
||||||
return options.stage;
|
return options.stage;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Synchronize on this before calling {@link #build}. */
|
||||||
* Synchronize on this before calling {@link #build}.
|
|
||||||
*/
|
|
||||||
Object lock() {
|
Object lock() {
|
||||||
return getState().lock();
|
// Lazily initializes bindingData and jitBindingData, if they were not already
|
||||||
|
// initialized with a parent injector by {@link #parent(InjectorImpl)}.
|
||||||
|
if (bindingData == null) {
|
||||||
|
jitBindingData = new InjectorJitBindingData(Optional.empty());
|
||||||
|
bindingData = new InjectorBindingData(Optional.empty());
|
||||||
|
}
|
||||||
|
return jitBindingData.lock();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -149,27 +124,38 @@ final class InjectorShell {
|
||||||
*/
|
*/
|
||||||
List<InjectorShell> build(
|
List<InjectorShell> build(
|
||||||
Initializer initializer,
|
Initializer initializer,
|
||||||
ProcessedBindingData bindingData,
|
ProcessedBindingData processedBindingData,
|
||||||
Stopwatch stopwatch,
|
ContinuousStopwatch stopwatch,
|
||||||
Errors errors) {
|
Errors errors) {
|
||||||
checkState(stage != null, "Stage not initialized");
|
checkState(stage != null, "Stage not initialized");
|
||||||
checkState(privateElements == null || parent != null, "PrivateElements with no parent");
|
checkState(privateElements == null || parent != null, "PrivateElements with no parent");
|
||||||
checkState(state != null, "no state. Did you remember to lock() ?");
|
checkState(bindingData != null, "no binding data. Did you remember to lock() ?");
|
||||||
|
checkState(
|
||||||
|
(privateElements == null && elements.isEmpty()) || modules.isEmpty(),
|
||||||
|
"The shell is either built from modules (root) or from PrivateElements (children).");
|
||||||
|
|
||||||
// bind Singleton if this is a top-level injector
|
// bind Singleton if this is a top-level injector
|
||||||
if (parent == null) {
|
if (parent == null) {
|
||||||
modules.add(0, new RootModule());
|
modules.add(0, new RootModule());
|
||||||
} else {
|
} else {
|
||||||
modules.add(0, new InheritedScannersModule(parent.state));
|
modules.add(0, new InheritedScannersModule(parent.getBindingData()));
|
||||||
}
|
}
|
||||||
elements.addAll(Elements.getElements(stage, modules));
|
elements.addAll(Elements.getElements(stage, modules));
|
||||||
|
|
||||||
|
// Check binding source restrictions only for the root shell (note that the root shell
|
||||||
|
// can have a parent Injector, when Injector.createChildInjector is called). It isn't
|
||||||
|
// necessary to call this check on child PrivateElements shells because it walks the entire
|
||||||
|
// tree of elements, recurring on PrivateElements.
|
||||||
|
if (privateElements == null) {
|
||||||
|
elements.addAll(BindingSourceRestriction.check(GUICE_INTERNAL, elements));
|
||||||
|
}
|
||||||
|
|
||||||
// Look for injector-changing options
|
// Look for injector-changing options
|
||||||
InjectorOptionsProcessor optionsProcessor = new InjectorOptionsProcessor(errors);
|
InjectorOptionsProcessor optionsProcessor = new InjectorOptionsProcessor(errors);
|
||||||
optionsProcessor.process(null, elements);
|
optionsProcessor.process(null, elements);
|
||||||
options = optionsProcessor.getOptions(stage, options);
|
options = optionsProcessor.getOptions(stage, options);
|
||||||
|
|
||||||
InjectorImpl injector = new InjectorImpl(parent, state, options);
|
InjectorImpl injector = new InjectorImpl(parent, bindingData, jitBindingData, options);
|
||||||
if (privateElements != null) {
|
if (privateElements != null) {
|
||||||
privateElements.initInjector(injector);
|
privateElements.initInjector(injector);
|
||||||
}
|
}
|
||||||
|
@ -184,10 +170,11 @@ final class InjectorShell {
|
||||||
new MessageProcessor(errors).process(injector, elements);
|
new MessageProcessor(errors).process(injector, elements);
|
||||||
|
|
||||||
new ListenerBindingProcessor(errors).process(injector, elements);
|
new ListenerBindingProcessor(errors).process(injector, elements);
|
||||||
List<TypeListenerBinding> typeListenerBindings = injector.state.getTypeListenerBindings();
|
List<TypeListenerBinding> typeListenerBindings =
|
||||||
|
injector.getBindingData().getTypeListenerBindings();
|
||||||
injector.membersInjectorStore = new MembersInjectorStore(injector, typeListenerBindings);
|
injector.membersInjectorStore = new MembersInjectorStore(injector, typeListenerBindings);
|
||||||
List<ProvisionListenerBinding> provisionListenerBindings =
|
List<ProvisionListenerBinding> provisionListenerBindings =
|
||||||
injector.state.getProvisionListenerBindings();
|
injector.getBindingData().getProvisionListenerBindings();
|
||||||
injector.provisionListenerStore =
|
injector.provisionListenerStore =
|
||||||
new ProvisionListenerCallbackStore(provisionListenerBindings);
|
new ProvisionListenerCallbackStore(provisionListenerBindings);
|
||||||
stopwatch.resetAndLog("TypeListeners & ProvisionListener creation");
|
stopwatch.resetAndLog("TypeListeners & ProvisionListener creation");
|
||||||
|
@ -200,38 +187,53 @@ final class InjectorShell {
|
||||||
|
|
||||||
bindStage(injector, stage);
|
bindStage(injector, stage);
|
||||||
bindInjector(injector);
|
bindInjector(injector);
|
||||||
//bindLogger(injector);
|
bindLogger(injector);
|
||||||
|
|
||||||
// Process all normal bindings, then UntargettedBindings.
|
// Process all normal bindings, then UntargettedBindings.
|
||||||
// This is necessary because UntargettedBindings can create JIT bindings
|
// This is necessary because UntargettedBindings can create JIT bindings
|
||||||
// and need all their other dependencies set up ahead of time.
|
// and need all their other dependencies set up ahead of time.
|
||||||
new BindingProcessor(errors, initializer, bindingData).process(injector, elements);
|
new BindingProcessor(errors, initializer, processedBindingData).process(injector, elements);
|
||||||
new UntargettedBindingProcessor(errors, bindingData).process(injector, elements);
|
new UntargettedBindingProcessor(errors, processedBindingData).process(injector, elements);
|
||||||
stopwatch.resetAndLog("Binding creation");
|
stopwatch.resetAndLog("Binding creation");
|
||||||
|
|
||||||
new ModuleAnnotatedMethodScannerProcessor(errors).process(injector, elements);
|
new ModuleAnnotatedMethodScannerProcessor(errors).process(injector, elements);
|
||||||
stopwatch.resetAndLog("Module annotated method scanners creation");
|
stopwatch.resetAndLog("Module annotated method scanners creation");
|
||||||
|
|
||||||
List<InjectorShell> injectorShells = Lists.newArrayList();
|
List<InjectorShell> injectorShells = Lists.newArrayList();
|
||||||
injectorShells.add(new InjectorShell(this, elements, injector));
|
injectorShells.add(new InjectorShell(elements, injector));
|
||||||
|
|
||||||
// recursively build child shells
|
// recursively build child shells
|
||||||
PrivateElementProcessor processor = new PrivateElementProcessor(errors);
|
PrivateElementProcessor processor = new PrivateElementProcessor(errors);
|
||||||
processor.process(injector, elements);
|
processor.process(injector, elements);
|
||||||
for (Builder builder : processor.getInjectorShellBuilders()) {
|
for (Builder builder : processor.getInjectorShellBuilders()) {
|
||||||
injectorShells.addAll(builder.build(initializer, bindingData, stopwatch, errors));
|
injectorShells.addAll(builder.build(initializer, processedBindingData, stopwatch, errors));
|
||||||
}
|
}
|
||||||
stopwatch.resetAndLog("Private environment creation");
|
stopwatch.resetAndLog("Private environment creation");
|
||||||
|
|
||||||
return injectorShells;
|
return injectorShells;
|
||||||
}
|
}
|
||||||
|
|
||||||
private State getState() {
|
|
||||||
if (state == null) {
|
|
||||||
state = new InheritingState(State.NONE);
|
|
||||||
}
|
|
||||||
return state;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Injector is a special case because we allow both parent and child injectors to both have a
|
||||||
|
* binding for that key.
|
||||||
|
*/
|
||||||
|
private static void bindInjector(InjectorImpl injector) {
|
||||||
|
Key<Injector> key = Key.get(Injector.class);
|
||||||
|
InjectorFactory injectorFactory = new InjectorFactory(injector);
|
||||||
|
injector
|
||||||
|
.getBindingData()
|
||||||
|
.putBinding(
|
||||||
|
key,
|
||||||
|
new ProviderInstanceBindingImpl<Injector>(
|
||||||
|
injector,
|
||||||
|
key,
|
||||||
|
SourceProvider.UNKNOWN_SOURCE,
|
||||||
|
injectorFactory,
|
||||||
|
Scoping.UNSCOPED,
|
||||||
|
injectorFactory,
|
||||||
|
ImmutableSet.<InjectionPoint>of()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class InjectorFactory implements InternalFactory<Injector>, Provider<Injector> {
|
private static class InjectorFactory implements InternalFactory<Injector>, Provider<Injector> {
|
||||||
|
@ -241,38 +243,78 @@ final class InjectorShell {
|
||||||
this.injector = injector;
|
this.injector = injector;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Injector get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
|
@Override
|
||||||
throws ErrorsException {
|
public Injector get(InternalContext context, Dependency<?> dependency, boolean linked) {
|
||||||
return injector;
|
return injector;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Injector get() {
|
public Injector get() {
|
||||||
return injector;
|
return injector;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Provider<Injector>";
|
return "Provider<Injector>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*private static class LoggerFactory implements InternalFactory<Logger>, Provider<Logger> {
|
/**
|
||||||
public Logger get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked) {
|
* 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
|
||||||
|
.getBindingData()
|
||||||
|
.putBinding(
|
||||||
|
key,
|
||||||
|
new ProviderInstanceBindingImpl<Logger>(
|
||||||
|
injector,
|
||||||
|
key,
|
||||||
|
SourceProvider.UNKNOWN_SOURCE,
|
||||||
|
loggerFactory,
|
||||||
|
Scoping.UNSCOPED,
|
||||||
|
loggerFactory,
|
||||||
|
ImmutableSet.<InjectionPoint>of()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class LoggerFactory implements InternalFactory<Logger>, Provider<Logger> {
|
||||||
|
@Override
|
||||||
|
public Logger get(InternalContext context, Dependency<?> dependency, boolean linked) {
|
||||||
InjectionPoint injectionPoint = dependency.getInjectionPoint();
|
InjectionPoint injectionPoint = dependency.getInjectionPoint();
|
||||||
return injectionPoint == null
|
return injectionPoint == null
|
||||||
? Logger.getAnonymousLogger()
|
? Logger.getAnonymousLogger()
|
||||||
: Logger.getLogger(injectionPoint.getMember().getDeclaringClass().getName());
|
: Logger.getLogger(injectionPoint.getMember().getDeclaringClass().getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Logger get() {
|
public Logger get() {
|
||||||
return Logger.getAnonymousLogger();
|
return Logger.getAnonymousLogger();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Provider<Logger>";
|
return "Provider<Logger>";
|
||||||
}
|
}
|
||||||
}*/
|
}
|
||||||
|
|
||||||
|
private static void bindStage(InjectorImpl injector, Stage stage) {
|
||||||
|
Key<Stage> key = Key.get(Stage.class);
|
||||||
|
InstanceBindingImpl<Stage> stageBinding =
|
||||||
|
new InstanceBindingImpl<Stage>(
|
||||||
|
injector,
|
||||||
|
key,
|
||||||
|
SourceProvider.UNKNOWN_SOURCE,
|
||||||
|
new ConstantFactory<Stage>(Initializables.of(stage)),
|
||||||
|
ImmutableSet.<InjectionPoint>of(),
|
||||||
|
stage);
|
||||||
|
injector.getBindingData().putBinding(key, stageBinding);
|
||||||
|
}
|
||||||
|
|
||||||
private static class RootModule implements Module {
|
private static class RootModule implements Module {
|
||||||
|
@Override
|
||||||
public void configure(Binder binder) {
|
public void configure(Binder binder) {
|
||||||
binder = binder.withSource(SourceProvider.UNKNOWN_SOURCE);
|
binder = binder.withSource(SourceProvider.UNKNOWN_SOURCE);
|
||||||
binder.bindScope(Singleton.class, SINGLETON);
|
binder.bindScope(Singleton.class, SINGLETON);
|
||||||
|
@ -281,14 +323,15 @@ final class InjectorShell {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class InheritedScannersModule implements Module {
|
private static class InheritedScannersModule implements Module {
|
||||||
private final State state;
|
private final InjectorBindingData bindingData;
|
||||||
|
|
||||||
InheritedScannersModule(State state) {
|
InheritedScannersModule(InjectorBindingData bindingData) {
|
||||||
this.state = state;
|
this.bindingData = bindingData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void configure(Binder binder) {
|
public void configure(Binder binder) {
|
||||||
for (ModuleAnnotatedMethodScannerBinding binding : state.getScannerBindings()) {
|
for (ModuleAnnotatedMethodScannerBinding binding : bindingData.getScannerBindings()) {
|
||||||
binding.applyTo(binder);
|
binding.applyTo(binder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,20 +5,17 @@ import com.google.common.base.Objects;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.inject.Binder;
|
import com.google.inject.Binder;
|
||||||
import com.google.inject.Key;
|
import com.google.inject.Key;
|
||||||
import com.google.inject.Provider;
|
|
||||||
import com.google.inject.spi.BindingTargetVisitor;
|
import com.google.inject.spi.BindingTargetVisitor;
|
||||||
import com.google.inject.spi.Dependency;
|
import com.google.inject.spi.Dependency;
|
||||||
import com.google.inject.spi.HasDependencies;
|
import com.google.inject.spi.HasDependencies;
|
||||||
import com.google.inject.spi.InjectionPoint;
|
import com.google.inject.spi.InjectionPoint;
|
||||||
import com.google.inject.spi.InstanceBinding;
|
import com.google.inject.spi.InstanceBinding;
|
||||||
import com.google.inject.util.Providers;
|
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
final class InstanceBindingImpl<T> extends BindingImpl<T> implements InstanceBinding<T> {
|
final class InstanceBindingImpl<T> extends BindingImpl<T> implements InstanceBinding<T> {
|
||||||
|
|
||||||
final T instance;
|
final T instance;
|
||||||
final Provider<T> provider;
|
|
||||||
final ImmutableSet<InjectionPoint> injectionPoints;
|
final ImmutableSet<InjectionPoint> injectionPoints;
|
||||||
|
|
||||||
public InstanceBindingImpl(InjectorImpl injector, Key<T> key, Object source,
|
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);
|
super(injector, key, source, internalFactory, Scoping.EAGER_SINGLETON);
|
||||||
this.injectionPoints = ImmutableSet.copyOf(injectionPoints);
|
this.injectionPoints = ImmutableSet.copyOf(injectionPoints);
|
||||||
this.instance = instance;
|
this.instance = instance;
|
||||||
this.provider = Providers.of(instance);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public InstanceBindingImpl(Object source, Key<T> key, Scoping scoping,
|
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);
|
super(source, key, scoping);
|
||||||
this.injectionPoints = ImmutableSet.copyOf(injectionPoints);
|
this.injectionPoints = ImmutableSet.copyOf(injectionPoints);
|
||||||
this.instance = instance;
|
this.instance = instance;
|
||||||
this.provider = Providers.of(instance);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Provider<T> getProvider() {
|
|
||||||
return this.provider;
|
|
||||||
}
|
|
||||||
|
|
||||||
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
|
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
|
||||||
return visitor.visit(this);
|
return visitor.visit(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public T getInstance() {
|
public T getInstance() {
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Set<InjectionPoint> getInjectionPoints() {
|
public Set<InjectionPoint> getInjectionPoints() {
|
||||||
return injectionPoints;
|
return injectionPoints;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Set<Dependency<?>> getDependencies() {
|
public Set<Dependency<?>> getDependencies() {
|
||||||
return instance instanceof HasDependencies
|
return instance instanceof HasDependencies
|
||||||
? ImmutableSet.copyOf(((HasDependencies) instance).getDependencies())
|
? ImmutableSet.copyOf(((HasDependencies) instance).getDependencies())
|
||||||
: Dependency.forInjectionPoints(injectionPoints);
|
: Dependency.forInjectionPoints(injectionPoints);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public BindingImpl<T> withScoping(Scoping scoping) {
|
public BindingImpl<T> withScoping(Scoping scoping) {
|
||||||
return new InstanceBindingImpl<T>(getSource(), getKey(), scoping, injectionPoints, instance);
|
return new InstanceBindingImpl<T>(getSource(), getKey(), scoping, injectionPoints, instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public BindingImpl<T> withKey(Key<T> key) {
|
public BindingImpl<T> withKey(Key<T> key) {
|
||||||
return new InstanceBindingImpl<T>(getSource(), key, getScoping(), injectionPoints, instance);
|
return new InstanceBindingImpl<T>(getSource(), key, getScoping(), injectionPoints, instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void applyTo(Binder binder) {
|
public void applyTo(Binder binder) {
|
||||||
// instance bindings aren't scoped
|
// instance bindings aren't scoped
|
||||||
binder.withSource(getSource()).bind(getKey()).toInstance(instance);
|
binder.withSource(getSource()).bind(getKey()).toInstance(instance);
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue