Coding your first portlet for Pepo

Coding a new portlet to include it in your personal portal is quite easy if you know how to program with Java.
In case you don't, you'll surely find a portlet that fullfill your needs in the ever growing portlets collection developped for the Pepo

What you need

Notions...

We will start with a few bases and notions that will be usefull for the coding of our first portlet.

A portlet

A portlet is a Java object that is dynamically loaded by Pepo when the user put it on a page. A portlet type is defined by a Java class.
The portlet is responsible for generating HTML code to render itself, based on the display mode selected by the user:

A Web server

Pepo works as a small Web server, limited to only one user, grabbing information from the Internet and the local PC, and creating HTML pages and sending them to the user's browser. The pages are formatted according to the user layout.
Contrarily to a stand-alone or client-server software, there's no permanent link between the Web server (the real application) and the browser (the display). Every time a user click on a link or an action button, the Pepo must determine the action to execute by analysing the request URL.
In the case of Pepo, this analyses is simplified as there is only one user. Yet the program must determine which portlet is concerned by the request. This is generally done putting the identifier of the portlet as a request parameter.

HTML

As the user uses Pepo with a Web browser, the portal pages are coded as HTML.
HTML is a page description language that uses tags, for instance <strong> to display the enclosing text in bold, and end it with </strong>, like in that example. If you don't know HTML, we recommend that you find a tutorial. The more simple solution is to search on the millions available on the Internet...
So each portlet must generate valid HTML code to display itself, without disrupting its neighbours, inside a valid HTML page (that to say between <body> and </body> tags). For that reason, we generally place the HTML code to render a portlet inside an HTML table. All this is explain in more details later...

XML

XML is also a tag language, just like HTML. But in the opposite of HTML where tag names are defined by the language, XML tags can be selected by the programmer. Contrarily to HTML where lower and upper cases are not distinguished, XML tags are case sensitive.
Pepo uses XML in numerous occasions, the more frequent is to save the portal and portlets state in configuration files.
Pepo includes a XML Digester that let thr programmer handle and analyse XML data very easily.

HTML Cascading Style Sheets

In order to give every user the ability to personalize the layout of his portal, Pepo uses HTML style sheets. A style sheet discriminates between the content (the information displayed by the portlet) and the look (font size, color...). Every portlet must report to the Pepo the styles needed to render itself, so the user can adapt them to change their look.
If you want to lear more about HTML style sheets, search the Internet using CSS, style sheet or Cascading Style Sheet with your favorite search engine.

To learn more on these subjects...

portlet
HTML
XML
HTML Cascading Style Sheets
Cascading Style Sheet standard, as defined by W3 consortium
Which styles your favorite browser knows...
A tutorial...

With these preliminaries, we are going to start coding a very simple portlet that will display a picture.

Displaying a picture

Our first portlet, the PhotoPortlet will display a picture in a frame. We want to allow the user to give the name of a graphic file, enter dimensions, and eventually a title.
We want him to be able to change the picture display, for instance the frame or the title style.

PhotoPortlet.java file

Our first portlet PhotoPortlet is written in the file PhotoPortlet.java that contains the PhotoPortlet class.
  1. Create the directory arborescence: tv/alterna/pp/photo
    1. tv
    2. alterna
    3. pp
    4. photo
  2. Change the CLASSPATH environment variable to include the root of the directories arborescence (below tv) that you've just created.
  3. Create the PhotoPortlet.java file in the photo directory.

We are going to comment the details of our first portlet code.

Package and import clauses

Every Java class should include the package it belongs to, followed by the list of imports necessary to compile. The PhotoPortlet doesn't go against that rule.

Our class belongs to the tv.alterna.pp.photo package. That's good practice to put all the files that compose a portlet in a separate package, but you don't need to name it with a tv.alterna.pp prefix.

package tv.alterna.pp.photo;
Then, we must list all imports. The first block references the base classes from the PersonalPortal
import tv.alterna.pp.PersonalPortal;
import tv.alterna.pp.PersonalPortlet;
import tv.alterna.pp.PageRequest;
import tv.alterna.pp.PortalConfig;
import tv.alterna.pp.StyleSheet;
import tv.alterna.pp.Style;
import tv.alterna.pp.StyleProperty;
import tv.alterna.pp.PortalUtil;

import tv.alterna.utils.HtmlUtil;

