diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md
index 2424a9c5fad..02344c8c465 100644
--- a/CHANGELOG_PENDING.md
+++ b/CHANGELOG_PENDING.md
@@ -1,3 +1,5 @@
### Improvements
+ - Fix emitted import statements in generated programs such that imports with the same symbol are fully qualified
+
### Bug Fixes
\ No newline at end of file
diff --git a/pkg/cmd/pulumi-language-java/language_test.go b/pkg/cmd/pulumi-language-java/language_test.go
index eeed5ea7e6b..4a5ecde152c 100644
--- a/pkg/cmd/pulumi-language-java/language_test.go
+++ b/pkg/cmd/pulumi-language-java/language_test.go
@@ -163,7 +163,6 @@ var expectedFailures = map[string]string{
"l1-output-array": "#1560 Empty array literals are not generated correctly",
"l1-output-map": "#1561 Map literals are not generated correctly",
"l1-output-string": "#1562 Large string literals are not generated correctly",
- "l2-failed-create-continue-on-error": "#1558 Duplicate identifiers aren't fully qualified",
"l2-invoke-dependencies": "#1563 Invoke argument and result handling",
"l2-invoke-options": "#1563 Invoke argument and result handling",
"l2-invoke-options-depends-on": "#1563 Invoke argument and result handling",
diff --git a/pkg/cmd/pulumi-language-java/testdata/projects/l2-failed-create-continue-on-error/Pulumi.yaml b/pkg/cmd/pulumi-language-java/testdata/projects/l2-failed-create-continue-on-error/Pulumi.yaml
new file mode 100644
index 00000000000..c1f82ca12aa
--- /dev/null
+++ b/pkg/cmd/pulumi-language-java/testdata/projects/l2-failed-create-continue-on-error/Pulumi.yaml
@@ -0,0 +1,2 @@
+name: l2-failed-create-continue-on-error
+runtime: java
diff --git a/pkg/cmd/pulumi-language-java/testdata/projects/l2-failed-create-continue-on-error/pom.xml b/pkg/cmd/pulumi-language-java/testdata/projects/l2-failed-create-continue-on-error/pom.xml
new file mode 100644
index 00000000000..a50390627ef
--- /dev/null
+++ b/pkg/cmd/pulumi-language-java/testdata/projects/l2-failed-create-continue-on-error/pom.xml
@@ -0,0 +1,103 @@
+
+
+ 4.0.0
+
+ com.pulumi
+ l2-failed-create-continue-on-error
+ 1.0-SNAPSHOT
+
+
+ UTF-8
+ 11
+ 11
+ 11
+ generated_program.App
+
+
+
+
+
+ repository-0
+ REPOSITORY
+
+
+
+
+
+ com.pulumi
+ pulumi
+ CORE.VERSION
+
+
+ com.pulumi
+ fail_on_create
+ 4.0.0
+
+ com.pulumi
+ simple
+ 2.0.0
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.2.2
+
+
+
+ true
+ ${mainClass}
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+ 3.4.2
+
+
+
+ true
+ ${mainClass}
+
+
+
+ jar-with-dependencies
+
+
+
+
+ make-my-jar-with-dependencies
+ package
+
+ single
+
+
+
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+ 3.1.0
+
+ ${mainClass}
+ ${mainArgs}
+
+
+
+ org.apache.maven.plugins
+ maven-wrapper-plugin
+ 3.1.1
+
+ 3.8.5
+
+
+
+
+
\ No newline at end of file
diff --git a/pkg/cmd/pulumi-language-java/testdata/projects/l2-failed-create-continue-on-error/src/main/java/generated_program/App.java b/pkg/cmd/pulumi-language-java/testdata/projects/l2-failed-create-continue-on-error/src/main/java/generated_program/App.java
new file mode 100644
index 00000000000..bff816315a1
--- /dev/null
+++ b/pkg/cmd/pulumi-language-java/testdata/projects/l2-failed-create-continue-on-error/src/main/java/generated_program/App.java
@@ -0,0 +1,47 @@
+package generated_program;
+
+import com.pulumi.Context;
+import com.pulumi.Pulumi;
+import com.pulumi.core.Output;
+import com.pulumi.resources.CustomResourceOptions;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Map;
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+
+public class App {
+ public static void main(String[] args) {
+ Pulumi.run(App::stack);
+ }
+
+ public static void stack(Context ctx) {
+ var failing = new com.pulumi.fail_on_create.Resource("failing", com.pulumi.fail_on_create.ResourceArgs.builder()
+ .value(false)
+ .build());
+
+ var dependent = new com.pulumi.simple.Resource("dependent", com.pulumi.simple.ResourceArgs.builder()
+ .value(true)
+ .build(), CustomResourceOptions.builder()
+ .dependsOn(failing)
+ .build());
+
+ var dependent_on_output = new com.pulumi.simple.Resource("dependent_on_output", com.pulumi.simple.ResourceArgs.builder()
+ .value(failing.value())
+ .build());
+
+ var independent = new com.pulumi.simple.Resource("independent", com.pulumi.simple.ResourceArgs.builder()
+ .value(true)
+ .build());
+
+ var double_dependency = new com.pulumi.simple.Resource("double_dependency", com.pulumi.simple.ResourceArgs.builder()
+ .value(true)
+ .build(), CustomResourceOptions.builder()
+ .dependsOn(
+ independent,
+ dependent_on_output)
+ .build());
+
+ }
+}
diff --git a/pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/README.md b/pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/README.md
new file mode 100644
index 00000000000..8d1c8b69c3f
--- /dev/null
+++ b/pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/README.md
@@ -0,0 +1 @@
+
diff --git a/pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/build.gradle b/pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/build.gradle
new file mode 100644
index 00000000000..2a2d880b018
--- /dev/null
+++ b/pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/build.gradle
@@ -0,0 +1,157 @@
+// *** WARNING: this file was generated by pulumi-java-gen ***
+// *** Do not edit by hand unless you're certain you know what you are doing! ***
+
+plugins {
+ id("signing")
+ id("java-library")
+ id("maven-publish")
+}
+
+group = "com.pulumi"
+
+def resolvedVersion = System.getenv("PACKAGE_VERSION") ?:
+ (project.version == "unspecified"
+ ? "4.0.0"
+ : project.version)
+
+def signingKey = System.getenv("SIGNING_KEY")
+def signingPassword = System.getenv("SIGNING_PASSWORD")
+def publishRepoURL = System.getenv("PUBLISH_REPO_URL")
+def publishRepoUsername = System.getenv("PUBLISH_REPO_USERNAME")
+def publishRepoPassword = System.getenv("PUBLISH_REPO_PASSWORD")
+
+java {
+ toolchain {
+ languageVersion = JavaLanguageVersion.of(11)
+ }
+}
+
+compileJava {
+ options.fork = true
+ options.forkOptions.jvmArgs.addAll(["-Xmx16g"])
+ options.encoding = "UTF-8"
+}
+
+repositories {
+ maven {
+ url("REPOSITORY")
+ }
+ mavenLocal()
+ maven { // The google mirror is less flaky than mavenCentral()
+ url("https://maven-central.storage-download.googleapis.com/maven2/")
+ }
+ mavenCentral()
+}
+
+dependencies {
+ implementation("com.google.code.findbugs:jsr305:3.0.2")
+ implementation("com.google.code.gson:gson:2.8.9")
+ implementation("com.pulumi:pulumi:CORE.VERSION")
+}
+
+task sourcesJar(type: Jar) {
+ from sourceSets.main.allJava
+ archiveClassifier.set('sources')
+}
+
+task javadocJar(type: Jar) {
+ from javadoc
+ archiveClassifier.set('javadoc')
+ zip64 = true
+}
+
+def genPulumiResources = tasks.register('genPulumiResources') {
+ doLast {
+ def resourcesDir = sourceSets.main.output.resourcesDir
+ def subDir = project.name.replace(".", "/")
+ def outDir = file("$resourcesDir/$subDir")
+ outDir.mkdirs()
+ new File(outDir, "version.txt").text = resolvedVersion
+ def builder = new groovy.json.JsonBuilder()
+ builder {
+ resource true
+ name "fail_on_create"
+ version resolvedVersion
+ }
+ def infoJson = builder.toPrettyString()
+ new File(outDir, "plugin.json").text = infoJson
+ }
+}
+
+jar.configure {
+ dependsOn genPulumiResources
+}
+
+publishing {
+ publications {
+ mainPublication(MavenPublication) {
+ groupId = "com.pulumi"
+ artifactId = "fail_on_create"
+ version = resolvedVersion
+ from components.java
+ artifact sourcesJar
+ artifact javadocJar
+
+ pom {
+ inceptionYear = ""
+ name = ""
+ packaging = "jar"
+ description = " "
+
+ url = "https://example.com"
+
+ scm {
+ connection = "https://example.com"
+ developerConnection = "https://example.com"
+ url = "https://example.com"
+ }
+
+ licenses {
+ license {
+ name = ""
+ url = ""
+ }
+ }
+
+ developers {
+ developer {
+ id = ""
+ name = ""
+ email = ""
+ }
+ }
+ }
+ }
+ }
+
+ if (publishRepoURL) {
+ repositories {
+ maven {
+ name = "PublishRepo"
+ url = publishRepoURL
+ credentials {
+ username = publishRepoUsername
+ password = publishRepoPassword
+ }
+ }
+ }
+ }
+}
+
+javadoc {
+ if (JavaVersion.current().isJava9Compatible()) {
+ options.addBooleanOption('html5', true)
+ }
+ options.jFlags("-Xmx8g", "-Xms512m")
+}
+
+jar {
+ zip64 = true
+}
+
+if (signingKey) {
+ signing {
+ useInMemoryPgpKeys(signingKey, signingPassword)
+ sign publishing.publications.mainPublication
+ }
+}
\ No newline at end of file
diff --git a/pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/settings.gradle b/pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/settings.gradle
new file mode 100644
index 00000000000..6f46da2d1a7
--- /dev/null
+++ b/pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/settings.gradle
@@ -0,0 +1,14 @@
+// *** WARNING: this file was generated by pulumi-java-gen. ***
+// *** Do not edit by hand unless you're certain you know what you are doing! ***
+
+pluginManagement {
+ repositories {
+ maven { // The google mirror is less flaky than mavenCentral()
+ url("https://maven-central.storage-download.googleapis.com/maven2/")
+ }
+ gradlePluginPortal()
+ }
+}
+
+rootProject.name = "com.pulumi.fail_on_create"
+include("lib")
diff --git a/pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/src/main/java/com/pulumi/fail_on_create/Provider.java b/pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/src/main/java/com/pulumi/fail_on_create/Provider.java
new file mode 100644
index 00000000000..513b5af421f
--- /dev/null
+++ b/pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/src/main/java/com/pulumi/fail_on_create/Provider.java
@@ -0,0 +1,54 @@
+// *** WARNING: this file was generated by pulumi-language-java. ***
+// *** Do not edit by hand unless you're certain you know what you are doing! ***
+
+package com.pulumi.fail_on_create;
+
+import com.pulumi.core.Output;
+import com.pulumi.core.annotations.ResourceType;
+import com.pulumi.core.internal.Codegen;
+import com.pulumi.fail_on_create.ProviderArgs;
+import com.pulumi.fail_on_create.Utilities;
+import javax.annotation.Nullable;
+
+@ResourceType(type="pulumi:providers:fail_on_create")
+public class Provider extends com.pulumi.resources.ProviderResource {
+ /**
+ *
+ * @param name The _unique_ name of the resulting resource.
+ */
+ public Provider(java.lang.String name) {
+ this(name, ProviderArgs.Empty);
+ }
+ /**
+ *
+ * @param name The _unique_ name of the resulting resource.
+ * @param args The arguments to use to populate this resource's properties.
+ */
+ public Provider(java.lang.String name, @Nullable ProviderArgs args) {
+ this(name, args, null);
+ }
+ /**
+ *
+ * @param name The _unique_ name of the resulting resource.
+ * @param args The arguments to use to populate this resource's properties.
+ * @param options A bag of options that control this resource's behavior.
+ */
+ public Provider(java.lang.String name, @Nullable ProviderArgs args, @Nullable com.pulumi.resources.CustomResourceOptions options) {
+ super("fail_on_create", name, makeArgs(args, options), makeResourceOptions(options, Codegen.empty()), false);
+ }
+
+ private static ProviderArgs makeArgs(@Nullable ProviderArgs args, @Nullable com.pulumi.resources.CustomResourceOptions options) {
+ if (options != null && options.getUrn().isPresent()) {
+ return null;
+ }
+ return args == null ? ProviderArgs.Empty : args;
+ }
+
+ private static com.pulumi.resources.CustomResourceOptions makeResourceOptions(@Nullable com.pulumi.resources.CustomResourceOptions options, @Nullable Output id) {
+ var defaultOptions = com.pulumi.resources.CustomResourceOptions.builder()
+ .version(Utilities.getVersion())
+ .build();
+ return com.pulumi.resources.CustomResourceOptions.merge(defaultOptions, options, id);
+ }
+
+}
diff --git a/pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/src/main/java/com/pulumi/fail_on_create/ProviderArgs.java b/pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/src/main/java/com/pulumi/fail_on_create/ProviderArgs.java
new file mode 100644
index 00000000000..c6323b23a12
--- /dev/null
+++ b/pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/src/main/java/com/pulumi/fail_on_create/ProviderArgs.java
@@ -0,0 +1,28 @@
+// *** WARNING: this file was generated by pulumi-language-java. ***
+// *** Do not edit by hand unless you're certain you know what you are doing! ***
+
+package com.pulumi.fail_on_create;
+
+
+
+
+public final class ProviderArgs extends com.pulumi.resources.ResourceArgs {
+
+ public static final ProviderArgs Empty = new ProviderArgs();
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static final class Builder {
+ private ProviderArgs $;
+
+ public Builder() {
+ $ = new ProviderArgs();
+ }
+ public ProviderArgs build() {
+ return $;
+ }
+ }
+
+}
diff --git a/pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/src/main/java/com/pulumi/fail_on_create/Resource.java b/pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/src/main/java/com/pulumi/fail_on_create/Resource.java
new file mode 100644
index 00000000000..416e9e088d7
--- /dev/null
+++ b/pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/src/main/java/com/pulumi/fail_on_create/Resource.java
@@ -0,0 +1,78 @@
+// *** WARNING: this file was generated by pulumi-language-java. ***
+// *** Do not edit by hand unless you're certain you know what you are doing! ***
+
+package com.pulumi.fail_on_create;
+
+import com.pulumi.core.Output;
+import com.pulumi.core.annotations.Export;
+import com.pulumi.core.annotations.ResourceType;
+import com.pulumi.core.internal.Codegen;
+import com.pulumi.fail_on_create.ResourceArgs;
+import com.pulumi.fail_on_create.Utilities;
+import java.lang.Boolean;
+import javax.annotation.Nullable;
+
+@ResourceType(type="fail_on_create:index:Resource")
+public class Resource extends com.pulumi.resources.CustomResource {
+ @Export(name="value", refs={Boolean.class}, tree="[0]")
+ private Output value;
+
+ public Output value() {
+ return this.value;
+ }
+
+ /**
+ *
+ * @param name The _unique_ name of the resulting resource.
+ */
+ public Resource(java.lang.String name) {
+ this(name, ResourceArgs.Empty);
+ }
+ /**
+ *
+ * @param name The _unique_ name of the resulting resource.
+ * @param args The arguments to use to populate this resource's properties.
+ */
+ public Resource(java.lang.String name, ResourceArgs args) {
+ this(name, args, null);
+ }
+ /**
+ *
+ * @param name The _unique_ name of the resulting resource.
+ * @param args The arguments to use to populate this resource's properties.
+ * @param options A bag of options that control this resource's behavior.
+ */
+ public Resource(java.lang.String name, ResourceArgs args, @Nullable com.pulumi.resources.CustomResourceOptions options) {
+ super("fail_on_create:index:Resource", name, makeArgs(args, options), makeResourceOptions(options, Codegen.empty()), false);
+ }
+
+ private Resource(java.lang.String name, Output id, @Nullable com.pulumi.resources.CustomResourceOptions options) {
+ super("fail_on_create:index:Resource", name, null, makeResourceOptions(options, id), false);
+ }
+
+ private static ResourceArgs makeArgs(ResourceArgs args, @Nullable com.pulumi.resources.CustomResourceOptions options) {
+ if (options != null && options.getUrn().isPresent()) {
+ return null;
+ }
+ return args == null ? ResourceArgs.Empty : args;
+ }
+
+ private static com.pulumi.resources.CustomResourceOptions makeResourceOptions(@Nullable com.pulumi.resources.CustomResourceOptions options, @Nullable Output id) {
+ var defaultOptions = com.pulumi.resources.CustomResourceOptions.builder()
+ .version(Utilities.getVersion())
+ .build();
+ return com.pulumi.resources.CustomResourceOptions.merge(defaultOptions, options, id);
+ }
+
+ /**
+ * Get an existing Host resource's state with the given name, ID, and optional extra
+ * properties used to qualify the lookup.
+ *
+ * @param name The _unique_ name of the resulting resource.
+ * @param id The _unique_ provider ID of the resource to lookup.
+ * @param options Optional settings to control the behavior of the CustomResource.
+ */
+ public static Resource get(java.lang.String name, Output id, @Nullable com.pulumi.resources.CustomResourceOptions options) {
+ return new Resource(name, id, options);
+ }
+}
diff --git a/pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/src/main/java/com/pulumi/fail_on_create/ResourceArgs.java b/pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/src/main/java/com/pulumi/fail_on_create/ResourceArgs.java
new file mode 100644
index 00000000000..505a2e28f4d
--- /dev/null
+++ b/pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/src/main/java/com/pulumi/fail_on_create/ResourceArgs.java
@@ -0,0 +1,65 @@
+// *** WARNING: this file was generated by pulumi-language-java. ***
+// *** Do not edit by hand unless you're certain you know what you are doing! ***
+
+package com.pulumi.fail_on_create;
+
+import com.pulumi.core.Output;
+import com.pulumi.core.annotations.Import;
+import com.pulumi.exceptions.MissingRequiredPropertyException;
+import java.lang.Boolean;
+import java.util.Objects;
+
+
+public final class ResourceArgs extends com.pulumi.resources.ResourceArgs {
+
+ public static final ResourceArgs Empty = new ResourceArgs();
+
+ @Import(name="value", required=true)
+ private Output value;
+
+ public Output value() {
+ return this.value;
+ }
+
+ private ResourceArgs() {}
+
+ private ResourceArgs(ResourceArgs $) {
+ this.value = $.value;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+ public static Builder builder(ResourceArgs defaults) {
+ return new Builder(defaults);
+ }
+
+ public static final class Builder {
+ private ResourceArgs $;
+
+ public Builder() {
+ $ = new ResourceArgs();
+ }
+
+ public Builder(ResourceArgs defaults) {
+ $ = new ResourceArgs(Objects.requireNonNull(defaults));
+ }
+
+ public Builder value(Output value) {
+ $.value = value;
+ return this;
+ }
+
+ public Builder value(Boolean value) {
+ return value(Output.of(value));
+ }
+
+ public ResourceArgs build() {
+ if ($.value == null) {
+ throw new MissingRequiredPropertyException("ResourceArgs", "value");
+ }
+ return $;
+ }
+ }
+
+}
diff --git a/pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/src/main/java/com/pulumi/fail_on_create/Utilities.java b/pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/src/main/java/com/pulumi/fail_on_create/Utilities.java
new file mode 100644
index 00000000000..5002cbcb0ff
--- /dev/null
+++ b/pkg/cmd/pulumi-language-java/testdata/sdks/fail_on_create-4.0.0/src/main/java/com/pulumi/fail_on_create/Utilities.java
@@ -0,0 +1,102 @@
+// *** WARNING: this file was generated by pulumi-language-java. ***
+// *** Do not edit by hand unless you're certain you know what you are doing! ***
+
+package com.pulumi.fail_on_create;
+
+
+
+
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import javax.annotation.Nullable;
+import com.pulumi.core.internal.Environment;
+import com.pulumi.deployment.InvokeOptions;
+import com.pulumi.deployment.InvokeOutputOptions;
+
+public class Utilities {
+
+ public static Optional getEnv(java.lang.String... names) {
+ for (var n : names) {
+ var value = Environment.getEnvironmentVariable(n);
+ if (value.isValue()) {
+ return Optional.of(value.value());
+ }
+ }
+ return Optional.empty();
+ }
+
+ public static Optional getEnvBoolean(java.lang.String... names) {
+ for (var n : names) {
+ var value = Environment.getBooleanEnvironmentVariable(n);
+ if (value.isValue()) {
+ return Optional.of(value.value());
+ }
+ }
+ return Optional.empty();
+ }
+
+ public static Optional getEnvInteger(java.lang.String... names) {
+ for (var n : names) {
+ var value = Environment.getIntegerEnvironmentVariable(n);
+ if (value.isValue()) {
+ return Optional.of(value.value());
+ }
+ }
+ return Optional.empty();
+ }
+
+ public static Optional getEnvDouble(java.lang.String... names) {
+ for (var n : names) {
+ var value = Environment.getDoubleEnvironmentVariable(n);
+ if (value.isValue()) {
+ return Optional.of(value.value());
+ }
+ }
+ return Optional.empty();
+ }
+
+ public static InvokeOptions withVersion(@Nullable InvokeOptions options) {
+ if (options != null && options.getVersion().isPresent()) {
+ return options;
+ }
+ return new InvokeOptions(
+ options == null ? null : options.getParent().orElse(null),
+ options == null ? null : options.getProvider().orElse(null),
+ getVersion()
+ );
+ }
+
+ public static InvokeOutputOptions withVersion(@Nullable InvokeOutputOptions options) {
+ if (options != null && options.getVersion().isPresent()) {
+ return options;
+ }
+ return new InvokeOutputOptions(
+ options == null ? null : options.getParent().orElse(null),
+ options == null ? null : options.getProvider().orElse(null),
+ getVersion(),
+ options == null ? null : options.getDependsOn()
+ );
+ }
+
+ private static final java.lang.String version;
+ public static java.lang.String getVersion() {
+ return version;
+ }
+
+ static {
+ var resourceName = "com/pulumi/fail_on_create/version.txt";
+ var versionFile = Utilities.class.getClassLoader().getResourceAsStream(resourceName);
+ if (versionFile == null) {
+ throw new IllegalStateException(
+ java.lang.String.format("expected resource '%s' on Classpath, not found", resourceName)
+ );
+ }
+ version = new BufferedReader(new InputStreamReader(versionFile))
+ .lines()
+ .collect(Collectors.joining("\n"))
+ .trim();
+ }
+}
diff --git a/pkg/codegen/java/gen.go b/pkg/codegen/java/gen.go
index bb804b4a8f0..b3b253d7968 100644
--- a/pkg/codegen/java/gen.go
+++ b/pkg/codegen/java/gen.go
@@ -2270,6 +2270,13 @@ func GeneratePackage(
)
}
+ // local dependencies should only be using the pulumi core sdk
+ // in the future we will be adding local dependencies to component providers
+ // but for non-component providers, we should only be using the pulumi core sdk
+ if parts[1] != "pulumi" {
+ continue
+ }
+
k := parts[0] + ":" + parts[1]
dependencies[k] = parts[2]
diff --git a/pkg/codegen/java/gen_program.go b/pkg/codegen/java/gen_program.go
index fb181577fcd..795e1c22c73 100644
--- a/pkg/codegen/java/gen_program.go
+++ b/pkg/codegen/java/gen_program.go
@@ -49,7 +49,8 @@ type generator struct {
//
// later on when apply a traversal sush as resourceGroup.getName(),
// we should rewrite it as resourceGroup.thenApply(getResourceGroupResult -> getResourceGroupResult.getName())
- functionInvokes map[string]*schema.Function
+ functionInvokes map[string]*schema.Function
+ emittedTypeImportSymbols codegen.StringSet
}
// genComment generates a comment into the output.
@@ -187,8 +188,9 @@ func GenerateProgram(program *pcl.Program) (map[string][]byte, hcl.Diagnostics,
}
g := &generator{
- program: program,
- functionInvokes: map[string]*schema.Function{},
+ program: program,
+ functionInvokes: map[string]*schema.Function{},
+ emittedTypeImportSymbols: codegen.NewStringSet(),
}
g.Formatter = format.NewFormatter(g)
@@ -583,16 +585,21 @@ func reduceInputTypeFromArray(arrayType *schema.ArrayType) *schema.ArrayType {
}
}
-func collectResourceImports(resource *pcl.Resource) []string {
+func (g *generator) collectResourceImports(resource *pcl.Resource) []string {
imports := make([]string, 0)
pkg, module, name, _ := resource.DecomposeToken()
resourceImport := pulumiImport(pkg, module, name)
imports = append(imports, resourceImport)
if len(resource.Inputs) > 0 || hasCustomResourceOptions(resource) {
// import args type name
- argsTypeName := resourceArgsTypeName(resource)
- resourceArgsImport := pulumiImport(pkg, module, argsTypeName)
- imports = append(imports, resourceArgsImport)
+ argsTypeName := g.resourceArgsTypeName(resource)
+ alreadyFullyQualified := strings.Contains(argsTypeName, ".")
+ if !alreadyFullyQualified {
+ resourceArgsImport := pulumiImport(pkg, module, argsTypeName)
+ imports = append(imports, resourceArgsImport)
+ } else {
+ imports = append(imports, argsTypeName)
+ }
resourceProperties := typedResourceProperties(resource)
for _, inputProperty := range resource.Inputs {
inputType := resourceProperties[inputProperty.Name]
@@ -716,7 +723,7 @@ func (g *generator) collectImports(nodes []pcl.Node) []string {
case *pcl.Resource:
// collect resource imports
resource := node
- imports = append(imports, collectResourceImports(resource)...)
+ imports = append(imports, g.collectResourceImports(resource)...)
for _, prop := range resource.Inputs {
_, diags := model.VisitExpression(prop.Value, model.IdentityVisitor, visitFunctionCalls)
g.diagnostics = append(g.diagnostics, diags...)
@@ -745,17 +752,61 @@ func (g *generator) genPreamble(w io.Writer, nodes []pcl.Node) {
g.genImport(w, "com.pulumi.Context")
g.genImport(w, "com.pulumi.Pulumi")
g.genImport(w, "com.pulumi.core.Output")
+
+ imports := g.collectImports(nodes)
+
+ importMember := func(importDef string) string {
+ return importDef[strings.LastIndex(importDef, ".")+1:]
+ }
+
+ // a map to keep track of member names of imports
+ // e.g. ResourceType -> [
+ // com.pulumi.my_package.ResourceType
+ // com.pulumi.your_package.ResourceType
+ // ]
+ importsByMember := map[string]codegen.StringSet{}
+ for _, importDef := range imports {
+ importMember := importMember(importDef)
+ if _, ok := importsByMember[importMember]; !ok {
+ // initialize the set
+ importsByMember[importMember] = codegen.NewStringSet()
+ }
+
+ importsByMember[importMember].Add(importDef)
+ }
+
+ // if we have two imports such as the following:
+ // - com.pulumi.my_package.ResourceType
+ // - com.pulumi.your_package.ResourceType
+ // then we shouldn't generate import statements for them
+ // instead, we use the fully qualified name at the usage site
+ importsWithDuplicateName := codegen.NewStringSet()
+ emittedTypeImportSymbols := codegen.NewStringSet()
+ for _, importDef := range imports {
+ if len(importsByMember[importMember(importDef)]) > 1 {
+ importsWithDuplicateName.Add(importDef)
+ } else {
+ emittedTypeImportSymbols.Add(importDef)
+ }
+ }
+
// Write out the specific imports from used nodes
- for _, importDef := range g.collectImports(nodes) {
+ for _, importDef := range imports {
if strings.HasSuffix(importDef, ".String") {
// A type named `String` is being imported so we need to fully qualify our
// use of the built-in `java.lang.String` type to avoid the conflict.
javaStringType = "java.lang.String"
}
- g.genImport(w, importDef)
+ if emittedTypeImportSymbols.Has(importDef) {
+ // do not generate import statements for symbols with duplicate members
+ // because those will require full qualification at the usage site
+ g.genImport(w, importDef)
+ }
}
+ g.emittedTypeImportSymbols = emittedTypeImportSymbols
+
if containsFunctionCall("toJSON", nodes) {
// import static functions from the Serialization class
g.Fgen(w, "import static com.pulumi.codegen.internal.Serialization.*;\n")
@@ -817,7 +868,7 @@ func (g *generator) genPostamble(w io.Writer, _ []pcl.Node) {
}
// resourceTypeName computes the Java resource class name for the given resource.
-func resourceTypeName(resource *pcl.Resource) string {
+func (g *generator) resourceTypeName(resource *pcl.Resource) string {
// Compute the resource type from the Pulumi type token.
pkg, module, member, diags := resource.DecomposeToken()
contract.Assertf(len(diags) == 0, "failed to decompose resource token: %v", diags)
@@ -825,12 +876,19 @@ func resourceTypeName(resource *pcl.Resource) string {
member = "Provider"
}
+ if !g.emittedTypeImportSymbols.Has(pulumiImport(pkg, module, member)) {
+ // if we didn't emit an import statement for this symbol
+ // it means that there was a duplicate member name in the imports
+ // so return the fully qualified resource name
+ return pulumiImport(pkg, module, member)
+ }
+
return names.Title(member)
}
// resourceArgsTypeName computes the Java arguments class name for the given resource.
-func resourceArgsTypeName(r *pcl.Resource) string {
- return fmt.Sprintf("%sArgs", resourceTypeName(r))
+func (g *generator) resourceArgsTypeName(r *pcl.Resource) string {
+ return fmt.Sprintf("%sArgs", g.resourceTypeName(r))
}
// Returns the expression that should be emitted for a resource's "name" parameter given its base name
@@ -987,8 +1045,8 @@ func typedResourceProperties(resource *pcl.Resource) map[string]schema.Type {
}
func (g *generator) genResource(w io.Writer, resource *pcl.Resource) {
- resourceTypeName := resourceTypeName(resource)
- resourceArgs := resourceArgsTypeName(resource)
+ resourceTypeName := g.resourceTypeName(resource)
+ resourceArgs := g.resourceArgsTypeName(resource)
variableName := names.LowerCamelCase(names.MakeValidIdentifier(resource.Name()))
g.genTrivia(w, resource.Definition.Tokens.GetType(""))
for _, l := range resource.Definition.Tokens.GetLabels(nil) {