Skip to content
This repository

Application logic is easily bound to a Jid Tree by subclassing a Jid class.

Jid Applications
Scalar Applications
Parameterized Factories
Tuple Applications
List Applications
Map Applications
ActorJid

JID Classes Referenced on this Page

Back: JidFactories Up: Home Next: Serialized Data

Jid Applications

The base class Jid has no persistent data. Ideal for a Hello world! application.

public interface Main extends TargetActor {
    public void processRequest(Proc req, RP rp) throws Exception;
}

Main is an interface that we can use as a target for a request. This way we can have any number of Jid classes that are targeted by the request.

public class Proc extends Request<Object, Main> {
    public final static Proc req = new Proc();

    @Override
    public void processRequest(JLPCActor targetActor, RP rp) throws Exception {
        Main a = (Main) targetActor;
        a.processRequest(this, rp);
    }

    @Override
    public boolean isTargetType(Actor targetActor) {
        return targetActor instanceof Main;
    }
}

Proc is a request that is targeted at Main and which does not expect a result.

public class HelloWorld extends Jid implements Main {
    @Override
    public void processRequest(Proc request, RP rp) throws Exception {
        System.out.println("Hello world!");
        rp.processResponse(null);
    }
}

HelloWorld is a subclass of Jid and a suitable target for a Proc request.

MailboxFactory mailboxFactory = JAMailboxFactory.newMailboxFactory(1);
Mailbox mailbox = mailboxFactory.createMailbox();
JAFuture future = new JAFuture();
JAFactory factory = new JAFactory();
factory.initialize(mailbox);
factory.defineActorType("hi", HelloWorld.class);

RootJid root = new RootJid();
root.initialize(mailbox, factory);
(new SetActor("hi")).send(future, root);
byte[] rootBytes = GetSerializedBytes.req.send(future, root);

RootJid root2 = new RootJid();
root2.initialize(mailbox, factory);
root2.load(rootBytes);
Actor a = (new ResolvePathname("0")).send(future, root2);
Proc.req.send(future, a);

And here is the test code. It does the following:

  1. Creates a mailboxFactory with 1 thread in its threadpool.
  2. Creates a mailbox.
  3. Creates a future for interacting with actors from non-actor code.
  4. Creates a factory.
  5. Binds the HelloWorld class to the "hi" actor type in the factory.
  6. Creates root as the top node of a Jid tree.
  7. Injects the factory as the parent of root.
  8. Creates an instance of HelloWorld as the content of the root container.
  9. Serializes the Jid tree into a byte array.
  10. Creates root2 as the top node of a second Jid tree.
  11. Injects factory into root2.
  12. Loads the serialized data, rootBytes, from the first Jid tree into the second Jid tree.
  13. Gets the contents, a, of root2.
  14. Sends a Proc request to a, which then prints "Hello world!".

Of interest: the rootBytes array is 8 bytes long. It contains only the actor type, "hi".

Scalar Applications

Some applications need to persist just a boolean, float, double, int, long or String. This is easily achieved by subclassing the appropriate scalar jid class.

public class LuckyNumber extends IntegerJid implements Main {
    @Override
    public void processRequest(Proc request, RP rp) throws Exception {
        System.out.println("Your lucky number is " + getValue());
        rp.processResponse(null);
    }
}

LuckyNumber is an application which needs only a persistent int. It subclasses IntegerJid, implements Main and handles a Proc request.

MailboxFactory mailboxFactory = JAMailboxFactory.newMailboxFactory(1);
Mailbox mailbox = mailboxFactory.createMailbox();
JAFuture future = new JAFuture();
JAFactory factory = new JAFactory();
factory.initialize(mailbox);
factory.defineActorType("lucky number", LuckyNumber.class);

RootJid root = new RootJid();
root.initialize(mailbox, factory);
(new SetActor("lucky number")).send(future, root);
Actor a = (new ResolvePathname("0")).send(future, root);
(new SetInteger(7)).send(future, a);
byte[] rootBytes = GetSerializedBytes.req.send(future, root);

RootJid root2 = new RootJid();
root2.initialize(mailbox, factory);
root2.load(rootBytes);
Actor a2 = (new ResolvePathname("0")).send(future, root2);
Proc.req.send(future, a2);

The test for LuckyNumber creates an instance of LuckyNumber as the content of the root and then assigns the value of 7 to that instance before serializing the jid tree to a byte array.

And when it creates a second jid tree from that byte array, it fetches the root content and sends a Proc request to it. The LuckyNumber instance then prints "Your lucky number is 7".

Of interest: the rootBytes array contains only a serialized string, "lucky number", and a serialized int, 7.

Parameterized Factories

Instead of serializing or hard-coding all the data, you can instead supply it as a factory parameter.

public class Greeter extends StringJid implements Main {
    public String greeting;

    @Override
    public void processRequest(Proc request, RP rp) throws Exception {
        if (greeting == null)
            throw new IllegalStateException("greeting is null");
        System.out.println(greeting + " " + getValue());
        rp.processResponse(null);
    }
}

The Greeter actor has one persistent data item--a string. This data is persisted by subclassing StringJid. But there is a second data item, greeting, which is set by its factory.

public class GreeterFactory extends ActorFactory {
    private String greeting;

    public GreeterFactory(String actorType, String greeting) {
        super(actorType);
        this.greeting = greeting;
    }

    @Override
    protected JLPCActor instantiateActor() throws Exception {
        Greeter greeter = new Greeter();
        greeter.greeting = greeting;
        return greeter;
    }
}

The GreeterFactory is used to create a Greeter actor and set its greeting. Here we see that GreeterFactory gets the greeting as a parameter on its constructor.

MailboxFactory mailboxFactory = JAMailboxFactory.newMailboxFactory(1);
Mailbox mailbox = mailboxFactory.createMailbox();
JAFuture future = new JAFuture();
JAFactory factory = new JAFactory();
factory.initialize(mailbox);
factory.registerActorFactory(new GreeterFactory("hi greeter", "Hi"));

RootJid root = new RootJid();
root.initialize(mailbox, factory);
(new SetActor("hi greeter")).send(future, root);
Actor a = (new ResolvePathname("0")).send(future, root);
(new SetString("Frank")).send(future, a);
byte[] rootBytes = GetSerializedBytes.req.send(future, root);

RootJid root2 = new RootJid();
root2.initialize(mailbox, factory);
root2.load(rootBytes);
Actor a2 = (new ResolvePathname("0")).send(future, root2);
Proc.req.send(future, a2);

In the test for Greeter, we create a GreeterFactory with the greeting parameter set to "Hi" and set the value of Greeter to "Frank". Now when we send a Proc request to Greeter it prints "Hi Frank".

Of interest: the serialized byte array of Greeter contains two Strings, "hi greeter" and "Frank".

Tuple Applications

A TupleJid actor is a collection of different types of Jid actors. The factories of the elements of a TupleJid instance are specified as parameters on the constructor of the factory used to create the TupleJid. For example, to create tuple jids which have 3 StringJid elements you would create the tuple factory like this:

TupleJidFactory tj3sf = new TupleJidFactory("tj3s", StringJidFactory.fac, StringJidFactory.fac, StringJidFactory.fac);

As most applications require a mix of different types of persistent data, the many applications are implemented as subclasses of TupleJid.

public class User extends TupleJid implements Main {
    @Override
    public void processRequest(Proc request, RP rp) throws Exception {
        StringJid name = (StringJid) iGet(0);
        System.out.println("name: " + name.getValue());
        IntegerJid age = (IntegerJid) iGet(1);
        System.out.println("age: " + age.getValue());
        StringJid location = (StringJid) iGet(2);
        System.out.println("location: " + location.getValue());
        rp.processResponse(null);
    }
}