import java.util.Iterator;
As our portlet will save and load its configuration in a file, it needs at least how to do it.
import java.io.PrintWriter;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.InputStream;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
Then, we will format configuration files with XML, and the XML Digester is here to help us it the task.
import org.apache.struts.digester.Digester;

import org.xml.sax.SAXException;

The PhotoPortlet class

As we have put our code in the PhotoPortlet.java file, the class will be name, to be really original, PhotoPortlet!
Portlets must inherit from the PersonalPortlet class that defines their default behavior and the contracts they must follow.
/**
 *  The Photo portlet is a very simple portlet to display a picture in a frame.
 *
 * @author     GenePi
 * @created    19 mars 2001
 */
public class PhotoPortlet
		 extends PersonalPortlet
{
Portlet class are declared with access control public to let the portal create instances when the user put a portlet of that type on his page.

PhotoPortlet attributes

We want the user to type in the picture file name, the dimensions of the picture on the page and a title. We will store this information into attributes, declared with access control private as the portlet methods are only allowed to change these data.
These attributes contains the state of each PhotoPortlet object that a user put on his portal page.
	/**
	 *  The file name of the picture.
	 */
	private String _fileName = "";

	/**
	 *  The width of the picture
	 */
	private String _width = "";

	/**
	 *  The height of the picture
	 */
	private String _height = "";
We don't have defined a _title atribute to store the picture title. In fact, this attribute is already defined for every portlet in the class PersonalPortlet.

Setters and getters

To read and write into these attributes, we are going to define access methods. These methods are used when the user change the value of an attribute, for instance changing the picture file name, or when the portlet objects are created when Pepo initializes.
For every attribute, we define a method to store the value (setAttribut()) and another to read it (getAttribut()).
	/**
	 *  Set the name of the picture file.
	 *
	 * @param  fileName  The file name.
	 */
	public void setFileName(final String fileName)
	{
		_fileName = (fileName == null) ? "" : fileName;
	}
We don't store the null value into String attributes, but we replace it with the empty string "". This make the display of the attribute simpler when the portlet is displayed in configuration mode.
	/**
	 *  Set the width of the picture.
	 *
	 * @param  width  The width of the picture.
	 */
	public void setWidth(final String width)
	{
		_width = (width == null) ? "" : width;
	}

	/**
	 *  Set the height of the picture.
	 *
	 * @param  height  The height of the picture.
	 */
	public void setHeight(final String height)
	{
		_height = (height == null) ? "" : height;
	}

	/**
	 *  Obtain the name of the file containing the picture.
	 *
	 *  The file name can be either:
	 *  - a URL http://...
	 *  - an absolute path file://...
	 *  - a relative path to the base of the portal
	 *
	 * @return    The file name
	 */
	protected String getFileName()
	{
		return _fileName;
	}
The picture file name can be, a Web URL (of the form http://www.asite...), or a local URL (of the form file:///C:/My Documents/...), or else a path relative to the portal base. The portal base determines the directory from which the Pepo looks for the resources, and can be changed in the GeneralPortlet with all other portal options.
	/**
	 *  Obtain the width of the picture.
	 *
	 * @return    The width in pixels
	 */
	protected String getWidth()
	{
		return _width;
	}

	/**
	 *  Obtain the height of the picture.
	 *
	 * @return    The height in pixels
	 */
	protected String getHeight()
	{
		return _height;
	}
All access methods that can be called from another package, and in particular from the portal engine or the XML Digester, must be defined with public access control..

Portlet display

A portlet runs mainly with 3 display modes, which are defined in the PersonalPortlet class: To react to these display modes, every portlet must redefine the following methods: The PersonalPortlet class has a default implementation of getLayoutHTML that only displays the title of the portlet. But you must define the other two methods.

Let's start with getDisplayHTML.
The objective of this method is to display a picture in a frame. To achieve that, we will use the HTML tag <img>. To create the frame, and to avoid that the portlet encroach upon the other components of the portal page, we will insert the generated HTML code into a table (tag <table>).
We will define two styles:

That way, the user will be able to change independantly the look of the portlet (background color, frame style...) and the title of the picture (color, font...).
The procedure will have to generate the follwing HTML code, that we indented for a better understanding:
<table class="photoPortlet">
  <tr>
    <td>
      <img src="MyPicture.jpg" border="0" alt="My picture">
    </td>
  </tr>
  <tr>
    <td class="photoTitle">
      My picture
    </td>
  </tr>
</table>
Let's go...
	/**
	 *  Generate the HTML code used to display the data.
	 *
	 * @param  request  The request.
	 * @return          The HTML used to display the data.
	 */
	protected String getDisplayHTML(final PageRequest request)
	{
		StringBuffer buf = new StringBuffer();
We check first that we know the file name of the picture. If we don't, we display an error message with the system style error.
		// Check that we have all needed information
		if (getFileName() == null || "".equals(getFileName()))
		{
			final String msgError = HtmlUtil.encode(getMessage("getDisplayHTML.error.fileMissing"));

			buf.append("<table class=\"photoPortlet\"><tr><td class=\"error\">");
			buf.append(msgError);
			buf.append("</td></tr></table>");

			return buf.toString();
		}
Then we can start to generate the HTML code. If the picture file name is a URL, then we can use it as source in the tag <img>. Else, we consider it's a relative path and we must concatenate it with the portal base.
		buf.append("<table class=\"photoPortlet\"><tr><td>");
		buf.append("<img src=\"");
		buf.append(HtmlUtil.encodeURI(PortalUtil.addBase(getFileName())));
		buf.append("\" border=\"0\"");
If the user has typed the image size, we take it into account to display the picture.
		if (getWidth() != null && !"".equals(getWidth()))
		{
			buf.append(" width=\"");
			buf.append(getWidth());
			buf.append("\"");
		}

		if (getHeight() != null && !"".equals(getHeight()))
		{
			buf.append(" height=\"");
			buf.append(getHeight());
			buf.append("\"");
		}

		buf.append(" alt=\"");
		buf.append(getTitle());
		buf.append("\">");
If the picture has a title, we add a new row to the table and put the title there, that will be displayed under the photo.
		if (getTitle() != null && !"".equals(getTitle()))
		{
			buf.append("</td></tr><tr><td class=\"photoTitle\">");
			buf.append(getTitle());
		}
		buf.append("</td></tr></table>\n");

		return buf.toString();
	}

Multilingual support

Your portlet should support displaying messages in multiple languages. The Pepo has the necessary tools to achieve that task without effort, like we did to print the error message in the getDisplayHTML()
function.
The getMessage() method, defined into the PersonalPortlet class, can find a message in the language selected by the user. You, as author, have the responsability to put the messages in the supported languages into the resource files. This method has orther overloaded variants with a variable number of arguments.
The resource file contains a list of keys, associated with the translated messages. The name of the resource file must end with a language suffix (fr for French messages, en for the English ones, es for the Spanish...) and have the extension .properties.

The HtmlUtil.encode() methods filters sensitive characters from the message and replace them with HTML entities. That way, you don't need to know if your messages contains characters like <, >, & or even accentuated ones.

Creating the resource files for our example:

  1. Edit the PhotoPortlet_fr.properties file and save it into the init directory.
  2. Type in the French messages used by our portlet:
    # PhotoPortlet - Messages en français
    #####################################
    
    getDisplayHTML.error.fileMissing=Le nom du fichier image n''a pas été renseigné.
    
    getParameterHTML.title=Titre de la photo
    getParameterHTML.help=Saisissez le nom d''un fichier graphique et indiquez les \
    dimensions de l''image si vous désirez la redimensionner. Vous pouvez saisir \
    une URL, un chemin absolu ou relatif par rapport à la base de Pepo.\n
    Pour changer la présentation du cadre de la photo, utilisez le portlet \
    Gestionnaire de Styles.
    getParameterHTML.fileName=Nom du fichier contenant l''image
    getParameterHTML.width=Largeur de l''image en pixels
    getParameterHTML.height=Hauteur de l''image en pixels
    
    # Styles
    ########
    style.photoPortlet=Cadre de la photo
    style.photoTitle=Titre de la photo
    
  3. Edit the PhotoPortlet_en.properties file and save it into the init directory too.
  4. Type in the text for the English messages (we will only support these two languages in our example, but you can add others if you wish):
    # PhotoPortlet - English messages
    #################################
    
    getDisplayHTML.error.fileMissing=The file name of the picture is missing and \
    can''t be displayed.
    
    getParameterHTML.title=Title of the photo
    getParameterHTML.help=Type the name of a graphic file and input the width and \
    height of the picture if you want to resize it. You can type an URL, an absolute \
    path file or relative to Pepo base.\nTo change the appearance of \
    theframe of the picture, use the Styles Manager portlet.
    getParameterHTML.fileName=Name of the picture file
    getParameterHTML.width=Width of the picture, in pixels
    getParameterHTML.height=Height of the picture, in pixels
    
    # Styles
    ########
    style.photoPortlet=Frame of the picture
    style.photoTitle=Title of the picture
    
The " character must be doubled in the messages resource files to be displayed correctly.

Input of the portlet parameters

When the portal user changes to configuration mode, he can enter the portlet parameters. In that mode, the portlet displays a form with the actual attributes values. The user can enter new values, and submit the changes.
The getParameterHTML() method has the responsability to generate the corresponding HTML code.
We want to generate the following HTML code:
<form method="get" action="/admin/Photos/">
  <input type="hidden" name="id" value="28">
  <table class="portlet">
    <tr>
      <td class="portletTitle" colspan="2">Photo - My Picture</td>
    </tr>
    <tr>
      <td class="portletHelp" colspan="2">Type the name
      of a graphic file and input the width and height of the picture if you want
      to resize it. You can type an URL, an absolute path file or relative to
      Pepo base.<br>To change the appearance of the frame of the
      picture, use the Styles Manager portlet.</td>
    </tr>
    <tr>
      <td class="label">Title of the photo</td>
      <td class="value"><input type="text" class="value" name="title" size="30" value="My Picture"></td>
    </tr>
    <tr>
      <td class="labelMandatory">Name of the picture file</td>
      <td class="valueMandatory"><input type="text" class="valueMandatory" name="fileName" size="30" value="img/MyPicture.jpg"></td>
    </tr>
    <tr>
      <td class="label">Width of the picture, in pixels</td>
      <td class="value"><input type="text" class="value" name="width" maxlength="5" size="5" value=""></td>
    </tr>
    <tr>
      <td class="label">Height of the picture, in pixels</td>
      <td class="value"><input type="text" class="value" name="height" maxlength="5" size="5" value=""></td>
    </tr>
    <tr class="action">
      <td class="action" colspan="2"><input type="submit" name="generalSubmit" class="submit" value="Submit">
      &nbsp;
      <input type="reset" name="generalReset" class="reset" value="Reset"></td>
    </tr>
  </table>
</form>
	/**
	 *  Generate the HTML code used in parameter input mode.
	 *
	 *  If the portlet has to process a form, it must include its
	 *  <code>id</code> in the parameters in order to receive the request
	 *  when the user submit the form.
	 *
	 * @param  request  The request.
	 * @return          The HTML code to input paramters.
	 */
	protected String getParameterHTML(final PageRequest request)
	{
Messages displayed to the user are retrieved from resource files.
		final String msgTitle = HtmlUtil.encode(getMessage("getParameterHTML.title"));
		final String msgHelp = HtmlUtil.encode(getMessage("getParameterHTML.help"));
		final String msgFileName = HtmlUtil.encode(getMessage("getParameterHTML.fileName"));
		final String msgWidth = HtmlUtil.encode(getMessage("getParameterHTML.width"));
		final String msgHeight = HtmlUtil.encode(getMessage("getParameterHTML.height"));
		final String msgSubmit = HtmlUtil.encode(getMessage("button.submit"));
		final String msgReset = HtmlUtil.encode(getMessage("button.reset"));

		StringBuffer buf = new StringBuffer();
In order for the portlet to receive the user input, it must tell the portal that it is interested in treating the request. It can do that with the method boolean PersonalPortlet.hasInterest(PageRequest request). The default implementation of that method sends the request to the portlet whose identifier is given as a request parameter. That's the reason why we define a hidden field that contains the portlet identifier, obtained with a call to getId().
		// Form
		buf.append("<form method=\"get\" action=\"");
		buf.append(request.getAction());
		buf.append("\">\n");
		buf.append("<input type=\"hidden\" name=\"id\" value=\"");
		buf.append(getId());
		buf.append("\">\n");
		
		buf.append("<table class=\"portlet\"><tr><td class=\"portletTitle\" colspan=\"2\">");
		buf.append(getHTMLIconHelp("help"));
		buf.append("Photo - ");
		buf.append(getTitle());
		buf.append("</td></tr>\n<tr><td class=\"portletHelp\" colspan=\"2\">");
		buf.append(msgHelp);
		buf.append("</td></tr>\n<tr><td class=\"label\">");
		buf.append(msgTitle);
		buf.append("</td><td class=\"value\"><input type=\"text\" class=\"value\" name=\"title\" size=\"30\" value=\"");
		buf.append(getTitle());
		buf.append("\"></td></tr>\n<tr><td class=\"labelMandatory\">");
		buf.append(msgFileName);
		buf.append("</td><td class=\"valueMandatory\"><input type=\"text\" class=\"valueMandatory\" name=\"fileName\" size=\"30\" value=\"");
		buf.append(getFileName());
		buf.append("\"></td></tr>\n<tr><td class=\"label\">");
		buf.append(msgWidth);
		buf.append("</td><td class=\"value\"><input type=\"text\" class=\"value\" name=\"width\" maxlength=\"5\" size=\"5\" value=\"");
		buf.append(getWidth());
		buf.append("\"></td></tr>\n<tr><td class=\"label\">");
		buf.append(msgHeight);
		buf.append("</td><td class=\"value\"><input type=\"text\" class=\"value\" name=\"height\" maxlength=\"5\" size=\"5\" value=\"");
		buf.append(getHeight());
		buf.append("\"></td></tr>\n");

		// Action buttons
		buf.append("<tr class=\"action\"><td class=\"action\" colspan=\"2\">");
		buf.append("<input type=\"submit\" name=\"generalSubmit\" class=\"submit\" value=\"");
		buf.append(msgSubmit);
		buf.append("\">&nbsp;");
		buf.append("<input type=\"reset\" name=\"generalReset\" class=\"reset\" value=\"");
		buf.append(msgReset);
		buf.append("\"></td></tr>\n");

		buf.append("</table>\n");
		buf.append("</form>");

		return buf.toString();
	}
When the portlet creates code for the display modes PersonalPortlet.LAYOUT or PersonalPortlet.PARAM, that's to say in the methods getLayoutHTML() and getParameterHTML(), you must not use the portlet style sheet but the system one instead. This is done to guarantee that all the portlets have the same look in that mode.

Process the user input

The user has enterd values into the portlet parameters form, we have now to take into account these values and store them into the portlet attributes.
The PersonalPortlet class defines the method below to do these operations: Pepo will call these methods, for the portlets that are interested in the request, giving them the full request and its parameters (the user input). These methods are called only on the portlets that want to receive the request: the portlets must redefine the PersonalPortlet.hasInterest(int mode, PageRequest request) method and return the value true to be called back. To make the development of portlets simpler, the default definition of hasInterest() considers that a portlet is interested in receiving a request if there's a parameter named id whose value is the portlet identifier.
That's the reason why we set an hidden field id in the portlet input form. We don't have anything more to do to process the user input...
	/**
	 *  Process the input from the parameters form.
	 *  Save the name of the file containing the picture, and other
	 *  information.
	 *
	 * @param  request  The request.
	 * @see             PersonalPortlet#hasInterest
	 */
	protected void processParameterForm(final PageRequest request)
	{
		String fileName = request.getParameterValue("fileName");
		setFileName(fileName);

		String title = request.getParameterValue("title");
		setTitle(title);

		String width = request.getParameterValue("width");
		setWidth(width);

		String height = request.getParameterValue("height");
		setHeight(height);
	}
This method only extracts values from the request parameters and store them in the portlet attributes.

Save the portlet state

At that point, our portlet is fully operational and can be used to display a picture in a portal page.
Though, user input is lost when we stop the PersonalPortlet. We are going to add methods to save the portlat state, and, of course, the ability to reload it.
	/**
	 *  Request to save the state of the portlet.
	 *  The configuration is usually written as an XML file.
	 *
	 * @exception  IOException  An exception occured while saving the state
	 * of the portlet.
	 */
	protected void saveConfig()
		throws IOException
	{
		PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(getConfigFile() + getId() + ".xml")));
This procedure only opens a PrintWriter and write the portlet content into.
Every instance of the PhotoPortlet has different attribute values, and we've decided to save them in XML files names PhotoPortletY.xml, where Y is the portlet identifier. The file name prefix is obtained with a call to PersonalPortlet.getConfigFile().
		out.println("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>");
		out.println();

		out.println("<!-- PhotoPortlet Configuration file -->");
		out.println("<!-- =============================== -->");
		out.println();
		out.println("<photoPortlet>");

		saveConfig(out);

		out.println("</photoPortlet>");

		out.flush();
		out.close();
	}
