|
|
Inheritance Blocking vs. Propogation Blocking in WebSphere Portal
Please link to our site, support our site, and remember: Happy WebSphere!
Please support our site, link to us, buy some books, and remember:
Happy Java!
|
|
|
|
|
|
|
Any preliminary look at the portlet API will demonstrate a major
similarity between portlets and servlets. Both portlets and servlets
handle the request-response cycle, both are Java centric components, and
both have access to the J2EE runtime environment. But there are some
major differences between portlets and servlets as well. One of the most
significant ways portlets are different from regular servlets is their
support for various modes, namely view, edit and help. When it comes to
portlet modes, there is no applicable analogy to typical servlet and JSP
programming. This chapter will look at the various modes available to
the portlets you create, and how a developer can programmatically take
advantage of these modes. Breaking Away from a Portlet as a Servlet
Mentality Handling a request-response cycle is the most fundamental
aspect of portlet programming, but inspecting a request and sending out
a response to the client through a portlet is by no means revolutionary.
After all, request-response programming is exactly what we do in a Java
servlet. Let?s compare a Portlet to a Servlet: instead of a doView
method, a Java Servlet has a doPost or a doGet method. Instead of being
passed a PortletRequest or a PortletResponse object, a Java servlet is
given an HttpServletRequest and HttpServletResponse object. In many
ways, handling the request-response cycle of a portlet is very similar
to handling the request-response cycle of a Servlet. In fact, one of the
great things about portlets is the fact that they leverage our existing
knowledge of the Servlet and JSP APIs. But what makes a portlet so
incredibly sexy is all of the features and services the Portlet API
affords us, over and above that of the Servlet API. The most fundamental
difference between a Portlet and a Servlet is the various modes in which
a Portlet can participate. A portlet has three standard, and any number
of custom, implementable modes, namely: F The View Mode F The Edit Mode
F The Help Mode The most common, and only required mode of a Portlet, is
the view mode. When a Portlet is displayed on a page, it is typically
displaying its view mode. In fact, the specification requires every
portlet to have a doView method so that the portlet can render itself on
a portal page. Optionally, a portlet can provide an implementation of
any of the other standard modes, with perhaps the most useful mode being
edit, and the most helpful, being help. J Figure 7-1The wrench, pencil,
and question mark represent the config, edit and help modes of a
portlet.Note that config is a custom mode. Basic Portlet Mode
Implementation At the most basic level, to support the standard portlet
modes, a portlet must implement a doEdit, doHelp, and doView method.
Figure 7-2 ----------------------------------------------- package
com.examscam.portlet; import java.io.*;import javax.portlet.*; public
class MultiModePortlet extends GenericPortlet { protected void
doView(RenderRequest request, RenderResponse response) throws
PortletException, IOException {
response.setContentType("text/html");
response.getWriter().print("This is the View mode."); }
protected void doEdit(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
response.setContentType("text/html");
response.getWriter().print("This is the Edit mode."); }
protected void doHelp(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
response.setContentType("text/html");
response.getWriter().print("This is the help mode."); } }
Along with an implementation of the appropriate do <Mode> method,
the deployment descriptor of the portlet must also indicate all of the
modes the portlet supports. """ <portlet>
<portlet-name>MultiModePortlet</portlet-name>
<display-name>MultiModePortlet</display-name>
<portlet-class> com.examscam.portlet.MultiModePortlet
</portlet-class> <supports>
<mime-type>text/html</mime-type>
<portlet-mode>view</portlet-mode>
<portlet-mode>edit</portlet-mode>
<portlet-mode>help</portlet-mode> </supports>
<portlet-info>
<title>MultiModePortlet</title></portlet-info>
</portlet> """ Portlet Mode Visualization When the
MultiModePortlet from Figure 7-2 is rendered by the portal, the skin of
the view mode displays edit, help, minimize and maximize icons. Clicking
on the question mark generates a pop-up window that displays the
contents of the doView method. Clicking on the pencil takes a user into
the edit mode of the portlet. The edit mode conveniently contains a
caret icon that can return a user to the view mode. Help Mode and the
doHelp Method The easiest portlet mode to implement has got to be the
help mode. The help mode is designed to simply display to the user
information about how to use the portlet. The help mode for the
NumberGuesserPortlet will simply inform the user that they need to keep
guessing a number until they get it right. Implementing the help mode
simply requires the implementation of a doHelp method in your code and
an update to the portlet.xml file to inform the portlet container that
your Portlet supports the help mode. User Customization and the Edit
Mode One of the main benefits of a portlet is the fact that it can be
customized by a user. If you have gone to any of the mega portals like
yahoo.com or excite.com, or have even installed the WebSphere Portal
Server and played around with some of the Pinnacore portlets, you have
inevitably run into a number of portlets you can customize by providing
the portlet any number of personalized parameters. The ubiquitous
weather portlet, the one that tells you the weather for three or four
major international cities the first time you see it, but also gives you
the ability to customize the portlet by telling it your ZIP code or the
city in which you live, is a prime example of a customizable portlet.
The weather portlet provides an edit mode that asks the user their zip
code or city of interest. The portlet stores that information
permanently, and then the next time the user views the weather portlet,
it will additionally display the weather for the specified city or zip
code of interest. The edit mode provides the user an opportunity to
provide personal preferences to a portlet. That information is then used
in the view mode to provide customized content delivery to the user. An
edit mode provides a user the opportunity to customize a portlet. To
handle these user requests for customizations, the portlet API matches
the edit mode of a portlet, to the doEdit method of a developer?s
portlet code. Figure 7-3 To implement the help and edit modes, a portlet
needs a doHelp and doEdit method. """protected void
doHelp(RenderRequest request, RenderResponse response) throws
PortletException, IOException {
response.setContentType("text/html");
response.getWriter().print("Just guess a number! ");
}"""protected void doEdit(RenderRequest request,
RenderResponse response) throws PortletException, IOException { String
url = "/numberguesseredit.jsp" ;
getPortletContext().getRequestDispatcher(url).include(request,
response);}""" Edit Mode and PortletPreferences Any
personal preferences provided to a portlet through the edit mode are
stored permanently. The next time the user visits that portal page, this
personalized information is retrieved for that user, and that user
specific information can be utilized to provide a customized view for
the user. As you could imagine, storing all of these user preferences
persistently, and tying that data to a specific user and a specific
portlet on a certain portal page is quite a bit of work. Fortunately,
implementing this functionality is not shouldered by the portlet
developer. Instead, the portlet API provides a very special component
called the PortletPreferences object. While in the action processing
phase of the edit mode of a portlet, a developer is allowed to stuff any
number of Strings into the PortletPreferences object, using the very
straightforward setValue(name, value) method. PortletPreferences prefs =
request.getPreferences(); if
(request.getParameter("guessedit") != null) { String newLimit
= request.getParameter("newlimit");
prefs.setValue("upperlimit", newLimit); prefs.store(); } Any
mode can access data stored in the PortletPreferences object, but only
during the action processing phase of the edit mode can you actually
change, add or update information stored as PortletPreferences. Store,
Store, Store! When a developer stuffs a Java object into a portlet?s
PortletData object, and subsequently calls the preferences.store()
method, the data that was stuffed into the preferences object is stored
persistently. A real rookie mistake is forgetting to call the store()
method after shoving Strings into the PortletPreferences object. There
is no design time type-checking to ensure the store method is called
after working with the PortletPreferences object, so you really have to
be diligent to ensure your preferences are stored permanently.
Initialization of PortletPreferences: portlet.xml One of the challenges
in working with the edit mode is initializing default values for
PortletPreferences, or as it was known in the Jetspeed API, PortletData.
With JSR-168, each portlet can configure any number of initial values by
specifying them in the portlet-preferences section of the deployment
descriptor. Furthermore, preferences can be multivalued, and stored as
an array of Strings, as opposed to just single value Strings. Finally,
PortletPreferences can be set as read-only. This essentially ensures
that a given PortletPrefernce will be common to all users of the
portlet. <portlet-preferences> <preference>
<name>upperlimit</name><value>10</value>
<read-only>false</read-only> </preference>
</portlet-preferences> It should also be noted that in order for a
portlet to support the edit mode, the portlet.xml file must have the
appropriate entry for the portlet. Manipulating PortletPreferences is
pretty much a moot endeavor if you haven?t specified in the deployment
descriptor that the portlet supports the edit mode. <supports>
<mime-type>text/html</mime-type>
<portlet-mode>view</portlet-mode>
<portlet-mode>edit</portlet-mode>
<portlet-mode>help</portlet-mode> </supports>
PortletPreferences and the NumberGuesser So, how can we take our simple,
NumberGuesserPortlet and complicate it to the n?th degree by adding user
customization through PortletPreferences? Well, by default, the user
guesses a number between 1 and 10. But perhaps we want the user to
customize the experience by choosing a number between 1 and 100, or 1
and 1000? We could easily make the range customizable through a
PortletPreference. Figure 7-6xxx Without preferences, our number
guessing game always uses 10 as the upper limit. We could store a String
value named upperlimit in the PortletPreferences object, and use the
portlet.xml file to give this value a default of 10.
<portlet-preferences> <preference>
<name>upperlimit</name><value>10</value>
<read-only>false</read-only> </preference>
</portlet-preferences> When the user starts the number guessing
game, we could use the upperlimit value, stored in the
PortletPreferences, to generate the magic number. /*obtain the
PortlePreferences object from the request*/ PortletPreferences prefs =
request.getPreferences(); /*pull the upperlimit String from the
preference*/ String upperLimit = prefs.getValue("upperlimit",
null); /*use the upperLimit to generate the new random number to be
guessed*/ long number
=(System.currentTimeMillis()%Integer.parseInt(upperLimit))+1; /*put the
new number in the session*/
session.setAttribute("magicnumber", new Long(number)); Figure
7-5 Updating the processAction method. public void processAction
(ActionRequest request, ActionResponse response) throws
PortletException, java.io.IOException {/* get the preferences and
session from the request object */ PortletPreferences prefs =
request.getPreferences();PortletSession session =
request.getPortletSession();/* if we are in the edit mode, get the new
range limit */ if (request.getParameter("guessedit") != null)
{ String newLimit = request.getParameter("newlimit");
prefs.setValue("upperlimit", newLimit); prefs.store();}/*
Obtain application data from the session. */Long magicNumber = (Long)
session.getAttribute("magicnumber");Long guess = new
Long(request.getParameter("number"));String guesses = (String)
session.getAttribute("guesses");String message = "Guess
Higher!!!";/* If a number was guessed, see if it is correct */if
(request.getParameter("guesssubmit") != null) { /* if the
magicNumber is null, this is their first guess. */ if (magicNumber ==
null) { String upperLimit = prefs.getValue("upperlimit",
null); /* use the range limit from preferences to calc the number */
long number = (System.currentTimeMillis()%Integer.parseInt(upperLimit))
+ 1; session.setAttribute("magicnumber", new Long(number));
guesses = "1"; }else { guesses = "" +
(Integer.parseInt(guesses) + 1); } if (guess.intValue() >
magicNumber.intValue()) { message = "Guess Lower."; } if
(guess.intValue() == magicNumber.intValue()) { message = magicNumber +
" is correct. # of guesses: " + guesses;
session.setAttribute("guesses","0");
session.removeAttribute("magicnumber"); }}/* appropriately set
the message and guess number in the session
*/session.setAttribute("message",
message);session.setAttribute("guesses", guesses);}
Customization Through the Edit Mode Figure 7-7 The user sets an custom
upper limit through the edit mode. While default values will be provided
through the portlet.xml file, the user can change the range by using the
edit mode of the portlet. The edit mode could be used to update the
preference, allowing the user to set a range larger than 1 to 10. /*if
the user clicked the button named guessedit from the edit page */ if
(request.getParameter("guessedit") != null) { String newLimit
= request.getParameter("newlimit");
prefs.setValue("upperlimit", newLimit); prefs.store(); } After
updating their preferences through the edit mode, when the user returns
to the view mode, all numbers will be generated based upon the new
range, as set in PortletPreferences. An additional code snippet in the
jsp allows the new upper range limit to be displayed to the user:
<%=renderRequest.getPreferences() .getValue("upperlimit",
null) %> Figure 7-8 Notice how the user has set the upper limit to
999, as opposed to the default of 10. Figure 7-9 Code used for the View
JSP in this example. Notice how the message is coming from the
PortletSession. <%@ page
contentType="text/html"%><%@taglib
uri="http://java.sun.com/portlet"
prefix="portlet"%><portlet:defineObjects
/><%=renderRequest.getPortletSession().getAttribute("message")%><FORM
action="<portlet:actionURL />">Pick a number between
1 and
<%=renderRequest.getPreferences().getValue("upperlimit",
null) %> <INPUT name="number" size="10"
type="text" /> <INPUT name="guesssubmit"
value="Guess!!" type="submit"
/></FORM>Number of
guesses:<%=renderRequest.getPortletSession().getAttribute("guesses")%>
Fig 7-10 The JSP used for the Edit mode, numberguesseredit.jsp <%@
page contentType="text/html"%><%@taglib
uri="http://java.sun.com/portlet"
prefix="portlet"%><portlet:defineObjects /> <FORM
action="<portlet:actionURL />">Choose the
range.<BR>The number will be between 1 and...<INPUT
name="newlimit" size="10" type="text"
/> <INPUT name="guessedit" value="Submit"
type="submit" /></FORM> Figure 7-11 Full portlet.xml
file. Notice the support for multiple modes, and the default value given
for the upperlimit preference. <portlet>
<portlet-name>NumberGuesser</portlet-name>
<display-name>NumberGuesser portlet</display-name>
<portlet-class> com.examscam.portlet.NumberGuesserPortlet
</portlet-class> <supports>
<mime-type>text/html</mime-type>
<portlet-mode>view</portlet-mode>
<portlet-mode>edit</portlet-mode>
<portlet-mode>help</portlet-mode> </supports>
<portlet-info> <title>NumberGuesser portlet</title>
</portlet-info> <portlet-preferences> <preference>
<name>upperlimit</name> <value>10</value>
<read-only>false</read-only> </preference>
</portlet-preferences></portlet> Fig. 7-12 The doEdit method
simply points to the edit.jsp. protected void doEdit (RenderRequest
request, RenderResponse response) throws PortletException, IOException {
String url = "/numberguesseredit.jsp" ;
this.getPortletContext().getRequestDispatcher(url) .include(request,
response);} Figure 7-11 NumberGuesser (Sorry it is so small, but I
wanted it on one page) package com.examscam.portlet;import
java.io.*;import javax.portlet.*;public class NumberGuesserPortlet
extends GenericPortlet { protected void doView(RenderRequest request,
RenderResponse response) throws PortletException, IOException {
PortletContext context = this.getPortletContext(); PortletSession
session = request.getPortletSession(); if
(session.getAttribute("message") == null) {
session.setAttribute("guesses", "0");
session.setAttribute("message", "Guess the
number!"); } String url = "/numberguesser.jsp";
context.getRequestDispatcher(url).include(request, response); }
protected void doHelp(RenderRequest request, RenderResponse
response)throws PortletException, IOException {
response.setContentType("text/html");
response.getWriter().print("You should be able to figure this
out!!!"); } protected void doEdit(RenderRequest request,
RenderResponse response) throws PortletException, IOException { String
url = "/numberguesseredit.jsp";
getPortletContext().getRequestDispatcher(url).include(request,response);
} public void processAction(ActionRequest request, ActionResponse
response) throws PortletException, java.io.IOException { PortletSession
session = request.getPortletSession(); /* obtain the PortlePreferences
object from the request */ PortletPreferences prefs =
request.getPreferences(); if
(request.getParameter("guessedit") != null) { String newLimit
= request.getParameter("newlimit");
prefs.setValue("upperlimit", newLimit); prefs.store(); } if
(request.getParameter("guesssubmit") != null) { if
(session.getAttribute("magicnumber") == null) { /* pull the
upperlimit String from the preference */ String upperLimit =
prefs.getValue("upperlimit", "3"); /* use the
upperLimit to generate the new random number to be guessed */ int
magicNumber = (int)((System.currentTimeMillis() %
Integer.parseInt(upperLimit)) + 1); /* put the new number in the session
*/ session.setAttribute("magicnumber", new
Integer(magicNumber)); session.setAttribute("guesses",
"0"); } Integer magicNumber =
(Integer)session.getAttribute("magicnumber"); String guesses =
(String) session.getAttribute("guesses"); guesses =
"" + (Integer.parseInt(guesses) + 1);
session.setAttribute("guesses", guesses);
session.setAttribute("message", "Guess higher!");
Integer guess = new Integer(request.getParameter("number"));
if (guess.intValue() > magicNumber.intValue()) {
session.setAttribute("message", "Guess lower!"); }
if (guess.intValue() == magicNumber.intValue()) { String message =
magicNumber + " is correct. Play again!";
session.setAttribute("message", message);
session.removeAttribute("magicnumber"); } } }} Figure 7-9 The
JSP used for the View mode, numberguesser.jsp <%@ page
contentType="text/html"%><%@ taglib
uri="http://java.sun.com/portlet"
prefix="portlet"%><portlet:defineObjects
/><%=renderRequest.getPortletSession().getAttribute("message")%><FORM
action="<portlet:actionURL />">I'm thinking of a
number between 1 and <%=renderRequest.getPreferences()
.getValue("upperlimit", null) %><INPUT
name="number" size="10" type="text" />
<INPUT name="guesssubmit" value="Guess!!"
type="submit" /></FORM>Number of
guesses:<%=renderRequest.getPortletSession().getAttribute("guesses")%>
Fig 7-10 The JSP used for the Edit mode, numberguesseredit.jsp <%@
page contentType="text/html"%><%@taglib
uri="http://java.sun.com/portlet"
prefix="portlet"%><portlet:defineObjects /> <FORM
action="<portlet:actionURL />">Choose the
range.<BR>The number will be between 1 and...<INPUT
name="newlimit" size="10" type="text"
/> <INPUT name="guessedit" value="Submit"
type="submit" /></FORM>Upper
Limit:<%=renderRequest.getPreferences().getValue("upperlimit",
null)%> PortletPreferences and the PreferencesValidator Part of the
JSR-168 API is a convenient interface called the PreferencesValidator.
PortetPreferences provide the end user the ability to customize their
user experience, but as with any type of input taken from a user, there
is the possibility that data provided to the server won?t be valid for a
particular scenario. To provide some level of control over how users
configure a particular preference, a PreferencesValidator can be coded,
and subsequently configured in the portlet.xml file. The
PreferencesValidator has only one method that needs to be overridden:
the validate(PortletPreferences prefs) method. The job of the developer
is to code a custom class that implements this interface, and then
configure the portlet.xml file, thus making the portal server aware of
the validator. The contract between the portlet.xml file and the portal
server ensures that before any data is saved into PortletPreferences,
the validate method of the PreferencesValidator will be invoked. If
validation fails, the validate method must throw the special
ValidatorException, which is part of the JSR-168 Portlet API. To ensure
that only numeric data is stored as an upper limit of the number
guessing game, a class that implements the PreferencesValidator was
coded, and configured in the portlet.xml file. PreferencesValidator and
ValidatorException NumberGuesserValidator As you can see, the
NumberGuesserValidator is relatively straight forward. The preference of
interest, the upperlimit, is pulled from the preferences object passed
into the validate method, and if the upperlimit field does not pass
muster, then a ValidatorException is thrown. package
com.examscam.portlet; import javax.portlet.*; public class
NumberGuesserValidator implements PreferencesValidator { public void
validate(PortletPreferences preferences) throws ValidatorException { try
{ String upperLimit = preferences.getValue("upperlimit",
null); if (upperLimit != null) { Integer.parseInt(upperLimit); } } catch
(NumberFormatException nfe) { throw new
ValidatorException("upperlimit", nfe, null); } } } Catching
PortletPreference Exceptions If you code a PreferencesValidator, it
makes sense that you would then catch the corresponding
ValidatorException in your portlet code; specifically, when you add new
data to your PortletPreferences. Notice that there are actually three
exceptions being handled in the following code snippet:
""" if (request.getParameter("guessedit") !=
null) { String newLimit = request.getParameter("newlimit");
try { prefs.setValue("upperlimit", newLimit); prefs.store(); }
catch (ValidatorException e) {e.printStackTrace(); } catch
(ReadOnlyException e) {e.printStackTrace(); } catch (IOException e)
{e.printStackTrace(); } } """ PortletPreference Related
Exceptions PortletPreferences are typically stored in a centralized
database, where the portal server can easily retrieve user
customizations. Because the act of storing data to a database, or even
just sending data across a network, can be problematic, the store method
of the PortletPreferences object can potentially throw the
java.io.IOException. More curiously though, the store method can throw
the javax.portlet.ReadOnlyException. In the portlet.xml file,
PortletPreferences can be configured, and default values can be assigned
to them. These default values will be used unless a user goes to the
edit mode and further customizes their preferences. However, in the
portlet.xml file, a preference can be marked as read-only. If a
preference is marked as read-only in the portlet.xml file, and an
attempt is made to edit that preference at runtime, a ReadOnlyException
is thrown. Finally, we should mention again that the PortletPreferences
object can potentially call the ValidatorException. If a
PreferencesValidator is configured in the portlet.xml file, and that
validator throws an exception when a particular field is placed into the
PortletPreferences, a ValidatorException will be triggered. Class
Diagram of the ReadOnlyException PreferencesValidator and the
portlet.xml File <?xml version="1.0"
encoding="UTF-8"?> <portlet-app
xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd"
version="1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd
http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd"
id="com.examscam.portlet.NumberGuesserPortlet.420453eee0">
<portlet> <portlet-name>NumberGuesser</portlet-name>
<display-name>NumberGuesser portlet</display-name>
<portlet-class> com.examscam.portlet.NumberGuesserPortlet
</portlet-class> <supports>
<mime-type>text/html</mime-type>
<portlet-mode>view</portlet-mode>
<portlet-mode>edit</portlet-mode>
<portlet-mode>help</portlet-mode> </supports>
<supported-locale>en</supported-locale> <portlet-info>
<title>NumberGuesser portlet</title> </portlet-info>
<portlet-preferences> <preference>
<name>upperlimit</name> <value>10</value>
<!--Setting read-only to true will trigger a ReadOnlyException if the
preference is updated at runtime.-->
<read-only>false</read-only> </preference> <!-- If
validation fails, a preferences-validator will throw a
ValidatorException.--> <preferences-validator>
com.examscam.portlet.NumberGuesserValidator
</preferences-validator> </portlet-preferences>
</portlet> </portlet-app> Question xxx-1 The decision as to
which of the various do methods to invoke, such as doEdit or doHelp,
occurs in which method? ˇ a) initˇ b) destroyˇ c) actionProcessˇ d)
doDispatch Question xxx-2 PortletPrefernces are not being saved. What
are two possibilities as to why this might be happening ¨ a) the store()
method is not being invoked¨ b) the save() method is not being invoked¨
c) the PortletPreferences are being configured in the doView method¨ d)
the PortletPreferences are being manipulated in the processAction method
Answer xxx-3 What are the standard, JSR-168, portlet modes? ¨ a) edit¨
b) view¨ c) help ¨ d) config Question xxx-4 To ensure a
PortletPreference has a default value, even if the user hasn?t
customized their portlet, the best plan of action is to: ¨ a) code a
default value in the web.xml file¨ b) code a default value in the
portlet.xml file¨ c) provide a default value as the second parameter of
the PortletPreferences getValue() method¨ d) accept a null value as a
valid PortletPreference Question 6-5 Which of the following checked
exceptions could be thrown by a call to the PortletPreferences store
method? ¨ a) ReadOnlyException¨ b) PortletException¨ c)
ValidatorException¨ d) IOException Answer xxx-9 Minimize, Maximize and
Normal are all valid: ˇ a) window statesˇ b) window modesˇ c) Portlet
statesˇ d) Portlet modes Answer 7xxx-7 Where is a PortletPreference
defined as being read only. ˇ a) programmatically through the
PortletPreferences objectˇ b) by adding a read-only parameter to the
portlet.xml fileˇ c) by adding a read-only parameter to the web.xml
fileˇ d) PortletPreferences are always editable Answer xxx-1 The
decision as to which of the various do methods to invoke, such as doEdit
or doHelp, occurs in which method? ˇ a) initˇ b) destroyˇ c)
actionProcessˇ d) doDispatch Option d) is correct.The init method is
called when a portlet is first loaded, and destroy is called when the
portlet is unloaded, but neither of those lifecycle methods have
anything to do with the render phase of a portlet. Similarly, the
actionProcess can help decide which mode or state in which a portlet
will appear, but it does not directly call doEdit or doView. It is the
doDispatch method that is responsible for figuring out which of the
do<mode> methods should be invoked for portlet rendering. Answer
xxx-2 PortletPrefernces are not being saved. What are two possibilities
as to why this might be happening ¨ a) the store() method is not being
invoked¨ b) the save() method is not being invoked¨ c) the
PortletPreferences are being configured in the doView method¨ d) the
PortletPreferences are being manipulated in the processAction method
Options a) and c) are correct.There are two common reasons why
PortletPreferences don?t get saved. First of all, developers forget to
call the store method, and secondly, developers try to manipulate
PortletPreferences in the wrong mode. Answer xxx-3 What are the
standard, JSR-168, portlet modes? ¨ a) edit¨ b) view¨ c) help ¨ d)
config Options a) b) and c) are correct.All JSR-168 compliant portal
servers must support view, edit and help modes. However, portal servers
are free to support any custom modes. The WebSphere Portal Server has
built in features for supporting a configure mode. Answer xxx-4 To
ensure a PortletPreference has a default value, even if the user hasn?t
customized their portlet, the best plan of action is to: ¨ a) code a
default value in the web.xml file¨ b) code a default value in the
portlet.xml file¨ c) provide a default value as the second parameter of
the PortletPreferences getValue() method¨ d) accept a null value as a
valid PortletPreference Options b) and c) are correct.Making sure
there?s a default value for a user configurable property is always a
challenge. With the PortletPreferences, we can code a default value in
two ways. First, we can put a preference value in the portlet.xml file.
Secondly, the getValue() method takes two parameters, the first being
the name of the property you wish to extract, and the second parameter
being a default value, just in case the property you are looking for
doesn?t exist. Answer 6-5 Which of the following checked exceptions
could be thrown by a call to the PortletPreferences store method? ¨ a)
ReadOnlyException¨ b) PortletException¨ c) ValidatorException¨ d)
IOException Options a) c) and d) are correct.PortletPreferences are
stored persistently, typically in a centralized database, and that
database write can throw an IOException. Furthermore, PortletPreferences
can be associated with a PreferencesValidator, which throws a
ValidatorException if a preference doesn?t pass a validation test.
Finally, PortletPreferences can be configured as being read-only, and if
someone tries to overwrite a read-only preference, a ReadOnlyException
is thrown. Answer xxx-6 Minimize, Maximize and Normal are all valid: ˇ
a) window statesˇ b) window modesˇ c) Portlet statesˇ d) Portlet modes
Answer a) is correct. Minimize, maximize and normal are the three
standard window states. Sometimes the terms state and modes are easily
confused. View and edit are modes, minimize and maximize are states.
Answer 7-7 Where is a PortletPreference defined as being read only. ˇ a)
programmatically through the PortletPreferences objectˇ b) by adding a
read-only parameter to the portlet.xml fileˇ c) by adding a read-only
parameter to the web.xml fileˇ d) PortletPreferences are always editable
Answer b) is correct. There is no programmatic way to set a
PortletPreference to be read-on; this can only be done by setting the
preference through the portlet.xml file, and subsequently adding a tag
indicating that the preference is read only. l Chapter 8Custom Portlet
Modes The JSR-168 portlet specification requires all portal servers to
provide services for the three standard portlet modes: view, edit and
help. However, portal administrators are allowed to provide support for
any number of extra portlet modes they think would be helpful. For
example, it is envisioned that many portlets would benefit from a print
mode that would make it possible to print out a portlet with the simple
click of a button. IBM always provided support for a configure mode with
their legacy portlet API, so it?s no surprise that the WebSphere Portal
Server comes with built in support for a configure mode. Portal Support
for Custom Modes First of all, I want to make it clear that you can
follow all of the programming steps required to create a portlet that
has a custom mode, but if the portal server you are deploying to doesn?t
support that custom mode, that custom mode just ain?t going to work. If
you want your portlets to render a custom portlet mode, that custom
portlet mode must be enabled on the portal server to which you are
deploying. Now, the WebSphere Portal Server supports the configure mode,
so I will demonstrate how to write a portlet that supports the configure
mode. The assumption is that we will be deploying to WebSphere.
Implementing Custom Portlet Modes The first step in coding a custom
portlet mode it overriding the inherited doDispatch method of the
GenericPortlet. The doDispatch method is always invoked before the
doView or doEdit modes, so, if we override this method and inspect to
see if our custom portlet mode is being invoked, we can capture the
request, and send it to our own custom do method, which in this case,
will be doCustomConfig(). And what do we do in our doCustomConfig ()
method? Well, anything we want. This method will be responsible for
rendering the config mode of the portlet. This method can do anything
you want it to do, but in all likelihood, the method will forward to a
jsp file. Figure xxx WebSphere Portal Server 6.x has inherent support
for the custom config mode. Even if custom modes are coded properly, if
a portal server is not configured to support that custom mode, the
custom mode simply will not be available to the end user. The Custom
do<mode> Method When the custom configure mode is being requested,
we call to our custom do<mode> method named doCustomConfig(). In
our example, the config mode simply prints a message about its own
self-actualization to the console. However, this method will never be
called, unless we override the doDispatch method. protected void
doCustomConfig( RenderRequest request, RenderResponse response) throws
PortletException, IOException { response.setContentType(?text/html?);
response.getWriter().print("I have a custom config mode!!!");
} Overriding the doDispatch Method When overriding the doDispacth
method, we first check to see if the portlet is minimized. The mode
being requested really doesn?t matter if the portlet is minimized, so if
it is, we just skip the whole render phase processing. If the portlet
isn?t minimized, then we check to see if the portlet mode being
requested is the config portlet mode. If it is, then we call our
doCustomConfig method. If our custom portlet mode is not being invoked,
we simply call super.doDispatch(), which essentially allows the portlet
to run as though our custom, overridden doDispatch method was never
called. protected void doDispatch( RenderRequest request, RenderResponse
response) throws PortletException, IOException{ /* don?t do anything if
the window is minimized */ if
(!WindowState.MINIMIZED.equals(request.getWindowState())) { /* find out
which portlet mode is being requested */ PortletMode requestedMode =
request.getPortletMode(); PortletMode customPortletMode = new
PortletMode("config"); /* if our custom mode is requested,
call our custom do method */ if
(requestedMode.equals(customPortletMode)) { /* this is the call to our
custom do <mode> method */ doCustomConfigure(request, response);
return; } /*if our custom portlet wasn?t called, allow the regular
portlet mode processing happen*/ super.doDispatch(request, response); }
} Editing the Portlet Deployment Descriptor Along with the overridden
doDispatch method, and the custom do<mode> method, the deployment
descriptor of the portlet application must be updated to indicate that a
new portlet mode is being supported. <?xml version="1.0"
encoding="UTF-8"?> <portlet-app
xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd"
version="1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd
http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd
id="com.examscam.portlet.CustomModePortlet.051cf00ef0">
<portlet>
<portlet-name>CustomModePortlet</portlet-name>
<display-name>CustomModePortlet</display-name>
<display-name xml:lang="en"> CustomModePortlet
</display-name> <portlet-class>
com.examscam.portlet.CustomModePortlet </portlet-class>
<expiration-cache>0</expiration-cache> <supports>
<mime-type>text/html</mime-type>
<portlet-mode>view</portlet-mode> <!-- This entry
indicates the portlet supports the config mode -->
<portlet-mode>config</portlet-mode> </supports>
<supported-locale>en</supported-locale> <portlet-info>
<title>CustomModePortlet</title> </portlet-info>
</portlet> <!-- This entry indicates the portlet application
supports config--> <custom-portlet-mode>
<portlet-mode>config</portlet-mode>
</custom-portlet-mode> </portlet-app> The Full
CustomModePortlet package com.examscam.portlet; import java.io.*;import
javax.portlet.*; public class CustomModePortlet extends GenericPortlet {
protected void doView (RenderRequest request, RenderResponse response)
throws PortletException, IOException {
response.setContentType(?text/html?);
response.getWriter().println("Custom Portlet View Mode"); }
protected void doCustomConfigure (RenderRequest request, RenderResponse
response) throws PortletException, IOException {
response.setContentType(?text/html?);
response.getWriter().println("Custom Config Mode!!!?); } protected
void doDispatch (RenderRequest request, RenderResponse response) throws
PortletException, IOException{ /* don?t do anything if the window is
minimized */ if
(!WindowState.MINIMIZED.equals(request.getWindowState())) { /* find out
which portlet mode is being requested */ PortletMode requestedMode =
request.getPortletMode(); PortletMode customPortletMode = new
PortletMode("config"); /* if our custom mode is requested,
call our custom do method */ if
(requestedMode.equals(customPortletMode)) { /* this is the call to our
custom do <mode> method */ doCustomConfigure(request, response);
return; } /*if our custom portlet wasn?t called, allow the regular
portlet mode processing happen*/ super.doDispatch(request, response); }
} } Answer 6-6 Special icons associated with a particular custom portlet
mode are defined: ˇ a) in the web.xml fileˇ b) in the portlet.xml fileˇ
c) in the render phase of the portletˇ d) in a portal skin Option d) is
correct.The cute little icons, such as the pencil for the edit mode, or
the wrench for the custom config mode, are part of a portal skin, and
defined outside of the actualy portlet application. The fact that icons
for custom modes is defined at the portal server level, not the portlet
level, is part of the reasons why portlets cannot simply define custom
portal modes on their own.
The PortletSession If the user is going to be good enough to
provide information to us through html forms and http headers, the least
we could do is keep track of that information, if only for the duration
of the user's visit to our site. To provide a stateful experience for
portal users, portlet developers rely upon the services of the
PortletSession, which, by the way, is remarkably similar to the
HttpSession object from the Servlet API. The PortletSession is easy to
use, and helps maintain a stateful experience for the user, overcoming
the stateless nature of the HTTP protocol. This chapter will look at the
PortletSession, how the PortletSession works, and will even discuss some
of the drawbacks and alternatives to using a PortletSession object.
Canada and the http protocol have one thing in common: they are both
stateless. Canada has ten provinces and three relatively empty
territories, but no states. Of course, they have been eyeing Michigan
for a while. Managing State with the PortletSession Object When a user
visits our site, we often need to keep track of information the user has
provided. Information stored in the PortletRequest or PortletResponse
objects are purged as soon as a response is sent back to the client,
which creates a problem if we want to keep track of information that a
user has provided on previous request-response cycles. To store user
specific information for the duration of the user's interaction with the
server, the Portal API provides a special object called the
PortletSession. Any useful piece of information, in the form of Java
objects, can be stuffed inside of a user's session object. That
information is then available to our portlet on all subsequent
request-response cycles. The PortletSession is effectively tied to the
user for which it is created. The Transient Nature of the PortletSession
One thing to note about the PortletSession is that it is transient ?
it's the hobo of the Portlet API. If the user leaves our site, doesn't
interact with the site for a predetermined amount of time (usually
thirty minutes), or even if the user closes their browser, information
stored in the PortletSession is lost, or at least, the session
containing the information will not longer be tied to the user.
Information stored in the PortletSession is not stored persistently. The
job of the PortletSession is to simply create a stateful experience for
users while interacting with our site. Taking Advantage of the
PortletSession Object Figure 5-1 shows a simple portlet that keeps track
of the number of times the doView method of a portlet is called. The
view count variable is stored in the PortletSession, under the key named
timesvisited. Every time the doView method of the portlet is called, the
view count increases by one. The view count is then displayed back to
the user. Notice how the SimpleSessionPortlet requires some session-data
management. When the portlet is first viewed, there will be no data
stored in the session regarding the view count. On the first iteration,
a timesvisited key, with an assigned value of 1, is placed into the
PortletSession. On subsequent iterations, when the timesvisited key is
present in the session, the associated value is extracted, incremented,
and then stored back in the PortletSession. Figure 5-1 Taking Advantage
of the PortletSession package com.examscam.portlet;import
java.io.*;import javax.portlet.*;public class SimpleSessionPortlet
extends GenericPortlet { protected void doView(RenderRequest request,
RenderResponse response)throws PortletException, IOException {
response.setContentType("text/html"); PrintWriter out =
response.getWriter(); /*grab the PortletSession out of the
PortletRequest*/ PortletSession session = request.getPortletSession();
/*pull the visitCount out of the session. On the first visit, this will
be null*/ String visitCount =
(String)session.getAttribute("timesvisited"); if (visitCount
== null) {
/*if this is the first time viewing the portlet, set the timevisited
value to 1*/ session.setAttribute("timesvisited", ""
+ 1);
out.print("Welcome to our little portlet!");
out.print("Click refresh,
minimize or maximize!!!"); } else { /*if this portlet has been
visited
before, increase the count*/ int newCount = Integer.parseInt(visitCount)
+ 1; session.setAttribute("timesvisited", "" +
newCount);
out.print("Number of times visiting this portlet: ");
out.print(newCount); } }} PortletSessions and User Portlets An
interesting aspect of the PortletSession is the fact that data stored in
the PortletSession, by default, is local and accessible only to the
portlet that created it. By default, data stored in the PortletSession
by one portlet cannot be shared by any other portlets on the same portal
page. If there are four portlets on a portal page, if they all use a
PortletSession object, then each one will have a separate, unshared,
PortletSession namespace. Even if two instances of the same portlet
appear on a portal page, each one will have its own, unshared,
PortletSession namespace. PortletSession and the PORTLET_SCOPE This
default behavior is known as PORTLET_SCOPE, and has been the way the
PortletSession object has behaved for years. However, developers have
done nothing but moan and groan about the fact that it is incredibly
difficult to share information between portlets that exist within the
same portlet application (think war file), so, with the JSR-168 portlet
specification, the Java Gods introduced the concept of a shared,
APPLICATION_SCOPE, for the PortletSession. When a portlet shoves an
object into the PortletSession, and specifies APPLICATION_SCOPE as the
visibility of the data, any portlet that is part of that same portlet
application, can obtain that data by providing the appropriate key when
making a call to the PortletSession's getAttribute method.
PortletSession Constants The JSR-168 API introduces two new constants,
or should I say, final static variables, in the PortletSession class.
These two new contains are called APPLICATION_SCOPE and PORTLET_SCOPE,
and represent the numbers 0x01 and 0x02 respectively. /* constants
representing the PortletSession scopes*/
PortletSession.APPLICATION_SCOPE //0x01 PortletSession.PORTLET_SCOPE
//0x02 Newly Overloaded PortletSession Methods In a typical Servlet and
JSP based application, along with legacy JetSpeed and proprietary
Portlet APIs, the setAttribute method is used to save data to the
PortletSession. The setAttribute method takes a name/value-object pair
as arguments. Data is pulled out of the session with a call to
getAttribute, with the name provided as a parameter; the associated
value-object is then returned to the calling program. However, in the
JSR-168 Portlet API, the setAttribute and getAttribute methods have been
overloaded, with an extra parameter added, that allows for one of the
two new portlet scopes to be specified. /* default mechanism for adding
to a PortletSession */ session.setAttribute(?key?, ?value?); /*
overloaded mechanism for adding to a PortletSession*/
session.setAttribute(?key?, ?value?, PortletSession.PORTLET_SCOPE);
/*placing data into the APPLICATION_SCOPE of a PortletSession*/
session.setAttribute(?key?, ?value?, PortletSession.APPLICATION_SCOPE);
/* default mechanism for pulling from a PortletSession */
session.getAttribute(?key?); /* overloaded mechanism for pulling from a
PortletSession*/ session.setAttribute(?key?,
PortletSession.PORTLET_SCOPE); /*pulling data from the
APPLICATION_SCOPE*/ session.setAttribute(?key?,
PortletSession.APPLICATION_SCOPE); PortletSession and the
APPLICATION_SCOPE When data placed into the PortletSession is given
APPLICATION_SCOPE, the data is visible to all other portlets that are
packaged as part of the same portlet application. Portlets are part of
the same portlet application when they are defined in a common
portlet.xml file, and packaged in a common war file. Many developers
believe that data stored in the APPLICATION_SCOPE of the PortletSession
is available to all of the portlets a users might access on the portal.
This is simply not the case. While APPLICATION_SCOPE allows a degree of
sharing of data, the data is not shared with portlets that are defined
in separate war files. All portlets sharing APPLICATION_SCOPE data must
be defined in a common portlet.xml file. Class Diagram of the
PortletSession ?The PortletSession interface provides a way to identify
a user across more than one request and to store transient information
about that user. A PortletSession is created per user client per portlet
application. A portlet can bind an object attribute into a
PortletSession by name. The PortletSession interface defines two scopes
for storing objects: APPLICATION_SCOPE PORTLET_SCOPE All objects stored
in the session using the APPLICATION_SCOPE must be available to all the
portlets, servlets and JSPs that belongs to the same portlet application
and that handles a request identified as being a part of the same
session. Objects stored in the session using the PORTLET_SCOPE must be
available to the portlet during requests for the same portlet window
that the objects where stored from. Attributes stored in the
PORTLET_SCOPE are not protected from other web components of the portlet
application. They are just conveniently namespaced. The portlet session
is based on the HttpSession. Therefore all HttpSession listeners do
apply to the portlet session and attributes set in the portlet session
are visible in the HttpSession and vice versa.? -Description of the
PortletSession from the IBM/Sun Portal API JavaDocs Reading from the
APPLICATION_SCOPE Our SimpleSessionPortlet defined in Fig. 5-1, places a
hitcount into the session using the key timesvisited. To allow another
portlet to access this hitcount, we could change the scope of the
timesvisited key to APPLICATION_SCOPE, and have a second Portlet, which
we will name SessionViewerPortlet, pull the key out of the
APPLICATION_SCOPE. package com.examscam.portlet; import java.io.*;import
javax.portlet.*; public class SessionViewerPortlet extends
GenericPortlet { protected void doView (RenderRequest request,
RenderResponse response) throws PortletException, IOException {
response.setContentType("text/html"); PrintWriter out =
response.getWriter(); PortletSession session =
request.getPortletSession(); Object count =
session.getAttribute("timesvisited",
PortletSession.APPLICATION_SCOPE);
if (count != null) { out.print("I found the count in the session:
");
out.print(count.toString()); } else { out.print("Couldn't find the
count
in the session."); } } } While the SessionViewerPortlet looks for
the
timesvisited key in the APPLICATION_SCOPE, the lookup will be
unsuccessful until the SimpleSessionPortlet explicitly places the
timesvisited key in the appropriate scope. Figure 5-2 Taking Advantage
of the PortletSession package com.examscam.portlet;import
java.io.*;import javax.portlet.*;public class SimpleSessionPortlet
extends GenericPortlet { protected void doView(RenderRequest request,
RenderResponse response) throws PortletException, IOException {
response.setContentType("text/html"); PrintWriter out =
response.getWriter(); PortletSession session =
request.getPortletSession(); String visitCount =
(String)session.getAttribute( "timesvisited",
PortletSession.APPLICATION_SCOPE); if (visitCount == null) { /*if this
is the first time viewing the portlet, set the timevisited value to 1*/
session.setAttribute("timesvisited", "" + 1,
PortletSession.APPLICATION_SCOPE); out.print("Welcome to our little
portlet!"); out.print("Click refresh, minimize or
maximize!!!"); } else
{ int newCount = Integer.parseInt(visitCount) + 1;
session.setAttribute("timesvisited", "" + newCount,
PortletSession.APPLICATION_SCOPE); out.print("Number of times
visiting
this portlet: "); out.print(newCount); } }} Now that the key
timesvisited is stored in the scope of the application, both the Session
ViewerPortlet, and the SimpleSessionPortlet, can share information
through the PortletSession. But notice how the two portlets are out of
sync, with one showing a count of 2, and the other showing a count of 3?
This very issue will lead us into the next chapter on the action
processing phase of a portlet. Portlet.xml File for PortletSession
Examples
<?xml version="1.0" encoding="UTF-8"?>
<portlet-app
xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd"
version="1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd
http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd"
id="PortletSessions.8a76e53fe0"> <portlet>
<portlet-name>SessionViewerPortlet</portlet-name>
<display-name>SessionViewerPortlet</display-name>
<portlet-class>
com.examscam.portlet.SessionViewerPortlet </portlet-class>
<supports> <mime-type>text/html</mime-type>
<portlet-mode>view</portlet-mode> </supports>
<portlet-info>
<title>SessionViewerPortlet</title>
</portlet-info> </portlet> <portlet>
<portlet-name>SimpleSessionPortlet</portlet-name>
<display-name>SimpleSessionPortlet</display-name>
<portlet-class>
com.examscam.portlet.SimpleSessionPortlet </portlet-class>
<supports> <mime-type>text/html</mime-type>
<portlet-mode>view</portlet-mode> </supports>
<portlet-info>
<title>SimpleSessionPortlet</title>
</portlet-info> </portlet> </portlet-app>
The NumberGuesser and the PortletSession Our NumberGuesserPortlet could
really use a good dose of the PortletSession object. Currently, our
NumberGuesserPortlet thinks up a number, and asks the user to guess it,
but it doesn't give the user a chance to keep trying until they guess
the magic number correctly. By incorporating the PortletSession object
into the NumberGuesserPortlet, we could store both the magic number and
the number of guessing attempts. We could even give the user a hint as
to whether they should guess higher or lower. When the user guesses
successfully, we can then provide a message that indicates their
success, and subsequently displays the number of attempts it took. Our
improved NumberGuesserPortlet appears in Figure xxx-2 Be aware that
changes will be required in the corresponding JSP in order for the
portlet to function properly. The JSP changes are listed in Figure
6xxx-5. Tips for Manipulating the PortletSession Information is pulled
out of the PortletSession using the getAttribute(String) method. Objects
are put into the PortletSession using the setAttribute(String, Object)
method. The name used to put an object into the session must exactly
match the name used to pull the object out. Spellling mistakes, or even
a different caSinG of letters will cause the return of a null object.
Always try to avoid NullPointerExceptions. J The PortletSession also
includes a method called removeAttribute(String). If you no longer have
use for an object you have stored in the PortletSession, that object
should be removed from the session. The misuse of session data is one of
the most significant performance bottlenecks a portal server, or any
J2EE application server for that matter, will encounter. Eliminating
unnecessary objects from your PortletSession, and avoiding what is known
as ?session bloat,' will help you avoid performance problems at runtime.
Figure 5-3 The New and Improved NumberGuesserPortlet package
com.examscam.portlet;import java.io.*;import javax.portlet.*;public
class NumberGuesserPortlet extends GenericPortlet {protected void doView
(RenderRequest request, RenderResponse response) throws
PortletException, IOException {PortletContext context =
this.getPortletContext();PortletSession session =
request.getPortletSession();if
(session.getAttribute("magicnumber") ==
null) { /* generate a magic number to start the game*/ int magicNumber =
(int) (System.currentTimeMillis() % 9) + 1; /* populate the session with
pertinent Java objects*/ session.setAttribute("magicnumber",
new
Integer(magicNumber)); session.setAttribute("guesses",
"0"); /* place
the message in the session*/ session.setAttribute("message",
"Guess the
number!");} else { /* get the magicNumber and number of guesses out
of
the session*/ /* NOTE: what will happen if the user didn't type anything
in? */ Integer magicNumber = (Integer)
session.getAttribute("magicnumber"); String guesses = (String)
session.getAttribute("guesses"); /* increment the number of
guesses*/
guesses = "" + (Integer.parseInt(guesses) + 1);
session.setAttribute("guesses", guesses); /* what did the user
guess? */
Integer guess = new Integer(request.getParameter("number"));
/* figure
out a message to return to the user*/ if (guess.intValue() >
magicNumber.intValue()) { session.setAttribute("message",
"Guess
lower!"); } else { session.setAttribute("message",
"Guess higher!"); }
if (guess.intValue() == magicNumber.intValue()) { String message =
magicNumber + " is correct. Play again!";
session.setAttribute("message", message); /* purge the session
of
unneeded data*/ session.removeAttribute("magicnumber"); }}/*
forward to
the jsp for display */String url =
"/numberguesser.jsp";context.getRequestDispatcher(url).include(request,
response);}} Inspecting the Better NumberGuesserPortlet The first two
lines of our improved portlet simply declares the PortletSession and
PortletContext objects for easy access later on in the portlet. The next
step is to see if a magic number exists in the session. If there is no
magic number in the session, then we assume the user is looking at this
portlet for the first time, or they are re-starting the guessing
process. In that case, we create a new magic number and stuff that
number into the PortletSession. We also set the number of guesses to
zero, after all, the number guessing game is just starting. Once all of
our objects have been initialized, and stuffed into the PortletSession,
we can forward to a JSP page, but before we do that, we stuff a little
message into the PortletSession. The message simply says ?Guess the
number!!!? This message will be printed out by the JSP. Figure 5-4
"""public class NumberGuesserPortlet extends
GenericPortlet {protected
void doView (RenderRequest request, RenderResponse response) throws
PortletException, IOException {PortletContext context =
this.getPortletContext();PortletSession session =
request.getPortletSession();if
(session.getAttribute("magicnumber") ==
null) { /*generate a magic number to start the game*/ int
magicNumber=(int)(System.currentTimeMillis() % 9)+1; /*populate the
session with pertinent Java objects*/
session.setAttribute("magicnumber", new Integer(magicNumber));
session.setAttribute("guesses", "0"); /*message is
set in the session*/
session.setAttribute("message", "Guess the
number!");}"""} When Form
Data is Present If request.getParameter(?number?) does not return null,
then indeed the user has just submitted a form, trying to guess the
magic number. In this case, we grab the magic number from the session,
then we grab the number of guesses from the session, and finally we grab
the number guessed by the user, using the request.getParameter(?number?)
method. ***Note that we have been lazy so far. If the use clicks submit
without typing anything into the textfield, or types in non-numeric
data, we are going to have a NumberFormatException. If the number is too
high, we send them to the numberguesser.jsp file with a message stuffed
into the session object instructing them to guess lower.
session.setAttribute("message", "Guess lower!"); If
the number is too
low, we send them to the numberguesser.jsp file with a message stuffed
into the request object instructing them to guess higher.
session.setAttribute("message", "Guess higher!"); If
the user guesses
the magic number correctly, we stuff a friendly message in the session
object, indicating that the user has successfully guessed the magic
number. After updating the message, we remove the magic number from the
session. After all, it is not needed now that the number has been
guessed. If the user wants to play again, we'll just generate a new
magic number, and set their number of guesses to zero. Figure 5-5
"""if
(guess.intValue() == magicNumber.intValue()) { String message=
magicNumber + " is correct. Play again!";
session.setAttribute("message", message); //Purge the session
of
unneeded data
session.removeAttribute("magicnumber");}"""
Debriefing the
NumberGuesser's JSP Pages A JSP is used to generate the markup needed to
display the portlet on a portal page. The JSP used in this example is
displayed in Figure 6xxx-5. Figure 5-6 Generating a View with a JSP
<%@
page contentType="text/html"%><%@taglib
uri="http://java.sun.com/portlet"
prefix="portlet"%>
<portlet:defineObjects />
<%=renderRequest.getPortletSession().
getAttribute("message")%>
<FORM action=""><portlet:renderURL />">I'm
thinking of a number between
1 and 10. <BR />
<INPUT name="number" size="10"
type="text" /><INPUT value="Guess!!"
type="submit" /></FORM>
Number of guesses:<%=renderRequest.getPortletSession()
.getAttribute("guesses")%> The biggest change for the JSP
on this
iteration of the NumberGuesserPortlet is the presence of the expression:
<%=renderRequest.getPortletSession().getAttribute(?message?)%>
just
before the input field. Inside the NumberGuesserPortlet, a message is
placed in the session scope, indicating whether a user should guess
higher, lower, or if the user is guessing for the first time, simply to
guess randomly. This message is then printed out just above the
textfield in the numberguesser.jsp page. This script allows a single JSP
page to be used dynamically, and render itself slightly differently,
depending upon the state of the application. Our improved form also
displays to the user how many guesses they have made at the magic
number, based on the guesses key placed into the PortletSession during
the doView method. Number of guesses:
<%=renderRequest.getPortletSession()
.getAttribute("guesses")%> Question
5-1 What type of data is acceptable for the storage in the
PortletSession? ˇ a) any class that inherits from java.lang.Objectˇ b)
any String valueˇ c) any serializable Java objectˇ d) any serializable
String value Question 5-2 Programmatically, what is the process for
storing transient data in the PortletSession? ˇ a) call the
setAttribute(name, value) method of the PortletSession, followed by the
store() methodˇ b) call the setAttribute(name, value) method of the
PortletSession, followed by the save() methodˇ c) call the
setAttribute(name, value) method of the PortletSession, followed by the
set() methodˇ d) call the setAttribute(name, value) method of the
PortletSession Question xxx-3 Information stored in the PortletSession
with the visibility of APPLICATION_SCOPE: ˇ a) will be stored
persistentlyˇ b) will be stored until the portlet application it is
associated is stoppedˇ c) will be destroyed when the user's session
times out ˇ d) will be stored until the portal server is shut down
Question 5-4 Which of the following is true about the JSR-168
PortletSession? ¨ a) Data stored in the PortletSession is also available
to the HttpSession of the Servlet and JSP API¨ b) Data stored in the
HttpSession is also available to Portlets through the PortletSession¨ c)
The PortletSession inherits from the HttpSession of the Servlet and JSP
API¨ d) The PortletSession is based on the HttpSession of the Servlet
and JSP API Question 5-5 A portlet needs to share session data with
another portlet defined in the same portlet.xml file. Which
PortletSession scope should be used? ˇ a) PORTLET_SCOPEˇ b)
SESSION_SCOPEˇ c) APPLICATION_SCOPEˇ d) This type of session sharing is
not possible Answer 5-1 What type of data is acceptable for the storage
in the PortletSession? ˇ a) any class that inherits from
java.lang.Objectˇ b) any String valueˇ c) any serializable Java objectˇ
d) any serializable String value Answer c) is correct. While
non-serialized objects can safely be placed in the PortletSession on a
non-distributed environment, in order to have portlet applications
behave properly in a distributed environment that employs multiple JVMs,
you must ensure that any object placed in the PortletSession is
serializable. Answer 5-2 Programmatically, what is the process for
storing transient data in the PortletSession? ˇ a) call the
setAttribute(name, value) method of the PortletSession, followed by the
store() methodˇ b) call the setAttribute(name, value) method of the
PortletSession, followed by the save() methodˇ c) call the
setAttribute(name, value) method of the PortletSession, followed by the
set() methodˇ d) call the setAttribute(name, value) method of the
PortletSession Answer d) is correct. In order to save data in the
PortletSession, you simply call the setAttribute method, providing a
name for the object being placed in the session, along with the object
itself. Only the PortletPreferences object needs an explicit call to
store() in order to ensure that data is saved persistently for the user.
Answer 5-3 Information stored in the PortletSession with the visibility
of APPLICATION_SCOPE: ˇ a) will be stored persistentlyˇ b) will be
stored until the portlet application it is associated is stoppedˇ c)
will be destroyed when the user's session times out ˇ d) will be stored
until the portal server is shut down Answer c) is correct.
PortletSession data, regardless of whether it is APPLICATION_SCOPE or
PORTLET_SCOPE, will be only stored transiently. Answer 5-4 Which of the
following is true about the JSR-168 PortletSession? ¨ a) Data stored in
the PortletSession is also available to the HttpSession of the Servlet
and JSP API¨ b) Data stored in the HttpSession is also available to
Portlets through the PortletSession¨ c) The PortletSession inherits from
the HttpSession of the Servlet and JSP API¨ d) The PortletSession is
based on the HttpSession of the Servlet and JSP API Answers a) and d)
are correct, with d) actually being lifted directly from the
PortletSession JavaDoc. Since the PortletSession is essentially the
HttpSession of the user, data stored in the HttpSession becomes
available to the user through the PortletSession. Answer 5-5 A portlet
needs to share session data with another portlet defined in the same
portlet.xml file. Which PortletSession scope should be used? ˇ a)
PORTLET_SCOPEˇ b) SESSION_SCOPEˇ c) APPLICATION_SCOPEˇ d) This type of
session sharing is not possible Answer c) is correct. The
APPLICATION_SCOPE makes it possible to share session data across
portlets that are part of the same portlet application.
|