Vous êtes sur la page 1sur 29

© 2005 Marty Hall

JSF: Controlling
Page Navigation

Core Servlets & JSP book: www.coreservlets.com


More Servlets & JSP book: www.moreservlets.com
Servlet/JSP/Struts/JSF Training: courses.coreservlets.com
http://courses.coreservlets.com/ -- Hands-on, customized training for Java, servlets, JSP, Struts, and JSF.

© 2005 Marty Hall

For live JSF training, please see


JSP/servlet/Struts/JSF training courses at
http://courses.coreservlets.com/.
Taught by the author of Core Servlets and JSP, More
Servlets and JSP, and this tutorial. Available at
public venues, or customized versions can be held
on-site at your organization.
http://courses.coreservlets.com/ -- Hands-on, customized training for Java, servlets, JSP, Struts, and JSF.
Topics in This Chapter
• JSF flow of control
• The basic steps in using JSF
• Static navigation
– One result mapping
• Dynamic navigation
– Multiple result mappings

4 J2EE training and tutorials: http://www.coreservlets.com

JSF Flow of Control


• A form is built from a palette of GUI
components and displayed to the user
• The form is submitted to itself
– I.e., both original URL and ACTION URL are
http://…/blah.faces
• A bean is created that represents the form
data
• The action designated by the form is
invoked
– This method uses the data from the form bean, invokes
business-logic and data-access logic, and creates/stores
results beans
• The action method returns a condition
• The page corresponding to the condition is
5
displayed J2EE training and tutorials: http://www.coreservlets.com
Steps in Using JSF
1. Create a bean to represent the form data
• Postponed until next section
2. Use f:view and h:form to create an input
form
3. Specify the action controller with the
action attribute of h:commandButton
4. Create an action controller that reads the
form data, invokes business logic, stores
results beans, and returns conditions
5. Use faces-config.xml to declare the form
bean and navigation rules
6. Create JSP pages corresponding to each
return condition
7. Protect raw JSP pages from access
6 J2EE training and tutorials: http://www.coreservlets.com

Example 1: Static Navigation


• Started by copying jsf-blank and renaming it
to jsf-test
• Original URL will be
http://hostname/jsf-test/register1.faces
• When form submitted, a static page
(result1.jsp) is displayed
• No business logic, beans, or Java code of
any sort
• Main points
– Format of original form
– Use of the faces-config.xml file

7 J2EE training and tutorials: http://www.coreservlets.com


Main Points of This Example
• Input form has following format:
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<f:view>
HTML markup
<h:form>
HTML markup and h: tags
</h:form>
HTML markup
</f:view>
• You use faces-config.xml to declare mappings:
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE faces-config PUBLIC …>
<faces-config>
<navigation-rule>
<from-view-id>/blah.jsp</from-view-id>
<navigation-case>
<from-outcome>some string</from-outcome>
<to-view-id>/WEB-INF/results/something.jsp</to-view-id>
</navigation-case>
</navigation-rule>
8 </faces-config> J2EE training and tutorials: http://www.coreservlets.com

Step 1: Create Form Bean


• Postponed until next section
– This example ignores form data

9 J2EE training and tutorials: http://www.coreservlets.com


Step 2: Create Input Form
• Basic format
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<f:view>

<BODY>

<h:form>

</h:form>

</BODY>
</f:view>
• Invoking page
– Actual file is blah.jsp
– URL is blah.faces

10 J2EE training and tutorials: http://www.coreservlets.com

Step 2: Create Input Form


• The h:form element
– ACTION is automatically self (current URL)
– METHOD is automatically POST
• Elements inside h:form
– Use special tags to represent input elements
• h:inputText corresponds to <INPUT TYPE="TEXT">
• h:inputSecret corresponds to <INPUT TYPE="PASSWORD">
• h:commandButton corresponds to <INPUT TYPE="SUBMIT">
– In later sections, we will see that input elements will be
automatically associated with bean properties
– For static navigation, specify arbitrary string as action of
h:commandButton
• String must match navigation rule from faces-config.xml
• More info on h:blah elements
– http://java.sun.com/j2ee/javaserverfaces/1.1/docs/tlddocs/

