SATIN - Programmer's Guide

[about] [documentation] [applications] [javadoc] [downloads] [publications] [metamodel] [project page]

Introduction to SATIN

SATIN (System Adaptation Targeting Integrated Networks) is a component metamodel (instantiated as a middleware system) that can be used to build adaptable systems. It is primarily geared for mobile systems, but is suitable for general resource constrained environments.

The main ideas behind SATIN are the following:
  1. Everything is a component.
  2. SATIN is a structurally reflective system. This means that applications running over SATIN can reason about what the local system can do, i.e. what functionality is currently installed.
  3. SATIN applications can use logical (code) mobility primitives to transfer objects, classes, data and components to other nodes, thus adapting the runtime functionality of nodes.
  4. Components that can be adapted by receiving code are Reflective components.

The SATIN middleware system itself, as well as applications developed over it, are represented as a collection of interacting collocated components. A SATIN component encapsulates some sort of functionality. Examples include user interfaces, audio codes, compression libraries, etc. SATIN components separate interfaces and implementations. As such, each component exports functionality, by implementing one or more interfaces called facets.

SATIN components are described using a collection of attributes. An attribute is a tuple of a key and a value. The set of all attributes of a component are called the properties of the component. The properties map each key to each associated tuple. Attributes are fundamental to the way that SATIN works - they are used to form query templates to reason about what components are available locally and remotely.

At the very least, a SATIN component implements the ComponentFacet (or a specialisation of it). This allows reasoning about the component and the values of its attributes. It also exports a basic component lifecycle, including a constructor and a destructor. A SATIN component has two states, ENABLED and DISABLED. The semantics of those are left to the programmer. The state is controlled by the ComponentFacet.

A reference to the ComponentFacet of all SATIN components are available via the SATIN container, which is a component specialisation. Registration and removal of components is handled by one or more Registrars. A registrar is also a component specialisation.

SATIN provides migration primitives for classes, objects, data and components. These are handled by a Deployer, which is again a SATIN component specialisation. The Deployer is responsible for sending and receiving Logical Mobility Units (LMUs). An LMU is a container that encapsulates arbitrary numbers of Classes, Objects, Data and Components. It is also described by attributes. An LMU can optionally have a handler, which is a class created by the programmer and is used to fully customise deployment.

Presentation slides about SATIN are available online here.

Programming in SATIN

This section provides information on how to program using SATIN and concludes by showing how to write a test component in SATIN and how to transfer it to another host.

Note that the SATIN middleware system is inherently modular. If the behaviour of one of the default components (e.g. Registrar, Deployer, etc.) is not satisfactory, they can be modified and replaced with more suitable ones.

Creating SATIN components


To create a simple SATIN component, create a class that extends
edu.UCL.satin.arch.components.Component and implements edu.UCL.satin.arch.facets.ComponentFacet.

SATIN components may depend on the functionality provided by other components. The component developer is responsible for retrieving the references of the other components required (see 2b). Note that SATIN components must have exactly one Java constructor, with no arguments.

The SATIN middleware system requires that every component defines the following attributes:
A component can either be a simple component or a Reflective one. Reflective components can be dynamically adapted at runtime by retrieving LMUs. By definition, the container is a Reflective component as it can receive components at runtime. When sending an LMU, both the physical destination (i.e. IP address) but also the logical destination (the identifier of the Reflective component the LMU is destined for) must be defined.

A reflective component has the option of inspecting an LMU before accepting it in its entirety, partially accepting it, rejecting it completely or even instantiating the LMUs handler.

Registering & Requesting Components

The central aspect of each instance of the SATIN middleware system is the Container, which contains references to all components available. There can be exactly one instance of the container on each node. The implementation of the container is called edu.UCL.satin.impl.container.Core and its component identifier is "STN:CONTAINER". To initialise it, call new edu.UCL.satin.impl.container.Core();

To retrieve a reference to the container, use the getContainer() static method. To retrieve the registrar, use the getDefaultRegistrar() method.

There are various ways to request components. The simplest one, is to simply pass the ID of a component to the getComponent() method. For example, another way to get a reference to the default registrar, would be to call getComponent("STN:COREREGISTRAR") on the container.

