Commands
In addition to dynamic properties, distributed objects can also have commands, which are a form of methods that can be added dynamically during run-time. A command is identified by a name, zero or more parameters and a return value, all of which are represented by strings.Commands are invoked on an object using the exec method of the IpObject class. The method takes only one parameter, which is a call string in a XML-like format that contains the command name and the parameters (if any). The call string has the following syntax:
<c>commandName</c>
[<p>param1</p>
<p>param2</p> …
<p>paramN</p>]
For instance:
// Invoke command add with parameters
4 and 34
String result = obj.exec("<c>add</c>
<p>4</p>
<p>34</p>");
The implementation of commands are handles by command handlers. PART supports three different types of command handlers.
- External command handler objects may be added to game objects, which allows for instance the application code to implement commands associated to objects
- Object may carry scripts that implement commands
- The game object class itself can contain Java code that implements commands
If the exec method is called on an object, PART invokes the command handlers on the master process as described below in order to find a handler that implements the command.
- First PART invokes external handlers, one after the other. If any handler returns a value which is not null, the handler is assumed to have handled the command, and the value is returned back to the caller of the exec method.
- If no external handlers exist or did handle the command, PART invokes the object's script (if set). This is done by invoking a script procedure with the same name as the command name. The parameters given to exec are passed as parameters to the script procedure. If the script do in fact define a matching procedure, the return value (if any) is passed back to the caller of the exec method. If the script procedure doesn't have a return value, null will be returned.
- If the object has no script, or the script doesn't have a matching procedure, the object's own command handler method is invoked.
If no handler handles the command, the exec method will throw an exception of type IpException.
External command handlers
PART allows “external” object to be registered as command handlers for a distributed object. This is done using the addCommandHandler method of the IpObject class.obj.addCommandHandler(handler);
A command handler is an object implementing the IpCommandHandler interface:
public interface IpCommandHandler {
String handleCommand(String command,
String[] args)
throws IpException;
}
Note that when adding a command handler to an object there is no need to specify which command the handler implements. A handler can thus handle many commands. As described above, PART uses the value returned by a handler to determine if the command was handled or not. If null is returned, the handler is assumed to have ignored the command, otherwise handled it. If the handler wants to return null as the result of the command execution, the programmer needs to use for instance an empty string as the return value:
String handleCommand(String command,
String[] args)
throws IpException
{
if
(command.equals(“find”) {
// handle find command by calling some
find method
String result = find(…);
if (result == null) {
// we
can‘t return null so we return an empty string
return
new String(“”);
} else {
return
result;
}
}
return
null; // command not handled
}
Since command handlers are only invoked on the master, there is no point in adding an external handler in a process which is not the process holding the master copy of the object. If this is done, addCommandHandler will throw a run-time exception.
The external command handler mechanism makes it possible for the application code to add new commands to an object, even during run-time.
Script command handlers
PART allows programs written in a scripting language to be associated to game objects. Scripts are represented in PART as strings with the following syntax:language://script
For instance:
String script = "tcl://proc test {}
{puts HelloWorld}";
Scripts may be added to an object using the setScript method:
obj.setScript(script);
An object can have only one script, so calling setScript will replace any previous script added to the object.
If an object has an associated script, the script procedures automatically become available as commands. For instance, to invoke the test procedure (and thus command), one would write:
obj.exec(<c>test</c>);
PART doesn't dictate which language to use when writing scripts. In fact, different languages may be used to write scripts for different objects. PART uses the language field of the script string to find a scripting interpreter class able to run the script. This means that interpreter classes supporting new languages can be "plugged in" to support the need of the application programmer.
Internal command handlers
The IpObject class defines a handleCommand method that can be overridden by sub-classes in order to handle the implementation of commands. By overriding this method in a sub-class, it is possible to write implementations of commands that thus will become available to any programmer using the object. For instance:
public class GameObject extends IpObject {
…
protected String handleCommand(String cmd, String[]
args)
throws IpException
{
if
(cmd.equals("add")) {
int a = Integer.parseInt(args[0]);
int b = Integer.parseInt(args[1]);
System.out.println("TestObject: Adding " + a + " and " + b);
return "" + (a+b);
}
return
null;
}
}
One may wonder why use an internal command handler at all, why not just write specific class methods, for instance:
public class GameObject extends IpObject {
…
int subtract(int a, int b)
{
return
a-b;
}
}
There may be several reasons for this. One might be that by using an internal handler mechanism, it becomes very easy to write "methods" that have a RPC semantic, i.e., they always get executed in the same process, no matter in which process they are invoked. The code that implements the add command above will always execute in the master process, while the subtract method is run in the same process as invokes it. If the programmer doesn't want to expose commands outside of the object, but still get the RPC semantics, commands can be used within access methods like this:
public class GameObject extends
IpObject {
…
int add(int a, int b)
{
return
exec("<c>add</>
<p>"+a+"</p>
<p>"+b+"</p>");
}
}
Another possible reason why to use the internal command handler mechanism instead of ordinary Java methods is that its possible to "create" a call to the command dynamically, while calls to Java methods needs to be programmed before compiling the code. For instance, all commands exported by an internal handler can automatically be called from a script run in the same object, and it's fairly easy to write various command handler application which accepts calls strings to executed in an object. This way of invoking code in an object is much harder to achieve if Java methods are used, especially as J2ME doesn't currently provide a introspection API.
When to use which type of command handler
Since there are three different types of command handlers, the programmer needs to understand the benefits and drawbacks of each type in order to decide which one to use in a certain situation. Below follows a short comparison of each type and some suggestions of when they should (and not) be used.A first difference is that external handlers and scripts can be dynamically added to objects during run-time, while internal handlers can not. Furthermore, since internal handlers are part of the class definition of objects, the commands that they implement will be available in each instance of that class. External handlers and scripts on the other hand may be added to specific instances only. This means that if the application wants to change the available commands during run-time, external handlers or scripts must be used.
Even though the dynamic aspect of scripts and external handlers may seem attractive, it has a (potential) disadvantage in that the code that makes the commands available in an object is not part of the object itself, but rather some "external" code. This means that a programmer who writes a distributed object class and wants to provide some commands needs to write some additional code outside of the class definition to make sure, for instance, that an external handler gets added. By using an internal handler on the other hand, the programmer has full control over which commands the object will export. Using an internal handler makes a class more "self-contained" in that all code that is needed to export and implement the commands are part of the class itself.
When comparing scripts and external handlers we see a number of differences, apart from the difference in programming language:
- An object can have several external handlers but just one script. This means that if several different sub-systems within an application wants to add commands to an object, external handler need to be used rather than scripts.
- Since scripts are represented as strings, they are more light-weight in that they can, for instance, be read from files, received from the network, etc., before being inserted into an object. External handlers, being instances of ordinary Java classes, are more heavy-weight in this sense. For instance, a script read from a file and then inserted into an object allows the command behaviour to be changed by simply editing a file, while changes to an external handler requires a recompilation to change the behaviour of the application.
- External handlers have full access to the Java API, while scripts might have a much more limited API (depending on the scripting language of course). This means that if the command implementation needs to be complex, involve modifications to the GUI, threading, etc., it might be necessary to use external handlers.
- Scripts are much slower to execute than Java code, so if speed is an issue, use external handlers if possible.