Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Linkage Checker: javax.annotation.Nullable false positive? #1045

Closed
suztomo opened this issue Nov 22, 2019 · 5 comments
Closed

Linkage Checker: javax.annotation.Nullable false positive? #1045

suztomo opened this issue Nov 22, 2019 · 5 comments
Assignees
Labels
Dependency Fix Fix needed in library in GCP orbit

Comments

@suztomo
Copy link
Contributor

suztomo commented Nov 22, 2019

Some Java annotations don't need to be present at runtime class path. I thought javax.annotation.Nullable may be such class when I saw Linkage Checker reported the following:

suztomo@suxtomo24:~/beam6$ ./gradlew -Ppublishing -PjavaLinkageArtifactIds=beam-sdks-java-io-amazon-web-services :checkJavaLinkage |tee linkage.log
Configuration on demand is an incubating feature.

> Task :runners:apex:buildDependencyTree
See the report at: file:///usr/local/google/home/suztomo/beam6/runners/apex/build/classes/java/main/org/apache/beam/runners/apex/dependency-tree

> Task :checkJavaLinkage
Class javax.annotation.Nullable is not found;
  referenced by 1 class file
    org.apache.beam.sdk.schemas.FieldValueTypeInformation (beam-sdks-java-core-2.18.0-SNAPSHOT.jar)
...

However, as per its Javadoc, the retention policy is RUNTIME, meaning that the annotation should be available at runtime.

Question

  • Why does @Nullable need to be present at runtime?
  • Will this cause an actual LinkageError if the annotation does not exist at runtime?
@suztomo
Copy link
Contributor Author

suztomo commented Dec 3, 2019

Will this cause an actual LinkageError if the annotation does not exist at runtime?

No, it did not. The code below just works without the presence of the Nullable class.

suztomo@suxtomo24:~/hello-nullable$ cat src/main/java/suztomo/App.java 
package suztomo;

import javax.annotation.Nullable;

public class App
{

    private static void f(@Nullable String argument) {
        System.out.println(argument);
    }

    public static void main( String[] args )
    {
        f( "Hello World!" );
    }
}
suztomo@suxtomo24:~/hello-nullable$ java -cp ./target/classes suztomo.App
Hello World!

So this is false positive; Linkage Checker should not report missing annotation classes.

Output from Javap

suztomo@suxtomo24:~/hello-nullable$ javap -private -v -cp target/classes suztomo.App
...
Constant pool:
   #1 = Methodref          #7.#27         // java/lang/Object."<init>":()V
   #2 = Fieldref           #28.#29        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = Methodref          #30.#31        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #4 = String             #32            // Hello World!
   #5 = Methodref          #6.#33         // suztomo/App.f:(Ljava/lang/String;)V
   #6 = Class              #34            // suztomo/App
   #7 = Class              #35            // java/lang/Object
   #8 = Utf8               <init>
   #9 = Utf8               ()V
  #10 = Utf8               Code
  #11 = Utf8               LineNumberTable
  #12 = Utf8               LocalVariableTable
  #13 = Utf8               this
  #14 = Utf8               Lsuztomo/App;
  #15 = Utf8               f
  #16 = Utf8               (Ljava/lang/String;)V
  #17 = Utf8               argument
  #18 = Utf8               Ljava/lang/String;
  #19 = Utf8               RuntimeVisibleParameterAnnotations
  #20 = Utf8               Ljavax/annotation/Nullable;
  #21 = Utf8               main
  #22 = Utf8               ([Ljava/lang/String;)V
  #23 = Utf8               args
  #24 = Utf8               [Ljava/lang/String;
  #25 = Utf8               SourceFile
  #26 = Utf8               App.java
  #27 = NameAndType        #8:#9          // "<init>":()V
  #28 = Class              #36            // java/lang/System
  #29 = NameAndType        #37:#38        // out:Ljava/io/PrintStream;
  #30 = Class              #39            // java/io/PrintStream
  #31 = NameAndType        #40:#16        // println:(Ljava/lang/String;)V
  #32 = Utf8               Hello World!
  #33 = NameAndType        #15:#16        // f:(Ljava/lang/String;)V
  #34 = Utf8               suztomo/App
  #35 = Utf8               java/lang/Object
  #36 = Utf8               java/lang/System
  #37 = Utf8               out
  #38 = Utf8               Ljava/io/PrintStream;
  #39 = Utf8               java/io/PrintStream
  #40 = Utf8               println
...
  private static void f(java.lang.String);
    descriptor: (Ljava/lang/String;)V
    flags: ACC_PRIVATE, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: aload_0
         4: invokevirtual #3                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         7: return
      LineNumberTable:
        line 9: 0
        line 10: 7
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       8     0 argument   Ljava/lang/String;
    RuntimeVisibleParameterAnnotations:
      parameter 0:
        0: #20()

This javap output tells that just using @Nullable does not create a class reference to javax.annotation.Nullable. It seems org.apache.beam.sdk.schemas.FieldValueTypeInformation was using @Nullable annotation in different way.

Beam's FieldValueTypeInformation

It reads the annotation using reflection:

    boolean nullable = method.isAnnotationPresent(Nullable.class);

This usage requires the Nullable class to be present in the class path. Example:

suztomo@suxtomo24:~/hello-nullable$ cat src/main/java/suztomo/App.java 
package suztomo;

import java.lang.reflect.Method;
import javax.annotation.Nullable;

public class App
{
    public static void main( String[] args )
    {

        Method method = App.class.getDeclaredMethods()[0];
        method.isAnnotationPresent(Nullable.class);
    }
}
suztomo@suxtomo24:~/hello-nullable$ java -cp ./target/classes suztomo.App
Exception in thread "main" java.lang.NoClassDefFoundError: javax/annotation/Nullable
	at suztomo.App.main(App.java:12)
Caused by: java.lang.ClassNotFoundException: javax.annotation.Nullable
	at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:352)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
	... 1 more

This now seems a genuine error.

Why sdks:java:core tests succeed?

It's because test runtime has guava-testlib.

testRuntimeClasspath - Runtime classpath of source set 'test'.
...
+--- com.google.guava:guava-testlib:20.0
|    +--- com.google.code.findbugs:jsr305:1.3.9
...

@suztomo
Copy link
Contributor Author

suztomo commented Dec 3, 2019

Created example repository to demonstrate the missing class problem.

https:/suztomo/beam-java-sdk-missing-nullable

@suztomo
Copy link
Contributor Author

suztomo commented Dec 9, 2019

Created BEAM-8917
javax.annotation.Nullable is missing for org.apache.beam.sdk.schemas.FieldValueTypeInformation

@suztomo
Copy link
Contributor Author

suztomo commented Dec 9, 2019

Created a PR: apache/beam#10324

@suztomo suztomo self-assigned this Dec 10, 2019
@suztomo suztomo added the Dependency Fix Fix needed in library in GCP orbit label Dec 10, 2019
@suztomo
Copy link
Contributor Author

suztomo commented Jan 30, 2020

It was true positive. Fixed in https://issues.apache.org/jira/projects/BEAM/issues/BEAM-8917

@suztomo suztomo closed this as completed Jan 30, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Dependency Fix Fix needed in library in GCP orbit
Projects
None yet
Development

No branches or pull requests

1 participant