The User actor has 3 persistent elements: name, age and location. These are implemented as a StringJid, IntegerJid and StringJid respectively.

public class UserFactory extends TupleJidFactory {
    public UserFactory(String actorType) {
        super(actorType, StringJidFactory.fac, IntegerJidFactory.fac, StringJidFactory.fac);
    }

    @Override
    protected User instantiateActor() throws Exception {
        User user = new User();
        assignElementFactories(user);
        return user;
    }
}

The above illustrates how to subclass TupleJidFactory.

MailboxFactory mailboxFactory = JAMailboxFactory.newMailboxFactory(1);
Mailbox mailbox = mailboxFactory.createMailbox();
JAFuture future = new JAFuture();
JAFactory factory = new JAFactory();
factory.initialize(mailbox);
factory.registerActorFactory(new UserFactory("user"));
JidFactories factories = new JidFactories();
factories.initialize(mailbox, factory);

RootJid root = new RootJid();
root.initialize(mailbox, factory);
(new SetActor("user")).send(future, root);
Actor name = (new ResolvePathname("0/0")).send(future, root);
(new SetString("Frank")).send(future, name);
Actor age = (new ResolvePathname("0/1")).send(future, root);
(new SetInteger(30)).send(future, age);
Actor location = (new ResolvePathname("0/2")).send(future, root);
(new SetString("Bangalore")).send(future, location);
byte[] rootBytes = GetSerializedBytes.req.send(future, root);

RootJid root2 = new RootJid();
root2.initialize(mailbox, factory);
root2.load(rootBytes);
Actor user = (new ResolvePathname("0")).send(future, root2);
Proc.req.send(future, user);

In the above test we make use of JidFactories, a convenience class which registers StringJidFactory, IntegerJidFactory and many others when its setParent method is called.

We also make good use of ResolvePathname, where a path of 0/1 applied to root first fetches the content of root, which is the User jid, and then the second element of User jid, which is the age.

Here is the test output:

name: Frank
age: 30
location: Bangalore

Of interest: the byte array holding the serialized root jid contains only the following:

  1. The actor type of the user, "user".
  2. A serialized int holding the length of the tuple.
  3. The name of the user, "Frank".
  4. The age of the user, 30. And
  5. The location of the user, "Bangalore".

List Applications

ListJid provides a persistent list of Jid actors. And the ListJidActor constructors have an elementFactory parameter for providing the Jid factory used to create the contents of a ListJid. For example, to create ListJid's holding a list of String Jid's, you might instantiate the ListJidFactory like this:

ListJidFactory ljsf = new ListJidFactory("ljs", StringJidFactory.fac);

public class Sum extends ListJid implements Main {
    @Override
    public void processRequest(Proc request, RP rp) throws Exception {
        initialize();
        int sum = 0;
        Iterator<_Jid> it = list.iterator();
        while (it.hasNext()) {
            IntegerJid ij = (IntegerJid) it.next();
            sum += ij.getValue();
        }
        System.out.println("Total: " + sum);
        rp.processResponse(null);
    }
}

The Sum actor subclasses ListJid and, when it receives a Proc request, prints the total of all its elements.

Note in particular the call to initialize before accessing list. This is to ensure that a newly loaded list is deserialized.

public class SumFactory extends ListJidFactory {
    @Override
    protected Sum instantiateActor(Mailbox mailbox)
            throws Exception {
        Sum sum = new Sum();
        sum.initialize(mailbox);
        assignElementsFactory(sum);
        return sum;
    }
}

The SumFactory actor is used to instantiate a Sum actor with IntegerJid elements.

MailboxFactory mailboxFactory = JAMailboxFactory.newMailboxFactory(1);
Mailbox mailbox = mailboxFactory.createMailbox();
JAFuture future = new JAFuture();
JAFactory factory = new JAFactory();
factory.initialize(mailbox);
factory.registerActorFactory(new SumFactory("sum"));
JidFactories factories = new JidFactories();
factories.initialize(mailbox, factory);