A more powerful way to request a component, is to form a query template using component attributes, and pass it over to the container. This works as follows: This will return an array of all components that match all the given attributes. The SATIN middleware system defines a GenericAttribute as an attribute generalisation that uses Objects to represent keys and values. There are various subtypes defined. What is of special interest, is the MatchAttribute. A MatchAttribute is an immutable attribute that allows for offering customisable comparator semantics with generic attributes. As such, a match attribute encapsulates a MatchFilter, which is an interface implementations of which perform the actual comparison between the value contained in a generic attribute and the one contained in the match attribute. As such, when comparing using GenericAttributes the comparison would only evaluate successfully if the value of the attributes exactly matched. Using a MatchAttribute, however, allows for arithmetic comparison, regular expression comparison, etc. There are various MatchAttributes defined. This allows, for example, to return the components that are provided by SATIN (as they are all prefixed with "STN:"), returning versions of a component greater than 3, etc.

Components can register listeners with the Container to be notified when components satisfying a certain set of criteria (defined by a query template) are registered with the system. A listener must implement the ComponentListener facet.

Packages and Documentation


The SATIN source code is documented, and an API can be generated using the Javadoc tool. Note that the identifiers of all components provided by SATIN are prefixed with STN:.

The implementation is conceptually split into various packages. The general structure is the following:

edu.UCL.satin.arch contains the abstract SATIN metamodel
edu.UCL.satin.impl contains the instantiations of the above as the SATIN middleware system
edu.UCL.satin.impl.arch.comms contains the middleware system's advertising and discovery framework
edu.UCL.satin.arch.facets contains the facets used by the metamodel
edu.UCL.satin.impl.advertising.central contains a simple publish subscribe advertising and discovery implementation
edu.UCL.satin.impl.arch.attributes contains various attribute specialisations

Note that the advertising and discovery framework and the various attributes are completely optional.

Hello World


This section provides a documented example of how to create a simple component using SATIN and send it on another host. In this example, we assume two nodes, A, B. A is sending component HelloWorld to B. Notice that the Deployer must be initialised and registered just like any other component.

