Contents
Managed Objects
A Managed Object is an entity in Ponder2 capable of receiving and replying to PonderTalk messages. A Managed Object is written in Java and uses Java annotations (i.e. @Ponder2op()) to create the links between PonderTalk message keywords and Java methods. This page gives an example of a Managed Object's design and implementation. After reading this you may like to progress to the more advanced guide.
AlarmClock
For this example we will design a Managed Object that can show and hide a picture of an alarm clock. In addition, it will be able to set the alarm clock ringing and turn it off again. We will use the GIF images below for the clock in its quiescent and ringing states. The window handling will be simply ordinary Java swing programming.
Operational Commands
The first thing to do is to decide on the name and the interface of your managed object. Let's call the new object AlarmClock. We will be controlling our managed object using PonderTalk. We need to show and hide the alarm clock and we need to be able to turn the alarm on and off. Assuming that our instantiated managed object is called alarm, the following PonderTalk commands will do the trick.
// Show the window alarm show. // Hide the window alarm hide. // Set the alarm ringing alarm setAlarm. // Cancel the alarm alarm cancelAlarm. // Set the alarm ringing and show it alarm setAlarm show.
It would be nice to get the status of the alarm as well in case we need to test it. The following would return a boolean
// Is the alarm visible? Returns a boolean alarm isVisible. // Is the alarm on? Returns a boolean alarm isAlarmOn.
Well that is the basic functionality but the above commands are a little add-hoc, they can be improved and made to be more consistent by combining them as follows
// Show or hide the window alarm visible: <true | false>. // Is the window shown or hidden, returns a boolean alarm visible. // Set or cancel the alarm alarm alarm: <true | false>. // Is the alarm on? alarm alarm.
More formally this gives us:
Command |
Argument(s) |
Return |
Description |
visible: |
aBoolean |
self |
Shows the alarm window if aBoolean is true else hides it |
visible |
|
aBoolean |
Returns true if the alarm window is visible else false |
alarm: |
aBoolean |
self |
Sets the alarm ringing if aBoolean is true else stops it |
alarm |
|
aBoolean |
Returns true if the alarm is ringing else false |
This style of command choices works out quite well i.e. use something: arg to set an attribute and something to return the current value. So, we will have four separate commands that the alarm managed object will accept.
Factory Commands
We also need to know how our managed object it to be created from its factory object. Normally a simple create message will suffice but in this case we should give the window a title. Since the title will not change during the life of the managed object it is appropriate to give the title when the object is instantiated.
// Create an instance of AlarmClock factory := root import: "AlarmClock". alarm := factory create: "A title".
This gives us:
Command |
Argument(s) |
Description |
create: |
aString |
Return a new instance of AlarmClock with the title set to aString |
Directory Setup
If you have your own Java development environment you can keep your sources in that and issue the appropriate command(s) to compile and run your alarm clock. However, if you follow these instructions you can use the supplied ant build.xml file to create and run your managed objects. In your Ponder2 directory you will have the following directories and files:
build.xml lib/ p2src/ (optional) src/
You will put your files underneath src:
src/ | +resources/ | | | +alarmon.gif | | | +alarmoff.gif | +net/ | +ponder2/ | +managedobjects/ | +AlarmClock.java
Your Java code will go in the net/ponder2/managedobjects package directory. Other files such as the alarm clock GIF images and PonderTalk .p2 files will go in the resources directory.
Implementation
Now that we have the correct directory setup, we can create our Java code. Create a file in managedobjects called AlarmClock.java. We can start with the following code. Since we are creating a Swing window we need to extend JFrame. Also since we are creating a managed object we need to implement the net.ponder2.ManagedObject interface. There are no required methods for this interface, it is solely required to tell the Java compiler to start creating stub code to map the PonderTalk calls into the class.
Note: Turn off line numbers if you want to copy and paste the code
1 package net.ponder2.managedobject;
2
3 import java.awt.*;
4 import javax.swing.*;
5 import net.ponder2.*;
6 import net.ponder2.apt.Ponder2op;
7
8 class AlarmClock extends JFrame implements ManagedObject {
9
10 }
Now we need to populate the class with constructor(s) and method(s). First, we need one constructor per factory message. In this case we only have the create: message so only need one constructor:
1 // The GIF images are stored here
2 private ImageIcon alarmon, alarmoff;
3
4 // This label is given the appropriate GIF image at the right time
5 private JLabel label;
6
7 @Ponder2op("create:")
8 public AlarmClock(String aString) {
9 // Set the window title to the PonderTalk supplied string
10 setTitle(aString);
11
12 // We need some standard Swing setup code
13
14
15 // Create icons for the alarm and a label to hold it
16 alarmon = new ImageIcon(this.getClass().getResource("/resource/alarm_clock-on.gif"));
17 alarmoff = new ImageIcon(this.getClass().getResource("/resource/alarm_clock-off.gif"));
18 label = new JLabel();
19 label.setIcon(alarmoff);
20
21 // Add the image to the frame's content pane;
22 getContentPane().setLayout(new BorderLayout());
23 getContentPane().add(label, BorderLayout.CENTER);
24
25 // Adjust the frame size to macth the GIF images
26 int width = alarmoff.getIconWidth();
27 int height = alarmoff.getIconHeight();
28 setSize(width, height+30);
29
30 }
The Ponder2op annotation tells the compiler to generate stub code to map the PonderTalk message create: to this constructor. Note the use of the ":" character, it tells the compiler that there will be one argument that is to be passed from the PonderTalk command to the constructor. More information about the annotation can be found at Ponder2opAnnotation.
Now we need to add methods for the operational commands, one per command. We have visible:, visible, alarm:, alarm. We can use the Ponder2op annotation is a similar manner:
1 private boolean visible = false;
2
3 @Ponder2op("visible:")
4 public void setAlarmVisible(boolean aBoolean) {
5 // Call the JFrame method
6 setVisible(aBoolean);
7 }
8
9 @Ponder2op("visible")
10 public boolean isAlarmVisible() {
11 // Call the JFrame method
12 return isVisible();
13 }
14
15 @Ponder2op("alarm:")
16 public void setAlarm(boolean aBoolean) {
17 label.setIcon(aBoolean ? alarmon : alarmoff);
18 }
19
20 @Ponder2op("alarm")
21 public boolean isAlarmSet() {
22 return label.getIcon() == alarmon;
23 }
Note that the method names do not have to match the Ponder2op annotations but it does make it clearer if they do. We now have a Java class which is also a Ponder2 managed Object.
Compiling
To compile our new class you can use the Ant build.xml file in the Ponder2 root directory. Simply enter the command:
ant build
Resources
Now we need the alarm clock images. These will go in the resource directory. They can be found in this zip file alarm_clock.zip. Download this file and extract it in the resource directory, it will create two files: alarm_clock-on.gif and alarm_clock-off.gif.
Running
We will see how to test the new managed object using the shell later. For the moment we will test it by writing some PonderTalk.
factory := root load: "AlarmClock". alarm := factory create: "Wake Up". // Put the alarm into the domain hierarchy so that the shell can access it later root at: "alarm" put: alarm. // Now show the alarm alarm visible: true.
Save this file as alarm.p2 in the resource directory. This may be run with the command:
ant run -Dboot=alarm.p2
If all goes well (and why not?) you should see a small alarm clock appear on your screen and it will just sit there. The Ponder2 application is still running. We can now use the shell to test our alarm clock further:
Interacting with AlarmClock
We can use the Command Shell to test our new managed object. Assuming the above section was successful and the alarm window is on your screen, try the following commands
Step |
Command |
Description |
1 |
$ telnet localhost 13570 |
Start a telnet session connected to the Ponder2 internal Command Shell |
2 |
$ ls |
List the root directory. You will see alarm there, that is your actual running Managed Object |
3 |
$ a := root/alarm |
Create a reference to the alarm managed object, it will save us typing root/alarm again |
4 |
$ a visible: false. |
Send the visible: message to the alarm, this will call the setAlarmVisible(boolean) method and hide the alarm |
5 |
$ a visible: true. |
Show the alarm again |
6 |
$ a alarm: true. |
Set the alarm ringing. You should see an animated clock in the window now |
7 |
$ a alarm. |
Ask for the status of the alarm |
Use ctrl-C to kill the running Ponder2 process.