Basic Usage

We will see simple examples of access to methods and public fields of the Java Logging API using jInterflex.

Invocation of static and instance methods

Let’s suppose we need to execute through reflection a piece of code equivalent to this:

Logger log =  Logger.getLogger("myLogger");
log.info("Test");

With jInterflex we will create a public auxiliary interface like this:

@JInterflexProxy(realClassName="java.util.logging.Logger")
public interface PLogger {

    public static final StaticPart STATIC =  JInterflex.proxy(StaticPart.class);

    public interface StaticPart {

        public PLogger getLogger(String name);
    }
    public void info(String text);
}

The name of the actual class to invoke through reflection is in the realClassName attribute of the JInterflexProxy annotation. The methods of the auxiliary interface mimic the signature of the instance methods of the actual class to invoke. On the other hand, the static methods are written in a separate class, the inner interface StaticPart. It is important to mention that only the methods or public fields to access have to be included in the auxiliary interfaces.

The method JInterflex.proxy() creates a proxy object of the class passed as an argument (in this example our inner class StaticPart) which will allow access through reflection to static methods and public fields of the actual class (in this case java.util.Logger).

The static method returns a PLogger object that can be used transparently like the actual java.util.Logger object.

The definition of a constant called STATIC (or with any other name) is optional, but recommended, because it improves the readability of the invocation and it also allows to create the proxy object only once, improving performance.

Now we can write the invocations:

PLogger log =  PLogger.STATIC.getLogger("myLogger");
log.info("Test");

It may be observed that the jInterflex invocation is very similar to the one without reflection. Moreover jInterflex will take care of the exception handling, throwing the same exception that the invocation does (a subclass of JInterflexException can also be thrown if there’s a problem preparing the invocation).

We can see that the constant STATIC makes the code very compact and readable. If we don’t define the STATIC constant the invocation would be:

PLogger.StaticPart clz = JInterflex.proxy(PLogger.StaticPart.class);
PLogger log =  clz.getLogger("myLogger");
log.info("Test");

We can compare the invocation using jInterflex with the normal invocation using the Reflection API:

try {
    Class<?> logClz = Class.forName("java.util.logging.Logger");
    Method getLoggerMethod = logClz.getDeclaredMethod("getLogger", String.class);
    Object log = getLoggerMethod.invoke(null, "myLogger");
    Method infoMethod = logClz.getDeclaredMethod("info", String.class);
    infoMethod.invoke(log, "Test");
}
catch (IllegalAccessException ex) {
     // -- Handle exception
}
catch (IllegalArgumentException ex) {
     // -- Handle exception
}
catch (InvocationTargetException ex) {
     // -- Handle exception
}
catch (NoSuchMethodException ex) {
     // -- Handle exception
}
catch (SecurityException ex) {
     // -- Handle exception
}
catch (ClassNotFoundException ex) {
     // -- Handle exception
}

Of course, these catch blocks can be omitted if we put their equivalent throws parts in the declaration of the method. On the other hand, there are some exception enhancements in JDK 7+ which can reduce the verbosity of these multiple catch blocks. The central part of the invocations would remain the same anyway.

Constructors

We are going to use another example from the same java.util.Logging package:

@JInterflexProxy(realClassName="java.util.logging.ConsoleHandler")
    public interface PConsoleHandler {

        public static final StaticPart STATIC = JInterflex.proxy(StaticPart.class);

        public interface StaticPart {

            public PConsoleHandler ctor();
        }
    }

It is clear from this example that constructors are defined in a similar way as static methods, in the inner class part of the interface. The main difference is the name of the method that mimics the constructor. If the name of the static method is a special one reserved for constructors, the constructor will be invoked when this method is called. The default name for constructors is "ctor", but it may be modified setting the attribute ctorMethodName of the JInterflexProxy annotation.

The return type of the constructor method is the auxiliary interface itself, because this is the kind of objects the constructor will create (and because an interface has no constructor, so we cannot leave empty the return type, like in a real constructor).

