NEW!! FREE MOCK EXAM SIMULATORS FOR IBM PORTAL TEST 399 & IBM PORTLET CERTIFICATION EXAM TEST 829!!!
Everything You Ever Wanted To Know About JSR-168 Portlet Development . . . PORTAL + TUTORIAL = www.portorials.com
Google

Awesome Introduction to What the Portal Is

If you're trying to undestand what a portal is all about, or more importantly, you're trying to explain to someone what the portal is all about, well, this the the presentation you absolutely need.

This tutorial effectively demonstrates what the portal is, how a user can navigate through a portal, it demonstrates some common user functionality, and it even shows how a user can customize their own portal experience. This is a quick tutorial, but it really hits the mark. Enjoy it, and please, tell your friends about it too!

Please support our site, visit our sponsors, link to us, tell your friends about the site, buy some books, and remember: Happy Java!

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.
Google



eXTReMe Tracker

ActionRequest ActionResponse GenericPortlet PortletRequestDispatcher . Portlet ..... PortletURL
PortletContext PortletException PortletMode a PortletModeException PortletPreferences
PortalContext PortletResponse PortletSession PortletSecurityException PreferencesValidator
PortletConfig RenderRequest RenderResponse UnavailableException PortletSessionUtil
PortletRequest ValidatorException WindowState WindowStateException UnmodifiableException