The org.bzdev.lang package

For specific topics, please see

The Callable interface

Starting with the initial version of Java (1.0), Java has provided an interface named Runnable with a single method named run(). This is used to provide the code that a thread will run. The earliest version of the BZDev class library (not publicly released) included a corresponding interface named Callable with a zero argument method named call(). The interface Callable is a synonym for Runnable in terms of functionality, but was added for two reasons:
  • to document that one was providing a method to be called, not the code that a thread to run.
  • so that one could discriminate between code that should be run in a thread from code that could be called. In particular, the org.bzdev.devqsim package allows both to be scheduled and needed to be able to tell the difference between the two cases at compile time. Similarly, the org.bzdev.scripting package contains a class that uses the Callable interface.
Subsequently, with the introduction of streams and lambda expressions, the package java.util.concurrent also defined an interface named Callable. While one would prefer that the names were different, the chances of a name conflict are small: the discrete-event simulation classes runs only one thread at a time, and it is not advisable to use multithreading with scripting unless one can ensure that only a single thread runs at any particular time.

The Callable interface provided by this class library does not take any arguments and does not return a value. There are, however, several related interfaces:

  • CallableArgs<Args>. This interface provides a method named call that does not return a value and that takes any number of arguments of type Args.
  • CallableArgsReturns<T,Args>. This interface provides a method named call that returns a value of type T and that takes any number of arguments of type Args.
  • CallableReturns<T>. This interface provides a method named call that This interface provides a method named call that returns a value of type T and that does not take any arguments.
  • ExceptionedCallable. interface provided by this class library does not take any arguments and does not return a value. It may throw an exception (any subclass of java.lang.Exception).
  • ExceptionedCallableArgs<Args>. This interface provides a method named call that does not return a value and that takes any number of arguments of type Args. It may throw an exception (any subclass of java.lang.Exception).
  • ExceptionedCallableArgsReturns<T,Args>. This interface provides a method named call that returns a value of type T and that takes any number of arguments of type Args. It may throw an exception (any subclass of java.lang.Exception).
  • ExceptionedCallableReturns<T>. This interface provides a method named call that returns a value of type T and that does not take any arguments. It may throw an exception (any subclass of java.lang.Exception).

Library loading

The class ResourceLibLoader supports looking up and loading a resource containing a shared library. A naming convention is used to allow the correct resource to be obtained for a given version of the operating system and a given machine architecture.

Dynamic Methods

