Servlet 1A:创建一个简单的客户注册servlet
Servlet 1B:改进客户注册servelt
Servlet2:JKToys Login和Toy Display Servlets
JSP3:JSP页面
JSPBeans4:JavaServer Pages with Beans
Studio5:WebSphere Studio Servlet Wizards
JDBCLab:用JDBC访问一个数据库
安全:Servlet安全
Servlet 1A:创建一个简单的客户注册servlet
在这个练习里,你将创建一个简单的servlet。这个servlet的目的是确认一个新客户在JKToys站点的成功注册。
在这个练习里你将访问JKToys网站。你从主页选择Kids Zone区域,注册为一个新客户。注册表格和网站的余下部分已经由网站设计者为你提供。在注册表格上按Register me now!按钮,将会调用服务器上的JKRegistration servlet。注册servlet将会用表格的信息注册一个用户,在JKTOYS数据库的CUSTOMER表创建一个新的条目。在注册时,指派一个唯一的客户号码。在下一个练习里你将用这个信息提供一个定制的 HTML页面确认注册。
第一部分
对所有的练习,确信你用了id:USERID和口令:PASSWORD登录进系统。
这个练习的前5个步骤只是给你的信息,请花时间读它们。实际的代码从第二部分开始。
1. 你将用IBM VisualAge for Java,version 2,Enterprise Edition+Enterprise Update(从现在开始用VAJ指代)来编辑,编译和发表你的练习。现在双击桌面上的图标打开VAJ产品。如果你看到了VAJ的欢迎窗口,选择“取消”按钮,这将放上Workbench窗口。你将看到带有几个包的WASDev项目已经为你预先装载了。选择com.ibm.waslab.servlet1包。在这个包里你将能看到RegistratonServlet类。这个类包含RegistrationServlet和startup methods required for you to complete this exercise.在这个练习里,你将集中注意力于doPost()方法。
2. JKToys网站已经布置到安装在你的机器上的web server上。这个站点的根目录是:[x]:\www\HTML\JKToys;[X]是web server所在的驱动器字母。网站是用NetObjects Fusion创建的。这个站点的HTML页面在html子目录里。JKToys注册页面的完整的路径是:[x]:\WWW\HTML\JKToys\html\body_jktoys_kids_zone_registraiton.aspl
3. 你还记得在讲座里提到,当用户按了一个表格上的提交按钮,在我们这里按钮上写着“Register me now!”,浏览器调用HTML <FORM>标记的ACTION子句里规定的URL。看一下HTML文件你将看到<FORM>标记包含以下文字:
<FORM NAME=”RegistrationForm”
ACTION=http://127.0.0.1/servlet/JKRegister METHOD=POST>
向网站设计者提供<FORM>标记的正确的参数是你的责任。
在ACTION子句里的URL指向主机127.0.0.1,这是一个指向本地主机的IP地址,换句话说,指向你自己的机器。网站设计者也许用“localhost”这个字代替IP地址。
注意:在某些情况下用”localhost”会降低你的服务器的性能,为了避免这个问题,你必须在你的机器里的一个叫HOSTS的文件里加一行,把localhost和IP地址对应起来。
在URL里跟在IP地址后面的servlet应用键,使得web server调用WebSphere Applicaiton Server(把HTTP请求传给它)。应用服务器在servlet classpath里配置的某个子目录或jar文件里定位servlet。IBM WebSphere Application Server的缺省servlet classpath是:[x]:\WebSphere\AppServer\servlets目录,[x]是WebSphere安装的目录。
JKRegister是servlet名字。这不是servlet的真实名字,而是在WebServer里给servlet的名字。在实践中,给servlets名字是很常见的,它降低显示在浏览器上的URL的复杂性和混乱。它还允许你向WebSphere里的你的servlet提供初始参数。按我们的配置,JKRegister被对应为:
com.ibm.waslab.servlet1.RegistrationServlet,这是这个servlet的包和类的名字。
METHOD=POST子句表示在servlet被调用时,servlet的doPost()方法将被service()方法调用。
4. 正如上面提到的,你的servlet将在doPost()方法里取得控制。选择RegistrationServlet。定位doPost()方法。注意给doPost的两个参数,HttpServletRequest和HttpServletResponse。这些对象让你找出你从哪里来,到哪里去,输入HTML表格的值是什么。在下面的步骤里你将学会怎样用它们。
5. doPost的开头几行提取输入到表格中的值,在数据库里注册新客户。现在你不需要关心哪个方法实际在数据库里注册了用户,在以后的练习里,你将自己写这个方法,你将学几个访问数据库的方法。你将在这个练习的后半部分写getFormImput()方法。
//Get the input form contents
Properties formInput=getFormInput(req);
//Register new customer in the database
register(formInput);
第二部分
在这个servlet里,当doPost()得到控制,第一个任务是取得用户输入和注册新客户。这是前面讨论的两行代码所作的。最后一步是通知客户注册成功了。
你在第二部分的任务是写doPost()方法的余下部分,通知用户注册成功。现在打开doPost()方法,看看有些什么:
public void doPost(HttpServletRequest req,HttpServletResponse res)
throws ServletException,jiava.io.IOException
{
//Get the input form contents
Properties formInput=getFormInput(req);
//Register new customer in the database
register(formInput);
//Obtain a PrintWriter from response object
//Set content type on the response header
//Output confirmation message
return;
}
注释说明了你的代码将要执行的任务。第一个任务是从response取得一个PrintWriter.看看你的课堂笔记,找个例子。如果你还是不懂,看最后的答案部分。得到一个PrintWriter后,你将需要设置response header的内容类型为”text/html”。你再看以下课堂笔记,找个例子吧。
最后,你将需要输出确认消息。页面的标题应该是”Registration Complete”,包含以下文字:
Greetings:
Your registration has been recorded in the database.
Thank you for your interest in JKToys.
We look forward to serving you in the future!
你的课堂笔记给出了在一个Servlet里打印文字的例子。如果你不懂,看最后的答案部分。
按Ctrl-s,或从右键选项选择”“保存””,保存doPost().记住,在VAJ保存你的方法的时候也编译了你的方法。如果你在编译时得到错误,VAJ会警告你,有时会提供正确的方法名或正确的语法来建议你怎样改正错误。改正你的错误,保存它。如果你不能解决错误,请向你的辅导员请求帮助。
一旦编译完成而没有错误,你可以试试你的servlet。为了调试的目的,你将在VAJ里运行你的servlet。你的 workspace已经用IBM WebSphere Application Server libraries配置过了。用这个配置你可以在VAJ里使用HTML和servlets。这将允许你在VAJ环境里调试你的servlets.
测试你的第一个servelt的步骤
1. 在VAJ里启动web server。打开IBM WebSphere Test Enviroment项目,在包com.ibm.servlet里,选择SERunner类,按Run按钮来启动服务器。你可以看控制台来确定web server是否在运行。它将打印许多行输出。
2. 然后,启动你的浏览器,在URL输入域输入http://127.0.0.1/JKToys/index.aspl,按enter。这将显示JKToys的主页。从banner选择Kids Zone。在kids zone,单击拖着标语的飞机,这将把你带到注册页面。填入表格的信息,按注册按钮,几秒钟之后你应该得到servlet的回应,表示一个成功的注册。
如果你真的要确信你的记录已经在数据库里,你可以在JKTOYS数据库的CUSTOMER表上执行一个查询,来看看你自己。你可以在DB2命令行里,或DB2文件夹里的Control Center工具作这个。
在这个练习里你作了些什么
在这个练习里你创建了一个将被JKToys的新客户注册表格调用的servlet。你在doPost()方法里增加代码,来提供给客户的反馈,告诉他们注册成功。你现在理解了表格,servlet和回应HTML页面之间的控制流。
你在你的servlet里动态创建一个HTML页面。在下一个练习里你将使用客户提供的信息进一步定制servlet给客户的回应。你还将检查错误,如表格信息遗漏或数据库问题。
答案
在你能向客户送回信息以前,你需要创建一个输出流,你将用这个同浏览器通信。
HttpServletResponse对象有一个方法来获取这样一个流。把下面的代码加到doPost()方法里register()调用的下面:
//Obtain output stream form response object
PrintWriter out=res.getWriter();
也可以创建别的类型的流,要看什么会回到浏览器。PrintWriter尤其适用于送字符串,因为它有打印新行和在一个现存行后面打印的方法,分别是println()和print()。
现在你已经有一个输出流,你可以开始把你的消息送回浏览器。记住,你实际作的是编辑一个HTML页面,它将被在用户的浏览器上显示。任何有效的HTML页面都可以送回。
在你送实在的文字以前,你应该设置回应的MIME类型。你用setContentType()方法在回应头里设置内容类型。输入以下行:
//Set content type on the response header
res.setContentType(“text/html”);
你可以输入这些行完成你的HTML页面。
//Output confirmaiton message
out.println(“<html>”);
out.println(“<head><title>Registration Complete</title></head>”);
out.println(“<body>”);
out.println(“<h2><br><br>Greetings:</h2>”);
out.println(“<br>Your registration has been recorded in the database”);
out.println(“<br>Thank you for your interest in JKToys”);
out.println(“<br>”);
out.println(“<h2>We look forward to serving you in the future!</h2>”);
out.println(“</body></html>”);
最后,你可以刷新和关闭流。在一个servlet的末尾这么作是可以的,只要你知道这个servlet将不会被另一个servlet调用来提供输出到流。你会在以后的练习里看到,一个servlet可以调用另外的servlets,每一个servlet的输出只是要完成一个HTML页面的所有的信息的一部分。如果任何一个servlet关闭流,主调servlets将不能使用这个流来完成页面的变换。所以当你关闭输出流时要小心。输入以下的行来完成servlet的编码。
outflush();
//Closing the output stream is not really necessary
out.close();
注意: 在设置和在这些练习的指导里,你将会被要求编辑.properties文件。确信你没有使用Notepad。用Wordpad或别的编辑器,保存你的操作系统的原始的句子结束符。
所有的练习从JKToysDBInfo.properties文件获得访问数据库所需要的参数。在这个文件里,你会看到USERID和PASSWORD等等的定义。如果你需要改变这个文件来适应你的配置,可在以下目录找到:
d:\IBMVJava\ide\project_resources\WASDev
d:\WebSphere\AppServer\servlets
第一个在运行在VAJ里是用,第二个在运行在一个网络服务器时用。
Servlet 1B:改进客户注册servelt
在这个练习里你将在输入表格上执行错误检查,发现用户没有填写的字段。对这个例子来说,所有字段都是强制的。
你将从上一个servlet你丢下的地方开始这个练习。你将从草稿重写getFormInput()方法。这个方法从表格提取参数,返回一个Properties对象,用字段名字作键值。这个方法还检查空白或空参数。你将使用属性对象中的值来定制送回去的确认消息。
第一部分:创建getFormInput()方法
1. 在VAJ里打开com.ibm.waslab.Servlet1包里的RegistrationServlet。改变现存的getFormInput()方法的signature,增加以下的”throws”列表。
Private Properties getFormInput (HttpSErvletRequest req) throws
InvalidParameterException
{
}
注意:这个方法抛出一个InvalidParameterException,这个异常的类已经提供给你了,在VAJ的com.ibm.waslab.exception包里。
getFormInput()方法声明为private,返回一个Properties对象,它将包含表格的参数名字和值。它把HttpServletRequest对象作为一个参数,因为它包含原始形式的输入表格的值。
2. 然后,增加代码检查空白字段。
注意: 在这些指导里,因为印刷的行的长度限制,有很多行代码在纸上被分开了。你应该在VAJ里把 这些行作为一个单独的行输入。
Properties formInput=new Properties();
Enumeration fieldNames=req.gerParameterName();
while (filedNames.hasMoreElemenets())
{
String paramName=(String)fieldNames.nextElement();
String paramValue=(String)
req.getParamterValues(paramName)[0];
//check for missing parameters(code to be added)
if (paramValue.length()= =0)
throw(new InvalidParameterException(“Required parameter missing”));
formInput.put(paramName,paramValue);
}
return formInput;
确信你理解所有已经有的代码。第一行声明一个新的Properties对象,命名为formInput,来保存参数名字/值对。然后这个request对象的getParameterNames()方法是用来取得一个所有参数名字的枚举。这些是表格里字段的名字,将被用作formInput properties对象的键值。
反复通过枚举,你取得参数名字和值。你检查,来确保每一个值有一个比0大的长度。如果长度等于0,一个异常被抛出和处理。如果值的长度比0大,它被保存在formInput,用字段名字作为键值。当枚举里没有更多的元素时,循环结束,返回填好的forminput Properties对象。
保存getFormInput()方法,重新打开doPost()方法。
第二部分:处理InvalidParameterException异常
因为getFormInput()现在可以抛出一个异常,doPost()里的代码得处理这个情况,把这个方法的调用放在一个try/catch块里。修改调用getFormInput()的代码段来处理这个异常。如果异常被抛出,调用方法missingParameterResponse(),这将生成一个错误HTML页面。在异常的情况下,你还将需要从doPost()方法返回,结束事务,不写任何东西到数据库。
重新提交一个表格来测试代码,这次有一个参数(输入表格上的一个字段)没有填入。错误页面应该被显示,而不是你在前一个练习里编码的成功注册消息。
第三部分:产生一个定制的输出HTML页面
1. 在这个部分你将定制用户在记录被存入数据库后看到的信息。你会在页面里显示客户的名字和新指派给客户的客户号码。在register()调用后面输入以下的行:
//Output confirmation message
out.println(“<html>”);
out.println(“<head><title>Registration Complete</title></head>”);
out.println(“<body>”);
//modified or additional lines of code are in bold
out.print(“<h2><br>Greetings:”);
out.println(“formInput.getProperty(“FNAME”)+”</h2>”);
out.println(“<br>Your registration has been recorded in the database”);
out.println(“<br><br>You have been assigned customer number”<strong>” +formInput.gerProperty(“CUSTNO”)+”</strong>);
out.println(“<br>Thank you for your interest in JKToys”);
out.println(“<br><h2>We lo“确定” forward to serving you in the future!</h2>”);
out.println(“<a href=\””+ServletUtil.getClientURL(req)+” /jktoys/htm/jktoys_kids_zone.aspl\”>”+”Back to the kids Zone!!!</a><br>”);
out.println(“</body></html>”);
out.flush();
out.close();
return;
在这个练习里我们假设写数据库成功。在一个真实的应用里,你应该捕捉在register()方法里的任何异常,显示合适的错误信息。
注意字段的值怎样从属性里提取,插入被建立的HTML页面的正确的地方。
从底向上第四行建立一个回到注册页面的超链接。ServletUtil.gerClientURL(req)的调用返回URL的第一部分,包含一些东西,象:http://127.0.0.1。http和主机名从请求对象里得到,组合进一个字符串,用来形成这个这个页面的完整的URL。web server的端口号,如果不是80,也要带上。ServletUtil是一个在com.ibm.waslab.common包里向你提供的类。
2. Property对象里的客户号条目不是原始表格的一部分,而是插入register()方法。这个唯一的号码是从数据库得到的:查询一个现有的最大的客户号,再加1。
3. 保存doPost()方法。如果你在编译期间得到错误,返回到源代码,改正它们,再编译。如果你不能解决错误,请向你的辅导员请求帮助。
4. 现在你已经准备好测试你的改进的servelt了。遵从前一个练习里的同样的测试步骤,通过正常的执行路径,输入所有的字段,按register,现在应该得到新的,改进的和定制的祝贺。
5. 再试一次,让一个或两个字段空白。按register按钮,你应该得到自己的错误HTML页面。
第四部分:输出和在VisualAge外面运行servlet
1. 要从“生产”环境运行Registration Servlet,先停止WebSphere Text Enviroment(SERunner)。
2. 从VisualAge输出类和源代码到WebSphere Application Server。选择WASDev项目,鼠标右击,选择Export。选择”directory”,击”Next”。按“Browse”按钮,浏览到c:\WebSphere\AppServer\servlets目录。选择.class和源代码。按Finish。这把类文件布置到AppServer。
3. 启动HTTP Server(Start->Programs->IBM HTTP Server->Start Server)。
4. 从浏览器访问Registration 页面,象以前一样重新运行Registration场景。
你在这个练习里做了些什么
在这个练习里,你学会怎样提取一个表格里的字段的值,和怎样把它们包含在属性里。你使用Properties对象里的信息来生成一个定制的动态的HTML页面。你也处理表格里提供的信息不全的情况,显示一个错误页。
答案
Properties formInput=null;
//Get the input form contens
try
{
formInput=getFormInput(req);
}
catch (InvalidParameterException e)
{
missingParameterResponse(out);
return;
}
Servlet2:JKToys Login和Toy Display Servlets
在这个练习里你将学会在一个web会话里用户在许多HTML页面和servlets里移动时维护信息,完成一个事务。
按照这个练习,你将能用HttpSession类存储在会话里持续的信息。你还将学习怎样阻止在浏览器里缓存页面,来防止缓存的页面显示前一个会话或查询的错误信息。在这个练习里,你将建立三个servlets:
Login
DisplayToys
TotalPurchase
这个三个servlets的组合将满足用户的需求。对每一个方法,大多数答案代码可以在一个叫cutAndPaste的方法里找到。这将使你在敲键盘敲烦的时候能”cut-and-paste”。
第一部分:修改Login servlet来保存会话数据
1. 在VAJ选择com.ibm.waslab.Servlet2包。从这个包里选择LoginSevlet。在doPost()里定位包含下面文字的代码行:
//Enter code here to retrieve the customer number form the form data
2. 在那个下面输入以下的代码。这个代码调用getCustomerNumber()方法来提取输入到HTML表格里的客户号。如果Login按钮被按下时客户号码为空,这个方法抛出一个InvalidParameterException;你用outputLoginFailedMessage()显示一个错误消息来处理这个异常。
String customerNumber=null;
try
{
customerNumber=getCustomerNumber(req);
}
catch (InvalidParameterException e)
{
//Output failure message
outputLoginFailedMessage(out,”You did not enter your customer number.”);
return;
}
3. 保存方法。你将得到错误,因为getCustomerNumber()和outputLoginFailedMessage()方法还没有被编码。带着错误保存方法,继续下一步。
4. 创建getCustomerNumber()方法。这个方法比前一个练习里的同名方法简单得多,因为只有一个字段要从表格里取出来。以下行表示这个方法:
private String getCustomerNumber(HttpServletRequest req) throws
InvalidParameterException{
String paramValue=(String)
req.getParameterValues(“CUSTNO”)[0];
//check for missing parameters
if(paramValue.length()==0)
throws(new InvalidParameterException(“Required parameter missing”));
return paramValue;
}
保存这个方法,返回到doPost()。
5. 在doPost()方法的下一行,调用locate()方法来取出对应输入的客户号码的记录:
//Look for this customer in the database
JKToysCustomer customer=locate(customerNumber);
如果没有在数据库里发现客户,返回一个null客户对象,否则的话它包含先前为这个客户号码保存在数据库里的信息。
6. 如果客户号码没有在数据库里找到,你需要提供一个合适的消息。在locate()调用下面输入这些行:
//customer will be null if customer not found in the database
if(customer= =null)
{
//Output failure message
outputLoginFailedMessage(out,”The customer number you entered was not found in our database.”);
return;
}
7. 继续下面的代码,处理客户号码在数据库里找到的情况:
else
{
//create a session and store the customer information
HttpSession session=req.getSession(true);
//if there is an existing session,invalidate it and re-create
if (session.isNew()= = false)
{
session.invalidate();
session=req.getSession(true);
}
session.putValue(“CUSTOMER”,customer);
//Output confirmation message
//Ensure that no caching takes place in the browser
res.setHeader(“Pragma”,”no-cache”);
res.setHeader(“Cache-Control”,”no-cache”);
res.setDateHeader(“Expires”,0);
String clientURL=ServletUtil.getClientURL(req);
out.println(“<html>”);
out.println(“<head><title>Login complete</title></head>”);
out.println(“<body>”);
out.print(“<p><h2>Greetings:”);
out.println(customer.getFirstName()+” ”+customer.getLastName()+”,from “ +customer.getCity()+”</h2></p>”);
out.println(“<p>Welcome back to JK Toys</p>”);
out.println(“<p>what can we do for you? Please select from the options below:</p>”);
out.println(“<a href=\””+clientURL+” /jktoys/html/body_jktoys_kids_zone_change.jsp\”>Update registration information</a><br>” );
out.println(“a href=\””+clientURL+”/servlet/JKOrderStatus\”>Status of my last order</a><br>”);
out.println(“a href=\””+clientURL+”/servlet/JKDisplayToys\”>Display toys for my age group(“+customer.getAge()+”)</a><br>”);
out.println(“<p>Thank you for your interest in JKToys</p>”);
out.println(“</body></html>”);
}
else语句后面的最初几行创建了一个会话。然后你检查是否是一个新的会话。如果这个会话不是新的,就把它失效掉,再创建一个新的会话。现下你有一个有效的会话了。用一个键值CUSTOMER保存会话里的customer Properties。这个数据将被同一个会话里的servlets使用。
接下去的三行是必需的,确保浏览器不会缓存servlet产生的页面。如果页面被缓存,它将可能在页面上显示从一个以前的缓存页面得来的错误的信息。
在这一页有一些定制。我们用客户的全名和城市来祝贺客户。我们也提到他在哪一个年龄组,在:Display Toys for my age group choice。同前一个练习一样,这个客户的数据是用getProperty()方法从customer Properties对象里取出来的。如果你希望,你可以修改HTML标记来修改消息的内容或格式为你更喜欢的样子。
注意,Display Toys choice的href标记会调用JKDisplayToys servlet。你将在这个练习的下一个步骤里继续做这个servlet。
保存doPost()方法。现在我们已经准备好写outputLoginFailedMessage()方法来完成这个servlet。这个有用的方法在doPost()里已经被调用了两次,因为需要输出同样的HTML,这两个只有一个句子不一样。我们可以在doPost()方法里编写HTML代码,但是,这是更好的方法。在”New Method”SmartGuide里创建outputLoginFailed(PrintWriter out,Stirng message)方法,输入以下的代码:
/**
*This is method prints the generic failure page with a specified message
*@param out java.io.PrintWriter
*@param message java.lang.String
*/
private void outputLoginFailedMessage(PrintWriter out,String message)
{
out.println(“<html>”);
out.println(“<head><title>Login Failed</title></head>”);
out.println(“<body>”);
out.println(“<p><h2>Sorry,could not log you in</h2>”);
out.println(“<p><font color=\”red\”>+message+”</font></p>”);
out.println(“<p>Please press the <strong>Back</strong>button,and resubmit.</p>”);
out.println(“<p>Thank you for your interest in JKToys!!!”);
out.println(“</body></html>”);
out.flush();
}
一旦你输入这个代码,最后的错误应该从doPost()里消失。
确信服务器在VAJ里运行,测试Login servlet。
进入JKToys Kids Zone页面里工作。在有飞行的飞机的页面上,击右边的椭圆图案。它将把你带到Login表格。输入一个你知道的已经存在的客户号码,按Login按钮。你将得到servlet的祝贺。现在试一下无效的客户号码,和一个空白客户号码。确信你在每种情况下都得到了正确的消息。
第二部分:创建DisplayToysServlet servlet
它将搜查数据库,按照客户的年龄显示玩具。
在VAJ的Servlet2包里,你将找到建了一半的servlet。打开DisplayToysServlet类。
1. 因为这个servlet不是从一个表格调用的,而是从网页上的一个href HTML标记调用的,它的doGet()方法将从servlet的service()方法调用。在doGet()方法里开始工作。在设置回应头不要缓存的代码后面输入这些行:
//Get customer information from session
HttpSession session=req.getSession(false);
//We should have a previous session,otherwise it’s an error
if(session= = null)
{
//Output failure message
showError(out);
return;
}
2. 注意,这次你把false作为参数传递给获取session的调用。这么作会只返回一个已经存在的session,而不会创建一个session。如果一个存在的session被找到,下一步是获取包含在session里的JKToysCustomer对象,它包含了客户信息。这个信息是这个练习的第一部分里的LoginServlet存进去的。如果我们有一个session,但是用键CUSTOEMR找不到值,我们就有一个无效的session,你会显示一个错误,然后退出。
//Create a new Customer and initialize withthe value in the session
JKToysCustomer customer=(JKToyscustomer)session.getValue(“CUSTOMER”);
//We should have a customer stored in the session,if not ,it’s an error
if(custmer= =null)
{
//Output failure message
showError(out);
return;
}
3. 下一行findToys()方法。客户的年龄作为参数被传进去。在这个方法里,数据库被访问,返回一个玩具的向量(vector)。如果没有为这个特定的客户找到玩具,一个null vector被返回。
//Look for toys in the database for customer’s age group
Vector toys=findToys(customer.getAge());
4. 在检查了玩具的向量不为空,displayToys()方法被调用,来建立一个表,显示向量里的玩具。类toy在com.ib.waslab.common包里提供给你。displayToys()方法接受请求对象、玩具向量、客户对象和PrintWriter作为参数。在显示了玩具以后,向量被保存在会话里,以备客户要定购。
if(toys!=null)
{
displayToys(req,toys,customer,out);
session.putValue(“TOYS”,toys);
}
5. 输入以下代码完成displayToys()方法。研究这个代码,看看HTML表是怎样创建的,向量是怎样循环取得所有元素的。
out.println(“<head><title>Your JK Toys,toys selection</title></head>”);
out.println(“<body>”);
out.println(“<p><h2>”+customer.getFirstName()+”,here are the toys we have in stock for your age group(“+customer.getAge()+”)</h2></p>”);
//show selected toys in a table
out.println(“<BR>”);
//Create form to display and select toys
out.println(“<FORM NAME=\”JKPurchaseForm\” ACTION=\”” +ServletUtil.getClientURL(req)+”/servlet/JKTotalPurchase\” METHOD=POST>”);
out.println(“<TABLE>”);
out.println(“<TABLE BORDER=1”);
out.println(“<tr bgcolor=#993366>”);
out.println(“<TH><font color=#FFFFFF>Picture</font>”);
out.println(“<TH><font color=#FFFFFF>Name</font>”);
out.println(“<TH><font color=#FFFFFF>Description</font>”);
out.println(“<TH><font color=#FFFFFF>Price</font>”);
out.println(“<TH><font color=#FFFFFF>Buy it?</font>”);
Enumeration selectedToys=toys.elements();
Toys aToy=null;
int checkboxNumber=0;
while (selectedToys.hasMoreElements())
{
aToy=(Toy)selectedToys.nextElement();
out.println(“<TR>”);
out.println(“<TD><IMG SRC=\”..”+aToy.getThumbnailFile()+”\”>”);
out.println(“<TD>”+aToy.getShortDescription());
out.println(“<TD>”+aToy.getFullDescription());
out.println(“<TD>”+aToy.getPriceAsString());
String boxId=String.valueOf(checkboxNumber++);
out.println(“<TD><INPUT id=”+boxId+”\”TYPE=\”checkbox\” NAME=\””+boxId+”\”>” );
}
out.println(“</TABLE>”);
out.println(“<br><br>”);
out.println(“<INPUT TYPE=\”submit\” NAME=\”PurchaseButton\” VALUE=\”Purchase selected toys\” id=\”PurchaseButton\”>”);
out.println(“</FORM>”);
out.println(“<p><h2>Thank you for your inteest in JKToys!!!</h2>”);
out.println(“</body></html>”);
out.flush();
out.close();
return;
}
注意,显示玩具的table是在一个form里建立的。table的最后一列包含一个checkbox,用来让客户选择哪些玩具他希望购买。checkbox的名字是从0开始的顺序的数字。它们将被用作索引,从包含所有玩具的向量里提取选择的玩具。这将在TotalPurchaseServlet里作。
每一个玩具的价格存在玩具对象里,这个值作为float保存。floats的格式不能被正确地打印,所以toy类有一个方法getPriceAsString(),它执行格式化。
保存这个方法。
6. 最后,创建一个showError()方法。它将在会话信息不管因何种原因无效时被调用。输入以下行实现这个方法。
private void showError(PrintWriter out){
//Outpur failure message
out.println(“<head><title>JK Toys error page</title></head>”);
out.println(“<body>”);
out.println(“<p><h2>Sorry,we don’t have a record of your customer name make a valid selection</h2>”);
out.println(“<p>Please press the <strong>Back</strong>button,and resubmit.</p>”);
out.println(“<p>Thank you for your interest in JKToys!!!”);
out.println(“</body></html>”);
out.flush();
return;
}
保存这个方法。
这个完成了DisplayToysServlet。你应该登录进JKToys Kids Zone,在确认你成功登录进站点的页面上选择Display toysfor my age group链接,以测试这个servlet。
第三部分:完成servlet,显示在DisplayToysServlet里选中的玩具
这个servlet,TotalPurchaseServlet,也总计选中的玩具的价格,显示总数。客户有个机会确认购买,完成定单,或取消定单。
1. 从com.ibmwaslab.servlet2包里选择TotalPurchaseServlet。打开doPost()方法。最初的几行已经为你输入好了:
//Obtain output stream from response object
PrintWriter out=res.getWriter();
//Set content type on the response header
res.setContentType(“text/html”);
//Get the session and retrieve the customer information
HttpSession session=req.getSession(false);
Vector toys=(Vector)session.getValue(“TOYS”);
这些行执行一些你在前面建立的servlets里作的典型的任务。PrintWriter从resposne 对象里取得,你设置MIME内容,从会话里取得玩具向量。这个向量包含所有这个客户的年龄组的玩具。
2. 在下面的步骤里,你将向doPost()方法里增加代码来完成功能。在doPost()输入:
//declare an object to hold the toy order
JKToysOrder toysToBuy=null;
//Get the input form contents
toysToBuy = getFromInput(req,toys);
实现你声明一个null JKToyOrder对象。这个对象将包含客户选择的玩具的集合。JKToyOrder类还包含定单的价格总和。
下一行调用了getFormInput()方法,你将在下一个步骤里建立它。保存doPost()方法,你将得到错误,因为你还没有些getFormInput()方法。继续,带着错误保存。
3. 创建getFormInput()方法。这个方法循环通过前面表格里的checkboxs,用checkbox的名字作为玩具向量的索引。被选中的玩具被加进定单。
注意:只有被选中的(checked)checkbox被从表格发送回servlet。submit按钮也被传送。按钮的值被忽略,看下面的代码。
输入下面的代码来完成getFormInput()方法。
private JKToyOrder getFormInput(HttpServletRequest req,Vector allToys){
JKToyOrder selectedToys =new JKToyOrder();
Enumeration fieldName=req.getParameterName();
int i=0;
while (fieldNames.hasMoreElements())
{
String paramName=(String)fieldName.nextElement();
String paramValue=(String)req.getParameterValue(paramName)[0];
try
{
int toyIndex=Integer.parseInt(paramName);
selectedToys.addToy(allToys.elementAt(toyIndex));
}
catch (NumberFormatException e)
{
//Skipping the button widget,only interested in checkboxes
//with numeric names
}
}
return slectedToys;
}
你也许要看看JkToyOrder类,看它是怎么工作的,它可以在com.ibm.waslab.common包里找到。
保存方法,打开doPost()。
4. 在你输入的最后一行后面,继续建立doPost()方法,输入以下的代码:
//toysToBuy order will be empty if nothing was selected
if(toysToBuy.isEmpty())
{
//Output failure message
out.println(“<html>”);
out.println(“<head><title>No toys selected</title></head>”);
out.println(“<body>”);
out.print(“<p><h2>Nothing selected</h2>”);
out.println(“<p><font color=\”red\”> You did not select any toys!</font></p>”);
out.println(“<p>Please press the <strong>Back</strong> button,and resubmit.</p>”);
out.println(“<p>Thank you for your interest in JKToys!!!”);
out.println(“</body></html>”);
out.flush();
return;
}
else
(
displaySelections(res,req,toysToBuy);
}
这段代码检查是否至少有一个玩具被选择,如果toysToBuy定单是空的,一个错误消息被显示。如果定单不空,displaySelections()方法被调用来显示一个包含被选中的玩具的表。
保存doPost()方法,应该没有错误。
5. 完成displaySelections()方法。这个方法同DisplayToysServlet里的displayToys方法很相似,有一些差别。输入以下的代码来完成TotalPurchase Servlet类里的一个新的方法:
//Enumeration selectedToys=toys.elements();
Toy aToy=null;
while(!toys.isEmpty())
{
aToy=(Toy) toys.getNextToy();
out.println(“<TR>”);
out.println(“<TD><IMG SRC=\”..”+aToy.getThumbnailFile()+”\”>”);
out.println(“<TD>”+aToy.getShortDescription());
out.println(“<TD>”+aToy.getFullDescriptin());
out.println(“<TD>”+aToy.getPriceAsString());
}
out.println(“<TR>”);
out.println(“<TD><INPUT TYPE=\”submit\=”
NAME=\”ConfirmationPurchaseButton\”
VALUE=\”Confirmation Purchase\”
id=\”ConfirmPurchaseButton\”>”);
out.println(“<TD><strong>Your total purchase amount is:</strong>”);
out.pritnln(“<TD><strong>”+toys.getTotalPriceAsString()+”</strong>”);
out.println(“</TABLE>”);
}
6. 这个方法负责显示选中的玩具,和显示定单的总价格。这里有两个按钮在表格上,一个处理购买,另一个取消定单;在这个课程里这两个按钮调用的servlets没有被实现,学生在业余时间实现它们。
在这个练习里你处理客户登录,基于客户的年龄显示一个选中的玩具的表,让客户从列表里选择玩具,显示最后的选中的玩具的总和和这次定购的总额。你学习了怎样用会话保持servlets和HTML页面之间的状态。你还学会了几种从一个表格里显示和选择项目的方法。
JSP3:JSP页面
在这个练习里你将创建一个JavaServer Page。servlet的目的是允许Customer Profile更新信息。
客户登录进JKToys的Kids Zone部分以后,客户可以作的一个选择是:更新他们的客户信息。为了处理这个,我们需要一个网页显示这个JKToysCustomer对象相联系的每一个信息(客户ID除外),作为编辑区域。这些字段需要用当前的客户记录的数据初始化。这些字段将是一个客户记录更新表格的一部分,将被提交给ProfileUpdateServlet来处理。
第一部分:JSP文件
1. 你将从一个HTML文件开始,[x]:\www\html\JKToys\html\body_jktoys-kids_zone_change_start.aspl(x是Lotus Domino Go Webserver安装的驱动器字母,文件是用NetObjects Fusion编辑的。第一步是在你的浏览器里打开这个文件(作为本地文件)。你看到的是一个Table,字段对应JKToysCustomer properties。每一个字段(加上一个隐藏字段)都需要在运行时用live Customer data生成。我们将用JSP语法作这个。
2. 用NetObjects ScriptBuilder打开HTML文件(Start->Programs->NetObjects ScriptBuilder->NetObjects ScriptBuilder 3.0)。ScriptBuilder是一个简单的(基于文字的)编辑器,重要用来编辑基于HTML的文件(.aspl,.asp,.jsp,.shtml,....)在这个文件里你将注意到几个HTML注释表明需要使用JSP代码的地方。定位第一个这样的注释,看上去应该象:
<!—The value field should be the Customer Number retrieved from the Customer object saved in the Session -->
你将需要先写一个scriptlet来从HttpSession里取得JKToysCustomer。下面的代码使用了JSP声明和JSP Scriptlet来完成这个:
<%@ import=com.ibm.waslab.common.* %>
<%KToysCustomer CUSTOMER=(JKToysCustomer)request.getSession(false).getValue(“CUSTOMER”);%>
Declaration provides a global import for the Servlet which will be generated during page compilation.这样com.ibm.waslab.common包里的类在我们的JSP scriptlet和expression里就不要用全名了。把以上的行加进HTML文件。
3. 然后,我们将提供JKToysCustomer对象的customerNumber属性作为HIDDEN输入域的值。在同INPUT 标签的VALUE=element相联系的引号里加入以下的JSP表达式:
<%=CUSTOMER.getCustomerNumber() %>
4. 我们现在需要把每一个字段放进表,用从CUSTOMER对象里来的它们当前的值。当你往下扫过文件,你会发现几个HTML标记看上去象:
<INPUT id=”FormsEditFieldx” TYPE=”text” NAME=”xxx” VALUE=”” SIZE=....>
对这些中的每一个,你要在值域的引号之间增加一个JSP表达式。(看注释一确定你应该输入什么。有6个这样的字段。)
5. 我们要显示的最后一个输入域是一个下拉列表,让客户选择年龄组。This needs to be populated and have the “currently” specified age group,”selected”.这需要更多的scriptlet来有效地作这个。你所要的是产生5个<OPTION>标签标记(在<SELECT>块的里面)。每一个标签应该是“Under 5”,”5-7”,”7-9”,”9-13”和”Over 13”。接着,这些”subtags”中有一个应该看上去这样:
<OPTION SELECTED> age_group
这个规定了初始选中的项目。
第二部分:发表、运行和调试JSP
1. 把改过的文件保存在[x]:\www\html|JKToys\html\body_jktoys_kids_zone_change.jsp。注意,这个文件名和文件类型同提供的不一样。
2. 接着输出你的WASDev项目(VA Java)到两个分开的目录树里(x;\WebSphere\AppServer\servlets和x:\IBMVJava\ide\project_resources\IBM WebSphere Test Enviroment\servlets).(第一个目录允许在VA Java外面运行,而第二个在SERunner的类路径里。)
3. 下面通过网站,登录进Kids Zones。(你应该从VAJava里面运行SERunner。)在登录确认页面,你选择Update registratio information。一旦你选择了这个,IBM WebSphere Application Server将调用页面编译。一个servlet将从.jsp文件产生,这个servlet将被编译。如果编译有错误(因为你的.jsp文件里的不合法的JSP语法),编译错误消息将在一个HTML错误页里返回(回到浏览器)。(注意,”Retrieve syntax error infromation”必须在JSP Execution Monitor Options dialog里被启用。选择Workspace->Tools->JSP Execution Monitor;等对话框窗口出现;确认”Retrieve syntax error infromation” checkbox 被checked。)
你将需要查看任何一个编译错误消息,修改你的.jsp文件,重复处理。如果你被错误消息弄糊涂了,你可以查看生成的servlet代码。这个代码被写进VA Java里的”JSP Page Compiler Generator Code”项目。
4. 在某些时候,如果你要调试你的JSP文件,你需要停止SERunner。确信”JSP Page Compiler Generator Code”项目被加进SERunner的类路径。重启SERunner,重新访问站点。就象你在你的servlet里加断点一样,你可以把它们加进JSP Servlet。
5. 你也许还希望看看页面编译的翻译过程。打开JSP Execution Monitor(JSP Execution Monitor Options dialog),在监视器里一步一步地访问.jsp文件。
你在这个练习里作了什么
这个练习说明了用JavaServer Pages来建立“HTML模板”文件,这个文件会被JSP页面编译过程转化为可执行的Servlets。得到的模板文件(.jsp)可以被WYSIWYG HTML编辑工具处理,可以用这个格式来维护而不是代码的格式(也就是servlet).
不同的JSP语法被试探,JSP Declaration,JSP Scriplets和JSP Expressions在被建立的.jsp文件里用到了。
答案
这里是建立下拉列表的scriptlet
<%
String[] ages=
{“Under 5”,”5-7”,”9-13”,”Over 13”};
for (int i=0;i<ages.length;i++){
out.print(“<OPTION”);
if ((CUSTOMER.gerAge().trim()).equals(ages[i]))
out.print(“SELECTED”);
out.println(“>”+ages[i]);
}
>
JSPBeans4:JavaServer Pages with Beans
在这个练习里,你将更新一个JavaBean,用它来在两个servlets之间通信,其中第二个servlet是从一个JavaServer Page生成的。JSP的目的是显示客户选择的可能要购买的玩具。
许多网页有复杂的布置。这很难在一个servlet里用编程的方法表达。这是服务器端脚本技术的主要理由。JSP编程模型的力量在于隔离可重用的组件(JavaBeans)里的应用代码,而不污染演示 HTML模板。
你将把一个JavaBean的一些状态扩展为属性,以便使用HTML模板语法的JSP容易地使用这个JavaBean。你还将用JSP标记和JSP HTML模板标记(insert、repeat)来扩充HTML文件。
第一部分:更新JKToys JavaBean
1. 在这个练习里我们将在com.ibm.waslab.common包里工作。我们从在类JKToyOrder上打开一个类浏览器开始。选择这个类的BeanInfo view。这个类是用来维护当前选择的要定购的玩具的。It is populated by the TotalPurchaseServlet。我们会让一个JSP表示“order review”信息,而不是用现在输出HTML和动态内容的displaySelections方法。
为了做到这个,我们需要一个新的方法来以”JavaBean”风格的方式循环过一个选择的玩具。这需要把列表作为一个Indexed Property。
创建一个新的IndexedProperty,orderedToy,在类JKToyOrder里。为了做到这个,单击Property Wizard 按钮。在结果对话框check readable,writeable,indexed。提供Property的名字为orderedToy,选择Property的类型为com.ibm.waslab.common.Toy(这个将被向导转化为Toy[])。对我们的目的而言,uncheck规定property为bound的box。按Finish.
2. 返回到类的Methods或Hierarchy视图,来实现刚才生成的4个方法。如果你看类定义,你会发现一个新的实例变量名字叫fieldOrderedToy,类型为Toy[]。这个是Bean Property wizard产生的。这个实例变量并不需要,因为玩具已经存在一个实例变量叫orderedToys,类型为java.util.Stack。这个实例变量已经在类的其它地方被使用了。
删除变量fieldOrderedToy,保存类。你将会在步骤1里Property wizard产生的三或四个附属方法里得到错误标记。
3. 编辑四个附属方法的每一个。使用java.util.Stack和java.util.Vector的正确的方法(例如CopyInto,elementAt,setElementAt,....)来实现这个四个方法的每一个。(需要帮助的话,看这个练习的末尾的答案。)
应该注意到,setOrderedToy(int Toy)和getOrderedToy(int)方法也许会抛一个ArrayOutOfBoundsException异常。这个异常会触发JSP里<REPEAT>标记里循环的结束。
4. 验证类JKToyOrder实现java.io.Serializable。
5. 最后,输出com.ibm.waslab.common包到以下路径:
[x:]\IBMVJava\ide\project_resources\IBM WebSphere Test Environment\servlets
[x:]\WebSphere\AppServer\servlets(只有在要在VAJava外面运行时才需要)
第二部分:调用JSP
1. 打开com.ibm.waslab.servlet2.TotalPurchaseServlet。编辑doPost()方法。在方法的底部(在另一个块里),displaySelections被调用。这个需要被对JSP的调用掉换。第一步是把JKToyOrder对象放进HttpSession对象或HttpServletRequest对象。
为了把定单对象加进会话,增加以下的代码:
session.putValue(“OrderedToys”,toysToBuy);
注意,定单从会话里取得的键是”OrderedToys”。
可选的,JavaBean可以被放在请求范围(而不是会话),把setAttribute方法用在com.sum.serve.http.HttpServiceRequest对象。
2. 下一步是调用callPage()方法来调用JSP。CallPage在回应对象里被调用。callPage是一个com.sun.server.http.HttpServiceResponse类上的方法。你必须先把res造型为类型com.sun.server.http.HttpServiceResponse。以下的代码将足够了(如果你加了一个import语句到类定义里面)。
HttpServiceResponse response=(HttpServiceResponse) res;
response.callPage(“/JKToys/html/jktoys_display_order.jsp”,req);
第三部分:Populating the JSP
1. 最后一步是创建JSP文件。文件jktoys_display_order_start.jsp(在x:\www\html\JKToys\html目录里找到)包含从displaySelection方法里提取的HTML(从com.ibm.waslab.servlet2.TotalPurchaseServlet)。你将需要为”OrderedToys”Bean增加一个BEAN标记。这个可以被放在文件的任何地方(在Bean被引用以前)。
2. 你还将要把TABLE的<tr>块包进一个<REPEAT>块。用insert标记和JSP表达式把动态内容插入HTML表格。
注意:你还将要增加一个scriptlet来在REPEAT块的开头测试一个ArrayOutOfBoundsException(在任何静态内容被推到回应流里去以前)。也要注意,getPriceAsString()方法没有同一个Property(在JkToyOrder类或Toy类)联系起来。<INSERT>标记不能用来显示这个信息,除非方法在每一个各自的类里被prompted为一个Property。如果你不想”prompt“这个属性的话,这个方法可以用scriptlet来调用。
或者,你可以只用scriptlet和JSP表达式而不是<REPEAT>和<REPEAT>标记。
3. 保存更新过的JSP文件为jktoys_display_order.jsp。(注意新的文件名!!!)测试正确的操作。
你在这个练习里作了什么
在这个练习里,你作了一个典型的Servlet->Bean->JSP“控制”流。注意,JSP文件可以被HTML WYSIWYG编辑器维护和编辑(同在Java方法里管理它不一样)。
答案:
public Toy getOrderedToy(int index) throws
ArrayIndexOutOfBoundsException{
return (Toy)orderedToys.elementAt(index);
}
public com.ibm.waslab.common.Toy[] getOrderedToy(){
com.ibm.waslab.commom.Toy[] toys=new
com.ibm.waslab.common.Toy{orderedToys.size()};
orderedToys.copyInto(toys);
return toys;
}
public void setOrderedToy(int index,Toy orderedToy)throws
ArrayIndexOutOfBoundsException{
orderedToys.setElementAt(orderedToy,index);
}
public void setOrderedToy(com.ibm.waslab.common.Toy[] orderedToy)
{
orderedToys=new java.util.Stack();
int max=orderrdToy.length;
for (int i=0;i<max;i++)
orderedToys.addElement(orderedToy[i]);
}
JSP的答案参见:x:\www\html\JKToys\jspSolutions\jktoys_display_order_soln.jsp。
Studio5:WebSphere Studio Servlet Wizards
在这个练习里,你将使用IBM WebSphere Studio的一个向导来创建一个客户管理界面。产生的servlet和JSP页面是让站点管理员能查看数据库里所有JKToys客户的记录。按照这个这个练习,你将能用Studio servlet wizard来建立更复杂的servlets和JSP的骨架。
对所有的练习,确信你用USEID为id和PASSWORD为口令登录进去的。
1. 你将用IBM WebSphere Studio,version 1.0(从此以后用Studio指代),建立一个简单的管理应用。选择Start,Programs,IBM WebSphere,Studio 1.0,WebSphere Studio,启动Studio。这将带来Developer Workbench,Studio的主窗口。Workbench给出一个基于项目的网站视图。从File菜单选择New Project选项创建一个新的项目。在Name域,输入JKToys,按“确定”按钮。
2. 建立管理应用的第一步是建立一个SQL查询对象。为了做到这个,你将利用Workbench提供的SQL向导。从Tools菜单选择SQL Wizard...项目。
3. 向导显示为一个对话框,有一个”tabbed”象notebook的外观。当它最初弹出时,对话框显示Welcome页面,问将要产生的SQL命令文件的名字。输入ViewAllCustomer到name输入域,按Next按钮。
4. 在下一页(Logon),输入jdbc:db2:JKToys到数据库URL输入域;USERID到用户输入域;PASSWORD到口令输入域。在Driver输入域选择IBM DB2 UDB local。输入这些值以后,按Connect按钮。这会连接数据库,取得JKToys数据库的元数据。
5. 确信Select Statement 类型被checked。还check USERID.CUSTOMER表。按Next按钮。
6. 因为只有一个表,Join是不需要的。按Next按钮。
7. 对Columns入口,先按Select all按钮,然后是Add按钮。然后按Next.
8. 你在建立的查询要从数据库取得所有的行。所以,你不要where子句。按Next按钮。
9. 按Finish来完成SQL语句。
10. 接着我们将运行Data Access Servlet Wizard。为了启动向导,从Tools菜单选择Studio Wizard...项目。Studio Wizard是一个同SQL wizard相似的对话框。
11. 在Welcome tab,选择Data Access Servlet,按Next按钮。
12. 在“第二个”Welcome tab,输入ViewCustomer到Name输入域,按Next。
13. 在SQL语句页面,“select from an active project”选项应该被选中,ViewAllCustomers.sql应该被选中。按Next。
14. 在输入页,确信”Yes please”被选中。按Next。
15. 对InputLayout,输入:为Page title输入View All Customers;清除User prompt的输入;Check Submit button check box,把文字改为View All Customers;uncheck Reset button 输入。按Next。
16. 对输出页面接受缺省值(所有列)。按Next。
17. 在Heading输入域增加文字JKToys Customers.然后选择输出字段(每次一个),用上下箭头移动字段,使得它们可以以以下的顺序出现:LNAME,FNAME,AGE,ADDR,CITY,STATE,ZIP,CUSTNO。按Next。
18. 选择不要创建一个custom Error Page。按Next。
19. 按Finish来生成Servlet,DataBean,Output JSP和Input HTML文件。一旦确认对话框消失,你应该看到以下文件在Workbench在JKToys项目里:
ViewAllCustomers.sql;ViewCustomersOutputPage.jsp;ViewcustomersInputPage.aspl。如果你看
classes/JKToys文件夹,你还会发现:
ViewCustomersServlet.java;
ViewCustomersServlet.servlet;
ViewCustomersServletBean.java;
ViewCustomersServlet.class;
ViewCustomersServletBean.class。
20.在发表这个应用之前,编辑ViewCustomersServlet.servlet,在Workbench里双击这个文件。这个文件是XML的servlet配置文件。我们要改变”default-page”的路径。在第5行,在<URL>后面,插入”/JKToys/html”。缺省页面的路径现在应该是/JKToys/html/ViewCustomersOutputPage.jsp。
<span c