IberAgents Tutorial: Personal Agent

In this tutorial we are going to show how to create a personal agent: an agent that runs on behalf of a user and stores her preferences.

You are supposed to have read the user guide and the previous tutorial on building an application. We will follow the same approach as before: define requirements, develop code and XSL template, and finally take a look at the results.

Requirements

We want to build a personal agent that runs on behalf of a user. The user must be able to login and then logout; new users are added on request. Every second the agent will update a counter, which will be shown to the user; it will also count the number of page views for the user. It must also contain some preferences editable by the user, which are then shown on the main web page.

The requirements are similar to any site that can be personalised by the user, with an additional feature: the software agent is kept running and can perform tasks on behalf of the user. It might e.g. check for updates to a particular site: any task that needs that run independent of the user.

User Preferences

The preferences for our users will be stored in a class that implements UserPreferences. It is a bean object, with getters and setters for each stored attribute.

	package com.ibertest.personal;

	import com.iberagents.user.UserPreferences;

	public class TestPersonalPreferences implements UserPreferences
	{
		...
	}
		

User Attributes

There are two mandatory attributes: user id and password. We will use two additional attributes: an integer containing number of times the agent has been called (acts) and a string containing the user's favorite color (favoriteColor).

There is an attribute that is usually added: the "repeat password" field we often see in web forms. So we will write five attributes, as we see below. (Getters and setters have been omitted for clarity.)

	public class TestPersonalPreferences implements UserPreferences
	{
		private String userId;
		private String password;
		private String repeatPassword;
		private int acts;
		private String favoriteColor;
		...
		// getters and setters
	}
		

Preferences Validation

The method validate() checks that the values in the preferences are ok. Depending on your application, you may want to check for unique user id, consistence with other values, etc. In our case, we simply check that the same password has been entered for the fields password and repeatPassword.

		/**
		 * Check passwords.
		 */
		public boolean validate() throws LifeCycleException
		{
			return this.password.equals(this.repeatPassword);
		}
		

Custom Behavior

We can also add here any convenience methods to modify preferences from the personal agent. For the example we will write a method that increases acts by one.

		/**
		 * Increase acts by one.
		 */
		public void increaseActs()
		{
			this.acts++;
		}
		

Preferences Code

Putting everything together, we get the following:

	package com.ibertest.personal;

	import com.iberagents.user.UserPreferences;
	public class TestPersonalPreferences implements UserPreferences
	{
		private String userId;
		private String password;
		private String repeatPassword;
		private int acts;
		private String favoriteColor;

		/**
		 * Check passwords.
		 */
		public boolean validate()
		{
			return this.password.equals(this.repeatPassword);
		}
	
		/**
		 * Increase acts by one.
		 */
		public void increaseActs()
		{
			this.acts++;
		}
	
		...
		// getters and setters
	}
		

You can view the complete source code here.

Building the Personal Agent

This section explains, step by step, how to build an agent according to the requirements.

Component

The first step is to choose component type. It is started when the user logs in, and therefore it should be a UserComponent; this automatically allows us to store user preferences. It must also run continously, and so it also implements Agent.

Since a UserComponent does not require a separate Java interface, we can just define everything in the same class, like this:

	package com.ibertest.personal;

	import com.iberagents.Agent;
	import com.iberagents.user.UserComponent;

	public class TestPersonalAgent implements UserComponent, Agent
	{
		...
	}
		

Preferences

Each UserComponent is responsible for storing the preferences for the user: we have to implement the methods getPreferences() and setPreferences(). It is enough to store them as an attribute.

		private TestPersonalPreferences preferences;
	
		public UserPreferences getPreferences()
		{
			return this.preferences;
		}
	
		public void setPreferences(UserPreferences prefs)
		{
			this.preferences = (TestPersonalPreferences) prefs;
		}
		

User Realm

The realm of a user is the place where user preferences are located. It can be an LDAP directory, some custom storage or just a plain old database.