11 J2EE training and tutorials: http://www.coreservlets.com


Step 2: Example Code
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<f:view>
<!DOCTYPE …>
<HTML>
<HEAD>…</HEAD>
<BODY>
<CENTER>
<TABLE BORDER=5>
<TR><TH CLASS="TITLE">New Account Registration</TH></TR>
</TABLE>
<P>
<h:form>
Email address: <h:inputText/><BR>
Password: <h:inputSecret/><BR>
<h:commandButton value="Sign Me Up!" action="register"/>
</h:form>
</CENTER></BODY></HTML>
</f:view>

12 J2EE training and tutorials: http://www.coreservlets.com

Step 3: Specify Action Controller


• Use the action attribute of h:commandButton
• For most real cases, you specify a method to be
called when form is submitted
– The method returns various strings, and faces-config.xml maps the
strings to output pages
– See upcoming example for details
• For static navigation, you specify a simple string
– faces-config.xml maps the string to an output page
• Example
<h:form>
Email address: <h:inputText/><BR>
Password: <h:inputSecret/><BR>
<h:commandButton value="Sign Me Up!" action="register"/>
</h:form>

13 J2EE training and tutorials: http://www.coreservlets.com


Steps 2 & 3: Example Results
• File is tomcat_dir/webapps/jsf-test/register1.jsp
• URL is http://localhost/jsf-test/register1.faces

14 J2EE training and tutorials: http://www.coreservlets.com

Step 4: Create an Action


Controller
• Postponed until next section
– This example ignores form data and invokes no logic or
Java code of any sort

15 J2EE training and tutorials: http://www.coreservlets.com


Step 5: Update faces-config.xml
• General format
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE faces-config PUBLIC …>
<faces-config>

</faces-config>
• Specifying the navigation rule

<faces-config>
<navigation-rule>
<from-view-id>/the-input-form.jsp</from-view-id>
<navigation-case>
<from-outcome>string-from-action</from-outcome>
<to-view-id>/WEB-INF/…/something.jsp</to-view-id>
</navigation-case>
</navigation-rule>
</faces-config>
16 J2EE training and tutorials: http://www.coreservlets.com

Step 5: Example Code


<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE faces-config PUBLIC …>
<faces-config>
<navigation-rule>
<from-view-id>/register1.jsp</from-view-id>
<navigation-case>
<from-outcome>register</from-outcome>
<to-view-id>/WEB-INF/results/result1.jsp</to-view-id>
</navigation-case>
</navigation-rule>
</faces-config>

17 J2EE training and tutorials: http://www.coreservlets.com


Step 6: Create Output JSP Page
• Note that RequestDispatcher.forward is
used behind the scenes
– So page can be in WEB-INF
• Example code:
– …/jsf-test/WEB-INF/results/result1.jsp
<!DOCTYPE …>
<HTML>
<HEAD>…</HEAD>
<BODY>
<CENTER>
<TABLE BORDER=5>
<TR><TH CLASS="TITLE">Success</TH></TR>
</TABLE>
<H2>You have registered successfully.<BR>
(Version 1)</H2>
</CENTER>
</BODY></HTML>
18 J2EE training and tutorials: http://www.coreservlets.com

Step 6: Example Result


• Note that URL is unchanged

19 J2EE training and tutorials: http://www.coreservlets.com


Step 7: Protect Raw JSP Pages
• Filename/URL correspondence
– Actual files are of the form blah.jsp
– URLs used are of the form blah.faces
– You must prevent clients from directly accessing JSP
pages
• Since they would give erroneous results
• Strategies
– You cannot put JSP pages in WEB-INF
• Because URL must correspond directly to file location
– So, use security-constraint entries in web.xml. But:
• Clumsy
• Easy to forget
• Results in confusing error messages
• This is a major drawback to JSF design
20 J2EE training and tutorials: http://www.coreservlets.com

Step 7: Example Code



<web-app>