Code on A (note that A assumes that B's address is hamsalad.cs.ucl.ac.uk):

/*
 * Created on 18-Feb-2005
 *
 */

import edu.UCL.satin.arch.attribute.GenericAttribute;
import edu.UCL.satin.arch.components.Component;
import edu.UCL.satin.arch.components.container.Container;
import edu.UCL.satin.arch.facets.ComponentFacet;
import edu.UCL.satin.arch.facets.DeployerFacet;
import edu.UCL.satin.arch.facets.RegistrarFacet;
import edu.UCL.satin.arch.lmu.LMU;
import edu.UCL.satin.impl.MiToolkitDeployer.HashLMU;
import edu.UCL.satin.impl.MiToolkitDeployer.MiToolkitDeployer;
import edu.UCL.satin.impl.container.Core;

/**
 * @author Stefanos Zachariadis
 *
 */
public class HelloWorld extends Component implements ComponentFacet {

	public HelloWorld() {
		super("STN:HELLOWORLD");
	}


	//this is the constructor of the component
	public boolean construct() {
		System.out.println("Hello SATIN world");
		//add a few attributes
		addAttribute(new GenericAttribute("BITRATE",new Integer(64),true));
		addAttribute(new GenericAttribute("VER",new Integer(1),true));
		addAttribute(new GenericAttribute("DEP","",true));
		addAttribute(new GenericAttribute("FACETS","ComponentFacet",true));
		return(true);
	}


	//this is the destructor of the component
	public void destroy() {
		System.out.println("Bye-bye cruel SATIN world");
		System.gc();
	}


	public boolean isEnabled() {
		//doesn't really do much in this component
		return true;
	}


	public void setEnabled(boolean enabled) {
		// doesn't really do much in this component
	}

	public static void main(String[] args) {
		new Core(); //initialise the container
		Container container = Container.getContainer(); //get a reference to it
		RegistrarFacet registrar = container.getDefaultRegistrar(); //get a reference to the registrar
		Component c=new HelloWorld(); //initialise the component
		if (!registrar.registerComponent(c)) { //register the component. If registration failed, then die
			System.err.println("Registration failed. Exiting...");
			System.exit(0); //this shouldn't really happen ;-)
		}

		container.getComponent("STN:DEBUG").setEnabled(true); //enables debug information
		//debuging is handled by a component

		DeployerFacet d = new MiToolkitDeployer(); //Initialise the deployer
		registrar.registerComponent((Component)d); //register it with the middleware
		d.setEnabled(true); //enable it

		/* The following statement creates a new LMU,
		 * with target "hamsalad.cs.ucl.ac.uk" and
		 * defines its destination as "STN:CONTAINER",
		 * which is the container of the recipient host.
		 *
		 * This effectively defines that the LMU should be sent to the
		 * container of hamsalad.cs.ucl.ac.uk.
		 *
		 * The container will try to register it.
		 */
		LMU lmu=new HashLMU("hamsalad.cs.ucl.ac.uk","STN:CONTAINER");
		lmu.addComponent(c); //adds the component to the LMU.

		d.send(lmu);//sends the LMU
	}

}

Code on B:


/*
 * Created on 18-Feb-2005
 *
 */

import java.util.Hashtable;

import edu.UCL.satin.arch.attribute.GenericAttribute;
import edu.UCL.satin.arch.components.Component;
import edu.UCL.satin.arch.components.container.Container;
import edu.UCL.satin.arch.facets.ComponentListener;
import edu.UCL.satin.impl.MiToolkitDeployer.MiToolkitDeployer;
import edu.UCL.satin.impl.arch.attribute.GreaterEqualThanFilter;
import edu.UCL.satin.impl.arch.attribute.MatchAttribute;
import edu.UCL.satin.impl.container.Core;

/**
 * @author Stefanos Zachariadis
 *
 */
public class GetHelloWorld implements ComponentListener {

	//this method is called when a component that was requested for was found
	public void componentFound(Component c) {
		System.out.println("The component "+c+" was registered");
		Container.getContainer().getDefaultRegistrar().removeComponent(c);
		System.out.println("The component was removed");
	}

	public static void main(String[] args) {

		//instantiates the container and the deployer. also enables debug
		Container c=new Core();
		c=Container.getContainer();
		c.getComponent("STN:DEBUG").setEnabled(true);
		c.getDefaultRegistrar().registerComponent(new MiToolkitDeployer());
		c.getComponent("STN:MITKDEP").setEnabled(true);

		GetHelloWorld obj=new GetHelloWorld();

		/*
		 * Creates a new query template. This template asks for the following:
		 * A "BITRATE" attribute with value greater or equal to 32
		 * A "VER" attribute with value of 1 (exactly)
		 */
		Hashtable template=new Hashtable();
		template.put("BITRATE",new MatchAttribute("BITRATE",new Integer(32),new GreaterEqualThanFilter()));
		template.put("VER",new GenericAttribute("VER",new Integer(1),true));

		//adds a listener for a component based on that template
		c.addListener((ComponentListener)obj,template);

	}
}


Starting Node B first and Node A second, the output that should be shown on Node B is the following (debugging output is omitted for clarity):
 [szachari@hamsalad devel]$ java  -classpath satin.jar GetHelloWorld
Hello SATIN world
The component ID=STN:HELLOWORLD,Bitrate=64,FACETS=ComponentFacet,DEP=,VER=1 was registered<
Bye-bye cruel SATIN world
The component was removed<

Advertising and Discovery in SATIN

This section shows how to use the SATIN advertising and discovery framework, which is optional.

To demonstrate the modularity and flexibility of the component metamodel, the SATIN middleware system offers a modular advertising and discovery framework.

A component that wishes to be advertised (e.g. codec repositories, etc.) needs to implement the Advertisable facet. Advertisable components export an advertising message. The format of the message is not defined and depends on the implementation. Discovery components represent discovery mechanisms and act as a container for RemoteComponents, which are components located remotely. As such, a RemoteComponent is a component specialisation, it has a Location and an Advertising message, and is immutable. The RemoteComponent abstraction does not export any functionality apart from that needed to reason about it (query its properties, location and advertising message) as the components that they represent are not registered locally (remember that SATIN is a local component model system). Getting a RemoteComponent reference from a Discovery component is identical to getting a Component reference from the Container. Similarly, the Discovery components support notification. RemoteComponents can be passed to a Deployer to be retrieved and registered locally.

Advertising techniques are represented by Advertiser components. Advertisable components can register themselves with Advertiser components to be advertised by them.

This abstract framework is instantiated using a simple publish subscribe advertising mechanism. This is composed of an advertising server component (edu.UCL.satin.impl.advertising.central.server.AdvertisingServer), an advertiser component that publishes advertisable components to the server (edu.UCL.satin.impl.advertising.central.clients.advertising.CentralAdvertising) and a Discovery component that retrieves information from the server (edu.UCL.satin.impl.advertising.central.clients.discovery.CentralDiscovery).

Note that the implementation of this is very simple (but usable) and is offered as proof of concept. It is there to demonstrate the flexibility of SATIN. There is also a multicast advertising and discovery scheme, but this is not available yet as it is unstable.

Advertising And Discovery Hello World

This section continues by modifying the HelloWorld program given previously. As such, HelloWorld becomes advertisable, Node A advertises it, Node B discovers it and retrieves it. The advertising server is also run in Node A. The code is fully documented.

Code on A:
/*
 * Created on 18-Feb-2005
 *
 */

import edu.UCL.satin.arch.attribute.GenericAttribute;
import edu.UCL.satin.arch.components.Component;
import edu.UCL.satin.arch.components.container.Container;
import edu.UCL.satin.arch.facets.ComponentFacet;
import edu.UCL.satin.arch.facets.DeployerFacet;
import edu.UCL.satin.arch.facets.RegistrarFacet;
import edu.UCL.satin.facets.Advertisable;
import edu.UCL.satin.impl.MiToolkitDeployer.MiToolkitDeployer;
import edu.UCL.satin.impl.advertising.central.clients.advertising.CentralAdvertising;
import edu.UCL.satin.impl.advertising.central.server.AdvertisingServer;
import edu.UCL.satin.impl.container.Core;

/**
 * @author Stefanos Zachariadis
 *
 */
public class HelloWorld extends Component implements ComponentFacet, Advertisable {

	public HelloWorld() {
		super("STN:HELLOWORLD");
	}

	//this is the advertising message of the component
	public Object getMessage() {
		return("Say hello to the world of advertising.");
	}

	//this is the constructor of the component
	public boolean construct() {
		System.out.println("Hello SATIN world");
		//add a few attributes
		addAttribute(new GenericAttribute("BITRATE",new Integer(64),true));
		addAttribute(new GenericAttribute("VER",new Integer(1),true));
		addAttribute(new GenericAttribute("FACETS","ComponentFacet",true));
		return(true);
	}


	//this is the destructor of the component
	public void destroy() {
		System.out.println("Bye-bye cruel SATIN world");
	}


	public boolean isEnabled() {
		//doesn't really do much in this component
		return true;
	}


	public void setEnabled(boolean enabled) {
		// doesn't really do much in this component
	}

	public static void main(String[] args) {
		new Core(); //initialise the container
		Container container = Container.getContainer(); //get a reference to it
		RegistrarFacet registrar = container.getDefaultRegistrar(); //get a reference to the registrar
		Component c=new HelloWorld(); //initialise the component
		if (!registrar.registerComponent(c)) { //register the component. If registration failed, then die
			System.err.println("Registration failed. Exiting...");
			System.exit(0); //this shouldn't really happen ;-)
		}

		container.getComponent("STN:DEBUG").setEnabled(true); //enables debug information
		//debug is handled by a component

		//creates a new advertising server, registers it and starts it
		AdvertisingServer srv=new AdvertisingServer();
		registrar.registerComponent(srv);
		srv.setEnabled(true);

		DeployerFacet d = new MiToolkitDeployer(); //Initialise the deployer
		registrar.registerComponent((Component)d); //register it with the middleware
		d.setEnabled(true); //enable it


		//creates a new advertising client, registers it,
		//gives it the location of the advertising server
		//registers HelloWorld to be advertised and starts it.
		CentralAdvertising adv=new CentralAdvertising();
		registrar.registerComponent(adv);
		adv.setIP("localhost");
		adv.addAdvertisable((Advertisable)c);
		adv.setEnabled(true);

	}

}

Code on B (note that B assumes that A's address is sandwich.cs.ucl.ac.uk):

/*
 * Created on 18-Feb-2005
 *
 */

import java.util.Hashtable;

import edu.UCL.satin.arch.attribute.GenericAttribute;
import edu.UCL.satin.arch.components.Component;
import edu.UCL.satin.arch.components.Deployer;
import edu.UCL.satin.arch.components.container.Container;
import edu.UCL.satin.arch.facets.ComponentListener;
import edu.UCL.satin.impl.MiToolkitDeployer.MiToolkitDeployer;
import edu.UCL.satin.impl.advertising.central.clients.discovery.CentralDiscovery;
import edu.UCL.satin.impl.arch.attribute.GreaterEqualThanFilter;
import edu.UCL.satin.impl.arch.attribute.MatchAttribute;
import edu.UCL.satin.impl.arch.components.comms.RemoteComponent;
import edu.UCL.satin.impl.container.Core;

/**
 * @author Stefanos Zachariadis
 *
 */
public class GetHelloWorld implements ComponentListener {

	//this method is called when a component that was requested for was found
	//note that in this case, the component can be both local and remote
	public void componentFound(Component c) {

		if(c instanceof RemoteComponent) { //the component was found remotely
			System.out.println("The component " + c + " that says " + ((RemoteComponent)(c)).getMessage() + " was found remotely, at location "+((RemoteComponent)(c)).getLocation().asString());
			MiToolkitDeployer d=(MiToolkitDeployer)(Container.getContainer().getComponent("STN:MITKDEP")); //gets an instance of the deployer
			d.asyncReceive((RemoteComponent)(c),Container.getContainer());
		}
		else { //the component was found locally
			System.out.println("The component "+c+" was registered");
			Container.getContainer().getDefaultRegistrar().removeComponent(c);
			System.out.println("The component was removed");
		}
	}

	public static void main(String[] args) {

		//instantiates the container and the deployer. also enables debug
		Container c=new Core();
		c=Container.getContainer();
		c.getComponent("STN:DEBUG").setEnabled(true);
		c.getDefaultRegistrar().registerComponent(new MiToolkitDeployer());
		c.getComponent("STN:MITKDEP").setEnabled(true);

		GetHelloWorld obj=new GetHelloWorld();


		//creates a new discovery component, tells it where to discover from
		//and registers it
		CentralDiscovery disc=new CentralDiscovery();
		c.getDefaultRegistrar().registerComponent(disc);
		disc.setIP("sandwich.cs.ucl.ac.uk");


		/*
		 * Creates a new query template. This template asks for the following:
		 * A "BITRATE" attribute with value greater or equal to 32
		 * A "VER" attribute with value of 1 (exactly)
		 */
		Hashtable template=new Hashtable();
		template.put("BITRATE",new MatchAttribute("BITRATE",new Integer(32),new GreaterEqualThanFilter()));

		//adds a listener for a component based on that template.
		//the listener is added both to the container and the discovery service
		c.addListener((ComponentListener)obj,template);
		disc.addListener((ComponentListener)obj,template);


		//starts the discovery service
		disc.setEnabled(true);
	}
}

Starting Node A first and node B second, here's the output obtained on node B (debugging information has been omitted for clarity):

[szachari@hamsalad devel]$ java  -classpath satin.jar:advdisc.jar:pubsub.jar GetHelloWorld
The component ID=STN:HELLOWORLD,BITRATE=64,FACETS=ComponentFacet,VER=1 that says Say hello to the world of advertising. was found remotely, at location 128.16.66.118
Hello SATIN world
The component ID=STN:HELLOWORLD,BITRATE=64,FACETS=ComponentFacet,VER=1 was registered
Bye-bye cruel SATIN world
The component was removed


Hosted on Sourceforge.net University College LondonDepartment of Computer Science Stefanos Zachariadis
Department of Computer Science
University College London.
August 2005