saveConfig() and loadConfig methods are automatically called by the portal when the user want to save the configuration, or when he starts the portal and all the portlets are initialized (or when he adds a new portlet to a page).

XML configuration file format

The PhotoPortlet configuration file supports a very simple XML syntax that translates the portlet attributes.
<!-- PhotoPortlet Configuration file -->
<!-- =============================== -->

<photoPortlet>
	<!-- Title of the photo or picture -->
	<title>MaPhoto</title>
	<!-- Name of the file containing the picture -->
	<fileName>img/MaPhoto.jpg</fileName>
	<!-- Width of the picture, in pixels or percent -->
	<width></width>
	<!-- Height of the picture, in pixels or percent -->
	<height></height>
</photoPortlet>
Remember that the XML syntax distinguishes between upper and lower case. So, <photoPortlet> is different from <PhotoPortlet>. You can check that your XML file is well balanced if you can open it with Microsoft Internet Explorer: that browser can read XML files and will print a message if it detects an error.

We have to handle two XML configuration files:

These files are put into the init directory.

Load portlet configuration

Now, we have to write the reverse operation and read the configuration file content to initialize the PhotoPortlet object.
The loadConfig() method is called when the portal is started and portlet instances are loaded, or when the user add a new PhotoPortlet on a page. In the first case, initialization data is found into init/PhotoPortletY.xml, and in the second case into init/PhotoPortlet.xml.
	/**
	 *  Request to read the portlet configuration file and set the
	 *  portlet state. We try first to read the configuration from the
	 *  file "PhotoPortlet{id}.xml", and if not found from "PhotoPortlet.xml".
	 *
	 * @param fileName The name of the configuration file.
	 * @exception  IOException  A problem occured while loading the
	 *  portlet configuration.
	 */
	protected void loadConfig(final String fileName)
		throws IOException
	{
		PersonalPortal.log(2, "Loading configuration of portlet: " + this);
		
		// Load the config
		InputStream in = null;
		try
		{
			in = new BufferedInputStream(new FileInputStream(fileName));
We use the XML Digester to read and interpret an XML file. It parses the file content and call the methods we specify when it matches patterns in the input file.
We invite you to read the XML Digester documentation for more information.
We create a new Digester object that parses XML files without validation (we don't have defined an XML grammar) and we make it communicate with the portlet (digester.push(this)).
			Digester digester = new Digester();
	
			digester.push(this);
			digester.setDebug(PersonalPortal.getDebug() - 1);
			digester.setValidating(false);
When the XML input stream matches the tag <title> inside a tag <photoPortlet>, we tell the Digester to call the setTitle() method with the <title> content as parameter.
			// Configuration of the portlet
			digester.addCallMethod("photoPortlet/title", "setTitle", 0);
We continue with the Digester rules to call the corresponding methods when we encounter the following tags <fileName>, <width> and <height; in the XML input stream.
			digester.addCallMethod("photoPortlet/fileName", "setFileName", 0);
			digester.addCallMethod("photoPortlet/width", "setWidth", 0);
			digester.addCallMethod("photoPortlet/height", "setHeight", 0);
The last step is to call the parse method to have the digester process the XML file and initialize the PhotoPortlet instance with the read values.
			digester.parse(in);
		}
		catch (IOException ioe)
		{
			PersonalPortal.log(0, "I/O error while reading PhotoPortlet configuration file. reason: " + ioe.getMessage());
		}
		catch (SAXException se)
		{
			PersonalPortal.log(0, "Error in the PhotoPortlet configuration file. reason: " + se.getMessage());
		}
		finally
		{
			if (in != null)
			{
				in.close();
			}
		}
	}

PhotoPortlet style sheet

We have used two styles named photoPortlet and photoTitle.
When the portal displays PhotoPortlet portlets on a user page, it needs to access the style definitions. As we say before, these styles are stores into the init/PhotoPortlet.xml file.

Here that file content:

<!-- PhotoPortlet Configuration file -->
<!-- =============================== -->

<photoPortlet>
	<!-- Title of the photo or picture -->
	<title>Ma Photo</title>
	<!-- Name of the file containing the picture -->
	<fileName>img/MaPhoto.jpg</fileName>
	<!-- Width of the picture, in pixels or percent -->
	<width></width>
	<!-- Height of the picture, in pixels or percent -->
	<height></height>

	<!--
	  Style sheet used by the PhotoPortlet
	-->
	<styles>
		<style name=".photoTitle"
		       description="style.photoTitle">
			<property name="font-family"
			          value="Arial, Helvetica, sans-serif" />
			<property name="font-size"
			          value="12px" />
			<property name="font-variant"
			          value="small-caps" />
			<property name="font-weight"
			          value="bold" />
			<property name="text-align"
			          value="center" />
			<property name="color"
			          value="yellow" />
		</style>
		<style name=".photoPortlet"
		       description="style.photoPortlet">
			<property name="border-width"
			          value="10" />
			<property name="border-color"
			          value="gold" />
			<property name="background-color"
			          value="goldenrod" />
			<property name="border-style"
			          value="outset" />
		</style>
	</styles>
</photoPortlet>

Loading the style sheet definitions

Pepo automatically calls the loadStyleSheet() and saveStyleSheet() methods when the portlet needs to load or save its style sheet.
	/**
	 *  Request to load the styles sheet to render the portlet as HTML.
	 *
	 * @exception  IOException  A problem occured while loading the portlet
	 *  style sheet.
	 */
	protected void loadStyleSheet()
		throws IOException
	{
Each portlet is called back to load the style sheet that it uses. Though, the style sheet is frequently shared between all portlets of the same type (PortletDefinition) that will be displayed in the same way.
We decide to load the style sheet only when necessary (not already done) by a call to StyleSheet.loadStyleSheet() with parameters the directory where to find the file and the matching patterns (<styles> tags inside of <photoPortlet> tag).
		// Loading the style sheet associated with that portlet, if necessary
		StyleSheet styleSheet = getDefinition().getStyleSheet();

		if (styleSheet == null)
		{
			styleSheet = StyleSheet.loadStyleSheet(getConfigFile() + ".xml", "photoPortlet");
If we can't load the style sheet, we decide to create a new file with default values.
			if (styleSheet == null)
			{
				styleSheet = new StyleSheet();

				Style style = new Style(".photoPortlet", "style.photoPortlet");
				style.addProperty(new StyleProperty("border-style", "outset"));
				style.addProperty(new StyleProperty("border-color", "gold"));
				style.addProperty(new StyleProperty("border-width", "10"));
				style.addProperty(new StyleProperty("background-color", "goldenrod"));
				styleSheet.addStyle(style);

				style = new Style(".photoTitle", "style.photoTitle");
				style.addProperty(new StyleProperty("font-family", "Arial, Helvetica, sans-serif"));
				style.addProperty(new StyleProperty("color", "yellow"));
				style.addProperty(new StyleProperty("font-size", "12px"));
				style.addProperty(new StyleProperty("font-variant", "small-caps"));
				style.addProperty(new StyleProperty("font-weight", "bold"));
				style.addProperty(new StyleProperty("text-align", "center"));
				styleSheet.addStyle(style);
			}
We associate the style sheet with the PhotoPortlet portlet type.
			
			getDefinition().setStyleSheet(styleSheet);
		}
	}

Saving the style sheet

Saving the style sheet is the reverse operation. Every portlet is called on the saveStyleSheet() method.
We decide that every PhotoPortlet will write into the init/PhotoPortlet.xml file its attributes values, followed by the style sheet properties. If the user has many instances of that type of portlet, they will all write into the same file: the attribute values of the last portlet instance will be used as default values. We could have defined other algorithms, but this one is definitively the simplest.
	/**
	 *  Request to save the style sheet associated with that portlet (or
	 *  common to all the portlets with the same portlet definition).
	 *
	 * @exception  IOException  If a problem occurs while saving the styles.
	 */
	protected void saveStyleSheet()
		throws IOException
	{
		PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(getConfigFile() + ".xml")));

		out.println("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>");
		out.println();

		out.println("<!-- PhotoPortlet Configuration file -->");
		out.println("<!-- =============================== -->");
		out.println();
		out.println("<photoPortlet>");
