Universität Paderborn - Home Universität Paderborn
Die Universität der Informationsgesellschaft
Documentation

In order to migrate the calling thread or to create a checkpoint, migration points have to be inserted into a program. Only there – at statements consisting of a call to the migrate() or checkpoint() method of the library class padmig.PadMig – the calling thread is migrated to a remote host or a checkpoint of the calling thread is created, respectively. In particular, these methods have the following signatures:

public static void migrate(java.net.URL migrationServer) throws padmig.MigrationException;

public static void checkpoint(java.io.File backupFile) throws padmig.MigrationException;

When a checkpoint is generated, the execution continues locally. In case the migration or checkpointing fails, a MigrationException will be thrown.

In order to allow migrations inside a method either directly by calling the migrate() or checkpoint() method or transitively by calling some other migratable method, the particular method has to be annotated with padmig.Migratory. The method, which forms the bottom element of the call stack to be migrated has to be annotated with padmig.Undock instead of Migratory (see figure below). According integrity checks are performed at compile time: the PadMig compiler migc stops with an error if a migratable method is called from an ordinary method, i.e., a method which is neither migratable nor undockable. migc also ensures that migratory annotations are consistent when inheriting from a (possibly abstract) class or when implementing or inheriting interfaces.

We distinguish two kinds of migrations: if a method annotated with Undock has a return value, we call migrations on this call stack synchronous as the local execution has to wait for the undocked method to return; otherwise, if the Undock method has no return value, we call migrations on that call stack asynchronous because the local execution can already continue with the next statement after the call to the Undock method directly after the Undock method has migrated.

As all local variables are migrated by default, it is necessary to mark the locals which cannot be migrated (because they are not serializable) or should not be migrated (as they generate an unnecessary overhead). This can be done using the padmig.DontMigrate Annotation. The PadMig compiler does not explicitly check for all possible kinds of side effects; however, the most prominent issues, such as open files or sockets, are often implicitly detected, e.g., because file or socket handles are not serializable. Note: In order to support, e.g., open socket connections in a transparent way with respect to inheritance etc., it is necessary to adapt the runtime environment appropriately by rewriting the according classes using proxies, which is out of the scope of this work.

The object whose method is to be migrated has to be serializable of course, i.e., it must implement the java.io.Serializable interface. The main class of an application is also required to implement a special main method defined in the padmig.Migratable interface:

public java.io.Serializable migratableMain(java.io.Serializable[] args);

For interoperability reasons, the parameters and return value of the main method can be any serializable object. The following example demonstrates all the languages features:

import java.io.*;
import java.net.*;
import padmig.*;

public class Example implements Migratable, Serializable {

	public boolean migrationNecessary() {
		// evaluate situation here ...
		return true;
	}
	
	public URL getMigrationTarget() {
		try {
			return new URL("pp://some.host:1234/migration_server_name");
		} catch (MalformedURLException mue) {
			// ...
		}
	}

	@Migratory
	public int someMethod(int n) throws MigrationException {
		for (int i=n; i>=0; i--) {
			// some complicated calculation here
			@DontMigrate
			File someFileHandle;
			// complicated calculation continued
			if (i % 10 == 0) {
				PadMig.checkpoint(new File("/path/to/bak/checkpoint-" + n + ".bak"));
			}
			if (migrationNecessary()) {
				PadMig.migrate(getMigrationTarget());
			}
		}
	}

	@Undock
	public int syncUndockMethod(int n) throws MigrationException {
		return 2 * someMethod(n);
	}

	@Undock
	public void asyncUndockMethod(int n) throws MigrationException {
		// remote result output
		System.out.println("the result is " + (2 * someMethod(n)));
	}

	public Serializable migratableMain(Serializable[] args) {
		try {
			asyncUndockMethod(21);
			System.out.println("thread has undocked");
			// local result output
			System.out.println("the result is " + syncUndockMethod(23));
		} catch (MigrationException e) {
			System.out.println("migration has failed");
			e.printStackTrace();
		}
		return null;
	}
}

Once you have compiled both your migratable program with the PadMig compiler migc and and the resulting code with the Java compiler javac, you are ready to run your migratable program – either standalone or as part of and other application.

In order to execute a migratable program stand-alone, you need to run a migration server padmig.standalone.Server on every possible migration target and start the migratable program via padmig.standalone.Client, which is a wrapper around the migratable main method. As the internal communication of PadMig is performed via the Paderborn Remote Method Invocation (PadRMI) library, the library padrmi.jar has to be included in the classpath. Java properties specify the IP address, port, etc. of the local machine and the codebase: either on a web server or file server as a http: or file: protocol URL or a pp: protocol URL pointing to one of the migration servers. Note that multiple cooperating migration servers must all point to the same codebase. Handlers for the PadRMI protocol (denoted by pp: in URLs) have to be installed into all participating JVMs as well via the Java Protocol Handler mechanism.

Instead of running your migratable application standalone, you can also integrate it into other Java applications – like it has been integrated into the web computing library PUB-Web – using the PadMig interoperability interface. In order to enable your application to accept incoming thread migrations, you need to start the PadRMI daemon and register a padmig.iop.Service, for example padmig.iop.DefaultServiceImpl:

padrmi.Server.startDefaultServer();
padrmi.Server.getDefaultServer().addObject(padmig.lib.PadMigLib.PADRMI_SERVICE_NAME, new padmig.iop.DefaultServiceImpl(), padmig.iop.Service.class, null, null);

Furthermore, you will probably want to implement the padmig.iop.MigrationListener interface and register it with the DefaultServiceImpl.addMigrationListener() method in order to obtain references to incoming migratable objects and to be notified about migration failures:

public void migratableObjectArrived(MigrationEvent event) {
	System.out.println("incoming migration associated with object " + event.getObject());
}

public void migratableObjectContinuationFailed(MigrationEvent event) {
	System.out.println("migration associated with object " + event.getObject() + " failed:");
	event.getError().printStackTrace();
}

Supposed you have correctly set the PadRMI related properties like in the standalone case and your migratable program HelloWorld is located at the path specified in the padrmi.path property, you can start your program from an enclosing Java application like this:

Object returnValue = padmig.Launcher.launch(new URL(padrmi.Server.getDefaultServer().getURL() + "/"), HelloWorld.class.getName(), new Serializable[] { "hi", "there!" });

Index A – Z | Imprint