Neuigkeiten von trion.
Immer gut informiert.

Micronaut mit IntelliJ IDEA

Micronaut ist ein modernes Java Framework, das speziell mit der Anforderung entwickelt wurde, den Einsatz von Reflection zu minimieren.
Dazu wird unter anderem Code Generierung zur Build Zeit verwendet. Micronaut kann mit Maven als auch Gradle als Buildsystem gut verwendet werden. Mit IntelliJ IDEA gibt es jedoch einiges zu beachten, sonst kann es zu schwer diagnostizierbaren Fehlern kommen.

Micronaut basiert fundamental auf Code-Generierung. Das ist unter anderem durch Java Annotation Processors umgesetzt. Diese müssen durch den Build verwendet werden. Bei Maven und Gradle geschieht dies automatisch, in IntelliJ Idea muss dazu eine Option aktiviert werden.

IntelliJ Idea implementiert den Build selbst, um damit unter anderem eine bessere Performance zu ermöglichen. Doch wenn der Build komplexer wird, bleibt es nicht aus, dass sich Abweichungen in der Umsetzung zwischen Idea und den typischen Build Tools ergibt.
Beispielsweise bei der Nutzung der konkreten Annotation Processors oder der konfigurierten Versionen.

Das kann dann zu dem folgenden NoSuchMethodError Fehler führen:

java: java.lang.NoSuchMethodError: Micronaut method io.micronaut.core.reflect.InstantiationUtils.instantiateReflectively(Class,Class[],Object[]) not found. Most likely reason for this issue is that you are running a newer version of Micronaut with code compiled against an older version. Please recompile the offending classes

Eine Option einen konsistenten Build sicherzustellen und dabei auch den oberen Fehler zu beheben, ist dabei das eingebaute Buildverfahren von IntelliJ zu deaktivieren und an die nativen Build Tools zu delegieren.
Die Konfiguration für Maven findet sich unter "Build, Execution, Deployment".

Das kann dann jedoch auch zu neuen Fehlern führen. Wird beispielsweise der clean Lifecycle von Maven manuell ausgeführt, sind ggf. Klassen nicht neu generiert, die zur Testausführung benötigt werden.
Der Fehler sieht dann beispielsweise so aus:

12:55:29.386 [main] DEBUG reactor.util.Loggers -- Using Slf4j logging framework
12:55:29.395 [main] DEBUG reactor.core.publisher.Hooks -- Enabling stacktrace debugging via onOperatorDebug
Internal Error occurred.
org.junit.platform.commons.JUnitException: TestEngine with ID 'junit-jupiter' failed to discover tests
	at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discoverEngineRoot(EngineDiscoveryOrchestrator.java:160)
	at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discoverSafely(EngineDiscoveryOrchestrator.java:132)
	at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discover(EngineDiscoveryOrchestrator.java:107)
	at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discover(EngineDiscoveryOrchestrator.java:78)
	at org.junit.platform.launcher.core.DefaultLauncher.discover(DefaultLauncher.java:99)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:85)
	at org.junit.platform.launcher.core.DelegatingLauncher.execute(DelegatingLauncher.java:47)
	at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:63)
	at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:57)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
	at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:232)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:55)
Caused by: org.junit.platform.commons.JUnitException: ClassSelector [className = 'dev.trion.k8s.OperatorTest', classLoader = null] resolution failed
	at org.junit.platform.launcher.listeners.discovery.AbortOnFailureLauncherDiscoveryListener.selectorProcessed(AbortOnFailureLauncherDiscoveryListener.java:39)
	at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution.resolveCompletely(EngineDiscoveryRequestResolution.java:103)
	at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution.run(EngineDiscoveryRequestResolution.java:83)
	at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolver.resolve(EngineDiscoveryRequestResolver.java:113)
	at org.junit.jupiter.engine.discovery.DiscoverySelectorResolver.resolveSelectors(DiscoverySelectorResolver.java:48)
	at org.junit.jupiter.engine.JupiterTestEngine.discover(JupiterTestEngine.java:69)
	at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discoverEngineRoot(EngineDiscoveryOrchestrator.java:152)
	... 13 more
Caused by: org.junit.platform.commons.PreconditionViolationException: Could not load class with name: dev.trion.k8s.OperatorTest
	at org.junit.platform.engine.discovery.ClassSelector.lambda$getJavaClass$0(ClassSelector.java:98)
	at org.junit.platform.commons.function.Try$Failure.getOrThrow(Try.java:335)
	at org.junit.platform.engine.discovery.ClassSelector.getJavaClass(ClassSelector.java:97)
	at org.junit.jupiter.engine.discovery.ClassSelectorResolver.resolve(ClassSelectorResolver.java:66)
	at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution.lambda$resolve$2(EngineDiscoveryRequestResolution.java:135)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:215)
	at java.base/java.util.ArrayList$ArrayListSpliterator.tryAdvance(ArrayList.java:1693)
	at java.base/java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:147)
	at java.base/java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:588)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:574)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:560)
	at java.base/java.util.stream.FindOps$FindOp.evaluateSequential(FindOps.java:150)
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:265)
	at java.base/java.util.stream.ReferencePipeline.findFirst(ReferencePipeline.java:687)
	at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution.resolve(EngineDiscoveryRequestResolution.java:189)
	at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution.resolve(EngineDiscoveryRequestResolution.java:126)
	at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution.resolveCompletely(EngineDiscoveryRequestResolution.java:92)
	... 18 more
