diff --git a/CHANGELOG.md b/CHANGELOG.md
index 726f59e8ff..a60f12c50f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,7 +1,8 @@
## Unreleased
-- Update to pulumi-java v0.12.0 #3025 (https://github.com/pulumi/pulumi-kubernetes/pull/3025)
+- Update to pulumi-java v0.12.0 (https://github.com/pulumi/pulumi-kubernetes/pull/3025)
- Fixed a panic that occurs when diffing Job resources containing `replaceUnready` annotations and an unreachable cluster connection. (https://github.com/pulumi/pulumi-kubernetes/pull/3024)
+- CustomResource for Java SDK (https://github.com/pulumi/pulumi-kubernetes/pull/3020)
## 4.12.0 (May 21, 2024)
diff --git a/Makefile b/Makefile
index fe1b0ee038..077033fd98 100644
--- a/Makefile
+++ b/Makefile
@@ -97,7 +97,8 @@ python_sdk::
java_sdk:: PACKAGE_VERSION := $(shell pulumictl convert-version --language generic -v "$(VERSION_GENERIC)")
java_sdk:: bin/pulumi-java-gen
- $(WORKING_DIR)/bin/$(JAVA_GEN) generate --schema $(SCHEMA_FILE) --out sdk/java --build gradle-nexus
+ $(WORKING_DIR)/bin/$(JAVA_GEN) generate --schema $(SCHEMA_FILE) --overlay provider/pkg/gen/java-templates \
+ --out sdk/java --build gradle-nexus
cd ${PACKDIR}/java/ && \
echo "module fake_java_module // Exclude this directory from Go tools\n\ngo 1.17" > go.mod && \
gradle --console=plain build
diff --git a/examples/java/customresource/Pulumi.yaml b/examples/java/customresource/Pulumi.yaml
new file mode 100644
index 0000000000..57f6cbec56
--- /dev/null
+++ b/examples/java/customresource/Pulumi.yaml
@@ -0,0 +1,7 @@
+name: example
+runtime: java
+description: An example for com.pulumi.kubernetes.apiextensions.CustomResource
+config:
+ pulumi:tags:
+ value:
+ pulumi:template: ""
diff --git a/examples/java/customresource/pom.xml b/examples/java/customresource/pom.xml
new file mode 100644
index 0000000000..7b3f51ab81
--- /dev/null
+++ b/examples/java/customresource/pom.xml
@@ -0,0 +1,97 @@
+
+
+ 4.0.0
+
+ com.pulumi
+ issue-2787-javaa
+ 1.0-SNAPSHOT
+
+
+ UTF-8
+ 11
+ 11
+ 11
+ myproject.App
+
+
+
+
+
+ com.pulumi
+ pulumi
+ (,1.0]
+
+
+ com.pulumi
+ kubernetes
+ (,5.0]
+
+
+ com.google.code.findbugs
+ jsr305
+ 3.0.2
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.2.2
+
+
+
+ true
+ ${mainClass}
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+ 3.3.0
+
+
+
+ true
+ ${mainClass}
+
+
+
+ jar-with-dependencies
+
+
+
+
+ make-my-jar-with-dependencies
+ package
+
+ single
+
+
+
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+ 3.0.0
+
+ ${mainClass}
+ ${mainArgs}
+
+
+
+ org.apache.maven.plugins
+ maven-wrapper-plugin
+ 3.1.0
+
+ 3.8.5
+
+
+
+
+
diff --git a/examples/java/customresource/src/main/java/myproject/App.java b/examples/java/customresource/src/main/java/myproject/App.java
new file mode 100644
index 0000000000..8a87f4978c
--- /dev/null
+++ b/examples/java/customresource/src/main/java/myproject/App.java
@@ -0,0 +1,299 @@
+package myproject;
+
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+
+import javax.annotation.Nullable;
+
+import com.pulumi.Pulumi;
+import com.pulumi.core.Output;
+import com.pulumi.core.annotations.CustomType;
+import com.pulumi.core.annotations.Export;
+import com.pulumi.core.annotations.Import;
+import com.pulumi.kubernetes.apiextensions.CustomResource;
+import com.pulumi.kubernetes.apiextensions.CustomResourceArgs;
+import com.pulumi.kubernetes.apiextensions.CustomResourceArgsBase;
+import com.pulumi.kubernetes.meta.v1.inputs.ObjectMetaArgs;
+
+/**
+ * Demonstration of working with custom resources in the Java SDK.
+ * Prerequisites:
+ * - cert-manager v1.x installed in your Kubernetes cluster.
+ *
+ * This example deploys a cert-manager.io/v1 Issuer to your Kubernetes cluster.
+ * Two different ways of defining the Issuer are demonstrated:
+ * - Using the CustomResource class directly.
+ * - Using a custom Issuer class that extends CustomResource, to provide a
+ * type-safe API.
+ */
+public class App {
+ public static void main(String[] args) {
+ Pulumi.run(ctx -> {
+ var issuer1 = new CustomResource("issuer1", CustomResourceArgs.builder()
+ .apiVersion("cert-manager.io/v1")
+ .kind("Issuer")
+ .metadata(ObjectMetaArgs.builder().build())
+ .build());
+ ctx.export("issuer1_name", issuer1.metadata().applyValue(s -> s.name()));
+
+ var issuer2 = new Issuer("issuer2", IssuerArgs.builder()
+ .metadata(ObjectMetaArgs.builder().build())
+ .spec(Inputs.IssuerSpecArgs.builder()
+ .selfSigned(Inputs.SelfSignedArgs.builder().build())
+ .build())
+ .build());
+
+ ctx.export("issuer2_name", issuer2.metadata().applyValue(s -> s.name()));
+ ctx.export("issuer2_selfsigned", issuer2.spec().applyValue(s -> s.selfSigned().isPresent()));
+ });
+ }
+}
+
+class Issuer extends CustomResource {
+ /**
+ * The spec of the Issuer.
+ */
+ @Export(name = "spec", refs = { Outputs.IssuerSpec.class }, tree = "[0]")
+ private Output spec;
+
+ public Output spec() {
+ return this.spec;
+ }
+
+ public Issuer(String name, @Nullable IssuerArgs args) {
+ super(name, makeArgs(args));
+ }
+
+ public Issuer(String name, @Nullable IssuerArgs args,
+ @Nullable com.pulumi.resources.CustomResourceOptions options) {
+ super(name, makeArgs(args), options);
+ }
+
+ protected Issuer(String name, Output id,
+ @Nullable com.pulumi.resources.CustomResourceOptions options) {
+ super(name, "cert-manager.io/v1", "Issuer", id, options);
+ }
+
+ private static IssuerArgs makeArgs(@Nullable IssuerArgs args) {
+ var builder = args == null ? IssuerArgs.builder() : IssuerArgs.builder(args);
+ return builder
+ .apiVersion("cert-manager.io/v1")
+ .kind("Issuer")
+ .build();
+ }
+
+ public static Issuer get(String name, Output id,
+ @Nullable com.pulumi.resources.CustomResourceOptions options) {
+ return new Issuer(name, id, options);
+ }
+}
+
+class IssuerArgs extends CustomResourceArgsBase {
+ /**
+ * The spec of the Issuer.
+ */
+ @Import(name = "spec", required = true)
+ @Nullable
+ private Output spec;
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static Builder builder(IssuerArgs defaults) {
+ return new Builder(defaults);
+ }
+
+ static class Builder extends CustomResourceArgsBase.Builder {
+ public Builder() {
+ super(new IssuerArgs());
+ }
+
+ public Builder(IssuerArgs defaults) {
+ super(new IssuerArgs(), defaults);
+ }
+
+ public Builder spec(@Nullable Output spec) {
+ $.spec = spec;
+ return this;
+ }
+
+ public Builder spec(Inputs.IssuerSpecArgs spec) {
+ return spec(Output.of(spec));
+ }
+
+ @Override
+ protected void copy(IssuerArgs args) {
+ super.copy(args);
+ $.spec = args.spec;
+ }
+ }
+}
+
+class Inputs {
+ public static final class IssuerSpecArgs extends com.pulumi.resources.ResourceArgs {
+
+ public static final IssuerSpecArgs Empty = new IssuerSpecArgs();
+
+ @Import(name = "selfSigned")
+ private @Nullable Output selfSigned;
+
+ public Optional