On this page
ProjectGen
Micronaut ProjectGen offers an API to generate JVM projects.
For this project, you can find a list of releases (with release notes) here:
The following dependency contains the core APIs to generate applications.
implementation("io.micronaut.projectgen:micronaut-projectgen-core")The following dependency contains utilities to test the generated application.
testImplementation("io.micronaut.projectgen:micronaut-projectgen-test")If you want to expose your project generator in an HTTP Server, add the following dependency:
implementation("io.micronaut.projectgen:micronaut-projectgen-http-server")A POST endpoint with a application/x-www-form-urlencoded content type allows you to download a ZIP file:
A POST endpoint with a application/x-www-form-urlencoded content type allows you to get a JSON payload with the visible features.
Adding the following dependency, a bean of type FeatureDiffer is registered:
implementation("io.github.java-diff-utils:java-diff-utils:4.15")Once you have a bean of type FeatureDiffer and
the io.micronaut.projectgen:micronaut-projectgen-http-server dependency the following endpoints which accept aPOST endpoints
with a application/x-www-form-urlencoded content type.
To generate a project, you create beans of type Feature.
Each feature has a unique identifier; you can offer a UI to allow users to select a feature.
To talk about the ProjectGen API, let’s discuss the following demo: a Hello World application with Maven and Gradle.
The project structure will be like this:
├── build.gradle.kts
├── gradle
│ └── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── mvnw
├── mvnw.bat
├── pom.xml
├── settings.gradle.kts
└── src
├── main
│ └── java
│ └── com
│ └── example
│ └── HelloWorld.java
└── test
└── java
└── com
└── example
└── HelloWorldTest.javaYou want to generate a build.gradle.kts with the following content:
plugins {
id("java")
id("application")
}
group = "io.micronaut.projectgen"
version = "1.0.0"
repositories {
mavenCentral()
}
dependencies {
testImplementation("org.junit.jupiter:junit-jupiter:5.10.2")
}
java {
sourceCompatibility = JavaVersion.VERSION_25
targetCompatibility = JavaVersion.VERSION_25
}
tasks.test {
useJUnitPlatform()
}
application {
mainClass.set("com.example.HelloWorld")
}and a pom.xml with the following content:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>io.micronaut.projectgen</groupId>
<artifactId>demo-project</artifactId>
<version>1.0.0</version>
<properties>
<maven.compiler.source>25</maven.compiler.source>
<maven.compiler.target>25</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.1.2</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<archive>
<manifest>
<mainClass>com.example.HelloWorld</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
</project>To generate a project, you need an implementation of the interface Options.
For example:
package io.micronaut.projectgen.demo;
import io.micronaut.projectgen.core.buildtools.BuildTool;
import io.micronaut.projectgen.core.buildtools.gradle.GradleDsl;
import io.micronaut.projectgen.core.options.GenericOptionsBuilder;
import io.micronaut.projectgen.core.options.JdkVersion;
import io.micronaut.projectgen.core.options.Options;
import java.util.List;
public final class OptionsFactory {
private OptionsFactory() {
}
public static Options create(List<String> features) {
return GenericOptionsBuilder.builder()
.name("demo")
.packageName("com.example")
.group("io.micronaut.projectgen")
.artifact("demo-project")
.version("1.0.0")
.features(features)
.buildTools(List.of(BuildTool.MAVEN, BuildTool.GRADLE))
.gradleDsl(GradleDsl.KOTLIN)
.java(JdkVersion.JDK_25)
.build();
}
}The following feature adds a dependency:
The following feature adds build properties:
package io.micronaut.projectgen.demo;
import io.micronaut.projectgen.core.buildtools.BuildProperties;
import io.micronaut.projectgen.core.feature.Feature;
import io.micronaut.projectgen.core.generator.GeneratorContext;
import io.micronaut.projectgen.core.options.Options;
import io.micronaut.projectgen.core.utils.OptionUtils;
import jakarta.inject.Singleton;
@Singleton
class MavenCompilerProperties implements Feature {
@Override
public String getName() {
return "maven-compiler-properties";
}
@Override
public boolean isVisible() {
return false;
}
@Override
public void apply(GeneratorContext generatorContext) {
Options options = generatorContext.getOptions();
if (OptionUtils.hasMavenBuildTool(options)) {
BuildProperties buildProperties = generatorContext.getRootModule()
.buildProperties();
String java = String.valueOf(options
.java()
.majorVersion());
buildProperties.put("maven.compiler.source", java);
buildProperties.put("maven.compiler.target", java);
}
}
}You can use ProjectGen GradlePlugin API to add Gradle Plugins to a project.
The following feature adds the Gradle Java Plugin.
package io.micronaut.projectgen.demo;
import io.micronaut.projectgen.core.buildtools.gradle.GradlePlugin;
import io.micronaut.projectgen.core.feature.Feature;
import io.micronaut.projectgen.core.generator.GeneratorContext;
import io.micronaut.projectgen.core.utils.OptionUtils;
import jakarta.inject.Singleton;
@Singleton
class GradleJavaPluginFeature implements Feature {
@Override
public String getName() {
return "gradle-plugin-java";
}
@Override
public boolean isVisible() {
return false;
}
@Override
public void apply(GeneratorContext generatorContext) {
if (OptionUtils.hasGradleBuildTool(generatorContext.getOptions())) {
int javaVersion = generatorContext.getOptions()
.java()
.majorVersion();
generatorContext.getRootModule()
.addBuildPlugin(GradlePlugin.builder()
.id("java")
.extension(String.format("""
java {
sourceCompatibility = JavaVersion.VERSION_%1$d
targetCompatibility = JavaVersion.VERSION_%1$d
}
tasks.test {
useJUnitPlatform()
}""", javaVersion))
.build());
}
}
}The following feature adds Gradle Application Plugin:
package io.micronaut.projectgen.demo;
import io.micronaut.projectgen.core.buildtools.gradle.GradlePlugin;
import io.micronaut.projectgen.core.feature.Feature;
import io.micronaut.projectgen.core.generator.GeneratorContext;
import io.micronaut.projectgen.core.utils.OptionUtils;
import jakarta.inject.Singleton;
@Singleton
class GradleApplicationPluginFeature implements Feature {
@Override
public String getName() {
return "gradle-plugin-application";
}
@Override
public boolean isVisible() {
return false;
}
@Override
public void apply(GeneratorContext generatorContext) {
if (OptionUtils.hasGradleBuildTool(generatorContext.getOptions())) {
generatorContext.getRootModule()
.addBuildPlugin(GradlePlugin.builder()
.id("application")
.extension("""
application {
mainClass.set("%s.HelloWorld")
}""".formatted(generatorContext.getOptions().packageName()))
.build());
}
}
}You can use ProjectGen MavenPlugin API to add Maven Plugins to a project.
The following feature adds the Maven Jar Plugin.
package io.micronaut.projectgen.demo;
import io.micronaut.projectgen.core.buildtools.maven.MavenPlugin;
import io.micronaut.projectgen.core.feature.Feature;
import io.micronaut.projectgen.core.generator.GeneratorContext;
import jakarta.inject.Singleton;
@Singleton
class MavenJarPluginFeature implements Feature {
@Override
public String getName() {
return "maven-jar-plugin";
}
@Override
public boolean isVisible() {
return false;
}
@Override
public void apply(GeneratorContext generatorContext) {
generatorContext.getRootModule().addBuildPlugin(MavenPlugin.builder()
.groupId("org.apache.maven.plugins")
.artifactId("maven-jar-plugin")
.extension(String.format("""
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<archive>
<manifest>
<mainClass>%s.HelloWorld</mainClass>
</manifest>
</archive>
</configuration>
</plugin>""", generatorContext.getOptions().packageName()))
.build());
}
}The following feature adds Maven Surefire Plugin:
package io.micronaut.projectgen.demo;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.projectgen.core.buildtools.maven.MavenPlugin;
import io.micronaut.projectgen.core.feature.Feature;
import io.micronaut.projectgen.core.generator.GeneratorContext;
import io.micronaut.projectgen.core.utils.OptionUtils;
import jakarta.inject.Singleton;
@Singleton
class MavenSurefirePlugin implements Feature {
private static final @NonNull MavenPlugin MAVEN_PLUGIN_SUREFIRE = MavenPlugin.builder() //
.groupId("org.apache.maven.plugins")
.artifactId("maven-surefire-plugin")
.version("3.1.2")
.build();
@Override
public boolean isVisible() {
return false;
}
@Override
public String getName() {
return "maven-surefire-plugin";
}
@Override
public void apply(GeneratorContext generatorContext) {
if (OptionUtils.hasMavenBuildTool(generatorContext.getOptions())) {
generatorContext.getRootModule().addBuildPlugin(MAVEN_PLUGIN_SUREFIRE);
}
}
}The following feature adds source code:
package io.micronaut.projectgen.demo;
import io.micronaut.projectgen.core.feature.Feature;
import io.micronaut.projectgen.core.generator.GeneratorContext;
import io.micronaut.projectgen.core.generator.ModuleContext;
import io.micronaut.projectgen.core.template.StringTemplate;
import jakarta.inject.Singleton;
@Singleton
class SampleCode implements Feature {
@Override
public String getName() {
return "sample-code";
}
@Override
public void apply(GeneratorContext generatorContext) {
ModuleContext module = generatorContext.getRootModule();
addHelloWorldJavaClass(module);
}
@Override
public boolean isVisible() {
return false;
}
private void addHelloWorldJavaClass(ModuleContext module) {
String path = "src/main/java/com/example/HelloWorld.java";
module.addTemplate("HelloWorld.java", new io.micronaut.projectgen.core.template.StringTemplate(path, """
package com.example;
public class HelloWorld {
public static void main(String[] args) {
System.out.println(hello());
}
public static String hello() {
return "Hello, World!";
}
}
"""));
}
}You can add files to the generated project. The previous example shows a String, but you can use the RockerWritable API to render files with Rocker Templates.
You can create beans of type DefaultFeature to be
automatically applied if the method DefaultFeature::shouldApply evaluates to true.
A common pattern is to define a DefaultFeature to act as the entry point of the project generation.
A feature can apply other features as shown below:
package io.micronaut.projectgen.demo;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.projectgen.core.buildtools.BuildProperties;
import io.micronaut.projectgen.core.buildtools.MavenCentral;
import io.micronaut.projectgen.core.buildtools.dependencies.Dependency;
import io.micronaut.projectgen.core.buildtools.gradle.GradlePlugin;
import io.micronaut.projectgen.core.buildtools.maven.MavenPlugin;
import io.micronaut.projectgen.core.feature.DefaultFeature;
import io.micronaut.projectgen.core.feature.Feature;
import io.micronaut.projectgen.core.feature.FeatureContext;
import io.micronaut.projectgen.core.generator.GeneratorContext;
import io.micronaut.projectgen.core.generator.ModuleContext;
import io.micronaut.projectgen.core.options.Options;
import io.micronaut.projectgen.core.utils.OptionUtils;
import jakarta.inject.Singleton;
import io.micronaut.projectgen.core.template.StringTemplate;
import java.util.Set;
@Singleton
class Root implements DefaultFeature {
private final GradleJavaPluginFeature gradleJavaPluginFeature;
private final GradleApplicationPluginFeature gradleApplicationPluginFeature;
private final MavenSurefirePlugin mavenSurefirePlugin;
private final MavenJarPluginFeature mavenJarPluginFeature;
private final SampleCode sampleCode;
private final MavenCompilerProperties mavenCompilerProperties;
Root(GradleJavaPluginFeature gradleJavaPluginFeature,
GradleApplicationPluginFeature gradleApplicationPluginFeature,
MavenSurefirePlugin mavenSurefirePlugin,
MavenJarPluginFeature mavenJarPluginFeature,
SampleCode sampleCode, MavenCompilerProperties mavenCompilerProperties) {
this.gradleJavaPluginFeature = gradleJavaPluginFeature;
this.gradleApplicationPluginFeature = gradleApplicationPluginFeature;
this.mavenSurefirePlugin = mavenSurefirePlugin;
this.mavenJarPluginFeature = mavenJarPluginFeature;
this.sampleCode = sampleCode;
this.mavenCompilerProperties = mavenCompilerProperties;
}
@Override
public void processSelectedFeatures(FeatureContext featureContext) {
featureContext.addFeature(gradleJavaPluginFeature);
featureContext.addFeature(gradleApplicationPluginFeature);
featureContext.addFeature(mavenSurefirePlugin);
featureContext.addFeature(mavenJarPluginFeature);
featureContext.addFeature(sampleCode);
featureContext.addFeature(mavenCompilerProperties);
}
@Override
public boolean shouldApply(Options options, Set<Feature> selectedFeatures) {
return true;
}
@Override
public String getName() {
return "entry-point";
}
@Override
public String getDescription() {
return "It generates a Hello World Maven and Gradle project";
}
@Override
public boolean isVisible() {
return false;
}
@Override
public void apply(GeneratorContext generatorContext) {
ModuleContext module = generatorContext.getRootModule();
Options options = generatorContext.getOptions();
populateModuleAttributes(module, options);
}
private void populateModuleAttributes(ModuleContext module, Options options) {
module.moduleAttributes()
.setCoordinate(Dependency.builder()
.groupId(options.group())
.artifactId(options.artifact())
.version(options.version())
.build());
}
}Generate projects in a command line application is a common use case of project generation. The following example shows a Micronaut CLI application which generates a project:
The following controller renders an HTML with a button that downloads a ZIP file with a project:
package io.micronaut.projectgen.demo;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.annotation.Produces;
@Controller
class HomeController {
@Get
@Produces(MediaType.TEXT_HTML)
String index() {
return """
<!DOCTYPE html>
<html>
<head>
<title>Download Project</title>
</head>
<body>
<form action="/api/v1/download/zip" method="POST">
<fieldset>
<input type="hidden" name="name" value="demo"/>
<input type="hidden" name="packageName" value="com.example"/>
<input type="hidden" name="group" value="io.micronaut.projectgen"/>
<input type="hidden" name="artifact" value="demo-project"/>
<input type="hidden" name="version" value="1.0.0"/>
<input type="hidden" name="build" value="MAVEN,GRADLE"/>
<input type="hidden" name="gradleDsl" value="KOTLIN"/>
<input type="hidden" name="java" value="21"/>
<input type="checkbox" name="features" value="hello-world-test"/> Generate Test<br/>
<input type="submit" value="Download" />
</fieldset>
</form>
</body>
</html>
""";
}
}|
Note
|
To render server-side HTML, use Micronaut Views. |
package io.micronaut.projectgen.demo;
import io.micronaut.http.HttpRequest;
import io.micronaut.http.MediaType;
import io.micronaut.http.client.BlockingHttpClient;
import io.micronaut.http.client.HttpClient;
import io.micronaut.http.client.annotation.Client;
import io.micronaut.projectgen.core.buildtools.BuildTool;
import io.micronaut.projectgen.core.buildtools.gradle.GradleDsl;
import io.micronaut.projectgen.core.options.JdkVersion;
import io.micronaut.test.extensions.junit5.annotation.MicronautTest;
import org.json.JSONException;
import org.junit.jupiter.api.Test;
import org.skyscreamer.jsonassert.JSONAssert;
import org.skyscreamer.jsonassert.JSONCompareMode;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@MicronautTest
class FeaturesTest {
@Test
void testFeaturesEndpoint(@Client("/") HttpClient httpClient) throws JSONException {
BlockingHttpClient client = httpClient.toBlocking();
Map<String, Object> form = new HashMap<>();
form.put("name", "demo");
form.put("packageName", "com.example");
form.put("group", "io.micronaut.projectgen");
form.put("artifact", "demo-project");
form.put("version", "1.0.0");
form.put("features", "hello-world-test");
form.put("build", List.of(BuildTool.MAVEN, BuildTool.GRADLE));
form.put("gradleDsl", GradleDsl.KOTLIN);
form.put("java", JdkVersion.JDK_21);
HttpRequest<?> request = HttpRequest.POST("/api/v1/features", form)
.contentType(MediaType.APPLICATION_FORM_URLENCODED_TYPE);
String json = assertDoesNotThrow(() -> client.retrieve(request));
assertNotNull(json);
String expected = """
{
"features":
[
{
"name":"hello-world-test",
"title":"Add Test",
"description":"Add Unit tests with Junit5 dependency",
"preview":false,
"community":false
}
]
}
""";
JSONAssert.assertEquals(
expected, json, JSONCompareMode.LENIENT);
}
}You can test the project generation easily:
package io.micronaut.projectgen.demo;
import io.micronaut.projectgen.core.buildtools.BuildTool;
import io.micronaut.projectgen.core.buildtools.Scope;
import io.micronaut.projectgen.core.io.PreviewGenerator;
import io.micronaut.projectgen.core.options.Options;
import io.micronaut.projectgen.test.BuildTestVerifier;
import io.micronaut.projectgen.test.GradleBuildTestVerifier;
import io.micronaut.projectgen.test.MavenBuildTestVerifier;
import io.micronaut.test.extensions.junit5.annotation.MicronautTest;
import org.junit.jupiter.api.Test;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
@MicronautTest(startApplication = false)
class ProjectGenTest {
@Test
void testProjectGeneration(PreviewGenerator previewGenerator) throws Exception {
Options options = OptionsFactory.create(Collections.emptyList());
Map<String, String> project = previewGenerator.generate(options);
assertTrue(project.containsKey("mvnw"));
assertTrue(project.containsKey("mvnw.bat"));
assertTrue(project.containsKey(".mvn/wrapper/maven-wrapper.jar"));
assertTrue(project.containsKey(".mvn/wrapper/maven-wrapper.properties"));
assertTrue(project.containsKey("pom.xml"));
assertTrue(project.containsKey("src/main/java/com/example/HelloWorld.java"));
assertFalse(project.containsKey("src/test/java/com/example/HelloWorldTest.java"));
String pom = project.get("pom.xml");
BuildTestVerifier verifier = new MavenBuildTestVerifier(pom, options.language());
assertEquals("25", verifier.getProperty("maven.compiler.source"));
assertEquals("25", verifier.getProperty("maven.compiler.target"));
assertTrue(verifier.hasBuildPlugin("org.apache.maven.plugins", "maven-jar-plugin"));
String buildGradleKts = project.get("build.gradle.kts");
verifier = new GradleBuildTestVerifier(buildGradleKts, BuildTool.GRADLE, options.language(), options.testFramework());
assertFalse(verifier.hasDependency("org.junit.jupiter", "junit-jupiter", Scope.TEST));
assertTrue(verifier.hasBuildPlugin("java"));
assertTrue(verifier.hasBuildPlugin("application"));
assertTrue(project.containsKey("settings.gradle.kts"));
String settings = project.get("settings.gradle.kts");
assertEquals("""
rootProject.name="demo"
""", settings);
assertTrue(project.containsKey("gradlew"));
assertTrue(project.containsKey("gradlew.bat"));
assertTrue(project.containsKey("gradle/wrapper/gradle-wrapper.jar"));
assertTrue(project.containsKey("gradle/wrapper/gradle-wrapper.properties"));
String gradleWrapperProperties = project.get("gradle/wrapper/gradle-wrapper.properties");
assertTrue(gradleWrapperProperties.contains("9.5.0"));
options = OptionsFactory.create(List.of("hello-world-test"));
project = previewGenerator.generate(options);
buildGradleKts = project.get("build.gradle.kts");
assertTrue(project.containsKey("src/test/java/com/example/HelloWorldTest.java"));
verifier = new GradleBuildTestVerifier(buildGradleKts, BuildTool.GRADLE, options.language(), options.testFramework());
assertTrue(verifier.hasDependency("org.junit.jupiter", "junit-jupiter", Scope.TEST), buildGradleKts);
assertTrue(project.containsKey("projectgen.properties"));
String props = project.get("projectgen.properties");
assertEquals("""
artifact=demo-project
java=JDK_25
buildTools[0]=maven
buildTools[1]=gradle
gradleDsl=KOTLIN
name=demo
packageName=com.example
version=1.0.0
group=io.micronaut.projectgen
""", props);
}
}You can find the source code of this project in this repository: