jsps, forms, and linking back to a portlet
In a good model-veiw-controller type of application, a Java
centric component should never be polluted with lots of lousy html.
With typical Servlet and Struts based applications, html is
usually generated by a Java Server Page (JSP); in this regard, portlet
applications are no different.
However, portlet applications do present some unusual
complications when deferring to a JSP for markup generation.
How does a JSP link back to a specific portlet on a page?
How does a portlet call a JSP? How do we gain access to the portlet API
specific PortletRequest and
PortletResponse object in JSP?
This chapter deals with basic JSP development, and the
issues that present themselves when deferring to a JSP for makup generation.
‘Gotchas’ when Deferring to a JSP from a
Portlet
CaSe SenSItiVe Names and Deceptive JSP Paths
JSP Deferment and Multiple Markup Support
Creating
Basic HTML Forms for User Input
Referencing the Originating
Portlet
The
Challenge of Linking Back to a Portlet
The
PortletURI Object
Required
Custom Tags for Portlet Based JSPs
The taglib Directive and the
<portletAPI:init/> Tag
Grabbing
Form Input From Within a Portlet
Multiple Portlets and Handling
Form Data
Encoding
Form Data with encodeNamespace( )
A Quick
Look at Some Portlet Custom Tags
Our
Input Form with Portlet Custom Tags
Deferring to a JSP for Markup Generation
While accessing the PrintWriter from the request object and
printing content directly back to the client in the doView method of a portlet
is easy, it certainly isn’t a best practice.
We have been spoiled so far with portlets that are relatively
light on html. However, for proper markup generation, portlets should defer to
a Java Server Page (JSP).
JSPs are web centric artifacts that are written largely in static
html, but can be interspersed with Java code to make them more dynamic.
While the concept of delegating to a JSP for view generation is
relatively simple and straight forward, the code required to do this with a
portlet is actually a little bit intimidating. To forward to a JSP named
welcome.jsp, the code in your doView() method would look like this:
getPortletConfig().getContext().include("welcome.jsp",
request, response);
|
Figure ?-? A Sample Java Server Page (JSP)
A JSP is mostly markup, with a little bit of Java
code (in bold) interspersed. Java Server Pages facilitate the development of
complex, dynamic web pages.
|
|
<%@
taglib uri="/WEB-INF/tld/portlet.tld" prefix="portletAPI"
%>
<portletAPI:init
/>
<B>Welcome
to this simple JSP<B>
<BR>We
did some snooping on you.
<BR>This
is what we learned about your browser:
<I> <%=portletRequest.getHeader("user-agent")
%> </I>
<BR>And
we know what language you speak, it's:
<I> <%=portletRequest.getLocale().getDisplayLanguage ( )
%> </I>
|
Generating
this amount of html using a PrintWriter inside of athe doView method of a
portlet would be insane. This is the type of html generation we like to defer
to a JSP file. After all, generating html is what JSP files do best.
‘Gotchas’ when Deferring to a JSP from a
Portlet
There are indubitably a few ‘gotchas’ you must be aware of before
you defer to a Java Server Page (JSP). Pay careful attention here: we are going
to show you how to avoid the frustrating task of trying to figure you why your
JSPs aren’t displaying properly.
CaSe SenSItiVe Names and Deceptive
JSP Paths
First and foremost, the name of a JSP file is case sensitive.
Make sure you spell the name correctly, and make sure the case in the ‘include’
method call of the portlet matches the case of the actual name of the JSP. For
the sake of simplicity, name your jsp and html files using all lower case
letters, rather than an undecipherable mish-mash of upper and lower case
letters.
Secondly, the location of the jsp file you reference in your
portlet is not relative to the root of the web module (the ‘Web Content’
folder). With typical Servlet based applications, the location of an included
or ‘forwarded to’ file is relative to the root of the web module, but this is
not necessarliy the case with portlet applications.
|
//the
welcome.jsp file is deceptively NOT in the root of the web module
|
|
getPortletConfig().getContext().include("welcome.jsp",
request, response);
|
In our example, the welcome.jsp is actually in the \html
subdirectory of the web module root. If you place your welcome.jsp file
directly under the root, you will get a “This portlet is unavailable” message
when you try to run the portlet. That’s never a good thing.
Furthermore, if a path is given to the jsp file in the include,
the html directory is implied after the path, and before the name
of the jsp. So, if the path in the include was:
include(“common/jsp/welcome.jsp”,
request, response);
then your web module would need a folder
named:
common/jsp/html/welcome.jsp
As you can see, the html comes after the folder path, and before
the name of the jsp file to which you are delegating.
JSP Deferment and Multiple Markup Support
So, why is it that despite telling the portal to look for a jsp
in the root of the web module, that it actually looks in a subfolder named
html?
Well, it’s all due to the fact that the portal dynamically matches
the jsp to which you are calling to the preferred markup language of the
client.
If the client device has a preferred markup language of html,
then the portal will look for a matching JSP in the html subfolder off of the
root of the web module. If the client device prefers wml, then the portal will
look for a matching JSP file in the wml subdirectory off of the root of the web
module.
By matching the preferred markup language of the client to a
subfolder with a matching name, dynamically supporting multiple markup
languages is as simple as placing markup specific JSP pages in the appropriate
directory. It’s coyote super-genious, really, although it can sometime throw
you off when you’re trying to get a simple portlet that includes a JSP working
properly.
|
Figure ?-? Actual Location of Included JSP Files
When the doView method defers to a JSP, the JSP
file must be placed in a folder named according to the markup language
generated by the JSP; in this case, html.
|
|
public void
doView (PortletRequest request, PortletResponse response)
throws PortletException, IOException {
getPortletConfig().getContext().include("welcome.jsp", request, response);
}
|
Creating Basic HTML Forms for User Input
Forwarding to a JSP to spit out a little ‘Hello World’
message isn’t going to win you any ‘programmer of the year’ awards. On the
other hand, using a JSP to generate an html form that takes input from a user
would be a fine application of your programming talents.
To grab input from a user, we use html forms. For example,
if we wanted the user to guess a magic number between one and ten, we might
provide an html form that looks something like figure ?-?:
|
Figure ?-? Using html Forms for User Input
Web based forms are used to grab input from the
user. This form expects a user to type a number between one and ten into the textfield and click the Guess!!
button.
|
|
|
Referencing the Originating Portlet
Using html forms to obtain input from a user is nothing
new to a web developer, but using forms in the portal presents a few unique
challenges.
One challenge is figuring out how to reference back our
originating portlet. A portlet can be placed on a variety of different web
pages at runtime. How can we invoke a web-based resource when we don’t know
what web page it will appear on?
The other challenge is making sure that the data submitted
on our form goes to our portlet, and our portlet only. We don’t want any other portlets
on the portlet page using data submitted from our portlet’s form.
Fortunately for us though, those clever sausages who put together the
portlet API have addressed these very challenges.
The Challenge of Linking Back to a Portlet
When a user
clicks submit on a form, there must be an object sitting on the server this is
ready to process that user’s request.
With typical
Servlet/JSP applications, form submissions are forwarded to a Servlet, and the
name of the Servlet is specified as the action attribute of the form;
Unfortunately, we can’t do that with a portlet.
For example,
if our number guessing application was implemented as a Servlet, there would be
an HttpServlet, perhaps creatively named NumberGuessServlet, that would respond
to the submission of the form and extract the users input. The Servlet would
constitute the ‘action’ of the form, and the form tag would look like this:
<FORM ACTION=“NumberGuessServlet”>
But a portlet
can’t make a direct call back to itself that easily. The best a portlet could
do is make a call back to the page the portlet is displayed on, but even that’s
impossible to configure in an html form, because at development time, we don’t
know which page, or on how many pages, our portlet will appear.
|
Figure ?-?
We don’t know the name of the page a portlet may appear on at runtime. This makes
hardcoding a target for the action attribute of the form element impossible.
In this case, the action attribute of the form has been left as a question
mark (?).
|
|
<FORM ACTION=”?” >
I'm thinking
of a number between 1 and 10.<BR><BR>
<I>What
is it?</I>
<INPUT
type="text" name="number" size="10">
<INPUT
type="submit" name="SUBMIT" value="Guess!!">
</FORM>
|
Notice how the
action attribute of the form element in figure ?-? points to a question mark.
Typcially, this would point to a Servlet or a CGI script that handles the
submission of a form. How can we direct to submission of the form back to our
portlet?
The PortletURI Object
Life would be simple if we could tell a form to directly call the
NumberGuesserPortlet, but we can’t. When a request is made to the portal
server, a single portlet can’t be invoked directly. Instead, the user must
request a portal page, and the portal server takes care of rendering all of the
portlets that are part of that page, which hopefully includes the portlet with which we are trying to communicate.
So, how do we make a new request to the portal to have our portlet, and
the portal page it is associated, re-invoked? The answer is to have the
PortletResponse create a PortletURI object, which essentially represents a link
back to the current portal page, and all of the portlets that appear on it.
response.createURI(); //generates a link
back to the current portal page
To create a link back to a portlet from within an html form, we need to
configure that action attribute of our form. This can be done using a JSP
expression:
< FORM ACTION = ”<%= portletResponse.createURI(
) %>”>
Unfortunately, out of the box, this line of code will not
compile. A taglib directive, along with a <portletAPI:init/> custom tag
must be added to make everything cosher. Figure ?-? deomontrates creating a
PortletURI object, while including the taglib directive, and the
portletAPI:init custom tag.
|
Figure ?-?
To create a link back to the page a portlet
appears on, the createURI() method of the PortletResponse can be used. Notice
the required custom tag and taglib directive.
|
|
<%@
taglib uri="/WEB-INF/tld/portlet.tld" prefix="portletAPI"
%>
<portletAPI:init
/>
<FORM ACTION=” <%= portletResponse.createURI( ) %> ” >
I'm thinking
of a number between 1 and 10.<BR><BR>
<I>What
is it?</I>
<INPUT
type="text" name="number" size="10">
<INPUT
type="submit" name="SUBMIT" value="Guess!!">
</FORM>
|
Required Custom Tags for Portlet Based JSPs
While
all JSPs understand that the word response used inside of an expression or a
scriptlet is a reference to the HttsServletResponse object, a standard JSP has
no knowledge of what a portetResponse object is, even if the reference occurs
in a JSP that is part of a portlet application.
The taglib Directive and the <portletAPI:init/> Tag
To
get the Java compiler to understand that when we say ‘portletResponse’ in our
JSPs, that we are referring to the PortletResponse object, we need to add two
lines to the top of our Java Server Page:
1. <%@ taglib
uri="/WEB-INF/tld/portlet.tld" prefix="portletAPI" %>
2. <portletAPI:init />
The first line
is the taglib, also known as a tag library directive. This taglib directive
indicates that the JSP is going to employ the services of the portletAPI custom
tag library.
The second
line, <portletAPI:init/> is an actual portletAPI custom tag in action.
The sole purpose of the portletAPI:init tag is to expose three important
portlet API objects:
>>> The PortletRequest object,
named portletRequest
>>> The PortletRepsonse
object, named portletResponse
>>> The PortletConfig
object: portletConfig
With local
access to the portletRequest, portletResponse, and portletConfig objects, you
have access to all of the other important portlet API objects, including the
PortletSession, PortletContext, PortletURI and others.
With the
taglib directive, and the portletAPI:init tag, any expression that uses objects
defined in the portlet API will compile and run successfully.
Grabbing Form Input From Within a Portlet
With an html
form that contains a textfield named ‘number’, all we have to do to figure out
what a user typed into that textfield is call the getParameter method of the
request object, and provide the name of that textfield. - in this case the name
is ‘number’. The input of the user will be returned to as a String.
Again,
anything you want to know about the user, which includes what the user typed
into a give textfield, is obtained through the PortletRequest object.
|
Figure ?-? Code
used to grab user input from a textfield named ‘number’.
|
|
package com.pulpjava.numberguesser;
import
org.apache.jetspeed.portlet.*; import
java.io.*;
public class
NumberGuesserPortlet extends PortletAdapter {
public void doView(PortletRequest request,
PortletResponse response)
throws PortletException, IOException {
//generate a magic number
String magicNumber = "" +(
System.currentTimeMillis() % 9 + 1);
//if the user submitted the form, find out
what number they guessed
String guess =
request.getParameter("number");
//if there is no guess, show the input
form to the user
if (guess==null){
getPortletConfig().getContext().include("numberform.jsp",
request, response);
}else {
PrintWriter out =
response.getWriter();
//if there is text
input from the users, tell them if they got the magic number
if (guess.equals(magicNumber)){
out.print("You gessed
it!");
}else {
out.print("You guessed wrong.");
}
out.print("<BR><BR>The magic number was
"+magicNumber);
}
}
}
|
Data Clashes on the Portal Server
With our funky
custom tags, and the clever use of the PortletURI object, our
NumberGuesserPortlet works pretty darn good, but there is a pesky little
problem buried in the JSP.
Our portlet
uses the request.getParameter(“number”) method call to figure out what
the user typed into the textfield named ‘number’. But remember, our
Portlet isn’t the only portlet on the page. What if another portlet also uses a
form, with a textfield called ‘number’? That other portlet would get
access to our portlet’s number, and that isn’t good.
Multiple Portlets and Handling Form Data
Remember, our
portlets don’t exist in a vacuum. The page that contains our NumberGuesserPortlet
might also include a StockSellingPortlet that wants to know the number
of shares a user wants to sell. That same page might also have a
SpousalBenefitsPortlets that needs to know the number of wives or
husbands a user has (Utah residents only). If each of those portlets makes a
request.getParameter(“number”) call in their doView method , each one will get
the number that was typed into the form of the NumberGuesserPortlet.
When a portal
page is rendered, the doView method of every portlet on the page is invoked,
not just that one portlet with which you might currently be interacting.
Encoding Form Data with encodeNamespace( )
To ensure that
form data being sent from our portlet doesn’t get confused with the form data
of another portlet, the PortletResponse object gives us a special method called
encodeNamespace(String). If we use the encodeNamespace method on the field
names in our form, no other portlet on the portal page that gets rendered back
to the client will have access to our data.
All form
elements should be encoded using the encodeNamespace(String) method of the
response.
|
Figure ?-?
To ensure form data is only sent back to the
intended portlet, the encodeNamespace method of the PortletResponse object
needs to be used.
|
|
<%@ taglib
uri="/WEB-INF/tld/portlet.tld" prefix="portletAPI" %>
<portletAPI:init
/>
<FORM
action="<%=portletResponse.createURI()%>">
I'm thinking
of a number between 1 and 10.
<BR><BR><I>What
is it?</I>
<INPUT
name= "<%=portletResponse.encodeNamespace("number")%>"
type="text"
size="10" />
<INPUT
name= "<%=portletResponse.encodeNamespace("SUBMIT")%>"
value="Guess!!" type="submit" />
</FORM>
|
A Quick Look at Some Portlet Custom Tags
Since creating
return URIs, and encoding form variables are such common tasks in our JSP
files, the Portlet API provides a couple of handy-dandy custom tags that make
these tasks just a little bit easier.
Comparatively,
it is much slicker to use the <potletAPI:createURI/> custom tag to spit
out a link back the the current portal page, than it is to use the
corresponding scriptlet.
With a JSP Expression:
<FORM action= ”<%=portletResponse.createURI() %>”
>
With a Custom Tag:
<FORM action= "< portletAPI:createURI />"
>
Similarly,
using a custom tag to encode a variable name used in a form is much more
readable and maintainable than using a JSP expression.
With a JSP: Expression:
name=" <%=portletResponse.encodeNamespace("number")%>
"
With a portletAPI Custom Tag:
name=" <portletAPI:encodeNamespace
value="number"/> "
There are fifteen custom tags defined by the portlet tag library
descriptor (tld). We will explore these tags in more detail as we encounter
them.
Table ?-?
|
Tags Defined by the
Portlet Custom Tag Library
|
|
createURI
|
dataAttribute
|
if
|
URIAction
|
|
createReturnURI
|
dataLoop
|
init
|
URIParameter
|
|
encodeURI
|
settingaAttribute
|
log
|
client
|
|
encodeNamespace
|
settingsLoop
|
text
|
|
|
Our Input Form with Portlet Custom Tags
|
Figure ?-?
Custom tags make Java Server Pages a little easier
to write, and a little easier to read, although some might debate just how
much more readable a JSP the is loaded with innumerable custom tags really
is..
|
|
<%@
taglib uri="/WEB-INF/tld/portlet.tld" prefix="portletAPI"
%>
<portletAPI:init
/>
<FORM
action="<portletAPI:createURI/>">
I'm thinking
of a number between 1 and 10.<BR><BR><I>What is
it?</I>
<INPUT
name="<portletAPI:encodeNamespace value="number"/>"
type="text"
size="10" >
<INPUT
name="<portletAPI:encodeNamespace
value="submit"/>"
type="submit"
value="Guess!!" >
</FORM>
|
|
Figure ?-? Using html Forms for User Input
While special custom tags are used to design
portlal JSP pages, the end user has no knowledge of the behind the scenes
implementation.
|
|
|