aspire
aspire copied to clipboard
Extending Aspire orchestration capability to Java-based apps
Background and Motivation
.NET Aspire, as an orchestration engine to build a clound-native application, is capable of bringing other type of applications written in different language like Java (Spring Boot for example). The current API offers both .AddContainer(...) and .AddExecutable(...) for the generic purpose.
However, it would be great if we can provide Java-specific extension methods like AddJavaApp(...) for generic Java app and AddSpringApp(...) for Spring-focused app, as we provide AddNodeApp(...) and AddNpmApp(...).
By doing so, we can wrap Java-specific arguments pre-built so that devs only focus on passing arguments they really need.
I've PoC'd for this suggestion here: https://github.com/aliencube/aspire-contribs/tree/main/src/Aspire.Contribs.Hosting.Java
Proposed API
I've got two API suggestions – one based on ExecutableResource and the other based on ContainerResource.
ExecutableResource-based API
- It's good for the Java app is in the same repository.
- It's easily built and orchestrated with the existing
PublishAsDockerfile(...)method. - It's not easy to handle the Java app outside the .NET Aspire-based repository.
public static class JavaAppHostingExtension
{
// Add Java app
public static IResourceBuilder<JavaAppExecutableResource> AddJavaApp(
this IDistributedApplicationBuilder builder,
string name,
string workingDirectory,
JavaAppExecutableResourceOptions options)
{
// Add Spring app
public static IResourceBuilder<JavaAppExecutableResource> AddSpringApp(
this IDistributedApplicationBuilder builder,
string name,
string workingDirectory,
JavaAppExecutableResourceOptions options)
{
ContainerResource-based API
- It's good for the Java app is outside the .NET Aspire-based repository. I think this is mostly the case.
- It's good for existing Java app container images already published to a container registry.
public static class JavaAppHostingExtension
{
// Add Java app
public static IResourceBuilder<JavaAppContainerResource> AddJavaApp(
this IDistributedApplicationBuilder builder,
string name,
JavaAppContainerResourceOptions options)
{
// Add Spring app
public static IResourceBuilder<JavaAppContainerResource> AddSpringApp(
this IDistributedApplicationBuilder builder,
string name,
JavaAppContainerResourceOptions options)
{
I'd also like to introduce the options pattern here to pass some common options like:
// JavaAppExecutableResourceOptions
public class JavaAppExecutableResourceOptions
{
public string? ApplicationName { get; set; } = "target/app.jar";
public int Port { get; set; } = 8080;
public string? OtelAgentPath { get; set; } = null;
public string[]? Args { get; set; } = null;
}
// JavaAppContainerResourceOptions
public class JavaAppContainerResourceOptions
{
public string? ContainerRegistry { get; set; } = "docker.io";
public string? ContainerImageName { get; set; }
public string ContainerImageTag { get; set; } = "latest";
public int Port { get; set; } = 8080;
public int TargetPort { get; set; } = 8080;
public string? OtelAgentPath { get; set; } = null;
public string[]? Args { get; set; } = null;
}
NOTE that the OtelAgentPath should be provided for Java app to be OpenTelemetry-able.
Therefore, based on the options instance, devs call the same method, AddSpringApp(...), and the method uses either container or executable.
Usage Examples
With this API suggestion, devs can orchestrate like this:
var containerapp = builder.AddSpringApp(
"containerapp",
new JavaAppContainerResourceOptions()
{
ContainerRegistry = "docker.io",
ContainerImageName = "my-org/my-spring-app",
Port = 8080,
TargetPort = 8080,
OtelAgentPath = "/agents"
});
var executableapp = builder.AddSpringApp(
"executableapp",
workingDirectory: "../Aspire.Contribs.Spring.Maven",
new JavaAppExecutableResourceOptions()
{
ApplicationName = "target/spring-maven-0.0.1-SNAPSHOT.jar",
Port = 8085,
OtelAgentPath = "../../agents",
});
var webapp = builder.AddProject<Projects.Aspire_Contribs_WebApp>("webapp")
.WithExternalHttpEndpoints()
.WithReference(containerapp)
.WithReference(executableapp)
.WithReference(apiapp);
Alternative Designs
Risks
I think we need to unify the resource types:
- I want to be able to run a spring app as a java process
- I want to be able to run a spring app in a container
- I want to publish a spring app as a container
We have a pattern for 1 and 3 today, this new resource type is number 2. I wonder if there's a way we can represent number 2 without a split resource type.
The other thing I would mention is that that the options object is a bit different from our existing builder pattern API (not sure if it's a bad thing). Feels similar to https://github.com/dotnet/aspire/issues/4472 for .NET project resources. There needs to be a way to say, use this endpoint name as the spring boot server port.
I'd like @mitchdenny's take on this.
- I want to be able to run a spring app as a java process
- I want to be able to run a spring app in a container
- I want to publish a spring app as a container
Currently, the .AddExecutable(...) method supports the pattern 1, and the .AddContainer(...) method supports the pattern 3. For the pattern 2, it's already published to a container registry, and we can simply refer it. My gut feeling is that both pattern 2 and 3 can be consolidated, as long as the pattern 2 has a Dockerfile. Let me double check on my end.
The other thing I would mention is that that the options object is a bit different from our existing builder pattern API (not sure if it's a bad thing). Feels similar to https://github.com/dotnet/aspire/issues/4472 for .NET project resources. There needs to be a way to say, use this endpoint name as the spring boot server port.
The introduction of the ...Options class is merely to simplify the number of commonly used arguments.
For the executable (pattern 1), it always uses server port and OTEL agent JAR file path with slight variations like port number value and agent path value.
For the container (pattern 3), yeah, I kind of agree it depends on the existing Dockerfile structure, it may not be necessary. But for the pattern 2, it may be necessary. Let me take a look.
I did a few experiments for the pattern 2 and 3 and it really depends on how the container image has been built. Therefore, providing the ...Options class as a collection of common values wouldn't make sense.
For the pattern 1, I also think it's reasonable to leave devs to pass their arguments including the OTEL agent JAR file path, instead of using that ...Options class.
Thinking of the pattern 3, though, it's only valid if the Spring app is under the same repo where .AppHost project is located.
I'd like @mitchdenny's take on this.
I'm starting to think that we have a WithDockerfile extension method which works with IResourceBuilder<ExecutableResource> which effectively results in a container resource being created in the manifest (impacts run mode as well).
Now, we can add Python app to .NET Aspire
#4142
Can Java follow the similar process?
@justinyoo absolutely!
Now, we can add Python app to .NET Aspire
#4142
Can Java follow the similar process?
+1 for adding Java supports
@justinyoo were you interested at taking a run at this?
I can help testing this feature/functionality. In Java/Spring Boot/Quarkus space for a long time, so can help think test strategies, execute and implement them. let me know.
This moved to the community toolkit https://github.com/CommunityToolkit/Aspire