RootJid root = new RootJid();
root.initialize(mailbox, factory);
(new SetActor("sum")).send(future, root);
Sum sum = (Sum) (new ResolvePathname("0")).send(future, root);
IAdd iAdd = new IAdd(-1);
IGet iGet = new IGet(-1);
iAdd.send(future, sum);
IntegerJid ij0 = (IntegerJid) iGet.send(future, sum);
(new SetInteger(1)).send(future, ij0);
iAdd.send(future, sum);
IntegerJid ij1 = (IntegerJid) iGet.send(future, sum);
(new SetInteger(2)).send(future, ij1);
iAdd.send(future, sum);
IntegerJid ij2 = (IntegerJid) iGet.send(future, sum);
(new SetInteger(3)).send(future, ij2);
byte[] rootBytes = GetSerializedBytes.req.send(future, root);

RootJid root2 = new RootJid();
root2.initialize(mailbox, factory);
root2.load(rootBytes);
Actor a = (new ResolvePathname("0")).send(future, root2);
Proc.req.send(future, a);

The test code for Sum creates a Sum actor, adds three elements and assigns 1, 2 and 3 to those elements before serializing the root tree. A Proc request is then sent to the deserialized root tree and Sum prints "Total: 6".

Of interest: the byte array of the serialized jid tree holds only the following:

  1. The actor type, "sum".
  2. A serialized int holding the serialized length of the list.
  3. A serialized int, 3, which is the number of entries in the list. And
  4. The serialized ints 1, 2 and 3.

Map Applications

MapJid maintains a list of 2-tuples (key/value pairs) sorted on the first element (the key). A binary search is used to support access of values by key.

public class Users extends StringMapJid implements Main {
    @Override
    public void processRequest(Proc request, RP rp) throws Exception {
        initialize();
        Iterator<_Jid> it = list.iterator();
        while (it.hasNext()) {
            TupleJid tj = (TupleJid) it.next();
            StringJid name = (StringJid) tj.iGet(0);
            StringJid email = (StringJid) tj.iGet(1);
            System.out.println("name: " + name.getValue() + ", email: " + email.getValue());
        }
        rp.processResponse(null);
    }
}

The Users actor maps names to email addresses and when it receives a Proc request it prints its contents. Users subclasses StringMapJid, which supports the use of a StringJid as a key.

public class UsersFactory extends ActorFactory {
    @Override
    protected Users instantiateActor(Mailbox mailbox)
            throws Exception {
        Users users = new Users(mailbox);
        users.valueFactory = StringJidFactory.fac;
        return users;
    }
}

UsersFactory is used to create instances of Users and to assign a StringJidFactory to valueFactory. (MapJid.valueFactory is used when adding a new tuple to the list.)

MailboxFactory mailboxFactory = JAMailboxFactory.newMailboxFactory(1);
Mailbox mailbox = mailboxFactory.createMailbox();
JAFuture future = new JAFuture();
JAFactory factory = new JAFactory();
factory.initialize(mailbox);
factory.registerActorFactory(new UsersFactory("users"));
JidFactories factories = new JidFactories();
factories.initialize(mailbox, factory);

RootJid root = new RootJid();
root.initialize(mailbox, factory);
(new SetActor("users")).send(future, root);
Users users = (Users) (new ResolvePathname("0")).send(future, root);
users.newKMake("John").send(future, users);
StringJid jEmail = (StringJid) users.newKGet("John").send(future, users);
jEmail.setValue("john123@gmail.com");
users.newKMake("Sam").send(future, users);
StringJid sEmail = (StringJid) users.newKGet("Sam").send(future, users);
sEmail.setValue("sammyjr@yahoo.com");
users.newKMake("Fred").send(future, users);
StringJid fEmail = (StringJid) users.newKGet("Fred").send(future, users);
fEmail.setValue("fredk@gmail.com");
byte[] rootBytes = GetSerializedBytes.req.send(future, root);

