-
Notifications
You must be signed in to change notification settings - Fork 1
BytecodeTransformations
Minha uses a separate class-loader for each host instance for two reasons:
-
A separate class-loader isolates host-instances in private virtualized sandboxes. Namely, separate static fields exist for each host.
-
The bytecode of classes is transformed upon loading to redirect invocations to the Java API to simulation models. The transformation applied depends both on the class being translated as well as the classes referenced.
In detail, the loader will can do three different transformations:
-
Redirect invocations of replaced Java API classes to faked classes. E.g. code that tries to use java.net.Socket is redirected to pt.minha.models.fake.java.net.Socket. This includes a number of hard corner cases, such as methods that have to be redirected explicitly (e.g. Class.forName()).
-
Redirect invocations to translated Java API classes. E.g. code that tries to use java.util.Hashtable is redirected to pt.minha.models.moved.java.util.Hashtable. This includes a number of hard corner cases, such as support for readObject/writeObject in serialization.
-
Replace native synchronization with simulation primitives. E.g. a synchronized block will be converted to use pt.minha.models.fake.java.util.concurrent.ReentrantLock. This includes reparenting the class under pt.minha.models.fake.java.lang.Object, which provides the lock instance. It also handles some hard corner cases on code that performs synchronization on global objects (e.g. java.lang.Integer).
By default, the Minha loader performs transformation 1, 2 and 3. Application and middleware classes under test are thus in this category. This means that off-the-shelf unmodified software packages get translated to interface with the simulation kernel without any additional configuration.
Classes to be translated can thus directly reference translated and global classes, indirectly they reference faked and moved classes. They cannot reference the original versions of faked or moved classes, as it would escape the virtualization sand-box.
The loader performs transformation 1, 2 and 3 on these classes. Moreover, references from other classes will trigger transformation 2 (when applicable). The platform provides a list of classes that are moved and, normally, the user of Minha should not be concerned with this.
Classes to be moved can thus directly reference global classes, indirectly they reference faked and other moved classes. They cannot reference the original versions of faked or moved classes, as it would escape the virtualization sand-box.
The loader only performs transformation 2 only on these classes. Moreover, references from other classes will trigger transformation 1 (when applicable). The platform provides a list of classes that are faked and, normally, the user of Minha should not be concerned with this.
Faked classes can thus directly reference faked, translated and global classes and indirectly moved classes. They can also reference original versions of faked classes. They cannot reference the original versions of moved classes, and when necessary, this is done indirectly.
Global classes are loaded by the parent system class-loader and shared between all host instances. This is useful for system classes (e.g. java.lang.Class) but also for communicating between different host instances (e.g. to implement simulated networking).
Global classes cannot thus reference translated, faked, or moved classes. They can also reference original versions of faked and moved classes.
The Minha class-loader is configured by specifying the rule used for each class in file instrument.properties available in the classpath. Sensible defaults are provided by the platform and, in most cases, need not be changed. Wildcards can be used to specify multiple classes. For instance:
java.util.Hashtable*=moved
mymodel.*=global
will configure java.util.Hashtable (and all its nested classes) as moved and all classes under mymodel package (recursively) as global (instead of translated).
Transformations applied to a translated class can be overridden using annotations. First, a class can be designated as global as follows:
import pt.minha.api.*;
@Global
public class Network {
public static Map<InetAddress,Host> hosts;
...
This means that the hosts variable will be global to the entire simulated world, and not just within one host instance.
Second, the set of transformations applied to a class can be changed as follows:
import pt.minha.api.*;
@Local(synch=true,useFaked=false,useMoved=false)
public class Debug {
public synchronized void print(String msg) {
System.out.println("DEBUG: "+msg);
}
This means that the method will be synchronized with simulation primitives, but will make use of native streams and System for output. This mechanism is key to break out of the sand-box to implement simulation models.