Maven/Retrolambda: how to detect dependencies on Java 8 classes
Asked Answered
W

1

8

Background:

We have maven-based java project, which targets JRE 1.7, but the source code uses lambdas, so we use retrolambda for transforming Java 8 source code to Java 7. Also we use StreamSupport backport library when we need streams, function.*, Optional, etc.

Usage of retrolambda involves configuring the project's both source and target language level to 1.8.

Everything works fine if there are no dependencies on java8 classes or methods (like java.util.stream.*, java.util.Optional, or methods introduced in java8 like Collection.forEach). If there are such usages then build passes, but it fails in runtime, when running under JVM of Java 8.

Question:

My goal is to fail the build in case when such dependencies exist. Is there any way of detecting dependencies on new Java 8 classes/methods in build-time?

I thought about 2 possible options, but I'm not sure whether either of them is doable:

  1. Some kind of bytecode analyzer for detecting depdencies on predefined classes and methods. Are there such tools/maven plugins?
  2. Lint (lint4j) rules. Not sure whether it's possible to detect dependency on class/method using lint
Wyly answered 3/11, 2016 at 13:42 Comment(6)
Just change the bootstrap classpath when compiling, pointing to the rt.jar of a Java 7 installation. To let the compiler work with lambda expressions, you may have to add a dummy jar to the path, containing a LambdaMetaFactory class having declarations of its two methods, in case the compiler verifies this. They don’t have to work, you’ll process the compiled code with retrolambda anyway (then, with the actual JRE8).Merchantable
Following Holger, this procedure worked for me: put stubs for LambdaConversionException, LambdaMetafactory and SerializedLambda inside the JRE7 rt.jar (using an additional dummy jar didn't work in my case. I got "Fatal Error: Unable to find package java.lang in classpath or bootclasspath"). Then point the maven-compiler-plugin to the enhanced rt.jar via its <bootclasspath> tag. As a test, I compiled the streamsupport sources (sans j8.u.DelegatingSpliterator which uses Java 8 APIs and therefore is not compilable with a Java 7 rt.jar).Landonlandor
One way could be to run your test suite with JRE 7. Exceptions should be thrown if you use Java 8 APIs.Gosse
@Joseph Earl Sure, but the OP's question was how to detect this at build time.Serle
@Serle usually tests are run as part of overall build lifecycleGosse
@JosephEarl you are right - the tests are part of the build cycle, but the problem in this solution is that tests never have 100% coverage. That's why I prefer solution which is not based not running the code, but on static analysisWyly
U
5

You can use the Animal Sniffer Maven Plugin for this. It allows you to check that your code only uses APIs from a specified baseline (called the "signature"). In your case you'd use the org.codehaus.mojo.signature:java17:1.0 signature.

As others pointed out, you also could set up the bootstrap classpath, but that a) requires a JDK 7 to be set up and b) makes the build a bit more complex as you need to point to the JDK 7 install. Animal Sniffer is is much easier to work with in my experience.

Unerring answered 3/11, 2016 at 21:13 Comment(1)
Nice, glad to hear.Unerring

© 2022 - 2024 — McMap. All rights reserved.