<security-constraint>
<display-name>
Prevent access to raw JSP pages that are for JSF pages.
</display-name>
<web-resource-collection>
<web-resource-name>Raw-JSF-JSP-Pages</web-resource-name>
<!-- Add url-pattern for EACH raw JSP page -->
<url-pattern>/welcome.jsp</url-pattern>
<url-pattern>/register1.jsp</url-pattern>

</web-resource-collection>
<auth-constraint>
<description>No roles, so no direct access</description>
</auth-constraint>
</security-constraint>
</web-app>

21 J2EE training and tutorials: http://www.coreservlets.com


Step 7: Example Results
• In Tomcat, results in confusing error message
• Cannot easily use error-page and error-code to
make a better error page
– Since 500 status code used for many other Tomcat errors

22 J2EE training and tutorials: http://www.coreservlets.com

Example 2: Dynamic Navigation


• Original URL will be
http://hostname/jsf-test/signup.faces
– Collects info to see if user qualifies for health plan
• When form submitted, one of two possible
results will be displayed
– User is accepted into health plan
– User is rejected from health plan
• Main points
– Specifying an action controller
– Creating an action controller
– Using faces-config.xml to
• Declare controller
• Map results to output pages
23 J2EE training and tutorials: http://www.coreservlets.com
Main Points of This Example
• Specify the controller with #{controllerName.methodName}
<h:commandButton
value="Sign Me Up!"
action="#{healthPlanController.register}"/>
• Controller method returns strings corresponding to conditions
– If null is returned, the form is redisplayed
– Unlike with Struts, the controller need not extend a special class
• Use faces-config.xml to declare the controller as follows
<faces-config>
<managed-bean>
<managed-bean-name>controller name</managed-bean-name>
<managed-bean-class>controller class</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
</faces-config>
• Add multiple navigation-rule entries to faces-config.xml
– One for each possible string returned by the controller
– If no string matches, the form is redisplayed

24 J2EE training and tutorials: http://www.coreservlets.com

Step 1: Create Form Bean


• Step 1: Create Form Bean
– Postponed until next section
• In this example, we ignore all input data
– Modeled accurately after real health insurance practices

25 J2EE training and tutorials: http://www.coreservlets.com


Step 2: Create Input Form
• Same general syntax as in previous example
– Except for action of commandButton
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<f:view>

<h:form>
First name: <h:inputText/><BR>
Last name: <h:inputText/><BR>
...
<h:commandButton
value="Sign Me Up!"
action="#{healthPlanController.signup}"/>
</h:form>…
</f:view>

26 J2EE training and tutorials: http://www.coreservlets.com

Step 3: Specify Action


Controller
• Use action attribute of h:commandButton
• Specify #{controllerName.methodName}
– The designated method takes no args and returns various
strings. faces-config.xml maps the strings to output pages
– The controller must be declared with a managed-beans
entry in faces-config.xml
• Example
<h:form>
First name: <h:inputText/><BR>
Last name: <h:inputText/><BR>...
<h:commandButton
value="Sign Me Up!"
action="#{healthPlanController.signup}"/>
</h:form>

27 J2EE training and tutorials: http://www.coreservlets.com


Steps 2 & 3: Example Results
• File is tomcat_dir/webapps/jsf-test/signup.jsp
• URL is http://localhost/jsf-test/signup.faces

28 J2EE training and tutorials: http://www.coreservlets.com

Step 4: Create an Action Controller


• Does not need to extend any particular
class
– Unlike Struts
• The designated method takes no arguments
and returns a String
– Each string should correspond to navigation rule in
faces-config.xml
– Returning null indicates that the original form should be
redisplayed
• Most controllers access request parameters
via beans
– Covered in next section
– Now, our business logic will be independent of input data
29 J2EE training and tutorials: http://www.coreservlets.com
Step 4: Example Code
package coreservlets;

public class HealthPlanController {


public String signup() {
if (Math.random() < 0.2) {
return("accepted");
} else {
return("rejected");
}
}
}

30 J2EE training and tutorials: http://www.coreservlets.com

Step 5: Update faces-config.xml


• Declaring action controller

<faces-config>
<managed-bean>
<managed-bean-name>
healthPlanController
</managed-bean-name>
<managed-bean-class>
coreservlets.HealthPlanController
</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>

</faces-config>

31 J2EE training and tutorials: http://www.coreservlets.com


Step 5: Update faces-config.xml
• Specifying navigation rules
– Outcomes should match return values of controller

<faces-config>

<navigation-rule>
<from-view-id>/signup.jsp</from-view-id>
<navigation-case>
<from-outcome>accepted</from-outcome>
<to-view-id>/WEB-INF/results/accepted.jsp</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>rejected</from-outcome>
<to-view-id>/WEB-INF/results/rejected.jsp</to-view-id>
</navigation-case>
</navigation-rule>

</faces-config>

32 J2EE training and tutorials: http://www.coreservlets.com

Step 6: Create Output JSP


Pages
• …/jsf-test/WEB-INF/results/accepted.jsp
<!DOCTYPE …>
<HTML>
<HEAD>…</HEAD>
<BODY>
<CENTER>
<TABLE BORDER=5>
<TR><TH CLASS="TITLE">Accepted!</TH></TR>
</TABLE>
<H2>You are accepted into our health plan.</H2>
Congratulations.
</CENTER>
</BODY></HTML>

33 J2EE training and tutorials: http://www.coreservlets.com


Step 6: Create Output JSP
Pages
• …/jsf-test/WEB-INF/results/rejected.jsp
<!DOCTYPE …>
<HTML>
<HEAD>…</HEAD>
<BODY>
<CENTER>
<TABLE BORDER=5>
<TR><TH CLASS="TITLE">Rejected!</TH></TR>
</TABLE>
<H2>You are rejected from our health plan.</H2>
Get lost.
</CENTER>
</BODY></HTML>

34 J2EE training and tutorials: http://www.coreservlets.com

Step 6: Results

35 J2EE training and tutorials: http://www.coreservlets.com


Step 7: Protect Raw JSP Pages

<web-app>

<security-constraint>
<display-name>
Prevent access to raw JSP pages that are for JSF pages.
</display-name>
<web-resource-collection>
<web-resource-name>Raw-JSF-JSP-Pages</web-resource-name>
<!-- Add url-pattern for EACH raw JSP page -->
<url-pattern>/welcome.jsp</url-pattern>
<url-pattern>/register1.jsp</url-pattern>
<url-pattern>/signup.jsp</url-pattern>
</web-resource-collection>
<auth-constraint>
<description>No roles, so no direct access</description>
</auth-constraint>
</security-constraint>
</web-app>

36 J2EE training and tutorials: http://www.coreservlets.com

Example 3:
Reading Request Data Manually
• Original URL will be
http://hostname/jsf-test/register2.faces
• When form submitted, one of three possible
results will be displayed
– Error message re illegal email address
– Error message re illegal password
– Success
• Main points
– Reading request data "by hand" in action controller

37 J2EE training and tutorials: http://www.coreservlets.com


Main Points of This Example
• Most real JSF apps use form beans
– Java classes that are created automatically and
automatically populated based on incoming request
parameters
• See next section
• This example reads by hand
– Requires slightly obscure code
• See next slide
– Done moderately frequently in real apps
• To read request headers or to access scoped variables that
are not related to form bean

38 J2EE training and tutorials: http://www.coreservlets.com

Step 1: Create Form Bean


• Postponed until next section
– This example reads form data the old fashioned way:
by directly calling request.getParameter.
– To do so, you first look up the ExternalContext, which
gives you access to the standard servlet and JSP objects
(request, response, ServletContext, session, etc.) Eg:
ExternalContext context =
FacesContext.getCurrentInstance().getExternalContext();
HttpServletRequest request =
(HttpServletRequest)context.getRequest();
String param1 = request.getParameter("name1");
– The ExternalContext also has methods to access params
and other data out hash tables.
• Request data
– getRequestParameterMap, getRequestCookieMap,
getRequestHeaderMap, etc
• Scoped variables
39 – getRequestMap, getSessionMap, getApplicationMap
J2EE training and tutorials: http://www.coreservlets.com
Step 2: Create Input Form
• Same as previous example, except
– h:form is given an id attribute
– h:inputText and h:inputSecret are also given an id
– The actual request param name is formID:elementID
• Only needed if you read request params manually!
• Example code
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<f:view>

