Giriraj Sharma
Giriraj Sharma

Reputation: 305

Read all classes decorated with a particular annotation at runtime

I am required to read all classes(interfaces) decorated with a particular annotation (say @HttpSecurity) at runtime. After scanning through, I wish to read and parse the fields(enum fields) of class decorated with annotations. For eg.

@HttpSecurity
public interface MyHttpSecurityConfig {

public enum secure {
    @Path(pathGroup = "/*", pathName = "")
    @Authc
    @Form(errorPage = "/error.html", loginPage = "/login.html", restoreOriginalRequest = "")
    @Authz
    @AllowedRoles(roles = { "roleA", "roleB" })
    @AllowedGroups(groups = { "groupA" })
    @AllowedRealms(realms = { "realmA" })
    @Expressions(expressions = { "#{identity.isLoggedIn()}" })
    Admin
  }
}

There might be one or more classes/interfaces decorated with @HttpSecurity. My first requirement is to fetch all such classes and second requirement is to build up HttpSecurityBuilder by reading the annotations and their values decorated on enum field(s). The second requirement is fine and can be done away using reflections. But, my problem is the first requirement. I want to achieve first requirement with JavaSE core i.e., without using any external dependency like google reflections. It might be assumed,if necessary, that we have the package name in which classes are to be scanned. Here is what I did usiNG cdi

Upvotes: 1

Views: 1304

Answers (2)

mperon
mperon

Reputation: 79

You can create a CDI Extension that observes the scan from CDI Annotations and create you customization, as the example below:

1) You need to create a Qualifier, by using you @HttpSecurity

@Qualifier
@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD, PARAMETER})
public @interface HttpSecurity {}

2) You need to create a extension by implementing the interface javax.enterprise.inject.spi.Extension:

    package net.mperon.cdi.extension;

    public class MyExtension implements Extension {

    private static final Logger log = LoggerFactory.getLogger(MyExtension.class);

    public <T> void processAnnotatedType(@Observes ProcessAnnotatedType<T> pat) {
        AnnotatedType<T> at = pat.getAnnotatedType();

        //if dont have you anotation, just continue
        if(!at.isAnnotationPresent(HttpSecurity.class)) {
            return;
        }

        //here you can read all annotation from object and do whatever you want:
        log.info("class: {}", at.getJavaClass());
        log.info("constructors: {}", at.getConstructors());
        log.info("fields: {}", at.getFields());
        log.info("methods: {}", at.getMethods());

        //and so more...

    }

}

3) You can see all methods and properties here

4) At last you need to create a service file, under META-INF/services named javax.enterprise.inject.spi.Extension

5) Inside this text file, you need to put you extension full class name, by example:

net.mperon.cdi.extension.MyExtension

Upvotes: 5

Spectre
Spectre

Reputation: 658

Unfortunately, Java doesn't provide an easy way to list classes in the "native" JRE. my favourite solution is the Google Reflections library, but if you don't want to use it there are other ways. One way would be to find the jar or jars in question and scan them for annotations on class files. This is achieved as follows:

// Jars are really just zip files with a different name
ZipInputStream zip = new ZipInputStream(new FileInputStream("/path/to/jar/file.jar"));

for(ZipEntry entry=zip.getNextEntry(); entry!=null; entry=zip.getNextEntry()) {
    String name = entry.getName();

    // We only want class files
    if(name.endsWith(".class") && !entry.isDirectory()) {

        // Remove the .class extension
        name = name.substring(0, name.length() - 6);

        // Replace the slashes in the path with '.'
        name.replaceAll("/",".");

        // Get the class object so we can use reflection on it
        Class<?> cls = Class.forName(name);
    }        
}

Upvotes: 2

Related Questions