Save the portlet attributes.
		// The configuration of the portlet, as example
		saveConfig(out);
Save the style sheet associated with the portlet type. The StyleSheet.getXMLConfig() is call with a indent level parameter to keep the XML file correctly indented.
		// The styles
		out.println("\n\n\t<!--");
		out.println("\t  Style sheet used by the PhotoPortlet");
		out.println("\t-->");

		StyleSheet styleSheet = getDefinition().getStyleSheet();
		if (styleSheet != null)
		{
			out.println(styleSheet.getXMLConfig(1));
		}

		out.println("</photoPortlet>");
		out.flush();
		out.close();
	}
The saveConfig method, already used in previous methods, actually save the portlet attributes.
	/**
	 *  Save the state of the PhotoPortlet on the writer.
	 *
	 * @param  out              The writer where to save the configuration of the portlet.
	 * @exception  IOException  If an IO error occurs.
	 */
	private void saveConfig(final PrintWriter out)
		throws IOException
	{
		out.println("\t<!-- Title of the photo or picture -->");
		if (getTitle() == null)
		out.print("\t<title>");
		out.print(getTitle());
		out.println("</title>");

		out.println("\t<!-- Name of the file containing the picture -->");
		out.print("\t<fileName>");
		out.print(getFileName());
		out.println("</fileName>");

		out.println("\t<!-- Width of the picture, in pixels or percent -->");
		out.print("\t<width>");
		out.print(getWidth());
		out.println("</width>");

		out.println("\t<!-- Height of the picture, in pixels or percent -->");
		out.print("\t<height>");
		out.print(getHeight());
		out.println("</height>");
	}

