Showing posts with label bestpractices. Show all posts
Showing posts with label bestpractices. Show all posts

Friday, November 16, 2007

How to scope Portlet data per Window

I have been often asked how it is possible to scope data in a Portlet for a specific window. One obvious way would be to generate some unique ID and store it in the Portlet preferences, however this means that we start to use the preferences of the Portlet as a database which is not advised by the specification. So it would be neat to be able to retrieve the window ID provided by the portal to the portlet container.



Since Portlet 2.0 it is possible to retrieve the window ID using the PortletRequest.getWindowID(). Still there is a way to have this window id value using the Portlet 1.0 specification, it may sound like an hack but it respects the specification. The trick is to use the fact that the Portlet session attributes are scoped using the window id value, for instance if the Portlet put the value foo in the Portlet session, it will be stored as javax.portlet.p.XYZ?foo where the XYZ is the window id.



Here is how to do it in practice:




public class WindowIDPortlet extends GenericPortlet
{

protected void doView(RenderRequest request, RenderResponse response) throws PortletException, PortletSecurityException, IOException
{
PortletSession session = request.getPortletSession();
WindowIDRetriever retriever = (WindowIDRetriever)session.getAttribute("retriever");
if (retriever == null)
{
retriever = new WindowIDRetriever();
session.setAttribute("retriever", retriever);
}
String windowID = retriever.getWindowID();

//
response.setContentType("text/html");
PrintWriter writer = response.getWriter();
writer.print("Window ID is equals to " + windowID);
}

public static class WindowIDRetriever implements HttpSessionBindingListener
{

/** . */
private String windowID;

public void valueBound(HttpSessionBindingEvent event)
{
String name = event.getName();
windowID = name.substring("javax.portlet.p.".length(), name.indexOf('?'));
}

public void valueUnbound(HttpSessionBindingEvent event)
{
}

public String getWindowID()
{
return windowID;
}
}
}




What you need to pay attention to is the fact that if you use the window id value as a key in a cache then it will be fine, however if you start to persist data using the window id as a key then as you will not be aware of the associated window destruction and you will not be able to remove associated data in the database when the portal destroys the window. In that case the best advice is probably to implement a purge mechanism that would remove the out dated entries in the database (which suppose that you associate with the records, the date at which it was inserted).

Tuesday, May 15, 2007

Portlet best practices #1

Disclaimer


I wanted to start to blog about portlet best practices based on the BOF I gave last year at JavaOne. It targets the JSR 168: Portlet Specification. However ideas should remain valid for the next versions of the spec although some ideas may be less interesting due to the enhancements of the spec. I will talk when it is possible about the JSR 286: Portlet Specification 2.0, i.e based on the published drafts.

I am also very open to any kind of discussions about what will be said here. In fact I would be glad to hear about your use cases and what you think is a best practice in your portlet world.

Portlet Best Practice #1


The first entry is about one of the main feature of portal which is personalization of content and delivering personalized content can occur at several levels.

Portlet preferences

Preferences allows a developer to store data that will personalize the behavior and the output of a portlet. My classic example of behavior is the city code that will fetch data from the favorite location of the user. For rendering my classic example is the maximum number of forums topics that a user wants to see displayed by the portlet.

Usually preferences are declared in the portlet deployment descriptor and then can be modified at runtime by the user. The personalization policy is defined by the portal, i.e the portal choses when preferences can be modified. A single personalization of the preferences of a portlet is called a portlet entity. A portlet entity can either be shared by many users or specific to a single user, again here it depends on the personalization policy of the portal.

Internationalization and localization

Internationalization (I18N) and localization (L10N) should not be underestimated when creating personalized content. It can be used of course to produce text in the user's language but it can also be used to format numbers, date and time in an appriopriate manner.

"I18N is the process of designing an application so that it can be adapted to various languages and regions without engineering changes to the software. L10N is performed by simply adding locale-specific components, such as translated text, data describing locale-specific behavior, fonts, and input methods" from the Sun Java Internationalization tutorial.

The Java platform defines the java.util.Locale object that identifies a combination of language and country. At runtime the PortletRequest provides a locale that is adapted to the current user. In JBoss Portal, an authenticated user is able to define a favorite locale, otherwise the locale provided by the web browser is used.

Locale sensitive objects accept a locale and change their behavior according to that locale, such as


  • java.text.NumberFormat is used to format and parse numbers

  • java.text.DateFormat is used to format and parse date objects

  • java.util.ResourceBundle is used to retrieve messages



I will probably explain in another post how to properly deal with resource bundles and locale because it would take too much long here.

User agent: browser, cell phone...

According to the nature of the device you are creating content for, you need to deliver content that the device can display. Okay I agree that it does not happen frequently in reality and usually a portlet will be tailored for one kind of device, if not the traditional web browser with the text/html content type.

The Portlet 2.0 specification adds support for Composite Capability/Pref Profiles (CC/PP) (based on the early draft):



User identity

It is possible at runtime to retrieve information about the current user as a java.util.Map object that contains the user profile. The semantic of the keys of the map is defined by the Platform for Privacy Preferences project.

First the portlet must define which attributes it will use in the deployment descriptor of the portlet application:


<portlet-app>
...
<user-attribute>
<description>First name</description>
<name>user.name.given</name>
</user-attribute>
<user-attribute>
<description>Last name</description>
<name>user.name.family</name>
</user-attribute>
...
</portlet-app>



Then at runtime it is possible to retrieve the declared attributes:


Map userInfo = (Map) request.getAttribute(PortletRequest.USER_INFO);
if (userInfo !=null)
{
String firstName = userInfo.get(“user.name.given”);
String lastName = userInfo.get(“user.name.family”);
}



Note that there are no guarantees that the returned values firstName and lastName are not null and the portlet developer must deal with those cases.

Conclusion


We have seen an overview of the different features provided by the portlet API that a developer can use to deliver content personalized for the user. It does not want to be an extensive course but rather a list of what could be done to achieve a good level of personalization. Of course you can chose to ignore them but if you want to make your application more attractive by providing a richer user experience then you should seriously take them into consideration.