 | Level: Introductory Nicholas Chase, President, Chase and Chase, Inc.
17 Oct 2003 In Part 21 of this series, Nick showed you how to add a custom attribute to users' information that represented their preferred topics. In this article, he shows you how to use that custom attribute as well as session information to enable users to choose between showing all topics and showing only their preferred topics.
To follow along with this article, you'll need to have a working installation of any 4.x version of WebSphere Portal Server, along with the ability to compile a portlet application, either manually or through the use of a tool such as WebSphere Studio Application Developer.
 |
Editor's update
The Web site MetroSphere.com -- the online technical community discussed in this article -- is no longer live. However, the information and screen captures regarding the installation of IBM WebSphere Portal are still accurate and relevant.
|
|
What we want to accomplish
MetroSphere is a community blogging application that provides information on numerous topics, but when a user logs in, he or she should have the opportunity to decide whether to see information on all of those topics or just those he or she has chosen as "favorites." In this article, I show you how to let users toggle between showing all topics or showing just their favorites. To do this, you'll need to take several steps:
- Save the "state" in the session so it's available not only to the
Topic List portlet, but also to any other portlet on the page.
- Retrieve the user's favorite topics, if applicable.
- Modify the topiclist JSP tag to accept this new parameter limiting the topics displayed.
Checking for a session
Before you can even think about saving information to a session, you need to determine whether the session exists or not. By default, an anonymous WebSphere Portal user never has a session, in order to save resources. Unfortunately, this default configuration isn't ideal if the user is going to click any links; the first time the user clicks a link, the action isn't processed. Fortunately, you can change the default configuration. In Part 20 of this series, "Create portlets: Implement and deploy the MetroSphere.com blogging system"
, I showed you how to make changes to the Portal configuration files that specified, among other things, that public users should have sessions.
Still, the first time an anonymous user comes to the site, the session doesn't yet exist. You can see this if you test for it in the TopicList portlet:
Listing 1. Testing for a session
package com.metrosphere.blog;
import org.apache.jetspeed.portlet.*;
import org.apache.jetspeed.portlets.*;
public class TopicList extends PortletAdapter {
public void init(PortletConfig portletConfig)
throws UnavailableException
{
super.init( portletConfig );
}
protected static final String viewJsp = "/WEB-INF/html/topiclist.jsp";
public void doView (PortletRequest request, PortletResponse response)
throws PortletException, java.io.IOException
{
PortletSession session = request.getPortletSession(false);
if (session == null){
response.getWriter().print("No session ");
} else {
response.getWriter().print("Session ");
}
PortletContext context = getPortletConfig().getContext();
context.include(viewJsp, request, response);
}
} |
By default, when we request the current session using getPortletSession(), the portal will create one if one doesn't exist. You don't want to use more resources than necessary, however, so you can specify (using false) that you don't want to create one if it doesn't exist. If you update the portlet application and sign out of the portal, you can see that there's no session in place. (Make sure that anonymous users have VIEW privileges on the page and portlet.)
Figure 1. No session
If you refresh the page, you will see that a session is, indeed, created.
Setting the state
Ultimately, you'll have four "states" to deal with:
- The user is not logged in and has no session.
- The user is not logged in but does have a session.
- The user is logged in, has a session, and is currently seeing all topics.
- The user is logged in, has a session, and is currently seeing only preferred topics.
Now, it might be tempting to keep track of the current state through the use of a member variable, and maybe even get() and set() methods, but we couldn't do that because of the architecture of the portal. Each portlet is instantiated only once, so any member variables effectively become class variables; every user will see the same value.
To get around this problem, you can set an attribute on the user's session, if one exists. (If there is no session, you know you're dealing with state number 1.) In this article, I'll show you how to use simple String values for the state, but in later parts of this series, I'll look at creating state objects that perform various functions.
What you want to do is set the state and create a link underneath the topic list that enables the user to change from one state to another. You can do that by creating an action that takes into account the current state and then use the actionPerformed() method to switch between them:
Listing 2. Creating the toggle link
package com.metrosphere.blog;
import org.apache.jetspeed.portlet.*;
import org.apache.jetspeed.portlets.*;
import org.apache.jetspeed.portlet.event.*;
public class TopicList extends PortletAdapter implements ActionListener {
public void init(PortletConfig portletConfig)
throws UnavailableException
{
super.init( portletConfig );
}
protected static final String viewJsp = "/WEB-INF/html/topiclist.jsp";
private void setPreftopics(PortletRequest request, String topicState){
PortletSession session = (PortletSession)request.getSession(false);
if (topicState.equals("showpref")) {
session.setAttribute("topicstate", "showpref");
} else {
session.setAttribute("topicstate", "showall");
}
}
public void doView (PortletRequest request, PortletResponse response)
throws PortletException, java.io.IOException
{
PortletSession session = request.getPortletSession(false);
PortletContext context = getPortletConfig().getContext();
context.include(viewJsp, request, response);
if (session != null){
PortletURI showTopicsURI = response.createURI();
String actionName = "";
String linktext = "";
if (session.getAttribute("topicstate") == null) {
actionName ="showpref";
linktext = "Show my topics";
} else {
if (session.getAttribute("topicstate").equals("showall")){
actionName = "showpref";
linktext = "Show my topics";
} else {
actionName = "showall";
linktext = "Show all topics";
}
}
if (request.getUser() != null){
DefaultPortletAction topicsAction =
new DefaultPortletAction(actionName);
showTopicsURI.addAction(topicsAction);
response.getWriter().print("<a href=\""+
showTopicsURI.toString()+"\">"+
linktext+"</a>");
}
}
}
public void actionPerformed (ActionEvent event) {
PortletAction portletAction = event.getAction();
PortletRequest request = event.getRequest();
if (portletAction instanceof DefaultPortletAction)
{
DefaultPortletAction action = (DefaultPortletAction)portletAction;
if (action.getName().equals("showpref")){
setPreftopics(request, "showpref");
} else {
setPreftopics(request, "showall");
}
}
}
} |
Let's take this one step at a time. If there's no session, you don't want to do anything; the user can't choose to see his or her own topics. If there is a session, you can retrieve the current value of the topicstate attribute. If there is none, assume that all topics are showing and give the user an opportunity to show only his or her own topics. If there is a value, create variables that will help you create a link pointing to the opposite state.
As far as the link itself, you only want to create and display it if the user is logged in, so you'll check the request for a current user. (I'll deal more with the user next, when I look at user attributes.) If the user is logged in, you'll create a URI with an action name that indicates what you want to do. If the current topicstate is showall, you want an action of showpref, and vice versa. Finally, display the link.
To process the link, implement the ActionListener interface and consequently, the actionPerformed() method. This method simply checks for the name of the action and calls setPreftopics() to set the new state. You could just as easily have set the new state here, in actionPerformed(), but in the next step you'll be setting user attributes as well, so you might as well pull it out into a separate method now.
The setPreftopics() method simply sets an attribute on the session according to the topicState argument. Note that you're not checking for a session here, because this method should never be called unless one has already been established, but in general it's a good idea to trap for such a situation.
When you first call the portlet, the Show my topics link appears. If you click it, it switches to the Show all topics link. An anonymous user won't see any link at all.
Now at this point, the topicstate attribute has been set on the session itself, so it's accessible from any portlet on any page, and you can use it (and any other attribute we set) to determine, say, what blog entries to display in the ShowBlog portlet. It's important to note, however, that there is no guarantee as to the order in which portlets are called when a page is generated; you can't say for sure that this portlet will be executed before the ShowBlog portlet. For subsequent calls, it won't matter; the attribute will already be set. For this request, however, it's an issue. In future parts of this series, I'll look at using messaging between portlets to solve the problem.
User attributes
In Part 21 of this series, "Create custom user attributes," I showed you how to create an attribute, favoriteTopics, that was attached to each user's information as he or she registered with MetroSphere. Now you're going to make use of that information. It's been stored as an attribute of the User object, just as you stored the topicstate as an attribute of the session. You can retrieve it using the getAttribute() method:
Listing 3. Getting a custom user attribute
...
private void setPreftopics(PortletRequest request, String topicState){
PortletSession session = (PortletSession)request.getSession(false);
if (topicState.equals("showpref")) {
session.setAttribute("showtopics",
(String)request.getUser().getAttribute("favoriteTopics"));
session.setAttribute("topicstate", "showpref");
} else {
session.setAttribute("showtopics", "-1");
session.setAttribute("topicstate", "showall");
}
}
public void doView (PortletRequest request, PortletResponse response)
throws PortletException, java.io.IOException
{
PortletSession session = request.getPortletSession(false);
response.getWriter().print("Preferred topics are: "+
getPreftopics(request)+" ");
PortletContext context = getPortletConfig().getContext();
context.include(viewJsp, request, response);
if (session != null){
... |
When you set the preferred topics, you created them as a comma-delimited list of topidId values.
You can see them now if you update the application:
Figure 2. No session
Altering the topiclist tag
Next, you need to adapt the topiclist tag, which (if you've been following along) you created as a part of the custom tag
library in Part 12 of this series ("Hands-on intro to JSP technology"), to accept the list of topics and work with them accordingly. The JSP page will need a new
String bean, which you'll use as an attribute on the tag itself:
Listing 4. Adding a new bean
<%@ taglib uri="/tld/blogtaglib.tld" prefix="blogutil" %>
<jsp:useBean id="prefTopics" type="java.lang.String" scope="request" />
<blogutil:topiclist selected="-1" preftopics="<%= prefTopics %>">
<b><jsp:getProperty name="topicelement" property="topicName" /></b><br />
</blogutil:topiclist> |
In order for the tag to accept this new attribute, however, you need to make note of it in the blogtaglib.tld definition file:
Listing 5. Adding a new attribute
...
<tag>
<name>topiclist</name>
<tagclass>com.metrosphere.util.TopicListTag</tagclass>
<attribute>
<name>selected</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>preftopics</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
... |
You can simply send the new information to the JSP page from the portlet, just as you would set any other attribute:
Listing 6. Adding a new attribute
...
}
private String getPreftopics(PortletRequest request){
PortletSession session = (PortletSession)request.getSession(false);
if (session == null){
return "-1";
} else {
String showtopics = (String)session.getAttribute("showtopics");
if (showtopics == null){
return "-1";
} else {
return showtopics;
}
}
}
public void doView (PortletRequest request, PortletResponse response)
throws PortletException, java.io.IOException
{
PortletSession session = request.getPortletSession(false);
request.setAttribute("prefTopics", getPreftopics(request));
PortletContext context = getPortletConfig().getContext();
context.include(viewJsp, request, response);
... |
You could retrieve the value directly, but using the getPreftopics() method lets you trap for nonexistent sessions and other problems without cluttering up the main doView() method.
Altering the tag library class
The last step is to make changes to the class behind the topiclist tag so that it adjusts for the new information. By design, a preftopics value of null or -1 means that all topics should be displayed.
Listing 7. Altering the TopicListTag class
package com.metrosphere.util;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.BodyTagSupport;
import javax.servlet.jsp.tagext.TagData;
import org.apache.jetspeed.portlet.*;
import org.apache.jetspeed.portlets.*;
import java.sql.*;
public class TopicListTag extends BodyTagSupport {
public TopicListTag() {
super();
}
private int selected;
public void setSelected(int newSelected) {
selected = newSelected;
}
public int getSelected() {
return selected;
}
private String preftopics = " 1=1";
public void setPreftopics(String newPreftopics) {
if (newPreftopics != "-1"){
// preftopics = "topicid = 3";
preftopics = "topicid in ("+newPreftopics+")";
}
}
public String getPreftopics() {
return preftopics;
}
Connection con = BlogConnection.getConnection();
ResultSet result = null;
public int doStartTag() {
try {
Statement stmt = con.createStatement();
result = stmt.executeQuery("select * from blogsystem.topics "
+"where topicid > 0 and "
+preftopics+" order by topicid");
if (result.next()) {
TopicBean topic = new TopicBean();
topic.setTopicId(result.getInt("topicid"));
topic.setTopicName(result.getString("topicname"));
if (selected == result.getInt("topicid")){
topic.setSelected(" selected=\"selected\" ");
}
pageContext.setAttribute("topicelement", topic);
return EVAL_BODY_TAG;
} else {
return SKIP_BODY;
}
} catch (Exception e) {
e.printStackTrace();
return SKIP_BODY;
}
}
public int doAfterBody() throws JspException {
try {
try {
bodyContent.writeOut(bodyContent.getEnclosingWriter());
bodyContent.clear();
} catch (java.io.IOException e) {
e.printStackTrace();
}
if (result.next()) {
TopicBean topic = new TopicBean();
topic.setTopicId(result.getInt("topicid"));
topic.setTopicName(result.getString("topicname"));
if (selected == result.getInt("topicid")){
topic.setSelected(" selected=\"selected\" ");
}
pageContext.setAttribute("topicelement", topic);
return EVAL_BODY_TAG;
} else {
return SKIP_BODY;
}
} catch (SQLException sqle) {
sqle.printStackTrace();
return SKIP_BODY;
}
}
public int doEndTag() {
try{
result.close();
con.close();
} catch (Exception e) {
e.printStackTrace();
}
return EVAL_PAGE;
}
} |
Now the topiclist tag will always display the appropriate topics. To see it in action, update the portlet application and click the link underneath the topic list to switch between states.
Figure 3. No session
Summary
To enable MetroSphere users to narrow content to only their favorite topics, we have used session variables and custom user attributes to record whether the user is currently viewing all topics or just their preferred topics, and we've sent those preferred topics to the topiclist tag. We also had to adjust the code behind the topiclist tag to account for the new parameter that represents the topics to be displayed. This article showed you how.
Resources
About the author  | 
|  | Nicholas Chase, a Studio B author, has been involved in Web site development for companies such as Lucent Technologies, Sun Microsystems, Oracle, and the Tampa Bay Buccaneers. Nick has been a high school physics teacher, a low-level radioactive waste facility manager, an online science fiction magazine editor, a multimedia engineer, and an Oracle instructor. More recently, he was the Chief Technology Officer of Site Dynamics Interactive Communications in Clearwater, Florida, and is the author of four books on Web development, including XML Primer Plus (Sams). |
Rate this page
|  |