From fd1fc1bd909d579565591911e65298c3d0aafcf9 Mon Sep 17 00:00:00 2001
From: Cause Chung
Date: Mon, 16 Jun 2025 21:31:55 -0400
Subject: [PATCH 01/17] Add maven plugin module
---
maven-plugin/pom.xml | 75 +++++++++++++++++++
.../sharedtype/maven/SharedTypeGenMojo.java | 19 +++++
mount-tmpfs.sh | 1 +
pom.xml | 2 +
.../writer/TemplateTypeFileWriter.java | 2 +-
5 files changed, 98 insertions(+), 1 deletion(-)
create mode 100644 maven-plugin/pom.xml
create mode 100644 maven-plugin/src/main/java/online/sharedtype/maven/SharedTypeGenMojo.java
diff --git a/maven-plugin/pom.xml b/maven-plugin/pom.xml
new file mode 100644
index 00000000..596a6b7d
--- /dev/null
+++ b/maven-plugin/pom.xml
@@ -0,0 +1,75 @@
+
+ 4.0.0
+
+ online.sharedtype
+ sharedtype-parent
+ 0.13.0-SNAPSHOT
+ ../pom.xml
+
+
+ sharedtype-maven-plugin
+ 0.13.0-SNAPSHOT
+ maven-plugin
+ SharedType Maven Plugin
+
+
+ 3.9.5
+ 3.15.1
+
+
+
+
+ org.apache.maven
+ maven-plugin-api
+ ${maven.version}
+ provided
+
+
+ org.apache.maven
+ maven-core
+ ${maven.version}
+ provided
+
+
+ org.apache.maven.plugin-tools
+ maven-plugin-annotations
+ ${maven-plugin-tools.version}
+ provided
+
+
+ javax.inject
+ javax.inject
+ 1
+ provided
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-plugin-plugin
+ ${maven-plugin-tools.version}
+
+
+ help-mojo
+
+
+ helpmojo
+
+
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-plugin-report-plugin
+ ${maven-plugin-tools.version}
+
+
+
+
diff --git a/maven-plugin/src/main/java/online/sharedtype/maven/SharedTypeGenMojo.java b/maven-plugin/src/main/java/online/sharedtype/maven/SharedTypeGenMojo.java
new file mode 100644
index 00000000..5a800bde
--- /dev/null
+++ b/maven-plugin/src/main/java/online/sharedtype/maven/SharedTypeGenMojo.java
@@ -0,0 +1,19 @@
+package online.sharedtype.maven;
+
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.plugins.annotations.Mojo;
+
+import javax.inject.Inject;
+
+@Mojo(name = "gen")
+public final class SharedTypeGenMojo extends AbstractMojo {
+ @Inject
+ private MavenSession session;
+
+ @Override
+ public void execute() throws MojoExecutionException, MojoFailureException {
+ }
+}
diff --git a/mount-tmpfs.sh b/mount-tmpfs.sh
index fd66ae3e..21dbeb82 100755
--- a/mount-tmpfs.sh
+++ b/mount-tmpfs.sh
@@ -22,6 +22,7 @@ mountTmpfs "$DIR/processor/target" 64M
mountTmpfs "$DIR/it/java17/target" 64M
mountTmpfs "$DIR/it/java8/target" 64M
mountTmpfs "$DIR/e2e/target" 64M
+mountTmpfs "$DIR/maven-plugin/target" 64M
mountTmpfs "$MAVEN_REPO_DIR" 64M
mountTmpfs "$DIR/client-test/rust/target" 512M
mountTmpfs "$DIR/client-test/typescript/dist" 32M
diff --git a/pom.xml b/pom.xml
index 0be3d344..4aabfce7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -43,6 +43,7 @@
UTF-8
+ UTF-8
8
8
21
@@ -264,6 +265,7 @@
annotation
processor
it
+ maven-plugin
diff --git a/processor/src/main/java/online/sharedtype/processor/writer/TemplateTypeFileWriter.java b/processor/src/main/java/online/sharedtype/processor/writer/TemplateTypeFileWriter.java
index fd38c69a..9e764eec 100644
--- a/processor/src/main/java/online/sharedtype/processor/writer/TemplateTypeFileWriter.java
+++ b/processor/src/main/java/online/sharedtype/processor/writer/TemplateTypeFileWriter.java
@@ -42,7 +42,7 @@ public void write(List typeDefs) throws IOException {
for (TypeDef typeDef : typeDefs) {
TypeDef duplicate = typeDef instanceof ConstantNamespaceDef ? null : simpleNames.get(typeDef.simpleName()); // todo: split class/enum and constant duplication checks
if (duplicate != null) {
- ctx.warn("Duplicate names found: %s and %s, which is not allowed in output code." +
+ ctx.warn("Duplicate names found: %s and %s, which may not be valid in output code." +
" You may use @SharedType(name=\"...\") to rename a type.", typeDef.qualifiedName(), duplicate.qualifiedName());
}
simpleNames.put(typeDef.simpleName(), typeDef);
From 669b27640fe83341f20cb23a75772587c593e9f1 Mon Sep 17 00:00:00 2001
From: Cause Chung
Date: Sat, 21 Jun 2025 12:03:59 -0400
Subject: [PATCH 02/17] Get maven compiler
---
.../sharedtype/maven/SharedTypeGenMojo.java | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/maven-plugin/src/main/java/online/sharedtype/maven/SharedTypeGenMojo.java b/maven-plugin/src/main/java/online/sharedtype/maven/SharedTypeGenMojo.java
index 5a800bde..7196c737 100644
--- a/maven-plugin/src/main/java/online/sharedtype/maven/SharedTypeGenMojo.java
+++ b/maven-plugin/src/main/java/online/sharedtype/maven/SharedTypeGenMojo.java
@@ -5,15 +5,31 @@
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.project.MavenProject;
import javax.inject.Inject;
+import javax.tools.JavaCompiler;
+import javax.tools.ToolProvider;
+import java.util.ServiceLoader;
@Mojo(name = "gen")
public final class SharedTypeGenMojo extends AbstractMojo {
+ private static final String COMPILER_ID = "javac";
@Inject
private MavenSession session;
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
+ MavenProject project = session.getCurrentProject();
+ JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
+
+ }
+
+ private JavaCompiler getJavaCompiler() {
+ JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
+ if (compiler != null) {
+ return compiler;
+ }
+ throw new IllegalStateException("Java compiler not found.");
}
}
From df24fbcc4fed2ec2202396f816adda8753cf7e77 Mon Sep 17 00:00:00 2001
From: Cause Chung
Date: Sat, 21 Jun 2025 18:23:28 -0400
Subject: [PATCH 03/17] Basic execution flow
---
maven-plugin/pom.xml | 5 ++
.../sharedtype/maven/DependencyResolver.java | 46 ++++++++++++
.../sharedtype/maven/SharedTypeGenMojo.java | 72 +++++++++++++++++--
.../sharedtype/maven/SourceFileGatherer.java | 23 ++++++
pom.xml | 5 ++
processor/src/main/java/module-info.java | 4 +-
...ava => SharedTypeAnnotationProcessor.java} | 2 +-
...=> SharedTypeAnnotationProcessorTest.java} | 4 +-
8 files changed, 150 insertions(+), 11 deletions(-)
create mode 100644 maven-plugin/src/main/java/online/sharedtype/maven/DependencyResolver.java
create mode 100644 maven-plugin/src/main/java/online/sharedtype/maven/SourceFileGatherer.java
rename processor/src/main/java/online/sharedtype/processor/{AnnotationProcessorImpl.java => SharedTypeAnnotationProcessor.java} (98%)
rename processor/src/test/java/online/sharedtype/processor/{AnnotationProcessorImplTest.java => SharedTypeAnnotationProcessorTest.java} (94%)
diff --git a/maven-plugin/pom.xml b/maven-plugin/pom.xml
index 596a6b7d..eb0a83b3 100644
--- a/maven-plugin/pom.xml
+++ b/maven-plugin/pom.xml
@@ -18,6 +18,11 @@
+
+ ${project.groupId}
+ sharedtype-ap
+ ${project.version}
+
org.apache.maven
maven-plugin-api
diff --git a/maven-plugin/src/main/java/online/sharedtype/maven/DependencyResolver.java b/maven-plugin/src/main/java/online/sharedtype/maven/DependencyResolver.java
new file mode 100644
index 00000000..2f1d639c
--- /dev/null
+++ b/maven-plugin/src/main/java/online/sharedtype/maven/DependencyResolver.java
@@ -0,0 +1,46 @@
+package online.sharedtype.maven;
+
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.project.MavenProject;
+import org.eclipse.aether.RepositorySystem;
+import org.eclipse.aether.artifact.DefaultArtifact;
+import org.eclipse.aether.collection.CollectRequest;
+import org.eclipse.aether.graph.Dependency;
+import org.eclipse.aether.resolution.DependencyRequest;
+import org.eclipse.aether.resolution.DependencyResult;
+
+import java.io.File;
+import java.util.List;
+import java.util.stream.Collectors;
+
+
+final class DependencyResolver {
+ private static final String VERSION = DependencyResolver.class.getPackage().getImplementationVersion();
+ private final RepositorySystem repositorySystem;
+ private final MavenSession session;
+ private final MavenProject project;
+
+ DependencyResolver(RepositorySystem repositorySystem, MavenSession session, MavenProject project) {
+ this.repositorySystem = repositorySystem;
+ this.session = session;
+ this.project = project;
+ }
+
+ List getProcessorDependencies() throws MojoExecutionException {
+ String artifactCoordinate = String.format("online.sharedtype:sharedtype-processor:%s", VERSION);
+ try {
+ Dependency dependency = new Dependency(new DefaultArtifact(artifactCoordinate), null);
+ CollectRequest collectRequest =
+ new CollectRequest(dependency, project.getRemoteProjectRepositories());
+ DependencyRequest dependencyRequest = new DependencyRequest();
+ dependencyRequest.setCollectRequest(collectRequest);
+ DependencyResult dependencyResult = repositorySystem.resolveDependencies(session.getRepositorySession(), dependencyRequest);
+ return dependencyResult.getArtifactResults().stream()
+ .map(resolved -> resolved.getArtifact().getFile())
+ .collect(Collectors.toList());
+ } catch (Exception e) {
+ throw new MojoExecutionException(String.format("Failed to resolve '%s'", artifactCoordinate), e);
+ }
+ }
+}
diff --git a/maven-plugin/src/main/java/online/sharedtype/maven/SharedTypeGenMojo.java b/maven-plugin/src/main/java/online/sharedtype/maven/SharedTypeGenMojo.java
index 7196c737..812511f1 100644
--- a/maven-plugin/src/main/java/online/sharedtype/maven/SharedTypeGenMojo.java
+++ b/maven-plugin/src/main/java/online/sharedtype/maven/SharedTypeGenMojo.java
@@ -1,35 +1,93 @@
package online.sharedtype.maven;
+import online.sharedtype.processor.SharedTypeAnnotationProcessor;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
+import org.eclipse.aether.RepositorySystem;
import javax.inject.Inject;
import javax.tools.JavaCompiler;
+import javax.tools.JavaFileObject;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.StandardLocation;
import javax.tools.ToolProvider;
-import java.util.ServiceLoader;
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.nio.charset.UnsupportedCharsetException;
+import java.nio.file.FileVisitOption;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Set;
@Mojo(name = "gen")
public final class SharedTypeGenMojo extends AbstractMojo {
- private static final String COMPILER_ID = "javac";
- @Inject
+ private static final Set DEFAULT_COMPILER_OPTIONS = Collections.singleton("-proc:only");
+ private @Inject RepositorySystem repositorySystem;
+ @Parameter(defaultValue = "${session}", readonly = true, required = true)
private MavenSession session;
+ @Parameter(defaultValue = "${project}", readonly = true, required = true)
+ private MavenProject project;
+ @Parameter(property = "encoding", defaultValue = "${project.build.sourceEncoding}")
+ private String encoding;
+ @Parameter(defaultValue = "${project.build.outputDirectory}", required = true, readonly = true)
+ private Path outputDirectory;
+ @Parameter(defaultValue = "generated-sources", required = false, readonly = true)
+ private Path generatedSourcesDirectory;
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
- MavenProject project = session.getCurrentProject();
- JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
+ JavaCompiler compiler = getJavaCompiler();
+// DependencyResolver dependencyResolver = new DependencyResolver(repositorySystem, session, project);
+ StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, charset(encoding));
+ try {
+// fileManager.setLocation(StandardLocation.ANNOTATION_PROCESSOR_PATH, dependencyResolver.getProcessorDependencies());
+ fileManager.setLocation(StandardLocation.SOURCE_OUTPUT, Collections.singleton(outputDirectory.resolve(generatedSourcesDirectory).toFile()));
+ } catch (IOException e) {
+ throw new MojoExecutionException(e);
+ }
+ Iterable extends JavaFileObject> sources = fileManager.getJavaFileObjectsFromFiles(walkAllSourceFiles());
+ JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, DEFAULT_COMPILER_OPTIONS, null, sources);
+ task.setProcessors(Collections.singleton(new SharedTypeAnnotationProcessor()));
+ }
+ private List walkAllSourceFiles() throws MojoExecutionException {
+ SourceFileGatherer gatherer = new SourceFileGatherer();
+ for (String compileSourceRoot : project.getCompileSourceRoots()) {
+ try {
+ Files.walkFileTree(Paths.get(compileSourceRoot), EnumSet.of(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE, gatherer);
+ } catch (IOException e) {
+ throw new MojoExecutionException(e);
+ }
+ }
+ return gatherer.getFiles();
}
- private JavaCompiler getJavaCompiler() {
+ private static JavaCompiler getJavaCompiler() throws MojoExecutionException {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
if (compiler != null) {
return compiler;
}
- throw new IllegalStateException("Java compiler not found.");
+ throw new MojoExecutionException("Java compiler not found, currently only compiler from jdk.compiler module is supported.");
+ }
+
+ private static Charset charset(String encoding) throws MojoFailureException {
+ if (encoding != null) {
+ try {
+ return Charset.forName(encoding);
+ } catch (UnsupportedCharsetException e) {
+ throw new MojoFailureException("Invalid 'encoding' option: " + encoding, e);
+ }
+ }
+ return null;
}
}
diff --git a/maven-plugin/src/main/java/online/sharedtype/maven/SourceFileGatherer.java b/maven-plugin/src/main/java/online/sharedtype/maven/SourceFileGatherer.java
new file mode 100644
index 00000000..877fb2b5
--- /dev/null
+++ b/maven-plugin/src/main/java/online/sharedtype/maven/SourceFileGatherer.java
@@ -0,0 +1,23 @@
+package online.sharedtype.maven;
+
+import java.io.File;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
+import java.util.List;
+
+final class SourceFileGatherer extends SimpleFileVisitor {
+ private final List files = new ArrayList<>();
+
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
+ files.add(file.toFile());
+ return FileVisitResult.CONTINUE;
+ }
+
+ List getFiles() {
+ return files;
+ }
+}
diff --git a/pom.xml b/pom.xml
index 4aabfce7..00c121fc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -62,6 +62,11 @@
sharedtype
${project.version}
+
+ ${project.groupId}
+ sharedtype-ap
+ ${project.version}
+
org.checkerframework
checker-qual
diff --git a/processor/src/main/java/module-info.java b/processor/src/main/java/module-info.java
index fa3cb89a..78bd4a41 100644
--- a/processor/src/main/java/module-info.java
+++ b/processor/src/main/java/module-info.java
@@ -1,3 +1,5 @@
+import online.sharedtype.processor.SharedTypeAnnotationProcessor;
+
module online.sharedtype.processor {
requires online.sharedtype.annotation;
requires java.base;
@@ -8,5 +10,5 @@
requires com.github.mustachejava;
- provides javax.annotation.processing.Processor with online.sharedtype.processor.AnnotationProcessorImpl;
+ provides javax.annotation.processing.Processor with SharedTypeAnnotationProcessor;
}
diff --git a/processor/src/main/java/online/sharedtype/processor/AnnotationProcessorImpl.java b/processor/src/main/java/online/sharedtype/processor/SharedTypeAnnotationProcessor.java
similarity index 98%
rename from processor/src/main/java/online/sharedtype/processor/AnnotationProcessorImpl.java
rename to processor/src/main/java/online/sharedtype/processor/SharedTypeAnnotationProcessor.java
index ce3af180..4a7a3537 100644
--- a/processor/src/main/java/online/sharedtype/processor/AnnotationProcessorImpl.java
+++ b/processor/src/main/java/online/sharedtype/processor/SharedTypeAnnotationProcessor.java
@@ -36,7 +36,7 @@
@SupportedAnnotationTypes("online.sharedtype.SharedType")
@SupportedOptions({"sharedtype.propsFile", "sharedtype.enabled"})
@AutoService(Processor.class)
-public final class AnnotationProcessorImpl extends AbstractProcessor {
+public final class SharedTypeAnnotationProcessor extends AbstractProcessor {
private static final String PROPS_FILE_OPTION_NAME = "sharedtype.propsFile";
private static final String DEFAULT_USER_PROPS_FILE = "sharedtype.properties";
private static final boolean ANNOTATION_CONSUMED = true;
diff --git a/processor/src/test/java/online/sharedtype/processor/AnnotationProcessorImplTest.java b/processor/src/test/java/online/sharedtype/processor/SharedTypeAnnotationProcessorTest.java
similarity index 94%
rename from processor/src/test/java/online/sharedtype/processor/AnnotationProcessorImplTest.java
rename to processor/src/test/java/online/sharedtype/processor/SharedTypeAnnotationProcessorTest.java
index dc04c6ac..a8ed7275 100644
--- a/processor/src/test/java/online/sharedtype/processor/AnnotationProcessorImplTest.java
+++ b/processor/src/test/java/online/sharedtype/processor/SharedTypeAnnotationProcessorTest.java
@@ -20,12 +20,12 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-class AnnotationProcessorImplTest {
+class SharedTypeAnnotationProcessorTest {
private final ContextMocks ctxMocks = new ContextMocks();
private final TypeDefParser typeDefParser = mock(TypeDefParser.class);
private final TypeResolver typeResolver = mock(TypeResolver.class);
private final TypeWriter typeWriter = mock(TypeWriter.class);
- private final AnnotationProcessorImpl processor = new AnnotationProcessorImpl();
+ private final SharedTypeAnnotationProcessor processor = new SharedTypeAnnotationProcessor();
private final ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(String.class);
From bda228cc47b4998b70bcd44131fa17a476a6b920 Mon Sep 17 00:00:00 2001
From: Cause Chung
Date: Sun, 22 Jun 2025 13:13:54 -0400
Subject: [PATCH 04/17] Basic plugin gen flow
---
doc/Development.md | 6 ++++
maven-plugin/it/pom.xml | 31 ++++++++++++++++
.../online/sharedtype/maven/it/MyClass.java | 8 +++++
maven-plugin/pom.xml | 36 ++++++++++++++++++-
.../sharedtype/maven/DependencyResolver.java | 27 +++++++++++++-
.../sharedtype/maven/SharedTypeGenMojo.java | 18 +++++-----
mount-tmpfs.sh | 3 +-
pom.xml | 1 +
8 files changed, 119 insertions(+), 11 deletions(-)
create mode 100644 maven-plugin/it/pom.xml
create mode 100644 maven-plugin/it/src/main/java/online/sharedtype/maven/it/MyClass.java
diff --git a/doc/Development.md b/doc/Development.md
index 3e98b25a..de360b12 100644
--- a/doc/Development.md
+++ b/doc/Development.md
@@ -83,6 +83,12 @@ Compile specific classes, **along with debug, this is useful for developing a sp
./mvnw clean compile -pl it/java17 -DcompileClasses=online/sharedtype/it/java8/TempClass.java
```
+### Maven Plugin Development
+Debug Maven plugin IT:
+```bash
+mvnd -pl maven-plugin clean install && ./mvne -pl maven-plugin/it sharedtype:gen
+```
+
## Coding Style Guide / Keep it simple
1. since annotation processing is one shot execution, JIT is not likely to optimize the code. So prefer plain loop than long calling stacks like Stream chains.
2. no adding dependencies without strong justification.
diff --git a/maven-plugin/it/pom.xml b/maven-plugin/it/pom.xml
new file mode 100644
index 00000000..2e82e840
--- /dev/null
+++ b/maven-plugin/it/pom.xml
@@ -0,0 +1,31 @@
+
+
+ 4.0.0
+
+ online.sharedtype
+ sharedtype-parent
+ 0.13.0-SNAPSHOT
+ ../../pom.xml
+
+
+ sharedtype-maven-plugin-it
+ SharedType Maven Plugin Integration Test
+
+
+
+ ${project.groupId}
+ sharedtype-ap
+ ${project.version}
+
+
+
+
+
+
+ online.sharedtype
+ sharedtype-maven-plugin
+ ${project.version}
+
+
+
+
diff --git a/maven-plugin/it/src/main/java/online/sharedtype/maven/it/MyClass.java b/maven-plugin/it/src/main/java/online/sharedtype/maven/it/MyClass.java
new file mode 100644
index 00000000..dee8f413
--- /dev/null
+++ b/maven-plugin/it/src/main/java/online/sharedtype/maven/it/MyClass.java
@@ -0,0 +1,8 @@
+package online.sharedtype.maven.it;
+
+import online.sharedtype.SharedType;
+
+@SharedType
+final class MyClass {
+ private int value;
+}
diff --git a/maven-plugin/pom.xml b/maven-plugin/pom.xml
index eb0a83b3..3012a4a2 100644
--- a/maven-plugin/pom.xml
+++ b/maven-plugin/pom.xml
@@ -1,4 +1,6 @@
-
+
+
4.0.0
online.sharedtype
@@ -47,6 +49,38 @@
1
provided
+
+
+ com.github.codeteapot.maven.plugin-testing
+ maven-plugin-testing-harness-junit-jupiter
+ 1.1.1
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter
+ test
+
+
+ org.junit-pioneer
+ junit-pioneer
+ test
+
+
+ org.assertj
+ assertj-core
+ test
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+ org.mockito
+ mockito-junit-jupiter
+ test
+
diff --git a/maven-plugin/src/main/java/online/sharedtype/maven/DependencyResolver.java b/maven-plugin/src/main/java/online/sharedtype/maven/DependencyResolver.java
index 2f1d639c..02d36406 100644
--- a/maven-plugin/src/main/java/online/sharedtype/maven/DependencyResolver.java
+++ b/maven-plugin/src/main/java/online/sharedtype/maven/DependencyResolver.java
@@ -1,9 +1,11 @@
package online.sharedtype.maven;
+import org.apache.maven.RepositoryUtils;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.project.MavenProject;
import org.eclipse.aether.RepositorySystem;
+import org.eclipse.aether.artifact.ArtifactTypeRegistry;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.collection.CollectRequest;
import org.eclipse.aether.graph.Dependency;
@@ -14,7 +16,6 @@
import java.util.List;
import java.util.stream.Collectors;
-
final class DependencyResolver {
private static final String VERSION = DependencyResolver.class.getPackage().getImplementationVersion();
private final RepositorySystem repositorySystem;
@@ -43,4 +44,28 @@ List getProcessorDependencies() throws MojoExecutionException {
throw new MojoExecutionException(String.format("Failed to resolve '%s'", artifactCoordinate), e);
}
}
+
+ List getSourceDependencies() throws MojoExecutionException {
+ try {
+ ArtifactTypeRegistry artifactTypeRegistry =
+ session.getRepositorySession().getArtifactTypeRegistry();
+ CollectRequest collectRequest = new CollectRequest(
+ project.getDependencies().stream().map(md -> convertDependency(md, artifactTypeRegistry)).collect(Collectors.toList()),
+ project.getDependencyManagement().getDependencies().stream().map(md -> convertDependency(md, artifactTypeRegistry)).collect(Collectors.toList()),
+ project.getRemoteProjectRepositories()
+ );
+ DependencyRequest dependencyRequest = new DependencyRequest();
+ dependencyRequest.setCollectRequest(collectRequest);
+ DependencyResult dependencyResult = repositorySystem.resolveDependencies(session.getRepositorySession(), dependencyRequest);
+ return dependencyResult.getArtifactResults().stream()
+ .map(resolved -> resolved.getArtifact().getFile())
+ .collect(Collectors.toList());
+ } catch (Exception e) {
+ throw new MojoExecutionException("Failed to resolve dependency, ", e);
+ }
+ }
+
+ private static Dependency convertDependency(org.apache.maven.model.Dependency md, ArtifactTypeRegistry artifactTypeRegistry) {
+ return RepositoryUtils.toDependency(md, artifactTypeRegistry);
+ }
}
diff --git a/maven-plugin/src/main/java/online/sharedtype/maven/SharedTypeGenMojo.java b/maven-plugin/src/main/java/online/sharedtype/maven/SharedTypeGenMojo.java
index 812511f1..3fcf6380 100644
--- a/maven-plugin/src/main/java/online/sharedtype/maven/SharedTypeGenMojo.java
+++ b/maven-plugin/src/main/java/online/sharedtype/maven/SharedTypeGenMojo.java
@@ -22,8 +22,8 @@
import java.nio.charset.UnsupportedCharsetException;
import java.nio.file.FileVisitOption;
import java.nio.file.Files;
-import java.nio.file.Path;
import java.nio.file.Paths;
+import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
@@ -31,7 +31,7 @@
@Mojo(name = "gen")
public final class SharedTypeGenMojo extends AbstractMojo {
- private static final Set DEFAULT_COMPILER_OPTIONS = Collections.singleton("-proc:only");
+ private static final List DEFAULT_COMPILER_OPTIONS = Arrays.asList("-proc:only", "-Asharedtype.enabled=true");
private @Inject RepositorySystem repositorySystem;
@Parameter(defaultValue = "${session}", readonly = true, required = true)
private MavenSession session;
@@ -39,25 +39,27 @@ public final class SharedTypeGenMojo extends AbstractMojo {
private MavenProject project;
@Parameter(property = "encoding", defaultValue = "${project.build.sourceEncoding}")
private String encoding;
- @Parameter(defaultValue = "${project.build.outputDirectory}", required = true, readonly = true)
- private Path outputDirectory;
- @Parameter(defaultValue = "generated-sources", required = false, readonly = true)
- private Path generatedSourcesDirectory;
+ @Parameter(defaultValue = "${project.build.directory}", required = true, readonly = true)
+ private String outputDirectory;
+ @Parameter(defaultValue = "generated-sources", readonly = true)
+ private String generatedSourcesDirectory;
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
JavaCompiler compiler = getJavaCompiler();
-// DependencyResolver dependencyResolver = new DependencyResolver(repositorySystem, session, project);
+ DependencyResolver dependencyResolver = new DependencyResolver(repositorySystem, session, project);
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, charset(encoding));
try {
// fileManager.setLocation(StandardLocation.ANNOTATION_PROCESSOR_PATH, dependencyResolver.getProcessorDependencies());
- fileManager.setLocation(StandardLocation.SOURCE_OUTPUT, Collections.singleton(outputDirectory.resolve(generatedSourcesDirectory).toFile()));
+ fileManager.setLocation(StandardLocation.SOURCE_OUTPUT, Collections.singleton(Paths.get(outputDirectory, generatedSourcesDirectory).toFile()));
+ fileManager.setLocation(StandardLocation.CLASS_PATH, dependencyResolver.getSourceDependencies());
} catch (IOException e) {
throw new MojoExecutionException(e);
}
Iterable extends JavaFileObject> sources = fileManager.getJavaFileObjectsFromFiles(walkAllSourceFiles());
JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, DEFAULT_COMPILER_OPTIONS, null, sources);
task.setProcessors(Collections.singleton(new SharedTypeAnnotationProcessor()));
+ task.call();
}
private List walkAllSourceFiles() throws MojoExecutionException {
diff --git a/mount-tmpfs.sh b/mount-tmpfs.sh
index 21dbeb82..b10e5bf8 100755
--- a/mount-tmpfs.sh
+++ b/mount-tmpfs.sh
@@ -22,7 +22,8 @@ mountTmpfs "$DIR/processor/target" 64M
mountTmpfs "$DIR/it/java17/target" 64M
mountTmpfs "$DIR/it/java8/target" 64M
mountTmpfs "$DIR/e2e/target" 64M
-mountTmpfs "$DIR/maven-plugin/target" 64M
+mountTmpfs "$DIR/maven-plugin/target" 32M
+mountTmpfs "$DIR/maven-plugin/it/target" 16M
mountTmpfs "$MAVEN_REPO_DIR" 64M
mountTmpfs "$DIR/client-test/rust/target" 512M
mountTmpfs "$DIR/client-test/typescript/dist" 32M
diff --git a/pom.xml b/pom.xml
index 00c121fc..0dd531c1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -271,6 +271,7 @@
processor
it
maven-plugin
+ maven-plugin/it
From 673226dba94dc08df60d1b39d7a54e682ca2fee2 Mon Sep 17 00:00:00 2001
From: Cause Chung
Date: Mon, 23 Jun 2025 21:58:43 -0400
Subject: [PATCH 05/17] Refine plugin parameters
---
doc/Development.md | 1 +
maven-plugin/it/pom.xml | 6 +-
maven-plugin/it/sharedtype.properties | 1 +
maven-plugin/pom.xml | 22 ++++++
.../sharedtype/maven/DependencyResolver.java | 29 +-------
.../sharedtype/maven/SharedTypeGenMojo.java | 69 +++++++++++++------
...leGatherer.java => SourceFileVisitor.java} | 7 +-
.../writer/JavaSerializationFileWriter.java | 10 ++-
8 files changed, 88 insertions(+), 57 deletions(-)
create mode 100644 maven-plugin/it/sharedtype.properties
rename maven-plugin/src/main/java/online/sharedtype/maven/{SourceFileGatherer.java => SourceFileVisitor.java} (69%)
diff --git a/doc/Development.md b/doc/Development.md
index de360b12..39cef68e 100644
--- a/doc/Development.md
+++ b/doc/Development.md
@@ -14,6 +14,7 @@ Internal types also have javadoc for more information.
* `java17` uses symlink to reuse types in `java8` then does more type checks, e.g. for Java `record`.
* `client-test` contains target languages' tests respectively against generated code.
* `e2e` contains e2e json 2-way serialization and deserialization tests against target languages' http servers.
+* `maven-plugin` contains maven plugin for SharedType annotation, and `maven-plugin/it` contains integration tests for maven plugin.
Domain types are shared among processor and integration tests to reduce maven module count.
diff --git a/maven-plugin/it/pom.xml b/maven-plugin/it/pom.xml
index 2e82e840..87c3c2d4 100644
--- a/maven-plugin/it/pom.xml
+++ b/maven-plugin/it/pom.xml
@@ -1,5 +1,6 @@
-
+
4.0.0
online.sharedtype
@@ -25,6 +26,9 @@
online.sharedtype
sharedtype-maven-plugin
${project.version}
+
+ ${project.basedir}/sharedtype.properties
+
diff --git a/maven-plugin/it/sharedtype.properties b/maven-plugin/it/sharedtype.properties
new file mode 100644
index 00000000..1e5e525d
--- /dev/null
+++ b/maven-plugin/it/sharedtype.properties
@@ -0,0 +1 @@
+sharedtype.targets=CONSOLE,TYPESCRIPT
diff --git a/maven-plugin/pom.xml b/maven-plugin/pom.xml
index 3012a4a2..1f792a49 100644
--- a/maven-plugin/pom.xml
+++ b/maven-plugin/pom.xml
@@ -83,6 +83,28 @@
+
+
+ maven-clean-plugin
+
+ true
+
+
+ ${project.build.directory}
+
+ **/*
+
+
+
+ ${settings.localRepository}/online/sharedtype/sharedtype-maven-plugin
+
+ **/*
+
+
+
+
+
+
diff --git a/maven-plugin/src/main/java/online/sharedtype/maven/DependencyResolver.java b/maven-plugin/src/main/java/online/sharedtype/maven/DependencyResolver.java
index 02d36406..d0aa9903 100644
--- a/maven-plugin/src/main/java/online/sharedtype/maven/DependencyResolver.java
+++ b/maven-plugin/src/main/java/online/sharedtype/maven/DependencyResolver.java
@@ -6,9 +6,7 @@
import org.apache.maven.project.MavenProject;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.artifact.ArtifactTypeRegistry;
-import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.collection.CollectRequest;
-import org.eclipse.aether.graph.Dependency;
import org.eclipse.aether.resolution.DependencyRequest;
import org.eclipse.aether.resolution.DependencyResult;
@@ -17,7 +15,6 @@
import java.util.stream.Collectors;
final class DependencyResolver {
- private static final String VERSION = DependencyResolver.class.getPackage().getImplementationVersion();
private final RepositorySystem repositorySystem;
private final MavenSession session;
private final MavenProject project;
@@ -28,30 +25,14 @@ final class DependencyResolver {
this.project = project;
}
- List getProcessorDependencies() throws MojoExecutionException {
- String artifactCoordinate = String.format("online.sharedtype:sharedtype-processor:%s", VERSION);
- try {
- Dependency dependency = new Dependency(new DefaultArtifact(artifactCoordinate), null);
- CollectRequest collectRequest =
- new CollectRequest(dependency, project.getRemoteProjectRepositories());
- DependencyRequest dependencyRequest = new DependencyRequest();
- dependencyRequest.setCollectRequest(collectRequest);
- DependencyResult dependencyResult = repositorySystem.resolveDependencies(session.getRepositorySession(), dependencyRequest);
- return dependencyResult.getArtifactResults().stream()
- .map(resolved -> resolved.getArtifact().getFile())
- .collect(Collectors.toList());
- } catch (Exception e) {
- throw new MojoExecutionException(String.format("Failed to resolve '%s'", artifactCoordinate), e);
- }
- }
-
List getSourceDependencies() throws MojoExecutionException {
try {
ArtifactTypeRegistry artifactTypeRegistry =
session.getRepositorySession().getArtifactTypeRegistry();
CollectRequest collectRequest = new CollectRequest(
- project.getDependencies().stream().map(md -> convertDependency(md, artifactTypeRegistry)).collect(Collectors.toList()),
- project.getDependencyManagement().getDependencies().stream().map(md -> convertDependency(md, artifactTypeRegistry)).collect(Collectors.toList()),
+ project.getDependencies().stream().map(md -> RepositoryUtils.toDependency(md, artifactTypeRegistry)).collect(Collectors.toList()),
+ project.getDependencyManagement().getDependencies().stream()
+ .map(md -> RepositoryUtils.toDependency(md, artifactTypeRegistry)).collect(Collectors.toList()),
project.getRemoteProjectRepositories()
);
DependencyRequest dependencyRequest = new DependencyRequest();
@@ -64,8 +45,4 @@ List getSourceDependencies() throws MojoExecutionException {
throw new MojoExecutionException("Failed to resolve dependency, ", e);
}
}
-
- private static Dependency convertDependency(org.apache.maven.model.Dependency md, ArtifactTypeRegistry artifactTypeRegistry) {
- return RepositoryUtils.toDependency(md, artifactTypeRegistry);
- }
}
diff --git a/maven-plugin/src/main/java/online/sharedtype/maven/SharedTypeGenMojo.java b/maven-plugin/src/main/java/online/sharedtype/maven/SharedTypeGenMojo.java
index 3fcf6380..5c7a8c7b 100644
--- a/maven-plugin/src/main/java/online/sharedtype/maven/SharedTypeGenMojo.java
+++ b/maven-plugin/src/main/java/online/sharedtype/maven/SharedTypeGenMojo.java
@@ -1,6 +1,7 @@
package online.sharedtype.maven;
import online.sharedtype.processor.SharedTypeAnnotationProcessor;
+import online.sharedtype.processor.support.annotation.Nullable;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
@@ -17,61 +18,84 @@
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;
import java.io.File;
-import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.UnsupportedCharsetException;
import java.nio.file.FileVisitOption;
import java.nio.file.Files;
+import java.nio.file.Path;
import java.nio.file.Paths;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
-import java.util.Set;
@Mojo(name = "gen")
public final class SharedTypeGenMojo extends AbstractMojo {
private static final List DEFAULT_COMPILER_OPTIONS = Arrays.asList("-proc:only", "-Asharedtype.enabled=true");
private @Inject RepositorySystem repositorySystem;
- @Parameter(defaultValue = "${session}", readonly = true, required = true)
+
+ @Inject
private MavenSession session;
- @Parameter(defaultValue = "${project}", readonly = true, required = true)
+
+ @Inject
private MavenProject project;
- @Parameter(property = "encoding", defaultValue = "${project.build.sourceEncoding}")
- private String encoding;
- @Parameter(defaultValue = "${project.build.directory}", required = true, readonly = true)
- private String outputDirectory;
- @Parameter(defaultValue = "generated-sources", readonly = true)
+
+ @Parameter(defaultValue = "generated-sources")
private String generatedSourcesDirectory;
+ /**
+ * The path of file 'sharedtype.properties'. If not provided, default values will be used. If provided, the file must exist.
+ */
+ @Nullable
+ @Parameter
+ private String propertyFile;
+
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
JavaCompiler compiler = getJavaCompiler();
+
DependencyResolver dependencyResolver = new DependencyResolver(repositorySystem, session, project);
- StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, charset(encoding));
+ StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, getCharset());
try {
-// fileManager.setLocation(StandardLocation.ANNOTATION_PROCESSOR_PATH, dependencyResolver.getProcessorDependencies());
- fileManager.setLocation(StandardLocation.SOURCE_OUTPUT, Collections.singleton(Paths.get(outputDirectory, generatedSourcesDirectory).toFile()));
+ String projectBuildDir = project.getBuild().getDirectory();
+ Path outputDir = Paths.get(projectBuildDir, generatedSourcesDirectory);
+ if (Files.notExists(outputDir)) {
+ Files.createDirectories(outputDir);
+ }
+ fileManager.setLocation(StandardLocation.SOURCE_OUTPUT, Collections.singleton(outputDir.toFile()));
fileManager.setLocation(StandardLocation.CLASS_PATH, dependencyResolver.getSourceDependencies());
- } catch (IOException e) {
+ } catch (Exception e) {
throw new MojoExecutionException(e);
}
Iterable extends JavaFileObject> sources = fileManager.getJavaFileObjectsFromFiles(walkAllSourceFiles());
- JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, DEFAULT_COMPILER_OPTIONS, null, sources);
+ JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, getCompilerOptions(), null, sources);
task.setProcessors(Collections.singleton(new SharedTypeAnnotationProcessor()));
task.call();
}
private List walkAllSourceFiles() throws MojoExecutionException {
- SourceFileGatherer gatherer = new SourceFileGatherer();
- for (String compileSourceRoot : project.getCompileSourceRoots()) {
- try {
- Files.walkFileTree(Paths.get(compileSourceRoot), EnumSet.of(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE, gatherer);
- } catch (IOException e) {
- throw new MojoExecutionException(e);
+ SourceFileVisitor visitor = new SourceFileVisitor();
+ try {
+ for (String compileSourceRoot : project.getCompileSourceRoots()) {
+ Files.walkFileTree(Paths.get(compileSourceRoot), EnumSet.of(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE, visitor);
+ }
+ } catch (Exception e) {
+ throw new MojoExecutionException(e);
+ }
+ return visitor.getFiles();
+ }
+
+ private List getCompilerOptions() throws MojoFailureException {
+ List options = new ArrayList<>(DEFAULT_COMPILER_OPTIONS.size() + 1);
+ options.addAll(DEFAULT_COMPILER_OPTIONS);
+ if (propertyFile != null) {
+ if (Files.notExists(Paths.get(propertyFile))) {
+ throw new MojoFailureException("Property file not found: " + propertyFile);
}
+ options.add("-Asharedtype.propsFile=" + propertyFile);
}
- return gatherer.getFiles();
+ return options;
}
private static JavaCompiler getJavaCompiler() throws MojoExecutionException {
@@ -82,7 +106,8 @@ private static JavaCompiler getJavaCompiler() throws MojoExecutionException {
throw new MojoExecutionException("Java compiler not found, currently only compiler from jdk.compiler module is supported.");
}
- private static Charset charset(String encoding) throws MojoFailureException {
+ private Charset getCharset() throws MojoFailureException {
+ String encoding = project.getProperties().getProperty("project.build.sourceEncoding");
if (encoding != null) {
try {
return Charset.forName(encoding);
diff --git a/maven-plugin/src/main/java/online/sharedtype/maven/SourceFileGatherer.java b/maven-plugin/src/main/java/online/sharedtype/maven/SourceFileVisitor.java
similarity index 69%
rename from maven-plugin/src/main/java/online/sharedtype/maven/SourceFileGatherer.java
rename to maven-plugin/src/main/java/online/sharedtype/maven/SourceFileVisitor.java
index 877fb2b5..f23e119d 100644
--- a/maven-plugin/src/main/java/online/sharedtype/maven/SourceFileGatherer.java
+++ b/maven-plugin/src/main/java/online/sharedtype/maven/SourceFileVisitor.java
@@ -8,12 +8,15 @@
import java.util.ArrayList;
import java.util.List;
-final class SourceFileGatherer extends SimpleFileVisitor {
+final class SourceFileVisitor extends SimpleFileVisitor {
+ private static final String FILE_EXTENSION = ".java";
private final List files = new ArrayList<>();
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
- files.add(file.toFile());
+ if (file.toString().endsWith(FILE_EXTENSION)) {
+ files.add(file.toFile());
+ }
return FileVisitResult.CONTINUE;
}
diff --git a/processor/src/main/java/online/sharedtype/processor/writer/JavaSerializationFileWriter.java b/processor/src/main/java/online/sharedtype/processor/writer/JavaSerializationFileWriter.java
index 7250e3b8..01dacba8 100644
--- a/processor/src/main/java/online/sharedtype/processor/writer/JavaSerializationFileWriter.java
+++ b/processor/src/main/java/online/sharedtype/processor/writer/JavaSerializationFileWriter.java
@@ -1,5 +1,6 @@
package online.sharedtype.processor.writer;
+import lombok.RequiredArgsConstructor;
import online.sharedtype.processor.domain.def.ConstantNamespaceDef;
import online.sharedtype.processor.domain.def.TypeDef;
import online.sharedtype.processor.context.Context;
@@ -18,18 +19,15 @@
*
* @author Cause Chung
*/
+@RequiredArgsConstructor
final class JavaSerializationFileWriter implements TypeWriter {
- private final Filer filer;
-
- JavaSerializationFileWriter(Context ctx) {
- this.filer = ctx.getProcessingEnv().getFiler();
- }
+ private final Context ctx;
@Override
public void write(List typeDefs) {
try {
for (TypeDef typeDef : typeDefs) {
- FileObject file = filer.createResource(StandardLocation.CLASS_OUTPUT, "", getTypeName(typeDef) + ".ser");
+ FileObject file = ctx.createSourceOutput(getTypeName(typeDef) + ".ser");
try(OutputStream outputStream = file.openOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(outputStream)) {
oos.writeObject(typeDef);
From f11882fb31ac6b4b6cc0fa8c2fb1bbbf2f40a808 Mon Sep 17 00:00:00 2001
From: Cause Chung
Date: Mon, 23 Jun 2025 22:41:42 -0400
Subject: [PATCH 06/17] Add it for maven plugin
---
maven-plugin/it/pom.xml | 31 +++++++++++++++++++
.../maven/it/MavenPluginIntegrationTest.java | 26 ++++++++++++++++
.../it/src/test/resources/expected-types.ts | 8 +++++
.../sharedtype/maven/SharedTypeGenMojo.java | 22 ++++++++-----
4 files changed, 80 insertions(+), 7 deletions(-)
create mode 100644 maven-plugin/it/src/test/java/online/sharedtype/maven/it/MavenPluginIntegrationTest.java
create mode 100644 maven-plugin/it/src/test/resources/expected-types.ts
diff --git a/maven-plugin/it/pom.xml b/maven-plugin/it/pom.xml
index 87c3c2d4..81ee54ff 100644
--- a/maven-plugin/it/pom.xml
+++ b/maven-plugin/it/pom.xml
@@ -18,6 +18,17 @@
sharedtype-ap
${project.version}
+
+
+ org.junit.jupiter
+ junit-jupiter
+ test
+
+
+ org.assertj
+ assertj-core
+ test
+
@@ -30,6 +41,26 @@
${project.basedir}/sharedtype.properties
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+
+
+ add-test-resource
+ generate-test-resources
+
+ add-test-resource
+
+
+
+
+ ${project.build.directory}/generated-sources
+
+
+
+
+
+
diff --git a/maven-plugin/it/src/test/java/online/sharedtype/maven/it/MavenPluginIntegrationTest.java b/maven-plugin/it/src/test/java/online/sharedtype/maven/it/MavenPluginIntegrationTest.java
new file mode 100644
index 00000000..b3afbe37
--- /dev/null
+++ b/maven-plugin/it/src/test/java/online/sharedtype/maven/it/MavenPluginIntegrationTest.java
@@ -0,0 +1,26 @@
+package online.sharedtype.maven.it;
+
+import org.junit.jupiter.api.Test;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+final class MavenPluginIntegrationTest {
+ @Test
+ void verifyGeneratedFile() throws Exception {
+ var expectedContent = readResourceContent("expected-types.ts");
+ var generatedFileContent = readResourceContent("types.ts");
+
+ assertThat(generatedFileContent).isEqualTo(expectedContent);
+ }
+
+ static String readResourceContent(String path) throws Exception {
+ var resource = MavenPluginIntegrationTest.class.getClassLoader().getResource(path);
+ if (resource == null) {
+ throw new RuntimeException("Resource not found: " + path);
+ }
+ return Files.readString(Path.of(resource.toURI()));
+ }
+}
diff --git a/maven-plugin/it/src/test/resources/expected-types.ts b/maven-plugin/it/src/test/resources/expected-types.ts
new file mode 100644
index 00000000..4470982d
--- /dev/null
+++ b/maven-plugin/it/src/test/resources/expected-types.ts
@@ -0,0 +1,8 @@
+// Code generated by https://github.com/SharedType/sharedtype
+
+
+
+export interface MyClass {
+ readonly value: number;
+}
+
diff --git a/maven-plugin/src/main/java/online/sharedtype/maven/SharedTypeGenMojo.java b/maven-plugin/src/main/java/online/sharedtype/maven/SharedTypeGenMojo.java
index 5c7a8c7b..31e60865 100644
--- a/maven-plugin/src/main/java/online/sharedtype/maven/SharedTypeGenMojo.java
+++ b/maven-plugin/src/main/java/online/sharedtype/maven/SharedTypeGenMojo.java
@@ -1,5 +1,6 @@
package online.sharedtype.maven;
+import online.sharedtype.SharedType;
import online.sharedtype.processor.SharedTypeAnnotationProcessor;
import online.sharedtype.processor.support.annotation.Nullable;
import org.apache.maven.execution.MavenSession;
@@ -30,6 +31,11 @@
import java.util.EnumSet;
import java.util.List;
+/**
+ * Generate types from {@link SharedType} annotated classes.
+ * See SharedType for details.
+ * @author Cause Chung
+ */
@Mojo(name = "gen")
public final class SharedTypeGenMojo extends AbstractMojo {
private static final List DEFAULT_COMPILER_OPTIONS = Arrays.asList("-proc:only", "-Asharedtype.enabled=true");
@@ -41,8 +47,11 @@ public final class SharedTypeGenMojo extends AbstractMojo {
@Inject
private MavenProject project;
- @Parameter(defaultValue = "generated-sources")
- private String generatedSourcesDirectory;
+ /**
+ * Output directory for generated types. Defaults to '${project.build.directory}/generated-sources'.
+ */
+ @Parameter(defaultValue = "${project.build.directory}/generated-sources")
+ private String outputDirectory;
/**
* The path of file 'sharedtype.properties'. If not provided, default values will be used. If provided, the file must exist.
@@ -58,12 +67,11 @@ public void execute() throws MojoExecutionException, MojoFailureException {
DependencyResolver dependencyResolver = new DependencyResolver(repositorySystem, session, project);
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, getCharset());
try {
- String projectBuildDir = project.getBuild().getDirectory();
- Path outputDir = Paths.get(projectBuildDir, generatedSourcesDirectory);
- if (Files.notExists(outputDir)) {
- Files.createDirectories(outputDir);
+ Path outputDirPath = Paths.get(outputDirectory);
+ if (Files.notExists(outputDirPath)) {
+ Files.createDirectories(outputDirPath);
}
- fileManager.setLocation(StandardLocation.SOURCE_OUTPUT, Collections.singleton(outputDir.toFile()));
+ fileManager.setLocation(StandardLocation.SOURCE_OUTPUT, Collections.singleton(outputDirPath.toFile()));
fileManager.setLocation(StandardLocation.CLASS_PATH, dependencyResolver.getSourceDependencies());
} catch (Exception e) {
throw new MojoExecutionException(e);
From 22b4e0dcdefffc504d5e526545f6905016964acd Mon Sep 17 00:00:00 2001
From: Cause Chung
Date: Tue, 24 Jun 2025 22:25:34 -0400
Subject: [PATCH 07/17] Remove properties-maven-plugin due to system property
pollution in mvnd
---
doc/Usage.md | 2 +-
it/pom.xml | 42 ++++++++++++++--------------------------
it/sharedtype.properties | 4 ++++
maven-plugin/it/pom.xml | 9 +++++++++
4 files changed, 28 insertions(+), 29 deletions(-)
diff --git a/doc/Usage.md b/doc/Usage.md
index 3d47d304..3d182ccd 100644
--- a/doc/Usage.md
+++ b/doc/Usage.md
@@ -90,7 +90,7 @@ or
```bash
MAVEN_OPTS="-Dsharedtype.typescript.custom-code-path=it/custom-code.ts" ./mvnw clean compile
```
-or can use [properties-maven-plugin](https://www.mojohaus.org/properties-maven-plugin/usage.html#set-system-properties) to set system properties for the build.
+or can use [properties-maven-plugin](https://www.mojohaus.org/properties-maven-plugin/usage.html#set-system-properties) to set system properties for the build. (Not recommended for multi-module builds, due to potential system property pollution.)
See [Default Properties](../processor/src/main/resources/sharedtype-default.properties) for details.
diff --git a/it/pom.xml b/it/pom.xml
index ebc0d744..d868fb60 100644
--- a/it/pom.xml
+++ b/it/pom.xml
@@ -104,34 +104,6 @@
${excludeClasses}
-
- org.codehaus.mojo
- properties-maven-plugin
- 1.2.1
-
-
-
- set-system-properties
-
-
-
-
- sharedtype.typescript.custom-code-path
- it/custom-code.ts
-
-
- sharedtype.go.custom-code-path
- it/custom-code.go
-
-
- sharedtype.rust.custom-code-path
- it/custom-code.rs
-
-
-
-
-
-
@@ -174,6 +146,20 @@
+
+ add-test-resource
+ generate-test-resources
+
+ add-test-resource
+
+
+
+
+ ${project.build.directory}/generated-sources
+
+
+
+
diff --git a/it/sharedtype.properties b/it/sharedtype.properties
index c93c1bf0..921c09d4 100644
--- a/it/sharedtype.properties
+++ b/it/sharedtype.properties
@@ -10,3 +10,7 @@ sharedtype.typescript.type-mappings=online.sharedtype.it.java8.types.MyType1:Arr
sharedtype.rust.type-mappings=online.sharedtype.it.java8.types.MyType1:Vec
sharedtype.go.type-mappings=online.sharedtype.it.java8.types.MyType1:[]
+
+sharedtype.typescript.custom-code-path=it/custom-code.ts
+sharedtype.go.custom-code-path=it/custom-code.go
+sharedtype.rust.custom-code-path=it/custom-code.rs
diff --git a/maven-plugin/it/pom.xml b/maven-plugin/it/pom.xml
index 81ee54ff..1be983df 100644
--- a/maven-plugin/it/pom.xml
+++ b/maven-plugin/it/pom.xml
@@ -40,6 +40,15 @@
${project.basedir}/sharedtype.properties
+
+
+ gen-for-testing
+ compile
+
+ gen
+
+
+
org.codehaus.mojo
From d912351cebe809509965e4ff2387c5ba4da76019 Mon Sep 17 00:00:00 2001
From: Cause Chung
Date: Sat, 28 Jun 2025 11:40:02 -0400
Subject: [PATCH 08/17] Supplier userProps to processor programmatically
---
.../SharedTypeAnnotationProcessor.java | 10 ++++-
.../processor/context/PropsFactory.java | 9 +++--
.../processor/context/ContextMocks.java | 2 +-
.../processor/context/PropsFactoryTest.java | 38 +++++++++++++++----
.../test-sharedtype-wrong-target.properties | 1 -
...-wrong-ts-optional-field-format.properties | 1 -
6 files changed, 45 insertions(+), 16 deletions(-)
delete mode 100644 processor/src/test/resources/test-sharedtype-wrong-target.properties
delete mode 100644 processor/src/test/resources/test-sharedtype-wrong-ts-optional-field-format.properties
diff --git a/processor/src/main/java/online/sharedtype/processor/SharedTypeAnnotationProcessor.java b/processor/src/main/java/online/sharedtype/processor/SharedTypeAnnotationProcessor.java
index 4a7a3537..7b48ad71 100644
--- a/processor/src/main/java/online/sharedtype/processor/SharedTypeAnnotationProcessor.java
+++ b/processor/src/main/java/online/sharedtype/processor/SharedTypeAnnotationProcessor.java
@@ -1,11 +1,13 @@
package online.sharedtype.processor;
import com.google.auto.service.AutoService;
+import lombok.Setter;
import online.sharedtype.processor.domain.def.TypeDef;
import online.sharedtype.processor.context.Context;
import online.sharedtype.processor.context.PropsFactory;
import online.sharedtype.processor.parser.TypeDefParser;
import online.sharedtype.processor.resolver.TypeResolver;
+import online.sharedtype.processor.support.annotation.Nullable;
import online.sharedtype.processor.support.annotation.VisibleForTesting;
import online.sharedtype.processor.support.exception.SharedTypeException;
import online.sharedtype.processor.support.exception.SharedTypeInternalError;
@@ -24,6 +26,7 @@
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import static online.sharedtype.processor.domain.Constants.ANNOTATION_QUALIFIED_NAME;
@@ -40,6 +43,9 @@ public final class SharedTypeAnnotationProcessor extends AbstractProcessor {
private static final String PROPS_FILE_OPTION_NAME = "sharedtype.propsFile";
private static final String DEFAULT_USER_PROPS_FILE = "sharedtype.properties";
private static final boolean ANNOTATION_CONSUMED = true;
+ /** Programmatically provided user properties, e.g. from Maven plugin */
+ @Nullable @Setter
+ private Map userProps;
private boolean enabled;
Context ctx;
TypeDefParser parser;
@@ -54,10 +60,10 @@ public SourceVersion getSupportedSourceVersion() {
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
- String configFile = processingEnv.getOptions().getOrDefault(PROPS_FILE_OPTION_NAME, DEFAULT_USER_PROPS_FILE);
+ String userPropsFile = processingEnv.getOptions().getOrDefault(PROPS_FILE_OPTION_NAME, DEFAULT_USER_PROPS_FILE);
enabled = isEnabled(processingEnv);
if (enabled) {
- ctx = new Context(processingEnv, PropsFactory.loadProps(Paths.get(configFile)));
+ ctx = new Context(processingEnv, PropsFactory.loadProps(Paths.get(userPropsFile), userProps));
parser = TypeDefParser.create(ctx);
resolver = TypeResolver.create(ctx, parser);
writer = TypeWriter.create(ctx);
diff --git a/processor/src/main/java/online/sharedtype/processor/context/PropsFactory.java b/processor/src/main/java/online/sharedtype/processor/context/PropsFactory.java
index 5c850a88..67461182 100644
--- a/processor/src/main/java/online/sharedtype/processor/context/PropsFactory.java
+++ b/processor/src/main/java/online/sharedtype/processor/context/PropsFactory.java
@@ -24,7 +24,7 @@
public final class PropsFactory {
private static final String DEFAULT_PROPERTIES_FILE = "sharedtype-default.properties";
- public static Props loadProps(@Nullable Path userPropertiesFile) {
+ public static Props loadProps(@Nullable Path userPropertiesFile, @Nullable Map userProperties) {
ClassLoader classLoader = PropsFactory.class.getClassLoader();
try (InputStream defaultPropsInputstream = classLoader.getResourceAsStream(DEFAULT_PROPERTIES_FILE);
InputStream userPropsInputstream = userPropertiesFile == null || Files.notExists(userPropertiesFile) ? null : Files.newInputStream(userPropertiesFile)) {
@@ -33,8 +33,11 @@ public static Props loadProps(@Nullable Path userPropertiesFile) {
if (userPropsInputstream != null) {
properties.load(userPropsInputstream);
}
+ if (userProperties != null) {
+ properties.putAll(userProperties);
+ }
properties.putAll(System.getProperties());
- Props props = loadProps(properties);
+ Props props = convertProps(properties);
if (props.getTypescript().getOptionalFieldFormats().isEmpty()) {
throw new IllegalArgumentException("Props 'typescript.optional-field-format' cannot be empty.");
}
@@ -44,7 +47,7 @@ public static Props loadProps(@Nullable Path userPropertiesFile) {
}
}
- private static Props loadProps(Properties properties) {
+ private static Props convertProps(Properties properties) {
Set targets = parseEnumSet(properties, "sharedtype.targets", OutputTarget.class, OutputTarget::valueOf);
return Props.builder()
.targets(targets)
diff --git a/processor/src/test/java/online/sharedtype/processor/context/ContextMocks.java b/processor/src/test/java/online/sharedtype/processor/context/ContextMocks.java
index 30cdaf00..958d8caa 100644
--- a/processor/src/test/java/online/sharedtype/processor/context/ContextMocks.java
+++ b/processor/src/test/java/online/sharedtype/processor/context/ContextMocks.java
@@ -29,7 +29,7 @@ public final class ContextMocks {
private final Context context = mock(Context.class);
public ContextMocks() {
- this.props = spy(PropsFactory.loadProps(null));
+ this.props = spy(PropsFactory.loadProps(null, null));
when(context.getProps()).thenReturn(props);
when(context.getProcessingEnv()).thenReturn(processingEnv);
when(processingEnv.getElementUtils()).thenReturn(elements);
diff --git a/processor/src/test/java/online/sharedtype/processor/context/PropsFactoryTest.java b/processor/src/test/java/online/sharedtype/processor/context/PropsFactoryTest.java
index 6e35982f..068456d9 100644
--- a/processor/src/test/java/online/sharedtype/processor/context/PropsFactoryTest.java
+++ b/processor/src/test/java/online/sharedtype/processor/context/PropsFactoryTest.java
@@ -3,10 +3,12 @@
import online.sharedtype.SharedType;
import org.junit.jupiter.api.Test;
import online.sharedtype.processor.support.exception.SharedTypeException;
+import org.junitpioneer.jupiter.SetSystemProperty;
import java.net.URISyntaxException;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
@@ -14,16 +16,33 @@
final class PropsFactoryTest {
@Test
- void loadUserProps() {
- Props props = PropsFactory.loadProps(resolveResource("test-sharedtype-user.properties"));
+ void loadUserPropsFile() {
+ Props props = PropsFactory.loadProps(resolveResource("test-sharedtype-user.properties"), null);
assertThat(props.getTargets()).containsExactly(OutputTarget.CONSOLE, OutputTarget.TYPESCRIPT);
assertThat(props.getOptionalAnnotations()).containsExactly("a.b.TsOptional");
assertThat(props.getTypescript().getJavaObjectMapType()).isEqualTo("unknown");
}
+ @Test
+ void loadUserProps() {
+ var userProps = Map.of("sharedtype.optional-annotations", "a.b.TsOptionalOverride");
+ Props props = PropsFactory.loadProps(resolveResource("test-sharedtype-user.properties"), userProps);
+ assertThat(props.getTargets()).containsExactly(OutputTarget.CONSOLE, OutputTarget.TYPESCRIPT);
+ assertThat(props.getOptionalAnnotations()).containsExactly("a.b.TsOptionalOverride");
+ }
+
+ @SetSystemProperty(key = "sharedtype.optional-annotations", value = "a.b.TsOptionalOverride2")
+ @Test
+ void loadSysProps() {
+ var userProps = Map.of("sharedtype.optional-annotations", "a.b.TsOptionalOverride");
+ Props props = PropsFactory.loadProps(resolveResource("test-sharedtype-user.properties"), userProps);
+ assertThat(props.getTargets()).containsExactly(OutputTarget.CONSOLE, OutputTarget.TYPESCRIPT);
+ assertThat(props.getOptionalAnnotations()).containsExactly("a.b.TsOptionalOverride2");
+ }
+
@Test
void loadDefaultProps() {
- Props props = PropsFactory.loadProps(Paths.get("not-exist"));
+ Props props = PropsFactory.loadProps(Paths.get("not-exist"), null);
assertThat(props.getTargets()).containsExactly(OutputTarget.TYPESCRIPT);
assertThat(props.getTargetTypes()).containsExactly(SharedType.TargetType.TYPESCRIPT);
assertThat(props.getOptionalAnnotations()).containsExactly("javax.annotation.Nullable");
@@ -80,13 +99,14 @@ void loadDefaultProps() {
@Test
void wrongTarget() {
- assertThatThrownBy(() -> PropsFactory.loadProps(resolveResource("test-sharedtype-wrong-target.properties")))
- .isInstanceOf(SharedTypeException.class);
+ var userProps = Map.of("sharedtype.targets", "ENGLISH");
+ assertThatThrownBy(() -> PropsFactory.loadProps(null, userProps)).isInstanceOf(SharedTypeException.class);
}
@Test
void wrongTypescriptOptionalFieldFormat() {
- assertThatThrownBy(() -> PropsFactory.loadProps(resolveResource("test-sharedtype-wrong-ts-optional-field-format.properties")))
+ var userProps = Map.of("sharedtype.typescript.optional-field-format", "abc,?");
+ assertThatThrownBy(() -> PropsFactory.loadProps(null, userProps))
.isInstanceOf(SharedTypeException.class)
.cause().cause()
.hasMessageContaining("Unknown optional field format: 'abc', only '?', 'null', 'undefined' are allowed");
@@ -94,7 +114,7 @@ void wrongTypescriptOptionalFieldFormat() {
@Test
void typeMappings() {
- Props props = PropsFactory.loadProps(resolveResource("test-sharedtype-type-mappings.properties"));
+ Props props = PropsFactory.loadProps(resolveResource("test-sharedtype-type-mappings.properties"), null);
assertThat(props.getTypescript().getTypeMappings()).containsExactly(
entry("MyType1", "RenamedType1"),
entry("MyType2", "RenamedType2")
@@ -103,7 +123,9 @@ void typeMappings() {
private static Path resolveResource(String resource) {
try {
- return Paths.get(PropsFactoryTest.class.getClassLoader().getResource(resource).toURI());
+ var testRes = PropsFactoryTest.class.getClassLoader().getResource(resource);
+ assert testRes != null;
+ return Paths.get(testRes.toURI());
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
diff --git a/processor/src/test/resources/test-sharedtype-wrong-target.properties b/processor/src/test/resources/test-sharedtype-wrong-target.properties
deleted file mode 100644
index 6ca45e9b..00000000
--- a/processor/src/test/resources/test-sharedtype-wrong-target.properties
+++ /dev/null
@@ -1 +0,0 @@
-sharedtype.targets=ENGLISH,
diff --git a/processor/src/test/resources/test-sharedtype-wrong-ts-optional-field-format.properties b/processor/src/test/resources/test-sharedtype-wrong-ts-optional-field-format.properties
deleted file mode 100644
index 0451512d..00000000
--- a/processor/src/test/resources/test-sharedtype-wrong-ts-optional-field-format.properties
+++ /dev/null
@@ -1 +0,0 @@
-sharedtype.typescript.optional-field-format=abc,?
From 2291aa4b1346e6761992df174f4e224a8dd0c1c0 Mon Sep 17 00:00:00 2001
From: Cause Chung
Date: Sat, 28 Jun 2025 11:52:55 -0400
Subject: [PATCH 09/17] Pass individual user props from plugin config
---
maven-plugin/it/pom.xml | 3 +++
.../it/src/test/resources/expected-types.ts | 3 +++
.../sharedtype/maven/SharedTypeGenMojo.java | 15 ++++++++++++++-
3 files changed, 20 insertions(+), 1 deletion(-)
diff --git a/maven-plugin/it/pom.xml b/maven-plugin/it/pom.xml
index 1be983df..24850c49 100644
--- a/maven-plugin/it/pom.xml
+++ b/maven-plugin/it/pom.xml
@@ -39,6 +39,9 @@
${project.version}
${project.basedir}/sharedtype.properties
+
+ ${project.parent.basedir}/it/custom-code.ts
+
diff --git a/maven-plugin/it/src/test/resources/expected-types.ts b/maven-plugin/it/src/test/resources/expected-types.ts
index 4470982d..59586d90 100644
--- a/maven-plugin/it/src/test/resources/expected-types.ts
+++ b/maven-plugin/it/src/test/resources/expected-types.ts
@@ -1,5 +1,8 @@
// Code generated by https://github.com/SharedType/sharedtype
+// test code snippet
+export interface CustomCode {
+}
export interface MyClass {
diff --git a/maven-plugin/src/main/java/online/sharedtype/maven/SharedTypeGenMojo.java b/maven-plugin/src/main/java/online/sharedtype/maven/SharedTypeGenMojo.java
index 31e60865..1e820d0f 100644
--- a/maven-plugin/src/main/java/online/sharedtype/maven/SharedTypeGenMojo.java
+++ b/maven-plugin/src/main/java/online/sharedtype/maven/SharedTypeGenMojo.java
@@ -30,6 +30,7 @@
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
+import java.util.Map;
/**
* Generate types from {@link SharedType} annotated classes.
@@ -55,11 +56,21 @@ public final class SharedTypeGenMojo extends AbstractMojo {
/**
* The path of file 'sharedtype.properties'. If not provided, default values will be used. If provided, the file must exist.
+ * User-provided properties will be checked in below order:
+ * 1. 'sharedtype.properties' file set by this config, 2. individual properties set by this plugin's 'properties' config, 3. System properties.
*/
@Nullable
@Parameter
private String propertyFile;
+ /**
+ * Sharedtype properties. See doc for all the property entries. User-provided properties will be checked in below order:
+ * 1. 'sharedtype.properties' file, 2. this config, 3. System properties.
+ */
+ @Nullable
+ @Parameter
+ private Map properties;
+
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
JavaCompiler compiler = getJavaCompiler();
@@ -78,7 +89,9 @@ public void execute() throws MojoExecutionException, MojoFailureException {
}
Iterable extends JavaFileObject> sources = fileManager.getJavaFileObjectsFromFiles(walkAllSourceFiles());
JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, getCompilerOptions(), null, sources);
- task.setProcessors(Collections.singleton(new SharedTypeAnnotationProcessor()));
+ SharedTypeAnnotationProcessor annotationProcessor = new SharedTypeAnnotationProcessor();
+ annotationProcessor.setUserProps(properties);
+ task.setProcessors(Collections.singleton(annotationProcessor));
task.call();
}
From eb57e22a3c299bd8bfea61a5f6785eeb54b812cb Mon Sep 17 00:00:00 2001
From: Cause Chung
Date: Sat, 28 Jun 2025 12:32:28 -0400
Subject: [PATCH 10/17] Failfast when custom code file does not exist
---
.../java/online/sharedtype/SharedType.java | 3 +--
.../sharedtype/processor/context/Props.java | 11 +++++++---
.../processor/context/PropsFactory.java | 21 +++++++++++++++----
.../writer/adaptor/AbstractDataAdaptor.java | 11 ++++++----
.../resources/sharedtype-default.properties | 15 ++++++-------
.../processor/context/PropsFactoryTest.java | 12 ++++++++---
.../TypescriptHeaderDataAdaptorTest.java | 9 ++++----
7 files changed, 53 insertions(+), 29 deletions(-)
diff --git a/annotation/src/main/java/online/sharedtype/SharedType.java b/annotation/src/main/java/online/sharedtype/SharedType.java
index e244b613..9f71876b 100644
--- a/annotation/src/main/java/online/sharedtype/SharedType.java
+++ b/annotation/src/main/java/online/sharedtype/SharedType.java
@@ -157,8 +157,7 @@
* Custom code snippet:
* Clients can provide custom code snippets to be injected into the emitted file.
* This can be useful when e.g. a 3rd party type is referenced at client code.
- * By default, SharedType will search files "sharedtype-custom-code.ts", "sharedtype-custom-code.rs" respectively on cmd path.
- * File paths can be configured via global properties.
+ * Custom code file paths can be configured via global properties.
*
*
*
diff --git a/processor/src/main/java/online/sharedtype/processor/context/Props.java b/processor/src/main/java/online/sharedtype/processor/context/Props.java
index 4901c2ec..d7e526f5 100644
--- a/processor/src/main/java/online/sharedtype/processor/context/Props.java
+++ b/processor/src/main/java/online/sharedtype/processor/context/Props.java
@@ -5,7 +5,9 @@
import lombok.Getter;
import lombok.experimental.Accessors;
import online.sharedtype.SharedType;
+import online.sharedtype.processor.support.annotation.Nullable;
+import java.nio.file.Path;
import java.util.Map;
import java.util.Set;
@@ -47,7 +49,8 @@ public static final class Typescript {
private final EnumFormat enumFormat;
private final FieldReadonlyType fieldReadonlyType;
private final Map typeMappings;
- private final String customCodePath;
+ @Nullable
+ private final Path customCodePath;
@Getter
public enum OptionalFieldFormat {
@@ -95,7 +98,8 @@ public static final class Go {
private final String targetDatetimeTypeLiteral;
private final EnumFormat enumFormat;
private final Map typeMappings;
- private final String customCodePath;
+ @Nullable
+ private final Path customCodePath;
public enum EnumFormat {
CONST, STRUCT,
@@ -118,6 +122,7 @@ public static final class Rust {
private final Set defaultTypeMacros;
private final String targetDatetimeTypeLiteral;
private final Map typeMappings;
- private final String customCodePath;
+ @Nullable
+ private final Path customCodePath;
}
}
diff --git a/processor/src/main/java/online/sharedtype/processor/context/PropsFactory.java b/processor/src/main/java/online/sharedtype/processor/context/PropsFactory.java
index 67461182..d0f9b749 100644
--- a/processor/src/main/java/online/sharedtype/processor/context/PropsFactory.java
+++ b/processor/src/main/java/online/sharedtype/processor/context/PropsFactory.java
@@ -1,11 +1,12 @@
package online.sharedtype.processor.context;
+import online.sharedtype.processor.support.annotation.Nullable;
import online.sharedtype.processor.support.exception.SharedTypeException;
-import online.sharedtype.processor.support.annotation.Nullable;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.nio.file.Paths;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
@@ -74,7 +75,7 @@ private static Props convertProps(Properties properties) {
.enumFormat(parseEnum(properties, "sharedtype.typescript.enum-format", Props.Typescript.EnumFormat::fromString))
.fieldReadonlyType(parseEnum(properties, "sharedtype.typescript.field-readonly-type", Props.Typescript.FieldReadonlyType::fromString))
.typeMappings(parseMap(properties, "sharedtype.typescript.type-mappings"))
- .customCodePath(properties.getProperty("sharedtype.typescript.custom-code-path"))
+ .customCodePath(resolvePath(properties, "sharedtype.typescript.custom-code-path"))
.build())
.go(Props.Go.builder()
.outputFileName(properties.getProperty("sharedtype.go.output-file-name"))
@@ -83,7 +84,7 @@ private static Props convertProps(Properties properties) {
.targetDatetimeTypeLiteral(properties.getProperty("sharedtype.go.target-datetime-type"))
.enumFormat(parseEnum(properties, "sharedtype.go.enum-format", Props.Go.EnumFormat::fromString))
.typeMappings(parseMap(properties, "sharedtype.go.type-mappings"))
- .customCodePath(properties.getProperty("sharedtype.go.custom-code-path"))
+ .customCodePath(resolvePath(properties,"sharedtype.go.custom-code-path"))
.build())
.rust(Props.Rust.builder()
.outputFileName(properties.getProperty("sharedtype.rust.output-file-name"))
@@ -92,7 +93,7 @@ private static Props convertProps(Properties properties) {
.defaultTypeMacros(splitArray(properties.getProperty("sharedtype.rust.default-macros-traits")))
.targetDatetimeTypeLiteral(properties.getProperty("sharedtype.rust.target-datetime-type"))
.typeMappings(parseMap(properties, "sharedtype.rust.type-mappings"))
- .customCodePath(properties.getProperty("sharedtype.rust.custom-code-path"))
+ .customCodePath(resolvePath(properties, "sharedtype.rust.custom-code-path"))
.hasEnumValueTypeAlias(parseBoolean(properties, "sharedtype.rust.enum-value-type-alias"))
.build())
.build();
@@ -170,6 +171,18 @@ private static Map parseMap(Properties properties, String proper
return map;
}
+ private static Path resolvePath(Properties properties, String propertyName) {
+ String value = properties.getProperty(propertyName);
+ if (value == null || value.isEmpty()) {
+ return null;
+ }
+ Path path = Paths.get(value);
+ if (Files.notExists(path)) {
+ throw new IllegalArgumentException(String.format("Invalid property %s=%s, file of path does not exist.", propertyName, path));
+ }
+ return path;
+ }
+
@SuppressWarnings("unchecked")
private static Class extends T> parseClass(String className) throws ClassNotFoundException {
return (Class extends T>) Class.forName(className);
diff --git a/processor/src/main/java/online/sharedtype/processor/writer/adaptor/AbstractDataAdaptor.java b/processor/src/main/java/online/sharedtype/processor/writer/adaptor/AbstractDataAdaptor.java
index ded1ec60..9766a807 100644
--- a/processor/src/main/java/online/sharedtype/processor/writer/adaptor/AbstractDataAdaptor.java
+++ b/processor/src/main/java/online/sharedtype/processor/writer/adaptor/AbstractDataAdaptor.java
@@ -3,12 +3,12 @@
import lombok.RequiredArgsConstructor;
import online.sharedtype.processor.context.Context;
import online.sharedtype.processor.context.RenderFlags;
+import online.sharedtype.processor.support.annotation.Nullable;
import online.sharedtype.processor.support.exception.SharedTypeException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.nio.file.Paths;
@RequiredArgsConstructor
abstract class AbstractDataAdaptor implements RenderDataAdaptor {
@@ -21,12 +21,15 @@ final RenderFlags renderFlags() {
abstract String customCodeSnippet();
- static String readCustomCodeSnippet(String path) {
- Path customCodePath = Paths.get(path);
- if (Files.notExists(customCodePath)) {
+ static String readCustomCodeSnippet(@Nullable Path customCodePath) {
+ if (customCodePath == null) {
return "";
}
+ if (Files.notExists(customCodePath)) { // this should be checked before calling this method when properties are loaded
+ throw new SharedTypeException(String.format("Custom code snippet not found at path '%s'", customCodePath));
+ }
+
try {
return new String(Files.readAllBytes(customCodePath));
} catch (IOException e) {
diff --git a/processor/src/main/resources/sharedtype-default.properties b/processor/src/main/resources/sharedtype-default.properties
index 7c56333d..6dd1f756 100644
--- a/processor/src/main/resources/sharedtype-default.properties
+++ b/processor/src/main/resources/sharedtype-default.properties
@@ -83,9 +83,8 @@ sharedtype.typescript.field-readonly-type=acyclic
sharedtype.typescript.type-mappings=
## Path to custom code snippet file, code snippet in the file will be injected into the top of generated file
-## If the file does not exist, it will be ignored
-## Below default assumes the file is on the cmd execution path
-sharedtype.typescript.custom-code-path=sharedtype-custom-code.ts
+## If the file of the cannot be found, an error will be thrown
+sharedtype.typescript.custom-code-path=
##############################
# Golang specific properties #
@@ -112,9 +111,8 @@ sharedtype.go.enum-format=const
sharedtype.go.type-mappings=
## Path to custom code snippet file, code snippet in the file will be injected into the top of generated file
-## If the file does not exist, it will be ignored
-## Below default assumes the file is on the cmd execution path
-sharedtype.go.custom-code-path=sharedtype-custom-code.go
+## If the file of the cannot be found, an error will be thrown
+sharedtype.go.custom-code-path=
############################
# Rust specific properties #
@@ -146,6 +144,5 @@ sharedtype.rust.target-datetime-type=String
sharedtype.rust.type-mappings=
## Path to custom code snippet file, code snippet in the file will be injected into the top of generated file
-## If the file does not exist, it will be ignored
-## Below default assumes the file is on the cmd execution path
-sharedtype.rust.custom-code-path=sharedtype-custom-code.rs
+## If the file of the cannot be found, an error will be thrown
+sharedtype.rust.custom-code-path=
diff --git a/processor/src/test/java/online/sharedtype/processor/context/PropsFactoryTest.java b/processor/src/test/java/online/sharedtype/processor/context/PropsFactoryTest.java
index 068456d9..9bbb2520 100644
--- a/processor/src/test/java/online/sharedtype/processor/context/PropsFactoryTest.java
+++ b/processor/src/test/java/online/sharedtype/processor/context/PropsFactoryTest.java
@@ -75,7 +75,7 @@ void loadDefaultProps() {
assertThat(typescriptProps.getEnumFormat()).isEqualTo(Props.Typescript.EnumFormat.UNION);
assertThat(typescriptProps.getFieldReadonlyType()).isEqualTo(Props.Typescript.FieldReadonlyType.ACYCLIC);
assertThat(typescriptProps.getTypeMappings()).isEmpty();
- assertThat(typescriptProps.getCustomCodePath()).isEqualTo("sharedtype-custom-code.ts");
+ assertThat(typescriptProps.getCustomCodePath()).isNull();
Props.Go goProps = props.getGo();
assertThat(goProps.getOutputFileName()).isEqualTo("types.go");
@@ -84,7 +84,7 @@ void loadDefaultProps() {
assertThat(goProps.getTargetDatetimeTypeLiteral()).isEqualTo("string");
assertThat(goProps.getEnumFormat()).isEqualTo(Props.Go.EnumFormat.CONST);
assertThat(goProps.getTypeMappings()).isEmpty();
- assertThat(goProps.getCustomCodePath()).isEqualTo("sharedtype-custom-code.go");
+ assertThat(goProps.getCustomCodePath()).isNull();
Props.Rust rustProps = props.getRust();
assertThat(rustProps.getOutputFileName()).isEqualTo("types.rs");
@@ -94,7 +94,13 @@ void loadDefaultProps() {
assertThat(rustProps.getDefaultTypeMacros()).containsExactly("Debug");
assertThat(rustProps.getTargetDatetimeTypeLiteral()).isEqualTo("String");
assertThat(rustProps.getTypeMappings()).isEmpty();
- assertThat(rustProps.getCustomCodePath()).isEqualTo("sharedtype-custom-code.rs");
+ assertThat(rustProps.getCustomCodePath()).isNull();
+ }
+
+ @Test
+ void failWhenCustomCodeFilePathProvidedButNotExists() {
+ var userProps = Map.of("sharedtype.typescript.custom-code-path", "not-exists.ts");
+ assertThatThrownBy(() -> PropsFactory.loadProps(null, userProps)).isInstanceOf(SharedTypeException.class);
}
@Test
diff --git a/processor/src/test/java/online/sharedtype/processor/writer/adaptor/TypescriptHeaderDataAdaptorTest.java b/processor/src/test/java/online/sharedtype/processor/writer/adaptor/TypescriptHeaderDataAdaptorTest.java
index ee374a11..6274f3a2 100644
--- a/processor/src/test/java/online/sharedtype/processor/writer/adaptor/TypescriptHeaderDataAdaptorTest.java
+++ b/processor/src/test/java/online/sharedtype/processor/writer/adaptor/TypescriptHeaderDataAdaptorTest.java
@@ -4,7 +4,10 @@
import org.junit.jupiter.api.Test;
import org.junitpioneer.jupiter.SetSystemProperty;
+import java.nio.file.Paths;
+
import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
final class TypescriptHeaderDataAdaptorTest {
@@ -16,11 +19,9 @@ void readCustomCodeSnippet() {
assertThat(adaptor.customCodeSnippet()).isEqualTo("interface A {}" + System.lineSeparator());
}
- @SetSystemProperty(key = "sharedtype.typescript.custom-code-path", value = "not-exists.ts")
@Test
void customCodeSnippetNoFile() {
- ContextMocks ctxMocks = new ContextMocks();
- TypescriptHeaderDataAdaptor adaptor = new TypescriptHeaderDataAdaptor(ctxMocks.getContext());
- assertThat(adaptor.customCodeSnippet()).isEqualTo("");
+ assertThatThrownBy(() -> TypescriptHeaderDataAdaptor.readCustomCodeSnippet(Paths.get("not-exists.ts")));
+ assertThat(TypescriptHeaderDataAdaptor.readCustomCodeSnippet(null)).isEqualTo("");
}
}
From d572f8c66bb0eadaff72caab8f9c36127129cc9b Mon Sep 17 00:00:00 2001
From: Cause Chung
Date: Sun, 29 Jun 2025 10:25:04 -0400
Subject: [PATCH 11/17] Use generate-test-resources phase for sharedtype:gen in
maven-plugin it
---
maven-plugin/it/pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/maven-plugin/it/pom.xml b/maven-plugin/it/pom.xml
index 24850c49..970ec252 100644
--- a/maven-plugin/it/pom.xml
+++ b/maven-plugin/it/pom.xml
@@ -46,7 +46,7 @@
gen-for-testing
- compile
+ generate-test-resources
gen
From ec31acedf02343832b627adb0a68951d7ee8db0d Mon Sep 17 00:00:00 2001
From: Cause Chung
Date: Sun, 29 Jun 2025 11:08:45 -0400
Subject: [PATCH 12/17] Add maven 3.2 compatibility test
---
.github/workflows/ci.yaml | 3 +
.../it/.mvn/wrapper/maven-wrapper.properties | 21 ++
maven-plugin/it/mvnw | 259 ++++++++++++++++++
maven-plugin/pom.xml | 3 +
.../sharedtype/maven/DependencyResolver.java | 3 +
.../sharedtype/maven/SourceFileVisitor.java | 3 +
pom.xml | 1 +
7 files changed, 293 insertions(+)
create mode 100644 maven-plugin/it/.mvn/wrapper/maven-wrapper.properties
create mode 100755 maven-plugin/it/mvnw
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index de522b67..38b82801 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -48,6 +48,9 @@ jobs:
run: ./mvnw install -e --ntp -B
- name: Test JPMS
run: ./mvnw clean verify -pl it/java8 -P it-jpms -e --ntp -B
+ - name: Test Maven 3.2.5 compatibility
+ working-directory: maven-plugin/it
+ run: ./mvnw stype:gen -e --ntp -B
- name: Javadoc
run: ./mvnw -P release javadoc:javadoc --ntp -B
- name: Upload generated sources
diff --git a/maven-plugin/it/.mvn/wrapper/maven-wrapper.properties b/maven-plugin/it/.mvn/wrapper/maven-wrapper.properties
new file mode 100644
index 00000000..7bf98fb4
--- /dev/null
+++ b/maven-plugin/it/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1,21 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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
+#
+# http://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.
+wrapperVersion=3.3.2
+distributionType=only-script
+
+# This is for Maven compatibility test
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.2.5/apache-maven-3.2.5-bin.zip
diff --git a/maven-plugin/it/mvnw b/maven-plugin/it/mvnw
new file mode 100755
index 00000000..19529ddf
--- /dev/null
+++ b/maven-plugin/it/mvnw
@@ -0,0 +1,259 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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
+#
+# http://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.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Apache Maven Wrapper startup batch script, version 3.3.2
+#
+# Optional ENV vars
+# -----------------
+# JAVA_HOME - location of a JDK home dir, required when download maven via java source
+# MVNW_REPOURL - repo url base for downloading maven distribution
+# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
+# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output
+# ----------------------------------------------------------------------------
+
+set -euf
+[ "${MVNW_VERBOSE-}" != debug ] || set -x
+
+# OS specific support.
+native_path() { printf %s\\n "$1"; }
+case "$(uname)" in
+CYGWIN* | MINGW*)
+ [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")"
+ native_path() { cygpath --path --windows "$1"; }
+ ;;
+esac
+
+# set JAVACMD and JAVACCMD
+set_java_home() {
+ # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched
+ 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"
+ JAVACCMD="$JAVA_HOME/jre/sh/javac"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ JAVACCMD="$JAVA_HOME/bin/javac"
+
+ if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then
+ echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2
+ echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2
+ return 1
+ fi
+ fi
+ else
+ JAVACMD="$(
+ 'set' +e
+ 'unset' -f command 2>/dev/null
+ 'command' -v java
+ )" || :
+ JAVACCMD="$(
+ 'set' +e
+ 'unset' -f command 2>/dev/null
+ 'command' -v javac
+ )" || :
+
+ if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then
+ echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2
+ return 1
+ fi
+ fi
+}
+
+# hash string like Java String::hashCode
+hash_string() {
+ str="${1:-}" h=0
+ while [ -n "$str" ]; do
+ char="${str%"${str#?}"}"
+ h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296))
+ str="${str#?}"
+ done
+ printf %x\\n $h
+}
+
+verbose() { :; }
+[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; }
+
+die() {
+ printf %s\\n "$1" >&2
+ exit 1
+}
+
+trim() {
+ # MWRAPPER-139:
+ # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds.
+ # Needed for removing poorly interpreted newline sequences when running in more
+ # exotic environments such as mingw bash on Windows.
+ printf "%s" "${1}" | tr -d '[:space:]'
+}
+
+# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties
+while IFS="=" read -r key value; do
+ case "${key-}" in
+ distributionUrl) distributionUrl=$(trim "${value-}") ;;
+ distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;;
+ esac
+done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties"
+[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties"
+
+case "${distributionUrl##*/}" in
+maven-mvnd-*bin.*)
+ MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/
+ case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in
+ *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;;
+ :Darwin*x86_64) distributionPlatform=darwin-amd64 ;;
+ :Darwin*arm64) distributionPlatform=darwin-aarch64 ;;
+ :Linux*x86_64*) distributionPlatform=linux-amd64 ;;
+ *)
+ echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2
+ distributionPlatform=linux-amd64
+ ;;
+ esac
+ distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip"
+ ;;
+maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;;
+*) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;
+esac
+
+# apply MVNW_REPOURL and calculate MAVEN_HOME
+# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/
+[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}"
+distributionUrlName="${distributionUrl##*/}"
+distributionUrlNameMain="${distributionUrlName%.*}"
+distributionUrlNameMain="${distributionUrlNameMain%-bin}"
+MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}"
+MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")"
+
+exec_maven() {
+ unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || :
+ exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD"
+}
+
+if [ -d "$MAVEN_HOME" ]; then
+ verbose "found existing MAVEN_HOME at $MAVEN_HOME"
+ exec_maven "$@"
+fi
+
+case "${distributionUrl-}" in
+*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;;
+*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;;
+esac
+
+# prepare tmp dir
+if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then
+ clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; }
+ trap clean HUP INT TERM EXIT
+else
+ die "cannot create temp dir"
+fi
+
+mkdir -p -- "${MAVEN_HOME%/*}"
+
+# Download and Install Apache Maven
+verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
+verbose "Downloading from: $distributionUrl"
+verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
+
+# select .zip or .tar.gz
+if ! command -v unzip >/dev/null; then
+ distributionUrl="${distributionUrl%.zip}.tar.gz"
+ distributionUrlName="${distributionUrl##*/}"
+fi
+
+# verbose opt
+__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR=''
+[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v
+
+# normalize http auth
+case "${MVNW_PASSWORD:+has-password}" in
+'') MVNW_USERNAME='' MVNW_PASSWORD='' ;;
+has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;;
+esac
+
+if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then
+ verbose "Found wget ... using wget"
+ wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl"
+elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then
+ verbose "Found curl ... using curl"
+ curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl"
+elif set_java_home; then
+ verbose "Falling back to use Java to download"
+ javaSource="$TMP_DOWNLOAD_DIR/Downloader.java"
+ targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName"
+ cat >"$javaSource" <<-END
+ public class Downloader extends java.net.Authenticator
+ {
+ protected java.net.PasswordAuthentication getPasswordAuthentication()
+ {
+ return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() );
+ }
+ public static void main( String[] args ) throws Exception
+ {
+ setDefault( new Downloader() );
+ java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() );
+ }
+ }
+ END
+ # For Cygwin/MinGW, switch paths to Windows format before running javac and java
+ verbose " - Compiling Downloader.java ..."
+ "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java"
+ verbose " - Running Downloader.java ..."
+ "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")"
+fi
+
+# If specified, validate the SHA-256 sum of the Maven distribution zip file
+if [ -n "${distributionSha256Sum-}" ]; then
+ distributionSha256Result=false
+ if [ "$MVN_CMD" = mvnd.sh ]; then
+ echo "Checksum validation is not supported for maven-mvnd." >&2
+ echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
+ exit 1
+ elif command -v sha256sum >/dev/null; then
+ if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then
+ distributionSha256Result=true
+ fi
+ elif command -v shasum >/dev/null; then
+ if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then
+ distributionSha256Result=true
+ fi
+ else
+ echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2
+ echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
+ exit 1
+ fi
+ if [ $distributionSha256Result = false ]; then
+ echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2
+ echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2
+ exit 1
+ fi
+fi
+
+# unzip and move
+if command -v unzip >/dev/null; then
+ unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip"
+else
+ tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar"
+fi
+printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url"
+mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"
+
+clean || :
+exec_maven "$@"
diff --git a/maven-plugin/pom.xml b/maven-plugin/pom.xml
index 1f792a49..307ea4cc 100644
--- a/maven-plugin/pom.xml
+++ b/maven-plugin/pom.xml
@@ -111,6 +111,9 @@
org.apache.maven.plugins
maven-plugin-plugin
${maven-plugin-tools.version}
+
+ stype
+
help-mojo
diff --git a/maven-plugin/src/main/java/online/sharedtype/maven/DependencyResolver.java b/maven-plugin/src/main/java/online/sharedtype/maven/DependencyResolver.java
index d0aa9903..f1cecb9e 100644
--- a/maven-plugin/src/main/java/online/sharedtype/maven/DependencyResolver.java
+++ b/maven-plugin/src/main/java/online/sharedtype/maven/DependencyResolver.java
@@ -14,6 +14,9 @@
import java.util.List;
import java.util.stream.Collectors;
+/**
+ * @author Cause Chung
+ */
final class DependencyResolver {
private final RepositorySystem repositorySystem;
private final MavenSession session;
diff --git a/maven-plugin/src/main/java/online/sharedtype/maven/SourceFileVisitor.java b/maven-plugin/src/main/java/online/sharedtype/maven/SourceFileVisitor.java
index f23e119d..586e2ce6 100644
--- a/maven-plugin/src/main/java/online/sharedtype/maven/SourceFileVisitor.java
+++ b/maven-plugin/src/main/java/online/sharedtype/maven/SourceFileVisitor.java
@@ -8,6 +8,9 @@
import java.util.ArrayList;
import java.util.List;
+/**
+ * @author Cause Chung
+ */
final class SourceFileVisitor extends SimpleFileVisitor {
private static final String FILE_EXTENSION = ".java";
private final List files = new ArrayList<>();
diff --git a/pom.xml b/pom.xml
index 0dd531c1..55607f90 100644
--- a/pom.xml
+++ b/pom.xml
@@ -285,6 +285,7 @@
annotation
processor
+ maven-plugin
From c54f18324c9e828bbbe03b4179a6023a6408dd3d Mon Sep 17 00:00:00 2001
From: Cause Chung
Date: Mon, 30 Jun 2025 12:54:44 -0400
Subject: [PATCH 13/17] Add diagnostic logging for maven plugin
---
maven-plugin/it/sharedtype.properties | 2 +-
.../online/sharedtype/maven/it/MyClass.java | 7 +++
.../it/src/test/resources/expected-types.ts | 4 ++
maven-plugin/pom.xml | 9 +++
.../maven/SharedTypeDiagnosticListener.java | 56 +++++++++++++++++++
.../sharedtype/maven/SharedTypeGenMojo.java | 14 +++--
.../sharedtype/maven/SharedTypeLogger.java | 44 +++++++++++++++
.../SharedTypeDiagnosticListenerTest.java | 53 ++++++++++++++++++
.../maven/SharedTypeLoggerTest.java | 35 ++++++++++++
processor/pom.xml | 4 +-
.../SharedTypeAnnotationProcessor.java | 6 +-
setenv | 2 +-
12 files changed, 226 insertions(+), 10 deletions(-)
create mode 100644 maven-plugin/src/main/java/online/sharedtype/maven/SharedTypeDiagnosticListener.java
create mode 100644 maven-plugin/src/main/java/online/sharedtype/maven/SharedTypeLogger.java
create mode 100644 maven-plugin/src/test/java/online/sharedtype/maven/SharedTypeDiagnosticListenerTest.java
create mode 100644 maven-plugin/src/test/java/online/sharedtype/maven/SharedTypeLoggerTest.java
diff --git a/maven-plugin/it/sharedtype.properties b/maven-plugin/it/sharedtype.properties
index 1e5e525d..19ee8ccd 100644
--- a/maven-plugin/it/sharedtype.properties
+++ b/maven-plugin/it/sharedtype.properties
@@ -1 +1 @@
-sharedtype.targets=CONSOLE,TYPESCRIPT
+sharedtype.targets=TYPESCRIPT
diff --git a/maven-plugin/it/src/main/java/online/sharedtype/maven/it/MyClass.java b/maven-plugin/it/src/main/java/online/sharedtype/maven/it/MyClass.java
index dee8f413..513ea98c 100644
--- a/maven-plugin/it/src/main/java/online/sharedtype/maven/it/MyClass.java
+++ b/maven-plugin/it/src/main/java/online/sharedtype/maven/it/MyClass.java
@@ -2,7 +2,14 @@
import online.sharedtype.SharedType;
+import java.util.Optional;
+
@SharedType
final class MyClass {
private int value;
}
+
+@SharedType
+final class MyClass2> {
+ private T value;
+}
diff --git a/maven-plugin/it/src/test/resources/expected-types.ts b/maven-plugin/it/src/test/resources/expected-types.ts
index 59586d90..f328f010 100644
--- a/maven-plugin/it/src/test/resources/expected-types.ts
+++ b/maven-plugin/it/src/test/resources/expected-types.ts
@@ -9,3 +9,7 @@ export interface MyClass {
readonly value: number;
}
+export interface MyClass2 {
+ readonly value: T;
+}
+
diff --git a/maven-plugin/pom.xml b/maven-plugin/pom.xml
index 307ea4cc..f5727706 100644
--- a/maven-plugin/pom.xml
+++ b/maven-plugin/pom.xml
@@ -124,6 +124,15 @@
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ -javaagent:${settings.localRepository}/org/mockito/mockito-core/${mockito.version}/mockito-core-${mockito.version}.jar
+
+
+
diff --git a/maven-plugin/src/main/java/online/sharedtype/maven/SharedTypeDiagnosticListener.java b/maven-plugin/src/main/java/online/sharedtype/maven/SharedTypeDiagnosticListener.java
new file mode 100644
index 00000000..7f0c372f
--- /dev/null
+++ b/maven-plugin/src/main/java/online/sharedtype/maven/SharedTypeDiagnosticListener.java
@@ -0,0 +1,56 @@
+package online.sharedtype.maven;
+
+import com.google.common.annotations.VisibleForTesting;
+import online.sharedtype.processor.support.annotation.SideEffect;
+import org.apache.maven.plugin.logging.Log;
+
+import javax.tools.Diagnostic;
+import javax.tools.DiagnosticListener;
+import javax.tools.JavaFileObject;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+final class SharedTypeDiagnosticListener implements DiagnosticListener {
+ private final Log log;
+ private final Path projectBaseDir;
+ SharedTypeDiagnosticListener(Log log, Path projectBaseDir) {
+ this.log = log;
+ this.projectBaseDir = projectBaseDir;
+ }
+
+ @Override
+ public void report(Diagnostic extends JavaFileObject> diagnostic) {
+ StringBuilder sb = new StringBuilder();
+
+ addSourceInfo(sb, diagnostic);
+ sb.append(diagnostic.getMessage(null));
+ String message = sb.toString();
+ switch (diagnostic.getKind()) {
+ case NOTE:
+ case OTHER:
+ log.info(message);
+ break;
+ case WARNING:
+ case MANDATORY_WARNING:
+ log.warn(message);
+ break;
+ case ERROR:
+ log.error(message);
+ break;
+ }
+ }
+
+ @VisibleForTesting
+ void addSourceInfo(@SideEffect StringBuilder sb, Diagnostic extends JavaFileObject> diagnostic) {
+ if (diagnostic.getSource() == null) {
+ return;
+ }
+ JavaFileObject source = diagnostic.getSource();
+ sb.append(projectBaseDir.relativize(Paths.get(source.getName())));
+ sb.append(':');
+ sb.append(diagnostic.getLineNumber());
+ sb.append(':');
+ sb.append(diagnostic.getColumnNumber());
+ sb.append(" ");
+ }
+}
diff --git a/maven-plugin/src/main/java/online/sharedtype/maven/SharedTypeGenMojo.java b/maven-plugin/src/main/java/online/sharedtype/maven/SharedTypeGenMojo.java
index 1e820d0f..b25bd1dc 100644
--- a/maven-plugin/src/main/java/online/sharedtype/maven/SharedTypeGenMojo.java
+++ b/maven-plugin/src/main/java/online/sharedtype/maven/SharedTypeGenMojo.java
@@ -73,6 +73,7 @@ public final class SharedTypeGenMojo extends AbstractMojo {
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
+ SharedTypeDiagnosticListener diagnosticListener = new SharedTypeDiagnosticListener(getLog(), project.getBasedir().toPath());
JavaCompiler compiler = getJavaCompiler();
DependencyResolver dependencyResolver = new DependencyResolver(repositorySystem, session, project);
@@ -88,11 +89,14 @@ public void execute() throws MojoExecutionException, MojoFailureException {
throw new MojoExecutionException(e);
}
Iterable extends JavaFileObject> sources = fileManager.getJavaFileObjectsFromFiles(walkAllSourceFiles());
- JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, getCompilerOptions(), null, sources);
- SharedTypeAnnotationProcessor annotationProcessor = new SharedTypeAnnotationProcessor();
- annotationProcessor.setUserProps(properties);
- task.setProcessors(Collections.singleton(annotationProcessor));
- task.call();
+
+ try(SharedTypeLogger logger = new SharedTypeLogger(getLog())) {
+ JavaCompiler.CompilationTask task = compiler.getTask(logger, fileManager, diagnosticListener, getCompilerOptions(), null, sources);
+ SharedTypeAnnotationProcessor annotationProcessor = new SharedTypeAnnotationProcessor();
+ annotationProcessor.setUserProps(properties);
+ task.setProcessors(Collections.singleton(annotationProcessor));
+ task.call();
+ }
}
private List walkAllSourceFiles() throws MojoExecutionException {
diff --git a/maven-plugin/src/main/java/online/sharedtype/maven/SharedTypeLogger.java b/maven-plugin/src/main/java/online/sharedtype/maven/SharedTypeLogger.java
new file mode 100644
index 00000000..838cb9f7
--- /dev/null
+++ b/maven-plugin/src/main/java/online/sharedtype/maven/SharedTypeLogger.java
@@ -0,0 +1,44 @@
+package online.sharedtype.maven;
+
+import org.apache.maven.plugin.logging.Log;
+
+import java.io.Writer;
+
+final class SharedTypeLogger extends Writer {
+ private final Log log;
+ private final StringBuffer buffer = new StringBuffer();
+
+ SharedTypeLogger(Log log) {
+ this.log = log;
+ }
+
+ @Override
+ public void write(char[] cbuf, int off, int len) {
+ buffer.append(cbuf, off, len);
+ }
+
+ @Override
+ public void flush() {
+ removeNewLine(buffer);
+ String message = buffer.toString();
+ if (!message.isEmpty()) {
+ log.info(message);
+ }
+ buffer.setLength(0);
+ }
+
+ @Override
+ public void close() {
+ flush();
+ }
+
+ private static void removeNewLine(StringBuffer buffer) {
+ if (buffer.length() <= 0) {
+ return;
+ }
+ char c;
+ while ((c = buffer.charAt(buffer.length() - 1)) == '\n' || (c == '\r')) {
+ buffer.delete(buffer.length() - 1, buffer.length());
+ }
+ }
+}
diff --git a/maven-plugin/src/test/java/online/sharedtype/maven/SharedTypeDiagnosticListenerTest.java b/maven-plugin/src/test/java/online/sharedtype/maven/SharedTypeDiagnosticListenerTest.java
new file mode 100644
index 00000000..2d197761
--- /dev/null
+++ b/maven-plugin/src/test/java/online/sharedtype/maven/SharedTypeDiagnosticListenerTest.java
@@ -0,0 +1,53 @@
+package online.sharedtype.maven;
+
+import org.apache.maven.plugin.logging.Log;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import javax.tools.Diagnostic;
+import javax.tools.JavaFileObject;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+final class SharedTypeDiagnosticListenerTest {
+ private @Mock Diagnostic diagnostic;
+ private @Mock Log log;
+ private final Path baseDir = Paths.get("/test/project/dir");
+ private SharedTypeDiagnosticListener listener;
+
+ private @Mock JavaFileObject source;
+
+ @BeforeEach
+ void setup() {
+ listener = new SharedTypeDiagnosticListener(log, baseDir);
+ }
+
+ @Test
+ void skipIfNoSourceCode() {
+ when(diagnostic.getSource()).thenReturn(null);
+
+ var sb = new StringBuilder();
+ listener.addSourceInfo(sb, diagnostic);
+ assertThat(sb.toString()).isEmpty();
+ }
+
+ @Test
+ void printSourceInfo() {
+ when(diagnostic.getSource()).thenReturn(source);
+ when(source.getName()).thenReturn("/test/project/dir/src/test.java");
+ when(diagnostic.getLineNumber()).thenReturn(69L);
+ when(diagnostic.getColumnNumber()).thenReturn(102L);
+
+ var sb = new StringBuilder();
+ listener.addSourceInfo(sb, diagnostic);
+ assertThat(sb.toString()).isEqualTo("src/test.java:69:102 ");
+ }
+}
diff --git a/maven-plugin/src/test/java/online/sharedtype/maven/SharedTypeLoggerTest.java b/maven-plugin/src/test/java/online/sharedtype/maven/SharedTypeLoggerTest.java
new file mode 100644
index 00000000..d4fb1d30
--- /dev/null
+++ b/maven-plugin/src/test/java/online/sharedtype/maven/SharedTypeLoggerTest.java
@@ -0,0 +1,35 @@
+package online.sharedtype.maven;
+
+import org.apache.maven.plugin.logging.Log;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.verify;
+
+@ExtendWith(MockitoExtension.class)
+final class SharedTypeLoggerTest {
+ private @Mock Log log;
+ private SharedTypeLogger logger;
+ private @Captor ArgumentCaptor messageCaptor;
+
+ @BeforeEach
+ void setup() {
+ logger = new SharedTypeLogger(log);
+ }
+
+ @Test
+ void removeEndNewLine() throws Exception {
+ logger.append("some ");
+ logger.append("content\r\n\n");
+
+ logger.flush();
+ verify(log).info(messageCaptor.capture());
+ assertThat(messageCaptor.getValue()).isEqualTo("some content");
+ }
+}
diff --git a/processor/pom.xml b/processor/pom.xml
index 1dad7caf..32e1ace4 100644
--- a/processor/pom.xml
+++ b/processor/pom.xml
@@ -136,7 +136,9 @@
org.apache.maven.plugins
maven-surefire-plugin
- -javaagent:${org.mockito:mockito-core:jar}
+
+ -javaagent:${settings.localRepository}/org/mockito/mockito-core/${mockito.version}/mockito-core-${mockito.version}.jar
+
diff --git a/processor/src/main/java/online/sharedtype/processor/SharedTypeAnnotationProcessor.java b/processor/src/main/java/online/sharedtype/processor/SharedTypeAnnotationProcessor.java
index 7b48ad71..44769f2d 100644
--- a/processor/src/main/java/online/sharedtype/processor/SharedTypeAnnotationProcessor.java
+++ b/processor/src/main/java/online/sharedtype/processor/SharedTypeAnnotationProcessor.java
@@ -41,6 +41,7 @@
@AutoService(Processor.class)
public final class SharedTypeAnnotationProcessor extends AbstractProcessor {
private static final String PROPS_FILE_OPTION_NAME = "sharedtype.propsFile";
+ private static final String PROPS_ENABLED = "sharedtype.enabled";
private static final String DEFAULT_USER_PROPS_FILE = "sharedtype.properties";
private static final boolean ANNOTATION_CONSUMED = true;
/** Programmatically provided user properties, e.g. from Maven plugin */
@@ -94,7 +95,8 @@ void doProcess(Set extends Element> elements) {
List typeDefs = parser.parse(typeElement);
discoveredDefs.addAll(typeDefs);
if (typeDefs.isEmpty()){
- ctx.warn(element, "Type '%s' is ignored or invalid, but annotated with '%s'.", typeElement.getQualifiedName().toString(), ANNOTATION_QUALIFIED_NAME);
+ ctx.warn(element, "Type '%s' is ignored or invalid, but annotated with '%s'.",
+ typeElement.getQualifiedName().toString(), ANNOTATION_QUALIFIED_NAME);
}
} else {
throw new SharedTypeInternalError(String.format("Unsupported element: %s of kind %s", element, element.getKind()));
@@ -109,7 +111,7 @@ void doProcess(Set extends Element> elements) {
}
private static boolean isEnabled(ProcessingEnvironment processingEnv) {
- String enabledExpr = processingEnv.getOptions().getOrDefault("sharedtype.enabled", "false");
+ String enabledExpr = processingEnv.getOptions().getOrDefault(PROPS_ENABLED, "false");
return enabledExpr.equalsIgnoreCase("true") || enabledExpr.equalsIgnoreCase("yes");
}
}
diff --git a/setenv b/setenv
index 2d61d8bf..de1330f5 100755
--- a/setenv
+++ b/setenv
@@ -23,7 +23,7 @@ export MAVEN_OPTS="-Xmx512m -Xms512m"
export PATH=$JAVA_HOME/bin:$MVND_HOME/bin:$PATH
if [ -z "$MVND_HOME" ];then
- echo "MVND_HOME is not set, mvnd is recommended for local development, see https://github.com/apache/maven-mvnd"
+ echo "MVND_HOME is not set, mvnd can speed up local build, see https://github.com/apache/maven-mvnd. (mvnd currently does not update plugins on each run, when developing maven plugin, use mvnw)"
java -version
else
mvnd -version
From ef8394a2a7bd58b18ac26951dbd1b3165bff795a Mon Sep 17 00:00:00 2001
From: Cause Chung
Date: Tue, 1 Jul 2025 09:43:28 -0400
Subject: [PATCH 14/17] Update doc
---
README.md | 10 +++----
doc/Usage.md | 85 +++++++++++++++++++++++-----------------------------
it/pom.xml | 6 ++--
3 files changed, 45 insertions(+), 56 deletions(-)
diff --git a/README.md b/README.md
index 4ed7bc43..4072f4ea 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
[](https://github.com/cuzfrog/sharedtype/actions/workflows/ci.yaml)
[](https://central.sonatype.com/search?q=g:online.sharedtype++a:sharedtype&smo=true)
-# SharedType - Sharing Java Types made easy
+# SharedType - Lightweight Java Type Sharing
From Java:
```java
@SharedType
@@ -36,13 +36,11 @@ pub struct User {
```
## Features
-* Java8 compatible; Java 9 module (Jigsaw) compatible.
+* Java8+ compatible.
* Generics support.
* Compile-time constant support.
-* Client source dependency is only `@SharedType` retained at source code level.
-* SharedType annotation processor has only 1 dependency: [mustache](https://github.com/spullara/mustache.java).
-* Parsing takes milliseconds with `-proc:only`.
-* Intuitive defaults, put `@SharedType` and there you go. Global + class level options.
+* Fast. (Execution takes milliseconds with `-proc:only`.)
+* Simple global + type level configurations.
## Documentation
* [User Guide](doc/Usage.md)
diff --git a/doc/Usage.md b/doc/Usage.md
index 3d182ccd..43defc15 100644
--- a/doc/Usage.md
+++ b/doc/Usage.md
@@ -5,51 +5,35 @@ Menu:
* [Configurations](#Configurations)
## Setup
-
### Maven
-Add sharedtype dependency, the annotation `@SharedType` is only used at compile time on source code:
-```xml
-
- online.sharedtype
- sharedtype
- ${sharedtype.version}
- provided
- true
-
-```
-
Add Maven properties:
```xml
-
- 0.12.1
- false
+ 0.13.0
```
-Setup annotation processing:
+
+Add sharedtype-maven-plugin:
```xml
- org.apache.maven.plugins
- maven-compiler-plugin
-
-
-
- online.sharedtype
- sharedtype-ap
- ${sharedtype.version}
-
-
- true
-
-
- ${compilerArg}
- -Asharedtype.enabled=${sharedtype.enabled}
-
-
+ online.sharedtype
+ sharedtype-maven-plugin
+ ${project.version}
```
+Add sharedtype dependency:
+```xml
+
+ online.sharedtype
+ sharedtype
+ ${sharedtype.version}
+ provided
+ true
+
+```
+
## Usage
### A simple example
@@ -59,8 +43,8 @@ Annotate on a class:
record User(String name, int age, String email) {}
```
-Execute annotation processing:
-* maven: `./mvnw compile -DcompilerArg=-proc:only -Dsharedtype.enabled=true`
+Execute:
+* maven: `./mvnw stype:gen` (Why `stype`? Because it's easy to type while explicitly enough to remember.)
By default, below code will be generated:
```typescript
@@ -75,25 +59,30 @@ export interface User {
#### Global options
By default, the file `sharedtype.properties` on current cmd path will be picked up.
-You can customize the path by config `maven-compiler-plugin`:
+You can customize the path:
```xml
-
- -Asharedtype.propsFile=${your.properties.path}
-
+
+
+ ${project.basedir}/sharedtype-my-custom.properties
+
+
```
-Properties can also be passed in as system properties, which will override the properties files, e.g.
-```bash
-./mvnw clean compile -Dsharedtype.typescript.custom-code-path=it/custom-code.ts
-```
-or
-```bash
-MAVEN_OPTS="-Dsharedtype.typescript.custom-code-path=it/custom-code.ts" ./mvnw clean compile
+You can also specify individual properties:
+```xml
+
+
+
+ ${project.basedir}/custom-code.ts
+
+
+
```
-or can use [properties-maven-plugin](https://www.mojohaus.org/properties-maven-plugin/usage.html#set-system-properties) to set system properties for the build. (Not recommended for multi-module builds, due to potential system property pollution.)
-See [Default Properties](../processor/src/main/resources/sharedtype-default.properties) for details.
+See [Default Properties](../processor/src/main/resources/sharedtype-default.properties) for all property entries.
+Execution goal `gen` can be bound to a Maven lifecycle phase.
+Annotation processing can also be setup and configured via `maven-compiler-plugin`, see [example](../it/pom.xml).
#### Per annotation options
See Javadoc on [@SharedType](../annotation/src/main/java/online/sharedtype/SharedType.java) for details.
diff --git a/it/pom.xml b/it/pom.xml
index d868fb60..cd285da5 100644
--- a/it/pom.xml
+++ b/it/pom.xml
@@ -1,5 +1,6 @@
-
+
4.0.0
online.sharedtype
@@ -13,7 +14,7 @@
pom
-
+
it/sharedtype.properties
@@ -99,6 +100,7 @@
${compilerArg}
-Asharedtype.propsFile=${sharedtype.propsFile}
-Asharedtype.enabled=true
+
${compileClasses}
${excludeClasses}
From 45d3ce5c3e2e1996af1335c9051692e50d9401dd Mon Sep 17 00:00:00 2001
From: Cause Chung
Date: Tue, 1 Jul 2025 09:58:19 -0400
Subject: [PATCH 15/17] Fix maven-plugin/it dependency
---
doc/Usage.md | 1 +
maven-plugin/it/pom.xml | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/doc/Usage.md b/doc/Usage.md
index 43defc15..c0325254 100644
--- a/doc/Usage.md
+++ b/doc/Usage.md
@@ -83,6 +83,7 @@ See [Default Properties](../processor/src/main/resources/sharedtype-default.prop
Execution goal `gen` can be bound to a Maven lifecycle phase.
Annotation processing can also be setup and configured via `maven-compiler-plugin`, see [example](../it/pom.xml).
+The advantage of using `sharedtype-maven-plugin` is that you don't need to execute other annotation processors if there are multiple.
#### Per annotation options
See Javadoc on [@SharedType](../annotation/src/main/java/online/sharedtype/SharedType.java) for details.
diff --git a/maven-plugin/it/pom.xml b/maven-plugin/it/pom.xml
index 970ec252..b52431a2 100644
--- a/maven-plugin/it/pom.xml
+++ b/maven-plugin/it/pom.xml
@@ -15,7 +15,7 @@
${project.groupId}
- sharedtype-ap
+ sharedtype
${project.version}
From f2965181c020eec61bb614345df5974093208f6a Mon Sep 17 00:00:00 2001
From: Cause Chung
Date: Tue, 1 Jul 2025 10:10:02 -0400
Subject: [PATCH 16/17] Fix ci
---
.github/workflows/ci.yaml | 5 ++++-
doc/Usage.md | 5 +++--
2 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 38b82801..e3d86f3c 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -50,7 +50,7 @@ jobs:
run: ./mvnw clean verify -pl it/java8 -P it-jpms -e --ntp -B
- name: Test Maven 3.2.5 compatibility
working-directory: maven-plugin/it
- run: ./mvnw stype:gen -e --ntp -B
+ run: ./mvnw stype:gen -e -B
- name: Javadoc
run: ./mvnw -P release javadoc:javadoc --ntp -B
- name: Upload generated sources
@@ -96,6 +96,9 @@ jobs:
key: build_maven
- name: Test
run: ./mvnw verify -pl it/java8 -e --ntp -B
+ - name: Maven Plugin Test
+ working-directory: maven-plugin/it
+ run: ./mvnw stype:gen -e -B
client_test:
needs: [build_and_test]
diff --git a/doc/Usage.md b/doc/Usage.md
index c0325254..9d9830b4 100644
--- a/doc/Usage.md
+++ b/doc/Usage.md
@@ -34,6 +34,9 @@ Add sharedtype dependency:
```
+`sharedtype-maven-plugin` requires Maven 3.2.5+.
+Annotation processing can also be setup and configured via `maven-compiler-plugin`, see [example](../it/pom.xml).
+The advantage of using `sharedtype-maven-plugin` is that no need to execute other annotation processors if there are multiple.
## Usage
### A simple example
@@ -82,8 +85,6 @@ You can also specify individual properties:
See [Default Properties](../processor/src/main/resources/sharedtype-default.properties) for all property entries.
Execution goal `gen` can be bound to a Maven lifecycle phase.
-Annotation processing can also be setup and configured via `maven-compiler-plugin`, see [example](../it/pom.xml).
-The advantage of using `sharedtype-maven-plugin` is that you don't need to execute other annotation processors if there are multiple.
#### Per annotation options
See Javadoc on [@SharedType](../annotation/src/main/java/online/sharedtype/SharedType.java) for details.
From 6bb25d15e4cc903472777aa499e25e96cb744ebe Mon Sep 17 00:00:00 2001
From: Cause Chung
Date: Sat, 5 Jul 2025 09:26:10 -0400
Subject: [PATCH 17/17] Fix java8 javac loading
---
maven-plugin/it/mvne | 4 ++
.../sharedtype/maven/DependencyResolver.java | 2 +-
.../sharedtype/maven/SharedTypeGenMojo.java | 19 ++++++--
processor/pom.xml | 47 ++++++++++++++++++-
4 files changed, 67 insertions(+), 5 deletions(-)
create mode 100755 maven-plugin/it/mvne
diff --git a/maven-plugin/it/mvne b/maven-plugin/it/mvne
new file mode 100755
index 00000000..2fccba55
--- /dev/null
+++ b/maven-plugin/it/mvne
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+export MAVEN_OPTS="-Xdebug -Xnoagent -Xint -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005 $MAVEN_OPTS"
+./mvnw "$@"
diff --git a/maven-plugin/src/main/java/online/sharedtype/maven/DependencyResolver.java b/maven-plugin/src/main/java/online/sharedtype/maven/DependencyResolver.java
index f1cecb9e..67e3038f 100644
--- a/maven-plugin/src/main/java/online/sharedtype/maven/DependencyResolver.java
+++ b/maven-plugin/src/main/java/online/sharedtype/maven/DependencyResolver.java
@@ -28,7 +28,7 @@ final class DependencyResolver {
this.project = project;
}
- List getSourceDependencies() throws MojoExecutionException {
+ List getClasspathDependencies() throws MojoExecutionException {
try {
ArtifactTypeRegistry artifactTypeRegistry =
session.getRepositorySession().getArtifactTypeRegistry();
diff --git a/maven-plugin/src/main/java/online/sharedtype/maven/SharedTypeGenMojo.java b/maven-plugin/src/main/java/online/sharedtype/maven/SharedTypeGenMojo.java
index b25bd1dc..10f1d5b5 100644
--- a/maven-plugin/src/main/java/online/sharedtype/maven/SharedTypeGenMojo.java
+++ b/maven-plugin/src/main/java/online/sharedtype/maven/SharedTypeGenMojo.java
@@ -35,10 +35,12 @@
/**
* Generate types from {@link SharedType} annotated classes.
* See SharedType for details.
+ *
* @author Cause Chung
*/
@Mojo(name = "gen")
public final class SharedTypeGenMojo extends AbstractMojo {
+ private static final String JAVA8_VERSION = "1.8";
private static final List DEFAULT_COMPILER_OPTIONS = Arrays.asList("-proc:only", "-Asharedtype.enabled=true");
private @Inject RepositorySystem repositorySystem;
@@ -84,13 +86,13 @@ public void execute() throws MojoExecutionException, MojoFailureException {
Files.createDirectories(outputDirPath);
}
fileManager.setLocation(StandardLocation.SOURCE_OUTPUT, Collections.singleton(outputDirPath.toFile()));
- fileManager.setLocation(StandardLocation.CLASS_PATH, dependencyResolver.getSourceDependencies());
+ fileManager.setLocation(StandardLocation.CLASS_PATH, dependencyResolver.getClasspathDependencies());
} catch (Exception e) {
throw new MojoExecutionException(e);
}
Iterable extends JavaFileObject> sources = fileManager.getJavaFileObjectsFromFiles(walkAllSourceFiles());
- try(SharedTypeLogger logger = new SharedTypeLogger(getLog())) {
+ try (SharedTypeLogger logger = new SharedTypeLogger(getLog())) {
JavaCompiler.CompilationTask task = compiler.getTask(logger, fileManager, diagnosticListener, getCompilerOptions(), null, sources);
SharedTypeAnnotationProcessor annotationProcessor = new SharedTypeAnnotationProcessor();
annotationProcessor.setUserProps(properties);
@@ -124,7 +126,18 @@ private List getCompilerOptions() throws MojoFailureException {
}
private static JavaCompiler getJavaCompiler() throws MojoExecutionException {
- JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
+ String javaVersion = System.getProperty("java.specification.version");
+ JavaCompiler compiler;
+ if (JAVA8_VERSION.equals(javaVersion)) {
+ try {
+ Class> javacToolClass = SharedTypeAnnotationProcessor.class.getClassLoader().loadClass("com.sun.tools.javac.api.JavacTool");
+ compiler = (JavaCompiler) javacToolClass.getConstructor().newInstance();
+ } catch (Exception e) {
+ throw new MojoExecutionException("Failed to load JavaCompiler.", e);
+ }
+ } else {
+ compiler = ToolProvider.getSystemJavaCompiler();
+ }
if (compiler != null) {
return compiler;
}
diff --git a/processor/pom.xml b/processor/pom.xml
index 32e1ace4..57974a1c 100644
--- a/processor/pom.xml
+++ b/processor/pom.xml
@@ -1,5 +1,6 @@
-
+
4.0.0
online.sharedtype
@@ -63,6 +64,50 @@
+
+
+
+ jdk-tools-jar-env-var
+
+ true
+
+ ${JAVA_HOME}/lib/tools.jar
+
+
+
+
+ ${JAVA_HOME}/lib/tools.jar
+
+
+
+ jdk-tools-jar-sys-prop
+
+ false
+
+ ${java.home}/../lib/tools.jar
+
+
+
+ ${java.home}/../lib/tools.jar
+
+
+
+ java8-jdk-tools-jar
+
+ 1.8
+
+
+
+ jdk.tools
+ jdk.tools
+ jdk1.8.0
+ system
+ ${toolsjar}
+
+
+
+
+