A style example

We're near to the end.
To give a more professional look to our portlet, we define the last method: getHTMLStylesExample() whose role is to generate HTML code showing the styles used by the portlet. That method is called when the user changes the style definitions for the portlet, as a visual feedback of modifications.
Our implementation will display a picture with a title, using the PhotoPortlet styles. If you choose not to redefine that method, the default implementation print a warning message telling there's no style example.
	/**
	 * Create an example demonstrating the use of the styles properties
	 * so that the user immediately see the impact of a style property
	 * change.
	 * Used to display an example in the StylesManager portlet.
	 *
	 * @param title The type of the portlet selected by the user, to
	 * appear in the title.
	 * @return The HTML code to create the example.
	 */
	protected String getHTMLStylesExample(final String title)
	{
		StringBuffer buf = new StringBuffer();
		
		buf.append("<table class=\"photoPortlet\"><tr><td><img src=\"");
		buf.append(HtmlUtil.encodeURI(PortalConfig.getBase()));
		buf.append("img/PhotoPortlet.jpg\" border=\"0\"></td></tr>\n<tr><td class=\"photoTitle\">");
		buf.append(HtmlUtil.encode(title));
		buf.append("</td></tr></table>\n");
		
		return buf.toString();
	}
Copy a JPEG file named PhotoPortlet.jpg into the img directory to serve as sample picture.