Once we have the auxiliary interface, we can invoke the constructor:

PConsoleHandler consoleHandler = PConsoleHandler.STATIC.ctor();

If we want to change the name of the method to be considered a constructor by jInterflex, we must specify a value for the optional attribute ctorMethodName. For example, if we want to use create as the constructor method name for our last auxiliary interface, we would write:

@JInterflexProxy(realClassName="java.util.logging.ConsoleHandler", ctorMethodName="create")
    public interface PConsoleHandler {

        public static final StaticPart STATIC = JInterflex.proxy(StaticPart.class);

        public interface StaticPart {

            public PConsoleHandler create();
        }
    }

Public Fields

@JInterflexProxy(realClassName="java.util.logging.Level")
    public interface PLevel {

        public static final StaticPart STATIC = JInterflex.proxy(StaticPart.class);

        public interface StaticPart {

            @FieldAccess
            public PLevel FINE();
            @FieldAccess
            public PLevel INFO();
            @FieldAccess
            public PLevel WARNING();
        }
    }

The public fields are also defined as methods in the inner static part, but they must include the @FieldAccess annotation. The name of the method must be the same name of the public field to be read.

The read access of public fields using jInterflex would be done this way:

PLevel level = PLevel.STATIC.FINE();

Exception handling

There are three levels of exceptions in jInterflex:

  • Clearly identified errors of usage: If the auxiliary interface that contains the information is incomplete or incorrect, the method proxy will throw a JInterflexIllegalArgumentException. For example, if a static method is defined in an interface without an enclosing class or whose enclosing class has no annotations. If we use the constant STATIC in the auxiliary interface as it is suggested, the access to it will throw an ExceptionOnInitializerError (the first time it’s used) or NoClassDefFoundError (after the first time). These errors will be caught at runtime, but in any test done before the code is deployed, because they will always fail.

  • Runtime-detectable errors: For these errors, the method proxy won’t throw any exception, but any access to a method of the auxiliary interface returned will throw a JInterflexNotAvailableException that includes the root (cause) exception (it is not a checked exception, so it is not necessary to catch it nor to declare it in the throws clause). For example, If the name of the class to invoke is not found at runtime.

  • Exceptions thrown by the method call itself: They are thrown as they are in the moment of the invocation, like in a normal call.

Checking the availability of proxy classes

The main use case of this library is the conditional use of functionality that may or may not be available at runtime. We can simply use the library as we have been doing, invoking the methods and reading the fields through the proxy objects created by the framework. If they are not available at runtime, a JInterflexNotAvailableException will be thrown, we capture it in a catch block and nothing happens.

The problem with this is that it can lead to a lot of exception captures in normal situations (not in exceptional -error- situations, where capturing exceptions would be ok). In order to minimize the exception captures we should be able to ask if a proxy class is available at runtime before using it. For this task we can use the isProxyAvailable method of the JInterflex class. For example, with our first simple invocation we would do:

if (JInterflex.isProxyAvailable(PLogger.STATIC)){
    PLogger log =  PLogger.STATIC.getLogger("myLogger");
    log.info("Test");
}
else {
  // -- Alternatives if we don't have these classes available at runtime
  ...
}

If we want to have the exception of the problem that prevented the classes to be available, we can use the getProxyException method. For example:

if (JInterflex.isProxyAvailable(PLogger.STATIC)){
    PLogger log =  PLogger.STATIC.getLogger("myLogger");
    log.info("Test");
}
else {
    // -- Trace the exception to the console, or with a Logger, etc...
    JInterflex.getProxyException(PLogger.STATIC).printStackTrace();
  ...
}

It’s important to mention that the methods JInterflex.isProxyAvailable and JInterflex.getProxyException would throw an exception if there is an error of the first group described above. Only the errors that can only be detectable at runtime (JInterflexNotAvailableException) would be caught. This allows to detect early the mistakes in the auxiliary interfaces.