<h:form id="form2">
Email address: <h:inputText id="email"/><BR>
Password: <h:inputSecret id="password"/><BR>
<h:commandButton value="Sign Me Up!"
action="#{registrationController.register}"/>
</h:form>…
</f:view>

40 J2EE training and tutorials: http://www.coreservlets.com

Step 3: Specify Action


Controller
• Use the action attribute of
h:commandButton
• Specify #{controllerName.methodName}
– Same as last example
• The designated method takes no args and returns various
strings. faces-config.xml maps the strings to output pages
• The controller must be declared with a managed-beans
entry in faces-config.xml
• Example
<h:form id="form2">
Email address: <h:inputText id="email"/><BR>
Password: <h:inputSecret id="password"/><BR>
<h:commandButton value="Sign Me Up!"
action="#{registrationController.register}"/>
</h:form>

41 J2EE training and tutorials: http://www.coreservlets.com


Steps 2 & 3: Example Results
• File is tomcat_dir/webapps/jsf-test/register2.jsp
• URL is http://localhost/jsf-test/register2.faces

42 J2EE training and tutorials: http://www.coreservlets.com

Step 4: Create an Action Controller


• Does not need to extend any particular
class
– Unlike Struts
• The designated method takes no arguments
and returns a String
– Each string should correspond to navigation rule in
faces-config.xml
– Returning null indicates that the original form should be
redisplayed
• Most controllers access request parameters
via beans
– We have not covered this yet
– We will use the manual approach via the ExternalContext
– ExternalContext useful in real life anyhow
• Cookies, session data, request headers, etc.
43 J2EE training and tutorials: http://www.coreservlets.com
Step 4: Example Code
package coreservlets;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.faces.context.*;

public class RegistrationController {


public String register() {
ExternalContext context =
FacesContext.getCurrentInstance().getExternalContext();
HttpServletRequest request =
(HttpServletRequest)context.getRequest();
String email = request.getParameter("form2:email");
String password = request.getParameter("form2:password");
if ((email == null) ||
(email.trim().length() < 3) ||
(email.indexOf("@") == -1)) {
return("bad-address");
} else if ((password == null) ||
(password.trim().length() < 6)) {
return("bad-password");
} else {
return("success");
44 }}} J2EE training and tutorials: http://www.coreservlets.com

Step 5: Update faces-config.xml


• Declaring action controller

<faces-config>
<managed-bean>
<managed-bean-name>
registrationController
</managed-bean-name>
<managed-bean-class>
coreservlets.RegistrationController
</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>

</faces-config>

45 J2EE training and tutorials: http://www.coreservlets.com


Step 5: Update faces-config.xml
• Specifying navigation rules
– Outcomes should match return values of action

<faces-config>

<navigation-rule>
<from-view-id>/register2.jsp</from-view-id>
<navigation-case>
<from-outcome>bad-address</from-outcome>
<to-view-id>/WEB-INF/results/bad-address2.jsp</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>bad-password</from-outcome>
<to-view-id>/WEB-INF/results/bad-password2.jsp</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>success</from-outcome>
<to-view-id>/WEB-INF/results/result2.jsp</to-view-id>
</navigation-case>
</navigation-rule>
</faces-config>

46 J2EE training and tutorials: http://www.coreservlets.com

Step 6: Create Output JSP


Pages
• …/jsf-test/WEB-INF/results/bad-address2.jsp
<!DOCTYPE …>
<HTML>
<HEAD>…</HEAD>
<BODY>
<CENTER>
<TABLE BORDER=5>
<TR><TH CLASS="TITLE">Illegal Email Address</TH></TR>
</TABLE>
<P>
Address must be of the form username@host.
Please <A HREF="register2.faces">try again</A>.
</CENTER>
</BODY></HTML>

47 J2EE training and tutorials: http://www.coreservlets.com


Step 6: Example Result for Bad
Email Address
• Input