Normally Java looks up an object's method at run time but bases the lookup on the method's signature (specifically the types of the method's arguments), which is determined at compile time. While this restriction is desirable for performance reasons, it is sometimes easier to develop software if dynamic methods are supported, in which the argument types are determined at run time. The org.bzdev.lang package supports dynamic methods by using annotations (staring with release 6 of the JDK, annotations are supported by the Java compiler). The mechanism is efficient, but with two restrictions: dynamic methods cannot be static methods and the use of generic types is not supported. The primary reason for not allowing generic types is that type-erasure is not compatible with the dispatch methods provided by "helper" classes.

Dynamic methods are supported by 6 annotation types (only 3 or 4 are needed for the simplest cases). The necessary types are

  • DynamicMethod. This annotation tags a method specified in a class or an interface as a dynamic method, proving the most general type for the methods (which can have return values and throw exceptions as desired). It takes the class name of a "helper" class as its argument (the helper class will be generated automatically). A dynamic method may not be a static method.
  • DMethodImpl. This annotation tags a method as being one that implements a dynamic method for specific argument types, and takes the name of the helper class used in the DynamicMethod annotation as an argument (the class name serves as a key to match dynamic method implementations with the corresponding dynamic method). These methods may be declared to be static.
  • DMethodContext or DMethodContexts. Each class that implements a dynamic method needs a "local helper" class for that dynamic method. A DMethodContext annotates the class that implements a dynamic method and, using the dynamic method's helper class as a key, provides the corresponding local helper class. If a class implements multiple dynamic methods, The DMethodContexts method should be used, which takes an array of DMethodContext annotations as its argument. A class annotated with DMethodContext or DMethodContexts must be declared to be static if the class is an inner class.

The two remaining annotations provide additional control:

  • DMethodOptions allows one to control the locking used by the helper class, to allow optimizations for single-threaded programs, to adjust cache sizes, and to enable tracing of method searches. The locking-mode values allow one to specify no locking, a mutex lock, a read-write lock, or a system default (normally a mutex but this can be changed at compile time).
  • DMethodOrder is used when dynamic methods are dispatched based on multiple arguments. It controls the search order for arguments and determines which arguments should use compile-time types rather than run-time types.

The implementation of a dynamic method should call its helper's dispatch method, returning a value of the return type is not void. The first argument to dispatch must be this, followed by the arguments declared in the method. Each class that implements a dynamic method has a DMethodContext annotation, specifying a local helper, for that method, and must call the local helper's register method in a static initializer, calling register with no arguments. If the system class loader is set to org.bzdev.lang.DMClassLoader, the register method will be called automatically. One can change the system class loader to DMClassLoader by setting the following option for the java command:


 java -Djava.system.class.loader=org.bzdev.lang.DMClassLoader ...

For hints on how to use the compiler and what to include in JAR files, see the documentation for DynamicMethod.

An example for the use of a dynamic method when the dynamic method is specified in a class is the following (note: when referring to a helper in a different package, the fully qualified class name must be used):


public class Foo {
     @DynamicMethod("FooIncrHelper")
     public  Number incr(Number x) throws Exception {
        return FooIncrHelper.getHelper().dispatch(this, x);
     }
 }

@DMethodContext(helper="FooIncrHelper", localHelper="BarIncrHelper")
public class Bar extends Foo {
    static {
        BarIncrHelper.register();
    }

    @DMethodImpl("FooIncrHelper")
    public Number doIncr(Integer x) thows Exception {
        return Integer.valueOf(x + 1);
    }
}
The call to dispatch will throw a runtime exception named MethodNotPresentException> if no method that matches the arguments is found. This exception can be caught to implement the default behavior for the dispatch method and is located in the org.bzdev.lang package. *

If incr was declared in an interface, an implementation must appear in the least specific class(es) that implement that interface. For example:


public interface Incrementer {
     @DynamicMethod("FooIncrementerHelper")
     public Number incr(Number x) throws Exception;
}

@DMethodContext(helper="FooIncrementerHelper",
                 localHelper="FooIncrHelper")
public class Foo implements Incrementer {
    static {
        FooIncrHelper.register();
    }
    public Number incr(Number x) {
        return FooIncrementerHelper.getHelper().dispatch(this, x);
    }
    @DMethodImpl("IncrementerHelper")
    public Number doIncr(Integer x) thows Exception {
        return Integer.valueOf(x + 1);
    }
}

@DMethodContext(helper="IncrementerHelper", localHelper="BarIncrHelper")
public class Bar extends Foo {
    static {
        BarIncrHelper.register();
    }
    @DMethodImpl("IncrementerHelper")
    public Number doIncr(Bar x) thows Exception {
        return Integer.valueOf(x + 1);
    }
}

For compilation, see the instructions in the BZDev library description.

Background

Several other approaches have been used to create similar capabilities.

One approach that is used when dynamic methods are not supported in a language is to use the visitor design pattern. The idea is to define two interfaces such as Visitor and Visitable defined as follows:


    interface Visitor {
      void visit(Foo obj);
      void visit(Bar obj);
    }
    interface Visitable {
        void accept(Visitor arg);
    }
then Foo is defined as follows:

    class Foo implements Visitable {
      void accept(Visitor arg) {
          arg.visit(this);
      }
    }

    class Bar extends Foo {
      void accept(Visitor arg) {
          arg.visit(this);
      }
    }

    class SomeVisitor implements Visitor {
       void visit(Foo obj) {
         ...
       }

       void visit(Bar obj) {
         ...
       }
    }
For mimicking dynamic methods, the accept method is written as given above: it merely calls the visit method with an argument of this, whose type matches the type of the class in which the acceptmethod is defined. The accept method is defined in all subclasses as well (subclasses of Foo in this example), so that the type at run time is known in the call to visit. When accept is called on an object of type Foo, even if referenced by a variable declared to be Visitable, the accept method that is called is the one defined for Foo. The accept method of Foo then calls its argument's visit method, passing it an argument of type Foo.

The downside to the use of this design pattern is that every time a new class that implements Visitable is defined, one must add a method to the Visitor interface and all Visitor classes must then implement that new method, even if there is really nothing for the method to do. In addition, as mentioned above, an accept method must be defined in all classes that implement Visitable and their subclasses, which is tedious when there are a number of subclasses. In addition, the interface and class definitions are typically in separate files. Attempts to mitigate these limitations have included the use of the Java reflection API (Jens Palsberg and C. Barry Jay, "The Essence of the Visitor Pattern"); however, the run-time cost is substantial: what would take 1.17 seconds using the visitor design pattern took 4 minutes, 59.93 seconds using the reflection API, an increase in running time of about a factor of 300 for trivial methods.

An alternative approach, which generates byte codes at runtime and loads those into the Java virtual machine, has also been proposed (Rémi Fora, Etienne Duris, and Gilles Roussel, "Reflection-based implementation of Java extensions: the double-dispatch use-case"). This latter approach is far more efficient (and comparable to the approach described below) but may fail when a sandbox security model is in use due to restrictions on generating new classes at run time (e.g., in applets and assuming no changes to the Java language). Security managers, however, have been removed from recent versions of Java.

The DynamicMethod annotation and some related annotations provides a design pattern that addresses some of these issues by allowing method look-up to be performed more efficiently at run time, with all the application code created at compile time so that there are no issues with security managers. A simple design pattern must be followed. One declares a dynamic method using a DynamicMethod annotation: for example,


 public class Foo {
     @DynamicMethod("FooIncrHelper")
     public  Number incr(Number x) throws Exception {
        return FooIncrHelper.dispatch(this, x);
     }
 }
This annotation declares that a generated class named FooIncrHelper will provide a method called dispatch that implements a dynamic method. The arguments for dispatch in the helper class are this to indicate the current instance, followed by the arguments for the method incr. If the DynamicMethod annotation appeared in an interface declaration, then a class implementing the method and calling the helper's dispatch method must appear in the first subclass of Object implementing the interface.

A subclass of Foo that implements the dynamic method incr will then use a DMethodImpl annotation to tag a method implementing the dynamic method for particular argument types (the dynamic method implementing incr will have a different name). The subclass itself will be annotated with a DMethodContext annotation, which provides the name of a subclass-specific helper class (the localHelper attribute) and associates it with the helper for the dynamic method incr. For example,


@DMethodContext(helper="FooIncrHelper", localHelper="BarIncrHelper")
public class Bar extends Foo {
    static {
       BarIncrHelper.register();
    }
    @DMethodImpl("FooIncrHelper")
    public Number doIncr(Integer x) throws Exception {
        return Integer.valueOf(x + 1);
    }
}
The static method register() provided by the local-helper class must be called when a class is initialized and when the annotation @DMethodContext is used. With earlier versions of Java, a class loader provided by the org.bzdev.lang package could call this method so that an explicit call would not be needed,but unfortunately that class loader is not work when Java modules are used.

If class Foo also provided a method annotated with @DMethodImpl("FooIncrHelper"), Foo itself would have to have a DMethodContext annotation with its helper attribute set to FooIncrHelper and its localHelper attribute set to some other class name. When multiple @DMethodContext annotations are needed for a class, the annotation @DMethodContexts should be used - @DMethodContexts has a single 'value' defined - an array of @DMethodContext. The design decision to annotate the class with the local helper data rather than the individual method was due in part for supporting the class loader DMClassLoader, which removes the need for the static initializers that call register() methods of the local helper classes.

The look-up can be based on more than one argument, and the default behavior is to do nothing, which eliminates the coding required when using the visitor design pattern when a change to the Visitor interface is made. The order in which argument types are searched can be controlled with a DMethodOrder annotation, applied to method definition or declaration annotated by the DynamicMethod annotation. In addition, One may use a throws clause in the method definitions and declarations.

For the single argument case, the overhead over a trivial implementation (a couple of method calls, and an if statement using the instanceof operator) is roughly a factor of 15, which can be reduced to a factor of 10 by setting an option to turn off locking for cases in which objects using a specific class are accessed by only one thread at a time. This assumes the method called performs a trivial operation - the overhead will not be noticeable if the method called does anything substantial. The implementation uses "helper" classes to dispatch method calls at run-time. These classes are automatically generated by the java compiler via an annotation processor. For the two-argument case, the overhead is approximately factor of 34 with locking and 29 without locking (in both cases, the running time is higher than in the single-argument case).

The equivalent of the visitor design pattern example above can be implemented using dynamic methods. For example:


    interface Visitor {
        @DynamicMethod("VisitorHelper")
        void visit(Object arg);
    }

    @DMethodContext(helper = "VistorHelper",
                    localHelper = "SomeVisitorHelper")
    class SomeVisitor implements Visitor {
        static {
          SomeVisitorHelper.register();
        }
        void visit(Object arg) {
          VisitorHelper.getHelper().dispatch(this, arg);
        }

        @DMethodImpl("VisitorHelper")
        void doVisit1(Foo arg) {
            ...
        }

        @DMethodImpl("VisitorHelper")
        void doVisit2(Bar arg) {
            ...
        }
    }
The Visitable interface is not needed, and explicit methods can be added only for those argument classes (Foo and Bar in this example) for which some action is needed. The Visitor interface does not have to be modified, and the visit method is not defined in subclasses of SomeVisitor. The method doVisit(Foo) does not have to be defined in subclasses of SomeVisitor unless it has to be coded differently in the subclass. If a default action is needed, one can add a declaration such as

        @DMethodImpl("VisitorHelper")
        void doVisit(Object arg) {
            return;             // do nothing.
        }
that will match any object passed as an argument. Alternatively, one may change the implementation of the visit method to catch an exception that indicates that the dispatch mechanism could not find a method to invoke:

        void visit(Object arg) {
          try {
              VisitorHelper.getHelper().dispatch(this, arg);
          } catch (MethodNotPresentException e) {
          }
        }
It is better in this case to define the doVisit method with an argument of type Object, as one of the doVisit methods may call a different dynamic method that throws MethodNotPresentException due to a programming error, in which case the partial execution of some methods may put the application into an illegal state.

The example above assumes the use of a dynamic-method-aware class loader such as org.bzdev.lang.DMClassLoader. If such a class loader is not used, then a static initializer has to be added to the SomeVisitor class:


    @DMethodContext(helper = "VistorHelper",
                    localHelper = "SomeVisitorHelper")
    class SomeVisitor implements Visitor {
        static {
             SomeVisitorHelper.register();
        }
        ...
    }
The call to the register() simply forces the SomeVisitorHelper class to be initialized: during initialization, SomeVisitorHelper will register itself with VisitorHelper to create tables that allow the correct method to be looked up at run time. The general rule is that whenever a class is annotated with a @DMethodContext or @DMethodContexts annotation, for each localHelper attribute provided, there must be call to each helper's static method register() in the class' initializer.

In the example above, if all classes for which visit can be called are subclasses of SomeVisitor, then the interface definition is not needed and the code can be written as follows:


    @DMethodContext(helper = "VistorHelper",
                    localHelper = "SomeVisitorHelper")
    class SomeVisitor {

        @DynamicMethod("VisitorHelper")
        void visit(Object arg) {
          VisitorHelper.getHelper().dispatch(this, arg);
        }

        @DMethodImpl("VisitorHelper")
        void doVisit(Foo arg) {
            ...
        }

        @DMethodImpl("VisitorHelper")
        void doVisit(Bar arg) {
            ...
        }
    }
Subclasses can implement doVisit methods, annotated with @DMethodImpl("VisitorHelper"), and subclasses that implement this method must of course have a @DMethodContext annotation for the subclass with a unique localHelper class name. For a similar example where a register() is called explicitly, one might define the interface (the complete file is shown)

package dmtest;
import org.bzdev.lang.annotations.*;

public interface Visitor {
    @DynamicMethod("VisitorHelper")
    void visit(Object arg);
}

package dmtest.a;
import org.bzdev.lang.annotations.*;

@DMethodContext(helper="VisitorHelper", localHelper="SomeVisitorHelper")
public class SomeVisitor implements Visitor {

    static {
        SomeVisitorHelper.register();
    }

    public void visit(Object arg) {
        VisitorHelper.getHelper().dispatch(this, arg);
    }

    @DMethodImpl("VisitorHelper")
    void doVisit1(Double arg) {
        System.out.println("double = " + arg.toString());
    }

    @DMethodImpl("VisitorHelper")
    void doVisit2(Integer arg) {
        System.out.println("integer = " + arg.toString());
    }

    public static void main(String argv[]) {
        Object obj1 = Double.valueOf(10.0);
        Object obj2 = Integer.valueOf(20);

        SomeVisitor sv = new SomeVisitor();

        sv.visit(obj1);
        sv.visit(obj2);
    }
}
The output when the program is run is

double = 10.0
integer = 20
By contrast, the following program will fail at compile time:

package dmtest.a;
import org.bzdev.lang.annotations.*;

public class SomeVisitor2 {
    public void visit(Double arg) {
        System.out.println("double = " + arg.toString());
    }
    public void visit(Integer arg) {
        System.out.println("integer = " + arg.toString());
    }

    public static void main(String argv[]) {
        Object obj1 = Double.valueOf(10.0);
        Object obj2 = Integer.valueOf(20);

        SomeVisitor sv = new SomeVisitor();

        sv.visit(obj1);
        sv.visit(obj2);
    }
}
Without dynamic methods, one would have to add a method such as

    public void visit(Object arg) {
        if (arg instanceof Double) visit((Double) arg);
        else if (arg instanceof Integer) visit((Integer) arg);
    }
which is error prone due to the need to make a change in multiple places.

For additional information about dynamic methods, read the documentation for

Miscellaneous classes

Finally, there are several miscellaneous classes:
  • ClassFinder. This class provides static methods that can recover information about a class given its name.
  • MathOps. This class provides static methods that compute floors or round numbers towards 0 or ±∞.
Other classes specify permissions that the user may wish to grant.