Portlet Life Cycle

All portlets can be automatically created when Jetspeed first starts up. The default and recommended behaviour is not to create the portlets until they are accessed on a PSML page. Once a page where a portlet exists is requested, then at this point, the portlet is created and added to the portlet cache.


# Jetspeed can automatically create/instantiate all your Portlets
# and place them in the cache when Jetspeed starts up. 
autocreate.portlets=false

At the time of portlet creation, a portlet's init() method is called. The init method is only called once, during the portlet creation phase. The lifetime of a portlet is controlled by how long a portlet stays in the cache. When a portlet is removed from the cache, the portlet is effectively removed from memory (left to the garbage collector) and is no longer accessible. If a portlet is removed from the cache, and then requested again, the portlet will have its init method called again, since a new instance is created of the portlet. Both the system administrator and programmer can control the lifetime of a portlet by controlling how long it lives in the cache.

A portlet is managed through a not so well defined life cycle that defines how it is initialized, how it handles requests for content, and how long it lives based on caching behaviour described later in this section. Thus you could consider there to be 3 phases in the life cycle of a portlet:

  • 1. Init
  • 2. Render
  • 3. Destroy

Jetspeed does support another phase in between phase 1 and 2. It is called the processAction phase. Unfortunately, the processAction phase does not manifest itself directly in the portlet interface. Instead, actions must be handled in action classes. Action classes are inherited from Turbine, and they bleed through into the design of Jetspeed, coupling the Jetspeed architecture to Turbine's in a very bad way. Velocity portlets attempt to rectify this design flaw, but the bottom line is that is still a kludge and the action processing phase is not a part of the portlet interface, but must be handled on another action interface. We will cover actions in more depth in the section on Velocity Portlets.

To match the phases with interface methods, we have:

PhaseMethod
Initinit
ProcessActionTurbine Actions
RendergetContent
Destroy--none--

During the init phase, it is recommended that you do any one-time initialisation of the portlet. Usually this involves the initialisation of costly resources such as database connections or other costly activities. The render phase is called per request. Every time a portlet's content is requested, the getContent method is called.

The destroy phase is dependent on the life time definition of the portlet. Unfortunately, your portlet is never notified when the destroy phase occurs. You can control the life time of your portlet. If your portlet inherits from AbstractPortlet, then the lifetime of your portlet is controlled by how long it lives in the cache, and by how many instances of the portlet there are in the cache.

First let's look at how long your portlet will live in the cache. By default, this is controlled by a global portal setting for all portlets:


# TimeToLive.default =
# number of milliseconds an unused portlet will remain in cache.
# Default 2700000 which is 45 minutes (45 * 60 * 1000)
services.PortletCache.TimeToLive.default=2700000

The default setting is 45 minutes. After a portlet has been in the cache for 45 minutes, it will be reloaded. This means the init method is called again. Since there is no destroy method, your portlet doesn't know when it is getting destroyed.

If your portlet inherits from AbstractPortlet or AbstractInstancePortlet, then your portlet is cacheable, but it will not be evicted from the cache, since the base implementation AbstractPortlet never allows for your portlet to expire.


public Expire getExpire()
{
try
{
           return ExpireFactory.getExpire( this,
                                       ExpireFactory.NO_EXPIRE );
        } catch ( JetspeedException e ) {
            Log.error( e );
            return null;
        }
    }
}

Some portlets will expire, such as the FileWatchPortlet and NewRSSPortlet which uses the cache to control refreshing of its content.

Whenever Jetspeed goes through the initialisation phase for a portlet, it will first check to see if the portlet already exists in the cache. It does this by looking up the portlet using a unique cache handle. The cache handle method can be used to control the number of instances of the same portlet in the cache. AbstractPortlet has a method getHandle. For the base class, a rather odd algorithm is used to determine whether a new portlet instance should be created:

  • 1. the URL parameter, but only if the cachedOnURL attribute is set to true
  • 2. all other parameters, both their names and values

The algorithm takes all of these strings and combines them to make a unique string. Thus the creation of multiple instances of possibly the same portlet is controlled by the uniqueness of the portlets parameters. This seems like a rather odd solution, but works perfectly fine if all of your portlets are RSS feeds, as Jetspeed was once designed but has since evolved into much more. In the example below of an RSS portlet, the key would be combined to create: http://www.mozilla.org/news.rdf|itemdisplayed-10. This isn't really very useful, since the items displayed shouldn't have any affect on the number of instances created of a portlet. Basing the cache on the URL makes a little more sense, since we control the number of portlets to be equal to the number of feeds in the system.


    <portlet-entry name="Mozilla" hidden="false"
                   type="ref" parent="RSS" application="false">
        <meta-info>
            <title>Mozilla</title>
        </meta-info>
         <classname>org.apache.jetspeed.portal.portlets.NewRSSPortlet</classname>
        <parameter name="itemdisplayed" value="10" hidden="false"
            cachedOnName="true" cachedOnValue="true"/>
        <url cachedOnURL="true">http://www.mozilla.org/news.rdf</url>
        <category group="Jetspeed">news.software.opensource</category>
    </portlet-entry>

The AbstractInstancePortlet has a different algorithm to create the uniqueness of a portlet:


        StringBuffer handle = new StringBuffer(256);
        handle.append(pc.getPageId());
        handle.append('/');
        handle.append(pc.getPortletId());
 
        return handle.toString(); 

New portlets are created for every portlet instance. A portlet instance is defined as each instance of a portlet referenced in a PSML file. Thus for each portlet defined on a PSML page, a new Java instance of the portlet is created and put in the cache with a unique handle of: PSML-PAGE-ID + PORTLET-ID. The important gain with this approach is that you can now have the same portlet twice on the page, and those two portlets will have their own private parameters.

This approach will have scalability issues. Think about the case where each user has his own page. Since we are creating new portlets for every user in the system with her own page, if there are thousands of simultaneous users, we have the same portlet instantiated thousands of time. It's a waste of memory. This approach was necessary in order to rectify a bug in Jetspeed's design: it shares all instance parameters across all instances. If you find this approach to be unsatisfactory, just override the getHandle method for your portlet and have its uniqueness based on the name of your portlet as shown in the commented out method in the tutorial example (its commented out in order for the tutorial example in 6.3 to work properly with multiple instances of the same portlet):


    public static Object getHandle(Object config)
    {
        PortletConfig pc = null;
 
        if (!(config instanceof PortletConfig))
        {
            return null;           
        }
        return pc.getName();
    }