48 J2EE training and tutorials: http://www.coreservlets.com

Step 6: Example Result for Bad


Email Address
• Output

49 J2EE training and tutorials: http://www.coreservlets.com


Step 6: Create Output JSP
Pages
• …/jsf-test/WEB-INF/results/bad-
password2.jsp
<!DOCTYPE …>
<HTML>
<HEAD>…</HEAD>
<BODY>
<CENTER>
<TABLE BORDER=5>
<TR><TH CLASS="TITLE">Illegal Password</TH></TR>
</TABLE>
<P>
Password must contain at least six characters.
Please <A HREF="register2.faces">try again</A>.
</CENTER>
</BODY></HTML>

50 J2EE training and tutorials: http://www.coreservlets.com

Step 6: Example Result for Bad


Password
• Input

51 J2EE training and tutorials: http://www.coreservlets.com


Step 6: Example Result for Bad
Password
• Output

52 J2EE training and tutorials: http://www.coreservlets.com

Step 6: Create Output JSP


Pages
• …/jsf-test/WEB-INF/results/result2.jsp
<!DOCTYPE …>
<HTML>
<HEAD>…</HEAD>
<BODY>
<CENTER>
<TABLE BORDER=5>
<TR><TH CLASS="TITLE">Success</TH></TR>
</TABLE>
<H2>You have registered successfully.<BR>
(Version 2)</H2>
</CENTER>
</BODY></HTML>

53 J2EE training and tutorials: http://www.coreservlets.com


Step 6: Example Result for
Good Input
• Input

54 J2EE training and tutorials: http://www.coreservlets.com

Step 6: Example Result for


Good Input
• Output

55 J2EE training and tutorials: http://www.coreservlets.com


Step 7: Protect Raw JSP Pages

<web-app>

<security-constraint>
<display-name>
Prevent access to raw JSP pages that are for JSF pages.
</display-name>
<web-resource-collection>
<web-resource-name>Raw-JSF-JSP-Pages</web-resource-name>
<!-- Add url-pattern for EACH raw JSP page -->
<url-pattern>/welcome.jsp</url-pattern>
<url-pattern>/register1.jsp</url-pattern>
<url-pattern>/signup.jsp</url-pattern>
<url-pattern>/register2.jsp</url-pattern>
</web-resource-collection>
<auth-constraint>
<description>No roles, so no direct access</description>
</auth-constraint>
</security-constraint>
</web-app>

56 J2EE training and tutorials: http://www.coreservlets.com

Value-Change Listeners
• All our examples used action attribute of
h:commandButton
– Specifies code to run when form is submitted
• How to respond to finer-grained events?
– Textfield value has changed
• E.g., fill in state automatically when ZIP code is entered
– New list entry has been selected
• Necessary ingredients
– Specify code that should run
• <h:inputText valueChangeListener="#{bean.method}"…/>
• Note that code runs before action controller
– Force form to be submitted when events occur
• <h:inputText valueChangeListener="#{bean.method}"
onclick="submit()" …/>
57 J2EE training and tutorials: http://www.coreservlets.com
Summary
• Basic steps to using JSF
– Create a bean to represent the form data
– Use f:view and h:form to create an input form
– Specify action controller with action of h:commandButton
– Create an action controller
– Use faces-config.xml to declare form bean and navigation rules
– Create JSP pages corresponding to each return condition
– Protect raw JSP pages from access
• Static navigation
– Specify outcome as action of button
• Outcome mapped by faces-config.xml to output page
• Dynamic navigation
– Specify method as action of button
– Method returns outcomes
• Outcomes mapped by faces-config.xml to output pages

58 J2EE training and tutorials: http://www.coreservlets.com

© 2005 Marty Hall

Questions?

Core Servlets & JSP book: www.coreservlets.com


More Servlets & JSP book: www.moreservlets.com
Servlet/JSP/Struts/JSF Training: courses.coreservlets.com
http://courses.coreservlets.com/ -- Hands-on, customized training for Java, servlets, JSP, Struts, and JSF.

Vous aimerez peut-être aussi