RootJid root2 = new RootJid();
root2.initialize(mailbox, factory);
root2.load(rootBytes);
Actor a = (new ResolvePathname("0")).send(future, root2);
Proc.req.send(future, a);

The test code for Users adds three name/email tuples to the map before serializing the jid tree. A second jid tree is created from the serialized data and is sent a Proc request. Users then prints this:

name: Fred, email: fredk@gmail.com
name: John, email: john123@gmail.com
name: Sam, email: sammyjr@yahoo.com

ActorJid

All the elements in a tuple, list or map collection are created by the factories specified in the collection's factory. This speeds up deserialization, but is not flexible enough for many applications. The ActorJid can however be used to hold arbitrary Jid actors, as it includes the actor type of its contents in its serialized data.

public class Blob extends StringMapJid implements Main {
    @Override
    public void processRequest(Proc request, RP rp) throws Exception {
        initialize();
        ActorJid aj = (ActorJid) kGet("fun");
        Actor a = aj.getValue();
        Proc.req.send(this, a, rp);
    }
}

Blob is a subclass of StringMapJid and its values are ActorJid's. A Proc request sent to Blob results in a Proc request being sent to the actor bound to the key "fun".

public class BlobFactory extends ActorFactory {
    public BlobFactory(String actorType) {
        super(actorType);
    }

    @Override
    protected Blob instantiateActor()
            throws Exception {
        Blob blob = new Blob();
        blob.valueFactory = ActorJidFactory.fac;
        return blob;
    }
}

BlobFactory creates a blob and sets the valueFactory to an instance of ActorJidFactory.

MailboxFactory mailboxFactory = JAMailboxFactory.newMailboxFactory(1);
Mailbox mailbox = mailboxFactory.createMailbox();
JAFuture future = new JAFuture();
JAFactory factory = new JAFactory();
factory.initialize(mailbox);
factory.registerActorFactory(new BlobFactory("blob"));
factory.defineActorType("hi", HelloWorld.class);
JidFactories factories = new JidFactories();
factories.initialize(mailbox, factory);

RootJid root = new RootJid();
root.initialize(mailbox, factory);
(new SetActor("blob")).send(future, root);
Blob blob = (Blob) (new ResolvePathname("0")).send(future, root);
blob.newKMake("fun").send(future, blob);
ActorJid fun = (ActorJid) blob.newKGet("fun").send(future, blob);
(new SetActor("hi")).send(future, fun);
byte[] rootBytes = GetSerializedBytes.req.send(future, root);

RootJid root2 = new RootJid();
root2.initialize(mailbox, factory);
root2.load(rootBytes);
Actor a = (new ResolvePathname("0")).send(future, root2);
Proc.req.send(future, a);

The test code for Blob creates an entry with the key "fun" and sets the value to an instance of the HelloWorld Jid actor before serializing the jid tree. A second jid tree is then created from the serialized data and a Proc request is sent to the contents of the second Root instance. The HelloWorld Jid then prints "Hello world!".

JID Classes Referenced on this Page

Jid
RootJid
SetActor
ResolvePathname
Main
Proc
HelloWorld
HelloWorldTest
IntegerJid
SetInteger
LuckyNumber
LuckyNumberTest
StringJid
SetString
Greeter
GreeterFactory
GreeterTest
TupleJid
TupleJidFactory
JidFactories
StringJidFactory
IntegerJidFactory
User
UserFactory
UserTest
ListJid
ListJidFactory
IAdd
IGet
Sum
SumFactory
SumTest
MapJid
StringMapJid
KMake
KGet
Users
UsersFactory
UsersTest
ActorJid
ActorJidFactory
Blob
BlobFactory
BlobTest

Something went wrong with that request. Please try again.