Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

@SafeVarargs and Java 6 interoperability

I have a method with a generic varargs parameter in my API. I want my API to be Java 6 source and binary compatible, but it would be nice if Java 7 API consumers wouldn't suffer from unnecessary "varargs" warnings.

A trick that I can think of is to add my own java.lang.SafeVarargs annotation to my API and ship it with my deliverable. As an effect:

  • Java 6 compilers wouldn't recognise this annotation and just ignore it.
  • Java 7 compilers would recognise this annotation, and probably (?) class-load the one from the JDK first and thus they wouldn't produce the annoying warnings anymore.

Apart from license concerns, is this guaranteed to work? It seems to work with javac. Or are there configurations where re-defining an annotation from the JDK has undesireable side-effects at the call-site? Or is there another way to solve this Java 6/7 interoperability issue?

A related question:

  • Using Java 7 SDK features in Java 6
like image 867
Lukas Eder Avatar asked May 03 '13 20:05

Lukas Eder


1 Answers

Question: is this guaranteed to work?

Answer: it depends. I would like to point out one potential issue.

The "real" @SafeVarargs annotation is declared with RetentionPolicy.RUNTIME (see here). The reason for this (in contrast with @Override, which has RetentionPolicy.SOURCE) is probably to allow the compiler to check for it at the calling site. However,

  • IF your version of the SafeVarargs annotation is declared with RetentionPolicy.RUNTIME
  • AND the code which uses it is run using JRE 6 (which does NOT contain this annotation)
  • AND your version of the SafeVarargs annotation is on the classpath

THEN any code which uses Reflection to get the method annotations at runtime (e.g. Spring framework) will fail miserably with SecurityException because the package name of the annotation starts with "java." (see the relevant code from the ClassLoader.preDefineClass() method and the stactrace below, both from Sun JRE 1.6.0_31).

If the annotation is on the classpath at compile time but not at runtime ("provided" scope in maven) then there is no exception thrown (at least with Sun JRE).

The best solution would be to have some Maven artifact like java.lang.java7-annotations at disposal and use it with scope "provided". I found something here (artifact: com.google.backport.safevarargs) but it is not in the central repo.


if ((name != null) && name.startsWith("java.")) {
    throw new SecurityException("Prohibited package name: " +
                                 name.substring(0, name.lastIndexOf('.')));
}
Exception in thread "main" java.lang.SecurityException: Prohibited package name: java.lang
    at java.lang.ClassLoader.preDefineClass(ClassLoader.java:479)
    at java.lang.ClassLoader.defineClassCond(ClassLoader.java:625)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:615)
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:141)
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:283)
    at java.net.URLClassLoader.access$000(URLClassLoader.java:58)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:197)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
like image 143
uk4sx Avatar answered Sep 27 '22 18:09

uk4sx