Caused by: java.lang.ClassNotFoundException: dev.trion.k8s.OperatorTest
	at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:580)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:490)
	at java.base/java.lang.Class.forName0(Native Method)
	at java.base/java.lang.Class.forName(Class.java:543)
	at org.junit.platform.commons.util.ReflectionUtils.lambda$tryToLoadClass$10(ReflectionUtils.java:890)
	at org.junit.platform.commons.function.Try.lambda$call$0(Try.java:57)
	at org.junit.platform.commons.function.Try.of(Try.java:93)
	at org.junit.platform.commons.function.Try.call(Try.java:57)
	at org.junit.platform.commons.util.ReflectionUtils.tryToLoadClass(ReflectionUtils.java:853)
	at org.junit.platform.commons.util.ReflectionUtils.tryToLoadClass(ReflectionUtils.java:790)
	at org.junit.platform.engine.discovery.ClassSelector.getJavaClass(ClassSelector.java:95)
	... 32 more

Die Lösung sieht dabei recht einfach aus: Der Test wird einfach erneut gestartet und der Fehler ist behoben.

Eine weitere Fehlerquelle ist die Instrumentierung des Codes, um auch bei asynchroner Ausführung hilfreiche Stacktraces zu erhalten. Dies kann jedoch zu dem folgenden Fehler führen, speziell bei der Ausführung von Unit Tests mit JUnit oder TestNG:

Exception in thread "main" java.lang.ClassCircularityError: ...

Wird die Instrumentierung abgeschaltet, verschwindet auch der Fehler. Auch diese Konfiguration wird in IntelliJ Idea vorgenommen, wie im folgenden Screenshot zu sehen.

Auch wenn mit diesen Einstellungen der Build zuverlässig funktioniert, so ist die Geschwindigkeit des nativen Builds im Vergleich zu dem IntelliJ Idea eigenen Build erheblich reduziert. Entsprechend wäre eine Konfiguration von IntelliJ für Micronaut wünschenswert, die alle genannten Probleme umgeht.

Dazu gilt es vor allem zu beachten, welche Annotation Prozessoren konkret verwendet werden. Auch wenn unter Default korrekt das Setting Obtain processors from project classpath ausgewählt ist, so speichert sich IntelliJ mitunter die ermittelten Prozessoren, die dann aus dem lokalen Maven Repository stammen. Aktualisiert man die Micronaut Version, so werden diese teilweise nicht korrekt neu ermittelt, sondern zeigen auf die alten Versionen.
Im Ergebnis erhält man mit Micronaut den java.lang.NoSuchMethodError.
Wird auch für das konkrete Projekt die Einstellung auf den Projekt-Classpath des Micronaut Projekts gesetzt, sollte auch der interne Build korrekt funktionieren.

Falls bei der Ausführung von JUnit Tests der folgende Fehler resultiert, kann dies durch eine Abhängigkeit aufglöst werden:

org.junit.jupiter.engine.execution.ConditionEvaluationException: Failed to evaluate condition [io.micronaut.test.extensions.junit5.MicronautJunit5Extension]: @MicronautTest used on test but no bean definition for the test present. This error indicates a misconfigured build or IDE. Please check 'micronaut.processing.group' or 'micronaut.processing.annotations' in your build. Add the 'micronaut-inject-java' annotation processor to your test processor path (for Java this is the testAnnotationProcessor scope, for Kotlin kaptTest and for Groovy testCompile). See the documentation for reference: https://micronaut-projects.github.io/micronaut-test/latest/guide/

	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:215)
	at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:197)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:215)
	at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:197)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:215)
	at java.base/java.util.ArrayList$ArrayListSpliterator.tryAdvance(ArrayList.java:1693)
	at java.base/java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:147)
	at java.base/java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:588)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:574)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:560)
	at java.base/java.util.stream.FindOps$FindOp.evaluateSequential(FindOps.java:150)
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:265)
	at java.base/java.util.stream.ReferencePipeline.findFirst(ReferencePipeline.java:687)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1604)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1604)
Caused by: org.junit.jupiter.api.extension.TestInstantiationException: @MicronautTest used on test but no bean definition for the test present. This error indicates a misconfigured build or IDE. Please check 'micronaut.processing.group' or 'micronaut.processing.annotations' in your build. Add the 'micronaut-inject-java' annotation processor to your test processor path (for Java this is the testAnnotationProcessor scope, for Kotlin kaptTest and for Groovy testCompile). See the documentation for reference: https://micronaut-projects.github.io/micronaut-test/latest/guide/
	at io.micronaut.test.extensions.junit5.MicronautJunit5Extension.evaluateExecutionCondition(MicronautJunit5Extension.java:245)
	... 15 more

Dazu wird im Maven Projekt das micronaut-inject-java explizit als Abhängigkeit aufgenommen.

    <dependency>
      <groupId>io.micronaut</groupId>
      <artifactId>micronaut-inject-java</artifactId>
    </dependency>

Nachdem das Maven Projekt synchronisiert wurde und ggf. ein Rebuild des gesamten Projektes durchgeführt wurde, sollte alles in IntelliJ Idea auch nach einem Micronaut Update einwandfrei funktionieren.




Zu den Themen Kubernetes, Micronaut und Spring Boot bieten wir sowohl Beratung, Entwicklungsunterstützung als auch passende Schulungen an:

Auch für Ihren individuellen Bedarf können wir Workshops und Schulungen anbieten. Sprechen Sie uns gerne an.

Feedback oder Fragen zu einem Artikel - per Twitter @triondevelop oder E-Mail freuen wir uns auf eine Kontaktaufnahme!

Zur Desktop Version des Artikels