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):
|
||||
|
||||
- only external dependencies are
|
||||
- javax.inject:javax.inject:1
|
||||
- javax.annotation:javax.annotation-api:1.2
|
||||
- com.google.guava:guava:19.0
|
||||
- org.xbib:jsr-305:1.0.0
|
||||
- com.google.guava:guava:30.1
|
||||
|
||||
- the extra AOP stuff was removed (aopalliance/cglib), no intercepting any more
|
||||
- removed all logging, uses of java.util.logger.* stuff and logger default binding
|
||||
- removed all java.io.Serializable dependent stuff
|
||||
- removed all java.io.Serializable dependent stuff. Note: everything which relies to be serializable was removed.
|
||||
- reformatted source by IntelliJ and cleaned up source of @author and @since tags
|
||||
- target of compilation is Java 8, no support for Java 6/7
|
||||
- target of compilation is Java 11, no support for Java 6/7
|
||||
- introduced Gradle as build system
|
||||
- junit tests made compatible to junit 4.12
|
||||
- assistedinject and multibindings extension included
|
||||
- junit tests made compatible to junit 4
|
||||
- assistedinject and multibindings extensions are included
|
||||
|
||||
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");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
|
124
build.gradle
124
build.gradle
|
@ -1,85 +1,51 @@
|
|||
|
||||
group = 'org.xbib'
|
||||
version = '4.0'
|
||||
|
||||
apply plugin: 'java'
|
||||
apply plugin: 'maven'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
mavenLocal()
|
||||
jcenter()
|
||||
maven {
|
||||
url "http://xbib.org/repository"
|
||||
}
|
||||
plugins {
|
||||
//id "checkstyle"
|
||||
//id "pmd"
|
||||
id 'maven-publish'
|
||||
id 'signing'
|
||||
id "io.github.gradle-nexus.publish-plugin" version "1.3.0"
|
||||
//id "com.github.spotbugs" version "5.0.14"
|
||||
id "org.cyclonedx.bom" version "1.7.2"
|
||||
//id "org.xbib.gradle.plugin.asciidoctor" version "3.0.0"
|
||||
}
|
||||
|
||||
configurations {
|
||||
wagon
|
||||
wrapper {
|
||||
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 {
|
||||
compile "javax.inject:javax.inject:1"
|
||||
compile 'javax.annotation:javax.annotation-api:1.2'
|
||||
compile "com.google.guava:guava:19.0"
|
||||
testCompile "junit:junit:4.12"
|
||||
testCompile "org.apache.logging.log4j:log4j-slf4j-impl:2.5"
|
||||
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'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
api libs.javax.inject
|
||||
api libs.guava
|
||||
testImplementation libs.junit4
|
||||
// Helper for com.google.common.testing.GcFinalization, com.google.common.testing.EqualsTester
|
||||
testImplementation libs.guava.testlib
|
||||
}
|
||||
|
|
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;
|
||||
|
||||
@Override
|
||||
public final synchronized void configure(Binder builder) {
|
||||
checkState(this.binder == null, "Re-entry is not allowed.");
|
||||
|
||||
|
@ -63,8 +64,7 @@ public abstract class AbstractModule implements Module {
|
|||
/**
|
||||
* @see Binder#bindScope(Class, Scope)
|
||||
*/
|
||||
protected void bindScope(Class<? extends Annotation> scopeAnnotation,
|
||||
Scope scope) {
|
||||
protected void bindScope(Class<? extends Annotation> scopeAnnotation, Scope scope) {
|
||||
binder().bindScope(scopeAnnotation, scope);
|
||||
}
|
||||
|
||||
|
|
|
@ -100,7 +100,7 @@ import java.lang.reflect.Proxy;
|
|||
* contribute their own custom scopes for use here as well.
|
||||
*
|
||||
* <pre>
|
||||
* bind(new TypeLiteral<PaymentService<CreditCard>>() {})
|
||||
* bind(new TypeLiteral<PaymentService<CreditCard>>() {})
|
||||
* .to(CreditCardPaymentService.class);</pre>
|
||||
*
|
||||
* This admittedly odd construct is the way to bind a parameterized type. It
|
||||
|
@ -159,7 +159,7 @@ import java.lang.reflect.Proxy;
|
|||
* your application.
|
||||
*
|
||||
* <pre>
|
||||
* Constructor<T> loneCtor = getLoneCtorFromServiceImplViaReflection();
|
||||
* Constructor<T> loneCtor = getLoneCtorFromServiceImplViaReflection();
|
||||
* bind(ServiceImpl.class)
|
||||
* .toConstructor(loneCtor);</pre>
|
||||
*
|
||||
|
@ -367,7 +367,7 @@ public interface Binder {
|
|||
* their clients.
|
||||
* @return a binder that shares its configuration with this binder.
|
||||
*/
|
||||
Binder skipSources(Class... classesToSkip);
|
||||
Binder skipSources(Class<?>... classesToSkip);
|
||||
|
||||
/**
|
||||
* Creates a new private child environment for bindings and other configuration. The returned
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package com.google.inject;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.inject.internal.Errors;
|
||||
import com.google.inject.internal.Messages;
|
||||
import com.google.inject.spi.Message;
|
||||
|
||||
import java.util.Collection;
|
||||
|
@ -24,7 +24,7 @@ public final class ConfigurationException extends RuntimeException {
|
|||
*/
|
||||
public ConfigurationException(Iterable<Message> messages) {
|
||||
this.messages = ImmutableSet.copyOf(messages);
|
||||
initCause(Errors.getOnlyCause(this.messages));
|
||||
initCause(Messages.getOnlyCause(this.messages));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -59,6 +59,6 @@ public final class ConfigurationException extends RuntimeException {
|
|||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return Errors.format("Guice configuration errors", messages);
|
||||
return Messages.formatMessages("Guice configuration errors", messages);
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
package com.google.inject;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.inject.internal.Errors;
|
||||
import com.google.inject.internal.Messages;
|
||||
import com.google.inject.spi.Message;
|
||||
|
||||
import java.util.Collection;
|
||||
|
@ -12,9 +12,9 @@ import static com.google.common.base.Preconditions.checkArgument;
|
|||
* Thrown when errors occur while creating a {@link Injector}. Includes a list of encountered
|
||||
* errors. Clients should catch this exception, log it, and stop execution.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class CreationException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 0;
|
||||
private final ImmutableSet<Message> messages;
|
||||
|
||||
/**
|
||||
|
@ -23,7 +23,7 @@ public class CreationException extends RuntimeException {
|
|||
public CreationException(Collection<Message> messages) {
|
||||
this.messages = ImmutableSet.copyOf(messages);
|
||||
checkArgument(!this.messages.isEmpty());
|
||||
initCause(Errors.getOnlyCause(this.messages));
|
||||
initCause(Messages.getOnlyCause(this.messages));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -35,6 +35,6 @@ public class CreationException extends RuntimeException {
|
|||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return Errors.format("Unable to create injector, see the following errors", messages);
|
||||
return Messages.formatMessages("Unable to create injector, see the following errors", messages);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,11 +76,7 @@ public final class Guice {
|
|||
* @throws CreationException if one or more errors occur during injector
|
||||
* construction
|
||||
*/
|
||||
public static Injector createInjector(Stage stage,
|
||||
Iterable<? extends Module> modules) {
|
||||
return new InternalInjectorCreator()
|
||||
.stage(stage)
|
||||
.addModules(modules)
|
||||
.build();
|
||||
public static Injector createInjector(Stage stage, Iterable<? extends Module> modules) {
|
||||
return new InternalInjectorCreator().stage(stage).addModules(modules).build();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package com.google.inject;
|
||||
|
||||
import com.google.inject.spi.Element;
|
||||
import com.google.inject.spi.InjectionPoint;
|
||||
import com.google.inject.spi.TypeConverterBinding;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
|
@ -225,4 +227,37 @@ public interface Injector {
|
|||
* <p>This method is part of the Guice SPI and is intended for use by tools and extensions.
|
||||
*/
|
||||
Set<TypeConverterBinding> getTypeConverterBindings();
|
||||
|
||||
|
||||
/**
|
||||
* Returns the elements that make up this injector. Note that not all kinds of elements are
|
||||
* returned.
|
||||
*
|
||||
* <p>The returned list does not include elements inherited from a {@link #getParent() parent
|
||||
* injector}, should one exist.
|
||||
*
|
||||
* <p>The returned list is immutable; it contains only the elements that were present when {@link
|
||||
* #getElements} was invoked. Subsequent calls may return a list with additional elements.
|
||||
*
|
||||
* <p>The returned list does not include data inherited from a {@link #getParent() parent
|
||||
* injector}, should one exist.
|
||||
*
|
||||
* <p>This method is part of the Guice SPI and is intended for use by tools and extensions.
|
||||
*/
|
||||
List<Element> getElements();
|
||||
|
||||
/**
|
||||
* Returns the injection points created for calls to {@link #getMembersInjector} (either directly
|
||||
* or indirectly, e.g. through {@link #injectMembers}.
|
||||
*
|
||||
* <p>This excludes any injection points from elements (which are accessible from each element via
|
||||
* the SPI), unless {@link #getMembersInjector} or {@link #injectMembers} were also called for the
|
||||
* same key.
|
||||
*
|
||||
* <p>The returned multimap does not include data inherited from a {@link #getParent() parent
|
||||
* injector}, should one exist.
|
||||
*
|
||||
* <p>This method is part of the Guice SPI and is intended for use by tools and extensions.
|
||||
*/
|
||||
Map<TypeLiteral<?>, List<InjectionPoint>> getAllMembersInjectorInjectionPoints();
|
||||
}
|
||||
|
|
|
@ -1,24 +1,20 @@
|
|||
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.checkNotNull;
|
||||
import static com.google.inject.internal.Annotations.generateAnnotation;
|
||||
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.
|
||||
* Matches the type and annotation at a point of injection.
|
||||
* Guice uses Key objects to identify a dependency that can be resolved by the Guice {@link
|
||||
* Injector}. A Guice key consists of an injection type and an optional annotation.
|
||||
*
|
||||
* <p>For example, {@code Key.get(Service.class, Transactional.class)} will
|
||||
* match:
|
||||
* <p>For example, {@code Key.get(Service.class, Transactional.class)} will match:
|
||||
*
|
||||
* <pre>
|
||||
* {@literal @}Inject
|
||||
|
@ -27,12 +23,13 @@ import static com.google.inject.internal.Annotations.isAllDefaultMethods;
|
|||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>{@code Key} supports generic types via subclassing just like {@link
|
||||
* TypeLiteral}.
|
||||
* <p>{@code Key} supports generic types via subclassing just like {@link TypeLiteral}.
|
||||
*
|
||||
* <p>Keys do not differentiate between primitive types (int, char, etc.) and
|
||||
* their corresponding wrapper types (Integer, Character, etc.). Primitive
|
||||
* types will be replaced with their wrapper types when keys are created.
|
||||
* <p>Keys do not differentiate between primitive types (int, char, etc.) and their corresponding
|
||||
* wrapper types (Integer, Character, etc.). Primitive types will be replaced with their wrapper
|
||||
* types when keys are created.
|
||||
*
|
||||
* @author crazybob@google.com (Bob Lee)
|
||||
*/
|
||||
public class Key<T> {
|
||||
|
||||
|
@ -40,38 +37,37 @@ public class Key<T> {
|
|||
|
||||
private final TypeLiteral<T> typeLiteral;
|
||||
private final int hashCode;
|
||||
private final Supplier<String> toStringSupplier;
|
||||
// This field is updated using the 'Data-Race-Ful' lazy intialization pattern
|
||||
// See http://jeremymanson.blogspot.com/2008/12/benign-data-races-in-java.html for a detailed
|
||||
// explanation.
|
||||
private String toString;
|
||||
|
||||
/**
|
||||
* Constructs a new key. Derives the type from this class's type parameter.
|
||||
*
|
||||
* <p>Clients create an empty anonymous subclass. Doing so embeds the type
|
||||
* parameter in the anonymous class's type hierarchy so we can reconstitute it
|
||||
* at runtime despite erasure.
|
||||
* <p>Clients create an empty anonymous subclass. Doing so embeds the type parameter in the
|
||||
* anonymous class's type hierarchy so we can reconstitute it at runtime despite erasure.
|
||||
*
|
||||
* <p>Example usage for a binding of type {@code Foo} annotated with
|
||||
* {@code @Bar}:
|
||||
* <p>Example usage for a binding of type {@code Foo} annotated with {@code @Bar}:
|
||||
*
|
||||
* <p>{@code new Key<Foo>(Bar.class) {}}.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Key(Class<? extends Annotation> annotationType) {
|
||||
this.annotationStrategy = strategyFor(annotationType);
|
||||
this.typeLiteral = MoreTypes.canonicalizeForKey(
|
||||
(TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass()));
|
||||
this.typeLiteral =
|
||||
MoreTypes.canonicalizeForKey(
|
||||
(TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass()));
|
||||
this.hashCode = computeHashCode();
|
||||
this.toStringSupplier = createToStringSupplier();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* parameter in the anonymous class's type hierarchy so we can reconstitute it
|
||||
* at runtime despite erasure.
|
||||
* <p>Clients create an empty anonymous subclass. Doing so embeds the type parameter in the
|
||||
* anonymous class's type hierarchy so we can reconstitute it at runtime despite erasure.
|
||||
*
|
||||
* <p>Example usage for a binding of type {@code Foo} annotated with
|
||||
* {@code @Bar}:
|
||||
* <p>Example usage for a binding of type {@code Foo} annotated with {@code @Bar}:
|
||||
*
|
||||
* <p>{@code new Key<Foo>(new Bar()) {}}.
|
||||
*/
|
||||
|
@ -79,18 +75,17 @@ public class Key<T> {
|
|||
protected Key(Annotation annotation) {
|
||||
// no usages, not test-covered
|
||||
this.annotationStrategy = strategyFor(annotation);
|
||||
this.typeLiteral = MoreTypes.canonicalizeForKey(
|
||||
(TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass()));
|
||||
this.typeLiteral =
|
||||
MoreTypes.canonicalizeForKey(
|
||||
(TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass()));
|
||||
this.hashCode = computeHashCode();
|
||||
this.toStringSupplier = createToStringSupplier();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* parameter in the anonymous class's type hierarchy so we can reconstitute it
|
||||
* at runtime despite erasure.
|
||||
* <p>Clients create an empty anonymous subclass. Doing so embeds the type parameter in the
|
||||
* anonymous class's type hierarchy so we can reconstitute it at runtime despite erasure.
|
||||
*
|
||||
* <p>Example usage for a binding of type {@code Foo}:
|
||||
*
|
||||
|
@ -99,191 +94,51 @@ public class Key<T> {
|
|||
@SuppressWarnings("unchecked")
|
||||
protected Key() {
|
||||
this.annotationStrategy = NullAnnotationStrategy.INSTANCE;
|
||||
this.typeLiteral = MoreTypes.canonicalizeForKey(
|
||||
(TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass()));
|
||||
this.typeLiteral =
|
||||
MoreTypes.canonicalizeForKey(
|
||||
(TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass()));
|
||||
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")
|
||||
private Key(Type type, AnnotationStrategy annotationStrategy) {
|
||||
this.annotationStrategy = annotationStrategy;
|
||||
this.typeLiteral = MoreTypes.canonicalizeForKey((TypeLiteral<T>) TypeLiteral.get(type));
|
||||
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) {
|
||||
this.annotationStrategy = annotationStrategy;
|
||||
this.typeLiteral = MoreTypes.canonicalizeForKey(typeLiteral);
|
||||
this.hashCode = computeHashCode();
|
||||
this.toStringSupplier = createToStringSupplier();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a key for an injection type and an annotation strategy.
|
||||
*/
|
||||
static <T> Key<T> get(Class<T> type,
|
||||
AnnotationStrategy annotationStrategy) {
|
||||
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.
|
||||
*/
|
||||
/** Computes the hash code for this key. */
|
||||
private int computeHashCode() {
|
||||
return typeLiteral.hashCode() * 31 + annotationStrategy.hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a {@link Supplier} which memoizes the value for lazy initialization.
|
||||
*/
|
||||
private Supplier<String> createToStringSupplier() {
|
||||
// The performance hit on access is acceptable since the intended use is for non-performance-
|
||||
// critical applications.
|
||||
return Suppliers.memoize(new Supplier<String>() {
|
||||
@Override
|
||||
public String get() {
|
||||
return "Key[type=" + typeLiteral + ", annotation=" + annotationStrategy + "]";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the key type.
|
||||
*/
|
||||
/** Gets the key type. */
|
||||
public final TypeLiteral<T> getTypeLiteral() {
|
||||
return typeLiteral;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the annotation type.
|
||||
*/
|
||||
/** Gets the annotation type. Will be {@code null} if this key lacks an annotation. */
|
||||
public final Class<? extends Annotation> 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() {
|
||||
return annotationStrategy.getAnnotation();
|
||||
}
|
||||
|
@ -306,9 +161,7 @@ public class Key<T> {
|
|||
return typeLiteral.getRawType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the key of this key's provider.
|
||||
*/
|
||||
/** Gets the key of this key's provider. */
|
||||
Key<Provider<T>> providerKey() {
|
||||
return ofType(typeLiteral.providerType());
|
||||
}
|
||||
|
@ -333,73 +186,136 @@ public class Key<T> {
|
|||
|
||||
@Override
|
||||
public final String toString() {
|
||||
return toStringSupplier.get();
|
||||
// Note: to not introduce dangerous data races the field should only be read once in this
|
||||
// method.
|
||||
String local = toString;
|
||||
if (local == null) {
|
||||
local = "Key[type=" + typeLiteral + ", annotation=" + annotationStrategy + "]";
|
||||
toString = local;
|
||||
}
|
||||
return local;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new key of the specified type with the same annotation as this
|
||||
* key.
|
||||
*/
|
||||
public <T> Key<T> ofType(Class<T> type) {
|
||||
/** 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<>(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
|
||||
* key.
|
||||
* Returns a new key of the specified type with the same annotation as this 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) {
|
||||
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
|
||||
* key.
|
||||
* Returns a new key of the specified type with the same annotation as this key.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public <T> Key<T> ofType(TypeLiteral<T> type) {
|
||||
return new Key<T>(type, annotationStrategy);
|
||||
public <U> Key<U> ofType(TypeLiteral<U> type) {
|
||||
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.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public boolean hasAttributes() {
|
||||
return annotationStrategy.hasAttributes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this key without annotation attributes, i.e. with only the
|
||||
* annotation type.
|
||||
* Returns this key without annotation attributes, i.e. with only the annotation type.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public Key<T> 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 {
|
||||
Annotation getAnnotation();
|
||||
|
||||
|
@ -410,6 +326,76 @@ public class Key<T> {
|
|||
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
|
||||
static class AnnotationInstanceStrategy implements AnnotationStrategy {
|
||||
|
||||
|
@ -419,18 +405,22 @@ public class Key<T> {
|
|||
this.annotation = checkNotNull(annotation, "annotation");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAttributes() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationStrategy withoutAttributes() {
|
||||
return new AnnotationTypeStrategy(getAnnotationType(), annotation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Annotation getAnnotation() {
|
||||
return annotation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Annotation> getAnnotationType() {
|
||||
return annotation.annotationType();
|
||||
}
|
||||
|
@ -463,24 +453,27 @@ public class Key<T> {
|
|||
// Keep the instance around if we have it so the client can request it.
|
||||
final Annotation annotation;
|
||||
|
||||
AnnotationTypeStrategy(Class<? extends Annotation> annotationType,
|
||||
Annotation annotation) {
|
||||
AnnotationTypeStrategy(Class<? extends Annotation> annotationType, Annotation annotation) {
|
||||
this.annotationType = checkNotNull(annotationType, "annotation type");
|
||||
this.annotation = annotation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAttributes() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationStrategy withoutAttributes() {
|
||||
throw new UnsupportedOperationException("Key already has no attributes.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Annotation getAnnotation() {
|
||||
return annotation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Annotation> getAnnotationType() {
|
||||
return annotationType;
|
||||
}
|
||||
|
|
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 skipSources(Class... classesToSkip);
|
||||
PrivateBinder skipSources(Class<?>... classesToSkip);
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package com.google.inject;
|
|||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
import javax.inject.Provider;
|
||||
|
||||
import static java.lang.annotation.ElementType.TYPE;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
|
|
@ -32,5 +32,6 @@ public interface Provider<T> extends javax.inject.Provider<T> {
|
|||
* @throws ProvisionException if an instance cannot be provided. Such exceptions include messages
|
||||
* and throwables to describe why provision failed.
|
||||
*/
|
||||
@Override
|
||||
T get();
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package com.google.inject;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.inject.internal.Errors;
|
||||
import com.google.inject.internal.Messages;
|
||||
import com.google.inject.spi.Message;
|
||||
|
||||
import java.util.Collection;
|
||||
|
@ -23,7 +23,7 @@ public final class ProvisionException extends RuntimeException {
|
|||
public ProvisionException(Iterable<Message> messages) {
|
||||
this.messages = ImmutableSet.copyOf(messages);
|
||||
checkArgument(!this.messages.isEmpty());
|
||||
initCause(Errors.getOnlyCause(this.messages));
|
||||
initCause(Messages.getOnlyCause(this.messages));
|
||||
}
|
||||
|
||||
public ProvisionException(String message, Throwable cause) {
|
||||
|
@ -44,6 +44,6 @@ public final class ProvisionException extends RuntimeException {
|
|||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return Errors.format("Unable to provision, see the following errors", messages);
|
||||
return Messages.formatMessages("Unable to provision, see the following errors", messages);
|
||||
}
|
||||
}
|
||||
|
|
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.
|
||||
*/
|
||||
public static final Scope NO_SCOPE = new Scope() {
|
||||
@Override
|
||||
public <T> Provider<T> scope(Key<T> key, Provider<T> unscoped) {
|
||||
return unscoped;
|
||||
}
|
||||
|
@ -37,21 +38,26 @@ public class Scopes {
|
|||
return "Scopes.NO_SCOPE";
|
||||
}
|
||||
};
|
||||
|
||||
private static final BindingScopingVisitor<Boolean> IS_SINGLETON_VISITOR
|
||||
= new BindingScopingVisitor<Boolean>() {
|
||||
= new BindingScopingVisitor<>() {
|
||||
@Override
|
||||
public Boolean visitNoScoping() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitScopeAnnotation(Class<? extends Annotation> scopeAnnotation) {
|
||||
return scopeAnnotation == Singleton.class
|
||||
|| scopeAnnotation == javax.inject.Singleton.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitScope(Scope scope) {
|
||||
return scope == Scopes.SINGLETON;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitEagerSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
@ -107,18 +113,22 @@ public class Scopes {
|
|||
final Class<? extends Annotation> scopeAnnotation) {
|
||||
do {
|
||||
boolean matches = binding.acceptScopingVisitor(new BindingScopingVisitor<Boolean>() {
|
||||
@Override
|
||||
public Boolean visitNoScoping() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitScopeAnnotation(Class<? extends Annotation> visitedAnnotation) {
|
||||
return visitedAnnotation == scopeAnnotation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitScope(Scope visitedScope) {
|
||||
return visitedScope == scope;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitEagerSingleton() {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -170,7 +170,7 @@ public class TypeLiteral<T> {
|
|||
// this implementation is made a little more complicated in an attempt to avoid object-creation
|
||||
while (true) {
|
||||
if (toResolve instanceof TypeVariable) {
|
||||
TypeVariable original = (TypeVariable) toResolve;
|
||||
TypeVariable<?> original = (TypeVariable) toResolve;
|
||||
toResolve = MoreTypes.resolveTypeVariable(type, rawType, original);
|
||||
if (toResolve == original) {
|
||||
return toResolve;
|
||||
|
|
|
@ -5,14 +5,15 @@ import com.google.inject.Key;
|
|||
import com.google.inject.Module;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.TypeLiteral;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
|
||||
/**
|
||||
* Provides a factory that combines the caller's arguments with injector-supplied values to
|
||||
* construct objects.
|
||||
*
|
||||
* <h3>Defining a factory</h3>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
|
@ -24,6 +25,7 @@ import java.lang.annotation.Annotation;
|
|||
* or <i>newPayment</i>.
|
||||
*
|
||||
* <h3>Creating a type that accepts factory parameters</h3>
|
||||
*
|
||||
* {@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
|
||||
* 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>
|
||||
*
|
||||
* <h3>Multiple factory methods for the same type</h3>
|
||||
*
|
||||
* 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
|
||||
* {@literal @}{@link AssistedInject}, in order to match the different parameters types of the
|
||||
* factory methods.
|
||||
* constructors in your concrete class, each constructor marked with with {@literal @}{@link
|
||||
* AssistedInject}, in order to match the different parameters types of the factory methods.
|
||||
*
|
||||
* <pre>public interface PaymentFactory {
|
||||
* Payment create(Date startDate, Money amount);
|
||||
|
@ -72,8 +74,8 @@ import java.lang.annotation.Annotation;
|
|||
* }</pre>
|
||||
*
|
||||
* <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()
|
||||
* .implement(Payment.class, RealPayment.class)
|
||||
|
@ -83,8 +85,9 @@ import java.lang.annotation.Annotation;
|
|||
* factory cannot be used until the injector has been initialized.
|
||||
*
|
||||
* <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 {
|
||||
* Payment create(Date startDate, Money amount);
|
||||
|
@ -100,221 +103,222 @@ import java.lang.annotation.Annotation;
|
|||
* // will be 'Shipment' itself, which is legal if it's not an interface.
|
||||
* .implement(Receipt.class, RealReceipt.class)
|
||||
* .build(OrderFactory.class));</pre>
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* <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 {
|
||||
* {@literal @}Inject private PaymentFactory paymentFactory;
|
||||
* {@literal @}Inject private PaymentFactory paymentFactory;
|
||||
*
|
||||
* public void doPayment(Money amount) {
|
||||
* Payment payment = paymentFactory.create(new Date(), amount);
|
||||
* payment.apply();
|
||||
* }
|
||||
* public void doPayment(Money amount) {
|
||||
* Payment payment = paymentFactory.create(new Date(), amount);
|
||||
* payment.apply();
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* <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
|
||||
* parameters. The names must be applied to the factory method's parameters:
|
||||
*
|
||||
* 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 parameters.
|
||||
* The names must be applied to the factory method's parameters:
|
||||
*
|
||||
* <pre>public interface PaymentFactory {
|
||||
* Payment create(
|
||||
* <strong>{@literal @}Assisted("startDate")</strong> Date startDate,
|
||||
* <strong>{@literal @}Assisted("dueDate")</strong> Date dueDate,
|
||||
* Money amount);
|
||||
* Payment create(
|
||||
* <strong>{@literal @}Assisted("startDate")</strong> Date startDate,
|
||||
* <strong>{@literal @}Assisted("dueDate")</strong> Date dueDate,
|
||||
* Money amount);
|
||||
* } </pre>
|
||||
*
|
||||
* ...and to the concrete type's constructor parameters:
|
||||
*
|
||||
* <pre>public class RealPayment implements Payment {
|
||||
* {@literal @}Inject
|
||||
* public RealPayment(
|
||||
* CreditService creditService,
|
||||
* AuthService authService,
|
||||
* <strong>{@literal @}Assisted("startDate")</strong> Date startDate,
|
||||
* <strong>{@literal @}Assisted("dueDate")</strong> Date dueDate,
|
||||
* <strong>{@literal @}Assisted</strong> Money amount) {
|
||||
* ...
|
||||
* }
|
||||
* {@literal @}Inject
|
||||
* public RealPayment(
|
||||
* CreditService creditService,
|
||||
* AuthService authService,
|
||||
* <strong>{@literal @}Assisted("startDate")</strong> Date startDate,
|
||||
* <strong>{@literal @}Assisted("dueDate")</strong> Date dueDate,
|
||||
* <strong>{@literal @}Assisted</strong> Money amount) {
|
||||
* ...
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* <h3>Values are created by Guice</h3>
|
||||
*
|
||||
* 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
|
||||
* returned.
|
||||
*
|
||||
* <h3>More configuration options</h3>
|
||||
*
|
||||
* 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:
|
||||
* <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:
|
||||
*
|
||||
* <pre>public interface FruitFactory {
|
||||
* Apple getApple(Color color);
|
||||
* Apple getApple(Color color);
|
||||
* }
|
||||
* ...
|
||||
* protected void configure() {
|
||||
* install(new FactoryModuleBuilder().build(FruitFactory.class));
|
||||
* install(new FactoryModuleBuilder().build(FruitFactory.class));
|
||||
* }</pre>
|
||||
*
|
||||
* 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:
|
||||
*
|
||||
* <pre>interface CarFactory {
|
||||
* {@literal @}Named("fast") Car getFastCar(Color color);
|
||||
* {@literal @}Named("clean") Car getCleanCar(Color color);
|
||||
* {@literal @}Named("fast") Car getFastCar(Color color);
|
||||
* {@literal @}Named("clean") Car getCleanCar(Color color);
|
||||
* }
|
||||
* ...
|
||||
* protected void configure() {
|
||||
* install(new FactoryModuleBuilder()
|
||||
* .implement(Car.class, Names.named("fast"), Porsche.class)
|
||||
* .implement(Car.class, Names.named("clean"), Prius.class)
|
||||
* .build(CarFactory.class));
|
||||
* install(new FactoryModuleBuilder()
|
||||
* .implement(Car.class, Names.named("fast"), Porsche.class)
|
||||
* .implement(Car.class, Names.named("clean"), Prius.class)
|
||||
* .build(CarFactory.class));
|
||||
* }</pre>
|
||||
*
|
||||
* <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 {
|
||||
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
return implement(source, TypeLiteral.get(target));
|
||||
}
|
||||
|
||||
/**
|
||||
* See the factory configuration examples at {@link FactoryModuleBuilder}.
|
||||
*/
|
||||
public <T> FactoryModuleBuilder implement(TypeLiteral<T> source,
|
||||
TypeLiteral<? extends T> target) {
|
||||
/** See the factory configuration examples at {@link FactoryModuleBuilder}. */
|
||||
public <T> FactoryModuleBuilder implement(
|
||||
TypeLiteral<T> source, TypeLiteral<? extends T> target) {
|
||||
return implement(Key.get(source), target);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the factory configuration examples at {@link FactoryModuleBuilder}.
|
||||
*/
|
||||
public <T> FactoryModuleBuilder implement(Class<T> source, Annotation annotation,
|
||||
Class<? extends T> target) {
|
||||
/** See the factory configuration examples at {@link FactoryModuleBuilder}. */
|
||||
public <T> FactoryModuleBuilder implement(
|
||||
Class<T> source, Annotation annotation, Class<? extends T> target) {
|
||||
return implement(source, annotation, TypeLiteral.get(target));
|
||||
}
|
||||
|
||||
/**
|
||||
* See the factory configuration examples at {@link FactoryModuleBuilder}.
|
||||
*/
|
||||
public <T> FactoryModuleBuilder implement(Class<T> source, Annotation annotation,
|
||||
TypeLiteral<? extends T> target) {
|
||||
/** See the factory configuration examples at {@link FactoryModuleBuilder}. */
|
||||
public <T> FactoryModuleBuilder implement(
|
||||
Class<T> source, Annotation annotation, TypeLiteral<? extends T> target) {
|
||||
return implement(TypeLiteral.get(source), annotation, target);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the factory configuration examples at {@link FactoryModuleBuilder}.
|
||||
*/
|
||||
public <T> FactoryModuleBuilder implement(TypeLiteral<T> source, Annotation annotation,
|
||||
Class<? extends T> target) {
|
||||
/** See the factory configuration examples at {@link FactoryModuleBuilder}. */
|
||||
public <T> FactoryModuleBuilder implement(
|
||||
TypeLiteral<T> source, Annotation annotation, Class<? extends T> target) {
|
||||
return implement(source, annotation, TypeLiteral.get(target));
|
||||
}
|
||||
|
||||
/**
|
||||
* See the factory configuration examples at {@link FactoryModuleBuilder}.
|
||||
*/
|
||||
public <T> FactoryModuleBuilder implement(TypeLiteral<T> source, Annotation annotation,
|
||||
TypeLiteral<? extends T> target) {
|
||||
/** See the factory configuration examples at {@link FactoryModuleBuilder}. */
|
||||
public <T> FactoryModuleBuilder implement(
|
||||
TypeLiteral<T> source, Annotation annotation, TypeLiteral<? extends T> target) {
|
||||
return implement(Key.get(source, annotation), target);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the factory configuration examples at {@link FactoryModuleBuilder}.
|
||||
*/
|
||||
public <T> FactoryModuleBuilder implement(Class<T> source,
|
||||
Class<? extends Annotation> annotationType, Class<? extends T> target) {
|
||||
/** See the factory configuration examples at {@link FactoryModuleBuilder}. */
|
||||
public <T> FactoryModuleBuilder implement(
|
||||
Class<T> source, Class<? extends Annotation> annotationType, Class<? extends T> target) {
|
||||
return implement(source, annotationType, TypeLiteral.get(target));
|
||||
}
|
||||
|
||||
/**
|
||||
* See the factory configuration examples at {@link FactoryModuleBuilder}.
|
||||
*/
|
||||
public <T> FactoryModuleBuilder implement(Class<T> source,
|
||||
Class<? extends Annotation> annotationType, TypeLiteral<? extends T> target) {
|
||||
/** See the factory configuration examples at {@link FactoryModuleBuilder}. */
|
||||
public <T> FactoryModuleBuilder implement(
|
||||
Class<T> source,
|
||||
Class<? extends Annotation> annotationType,
|
||||
TypeLiteral<? extends T> target) {
|
||||
return implement(TypeLiteral.get(source), annotationType, target);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the factory configuration examples at {@link FactoryModuleBuilder}.
|
||||
*/
|
||||
public <T> FactoryModuleBuilder implement(TypeLiteral<T> source,
|
||||
Class<? extends Annotation> annotationType, Class<? extends T> target) {
|
||||
/** See the factory configuration examples at {@link FactoryModuleBuilder}. */
|
||||
public <T> FactoryModuleBuilder implement(
|
||||
TypeLiteral<T> source,
|
||||
Class<? extends Annotation> annotationType,
|
||||
Class<? extends T> target) {
|
||||
return implement(source, annotationType, TypeLiteral.get(target));
|
||||
}
|
||||
|
||||
/**
|
||||
* See the factory configuration examples at {@link FactoryModuleBuilder}.
|
||||
*/
|
||||
public <T> FactoryModuleBuilder implement(TypeLiteral<T> source,
|
||||
Class<? extends Annotation> annotationType, TypeLiteral<? extends T> target) {
|
||||
/** See the factory configuration examples at {@link FactoryModuleBuilder}. */
|
||||
public <T> FactoryModuleBuilder implement(
|
||||
TypeLiteral<T> source,
|
||||
Class<? extends Annotation> annotationType,
|
||||
TypeLiteral<? extends T> 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) {
|
||||
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) {
|
||||
bindings.addBinding(source, target);
|
||||
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) {
|
||||
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) {
|
||||
return build(Key.get(factoryInterface));
|
||||
}
|
||||
|
||||
|
||||
public <F> Module build(final Key<F> factoryInterface) {
|
||||
return new AbstractModule() {
|
||||
@Override
|
||||
protected void configure() {
|
||||
Provider<F> provider = new FactoryProvider2<F>(factoryInterface, bindings);
|
||||
bind(factoryInterface).toProvider(provider);
|
||||
Provider<F> provider = new FactoryProvider2<>(factoryInterface, bindings, lookups);
|
||||
binder().skipSources(this.getClass()).bind(factoryInterface).toProvider(provider);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package com.google.inject.assistedinject;
|
||||
|
||||
import static com.google.inject.internal.Annotations.getKey;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
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.HasDependencies;
|
||||
import com.google.inject.spi.Message;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
|
@ -27,8 +28,6 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.google.inject.internal.Annotations.getKey;
|
||||
|
||||
/**
|
||||
* <strong>Obsolete.</strong> Prefer {@link FactoryModuleBuilder} for its more concise API and
|
||||
* additional capability.
|
||||
|
@ -37,20 +36,25 @@ import static com.google.inject.internal.Annotations.getKey;
|
|||
* construct objects.
|
||||
*
|
||||
* <h3>Defining a factory</h3>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* <pre>public interface PaymentFactory {
|
||||
* Payment create(Date startDate, Money amount);
|
||||
* }</pre>
|
||||
*
|
||||
* You can name your factory methods whatever you like, such as <i>create</i>, <i>createPayment</i>
|
||||
* or <i>newPayment</i>.
|
||||
*
|
||||
* <h3>Creating a type that accepts factory parameters</h3>
|
||||
*
|
||||
* {@code constructedType} is a concrete class with an {@literal @}{@link 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 parameter
|
||||
* requires an {@literal @}{@link Assisted} annotation. This serves to document that the parameter
|
||||
* is not bound by your application's modules.
|
||||
* constructor. In addition to injector-supplied parameters, the constructor should have parameters
|
||||
* that match each of the factory method's parameters. Each factory-supplied parameter requires an
|
||||
* {@literal @}{@link Assisted} annotation. This serves to document that the parameter is not bound
|
||||
* by your application's modules.
|
||||
*
|
||||
* <pre>public class RealPayment implements Payment {
|
||||
* {@literal @}Inject
|
||||
* public RealPayment(
|
||||
|
@ -61,19 +65,25 @@ import static com.google.inject.internal.Annotations.getKey;
|
|||
* ...
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* Any parameter that permits a null value should also be annotated {@code @Nullable}.
|
||||
*
|
||||
* <h3>Configuring factories</h3>
|
||||
*
|
||||
* In your {@link com.google.inject.Module module}, bind the factory interface to the returned
|
||||
* factory:
|
||||
*
|
||||
* <pre>bind(PaymentFactory.class).toProvider(
|
||||
* 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
|
||||
* factory cannot be used until the injector has been initialized.
|
||||
*
|
||||
* <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 {
|
||||
* {@literal @}Inject private PaymentFactory paymentFactory;
|
||||
*
|
||||
|
@ -84,9 +94,10 @@ import static com.google.inject.internal.Annotations.getKey;
|
|||
* }</pre>
|
||||
*
|
||||
* <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
|
||||
* parameters. The names must be applied to the factory method's parameters:
|
||||
*
|
||||
* 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 parameters.
|
||||
* The names must be applied to the factory method's parameters:
|
||||
*
|
||||
* <pre>public interface PaymentFactory {
|
||||
* Payment create(
|
||||
|
@ -94,7 +105,9 @@ import static com.google.inject.internal.Annotations.getKey;
|
|||
* <strong>{@literal @}Assisted("dueDate")</strong> Date dueDate,
|
||||
* Money amount);
|
||||
* } </pre>
|
||||
*
|
||||
* ...and to the concrete type's constructor parameters:
|
||||
*
|
||||
* <pre>public class RealPayment implements Payment {
|
||||
* {@literal @}Inject
|
||||
* public RealPayment(
|
||||
|
@ -108,46 +121,42 @@ import static com.google.inject.internal.Annotations.getKey;
|
|||
* }</pre>
|
||||
*
|
||||
* <h3>Values are created by Guice</h3>
|
||||
*
|
||||
* 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
|
||||
* returned.
|
||||
*
|
||||
* <h3>Backwards compatibility using {@literal @}AssistedInject</h3>
|
||||
*
|
||||
* Instead of the {@literal @}Inject annotation, you may annotate the constructed classes with
|
||||
* {@literal @}{@link AssistedInject}. This triggers a limited backwards-compatability mode.
|
||||
*
|
||||
* <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
|
||||
* used for the first {@literal @}Assisted constructor parameter, etc.. Annotation names have no
|
||||
* effect.
|
||||
* <strong>parameters are matched by their order</strong>. The first factory method argument is used
|
||||
* for the first {@literal @}Assisted constructor parameter, etc.. Annotation names have no effect.
|
||||
*
|
||||
* <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.
|
||||
*
|
||||
* @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
|
||||
public class FactoryProvider<F> implements Provider<F>, HasDependencies {
|
||||
|
||||
/*
|
||||
* This class implements the old @AssistedInject implementation that manually matches constructors
|
||||
* to factory methods. The new child injector implementation lives in FactoryProvider2.
|
||||
*/
|
||||
/*
|
||||
* This class implements the old @AssistedInject implementation that manually matches constructors
|
||||
* to factory methods. The new child injector implementation lives in FactoryProvider2.
|
||||
*/
|
||||
|
||||
private Injector injector;
|
||||
|
||||
private final TypeLiteral<F> factoryType;
|
||||
private final TypeLiteral<?> implementationType;
|
||||
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) {
|
||||
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(
|
||||
TypeLiteral<F> factoryType, TypeLiteral<?> implementationType) {
|
||||
Map<Method, AssistedConstructor<?>> factoryMethodToConstructor
|
||||
= createMethodMapping(factoryType, implementationType);
|
||||
Map<Method, AssistedConstructor<?>> factoryMethodToConstructor =
|
||||
createMethodMapping(factoryType, implementationType);
|
||||
|
||||
if (!factoryMethodToConstructor.isEmpty()) {
|
||||
return new FactoryProvider<F>(factoryType, implementationType, factoryMethodToConstructor);
|
||||
|
@ -170,8 +179,8 @@ public class FactoryProvider<F> implements Provider<F>, HasDependencies {
|
|||
|
||||
try {
|
||||
for (Method method : factoryType.getRawType().getMethods()) {
|
||||
Key<?> returnType = getKey(factoryType.getReturnType(method), method,
|
||||
method.getAnnotations(), errors);
|
||||
Key<?> returnType =
|
||||
getKey(factoryType.getReturnType(method), method, method.getAnnotations(), errors);
|
||||
if (!implementationKey.equals(returnType)) {
|
||||
collector.addBinding(returnType, implementationType);
|
||||
}
|
||||
|
@ -180,83 +189,18 @@ public class FactoryProvider<F> implements Provider<F>, HasDependencies {
|
|||
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(
|
||||
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;
|
||||
}
|
||||
|
||||
private static ConfigurationException newConfigurationException(String format, Object... args) {
|
||||
return new ConfigurationException(ImmutableSet.of(new Message(Errors.format(format, args))));
|
||||
private FactoryProvider(
|
||||
TypeLiteral<F> factoryType,
|
||||
TypeLiteral<?> implementationType,
|
||||
Map<Method, AssistedConstructor<?>> factoryMethodToConstructor) {
|
||||
this.factoryType = factoryType;
|
||||
this.implementationType = implementationType;
|
||||
this.factoryMethodToConstructor = factoryMethodToConstructor;
|
||||
checkDeclaredExceptionsMatch();
|
||||
}
|
||||
|
||||
@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
|
||||
// error to the injector. Throughout this class we throw exceptions
|
||||
// to add errors, which isn't really the best way in Guice
|
||||
throw newConfigurationException("Parameter of type '%s' is not injectable or annotated "
|
||||
+ "with @Assisted for Constructor '%s'", p, c);
|
||||
throw newConfigurationException(
|
||||
"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()) {
|
||||
if (!isConstructorExceptionCompatibleWithFactoryExeception(
|
||||
constructorException, entry.getKey().getExceptionTypes())) {
|
||||
throw newConfigurationException("Constructor %s declares an exception, but no compatible "
|
||||
+ "exception is thrown by the factory method %s", entry.getValue(), entry.getKey());
|
||||
throw newConfigurationException(
|
||||
"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);
|
||||
}
|
||||
|
||||
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() {
|
||||
List<Dependency<?>> dependencies = Lists.newArrayList();
|
||||
for (AssistedConstructor<?> constructor : factoryMethodToConstructor.values()) {
|
||||
|
@ -313,51 +337,55 @@ public class FactoryProvider<F> implements Provider<F>, HasDependencies {
|
|||
return ImmutableSet.copyOf(dependencies);
|
||||
}
|
||||
|
||||
@Override
|
||||
public F get() {
|
||||
InvocationHandler invocationHandler = new InvocationHandler() {
|
||||
public Object invoke(Object proxy, Method method, Object[] creationArgs) throws Throwable {
|
||||
// pass methods from Object.class to the proxy
|
||||
if (method.getDeclaringClass().equals(Object.class)) {
|
||||
if ("equals".equals(method.getName())) {
|
||||
return proxy == creationArgs[0];
|
||||
} else if ("hashCode".equals(method.getName())) {
|
||||
return System.identityHashCode(proxy);
|
||||
} else {
|
||||
return method.invoke(this, creationArgs);
|
||||
InvocationHandler invocationHandler =
|
||||
new InvocationHandler() {
|
||||
@Override
|
||||
public Object invoke(Object proxy, Method method, Object[] creationArgs)
|
||||
throws Throwable {
|
||||
// pass methods from Object.class to the proxy
|
||||
if (method.getDeclaringClass().equals(Object.class)) {
|
||||
if ("equals".equals(method.getName())) {
|
||||
return proxy == creationArgs[0];
|
||||
} else if ("hashCode".equals(method.getName())) {
|
||||
return System.identityHashCode(proxy);
|
||||
} else {
|
||||
return method.invoke(this, creationArgs);
|
||||
}
|
||||
}
|
||||
|
||||
AssistedConstructor<?> constructor = factoryMethodToConstructor.get(method);
|
||||
Object[] constructorArgs = gatherArgsForConstructor(constructor, creationArgs);
|
||||
Object objectToReturn = constructor.newInstance(constructorArgs);
|
||||
injector.injectMembers(objectToReturn);
|
||||
return objectToReturn;
|
||||
}
|
||||
}
|
||||
|
||||
AssistedConstructor<?> constructor = factoryMethodToConstructor.get(method);
|
||||
Object[] constructorArgs = gatherArgsForConstructor(constructor, creationArgs);
|
||||
Object objectToReturn = constructor.newInstance(constructorArgs);
|
||||
injector.injectMembers(objectToReturn);
|
||||
return objectToReturn;
|
||||
}
|
||||
public Object[] gatherArgsForConstructor(
|
||||
AssistedConstructor<?> constructor, Object[] factoryArgs) {
|
||||
int numParams = constructor.getAllParameters().size();
|
||||
int argPosition = 0;
|
||||
Object[] result = new Object[numParams];
|
||||
|
||||
public Object[] gatherArgsForConstructor(
|
||||
AssistedConstructor<?> constructor,
|
||||
Object[] factoryArgs) {
|
||||
int numParams = constructor.getAllParameters().size();
|
||||
int argPosition = 0;
|
||||
Object[] result = new Object[numParams];
|
||||
|
||||
for (int i = 0; i < numParams; i++) {
|
||||
Parameter parameter = constructor.getAllParameters().get(i);
|
||||
if (parameter.isProvidedByFactory()) {
|
||||
result[i] = factoryArgs[argPosition];
|
||||
argPosition++;
|
||||
} else {
|
||||
result[i] = parameter.getValue(injector);
|
||||
for (int i = 0; i < numParams; i++) {
|
||||
Parameter parameter = constructor.getAllParameters().get(i);
|
||||
if (parameter.isProvidedByFactory()) {
|
||||
result[i] = factoryArgs[argPosition];
|
||||
argPosition++;
|
||||
} else {
|
||||
result[i] = parameter.getValue(injector);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@SuppressWarnings("unchecked") // we imprecisely treat the class literal of T as a Class<T>
|
||||
Class<F> factoryRawType = (Class<F>) (Class<?>) factoryType.getRawType();
|
||||
return factoryRawType.cast(Proxy.newProxyInstance(factoryRawType.getClassLoader(),
|
||||
new Class[]{factoryRawType}, invocationHandler));
|
||||
Class<F> factoryRawType = (Class<F>) (Class<?>) factoryType.getRawType();
|
||||
return factoryRawType.cast(
|
||||
Proxy.newProxyInstance(
|
||||
factoryRawType.getClassLoader(), new Class<?>[] {factoryRawType}, invocationHandler));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -374,4 +402,8 @@ public class FactoryProvider<F> implements Provider<F>, HasDependencies {
|
|||
return factoryType.equals(other.factoryType)
|
||||
&& implementationType.equals(other.implementationType);
|
||||
}
|
||||
|
||||
private static ConfigurationException newConfigurationException(String format, Object... args) {
|
||||
return new ConfigurationException(ImmutableSet.of(new Message(Errors.format(format, args))));
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -51,20 +51,17 @@ public interface LinkedBindingBuilder<T> extends ScopedBindingBuilder {
|
|||
/**
|
||||
* See the EDSL examples at {@link com.google.inject.Binder}.
|
||||
*/
|
||||
ScopedBindingBuilder toProvider(
|
||||
Class<? extends javax.inject.Provider<? extends T>> providerType);
|
||||
ScopedBindingBuilder toProvider(Class<? extends javax.inject.Provider<? extends T>> providerType);
|
||||
|
||||
/**
|
||||
* See the EDSL examples at {@link com.google.inject.Binder}.
|
||||
*/
|
||||
ScopedBindingBuilder toProvider(
|
||||
TypeLiteral<? extends javax.inject.Provider<? extends T>> providerType);
|
||||
ScopedBindingBuilder toProvider(TypeLiteral<? extends javax.inject.Provider<? extends T>> providerType);
|
||||
|
||||
/**
|
||||
* See the EDSL examples at {@link com.google.inject.Binder}.
|
||||
*/
|
||||
ScopedBindingBuilder toProvider(
|
||||
Key<? extends javax.inject.Provider<? extends T>> providerKey);
|
||||
ScopedBindingBuilder toProvider(Key<? extends javax.inject.Provider<? extends T>> providerKey);
|
||||
|
||||
/**
|
||||
* See the EDSL examples at {@link com.google.inject.Binder}.
|
||||
|
@ -74,6 +71,5 @@ public interface LinkedBindingBuilder<T> extends ScopedBindingBuilder {
|
|||
/**
|
||||
* See the EDSL examples at {@link com.google.inject.Binder}.
|
||||
*/
|
||||
<S extends T> ScopedBindingBuilder toConstructor(
|
||||
Constructor<S> constructor, TypeLiteral<? extends S> type);
|
||||
<S extends T> ScopedBindingBuilder toConstructor(Constructor<S> constructor, TypeLiteral<? extends S> type);
|
||||
}
|
||||
|
|
|
@ -18,14 +18,14 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
|||
public abstract class AbstractBindingBuilder<T> {
|
||||
|
||||
public static final String IMPLEMENTATION_ALREADY_SET = "Implementation is set more than once.";
|
||||
public static final String SINGLE_INSTANCE_AND_SCOPE
|
||||
= "Setting the scope is not permitted when binding to a single instance.";
|
||||
public static final String SINGLE_INSTANCE_AND_SCOPE =
|
||||
"Setting the scope is not permitted when binding to a single instance.";
|
||||
public static final String SCOPE_ALREADY_SET = "Scope is set more than once.";
|
||||
public static final String BINDING_TO_NULL = "Binding to null instances is not allowed. "
|
||||
+ "Use toProvider(Providers.of(null)) if this is your intended behaviour.";
|
||||
public static final String BINDING_TO_NULL = "Binding to null instances is not allowed. " +
|
||||
"Use toProvider(Providers.of(null)) if this is your intended behaviour.";
|
||||
public static final String CONSTANT_VALUE_ALREADY_SET = "Constant value is set more than once.";
|
||||
public static final String ANNOTATION_ALREADY_SPECIFIED
|
||||
= "More than one annotation is specified for this binding.";
|
||||
public static final String ANNOTATION_ALREADY_SPECIFIED =
|
||||
"More than one annotation is specified for this binding.";
|
||||
|
||||
protected static final Key<?> NULL_KEY = Key.get(Void.class);
|
||||
protected final Binder binder;
|
||||
|
@ -37,7 +37,7 @@ public abstract class AbstractBindingBuilder<T> {
|
|||
this.binder = binder;
|
||||
this.elements = elements;
|
||||
this.position = elements.size();
|
||||
this.binding = new UntargettedBindingImpl<T>(source, key, Scoping.UNSCOPED);
|
||||
this.binding = new UntargettedBindingImpl<>(source, key, Scoping.UNSCOPED);
|
||||
elements.add(position, this.binding);
|
||||
}
|
||||
|
||||
|
@ -57,8 +57,7 @@ public abstract class AbstractBindingBuilder<T> {
|
|||
protected BindingImpl<T> annotatedWithInternal(Class<? extends Annotation> annotationType) {
|
||||
checkNotNull(annotationType, "annotationType");
|
||||
checkNotAnnotated();
|
||||
return setBinding(binding.withKey(
|
||||
Key.get(this.binding.getKey().getTypeLiteral(), annotationType)));
|
||||
return setBinding(binding.withKey(Key.get(this.binding.getKey().getTypeLiteral(), annotationType)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -67,8 +66,7 @@ public abstract class AbstractBindingBuilder<T> {
|
|||
protected BindingImpl<T> annotatedWithInternal(Annotation annotation) {
|
||||
checkNotNull(annotation, "annotation");
|
||||
checkNotAnnotated();
|
||||
return setBinding(binding.withKey(
|
||||
Key.get(this.binding.getKey().getTypeLiteral(), annotation)));
|
||||
return setBinding(binding.withKey(Key.get(this.binding.getKey().getTypeLiteral(), annotation)));
|
||||
}
|
||||
|
||||
public void in(final Class<? extends Annotation> scopeAnnotation) {
|
||||
|
|
|
@ -14,34 +14,33 @@ import com.google.inject.Stage;
|
|||
import com.google.inject.TypeLiteral;
|
||||
import com.google.inject.spi.DefaultBindingTargetVisitor;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Guarantees that processing of Binding elements happens in a sane way.
|
||||
*/
|
||||
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
|
||||
// all our unit tests.
|
||||
private static final Set<Class<?>> FORBIDDEN_TYPES = ImmutableSet.<Class<?>>of(
|
||||
AbstractModule.class,
|
||||
Binder.class,
|
||||
Binding.class,
|
||||
Injector.class,
|
||||
Key.class,
|
||||
MembersInjector.class,
|
||||
Module.class,
|
||||
Provider.class,
|
||||
Scope.class,
|
||||
Stage.class,
|
||||
TypeLiteral.class);
|
||||
private static final ImmutableSet<Class<?>> FORBIDDEN_TYPES =
|
||||
ImmutableSet.<Class<?>>of(
|
||||
AbstractModule.class,
|
||||
Binder.class,
|
||||
Binding.class,
|
||||
Injector.class,
|
||||
Key.class,
|
||||
MembersInjector.class,
|
||||
Module.class,
|
||||
Provider.class,
|
||||
Scope.class,
|
||||
Stage.class,
|
||||
TypeLiteral.class);
|
||||
|
||||
protected final ProcessedBindingData bindingData;
|
||||
protected final ProcessedBindingData processedBindingData;
|
||||
|
||||
AbstractBindingProcessor(Errors errors, ProcessedBindingData bindingData) {
|
||||
AbstractBindingProcessor(Errors errors, ProcessedBindingData processedBindingData) {
|
||||
super(errors);
|
||||
this.bindingData = bindingData;
|
||||
this.processedBindingData = processedBindingData;
|
||||
}
|
||||
|
||||
protected <T> UntargettedBindingImpl<T> invalidBinding(
|
||||
|
@ -61,10 +60,10 @@ abstract class AbstractBindingProcessor extends AbstractProcessor {
|
|||
BindingImpl<?> original = injector.getExistingBinding(key);
|
||||
if (original != null) {
|
||||
// If it failed because of an explicit duplicate binding...
|
||||
if (injector.state.getExplicitBinding(key) != null) {
|
||||
if (injector.getBindingData().getExplicitBinding(key) != null) {
|
||||
try {
|
||||
if (!isOkayDuplicate(original, binding, injector.state)) {
|
||||
errors.bindingAlreadySet(key, original.getSource());
|
||||
if (!isOkayDuplicate(original, binding, injector.getBindingData())) {
|
||||
errors.bindingAlreadySet(binding, original);
|
||||
return;
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
|
@ -80,27 +79,34 @@ abstract class AbstractBindingProcessor extends AbstractProcessor {
|
|||
}
|
||||
|
||||
// prevent the parent from creating a JIT binding for this key
|
||||
injector.state.parent().blacklist(key, injector.state, binding.getSource());
|
||||
injector.state.putBinding(key, binding);
|
||||
injector
|
||||
.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
|
||||
* are considered duplicates.
|
||||
* We tolerate duplicate bindings if one exposes the other or if the two bindings are considered
|
||||
* duplicates (see {@link Bindings#areDuplicates(BindingImpl, BindingImpl)}.
|
||||
*
|
||||
* @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) {
|
||||
ExposedBindingImpl exposed = (ExposedBindingImpl) original;
|
||||
ExposedBindingImpl<?> exposed = (ExposedBindingImpl<?>) original;
|
||||
InjectorImpl exposedFrom = (InjectorImpl) exposed.getPrivateElements().getInjector();
|
||||
return (exposedFrom == binding.getInjector());
|
||||
} 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
|
||||
// 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
|
||||
* actually process the binding should call prepareBinding first.
|
||||
* Processor for visiting bindings. Each overriden method that wants to actually process the
|
||||
* binding should call prepareBinding first.
|
||||
*/
|
||||
abstract class Processor<T, V> extends DefaultBindingTargetVisitor<T, V> {
|
||||
final Object source;
|
||||
|
@ -131,16 +137,28 @@ abstract class AbstractBindingProcessor extends AbstractProcessor {
|
|||
scoping = Scoping.makeInjectable(scoping, injector, errors);
|
||||
}
|
||||
|
||||
protected void scheduleInitialization(final BindingImpl<?> binding) {
|
||||
bindingData.addUninitializedBinding(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
binding.getInjector().initializeBinding(binding, errors.withSource(source));
|
||||
} catch (ErrorsException e) {
|
||||
errors.merge(e.getErrors());
|
||||
}
|
||||
}
|
||||
});
|
||||
/**
|
||||
* Schedule initialization of this binding to occur immediately after all bindings have been
|
||||
* initialially processed.
|
||||
*/
|
||||
protected void scheduleInitialization(BindingImpl<?> binding) {
|
||||
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 {
|
||||
binding.getInjector().initializeBinding(binding, errors.withSource(source));
|
||||
} catch (ErrorsException e) {
|
||||
errors.merge(e.getErrors());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
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.MapJoiner;
|
||||
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.name.Named;
|
||||
import com.google.inject.name.Names;
|
||||
|
||||
import javax.inject.Qualifier;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
@ -28,37 +27,16 @@ import java.lang.reflect.Proxy;
|
|||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import javax.inject.Qualifier;
|
||||
|
||||
/**
|
||||
* Annotation utilities.
|
||||
*
|
||||
* @author crazybob@google.com (Bob Lee)
|
||||
*/
|
||||
public class Annotations {
|
||||
|
||||
private static final MapJoiner JOINER = Joiner.on(", ").withKeyValueSeparator("=");
|
||||
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.
|
||||
*/
|
||||
/** Returns {@code true} if the given annotation type has no attributes. */
|
||||
public static boolean isMarker(Class<? extends Annotation> annotationType) {
|
||||
return annotationType.getDeclaredMethods().length == 0;
|
||||
}
|
||||
|
@ -74,11 +52,22 @@ public class Annotations {
|
|||
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
|
||||
* optionals.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@SuppressWarnings("unchecked") // Safe because generateAnnotationImpl returns T for Class<T>
|
||||
public static <T extends Annotation> T generateAnnotation(Class<T> annotationType) {
|
||||
Preconditions.checkState(
|
||||
isAllDefaultMethods(annotationType), "%s is not all default methods", annotationType);
|
||||
|
@ -87,26 +76,27 @@ public class Annotations {
|
|||
|
||||
private static <T extends Annotation> T generateAnnotationImpl(final Class<T> annotationType) {
|
||||
final Map<String, Object> members = resolveMembers(annotationType);
|
||||
return annotationType.cast(Proxy.newProxyInstance(
|
||||
annotationType.getClassLoader(),
|
||||
new Class<?>[]{annotationType},
|
||||
new InvocationHandler() {
|
||||
@Override
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Exception {
|
||||
String name = method.getName();
|
||||
if (name.equals("annotationType")) {
|
||||
return annotationType;
|
||||
} else if (name.equals("toString")) {
|
||||
return annotationToString(annotationType, members);
|
||||
} else if (name.equals("hashCode")) {
|
||||
return annotationHashCode(annotationType, members);
|
||||
} else if (name.equals("equals")) {
|
||||
return annotationEquals(annotationType, members, args[0]);
|
||||
} else {
|
||||
return members.get(name);
|
||||
}
|
||||
}
|
||||
}));
|
||||
return annotationType.cast(
|
||||
Proxy.newProxyInstance(
|
||||
annotationType.getClassLoader(),
|
||||
new Class<?>[] {annotationType},
|
||||
new InvocationHandler() {
|
||||
@Override
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Exception {
|
||||
String name = method.getName();
|
||||
if (name.equals("annotationType")) {
|
||||
return annotationType;
|
||||
} else if (name.equals("toString")) {
|
||||
return annotationToString(annotationType, members);
|
||||
} else if (name.equals("hashCode")) {
|
||||
return annotationHashCode(annotationType, members);
|
||||
} else if (name.equals("equals")) {
|
||||
return annotationEquals(annotationType, members, args[0]);
|
||||
} else {
|
||||
return members.get(name);
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private static ImmutableMap<String, Object> resolveMembers(
|
||||
|
@ -118,68 +108,67 @@ public class Annotations {
|
|||
return result.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements {@link Annotation#equals}.
|
||||
*/
|
||||
private static boolean annotationEquals(Class<? extends Annotation> type,
|
||||
Map<String, Object> members, Object other) throws Exception {
|
||||
/** Implements {@link Annotation#equals}. */
|
||||
private static boolean annotationEquals(
|
||||
Class<? extends Annotation> type, Map<String, Object> members, Object other)
|
||||
throws Exception {
|
||||
if (!type.isInstance(other)) {
|
||||
return false;
|
||||
}
|
||||
for (Method method : type.getDeclaredMethods()) {
|
||||
String name = method.getName();
|
||||
if (!Arrays.deepEquals(
|
||||
new Object[]{method.invoke(other)}, new Object[]{members.get(name)})) {
|
||||
new Object[] {method.invoke(other)}, new Object[] {members.get(name)})) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements {@link Annotation#hashCode}.
|
||||
*/
|
||||
private static int annotationHashCode(Class<? extends Annotation> type,
|
||||
Map<String, Object> members) throws Exception {
|
||||
/** Implements {@link Annotation#hashCode}. */
|
||||
private static int annotationHashCode(
|
||||
Class<? extends Annotation> type, Map<String, Object> members) throws Exception {
|
||||
int result = 0;
|
||||
for (Method method : type.getDeclaredMethods()) {
|
||||
String name = method.getName();
|
||||
Object value = members.get(name);
|
||||
result += (127 * name.hashCode()) ^ (Arrays.deepHashCode(new Object[]{value}) - 31);
|
||||
result += (127 * name.hashCode()) ^ (Arrays.deepHashCode(new Object[] {value}) - 31);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements {@link Annotation#toString}.
|
||||
*/
|
||||
private static String annotationToString(Class<? extends Annotation> type,
|
||||
Map<String, Object> members) throws Exception {
|
||||
private static final MapJoiner JOINER = Joiner.on(", ").withKeyValueSeparator("=");
|
||||
|
||||
/** Implements {@link Annotation#toString}. */
|
||||
private static String annotationToString(
|
||||
Class<? extends Annotation> type, Map<String, Object> members) throws Exception {
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
Retention retention = annotationType.getAnnotation(Retention.class);
|
||||
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(
|
||||
Errors errors, Class<?> implementation) {
|
||||
return findScopeAnnotation(errors, implementation.getAnnotations());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the scoping annotation, or null if there isn't one.
|
||||
*/
|
||||
public static Class<? extends Annotation> findScopeAnnotation(Errors errors, Annotation[] annotations) {
|
||||
/** Returns the scoping annotation, or null if there isn't one. */
|
||||
public static Class<? extends Annotation> findScopeAnnotation(
|
||||
Errors errors, Annotation[] annotations) {
|
||||
Class<? extends Annotation> found = null;
|
||||
|
||||
for (Annotation annotation : annotations) {
|
||||
|
@ -207,13 +196,115 @@ public class Annotations {
|
|||
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) {
|
||||
return scopeChecker.hasAnnotations(annotationType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an error if there is a misplaced annotations on {@code type}. Scoping
|
||||
* annotations are not allowed on abstract classes or interfaces.
|
||||
* Adds an error if there is a misplaced annotations on {@code type}. Scoping annotations are not
|
||||
* allowed on abstract classes or interfaces.
|
||||
*/
|
||||
public static void checkForMisplacedScopeAnnotations(
|
||||
Class<?> type, Object source, Errors errors) {
|
||||
|
@ -229,20 +320,20 @@ public class Annotations {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a key for the given type, member and annotations.
|
||||
*/
|
||||
public static Key<?> getKey(TypeLiteral<?> type, Member member, Annotation[] annotations,
|
||||
Errors errors) throws ErrorsException {
|
||||
// NOTE: getKey/findBindingAnnotation are used by Gin which is abandoned. So changing this API
|
||||
// will prevent Gin users from upgrading Guice version.
|
||||
|
||||
/** Gets a key for the given type, member and annotations. */
|
||||
public static Key<?> getKey(
|
||||
TypeLiteral<?> type, Member member, Annotation[] annotations, Errors errors)
|
||||
throws ErrorsException {
|
||||
int numErrorsBefore = errors.size();
|
||||
Annotation found = findBindingAnnotation(errors, member, annotations);
|
||||
errors.throwIfNewErrors(numErrorsBefore);
|
||||
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(
|
||||
Errors errors, Member member, Annotation[] annotations) {
|
||||
Annotation found = null;
|
||||
|
@ -261,16 +352,17 @@ public class Annotations {
|
|||
return found;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if annotations of the specified type are binding annotations.
|
||||
*/
|
||||
private static final AnnotationChecker bindingAnnotationChecker =
|
||||
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) {
|
||||
return bindingAnnotationChecker.hasAnnotations(annotationType);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the annotation is an instance of {@code javax.inject.Named}, canonicalizes to
|
||||
* com.google.guice.name.Named. Returns the given annotation otherwise.
|
||||
* com.google.guice.name.Named. Returns the given annotation otherwise.
|
||||
*/
|
||||
public static Annotation canonicalizeIfNamed(Annotation annotation) {
|
||||
if (annotation instanceof javax.inject.Named) {
|
||||
|
@ -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 {
|
||||
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>() {
|
||||
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);
|
||||
public static String nameOf(Key<?> key) {
|
||||
Annotation annotation = key.getAnnotation();
|
||||
Class<? extends Annotation> annotationType = key.getAnnotationType();
|
||||
if (annotation != null && !isMarker(annotationType)) {
|
||||
return key.getAnnotation().toString();
|
||||
} else if (key.getAnnotationType() != null) {
|
||||
return "@" + key.getAnnotationType().getName();
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BindingBuilder<T> annotatedWith(Class<? extends Annotation> annotationType) {
|
||||
annotatedWithInternal(annotationType);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BindingBuilder<T> annotatedWith(Annotation annotation) {
|
||||
annotatedWithInternal(annotation);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BindingBuilder<T> to(Class<? extends T> implementation) {
|
||||
return to(Key.get(implementation));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BindingBuilder<T> to(TypeLiteral<? extends T> implementation) {
|
||||
return to(Key.get(implementation));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BindingBuilder<T> to(Key<? extends T> linkedKey) {
|
||||
checkNotNull(linkedKey, "linkedKey");
|
||||
checkNotTargetted();
|
||||
|
@ -56,6 +61,7 @@ public class BindingBuilder<T> extends AbstractBindingBuilder<T>
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toInstance(T instance) {
|
||||
checkNotTargetted();
|
||||
|
||||
|
@ -79,10 +85,12 @@ public class BindingBuilder<T> extends AbstractBindingBuilder<T>
|
|||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public BindingBuilder<T> toProvider(Provider<? extends T> provider) {
|
||||
return toProvider((javax.inject.Provider<T>) provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BindingBuilder<T> toProvider(javax.inject.Provider<? extends T> provider) {
|
||||
checkNotNull(provider, "provider");
|
||||
checkNotTargetted();
|
||||
|
@ -102,16 +110,19 @@ public class BindingBuilder<T> extends AbstractBindingBuilder<T>
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BindingBuilder<T> toProvider(
|
||||
Class<? extends javax.inject.Provider<? extends T>> providerType) {
|
||||
return toProvider(Key.get(providerType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BindingBuilder<T> toProvider(
|
||||
TypeLiteral<? extends javax.inject.Provider<? extends T>> providerType) {
|
||||
return toProvider(Key.get(providerType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BindingBuilder<T> toProvider(
|
||||
Key<? extends javax.inject.Provider<? extends T>> providerKey) {
|
||||
checkNotNull(providerKey, "providerKey");
|
||||
|
@ -123,10 +134,12 @@ public class BindingBuilder<T> extends AbstractBindingBuilder<T>
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S extends T> ScopedBindingBuilder toConstructor(Constructor<S> constructor) {
|
||||
return toConstructor(constructor, TypeLiteral.get(constructor.getDeclaringClass()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S extends T> ScopedBindingBuilder toConstructor(Constructor<S> constructor,
|
||||
TypeLiteral<? extends S> type) {
|
||||
checkNotNull(constructor, "constructor");
|
||||
|
|
|
@ -34,14 +34,17 @@ public abstract class BindingImpl<T> implements Binding<T> {
|
|||
this.scoping = scoping;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key<T> getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Provider<T> getProvider() {
|
||||
if (provider == null) {
|
||||
if (injector == null) {
|
||||
|
@ -69,10 +72,12 @@ public abstract class BindingImpl<T> implements Binding<T> {
|
|||
return this instanceof InstanceBinding;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> V acceptVisitor(ElementVisitor<V> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> V acceptScopingVisitor(BindingScopingVisitor<V> visitor) {
|
||||
return scoping.acceptVisitor(visitor);
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@ import com.google.inject.spi.ProviderBinding;
|
|||
import com.google.inject.spi.ProviderInstanceBinding;
|
||||
import com.google.inject.spi.ProviderKeyBinding;
|
||||
import com.google.inject.spi.UntargettedBinding;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
|
@ -25,8 +24,9 @@ final class BindingProcessor extends AbstractBindingProcessor {
|
|||
|
||||
private final Initializer initializer;
|
||||
|
||||
BindingProcessor(Errors errors, Initializer initializer, ProcessedBindingData bindingData) {
|
||||
super(errors, bindingData);
|
||||
BindingProcessor(
|
||||
Errors errors, Initializer initializer, ProcessedBindingData processedBindingData) {
|
||||
super(errors, processedBindingData);
|
||||
this.initializer = initializer;
|
||||
}
|
||||
|
||||
|
@ -49,118 +49,174 @@ final class BindingProcessor extends AbstractBindingProcessor {
|
|||
return true;
|
||||
}
|
||||
|
||||
return command.acceptTargetVisitor(new Processor<T, Boolean>((BindingImpl<T>) command) {
|
||||
@Override
|
||||
public Boolean visit(ConstructorBinding<? extends T> binding) {
|
||||
prepareBinding();
|
||||
try {
|
||||
ConstructorBindingImpl<T> onInjector = ConstructorBindingImpl.create(injector, key,
|
||||
binding.getConstructor(), source, scoping, errors, false, false);
|
||||
scheduleInitialization(onInjector);
|
||||
putBinding(onInjector);
|
||||
} catch (ErrorsException e) {
|
||||
errors.merge(e.getErrors());
|
||||
putBinding(invalidBinding(injector, key, source));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return command.acceptTargetVisitor(
|
||||
new Processor<T, Boolean>((BindingImpl<T>) command) {
|
||||
@Override
|
||||
public Boolean visit(ConstructorBinding<? extends T> binding) {
|
||||
prepareBinding();
|
||||
try {
|
||||
ConstructorBindingImpl<T> onInjector =
|
||||
ConstructorBindingImpl.create(
|
||||
injector,
|
||||
key,
|
||||
binding.getConstructor(),
|
||||
source,
|
||||
scoping,
|
||||
errors,
|
||||
false,
|
||||
false);
|
||||
scheduleInitialization(onInjector);
|
||||
putBinding(onInjector);
|
||||
} catch (ErrorsException e) {
|
||||
errors.merge(e.getErrors());
|
||||
putBinding(invalidBinding(injector, key, source));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visit(InstanceBinding<? extends T> binding) {
|
||||
prepareBinding();
|
||||
Set<InjectionPoint> injectionPoints = binding.getInjectionPoints();
|
||||
T instance = binding.getInstance();
|
||||
@SuppressWarnings("unchecked") // safe to cast to binding<T> because
|
||||
@Override
|
||||
public Boolean visit(InstanceBinding<? extends T> binding) {
|
||||
prepareBinding();
|
||||
Set<InjectionPoint> injectionPoints = binding.getInjectionPoints();
|
||||
T instance = binding.getInstance();
|
||||
@SuppressWarnings("unchecked") // safe to cast to binding<T> because
|
||||
// the processor was constructed w/ it
|
||||
Initializable<T> ref = initializer.requestInjection(
|
||||
injector, instance, (Binding<T>) binding, source, injectionPoints);
|
||||
ConstantFactory<? extends T> factory = new ConstantFactory<T>(ref);
|
||||
InternalFactory<? extends T> scopedFactory
|
||||
= Scoping.scope(key, injector, factory, source, scoping);
|
||||
putBinding(new InstanceBindingImpl<T>(injector, key, source, scopedFactory, injectionPoints,
|
||||
instance));
|
||||
return true;
|
||||
}
|
||||
Initializable<T> ref =
|
||||
initializer.requestInjection(
|
||||
injector, instance, (Binding<T>) binding, source, injectionPoints);
|
||||
ConstantFactory<? extends T> factory = new ConstantFactory<>(ref);
|
||||
InternalFactory<? extends T> scopedFactory =
|
||||
Scoping.scope(key, injector, factory, source, scoping);
|
||||
putBinding(
|
||||
new InstanceBindingImpl<T>(
|
||||
injector, key, source, scopedFactory, injectionPoints, instance));
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visit(ProviderInstanceBinding<? extends T> binding) {
|
||||
prepareBinding();
|
||||
javax.inject.Provider<? extends T> provider = binding.getUserSuppliedProvider();
|
||||
Set<InjectionPoint> injectionPoints = binding.getInjectionPoints();
|
||||
Initializable<? extends javax.inject.Provider<? extends T>> initializable =
|
||||
initializer.<javax.inject.Provider<? extends T>>requestInjection(
|
||||
injector, provider, null, source, injectionPoints);
|
||||
// always visited with Binding<T>
|
||||
@SuppressWarnings("unchecked")
|
||||
InternalFactory<T> factory = new InternalFactoryToInitializableAdapter<T>(
|
||||
initializable, source,
|
||||
injector.provisionListenerStore.get((ProviderInstanceBinding<T>) binding));
|
||||
InternalFactory<? extends T> scopedFactory
|
||||
= Scoping.scope(key, injector, factory, source, scoping);
|
||||
putBinding(new ProviderInstanceBindingImpl<T>(injector, key, source, scopedFactory, scoping,
|
||||
provider, injectionPoints));
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public Boolean visit(ProviderInstanceBinding<? extends T> binding) {
|
||||
prepareBinding();
|
||||
javax.inject.Provider<? extends T> provider = binding.getUserSuppliedProvider();
|
||||
if (provider instanceof InternalProviderInstanceBindingImpl.Factory) {
|
||||
@SuppressWarnings("unchecked")
|
||||
InternalProviderInstanceBindingImpl.Factory<T> asProviderMethod =
|
||||
(InternalProviderInstanceBindingImpl.Factory<T>) provider;
|
||||
return visitInternalProviderInstanceBindingFactory(asProviderMethod);
|
||||
}
|
||||
Set<InjectionPoint> injectionPoints = binding.getInjectionPoints();
|
||||
Initializable<? extends javax.inject.Provider<? extends T>> initializable =
|
||||
initializer.<javax.inject.Provider<? extends T>>requestInjection(
|
||||
injector, provider, null, source, injectionPoints);
|
||||
// always visited with Binding<T>
|
||||
@SuppressWarnings("unchecked")
|
||||
InternalFactory<T> factory =
|
||||
new InternalFactoryToInitializableAdapter<T>(
|
||||
initializable,
|
||||
source,
|
||||
injector.provisionListenerStore.get((ProviderInstanceBinding<T>) binding));
|
||||
InternalFactory<? extends T> scopedFactory =
|
||||
Scoping.scope(key, injector, factory, source, scoping);
|
||||
putBinding(
|
||||
new ProviderInstanceBindingImpl<T>(
|
||||
injector, key, source, scopedFactory, scoping, provider, injectionPoints));
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visit(ProviderKeyBinding<? extends T> binding) {
|
||||
prepareBinding();
|
||||
Key<? extends javax.inject.Provider<? extends T>> providerKey = binding.getProviderKey();
|
||||
// always visited with Binding<T>
|
||||
@SuppressWarnings("unchecked")
|
||||
BoundProviderFactory<T> boundProviderFactory = new BoundProviderFactory<T>(
|
||||
injector, providerKey, source,
|
||||
injector.provisionListenerStore.get((ProviderKeyBinding<T>) binding));
|
||||
bindingData.addCreationListener(boundProviderFactory);
|
||||
InternalFactory<? extends T> scopedFactory = Scoping.scope(
|
||||
key, injector, (InternalFactory<? extends T>) boundProviderFactory, source, scoping);
|
||||
putBinding(new LinkedProviderBindingImpl<T>(
|
||||
injector, key, source, scopedFactory, scoping, providerKey));
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public Boolean visit(ProviderKeyBinding<? extends T> binding) {
|
||||
prepareBinding();
|
||||
Key<? extends javax.inject.Provider<? extends T>> providerKey =
|
||||
binding.getProviderKey();
|
||||
// always visited with Binding<T>
|
||||
@SuppressWarnings("unchecked")
|
||||
BoundProviderFactory<T> boundProviderFactory =
|
||||
new BoundProviderFactory<T>(
|
||||
injector,
|
||||
providerKey,
|
||||
source,
|
||||
injector.provisionListenerStore.get((ProviderKeyBinding<T>) binding));
|
||||
processedBindingData.addCreationListener(boundProviderFactory);
|
||||
InternalFactory<? extends T> scopedFactory =
|
||||
Scoping.scope(
|
||||
key,
|
||||
injector,
|
||||
(InternalFactory<? extends T>) boundProviderFactory,
|
||||
source,
|
||||
scoping);
|
||||
putBinding(
|
||||
new LinkedProviderBindingImpl<T>(
|
||||
injector, key, source, scopedFactory, scoping, providerKey));
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visit(LinkedKeyBinding<? extends T> binding) {
|
||||
prepareBinding();
|
||||
Key<? extends T> linkedKey = binding.getLinkedKey();
|
||||
if (key.equals(linkedKey)) {
|
||||
errors.recursiveBinding();
|
||||
}
|
||||
@Override
|
||||
public Boolean visit(LinkedKeyBinding<? extends T> binding) {
|
||||
prepareBinding();
|
||||
Key<? extends T> linkedKey = binding.getLinkedKey();
|
||||
if (key.equals(linkedKey)) {
|
||||
// TODO: b/168656899 check for transitive recursive binding
|
||||
errors.recursiveBinding(key, linkedKey);
|
||||
}
|
||||
|
||||
FactoryProxy<T> factory = new FactoryProxy<T>(injector, key, linkedKey, source);
|
||||
bindingData.addCreationListener(factory);
|
||||
InternalFactory<? extends T> scopedFactory
|
||||
= Scoping.scope(key, injector, factory, source, scoping);
|
||||
putBinding(
|
||||
new LinkedBindingImpl<T>(injector, key, source, scopedFactory, scoping, linkedKey));
|
||||
return true;
|
||||
}
|
||||
FactoryProxy<T> factory = new FactoryProxy<>(injector, key, linkedKey, source);
|
||||
processedBindingData.addCreationListener(factory);
|
||||
InternalFactory<? extends T> scopedFactory =
|
||||
Scoping.scope(key, injector, factory, source, scoping);
|
||||
putBinding(
|
||||
new LinkedBindingImpl<T>(injector, key, source, scopedFactory, scoping, linkedKey));
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visit(UntargettedBinding<? extends T> untargetted) {
|
||||
return false;
|
||||
}
|
||||
/** 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
|
||||
public Boolean visit(ExposedBinding<? extends T> binding) {
|
||||
throw new IllegalArgumentException("Cannot apply a non-module element");
|
||||
}
|
||||
@Override
|
||||
public Boolean visit(UntargettedBinding<? extends T> untargetted) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visit(ConvertedConstantBinding<? extends T> binding) {
|
||||
throw new IllegalArgumentException("Cannot apply a non-module element");
|
||||
}
|
||||
@Override
|
||||
public Boolean visit(ExposedBinding<? extends T> binding) {
|
||||
throw new IllegalArgumentException("Cannot apply a non-module element");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visit(ProviderBinding<? extends T> binding) {
|
||||
throw new IllegalArgumentException("Cannot apply a non-module element");
|
||||
}
|
||||
@Override
|
||||
public Boolean visit(ConvertedConstantBinding<? extends T> binding) {
|
||||
throw new IllegalArgumentException("Cannot apply a non-module element");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Boolean visitOther(Binding<? extends T> binding) {
|
||||
throw new IllegalStateException("BindingProcessor should override all visitations");
|
||||
}
|
||||
});
|
||||
@Override
|
||||
public Boolean visit(ProviderBinding<? extends T> binding) {
|
||||
throw new IllegalArgumentException("Cannot apply a non-module element");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Boolean visitOther(Binding<? extends T> binding) {
|
||||
throw new IllegalStateException("BindingProcessor should override all visitations");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -172,9 +228,14 @@ final class BindingProcessor extends AbstractBindingProcessor {
|
|||
}
|
||||
|
||||
private <T> void bindExposed(PrivateElements privateElements, Key<T> key) {
|
||||
ExposedKeyFactory<T> exposedKeyFactory = new ExposedKeyFactory<T>(key, privateElements);
|
||||
bindingData.addCreationListener(exposedKeyFactory);
|
||||
putBinding(new ExposedBindingImpl<T>(
|
||||
injector, privateElements.getExposedSource(key), key, exposedKeyFactory, privateElements));
|
||||
ExposedKeyFactory<T> exposedKeyFactory = new ExposedKeyFactory<>(key, privateElements);
|
||||
processedBindingData.addCreationListener(exposedKeyFactory);
|
||||
putBinding(
|
||||
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 static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Delegates to a custom factory which is also bound in the injector.
|
||||
*/
|
||||
final class BoundProviderFactory<T> extends ProviderInternalFactory<T> implements CreationListener {
|
||||
|
||||
final Key<? extends javax.inject.Provider<? extends T>> providerKey;
|
||||
private final Key<? extends javax.inject.Provider<? extends T>> providerKey;
|
||||
|
||||
private final ProvisionListenerStackCallback<T> provisionCallback;
|
||||
|
||||
private final InjectorImpl injector;
|
||||
|
||||
private InternalFactory<? extends javax.inject.Provider<? extends T>> providerFactory;
|
||||
|
||||
BoundProviderFactory(
|
||||
|
@ -24,11 +25,12 @@ final class BoundProviderFactory<T> extends ProviderInternalFactory<T> implement
|
|||
Object source,
|
||||
ProvisionListenerStackCallback<T> provisionCallback) {
|
||||
super(source);
|
||||
this.provisionCallback = checkNotNull(provisionCallback, "provisionCallback");
|
||||
this.provisionCallback = provisionCallback;
|
||||
this.injector = injector;
|
||||
this.providerKey = providerKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notify(Errors errors) {
|
||||
try {
|
||||
providerFactory = injector.getInternalFactory(providerKey, errors.withSource(source), JitLimitation.NEW_OR_EXISTING_JIT);
|
||||
|
@ -37,25 +39,27 @@ final class BoundProviderFactory<T> extends ProviderInternalFactory<T> implement
|
|||
}
|
||||
}
|
||||
|
||||
public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
|
||||
throws ErrorsException {
|
||||
@Override
|
||||
public T get(InternalContext context, Dependency<?> dependency, boolean linked)
|
||||
throws InternalProvisionException {
|
||||
context.pushState(providerKey, source);
|
||||
try {
|
||||
errors = errors.withSource(providerKey);
|
||||
javax.inject.Provider<? extends T> provider = providerFactory.get(errors, context, dependency, true);
|
||||
return circularGet(provider, errors, context, dependency, provisionCallback);
|
||||
javax.inject.Provider<? extends T> provider = providerFactory.get(context, dependency, true);
|
||||
return circularGet(provider, context, dependency, provisionCallback);
|
||||
} catch (InternalProvisionException ipe) {
|
||||
throw ipe.addSource(providerKey);
|
||||
} finally {
|
||||
context.popState();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected T provision(Provider<? extends T> provider, Errors errors, Dependency<?> dependency,
|
||||
ConstructionContext<T> constructionContext) throws ErrorsException {
|
||||
protected T provision(Provider<? extends T> provider, Dependency<?> dependency,
|
||||
ConstructionContext<T> constructionContext) throws InternalProvisionException {
|
||||
try {
|
||||
return super.provision(provider, errors, dependency, constructionContext);
|
||||
return super.provision(provider, dependency, constructionContext);
|
||||
} catch (RuntimeException userException) {
|
||||
throw errors.errorInProvider(userException).toException();
|
||||
throw InternalProvisionException.errorInProvider(userException);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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.ConstantBindingBuilder;
|
||||
import com.google.inject.spi.Element;
|
||||
import com.google.inject.spi.InjectionPoint;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.List;
|
||||
|
@ -24,56 +23,69 @@ public final class ConstantBindingBuilderImpl<T>
|
|||
super(binder, elements, source, (Key<T>) NULL_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConstantBindingBuilder annotatedWith(Class<? extends Annotation> annotationType) {
|
||||
annotatedWithInternal(annotationType);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConstantBindingBuilder annotatedWith(Annotation annotation) {
|
||||
annotatedWithInternal(annotation);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void to(final String value) {
|
||||
toConstant(String.class, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void to(final int value) {
|
||||
toConstant(Integer.class, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void to(final long value) {
|
||||
toConstant(Long.class, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void to(final boolean value) {
|
||||
toConstant(Boolean.class, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void to(final double value) {
|
||||
toConstant(Double.class, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void to(final float value) {
|
||||
toConstant(Float.class, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void to(final short value) {
|
||||
toConstant(Short.class, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void to(final char value) {
|
||||
toConstant(Character.class, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void to(final byte value) {
|
||||
toConstant(Byte.class, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void to(final Class<?> value) {
|
||||
toConstant(Class.class, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E extends Enum<E>> void to(final E value) {
|
||||
toConstant(value.getDeclaringClass(), value);
|
||||
}
|
||||
|
@ -105,7 +117,7 @@ public final class ConstantBindingBuilderImpl<T>
|
|||
}
|
||||
|
||||
setBinding(new InstanceBindingImpl<T>(
|
||||
base.getSource(), key, base.getScoping(), ImmutableSet.<InjectionPoint>of(), instanceAsT));
|
||||
base.getSource(), key, base.getScoping(), ImmutableSet.of(), instanceAsT));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -7,15 +7,17 @@ final class ConstantFactory<T> implements InternalFactory<T> {
|
|||
|
||||
private final Initializable<T> initializable;
|
||||
|
||||
public ConstantFactory(Initializable<T> initializable) {
|
||||
ConstantFactory(Initializable<T> initializable) {
|
||||
this.initializable = initializable;
|
||||
}
|
||||
|
||||
public T get(Errors errors, InternalContext context, Dependency dependency, boolean linked)
|
||||
throws ErrorsException {
|
||||
return initializable.get(errors);
|
||||
@Override
|
||||
public T get(InternalContext context, Dependency<?> dependency, boolean linked)
|
||||
throws InternalProvisionException {
|
||||
return initializable.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return MoreObjects.toStringHelper(ConstantFactory.class)
|
||||
.add("value", initializable)
|
||||
|
|
|
@ -41,26 +41,26 @@ final class ConstructionContext<T> {
|
|||
invocationHandlers = null;
|
||||
}
|
||||
|
||||
public Object createProxy(Errors errors, InjectorOptions injectorOptions,
|
||||
Class<?> expectedType) throws ErrorsException {
|
||||
public Object createProxy(InjectorOptions injectorOptions,
|
||||
Class<?> expectedType) throws InternalProvisionException {
|
||||
if (injectorOptions.disableCircularProxies) {
|
||||
throw errors.circularProxiesDisabled(expectedType).toException();
|
||||
throw InternalProvisionException.circularDependenciesDisabled(expectedType);
|
||||
}
|
||||
if (!expectedType.isInterface()) {
|
||||
throw errors.cannotSatisfyCircularDependency(expectedType).toException();
|
||||
throw InternalProvisionException.cannotProxyClass(expectedType);
|
||||
}
|
||||
|
||||
if (invocationHandlers == null) {
|
||||
invocationHandlers = new ArrayList<DelegatingInvocationHandler<T>>();
|
||||
invocationHandlers = new ArrayList<>();
|
||||
}
|
||||
|
||||
DelegatingInvocationHandler<T> invocationHandler = new DelegatingInvocationHandler<T>();
|
||||
DelegatingInvocationHandler<T> invocationHandler = new DelegatingInvocationHandler<>();
|
||||
invocationHandlers.add(invocationHandler);
|
||||
|
||||
ClassLoader classLoader = expectedType.getClass().getClassLoader() != null ?
|
||||
expectedType.getClass().getClassLoader() : ClassLoader.getSystemClassLoader();
|
||||
return expectedType.cast(Proxy.newProxyInstance(classLoader,
|
||||
new Class[]{expectedType, CircularDependencyProxy.class}, invocationHandler));
|
||||
new Class<?>[]{expectedType, CircularDependencyProxy.class}, invocationHandler));
|
||||
}
|
||||
|
||||
public void setProxyDelegates(T delegate) {
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
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.Objects;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.inject.Binder;
|
||||
import com.google.inject.ConfigurationException;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Key;
|
||||
import com.google.inject.TypeLiteral;
|
||||
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.Dependency;
|
||||
import com.google.inject.spi.InjectionPoint;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Modifier;
|
||||
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>
|
||||
implements ConstructorBinding<T>, DelayedInitialize {
|
||||
|
||||
private final Factory<T> factory;
|
||||
private final InjectionPoint constructorInjectionPoint;
|
||||
|
||||
private ConstructorBindingImpl(InjectorImpl injector, Key<T> key, Object source,
|
||||
InternalFactory<? extends T> scopedFactory, Scoping scoping, Factory<T> factory,
|
||||
InjectionPoint constructorInjectionPoint) {
|
||||
private ConstructorBindingImpl(
|
||||
InjectorImpl injector,
|
||||
Key<T> key,
|
||||
Object source,
|
||||
InternalFactory<? extends T> scopedFactory,
|
||||
Scoping scoping,
|
||||
Factory<T> factory,
|
||||
InjectionPoint constructorInjectionPoint) {
|
||||
super(injector, key, source, scopedFactory, scoping);
|
||||
this.factory = factory;
|
||||
this.constructorInjectionPoint = constructorInjectionPoint;
|
||||
}
|
||||
|
||||
public ConstructorBindingImpl(Key<T> key, Object source, Scoping scoping,
|
||||
InjectionPoint constructorInjectionPoint, Set<InjectionPoint> injectionPoints) {
|
||||
public ConstructorBindingImpl(
|
||||
Key<T> key,
|
||||
Object source,
|
||||
Scoping scoping,
|
||||
InjectionPoint constructorInjectionPoint,
|
||||
Set<InjectionPoint> injectionPoints) {
|
||||
super(source, key, scoping);
|
||||
this.factory = new Factory<T>(false, key);
|
||||
ConstructionProxy<T> constructionProxy
|
||||
= new DefaultConstructionProxyFactory<T>(constructorInjectionPoint).create();
|
||||
this.factory = new Factory<>(false, key);
|
||||
ConstructionProxy<T> constructionProxy =
|
||||
new DefaultConstructionProxyFactory<T>(constructorInjectionPoint).create();
|
||||
this.constructorInjectionPoint = constructorInjectionPoint;
|
||||
factory.constructorInjector = new ConstructorInjector<T>(
|
||||
injectionPoints, constructionProxy, null, null);
|
||||
factory.constructorInjector =
|
||||
new ConstructorInjector<T>(injectionPoints, constructionProxy, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param constructorInjector the constructor to use, or {@code null} to use the default.
|
||||
* @param failIfNotLinked true if this ConstructorBindingImpl's InternalFactory should
|
||||
* only succeed if retrieved from a linked binding
|
||||
* @param failIfNotLinked true if this ConstructorBindingImpl's InternalFactory should only
|
||||
* succeed if retrieved from a linked binding
|
||||
*/
|
||||
static <T> ConstructorBindingImpl<T> create(InjectorImpl injector, Key<T> key,
|
||||
InjectionPoint constructorInjector, Object source, Scoping scoping, Errors errors,
|
||||
boolean failIfNotLinked, boolean failIfNotExplicit)
|
||||
static <T> ConstructorBindingImpl<T> create(
|
||||
InjectorImpl injector,
|
||||
Key<T> key,
|
||||
InjectionPoint constructorInjector,
|
||||
Object source,
|
||||
Scoping scoping,
|
||||
Errors errors,
|
||||
boolean failIfNotLinked,
|
||||
boolean atInjectRequired)
|
||||
throws ErrorsException {
|
||||
int numErrors = errors.size();
|
||||
|
||||
@SuppressWarnings("unchecked") // constructorBinding guarantees type is consistent
|
||||
Class<? super T> rawType = constructorInjector == null
|
||||
? key.getTypeLiteral().getRawType()
|
||||
: (Class) constructorInjector.getDeclaringType().getRawType();
|
||||
Class<?> rawType =
|
||||
constructorInjector == null
|
||||
? key.getTypeLiteral().getRawType()
|
||||
: constructorInjector.getDeclaringType().getRawType();
|
||||
|
||||
// We can't inject abstract classes.
|
||||
if (Modifier.isAbstract(rawType.getModifiers())) {
|
||||
errors.missingImplementation(key);
|
||||
errors.missingImplementationWithHint(key, injector);
|
||||
}
|
||||
|
||||
// Error: Inner class.
|
||||
|
@ -78,10 +93,8 @@ final class ConstructorBindingImpl<T> extends BindingImpl<T>
|
|||
// Find a constructor annotated @Inject
|
||||
if (constructorInjector == null) {
|
||||
try {
|
||||
constructorInjector = InjectionPoint.forConstructorOf(key.getTypeLiteral());
|
||||
if (failIfNotExplicit && !hasAtInject((Constructor) constructorInjector.getMember())) {
|
||||
errors.atInjectRequired(rawType);
|
||||
}
|
||||
constructorInjector =
|
||||
InjectionPoint.forConstructorOf(key.getTypeLiteral(), atInjectRequired);
|
||||
} catch (ConfigurationException e) {
|
||||
throw errors.merge(e.getErrorMessages()).toException();
|
||||
}
|
||||
|
@ -92,47 +105,36 @@ final class ConstructorBindingImpl<T> extends BindingImpl<T>
|
|||
Class<?> annotatedType = constructorInjector.getMember().getDeclaringClass();
|
||||
Class<? extends Annotation> scopeAnnotation = findScopeAnnotation(errors, annotatedType);
|
||||
if (scopeAnnotation != null) {
|
||||
scoping = Scoping.makeInjectable(Scoping.forAnnotation(scopeAnnotation),
|
||||
injector, errors.withSource(rawType));
|
||||
scoping =
|
||||
Scoping.makeInjectable(
|
||||
Scoping.forAnnotation(scopeAnnotation), injector, errors.withSource(rawType));
|
||||
}
|
||||
}
|
||||
|
||||
errors.throwIfNewErrors(numErrors);
|
||||
|
||||
Factory<T> factoryFactory = new Factory<T>(failIfNotLinked, key);
|
||||
InternalFactory<? extends T> scopedFactory
|
||||
= Scoping.scope(key, injector, factoryFactory, source, scoping);
|
||||
Factory<T> factoryFactory = new Factory<>(failIfNotLinked, key);
|
||||
InternalFactory<? extends T> scopedFactory =
|
||||
Scoping.scope(key, injector, factoryFactory, source, scoping);
|
||||
|
||||
return new ConstructorBindingImpl<T>(
|
||||
injector, key, source, scopedFactory, scoping, factoryFactory, constructorInjector);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked") // the result type always agrees with the ConstructorInjector type
|
||||
public void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
|
||||
factory.constructorInjector =
|
||||
(ConstructorInjector<T>) injector.constructors.get(constructorInjectionPoint, errors);
|
||||
factory.provisionCallback =
|
||||
injector.provisionListenerStore.get(this);
|
||||
factory.provisionCallback = 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() {
|
||||
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() {
|
||||
if (factory.constructorInjector != null) {
|
||||
return factory.constructorInjector.getConstructionProxy().getInjectionPoint();
|
||||
|
@ -141,47 +143,51 @@ 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() {
|
||||
ImmutableSet.Builder<InjectionPoint> builder = ImmutableSet.builder();
|
||||
if (factory.constructorInjector == null) {
|
||||
builder.add(constructorInjectionPoint);
|
||||
// If the below throws, it's OK -- we just ignore those dependencies, because no one
|
||||
// could have used them anyway.
|
||||
try {
|
||||
builder.addAll(InjectionPoint.forInstanceMethodsAndFields(constructorInjectionPoint.getDeclaringType()));
|
||||
builder.addAll(
|
||||
InjectionPoint.forInstanceMethodsAndFields(
|
||||
constructorInjectionPoint.getDeclaringType()));
|
||||
} catch (ConfigurationException ignored) {
|
||||
// This is OK -- we just ignore those dependencies, because no one could have used them
|
||||
// anyway.
|
||||
}
|
||||
} else {
|
||||
builder.add(getConstructor())
|
||||
.addAll(getInjectableMembers());
|
||||
builder.add(getConstructor()).addAll(getInjectableMembers());
|
||||
}
|
||||
|
||||
return Dependency.forInjectionPoints(builder.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
|
||||
checkState(factory.constructorInjector != null, "not initialized");
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InjectionPoint getConstructor() {
|
||||
checkState(factory.constructorInjector != null, "Binding is not ready");
|
||||
return factory.constructorInjector.getConstructionProxy().getInjectionPoint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<InjectionPoint> getInjectableMembers() {
|
||||
checkState(factory.constructorInjector != null, "Binding is not ready");
|
||||
return factory.constructorInjector.getInjectableMembers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Dependency<?>> getDependencies() {
|
||||
return Dependency.forInjectionPoints(new ImmutableSet.Builder<InjectionPoint>()
|
||||
.add(getConstructor())
|
||||
.addAll(getInjectableMembers())
|
||||
.build());
|
||||
return Dependency.forInjectionPoints(
|
||||
new ImmutableSet.Builder<InjectionPoint>()
|
||||
.add(getConstructor())
|
||||
.addAll(getInjectableMembers())
|
||||
.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -196,11 +202,17 @@ final class ConstructorBindingImpl<T> extends BindingImpl<T>
|
|||
null, key, getSource(), factory, getScoping(), factory, constructorInjectionPoint);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked") // the raw constructor member and declaring type always agree
|
||||
public void applyTo(Binder binder) {
|
||||
InjectionPoint constructor = getConstructor();
|
||||
getScoping().applyTo(binder.withSource(getSource()).bind(getKey()).toConstructor(
|
||||
(Constructor) getConstructor().getMember(), (TypeLiteral) constructor.getDeclaringType()));
|
||||
getScoping()
|
||||
.applyTo(
|
||||
withTrustedSource(GUICE_INTERNAL, binder, getSource())
|
||||
.bind(getKey())
|
||||
.toConstructor(
|
||||
(Constructor) getConstructor().getMember(),
|
||||
(TypeLiteral) constructor.getDeclaringType()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -240,19 +252,22 @@ final class ConstructorBindingImpl<T> extends BindingImpl<T>
|
|||
this.key = key;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
|
||||
throws ErrorsException {
|
||||
checkState(constructorInjector != null, "Constructor not ready");
|
||||
public T get(InternalContext context, Dependency<?> dependency, boolean linked)
|
||||
throws InternalProvisionException {
|
||||
ConstructorInjector<T> localInjector = constructorInjector;
|
||||
if (localInjector == null) {
|
||||
throw new IllegalStateException("Constructor not ready");
|
||||
}
|
||||
|
||||
if (failIfNotLinked && !linked) {
|
||||
throw errors.jitDisabled(key).toException();
|
||||
if (!linked && failIfNotLinked) {
|
||||
throw InternalProvisionException.jitDisabled(key);
|
||||
}
|
||||
|
||||
// This may not actually be safe because it could return a super type of T (if that's all the
|
||||
// client needs), but it should be OK in practice thanks to the wonders of erasure.
|
||||
return (T) constructorInjector.construct(errors, context,
|
||||
dependency.getKey().getTypeLiteral().getRawType(), provisionCallback);
|
||||
return (T) localInjector.construct(context, dependency, provisionCallback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package com.google.inject.internal;
|
|||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.inject.internal.ProvisionListenerStackCallback.ProvisionCallback;
|
||||
import com.google.inject.spi.Dependency;
|
||||
import com.google.inject.spi.InjectionPoint;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
@ -40,37 +41,39 @@ final class ConstructorInjector<T> {
|
|||
* Construct an instance. Returns {@code Object} instead of {@code T} because
|
||||
* it may return a proxy.
|
||||
*/
|
||||
Object construct(final Errors errors, final InternalContext context,
|
||||
Class<?> expectedType,
|
||||
Object construct(final InternalContext context,
|
||||
Dependency<?> dependency,
|
||||
ProvisionListenerStackCallback<T> provisionCallback)
|
||||
throws ErrorsException {
|
||||
throws InternalProvisionException {
|
||||
final ConstructionContext<T> constructionContext = context.getConstructionContext(this);
|
||||
|
||||
// We have a circular reference between constructors. Return a proxy.
|
||||
if (constructionContext.isConstructing()) {
|
||||
// TODO (crazybob): if we can't proxy this object, can we proxy the other object?
|
||||
return constructionContext.createProxy(
|
||||
errors, context.getInjectorOptions(), expectedType);
|
||||
context.getInjectorOptions(), dependency.getKey().getTypeLiteral().getRawType());
|
||||
}
|
||||
|
||||
// If we're re-entering this factory while injecting fields or methods,
|
||||
// return the same instance. This prevents infinite loops.
|
||||
T t = constructionContext.getCurrentReference();
|
||||
if (t != null) {
|
||||
return t;
|
||||
if (context.getInjectorOptions().disableCircularProxies) {
|
||||
throw InternalProvisionException.circularDependenciesDisabled(
|
||||
dependency.getKey().getTypeLiteral().getRawType());
|
||||
} else {
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
constructionContext.startConstruction();
|
||||
try {
|
||||
// Optimization: Don't go through the callback stack if we have no listeners.
|
||||
if (!provisionCallback.hasListeners()) {
|
||||
return provision(errors, context, constructionContext);
|
||||
if (provisionCallback == null) {
|
||||
return provision(context, constructionContext);
|
||||
} else {
|
||||
return provisionCallback.provision(errors, context, new ProvisionCallback<T>() {
|
||||
public T call() throws ErrorsException {
|
||||
return provision(errors, context, constructionContext);
|
||||
}
|
||||
});
|
||||
return provisionCallback.provision(context, () ->
|
||||
provision(context, constructionContext));
|
||||
}
|
||||
} finally {
|
||||
constructionContext.finishConstruction();
|
||||
|
@ -80,12 +83,12 @@ final class ConstructorInjector<T> {
|
|||
/**
|
||||
* Provisions a new T.
|
||||
*/
|
||||
private T provision(Errors errors, InternalContext context,
|
||||
ConstructionContext<T> constructionContext) throws ErrorsException {
|
||||
private T provision(InternalContext context, ConstructionContext<T> constructionContext)
|
||||
throws InternalProvisionException {
|
||||
try {
|
||||
T t;
|
||||
try {
|
||||
Object[] parameters = SingleParameterInjector.getAll(errors, context, parameterInjectors);
|
||||
Object[] parameters = SingleParameterInjector.getAll(context, parameterInjectors);
|
||||
t = constructionProxy.newInstance(parameters);
|
||||
constructionContext.setProxyDelegates(t);
|
||||
} finally {
|
||||
|
@ -94,17 +97,17 @@ final class ConstructorInjector<T> {
|
|||
|
||||
// Store reference. If an injector re-enters this factory, they'll get the same reference.
|
||||
constructionContext.setCurrentReference(t);
|
||||
|
||||
membersInjector.injectMembers(t, errors, context, false);
|
||||
membersInjector.notifyListeners(t, errors);
|
||||
MembersInjectorImpl<T> localMembersInjector = membersInjector;
|
||||
localMembersInjector.injectMembers(t, context, false);
|
||||
localMembersInjector.notifyListeners(t);
|
||||
|
||||
return t;
|
||||
} catch (InvocationTargetException userException) {
|
||||
Throwable cause = userException.getCause() != null
|
||||
? userException.getCause()
|
||||
: userException;
|
||||
throw errors.withSource(constructionProxy.getInjectionPoint())
|
||||
.errorInjectingConstructor(cause).toException();
|
||||
throw InternalProvisionException.errorInjectingConstructor(cause)
|
||||
.addSource(constructionProxy.getInjectionPoint());
|
||||
} finally {
|
||||
constructionContext.removeCurrentReference();
|
||||
}
|
||||
|
|
|
@ -8,8 +8,8 @@ import com.google.inject.spi.InjectionPoint;
|
|||
final class ConstructorInjectorStore {
|
||||
private final InjectorImpl injector;
|
||||
|
||||
private final FailableCache<InjectionPoint, ConstructorInjector<?>> cache
|
||||
= new FailableCache<InjectionPoint, ConstructorInjector<?>>() {
|
||||
private final FailableCache<InjectionPoint, ConstructorInjector<?>> cache =
|
||||
new FailableCache<>() {
|
||||
@Override
|
||||
protected ConstructorInjector<?> create(InjectionPoint constructorInjector, Errors errors)
|
||||
throws ErrorsException {
|
||||
|
@ -46,12 +46,12 @@ final class ConstructorInjectorStore {
|
|||
throws ErrorsException {
|
||||
int numErrorsBefore = errors.size();
|
||||
|
||||
SingleParameterInjector<?>[] constructorParameterInjectors
|
||||
= injector.getParametersInjectors(injectionPoint.getDependencies(), errors);
|
||||
SingleParameterInjector<?>[] constructorParameterInjectors =
|
||||
injector.getParametersInjectors(injectionPoint.getDependencies(), errors);
|
||||
|
||||
@SuppressWarnings("unchecked") // the injector type agrees with the injection point type
|
||||
MembersInjectorImpl<T> membersInjector = (MembersInjectorImpl<T>) injector.membersInjectorStore
|
||||
.get(injectionPoint.getDeclaringType(), errors);
|
||||
MembersInjectorImpl<T> membersInjector = (MembersInjectorImpl<T>)
|
||||
injector.membersInjectorStore.get(injectionPoint.getDeclaringType(), errors);
|
||||
|
||||
ConstructionProxyFactory<T> factory = new DefaultConstructionProxyFactory<T>(injectionPoint);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.collect.ImmutableListMultimap;
|
||||
import com.google.common.collect.LinkedHashMultimap;
|
||||
import com.google.common.collect.ListMultimap;
|
||||
|
@ -12,7 +11,6 @@ import com.google.common.collect.Multimaps;
|
|||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
@ -43,7 +41,7 @@ interface CycleDetectingLock<ID> {
|
|||
* In case no cycle is detected performance is O(threads creating singletons),
|
||||
* in case cycle is detected performance is O(singleton locks).
|
||||
*/
|
||||
ListMultimap<Long, ID> lockOrDetectPotentialLocksCycle();
|
||||
ListMultimap<Thread, ID> lockOrDetectPotentialLocksCycle();
|
||||
|
||||
/**
|
||||
* Unlocks previously locked lock.
|
||||
|
@ -82,7 +80,7 @@ interface CycleDetectingLock<ID> {
|
|||
*
|
||||
* Guarded by {@code this}.
|
||||
*/
|
||||
private final Multimap<Long, ReentrantCycleDetectingLock> locksOwnedByThread =
|
||||
private static final Multimap<Thread, ReentrantCycleDetectingLock<?>> locksOwnedByThread =
|
||||
LinkedHashMultimap.create();
|
||||
/**
|
||||
* Specifies lock that thread is currently waiting on to own it.
|
||||
|
@ -100,22 +98,22 @@ interface CycleDetectingLock<ID> {
|
|||
*
|
||||
* Guarded by {@code this}.
|
||||
*/
|
||||
private Map<Long, ReentrantCycleDetectingLock> lockThreadIsWaitingOn = Maps.newHashMap();
|
||||
private static Map<Thread, ReentrantCycleDetectingLock<?>> lockThreadIsWaitingOn = Maps.newHashMap();
|
||||
|
||||
/**
|
||||
* Creates new lock within this factory context. We can guarantee that locks created by
|
||||
* the same factory would not deadlock.
|
||||
*
|
||||
* @param newLockId lock id that would be used to report lock cycles if detected
|
||||
* @param userLockId lock id that would be used to report lock cycles if detected
|
||||
*/
|
||||
CycleDetectingLock<ID> create(ID newLockId) {
|
||||
return new ReentrantCycleDetectingLock(newLockId, new ReentrantLock());
|
||||
CycleDetectingLock<ID> create(ID userLockId) {
|
||||
return new ReentrantCycleDetectingLock<>(this, userLockId, new ReentrantLock());
|
||||
}
|
||||
|
||||
/**
|
||||
* The implementation for {@link CycleDetectingLock}.
|
||||
*/
|
||||
class ReentrantCycleDetectingLock implements CycleDetectingLock<ID> {
|
||||
static class ReentrantCycleDetectingLock<ID> implements CycleDetectingLock<ID> {
|
||||
|
||||
/**
|
||||
* Underlying lock used for actual waiting when no potential deadlocks are detected.
|
||||
|
@ -125,50 +123,56 @@ interface CycleDetectingLock<ID> {
|
|||
* User id for this lock.
|
||||
*/
|
||||
private final ID userLockId;
|
||||
/** Factory that was used to create this lock. */
|
||||
private final CycleDetectingLockFactory<ID> lockFactory;
|
||||
/**
|
||||
* Thread id for the thread that owned this lock. Nullable.
|
||||
* Guarded by {@code CycleDetectingLockFactory.this}.
|
||||
*/
|
||||
private Long lockOwnerThreadId = null;
|
||||
private Thread lockOwnerThread = null;
|
||||
/**
|
||||
* Number of times that thread owned this lock.
|
||||
* Guarded by {@code CycleDetectingLockFactory.this}.
|
||||
*/
|
||||
private int lockReentranceCount = 0;
|
||||
|
||||
ReentrantCycleDetectingLock(ID userLockId, Lock lockImplementation) {
|
||||
ReentrantCycleDetectingLock(
|
||||
CycleDetectingLockFactory<ID> lockFactory, ID userLockId, Lock lockImplementation) {
|
||||
this.lockFactory = lockFactory;
|
||||
this.userLockId = Preconditions.checkNotNull(userLockId, "userLockId");
|
||||
this.lockImplementation = Preconditions.checkNotNull(
|
||||
lockImplementation, "lockImplementation");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListMultimap<Long, ID> lockOrDetectPotentialLocksCycle() {
|
||||
final long currentThreadId = Thread.currentThread().getId();
|
||||
synchronized (CycleDetectingLockFactory.this) {
|
||||
public ListMultimap<Thread, ID> lockOrDetectPotentialLocksCycle() {
|
||||
final Thread currentThread = Thread.currentThread();
|
||||
synchronized (CycleDetectingLockFactory.class) {
|
||||
checkState();
|
||||
ListMultimap<Long, ID> locksInCycle = detectPotentialLocksCycle();
|
||||
// Add this lock to the waiting map to ensure it is included in any reported lock cycle.
|
||||
lockThreadIsWaitingOn.put(currentThread, this);
|
||||
ListMultimap<Thread, ID> locksInCycle = detectPotentialLocksCycle();
|
||||
if (!locksInCycle.isEmpty()) {
|
||||
// We aren't actually going to wait for this lock, so remove it from the map.
|
||||
lockThreadIsWaitingOn.remove(currentThread);
|
||||
// potential deadlock is found, we don't try to take this lock
|
||||
return locksInCycle;
|
||||
}
|
||||
|
||||
lockThreadIsWaitingOn.put(currentThreadId, this);
|
||||
}
|
||||
|
||||
// this may be blocking, but we don't expect it to cause a deadlock
|
||||
lockImplementation.lock();
|
||||
|
||||
synchronized (CycleDetectingLockFactory.this) {
|
||||
synchronized (CycleDetectingLockFactory.class) {
|
||||
// current thread is no longer waiting on this lock
|
||||
lockThreadIsWaitingOn.remove(currentThreadId);
|
||||
lockThreadIsWaitingOn.remove(currentThread);
|
||||
checkState();
|
||||
|
||||
// mark it as owned by us
|
||||
lockOwnerThreadId = currentThreadId;
|
||||
lockOwnerThread = currentThread;
|
||||
lockReentranceCount++;
|
||||
// add this lock to the list of locks owned by a current thread
|
||||
locksOwnedByThread.put(currentThreadId, this);
|
||||
locksOwnedByThread.put(currentThread, this);
|
||||
}
|
||||
// no deadlock is found, locking successful
|
||||
return ImmutableListMultimap.of();
|
||||
|
@ -176,12 +180,12 @@ interface CycleDetectingLock<ID> {
|
|||
|
||||
@Override
|
||||
public void unlock() {
|
||||
final long currentThreadId = Thread.currentThread().getId();
|
||||
synchronized (CycleDetectingLockFactory.this) {
|
||||
final Thread currentThread = Thread.currentThread();
|
||||
synchronized (CycleDetectingLockFactory.class) {
|
||||
checkState();
|
||||
Preconditions.checkState(lockOwnerThreadId != null,
|
||||
Preconditions.checkState(lockOwnerThread != null,
|
||||
"Thread is trying to unlock a lock that is not locked");
|
||||
Preconditions.checkState(lockOwnerThreadId == currentThreadId,
|
||||
Preconditions.checkState(lockOwnerThread == currentThread,
|
||||
"Thread is trying to unlock a lock owned by another thread");
|
||||
|
||||
// releasing underlying lock
|
||||
|
@ -191,12 +195,12 @@ interface CycleDetectingLock<ID> {
|
|||
lockReentranceCount--;
|
||||
if (lockReentranceCount == 0) {
|
||||
// we no longer own this lock
|
||||
lockOwnerThreadId = null;
|
||||
Preconditions.checkState(locksOwnedByThread.remove(currentThreadId, this),
|
||||
lockOwnerThread = null;
|
||||
Preconditions.checkState(locksOwnedByThread.remove(currentThread, this),
|
||||
"Internal error: Can not find this lock in locks owned by a current thread");
|
||||
if (locksOwnedByThread.get(currentThreadId).isEmpty()) {
|
||||
if (locksOwnedByThread.get(currentThread).isEmpty()) {
|
||||
// clearing memory
|
||||
locksOwnedByThread.removeAll(currentThreadId);
|
||||
locksOwnedByThread.removeAll(currentThread);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -206,14 +210,14 @@ interface CycleDetectingLock<ID> {
|
|||
* Check consistency of an internal state.
|
||||
*/
|
||||
void checkState() throws IllegalStateException {
|
||||
final long currentThreadId = Thread.currentThread().getId();
|
||||
Preconditions.checkState(!lockThreadIsWaitingOn.containsKey(currentThreadId),
|
||||
final Thread currentThread = Thread.currentThread();
|
||||
Preconditions.checkState(!lockThreadIsWaitingOn.containsKey(currentThread),
|
||||
"Internal error: Thread should not be in a waiting thread on a lock now");
|
||||
if (lockOwnerThreadId != null) {
|
||||
if (lockOwnerThread != null) {
|
||||
// check state of a locked lock
|
||||
Preconditions.checkState(lockReentranceCount >= 0,
|
||||
"Internal error: Lock ownership and reentrance count internal states do not match");
|
||||
Preconditions.checkState(locksOwnedByThread.get(lockOwnerThreadId).contains(this),
|
||||
Preconditions.checkState(locksOwnedByThread.get(lockOwnerThread).contains(this),
|
||||
"Internal error: Set of locks owned by a current thread and lock "
|
||||
+ "ownership status do not match");
|
||||
} else {
|
||||
|
@ -233,74 +237,80 @@ interface CycleDetectingLock<ID> {
|
|||
*
|
||||
* @see CycleDetectingLock#lockOrDetectPotentialLocksCycle()
|
||||
*/
|
||||
private ListMultimap<Long, ID> detectPotentialLocksCycle() {
|
||||
final long currentThreadId = Thread.currentThread().getId();
|
||||
if (lockOwnerThreadId == null || lockOwnerThreadId == currentThreadId) {
|
||||
private ListMultimap<Thread, ID> detectPotentialLocksCycle() {
|
||||
final Thread currentThread = Thread.currentThread();
|
||||
if (lockOwnerThread == null || lockOwnerThread == currentThread) {
|
||||
// if nobody owns this lock, lock cycle is impossible
|
||||
// if a current thread owns this lock, we let Guice to handle it
|
||||
return ImmutableListMultimap.of();
|
||||
}
|
||||
|
||||
ListMultimap<Long, ID> potentialLocksCycle = Multimaps.newListMultimap(
|
||||
new LinkedHashMap<Long, Collection<ID>>(),
|
||||
new Supplier<List<ID>>() {
|
||||
@Override
|
||||
public List<ID> get() {
|
||||
return Lists.newArrayList();
|
||||
}
|
||||
});
|
||||
ListMultimap<Thread, ID> potentialLocksCycle = Multimaps.newListMultimap(
|
||||
new LinkedHashMap<>(), Lists::newArrayList);
|
||||
// lock that is a part of a potential locks cycle, starts with current lock
|
||||
ReentrantCycleDetectingLock lockOwnerWaitingOn = this;
|
||||
ReentrantCycleDetectingLock<?> lockOwnerWaitingOn = this;
|
||||
// try to find a dependency path between lock's owner thread and a current thread
|
||||
while (lockOwnerWaitingOn != null && lockOwnerWaitingOn.lockOwnerThreadId != null) {
|
||||
Long threadOwnerThreadWaits = lockOwnerWaitingOn.lockOwnerThreadId;
|
||||
while (lockOwnerWaitingOn != null && lockOwnerWaitingOn.lockOwnerThread != null) {
|
||||
Thread threadOwnerThreadWaits = lockOwnerWaitingOn.lockOwnerThread;
|
||||
// in case locks cycle exists lock we're waiting for is part of it
|
||||
potentialLocksCycle.putAll(threadOwnerThreadWaits,
|
||||
getAllLockIdsAfter(threadOwnerThreadWaits, lockOwnerWaitingOn));
|
||||
|
||||
if (threadOwnerThreadWaits == currentThreadId) {
|
||||
lockOwnerWaitingOn =
|
||||
addAllLockIdsAfter(threadOwnerThreadWaits, lockOwnerWaitingOn, potentialLocksCycle);
|
||||
if (threadOwnerThreadWaits == currentThread) {
|
||||
// owner thread depends on current thread, cycle detected
|
||||
return potentialLocksCycle;
|
||||
}
|
||||
// going for the next thread we wait on indirectly
|
||||
lockOwnerWaitingOn = lockThreadIsWaitingOn.get(threadOwnerThreadWaits);
|
||||
}
|
||||
// no dependency path from an owner thread to a current thread
|
||||
return ImmutableListMultimap.of();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return locks owned by a thread after a lock specified, inclusive.
|
||||
* Adds all locks held by the given thread that are after the given lock and then returns the
|
||||
* lock the thread is currently waiting on, if any
|
||||
*/
|
||||
private List<ID> getAllLockIdsAfter(long threadId, ReentrantCycleDetectingLock lock) {
|
||||
List<ID> ids = Lists.newArrayList();
|
||||
private ReentrantCycleDetectingLock<?> addAllLockIdsAfter(
|
||||
Thread thread,
|
||||
ReentrantCycleDetectingLock<?> lock,
|
||||
ListMultimap<Thread, ID> potentialLocksCycle) {
|
||||
boolean found = false;
|
||||
Collection<ReentrantCycleDetectingLock> ownedLocks = locksOwnedByThread.get(threadId);
|
||||
Collection<ReentrantCycleDetectingLock<?>> ownedLocks = locksOwnedByThread.get(thread);
|
||||
Preconditions.checkNotNull(ownedLocks,
|
||||
"Internal error: No locks were found taken by a thread");
|
||||
for (ReentrantCycleDetectingLock ownedLock : ownedLocks) {
|
||||
for (ReentrantCycleDetectingLock<?> ownedLock : ownedLocks) {
|
||||
if (ownedLock == lock) {
|
||||
found = true;
|
||||
}
|
||||
if (found) {
|
||||
ids.add(ownedLock.userLockId);
|
||||
if (found && ownedLock.lockFactory == this.lockFactory) {
|
||||
// All locks are stored in a shared map therefore there is no way to
|
||||
// enforce type safety. We know that our cast is valid as we check for a lock's
|
||||
// factory. If the lock was generated by the
|
||||
// same factory it has to have same type as the current lock.
|
||||
@SuppressWarnings("unchecked")
|
||||
ID userLockId = (ID) ownedLock.userLockId;
|
||||
potentialLocksCycle.put(thread, userLockId);
|
||||
}
|
||||
}
|
||||
Preconditions.checkState(found, "Internal error: We can not find locks that "
|
||||
+ "created a cycle that we detected");
|
||||
return ids;
|
||||
Preconditions.checkState(found,
|
||||
"Internal error: We can not find locks that created a cycle that we detected");
|
||||
ReentrantCycleDetectingLock<?> unownedLock = lockThreadIsWaitingOn.get(thread);
|
||||
// If this thread is waiting for a lock add it to the cycle and return it
|
||||
if (unownedLock != null && unownedLock.lockFactory == this.lockFactory) {
|
||||
@SuppressWarnings("unchecked")
|
||||
ID typed = (ID) unownedLock.userLockId;
|
||||
potentialLocksCycle.put(thread, typed);
|
||||
}
|
||||
return unownedLock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
// copy is made to prevent a data race
|
||||
// no synchronization is used, potentially stale data, should be good enough
|
||||
Long localLockOwnerThreadId = this.lockOwnerThreadId;
|
||||
if (localLockOwnerThreadId != null) {
|
||||
return String.format("CycleDetectingLock[%s][locked by %s]",
|
||||
userLockId, localLockOwnerThreadId);
|
||||
Thread thread = this.lockOwnerThread;
|
||||
if (thread != null) {
|
||||
return String.format("%s[%s][locked by %s]", super.toString(), userLockId, thread);
|
||||
} else {
|
||||
return String.format("CycleDetectingLock[%s][unlocked]", userLockId);
|
||||
return String.format("%s[%s][unlocked]", super.toString(), userLockId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
final Constructor<T> constructor = (Constructor<T>) injectionPoint.getMember();
|
||||
|
||||
// Use FastConstructor if the constructor is public.
|
||||
if (Modifier.isPublic(constructor.getModifiers())) {
|
||||
Class<T> classToConstruct = constructor.getDeclaringClass();
|
||||
if (!Modifier.isPublic(classToConstruct.getModifiers())) {
|
||||
constructor.setAccessible(true);
|
||||
}
|
||||
} else {
|
||||
if (!Modifier.isPublic(constructor.getDeclaringClass().getModifiers()) ||
|
||||
!Modifier.isPublic(constructor.getModifiers())) {
|
||||
constructor.setAccessible(true);
|
||||
}
|
||||
|
||||
|
@ -38,17 +33,17 @@ final class DefaultConstructionProxyFactory<T> implements ConstructionProxyFacto
|
|||
public T newInstance(Object... arguments) throws InvocationTargetException {
|
||||
try {
|
||||
return constructor.newInstance(arguments);
|
||||
} catch (InstantiationException e) {
|
||||
} catch (InstantiationException | IllegalAccessException e) {
|
||||
throw new AssertionError(e); // shouldn't happen, we know this is a concrete type
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new AssertionError(e); // a security manager is blocking us, we're hosed
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InjectionPoint getInjectionPoint() {
|
||||
return injectionPoint;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Constructor<T> getConstructor() {
|
||||
return constructor;
|
||||
}
|
||||
|
|
|
@ -32,13 +32,13 @@ final class DeferredLookups implements Lookups {
|
|||
}
|
||||
|
||||
public <T> Provider<T> getProvider(Key<T> key) {
|
||||
ProviderLookup<T> lookup = new ProviderLookup<T>(key, key);
|
||||
ProviderLookup<T> lookup = new ProviderLookup<>(key, key);
|
||||
lookups.add(lookup);
|
||||
return lookup.getProvider();
|
||||
}
|
||||
|
||||
public <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> type) {
|
||||
MembersInjectorLookup<T> lookup = new MembersInjectorLookup<T>(type, type);
|
||||
MembersInjectorLookup<T> lookup = new MembersInjectorLookup<>(type, type);
|
||||
lookups.add(lookup);
|
||||
return lookup.getMembersInjector();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
@ -25,7 +25,6 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
|||
|
||||
enum Type {
|
||||
MAPBINDER,
|
||||
MULTIBINDER,
|
||||
OPTIONALBINDER;
|
||||
MULTIBINDER;
|
||||
}
|
||||
}
|
|
@ -43,6 +43,7 @@ final class EncounterImpl<T> implements TypeEncounter<T> {
|
|||
: ImmutableSet.copyOf(injectionListeners);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(MembersInjector<? super T> membersInjector) {
|
||||
checkState(valid, "Encounters may not be used after hear() returns.");
|
||||
|
||||
|
@ -53,6 +54,7 @@ final class EncounterImpl<T> implements TypeEncounter<T> {
|
|||
membersInjectors.add(membersInjector);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(InjectionListener<? super T> injectionListener) {
|
||||
checkState(valid, "Encounters may not be used after hear() returns.");
|
||||
|
||||
|
@ -63,35 +65,42 @@ final class EncounterImpl<T> implements TypeEncounter<T> {
|
|||
injectionListeners.add(injectionListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addError(String message, Object... arguments) {
|
||||
checkState(valid, "Encounters may not be used after hear() returns.");
|
||||
errors.addMessage(message, arguments);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addError(Throwable t) {
|
||||
checkState(valid, "Encounters may not be used after hear() returns.");
|
||||
errors.errorInUserCode(t, "An exception was caught and reported. Message: %s", t.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addError(Message message) {
|
||||
checkState(valid, "Encounters may not be used after hear() returns.");
|
||||
errors.addMessage(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Provider<T> getProvider(Key<T> key) {
|
||||
checkState(valid, "Encounters may not be used after hear() returns.");
|
||||
return lookups.getProvider(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Provider<T> getProvider(Class<T> type) {
|
||||
return getProvider(Key.get(type));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> typeLiteral) {
|
||||
checkState(valid, "Encounters may not be used after hear() returns.");
|
||||
return lookups.getMembersInjector(typeLiteral);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> MembersInjector<T> getMembersInjector(Class<T> type) {
|
||||
return getMembersInjector(TypeLiteral.get(type));
|
||||
}
|
||||
|
|
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")
|
||||
public class ErrorsException extends Exception {
|
||||
|
||||
// NOTE: this is used by Gin which is abandoned. So changing this API will prevent Gin users from
|
||||
// upgrading Guice version.
|
||||
|
||||
private final Errors errors;
|
||||
|
||||
public ErrorsException(Errors errors) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Dependency<?>> getDependencies() {
|
||||
return ImmutableSet.<Dependency<?>>of(Dependency.get(Key.get(Injector.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrivateElements getPrivateElements() {
|
||||
return privateElements;
|
||||
}
|
||||
|
|
|
@ -18,9 +18,10 @@ final class ExposedKeyFactory<T> implements InternalFactory<T>, CreationListener
|
|||
this.privateElements = privateElements;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notify(Errors errors) {
|
||||
InjectorImpl privateInjector = (InjectorImpl) privateElements.getInjector();
|
||||
BindingImpl<T> explicitBinding = privateInjector.state.getExplicitBinding(key);
|
||||
BindingImpl<T> explicitBinding = privateInjector.getBindingData().getExplicitBinding(key);
|
||||
|
||||
// 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
|
||||
|
@ -33,8 +34,10 @@ final class ExposedKeyFactory<T> implements InternalFactory<T>, CreationListener
|
|||
this.delegate = explicitBinding;
|
||||
}
|
||||
|
||||
public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
|
||||
throws ErrorsException {
|
||||
return delegate.getInternalFactory().get(errors, context, dependency, linked);
|
||||
@Override
|
||||
public T get(InternalContext context, Dependency<?> dependency, boolean 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) {
|
||||
Preconditions.checkNotNull(annotationType, "annotationType");
|
||||
checkNotAnnotated();
|
||||
key = Key.get(key.getTypeLiteral(), annotationType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void annotatedWith(Annotation annotation) {
|
||||
Preconditions.checkNotNull(annotation, "annotation");
|
||||
checkNotAnnotated();
|
||||
|
|
|
@ -25,6 +25,7 @@ final class FactoryProxy<T> implements InternalFactory<T>, CreationListener {
|
|||
this.source = source;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notify(final Errors errors) {
|
||||
try {
|
||||
targetFactory = injector.getInternalFactory(targetKey, errors.withSource(source), JitLimitation.NEW_OR_EXISTING_JIT);
|
||||
|
@ -33,11 +34,15 @@ final class FactoryProxy<T> implements InternalFactory<T>, CreationListener {
|
|||
}
|
||||
}
|
||||
|
||||
public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
|
||||
throws ErrorsException {
|
||||
context.pushState(targetKey, source);
|
||||
@Override
|
||||
public T get(InternalContext context, Dependency<?> dependency, boolean linked)
|
||||
throws InternalProvisionException {
|
||||
Key<? extends T> localTargetKey = targetKey;
|
||||
context.pushState(localTargetKey, source);
|
||||
try {
|
||||
return targetFactory.get(errors.withSource(targetKey), context, dependency, true);
|
||||
return targetFactory.get(context, dependency, true);
|
||||
} catch (InternalProvisionException ipe) {
|
||||
throw ipe.addSource(localTargetKey);
|
||||
} finally {
|
||||
context.popState();
|
||||
}
|
||||
|
|
|
@ -3,6 +3,9 @@ package com.google.inject.internal;
|
|||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Maps;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Lazily creates (and caches) values for keys. If creating the value fails (with errors), an
|
||||
|
@ -10,8 +13,8 @@ import com.google.common.cache.LoadingCache;
|
|||
*/
|
||||
public abstract class FailableCache<K, V> {
|
||||
|
||||
private final LoadingCache<K, Object> delegate = CacheBuilder.newBuilder().build(
|
||||
new CacheLoader<K, Object>() {
|
||||
private final LoadingCache<K, Object> delegate = CacheBuilder.newBuilder()
|
||||
.build(new CacheLoader<>() {
|
||||
public Object load(K key) {
|
||||
Errors errors = new Errors();
|
||||
V result = null;
|
||||
|
@ -33,7 +36,7 @@ public abstract class FailableCache<K, V> {
|
|||
throw errors.toException();
|
||||
} else {
|
||||
@SuppressWarnings("unchecked") // create returned a non-error result, so this is safe
|
||||
V result = (V) resultOrError;
|
||||
V result = (V) resultOrError;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
@ -41,4 +44,14 @@ public abstract class FailableCache<K, V> {
|
|||
boolean remove(K key) {
|
||||
return delegate.asMap().remove(key) != null;
|
||||
}
|
||||
|
||||
Map<K, V> asMap() {
|
||||
return Maps.transformValues(Maps.filterValues(ImmutableMap.copyOf(delegate.asMap()),
|
||||
resultOrError -> !(resultOrError instanceof Errors)),
|
||||
resultOrError -> {
|
||||
@SuppressWarnings("unchecked") // create returned a non-error result, so this is safe
|
||||
V result = (V) resultOrError;
|
||||
return result;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.inject.Binding;
|
||||
|
@ -23,17 +23,22 @@ import java.lang.annotation.Annotation;
|
|||
/**
|
||||
* Visits bindings to return a {@code IndexedBinding} that can be used to emulate the binding
|
||||
* deduplication that Guice internally performs.
|
||||
*
|
||||
* <p>Note: simply using equals/hashCode on the BindingImpls doesn't work because they all have
|
||||
* unique annotations. This works around that by reimplementing equality semantics that ignores
|
||||
* {@link Element#uniqueId()}. A better solution might be to introduce the idea of an 'anonymous'
|
||||
* binding to guice, that might support this usecase directly.
|
||||
*/
|
||||
class Indexer extends DefaultBindingTargetVisitor<Object, Indexer.IndexedBinding>
|
||||
public class Indexer extends DefaultBindingTargetVisitor<Object, Indexer.IndexedBinding>
|
||||
implements BindingScopingVisitor<Object> {
|
||||
private static final Object EAGER_SINGLETON = new Object();
|
||||
final Injector injector;
|
||||
private final Injector injector;
|
||||
|
||||
Indexer(Injector injector) {
|
||||
public Indexer(Injector injector) {
|
||||
this.injector = injector;
|
||||
}
|
||||
|
||||
boolean isIndexable(Binding<?> binding) {
|
||||
public boolean isIndexable(Binding<?> binding) {
|
||||
return binding.getKey().getAnnotation() instanceof Element;
|
||||
}
|
||||
|
||||
|
@ -126,7 +131,7 @@ class Indexer extends DefaultBindingTargetVisitor<Object, Indexer.IndexedBinding
|
|||
PROVIDED_BY,
|
||||
}
|
||||
|
||||
static class IndexedBinding {
|
||||
public static class IndexedBinding {
|
||||
final String annotationName;
|
||||
final Element.Type annotationType;
|
||||
final TypeLiteral<?> typeLiteral;
|
||||
|
@ -134,7 +139,7 @@ class Indexer extends DefaultBindingTargetVisitor<Object, Indexer.IndexedBinding
|
|||
final BindingType type;
|
||||
final Object extraEquality;
|
||||
|
||||
IndexedBinding(Binding<?> binding, BindingType type, Object scope, Object extraEquality) {
|
||||
public IndexedBinding(Binding<?> binding, BindingType type, Object scope, Object extraEquality) {
|
||||
this.scope = scope;
|
||||
this.type = type;
|
||||
this.extraEquality = extraEquality;
|
|
@ -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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
static <T> Initializable<T> of(final T instance) {
|
||||
return new Initializable<T>() {
|
||||
public T get(Errors errors) throws ErrorsException {
|
||||
return new Initializable<>() {
|
||||
public T get() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,15 +3,16 @@ package com.google.inject.internal;
|
|||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.inject.Binding;
|
||||
import com.google.inject.Key;
|
||||
import com.google.inject.Stage;
|
||||
import com.google.inject.TypeLiteral;
|
||||
import com.google.inject.spi.InjectionPoint;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
|
@ -23,27 +24,30 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
|||
*/
|
||||
final class Initializer {
|
||||
|
||||
/**
|
||||
* the only thread that we'll use to inject members.
|
||||
*/
|
||||
private final Thread creatingThread = Thread.currentThread();
|
||||
/** Is set to true once {@link #validateOustandingInjections} is called. */
|
||||
private volatile boolean validationStarted = false;
|
||||
|
||||
/**
|
||||
* zero means everything is injected.
|
||||
* Allows us to detect circular dependencies. It's only used during injectable reference
|
||||
* initialization. After initialization direct access through volatile field is used.
|
||||
*/
|
||||
private final CountDownLatch ready = new CountDownLatch(1);
|
||||
private final CycleDetectingLock.CycleDetectingLockFactory<Class<?>> cycleDetectingLockFactory =
|
||||
new CycleDetectingLock.CycleDetectingLockFactory<>();
|
||||
|
||||
/**
|
||||
* Maps from instances that need injection to the MembersInjector that will inject them.
|
||||
* Instances that need injection during injector creation to a source that registered them. New
|
||||
* references added before {@link #validateOustandingInjections}. Cleared up in {@link
|
||||
* #injectAll}.
|
||||
*/
|
||||
private final Map<Object, MembersInjectorImpl<?>> pendingMembersInjectors =
|
||||
private final List<InjectableReference<?>> pendingInjections = Lists.newArrayList();
|
||||
|
||||
/**
|
||||
* Map that guarantees that no instance would get two references. New references added before
|
||||
* {@link #validateOustandingInjections}. Cleared up in {@link #validateOustandingInjections}.
|
||||
*/
|
||||
private final IdentityHashMap<Object, InjectableReference<?>> initializablesCache =
|
||||
Maps.newIdentityHashMap();
|
||||
|
||||
/**
|
||||
* Maps instances that need injection to a source that registered them
|
||||
*/
|
||||
private final Map<Object, InjectableReference<?>> pendingInjection = Maps.newIdentityHashMap();
|
||||
|
||||
/**
|
||||
* Registers an instance for member injection when that step is performed.
|
||||
*
|
||||
|
@ -55,21 +59,35 @@ final class Initializer {
|
|||
<T> Initializable<T> requestInjection(InjectorImpl injector, T instance, Binding<T> binding,
|
||||
Object source, Set<InjectionPoint> injectionPoints) {
|
||||
checkNotNull(source);
|
||||
|
||||
Preconditions.checkState(
|
||||
!validationStarted, "Member injection could not be requested after validation is started");
|
||||
ProvisionListenerStackCallback<T> provisionCallback =
|
||||
binding == null ? null : injector.provisionListenerStore.get(binding);
|
||||
|
||||
// short circuit if the object has no injections or listeners.
|
||||
if (instance == null || (injectionPoints.isEmpty()
|
||||
&& !injector.membersInjectorStore.hasTypeListeners()
|
||||
&& (provisionCallback == null || !provisionCallback.hasListeners()))) {
|
||||
&& (provisionCallback == null))) {
|
||||
return Initializables.of(instance);
|
||||
}
|
||||
|
||||
InjectableReference<T> initializable = new InjectableReference<T>(
|
||||
injector, instance, binding == null ? null : binding.getKey(), provisionCallback, source);
|
||||
pendingInjection.put(instance, initializable);
|
||||
return initializable;
|
||||
if (initializablesCache.containsKey(instance)) {
|
||||
@SuppressWarnings("unchecked") // Map from T to InjectableReference<T>
|
||||
Initializable<T> cached = (Initializable<T>) initializablesCache.get(instance);
|
||||
return cached;
|
||||
}
|
||||
|
||||
InjectableReference<T> injectableReference =
|
||||
new InjectableReference<T>(
|
||||
injector,
|
||||
instance,
|
||||
binding == null ? null : binding.getKey(),
|
||||
provisionCallback,
|
||||
source,
|
||||
cycleDetectingLockFactory.create(instance.getClass()));
|
||||
initializablesCache.put(instance, injectableReference);
|
||||
pendingInjections.add(injectableReference);
|
||||
return injectableReference;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -77,9 +95,11 @@ final class Initializer {
|
|||
* on the injected instances.
|
||||
*/
|
||||
void validateOustandingInjections(Errors errors) {
|
||||
for (InjectableReference<?> reference : pendingInjection.values()) {
|
||||
validationStarted = true;
|
||||
initializablesCache.clear();
|
||||
for (InjectableReference<?> reference : pendingInjections) {
|
||||
try {
|
||||
pendingMembersInjectors.put(reference.instance, reference.validate(errors));
|
||||
reference.validate(errors);
|
||||
} catch (ErrorsException e) {
|
||||
errors.merge(e.getErrors());
|
||||
}
|
||||
|
@ -92,85 +112,122 @@ final class Initializer {
|
|||
* instances are codependent (directly or transitively), ordering of injection is arbitrary.
|
||||
*/
|
||||
void injectAll(final Errors errors) {
|
||||
// loop over a defensive copy since ensureInjected() mutates the set. Unfortunately, that copy
|
||||
// is made complicated by a bug in IBM's JDK, wherein entrySet().toArray(Object[]) doesn't work
|
||||
for (InjectableReference<?> reference : Lists.newArrayList(pendingInjection.values())) {
|
||||
Preconditions.checkState(validationStarted, "Validation should be done before injection");
|
||||
for (InjectableReference<?> reference : pendingInjections) {
|
||||
try {
|
||||
reference.get(errors);
|
||||
} catch (ErrorsException e) {
|
||||
errors.merge(e.getErrors());
|
||||
reference.get();
|
||||
} catch (InternalProvisionException ipe) {
|
||||
errors.merge(ipe);
|
||||
}
|
||||
}
|
||||
|
||||
if (!pendingInjection.isEmpty()) {
|
||||
throw new AssertionError("Failed to satisfy " + pendingInjection);
|
||||
}
|
||||
|
||||
ready.countDown();
|
||||
pendingInjections.clear();
|
||||
}
|
||||
|
||||
private class InjectableReference<T> implements Initializable<T> {
|
||||
private enum InjectableReferenceState {
|
||||
NEW,
|
||||
VALIDATED,
|
||||
INJECTING,
|
||||
READY
|
||||
}
|
||||
|
||||
private static class InjectableReference<T> implements Initializable<T> {
|
||||
private volatile InjectableReferenceState state = InjectableReferenceState.NEW;
|
||||
private volatile MembersInjectorImpl<T> membersInjector = null;
|
||||
|
||||
private final InjectorImpl injector;
|
||||
private final T instance;
|
||||
private final Object source;
|
||||
private final Key<T> key;
|
||||
private final ProvisionListenerStackCallback<T> provisionCallback;
|
||||
private final CycleDetectingLock<?> lock;
|
||||
|
||||
public InjectableReference(InjectorImpl injector, T instance, Key<T> key,
|
||||
ProvisionListenerStackCallback<T> provisionCallback, Object source) {
|
||||
ProvisionListenerStackCallback<T> provisionCallback,
|
||||
Object source,
|
||||
CycleDetectingLock<?> lock) {
|
||||
this.injector = injector;
|
||||
this.key = key; // possibly null!
|
||||
this.provisionCallback = provisionCallback; // possibly null!
|
||||
this.instance = checkNotNull(instance, "instance");
|
||||
this.source = checkNotNull(source, "source");
|
||||
this.lock = checkNotNull(lock, "lock");
|
||||
}
|
||||
|
||||
public MembersInjectorImpl<T> validate(Errors errors) throws ErrorsException {
|
||||
public void validate(Errors errors) throws ErrorsException {
|
||||
@SuppressWarnings("unchecked") // the type of 'T' is a TypeLiteral<T>
|
||||
TypeLiteral<T> type = TypeLiteral.get((Class<T>) instance.getClass());
|
||||
return injector.membersInjectorStore.get(type, errors.withSource(source));
|
||||
TypeLiteral<T> type = TypeLiteral.get((Class<T>) instance.getClass());
|
||||
membersInjector = injector.membersInjectorStore.get(type, errors.withSource(source));
|
||||
Preconditions.checkNotNull(
|
||||
membersInjector,
|
||||
"No membersInjector available for instance: %s, from key: %s",
|
||||
instance,
|
||||
key);
|
||||
state = InjectableReferenceState.VALIDATED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reentrant. If {@code instance} was registered for injection at injector-creation time, this
|
||||
* method will ensure that all its members have been injected before returning.
|
||||
*/
|
||||
public T get(Errors errors) throws ErrorsException {
|
||||
if (ready.getCount() == 0) {
|
||||
@Override
|
||||
public T get() throws InternalProvisionException {
|
||||
// skipping acquiring lock if initialization is already finished
|
||||
if (state == InjectableReferenceState.READY) {
|
||||
return instance;
|
||||
}
|
||||
// acquire lock for current binding to initialize an instance
|
||||
Multimap<?, ?> lockCycle = lock.lockOrDetectPotentialLocksCycle();
|
||||
if (!lockCycle.isEmpty()) {
|
||||
// Potential deadlock detected and creation lock is not taken.
|
||||
// According to injectAll()'s contract return non-initialized instance.
|
||||
|
||||
// just wait for everything to be injected by another thread
|
||||
if (Thread.currentThread() != creatingThread) {
|
||||
try {
|
||||
ready.await();
|
||||
return instance;
|
||||
} catch (InterruptedException e) {
|
||||
// Give up, since we don't know if our injection is ready
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
// This condition should not be possible under the current Guice implementation.
|
||||
// This clause exists for defensive programming purposes.
|
||||
|
||||
// Reasoning:
|
||||
// get() is called either directly from injectAll(), holds no locks and can not create
|
||||
// a cycle, or it is called through a singleton scope, which resolves deadlocks by itself.
|
||||
// Before calling get() object has to be requested for injection.
|
||||
// Initializer.requestInjection() is called either for constant object bindings, which wrap
|
||||
// creation into a Singleton scope, or from Binder.requestInjection(), which
|
||||
// has to use Singleton scope to reuse the same InjectableReference to potentially
|
||||
// create a lock cycle.
|
||||
return instance;
|
||||
}
|
||||
|
||||
// toInject needs injection, do it right away. we only do this once, even if it fails
|
||||
if (pendingInjection.remove(instance) != null) {
|
||||
// safe because we only insert a members injector for the appropriate instance
|
||||
@SuppressWarnings("unchecked")
|
||||
MembersInjectorImpl<T> membersInjector =
|
||||
(MembersInjectorImpl<T>) pendingMembersInjectors.remove(instance);
|
||||
Preconditions.checkState(membersInjector != null,
|
||||
"No membersInjector available for instance: %s, from key: %s", instance, key);
|
||||
try {
|
||||
// lock acquired, current thread owns this instance initialization
|
||||
switch (state) {
|
||||
case READY:
|
||||
return instance;
|
||||
// When instance depends on itself in the same thread potential dead lock
|
||||
// is not detected. We have to prevent a stack overflow and we use
|
||||
// an "injecting" stage to short-circuit a call.
|
||||
case INJECTING:
|
||||
return instance;
|
||||
case VALIDATED:
|
||||
state = InjectableReferenceState.INJECTING;
|
||||
break;
|
||||
case NEW:
|
||||
throw new IllegalStateException("InjectableReference is not validated yet");
|
||||
default:
|
||||
throw new IllegalStateException("Unknown state: " + state);
|
||||
}
|
||||
|
||||
// if in Stage.TOOL, we only want to inject & notify toolable injection points.
|
||||
// (otherwise we'll inject all of them)
|
||||
membersInjector.injectAndNotify(instance,
|
||||
errors.withSource(source),
|
||||
key,
|
||||
provisionCallback,
|
||||
source,
|
||||
injector.options.stage == Stage.TOOL);
|
||||
try {
|
||||
membersInjector.injectAndNotify(
|
||||
instance, key, provisionCallback, source, injector.options.stage == Stage.TOOL);
|
||||
} catch (InternalProvisionException ipe) {
|
||||
throw ipe.addSource(source);
|
||||
}
|
||||
// mark instance as ready to skip a lock on subsequent calls
|
||||
state = InjectableReferenceState.READY;
|
||||
return instance;
|
||||
} finally {
|
||||
// always release our creation lock, even on failures
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -98,21 +98,19 @@ final class InjectionRequestProcessor extends AbstractProcessor {
|
|||
}
|
||||
|
||||
void injectMembers() {
|
||||
try {
|
||||
injector.callInContext(new ContextualCallable<Void>() {
|
||||
public Void call(InternalContext context) {
|
||||
for (SingleMemberInjector memberInjector : memberInjectors) {
|
||||
// Run injections if we're not in tool stage (ie, PRODUCTION or DEV),
|
||||
// or if we are in tool stage and the injection point is toolable.
|
||||
if (injector.options.stage != Stage.TOOL || memberInjector.getInjectionPoint().isToolable()) {
|
||||
memberInjector.inject(errors, context, null);
|
||||
}
|
||||
try (InternalContext context = injector.enterContext()) {
|
||||
boolean isStageTool = injector.options.stage == Stage.TOOL;
|
||||
for (SingleMemberInjector memberInjector : memberInjectors) {
|
||||
// Run injections if we're not in tool stage (ie, PRODUCTION or DEV),
|
||||
// or if we are in tool stage and the injection point is toolable.
|
||||
if (!isStageTool || memberInjector.getInjectionPoint().isToolable()) {
|
||||
try {
|
||||
memberInjector.inject(context, null);
|
||||
} catch (InternalProvisionException e) {
|
||||
errors.merge(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
} catch (ErrorsException e) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
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.Lists;
|
||||
import com.google.inject.Binder;
|
||||
|
@ -10,8 +14,9 @@ import com.google.inject.Provider;
|
|||
import com.google.inject.Singleton;
|
||||
import com.google.inject.Stage;
|
||||
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.Stopwatch;
|
||||
import com.google.inject.spi.BindingSourceRestriction;
|
||||
import com.google.inject.spi.Dependency;
|
||||
import com.google.inject.spi.Element;
|
||||
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.ProvisionListenerBinding;
|
||||
import com.google.inject.spi.TypeListenerBinding;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.inject.Scopes.SINGLETON;
|
||||
import java.util.Optional;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* A partially-initialized injector. See {@link InternalInjectorCreator}, which
|
||||
* uses this to build a tree of injectors in batch.
|
||||
* InjectorShell is used by {@link InternalInjectorCreator} to recursively create a tree of
|
||||
* 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 {
|
||||
|
||||
private final List<Element> elements;
|
||||
private final InjectorImpl injector;
|
||||
|
||||
private InjectorShell(Builder builder, List<Element> elements, InjectorImpl injector) {
|
||||
private InjectorShell(List<Element> elements, InjectorImpl injector) {
|
||||
this.elements = elements;
|
||||
this.injector = injector;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
return injector;
|
||||
}
|
||||
|
@ -90,18 +65,15 @@ final class InjectorShell {
|
|||
private final List<Element> elements = Lists.newArrayList();
|
||||
private final List<Module> modules = Lists.newArrayList();
|
||||
|
||||
/**
|
||||
* lazily constructed
|
||||
*/
|
||||
private State state;
|
||||
// lazily constructed fields
|
||||
private InjectorBindingData bindingData;
|
||||
private InjectorJitBindingData jitBindingData;
|
||||
|
||||
private InjectorImpl parent;
|
||||
private InjectorOptions options;
|
||||
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;
|
||||
|
||||
Builder stage(Stage stage) {
|
||||
|
@ -111,7 +83,8 @@ final class InjectorShell {
|
|||
|
||||
Builder parent(InjectorImpl 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.stage = options.stage;
|
||||
return this;
|
||||
|
@ -124,10 +97,8 @@ final class InjectorShell {
|
|||
}
|
||||
|
||||
void addModules(Iterable<? extends Module> modules) {
|
||||
if (modules != null) {
|
||||
for (Module module : modules) {
|
||||
this.modules.add(module);
|
||||
}
|
||||
for (Module module : modules) {
|
||||
this.modules.add(module);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -135,11 +106,15 @@ final class InjectorShell {
|
|||
return options.stage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronize on this before calling {@link #build}.
|
||||
*/
|
||||
/** Synchronize on this before calling {@link #build}. */
|
||||
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(
|
||||
Initializer initializer,
|
||||
ProcessedBindingData bindingData,
|
||||
Stopwatch stopwatch,
|
||||
ProcessedBindingData processedBindingData,
|
||||
ContinuousStopwatch stopwatch,
|
||||
Errors errors) {
|
||||
checkState(stage != null, "Stage not initialized");
|
||||
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
|
||||
if (parent == null) {
|
||||
modules.add(0, new RootModule());
|
||||
} else {
|
||||
modules.add(0, new InheritedScannersModule(parent.state));
|
||||
modules.add(0, new InheritedScannersModule(parent.getBindingData()));
|
||||
}
|
||||
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
|
||||
InjectorOptionsProcessor optionsProcessor = new InjectorOptionsProcessor(errors);
|
||||
optionsProcessor.process(null, elements);
|
||||
options = optionsProcessor.getOptions(stage, options);
|
||||
|
||||
InjectorImpl injector = new InjectorImpl(parent, state, options);
|
||||
InjectorImpl injector = new InjectorImpl(parent, bindingData, jitBindingData, options);
|
||||
if (privateElements != null) {
|
||||
privateElements.initInjector(injector);
|
||||
}
|
||||
|
@ -184,10 +170,11 @@ final class InjectorShell {
|
|||
new MessageProcessor(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);
|
||||
List<ProvisionListenerBinding> provisionListenerBindings =
|
||||
injector.state.getProvisionListenerBindings();
|
||||
injector.getBindingData().getProvisionListenerBindings();
|
||||
injector.provisionListenerStore =
|
||||
new ProvisionListenerCallbackStore(provisionListenerBindings);
|
||||
stopwatch.resetAndLog("TypeListeners & ProvisionListener creation");
|
||||
|
@ -200,38 +187,53 @@ final class InjectorShell {
|
|||
|
||||
bindStage(injector, stage);
|
||||
bindInjector(injector);
|
||||
//bindLogger(injector);
|
||||
bindLogger(injector);
|
||||
|
||||
// Process all normal bindings, then UntargettedBindings.
|
||||
// This is necessary because UntargettedBindings can create JIT bindings
|
||||
// and need all their other dependencies set up ahead of time.
|
||||
new BindingProcessor(errors, initializer, bindingData).process(injector, elements);
|
||||
new UntargettedBindingProcessor(errors, bindingData).process(injector, elements);
|
||||
new BindingProcessor(errors, initializer, processedBindingData).process(injector, elements);
|
||||
new UntargettedBindingProcessor(errors, processedBindingData).process(injector, elements);
|
||||
stopwatch.resetAndLog("Binding creation");
|
||||
|
||||
new ModuleAnnotatedMethodScannerProcessor(errors).process(injector, elements);
|
||||
stopwatch.resetAndLog("Module annotated method scanners creation");
|
||||
|
||||
List<InjectorShell> injectorShells = Lists.newArrayList();
|
||||
injectorShells.add(new InjectorShell(this, elements, injector));
|
||||
injectorShells.add(new InjectorShell(elements, injector));
|
||||
|
||||
// recursively build child shells
|
||||
PrivateElementProcessor processor = new PrivateElementProcessor(errors);
|
||||
processor.process(injector, elements);
|
||||
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");
|
||||
|
||||
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> {
|
||||
|
@ -241,38 +243,78 @@ final class InjectorShell {
|
|||
this.injector = injector;
|
||||
}
|
||||
|
||||
public Injector get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
|
||||
throws ErrorsException {
|
||||
@Override
|
||||
public Injector get(InternalContext context, Dependency<?> dependency, boolean linked) {
|
||||
return injector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Injector get() {
|
||||
return injector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
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();
|
||||
return injectionPoint == null
|
||||
? Logger.getAnonymousLogger()
|
||||
: Logger.getLogger(injectionPoint.getMember().getDeclaringClass().getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Logger get() {
|
||||
return Logger.getAnonymousLogger();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
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 {
|
||||
@Override
|
||||
public void configure(Binder binder) {
|
||||
binder = binder.withSource(SourceProvider.UNKNOWN_SOURCE);
|
||||
binder.bindScope(Singleton.class, SINGLETON);
|
||||
|
@ -281,14 +323,15 @@ final class InjectorShell {
|
|||
}
|
||||
|
||||
private static class InheritedScannersModule implements Module {
|
||||
private final State state;
|
||||
private final InjectorBindingData bindingData;
|
||||
|
||||
InheritedScannersModule(State state) {
|
||||
this.state = state;
|
||||
InheritedScannersModule(InjectorBindingData bindingData) {
|
||||
this.bindingData = bindingData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(Binder binder) {
|
||||
for (ModuleAnnotatedMethodScannerBinding binding : state.getScannerBindings()) {
|
||||
for (ModuleAnnotatedMethodScannerBinding binding : bindingData.getScannerBindings()) {
|
||||
binding.applyTo(binder);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,20 +5,17 @@ import com.google.common.base.Objects;
|
|||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.inject.Binder;
|
||||
import com.google.inject.Key;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.spi.BindingTargetVisitor;
|
||||
import com.google.inject.spi.Dependency;
|
||||
import com.google.inject.spi.HasDependencies;
|
||||
import com.google.inject.spi.InjectionPoint;
|
||||
import com.google.inject.spi.InstanceBinding;
|
||||
import com.google.inject.util.Providers;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
final class InstanceBindingImpl<T> extends BindingImpl<T> implements InstanceBinding<T> {
|
||||
|
||||
final T instance;
|
||||
final Provider<T> provider;
|
||||
final ImmutableSet<InjectionPoint> injectionPoints;
|
||||
|
||||
public InstanceBindingImpl(InjectorImpl injector, Key<T> key, Object source,
|
||||
|
@ -27,7 +24,6 @@ final class InstanceBindingImpl<T> extends BindingImpl<T> implements InstanceBin
|
|||
super(injector, key, source, internalFactory, Scoping.EAGER_SINGLETON);
|
||||
this.injectionPoints = ImmutableSet.copyOf(injectionPoints);
|
||||
this.instance = instance;
|
||||
this.provider = Providers.of(instance);
|
||||
}
|
||||
|
||||
public InstanceBindingImpl(Object source, Key<T> key, Scoping scoping,
|
||||
|
@ -35,40 +31,41 @@ final class InstanceBindingImpl<T> extends BindingImpl<T> implements InstanceBin
|
|||
super(source, key, scoping);
|
||||
this.injectionPoints = ImmutableSet.copyOf(injectionPoints);
|
||||
this.instance = instance;
|
||||
this.provider = Providers.of(instance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Provider<T> getProvider() {
|
||||
return this.provider;
|
||||
}
|
||||
|
||||
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<InjectionPoint> getInjectionPoints() {
|
||||
return injectionPoints;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Dependency<?>> getDependencies() {
|
||||
return instance instanceof HasDependencies
|
||||
? ImmutableSet.copyOf(((HasDependencies) instance).getDependencies())
|
||||
: Dependency.forInjectionPoints(injectionPoints);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BindingImpl<T> withScoping(Scoping scoping) {
|
||||
return new InstanceBindingImpl<T>(getSource(), getKey(), scoping, injectionPoints, instance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BindingImpl<T> withKey(Key<T> key) {
|
||||
return new InstanceBindingImpl<T>(getSource(), key, getScoping(), injectionPoints, instance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyTo(Binder binder) {
|
||||
// instance bindings aren't scoped
|
||||
binder.withSource(getSource()).bind(getKey()).toInstance(instance);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue