??xml version="1.0" encoding="utf-8" standalone="yes"?> 1.PageResultSet.java文g~译成class文gQƈ攑օ你的Web 2.在你的ActioncM: 3.在你的JSP面? <!-- 昄分页工具?--> <%=pageResultSet.getToolBar("list_users.do")%> //PageResultSet.java import java.util.*; /** public PageResultSet(Collection data) { public PageResultSet(Collection data, int curPage) { public PageResultSet(Collection data, int curPage, int pageSize) { /** /** /** /** /** /**
author: evan
email: evan_zhao@hotmail.com
背景Q?BR>1Q?nbsp; 需要将数据库查询结果在JSP中以列表方式昄
2Q?nbsp; 在一个良好的J2EE模式中数据库查询一般用DAO实现QData Access ObjectQ, JSP仅用于显C数?BR>
问题Q?BR> 通过JDBC ResultSet可获取查询结果(存在于数据库~冲区内Q,但在Statement、Connection关闭后ResultSet即不可用。因此需要一U方式取出所有查询结果ƈ传递至JSP面?BR>
解决Ҏ一Q?BR> 使用Value Object。将每条记录均封装成JavaBean对象Q把q些对象装入Collection传送给JSP昄。这U方法的~点是每一U查询都需要定义一个java classQƈ且将记录数据装成java对象时也需要很多额外的代码?BR>CZ代码Q?BR>
解决Ҏ?/B>Q?BR> 遍历ResultSet取出所有数据封装进Collection?BR>具体做法Q?BR>1Q?nbsp; 生成一个List对象(List list = new ArrayList() )?BR>2Q?nbsp; 生成一个Map对象(Map map = new HashMap() )。用Map装一行数据,key为各字段名,value为对应的倹{?map.put(“USER_NAME?, rs.getString(“USER_NAME?)
3Q?nbsp; 第2 步生成的Map对象装入W?步的list对象?list.add(map) )?BR>4Q?nbsp; 重复2?步直到ResultSet遍历完毕
在DBUtil. resultSetToList(ResultSet rs)Ҏ中实C上述q程Q所有列名均使用大写Q,可参考用?BR>
CZ代码Q?BR>
解决Ҏ?/B>Q?BR> 使用RowSet?BR>RowSet是JDBC2.0中提供的接口,Oracle对该接口有相应实玎ͼ其中很有用的是oracle.jdbc.rowset.OracleCachedRowSet?nbsp;OracleCachedRowSet实现了ResultSet中的所有方法,但与ResultSet不同的是QOracleCachedRowSet中的数据在Connection关闭后仍然有效?BR>
oracle的rowset实现?A >http://otn.oracle.com/software/content.html的jdbc下蝲里有Q名U是ocrs12.zip
CZ代码Q?BR>
适用场合Q?BR> Ҏ一使用于定制的查询操作
Ҏ二适用于多条查询语句或需要对查询l果q行处理的情c?BR> Ҏ三适合于单条查询语句,适用于快速开发?BR>
相关链接Q?BR> 如果需要分|C参考:JSP分页技术实?/A>
如果查询l果需要生成WORD或者EXCELQ请参考:使用jsp实现word、excel格式报表打印
附:DBUtil代码Q?BR>
]]>
应用E序的WEB-INF/classes/com/youngor/util目录下,可以对包名做相应修改?/P>
先从业务处理逻辑cM取出数据(ArrayList或Vector格式)
UserBO userBO=new UserBO();
Collection data=userBO.findUsers();//CZҎ
再得到当前页curPage和每记录数pageSize
int curPage = Integer.parseInt(request.getParameter(“cur_page?);
int pageSize=15;
然后生成PageResultSet对象
PageResultSet dataList = new PageResultSet(data, curPage, pageSize);
request.setAttribute("usersList", usersList);
<%
PageResultSet pageResultSet=(PageResultSet)request.getAttribute("usersList");
ArrayList usersList=(ArrayList)pageResultSet.getData();
for(int i=0;i<usersList.size();i++)
{
UserEO userEO=(UserEO)usersList.get(i);%>
<tr>
<td><a href="view_user.do?id=<%=userEO.getId()%>"><%=userEO.getUsername()%></a></td>
<td><%=userEO.getName()%></td>
<td><%=userEO.getPhoneNumber()%></td>
<td><%=userEO.getEmailBox()%></td>
<td><%=userEO.getAddress()%></td>
<td><%=userEO.getPostcode()%></td>
</tr>
<%}%>
</table></td>
</tr>
</table>
注意Q?BR>1、如果你觉得分页工具栏不能满你的要求,可以用PageResultSetcM的公共方?BR>first()、previous()、next()、last()定制自己的工hQƈ且,你还可以在PageResultSet中定义多个样式的工具栏;
2、getToolBar(String url)Ҏ接受带查询字W串的参敎ͼ比如“list_users.do?class_id=1“?/P>
package com.youngor.util;
* <p>Title: PageResultSet</p>
*
* <p>Description:分页c?</p>
*
* <p>Copyright: Copyright (c) 2004</p>
*
* <p>Company:youngor-studio(http://www.54youngor.com) </p>
* @author:伍维?BR> * @version 1.0
*/
public class PageResultSet {
/**
* 分页数据
*/
private Collection data = null;
/**
* 当前?BR> */
private int curPage;
/**
* 每页昄的记录数
*/
private int pageSize;
/**
* 记录行数
*/
private int rowsCount;
/**
* |
*/
private int pageCount;
this.data = data;
this.curPage = 1;
this.pageSize = 10;
this.rowsCount = data.size();
this.pageCount = (int) Math.ceil((double) rowsCount / pageSize);
}
this.data = data;
this.curPage = curPage;
this.pageSize = 10;
this.rowsCount = data.size();
this.pageCount = (int) Math.ceil((double) rowsCount / pageSize);
}
this.data = data;
this.curPage = curPage;
this.pageSize = pageSize;
this.rowsCount = data.size();
this.pageCount = (int) Math.ceil((double) rowsCount / pageSize);
}
* getCurPage:q回当前的页?BR> *
* @return int
*/
public int getCurPage() {
return curPage;
}
* getPageSizeQ返回分大?BR> *
* @return int
*/
public int getPageSize() {
return pageSize;
}
* getRowsCountQ返回总记录行?BR> *
* @return int
*/
public int getRowsCount() {
return rowsCount;
}
* getPageCountQ返回总页?BR> *
* @return int
*/
public int getPageCount() {
return pageCount;
}
/**
* W一?BR> * @return int
*/
public int first() {
return 1;
}
/**
* 最后一?BR> * @return int
*/
public int last() {
return pageCount;
}
/**
* 上一?BR> * @return int
*/
public int previous() {
return (curPage - 1 < 1) ? 1 : curPage - 1;
}
/**
* 下一?BR> * @return int
*/
public int next() {
return (curPage + 1 > pageCount) ? pageCount : curPage + 1;
}
* W一?BR> * @return boolean
*/
public boolean isFirst() {
return (curPage==1)?true:false;
}
* W一?BR> * @return boolean
*/
public boolean isLast() {
return (curPage==pageCount)?true:false;
}
/**
* 获取当前|?BR> * @return Collection
*/
public Collection getData() {
Collection curData = null;
if (data != null) {
int start = (curPage - 1) * pageSize;
int end = 0;
if (start + pageSize > rowsCount)
end = rowsCount;
else
end = start + pageSize;
ArrayList arrayCurData = new ArrayList();
ArrayList arrayData = null;
Vector vectorCurData = new Vector();
Vector vectorData = null;
boolean isArray = true;
if (data instanceof ArrayList) {
arrayData = (ArrayList) data;
isArray = true;
} else if (data instanceof Vector) {
vectorData = (Vector) data;
isArray = false;
}
for (int i = start; i < end; i++) {
if (isArray) {
arrayCurData.add(arrayData.get(i));
} else {
vectorData.add(vectorData.elementAt(i));
}
}
if (isArray) {
curData = (Collection) arrayCurData;
} else {
curData = (Collection) vectorCurData;
}
}
return curData;
}
/**
* 获取工具?BR> * @return String
*/
public String getToolBar(String fileName){
String temp="";
if(fileName.indexOf("?")==-1)
{
temp="?";
}
else
{
temp="&";
}
String str="<form method='post' name='frmPage' action='"+fileName+"'>";
str+="<p align='center'>";
if(isFirst())
str+="首页 上一?amp;nbsp;";
else
{
str+="<a href='"+fileName+temp+"cur_page=1'>首页</a> ";
str+="<a href='"+fileName+temp+"cur_page="+(curPage-1)+"'>上一?lt;/a> ";
}
if(isLast())
str+="下一?N ";
else
{
str+="<a href='"+fileName+temp+"cur_page="+(curPage+1)+"'>下一?lt;/a> ";
str+="<a href='"+fileName+temp+"cur_page="+pageCount+"'>N</a> ";
}
str+=" ?lt;b>"+rowsCount+"</b>条记?amp;nbsp;";
str+=" 转到<select name='page' onChange=\"location='"+fileName+temp+"cur_page='+this.options[this.selectedIndex].value\">";
for(int i=1;i<=pageCount;i++)
{
if(i==curPage)
str+="<option value='"+i+"' selected>W?+i+"?lt;/option>";
else
str+="<option value='"+i+"'>W?+i+"?lt;/option>";
}
str+="</select></p></form>";
return str;
}
}
]]>
<html>
<head>
</head>
<body onLoad="document.myForm.submit()">
<form action="https://loginserver.yourcorp.com/webapp/login
servlet" name="myForm" method="POST">
<input type="hidden" name="key" value="!@#$EncryptedString!@#$">
</form>
</body>
</html>
Listing 2
<html>
<head>
<title>Hello World
<SCRIPT language="JavaScript" SRC="https://partner1/servlet/LCMMSServlet/login?data=ALKSDFJQWER...JLQKWE">
</SCRIPT>
<SCRIPT language="JavaScript"
SRC="https://partner2/servlet/LCMMSServlet/login?data=ALKSDFJQWER...JLQKWE">
</SCRIPT>
<SCRIPT language="JavaScript">
function postForm() {
document.myForm.submit( );
}
</SCRIPT>
</head>
<body bgcolor=#FFFFFF onLoad="postForm()">
<form action="https://myserver/servlet/LCMMSServlet/authenticated"
method="POST" name="myForm">
<input type="hidden" name="data" value="ALKSDFJQWER...JLQKWE">
<input type="hidden" name="url" value="/requested/url?param1=val1¶m2=val2">
</form>
</body>
</html>
Listing 3
<FRAMESET ROWS="100%,0%,0%" onLoad="submitViewableFrameForm()">
<FRAME NAME="viewable" SRC="TempFrame.jsp">
<!--The frame below logs in the browser to partner1 -->
<FRAME NAME="setPartner1cookie"
SRC="https://partner1:7002/servlet/LCMMSServlet/login?data=ALKSDFJQWER...JLQKWE">
<!--The frame below logs in the browser to partner2 -->
<FRAME NAME="setPartner2cookie"
SRC="https://partner2:7002/servlet/LCMMSServlet/login?data=ALKSDFJQWER...JLQKWE">
</FRAMESET>
Listing 4
<html>
<head>
</head>
<body bgcolor=#FFFFFF>
Put some text here like "Logging in...Please wait."
<form action="https://myserver/servlet/LCMMSServlet/authenticated"
method="POST" name="myForm" target="_top">
<input type="hidden" name="data" value="ALKSDFJQWER...JLQKWE">
<input type="hidden" name="url" value="/requested/url?param1=val1¶m2=val2">
</form>
</body>
</html>
Listing 5
CryptTool ct = CryptToolFactory.getCryptTool( ... );
Properties p = new Properties ( );
... //Get user Id
String userId = ...;
p.setProperty ("uid",userId);
p.setProperty("anotherProp", someValue);
//The String returned is a hex encoded ciphertext
String encryptedInfo = ct.encrypt(p);
Cookie c = new Cookie ("SSO",encryptedInfo);
c.setMaxAge(-1);
c.setDomain(".yourDomain.com");
c.setPath("/");
//If this is a login server cookie and cookie has to be sent over SSL
c.setSecure(true);
//Send cookie to client
response.addCookie (c );
Listing 6
package jdj.sso.test;
import java.net.*;
import java.io.*;
public class HttPSocketClient {
public static void main(String[] args) throws Exception {
String host = null;
int port = -1;
String path = null;
for (int i = 0; i < args.length; i++)
System.out.println(args[i]);
if (args.length < 3) {
System.out.println(
"USAGE: java HttPSocketClient " +
"host port requestedfilepath");
System.exit(-1);
}
try {
host = args[0];
port = Integer.parseInt(args[1]);
path = args[2];
} catch (IllegalArgumentException e) {
System.out.println("USAGE: java HttPSocketClient " +
"host port requestedfilepath");
System.exit(-1);
}
try {
Socket socket = new Socket(host,port);
PrintWriter out = new PrintWriter(
new BufferedWriter(
new OutputStreamWriter(
socket.getOutputStream())));
out.println("GET " + path + " HTTP/1.1");
out.println();
out.flush();
BufferedReader in = new BufferedReader(
new InputStreamReader(
socket.getInputStream()));
String inputLine;
while ((inputLine = in.readLine()) != null)
System.out.println(inputLine);
in.close();
out.close();
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Listing 7
grant CodeBase "file:./Login.jar" {
permission java.security.AllPermission;
};
grant CodeBase "file:./HttpSocketClient.jar",
Principal javax.security.auth.kerberos.KerberosPrincipal
"your_kerb_username@your_realm" {
permission java.net.SocketPermission "*", "connect";
permission javax.security.auth.kerberos.ServicePermission
"krbtgt/your_realm@your_realm",
"initiate";
permission javax.security.auth.kerberos.ServicePermission
"server_service_principal@your_realm",
"initiate";
};
做到以上几点Q一个简单的打印功能实CQ?BR>
2
<%@ page contentType="application/msword;charset=GBK" %>
2.创徏weblogic服务 安装成功后,依次点击”开始?>”BEA WebLogic Platform 8.1?>”Configuration Wizard”,启动”BEA WebLogic Configuration Wizard?选择”create a new weblogic configuration”,然后”next?在”template”选择”base weblogic server domain?然后”next?没有特别的需要就不修攚w认选择Q然后”next”,在这个界面上输入用户名、密码和创徏q个服务的描qͼ误住这个用户密码它是启动这个服务和q入服务控制台的帐号Q?然后”next”,选择jdk的版?然后”next”,在这个界面你可以修改创徏服务的目录和名称Q然后按”create”开始创建?
3.创徏应用目录 创徏应用目录有两U方式分别介l如下:
3.1 最单的应用目录创徏 当weblogic服务创徏成功后,再次打开“Configuration Wizard”,q次选择”extend and existing weblogic configuration(扩展weblogic配置)?然后”next?选择weblogic服务目录Q然后”next?在”Configuration Extensions”中N”DefaultWebApp”,然后以下取默认g路”next”直到”import”就OK了。默认应用目录一般在D:\bea\user_projects\applications\mydomain\DefaultWebApp(注:d:为我PCZ安装weblogic的盘W,读者根据安装目录进行查??
试Q启动weblogic服务,然后输入http://localhost:7001可以看Cl性页面了?
3.2使用控制台创建应用目?当weblogic服务创徏成功?启动weblogic服务,然后在浏览器中输?A href="http://localhost:7001/console">http://localhost:7001/console 在登录界面输入你创徏weblogic服务时的用户密码?
a.创徏一个新目录做ؓ卛_发布的应用目录,我用d:\appweb做ؓ例了目录,在该目录下再创徏WEB-INF,在WEB-INF下面创徏web.xml文gQ目录结构ؓ
appweb
|__WEB-INF/web.xml
web.xml内容一般是:
<?xml version="1.0" ?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "<web-app>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</web-app>
b.目录创徏完成后,在控制台的首,选择“Web Application Modules?>”Deploy a new Web Application Module... ?在”Location”下选择你刚才创建的appweb(注意q个目录一定要有WEB-INF目录Qƈ且在WEB-INF目录下一定要有web.xml文gQ否则不能够创徏应用目录)。选择后,点击”target module”,q回Q可以看到当前weblogic服务中依成功创徏的应用目录了。然后再创徏一个index.html文档攑֜appweb目录下,完成了应用目录的创建?
试Q启动weblogic服务,然后输入http://localhost:7001/appweb/index.html可以看到自p|的面了?
2
二、开始部|J2EE web目
Web目的部|采用Configuration Wizard工具。依ơ点几Z开始?>“BEA WebLogic Platform
8.1?>“Configuration Wizard”,启动”BEA WebLogic Configuration Wizard”?/P>
2.1 新徏weblogic domain
选择”新建weblogic配置”,
然后”下一步?在”模李쀝选择”base weblogic server domain?
然后”下一步?没有特别的需要就不修攚w认选择Q然后”下一步”,在这个界面上输入用户名、密码(如填写用户wangnewtonQ密码wangnewtonQ和创徏q个服务的描qͼ误住这个用户密码它是启动这个服务和q入服务控制台的帐号Q?/P>
然后”下一步”,选择jdk的版?然后”下一步”,在这个界面你可以修改创徏服务的目录和配置名称Q然后按”创建”开始创建?/P>
点击完成l束?/P>
2.2 创徏应用目录Q创建应用目录有两种方式分别介绍如下
2.2.1Ҏ一Ql采用Configuration Wizard 创徏
q次选择”extend and existing weblogic configuration(扩展weblogic配置)?
然后“下一步?选择前面配置的weblogic配置目录mydomainQ然后“下一步?
在”Configuration Extensions”中N”DefaultWebApp”,
然后以下取默认g路”下一步”直到”导入”就OK了?BR> 默认应用目录一般在D:\bea\user_projects\applications\mydomain\DefaultWebApp(注:d:为我PCZ安装weblogic的盘W,读者根据安装目录进行查??
试Q启动weblogic服务,然后输入http://localhost:7001可以看Cl性页面了?/P>
2.2.2Ҏ二:使用控制台创建应用目?BR> 使用q个Ҏ最好先要有一个可以发布的web应用Q比如大家做q的Ztomcat的项目。没有的话至要有webapp\WEB-INF\web.xml文gQweb.xml文gcM于:
webapp
|__WEB-INF/web.xml
web.xml内容一般是:
<?xml version="1.0" ?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "<web-app>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</web-app>
创徏好如上的文gl构和web.xml后,启动weblogic服务,完成后在览器中输入http://127.0.0.1:7001/console
在登录界面输入你创徏weblogic服务时的用户密码QwangnewtonQ。然后sign inQ在下面的页面中选择“Web Application Modules?/P>
点击后进入该界面Q点击Deploy a new Web Application Module...
在“Location”下选择你刚才创建的appweb(注意q个目录一定要有WEB-INF目录Qƈ且在WEB-INF目录下一定要有web.xml文gQ否则不能够创徏应用目录)。选择后,点击”target module”,
q回Q可以看到当前weblogic服务中依成功创徏的应用目录了。然后再创徏一个index.html文攑֜appweb目录下,完成了应用目录的创建?/P>
试Q启动weblogic服务,然后输入http://127.0.0.1:7001/webapp/index.html可以看到自p|的面了?/P>
PSQ相Ҏ_Ҏ二是实际目中用最多的一U配|方法?BR> l箋PSQ如何设|项目ؓ默认启动路径Q?BR> 对于刚才创徏的应用目录,总要通过http://127.0.0.1:7001/webapp/index.htmlQ能否直接设|成通过http://127.0.0.1:7001/index.html可以讉K的方式呢Q?/P>
Ҏ是在webapp\WEB-INF下新Z个weblogic.xml文gQ?BR>webapp
|__WEB-INF/weblogic.xml
weblogic.xml内容一般是:
<!DOCTYPE weblogic-web-app PUBLIC "-//BEA Systems, Inc.//DTD Web Application 8.1//EN" "<weblogic-web-app>
<context-root>/</context-root>
</weblogic-web-app>
三、weblogic q接池的配置
实例背景QSQL-SERVER 2000的数据库 + JDBC3.0
3.1 配置q接?BR> q入控制?console) 可以看到该界?Q然后点?Connetion Pools
点击后进入该界面Q点击configure a new JDBC Conneciton Pool
点击后显C界面 因ؓ是用SQL-SERVER 2000 所以选择MS SQL Server
选择后,选择 MicroSoft’s MS SQL Server Driver (type) Version 。。。[倒数W三个^-^]然后点击Continue
按页面要求填入相关信?/P>
此步骤完成后Q进入该面
点击 Test Driver Configuration Q如果是成功昄该页
点击Create and deploy,
3.2 配置数据?BR> q入控制収ͼ点击date source。。?/P>
点击后显C?点击 configure a new JDBC Data Source
点击后进入该?/P>
以下步骤都是默认选择后显C?/P>
由此Q数据源配|成功了Q记住这里的JNDIName配置为MyJNDIQ稍后将会在E序中被使用?/P>
四、weblogic q接池测试程?BR> Z方便Q采用一个简单jspE序q行q接池的试Q连接池使用h不但可以提高pȝ吞吐量,而且q接E序也是很简单的。一般大家手头上都会有专业的数据库连接组Ӟ把这个组件修Ҏ为weblogicq接池的lg也是很简单的Q只需要执行简单几行代码替换就ok了。但作ؓ新手Q可以通过q个jsp试刚才建立的连接池讄?/P>
<%@ page contentType="text/html;charset=GBK" %>
<%@ page import= "java.sql.* " %>
<%@ page import= "javax.sql.* " %>
<%@ page import= "javax.naming.* "%>
<HTML>
<HEAD>
<TITLE> </TITLE>
<META NAME="Generator" CONTENT="EditPlus2.11">
</HEAD>
<BODY>
<%
// 从weblogic 8 文档抄来
Context ctx = null;
Hashtable ht = new Hashtable();
ht.put(Context.INITIAL_CONTEXT_FACTORY,"weblogic.jndi.WLInitialContextFactory");
ht.put(Context.PROVIDER_URL,"t3://127.0.0.1:7001");
// ht.put(Context.SECURITY_PRINCIPAL, "admin");
// ht.put(Context.SECURITY_CREDENTIALS, "11111111"); Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
ctx = new InitialContext(ht);
DataSource ds = (DataSource)ctx.lookup ("MyJNDI");
conn = ds.getConnection();
stmt = conn.createStatement();
stmt.execute("select * from accounts");
rs = stmt.getResultSet();
while ( rs.next()){
out.println( rs.getString(1)+"___");
out.println( rs.getString(2)+"___");
out.println( rs.getString(3)+"<br>");
}
stmt.close();
stmt=null;
conn.close(); conn=null;
}catch (Exception e) {
out.println("错误 !! ERR !" );
}
finally {
try {
ctx.close();
} catch (Exception e) {
out.println("ctx ERR !" ); }
try {
if (rs != null) rs.close();
} catch (Exception e) {
out.println("rs ERR !" ); }
try {
if (stmt != null) stmt.close();
} catch (Exception e) {
out.println("stmt ERR !" ); }
try {
if (conn != null) conn.close();
} catch (Exception e) {
out.println("conn ERR !" ); } }
%>
</BODY>
</HTML>
log4j.appender.ROOT=org.apache.log4j.RollingFileAppender
log4j.appender.ROOT.File=MMQB_Debug_Info.log
log4j.appender.ROOT.MaxFileSize=200KB
log4j.appender.ROOT.MaxBackupIndex=5
log4j.appender.ROOT.layout=org.apache.log4j.PatternLayout
log4j.appender.ROOT.layout.ConversionPattern=[%d] %c %-5p - %m%n
#log4j.logger.com.webage.ejbs=INFO
3.在程序中
protected Log log = LogFactory.getLog(this.getClass().getName());
log.debug("debug infomation");
首先建立一个servlet
/*
* 创徏日期 2005-4-4
*
* TODO 要更Ҏ生成的文件的模板Q请转至
* H口 Q?首选项 Q?Java Q?代码样式 Q?代码模板
*/
package cn.wst.common.log4j;
/*
* 创徏日期 2005-3-8
*
* 源文? MyLog4jInit.java
*/
import java.io.IOException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.xml.DOMConfigurator;
import org.apache.log4j.PropertyConfigurator;
/**
* 用于初始化Log4j
*
* @author wst
* @version 1.0
*/
public class MyLog4jInit extends HttpServlet {
public void init(ServletConfig config) throws ServletException {
super.init(config);
String prefix = getServletContext().getRealPath("/");
String file = getInitParameter("log4j"); // if the log4j-init-file is ot
System.out.println("-------- Log4J Start [AddressBook] --------- ");
if ( file != null ) {
DOMConfigurator.configure(prefix + file);
// PropertyConfigurator.configure(prefix+file);
}
}
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
}
}
然后在web-inf里面的最开头配|servlet
<servlet>
<servlet-name>MyLog4jInit</servlet-name>
<servlet-class>cn.wst.common.log4j.MyLog4jInit</servlet-class>
<init-param>
<param-name>log4j</param-name>
<param-value>WEB-INF/log4j.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup><!--log4j的初始化-->
</servlet>
相应在web-inf下的log4j.xmlQ?BR><?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j=" <appender name="cn.wst.file.log"
class="org.apache.log4j.RollingFileAppender">
<param name="File" value="C:/addrLog.txt" />
<param name="Append" value="false" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-d{yyyy-MM-dd HH:mm:ss} [%C:%M()]-[%p] %m%n" />
</layout>
</appender>
<appender name="cn.wst.console.log"
class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%5p (%F:%L) - %m%n" />
</layout>
</appender>
<!-- use this to turn on debug to a rolling file. -->
<root>
<level value="info" />
<appender-ref ref="cn.wst.console.log" />
<appender-ref ref="cn.wst.file.log " />
</root>
</log4j:configuration>
在程序中使用使用Q假讄名ؓFind_hotel_servletQ则
先证明全局变量static Logger logger = Logger.getLogger(Find_hotel_servlet.class.getName());
然后使用只要Find_hotel_servlet.logger.info(Q*Q*Q?可以了
Adapted from:
Pro Jakarta Commons, by Harshad Oak
Publisher: Apress
ISBN: 1590592832
All communication over the Internet happens using a standard set of protocols, such as File Transfer Protocol (FTP), Simple Mail Transfer Protocol (SMTP), Post Office Protocol (POP), Hypertext Transfer Protocol (HTTP), and so on. HTTP is one of the most popular of these protocols and is integral to the World Wide Web. It is also a protocol that many of today’s applications have to be aware of and use wisely. If a Java application has to interact using HTTP, the Commons HttpClient component can make things a bit easier. Using this component, you do not have to worry about all the technicalities of the protocol but just concern yourself with the various classes and methods provided by the HttpClient component. In this article you will have a look at the capabilities of the HttpClient component and also some hands-on examples.
You will also have a quick look at the FileUpload component, which simplifies file-upload tasks on the server side. Finally, you will work through an example where you use HttpClient and FileUpload together.
NOTE For all server-based examples in this article, I have used Tomcat version 4.0.6; however, you should not have any problems if you use another server that supports servlets and Java Server Page (JSP) technology.
Table 9-1 shows the details for the components covered in this article.
Table 9-1. Component Details
Name | Version | Package |
HttpClient | 2.0-rc1 | org.apache.commons.httpclient |
FileUpload | 1.0 | org.apache.commons.fileupload |
HttpClient is an attempt to provide a simple Application Programming Interface (API) that Java developers can use to create code that communicates over HTTP. If you are developing a Web browser or just an application that occasionally needs some data from the Web, HttpClient can help you develop the client code that will communicate over HTTP. As the name suggests, HttpClient is meant only for HTTP client code and cannot be used to, say, develop a server that processes HTTP requests.
I recommend you use HttpClient instead of using the java.net classes because HttpClient is easier to work with, it supports some HTTP features that java.net does not, and it has a vibrant community backing it. Visit http://www.nogoop.com/product_16.html#compare to compare HttpClient, java.net, and a couple of other APIs.
With a number of Commons components, the Javadocs are the only real documentation that exists. However, with HttpClient some good documentation exists beyond the Javadocs. A short tutorial at http://jakarta.apache.org/commons/httpclient/tutorial.html can get you started with HttpClient.
These are some of the important features of HttpClient:
You will now see the various elements of the HttpClient component and how they fall into place to get you talking over HTTP.
HttpClient uses the Commons Logging component, so the only dependency for HttpClient to work properly is that the commons-logging component Java Archive (JAR) file be present. Using HttpClient to handle most requirements is fairly simple. You just need to understand a few key classes and interfaces. The following sections present a simple example of sending a GET request and then explain the classes that play a role in how the example works.
The GET method is the most common method used to send an HTTP request. Every time you click a hyperlink, you send an HTTP request using the GET method. You will now see an example of sending a GET request using HttpClient based Java code. The code in Listing 9-1 sends a GET request to the URL http://localhost:8080/validatorStrutsApp/userInfo.do and has three request parameters, firstname, lastname, and email.
Listing 9-1. HttpClientTrial package com.commonsbook.chap9; import java.io.FileOutputStream; import java.io.IOException; import org.apache.commons.httpclient.*; import org.apache.commons.httpclient.methods.GetMethod; public class SubmitHttpForm { private static String url = "http://localhost:8080/validatorStrutsApp/userInfo.do"; public static void main(String[] args) { //Instantiate an HttpClient HttpClient client = new HttpClient(); //Instantiate a GET HTTP method HttpMethod method = new GetMethod(url); //Define name-value pairs to set into the QueryString NameValuePair nvp1= new NameValuePair("firstName","fname"); NameValuePair nvp2= new NameValuePair("lastName","lname"); NameValuePair nvp3= new NameValuePair("email","email@email.com"); method.setQueryString(new NameValuePair[]{nvp1,nvp2, nvp3}); try{ int statusCode = client.executeMethod(method); System.out.println("QueryString>>> "+method.getQueryString()); System.out.println("Status Text>>>" +HttpStatus.getStatusText(statusCode)); //Get data as a String System.out.println(method.getResponseBodyAsString()); //OR as a byte array byte [] res = method.getResponseBody(); //write to file FileOutputStream fos= new FileOutputStream("donepage.html"); fos.write(res); //release connection method.releaseConnection(); } catch(IOException e) { e.printStackTrace(); } } }
The output on executing this piece of code will depend on the response you get to your GET request.
The following steps take place in the class SubmitHttpForm to invoke the URL specified, including passing the three parameters as part of the query string, displaying the response, and writing the response to a file:
NOTE The class org.apache.commons.httpclient.HttpStatus defines static int variables that map to HTTP status codes.
In this example, you can see how easily you can fire a request and get a response over HTTP using the HttpClient component. You might have noted that writing such code has a lot of potential to enable testing of Web applications quickly and to even load test them. This has led to HttpClient being used in popular testing framework such as Jakarta Cactus, HTMLUnit, and so on. You can find in the documentation a list of popular applications that use HttpClient.
You used the GET method to send name/value pairs as part of a request. However, the GET method cannot always serve your purpose, and in some cases using the POST method is a better option.
Listing 9-2 shows an example where you enclose an Extensible Markup Language (XML) file within a request and send it using the POST method to a JSP named GetRequest.jsp. The JSP will just print the request headers it receives. These headers will show if the request got across properly.
Listing 9-2. Sending an XML File Using the POST Method package com.commonsbook.chap9; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.methods.PostMethod; import java.io.File; import java.io.FileInputStream; import java.io.IOException; public class PostAFile { private static String url = "http://localhost:8080/HttpServerSideApp/GetRequest.jsp"; public static void main(String[] args) throws IOException { HttpClient client = new HttpClient(); PostMethod postMethod = new PostMethod(url); client.setConnectionTimeout(8000); // Send any XML file as the body of the POST request File f = new File("students.xml"); System.out.println("File Length = " + f.length()); postMethod.setRequestBody(new FileInputStream(f)); postMethod.setRequestHeader("Content-type", "text/xml; charset=ISO-8859-1"); int statusCode1 = client.executeMethod(postMethod); System.out.println("statusLine>>>" + postMethod.getStatusLine()); postMethod.releaseConnection(); } }
In Listing 9-2, I have stated the URL for GetRequest.jsp using a server I am running locally on port 8080. This URL will vary based on the server where the JSP is being maintained. In this example, you create an instance of the classes HttpClient and PostMethod. You set the connection timeout for the HTTP connection to 3,000 milliseconds and then set an XML file into the request body. I am using a file named students.xml however, the contents of the file are not relevant to the example, and you could very well use any other file. Because you are sending an XML file, you also set the Content-Type header to state the format and the character set. GetRequest.jsp contains only a scriptlet that prints the request headers. The contents of the JSP are as follows:
<% java.util.Enumeration e= request.getHeaderNames(); while (e.hasMoreElements()) { String headerName=(String)e.nextElement(); System.out.println(headerName +" = "+request.getHeader(headerName)); } %>
Upon executing the class PostAFile, the JSP gets invoked, and the output displayed on the server console is as follows:
content-type = text/xml; charset=ISO-8859-1 user-agent = Jakarta Commons-HttpClient/2.0rc1 host = localhost:8080 content-length = 279
The output shown on the console where the PostAFile class was executed is as follows:
File Length = 279 statusLine>>>HTTP/1.1 200 OK
Note that the output on the server shows the content length as 279 (bytes), the same as the length of the file students.xml that is shown on the application console. Because you are not invoking the JSP using any browser, the User-Agent header that normally states the browser specifics shows the HttpClient version being used instead.
NOTE In this example, you sent a single file over HTTP. To upload multiple files, the MultipartPostMethod class is a better alternative. You will look at it later in the “Introducing FileUpload?section.
HttpClient provides cookie management features that can be particularly useful to test the way an application handles cookies. Listing 9-3 shows an example where you use HttpClient to add a cookie to a request and also to list details of cookies set by the JSP you invoke using the HttpClient code.
The HttpState class plays an important role while working with cookies. The HttpState class works as a container for HTTP attributes such as cookies that can persist from one request to another. When you normally surf the Web, the Web browser is what stores the HTTP attributes.
Listing 9-3. CookiesTrial.java package com.commonsbook.chap9; import org.apache.commons.httpclient.Cookie; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpState; import org.apache.commons.httpclient.cookie.CookiePolicy; import org.apache.commons.httpclient.methods.GetMethod; public class CookiesTrial { private static String url = "http://127.0.0.1:8080/HttpServerSideApp/CookieMgt.jsp"; public static void main(String[] args) throws Exception { //A new cookie for the domain 127.0.0.1 //Cookie Name= ABCD Value=00000 Path=/ MaxAge=-1 Secure=False Cookie mycookie = new Cookie("127.0.0.1", "ABCD", "00000", "/", -1, false); //Create a new HttpState container HttpState initialState = new HttpState(); initialState.addCookie(mycookie); //Set to COMPATIBILITY for it to work in as many cases as possible initialState.setCookiePolicy(CookiePolicy.COMPATIBILITY); //create new client HttpClient httpclient = new HttpClient(); //set the HttpState for the client httpclient.setState(initialState); GetMethod getMethod = new GetMethod(url); //Execute a GET method int result = httpclient.executeMethod(getMethod); System.out.println("statusLine>>>"+getMethod.getStatusLine()); //Get cookies stored in the HttpState for this instance of HttpClient Cookie[] cookies = httpclient.getState().getCookies(); for (int i = 0; i < cookies.length; i++) { System.out.println("\nCookieName="+cookies[i].getName()); System.out.println("Value="+cookies[i].getValue()); System.out.println("Domain="+cookies[i].getDomain()); } getMethod.releaseConnection(); } }
In Listing 9-3, you use the HttpState instance to store a new cookie and then associate this instance with the HttpClient instance. You then invoke CookieMgt.jsp. This JSP is meant to print the cookies it finds in the request and then add a cookie of its own. The JSP code is as follows:
<% Cookie[] cookies= request.getCookies(); for (int i = 0; i < cookies.length; i++) { System.out.println(cookies[i].getName() +" = "+cookies[i].getValue()); } //Add a new cookie response.addCookie(new Cookie("XYZ","12345")); %>
CAUTION HttpClient code uses the class org.apache.commons.httpclient.Cookie, and JSP and servlet code uses the class javax.servlet.http.Cookie.
The output on the application console upon executing the CookiesTrial class and invoking CookieMgt.jsp is as follows:
statusLine>>>HTTP/1.1 200 OK CookieName=ABCD Value=00000 Domain=127.0.0.1 CookieName=XYZ Value=12345 Domain=127.0.0.1 CookieName=JSESSIONID Value=C46581331881A84483F0004390F94508 Domain=127.0.0.1
In this output, note that although the cookie named ABCD has been created from CookiesTrial, the other cookie named XYZ is the one inserted by the JSP code. The cookie named JSESSIONID is meant for session tracking and gets created upon invoking the JSP. The output as displayed on the console of the server when the JSP is executed is as follows:
ABCD = 00000
This shows that when CookieMgt.jsp receives the request from the CookiesTrial class, the cookie named ABCD was the only cookie that existed. The sidebar “HTTPS and Proxy Servers?shows how you should handle requests over HTTPS and configure your client to go through a proxy.
|
You will now see the HttpClient component’s capability to use MultipartPostMethod to upload multiple files. You will look at this in tandem with the Commons FileUpload component. This Commons component is specifically meant to handle the server-side tasks associated with file uploads.
The FileUpload component has the capability of simplifying the handling of files uploaded to a server. Note that the FileUpload component is meant for use on the server side; in other words, it handles where the files are being uploaded to—not the client side where the files are uploaded from. Uploading files from an HTML form is pretty simple; however, handling these files when they get to the server is not that simple. If you want to apply any rules and store these files based on those rules, things get more difficult.
The FileUpload component remedies this situation, and in very few lines of code you can easily manage the files uploaded and store them in appropriate locations. You will now see an example where you upload some files first using a standard HTML form and then using HttpClient code.
The commonly used methodology to upload files is to have an HTML form where you define the files you want to upload. A common example of this HTML interface is the Web page you encounter when you want to attach files to an email while using any of the popular Web mail services.
In this example, you will create a simple HTML page where you provide for three files to be uploaded. Listing 9-4 shows the HTML for this page. Note that the enctype attribute for the form has the value multipart/form-data, and the input tag used is of type file. Based on the value of the action attribute, on form submission, the data is sent to ProcessFileUpload.jsp.
Listing 9-4. UploadFiles.html <HTML> <HEAD> <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252"/> <TITLE>File Upload Page</TITLE> </HEAD> <BODY>Upload Files <FORM name="filesForm" action="ProcessFileUpload.jsp" method="post" enctype="multipart/form-data"> File 1:<input type="file" name="file1"/><br/> File 2:<input type="file" name="file2"/><br/> File 3:<input type="file" name="file3"/><br/> <input type="submit" name="Submit" value="Upload Files"/> </FORM> </BODY> </HTML>
You can use a servlet to handle the file upload. I have used JSP to minimize the code you need to write. The task that the JSP has to accomplish is to pick up the files that are sent as part of the request and store these files on the server. In the JSP, instead of displaying the result of the upload in the Web browser, I have chosen to print messages on the server console so that you can use this same JSP when it is not invoked through an HTML form but by using HttpClient-based code.
Listing 9-5 shows the JSP code. Note the code that checks whether the item is a form field. This check is required because the Submit button contents are also sent as part of the request, and you want to distinguish between this data and the files that are part of the request. You have set the maximum file size to 1,000,000 bytes using the setSizeMax method.
Listing 9-5. ProcessFileUpload.jsp <%@ page contentType="text/html;charset=windows-1252"%> <%@ page import="org.apache.commons.fileupload.DiskFileUpload"%> <%@ page import="org.apache.commons.fileupload.FileItem"%> <%@ page import="java.util.List"%> <%@ page import="java.util.Iterator"%> <%@ page import="java.io.File"%> html> <head> <meta http-equiv="Content-Type" content="text/html; charset=windows-1252"> <title>Process File Upload</title> </head> <% System.out.println("Content Type ="+request.getContentType()); DiskFileUpload fu = new DiskFileUpload(); // If file size exceeds, a FileUploadException will be thrown fu.setSizeMax(1000000); List fileItems = fu.parseRequest(request); Iterator itr = fileItems.iterator(); while(itr.hasNext()) { FileItem fi = (FileItem)itr.next(); //Check if not form field so as to only handle the file inputs //else condition handles the submit button input if(!fi.isFormField()) { System.out.println("\nNAME: "+fi.getName()); System.out.println("SIZE: "+fi.getSize()); //System.out.println(fi.getOutputStream().toString()); File fNew= new File(application.getRealPath("/"), fi.getName()); System.out.println(fNew.getAbsolutePath()); fi.write(fNew); } else { System.out.println("Field ="+fi.getFieldName()); } } %> <body> Upload Successful!! </body> </html>
CAUTION With FileUpload 1.0 I found that when the form was submitted using Opera version 7.11, the getName method of the class FileItem returns just the name of the file. However, if the form is submitted using Internet Explorer 5.5, the filename along with its entire path is returned by the same method. This can cause some problems.
To run this example, you can use any three files, as the contents of the files are not important. Upon submitting the form using Opera and uploading three random XML files, the output I got on the Tomcat server console was as follows:
Content Type =multipart/form-data; boundary=----------rz7ZNYDVpN1To8L73sZ6OE NAME: academy.xml SIZE: 951 D:\javaGizmos\jakarta-tomcat-4.0.1\webapps\HttpServerSideApp\academy.xml NAME: academyRules.xml SIZE: 1211 D:\javaGizmos\jakarta-tomcat-4.0.1\webapps\HttpServerSideApp\academyRules.xml NAME: students.xml SIZE: 279 D:\javaGizmos\jakarta-tomcat-4.0.1\webapps\HttpServerSideApp\students.xml Field =Submit However, when submitting this same form using Internet Explorer 5.5, the output on the server console was as follows: Content Type =multipart/form-data; boundary=---------------------------7d3bb1de0 2e4 NAME: D:\temp\academy.xml SIZE: 951 D:\javaGizmos\jakarta-tomcat-4.0.1\webapps\HttpServerSideApp\D:\temp\academy.xml
The browser displayed the following message: “The requested resource (D:\javaGizmos\jakarta-tomcat-4.0.1\webapps\HttpServerSideApp\D:\temp\academy.xml (The filename, directory name, or volume label syntax is incorrect)) is not available.?/P>
This contrasting behavior on different browsers can cause problems. One workaround that I found in an article at http://www.onjava.com/pub/a/onjava/2003/06/25/commons.html is to first create a file reference with whatever is supplied by the getName method and then create a new file reference using the name returned by the earlier file reference. Therefore, you can insert the following code to have your code work with both browsers (I wonder who the guilty party is…blaming Microsoft is always the easy way out)
File tempFileRef = new File(fi.getName()); File fNew = new File(application.getRealPath("/"),tempFileRef.getName());
In this section, you uploaded files using a standard HTML form mechanism. However, often a need arises to be able to upload files from within your Java code, without any browser or form coming into the picture. In the next section, you will look at HttpClient-based file upload.
Earlier in the article you saw some of the capabilities of the HttpClient component. One capability I did not cover was its ability to send multipart requests. In this section, you will use this capability to upload a few files to the same JSP that you used for uploads using HTML.
The class org.apache.commons.httpclient.methods.MultipartPostMethod provides the multipart method capability to send multipart-encoded forms, and the package org.apache.commons.httpclient.methods.multipart has the support classes required. Sending a multipart form using HttpClient is quite simple. In the code in Listing 9-6, you send three files to ProcessFileUpload.jsp.
Listing 9-6. HttpMultiPartFileUpload.java package com.commonsbook.chap9; import java.io.File; import java.io.IOException; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.methods.MultipartPostMethod; public class HttpMultiPartFileUpload { private static String url = "http://localhost:8080/HttpServerSideApp/ProcessFileUpload.jsp"; public static void main(String[] args) throws IOException { HttpClient client = new HttpClient(); MultipartPostMethod mPost = new MultipartPostMethod(url); client.setConnectionTimeout(8000); // Send any XML file as the body of the POST request File f1 = new File("students.xml"); File f2 = new File("academy.xml"); File f3 = new File("academyRules.xml"); System.out.println("File1 Length = " + f1.length()); System.out.println("File2 Length = " + f2.length()); System.out.println("File3 Length = " + f3.length()); mPost.addParameter(f1.getName(), f1); mPost.addParameter(f2.getName(), f2); mPost.addParameter(f3.getName(), f3); int statusCode1 = client.executeMethod(mPost); System.out.println("statusLine>>>" + mPost.getStatusLine()); mPost.releaseConnection(); } }
In this code, you just add the files as parameters and execute the method. The ProcessFileUpload.jsp file gets invoked, and the output is as follows:
Content Type =multipart/form-data; boundary=----------------31415926535897932384 6 NAME: students.xml SIZE: 279 D:\javaGizmos\jakarta-tomcat-4.0.1\webapps\HttpServerSideApp\students.xml NAME: academy.xml SIZE: 951 D:\javaGizmos\jakarta-tomcat-4.0.1\webapps\HttpServerSideApp\academy.xml NAME: academyRules.xml SIZE: 1211 D:\javaGizmos\jakarta-tomcat-4.0.1\webapps\HttpServerSideApp\academyRules.xml
Thus, file uploads on the server side become quite a simple task if you are using the Commons FileUpload component.
In this article, you saw the HttpClient and FileUpload components. Although HttpClient can be useful in any kind of applications that use HTTP for communication, the FileUpload component has a much more specific scope. One important plus for HttpClient is the existence of a decent user guide and tutorial. The FileUpload component can be just what you are looking for if you are wondering what to use and how to manage files uploaded through your application.
public class MD5 {
/* 下面q些S11-S44实际上是一?*4的矩阵,在原始的C实现中是?define 实现的,
q里把它们实现成为static final是表CZ只读Q切能在同一个进E空间内的多?BR> Instance间共?/
static final int S11 = 7;
static final int S12 = 12;
static final int S13 = 17;
static final int S14 = 22;
static final int S21 = 5;
static final int S22 = 9;
static final int S23 = 14;
static final int S24 = 20;
static final int S31 = 4;
static final int S32 = 11;
static final int S33 = 16;
static final int S34 = 23;
static final int S41 = 6;
static final int S42 = 10;
static final int S43 = 15;
static final int S44 = 21;
static final byte[] PADDING = { -128, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
/* 下面的三个成员是MD5计算q程中用到的3个核心数据,在原始的C实现?BR> 被定义到MD5_CTXl构?BR>
*/
private long[] state = new long[4]; // state (ABCD)
private long[] count = new long[2]; // number of bits, modulo 2^64 (lsb first)
private byte[] buffer = new byte[64]; // input buffer
/* digestHexStr是MD5的唯一一个公共成员,是最Cơ计结果的
16q制ASCII表示.
*/
public String digestHexStr;
/* digest,是最Cơ计结果的2q制内部表示Q表C?28bit的MD5?
*/
private byte[] digest = new byte[16];
/*
getMD5ofStr是类MD5最主要的公共方法,入口参数是你惌q行MD5变换的字W串
q回的是变换完的l果Q这个结果是从公共成员digestHexStr取得的.
*/
public String getMD5ofStr(String inbuf) {
md5Init();
md5Update(inbuf.getBytes(), inbuf.length());
md5Final();
digestHexStr = "";
for (int i = 0; i < 16; i++) {
digestHexStr += byteHEX(digest[i]);
}
return digestHexStr;
}
// q是MD5q个cȝ标准构造函敎ͼJavaBean要求有一个public的ƈ且没有参数的构造函?BR> public MD5() {
md5Init();
return;
}
/* md5Init是一个初始化函数Q初始化核心变量Q装入标准的qL */
private void md5Init() {
count[0] = 0L;
count[1] = 0L;
///* Load magic initialization constants.
state[0] = 0x67452301L;
state[1] = 0xefcdab89L;
state[2] = 0x98badcfeL;
state[3] = 0x10325476L;
return;
}
/* F, G, H ,I ?个基本的MD5函数Q在原始的MD5的C实现中,׃它们?BR> 单的位运,可能Z效率的考虑把它们实现成了宏Q在java中,我们把它?BR> 实现成了privateҎQ名字保持了原来C中的?*/
private long F(long x, long y, long z) {
return (x & y) | ((~x) & z);
}
private long G(long x, long y, long z) {
return (x & z) | (y & (~z));
}
private long H(long x, long y, long z) {
return x ^ y ^ z;
}
private long I(long x, long y, long z) {
return y ^ (x | (~z));
}
/*
FF,GG,HH和II调用F,G,H,Iq行q一步变?BR> FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
Rotation is separate from addition to prevent recomputation.
*/
private long FF(long a, long b, long c, long d, long x, long s,
long ac) {
a += F (b, c, d) + x + ac;
a = ((int) a << s) | ((int) a >>> (32 - s));
a += b;
return a;
}
private long GG(long a, long b, long c, long d, long x, long s,
long ac) {
a += G (b, c, d) + x + ac;
a = ((int) a << s) | ((int) a >>> (32 - s));
a += b;
return a;
}
private long HH(long a, long b, long c, long d, long x, long s,
long ac) {
a += H (b, c, d) + x + ac;
a = ((int) a << s) | ((int) a >>> (32 - s));
a += b;
return a;
}
private long II(long a, long b, long c, long d, long x, long s,
long ac) {
a += I (b, c, d) + x + ac;
a = ((int) a << s) | ((int) a >>> (32 - s));
a += b;
return a;
}
/*
md5Update是MD5的主计算q程Qinbuf是要变换的字节串Qinputlen是长度,q个
函数由getMD5ofStr调用Q调用之前需要调用md5initQ因此把它设计成private?BR> */
private void md5Update(byte[] inbuf, int inputLen) {
int i, index, partLen;
byte[] block = new byte[64];
index = (int)(count[0] >>> 3) & 0x3F;
// /* Update number of bits */
if ((count[0] += (inputLen << 3)) < (inputLen << 3))
count[1]++;
count[1] += (inputLen >>> 29);
partLen = 64 - index;
// Transform as many times as possible.
if (inputLen >= partLen) {
md5Memcpy(buffer, inbuf, index, 0, partLen);
md5Transform(buffer);
for (i = partLen; i + 63 < inputLen; i += 64) {
md5Memcpy(block, inbuf, 0, i, 64);
md5Transform (block);
}
index = 0;
} else
i = 0;
///* Buffer remaining input */
md5Memcpy(buffer, inbuf, index, i, inputLen - i);
}
/*
md5Final整理和填写输出结?BR> */
private void md5Final () {
byte[] bits = new byte[8];
int index, padLen;
///* Save number of bits */
Encode (bits, count, 8);
///* Pad out to 56 mod 64.
index = (int)(count[0] >>> 3) & 0x3f;
padLen = (index < 56) ? (56 - index) : (120 - index);
md5Update (PADDING, padLen);
///* Append length (before padding) */
md5Update(bits, 8);
///* Store state in digest */
Encode (digest, state, 16);
}
/* md5Memcpy是一个内部用的byte数组的块拯函数Q从input的inpos开始把len长度?BR> 字节拯到output的outpos位置开?
*/
private void md5Memcpy (byte[] output, byte[] input,
int outpos, int inpos, int len)
{
int i;
for (i = 0; i < len; i++)
output[outpos + i] = input[inpos + i];
}
/*
md5Transform是MD5核心变换E序Q有md5Update调用Qblock是分块的原始字节
*/
private void md5Transform (byte block[]) {
long a = state[0], b = state[1], c = state[2], d = state[3];
long[] x = new long[16];
Decode (x, block, 64);
/* Round 1 */
a = FF (a, b, c, d, x[0], S11, 0xd76aa478L); /* 1 */
d = FF (d, a, b, c, x[1], S12, 0xe8c7b756L); /* 2 */
c = FF (c, d, a, b, x[2], S13, 0x242070dbL); /* 3 */
b = FF (b, c, d, a, x[3], S14, 0xc1bdceeeL); /* 4 */
a = FF (a, b, c, d, x[4], S11, 0xf57c0fafL); /* 5 */
d = FF (d, a, b, c, x[5], S12, 0x4787c62aL); /* 6 */
c = FF (c, d, a, b, x[6], S13, 0xa8304613L); /* 7 */
b = FF (b, c, d, a, x[7], S14, 0xfd469501L); /* 8 */
a = FF (a, b, c, d, x[8], S11, 0x698098d8L); /* 9 */
d = FF (d, a, b, c, x[9], S12, 0x8b44f7afL); /* 10 */
c = FF (c, d, a, b, x[10], S13, 0xffff5bb1L); /* 11 */
b = FF (b, c, d, a, x[11], S14, 0x895cd7beL); /* 12 */
a = FF (a, b, c, d, x[12], S11, 0x6b901122L); /* 13 */
d = FF (d, a, b, c, x[13], S12, 0xfd987193L); /* 14 */
c = FF (c, d, a, b, x[14], S13, 0xa679438eL); /* 15 */
b = FF (b, c, d, a, x[15], S14, 0x49b40821L); /* 16 */
/* Round 2 */
a = GG (a, b, c, d, x[1], S21, 0xf61e2562L); /* 17 */
d = GG (d, a, b, c, x[6], S22, 0xc040b340L); /* 18 */
c = GG (c, d, a, b, x[11], S23, 0x265e5a51L); /* 19 */
b = GG (b, c, d, a, x[0], S24, 0xe9b6c7aaL); /* 20 */
a = GG (a, b, c, d, x[5], S21, 0xd62f105dL); /* 21 */
d = GG (d, a, b, c, x[10], S22, 0x2441453L); /* 22 */
c = GG (c, d, a, b, x[15], S23, 0xd8a1e681L); /* 23 */
b = GG (b, c, d, a, x[4], S24, 0xe7d3fbc8L); /* 24 */
a = GG (a, b, c, d, x[9], S21, 0x21e1cde6L); /* 25 */
d = GG (d, a, b, c, x[14], S22, 0xc33707d6L); /* 26 */
c = GG (c, d, a, b, x[3], S23, 0xf4d50d87L); /* 27 */
b = GG (b, c, d, a, x[8], S24, 0x455a14edL); /* 28 */
a = GG (a, b, c, d, x[13], S21, 0xa9e3e905L); /* 29 */
d = GG (d, a, b, c, x[2], S22, 0xfcefa3f8L); /* 30 */
c = GG (c, d, a, b, x[7], S23, 0x676f02d9L); /* 31 */
b = GG (b, c, d, a, x[12], S24, 0x8d2a4c8aL); /* 32 */
/* Round 3 */
a = HH (a, b, c, d, x[5], S31, 0xfffa3942L); /* 33 */
d = HH (d, a, b, c, x[8], S32, 0x8771f681L); /* 34 */
c = HH (c, d, a, b, x[11], S33, 0x6d9d6122L); /* 35 */
b = HH (b, c, d, a, x[14], S34, 0xfde5380cL); /* 36 */
a = HH (a, b, c, d, x[1], S31, 0xa4beea44L); /* 37 */
d = HH (d, a, b, c, x[4], S32, 0x4bdecfa9L); /* 38 */
c = HH (c, d, a, b, x[7], S33, 0xf6bb4b60L); /* 39 */
b = HH (b, c, d, a, x[10], S34, 0xbebfbc70L); /* 40 */
a = HH (a, b, c, d, x[13], S31, 0x289b7ec6L); /* 41 */
d = HH (d, a, b, c, x[0], S32, 0xeaa127faL); /* 42 */
c = HH (c, d, a, b, x[3], S33, 0xd4ef3085L); /* 43 */
b = HH (b, c, d, a, x[6], S34, 0x4881d05L); /* 44 */
a = HH (a, b, c, d, x[9], S31, 0xd9d4d039L); /* 45 */
d = HH (d, a, b, c, x[12], S32, 0xe6db99e5L); /* 46 */
c = HH (c, d, a, b, x[15], S33, 0x1fa27cf8L); /* 47 */
b = HH (b, c, d, a, x[2], S34, 0xc4ac5665L); /* 48 */
/* Round 4 */
a = II (a, b, c, d, x[0], S41, 0xf4292244L); /* 49 */
d = II (d, a, b, c, x[7], S42, 0x432aff97L); /* 50 */
c = II (c, d, a, b, x[14], S43, 0xab9423a7L); /* 51 */
b = II (b, c, d, a, x[5], S44, 0xfc93a039L); /* 52 */
a = II (a, b, c, d, x[12], S41, 0x655b59c3L); /* 53 */
d = II (d, a, b, c, x[3], S42, 0x8f0ccc92L); /* 54 */
c = II (c, d, a, b, x[10], S43, 0xffeff47dL); /* 55 */
b = II (b, c, d, a, x[1], S44, 0x85845dd1L); /* 56 */
a = II (a, b, c, d, x[8], S41, 0x6fa87e4fL); /* 57 */
d = II (d, a, b, c, x[15], S42, 0xfe2ce6e0L); /* 58 */
c = II (c, d, a, b, x[6], S43, 0xa3014314L); /* 59 */
b = II (b, c, d, a, x[13], S44, 0x4e0811a1L); /* 60 */
a = II (a, b, c, d, x[4], S41, 0xf7537e82L); /* 61 */
d = II (d, a, b, c, x[11], S42, 0xbd3af235L); /* 62 */
c = II (c, d, a, b, x[2], S43, 0x2ad7d2bbL); /* 63 */
b = II (b, c, d, a, x[9], S44, 0xeb86d391L); /* 64 */
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
}
/*Encode把long数组按顺序拆成byte数组Q因为java的longcd?4bit的,
只拆?2bitQ以适应原始C实现的用?BR> */
private void Encode (byte[] output, long[] input, int len) {
int i, j;
for (i = 0, j = 0; j < len; i++, j += 4) {
output[j] = (byte)(input[i] & 0xffL);
output[j + 1] = (byte)((input[i] >>> 8) & 0xffL);
output[j + 2] = (byte)((input[i] >>> 16) & 0xffL);
output[j + 3] = (byte)((input[i] >>> 24) & 0xffL);
}
}
/*Decode把byte数组按顺序合成成long数组Q因为java的longcd?4bit的,
只合成低32bitQ高32bit清零Q以适应原始C实现的用?BR> */
private void Decode (long[] output, byte[] input, int len) {
int i, j;
for (i = 0, j = 0; j < len; i++, j += 4)
output[i] = b2iu(input[j]) |
(b2iu(input[j + 1]) << 8) |
(b2iu(input[j + 2]) << 16) |
(b2iu(input[j + 3]) << 24);
return;
}
/*
b2iu是我写的一个把byte按照不考虑正负L原则的"升位Q程序,因ؓjava没有unsignedq算
*/
public static long b2iu(byte b) {
return b < 0 ? b & 0x7F + 128 : b;
}
/*byteHEX()Q用来把一个bytecd的数转换成十六进制的ASCII表示Q?BR> 因ؓjava中的byte的toString无法实现q一点,我们又没有C语言中的
sprintf(outbuf,"%02X",ib)
*/
public static String byteHEX(byte ib) {
char[] Digit = { '0','1','2','3','4','5','6','7','8','9',
'A','B','C','D','E','F' };
char [] ob = new char[2];
ob[0] = Digit[(ib >>> 4) & 0X0F];
ob[1] = Digit[ib & 0X0F];
String s = new String(ob);
return s;
}
public static void main(String args[]) {
MD5 m = new MD5();
if (Array.getLength(args) == 0) { //如果没有参数Q执行标准的Test Suite
System.out.println("MD5 Test suite:");
System.out.println("MD5(\"\"):"+m.getMD5ofStr(""));
System.out.println("MD5(\"a\"):"+m.getMD5ofStr("a"));
System.out.println("MD5(\"abc\"):"+m.getMD5ofStr("abc"));
System.out.println("MD5(\"message digest\"):"+m.getMD5ofStr("message digest"));
System.out.println("MD5(\"abcdefghijklmnopqrstuvwxyz\"):"+
m.getMD5ofStr("abcdefghijklmnopqrstuvwxyz"));
System.out.println("MD5(\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\"):"+
m.getMD5ofStr("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"));
}
else
System.out.println("MD5(" + args[0] + ")=" + m.getMD5ofStr(args[0]));
}
}
login.jsp
<%@ page language='java' %>
<jsp:useBean id='oMD5' scope='request' class='beartool.MD5'/>
<%@ page import='java.util.*'%>
<%@ page import='java.sql.*'%>
<html>
<body>
<%
String userid = request.getParameter("UserID"); //获取用户输入UserID
String password = request.getParameter("Password"); //获取用户输入的Password
String pwdmd5 = oMD5.getMD5ofStr(password); //计算MD5的?BR>
PrintWriter rp = response.getWriter();
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
Connection con = DriverManager.getConnection("jdbc:odbc:community", "", "");
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("select * from users where userID ='"+userid+"' and pwdmd5= '" + pwdmd5+"'" );
if (rs.next())
{
rp.print("Login OK");
}
else
{
rp.print("Login Fail");
}
stmt.close();
con.close();
%>
</body>
</html>
~者的话:
虽然 MD5 {法?jdk 中早已实玎ͼ?MessageDigestc)Q但作者从 MD5 的原理分析讲q?MD5 具体法?Java实现q给Z个完整的CZE序Q我惌Ҏ们的读者来说还是会有很多帮助的?/I>
MD5?/SPAN>
MD5的全U是Message-Digest Algorithm 5Q在90q代初由MIT的计机U学实验室和RSA Data Security Inc发明Q经MD2、MD3和MD4发展而来?/P>
Message-Digest泛指字节?Message)的Hash变换Q就是把一个Q意长度的字节串变换成一定长的大整数。请注意我用了“字节串”而不是“字W串”这个词Q是因ؓq种变换只与字节的值有养I与字W集或编码方式无兟?/P>
MD5Q意长度的“字节串”变换成一?28bit的大整数Qƈ且它是一个不可逆的字符串变换算法,换句话说是Q即使你看到源程序和法描述Q也无法一个MD5的值变换回原始的字W串Q从数学原理上说Q是因ؓ原始的字W串有无I多个,q有点象不存在反函数的数学函数?/P>
MD5的典型应用是对一DMessage(字节?产生fingerprint(指纹)Q以防止被“篡改”。D个例子,你将一D话写在一个叫readme.txt文g中,q对q个readme.txt产生一个MD5的值ƈ记录在案Q然后你可以传播q个文gl别人,别h如果修改了文件中的Q何内容,你对q个文g重新计算MD5时就会发现。如果再有一个第三方的认证机构,用MD5q可以防止文件作者的“抵赖”,q就是所谓的数字{应用?/P>
MD5q广泛用于加密和解密技术上Q在很多操作pȝ中,用户的密码是以MD5|或类似的其它法Q的方式保存的,用户Login的时候,pȝ是把用户输入的密码计成MD5|然后再去和系l中保存的MD5D行比较,而系lƈ不“知道”用L密码是什么?/P>
一些黑客破莯U密码的Ҏ是一U被UCؓ“跑字典”的Ҏ。有两种Ҏ得到字典Q一U是日常搜集的用做密码的字符串表Q另一U是用排列组合方法生成的Q先用MD5E序计算些字兔R的MD5|然后再用目标的MD5值在q个字典中检索?/P>
即假设密码的最大长度ؓ8Q同时密码只能是字母和数字,?6+26+10=62个字W,排列l合出的字典的项数则是P(62,1)+P(62,2)?+P(62,8)Q那也已l是一个很天文的数字了Q存储这个字典就需要TBU的盘l,而且q种Ҏq有一个前提,是能获得目标̎L密码MD5值的情况下才可以?/P>
在很多电子商务和C应用中,理用户的Account是一U最常用的基本功能,管很多Application Server提供了这些基本组Ӟ但很多应用开发者ؓ了管理的更大的灵zL还是喜Ƣ采用关pL据库来管理用P懒惰的做法是用户的密码往往使用明文或简单的变换后直接保存在数据库中Q因此这些用L密码对Y件开发者或pȝ理员来说可以说毫无保密可言Q本文的目的是介lMD5的Java Bean的实玎ͼ同时l出用MD5来处理用LAccount密码的例子,q种Ҏ使得理员和E序设计者都无法看到用户的密码,管他们可以初始化它们。但重要的一Ҏ对于用户密码讄习惯的保护?/P>
有兴的读者可以从q里取得MD5也就是RFC 1321的文本。http://www.ietf.org/rfc/rfc1321.txt
实现{略
MD5的算法在RFC1321中实际上已经提供了C的实玎ͼ我们其实马上p惛_Q至有两种用Java实现它的ҎQ第一U是Q用Java语言重新写整个算法,或者再说简单点是把CE序改写成JavaE序。第二种是,用JNI(Java Native Interface)来实玎ͼ核心法仍然用这个CE序Q用Javacȝ它包个壳?/P>
但我个h认ؓQJNI应该是JavaZ解决某类问题时的没有办法的办法(比如与操作系l或I/O讑֤密切相关的应用)Q同时ؓ了提供和其它语言的互操作性的一个手Dc用JNI带来的最大问题是引入了^台的依赖性,打破了SUN所鼓吹的“一ơ编写到处运行”的Java好处。因此,我决定采取第一U方法,一来和大家一起尝试一下“一ơ编写到处运行”的好处Q二来检验一下Java 2现在对于比较密集的计的效率问题?
实现q程
限于q篇文章的篇q,同时也ؓ了更多的读者能够真正专注于问题本nQ我不想某一UJava集成开发环境来介绍q个Java Bean的制作过E,介绍一个方法时我发现步骤和命o很清晎ͼ我相信有M一UJava集成环境三天以上l验的读者都会知道如何把q些代码在集成环境中~译和运行。用集成环境讲述问题往往需要配很多屏幕截图Q这也是我一直对集成环境很头疼的原因。我使用了一个普通的文本~辑器,同时使用了Sun公司标准的JDK 1.3.0 for Windows NT?/P>
其实把C转换成Java对于一个有一定C语言基础的程序员q不困难Q这两个语言的基本语法几乎完全一_我大概花了一个小时的旉完成了代码的转换工作Q我主要作了下面几g事:
需要注意的是,很多早期的C~译器的intcd?6 bit的,MD5使用了unsigned long intQƈ认ؓ它是32bit的无W号整数。而在Java中int?2 bit的,long?4 bit的。在MD5的C实现中,使用了大量的位操作。这里需要指出的一ҎQ尽Java提供了位操作Q由于Java没有unsignedcdQ对于右UM操作多提供了一个无W号右移Q?gt;>>Q等价于C中的 >> 对于unsigned 数的处理?/P>
因ؓJava不提供无W号数的q算Q两个大int数相加就会溢出得C个负数或异常Q因此我一些关键变量在Java中改成了longcd(64bit)。我个h认ؓq比自己去重新定义一l无W号数的cd旉载那些运符要方便,同时效率高很多ƈ且代码也易读QOO(Object Oriented)的滥用反而会D效率低下?/P>
限于幅Q这里不再给出原始的C代码Q有兴趣对照的读者朋友可以去看RFC 1321?MD5.java源代?/A>
试
在RFC 1321中,l出了Test suite用来验你的实现是否正:
|
q些输出l果的含义是指:I字W串””的MD5值是d41d8cd98f00b204e9800998ecf8427eQ字W串”a”的MD5值是
0cc175b9c0f1b6a831c399e269772661…?
~译q运行我们的E序Q?
|
Z来不与别h的同名程序冲H,我在我的E序的第一行用了package beartool;
因此~译命o javac KCd . MD5.java 命o在我们的工作目录下自动徏立了一?beartool 目录Q目录下攄~译成功?MD5.class
我们得到和Test suite同样的结果。当然还可以l箋试你感兴趣的其它MD5变换Q例如:
|
给?234的MD5倹{?/P>
可能是我的计机知识是从Apple II和Z80单板机开始的Q我对大写十六进制代码有偏好Q如果您想用小写的Digest String只需要把byteHEX函数中的A、B、C、D、E、FҎa、b?c、d、e、f可以了?/P>
MD5据称是一U比较耗时的计,我们的Java版MD5一闪就出来了Q没遇到什么障,而且用肉眼感觉不出来Java版的MD5比C版的慢?/P>
Z试它的兼容性,我把q个MD5.class文g拯到我的另一台Linux+IBM JDK 1.3的机器上Q执行后得到同样l果Q确实是“一ơ编写到处运行了”?
Java Beanq?/SPAN>
现在Q我们已l完成ƈ单测试了q个Java ClassQ我们文章的标题是做一个Java Bean?/P>
其实普通的Java Bean很简单,q不是什么全新的或伟大的概念Q就是一个Java的ClassQ尽?Sun规定了一些需要实现的ҎQ但q不是强制的。而EJB(Enterprise Java Bean)无非规定了一些必d玎ͼ非常cM于响应事Ӟ的方法,q些Ҏ是供EJB Container使用(调用)的?/P>
在一个Java Application或Applet里用这个bean非常单,最单的Ҏ是你要用这个类的源码工作目录下Z个beartool目录Q把q个class文g拯q去Q然后在你的E序中import beartool.MD5可以了。最后打包成.jar?war是保持这个相对的目录关系p了?/P>
Javaq有一个小的好处是你q不需要摘除我们的MD5cM那个mainҎQ它已经是一个可以工作的Java Bean了。Java有一个非常大的优Ҏ她允许很方便地让多种q行形式在同一l代码中共存Q比如,你可以写一个类Q它x一个控制台Application和GUI ApplicationQ同时又是一个AppletQ同时还是一个Java BeanQ这对于试、维护和发布E序提供了极大的方便Q这里的试Ҏmainq可以放C个内部类中?
q里讲述了把试和示例代码放在一个内部静态类的好处,是一U不错的工程化技巧和途径?
把Java Bean装到JSP?/SPAN>
正如我们在本文开头讲q的那样Q我们对q个MD5 Bean的应用是Z一个用L理,q里我们假设了一个虚拟社区的用户loginq程Q用L信息保存在数据库的个名ؓusers的表中。这个表有两个字D和我们的这个例子有养Iuserid :char(20)和pwdmd5 :char(32)Quserid是这个表的Primary KeyQpwdmd5保存密码的MD5ԌMD5值是一?28bit的大整数Q表C成16q制的ASCII需?2个字W?/P>
q里l出两个文gQ?login.html是用来接受用戯入的formQ?login.jsp 用来模拟使用MD5 Bean的loginq程?
Z使我们的试环境单v见,我们在JSP中用了JDK内置的JDBC-ODBC Bridge DriverQcommunity是ODBC的DSN的名字,如果你用其它的JDBC DriverQ替换掉login.jsp中的
|
login.jsp的工作原理很单,通过post接收用户输入的UserID和PasswordQ然后将Password变换成MD5Ԍ然后在users表中LUserID和pwdmd5Q因为UserID是users表的Primary KeyQ如果变换后的pwdmd5与表中的记录不符Q那么SQL查询会得C个空的结果集?/P>
q里需要简单介l的是,使用q个Bean只需要在你的JSP应用E序的WEB-INF/classes下徏立一个beartool目录Q然后将MD5.class拯到那个目录下可以了。如果你使用一些集成开发环境,请参考它们的deploy工具的说明。在JSP使用一个java Bean关键的一句声明是E序中的W?行:
|
id=实际上是指示JSP Container创徏Bean的实例时用的实例变量名。在后面?lt;%?>之间的JavaE序中,你可以引用它。在E序中可以看刎ͼ通过 pwdmd5=oMD5.getMD5ofStr (password) 引用了我们的MD5 Java Bean提供的唯一一个公共方? getMD5ofStr?/B>
Java Application Server执行.JSP的过E是先把它预~译?javaQ那些Tag在预~译时会成ؓjava语句Q,然后再编译成.class。这些都是系l自动完成和l护的,那个.class也称为Servlet。当Ӟ如果你愿意,你也可以帮助Java Application Serverd本该它干的事情,自己直接dServletQ但用Servlet去输出HTML那简直是回到了用C写CGIE序的恶梦时代?/P>
如果你的输出是一个复杂的表格Q比较方便的Ҏ我想q是用一个你所熟悉的HTML~辑器编写一个“模李쀝,然后在把JSP代码“嵌入”进厅R尽这UJSP代码被有些专家指责ؓ“空心粉”,它的有个缺Ҏ代码比较隄理和重复使用Q但是程序设计永q需要的是q样的权衡。我个h认ؓQ对于中、小型项目,比较理想的结构是把数据表C(或不严格地称作WEB界面相关Q的部分用JSP写,和界面不相关的放在Bean里面Q一般情况下是不需要直接写Servlet的?/P>
如果你觉得这U方法不是非常的OO(Object Oriented)Q你可以l承QextendsQ它一把,再写一个bean把用L理的功能包进厅R?
到底能不能兼容?
我测试了三种Java应用服务器环境,Resin 1.2.3、Sun J2EE 1.2、IBM WebSphere 3.5Q所q的是这个Java Bean都没有Q何问题,原因其实是因为它仅仅是个计算E序Q不涉及操作pȝQI/O讑֤。其实用其它语言也能单地实现它的兼容性的QJava的唯一优点是,你只需提供一个Ş态的q行码就可以了。请注意“Ş态”二字,现在很多计算l构和操作系l除了语a本n之外都定义了大量的代码Ş态,很简单的一DC语言核心代码Q{换成不同形态要考虑很多问题Q用很多工P同时受很多限Ӟ有时候学习一U新的“Ş态”所p的精力可能比解决问题本nq多。比如光Windows有EXE、Service、的普通DLL、COM DLL以前q有OCX{等{等Q在Unix上虽说要单一些,但要也要提供一?h定义一大堆宏,q要考虑不同q_~译器版本的位长度问题。我惌是JavaҎ来说的一个非帔R要的力吧?
IETF RFC 1321 http:// http://www.ietf.org/rfc/rfc1321.txt q是关于MD5最原始和权威的文档Q同时包含了完整的C语言实现。这份文是1992q提交的Q它的CE序q不是ANSI标准语法的,有些老Unix的亲切感。在Windows下编译这个CE序要小改动一下?
J2EE教程 http://java.sun.com/j2ee/tutorial/doc/information/resources.html SUN的J2EE教程~得很不错,a意赅Qؓ全面了解Java Bean和JSP背后的体pȝ构提供了一个不错的L或烦引?
Bruce Eckel《Think in Java 2 ndEdition?q也是Developer Works推荐下蝲的一本不错的Java教科书,我个本书是写l至有一U程序设计语al验的程序员的?
import java.security.*;
import java.security.spec.*;
class MD5_Test{
public final static String MD5(String s){
char hexDigits[] = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd',
'e', 'f'};
try {
byte[] strTemp = s.getBytes();
MessageDigest mdTemp = MessageDigest.getInstance("MD5");
mdTemp.update(strTemp);
byte[] md = mdTemp.digest();
int j = md.length;
char str[] = new char[j * 2];
int k = 0;
for (int i = 0; i < j; i++) {
byte byte0 = md[i];
str[k++] = hexDigits[byte0 >>> 4 & 0xf];
str[k++] = hexDigits[byte0 & 0xf];
}
return new String(str);
}
catch (Exception e){
return null;
}
}
public static void main(String[] args){
//MD5_Test aa = new MD5_Test();
System.out.print(MD5_Test.MD5("XX"));
}