Skip to content

Commit 965210b

Browse files
authored
Merge pull request #302 from rosette-api/no-jira-jackson-mixin-warnings-and-errors
NO-JIRA: improve mixins failure modes
2 parents e9d02a6 + cccdfc1 commit 965210b

File tree

10 files changed

+234
-17
lines changed

10 files changed

+234
-17
lines changed

CI.Jenkinsfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
node ("docker-light") {
22
def sourceDir = pwd()
33
try {
4-
env.JAVA_HOME = "${tool 'java21'}"
4+
env.JAVA_HOME = "${tool 'java25'}"
55
env.PATH = "${env.JAVA_HOME}/bin:${env.PATH}"
66
def mavenLocalRepo = "$JENKINS_HOME/maven-local-repositories/executor-$EXECUTOR_NUMBER"
77
stage("Clean up") {
@@ -30,7 +30,7 @@ node ("docker-light") {
3030
--pull always \
3131
--volume ${sourceDir}:/source \
3232
--volume /opt/maven-basis:/opt/maven-basis \
33-
eclipse-temurin:21-jdk-noble \
33+
eclipse-temurin:25-jdk-noble \
3434
bash -c \"apt-get update && \
3535
apt-get install -y git && \
3636
pushd /source && \

Jenkinsfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
node ("docker-light") {
22
def SOURCEDIR = pwd()
33
try {
4-
env.JAVA_HOME = "${tool 'java21'}"
4+
env.JAVA_HOME = "${tool 'java25'}"
55
env.PATH = "${env.JAVA_HOME}/bin:${env.PATH}"
66
def mavenLocalRepo = "$JENKINS_HOME/maven-local-repositories/executor-$EXECUTOR_NUMBER"
77
stage("Clean up") {

all/pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@
6969
<artifactId>maven-javadoc-plugin</artifactId>
7070
<configuration>
7171
<sourcepath>../model/target/delombok;../api/src/main/java</sourcepath>
72+
<failOnError>false</failOnError>
73+
<failOnWarnings>false</failOnWarnings>
74+
<quiet>true</quiet>
75+
<doclint>none</doclint>
7276
<additionalDependencies>
7377
<additionalDependency>
7478
<groupId>com.basistech.rosette</groupId>

annotations/pom.xml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,24 @@
4444
<version>${lombok.version}</version>
4545
<scope>provided</scope>
4646
</dependency>
47+
<dependency>
48+
<groupId>org.junit.jupiter</groupId>
49+
<artifactId>junit-jupiter-api</artifactId>
50+
<version>${junit.version}</version>
51+
<scope>test</scope>
52+
</dependency>
53+
<dependency>
54+
<groupId>org.junit.jupiter</groupId>
55+
<artifactId>junit-jupiter-engine</artifactId>
56+
<version>${junit.version}</version>
57+
<scope>test</scope>
58+
</dependency>
59+
<dependency>
60+
<groupId>com.google.testing.compile</groupId>
61+
<artifactId>compile-testing</artifactId>
62+
<version>${google-testing-compile.version}</version>
63+
<scope>test</scope>
64+
</dependency>
4765
</dependencies>
4866
<build>
4967
<plugins>

annotations/src/main/java/com/basistech/rosette/annotations/JacksonMixinProcessor.java

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,7 @@
3838
import javax.lang.model.element.TypeElement;
3939
import javax.tools.Diagnostic;
4040
import java.io.IOException;
41-
import java.util.Arrays;
42-
import java.util.Collections;
4341
import java.util.HashMap;
44-
import java.util.HashSet;
4542
import java.util.Map;
4643
import java.util.Set;
4744

@@ -75,7 +72,7 @@ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment
7572
String elementSimpleName = typeElement.getSimpleName().toString();
7673
if (typeElement.getAnnotation(Builder.class) != null) {
7774
TypeSpec.Builder mixinClassBuilder = TypeSpec
78-
.classBuilder(typeElement.getSimpleName().toString() + "Mixin")
75+
.classBuilder(typeElement.getSimpleName() + "Mixin")
7976
.addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
8077
.addAnnotation(AnnotationSpec.builder(JsonTypeName.class)
8178
.addMember(VALUE, "$S", typeElement.getSimpleName())
@@ -89,7 +86,7 @@ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment
8986
.add("JsonInclude.Include.NON_NULL").build())
9087
.build())
9188
.addType(TypeSpec
92-
.classBuilder(typeElement.getSimpleName().toString() + "MixinBuilder")
89+
.classBuilder(typeElement.getSimpleName() + "MixinBuilder")
9390
.addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
9491
.addAnnotation(AnnotationSpec.builder(JsonPOJOBuilder.class)
9592
.addMember("withPrefix", "$S", "")
@@ -101,13 +98,23 @@ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment
10198
.build()
10299
.writeTo(processingEnvironment.getFiler());
103100
addMixinCode.put(elementQualifiedName + ".class",
104-
packageName + "." + typeElement.getSimpleName().toString() + "Mixin" + ".class");
101+
packageName + "." + typeElement.getSimpleName() + "Mixin" + ".class");
105102
addMixinCode.put(elementQualifiedName + "." + elementSimpleName + "Builder.class",
106-
packageName + "." + typeElement.getSimpleName().toString()
107-
+ "Mixin." + typeElement.getSimpleName().toString() + "MixinBuilder.class");
103+
packageName + "." + typeElement.getSimpleName()
104+
+ "Mixin." + typeElement.getSimpleName() + "MixinBuilder.class");
108105
} catch (IOException e) {
109-
e.printStackTrace();
106+
processingEnvironment.getMessager().printMessage(
107+
Diagnostic.Kind.ERROR,
108+
"Failed to generate mixin: " + e.getMessage(),
109+
element
110+
);
110111
}
112+
} else {
113+
processingEnvironment.getMessager().printMessage(
114+
Diagnostic.Kind.ERROR,
115+
"@JacksonMixin requires @Builder annotation",
116+
element
117+
);
111118
}
112119
}
113120

@@ -129,15 +136,18 @@ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment
129136
.build()
130137
.writeTo(processingEnvironment.getFiler());
131138
} catch (IOException e) {
132-
e.printStackTrace();
139+
processingEnvironment.getMessager().printMessage(
140+
Diagnostic.Kind.ERROR,
141+
"Failed to generate MixinUtil: " + e.getMessage()
142+
);
133143
}
134144

135145
return true;
136146
}
137147

138148
@Override
139149
public Set<String> getSupportedAnnotationTypes() {
140-
return Collections.unmodifiableSet(new HashSet<>(Arrays.asList(JacksonMixin.class.getCanonicalName())));
150+
return Set.of(JacksonMixin.class.getCanonicalName());
141151
}
142152

143153
@Override
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
/*
2+
* Copyright 2026 Basis Technology Corp.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.basistech.rosette.annotations;
18+
19+
import com.google.testing.compile.Compilation;
20+
import com.google.testing.compile.Compiler;
21+
import com.google.testing.compile.JavaFileObjects;
22+
import org.junit.jupiter.api.Test;
23+
24+
import javax.tools.Diagnostic;
25+
import javax.tools.JavaFileObject;
26+
import java.util.List;
27+
28+
import static com.google.testing.compile.CompilationSubject.assertThat;
29+
import static org.junit.jupiter.api.Assertions.assertEquals;
30+
import static org.junit.jupiter.api.Assertions.assertTrue;
31+
32+
class JacksonMixinProcessorTest {
33+
34+
@Test
35+
void testJacksonMixinWithoutBuilderProducesError() {
36+
JavaFileObject classWithoutBuilder = JavaFileObjects.forSourceString(
37+
"test.ModelWithoutBuilder",
38+
"""
39+
package test;
40+
41+
import com.basistech.rosette.annotations.JacksonMixin;
42+
import lombok.Value;
43+
44+
@Value
45+
@JacksonMixin
46+
public class ModelWithoutBuilder {
47+
String name;
48+
}
49+
"""
50+
);
51+
52+
Compilation compilation = Compiler.javac()
53+
.withProcessors(new JacksonMixinProcessor())
54+
.compile(classWithoutBuilder);
55+
56+
assertThat(compilation).failed();
57+
58+
List<Diagnostic<? extends JavaFileObject>> errors = compilation.errors();
59+
String errorMessage = errors.get(0).getMessage(null);
60+
assertTrue(errorMessage.contains("@JacksonMixin requires @Builder annotation"),
61+
"Error should state that Builder annotation is required. Actual: " + errorMessage);
62+
}
63+
64+
@Test
65+
void testClassWithoutJacksonMixinHasNoWarnings() {
66+
JavaFileObject normalClass = JavaFileObjects.forSourceString(
67+
"test.NormalClass",
68+
"""
69+
package test;
70+
71+
public class NormalClass {
72+
String name;
73+
}
74+
"""
75+
);
76+
77+
Compilation compilation = Compiler.javac()
78+
.withProcessors(new JacksonMixinProcessor())
79+
.compile(normalClass);
80+
81+
assertThat(compilation).succeeded();
82+
83+
// Should have no warnings
84+
List<Diagnostic<? extends JavaFileObject>> warnings = compilation.warnings();
85+
assertEquals(0, warnings.size(),
86+
"Should have no warnings for classes without JacksonMixin");
87+
}
88+
89+
90+
@Test
91+
void testJacksonMixinOnInterfaceProducesError() {
92+
JavaFileObject interfaceWithAnnotation = JavaFileObjects.forSourceString(
93+
"test.MyInterface",
94+
"""
95+
package test;
96+
97+
import com.basistech.rosette.annotations.JacksonMixin;
98+
99+
@JacksonMixin
100+
public interface MyInterface {
101+
String getName();
102+
}
103+
"""
104+
);
105+
106+
Compilation compilation = Compiler.javac()
107+
.withProcessors(new JacksonMixinProcessor())
108+
.compile(interfaceWithAnnotation);
109+
110+
// Should fail with an error
111+
assertThat(compilation).failed();
112+
113+
// Verify it's a meaningful error message (not a stack trace)
114+
List<Diagnostic<? extends JavaFileObject>> errors = compilation.errors();
115+
String errorMessage = errors.get(0).getMessage(null);
116+
assertTrue(errorMessage.contains("Annotation only available to Class"),
117+
"Error should state annotation is only for classes. Actual: " + errorMessage);
118+
}
119+
120+
@Test
121+
void testJacksonMixinOnEnumProducesError() {
122+
JavaFileObject enumWithAnnotation = JavaFileObjects.forSourceString(
123+
"test.MyEnum",
124+
"""
125+
package test;
126+
127+
import com.basistech.rosette.annotations.JacksonMixin;
128+
129+
@JacksonMixin
130+
public enum MyEnum {
131+
VALUE1, VALUE2
132+
}
133+
"""
134+
);
135+
136+
Compilation compilation = Compiler.javac()
137+
.withProcessors(new JacksonMixinProcessor())
138+
.compile(enumWithAnnotation);
139+
140+
// Should fail with an error
141+
assertThat(compilation).failed();
142+
143+
// Verify it's a meaningful error message (not a stack trace)
144+
List<Diagnostic<? extends JavaFileObject>> errors = compilation.errors();
145+
String errorMessage = errors.get(0).getMessage(null);
146+
assertTrue(errorMessage.contains("Annotation only available to Class"),
147+
"Error should state annotation is only for classes. Actual: " + errorMessage);
148+
}
149+
150+
@Test
151+
void testGetSupportedAnnotationTypes() {
152+
JacksonMixinProcessor processor = new JacksonMixinProcessor();
153+
java.util.Set<String> supportedTypes = processor.getSupportedAnnotationTypes();
154+
155+
// Should have exactly one supported annotation type
156+
assertEquals(1, supportedTypes.size(),
157+
"Should support exactly one annotation type");
158+
159+
// Should be the JacksonMixin annotation
160+
String expectedType = JacksonMixin.class.getCanonicalName();
161+
assertTrue(supportedTypes.contains(expectedType),
162+
"Should support " + expectedType + " annotation. Actual: " + supportedTypes);
163+
}
164+
}

api/pom.xml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<!--
3-
Copyright 2018-2024 Basis Technology Corp.
3+
Copyright 2018-2026 Babel Street Ltd.
44
55
Licensed under the Apache License, Version 2.0 (the "License");
66
you may not use this file except in compliance with the License.
@@ -106,6 +106,9 @@
106106
<plugin>
107107
<groupId>org.apache.maven.plugins</groupId>
108108
<artifactId>maven-surefire-plugin</artifactId>
109+
<configuration>
110+
<redirectTestOutputToFile>true</redirectTestOutputToFile>
111+
</configuration>
109112
</plugin>
110113
</plugins>
111114
<resources>

examples/pom.xml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,4 +66,18 @@
6666
<artifactId>slf4j-simple</artifactId>
6767
</dependency>
6868
</dependencies>
69+
<build>
70+
<plugins>
71+
<plugin>
72+
<groupId>org.apache.maven.plugins</groupId>
73+
<artifactId>maven-javadoc-plugin</artifactId>
74+
<configuration>
75+
<failOnError>false</failOnError>
76+
<failOnWarnings>false</failOnWarnings>
77+
<quiet>true</quiet>
78+
<doclint>none</doclint>
79+
</configuration>
80+
</plugin>
81+
</plugins>
82+
</build>
6983
</project>

model/pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,10 @@
104104
</executions>
105105
<configuration>
106106
<sourcepath>${delombok.output.directory}</sourcepath>
107+
<failOnError>false</failOnError>
108+
<failOnWarnings>false</failOnWarnings>
109+
<quiet>true</quiet>
110+
<doclint>none</doclint>
107111
</configuration>
108112
</plugin>
109113
<plugin>

pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
</parent>
2929
<packaging>pom</packaging>
3030
<inceptionYear>2014</inceptionYear>
31-
<url>http://rosette-api.github.io/java</url>
31+
<url>https://rosette-api.github.io/java/</url>
3232
<scm>
3333
<connection>scm:git:git@github.com:rosette-api/java.git</connection>
3434
<developerConnection>scm:git:git@github.com:rosette-api/java.git</developerConnection>
@@ -44,6 +44,7 @@
4444
<properties>
4545
<build-helper-maven-plugin.version>3.6.1</build-helper-maven-plugin.version>
4646
<commons-codec.version>1.20.0</commons-codec.version>
47+
<google-testing-compile.version>0.21.0</google-testing-compile.version>
4748
<jakarta-validation-api.version>3.1.1</jakarta-validation-api.version>
4849
<java-poet.version>0.10.0</java-poet.version>
4950
<junit.version>6.0.1</junit.version>
@@ -90,7 +91,6 @@
9091
<groupId>org.apache.maven.plugins</groupId>
9192
<artifactId>maven-compiler-plugin</artifactId>
9293
<configuration>
93-
<compilerVersion>17</compilerVersion>
9494
<source>11</source>
9595
<target>11</target>
9696
</configuration>

0 commit comments

Comments
 (0)