IberAgents includes the DatabaseRealm, so that preferences go to the persistence layer. In the constructor we specify the class of the preferences objects we will use. It is stored as a static attribute because the same realm will be used by all personal agents.

		private static DatabaseRealm realm =
			new DatabaseRealm(TestPersonalPreferences.class);
	
		public UserRealm getRealm()
		{
			return realm;
		}
		

Standard Web Methods

First we implement the standard command(), which does nothing; and show(), which shows the main page of the personal agent (the one we see after login). For last one we will use the bean serializer, which takes the preferences object and returns an iberxml element containing its serialization.

		public void command(String command, RequestParameters parameters)
			throws PlatformException
		{
		}

		public ResponsePage show(String arg0, RequestParameters arg1)
			throws PlatformException
		{
			Serializer serializer = (Serializer) Finder.findCore(Serializer.class);
			return new TransformationPage(
				serializer.serialize(this.preferences),
				"testAgent.xsl");
		}
		

In show(), we serialize the preferences and transform them using a xsl template, as we will see below.

Custom Web Methods

A short explanation on web methods is in order. In contrast with J2EE servlets, IberAgents uses functional behavior in web calls: methods that return a web page do not modify internal state (in functional speak, they don't have side effects), while methods that modify internal state do not return anything. You can see this behavior in bean objects: getter methods (of the form getVariable()) return values but do not modify internal state; while setter methods (of the form setVariable()) modify internal state and do not return anything.

The platform will automatically call the correct web methods, based on the parameters command and pageType. In case no methods match their values, the standard methods command() and show() will be called.

So we add two methods: one called modifyPreferences() that returns void, and another one called editConfigurationPage() that returns a ResponsePage. The first modifies configuration values, while the second shows the current configuration for edition.

Now, we can use the BeanEditor to perform these tasks for us: we simply have to call the appropriate methods, as follows.

		public void modifyPreferences(RequestParameters parameters)
			throws PlatformException
		{
			BeanEditor editor = (BeanEditor) Finder.findCore(BeanEditor.class);
			editor.editBean(this.preferences, parameters);
		}
	
		public ResponsePage editPreferencesPage(RequestParameters parameters)
			throws PlatformException
		{
			BeanEditor editor = (BeanEditor) Finder.findCore(BeanEditor.class);
			// get the page to edit preferences
			ParametrizablePage page =
				editor.editBeanPage(this.preferences, parameters);
			// set command and page type after edition
			page.setCommand("modifyPreferences");
			page.setPageType("editPreferencesPage");
			// create link that points to main page
			PageLink link = page.createLink("main");
			link.setParameter("show", "");
			// return our page
			return page;
		}
		

The code for editPreferencesPage() shows how send pages your way; you just set both command and page type, and all links will automatically use these parameters. We also add a custom link called "main", which has a different show parameter (show=""); clicking on it will take the user to the main page via the show() method.

Agent Methods

Our last task is to implement those methods that implement agent behavior. In act() the number of acts is increased by one. Delay between rounds is always one second (1000 milliseconds), and the agent never finishes.

		public void act()
		{
			this.preferences.increaseActs();
		}

		public long getDelay()
		{
			return 1000;
		}

		public boolean finished()
		{
			return false;
		}
		

Complete Code

The resulting class is a bit long to show here; you can view the source code online.

Building the XSL Template

The XSL file to build is called testAgent.xsl, matching the name used in the previous code. From it we may extract the values in the xml element we generated before. Below is a sample xml file received by this XSL template, extracted from the log file.

<bean type="com.ibertest.personal.TestPersonalPreferences">
  <acts type="int">27</acts>
  <favoriteColor type="string">green</favoriteColor>
  <password type="password">gonzo</password>
  <repeatPassword type="password">gonzo</repeatPassword>
  <userId type="string">gonzo</userId>
</bean>
		

As you see, we have available all values in the user preferences. In the template only those that interest us will be shown. We will also add a link at the bottom to edit user preferences, making the show parameter contain the name of the appropriate method, editPreferencesPage().

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
	<xsl:output method="xml" indent="yes"/>

	<xsl:template match="/">
		<html>
			<head>
				<title>Personal Agent</title>
			</head>
			<body>
				<h1>Personal Agent - Main Page</h1>
				<p>
				Hi,
				<xsl:value-of select="bean/userId" />,
				I'm your personal agent. I have
				run
				<xsl:value-of select="bean/acts" />
				times.
				</p>
				<p>
					<font>
						<xsl:attribute name="color">
							<xsl:value-of select="bean/favoriteColor" />
						</xsl:attribute>
					This text will be shown in your
					favorite color.
					</font>
				</p>
				<p>
				You can edit your
				<a href="platform?component=com.ibertest.personal.TestPersonalAgent
				&amp;show=editPreferencesPage">preferences</a>.
				</p>
			</body>
		</html>
	</xsl:template>
</xsl:stylesheet>
		

We could simply place the skeleton XHTML code in the template, and then replace the variable values with their <xsl:value-of> counterparts. The complete template is here.

Testing the Result

The code is ready; let's see how it comes out. Build the ibertest.jar using the build file and deploy it to the platform, then start IberAgents as we saw in the previous tutorial.

Access the Personal Agent Page

The URL for our personal agent is http://localhost:8004/iberagents/platform?component=com.ibertest.personal.TestPersonalAgent; we get the login page since it is a user component.

login page
Login for TestPersonalAgent.

Create a New User

Since we have no users in the database yet, your first task is to click on "sign up!" to create a personal agent. You have to fill in the initial values for user preferences, as shown below.

creating a new user
Page to create a new user.

All values can be modified later. Once you click on "modify values", our method validate() is called to check that the password was entered correctly, and you are presented with the main agent page.

Main Agent Page

main agent page
Main page for the personal agent.

The personal agent greets you by user id, tells you how many times it has acted, and shows some text in your favorite color. How more personal can you get? Well, maybe you want to modify the color after a while.

Edit User Preferences

If you follow the link to "preferences", you will get a page like the one below. As you see, the interface for editing a bean object is very similar to the creation page we saw before.

agent preferences
Page with personal agent preferences.

If you change the favorite color to blue and click on "modify values", you get the new values on the same page.

modified preferences
Modified user preferences.

Now you want to try the link we added to the main page at the bottom of the page.

modified agent page
Main agent page with new preferences.

As expected, the text is now shown in blue. Our personal agent seems to work fine.

Adding Values

Now is the time to check if all requirements are met. Oops!, we forgot to count the number of page views. A good opportunity to revisit all sections and in the process show the extensibility of our personal agent once the basic code is in place; all additions are shown in bold.

Preferences

A new parameter must be added to the user preferences: an integer containing page views. A new convenience method increases this counter. And of course the getter and setter methods.

	public class TestPersonalPreferences implements UserPreferences
	{
		private String userId;
		private String password;
		private String repeatPassword;
		private int acts;
		private String favoriteColor;
		private int pageViews;
		
		public void increasePageViews()
		{
			this.pageViews++;
		}
		
		// getter and setter
		// ...
	}
		

Personal Agent

In the method show() we increase page views.

		public ResponsePage show(String arg0, RequestParameters arg1)
			throws PlatformException
		{
			this.preferences.increasePageViews();
			// ...
		}
		

XSL Template

At the bottom of the XSL template we add a new paragraph:

				...
				<p>
				Page views:
				<xsl:value-of select="bean/pageViews" />
				</p>
			</body>
		

Result

And that's it! After redeploying ibertest.jar, the result is that the number of page views appears at the bottom of the main agent page.

modified agent page
Main agent page with page views.

How come we did not have to modify any database fields? For this test we are using file persistence; any fields not present in the original user preferences are assumed to be empty.

Conclusion

One of the original aims of IberAgents was to enable personalization in web applications. As a consequence, it has been a priority to have a simple mechanism for building personal agents and storing user preferences. In this tutorial we saw that it is indeed very easy to create and maintain personal agents; most of the work is done by the platform itself.

You can proceed to download the skeleton for ibertest, which includes complete code for the personal agent built here. Please contact us if you have any comments about this tutorial.


Updated: Dec 3 2004.
© 2004 Ibermática.
Webmaster

Valid XHTML 1.1!