User Tools

Site Tools


runtime_evolution

This article assumes that you've completed the dynamic linking tutorial. Dana is in essence an object-oriented programming language which supports full runtime evolution of system behaviour. Runtime evolution is performed with respect to the dependencies (or required interfaces) of components. As you've already seen we can dynamically select which components we use to resolve each required interface in the system. We can also seamlessly modify these selected components later on during system execution. This is done by changing the component C, used to satisfy a particular required interface of a component X, to a different component D.

In other words, we look at one required interface of component X, which is currently connected to component C, and we switch that connection to component D. The only assumption that we make in such a switch is that components C and D both have the same provided interface. Internally, C and D can be completely different implementations of that interface type with completely different internal state; the internal details of components are opaque to runtime evolution.

This tutorial explains how runtime evolution is driven using esher. Details on the internal mechanics of evolution are covered in the article on evolution mechanics.

As an example system here we'll use TCP/IP and perform runtime evolution at the server. The architecture of our system will look like this:

First we'll explain the code we'll be using to create the above system. We introduce one new interface type and one record type which you'll need to create and put into the resources/data/ directory of your Dana installation. In this directory, create a new file Source.dn and place the following code into it:

interface Source{
	int getNextNumber();
	int getNumber();
	}

Then create a file SourceReply.dn in the same directory and place the following code in it:

record SourceReply{
	int32 number;
	}

During compilation this interface and record type can be accessed using data.Source and data.SourceReply respectively.

The rest of the source files will go directly into the components/ directory of your Dana installation. Download each of the following files to that directory:

Server.dn
uses data.SourceReply;
 
component provides App requires net.TCPServerSocket, net.TCPSocket, data.Source source {
 
   void handleStream(TCPSocket client)
      {
      SourceReply reply;
      reply.number = source.getNextNumber();
      client.send((serial) reply);
      client.disconnect();
      }
 
   int App:main(AppParam params[])
      {
      TCPServerSocket master = new TCPServerSocket();
      master.bind("ANY", 2014);
 
      while (true)
         {
         TCPSocket client = new TCPSocket();
         if (client.accept(master))
            handleStream(client);
         }
 
      return 0;
      }
   }
Client.dn
uses data.SourceReply;
 
component provides App requires io.Output out, data.IntUtil iu, net.TCPSocket, time.Timer timer {
 
   int App:main(AppParam params[])
      {
      TCPSocket client = new TCPSocket();
 
      while (true)
         {
         if (client.connect("127.0.0.1", 2014))
            {
            SourceReply reply;
 
            ((serial) reply) =[] client.recv(((serial) reply).arrayLength);
 
            out.println(iu.intToString(reply.number));
            }
 
         timer.sleep(2000);
         }
 
      return 0;
      }
   }
SourceA.dn
component provides data.Source{
   int number;
 
   int Source:getNextNumber()
      {
      number ++;
      return number;
      }
 
   int Source:getNumber()
      {
      return number;
      }
 
   bool Source:clone(Object o)
      {
      Source src = o;
 
      number = src.getNumber();
 
      return true;
      }
   }
SourceB.dn
component provides data.Source{
   int myNum;
 
   int Source:getNextNumber()
      {
      myNum += 2;
      return myNum;
      }
 
   int Source:getNumber()
      {
      return myNum;
      }
 
   bool Source:clone(Object o)
      {
      Source s = o;
 
      myNum = s.getNumber();
 
      return true;
      }
   }

And place the following configuration fragments into the same components/ directory:

server.txt
App=Server.o
TCPServerSocket->TCP
TCPSocket->TCP
Source->Source
client.txt
App=Client.o
Timer->Timer
TCPSocket->TCP
tcp.txt
TCP=net/TCP.o
timer.txt
Timer = time/Timer.o
sourcea.txt
Source=SourceA.o
sourceb.txt
Source=SourceB.o

Next, open a command prompt in your components/ directory and compile each of the above components as follows:

dnc Server.dn
dnc Client.dn
dnc SourceA.dn
dnc SourceB.dn

And make sure that the system components we'll be using are also compiled:

dnc net/TCP.dn
dnc time/Timer.dn

Now open a second command prompt in the same directory which we'll use for the client side of the system.

In both command prompts issue the command:

dana esher

In the first command prompt use esher's commands to add in the components that we need for the server side of the system by issuing the following sequence:

add server.txt
add tcp.txt
add sourcea.txt

In the second command prompt add in the components that we need for the client side of the system with the following sequence of commands:

add client.txt
add tcp.txt
add timer.txt

The second command prompt should now start printing out one number every two seconds. We'll now perform some runtime evolution at the server side.

Back in the first command prompt we start by adding in the alternative “Source” implementation:

add sourceb.txt

Now we have two different implementations of the same provided interface type present in the system at the same time. If we use the pa command we'll see a report like this:

[0]: App (Server.o)
       TCPServerSocket -> TCP
       TCPSocket -> TCP
       Source -> Source
[1]: TCP (net\TCP.o)
[2]: Source (SourceA.o)
[3]: Source (SourceB.o)

We can use esher's tra command to switch between two implementations. Keep an eye on the client output in the second command prompt, and while you do so in the first command prompt use the command:

tra App Source 3

This tells esher to switch the App component's dependency called Source to now be resolved against component index number 3 (i.e. SourceB.o). When you press enter the transformation takes place immediately and you'll see in the client output that the numbers start incrementing by 2 instead of by 1. Importantly you'll notice that no number in the sequence is missed when the transition is made. You can switch back to the original implementation again with the command:

tra App Source 2

Again you'll see the change immediately in the client-side which will return to increments of 1, again without missing a number in the sequence. You can further modify the system by using esher's rem command to remove whichever of the Source variants is not currently in use, for example rem 2 if you're not currently using SourceA.o. Add it back in with add sourcea.txt as normal.

The mechanics of how runtime evolution works, including why no numbers get missed out in the above sequence even though the two alternative implementations are mutually opaque, are covered in the evolution mechanics article.

runtime_evolution.txt · Last modified: 2014/02/06 14:11 by barryfp

Page Tools