import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.security.BasicPermission; public class OpaqueAnnotations { /** * Declares that an annotation annuls referential transparency on its target. * Tools which modify source or bytecode are constrained in the kinds of changes * they can make to the annotated element. For example: * *

Opacity may be specified on packages, types, fields, constructors, and methods. * (It may not be specified on local or anonymous classes.) *

The purpose of the meta-annotation is to decouple tools from frameworks. * For example, a bytecode obfuscator cannot be expected to know about every * kind of XML configuration file referring to Java elements ever created. * Instead, the author of the framework interpreting a particular kind of * configuration file may specify an opaque annotation for these elements: *

     * @Opaque
     * @Target(ElementType.TYPE)
     * public @interface RegisteredServlet {}
     * 
*

Now when a user writes a configuration file: *

     * <servlets>
     *     <servlet>org.me.SayHello</servlet>
     * </servlets>
     * 
*

the associated element can be annotated: *

     * package org.me;
     * @some.framework.RegisteredServlet
     * public class SayHello implements Servlet {...}
     * 
*

The annotation provides multiple benefits to the user: *

*

In the case of an existing annotation which is handled by a custom * annotation processor, marking the annotation opaque likewise protects * annotated elements from being considered by tools only for their apparent * semantic value. Non-opaque annotations such as @SuppressWarnings * or @NotNull would not receive this special treatment. *

An IDE might also compute a list of all changes to opaquely annotated * elements from a snapshot (e.g. last commit to a SCM) to current sources, * considering those changes which violate the declared opacity (e.g. renames * of {@link Opacity#NAME} elements). Such a summary display could be presented * to the user for manual review: any entry represents a source code change * which might have unforeseen implications beyond the compilability of the * current project. */ @Target(ElementType.ANNOTATION_TYPE) @Retention(RetentionPolicy.CLASS) public @interface Opaque { Opacity strictness() default Opacity.NAME; } /** * Defines the extent to which an annotation constrains changes to its target. */ public enum Opacity { /** * The structure of the annotated element must be preserved. * Permitted refactorings on the annotated element include: *

*

Forbidden refactorings include: *

*

This opacity may be used for elements which are not referred to explicitly * from any other source (Java or otherwise) but which must exist so that * an annotation processor will find them. It could also be used for an * annotation marking a field in a POJO as being mappable to a database column, * provided that the annotation specified the column name; if the column name * were defaulted from the field name, then {@link #NAME} must be used. */ SIGNATURE, /** * The structure and name of the annotated element must be preserved. * Compared to {@link #SIGNATURE}, renaming and moving are not permitted. * The same applies to any enclosing class(es), as these form part of the * fully qualified name of the element. *

This is the most usual opacity, used for elements which are somehow * referred to by name other than by direct reference in the Java language. * A typical example would be a class, field, or method named in an XML * configuration file associated with the application, and interpreted at * runtime by some sort of container or utility. */ NAME, /** * The annotated element and its accessible descendants must be preserved. * Similar to {@link #NAME} but applies also to public or protected * fields, constructors, methods, and (recursively) nested types of a type; * or types contained in a package, as well as their accessible descendants. * (This opacity is only meaningful if the annotation supports types or packages.) * Essentially provides a shortcut for specifying the {@link #NAME}-opaque annotation * on each descendant explicitly. */ MEMBERS } // ---- EXAMPLE USAGES ---- /** * Indicates that a member of a class is expected to be accessed reflectively. * The member may or may not be public. * {@link UndeclaredReflectPermission} may be used to help enforce the restriction. *

Note that a class itself may not be annotated, as anyone can load a class object. *

An IDE may use a nonempty accessor list to extend the usual results of a * "find usages" query. */ @Opaque(strictness=Opacity.NAME) @Target({ElementType.FIELD, ElementType.METHOD, ElementType.CONSTRUCTOR}) @Retention(RetentionPolicy.RUNTIME) public @interface ReflectivelyAccessed { /** * List of classes which are expected to access the annotated member using reflection. * If empty (the default), anyone might access it. */ String[] by() default {}; } /** * Permission to look up a class member without preauthorization. * Must be given the name: suppressAccessChecks */ public static class UndeclaredReflectPermission extends BasicPermission { public UndeclaredReflectPermission(String name) {super(name);} /** * May be called from an override of {@link SecurityManager#checkMemberAccess}. *

    *
  1. If the caller has {@link UndeclaredReflectPermission}, access is permitted. *
  2. If the member is annotated by {@link ReflectivelyAccessed} with an empty * accessor list, access is permitted. *
  3. If the member is annotated by {@link ReflectivelyAccessed} and the caller * of {@link Class#getMethod} etc. is present in its accessor list, access is permitted. *
  4. Otherwise access is denied. *
*

Note that since {@link SecurityManager#checkMemberAccess} does not specify * which member is being accessed, we must conservatively allow access to * any member of a class for which at least some member is authorized. * If {@link Class#getMethod} etc. were enhanced to call a new overload * of this method, we could do an exact check here. * @param clazz class on which reflective member access is to be performed * @param classContext invocation stack, as in {@link SecurityManager#getClassContext} * @throws SecurityException in case access is forbidden */ public static void checkMemberAccess(Class clazz, Class[] classContext) throws SecurityException { // ... } } /** * Marks a public entry point to an application. * The entry point would usually be specified by name outside of Java code. * Currently supported: *

*/ @Opaque(strictness=Opacity.NAME) @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.CLASS) public @interface EntryPoint {} /** * A declarative service registration. * May be added to META-INF/services of a JAR by an annotation processor * for later lookup by {@link ServiceLoader}. */ @Opaque(strictness=Opacity.SIGNATURE) @Target(ElementType.TYPE) @Retention(RetentionPolicy.SOURCE) @interface Service { Class[] interfaces(); } /** * Indicates that a publicly accessible element is part of a published API. *

The implicit assumption is that other elements technically accessible * by Java language/VM rules are not considered part of the API * and are subject to arbitrary change. It is common for tools to assume * that classes given as input (present in a source project, a JAR, ...) * form a closed system and that it is permitted to refactor public (or * protected) elements so long as all apparent usages are updated. This * annotation may be used to indicate to tools that certain of these * public elements form a published API which may not be changed arbitrarily. *

A {@link ClassLoader} in a module system might under some conditions refuse * to load a class if it statically referred to elements declared in another module * which were not marked with this annotation (or contained within an element * marked with it). A special Javadoc tool could also be configured to ignore * public and protected elements not marked (perhaps indirectly) with this * annotation. A special Java compiler tool (or build script) could permit * compilation against only those packages or types marked with the annotation * (and perhaps also against particular annotated members of a type * not itself annotated, though this is much harder to implement). */ @Opaque(strictness=Opacity.MEMBERS) @Target({ElementType.PACKAGE, ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.CONSTRUCTOR}) @Retention(RetentionPolicy.CLASS) @interface Published {} }