Adding the PhotoPortlet code to Pepo

We must register into Pepo that we have created a new portlet type and how to use it. This operation is done into the init/PersonalPortal.xml file.
  1. Open the init/PersonalPortal.xml with a file editor.
  2. Search the <portlets> section
  3. Add the follwing lines to that section:.
    	<!-- List the portlets available. -->
    	<!-- ============================ -->
    	<!--
    	  For each portlet definition, we have the attributes:
    	  - type: The type of the portlet.
    	  - description: The name that will be presented to the user
    	    to select the portlet in the portal.
    	  - className: The class of the portlet, used to load it if the
    	    user select it in his portal.
    	  - propertyFile: The base file name of the property file
    	    containing the localized messages for that type of portlet.
    	  -->
    	<portlets>
    		<definition type="mailPOP3"
    		            description="Courier POP3"
    		            className="tv.alterna.pp.mail.POP3Portlet"
    		            propertyFile="init/POP3Portlet" />
    
    We say that the portlet type is photo (the type name must be unique), that the portlet is defined by the Java class tv.alterna.pp.photo.PhotoPortlet and where is located the configuration file (init/PhotoPortlet). We also have to define a brief comment describing the portlet, that will be displayed to the user when he wants to select it and place a new instance on a portal page (Photo and picture).
    		<definition type="photo"
    		            description="Photo and picture"
    		            className="tv.alterna.pp.photo.PhotoPortlet"
    		            propertyFile="init/PhotoPortlet" />
    
    Lines were removed here...
    	</portlets>
    
  4. Save the file.

Looking at the result

Here is the last step. The last operations are:
  1. Save the PhotoPortlet.java file.
  2. Compile it with the Java compiler. Correct errors if necessary...
  3. Stop and restart Pepo to load the modifications.

If you swith into the portal configuration mode, you will notice that a new portlet type is available Photo and picture that you can use to display a picture.

When it does not work...

Computer programs never work at first as intended. If you can't make your portlet work, check the following points:

Next steps...

To learn more in portlet development for Pepo, you can study the code of other portlets given as examples:

To learn on these subjects...

Resource files and multilingual support
ResourceBundle
XML Digester
The Struts project from where originated the XML Digester
Other portlets
SnapshotTVPortlet
POP3Portlet
NewsPortlet
ToDoPortlet