CHM 制作精灵

欢迎使用 CHM 制作精灵

“CHM 制作精灵”是一款将Html文档（网页文件）转化为 Html Help文档（CHM格式文档）的软件；是集 HTML Help Workshop工程创建，目录、索引编写及工程编译等多种功能于一身的 CHM电子图书制作软件。与HTML Help Workshop配合使用制作出来的 Html Help 文档会更见出色。欢迎使用 “CHM 制作精灵”来制作更多 CHM电子图书！！！

软件开发背景：

1、CHM文件格式已在网上广为流传，被称为一种电子书籍格式。其特点在于，它的每一个帮助页都是一个Web页，您可以像浏览网站一样容易地阅读 HTML 帮助文件。HTML 帮助文件甚至支持 ActiveX, JavaScrip, VBScrip 和 Dll 等。HTML帮助文件类似资源管理器的窗口的浏览方式，使用极其方便。另外，还具有管理文档方便、容量大、压缩比例高等优点。

2、目前HTML Help Workshop v4.74是制作chm唯一比较流行的工具，但是，存在着效率低，动作简单重复等缺点；另外，网上也有一些制作chm文档小软件，但是，它们多为共享软件，注册费少则人民币十几元多则上百元，几乎没有免费的，至少我没有找到。

3、制作一种高效工具让电脑来完成这种简单重复的工作是必要的，从文件格式（工程文件hhp、目录文件hhc、索引文件hhk）看文件结构是可读的，所以说制作一种高效工具是可行的。

软件功能：

将Html文档（网页文件）自动转化为 Html Help文档（CHM格式文档）。

1、 创建 HTML Help Workshop 工程。

2、为 HTML Help Workshop 工程编写目录及索引，其中标题是 Html文档（网页文件）<Title>。

3、编译 HTML Help Workshop 工程，最终生成 Html Help文档。

sxy868 版权所有
Copyright © 2002-2003
sxy868@sohu.com


- Draw a pie chart

此电子文档属C#乐园QQ群3256045整理。
- Draw a pie chart

上一页 目录 下一页

- Draw a pie chart

作者：blackwhites

email: blackwhites@sohu.com

日期：2000-10-11 15:06:31

 import java.util.*;

 import java.awt.*;

 import java.applet.Applet;

 public class Graph extends Applet {

  int    depth, radius;

 public void init() {

   float value;

   String at = getParameter("width");

   radius = (at != null) ?  Integer.valueOf(at).intValue() : 100;

   at = getParameter("depth");

   depth = (at != null) ? Integer.valueOf(at).intValue() : 20;

   at = getParameter("values");

   PieChartCanvas c = new PieChartCanvas(radius, depth);

   setLayout(new BorderLayout());

   // Create Hashtable to map color name (String) to Color type

   Hashtable colors = new Hashtable();

   colors.put("green", Color.green);

   colors.put("red", Color.red);

   colors.put("blue", Color.blue);

   colors.put("yellow", Color.yellow);

   colors.put("magenta", Color.magenta);

   colors.put("cyan", Color.cyan);

   colors.put("orange", Color.orange);

   colors.put("pink", Color.pink);

   colors.put("white", Color.white);

   colors.put("black", Color.black);

   // "value-color,value-color,..."

   StringTokenizer t = new StringTokenizer(at, ",");

   String s;

   int i;

   while (t.hasMoreTokens()) {

     s = t.nextToken();

     i = s.indexOf('-');

     value = Float.valueOf(s.substring(0, i)).floatValue();

     c.addSlice(value, (Color)colors.get(s.substring(i + 1)));

    }

   resize(c.getMinimumSize().width, c.getMinimumSize().height);

   add("Center", c);

   }

 }

 class PieChartCanvas extends Canvas {

   /*

   ** author     Ciaran Treanor  ciaran@broadcom.ie

   */

   final double aspectFudge = 2.5;

   int radius, depth, called = 1, numSlices = 0;

   float total = 0, value[] = new float[10];

   Color color[] = new Color[10];

   Graphics offGraphics;

   Image gfxBuff;

   public PieChartCanvas(int radius, int depth) {

     this.value = value;

     this.color = color;

     this.radius = radius;

     this.depth = depth;

     }

   public void paint(Graphics g) {

     int startAngle;

     float angle;

     Dimension d = getSize();

     if(gfxBuff == null) {

       gfxBuff = createImage(d.width, d.height);

       offGraphics = gfxBuff.getGraphics();

       offGraphics.setColor(getBackground());

       offGraphics.fillRect(0, 0, d.width, d.height);

       }

     // do the 3d effect

     for(int x = depth; x >= 1; x--) {

       startAngle = -45;

       for(int i = 0; i < numSlices; i++) {

         offGraphics.setColor(color[i].darker());

         angle = Math.round(360 * (value[i] / total));

         offGraphics.fillArc(0, x, radius, (int)(radius / aspectFudge),

             startAngle, (int)angle);

             startAngle += angle;

         }

     }

     // draw the pie slice

     startAngle = -45;

     for(int i = 0; i < numSlices; i++) {

       offGraphics.setColor(color[i]);

       angle = Math.round(360 * (value[i] / total));

       offGraphics.fillArc(0, 0, radius, (int)(radius / aspectFudge),

          startAngle, (int)angle);

          startAngle += angle;

       }

     g.drawImage(gfxBuff, 0, 0, null);

     }

   public void addSlice(float value, Color color) {

     this.value[numSlices] = value;

     this.color[numSlices++] = color;

     total += value;

     }

   public Dimension getPreferredSize() {

     return getMinimumSize();

     }

   public Dimension getMinimumSize() {

     return new Dimension(radius, (int)((radius / aspectFudge) + depth));

     }

   }

[JavaPie.hmtl]

 <HTML>

 <APPLET CODE=Graph.class WIDTH=150 HEIGHT=150>

   <PARAM NAME="depth"  VALUE="30">

   <PARAM NAME="width"  VALUE="120">

   <PARAM NAME="values" VALUE="1-red,2-green,3-blue">

 </APPLET>

 </HMTL>

上一页 目录 下一页

Made by an Unregistered version of eTextWizard V 1.95


（2）

此电子文档属C#乐园QQ群3256045整理。
（2）

上一页 目录 下一页

（2）

作者：friendcn

日期：2001-2-22 12:49:46

JSP tags run on the server and are not interpreted by the client like HTML tags are.

JSP tags provide proper code re-use.

HTML and JavaScript can be added to pages using a JSP mechanism called include. However, developers have a tendency to create huge JavaScript library files, and these libraries are included into the JSP file. The result is a much larger than necessary HTML page returned to the client. The proper use of include is for HTML snippets for such things as page headers and footers.

By abstracting out the Java code, JSP tags have promoted specialization of development roles.

Model-View-Controller (MVC)

JSP tags solved only part of our problem. We still have issues with validation, flow control, and updating the state of the application. This is where MVC comes to the rescue. MVC helps resolve some of the issues with the single module approach by dividing the problem into three categories:

Model

The model contains the core of the application's functionality. The model encapsulates the state of the application. Sometimes the only functionality it contains is state. It knows nothing about the view or controller.

View

The view provides the presentation of the model. It is the look of the application. The view can access the model getters, but it has no knowledge of the setters. In addition, it knows nothing about the controller. The view should be notified when changes to the model occur.

Controller

The controller reacts to the user input. It creates and sets the model.

MVC Model 2

The Web brought some unique challenges to software developers, most notably the stateless connection between the client and the server. This stateless behavior made it difficult for the model to notify the view of changes. On the Web, the browser has to re-query the server to discover modification to the state of the application.

Another noticeable change is that the view uses different technology for implementation than the model or controller. Of course, we could use Java (or PERL, C/C++ or what ever) code to generate HTML. There are several disadvantages to that approach:

Java programmers should develop services, not HTML.

Changes to layout would require changes to code.

Customers of the service should be able to create pages to meet their specific needs.

The page designer isn抰 able to have direct involvement in page development.

HTML embedded into code is ugly.

For the Web, the classical form of MVC needed to change. Figure 4 displays the Web adaptation of MVC, also commonly known as MVC Model 2 or MVC 2.

Figure 4. MVC Model 2

Struts, an MVC 2 implementation

Struts is a set of cooperating classes, servlets, and JSP tags that make up a reusable MVC 2 design. This definition implies that Struts is a framework, rather than a library, but Struts also contains an extensive tag library and utility classes that work independently of the framework. Figure 5 displays an overview of Struts.

Figure 5. Struts overview

Struts overview

Client browser

An HTTP request from the client browser creates an event. The Web container will respond with an HTTP response.

Controller

The Controller receives the request from the browser, and makes the decision where to send the request. With Struts, the Controller is a command design pattern implemented as a servlet. The struts-config.xml file configures the Controller.

Business logic

The business logic updates the state of the model and helps control the flow of the application. With Struts this is done with an Action class as a thin wrapper to the actual business logic.

Model state

The model represents the state of the application. The business objects update the application state. ActionForm bean represents the Model state at a session or request level, and not at a persistent level. The JSP file reads information from the ActionForm bean using JSP tags.

View

The view is simply a JSP file. There is no flow logic, no business logic, and no model information -- just tags. Tags are one of the things that make Struts unique compared to other frameworks like Velocity.

Struts details

Displayed in Figure 6 is a stripped-down UML diagram of the org.apache.struts.action package. Figure 6 shows the minimal relationships among ActionServlet (Controller), ActionForm (Form State), and Action (Model Wrapper).

Figure 6. UML diagram of the relationship of the Command (ActionServlet) to the Model (Action & ActionForm)

The ActionServlet class

Do you remember the days of function mappings? You would map some input event to a pointer to a function. If you where slick, you would place the configuration information into a file and load the file at run time. Function pointer arrays were the good old days of structured programming in C.

Life is better now that we have Java technology, XML, J2EE, and all that. The Struts Controller is a servlet that maps events (an event generally being an HTTP post) to classes. And guess what -- the Controller uses a configuration file so you don抰 have to hard-code the values. Life changes, but stays the same.

ActionServlet is the Command part of the MVC implementation and is the core of the Framework. ActionServlet (Command) creates and uses Action, an ActionForm, and ActionForward. As mentioned earlier, the struts-config.xml file configures the Command. During the creation of the Web project, Action and ActionForm are extended to solve the specific problem space. The file struts-config.xml instructs ActionServlet on how to use the extended classes. There are several advantages to this approach:

The entire logical flow of the application is in a hierarchical text file. This makes it easier to view and understand, especially with large applications.

The page designer does not have to wade through Java code to understand the flow of the application.

The Java developer does not need to recompile code when making flow changes.

Command functionality can be added by extending ActionServlet.

The ActionForm class

ActionForm maintains the session state for the Web application. ActionForm is an abstract class that is sub-classed for each input form model. When I say input form model, I am saying ActionForm represents a general concept of data that is set or updated by a HTML form. For instance, you may have a UserActionForm that is set by an HTML Form. The Struts framework will:

Check to see if a UserActionForm exists; if not, it will create an instance of the class.

Struts will set the state of the UserActionForm using corresponding fields from the HttpServletRequest. No more dreadful request.getParameter() calls. For instance, the Struts framework will take fname from request stream and call UserActionForm.setFname().

The Struts framework updates the state of the UserActionForm before passing it to the business wrapper UserAction.

Before passing it to the Action class, Struts will also conduct form state validation by calling the validation() method on UserActionForm. Note: This is not always wise to do. There might be ways of using UserActionForm in other pages or business objects, where the validation might be different. Validation of the state might be better in the UserAction class.

The UserActionForm can be maintained at a session level.

Notes:

The struts-config.xml file controls which HTML form request maps to which ActionForm.

Multiple requests can be mapped UserActionForm.

UserActionForm can be mapped over multiple pages for things such as wizards.

The Action class

The Action class is a wrapper around the business logic. The purpose of Action class is to translate the HttpServletRequest to the business logic. To use Action, subclass and overwrite the process() method.

The ActionServlet (Command) passes the parameterized classes to ActionForm using the perform() method. Again, no more dreadful request.getParameter() calls. By the time the event gets here, the input form data (or HTML form data) has already been translated out of the request stream and into an ActionForm class.

Note: "Think thin" when extending the Action class. The Action class should control the flow and not the logic of the application. By placing the business logic in a separate package or EJB, we allow flexibility and reuse.

Another way of thinking about Action class is as the Adapter design pattern. The purpose of the Action is to "Convert the interface of a class into another interface the clients expect. Adapter lets classes work together that couldn抰 otherwise because of incompatibility interface" (from Design Patterns - Elements of Reusable OO Software by Gof). The client in this instance is the ActionServlet that knows nothing about our specific business class interface. Therefore, Struts provides a business interface it does understand, Action. By extending the Action, we make our business interface compatible with Struts business interface. (An interesting observation is that Action is a class and not an interface. Action started as an interface and changed into a class over time. Nothing's perfect.)

The Error classes

The UML diagram (Figure 6) also included ActionError and ActionErrors. ActionError encapsulates an individual error message. ActionErrors is a container of ActionError classes that the View can access using tags. ActionErrors is Struts way of keeping up with a list of errors.

Figure 7. UML diagram of the relationship of the Command (ActionServlet) to the Model (Action)

The ActionMapping class

An incoming event is normally in the form of an HTTP request, which the servlet Container turns into an HttpServletRequest. The Controller looks at the incoming event and dispatches the request to an Action class. The struts-config.xml determines what Action class the Controller calls. The struts-config.xml configuration information is translated into a set of ActionMapping, which are put into container of ActionMappings. (If you have not noticed it, classes that end with s are containers)

The ActionMapping contains the knowledge of how a specific event maps to specific Actions. The ActionServlet (Command) passes the ActionMapping to the Action class via the perform() method. This allows Action to access the information to control flow.

ActionMappings

ActionMappings is a collection of ActionMapping objects.

Mailing list sample revisited

Let抯 see how Struts can solve the problems plaguing join.jsp. Two projects make up the rewrite. The first project contains the business logic portion of the application that is independent of the Web application. The independent layer could be a common service layer implemented with EJB technology. For demonstration purposes, I have created a package called business using an Ant build process. There are several reasons for the independent business layer:

Separate responsibilities

A separate package allows the manager to delegate responsibilities within the development group. This also helps promote developer responsibility.

Off-the-shelf

Think of the developer as treating the package as a piece of commercial software. By putting it into a different package, it gives it more of the feel of being off-the-shelf. The package might be off-the-shelf or might be developed by a different group within your organization.

Avoiding unnecessary build and unit testing

A separate build process helps avoid unnecessary build and unit testing.

Developed using interfaces

It helps to think from an interface perspective when doing development and avoids possible coupling. This is an extremely important fact. When doing your own business package, the business classes should not care if a Web application or a stand-alone Java application is making the calls. Therefore, avoid any reference to servlet API or Struts API calls in the business layer.

Stability

Not every organization conducts daily, weekly, or even monthly releases. Hence, when doing development, stable interface points are nice. Just because the business package is in a state of flux, does not mean the Web project should be in a state of flux.

Business build note

I use Ant to build the projects, and JUnit to run the unit test. The business.zip contains everything to build the business project, except for Ant and JUnit. The package script will build the classes, run the unit test, create the Java docs and jar file, and compress all of it into a zip file to deliver to a customer. You can deploy to other platforms by modifying the build.xml. Business.jar is in the Web download portion, therefore, you do not have to download and build the business package.

Web project

The second project is a Web application developed with Struts. You will need a JSP 1.1 and Servlet 2.2-compliant container. The quickest way to get started is to download and install Tomcat 3.2 (see Resources). Until there is a 1.0 release of Struts, I recommend getting the latest build from the Jakarta project (see Resources). This has been a huge issue for me, and I cannot insure that my sample Web project will build with your Struts download. Struts is in a constant state of change, and I have had to constantly update my project. I used jakarta-struts-20010105.zip for this project. Figure 8 displays the Web project layout. If you have Ant installed, running the build will create a war file called joinStruts.war that is ready for deployment.

Figure 8. Web project layout

Listing 4 displays the converted JSP file called joinMVC.jsp. The file has gone from 50 lines to 19 lines, and now contains no Java code. This is a huge improvement from a page designer perspective.

Listing 4. joinMVC.jsp -- simple JSP revisited

<%@ page language="java" %>

<%@ taglib uri="/WEB-INF/struts.tld" prefix="struts" %>

<%@ taglib uri="/WEB-INF/struts-form.tld" prefix="form" %>

<html>

<head>

<title><struts:message key="join.title"/></title>

</head>

<body bgcolor="white">

<form:errors/>

<h3>Enter your email to join the group</h3>

<form:form action="join.do" focus="email" >

    <form:text   property="email" size="30" maxlength="30"/>

    <form:submit property="submit" value="Submit"/>

</form:form>

</body>

</html>


（3）

此电子文档属C#乐园QQ群3256045整理。
（3）

上一页 目录 下一页

（3）

作者：friendcn

日期：2001-2-22 12:50:46

Page changes

The following are the list of changes that occurred using the Struts tag library:

Imports

<%@ taglib uri="/WEB-INF/struts.tld" prefix="struts" %>

The <%@page import? for Java has been replaced by <%@ taglib uri? for the Struts tag library.

Text

<struts:message key="join.title"/>

The resource property file contains the text for join.title. In this example, ApplicationResources property file contains the name-value pair. This makes string review and changes for internationalization easier.

Errors

<form:errors/>

ActionServlet or ActionForm builds the error message to display. These error messages can also be contained in the property file. ApplicationResources also provides a way of formatting the error by setting error.header and error.footer.

HTML Form

<form:form action="join.do" focus="email" >

JSP <form> tags and attributes replace HTML <form> tags and attributes. <form action="join.jsp" name="join"> has changed to <form:form action="join.do" focus="email" >.

HTML <input> tag has been replaced by <form:text/>.

HTML <submit> tag has been replaced by <form:submit/>.

Model -- Session state

JoinForm subclasses ActionForm and contains the form data. The form data in this example is simply the e-mail address. I have added a setter and getter for the e-mail address for the framework to access. For demonstration purposes, I overwrote the validate() method and used the error tracking feature of Struts. Struts will create JoinForm and set the state information.

Model -- Business logic

As we discussed earlier, Action is the interface between the Controller and the actual business object. JoinAction wraps the calls to the business.jar that was originally in join.jsp. The perform() method for JoinAction is displayed in Listing 5.

Listing 5. - JoinAction.perform()

public ActionForward perform(ActionMapping mapping,

                             ActionForm form,

                             HttpServletRequest request,

                             HttpServletResponse response)

                             throws IOException, ServletException {

    // Extract attributes and parameters we will need

    JoinForm joinForm = (JoinForm) form;

    String email = joinForm.getEmail();

    ActionErrors errors = new ActionErrors();

    // store input....

    try {

        business.db.MailingList.AddEmail(email);

    } catch (Exception e) {

        // log, print stack

        // display error back to user

        errors.add("email",new ActionError("error.mailing.db.add"));

    }

    // If any messages is required, save the specified error messages keys

    // into the HTTP request for use by the <struts:errors> tag.

    if (!errors.empty()) {

        saveErrors(request, errors);

        // return to the original form

        return (new ActionForward(mapping.getInput()));

    }

    // Forward control to the specified 'success' URI that is in the Action.xml

    return (mapping.findForward("success"));

}

Note: The perform() returns a class called ActionForward that tells the Controller where to go next. In this example, I am using the mapping passed in from the Controller to determine where to go.

Controller

I have modified the JSP file and created two new classes: one to contain form data and one to call the business package. Finally, I glue it all together with changes to the configuration file struts-config.xml. Listing 6 displays the action element I added to control the flow of joinMVC.jsp.

Listing 6. Action Configuration <action  path="/join"

         name="joinForm"

         type="web.mailinglist.JoinAction"

        scope="request"

        input="/joinMVC.jsp"

     validate="true">

    <forward  name="success"  path="/welcome.html"/>

</action>

The action element describes a mapping from a request path to the corresponding Action classes that should be used to process the requests. Each request type should have a corresponding action element describing how to process the request. On a join request:

joinForm is used to hold the form data.

Since validate is marked true, joinForm will try to validate itself.

web.mailinglist.JoinAction is the action class used to process requests for this mapping.

If everything works correctly, the request will forward to welcome.jsp.

If there is a business logic failure, the flow will return to joinMVC.jsp, which is the original page that made the request. Why is this? In the action element in Listing 6 is an attribute called input with a value of "/joinMVC.jsp". In my JoinAction.perform(), displayed in Listing 5, if the business logic fails, perform() returns an ActionForward using mapping.getInput() as the parameter. The getInput() in this instance is "/joinMVC.jsp". If the business logic fails, it will return to joinMVC.jsp, which is the original page that made the request.

Before and after Struts

As we can see from Figure 9, a lot of complexity and layers have been added. No more direct calls from the JSP file to the Service layer.

Figure 9. Before and after Struts

Struts pros

Use of JSP tag mechanism

The tag feature promotes reusable code and abstracts Java code from the JSP file. This feature allows nice integration into JSP-based development tools that allow authoring with tags.

Tag library

Why re-invent the wheel, or a tag library? If you cannot find something you need in the library, contribute. In addition, Struts provides a starting point if you are learning JSP tag technology.

Open source

You have all the advantages of open source, such as being able to see the code and having everyone else using the library reviewing the code. Many eyes make for great code review.

Sample MVC implementation

Struts offers some insight if you want to create your own MVC implementation.

Manage the problem space

Divide and conquer is a nice way of solving the problem and making the problem manageable. Of course, the sword cuts both ways. The problem is more complex and needs more management.

Struts cons

Youth

Struts development is still in preliminary form. They are working toward releasing a version 1.0, but as with any 1.0 version, it does not provide all the bells and whistles.

Change

The framework is undergoing a rapid amount of change. A great deal of change has occurred between Struts 0.5 and 1.0. You may want to download the most current Struts nightly distributions, to avoid deprecated methods. In the last 6 months, I have seen the Struts library grow from 90K to over 270K. I had to modify my examples several times because of changes in Struts, and I am not going to guarantee my examples will work with the version of Struts you download.

Correct level of abstraction

Does Struts provide the correct level of abstraction? What is the proper level of abstraction for the page designer? That is the $64K question. Should we allow a page designer access to Java code in page development? Some frameworks like Velocity say no, and provide yet another language to learn for Web development. There is some validity to limiting Java code access in UI development. Most importantly, give a page designer a little bit of Java, and he will use a lot of Java. I saw this happen all the time in Microsoft ASP development. In ASP development, you were supposed to create COM objects and then write a little ASP script to glue it all together. Instead, the ASP developers would go crazy with ASP script. I would hear "Why wait for a COM developer to create it when I can program it directly with VBScript?" Struts helps limit the amount of Java code required in a JSP file via tag libraries. One such library is the Logic Tag, which manages conditional generation of output, but this does not prevent the UI developer from going nuts with Java code. Whatever type of framework you decide to use, you should understand the environment in which you are deploying and maintaining the framework. Of course, this task is easier said than done.

Limited scope

Struts is a Web-based MVC solution that is meant be implemented with HTML, JSP files, and servlets.

J2EE application support

Struts requires a servlet container that supports JSP 1.1 and Servlet 2.2 specifications. This alone will not solve all your install issues, unless you are using Tomcat 3.2. I have had a great deal of problems installing the library with Netscape iPlanet 6.0, which is supposedly the first J2EE-compliant application server. I recommend visiting the Struts User Mailing List archive (see Resources) when you run into problems.

Complexity

Separating the problem into parts introduces complexity. There is no question that some education will have to go on to understand Struts. With the constant changes occurring, this can be frustrating at times. Welcome to the Web.

Where is...

I could point out other issues, for instance, where are the client side validations, adaptable workflow, and dynamic strategy pattern for the controller? However, at this point, it is too easy to be a critic, and some of the issues are insignificant, or are reasonable for a 1.0 release. The way the Struts team goes at it, Struts might have these features by the time you read this article, or soon after.

Future of Struts

Things change rapidly in this new age of software development. In less than 5 years, I have seen things go from cgi/perl, to ISAPI/NSAPI, to ASP with VB, and now Java and J2EE. Sun is working hard to adapt changes to the JSP/servlet architecture, just as they have in the past with the Java language and API. You can obtain drafts of the new JSP 1.2 and Servlet 2.3 specifications from the Sun Web site. Additionally, a standard tag library for JSP files is appearing; see Resources for links to the specifications and the tag library.

Final notes

Struts solved some big problems using tags and MVC. This approach aided in code re-usability and flexibility. By separating the problem into smaller components, you will be more likely to reuse when changes do occur in the technology or problem space. Additionally, Struts enabled page designers and Java developers to focus on what they do best. Yet, the tradeoff in increased robustness implies an increase in complexity. Struts is much more complex than a simple single JSP page, but for larger systems Struts actually helps manage the complexity. Additionally, I do not want to write my own MVC implementation, just learn one. Whether you use Struts or not, reviewing the Struts framework (excuse me, library) can give you a better understanding of JSP files and servlets features, and how to combine them for your next Web project. Just as struts are essential to the structure of a wing, Struts might become an indispensable part of your next Web project.


(翻译)如何成为一个优秀的jsp程序员

此电子文档属C#乐园QQ群3256045整理。
(翻译)如何成为一个优秀的jsp程序员

上一页 目录 下一页

(翻译)如何成为一个优秀的jsp程序员

作者：asp3000

email: coolknight@263.net

日期：2000-12-29 20:41:03

(翻译)如何成为一个优秀的jsp程序员

作者：Casey Kochmer

翻译：刘玉锋 http://www.21jsp.com

在网上看到很多问题是关于如何学习jsp的，正好网上看到一篇关于学习jsp的文章，就摘了一部分翻译过来，希望能对大家学习jsp有点指

导。

一个普通的错误是把JSP当作简化的 Java。它不是，（事实上, JSP 是简化的 servlets 。）程序员通常试着没有学习要求的支持技巧而

直接学习 JSP 。JSP 是一个衔接技术，并且成功地连接你需要理解的另外的技术。如果你已经知道 Java ， HTML 和 Javascript，这意味着

JSP 将确实是简单的。

需要成为一个成功的 JSP 程序员可以参考这个时间表。请注意下列：

*忽略你已经熟悉的步骤。

*训练的时间只是代表学习好足够的基础时间，这样才能转移到下一步。

１、建立并且理解你的Web Server。

因为Apache 是免费的并且在大多数平台上工作，为训练目的推荐 Apache。

安装时间：2 天。

２、 保证你理解 HTML / XHTML 。

你将需要了解html基础, 特别是 HTML 布局中的table的使用。XHTML 不久将代替 HTML ，学习 XHTML 的基础是一个好主意。许多程序员

通过 HTML IDE 学习 HTML ( 集成开发环境 ) 。因为大多数 HTML IDE产生混乱的HTMl语法，所以花时间学习手工写作html是很有必要的。因

为你将会使用 JSP 和 HTML 混合编程，精通HTML语法是重要的。所以，你必须能流利地写 HTML 。

训练时间：2 ～ 4 个星期。

３、开始学习 Java 。

开始学习 Java 1.3 理解 Java 基础是很重要的。不用担心学习Swing或 Java 的图形方面，因为在JSP 中你不会使用这些特征。集中精力

在 Java 工作的细节，学习 Java 的逻辑，也在 Java Bean上花时间。学习Applet是好的, 但是就象Swing， JSP 的大多数应用将不使用小程

序。

训练时间：3 ～ 6 个星期。

３、学习 JavaScript

学习怎么将 JavaScript在HTML中验证输入的Form元素。也学习 JavaScript怎么能在一 HTML 页以内修改Form的元素。最后要求你能从一

HTML 页内的事件中触发 JavaScript Function。

训练时间：一～ 2 个星期。

４、学习并且理解你的Web Server的更好的细节。

熟悉Web Server的特征，这是很重要的。

训练时间：2 天。

５、建立你的 JSP Server

我推荐以Tomcat开始。它可以很好地运行JSP程序。当你不能在生产使用Tomcat时，学习尽可能多的知识以便于更好的运行程序。另外, 许

多 JSP 程序员使用Tomcat。因此当你遇到一个问题时，你将容易发现帮助。

安装时间：一～ 2 天。

６、开始学习 JSP 。

基本的 JSP 学习通过的步骤 1到步骤6可以完成, 然后使用 JSP 对象和脚本写 JSP 程序来联系。学习 JSP 的另外一个方面可以学习怎么创

建一个分布式的应用程序。

训练时间：4 ～ 6 个星期。

７、学习更多的 JSP server。

没有关于更多的 JSP Server当然也可以运行jsp程序。然而, 许多 JSP server都由自己特殊的特征，可以让你更好的理解你的JSP 工程。

学习更多的Jsp server如何处理jsp程序是有必要的。同样也可以优化你的 JSP 应用程序，并且使之运行得更快而不出任何问题。

训练时间：2 ～ 7 天。

８、 学习 JDBC 。

JSP 大多数应用将使用数据库，JDBC 被用于数据库连接。经常忽略的一个事实就是，每个 JDBC Driver 所支持的东西是相当不同的。了

解并熟悉在jsp工程上被使用的 JDBC driver的细节是很重要的。

（有时这部分的学习被包含在前面 Java 或JSP的学习中了 。）

训练时间：1～ 2 个星期。

到现在，你已经成为了熟练的 JSP 程序员。仍然有很多需要学习，你可以考虑扩展你的知识比如 DHTML ， XML ，java证书， JSP Tag

Libraries 或 Servlets , 看你想要造什么类型的网站而决定了。

这些训练是JSP 的核心。你不必都学习上面所有的, 取决于你在工程中分配到什么任务和你已经有什么知识。但是这是我成功地训练程序员

的时间表。关键的单元是时间。平均的说, 5 个月时间确实能够训练一个人 ( 从开始到完成 ) 成为一个对jsp熟悉程序员。5 个月时间似乎很

长，但要成为一个资深的WEB程序员所学的东西远远不止这一些。

也许你认为这样学习一种语言花费的时间太长了，因为学 ASP 会更快、时间会更短。 但是学习 ASP 不需要学习java的。

下面是部分比较好的jsp学习书籍，可以直接在网上找到：

Servlets and JavaServer Pages (JSP) 1.0: A Tutorial (Marty Hall. 1999)

http://www.apl.jhu.edu/~hall/java/Servlet-Tutorial/

JSP: The Short Course (Ray Carnes 8.26.2000)

http://www.jspinsider.com/tutorials/jsp/Ray/JSPB_Intro.html

JavaServer Pages Fundamentals (Govind Seshadri 9.13.2000)

http://developer.java.sun.com/developer/onlineTraining/JSPIntro/

The Java Tutorial (Sun)

http://java.sun.com/docs/books/tutorial/

JSP Tag Extensions (Wrox 2000)

http://www.jspinsider.com/tutorials/tagextensions/wrox/4656_Content.html

JSP Product Page (Sun)

http://java.sun.com/products/jsp/


《国内不谈java》（转）

此电子文档属C#乐园QQ群3256045整理。
《国内不谈java》（转）

上一页 目录 下一页

《国内不谈java》（转）

作者：NOVEMBER RAIN

日期：2001-4-8 23:21:01

国内不谈java－－会有千万人跳出来和你争嘴的。

越是如此，我越是不忍心不说出来，越是不不忍心看到在这个

领域被国外的同行越拉越远－－在硅谷的感受。

我是96年毕业的,正值java刚出,火气冲天之时。

我当时是一名C++的狂热者，有着3年的C++经验。

接触java也仅仅是在作毕业设计的时候用过，对java也算是有了基本了解，

那时的java才jdk1.0，烂的很，连些基本功能都没有，和大家一样，

对java根本就不认可。作完了设计之后，就把java扔到一边去了。自认为C++不错，

还是干自己的老本行吧。毕业时我认为精通C++，并且有java的基础，

算是身怀两种绝技了，在国内的IT（那时还不叫IT）还可以混个明堂出来吧。

怀着对未来美好的憧憬和对C++的无限的崇拜，我出来闯荡了。

唉－－出去的情况于我的想法完全两样，delphi,VB漫天飞，C＋＋高不可攀，根本

无用武之地。我大失所望，可我偏偏又是一个C＋＋偏执狂，要我去改学其它语言，

在我看来简直是对C++的侮辱，也是对我信念的侮辱，是绝对不可能的！对国内失望之余

，

于是我想到IT技术前沿的美国，于是满怀希望来到到了IT精英汇集的地方－－硅谷。

我想这下总算可以施展我深藏多年的C＋＋才华了吧。

我－－再一次的错了－－在硅谷，VB,delphi根本不入流，虽然C++还继续再用，

但是已经是大不如以前了，不过有c++背景的找工作要相对容易些。

这里，程序员们，大小的managers，chargers只对Java感兴趣。

没想到，万万没想到。－－这里反微软的气氛很浓，

也许是Sun,Oracle,IBM,AOL等巨头公司的大本营在此的缘由吧。呆过一段时间后，

我发现这里只要是稍大一点的公司，都在同时在维护着几套System，

要一劳永逸的解决这些问题，让这些System无缝的衔接起来，

java是最好的不过的解决方案。

在这里，个大巨头公司们对java几乎在玩命似的疯狂：

ibm在全球16个java实验室24小时续以奋战，扛着“java就是一切”的大旗，

投入java的钱不比sun的少；

intel整装待发，全力以赴赶制java芯片，以求在java谋得一席之地；

oracle,Sybase,informix,DB2这些王牌数据库厂商更是纷纷下马，

把java绑定到自己的产品中，提供对java最全面，最直接的支持；

Inprise,BEA,Iona,netscape联盟等一大批系统集成、支援厂商，

也不甘落后，争先恐后的开发自己的java工具、应用服务软件，

目的只有一个，让自己的产品带上一个响亮的"J"字；

cisco,3Com,HP,NEC等一大批网络设备供应商对embed  java表现了浓厚的兴趣，

一批又一批的带java接口的智能设备相续涌现出来，在这个市场上的竞争异常激烈，谁

也不敢怠慢；

sun自己就更不用说了，sun创造了java,但java并非sun的专有，来自巨头们的竞争，

也让sun感到了前所未有的压力，在“捍卫java，保卫java,发展java”的方针下，

带领巨头们发布了面向不同领域的各个版本：面向PC领域的java2 Standard Edition，

面向企业级计算的Java 2 Enterprise Edition，面向嵌入式系统的Java2 Embedded Ed

ition,

面向智能终端的Personal Java Edition。

在这样的一种趋势下，迫不得已，只有放下曾经让我无限自豪、热情彭湃的C++－－我心

爱的C++！

一边，在国内，是还达不到使用C++这样的高度；

另一边，在硅谷，C++已经丧失了昔日的辉煌。

感叹万余，痛定思痛－－随即，以着极大的热情投入到java的事业中，

幸好有着C++的功底和以前对java的基本接触，java很快就上手，

来到了java世界里，啊，原来java还可以这么用，这是以前根本没想到的，

以前一直以为自己是个oop行家，这才发现跟java比，简直就是小巫见大巫－－

oop在java中被运用的炉火纯青，java本是是一个开放的体系，各家厂商都可以

对她扩展、实现，要维护整个java世界的纯洁，他们采用了一种绝妙的方法，

运用java的100%oop特点，对于规范的定义只是一些接口，而这些接口的实现，

则完全由各个厂家去负责，多么的和谐，多么的完美！理解不了这些，你就根本

无法理解象EJB,Servlet/JSP,JTA、RMI/IIOP、JNDI,JMS，Jini....这些java新秀的威力

，

稍大一点的公司（除了Microsoft），无一不对她趋之若宠，源源不断的

钱财、人力都愿意往这里扔。这仅仅只是个j2ee，也是到目前为止，

业界中最为完美的企业解决方案，就更不用说j2me了，

想做下一代internet接入设备，除了j2me可以说是别无选择，更要命的是她

完全可以与现有系统紧密的衔接起来......

......

－－我并非是想把C＋＋说得一无是处，我本人对C＋＋仍然是有着无比的崇拜，

只是每把刀都有每把刀的用处，在系统、支撑软件领域，C++还是老大，

只是不要把这种老大的思想随处烂放。在应用领域现在是java，不管你承认也好，

否认也罢，辛辛苦苦用C++写的一套Solution才买10万还不到，而java轻松

就完成的Solution可以卖到几百万，这就是区别；同样，如果仅仅把java当作

applet，application用在桌面环境中，她的的确确又比任何一种语言都烂。

我所说的只是国内的环境影响着我们每一个人，当java one 2000在美国红红火火的举行

，

多达4万家公司挤进会场，更是有3000余名专家、学者在会上慷慨陈辞时，而国内还是不

以为然，

守着以前的老家当，倒是精明的日本人，早就预定了数十个座位；

当个大公司在java的领域里进行惨烈争夺的时候，国内还抱着VB,Delphi 枕着C＋＋睡大

觉。

“java？－－不过是个玩具儿”，朋友、兄弟－－我真的再也不想听到这样的话了，也

许你说这话的时候，

有一丝的快感，但是你应该知道，在你笑得时候，人家国外的同行比你笑得更开心，他

们是何等的希望

我们永远都把她当作玩具！

我真的希望国内的朋友们，到网上去看一看，到国外的公司去看一看，不要被国内的氛

围、环境所左右。

我不想再说了，我实在是不忍心看到在这个领域里，被国外的同行越拉越远！－－事实

上是已经被远远的

拉在后面！


《学不会的JAVA，消不了的忧愁》之五——献给那些酷爱计算机编程的人们

此电子文档属C#乐园QQ群3256045整理。
《学不会的JAVA，消不了的忧愁》之五——献给那些酷爱计算机编程的人们

上一页 目录 下一页

《学不会的JAVA，消不了的忧愁》之五——献给那些酷爱计算机编程的人们

作者：NOVEMBER RAIN

email: haoy@dongyu.com.cn

日期：2001-5-9 17:36:13

各位网友，这是《学不会的JAVA，消不了的忧愁》之五了，在座的不泛JAVA高手，小生一而三，三而再地在爪哇连盟.COM自由论坛上发布奇谈怪论，不知是否碍了大家?不管怎样，灯火夜夜开，闲言说不尽，在没有接到贴子管理员的严重警告之前，这个长篇大贴将继续连载下去。

为了100%尊重网友，所有这些贴子都是本人亲手一词一句精心制作，字里行间不出现任何关于其它URL的链接，也没有任何商业味道，请大家放心阅读。

本人自学JAVA两年有余，第一年发现自己对JAVA很熟，第二年觉得JAVA变了，很多地方还没有摸她一下呢，现在更糟，越学越木了，真是“学不会的JAVA，消不了的忧愁”。sign~~(我在长叹息！）

……

--------------------------------------------------------------------------------

闲话少说，言归正传。

上贴讲了关于类和对象的一些基本概念，现在我们再来看看类库。

不知大家是否在制造厂呆过，在深圳，像塑胶制品厂、五金制品厂可多啦。在这些厂里，生产东西有个生死逃不了的工序--工模制造。工模做好后，成了其它产品制造的榜样，应该说“槽”更确切一点。会造工模的比会造JAVA类的吃香。或许，前者造的模子是硬的，摸得着的，给人踏实的感觉，而后者造的JAVA模子（Classes)是软的，给人不实在的感觉。所谓外行看热闹，内行看门道，那些造塑胶、五金模模的工人当然不懂我们用JAVA造的软件模子。然而，硬的模子也好，软的模子也好，它们在管理方面大都相同。会管理JAVA模子的人一定会管理工模，反过来，就不成立了，这点，值得我们搞JAVA的家族骄傲。

制造厂产品制作越多、越久，模子也就越多。很多产品外形大同小异，君不见诺基亚3210和8250的机型，除了后者小一点外，身材基本相同，所以大多产品的工模重复使用的概率很高。为了下次产品开发能够快速找到本次做好的这个模子，对模子进行分门别类分开存放并按名称排列是一种很好的方法。喏，这是诺基亚3210系列的工模柜，放的都是这个系列的模子，还有一些模子说明书、技术资料等。那边是诺基亚8250系列的工模柜，存放的是8250系列的模子以及说明书和技术资料……所有这些工模柜都存放在一个房子里，暂就叫它工模仓库吧。这个工模仓库对工厂来说，用途你瓦大不大？

所有的JAVA的模子，即所有的JAVA 类，管理方式与上面所讲的一模一样。你用JAVA开发软件的时间越长，编写的类就会越多，为了下次能借用本次编写的类，你得和上面一样，对JAVA类分类型（请原谅我在同一句话里用一个字的两种不同含义），这类是专门用来制作按钮、标签、文本框等等的，那类是为了开发数据库应用程序专用的，另一类是专调颜色的……每一个类别都帮他取一个名字吧，取什么名字好呢？在爪哇里面，分别叫Swing(小时候叫AWT)、SQL、Color……，通常我们不这样说Swing类别、SQL类别、Color类别……，而是说Swing包、SQL包、Color包……所有这些包存放在一个地方（也就是一个文件夹下），由此可见，这个文件夹可是个金矿。

内含了所有JAVA包的文件夹就是通常讲的JAVA类库，现在你应该明了，类库里面放的都是包，大包小包；包里面放的都是类，大类小类，类里面放的都是什么？是我上贴讲的那些东西，方法啊、属性啊等等。当然，有时候，包里还可以放入别的东西，象接口、异常、错误，这些东西你现在还不知道，我后面的帖子会再慢慢告诉你。

一个很重要的概念，在工模库里面，有些工模只有同类产品可以借用，有些工模所有产品都可以借用，还有些根本无法借用。JAVA包里面的类也一样，有所有包里面的类都可以用得到的，叫public类；有本包之内的类才可以用得到，叫protect类；还有些类是吃独食的，任何其它类都不准打它主意，这叫自私类（private)，更绝的是，大多数类都会生类仔，而有些类是性无能，不会生仔的，这又叫什么类呢？JAVA里面叫性无能类，哦，不不不，叫终止类（final),似乎还有几种类，但我还不知道怎样称呼，问问SUN公司吧！

在爪哇里面这样分类有什么作用呢，用途可大，这是后话，本贴不提。

本来打算再讲讲类的继承概念和JDK的简明使用，由于时间有限，这贴不讲啦，请看下贴吧！

--------------------------------------------------------------------------------

这贴我们讲了类库的概念，还有什么叫包，为什么要把所有类打成不同种类的包，同时涉及到了public类、protcet类、pvivate类、final类的一些解释，这些都是很重要的概念，大家懂了吗？

直到现在，我没有举一个程序例子来说明问题，随着贴子的发展，例子会越来越多，而到了讲解JDBC、Solect、JAVA Bean、Enterprise JAVA Bean、Selvlet、Applet……等等的时候，没有例子讲解简直无法开口。

请大家百分之二百放心，我的口中从不出人家听不懂的话，我曾经让一个64岁还在江西种田的农夫明白了VC动态调用的概念。

在学校里，我办计算机自由论坛接近3年。课堂上，老师讲DOS没人听，课堂外，我讲DOS教室挤得人挨人。

我的知识很贫泛，懂得东西太少，但我能把我已经懂了的让大家都懂。我说话的方式曾经让很多小企业的老板吃惊，甚至连我们学校的图书馆馆长也为之动容，这也就是为什么我在学校的时候就能接到软件开发的业务。

哎哎，又讲了闲话，对不起大家，这贴到此结束，好戏在后头，请看下贴《学不会的JAVA，消不了的忧愁》之六——献给那些酷爱计算机编程的人们。


1. Servlet的基础结构

此电子文档属C#乐园QQ群3256045整理。
1. Servlet的基础结构

上一页 目录 下一页

1. Servlet的基础结构

作者：shiholly

日期：2001-2-7 11:15:09

Servlet 教程

1. Servlet的基础结构

    下面我们来谈谈处理用户GET请求的最简单的servlet框架。GET请求就是用户在浏览器的地址栏里输入一个地址、在网页上点击连接或产生一个没有定义方法的HTML表单后产生的请求。Servlets还能轻松处理表单的提交(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" 读取http信息头（如cookie）

// 和HTML表单数据 (如用户输入和提交的数据)

// 用"response"指定http响应 和http信息头

// (如指明信息的类型、设定cookie).

PrintWriter out = response.getWriter();

// 用 "out"输出内容到浏览器

} }

    不管是用doGet还是doPost方法，servlet都要用到HttpServlet扩展类。这些方法可分为两类：HttpServletRequest和HttpServletResponse。HttpServletRequest含有获得表单数据、HTTP信息头等信息的方法。 HttpServletResponse则包含指明HTTP响应 (200, 404,等。)、信息头(Content-Type, Set-Cookie, 等。)的方法，更重要的是你能使用PrintWriter方法向客户端输出信息。注意doGet和doPost方法会抛出两个异常，所以必须在定义里包含他们。要用到PrintWriter、HttpServlet和HttpServletRequest， HttpServletResponse方法还必须分别引入java.io、javax.servlet和javax.servlet.http。 一般来说，doGet和doPost是被service方法调用的，但有时候你可能想越过service方法而直接使用自己定义的service方法，比如定义一个既能处理GET也能处理POST请求的servlet。

本人译自http://www.apl.jhu.edu/~hall/java/Servlet-Tutorial/

有不妥之处，还请各位指教！


2. 一个简单的Servlet--产生存文本

此电子文档属C#乐园QQ群3256045整理。
2. 一个简单的Servlet--产生存文本

上一页 目录 下一页

2. 一个简单的Servlet--产生存文本

作者：shiholly

日期：2001-2-7 11:19:51

Servlet 教程

2. 一个简单的Servlet--产生存文本

    下面是一个产生存文本的简单servlet的实例。

2.1 HelloWorld.java

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");

}

}

2.2 编译和安装Servlet

    注意，安装servlet的方法会因web服务器不同而不同。详细请参考您使用的web服务器所带的安装文档。Java Web 服务器即Java Web Server(JWS)2.0上带有联机实例。在JWS里，servlet程序必须JWS放在安装目录的servlets目录下才能运行。如果web服务器由多人使用并且没有好的虚拟服务器的下层结构来自动避免冲突，你也可以像我一样在servlets目录下创建一个独立包（我取名叫hall）即在servlets目录下创建一个hall子目录，并把你写的servlet放在该目录下。这里我把HelloWorld.java放在hall目录下。其他大多数服务器的安装都大同小异，本教程的servlet和JSP例子都在BEA Weblogic和IBM的WebSphere 3.0测试通过。其中WebSphere对于虚拟服务器有一套优秀的机制，所以没有必要为了名字的冲突而单独使用包。

    如果你以前从未用过包，那么有两种在包内编译类文件的方法可以使用。

    一种方法就是在CLASSPATH里设置好指向包含有你的servlet文件的目录，这样就可以在该目录下正常编译。举个例子，如果你在Windows下的servlet的目录是C:\JavaWebServer\servlets，包名（即子目录）是hall, 那么在dos窗口设置如下：

DOS> set CLASSPATH=C:\JavaWebServer\servlets;%CLASSPATH%

DOS> cd C:\JavaWebServer\servlets\hall

DOS> javac YourServlet.java

    第一步、设置路径。最好一步到位，免得以后每次都要重新打开一个dos窗口。Windows 95/98系统下在autoexec.bat文件里加入"set CLASSPATH=..."表达式，将CLASSPATH指向servlet.jar和jsp.jar。在NT下，开始菜单-设置-控制面板，选择“系统”，选择“环境”，然后输入变量名和变量值。注意如果你的包名是 name1.name2.name3的格式，还应该设置CLASSPATH指向包的顶层（即name1）。

    第二种方法是转到包的上层目录，然后执行"javac directory\YourServlet.java" (注意Windows下是反斜杆)或者unix下"javac directory/YourServlet.java" (注意Unix下是正斜杆)。再举个例子，比如你在windows下的servlet目录是C:\JavaWebServer\servlets，包名也就是子目录名是hall，那么执行下列操作：

DOS> cd C:\JavaWebServer\servlets

DOS> javac hall\YourServlet.java

    注意Windows下，大部分JDK 1.1版本的javac都要求反斜杆，后来在JDK 1.2里得到了修正，但是由于很多web服务器是根据JDK1.1来配的，所以为了方便起见许多servlet作者还是坚持反斜杆。

    javac的-d选项可以用来指定将.class文件和源码文件放在不同的地方。

2.3 运行Servlet servlet

    一般是放在JWS安装目录下的servlets目录下，并通过 http://host/servlet/ServletName

译自http://www.apl.jhu.edu/~hall/java/Servlet-Tutorial/


5.2 分布式应用开发技术概述

此电子文档属C#乐园QQ群3256045整理。
5.2 分布式应用开发技术概述

上一页 目录 下一页

5.2 分布式应用开发技术概述

作者：easyinfonet

email: easyinfonet@sina.com.cn

日期：2000-11-14 23:14:46

5.2 分布式应用开发技术概述

目前分布对象技术已经成为建立应用框架(Application Framework)和软构件(Software Component)的核心技术，在开发大型分布式应用系统中表现出强大的生命力，并形成了三项具有代表性的主流技术，即OMG的CORBA(Common Object Request Broker Architecture)、Microsoft的ActiveX/DCOM (Distributed Compound Object Model)和SUN公司的Java/RMI。

5.2.1  CORBA、DCOM、RMI

1．CORBA

----OMG是一个非盈利性国际组织，致力于使CORBA成为"无所不在的中间件"。1989年成立时仅有8家公司参与，而今天已经是拥有900多个机构成员的"议会式"标准化组织，世界上几乎所有最有影响的计算机公司（如IBM、Microsoft和HP等）、著名的工商企业（如Boeing、Citibank和FordMotor等）和大学研究机构都是这个组织的成员。OMG所制定的分布对象计算标准规范包括CORBA/IIOP 、对象服务、公共实施和领域接口规范。遵照这些规范开发出的分布计算软件环境可以在几乎所有的主流硬件平台和操作系统上运行。现在，CORBA/IIOP已成为Internet上实现对象互访的技术标准，OMG的IIOP也已成为许多公司（如Oracle、Netscape、Sun和IBM等）进行系统集成的基本协议。1995年以来，基于CORBA软件的企业级应用发展迅猛，大有覆盖DCE之势。目前世界上有一定影响的CORBA软件制造商已有10多家。

2．DCOM

ActiveX/DCOM是由Microsoft推出的对象构件模型，最初用于集成Microsoft的办公软件，目前已发展成为Microsoft世界的应用系统集成标准，并集中反映在其产品ActiveX中。在分布计算技术上，OMG的优势比Microsoft至少领先2～3年。目前，只有OMG的技术能够支持异构环境中大型分布式应用的开发，而Microsoft的DCOM技术尚不能胜任。Microsoft的优势主要表现在应用和市场能力上。从未来市场策略考虑，Microsoft决定支持OMG提出的OLE/COM与CORBA的互操作标准，从而使COM的对象能够与CORBA的对象进行通信。今后3～5年内，OMG和Microsoft的分布对象技术将共存，并在许多方面相互渗透。

3．RMI

--- -按照Sun和Javasoft对Java的界定，Java是一个应用程序开发平台，它提供了可移植、可解释、高性能和面向对象的编程语言及运行环境。RMI(RemoteMethodInvocation)是分布在网络中的各类Java对象之间进行方法调用的ORB机制。CORBA技术与Java技术存在天然的联系，因为Sun是OMG的创始成员，CORBA标准中的许多内容（例如IDL标准、IIOP标准）是以SUN公司提交的方案为核心制定的。CORBA与Java/RMI的主要区别在于以下两个方面：

·    程序设计语言无关性是CORBA的重要设计原则，而Java/RMI依赖于Java语言和Java虚拟机；

·    Java/RMI技术的最大成就是使对象能够在Internet上迁移和执行，而CORBA2.0标准中只考虑对象的远程访问，没有对象作为"值"传递的承诺。

----由于这两个技术的天然联系和各自的优势，CORBA技术与Java技术的融合已成为必然。

5.2.2  发展趋势

----与Internet技术结合是分布对象技术值得重视的发展趋势。Internet技术的发展，特别是90年代中期WWW技术的广泛应用，给分布对象技术的应用开辟了更广阔的空间，Object Web成为人们关注的新热点。从技术的角度看，WWW应用已经经历了Hypertext Web和Interactive Web两种形态，但都存在很大的局限性，突出地表现为应用接入能力受到限制。Java的出现为在浏览器上运行程序提供了新的途径，标志着ObjectWeb时代的开始。就以网络为中心的分布式应用的发展而言，这种把对象下载到浏览器中执行的机制也有局限。主要表现在大量的遗留应用不是用Java写的，自然无法下载到浏览器中运行；同时，某些重要的应用，包括信息共享与管理类的应用(如数据库系统)以及信息采集与信号监测应用(如实施网络管理的Agent)在逻辑上不能下载到客户端运行；另外，需要"减肥"的客户机可能难以承受越来越庞大的应用系统。尽管Java的RMI解决了访问异地Java对象的问题，但如何管理和访问异地其他大量非Java对象的问题并没有解决。另外，JavaRMI没有提供分布对象事务管理等服务。1996年下半年，Java与CORBA开始携手，共同奠定ObjectWeb时代的技术基础。

----何谓ObjectWeb？对此目前并没有公认的定义，但可以从应用和技术两个角度给出一定意义上的解释。

----1．应用内涵

----ObjectWeb是继HypertextWeb和InteractiveWeb之后新的Web应用，包括"保守的"和"理想的"两类应用形态。

·    保守的应用形态

用户能够按照对象设计者的思路自由、便捷地在Internet上操纵感兴趣的对象；

·    理想的应用形态

用户能够按照自己的需要自由、便捷地在Internet上操纵感兴趣的对象。

----与HypertextWeb和InteractiveWeb比较，Objec tWeb应用给用户的突出感受是操纵的实体不再局限于含有"超链"的文档，作用于实体的操作行为不再局限于"下载"一类的文件传输行为。下面给出两个应用(虽然目前还没有成熟的应用，但实现该应用的技术途径已经成熟)的简单陈述，以帮助对bjectWeb的理解。

---  -(1)开放的Internet/Intranet管理

----  典型的分布式系统管理采用Agent/Manager体系结构，Agent运行在被管理的设备(如服务器、路由器和智能交换机等)上，Manager通过与Agent的交互实施分布式系统管理。但问题是Manager不能管理采用新标准的新设备，这对于Internet管理是不合适的。开放的Internet/Intranet管理有两方面的含义：第一是指Agent能够在Internet上公布其管理功能的接口，并接受任何合法的Manager按照标准的远程对象访问协议(如CORBA/IIOP)所进行的访问；第二是指Manager能够访问任何时刻加入Internet的Agent，只要该Agent支持Internet上标准的远程对象访问协议，并在加入Internet时按照标准的协议公布其接口。这时，Manager就是管理对象的浏览器。我们注意到Sun公司前不久公布的Jini能够实现该应用构思。

- -- -(2)Web组合文档

--- -传统的组合文档(如MicroSoft的Office95)是台式应用，没有跨网络引用组件的功能。而目前流行的Web浏览器基于超文本结构，不能像组合文档一样自由组合和操作对象。Web组合文档较之传统的组合文档增加了跨网络引用组件的能力，这意味着复杂的组合文档应用能够在手持PC或其他瘦客户机上运行。例如，一个汽车设计专家在远离自己的设计中心讲学时，能够通过随身携带的笔记本计算机，现场演示从设计中心服务器上选择若干汽车部件，修改后组合一种新型的汽车，并现场调用设计中心的仿真系统对新设计的汽车进行各种仿真演示。

---- 2。技术内涵

--- -ObjectWeb技术主要包括对象开发技术和对象访问技术。从当前技术现状看，ObjectWeb技术成分如表1.1所示。

表1-1 ObjectWeb技术构成

    Hypertest Web/Interactive Web    ObjectWeb

对象    超文本    COM对象，Java对象、CORBA对象、各类组件

对象访问    HTTP/CGI    DCOM、RMI、CORBA/IIOP、HTTP/CGI

----"对象"与"对象访问"的关系可以用"货物运输系统"来类比。"对象"是ObjectWeb上的"货"。Internet上已存在大量的超文本，相对而言COM对象、Java对象、CORBA对象和各类组件则少得多。由于台式环境中的COM对象非常丰富，今后几年DCOM对象可能首先会在Internet上丰富起来。按照Java技术现在的发展势头，Internet上的Java对象也会很快多起来。由于CORBA对象具有语言、操作系统和硬件平台无关性的特点，它是最容易将大量的遗留应用和C＋＋、Smalltalk对象带入ObjectWeb的技术；随着Java与CORBA的携手，Java对象又会成为CORBA对象的特例；COM对象与CORBA对象的互操作标准已经制定。CORBA对象将迎来大发展的时期。

----  "对象访问"技术是ObjectWeb上的"车"。HTTP用于下载HTML页面和JavaApplet。Microsoft的对象RPC、RMI和CORBA/IIOP协议分别是远程访问DCOM对象、Java对象和CORBA对象的协议。从访问异地应用的意义上讲，HTTP/CGI也可视为一种对象访问协议，只是功能和性能存在很大局限。RMI与CORBA正在逐步融合，有人称融合后的对象访问技术为Java/CORBA。今后一个时期，在ObjectWeb领域占主导地位的技术将是ActiveX/DCOM和Java/CORBA。ActiveX/DCOM的应用丰富，占据了PC用户的绝对优势，一旦大力投入ObjectWeb的开发，市场优势不可阻挡。Java/CORBA的技术优于ActiveX/DCOM，发展势头和潜力不可低估。也许，在ObjectWeb领域ActiveX/DCOM与Java/CORBA的关系会像操作系统领域NT与Unix的关系一样。发展Java/CORBA的当务之急是应用开发，把握未来的最好办法是创造未来

5.2.3 三种主流技术比较

1．CORBA

CORBA有几个基本的优点：与开发语言无关的独立性，与开发者无关的独立性和与操作系统无关的独立性。CORBA的ORB在当前每一种主流操作系统上均有实现（仅就Microsoft的各种操作系统来说，CORBA获得的支持甚至超越了DCOM）。除此之外，CORBAORB可以访问多种语言实现的对象（包括C++、COBOL、Smalltalk和Java）。借助于IIOP，某一开发者（比如说Visigenic）开发的CORBAORB能够获取，操作远程的由其他的开发者（比如说IONA）开发的对象。JavaORB允许客户端在没有安装任何特别软件的情况下实现Java客户端应用程序。（JavaORB的类可与小应用一起动态下载，也可能与浏览器捆绑在一起。）

2．DCOM:Microsoft的法宝

目前，Microsoft的分布式组件对象模型（Distributed Componont Object Model）仅运行于两种操作系统之上：Windows95和WindowsNT4。Microsoft正在与第三方开发商协作，以将DCOM移到其它的操作系统上（包括MVS和几种UNIX操作系统）。象CORBA一样，DCOM是独立于语言的，它用Microsoft的对象描述语言（ODL）通过接口对对象加以描述。

与CORBA相比，DCOM有三个缺点。首先，它由单一开发者（微软）定义并控制，这大大限制了DCOM使用者的选择范围（比方说开发工具和风格）。其次，DCOM缺乏众多的平台支持，这极大程度地制约了代码的可重用性和DCOM应用的可扩展性。最后，与CORBA相比，DCOM是一种非常不成熟的技术。尽管微软目前正为DCOM加入消息和事务支持，但这些功能在1994年的CORBA2.0就已经实现了，并且正由几家不同的CORBA软件开发商所发行。

为了使一个Java的小应用/应用程序得以访问服务端的DCOM对象，开发者不得不使用IE浏览器和Windows95/NT平台，只有这样才能支持客户机软件。这样的限制当然削弱了应用程序在Web上的可用性。而另一方面，DCOM的一个优势在于，对WindowsNT/95用户免费。过去的历史已经表明，这是与Microsoft竞争的重大障碍。

3．RMI：Sun的Java-Only解决方案

Remote Method Invocation是最新的JDK1.1中的重要特色。RMI使得Java客户能够访问远程服务对象。这听起来似乎十分类似于CORBA，但两者并不一样。其关键在于服务器端的应用程序也必须用Java编写，且只能使用JDK1.1中提供的工具。你根本无法把过去编制的代码加到新程序中去，除此之外，RMI还有许多其它缺陷。

与CORBA不同，RMI没有服务这一概念。另外，根据RMI写出的Java服务器对象往往性能低劣，这个缺点源于Java虚拟机。（有趣的是，JavaCORBA服务器比RMI服务器表现出更好的性能。）RMI也不包括象CORBAORB那样的对象击活功能。

实际上，RMI及Java技术更可能向OMG的标准靠拢，而不是背道而驰。Sun已经宣布，Java事务服务（JavaTransactionServeces）将建立在OMG的对象事务服务（ObjectTransactionServeces）。该公司还曾发布其长远计划：使RMI对象可以通过IIOP互通讯。

总而言之，RMI对于用纯Java书写的小规模的应用程序来说，是一种可行方案。但CORBA提供了集成的基础，这种集成是指新开发的代码和已有对象的集成，同时允许将来加以扩展。在做出取此舍彼的选择之前，必须权衡上面的各种因素，并仔细审视每种技术的现状。


5.3 CORBA技术及实例

此电子文档属C#乐园QQ群3256045整理。
5.3 CORBA技术及实例

上一页 目录 下一页

5.3 CORBA技术及实例

作者：easyinfonet

email: easyinfonet@sina.com.cn

日期：2000-11-14 23:15:19

5.3  CORBA技术及实例

CORBA是一种规范，它定义了分布式对象如何实现互操作。在WorldWideWeb盛行之前，特别是Java编程语言风靡之前，C＋＋开发者基本将CORBA作为其高端分布式对象的解决方案。

实际的CORBA规范归对象管理组（ObjeotManagementGroup）管辖，这是一家由700多家公司组成的开放的研讨会，其工作是制订对象计算的开放标准。CORBA对象可以用任何一种CORBA软件开发商所支持的语言，如C、C++、Java、Ada和Smalltalk，来编写。同样地，CORBA对象可以运行在任何一种CORBA软件开发商所支持的平台上，如Solaris，Windows95/NT,OpenVMS、DigitalUnix、HP-UX或AIX等。这意味着，我们可以在Windows95下运行Java应用程序，同时动态调入并使用C＋＋对象，而实际上，该对象可能存储于一个在Internet上的UnixWeb服务器上。

使用    接口描述语言（InterfaceDescriptionLanguage）编写的对象接口，使得与语言无关的独立性成为可能。IDL使得所有CORBA对象以一种方式被描述，仅仅需要一个由本地语言（C/C++、CORBA、Java）到IDL的“桥梁”。CORBA对象的互通信要以对象请求解析器（Object Request Broker）为中介，这种互通可以在多种流行通信协议之上（如TCP/IP或是IPX/SPX）实现。在TCP/IP上，来自于不同开发商的ORB用InternetInter-Orb协议（IIOP）进行通讯，这是CORBA2.0标准（最新的版本）的一部分。

目前，对于较为流行的编程语言（包括C＋＋，Smalltalk，Java和Ada95），已经有了许多第三方的ORB。随着其他语言的逐渐流行，CORBA开发商毫无疑问地要做出相应的ORB来支持它们。

5.3.1  CORBA简介

最初，OMG在1990年制订了对象管理体系（ObjectManagementArchitecture），即OMA，来描述应用程序如何实现互操作。作为其中的一部分，需要有一个标准规范应用程序片段即对象的互操作──这导致了CORBA的诞生。OMA定义了组成CORBA的四个主要部分。（图1.1）

图1.1  OMG的CORBA参考模型

l    Object Request Broker，ORB作为对象互通讯的软总线。

l    Object Services，定义加入ORB的系统级服务，如安全性、命名和事务处理。

l    Common Facilities定义应用程序级服务，如复合文档等。

l    Application Interface 定义现实世界的对象和应用，如飞机或银行帐户。

1．ObjectRequestBroker详述

OMA最重要的部分就是ORB。为了创建一个遵从CORBA规范的应用程序，ORB是CORBA四大部分中唯一必须提供的。许多ORB版本根本不带CORBAServices或是CORBAFacilities，你可以自制（或购买）商用对象。但是，没有ORB，CORBA应用程序绝对无法工作。（图5.2）

图5-.2 单个ORB的体系结构

CORB ORB最显见的功能，是对你的应用程序或是其它ORB的请求予以响应。在CORBA应用程序运行期间，你的ORB可能被请求做许多不同的事情，包括：

l    查找并调用远程计算机上的对象

l    负责不同编程语言之间的参数转换（如C＋＋到Java）

l    可超越本机界限的安全管理

l    为其它的ORB收集并发布本地对象的metadata

l    用下载的代码（stub）中描述的静态方法调用去击活远程对象中的方法

l    用动态方法调用击活远程对象

l    自动击活一个当前没有装入内存运行的对象。

l    将回调方法导引向其管理之下的本地对象

实现细节对软件开发者的透明性，是ORB的一个杰出的特性。用户只须在代码中提供相应的hooks，用于初始化ORB并向ORB登记该应用程序，就可以将该应用程序和大量分布式对象建立联系。

2．用IDL描述对象

为了保持CORBA的商业中立性和语言中立性，必须有一个中介，存在于象C＋＋CORBA服务器代码和JavaCORBA客户机这样的实体之间。这就是IDL。一个底层对象的若干相关方法和属性被IDL集入一个单一接口。一旦IDL接口定义完成，它可以以stub码或框架代码（skeletoncode）的形式编译成你选用的语言。在所有的ORB中都有IDL编译器。例如，VisigenicVisiBrokerforJavaORB中就含有Java/IDL编译器，而VisigenicVisiBrokerforC++ORB则提供了C＋＋/IDL编译器。

有一点值得注意的是IDL不同于其它的面向对象程序设计语言，我们不能用它指定它所定义的类或是方法的具体实现。因此，将它仅仅作为一种定义底层对象接口的语言要好得多。

就象在Java中将属性和方法封装到相关的类中一样，上述各项均包含在IDL的模块之中。在一个模块之中可以定义一个或多个接口。表一中的简单IDL模块名为TheModule，它含有一个称为TheInterface的基本接口。该接口仅有一个定义为整型的简单变量（即TheVariable）。

5.3.2  用Java做CORBA开发实例

为了创建一个分布式的Java小应用，并让它用CORBA访问服务器对象，我们利用一个流行的商用ORB，并用IDL定义对象接口。在示例小应用中，我们选用了Visigenic VisiBroker for Java。这种ORB已经经过Oracle、Netscape和Novell等公司的认证，并已被纳入NetscapeNavigator4.0。

注意：你可以在非NetscapeNavigator4.0的浏览器中运行这个小应用。由于它首先要从别处下载一些Java类文件，启动速度可能会稍慢一些。

我们将用一个简单的Java小应用调用一个使用CORBA的服务器对象。为简单起见，同样用Java书写服务器对象。该服务器对象用一个数组存储有关各种CORBAORB开发商及他们产品的信息。客户小应用将调用该对象并查询数组。一个更为完整的例子（仔细思考一下）是将ORB信息存储于关系数据库中，利用JDBC（或是别的数据库访问方法）获得相关信息。这种方法将用CORBA生成一个真正的三层应用程序。

1．最简单的IDL模块

Module TheModule

{

    interface TheInterface

    {

          long TheVariable;

    };

};

如果你用一个IDL到Java的编译器编译这个IDL模块（如Visigenic的idl2java），就会得到表1-2中的Java接口。

表1-2与TheModule相应的Java代码

package TheModule;

public interface TheInterface

{

public int TheVariable;

}

2．ORBQuery小应用

这个客户端的小应用含有标准的JavaGUI，并将调用一个远程CORBA对象。一旦该对象被调用，就可以使用其方法获得某一指定CORBA ORB的信息。在服务器端，为了获得特定ORB的如下信息：名称（Name）、销售商（Vendor）、操作系统（OperatingSystem）、语言（Languages）和URL，我们必须定义五个方法。因此，必须在IDL接口中定义这五种方法才能获取相应信息。表1-3定义了这个名为ORBInfo的接口：

表1-3:ORBInfoIDL界面

module ORBQuery

{

    interface ORBInfo

    {

          string GetName(in long index);

          string GetVendor(in long  index);

          string GetOS(in long index);

          string GetLanguages(in long  index);

          string GetURL(in long  index);

    };

};VisiBroker安装中含有一个IDL编译器──idl2java，你可以用它生成实现该接口必需的Java代码。软件安装完成之后，只要执行如下命令即可生成代码：

idl2javaORBInfo.idl

这步操作将创建一个名为ORBQuery的子目录（与ORBQueryJava包相对应）。在该目录内有8个文件：ORBInfo.java,ORBInfoHolder.java、ORBInfoHelper.java、_st_ORBInfo.java，_sk_ORBInfo.java、ORBInfoOperations.java、_tie_ORBInfo.java和_example_ORBInfo.java。你可能已经猜到，ORBInfo.java文件含有定义ORBInfo接口的Java源文件，但其它的Java类又怎样呢？

ORBInfoHolder.java文件内含有一个传递参数时使用的主类（holderclass），而ORBInfo-Helper类则定义了各种实用函数。_st_ORBInfo类定义了客户stub，_sk_ORBInfo定义了服务器框架类（skeletonclass）。

ORBInfoOperations和_tie_ORBInfo类用于实现一种捆绑机制，这是VisiBroker的一个特性，它使得实现类能够继承框架类之外的类。在示例中，我们不会直接使用这几个类。最后，_example_ORBInfo含有一个示例服务器对象，对它加以扩展就可创建一个服务器应用程序。

通过IDL编译器生成的这八个Java类，我们可以构建一个框架，由一个接口（interface）、一个stub、一个skeleton和几个帮助类，我们可以用Java创建自己的客户机/服务器模式的CORBA应用程序。

3．创建服务器应用程序

下面，我们需要创建一个向服务器ORB登记ORBInfo对象的服务器应用程序。这个新对象将扩充框架类（skeletonclass）并实现ORBInfo接口。因此，该服务器共需两个新类：一个用于定义服务器对象并实现ORBInfo接口，另一个向服务器ORB登记该对象。ORBQuery类包含的标准Java代码负责取回数组中的指定元素。Server类中含有CORBA特有的功能。

接下来的例子中，开始是初始化ORB。然后，用“ORBInfo”字符串向ORB登记类，客户机利用这个字符串检索一个对象。所有的操作完成后，调用boa.obj_is_ready()，通知ORB一切准备就绪。

表1-4是Server类，它向ORB登记了ORBInfo对象。

表1-4　服务器类

public class Server {

  public static void main(String[] args) {

    try {

      // Initialize the ORB.

      org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init();

      // Initialize the BOA.

      org.omg.CORBA.BOA boa = orb.BOA_init();

      // Create the ORBQuery.

      ORBQuery serverQuery = new ORBQuery("ORBInfo");

      // Export the newly create object.

      boa.obj_is_ready(serverQuery);

      System.out.println(serverQuery + " is ready.");

      // Wait for incoming requests

      boa.impl_is_ready();

    }

    catch(org.omg.CORBA.SystemException e) {

      System.err.println(e);

    }

  }

}

表1-5给出了ORBQuery类，它实现了接口及五个帮助方法。

表1-5:ORBQuery类

import java.util.*;

class ORBQuery extends ORBQuery._sk_ORBInfo {

  String[][] ORBVendors =

      {{"PowerBroker","Orbix","VisiBroker","ComponentBroker","

                    Solaris NEO"},

      {"Expersoft Corp.","Iona Technologies","Visigenic Software",

                    "IBM","Sun"},

      {"OLE and ActiveX Bridges; Windows95/NT;

                    Solaris; HP-UX; AIX; JDK 1.0.2",

      "Windows95/NT, MVS, OS/2, QNX, VxWorks, Solaris, HP-UX,

                    Irix, AIX, Digital UNIX, OLE Bridge",

      "Windows95/NT, Sun OS, Solaris, HP-UX, AIX, Irix",

      "Windows95/NT, Solaris, HP-UX, AIX, OS/390, OS/2, AS/400",

      "Solaris (Client & Server), Windows95/NT (client), JDK 1.0.2"},

      {"C++, Java", "Java, Smalltalk, Ada95, C++", "Java, C++",

                    "Java, C++", "Java, C++"},

      {"http://www.expersoft.com","http://www.iona.com",

                    "http://www.visigenic.com",

      "http://www.software.ibm.com/ad/cb","http://www.sun.com/solaris/neo/solaris_neo/index.html"}};

  ORBQuery(String name) {

    super(name);

  }

  public java.lang.String GetName(int index)

  {

    String Name;

    Name = ORBVendors[index][0];

    return Name;

  }

  public java.lang.String GetVendor(int index)

  {

    String Vendor;

    Vendor = ORBVendors[index][1];

    return Vendor;

  }

  public java.lang.String GetOS(int index)

  {

    String OS;

    OS = ORBVendors[index][2];

    return OS;

  }

  public java.lang.String GetLanguages(int index)

  {

    String Languages;

    Languages = ORBVendors[index][3];

    return Languages;

  }

  public java.lang.String GetURL(int index)

  {

    String URL;

    URL = ORBVendors[index][4];

    return URL;

  }

}

至此，我们已经写好了所有服务器方必需的代码，下一步的工作是创建客户小应用，初始化客户ORB，进而击活并调用刚刚生成的服务器对象。

4．创建CORBA小应用

正如服务器对象要向服务器ORB登记一样，客户端小应用或应用程序需要向客户机ORB登记。当要获得远程CORBA对象时，客户机采用了一种间接的方法，它通知客户机ORB其意图，由ORB负责ORB到ORB的通讯。这种请求方式由下面两行代码实现（在VisiBrokerforJava中）：

    //初始化ORB(使用applet)

    org.omg.CORBA.ORBorb=org.omg.CORBA.ORB.init(this);

//检索applet要调用的ORBInfo接口对象

ORBInfoQuery=ORBQuery.ORBInfoHelper.bind(orb,"ORBInfo");

执行了bind()方法调用之后，我们的ORBInfoQuery本地变量与服务器的ORBInfo对象绑在一起。这一操作完成之后，我们就可以调用帮助方法来实现客户端小应用。记住，尽管示例中我们完全使用了Java语言，但实际上，服务器对象也可以用其他语言来实现，如：COBOL、C＋＋、Ada和Smalltalk。

5．编写简单的CORBA服务的一般流程

（以Java2为例）：

l    编写所需要的接口IDL文件。

n    foo.idl

module foo{

    interface function{

        float square_root(in float number);

    }

}

l    用idltojava编译idl文件。

n    idltojava –fno-cpp foo.idl

l    用Javac编译所产生的类。

n    javac ~\*.java

l    生成实现类。

n    (functions.java) functinosImpl.java

l    生成实现服务器。

n    fooServer.java

l    生成客户机应用程序（或小程序）。

n    fooClient.java

l    编译实现服务器和客户机代码。

n    javac functionsImpl.java fooServer.java fooClient.java

l    启动命名服务应用程序tnameserv。

n    tnameserv –ORBInitialPort 1080

l    启动服务器（用命名服务注册）。

n    java fooServer –ORBInitialPort 1080

l    启动客户机。

n    java fooClient –ORBInitialPort 1080

6．结论

与单纯的Java小应用相比，创建CORBAJava应用稍显复杂。事实上，这同样比用RMI创建纯粹的Java分布式对象应用要难一些。但是，CORBA和Java的联合，所创建的应用程序功能更为强大，更具可扩充性，这是单纯用JDK开发所无法比拟的。另外，现实是企业界很少使用Java应用程序。CORBA是两全其美的选择，一方面它允许开发者利用已有的代码保护投资，另一方面又可以充分发挥Java编程语言的优势。

5.4  JSP与CORBA技术的结合使用

若想实现Web上分布式客户机/服务器模式的应用程序，可以选择多种不同的技术。但是，对于必须服务于大量用户的应用，我们认为应该首选CORBA，因为它具有可扩展性和业界广泛的支持。由于CORBA的强健性可以提高Java编程语言的性能，利用已有的基于标准的技术，Java开发者可以编写出功能强大的Web应用程序。

想要对已有的Web软件资源加以利用，或是编写真正功能强大的应用程序，使Internet/Intranet成为通信主干，所使用的技术必须具有以下特性：

·    支持现有的C、C＋＋和COBOL代码

·    支持Java，以建立具有可移值性、平台独立性的面向对象的应用程序

·    商业中立性，只有这样，应用程序才易于维护，且不会被时间淘汰。

·    可扩充性，能够支持大量用户

·    它应该被多种平台所支持，而不是固定于某种平台之上。

·    开发模型应该是面向对象的（因为OOP有许多固有优势）

·    端到端的安全性

·    广泛的业界支持

这种技术便是以CORBA为最佳。

Servlets是JAVA 2.0中新增的一个全新功能，它是运行在请求/面向请求服务器上的模块，比如一个Java-enabled web 服务器, 和类似这样的延伸场合. 例如, 一个servlet可以从一个HTML订单表中获取数据然后用一些商业上的算法来更新公司相应的订单数据库。

也就是说：servlet能够象CGI脚本一样扩展WEB服务器功能，但是servlet占用很少密集资源，有很多用CGI脚本编制的一些站点由于访问量剧增,性能迅速下降，这是CGI脚本一个缺点。同时由于servlet是用java编写的，因此是跨平台的。可以这样说，实际servlet是电子商务真正的开始。

跨平台的Java Servlet与跨语言的corba的强强联合，使得开发者可以编写出功能强大的Web应用程序。


About UBB with java has a bug.

此电子文档属C#乐园QQ群3256045整理。
About UBB with java has a bug.

上一页 目录 下一页

About UBB with java has a bug.

作者：iceant

email: iceant@21cn.com

日期：9/4/2001 6:43:55 PM

// the bug has been fixed

/*+-------------------------------

    $REReplace.java 2001-9-4

    $author pizer.chen

    $history:

        0.2 fixed a bug.

        0.1 ...

+-------------------------------*/

package com.wacos.util.ubb;

import java.io.*;

import java.util.*;

import org.apache.regexp.*;

/**

 *

 * @author  Pizer.chen -- iceant -- 陈鹏

 * @version 0.2

 */

public class REReplace

{

    /**

     * replace the inStr with pattern1 & pattern2

    **/

    public static String replace(String pattern1,String pattern2,String inStr){

        try {

            RE re = new RE(pattern1);

            RE re2 = new RE("\\$([0-9])");

            String tempPat2=pattern2;

            int point=0;

            while(re.match(inStr)){

                while(re2.match(pattern2)){

                    point = Integer.parseInt(re2.getParen(1));

                    pattern2=re2.subst(pattern2,re.getParen(point),RE.REPLACE_FIRSTONLY);

                }

                inStr = re.subst(inStr,pattern2,RE.REPLACE_FIRSTONLY);

                pattern2 = tempPat2;

            }

            return inStr;

        }

        catch (Exception e) {

            System.out.println(e);

            return e.toString();

        }

    }

}


Acme包中的以Post方式发送数据的例子。

此电子文档属C#乐园QQ群3256045整理。
Acme包中的以Post方式发送数据的例子。

上一页 目录 下一页

Acme包中的以Post方式发送数据的例子。

作者：sonymusic

email: sonymusic@china.com

日期：2001-5-11 13:55:13

package Acme;

import java.net.*;

import java.io.*;

import java.util.*;

import java.text.*;

/// A URLConnection that implements POST.

// <P>

// Some implementations of URLConnection, e.g. the one in Navigator 3.0,

// do not support POST.  This is a stripped-down version that does.

// <P>

// Note that it can't inherit from java.net.URLConnection because that

// class has no public constructors.  Not all the standard URLConnection

// methods are re-implemented here, just the ones necessary for posting.

// <P>

// This class is not needed in current browsers.

// <P>

// <A HREF="/resources/classes/Acme/PostURLConnection.java">Fetch the software.</A><BR>

// <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>

public class PostURLConnection

    {

    private URL url;

    private boolean doInput = false;

    private boolean doOutput = true;

    private boolean useCaches = false;

    private Vector reqHeaderNames = new Vector();

    private Vector reqHeaderValues = new Vector();

    private Vector resHeaderNames = null;

    private Vector resHeaderValues = null;

    private Socket socket;

    private OutputStream out;

    private InputStream in;

    /// Constructs a POST URL connection to the specified URL.

    // @param url the specified URL

    public PostURLConnection( URL url )

    {

    this.url = url;

    }

    private boolean connected = false;

    public void connect() throws IOException

    {

    if ( connected )

        return;

    if ( ! useCaches )

        setRequestProperty( "Pragma", "no-cache" );

    String protocol = url.getProtocol();

    if ( ! protocol.equals( "http" ) )

        throw new UnknownServiceException( "unknown protocol" );

    String host = url.getHost();

    int port = url.getPort();

    if ( port == -1 )

        port = 80;

    String file = url.getFile();

    socket = new Socket( host, port );

    out = socket.getOutputStream();

    PrintStream pout = new PrintStream( out );

    String method;

    if ( doOutput )

        method = "POST";

    else

        method = "GET";

    pout.println( method + " " + file + " HTTP/1.0" );

    for ( int i = 0; i < reqHeaderNames.size(); ++i )

        {

        String name = (String) reqHeaderNames.elementAt( i );

        String value = (String) reqHeaderValues.elementAt( i );

        pout.println( name + ": " + value );

        }

    pout.println( "" );

    pout.flush();

    connected = true;

    }

    private boolean inputStarted = false;

    private void startInput() throws IOException

    {

    connect();

    if ( inputStarted )

        return;

    in = socket.getInputStream();

    resHeaderNames = new Vector();

    resHeaderValues = new Vector();

    DataInputStream din = new DataInputStream( in );

    String line;

    // Read and ignore the status line.

    line = din.readLine();

    // Read and save the header lines.

    while ( true )

        {

        line = din.readLine();

        if ( line == null || line.length() == 0 )

        break;

        int colonBlank = line.indexOf( ": " );

        if ( colonBlank != -1 )

        {

        String name = line.substring( 0, colonBlank );

        String value = line.substring( colonBlank + 2 );

        resHeaderNames.addElement( name.toLowerCase() );

        resHeaderValues.addElement( value );

        }

        }

    inputStarted = true;

    }

    public void close() throws IOException

    {

    if ( ! connected )

        return;

    out.close();

    if ( inputStarted )

        in.close();

    socket.close();

    }

    /// Gets the URL for this connection.

    public URL getURL()

    {

    return url;

    }

    // Gets the content length.  Returns -1 if not known.

    public int getContentLength() throws IOException

        {

        return getHeaderFieldInt( "content-length", -1 );

    }

    /// Gets the content type.  Returns null if not known.

    public String getContentType() throws IOException

    {

    return getHeaderField( "content-type" );

    }

    /// Gets a header field by name.  Returns null if not known.

    // @param name the name of the header field

    public String getHeaderField( String name ) throws IOException

    {

    if ( resHeaderNames == null )

        startInput();

    int i = resHeaderNames.indexOf( name.toLowerCase() );

    if ( i == -1 )

        return null;

    return (String) resHeaderValues.elementAt( i );

    }

    /// Gets a header field by name.  Returns null if not known.

    // The field is parsed as an integer.

    // @param name the name of the header field

    // @param def the value to return if the field is missing or malformed.

    public int getHeaderFieldInt( String name, int def ) throws IOException

    {

    try

        {

        return Integer.parseInt( getHeaderField( name ) );

        }

    catch ( NumberFormatException t )

        {

        return def;

        }

    }

    /// Gets a header field by name.  Returns null if not known.

    // The field is parsed as a date.

    // @param name the name of the header field

    // @param def the value to return if the field is missing or malformed.

    public long getHeaderFieldDate( String name, long def ) throws IOException

    {

    try

        {

        return DateFormat.getDateInstance().parse( getHeaderField( name ) ).getTime();

        }

    catch ( ParseException e )

        {

        throw new IOException( e.toString() );

        }

    }

    /// Call this routine to get an InputStream that reads from the object.

    // @exception UnknownServiceException If the protocol does not support input.

    public InputStream getInputStream() throws IOException

    {

    if ( ! doInput )

        throw new UnknownServiceException(

        "connection doesn't support input" );

    startInput();

    return in;

    }

    /// Call this routine to get an OutputStream that writes to the object.

    // @exception UnknownServiceException If the protocol does not support output.

    public OutputStream getOutputStream() throws IOException

    {

    if ( ! doOutput )

        throw new UnknownServiceException(

        "connection doesn't support output" );

    connect();

    return out;

    }

    /// Returns the String representation of the URL connection.

    public String toString()

    {

    return this.getClass().getName() + ":" + url;

    }

    /// A URL connection can be used for input and/or output.  Set the DoInput

    // flag to true if you intend to use the URL connection for input,

    // false if not.  The default for PostURLConnections is false.

    public void setDoInput( boolean doInput )

    {

    if ( connected )

        throw new IllegalAccessError( "already connected" );

    this.doInput = doInput;

    }

    public boolean getDoInput()

    {

    return doInput;

    }

    /// A URL connection can be used for input and/or output.  Set the DoOutput

    // flag to true if you intend to use the URL connection for output,

    // false if not.  The default for PostURLConnections is true.

    public void setDoOutput( boolean doOutput )

    {

    if ( connected )

        throw new IllegalAccessError( "already connected" );

    this.doOutput = doOutput;

    }

    public boolean getDoOutput()

    {

    return doOutput;

    }

    /// Some protocols do caching of documents.  Occasionally, it is important

    // to be able to "tunnel through" and ignore the caches (e.g. the "reload"

    // button in a browser).  If the UseCaches flag on a connection is true,

    // the connection is allowed to use whatever caches it can.  If false,

    // caches are to be ignored.  The default for PostURLConnections is false.

    public void setUseCaches( boolean useCaches )

    {

    if ( connected )

        throw new IllegalAccessError( "already connected" );

    this.useCaches = useCaches;

    }

    public boolean getUseCaches()

    {

    return useCaches;

    }

    // Sets/gets a general request property.

    // @param name The keyword by which the request is known (eg "accept")

    // @param value The value associated with it.

    public void setRequestProperty( String name, String value )

    {

    if ( connected )

        throw new IllegalAccessError("already connected");

    reqHeaderNames.addElement( name );

    reqHeaderValues.addElement( value );

    }

    public String getRequestProperty( String name )

    {

    if ( connected )

        throw new IllegalAccessError( "already connected" );

    int i = reqHeaderNames.indexOf( name );

    if ( i == -1 )

        return null;

    return (String) reqHeaderValues.elementAt( i );

    }

    }


allaire 发布jrun 3.0 rc1版

此电子文档属C#乐园QQ群3256045整理。
allaire 发布jrun 3.0 rc1版

上一页 目录 下一页

allaire 发布jrun 3.0 rc1版

作者：jjx

日期：00-4-29 10:17:13

allaire 发布jrun 3.0 rc1版

jjx整理自allaire邮件列表，主页:[url]http://202.106.124.52/jjx[/url]

allaire发布了jrun 3.0,该版本新增以下特点

1、在jrun management console中分发ejbs

2、在jrun management console中允许你为jdbc 数据源设置数据库连接池(此功能类似于odbc连接池)。

3、为j2ee应用分发提供支持enterprise archive(.ear)文件

4、当前有22定制tags可用在jsp页面中

该版本当前可用在HP-UX, Linux, Windows,Solaris,SGI IRIX, IBM AIX, Digital Unix个平台之上

下载地址

http://beta.allaire.com

allaire还发布了jrun studio beta 1

该工具提供一可视化开发工具供开发jsp/servlet之用

以下是特点简介

- 通过jrun rds访问远程和本地服务器文件

- Server Development mapping (就是在开发环境中将本地文件和url对照，如c:\hellp.jsp和http://localhost/hello.jsp

- 支持数据源浏览和建立sql 语句

-JSP QuickBar 支持全部  JSP 1.1 标记

-jsp 标记编辑和增强帮助

- jsp彩色代码支持

- jsp标记检查

呵呵，我还没有下载过，但好像这些功能是homesite都具备或是增强吧

下载地址：

http://beta.allaire.com

最后是Cluster Cats for JRun rc 1,为jrun server提供集群支持

嘿嘿，coldfusion的开发者，现在了解allaire对coldfusion的态度了吧，还不放弃它，学习jsp/asp开发。不过java语言好像没有asp/coldfusion来的简单了吧


Apache Tomcat 重要信息暴露漏洞

作者：asp3000

日期：2000-9-27 10:16:40

bugtraq id 1532

class Design Error

cve GENERIC-MAP-NOMATCH

remote Yes

local No

published July 20, 2000

updated August 02, 2000

vulnerable Apache Group Tomcat 3.1

- Sun Solaris 8.0

- Sun Solaris 7.0

- SGI IRIX 6.5

- SGI IRIX 6.4

- RedHat Linux 6.2 i386

- RedHat Linux 6.1 i386

- NetBSD NetBSD 1.4.2 x86

- NetBSD NetBSD 1.4.1 x86

- MandrakeSoft Linux Mandrake 7.1

- MandrakeSoft Linux Mandrake 7.0

- FreeBSD FreeBSD 5.0

- FreeBSD FreeBSD 4.0

- Digital UNIX 4.0

- Debian Linux 2.2

- Debian Linux 2.1

- Connectiva Linux 5.1

- Caldera OpenLinux 2.4

- BSDI BSD/OS 4.0

Apache Group Tomcat 3.0

A vulnerability exists in the snoop servlet portion of the Tomcat package, version 3.1, from the Apache Software Foundation. Upon hitting an nonexistent file with the .snp extension, too much information is presented by the server as part of the error message. This information may be useful to a would be attacker in conducting further attacks. This information includes full paths, OS information, and other information that may be sensitive.

http://narco.guerrilla.sucks.co:8080/examples/jsp/snp/anything.snp

====

Snoop Servlet

Servlet init parameters:

Context init parameters:

Context attributes:

javax.servlet.context.tempdir =

/appsrv2/jakarta-tomcat/work/localhost_8080%2Fexamples

sun.servlet.workdir =

/appsrv2/jakarta-tomcat/work/localhost_8080%2Fexamples

Request attributes:

Servlet Name: snoop

Protocol: HTTP/1.0

Scheme: http

Server Name: narco.goverment.sucks.co

Server Port: 8080

Server Info: Tomcat Web Server/3.1 (JSP 1.1; Servlet 2.2; Java 1.1.8; AIX

4.2 POWER_RS; java.vendor=IBM Corporation)

Remote Addr: xxx.xxx.xxx.xxx

Remote Host: xxx.xxx.xxx.xxx

Character Encoding: null

Content Length: -1

Content Type: null

Locale: en

Default Response Buffer: 8192

Parameter names in this request:

Headers in this request:

Host: narco.goverment.sucks.co:8080

Accept-Encoding: gzip

Cookie: JSESSIONID=To1212mC7833304641226407At

Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, image/png,

*/*

Connection: Keep-Alive

Accept-Charset: iso-8859-1,*,utf-8

User-Agent: Mozilla/4.51 [en] (Winsucks; I)

Accept-Language: en

Cookies in this request:

JSESSIONID = To1212mC7833304641226407At

Request Is Secure: false

Auth Type: null

HTTP Method: GET

Remote User: null

Request URI: /examples/jsp/snp/anything.snp

Context Path: /examples

Servlet Path: /jsp/snp/anything.snp

Path Info: null

Path Trans: null

Query String: null

Requested Session Id: To1212mC7833304641226407At

Current Session Id: To1212mC7833304641226407At

Session Created Time: 964047263477

Session Last Accessed Time: 964047528749

Session Max Inactive Interval Seconds: 1800

Session values:

numguess = num.NumberGuessBean@6bfa9a1


Apache+Servlet+jsp

作者：大傻瓜

email: dalinqin@yeah.net

日期：2000-07-11 15:16:08

本文只讨论如何在Windows NT 4.0上安装Apache+Servlet+jsp。

本文中的配置情况如下：

    Windows NT 4.0

    jdk1_2_2-001-win.exe

    apache_1_3_12_win32.exe

    ApacheModuleJServ.dll

    tomcat.zip(3.0)

一、软件下载

    JDK

        http://java.sun.com/products/jdk/1.2/

    Apache Server

        http://www.apache.org/dist/binaries/win32/apache_1_3_12_win32.exe

    Apache JServ

        http://jakarta.apache.org/builds/tomcat/release/v3.0/win32/ApacheModuleJServ.dll

    Tomcat

        http://jakarta.apache.org/builds/tomcat/release/v3.0/tomcat.zip

二、软件安装

    （一）JDK

        1、双击jdk1_2_2-001-win.exe文件进行安装，使用缺省配置进行安装，JDK的缺省安装目录为

           C:\jdk1.2.2，JRE的缺省安装目录为C:\Program Files\JavaSoft\JRE\1.2；

        2、重启计算机；

        3、更新下列环境变量：把C:\jdk1.2.2\bin目录追加到PATH中；把

           .;C:\jdk1.2.2\lib\tools.jar;C:\jdk1.2.2\lib\dt.jar加入到CLASSPATH中。更新

           方法：控制面板－＞系统－＞环境－＞系统变量；

        4、测试applet：

             1)打开command窗口；

             2)切换到C:\jdk1.2.2\demo\applets\TicTacToe目录；

             3)运行appletviewer example1.htm；

             4)一切正常;

        5、测试application：

               1)在我的D:\HZQ\Java目录下创建一个test.java文件，内容如下：

                   public class test

                   {

                    public static void main(String arc[]){

                    System.out.println("JDK安装成功了，朋友！");

                    }

                   };

             2)打开command窗口；

             3)切换到D:\HZQ\Java目录；

             4)运行javac test.java进行编译；

             5)运行java test运行这个程序；

             6)一切正常。

    （二）Apache Server

        1、双击apache_1_3_12_win32.exe文件进行安装，使用缺省配置进行安装，缺省安装目录为

           C:\Program Files\Apache Group\Apache，我用$APACHE_ROOT来代替安装目的地；

        2、修改$APACHE_ROOT\conf\httpd.conf：

             1)PORT

                  设置Apache Web Server运行时使用的端口号，由于我这里的IIS也在运行，IIS使

                  用了80端口号，于是我把它改成Port 8080，以后在浏览器上输入

                  http://localhost:8080/才能访问到Apache Web Server服务器，输入

                  http://localhost/时能访问到IIS，这样两个Web Server都可以用了哦，我用

$APACHE_PORT标识；

                2)DocumentRoot

                  设置文档根目录，当您在浏览器上输入http://localhost:$APACHE_PORT/时，服

                  务器会从文档根目录读取数据，由于我的程序都放在D:\HZQ下，所以我把

                  DocumentRoot设置为D:/HZQ；

        3、Apache安装完后在"开始－＞程序"菜单组中多了Apache Web Server菜单组，运行其中的

           Install Apache as a service，这样的话在"开始－＞设置－＞控制面板－＞服务"中就

           多了一个名为Apache的服务，您可以用它来启动或停止Apache服务；

        4、打开"开始－＞设置－＞控制面板－＞服务"，选中Apache这个服务，按"开始"启动Apache

           服务；

        5、我在浏览器上输入http://localhost:$APACHE_PORT/，就看到了D:\HZQ下的所有文件，

           这时表明Apache服务已经安装成功。

    （三）Apache JServ

        1、把ApacheModuleJServ.dll文件拷贝到$APACHE_ROOT\modules目录下；

        2、修改$APACHE_ROOT\conf\httpd.conf：

               在该文件中加上LoadModule jserv_module modules/ApacheModuleJServ.dll以

               启动Apache JServ；

        3、重新启动Apache服务。

    （四）Tomcat

        1、用WinZIP等解压缩软件把tomcat.zip解压缩到一个目录下，我把它解压缩到C:\，它会自动

           创建tomcat子目录，这样在C盘就多了一个目录C:/tomcat，我用$TOMCAT标识；

        2、打开$APACHE_ROOT\conf\httpd.conf文件，在该文件最后加上类似这样一句话：

               Include $TOMCAT/etc/tomcat.conf

           在我这里为Include C:/tomcat/etc/tomcat.conf；

        3、修改Tomcat运行的端口号，注意Tomcat自己有一个独立的HTTP服务器，它必须使用一个还未

           被使用的端口号，我这里的8081还未被占用，我用$TOMCAT_PORT标识，分配给Tomcat：

               1)打开$TOMCAT/server.xml；

               2)修改ContextManager：

        4、双击$TOMCAT目录下的startup.bat来启动Tomcat；

        5、在浏览器上输入http://localhost:$TOMCAT_PORT/Version 3.0这一页

           就表示Tomcat安装成功了；

        6、在浏览器上输入http://localhost:$APACHE_PORT/examples/servlets/，能看到

           Servlet Examples with Code这一页就表示Apache+Servlet也成功了；

        7、在浏览器上输入http://localhost:$APACHE_PORT/examples/jsp/，能看到jsp Samples

           这一页就表示Apache+jsp也成功了。

附：

    JDK Download

        http://java.sun.com/products/jdk/1.2/

    Apache Server Download

        http://www.apache.org/dist/binaries/win32/apache_1_3_12_win32.exe

    Apache JServ Download

        http://jakarta.apache.org/builds/tomcat/release/v3.0/win32/ApacheModuleJServ.dll

    Tomcat Download

        http://jakarta.apache.org/builds/tomcat/release/v3.0/tomcat.zip

    Java(TM) 2 SDK 1.2.2-001 Installation Instructions

        http://java.sun.com/products/jdk/1.2/install-windows.html

    Tomcat FAQ

        http://jakarta.apache.org/cvsweb/index.cgi/jakarta-tomcat/src/doc/faq

    Tomcat+Apache-HOWTO

        http://jakarta.apache.org/cvsweb/index.cgi/jakarta-tomcat/src/doc/Tomcat%2bApache-HOWT


APACHE+TOMCAT+ORACLE安装指南(少爷原创)，因为很多有这方面的问题所以就贴出来

此电子文档属C#乐园QQ群3256045整理。
APACHE+TOMCAT+ORACLE安装指南(少爷原创)，因为很多有这方面的问题所以就贴出来

上一页 目录 下一页

APACHE+TOMCAT+ORACLE安装指南(少爷原创)，因为很多有这方面的问题所以就贴出来

作者：令少爷

email: dj12@8848.net

日期：7/10/2001 9:54:02 AM

一、    所需软件

以WINDOWS 98为例，其它环境类似。

a)    JDK1.2.2；

JAVA开发工具。

b)    apache_1_3_12_win32.exe;

WEB 服务器。

c)    jakarta-tomcat 3.1;

支持JSP、SERVLET。

d)    ApacheJServ-1.1.1.exe；

用来连接apache和TOMCAT。

e)    jsdk20-win32.exe；

支持SERVLET。

f)    ORACLE8i的服务器版和ORACLE8.1.7版的JDBC驱动classes12.zip；

数据库软件。

二、    具体步骤

a)    将JDK装在c:\jdk1.2.2目录下；

b)    TOMCAT的安装与配置；

1．    将TOMCAT的压缩文件解压到c:\根目录下，在C盘下就会出现一个jakatar-tomcat的目录，将其改为tomcat。

2．    在c:\tomcat\webapps目录下创建hnzq目录，在hnzq目录下创建jsp和classes目录。

3．    编缉c:\tomcat\bin目录下的server.xml文件，找到ContextManager这个tag,在<ContextManager>的结束标记前面加上以下标记：

<Context path=”/hnzq” docBase=”webapps/hnzq” debug=”0” reloadable=”true”>

</Context>

4．    编缉C盘根目录下的autoexec.bat，设置以下环境变量：

SET JAVA_HOME=C：\JDK1.2.2；

SET TOMCAT_HOME=C：\TOMCAT;

            修改PATH环境变量：

                SET PATH=%PATH%;JAVA_HOME\BIN;JAVA_HOME

把以下路径加到classpath中：

JAVA_HOME\LIB\TOOLS.JAR;

JAVA_HOME\LIB\DT.JAR;

TOMCAT_HOME\WEBAPPS\HNZQ\CLASSES;

5．    将ORACLE8.1.7的JDBC驱动文件及所有BEAN的类和包按原有路径拷贝到c:\tomcat\webapps\hnzq\classes\目录下。

6．    将所有的JSP文件按原有路径拷贝到c:\tomcat\webapps\hnzq\jsp\目录下。

7．    执行c:\tomcat\bin目录下的startup.bat文件，执行完后，startup.bat会在一个DOS窗口启动TOMCAT，如果在执行时出现Out of environment space的提示，将DOS窗口的缓冲区大小设为2816或更高就OK了。这时在浏览器的地址栏输入：http://localhost:8080/ 然后回车，就可看到一只可爱的小猫，说明TOMCAT已经启动成功了。

c)    apache的安装；

1．    将Apache Web Server 安装在c:\apache目录下。

2．    将jsdk20-win32.exe 安装在c:\jsdk2.0目录下。

3．    将ApacheJServ-1.1.1.exe 安装在c:\ Apache JServ 1.1.1目录下，安装过程中需要指定JVM所在的目录，即JAVA_HOME\BIN目录；还需要指定JSDK所在目录c:\jsdk2.0。

d)    连接apache和tomcat;

1．    用文本编缉器打开c:\apache\conf\httpd.conf文件，在文件的最后加上如下指令：

   Include “Tomcat_home\conf\tomcat-apache.conf”

e)    运行；

1．    先执行c:\tomcat\bin\startup.bat，等待出现另一个DOS窗口后TOMCAT就启动了。

2．    新开一个DOS窗口，进入c:\apache\目录，执行apache.exe文件，此时出现如下出错信息：

  apj*:can’t be specified more than once per host

其中*号代表一些字符串，如ApJServLogLevel，用文本编缉器打开c:\ Apache JServ 1.1.1\jserv.conf文件，将apj*的相关行注释掉就可以了，直到没有出错信息为止。

3．    经过以上步骤后，Apache+Tomcat就可以正常工作了。此时在浏览器的地址栏输入：http://localhost/hnzq/jsp/gssy/index.htm/就可以看到整个网页了。

三、    注意事项及其它

a)    ORACLE的JDBC驱动只能是8.1.7或更高版本的驱动才能支持JDBC2.0。

b)    因为Apache JServ 1.1.1不能认识JSDK2.1或其它版本，所以只能用JSDK2.0。

c)    如需以上所有软件或其它未尽事宜请联系：

j2ee@8848.net

                                                    令少爷


applet 代码2

作者：blackwhites

email: blackwhites@sohu.com

日期：2000-10-11 15:02:01

Limit TextField input to numeric valueimport java.awt.*;

 import java.awt.event.*;

 public class TextFieldWithLimit extends TextField

    implements KeyListener {

    private int  maxLength;

    public TextFieldWithLimit

           (String initialStr,int col,int maxLength) {

      super(initialStr,col);

      this.maxLength = maxLength;

      addKeyListener(this);

      }

    public TextFieldWithLimit (int col,int maxLength) {

      this("",col,maxLength);

      }

    public void keyTyped(KeyEvent e) {

      char c = e.getKeyChar();

      int len = getText().length();

      if (len < maxLength) {

         return;

         }

      else {

         if((c==KeyEvent.VK_BACK_SPACE)||

            (c==KeyEvent.VK_DELETE) ||

            (c==KeyEvent.VK_ENTER)||

            (c==KeyEvent.VK_TAB)||

             e.isActionKey())

            return;

          else {

            e.consume();

           }

          }

      }

    public void keyPressed(KeyEvent e) { }

    public void keyReleased(KeyEvent e) { }

    }


ASP+和JSP之间的抉择（转载）

作者：罗亭

email: tingluo@263.net

日期：2000-12-7 17:54:24

/*

转自jsp中国论坛

另：Blueski是谁？特此致谢

*/

///***********************************以下为内容

{

Blueski为您编译。

原文名：JSP VS ASP Part 2: The Future According to ASP+

英文作者没有记下，非常抱歉

}

由于我同时是ASP和JSP的程序员，所以正开始写一系列的文章来比较两者的差别，

当我写到一半时，ASP+被宣布了，所以我在重新开始审视这个问题。

我正在做一个JSP项目和一个ASP项目，后者十分有趣，因为它开始是采用ASP3.0，

但当项目结束后我会将转换为ASP+。那时我将可以对ASP+有更近的考察，并提供更好的

ASP+和JSP的比较。本文提供了我对ASP+，以及它对于象我这样的一个普通开发人员会产生的影响做一个初步观察。

通过最初步的比较，我发现：

1) 面向对象性:

ASP+将C#作为一种面向对象语言，在很多方面来看，C#将成为微软的与Java相似的语言。

C#另一个有趣的地方是所有对象都自动变成为COM对象。如果C#能取得很大的市场份额，那么它将给ASP+带来类似于Java的功能，并且具备更快的性能，因为它可以和Windows环境紧密集成。

C# 是ASP+开发中一个最重要的功能，微软会将C#发展成为Java的强劲对手。这也是他们.Net框架的一个重要组成部分。我认为C#是微软在编程市场上击败对手的主要工具。我期待着微软能在这个产品后面倾注全力，这样，C#可以成为许多程序员的又一种选择。C#产生的结果是将进一步加固微软和Sun产品的战线。这对用户是有利的，他们可以选择两者之一来开发新的应用。

2) 数据库连接：

ASP另一个亮点是它使用ADO对象，ODBC, OLE-DB和事务处理管理器。因此ASP Web数据库应用开发特别简单。ASP+发展了更多的功能，因为有了ADO+！ADO+带来了更强大更快速的功能。JSP和JDBC目前在易用性和性能上同ASP/ADO相比已有些落后，当新版本ASP+/ADO+出现后这样的差别会更明显。我个人希望SUN应尽快能花大力气来追赶ASP+/ADO+的组合。

3)大型站点应用：

ASP+将对大型站点(web farms)有更好的支持。事实上，微软在这方面付出了巨大的努力。 ASP+可以让你考虑到多服务器(multiple servers)的场合，当你需要更强大的功能时，仅仅只需要增加一台服务器。整个.Net框架已经充分地提供了这个方法。ASP+提供了外部会话状态(external session state)来提供内置式web farm的支持。另外，由于请求的各组件相互间经过了充分的优化，所以速度很快。

于是ASP+现在可以在大型项目方面与JSP一样具有等同的能力。 而ASP+还有价格方面的优势，因为所有的组件将是服务器操作系统的一部分。对于JSP,你需要购买昂贵的应用服务器群来达到同样的目的。

4) ASP+还提供更多的其它方面的新特性，例如：

*内置的对象缓存和页面结果缓存。

*内置的XML支持，可用于XML数据集的简单处理。

*服务器控制提供了更充分的交互式控制。

ASP+确实对ASP进行了较大的发展。

在我审视完ASP+后，我的初步结论是：我未来项目主要还是要采用基于Java/JSP的技术。这有以下三个理由：

1) 要真正发挥ASP+潜力，你要使用C#或vb.net。这两种语言将成为ASP+标准的核心的脚本语言。 这对ASP的未来发展很有好处。我向来不喜欢用当前的Visual Basic来制作com对象。新的ASP+核心脚本语言更能发挥ASP+的价值。

显然，所有这些ASP+的新进展促使我对今后语言选择做一个决定，它将占据我未来的无数时间和努力。

当Java发展起来时我学习了Java，那时我预感到这将是围绕我未来程序生涯的语言。于是，JSP成为我在web开发中的第一选择。当我目前的ASP项目完成后，我将重新考虑C#是否符合我的编程工具体系。

现在我已经是一个JSP fan，除非微软能够将我整个扭转过去，我猜想我正面临ASP+的巨大挑战。

2) ASP+依然完全锁定在微软的操作系统中。JSP比ASP+拥有更大的范围，它在别的服务器配置中也有很好的伸缩性。虽然Windows 2000 的优越性日益显露，这对开发人员不构成问题。

3) 我并不能确信是否我能认同ASP+结构的各种方面，这需要更深入的分析，所以，我很犹豫。如果我使用了1-2个月，也许会改变看法。总的来说，这需要在一个很高的层次来看待这个beta产品。

作为一个ASP开发人员，ASP+让我停下来反思。未来8个月的ASP新项目将变得很困难。为什么？因为 ASP+和C# 带来了一个难题，它们要到2001年才正式提供。而很多性能方面的要求现在就需要确定。由于ASP+只是一个beta软件，所以现在我的第一个选择反而很简单了。现在的项目就采用ASP3.0。我认为各种beta版的语言都不太可信。

但我不能忽视ASP+,因为它很快就要推出，并代表了ASP的未来。ASP项目的管理者还需要考虑到未来的可平滑移植性及付出的时间代价等。另一个结果则是，这似的在近期使用PHP和JSP来代替ASP变得更可靠一些。

总的来说，

1) 如果使用ASP+，你将花费额外的时间学一些新的工具。

2) 新的工具有待于版本化，所以为什么不先关注于目前存在的东西？

3) ASP+将来会拥有的特性JSP已经拥有。

4) ASP+将你锁定到纯Microsoft的体系中。（我读了一个关于Microsoft将.Net移植到

Linux的故事，但我觉得不太可信。）

最后，我预测会有很多人加入到PHP和JSP的行列。可是，仍然会有一部分人会受到

ASP+给业界带来的激动所感染。(我从没看到一个beta编程产品会带来如此的震撼。)

以下是一篇相关的讨论.net的文章。

ASP+以自由软件作为开始，现在已经渐渐成为Microsoft的.Net框架的基石之一。

Sun应该从中得到教训。特别注意的是，你需要许可证才能升级到ASP+，因为它和太多的产品集成在一起了。.Net框架太大了。在假定某个产品可以成为真正的产品之前，我通常等待它的第一个service pack。所以，即使ASP+已经在2001第一季度被版本化，我仍然要等到2001第二季度。

当然，因为ASP+ 已经产生了如此的激动人心，一旦推出，很多商店都会马上提供ASP+。

事实上我早已经使用beta版ASP+! 它使用 .aspx 后缀，这使我我感觉Microsoft会在近期推出第一个ASP+的service pack。

下一年我们将看到在这个巨大的市场上Sun和Microsoft如何展开决斗，而程序员门又将做什么样的选择呢？


bea weblgic中介绍的一个jsp调用ejb的好例子，相当详细。

作者：diego lynn

日期：2001-3-15 15:05:59

<!-- Copyright (c) 1999-2000 by BEA Systems, Inc. All Rights Reserved.-->

<!-- Here we use a Java string ("PAGETITLE") to set

     the same phrase as the title and as a head.-->

<html>

<head>

<title>

<%= pagetitle %>

</title>

</head>

<body bgcolor=#FFFFFF>

<font face="Helvetica">

<h2>

<font color=#DB1260>

<%= pagetitle %>

</font>

</h2>

<!-- Using the "import" feature -->

<%@ page import="

    javax.naming.*,

    javax.ejb.*,

    java.rmi.RemoteException,

    java.rmi.Remote,

    java.util.*,

    examples.ejb.basic.beanManaged.*

"%>

<!-- Here, we declare a class method -->

<%!

  String pagetitle = "JSP example using EJBean-managed persistence";

  String url       = "t3://localhost:7001";

  String accountId = "10020";

  // Declaring a Java class

  public Context getInitialContext() throws Exception {

    Properties p = new Properties();

    p.put(Context.INITIAL_CONTEXT_FACTORY,

          "weblogic.jndi.WLInitialContextFactory");

    p.put(Context.PROVIDER_URL, url);

    return new InitialContext(p);

  }

  String getStackTraceAsString(Exception e)

  {

    // Dump the stack trace to a buffered stream, then send it's contents

    // to the JSPWriter.

    ByteArrayOutputStream ostr = new ByteArrayOutputStream();

    e.printStackTrace(new PrintWriter(ostr));

    return(ostr.toString());

  }

%>

<!-- Note that the following Java code will be inserted into the body

of the servlet -->

<%

  double amount  = 100;

  double balance = 3000;

  AccountPK accountKey = new AccountPK();

  accountKey.accountId = accountId;

  try {

    // Contact the AccountBean container (the "AccountHome") through JNDI.

    Context ctx = getInitialContext();

    AccountHome home = (AccountHome) ctx.lookup("beanManaged.AccountHome");

%>

<p>

<b>Looking up account <%= accountId %> ...</b>

<%

    Account ac = null;

    try {

      ac = (Account) home.findByPrimaryKey(accountKey);

    }

    catch (Exception ee) {

        out.println("<br>Did not find "+ accountId);

    }

    if (ac == null) {

      out.print("<br>Account " + accountId +

                " being created; opening balance is $" + balance);

      ac = home.create(accountId, balance);

    }

    else {

      out.print("<br>Account " + accountId +

                " found; balance is $" + ac.balance());

    }

%>

<h2>Part A:</h2>

Deposit and attempt to withdraw more than the current

account balance. An application-specific exception should be thrown:-

<p>

Current Balance: $ <%= ac.balance() %> <br>

Depositing: $ <%= amount %>

<p>

<%

    // Deposit the amount into the account

    balance = ac.deposit(amount);

%>

New balance: $ <%= balance %>

<p>

<h2>PartB:</h2>

Withdraw an amount greater than

current balance. Expecting an exception...

<p>

<%

    amount = balance + 10;

    try {

      balance = ac.withdraw(amount);

      out.print("Error: An exception should have been thrown.");

    }

    catch (ProcessingErrorException pe) {

      out.print("Received expected Processing Error:" + pe);

      out.print("<pre>" + getStackTraceAsString(pe) + "</pre>");

    }

%>

<h2>Part C</h2>

Create some new accounts, with different initial balances.

Find all the accounts with a balance greater than a specific value.

When finished, the new accounts are removed.

<p>

<%

    int numAccounts = 5;

    long now = System.currentTimeMillis();

    Vector v = new Vector();

    for (int i = 0; i < numAccounts; i++) {

      String id = "" + now + i;     // create unique account  id

      balance = i*100;              // initial balance

      v.addElement(home.create(id, balance));

%>

<br>Created account: <%= id %> with  balance: $ <%= balance %>

<%

    } // end of creating accounts for loop

    if (v.size() == numAccounts) {

      out.print("<p>Success: " + numAccounts + " accounts successfully created");

    }

    else {

      out.print("<p>Error: Only " + v.size() +

                " accounts were created successfully");

    }

%>

<%

    double balanceGreaterThan = 700;

%>

<p>Querying for accounts with a balance greater than <%= balanceGreaterThan %>

<table><tr><th Account ID><th Balance></tr>

<%

    Enumeration e = home.findBigAccounts(balanceGreaterThan);

    if (e != null) {

      while (e.hasMoreElements()) {

        Account bigAccount= (Account) e.nextElement();

%>

<tr>

    <td><%= bigAccount.getPrimaryKey() %></td>

    <td><%= bigAccount.balance() %></td>

</tr>

<%

        // Thread.sleep(1000);

      }

    }

%>

</table>

<p>Now Removing the accounts we just created...

<%

    for (int i = 0; i < numAccounts; i++) {

      String id = String.valueOf(now) + String.valueOf(i);

      ((Account)(v.elementAt(i))).remove();

      out.print("<br>Removed account: " +id);

    }

  // Catch exceptions

  }

  catch (ProcessingErrorException pe) {

    out.print("<p>Unexpected Processing Error: " + pe +

              "<pre>"+getStackTraceAsString(pe)+"</pre>");

  }

  catch (Exception e) {

    out.print("<p>:::::::::::::: Unexpected Error :::::::::::::::::");

    out.print("<pre>"+getStackTraceAsString(e)+"</pre>");

  }

  finally {

%>

<p>Completed EJB operations at <%= new Date() %>

<p><hr width=80%>

<%

  }

%>

<p>

<font size=-1>

Copyright &copy; 1999-2000 by BEA Systems, Inc. All Rights Reserved.

</font>

</font>

</body>

</html>


Beginner Using Servlets to display, insert and update records in database.(1)

作者：spring.z

email: spring.z@elong.com

日期：2001-7-3 9:08:37

Displaying Records from the Database with Java Servlets.

Overview :

In this article I'll explain each step you need to know to display records from the database using Servlets. The steps for displaying records in JSP pages and Java Beans are almost the same. We will first build a small example Microsoft Access database, create a DSN for it and using JDBC ( Java Database Connectivity ) driver connect with it and display the records from a given table. Since 80% of your time developing applications will be spent on interacting with databases, you should pay utmost importance to this article.

Access Database :

You can use any database of your choice but for this article I will stick with Microsoft Access database on a Windows platform. The steps for creating such a database and assigning a DSN have already been explained in detail in an earlier article, you should consult that article for details. I will only briefly mention it here.

Create a new Access database with the name of 'odbc_exmp.mdb' and create a table 'Names' containing three fields 'ID', 'first_name' and 'last_name' where 'ID' is the primary key :

Go to the control panel and create a new DSN ( Data Source Name ) 'odbc_exmp' for it and point it to the path of your database on your computer.

Populate the 'Names' table with any values like the following so that we can display the records later :

We are finished with the database. Move on to the next page where we create our Java Servlet to display these records on the user screen.

DisplayServlet :

Create a new DisplayServlet.java file in the /APP_NAME/WEB-INF/classes/com/stardeveloper/servlets/db/ folder. Note /APP_NAME/ is the path of your application within your application server, in Tomcat 4.0 /APP_NAME/ will be /CATALINA_HOME/webapps/star/ where 'star' is the name of the application.

Copy and paste the following code into DisplayServlet.java file and compile it :

package com.stardeveloper.servlets.db;

import java.sql.*;

import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

public class DisplayServlet extends HttpServlet {

    public void doGet(HttpServletRequest req, HttpServletResponse res)

        throws ServletException, IOException {

        res.setContentType("text/html");

        PrintWriter out = res.getWriter();

        out.print("<html><head>");

        out.print("</head><body>");

        out.print("<form action=\"");

        out.print( req.getRequestURI() );

        out.print("\" method=\"post\">");

        out.print("<input type=\"submit\" ");

        out.print("value=\" \">  ");

        out.print("Display Records</form>");

        out.print("</body></html>");

        out.close();

    }

    public void doPost(HttpServletRequest req, HttpServletResponse res)

        throws ServletException, IOException {

        res.setContentType("text/html");

        PrintWriter out = res.getWriter();

        out.print("<html><head>");

        out.print("</head><body>");

        out.print("<code><pre>");

        out.print("<font color=green>ID\tFirst ");

        out.println("Name\tLast Name\n</font>");

        // debugging info

        long time1 = System.currentTimeMillis();

        // connecting to database

        Connection con = null;

        Statement stmt = null;

        ResultSet rs = null;

        try {

            Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");

            con = DriverManager.getConnection("jdbc:odbc:odbc_exmp");

            stmt = con.createStatement();

            rs = stmt.executeQuery("SELECT * FROM Names");

            // displaying records

            while(rs.next()) {

                out.print(rs.getObject(1).toString());

                out.print("\t");

                out.print(rs.getObject(2).toString());

                out.print("\t\t");

                out.print(rs.getObject(3).toString());

                out.print("\n");

            }

        } catch (SQLException e) {

            throw new

            ServletException("Servlet Could not display records.", e);

        } catch (ClassNotFoundException e) {

            throw new

            ServletException("JDBC Driver not found.", e);

        } finally {

            try {

                if(rs != null) {

                    rs.close();

                    rs = null;

                }

                if(stmt != null) {

                    stmt.close();

                    stmt = null;

                }

                if(con != null) {

                    con.close();

                    con = null;

                }

            } catch (SQLException e) {}

        }

        // debugging info

        long time2 = System.currentTimeMillis();

        out.print("</pre></code>");

        out.print("<p>Search took : ");

        out.print( (time2 - time1) );

        out.print(" ms.</p>");

        out.print("<p\"><a href=\"");

        out.print( req.getRequestURI() );

        out.print("\">Back</a></p>");

        out.print("</body></html>");

        out.close();

    }

}

Start your application server and point your browser to http://localhost:8080/star/servlet/com.stardeveloper.servlets.db.DisplayServletto see the Servlet display records on your computer. To see the demo please move on to the last page of this article.

Move on to the next page where I explain the steps shown above to connect to the database.

Steps of Connecting to Database :

In the previous page we developed a Servlet 'DisplayServlet' which extends HttpServlet class and overrides doGet() and doPost() methods. In the doGet() method we display a Form to the user to click, after which he will be shown records from our 'odbc_exmp.mdb' database. Nothing much to talk about yet. In doPost() method we make a connection to the database and iterate through it's records. This is the method which needs more explanation.

Step 1 : Loading JDBC Driver :

First step to connect to any database using JDBC is to first load a JDBC driver. Once loaded we use methods of this driver to interact with the database. If JDBC driver is not loaded or could not be loaded we cannot connect to the database. For this example we are using Sun's JDBC/ODBC bridge driver though not a production class driver does allow us to connect to any ODBC compliant database on Windows platform. So the first step will be :

    Connection con = null;

    Statement stmt = null;

    ResultSet rs = null;

    try {

        Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");

Notice that we first declare the three objects we will be using, Connection, Statement and ResultSet objects. All of them are found in the java.sql package. We then start a try block to catch any exceptions that may be thrown. Last line is the main step where we dynamically load the JDBC driver using Class.forName() method. sun.jdbc.odbc.JdbcOdbcDriver is the JDBC driver we are using.

Step 2 : Making Connection :

Ok we have loaded the JDBC driver it is time to make connection to the database. We do this using DriverManager.getConnection() method. As an argument to this method we provide 'path' to the database we are going to connect with.

    con = DriverManager.getConnection("jdbc:odbc:odbc_exmp");

The path to our database is 'jdbc:odbc:odbc_exmp'. If you have to substitute some other DSN then replace 'odbc_exmp' with the newer one e.g. 'jdbc:odbc:newDSN'.

Step 3 : Retrieving Records :

We are connected to the database, how to display records? well to display records we will have to create just two more objects, Statement and ResultSet. Statement object encapsulates the SQL query we want to execute and ResultSet object is the real object which carries our records which we want to display.

    stmt = con.createStatement();

    rs = stmt.executeQuery("SELECT * FROM Names");

We create the Statement object using Connection.createStatement() method. Once created we provide it with the SQL query we want to use to retrieve records, which in this case is "SELECT * FROM Names". This query retrieves all records from the 'Names' table. Statement.executeQuery() method returns a ResultSet object containing the records retrieved using that query.

Last 3 steps are explained on the next page.

Step 4 : Displaying Records :

We now have a ResultSet object filled with records retrieved from the database, so how to show them? This is how we iterate through the records :

    while(rs.next()) {

        out.print(rs.getObject(1).toString());

        out.print("\t");

        out.print(rs.getObject(2).toString());

        out.print("\t\t");

        out.print(rs.getObject(3).toString());

        out.print("\n");

    }

Using a while loop we iterate through the records. Notice that ResultSet.next() methods does two things. One that it returns a boolean ( true / false ) value indicating more records are there to be shown and second that it moves the database cursor one row forward so that next time when we call ResultSet.getObject() method we get object from the next row.

Within the while loop we access records for each field of the table 'Names' using ResultSet.getObject(n) method where 'n' is the number of field from left to right. Since our table 'Names' had three fields we use this method thrice in the loop and each time incrementing it by one to show record from each field. We then use Object.toString() method to display a String value back to the user.

Step 5 : Catching Exceptions :

We connected to the database, retrieved records, showed it to the user, now what? catch the exceptions that may be thrown during unforseen situations.

    } catch (SQLException e) {

        throw new

        ServletException("Servlet Could not display records.", e);

    } catch (ClassNotFoundException e) {

        throw new

        ServletException("JDBC Driver not found.", e);

    }

Step 6 : Closing Connection :

In the end we close connection to the database.

    finally {

        try {

            if(rs != null) {

                rs.close();

                rs = null;

            }

            if(stmt != null) {

                stmt.close();

                stmt = null;

            }

            if(con != null) {

                con.close();

                con = null;

            }

        } catch (SQLException e) {}

    }

We close all the objects we created earlier and trap all the exceptions that might have been thrown.

This is pretty much it. These are all the steps you need to know to display records from the database using JDBC. In the next page I sum up what we have learned in this tutorial.

Summary :

In this tutorial we developed a 'DisplayServlet' which displays records from 'Names' table in 'odbc_exmp.mdb' database. We discussed all the steps of making a connection to the database to closing that connection in detail. Following are the steps we discussed :

Load the JDBC driver

Make a Connection

Retrieve Records

Display Records

Catch Exceptions

Close Connection

Tip :

To establish a connection to any database you need to have a JDBC driver with you. In most cases this will be provided to you by the database vendor e.g. in case of Oracle 8i. But some database providers haven't created JDBC drivers themselves due to political reasons e.g. Microsoft SQL Server, in which case you have to buy JDBC drivers from some other JDBC driver vendor at some price. Quite a few JDBC drivers of open source RDBMS are available for free e.g. MySQL and PostgreSQL.

JDBC driver is of four types ( I, II, III, IV ). Type IV is the fastest of all and you should always look for type IV JDBC driver, drill this into your brain; always use type IV JDBC driver. Almost all RDMS have type IV JDBC drivers available so there is no reason as to why not to use them.

Ok fellows this is it. Kindly post your questions in the Forum. Enjoy!

There is no associated material for download

Click here to see the demo


Beginner Using Servlets to display, insert and update records in database.(2)

作者：spring.z

email: spring.z@elong.com

日期：2001-7-3 9:10:13

Inserting records into the Database with Java Servlets.

Overview :

This article is next in the series of articles about selecting, inserting, updating and deleting records from the database using JDBC. In this article we will learn how to insert records into the database. If you have followed my earlier article about 'Displaying Records from the Database' then this article is not going to be difficult at all. 90% of the code will be same. So if you haven't read that article then I will suggest that you go through that article before starting this one as quite a few important things have been explained in detail there.

How to Insert Records ?

To insert records into the database we will have to learn about another JDBC class, PreparedStatement. Although we can insert records using the Statement class we discussed in the last article, the INSERT operation is less efficient and not optimized at all. PreparedStatement fills that gap and lets us build SQL queries which are compiled and thus more efficient.

Note that not all database vendors support PreparedStatement class but still it is not a bad habit to use this class so that the ones that do support PreparedStatement class get the extra efficiency.

PreparedStatement :

This class like other JDBC classes we have been discussing is present in the java.sql package. This is how you get handle on a PreparedStatement object :

    String sql = "INSERT INTO Names(first_name, last_name) VALUES (?,?)";

    // con is Connection object

    PreparedStatement ps = con.prepareStatement(sql);

Connection.prepareStatement() returns a reference to the PreparedStatement object. The only argument to the Connection.prepareStatement() method is an SQL statement containing optional '?' ( question mark ) containing SQL statement.

You should put '?' marks in the statement where you are going to put or change the values, for example in my example above I placed '?' marks at two places where I will put different values depending on the values inserted by the user.

So how to set the values of '?' parameters. You set the values by using a setXxx() methods of PreparedStatement class. setXxx() are over 25 methods whose syntax is setObject(int paramIndex, Object o) where paramIndex is the number of '?' mark from left to right in the SQL statement. For example we will use setString(1, value1) and setString(2, value2) methods to set the value of both parameters to two different values.

    ps.setString(1, "First Name");

    ps.setString(2, "Last Name");

    ps.executeUpdate();

Once the parameters are set in the PreparedStatement object, we execute the query using PreparedStatement.executeUpdate() method. You should use PreparedStatement.executeUpdate() for INSERT, UPDATE and DELETE SQL queries and PreparedStatement.executeQuery() for any SQL statement that returns records.

On the next page we make use of PreparedStatement object to develop a user Form page in which a user can enter his first and last name and when presses the 'submit' button the records are inserted into the database using the methods we just discussed.

InsertServlet :

Create a new InsertServlet.java file in the /APP_NAME/WEB-INF/classes/com/stardeveloper/servlets/db/ folder. Note /APP_NAME/ is the path of your application within your application server, in Tomcat 4.0 /APP_NAME/ will be /CATALINA_HOME/webapps/star/ where 'star' is the name of the application.

Copy and paste the following code into the InsertServlet.java file :

package com.stardeveloper.servlets.db;

import java.sql.*;

import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

public class InsertServlet extends HttpServlet {

    public void doGet(HttpServletRequest req, HttpServletResponse res)

        throws ServletException, IOException {

        res.setContentType("text/html");

        PrintWriter out = res.getWriter();

        out.print("<html><body>");

        out.print("<form action=\"");

        out.print( req.getRequestURI() );

        out.print("\" method=\"post\">");

        out.print("First Name :<br>");

        out.print("<input type=\"text\" name=\"first\"><br>");

        out.print("Last Name :<br>");

        out.print("<input type=\"text\" name=\"last\">");

        out.print("<br><br><input type=\"submit\" value=\" \">");

        out.print("   Insert Record");

        out.print("    <input type=\"submit\" value=\" \">");

        out.print("   Display Records</form>");

        out.print("</body></html>");

        out.close();

    }

    public void doPost(HttpServletRequest req, HttpServletResponse res)

        throws ServletException, IOException {

        res.setContentType("text/html");

        PrintWriter out = res.getWriter();

        out.print("<html><body>");

        out.print("<code><pre>");

        out.println("ID\tFirst Name\tLast Name\n");

        // receiving parameters

        String first = req.getParameter("first").trim();

        String last = req.getParameter("last").trim();

        boolean proceed = false;

        if(first != null && last != null)

            if(first.length() > 0 && last.length() > 0)

                proceed = true;

        // connecting to database

        Connection con;

        Statement stmt;

        ResultSet rs;

        PreparedStatement ps;

        try {

            Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");

            con=DriverManager.getConnection("jdbc:odbc:odbc_exmp");

            String sql = "INSERT INTO Names(first_name,");

            sql += " last_name) VALUES (?,?)";

            ps = con.prepareStatement(sql);

            stmt = con.createStatement();

            // inserting records

            if(proceed) {

                ps.setString(1, first);

                ps.setString(2, last);

                ps.executeUpdate();

            }

            // displaying records

            rs = stmt.executeQuery("SELECT * FROM Names");

            while(rs.next()) {

                out.print(rs.getObject(1).toString());

                out.print("\t");

                out.print(rs.getObject(2).toString());

                out.print("\t\t");

                out.print(rs.getObject(3).toString());

                out.print("\n");

            }

        } catch (SQLException e) {

            throw new ServletException(e);

        } catch (ClassNotFoundException e) {

            throw new ServletException(e);

        } finally {

            try {

                if(rs != null)

                    rs.close();

                if(stmt != null)

                    stmt.close();

                if(ps != null)

                    ps.close();

                if(con != null)

                    con.close();

            } catch (SQLException e) {}

        }

        out.print("</body></html>");

        out.close();

    }

}

Start your application server and point your browser to http://localhost:8080/star/servlet/com.stardeveloper.servlets.db.InsertServletto see the Servlet on your computer. To see the demo please move on to the last page of this article.

For explanation of InsertServlet code above, please proceed to the next page.

Explanation :

Our InsertServlet class extends from HttpServlet class and overrides two methods; doGet() and doPost(). In doGet() we simply display a Form to the user with two input fields for first and last names and two submit buttons, one for inserting and the other one for displaying records.

    String first = req.getParameter("first").trim();

    String last = req.getParameter("last").trim();

    boolean proceed = false;

    if(first != null && last != null)

        if(first.length() > 0 && last.length() > 0)

            proceed = true;

In doPost() we retrieve the first and last name values entered by the user using HttpServletRequest.getParameter() method.

Using a double if statement we make sure that we are not entering null values into the database. If user has entered both first and last name then we proceed.

    Connection con;

    Statement stmt;

    ResultSet rs;

    PreparedStatement ps;

We declare the objects we are going to use to interact with the database.

    Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");

    con=DriverManager.getConnection("jdbc:odbc:odbc_exmp");

We load the Sun's JDBC/ODBC driver and establish connection to our database using the DSN 'odbc_exmp'. Notice that this is the same database we used in 'Displaying Records from the Database'. Please consult that article to see the steps of creating such a database and assigning DSN.

    String sql = "INSERT INTO Names(first_name,");

    sql += " last_name) VALUES (?,?)";

    ps = con.prepareStatement(sql);

    stmt = con.createStatement();

We build the SQL statement which we will use to insert records into the database. Next we create the PreparedStatement and Statement objects using Connection object's methods.

    if(proceed) {

        ps.setString(1, first);

        ps.setString(2, last);

        ps.executeUpdate();

    }

Next we set the two '?' mark parameters in our PreparedStatement object and insert the records using PreparedStatement.executeUpdate() method.

    rs = stmt.executeQuery("SELECT * FROM Names");

    while(rs.next()) {

        out.print(rs.getObject(1).toString());

        out.print("\t");

        out.print(rs.getObject(2).toString());

        out.print("\t\t");

        out.print(rs.getObject(3).toString());

        out.print("\n");

    }

We create our ResultSet object by executing the SELECT query. We then iterate through the records and display it to the user. Notice that the new record we just inserted will also be visible to the user during this iteration.

    if(rs != null)

        rs.close();

    if(stmt != null)

        stmt.close();

    if(ps != null)

        ps.close();

    if(con != null)

        con.close();

Close all the objects that we created.

I haven't mentioned try..catch statements that we used to catch different exceptions that may be thrown during opening and closing of database connection.

On the next page I sum up what we learned in this article.

Summary :

In this step by step tutorial we learned what is PreparedStatement class and how to use it to build fast SQL statements. We then moved forward to build a simple Form application in which a user enters his first and last name and these values are inserted into the database. After that all the names entered are displayed to user.

The database we used in this article was a Microsoft Access database 'odbc_exmp.mdb' that we built in 'Displaying Records from the Database' article. The driver we used was JDBC/ODBC driver, this driver comes with Java Development Kit so you don't need to download and install it separately. For more information on how we built the 'odbc_exmp.mdb' database and what are different types JDBC drivers please consult the above mentioned article.

That's it for this article. Kindly post your questions in the Forum. Thanks.

There is no associated material for download

Click here to see the demo


Beginner Using Servlets to display, insert and update records in database.(3)

此电子文档属C#乐园QQ群3256045整理。
Beginner Using Servlets to display, insert and update records in database.(3)

上一页 目录 下一页

Beginner Using Servlets to display, insert and update records in database.(3)

作者：spring.z

email: spring.z@elong.com

日期：2001-7-3 9:11:12

Updating records in the Database with Java Servlets.

Overview :

This article is next in the series of articles about selecting, inserting, updating and deleting records from the database using JDBC. In this article we will learn how to update records in the database. If you have followed my earlier article about 'Inserting records in the Database' then this article is not going to be difficult at all. 90% of the code will be same. So if you haven't read that article then I will suggest that you go through that article before starting this one as quite a few important things have been explained in detail there.

How to Update Records ?

To update records in the database we will be using the same PreparedStatement class we used before for inserting records. Although we can update records using the Statement class, the update operation is less efficient and not optimized at all. PreparedStatement fills that gap and lets us build SQL queries which are compiled and thus more efficient.

PreparedStatement :

This class like other JDBC classes we have been discussing is present in the java.sql package. This is how you get handle on a PreparedStatement object :

String sql = "UPDATE Names SET first_name=?, last_name=? WHERE ID=?";

// con is Connection object

PreparedStatement ps = con.prepareStatement(sql);

Connection.prepareStatement() returns a reference to the PreparedStatement object. The only argument to the Connection.prepareStatement() method is an SQL statement containing optional '?' ( question mark ).

You should put '?' marks in the statement where you are going to put or change the values, for example in my example above I placed '?' marks at three places where I will put different values depending on the values entered by the user.

So how to set the values of '?' parameters. You set the values by using a setXxx() methods of PreparedStatement class. setXxx() are over 25 methods whose syntax is setObject(int paramIndex, Object o) where paramIndex is the number of '?' mark from left to right in the SQL statement. For example we will use setString(1, value1) and setString(2, value2) methods to set the value of both parameters to two different values. And setInt(3, value3) to set the value of third '?' mark to value3.

    ps.setString(1, "First Name");

    ps.setString(2, "Last Name");

    ps.setId(3, 1);

    ps.executeUpdate();

Once the parameters are set in the PreparedStatement object, we execute the query using PreparedStatement.executeUpdate() method. You should use PreparedStatement.executeUpdate() for update, UPDATE and DELETE SQL queries and PreparedStatement.executeQuery() for any SQL statement that returns records.

On the next page we make use of PreparedStatement object to develop a user Form page in which a user can update his first and last names and then when he presses the 'submit' button the existing record is updateed in the database using the methods we just discussed.

UpdateServlet :

Create a new UpdateServlet.java file in the /APP_NAME/WEB-INF/classes/com/stardeveloper/servlets/db/ folder. Note /APP_NAME/ is the path of your application within your application server, in Tomcat 4.0 /APP_NAME/ will be /CATALINA_HOME/webapps/star/ where 'star' is the name of the application.

Copy and paste the following code into the UpdateServlet.java file :

package com.stardeveloper.servlets.db;

import java.sql.*;

import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

public class UpdateServlet extends HttpServlet {

    public void doGet(HttpServletRequest req, HttpServletResponse res)

        throws ServletException, IOException {

        res.setContentType("text/html");

        PrintWriter out = res.getWriter();

        out.print("<html><body>");

        // connecting to database

        Connection con = null;

        Statement stmt = null;

        ResultSet rs = null;

        try {

            Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");

            con=DriverManager.getConnection("jdbc:odbc:odbc_exmp");

            stmt = con.createStatement();

            // displaying records

            rs = stmt.executeQuery("SELECT * FROM Names");

            out.print("<form action=\"");

            out.print( req.getRequestURI() );

            out.print("\" method=\"post\">");

            out.print("<input type=\"hidden\" name=\"id\"");

            out.print(" value=\"0\">");

            out.print("<input type=\"submit\" value=\" \">");

            out.print("    Display Records<br><br>");

            out.print("First & Last Names :<br><br>");

            while(rs.next()) {

                out.print("<form action=\"");

                out.print( req.getRequestURI() );

                out.print("\" method=\"post\">");

                out.print("<input type=\"hidden\"");

                out.print(" name=\"id\" value=\"");

                out.print( rs.getObject(1).toString() );

                out.print("\">");

                out.print("<input type=\"text\"");

                out.print(" name=\"first\" value=\"");

                out.print( rs.getObject(2).toString() );

                out.print("\">");

                out.print("<input type=\"text\"");

                out.print(" name=\"last\" value=\"");

                out.print( rs.getObject(3).toString() );

                out.print("\">");

                out.print("    <input type=\"submit\"");

                out.print(" value=\" \">");

                out.print("   Update Record<br>");

                out.print("</form>");

            }

        } catch (Exception e) {

            throw new ServletException(e);

        } finally {

            try {

                if(rs != null) {

                    rs.close();

                    rs = null;

                }

                if(stmt != null) {

                    stmt.close();

                    stmt = null;

                }

                if(con != null) {

                    con.close();

                    con = null;

                }

            } catch (SQLException e) {}

        }

        out.print("</pre></code>");

        out.print("<p\"><a href=\"");

        out.print( req.getRequestURI() );

        out.print("\">Back</a></p>");

        out.print("</body></html>");

        out.close();

    }

    public void doPost(HttpServletRequest req, HttpServletResponse res)

        throws ServletException, IOException {

        res.setContentType("text/html");

        PrintWriter out = res.getWriter();

        out.print("<html><body>");

        out.print("<code><pre>");

        out.print("<font color=green>ID\t");

        out.println("First Name\tLast Name\n</font>");

        // receiving parameters

        String first = req.getParameter("first").trim();

        String last = req.getParameter("last").trim();

        int id;

        try {

            id = Integer.parseInt(req.getParameter("id").trim());

        } catch (NumberFormatException e) {

            throw new ServletException(e);

        }

        boolean proceed = false;

        if(first != null && last != null)

            if(first.length() > 0 && last.length() > 0)

                proceed = true;

        // connecting to database

        Connection con = null;

        Statement stmt = null;

        ResultSet rs = null;

        PreparedStatement ps = null;

        try {

            Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");

            con=DriverManager.getConnection("jdbc:odbc:odbc_exmp");

            String sql = "UPDATE Names SET first_name=?";

            sql += ", last_name=? WHERE ID=?";

            ps = con.prepareStatement(sql);

            stmt = con.createStatement();

            // updating records

            if(proceed) {

                ps.setString(1, first);

                ps.setString(2, last);

                ps.setInt(3, id);

                ps.executeUpdate();

            }

            // displaying records

            rs = stmt.executeQuery("SELECT * FROM Names");

            while(rs.next()) {

                out.print( rs.getObject(1).toString() );

                out.print("\t");

                out.print( rs.getObject(2).toString() );

                out.print("\t\t");

                out.print( rs.getObject(3).toString() );

                out.print("\n");

            }

        } catch (Exception e) {

            throw new ServletException(e);

        } finally {

            try {

                if(rs != null) {

                    rs.close();

                    rs = null;

                }

                if(stmt != null) {

                    stmt.close();

                    stmt = null;

                }

                if(ps != null) {

                    ps.close();

                    ps = null;

                }

                if(con != null) {

                    con.close();

                    con = null;

                }

            } catch (SQLException e) {}

        }

        out.print("</pre></code>");

        out.print("<p\"><a href=\"");

        out.print( req.getRequestURI() );

        out.print("\">Back</a></p>");

        out.print("</body></html>");

        out.close();

    }

}

Start your application server and point your browser to http://localhost:8080/star/servlet/com.stardeveloper.servlets.db.UpdateServletto see the Servlet on your computer. To see the demo please move on to the last page of this article.

For explanation of UpdateServlet code above, please proceed to the next page.

Explanation :

Notice that we are using the same Access database we built in the 'Displaying Records from the Database' article with the DSN of 'odbc_exmp'. Please consult that article for more details on the database and 'Names' table.

Our UpdateServlet class extends from HttpServlet class and overrides two methods; doGet() and doPost(). In doGet() we connect to the database and retrieve all the values from the 'Names' table and display a Form to the user with two input fields containing first and last names along with a submit button for updating records.

    String first = req.getParameter("first").trim();

    String last = req.getParameter("last").trim();

    int id;

    try {

        id = Integer.parseInt(req.getParameter("id").trim());

    } catch (NumberFormatException e) {

        throw new ServletException(e);

    }

    boolean proceed = false;

    if(first != null && last != null)

        if(first.length() > 0 && last.length() > 0)

            proceed = true;

In doPost() we retrieve the first and last name values entered by the user using HttpServletRequest.getParameter() method.

Using a double if statement we make sure that we are not entering null values into the database. If user has entered both first and last name then we proceed.

    Connection con;

    Statement stmt;

    ResultSet rs;

    PreparedStatement ps;

We declare the objects we are going to use to interact with the database.

    Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");

    con=DriverManager.getConnection("jdbc:odbc:odbc_exmp");

We load the Sun's JDBC/ODBC driver and establish connection to our database using the DSN 'odbc_exmp'. Notice that this is the same database we used in 'Displaying Records from the Database'. Please consult that article to see the steps of creating such a database and assigning DSN.

    String sql = "UPDATE Names SET first_name=?";

    sql += ", last_name=? WHERE ID=?";

    ps = con.prepareStatement(sql);

    stmt = con.createStatement();

We build the SQL statement which we will use to insert records into the database. Next we create the PreparedStatement and Statement objects using Connection object's methods.

    if(proceed) {

        ps.setString(1, first);

        ps.setString(2, last);

        ps.setInt(3, id);

        ps.executeUpdate();

    }

Next we set the three '?' mark parameters in our PreparedStatement object and update the record using PreparedStatement.executeUpdate() method.

    rs = stmt.executeQuery("SELECT * FROM Names");

    while(rs.next()) {

        out.print(rs.getObject(1).toString());

        out.print("\t");

        out.print(rs.getObject(2).toString());

        out.print("\t\t");

        out.print(rs.getObject(3).toString());

        out.print("\n");

    }

We create our ResultSet object by executing the SELECT query. We then iterate through the records and display it to the user. Notice that the new record we just inserted will also be visible to the user during this iteration.

    if(rs != null)

        rs.close();

    if(stmt != null)

        stmt.close();

    if(ps != null)

        ps.close();

    if(con != null)

        con.close();

Close all the objects that we created.

I haven't mentioned try..catch statements that we used to catch different exceptions that may be thrown during opening and closing of database connection.

On the next page I sum up what we learned in this article.

Summary :

In this step by step tutorial we learned what is PreparedStatement class and how to use it to build fast SQL statements. We then moved forward to build a simple Form application in which we display first and last names of all the users in our database allowing a user to update his first and last name and these values are then updated in the database. After that all the names are displayed to user. Note that this application assumes that you have already built the 'odbc_exmp.mdb' Access database with a DSN of 'odbc_exmp' and contains a single table 'Names' with three fields 'ID', 'first_name' and 'last_name' where 'ID' is the primary key. To learn more about this database read 'Displaying Records from the Database' article.

The driver we used was JDBC/ODBC driver, this driver comes with Java Development Kit so you don't need to download and install it separately. For more information on how we built the 'odbc_exmp.mdb' database and what are different types JDBC drivers please consult the above mentioned article.

If you have followed my 'Inserting Records into the Database' article then you must have noticed that the only real change that is required to update records instead of inserting is to change the SQL query from INSERT to UPDATE, so you have got only one line to change.

That's it for this article. Kindly post your questions in the Forum. Thanks.

There is no associated material for download

Click here to see the demo


C#、Java之比较

此电子文档属C#乐园QQ群3256045整理。
C#、Java之比较

上一页 目录 下一页

C#、Java之比较

作者：lindows

日期：2001-2-22 16:52:44

    很多人说C#是微软用来和Java抗衡的武器，因为二者在很大程度上有着惊人的相似，尽管如此，两者不同的地方也很多，所谓“于细微处见差异”。那么两者的相似和区别都在什么地方呢？我们从今天开始，会从各个角度来对比C#和Java的特点，希望能对正在学习、使用C#的朋友有所帮助。

1、C#和.NET平台的概貌

    2000年6月，微软发布C#语言和.NET平台。C#语言是一种强类型的，面向对象的语言，它具有语法简单、表达力强的特点，而.NET平台则是构成微软的“.NET计划”的基石。

    .NET平台的核心包括两方面，一方面就是著名的通用语言运行机(Common Language Runtime)，虽然这个名词起得晦涩了点，不过大家可以拿它和Java的虚拟机来作比较，二者完成的任务大致相同；另一方面就是一大堆通用函数库，这些库函数可以被多种语言调用，并且通过编译都产生一种共同的中间语言（Intermediate Language），这种语言也可以拿Java的字节码来类比，虽然完成的方式有些不一样。

2、C#和Java

    下面简单地把C#和Java的相似处列出来，虽然在这里我们重点讨论的是C#和Java的不同点，但是了解一下二者的相同之处也是很有必要的。

    二者都编译成跨平台的、跨语言的代码，并且代码只能在一个受控制的环境中运行

    自动回收垃圾内存，并且消除了指针（在C#中可以使用指针，不过必须注明unsafe关键字）

    都不需要头文件，所有的代码都被“包(package)”限制在某个范围内，并且因为没有头文件，所以消除了类定义的循环依赖

    所有的类都是从对象派生出来，并且必须使用New关键字分配内存

    用对象加锁的方式来支持多线程

    都具有接口(interface)的概念

内部类

    继承类的时候不会以某种特定的访问权限来继承；

    没有全局函数或者常量，一切必须属于类；

    数组或者字符串都自带长度计算和边界检查；

    只使用“.”操作符，没有“->”和“::”；

    “null”、“boolean”和“bool”成为了关键字；

    任何变量均在使用前进行初始化；

    不能使用整数来返回到if条件语句中，必须使用布尔值；

    “Try”模块后可以有“finally” ；

3. 属性（Property）

    属性的概念对大家来说应该是很熟悉的，类成员函数可以自由地访问本类中的任何属性成员。不过若要从一个类中去访问另一个类中的属性，那就比较麻烦了，所以很多时候我们使用Getxxx和Setxxx方法，这样看起来显得极不自然，比如用Java或者C++，代码是这样的：

    foo.setSize (getSize () + 1);

    label.getFont().setBold (true);

    但是，在C#中，这样的方法被“属性化”了。同样的代码，在C#就变成了：

    foo.size++;

    label.font.bold = true;

    可以看出来，C#显然更容易阅读和理解。我们从这个“属性方法”的子程序代码中，也可以看到类似情况：

Java/C++:

public int getSize()

{

    return size;

}

public void setSize (int value)

{

    size = value;

}

C#:

public int Size

{

  get{return size;}

  set{size = value;}

}

    为了区分这种属性化的方法和类的属性成员，在C#中把属性成员称作“域(field)”，而“属性”则成为这种“属性化的方法”专用的名词。顺便说一句，其实这样的属性化方法在VB和DELPHI中是经常碰到的，在VB中它也就叫属性。

    另外，在C#中Get和Set必须成对出现，一种属性不能只有Get而没有Set（在Java和C++中就可以只有Get或者只有Set），C#中这样做的好处在于便于维护，假如要对某种属性进行修改，就会同时注意Get和Set方法，同时修改，不会改了这个忘了那个。

4、对象索引机制（Indexer）

    C#中引入了对象索引机制。说得明白点，对象索引其实就是对象数组。这里和上一节中的属性联系起来讲一下，属性需要隐藏Get和Set方法，而在索引机制中，各个对象的Get或者Set方法是暴露出来的。比如下面的例子就比较清楚地说明了这一点。

public class Skyscraper

{

  Story[] stories;

  public Story this [int index] {

    get {

      return stories [index];

    }

    set {

      if (value != null) {

        stories [index] = value;

      }

    }

  }

...

}

Skyscraper empireState = new Skyscraper (...);

empireState [102] = new Story ("The Top One", ...);

    呵呵，有了这种特性，我们就再不用怕课堂上老师叫我们写对象数组这种程序了。

5. 指代（Delegate）

    指代这个玩意很特别，它有点象指针，但又不完全是，不过大家还是可以把它理解为一种类型安全的、面向对象的指针。（什么是类型安全和面向对象就不用讲了吧？）顺便提一句，有很多书上把Delegate翻译成代理，我觉得这样翻不够确切，翻译成“指代”更恰当些，道理上吻合，并且还符合它的本来意思——微软本来就是用Delegate来“取代指针”，所以叫“指代”，呵呵。

    说起指代，也许至今Sun还会对它愤愤不已，为什么呢？因为在Sun的标准Java中是没有这个东西的，它是微软99年发布的MSVJ++6添加的“新特性”。为此，两家公司吵得不亦乐乎，并且还专门在网上写了大量文章互相攻击，有兴趣的朋友可以去看看（只有英文版）。

http://www.Javasoft.com/docs/white/delegates.html

http://msdn.microsoft.com/visualj/technical/articles/delegates/truth.asp

    话归正传，指代有什么特点呢？一个明显的特点就是它具有了指针的行为，就好象从Java又倒回到了C++。在C#中，指代完成的功能大概和C++里面的指针，以及Java中的接口相当。但是，指代比起C++的“正宗指针”来又要高明一些，因为它可以同时拥有多个方法，相当于C++里面的指针能同时指向多个函数，并且是类型安全的，这一点体现了它的“对象”特性；而比起Java的接口来，指代高明的地方在于它能可以不经过内部类就调用函数，或者用少量代码就能调用多种函数，这一点体现了它的“指针”特性。呵呵，很有“波粒二象性”的味道吧？指代最重要的应用在于对于事件的处理，下一节我们将重点介绍。

6、事件（Event）

    C#对事件是直接支持的（这个特点也是MSVJ所具有的）。当前很多主流程序语言处理事件的方式各不相同，Delphi采用的是函数指针（这在Delphi中的术语是“closure”）、Java用改编类来实现、VC用WindowsAPI的消息系统，而C#则直接使用delegate和event关键字来解决这个问题。下面让我们来看一个例子，例子中会给大家举出声明、调用和处理事件的全过程。

//首先是指代的声明，它定义了唤醒某个函数的事件信号

public delegate void ScoreChangeEventHandler (int newScore, ref bool cancel);

//定义一个产生事件的类

public class Game

{

  // 注意这里使用了event关键字

  public event ScoreChangeEventHandler ScoreChange;

    int score;

    // Score 属性

    public int Score

    {

      get {

        return score;

      }

      set {

        if (score != value)

        {

          bool cancel = false;

          ScoreChange (value, ref cancel);

          if (! cancel)

          score = value;

        }

    }

}

// 处理事件的类

public class Referee

{

  public Referee (Game game)

  {

    // 裁判负责调整比赛中的分数变化

    game.ScoreChange += new ScoreChangeEventHandler (game_ScoreChange);

  }

  // 注意这里的函数是怎样和ScoreChangeEventHandler的信号对上号的

  private void game_ScoreChange (int newScore, ref bool cancel)

  {

    if (newScore < 100)

      System.Console.WriteLine ("Good Score");

    else

    {

      cancel = true;

      System.Console.WriteLine ("No Score can be that high!");

    }

  }

}

// 主函数类，用于测试上述特性

public class GameTest

{

  public static void Main ()

  {

    Game game = new Game ();

    Referee referee = new Referee (game);

    game.Score = 70;

    game.Score = 110;

  }

}

    在主函数中，我们创建了一个game对象和一个裁判对象，然后我们通过改变比赛分数，来观察裁判对此会有什么响应。

    请注意，我们的这个系统中，Game对象是感觉不到裁判对象的存在的，Game对象在这里只负责产生事件，至于有谁会来倾听这个事件，并为之作出反应，Game对象是不作任何表态的。

    我们注意到，在裁判类的Referee函数中，Game.ScoreChange后面使用了+=和-=操作符，这是什么意思呢？回到我们定义ScoreChange的地方，可以发现ScoreChange是用event关键字修饰的，那么这里的意思就很明白了：ScoreChange是一个事件，而事件被触发后需要相应的事件处理机制，+=/-=就是为这个事件增加/移除相对应的事件处理程序，而且，并不是一个事件只能对应一个处理程序，我们还可以用这两个操作符为同一事件增加/移除数个事件处理程序。怎么样？很方便吧！

    在实际应用中，和我们上面讲的（竞赛-裁判）机制很相近的系统就是图形用户界面系统了。Game对象可以看作是图形界面上的小零件，而得分事件就相当于用户输入事件，而裁判就相当于相应的应用程序，用于处理用户输入。

    指代机制的首次亮相是在MSVJ里，它是由Anders Hejlsberg发明的，现在又用到了C#中。指代用在Java语言中的后果，则直接导致了微软和Sun之间对类和指针的关系产生了大量的争论和探讨。有意思的是，Java的发明者James Gosling非常幽默地称呼指代的发明者Anders Hejlsberg为“‘函数指针’先生”，因为Anders Hejlsberg总是想方设法地把指针变相地往各种语言中放；不过有人在看了Java中大量地使用了各种类后，也戏称Java的发明者James Gosling为“‘全都是类’先生”，真是其中滋味，尽在不言中啊。


classpath详解(谨献给那些找不到北的朋友)

此电子文档属C#乐园QQ群3256045整理。
classpath详解(谨献给那些找不到北的朋友)

上一页 目录 下一页

classpath详解(谨献给那些找不到北的朋友)

作者：r12

日期：2001-4-21 0:11:43

设置类路径

结构

可通过对 JDK 工具使用 -classpath 选项（首选方法）或设置 CLASSPATH 环境变量来设置类路径。

    C:> jdkTool -classpath path1;path2...

    C:> set CLASSPATH=path1;path2...

每个 path 以文件名或目录结尾，该文件名或目录取决于将类路径设置成什么：

对于包含 .class 文件的 .zip 或 .jar 文件，路径以 .zip 或 .jar 文件名结尾。

对于未命名包中的 .class 文件，路径以包含 .class 文件的目录结尾。

对于已命名包中的 .class 文件，路径以包含“root”包（完整包名中的第一个包）的目录结尾。

用分号来分隔多个项目。使用 set 命令时，需要省略等号两边的空格（=）。其中 jdkTool 可以为 java、javac、javadoc，等等。有关详细列表，参见 JDK 开发工具。

说明

类路径告诉 Java 应用程序去哪里查找第三方和自定义类 -- 即不是 Java 扩展或 Java 平台一部分的类。在 JDK 1.2 中，JVM 和其他 JDK 工具通过依次搜索平台库、库扩展和类路径来查找类（有关搜索策略的详细信息，参见如何查找类）。

多数应用程序的类库都会充分利用扩展机制。因此，只有想要加载某个类库 (a) 未位于当前目录或其分枝包中 且 (b) 未位于扩展机制所指定的位置时，才需设置类路径。

如果用户是从旧版本 JDK 升级而来，则启动设置可能包括不再需要的 CLASSPATH 设置。这时应该删除任何非应用程序特定的设置。有些使用 Java 虚拟机的第三方应用程序可能修改 CLASSPATH 环境变量以包括它们使用的类库。这种设置可以保留。

可通过在调用 JVM 或其他 JDK 工具时使用 Java 工具的 -classpath 选项改变类路径（例如： java -classpath ...）。这是改变类路径的首选方法。还可通过使用 CLASSPATH 环境变量改变类路径。

注意：JDK 1.2 缺省类路径是当前目录。设置 CLASSPATH 变量或使用 -classpath 命令行开关将覆盖缺省值，因而如果想要在搜索路径中包括当前目录，则必须在新设置中包括“.”。

类可以存储在目录（文件夹）或归档文件（例如 classes.zip 或 classes.jar）中。有关归档文件的详细信息和类路径的工作原理，参见本文档最后的理解类路径和包名。

重要说明：JDK 旧版本在缺省类路径中还包括 <jdk-dir>/classes 项。该目录仅供 JDK 使用，且不用于应用程序类。应用程序类应该放置在 JDK 外部的目录。这样，安装新 JDK 时不必需要重新安装应用程序库。为了与旧版本的兼容性，使用 <jdk-dir>/classes 目录作为类库的应用程序在当前版本中仍能运行，但不能保证在以后的版本中它们还能运行。

使用 Java 工具的 -classpath 选项

Java 工具 java、jdb、javac 和 javah 具有 -classpath 选项，在工具运行时它将取代缺省类路径或由 CLASSPATH 环境变量所指定的类路径。这是改变类路径设置的推荐方法，因为这样每个应用程序可具有它所需的类路径而又不会干扰其他应用程序。

运行时工具 java 和 jdb 还具有 -cp 选项。该选项是 -classpath 的缩写。

对于非常特殊的情况，java 和 javac 都具有开关，使得可改变它们使用的路径以查找它们自己的类库。但是，绝大多数用户从来都不会用到这些开关。

使用 CLASSPATH 环境变量

如前一节中所述，一般用户将想要使用 -classpath 命令行选项。本节将介绍如何设置 CLASSPATH 环境变量或清除以前安装遗留下的设置。

设置 CLASSPATH

在 DOS 提示符下，可用 set 命令修改 CLASSPATH 环境变量。其格式为：

   set CLASSPATH=path1;path2 ...

路径应该以指定驱动器的字母开头，例如 C:\...。这样，在偶然切换到不同驱动器时仍可找到类（例如，如果路径项以 \... 开头，并且当前位于驱动器 D: 上，则所需的类将在 D: 而不是 C: 驱动器上找）。

清除 CLASSPATH

如果 CLASSPATH 环境变量被设置成不正确的值，或启动文件或脚本程序设置了不正确路径，则可通过使用下列命令清除 CLASSPATH：

  C:> set CLASSPATH=

该命令仅清除当前会话的 CLASSPATH。要确保在以后的会话中具有正确的 CLASSPATH 设置，则应该删除或修改启动设置。

更改启动设置

如果在系统启动时设置 CLASSPATH 变量，则查找它的位置取决于所使用的操作系统：

  操作系统 方法

Windows 98 和

Windows 95 检查 autoexec.bat 文件中的 set 命令。

Windows NT 启动“控制面板”，选择“系统”，单击“环境”选项卡，并在“用户变量”部分中，检查 CLASSPATH 变量。

理解类路径和包名

Java 类被组织成包，而这些包被映射到文件系统中的目录。但是与文件系统不同的是，无论何时指定包名，都应指定完整包名 -- 永远不要仅指定它的一部分。例如，java.awt.Button 的包名总是应指定为 java.awt。

例如，假定想要 Java 运行环境在包 utility.myapp 中查找名为 Cool.class 的类。如果该目录的路径为 C:\java\MyClasses\utility\myapp，则应该将类路径设置成包含 C:\java\MyClasses。

要运行该应用程序，可使用下述 JVM 命令：

C:>  java -classpath C:\java\MyClasses utility.myapp.Cool

当该应用程序运行时，JVM 使用类路径设置查找在 utility.myapp 包中定义的任何其他类。

注意：应在命令中指定完整包名。例如，设置类路径包含 C:\java\MyClasses\utility 并使用命令 java myapp.Cool 是不可能的，因为这找不到该类。

（您可能想知道用什么定义类的包名。答案是：包名是类的一部分，并且不能修改，除非重新编译该类。）

注意：包规范机制一个有趣的结果是，属于相同包的多个文件实际上可存在于不同的目录中。对于每个类，包名都是相同的，但是每个文件的路径可从类路径中的不同目录开始。

文件夹和归档文件

当类存储在目录（文件夹）中时，例如 c:\java\MyClasses\utility\myapp，则类路径项指向包含包名第一个元素的目录（在这里为 C:\java\MyClasses，因为包名是 utility.myapp）。

但是当类存储在归档文件（.zip 或 .jar 文件）中时，类路径项则是该 .zip 或 .jar 文件的路径。例如，要使用位于 .jar 文件中的类库，则命令应该类似下述形式：

   java -classpath C:\java\MyClasses\myclasses.jar utility.myapp.Cool

多重指定

要在目录 C:\java\MyClasses 和 C:\java\OtherClasses 中查找类文件，则可将类路径设置为：

   java -classpath C:\java\MyClasses;C:\java\OtherClasses ...

注意两个路径之间用分号分隔。

指定次序

指定多个类路径项的次序是相当重要的。Java 解释器将按照目录在类路径变量中的次序在各个目录中查找类。在上例中，Java 解释器首先在目录 C:\java\MyClasses 中查找需要的类。只有当它在该目录中没有找到该类时，解释器才能到 C:\java\OtherClasses 目录中查找。


CLASS文件的存放位置

作者：何志强

email: hhzqq@21cn.com

日期：2000-8-11 9:01:57

在Linux平台下的Tomcat中，只需把相关的类库(*.zip、*.jar)放到$TOMCAT_HOME/lib目录下，Tomcat启动时就会自动将这些类库放到CLASSPATH环境变量中，Windows平台下的Tomcat不行，必须手工添加到CLASSPATH环境变量中。


CONNECTING DATABASE USING JAVASERVERPAGES （转载）

此电子文档属C#乐园QQ群3256045整理。
CONNECTING DATABASE USING JAVASERVERPAGES （转载）

上一页 目录 下一页

CONNECTING DATABASE USING JAVASERVERPAGES （转载）

作者：阿土伯

email: lu1206@yeah.net

日期：2001-1-5 11:04:15

I have given you the source code, how to connect with database from jsp. this is easy and simple.cut and paste the below code and modify according to your requirements.This is esspecially for those who had coding experience in ASP.I have given proper documentation.

 <!--first we've to import the necessary packages-->

<!--importing packages starts-->

<%@ page info="database handler"%>

<%@ page import="java.io.*"%>

<%@ page import="java.util.*"%>

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

<%@ page import="javax.servlet.*"%>

<%@ page import="javax.servlet.http.*"%>

<!--importing packages ends-->

<%

try

{

//loading the drivers

//this code loads JDBD-ODBC Driver

Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");

//making connection

//The second step in establishing a connection is to have the appropriate

//driver connect to the DBMS. The following line of code illustrates the

//general idea:

String url="jdbc:odbc:test";

//where "test" is the system dsn name

Connection con=DriverManager.getConnection(url, "administrator", "password");

//where "administrator is the username to

//access the database and password is " password"

//DriverManager's getconnection method estabilishes connection

//with the database specified in the JDBC URL

//Then create jdbc statement

Statement stmt = con.createStatement();

String query="CREATE TABLE COFFEES" +

"(COF_NAME VARCHAR(32),"+

"SUP_ID INTEGER,"+

"PRICE FLOAT,"+

"SALES INTEGER,"+

"TOTAL INTEGER)";

//Execute the statement

stmt.executeUpdate(query);

}

catch (Exception e) {}

//give confirmation message

out.println("table coffees created");

%>


Cookie 规范

作者：87zd

日期：2000-12-1 21:52:36

PERSISTENT CLIENT STATE

HTTP COOKIES

Preliminary Specification - Use with caution

--------------------------------------------------------------------------------

INTRODUCTION

Cookies are a general mechanism which server side connections (such as CGI scripts) can use to both store and retrieve information on the client side of the connection. The addition of a simple, persistent, client-side state significantly extends the capabilities of Web-based client/server applications.

OVERVIEW

A server, when returning an HTTP object to a client, may also send a piece of state information which the client will store. Included in that state object is a description of the range of URLs for which that state is valid. Any future HTTP requests made by the client which fall in that range will include a transmittal of the current value of the state object from the client back to the server. The state object is called a cookie, for no compelling reason.

This simple mechanism provides a powerful new tool which enables a host of new types of applications to be written for web-based environments. Shopping applications can now store information about the currently selected items, for fee services can send back registration information and free the client from retyping a user-id on next connection, sites can store per-user preferences on the client, and have the client supply those preferences every time that site is connected to.

SPECIFICATION

A cookie is introduced to the client by including a Set-Cookie header as part of an HTTP response, typically this will be generated by a CGI script.

Syntax of the Set-Cookie HTTP Response Header

This is the format a CGI script would use to add to the HTTP headers a new piece of data which is to be stored by the client for later retrieval.

Set-Cookie: NAME=VALUE; expires=DATE;

path=PATH; domain=DOMAIN_NAME; secure

NAME=VALUE

This string is a sequence of characters excluding semi-colon, comma and white space. If there is a need to place such data in the name or value, some encoding method such as URL style %XX encoding is recommended, though no encoding is defined or required.

This is the only required attribute on the Set-Cookie header.

expires=DATE

The expires attribute specifies a date string that defines the valid life time of that cookie. Once the expiration date has been reached, the cookie will no longer be stored or given out.

The date string is formatted as:

Wdy, DD-Mon-YYYY HH:MM:SS GMT

This is based on RFC 822, RFC 850, RFC 1036, and RFC 1123, with the variations that the only legal time zone is GMT and the separators between the elements of the date must be dashes.

expires is an optional attribute. If not specified, the cookie will expire when the user's session ends.

Note: There is a bug in Netscape Navigator version 1.1 and earlier. Only cookies whose path attribute is set explicitly to "/" will be properly saved between sessions if they have an expires attribute.

domain=DOMAIN_NAME

When searching the cookie list for valid cookies, a comparison of the domain attributes of the cookie is made with the Internet domain name of the host from which the URL will be fetched. If there is a tail match, then the cookie will go through path matching to see if it should be sent. "Tail matching" means that domain attribute is matched against the tail of the fully qualified domain name of the host. A domain attribute of "acme.com" would match host names "anvil.acme.com" as well as "shipping.crate.acme.com".

Only hosts within the specified domain can set a cookie for a domain and domains must have at least two (2) or three (3) periods in them to prevent domains of the form: ".com", ".edu", and "va.us". Any domain that fails within one of the seven special top level domains listed below only require two periods. Any other domain requires at least three. The seven special top level domains are: "COM", "EDU", "NET", "ORG", "GOV", "MIL", and "INT".

The default value of domain is the host name of the server which generated the cookie response.

path=PATH

The path attribute is used to specify the subset of URLs in a domain for which the cookie is valid. If a cookie has already passed domain matching, then the pathname component of the URL is compared with the path attribute, and if there is a match, the cookie is considered valid and is sent along with the URL request. The path "/foo" would match "/foobar" and "/foo/bar.html". The path "/" is the most general path.

If the path is not specified, it as assumed to be the same path as the document being described by the header which contains the cookie.

secure

If a cookie is marked secure, it will only be transmitted if the communications channel with the host is a secure one. Currently this means that secure cookies will only be sent to HTTPS (HTTP over SSL) servers.

If secure is not specified, a cookie is considered safe to be sent in the clear over unsecured channels.

Syntax of the Cookie HTTP Request Header

When requesting a URL from an HTTP server, the browser will match the URL against all cookies and if any of them match, a line containing the name/value pairs of all matching cookies will be included in the HTTP request. Here is the format of that line:

Cookie: NAME1=OPAQUE_STRING1; NAME2=OPAQUE_STRING2 ...

Additional Notes

Multiple Set-Cookie headers can be issued in a single server response.

Instances of the same path and name will overwrite each other, with the latest instance taking precedence. Instances of the same path but different names will add additional mappings.

Setting the path to a higher-level value does not override other more specific path mappings. If there are multiple matches for a given cookie name, but with separate paths, all the matching cookies will be sent. (See examples below.)

The expires header lets the client know when it is safe to purge the mapping but the client is not required to do so. A client may also delete a cookie before it's expiration date arrives if the number of cookies exceeds its internal limits.

When sending cookies to a server, all cookies with a more specific path mapping should be sent before cookies with less specific path mappings. For example, a cookie "name1=foo" with a path mapping of "/" should be sent after a cookie "name1=foo2" with a path mapping of "/bar" if they are both to be sent.

There are limitations on the number of cookies that a client can store at any one time. This is a specification of the minimum number of cookies that a client should be prepared to receive and store.

300 total cookies

4 kilobytes per cookie, where the name and the OPAQUE_STRING combine to form the 4 kilobyte limit.

20 cookies per server or domain. (note that completely specified hosts and domains are treated as separate entities and have a 20 cookie limitation for each, not combined)

Servers should not expect clients to be able to exceed these limits. When the 300 cookie limit or the 20 cookie per server limit is exceeded, clients should delete the least recently used cookie. When a cookie larger than 4 kilobytes is encountered the cookie should be trimmed to fit, but the name should remain intact as long as it is less than 4 kilobytes.

If a CGI script wishes to delete a cookie, it can do so by returning a cookie with the same name, and an expires time which is in the past. The path and name must match exactly in order for the expiring cookie to replace the valid cookie. This requirement makes it difficult for anyone but the originator of a cookie to delete a cookie.

When caching HTTP, as a proxy server might do, the Set-cookie response header should never be cached.

If a proxy server receives a response which contains a Set-cookie header, it should propagate the Set-cookie header to the client, regardless of whether the response was 304 (Not Modified) or 200 (OK).

Similarly, if a client request contains a Cookie: header, it should be forwarded through a proxy, even if the conditional If-modified-since request is being made.

EXAMPLES

Here are some sample exchanges which are designed to illustrate the use of cookies.

First Example transaction sequence:

Client requests a document, and receives in the response:

Set-Cookie: CUSTOMER=WILE_E_COYOTE; path=/; expires=Wednesday, 09-Nov-99 23:12:40 GMT

When client requests a URL in path "/" on this server, it sends:

Cookie: CUSTOMER=WILE_E_COYOTE

Client requests a document, and receives in the response:

Set-Cookie: PART_NUMBER=ROCKET_LAUNCHER_0001; path=/

When client requests a URL in path "/" on this server, it sends:

Cookie: CUSTOMER=WILE_E_COYOTE; PART_NUMBER=ROCKET_LAUNCHER_0001

Client receives:

Set-Cookie: SHIPPING=FEDEX; path=/foo

When client requests a URL in path "/" on this server, it sends:

Cookie: CUSTOMER=WILE_E_COYOTE; PART_NUMBER=ROCKET_LAUNCHER_0001

When client requests a URL in path "/foo" on this server, it sends:

Cookie: CUSTOMER=WILE_E_COYOTE; PART_NUMBER=ROCKET_LAUNCHER_0001; SHIPPING=FEDEX

Second Example transaction sequence:

Assume all mappings from above have been cleared.

Client receives:

Set-Cookie: PART_NUMBER=ROCKET_LAUNCHER_0001; path=/

When client requests a URL in path "/" on this server, it sends:

Cookie: PART_NUMBER=ROCKET_LAUNCHER_0001

Client receives:

Set-Cookie: PART_NUMBER=RIDING_ROCKET_0023; path=/ammo

When client requests a URL in path "/ammo" on this server, it sends:

Cookie: PART_NUMBER=RIDING_ROCKET_0023; PART_NUMBER=ROCKET_LAUNCHER_0001

NOTE: There are two name/value pairs named "PART_NUMBER" due to the inheritance of the "/" mapping in addition to the "/ammo" mapping.

--------------------------------------------------------------------------------


CORBA例子（转）————————我也不知道是什麽东东-)

作者：asp2001

email: asp2001@163.net

日期：2000-8-12 9:38:37

Below is a simple example of a CORBA program

download the source file

1. produce a idl file like this

   hello.idl

   module HelloApp {

     interface Hello    {

         string sayHello();

    };

  };

2. produce stub and skeleton files through idltojava.exe

   idltojava hello.idl

   idltojava is not include in the jdk. But you can download it from idldojava.

3. write a server program like this

// HelloServer.java

import HelloApp.*;

import org.omg.CosNaming.*;

import org.omg.CosNaming.NamingContextPackage.*;

import org.omg.CORBA.*;

import java.io.*;

class HelloServant extends _HelloImplBase

{

    public String sayHello()

    {

       return "\nHello world !!\n";

    }

}

public class HelloServer {

    public static void main(String args[])

    {

    try{

        // create and initialize the ORB

        ORB orb = ORB.init(args, null);

        // create servant and register it with the ORB

        HelloServant helloRef = new HelloServant();

        orb.connect(helloRef);

        // get the root naming context

        org.omg.CORBA.Object objRef =

        orb.resolve_initial_references("NameService");

        NamingContext ncRef = NamingContextHelper.narrow(objRef);

        // bind the Object Reference in Naming

        NameComponent nc = new NameComponent("Hello", "");

        NameComponent path[] = {nc};

        ncRef.rebind(path, helloRef);

        // wait for invocations from clients

            java.lang.Object sync = new java.lang.Object();

            synchronized (sync) {

                sync.wait();

            }

    } catch (Exception e) {

        System.err.println("ERROR: " + e);

        e.printStackTrace(System.out);

    }

    }

}

4. write a client program like this

// HelloClient.java

import HelloApp.*;

import org.omg.CosNaming.*;

import org.omg.CORBA.*;

public class HelloClient

{

    public static void main(String args[])

    {

    try{

        // create and initialize the ORB

        ORB orb = ORB.init(args, null);

            // get the root naming context

            org.omg.CORBA.Object objRef =

        orb.resolve_initial_references("NameService");

            NamingContext ncRef = NamingContextHelper.narrow(objRef);

            // test

            System.out.println("OK..");

            // resolve the Object Reference in Naming

            NameComponent nc = new NameComponent("Hello", "");

            NameComponent path[] = {nc};

            Hello helloRef = HelloHelper.narrow(ncRef.resolve(path));

        // call the Hello server object and print results

           //String oldhello = helloRef.lastMessage();

           //System.out.println(oldhello);

        String Hello = helloRef.sayHello();

        System.out.println(Hello);

    } catch (Exception e) {

        System.out.println("ERROR : " + e) ;

        e.printStackTrace(System.out);

    }

    }

}

5. complie these files

   javac *.java HelloApp/*.java

6. run the application

  a. first you've to run the Name Service prior to the others likethis

     c:\>tnameserv

  b. run server

     c:\>java HelloServer

  c. run client

     c:\>java HelloClient

 If you have problems with this example, do let me know!


Declarations and Access Control (1)

此电子文档属C#乐园QQ群3256045整理。
Declarations and Access Control (1)

上一页 目录 下一页

Declarations and Access Control (1)

作者：jeru

email: jeru@163.net

日期：7/3/2001 9:59:06 AM

1) Declarations and Access Control

Objective 1

Write code that declares, constructs and initializes arrays of any base type using any of the permitted forms, both for declaration and for initialization.

1.    Arrays are Java objects. (An object is a class instance or an array.)  and may be assigned to variables of type Object.  All methods of class Object may be invoked on an array.  If you create an array of 5 Strings, there will be 6 objects created.

2.    Arrays should be

1.    Declared. (int[] a; String b[]; Object []c;  Size should not be specified now)

2.    Allocated (constructed). ( a = new int[10]; c = new String[arraysize] )

3.    Initialized. for (int i = 0; i < a.length; a[i++] = 0)

3.    The above three can be done in one step. int a[] = { 1, 2, 3 }; or  int a[] = new int[] { 1, 2, 3 }; But never specify the size with the new statement.

4.    Java arrays are static arrays. Size has to be specified at compile time. Array.length returns array’s size, cannot be changed.  (Use Vectors for dynamic purposes).

5.    Array size is never specified with the reference variable, it is always maintained with the array object. It is maintained in array.length, which is a final instance variable.  Note that arrays have a length field (or property) not a length() method. When you start to use Strings you will use the string, length method, as in  s.length();

6.    Anonymous arrays can be created and used like this:  new int[] {1,2,3} or new int[10]

7.    Arrays with zero elements can be created. args array to the main method will be a zero element array if no command parameters are specified. In this case args.length is 0.

8.    Comma after the last initializer in array declaration is ignored.

    int[] i = new int[2] { 5, 10};  // Wrong

int i[5] = { 1, 2, 3, 4, 5};  // Wrong

int[] i[] = {{},  new int[] {} }; // Correct

int i[][] = { {1,2}, new int[2] }; // Correct

int i[] = { 1, 2, 3, 4, } ; // Correct

int i[][] = new int [10][];  //Correct,  i.length=10.

    int [] i, j[] == int i[], j[][];

9.    Array indexes start with 0. Index is an int data type.

10.    Square brackets can come after datatype or before/after variable name. White spaces are fine. Compiler just ignores them.

11.    Arrays declared even as member variables also need to be allocated memory explicitly.

static int a[];

static int b[] = {1,2,3};

public static void main(String s[]) {

    System.out.println(a[0]); // Throws a null pointer exception

    System.out.println(b[0]); // This code runs fine

    System.out.println(a); // Prints ‘null’

    System.out.println(b); // Prints a string which is returned by toString

}

12.    Once declared and allocated (even for local arrays inside methods), array elements are automatically initialized to the default values.(0 for numeric arrays, false for boolean, '\0' for character arrays, and null for objects).

13.    If only declared (not constructed), member array variables default to null, but local array variables will not default to null(compile error).

14.    Java doesn’t support multidimensional arrays formally, but it supports arrays of arrays. From the specification - “The number of bracket pairs indicates the depth of array nesting.” So this can perform as a multidimensional array. (no limit to levels of array nesting)

15.    Arrays must be indexed by int values; short, byte, or char values may also be used as index values because they are subjected to unary numeric promotion and become int values. An attempt to access an array component with a long index value results in a compile-time error.

16.    All array accesses are checked at run time; an attempt to use an index that is less than zero or greater than or equal to the length of the array causes an ArrayIndexOutOfBoundsException to be thrown.

17.    Every array implements the interfaces Cloneable and java.io.Serializable.

18.    Array Store Exception

If an array variable v has type A[], where A is a reference type, then v can hold a reference to an instance of any array type B[], provided B can be assigned to A.

Thus, the example:

class Point { int x, y; }

class ColoredPoint extends Point { int color; }

class Test {

    public static void main(String[] args) {

        ColoredPoint[] cpa = new ColoredPoint[10];

        Point[] pa = cpa;

        System.out.println(pa[1] == null);

        try {

            pa[0] = new Point();

        } catch (ArrayStoreException e) {

            System.out.println(e);

        }

    }

}

produces the output:

true

java.lang.ArrayStoreException

Testing whether pa[1] is null, will not result in a run-time type error. This is because the element of the array of type ColoredPoint[] is a ColoredPoint, and every ColoredPoint can stand in for a Point, since Point is the superclass of ColoredPoint.

On the other hand, an assignment to the array pa can result in a run-time error. At compile time, an assignment to an element of pa is checked to make sure that the value assigned is a Point. But since pa holds a reference to an array of ColoredPoint, the assignment is valid only if the type of the value assigned at run-time is, more specifically, a ColoredPoint. if not, an ArrayStoreException is thrown.

19.    Every element of an array must be of the same type The type of the elements of an array is decided when the array is declared. If you need a way of storing a group of elements of different types, you can use the collection classes.


Declarations and Access Control (2)

作者：jeru

email: jeru@163.net

日期：7/3/2001 9:59:34 AM

Objective 2

Declare classes, inner classes, methods, instance variables static, variables and automatic (method local) variables, making appropriate use of all permitted modifiers (such as public final static abstract and so forth). State the significance of each of these modifiers both singly and in combination and state the effect of package relationships on declared items qualified by these modifiers.

1. Two types of variables.

1.    Member variables

·    Accessible anywhere in the class.

·    Automatically initialized before invoking any constructor.

·    Static variables are initialized at class load time.

·    Can have the same name as the class.

2.    Automatic variables(method local)

·    Must be initialized explicitly. (compiler will catch it when using, but doesn’t catch it if no using) Object references can be initialized to null to make the compiler happy.

·    Can have the same name as a member variable, resolution is based on scope.

·    Can only be final.  Not other modifiers.

2.   Modifiers are Java keywords that provide information to compiler about the nature of the code, data and classes.   The visibility modifiers are part of the encapsulation mechanism for Java. Encapsulation allows separation of the interface from the implementation of methods.

3.  Access modifiers – public, protected, private

·    Only applied to class level variables. (Method variables are visible only inside the method.)

·    Can be applied to class itself (only to inner classes declared at class level, no such thing as protected or private top level class)

·    Can be applied to methods and constructors.

·    If a class is accessible, it doesn’t mean, the members are also accessible. But if the class is not accessible, the members are not accessible, even though they are declared public.

·    If no access modifier is specified, then the accessibility is default package visibility. All classes in the same package can access the feature. It’s called as friendly access. But friendly is not a Java keyword. Same directory is same package in Java’s consideration.

·    Only one outer class per file can be declared public.  If you declare more than one class in a file to be public, a compile time error will occur.

·    ‘private’ means only the class can access it, not even sub-classes.  So, it’ll cause access denial to a sub-class’s own variable/method.

·    These modifiers dictate, which classes can access the features. An instance of a class can access the private features of another instance of the same class.

·    ‘protected’ means all classes in the same package (like default) and sub-classes in any package can access the features. But a subclass in another package can access the protected members in the super-class via only the references of subclass or its subclasses. A subclass in the same package doesn’t have this restriction. This ensures that classes from other packages are accessing only the members that are part of their inheritance hierarchy.

·    Methods cannot be overridden to be more private. Only the direction shown in following figure is permitted from parent classes to sub-classes.

private à friendly (default) à protected à public

    Parent classes                     Sub-classes

4.  final

·    final classes cannot be sub-classed.

·    final variables cannot be changed.

·    final methods cannot be overridden.  Any methods in a final class are automatically final.

·    Method arguments marked final are read-only. Compiler error, if trying to assign values to final arguments inside the method.

·    Final variables that are not assigned a value at the declaration and method arguments that are marked final are called blank final variables. Try to use blank final variables will give compile error.  They can only be assigned a value at most once in all constructor or initialized block.

·    Static final variables have to be assigned at the declaration time or in static initialized block.

·    Local variables can be declared final as well.

5.  abstract

·    Can be applied to classes and methods.

·    Opposite of final, abstract must be sub-classed.

·    A class should be declared abstract,

1.    if it has any abstract methods.

2.    if it doesn’t provide implementation to any of the abstract methods it inherited

3.    if it doesn’t provide implementation to any of the methods in an interface that it says implementing.

·    Just terminate the abstract method signature with a ‘;’, curly braces will give a compiler error.

·    A class can be abstract even if it doesn’t have any abstract methods.

·    Abstract methods may not be static, final, private, native, synchonized.

·    A class that is abstract may not be instantiated (ie, you may not call its constructor, but in subclass’s constructor, super() works)

6.  static

·    Can be applied to nested classes, methods, variables, free floating code-block (static initializer)

·    static means one per class, not one for each object no matter how many instance of a class might

exist. This means that you can use them without creating an instance of a class.

·    Static variables are initialized at class load time. A class has only one copy of these variables.

·    Static methods can access only static variables. (They have no this)

·    Access by class name is a recommended way to access static methods/variables.

·    Static methods may not be overridden to be non-static.

·    Non-static methods may not be overridden to be static.

·    Local variables cannot be declared as static.

·    Actually, static methods are not participating in the usual overriding mechanism of invoking the methods based on the class of the object at runtime. Static method binding is done at compile time, so the method to be invoked is determined by the type of reference variable rather than the actual type of the object it holds at runtime.

public class StaticOverridingTest {

  public static void main(String s[]) {

    Child c = new Child();

    c.doStuff(); // This will invoke Child.doStuff()

    Parent p = new Parent();

    p.doStuff(); // This will invoke Parent.doStuff()

    p = c;

    p.doStuff(); // This will invoke Parent.doStuff(), rather than Child.doStuff()

  }

}

class Parent {

  static int x = 100;

  public static void doStuff() {

    System.out.println("In Parent..doStuff");

    System.out.println(x);

  }

}

class Child extends Parent {

  static int x = 200;

  public static void doStuff() {

    System.out.println("In Child..doStuff");

    System.out.println(x);

  }

 }

7.  native

·    Can be applied to methods only. (static methods also)

·    Written in a non-Java language, compiled for a single machine target type.

·    Java classes use lot of native methods for performance and for accessing hardware Java is not aware of.

·    Native method signature should be terminated by a ‘;’, curly braces will provide a compiler error.

·    native doesn’t affect access qualifiers. Native methods can be private.

·    abstract can appear with native declaration. This forces the entire class to be abstract.(obviously)

·    Can pass/return Java objects from native methods.

·    System.loadLibrary is used in static initializer code to load native libraries. If the library is not loaded when the static method is called, an UnsatisfiedLinkError is thrown.

8.  transient

·    Can be applied to class level variables only.(Local variables cannot be declared transient)

·    Variables marked transient are never serialized. (Static variables are not serialized anyway.)

·    Not stored as part of object’s persistent state, i.e. not written out during serialization.

·    Can be used for security.

9.  synchronized

·    Can be applied to methods or parts of methods only.

·    Used to control access to critical code in multi-threaded programs.

10.  volatile

·    Can be applied to variables only.

·    Can be applied to static variables.

·    Cannot be applied to final variables.

·    Declaring a variable volatile indicates that it might be modified asynchronously, so that all threads will get the correct value of the variable.

·    Used in multi-processor environments.

Modifier    Class    Inner classes (Except local and anonymous classes)    Variable    Method    Constructor    Free floating Code block

public    Y    Y    Y(not local)    Y    Y    N

protected    N    Y     Y(not local)    Y    Y    N

(friendly)No access modifier    Y    Y (OK for all)    Y    Y    Y    N

private    N    Y    Y(not local)    Y    Y    N

final    Y    Y (Except anonymous classes)    Y    Y    N    N

abstract    Y    Y (Except anonymous classes)    N    Y    N    N

static    N    Y     Y(not local)    Y    N    Y (static initializer)

native    N    N    N    Y    N    N

transient    N    N    Y(not local)    N    N    N

synchronized    N    N    N    Y    N    Y (part of method, also need to specify an object on which a lock should be obtained)

volatile    N    N    Y    N    N    N

Objective 3

For a given class, determine if a default constructor will be created and if so state the prototype of that constructor.

Constructors and Sub-classing

·    Constructors are not inherited as normal methods, they have to be defined in the class itself.

·    Constructors have same name with the class, no return type.  A method with a class name, but with a return type is just a method by compiler. Expect trick questions using this.

·    If you define no constructors at all, then the compiler provides a default constructor with no arguments and same modifier with class.

·    We can’t compile a sub-class if the immediate super-class doesn’t have a no argument default constructor, and sub-class constructors are not calling super or this explicitly (and expect the compiler to insert an implicit super() call )

·    A constructor can call other overloaded constructors by ‘this (arguments)’. If you use this, it must be the first statement in the constructor.

·    A constructor can’t call the same constructor from within. Compiler will say ‘ recursive constructor invocation’

·    A constructor can call the parent class constructor explicitly by using ‘super (arguments)’. If you do this, it must be first the statement in the constructor.

·    Obviously, we can’t use both this and super in the same constructor. If compiler sees a this or super, it won’t insert a default call to super().

·    Constructor body can have an empty return statement.

·    Only modifiers that a constructor can have are the accessibility modifiers.

·    Initializers are used in initialization of objects and classes and to define constants in interfaces. These initializers are :

1.    Static and Instance variable initializer expressions.

Literals and method calls to initialize variables. Static variables can be initialized

only by static method calls.

Cannot pass on the checked exceptions. Must catch and handle them.

2.    Static initializer blocks.

       Used to initialize static variables and load native libraries.

Cannot pass on the checked exceptions. Must catch and handle them.

3.    Instance initializer blocks.

       Used to factor out code that is common to all the constructors.

       Also useful with anonymous classes since they cannot have constructors.

       All constructors must declare the uncaught checked exceptions, if any.

       Instance Initializers in anonymous classes can throw any exception.(?)

·    In all the initializers, forward referencing of variables is not allowed. Forward referencing of methods is allowed.

·    Order of code execution (when creating an object) is a bit tricky.

1.    static variables initialization.

2.    static initializer block execution. (in the order of declaration, if multiple blocks found)

3.    constructor header ( super or this – implicit or explicit )

4.    instance variables initialization / instance initializer block(s) execution

5.    rest of the code in the constructor

Objective 4

State the legal return types for any method given the declarations of all related methods in this or parent classes.

See mynote_6 for overloeading and overridden.

Examples:

Q1.

Assume we have the following code in the file /abc/def/Q.java:

//File: /abc/def/Q.java:

package def;

public class Q {

   private int privateVar;

   int packageVar;

   protected int protectedVar;

   public int publicVar;

}

and this code is in /abc/Sub.java:

//File: /abc/Sub.java:

public class Sub extends Tester {

}

and this code is in /abc/Tester.java:

//File: /abc/Tester.java:

import def.Q;

public class Tester extends Q {

   Q q = new Q();

   Sub sub = new Sub();

   public void someMethod() {

      // First, try to refer to q's memebers.

      q.privateVar = 1;    // compiler error

      q.packageVar = 2;    // compiler error

      q.protectedVar = 3;  // compiler error

      q.publicVar = 4;     // fine

      // Next, try to refer to this object's members

      // supplied by class Q.

      privateVar = 5;        // compiler error

      packageVar = 6;        // compiler error

      protectedVar = 7;      // fine

      publicVar = 8;         // fine

      // Next, let's try to access the members of

      // another instance of Tester.

      Tester t = new Tester();

      t.privateVar = 9;      // compiler error

      t.packageVar = 10;     // compiler error

      t.protectedVar = 11;   // fine

      t.publicVar = 12;      // fine

      // Finally, try to refer to the members in a

      // subclass of Tester.

      sub.privateVar = 13;   // compiler error

      sub.packageVar = 14;   // compiler error

      sub.protectedVar = 15; // fine

      sub.publicVar = 16;    // fine

   }

}

Q2

Determine the result of attempting to compile and run the following code:

class A {

   public A() {

      System.out.println("AAA");

   }

   {

      System.out.println("456");

   }

}

public class B extends A {

   B() {

      this(12);

      System.out.println("BBB");

   }

   B(int x) {

      System.out.println("CCC");

   }

   {

      System.out.println("123");

   }

   public static void main(String[] args) {

      new B();

   }

}

The output is:

456

AAA

123

CCC

BBB

Q3

Determine the result of attempting to compile and run the following code:

class A {

   public int Avar;

   public A() {

      System.out.println("AAA");

      doSomething();

   }

   public void doSomething() {

      Avar = 1111;

      System.out.println("A.doSomething()");

   }

}

public class B extends A {

   public int Bvar = 2222;

   public B() {

      System.out.println("BBB");

      doSomething();

      System.out.println("Avar=" + Avar);

   }

   public void doSomething() {

      System.out.println("Bvar=" + Bvar);

   }

   public static void main(String[] args) {

      new B();

   }

}

The output is:

AAA

Bvar=0

BBB

Bvar=2222

Avar=0

Q4

A class is not abstract if it implements all superinterfaces either directly or inherited

from superclass.

eg. class a { void method() {} }

interface ab { void method(); }

class abc extends a implements ab {} // PERFECTLY LEGAL since superclass has

implemented the interface method signature.


easy! 不好意思， 错按了回车

作者：丑丑牛

日期：00-4-28 16:58:26

主要流程

1。 你要有一个Frame对象

2。调用java.awt.Toolkit.getPrintJob(Frame parent,String jobname,Properties props); 得到PrintJob

3。调用PrintJob.getGraphics() 得到Graphics 实例

4。在Graphics 实例上drawLine(), drawString()， 为所欲为

5。一页打完调用dispose()

6. 打印工作完成， 调用end();

下面抄个例程给你

：

import java.awt.*;

import java.applet.*;

public class PrintHelloWorld extends Applet

{

   public void init()

   {

     Frame aFrame = new Frame();

     PrintJob job = ToolKit.getPrintJob(aFrame,"hello",null);

     Graphics g = job.getGraphics();

     g.drawString("Hello world",50,100);

     g.dispose();

     job.end();

   }

}

注意：

IE的好多安全权限要放开吆


EJB概述(上)

作者：asp2001

email: asp2001@163.net

日期：00-7-4 下午 07:29:04

EJB概述(上)

2000-05-15· ilamei·cpcw

概述：

本节包括了一下主要内容：

EJB的体系结构：介绍了EJB技术的体系结构。

为什么使用EJB容器

EJB的体系结构：

    EJB的上层的分布式应用程序是基于对象组件模型的，低层的事务服务用了API技术。EJB技术简化了用JAVA语言编写的企业应用系统的开发，配置，和执行。EJB的体系结构的规范由Sun Microsystems公司制定。Inprise的EJB容器是基于1.1版的规范。

    EJB技术定义了一组可重用的组件：Enterprise Beans。你可以利用这些组件，象搭积木一样的建立你的分布式应用程序。当你把代码写好之后，这些组件就被组合到特定的文件中去。每个文件有一个或多个Enterprise Beans，在加上一些配置参数。最后，这些Enterprise Beans被配置到一个装了EJB容器的平台上。客户能够通过这些Beans的home接口，定位到某个beans，并产生这个beans的一个实例。这样，客户就能够调用Beans的应用方法和远程接口。

    EJB服务器作为容器和低层平台的桥梁管理着EJB容器和函数。它向EJB容器提供了访问系统服务的能力。例如：数据库的管理和事务的管理，或者对于其它的Enterprise的应用服务器。

    所有的EJB 实例都运行在EJB容器中。容器提供了系统级的服务，控制了EJB的生命周期。因为容器掌握了绝大多数系统级的issues(呵呵，不知道怎么翻译)。Enterprise Beans 的开发者不需要应用逻辑考虑进来。通常来说，EJB容器掌握了以下的系统级issues.

    1、Security--配置描述器（The Deployment descriptor）定义了客户能够访问的不同的应用函数。容器通过只允许授权的客户访问这些函数来达到这个效果。

    2、Remote Connectivity--容器为远程链接管理着低层的通信issues，而且对Enterprise Beas的开发者和客户都隐藏了通信issues.Enterprise Beans的开发者在编写应用方法的时候，就象是在条用本地的平台一样的。客户也不清楚他们调用的方法可能是在远程被处理的。

    3、Life Cycle managment--客户简单的创建一个Enterprise beans的实例，并通常取消一个实例。而容器管理着Enterprise Beans的实例，使Enterprise Beans实现最大的效能和内存利用率。容器能够这样来激活和去活Enterprise Beans，保持众多客户共享的实例池。等等。

    4、Trasction management—配置描述器定义了Enterprise beans 的事务处理的需求。容器管理着那些管理分布式事务处理的复杂的issues。这些事务可能要在不同的平台之间更新数据库。容器使这些事务之间互相独立，互不干扰。保证所有的更新数据库都是成功发生的，否者，就回滚到事务处理之前的状态。

Enterprise Beans 组件：

Enterprise Beans 是基于分布式事务处理的企业级应用程序的组件。所有的Enterprise beans都有如下的特点：

Enterprise beans包含了处理企业数据的应用逻辑。

Enterprise beans provider定义了Enterprise beans的客户界面（Enterprise beans 的远程界面）。这样的界面不受容器和服务器的影响。于是，当一个Enterprise beans被集合到一个应用程序中去时，不用更改代码和重新编译。

Enterprise beans能够被定制

各种系统级的服务，例如安全和事务处理的特性，都不是属于Enterprise beans class的。而是由配置和组装应用程序的工具来实现。

有两种类型的Enterprise beans: Session beans 和 entity beans.

Session beans:

    Session beans 是一种作为单个的client执行的对象。作为对远程的任务请求的相应，容器产生一个Session beans 的实例。一个Session beans有一个client.从某种程度上来说，一个Session bean 对于服务器来说就代表了它的那个client.Session beans 也能用于事务，它能够更新共享的数据，但它不直接描绘这些共享的数据。

    Session beans 的生命周期是相对较短的。典型的是，只有当client保持会话的时候，Session beans 才是活着的。一旦client退出了，Session beans 就不再与client相联系了。Session beans被看成是瞬时的，因为如果容器崩溃了，那么client必须重新建立一个新的Session对象来继续会话。

    一个Session bean典型的声明了与client的互操作或者会话。也就是说，Session bean了在客户会话期间，通过方法的调用，掌握Client的信息。一个具有状态的Session bean称为有状态的Session bean.当client终止与Session beans互操作的时候.会话终止了，而且，bean 也不再拥有状态值。

    一个Session bean也可能是一个无状态的 session bean.无状态的Session beans并不掌握它的客户的信息或者状态。Client能够调用beans的方法来完成一些操作。但是，beans只是在方法调用的时候才知道client的参数变量。当方法调用完成以后，beans并不继续保持这些参数变量。这样，所有的无状态的session beans的实例都是相同的，除非它正在方法调用期间。这样，无状态的Session beans就能够支持多个client.容器能够声明一个无状态的Session beans.能够将任何Session beans指定给任何client.

Entity Beans:

    Entity Beans对数据库中的数据提供了一种对象的视图。例如：一个Entity bean能够模拟数据库表中一行相关的数据。多个client能够共享访问同一个Entity bean.多个client也能够同时的访问同一个Entity bean.Entity beans通过事务的上下文来访问或更新下层的数据。这样，数据的完整性就能够被保证。

    Entity Beans能存活相对教长的时间，并且状态是持续的。只要数据库中的数据存在，Entity beans就一直存活。而不是按照应用程序或者服务进程来说的。即使EJB容器崩溃了，Entity beans也是存活的。Entity Beans生命周期能够被容器或者 Beans自己管理。如果由容器控制着保证 Entity beans持续的issus。如果由Beans自己管理，就必须写Entity beans的代码，包括访问数据库的调用。

    Entity Beans是由主键（primary key 一种唯一的对象标识符）标识的。通常，主键与标识数据库中的一块数据，例如一个表中的一行，的主键是相同的。主键是client能够定位特定的数据块。

EJB roles:

    EJB体系结构通过把编程的过程分为6个不同的角色而使开发复杂的的应用系统变的简单。6个不同的角色，每个都有特定的任务。6个角色包括下层的服务，应用程序的开发，配置和操作说明（issues）

    EJB体系结构能够简化应用程序开发人员的工作。在EJB体系结构中，容器和代理服务承担了很多在其他环境中又程序设计人员承担的工作。这样的代理提供了很多原来需要程序设计人员编写代码的系统级的服务。

Infrastructure roles;（下层构造角色）

    EJB Server Providor服务提供者是典型的提供分布式低层服务的代理。EJB服务提供者提供了一个分布式应用程序开发者需要的平台和设施。也提供了分布式程序的运行时环境。

    EJB Container Providor 容器提供者是分布式系统，事务处理，安全方面的专家容器提供者提供了一个EJB的配置工具，和这些配置实例的运行时支持。

    一个容器对一个或多个EJB来说，是一个运行时系统。它将Enterprise Beans和EJB服务，包括事务服务，安全服务，分布式网络管理等胶合在一起。容器是既是一个定制的代码和对特定的 Enterprise beans产生详细代码的工具。容器也提供了一个配置Enterprise beans的工具和管理监视应用程序的方法。


EJB概述(下)

此电子文档属C#乐园QQ群3256045整理。
EJB概述(下)

上一页 目录 下一页

EJB概述(下)

作者：asp2001

email: asp2001@163.net

日期：00-7-4 下午 07:30:27

EJB概述(下)

2000-05-15· ilamei·cpcw

Application roles：（应用角色）

    Enterprise beans Provider:Beans提供者在应用领域方面是专家。例如：Beans提供者可能是金融或电信方面的专家。Beans提供者提供了业务tasks，Enterprise beans的业务方法，定义了beans的romote和home接口，还定义了beans的配置描述器。因为容器管理着系统级的任务，所以，Beans提供者便不需要关心分布处理，事务处理，安全性能等从应用方面看来没什么关系的方面。

Application Assembler程序组装器:

    这是一个将定制的Enterprise beans和其它的组件，例如，GUI client,applet,Servelet等组装成一个完整的应用程序的行家。在Enterprise的配置器下，组装器在组装成一个应用程序的时候，只关心beans的接口，包括romote接口和home接口。而不关心这些接口是怎么实现的。组装的结果就是一个一个的应用程序，或者由多个Enterprise beans组合成的一个比较复杂的Enterprise beans.

Deployment and Operate roles配置和操作角色

    配置器为特定的Enterprise beans配置特殊的操作环境。配置器为了适应应用程序，将多个Enerprise beans 组合起来，通过修改Enterprise beans 的属性来达到配置相应的操作环境。例如，配置器通过设置配置描述符的相应的属性来设置事务和安全方针。配置器的另一个任务是将应用程序与现有的Enterprise管理软件结合起来。

System Administrator系统管理者；

    是与配置应用有关的。管理者配置个管理Enterprise计算和网络服务的低层，包括EJB服务和容器。管理员监视着应用服务器的运行状况，当应用服务器不正常运行是就采取相应的措施。典型的说，管理员使用Enterprise管理工具来与应用程序打交道，通过由容器提供的hooks来配置。

    传统的应用程序开发者现在是Enterprise beans的提供者。也可能是应用程序的装配者。这样能使他们将主要精力放在应用问题或者应用逻辑上面。当安装Enterprise beans时，配置器定义了一系列的配置方针。机制的复杂性和执行配置的方针就交给了专门的代理。虽然分布式的应用程序还是比较复杂，可是，应用程序设计者的工作却变的简单了，因为所有复杂的工作都交给了EJB服务器和容器提供者了。

    通过大量的设计之前的定义和命名约定，EJB达到了前面提到的目的。这样的编程体系结构限制了一定的自由，但是，允许EJB服务器和容器提供者假定应用程序的设计，并以一种行之有效的方式来支持他们。

EJB的设计模式和命名约定：

    有三种途径来建立一个基于对象的，多层的，分布式的系统：无状态服务的途径，基于会话的途径和持续对象的途径。

    无状态的服务是通过对象的操作提供一种功能的函数，但是，不保持会话的状态。当一个客户使用无状态的对象时，客户不能够提供上一次操作的信息。

    基于会话的设计产生了一个中间层的对象，称为一个会话（Session）。这个会话可以看成是这个客户的代理。典型的，会话的生命是由客户和所在的服务程序决定的。客户如果完成了会话就可以将对象移走。如果服务终止了，会话对象就会超时。会话对象就会变的无效了。

    持续对象设计模式绑定了存在数据库中的一块数据。提供了操作这块数据的一些个操作。持续对象是由多个客户共享的。起生命期是由存储数据的库所决定的。

    EJB规范中将这些分别称做：stateless Session, stateful Session, Entiry Beans.Session Beans模式就是基于会话的设计模式。Entiry Beans模式就是持续对象设计模式。每种模式都定义了一些接口和命名约定。

Enterprise JavaBeans infrastruct （EJB的基础结构）

    容器和服务提供者实现了EJB的基础构造。这些基础构造处理了EJB的分布式方面，事务管理，安全性方面。EJB规范定义了基础构造和J ava API的为了适应各种情况的要求。而没有去指定用什么技术，平台，协议来实现他们。

    Enterprise beans, container, server说明了EJB基础结构的要求，EJB基础结构必须提供客户和Enterprise beans通信的通道。虽然这不是EJB规范所定义的。但保证通道的安全也是很重要的。特别是当客户通过internet访问远程的Enterprise beans时。EJB基础结构也必须能够加强Enterprise beans 的访问控制。

    Enterprise beans所有的数据的持续性必须被保证。EJB的低层结构必须提供在企业级的计算环境中与现存系统的结合能力。Beans的所有通信都是分布式事务的一部分，需要由EJB低层结构来管理对于一个成功的配置，EJB低层必须提供分布式应用管理工具的钩子（hooks）

Container

    容器可能是EJB里面最重要的概念。因为它向程序开发人员提供了最大程度的方便。基于对象的中间层软件平台如CORBA或者远过程调用RMI是程序开发者从网络传输中解放出来，通过提供对象定位机制，数据重组机制等等。容器的概念则走的更远一点。通过简化分布式应用程序的那些复杂的方面，如：安全性，事务协调性，数据持久性。

    一旦Enterprise Beans准备好了可以配置了。就被打包成一个标准的JAVA文件，ejb-jar文件。一个ejb-jar文件可以包括一个或多个的Enterprise Beans。对每一个Enterprise beans，都由它的接口，类，和配置描述器。

Enterprise Beans Provider提供如下东西给ejb-jar 文件：

Enterprise Beans 的remote 接口，接口说明了Client端能调用的函数。

Enterprise Beans 的home 接口，home接口使客户生成和定位一个Enterprise Beans的实例。

Enterprise 类本身，实现了Enterprise beans 的语义。

Deploy Descripot配置描述器，deployer通过利用配置描述器来提供没有在代码中申明的信息。例如方针和属性。EJB的1.0规范把配置描述器定义为javax.ejb.deployment. SessionDescriptor类或者javax.ejb.deployment.EntityDescriptor的一个实例，但是在1.1版本中不赞成这样做。而是用XML来定义配置描述器。

    配置一个Enterprise Beans 意味着将一个Enterprise Bean安装到容器中去。安装过程包括了如下几个方面：

保证不同的Enterprise beans能够工作在一起。

用名字服务来注册一个Enterprise Bean

通过EJB服务的通信系统提供访问EJB的入口。

执行事务管理和安全方针。

    在一个EJB容器中可以又任意多个Enterprise beans.除了安装和执行以外，容器也提供了配置Enterprise beans的工具。

    为什么用Inprise的EJB 容器？

    Inprise EJB容器提供的完全的服务

完全支持EJB1。1规范。

一个有效的运行和管理EJBs的容器。

提供命名服务，事务服务，植入Java RDBMS的内部。编程，配置简单。

充分的例子来演示怎样使用EJBs和EJB容器。

    对于Enterprise beans的开发者来说，EJB 容器是一个高级Enterprise类产品。因为它有如下优点：

完整的，灵活的运行时环境：

EJB容器完全实现了EJB1。1的规范，包括了它的所有功能。

每一个由工具产生的Enterprise bean都同时是Enterprise bean 对象和CORBA对象。

EJB容器能够进行单独配置，100%的纯JAVA服务，或者作为完全的分布式的配置。这样的灵活性意味着你能够根据你的要求调整你的应用程序。

与其它的EJB服务不一样，Inprise 的EJB容器服务并没有限制一定要分开。而是任意个数的Enterprise beans可以放在任意个数的容器中，这些容器也可以放在任意个数机器上。并且支持分布式的事务协议，允许Enterprise beans的分别裁定。允许部分的配置你的应用，而将你的性能配置成最优化。

Inprise 的EJB是建立在Vbroker和RMI—IIOP之上的。

EJB容器是建立在Inprise的Vbbroker之上的。VisBroker是已经被证明的在各种条件下都是ORB的领导角色。无论在各大公司的联合测试下，或者在实际的应用环境中。VisiBroker提供了一个很好的的运行时的任务的完成。

为了更好的性能，Vbroker使用了大量的运行时优化，高级的多路链接，链接池和管理，线程池和管理。

客户和Enterprise beans,Enterprise benas之间，Enterprise beans 和其它的CORBA对象之间的通信都是通过用VisiBroker的IIOP方式进行的。VisiBroker完全适应CORBA2。3规范，RMI—IIOP通过值传递的方式来实现。这意味着，复杂的JAVA数据类型能够用新的IDL值类型写到IIOP里面去。就象在CORBA2。3规范中一样。数据和服务之间相互协调怎样传送复杂的数据类型是很重要的。否则，协同能力就会受到危及。EJB容器能够与其他的所有支持RMI-over-IIOP的进行协同工作。

安全特性是可由VisiBroker所有的。保证信用是从客户传象服务。

事务上下文也是由visibroker所有的。保证当CORBA客户开始一个事务时，并且访问EJB容器的服务时。事务上下文被传向服务，当服务需要调用环境中的各项资源时，就要使用事务上下文。

事务的两段提交是由Inprise的ITS管理的。如果JDBC支持两阶段提交协议，EJB就支持两阶段提交协议。当时，如果JDBC不支持两阶段提交，两阶段提交就不能被完成。

Enterprise的EJB容器是一个CORBA对象：

    Inprise 的java2iiop编译器和EJB容器一样，都是与CORBA兼容的。EJB容器能够理解RMI调用，但是，它用IDL来存放接口定义。虽然Java2iiop编译器从java接口产生了stub 和skeleton.也能够从java接口中产生IDL。然后在其他语言里面用。对于一个CORBA客户端，EJB容器是一个CORBA服务，EJB容器工具就是与控制EJB的功能相同的CORBA工具。

EJB容器是建立在JNDI和CosNaming，JTS/OTS基础山的。他们都是完全支持CORBA的。

容器对不同的Enterprise beans 的支持：

    EJB容器的主要的特征就是它提供对各种各样的Enterprise beans的管理和支持。包括：

有状态或无状态的Session Enterprise beans.

Entiry beans包括container-manager和beans-manager的持续性。

    EJB容器可以包括一个或多个的jar文件。每一个jar文件又能够包括多个的Enterprise beans。每个Enterprise bean都包括一个配置描述器。一个EJBhome接口，一个EJBremote接口，还有Enterprise bean的实现代码。

使Inprise 的EJB容器不同的是：它支持许多当前的客户，包括如下两个方面：

VisiBroler提供了连接管理，允许一个服务同时支持比TCP连接数多的客户连接。用最近最少使用算法来关闭连接。

对于客户来说，容器本身是无状态的。意味着容器并没有为每一个新的Enterprise bean会话分配内存。这样，容器就能够支持任意多的无状态的Session Enterprise beans.

配置的支持：

    Inprise 容器提供了产生代码的编译器。也提供了一个验证的工具来在配置之前检查你的Enterprise beans.支持EJB1。1的JNDI命名基于XML的配置描述器。

事务管理者：

    事务管理者提供了事务管理的手段。包括分布式的两段提交的支持。遵从最新的SUN公司JTS规范。也支持最新的OMG的OTS规范。在开发时尽量少用JTS而在配置时使用健壮的ITS。

JDBC链接池和事务综合：

    所有JDBC访问数据库都是通过DataSource对象的实现来完成的。DataSource对象与数据库的池链接还有JTS管理的全局的事务处理和用JDBC管理的数据库事务处理。

命名服务：

    命名服务提供了分布式的命名管理。服务遵从JAVA的命名和目录规范（JNDS）。也遵从OMG的cosNaming规范。与事务服务类似，在配置的时候，你可以用JNS，也可以用更健壮的INS。JNDI提供的容器能够运行在其他的JNDI实现之上。这样就能够使用其他的命名服务，例如：LDAP。

安全性的支持：

    在CORBA之上的Inprise 的安全服务是附加的。

JAVA数据库：

    Java数据库提供了一个全关系型的高性能的数据库来存储Enterprise beans的状态。

    JAVA数据库既可以运行在同一个进程的容器中，或者，为了提高性能，也可以运行在一个单独的进程中。实际上，JAVA 数据库是即插即用的。意味着终端用户能够用一个另外的实现就取代了所有的JAVA版本。例如，能够实现一个用别的数据库来存储会话状态和Entiry Data的后台存储。

容器管理的Entity beans的持久性：

    Inprise 提供了一个CMP（Container-Managed Persistence）引擎来提供透明的对象关系映射和在纯JDBC上的持续框架。更进一步，第三方的工具可以通过开放的API插进来。

与其他组件的结合：

    在配置环境的真实世界里，EJB的四个模式能够与以下的组件工作在一起：

client链接到Enterprise beans对象。Clients可以由java Enterprise bean接口的EJB对象，或者IDL接口的CORBA对象。在Inprise 的EJB容器服务中，每一个Enterprise bean都同时是EJB对象也是CORBA对象。CORBA客户能够用所有支持CORBA的语言来实现，包括C++，Java,Delphi.

从Enterprise bean的数据库访问。典型的，Enterprise beans对象是Entity beans对象，或者是beans-managed,或者是container-managed.

后端服务：CORBA服务，用C++，Java ,Delphi实现，运行在任何兼容CORBA的ORB中。EJB服务，包括所有其他的inprise的服务，或者其他支持IIOP的代理商的服务。其它原来的服务，包括ERP系统，main-frame程序。等等。


EJB内部资参1

此电子文档属C#乐园QQ群3256045整理。
EJB内部资参1

上一页 目录 下一页

EJB内部资参1

作者：jeru

email: jeru@163.net

日期：2001-1-12 18:09:27

enterpriseJavaBean 入门

一 基本知识

1． 背景

Java由于其良好的跨平台行而成为服务器端的理想语言，为了利用Java实现服务

器端的计算，SUN推出了一个完整的开发平台J2EE，其目的是为基于Java的服务器

端配置提供一个独立于平台的可携带的多用户企业级的安全平台，而J2EE的基石

就是Enterprise JavaBeans(EJB),EJB是建立基于JAVA的服务器端组件的标准，它

定义了如何编写服务器端组件，提供了组件与管理组件的应用服务器之间的标准

约定，EJB是一种组件架构，使得开发人员能够快速开发出具有伸缩性的企业级应

用。

2． EJB简介

1）JavaBeans和Enterprise JavaBeans

JavaBeans是Java的组件模型。在JavaBeans规范中定义了事件和属性等特征。En

terprise JavaBeans也定义了一个Java组件模型，但是Enterprise JavaBeans组

件模型和JavaBeans组件模型是不同的。 JavaBeans重点是允许开发者在开发工具

中可视化的操纵组件。JavaBeans规范详细地解释了组件间事件登记、传递、识别

和属性使用、定制和持久化的应用编程接口和语意。 Enterprise JavaBeans的侧

重点是详细地定义了一个可以portably地部署Java组件的服务框架模型。因此，

其中并没提及事件，因为enterprise bean通常不发送和接受事件。同样也没有提

及属性------属性定制并不是在开发时进行，而是在运行时（实际上在部署时）

通过一个部署描述符来描述。

不要寻找JavaBeans和Enterprise JavaBeans之间的相似性。他们都是组件模型规

范，但是前者说明了开发工具中应用程序组装的问题，而后者则侧重于部署组件

的服务框架的细节。不要错误地认为JavaBeans是用于客户端的开发，Enterpris

e JavaBeans是用于服务器端的开发。JavaBeans也可作为进行非图形化服务器端

Java应用开发的组件模型。区别是当你使用JavaBeans创建服务器应用时，你还得

设计整个的服务框架。用Enterprise Javabeans框架是现成的，你只需遵守它的

APIs.对于复杂的服务器端应用程序，显然使用Enterprise JavaBeans比重新开发

更简单

2）EJB体系结构

EJB是一种组件架构，它采用分而制之的方法实现服务器端的计算。

EJB规范定义了六种不同的角色来完成其任务， 包括：

? Bean provider: 提供可重用的商业组件

? Container provider: 为ejb 应用提供低层次的运行环境

? Server provider:提供应用程序服务器用以包含，管理和配置ejb组件，目前E

JB规范没有提供EJB Container和EJB Server之间的接口，故二者的提供商是合一

的，有：BEA的Weblogic server,SUN的NetDynamics,IBM的WebSphere,Oracle的8

i等。

? Application assembler:负责将不同的组件组织起来构成能够运行的应用程序

? Deployer: 负责将要使用的组件安装到应用服务器中并加以配置

? System Administrator: 负责配置好的系统的管理等

这六种角色的流程图如下：

Enterprise JavaBean 驻留在EJB Container中，通过EJB Container对ejb进行管

理。EJB规范定义了二者之间的接口。

Javax.ejb.EJBHome接口列出了所有定位、创建、删除EJB 类实例的方法。Home对

象是home接口的实现。EJB类开发者必须定义home接口。EJB Container Provide

r应该提供从home接口中产生home对象实现的方法。

远程接口（remote interface）列出了EJB类中的商业方法。Javax.ejb.EJBObje

ct实现远程接口，并且客户端通过它访问EJB实例的商业方法。EJB开发者定义远

程接口，EJB Container Provider提供产生相应的EJBObject的方法。客户端不能

得到EJB实例的引用，只能得到它的EJBObject实例的引用。当客户端调用一个方

法，EJBObject接受请求并把它传给EJB实例，同时提供进程中必要的包装功能。

客户端应用程序通过home对象来定位、创建、删除EJB类的实例，通过EJBObject

来调用实例中的商业方法。客户端可以用Java来编程，通过Java RMI来访问访问

home对象和EJBObject,或用其他语言编程并通过CORBA/IIOP访问，使得部署的服

务器端组件可以通过CORBA接口来访问。

Enterprise javaBean规范将Enterprise Beans 分为两种：session bean和enti

ty bean.

I. session bean表示的是调用它的客户端代码索要完成的工作，是一种商业处理

过程对象，它实现商业逻辑，商业规则以及工作流程，例如：报价，订单处理，

视频压缩，股票交易等。session bean之所以叫session bean是因为其生命周期

与调用它的客户端相同。

session bean又分为两种：stateless和stateful, stateful session bean用于

贯穿多个方法请求和事务的商业过程，例如：网上商店，用户进入商店后，可以

将商品加入再现的购物车，组件必须跟踪用户的状态（如：购物车）；而statel

ess session bean用于客户调用方法期间不用维护任何状态信息，例如：解决复

杂数学运算的视频压缩/解压缩。

II. entity bean用来代表商业过程中处理的永久性的数据，例如：银行出纳员组

件完成储蓄等商业过程，其中涉及的数据时银行账户数据

entity bean用来代表底层的对象。最常用的是用entity bean代表关系库中的数

据。一个简单的entity bean 可以定义成代表数据库表的一个记录，也就是每一

个实例代表一个特殊的记录。更复杂的entity bean可以代表数据库表间关联视图

。在entity bean中还可以考虑包含厂商的增强功能，如对象--关系映射的集成。

通常用entity类代表一个数据库表比代表多个相关联的表更简单且更有效。反过

来可以轻易地向entity类的定义中增加关联，这样可以最大地复用cache并减小旧

数据的表现。

III. entity bean和session bean的比较

看起来session bean好象没什么用处，尤其对于数据驱动的应用程序。当然事实

并不是这样。因为entity bean（譬如说）代表底层数据库的一行，则entity be

an实例和数据库记录间就是一对一的关系。因为多个客户端必须访问底层记录，

这意味着，不同于session bean，客户端必须共享entity bean。因为是共享的，

所以entity bean不允许保存每个客户端的信息。session bean允许保存客户端的

状态信息，客户端和session bean实例间是一对一的。entity bean允许保存记录

的信息，entity bean实例和记录间是一对一的。一个理想的情况是客户端通过s

ession bean连接服务器，然后session bean通过entity bean访问数据库。这使

得既可以保存客户端的信息又可以保存数据库记录的信息。

同时session bean也不能提供在相同或不同的EJB类调用间进行全局的事务控制。

没有session bean，应用程序开发者（客户端开发者）就必须理解EJB类的事务要

求，并使用客户端的事务划分来提供事务控制。EJB的主要好处就是应用开发者不

需知道EJB类的事务需求。一个session bean可以代表一个商业操作，进行事务控

制，不需要客户端进行事务划分。

以下我们作为EJB provider的角色来讨论Enterprise javaBean的开发。

3,EJB 开发

A) Enterprise JavaBean的由如下部分构成

Enterprise Bean 类： 包含ejb 组件的详细实现，对于session Bean它代表了

与业务处理相关的逻辑的实现，而对于entity Bean 它代表了与数据逻辑相关的

实现

EJB Object :是由EJB Container来实现的远程接口，它是客户端和bean 实例之

间的中介，传递客户端调用到bean 实例;

Home 接口：充当EJB Object工厂的java接口，创建EJB Object，Home接口的实现

由EJB Container负责。

Deploy descriptor:描述你的ejb 中间件的相关要求，包括：通知EJB Containe

r如何管理你的bean,事务的需要，安全需要等等。

特定于bean 的属性： 利用该属性，bean可以在运行时读取并调整自己的功能

Ejb-jar 文件：将生成的bean 类，home接口，Ejb object,deployment descrip

tor和bean 的properties 打包进ejb jar 文件，这样就可以deploy了。

B) Enterprise JavaBean编程的具体步骤

实现javax.ejb.EnterpriseBean接口：

对于session bean 可以直接实现javax.ejb.SessionBean接口，对于entity bea

n 可以直接实现javax.ejb.EntityBean接口,而后实现本bean特有的功能。

扩展javax.ejb.EJBObject接口：

增加在Enterprise Bean实现类中供客户调用的方法的相应的方法，必须相同

扩展javax.ejb.EJBHome接口：

增加创建上述扩展的javax.ejb.EJBObject接口的相应的create()方法。

编写deploy descriptor文件：

目前EJB 1.1规范规定了用XML文件来描述它，并提供了相应的DTD.

生成EJB jar 文件：

利用jar命令将上述生成的相应文件进行打包。

在第二部分给出了ejb开发的详细说明及相关注意事项

二 例子程序

以下的例子程序是以weblogic server(ver5.1)作为EJB Server(EJB Container)

的， 不同的EJB Server 在一些方面有所不同 （不同的地方另加注释）

例子一. Hello, world (Stateless Session Bean)

包括EJB相应接口的扩展和实现、deploy descriptor文件的生成以及.jar文件生

成；

服务器端EJB开发

EJB相应接口的扩展和实现：（与EJB Server无关）

第一步： 实现javax.ejb.SessionBean 接口，并提供该Bean的特定功能， "Hel

lo, world" Stateless SessionBean 仅仅向客户端返回“Hello,world!”字符串

注意：除了要实现javax.ejb.SessionBean接口外（ejbActivate(),ejbPassivat

e(), ejbRemove(), setSessionContext()），必须：

1）．实现公共方法ejbCreate(),该方法对于stateless SessionBean 无需带任何

参数而对stateful SessionBean则根据需要自定；

2）提供给客户调用的方法， 如：本例的hello()方法

package com.jsper.ejbexample.session.helloworld;

import javax.ejb.*;

import java.rmi.RemoteException;

public class HelloBean implements SessionBean {

public HelloBean() {

super();

}

/**

*由EJB Server（EJB Container） 调用，用于激活SessionBean的实例

*/

public void ejbActivate() throws EJBException, java.rmi.RemoteExceptio

n

{

System.out.println("ejbActivate()");

}

/**

*由EJB Server（EJB Container） 调用，用于创建本SessionBean的实例

* 创建日期：(2000-8-1 16:00:45)

*/

public void ejbCreate() throws javax.ejb.CreateException

{

System.out.println("ejbCreate()");

}

/**

* 由EJB Server（EJB Container） 调用，用于惰化SessionBean的实例

*/

public void ejbPassivate() throws EJBException, java.rmi.RemoteExcepti

on

{

System.out.println("ejbPassivate()");

}

/**

* 由EJB Server（EJB Container） 调用，用于删除SessionBean的实例

*/

public void ejbRemove() throws EJBException, java.rmi.RemoteException

{

System.out.println("ejbRemove()");

}

/**

* 本SessionBean提供给客户调用的方法，但不能由客户直接调用，而是通过Ejb

Object远程接口调用

* 创建日期：(2000-8-1 16:04:58)

* @return java.lang.String

*/

public String hello() {

return "Hello, World!";

}

/**

* 由EJB Server（EJB Container） 调用，用于设置SessionBean的上下文

*/

public void setSessionContext(SessionContext arg1) throws EJBException

, java.rmi.RemoteException

{

System.out.println("setSessionContext()");

}

}

第二步：扩展javax.ejb.EJBObject接口，增加在第一步SessionBean 接口实现中

提供给客户调用 的方法， 如本例的hello()方法

注意：1）增加的方法名称必须与相应的SessionBean实现中方法相同（case-sen

sitive）;

2) 增加的方法必须抛出java.rmi.RemoteException异常

package com.jsper.ejbexample.session.helloworld;

import java.rmi.*;

import javax.ejb.*;

public interface Hello extends EJBObject {

/**

* 由客户端直接调用，返回“hello,world!”,实现由相应的SessionBean完成

* 创建日期：(2000-8-1 15:56:25)

* @return java.lang.String

* @exception java.rmi.RemoteException 异常说明。

*/

String hello() throws java.rmi.RemoteException;

}

第三步：扩展javax.ejb.EJBHome接口,增加创建EJBHome接口对象的create()方法

注意： 1）create()方法中参数的有无无关紧要，但必须与SessionBean接口实现

类中ejbCreate()方法相对应，包括从方法的个数上和参数上;例如：若SessionB

ean接口实现类中有ejbCreate(),ejbCreate(int nVal)方法，则该接口扩展中必

须增加create(),create(int nVal)方法

2）create()方法必须抛出javax.ejb.CreateException 和java.rmi.RemoteExce

ption 异常

package com.jsper.ejbexample.session.helloworld;

import java.rmi.*;

import javax.ejb.*;

public interface HelloHome extends EJBHome {

/**

* 创建EJBObject扩展对象Hello

* 创建日期：(2000-8-1 16:13:53)

* @return com.jsper.ejbexample.session.helloworld.Hello

* @exception javax.ejb.CreateException 异常说明。

* @exception java.rmi.RemoteException 异常说明。

*/

Hello create() throws javax.ejb.CreateException, java.rmi.RemoteExcept

ion;

}

? deploy descriptor文件的生成

EJB1.1规范规定了必须利用xml文件构造deploy descriptor文件，并给出了相应

的DTD。本例的ejb-jar.xml文件如下：

images/green-cube.gif

images/orange-cube.gif

helloworld

com.jsper.ejbexample.session.helloworld.HelloHome

com.jsper.ejbexample.session.helloworld.Hello

com.jsper.ejbexample.session.helloworld.HelloBean

Stateless

Container

  BEAS

  java.lang.Double

  100.0

  MSFT

  java.lang.Double

  150.0

  helloworld

  Remote

  *

Required

对于weblogic Server还必须构造一个名为weblogic-ejb-jar.xml的文件，BEA公

司给出了相应的DTD.它描述了相应的EJB的名称以及JNDI的名称（**该部分与EJB

Server有关，ejb开发人员可省去，而由DEPLOYER来完成**）

      helloworld

100

      hello.HelloHome

? 提供用于标示本EJB的大小图标（icon）,注意：图像的格式必须是JPEG 或 GI

F。

本例提供了图像文件：orange-cube.gif和green-cube.gif(见附件)

? 构造.jar文件

第一步：编译相应的.java文件

本例中包括：Hello.java,HelloBean.java,HelloHome.java ，位于包com.jsper

.ejbexample.session.helloworld中。编译后相应的.class文件放在相对目录co

m\jsper\ejbexample\session\helloworld 中。

第二步：创建与com的同级目录META-INF,将相应的.xml文件拷贝到该目录下;将相

应的图标文件拷贝到相对目录（相对于该.xml文件父目录）中（本例为与com的同

级目录images）

第三步：利用JAR命令将目录com,images,META-INF中的内容做成.jar文件

本例JAR文件名成为std_hello.jar

jar cv0f std_hello.jar META-INF com images

至此， 对于EJB Provider的工作已经完成。

但是由于我们的开发人员在开发ejb的时候需要进行本地调试等工作，因此其既充

当ejb provider的角色，又充当其他的脚色， 因此，其可能需要作如下工作（仅

供参考，接上述工作）：

? 生成可deployable的jar文件

前边生成的jar文件（若不含weblogic-ejb-jar.xml文件）是不依赖于ejb serve

r（ejb container）的，而如前所述，不同的ejb server对于ejb的配置是不同的

，因此需要加上特定的信息

对于weblogic server则是通过相应的ejbc命令或deploy utility在原.jar文件的

基础上生成新的可deployable的.jar文件。(关于ejbc的详细使用请参见有关资料

)：

java –classpath e:\weblogic\classes;e:\weblogic\lib\weblogicaux.jar

-Dweblogic.home=e:\weblogic weblogic.ejbc -compiler javac std_hello.ja

r ejb_helloworld.jar

? 设定weblogic的环境

将最终的jar文件(本例ejb_helloworld.jar)放在weblogic的某一子目录下（例如

：myserver），而后将.jar文件所在的路径（ e:/weblogic/myserver/ ejb_hel

loworld.jar）添加到weblogic.properties文件的入口weblogic.ejb.deploy中（

weblogic.ejb.deploy=e:/weblogic/myserver/ejb_helloworld.jar）

? 启动weblogic server, 在其启动的控制台中可以见到相应的ejb的装载成功的

信息。

? 客户端调用


EJB内部资参2

作者：jeru

email: jeru@163.net

日期：2001-1-12 18:10:03

使用EJB你就不用你自己写支持分布式的对象的框架了

Java Beans是个功能的部件，而不是一个可运行的程序， 不需要也不能发布它，

但它又必须有一个可依赖的环境来运行

EJB则是可发布的部件、发布到一个容器中， 装配成更大的系统

EJB和Applet和Servlet相似， Applet的容器是Browser, Servlet的容器是

支持Java的Web Server， 而EJB的容器是Application Server

EJB1.1　 (2.0的规范已经发布了，请到sun去download)

软件生产工业化

多层应用简单化

事务处理

并发安全

Enterprise beans当前两个类型(2.0中有新内容， 我自己还没理解，不多讲了)

Session Beans(又包括stateful 和stateless) 业务过程相关的逻辑，比如各种

计算，查询

entity beans 数据相关的逻辑， 比如修改帐号的余额

EJB的规范定义了一些你的Bean可以实现标准的接口。 这些接口强迫你实现其中

特定方法。EJB的容器使用这些方法来管理你的Bean以及传递事件

最基本的接口javax.ejb.EnterpriseBean

public interface javax.ejb.EnterpriseBean extends java.io.Serializable

{}

这个接口中没有方法，起到标示你的Bean是个Enterprise bean.

sesssion beans 和entity beans有许多接口都是从这个特定的接口继承来的（所

以你的bean并不直接实现EnterpriseBean接口）。所有的session beans都从jav

ax.ejb.SessionBean继承，同理所有的EntityBean都从javax.ejb.EntityBean继

承。

remote object 由MiddleSoftware提供的工具生成

remote interface 远程接口---客户端的应用调用的接口

java.ejb.EJBObject

public interface EJBObject extends java.rmi.Remote {

public abstract javax.ejb.EJBHome getEJBHome() throws java.rmi.RemoteE

xception;

public abstract javax.ejb.Handle getHandle() throws java.rmi.RemoteExc

eption;

//一个EJB的持续引用， 存储起来重新构造

public abstract java.lang.Object getPrimaryKey() throws java.rmi.Remot

eException;

//只用在Entity Beans

public abstract boolean isIdentical(javax.ejb.EJBObject param1) throws

java.rmi.RemoteException;

public abstract void remove() throws java.rmi.RemoteException, javax.e

jb.RemoveException;

}

home object --　EJB object factory, 由工具生成，是EJB container的一部分

创建EJB objects

发现已经存在的EJB objects (for entity beans)

删除EJB objects

home interface --本地接口,

定义创建的方法，查找的方法和析构的方法

package javax.ejb;

import java.rmi.Remote;

import java.rmi.RemoteException;

// Referenced classes of package javax.ejb:

//　　　　　　RemoveException, EJBMetaData, HomeHandle, Handle

public interface EJBHome

    extends Remote

{

    public abstract EJBMetaData getEJBMetaData()

        throws RemoteException;

    public abstract HomeHandle getHomeHandle()

        throws RemoteException;

    public abstract void remove(Object obj)

        throws RemoteException, RemoveException;

    public abstract void remove(Handle handle)

        throws RemoteException, RemoveException;

}

session bean

package javax.ejb;

import java.rmi.RemoteException;

// Referenced classes of package javax.ejb:

//　　　　　　EJBException, EnterpriseBean, SessionContext

public interface SessionBean

    extends EnterpriseBean

{

    public abstract void ejbActivate()

        throws EJBException, RemoteException;

    public abstract void ejbPassivate()

        throws EJBException, RemoteException;

    public abstract void ejbRemove()

        throws EJBException, RemoteException;

    public abstract void setSessionContext(SessionContext sessionconte

xt)

        throws EJBException, RemoteException;

}

setSessionContext(SessionContext sessioncontext)

session context 是session bean 和container交互的通道， 通常的实现:

import javax.ejb.*;

public class MyBean implements SessionBean

{

private SessionBean sessiontext;

public void setSessionContext(SessionContext sessioncontext)

         throws EJBException, RemoteException

        {

         this.sessiontext = sessiontext;

        }

        ......

}

public void ejbCreate(...)

至少实现一个

home object实现相应参数的一个create方法

比如

你的bean中有一个ejbCreate(int i)时

home object中有

public void create(int i)

钝化和激活 ---仅用于stateful session bean

    public abstract void ejbPassivate()

    public abstract void ejbActivate()

当太多的session bean被事例化时,container做钝化和激活操作， 释放和打开资

源

//stateless session bean

对于所有的客户端是相同的，所有的信息通过参数传递或从数据库等外部得到

初始化的唯一方式是无参数的 ejbCreate()方法

home object 有相应的无参数create（）方法

客户端调用过程：

1、Look up a home object.

2、Use the home object to create an EJB object.

3、Call business methods on the EJB object.

4、Remove the EJB object.

Look up a home object

your client code must use the JNDI. J2EE products exploit directory se

rvices to stroe location infromation for resources that your applicati

on code uses in an enterprise deployment. These resources could be EJB

home objects, enterprise bean enviroment properties, database deriver

s, message service drivers, and other resources. By using directory se

rvices, you can writer application code that does not depend on specif

ic machine names or locations. This is all part of EJB's location tran

sparency, and it keeps your code portable. If later you decide thata r

esources should be located elsewhere, your code will not need to be re

built because the directory service can simply be updated to reflect t

he new resource locations. This greatly enhances maintenance of a mult

i-tier deployment that may evolve over time.

There are two common steps that must be taken to find any resource in

a J2EE deployment:

1. Associate the resource with a "nickname" in your deployment descrip

tor. Your J2EE product will bind the nickname to the resource.

2. Clients of the resource can use the nickname with JNDI to look up t

he resource across a deployment.

目前的主要的分布式应用框架

1、 Miscrosoft's Distribute interNet Appplications Architecture(DNA)

相关的平台和技术

NT

DCOM

MSMQ

MTS

Microsoft Wolfpack

Microsoft SQL Server

Microsoft Internet Information Server

Microsoft Management Console

2、SUN's J2EE

J2EE是规范而不是产品， 不至于让用户绑定到一个卖家（Microsoft）

支持高端的Unix平台

内置的CORBA支持

3、The Object Management Group's CORBA Standard

Common Object Request Broker Architecture (CORBA)

Internet Inter-ORB Protocol (IIOP)


EJB内部资参3

此电子文档属C#乐园QQ群3256045整理。
EJB内部资参3

上一页 目录 下一页

EJB内部资参3

作者：jeru

email: jeru@163.net

日期：2001-1-12 18:10:41

EJB开发概述

1、EJB的开发

先泛泛而论，讲一讲EJB的开发步骤。

1.1 SessionBean的开发

第一步， 写远程接口（remote interface）,

继承EJBObject接口，把需要调用的public方法写在里面(这些方法将在SessionB

ean中实现)，注意要声明throws java.rmi.RemoteException。

例如：

package jsper.ejb;

import java.rmi.*;

import javax.ejb.*;

public interface MyEJB extends EJBObject

{

public String sayHello() throws java.rmi.RemoteException;

}

第二步， 写Home接口（生成EJBObject引用的factory)

至少生成一个create方法, 注意要声明throws java.rmi.RemoteException和jav

ax.ejb.CreateException。

比如：

package jsper.ejb;

import java.rmi.*;

import javax.ejb.*;

public interface MyEJBHome extends EJBHome

{

MyEJB create() throws java.rmi.RemoteException, javax.ejb.CreateExcept

ion;

}

第三步， 写真正的Session Bean的实现(实现定义在远程接口中的方法)， 需要

实现javax.ejb.SessionBean接口

注意：不能用implents MyEJB的方式直接实现远程接口,此处不用抛出RemoteExc

eption

package jsper.ejb;

import java.rmi.RemoteException;

import javax.ejb.*;

public class MyEJBClass implements SessionBean {

  public MyEJBClass() {

  }

  //定义在SessionBean 中的方法

  public void ejbCreate() throws RemoteException, CreateException {

  }

  public void ejbActivate() throws RemoteException {

  }

  public void ejbPassivate() throws RemoteException {

  }

  public void ejbRemove() throws RemoteException {

  }

public void setSessionContext(SessionContext ctx)

throws RemoteException {

  }

  //此处是具体的实现

  public String sayHello()

  {

    System.out.println("Hello");

  }

}

第四步，写一个发布用的配置文件ejb-jar.xml

需要提供的信息：

Bean Home name -- The nickname that clients use to lookup your bean's

home object.

Enterprise bean class name -- The fully qualified name of the enterpri

se bean class.

   Home interface class name

   Remote interface class name

Re-entrant -- Whether the enterprise bean allow re-entrant calls. This

setting must be false for session beans(it applies to entity beans on

ly)

   stateful or stateless

   Session timeout --　The length of time (in seconds) before a client

should time out when calling methods on your bean.

最后你还可以提供属于自己的配置信息供自己控制EJB的工作方式。

例子：

helloEjb

com.jsper.ejb.MyEJBHome

com.jsper.ejb.MyEJB

com.jsper.ejb.MyEJBClass

Stateless

Container

第五步，将你的所有文件用jar工具生成jar文件

ejb-jar.xml须在顶级的META-INF子目录

这句话比较咬嘴， 举个例子

mylib----META-INF--*.XML

|

|com--coucouniu--ejb---EJBClass

      |-EJBHome

      |-EJB

在生成.jar文件时

sh>cd mylib　　//注意此处所在目录

sh>jar cv0f myejb.jar *

请注意： 到这一步我们做出的东西都是和和特定的EJB Server是无关的， 只是

和遵循EJB的标准有关

第六步，使用特定平台的发布工具生成发布使用的jar文件。

不同的中间件产品此步骤非常不同， 产生的结果都是生成只有自己的EJB Serve

r能理解的远程接口和Home接口实现等等东西，打包在一个jar文件中

一般是很简单的

第七步，把.jar文件发布到EJB Server

根据不同的中间件产品此步骤非常不同， 可以分为启动时发布和运行时发布两种

，一般是很简单的, 以weblogic为例：

1、在weblogic.properties 文件中配置使weblogic 启动时自动装载。

添加一个条目比如：

weblogic.ejb.deploy=C:/weblogic510/myserver/ejb_basic_beanManaged.jar，

\

    C:/weblogic510/myserver/ejb_basic_test.jar

2、使用deploy或DeployerTool动态装载/卸载/更新

第八步，写客户端的程序（我迄今为止的理解）

在我们使用发布工具把EJB发布到EJB Container的过程中，会绑定一个名字到Co

ntainer的目录服务中，现在我们要调用时从这个目录服务中把EJBHome对象取出

， 这里分为从本地和外部两种情况：

一种是客户端本地调用EJB。 比如和EJB引擎和Servlet引擎是整合在同一个Appl

ication Server中， 这时当一个Servlet要调用EJB时无须验证，即可得到EJBHo

me接口的实现

  Context ic = new InitialContext();

      System.out.println("Looking for the EJB published as 'hello'");

      com.jsper.ejb.MyEJBHome homeInterface = (com.jsper.ejb.MyEJBHome

) ic.lookup(“hello”); //发布时绑定的名字是hello

  这样就可从目录服务中得到Home接口的实现， 也是我们最常用的方式， 可移

植性很好

外部调用的话首先要经过身份验证，

比如Oracle8i ：

String ejbUrl = "sess_iiop://localhost:2481:ORCL/test/MyEJB";

    String username = "scott";

    String password = "tiger";

    // Setup the environment

    Hashtable environment = new Hashtable();

    // Tell JNDI to speak sess_iiop

    environment.put(javax.naming.Context.URL_PKG_PREFIXES, "oracle.aur

ora.jndi");

    // Tell sess_iiop who the user is

    environment.put(Context.SECURITY_PRINCIPAL, username);

    // Tell sess_iiop what the password is

    environment.put(Context.SECURITY_CREDENTIALS, password);

    // Tell sess_iiop to use credential authentication

environment.put(Context.SECURITY_AUTHENTICATION, ServiceCtx.NON_SSL_LO

GIN);

// Lookup the URL

    com.jsper.ejb.MyEJBHome homeInterface = null;

    try {

      System.out.println("Creating an initial context");

      Context ic = new InitialContext(environment);

      System.out.println("Looking for the EJB published as 'test/MyEJB

'");

      homeInterface = (com.jsper.ejb.MyEJBHome) ic.lookup(ejbUrl);

    }

    catch (ActivationException e) {

      System.out.println("Unable to activate : " + e.getMessage());

      e.printStackTrace();

      System.exit(1);

    }

再比如weblogic的调用方式：

try

{

    // Get an InitialContext

    String url="t3://localhost:7001";

    Properties h = new Properties();

    h.put(Context.INITIAL_CONTEXT_FACTORY,

"weblogic.jndi.WLInitialContextFactory");

    h.put(Context.PROVIDER_URL, url);

Context ctx =　new InitialContext(h);

    System.out.println("Getting the EJBHome object...");

      com.jsper.ejb.EJBHome tmp= (com.jsper.ejb.EJBHome)ctx.lookup("he

llo");

//create three element array of COUNT object

    EJB ejb =tmp.create();

    System.out.println(ejb.sayHello());

}

catch(Exception e)

{

e.printStackTrace();

}

由于和具体的目录服务、协议相关，为了达到可移植的目的，只好多做一些工作

，幸好一般不需要做这些工作。

附图

EJB的调用模型

Passivation of stateful session bean

Activation of stateful session bean


EJB内部资参5

作者：jeru

email: jeru@163.net

日期：2001-1-12 18:11:54

这里讲讲如何使用jdeveloper开发EJB。在此之前请先阅读 ‘EJB开发概述.doc'

由于jDeveloper中提供了一系列的向导（写起文档很麻烦）完成各种功能，兼之

jDeveloper中的帮助说明得很详细，没有另写文档

jDeveloper的文档在

Help->help topics->user's guaides->Developing Applications->Developing

Java Components->Developing and Deploying Enterprise JavaBeans

使用jDeveloper3.0能够开发EJB1.0（可惜不支持EJB1.1）, 并能把EJB发布到Or

acle8i(需要配置成多线/进程服务器)或Oracle Application server/iAs

开发EJB的方式：

通过向导file->new...->Enterprise java bean

1、生成一个新的EJB类框架和Remote interface, Home interface

2、把已经存在的类封装成EJB

3、把已经存在的EJB的class文件封装成EJB

注意：

根据jdeveloper提供的这些功能，我们可以省去写Remote interface 和Home in

terface的工作。

而直接写EJB 实现文件甚至bean文件， 但为了使开发的东西看起来干净一些，建

议不要使用包装bean的方式。

生成Deployment descriptor文件, jDevelper提供一个向导做这件事情， 挺简单

的

在生成EJB的向导中选择可以生成Deplyment descriptor文件， 以后双击生成的

.prf文件向Oracle8i或OAS发布。

或者：

在项目文件夹右击鼠标->New Deployment Profile...

或者：

在EJB文件夹右击鼠标->Create jServer/EJB deployment profile...或create

OAS/EJB deployment profile...

以后就是按照向导的提示一步步进行，不再赘述。

使用jDeveloper开发EJB的总体印象：

优点：

由于是和自己的产品（8i/OAS）整合在一起, jDeveloper开发EJB是一个好的思路

， 速度是很快的(不出错的情况下)， 隐藏了Deploy的大部分细节，大大加快开

发进度。根据。

缺点：

1、8i只支持EJB的1.0规范, 版本有点低。（根据Oracle公司的iAS 白皮书， iA

S的版本2支持EJB1.1）。 而别的产品如weblogic, jrun现在支持的时EJB1.1规范

，这样在向这些平台发布时还有一定的工作量。（1.0中deploy descriper是jav

a类， 在1.1中已改为ejb-jar.xml文件）

3、在8i中没有和servlet和jsp的引擎在本地，这样在web server访问EJB时实际

上是从外部， JNDI访问需要先验证身份， 给客户端servlet程序的移植带来一定

的工作量（OAS没有这个问题）。

2、在有的机器上jDeveloper3.0的connection manager有时连接8i不能成功， 开

发无法进行。具体原因尚未查明。（在程序中能连接成功）

3、在向8i deploy时出错时定位错误困难， 提示往往只是complie failed或dep

loy failed给开发造成困难。

4、稳定性欠佳。较频繁地出现非法访问内存之类的错误

根据以上原因， 可以考虑使用支持EJB1.1规范的iAS作为将来程序开发的平台。

在别的平台做实施只需用相应平台的Deploy tools重新发布EJB.


ejb一则，franzy@163.net多多交流哦

作者：javajava

日期：2000-11-15 19:59:26

什么是 Enterprise JavaBeans 组件？

Ken Nordby

软件工程师，IBM

2000 年 7 月

本文的第 3 部分说明 Enterprise JavaBeans 组件的部署过程，部署并不仅仅是

安装，因为它通常还涉及代码生成。部署还使用了一个特殊的部署描述符文件，

此文件支持控制企业级 bean 行为（如某个 bean 是否需要事务）的参数。bean

部署的这一特性支持 bean 行为的说明性、纲领性规范的 EJB 目标。第 3 部分

还比较了持久性的两种主要类型，bean 管理式持久性和容器管理式持久性，并讨

论了 EJB 组件与 CORBA 的关系。同时还给出了一个简单的三层 EJB 应用程序。

部署过程

Enterprise JavaBeans (EJB) 组件是在称为部署的特定过程中安装的。由容器组

件提供对部署过程的支持。在高级别上，部署由下列步骤组成：

bean 的开发人员创建必需的类文件、接口文件和控制信息。

容器分析输入文件并生成必要的类。

容器将条目添加到指向本地对象的 JNDI 命名空间中。

EJB 组件的开发人员编写 bean 的 Java 源文件，此文件包含为这个 bean 提供

功能的业务逻辑方法，还包括 ejbCreate() 方法。bean 类还必须实现

javax.ejb.SessionBean 接口或 javax.ejb.EntityBean 接口。此外，bean 的开

发人员编写接口文件，定义对 javax.ejb.EJBHome 接口和 javax.ejb.EJBObject

接口的扩展。EJBHome 接口的扩展，称为 bean 的本地接口，包含一个创建方法

，并且如果 bean 是一个实体 bean，它还会包含一个 finder 方法。EJBObject

接口的扩展，称为 bean 的远程接口，指定在 bean 本身中定义的业务逻辑方法

。

bean 的开发人员提供由部署描述符、环境属性和清单式文件组成的控制信息。

部署描述符是 javax.ejb.deployment.SessionDescriptor 对象或

javax.ejb.deployment.EntityDescriptor 对象的序列化实例。

环境属性作为键-值对存储在一个文件中，可通过 java.util.Properties 对象访

问此文件。

清单式文件是标识企业级 bean 及其相关文件所必需的。

企业级 bean 的类文件、这两个接口的类文件、部署描述符文件、环境属性文件

和清单式文件都是使用名为 ejb-jar 的文件格式归档的。所生成的 ejb-jar 文

件提供给容器，作为部署过程的输入。

在部署时，容器分析 ejb-jar 文件的内容，并采取必要的操作使此 bean 可用。

这些操作包括：生成实现 bean 的本地和远程接口的新 Java 类，将本地接口实

现绑定到 JNDI 命名空间中，生成桩模块和 skeleton helper 类，后者是支持

RMI 通信所必需的。容器也可以生成 bean 的子类，并入容器专用的代码，以方

便对 bean 的管理。部署时由容器生成的类通常是容器专用的，而不像 EJB 组件

本身那样具有可移植性。

持久性、事务和安全

在为 EJB 组件提供持久性、事务和安全服务方面，EJB 容器可扮演主要角色。是

将这些服务的职责指定给容器，还是假定职责由 bean 自身负责，EJB 规范为

bean 的开发人员提供了灵活性。例如，对实体 bean 的持久性支持既可以由

bean 管理，也可以由容器管理。如果 EJB 组件开发人员选择使用容器管理式持

久性，他们就会在部署描述符中添加一个称为 containerManagedFields 的属性

。根据 EJB 规范：

“containerManagedFields 属性的值是一个实例字段列表，企业级 bean 提供者

希望，容器通过从数据库加载或将其存储到数据库，来管理这些实例字段。企业

级 bean 代码不应该包含任何数据库访问调用 -- 数据库访问调用将由容器工具

在部署时生成。

“专用于提供容器管理式持久性支持的容器，通常将提供丰富的部署时工具，以

允许企业级 bean 部署者建立实例字段到基础数据源的映射。一般认为，尽管容

器提供者的工具简化了映射进程，但映射进程仍可能涉及到 bean 部署者（即映

射进程不是全自动的）。”(Enterprise JavaBeans Specification 1.0)

除了支持容器管理式持久性以外，EJB 体系结构还支持容器对事务的管理。该规

范规定：

“Enterprise JavaBeans 是一种高级组件框架，它试图使应用程序开发人员不面

对系统的复杂性。因此，大多数企业级 bean 及其客户机不需要通过程序访问事

务管理。”(Enterprise JavaBeans Specification 1.0)

当 bean 的开发人员依赖容器进行事务管理时，就称为容器管理式定界，容器使

用在部署时提供的事务属性：

“无论客户机何时调用企业级 bean，容器都会介入这个方法调用。这种介入允许

容器通过事务属性显式控制事务定界。例如，如果企业级 bean 部署了

TX_REQUIRED 事务属性，则无论何时，只要客户机调用支持事务的企业级 bean，

容器就会自动启动事务，而客户机并不与任何事务上下文相关联。”(Enterprise

JavaBeans Specification 1.0)

如果开发人员选择在 bean 内支持事务，则他们在部署描述符中指定

TX_BEAN_MANAGED 事务属性，然后就可以在 bean 自身内部自由使用

javax.transaction.UserTransaction 接口划分事务边界。通过认出

TX_BEAN_MANAGED 事务属性，容器就能知道不必介入事务支持。

通过增强 AccessControlEntry 对象和 RunAs 安全标识中指定的限制，容器为

EJB 组件提供安全支持。AccessControlEntry 对象在 bean 级别上或针对单个方

法，将 Identity 对象与企业级 bean 相关联。Identity 对象反映允许调用

bean 的方法的用户或角色。当容器试图访问数据源或另一个 bean 时，它们也会

将 RunAs 安全身份应用于 EJB 组件。可将 RunAs 身份设置为等同于某个特定用

户帐户、有权限的系统帐户或客户机安全身份。访问控制和 RunAs 的信息是

bean 的开发人员在部署描述符中指定的，将影响容器管理 bean 的与安全有关的

行为方式。

虽然 EJB 1.0 规范也提到安全问题，但更详细的安全功能定义，见该规范的后续

版本。

CORBA 和 EJB 技术的关系

公用对象请求代理程序体系结构 (CORBA) 为分布式对象的平台中立和语言中立的

计算环境奠定了基础。在 CORBA 环境中，功能驻留于对象之中，而客户机可通过

对象请求代理程序 (ORB) 访问这些对象。完整的 CORBA 实现提供 ORB，外加称

为 CORBA 对象服务和 CORBA 公用工具的几个运行时服务。也可只提供 ORB，不

提供相关联的对象服务和公用工具（例如，IBM 就提供这样的两种独立 ORB）。

实现基本 ORB 功能的软件称为 ORB 核心。为了支持语言无关性，CORBA 应用程

序是用接口定义语言 (IDL) 编写的。该语言在语法上类似于 C++，但不包含语义

：IDL 中指定的操作是操作接口，而不是操作实现。由于它对多种平台和多种语

言的支持，以及源自其分布式特征的可伸缩性，CORBA 非常适合于管理企业规模

的信息系统。

设计 EJB 规范也是为了支持企业信息系统。这样说来，CORBA 是一个竞争者吗？

根据 Frequently Asked Questions for Enterprise JavaBeans，答案是否定的

：

“实际上，EJB 技术很好地补充了 CORBA。CORBA 提供了一个强大的基于标准的

基础结构，可在此结构之上构建 EJB 服务器。EJB 技术使得在 CORBA 基础结构

的顶层构建应用程序变得更为容易。”（Enterprise JavaBeans 常见问题解答）

虽然 EJB 规范和 CORBA 规范说明的是不同的技术，但 EJB 实现目前利用 CORBA

技术的某些方面。一个例子就是 RMI/IIOP。EJB 规范要求 EJB 组件及其容器使

用 Remote Method Invocation (RMI) 技术，实现分布式对象之间的方法调用。

RMI 规定远程方法的语法和语义，但并不规定应使用何种传输协议提供网络连接

。CORBA Internet 对象请求代理程序间协议 (IIOP) 基本上定义了通过 TCP/IP

传输 CORBA 消息的一种方法。开发使用 IIOP 消息形式交换 RMI 数据的 EJB 实

现，说明了 EJB 应用程序怎样才能有效地使用 CORBA 技术的各部分。这种网络

也支持与 CORBA 应用程序的互操作性，后者使用 IIOP 发送本地 CORBA 消息，

与 RMI 无关。IBM 的 EJB 实现，即 WebSphere Application Server，优化了

IIOP 的使用，方法是，弄清楚分布式对象何时驻留在同一台服务器上，并且只在

对象确实在远程时才调用 IIOP。

为了方便既并入 EJB 技术，又并入 CORBA 技术的企业系统的开发，Sun

Microsystems 在 EJB 规范和 CORBA 之间创建了一种映射。将 EJB 体系结构映

射到 CORBA，影响到 EJB 技术的几个方面，包括对象分布、命名和事务。CORBA

映射的主要目的是，保证不同厂商构建的 EJB 服务器之间的互操作性。互操作性

提供以下好处：

CORBA 客户机可以访问部署在基于 CORBA 的 EJB 服务器上的 EJB 组件

客户机程序在事务中可以将对 CORBA 对象的调用，与对企业级 bean 的调用混合

在一起

事务可以跨多个 bean，而这些 bean 又位于来自不同厂商的基于 CORBA 的多台

EJB 服务器上

使用来自某个厂商的 ORB 的客户机，可以访问另一个厂商基于 CORBA 的 EJB 服

务器上的 bean

对于要访问 EJB 组件的 CORBA 客户机来说，bean 接口被映射到 IDL。例如，可

将股票交易 bean 中定义的 buy() 和 sell() 方法，指定为 IDL 文件中的

CORBA 操作。非 bean 的 CORBA 客户机，如 C++ 客户机，可以访问这个 bean，

并用标准 CORBA 调用来调用 bean 的方法。如果容器使用 IIOP 作为它的分布式

对象协议，则该容器的职责是，生成与企业级 bean 及其接口对应的 IDL。

EJB 命名服务，它以“CORBA 对象服务”命名服务为基础，使 EJB 组件可用于

CORBA 客户机。Java Naming and Directory Interface (JNDI) 可提供到 CORBA

命名服务的接口，同时，客户机既可以通过 JNDI 调用间接访问基础命名服务，

也可以通过“CORBA 对象服务” (COS) 命名 API 直接访问该服务。

EJB 事务支持依赖于 CORBA Transaction Service，即 Object Transaction

Service (OTS)。Java Transaction Service (JTS) 代表 OTS 的 Java 绑定，它

是语言中立的。基于 CORBA 的 EJB 容器必须识别 CORBA 客户机通过 OTS 接口

发出的事务边界，以及 EJB 应用程序通过 Java Transaction API (JTA) 接口发

出的事务，JTA 是到 JTS 的应用程序级接口。JTA 还代表 Open Group XA 接口

的 Java 绑定，以便将应用程序资源连接到外部事务管理器。JIA 中含存的

javax.transaction.UserTransaction 接口，为事务边界的应用程序级控制提供

API。UserTransaction 接口，既可由其事务属性设置为 TX_BEAN_MANAGED 的

bean 使用，以可由 Java 客户机使用。

使用 EJB 组件

因为 EJB 体系结构被设计为高度灵活的，并支持使用任意复杂的方式连接企业级

bean，所以可构建许多不同的方案，来说明应用程序可怎样使用企业级 bean。一

个有用的方案提出将 EJB 组件表示为三层信息系统的关键组件，该系统将企业数

据、事务和应用程序资源连接到 Web 上。

基于 EJB 的三层编程模型视 Web 浏览器为第一层，视支持应用程序的 Web 服务

器为第二层，视企业信息资源为第三层。在此编程模型中，除了 EJB 技术外，还

实现了 Java servlet 技术、JavaBeans 技术和 Java Server Page (JSP) 技术

。下图显示了各层的排列情况：

第一层是瘦客户机 -- 通常是 Web 浏览器，它可以处理普通 Web 数据类型，如

HTML 和 GIF，并支持 HTTP 通信。第二层是 Web 应用程序服务器，它是用代码

扩充的 Web 服务器，用来对能够通过 Web 服务器调用的应用程序提供运行时支

持。现有的 Web 应用程序都沿用 CGI-BIN 编程模型，但预计第二层应用程序开

发将转向 Java servlet 编程模型，后者提供大幅改善的性能和可移植性。除支

持 Java servlet 外，Web 应用程序服务器还将添加 EJB 服务器功能，以支持使

用 EJB 组件的应用程序。第三层代表企业级信息资源，可以包括关系数据库和面

向对象的数据库、事务监视器和定制的应用程序。EJB 技术在这一设计中扮演着

关键角色，因为，它使驻留在第二层上的应用程序组件，与组成第三层的企业资

源之间的接口，得以标准化。

Java servlet、Java beans 和 Java server page 不是 EJB 应用程序方案的必

需元素，但它们可与 EJB 组件一起工作，以提供基于 Java 的内聚性的应用程序

环境。此处描绘的环境将以下职责指定给参与工作的 Java 组件：

给 Java servlet 指定了应用程序“控制器”的角色

JSP 页面处理数据表示和用户界面的任务

Java bean 充当“数据封装器”，存储中间结果的数据

EJB 组件提供访问企业信息资源的机制

客户可以使用一个假定的、基于 EJB 的 Web 应用程序更新现有的库存，并用容

器管理式持久性和容器管理式事务，将访问库存数据库的位置封装在实体 EJB 组

件的内部。库存票据可通过 Web 浏览器输入，浏览器提供一个 HTML 表单来捕获

产品编号、供应商，等等，并在提交时调用一个 servlet。servlet 代码充当应

用程序控制器角色，确定哪些企业数据库需要更新，需要用户追加什样的信息。

servlet 可通过代表它的实体 bean 访问主库存数据库，并调用 JNDI 接口获取

对此 bean 的本地对象的引用，然后使用 finder 方法定位所请求产品编号的远

程对象。此时，通过调用远程对象的方法，servlet 可更新库存计数，接着容器

将此方法委托给 EJB 组件。因为容器根据数据库更新，以对 bean 透明的方式划

分一个事务，而且以对 bean 透明的方式将数据写入数据库来保证数据持久性，

所以也就保持了数据的完整性。

从 EJB 组件返回到 servlet 的任何结果信息，都可以使用 setter 方法存储在

一个（非企业的） Java bean 的属性中。此时 servlet 可将控制权转让给一个

适当的 JSP 页面，以便将这些结果组合到表示格式中，并将结果返回给用户。

JSP 页面很可能主要由静态文本和有关单个事务的可变信息占位符组成。在向浏

览器发送表示数据之前，JSP 页面使用 getter 方法从 Java bean 的属性中获得

可变数据元素。

基于 EJB 的三层设计提供了几个好处，包括：

访问企业数据的业务逻辑可封装在可重用、可移植的企业级 bean 中。

现有的企业系统只需很少修改或者根本不需要修改，就可以集成为企业级 bean。

企业应用程序所需的运行时服务，如事务和持久性，可以从 bean 中分解出来，

并指定给此 bean 的容器。

无须更改 EJB 组件，即可修改控制应用程序流程的 Servlet。

Servlet 代码可将注意力集中在应用程序控制逻辑上，而无须考虑数据表示。

JSP 页面可将静态和动态内容混合在一起，生成表示信息。

用 Java 语言编写的系统组件，对于具有 JVM 的任何平台都是可移植的。

结论

在开发能够支持关键任务的企业级信息系统的过程中，EJB 规范代表了 Java 技

术的下一个发展阶段。EJB 组件将随 JavaBeans 规范引入的 Java 组件模型，扩

展到服务器领域，从而使业务逻辑组件的开发可以跨企业应用程序重用，并且可

以跨支持 Java 的平台移植。由于包含了基于 RMI 技术的对象分布，所以支持跨

多层的可执行组件的分立，从而允许最大的实现灵活性和高度可伸缩性。如果将

常规的企业应用程序运行时服务重新定义为可指定给容器抽象的对象服务，则允

许 EJB 组件的开发人员将精力集中在业务逻辑上，从而减小了通常与运行时服务

相关的复杂性和平台相关性。

增强 Java 运行环境，以包括命名和目录服务的标准接口、关系数据库访问、事

务服务和远程对象访问，使 Java 开发人员能够编写强健的企业应用程序，而不

必离开 Java 编程环境。将其它 Java 技术 -- 如 Java servlet 和 JavaServer

Pages 技术 -- 与 EJB 组件一起使用，可创建一个对于大型企业系统来说足够强

健的紧凑编程模型，但由于使用了巧妙的接口，从而简化了开发工作。而且，因

为 EJB 体系结构是 JavaBeans 组件模型的逻辑扩展，所以作为 EJB 组件开发的

业务逻辑可跨多个企业应用程序重用。

企业级 bean 体系结构的另一个好处是，提供了现有企业信息系统的直接集成通

道，此通道可能与 Java 编程语言或 bean 编程模型毫无共同之处。因为现有的

企业信息资源 -- 如关系数据库、事务监视器和业务应用程序的定制新品种 --

可通过将它们封装在 EJB 组件中连接到 Web 前端，而无须替换应用程序或重写

主要代码段，所以，客户可保护其现有的 IT 投资。

考虑到 EJB 技术的巨大前景，IT 业界以相当大的兴趣欢迎 EJB 规范，就不是什

么令人惊讶的事了。EJB 体系结构提供的一个最大好处可能是，把业务逻辑编程

与将业务逻辑和企业级服务器端运行环境的复杂集成分离开来。如果部署了 EJB

组件的容器承担了管理运行时服务（如持久性、事务和并发数据库访问）的职责

，则 bean 的开发人员就可以自由地将精力集中在开发封装业务逻辑的软件组件

上。JavaSoft 副总裁表述了 EJB 技术的重要性（引自 Sun Microsystems 网站

）：

“‘Enterprise JavaBeans API 将为企业开发人员和解决方案提供商提供一种新

的战略武器，供他们建立下一代行业领先的、基于关键业务的应用程序，’Sun

Microsystems 的 JavaSoft 软件产品部副总裁，Jon Kannegaard 说：‘因为用

Enterprise JavaBeans API 设计的应用程序将与现有的企业系统一起工作，所以

企业利用 Java 平台会获得新的竞争优势，同时还保留他们对现有技术的投资，

’Kannegaard 继续说。

“使用 Enterprise JavaBeans API，开发人员将能够消除应用程序开发过程中的

复杂性。这是可能的，因为每个 Enterprise JavaBeans 组件都封装了一个基本

的业务功能。目前开发人员必须懂得如何编写业务逻辑和专门的系统级程序，以

控制诸如安全性功能部件和处理多个事务的能力 -- 一项既枯燥又复杂的任务。

Enterprise JavaBeans API 使全体开发人员能够将精力集中在编写解决业务问题

的逻辑上，而不是将精力集中在编写用以简化不同技术间交互作用的代码上。”

（新闻稿：Sun Releases Draft Enterprise JavaBeans Specification for

Public Review）


Finalists announced for JavaWorld Editors' Choice Awards

作者：梦

email: mfgqd@263.net

日期：2001-5-23 18:02:50

And the finalists are:

Best Java Virtual Machine

Blackdown 1.3 Linux, Blackdown.org

Java HotSpot Server VM 2.0, Solaris, Sun Microsystems

Java HotSpot Server VM 2.0, Windows, Sun Microsystems

Best Java IDE

JBuilder 4 Enterprise, Borland Software

Forte for Java 2.0, Sun Microsystems

VisualAge for Java 3.5, Enterprise Edition, IBM

Best Java Performance Monitoring/Testing Tool

Optimizeit 4.0, VMGear

JUnit 3.5, JUnit.org

Jtest 3.3, ParaSoft

Best Java Class Library

JGL 3.1, ObjectSpace

The Collections Framework, Sun Microsystems

JClass Enterprise Suite 4.5.1, Sitraka Software

Best Java Application Server

WebSphere Application Server 3.5, Advanced Edition, IBM

BEA WebLogic Server 6.0, BEA Systems

iPlanet Application Server 6.0, Enterprise Edition, iPlanet E-Commerce Solutions

Best Java Device Application

WebSphere Transcoding Publisher 3.5, IBM

Whiteboard SDK 1.0, Bluetooth Edition, Zucotto Wireless

Java 2 Platform Micro Edition, Wireless Toolkit 1.0.1, Sun Microsystems

Best Java-XML Technology

Java API for XML Processing (JAXP) 1.1, Sun Microsystems

Xalan-Java 1.2.2, The Apache XML Project

XML Parser for Java 3.1.1, IBM alphaWorks

Best Java Book

Java Servlet Programming, Second Edition, Jason Hunter, William Crawford (O'Reilly & Associates, 2001)

Java Performance Tuning, Jack Shirazi (O'Reilly & Associates, 2000)

Thinking in Java, Second Edition, Bruce Eckel (Prentice Hall, 2000)

Most Useful New or Revised Java API/Technology

Java Message Service API 1.0.2, Sun Microsystems

Log4j 1.0.4, The Jakarta Project

Mobile Information Device Profile (MIDP), Sun Microsystems/JCP

Most Innovative Java Product

Tomcat 3.2, The Jakarta Project

Java Web Start 1.0, Sun Microsystems

VisualAge for Java 3.5, Enterprise Edition, IBM


Flow control and exception Handling

作者：jeru

email: jeru@163.net

日期：7/3/2001 10:00:19 AM

2) Flow control and exception Handling

Objective 1)

Write code using if and switch statements and identify legal argument types for these statements.

·    Unreachable statements produce a compile-time error.

while (false) { x = 3; } // won’t compile

for (;false;) { x =3; } // won’t compile

if (false) {x = 3; } // will compile, to provide the ability to conditionally compile the code.

while(1==2) {…} // fine

·    A local variable in a block may be re-declared in another local block, if the blocks are disjoint.

·    if takes a boolean arguments. Parenthesis required. else part is optional. else if structure provides multiple selective branching.

·    switch takes an argument of byte, short, char or int.(assignment compatible to int)

·    case value should be a constant expression that can be evaluated at compile time.

·    Compiler checks each case value against the range of the switch expression’s data type.  The following code won’t compile.

byte b;

switch (b) {

   case 200: // 200 not in range of byte

   default:

}

·    We need to place a break statement in each case block to prevent the execution to fall through other case blocks. But this is not a part of switch statement and not enforced by the compiler.

·    default case can be placed anywhere. It’ll be executed only if none of the case values match.

·    switch can be nested. Nested case labels are independent, don’t clash with outer case labels.

·    Empty switch construct is a valid construct. But any statement within the switch block should come under a case label or the default case label.

Objective 2)

Write code using all forms of loops including labeled and unlabeled use of break and continue and state the values taken by loop counter variables during and after loop execution.

·    3 constructs – for, while, do

·    All loops are controlled by a boolean expression.

·    In while and for, the test occurs at the top.  In do, test occurs at the bottom, so the body is executed at least once.

·    In for, we can declare multiple variables in the first part of the loop separated by commas, also we can have multiple statements in the third part separated by commas.

·    In the first section of for statement, we can have a list of declaration statements or a list of expression statements, but not both. We cannot mix them.

·    All expressions in the third section of for statement will always execute, even if the first expression makes the loop condition false. There is no short –circuit here.

·    break statement can be used with any kind of loop or a switch statement or just a labeled block.

·    continue statement can be used with only a loop (any kind of loop).

·    Loops can have labels. The break statement abandons processing of the current loop entirely, the continue statement only abandons the currently processing time around the loop.

·    Names of the labels follow the same rules as the name of the variables.(Identifiers)

·    Labels can have the same name, as long as they don’t enclose one another.

·    There is no restriction against using the same identifier as a label and as the name of a package, class, interface, method, field, parameter, or local variable.

·    In fact, labels may be applied to any statements, but they are only useful in the context of beak and continue in loop constructions.

Objective 3)

Write code that makes proper use of exceptions and exception handling clauses (try catch finally) and declares methods and overriding methods that throw exceptions.

·    In any method or constructor (not class) that contains lines that might throw a checked exception, you must either handle the exception using a try/catch construct, or declare that the method throws the exception. Do not need both, but compiler wouldn’t compain that.

·    Java.lang.RuntimeException and java.lang.Error need not be handled or declared.

·    Use throw new xxxException() to throw an exception explicitly. If the thrown object is null, a NullPointerException will be thrown at the handler.

·    If a method A() is declared as throwing an Exception by the throws clause, when you call A() in method B(), method B() has to either throws exception or catch the exception by A().

·    An exception causes a jump to the end of try block. If the exception occurred in a method called from a try block, the called method is abandoned.

·    If there’s a catch block for the occurred exception or a parent class of the exception, the exception is now considered handled.

·    The try/catch clause must trap errors in the order their natural order of hierarchy.

·    At least one ‘catch’ block or one ‘finally’ block must accompany a ‘try’ statement. If all 3 blocks are present, the order is important. (try/catch/finally)

·    finally and catch can come only with try, they cannot appear on their own.

·    Regardless of whether or not an exception occurred or whether or not it was handled, if there is a finally block, it’ll be executed always. (Even if there is a return statement in try block).

·    System.exit() and error conditions are the only exceptions where finally block is not executed.

·    If there was no exception or the exception was handled, execution continues at the statement after the try/catch/finally blocks.

·    If the exception is not handled, the process repeats looking for next enclosing try block up the call hierarchy. If this search reaches the top level of the hierarchy (the point at which the thread was created), then the thread is killed and message stack trace is dumped to System.err.

·    If an exception handler re-throws an exception (throw in a catch block), same rules apply. Either you need to have a try/catch within the catch or specify the entire method as throwing the exception that’s being re-thrown in the catch block. Catch blocks at the same level will not handle the exceptions thrown in a catch block – it needs its own handlers.

·    If there’s no code in try block that may throw exceptions specified in the catch blocks, compiler will produce an error. (This is not the case for super-class Exception)

·    In other words, an overriding method may not throw checked exceptions that are not thrown by the overridden method. A method can return an Exception(?)

Here is the exception hierarchy.

Object

   |

   |

Throwable

 |            |

 |            |

 |            |

 |        Error

 |

 |

Exception-->ClassNotFoundException,

      ClassNotSupportedException,

                     IllegalAccessException,

                     InstantiationException,

                     InterruptedException,

                     NoSuchMethodException,

                     RuntimeException, -----à  EmptyStackException,

                    AWTException,             NoSuchElementException,

                     IOException                 ArithmeticException,

                ArrayStoreException,

        ClassCastException,

        IllegalArgumentException, ---à  IllegalThreadStateException

        IllegalMonitorStateException,      NumberFormatException

        IndexOutOfBoundsException,

        NegativeArraySizeException,

        NullPointerException,

        SecurityException.

IndexOutOfBoundsException-->ArrayIndexOutOfBoundsException, StringIndexOutOfBoundsException

IOException-->    EOFException,

FileNotFoundException,

InterruptedIOException,

UTFDataFormatException,

MalformedURLException,

ProtocolException,

SockException,

UnknownHostException,

UnknownServiceException.


ftp站点荟萃，你自己找找看看。不好意思！

此电子文档属C#乐园QQ群3256045整理。
ftp站点荟萃，你自己找找看看。不好意思！

上一页 目录 下一页

ftp站点荟萃，你自己找找看看。不好意思！

作者：china&boy

email: jiabaoxu@china.com

日期：2001-3-28 17:07:11

ftp://warez:warez@24.114.207.138

ftp://mp3:mp3@24.185.90.80

ftp://212.100.182.37:22

ftp://950:rulez@24.128.178.67

ftp://warez:warez@64.222.18.111

ftp://warez:warez@205.151.67.44

ftp://warez:warez@62.49.143.154

ftp://216.242.54.226/.tagged/By DA]Psych0dad/

ftp://huge:acdcfan@24.69.39.19

ftp://196.14.124.36/Private/010210010535p/_lrg/POseidon/

ftp://dmb:mp3@129.2.228.137

ftp://158.36.23.215/.tagged by Crashkid/. filled by Crashkid/

ftp://207.201.192.246/.~tmp/.Filled by Typhon666/. For Legion-FXP/

ftp://defaultefault@207.30.161.245

ftp://tom:waits@24.25.154.69

ftp://mp3:mp3@129.252.67.21:2004

ftp://206.230.9.21/pub/NitrogeniX/

ftp://204.50.118.113/.Tagged by/Endymion/-=From Delight Team=-/will be good/i u feel good/-=Mp3=-/

ftp://63.225.13.72/_vti_pvt/_vti_tag/l/a/u/g/h/.-=FireandicE=-/

ftp://64.248.87.4/WinZip/venkat/tagged/.NitrogeniX/. sniper4422/

ftp://leech:leech@213.64.51.211

ftp://63.239.238.190/~tmp/scannedby loadet/for illusionfxp/

ftp://morewarez:muzak@65.12.134.163

ftp://psychoticdreams:andc3sium@205.243.26.3

ftp://128.187.18.100/incoming/tagged/4DU/upped/by/souls/

ftp://64.225.139.193/cgi-bin/_vti_pvt/pub/by/el__/dude

ftp://204.0.70.230//?T A G G E D ?????By ELVIRA ?????????癕istress of the Dark??????

ftp://128.218.60.37/tmpdir/.from TheForce/

ftp://63.239.98.12/RemPub/WINDOWS/STUFF4 YOU/

ftp://211.233.14.97/_vti_log/lol/iso!~/-_-/

ftp://140.109.48.161/temp/~/.rvlreused/-=RIP =-/

ftp://216.50.10.23/Public/~tmp/tagged/HaCkSaW/

ftp://164.8.19.227/.temp/tag/.for/isolation/....~/

ftp://208.135.114.208/_vti_pvt/.001027023631p/.tmp/.by/.avalon/

ftp://24.29.222.210/~tmp/.scannedby slolurner/For Isolation - Filled by Yorkie/

ftp://208.63.158.32/_vti_pvt/.temp/._filled/===for===/vispers/tag_by_Predator/

ftp://207.160.59.253/_vti_log/.tmp/~/scanned by Antity/for Andromeda/and team sweden/~.~~..~/you found it !!/

ftp://165.21.116.239/Pub/.tmp/~/actifkkdd/actforlong/

ftp://199.232.145.89/tmp/.For/. XFer/

ftp://198.37.18.203/_vit_biz/

ftp://212.50.194.130//~/ .scanned/@/ .high.resolution/.4~TheForuM~/ .W.E.S. / .by/ .K.!.l.l.y/

ftp://205.166.231.17/.tmp/.by--=Spock2000=-- for DivX4u/

ftp://199.17.29.138/scanned_by_bytor/filled_by_SharP/SharP@PnW/

ftp://216.84.64.154/~tmp/.scanned/by/pixel/filled/for/-=cyphonic=-/

ftp://209.91.102.61/pub/==taGGed==/==byOkano3d==/==for Ghostfxp2001 & RN==/

ftp://63.239.98.113/pub/b/e/h/a/a/v/e/.tagged By BEHAVE/behave/.~ f**k off deleters/before/I/kick/ur/f**ken/ass/b/e/h/a/v/e/1/

ftp://203.161.232.212/~tmp/scannedby loadet/for illusionfxp/filled by acehigh/

ftp://209.235.11.91/pub/incoming/temp/for/Xenocide/0-days l:anonymous

p:user@some.com port:21

ftp://194.251.26.12

/010218140607p/4/all(5spc)of/my(5spc)friends/from(5spc)AcesOne/

ftp://206.246.149.66

/cgi-bin/SQL/home/logs/.(8spc)temp/.~~~~~~&&&&%%%%%&&&&%%%/i ve got my

counter back/scanned & filled by/AR3z

ftp://128.59.192.62/Joe/joe/Start Menu/Programs/Norton SystemWorks/;

WAR ;/nul/; TAGGED ;/

ftp://209.35.32.66/_knobas/.tagged/.justiso/ login:warez

password:warez port:21

ftp://129.81.169.16/ /.tagged/.by_laboule/.for_japanopital/

ftp://209.21.225.9/_vti_pvt/[3]RoboFxP/somenewshit/.[5]fromgimme/

ftp://207.253.9.197/~tmp/.tmp/

ftp://195.15.89.81/~/

ftp://207.243.157.61/<1sp>/.~~~/.<3sp>TeamSoS/ l:anonymous

p:me@where.why port:21

ftp://203.231.231.224/_vti_pvt/test_by_ohni_42/fxped<1sp>by<1sp>Delgard

o/.<3sp>filled<1sp>by<1sp>MTrancer/ l:anonymous p:me@where.why port:21

ftp://206.204.35.22/Microsoft<1sp>FTP/<3sp>Tagged by The

FBI/<3sp>Tagged by The FBI/<5sp>~/~<3sp>~/~/. The FBI Files/.

Wolffmanz/~For_FxPMatriX~/The FBI & Wolffmanz/F/A/P/

ftp://209.61.151.28/.<1sp>pub/.<4sp>tagged/In`~``/~~``HeRe~~``/

ftp://64.48.22.37/010212082843p/. tmp/for/nz-fxp/

ftp://24.112.22.147l:anonymous p:me@where.why port:21

ftp://24.15.149.185/My Music/ l:anonymous p:me@where.why port:21

ftp://212.214.130.194/ /CarLingFXP/Scanned by Smurff/.

Fill<17>.<18>by/-=BeDLaM=- TiMeS2 FXP/

ftp://208.48.181.242l:anonymous p:me@where.why port:21

ftp://203.37.171.20/~test/(3)tagged/byme/(3)fortaipanfxp/(3)mp3s

ftp://213.100.88.209/

ftp://12.25.96.80/.tmp/4/legionfxp/ l:anonymous p:me@where.why port:21

ftp://213.59.201.162/ l:anonymous p:me@where.why port:21

ftp://212.214.128.23

/_vti_log/.<3sp>tagged/4guardian_by<1sp>-=jankertje=-/.<1sp>new<1sp>stu

ff/.<1sp>f**k<1sp>off<1sp>deleters/.<1sp>another<1sp>one/ l:anonymous

p:me@where.why port:21

ftp://216.78.193.71/Classical/

ftp://24.240.35.177/songs/

ftp://131.161.77.101/MV/.Avnerus/_4/.The<1sp>Crew<1sp>FXP/ l:anonymous

p:me@where.why port:21

ftp://64.50.82.99/_vti_txt/uploaded by teddykiller/for

Taipan-fxp/deleters f**kin' Die/

ftp://134.71.84.88

/finanacial_form_o.bak/fdn.lnk/cgi-bin/financial/inprogress/. ntk/

l:anonymous p:anonymous port:21

ftp://204.187.65.16/_vti_pvt/Scanned/BY/F!st/Filled/By_Guam/

ftp://216.115.235.203

/dll<1sp>files/g4all/..o.k/Anonymous.Proxy.Verifier.v1.0.9.Win9xNT2K-EC

G/dc-elite/

ftp://216.132.125.30/pub/. ~/.<50

spaces>.~behave/temp/b/e/h/a/v/e/o/k/i/e/d/o/k/i/e/1

ftp://208.197.77.18/.Tagged/for/sirusnpyramid/

ftp://128.187.90.42

/_vti_log/test/.<20sp>tapcom/proxi/JACKASSSSSSSSSS/.<5sp>187/

l:anonymous p:me@where.why port:21

ftp://212.66.2.233/temp/[FFxP<1sp>TagG]<21sp>/@In Use@/

ftp://195.64.94.134/

ftp://209.35.82.4/Anonymous/incoming /pkd/-=APPZ=-/

ftp://12.21.236.1/pub/.coolcow/. for/kinetik/. members/

ftp://209.235.11.91/pub/incoming/temp/for/Xenocide/0-days

ftp://24.202.155.50/~/.scanned by/. ajnabi/. filled by/. puscifer/.

for/. Fresh FXP/

ftp://209.114.75.162/.tag/~/.scanned by Thornado/.for Delusional FXP/~

filled by mysen ~/

ftp://206.246.149.66/cgi-bin/SQL/home/logs/.

temp/.~~~~~~&&&&%%%%%&&&&%%%/i ve got my counter back/scanned & filled

by/AR3z

ftp://208.192.53.113/ptf/.vcbf/

ftp://204.184.137.101/WINDOWS/LocalSettings/Application

Data/Microsoft/FORMS/.tag/~/.filled by mysen/

ftp://63.239.98.113/_vti_pvt/tagged/uploads/by/~/zyrex/-=5. Don't

upload =-/

ftp://164.8.19.227/.temp/tag/.for/isolation/....~/

ftp://129.81.169.16//.tagged/.by_laboule/.for_japanopital/

ftp://204.49.214.55/_vti_pvt/_vti_cnf/jaz/nz-fxp/

ftp://128.59.192.62/Joe/joe/StartMenu/Programs/Norton SystemWorks/;

WAR ;/nul/; TAGGED ;/

ftp://207.160.59.253/_vti_txt/

ftp://207.214.206.162/.tmp/.daily/.bXn/0227/

ftp://198.144.7.98/.tmp/. RVL/

ftp://205.129.15.5/gringo/ FFxP&QcFxp/

ftp://216.213.80.44/--==GSM_STUFF==--/-=LA WAREZ =-/

ftp://194.88.71.194/incoming/._vti_irp/temp/. tagged/by varkentje/

ftp://202.186.86.156/.tag/.for/-=UnrealFXP=-/~/. filled by Primus/

ftp://207.8.160.34/_vti_pvt/cfg/prv_vgt/for/@madftp@/byazkikr_14/enjoy

your stay!!!/sims expansion pack/

ftp://153.90.193.3/Anonymous/xdriver/tmp/_vti_pvt/.cryo2000/

ftp://64.232.44.80//pub/by/el__/dude/

ftp://130.108.147.75/pub/scannedby? upped by 777/

ftp://209.220.226.28/.TWR/-=0day=-/

ftp://62.11.123.119/-=Audio=-/

ftp://38.202.2.228/_vti_pvt/010226085936p/.tmp/. Tagged by FuturisO

FxP/--==iso Games==--/

ftp://209.217.181.240/~tmp/.tagged/~/~ filled by mysen ~/

ftp://164.76.105.88/tmp/~/./.tagged /.intell/

ftp://207.126.112.195/.pub / . Sonic / .Tooth/

ftp://207.215.63.154/_vti_pvt/.cRyo2o0o/~/_vti_emp/-=MP3=-/

ftp://63.77.83.65/%20~%20%20temp/scannend%20by%20scheisserchen/%20%20gr

oup%20omansblood/%20%20and/K9%20FXP%20Team/-===%20Mp3%20===-/

ftp://65.2.199.179/My%20Music/

ftp://216.205.120.66/politicalsense/test/_vti_txt/.tmp/.4/~tmp/filled%2

0by/.%20%20%20s%20%20ze%20%20r/__%20.0/

ftp://ripz:ripper@196.2.17.166/

ftp://211.233.3.188/%20-=Tagged%20By%20PFE=-%20/%20.a%20/%20Pfe%20/

ftp://63.98.220.205/~tmp/dc

ftp://216.103.43.54/Pub/.%20/.tagged/.Warez/.Effect/Ripz/

ftp://207.105.168.174/~tmp/.tagged/thfxp%20/ircsum/filled%20%20by%20%20

popv/

ftp://208.57.253.11/.fxp/apps/

ftp://24.6.52.231/.%20~/.%20filled/.%20by/.%20puscifer/.%20for/.%20Fres

hFXP/Appz

ftp://64.41.9.160/htdocs/images/.tmp/%20%20%20%20%20.by%20Mitch/

ftp://198.144.7.46/.%20%20tmp/.%20%20RVL/

ftp://209.186.100.4//.tmp/.tmp /ls/.4 /ThE~FoRuM/LPT1 /-=tFfxp=-/

ftp://209.119.152.146/_vti_pvt/. tagged by No_Skillz/ . No_Skillz

pubby/

ftp://204.49.214.55/_vti_pvt/_vti_cnf/jaz/nz-fxp/

ftp://209.220.226.28/.TWR/-=0day=-/

ftp://64.90.24.238/_vti_pvt/.010213080611p/scanby ac1dl0rd/. filled/.

by juliana/

ftp://207.160.59.253/_vti_txt/

ftp://210.68.104.29/_vti_pvt/~/byRunFix/4/. WarezEffect/

ftp://63.225.13.54/_vti_pvt/new/by/dnice

ftp://130.108.147.75/pub/scannedby? upped by 777/

ftp://207.214.206.162/.tmp/.daily/.bXn/0227/

ftp://64.232.44.80//pub/by/el__/dude/

ftp://195.131.11.3/Taggedby Team Sweden/

ftp://202.186.86.156/.tag/.for/-=UnrealFXP=-/~/. filled by Primus/

ftp://216.149.218.34/503/Parent Folder ./test/. Twister/

ftp://209.217.181.240/~tmp/.tagged/~/~ filled by mysen ~/

ftp://194.88.71.194/incoming/._vti_irp/temp/. tagged/by varkentje/

ftp://mp3:mp3@216.12.215.19/

ftp://212.47.211.82/

ftp://24.169.52.87/

ftp://136.165.130.18/download/

ftp://loginassword@207.70.25.34/

ftp://edo93:edo93@142.103.42.84/

ftp://anonymous:anonymous@216.147.91.70/

ftp://207.253.196.78/

ftp://lotsofstuff:lotsofstuff@24.20.203.215/

ftp://157.25.5.5/

ftp://emu:emu@132.229.149.82/

ftp://pornorn@212.127.143.224/

ftp://quick:warez@24.226.36.207/

ftp://anonymous:anon@136.165.130.18/download/

ftp://123:123@209.178.117.22/

ftp://divxivx@134.68.118.75/

ftp://glass:glass@204.196.172.238/

ftp://DivX:4alL@216.64.161.69/

ftp://movies:ratio@24.183.240.7/

ftp://AllStuff:free@160.79.55.242/

ftp://128.104.17.95/

ftp://guest:guest@128.228.133.8/

ftp://spawn:simmons@209.249.30.83/


Garbage Collection

作者：jeru

email: jeru@163.net

日期：7/3/2001 9:56:54 AM

3)Garbage Collection

State the behavior that is guaranteed by the garbage collection system and write code that explicitly makes objects eligible for collection.

1.    Garbage collection is a mechanism for reclaiming memory from objects that are no longer in use, and making the memory available for new objects.

2.    An object being no longer in use means that it can’t be referenced by any ‘active’ part of the program.

3.    Garbage collection runs in a low priority thread. It may kick in when memory is too low. No guarantee.

4.    It’s not possible to force garbage collection. Invoking System.gc may start garbage collection process.

5.    There are no guarantees that the objects no longer in use will be garbage collected and their finalizers executed at all. gc might not even be run if the program execution does not warrant it. Thus any memory allocated during program execution might remain allocated after program termination, unless reclaimed by the OS or by other means.

6.    There are also no guarantees on the order in which the objects will be garbage collected or on the order in which the finalizers are called.

7.    Circular references do not prevent objects from being garbage collected.

8.    We can set the reference variables to null, hinting the gc to garbage collect the objects referred by the variables. Even if we do that, the object may not be gc-ed if it’s attached to a listener. (Typical in case of AWT components) Remember to remove the listener first.

9.    All objects have a finalize method. It is inherited from the Object class.

10.    finalize method is used to release system resources other than memory. (such as file handles and network connections) The order in which finalize methods are called may not reflect the order in which objects are created. Don’t rely on it. This is the signature of the finalize method.

protected void finalize() throws Throwable { }

In the descendents this method can be protected or public. Descendents can restrict the exception list that can be thrown by this method.

11.    finalize is called only once for an object. If any exception is thrown in finalize, the object is still eligible for garbage collection (at the discretion of gc)

12.    gc keeps track of unreachable objects and garbage-collects them, but an unreachable object can become reachable again by letting know other objects of its existence from its finalize method (when called by gc). This ‘resurrection’ can be done only once, since finalize is called only one for an object.

13.    finalize can be called explicitly, but it does not garbage collect the object.

14.    finalize can be overloaded, but only the method with original finalize signature will be called by gc.

15.    finalize is not implicitly chained. A finalize method in sub-class should call finalize in super class explicitly as its last action for proper functioning. But compiler doesn’t enforce this check.

16.    System.runFinalization can be used to run the finalizers (which have not been executed before) for the objects eligible for garbage collection.

17.    Local variables in methods go out of scope when the method exits. At this point the methods are eligible for garbage collection. Each time the method comes into scope the local variables are re-created.

18.    Java uses a "mark sweep garbage collection algorithm, which traverses all the object references, marking any objects that are referred to and then garbage collecting any objects that are unmarked.

19.    Java allows you to add a finalize() method to any class. The finalize() method will be called before the garbage collector sweeps away the object. In practice, do not rely on the finalize method for recycling any resources that are in short supply - you simply cannot know when this method will be called.

In the exam point of view :

&#61623;&#61472;You must be able to identify when an object is available for gc - you have either set it to null or you

have "redirected" the variable that was originally referring to it, so that it now refers to a different

object.

&#61623;&#61472;if you have a reference to an object say, A and then you pass A as an argument to some constructor -

new obj(A); - then even if you null your reference - A=null; - you can't say that A is available for

gc. So just follow the references and when they drop to zero you know its eligible/available for gc,

not that it will happen.

eg,

1. obj = new Jo();

2. obj.doSomething();

3. obj = new Jo(); //Same as obj=null;

4. obj.doSomething();

Object a = new Object();

Object a=null; //Now the object created in 1st line is available for gc

Object a=new Object();

a = new Object(); //same.

// Now original object created in line 1 is available for gc and a new

object is now out there referenced by "a".

Aclass a = new Aclass(); // Object 1

Aclass b= new Aclass(); // Object 2

Aclass c = new Aclass(); // Object 3

a=b; // now we have no valid object reference to object "a" and it will be

// garbage collected sometime after this statement. But when?......

a=c;

c=null; // no garbage collection will be eligible since

// "a" still refers to Object 3

a=null; // now object "c" is eligible for gc since it always had a valid reference.

// Should "b" go out of scope; then we would possibly have eligibility for gc.

// there might still be other references to object "b" preventing the collection.


Good Java Style Part 1

此电子文档属C#乐园QQ群3256045整理。
Good Java Style Part 1

上一页 目录 下一页

Good Java Style Part 1

作者：jeru

email: jeru@163.net

日期：8/9/2001 6:11:15 PM

Good Java Style: Part 1

By Thornton Rose

Introduction

Having worked as a software developer and consultant for many years, I have seen a large amount of code in a variety of programming languages. It has run the gamut from elegant to ugly, and unfortunately much of it has been ugly. I hope to persuade you, and my fellow developers, that we should give as much attention to the style of our code as we give to the user interface and other visible parts of an application. In this the first part of a two part series, I explain why we should care about how our code looks and illustrate some general elements of good Java style.

Why Style Matters

Even though Java is used to write programs rather than prose, it is still used to express thoughts and ideas. And, in addition to conveying information, those thoughts and ideas must actually do something. Worrying about good style may seem like a waste of time, but it behooves us to write our code such that the thoughts and ideas it expresses are exceptionally clear.

Here are several reasons for using good style [from "Java Code Conventions," Sun Microsystems]:

80% of the lifetime cost of a software product goes to maintenance.

Hardly any software is maintained for its whole life by the original author(s).

Using good style improves the maintainability of software code.

If the source code is shipped with the software, it should be as well-packaged, clean, and professional as the rest of the product.

Writing code with good style also provides the following benefits:

It improves the readability, consistency, and homogeneity of the code, which makes it easier to understand and maintain.

It makes the code easier to trace and debug, because it's clear and consistent.

It allows you to continue more easily where you or another programmer stopped, particularly after a long period of time.

It increases the benefit of code walkthroughs, because the participants can focus more on what the code is doing.

General Guidelines

Writing Java with good style is not hard, but it does require attention to detail. Here are some general guidelines to follow:

Make the code clear and easy to read.

Make the code consistent.

Use obvious identifier names.

Logically organize your files and classes.

Have only one class per file (not including inner classes).

Use a maximum line width of 80-90 characters.

Use whitespace and/or other separators judiciously.

Use spaces instead of tabs for indentation.

Tabs vs. Spaces

Tabs vs. spaces is one of several religious issues related to writing code, and I am not suggesting that there is only one right way. I espouse using spaces because it ensures that my code will look the same in my editor as it does in your editor and vice versa. If you feel that using spaces instead of tabs "just ain't right", then by all means use tabs.

Braces and Indentation

Indent style (cf., Raymond, "Indent Style"), or the placement of braces ("{" and "}") and the associated indentation of code, is another of the religious issues related to writing code. There are several indent styles common to C-style languages like Java, and I am not going to suggest that one of them is superior. In most of the example code in this article, I use what is usually referred to as K&R style. If you don't like K&R, by all means use another style.

Comments

There are two type of comments that you can put in your Java code: Javadoc comments (also called documentation comments) and implementation comments. Javadoc comments can be extracted by the javadoc tool to produce API documentation. Implementation comments are those comments that explain the how and why of the code. Use the following guidelines for commenting your Java code:

Use Javadoc comments wherever they are allowed (on classes and methods at minimum).

Use block comments rather than end-of-line/trailing comments, except in special cases, such as variable declarations.

Also, keep in mind that good comments are helpful; bad comments are a nuisance.

Example 1. Bad Comment Style

   // applyRotAscii() -- Apply ASCII ROT

   private void applyRotAscii(){

      try{

         int rotLength = Integer.parseInt(rotationLengthField.getText().trim()); // get rot len

         RotAscii cipher = new RotAscii(rotLength); // new cipher

         textArea.setText(cipher.transform(textArea.getText())); // transform

      }catch(Exception ex){

         /* Show exception */

         ExceptionDialog.show(this, "Invalid rotation length: ", ex); }

   }

Example 2. Good Comment Style.

   /**

    * Apply the ASCII rotation cipher to the user's text. The length is retrieved

    * from the rotation length field, and the user's text is retrieved from the

    * text area.

    *

    * @author Thornton Rose

    */

   private void applyRotAscii() {

      int      rotLength = 0;  // rotation length

      RotAscii cipher = null;  // ASCII rotation cipher

      try {

         // Get rotation length field and convert to integer.

         rotLength = Integer.parseInt(rotationLengthField.getText().trim());

         // Create ASCII rotation cipher and transform the user's text with it.

         cipher = new RotAscii(rotLength);

         textArea.setText(cipher.transform(textArea.getText()));

       } catch(Exception ex) {

         // Report the exception to the user.

         ExceptionDialog.show(this, "Invalid rotation length: ", ex);

      }

   }

Blocks and Statements

Use the following guidelines for writing blocks and statements:

Put only one statement per line.

Always use braces with control statements (e.g., 'if').

Consider marking the end of a block with a comment (e.g., } // end if), particularly with long or nested blocks.

Put variable declarations at the beginning of a block.

Always initialize variables.

If you want to be a perfectionist, left-align variable names.

Indent the case clauses in a switch block.

Put whitespace before and after operators.

In if, for, or while, put whitespace before the "(".

Use whitespace and parentheses in expressions to increase readability.

Variables used in 'for' loops are the exception to putting variables at the beginning of a block. The loop variable(s) may be declared in the initialization part of the for statement, e.g., for (int i = 0; ...)

.

Putting a comment at the end of a block can help you track down accidentally deleted closing braces. Finding those in a large source file can sometimes drive you nearly crazy.

Example 3. Bad Block Style.

   try{

      for(int i=0;i<5;i++){

         ...

         }

      int threshold=calculateThreshold();

      float variance=(threshold*2.8)-1;

      int c=0;

      if (threshold<=15) c=calculateCoefficient();

      switch(c){

      case 1: setCeiling(c*2); break;

      case 2: setCeiling(c*3); break;

      else: freakOut();

      }

   }catch(Exception ex){ ... }

Example 4. Good Block Style.

   try {

      int   threshold  = 0;

      float variance    = 0.0;

      int   coefficient = 0;

      // Prepare 5 cycles.

      for (int i = 0; i < 5; i ++){

         prepareCycle(i);

      }

      // Calculate the threshold and variance.

      threshold = calculateThreshold();

      variance = (threshold * 2.8) - 1;

      // If the threshold is less than the maximum, calculate the coefficient.

      // Otherwise, throw an exception.

      if (threshold <= MAX_THRESHOLD) {

         coefficient = calculateCoefficient();

      } else {

         throw new RuntimeException("Threshold exceeded!");

      }

      // Set the ceiling based on the coefficient.

      switch (coefficient) {

         case 1:

            setCeiling(coefficient * 2);

            break;

         case 2:

            setCeiling(coefficient * 3);

            break;

         else:

            freakOut();

      } // end switch

   } catch(Exception ex) {

      ...

   } // end try

Related Links

Tabs vs. Spaces, Jamie Zawinski.

Writing Robust Java Code — The Ambysoft Inc. Coding Standards for Java, Scott Ambler.

Draft Java Coding Standard, Doug Lea.

Java Code Conventions, Sun Microsystems, Inc.

How to Write Doc Comments for Javadoc, Sun Microsystems, Inc.

The Jargon File (known in print as The New Hacker's Dictionary), Eric S. Raymond.

Indent Style, The Jargon File, Eric S. Raymond.

References

Java Code Conventions. Copyright ) 1995-2000 Sun Microsystems, Inc.

About the Author

Thornton Rose is a contract software developer in Atlanta, Ga. He can be reached via e-mail at thornton.rose@mindspring.com.


Good Java Style Part 2

作者：jeru

email: jeru@163.net

日期：8/9/2001 6:12:34 PM

Good Java Style: Part 2

By Thornton Rose

Introduction

This is the conclusion of a two-part series on Java coding style. In Good Java Style: Part 1

, I introduced my case for writing Java code using good habits, explained why we should care about the way our code looks, and illustrated some general elements of good Java style. In this part, I illustrate more elements of good style and bring my case to a conclusion.

Source Files

There are many ways that a Java source file can be organized. Here is one that works well:

File header comment (optional).

Package declaration.

Blank line or other separator.

Import statements.

Blank line or other separator.

Class(es).

Example 1. Bad File Organization.

   package org.rotpad;

   import java.awt.*;

   import javax.swing.event.*;

   import org.javacogs.*;

   import javax.swing.*;

   import java.awt.event.*;

   class Foo {

    ...

   }

   public class RotPad extends JFrame {

    ...

   }

Example 2. Good File Organization.

   package org.rotpad;

   // Java classes

   import java.awt.*;

   import java.awt.event.*;

   import javax.swing.*;

   import javax.swing.event.*;

   // JavaCogs classes

   import org.javacogs.*;

   /**

    * RotPad is a simple GUI application for performing rotation ciphers on plain

    * text.

    *

    * @author Thornton Rose

    * @version 1.0

    */

   public class RotPad extends JFrame {

      ...

   }

   //-----------------------------------------------------------------------------

   /**

    * Foo is ...

    *

    * @author Thornton Rose

    * @version 1.0

    */

   class Foo {

      ...

   }

Import Statements

A complex class can have a large number of imports, which can get unruly, especially if you prefer to import individual classes instead of whole packages (e.g., java.awt.*). To get a handle on imports, organize them as follows:

Java standard classes (java.*).

Java extension classes (javax.*).

Third-party classes.

Application classes.

Be sure to comment the third-party and application classes, particularly those that do not have obvious names. Use end-of-line comments, or put a comment at the beginning of the section. Also, if you really want to be a perfectionist, order each group of imports alphabetically.

Example 3. Bad Import Style.

   import java.util.*;

   import javax.swing.*;

   import java.awt.event*;

   import com.gensym.com.*;

   import javax.swing.table.*;

   import com.pv.jfcx.*;

   import java.awt.*;

   import com.melthorn.util.*;

Example 4a. Good Import Style.

   import java.awt.*;

   import java.awt.event*;

   import java.util.*;

   import javax.swing.table.*;

   import com.gensym.com.*;     // BeanXporter

   import com.pv.jfcx.*;        // ProtoView

   import com.melthorn.util.*;  // Utilities

Example 4b. Good Import Style.

   // Java classes

   import java.awt.*;

   import java.awt.event*;

   import java.util.*;

   import javax.swing.table.*;

   // BeanXporter

   import com.gensym.com.*;

   // ProtoView GUI components

   import com.pv.jfcx.*;

   // Application classes

   import com.melthorn.util.*;

Classes

Organizing a Java source file without organizing the classes in it would not gain you much in the way of proper style. Here's how to organize the classes in your source files:

Javadoc comment or other header comment.

Class declaration.

Field declarations.

Blank line or other separator.

Constructors.

Blank line or other separator.

Methods, except main()

, grouped logically.

Blank line or other separator.

Inner classes.

Blank line or other separator.

main()

.

Example 5. Bad Class Style.

   // RotPad -- GUI app. for ROT ciphering

   public class RotPad extends JFrame {

   private static final String TRANSFORM_ROT13    = "ROT13";

   private static final String TRANSFORM_ROT13N5  = "ROT13N5";

   private static final String TRANSFORM_ROTASCII = "ROT-ASCII";

   private void jbInit() throws Exception {

      ...

   }

   public static final String TITLE   = "RotPad";

   public static final String VERSION = "1.0";

   public static void main(String[] args) {

      ...

   }

   public RotPad() {

      ...

   }

   private JPanel jPanel1 = new JPanel();

   private JPanel jPanel2 = new JPanel();

   private BorderLayout borderLayout1 = new BorderLayout();

   ...

   }

Example 6. Good Class Style.

   /**

    * RotPad is a simple GUI application for performing rotation ciphers on plain

    * text.

    *

    * @author Thornton Rose

    * @version 1.0

    */

   public class RotPad extends JFrame {

      // Public constants

      public static final String TITLE   = "RotPad";

      public static final String VERSION = "1.0";

      // Private constants

      private static final String TRANSFORM_ROT13    = "ROT13";

      private static final String TRANSFORM_ROT13N5  = "ROT13N5";

      private static final String TRANSFORM_ROTASCII = "ROT-ASCII";

      // GUI components [JBuilder generated]

      private BorderLayout borderLayout1 = new BorderLayout();

      private JPanel jPanel1 = new JPanel();

      private JPanel jPanel2 = new JPanel();

      ...

      /**

       * Construct a new instance of this class.

       */

      public RotPad() {

         ...

      }

      /**

       * Initialize UI components. [JBuilder generated]

       */

      private void jbInit() throws Exception {

         ...

      }

      ...

      //--------------------------------------------------------------------------

      /**

       * Start the application.

       */

      public static void main(String[] args) {

         ...

      }

   }

Field Declarations

Some classes have a large number of fields, which can become difficult to maintain if they are not organized well. Organize them as follows:

Public contstants (final and static final).

Public variables.

Protected constants.

Protected variables.

Package constants.

Package variables.

Private constants.

Private variables.

Additionally, use the following guidelines for writing field declarations:

Use one declaration per line.

Use Javadoc comments on public and protected fields, at minimum.

Use UPPERCASE for the names of constants. Using uppercase makes them much more obvious in both declarations and expressions.

If you use a tool that generates field declarations, such as JBuilder or Visual Cafe, keep the generated fields separate from the other fields. It makes maintenance of the UI code much easier.

Example 7. Bad Field Style.

   public class CustomerSearchDialog extends JDialog {

      private JLabel firstNameLabel = new JLabel();

      private JLabel lastNameLabel = new JLabel();

      public static final RESULT_SELECT = 1;

      private Vector results = new Vector(); // Search results.

      private DefaultTableModel tableModel = new DefaultTableModel();

      public static final RESULT_CANCEL = 0;

      // ...

   }

Example 8. Good Field Style.

   /**

    * ...

    */

   public class CustomerSearchDialog extends JDialog {

      /**

       * Indicates that search was cancelled; returned by showDialog() when

       * user clicks cancel button.

       */

      public static final RESULT_CANCEL = 0;

      /**

       * Indicates that a customer was selected; returned by showDialog() when

       * user clicks select button.

       */

      public static final RESULT_SELECT = 1;

      private Vector            results    = new Vector();             // Search results.

      private DefaultTableModel tableModel = new DefaultTableModel();  // Grid model.

      // GUI fields. [JBuilder]

      private JLabel firstNameLabel = new JLabel();

      private JLabel lastNameLabel = new JLabel();

      // ...

   }

Method Declarations

Use the following guidelines for writing method declarations:

Always have a Javadoc comment or some other header comment.

Always put the access modifier first.

If the line is too long, break it into one or more lines.

If the method has more than a few parameters, consider putting each on a separate line.

Don't put whitespace between the method name and the opening parenthesis ("(").

Always put whitespace (which could be a line break) between the closing parenthesis (")") and the opening brace ("{").

Example 9. Bad Method Style.

   public int getTypeCount (String custType)

   {

   ...

   }

   static public getInstance(){ ... };

   public void showRange()

      throws RangeException {

    ...

   }

Example 10. Good Method Styles.

   /**

    * Return the single instance of this class.

    */

   public static CalculationEngine getInstance() {

      return instance;

   }

   /**

    * Calculate the consumption coefficient.

    */

   public float calculateConsumptionCoefficient(int base, float variance,

         int iterations) throws RangeException {

      // ...

   }

   /**

    * Calculate the consumption coefficient.

    */

   public float calculateConsumptionCoefficient(

         int base,

         float variance,

         int iterations)

      throws RangeException

   {

      // ...

   }

   /**

    * Calculate the consumption coefficient.

    */

   public float calculateConsumptionCoefficient(int base,

                                                float variance,

                                                int iterations)

      throws RangeException

   {

      // ...

   }

Conclusion

In conclusion, I have one final thought for you on the subject of code style. No matter what guidelines you follow, and no matter how fervent your beliefs about things like indent style (cf., Raymond, "Indent Style"), remember that when you write code your overall goal should be to make the code understandable and maintainable by someone else.

Related Links

Indent Style, The Jargon File, Eric S. Raymond.

Tabs vs. Spaces, Jamie Zawinski.

Writing Robust Java Code — The Ambysoft Inc. Coding Standards for Java, Scott Ambler.

Draft Java Coding Standard, Doug Lea.

Java Code Conventions, Sun Microsystems, Inc.

How to Write Doc Comments for Javadoc, Sun Microsystems, Inc.

The Jargon File (known in print as The New Hacker's Dictionary), Eric S. Raymond.

About the Author

Thornton Rose is a contract software developer in Atlanta, Ga. He can be reached via e-mail at thornton.rose@mindspring.com.


HTML、Swing还是XML，Java客户端工具选择？(二) [转]

作者：jeru

email: jeru@163.net

日期：8/6/2001 6:07:11 PM

  基于Swing的GUI客户端

    今天还有多少人在使用Java Applet作为客户端？也许使用基于HTML的UI更安全，但这是最好的选择吗？

    AT&T的一个业务部门Telecorp PCS曾经开发过一个应用程序，使其商店可以收集希望购买移动电话的用户的资料，检查其信用卡，然后立即开通移动电话，除了确认用户输入的信息外，应用还必须通过使用排序、选择和其他的标准数据库功能处理提交的报告。此外，当一个新的移动电话开通后，这个应用程序还需要显示一个通知。

    你能相像用HTML来完成这一切吗？也许可能，但它将非常讨厌，而且速度很慢，需要不间断地使用网络连接。Telecorp PCS决定冒险在交互的Applet中使用Java，那么结果如何呢？完全成功，这一应用程序在开发时采用了Swing，并布署在采用了Java插件的互联网网站上。通过使用Swing UI类，很简单地完成了应用所要求的功能。

    我相信许多开发人员在早期使用Java时，都使用过applet，并且在解决各种浏览器之间的不兼容性、applet下载时间、性能方面花费过大量的时间。对客户端Java最大的批评来自其对象性，但现在情况已经有了很大的改观。Sun已经花费了大量的时间来改进其代码的质量，下面我将向你说明为什么基于Swing的UI是值得一试的。

     Swing及其布署模式

    我无需对Swing的内部架构以及类和界面的设计、设计模板的实现方面有多少新思想多作叙述了。Swing几乎是我见过的最彻底的窗体系统，它的容器、组件和UI元素之间的关系非常清晰。Swing的架构是基于Model-View-Controller（MVC）设计模板的，其数据与数据的表达和处理相互独立。

    大多数的Swing模型都是由各种UI元素共享的。例如，JTable使用和JList、JTree相同的模型集，这就使得学习和使用Swing非常简单,而且Command、Observable和Listener等模板提供了很好的灵活性和良好的面向对象特性。也许Swing架构中唯一的不足之处是所有的事件都被交付到相同的EventDispatch线程中，使整个GUI客户端应用程序只有一个线程。但我们可以通过使用不同的线程响应用户的命令而不通过EventDispatch线程来完成所有操作，就可以很简单地克服Swing这一缺点。

    Sun发布的每个JDK版本都对Java和Swing的性能都进行了改进。JDK 1.3中与Swing相关的改进表现在性能、内存消耗和一个输入确认框架。性能和内存消耗方面的改进相当可观。我们公司将客户端的应用程序由JDK 1.2.2升级到1.3后，内存消耗降低了30%，一些应用程序内存占用减少得更多。由于Swing内部的初始化过程被优化了，我们的客户端应用程序的运行速度和响应速度都更快了。简而言之，对速度影响最大的是加载其他界面组件时自动产生的大量的类，而这一方案中只包含有一个类。另一个重大的变化是缺省的JVM是HotSpot Client VM，它专门针对GUI绘制和客户端应用程序执行进行了优化，可以通过在命令行方式下运行java命令得到缺省的JVM。

    输入确认框可以使我们很方便地通过编程实现命令字段或输入确认。在这以前，如果要在处理下一个字段之前，对前一个用户输入进行处理，必须在该部件上添加一个监听程序，每当该部件不再是焦点后都需要对它进行确认，这种方式非常单调和乏味。使用新的InputVerifier类，可以通过创建InputVerifier子类的一个实例，并将它赋予需要确认的JComponent，就能达到相同的目的。在焦点转换之前，部件将自动地调用verify()方法。


HTML、Swing还是XML，Java客户端工具选择？(三) [转]

作者：jeru

email: jeru@163.net

日期：8/6/2001 6:09:00 PM

Swing存在的问题在于布署时的速度和兼容性问题。现在，它的一个重大改进解决了这些问题并使Java客户端应用程序重新成为一个可行的选择，CPU的速度在过去2年中翻了一番。在JDK 1.3中，基于Swing的应用程序的运行速度已经非常快了，所需要的内存也相当少。这就使我们在布署Swing方面还存在着最后一个问题，那就是如何进行布署，在这里，我们有三种解决方案可供选择。

    方案一：Java插件

    基于浏览器的Java中最精彩的特性之一是Java插件。对HTML网页作简单的修改就能够消除对浏览器JVM的依赖，并使我们可以在Sun的标准JVM中运行Applet。一旦安装了JRE，Applet就被下载到本地磁盘上，并被放置在高速缓冲区中，再打开带Applet的HTML网页的速度就会快许多，原因是所有的东西都是在本地磁盘上的。为说明其工作原理，我们首先来看看原来的Applet布署方式，HTML网页是如何使用插件的，我们假设你已经掌握了HTML和Java Applet的有关知识，并创建了如下的网页：

[TABLE][TR][TD][B]＜HTML＞

＜HEAD＞

＜TITLE＞My traditional applet page＜/TITLE＞

＜/HEAD＞

＜BODY＞

＜APPLET CODE=HelloWorld.class ARCHIVE=HelloWorld.jar＞

Sorry, looks like I bumped into another browser that doesn't support Java applets

＜/APPLET＞

＜/BODY＞

[/B][/TD][/TR][/TABLE]

    这种方式的缺点是它依赖浏览器JVM来加载和执行HelloWorld类。考虑到市场上存在有多种浏览器，它们执行Java的方式各不相同，使得Applet的布署成为一件令人恐惧的事。你必须保证在经过测试的JVM中运行Applet。我们不要求浏览器运行Java，而要求浏览器安装和运行我们将要在其中运行Applet的JVM。在IE中，我们可以通过使用＜OBJECT＞标志来完成这一任务，在其他的浏览器中，这一标志可能会有所不同，例如在Netscape Navigator中是＜EMBED＞。修改后的网页如下所示：

[TABLE][TR][TD][B]＜HTML＞

＜HEAD＞

＜TITLE＞My new applet page＜/TITLE＞

＜/HEAD＞

＜BODY＞

＜OBJECT classid="clsid:8AD9C840-044E-11D1-B3E9-00805F499D93"

width=100% height=100

codebase="./j2re-1_3_0_02-win.exe#Version=1,3,0,2"＞

＜param name="code" value="HelloWorld.class"＞

＜param name="archive" value="HelloWorld.jar"＞

＜param name="cache_archive" value="HelloWorld.jar"＞

＜param name="cache_option" value="Plugin"＞

＜/OBJECT＞

＜/BODY＞

[/B][/TD][/TR][/TABLE]

上面的网页将使浏览器检查指定ClassID的对象是否已经安装，如果没有安装，则从指定的URL下载JVM，并进行安装。然后浏览器执行插件，并下载和显示Applet。我们可以通过http://192.9.48.9/products/plugin/1.3/demos/applets/GraphLayout/example1.html

    插件带来的好处是它可以支持各种操作平台上的所有浏览器，此外，它还提供了一个有保障的执行环境，插件只需要安装一次，就可以对所有Applet进行缓冲，使再次访问网站时非常容易。这一方法的一个最大不足之处是，在运行Applet之前，必须下载一个大小为5MB的插件，这在低速的互联网连接上尤其令人不能容忍。事实上，如果你的Applet只是一个大小为5KB的网页顶端的一个钟表，为此而下载一个5MB的插件是得不偿失的。

    方案二：使用Java Web Start

    布署Java应用软件的另一种方式是Sun公司的Java Web Start，它在本质上与Java插件相似，只是在第一个步骤上有明显的不同。Java Web Start要求在每台台式机上进行人工安装，这一点远不如插件的自动安装。Java Web Start的安装相当简单，一旦安装完毕，依赖Java Web Start的应用程序就可以被下载和安装。就象插件一样，应用程序也是通过互联网发行的。

    根据我的经验，Java插件在安装上与Java Web Start相似，但比Java Web Start的用户亲和性更好，原因是它要求的管理员或用户干预更少。也有一些公司创建了自己的功能类似的布署工具，这些工具有时候比Java Web Start还好用。例如，Sitraka公司的DeployDirector在性能上优于Java Web Start，并且安装也更简单。

    总而言之，通过使用Java插件和Java Web Start，基于Swing的应用程序的布署比原来要简单和安全许多，但仍然比点击一个只有JavaScript的HTML网页要复杂得多。而且有些用户可能对在本地机器上安装JVM所需要完成的步骤有被胁迫的感觉，或者没有发现Swing所带来的好处，但如果需要一个动态GUI用户界面，使用户享有更多地灵活性，没有一种方法比采用Swing Applet更好了。

    此外，如果整个开发都是基于Java的，在HTML请求数据和应用程序内部结构之间就无需进行映射。RMI可以提供快速的双向网络调用，它可以回叫客户端应用程序，提醒用户根据服务器的要求更新显示内容。

    方案三、以纯HTML方式布署Java Swing

    尽管HTML和Swing在开发客户端应用软件方面各有利弊，但很明显的是，理想的解决方案应该是二者都支持。然而，由于这二种技术在本质上具有较大的区别，在一个应用程序中只能采用二者之一。尽管大多数用户都会喜欢基于Swing的快速交互客户端应用程序，但下载并在客户端系统上安装JRE并非总是一个很好的选择。有时候，安全和防火墙方面的限制使得RMI很难在网络上运行。在这种情况下，我们需要的是一种可以在所有系统上运行的交互式客户端应用程序，即使我们能够使用的客户端应用程序只有浏览器。CreamTec公司的WebCream可以充当Swing-HTML之间的桥梁。

    WebCream是一种Java工具，它可以为基于GUI的Java应用程序和Applet提供自动的互联网访问，可以使我们利用AWT和Swing实现GUI前端应用程序，同时，可以自动地使HTML访问该应用程序。在一定程度上，可以把WebCream看作是动态的Java-to-HTML转换工具，它可以即时地把Java中的框架和对话转换为HTML。然后，将Webpage行为模仿为GUI事件，以保持应用程序原有的逻辑。WebCream不要求对现有的表格和业务逻辑进行修改，也无需学习任何新的API，它旨在发行现有的应用程序和applets。WebCream只是设置互联网服务器和描述应用程序属性文件的工具，它的标准版具有全部的功能，而且是免费的。WebCream还无需在客户端的机器上进行安装，甚至无需浏览器支持Java，因为浏览器接收到的全部都是HTML代码。

    据所我知，只有WebCream才具有这样的功能，没有其他的工具可以提供相似的解决方案。但也有一些产品采用不同的方法使原本不是为互联网设计的应用程序具有互联网访问功能。Windows 2000中有一种内置的终端服务器（Terminal Server）服务，可以使用户只要在本地系统登录就可以通过远程方式访问服务器。象Citrix系统公司的MetaFrame那样，终端服务器向远程终端发送一个视频流，并为在服务器上运行的应用程序模仿用户的行为。它在高速网络上可以很好地运行，在低速网络上的表现则不尽人意。它在Java应用程序方面还有问题，因为它们不使用本机的绘制和滚动例程。终端服务器的可伸缩性还不太强，原因是每个用户都在运行它的一个拷贝。由WebCream转换过的应用程序在形式上与在本地系统上运行的应用程序有所不同，但它的性能更好，因为只有用户在提交一个页面时，才会与服务器进行连接。所有由具有WebCream功能的应用程序服务的用户可以共享一个JVM，因此也可以大大降低资源的消耗。

    为了演示WebCream的工作原理，下面的2个图显示了使用WebCream时，一个样例GUI应用程序在HTML前端应用程序中是如何运行的。图1是一个正在运行的GUI应用程序（源代码），图2是WebCream管理着同一个应用程序，窗口表示浏览器中的网页。

   [TABLE][TR][TD][img]http://www.yesky.com/20010704/jt-2001-7-3-1.gif[/img]

（图1：例子中的GUI程序）

[/TD][/TR][/TABLE]　　　　　　　　　　　　　　 [TABLE][TR][TD][img]http://www.yesky.com/20010704/jt-2001-7-3-2.gif[/img]

（图2：使用了WebCream后的GUI）

[/TD][/TR][/TABLE]

    Swing-HTML转换方式并不适合所有的用户，WebCream在一定程度上允许通过HTML访问前端应用而提高基于Swing的前端应用程序的价值。有95%的应用程序可以无缝地转换成HTML，另有5%的程序则需要改变数据的表达和处理方式。


HTML、Swing还是XML，Java客户端工具选择？(四) [转]

作者：jeru

email: jeru@163.net

日期：8/6/2001 6:12:01 PM

基于XML/XSLT的客户端应用程序

         在这里，我们假定你已经对XML有了基本的理解，我将重点讨论其前端应用程序的开发。与XML相关的用户界面开发的一个主要特征是内容和表达方法之间互不干涉。简单地说，就是数据被保存为XML文档，而不再负责数据的使用和显示，根据决定数据格式和在网页上输出方式的样式表（XSLT）在HTML、WML、XHTML和其他表达形式中选择一种数据表达形式。通过使各层之间保持相对的独立，让每个层处理各自的任务，这种方法的优点是非常明显的。

    尽管XML提供了很好的数据发布模式，可以生成不同的表达模式，它仍然不能解决所有的问题。XML值得称道的是让开发人员专注于数据的处理，而让设计人员和艺术家来处理数据的表达形式。应用程序则把生成的XML文件保存在内存或存储在磁盘上，为得到指定的表达类型，例如HTML，可以通过适当的XSLT类型表对内容进行转换，该类型表可以将XML文档转换为HTML文档。类型表与判断文档中的内容应当如何分布在网页上的模板类似。它还可以转换数据，执行传统的命令和循环来处理数据，进行决策，因此它也可能变得非常复杂。

    类型表的优点之一是，如果要从一部移动电话上访问同一个应用程序，所需要作的全部工作就是再创建一个新的WML类型表。从理论上说，这个应用程序的所有其他部分都无需作任何改变，这使得服务器端的编程工作变得非常高效。采用XML实现一个前端应用程序将使一些任务变得简单，因为显示的数据和处理这些数据的代码都无需改变。开发应用程序各部分的开发小组可以独立工作，从而加快开发进程。

    然而，我曾经在基于XML的前端应用程序开发中吃过苦头，它的二个最主要的缺点是缺乏帮助生成XML以及类型表开发方面的工具和处理速度，第一个因素对那些使用DreamWeaver和FrontPage等可视化HTML开发工具创建HTML网页的开发人员的影响更大。就我本人而言，我还是喜欢使用DreamWeaver，但我实在不能从在文本编辑器中编写HTML代码中得到什么乐趣。毕竟，现在已经是21世纪了，我们来看一个将XML文档转换为HTML的非常简单的XSLT类型表：

＜?xml version="1.0"?＞

＜xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

＜xsl:template match="page"＞

＜html＞

＜head＞

＜title＞＜xsl:value-of select="title"/＞＜/title＞

＜/head＞

＜body＞

＜xsl:apply-templates/＞

＜/body＞

＜/html＞

＜/xsl:template＞

＜xsl:template match="title"＞

＜h1＞＜xsl:apply-templates/＞＜/h1＞

＜/xsl:template＞

＜/xsl:stylesheet＞

    如果所选的XSL类型表适当的话，上面的代码会生成如下所示的HTML代码：

＜html＞

＜head＞

＜title＞My first page＜/title＞

＜/head＞

＜body＞

＜h1＞Hello, world＜/h1＞

＜/body＞

＜/html＞

    我们会发现，上面的代码与我们用HTML开发工具得到的代码不大相同。不幸地是，我们必须用手工的方式对它进行编辑，在一个可以生成HTML文档的工具中对它进行处理后，然后在浏览器中打开生成的文档。如果仅仅是为了美观而改变字体的大小，那么就无需这么作了。

    第二个问题是在运行时完成所有的任务需要许多时间。如果数据格式不是XML，还需要生成XML文档，在类型表对XML进行转换处理后，才能生成HTML代码。与在Servlet或JSP应用程序中向内存缓存中写文件相比，速度和简洁性实在不是基于XML的前端应用程序的优点。

    总而言之，如果需要动态地生成不同版面和窗体的表达形式时，就需要使用XML。如果表达形式需要经常变化或需要非常灵活，就应该让设计人员重新设计新的类型表。需要记信的是，设计人员只要掌握XML和XSLT就万事大吉了。

    另一个需要使用基于XML的UI的场合是你需要处理的资料是XML文档，而不是Java对象或关系数据库。XML是一种在未来颇有前途的新技术，然而，目前使用JSP开发前端应用不是比较方便的，尤其是HTML是唯一一种前端开发工具时更是如此。使用JavaBeans和JSP标识库等一些著名的设计模式则能够使数据的内容和表达形式很好的分离。随着自动对XML/XSLT进行编辑的工具的出现，这些技术在使用方面将更加简单方便。

    结论

    具体到你自己的Java应用程序，这三种前端技术各有利弊，任何一种技术都不能在所有方面超过其他二种。针对具体的应用程序，我们必须对需求、用户的期望进行详细分析，对这三种技术进行评估。下面是一些基本的准则：

    使用HTML/JSP：

      ━━适合于由大量图形和美术作品组成的静态内容。

      ━━前端应用程序的界面面向使用所有平台的用户。

      ━━用户所使用的互联网连接较慢。

      ━━希望快速地构建功能比较单一的应用程序。

    使用Java Swing：

      ━━适合创建具有内置、与界面相关的逻辑的GUI。

      ━━可以减轻网络流量，提供尽可能的即时响应。

      ━━如果用户对界面的质量和功能有较高的期望。

      ━━如果UI的功能比其美感更重要时。

    使用XML/XSLT

      ━━需要支持许多不同的而且经常变化的窗体。

      ━━数据是XML格式。

      ━━需要个性化。

      ━━计划提供无线访问等不同的访问方式。


HTML、Swing还是XML，Java客户端工具选择？(一) [转]

此电子文档属C#乐园QQ群3256045整理。
HTML、Swing还是XML，Java客户端工具选择？(一) [转]

上一页 目录 下一页

HTML、Swing还是XML，Java客户端工具选择？(一) [转]

作者：jeru

email: jeru@163.net

日期：8/6/2001 6:05:31 PM

[前言：] Java软件设计师和管理人员经常会面临这样的难题：在开发应用软件的客户端时，应该在Swing、HTML、XML三种技术中选择谁。在这篇文章中，我将把自己在这三种技术方面的经验与广大读者共享，并对在Java应用软件开发中选择哪一种技术提出一些标准和技巧。在文章的最后，还会介绍一种整合Java Swing和HTML的新方法。

    与现有的技术相比，Java有明显的优点，因此它已经在服务器端应用软件的开发中确立了主导地位。然而，由于每一个应用程序都有几种形式的用户界面和前端的表达形式，我们中的许多人都对在客户端采用Java技术有不好的印象，因此在客户端的开发中采用HTML似乎已经成为唯一的选择了。

    实际情况是，在客户端应用程序的开发中不止只有HTML一种选择，我们将在本文讨论基于Java的应用软件开发中三种用户界面的解决方案。我将首先讨论HTML与JSP和servlet联合使用的优点和缺点，然后讨论Java最初的目标之一：通过GUI Applet实现交互式互联网。最后，我们将讨论XML以及由它所衍生出来的其他技术。

    JSP/Servlets环境下的HTML客户端

    大多数读者都曾编写过servlet、开发过JSP应用，清楚基于HTML的用户界面的优点和缺点。选择HTML的最大的理由仍然是其广泛的平台适应性，基本HTML的标准性很好，虽然比较枯燥，但它可以很好地完成工作。

    由于HTTP/HTTPS协议非常简单，可以使开发的应用程序很好地适应各种网络配置和防火墻。但这是有代价的，HTML缺乏与用户的交互性，而且对用户每个行为的响应都需要与服务器进行连接。作为一名编程人员，我们一直在追求简单性，并使开发的软件可以适合所有的浏览器？然而简单性并不总是好的，简单地说，与静态HTML相比，JavaScript可以较好地实现不太复杂的交互性，但对于开发复杂的用户界面而言，它还是不能胜任的。

    除非拥有高速的互联网连接，否则你一定有过焦急的等待加载一个网页的经历。尽管瘦客户端提供了一些很好的非交互性的用户界面，但传统的胖客户终端一般情况下都比它们更聪明。例如，Netscape Communicator和MS Outlook等电子邮件的前端就比基于互联网的电子邮件工具具有更好的用户亲和性。

    用Java开发一个HTML前端应用是一个很好的选择，因为HTML提供了跨平台的内容传输能力。编程人员可以使用Java Servlets和JSP开发在任何操作系统平台上运行的服务器端应用程序。同时考虑到众多的Java API和它得到的广泛的服务器开发商的支持，对于开发可伸缩互联网站而言，Java是一种理想的选择。

    总而言之，配合使用Servlets和JSP的HTML前端开发工具是开发静态界面的很好的方式，但当需要对用户的行为作出快速反应和需要对数据进行高速处理的复杂应用时，这种方式则不大理想。


IBM WebSphere JSP源代码暴露漏洞

作者：asp3000

日期：2000-9-27 10:15:49

bugtraq id 1500

class Access Validation Error

cve GENERIC-MAP-NOMATCH

remote Yes

local Yes

published July 24, 2000

updated July 24, 2000

vulnerable IBM Websphere Application Server 3.0.21

- Sun Solaris 8.0

- Microsoft Windows NT 4.0

- Linux kernel 2.3.x

- IBM AIX 4.3

IBM Websphere Application Server 3.0

- Sun Solaris 8.0

- Novell Netware 5.0

- Microsoft Windows NT 4.0

- Linux kernel 2.3.x

- IBM AIX 4.3

IBM Websphere Application Server 2.0

- Sun Solaris 8.0

- Novell Netware 5.0

- Microsoft Windows NT 4.0

- Linux kernel 2.3.x

- IBM AIX 4.3

Certain versions of the IBM WebSphere application server ship with a vulnerability which allows malicious users to view the source of any document which resides in the web document root directory.

This is possible via a flaw which allows a default servlet (different servlets are used to parse different types of content, JHTML, HTMl, JSP, etc.) This default servlet will display the document/page without parsing/compiling it hence allowing the code to be viewed by the end user.

The Foundstone, Inc. advisory which covered this problem detailed the following method of verifying the vulnerability - full text of this advisory is available in the 'Credit' section of this entry:

"It is easy to verify this vulnerability for a given system. Prefixing the path to web pages with "/servlet/file/" in the URL causes the file to be displayed without being

parsed or compiled. For example if the URL for a file "login.jsp" is:

http://site.running.websphere/login.jsp

then accessing

http://site.running.websphere/servlet/file/login.jsp

would cause the unparsed contents of the file to show up in the web browser."


import和usebean是干嘛的。（你们怎么会认为这两者有什么共同点呢？）

作者：sonymusic

email: sonymusic@china.com

日期：7/31/2001 5:44:11 PM

[b][#ff0000]这是绝对的不同嘛。

[/#]1、import[/b]

import只是引用一个类或者一个包。这样在使用的时候就不必输入类的全名了。比如：java.util.Date d=new java.util.Date()，这样比较累，尤其是很多代码的时候。我们可以这样写：

import java.util.*;

// or

//import java.util.Date

Date d =new Date();

在JSP中使用IMPORT和在JAVA中使用没什么不同，

<%@ page import="java.util.*"%>

以下是JRUN生成的SERVLET代码；

import java.util.*;

[b]2、usebean

[/b]usebean实际上是创建或者得到类的一个实例。

看看JRUN为一个scope为page的usebean生成的SERVLET代码

<jsp:useBean id="myd" scope="page" class="Date"/>

servlet代码：

Date myd = (Date)pageContext.getAttribute("myd", PageContext.PAGE_SCOPE );

if(myd == null) {

    if(JRunJSPStaticHelpers.getAndSetBean(pageContext, "myd", Date.class  ,  PageContext.PAGE_SCOPE , 2)) {

        myd = (Date)pageContext.getAttribute("myd", PageContext.PAGE_SCOPE );

    }

    else {

        myd = (Date)pageContext.getAttribute("myd", PageContext.PAGE_SCOPE );

    }

}

pageContext.setAttribute("myd",myd);

现在应该明白有什么不同了吧。

sonymusic


Index Page

上一页 目录 下一页

目录

- Draw a pie chart
（2）
（3）
(翻译)如何成为一个优秀的jsp程序员
《国内不谈java》（转）
《学不会的JAVA，消不了的忧愁》之五——献给那些酷爱计算机编程的人们
About UBB with java has a bug.
Acme包中的以Post方式发送数据的例子。
allaire 发布jrun 3.0 rc1版
Apache Tomcat 重要信息暴露漏洞
Apache+Servlet+jsp
APACHE+TOMCAT+ORACLE安装指南(少爷原创)，因为很多有这方面的问题所以就贴出来
applet 代码2
ASP+和JSP之间的抉择（转载）
bea weblgic中介绍的一个jsp调用ejb的好例子，相当详细。
Beginner Using Servlets to display, insert and update records in database.(1)
Beginner Using Servlets to display, insert and update records in database.(2)
Beginner Using Servlets to display, insert and update records in database.(3)
C#、Java之比较
classpath详解(谨献给那些找不到北的朋友)
CLASS文件的存放位置
CONNECTING DATABASE USING JAVASERVERPAGES （转载）
Cookie 规范
CORBA例子（转）————————我也不知道是什麽东东-)
Declarations and Access Control (1)
Declarations and Access Control (2)
easy! 不好意思， 错按了回车
EJB概述(上)
EJB概述(下)
EJB内部资参1
EJB内部资参2
EJB内部资参3
EJB内部资参5
ejb一则，franzy@163.net多多交流哦
Finalists announced for JavaWorld Editors' Choice Awards
Flow control and exception Handling
ftp站点荟萃，你自己找找看看。不好意思！
Garbage Collection
Good Java Style Part 1
Good Java Style Part 2
HTML、Swing还是XML，Java客户端工具选择？(二) [转]
HTML、Swing还是XML，Java客户端工具选择？(三) [转]
HTML、Swing还是XML，Java客户端工具选择？(四) [转]
HTML、Swing还是XML，Java客户端工具选择？(一) [转]
IBM WebSphere JSP源代码暴露漏洞
import和usebean是干嘛的。（你们怎么会认为这两者有什么共同点呢？）
int 和 String 互相转换的多种方法 (转)
j2ee doc 翻译系列之二 （2 完）
j2ee doc 翻译系列之二
j2ee的jdbc配置指南（二 ） 连接sql server 数据库
J2EE技术
j2ee技术简单介绍
J2EE配置指南（1）
J2EE配置指南（2）
java applet 签名和认证 －－－大致过程和我的问题
java applet 源程序学习1
java bean 与 ejb的区别
Java Socket编程（二）
Java Socket编程（三）
Java Socket编程（三）-1
Java Socket编程（四）
Java Socket编程（五）
Java Socket编程（一）
Java Web Start 1.0 Architecture
Java Web Start 1.0 Developer's Guide (1)
Java Web Start 1.0 developer's guide (2)
java web start 1.0 developer's guide (3)
java web start 1.0 developer's guide (4)
Java Web Start 1.0 FAQ (2)
Java Web Start 1.0 FAQ (3)
Java Web Start 1.0 FAQ
Java 编程技术中汉字问题的分析及解决(转)
Java 编程技术中汉字问题的分析及解决(转自IBM)
Java 程序编码规范
java 技术分析
Java 线 程 的 讨 论 与 应 用 (转)
Java 在ClientServer 网络中的应用 (转)
Java 中对文件的读写操作之比较
JavaBean 与 Enterprise JavaBean：有什么不同？（转）
JavaBean持久化
JavaBean的事件
JavaBean的属性
JavaBean调用示例(转)
JavaBean用户化
Javajsp 好的入门文章共赏
Java2的安全新特性下的Applet数字签名具体实现方法
javamail中使用发信身份验证
JavaScript中如何判断一个字符串是否为合法日期
JavaServer Page(JSP)简介1
JavaServer Pages (JSP) 1.0简单介绍 ---III
JavaServer PagesTM 白皮书
Java布局管理器深入讨论
Java代码编写的一般性指导 (转贴)
JAVA的声音处理方法 (转自伊氏女人)
JAVA的网络功能与编程 二 （转）--建议放入精华区 ？ 因我等新手爱看
Java的网络功能与编程 一（转）---建议放入精华区
JAVA动态效果（1）表格时钟
java跟microsoft转！
Java混淆编译器（转apusic.com）
Java入门理解构造器 （转）
Java性能的优化（上）(转)
Java性能的优化（下）(转)
JAVA学习，是一条漫长的道路(转贴)
java学习体悟 作者： huli·yesky (转载)
Java与C／C++的区别(转)
Java语言中字符的处理
java在Linux下的web应用(二）转
java在Linux下的web应用(三)
java在Linux下的web应用(一)(转
Java正则表达式详解(上)
Java正则表达式详解(下)
java中关于html的转换
Java中利用散列表实现股票行情的查询(转)
Java中文问题详解(高手必读）
Java中文问题详解（转）
Java中文问题详解
java中有关日期的显示问题
java中怎样使用库中的文件(import 文件)
JAVA专业术语集
JB4中文教程
Jbuider4新特性介绍（二）
Jbuider4新特性介绍（三）
Jbuider4新特性介绍
JBuilder 4 概观 (转贴）
JBUILDER 5新增功能介绍
Jbuilder4.0 使用心得
JBuilder5 + Weblogic 6.0 安装配置全攻略（同事老魏写的）
jBuiler 4白皮书(来自inprise网站)
JCWC exam is available now
JDBC Connection Pool
JDBC 解决方案(转)
JDBC 解决方案
JDBC 入门
JDBC 示例（转）
jdbc3中的RowSet 接口规范 [边看边译]
JDBCTM 指南：入门
JDBCTM 指南：入门2 - 连接
JDBCTM 指南：入门3 - DriverManager
JDBCTM 指南：入门4 - Statement
JDBCTM 指南：入门5 - ResultSet
JDBCTM 指南：入门6 - PreparedStatement
JDBCTM 指南：入门7 - CallableStatement
JDBC和ODBC (转）
JDBC技术介绍 转
JDBC接口技术 转
JDK1.2关于JDBC中文问题的解决方案
Jive 中的设计模式 (续)
JMS资料（译文）(-)
Jrun 快速定位编译时期错误位置
JRun常见问题回答 (JRun 中文 FAQ)
JSP - FAQ (1)
JSP - FAQ (2)
JSP - FAQ (3)
JSP - FAQ (4)
JSP - FAQ (5)
Jsp + JavaBean循序渐进教程(二)
Jsp + JavaBean循序渐进教程(三)
Jsp + JavaBean循序渐进教程(四)上
Jsp + JavaBean循序渐进教程(四)下
Jsp + JavaBean循序渐进教程(一)
JSP Insider 5 在JSP中使用JavaMail--1
JSP Insider 5 在JSP中使用JavaMail---2
JSP Insider 5 在JSP中使用JavaMail---3
JSP Insider 5 在JSP中使用JavaMail---4
JSP 的模板
JSP 构 架--2种方式
JSP 项目设计中的方法论(1)转
JSP 项目设计中的方法论(2)转
jsp+beans实现读取数据库的例子
Jsp+JavaBean循序渐进教程(六)
Jsp+JavaBean循序渐进教程(五)
JSPServlet 中的汉字编码问题(转)
JSP9种基本内置组件（可与ASP的6种内部组件相对应）（转）
jspservlet中文问题的解决（转贴，好东西）
JSP标签库介绍 （1）转
JSP标签库介绍 （2）转
JSP标签库介绍 （3）转
JSP标签库介绍 （4）转
JSP传递参数方法
JSP的login程序代码
JSP的mysql_jdbc驱动程序
JSP的内部对象--JSP开发入门四
jsp调用oracle的存储过程
jsp发送邮件实例（高手必读）
JSP高级技术如何开发动态网站
jsp或者说JAVA倒底有多快？这里有一个计时类，可以帮你的忙。同时支持JAVA和JSP。内有例子。
jsp简介
jsp简介3
jsp简介4
JSP简明教程：行为标签与实例(转)
JSP里request变量列表
JSP例程 - 获取各种CGI环境变量(1.1版本)
JSP例程 - 获取各种CGI环境变量
JSP例程 - 在JSP中定义函数
JSP其他相关资源--JSP开发入门五
JSP入门
JSP实现在浏览器关闭cookies情况下的会话管理
JSP数据库操作例程 - 存储过程 - JDBC-ODBC - SQL Server - 1.1版本
JSP数据库操作例程 - 数据分页显示 - JDBC 2.0 - Oracle
JSP文件操作例程 - 读文件
jsp文件操作之读取篇
jsp文件操作之写入篇
jsp文件操作之追加篇
JSP由浅入深(1)—— 熟悉JSP服务器
JSP由浅入深(4)—— Scriptlets（2）
JSP由浅入深(4)—— Scriptlets
JSP由浅入深(5)—— Scriptlets和HTML的混合
jsp与applet通信
jsp与ejb通信 （转）
jsp与ejb通信
JSP与JavaBean --JSP开发入门三
JSP与Servlets的区别
JSP语法（1）——HTML注释
JSP语法（2）——隐藏注释
JSP语法（3）——声明
JSP语法（4）——表达式
JSP语法（5）——Scriptlet
JSP语法（6）——Page指令
JSP语法（7）——Taglib指令
JSP语法（8）——jspforward
JSP语法（9）——jspgetProperty
JSP语法（10）——jspinclude
JSP语法（11）——jspplugin
JSP语法（12）——jspuseBean
JSP语法（13）——jspuseBean
JSP语法，英文版，PDF格式
JSP语法的基本原理--JSP开发入门二
jsp源码实例1(输出）
jsp源码实例2(获取表单参数）
jsp源码实例3(获取jsp各种参数）
jsp源码实例4(cookie）
jsp源码实例4(搜索引擎）
JSP中变量的定义域
Jsp中的内部对象，及其所对应的JAVA对象
jsp中同时应用“存储过程”和“javabean”的例子
JSP中文留言簿
JSP中文问题解决方案。
js与applet的相互调用。
Language Fundamentals
Linux-JSP-JDBC-MySQL测试 - Insert
Linux-JSP-JDBC-MySQL测试 - Select
MD5算法（看在你是女孩的份上，给你一个吧）
MySQL的JDBC驱动程序下载地址
NT下安装JSP
Operators and Assignments(1)
Operators and Assignments(2)
Oracle中ID自增得方法(转自JSP001)
orcale测试实例
Orion下自定义Tag
Overloading overriding runtime type and object orientation (1)
Overloading overriding runtime type and object orientation (2)
PreparedStatement
Reading and Writing a Properties File(From Sun)
redhat6.1+apache+tomcat安装指南-jsp使用 (转)
RedHat7.0下安装Oracle8i
Resin服务器平台介绍
Resin与IIS结合的一些补充技巧
Resin中JSP的范例
Servlet Step by Step (1)
Servlet 和 XML：互为补充（二）（转）
Servlet 和 XML：互为补充（一）（转）
servlet2.3(续)
servlet2.3
Servlet和JSP的通信
Servlet基础例程 - HelloServlet - Linux版本
Servlet基础例程 - HelloServlet - NT版本
soap规范（完）
soap规范（1）
soap规范（2）
soap规范（3）
soap规范（4）
soap规范（5）
Sorry,我刚找到的：配置J2EE支持Mysql （转）
SSL與數位認證
Stream Tokenizing（分解字符串）
Sun Java Web服务器远程可执行命令漏洞
Threads
Timer Task 的简单范例(相当触发器,转自Sun)
Tomcat IIS HowTo将Tomcat装入IIS全攻略
TOMCAT+IIS配置方法
TomCat和Jakarta计划
Tomcat中文手册(1)_转
Tomcat中文手册(2)_转
url安全在 netscape6.0中
Use Java to implement the UBB future.
vaj 和 jbuid使用感受
Vector在Java编程中的应用
weblogic文档大全（URL）
weblogic6.0使用心得
WebSphere快速入门(1)
WebSphere快速入门(2)
WebSphere快速入门(3)
WebSphere快速入门(4)
WebSphere快速入门(5)
WebSphere快速入门(6)
WebSphere快速入门(7)
WebSphere快速入门(8)
WebSphere快速入门(9)
WebSphere快速入门(10)
WebSphere快速入门(11)
WebSphere快速入门(12)
WebSphere快速入门(13)
WebSphere快速入门(14)
WebSphere快速入门(15)
WebSphere快速入门(16)
WebSphere快速入门(17)
WebSphere快速入门(18)
WebSphere快速入门(19)
WebSphere快速入门(20)
WebSphere快速入门(21)
WebSphere快速入门(22)
WIN2000 下JSP环境的建立(IIS+resin+j2sdk)
windows环境下安装tomcat 修订版本
XML和J2EE的组合技术 (转)
XP 精华----如何使 Java 项目获得更大成功
安装 JRun
安装好你的机器来使用JSP（转）
安装好你的机器来使用JSP--JSP开发入门一
版主强烈推荐的问问题的方式。(因为很多朋友问的问题都很不清楚。)
本人在玩JB4的时候一些发现：(SONY老大。希望这是我的第一篇进JAVA版精华区的文章)
编写跨平台Java程序注意事项
补充两个有用的关于时间的函数
不难，请看javamail 发送附件的例子
不需ODBC可由IP地址与端口号建立与SQLSERVER的连接
初学jsp心得(一)
从数据库中读取并生成图片的Servlet
带JavaBean 的JSP ：
第二部分：EJB 编程模型（转）
第三部分：布署和使用 Enterprise JavaBeans 组件（二）（转）
第三部分：布署和使用 Enterprise JavaBeans 组件（一）（转）
第一部分：EJB 体系结构的历史和目标 （二）（转）
第一次jsp接触(win32安装版)
多线程程序设计 (转)
发邮件时终于可以通过sina的smtp验证了（附代码）
发主流技术的评价之JSP(转帖）
分别用java Application 及 JSP调用javaBean的例子
分布式对话服务器的管理（1）
分布式对话服务器的管理（2）
分布式对话服务器的管理（3）
分布式对话服务器的管理（4）
分布式对话服务器的管理（5）
分布式对话服务器的管理（6）
给javabeans的礼物一篇：JSP基本语法
给你个比较全的关于时间的操作的东西------
给你看看我的一个方法
给你一个SERVLET保治百病
给你一个servlet例子，JSP差不多
给你一篇ibm的介绍Structs的文章（1）
给所有刚学JAVA以及常来此版的兄弟
根据dtd读写xml实例
关于［开心就好］提出的“如何杀死一只鸡”的问题的讨论意见
关于cachedRowSet的一篇文章(java.sun.com)
关于finalize()方法的解释。
关于java 的 Classpath全解java入门之二
关于java 的 Classpath全解java入门之一
关于Servlet、Jsp中的多国语言显示 -- 转自高人
关于sessionListener
关于回答网友有关servlet和servlet之间通信的问题（建议放入精华区）
关于扰乱器与反编译 (转载)
关于中间件的问答实录（MIDWARE FAQ)
果果来看(一个封装了基本JDBC操作的类)
好东西介绍，直接用 java 命令行动态生成jpg文件
好久没来了，大家下载几个带源码的例子吧，BBS,Counter,guestbook,and so on
好文--Jive 中的设计模式 (转自IBM DeveloperWorks)
回答：为什么执行JAVA程序时，会出现Exception in threadmain java.lang.NoClassDefFoundError的错？（太多人问了，烦！建议放入精华区！）
回答以前网友提出时间的问题
回复★★★各位高手，win2000server下安装tomcat后，再安装apache有什么好处？？？
回复java新手的问题！
回复XML 和Java
回复奇怪问题：我把resin和iis结合起来以后，asp程序就不能用了，把源码都显示出来了？？怎么解决阿
回复请教各位,如何区分鼠标事件中的左键点击还是右键点击谢过先!
回复请问哪里有jboss有关配置的教程，最好是中文的，先谢了
回复如何在Web页上实现文件上传
回复谁能讲一讲setproperty(name=)的用法或介绍相关资料谢了!!
回复谁有用java mail 收 html 信件的程序呀？
回复有没有现成的class实现crypt算法的，或者源代码也可以的。
回复在jsp中作HTTP认证的方法,（希望版主可以把它贴到专题文章中）
基础知识 -- JavaDoc用法
基于MySQL的高性能数据库应用开发(使用C＋＋操作MYSQL)
几则JSP入门知识总结---像我这样的菜鸟朋友看过来：P
纪念在chinaasp积分过一百呕心原创一篇（Java 中对文件的读写操作之比较）拿分好难呀，555~~~，不知道那些几千分的老妖们是怎么灌水的。
简单的例子
簡易物件存取協定(转) -- 告诉你什么是SOAP,繁体
建议用JUNIT建立测试类
将 Microsoft 的 Internet Information Server 用作 Java servlet 引擎　二　（给学过ASP的朋友）
将 Microsoft 的 Internet Information Server 用作 Java servlet 引擎　一　（给学过ASP的朋友）
将 Microsoft 的 Internet Information Server 用作 Java servlet 引擎（转）
精彩专题，不能不看《微软 vs Sun》!!
具体见内！
看看String类的构造方法
看看也好：Java vs .NET 非官方观点
困扰JSP的一些问题
利用Java实现zip压缩解压缩
利用Java实现串口全双工通讯 (转载)
利用RamdonAccessFile来实现文件的追加！
利用UDP Sockets技术实现IP多点传送1（转中华技术网）
利用UDP Sockets技术实现IP多点传送2（转中华技术网）
两种INCLUDE方式的不同. (为什么出错,你自己看吧.)
了解Java，便了解了微软.NET
另一种bbs设计的思路，请各位高手给予评价。
浏览器中显示JApplet(可解决引入Swing包的问题))
论坛搜索《sm版》
内有一篇文章或许有用
内置加密软件的嵌入用途Java软件今秋问世
评论（选自CSDN)
签名一个使用插件1。3版的Java小程序 (作者：山水 欢迎转载 哈哈！~!@#$%^&）
浅谈4种类型的JDBC驱动程序
取时间的小类
全面研读 EJB 2.0
让JSP页面过期, 即保证每次的JSP页面都是最新的.
让我来教你吧，看这个例子：JSP数据库操作例程 - 数据分页显示 - JDBC 2.0 - Oracle
认 识 JINI (1 )
认 识 JINI （2）
认 识 JINI （3）
如何捕获视频。
如何捕获音频及输出音频。
如何成为一个优秀的jsp程序员
如何从mail server删除一条消息的例子
如何设置application 变量
如何用 servlet 打开非 HTML 格式的文档（转）
如何在 Java 应用程序中读取 8 位和 24 位 Microsoft Windows 位图（转）
如何在 jdbc 印出中文
如何制作透明组件。
三种Web开发主流技术ASP-PHP-JSP的评价[转载] --希望给大家作为参考
什么是Servlet以及Servlet的优越性
实体对象的抽象以及一种基于数据库的实现(转)
使用 Java 连接处于proxy(or 防火墙)之外的HTTP服务器
使用 XML 小服务程序配置文件配置小服务程序
使用Java Applet访问数据库（转载）
使用JavaBean高效处理JSP(1)
使用JavaBean高效处理JSP(2)
使用JavaBean高效处理JSP(3)
使用JavaBean高效处理JSP(4)
使用JAVA开发CORBA应用
使用JSP读取客户端信息
使用servlet和jsp上载文件
试验win2k在apache1.3上配置tomcat3.1
数据类型对软件性能的影响
台湾出的JSP电子版,内容很详细
提高Java代码可重用性的三个措施(1)
提高Java代码可重用性的三个措施(2)
提高Java代码可重用性的三个措施(3)
提高Java代码可重用性的三个措施
通过socket访问数据库
通用数据库操作BEAN
推出weblogic+oracle 分页
推荐几个好资源
往JRE里增加字体
为什么jsp不能取代servlet
我的面向对象程序观 (转)
我认为JSP有问题（上）
我认为JSP有问题（下）
无状态Enterprise JavaBeans的观点
小议如何在 Applet 中显示图象
小议如何在 Application 中显示图象
小议如何在组件上显示图象
新官上任，转贴一篇：Java做一个最简单的通话程序
新书快讯：Enterprise JAVABEANS（第二版）
续 太长了
学习JAVA的第一方案 （转贴）
也谈新郎、sohu新闻系统的技术，当然是怎么用jsp实现的!!!!!
一份 tomcat vs resin 的测试报告。。。
一个 Java，各自表述
一个applet---servlet通讯的例子---applet文件
一个applet---servlet通讯的例子---servlet文件
一个JDBC－ODBC桥的字符集参数的例子。
一个进行Base64编码的类。
一个连接池的例子（来自JIVE）（1）
一个连接池的例子（来自JIVE）（2）
一个连接池的例子（来自JIVE）（3）
一个连接池的例子（来自JIVE）（4）
一个连接池的例子（来自JIVE）（5）
一个连接池的例子（来自JIVE）（6）
一个连接池的例子（说明）
一个区分Bean的作用域的例子，即jspuseBean scope=xx的scope属性
一个最简单的 JavaBeanMaker(原创)
一些非常有用的JAVA常用方法(1)
一些非常有用的JAVA常用方法( 2)
以下这段servlet代码变为jsp大致的写法是。。。。。。
异常处理优劣观(转载)
用 Java 保存位图文件（转）
用 Java 保存位图文件
用 javabean 来实现 MySQL 的分页显示
用 JSP 在客户端生成 JavaScript 代码来实现表单校验
用 Servlets 动态生成图片
用javamail发邮件(含附件),用jBuilder3实现
用Java绘制K线 (转)
用Java绘制K线 (转2)
用java中的canendal 如何计算两个日期之间的每一日？
用JAVA转换简繁体的基础知识 (不用对照表，用算法实现)
用JDBC访问一个数据库
用JSP构建动态网站 (1)
用JSP构建动态网站 (2)
用JSP构建动态网站 (3)
用JSP构建动态网站 (4)
用JSP和数据库做的购物车的源程序：（欢迎大家指点！）
用JSP链接到数据库
用JSP实现的一个日历程序
用Swing实现数据表格功能（加入精华区吧）
用Swing制作欢迎屏幕 (转自赛迪网)
用连接池提高Servlet访问数据库的效率 (-)
邮件例程 - JavaMail - 发送HTML邮件
杂谈 By Haha
在JAVA应用程序中如何实现FTP的功能 (转)
在Java中获取系统属性
在JSP编译的时候，服务器内部做了什么？
在JSP页面中实现检索数据的分页显示
在jsp中发送email !
在JSP中写text文件
在Linux上安装Tomcat
在windows2000 server中文版上安装apache 原创
在Windows上安装Tomcat
在按钮上放图标
在浏览器关闭cookie情况下进行会话管理
在网页上发布统计曲线
怎样避免NullPointerExceptions（原创）
这是我刚才做出来的一个查询sqlserver，大家或许会体会一些东西
真巧, 我刚好做了一个扫描整个网段的多线程程序, 不过利用ping加参数
真正的apache，tomcat，mod_jk安装指南 原创
值得关注：Java 1.4 即将发布版本的性能和新功能一览(转自IBM developerWorks)
中间件与jsp
转：《学不会的JAVA，消不了的忧愁》之二——献给那些所有酷爱计算机编程的人们
转：《学不会的JAVA，消不了的忧愁》之三
转：《学不会的JAVA，消不了的忧愁》之四
转：第一部分：EJB 体系结构的历史和目标
转：学不会的JAVA，消不了的忧愁！（1）
转－关于多线程
转－类的深入研究
转－如何学习java
转帖：JAVA无用论
转载：java的文件对象操作
转载--微软98年关于MTS和EJB的比较说明文件.其实Sun也有类似的对比文件,我忘记在那里了!(1)
转载--微软98年关于MTS和EJB的比较说明文件.其实Sun也有类似的对比文件,我忘记在那里了!(2)
准备翻译j2ee的doc，不知道有没有意义，请大家讨论。先贴一个：配置指南
最大化JAVA代码的可重用性 (转)
最大限制地提高代码的可重用性（转）
最简单的时间格式类
最近比较烦（程序员版）转载（版权所有：gtth 原作 ）
1. Servlet的基础结构
2. 一个简单的Servlet--产生存文本
5.3 CORBA技术及实例
5.2 分布式应用开发技术概述


int 和 String 互相转换的多种方法 (转)

作者：jeru

email: jeru@163.net

日期：7/4/2001 3:49:44 PM

5.1 如何将字串 String 转换成整数 int?

A. 有两个方法:

1). int i = Integer.parseInt([String]); 或

i = Integer.parseInt([String],[int radix]);

2). int i = Integer.valueOf(my_str).intValue();

注: 字串转成 Double, Float, Long 的方法大同小异.

5.2 如何将整数 int 转换成字串 String ?

A. 有叁种方法:

1.) String s = String.valueOf(i);

2.) String s = Integer.toString(i);

3.) String s = "" + i;

注: Double, Float, Long 转成字串的方法大同小异.


j2ee doc 翻译系列之二 （2 完）

作者：freehero

email: jdxmoon@163.net

日期：2000-12-4 19:56:42

声明：本文档由freehero翻译，未经作者同意不能用于商业用途，您可以自由的复制和传播，文档只供您理解j2ee，因作者能力有限，建议您同时阅读英文文档，对于因为翻译的错误代来的后果作者不负任何责任，欢迎您来信讨论freehero@163.net（转载请保留）

    3。对于一个有很长的内容列表的组件连说，你有可能在打包的过程中遇到命令行的参数长度的限制

。避免的方法：把帮助的类加入到一个.jar库文件中，然后把这个.jar文件加入到应用程序的.ear文件中

。

    4。如果在一个含有ejb .jar文件的企业bean中有jndi名字，然后一个新的bean添加到这个同样的

.jar文件中。jndi的名字数据可能丢失。解决的方法：尽量在指定jndi名字之前完成ejb .jar文件。

    5。在新建一个ejb的向导的通用对话框中，类的名称有可能丢失。丢失的情况发生在你后退然后前进

的动作中。

    6。当你在核实的过程中新增文件的时候，有时候文件选择的状态有可能不正确。使用文件选择器选

择一个目录后，你可能没有新增正确的文件。解决的方法：通过在另外一个工具的中引用这个文件，来刷

新cache。例如：从file菜单，选择add ejb jar到应用程序，click 取消，然后到核实的对话框中。

    7。杂新增企业bean的响动中，如果选择了错误的类，然后你回退选择正确的项目，有时候这个选择

的类的container-managed persistence属性有可能不正确。解决的方法：或者重新启动向导，或者选择

另外一种类型的bean。

    安全问题的bug

    当用户重新定向到基于窗体的对话框页面的时候，如果没有注册（比如选择了浏览器的back），于是

会被拒绝。解决的方法：从新启动浏览器。

     jsp的bug

     在特定的条件下，server启动的时候加载servlets有可能失败。j2ee server启动的时候可以加载一

个servlet并且调用这个servlet的init方法。但是结果是不可预料的。这些加载的servlets包括在

web.xml文件的<load-on-startup元素。

     RMI-IIOP Bugs

     1。如果你的jdk安装了RMI-IIOP部分，j2ee将不能工作。解决的办法是安装一个干净的jdk。

     2。j2ee的客户和server不能在基于IIOP1.0 协议的orb的产品中实现。

    其他软件的bug

    1。在solaris平台上，如果环境变量没有包括setenv JIT_ARGS="bco=off"，j2ee jit将会引起

cloudscape的失败。

    2。在切换j2ee server从non-recoverable  的模式到recoverable模式之前，要注意首先运行

cleanup脚本。

    3。运行j2ee，jdk是十分必要的。在一些系统中当安装jdk是时候，用户可以选择stand-alone JRE的

模式。这样的模式不能运行j2ee。另外一个问题是：如果jre的root设置成jdk的安装目录，错误有可能发

生。在这种情况下，当你运行的时候一切是好的，但是deploy的时候，会有如下错误：

    java.rmi.RemoteException: Error processing ejb jar:access denied:

    (java.io.FilePermission e:\testjava\lib\ext read)

    4。当https服务没有启动的时候，j2ee server会打印一个错误，你可以忽略如果你不需要https。

    5。在企业bean中，如果父类的参数或者返回值不是serializabl的，在父类中不要调用

deserialization。要确定所有的父类是serializable。

    6。web组件的server.policy 文件和j2ee要符合。这个文件给出了

${com.sun.enterprise.home}/public_htm/ 下的权限。应用这个文件，遵从下面的步骤：

    a：在lib/security/server.policy文件中，删除下面的行：

    grant codeBase "file:${com.sun.enterprise.home}/public_html/-" {

        permission java.security.AllPermission;

    }

    b：替换删除的行的内容如下：

    grant codeBase "file:${com.sun.enterprise.home}/public_html/-" {

        permission java.lang.RuntimePermission "loadLibrary.*";

        permission java.lang.RuntimePermission "accessClassInPackage.*";

        permission java.lang.RuntimePermission "queuePrintJob";

        permission java.lang.RuntimePermission "modifyThreadGroup";

        permission java.io.FilePermission "<<ALL FILES>>", "read,write";

        permission java.net.SocketPermission "*", "connect";

        // "standard" properies that can be read by anyone

        permission java.util.PropertyPermission "*", "read";

        // set the JSSE provider for lazy authentication of app. clients.

        permission java.security.SecurityPermission "putProviderProperty.JSSE";

        permission java.security.SecurityPermission "insertProvider.JSSE";

};


j2ee doc 翻译系列之二

此电子文档属C#乐园QQ群3256045整理。
j2ee doc 翻译系列之二

上一页 目录 下一页

j2ee doc 翻译系列之二

作者：freehero

email: jdxmoon@163.net

日期：2000-11-29 18:03:36

声明：本文档由freehero翻译，未经作者同意不能用于商业用途，您可以自由的复制和传播，文档只供您理解j2ee，因作者能力有限，建议您同时阅读英文文档，对于因为翻译的错误代来的后果作者不负任何责任，欢迎您来信讨论freehero@163.net

JavaTM 2 SDK,Enterprise Edition 介绍

1。注意事项

    如果你第一次使用JavaTM 2 SDK, Enterprise Edition (J2EE SDK) ，你应该遵从下面的步骤：

    1。确认你安装了JavaTM 2 SDK, Enterprise Edition (J2EE SDK) 。

    2。配置你的JavaTM 2 SDK, Enterprise Edition (J2EE SDK) 软件。

    3。阅读  《开始j2ee》文档，开始建立年的ejb。

2。支持的平台

   SolarisTM Operating Environment, version 2.6

   Windows NT, version 4.0

3。支持的数据库和jdbc 驱动

    这个版本支持jdbc api的1.0和2.0版本的数据库驱动。我们在以下数据库上做了很好的测试：

    Oracle8 Server, version 8.05

    Microsoft SQL Server, versions 6.5, 7.0

    Cloudscape, version 3.0

    更多的jdbc信息，请参阅jdbc data access api web页面（

"http://www.java.sun.com/products/jdbc/index.html

http://www.java.sun.com/products/jdbc/jdbc.driver.html

有关jdbc 驱动的内容进行配置。如果你使用cloudscape 数据库，你不需要配置它的驱动，因为在本版本已经

包括了它的驱动。

4。本版本的内容包括：

    服务：

    EJB

    HTTP

    HTTP over SSL

    COS naming

    Authentication

    工具：

    Application Deployment Tool

    一个打包j2ee组件和发布应用程序的工具。

    Realm Tool

    这个工具管理j2ee  server 的用户。

    Verifier

    这个工具核实j2ee组件文件。

    Packager

    这个工具打包j2ee组件。

    Cleanup Script

    删除所有发布的j2ee的应用程序。

    Cloudscape 数据库

    本版本包括一个用java语言写的关系型数据库——cloudscape 3.0。尽管本文档例子的代码是用

cloudscape做测试的，但是你可以使用任何本版本支持的数据库。

    对企业的java apis

    Enterprise JavaBeans API, version 1.1

    Java Servlets, version 2.2

    JavaServer Pages technology, version 1.1

    JDBC Standard Extension, version 2.0

    Java Naming and Directory InterfaceTM API, version 1.2 (CosNaming SPI)

    RMI/IIOP

    Java Transaction API, version 1.0

    JavaMail API, version 1.1

    Java Messaging Service, version 1.0 (API only, no implementation)

5。需要的软件。

    首先，你需要安装JDK 1.2 。The JavaTM 2 SDK, Standard Edition 提供了运行和建立j2ee应用程序的

能力。如果你的client在另外一个机器上，client上也必须安装jdk1.2。

    能支持j2ee的jdk1.2如下：

    http://www.java.sun.com/products/jdk/1.2/download-solaris.htm

    http://www.sun.com/solaris/java

    ttp://http://www.java.sun.com/products/jdk/1.2/download-windows.html

    其他版本的jdk1.2没有做过测试，并且不支持j2ee。

    注意：jdk1.2中不能安装 The RMI-IIOP standard extension 。

6。当前的缺陷。

    本版本的目的是向你介绍j2ee技术，给你提供一个向我们反馈信息的机会，以便我们以后做的更好。本版

本中有几个缺陷。

    1。Container-Managed Persistence

    实现基于container-managed persistence 的entity beans不能完全实现所有的把物体影射到关系型数

据库的特性。

    entity bean 类只能影射到一个数据库中的表

    一个container-managed field 只能影射到一个表中的字段

    当容器装入container-managed fields 的时候，它回装入所有的container-managed fields 。当装入的

数据非常大的时候，性能回受到影响。因为有些数据是商业方法所不需要的。

    如果多个entity beans 的container-managed fields影射到数据库中同一个数据的时候，并且如果这些

beans在同一个事务中被引用，他们可能会对数据的一致性造成影响。

    在ejbCreate, ejbRemove, ejbLoad, and ejbStore 方法中包含sql语句的beans中，当用deploytool分布

它们的时候，你只能修改表和字段的名字，不能修改作为参数传入的数目和它们的顺序。

    在sql 语句中不能调用存储过程。

    在Create Table语句中，你可以更改表的字段的sql类型，以便和实际的数据类型相符。

    所有发生起来语句中的表名和字段的名字必须一致。

    通常的sql语句只在Cloudscape, Oracle, and Microsoft SQL Server上做了测试，其他的数据库你应该

自己测试。

    重复进行Result Set 时进行数据库调用

    对于一些数据库的驱动而言，在重复进行Result Set 时不能连接数据库。在下面的例子中，the

stmt.executeUpdate 会失败。

    String query = "SELECT DEPTNO FROM DEPT . . .";

    String update = "UPDATE EMPLOYEE . . .";

    ResultSet rs = stmt.executeQuery(query);

    while (rs.next()) {

       String s = rs.getString("DEPTNO");

       // The following statement will FAIL:

       stmt.executeUpdate(update);

    . . .

    }

    你可以通过以下几种方法绕开此限制：

    1。使用一个包含次sql的存储过程。

    2。把重复的语句定义成一个子查询，例如：

       String update = "UPDATE EMPLOYEE

       SET DEPTNO =

         (SELECT DEPTNO FROM DEPT

         WHERE LOC = `ATLANTA')

       WHERE TITLE = `ENGINEER'";

    3。把第二个sql语句放在另外一个方法中，然后调用另外一个方法，在这种情况下，必须注意另外一个的

方法事RequiresNew类型的事务。同时一应该注意第二个sql语句运行在另外的事务中。这样，如果第二个事物

失败的话，你应该使第一个事物

回滚。

    强壮性和内存问题

    我们预期在j2ee的server运行在内存小于128m的系统中，或者deploy的j2ee应用程序数目大于16的时候，

或者几千个jeb实例化的时候，系统会出现问题。如果你的内存溢出，shutdown并且重新启动j2ee server。

    通常的分布环境

    在这个版本中，每一个develper应该工作在自己的j2ee sdk环境中。多个developer不能共享一个j2ee

server。

    已知的bug

    分布的bug

    1。在寻找一个 web resource collectin 时键入url后应该键入回车以便确认数据已经发送

    2。当gui界面中的edit菜单被选中的情况：典型的情况时：树型结构看起来象是已经被选中，实际上另外

一个组件被选中了。因此，edit操作会对另外一个组件进行操作。

    3。


j2ee的jdbc配置指南（二 ） 连接sql server 数据库

此电子文档属C#乐园QQ群3256045整理。
j2ee的jdbc配置指南（二 ） 连接sql server 数据库

上一页 目录 下一页

j2ee的jdbc配置指南（二 ） 连接sql server 数据库

作者：freehero

email: jdxmoon@163.net

日期：2000-11-29 14:34:46

1。到http://www.inetsoftware.dedownload  Opta2000.zip  ,zip以后一个.jar文件和几个.java的测试例子。同时有一个xa目录，是j2ee的例子。

2.配置default.proprtise文件

2。cd  xa

3.javac -c %j2ee_classpath%;Opta2000.jar  EJB  HOme  Interface

4.deploy   it

5.mk  client

6.run client

详细的步骤在  inetsoftware.de  上有（j2se的配置）


J2EE技术

作者：luflying

email: zhang_yng@citiz.net

日期：2000-11-24 10:44:38

一. J2EE技术

什麽是Java2 platform,enterprise edition?

J2EE是美国Sun公司刚刚推出的一种全新概念的模型,与传统的互联网应用程序模型相比有着不可比拟的优势。

当今许多企业都需要扩展他们的业务范围,降低自身经营成本,缩短他们和客户之间的响应时间,这就需要存在一种简捷,快速的服务于企业,合作伙伴和雇员之间。

典型的说，提供这些服务的应用软件必须同企业信息系统（EIS）相结合，并提供新的能向更为广阔的用户提供的服务。这些服务要具备以下的特点：

a． 高可用性：来满足现在的全球商业环境

b． 安全性：保护用户的隐私和企业数据的安全

c． 可依赖性和可扩展性：保证商业交易的正确和迅捷

通常这些服务是由分布的应用程序组成的，包括前端数据端和后端数据源以及它们之间的一层或几层，这些中间层提供了把商业功能和数据与EIS相结合的功能。这些中间层把客户端从复杂的商业逻辑中分离出来，利用成熟的INTERNET技术使用户在管理上所花费的时间最小化。

J2EE正式降低了开发这种中间层服务的成本和复杂程度，因而使得服务可以被快

速的展开，并能够更轻松的面对竞争中的压力。

J2EE通过定义一种标准的结构来实现它的优势，如下：

a． J2EE Application Programming Model ----一种用于开发多层次，瘦型客户用户程

序的标准设计模型

b． J2EE Platform----一个标准的平台，用来整合J2EE的应用程序，指定一系列的

接口和方法

c． J2EE Compatibility Test Suite----一套兼容测试组件，用来检测产品是否同J2EE 平台兼容

d． J2EE Reference Implementation----用来示范J2EE的能力

2．J2EE 应用程序模型

J2EE是被设计为顾客，雇员，供应商，合作者提供企业级服务，这样的应用程序天生具有复杂性，他们要访问各种类型的数据并分发于大量的客户端。

为了更好的控制，管理这些应用程序，支持各种各样用户的商业功能要在中间层引入，中间层描述了一个被企业的信息技术部门紧紧控制的环境。

J2EE应用程序依靠于EIS层来存储企业的商业数据。这些数据和用来管理它的系统是企业中的核心。

最初,双层结构(client-server)应用程序模型许诺将提高伸缩性与更广阔的功能,但不幸的是,直接向用户提供EIS服务的复杂性和再每台用户计算机上安装和维护商业逻辑所引起的管理上的问题成了很大的限制。

而这些再双层结构中的限制在三层结构中已不存在了。

如今要求既开发服务的商业功能，还要开发访问数据库和其他资源的代码，这是很复杂的，因为每一种多层结构的服务器都有它自己的应用模型，因此组建一直富有经验的开发队伍将是困难的。另外，随着服务规模的扩大，为了降低开支和加快响应速度，也要经常对其基层代码进行修改。

J2EE应用模型定义了一种建筑模型来让多层应用程序实现服务，并消除了以上的问题，提供了可伸缩的，易访问的，易于管理的方法。

J2EE应用模型把实现多层结构服务的工作划分为二部分：

开发者实现商业和表达逻辑，以及由J2EE平台提供的标准的系统服务。

开发者可以依赖于这个平台为开发中间层服务中遇到的系统级硬件问题提供

解决方案

J2EE应用模型为中间层应用程序提供了编译一次，任意运行的特点（write once，run anywhere），这种标准模型最小化了培训开发人员的费用。

J2EE应用程序模型通过在建立多层应用程序中最小化其复杂程度，围简化和加速应用程序的开发迈出了重要的一步。

3．Java技术基础

J2EE应用程序模型起始于Java程序语言和Java虚拟机，他们提供的不断提高的便携性，安全性和开发人员能力的提高是应用程序模型的基础。

应用程序模型通常包括JavaBean组件模型，JavaBean是的为普通功能编写基于Java技术的代码更加容易，利用JavaBean的开发工具可以可视的定制，组合这些组件。

4．安全性

当其他的应用程序模型需要在每个程序中有特定平台的安全性时，J2EE平台环境使在开发时安全约束便已定义了。

J2EE平台定义了一种标准的公开的存取控制规则，当程序在企业平台上开发时就已被程序师定义和解释了。J2EE也需要提供一个标准的注册机制以便应用程序不用将这些注册机制和逻辑相混合，相同的工作要执行于大量的不同的环境中而并不需要改变源代码。

例如：J2EE应用程序开发人员可以指定几个安全级别（如，user，superuser，administrator），于是他们写出代码来检查当前用户权限的级别当用户访问数据时。在开发阶段，开发人员赋予多组用户适当的安全级，使应用程序能够容易的判断限制级在执行限制操作之前。

5．中间层

J2EE应用程序模型的一个主要优点便是在中间层的多层应用程序。在J2EE平台，中间层商业功能是由企业JavaBean实现的。

这些企业Beans允许服务开发者集中于商业逻辑，并且让EJB Server处理传送稳定的，可升级服务的复杂工作。

JavaServerPages 技术和Servlets向客户层提供了易于访问的Internet型服务的中间层功能。Jsp技术使用户接口开发者更加容易的提供向任何浏览器用户提供动态页面的服务。Servlets让基于Java技术的开发者有更大的自由来在Java语言中提供完全的动态服务。

6． 客户层

J2EE平台支持几种类型的客户。

许多J2EE服务被设计为浏览器用户服务，这些服务通过动态生成Html页和表单来同客户端实现交互，而Jsp和Servlets是通过让这些商业数据通过某种方式格式化，使客户端更容易来同它工作。这些客户端可以是运行于浏览器中的JavaApplet和基于Java技术的程序。

要注意的是安全是多层结构的关键部分，在J2EE中安全性总是通过平台和管理员来解决的。在大多案例中，服务方或客户方都不需要开发者与安全逻辑。

7． 基于Html页的客户

在Java2中可以实现向用户的浏览器直接提供动态Html页的服务，在JavaServerPage 技术中通过在Html页中插入Java代码动态生成页是一种很容易的事情。在一些例子中，某些服务需要一些综合的代码，者可以通过从Jsp中调用JavaBean的方法来实现。

8． J2EE声明

J2EE应用程序模型的一个重要的目标就是使应用程序最小化。实现这一点的一种方法是提高在J2EE平台上运行普通任务的负担，这些普通任务包括强制一个应用程序的安全目标，执行它的交易处理，链接它所需要的组件。

J2EE提供了一种简单的，公开的方式来说明这些行为。这些说明被分散的放在各部分代码中和开发描述中，开发描述是应用程序包的一部分，这些基于xml的说明使应用程序开发者不用修改任何组件就可以改变应用程序的作用。


j2ee技术简单介绍

作者：iamsun

email: 111@111.111

日期：2001-3-9 10:57:10

j2ee技术

    java2的企业版是一系列中间件服务组合起来的强健的系统，这使得服务器方应用程序开发人员的工

作非常简单。j2ee包括的技术如下：

    Enterprise JavaBeans (EJB)

    EJB定义了服务器方组件编写的规则和，同时定义了如何管理组件和应用程序以及他们之间如何通信

。EJB促进了组件交易市场的发展，在这里，组件提供商可以出售可重用的组件。

    Java Remote Method Invocation (RMI) and RMI-IIOP

    RMI允许内部通信并且提供和其他服务通信的能力。RMI-IIOP是使用IIOP协议的RMI的精简扩充，可

以和corba整合在一起。

    Java Naming and Directory Interface (JNDI)

    JNDI鉴别组件或者其他网络资源的位置。

    Java Database Connectivity (JDBC)

    JDBC是一个关系数据库的桥，允许操作关系型数据库

    Java Transaction API (JTA) and Java Transaction Service (JTS)

    JTA和JTS规范允许组件支持交易保护。

    Java Messaging Service (JMS)

    JMS 允许分布式对象异步通信。

    Java Servlets and Java Server Pages (JSPs)

    Servlets 和 JSPs 是网络组件，观念上符合面向请求/回应计算，象通过http协议和client端通信

。

    Java IDL.

    java IDL是sun公司实现基于java的corba技术。Java IDL 允许和其他语言的应用整合，同时也允许

基于corba的分布式计算。

    JavaMail

    JavaMail服务允许你通过你的java程序发送平台独立和协议独立的e-mail信息。

    Connectors

    Connectors 使的j2ee能够很好的和大型机系统上运行的高端应用整合在一起，比如ＥＲＰ系统。

    The Extensible Markup Language (XML)

    好几种j2ee技术都是用xml的组织和描述信息的，比如：EJB1.1 ,JSP等等。


J2EE配置指南（1）

作者：运气

email: webmaster@chinaspx.com

日期：2001-7-3 14:20:00

    1、介绍

    本文介绍J2EE环境的配置，所有涉及的文件都在J2EE安装目录下的oncfig目录，这些文件都是文本文件，你可以有任意的文本编辑器来编辑它们。

    通常情况下，你没有改变这些文件的必要。但是如果你使用的数据库驱动不是cloudscape的时候，你需要遵循本章的内容进行配置。

    2、JDBC驱动

    默认的J2EE安装后支持cloudscape数据库（一个sun公司提供的演示数据库），如果你的ejb使用的jdbc不是连接cloudscape数据库，那么你需要按照下面的知道进行配置。（如果你不能确定本版本的J2EE支持你使用的jdbc驱动，请到 11111 看相关信息。）

    3、驱动的位置

    你必须把驱动文件（.jar文件）copy到 $J2EE_HOME/lib/system目录下。并且保证该文件包含在J2EE_CLASSPATH 的环境变量中。

    4、J2EE_CLASSPATH 环境变量

    J2EE server通过JDBC驱动连接数据库。通通过J2EE_CLASSPATH 的环境变量定位驱动文件（.jar文件）的位置。你可以在启动J2EE前在命令行设置这个变量。但是我们推荐你在脚本中设置J2EE_CLASSPATH 变量。编辑用户配置文件在安装的过程中是必须的一步。在unix中，用户配置文件是 /bin/userconfig.sh，windows nt中是bin/userconfig.bat

    注意：你应该同时在CLASSPATH环境变量中去掉驱动文件（.jar文件）。

    5、JDBC 1.0 驱动

    为了配置jdbc1.0驱动，编辑config/default.properties 文件的jdbc.drivers 和 jdbc.datasources两个属性值。再次要注意的是驱动文件必须包含在J2EE_CLASSPATH环境变量中。

    jdbc.drivers 属性

    jdbc.drivers 属性的值是有一系列的冒号分割的jdbc驱动的类名。格式如下：

    jdbc.drivers=:: . . .

    例如：

      jdbc.drivers=oracle.jdbc.driver.OracleDriver:testutil.MyDriver

      jdbc.datasources 属性值

      jdbc.datasources 属性值由两部分组成，一个是datasource的jndi的名字，一个是数据库的url。典型的情况是：jndi名字是数据库的逻辑名字，rul指定数据库的实际位置。jndi名字和url都是在ejb编码的过程中指定的。rul的格式，请参照jdbc驱动提供商的文档。

      jdbc.datasources 的语法如下：

        jdbc.datasources=|||. . .

      |符号有两个作用。第一，它隔开几个jndi_datasource；第二，它隔开每一个jndi_datasource中的jndi_datasource> 和 元素.

      jndi_datasource元素的语法如下：

        jdbc/

    由jdbc关键字开头，jndi_name是在jndi目录中指定的datasouce名字。

    例如：

      jdbc.datasources=jdbc/Oracle|jdbc:oracle:thin@rtc:1521:acct|jdbc/MyDB|jdbc:test

    6、支持JTA的JDBC2.0驱动

      配置JTA的JDBC2.0的驱动，你需要设置以下属性的值：

        jdbc20.datasources

        xadatasource..jndiname

        xadatasource..classname

        xadatasource..dbuser

        xadatasource..dbpassword

        xadatasource..prop.

      同时你也必须在J2EE_CLASSPATH 环境变量中包括jdbc驱动文件。

        jdbc20.datasources 属性

        jdbc20.datasources 属性的值和jndi名字连接，用于定位datasouce的位置（它就是lookup方法的参数）。

        jdbc20.datasources 的语法如下：

jdbc20.datasources=||||的作用有两个，第一，隔开jndi_datasource 和并组成一对，第二，隔开每一对的jndi_datasource> 、元素。

         和 jndi_XA_datasource有相同的语法：

        jdbc/

        jdbc关键字开头。jndi_name是datasource名字，这个名字指向jndi目录。

        例如：

        jdbc20.datasources=jdbc/Merant|jdbc/XAMerant|jdbc/Finch|jdbc/XAFinch

        xadatasource属性

        xadatasource..jndiname 属性指定XA DataSource的jndi名字。语法如下：

          xadatasource..jndiname=jdbc/

       n代表每一组xadatasource。第一个jdbc2.0驱动n为0，下一个是1，2等等。jndi_XA_datasource元素和jdbc20.datasources 指定的值对应。The xadatasource..dbuser 和 xadatasource..dbpassword 属性指定数据库用户和密码。通常该用户具有管理员的权限（更多的信息请看 2222 The distributed.transaction.recovery ）。

        xadatasource..prop. 指定和特定的属性值关联的值。实际的值可能是jdbc驱动。

    例如：

        xadatasource.0.jndiname=jdbc/XAMerant

        xadatasource.0.classname=com.merant.sequelink.jdbcx.datasource.SequeLinkDataSource

        xadatasource.0.prop.url=jdbc:sequelink://mypc:5000/[Oracle]


J2EE配置指南（2）

作者：运气

email: webmaster@chinaspx.com

日期：2001-7-3 14:21:41

    7、事务

    你可以编辑config/default.properties 文件控制事务发生和超时设置。distributed.transaction.recovery 属性这个属性控制分布事物是否发生。这些事物可以发生的话，下面的条件必须满足：

    跨库操作时的事物范围。

    J2EE程序通过jdbc2.0 JTA驱动访问数据库

    当故障发生的时候，事物的各个的组成部分可以按照符合两段提交协议的方式提交或回滚。

    事物发生的时候，server重新启动后事物可以提交或回滚。

    distributed.transaction.recovery的值为true或者false。J2EE安装后默认为false。

    distributed.transaction.recovery=false

    transaction.time属性

    在应用container-managed 事物的ejb中，你可以控制事物超时的间隔通过设置transaction.timeout的值。例如：

      transaction.timeout=5

    在设置以后，如果事物在5秒内没有完成，J2EE事物管理器将回滚它。当J2EE第一次安装的时候，timeout的值时0代表没有超时。

    只有使用container-managed 管理事物的ejb受到timeout的影响。至于在bean-managed和JTA　transactions 中你可以引用 UserTransaction 的 setTransactionTimeout方法。同时你也可以在其他组建中引用setTransactionTimeout方法：比如servlets和jsp。

    8、端口

    J2EE需要tcp/ip端口。修改config目录下的相应文件可以修改端口号。下表是端口号和相应的文件。

    EJB 9191 ejb.properties http.port=9191

    HTTP 8000 web.properties port=8000

    HTTPS 7000 web.properties https.port=7000

    Naming and Directory 1050 orb.properties port=1050

    9191 端口用于提供client下载需要的stub类

    8000 提供http服务

    7000 提供https服务

    1050 基于orb的jndi名字服务使用的端口

    9、日志文件

    J2EE server有几个日志文件。默认这些文件在logs目录下。编辑config/default.properties 文件中的 log.directory 属性可以更改默认目录。

    log.directory=logs

    同时你也可以编辑default.properties 文件更改日志文件的名字。例如：你可以修改log.output 属性的值来修改the output.log 文件的名字。

    一般来说，日志文件决定于启动J2EE的模式。（single还是multiple VM (virtual machine) 模式） Single VM 日志文件默认的情况是Single VM 模式。在这种情况下，日志文件位于

      $J2EE_HOME///ejb

    logs是在default.properties 文件中log.directory 属性指定的。host元素是计算机名称。生成下列文件。

    system.out

    system.err

    event.log

    output.log

    error.log

    system.out 和system.err文件包括ejb中 System.out 和 System.err的输出。如果你运行J2EE -verbose数，输出被写到stdout（标准输出） 和stderr（标准错误。输出）。system.out 和 system.err日志文件不建。只有config/auth.properties 文件中audit属性值为true时audit.lo才创建。

    Multiple VM 日志

    如果你用J2EE -mutiVM启动server，ejb和http服务在自己的虚拟机以单独的服务启动。每一个depploy的应用程序也在自己的虚拟机中运行。日志文件和上述的一样，但是存放的目录不同。目录如下：

    EJB $J2EE_HOME//ejb/ejbd

    HTTP $J2EE_HOME//ejb/httpd

    deployed application $J2EE_HOME//ejb/

    web server日志

    编辑web.properties文件中的内容，可以改变由web server创建的日志文件的名字。

    10、安全

    未授权用户

     对于J2EE来说，当有未授权的用户试图调用ejb container的时候，J2EE必须存在一般意义上的未授权用户存在。在J2EE中这个用户名字是guest，密码是guest123。你可以在auth.properties 文件中修改用户名和密码。

    default.principal.name=guest

    default.principal.password=guest123

    Keystore密码

    如果用户使用公钥访问https，认证信息放在/.keystore 文件中。同时user-home是System.getProperty("user.home")的返回值。keystore是一个典型的密码文件。默认的密码是changeit。可以在web.properties 文件中修改keystore.password 属性的值来改变密码。

    ANYONE 角色

    在application deployment工具的security面板中，默认的情况下方法分配给ANYONE 角色。这样的结果 是所有的用户和组都可以调用它。如果你没有影射到一个特定的角色，每一个用户和用户都可以调用它。可以在auth.properties 文件中设置anyone.role.name 的值来改变默认anyone角色。

    钝化发生的条件：内存限制

    当ejb container开始钝化一个ejb的时候，它把ejb保存在附属存储中并且试图重新分配内存。默认的情况下，如果内存使用超过128m的时候钝化开始发生。可以编辑config/default.properties 文件中passivation.threshold.memory 属性的值来修改默认值。

    passivation.threshold.memory=128000000

值必须是一个正的整数。当你减少这个值的时候钝化现象回经常发生。

    jndi name server host

    如果jndi name server和J2EE server不在同一个机器上，你必须改变config/orb.properties 文件中host属性的值。

    host=localhost

    HTTP 文档根

    默认的情况下，http和https服务的文档是public_html。你可以编辑web.properties 文件中documentroot属性的值来改变它。

    documentroot=public_html/


java applet 签名和认证 －－－大致过程和我的问题

作者：netkey

日期：2001-5-25 11:44:10

在java中的 applet 的认证和签名 确实麻烦。。

 java.sun.com  的tutorial 上有用apple 在本地写一个文件的例子：

 http://java.sun.com/docs/books/tutorial/security1.2/tour1/

 但只能用AppletViewer 可以看:

 appletviewer http://java.sun.com/docs/books/tutorial/security1.2/tour1/example-1dot2/WriteFile.html

  又在[阿费]的帮助下找到可以用ie or Nescape 查看你的applet 的例子：

  http://www.intelligentsearch.org/namesearch_from_java.htm#_Toc490550108

大致过程如下：：

服务器端：

1。 Compile the java classes --写applet并编译。

    javac *.class

2.   Place the class files in a JAR file    --打包：

    jar cvf  yourjarname.jar  yourdir/*.class

    1). Create a Keystore and Keys for signing the JAR file

        keytool –genkey –keystore ist.keystore –alias IST

    2). Sign the JAR file                  －－对jar 签名

        jarsigner –keystore ist.keystore ist.jar IST

    3). Create the Public Key Certificate  －－创建公钥

        keytool –export –keystore ist.keystore –alias IST –file IstPubKey.cert

客户端：

   1。Import the Public Key Certificate －－导入公钥

      keytool –import –alias IST –file IstPubKey.cert –keystore client.store

   2。Modifing a policy file  －－修改 policy文件

   3。 Run the applet   --运行applet

   !!! 这还只能用Applet viewer 查看你的applet

用ie访问：

First, the <APPLET> tag will not be able to activate the Java Plug-in for use in IE. This tag should be substituted by the <OBJECT> tag

 －－－由于IE 的java Plug-in 不能被<APPLET>标签激活。你还要将他换成<OBJECT>标签。

 these changes to the HTML file can also be done with the help of HTMLConverter. You can go to download this software from http://java.sun.com/products/plugin/index.html.

---- 这样你还要去下一个HTMLConverter

---- 将经过转换的html 和 class 文件copy 到你的服务器上。

－－ 好了。终于可以享受一下成果了。

     用ie 访问：http://yourserver:youreport/WriteFile.html

     还不行？？

     －－IE 还要下载Plug-in 才能执行，

     在1小时加26分46秒后：

     kao ,终于在本地写了一个文件。

////////////////////////////////////////////

 感谢：

  【wmlf】 和 【阿费】提供的资料和帮助

////////////////////////////////////////////

 我的问题：

1。如果我写的applet，放在网上，

他还必需下载或自己修改java.security and java.policy

然后放到 ：

<java home>/lib/security 下（如果安装了jdk)

<use dir>/.java.policy (没有 jdk)

那不是太麻烦了？？？？

谁还会用你的applet ????

2。是不是我对applet 认证理解不对，，本来applet的认证没这样繁？？


java applet 源程序学习1

此电子文档属C#乐园QQ群3256045整理。
java applet 源程序学习1

上一页 目录 下一页

java applet 源程序学习1

作者：blackwhites

email: blackwhites@sohu.com

日期：2000-10-11 14:59:45

- Limit TextField input to a maximum length

 import java.awt.*;

 import java.awt.event.*;

 public class TextFieldWithLimit extends TextField

    implements KeyListener {

    private int  maxLength;

    public TextFieldWithLimit

           (String initialStr,int col,int maxLength) {

      super(initialStr,col);

      this.maxLength = maxLength;

      addKeyListener(this);

      }

    public TextFieldWithLimit (int col,int maxLength) {

      this("",col,maxLength);

      }

    public void keyTyped(KeyEvent e) {

      char c = e.getKeyChar();

      int len = getText().length();

      if (len < maxLength) {

         return;

         }

      else {

         if((c==KeyEvent.VK_BACK_SPACE)||

            (c==KeyEvent.VK_DELETE) ||

            (c==KeyEvent.VK_ENTER)||

            (c==KeyEvent.VK_TAB)||

             e.isActionKey())

            return;

          else {

            e.consume();

           }

          }

      }

    public void keyPressed(KeyEvent e) { }

    public void keyReleased(KeyEvent e) { }

    }


java bean 与 ejb的区别

作者：blackwhites

日期：2000-12-13 17:57:12

您现在可能已在使用 JavaBean，但还不了解它。如果有支持 Java 的浏览器，那么，在桌面上使用 JavaBean 就没有限制。使用的 Web 页面可以将 bean 作为小应用程序的一部分。您很快就会和作为浏览器可视部分的 JavaBean 交互，然后，那些 JavaBean 将与服务器上的 EJB 接口。这种能力也可以扩展到因特网和内部网。

JavaBean 和 Server Bean（通常称为 Enterprise JavaBean (EJB)）有一些基本相同之处。它们都是用一组特性创建，以执行其特定任务的对象或组件。它们还有从当前所驻留服务器上的容器获得其它特性的能力。这使得 bean 的行为根据特定任务和所在环境的不同而有所不同。

这开辟了巨大商机。因为 JavaBean 是与平台无关的，所以对于将来的解决方案，供应商可以轻易向不同用户推出其客户机方的 JavaBean，而不必创建或维护不同的版本。这些 JavaBean 可以与执行商业功能（例如订购、信用卡处理、电子汇款、存货分配、运输等）的 EJB 配合使用。这里有巨大潜力，而这正是组件代理（WebSphere Application Server 企业版）设计提供的那种潜力。

JavaBean 是一种组件，它在内部有接口或有与其相关的属性，以便不同人在不同时间开发的 bean 可以询问和集成。可以构建一个 bean，而在以后构造时将其与其它 bean 绑定。这种过程提供了先构建，然后重复使用的方法，这就是组件的概念。可以将这种单一应用程序部署成独立程序、ActiveX 组件或在浏览器中。

JavaBean 因其外部接口（即属性接口）而与纯对象不同。这种接口允许工具读取组件要执行的功能，将其与其它 bean 挂钩，以及将其插入其它环境。JavaBean 设计成对单一进程而言是本地的，它们在运行时通常可视。这种可视组件可能是按钮、列表框、图形或图表 - 但这不是必需的。

可执行组件

Server Bean 或 EJB 是部署在服务器上的可执行组件或商业对象。有一个协议允许对其进行远程访问或在特定服务器上安装或部署它们。有一系列机制允许它们将服务安全性、事务行为、并发性（由多个客户机同时访问的能力）和持久性（其状态可以保存多久）的主要方面授权给 EJB 服务器上其所在的容器。当安装在容器中时，它们获得各自的行为，该行为提供不同质量的服务，因此，选择正确的 EJB 服务器至关重要。这正是 IBM WebSphere 企业版的优势所在。

EJB 是设计成运行在服务器上，并由客户机调用的非可视远程对象。可通过多个非可视 JavaBean 构建 EJB。它们有一个部署描述符，其目的与 JavaBean 属性相同：它是以后可由工具读取的 bean 的描述。EJB 还独立于平台，一旦编写好，还可以在任何支持 Java 的平台（包括客户机和服务器）上使用。

因为 EJB 由诸如 IBM VisualAge for Java 这样的工具集生成，所以，它是基于服务器的对象，并用于远程调用。它们安装在 EJB 服务器上，并象调用其它 CORBA 远程对象那样获得进行调用的远程接口。

ActiveX 对象

可以将 JavaBean 部署成 ActiveX 对象，虽然 EJB 的代理也可以这样做，但是，因为 ActiveX 运行在桌面上，所以，EJB 本身不能成为 ActiveX 对象。要在与平台相关的、仅 Windows 平台上做到这一点，开发人员可以将 JavaBean 变换成 ActiveX 组件。

好处

EJB 的主要好处在于：构建 bean 时，bean 开发人员可以规定需要什么类型的行为，而不必规定如何去做。开发分为两部分：程序员开发 bean，然后验证：它可与构建工具一起工作，并包括标识所需服务质量行为种类的部署描述符。下一步，另一个程序员可以采用这个 bean，并使用读取 EJB 部署描述符的部署工具，然后将该 bean 安装到 Enterprise Java Server 上的容器中。在第二步中，部署工具采取一些操作 - 这可能意味着生成如状态保存代码，放入事务挂钩，或执行安全性检查这样的代码。所有这些操作由部署工具生成，bean 开发人员和部署人员可以是不同的人。

可以通过使用部署工具，将任何独立于平台的 JavaBean 改写成具有可靠服务质量、特定于平台的 EJB，以满足现有商业系统和应用程序的特定需求。这就是 EJB 服务器对集成系统、网络和体系结构如此重要的原因所在。

EJB 与 IBM WebSphere 企业版

在 IBM WebSphere 企业版中使用时，可以将 EJB 配置成被管理的商业对象。接受它们授权服务的容器是其安装到的容器。将 EJB 的持久性部分映射在数据或状态对象中。EJB 服务器为 EJB 提供不同的服务质量，选择正确的 EJB 服务器可能对满足完整的商业需求至关重要。“组件代理”功能极其健壮，该功能提供如负载均衡和支持服务器组中多台机器的高级功能。它还有大大超出 Enterprise Java Server (EJS) 规范所倡导的系统管理功能。因此，按照基本标准编写的 JavaBean 或 EJB 可以运行在使用“组件代理”功能的 WebSphere 企业版上，并获得那些所有的附加功能。

EJB 服务器还提供独特的特性和服务质量，而且不完全相同。IBM“组件代理”有一些强大特性 - 例如，可伸缩性，它允许开发人员将 EJB 部署到从小型系统到大型网络的不同类型服务器。开发人员可以从小处入手，例如，在一个部门中，首先在 LAN 的 Java 服务器上部署，一旦准备好，就知道可以将在那里创建的 JavaBean 和 EJB 部署到全球网络。然后，开发人员可以测试并熟悉这些 bean，试运行，制作样本等等。满意之后，开发人员可以通过将其移至高性能服务器，来大幅度扩大其规模。JavaBean 和 EJB 不受任何计算机体系结构边界的限制。它们用 Java 编写，可以运行在任何具有 Java 虚拟机的系统上，并可以使用任何 Enterprise Java Server (EJS) 来部署对象。因此，开发人员现在可以在方便的系统上构建，以后在方便的系统上部署，而不必是同一台或同样类型的机器。

IBM WebSphere 企业版支持将商业对象部署到多台服务器。EJB 作为商业对象集成到“组件代理”功能，并作为任何其它商业对象处理。因此，EJB 可以连接到所选的后端系统，并执行任何所需操作，以满足其商业需求。这就成为“组件代理”为 EJB 提供的基础设施。通过将“组件代理”用作 EJB 服务器，开发人员将能够继续使用当前旧有系统，并将其与电子商务接口一起提供。

为使 EJB 能在 WebSphere“组件代理”环境中工作，可以使用“组件代理”部署工具将其安装在一台或多台服务器上，然后将其添加到命名服务器，以便可以全局查找到它。任何可以访问公共命名服务器的人都可以找到它，找到其宿主，并可以在宿主上执行方法，同时创建 EJB。这就是“代理组件”要做的事。

示例

让我们举一个在 Web 购物站点上可以看到的电子购物车的例子。用户的购物车是一个 JavaBean。用户将货架上的商品放入购物车，这些商品本身是 JavaBean。它们全部可视，并且面向用户。结帐时，将用户购物车中的商品发送到服务器上的 EJB，该 EJB 执行一些必要的操作，如检查信用卡授权和可用额度，生成封条，或生成给发货部门的有关提什么货和发货地点的特殊指示 - 这就是商业程序已在进行的活动。

结束语

Bean 的全部意义不只是其现有能力，更在于其可以为商业提供的有竞争力的潜在能力。IT 设计师和应用开发人员现在可以将精力完全集中在商业逻辑，而将如事务、持久性和安全性的底层工作留给服务器。WebSphere 的“组件代理”功能将提供所有这些（还有后端访问）和对象事务管理器。


Java Socket编程（二）

作者：风之彩

email: zhonggb@163.net

日期：2001-6-29 14:44:36

Java面向连接的类

Sockets有两种主要的操作方式:面向连接的和无连接的.面向连接的sockets操作就像一部电话,他们必须建立一个连接和一人呼叫.所有的事情在到达时的顺序与它们出发时的顺序时一样.无连接的sockets操作就像是一个邮件投递,,没有什么保证,多个邮件可能在到达时的顺序与出发时的顺序不一样.

到底用哪种模式是邮应用程序的需要决定的.如果可靠性更重要的话,用面向连接的操作会好一些.比如文件服务器需要他们的数据的正确性和有序性.如果一些数据丢失了,系统的有效性将会失去.一些服务器,比如间歇性地发送一些数据块.如果数据丢了的话,服务器并不想要再重新发过一次.因为当数据到达的时候,它可能已经过时了.确保数据的有序性和正确性需要额外的操作的内存消耗,额外的费用将会降低系统的回应速率.

无连接的操作使用数据报协议.一个数据报是一个独立的单元,它包含了所有的这次投递的信息.把它想象成一个信封吧,它有目的地址和要发送的内容.这个模式下的socket不需要连接一个目的的socket,它只是简单地投出数据报.无连接的操作是快速的和高效的,但是数据安全性不佳.

面向连接的操作使用TCP协议.一个这个模式下的socket必须在发送数据之前与目的地的socket取得一个连接.一旦连接建立了,sockets就可以使用一个流接口:打开-读-写-关闭.所有的发送的信息都会在另一端以同样的顺序被接收.面向连接的操作比无连接的操作效率更低,但是数据的安全性更高.

SUN一直是网络建设的支持者,所以在Java中支持sockets就不足为奇了.实际上,Java降低了建立一个sockets程序的难度.每一个传输模式都被封装到了不同的类中.面向连接的类将会首先被我们讨论.

在Java中面向连接的类有两种形式,它们分别是客户端和服务器端.客户端这一部分是最简单的,所以我们先讨论它.

列表9.1列出了一个简单的客户端的程序.它向一个服务器发出一个请求,取回一个HTML文档,并把它显示在控制台上.

9.1一个简单的socket客户端

import java.io.*;

import java.net.*;

/**

* 一个简单的从服务器取回一个HTML页面的程序

* 注意:merlin是本地机器的名字

*/

public class SimpleWebClient {

public static void main(String args[])

{

try

{

// 打开一个客户端socket连接

Socket clientSocket1 = new Socket("merlin", 80);

System.out.println("Client1: " + clientSocket1);

// 取得一个网页

getPage(clientSocket1);

}

catch (UnknownHostException uhe)

{

System.out.println("UnknownHostException: " + uhe);

}

catch (IOException ioe)

{

System.err.println("IOException: " + ioe);

}

}

/**

*通过建立的连接请求一个页面,显示回应然后关闭socket

*/

public static void getPage(Socket clientSocket)

{

try

{

// 需要输入和输出流

DataOutputStream outbound = new DataOutputStream(

clientSocket.getOutputStream() );

DataInputStream inbound = new DataInputStream(

clientSocket.getInputStream() );

// 向服务器发出HTTP请求

outbound.writeBytes("GET / HTTP/1.0\r\n\r\n");

// 读出回应

String responseLine;

while ((responseLine = inbound.readLine()) != null)

{

// 把每一行显示出来

System.out.println(responseLine);

if ( responseLine.indexOf("") != -1 )

break;

}

// 清除

outbound.close();

inbound.close();

clientSocket.close();

}

catch (IOException ioe)

{

System.out.println("IOException: " + ioe);

}

}

}

回忆一个,一个客户端向一个正在监听的服务器socket发出一个连接.客户端的sockets是用Socket类建立的.下面的程序建立了一个客户端的socket并且连接到了一个主机:

Socket clientSocket = new Socket("merlin", 80);

第一个参数是你想要连接的主机的名称,第二个参数是端口号.一个主机名称指定了目的的名称.端口号指定了由哪个应用程序来接收.在我们的情况下,必须指定80,因为它是默认的HTTP协议的端口.另外的知名的端口列在表9.1中,看:

知名的端品:

echo 7

daytime 13

daytime 13

ftp 21

telnet 23

smtp 25

finger 79

http 80

pop3 110

因为Socket类是面向连接的,它提供了一个可供读写的流接口.java.io包中的类可以用来访问一个已连接的socket:

DataOutputStream outbound = new DataOutputStream(

clientSocket.getOutputStream() );

DataInputStream inbound = new DataInputStream( clientSocket.getInputStream()

);

一旦流建立了,一般的流操作就可以做了:

outbound.writeBytes("GET / HTTP/1.0\r\n\r\n);

String responseLine;

while ( (responseLine = inbound.readLine()) != null)

{

System.out.println(responseLine);

}

以上的小程序请求了一个WEB页面并且把它显示出来.当程序完成之后,连接必须关闭.

outbound.close();

inbound.close();

clientSocket.close();

注意socket流必须首先关闭.所有的的socket流必须在socket关闭之前关闭.这个小程序非常地简单,但是所有的客户端程序都必须遵首下面的基本的步骤:

1.建立客户端socket连接.

2.得到socket的读和写的流.

3.利用流.

4.关闭流.

5.关闭socket.

使用一个服务器端的socket只是有一点复杂,它将在下面讲到.


Java Socket编程（三）

此电子文档属C#乐园QQ群3256045整理。
Java Socket编程（三）

上一页 目录 下一页

Java Socket编程（三）

作者：风之彩

email: zhonggb@163.net

日期：2001-6-29 14:50:30

服务器Sockets

列表9.2是一个服务器应用程序的一部分.

列表9.2 一个简单的服务器程序

/**

* 一个监听端口并提供HTML文档的程序.

*/

class SimpleWebServer {

public static void main(String args[])

{

ServerSocket serverSocket = null;

Socket clientSocket = null;

int connects = 0;

try

{

{

// 建立一个服务器socket

serverSocket = new ServerSocket(80, 5);

while (connects < 5)

{

// 等待连接

clientSocket = serverSocket.accept();

//服务连接

ServiceClient(clientSocket);

connects++;

}

serverSocket.close();

}

catch (IOException ioe)

{

System.out.println("Error in SimpleWebServer: " + ioe);

}

}

public static void ServiceClient(Socket client)

throws IOException

{

DataInputStream inbound = null;

DataOutputStream outbound = null;

try

{

// 得到IO流

inbound = new DataInputStream( client.getInputStream());

outbound = new DataOutputStream( client.getOutputStream());

//格式化输出(回应头和很少的HTML文档)

StringBuffer buffer = PrepareOutput();

String inputLine;

while ((inputLine = inbound.readLine()) != null)

{

//如果到了HTTP请求的尾部,就发送回应

if ( inputLine.equals("") )

{

outbound.writeBytes(buffer.toString());

break;

}

}

}

finally

{

// 清除

System.out.println("Cleaning up connection: " + client);

tln("Cleaning up connection: " + client);

outbound.close();

inbound.close();

client.close();

client.close();

}

}


Java Socket编程（三）-1

作者：风之彩

email: zhonggb@163.net

日期：2001-6-29 14:50:58

服务器Sockets

服务器并不是主动地建立连接.相反地,他们是被动地监听一个客户端的连接请示然后给他们服务.服务器是由类ServerSocket来建立的.下面的程序建立了一个服务器端socket并把它绑定到80端口:

ServerSocket serverSocket = new ServerSocket(80, 5);

第一个参数是服务器要监听的端口.第二个参数是可选的.API文档中说明了这是一个监听时间,但是在传统的socket程序中第二个参数是监听深度.一个服务器可以同时接收多个连接请求,但是每次只能处理一个.监听堆是一个无回答的连接请求队列.上面的请求建立一个连接来处理最后五个请求.如果省略了后面的一个参数,则默认值是50.

ServerSocket serverSocket = new ServerSocket(80, 5);

一旦socket建立了并开始监听连接,进来的连接将会建立并放在监听堆.accetp()方法把在堆中的连接取出来.

Socket clientSocket = serverSocket.accept();

这个方法返回一个用来与来访者对话的客户端连接.服务器本身不可能建立对话,相反地,服务器socket会使用accept()方法来产生一个新的socket.服务器socket依旧打开并排列新的连接请求.

与客户端socket一样,下面的一步建立输入和输出流:

DataInputStream inbound = new DataInputStream( clientSocket.getInputStream() ); DataOutputStream outbound = new DataOutputStream( clientSocket.getOutputStream() );

一般的I/O操作可以在新建的流中运用.在服务器回应前它等待客户端发送一个空白的行.当会话结束时,服务器关闭流和客户端socket.如果在队列中没有请示将会出现什么情况呢?那个方法将会等待一个的到来.这个行为叫阻塞.accept()方法将会阻塞服务器线程直到一个呼叫到来.当5个连接处理完闭之后,服务器退出.任何的在队列中的呼叫将会被取消.

所有的服务器都要有以下的基本的步骤:

1.建立一个服务器socket并开始监听.

2.使用accept()方法取得新的连接.

3.建立输入和输出流.

4.在已有的协议上产生会话.

5.关闭客户端流和socket.

6.回到第二步或者到第七步.

7.关闭服务器socket.


Java Socket编程（四）

作者：风之彩

email: zhonggb@163.net

日期：2001-6-29 14:51:19

重复和并发服务器

所有的这些调用都可以掷出一个UnknownHostException违例.如果一台计算机没有连接上DNS服务器,或者主机的确没有找到,这个违例就会被掷出.如果一台计算机没有一个激活的TCP/IP配置,getLocalHost()也为失败并掷出一个违例.

一旦一个地址被确定了,数据报就可以被送出了.下面的程序传输了一个字符串给目的socket:

String toSend = "This is the data to send!");

byte[] sendbuf = new byte[ toSend.length() ];

toSend.getBytes( 0, toSend.length(), sendbuf, 0 );

DatagramPacket sendPacket = new DatagramPacket( sendbuf, sendbuf.length,

addr, port);

clientSocket.send( sendPacket );

首先,字符串必须被转换成一个字节数组.然后,一个新的DatagramPacket实例必须被建立.注意构建器的最后两个参数.因为要发送一个包,所以地址和端口必须被给定.一个applet可能可以知道它的服务器的地址,但是服务器如何知道它的客户机的地址呢.当任何一个包被收到后,返回的地址和端口会被解压出来,并通过getAddress()和getPort()方法得到.这就是一个服务器如何回应一个客户端的包:

DatagramPacket sendPacket = new DatagramPacket( sendbuf, sendbuf.length,

recvPacket.getAddress(), recvPacket.getPort() );

serverSocket.send( sendPacket );

不像面向连接的操作,数据报服务器服务器其实比数据报客户端更简单:

数据报服务器

一个数据报服务器的基本步骤:

1.在一个指定的端口上建立一个数据报socket.

2.用receive方法等待进来的包.

3.用特定的协议来回应收到的包.

4.回到第二步或继续第二步.

5.关闭数据报socket.

列表9.3演示了一人简单的数据报回应服务器.它将回应它收到的包.

列表9.3.一个简单的数据报回应服务器

import java.io.*;

import java.net.*;

public class SimpleDatagramServer

{

public static void main(String[] args)

{

DatagramSocket socket = null;

DatagramPacket recvPacket, sendPacket;

try

{

socket = new DatagramSocket(4545);

while (socket != null)

{

recvPacket= new DatagramPacket(new byte[512], 512);

socket.receive(recvPacket);

sendPacket = new DatagramPacket(

recvPacket.getData(), recvPacket.getLength(),

recvPacket.getAddress(), recvPacket.getPort() );

socket.send( sendPacket );

}

}

catch (SocketException se)

{

System.out.println("Error in SimpleDatagramServer: " + se);

}

catch (IOException ioe)

{

System.out.println("Error in SimpleDatagramServer: " + ioe);

这个应用程序被当作一个重复的服务器.因为它只有在处理完一个进程以后才会接受另一个连接.更多的复杂服务器是并发的.它为每一个请求分配一个线程,而不是来一个处理一个.所以看起来它在同时处理多人请求.所有的商业的服务器都是并发的服务器.

Java数据报类

不像面向连接的类,数据报的客户端和服务器端的类在表面上是一样的.下面的程序建立了一个客户和服务器商的数据报sockets:

DatagramSocket serverSocket = new DatagramSocket( 4545 );

DatagramSocket clientSocket = new DatagramSocket();

服务器用参数4545来指定端口号,由于客户端将要呼叫服务器,客户端可以利用可利用的端口.如果省略第二个参数,程序会让操作系统分配一个可用的端口.客户端可以请求一个指定的端口,但是如果其它的应用程序已经绑定到这个端口之上,请求将会失败.如果你的意图不是作为一个服务器,最好不要指定端口.

由于流不能由交谈得到,那么我么如何与一个数据报Socket进行对话.答案在于数据报类.

接收数据报

DatagramPacket类是用来通过DatagramSocket类接收和发送数据的类.packet类包括了连接信息和数据.就如前面所说的一样,数据报是自身独立的传输单元.DatagramPacket类压缩了这些单元.下面的程序表示了用一个数据报socket来接收数据:

DatagramPacket packet = new DatagramPacket(new byte[512], 512); clientSocket.receive(packet);

clientSocket.receive(packet);

packet的构建器需要知道将得到的数据放在哪儿.一个512字节的缓存被建立并且作为构建器的第二个参数.每二个构建器参数是缓存的大小.就像ServerSocket类的accept()方法一样,receive()方法在数据可用之前将会阻塞.

发送数据报

发送数据报是非常地简单地,所有需要的只是一个地址.地址是由InetAddress类来建立的.这个类没有公共的构建器,但是它有几个static的方法,可以用来建立这个类的实例.下面的列表列出了建立InetAddress类的实例的方法:

Public InetAddress Creation Methods

InetAddress getByName(String host);

InetAddress[] getAllByName(String host);

InetAddress getLocalHost();

得到本地主机的地址是非常地有用的,只有前面两个方法是用来发送数据包的.getByName()和getAllByName()需要目的主机的地址.第一个方法仅仅只是返回第一个符合条件的东西.第二个方法是必须的,因为一台计算机可能有多个地址.在这种情况下,这台计算机被称为multi-homed.

所有的建立的方法都被标记为static.它们必须像下面这样得到调用:

InetAddress addr1 = InetAddress.getByName("merlin");

InetAddress addr2[] = InetAddress.getAllByName("merlin");

InetAddress addr3 = InetAddress.getLocalHost();


Java Socket编程（五）

作者：风之彩

email: zhonggb@163.net

日期：2001-6-29 14:52:11

简单的WEB服务器

一个简单的WEB服务器将由列表9.2这样构建.当然,还必须要对方法和回应事件进行改进.简单的服务器不会分析和存储请求头.新的WEB服务器将分析和存储请求,为以后的处理作准备.为了达到这个目的,你必须有一个包含HTTP请求的类.

HTTPrequest类

列表9.5列出了一个完整的HTTPrequest类.这个类必须包括一个请求头所需的所有信息.

列表9.5.HTTPrequest类.

import java.io.*;

import java.util.*;

import java.net.*;

import NameValue;

/**

* 这个类有一个HTTP请求的所有信息

*/

public class HTTPrequest

{

public String version;

public String method;

public String file;

public Socket clientSocket;

public DataInputStream inbound;

public NameValue headerpairs[];

/**

* 建立一个这个类的实例

*/

public HTTPrequest()

{

version = null;

method = null;

file = null;

clientSocket = null;

inbound = null;

inbound = null;

headerpairs = new NameValue[0];

}

/**

* 加入一个名称/值对到核心数组

*/

public void addNameValue(String name, String value)

{

try

{

NameValue temp[] = new NameValue[ headerpairs.length + 1 ];

System.arraycopy(headerpairs, 0, temp, 0, headerpairs.length);

temp[ headerpairs.length ] = new NameValue(name, value);

headerpairs = temp;

}

catch (NullPointerException npe)

{

System.out.println("NullPointerException while adding name-value:

" + npe);

}

}

/**

* 以字符串的形式归还这个类

*/

public String toString()

{

String s = method + " " + file + " " + version + "\n";

for (int x = 0; x < headerpairs.length; x++ )

s += headerpairs[x] + "\n";

return s;

}

}

NameValue类简单地存储了两个字符串:name 和 value.当一个新的对要被加入时,一个新的数组将被分配.新的数组接受了旧的数组和新的成员.旧的数组然后被一个新建的对象覆盖了.


Java Socket编程（一）

作者：风之彩

email: zhonggb@163.net

日期：2001-6-29 14:38:09

Socket传输模式

Sockets有两种主要的操作方式:面向连接的和无连接的.面向连接的sockets操作就像一部电话,他们必须建立一个连接和一人呼叫.所有的事情在到达时的顺序与它们出发时的顺序时一样.无连接的sockets操作就像是一个邮件投递,,没有什么保证,多个邮件可能在到达时的顺序与出发时的顺序不一样.

到底用哪种模式是邮应用程序的需要决定的.如果可靠性更重要的话,用面向连接的操作会好一些.比如文件服务器需要他们的数据的正确性和有序性.如果一些数据丢失了,系统的有效性将会失去.一些服务器,比如间歇性地发送一些数据块.如果数据丢了的话,服务器并不想要再重新发过一次.因为当数据到达的时候,它可能已经过时了.确保数据的有序性和正确性需要额外的操作的内存消耗,额外的费用将会降低系统的回应速率.

无连接的操作使用数据报协议.一个数据报是一个独立的单元,它包含了所有的这次投递的信息.把它想象成一个信封吧,它有目的地址和要发送的内容.这个模式下的socket不需要连接一个目的的socket,它只是简单地投出数据报.无连接的操作是快速的和高效的,但是数据安全性不佳.

面向连接的操作使用TCP协议.一个这个模式下的socket必须在发送数据之前与目的地的socket取得一个连接.一旦连接建立了,sockets就可以使用一个流接口:打开-读-写-关闭.所有的发送的信息都会在另一端以同样的顺序被接收.面向连接的操作比无连接的操作效率更低,但是数据的安全性更高.

SUN一直是网络建设的支持者,所以在Java中支持sockets就不足为奇了.实际上,Java降低了建立一个sockets程序的难度.每一个传输模式都被封装到了不同的类中.面向连接的类将会首先被我们讨论.

Sockets有两种主要的操作方式:面向连接的和无连接的.面向连接的sockets操作就像一部电话,他们必须建立一个连接和一人呼叫.所有的事情在到达时的顺序与它们出发时的顺序时一样.无连接的sockets操作就像是一个邮件投递,,没有什么保证,多个邮件可能在到达时的顺序与出发时的顺序不一样.

到底用哪种模式是邮应用程序的需要决定的.如果可靠性更重要的话,用面向连接的操作会好一些.比如文件服务器需要他们的数据的正确性和有序性.如果一些数据丢失了,系统的有效性将会失去.一些服务器,比如间歇性地发送一些数据块.如果数据丢了的话,服务器并不想要再重新发过一次.因为当数据到达的时候,它可能已经过时了.确保数据的有序性和正确性需要额外的操作的内存消耗,额外的费用将会降低系统的回应速率.

无连接的操作使用数据报协议.一个数据报是一个独立的单元,它包含了所有的这次投递的信息.把它想象成一个信封吧,它有目的地址和要发送的内容.这个模式下的socket不需要连接一个目的的socket,它只是简单地投出数据报.无连接的操作是快速的和高效的,但是数据安全性不佳.

面向连接的操作使用TCP协议.一个这个模式下的socket必须在发送数据之前与目的地的socket取得一个连接.一旦连接建立了,sockets就可以使用一个流接口:打开-读-写-关闭.所有的发送的信息都会在另一端以同样的顺序被接收.面向连接的操作比无连接的操作效率更低,但是数据的安全性更高.

SUN一直是网络建设的支持者,所以在Java中支持sockets就不足为奇了.实际上,Java降低了建立一个sockets程序的难度.每一个传输模式都被封装到了不同的类中.面向连接的类将会首先被我们讨论.


Java Web Start 1.0 Architecture

作者：diego lynn

日期：2001-3-25 10:26:21

Version 1.0

ARCHITECTURE

JavaTM Web Start is designed on the premise the most people still want applications for many activities. Most users are not dreaming of giving up their favorite traditional word processor, spreadsheet application, or email client for some HTML-based interface in a browser. The application interface has a number of benefits that are very appealing:

The user interface is very rich and responsive

Applications are easy to launch from Start Menu, Desktop, or by other means - and do not necessarily require the Web browser to be running

The speed and responsiveness of the application does not depend on the connection speed

Applications work offline, for example, in a plane when traveling

However, traditional applications also have a number of problems. In particular, applications are often complicated to install, and even worse to upgrade.

For companies that maintain hundreds or even thousands of desktop computers for their employees, keeping software up-to-date on these computers is typically a major headache. These problems have encouraged many companies to look into using HTML-based applications because of their simple and cost-effective deployment.

Java Web Start technology, the innovative technology for deploying applications based on the Java 2 platform, enables you to launch full-featured applications via any browser, on any platform, from anywhere on the Web, in a secure fashion. It provides the best of both worlds: The ease of deployment and use of HTML, as well as the power and flexibility of a full-fledged application.

The figure below shows how Java Web Start technology works both from a user and technical perspective:

The yellow arrow shows the user experience. A single click on a link launches a full-featured Java technology-based application, which may never have been present on the local computer before.

What really happens behind the scenes is shown by the gray arrows:

When the user clicks on a download link, the link instructs the browser to invoke Java Web Start technology. Java Web Start technology presents a splash screen.

Java Web Start technology queries the Web to determine if all the resources needed for the application are already downloaded. If they are, and the most recent version of the application is present, the application will be launched immediately (step 3).

If the resources are not present or an update is available, Java Web Start will download the needed resources. Thus, the initial download and subsequent updates of an application happen transparently.

The steps described above are very similar to how other helper applications such as Real Audio or Acrobat Reader work. Java Web Start technology makes the Java 2 platform available to the browser, so you can launch any web-based application simply and securely.

The only prerequisite is to download and install Java Web Start software on the client machine. This is a one-time download and, once done, installing and upgrading applications might be a thing of the past.

When to use Java Web Start Technology:

Given the nature of many productivity tools and traditional client-side applications, providing an HTML-based front-end to the application or service is not adequate.

For example, Web-based e-mail has been widely successful. It is a convenient tool when the volume of e-mail is relative low. Because most corporate users of e-mail and many individuals receive high volumes of e-mail, typically dozens a day, using an HTML-based interface would prove cumbersome and ineffective.

With Java Web Start technology, which works with virtually all Web servers, the application service providers (ASP), either internally to the company or externally on the Web, can easily supply a full-featured application to users. Initially, using the application version is slower, since it needs to be downloaded. This will typically take in the order of minutes, which is high compared to the order of seconds for HTML. However, this is only a "first-time activation" cost. For subsequent uses, the application is cached locally and launches as quickly as any other local application.

Therefore, many services can be greatly improved by providing a full-fledged application interface. The first-time activation cost is higher than with HTML, but that time is later saved by the enhanced features and better user experience, especially for power users.

Java Web Start technology is an ideal companion to HTML-based clients. A service can provide a simple and easily accessible interface using HTML, while also providing a rich experience for power users using Java Web Start technology. The ease-of-use for users is virtually identical -- except for the first-time activation cost -- and the management and server side requirements for both solutions are the same.

Features of Java Web Start Technology:

Applications written on the Java 2 platform for deployment with Java Web Start are always up-to-date and available. Every application runs in a dedicated Java Runtime Environment (JRE), independent of a specific browser or computer platform technology. Java Web Start supports:

Multiple JREs

Code-signing

Sandboxing

Versioning and incremental updates

Desktop integration

Offline operation

Automatic installation of JREs and optional packages Application launcher

Java Web Start technology provides a rich set of features that give easy access to the latest versions of applications for the end-user, easy management and deployment of applications for the IT department, and easy development for the application vendors.


Java Web Start 1.0 Developer's Guide (1)

此电子文档属C#乐园QQ群3256045整理。
Java Web Start 1.0 Developer's Guide (1)

上一页 目录 下一页

Java Web Start 1.0 Developer's Guide (1)

作者：diego lynn

日期：2001-3-25 10:51:51

Developer's Guide

Java TM Web Start

Version 1.0

This developer's guide provides documentation on how to deploy applications using Java TM Web Start and the JNLP technology.

 Please send comments and feedback to javawebstart-feedback@eng.sun.com.

Table of Contents

Java Web Start Technology

Where to find Java Web Start

Requirements (desktop and server)

Setting up the Web site

Application Development Considerations

Converting JNLP files to work with this release

JNLP File Syntax

JNLP API Examples

JNLP API (JavaDoc)

Java Web Start Technology

Java Web Start is a helper application that gets associated with a Web browser. When a user clicks on a link that points to a special launch file (JNLP file), it causes the browser to launch Java Web Start, which then automatically downloads, caches, and runs the given Java Technology-based application. The entire process is typically completed without requiring any user interaction, except for the initial single click.

From a technology standpoint, Java Web Start has a number of key benefits that make it an attractive platform to use for deploying applications:

Java Web Start is built exclusively to launch applications written to the Java 2 SE platform. Thus, a single application can be made available on a Web server and then deployed on a wide-variety of platforms, including Windows 95/98/NT/2000, Linux, and Solaris. The Java platform has proven to be a very robust, productive, and expressive development platform, leading to a significant cost savings due to minimized development and testing costs.

Java Web Start supports multiple revisions of the Java 2 platform, Standard Edition. Thus, an application can request a particular version of the platform it requires, such as Java 2 SE 1.3.0. Several applications can run at the same time on different platform revisions without causing conflicts, and Java Web Start can automatically download and install a revision of the platform if an application requests a version that is not installed on the client system.

Java Web Start allows applications to be launched independently of a Web browser. This can be used for off-line operation of an application, where launching through the browser is often inconvenient or impossible. The application can also be launched through desktop shortcuts, making launching the Web-deployed application similar to launching a native application.

Java Web Start takes advantage of the inherent security of the Java Platform. Applications are by default run in a protective environment (sandbox) with restricted access to local disk and network resources. It allows the user to safely run applications from sources that are not trusted.

Java Web Start is browser independent and mostly platform independent. Thus, it works with virtually all browsers, and is expected to be ported to all platforms that supports Java 2 SE in a relatively short time frame.

Applications launched with Java Web Start are cached locally. Thus, an already-downloaded application is launched on par with a traditionally installed application.

The technology underlying Java Web Start is the Java TM Network Launching Protocol & API (JNLP). This technology is currently under development via the Java Community Process (JCP). Java Web Start is the reference implementation (RI) for the JNLP specification. The JNLP technology defines, among other things, a standard file format that describes how to launch an application called a JNLP file.

Where to find Java Web Start

Java Web Start can be downloaded from http://java.sun.com/products/javawebstart/.The JNLP specification can be found at http://java.sun.com/aboutJava/communityprocess/jsr/jsr_056_jnlp.html.

Requirements

Desktop/Client requirements

The client machine requires support for the Java Runtime Environment (JRE), version 1.2.2 or later. Java Web Start Version 1.0 is available for Windows 95/98/NT/2000, Solaris (SPARC & Intel editions), and Linux (RH6.1/x86).

See the README document for details.

Server requirements

Applications can be deployed from any standard Web server. In order to use Java Web Start, the Web server must be configured with support for a new MIME type as explained below.

Setting up the Web site

Java Web Start leverages existing Internet technology, such as the HTTP protocol and Web servers, so existing infrastructure for deploying HTML-based contents can be reused to deploy Java Technology-based applications using Java Web Start.

In order to deploy your application to client machines, you must make sure that all files containing your application are accessible through a Web server. This typically amounts to copying one or more JAR files, along with a JNLP file, into the Web server's directories. The set-up required for enabling the Web site to support Java Web Start is very similar to deploying HTML-based contents. The only caveat is that a new MIME type needs to be configured for the Web server.

1. Configure the Web server to use the Java Web Start MIME type

Configure the Web server so that all files with the .jnlp file extension are set to the application/x-java-jnlp-file MIME type.

Most Web browsers uses the MIME type returned with the contents from the Web server to determine how to handle the particular content. The server must return application/x-java-jnlp-file MIME type for JNLP files in order for Java Web Start to be invoked.

Each Web server has a specific way in which to add MIME types. For example, for the Apache Web server you must add the following line to the .mime.types configuration file:

application/x-java-jnlp-file JNLP

Check the documentation for the specifics of your Web server.

2. Create a JNLP file for the application

The easiest way to create this file is to modify an existing JNLP file to your requirements.

The syntax and format for the JNLP file is described in a later section.

3. Make the application accessible on the Web server

Ensure your application's JAR files and the JNLP file are accessible at the URLs listed in the JNLP file.

4. Create a link from the Web page to the JNLP file

The link to the JNLP file is a standard HTML link. For example:

   <a href="MyApp.jnlp">Launch My Application</a>

Given the setup in step 1, the Web server will automatically return the application/x-java-jnlp-file MIME type with any file with the .jnlp extension. This is critical for making sure that a Web browser will launch Java Web Start when downloading the file.

A link to the Java Web Start installer should also be provided on the Web page, so users who do not already have Java Web Start installed can download and install the software.

Detecting if Java Web Start is installed

It is possible to detect if Java Web Start is installed by using a little bit of JavaScript/VBScript in the HTML page. Using this sample script below, a link to an application can be inserted as:

    <SCRIPT LANGUAGE="Javascript">

        <!--

        insertLink("http://www.mycompany.com/my-app.jnlp",

                   "My Application");

        // -->

    </SCRIPT>

The insertLink method detects if Java Web Start is installed or not. If it is installed, the HTML will be similar to:

        <a href="http://www.mycompany.com/my-app.jnlp">MyApplication</a>

If Java Web Start is not installed, the following HTML will be emitted:

        Need to install Java Web Start

In a real life situation, this should probably be a link to a download page.

The sample JavaScript/VBScript is listed below:

       <SCRIPT LANGUAGE="Javascript">

          var javawsInstalled = 0;

          isIE = "false";

          if (navigator.mimeTypes && navigator.mimeTypes.length) {

             x = navigator.mimeTypes['application/x-java-jnlp-file'];

             if (x) javawsInstalled = 1;

          } else {

             isIE = "true";

          }

          function insertLink(url, name) {

           if (javawsInstalled) {

                document.write("<a href=" + url + ">"  + name + "</a>");

             } else {

                document.write("Need to install Java Web Start");

             }

          }

      </SCRIPT>

      <SCRIPT LANGUAGE="VBScript">

          on error resume next

          If isIE = "true" Then

            If Not(IsObject(CreateObject("JavaWebStart.IsInstalled"))) Then

                  javawsInstalled = 0

            Else

                  javawsInstalled = 1

            End If

          End If

      </SCRIPT>


Java Web Start 1.0 developer's guide (2)

作者：diego lynn

日期：2001-3-25 10:54:32

Application Development Considerations

Developing applications that can be deployed with Java Web Start are generally the same as developing a stand-alone application for the Java 2 platform. Hence, the entry point for the application is the standard public static void main(String[] argv).

However, in order to support Web deployment -- automatic download and launching of the application -- and to ensure that an application can run in a secure sandbox, there are a few added considerations:

An application must be delivered as a set of JAR files.

All application resources, such as files and images must be stored in JAR files; and they must be referred to using the getResource mechanism in the Java 2 platform (see below).

If an application is written to run in a secure sandbox, it must follow these restrictions:

No access to local disk.

All JAR files must be downloaded from the same host.

Network connections are enabled only to the host from which the JAR files are downloaded.

No security manager can be installed.

No native libraries.

Limited access to system properties. The application has read/write access to all system properties defined in the JNLP File, as well as read-only access to the same set of properties than an Applet has access to.

An application is allowed to use the System.exit call.

An application that needs unrestricted access to the system will need to be delivered in a set of signed JAR files. All entries in each JAR file must be signed.

Retrieving Resources from JAR files

Java Web Start only transfers JAR files from the Web server to the client machine. It determines where to store the JAR files on the local machine. Thus, an application cannot use disk-relative references to resources such as images and configuration files.

All application resources must be retrieved from the JAR files specified in the resources section of the JNLP file, or retrieved explicitly using an HTTP request to the Web server. We recommend storing resources in JAR files, since they will be cached on the local machine by Java Web Start.

The following code example shows how to retrieve images from a JAR file:

   // Get current classloader

   ClassLoader cl = this.getClass().getClassLoader();

   // Create icons

   Icon saveIcon  = new ImageIcon(cl.getResource("images/save.gif"));

   Icon cutIcon   = new ImageIcon(cl.getResource("images/cut.gif"));

   ...

The example assumes that the following entries exist in one of the JAR files for the application:

images/save.gif

images/cut.gif

Security and Code Signing

Java Web Start addresses the security issues of:

Protecting users against malicious code (intentional & unintentional) that may affect local files

Protecting enterprises against code that may attempt to access or destroy data on networks

Applications launched with Java Web Start are -- by default -- run in a restricted environment where they have limited access to local computing resources, such as storage devices and the local network. In this sandbox environment, Java Web Start can guarantee that a downloaded and potentially untrusted application cannot compromise the security of the local files or the network.

An additional security feature supported by Java Web Start is digital code signing. If an application being invoked is delivered in one or more signed JAR files, Java Web Start will verify that the contents of the JAR file have not been modified since they were signed. If verification of a digital signature fails, Java Web Start will not run the application, since it may have been compromised by a third-party.

The support for code signing is important for both users and for application service providers. This service makes it possible for users to verify that an application comes from a trusted source. Because the application service provider signs the code, both can be ensured that no other party can impersonate the application on the Web. A signed application that is trusted by the user can also request additional system privileges, such as access to a local disk.

Java Web Start presents a dialog displaying the application's origin, based on the signer's certificate, before the application is launched. Thereby allowing the user to make an informed decision whether to grant additional privileges to the downloaded code, or not.

An application can request full access to a client system when all its JAR files are signed by including the following settings in the JNLP file:

<security>

   <all-permissions/>

</security>

The implementation of code signing in Java Web Start is based on the security API in the core Java 2 Platform. The Java 2 SE JRE 1.2.x supports code signing with the SHAwithSDA algorithm. Java 2 SE JRE 1.3 also supports MD2withRSA and MD5withRSA. The MD5withRSA is currently the most often used algorithm.

Developers sign code for use with Java Web Start in the same manner as for Java Applets by using the standard jarsigner tool from the Java 2 SE SDK. The documentation for the jarsigner tool provides examples on how to sign code, create test certificates, and other signing related issues.

Java Web Start also support use of the Netscape signtool when used with the Java 2 SE JRE 1.3.0. See the Netscape Web site for details: http://developer.netscape.com/software/signedobj/

Signing JAR Files with a Test Certificate

Here are the steps needed to sign a JAR file with a test certificate:

Make sure that you have a JDK 1.2 or JDK 1.3 keytool and jarsigner in your path (located in the J2SE SDK bin directory).

Create a new key in a new keystore as follows:

keytool -genkey -keystore myKeystore -alias myself

You will get prompted for a information about the new key, such as password, name, etc. This will create the myKeystore file on disk.

Then, create a self-signed test certificate as follows:

keytool -selfcert -alias myself -keystore myKeystore

This will prompt for the password. Generating the certificate takes a few minutes.

Check to make sure that everything is ok. To list the contents of the keystore, use the command:

keytool -list -keystore myKeystore

It should list something like:

Keystore type: jks

Keystore provider: SUN

Your keystore contains 1 entry:

myself, Tue Jan 23 19:29:32 PST 2001, keyEntry,

Certificate fingerprint (MD5):

C2:E9:BF:F9:D3:DF:4C:8F:3C:5F:22:9E:AF:0B:42:9D

Finally, sign the JAR file with the test certificate as follows:

jarsigner -keystore myKeystore test.jar myself

Repeat this step on all of your JAR files.

Please note that a self-signed test certificate should only be used for internal testing, since it does not provide any guarantees about the identity of the user and therefore cannot be trusted. A trust-worthy certificate can be obtained from a certificate authority, such as VeriSign, and should be used when the application is put into production.

Converting JNLP files to work with this release

The JNLP file format has changed significantly since the 0.4 early access release.  Below is a list of the most common modifications that needs to be applied to a 0.4 JNLP file to make it compatible with Java Web Start 1.0:

Set spec attribute to 1.0.

Rename the unrestricted element to all-permissions.

Rename the jre element to j2se and move this element inside the resources element (i.e., make j2se a subelement of resources).

Converting JNLP files used with the 1.0-beta or 1.0-rc release only requires updating the spec attribute to "1.0".

Descriptions of new JNLP file features can be found below.

JNLP File Syntax

The format used in this release correspond to the format specified in the Java Network Launching Protocol and API (JNLP) Specification, v1.0. This document describes the most commonly used elements of the JNLP file. Refer to the specification for a complete description of the format.

The JNLP file is an XML document. The following shows a complete example of a JNLP file:

<?xml version="1.0" encoding="utf-8"?>

<!-- JNLP File for SwingSet2 Demo Application -->

<jnlp

  spec="1.0+"

  codebase="http://javaweb.eng.com/jaws/apps"

  href="swingset2.jnlp">

  <information>

    <title>SwingSet2 Demo Application</title>

    <vendor>Sun Microsystems, Inc.</vendor>

    <homepage href="docs/help.html"/>

    <description>SwingSet2 Demo Application</description>

    <description kind="short">A demo of the capabilities of the Swing Graphical User Interface.</description>

    <icon href="images/swingset2.jpg"/>

    <offline-allowed/>

  </information>

  <security>

      <all-permissions/>

  </security>

  <resources>

    <j2se version="1.3"/>

    <jar href="lib/SwingSet2.jar"/>

  </resources>

  <application-desc main-class="SwingSet2"/>

</jnlp>

The example shows the basic outline of the document. The root element is jnlp, which has four subelements: information, security, resources, and application-desc. In addition, Java Web Start also supports launching Applets by using the applet-desc element. The elements are described in more detail below.

The JNLP Element

spec attribute: This attribute must be 1.0 or higher to work with this release. The default value is "1.0+". Thus, it can typically be omited.

codebase attribute: All relative URLs specified in href attributes in the JNLP file is using this URL as a base.

href attribute: This is a URL pointing to the location of the JNLP file itself. Java Web Start requires this attribute to be set in order for the application to be included in the Application Manager.

The Information Element

title element: The name of the application.

vendor element: The name of the vendor of the application.

homepage element: Contains a single attribute, href, which is a URL locating the home page for the Application. It is used by the Application Manager to point the user to a Web page where they can find more information about the application.

description element: A short statement about the application. Description elements are optional. The kind attribute defines how the description should be used, it can have one of the following values:

one-line: If a reference to the application is going to appear on one row in a list or a table, this description will be used.

short: If a reference to the application is going to be displayed in a situation where there is room for a paragraph, this description is used.

tooltip: A description of the application intended to be used as a tooltip.

Only one description element of each kind can be specified. A description element without a kind is used as a default value. Thus, if Java Web Start needs a description of kind short, and it is not specified in the JNLP file, then the text from the description without an attribute is used

All descriptions contain plain text. No formatting, such as HTML tags is supported.

icon element: Contains an HTTP URL to an image file in either GIF or JPEG format. The icons are used to represents the application when Java Web Start presents the application to the user during launch, in the Application Manager, and in desktop shortcuts. A 64x64 icon is shown during download and a 32x32 for desktop icons and in the application manager. Java Web Start automatically resizes an icon to the appropriate size.


java web start 1.0 developer's guide (3)

作者：diego lynn

日期：2001-3-25 10:55:09

Optional width and height attributes can be used to indicate the size of the images.

offline-allowed element: The optional offline-allowed element indicates if the application can be launched offline.

Applications that are not marked offline in the JNLP file will not be able to be launched through the Application Manager, if the offline checkbox is checked. The default is that an application only works if the client system is online.

The Security Element

Each application is, by default, run in a restricted execution environment, similar to the Applet sandbox.  The security element can be used to request unrestricted access.

If the all-permissions element is specified, the application will have full access to the client machine and local network. If an application requested full access, then all JAR files must be signed. The user will be prompted to accept the certificate the first time the application is launched.

The Resources Element

The resources element is used to specify all the resources, such as Java class files, native libraries, and system properties, that are part of the application.  A resource definition can be restricted to a specific operating system, architecture, or locale using the os, arch, and locale attributes.

The resources element has 6 different possible subelements: jar, nativelib, j2se, property, package, and extension. The package and extension elements are not discussed in this developer's guide. See specification  for details.

A jar element specifies a JAR file that is part of the application's classpath.  For example:

  <jar href="myjar.jar"/>

The jar file will be loaded into the JVM using a ClassLoader object.  The jar file will typically contain Java classes that contain the code for the particular application, but can also contain other resources, such as icons and configuration files, that are available through the getResource mechanism.

A nativelib element specifies a JAR file that contains native libraries.  For example:

    <nativelib href="lib/windows/corelib.jar"/>

The JNLP Client must ensure that each file entry in the root directory of the JAR file (i.e., /) can be loaded into the running process using the System.loadLibrary method.  Each entry must contain a platform-dependent shared library with the correct naming convention, e.g., *.dll on Windows, or lib*.so on Solaris/Linux.  The application is responsible for doing the actual call to System.loadLibrary.

Native libraries would typically be included in a resources element that is guarded against a particular operating system and architecture.  For example:

    <resources os="SunOS" arch="sparc">

        <nativelib href="lib/solaris/corelibs.jar"/>

    </resource>

By default, jar and nativelib resources will be downloaded eagerly, i.e., they are downloaded and available locally to the JVM running the application before the application is launched.  The jar and nativelib elements also allow a resource to be specified as lazy.  This means that the resource does not necessarily need to be downloaded onto the client system before the application is launched.

The download attribute is used to control whether a resource is downloaded eagerly or lazily.  For example:

    <jar href="sound.jar" download="lazy"/>

    <nativelib href="native-sound.jar" download="eager"/>

The j2se element specifies what Java 2 SE Runtime Environment (JRE) versions an application is supported on, as well as standard parameters to the Java Virtual Machine.  Several JREs can be specified, which indicates a prioritized list of the supported JREs, with the most preferred version first.  For example:

       <j2se version="1.3" initial-heap-size="64m"/>

       <j2se version="1.2"/>

The version attribute refers to, by default, a particular revision of the Java 2 platform. Current defined revisions are "1.2" and "1.3". The JNLP specification also allows specifying exact product versions, such as the Java 2 JRE 1.2.2-001 by Sun Microsystems, Inc.

The property element defines a system property that will be available through the System.getProperty and System.getProperties methods. It has two required attributes:  name and value.  For example:

<property name="key" value="overwritten"/>

The Application-Desc Element

The application element indicates that the JNLP file is launching an application (as opposed to an Applet). The application element has an optional attribute, main-class, which can be used to specify the name of the application's main class, i.e., the class that contains the public static void main(String argv[]) method where execution must begin.

The main-class attribute can be omitted, if the first JAR file specified in the JNLP File contains a manifest file containing the main class.

Arguments can be specified to the application be including one or more nested argument elements. For example:

  <application-desc main-class="Main">

    <argument>arg1</argument>

    <argument>arg2</argument>

  </application-desc>

The Applet-Desc Element

Java Web Start has support for launching Java Applets. This support provides easy migration of existing code to Java Web Start.

An Applet is launched using the applet-desc element instead of the application-desc element. For example:

  <applet-desc

      documentBase="http://..."

      name="TimePilot"

      main-class="TimePilot.TimePilotApp"

      width="527"

      height="428">

    <param name="key1" value="value1"/>

    <param name="key2" value="value2"/>

  </applet-desc>

The JAR files that make up the Applet are described using the resources element as for applications. The documentBase must be provided explicitly since a JNLP file is not embedded in an HTML page. The rest of the attributes correspond to the respective HTML applet tag elements.

The main-class attribute is used instead of the code attribute.  The main-class attribute is assigned the name of the Applet class (without the .class extension).  This attribute can be omitted if the Applet class can be found from the Main-Class manifest entry in the main JAR file.

Note: Applets must be packaged in JAR files in order to work with Java Web Start.


java web start 1.0 developer's guide (4)

作者：diego lynn

日期：2001-3-25 10:55:47

JNLP API Examples

The JNLP API is designed to provide additional information to the application that would otherwise not be available using the standard Java 2 SE API. The following code examples show how the following services can be used: BasicService, ClipboardService, DownloadService, FileOpenService, FileSaveService, PrintService, and PersistenceService.

The public classes and interfaces in the JNLP API are included in the jnlp.jar file. This JAR file must be included in the classpath when compiling source files that use the JNLP API. For example (on Windows):

javac -classpath .;jnlp.jar *.java

The jnlp.jar file is included in the JNLP Developers Pack 1.0.

Using a BasicService Service

The javax.jnlp.BasicService service provides a set of methods for querying and interacting with the environment similar to what the AppletContext provides for a Java Applet.

The showURL method uses the JNLP API to direct the default browser on the platform to show the given URL. The method returns true if the request successes, otherwise false.

import javax.jnlp.*;

   ...

   // Method to show a URL

   boolean showURL(URL url) {

       try {

           // Lookup the javax.jnlp.BasicService object

           BasicService bs = (BasicService)ServiceManager.lookup("javax.jnlp.BasicService");

           // Invoke the showDocument method

           return bs.showDocument(url);

       } catch(UnavailableServiceException ue) {

           // Service is not supported

           return false;

       }

    }

Using a ClipboardService Service

The javax.jnlp.ClipboardService service provides methods for accessing the shared system-wide clipboard, even for applications that are running in the restricted execution environment.

Java Web Start will warn the user of the potential security risk of letting an untrusted application access potentially confidential information stored in the clipboard, or overwriting contents stored in the clipboard.

import javax.jnlp;

    ...

    private ClipboardService cs;

    try {

        cs = (ClipboardService)ServiceManager.lookup

                 ("javax.jnlp.ClipboardService");

    } catch (UnavailableServiceException e) {

        cs = null;

    }

    if (cs != null) {

        // set the system clipboard contents to a string selection

        StringSelection ss = new StringSelection("Java Web Start!");

        cs.setContents(ss);

        // get the contents of the system clipboard and print them

        Transferable tr = cs.getContents();

        if (tr.isDataFlavorSupported(DataFlavor.stringFlavor)) {

           try {

                String s = (String)tr.getTransferData(DataFlavor.stringFlavor);

                System.out.println("Clipboard contents: " + s);

            } catch (Exception e) {

                e.printStackTrace();

            }

        }

    }

Using a DownloadService Service

The javax.jnlp.DownloadService service allows an application to control how its own resources are cached.

The service allows an application to determine which of its resources are cached, to force resources to be cached, and to remove resources from the cache.

import javax.jnlp.*;

    ...

    DownloadService ds;

    try {

        ds = (DownloadService)ServiceManager.lookup("javax.jnlp.DownloadService");

    } catch (UnavailableServiceException e) {

        ds = null;

    }

    if (ds != null) {

        try {

           // determine if a particular resource is cached

            URL url =

                    new URL("http://java.sun.com/products/javawebstart/lib/draw.jar" ;

            boolean cached = ds.isResourceCached(url, "1.0");

           // remove the resource from the cache

           if (cached) {

                ds.removeResource(url, "1.0");

            }

           // reload the resource into the cache

            DownloadServiceListener dsl = ds.getDefaultProgressWindow();

            ds.loadResource(url, "1.0", dsl);

        } catch (Exception e) {

            e.printStackTrace();

        }

    }

Using a FileOpenService Service

The javax.jnlp.FileOpenService service provides methods for importing files from the local disk, even for applications that are running in the restricted execution environment.

This interface is designed to provide the same kind of of disk access to potentially untrusted Web-deployed applications that a Web developer has when using HTML.  HTML forms support the inclusion of files by displaying a file open dialog.

import javax.jnlp.*;

    ...

    FileOpenService fos;

    try {

        fos = (FileOpenService)ServiceManager.lookup("javax.jnlp.FileOpenService");

    } catch (UnavailableServiceException e) {

        fos = null;

    }

    if (fos != null) {

        try {

           // ask user to select a file through this service

            FileContents fc = fos.openFileDialog(null, null);

           // as user to select multiple files through this service

            FileContents [] fcs = fos.openMultiFileDialog(null, null);

        } catch (Exception e) {

            e.printStackTrace();

        }

    }

Also see Using FileContents.

Using a FileSaveService Service

The javax.jnlp.FileSaveService service provides methods for exporting files to the local disk, even for applications that are running in the restricted execution environment.

This interface is designed to provide the same level of disk access to potentially untrusted Web-deployed applications that a Web browser provides for contents that it is displaying.  Most browsers provide a Save As... dialog as part of their user interface.

    import javax.jnlp.*;

    ...

    FileSaveService fss;

    FileOpenService fos;

    try {

        fos = (FileOpenService)ServiceManager.lookup("javax.jnlp.FileOpenService");

        fss = (FileSaveService)ServiceManager.lookup

                                   ("javax.jnlp.FileSaveService");

    } catch (UnavailableServiceException e) {

        fss = null;

        fos = null;

    }

    if (fss != null && fos != null) {

        try {

           // get a file with FileOpenService

            FileContents fc = fos.openFileDialog(null, null);

           // one way to save a file

            FileContents newfc = fss.saveFileDialog(null, null,

            fc.getInputStream(), "newFileName.txt");

           // another way to save a file

            FileContents newfc2 = fss.saveAsFileDialog(null, null, fc);

        } catch (Exception e) {

            e.printStackTrace();

        }

    }

Also see Using FileContents.


Java Web Start 1.0 FAQ (2)

作者：diego lynn

日期：2001-3-25 11:00:19

Can I implement my own JavaTM Web Start?

The underlying technology for JavaTM Web Start, the Java Network Launching Protocol and API, is being developed through the Java Community Process.

http://java.sun.com/aboutJava/communityprocess/jsr/jsr_056_jnlp.html

Thus, you can implement this protocol in any product, however note that additional licensing and terms must be met to implement any JCP(SM) technology including the JNLP technology. back to top

What are the supported browsers?

JavaTM Web Start supports primarily Internet Explorer 4 or higher and Netscape 4.X. However JNLP files can be launched from any browser if the mime-type association is done correctly. Please note that JavaTM Web Start uses the browsers settings and may launch a browser to show a URL these may/may not work with unsupported browsers.

JavaTM Web Start works with Netscape 6. However, you will manually need to register JavaTM Web Start with NS6. This is done in the Navigator/Applications section of the Navigator/Helper Application section.

This will be done automatically in a later version of the JavaTM Web Start installer. back to top

When and where can we get the European and Asian language versions of JavaTM Web Start?

We are currently working on the Asian and European versions which will be made available in a forthcoming release in the early or mid part of 2001. The localized versions would be: French, German, Italian, Spanish, Swedish, Japanese, Korean, Simplified Chinese and Traditional Chinese. back to top

Is there a comprehensive list of Online Resources for JavaTM Web Start?

* JSR-56:

http://java.sun.com/aboutJava/communityprocess/jsr/jsr_056_jnlp.html

* Product Page:

http://java.sun.com/products/javawebstart/

* README:

http://java.sun.com/products/javawebstart/docs/readme.html

* Developer's Guide:

http://java.sun.com/products/javawebstart/docs/developersguide.html

* Java Web Start Discussion Forums http://forum.java.sun.com/list/discuss.sun.java.web.start.jnlp

back to top

Can I deploy any application with JavaTM Web Start?

JavaTM Web Start is an application launcher for JavaTM 2 technology based applications that are written to be Web-deployed.

An application must be delivered in a set of JAR files and all application resources, such as images, configuration files and Native libraries, must be included in the JAR files. The resources must be looked up using the ClassLoader getResource or another method. JavaTM Web Start only transfers JAR files from the Web server to the client.

If an application is written to run in a restricted execution environment (sandbox), then access to disk is not permitted and the application may only connect to the host on which it resides. back to top

Are there any third party tools available to deploy my application with Java Web Start?

Sitraka Software's free DeployDirector Bundler for JNLP automatically generates the required XML files and .WAR file needed by the server to deploy the bundle. back to top

What applications are available for JavaTM Web Start?

Currently we have a set of demo applications to try out at: http://java.sun.com/products/javawebstart/demos.html.However most JavaTM applications can be made to run with JavaTM Web Start. back to top

Is JavaTM Web Start a software distribution mechanism like Marimba and MS SMS?

JavaTM Web Start is an application launcher for JavaTM 2 technology-based applications. It allows easy distribution of full-featured applications based on the Java platform from a Web server to a client machine with minimal user interaction.

The software distribution technology is only one aspect of JavaTM Web Start. It also provides security, updates to the applications, ease-of-use for end users, and flexibility for developers when they create the applications. back to top

Is JavaTM Web Start an application installer?

JavaTM Web Start is an application launcher for JavaTM 2 technology-based applications that are written to be Web-deployed. It launches only those applications written on the JavaTM 2 platform.

JavaTM Web Start caches resources locally on the disk, but this is only one aspect of its technology. It also provides a secure execution environment and a virtually transparent updating facility for applications. The end user does not need to manually initiate a software update because the application is updated each time it is used. back to top

What version of the JavaTM platform does JavaTM Web Start work with?

JavaTM Web Start is an application launcher for JavaTM 2 technology-based applications that are written to be Web-deployed. It launches only those applications written for the JavaTM 2 platform. back to top

How does JavaTM Web Start relate to Plug-in?

The two approaches are very similar. The key difference is in the user experience. If the JavaTM application/applet needs to interact with a web page and be tightly bound to a web browsers, then applets may be the solution. On the other hand, if browser independence is important then JavaTM Web Start is the deployment platform of choice. There are a number of other differences, but this is the fundamental difference.

JavaTM Plug-in enables users to run JavaTM 2 technology-based applets inside a browser.

JavaTM Web Start enables users to download full-featured applications with any browser. Once they have downloaded and launched an application, the browser can be closed, while the application continues working.

The application is not dependent upon an open browser to function. The browser can be shut down or you can go to a different Web page and the application will continue running. back to top

Licensing and Support Questions

Is there a Licensing fee for JavaTM Web Start?

There is no licensing fee or royalty to redistribute JavaTM Web Start (per terms of the license that comes with the software). There is also no fee to obtain JavaTM Web Start from http://java.sun.com/products/javawebstart/.  back to top

Can we deploy/redistribute JavaTM Web Start with our application?

Yes, JavaTM Web Start can be bundled with your application. back to top

Are there any restrictions to redistributing JavaTM Web Start?

This release includes Sun's standard Binary Code License (BCL) + any supplemental terms, similar to the JRE terms. In general the terms tend to boil down to "you must ship is as, without modification." back to top

Where can I find the Licensing Terms for the JRE?

The JRE licensing terms can be found here:

http://java.sun.com/j2se/1.3/jre/j2re1_3_0.license&nbs p; back to top

What kind of support is available for JavaTM Web Start with the License?

Support is an optional purchase. We recommend you explore these options:

http://www.sun.com/developers/support/index.html  back to top

Technical Questions

Why does JavaTM Web Start download the application the next time?

JavaTM Web Start will always launch the application from the cache, if possible, and it will simultaneously perform a background check with the server for updates. If updates are available, then it will notify the user, and launch the update versions the next time. This approach ensures fast startup time in the common case where there is no update, and also makes sure that an application can be launched offline.

For the 1.0 release, this behavior can be overwritten by adding the line javaws.cfg.forceUpdate=true in a client's javaws.cfg file. This will force Java Web Start to check for an update the first time. Unfortunately though, setting this flag will cause offline mode to not work properly.

In a future release of Java Web Start, we expect to change the behavior of the update checking to immediatly launch the newer version of the application, if an update is available, while still preserving offline launching. back to top


Java Web Start 1.0 FAQ (3)

作者：diego lynn

日期：2001-3-25 11:01:04

Do I need to change my application to work with JavaTM Web Start?

If your application is written to the JavaTM 2 platform, and is delivered as a set of JAR files, there should be no need to revise your application.

Make sure that all your application resources (such as images and resource bundles, for example) are retrieved from a JAR file, since JavaTM Web Start launches an application by invoking the public static void main(String[] args) method.

And if your application needs unrestricted access to the system, (for example, network or disk access), you will need to sign your code. back to top

Is there a migration path from applets to applications? OR Does JavaTM Web Start support applets?

JavaTM Web Start is primarily designed for application deployment. You specify all requirements for your application in the JNLP file, and off you go. We do provide the ability to launch Applets in much the same way as the traditional AppletViewer. The built-in AppletViewer is there to provide an easy migration path for existing Applets that want to take advantage of JavaTM Web Start. However, it is not intended to be a full implementation of the Plug-In. The Plug-In is the primary launching vehicle for Applets. The built-in AppletViewer in JavaTM Web Start has certain limitation, e.g., you can not specify class files as resources and policy files are not accepted. back to top

Is there a way to pass VM properties to JavaTM Runtime?

JavaTM Web Start allows certain JVM flags to be set in the JNLP file. Allowing the complete set could compromise security as well as limit portability across different platforms and implementations. Properties (-D) can be set using the

<property name="..." value=".."/>

element in the <resources> section. The maximum and initial heap size can be set using the initial-heap-size and max-heap-size attributes of the j2se element, e.g.,

<j2se version="..." max-heap-size="100M"/>   back to top

I don't want to rely on server time stamp. How can I specify versioned JAR and how can I get incremental updates of JARs?

JavaTM Web Start 1.0 supports versioned JARs and incremental update. You have the ability to specify exact versions of the JAR files you want, instead of relying on the time-stamp information to know if an update is available.

Using version-id's also allows you to provide incremental updates from one version to another.

See the JNLP specification for the details:

http://java.sun.com/aboutJava/communityprocess/jsr/jsr_056_jnlp.html

back to top

Are JAR files shared between applications?

Each JAR file that a JNLP Client (such as JavaTM Web Start) downloads, is uniquely identified with a URL. If two JNLP files uses the same URL, then the resource will only be downloaded once and shared. This is similar to the caching implementations with Internet browsers. back to top

Is JRE 1.1 supported?

JavaTM Web Start relies heavily on the new security model introduced with Java 2, and would be very hard (if not impossible) to implement on top of JRE 1.1.x. Therefore, the answer is no. back to top

How can I provide my own splash screen?

JavaTM Web Start needs to put up the initial splash screen, since at that point there is no application specific code. Downloading the application code might take a few seconds to several minutes. That is too long to wait to get the initial splash screen.

It is important that all applications launched through JavaTM Web Start have an initial common look, so the security paradigm that this model implies, can be properly communicated to the end user.

An application can be written, so the initial download is rather small, e.g., in the order of 10 - 20 K. The initial download will then put up the initial application splash screen, and then proceeds to download the rest of the application. back to top

How can I save the Application state on the local system?

A sandboxed application can store state, using the PersistenceService APIs.

This API is somewhat similar to cookies for HTML pages. Thus, it will be a secure way of persistently store information locally on the client computer.

Please refer to the Specification and Developers Guide for more information:

http://java.sun.com/aboutJava/communityprocess/jsr/jsr_056_jnlp.html

http://java.sun.com/products/javawebstart/docs/developersguide.html#dev

back to top

If my application needs a specific JRE to run, how do I specify this in my JNLP file?

A <j2se version="..."> specifies a platform version. Currently, there are two revisions of the Java 2 platform, 1.2 and 1.3.

The 1.2 platform is implemented by e.g Sun JRE's 1.2, 1.2.1, 1.2.2-w, etc.

The 1.3 platform is implemented by e.g Sun JRE's 1.3.0 so far.

You can request a specific product version by including a vendor URL. For Sun's JREs that will be

http://jsp.java.sun.com/servlet/javawsExtensionServer

Thus, the following J2SE tag will request any Sun 1.2.2 implementation.

<j2se version="1.2.2*" href="http://jsp.java.sun.com/servlet/javawsExtensionServer"/>

You can see all the versions of the installed JREs by inspecting the preferences panel of JavaTM Web Start's built-in application manager. back to top

How can I specify my resource using relative URLs to the Web page?

This is currently not possible since the URLs in the <jar> elements are all relative to the in the codebase attribute. However, the codebase attribute cannot be relative. However, we are looking at alternate solutions to solve this problem in a future release. back to top

How do I specify a version of a Standard Extension?

The JNLP specification defines an extension mechanism that covers standard extension. Thus, you will be able to specify exactly a version of an extension you need and have it automatically downloaded and installed. The JNLP specification describes this in detail:

http://java.sun.com/aboutJava/communityprocess/jsr/jsr_056_jnlp.html

back to top

Can I rely on Class-Path in the Manifest file?

JavaTM Web Start does not support the Class-Path entry in the manifest file. The Class-Path attribute is entirely file-centric, where as JavaTM Web Start and JNLP is Web-centric, i.e., based on URLs. Thus, the two models do not merge easily.

Instead of relying on the Class-Path entry, you can list multiple JAR files in the JNLP file, e.g.,:

<resources>

    <jar href="A.jar"/>

    <jar href="B.jar"/>

</resources>

The JNLP specification describes how JavaTM Web Start works in detail:

http://java.sun.com/aboutJava/communityprocess/jsr/jsr_056_jnlp.html

In a JNLP file, you can factor out dependencies on a set of JAR files to another JNLP file using the <extension... > element. Thus, you can achieve the same kind of re-usability and ease of maintenance as you do with the Class-Path entry. This is specified in the specification.

JNLP also implements a just-in-time downloading mechanism, just as for Applets. For each resource in a JNLP file, you can specify which parts should be eagerly or lazily downloaded, i.e., before the application is launched and which ones can be downloaded later. Default is eager download. Furthermore, the specification includes an API for which you can programatically query JavaTM Web Start about which resources are available and request them to be downloaded. Thus, you can write download/network aware applications. back to top

Can I use JavaTM Web Start even if my Application depends or uses native code (.dlls, .so)?

JavaTM Web Start can be used to deploy JavaTM Technology-based applications that also depends on native code. The <nativelib ...> element can be used to specify native libraries (such as DLLs or .so files) that an application depends on. See the Developer's Guide for details.

http://java.sun.com/products/javawebstart/docs/developersguide.htmlback to top


Java Web Start 1.0 FAQ

作者：diego lynn

日期：2001-3-25 10:59:29

General information

What is JavaTM Web Start?

Where do I get more information?

Why should I use JavaTM Web Start?

How can I launch applications with JavaTM Web Start?

Does it matter how I launch an application?

What are the system requirements for JavaTM Web Start?

What platforms does JavaTM Web Start run on?

What are the server-side system requirements for JavaTM Web Start?

Is JavaTM Web Start based on a standard?

Can I implement my own JavaTM Web Start?

What are the supported browsers?

When and where can we get the European and Asian language versions of JavaTM Web Start?

Is there a comprehensive list of Online Resources for JavaTM Web Start?

Can I deploy any application with JavaTM Web Start?

Are there any third party tools available to deploy my application with Java Web Start?

What applications are available for JavaTM Web Start?

Is JavaTM Web Start a software distribution mechanism like Marimba and MS SMS?

Is JavaTM Web Start an application installer?

What version of the JavaTM platform does JavaTM Web Start work with?

How does JavaTM Web Start relate to Plug-in?

Licensing and Support Questions:

Is there a Licensing fee for JavaTM Web Start?

Can we deploy/redistribute JavaTM Web Start with our application?

Are there any restrictions to redistributing JavaTM Web Start?

Where can I find the Licensing Terms for the JRE?

What kind of support is available for JavaTM Web Start with the License?

Technical information

How do I detect if JavaTM Web Start is installed?

Why does Java Web Start download the application the next time?

Do I need to change my application to work with JavaTM Web Start?

Is there a migration path from applets to applications?

Does JavaTM Web Start support applets?

Is there a way to pass VM properties to JavaTM Runtime?

I don't want to rely on server time stamp. How can I specify versioned JAR and how can I get incremental updates of JARs?

Are JAR files shared between applications?

Is JRE 1.1 supported?

How can I provide my own splash screen?

How can I save the Application state on the local system?

If my application needs a specific JRE to run, how do I specify this in my JNLP file?

How can I specify my resource using relative URLs?

How do I specify a version of a Standard Extension?

Can I rely on Class-Path in the Manifest file?

Can I use JavaTM Web Start even if my Application depends or uses native code (.dlls, .so)?

How can I load resources within my application using Class.forName and ClassLoader.getSystemClassLoader in JavaTM Web Start?

How can I launch JavaTM Web Start from the command line?

How do I use JAR files that are signed by different certificates?

Security Questions:

How secure is JavaTM Web Start?

What is a secure sandbox?

How is the JavaTM Web Start secure sandbox more flexible?

How can I enable my application to gain unrestricted access to the system?

Is there support for encrypted download of JAR files?

What is this Security Dialog all about?

Is there an online resource to learn more about Security tools?

Troubleshooting Questions:

What should I do when JavaTM Web Start reports that the installation is bad?

When I start JavaTM Web Start nothing happens except for a Splash Screen.

I installed JavaTM Web Start but I cannot load resources over the network?

Application ran before (1.0 Beta and prior) now I am getting AccessControlExceptions.

Browser shows JNLP file as plain text?

I get a Bad MIME type error when accessing the JNLP file?

What does the "Warning: Failed to verify the authenticity of this certificate. No assertions can be made of the origin or validity of the code." mean?

I cannot seem to access my local system as "localhost" to download resources?

Why does JavaTM Web Start always reload JAR files from IIS server?

I am having trouble with my application, how do I trace System.out and System.err?

I see "Launching Application" screen and it loops forever?

General information

What is JavaTM Web Start?

JavaTM Web Start provides a platform-independent, secure, and robust deployment technology. It enables developers to deploy full-featured applications to end-users by making the applications available on a standard Web server. By using any Web browser, end-users can launch the applications and be confident they always have the most-recent version. back to top

Where do I get more information?

Click here to download JavaTM Web Start.

Go to the README for comprehensive instructions on downloading, installing, and using JavaTM Web Start if you are an end-user.

If you are a developer, go to the Developer's Guide for concise, detailed technical information on how to deploy applications using JavaTM Web Start.

If you are a site architect, go to the Architecture Page for examples of the types of Web-based solutions that are enabled by Java Web Start and the Java 2 platform, or to the Java Web Start Architecture diagram for more information on how Java Web Start functions. back to top

Why should I use JavaTM Web Start?

It's an easy, robust, and secure way to deploy applications directly from the Web. Developers can make applications readily available via the Web. In addition, JavaTM Web Start provides JavaTM runtime environment (JRE) management capabilities, it's easy to set up, it's browser-independent, and it's an efficient way to deploy Web application solutions.

Users can easily access applications much as they would a Web page -- without a separate installation step. From the desktop, users can access and use applications based on JavaTM 2 technology, using a richer and more responsive user interface than is available on a Web page. And, once a JavaTM Web Start based application is installed, users simply click to run the application whenever needed.

Users do not need to manually update applications because each time they launch an application, it is transparently updated from the Web -- so they always use the most recent version available. back to top

How can I launch applications with JavaTM Web Start?

You initially launch a new application by clicking on a link from a Web page.

If you use an application frequently, create a shortcut from your desktop or from the Start Menu by allowing JavaTM Web Start to place an icon on your desktop. JavaTM Web Start will ask the second time you launch an application if you would like to create shortcuts or an entry in the Start Menu. If you say "yes," all future launches of the application can start without a browser.

JavaTM Web Start also provides an Application Manager, which is launched by clicking on the icon on your desktop if you are running the Microsoft Windows operating system, or by using the <install-dir>/javaws command on SolarisTM Operating Environment and Linux platforms. The Application Manager keeps track of all applications that have been downloaded and launched using JavaTM Web Start, and allows you to directly launch them.back to top

Does it matter how I launch an application?

No, applications launch in the same manner no matter which method you use: from a Web page, from the shortcut on the desktop, from the Start menu, or through the JavaTM Web Start Application Manager.

JavaTM Web Start always checks to see if a newer version of the application is available for use and automatically downloads that from the Web for you.

If the application you are using has not been digitally signed, JavaTM Web Start will launch it in a restricted and secure execution environment. An application that is not signed, or one that you do not trust, will never be run with unrestricted access to your local system or network.back to top

What are the system requirements for JavaTM Web Start?

Any client system that supports the JavaTM 2 platform can use JavaTM Web Start. JavaTM Web Start works with virtually all browsers. back to top

What platforms does JavaTM Web Start run on?

Sun Microsystems provides versions for Windows 95, Windows 98, Windows NT, and Windows 2000; SolarisTM Operating Environment (SPARC and Intel editions); and Linux/i486.

back to top

What are the server-side system requirements for JavaTM Web Start?

JavaTM Web Start uses the HTTP protocol for communication between the client and the server. A standard unmodified Web server can be used to host an application.

If you require additional services, such as version-based and incremental updates for your applications, the Web Server will need to support Servlets or cgi-scripting. back to top

Is JavaTM Web Start based on a standard?

Yes, the Java Community ProcessSM is a series of public meetings in which the definition of the JavaTM technology was created.

Java Network Launching Protocol is a technology which is a part of the JCPSM standard. Its specification number is JSR 000056, the Java Network Launching Protocol  and Application Programming Interface (JNLP).

JavaTM Web Start 1.0 is the product-quality reference inplementation for the JNLP technology described above, therefore other platform vendors are encouraged to port Java(TM) Web Start to their platform or implement the specification.

For the specification please see: http://java.sun.com/aboutJava/communityprocess/jsr/jsr_056_jnlp.html

back to top


Java 编程技术中汉字问题的分析及解决(转)

作者：zergman

email: zergman@chinaasp.com

日期：2001-6-15 13:22:02

[BLOCKQUOTE][BLOCKQUOTE][BLOCKQUOTE]

[b]Java 编程技术中汉字问题的分析及解决[/b]

[b][/b][/BLOCKQUOTE][/BLOCKQUOTE][/BLOCKQUOTE]

段明辉

自由撰稿人

2000 年 11月 8日

[TABLE][TR][TD][#ffffff][B]内容:[/B][/#][/TD][/TR][TR][TD][img]http://www.cn.ibm.com/developerWorks/i/c.gif[/img][/TD][/TR][TR][TD][img]http://www.cn.ibm.com/developerWorks/i/c.gif[/img][/TD][/TR][TR][TD][url href=http://www.cn.ibm.com/developerWorks/java/java_chinese/index.shtml#1]汉字编码的常识[/url][/TD][/TR][TR][TD] [url href=http://www.cn.ibm.com/developerWorks/java/java_chinese/index.shtml#2]Java 中文问题的初步认识[/url][/TD][/TR][TR][TD] [url href=http://www.cn.ibm.com/developerWorks/java/java_chinese/index.shtml#3]Java 中文问题的表层分析及处理[/url][/TD][/TR][TR][TD] [url href=http://www.cn.ibm.com/developerWorks/java/java_chinese/index.shtml#4]Java 中文问题的根源分析及解决[/url][/TD][/TR][TR][TD] [url href=http://www.cn.ibm.com/developerWorks/java/java_chinese/index.shtml#5]Java Servlet 中文问题的根源[/url][/TD][/TR][TR][TD] [url href=http://www.cn.ibm.com/developerWorks/java/java_chinese/index.shtml#6]修改 Servlet.jar[/url][/TD][/TR][TR][TD] [url href=http://www.cn.ibm.com/developerWorks/java/java_chinese/index.shtml#7]中文乱码的处理函数[/url][/TD][/TR][TR][TD] [url href=http://www.cn.ibm.com/developerWorks/java/java_chinese/index.shtml#resources]参考资料[/url][/TD][/TR][TR][TD] [url href=http://www.cn.ibm.com/developerWorks/java/java_chinese/index.shtml#author]作者简介[/url][/TD][/TR][TR][TD][img]http://www.cn.ibm.com/developerWorks/i/c.gif[/img][/TD][/TR][/TABLE]

在基于 Java 语言的编程中，我们经常碰到汉字的处理及显示的问题。一大堆看不懂的乱码肯定不是我们愿意看到的显示效果，怎样才能够让那些汉字正确显示呢？Java 语言默认的编码方式是UNICODE ，而我们中国人通常使用的文件和数据库都是基于 GB2312 或者 BIG5 等方式编码的，怎样才能够恰当地选择汉字编码方式并正确地处理汉字的编码呢？本文将从汉字编码的常识入手，结合 Java 编程实例，分析以上两个问题并提出解决它们的方案。

现在 Java 编程语言已经广泛应用于互联网世界，早在 Sun 公司开发 Java 语言的时候，就已经考虑到对非英文字符的支持了。Sun 公司公布的 Java 运行环境（JRE）本身就分英文版和国际版，但只有国际版才支持非英文字符。不过在 Java 编程语言的应用中，对中文字符的支持并非如同 Java Soft 的标准规范中所宣称的那样完美，因为中文字符集不只一个，而且不同的操作系统对中文字符的支持也不尽相同，所以会有许多和汉字编码处理有关的问题在我们进行应用开发中困扰着我们。有很多关于这些问题的解答，但都比较琐碎，并不能够满足大家迫切解决问题的愿望，关于 Java 中文问题的系统研究并不多，本文从汉字编码常识出发，分析 Java 中文问题，希望对大家解决这个问题有所帮助。

[B]汉字编码的常识[/B]

我们知道，英文字符一般是以一个字节来表示的，最常用的编码方法是 ASCII 。但一个字节最多只能区分256个字符，而汉字成千上万，所以现在都以双字节来表示汉字，为了能够与英文字符分开，每个字节的最高位一定为1，这样双字节最多可以表示64K格字符。我们经常碰到的编码方式有 GB2312、BIG5、UNICODE 等。关于具体编码方式的详细资料，有兴趣的读者可以查阅相关资料。我肤浅谈一下和我们关系密切的 GB2312 和 UNICODE。GB2312 码，中华人民共和国国家标准汉字信息交换用编码，是一个由中华人民共和国国家标准总局发布的关于简化汉字的编码，通行于中国大陆地区及新加坡，简称国标码。两个字节中，第一个字节（高字节）的值为区号值加32（20H），第二个字节（低字节）的值为位号值加32（20H），用这两个值来表示一个汉字的编码。UNICODE 码是微软提出的解决多国字符问题的多字节等长编码，它对英文字符采取前面加“0”字节的策略实现等长兼容。如 “A” 的 ASCII 码为0x41，UNICODE 就为0x00，0x41。利用特殊的工具各种编码之间可以互相转换。

[B]Java 中文问题的初步认识[/B]

我们基于 Java 编程语言进行应用开发时，不可避免地要处理中文。Java 编程语言默认的编码方式是 UNICODE，而我们通常使用的数据库及文件都是基于 GB2312 编码的，我们经常碰到这样的情况：浏览基于 JSP 技术的网站看到的是乱码，文件打开后看到的也是乱码，被 Java 修改过的数据库的内容在别的场合应用时无法继续正确地提供信息。

String sEnglish = “apple”;

String sChinese = “苹果”;

String s = “苹果 apple ”;

sEnglish 的长度是5，sChinese的长度是4，而 s 默认的长度是14。对于 sEnglish来说， Java 中的各个类都支持得非常好，肯定能够正确显示。但对于 sChinese 和 s 来说，虽然 Java Soft 声明 Java 的基本类已经考虑到对多国字符的支持（默认 UNICODE 编码），但是如果操作系统的默认编码不是 UNICODE ，而是国标码等。从 Java 源代码到得到正确的结果，要经过 “Java 源代码-> Java 字节码-> ;虚拟机->操作系统->显示设备”的过程。在上述过程中的每一步骤，我们都必须正确地处理汉字的编码，才能够使最终的显示结果正确。

“ Java 源代码-> Java 字节码”，标准的 Java 编译器 javac 使用的字符集是系统默认的字符集，比如在中文 Windows 操作系统上就是 GBK ,而在 Linux 操作系统上就是ISO-8859-1，所以大家会发现在 Linux 操作系统上编译的类中源文件中的中文字符都出了问题，解决的办法就是在编译的时候添加 encoding 参数，这样才能够与平台无关。用法是

javac –encoding GBK。

“ Java 字节码->虚拟机->操作系统”， Java 运行环境 （JRE） 分英文版和国际版，但只有国际版才支持非英文字符。 Java 开发工具包 （JDK） 肯定支持多国字符，但并非所有的计算机用户都安装了 JDK 。很多操作系统及应用软件为了能够更好的支持 Java ，都内嵌了 JRE 的国际版本，为自己支持多国字符提供了方便。

“操作系统->显示设备”，对于汉字来说，操作系统必须支持并能够显示它。英文操作系统如果不搭配特殊的应用软件的话，是肯定不能够显示中文的。

还有一个问题，就是在 Java 编程过程中，对中文字符进行正确的编码转换。例如，向网页输出中文字符串的时候，不论你是用

out.println(string);       // string 是含中文的字符串

还是用

<%=string%>，都必须作 UNICODE 到 GBK 的转换，或者手动，或者自动。在 JSP 1.0中，可以定义输出字符集，从而实现内码的自动转换。用法是

<%@page ContentType=”text/html;charset=gb2312” %>

但是在一些 JSP 版本中并没有提供对输出字符集的支持，（例如 JSP 0.92），这就需要手动编码输出了，方法非常多。最常用的方法是

String s1 = request.getParameter(“keyword”);

String s2 = new String(s1.getBytes(“ISO-8859-1”),”GBK”);

getBytes 方法用于将中文字符以“ISO-8859-1”编码方式转化成字节数组，而“GBK” 是目标编码方式。我们从以ISO-8859-1方式编码的数据库中读出中文字符串 s1 ，经过上述转换过程，在支持 GBK 字符集的操作系统和应用软件中就能够正确显示中文字符串 s2 。

[B]Java 中文问题的表层分析及处理[/B] [TABLE][TR][TD]

背景 [/TD][/TR][TR][TD]

开发环境 [/TD][TD]

JDK1.15 [/TD][TD]

Vcafe2.0 [/TD][TD]

JPadPro [/TD][/TR][TR][TD]

服务器端 [/TD][TD]

NT IIS [/TD][TD]

Sybase System [/TD][TD]

Jconnect（JDBC） [/TD][/TR][TR][TD]

客户端 [/TD][TD]

IE5.0 [/TD][TD]

Pwin98 [/TD][TD]

  [/TD][/TR][/TABLE]

.CLASS 文件存放在服务器端，由客户端的浏览器运行 APPLET ， APPLET 只起调入 FRAME 类等主程序的作用。界面包括 Textfield ，TextArea，List，Choice 等。

I.       取中文

用 JDBC 执行 SELECT 语句从服务器端读取数据（中文）后，将数据用 APPEND 方法加到 TextArea（TA） ，不能正确显示。但加到 List 中时，大部分汉字却可正确显示。

将数据按“ISO-8859-1” 编码方式转化为字节数组，再按系统缺省编码方式 （Default Character Encoding） 转化为 STRING ，即可在 TA 和 List 中正确显示。

程序段如下：

dbstr2 = results.getString(1);

//After reading the result from DB server，converting it to string.

dbbyte1 = dbstr2.getBytes(“iso-8859-1”);

dbstr1 = new String(dbbyte1);

在转换字符串时不采用系统默认编码方式，而直接采用“ GBK” 或者 “GB2312” ,在 A 和 B 两种情况下，从数据库取数据都没有问题。

II.    写中文到数据库

处理方式与“取中文”相逆，先将 SQL 语句按系统缺省编码方式转化为字节数组，再按“ISO-8859-1”编码方式转化为 STRING ，最后送去执行，则中文信息可正确写入数据库。

程序段如下：

sqlstmt = tf_input.getText();

//Before sending statement to DB server，converting it to sql statement.

dbbyte1 = sqlstmt.getBytes();

sqlstmt = newString(dbbyte1,”iso-8859-1”);

_stmt = _con.createStatement();

_stmt.executeUpdate(sqlstmt);

……

问题：如果客户机上存在 CLASSPATH 指向 JDK 的 CLASSES.ZIP 时（称为 A 情况），上述程序代码可正确执行。但是如果客户机只有浏览器，而没有 JDK 和 CLASSPATH 时（称为 B 情况），则汉字无法正确转换。

我们的分析：

1.经过测试，在 A 情况下，程序运行时系统的缺省编码方式为 GBK 或者 GB2312 。在 B 情况下，程序启动时浏览器的 JAVA 控制台中出现如下错误信息：

Can't find resource for sun.awt.windows.awtLocalization_zh_CN

然后系统的缺省编码方式为“8859-1”。

2.如果在转换字符串时不采用系统缺省编码方式，而是直接采用 “GBK” 或“GB2312”，则在 A 情况下程序仍然可正常运行，在 B 情况下，系统出现错误：

UnsupportedEncodingException。

3.在客户机上，把 JDK 的 CLASSES.ZIP 解压后，放在另一个目录中， CLASSPATH 只包含该目录。然后一边逐步删除该目录中的 .CLASS 文件，另一边运行测试程序，最后发现在一千多个 CLASS 文件中，只有一个是必不可少的，该文件是：

sun.io.CharToByteDoubleByte.class。

将该文件拷到服务器端和其它的类放在一起，并在程序的开头 IMPORT 它，在 B 情况下程序仍然无法正常运行。

4.在 A 情况下，如果在 CLASSPTH 中去掉 sun.io.CharToByteDoubleByte.class ，则程序运行时测得默认编码方式为“8859-1”，否则为 “GBK” 或 “GB2312” 。

如果 JDK 的版本为1.2以上的话，在 B 情况下遇到的问题得到了很好的解决，测试的步骤同上，有兴趣的读者可以尝试一下。

[B]Java 中文问题的根源分析及解决[/B]

在简体中文 MS Windows 98 + JDK 1.3 下，可以用 System.getProperties() 得到 Java 运行环境的一些基本属性，类 PoorChinese 可以帮助我们得到这些属性。

类 PoorChinese 的源代码：

public class PoorChinese {

   public static void main(String[] args) {

       System.getProperties().list(System.out);

   }

}

执行 java PoorChinese 后，我们会得到:

系统变量 file.encoding 的值为 GBK ，user.language 的值为 zh ， user.region 的值为 CN ，这些系统变量的值决定了系统默认的编码方式是 GBK 。

在上述系统中，下面的代码将 GB2312 文件转换成 Big5 文件，它们能够帮助我们理解 Java 中汉字编码的转化:

import java.io.*;

import java.util.*;

public class gb2big5 {

static int iCharNum=0;

public static void main(String[] args) {

System.out.println("Input GB2312 file, output Big5 file.");

if (args.length!=2) {

System.err.println("Usage: jview gb2big5 gbfile big5file");

System.exit(1);

   }

String inputString = readInput(args[0]);

writeOutput(inputString,args[1]);

System.out.println("Number of Characters in file: "+iCharNum+".");

}

static void writeOutput(String str, String strOutFile) {

try {

FileOutputStream fos = new FileOutputStream(strOutFile);

Writer out = new OutputStreamWriter(fos, "Big5");

out.write(str);

out.close();

}

catch (IOException e) {

e.printStackTrace();

e.printStackTrace();

}

}

static String readInput(String strInFile) {

StringBuffer buffer = new StringBuffer();

try {

FileInputStream fis = new FileInputStream(strInFile);

InputStreamReader isr = new InputStreamReader(fis, "GB2312");

Reader in = new BufferedReader(isr);

int ch;

while ((ch = in.read()) > -1) {

iCharNum += 1;

buffer.append((char)ch);

}

in.close();

return buffer.toString();

}

catch (IOException e) {

e.printStackTrace();

return null;

}

}

}

编码转化的过程如下：

       ByteToCharGB2312         CharToByteBig5

GB2312------------------>Unicode------------->Big5

执行 java gb2big5 gb.txt big5.txt ，如果 gb.txt 的内容是“今天星期三”，则得到的文件 big5.txt 中的字符能够正确显示；而如果 gb.txt 的内容是“情人节快乐”，则得到的文件 big5.txt 中对应于“节”和“乐”的字符都是符号“？”（0x3F），可见 sun.io.ByteToCharGB2312 和 sun.io.CharToByteBig5 这两个基本类并没有编好。

正如上例一样， Java 的基本类也可能存在问题。由于国际化的工作并不是在国内完成的，所以在这些基本类发布之前，没有经过严格的测试，所以对中文字符的支持并不像 Java Soft 所声称的那样完美。前不久，我的一位技术上的朋友发信给我说，他终于找到了[B] Java Servlet 中文问题的根源[/B]。两周以来，他一直为 Java Servlet 的中文问题所困扰，因为每面对一个含有中文字符的字符串都必须进行强制转换才能够得到正确的结果（这好象是大家公认的唯一的解决办法）。后来，他确实不想如此继续安分下去了，因为这样的事情确实不应该是高级程序员所要做的工作，他就找出 Servlet 解码的源代码进行分析，因为他怀疑问题就出在解码这部分。经过四个小时的奋斗，他终于找到了问题的根源所在。原来他的怀疑是正确的， Servlet 的解码部分完全没有考虑双字节，直接把 %XX 当作一个字符。（原来 Java Soft 也会犯这幺低级的错误！）

如果你对这个问题有兴趣或者遇到了同样的烦恼的话，你可以按照他的步骤[B]对[/B][B] Servlet.jar 进行修改[/B]：

找到源代码 HttpUtils 中的 static private String parseName ，在返回前将 sb（StringBuffer） 复制成 byte bs[] ，然后 return new String(bs,”GB2312”)。作上述修改后就需要自己解码了：

HashTable form=HttpUtils .parseQueryString(request.getQueryString())或者

form=HttpUtils.parsePostData(……)

千万别忘了编译后放到 Servlet.jar 里面。

五、 关于 Java 中文问题的总结

Java 编程语言成长于网络世界，这就要求 Java 对多国字符有很好的支持。 Java 编程语言适应了计算的网络化的需求，为它能够在网络世界迅速成长奠定了坚实的基础。 Java 的缔造者 （Java Soft） 已经考虑到 Java 编程语言对多国字符的支持，只是现在的解决方案有很多缺陷在里面，需要我们付诸一些补偿性的措施。而世界标准化组织也在努力把人类所有的文字统一在一种编码之中，其中一种方案是 ISO10646 ，它用四个字节来表示一个字符。当然，在这种方案未被采用之前，还是希望 Java Soft 能够严格地测试它的产品，为用户带来更多的方便。

[B]附[/B]一个用于从数据库和网络中取出[B]中文乱码的处理函数[/B]，入参是有问题的字符串，出参是问题已经解决了的字符串。

        String parseChinese(String in)

        {

                String s = null;

                byte temp [];

                if (in == null)

                {

                        System.out.println("Warn:Chinese null founded!");

                                return new String("");

                }

                try

                {

                        temp=in.getBytes("iso-8859-1");

                        temp=in.getBytes("iso-8859-1");

                        s = new String(temp);

                }

                {

                        System.out.println("Warn:Chinese null founded!");

                                return new String("");

                }

                try

                {

                        temp=in.getBytes("iso-8859-1");

                        s = new String(temp);

                }

                catch(UnsupportedEncodingException e)

                {

                        System.out.println (e.toString());

                }

                return s;

        }

[b]参考资料[/b][UL][LI][url href=http://smth.org/SMTH2000/bbsdoc?NJava]BBS水木清华站的 Java 讨论区[/url]

[UL][LI]中国最大的电子公告板的 Java 讨论区，有众多高校的 Java 爱好者在此进行关于 Java 技术的讨论 [/LI][/UL][/LI][/UL]

[b]作者简介[/b] [UL][LI]段明辉([email]duanmh@dns.ime.tsinghua.edu.cn[/email])，清华大学电子工程系学生 [UL][LI]现在正在清华大学微电子学研究所从事 Java 智能卡微处理器的研究和开发 [LI]领导 BBS 水木清华站的 Java 讨论组，为众多 Java 技术应用者提供解决方案 [/LI][/UL][/LI][/UL]


Java 编程技术中汉字问题的分析及解决(转自IBM)

作者：sonymusic

email: sonymusic@china.com

日期：2000-12-18 1:13:46

[h1]Java 编程技术中汉字问题的分析及解决[/h1]

段明辉

自由撰稿人

2000 年 11月 8日

[hr]在基于 Java 语言的编程中，我们经常碰到汉字的处理及显示的问题。一大堆看不懂的乱码肯定不是我们愿意看到的显示效果，怎样才能够让那些汉字正确显示呢？Java 语言默认的编码方式是UNICODE ，而我们中国人通常使用的文件和数据库都是基于 GB2312 或者 BIG5 等方式编码的，怎样才能够恰当地选择汉字编码方式并正确地处理汉字的编码呢？本文将从汉字编码的常识入手，结合 Java 编程实例，分析以上两个问题并提出解决它们的方案。[hr]

现在 Java 编程语言已经广泛应用于互联网世界，早在 Sun 公司开发 Java 语言的时候，就已经考虑到对非英文字符的支持了。Sun 公司公布的 Java 运行环境（JRE）本身就分英文版和国际版，但只有国际版才支持非英文字符。不过在 Java 编程语言的应用中，对中文字符的支持并非如同 Java Soft 的标准规范中所宣称的那样完美，因为中文字符集不只一个，而且不同的操作系统对中文字符的支持也不尽相同，所以会有许多和汉字编码处理有关的问题在我们进行应用开发中困扰着我们。有很多关于这些问题的解答，但都比较琐碎，并不能够满足大家迫切解决问题的愿望，关于 Java 中文问题的系统研究并不多，本文从汉字编码常识出发，分析 Java 中文问题，希望对大家解决这个问题有所帮助。

[b]汉字编码的常识[/b]

我们知道，英文字符一般是以一个字节来表示的，最常用的编码方法是 ASCII 。但一个字节最多只能区分256个字符，而汉字成千上万，所以现在都以双字节来表示汉字，为了能够与英文字符分开，每个字节的最高位一定为1，这样双字节最多可以表示64K格字符。我们经常碰到的编码方式有 GB2312、BIG5、UNICODE 等。关于具体编码方式的详细资料，有兴趣的读者可以查阅相关资料。我肤浅谈一下和我们关系密切的 GB2312 和 UNICODE。GB2312 码，中华人民共和国国家标准汉字信息交换用编码，是一个由中华人民共和国国家标准总局发布的关于简化汉字的编码，通行于中国大陆地区及新加坡，简称国标码。两个字节中，第一个字节（高字节）的值为区号值加32（20H），第二个字节（低字节）的值为位号值加32（20H），用这两个值来表示一个汉字的编码。UNICODE 码是微软提出的解决多国字符问题的多字节等长编码，它对英文字符采取前面加“0”字节的策略实现等长兼容。如 “A” 的 ASCII 码为0x41，UNICODE 就为0x00，0x41。利用特殊的工具各种编码之间可以互相转换。

[b]Java 中文问题的初步认识[/b]

我们基于 Java 编程语言进行应用开发时，不可避免地要处理中文。Java 编程语言默认的编码方式是 UNICODE，而我们通常使用的数据库及文件都是基于 GB2312 编码的，我们经常碰到这样的情况：浏览基于 JSP 技术的网站看到的是乱码，文件打开后看到的也是乱码，被 Java 修改过的数据库的内容在别的场合应用时无法继续正确地提供信息。

String sEnglish = “apple”;

String sChinese = “苹果”;

String s = “苹果 apple ”;

sEnglish 的长度是5，sChinese的长度是4，而 s 默认的长度是14。对于 sEnglish来说， Java 中的各个类都支持得非常好，肯定能够正确显示。但对于 sChinese 和 s 来说，虽然 Java Soft 声明 Java 的基本类已经考虑到对多国字符的支持（默认 UNICODE 编码），但是如果操作系统的默认编码不是 UNICODE ，而是国标码等。从 Java 源代码到得到正确的结果，要经过 “Java 源代码-> Java 字节码-> ;虚拟机->操作系统->显示设备”的过程。在上述过程中的每一步骤，我们都必须正确地处理汉字的编码，才能够使最终的显示结果正确。

“ Java 源代码-> Java 字节码”，标准的 Java 编译器 javac 使用的字符集是系统默认的字符集，比如在中文 Windows 操作系统上就是 GBK ,而在 Linux 操作系统上就是ISO-8859-1，所以大家会发现在 Linux 操作系统上编译的类中源文件中的中文字符都出了问题，解决的办法就是在编译的时候添加 encoding 参数，这样才能够与平台无关。用法是

javac –encoding GBK。

“ Java 字节码->虚拟机->操作系统”， Java 运行环境 （JRE） 分英文版和国际版，但只有国际版才支持非英文字符。 Java 开发工具包 （JDK） 肯定支持多国字符，但并非所有的计算机用户都安装了 JDK 。很多操作系统及应用软件为了能够更好的支持 Java ，都内嵌了 JRE 的国际版本，为自己支持多国字符提供了方便。

“操作系统->显示设备”，对于汉字来说，操作系统必须支持并能够显示它。英文操作系统如果不搭配特殊的应用软件的话，是肯定不能够显示中文的。

还有一个问题，就是在 Java 编程过程中，对中文字符进行正确的编码转换。例如，向网页输出中文字符串的时候，不论你是用

out.println(string);       // string 是含中文的字符串

还是用

<%=string%>，都必须作 UNICODE 到 GBK 的转换，或者手动，或者自动。在 JSP 1.0中，可以定义输出字符集，从而实现内码的自动转换。用法是

<%@page ContentType=”text/html;charset=gb2312” %>

但是在一些 JSP 版本中并没有提供对输出字符集的支持，（例如 JSP 0.92），这就需要手动编码输出了，方法非常多。最常用的方法是

String s1 = request.getParameter(“keyword”);

String s2 = new String(s1.getBytes(“ISO-8859-1”),”GBK”);

getBytes 方法用于将中文字符以“ISO-8859-1”编码方式转化成字节数组，而“GBK” 是目标编码方式。我们从以ISO-8859-1方式编码的数据库中读出中文字符串 s1 ，经过上述转换过程，在支持 GBK 字符集的操作系统和应用软件中就能够正确显示中文字符串 s2 。

[b]Java 中文问题的表层分析及处理[/b]

背景

开发环境

 JDK1.15

 Vcafe2.0

 JPadPro

服务器端

 NT IIS

 Sybase System

 Jconnect（JDBC）

客户端

 IE5.0

 Pwin98

.CLASS 文件存放在服务器端，由客户端的浏览器运行 APPLET ， APPLET 只起调入 FRAME 类等主程序的作用。界面包括 Textfield ，TextArea，List，Choice 等。

I.       取中文

用 JDBC 执行 SELECT 语句从服务器端读取数据（中文）后，将数据用 APPEND 方法加到 TextArea（TA） ，不能正确显示。但加到 List 中时，大部分汉字却可正确显示。

将数据按“ISO-8859-1” 编码方式转化为字节数组，再按系统缺省编码方式 （Default Character Encoding） 转化为 STRING ，即可在 TA 和 List 中正确显示。

程序段如下：

dbstr2 = results.getString(1);

//After reading the result from DB server，converting it to string.

dbbyte1 = dbstr2.getBytes(“iso-8859-1”);

dbstr1 = new String(dbbyte1);

在转换字符串时不采用系统默认编码方式，而直接采用“ GBK” 或者 “GB2312” ,在 A 和 B 两种情况下，从数据库取数据都没有问题。

II.    写中文到数据库

处理方式与“取中文”相逆，先将 SQL 语句按系统缺省编码方式转化为字节数组，再按“ISO-8859-1”编码方式转化为 STRING ，最后送去执行，则中文信息可正确写入数据库。

程序段如下：

sqlstmt = tf_input.getText();

//Before sending statement to DB server，converting it to sql statement.

dbbyte1 = sqlstmt.getBytes();

sqlstmt = newString(dbbyte1,”iso-8859-1”);

_stmt = _con.createStatement();

_stmt.executeUpdate(sqlstmt);

……

问题：如果客户机上存在 CLASSPATH 指向 JDK 的 CLASSES.ZIP 时（称为 A 情况），上述程序代码可正确执行。但是如果客户机只有浏览器，而没有 JDK 和 CLASSPATH 时（称为 B 情况），则汉字无法正确转换。

我们的分析：

1.经过测试，在 A 情况下，程序运行时系统的缺省编码方式为 GBK 或者 GB2312 。在 B 情况下，程序启动时浏览器的 JAVA 控制台中出现如下错误信息：

Can't find resource for sun.awt.windows.awtLocalization_zh_CN

然后系统的缺省编码方式为“8859-1”。

2.如果在转换字符串时不采用系统缺省编码方式，而是直接采用 “GBK” 或“GB2312”，则在 A 情况下程序仍然可正常运行，在 B 情况下，系统出现错误：

UnsupportedEncodingException。

3.在客户机上，把 JDK 的 CLASSES.ZIP 解压后，放在另一个目录中， CLASSPATH 只包含该目录。然后一边逐步删除该目录中的 .CLASS 文件，另一边运行测试程序，最后发现在一千多个 CLASS 文件中，只有一个是必不可少的，该文件是：

sun.io.CharToByteDoubleByte.class。

将该文件拷到服务器端和其它的类放在一起，并在程序的开头 IMPORT 它，在 B 情况下程序仍然无法正常运行。

4.在 A 情况下，如果在 CLASSPTH 中去掉 sun.io.CharToByteDoubleByte.class ，则程序运行时测得默认编码方式为“8859-1”，否则为 “GBK” 或 “GB2312” 。

如果 JDK 的版本为1.2以上的话，在 B 情况下遇到的问题得到了很好的解决，测试的步骤同上，有兴趣的读者可以尝试一下。

[/b]Java 中文问题的根源分析及解决[/b]

在简体中文 MS Windows 98 + JDK 1.3 下，可以用 System.getProperties() 得到 Java 运行环境的一些基本属性，类 PoorChinese 可以帮助我们得到这些属性。

类 PoorChinese 的源代码：

public class PoorChinese {

   public static void main(String[] args) {

       System.getProperties().list(System.out);

   }

}

执行 java PoorChinese 后，我们会得到:

系统变量 file.encoding 的值为 GBK ，user.language 的值为 zh ， user.region 的值为 CN ，这些系统变量的值决定了系统默认的编码方式是 GBK 。

在上述系统中，下面的代码将 GB2312 文件转换成 Big5 文件，它们能够帮助我们理解 Java 中汉字编码的转化:

import java.io.*;

import java.util.*;

public class gb2big5 {

static int iCharNum=0;

public static void main(String[] args) {

System.out.println("Input GB2312 file, output Big5 file.");

if (args.length!=2) {

System.err.println("Usage: jview gb2big5 gbfile big5file");

System.exit(1);

   }

String inputString = readInput(args[0]);

writeOutput(inputString,args[1]);

System.out.println("Number of Characters in file: "+iCharNum+".");

}

static void writeOutput(String str, String strOutFile) {

try {

FileOutputStream fos = new FileOutputStream(strOutFile);

Writer out = new OutputStreamWriter(fos, "Big5");

out.write(str);

out.close();

}

catch (IOException e) {

e.printStackTrace();

e.printStackTrace();

}

}

static String readInput(String strInFile) {

StringBuffer buffer = new StringBuffer();

try {

FileInputStream fis = new FileInputStream(strInFile);

InputStreamReader isr = new InputStreamReader(fis, "GB2312");

Reader in = new BufferedReader(isr);

int ch;

while ((ch = in.read()) > -1) {

iCharNum += 1;

buffer.append((char)ch);

}

in.close();

return buffer.toString();

}

catch (IOException e) {

e.printStackTrace();

return null;

}

}

}

编码转化的过程如下：

       ByteToCharGB2312         CharToByteBig5

GB2312------------------>Unicode------------->Big5

执行 java gb2big5 gb.txt big5.txt ，如果 gb.txt 的内容是“今天星期三”，则得到的文件 big5.txt 中的字符能够正确显示；而如果 gb.txt 的内容是“情人节快乐”，则得到的文件 big5.txt 中对应于“节”和“乐”的字符都是符号“？”（0x3F），可见 sun.io.ByteToCharGB2312 和 sun.io.CharToByteBig5 这两个基本类并没有编好。

正如上例一样， Java 的基本类也可能存在问题。由于国际化的工作并不是在国内完成的，所以在这些基本类发布之前，没有经过严格的测试，所以对中文字符的支持并不像 Java Soft 所声称的那样完美。前不久，我的一位技术上的朋友发信给我说，他终于找到了 Java Servlet 中文问题的根源。两周以来，他一直为 Java Servlet 的中文问题所困扰，因为每面对一个含有中文字符的字符串都必须进行强制转换才能够得到正确的结果（这好象是大家公认的唯一的解决办法）。后来，他确实不想如此继续安分下去了，因为这样的事情确实不应该是高级程序员所要做的工作，他就找出 Servlet 解码的源代码进行分析，因为他怀疑问题就出在解码这部分。经过四个小时的奋斗，他终于找到了问题的根源所在。原来他的怀疑是正确的， Servlet 的解码部分完全没有考虑双字节，直接把 %XX 当作一个字符。（原来 Java Soft 也会犯这幺低级的错误！）

如果你对这个问题有兴趣或者遇到了同样的烦恼的话，你可以按照他的步骤对 Servlet.jar 进行修改：

找到源代码 HttpUtils 中的 static private String parseName ，在返回前将 sb（StringBuffer） 复制成 byte bs[] ，然后 return new String(bs,”GB2312”)。作上述修改后就需要自己解码了：

HashTable form=HttpUtils .parseQueryString(request.getQueryString())或者

form=HttpUtils.parsePostData(……)

千万别忘了编译后放到 Servlet.jar 里面。

[b]五、 关于 Java 中文问题的总结 [/b]

Java 编程语言成长于网络世界，这就要求 Java 对多国字符有很好的支持。 Java 编程语言适应了计算的网络化的需求，为它能够在网络世界迅速成长奠定了坚实的基础。 Java 的缔造者 （Java Soft） 已经考虑到 Java 编程语言对多国字符的支持，只是现在的解决方案有很多缺陷在里面，需要我们付诸一些补偿性的措施。而世界标准化组织也在努力把人类所有的文字统一在一种编码之中，其中一种方案是 ISO10646 ，它用四个字节来表示一个字符。当然，在这种方案未被采用之前，还是希望 Java Soft 能够严格地测试它的产品，为用户带来更多的方便。

附一个用于从数据库和网络中取出中文乱码的处理函数，入参是有问题的字符串，出参是问题已经解决了的字符串。

        String parseChinese(String in)

        {

                String s = null;

                byte temp [];

                if (in == null)

                {

                        System.out.println("Warn:Chinese null founded!");

                                return new String("");

                }

                try

                {

                        temp=in.getBytes("iso-8859-1");

                        temp=in.getBytes("iso-8859-1");

                        s = new String(temp);

                }

                {

                        System.out.println("Warn:Chinese null founded!");

                                return new String("");

                }

                try

                {

                        temp=in.getBytes("iso-8859-1");

                        s = new String(temp);

                }

                catch(UnsupportedEncodingException e)

                {

                        System.out.println (e.toString());

                }

                return s;

        }

[hr]

[b]作者简介[/b]

段明辉，清华大学电子工程系学生

现在正在清华大学微电子学研究所从事 Java 智能卡微处理器的研究和开发

领导 BBS 水木清华站的 Java 讨论组，为众多 Java 技术应用者提供解决方案


Java 程序编码规范

作者：zergman

email: zergman@chinaasp.com

日期：2001-6-15 13:24:42

[BLOCKQUOTE][BLOCKQUOTE][BLOCKQUOTE][BLOCKQUOTE][BLOCKQUOTE][BLOCKQUOTE]

[b]Java 程序编码规范[/b]

[b][/b][/BLOCKQUOTE][/BLOCKQUOTE][/BLOCKQUOTE][/BLOCKQUOTE][/BLOCKQUOTE][/BLOCKQUOTE]

李小敏

http://www.yway.com软件工程师

2000年12月

[TABLE][TR][TD][#ffffff][B]内容:[/B][/#][/TD][/TR][TR][TD][img]http://www.cn.ibm.com/developerWorks/i/c.gif[/img][/TD][/TR][TR][TD][img]http://www.cn.ibm.com/developerWorks/i/c.gif[/img][/TD][/TR][TR][TD][url href=http://www.cn.ibm.com/developerWorks/java/java_standard/index.shtml#1]命名惯例[/url][/TD][/TR][TR][TD] [url href=http://www.cn.ibm.com/developerWorks/java/java_standard/index.shtml#2]Java文件样式[/url][/TD][/TR][TR][TD] [url href=http://www.cn.ibm.com/developerWorks/java/java_standard/index.shtml#3]代码编写格式[/url][/TD][/TR][TR][TD] [url href=http://www.cn.ibm.com/developerWorks/java/java_standard/index.shtml#4]程序编写[/url][/TD][/TR][TR][TD] [url href=http://www.cn.ibm.com/developerWorks/java/java_standard/index.shtml#5]编程技巧[/url][/TD][/TR][TR][TD] [url href=http://www.cn.ibm.com/developerWorks/java/java_standard/index.shtml#6]Swing[/url][/TD][/TR][TR][TD] [url href=http://www.cn.ibm.com/developerWorks/java/java_standard/index.shtml#7]调试[/url][/TD][/TR][TR][TD] [url href=http://www.cn.ibm.com/developerWorks/java/java_standard/index.shtml#8]性能[/url][/TD][/TR][TR][TD] [url href=http://www.cn.ibm.com/developerWorks/java/java_standard/index.shtml#9]可移植性[/url][/TD][/TR][TR][TD] [url href=http://www.cn.ibm.com/developerWorks/java/java_standard/index.shtml#resources]参考资料[/url][/TD][/TR][TR][TD] [url href=http://www.cn.ibm.com/developerWorks/java/java_standard/index.shtml#author]作者简介[/url][/TD][/TR][TR][TD][img]http://www.cn.ibm.com/developerWorks/i/c.gif[/img][/TD][/TR][/TABLE]

[i]    所有的程序开发手册都包含了各种规则。一些习惯自由程序人员可能对这些规则很不适应，但是在多个开发人员共同写作的情况下，这些规则是必需的。这不仅仅是为了开发效率来考虑，而且也是为了后期维护考虑。[/i]

[b]命名规范[/b]

定义这个规范的目的是让项目中所有的文档都看起来像一个人写的，增加可读性，减少项目组中因为换人而带来的损失。（这些规范并不是一定要绝对遵守，但是一定要让程序有良好的可读性）

[UL][LI]Package 的命名

Package 的名字应该都是由一个小写单词组成。 [/LI][/UL][UL][LI]Class 的命名

Class 的名字必须由大写字母开头而其他字母都小写的单词组成 [/LI][/UL][UL][LI]Class 变量的命名

变量的名字必须用一个小写字母开头。后面的单词用大写字母开头。 [/LI][/UL][UL][LI]Static Final 变量的命名

Static Final 变量的名字应该都大写，并且指出完整含义。 [/LI][/UL][UL][LI]参数的命名

参数的名字必须和变量的命名规范一致。 [/LI][/UL][UL][LI]数组的命名

数组应该总是用下面的方式来命名： [TABLE][TR][TD]

[PRE]byte[] buffer;        [/PRE][/TD][/TR][/TABLE]而不是： [TABLE][TR][TD]

[PRE]byte buffer[];        [/PRE][/TD][/TR][/TABLE][/LI][/UL][UL][LI]方法的参数

使用有意义的参数命名，如果可能的话，使用和要赋值的字段一样的名字： [TABLE][TR][TD]

[PRE]SetCounter(int size){    this.size = size;}        [/PRE][/TD][/TR][/TABLE][/LI][/UL]

[b]Java 文件样式[/b]

所有的 Java(*.java) 文件都必须遵守如下的样式规则

[UL][LI]版权信息

版权信息必须在 java 文件的开头，比如： [TABLE][TR][TD]

[PRE]/** * Copyright &reg; 2000 Shanghai XXX Co. Ltd. * All right reserved. */        [/PRE][/TD][/TR][/TABLE]其他不需要出现在 javadoc 的信息也可以包含在这里。

[/LI][/UL][UL][LI]Package/Imports

package 行要在 import 行之前，import 中标准的包名要在本地的包名之前，而且按照字母顺序排列。如果 import 行中包含了同一个包中的不同子目录，则应该用 * 来处理。 [TABLE][TR][TD]

[PRE]package hotlava.net.stats;import java.io.*;import java.util.Observable;import hotlava.util.Application;        [/PRE][/TD][/TR][/TABLE]这里 java.io.* 使用来代替InputStream and OutputStream 的。

[/LI][/UL][UL][LI]Class

接下来的是类的注释，一般是用来解释类的。 [TABLE][TR][TD]

[PRE]/** * A class representing a set of packet and byte counters * It is observable to allow it to be watched, but only * reports changes when the current set is complete */        [/PRE][/TD][/TR][/TABLE]接下来是类定义，包含了在不同的行的 extends 和 implements [TABLE][TR][TD]

[PRE]public class CounterSet        extends Observable        implements Cloneable        [/PRE][/TD][/TR][/TABLE][/LI][/UL][UL][LI]Class Fields

接下来是类的成员变量： [TABLE][TR][TD]

[PRE]/** * Packet counters */protected int[] packets;[/PRE][/TD][/TR][/TABLE]public 的成员变量必须生成文档（JavaDoc）。proceted、private和 package 定义的成员变量如果名字含义明确的话，可以没有注释。

[/LI][/UL][UL][LI]存取方法

接下来是类变量的存取的方法。它只是简单的用来将类的变量赋值获取值的话，可以简单的写在一行上。 [TABLE][TR][TD]

[PRE]/** * Get the counters * @return an array containing the statistical data.  This array has been * freshly allocated and can be modified by the caller. */public int[] getPackets() { return copyArray(packets, offset); }public int[] getBytes() { return copyArray(bytes, offset); }public int[] getPackets() { return packets; }public void setPackets(int[] packets) { this.packets = packets; }[/PRE][/TD][/TR][/TABLE]其它的方法不要写在一行上

[/LI][/UL][UL][LI]构造函数

接下来是构造函数，它应该用递增的方式写（比如：参数多的写在后面）。

访问类型 ("public", "private" 等.) 和 任何 "static", "final" 或 "synchronized" 应该在一行中，并且方法和参数另写一行，这样可以使方法和参数更易读。 [TABLE][TR][TD]

[PRE]publicCounterSet(int size){  this.size = size;}[/PRE][/TD][/TR][/TABLE][/LI][/UL][UL][LI]克隆方法

如果这个类是可以被克隆的，那么下一步就是 clone 方法： [TABLE][TR][TD]

[PRE]publicObject clone() {  try {    CounterSet obj = (CounterSet)super.clone();    obj.packets = (int[])packets.clone();    obj.size = size;    return obj;  }catch(CloneNotSupportedException e) {    throw new InternalError("Unexpected CloneNotSUpportedException: " + e.getMessage());  }}[/PRE][/TD][/TR][/TABLE][/LI][/UL][UL][LI]类方法

下面开始写类的方法： [TABLE][TR][TD]

[PRE]/** * Set the packet counters * (such as when restoring from a database) */protected finalvoid setArray(int[] r1, int[] r2, int[] r3, int[] r4)  throws IllegalArgumentException{  //  // Ensure the arrays are of equal size  //  if (r1.length != r2.length || r1.length != r3.length || r1.length != r4.length)    throw new IllegalArgumentException("Arrays must be of the same size");  System.arraycopy(r1, 0, r3, 0, r1.length);  System.arraycopy(r2, 0, r4, 0, r1.length);}[/PRE][/TD][/TR][/TABLE][/LI][/UL][UL][LI]toString 方法

无论如何，每一个类都应该定义 toString 方法： [TABLE][TR][TD]

[PRE]publicString toString() {  String retval = "CounterSet: ";    for (int i = 0; i < data.length(); i++) {      retval += data.bytes.toString();      retval += data.packets.toString();    }    return retval;  }}[/PRE][/TD][/TR][/TABLE][/LI][/UL][UL][LI]main 方法

如果main(String[]) 方法已经定义了, 那么它应该写在类的底部. [/LI][/UL]

[b]代码编写格式[/b]

[UL][LI]代码样式

代码应该用 unix 的格式，而不是 windows 的（比如：回车变成回车+换行） [/LI][/UL][UL][LI]文档化

必须用 javadoc 来为类生成文档。不仅因为它是标准，这也是被各种 java 编译器都认可的方法。使用 @author 标记是不被推荐的，因为代码不应该是被个人拥有的。 [/LI][/UL][UL][LI]缩进

缩进应该是每行2个空格. 不要在源文件中保存Tab字符. 在使用不同的源代码管理工具时Tab字符将因为用户设置的不同而扩展为不同的宽度.

如果你使用 UltrEdit 作为你的 Java 源代码编辑器的话，你可以通过如下操作来禁止保存Tab字符, 方法是通过 UltrEdit中先设定 Tab 使用的长度室2个空格，然后用 Format|Tabs to Spaces 菜单将 Tab 转换为空格。 [/LI][/UL][UL][LI]页宽

页宽应该设置为80字符. 源代码一般不会超过这个宽度, 并导致无法完整显示, 但这一设置也可以灵活调整. 在任何情况下, 超长的语句应该在一个逗号或者一个操作符后折行. 一条语句折行后, 应该比原来的语句再缩进2个字符. [/LI][/UL][UL][LI]{} 对

{} 中的语句应该单独作为一行. 例如, 下面的第1行是错误的, 第2行是正确的: [TABLE][TR][TD]

[PRE]if (i>0) { i ++ }; // 错误, { 和 } 在同一行 if (i>0) { i ++ };       // 正确, { 单独作为一行 } 语句永远单独作为一行. [/PRE][/TD][/TR][/TABLE]如果 } 语句应该缩进到与其相对应的 { 那一行相对齐的位置。

[/LI][/UL][UL][LI]括号

左括号和后一个字符之间不应该出现空格, 同样, 右括号和前一个字符之间也不应该出现空格. 下面的例子说明括号和空格的错误及正确使用:

CallProc( AParameter ); // 错误

CallProc(AParameter); // 正确

不要在语句中使用无意义的括号. 括号只应该为达到某种目的而出现在源代码中。下面的例子说明错误和正确的用法:

if ((I) = 42) { // 错误 - 括号毫无意义

if (I == 42) or (J == 42) then // 正确 - 的确需要括号

[/LI][/UL]

[b]程序编写规范[/b]

[UL][LI]exit()

exit 除了在 main 中可以被调用外，其他的地方不应该调用。因为这样做不给任何代码代码机会来截获退出。一个类似后台服务地程序不应该因为某一个库模块决定了要退出就退出。 [/LI][/UL][UL][LI]异常

申明的错误应该抛出一个RuntimeException或者派生的异常。

顶层的main()函数应该截获所有的异常，并且打印（或者记录在日志中）在屏幕上。 [/LI][/UL][UL][LI]垃圾收集

JAVA使用成熟的后台垃圾收集技术来代替引用计数。但是这样会导致一个问题：你必须在使用完对象的实例以后进行清场工作。比如一个prel的程序员可能这么写： [TABLE][TR][TD]

[PRE]    ...    {        FileOutputStream fos = new FileOutputStream(projectFile);        project.save(fos, "IDE Project File");     }    ...[/PRE][/TD][/TR][/TABLE]除非输出流一出作用域就关闭，非引用计数的程序语言，比如JAVA，是不能自动完成变量的清场工作的。必须象下面一样写： [TABLE][TR][TD]

[PRE]    FileOutputStream fos = new FileOutputStream(projectFile);    project.save(fos, "IDE Project File");     fos.close();[/PRE][/TD][/TR][/TABLE][/LI][/UL][UL][LI]Clone

下面是一种有用的方法： [TABLE][TR][TD]

[PRE]  implements Cloneable  public    Object clone()    {      try {        ThisClass obj = (ThisClass)super.clone();        obj.field1 = (int[])field1.clone();        obj.field2 = field2;        return obj;      } catch(CloneNotSupportedException e) {        throw new InternalError("Unexpected CloneNotSUpportedException: " + e.getMessage());      }  }[/PRE][/TD][/TR][/TABLE][/LI][/UL][UL][LI]final 类

绝对不要因为性能的原因将类定义为 final 的（除非程序的框架要求）

如果一个类还没有准备好被继承，最好在类文档中注明，而不要将她定义为 final 的。这是因为没有人可以保证会不会由于什么原因需要继承她。 [/LI][/UL][UL][LI]访问类的成员变量

大部分的类成员变量应该定义为 protected 的来防止继承类使用他们。

注意，要用"int[] packets"，而不是"int packets[]"，后一种永远也不要用。 [TABLE][TR][TD]

[PRE]    public void setPackets(int[] packets) { this.packets = packets; }      CounterSet(int size)        {          this.size = size;        }[/PRE][/TD][/TR][/TABLE][/LI][/UL]

[b]编程技巧[/b]

[UL][LI]byte 数组转换到 characters

为了将 byte 数组转换到 characters，你可以这么做：

"Hello world!".getBytes();

[/LI][/UL][UL][LI]Utility 类

Utility 类（仅仅提供方法的类）应该被申明为抽象的来防止被继承或被初始化。

[/LI][/UL][UL][LI]初始化

下面的代码是一种很好的初始化数组的方法：

objectArguments = new Object[] { arguments };

[/LI][/UL][UL][LI]枚举类型

JAVA 对枚举的支持不好，但是下面的代码是一种很有用的模板： [TABLE][TR][TD]

[PRE]class Colour {  public static final Colour BLACK = new Colour(0, 0, 0);  public static final Colour RED = new Colour(0xFF, 0, 0);  public static final Colour GREEN = new Colour(0, 0xFF, 0);  public static final Colour BLUE = new Colour(0, 0, 0xFF);  public static final Colour WHITE = new Colour(0xFF, 0xFF, 0xFF);}[/PRE][/TD][/TR][/TABLE]这种技术实现了RED, GREEN, BLUE 等可以象其他语言的枚举类型一样使用的常量。 他们可以用 '==' 操作符来比较。

但是这样使用有一个缺陷：如果一个用户用这样的方法来创建颜色 BLACK

new Colour(0,0,0)

那么这就是另外一个对象，'=='操作符就会产生错误。她的 equal() 方法仍然有效。由于这个原因，这个技术的缺陷最好注明在文档中，或者只在自己的包中使用。 [/LI][/UL]

[b]Swing[/b]

[UL][LI]避免使用 AWT 组件 [UL][LI]混合使用 AWT 和 Swing 组件

如果要将 AWT 组件和 Swing 组件混合起来使用的话，请小心使用。实际上，尽量不要将他们混合起来使用。

[/LI][/UL][UL][LI]滚动的 AWT 组件

AWT 组件绝对不要用 JscrollPane 类来实现滚动。滚动 AWT 组件的时候一定要用 AWT ScrollPane 组件来实现。

[/LI][/UL][UL][LI]避免在 InternalFrame 组件中使用 AWT 组件

尽量不要这么做，要不然会出现不可预料的后果。

[/LI][/UL][UL][LI]Z-Order 问题

AWT 组件总是显示在 Swing 组件之上。当使用包含 AWT 组件的 POP-UP 菜单的时候要小心，尽量不要这样使用。

[/LI][/UL][/LI][/UL]

[b]调试[/b]

[UL][LI]调试在软件开发中是一个很重要的部分，存在软件生命周期的各个部分中。调试能够用配置开、关是最基本的。

很常用的一种调试方法就是用一个 PrintStream 类成员，在没有定义调试流的时候就为 null，类要定义一个 debug 方法来设置调试用的流。

[/LI][/UL]

[b]性能[/b]

[UL][LI]在写代码的时候，从头至尾都应该考虑性能问题。这不是说时间都应该浪费在优化代码上，而是我们时刻应该提醒自己要注意代码的效率。比如：如果没有时间来实现一个高效的算法，那么我们应该在文档中记录下来，以便在以后有空的时候再来实现她。

不是所有的人都同意在写代码的时候应该优化性能这个观点的，他们认为性能优化的问题应该在项目的后期再去考虑，也就是在程序的轮廓已经实现了以后。 [/LI][/UL][UL][LI]不必要的对象构造 [UL][LI]不要在循环中构造和释放对象

[/LI][/UL][UL][LI]使用 StringBuffer 对象

在处理 String 的时候要尽量使用 StringBuffer 类，StringBuffer 类是构成 String 类的基础。String 类将 StringBuffer 类封装了起来，（以花费更多时间为代价）为开发人员提供了一个安全的接口。当我们在构造字符串的时候，我们应该用 StringBuffer 来实现大部分的工作，当工作完成后将 StringBuffer 对象再转换为需要的 String 对象。比如：如果有一个字符串必须不断地在其后添加许多字符来完成构造，那么我们应该使用 StringBuffer 对象和她的 append() 方法。如果我们用 String 对象代替 StringBuffer 对象的话，会花费许多不必要的创建和释放对象的 CPU 时间。

[/LI][/UL][/LI][/UL][UL][LI]避免太多的使用 synchronized 关键字

避免不必要的使用关键字 synchronized，应该在必要的时候再使用她，这是一个避免死锁的好方法。 [/LI][/UL]

[b]可移植性[/b]

Borland Jbulider 不喜欢 synchronized 这个关键字，如果你的断点设在这些关键字的作用域内的话，调试的时候你会发现的断点会到处乱跳，让你不知所措。除非必须，尽量不要使用。

[UL][LI]换行

如果需要换行的话，尽量用 println 来代替在字符串中使用"\n"。

你不要这样：

System.out.print("Hello,world!\n");

要这样：

System.out.println("Hello,world!");

或者你构造一个带换行符的字符串，至少要象这样：

String newline = System.getProperty("line.separator");

System.out.println("Hello world" + newline);

[/LI][/UL][UL][LI]PrintStream

PrintStream 已经被不赞成（deprecated）使用，用 PrintWrite 来代替她。 [/LI][/UL]

[b]参考资料[/b][UL][LI]查找有关 Thread 类和 [url href=http://java.sun.com/products/jdk/1.2/docs/api/index.html]Java2 平台，标准版，API 规范[/url]的其他方面的信息。 [/LI][/UL]


java 技术分析

作者：seaside

日期：2001-4-20 11:52:17

代码一致性问题

java 的代码是采取伪编译的，属于特殊的脚本文件；

既然是脚本，当然是“跨平台的”；

知道 php 吧？

编程思想问题

java 确实引入了一些新的概念，对于编程是一种促进；

语言表现形式

java 使用的是改进了的 C++ 风格，通过格式化的命名解决了 C 中命名中断问题；

微软的 .net 也参考了它的风格；

这一点值得我们学习；

界面问题

java 的界面让人不敢恭维；

在所有平台表现都是同样糟糕的（图形或者字符）界面；

程序员所接触的是阔别多年的字符界面（让人感到很亲切）；

运行效率问题

由于它是解释执行，效率自然很低；

最典型的是 JBuilder，让我们在工作之余可以轻松的喝杯咖啡，作为 java 的程序员，真应该感谢 sun；

有人说“可以理解做企业级应用的程序员对JAVA的喜爱。从他们的立场，软件容易修改和移植比运行效率高要重要的多”，真的这样吗？我对此深表怀疑；

还有人说“语言变得无意义，每种语言都能做相同的事,并有相同的执行效率,这是马上要发生的事”，但对于脚本语言（包括 java），是不可能达到编译语言的执行效率的，看来，我们只能用 java 跟 php 相比了；

投资问题

java 应用的开发和运行费用是很高的；

操作系统和数据库都需要从第三方购买；

（大家都感觉 M$ 黑，现在看来甲骨文更黑）

历史遗留问题

java 是个“全新的”系统，所有的历史遗留它一概不管；

采用 java，原来的东西都推翻了重做，浪费多严重？

创新问题

java 是有些创新思想，但如果说它是“全新的”语言，嘿嘿，sun 好意思承认吗？

很多概念早有了，大家都是在学习前人经验基础上发展的，java 当然也不例外；

很多原来就有的东西，sun 改头换面，或许是为了避免纠纷，或许是为了哗众取宠，炮制出新的概念，欺世盗名；

有人说“微软最为擅长的就是在技术落后的情况下，借鉴别人的想法，整合到自己的战车上”，sun 难道比微软高明多少？

技术垄断问题

众所周知，sun 也是封闭源代码的；

或许，现在有些东西 sun 是免费提供的，当它真正强大以后，鬼知道他们会怎样；

舍弃微软，投奔太阳，所谓才离狼穴，又入虎口；

sun 的野心，不可不察；

JIT 的必要性

采取字节码（脚本）是为了跨平台；

但是，众所周知，代码的更改并不是经常的，运行次数比版本发布的次数不知道要高上多少倍，每次运行都重新编译，有这个必要吗？！

“虚拟机”问题

java “虚拟机”其实是一个脚本解释执行程序，换了名字而已；

有人说“虚拟机是一种强大的故障保护系统还是有一定现实意义的”，虚拟机跟故障保护系统没有丝毫关系，况且 java 程序出错的情况在 chinaren 上面是有目共睹的；

为什么使用 java

可以达成共识的只有“为了混饭吃”；

有个“职业程序员”说是因为“流行”，但他说的“职业的人注重走路本身，不管走到哪里，因为它往往要自己找路走，更多的人是为了到达目的，这是个侧重点的问题”就很古怪，用 java 是为了用着玩，而不是为了使项目达到目的？

还有个说法，“随便找个做MIS的，稍微白呼两句就能扔出去做JAVA高手状了”；

有个对于 java 崇拜的五体投地的人说漏了嘴，“开发服务器软件现在最佳的语言还是c,c++,需要画大量的用户界面还是用vb,pb快些”，哈哈；


Java 线 程 的 讨 论 与 应 用 (转)

此电子文档属C#乐园QQ群3256045整理。
Java 线 程 的 讨 论 与 应 用 (转)

上一页 目录 下一页

Java 线 程 的 讨 论 与 应 用 (转)

作者：jeru

日期：2001-3-7 11:20:12

Java 线 程 的 讨 论 与 应 用

龙 士 工

一、 为 什 么 要 研 究 和 使 用 线 程

一 般 来 说， 计 算 机 正 在 执 行 的 程 序 称 作 进 程（process)， 进 程 有 不 同 的 地 址 空 间 并 且 是 在 同 一 系 统 上 运 行 的 不 同 程 序， 如 Ｗ Ｏ Ｒ Ｄ 和Excel， 进 程 间 的 通 讯 是 很 费 时 而 且 有 限 的。 上 下 文 切 换、 改 变 运 行 的 进 程 也 是 非 常 复 杂 的。 进 程 间 通 讯 复 杂， 可 能 需 要 管 道、 消 息 队 列、 共 享 内 存(sharedmemory) 或 信 号 处 理 来 保 证 进 程 间 的 通 讯。 尽 管 许 多 程 序 都 在 运 行， 但 一 次 只 能 与 一 个 程 序 打 交 道。

线 程（thread) 是 指 进 程 中 单 一 顺 序 的 控 制 流。 又 称 为 轻 量 级 进 程。 线 程 则 共 享 相 同 的 地 址 空 间 并 共 同 构 成 一 个 大 的 进 程。 线 程 间 的 通 讯 是 非 常 简 单 而 有 效 的， 上 下 文 切 换 非 常 快 并 且 是 整 个 大 程 序 的 一 部 分 切 换。 线 程 仅 是 过 程 调 用， 它 们 彼 此 独 立 执 行， 线 程 使 得 在 一 个 应 用 程 序 中， 程 序 的 编 写 更 加 自 由 和 丰 富。 线 程 的 兴 趣 在 于， 一 个 程 序 中 同 时 使 用 多 个 线 程 来 完 成 不 同 的 任 务。 因 此 如 果 很 好 地 利 用 线 程， 可 以 大 大 简 化 应 用 程 序 设 计。 多 线 程 可 以 增 进 程 序 的 交 互 性， 提 供 更 好 的 能 力 和 功 能、 更 好 的GUI 和 更 好 的 服 务 器 功 能。 给 二 个 例 子 说 明 如 下：

例 一： 利 用 多 线 程 并 行 机 制 可 以 很 好 地 解 决 交 互 式 网 络 程 序 中 的 许 多 问 题， 如： 大 量 的 网 络 文 件 资 源 的 读 写、 用 户 输 入 响 应、 动 画 显 示 等 问 题 不 需 要 Ｃ Ｐ Ｕ 的 多 少 时 间； 而 耗 时 的 复 杂 计 算 通 常 并 不 需 要 立 即 响 应， 所 以 无 需 将 Ｃ Ｐ Ｕ 全 给 它。 例 如， 从 一 个 慢 速 的 网 络 上 读 取 一 数 据 流 也 许 要 １ 分 钟 时 间， 但 需 要 Ｃ Ｐ Ｕ 参 与 传 输 数 据 的 时 间 则 非 常 短； 响 应 用 户 的 输 入 如 击 键， 就 算 最 快 的 输 入 员， １ 秒 钟 击 键 １ ０ 次， 也 不 需 要 Ｃ Ｐ Ｕ 的 多 少 时 间。 动 画 程 序 比 较 耗 时， 一 幅 画 在 １ 秒 内 要 重 绘 ５ － １ ０ 次， 但 Ｃ Ｐ Ｕ 在 大 部 分 时 间 仍 处 于 空 闲 状 态。 在 传 统 的 单 线 程 环 境 下 的 问 题 是 用 户 必 须 等 待 每 个 任 务 完 成 后 才 能 进 行 下 一 个 任 务。 即 使 Ｃ Ｐ Ｕ 大 部 分 时 间 空 闲， 也 只 能 按 步 就 班 地 工 作。 多 线 程 可 以 很 好 地 解 决 这 些 问 题 避 免 引 起 用 户 的 等 待。 如： 耗 时 的 复 杂 计 算 应 用 就 可 划 分 成 两 个 控 制 线 程: 一 个 处 理GUI 的 用 户 事 件, 另 一 个 进 行 后 台 计 算。

例 二： 如 并 发 服 务 器， 它 面 向 不 定 长 时 间 内 处 理 完 的 请 求, 对 每 个 请 求 由 服 务 器 的 线 程 处 理。 传 统 的 并 发 服 务 器 往 往 是 基 于 多 进 程 机 制 的, 每 个 客 户 一 个 进 程, 需 要 操 作 系 统 的 干 预, 进 程 的 数 目 受 操 作 系 统 的 限 制。 本 文 利 用Java 的 线 程 机 制 建 立 了 基 于 多 线 程 的 并 发 服 务 器。 生 成 和 管 理 他 们 是 相 当 简 单 的 操 作。 线 程 被 用 来 建 立 请 求 驱 动 的 服 务 程 序, 每 个 客 户 一 个 线 程, 多 个 线 程 可 以 并 发 执 行。 特 别 地 线 程 具 有 如 下 特 性（1） 线 程 共 享 父 进 程 的 所 有 程 序 和 数 据（2） 有 自 身 的 运 行 单 元（3） 有 它 自 己 的 私 有 存 储 和 执 行 环 境（ 尤 其 是 处 理 器 寄 存 器）， 使 得 服 务 器 进 程 不 随 客 户 数 的 增 加 而 线 性 增 加。 可 减 少 服 务 器 进 程 的 压 力, 降 低 开 销, 充 分 利 用CPU 的 资 源。 以 上 并 发 服 务 器 在 某 一 瞬 间 由 同 一 服 务 器 进 程 所 产 生 的 多 个 并 发 线 程 对 多 个 客 户 的 并 发 请 求 采 取 分 而 治 之 的 措 施, 从 而 解 决 了 并 发 请 求 的 问 题。 各 线 程 即 可 以 独 立 操 作, 又 可 以 协 同 作 业。 降 低 了 服 务 器 的 复 杂 度。

Java 是 基 于 操 作 系 统 级 的 多 线 程 环 境 之 上 设 计 的，Java 的 运 行 器 依 靠 多 线 程 来 执 行 任 务， 并 且 所 有 类 库 在 设 计 时 都 考 虑 到 多 线 程 机 制。

二、Java 线 程 的 结 构

Java 支 持 一 种“ 抢 占 式”（preemptive) 调 度 方 式。

线 程 从 产 生 到 消 失， 可 分5 个 状 态：

Newborn

线 程 在 己 被 创 建 但 未 执 行 这 段 时 间 内, 处 于 一 个 特 殊 的"Newborn" 状 态, 这 时, 线 程 对 象 己 被 分 配 内 存 空 间, 其 私 有 数 据 己 被 初 始 化, 但 该 线 程 还 未 被 调 度。 此 时 线 程 对 象 可 通 过start（） 方 法 调 度, 或 者 利 用stop（） 方 法 杀 死. 新 创 建 的 线 程 一 旦 被 调 度, 就 将 切 换 到"Runnable" 状 态。

Runnable

Runnable 意 即 线 程 的 就 绪 状 态, 表 示 线 程 正 等 待 处 理 器 资 源, 随 时 可 被 调 用 执 行。 处 于 就 绪 状 态 的 线 程 事 实 上 己 被 调 度, 也 就 是 说, 它 们 己 经 被 放 到 某 一 队 列 等 待 执 行。 处 于 就 绪 状 态 的 线 程 何 时 可 真 正 执 行, 取 决 于 线 程 优 先 级 以 及 队 列 的 当 前 状 况。 线 程 的 优 先 级 如 果 相 同, 将 遵 循" 先 来 先 服 务" 的 调 度 原 则。

线 程 依 据 自 身 优 先 级 进 入 等 待 队 列 的 相 应 位 置。 某 些 系 统 线 程 具 有 最 高 优 先 级, 这 些 最 高 优 先 级 线 程 一 旦 进 入 就 绪 状 态, 将 抢 占 当 前 正 在 执 行 的 线 程 的 处 理 器 资 源, 当 前 线 程 只 能 重 新 在 等 待 队 列 寻 找 自 己 的 位 置. 这 些 具 有 最 高 优 先 级 的 线 程 执 行 完 自 己 的 任 务 之 后, 将 睡 眠 一 段 时 间, 等 待 被 某 一 事 件 唤 醒. 一 旦 被 唤, 这 些 线 程 就 又 开 始 抢 占 处 理 器 资 源。 这 些 最 高 优 先 级 线 程 通 常 用 来 执 行 一 些 关 键 性 任 务, 如 屏 幕 显 示。

低 优 先 级 线 程 需 等 待 更 长 的 时 间 才 能 有 机 会 运 行。 由 于 系 统 本 身 无 法 中 止 高 优 先 级 线 程 的 执 行, 因 此, 如 果 你 的 程 序 中 用 到 了 优 先 级 较 高 的 线 程 对 象, 那 么 最 好 不 时 让 这 些 线 程 放 弃 对 处 理 器 资 源 的 控 制 权, 以 使 其 他 线 程 能 够 有 机 运 行。

Running

"Running"（ 运 行） 状 态 表 明 线 程 正 在 运 行, 该 线 己 经 拥 有 了 对 处 理 器 的 控 制 权, 其 代 码 目 前 正 在 运 行。 这 个 线 程 将 一 直 运 行 直 到 运 行 完 毕, 除 非 运 行 过 程 的 控 制 权 被 一 优 先 级 更 高 的 线 程 强 占。

综 合 起 来, 线 程 在 如 下3 种 情 形 之 下 将 释 放 对 处 理 器 的 控 制 权:

1. 主 动 或 被 动 地 释 放 对 处 理 器 资 源 的 控 制 权。 这 时, 该 线 程 必 须 再 次 进 入 等 待 队 列, 等 待 其 他 优 先 级 高 或 相 等 线 程 执 行 完 毕。

2. 睡 眠 一 段 确 定 的 时 间, 不 进 入 等 待 队 列。 这 段 确 定 的 时 间 段 到 期 之 后, 重 新 开 始 运 行。

3. 等 待 某 一 事 件 唤 醒 自 己。

Blocked

一 个 线 程 如 果 处 于"Blocked"（ 堵 塞） 状 态, 那 么 暂 时 这 个 线 程 将 无 法 进 入 就 绪 队 列。 处 于 堵 塞 状 态 的 线 程 通 常 必 须 由 某 些 事 件 才 能 唤 醒。 至 于 是 何 种 事 件, 则 取 决 于 堵 塞 发 生 的 原 因: 处 于 睡 眠 中 的 线 程 必 须 被 堵 塞 一 段 固 定 的 时 间; 被 挂 起、 或 处 于 消 息 等 待 状 态 的 线 程 则 必 须 由 一 外 来 事 件 唤 醒。

Dead

Dead 表 示 线 程 巳 退 出 运 行 状 态, 并 且 不 再 进 入 就 绪 队 列. 其 中 原 因 可 能 是 线 程 巳 执 行 完 毕（ 正 常 结 束）, 也 可 能 是 该 线 程 被 另 一 线 程 所 强 行 中 断（kill）。

三、 创 建 和 使 用 线 程 的 基 本 方 法

1. 线 程 的 产 生

在Java 语 言 中, 可 采 用 两 种 方 式 产 生 线 程: 一 是 实 现 一 个Runnable 界 面, 二 是 扩 充 一 个Thread 类.java.lang 中 定 义 了 一 个 直 接 从 根 类Object 中 派 生 的Thread 类. 所 有 以 这 个 类 派 生 的 子 类 或 间 接 子 类， 均 为 线 程。 在 这 种 方 式 中, 需 要 作 为 一 个 线 程 执 行 的 类 只 能 继 承、 扩 充 单 一 的 父 类。 下 面 的 例 子 通 过 扩 充Thread 类, 用 该 线 程 自 己 的 实 现 来 覆 盖Thread.run(), 产 生 一 个 新 类Counter。run () 方 法 是Counter 类 线 程 所 作 的 全 部 操 作.

import java.lang.*;

public class Counter extends Thread

{

    public void run ()

{....}

}

实 现Runnable 界 面 是 最 常 用 的 产 生 线 程 的 方 法, 它 打 破 了 扩 充Thread 类 方 式 的 限 制。

Java 语 言 源 码 中,Runnable 界 面 只 包 含 了 一 个 抽 象 方 法, 其 定 义 如 下:

package java.lang.*;

public interface Runnable {

public abstract void run ();

}

所 有 实 现 了Runnable 界 面 的 类 的 对 象 都 可 以 以 线 程 方 式 执 行. 下 面 的 例 子 产 生 与 上 面 例 子 相 同 的 类. 可 以 看 到counter 类 中 使 用 了 一 个Thread 类 的 变 量.

import java.lang.*;

public class counter implements Runnable

{ Thread T;

public void run ()

{...}

}

2、 基 本 方 法

.public synchronized void start()

启 动 线 程 对 象, 调 用 其run() 方 法, 随 即 返 回。

.pubilc final void stop()

停 止 线 程 的 执 行。

.public final void resume()

唤 醒 被 挂 起 的 线 程。 只 在 调 用suspend() 之 后 有 效。

.public final void suspend()

挂 起 线 程 的 执 行。

.public static void yield()

暂 时 中 止 当 前 正 在 执 行 的 线 程 对 象 的 运 行。 若 存 在 其 他 线 程, 则 随 后 调 用 下 一 个 线 程。

.public static void sleep (long mills)throws InterruptedException

使 当 前 正 处 运 行 状 态 的 线 程 睡 眠mills 毫 秒。

.public final void wait() throws InterruptedException

使 线 程 进 入 等 待 状 态， 直 到 被 另 一 线 程 唤 醒

.public final void motify()

把 线 程 状 态 的 变 化 通 知 给 另 一 等 待 线 程。

四、 线 程 的 同 步

线 程 的 使 用， 主 要 在 于 一 个 进 程 中 多 个 线 程 的 协 同 工 作， 所 以 线 程 的 同 步 就 很 重 要。 线 程 的 同 步 用 于 线 程 共 享 数 据， 转 换 和 控 制 线 程 的 执 行， 保 证 内 存 的 一 致 性。

在 Java 中, 运 行 环 境 使 用 程 序( Monitor) 来 解 决 线 程 同 步 的 问 题。 管 程 是 一 种 并 发 同 步 机 制, 它 包 括 用 于 分 配 一 个 特 定 的 共 享 资 源 或 一 组 共 享 资 源 的 数 据 和 方 法.

Java 为 每 一 个 拥 有 synchronized 方 法 的 对 象 实 例 提 供 了 一 个 唯 一 的 管 程。 为 了 完 成 分 配 资 源 的 功 能, 线 程 必 须 调 用 管 程 入 口。 管 程 入 口 就 是synchronized 方 法 入 口。 当 调 用 同 步( synchronized) 方 法 时, 该 线 程 就 获 得 了 该 管 程。

管 程 边 界 上 实 行 严 格 的 互 斥, 在 同 一 时 刻, 只 允 许 一 个 线 程 进 入 管 程; 当 管 程 中 已 有 了 一 个 线 程 时, 其 它 希 望 进 入 管 程 的 线 程 必 须 等 待, 这 种 等 待 是 由 管 程 自 动 管 理 的。

如 果 调 用 管 程 入 口 的 线 程 发 现 资 源 已 被 分 配, 管 程 中 的 这 个 线 程 将 调 用 等 待 操 作wait()。 进 入wait() 后, 该 线 程 放 弃 占 用 管 程, 在 管 程 外 面 等 待, 以 便 其 它 线 程 进 入 管 程。

最 终, 占 用 资 源 的 线 程 将 调 用 一 个 管 程 的 入 口 把 资 源 归 还 给 系 统, 此 时, 该 线 程 需 调 用 一 个 通 知 操 作notify(), 通 知 系 统 允 许 其 中 一 个 等 待 的 线 程 获 得 管 程 并 得 到 资 源。 被 通 知 的 线 程 是 排 队 的, 从 而 避 免 无 限 拖 延。

在 Java.lang 中 提 供 了 用 来 编 写 管 程 的 两 个 方 法: notify() 和wait()。 此 外 还 有notifyAll(), 它 通 知 所 有 等 待 的 线 程, 使 它 们 竞 争 管 程, 结 果 是 其 中 一 个 获 得 管 程, 其 佘 返 回 等 待 状 态。

五、 线 程 的 控 制

线 程 的 控 制 分 为 停 止 线 程 和 启 动 线 程。

.public final void suspend()

挂 起 线 程 的 执 行。

.public final void resume()

唤 醒 被 挂 起 的 线 程。 使 一 个 暂 停 的 线 程 可 用 于 调 度。

因 为 线 程 的 调 度 为 抢 占 式 机 制， 也 可 使 用 线 程 的 优 先 级 来 对 线 程 进 行 控 制。

.public final void setPriority(int newPriority)

设 置 线 程 优 先 级。

.public final int getPriority()

获 取 并 返 回 线 程 的 优 先 级。

线 程 的 优 先 级 用 于 在 运 行 队 列 中 给 线 程 排 序，Java 提 供 的 抢 占 式 调 度， 使 得 高 级 别 的 线 程 先 运 行。

六、 线 程 的 应 用

在 实 际 应 用 中， 线 程 使 用 的 范 围 很 广， 可 用 于 控 制 实 时 数 据 处 理、 快 速 的 网 络 服 务, 还 有 更 快 的 图 象 绘 制 和 打 印， 以 及 数 据 库 中 的 数 据 的 取 回 和 处 理 等 等。 在Java 中 一 个 在 不 停 运 行 的 提 供 一 些 基 本 服 务 的 例 子 是 垃 圾 收 集 线 程， 垃 圾 收 集 线 程，。 该 线 程 由Java 虚 拟 机 提 供。 它 扫 描 程 序 中 不 再 被 访 问 的 变 量， 将 其 所 占 的 系 统 资 源 释 放 给 系 统。


Java 在ClientServer 网络中的应用 (转)

作者：asp2001

email: asp2001@163.net

日期：2000-8-12 9:40:07

Java 在Client/Server 网络中的应用

（作者：　2000年08月09日 10:19）

    随着Java语言的日益流行，特别是Java与Internet Web的密切结合，使它在全球取得了巨大的成功。Java语言以其独立于平台、面向对象、分布式、多线索及完善的安全机制等特色，成为现代信息系统建设中的良好的开发平台和运行环境。

一、Java网络应用模型

    和Internet上的许多环境一样，完整的Java应用环境实际上也是一个客户机/服务器环境，更确切地说是浏览器/服务器模型（即Browser/Server模型，简称Web模型）。但与传统的客户机/服务器(C/S) 的二层结构不同，应用Java的Web模型是由三层结构组成的。传统的C/S结构通过消息传递机制，由客户端发出请求给服务器，服务器进行相应处理后经传递机制送回客户端。而在Web模型中，服务器一端被分解成两部分：一部分是应用服务器(Web 服务器)，另一部分是数据库服务器。

    针对分布式计算环境，Java通过其网络类库提供了良好的支持。对数据分布，Java提供了一个URL(Uniform Resource Locator) 对象， 利用此对象可打开并访问网络上的对象，其访问方式与访问本地文件系统几乎完全相同。对操作分布，Java的客户机/ 服务器模式可以把运算从服务器分散到客户一端（服务器负责提供查询结果，客户机负责组织结果的显示），从而提高整个系统的执行效率，增加动态可扩充性。Java网络类库是Java 语言为适应Internet 环境而进行的扩展。另外，为适应Internet的不断发展，Java还提供了动态扩充协议，以不断扩充Java网络类库。

    Java的网络类库支持多种Internet协议，包括Telnet, FTP 和HTTP (WWW)，与此相对应的Java网络类库的子类库为：

Java.net

Java.net.ftp

Java.net.http://www.content

Java.net.http://www.html

Java.net.http://www.http

    这些子类库各自容纳了可用于处理Internet协议的类和方法。其中，java.net用于处理一些基本的网络功能，包括远程登录(Telnet)；java.net.ftp用于处理ftp协议；java.net.http://www.content页面内容；java.net.www.html 和java.net.www.http 则分别提供了对HTML 语言和HTTP 协议的支持。

二、客户机/服务器环境下的Java应用程序

客户机/服务器在分布处理过程中，使用基于连接的网络通信模式。该通信模式首先在客户机和服务器之间定义一套通信协议，并创建一Socket类，利用这个类建立一条可靠的链接；然后，客户机/服务器再在这条链接上可靠地传输数据。客户机发出请求，服务器监听来自客户机的请求，并为客户机提供响应服务。这就是典型的"请求-- 应答" 模式。下面是客户机/服务器的一个典型运作过程：

1、服务器监听相应端口的输入；

2、客户机发出一个请求；

3、服务器接收到此请求；

4、服务器处理这个请求，并把结果返回给客户机；

5、重复上述过程，直至完成一次会话过程。

    按照以上过程，我们使用Java语言编写一个分别针对服务器和客户机的应用程序（Application）。该程序在服务器上时，程序负责监听客户机请求，为每个客户机请求建立Socket 连接，从而为客户机提供服务。本程序提供的服务为：读取来自客户机的一行文本，反转该文本，并把它发回给客户机。

    通过该程序实例我们看到，使用Java语言设计C/S程序时需要注意以下几点：

（1）、 服务器应使用ServerSocket 类来处理客户机的连接请求。当客户机连接到服务器所监听的端口时，ServerSocket将分配一新的Socket 对象。这个新的Socket 对象将连接到一些新端口，负责处理与之相对应客户机的通信。然后，服务器继续监听ServerSocket，处理新的客户机连接。

    Socket 和ServerSocket 是Java网络类库提供的两个类。

（2）、服务器使用了多线程机制。Server对象本身就是一个线程，它的run()方法是一个无限循环，用以监听来自客户机的连接。每当有一个新的客户机连接时，ServerSocket就会创建一个新的Socket类实例，同时服务器也将创建一新线程，即一个Connection 对象，以处理基于Socket 的通信。与客户机的所有通信均由这个Connection 对象处理。Connection的构造函数将初始化基于Socket 对象的通信流，并启动线程的运行。与客户机 的通信以及服务的提供，均由Connection对象处理。

（3）、客户机首先创建一Socket对象，用以与服务器通信。之后需创建两个对象：DataInputStream 和PrintStream，前者用以从Socket 的InputStream 输入流中读取数据，后者则用于往Socket的OutputStream 中写数据。最后，客户机程序从标准输入（如：控制台）中读取数据，并把这些数据写到服务器，在从服务器读取应答消息，然后把这些应答消息写到准输出。

    以下分别为服务器和客户机端的源程序清单。本程序在NT 4.0 网络环境（TCP/IP）下使用JDK1.1 调试通过。

三、编写服务器类Java程序

// Server.java

import java.io.*;

import java.net.*;

public class Server extends Thread

{

        public final static int Default_Port=6543;

        protectd int port;

        protectd ServerSockt listen_socket;

// 定义出错例程：如果出现异常错误，退出程序。

Public static void fail(Exception e, String msg)

{

        System.err.println(msg + ": " + e);

        System.exit(1);

}

// 定义并启动服务器的Socket 例程，监听客户机的连接请求。

public Server(int port)

{

if(port == 0) port = Default_Port;

this.port = port;

try

{

listen_socket = new ServerSocket(port);

}

catch(IOException e) fail(e, "Exception creating server socket");

System.out.println("Server: listening on port" + port);

This.start();

}

/* 下面为服务器监听线程的主程序。该线程一直循环执行，监听并接受客户机发出的连接

请求。对每一个连接，均产生一个连接对象与之对应，通过Socket 通道进行通信。*/

public void run()

{

try

        {

while(true)

{

        Socket client_socket = listen_socket.accept();

        Connection c = new Connection(client_socket);

                        }

}

catch(IOException e) fail(e,"Exception while listening for connections")

}

// 启动服务器主程序

public static void main(String args[])

{

        int port = 0;

        if (args.length == 1)

        {

        try port = Integer.parseInt(args[0]);

        catch(NumberFormatException e) port = 0;

}

new Server(port);

}　　　　　　　　　　　　　　　　　　　　　　 // End of the main

}　　　　　　　　　　　　　　　　　　 // End of Server class

//以下定义了Connection 类，它是用来处理与客户机的所有通信的线程。

class Connection extends Thread

{

protected Socket client;

        protected DataInputStream in;

                protected PrintStream out;

// 初始化通信流并启动线程

public Connection(Socket client_socket)

{

                client = client_socket;

        try

                {

                        in = new DataInputStream(client.getinputStream());

                out = new PrintStream(client.getOutputStream());

}

catch(IOException e)

        {

        try client.close();

                catch(IOException e2);

        System.err.println("Exception while getting socket streram: " + e);

Return;

}

this.start;

}　　　　　　　　　　　　　　　　　　 // End of Connection method

// 服务例程：读出一行文本；反转文本；返回文本。

public void run()

{

                String line;

        StringBuffer revline;

int len;

        try

{

                for(;;)

{

                        // Read a line

                                line = in.readline();

                                        if(line == null) break;

                // Reverse the line

                                len = line.length();

                                revline = new StringBuffer(len);

                for(int i = len-1; i >=0; i--)

                revline.insert(len-1-I;line.charAt(i));

                     // Write out the reverse line

                out.println(revline);

                        }

catch(IOException e);

finally try client.close();

catch(IOException e2);

}

// End of run method

}

// End of Connection class

3、编写客户机类Java 程序

// Client.java

import java.io.*;

import java.net.*;

public class Client extends

{

        public static final int Default_Port = 6543;

// 定义出错例程

        public static final void usage()

        {

        System.out.println("Usage: Java Client []");

                System.exit(0);

}

        public static void main(String args[])

{

        int port = Default_Port;

Socket s = null;

// 解析端口参数

if ((args.length != 1)&&(args.length != 2 )) usage();

if (args.length == 1)

        port = Default_Port;

else

{

        try port = Integer.parseInt(args[1]);

        catch(NumberFormaatException e) usage();

}

try{

        // 产生一个Socket ，通过指定的端口与主机通信。

        s = new Socket(args[0], port);

// 产生用于发出和接收的文本字符流

DataInputStream sin = new DataInputStream(s.getInputStream());

PrintStream sout = new DataInputStream(s.getInputStream());

// 从控制台读入字符流

DataInputStream in = new DataInputStream(System.in);

        // 返回连接的地址和端口

System.out.println("Connected to"+s.getInetAddress()+":"+ s.getPort());

        String line;

        For(;;)

        {

                // 显示提示符

                System.out.print(" >");

                System.out.flush();

                // 读入控制台输入的一行字符

                line = in.readline();

                if (line == null) break;

                // 将接收的文本行送至服务器

                sout.println(line);

                // 从服务器接收一行字符

                line = sin.readline();

                // Check if connection is closed(i.e. for EOF)

                if(line == null)

                {

                        System.out.println("Connection closed by server.");

                        Break;

}

        // 在控制台上显示接收的字符

        System.out.println(line);

}

// End of for loop

}

/ End of try

catch(IOException e ) System.err.println(e);

// Always be sure to close the socket

finally

{

        try if(s != null) s.close();

        catch(IOException e2);

}

}　　　　　　　　　　　　　　　　　　　　　　 // End of main

}　　　　　　　　　　　　　　 // End of Client

运行该客户机程序时，必须以服务器主机名作为第一个参数，服务器端口号为第二个参数，

其中服务器端口号可缺省。


Java 中对文件的读写操作之比较

作者：jeru

日期：2000-11-29 11:55:18

Java 中对文件的读写操作之比较

作者：Jeru Liu

日期：November 29,2000

版本：1.0

Java 对文件进行读写操作的例子很多，让初学者感到十分困惑，我觉得有必要将各种方法进行

一次分析，归类，理清不同方法之间的异同点。

一．在 JDK 1.0 中，通常是用 InputStream & OutputStream 这两个基类来进行读写操作的。

InputStream 中的 FileInputStream 类似一个文件句柄，通过它来对文件进行操作，类似的，在

OutputStream 中我们有 FileOutputStream 这个对象。

用FileInputStream 来读取数据的常用方法是：

FileInputStream fstream = new FileInputStream(args[0]);

DataInputStream in = new DataInputStream(fstream);

用 in.readLine() 来得到数据，然后用 in.close() 关闭输入流。

完整代码见 Example 1。

用FileOutputStream 来写入数据的常用方法是：

FileOutputStream out out = new FileOutputStream("myfile.txt");

PrintStream p = new PrintStream( out );

用 p.println() 来写入数据，然后用 p.close() 关闭输入。

完整代码见 Example 2。

二．在 JDK 1.1中，支持两个新的对象 Reader & Writer, 它们只能用来对文本文件进行操作，而

JDK1.1中的 InputStream & OutputStream 可以对文本文件或二进制文件进行操作。

用FileReader 来读取文件的常用方法是：

FileReader fr = new FileReader("mydata.txt");

BufferedReader br = new BufferedReader(fr);

用 br.readLing() 来读出数据，然后用br.close() 关闭缓存，用fr.close() 关闭文件。

完整代码见 Example 3。

用 FileWriter 来写入文件的常用方法是：

FileWriter fw = new FileWriter("mydata.txt");

PrintWriter out = new PrintWriter(fw);

在用out.print 或 out.println 来往文件中写入数据，out.print 和 out.println的唯一区别是后者写

入数据或会自动开一新行。写完后要记得 用out.close() 关闭输出，用fw.close() 关闭文件。

完整代码见 Example 4。

-------------------------------------------------------------- following is the source code of examples------------------------------------------------------

Example 1:

// FileInputDemo

// Demonstrates FileInputStream and DataInputStream

import java.io.*;

class FileInputDemo {

  public static void main(String args[]) {

    // args.length is equivalent to argc in C

    if (args.length == 1) {

      try {

        // Open the file that is the first command line parameter

        FileInputStream fstream = new FileInputStream(args[0]);

        // Convert our input stream to a DataInputStream

        DataInputStream in = new DataInputStream(fstream);

        // Continue to read lines while there are still some left to read

        while (in.available() !=0) {

          // Print file line to screen

          System.out.println (in.readLine());

        }

        in.close();

      } catch (Exception e) {

        System.err.println("File input error");

      }

    }

    else

      System.out.println("Invalid parameters");

  }

}

Example 2:

// FileOutputDemo

// Demonstration of FileOutputStream and PrintStream classes

import java.io.*;

class FileOutputDemo

{

  public static void main(String args[])  {

  FileOutputStream out; // declare a file output object

    PrintStream p; // declare a print stream object

try {

  // connected to "myfile.txt"

      out = new FileOutputStream("myfile.txt");

      // Connect print stream to the output stream

      p = new PrintStream( out );

      p.println ("This is written to a file");

      p.close();

    } catch (Exception e) {

      System.err.println ("Error writing to file");

    }

  }

}

Example 3:

// FileReadTest.java

// User FileReader in JDK1.1 to read a file

import java.io.*;

class FileReadTest {

  public static void main (String[] args) {

    FileReadTest t = new FileReadTest();

    t.readMyFile();

}

  void readMyFile() {

    String record = null;

    int recCount = 0;

    try {

FileReader fr = new FileReader("mydata.txt");

       BufferedReader br = new BufferedReader(fr);

       record = new String();

       while ((record = br.readLine()) != null) {

         recCount++;

         System.out.println(recCount + ": " + record);

}

br.close();

fr.close();

     } catch (IOException e) {

         System.out.println("Uh oh, got an IOException error!");

         e.printStackTrace();

     }

}

}

Example 4:

// FileWriteTest.java

// User FileWriter in JDK1.1 to writer a file

import java.io.*;

class FileWriteTest {

  public static void main (String[] args) {

    FileWriteTest t = new FileWriteTest();

    t.WriteMyFile();

}

  void WriteMyFile() {

    try {

FileWriter fw = new FileWriter("mydata.txt");

PrintWriter out = new PrintWriter(fw);

out.print(“hi,this will be wirte into the file!”);

out.close();

fw.close();

     } catch (IOException e) {

         System.out.println("Uh oh, got an IOException error!");

         e.printStackTrace();

     }

}

}


Java2的安全新特性下的Applet数字签名具体实现方法

作者：阿费

email: afeilb@163.net

日期：6/12/2001 11:50:11 AM

Java2的安全新特性下的Applet数字签名具体实现方法

北京  阿费

自从Java技术开始应用以来，人们对Java平台的安全性以及由于部署Java技术所引发的安全问题给予了极大的关注。特别是在1998年11月Java2发布后，Java的安全体系结构发生了根本的改进，对于终端用户而言，它可以保护文件和私人数据不被恶意的程序或病毒感染和破坏，鉴别代码提供者的身份。对于开发者而言，通过使用API方法，能够将安全性功能集成到应用程序中，因为API的体系结构能够定义和集成对特定的资源的使用权限、加密、安全性管理、策略管理，并提供了一些类来管理公钥／密钥对及信任用户群的公钥证书。同时系统管理员、开发者和用户可以使用它提供的工具管理钥匙库，在JAR文件中生成数字签名、签名的完整性检测、创建和修改策略文件。按照Java设计者的观点，Java安全包括2个方面的内容，首先将Java作为一种安全的平台提供给用户，在此平台上，可安全地运行Java程序；其次提供用Java编程语言实现的安全工具和服务，它使得诸如企业界这样一些对安全非常敏感的领域也可应用Java技术。本文将就这二个方面介绍Java2的安全性新特性以及该新特性下的Applet数字签名的具体实现方法。

  Java2采用了如图1所示的新的安全体系结构，并基于这种安全体系结构提供了很多新特性。

图1 JDK1．2安全模式

1．1　密纹访问控制

    这种能力从一开始就在JDK中存在。但要使用它，应用程序的编写者不得不做大量的编程工作例如，创建SecurityManager和Classloader类的子类并使其用户化。HotJava1．0就是一个这样的应用程序，它允许浏览器用户在几个不同的安全等级上进行选择。然而，这种编程涉及非常敏感的安全问题，它要求程序员对计算机安全有精深的理解和纯熟的技巧。新的安全体系结构将使这些变得简单而安全。

1．2　易于配置的安全策略

    与上述情况相似，这种能力在原来的JDK中也是存在的，但是不便于使用，而且编写安全代码也不是简单明了的事情。于是，人们期望能够允许应用程序的编写者和用户能够不通过编程来设置安全策略。

1．3　便于扩展的访问控制结构

    一直到JDK1．1为止，为了创建1个新的访问许可，你必须在SecurityManager类中增加1个新的check方法。新的安全体系结构则允许设置各类访问许可（每个都表示对1个系统资源的访问），并能对所有正确访问许可（包括未定义的许可）进行自动处理。

1．4　安全检查扩展至所有Java程序

    那种所有本地代码是可信的内置概念将不复存在，取而代之的将是本地代码（例如非系统代码，安装在本地的应用程序包等）服从于与Applet相同的安全控制，但是可以声明对本地代码的政策是最宽容的，从而使这些代码可被认为是完全可信而有效地运行。上述原则也可应用于已签字的Applet和任何Java应用程序。

2　Java2安全体系的概念及运行机制

2．1　保护域

    Java2安全体系结构中的一个基本的概念是保护域（Protected Domain）。1个域可通过对象集来划分范围，这些对象当前可由1个主体直接访问。而主体是在计算机系统中被授予许可的实体。JDK1．0所利用的沙箱就是一个有着固定边界的保护域实例。保护域的概念是一种在保护单元间起着分组和隔离作用的便利机制。例如，我们可以将保护域分开以避免它们之间的直接交互作用，于是，任何允许的交互作用必须通过可信系统代码或被有关的域所明确允许。

    保护域通常分为明确的2个类别，系统域和应用程序域。所有被保护的外部资源如：文件系统、网络设施以及屏幕和键盘等仅能通过系统域来访问。图2中显示了1个Java应用环境的域的组成。从概念上讲，1个域包括1组类，这些类的实例被授予相同的一组许可。保护域是由现行策略所确定的。Java应用程序环境保持了来自代码（类和实例）到它们的保护域然后再到它们的许可的映射，如图3所示。1个线程的执行可能完全发生在1个单一的保护域中，也可能涉及1个应用程序域或是系统域。例如：1个打印消息的应用程序将不得不与系统域发生交互作用，因为系统域是唯一对输出流的访问点。在此种情况下的任何时候，应用程序域都不能通过调用系统域获得除打印消息外的任何额外许可，否则将是一个严重的安全性隐患。在相反的情形下，1个系统域从1个应用程序域中调用1个方法，如当1个AWT系统域调用1个Applet的绘画方法来显示这个Applet时，有效访问权限与应用程序域所允许的当前权限在任何时候都相同，这一点也是同样至关重要的。换句话说，一个具有较低权限的域不能通过调用一个更高权限的域，或被一个更高权限的域所调用来获得额外的许可。上述有关1个线程涉及2个保护域的讨论自然地归纳为1个遍历多重保护域的线程，计算许可的一个简单而谨慎的经验做法是：

图2 Java应用环境的域的组成

图3 类→保护域→权限的映射

    （1）一个执行线程的许可集可被认为是由该线程所遍历的所有保护域的许可的交集。

    （2）当1条代码调用doPrivileged方法时，执行线程的许可集被认为是包括所有代码的保护域以及由它直接或间接调用的保护域的权限。即通过doPrivileged方法可使1条可信代码能临时访问更多的资源，这在某些情况下是必要的。例如，1个应用程序可能不被允许直接访问包含字体的文件，但是，显示文本的系统实用程序必须代表用户获得那些字体。

    在执行期间，当请求访问1个关键系统资源（如文件I／O和网络I／O）或者API方法需要执行1个敏感的操作时，例如读1个文件，资源处理代码直接或间接地调用1个特殊的称为访问控制（AccessController）类的方法，访问控制类通过检查调用栈来作出决定是否准予该请求或操作发生。在调用栈中是执行该操作需要调用的一些类的成员方法，因为每个类都属于一些保护域，每个保护域都建立了一些策略，因此在调用栈的每个方法都被分配了1组权限。访问控制类由栈顶开始，自顶向下检查每个方法，看是否方法被所在的保护域所允许，如果发现一个方法权限没有得到允许，访问控制类就抛出安全性异常；反之，如果到达栈底仍未抛出异常，即说明调用栈中的所有方法均满足保护域的权限要求，访问控制允许操作发生。其中有一种特殊的情况，即当访问控制遍历调用栈时，将查找是否存在优先域（Privileged Domain），如果存在优先域，即使没有到达栈底，访问控制也将停止遍历调用栈并允许操作发生。虽然新的安全机制初看上去增加了许多调用API方法的消耗，但是Java2确实使用了一些技术去加速检查权限的过程，例如访问控制将过滤掉重复的域并在遇到第一个优先域时停止检查，这说明额外的操作将是一个本地的方法调用，SUN的基准测试显示了新的安全检查是相当快的。

    最后，每个域包括系统或应用程序域可以对其域边界内的内部资源进行附加保护。例如，一个银行系统的应用程序可能需要支持并保护其内部的一些概念，如查帐、存款和取款等。由于此种保护的语义不像那些可预测的语义可以被JDK预置，因而，在这个层次上的保护最好留给系统或应用程序开发员来做。

    目前，1个域单独地由1个代码来源（CodeSource）鉴别，它封装了在该域中运行的代码的2个特性：代码基址和公共密钥证书集，公共密钥对应于在该域中为所有代码签字的私有密钥。因而，由相同的密钥签字和来自相同URL的类被放在同一个域中。1个域还包含在该域中授予代码的许可，它是由现行安全策略所决定的。

2．2　证书、钥匙库及其相关工具

    在Java2的安全体系下，1个Applet开发和运行的过程如下：

    在代码的分发端：

    （1）开发Java源程序并对其进行编译。

    （2）用JAR工具对类文件和资源文件进行封装。

    （3）用keytool创建公钥和密钥，生成X。509V1签名证书，输出证书。

    （4）通过jarsigner工具用生成的密钥对JAR文件进行数字签名。

    在代码的接收端：

    （1）用keytool输入证书视其为可信任。

    （2）用policytool创建和修改安全性策略配置文件，授权请求的访问权限。

    （3）从网络取得字节码，用公钥验证数字签名证书和文档代码的完整性。

    （4）验证字节码的合法性，根据策略文件分配相应权限。

    （5）执行代码，完成后被垃圾回收器回收内存。

    在用公钥验证数字签名证书之前，接收方需要确认公钥自身的可靠性，因此通常情况是提供一个包含公钥的证书而不是公钥自身。1个证书包括：

    （1）1个公钥。

    （2）1个唯一的名字实体（个人或公司），它是证书的所有者，包含用户名字、公司、组织、城市、地址、国家代码、省份等信息。

    （3）数字签名：1个证书被1个分发者的实体签名，保证证书确实包含另1个实体（所有者）的公钥。

    （4）分发者的标识名信息。

    对于接收者可以用分发者的公钥来验证他的数字签名，检查证书的合法性。然而公钥可能包含在另一个证书中，而数字签名需要用另一个证书的分发者的公钥来验证，这样嵌套下去，直到一个公钥被接收者确认是可信任的。如果接收者不能建立信任链，例如：1个分发者的证书不合法，那么可以用keytool－import命令来计算指纹，每个指纹是一个相关的短数字，它唯一可靠地标识证书（指纹是一个用信息摘要算法计算的证书信息的哈希值），接收者可以呼叫证书的所有者，并比较发出的证书和接收证书的指纹，如果指纹相同，则证书不同。因此能够保证证书在传递的过程中未被修改。另一个潜在的问题是发送者身份的标识，有时一个证书是自签名的，即使用证书中的公钥相对应的密钥进行签名，如果接收者已经知道或信任发送者，那么就没有任何问题。否则发送者需要从一个可信任的第3方得到证书，这个第3方通常是一个证书的授权机构CA，那么首先发送一个自签名的证书签名请求CSR给CA，由CA验证CSR的签名及发送CSR的身份、许可证以及其它信息。然后CA通过一个用CA的密钥进行签名的证书，授权CSR的发送者作为公钥的所有者，任何人只要信任CA的公钥，都可以用之来验证证书的签名，很多情况下CA自身有一个来自更高一级的CA的证书，从而构成证书链。所有信任的证书实体都可以作为信任证书被引入钥匙库，每个证书中的公钥都可以用来验证用相应的密钥生成的签名。

    发送者在发送签名的代码和文档时还相应提供包含与签名的密钥相应的公钥证书。用keytool－export命令或API函数可以从钥匙库中输出证书到文件中，然后将这个文件发送给需要的接收者，由接收者用keytool－import命令或API函数将其引入钥匙库中。如果用jarsigner工具为JAR文件生成签名，他会从钥匙库中取出证书及证书链，并和签名一起放入JAR文件。

    密钥和相应的公钥证书存放在一个由口令保护的数据库中，称为钥匙库（keystore）。1个钥匙库包含2种类型的条目，可信任的证书条目，钥匙和证书条目，每个都包含1个密钥和与密钥相应的公钥证书，在钥匙库中的每个条目都有1个别名进行标识。1个钥匙库的所有者在钥匙库中可以有多个钥匙，可以通过不同的别名进行访问，每个别名通常是用钥匙库的所有者使用的钥匙的特定角色来命名，别名也可以标识钥匙的目的。例如：SignPersonalEmail可以被用来标识1个钥匙库的条目，它的密钥用于签名个人邮件，SignJarFiles用于标识1个条目，它的密钥用于签名JAR文件。

3　Applet的数字签名认证实现的具体方法、步骤

3．1 结合我自己开发的基于JAVA2的Applet

我的项目是使用APPLET制作一个实时消息队列监控程序，由于涉及到了本地资源，对APPLET一定要进行数字签名和认证。我使用的环境是WINDOWS2000，应用服务器是WEBLOGIC6.0，开发环境是JBUILDER4.0。之前我提醒大家一定要注意服务器端和客户端的概念。那些文件应该在服务器端，那些文件应该在客户端。

   首先在客户端使用JRE1.3.0_01（JAVA运行环境1.3.0.1版本）以取代IE的JVM(JAVA虚拟机)，可以到http://WWW.JAVA.SUN.COM

    在服务器端的调用APPLET的HTML文件中也需要将它包含进来，以便没有事先安装JRE的客户端下载，具体的写法，请接着往下看；

具体步骤如下:

服务器端：

     1．将程序需要用到的各种包文件全部解压(我这儿要用到WEBLOGIC的JMS包使用命令jar  xf  weblogicc.jar)，然后使用JDK的打包命令将编译好的监控程序.class和刚才解压的包一起打包到一个包中。(前提我已经将监控程序和解开的包都放在同一个目录下了)，都是dos状态下的命令，具体命令见jdk1.3(1.2)的bin目录下，

  命令如下：

jar cvf  monitor.jar *.class

此命令生成一个名为monitor.jar的包

2．为刚才创建的包文件（monitor.jar）创建keystore和keys。其中

keystore将用来存放密匙(private keys)和公共钥匙的认证，alias别名这儿取为monitor。

命令如下：

keytool -genkey -keystore monitor.keystore –alias monitor

   此命令生成了一个名为monitor.keystore的keystore文件，

接着这条命令，系统会问你好多问题，比如你的公司名称，你

的地址，你要设定的密码等等，都由自己的随便写。

3．使用刚才生成的钥匙来对jar文件进行签名

命令如下：

   jarsigner  -keystore  monitor.keystore  monitor.jar monitor

      这个命令将对monitor.jar文件进行签名，不会生成新文件。

4．将公共钥匙导入到一个cer文件中，这个cer文件就是要拷贝到客户端的唯一文件 。

命令如下：

keytool  -export  -keystore  monitor.keystore -alias monitor  -file  monitor.cer

         此条命令将生成monitor.cer认证文件，当然这几步都有可能问你刚

才设置的密码。

这样就完成了服务器端的设置。这时你就可以将jar文件和keystore文件以及cer文件(我这儿是monitor.jar,monitor.keystore,monitor.cer)拷贝到服务器的目录下了，我用的是weblogic6.0，所以就拷贝到C:\bea\wlserver6.0\config\mydomain\applications\DefaultWebApp_myserver下的自己建的一个目录下了。

客户端：

1．    首先应该安装jre1.3.0_01，然后将服务器端生成的monitor.cer

文件拷贝到jre的特定目录下，我这儿是：

c:\program files\javasoft\jre\1.3.0_01\lib\security目录下。

2．    将公共钥匙倒入到jre的cacerts（这是jre的默认keystore）

命令如下：

   keytool  -import  -alias monitor -file  monitor.cer

-keystore  cacerts

    注意这儿要你输入的是cacerts的密码，应该是changeit，而不

是你自己设定的keystore的密码。

3．    修改policy策略文件，在dos状态下使用命令  policytool

系统会自动弹出一个policytool的对话框，如图4所示，在这里面首先选择file菜单的open项，

打开c:\program files\javasoft\jre\1.3.0_01\lib\security目录下的java.poliy文件，然后在edit菜单中选择Change keystore ，在对话框中new keystore url:中输入

file:/c:/program files /javasoft/jre/1.3.0_01/lib/security/cacerts,

这儿要注意反斜杠，在new keystore type 中输入JKS，这是cacerts的固定格式，然后单击Add Policy Entry，在出现的对话框中CodeBase中输入：

      http://URL:7001/*

其中的URL是服务器的IP地址，7001是我的weblogic的端口，如果你是在别的应用服务器上比如说是apache，那端口号就可以省略掉。

在SignedBy中输入(别名alias):这儿是Monitor

然后单击add peimission按钮，在出现的对话框中permission中选择你想给这个applet的权限，这儿具体有许多权限，读者可以自己找资料看看。我这儿就选用allpeimission，右边的signedBy中输入别名：monitor

最后保存，在file菜单的save项。

  当然你可以看见我已经对多个包实现了签名认证。

图4-policytool的策略修改命令界面

这样客户端的设置就完成了。在客户端用ie运行该applet程序时，会询问你是不是对该签名授权，选择授权后，包会自动从服务器下载到本地计算机，而且ie会自动启动jre，在右下栏中可以看见，相当于ie的java控制台。

4．调用applet的html文件

大家都知道由于java2的安全性，对applet的正常调用的html文件已经不能再使用了，而改为ActiveX类型的调用。具体的又分ie和nescape的不同写法，这一些在sun网上都能找到现成的教程。我就不多说了，只是将我的这个小程序为ie写的的html给大家看看。

<html>

<META HTTP-EQUIV="Content-Type" CONTENT="text/html;CHARSET=gb2312">

<center>

<h3>消息中心实时监控平台</h3>

<hr>

<OBJECT classid="clsid:8AD9C840-044E-11D1-B3E9-00805F499D93"

    width="900" height="520" align="baseline" codebase="http://192.168.2.217:7001/j2re-1_3_0_01-win-i.exe#Version=1,3,0,0">

<PARAM NAME="java_code" VALUE="wise.monitor.applet.monitorApplet">

    <PARAM NAME="java_codebase" VALUE="monitor/classes">

    <PARAM NAME="java_type" VALUE="application/x-java-applet;version=1.3">

    <PARAM NAME="ARCHIVE" VALUE="monitor.jar" >

    <PARAM NAME="scriptable" VALUE="true">

</OBJECT>

</center>

</html>

其中我要强调一点，因为applet每一次的改动都需要重新打包签名，手续非常繁琐，所以在具体的实现中要将一些会变化参数放到html文件中来，传到applet中去，这一点网上文章好多，自己去看吧。

另外一个就是有朋友问我，那这样不是太麻烦了，每一个客户端都要进行复杂的dos命令操作，我只能说一目前我的水平只能将一个已经做好的客户端文件cer文件和java.policy以及cacerts文件直接拷贝到客户端，当然这也有缺陷，如果别人的计算机已经有了认证，就会丢失。就这些问题我们可以一起探讨。

另外还有一点优化，就是在打包的时候，我这儿只讲了把所有要用的涉及到安全性的包和源程序到要打到一个包中。这样如果包非常大的话，会非常影响下载的速度，如果可以使用本地计算机的包就好了，这一点jre也做到了，具体的要到控制面板的jre控制台上去设置。这个就留着读者自己去摸索吧。

结束语

   我发现网上java相关的资料非常少，中文的更少，所以希望自己能将一些小知识和大家共享，省掉许多重复的无用功。如果大家对这个问题还有不清楚的地方，或者就这问题相进一步展开讨论的，请和我联系,我的信箱是afeilb@163.net。希望我们能共同进步！

  这篇文章也采纳了一些别的文章的优点，在此要多谢

南京东南大学计算机科学与工程系的金胜昔、步俊杰、吉逸。

          Afei

                                          2001.6.11晚 北京


JavaBean 与 Enterprise JavaBean：有什么不同？（转）

作者：zergman

email: zergman@chinaasp.com

日期：2001-6-15 13:59:43

编者注：下文基于圆桌讨论会。参与者包括 IBM“组件代理”beta 支持小组的 Ken Burget，和 IBM Enterprise Java Bean 开发部门的 Liane Acker、Jim Knutson 和 David Morrill。[/BLOCKQUOTE]

您现在可能已在使用 JavaBean，但还不了解它。如果有支持 Java 的浏览器，那么，在桌面上使用 JavaBean 就没有限制。使用的 Web 页面可以将 bean 作为小应用程序的一部分。您很快就会和作为浏览器可视部分的 JavaBean 交互，然后，那些 JavaBean 将与服务器上的 EJB 接口。这种能力也可以扩展到因特网和内部网。

JavaBean 和 Server Bean（通常称为 Enterprise JavaBean (EJB)）有一些基本相同之处。它们都是用一组特性创建，以执行其特定任务的对象或组件。它们还有从当前所驻留服务器上的容器获得其它特性的能力。这使得 bean 的行为根据特定任务和所在环境的不同而有所不同。

这开辟了巨大商机。因为 JavaBean 是与平台无关的，所以对于将来的解决方案，供应商可以轻易向不同用户推出其客户机方的 JavaBean，而不必创建或维护不同的版本。这些 JavaBean 可以与执行商业功能（例如订购、信用卡处理、电子汇款、存货分配、运输等）的 EJB 配合使用。这里有巨大潜力，而这正是组件代理（WebSphere Application Server 企业版）设计提供的那种潜力。

JavaBean 是一种组件，它在内部有接口或有与其相关的属性，以便不同人在不同时间开发的 bean 可以询问和集成。可以构建一个 bean，而在以后构造时将其与其它 bean 绑定。这种过程提供了先构建，然后重复使用的方法，这就是组件的概念。可以将这种单一应用程序部署成独立程序、ActiveX 组件或在浏览器中。

JavaBean 因其外部接口（即属性接口）而与纯对象不同。这种接口允许工具读取组件要执行的功能，将其与其它 bean 挂钩，以及将其插入其它环境。JavaBean 设计成对单一进程而言是本地的，它们在运行时通常可视。这种可视组件可能是按钮、列表框、图形或图表 - 但这不是必需的。

可执行组件

Server Bean 或 EJB 是部署在服务器上的可执行组件或商业对象。有一个协议允许对其进行远程访问或在特定服务器上安装或部署它们。有一系列机制允许它们将服务安全性、事务行为、并发性（由多个客户机同时访问的能力）和持久性（其状态可以保存多久）的主要方面授权给 EJB 服务器上其所在的容器。当安装在容器中时，它们获得各自的行为，该行为提供不同质量的服务，因此，选择正确的 EJB 服务器至关重要。这正是 IBM WebSphere 企业版的优势所在。

EJB 是设计成运行在服务器上，并由客户机调用的非可视远程对象。可通过多个非可视 JavaBean 构建 EJB。它们有一个部署描述符，其目的与 JavaBean 属性相同：它是以后可由工具读取的 bean 的描述。EJB 还独立于平台，一旦编写好，还可以在任何支持 Java 的平台（包括客户机和服务器）上使用。

因为 EJB 由诸如 IBM VisualAge for Java 这样的工具集生成，所以，它是基于服务器的对象，并用于远程调用。它们安装在 EJB 服务器上，并象调用其它 CORBA 远程对象那样获得进行调用的远程接口。

ActiveX 对象

可以将 JavaBean 部署成 ActiveX 对象，虽然 EJB 的代理也可以这样做，但是，因为 ActiveX 运行在桌面上，所以，EJB 本身不能成为 ActiveX 对象。要在与平台相关的、仅 Windows 平台上做到这一点，开发人员可以将 JavaBean 变换成 ActiveX 组件。

好处

EJB 的主要好处在于：构建 bean 时，bean 开发人员可以规定需要什么类型的行为，而不必规定如何去做。开发分为两部分：程序员开发 bean，然后验证：它可与构建工具一起工作，并包括标识所需服务质量行为种类的部署描述符。下一步，另一个程序员可以采用这个 bean，并使用读取 EJB 部署描述符的部署工具，然后将该 bean 安装到 Enterprise Java Server 上的容器中。在第二步中，部署工具采取一些操作 - 这可能意味着生成如状态保存代码，放入事务挂钩，或执行安全性检查这样的代码。所有这些操作由部署工具生成，bean 开发人员和部署人员可以是不同的人。

可以通过使用部署工具，将任何独立于平台的 JavaBean 改写成具有可靠服务质量、特定于平台的 EJB，以满足现有商业系统和应用程序的特定需求。这就是 EJB 服务器对集成系统、网络和体系结构如此重要的原因所在。

EJB 与 IBM WebSphere 企业版

在 IBM WebSphere 企业版中使用时，可以将 EJB 配置成被管理的商业对象。接受它们授权服务的容器是其安装到的容器。将 EJB 的持久性部分映射在数据或状态对象中。EJB 服务器为 EJB 提供不同的服务质量，选择正确的 EJB 服务器可能对满足完整的商业需求至关重要。“组件代理”功能极其健壮，该功能提供如负载均衡和支持服务器组中多台机器的高级功能。它还有大大超出 Enterprise Java Server (EJS) 规范所倡导的系统管理功能。因此，按照基本标准编写的 JavaBean 或 EJB 可以运行在使用“组件代理”功能的 WebSphere 企业版上，并获得那些所有的附加功能。

EJB 服务器还提供独特的特性和服务质量，而且不完全相同。IBM“组件代理”有一些强大特性 - 例如，可伸缩性，它允许开发人员将 EJB 部署到从小型系统到大型网络的不同类型服务器。开发人员可以从小处入手，例如，在一个部门中，首先在 LAN 的 Java 服务器上部署，一旦准备好，就知道可以将在那里创建的 JavaBean 和 EJB 部署到全球网络。然后，开发人员可以测试并熟悉这些 bean，试运行，制作样本等等。满意之后，开发人员可以通过将其移至高性能服务器，来大幅度扩大其规模。JavaBean 和 EJB 不受任何计算机体系结构边界的限制。它们用 Java 编写，可以运行在任何具有 Java 虚拟机的系统上，并可以使用任何 Enterprise Java Server (EJS) 来部署对象。因此，开发人员现在可以在方便的系统上构建，以后在方便的系统上部署，而不必是同一台或同样类型的机器。

IBM WebSphere 企业版支持将商业对象部署到多台服务器。EJB 作为商业对象集成到“组件代理”功能，并作为任何其它商业对象处理。因此，EJB 可以连接到所选的后端系统，并执行任何所需操作，以满足其商业需求。这就成为“组件代理”为 EJB 提供的基础设施。通过将“组件代理”用作 EJB 服务器，开发人员将能够继续使用当前旧有系统，并将其与电子商务接口一起提供。

为使 EJB 能在 WebSphere“组件代理”环境中工作，可以使用“组件代理”部署工具将其安装在一台或多台服务器上，然后将其添加到命名服务器，以便可以全局查找到它。任何可以访问公共命名服务器的人都可以找到它，找到其宿主，并可以在宿主上执行方法，同时创建 EJB。这就是“代理组件”要做的事。

示例

让我们举一个在 Web 购物站点上可以看到的电子购物车的例子。用户的购物车是一个 JavaBean。用户将货架上的商品放入购物车，这些商品本身是 JavaBean。它们全部可视，并且面向用户。结帐时，将用户购物车中的商品发送到服务器上的 EJB，该 EJB 执行一些必要的操作，如检查信用卡授权和可用额度，生成封条，或生成给发货部门的有关提什么货和发货地点的特殊指示 - 这就是商业程序已在进行的活动。

结束语

Bean 的全部意义不只是其现有能力，更在于其可以为商业提供的有竞争力的潜在能力。IT 设计师和应用开发人员现在可以将精力完全集中在商业逻辑，而将如事务、持久性和安全性的底层工作留给服务器。WebSphere 的“组件代理”功能将提供所有这些（还有后端访问）和对象事务管理器。

关于作者

Mike Day 是 IBM 对象中间件市场营销小组的成员。他主持有关读者普遍感兴趣的主题的圆桌会议。他们根据相关背景、经验或在讨论主题时的表现来挑选圆桌会议佳宾。如果想推荐要讨论的话题，请给我们来信，来信地址 compbrok@us.ibm.com


JavaBean持久化

作者：jspfuns

日期：2000-8-10 17:36:11

当一个JavaBean在构造工具内被用户化，并与其它Bean建立连接之后，它的所有状态都应当可被保存，下一次被load进构造工具内或在运行时，就应当是上一次修改完的信息。为了能做到这一点，要把Bean的某些字段的信息保存下来，在定义Bean时要使它实现java.io.Serializable接口。例如：

public class Button implements java.io.Serializable {

}

实现了序列化接口的Bean中字段的信息将被自动保存。若不想保存某些字段的信息则可在这些字段前冠以transient或static关键字，transient和static变量的信息是不可被保存的。通常，一个Bean所有公开出来的属性都应当是被保存的，也可有选择地保存内部状态。 Bean开发者在修改软件时，可以添加字段，移走对其它类的引用，改变一个字段的private/protected/public状态，这些都不影响类的存储结构关系。然而，当从类中删除一个字段，改变一个变量在类体系中的位置，把某个字段改成transient/static，或原来是transient/static，现改为别的特性时，都将引起存储关系的变化。

5 JavaBean的存储格式

JavaBean组件被设计出来后，一般是以扩展名为jar的Zip格式文件存储，在jar中包含与JavaBean有关的信息，并以MANIFEST文件指定其中的哪些类是JavaBean。以jar文件存储的JavaBean在网络中传送时极大地减少了数据的传输数量，并把JavaBean运行时所需要的一些资源捆绑在一起 本章主要论述了JavaBeans的一些内部特性及其常规设计方法，参考的是JavaBeans规范1.0A版本。随着世界各大ISV对JavaBeans越来越多的支持，规范在一些细节上还在不断演化，但基本框架不会再有大的变动。


JavaBean的事件

作者：jspfuns

日期：2000-8-10 17:34:14

事件处理是JavaBean体系结构的核心之一。通过事件处理机制，可让一些组件作为事件源，发出可被描述环境或其它组件接收的事件。这样，不同的组件就可在构造工具内组合在一起，组件之间通过事件的传递进行通信，构成一个应用。从概念上讲，事件是一种在"源对象"和"监听者对象"之间，某种状态发生变化的传递机制。事件有许多不同的用途，例如在Windows系统中常要处理的鼠标事件、窗口边界改变事件、键盘事件等。在Java和JavaBean中则是定义了一个一般的、可扩充的事件机制，这种机制能够：

对事件类型和传递的模型的定义和扩充提供一个公共框架，并适合于广泛的应用。

与Java语言和环境有较高的集成度。

事件能被描述环境捕获和点火。

能使其它构造工具采取某种技术在设计时直接控制事件，以及事件源和事件监听者之间的联系。

事件机制本身不依赖于复杂的开发工具。

特别地，还应当：

能够发现指定的对象类可以生成的事件。

能够发现指定的对象类可以观察（监听）到的事件。

提供一个常规的注册机制，允许动态操纵事件源与事件监听者之间的关系。

不需要其它的虚拟机和语言即可实现。

事件源与监听者之间可进行高效的事件传递。

能完成JavaBean事件模型与相关的其它组件体系结构事件模型的中立映射。

3.2.1 概述

JavaBean事件模型的总体结构图见图3.3,

主要构成有： 事件从事件源到监听者的传递是通过对目标监听者对象的Java方法调用进行的。 对每个明确的事件的发生，都相应地定义一个明确的Java方法。这些方法都集中定义在事件监听者（EventListener）接口中，这个接口要继承java.util.EventListener。 实现了事件监听者接口中一些或全部方法的类就是事件监听者。 伴随着事件的发生，相应的状态通常都封装在事件状态对象中，该对象必须继承自java.util.EventObject。事件状态对象作为单参传递给应响应该事件的监听者方法中。 发出某种特定事件的事件源的标识是：遵从规定的设计格式为事件监听者定义注册方法，并接受对指定事件监听者接口实例的引用。 有时，事件监听者不能直接实现事件监听者接口，或者还有其它的额外动作时，就要在一个源与其它一个或多个监听者之间插入一个事件适配器类的实例，来建立它们之间的联系。

3.2.2 事件状态对象（Event State Object）

与事件发生有关的状态信息一般都封装在一个事件状态对象中，这种对象是java.util.EventObject的子类。按设计习惯，这种事件状态对象类的名应以Event结尾。例如：

public class MouseMovedExampleEvent extends java.util.EventObject

{ protected int x, y；

/*　创建一个鼠标移动事件MouseMovedExampleEvent */

  MouseMovedExampleEvent(java.awt.Component source, Point location) {

super(source);

x = location.x;

y = location.y;

}

/* 获取鼠标位置*/

public Point getLocation() {

return new Point(x, y);

}}

3.2.3事件监听者接口（EventListener Interface）与事件监听者

由于Java事件模型是基于方法调用，因而需要一个定义并组织事件操纵方法的方式。JavaBean中，事件操纵方法都被定义在继承了java.util.EventListener类的EventListener接口中，按规定，EventListener接口的命名要以Listener结尾。任何一个类如果想操纵在EventListener接口中定义的方法都必须以实现这个接口方式进行。这个类也就是事件监听者。例如：

/*先定义了一个鼠标移动事件对象*/

    public class MouseMovedExampleEvent extends java.util.EventObject {

     // 在此类中包含了与鼠标移动事件有关的状态信息

         ...

    }

    /*定义了鼠标移动事件的监听者接口*/

    interface MouseMovedExampleListener extends java.util.EventListener {

/*在这个接口中定义了鼠标移动事件监听者所应支持的方法*/

void mouseMoved(MouseMovedExampleEvent mme);

}

在接口中只定义方法名，方法的参数和返回值类型。如：上面接口中的mouseMoved方法的具体实现是在下面的ArbitraryObject类中定义的。

class ArbitraryObject implements MouseMovedExampleListener {

      public void mouseMoved(MouseMovedExampleEvent mme)

   { ... }

｝

ArbitraryObject就是MouseMovedExampleEvent事件的监听者。

3.2.4 事件监听者的注册与注销

为了各种可能的事件监听者把自己注册入合适的事件源中，建立源与事件监听者间的事件流，事件源必须为事件监听者提供注册和注销的方法。在前面的bound属性介绍中已看到了这种使用过程，在实际中，事件监听者的注册和注销要使用标准的设计格式：

public void add< ListenerType>(< ListenerType> listener)；

public void remove< ListenerType>(< ListenerType> listener)；

例如：

    首先定义了一个事件监听者接口：

public interface ModelChangedListener extends java.util.EventListener {

void modelChanged(EventObject e);

}

接着定义事件源类：

public abstract class Model {

private Vector listeners = new Vector(); // 定义了一个储存事件监听者的数组

/*上面设计格式中的< ListenerType>在此处即是下面的ModelChangedListener*/

public synchronized void addModelChangedListener(ModelChangedListener mcl)

    { listeners.addElement(mcl); }//把监听者注册入listeners数组中

public synchronized void removeModelChangedListener(ModelChangedListener mcl)

        { listeners.removeElement(mcl); //把监听者从listeners中注销

        ｝

    /*以上两个方法的前面均冠以synchronized，是因为运行在多线程环境时，可能同时有几个对象同时要进行注册和注销操作，使用synchronized来确保它们之间的同步。开发工具或程序员使用这两个方法建立源与监听者之间的事件流*/

    protected void notifyModelChanged() {/**事件源使用本方法通知监听者发生了modelChanged事件*/

       Vector l;

        EventObject e = new EventObject(this);

        /* 首先要把监听者拷贝到l数组中，冻结EventListeners的状态以传递事件。这样来确保在事件传递到所有监听者之前，已接收了事件的目标监听者的对应方法暂不生效。*/

        synchronized(this) {

            l = (Vector)listeners.clone();

        }

        for (int i = 0; i < l.size(); i++) {

         /* 依次通知注册在监听者队列中的每个监听者发生了modelChanged事件，

         并把事件状态对象e作为参数传递给监听者队列中的每个监听者*/

((ModelChangedListener)l.elementAt(i)).modelChanged(e);

        }

       }

      ｝

在程序中可见事件源Model类显式地调用了接口中的modelChanged方法，实际是把事件状态对象e作为参数，传递给了监听者类中的modelChanged方法。

3.2.5适配类

适配类是Java事件模型中极其重要的一部分。在一些应用场合，事件从源到监听者之间的传递要通过适配类来"转发"。例如：当事件源发出一个事件，而有几个事件监听者对象都可接收该事件，但只有指定对象做出反应时，就要在事件源与事件监听者之间插入一个事件适配器类，由适配器类来指定事件应该是由哪些监听者来响应。

图3.4是适配类模型的框架： 从上图中可见，适配类成为了事件监听者，事件源实际是把适配类作为监听者注册入监听者队列中，而真正的事件响应者并未在监听者队列中，事件响应者应做的动作由适配类决定。目前绝大多数的开发工具在生成代码时，事件处理都是通过适配类来进行的。


JavaBean的属性

作者：jspfuns

日期：2000-8-10 17:32:36

JavaBean的属性与一般Java程序中所指的属性，或者说与所有面向对象的程序设计语言中对象的属性是一个概念，在程序中的具体体现就是类中的变量。在JavaBean设计中，按照属性的不同作用又细分为四类：Simple, Index, Bound与Constrained属性。

3.1.1 Simple属性

一个简单属性表示一个伴随有一对get/set方法（C语言的过程或函数在Java程序中称为"方法"）的变量。属性名与和该属性相关的get/set方法名对应。例如：如果有setX和getX方法，则暗指有一个名为"X"的属性。如果有一个方法名为isX，则通常暗指"X"是一个布尔属性（即X的值为true或false）。例如在下面这个程序中：

public class alden1 extends Canvas {

string ourString= "Hello"; //属性名为ourString，类型为字符串

public alden1(){ 　　　　//alden1()是alden1的构造函数，与C++中构造函数的意义相同

setBackground(Color.red);

setForeground(Color.blue);

}

/* "set"属性*/

public void setString(String newString) {

ourString=newString;

}

/* "get"属性 */

public String getString() {

return ourString;

}

}

3.1.2 Indexed属性

一个Indexed属性表示一个数组值。使用与该属性对应的set/get方法可取得数组中的数值。该属性也可一次设置或取得整个数组的值。例：

public class alden2 extends Canvas {

int[] dataSet={1,2,3,4,5,6}; // dataSet是一个indexed属性

public alden2() {

setBackground(Color.red);

setForeground(Color.blue);

}

/* 设置整个数组　*/

public void setDataSet(int[] x){

dataSet=x;

}

/* 设置数组中的单个元素值 */

public void setDataSet(int index, int x){

dataSet[index]=x;

}

/* 取得整个数组值 */

public int[] getDataSet(){

return dataSet;

}

/* 取得数组中的指定元素值 */

public int getDataSet(int x){

return dataSet[x];

}

}

3.1.3 Bound属性

一个Bound属性是指当该种属性的值发生变化时，要通知其它的对象。每次属性值改变时，这种属性就点火一个PropertyChange事件(在Java程序中，事件也是一个对象)。事件中封装了属性名、属性的原值、属性变化后的新值。这种事件是传递到其它的Bean，至于接收事件的Bean应做什么动作由其自己定义。

图3.1是一个简单Bound属性示意图，当PushButton的background属性 与Dialog的background属性bind时，若PushButton的background属性发生变化时，Dialog的background属性也发生同样的变化。 例：

public class alden3 extends Canvas{

String ourString= "Hello"; //ourString是一个bound属性

private PropertyChangeSupport changes = new PropertyChangeSupport(this);

/** 注：Java是纯面向对象的语言，如果要使用某种方法则必须指明是要使用哪个对象的方法，在下面的程序中要进行点火事件的操作，这种操作所使用的方法是在PropertyChangeSupport类中的。所以上面声明并实例化了一个changes对象，在下面将使用changes的firePropertyChange方法来点火ourString的属性改变事件。*/

public void setString(string newString){

String oldString = ourString;

ourString = newString;

/* ourString的属性值已发生变化，于是接着点火属性改变事件 */

changes.firePropertyChange("ourString",oldString,newString);

}

public String getString(){

return ourString;

}

/** 以下代码是为开发工具所使用的。我们不能预知alden3将与哪些其它的Bean组合成为一个应用，无法预知若alden3的ourString属性发生变化时有哪些其它的组件与此变化有关，因而alden3这个Bean要预留出一些接口给开发工具，开发工具使用这些接口，把其它的JavaBean对象与alden3挂接。*/

public void addPropertyChangeListener(PropertyChangeLisener l){

changes.addPropertyChangeListener(l);

}

public void removePropertyChangeListener(PropertyChangeListener l){

changes.removePropertyChangeListener(l);

}

通过上面的代码，开发工具调用changes的addPropertyChangeListener方法把其它JavaBean注册入ourString属性的监听者队列l中，l是一个Vector数组，可存储任何Java对象。开发工具也可使用changes的removePropertyChangeListener方法，从l中注销指定的对象，使alden3的ourString属性的改变不再与这个对象有关。当然，当程序员手写代码编制程序时，也可直接调用这两个方法，把其它Java对象与alden3挂接。

3.1.4 Constrained属性

一个JavaBean的constrained属性，是指当这个属性的值要发生变化时，与这个属性已建立了某种连接的其它Java对象可否决属性值的改变。constrained属性的监听者通过抛出PropertyVetoException来阻止该属性值 的改变。过程如图3.2

例：下面程序中的constrained属性是PriceInCents。

public class JellyBean extends Canvas{

private PropertyChangeSupport changes=new PropertyChangeSupport(this);

private VetoableChangeSupport Vetos=new VetoableChangeSupport(this);

/*与前述changes相同，可使用VetoableChangeSupport对象的实例Vetos中的方法，在特定条件下来阻止PriceInCents值的改变。*/

......

public void setPriceInCents(int newPriceInCents) throws PropertyVetoException {

/*　方法名中throws PropertyVetoException的作用是当有其它Java对象否决PriceInCents的改变时，要抛出例外。*/ /* 先保存原来的属性值*/

int oldPriceInCents=ourPriceInCents;

/**点火属性改变否决事件*/

vetos.fireVetoableChange("priceInCents",new Integer(OldPriceInCents), new Integer(newPriceInCents));

/**若有其它对象否决priceInCents的改变，则程序抛出例外，不再继续执行下面的两条语句，方法结束。若无其它对象否决priceInCents的改变，则在下面的代码中把ourPriceIncents赋予新值，并点火属性改变事件*/

ourPriceInCents=newPriceInCents;

changes.firePropertyChange("priceInCents", new Integer(oldPriceInCents),new Integer(newPriceInCents));

}

/**与前述changes相同，也要为PriceInCents属性预留接口，使其它对象可注册入PriceInCents否决改变监听者队列中，或把该对象从中注销

public void addVetoableChangeListener(VetoableChangeListener l)

{ vetos.addVetoableChangeListener(l);

}

public void removeVetoableChangeListener(VetoableChangeListener l){

vetos.removeVetoableChangeListener(l);

}

......

}

从上面的例子中可看到，一个constrained属性有两种监听者：属性变化监听者和否决属性改变的监听者。否决属性改变的监听者在自己的对象代码中有相应的控制语句，在监听到有constrained属性要发生变化时，在控制语句中判断是否应否决这个属性值的改变。

总之，某个Bean的constrained属性值可否改变取决于其它的Bean或者是Java对象是否允许这种改变。允许与否的条件由其它的Bean或Java对象在自己的类中进行定义。


JavaBean调用示例(转)

作者：asp2001

email: asp2001@163.net

日期：2000-8-11 19:22:03

JavaBean

    JavaBean 是一种基于 Java 的软件组件。 JSP 对于在 Web 应用中集成 JavaBean 组件提供了完善的支持。这种支持不仅能缩短开发时间（可以直接利用经测试和可信任的已有组件，避免了重复开发），也为 JSP 应用带来了更多的可伸缩性。 JavaBean 组件可以用来执行复杂的计算任务，或负责与数据库的交互以及数据提取等。如果我们有三个 JavaBean ，它们分别具有显示新闻、股票价格、天气情况的功能，则创建包含所有这三种功能的 Web 页面只需要实例化这三个 Bean ，使用 HTML 表格将它们依次定位就可以了。

   为说明在 JSP 环境下 JavaBean 的应用，我们创建了一个名为 TaxRate 的 Bean 。它有两个属性，即 Product （产品）和 Rate （税率）。两个 set 方法分别用来设置这两个属性，两个 get 方法则用于提取这两个属性。在实际应用中，这种 Bean 一般应当从数据库提取税率值，此处我们简化了这个过程，允许任意设定税率。下面是这个 Bean 的代码清单：

   package tax;

   public class TaxRate {

   String Product;

   double Rate;

   public TaxRate() {

   this.Product = "A001";

   this.Rate = 5;

   }

   public void setProduct (String ProductName) {

   this.Product = ProductName;

   }

   public String getProduct() {

   return (this.Product);

   }

   public void setRate (double rateValue) {

   this.Rate = rateValue;

   }

   public double getRate () {

   return (this.Rate);

   }

   }

     在 JSP 页面中应用上述 Bean 要用到 < jsp:useBean> 标记。依赖于具体使用的 JSP 引擎的不同，在何处配置以及如何配置 Bean 的方法也可能略有不同。本文将这个 Bean 的 .class 文件放在 c:\jswdk-1.0\examples\WEB-INF\jsp\beans\tax 目录下，这里的 tax 是一个专门存放该 Bean 的目录。下面是一个应用上述 Bean 的示例页面：

   < HTML>

   < BODY>

   < %@ page language="java" %>

   < jsp:useBean id="taxbean" scope="application" class="tax.TaxRate" />

   < % taxbean.setProduct("A002");

   taxbean.setRate(17);

   %>

  使用方法 1 ： < p>

  产品 : < %= taxbean.getProduct() %> < br>

  税率 : < %= taxbean.getRate() %>

   < p>

   < % taxbean.setProduct("A003");

   taxbean.setRate(3);

   %>

   < b> 使用方法 2 ： < /b> < p>

  产品 : < jsp:getProperty name="taxbean" property="Product" />

   < br>

  税率 : < jsp:getProperty name="taxbean" property="Rate" />

   < /BODY>

   < /HTML>

     在 < jsp:useBean> 标记内定义了几个属性，其中 id 是整个 JSP 页面内该 Bean 的标识， scope 属性定义了该 Bean 的生存时间， class 属性说明了该 Bean 的类文件（从包名开始）。

     这个 JSP 页面不仅使用了 Bean 的 set 和 get 方法设置和提取属性值，还用到了提取 Bean 属性值的第二种方法，即使用 < jsp:getProperty> 标记。 < jsp:getProperty> 中的 name 属性即为 < jsp:useBean> 中定义的 Bean 的 id ，它的 property 属性指定的是目标属性的名字。

一旦你申明了JavaBean，你就可以存取它的特性来定制它。要取得一特性的值，如上所述，可使用<jsp:getProperty>标记。有了<jsp:getProperty>标记，你可以指定要使用的bean名称(从useBean的id)，以及你要取得值的特性。接着，真正的值就会放在输出里：

<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 %> />.

有关JavaBean的最后一点:要让网络服务器可以找到JavaBean，你要将它们的class类别放在特别位置。对JSWDK而言，最简单的地方是在安装目录里的类别目录，例如 \jswdk-1.0.1\classes。


JavaBean用户化

作者：jspfuns

日期：2000-8-10 17:35:12

JavaBean开发者可以给一个Bean添加用户化器（Customizer）、属性编辑器（PropertyEditor）和BeanInfo接口来描述一个Bean的内容，Bean的使用者可在构造环境中通过与Bean附带在一起的这些信息来用户化Bean的外观和应做的动作。一个Bean不必都有BeanCustomizer、PrpertyEditor和BeanInfo，根据实际情况，这些是可选的，当有些Bean较复杂时，就要提供这些信息，以Wizard的方式使Bean的使用者能够用户化一个Bean。有些简单的Bean可能这些信息都没有，则构造工具可使用自带的透视装置，透视出Bean的内容，并把信息显示到标准的属性表或事件表中供使用者用户化Bean，前几节提到的Bean的属性、方法和事件名要以一定的格式命名，主要的作用就是供开发工具对Bean进行透视。当然也是给程序员在手写程序中使用Bean提供方便，使他能观其名、知其意。

3.3.1用户化器接口（Customizer Interface）

当一个Bean有了自己的用户化器时，在构造工具内就可展现出自己的属性表。在定义用户化器时必须要实现java.beans.Customizer接口。例如，下面是一个"按钮"Bean的用户化一器：

public class OurButtonCustomizer extends Panel implements Customizer {

... ...

/*当实现象OurButtonCustomizer这样的常规属性表时，一定要在其中实现addProperChangeListener

和removePropertyChangeListener,这样，构造工具可用这些功能代码为属性事件添加监听者。*/

... ...

private PropertyChangeSupport changes=new PropertyChangeSupport(this);

public void addPropertyChangeListener(PropertyChangeListener l) {

changes.addPropertyChangeListener(l);

public void removePropertyChangeListener(PropertyChangeListener l) {

changes.removePropertyChangeListener(l);

}

... ...

3.3.2 属性编辑器接口（PropertyEditor Interface）

一个JavaBean可提供PropertyEditor类，为指定的属性创建一个编辑器。这个类必须继承自java.beans.PropertyEditorSupport类。构造工具与手写代码的程序员不直接使用这个类，而是在下一小节的BeanInfo中实例化并调用这个类。例：

public class MoleculeNameEditor extends java.beans.PropertyEditorSupport {

public String[] getTags() {

String resule[]={

"HyaluronicAcid","Benzene","buckmisterfullerine",

"cyclohexane","ethane","water"};

return resule;}

}

上例中是为Tags属性创建了属性编辑器，在构造工具内，可从下拉表格中选择MoleculeName的属性应是"HyaluronicAid"或是"water"。

3.3.3BeanInfo接口

每个Bean类也可能有与之相关的BeanInfo类，在其中描述了这个Bean在构造工具内出现时的外观。BeanInfo中可定义属性、方法、事件，显示它们的名称，提供简单的帮助说明。 例如：

public class MoleculeBeanInfo extends SimpleBeanInfo {

public PropertyDescriptor[] getPropertyDescriptors() {

try {

PropertyDescriptor pd=new PropertyDescriptor("moleculeName",Molecule.class);

/*通过pd引用了上一节的MoleculeNameEditor类,取得并返回moleculeName属性*/

pd.setPropertyEditorClass(MoleculeNameEditor.class);

PropertyDescriptor result[]={pd};

return result;

} catch(Exception ex) {

System.err.println("MoleculeBeanInfo: unexpected exeption: "+ex);

return null;

}

}

}


Javajsp 好的入门文章共赏

作者：丑丑牛

日期：00-4-28 18:02:12

用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、在 http://java.sun.com/jdk/2 SDK、Standard Edition、v 1.2.2)。

2、在http://java.sun.com/products/jsp/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:8080JSWDK 的欢迎页就说明JSP实验环境已经建成，可进入下一步实验。 要关闭Web服务器则运行stopserver.bat。

三、 JSP简单示例 JSWDK中包含的Web服务器的文档目录在缺省状态下为\jswdk-1.0.1\webpages，主文档在缺省状态下为index.html和index.jsp。也就是说访问http://localhost:8080用文本编辑器，如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文件中用<%以及%>括起来的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 这样网站的界面就能统一起来，而设计者可以集中精力在功能模块上处理用户登录、连接数据库、发送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

<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

注意，如果修改和重新编译了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）、answers（备注型）。这个表中可以存放一些编程知识的常见问题及答案，如图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:odbc: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

限于篇幅，本文不能列举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/

从一个平台移植到另外一个平台，JSP和JavaBean甚至不用重新编译，因为Java字节码都是标准的与平台无关的。笔者将在NT下实验的JSP网页原封不动地拿到Linux下就运行起来了，感觉非常满意。

九、 结论

综上所述，JSP可谓是构建动态网站的一个利器，故推荐给读者，祝愿你们掌握JSP，开发出优秀的网站。ASP程序员也请试一下，JSP里也有session、request、response/out等对象，亲自试试看如何。

附文

这里是关于JSP的重要参考资料，有兴趣的读者可以进一步阅读。

http://java.sun.com/products/jsp/faq.html

http://www.esperanto.org.nz/jsp/jspfaq.html

http://www.zdnet.com/pcweek/stories/news/0,4153,410709,00.html

http://developer.netscape.com/viewsource/kuslich_jsp

/kuslich_jsp.html

http://web2.java.sun.com/products/jsp/jsp-asp.html

http://www.asptoday.com/articles/19991022.htm


javamail中使用发信身份验证

作者：梦

email: mfgqd@263.net

日期：8/17/2001 5:31:06 PM

[b][#000080]问题：[/#]

[/b]朝三暮四郎 于 2001-8-17 17:23:50 加贴在 JAVA技术 ←返回版面

请教javamail发信的身份验证问题！！！

javamail中的demo都是不要身份验证的，

我看了几遍http://java.sun.com/products/javamail/1.2/docs/javadocs/index.html

还是不知道怎么改！！！

[b][#000080]回答：[/#]

[/b]这样使用即可：

props.put("mail.smtp.auth", "true");

Transport transport = session.getTransport("smtp");

transport.connect(host, username, password);


JavaScript中如何判断一个字符串是否为合法日期

作者：何志强

email: hhzqq@21cn.com

日期：2000-8-9 16:28:42

/*

作者：何志强[hhzqq@21cn.com]

日期：2000-08-09

版本：1.0

功能：判断一个字符串是否为合法日期

*/

//日期格式：YYYY-MM-DD

function isdate(strDate){

   var strSeparator = "-"; //日期分隔符

   var strDateArray;

   var intYear;

   var intMonth;

   var intDay;

   var boolLeapYear;

   strDateArray = strDate.split(strSeparator);

   if(strDateArray.length!=3) return false;

   intYear = parseInt(strDateArray[0],10);

   intMonth = parseInt(strDateArray[1],10);

   intDay = parseInt(strDateArray[2],10);

   if(isNaN(intYear)||isNaN(intMonth)||isNaN(intDay)) return false;

   if(intMonth>12||intMonth<1) return false;

   if((intMonth==1||intMonth==3||intMonth==5||intMonth==7||intMonth==8||intMonth==10||intMonth==12)&&(intDay>31||intDay<1)) return false;

   if((intMonth==4||intMonth==6||intMonth==9||intMonth==11)&&(intDay>30||intDay<1)) return false;

   if(intMonth==2){

      if(intDay<1) return false;

      boolLeapYear = false;

      if((intYear%100)==0){

         if((intYear%400)==0) boolLeapYear = true;

      }

      else{

         if((intYear%4)==0) boolLeapYear = true;

      }

      if(boolLeapYear){

         if(intDay>29) return false;

      }

      else{

         if(intDay>28) return false;

      }

   }

   return true;

}


JavaServer Page(JSP)简介1

作者：blackwhites

日期：2000-10-23 17:17:41

JavaServer Page(JSP)是一种基于Java的技术，用于生成动态网页，支持跨平台、跨Web服务器。

JSP可以与Microsoft的Active Server Page相比，但它使用类似HTML的标记以及Java代码片段来代替VBScript。如果你使用本身不支持ASP的Web服务器（即Apache或Netscape服务器），那么可以考虑JSP。你也可以为这些服务器购买ASP插入模块，但是很贵。而且，现在Sun还不对使用JSP收取许可费（尽管将来有可能会收的）。Solaris、Linux以及Windows的这些组件也非常容易获得。

不要把JSP和服务器端的JavaScript弄混。Web服务器可以自动地把由JSP生成的Java代码段转换成Java Servlet。JSP还可以自动控制以前用Perl脚本或服务器专用API（如ASP）手工编写的许多功能。

现在我们将带你入门，并帮你创建一你需要一个Java 2 Software Development Kit（JSDK），它以前被称为Java Development Kit（JDK），你还需要JavaServer Web Development Kit (JSWDK)、Tomcat或其他支持JSP的Web服务器。Sun免费提供Windows、Solaris和Linux等操作系统的JSDK和JSWDK。

如果你想在你现有的Web服务器使用JSP，而这个Web服务器本身不支持JSP和Java servlet，试一试Allaire的Jrun，它可以作为Netscape的Enterprise和FastTrack Server、Microsoft的Internet Information Server（IIS）和Personal Web Server（PWS）、Apache以及其他一些服务器的插件。你还可以使用Apache Web服务器的Java版本，它包含在最新的JSWDK中。

下载并安装软件

目前，JSDK的1.2.2-001版本是一个可下载安装的归档文件。这个大约20MB的下载软件提供了一个完整的Java开发环境，它可以让你创建任何使用标准核心API的基于Java的解决方案。不过，你的Web服务器使用JSP时唯一要做的就是访问Java的编译器。为了告诉Web服务器编译器的位置，需要将环境变量JAVA_HOME设置为指向JSDK的安装目录。如果你已在Windows上安装并选择了缺省目录，则应在autoexec.bat文件中加入“set JAVA_HOME=C:\1.2.2”一行，然后重新启动计算机。

在安装了JSDK后，下载并安装JSWDK或基于Java的Apache Web服务器——beta版的Tomcat。你把它放在哪里都可以，只要你能再找到它。一般来说，它被放在最高一级的目录中，这样，你就可以替换JSWDK或JSDK的版本而不需要移动另一个了。在安装了这个文件后，你就可以进行JSP开发了。

正确安装了JSWDK后，运行startserver命令启动Web服务器。缺省情况下，它在8080端口侦听。要检查安装是否正确，你可以在启动服务器之后加载一个JSP示例文件（http://localhost:8080/examples/jsp/）。如果你可以成功地运行示例文件，这说明你的安装是正确的。但是如果在用来启动服务器的控制台窗口中显示了错误信息，那么你就需要解决这个问题。最常出现的问题是没有设置（或没有正确设置）JAVA_HOME环境变量。要查看当前的环境变量设置，可以在DOS命令提示符下输入“set”命令。

开始使用

在解释JSP语法之前，先创建一个显示当前日期和时间的Web页面，并保存为sample.jsp。

<html>

<head>

<title>First Page</title>

</head>

<body>

<H3>Today is:

<%= new java.util.Date() %>

</H3>

</body>

</html>

把这个文件和你所有的HTML和JSP页面放进JSWDK安装目录下的Web页面目录中。你可以在http://localhost:8080/sample.jspservlet，这样你就可以看到系统当前的日期和时间。现在，在下载、安装和配置了开发环境之后，你可以去学习JSP语法并创建自己的基于JSP的解决方案了。


JavaServer Pages (JSP) 1.0简单介绍 ---III

作者：whitefox

email: whitefox.jiang@corp.elong.com

日期：2000-7-4 19:45:50

8.2 The jsp:useBean Action

This action lets you load in a JavaBean to be used in the JSP page. This is a a very useful capability because it lets you exploit the reusability of Java classes without sacrificing the convenience that JSP adds over servlets alone. The simplest syntax for specifying that a bean should be used is:

<jsp:useBean id="name" class="package.class" />

This usually means "instantiate an object of the class specified by class, and bind it to a variable with the name specified by id." However, as we'll see shortly, you can specify a scope attribute that makes the bean associated with more than just the current page. In that case, it is useful to obtain references to existing beans, and the jsp:useBean action specifies that a new object is instantiated only if there is no existing one with the same id and scope. Now, once you have a bean, you can modify its properties via jsp:setProperty, or by using a scriptlet and calling a method explicitly on the object with the variable name specified earlier via the id attribute. Recall that with beans, when you say "this bean has a property of typeX called foo", you really mean "this class has a method called getFoo that returns something of type X, and another method called setFoo that takes an X as an argument." The jsp:setProperty action is discussed in more detail in the next section, but for now note that you can either supply an explicit value, give a param attribute to say that the value is derived from the named request parameter, or just list the property to indicate that the value should be derived from the request parameter with the same name as the property. You read existing properties in a JSP expression or scriptlet by calling the appropriate getXxx method, or more commonly, by using the jsp:getProperty action.

Note that the class specified for the bean must be in the server's regular class path, not the part reserved for classes that get automatically reloaded when they change. For example, in the Java Web Server, it and all the classes it uses should go in the classes directory or be in a jar file in the lib directory, not be in the servlets directory.

Here is a very simple example that loads a bean and sets/gets a simple String parameter.

BeanTest.jsp

You can also download the source or try it on-line.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">

<HTML>

<HEAD>

<TITLE>Reusing JavaBeans in JSP</TITLE>

<LINK REL=STYLESHEET

      HREF="My-Style-Sheet.css"

      TYPE="text/css">

</HEAD>

<BODY>

<CENTER>

<TABLE BORDER=5>

  <TR><TH CLASS="TITLE">

      Reusing JavaBeans in JSP</TABLE>

</CENTER>

<P>

<jsp:useBean id="test" class="hall.SimpleBean" />

<jsp:setProperty name="test"

                 property="message"

                 value="Hello WWW" />

<H1>Message: <I>

<jsp:getProperty name="test" property="message" />

</I></H1>

</BODY>

</HTML>

SimpleBean.java

Here's the source code for the bean used in the BeanTest JSP page. You can also download the source.

package hall;

public class SimpleBean {

  private String message = "No message specified";

  public String getMessage() {

    return(message);

  }

  public void setMessage(String message) {

    this.message = message;

  }

}

Here's a typical result:

8.3 More jsp:useBean Details

The simplest way to use a bean is to use

   <jsp:useBean id="name" class="package.class" />

to load the bean, then use jsp:setProperty and jsp:getProperty to modify and retrieve bean properties. However, there are two other options. First, you can use the container format, namely

  <jsp:useBean ...>

    Body

  </jsp:useBean>

to indicate that the Body portion should be executed only when the bean is first instantiated, not when an existing bean is found and used. As discussed below, beans can be shared, so not all jsp:useBean statements result in a new bean being instantiated. Second, in addition to id and class, there are three other attributes that you can use: scope, type, and beanName. These attributes are summarized in the following table.

Atribute  Usage

id  Gives a name to the variable that will reference the bean. A previous bean object is used instead of instantiating a new one if one can be found with the same id and scope.

class  Designates the full package name of the bean.

scope  Indicates the context in which the bean should be made available. There are four possible values: page, request, session, and application. The default, page, indicates that the bean is only available on the current page (stored in the PageContext of the current page). A value of request indicates that the bean is only available for the current client request (stored in the ServletRequest object). A value of session indicates that the object is available to all pages during the life of the current HttpSession. Finally, a value of application indicates that it is available to all pages that share the same ServletContext. The reason that the scope matters is that a jsp:useBean entry will only result in a new object being instantiated if there is no previous object with the same id and scope. Otherwise the previously existing object is used, and any jsp:setParameter elements or other entries between the jsp:useBean start and end tags will be ignored.

type  Specifies the type of the variable that will refer to the object. This must match the classname or be a superclass or an interface that the class implements. Remember that the name of the variable is designated via the id attribute.

beanName  Gives the name of the bean, as you would supply it to the instantiate method of Beans. It is permissible to supply a type and a beanName, and omit the class attribute.

8.4 The jsp:setProperty Action

You use jsp:setProperty to give values to properties of beans that have been referenced earlier. You can do this in two contexts. First, you can use jsp:setProperty after, but outside of, a jsp:useBean element, as below:

<jsp:useBean id="myName" ... />

...

<jsp:setProperty name="myName"

                 property="someProperty" ... />

In this case, the jsp:setProperty is executed regardless of whether a new bean was instantiated or an existing bean was found. A second context in which jsp:setProperty can appear is inside the body of a jsp:useBean element, as below:

<jsp:useBean id="myName" ... >

  ...

  <jsp:setProperty name="myName"

                   property="someProperty" ... />

</jsp:useBean>

Here, the jsp:setProperty is executed only if a new object was instantiated, not if an existing one was found.

There are four possible attributes of jsp:setProperty:

Attribute  Usage

name  This required attribute designates the bean whose property will be set. The jsp:useBean element must appear before the jsp:setProperty element.

property  This required attribute indicates the property you want to set. However, there is one special case: a value of "*" means that all request parameters whose names match bean property names will be passed to the appropriate setter methods.

value  This optional attribute specifies the value for the property. String values are automatically converted to numbers, boolean, Boolean, byte, Byte, char, and Character via the standard valueOf method in the target or wrapper class. For example, a value of "true" for a boolean or Boolean property will be converted via Boolean.valueOf, and a value of "42" for an int or Integer property will be converted via Integer.valueOf. You can't use both value and param, but it is permissible to use neither. See the discussion of param below.

param  This optional attribute designates the request parameter from which the property should be derived. If the current request has no such parameter, nothing is done: the system does not pass null to the setter method of the property. Thus, you can let the bean itself supply default values, overriding them only when the request parameters say to do so. For example, the following snippet says "set the numberOfItems property to whatever the value of the numItems request parameter is, if there is such a request parameter. Otherwise don't do anything."

<jsp:setProperty name="orderBean"

                 property="numberOfItems"

                 param="numItems" />

If you omit both value and param, it is the same as if you supplied a param name that matches the property name. You can take this idea of automatically using the request property whose name matches the property one step further by supplying a property name of "*" and omitting both value and param. In this case, the server iterates through available properties and request parameters, matching up ones with identical names.

Here's an example that uses a bean to create a table of prime numbers. If there is a parameter named numDigits in the request data, it is passed into the bean's numDigits property. Likewise for numPrimes.

JspPrimes.jsp

To download the JSP source, right click on the source code link. You can also download the source code for the NumberedPrimes bean referenced by the jsp:useBean element. Browse the source code directory for other Java classes used by NumberedPrimes. The best way to try it out on-line is to start with the HTML page that acts as a front end to it.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">

<HTML>

<HEAD>

<TITLE>Reusing JavaBeans in JSP</TITLE>

<LINK REL=STYLESHEET

      HREF="My-Style-Sheet.css"

      TYPE="text/css">

</HEAD>

<BODY>

<CENTER>

<TABLE BORDER=5>

  <TR><TH CLASS="TITLE">

      Reusing JavaBeans in JSP</TABLE>

</CENTER>

<P>

<jsp:useBean id="primeTable" class="hall.NumberedPrimes" />

<jsp:setProperty name="primeTable" property="numDigits" />

<jsp:setProperty name="primeTable" property="numPrimes" />

Some <jsp:getProperty name="primeTable" property="numDigits" />

digit primes:

<jsp:getProperty name="primeTable" property="numberedList" />

</BODY>

</HTML>

Here's a typical result:

8.5 The jsp:getProperty Action

This element retrieves the value of a bean property, converts it to a string, and inserts it into the output. The two required attributes are name, the name of a bean previously referenced via jsp:useBean, and property, the property whose value should be inserted. Here's an example; for more examples, see Sections 8.2 and 8.4.

<jsp:useBean id="itemBean" ... />

...

<UL>

  <LI>Number of items:

      <jsp:getProperty name="itemBean" property="numItems" />

  <LI>Cost of each:

      <jsp:getProperty name="itemBean" property="unitCost" />

</UL>

8.6 The jsp:forward Action

This action lets you forward the request to another page. It has a single attribute, page, which should consist of a relative URL. This could be a static value, or could be computed at request time, as in the two examples below.

<jsp:forward page="/utils/errorReporter.jsp" />

<jsp:forward page="<%= someJavaExpression %>" />

8.7 The jsp:plugin Action

This action lets you insert the browser-specific OBJECT or EMBED element needed to specify that the browser run an applet using the Java plugin.

9. Comments and Character Quoting Conventions

There are a small number of special constructs you can use in various cases to insert comments or characters that would otherwise be treated specially. Here's a summary:

Syntax  Purpose

<%-- comment --%>

 A JSP comment. Ignored by JSP-to-scriptlet translator. Any embedded JSP scripting elements, directives, or actions are ignored.

<!-- comment -->

 An HTML comment. Passed through to resultant HTML. Any embedded JSP scripting elements, directives, or actions are executed normally.

<\%

 Used in template text (static HTML) where you really want "<%".

%\>

 Used in scripting elements where you really want "%>".

\'

 A single quote in an attribute that uses single quotes. Remember, however, that you can use either single or double quotes, and the other type of quote will then be a regular character.

\"

 A double quote in an attribute that uses double quotes. Remember, however, that you can use either single or double quotes, and the other type of quote will then be a regular character.

%\>

 %> in an attribute.

<\%

 <% in an attribute.


JavaServer PagesTM 白皮书

作者：whitefox

email: whitefox.jiang@corp.elong.com

日期：00-7-4 下午 07:33:54

JavaServer PagesTM 白皮书

JavaServer PagesTM (JSP)技术为创建显示动态生成内容的Web页面提供了一个简捷而快速的方法。JSP技术的设计目的是使得构造基于Web的应用程序更加容易和快捷，而这些应用程序能够与各种Web服务器，应用服务器，浏览器和开发工具共同工作。

本白皮书提供了JSP技术的全面概述，描述了其开发背景以及这项技术的总体目标。同时，在一个简单示例中，还描述了一个基于JavaTM技术的页面的关键组成部分。

开发基于Web的应用程序：背景

在万维网短暂的历史中，它已经从一个大部分显示静态信息的网络演化到对股票进行交易和进行购书操作的一个基础设施。在各种各样的应用程序中，对于可能使用的基于Web的客户端，看上去没有任何限制。

基于浏览器客户端的应用程序比传统的基于客户机/服务器的应用程序有几个好处。这些好处包括几乎没有限制的客户端访问和极其简化的应用程序部署和管理（要更新一个应用程序，管理人员只需要更改一个基于服务器的程序，而不是成千上万的安装在客户端的应用程序）。这样，软件工业正迅速地向建造基于浏览器客户端的多层次应用程序迈进。

这些快速增长的精巧的基于Web的应用程序要求开发技术上的改进。静态HTML对于显示相对静态的内容是不错的选择；新的挑战在于创建交互的基于Web的应用程序，在这些程序中，页面的内容是基于用户的请求或者系统的状态，而不是预先定义的文字。

对于这个问题的一个早期解决方案是使用CGI-BIN接口；开发人员编写与接口相关的单独的程序，以及基于Web的应用程序，后者通过Web服务器来调用前者。这个方案有着严重的扩展性问题——每个新的CGI要求在服务器上新增一个进程。如果多个用户并发地访问该程序，这些进程将消耗该Web服务器所有的可用资源，并且系统性能降低到极其低下的地步。

某些Web服务器供应商已经尝试通过为他们的服务器提供“插件”和API来简化Web应用程序的开发。这些解决方案是与特定的Web服务器相关的，不能解决跨多个供应商的解决方案的问题。例如，微软的Active Server PagesTM(ASP) 技术使得在Web页面上创建动态内容更加容易，但是也只能工作在微软的IIS和Personal Web Server上。

还存在其他的解决方案，但是都不能使一个普通的页面设计者能够轻易地掌握。例如，象Java Servlets这样的技术就可以使得用Java语言编写交互的应用程序的服务器端的代码变得容易。一个Java Servlets就是一个基于Java技术的运行在服务器端的程序（与Applet不同，后者运行在浏览器端）。开发人员能够编写出这样的Servlet，以接收来自Web浏览器的HTTP请求，动态地生成响应（可能要查询数据库来完成这项请求），然后发送包含HTML或XML文档的响应到浏览器。

采用这种方法，整个网页必须都在Java Servlet中制作。如果开发人员或者Web管理人员想要调整页面显示，就不得不编辑并重新编译该Java Servlet，即使在逻辑上已经能够运行了。采用这种方法，生成带有动态内容的页面仍然需要应用程序的开发技巧。

很显然，目前所需要的是一个业界范围内的创建动态内容页面的解决方案。这个方案将解决当前方案所受到的限制，即：

能够在任何Web或应用程序服务器上运行

将应用程序逻辑和页面显示分离

能够快速地开发和测试

简化开发基于Web的交互式应用程序的过程

JavaServer Pages (JSP)技术就是被设计用来满足这样的要求的。JSP规范是Web服务器、应用服务器、交易系统、以及开发工具供应商间广泛合作的结果。太阳微系统公司(Sun Microsystems Inc.)开发出这个规范来整合和平衡已经存在的对Java编程环境（例如，Java Servlet和JavaBeansTM）进行支持的技术和工具。其结果是产生了一种新的、开发基于Web应用程序的方法，给予使用基于组件应用逻辑的页面设计者以强大的功能。

Web应用开发的JavaServer Pages技术方法

在开发JSP规范的过程中，太阳微系统公司(Sun Microsystems Inc.)与许许多多主要的Web服务器、应用服务器和开发工具供应商，以及各种各样富有经验的开发团体进行合作。其结果是找到了一种为应用和页面开发人员平衡了可移植性和易用性的开发方法。

JSP技术在多个方面加速了动态Web页面的开发：

将内容的生成和显示进行分离

使用JSP技术，Web页面开发人员可以使用HTML或者XML标识来设计和格式化最终页面。使用JSP标识或者小脚本来生成页面上的动态内容（内容是根据请求来变化的，例如请求帐户信息或者特定的一瓶酒的价格）。生成内容的逻辑被封装在标识和JavaBeans组件中，并且捆绑在小脚本中，所有的脚本在服务器端运行。如果核心逻辑被封装在标识和Beans中，那么其他人，如Web管理人员和页面设计者，能够编辑和使用JSP页面，而不影响内容的生成。

在服务器端，JSP引擎解释JSP标识和小脚本，生成所请求的内容（例如，通过访问JavaBeans组件，使用JDBCTM技术访问数据库，或者包含文件），并且将结果以HTML（或者XML）页面的形式发送回浏览器。这有助于作者保护自己的代码，而又保证任何基于HTML的Web浏览器的完全可用性。

强调可重用的组件

绝大多数JSP页面依赖于可重用的，跨平台的组件（JavaBeans或者Enterprise JavaBeansTM组件）来执行应用程序所要求的更为复杂的处理。开发人员能够共享和交换执行普通操作的组件，或者使得这些组件为更多的使用者或者客户团体所使用。基于组件的方法加速了总体开发过程，并且使得各种组织在他们现有的技能和优化结果的开发努力中得到平衡。

采用标识简化页面开发

Web页面开发人员不会都是熟悉脚本语言的编程人员。JavaServer Page技术封装了许多功能，这些功能是在易用的、与JSP相关的XML标识中进行动态内容生成所需要的。标准的JSP标识能够访问和实例化JavaBeans组件，设置或者检索组件属性，下载Applet，以及执行用其他方法更难于编码和耗时的功能。

通过开发定制化标识库，JSP技术是可以扩展的。今后，第三方开发人员和其他人员可以为常用功能创建自己的标识库。这使得Web页面开发人员能够使用熟悉的工具和如同标识一样的执行特定功能的构件来工作。

JSP技术很容易整合到多种应用体系结构中，以利用现存的工具和技巧，并且扩展到能够支持企业级的分布式应用。作为采用Java技术家族的一部分，以及Java 2（企业版体系结构）的一个组成部分，JSP技术能够支持高度复杂的基于Web的应用。

由于JSP页面的内置脚本语言是基于Java编程语言的，而且所有的JSP页面都被编译成为Java Servlet，JSP页面就具有Java技术的所有好处，包括健壮的存储管理和安全性。

作为Java平台的一部分，JSP拥有Java编程语言“一次编写，各处运行”的特点。随着越来越多的供应商将JSP支持添加到他们的产品中，您可以使用自己所选择的服务器和工具，更改工具或服务器并不影响当前的应用。

当与Java 2平台，企业版(J2EE)和Enterprise JavaBean技术整合时，JSP页面将提供企业级的扩展性和性能，这对于在虚拟企业中部署基于Web的应用是必需的。

JSP页面看上去象什么？

JSP页面看上去象标准的HTML和XML页面，并附带有JSP引擎能够处理和抽取的额外元件。通常，JSP元件创建插入最终页面的文本。

使用示例是描述JSP技术的最好方法。下面的JSP页面非常简单；它打印带年、当月的天，并且根据时间使用"Good Morning"和"Good Afternoon"对您表示欢迎。

该页面结合了普通的HTML和大量JSP元件组成。

对时钟JavaBeans组件的调用

对一个外部文件的包含（用于著作权信息）

JSP表达式和脚本

<HTML>

<%@ page language=="java" imports=="com.wombat.JSP.*" %>

<H1>Welcome</H1>

<P>Today is </P>

<jsp:useBean id=="clock" class=="calendar.jspCalendar" />

<UL>

<LI>Day: <%==clock.getDayOfMonth() %>

<LI>Year: <%==clock.getYear() %>

</UL>

<% if (Calendar.getInstance().get(Calendar.AM_PM) ==== Calendar.AM) { %>

Good Morning

<% } else { %>

Good Afternoon

<% } %>

<%@ include file=="copyright.html" %>

</HTML>

这个页面包含下面这些组件：

一个JSP指示将信息传送到JSP引擎。在这个示例中，第一行指出从该页面即将访问的一些Java编程语言的扩展的位置。指示被设置在<%@和%>标记中。

固定模板数据：所有JSP引擎不能识别的标识将随结果页面发送。通常，这些标识是HTML或者XML标识。在上面的例子中包括无序列表(UL)和H1标识。

JSP动作或者标识：这些通常作为标准或定制标识被实现，并且具有XML标识的语法。在这个例子中，jsp:useBean标识实例化服务器端的Clock JavaBean。

一个表达式：JSP引擎计算在<%==和%>标记间的所有东西。在上面的列表项中，时钟组件(Clock)的Day和Year属性值作为字符串返回，并且作为输出插入到JSP文件中。在上面的例子中，第一个列表项是日子，第二个是年份。

小脚本是执行不为标识所支持的功能或者将所有的东西捆绑在一起的小的脚本。JSP 1.0软件的内置脚本语言是基于Java语言的。在上面示例中的小脚本确定现在是上午还是下午，并且据此来欢迎用户。

这个例子可能小了一点，但是技术上却不是。从业务上可以将关键的处理封装在服务器端的组件中，并且Web开发人员能够使用熟悉的语法和工具很容易地访问这些信息。基于Java的小脚本提供了一种灵活的方式以执行其他功能，而不要求扩展的脚本语言。页面作为整体是可读和可理解的，这就使得查找或者预防问题以及

共享工作更加容易。

这些组件中的一部分在下面有更详细的描述。

JSP指示

JSP页面使用JSP指示将指令传递到JSP引擎。这其中包括：

JSP页面指示传递页面相关的信息，例如缓冲区和线程信息或者出错处理。

语言指示指定脚本语言，以及所有的扩展。

包含指示（在上面例子中有显示）可以被用来在页面中包含一个外部的文档。一个好的例子是著作权文档或者公司信息文档——在一个集中地点保存该文档并且在页面中包含比在各个JSP页面中更新要容易些。当然，被包含的文件也可能是另一个JSP文件。

标识库指示指出页面可以调用的一个客户标识库。

JSP标识

绝大多数JSP处理将通过与JSP相关的基于XML的标识完成。JSP 1.0中包含大量标准标识，这些标识作为核心标识包括：

jsp:useBean 这个标识声明对一个JavaBeans组件实例的使用。如果该组件的实例不存在，JavaBeans组件将实例化和注册这个标识。

jsp:setProperty 这个标识在组件的实例中设置一个属性的值。

jsp:getProperty 这个标识获取一个组件的实例的属性值，将其转化为字符串，并且将它放入隐含对象"out"中。

jsp:include

jsp:forward

1.1版本将包含更多的标准标识。

标识的好处在于它们易于在应用程序间使用和共享。基于标识的语法的真正威力来自于客户标识库的开发，使得工具供应商或其他人员能够为特定的要求创建和分派标识。

脚本元件

JSP页面可以在页面中包含小的脚本，称之为小脚本(scriptlets)。小脚本是一个代码片段，在请求的处理过程中被执行。小脚本可以和页面中的静态元件组合（正如上面的例子一样）起来创建动态生成的页面。

脚本在<%和%>标志中被描述。在这对标志中的所有东西都会被脚本描述语言引擎执行，在我们的例子中是主机上的Java虚拟机。

JSP规范支持所有常用的脚本元件，包括表达式和声明。

JSP页面的应用模型

JSP页面由JSP引擎执行，引擎安装在Web服务器或者使用JSP的应用服务器上。JSP引擎接受客户端对JSP页面的请求，并且生成JSP页面给客户端的响应。

JSP页面通常被编译成为Java Servlet。后者是一个标准的Java扩展，在http://www.java.sun.com

当JSP页面第一次被调用时，如果它还不存在，就会被编译成为一个Java Servlet类，并且存储在服务器的内存中。这使得在接下来的对该页面的调用有非常快的响应。（这避免了CGI-BIN为每个HTTP请求生成一个新的进程的问题，或是服务器端引用所引起的运行时语法分析。）

JSP页面可以包含在多种不同的应用体系结构或者模型中。JSP页面可以用于由不同协议、组件和格式所组成的联合体中。下面的小节描述了一些可能发生的情况。

一个简单应用

在一个简单实现中，浏览器直接调用JSP页面，JSP页面自己生成被请求的内容（可能会调用JDBC直接从数据库中获取信息）。JSP页面能够调用JDBC或者Java BlendTM组件来生成结果，并且创建标准的HTML，作为结果发送回浏览器。

这个模型基本上用JSP页面（编译成为Java Servlet）代替了CGI-BIN概念。这个方法拥有下列优点：

简单而快速地编程

页面作者可以很容易地根据请求和资源状态生成动态内容

这个结构在许多应用上工作良好，但不能扩展到大量的基于Web的并发客户访问稀少的企业资源，因为每个客户必须建立或者共享一个到可用内容资源的连接。例如，如果JSP页面访问数据库，可能生成许多到数据库的连接，这将影响到数据库的性能。

使用Java Servlet的一个灵活的应用

在另一种可能的配置中，基于Web的客户机可能直接对Java Servlet进行请求，Servlet生成动态内容，将结果捆绑到一个结果对象中并且调用JSP页面。JSP页面从该对象中访问动态内容，并且将结果（例如HTML）发送回浏览器。

这个方法创建了更多的可以为应用程序间共享的可重用的组件，并且可以作为更大的应用的一部分完成。但是，在处理如数据库一样的企业资源的连接时，还是存在扩展性问题。

采用企业级JavaBean技术的可扩展处理

JSP页面也可以作为企业级JavaBean(EJB)体系结构中的一个中间层。在这种情况下，JSP页面和后端资源通过EJB组件进行交互。

EJB组件管理对后端资源的访问，从而为众多的并发使用者提供可扩展的性能。对于电子商务或者其他应用，EJB管理交易和潜在的安全性。这将简化JSP页面。这个模型将为Java 2企业版(J2EE)平台所支持。

JSP页面与XML技术的集成

JSP页面可以用于生成XML和HTML页面。

对于简单的XML生成，开发人员可以包含XML标识和JSP页面的静态模板部分。对于动态XML生成，使用基于服务器的对象和生成XML输出的客户化标识。

JSP页面与XML工具不是不兼容的。尽管Sun在设计JSP规范时使得JSP页面即使手工操作对于创作者而言也是很容易的，JSP规范同时也提供了一个机制以便于创建任意JSP页面的一个XML版本。通过这种方法，XML工具能够创作和操作JSP页面。

通过将JSP标识和元件转换为与XML兼容的对等物，可以使用基于XML的工具来操作JSP页面。例如，脚本可以被包含在<%和%>之中，或者基于XML标志的<jsp:scriptlet>和</jsp:scriptlet>中。事实上，经过下面这样简单的几步将JSP页面转换为XML页面是可能的，这些步骤包括：

增加一个JSP根元件

将元件和指示转换为XML兼容的对等物

为页面上其他的元件（通常非JSP）创建CDATA元件

通过这个与XML兼容的方法，创建HTML页面的设计者仍然拥有一个快速创建动态Web页面的易用环境，同时，基于XML的工具和服务可以与JSP页面集成并且和JSP兼容的服务器一起工作。

JSP技术的未来

JSP技术被设计为一个开放的，可扩展的建立动态Web页面的标准。开发人员可以使用JSP页面来创建可移植的Web应用，在不同的Web和应用服务器上为不同的场合所运行，而不论采用什么适合本身场合和需要的创建工具。

通过与业界领袖的合作，Sun保证JSP规范是开放的和可移植的。可以使用任何客户机和服务器平台，在任何地方编写和部署它们。将来，工具供应商和其他厂商将通过为专门的功能提供客户化的标识库而扩展平台的功能。

JSP规范的1.0版本是通向动态Web页面生成的一个开放的工业标准方法的第一步。1.0版本通过一个核心标识集、隐含对象以及开始创建动态Web页面所需的基本功能构成了该方法的基础。已经有几个Web服务器，应用服务器和开发工具供应商正在为他们的产品添加JSP1.0的支持，这样在业界已经拥有了最初的、立即的支持。

在1999晚些时候将完成的1.1版本，通过更多的XML支持、客户化标识、以及与J2EE的集成而扩展这个版本。并且供应商们可能会有选择地扩展和扩充在JSP规范中基本的、必需的功能。JSP引擎能够强有力地支持多种脚本语言和对象模型。在业界扩充和使用JSP技术能力的同时，也向Sun承诺将保证JSP技术保持平台和服务器间固有的可移植性。


Java布局管理器深入讨论

作者：五一

日期：2001-6-9 13:30:12

Java布局管理器深入讨论

5/23/2001 16:59:54· 刘之佑·yesky

--------------------------------------------------------------------------------

    我们都知道，java的GUI界面定义是由awt类和swing类来完成的。它在布局管理上面采用了容器和布局管理分离的方案。也就是说，容器只管将其他小件放入其中，而不管这些小件是如何放置的。对于布局的管理交给专门的布局管理器类（LayoutManager）来完成。

    其实，java在GUI方面应该是并不成功的。Awt类和swing类的结构非常复杂，加上充斥其间的子类继承和接口实现，使得要想掌握这两个类非常困难。这也是很多的java程序员抱怨的事情，但GUI已经成了程序发展的方向，所以这里我们也得勉为其难了。

    现在我们来看java中布局管理器的具体实现。我们前面说过，java中的容器类（Container），它们只管加入小件（Meta），也就是说，它只使用自己的add()方法向自己内部加入小件。同时他记录这些加入其内部的小件的个数，可以通过container.getComponentCount()方法类获得小件的数目，通过container.getComponent(i)来获得相应小件的句柄。然后LayoutManager类就可以通过这些信息来实际布局其中的小件了。

    java已经为我们提供了几个常用的布局管理器类，例如：BorderLayout、FlowLayout、GridBagLayout等等。但在实际的布局上，我们还是会有其他的需要。我在不久前的一个问题中曾经要一个垂直的流式布局，我称之为VflowLayout，其实BoxLayout和GridBagLayout可以完成类似的工作，但前者是swing类的成员，我的客户端是一个applet，不能使用，而后者必须在类生成的时候指定列数，而失去了灵活性，所以我决定重写一个自己的布局管理器来实现。经过分析，所有的LayoutManager都要实现一个接口，就是LayoutManager Inerface或者是他的一个子接口LayoutManager2 Interface，后者用于复杂的布局管理，例如GridCardLayout。LayoutManager有五个方法需要实现，分别是：

    1、public void addLayoutComponent(String name, Component comp);

    2、public void removeLayoutComponent(Component comp);

    3、public Dimension preferredLayoutSize(Container container);

    4、public Dimension minimumLayoutSize(Container container);

    5、public void layoutContainer(Container container);

    第一个方法其实就是你在使用container.add(String name,component comp);时调用的方法，例如BorderLayout为布局管理器时。但在FlowLayout中由于没有其他的附加信息，所以不需要填充这个方法。相应的第二个方法也就不需要填充了。真正核心的方法是第三个和第五个方法，前者是最终确定Container有多大的，而后者就是决定Container中各个小件的实际位置的了。也就是说，当我们用container.setLayout(LayoutManager)后，再加入小件后，最后系统做的工作其实是LayoutManager. layoutContainer(container);和container.setSize(LayoutManager. PreferredLayoutSize(container));。

    下面是我的新类：VflowLayout。

    package render_account;

    import java.awt.*;

    import java.io.*;

    public class VFlowLayout implements LayoutManager,Serializable{

      int hgap;

      int vgap;

      public VFlowLayout(){

        this(5,5);

      }

      public VFlowLayout(int i,int j){

        this.hgap=i;

        this.vgap=j;

      }

      public void addLayoutComponent(String name, Component comp){

      }

      public void removeLayoutComponent(Component comp){

        }

      public Dimension preferredLayoutSize(Container container){

        synchronized(container.getTreeLock()){

        Dimension dimension1=new Dimension(0,0);

        int i=container.getComponentCount();

        for(int j=0;j 　　　　　　Component component = container.getComponent(j);

            if(component.isVisible()){

                Dimension dimension2=component.getPreferredSize();

                dimension1.width=Math.max(dimension1.width,dimension2.width);

                if(j>0)

                  dimension1.height+=vgap;

                  dimension1.height+=dimension2.height;

                }

            }

            Insets insets=container.getInsets();

            dimension1.height+=insets.top+insets.bottom+vgap*2;

            dimension1.width+=insets.left+insets.right+hgap*2;

            Dimension dimension=dimension1;

            return dimension;

            file://return(new Dimension(50,200));

          }

        }

        public Dimension minimumLayoutSize(Container container){

          synchronized(container.getTreeLock()){

          Dimension dimension1=new Dimension(0,0);

          int i=container.getComponentCount();

          for(int j=0;j 　　　　　Component component = container.getComponent(j);

          if(component.isVisible()){

            Dimension dimension2=component.getMinimumSize();

            dimension1.width=Math.max(dimension1.width,dimension2.width);

            if(j>0)

              dimension1.height+=vgap;

              dimension1.height+=dimension2.height;

            }

          }

          Insets insets=container.getInsets();

          dimension1.height+=insets.top+insets.bottom+vgap*2;

          dimension1.width+=insets.left+insets.right+hgap*2;

          Dimension dimension=dimension1;

          return dimension;

        }

      }

      public void layoutContainer(Container container){

        synchronized(container.getTreeLock()){

        Insets insets=container.getInsets();

        int vSpace=container.getSize().height-(insets.top+insets.bottom+vgap*2);

        int componentCount=container.getComponentCount();

        int left=insets.left+hgap;

        int totalHeight=0;

        int width=0;

        int componentStart=0;

        for(int i=0;i 　　　　　Component component=container.getComponent(i);

          if(component.isVisible()){

            Dimension dimension=component.getPreferredSize();

            component.setSize(dimension.width,dimension.height);

            if(totalHeight==0 || totalHeight+dimension.height<=vSpace){

              if(totalHeight>0)

                totalHeight+=vgap;

                totalHeight+=dimension.height;

                width=Math.max(width,dimension.width);

              }else{

                moveComponents(container,insets.top+vgap,left,width,componentStart,i);

                totalHeight=0;

                left+=hgap+width;

                width=dimension.width;

                componentStart=i;

              }

            }

          }

          moveComponents(container,insets.top+vgap,left,width,componentStart,componentCount);

        }

      }

      private void moveComponents(Container container,int top,int left,int width,int 　　　　　　　　　componentStart,int componentEnd){

          synchronized(container.getTreeLock()){

            for(int i=componentStart;i 　　　　　　　Component component=container.getComponent(i);

              if(component.isVisible()){

                component.setLocation(left,top);

                top+=component.getPreferredSize().height+vgap;

              }

            }

          }

        }

        public void setHgap(int i){

            this.hgap=i;

        }

        public void setVgap(int i){

            this.vgap=i;

        }

        public int getHgap(){

          return(this.hgap);

        }

        public int getVgap(){

          return(this.vgap);

        }

      }

    大家可以试一下。


Java代码编写的一般性指导 (转贴)

作者：赖皮王子

日期：2001-6-9 17:01:30

作者 : javaduke(http://www.dukejava.com

(1) 类名首字母应该大写。字段、方法以及对象（句柄）的首字母应小写。对于所有标识符，其中包含的所有单词都应紧靠在一起，而且大写中间单词的首字母。例如：

ThisIsAClassName

thisIsMethodOrFieldName

若在定义中出现了常数初始化字符，则大写static final基本类型标识符中的所有字母。这样便可标志出它们属于编译期的常数。

Java包（Package）属于一种特殊情况：它们全都是小写字母，即便中间的单词亦是如此。对于域名扩展名称，如com，org，net或者edu等，全部都应小写（这也是Java 1.1和Java 1.2的区别之一）。

(2) 为了常规用途而创建一个类时，请采取"经典形式"，并包含对下述元素的定义：

equals()

hashCode()

toString()

clone()（implement Cloneable）

implement Serializable

(3) 对于自己创建的每一个类，都考虑置入一个main()，其中包含了用于测试那个类的代码。为使用一个项目中的类，我们没必要删除测试代码。若进行了任何形式的改动，可方便地返回测试。这些代码也可作为如何使用类的一个示例使用。

(4) 应将方法设计成简要的、功能性单元，用它描述和实现一个不连续的类接口部分。理想情况下，方法应简明扼要。若长度很大，可考虑通过某种方式将其分割成较短的几个方法。这样做也便于类内代码的重复使用（有些时候，方法必须非常大，但它们仍应只做同样的一件事情）。

(5) 设计一个类时，请设身处地为客户程序员考虑一下（类的使用方法应该是非常明确的）。然后，再设身处地为管理代码的人考虑一下（预计有可能进行哪些形式的修改，想想用什么方法可把它们变得更简单）。

(6) 使类尽可能短小精悍，而且只解决一个特定的问题。下面是对类设计的一些建议：

■一个复杂的开关语句：考虑采用"多形"机制

■数量众多的方法涉及到类型差别极大的操作：考虑用几个类来分别实现

■许多成员变量在特征上有很大的差别：考虑使用几个类

(7) 让一切东西都尽可能地"私有"--private。可使库的某一部分"公共化"（一个方法、类或者一个字段等等），就永远不能把它拿出。若强行拿出，就可能破坏其他人现有的代码，使他们不得不重新编写和设计。若只公布自己必须公布的，就可放心大胆地改变其他任何东西。在多线程环境中，隐私是特别重要的一个因素--只有private字段才能在非同步使用的情况下受到保护。

(8) 谨惕"巨大对象综合症"。对一些习惯于顺序编程思维、且初涉OOP领域的新手，往往喜欢先写一个顺序执行的程序，再把它嵌入一个或两个巨大的对象里。根据编程原理，对象表达的应该是应用程序的概念，而非应用程序本身。

(9) 若不得已进行一些不太雅观的编程，至少应该把那些代码置于一个类的内部。

(10) 任何时候只要发现类与类之间结合得非常紧密，就需要考虑是否采用内部类，从而改善编码及维护工作（参见第14章14.1.2小节的"用内部类改进代码"）。

(11) 尽可能细致地加上注释，并用javadoc注释文档语法生成自己的程序文档。

(12) 避免使用"魔术数字"，这些数字很难与代码很好地配合。如以后需要修改它，无疑会成为一场噩梦，因为根本不知道"100"到底是指"数组大小"还是"其他全然不同的东西"。所以，我们应创建一个常数，并为其使用具有说服力的描述性名称，并在整个程序中都采用常数标识符。这样可使程序更易理解以及更易维护。

(13) 涉及构建器和异常的时候，通常希望重新丢弃在构建器中捕获的任何异常--如果它造成了那个对象的创建失败。这样一来，调用者就不会以为那个对象已正确地创建，从而盲目地继续。

(14) 当客户程序员用完对象以后，若你的类要求进行任何清除工作，可考虑将清除代码置于一个良好定义的方法里，采用类似于cleanup()这样的名字，明确表明自己的用途。除此以外，可在类内放置一个boolean（布尔）标记，指出对象是否已被清除。在类的finalize()方法里，请确定对象已被清除，并已丢弃了从RuntimeException继承的一个类（如果还没有的话），从而指出一个编程错误。在采取象这样的方案之前，请确定finalize()能够在自己的系统中工作（可能需要调用System.runFinalizersOnExit(true)，从而确保这一行为）。

(15) 在一个特定的作用域内，若一个对象必须清除（非由垃圾收集机制处理），请采用下述方法：初始化对象；若成功，则立即进入一个含有finally从句的try块，开始清除工作。

(16) 若在初始化过程中需要覆盖（取消）finalize()，请记住调用super.finalize()（若Object属于我们的直接超类，则无此必要）。在对finalize()进行覆盖的过程中，对super.finalize()的调用应属于最后一个行动，而不应是第一个行动，这样可确保在需要基础类组件的时候它们依然有效。

(17) 创建大小固定的对象集合时，请将它们传输至一个数组（若准备从一个方法里返回这个集合，更应如此操作）。这样一来，我们就可享受到数组在编译期进行类型检查的好处。此外，为使用它们，数组的接收者也许并不需要将对象"造型"到数组里。

(18) 尽量使用interfaces，不要使用abstract类。若已知某样东西准备成为一个基础类，那么第一个选择应是将其变成一个interface（接口）。只有在不得不使用方法定义或者成员变量的时候，才需要将其变成一个abstract（抽象）类。接口主要描述了客户希望做什么事情，而一个类则致力于（或允许）具体的实施细节。

(19) 在构建器内部，只进行那些将对象设为正确状态所需的工作。尽可能地避免调用其他方法，因为那些方法可能被其他人覆盖或取消，从而在构建过程中产生不可预知的结果（参见第7章的详细说明）。

(20) 对象不应只是简单地容纳一些数据；它们的行为也应得到良好的定义。

(21) 在现成类的基础上创建新类时，请首先选择"新建"或"创作"。只有自己的设计要求必须继承时，才应考虑这方面的问题。若在本来允许新建的场合使用了继承，则整个设计会变得没有必要地复杂。

(22) 用继承及方法覆盖来表示行为间的差异，而用字段表示状态间的区别。一个非常极端的例子是通过对不同类的继承来表示颜色，这是绝对应该避免的：应直接使用一个"颜色"字段。

(23) 为避免编程时遇到麻烦，请保证在自己类路径指到的任何地方，每个名字都仅对应一个类。否则，编译器可能先找到同名的另一个类，并报告出错消息。若怀疑自己碰到了类路径问题，请试试在类路径的每一个起点，搜索一下同名的.class文件。

(24) 在Java 1.1 AWT中使用事件"适配器"时，特别容易碰到一个陷阱。若覆盖了某个适配器方法，同时拼写方法没有特别讲究，最后的结果就是新添加一个方法，而不是覆盖现成方法。然而，由于这样做是完全合法的，所以不会从编译器或运行期系统获得任何出错提示--只不过代码的工作就变得不正常了。

(25) 用合理的设计方案消除"伪功能"。也就是说，假若只需要创建类的一个对象，就不要提前限制自己使用应用程序，并加上一条"只生成其中一个"注释。请考虑将其封装成一个"独生子"的形式。若在主程序里有大量散乱的代码，用于创建自己的对象，请考虑采纳一种创造性的方案，将些代码封装起来。

(26) 警惕"分析瘫痪"。请记住，无论如何都要提前了解整个项目的状况，再去考察其中的细节。由于把握了全局，可快速认识自己未知的一些因素，防止在考察细节的时候陷入"死逻辑"中。

(27) 警惕"过早优化"。首先让它运行起来，再考虑变得更快--但只有在自己必须这样做、而且经证实在某部分代码中的确存在一个性能瓶颈的时候，才应进行优化。除非用专门的工具分析瓶颈，否则很有可能是在浪费自己的时间。性能提升的隐含代价是自己的代码变得难于理解，而且难于维护。

(28) 请记住，阅读代码的时间比写代码的时间多得多。思路清晰的设计可获得易于理解的程序，但注释、细致的解释以及一些示例往往具有不可估量的价值。无论对你自己，还是对后来的人，它们都是相当重要的。如对此仍有怀疑，那么请试想自己试图从联机Java文档里找出有用信息时碰到的挫折，这样或许能将你说服。

(29) 如认为自己已进行了良好的分析、设计或者实施，那么请稍微更换一下思维角度。试试邀请一些外来人士--并不一定是专家，但可以是来自本公司其他部门的人。请他们用完全新鲜的眼光考察你的工作，看看是否能找出你一度熟视无睹的问题。采取这种方式，往往能在最适合修改的阶段找出一些关键性的问题，避免产品发行后再解决问题而造成的金钱及精力方面的损失。

(30) 良好的设计能带来最大的回报。简言之，对于一个特定的问题，通常会花较长的时间才能找到一种最恰当的解决方案。但一旦找到了正确的方法，以后的工作就轻松多了，再也不用经历数小时、数天或者数月的痛苦挣扎。我们的努力工作会带来最大的回报（甚至无可估量）。而且由于自己倾注了大量心血，最终获得一个出色的设计方案，成功的快感也是令人心动的。坚持抵制草草完工的诱惑--那样做往往得不偿失


JAVA的声音处理方法 (转自伊氏女人)

作者：jeru

email: jeru@163.net

日期：6/27/2001 5:27:48 PM

JAVA的声音处理方法

作者：蝙蝠vs堕落 　　编辑：violet

 JAVA的声音处理

     数字音频格式有很多种，其质量与采样频率和采样精度两个参数有关。频率的表示单位为赫兹〔Hz〕，它表示每秒采样次数。采样频率越高，音质就越好。采样精度为每次采样所存储的数据数量，它决定每个数字信号所能够表示的离散振幅的数量。存储每个样本的数据越多，音质就越好。但是高品质的声音需要占用大量的内存和磁盘空间。考虑到网络带宽，在Internet连接上传输就需要花费很长的时间。对于Applet来说，保证声音文件的最小化是极为重要的。

 Java能够支持以下四种声音格式：

 ◆AU

◆AIFF

◆WAVE

◆MIDI

    第一种声音格式AU为以前的Java 1.1版本支持的唯一的声音格式。采样频率为8000赫兹，采样精度为8位。AIFF和WAVE与AU格式一样，都用来表示数字化的声音。其中，WAVE格式提供了更宽范围的音质。MIDI格式专用于音乐，并且以音符与乐器而不是数字化的声音来描述声音的。

    关于声音的创建：声音关键工具为Windows9X中的标准应用程序—录音机。录制的格式为WAVE。另外还有很多专业水准的应用程序，采样和编辑数字化的声音，及格式的转换。

 资源需求：

 ■ 硬件资源需求：

⑴声卡

⑵音箱

＃如需要录制声音，这还需麦克风。

■ 软件资源需求：

⑴windows9X操作系统

⑵网络浏览器

⑶Java插件/运行时间环境

 处理方案一

     JAVA2开始，爪哇语言能够支持上述四种声音格式。简单的方法为在java中通过Applet类的AudioClip()接口来装载声音。该接口风钻了有关声音片断的常用方法，具有对播放声音片断的最小支持。

该接口定义了如下方法：

■ play()

语法：void play()

功能：开始播放声音片断。

不论声音片断是否已经在播放，都从头播放。播放一遍但不重复。

■ loop()

语法：void loop()

功能：循环播放声音片断。

调用该方法是不论声音片断是否已在播放，都从头开始播放。

■ stop()

语法：void stop()

功能：停止播放声音片断。

AudioClip接口可以通过getAudioClip()及getCodeBase()方法来获取声音片断及

URL地址。可以利用此方法在WEB页中播放指定的声音片断。

＃需求：基于JDK1.2以上版本的开发环境。如果只播放AU格式的声音文件，JDK1

.1就可。

附：soundtest.html源代码

《HTML》

《HEAD》

《TITLE》

HTML Test Page

《/TITLE》

《/HEAD》

《BODY》

《APPLET

CODEBASE = "" CODE = "test.class" NAME = "TestApplet" WID

TH = 400 HEIGHT = 200 HSPACE = 0 VSPACE = 0 ALIGN = mi

ddle》

《PARAM NAME = "clip" VALUE = "sound.AU"》

《/APPLET》

《/BODY》

《/HTML》

附：test,java源代码

package SoundTest;

import java.awt.*;

import java.awt.event.*;

import java.applet.*;

import javax.swing.*;

import java.net.*;

public class test extends JApplet {

boolean isStandalone = false;

String sound;

JButton jButtonPlay = new JButton();

JButton jButtonLoop = new JButton();

JButton jButtonStop = new JButton();

JLabel jLabel1 = new JLabel();

AudioClip clip;

//Get a parameter value

public String getParameter(String key, String def) {

return isStandalone ? System.getProperty(key, def) :

(getParameter(key) != null ? getParameter(key) : def);

}

//Construct the applet

public test() {

}

//Initialize the applet

public void init() {

try {

jbInit();

}

catch(Exception e) {

e.printStackTrace();

}

}

//Component initialization

private void jbInit() throws Exception {

jButtonPlay.setText("Play");

jButtonPlay.setBounds(new Rectangle(50, 85, 80, 40));

jButtonPlay.addMouseListener(new java.awt.event.MouseAdapter() {

public void mouseClicked(MouseEvent e) {

jButtonPlay_mouseClicked(e);

}

});

this.setSize(new Dimension(400,200));

this.getContentPane().setLayout(null);

jButtonLoop.setText("Loop");

jButtonLoop.setBounds(new Rectangle(150, 85, 80, 40));

jButtonLoop.addMouseListener(new java.awt.event.MouseAdapter() {

public void mouseClicked(MouseEvent e) {

jButtonLoop_mouseClicked(e);

}

});

jButtonStop.setText("Stop");

jButtonStop.setBounds(new Rectangle(250, 85, 80, 40));

jButtonStop.addMouseListener(new java.awt.event.MouseAdapter() {

public void mouseClicked(MouseEvent e) {

jButtonStop_mouseClicked(e);

}

});

jLabel1.setText("Sound Test Demo");

jLabel1.setBounds(new Rectangle(109, 28, 186, 28));

this.getContentPane().add(jButtonPlay, null);

this.getContentPane().add(jButtonStop, null);

this.getContentPane().add(jButtonLoop, null);

this.getContentPane().add(jLabel1, null);

try { sound = this.getParameter("clip", "sound.AU"); } catch (Exce

ption e) { e.printStackTrace(); }

if(sound!=null){

try{

clip=JApplet.newAudioClip (new URL(getCodeBase(),sound));

}

catch(MalformedURLException e){

System.out.println ("Bad URL");

}

}

}

//Get Applet information

public String getAppletInfo() {

return "Applet Information";

}

//Get parameter info

public String[][] getParameterInfo() {

String[][] pinfo =

{

{"clip", "String", "sound.AU"},

};

return pinfo;

}

void jButtonPlay_mouseClicked(MouseEvent e) {

clip.play();

}

void jButtonLoop_mouseClicked(MouseEvent e) {

clip.loop();

}

void jButtonStop_mouseClicked(MouseEvent e) {

clip.stop();

}

}

 处理方案二

     利用JAVA媒体框架中的Sound API来处理声音片断。Sound API 被包含在SUN公司的JAVA媒体框架中，也包含在JDK1.3版本中。编译源代码需要支持JDK1.3的开发环境和运行时间环境。

    可以利用Sound API在网页中加入Applet的方式达到要求。但浏览器需要通过Java插件或运行时间环境的支持。

Sound API主要包括以下四部分：

Packages

javax.sound.midi 提供MIDI (Musical Instrument Digital Interface)的I/O、

序列、合成的接口和类。

javax.sound.midi.spi Supplies interfaces for service providers to impl

ement when offering new MIDI devices, MIDI file readers and writers, o

r sound bank readers.

javax.sound.sampled 为采样数字音频的捕获、处理、重放提供接口和类。

javax.sound.sampled.spi Supplies abstract classes for service provider

s to subclass when offering new audio devices, sound file readers and

writers, or audio format converters.

 处理方案三

     利用JAVA 3D中的sound类。该类似声音资源的抽象定义。每一个具体的声音对象都可以应用sound的方法。

Sound类的类关系如下：

java.lang.Object

+-javax.media.j3d.SceneGraphObject

+-javax.media.j3d.Node

+-javax.media.j3d.Leaf

+-javax.media.j3d.Sound

sound类的子类有：

⒈BackgroundSound

⒉PointSound

⒊ConeSound

使用上述三种声音的步骤如下：

⒈定义并生成一个MediaContainer对象，同时提供对象所需要的声音文件，通常

要单独设置一个存放AU/WAV声音文件的目录。

⒉定义一个BackgroundSound/ PointSound/ ConeSound对象，根据需要，设置声

音的是否循环、强度大小等参数，设置它的作用范围并setEnable用使其开始作用。

⒊根据具体的对象设置坐标系。

＃JAVA 3D应用程序只能播放au/wav格式的声音文件。不能播放midi文件。


JAVA的网络功能与编程 二 （转）--建议放入精华区 ？ 因我等新手爱看

作者：jtwww

日期：2000-11-6 16:10:59

    五、显示网络上其他HTML文档

    利用Java提供的getAppletContext().showDocument(URL)可以

显示其他结点的HTML文档，同前面的显示网络上其他结点的图象，

有两种格式，下面各举一例：

                      ●程序8   格式一

import java.applet.*;

import java.awt.*;

import java.net.*;

public class showdoc extends Applet

{

    URL docur= null;

    public void paint(Graphics g) {

      try {

            docur=new URL("http://www.shu.edu.cn/~xyx/doc/manhua.html" ;

      }

      catch (MalformedURLException e) {

                System.out.println("Can't open the URL ");

      }

      if (docur != null) {

                    getAppletContext().showDocument(docur,"_blank");

      }

    }

}

                          ●程序9 格式二

import java.applet.*;

import java.awt.*;

import java.net.*;

public class showdoc2 extends Applet

{

   URL docur= null;

   public void paint(Graphics g) {

      try {

getAppletContext().showDocument(new URL("http://www.shu.edu.cn/

~xyx/doc/manhua.html"));

      }

      catch (MalformedURLException e) {

                System.out.println("Can't open the URL ");

      }

   }

}

     六、读取网络上文件内容

     前述的网络功能只是显示或播放网络上结点的图象、 声音及

HTML文档，并没有对其内容进行处理。事实上，Java还可读取网络

上文件的内容，并对其内容进行处理。

    读取网络上文件内容的步骤可如下：

    1. 创建一个URL类型的对象

    如：

    String url = "ftp://202.120.127.218/incoming/test/readtxt.html";

URL fileur;

        try {

fileur = new URL(url); }

        catch ( MalformedURLException e) {

               System.out.println("Can't get URL: " );

        }

    2. 利用URL类的openStream()，获得对应的InputStream类的对象

    如：

InputStream filecon = fileur.openStream();

    3. 将InputStream对象转化为DataInputStream类的对象

    如：

    DataInputStream filedata = new DataInputStream(filecon);

    4. 读取内容

    如对前面的filedata，可用filedata.readLine() 一行一行读

取内容，或用filedata.readchar一个字符一个字符读取内容。 对

读取到的内容，可由Java Applet进行各种处理， 并将处理结果用

各种方式显示出来。

    下面的例子是读取 http://www.shu.edu.cn/~xyx/doc/manhua.html

文件内容的例子，为简洁起见，该例中只将文件的内容逐行读出，

并在文本区显示出来。

●程序10

import java.io.*;

import java.net.*;

import java.awt.*;

import java.applet.*;

public class showfile extends Applet{

     URL fileur;

     TextArea showarea = new TextArea("Please wait a while for get

text",10,70);

     public void init() {

        String url = "http://www.shu.edu.cn/~xyx/doc/manhua.html";

        try { fileur = new URL(url); }

        catch ( MalformedURLException e) {

               System.out.println("Can't get URL: " );

        }

        add(showarea);

}

    public void paint(Graphics g) {

        InputStream filecon = null;

        DataInputStream filedata = null;

        String fileline;

try {

                filecon = fileur.openStream();

                filedata = new DataInputStream(filecon);

                while ((fileline = filedata.readLine()) != null) {

                        showarea.appendText(fileline+"\n");

}

}

catch (IOException e) {

                System.out.println("Error in I/O:" + e.getMessage());

}

   }

}

    七、动态使用网络上资源

    在前面介绍的例子的基础上，可以动态地利用网络上的资源。

其方法是编制一个线程，每隔一定时间自动到相应结点读取最新的

内容。本文对线程的编制不再展开，读者可参考有关文章或直接套

用下面的例子。

    例如对上例中读取http://www.shu.edu.cn/~xyx/doc/manhua

.html文件内容的例子，加入线程后如下所示。该例子每隔5秒更新

一次数据。如果http://www.shu.edu.cn/~xyx/doc/manhua.html

存放的是一些变化较快的信息如股市行情等，并有程序随时动态地

更新其内容，则在Web中加入这种Java Applet，可以让流览者得到

动态的信息。进一步，也可以在程序中对数据进行处理，并用图形

方式显示处理结果。例如将各时刻的数据绘制成曲线，流览者可以

看到动态变化的曲线。

//程序11

import java.io.*;

import java.net.*;

import java.awt.*;

import java.applet.*;

public class dynashow extends java.applet.Applet

    implements Runnable {

    Thread dthread;

    URL fileur;

TextArea showarea = new TextArea("Wait for a while...",10,70);

     public void init() {

        String url = " http://www.shu.edu.cn/~xyx/doc/manhua.html";

        try { fileur = new URL(url); }

        catch ( MalformedURLException e) {

               System.out.println("Can't get URL: " );

        }

        add(showarea);

     }

     public void start() {

        if (dthread == null)

        {

                dthread = new Thread(this);

                dthread.start();

        }

    }

    public void stop() {

        if (dthread != null) {

                dthread.stop();

                dthread = null;

        }

    }

    public void run() {

        InputStream filecon = null;

        DataInputStream filedata = null;

        String fileline;

        while(true){

            try {

                filecon = fileur.openStream();

                filedata = new DataInputStream(filecon);

                while ((fileline = filedata.readLine()) != null) {

                        showarea.appendText(fileline+"\n");

        }

            }

           catch (IOException e) {

                System.out.println("Error in I/O:" + e.getMessage());

           }

           try{

                dthread.sleep(5000);

           }

           catch (InterruptedException e){}

           repaint();

       }

     }

}

    八、Java网络能力的限制

    出于安全性考虑，在用netscape浏览时，Java Applet 只能和

其所在的主机建立连接，因此，前面的程序编译后大部分只能存放

在http://www.shu.edu.cn/~xyx

需更改程序中的结点地址。否则浏览器将显示安全出错。

    但对显示网络上其他HTML文档没有此限制（如程序8、9），读

者可以将程序编译后放到任意WWW服务器或FTP服务器，均可正常运

行。

    此外，当浏览器从本地盘打开调用Java Applet的HTML文档时，

也不受此限制。因此，本文所有的程序都可存放在本地盘编译，只

要用netscape的File/Open File菜单打开，便可正确运行。

    对于另一种Java程序－－Java Application，也无此限制，例

如对于读取网络上文件内容的程序10，对应的Java Application可

作如下编程：

●程序11

import java.io.*;

import java.net.*;

import java.awt.*;

class showfile2 {

     public static void main(String args[]){

        InputStream filecon = null;

        DataInputStream filedata = null;

        String fileline;

        String url = "http://www.shu.edu.cn/~xyx/doc/manhua.html";

        URL fileur;

        try {

   fileur = new URL(url);

                filecon = fileur.openStream();

                filedata = new DataInputStream(filecon);

                while ((fileline = filedata.readLine()) != null) {

System.out.println(fileline+"\n");

}

}

catch (IOException e) {

                System.out.println("Error in I/O:" + e.getMessage());

}

  }

}

    将其以showfile2.java存盘，用javac showfile2.java编译后，

只需执行“java showfile2”便可以在屏幕上打印出

http://www.shu.edu.cn/~xyx/doc/manhua.html文件的内容。

    九、创建URL对象的方法

    在前面的例子中我们统一使用new URL(url字符串)的形式创建

URL对象。其实，Java提供了四种创建URL对象的形式：

    1.new URL(url字符串)    本文中的程序均采用此种格式，如：

new  URL("http://www.shu.edu.cn/~xyx/doc/manhua.html"

    2.new URL(协议，主机名，文件名或路径)    如程序2中的

String  url = "http://www.shu.edu.cn/~xyx/img/shnet.jpg";

image = getImage(new URL(url));部分可改为：

image = getImage(new URL("http","http://www.shu.edu.cn","/~xyx/img/shnet.jpg"));

    3.new URL(协议，主机名，端口号，文件名或路径)1

如：new URL("http","http://www.shu.edu.cn",80,"/~xyx/doc/manhua.html")

    4.new URL(基准url，文件名或路径)

    十、实现网络功能的其他方法

    以上着重介绍了利用Java的URL类实现从网络上获取声音、 图

象、HTML文档及文件数据的编程方法。Java的网络功能很强大，除

上面介绍的外，还可以利用URLconnection 类实现更广泛的网络功

能，如向WWW 服务器上的 CGI 程序发送信息等； 通过 Socket 及

ServerSocket类，可以自己编写客户软件及服务软件，并可以自己

设计通讯协议。

〖参考文献〗

Laura Lemay,Charles L. Perkins  "Teach Yourself JAVA in 21 Days"

摘自《Internet世界》


Java的网络功能与编程 一（转）---建议放入精华区

作者：jtwww

日期：2000-11-6 16:07:55

Java的网络功能与编程

徐迎晓

(上海大学计算中心25＃)

摘  要：Java语言是Internet上最热门的编程语言，本文针对

       Java的网络功能，对Java从网络上获取图象、声音、

       HTML文档及文本文件等编程方法作了初步的介绍，同

       时介绍了动态获取网络上资源的方法作了介绍。文中

       提供了大量简明易懂的实例。

关键词：Java；Internet；网络

    Java语言是Internet上新兴的编程语言，对Java的特性以及基

本的编程方法已有很多文章作过介绍。但是，广大Java爱好者更希望

了解Java更深一步的编程方法，本文就Java的网络功能及其编程方法

作一初步的介绍。

    为了方便初次接触Java的读者，本文先就Java编程的一些常识

作简单介绍。

    一、Java编程简介

    1. 编程环境：

    对于大部分读者，以下的配置是较为经济的一种选择：

操作系统 Win95

编译软件 JDK1.01

浏览软件 Netscape2.0以上（32位）

    2. 编程方法：

    先用文本编辑器如Edit、NotePad等输入Java程序，以.java为

文件名后缀存盘。

    再执行命令行：“Javac 文件名”来编译Java程序。编译后生成

后缀为.class的字节码文件。

    最后，如果是Java Applitcation，则执行命令行：“Java 字

节码文件名”来运行Java程序。

    如果是Java Applet，则用文本编辑器输入调用该Java Applet

的HTML 文档， 以 .htm 为文件名后缀存盘。 再执行命令行：

“appletviewer HTML文件名”来运行Java Applet。或用Netscape

打开该HTML文档。

    3. 关于本文中程序的说明

    为了使程序能够最简洁地体现其所代表的编程方法，本文中的程

序一般采用最简单的形式，省略了线程等内容。因此，本文的程序不

是“好”的程序，但最容易为初学者理解。

    本文的所有程序经编译后，生成的字节码文件及对应的HTML文档

已上载到http://www.shu.edu.cn/~xyx/test/jvnet均可正确

运行。连入Internet的读者可用浏览器打开该地址，查看运行效果。

    连入Internet的读者也可以在本地硬盘输入并编译本文的程序，

用Netscape的File/Open File菜单打开HTML文档，体会编程方法并

查看运行效果。如果读者想将Java Applet 放到自己的主机上或其他

ftp服务器上，在Netscape中用http协议或ftp协议调用，出于安全

性限制，应作如下修改：

   如果读者在某个WWW主机上有帐号，可以做个人Homepage（一般

在用户根目录创建WWW或public_html目录即可，Homepage的地址为

http://HostName/~个人帐号），可将本文程序中对应的

http://www.shu.edu.cn/~xyx/

然后将编译后生成的字节码文件及对应的HTML文档上载到自己的结点上。

    如果读者的计算机连入了Internet，也可以找一个可以上载的

ftp结点，如：ftp://ftp.shnet.edu.cn/incoming

对应的http://www.shu.edu.cn/~xyx/

将编译后生成的字节码文件及对应的HTML文档上载到该结点上，以查

看运行效果。

    如果读者的计算机没有联网，也可以在单机上运行Web 服务软

件如Webstar for Win95，将本文程序中对应的http: //http://www.shu

.edu.cn/~xyx/部分修改为“http://

网络编程。

    二、Java网络功能及获取网络上资源的一般步骤

    Java程序可以获取网络上结点的图象、声音、HTML文档及文本

等资源，并可以对获得的资源进行处理。例如Java程序可以每隔一

定时间读取某结点提供的最新数据，并以图表的形式显示出来。

    在编程处理上，一般先生成一个URL类型的对象，然后用 Java

中相应的方法（method）获取该对象所代表的资源。下面分别介绍

Java网络功能的几个例子，并由此介绍几种不同的编程方法。

    三、从网络上获取图象

    Java Applet可以直接从网络上结点获取图象并显示出来。 为

了了解其编程方法和从本地显示图象的编程有何不同，我们先不考虑

网络功能，来看一个简单的图象显示的例子：

●程序1

import java.applet.*;

import java.awt.*;

public class imag0 extends Applet{

  Image image;

  public void init() {

image=getImage(getDocumentBase(),"test.gif");

  }

  public void paint(Graphics g) {

     g.drawImage(image, 0, 0,this);

  }

}

    这是一个最简单的获取并显示图象的例子， 在该例中， 先用

getImage(getDocumentBase(),图象文件名)从HTML文档所在位置调

用图象test.gif，并由此生成一个Image类型的对象image， 然后用

drawImage(image, 0, 0,this)在屏幕上将图象显示出来。

    如果想从网络上其他结点获取图象，关键是创建对应于网络上

其他结点的Image类型的对象，一旦获得Image类型的对象获得了，

便可以对其进行任何可能的图象操作。

    Java提供了如下方法可以创建对应于其他结点的图象：

getImage(new URL(字符串))

    其使用格式可有两种：

    String  url = "结点URL";

    Image image;

    try {

      image = getImage(new URL(url));

    }

catch(Exception e){

System.out.println("Can't open the URL ");

    }

或

URL imgur=null;

Image image;

try {

            imgur=new URL("结点URL ");

     }

     catch (MalformedURLException e) {

            System.out.println("Can't open the URL ");

     }

     image=getImage(imgur);

    前一种格式用“new URL(url)”生成 URL 对象， 并直接作为

getImage的参数，后一种格式先用“new URL(url)”生成一个 URL

对象，再传给getImage。两种格式本质上是一样的。两种格式中，

生成URL对象的部分都包含在

try{

获取URL对象

                           }

catch (MalformedURLException e) {

出错提示

                           }

中。

    例如要调用http://www.shu.edu.cn/~xyx/img/shnet.jpg

的图象，第一种格式完整的程序如下：

●程序2

import java.applet.*;

import java.net.*;

import java.awt.*;

public class imag extends Applet{

  Image image;

  public void init() {

    String  url = "http://www.shu.edu.cn/~xyx/img/shnet.jpg";

    try {

      image = getImage(new URL(url));

    } catch(Exception e){}

  }

  public void paint(Graphics g) {

     g.drawImage(image, 0, 0,this);

  }

}

    第二种格式完整的程序如下：

●程序3

import java.applet.*;

import java.net.*;

import java.awt.*;

public class imag2 extends Applet{

  Image image;

  URL imgur=null;

  public void init() {

     try {

      imgur=new URL("http://www.shu.edu.cn/~xyx/img/shnet.jpg" ;

         }

        catch (MalformedURLException e) {

                System.out.println("Can't open the URL ");

      }

      image=getImage(imgur);

  }

  public void paint(Graphics g) {

     g.drawImage(image, 0, 0,this);

  }

}

    将上述两个程序分别以imag.java和imag2. java 存盘， 执行

javac imag.java和javac imag2.java，将得到编译后生成的imag

.class和imag2.class，最后创建调用这两个Java Applet的HTML文

档，如imag.class对应的HTML文档可如下：

< html >

< head >

< title >Example < /title >

< /head >

< center >

< applet code=imag.class width=550 height=250 >

< /applet >

< /html >

    将该HTML文档存入test.html文件，用Netscape打开， 如果你

的计算机连入了Internet，便可以看到Java Applet 所显示的从网

络上获得的图象了。（对于本文中其他不同的Java Applet， 对应

的HTML文档只要修改其中相应的“code=imag.class”即可。）

    四、从网络上获取声音

    Java从网络上获取声音文件并播放声音的编程方法有两类，一

是利用Java提供的play(URL)及play(URL,String) 直接播放网络

上的声音文件，另一类是通过getAudioClip(URL)或getAudioClip

(URL,String)先从网络上获取声音文件，并生成AudioClip 类型的

对象，然后对该对象进行操作。

    前者的使用格式是：

String  Audur = "结点URL";

        try {

     play(new URL(Audur));

        } catch(Exception e){}

或

String  Audur = "结点URL";

        try {

           play(new URL(Audur),声音文件名);

        } catch(Exception e){}

    后者使用的格式是：

String Audur = "结点URL";

AudioClip  loopClip;

    try {

           loopClip = getAudioClip(new URL(Audur));

        }

catch(Exception e){

System.out.println("Can't open the URL ");

                  }

或

String Audur = "结点URL";

    AudioClip  loopClip;

    try {

           loopClip = getAudioClip(new URL(Audur) ,声音文件名);

                    }

catch(Exception e){

System.out.println("Can't open the URL ");

}

    上面的四种格式都是将生成URL对象部分－－“new URL(url)”

直接作为play或getAudioClip的参数；和前面处理图象的例子一样，

也可以先用“new URL(url)”获取一个URL对象， 再传给 play 或

getAudioClip。如对第一种play(URL)的格式， 也可采用如下的编

程格式：

URL  Audur =null;

        try {

   Audur=new URL("结点URL ");

        } catch(Exception e){

System.out.println("Can't open the URL ");

     }

    play(Audur);

    下面对前述四种从网络上获取并播放声音文件的格式各举一简单的例子，

以作编程时参考：

                         ●程序4   格式一

import java.applet.*;

import java.awt.*;

import java.net.*;

public class sound1 extends Applet

{   AudioClip  loopClip;

    public void paint(Graphics g) {

    String  Audur = "http://www.shu.edu.cn/~xyx/java/Animator/audio/bark.au";

        try {

     play(new URL(Audur));

        } catch(Exception e){}

    }

}

                             ●程序5   格式二

import java.applet.*;

import java.awt.*;

import java.net.*;

public class sound2 extends Applet

{   AudioClip  loopClip;

     public void paint(Graphics g) {

        String  Audur = "http://www.shu.edu.cn/~xyx/java/Animator/audio/";

        try {

           play(new URL(Audur),"bark.au");

        } catch(Exception e){}

    }

}

                             ●程序6   格式三

import java.applet.*;

import java.awt.*;

import java.net.*;

public class sound extends Applet{

   AudioClip  loopClip;

   public void init() {

     String  Audur = "http://www.shu.edu.cn/~xyx/java/Animator/audio/bark.au";

        try {

           loopClip = getAudioClip(new URL(Audur));

        } catch(Exception e){}

    }

   public void paint(Graphics g){

        loopClip.loop();

   }

}

●程序7  格式四

import java.applet.*;

import java.awt.*;

import java.net.*;

public class sound0 extends Applet{

   AudioClip  loopClip;

   URL auur;

   public void init() {

     try {

            auur=new URL("http://www.shu.edu.cn/~xyx/java/Animator/audio/" ;

      }

      catch (MalformedURLException e) {

                System.out.println("Can't open the URL ");

      }

      loopClip = getAudioClip(auur,"bark.au");

    }

   public void paint(Graphics g){

        loopClip.loop();

   }

}


JAVA动态效果（1）表格时钟

作者：ZZH0915

日期：2000-9-19 12:23:46

<body onLoad="startit()">

<script LANGUAGE="JavaScript">

<!--

var isn1=null;

var isn2=false;

today=new Date();

function stopit()

{

if(isn2)

{

clearTimeout(isn1);

}

isn2=false;

}

function startit()

{

stopit();

isnclock();

}

function isnclock()

{

var now=new Date();

var hrs=now.getHours();

var min=now.getMinutes();

var sec=now.getSeconds();

document.clckh.disp1.value=""+((hrs>12)?hrs-12:hrs);

document.clckh.disp2.value=((min<10)?"0":"")+min;

document.clckh.disp3.value=((sec<10)?"0":"")+sec;

document.clckh.disp4.value=(hrs>=12)?"下午":"上午";

isn1=setTimeout("isnclock()",1000);

isn2=true;

}

function isnArray()

{

argnr=isnArray.arguments.length

for(var i=0;

i<argnr;

i++)

{

this[i+1]=isnArray.arguments[i];

}

}

var isnMonths=new isnArray("1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月");

var isnDays=new isnArray("星期一","星期二","星期三","星期四","星期五","星期六","星期日");isnDays[0]="星期日";

//-->

</script>

<form NAME="clckh" onSubmit="0">

<table BORDER="2">

<tr>

<td>时间</td>

<td><div align="center"><center><p>点</td>

<td align="center"><div align="center"><center><p>分</td>

<td align="center"><div align="center"><center><p>秒</td>

<td align="center">　</td>

</tr>

<tr align="center">

<td>　</td>

<td VALIGN="TOP"><input TYPE="text" NAME="disp1" SIZE="2"></td>

<td VALIGN="TOP"><input TYPE="text" NAME="disp2" SIZE="2"></td>

<td VALIGN="TOP"><input TYPE="text" NAME="disp3" SIZE="2"></td>

<td VALIGN="TOP"><input TYPE="text" NAME="disp4" SIZE="3"></td>

</tr>

<script LANGUAGE="JavaScript">

<!--

var thisyear=today.getYear();

if (thisyear< 2000)

thisyear=1900+thisyear;

document.write("<TR><TD>日期</TD><TD COLSPAN=4>"

+isnDays[today.getDay()]+","+isnMonths[today.getMonth()+1]

+","+today.getDate()+"月," +thisyear+"年");

//-->

</script>

</table>

</form>


java跟microsoft转！

作者：电脑

email: fjxmcool@sina.com

日期：2001-4-24 15:58:10

Microsoft刚刚公布Beta版本的.NET架构软体，其中根本不支援 Java，它们在英国的策略联盟经理 Gordon Smille公开向程式设计师们宣称要尽早放弃Java，赶快去学C#。因为一年以後会正式推出的.NET并不支援Java，现在学Java所花的工夫到时候全都会没有用。所以现在数以万计正在学Java的人是在浪费时间。Microsoft 的.NET已经宣布会支援十五种各式各样的程式语言，其中独缺Java。

Microsoft 的策略已经很明显，它是专门冲著Java而来。在现今的网路电子交易平台上，Java已经是业界标准。但是 Microsoft到这个时候还想用这种锁国政策，逼使用者二选一，这样对大家根本没有什么好处。所以这项宣布引起许多分析家和业界的反驳，Gartner Group 的分析师MassimoPezzini指出Microsoft太天真了，电子商务是各行各业必须加紧脚步建置完成的首要任务，大家不可能呆坐一年等这套伟大的.NET正式现身。ATG的技术总监Joseph Chung 说得更传神，他说Microsoft的做法好比是要学生们学任何一种语言，就是不准学英文一样。他说身为一个程式设计师，就是要学会市场上需求最大的程式语言，而目前需求最大的语言就是Java。

依我个人的浅见，大家可以根本不必理会 Microsoft的恐吓。因为它不支援Java，将来如果.NET真的那么强大，真的那么好用，众多厂商自然会让Java支援.NET，就像现在Sun推出的 Windows版JDK一样。而且就程式语言本身，C#根本就是变型版的Java，我常开玩笑说，以後只要把现有的Java程式的副档名改掉，也许再“搜寻/置换“其中某几个关键字(Keyword)，就是C#程式了。所以大家尽管放心，即使是 Microsoft的信徒们，也放心大胆来学Java吧 !学会了Java，等於学会C#，反正即使你现在想学C#，也根本没东西让你学，所以欢迎所有人现在就加入Java的行列。

欢迎转载，但请保留如下内容。


Java混淆编译器（转apusic.com）

作者：fuyi

email: fuyi@apusic.com

日期：2001-6-30 16:58:25

最近试用了几个Java混淆器(Java Obfuscator)，感觉没有一个完全另人满意的，于是想干脆自己写一个得了。翻了几页Java虚拟机规范之后突发奇想，别的混淆器都是在编译好的byte code上做文章，能不能从源码直接编译成经过混淆的class文件呢？就这样花了一个多星期的时间写了一个Java混淆编译器(Java Obfuscator Compiler)。

Q: 什么是混淆器？

A: 由于Java程序运行时是动态连接的，因此编译成的目标文件中包含有符号表，使得Java程序很容易被反编译，混淆器可以打乱class文件中的符号信息，使反向工程变得非常困难。

Q: 现有的混淆器有什么问题？

A: 现有的混淆器都是对编译好的class文件进行混淆，这样就需要编译和混淆两个步骤。并不是所有的符号都需要混淆，如果你开发的是一个类库，或者某些类需要动态装载，那些公共API就必须保留符号不变，这样别人才能使用你的类库。现有的混淆器提供了GUI或脚本的方式来对那些需要保留的符号名称进行配置，如果程序较大时配置工作变得很复杂，而程序一旦修改配置工作又要重新进行。某些混淆器能够调整字节码的顺序，使反编译更加困难，但我经历过混淆之后的程序运行出错的情况。

Q: Java混淆编译器是如何工作的？

A: Java混淆编译器是在Sun JDK中提供的Java编译器(javac)的基础上完成的，修改了代码生成过程，对编译器生成的中间代码进行混淆，最后再生成class文件，这样编译和混淆只需要一个步骤就可以完成。另外可以在源程序中插入符号保留指令来控制哪些符号需要保留，不需要单独的配置。

Q: 如何安装和运行JOC？

A: 下载joc.jar (http://www.apusic.com/product/cpsy.htm-jar joc.jar就可以启动Java混淆编译器，joc的命令行参数和javac完全相同，但增加了一个新的参数-Xobfuscate，它的用法如下：

       -Xobfuscate:<level>

其中<level>指定混淆级别，可以是以下几种级别：

       -Xobfuscate:none        不进行混淆

       -Xobfuscate:private     对所有private访问级别的元素进行混淆

       -Xobfuscate:package     对所有private或package private元素进行混淆

       -Xobfuscate:protected   对所有private, package private, protected元素进行混淆

       -Xobfuscate:public      对所有的元素都进行混淆

       -Xobfuscate:all         相当于-Xobfuscate:public

如果使用-Xobfuscate不带级别参数，则相当于-Xobfuscate:package

Q: 如何使用符号保留指令？

A: 除了在命令行用-Xobfuscate参数控制符号混淆级别外，还可以在源代码中使用符号保留指令来控制那些符号需要保留，符号保留指令是一个Java文档注释指令，可以插入在类和类成员的文档注释中，例如：

       /**

        * This class should preserve.

        * @preserve

        */

       public class Foo {

           /**

            * You can specify which field should be preserved.

            * @preserve

            */

           private int x;

           /**

            * This field is not preserved.

            */

           private int y;

           /**

            * You can also preserve methods.

            * @preserve

            */

           public void hello() {}

           /**

            * This method is not preserved.

            */

           private void collect() {}

       }

如果没有@preserve指令，则根据混淆级别及成员的访问级别来确定符号是否保留。

对于类的符号保留指令可以附带一个保留级别参数，来控制类成员的符号保留，包括：

       @preserve            仅对类名进行保留，类成员的保留根据-Xobfuscate命令行参数决定

       @preserve public     保留所有public成员

       @preserve protected  保留所有public和protected成员

       @preserve package    保留所有public, protected, package private成员

       @preserve private    保留所有成员

       @preserve all        相当于@preserve private

Q: JOC有哪些限制？

A: 不支持分别编译，必须对所有的源文件进行混淆编译。

最后给出一个JOC混淆的效果：

源文件：

import java.awt.event.*;

import javax.swing.*;

public class AboutBox extends JDialog

{

   public AboutBox()

   {

       initForm();

   }

   JPanel panel1 = new JPanel();

   JButton button1 = new JButton();

   JLabel jLabel2 = new JLabel();

   JTextArea jTextArea1 = new JTextArea();

   /**

    * NOTE: The following code is required by the form designer.

    * It can be modified using the form editor.  Do not

    * modify it using the code editor.

    */

   private void initForm()

   {

       this.setDefaultCloseOperation( WindowConstants.DISPOSE_ON_CLOSE );

       this.getContentPane().setLayout( new java.awt.CardLayout());

       this.setModal( true );

       this.setResizable( false );

       this.setTitle( "About..." );

       panel1.setLayout( null );

       button1.setText( "OK" );

       button1.setBounds( 272, 168, 88, 24 );

       panel1.add( button1 );

       jLabel2.setText( "File System Viewer for Swing 1.1.1" );

       jLabel2.setVerticalAlignment( SwingConstants.TOP );

       jLabel2.setBounds( 64, 32, 240, 56 );

       panel1.add( jLabel2 );

       jTextArea1.setFont( new java.awt.Font( "Dialog", 0, 10 ));

       jTextArea1.setLineWrap( true );

       jTextArea1.setOpaque( false );

       jTextArea1.setText( "This computer program is protected by copyright law." );

       jTextArea1.setWrapStyleWord( true );

       jTextArea1.setBounds( 8, 112, 256, 80 );

       panel1.add( jTextArea1 );

       this.getContentPane().add( panel1, "Card1" );

       this.setSize( 376, 228 );

       button1.addActionListener( new java.awt.event.ActionListener(){

               public void actionPerformed( java.awt.event.ActionEvent ev ){

                   button1_actionPerformed( ev );

               }});

   }

   private void button1_actionPerformed(ActionEvent ev)

   {

       this.dispose();

   }

}

经Javac编译后用JAD反编译的结果：

import java.awt.*;

import java.awt.event.ActionEvent;

import java.awt.event.ActionListener;

import javax.swing.*;

import javax.swing.text.JTextComponent;

public class AboutBox extends JDialog

{

   JPanel panel1;

   JButton button1;

   JLabel jLabel2;

   JTextArea jTextArea1;

   public AboutBox()

   {

       panel1 = new JPanel();

       button1 = new JButton();

       jLabel2 = new JLabel();

       jTextArea1 = new JTextArea();

       initForm();

   }

   private void initForm()

   {

       setDefaultCloseOperation(2);

       getContentPane().setLayout(new CardLayout());

       setModal(true);

       setResizable(false);

       setTitle("About...");

       panel1.setLayout(null);

       button1.setText("OK");

       button1.setBounds(272, 168, 88, 24);

       panel1.add(button1);

       jLabel2.setText("File System Viewer for Swing 1.1.1");

       jLabel2.setVerticalAlignment(1);

       jLabel2.setBounds(64, 32, 240, 56);

       panel1.add(jLabel2);

       jTextArea1.setFont(new Font("Dialog", 0, 10));

       jTextArea1.setLineWrap(true);

       jTextArea1.setOpaque(false);

       jTextArea1.setText("This computer program is protected by copyright law.");

       jTextArea1.setWrapStyleWord(true);

       jTextArea1.setBounds(8, 112, 256, 80);

       panel1.add(jTextArea1);

       getContentPane().add(panel1, "Card1");

       setSize(376, 228);

       button1.addActionListener(new ActionListener() {

           public void actionPerformed(ActionEvent actionevent)

           {

               button1_actionPerformed(actionevent);

           }

       });

   }

   private void button1_actionPerformed(ActionEvent actionevent)

   {

       dispose();

   }

}

经JOC混淆编译后用JAD反编译的结果：

import java.awt.*;

import java.awt.event.ActionEvent;

import java.awt.event.ActionListener;

import javax.swing.*;

import javax.swing.text.JTextComponent;

public class AboutBox extends JDialog

{

   JPanel _$1;

   JButton _$2;

   JLabel _$3;

   JTextArea _$4;

   public AboutBox()

   {

       _$1 = new JPanel();

       _$2 = new JButton();

       _$3 = new JLabel();

       _$4 = new JTextArea();

       _$1();

   }

   private void _$1()

   {

       2;

       this;

       JVM INSTR swap ;

       setDefaultCloseOperation();

       getContentPane().setLayout(new CardLayout());

       true;

       this;

       JVM INSTR swap ;

       setModal();

       false;

       this;

       JVM INSTR swap ;

       setResizable();

       "About...";

       this;

       JVM INSTR swap ;

       setTitle();

       _$1.setLayout(null);

       _$2.setText("OK");

       _$2;

       168;

       272;

       JVM INSTR swap ;

       24;

       88;

       JVM INSTR swap ;

       setBounds();

       _$1.add(_$2);

       _$3.setText("File System Viewer for Swing 1.1.1");

       _$3.setVerticalAlignment(1);

       _$3;

       32;

       64;

       JVM INSTR swap ;

       56;

       240;

       JVM INSTR swap ;

       setBounds();

       _$1.add(_$3);

       _$4;

       JVM INSTR new #13  <Class Font>;

       JVM INSTR dup ;

       0;

       "Dialog";

       JVM INSTR swap ;

       10;

       Font();

       setFont();

       _$4.setLineWrap(true);

       _$4.setOpaque(false);

       _$4.setText("This computer program is protected by copyright law.");

       _$4.setWrapStyleWord(true);

       _$4;

       112;

       8;

       JVM INSTR swap ;

       80;

       256;

       JVM INSTR swap ;

       setBounds();

       _$1.add(_$4);

       getContentPane().add(_$1, "Card1");

       376;

       this;

       JVM INSTR swap ;

       228;

       setSize();

       _$2.addActionListener(new IIlIlIIIIlllIIII(this));

       return;

   }

   private void _$1(ActionEvent actionevent)

   {

       dispose();

   }

/*

   static void access$0(AboutBox aboutbox, ActionEvent actionevent)

   {

       actionevent;

       aboutbox;

       JVM INSTR swap ;

       _$1();

       return;

   }

*/

// Unreferenced inner classes:

/* anonymous class */

   final class IIlIlIIIIlllIIII

       implements ActionListener

   {

       public void actionPerformed(ActionEvent actionevent)

       {

           AboutBox.access$0(AboutBox.this, actionevent);

       }

           {

               AboutBox.this;

               this;

               JVM INSTR swap ;

               this$0;

           }

   }

}


Java入门理解构造器 （转）

作者：jeru

日期：2001-3-6 11:26:53

Java入门:理解构造器

译者：东方一蛇

理解构造器--构造器和方法的区别

摘要

要学习Java,你必须理解构造器。因为构造器可以提供许多特殊的方法，这个对于初学者经常混淆。但是，构造器和方法又有很多重要的区别。

原作者：Robert Nielsen 原站：http://www.javaworld.com

我们说构造器是一种方法，就象讲澳大利亚的鸭嘴兽是一种哺育动物。（按：老外喜欢打比喻，我也就照着翻译）。要理解鸭嘴兽，那么先必须理解它和其他哺育动物的区别。同样地，要理解构造器，那么就要了解构造器和方法的区别。所有学习java的人，尤其是对那些要认证考试的，理解构造器是非常重要的。下面将简单介绍一下 ，最后用一个表作了些简单的总结。

功能和作用的不同

构造器是为了创建一个类的实例。这个过程也可以在创建一个对象的时候用到：Platypus p1 = new Platypus();

相反，方法的作用是为了执行java代码。

修饰符，返回值和命名的不同

构造器和方法在下面三个方便的区别：修饰符，返回值，命名。和方法一样，构造器可以有任何访问的修饰： public, protected, private或者没有修饰（通常被package 和 friendly调用）. 不同于方法的是，构造器不能有以下非访问性质的修饰： abstract, final, native, static, 或者 synchronized。

返回类型也是非常重要的。方法能返回任何类型的值或者无返回值（void），构造器没有返回值，也不需要void。

最后，谈谈两者的命名。构造器使用和类相同的名字，而方法则不同。按照习惯，方法通常用小写字母开始，而构造器通常用大写字母开始。构造器通常是一个名词，因为它和类名相同；而方法通常更接近动词，因为它说明一个操作。

"this"的用法

构造器和方法使用关键字this有很大的区别。方法引用this指向正在执行方法的类的实例。静态方法不能使用this关键字，因为静态方法不属于类的实例，所以this也就没有什么东西去指向。构造器的this指向同一个类中，不同参数列表的另外一个构造器，我们看看下面的代码：

public class Platypus {

String name;

Platypus(String input) {

name = input;

}

Platypus() {

this("John/Mary Doe");

}

public static void main(String args[]) {

Platypus p1 = new Platypus("digger");

Platypus p2 = new Platypus();

}

}

在上面的代码中，有2个不同参数列表的构造器。第一个构造器，给类的成员name赋值，第二个构造器，调用第一个构造器，给成员变量name一个初始值 "John/Mary Doe".

在构造器中，如果要使用关键字this,那么，必须放在第一行，如果不这样，将导致一个编译错误。

"super"的用法

构造器和方法，都用关键字super指向超类，但是用的方法不一样。方法用这个关键字去执行被重载的超类中的方法。看下面的例子：

class Mammal {

void getBirthInfo() {

System.out.println("born alive.");

}

}

class Platypus extends Mammal {

void getBirthInfo() {

System.out.println("hatch from eggs");

System.out.print("a mammal normally is ");

super.getBirthInfo();

}

}

在上面的例子中，使用super.getBirthInfo()去调用超类Mammal中被重载的方法。

构造器使用super去调用超类中的构造器。而且这行代码必须放在第一行，否则编译将出错。看下面的例子：

public class SuperClassDemo {

SuperClassDemo() {}

}

class Child extends SuperClassDemo {

Child() {

super();

}

}

在上面这个没有什么实际意义的例子中，构造器 Child()包含了 super,它的作用就是将超类中的构造器SuperClassDemo实例化，并加到 Child类中。

编译器自动加入代码

编译器自动加入代码到构造器，对于这个，java程序员新手可能比较混淆。当我们写一个没有构造器的类，编译的时候，编译器会自动加上一个不带参数的构造器，例如：public class Example {}

编译后将如下代码：

public class Example {

Example() {}

}

在构造器的第一行，没有使用super，那么编译器也会自动加上，例如：

public class TestConstructors {

TestConstructors() {}

}

编译器会加上代码，如下：

public class TestConstructors {

TestConstructors() {

super;

}

}

仔细想一下，就知道下面的代码

public class Example {}

经过会被编译器加代码形如：

public class Example {

Example() {

super;

}

}

继承

构造器是不能被继承的。子类可以继承超类的任何方法。看看下面的代码：

public class Example {

public void sayHi {

system.out.println("Hi");

}

Example() {}

}

public class SubClass extends Example {

}

类 SubClass 自动继承了父类中的sayHi方法，但是，父类中的构造器 Example()却不能被继承。

总结

 主题

 构造器

 方法

 功能

 建立一个类的实例

 java功能语句

 修饰

 不能用bstract, final, native, static,

or synchronized

 能

 返回类型

 没有返回值，没有void

 有返回值，或者void

 命名

 和类名相同；通常为名词，大写开头

 通常代表一个动词的意思，小写开头

 this

 指向同一个类中另外一个构造器，在第一行

 指向当前类的一个实例，不能用于静态方法

 super

 调用父类的构造器，在第一行

 调用父类中一个重载的方法

 继承

 构造器不能被继承

 方法可以被继承

 编译器自动加入一个缺省的构造器

 自动加入（如果没有）

 不支持

 编译器自动加入一个缺省的调用到超类的构造器

 自动加入（如果没有）

 不支持


Java性能的优化（上）(转)

作者：运气

email: webmaster@chinaspx.com

日期：2001-7-10 15:50:57

Java在九十年代中期出现以后，在赢得赞叹的同时，也引来了一些批评。赢得的赞叹主要是Java的跨平台的操作性，即所谓的”Write Once,Run Anywhere”.但由于Java的性能和运行效率同C相比，仍然有很大的差距，从而引来了很多的批评。

对于服务器端的应用程序，由于不大涉及到界面设计和程序的频繁重启，Java的性能问题看似不大明显，从而一些Java的技术，如JSP,Servlet,EJB等在服务器端编程方面得到了很大的应用，但实际上，Java的性能问题在服务器端依然存在。下面我将分四个方面来讨论Java的性能和执行效率以及提高Java性能的一些方法。

一．关于性能的基本知识

1．性能的定义

在我们讨论怎样提高Java的性能之前，我们需要明白“性能“的真正含义。我们一般定义如下五个方面作为评判性能的标准。

1） 运算的性能----哪一个算法的执行性能最好

2） 内存的分配----程序需要分配多少内存，运行时的效率和性能最高。

3） 启动的时间----程序启动需要多少时间。

4） 程序的可伸缩性-----程序在用户负载过重的情况下的表现。

5） 性能的认识------用户怎样才能认识到程序的性能。

对于不同的应用程序，对性能的要求也不同。例如，大部分的应用程序在启动时需要较长的时间，从而对启动时间的要求有所降低；服务器端的应用程序通常都分配有较大的内存空间，所以对内存的要求也有所降低。但是，这并不是所这两方面的性能可以被忽略。其次，算法的性能对于那些把商务逻辑运用到事务性操作的应用程序来讲非常重要。总的来讲，对应用程序的要求将决定对各个性能的优先级。

2．怎样才能提高JAVA的性能

提高JAVA的性能，一般考虑如下的四个主要方面：

（1） 程序设计的方法和模式

一个良好的设计能提高程序的性能，这一点不仅适用于JAVA，也适用也任何的编程语言。因为它充分利用了各种资源，如内存，CPU,高速缓存，对象缓冲池及多线程，从而设计出高性能和可伸缩性强的系统。

当然，为了提高程序的性能而改变原来的设计是比较困难的，但是，程序性能的重要性常常要高于设计上带来的变化。因此，在编程开始之前就应该有一个好的设计模型和方法。

（2） JAVA布署的环境。

JAVA布署的环境就是指用来解释和执行JAVA字节码的技术，一般有如下五种。即解释指令技术(Interpreter Technology)，及时编译的技术(Just In Time Compilier Technology), 适应性优化技术（Adaptive Optimization Technology）, 动态优化，提前编译为机器码的技术（Dynamic Optimization,Ahead Of Time Technology）和编译为机器码的技术（Translator Technology）.

这些技术一般都通过优化线程模型，调整堆和栈的大小来优化JAVA的性能。在考虑提高JAVA的性能时，首先要找到影响JAVA性能的瓶颈（BottleNecks），在确认了设计的合理性后，应该调整JAVA布署的环境，通过改变一些参数来提高JAVA应用程序的性能。具体内容见第二节。

（3） JAVA应用程序的实现

当讨论应用程序的性能问题时，大多数的程序员都会考虑程序的代码，这当然是对的，当更重要的是要找到影响程序性能的瓶颈代码。为了找到这些瓶颈代码，我们一般会使用一些辅助的工具，如Jprobe,Optimizit,Vtune以及一些分析的工具如TowerJ Performance等。这些辅助的工具能跟踪应用程序中执行每个函数或方法所消耗掉的时间，从而改善程序的性能。

(4) 硬件和操作系统

为了提高JAVA应用程序的性能，而采用跟快的CPU和更多的内存，并认为这是提高程序性能的唯一方法，但事实并非如此。实践经验和事实证明，只有遭到了应用程序性能的瓶颈，从而采取适当得方法，如设计模式，布署的环境，操作系统的调整，才是最有效的。

3．程序中通常的性能瓶颈。

所有的应用程序都存在性能瓶颈，为了提高应用程序的性能，就要尽可能的减少程序的瓶颈。以下是在JAVA程序中经常存在的性能瓶颈。

[img]http://www.computerworld.com.cn/htm/app/aprog/01_7_9_3a.gif[/img]

了解了这些瓶颈后，就可以有针对性的减少这些瓶颈，从而提高JAVA应用程序的性能

4. 提高JAVA程序性能的步骤

为了提高JAVA程序的性能，需要遵循如下的六个步骤。

a) 明确对性能的具体要求

在实施一个项目之前，必须要明确该项目对于程序性能的具体要求，如：这个应用程序要支持5000个并发的用户，并且响应时间要在5秒钟之内。但同时也要明白对于性能的要求不应该同对程序的其他要求冲突。

b) 了解当前程序的性能

你应该了解你的应用程序的性能同项目所要求性能之间的差距。通常的指标是单位时间内的处理数和响应时间，有时还会比较CPU和内存的利用率。

c) 找到程序的性能瓶颈

为了发现程序中的性能瓶颈，通常会使用一些分析工具，如：TowerJ Application Performance Analyzer或VTune来察看和分析程序堆栈中各个元素的消耗时间，从而正确的找到并改正引起性能降低的瓶颈代码，从而提高程序的性能。这些工具还能发现诸如过多的异常处理，垃圾回收等潜在的问题。

d) 采取适当的措施来提高性能

找到了引起程序性能降低的瓶颈代码后，我们就可以用前面介绍过的提高性能的四个方面，即设计模式，JAVA代码的实现，布署JAVA的环境和操作系统来提高应用程序的性能。具体内容将在下面的内容中作详细说明。

e) 只进行某一方面的修改来提高性能

一次只改变可能引起性能降低的某一方面，然后观察程序的性能是否有所提高，而不应该一次改变多个方面，因为这样你将不知道到底哪个方面的改变提高了程序的性能，哪个方面没有，即不能知道程序瓶颈在哪。

f) 返回到步骤c,继续作类似的工作，一直达到要求的性能为止。

二． JAVA布署的环境和编译技术

  开发JAVA应用程序时，首先把JAVA的源程序编译为与平台无关的字节码。这些字节码就可以被各种基于JVM的技术所执行。这些技术主要分为两个大类。即基于解释的技术和基于提前编译为本地码的技术。其示意图如下：

[img]http://www.computerworld.com.cn/htm/app/aprog/01_7_9_3b.gif[/img]

具体可分为如下的五类：

a) 解释指令技术

其结构图和执行过程如下：

[img]http://www.computerworld.com.cn/htm/app/aprog/01_7_9_3c.gif[/img]

  JAVA的编译器首先把JAVA源文件编译为字节码。这些字节码对于JAVA虚拟机(JVM)来讲就是机器的指令码。然后，JAVA的解释器不断的循环取出字节码进行解释并执行。

  这样做的优点是可以实现JAVA语言的跨平台，同时生成的字节码也比较紧凑。JAVA的一些优点，如安全性，动态性都得保持；但缺点是省生成的字节码没有经过什么优化，同全部编译好的本地码相比，速度比较慢。

b) 及时编译技术（Just In Time）

    及时编译技术是为了解决指令解释技术效率比较低，速度比较慢的情况下提出的，其结构图如下所示。

[img]http://www.computerworld.com.cn/htm/app/aprog/01_7_9_3d.gif[/img]

其主要变化是在JAVA程序执行之前，又JIT编译器把JAVA的字节码编译为机器码。从而在程序运行时直接执行机器码，而不用对字节码进行解释。同时对代码也进行了部分的优化。

这样做的优点是大大提高了JAVA程序的性能。同时，由于编译的结果并不在程序运行间保存，因此也节约了存储空间了加载程序的时间；缺点是由于JIT编译器对所有的代码都想优化，因此也浪费了很多的时间。

IBM和SUN公司都提供了相关的JIT产品。

c) 适应性优化技术（Adaptive Optimization Technology）

同JIT技术相比，适应性优化技术并不对所有的字节码进行优化。它会跟踪程序运行的成个过程，从而发现需要优化的代码，对代码进行动态的优化。对优化的代码，采取80/20的策略。从理论上讲，程序运行的时间越长，代码就越优化。其结构图如下：

[img]http://www.computerworld.com.cn/htm/app/aprog/01_7_9_3e.gif[/img]

其优点是适应性优化技术充分利用了程序执行时的信息，发行程序的性能瓶颈，从而提高程序的性能；其缺点是在进行优化时可能会选择不当，发而降低了程序的性能。

其主要产品又IBM,SUN的HotSpot.

d) 动态优化，提前编译为机器码的技术（Dynamic Optimization,Ahead Of Time）

动态优化技术充分利用了JAVA源码编译，字节码编译，动态编译和静态编译的技术。其输入时JAVA的原码或字节码，而输出是经过高度优化的可执行代码和个来动态库的混合(Window中是DLL文件，UNIX中是共享库.a .so文件)。其结构如下：

[img]http://www.computerworld.com.cn/htm/app/aprog/01_7_9_3f.gif[/img]

其优点是能大大提高程序的性能；缺点是破坏了JAVA的可移植性，也对JAVA的安全带来了一定的隐患。

其主要产品是TowerJ3.0.


Java性能的优化（下）(转)

作者：运气

email: webmaster@chinaspx.com

日期：2001-7-10 15:53:12

三．优化JAVA程序设计和编码，提高JAVA程序性能的一些方法。

通过使用一些前面介绍过的辅助性工具来找到程序中的瓶颈，然后就可以对瓶颈部分的代码进行优化。一般有两种方案：即优化代码或更改设计方法。我们一般会选择后者，因为不去调用以下代码要比调用一些优化的代码更能提高程序的性能。而一个设计良好的程序能够精简代码，从而提高性能。

下面将提供一些在JAVA程序的设计和编码中，为了能够提高JAVA程序的性能，而经常采用的一些方法和技巧。

1．对象的生成和大小的调整。

JAVA程序设计中一个普遍的问题就是没有好好的利用JAVA语言本身提供的函数，从而常常会生成大量的对象（或实例）。由于系统不仅要花时间生成对象，以后可能还需花时间对这些对象进行垃圾回收和处理。因此，生成过多的对象将会给程序的性能带来很大的影响。

例1：关于String ,StringBuffer，+和append

JAVA语言提供了对于String类型变量的操作。但如果使用不当，会给程序的性能带来影响。如下面的语句：

String name=new String(“HuangWeiFeng”);

System.out.println(name+”is my name”);

看似已经很精简了，其实并非如此。为了生成二进制的代码，要进行如下的步骤和操作。

（1） 生成新的字符串 new String(STR_1);

（2） 复制该字符串。

（3） 加载字符串常量”HuangWeiFeng”(STR_2);

（4） 调用字符串的构架器（Constructor）;

（5） 保存该字符串到数组中（从位置0开始）

（6） 从java.io.PrintStream类中得到静态的out变量

（7） 生成新的字符串缓冲变量new StringBuffer(STR_BUF_1);

（8） 复制该字符串缓冲变量

（9） 调用字符串缓冲的构架器（Constructor）;

（10） 保存该字符串缓冲到数组中（从位置1开始）

（11） 以STR_1为参数，调用字符串缓冲(StringBuffer)类中的append方法。

（12） 加载字符串常量”is my name”(STR_3);

（13） 以STR_3为参数，调用字符串缓冲(StringBuffer)类中的append方法。

（14） 对于STR_BUF_1执行toString命令。

（15） 调用out变量中的println方法，输出结果。

由此可以看出，这两行简单的代码，就生成了STR_1,STR_2,STR_3,STR_4和STR_BUF_1五个对象变量。这些生成的类的实例一般都存放在堆中。堆要对所有类的超类，类的实例进行初始化，同时还要调用类极其每个超类的构架器。而这些操作都是非常消耗系统资源的。因此，对对象的生成进行限制，是完全有必要的。

经修改，上面的代码可以用如下的代码来替换。

StringBuffer name=new StringBuffer(“HuangWeiFeng”);

System.out.println(name.append(“is my name.”).toString());

系统将进行如下的操作。

(1) 生成新的字符串缓冲变量new StringBuffer(STR_BUF_1);

(2) 复制该字符串缓冲变量

(3) 加载字符串常量”HuangWeiFeng”(STR_1);

(4) 调用字符串缓冲的构架器（Constructor）;

(5) 保存该字符串缓冲到数组中（从位置1开始）

(6) 从java.io.PrintStream类中得到静态的out变量

(7) 加载STR_BUF_1;

(8) 加载字符串常量”is my name”(STR_2);

(9) 以STR_2为参数，调用字符串缓冲(StringBuffer)实例中的append方法。

(10) 对于STR_BUF_1执行toString命令。(STR_3)

(11)调用out变量中的println方法，输出结果。

由此可以看出，经过改进后的代码只生成了四个对象变量：STR_1,STR_2,STR_3和STR_BUF_1.你可能觉得少生成一个对象不会对程序的性能有很大的提高。但下面的代码段2的执行速度将是代码段1的2倍。因为代码段1生成了八个对象，而代码段2只生成了四个对象。

代码段1：

String name= new StringBuffer(“HuangWeiFeng”);

name+=”is my”;

name+=”name”;

代码段2：

StringBuffer name=new StringBuffer(“HuangWeiFeng”);

name.append(“is my”);

name.append(“name.”).toString();

因此，充分的利用JAVA提供的库函数来优化程序，对提高JAVA程序的性能时非常重要的.其注意点主要有如下几方面；

（1） 尽可能的使用静态变量（Static Class Variables）

如果类中的变量不会随他的实例而变化，就可以定义为静态变量，从而使他所有的实例都共享这个变量。

例：

public class foo

{

SomeObject so=new SomeObject();

}

就可以定义为：

public class foo

{

static SomeObject so=new SomeObject();

}

（2） 不要对已生成的对象作过多的改变。

对于一些类(如：String类)来讲，宁愿在重新生成一个新的对象实例，而不应该修改已经生成的对象实例。

例：

String name=”Huang”;

name=”Wei”;

name=”Feng”;

上述代码生成了三个String类型的对象实例。而前两个马上就需要系统进行垃圾回收处理。如果要对字符串进行连接的操作，性能将得更差。因为系统将不得为此生成更多得临时变量。如上例1所示。

（3） 生成对象时，要分配给它合理的空间和大小

JAVA中的很多类都有它的默认的空间分配大小。对于StringBuffer类来讲，默认的分配空间大小是16个字符。如果在程序中使用StringBuffer的空间大小不是16个字符，那么就必须进行正确的初始化。

（4） 避免生成不太使用或生命周期短的对象或变量。

对于这种情况，因该定义一个对象缓冲池。以为管理一个对象缓冲池的开销要比频繁的生成和回收对象的开销小的多。

（5） 只在对象作用范围内进行初始化。

JAVA允许在代码的任何地方定义和初始化对象。这样，就可以只在对象作用的范围内进行初始化。从而节约系统的开销。

例：

SomeObject so=new SomeObject();

If(x==1) then

{

Foo=so.getXX();

}

可以修改为：

if(x==1) then

{

SomeObject so=new SomeObject();

Foo=so.getXX();

}

2．异常(Exceptions)

JAVA语言中提供了try/catch来发方便用户捕捉异常，进行异常的处理。但是如果使用不当，也会给JAVA程序的性能带来影响。因此，要注意以下两点。

(1) 避免对应用程序的逻辑使用try/catch

如果可以用if,while等逻辑语句来处理，那么就尽可能的不用try/catch语句

(2) 重用异常

在必须要进行异常的处理时，要尽可能的重用已经存在的异常对象。以为在异常的处理中，生成一个异常对象要消耗掉大部分的时间。

3. 线程(Threading)

一个高性能的应用程序中一般都会用到线程。因为线程能充分利用系统的资源。在其他线程因为等待硬盘或网络读写而 时，程序能继续处理和运行。但是对线程运用不当，也会影响程序的性能。

例2：正确使用Vector类

Vector主要用来保存各种类型的对象（包括相同类型和不同类型的对象）。但是在一些情况下使用会给程序带来性能上的影响。这主要是由Vector类的两个特点所决定的。第一，Vector提供了线程的安全保护功能。即使Vector类中的许多方法同步。但是如果你已经确认你的应用程序是单线程，这些方法的同步就完全不必要了。第二，在Vector查找存储的各种对象时，常常要花很多的时间进行类型的匹配。而当这些对象都是同一类型时，这些匹配就完全不必要了。因此，有必要设计一个单线程的，保存特定类型对象的类或集合来替代Vector类.用来替换的程序如下（StringVector.java）：

public class StringVector

{

private String [] data;

private int count;

public StringVector() { this(10); // default size is 10 }

public StringVector(int initialSize)

{

data = new String[initialSize];

}

public void add(String str)

{

// ignore null strings

if(str == null) { return; }

ensureCapacity(count + 1);

data[count++] = str;

}

private void ensureCapacity(int minCapacity)

{

int oldCapacity = data.length;

if (minCapacity > oldCapacity)

{

String oldData[] = data;

int newCapacity = oldCapacity * 2;

data = new String[newCapacity];

System.arraycopy(oldData, 0, data, 0, count);

}

}

public void remove(String str)

{

if(str == null) { return // ignore null str }

for(int i = 0; i < count; i++)

{

// check for a match

if(data[i].equals(str))

{

System.arraycopy(data,i+1,data,i,count-1); // copy data

// allow previously valid array element be gc'd

data[--count] = null;

return;

}

}

}

public final String getStringAt(int index) {

if(index < 0) { return null; }

else if(index > count)

{

return null; // index is > # strings

}

else { return data[index]; // index is good }

}

/* * * * * * * * * * * * * * * *StringVector.java * * * * * * * * * * * * * * * * */

因此，代码：

Vector Strings=new Vector();

Strings.add(“One”);

Strings.add(“Two”);

String Second=(String)Strings.elementAt(1);

可以用如下的代码替换：

StringVector Strings=new StringVector();

Strings.add(“One”);

Strings.add(“Two”);

String Second=Strings.getStringAt(1);

这样就可以通过优化线程来提高JAVA程序的性能。用于测试的程序如下（TestCollection.java）:

import java.util.Vector;

public class TestCollection

{

public static void main(String args [])

{

TestCollection collect = new TestCollection();

if(args.length == 0)

{

System.out.println(

"Usage: java TestCollection [ vector | stringvector ]");

System.exit(1);

}

if(args[0].equals("vector"))

{

Vector store = new Vector();

long start = System.currentTimeMillis();

for(int i = 0; i < 1000000; i++)

{

store.addElement("string");

}

long finish = System.currentTimeMillis();

System.out.println((finish-start));

start = System.currentTimeMillis();

for(int i = 0; i < 1000000; i++)

{

String result = (String)store.elementAt(i);

}

finish = System.currentTimeMillis();

System.out.println((finish-start));

}

else if(args[0].equals("stringvector"))

{

StringVector store = new StringVector();

long start = System.currentTimeMillis();

for(int i = 0; i < 1000000; i++) { store.add("string"); }

long finish = System.currentTimeMillis();

System.out.println((finish-start));

start = System.currentTimeMillis();

for(int i = 0; i < 1000000; i++) {

String result = store.getStringAt(i);

}

finish = System.currentTimeMillis();

System.out.println((finish-start));

}

}

}

/* * * * * * * * * * * * * * * *TestCollection.java * * * * * * * * * * * * * * * * */

测试的结果如下（假设标准的时间为１，越小性能越好）：

[img]http://www.computerworld.com.cn/htm/app/aprog/01_7_9_4.gif[/img]

关于线程的操作，要注意如下几个方面。

(1) 防止过多的同步

如上所示，不必要的同步常常会造成程序性能的下降。因此，如果程序是单线程，则一定不要使用同步。

(2) 同步方法而不要同步整个代码段

      对某个方法或函数进行同步比对整个代码段进行同步的性能要好。

(3) 对每个对象使用多”锁”的机制来增大并发。

一般每个对象都只有一个”锁”，这就表明如果两个线程执行一个对象的两个不同的同步方法时，会发生”死锁”。即使这两个方法并不共享任何资源。为了避免这个问题，可以对一个对象实行”多锁”的机制。如下所示：

class foo

{

private static int var1;

private static Object lock1=new Object();

private static int var2;

private static Object lock2=new Object();

public static void increment1()

{

synchronized(lock1)

{

var1++;

}

}

public static void increment2()

{

synchronized(lock2)

{

var2++;

}

}

}

4．输入和输出(I/O)

输入和输出包括很多方面，但涉及最多的是对硬盘，网络或数据库的读写操作。对于读写操作，又分为有缓存和没有缓存的；对于数据库的操作，又可以有多种类型的JDBC驱动器可以选择。但无论怎样，都会给程序的性能带来影响。因此，需要注意如下几点：

(1) 使用输入输出缓冲

      尽可能的多使用缓存。但如果要经常对缓存进行刷新（flush）,则建议不要使用缓存。

(2) 输出流(Output Stream)和Unicode字符串

      当时用Output Stream和Unicode字符串时，Write类的开销比较大。因为它要实现Unicode到字节(byte)的转换.因此，如果可能的话,在使用Write类之前就实现转换或用OutputStream类代替Writer类来使用。

(3) 当需序列化时使用transient

      当序列化一个类或对象时，对于那些原子类型（atomic）或可以重建的原素要表识为transient类型。这样就不用每一次都进行序列化。如果这些序列化的对象要在网络上传输，这一小小的改变对性能会有很大的提高。

(4) 使用高速缓存（Cache）

      对于那些经常要使用而又不大变化的对象或数据，可以把它存储在高速缓存中。这样就可以提高访问的速度。这一点对于从数据库中返回的结果集尤其重要。

(5) 使用速度快的JDBC驱动器（Driver）

      JAVA对访问数据库提供了四种方法。这其中有两种是JDBC驱动器。一种是用JAVA外包的本地驱动器；另一种是完全的JAVA驱动器。具体要使用哪一种得根据JAVA布署的环境和应用程序本身来定。

5.一些其他的经验和技巧

(1) 使用局部变量

(2) 避免在同一个类中动过调用函数或方法(get或set)来设置或调用变量。

(3) 避免在循环中生成同一个变量或调用同一个函数（参数变量也一样）

(4) 尽可能的使用static,final,private等关键字

(5) 当复制大量数据时，使用System.arraycopy()命令。


JAVA学习，是一条漫长的道路(转贴)

作者：gmliao

email: gmliao@sina.com

日期：9/6/2001 10:13:00 AM

作者:蔡学镛  发表日期:2001-08-13  浏览人次 :378

我在Java 1.0正式问世前就开始学习Java，这么多年过去了，到现在我的Java学习历程还没有停过。我阅读原文书，研究原始码，撰写程序，自认为走得扎实，不奢望一步登天。像我这样老式的学习方式，显然和现在的快餐主义背道而驰。从许多读者的来信和学生的反应中，我发现大多数的人对于Java的学习历程都差不多是：因为公司需要使用Java来进行服务器的计画，所以急急忙忙地学习Java语言，然后就开始使用J2EE的API，开始写起程序来了。如此急就章的学习方式，程序员基础能力根本就不够，对于对象导向精髓不能掌握，对于Java语言内部的运作机制毫无所悉，对API的整体连贯性懵懵懂懂。

当然，我们也不好因此责怪程序员，毕竟软件技术变动得太快。公司不可能给程序员足够的训练之后才开始做计画。程序员一下子被指派使用A技术，还没弄懂A技术是怎么回事，又被指派使用B技术，而且都是缝缝补补的方式边学边用，每次都像是全新的开始，遑论技术能量的累积。

我很庆幸的是，我不太有这样的困扰。因为我是资讯工程系出身（而且我大学时上课一向很认真），所以理论基础稳固，学习新技术对我来说不是难事。我就读大学时，周遭许多同学都瞧不起数据结构、程序语言、操作系统这些所谓「学院派」的课程，以为这些课程一点都不实用。他们认为到了外面公司，这些信息科系所学的一切都派不上用场，「只要会Visual Basic和数据库就够了」。这种偏差的心态，恐怕会使得他们在知识经济时代吃足了苦头。

另外还有一派同学很瞧不起程序设计工作，他们告诉我，像我这样会写程序的人，未来进了社会「还不是被他们这些走管理的人踩在脚下」。所以，他们很轻忽理工课程的学习，甚至还有人相当热衷「成功学」，认为这是迈向成功的快捷方式，却因此把学校的课业弃之不顾。我不敢相信有人竟然如此地本末倒置。

前一类的人太过于短视近利，后一类的人太过于好高骛远。我一直很不能理解这些人的想法为什么会这样，或许是因为社会环境的风气使然。我很庆幸我到目前还没被社会的大染缸给玷污了（最好这辈子都不要）。我不认为我的学习方式是一种典范，但是一路走来，倒也颇有进展。许多读者来信问我的学习历程，虽然我个人的学习方式不见得适用于每个人，但或许还有一些参考价值（特别是对于那些有志进入信息行业的年轻学子），我想透过本文简短地叙述一下。

我一向是采用先深后广（也称为Bottom-Up，Deep-First）的学习方式。比方说，当我在学A技术的时候，学到一半发现需要B技术的基础，我会到书局找出一两本B技术的书，然后把A先搁着，开始看起B技术的书。甚至，我在技术书籍上看到不太熟悉的英文句构时，我会找出一本英文文法书详细读过。这种先深后广的学习方式，适合学生时代全面地自我能力提升，但不适合业界人士。试想，老板要你开发的ERP系统已经延迟了，你怎有空研究J2EE原文书中的英文文法。先深后广的好处是，学习很扎实；缺点是有时候会偏离主题太多。有一次我发现我原本是要学某软件技术，几次「先深后广」下来，我居然看起老子的道德经了。

在技术上，我一直都是一个喜新厌旧的人，很少有软件技术能让我持续研究半年以上，我几乎每隔几个月就要换一次领域。Java 能让我持续这么久，也正是因为Java的领域广。透过Java，我的技术视野变开阔了。这些年来，我换过的 Java 相关领域包括了：虚拟机器、数据库、企业运算、多媒体、2D/3D图学、网络.…..等。

我的学习完全是兴趣导向的，所以压力并不大。因为有兴趣，所以我会很想充分理解一切细节。又因为理解，所以许多原本片片断断的知识都可以渐渐互相融会贯通，累积技术能量，理论和实务之间的藩篱被打破了，学习效率倍增。

我多年来的学习触觉很敏锐，我常常会抢先一步学好有前瞻性的技术。比方说，Java还在beta时、UML还在0.8时、XML还在draft时，我都已经透过网络下载技术文件回来每天抱着猛读了。而在Java、UML、XML当红之后，我已经差不多把这些技术都摸熟了。

至于该学什么技术，我的判断方式是以技术的优劣来决定。优秀而有独到之处的技术是我的最爱，虽然这类的技术不见得会在市场上胜出，但学习这些技术所得到的启发，对于技术能量的累积与能力的提升会有相当大的助益。至于技术差，但市场需求甚殷者，我还是懒得碰。（好吧！我承认我曾因为市场需求的缘故而学过MFC。越清楚MFC的技术细节，越是讨厌它，这真是个不堪回首的经验。）

我通常只看英文技术资料，毕竟大部分第一手的技术信息都是以英文来传播。所以我很早就开始阅读英文技术资料。读英文技术资料的好处是，就算没有学到书中的专业知识，至少也累积培养了英文阅读能力，我一直都是抱着这样的态度。一开始是正襟危坐的看英文技术书籍，字典、翻译机随侍在侧；几年下来，现在是躺着看、趴着看、很随性地看英文技术书籍，因为看英文技术书籍变成一种习惯了。现在，我可以用很快的速度吸收英文技术书籍的知识（有人叫我「吃书的机器」，我把这称号当作是一种恭维）。

近年来，我花在写程序的时间不多，因为时间对我来说很宝贵，而写程序很浪费时间。对初学者来说，大量地写程序是必要的，但过了某个阶段之后，写程序所带来的技术能力成长已经到了极限，还不如多花一点时间看书，学新技术和新观念。

我从国小时期开始学习写程序，迄今已有近十八年的时间；采取上述的方式密集学习，迄今也有近十年的光景。迩来数年，我接触的领域越来越广，而且学习速度正在加快，我认为是以前那些努力植下的根苗开始成长了。看看现在的我，或许你会觉得羡慕，但回顾这段学习的岁月，何尝不是一条漫长的道路。


java学习体悟 作者： huli·yesky (转载)

作者：wflm

email: wflm@263.net

日期：2001-5-27 15:02:20

java 出现在我们眼前,时间还很短,当他还只是一个半成品的时候,我们就被告知,java将成为软件发展的规范和希望. 然后,java出现了,jdk的出现,我们发现其实java并不习惯,因为我们已经适应了window下的visual编程,对于jdk这种编程方法已经很不喜欢,即使visual j++的出现,所增加的还不过是一个编辑环境.而且大家所熟悉的visual j++所支持的功能很少,可以说除了支持ActiveX之外,就是很纯粹的jdk1.1.这样的环境和功能,是根本不适合开发大型软件的所以,随着visual j++的兴起,java applet仿佛成为java唯一的功能了,几乎所有介绍java的书籍,网站,都被javaapplet充斥在许多人眼中,小应用程序是java唯一能做的事情了.可能还不如javascrip好用,当年如同耶稣降临似的java,仿佛成了鸡肋.用处的确不大.

    但我初次接触java,我就被java 面向对象的思想,舒服的代码形式所吸引,对于面向对象的编程,大家都已经耳熟能详,c++好像成为面向对象的标准语言,而且delphi,vb,pb好像都挂上了面向对象的边了.但在我看来,delphi在设计上完全的面向对象,而且在他的内部,他很好的处理了对象之间的关系,但是在大家使用delphi的编程过程中,可以说已经被borland公司的对象束缚,例如面向对象有个很重要的应用,就是内部操作和外部界面的分离,也就是数据和界面的分离,但是使用过delphi的人都知道,这个在delphi中是很难的,因为你对界面的直接操作就直接写到了界面对象相应的操作中.很多人问我,为什么VC中为什么要初始化成一个Doc类,一个View类呢?我想,这就是没有理解面向对象的思想,为什么呢,就是界面实现和数据操作的分离嘛,可是很多人几乎不使用Doc类,把所有的数据操作都写到View中,因为这样免去了在View中操作Doc类的貌似复杂不必要的操作.至于vb,pb,我想大部分人在编程中,实在是很难体会面向对象的.因为这些语言和工具,本来就很不适合来实现对象操作.

    如果你用过smalltalk语言,你会发现,什么是面向对象,所有的语句,甚至赋值,条件等等基本语句都是对象,学习smalltalk语言,很烦,即使你说自己对C++都是非常的熟练精通,为什么呢,因为我们绝大部分人使用C++,都没有真正体会到面向对象,当然在作项目的时候,也很难真正使用面向对象的方法.很希望大家有机会,学习使用一下smalltalk,因为这种语言能真正逼迫你体会面向对象.

    说了这么多面向对象,有点跑题,现在我回到java,为什么我一再的强调面向对象呢,因为smalltalk毕竟不是真正高效率的编程语言,我用过很多编程工具,可以说几乎所有的我都作过东西,但是我看来,只有java的面向对象,编程方法,才是我最喜欢的.使用java编程,你会感觉到什么是对象,感觉到自己编写的程序原来也可以这样的有条理.这样的舒服.我和java斑竹lolo曾经讨论过这个感受,他也很有同样的感受.

    但是,大家或许会说,java很好,但是他能干什么呢? 除了applet他能做什么?对于这个问题,我要告诉大家,他能做的东西很多,java到底能做什么，我使用java时间不是很长，但是我感到高兴的是，在这有限的时间里面，我接触的东西或许已经足够让我对java的前景产生了很大的兴趣和信心。

    如果大家能到Sun公司主页上看看，会发现太多的东西可能是你听都没有听过的，jini,jmx,java3d,servlet,rmi……太多了，如此多的新技术，而且功能都是前所未有，起码是非常不同的。在国外，java程序员已经超过其他语言程序员，其中IBM公司就使用自己开发的visualage for java为法国开发了全国铁路管理系统。这个项目说明了java绝对不是只能开发小东西的，只能是applet，当然java的网络支持或许是所有编程语言中最强的了，他在网络的应用功能远远超过任何语言，大家或许很喜欢而且很熟练CGI的开发，但是servlet的功能却更标准化，使用java会让你感受到舒适。

    jini可能很多人不知道是什么，我也不很清楚，但是自从lolo和我说了jini的思想的时候，我认为他，起码是他的思想绝对将在不远的将来充满我们生活的空间。

    今天我看了一本xml方面的书，再一次感觉到了java的无处不在。xml的思想很新，非常的有发展前途，记得lolo曾经告诉我，xml将会得到极大的发展。我期待这样的一天。

    我的方向是多媒体，所以我对例如3维效果非常感兴趣，以前我只能通过VC,C++ builder来编写出3维图形效果，如果将这种效果放在网上，即编写出支持3D效果的applet却不能，当然也有折中的方法，但那样作出来的效果很不好，也就是不是真正的3D图形，都是线条组成的，没有openGL作出来的那种虚拟效果，不过很快我就发现java突破了这个限制，sun公司的jdk for 3d(opengl)实现了对opengl的支持，作出来的效果虽然还达不到使用VC编写的那样复杂，但是再网上已经是非常难得了。

    前些天我访问http://www.opengl.org

    java对于声音，图象的处理，或许还不能达到专业效果，单对于他来说已经是非常不错了。

    今天一个朋友的提醒让我想起来。java语言的跨平台是大家都公认的，但是不知道是否注意到使用java编程，有一个和别的编程语言很不一样的地方。就是虽然都是C++语言，但是VC和C++ builder之间的共享性受到了很大的限制，虽然C++ builder提供从MFC到自身支持的过程，但是具体实现上很难。

    面向对象技术最核心的就是可重用性--reuseable，可是C++，object pascal等等都把这个优势降低了。可是java却做到了这一点，无论你使用jdk,jbuilder,Vj,visualagefor java，你都能做到对象之间的充分共享。甚至你可以在编程的时候，有时候选择这个工具，有时候选择那个工具，但是，这一切自由都不妨碍这个软件的整体统一性。如果你觉得Visual j++功能太差，可是jdk的编辑支持又不能忍受，你完全可以将jdk的class让vj来调试编译。你觉得visualagefor java的select控件对数据库的支持很好，你也可以将它导出，让jbuilder来调用，一切太自由了，自由意味着解放，你不必拘束于一种软件，你不必说我不会Visualage

    而只了解jdk，这一切，在java里面都不是问题。java的reuseable，实在是让我高兴。


Java与C／C++的区别(转)

作者：lindows

日期：2001-3-1 10:49:57

    Java与C／C++的区别

           （OCR by Lindows）

    首先应该清楚，Java是由C++发展而来的，它保留了c++的大部分内容，类似于c++，

但句法更清晰，规模更小，更易学。它是在对多种程序设计语言进行了深入细致研究的

基础上，据弃了其他语言的不足之处，从根本上解决了c++的固有缺陷，而产生的一种

新的完全面向对象的语言。

    Java和c++的相似之处多于不同之处，但两种语言问几处主要的不同使得Java更容易

学习，并且编程环境更为简单。

     因篇幅所限，这里不能完全列出不同之处，仅列出比较显著的区别：

    1．指针

    Java无指针，并且增添了自动的内存管理功能，从而有效地防

止了c／c++语言中指针操作失误，如指针悬空所造成的系统崩溃。

比w操作返回一对象的引用，类似于c++中的引用；在c++中，

new返回一个对象的指针。在Java中无指针，不会遇到下面这样的

语句：

  Mywork—＞Mywork();

  没有指针的程序无法访问不属于它的内存，消除了在c++

中—些常见的错误，这有利于Java程序的安全。

    2．多重继承

    c++支持多重继承，这是c++的一个特征，它允许多父类派

生一个类。尽管多重继承功能很强，但使用复杂，而且会引起许多麻

烦，编译程序实现它也很不容易。Java不支持多重继承，但允许一个

类继承多个接口(界面)，实现了c++多重继承的功能，又避免了c++的

许多缺陷。

  3．数据类型及类

    住Java‘fJ，使用了个能和整数湖蝴的布尔类型(bo01ean)，它仪有二个值：true或false。

Java是完全面向对象的语言，所有函数和变量部必须是类的一部分。除了基本数据类型(如

控型初浮点型)之外，其余的都作为类对象，包括中和数组。对象将数据(Proper山s)和方法

(meth。d)结合起来，把它们封装在类中，这样每个对象都可实现自己的特点和行为。而c++

允许将函数和变量定义为全局的。

    此外，Java中取消了c／c++中的结构和联合，使编译程序更简捷。

    4．自动内存管理

    Java程序中所有的对象都是用new操作符建立在堆栈上，这个操作符类似于c++的

new操作符。下面的语句由一个类Readkcy建立了一个对象，然后调用该对象的work方

法：

    配『9dLey my们rk；

    m?work＝new Readkey()

    m!work．work()g

    这个简单的代码片段突出了Ja调和c十十的一些主要差别，在c十十中，语句Read—

key”ywork在堆栈结构上建立了一个Readkcy的实例。在JaM中不支持基于堆栈的对象，

所以这条语句建立了一个叫”ywork的变量，该变量中存有对象Readk。7的引用。

    Javs自动进行无用内存回收操作，不需要程序员进行删除。而c十十中必须由程序贝释

放内存资源，增加了程序设计者的负扔。Java中当一个对象不被再用到时，无用内存回收器

将给它加上标签以示删除。无用内存回收器在后台运行，利用空闲时间工作。

    5．操作符重载

    Java不支持操作符重载。操作符重载被认为是c十十的突出特征，在Java中虽然类大

体上pJ以实现这样的功能，但操作符重载的方便性仍然丢失了不少。Java语言不支持操作

符重载是为了保持Java语言尽可能简单。

  6．预处理功能

  Java允预处理器．因此不支持预处理功能。c／c十十在编译过程中都有一个预编泽阶

段，即众所周知的预处理器。预处理器为开发人员提供了方便，但增加丁编译的复杂性。JaM

没有预处理器，但它提供的引入语句(i”Port)与c十十预处理器的功能类似。

  7.  Java不支持缺省函数参数，而c十十支持。

    在c中，代码组织在函数中，函数可以访问程序的全局变量。c十十增加了类，提供了类

算法，该算法是与类相连的函数，c十十类方法与Java类方法十分相似，然而，由于c十十仍

然支持c，所以不能阻止c十十开发人员使用函数，结果函数和方法混合使用使得程序比较

混乱。

    Java没有函数，作为一个比c十十更纯的面向对象的语言，Java强迫开发人员把所有

例行程序包括在类中，事实上，用方法实现例行程序可激励开发人员更好地组织编码。

8    字符串

    c和c十十不支持字符串变量，在c和c十十程序中使用Null终止符代表字符串的结

束，在Java中字符串是用类对象(strinR和stringBuffer)来实现的，这些类对象是Java语言

的核心，用类对象实现字符串有以下几个优点：

    (1)在整个系统中建立字符串和访问字符串元素的方法是一致的；

    (2)J3阳字符串类是作为Java语言的一部分定义的，而不是作为外加的延伸部分；

    (3)Java字符串执行运行时检空，可帮助排除一些运行时发生的错误；

    (4)可对字符串用“十”进行连接操作。

    9“goto语句

    “可怕”的goto语句是c和c++的“遗物”，它是该语言技术上的合法部分，引用goto

语句引起了程序结构的混乱，不易理解，goto语句子要用于无条件转移子程序和多结构分

支技术。

    鉴于以广理由，Java不提供goto语句，它虽然指定goto作为关键字，但不支持它的使

用，使程序简洁易读。

    l0．类型转换

    在c和c十十中有时出现数据类型的隐含转换，这就涉及了自动强制类型转换问题。例

如，在c十十中可将一浮点值赋予整型变量，并去掉其尾数。Java不支持c十十中的自动强

制类型转换，如果需要，必须由程序显式进行强制类型转换。

    11.其它

    (1)break和continue后可加标号，以便跳出它标识的块。如：

inTlJ；

char a;

 outing;

{for(i＝：i＜5；i十十)

for(j＝0;j＜3i j++)

{if((a＝(char)system.in.read())

break outing;

}}

    2)析构函数为void finalize映(){}

Java并不保证一定调用该函数，因此不能依赖该函数来保证所写代码的正常运行。

    (3)用packa8e将有关联的类组织在一起。例如，将自己编写的类组织成packaBe；

Packa8e mycla5se55

    (4)用extaNs表示继承(派生)关系，如；

class ne订—clas5一name extends oN—class—name 47

    (5)虚函数用ab列ra瓤修饰，而不用v加ual。

    (6)提供丁一个创建完全虚类(界面)的方法；

    Public interface intl『facenamEt／／abstract mGthods)

利用界面可以为虚类定义协议，而不关心具体实施。一个类可以实现多个界面，同一个界面

也可以被多个类实现。实现界面使用imNemen贿，如：

    public classname implmen比int6rfacena毗4)

    (7)异常。用于捕获例外事件，增强系统容错能力

try{／／可能产生例外的代码

}catch(exceptionType name){

//处理

其中exceptionType表示异常类型，如Arith

Java语言入门

袁小春　刘春阳　　著

王钢　主审

鸣谢

前言

第一章　Java的产生与特点.............................................................袁小春

第二章　Java程序编译与运行环境.......................................................袁小春

第三章　Java程序设计基础 ............................................................袁小春

第四章　Java应用程序的基本结构.......................................................袁小春

第五章　Java的类.....................................................................刘春阳

第六章　Java图形用户接口.............................................................刘春阳

第七章　Java的多线程.................................................................袁小春

第八章　Java的“异常”...............................................................刘春阳

第九章　Java的输入输出操作...........................................................袁小春

第十章　Java的网络操作...............................................................袁小春

第十一章　Java应用程序开发实例.......................................................袁小春


Java语言中字符的处理

作者：jeru

email: jeru@163.net

日期：2000-12-26 10:46:54

山西省网络管理中心任军

----摘要：本文主要讨论了Java语言中字符的特殊表达形式，尤其是中文信息的表达处理，阐述了字符处理的关键是要将十六位Unicode字符，转换为本地下层平台，也就是运行Java虚拟处理机的平台能够理解的字符形式。

----关键词：Java、字符、8位、16位、Unicode字符集

----Java是一种编程语言、一个运行系统、一套开发工具和一个应用程序编程界面（API）。Java建立在C++的熟悉、有用的特征之上，而取消了C++的复杂的、危险的和多余的元素。它是一个更安全、更简单、更容易使用的语言。

1、Java的字符表达

----Java语言和C语言对字符进行了互不相同的描述，Java使用16位的Unicode字符集(该标准描述了许多语言的各种不同字符),因此Java字符是一个16位的无符号整数，字符变量用来存放单个字符，而不是完整的字符串。

----一个字符(character)，就是单个字母(letter)，许多字母构成一个单词，一组单词组成句子，以此类推。但是对于含有诸如中文信息的字符，就不是那么简单了。

----Java的基本的char类型被定义成无符号的16位，它是Java中唯一的一个无符号类型。使用16位表达字符的主要原因是要让Java能够支持任何Unicode字符，因此而使得Java适用于描述或显示任何被Unicode支持的语言，可移植性也就会更好。但是，能够支持某种语言的字符串显示，和能够正确打印某种语言的字符串，常常是两个不同的问题。由于Oak（Java最初的代号）开发组的主要环境是Unix系统和某些源于Unix的系统，所以对开发人员来说，最为方便实用的字符集是ISOLatin-1。相应地，这一开发组就带有Unix遗传性，也就导致了Java的I/O系统在很大程度上以Unix的流概念为模型，而在Unix系统中，每一种I/O设备都是用一串8比特的流来表示。这种在I/O系统方面取模于Unix的做法，使得Java语言拥有16位的Java字符，而却只有8位的输入设备，这样就给Java带来了些不足。因此在任何一处Java字符串按8位来读入或写出的地方，都得有一小段程序代码，被称为"劈(hack)"，来将8位的字符映射成为16位Unicode，或将16位的Unicode劈成8位字符。

2、问题及解决

----我们要实现从一个文件读取信息，尤其是读取含有中文信息的文件，并将读取到的信息显示在屏幕上，一般我们使用FileInputStream函数打开文件、readChar函数读入字符。如下：

 import java.io.*;

 public class rf{

      public static void main(String args[]) {

           FileInputStream fis;

           DataInputStream dis;

           char c;

           try {

               fis = new FileInputStream("xinxi.txt");

               dis = new DataInputStream(fis);

               while (true) {

                   c = dis.readChar();

                   System.out.print(c);

                   System.out.flush();

                   if (c == '\n') break;

               }

               fis.close();

           } catch (Exception e) { }

           System.exit(0);

     }

 }

----但是事实上，运行这一程序，所能得到的输出结果是一堆无用的乱码。不能正确输出xinxi.txt文件内容，其原因是readChar函数读入的是16位的Unicode字符，而System.out.print却将其当作八位的ISO latin-1字符输出。

----Java 1.1版本引入了一套全新的Readers和Writers接口来处理字符。我们可以利用InputStreamReader类而不是DataInputStream来处理文件。修改上面的程序如下：

import java.io.*;

 public class rf {

      public static void main(String args[]) {

           FileInputStream fis;

           InputStreamReader irs;

           char ch;

           try {

               fis = new FileInputStream("xinxi.txt");

               irs = new InputStreamReader(fis);

               while (true) {

                   ch = (char) irs.read();

                   System.out.print(c);

                   System.out.flush();

                   if (ch == '\n') break;

               }

               fis.close();

           } catch (Exception e) { }

           System.exit(0);

     }

 }

----这样才能正确输出xinxi.txt中的文本(尤其是中文信息)。另外，当xinxi.txt文件来自不同的机器，即来自不同操作平台（或汉字内码不同）的机器，比如： 文件来自客户端（客户端上传文件给服务器），而读取文中信息的操作由服务器端执行。如果用上面的程序来实现这一功能，就有可能仍然不能得到正确的结果。其原因就是输入编码转换失败，我们还需要进行如下的改动：

     ......

        int c1;

    int j=0;

        StringBuffer str=new StringBuffer();

        char lll[][]= new char[20][500];

        String ll="";

     try {

          fis = new FileInputStream("fname.txt");

          irs = new InputStreamReader(fis);

              c1=irs.read(lll[1],0,50);

          while (lll[1][j]!=' ') {

               str.append(lll[1][j]);

               j=j+1;

              }

                   ll=str.toString();

           System.out.println(ll);

       } catch (IOException e) {

              System.out.println(e.toString());}

        ......

----这样，输出的结果就正确了。当然，上面的程序是不完整的，只是说明了一下解决的方法。

----总之，Java语言中字符处理，尤其是中文信息的处理，比较特殊。在Java中，字符处理的关键是要将十六位Unicode字符，转换为本地下层平台也就是运行Java虚拟处理机的平台能够理解的字符形式。


java在Linux下的web应用(二）转

作者：blackwhites

日期：2000-10-25 18:03:37

gnujsp1.0.0在RedHat下基于apache&nbsnbsp;jserv的安装&nbsnbsp;

首先确认你已经安装了apache&nbsnbsp;jserv，如果你还没有安装apache&nbsnbsp;jserv,请参考在《RedHat下&nbsnbsp;

安装apache&nbsnbsp;jserv&nbsnbsp;1.1.2》一文。&nbsnbsp;

默认jserv安装在/usr/local/jserv下，jserv.conf在/usr/local/jserv/conf下&nbsnbsp;

一、需要的软件&nbsnbsp;

从http://www.klomp.org/gnujsp/

二、&nbsnbsp;

#cp&nbsnbsp;gnujsp-1.0.0.tar.gz&nbsnbsp;/usr/local&nbsnbsp;

#tar&nbsnbsp;xvzf&nbsnbsp;gnujsp-1.0.0.tar.gz&nbsnbsp;

#ln&nbsnbsp;-s&nbsnbsp;gnujsp-1.0.0&nbsnbsp;gnujsp&nbsnbsp;

#cd&nbsnbsp;/usr/local/jserv/etc&nbsnbsp;

#cp&nbsnbsp;zone.properties&nbsnbsp;alph.properties&nbsnbsp;

#vi&nbsnbsp;alph.properties修改alph.properties加入：&nbsnbsp;

#&nbsnbsp;List&nbsnbsp;of&nbsnbsp;Repositories&nbsnbsp;

#######################&nbsnbsp;

repositories=/usr/local/gnujsp-1.0.0/lib/gnujsp10.jar&nbsnbsp;

repositories=/usr/local/gnujsp-1.0.0/lib/servlet-2.0-plus.jar&nbsnbsp;

repositories=/your/jdbcbeans/path#你的javabean的路径&nbsnbsp;

#repositories=/home/heyl/lib/java/Acme.jar&nbsnbsp;

#repositories=/usr/local/webl-3.0/lib/WebL.jar&nbsnbsp;

#&nbsnbsp;Classloader&nbsnbsp;parameters&nbsnbsp;

#########################&nbsnbsp;

autoreload.classes=true&nbsnbsp;

#&nbsnbsp;Enable&nbsnbsp;servlet&nbsnbsp;resourced&nbsnbsp;autoreloading&nbsnbsp;(properties&nbsnbsp;and&nbsnbsp;other&nbsnbsp;

#&nbsnbsp;loaded&nbsnbsp;resources)&nbsnbsp;

autoreload.file=true&nbsnbsp;

...&nbsnbsp;

#&nbsnbsp;Servlet&nbsnbsp;Aliases&nbsnbsp;

##################&nbsnbsp;

#&nbsnbsp;Note:&nbsnbsp;if&nbsnbsp;using&nbsnbsp;webl&nbsnbsp;or&nbsnbsp;Acme&nbsnbsp;stuff,&nbsnbsp;add&nbsnbsp;the&nbsnbsp;jars&nbsnbsp;here&nbsnbsp;too&nbsnbsp;

#&nbsnbsp;if&nbsnbsp;you&nbsnbsp;added&nbsnbsp;them&nbsnbsp;to&nbsnbsp;the&nbsnbsp;repositories&nbsnbsp;above&nbsnbsp;(alph)&nbsnbsp;

#&nbsnbsp;In&nbsnbsp;my&nbsnbsp;real&nbsnbsp;settings&nbsnbsp;I&nbsnbsp;added:&nbsnbsp;usepackages=true,language=de,country=de&nbsnbsp;

#&nbsnbsp;but&nbsnbsp;I&nbsnbsp;guess&nbsnbsp;you&nbsnbsp;may&nbsnbsp;prefer&nbsnbsp;the&nbsnbsp;default&nbsnbsp;locale&nbsnbsp;:-)&nbsnbsp;(alph)&nbsnbsp;

servlet.gnujsp.code=org.gjt.jsp.JspServlet&nbsnbsp;

servlet.gnujsp.initArgs=checkclass=true,pagebase=/usr/local/www/doc,&nbsnbsp;

scratchdir=/var/local/apache/jsp,debug=true,compiler=builtin-javac&nbsnbsp;

-classpath&nbsnbsp;%classpath%:%scratchdir%:/usr/local/gnujsp-1.0.0/lib/servlet-2.0-plus.jar:&nbsnbsp;

/usr/local/gnujsp-1.0.0/lib/gnujsp10.jar:/your/jdbcbeans/path&nbsnbsp;

-d&nbsnbsp;%scratchdir%&nbsnbsp;-deprecation&nbsnbsp;%source%&nbsnbsp;

#其中$scratchdir为jsp文件生成的java和class文件的目录&nbsnbsp;

存盘&nbsnbsp;

三、&nbsnbsp;

#vi&nbsnbsp;jserv.properties加入&nbsnbsp;

wrapper.classpath=/usr/lib/jdk/lib/tools.zip&nbsnbsp;

wrapper.classpath=/usr/local/jserv/lib/ApacheJServ.jar&nbsnbsp;

wrapper.classpath=/usr/local/JSDK/lib/jsdk.jar&nbsnbsp;

......&nbsnbsp;

zones=root,alph&nbsnbsp;

......&nbsnbsp;

alph.properties=/usr/local/apache-1.3.6/conf/alph.properties&nbsnbsp;

......&nbsnbsp;

log.file=/usr/local/apache/logs/jserv.log#日志文件&nbsnbsp;

四、在/usr/local/jserv/etc/jserv.conf中加进&nbsnbsp;

ApJServMount&nbsnbsp;/alph&nbsnbsp;/alph&nbsnbsp;

Action&nbsnbsp;jsp-handler&nbsnbsp;/servlet/gnujsp&nbsnbsp;

AddHandler&nbsnbsp;jsp-handler&nbsnbsp;jsp&nbsnbsp;

五、&nbsnbsp;

启动你的apache&nbsnbsp;

把/usr/local/gnujsp/examples下的文件拷贝到web发布目录下，用浏览器调用&nbsnbsp;

http://yourserver/dir/date.jsp&nbsnbsp;

http://yourserver/dir/hello.jap&nbsnbsp;

http://yourserver/dir/snoop.jsp&nbsnbsp;

怎么样，结果出来了没有呢？&nbsnbsp;

然后你自己改动这些文件，加入中文测试，和servlet在redhat下一样，测试表明gnujsp也&nbsnbsp;

没有中文显示的问题。&nbsnbsp;

相关servlet/jsp信息请到java.sun.com查阅相关信息&nbsnbsp;


java在Linux下的web应用(三)

作者：blackwhites

日期：2000-10-25 18:04:29

cocoon 1.7.4在RedHat下基于apache jserv的安装

cocoon是xml在apache上的实现，作为一种web发布系统。本文介绍cocoon在RedHat下的安装。

首先确认你已经安装了apache jserv，如果你还没有安装apache jserv,请参考本站

《RedHat下安装apache jserv 1.1.2》一文。

默认jserv安装在/usr/local/jserv下，jserv.conf在/usr/local/jserv/conf下

1、需要的软件

http://xml.apache.org

2、

#cp Cocoon-1.7.4.tar.gz /usr/local

#tar xvzf Cocoon-1.7.4.tar.gz

#ln -s cocoon-1.7.4 cocoon

3、更改配置文件

#cd /usr/local/jserv/etc

#vi jserv.properties加入

wrapper.classpath=/usr/local/jdk/lib/tools.jar

wrapper.classpath=/usr/local/cocoon/bin/cocoon.jar

wrapper.classpath=/usr/local/cocoon/lib/xerces_1_0_3.jar

wrapper.classpath=/usr/local/cocoon/lib/xalan_1_0_1.jar

wrapper.classpath=/usr/local/cocoon/;lib/fop_0_12_1.jar

wrapper.classpath=/usr/local/JSDK/lib/jsdk.jar

#注意这里不能用/usr/local/cocoon/lib/servlet_2_2.jar因为jserv不支持servlet2.2

存盘

#vi zone.properties加入

servlet.org.apache.cocoon.Cocoon.initArgs=/usr/local/cocoon/conf/cocoon.properties

存盘

#vi jserv.conf加进

Action cocoon /servlets/org.apache.cocoon.Cocoon

AddHandler cocoon xml

存盘

4、测试

重新启动你的apache

通过浏览器调用http://yourserver/Cocoon.xml

把/usr/local/cocoon/samples下的文件拷贝到web发布目录下，

#cp -R /usr/locao/cocoon/samples /usr/local/apache/htdocs/xml

用浏览器调用

http://yourserve/xml/index.xml

5、中文问题

更改index.xml

然后把一些信息改成中文，调用，应该是出现乱码，怎么解决呢？如下方法解决：

更改/usr/local/cocoon/src/apache/cocoon/formatter/下的文件，

把"text/html"更改成"text/html;charset=GB2312"

然后运行/usr/local/cocoon/build.sh，这个命令将编译src,

结果文件cocoon.jar在/usr/local/build/下

然后你再更改/usr/local/jserv/etc/jserv.properties中的

wrapper.classpath=/usr/local/cocoon/bin/cocoon.jar

为wrapper.classpath=/usr/local/cocoon/build/cocoon.jar

重新启动apache，看见了你的中文了吗？呵呵就这么简单

对xml/java/sevlet等相关信息请参考相关网站


java在Linux下的web应用(一)(转

作者：blackwhites

日期：2000-10-25 18:00:05

RedHat下安装apache jserv 1.1.2

虽然apache jserv的开发小组现在都从事tomcat的开发，而且apache jserv只支持servlet 2.0

的标准，并且要加其他的软件(gnujsp)才能支持jsp;但是apache jserv在现阶段比tomcat稳定，

因此现在apache jserv+gnujsp比较适合应用于web。如果想使用servlet2.2和jsp1.1的话，请

使用tomcat，参看tomcat的安装。本文介绍apache jserv 1.1.1在RedHat下的安装。

一、需要的软件：

jdk_1.2.2从http://java.sun.com

apache_1.3.12从http://www.apache.org

apache jserv-1.1.1从java.apache.org下载

jsdk2.0(只能是2.0)从http://java.sun.com

二、安装过程

请先安装好jdk，具体可参考jdk安装，注意设置好CLASSPATH和PATH

拷贝apache,apache jserv源码包到/tmp下

#tar xvzf apache_1.3.12.tar.gz

#tar xvzf ApacheJServ-1.1.2.tar.gz

1、静态编译，即编译进apache

#cd apache_1.3.12

#./configure --prefix=/usr/local/apache

配置apache jserv编译参数

#cd ../ApacheJServ-1.1.2

#./configure

--prefix=/usr/local/jserv

--with-apache-src=../apache_1.3.12

--with-jdk-home=/usr/local/jdk

--with-java-platform=2

--with-JSDK=/usr/local/JSDK/lib/jsdk.jar

#make

#make install

编译apache和apache jserv

#cd ../apache_1.3.12

#./configure

--prefix=/usr/local/apache

--activate-module=src/modules/jserv/libjserv.a

#make

#make install

运行#/www/bin/httpd -l检查一下mod_jserv是否编译进apache了

    2、动态编译(DSO模式）

#cd apache_1.3.12

#./configure

--prefix=/usr/local/apache

--enable-shared=max

#make

#make install

配置apache jserv编译参数

#cd ../ApacheJServ-1.1.2

#./configure

--prefix=/usr/local/jserv

--with-apxs=/usr/local/apache/bin/apxs

--with-jdk-home=/usr/local/jdk

--with-java-platform=2

--with-JSDK=/usr/local/JSDK/lib/jsdk.jar

#make

#make install

如果你想使用mod_ssl请加入--with-EAPI选项

三、更改配置文件

在/www/conf/httpd.conf中加入

Include /usr/local/jserv/etc/jserv.conf

如果你用DSO模式安装的话，请把jserv.conf中

LoadModule jserv_module /usr/local/jserv/libexec/mod_jserv.so前面的注释去掉

启动apache

用浏览器测试http://yourserver/servlets/IsItWorking

看见了Yes,It's working!了吗？你成功了！！

你可以更改jserv.conf中的

SetHandler jserv-status

order deny,allow

deny from all

加入你的信任主机allow from xxxx

这样在信任主机上通过http://yourserver/jserv/(注意后面这个"/"不能少）可以看到你的

apache jserv的配置信息。

接着在/usr/local/jserv/servlets下放入你自己的servlet class实验一下效果吧，测试结果

表明在redhat6.2下没有中文显示问题，一切ok!

其他关于servlet zone的设置、load balance等等请参考apache jserv文档和java.apache.org网站


Java正则表达式详解(上)

作者：歪书生

email: netnerd@163.com

日期：8/3/2001 5:36:11 PM

[TABLE][TR][TD]如果你曾经用过Perl或任何其他内建正则表达式支持的语言，你一定知道用正则表达式处理文本和匹配模式是多么简单。如果你不熟悉这个术语，那么“正则表达式”（Regular Expression）就是一个字符构成的串，它定义了一个用来搜索匹配字符串的模式。 [/TD][/TR][/TABLE][TABLE][TR][TD]许多语言，包括Perl、PHP、Python、JavaScript和JScript，都支持用正则表达式处理文本，一些文本编辑器用正则表达式实现高级“搜索-替换”功能。那么Java又怎样呢？本文写作时，一个包含了用正则表达式进行文本处理的Java规范需求（Specification Request）已经得到认可，你可以期待在JDK的下一版本中看到它。 [/TD][/TR][/TABLE][TABLE][TR][TD]然而，如果现在就需要使用正则表达式，又该怎么办呢？你可以从Apache.org下载源代码开放的Jakarta-ORO库。本文接下来的内容先简要地介绍正则表达式的入门知识，然后以Jakarta-ORO API为例介绍如何使用正则表达式。 [/TD][/TR][/TABLE][TABLE][TR][TD][B]一、正则表达式基础知识[/B] [/TD][/TR][/TABLE][TABLE][TR][TD]我们先从简单的开始。假设你要搜索一个包含字符“cat”的字符串，搜索用的正则表达式就是“cat”。如果搜索对大小写不敏感，单词“catalog”、“Catherine”、“sophisticated”都可以匹配。也就是说： [/TD][/TR][/TABLE][TABLE][TR][TD][img]http://www.ccw.com.cn/htm/app/aprog/01_7_31_4_a.jpg[/img][/TD][/TR][/TABLE][TABLE][TR][TD][B]1.1 句点符号[/B] [/TD][/TR][/TABLE][TABLE][TR][TD]假设你在玩英文拼字游戏，想要找出三个字母的单词，而且这些单词必须以“t”字母开头，以“n”字母结束。另外，假设有一本英文字典，你可以用正则表达式搜索它的全部内容。要构造出这个正则表达式，你可以使用一个通配符——句点符号“.”。这样，完整的表达式就是“t.n”，它匹配“tan”、“ten”、“tin”和“ton”，还匹配“t#n”、“tpn”甚至“t n”，还有其他许多无意义的组合。这是因为句点符号匹配所有字符，包括空格、Tab字符甚至换行符： [/TD][/TR][/TABLE][TABLE][TR][TD][img]http://www.ccw.com.cn/htm/app/aprog/01_7_31_4_b.jpg[/img] [/TD][/TR][/TABLE][TABLE][TR][TD][B]1.2 方括号符号[/B] [/TD][/TR][/TABLE][TABLE][TR][TD]为了解决句点符号匹配范围过于广泛这一问题，你可以在方括号（“[]”）里面指定看来有意义的字符。此时，只有方括号里面指定的字符才参与匹配。也就是说，正则表达式“t[aeio]n”只匹配“tan”、“Ten”、“tin”和“ton”。但“Toon”不匹配，因为在方括号之内你只能匹配单个字符： [/TD][/TR][/TABLE][TABLE][TR][TD][img]http://www.ccw.com.cn/htm/app/aprog/01_7_31_4_c.jpg[/img][/TD][/TR][/TABLE][TABLE][TR][TD][B]1.3 “或”符号[/B] [/TD][/TR][/TABLE][TABLE][TR][TD]如果除了上面匹配的所有单词之外，你还想要匹配“toon”，那么，你可以使用“|”操作符。“|”操作符的基本意义就是“或”运算。要匹配“toon”，使用“t(a|e|i|o|oo)n”正则表达式。这里不能使用方扩号，因为方括号只允许匹配单个字符；这里必须使用圆括号“()”。圆括号还可以用来分组，具体请参见后面介绍。 [/TD][/TR][/TABLE][TABLE][TR][TD][img]http://www.ccw.com.cn/htm/app/aprog/01_7_31_4_d.jpg[/img] [/TD][/TR][/TABLE][TABLE][TR][TD][B]1.4 表示匹配次数的符号[/B] [/TD][/TR][/TABLE][TABLE][TR][TD]表一显示了表示匹配次数的符号，这些符号用来确定紧靠该符号左边的符号出现的次数： [/TD][/TR][/TABLE][TABLE][TR][TD]

[img]http://www.ccw.com.cn/htm/app/aprog/01_7_31_4n.jpg[/img][/TD][/TR][/TABLE][TABLE][TR][TD][/TD][/TR][/TABLE][TABLE][TR][TD]

[img]http://www.ccw.com.cn/htm/app/aprog/01_7_31_4a.gif[/img][/TD][/TR][/TABLE][TABLE][TR][TD]

图一：匹配所有123-12-1234形式的社会安全号码[/TD][/TR][/TABLE][TABLE][TR][TD]假设进行搜索的时候，你希望连字符号可以出现，也可以不出现——即，999-99-9999和999999999都属于正确的格式。这时，你可以在连字符号后面加上“？”数量限定符号，如图二所示： [/TD][/TR][/TABLE][TABLE][TR][TD]

[img]http://www.ccw.com.cn/htm/app/aprog/01_7_31_4b.gif[/img][/TD][/TR][/TABLE][TABLE][TR][TD]

图二：匹配所有123-12-1234和123121234形式的社会安全号码[/TD][/TR][/TABLE][TABLE][TR][TD]下面我们再来看另外一个例子。美国汽车牌照的一种格式是四个数字加上二个字母。它的正则表达式前面是数字部分“[0-9]{4}”，再加上字母部分“[A-Z]{2}”。图三显示了完整的正则表达式。 [/TD][/TR][/TABLE][TABLE][TR][TD]

[img]http://www.ccw.com.cn/htm/app/aprog/01_7_31_4c.gif[/img][/TD][/TR][/TABLE][TABLE][TR][TD]

图三：匹配典型的美国汽车牌照号码，如8836KV[/TD][/TR][/TABLE][TABLE][TR][TD]1.5 “否”符号 [/TD][/TR][/TABLE][TABLE][TR][TD]“^”符号称为“否”符号。如果用在方括号内，“^”表示不想要匹配的字符。例如，图四的正则表达式匹配所有单词，但以“X”字母开头的单词除外。 [/TD][/TR][/TABLE][TABLE][TR][TD]

[img]http://www.ccw.com.cn/htm/app/aprog/01_7_31_4d.gif[/img][/TD][/TR][/TABLE][TABLE][TR][TD]

图四：匹配所有单词，但“X”开头的除外[/TD][/TR][/TABLE][TABLE][TR][TD]1.6 圆括号和空白符号 [/TD][/TR][/TABLE][TABLE][TR][TD]假设要从格式为“June 26, 1951”的生日日期中提取出月份部分，用来匹配该日期的正则表达式可以如图五所示： [/TD][/TR][/TABLE][TABLE][TR][TD]

[img]http://www.ccw.com.cn/htm/app/aprog/01_7_31_4e.gif[/img][/TD][/TR][/TABLE][TABLE][TR][TD]

图五：匹配所有Moth DD,YYYY格式的日期[/TD][/TR][/TABLE][TABLE][TR][TD]新出现的“\s”符号是空白符号，匹配所有的空白字符，包括Tab字符。如果字符串正确匹配，接下来如何提取出月份部分呢？只需在月份周围加上一个圆括号创建一个组，然后用ORO API（本文后面详细讨论）提取出它的值。修改后的正则表达式如图六所示： [/TD][/TR][/TABLE][TABLE][TR][TD]

[img]http://www.ccw.com.cn/htm/app/aprog/01_7_31_4f.gif[/img][/TD][/TR][/TABLE][TABLE][TR][TD]

图六：匹配所有Month DD,YYYY格式的日期，定义月份值为第一个组[/TD][/TR][/TABLE][TABLE][TR][TD][B]1.7 其它符号[/B] [/TD][/TR][/TABLE][TABLE][TR][TD]为简便起见，你可以使用一些为常见正则表达式创建的快捷符号。如表二所示： [/TD][/TR][/TABLE][TABLE][TR][TD]表二：常用符号 [/TD][/TR][/TABLE][TABLE][TR][TD]

[img]http://www.ccw.com.cn/htm/app/aprog/01_7_31_4o.jpg[/img][/TD][/TR][/TABLE][TABLE][TR][TD]例如，在前面社会安全号码的例子中，所有出现“[0-9]”的地方我们都可以使用“\d”。修改后的正则表达式如图七所示： [/TD][/TR][/TABLE][TABLE][TR][TD]

[img]http://www.ccw.com.cn/htm/app/aprog/01_7_31_4g.gif[/img][/TD][/TR][/TABLE][TABLE][TR][TD]

图七：匹配所有123-12-1234格式的社会安全号码[/TD][/TR][/TABLE][TABLE][TR][TD][B]二、Jakarta-ORO库[/B] [/TD][/TR][/TABLE][TABLE][TR][TD]有许多源代码开放的正则表达式库可供Java程序员使用，而且它们中的许多支持Perl 5兼容的正则表达式语法。我在这里选用的是Jakarta-ORO正则表达式库，它是最全面的正则表达式API之一，而且它与Perl 5正则表达式完全兼容。另外，它也是优化得最好的API之一。 [/TD][/TR][/TABLE][TABLE][TR][TD]Jakarta-ORO库以前叫做OROMatcher，Daniel Savarese大方地把它赠送给了Jakarta Project。你可以按照本文最后参考资源的说明下载它。 [/TD][/TR][/TABLE][TABLE][TR][TD]我首先将简要介绍使用Jakarta-ORO库时你必须创建和访问的对象，然后介绍如何使用Jakarta-ORO API。 [/TD][/TR][/TABLE][TABLE][TR][TD][B]▲ PatternCompiler对象[/B] [/TD][/TR][/TABLE][TABLE][TR][TD]首先，创建一个Perl5Compiler类的实例，并把它赋值给PatternCompiler接口对象。Perl5Compiler是PatternCompiler接口的一个实现，允许你把正则表达式编译成用来匹配的Pattern对象。 [/TD][/TR][/TABLE]


Java正则表达式详解(下)

作者：歪书生

email: netnerd@163.com

日期：8/3/2001 5:25:39 PM

[TABLE][TR][TD][B]三、应用实例[/B] [/TD][/TR][/TABLE][TABLE][TR][TD]下面我们来看看Jakarta-ORO库的一些应用实例。 [/TD][/TR][/TABLE][TABLE][TR][TD][B]3.1 日志文件处理[/B] [/TD][/TR][/TABLE][TABLE][TR][TD]任务：分析一个Web服务器日志文件，确定每一个用户花在网站上的时间。在典型的BEA WebLogic日志文件中，日志记录的格式如下： [/TD][/TR][/TABLE][TABLE][TR][TD][img]http://www.ccw.com.cn/htm/app/aprog/01_7_31_4_i.jpg[/img][/TD][/TR][/TABLE][TABLE][TR][TD][/TD][/TR][/TABLE][TABLE][TR][TD]首先我们来看看IP地址。IP地址有4个字节构成，每一个字节的值在0到255之间，各个字节通过一个句点分隔。因此，IP地址中的每一个字节有至少一个、最多三个数字。图八显示了为IP地址编写的正则表达式： [/TD][/TR][/TABLE][TABLE][TR][TD]

[img]http://www.ccw.com.cn/htm/app/aprog/01_7_31_4h.gif[/img][/TD][/TR][/TABLE][TABLE][TR][TD]

图八：匹配IP地址[/TD][/TR][/TABLE][TABLE][TR][TD]IP地址中的句点字符必须进行转义处理（前面加上“\”），因为IP地址中的句点具有它本来的含义，而不是采用正则表达式语法中的特殊含义。句点在正则表达式中的特殊含义本文前面已经介绍。 [/TD][/TR][/TABLE][TABLE][TR][TD]日志记录的时间部分由一对方括号包围。你可以按照如下思路提取出方括号里面的所有内容：首先搜索起始方括号字符（“[”），提取出所有不超过结束方括号字符（“]”）的内容，向前寻找直至找到结束方括号字符。图九显示了这部分的正则表达式。 [/TD][/TR][/TABLE][TABLE][TR][TD]

[img]http://www.ccw.com.cn/htm/app/aprog/01_7_31_4i.gif[/img][/TD][/TR][/TABLE][TABLE][TR][TD]

图九：匹配至少一个字符，直至找到“]”[/TD][/TR][/TABLE][TABLE][TR][TD]现在，把上述两个正则表达式加上分组符号（圆括号）后合并成单个表达式，这样就可以从日志记录提取出IP地址和时间。注意，为了匹配“- -”（但不提取它），正则表达式中间加入了“\s-\s-\s”。完整的正则表达式如图十所示。 [/TD][/TR][/TABLE][TABLE][TR][TD]

[img]http://www.ccw.com.cn/htm/app/aprog/01_7_31_4j.gif[/img][/TD][/TR][/TABLE][TABLE][TR][TD]

图十：匹配IP地址和时间标记[/TD][/TR][/TABLE][TABLE][TR][TD]现在正则表达式已经编写完毕，接下来可以编写使用正则表达式库的Java代码了。 [/TD][/TR][/TABLE][TABLE][TR][TD]为使用Jakarta-ORO库，首先创建正则表达式字符串和待分析的日志记录字符串： [/TD][/TR][/TABLE][TABLE][TR][TD][img]http://www.ccw.com.cn/htm/app/aprog/01_7_31_4_j.jpg[/img][/TD][/TR][/TABLE][TABLE][TR][TD]这里使用的正则表达式与图十的正则表达式差不多完全相同，但有一点例外：在Java中，你必须对每一个向前的斜杠（“\”）进行转义处理。图十不是Java的表示形式，所以我们要在每个“\”前面加上一个“\”以免出现编译错误。遗憾的是，转义处理过程很容易出现错误，所以应该小心谨慎。你可以首先输入未经转义处理的正则表达式，然后从左到右依次把每一个“\”替换成“\\”。如果要复检，你可以试着把它输出到屏幕上。 [/TD][/TR][/TABLE][TABLE][TR][TD]初始化字符串之后，实例化PatternCompiler对象，用PatternCompiler编译正则表达式创建一个Pattern对象： [/TD][/TR][/TABLE][TABLE][TR][TD][img]http://www.ccw.com.cn/htm/app/aprog/01_7_31_4_k.jpg[/img] [/TD][/TR][/TABLE][TABLE][TR][TD]现在，创建PatternMatcher对象，调用PatternMatcher接口的contain()方法检查匹配情况： [/TD][/TR][/TABLE][TABLE][TR][TD][img]http://www.ccw.com.cn/htm/app/aprog/01_7_31_4_l.jpg[/img] [/TD][/TR][/TABLE][TABLE][TR][TD]接下来，利用PatternMatcher接口返回的MatchResult对象，输出匹配的组。由于logEntry字符串包含匹配的内容，你可以看到类如下面的输出： [/TD][/TR][/TABLE][TABLE][TR][TD][img]http://www.ccw.com.cn/htm/app/aprog/01_7_31_4_m.jpg[/img][/TD][/TR][/TABLE][TABLE][TR][TD][B]3.2 HTML处理实例一[/B] [/TD][/TR][/TABLE][TABLE][TR][TD]下面一个任务是分析HTML页面内FONT标记的所有属性。HTML页面内典型的FONT标记如下所示： [/TD][/TR][/TABLE][TABLE][TR][TD][img]http://www.ccw.com.cn/htm/app/aprog/01_7_31_4_n.jpg[/img] [/TD][/TR][/TABLE][TABLE][TR][TD]程序将按照如下形式，输出每一个FONT标记的属性： [/TD][/TR][/TABLE][TABLE][TR][TD][img]http://www.ccw.com.cn/htm/app/aprog/01_7_31_4_o.jpg[/img] [/TD][/TR][/TABLE][TABLE][TR][TD]在这种情况下，我建议你使用两个正则表达式。第一个如图十一所示，它从字体标记提取出“"face="Arial, Serif" size="+2" color="red"”。 [/TD][/TR][/TABLE][TABLE][TR][TD]

[img]http://www.ccw.com.cn/htm/app/aprog/01_7_31_4k.gif[/img][/TD][/TR][/TABLE][TABLE][TR][TD]

图十一：匹配FONT标记的所有属性[/TD][/TR][/TABLE][TABLE][TR][TD]第二个正则表达式如图十二所示，它把各个属性分割成名字-值对。 [/TD][/TR][/TABLE][TABLE][TR][TD]

[img]http://www.ccw.com.cn/htm/app/aprog/01_7_31_4l.gif[/img][/TD][/TR][/TABLE][TABLE][TR][TD]

图十二：匹配单个属性，并把它分割成名字-值对[/TD][/TR][/TABLE][TABLE][TR][TD]分割结果为： [/TD][/TR][/TABLE][TABLE][TR][TD][img]http://www.ccw.com.cn/htm/app/aprog/01_7_31_4_p.jpg[/img][/TD][/TR][/TABLE][TABLE][TR][TD]现在我们来看看完成这个任务的Java代码。首先创建两个正则表达式字符串，用Perl5Compiler把它们编译成Pattern对象。编译正则表达式的时候，指定Perl5Compiler.CASE_INSENSITIVE_MASK选项，使得匹配操作不区分大小写。 [/TD][/TR][/TABLE][TABLE][TR][TD]接下来，创建一个执行匹配操作的Perl5Matcher对象。 [/TD][/TR][/TABLE][TABLE][TR][TD][img]http://www.ccw.com.cn/htm/app/aprog/01_7_31_4_q.jpg[/img] [/TD][/TR][/TABLE][TABLE][TR][TD]假设有一个String类型的变量html，它代表了HTML文件中的一行内容。如果html字符串包含FONT标记，匹配器将返回true。此时，你可以用匹配器对象返回的MatchResult对象获得第一个组，它包含了FONT的所有属性： [/TD][/TR][/TABLE][TABLE][TR][TD][img]http://www.ccw.com.cn/htm/app/aprog/01_7_31_4_r.jpg[/img] [/TD][/TR][/TABLE][TABLE][TR][TD]接下来创建一个PatternMatcherInput对象。这个对象允许你从最后一次匹配的位置开始继续进行匹配操作，因此，它很适合于提取FONT标记内属性的名字-值对。创建PatternMatcherInput对象，以参数形式传入待匹配的字符串。然后，用匹配器实例提取出每一个FONT的属性。这通过指定PatternMatcherInput对象（而不是字符串对象）为参数，反复地调用PatternMatcher对象的contains()方法完成。PatternMatcherInput对象之中的每一次迭代将把它内部的指针向前移动，下一次检测将从前一次匹配位置的后面开始。 [/TD][/TR][/TABLE][TABLE][TR][TD]本例的输出结果如下： [/TD][/TR][/TABLE][TABLE][TR][TD][img]http://www.ccw.com.cn/htm/app/aprog/01_7_31_4_s.jpg[/img] [/TD][/TR][/TABLE][TABLE][TR][TD][B]3.3 HTML处理实例二[/B] [/TD][/TR][/TABLE][TABLE][TR][TD]下面我们来看看另一个处理HTML的例子。这一次，我们假定Web服务器从widgets.acme.com移到了newserver.acme.com。现在你要修改一些页面中的链接： [/TD][/TR][/TABLE][TABLE][TR][TD][img]http://www.ccw.com.cn/htm/app/aprog/01_7_31_4_t.jpg[/img] [/TD][/TR][/TABLE][TABLE][TR][TD]执行这个搜索的正则表达式如图十三所示： [/TD][/TR][/TABLE][TABLE][TR][TD]

[img]http://www.ccw.com.cn/htm/app/aprog/01_7_31_4m.gif[/img][/TD][/TR][/TABLE][TABLE][TR][TD]

图十三：匹配修改前的链接[/TD][/TR][/TABLE][TABLE][TR][TD]如果能够匹配这个正则表达式，你可以用下面的内容替换图十三的链接： [/TD][/TR][/TABLE][TABLE][TR][TD][url href=http://newserver.acme.com/interface.html#$1][/url][img]http://www.ccw.com.cn/htm/app/aprog/01_7_31_4_u.jpg[/img][/TD][/TR][/TABLE][TABLE][TR][TD][/TD][/TR][/TABLE][TABLE][TR][TD]现在，返回Java。就象前面我们所做的那样，你必须创建测试字符串，创建把正则表达式编译到Pattern对象所必需的对象，以及创建一个PatternMatcher对象：[img]http://www.ccw.com.cn/htm/app/aprog/01_7_31_4_v.jpg[/img] [/TD][/TR][/TABLE][TABLE][TR][TD]接下来，用com.oroinc.text.regex包Util类的substitute()静态方法进行替换，输出结果字符串： [/TD][/TR][/TABLE][TABLE][TR][TD][img]http://www.ccw.com.cn/htm/app/aprog/01_7_31_4_w.jpg[/img] [/TD][/TR][/TABLE][TABLE][TR][TD]Util.substitute()方法的语法如下： [/TD][/TR][/TABLE][TABLE][TR][TD][img]http://www.ccw.com.cn/htm/app/aprog/01_7_31_4_x.jpg[/img] [/TD][/TR][/TABLE][TABLE][TR][TD]这个调用的前两个参数是以前创建的PatternMatcher和Pattern对象。第三个参数是一个Substiution对象，它决定了替换操作如何进行。本例使用的是Perl5Substitution对象，它能够进行Perl5风格的替换。第四个参数是想要进行替换操作的字符串，最后一个参数允许指定是否替换模式的所有匹配子串（Util.SUBSTITUTE_ALL），或只替换指定的次数。 [/TD][/TR][/TABLE][TABLE][TR][TD][B]【结束语】[/B]在这篇文章中，我为你介绍了正则表达式的强大功能。只要正确运用，正则表达式能够在字符串提取和文本修改中起到很大的作用。另外，我还介绍了如何在Java程序中通过Jakarta-ORO库利用正则表达式。至于最终采用老式的字符串处理方式（使用StringTokenizer，charAt，和substring），还是采用正则表达式，这就有待你自己决定了。[/TD][/TR][/TABLE]


java中关于html的转换

作者：blackwhites

日期：2000-11-10 14:37:02

请看下面这个例子

  public static String escapeHTMLTags( String input ) {

        // Check if the string is null or zero length -- if so, return what was sent in.

        if( input == null || input.length() == 0 ) {

            return input;

        }

        // Use a StringBuffer in lieu of String concatenation -- it is much more efficient this way.

        StringBuffer buf = new StringBuffer();

        char ch = ' ';

        for( int i=0; i<input.length(); i++ ) {

            ch = input.charAt(i);

            if( ch == '<' ) {

                buf.append( "&lt;" );

            }

            else if( ch == '>' ) {

                buf.append( "&gt;" );

            }

            else {

                buf.append( ch );

            }

        }

        return buf.toString();

    }


Java中利用散列表实现股票行情的查询(转)

作者：asp2001

email: asp2001@163.net

日期：2000-8-9 12:50:30

Java中利用散列表实现股票行情的查询

建设银行云南省保山地区分行

杨绍方

---- 在java中，提供了一个散列表类Hashtable，利用该类，我们可以按照特定的方式来存储数据，从而达到快速检索的目的。本文以查询股票的收盘数据为例，详细地说明java中散列表的使用方法。

一、散列表的原理

---- 散列表，又称为哈希表，是线性表中一种重要的存储方式和检索方法。在散列表中，可以对节点进行快速检索。散列表算法的基本思想是：由结点的关键码值决定结点的存储地址，即以关键码值k为自变量，通过一定的函数关系h（称为散列函数），计算出对应的函数值h(k)来，将这个值解释为结点的存储地址，将结点存入该地址中，检索时，根据要检索的关键码值，用同样的散列函数计算出地址，然后，到相应的地址中去获取要找的结点数据。因此，散列表有一个重要特征：平均检索的长度不直接依赖于表中元素的个数。

---- 散列表最重要的一个指标是负载因子，即散列表中结点数目与表中能容纳的总结点数的比值，它描述了散列表的饱和程度，负载因子越接近1.0，内存的使用效率越高，元素的寻找时间越长，同样，负载因子越接近0.0，元素的寻找时间越短，但内存的浪费越大。Hashtable类缺省的负载因子为0.75.

二、Hashtable类

---- Hashtable类为我们提供了散列表完整的功能，可以让我们很方便地构造和使用散列表，查询信息。

---- 1.创建散列表对象

---- Hashtable类的构造器主要有下面几种形式：

public Hashtable(int initialCapacity, float loadFactor)；

public Hashtable(int initialCapacity)；

public Hashtable()；

在本文的实例中，我们使用了最简单的一种：

Hashtable stockInfo = new Hashtable();

---- 2.充填数据

---- 当构造了Hashtable对象后，我们就可以将数据填入该对象中，以便以后查询。Hashtable类提供了put方法来完成数据的装填，其原型如下：

---- public synchronized Object put(Object key, Object value)；

---- 3.查询数据

---- 查询数据可以使用get方法，其原型如下：

---- public synchronized Object get(Object key)

---- 4.其它常用的方法

public int size();

//返回散列表中的结点数目

public boolean isEmpty();

//判断散列表是否为空

public boolean containsValue(Object value);

//判断散列表中是否含有某值

public synchronized boolean containsKey(Object key);

//判断散列表中是否含有某个结点

public synchronized void clear();

//清空整个散列表

三、StringTokenizer类

---- StringTokenizer类的主要用途是将字符串以定界符为界，分析为一个个的token（可理解为单词），定界符可以自己指定。

---- 构造器有下面几种形式：

public StringTokenizer(String str,

String delim, boolean returnTokens);

public StringTokenizer(String str, String delim);

public StringTokenizer(String str);

其中，str为需分析的字符串，delim为定界符，

Tokens描述是否将定界符作为一个token。

---- 其它常用的方法有：

public boolean hasMoreTokens() ;

//判断字符串中是否还有token

public String nextToken();//

StringTokenizer对象的下一个token

四、实例

---- 本文使用的股票行情为上海和深圳证券交易所的收盘行情，文件名为hqsj.txt，下面是文件中的一行数据：

---- 600122宏图高科 18.90 18.80 18.90 18.20 18.27 3155 582.96

---- 下面是完整的源程序，在JDK1.2下使用javac编译通过。

import java.io.*;

import java.util.*;

import java.awt.*;

import java.applet.*;

import java.awt.event.*;

public class StockQuote extends Applet

implements ActionListener

{

private static final File INFO_FILE =

new File("hqsj.txt");

private Hashtable stockInfo;

TextField stockID;

Button button1;

private String quoteid,quotename;

public void init()

{

add(new Label("股票代码"));

stockID = new TextField(6);

add(stockID);

button1 = new Button("查询");

button1.addActionListener(this);

add(button1);

resize(500, 300);

}

public void start()

{

loadinfo();

}

protected boolean loadinfo()

{

String fileLine;

StringTokenizer tokenize;

String id;

StringBuffer name;

try {

// 创建一个访问数据文件的stream

BufferedReader stockInput = new

BufferedReader(new FileReader(INFO_FILE));

// 创建Hashtable对象

stockInfo = new Hashtable();

// 每次从文件中读一行数据

while ((fileLine = stockInput.readLine()) != null) {

// 将每一行数据分解为tokens.

tokenize = new StringTokenizer(fileLine);

try {

id = tokenize.nextToken();

// 创建一个放置股票信息的buffer

name = new StringBuffer();

while(tokenize.hasMoreTokens()) {

name.append(tokenize.nextToken());

if (tokenize.hasMoreTokens()) {

name.append("");

}

}

// 向Hashtable中充填记录

stockInfo.put(id,name.toString());

} catch(NullPointerException excpt) {

System.err.println("充填数据时出错: " + excpt);

} catch(NoSuchElementException excpt) {

System.err.println("无效的数据记录 " +

"in file: " + excpt);

}

}

stockInput.close();

} catch(FileNotFoundException excpt) {

System.err.println("不能发现文件: " + excpt);

return false;

} catch(IOException excpt) {

System.err.println("I/O故障: " + excpt);

return false;

}

 return true;

}

protected String getQuote(String StockID)

{

String info;

// 从Hashtable得到数据

info = (String)stockInfo.get(StockID);

if (info != null)

return info;

else

return "股票代码错误！";

}

public void paint(Graphics g)

{

g.drawString("股票代码"+quoteid+":" ,10,60);

g.drawString("股票名称"+"前收"+"今开"+"最高"

+"最低"+"收盘"+"交易量"+"交易金额", 10, 90);

g.drawString(quotename, 10, 120);

}

public void actionPerformed(ActionEvent ev)

{

String label = ev.getActionCommand();

if (label.equals("查询"))

{

quoteid = stockID.getText();

if(quoteid != null)

quotename = getQuote(quoteid);

else quotename = "请输入股票代码！";

repaint();

}

}

}

---- 由于java固有的、安全方面的限制，如果不使用SecurityPermission或数字签名等措施，java程序就不具有读取本地文件的权限，为了节省篇幅，本文对此不再多做讨论，将编译得到的StockQuote.class放到一个.html文件中，直接使用jdk1.2提供的appletviewer，其命令行的使用方法如下：

d:\jdk1.2\bin\appletviewer  StockQuote.html


Java中文问题详解(高手必读）

作者：blackwhites

日期：2000-10-18 17:26:02

我来说一下tomcat如何实现JSP的你就明白了。

预备知识：

  1.字节和unicode

    Java内核是unicode的，就连class文件也是，但是很多媒体，包括文件/流的保存方式

    是使用字节流的。 因此Java要对这些字节流经行转化。char是unicode的，而byte是字节.

    Java中byte/char互转的函数在sun.io的包中间有。其中ByteToCharConverter类是中调度，

    可以用来告诉你，你用的Convertor。其中两个很常用的静态函数是

     public static ByteToCharConverter getDefault() ;

     public static ByteToCharConverter getConverter(String encoding);

    如果你不指定converter，则系统会自动使用当前的Encoding,GB平台上用GBK,EN平台上用

    8859_1

    我们来就一个简单的例子：

          "你"的gb码是：0xC4E3 ,unicode是0x4F60

          你用:

          --encoding="gb2312";

          --byte b[]={(byte)'\u00c4',(byte)'\u00E3'};

          --convertor=ByteToCharConverter.getConverter(encoding);

          --char [] c=converter.convertAll(b);

          --for(int i=0;i<c.length;c++)

          --{

          -- System.out.println(Integer.toHexString(c[i]));

          --}

          --打印出来是0x4F60

          --但是如果使用8859_1的编码，打印出来是

          --0x00C4,0x00E3

          ----例１

         反过来：

         --encoding="gb2312";

               char c[]={'\u4F60'};

               convertor=ByteToCharConverter.getConverter(encoding);

          --byte [] b=converter.convertAll(c);

          --for(int i=0;i<b.length;c++)

          --{

          -- System.out.println(Integer.toHexString(b[i]));

          --}

            --打印出来是：0xC4,0xE3

            ----例２

            --如果用8859_1就是0x3F，?号，表示无法转化　　　　　　--

           很多中文问题就是从这两个最简单的类派生出来的。而却有很多类

    不直接支持把Encoding输入，这给我们带来诸多不便。很多程序难得用encoding

    了，直接用default的encoding，这就给我们移植带来了很多困难

    --

    2.UTF-8

    --UTF-8是和Unicode一一对应的，其实现很简单

    --

     -- 7位的Unicode: 0 _ _ _ _ _ _ _

    --11位的Unicode: 1 1 0 _ _ _ _ _ 1 0 _ _ _ _ _ _

    --16位的Unicode: 1 1 1 0 _ _ _ _ 1 0 _ _ _ _ _ _ 1 0 _ _ _ _ _ _

    --21位的Unicode: 1 1 1 1 0 _ _ _ 1 0 _ _ _ _ _ _ 1 0 _ _ _ _ _ _ 1 0 _ _ _ _ _ _

    --大多数情况是只使用到16位以下的Unicode:

    --"你"的gb码是：0xC4E3 ,unicode是0x4F60

    --我们还是用上面的例子

    --　　--例１：0xC4E3的二进制：

    --　　--　　　 1 1 0 0 0 1 0 0 1 1 1 0 0 0 1 1

    --　　--　　　 由于只有两位我们按照两位的编码来排，但是我们发现这行不通，

    --　　--　　　 因为第７位不是0因此，返回"?"

    --　　--

    --　　--例２：0x4F60的二进制：

    --　　--　　　 0 1 0 0 1 1 1 1 0 1 1 0 0 0 0 0

    --　　--　　　 我们用UTF-8补齐，变成：

    --　　--　　　 11100100 10111101 10100000

    --　　--　　　 E4--BD-- A0

    --　　--　　　 于是返回0xE4,0xBD,0xA0

    --　　--

    3.String和byte[]

    --String其实核心是char[],然而要把byte转化成String，必须经过编码。

    --String.length()其实就是char数组的长度，如果使用不同的编码，很可

    --能会错分，造成散字和乱码。

    --例：

    ----byte [] b={(byte)'\u00c4',(byte)'\u00e3'};

    ----String str=new String(b,encoding);　　----

    ----如果encoding=8859_1，会有两个字，但是encoding=gb2312只有一个字　　----

    --这个问题在处理分页是经常发生

    4.Reader,Writer/InputStream,OutputStream

    --Reader和Writer核心是char，InputStream和OutputStream核心是byte。

    --但是Reader和Writer的主要目的是要把Char读/写InputStream/OutputStream

--一个reader的例子：

--文件test.txt只有一个"你"字，0xC4,0xE3--

--String encoding=;

--InputStreamReader reader=new InputStreamReader(

----new FileInputStream("text.txt"),encoding);

--char []c=new char[10];

--int length=reader.read(c);

--for(int i=0;i<c.length;i++)

----System.out.println(c[i]);

    --如果encoding是gb2312，则只有一个字符，如果encoding=8859_1，则有两个字符

    --------

--

--

     ----

  2.我们要对Java的编译器有所了解：

  --javac -encoding

   我们常常没有用到ENCODING这个参数。其实Encoding这个参数对于跨平台的操作是很重要的。

   如果没有指定Encoding，则按照系统的默认Encoding,gb平台上是gb2312，英文平台上是ISO8859_1。

  --Java的编译器实际上是调用sun.tools.javac.Main的类，对文件进行编译，这个类　--

  有compile函数中间有一个encoding的变量,-encoding的参数其实直接传给encoding变量。

  编译器就是根据这个变量来读取java文件的，然后把用UTF-8形式编译成class文件。

  一个例子：

  --public void test()

  --{

  ----String str="你";

  ----FileWriter write=new FileWriter("test.txt");

  ----write.write(str);

  ----write.close();

  --}

  ----例３

--如果用gb2312编译，你会找到E4 BD A0的字段

--

--如果用8859_1编译，

--00C4 00E3的二进制：

--00000000 11000100 00000000 11100011--

--因为每个字符都大于7位，因此用11位编码：

--11000001 10000100 11000011 10100011

--C1-- 84--　C3--　 A3

--你会找到C1 84 C3 A3 --

    但是我们往往忽略掉这个参数，因此这样往往会有跨平台的问题：

    --　　例３在中文平台上编译，生成ZhClass

    --　　例３在英文平台上编译，输出EnClass

    --1.　 ZhClass在中文平台上执行OK,但是在英文平台上不行

    --2.　 EnClass在英文平台上执行OK,但是在中文平台上不行

    原因：

  --1.在中文平台上编译后，其实str在运行态的char[]是0x4F60,　----

  --在中文平台上运行，FileWriter的缺省编码是gb2312,因此

  --CharToByteConverter会自动用调用gb2312的converter,把str转化

  --成byte输入到FileOutputStream中，于是0xC4,0xE3放进了文件。

  --但是如果是在英文平台下，CharToByteConverter的缺省值是8859_1,

  --FileWriter会自动调用8859_1去转化str,但是他无法解释，因此他会

  --输出"?"　----

  --2.　在英文平台上编译后，其实str在运行态的char[]是0x00C4 0x00E3,　----

  --在中文平台上运行，中文无法识别，因此会出现??

  --　　在英文平台上，0x00C4-->0xC4,0x00E3->0xE3，因此0xC4,0xE3被放进了

  --文件

----

1.对于JSP正文的解释：

--Tomcat首先看一下你的叶面中有没有"<%@page include的符号。有，则在相同

--地方设定response.setContentType(..);按照encoding的来读，没有他按照8859_1

--读取文件，然后用UTF-8写成.java文件，然后用sun.tools.Main去读取这个文件，

--（当然它使用UTF-8去读），然后编译成class文件

--setContentType改变的是out的属性，out变量缺省的encoding是8859_1

2.对Parameter的解释

--很不幸Parameter只有ISO8859_1的解释，这个质料可以在servlet的实现代码中找到。

3.对include的解释

格式的，但是很不幸，由于那个写"org.apache.jasper.compiler.Parser"的人

在数组JspUtil.ValidAttribute[]忘记加了一个参数：encoding,因此导致不支

持这种方式。你完全可以编译源代码，加上对encoding的支持

总结：

如果你在NT底下，最简单的方法就是欺骗java,不加任何Encoding变量：

<html>

你好<%=request.getParameter("value")%>

</html>

http://localhost/test/test.jsp?value=

结果:你好你

但这种方法局限性较大，比如对上传的文章分段，这样的做法是死定的，最好的

解决方案是用这种方案：

<%@ page contentType="text/html;charset=gb2312" %>

<html>

你好<%=new String(request.getParameter("value").getBytes("8859_1"),"gb2312")%>

</html>


Java中文问题详解（转）

作者：运气

email: webmaster@chinaspx.com

日期：2001-7-20 15:49:16

我来说一下tomcat如何实现jsp的你就明白了。

预备知识：

  1.字节和unicode

    java内核是unicode的，就连class文件也是，但是很多媒体，包括文件/流的保存方式

    是使用字节流的。 因此java要对这些字节流经行转化。char是unicode的，而byte是字节.

    java中byte/char互转的函数在sun.io的包中间有。其中bytetocharconverter类是中调度，

    可以用来告诉你，你用的convertor。其中两个很常用的静态函数是

     public static bytetocharconverter getdefault() ;

     public static bytetocharconverter getconverter(string encoding);

    如果你不指定converter，则系统会自动使用当前的encoding,gb平台上用gbk,en平台上用

    8859_1

    我们来就一个简单的例子：

          "你"的gb码是：0xc4e3 ,unicode是0x4f60

          你用:

          --encoding="gb2312";

          --byte b[]={(byte)'\u00c4',(byte)'\u00e3'};

          --convertor=bytetocharconverter.getconverter(encoding);

          --char [] c=converter.convertall(b);

          --for(int i=0;i　　　　　--{

          -- system.out.println(integer.tohexstring(c[i]));

          --}

          --打印出来是0x4f60

          --但是如果使用8859_1的编码，打印出来是

          --0x00c4,0x00e3

          ----例１

         反过来：

         --encoding="gb2312";

               char c[]={'\u4f60'};

               convertor=bytetocharconverter.getconverter(encoding);

          --byte [] b=converter.convertall(c);

          --for(int i=0;i　　　　　--{

          -- system.out.println(integer.tohexstring(b[i]));

          --}

            --打印出来是：0xc4,0xe3

            ----例２

            --如果用8859_1就是0x3f，?号，表示无法转化　　　　　　--

           很多中文问题就是从这两个最简单的类派生出来的。而却有很多类

    不直接支持把encoding输入，这给我们带来诸多不便。很多程序难得用encoding

    了，直接用default的encoding，这就给我们移植带来了很多困难

    --

    2.utf-8

    --utf-8是和unicode一一对应的，其实现很简单

    --

     -- 7位的unicode: 0 _ _ _ _ _ _ _

    --11位的unicode: 1 1 0 _ _ _ _ _ 1 0 _ _ _ _ _ _

    --16位的unicode: 1 1 1 0 _ _ _ _ 1 0 _ _ _ _ _ _ 1 0 _ _ _ _ _ _

    --21位的unicode: 1 1 1 1 0 _ _ _ 1 0 _ _ _ _ _ _ 1 0 _ _ _ _ _ _ 1 0 _ _ _ _ _ _

    --大多数情况是只使用到16位以下的unicode:

    --"你"的gb码是：0xc4e3 ,unicode是0x4f60

    --我们还是用上面的例子

    --　　--例１：0xc4e3的二进制：

    --　　--　　　 1 1 0 0 0 1 0 0 1 1 1 0 0 0 1 1

    --　　--　　　 由于只有两位我们按照两位的编码来排，但是我们发现这行不通，

    --　　--　　　 因为第７位不是0因此，返回"?"

    --　　--

    --　　--例２：0x4f60的二进制：

    --　　--　　　 0 1 0 0 1 1 1 1 0 1 1 0 0 0 0 0

    --　　--　　　 我们用utf-8补齐，变成：

    --　　--　　　 11100100 10111101 10100000

    --　　--　　　 e4--bd-- a0

    --　　--　　　 于是返回0xe4,0xbd,0xa0

    --　　--

    3.string和byte[]

    --string其实核心是char[],然而要把byte转化成string，必须经过编码。

    --string.length()其实就是char数组的长度，如果使用不同的编码，很可

    --能会错分，造成散字和乱码。

    --例：

    ----byte [] b={(byte)'\u00c4',(byte)'\u00e3'};

    ----string str=new string(b,encoding);　　----

    ----如果encoding=8859_1，会有两个字，但是encoding=gb2312只有一个字　　----

    --这个问题在处理分页是经常发生

    4.reader,writer/inputstream,outputstream

    --reader和writer核心是char，inputstream和outputstream核心是byte。

    --但是reader和writer的主要目的是要把char读/写inputstream/outputstream

--一个reader的例子：

--文件test.txt只有一个"你"字，0xc4,0xe3--

--string encoding=;

--inputstreamreader reader=new inputstreamreader(

----new fileinputstream("text.txt"),encoding);

--char []c=new char[10];

--int length=reader.read(c);

--for(int i=0;i----system.out.println(c[i]);

    --如果encoding是gb2312，则只有一个字符，如果encoding=8859_1，则有两个字符

    --------

--

--

     ----

  2.我们要对java的编译器有所了解：

  --javac -encoding

   我们常常没有用到encoding这个参数。其实encoding这个参数对于跨平台的操作是很重要的。

   如果没有指定encoding，则按照系统的默认encoding,gb平台上是gb2312，英文平台上是iso8859_1。

  --java的编译器实际上是调用sun.tools.javac.main的类，对文件进行编译，这个类　--

  有compile函数中间有一个encoding的变量,-encoding的参数其实直接传给encoding变量。

  编译器就是根据这个变量来读取java文件的，然后把用utf-8形式编译成class文件。

  一个例子：

  --public void test()

  --{

  ----string str="你";

  ----filewriter write=new filewriter("test.txt");

  ----write.write(str);

  ----write.close();

  --}

  ----例３

--如果用gb2312编译，你会找到e4 bd a0的字段

--

--如果用8859_1编译，

--00c4 00e3的二进制：

--00000000 11000100 00000000 11100011--

--因为每个字符都大于7位，因此用11位编码：

--11000001 10000100 11000011 10100011

--c1-- 84--　c3--　 a3

--你会找到c1 84 c3 a3 --

    但是我们往往忽略掉这个参数，因此这样往往会有跨平台的问题：

    --　　例３在中文平台上编译，生成zhclass

    --　　例３在英文平台上编译，输出enclass

    --1.　 zhclass在中文平台上执行ok,但是在英文平台上不行

    --2.　 enclass在英文平台上执行ok,但是在中文平台上不行

    原因：

  --1.在中文平台上编译后，其实str在运行态的char[]是0x4f60,　----

  --在中文平台上运行，filewriter的缺省编码是gb2312,因此

  --chartobyteconverter会自动用调用gb2312的converter,把str转化

  --成byte输入到fileoutputstream中，于是0xc4,0xe3放进了文件。

  --但是如果是在英文平台下，chartobyteconverter的缺省值是8859_1,

  --filewriter会自动调用8859_1去转化str,但是他无法解释，因此他会

  --输出"?"　----

  --2.　在英文平台上编译后，其实str在运行态的char[]是0x00c4 0x00e3,　----

  --在中文平台上运行，中文无法识别，因此会出现??

  --　　在英文平台上，0x00c4-->0xc4,0x00e3->0xe3，因此0xc4,0xe3被放进了

  --文件

----

1.对于jsp正文的解释：

--tomcat首先看一下你的叶面中有没有"

http://localhost/test/test.jsp?value=

结果:你好你

但这种方法局限性较大，比如对上传的文章分段，这样的做法是死定的，最好的

解决方案是用这种方案：

你好

必读好文,但解决方案不敢恭维

发言者：小熊

时 间：2000/10/17 17:21:31 来 自：http://www.javaunion.org

--------------------------------------------------------------------------------

1.网页传参数不提倡用get方法,而且用户可以调整是否用utf-8发送

2.建议jsp中最好不要用,实际上加不加这句都有实现中文正常显示的方案,我认为不加方便些,至少不用写这些代码,如下的配置我认为可以使中文正常显示:

a.所有的javabean用iso8859-1编译

b.jsp文件中不要写以上charset=gb2312的语句(写了反而错)

在tomcat情况下注意以上2点就行---了,等等,对于其他有可能不行的jsp服务器,加上以下

c.服务器上的操作系统语言设为英文(像没有装类似bluepoint中文系统的linux一般本来就是英文)

就行---了

谁要是还不对,请报告....

re：必读好文,但解决方案不敢恭维

发言者：congling

时 间：2000/10/17 21:06:23 来 自：http://www.javaunion.org

--------------------------------------------------------------------------------

tomcat的参数问题无论是get或是post方式都是用8859_1编码的。这个可以看tomcat servlet实现的源代码:

a) 对于post方法

  javax.servlet.http.httputils的parsepostdata方法： (对于post的form数据)

  string postedbody = new string(postedbytes, 0, len, "8859_1");)这里是没有问题的因为中文都会用%来说明。但是parsename这个函数，却没有把是中文的东西整合起来，他只是简单的拼凑，因此可以认定他是使用8859_1的编码规则

   sb.append((char) integer.parseint(s.substring(i+1, i+3), 16));

----　　i += 2;

--

b) 对于get方法

  org.apache.tomcat.service.http.httprequestadapter

      --　line=new string(buf, 0, count,

             constants.characterencoding.default);

----constants.characterencoding.default=8859_1

  这段代码不好跟踪，千万不要被一些假象迷惑住。httprequestadapter是从requestimpl中派生的。但是，实际上用8080端口的server并没有直接用到requestimpl，而是用了httprequestadapter来获得querystring

对于加不加encoding，我保留我的意见，因为如果要解决上传文件分页问题，必须要用他来编码。而且编码能保证在一些beans当中的传递性。

看来我要在这里说明一下了

发言者：小熊

时 间：1970/01/01 07:59:59 来 自：http://www.javaunion.org

--------------------------------------------------------------------------------

tomcat仅仅是一个对jsp1.1,servlet2.2的一个标准的实现,我们不应该要求这个免费软件在细致末节上和性能上都面面俱到,它主要考虑的英文用户,这也是为什么不作特殊转换我们的汉字用url方法传递有问题的原因,我们大部分浏览器ie其高级设置中始终以utf-8发送url的选项缺省是选上的,如果说这是tomcat的bug也是可以的,另外tomcat不管当前的操作系统是什么语言,好像都按iso8859去编译jsp,我觉得也有点欠妥,但是不管怎么说,新标准的实现和热门的软件在语言的支持方面永远都是先考虑英文

我的方案什么说要好一些呢

1.还是那句话,英文国家的软件永远都是先考虑英文,java虚拟机的规范中要求虚拟机内部必须实现iso8859,unicode,utf-8三种,其他的不作要求,我们用的jdk中的虚拟机就是这样,嵌入式的就更不用说了,也就是说其他的encode都很可能不是java虚拟机内部直接支持的,我们的中文自然也不在其列,需要外部的包支持转换,sun jdk应该在i18n.jar中,用iso8859速度最快,不需要其它调用和交换什么的,更没有读包的io操作

2.至少少写了代码,没有额外操作,简洁的风格谁不喜欢

3.所写的jsp页面国际性化好,我才写了一个jsp+javabeans的聊天室软件(没有用到servlet,jsp真的确实很好),同样的程序美国人用他们的浏览器进入就是英文界面,中文进入就是中文界面,如果加上charset=gb2312至少很麻烦

4.限定了gb2312,如果用户要用gbk,怎么办,不加更好,不管什么的字符集,只要我当前浏览器设定的是,我就能显示出来

总结:无论从速度上,开发效率上,和可扩展性上考虑,我的方案都比你的好,另外,我找不到你的方案比我的好的地方


Java中文问题详解

作者：weljava

email: weljava@263.net

日期：2000-12-14 16:03:40

发信人: aweiwei (还是学习的好), 信区: JAVA

标  题: Java中文问题详解

发信站: 碧海青天 (Wed Nov 29 09:50:42 2000), 转信

Java中文问题详解

    2000-11-25 10:09

------------------------------------------------------------------------

--------

作者：转载自Java联盟论坛

Java中文问题详解 发言者：congling 时 间：2000/10/16 22:51:22 来 自：

http://www.javaunion.org

------------------------------------------------------------------------

--------

我来说一下tomcat如何实现JSP的你就明白了。 预备知识： 　1.字节和unicode

    Java内核是unicode的，就连class文件也是，但是很多媒体，包括文件/流的

保存方式 　　是使用字节流的。 因此Java要对这些字节流经行转化。char是

unicode的，而byte是字节. 　　Java中byte/char互转的函数在sun.io的包中间有

。其中ByteToCharConverter类是中调度， 　　可以用来告诉你，你用的

Convertor。其中两个很常用的静态函数是 　　 public static

ByteToCharConverter getDefault() ; 　　 public static

ByteToCharConverter getConverter(String encoding); 　　如果你不指定

converter，则系统会自动使用当前的Encoding,GB平台上用GBK,EN平台上用

8859_1 　　 　　我们来就一个简单的例子： 　　　　　“你“的gb码是：

0xC4E3 ,unicode是0x4F60 　　　　　你用: 　　　　　--encoding=“gb2312“;

           --byte b[]={(byte)‘u00c4‘,(byte)‘u00E3‘};

--convertor=ByteToCharConverter.getConverter(encoding); 　　　　　--char

 [] c=converter.convertAll(b); 　　　　　--for(int i=0;i0xC4,

0x00E3->0xE3，因此0xC4,0xE3被放进了 　--文件 ---- 1.对于JSP正文的解释：

 --Tomcat首先看一下你的叶面中有没有“

http://localhost/test/test.jsp?value=

结果:你好你

但这种方法局限性较大，比如对上传的文章分段，这样的做法是死定的，最好的

解决方案是用这种方案： 你好

必读好文,但解决方案不敢恭维 发言者：小熊 时 间：2000/10/17 17:21:31 来

自：http://www.javaunion.org

------------------------------------------------------------------------

--------

1.网页传参数不提倡用get方法,而且用户可以调整是否用utf-8发送 2.建议jsp中

最好不要用,实际上加不加这句都有实现中文正常显示的方案,我认为不加方便些,

至少不用写这些代码,如下的配置我认为可以使中文正常显示: a.所有的javabean

用iso8859-1编译 b.jsp文件中不要写以上charset=gb2312的语句(写了反而错)

在tomcat情况下注意以上2点就行---了,等等,对于其他有可能不行的jsp服务器,加

上以下 c.服务器上的操作系统语言设为英文(像没有装类似bluepoint中文系统的

linux一般本来就是英文) 就行---了

谁要是还不对,请报告....

Re：必读好文,但解决方案不敢恭维 发言者：congling 时 间：2000/10/17 21:

06:23 来 自：http://www.javaunion.org

------------------------------------------------------------------------

--------

Tomcat的参数问题无论是GET或是POST方式都是用8859_1编码的。这个可以看

Tomcat Servlet实现的源代码: a) 对于POST方法 　javax.servlet.http.

HttpUtils的parsePostData方法： (对于POST的Form数据) 　String

postedBody = new String(postedBytes, 0, len, “8859_1“);)这里是没有问题

的因为中文都会用%来说明。但是parseName这个函数，却没有把是中文的东西整合

起来，他只是简单的拼凑，因此可以认定他是使用8859_1的编码规则 　 sb.

append((char) Integer.parseInt(s.substring(i+1, i+3), 16)); ----　　i +=

 2; -- b) 对于GET方法 　org.apache.tomcat.service.http.

HttpRequestAdapter 　　　--　line=new String(buf, 0, count,

 Constants.CharacterEncoding.Default); ----Constants.CharacterEncoding.

Default=8859_1 　这段代码不好跟踪，千万不要被一些假象迷惑住。

HttpRequestAdapter是从RequestImpl中派生的。但是，实际上用8080端口的

Server并没有直接用到RequestImpl，而是用了HttpRequestAdapter来获得

queryString

对于加不加encoding，我保留我的意见，因为如果要解决上传文件分页问题，必须

要用他来编码。而且编码能保证在一些Beans当中的传递性。

看来我要在这里说明一下了 发言者：小熊 时 间：1970/01/01 07:59:59 来 自：

http://www.javaunion.org

------------------------------------------------------------------------

--------

Tomcat仅仅是一个对jsp1.1,servlet2.2的一个标准的实现,我们不应该要求这个免

费软件在细致末节上和性能上都面面俱到,它主要考虑的英文用户,这也是为什么不

作特殊转换我们的汉字用url方法传递有问题的原因,我们大部分浏览器ie其高级设

置中始终以utf-8发送url的选项缺省是选上的,如果说这是tomcat的bug也是可以的

,另外Tomcat不管当前的操作系统是什么语言,好像都按iso8859去编译jsp,我觉得

也有点欠妥,但是不管怎么说,新标准的实现和热门的软件在语言的支持方面永远都

是先考虑英文

我的方案什么说要好一些呢 1.还是那句话,英文国家的软件永远都是先考虑英文,

java虚拟机的规范中要求虚拟机内部必须实现iso8859,unicode,UTF-8三种,其他的

不作要求,我们用的jdk中的虚拟机就是这样,嵌入式的就更不用说了,也就是说其他

的ENCODE都很可能不是java虚拟机内部直接支持的,我们的中文自然也不在其列,需

要外部的包支持转换,sun jdk应该在i18n.jar中,用iso8859速度最快,不需要其它

调用和交换什么的,更没有读包的io操作 2.至少少写了代码,没有额外操作,简洁的

风格谁不喜欢 3.所写的jsp页面国际性化好,我才写了一个jsp+javabeans的聊天室

软件(没有用到servlet,jsp真的确实很好),同样的程序美国人用他们的浏览器进入

就是英文界面,中文进入就是中文界面,如果加上charset=gb2312至少很麻烦 4.限

定了gb2312,如果用户要用GBK,怎么办,不加更好,不管什么的字符集,只要我当前浏

览器设定的是,我就能显示出来

总结:无论从速度上,开发效率上,和可扩展性上考虑,我的方案都比你的好,另外,我

找不到你的方案比我的好的地方.

广告服务 | 隐私保护 | 版权所有 | 联系我们

版权所有 深圳盛亚电脑技术有限公司 & 深圳天萧工作室

Copyright (C) 2000 Shenzhen Superasia Computer Technology Co., Ltd.

All Rights Reserved

--

  吾本楚狂人，疯歌笑孔丘


java中有关日期的显示问题

作者：blackwhites

日期：2001-1-9 10:18:21

import java.util.*;

import java.text.*;

public class StyleDemo {

   public static void main(String[] args) {

      Date now = new Date();

      DateFormat df = DateFormat.getDateInstance();

      DateFormat df1 = DateFormat.getDateInstance(DateFormat.SHORT);

      DateFormat df2 = DateFormat.getDateInstance(DateFormat.MEDIUM);

      DateFormat df3 = DateFormat.getDateInstance(DateFormat.LONG);

      DateFormat df4 = DateFormat.getDateInstance(DateFormat.FULL);

      String s = df.format(now);

      String s1 = df1.format(now);

      String s2 = df2.format(now);

      String s3 = df3.format(now);

      String s4 = df4.format(now);

      System.out.println("(Default) Today is " + s);

      System.out.println("(SHORT) Today is " + s1);

      System.out.println("(MEDIUM) Today is " + s2);

      System.out.println("(LONG) Today is " + s3);

      System.out.println("(FULL) Today is " + s4);

   }

}

That program output the following:

(Default) Today is Nov 8, 2000

(SHORT) Today is 11/8/00

(MEDIUM) Today is Nov 8, 2000

(LONG) Today is November 8, 2000

(FULL) Today is Wednesday, November 8, 2000


java中怎样使用库中的文件(import 文件)

作者：jiafa

email: jiafahehe@sina.com

日期：2001-4-19 16:36:29

我做了一个测试，用到3个文件: test.java ,   list.java,  litest.java 都保存在 /mnt/test/buel/util文件夹中

// test.java

  package buel.util

 public class test  {

     public test() {

           System.out.println("this is from test");

                           }

                               }

//list.java

package buel.util

  public class list {

       public list()  {

       System.out.println("this is from list");

                            }

      public int finds(int i)  {

        system.out.print("we find i="+i);

                                           }

                             }

//litest.java

import buel.util;

    public class litest{

          public litest() {

                    System.out.println("this is from list");

                   list  L=new list();

                   test t=new test();

                   L.find(6);

                                   }

    }

 java 的环境变量我增加了 /mnt/test

然后进行编译test.java  和list.java 都编译成功了，在编译litest时出现:cannot find method find(int);

这是怎么回事呢？


JAVA专业术语集

作者：javan

日期：2001-4-6 11:23:02

刚开始的时候，总对许多名词不理解，既然有了资料，就共享喽：

    API：Java ApplicationProgrammingInterface API（应用程序接口）是事先写好

的代码, 组织到相关包。例如 Applet 和 AWT 包包括建立字体、菜单、按钮的类

（CLASS），全部的Java API被包含在JavaTM 2 Standard Edition 1. 在线API文

档　2. API文档下载

Applet Applet（小应用程序）是在WEB浏览器里运行的java程序。Applets使用G

UI，可能有文本、图象、按钮、声音等。 AWT 和 SWING 通常被联合使用。 1.

架构Applets 2. 写一个Applets 3. java.applet包

AWT：Abstract WindowToolkit AWT（抽象窗口工具集）是一个包含用来建立App

let和应用程序需要用到的组件（比如按钮、菜单、滚动条等）的包（package）

 。 1. AWT原则 2. 用 AWT构造GUI 3. java.awt 包描述 4. AWT论坛

JavaBeansTM JavaBean可重复利用、可互换的软件组件。 JavaBeans 可能是简单

的象按钮或者是访问数据库的工具等。 1. JavaBeans 技术: BeanContext API

2. JavaBeans论坛

JFC：JavaTM Foundation Classes (JFC) JFC（JAVA基础类）集合了GUI组件以及

其他能简化开发和展开桌面和Internet/Intranet应用的服务。 1. Java Founda

tion Classes (JFC) 2. JFC FAQ

JNI：JavaTMNative Interface JNI（JAVA本地接口）是 JDK中JAVA的本地编程接

口，JNI允许Java代码操作用其他的高级（推荐）编程语言（比如C、C++）编写的

应用程序和库。 1. 指南: JNI 2. JNI技术3. JNI 1.2

JSP：JavaServerTM Pages 通过在HTML页面里嵌入scriptlets (Java编程语言代

码) 构造JSP， JSP 页面处理表单，完成计算， 或者做JAVA语言能做的其他任何

事情。 1. JSP指南 2. JSP:开发着的观点 3. JSP Pro 4. Servlets和 JSP5. J

SP论坛

J2EETM：JavaTM 2PlatformEnterpriseEdition J2EE（JAVA2企业版）平台提供一

个基于组件设计、开发、集合、展开企业应用的途径。J2EE 平台提供了多层、分

布式的应用模型，重新利用组件的能力，统一安全的模式以及灵活 的处理控制能

力。 1. J2EE体系结构2. J2EETM 蓝图摘要3. EJBTM技术基础4. Java编程论坛

J2METM：JavaTM 2MicroEdition J2ME （JAVA2精简版）API规格基于J2SETM ，但

是被修改成为只能适合某种产品的单一要求。J2ME使JAVA程序应用于电话卡、寻

呼机等其他消费产品成为可能。 1. J2ME技术2. 消费和商业论坛

J2SE：JavaTM 2StandardEdition J2SE（JAVA2标准版）包括基本编译器、小工具

、运行环境、用来开发、运行applets和java程序 的APIs。 1. 下载J2SE 1.32.

 在线文档3. java平台介绍4. 牛刀小试

JVM ：JavaTM VirtualMachine1 JVM（JAVA虚拟机）：能执行java编译器产生的

指令的运行环境。 能嵌入各种不同的产品（比如web浏览器、操作系统） 1. JV

M论坛

JDBCTMJavaDataBaseConnect 您可以用JDBC（JAVA数据库互联） API访问支持JD

BC的任何数据库。J2SE包括JDBC API。 1. JDBC API2. JDBC基础3. JDBC论坛

JDKTM：JavaDeveloper'sKit JDK（JAVA开发工具集）由 API类，Java编译器，J

VM解释器。可用来编applets和应用程序当前版本为J2SE。 1. 下载J2SE 1.3 2.

 在线文档 3. java平台介绍4. 牛刀小试5. JDK 1.2 路标

JINITM Jini网络技术使任何的服务（企业系统到炊具到网络）变平稳和简单。J

ini 体系结构让每种服务(硬件设备和软件系统) 自动对话。 1. JiniTM 技术中

的分布式事件2. 怎样为Jini服务配属用户接口3. Jini核心4. 分布式计算论坛

Project Swing javax.swing包通常用于applet和应用程序的GUI组件。Swing类允

许程序员设定组件的不同平台，Swing是JFC中轻重量级的组件。 1. Swing基础

1 2. Swing基础 2 3. Swing建立GUI4. 建立用户界面5. Swing论坛

RMI：Remote Method Invocation RMI（远程方法调用）使java程序越过网络通信

。程序可以运行在网络两端不同的机器上；这种更高级别的基于方法的网络通信

方式允许访问远程的对象，有如本地的对象。 1. RMI基础2. RMI-IIOP文档3. R

MI教程4. RMI-IIOP论坛

Servlets Servlet是服务器的扩展，他提高了服务器的功能。Servlets 处理表单

处理请求转向及鉴别用户ID密码等，建立动态的内容。 1. Servlets概观 2. 写

Servlets 3. Servlet API更新 4. Servlets基础


JB4中文教程

作者：罗亭

email: tingluo@263.net

日期：7/30/2001 12:08:16 PM

/** 各位手脚要快点,我试过，支持ant */

JBuilder4 开发人员指南中文版本(PDF)

URL：

http://www.inprises.com/control/jbuilderch/01.pdf

http://www.inprises.com/control/jbuilderch/02.pdf

........

.......

http://www.inprises.com/control/jbuilderch/39.pdf


Jbuider4新特性介绍（二）

作者：luflying

email: zhang_yng@citiz.net

日期：2000-11-24 12:40:17

二、 企业JavaBeans

    在Jbuilder4中，可以更容易的创建(create)配置(deploy)企业JavaBean。提供了一系列的向导来进行创建，处理，配置企业JavaBean的流程。把IAS的配置描述编辑器集成到Jbuilder4的开发环境中（不过，并没有把IAS4.1内置到Jbuilder4中，需要另外下载安装）。Jbuilder4还支持BEA的WebLogic Server 5.1。

    Jbuider4的一个新的特性就是EJB Group功能。每一个EJB Group都代表了一个可配置的JAR文件。通过EJB Group，可以对已有的EJB在EJB Group之间进行移植。一个project工程中可以有多个EJB Group。

    Entity Bean Modeler可以创建一个映射到现存表的实体Entity Bean。可以产生所有必须的java代码，更新EJB Group中配置描述。在Jbuilder4中，可以：

    1、用EJB 向导创建Session Bean和Entity Bean

    2、用Entity Bean Modeler将Entity Bean映射到现有的table中。

    3、通过EJB Group向导创建EJB Group。这样，就可以在一个应用程序里面放多个的Enterprise JavaBean。

    4、可以将一个Enterprise JavaBean从一个EJB Group移植到另一个EJB Group

    5、一个应用程序里面可以有多个的EJB Group。

    6、用Make和Rebuild就可以自动的创建可配置的JAR文件。

    7、可以在node viewer中查看JAR的内容。

    8、通过Test Client wizard可以在本地的容器或者与EJB1.1兼容的应用服务器中测试Enterprise JavaBean。

    9、运行或调式EJB的时候会自动的配置EJB。

三、 支持应用服务器

    Jbuilder4允许选择支持的应用服务器。保持着对每个应用服务器的配置。在Project|Project Properties菜单中，run标签中的EJB标签。缺省的选择是IAS，但是 也可以在Jbuilder4的集成开发环境中直接使用BEA的WebLogic5.1。在tool|Enterprise Setup菜单中，选择Application Server标签，然后可以选择WebLogic5.1，并指定路径。

    也可以通过OpenTools API,插入其它的应用服务器。

四、 团队开发功能

    工程文件（*.jpr和*.jpx）可以分为两部分，private和shared。Windows, watch, breakpoints保存在private私有部分，而libraries和编译选项保存在公有shared部分。

    版本追踪和控制：Jbuilder4对每一个版本都提供更改的支持。要查看一个文件的版本信息，单击底部的histroy标签，里面有每个文件的历史修改记录。Jbuilder自动的保持着文件的备份。可以设定备份的个数。Histroy页面就用这些备份来管理着历史的修改记录。可以访问上一个版本文件。也可以查看两个版本之间做了哪些修改。

    Jbuilder4的企业版还提供了CVS（Concurrent Versions System并发版本系统）

五、 OpenTools API开放工具API

    增强了OpenTools API,使其更简单，更容易使用。可以做默认的path这样的小修改，也可以设计自己的向导这样的复杂的修改。

六、 运行和调试

    在Run|Configurations菜单中可以创建另外的配置项。

    调试的功能也显著的增强了，所有的调试列表都支持复选，在split view中可以看到数据可线程。可以将线程，断点的浮动窗口接在一起。


Jbuider4新特性介绍（三）

作者：luflying

email: zhang_yng@citiz.net

日期：2000-11-24 12:43:03

七、 数据库工具

    增强了数据库工具功能，也更加容易使用了。

    JdataStore:更快，更灵活了。其底层的连接池的性能有显著的提高。Jbuilder4对支持JTA的支持使　　　　JdataStore连接可以利用标准的XA接口参与分布式的事务处理。

    JdataStore Explorer可以用图形化的界面创建和操作table。可以创建表的索引。现在JdataStore支持交叉连接，内连接，外连接，左，右连接。在现在的发行版本中，是通过“natural”或 “using”关键字来指定连接的域。JdataStore现在也支持SQL-92的连接序列，支持分级的子查询。

但是Jbuilder4也还是它的不足：

    1. 和中文系统有冲突。至少和Win2000的中文系统有冲突，它的菜单总是触发中文输入条。

    2. 速度不够快。还是要求高配置。

    3. 由于需要Application Server，而且要求装在同一机器上，开发EJB要求的硬件环境太高

    4. 缺乏WebGain Studio里的一些有用工具，如StructureBuilder。缺乏和Rose等工具的高效集成

    5. 支持的工具，如版本控制的工具比较少

     接下来是实现远程接口，远程接口的实现类应该：

     ⑴ 指定所实现的一个或多个接口。

     ⑵ 定义远程对象的构造方法。

     ⑶ 提供所有可供远程调用的方法的实现。

     ⑷ 创建和安装安全管理器。

     ⑸ 创建一个或多个远程对象实例。

     ⑹ 注册至少一个远程对象。


Jbuider4新特性介绍

作者：luflying

email: zhang_yng@citiz.net

日期：2000-11-24 12:37:22

Jbuider4有很多以前版本没有的新特性，另外还根据程序开发者的要求，在某些地方做了增强：

    1、Web程序的开发，企业JavaBean和大程序的团队开发是Jbuider4新特性的重点。

    2、增强了开放的API工具，更易与开发者使用。

    3、增加了对应用服务器的支持

    4、对用户界面，特别是对File|Open菜单中的向导，做了重新的设计。

    5、增加了一些调试功能

    6对JdataStore和JDBC Explorer进行了改进，数据库的操作更简单。

下面详细的说明Jbuider4的新性能。

一、 Web应用程序的开发：

    Jbuilder4支持Web应用程序的开发，增加了对XML的支持，提供了更好的Servlet和JSP的支持。通过InternetBeans Express和一些组件，能够在Java和html之间进行无缝数据转换。

    Jbuilder4内置了Tomcat3.1,可以动态的调试Servlet和JSP程序。

    每一个Servlet都有一个别名(Servlet-name)和init()初始化函数的参数，能够离开SHTML独立执行。

    Jbuilder4提供了增强的JSP支持，很容易的就可以直接调试JSP源代码。

    提供了XML的支持。能够简单快速的编写XML文件。XML关键字的高亮度显示很容易的对整个XML文件的结构了如指掌。

    通过InternetBeans Express控件，可以动态的产生WEB页面的内容。


JBuilder 4 概观 (转贴）

作者：netkey

日期：2001-3-20 20:20:30

JBuilder 4 概观

作者: 翻译：廖铮

日期: 2001年01月09日

JDJ：JBuilder的最新版本今天终于发布了，这真是个好消息。Tony，你可以谈谈Borland公司在JBuilder 和Java方面做了哪些工作吗？

de la Lama：当然，我们对此都很激动，我也很乐意首次向公众展示和介绍这一新产品。它所具有的一些崭新的功能特性是最令我们感兴趣的，我想，正是这些新东西将把JBuilder 4作为下一代 Java应用的开发工具推向前台。现在我们拥有了新的团队开发解决方案，这一方案建立在CVS [Concurrent Versions System] 知识库的基础之上。你也许比较熟悉运行在Solaris、Linux和Windows这三种平台之上的JBuilder。而我们需要新的工具可以跨越不同操作系统的开发平台甚至扩展到Internet之上。基于CVS的知识库已经完全整合到了这一新产品中。这是一项很不错的新技术，也是我们最希望JBuilder 4为大家所了解的一点。

我还想提到一些和程序调试有关的特性。有些人可能还不知道JBuilder 4中已经具备了完整的JSP调试工具。实际上你可以在JSP中设置断点、查看变量等等。你还可以直接进出在JSP中标记的EJB。这个调试环境确实是很有意思的。

JBuilder 4还提供了对Servlet 2.2 和JSP 1.1的支持。这些都是目前Java技术中的最新标准。新产品中包括了Tomcat和 Apache。如果你将JBuilder 4看作一套完整的开发包，那么实际上你得到的是企业级的开发产品，其中不仅包括了Borland 应用服务器（包括VisiBroker），还包括了JSP runner、servlet runner和Tomcat。在这样良好的开发环境之下，你可以实现编写代码、调试程序、本地部署乃至测试EJB等一系列工作。我们确实为能推出如此成功的开发环境而感到兴奋。

JDJ：你谈到了JBuilder 中的一些不同特性，比如向导程序、代码生成工具等等，他们和JBuilder的竞争产品中存在哪些不同之处呢？

de la Lama：我们采用所谓的 EJB modeler来创建容器管理持久性，也就是EJB management persistence。默认情况下我们为用户和主类创建全套EJB接口。这样就为用户节约了大量的开发时间。JBuilder 4 开始支持WebLogic，用户由此不仅可以建立EJB，此外还可以在BEA的WebLogic服务器上部署、运行和调试这些EJB。所以我们目前实际上在支持两种应用服务器，我们自己的Borland应用服务器和WebLogic服务器。用户实际上最感兴趣的特性是他们拥有了在WebLogic上部署应用的能力。而这一切只需将EJB提交给服务器，期间根本无须重新启动服务器而由其自动处理。从而再次节约了大量的开发时间。

JDJ：显而易见，CVS是你们专为Internet准备的。那么它是否经过了加密？安全性又如何？

de la Lama：这个问题问得好。几天前当我和我们的一些开发人员在一起的时候，我们谈到了一个基于CVS的open-search项目（project），大家都想看看该项目的代码。于是我们启动了JBuilder，并用JBuilder创建了一个项目，然后将其指向知识库，并设置了URL和口令，在很短的时间之内我们就将该项目下载到了JBuilder内。5分钟后我们简单地编译了整个项目。怎么样，够酷吧？现在基于CVS的开放源代码程序很多，我认为，CVS确实有助于从JBuilder内部快速地开发新的程序代码。

JDJ：目前我已经注意到了所谓的开放数据库支持技术。JBuilder 的数据库特性又怎么样？

de la Lama：我们对新产品的数据库支持尤感自豪。Borland一直就擅于建立数据库开发的良好环境，以前我们创建了三种版本的数据库开发产品dataexpress。这是用来建立用户应用程序的软件框架。它将用户同令人厌烦的JDBC编码隔离开来。这样只要使用dataexpress 和它所提供的开发工具就使得用户可以获得Jdatastore所带来的强大能力（该产品将升级到版本4），所谓的Jdatastore是一种全面基于Java的数据库。它是一种完全面向对象的小型数据库，可以让用户在本地建立可靠的应用程序。目前新品的专业版和企业版中都包含了该功能。在企业版中你可以获得dataexpress产生的实际框架源代码。对数据库编程而言这确实是很有价值的。

JDJ：我估计人们会首先试用JBuilder的评估版。也许你可以谈谈你们的Web站点及其内容。

de la Lama：我们以前销售一种叫做JBuilder Standard的标准版本。该版本售价99美元并且包含了完全版的核心功能，比如编辑、调试、编译和项目管理等。我们目前推出的此类版本已经改为基础（Foundation）版，该版本是免费的。其软件光盘具有JBuilder的全部功能，不会过期，也不会有类的限制。不过这一版本没有专业版和企业版中所包含的数据库和企业开发特性。该版本可以运行在Solaris、Linux和Windows平台之上。它装备了一个高效的编译器。如果你再没有其他需求的话，只管使用基础版就行了！如果还需要其他功能就要考虑使用我们的专业版和企业版JBuilder 4了。

作者简历

David Johnson是Verge Technologies Group公司的首席执行官, 他的电子邮件地址是djohnson@vergecorp.com  。


JBUILDER 5新增功能介绍

作者：大哥

email: er@wsh.sdf

日期：2001-5-27 17:28:46

JBUILDER 5新增功能

 ----JBuilder 5带来了 100多种新增功能，这将使

Java开发更方便快捷。

/*

Blueski制作编译

2001-05-18

前言：

JBuilder是目前可选的为数不多的Java强力开发工具。2001-05-15 Borland宣布了JBuilder5，并将于2001年6月上市。新产品在功能上、协同管理、对J2EE和XML的支持等方面均有很大改进。小龙亭以最及时的速度为您提供以下介绍。

更多信息可去 http://www.borland.com/jbuilder/jb5

*/

产品性能Productivity

改进了和大多数目前在Java&#8482; 2平台上提供解决方案方面取得广泛声誉的可视化开发工具的产品相一致的性能。

开发者Productivity

VisualAge&reg; for Java form designer support

Themed audio feedback with customizable audio themes

AppBrowser Editor特性

Macintosh&reg; keymap

CodeWarrior&reg; for Mac&reg; keymap

BeansExpress&#8482; ，用于快速创建Java组件

Enhanced! Creation of property getter/setter methods from existing members

Synchronized scrolling between BeansExpress&#8482; and implementation code

Iterative design of EJB&#8482; Entity Beans

向导Wizards

Web Start wizard creates JNLP and generates home page with Java plug-in detection

Deployment with Archive Builder&#8482;

Enhanced! Archive Builder&#8482; wizard to quickly build deployable archive types for Applets, Applications, JavaBeans and OpenTools

Web archive (WAR) support for packaging Web applications

Enhanced! Java archive and PkZIP&reg; (JAR and ZIP) format support

Web Start Applet and Application packaging support

Deployment of user defined file types

Adding multiple classes or entire packages to deployment archive

J2EE application client packaging

Resource adapters archive (RAR) support for packaging J2EE EIS connectors

Enterprise archive (EAR) support for packaging an entire set of components of an application

Sample Applications

XML tutorial showing examples of validation, databinding, XSL, database import/output and more

Borland bookstore application to demonstrate InternetBeans Express&#8482; with DataExpress&#8482; for EJB and more

JBuilder安装支持

Product uninstall

Dynamic product license registration

Product install from server

Generic product installation for unsupported platforms

CVS client installation

Java/XML 开发

充分利用跨平台XML的优势。可以使用源代码和XML和HTML的输出的浏览器显示来转换XML文档。对XML文档进行合法性检查并跟踪错误。JBuilder使用工具来定义结构和规则，数据绑定以及对XML文档可编程的操作，简化了XML和数据库文件之间的数据传输。

普通的XML支持

Project support for XML file types: XSD, XSP, DTD, XSL, MAP (XML-DBMS), WML, XHTML

Browser based XML viewer

Tree view for XML in browser view

XML 环境

JAXP and DOM integrated library integration

Xerces (XML) and Xalan (XSLT) integration

Validation with error tracing

Transformation with source and browser view of both XML and HTML output

Transform trace for selection, generation and templates

Cocoon publishing engine integration for creating XML web applications

XML based Web application projects with Cocoon runtime integration

JavaBeans Binding and Persistence

Generate XML instance from DTD

Generate DTD from XML

Generate Java classes from DTD

Generate Java classes from Schema

Open architecture for 3rd party data-binding solutions

领先的 XML支持

SAX parser generator wizard

XML ModelTableBean and XML ModelSQLBean to import/export DTD/XML data from or to relational databases

XML TemplateTableBean and XML TemplateSQLBean to transform automatically generated XSL style sheets from XML to HTML

XML mapping generation from DBMS

XML SQL generation from DBMS

XML SQLBean and XML TemplateSQLBean to generate XML packets complying to Borland XML DTD on declarative SQL templates

团队开发

JBuilder 5 简化了大型团队的开发协同管理，这是通过提供对Rational&reg; ClearCase&reg;, CVS, 和Microsoft&reg; Visual SourceSafe.&#8482;的支持来实现的。另外，还改进了目前的source code management，通过使用带有高生产性工作流模型的integrated concurrent version system repository engine，可以通过网络和Internet进行协作。 使用了history revision browser来可视化地更新和解决版本冲突，协调workspace和repository之间的改变，包括了源码级别的差异显示，唯一性的source revisions和在source editor中的冲突导航等，增加了to-do lists和to-do Javadoc comments来帮助在开发过程中保持对工作的跟踪。

受支持的版本控制系统

Enhanced! CVS (Concurrent Version System) support

Rational&reg; ClearCase&reg; version 4.1 support

Microsoft&reg; Visual SourceSafe&#8482; support

Merant&#8482; PVCS&reg; Version Manager and Dimension support through SCC

Integrated Team Development

Check out from branch

Set version label: per file, per branch, per project

Synchronized scrolling between source and history pane

Difference view of arbitrary files

CVS Specific

Create local CVS repository

Enhanced! CVS commit browser to resolve conflicts

CVS file locking support with edit, un-edit and watches

CVS server add-on to list projects and branches

Create project from CVS repository

Rational&reg; ClearCase&reg; Specific

View editor for selecting, creating and modifying ClearCase views

Checking in and checking out file to and from Version Object Base (VOB) view and undo check-out

Direct protocol support for ClearCase

Create project from VOB

Place project into VOB

Microsoft&reg; Visual SourceSafe&#8482; (VSS) Specific

VSS commit browser to resolve conflicts

Pull and Post files from and to VSS project

Direct protocol support for VSS

Create project from VSS

Place JBuilder project into VSS

Merant&reg; PVCS&#8482; Specific **

PVCS management through SCC

Pull and Post files from and to PVCS project

Create project from VS Place JBuilder project into VSS

** 在 Companion Tools CD上单独提供;仅用于Windows平台;Borland不提供技术支持。

Web开发

JBuilder 5 通过与最新的Tomcat 3.2 servlet engine的集成,使得Web开发变得更为容易。Web deployment wizards, Java Web Start support, WAR archives, 一个已经集成了的Web browser和runtime environment for servlets and JSP, remote debugging of servlets, J2EE 1.3-complaint Web development support （包括tag libraries, filters）以及Web deployment descriptors（包括WAR files）。

Web 开发

Servlet Engine Integration

Tomcat 3.2 with Servlet 2.2 and JSP 1.1

Ready for Tomcat 4.0 with Servlet 2.3 and JSP 1.2

Servlet engine OpenTools sample documentation

WebLogic&reg; 5.1 and 6.0 Servlet engine support

Web 配置实施

Advanced error detection of embedded Java source in JSP, by pre-compile before deploy to Servlet engine

Web Start Application wizard

Web Start Applet wizard

WAR Web application archives

Two-way descriptor editor according to Servlet 2.2/2.3 specification

Java Web Start support

Java Web Start ready applications (JNLP) creation

Java Network Launching Protocol (JNLP) support v1.0.0

Web Start homepage generation with Java plug-in detection

集成的 Web开发

Unified configuration of AppServer for JSP, Servlet and EJB when used as Web server

JavaScript&reg; execution and viewing for most common JavaScript tasks

Advanced Web Development for Java Servlet support

Servlet 2.3 filters and listeners

Enterprise Application Development

使用Enterprise JavaBeans进行快速开发和实施J2EE applications, Enterprise Archives (EAR), Resource Archives (RAR)以及Web Archives (RAR)，为最新的WebLogic 6，WebSphere和新的Borland AppServer 4.5提供无缝支持。使用了经过改进的EJB Test Client wizard, 新的DataExpress for EJB和 增强了的entity bean modeler来建立和实施分布式应用。

Supported Application Servers

Borland AppServer (BAS) 4.5 with EJB 1.1 and J2EE 1.2

Enhanced! BEA&reg; WebLogic&reg; 5.1 with EJB 1.1

BEA WebLogic 6.0 with EJB 1.1 and J2EE 1.2

IBM&reg; WebSphere&reg; 3.5 with EJB 1.0

Enterprise Deployment Archives (EAR)

EJB Archives according to J2EE 1.2 specification

EAR Group file nodes

Import existing external archives into EAR group

Import from existing EJB groups

EAR packaging, deployment and run for Borland AppServer 4.5 and WebLogic 6

J2EE application client JAR files

EJB Test Client wizard

Boundary testing

Generate test clients from a pre-packaged EJB archive

DataExpress for EJB&#8482;

DataExpress for EJB for seamless access of session beans and entity beans through DataExpress

DataExpress access beans SessionBeanConnection, EJBClientDataSet, EntityBeanProvider, EntityBeanResolver to connect back-end business logic to any front-end seamlessly

Rapid J2EE&#8482; compliant development and deployment

Local and remote Hot-Deploy deployment to running EJB container

JMS wizard for creating message producers and consumers

Generate or update source implementation skeletons from remote interface

Resource adapters archive (RAR) support for packaging J2EE EIS connectors

Entity Bean Modeler&#8482;

Enhanced! User defined mapping between table metadata and Java data types

Generate BMP source code from table relations

Borland&reg; AppServer&#8482; (BAS, formerly IAS)

Borland AppServer 4.5 development license, a complete EJB 1.1 implementation

VisiBroker&reg; 4.5 for CORBA&reg; ORB&#8482; with full support for OMG&#8482; 2.3 spec and integration into BAS

BEA&reg; WebLogic&reg; Server Support

Integrated WebLogic Server 6.0 (EJB 1.1) development support

Enhanced! Integrated WebLogic Server 5.1.x development support

IBM&reg; WebSphere&reg; Server Support

Integrated WebSphere 3.5 development support

Support for BMP Entity Bean creation

Targeted deployment to WebSphere Server

Integrated Two-Way-Tools for managing and deploying beans, including Deployment Descriptor Editor, Entity Bean Modeler and more

3rd Party Tools Integration

Sitraka&#8482; JClass&#8482; development license

/n software IP*Works! Beans Internet components development license

要了解更多信息可参考 JBuilder Feature Matrix.

(the end)


Jbuilder4.0 使用心得

作者：令少爷

email: dj12@8848.net

日期：2001-6-18 11:13:28

Jbuilder4.0 使用心得之一

*问题描述：

在进行Java程序开发时，不可避免会使用到第三者开发的源程序包(如lulusoft.common)，如何将第三者的源程序包正确集成到到自己的源程序中?使用第三者的源程序包，一般情况下并不修改其源程序，认为其源程序是正确的，并要可以在自己的多个项目文件(.jpr)中使用。

*表述假定：

为方便下面的描述，我们假定如下。

1.自己的项目描述：

工程名称：Myproject.jpx

Src目录：../jbuilder4/Myproject/src/

Lib目录：../jbuilder4/Myproject/lib/

Bak目录：../jbuilder4/Myproject/bak/

Doc目录：../jbuilder4/Myproject/doc/

2.第三者源程序包描述：

包名定义:HTMLPARSER

组织结构：com.3rd.html.parser.*

存放位置：D:\Java3rdPackage

*解决方法：

A. 如果仅在自己的一个单独的项目文件(.jpr)中使用的话，处理方法很简单。设置方法描述如下：

1.新建自己的工程。

2.将第三者的源程序按其目录结构加入到自己的工程的源程序目录中。示例如下：

将此源程序包 com.3rd.html.parser.* (含目录结构与源程序，不含\Java3rdPackage)拷贝到自己新建项目之源程序目录(../jbuilder4/Myproject/src/)中。

3.在JBuilder4的IDE环境的操作条中，单击[Refresh]刷新按钮，源程序包将出现在自已的项目文件中。

B.如果要在自己的多个项目文件(.jpr)文件中使用第三者的源程序包，也使用以上的方法的话，将会造成一套第三者源程序包在多个项目的Src目录中重复拷贝。如果自己修改了第三者源程序包的话，将会使第三者源程序包出现版本差异。为了解决这个问题，可以将第三者源程序包与自己的项目分开存放，设置方法描述如下：

1.定义第三者源程序包为Libariry。

在JBuilder IDE环境中，进行菜单操作[Tools]-[Config Libariries...]；

在弹出的[Config Libariries]对话框中，单击[New]，根据Libariry Wizard进行库设置。设置新包名为HTMLPARSER。在设置时要注意的是其Source指定时，只须指定到其目录结构的前一级(即只须指定Source为d:\Java3rdPackage)；而其Lib应为空(因为可以由源程序编译自动生成lib的.class文件)。

2.在自己的项目中进行设置。

在JBuilder IDE环境中，进行菜单操作[Project]-[Project Properties...];

在弹出的[Project Properties]对话框中，在Source栏增加第三者源程序包的所在目录(D:\Java3rdPackage)；在[Required Libaries]栏中增加新包名HTMLPARSER，关闭对话框。

在项目窗(Project Panel)中，选中项目文件Myproject.jpx；

右击鼠标；在弹出菜单中，单击[Add files|Packages...]；

在弹出的[Add files or Packages to project]对话框中，单击[packages]页，选取com.3rd.html.parser包，单击[OK]按钮;

回到在项目窗(Project Panel)中，可见项目文件Myproject.jpx中增加了com.3rd.html.parser包。

在JBuilder4的IDE环境的操作条中，单击[Refresh]刷新按钮，源程序包将变成Automatic source package包，也出现在自已的项目文件中。

至此，大功告成。


JBuilder5 + Weblogic 6.0 安装配置全攻略（同事老魏写的）

作者：hp.q

email: hp.q@263.net

日期：6/18/2001 1:38:01 PM

一、安装JBuilder5 （秦海鹏提供）

        1、安装Jbuilder5

        2、启动Jbuilder5，你会发现启动不起来，这时你到 \jbuilder5\bin\jbuilder.config 文件中把 “mainclass com.borland.jbuilder.JBuilder” 这一行加上“#”号注释掉，再启动Jbuilder5，Jbuilder5就会报一个错，下面再一次修改 \jbuilder5\bin\jbuilder.config 文件，把 “#mainclass.borland.jbuilder.JBuilder” 这一行的“#”号删掉，再启动Jbuilder5，OK

（hp.q注：这一个并不正确，其实是我瞎改凑巧改对的，后来我分析是因为我们装了vss的缘故，只要去掉vss的两行就可以了。

）

        3、Jbuilder密码： xa52-?npkv-gcqzw

                     m69-tet

二、安装配置Weblogic（Bob's Home Page提供）

    BEA WebLogic Server 6.0 Final <http://commerce.beasys.com/downloads/weblogic_server.jsp#wls>Crack: bea_wls60final_crack.zip <http://go.163.com/~bob0215/bea_wls60final_crack.zip> (size: 16KB)

    用法：比如我将 WebLogic Server 6.0 Final 安装在 C:\bea 目录下，将上面的 bea_wls60final_crack.zip <http://go.163.com/~bob0215/bea_wls60final_crack.zip>解开后，将其中的 cr_wls60f.jar 拷到 C:\bea\wlserver6.0\lib 目录下，将 license.bea 拷到 C:\bea 目录下(主要是改expiration="never", units="unlimited"就可以了)，将 C:\bea\wlserver6.0\config\mydomain 目录中的 startWebLogic.cmd 文件中的

   set CLASSPATH=.;.\lib\weblogic_sp.jar;.\lib\weblogic.jar

   一行改为：

   set CLASSPATH=.\lib\cr_wls60f.jar;.;.\lib\weblogic_sp.jar;.\lib\weblogic.jar

    然后运行 .\startWebLogic.cmd. To confirm the server is running, visit http://127.0.0.1:7001/console/index.jsp.Enjoy !

    注意：这里的 mydomain / myserver 是在安装时选择的缺省名字，请用相应的实际名字代替。

    上面的 bea_wls60final_crack.zip <http://go.163.com/~bob0215/bea_wls60final_crack.zip>中还提供了 examples & petstore 的相应启动文件供大家参考。

三、配置Jbuilder5（魏文麒提供）

    1、tools->enterprise setup

              一定要配IAS4.1，（如有BAS4.5配BAS4.5更好）不然EJB出不来！

       配Weblogic6.0只要把Weblogic6.0的路径设好即可。

    2、project->default project properties

              配置run面板：VM parameters的值在“-ms64m -mx64m ”后加入

       “ -classpath .\lib\cr_wls60f.jar;.;.\lib\weblogic_sp.jar;.\lib\weblogic.jar ”

       这样在Jbuilder5中调用Weblogic6.0时权限就不会有问题了！

       配置servers面板：选择weblogic6.0为应用服务器，建议选上“application server is web server”项，这样jsp就也用weblogic6.0来调试了（而不是tomcat）

    3、其他配置与jbuilder4+weblogic5.1的配法相同。

    以上配置均在机器上正常运行！


jBuiler 4白皮书(来自inprise网站)

作者：sonymusic

email: sonymusic@china.com

日期：2000-10-21 23:40:23

纯Java可视化开发

JBuilder 已成为当今开发者首选的Java?开发环境。它将Web和企业应用开发与灵活高效的团队开发环境结合在一起，为开发者提供了一个端对端应用开发环境。JBuilder支持最新的Java标准，它的可视化工具和向导使应用开发变得方便快捷。JBuilder同时支持Windows、Linux 和Solaris 三种开发平台。由于JBuilder具有开放性、可扩展性、遵循标准等特点，使得用JBuilder开发符合J2EE标准的电子商务应用、分发Internet分布式的关键性企业级应用、建立互联数据库、设计数据驱动的动态网页应用以及开发servlets、JSPs异常快捷，从而加速产品推向市场的周期。JBuilder支持最新的Java技术，包括applets、JSP/Servlets、JavaBeans、Enterprise JavaBeans、CORBA应用。JBuilder4 的IDE环境包括：

1.AppBrowser.可以用XML方式进行项目管理；

2.StructureInsight.可以实时反映程序结构状况；

3.HTML和 XML浏览器；

4.先进的图形化调试环境；

5.CodeInsight.编程向导可以智能感知代码书写的状况；

6.可由用户任意扩展的代码编辑器；

7.双向工具(Two-Way-Tools)；

8.可视化的JFC/Swing设计器；

9.BeansExpress；

10.DataExpress；

11.闪速编译器。

高效团队开发

JBuilder4的集成团队开发环境非常灵活并具有可扩展的特性，简化了大型分散开发团队的原代码的并发管理。JBuilder4 集成的CVS源码库引擎在局域网和互联网上扩展了原有的代码管理。历史修订浏览器使工作空间（当前代码）和源码库（历史代码）协同操作，完成版本更新、解决版本冲突问题。在IDE中，你可以看到修改过的代码被显式地标志出来，你可以浏览这些更改信息和冲突标志。你还可以使用计划列单（to do lists）和计划Javadoc注释（to do Javadoc comments）来跟踪编程进展。

快速生成EJB

JBuilder4 拥有一个可视化的双向EJB设计器，用于定义EJB的属性、BeanInfo、事件和方法。开发者只要轻点鼠标既可以生成纯JAVA的EJBs和JavaBeans。使用JBuilder4 的EJB分发描述符编辑器，开发者能可视化地编辑EJB的环境属性、分发控制描述符，实现与特定应用服务器无关的EJB分发。对于Entity Bean，开发者可以用Entity Bean Modeler可视化地创建数据库对象关系映射，将数据源、表、索引和域直接映射到Entity Bean上。

支持各种应用服务器

JBuilder与Inprise Application Server紧密集成，同时支持WebLogic Server,支持EJB 1.1和准EJB 2.0，可以快速开发分发J2EE的电子商务应用。在开发环境中，既可以本地也可以远程运行调试EJB。在分发EJB时，你无须再次启动应用服务器。

用Servlets和JSPs开发和调试动态Web应用

JBuilder4向导提供的可视化工具，既可以创建瘦客户端程序也能够创建由任意Web 浏览器访问的纯Java程序。在IDE环境中，对JSP内嵌代码可设置断点进行调试，IDE环境还能动态感知这些代码。对于Servlets，开发者可以在本地或远程的Servlet容器中对之进行调试。

用DataExpress和InternetBeans创建专业数据库应用

dbSwing组件库使用DataExpress，直接支持Swing数据模式。用这些数据感知dbSwing组件（如enhanced Tree、List、Table等），可以很容易地开发数据库应用。各种各样的InternetBeans组件让开发者能快速生成数据驱动的动态Web应用。利用DataExpress组件及EJB技术，可将商务逻辑和高速Web前端程序快速连接在一起。通过XSQL查询，XML Servlet可为任意的JDBC数据源生成XML形式的数据集。使用JDataStore?4,可以有效地管理数据。JdataStore是一个纯Java、对象关系型的数据库系统，由于具有占用资源小、零管理和高性能等特性，所以适用于 Web、移动设备、嵌入式数据库系统。

创建(没有专有代码和标记)纯Java2应用

JBuilder4是用纯Java 语言编写的，其代码不含任何专属代码和标记。Jbuilder 4支持最新的Java标准，包括Java 2,Java 2 Swing/JFC, XML, Java2D,Java Collections, Message Queue, Accessibility APIs, JDK 1.1, JavaBeans, JDBC, Enterprise JavaBeans, JSP/Servlets, Serialization, InnerClasses, RMI, JNI, Java Archives。开发者可以通过JDK开关选择需要使用的JDK版本，包括J2ME、J2SE、J2EE或其它JDK。一旦进入新的JDK环境，就可以用它编译、运行、调试代码。

先进灵活的调试

JBuilder拥有专业化的图形界面调试，支持远程调试和多线程调试。调试器支持各种JDK版本，包括J2ME、J2SE和J2EE，支持多JDK。开发者可以在调试过程中设置条件断点或跨进程断点。在断点处，可以检测、修改变量，同时查看多个线程，可以在本地或远程代码中单步调试定位。用调试器可以有效地排除线程死锁。在调试时可任意停止或忽略条件判断。远程调试还提供一个浏览界面，用于查看运行在多个JVM的所有进程，这些进程可能分布在各种操作系统之下，如HP-UX,Tru64,AIX等等，支持Java2调试的任何环境。

快速编程

JBuilder有一个可扩展的源码编辑器。它的CodeInsight向导自动访问相关Java对象的属性、事件、方法和包的上下文提示。开发者可以选择自己习惯的IDE键盘风格，象CUA、Emacs、Visual Studio或Brief，也可以可视化地定义自己的快捷键。

灵活开放的IDE架构

你可以使用开放工具API将一些附加的或第三方工具紧密集成到JBuilder环境中，扩展IDE的功能。

完整的企业方案

JBuilder是Borland企业级解决方案的重要组成部分，该方案为用户开发J2EE兼容电子商务应用提供：

1.JBuilder----开发工具；

2.Inprise Application Server---运行和分发环境；

3.AppCenter----管理平台。


JCWC exam is available now

作者：jeru

email: jeru@163.net

日期：2001-7-24 14:22:54

http://www.certit.com/default.htm?head2.htm&2

Sun to release new Java exam

Sun has announced that it will release Java Certified Web Component Developer exam on July 31.

According to Sun, this certification is for Sun Certified Programmers for Java[tm] 2 Platform who are using the Java technology servlet and JavaServer Pages[tm] (JSP[tm]) APIs to develop Web applications. Certification is available for the Java 2 Platform, Enterprise Edition (J2EE[tm]). The certification consists of just one exam. Its cost is $ 150.

The exam, numbered 310-080, will consist both multiple choice and short answer questions. The prerequisite for the exam is Sun Certified Programmer for Java Platform. Sun, however, says that number of questions, passing score and duration has not yet been finalized.

For more info, click: http://suned.sun.com/US/certification/java/java_web.html

For exam objectives, click: http://suned.sun.com/US/certification/java/exam_objectives.html#web


JDBC Connection Pool

作者：jeru

日期：2000-12-7 12:04:45

数据库连接池简介

在传统的两层结构中，客户端程序在启动时打开数据库连接，在退出程序时关闭数据库连接。这样，在整个程序运行中，每个客户端始终占用一个数据库连接，即使在大量没有数据库操作的空闲时间，如用户输入数据时，从而造成数据库连接的使用效率低下。

在三层结构模式中，数据库连接通过中间层的连接池管理。只有当用户真正需要进行数据库操作时，中间层才从连接池申请一个连接，数据库操作完毕，连接立即释放到连接池中，以供其他用户使用。这样，不仅大大提高了数据库连接的使用效率，使得大量用户可以共享较少的数据库连接，而且省去了建立连接的时间。

连接池的配置使用

数据库连接池是应用服务器的一项基本功能，我们以Apusic Application Server为例，来说明JDBC连接池的配置使用。

Apusic JDBC连接池提供对多种数据库的支持，如Oracle、MS SqlServer、Sybase、Informix、DB2等。

Apusic JDBC连接池可以通过数据库本身的JDBC Driver连接到数据库，也可以通过JDBC-ODBC桥连接到数据库。下面我们以Oracle为例说明如何配置连接池：

Oracle数据库的JDBC Driver包文件classes111.zip在/usr/oracle/jdbc/lib(假设oracle的安装目录是/usr/oracle)目录下，首先将classes111.zip加入到系统的CLASSPATH中。然后在apusic/config/apusic.conf(假设安装目录为apusic) 中作如下设置：

<SERVICE

CLASS="com.apusic.jdbc.PoolManager"

NAME="JdbcPool:name=jdbc/sample"

>

<ATTRIBUTE NAME="ExpirationTime" VALUE="300"/>

<ATTRIBUTE NAME="MinCapacity" VALUE="5"/>

<ATTRIBUTE NAME="URL" VALUE="jdbc:oracle:thin:@192.168.19.136:1521:orcl"/>

<ATTRIBUTE NAME="ConnectionProperties" VALUE="user=gtj,password=abc123"/>

<ATTRIBUTE NAME="DriverClassName" VALUE="oracle.jdbc.driver.OracleDriver" />

<ATTRIBUTE NAME="MaxCapacity" VALUE="30"/>

</SERVICE>

ExpirationTime:       超时时间，单位是秒。当一个数据库连接超过expirationTime设定时间不被使用

                      时,系统会自动关闭这个数据库连接。默认值为300秒

MinCapacity:          最小连接数

URL:                  数据库的URL

ConnectionProperties: 连接属性，其中：user用户名，password密码

DriverClassName:      JDBC驱动程序类名

MaxCapacity:          最大连接数

192.168.19.136:       oracle所在计算机的IP地址。

调用连接池

我们以一个JSP程序为例，说明如何使用连接池。首先通过JNDI得到DataSource，再的得到连接Connection，如下例所示：

<html>

<head>

<title>Jsp sample</title>

</head>

<body>

<p>

<%@ page contentType="text/html;charset=gb2312" %>

<%@ page import="

java.sql.*,

javax.naming.*,

javax.sql.*

"%>

<%

try{

    Context ctx = new InitialContext();

    DataSource ds = (DataSource)ctx.lookup("jdbc/sample");

    Connection con = ds.getConnection();

    Statement stmt = con.createStatement();

    ResultSet rs = stmt.executeQuery("select ENAME from EMP");

    while(rs.next()){

        out.println("<p>" + rs.getString(1));

    }

    rs.close();

    stmt.close();

}catch(Exception e){

    System.out.println("jsp:" + e.getMessage());

}finally{

    try{

        con.close();

    }catch(Exception e1){}

}

%>

</body>

</html>


JDBC 解决方案(转)

作者：令少爷

email: dj12@8848.net

日期：2001-7-10 9:13:02

JDBC 解决方案

Java的兴起和发展加速了Internet与Intranet应用的开发。越来越多的企业使用不受平台限制的Java语言，开发复杂的商务

关键任务。

作为Java语言的创始者，JavaSoft公司早就预见到市场需要一项针对Java的数据存取技术，并早已与MERANT公司联手开发

Java JDBC技术，作为Java数据库连接技术的一项标准。

JDBC的四种解决方案

JDBC是通用的SQL(结构化查询语言)数据库访问框架，带有简单且功能强大的应用程序接口(API)。利用JDBC API，Java开发

人员可以在单一的标准化功能库中编码，生成独立于数据库管理系统(DBMS)的Java应用系统。

JDBC以X/Open组织的标准定义SQL调用级接口(Call Level Interface，CLI)。X/Open组织的宗旨是提供基于开放式系统的

数据存取标准，实现相互兼容。JDBC就是使用单一标准化界面存取多种数据源，以实现应用系统间的互操作。

JavaSoft将JDBC驱动程序细分为四大类，分别是JDBC/ODBC Bridge、NativeAPI PartlyJava、NetProtocol All

Java及Native Protocol AllJava。

JDBC/ODBC Bridge是一项瘦编译元件，能将JDBC功能调用转换为ODBC功能调用。使用JDBC/ODBC Bridge，Java开发人员编

写的兼容JDBC的应用系统及小型应用程序，可与目前市场上任何ODBC连接解决方案一并使用。

NativeAPI PartlyJava驱动程序主要将JDBC调用转换为指定专用数据库系统的客户机API，从而利用SQL＊NET、I

Connect及OpenClient分别连接到Oracle、Informix及Sybase等数据库上。使用PartlyJava驱动程序的最大优点是，可

利用企业内现有的数据库中间件。

NetProtocol AllJava驱动程序可将JDBC调用转换成为不受任何数据库限制的网络协议，服务器可把这一网络协议编译

为数据库协议。这项JDBC解决方案只有一个单一通用的全Java驱动程序，可连接许多不同的数据库。采取哪种网络协议作为

中介，取决于JDBC驱动程序供应商。

NativeProtocol AllJava驱动程序是数据库专用JDBC驱动程序，可将JDBC调用转换为数据库可直接使用的网络协议，

客户机因此可直接访问数据库服务器。从根本上说，NativeProtocol AllJava驱动程序能以一个纯粹本地的Java JDBC

客户机，全面取代客户机API，如SQL＊NET、IConnect及OpenClient等。

在不同环境下执行JDBC驱动程序

业界现有多种选择建立JDBC连接。选择JDBC解决方案主要应考虑所使用的Java程序的结构特点。

Java的最大优点是其不受平台限制的通用特性，若选用包含非Java元件的执行方案，或选用指定数据库类型的执行方案，

会严重影响通用性。

在选择的过程中，应兼顾短期和长期目标。对于寻求近期目标的用户，如果选择条件牵涉C语言和C＋＋语言，那么必须同

时考虑特定数据库供应商的API或其他专用技术，这时NetProtocol AllJava解决方案可能是相对较可靠的策略，有助

于充分实现跨网络的数据连接。


JDBC 解决方案

作者：jeru

日期：2000-12-9 18:57:57

Java的兴起和发展加速了Internet与Intranet应用的开发。越来越多的企业使用不受平台限制的Java语言，开发复杂的商务

关键任务。

作为Java语言的创始者，JavaSoft公司早就预见到市场需要一项针对Java的数据存取技术，并早已与MERANT公司联手开发

Java JDBC技术，作为Java数据库连接技术的一项标准。

JDBC的四种解决方案

JDBC是通用的SQL(结构化查询语言)数据库访问框架，带有简单且功能强大的应用程序接口(API)。利用JDBC API，Java开发

人员可以在单一的标准化功能库中编码，生成独立于数据库管理系统(DBMS)的Java应用系统。

JDBC以X/Open组织的标准定义SQL调用级接口(Call Level Interface，CLI)。X/Open组织的宗旨是提供基于开放式系统的

数据存取标准，实现相互兼容。JDBC就是使用单一标准化界面存取多种数据源，以实现应用系统间的互操作。

JavaSoft将JDBC驱动程序细分为四大类，分别是JDBC/ODBC Bridge、NativeAPI PartlyJava、NetProtocol All

Java及Native Protocol AllJava。

JDBC/ODBC Bridge是一项瘦编译元件，能将JDBC功能调用转换为ODBC功能调用。使用JDBC/ODBC Bridge，Java开发人员编

写的兼容JDBC的应用系统及小型应用程序，可与目前市场上任何ODBC连接解决方案一并使用。

NativeAPI PartlyJava驱动程序主要将JDBC调用转换为指定专用数据库系统的客户机API，从而利用SQL＊NET、I

Connect及OpenClient分别连接到Oracle、Informix及Sybase等数据库上。使用PartlyJava驱动程序的最大优点是，可

利用企业内现有的数据库中间件。

NetProtocol AllJava驱动程序可将JDBC调用转换成为不受任何数据库限制的网络协议，服务器可把这一网络协议编译

为数据库协议。这项JDBC解决方案只有一个单一通用的全Java驱动程序，可连接许多不同的数据库。采取哪种网络协议作为

中介，取决于JDBC驱动程序供应商。

NativeProtocol AllJava驱动程序是数据库专用JDBC驱动程序，可将JDBC调用转换为数据库可直接使用的网络协议，

客户机因此可直接访问数据库服务器。从根本上说，NativeProtocol AllJava驱动程序能以一个纯粹本地的Java JDBC

客户机，全面取代客户机API，如SQL＊NET、IConnect及OpenClient等。

在不同环境下执行JDBC驱动程序

业界现有多种选择建立JDBC连接。选择JDBC解决方案主要应考虑所使用的Java程序的结构特点。

Java的最大优点是其不受平台限制的通用特性，若选用包含非Java元件的执行方案，或选用指定数据库类型的执行方案，

会严重影响通用性。

在选择的过程中，应兼顾短期和长期目标。对于寻求近期目标的用户，如果选择条件牵涉C语言和C＋＋语言，那么必须同

时考虑特定数据库供应商的API或其他专用技术，这时NetProtocol AllJava解决方案可能是相对较可靠的策略，有助

于充分实现跨网络的数据连接。


JDBC 入门

作者：zergman

email: zergman@chinaasp.com

日期：6/12/2001 1:21:07 PM

JDBC 入门 -- 建立联接

教程：JDBC 入门 作者：Maydene Fisher 翻译：comer

    你需要做的第一事情是你与想要使用的 DBMS 建立一个连接。这包含 2 个步骤：装载驱动程序并建立连接。

装载驱动程序

    装载驱动程序只需要非常简单的一行代码。例如，你想要使用 JDBC-ODBC 桥驱动程序, 可以用下列代码装载它：

        Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");

    你的驱动程序文档将告诉你应该使用的类名。例如， 如果类名是 jdbc.DriverXYZ ，你将用代码以下的代码装载驱动程序：

        Class.forName("jdbc.DriverXYZ");

    你不需要创建一个驱动程序类的实例并且用 DriverManager 登记它，因为调用 Class.forName 将自动将加载驱动程序类。如果你曾自己创建实例，你将创建一个不必要的副本，但它不会带来什么坏处。

    加载 Driver 类后，它们即可用来与数据库建立连接。

建立连接

    第二步就是用适当的驱动程序类与 DBMS 建立一个连接。下列代码是一般的做法：

        Connection con = DriverManager.getConnection(url, "myLogin", "myPassword");

    这个步骤也非常简单，最难的是怎么提供 url。如果你正在使用 JDBC-ODBC 桥， JDBC URL 将以 jdbc:odbc 开始：余下 URL 通常是你的数据源名字或数据库系统。因此，假设你正在使用 ODBC 存取一个叫 "Fred" 的 ODBC 数据源，你的 JDBC URL 是 jdbc:odbc:Fred 。把 "myLogin" 及 "myPassword" 替换为你登陆 DBMS 的用户名及口令。如果你登陆数据库系统的用户名为 "Fernanda" 口令为 "J8"，只需下面的 2 行代码就可以建立一个连接：

String url = "jdbc:odbc:Fred";

Connection con = DriverManager.getConnection(url,"Fernanda", "J8");

    如果你使用的是第三方开发了的 JDBC驱动程序，文档将告诉你该使用什么 subprotocol， 就是在 JDBC URL 中放在 jdbc 后面的部分。例如, 如果驱动程序开发者注册了 acme 作为 subprotocol， JDBC URL 的第一和第二部分将是 jdbc:acme。驱动程序文档也会告诉你余下 JDBC URL 的格式。JDBC URL 最后一部分提供了定位数据库的信息。

    如果你装载的驱动程序识别了提供给 DriverManager.getConnection 的 JDBC URL ，那个驱动程序将根据 JDBC URL 建立一个到指定 DBMS 的连接。正如名称所示，DriverManager 类在幕后为你管理建立连接的所有细节。除非你是正在写驱动程序，你可能无需使用此类的其它任何方法，一般程序员需要在此类中直接使用的唯一方法是 DriverManager.getConnection。

    DriverManager.getConnection 方法返回一个打开的连接，你可以使用此连接创建 JDBC statements 并发送 SQL 语句到数据库。在前面的例子里，con 对象是一个打开的连接，并且我们要在以后的例子里使用它。

JDBC 入门 -- 设置表

创建表

    首先，我们在我们的示例数据库创建其中一张表 COFFEES，包含在咖啡店所卖咖啡的必要的信息，包括咖啡名字，他们的价格，本星期卖了多少磅及迄今为止卖的数目。关于 COFFEES 表我们以后会详细描述，如下：

COF_NAME SUP_ID PRICE SALES TOTAL

Colombian 101 7.99 0 0

French_Roast 49 8.99 0 0

Espresso 150 9.99 0 0

Colombian_Decaf 101 8.99 0 0

French_Roast_Decaf 49 9.99 0 0

    存储咖啡名的列是 COF_NAME，它的 SQL 数据类型是 VARCHAR，最大的长度为 32 个字符。因为我们所卖的每种类型咖啡都使用不同的名字，名字可用于作为唯一识别咖啡的标识，因此可用于作主键。第二个列叫 SUP_ID，用于保存咖啡供应商标识；其 SQL 数据类型为 INTEGER。第 3 列叫 PRICE，因为它需要保存带小数的十进制数，因此它的 SQL 类型为 FLOAT。（注意，通常钱的 SQL 类型为 DECIMAL 或 NUMERIC，但在不同 DBMSs 间存在差异，为了避免于老版本的 JDBC 的不兼容性在本教程我们采用更标准的 FLOAT 类型）SALES 列的 SQL 类型为 INTEGER，其值为本星期所卖咖啡的磅数。最后一列，TOTAL 的 SQL 类型为 INTEGER，保存了迄今为止所卖咖啡的总磅数。

    数据库里的第二个表 SUPPLIERS，保存了每个供应商的信息：

SUP_ID SUP_NAME STREET CITY STATE ZIP

101 Acme, Inc. 99 Market Street Groundsville CA 95199

49 Superior Coffee 1 Party Place Mendocino CA 95460

150 The High Ground 100 Coffee Lane Meadows CA 93966

    COFFEES 跟 SUPPLIERS 都包含列 SUP_ID，它意味着可以用 SELECT 语句从这两张表中取得有关信息。列 SUP_ID 是 SUPPLIERS 表的主键，用于唯一识别每个咖啡供应商。在 COFFEES 表中，SUP_ID 列被称外键。注意每个 SUP_ID 值在 SUPPLIERS 表里只出现一次；这对主键是必须的。在 COFFEES 表里，它作为外键，显然它可以有重复的 SUP_ID 值，因为同一供应商可以提供很多种的咖啡。在本节的最后，你将看见如何在 SELECT 语句中使用主键及外键的一个例子。

    下面的 SQL 语句用于创建 COFFEES 表。列由列名跟空格跟 SQL 类型组成。列（包括列名及其 SQL 类型）跟下一个之间用逗号分隔。VARCHAR 类型创建定义了最大长度， 因此它需要有一个参数来表示最大长度。参数必须在类型后面的括号内。SQL 语句如下，列 COF_NAME 的长度 被限定为不得超过 32 个字符：

CREATE TABLE COFFEES

(COF_NAME VARCHAR(32),

SUP_ID INTEGER,

PRICE FLOAT,

SALES INTEGER,

TOTAL INTEGER)

    这些代码不带 DBMS 语句结束符， 因为每个 DBMS 都可能不同。例如, Oracle 使用一个分号 (;) 作为语句的结束，而 Sybase 使用 go。你所使用的驱动程序会自动提供合适的语句结束符，因此你无须把它包括在你的 JDBC 代码中。

    另外，我们应该指出的的是 SQL 语句的格式。在 CREATE TABLE 语句中，关键字采用大写字符，并且每个项目都另起一行。SQL 并没有此要求；仅仅是为了更容易阅读。SQL 标准是不区分关键词的大小写的, 因此，如下例中的 SELECT 语句可以有多种写法。因此下面两个不同写法的语句对 SQL 来说是一样的。

SELECT First_Name, Last_Name

FROM Employees

WHERE Last_Name LIKE "Washington"

select First_Name, Last_Name from Employees where

Last_Name like "Washington"

    然而，引号里的内容是区分大小写的：在名字"Washington" 里 "W" 必须被大写，并且余下的字符必须是小写的。

    对于标识，不同的 DBMS 有不同的要求，例如, 某些 DBMSs 要求那些列名及表名必须跟创建时的一样，有些则没有此要求。为安全起见，我们全部使用大写标识如 COFFEES、SUPPLIERS，因为我们是那样定义他们的。

    到止我们写了创建 COFFEES 表的 SQL 语句。现在我们在它外面加上引号（使它成为字符串），并且字符串赋值给变量 createTableCoffees，在以后的 JDBC 代码中我们可以使用此变量。正如看到的，DBMS 并不在意分行，但对 Java 语言来，String 对象分行是通不过编译的。因而，我们可以用加号 (+) 把每一行的串连接。

String createTableCoffees = "CREATE TABLE COFFEES " +

"(COF_NAME VARCHAR(32), SUP_ID INTEGER, PRICE FLOAT, " +

"SALES INTEGER, TOTAL INTEGER)";

    我们在 CREATE TABLE 语句中使用的数据类型是通用的 SQL 类型（也称 JDBC 类型）它们在类 java.sql.Types 中定义。DBMSs 通常使用这些标准的类型，因此，当你要尝试一些 JDBC 应用程序时，你可以直接使用 CreateCoffees.java 应用程序，它使用了 CREATE TABLE 语句。如果你的 DBMS 使用了它的自己的本地的类型名字，我们为你供应其它的应用程序，我们将在后面详细解释。

    在运用任何应用程序前，当然，我们将让你了解 JDBC 的基础。

创建 JDBC Statements 对象

    Statement 对象用于把 SQL 语句发送到 DBMS 。你只须简单地创建一个 Statement 对象并且然后执行它，使用适当的方法执行你发送的 SQL 语句。对 SELECT 语句来说，可以使用 executeQuery。要创建或修改表的语句，使用的方法是 executeUpdate。

    需要一个活跃的连接的来创建 Statement 对象的实例。在下面的例子中，我们使用我们的 Connection 对象 con 创建 Statement 对象 stmt：

Statement stmt = con.createStatement();

    到此 stmt 已经存在了，但它还没有把 SQL 语句传递到 DBMS。我们需要提供 SQL 语句作为参数提供给我们使用的 Statement 的方法。例如，在下面的代码段里，我们使用上面例子中的 SQL 语句作为 executeUpdate 的参数：

stmt.executeUpdate("CREATE TABLE COFFEES " +

"(COF_NAME VARCHAR(32), SUP_ID INTEGER, PRICE FLOAT, " +

"SALES INTEGER, TOTAL INTEGER)");

    因为我们已经把 SQL 语句赋给了 createTableCoffees 变量，我们可以如下方式书写代码：

stmt.executeUpdate(createTableCoffees);

执行语句

    我们使用 executeUpdate 方法是因为在 createTableCoffees 中的 SQL 语句是 DDL （数据定义语言）语句。创建表，改变表，删除表都是 DDL 语句的例子，要用 executeUpdate 方法来执行。你也可以从它的名字里看出，方法 executeUpdate 也被用于执行更新表 SQL 语句。实际上，相对于创建表来说，executeUpdate 用于更新表的时间更多，因为表只需要创建一次，但经常被更新。

    被使用最多的执行 SQL 语句的方法是 executeQuery。这个方法被用来执行 SELECT 语句，它几乎是使用最多的 SQL 语句。马上你将看到如何使用这个方法。

在表中输入数据

    我们已经显示了如何通过指定列名、数据类型来创建表 COFFEES，但是这仅仅建立表的结构。表还没有任何数据。我们将次输入一行数据到表中，提供每列的信息，注意插入的数据显示顺序跟表创建时候是一样的，既缺省顺序。

    下列代码插入一个行数据，COF_NAME 的值为 Colombian，SUP_ID 为 101，PRICE 为 7.99，SALES 0，TOTAL 0。就象创建 COFFEES 表一样，我们创建一 Statement 对象，并执行 executeUpdate 方法。

    因为 SQL 语句一行显示不下，因此我们把它分为两行，并用加号 (+) 相连。特别要注意的是，在 COFFEES 和 VALUES 之间要有空格。这个空格必须在引号之内并且要在 COFFEES 跟 VALUES 之间；没有这个空格，SQL 语句将被错误地被读作为 "INSERT INTO COFFEESVALUES ..."，并且 DBMS 将寻找表 COFFEESVALUES。还要注意的是在 coffee name 上我们使用了单引号。

Statement stmt = con.createStatement();

stmt.executeUpdate(

"INSERT INTO COFFEES " +

"VALUES ('Colombian', 101, 7.99, 0, 0)");

    下面的代码把第二行插入到表 COFFEES 中。我们可以在使用 Statement 对象而无须为每次执行创建一个新的。

stmt.executeUpdate("INSERT INTO COFFEES " +

"VALUES ('French_Roast', 49, 8.99, 0, 0)");

    剩下行的数据如下：

stmt.executeUpdate("INSERT INTO COFFEES " +

"VALUES ('Espresso', 150, 9.99, 0, 0)");

stmt.executeUpdate("INSERT INTO COFFEES " +

"VALUES ('Colombian_Decaf', 101, 8.99, 0, 0)");

stmt.executeUpdate("INSERT INTO COFFEES " +

"VALUES ('French_Roast_Decaf', 49, 9.99, 0, 0)");

从表中取得数据

    既然表 COFFEES 中已经有数据了，我们就可以写一个 SELECT 语句来取得这些值。下面的 SQL 语句中星号 (*) 表示选择所有的列。因为没有用 WHERE 子句来限制所选的行，因此下面的 SQL 语句选择的是整个表。

SELECT * FROM COFFEES

    结果是整个表的数据，如下：

COF_NAME SUP_ID PRICE SALES TOTAL

--------------- ------ ----- ----- -----

Colombian 101 7.99 0 0

French_Roast 49 8.99 0 0

Espresso 150 9.99 0 0

Colombian_Decaf 101 8.99 0 0

French_Roast_Decaf 49 9.99 0 0

    如果你直接在数据库系统里输入 SQL 查询语句，你将在你的终端上看到如上的结果。当我们通过一个 Java 应用程序存取一个数据库时，正如我们马上要做的一样，我们需要检索结果以便我们能使用他们。你将在下一节看到如何实现。

这是 SELECT 语句的另一个例子，这将得到咖啡及其各自每磅单价的列表。

SELECT COF_NAME, PRICE FROM COFFEES

查询的结果集将具有如下形式：

COF_NAME PRICE

-------- ---------- -----

Colombian 7.99

French_Roast 8.99

Espresso 9.99

Colombian_Decaf 8.99

French_Roast_Decaf 9.99

上面 SELECT 语句取得了所有咖啡的名字及价格。而下面的 SELECT 语句限制那些每磅价格低于 $9.00 的咖啡才被选择。

SELECT COF_NAME, PRICE

FROM COFFEES

WHERE PRICE < 9.00

结果集将具有如下形式：

COF_NAME PRICE

-------- ------- -----

Colombian 7.99

French_Roast 8.99

Colombian Decaf 8.99

JDBC 入门 -- 开始

你需要做的第一事情是你要正确的安装。这包含下列几个步骤：

在你的计算机上安装 Java 和 JDBC

    Java 数据库连接 (JDBC) 是一个标准 SQL（Structured Query Language，结构化查询语言）数据库访问接口, 可以为多种关系数据库提供统一访问。JDBC（Java DataBase Connection，Java 数据库连接） 也提供一种基准，据此可以构建更高级的工具和接口。 目前的 JDK（Java Development Kit，Java 开发工具包）软件捆绑包括 JDBC 和 JDBC-ODBC（Open DataBase Connection，开放式数据库连接）桥。这些包也可独立得到，以跟 JDK 1.0 一起使用。应该注意的是，本文的示例使用了 JDBC 2.0 接口，需要 JDK 2.0 来运行，不能在 JDK 1.1 下运行。

    你可以从 http://java.sun.com/products/JDK/CurrentRelease找到最新版。

安装驱动程序

    你的驱动程序应该有安装方法。为特定的 DBMSs 写的 JDBC 驱动程序安装时只要拷贝到你的计算机上就可以了。并不需要特殊的配置。

    如果你下载的是 Solaris 或 Windows JDK1.1 版本，桥作为包 sun.jdbc.odbc 与 JDK 一起自动安装。有关安装和配置 ODBC 的信息，请咨询 ODBC 驱动程序厂商。桥无须特殊配置。有关客户机安装和配置信息，请咨询数据库厂商。

如果需要，安装数据库系统

    如果你不能确认是否安装了数据库系统，你需要按照供应商的要求安装数据库。大多数用户都已经安装了数据库，可继续使用他们安装好的数据库。

配置数据库

    我们假设数据库 COFFEEBREAK 已经存在。（创建一个数据库并不困难，但需要一定的权限并通常是由数据库管理员来做）你还需要在此数据库里创建本教程作为例子使用的表。我们有意限制表的大小跟及数目，以便于管理。

    假设我们的数据库是在一个咖啡馆里使用， 咖啡豆按磅卖，而咖啡则以杯为单位。为了简单起见，还假定经营者只需要 2 张表，分别存放不同种类的咖啡及咖啡供应商的有关信息。

    首先我们演示怎么打开一个 DBMS 连接， 及 JDBC 是怎么发送 SQL 语句到你的 DBMS。通过这些代码，我们将表明使用 JDBC 传递 SQL 语句到你的 DBMS 并处理返回的结果是非常简单的。

    所有的代码在主要的几个 DBMS 产品做了测试。然而, 如果你使用 JDBC-ODBC 桥来连接旧版本 ODBC 驱动程序时，可能会遇到一些兼容性问题。


JDBC 示例（转）

作者：zergman

email: zergman@chinaasp.com

日期：6/12/2001 1:02:10 PM

发布者:flyfox

数据库连接对动态网站来说是最为重要的部分，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)，其中设计表(Table) faqs,有字段id(自动增量型，并设为主关键字), subject(文字型，长度200), answers(备注型)。这个表中可以存放一些编程知识的常见问题及答案。见图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:odbc: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();

%>


jdbc3中的RowSet 接口规范 [边看边译]

作者：罗亭

email: tingluo@263.net

日期：2000-12-8 17:48:13

标题：jdbc3中的RowSet 接口规范

来自：sun的jdbc3.0 spec

RowSet 接口增加了对javaBean组件模式的支持，在一个可视化的Bean组件开发环境中一个RowSet可以作为一个Bean来使用；

一个RowSet可以在设计时建立和配置并在运行时执行；

一个RowSet提供了一系列的JavaBean属性来允许一个RowSet实例配置并连接到jdbc数据源并读数据库中的数据，一组setXXX()方法提供了一种方法来向一个RowSet实例传入参数；RowSet 接口支持JavaBean中的事件机制，当RowSet中的某一个重要事件发生，如它的值被修改时，允许同一个应用程序中的另一个组件得到通报。

虽然它成了jdbc API中的一个部分，但RowSet 接口其实是独立存在的，

上一句我不会翻译[原文如下：The RowSet interface is unique in that it is intended to be implemented using the rest of the JDBC(TM) API. ]

换句话说，RowSet 接口是一层浮在jdbc Api上执行的软件；RowSet 的具体实现能被任何人，包括jdbc驱动提供商将其作为驱动的一个部分来提供。

.

.

.

具体的方法及属性，略……

/*

*译者：罗亭

*E文水平差，有误请谅（：

*转载请注明

*/


JDBCTM 指南：入门

作者：jeru

日期：2000-12-9 19:00:08

本简介是从《JDBCTM Database Access from JavaTM: A Tutorial and Annotated Reference 》这本书中摘引来的。

JavaSoft 目前正在准备这本书。这是一本教程，同时也是 JDBC 的重要参考手册，它将作为 Java 系列的组成部份在

1997 年春季由 Addison-Wesley 出版公司出版。

1.1 什么是 JDBCTM？

JDBCTM 是一种用于执行 SQL 语句的 JavaTM API（有意思的是，JDBC 本身是个商标名而不是一个缩写字；然而，JDBC

常被认为是代表 “Java 数据库连接 (Java Database Connectivity)”）。它由一组用 Java 编程语言编写的类和接口

组成。JDBC 为工具/数据库开发人员提供了一个标准的 API，使他们能够用纯 Java API 来编写数据库应用程序。

有了 JDBC，向各种关系数据库发送 SQL 语句就是一件很容易的事。换言之，有了 JDBC API，就不必为访问 Sybase 数

据库专门写一个程序，为访问 Oracle 数据库又专门写一个程序，为访问 Informix 数据库又写另一个程序，等等。您只

需用 JDBC API 写一个程序就够了，它可向相应数据库发送 SQL 语句。而且，使用 Java 编程语言编写的应用程序，就

无须去忧虑要为不同的平台编写不同的应用程序。将 Java 和 JDBC 结合起来将使程序员只须写一遍程序就可让它在任何

平台上运行。

Java 具有坚固、安全、易于使用、易于理解和可从网络上自动下载等特性，是编写数据库应用程序的杰出语言。所需要的

只是 Java 应用程序与各种不同数据库之间进行对话的方法。而 JDBC 正是作为此种用途的机制。

JDBC 扩展了 Java 的功能。例如，用 Java 和 JDBC API 可以发布含有 applet 的网页，而该 applet 使用的信息可能来

自远程数据库。企业也可以用 JDBC 通过 Intranet 将所有职员连到一个或多个内部数据库中（即使这些职员所用的计算机

有 Windows、 Macintosh 和 UNIX 等各种不同的操作系统）。随着越来越多的程序员开始使用 Java 编程语言，对从 Java

中便捷地访问数据库的要求也在日益增加。

MIS 管理员们都喜欢 Java 和 JDBC 的结合，因为它使信息传播变得容易和经济。企业可继续使用它们安装好的数据库，并

能便捷地存取信息，即使这些信息是储存在不同数据库管理系统上。新程序的开发期很短。安装和版本控制将大为简化。程

序员可只编写一遍应用程序或只更新一次，然后将它放到服务器上，随后任何人就都可得到最新版本的应用程序。对于商务

上的销售信息服务， Java 和 JDBC 可为外部客户提供获取信息更新的更好方法。

1.1.1 JDBC 的用途是什么？

简单地说，JDBC 可做三件事：

与数据库建立连接，

发送 SQL 语句，

处理结果。

下列代码段给出了以上三步的基本示例：

Connection con = DriverManager.getConnection (

"jdbc:odbc:wombat", "login", "password");

Statement stmt = con.createStatement();

ResultSet rs = stmt.executeQuery("SELECT a, b, c FROM Table1");

while (rs.next()) {

int x = rs.getInt("a");

String s = rs.getString("b");

float f = rs.getFloat("c");

}

1.1.2 JDBC 是一种低级 API ，是高级 API 的基础

JDBC 是个“低级”接口，也就是说，它用于直接调用 SQL 命令。在这方面它的功能极佳，并比其它的数据库连接 API 易

于使用，但它同时也被设计为一种基础接口，在它之上可以建立高级接口和工具。高级接口是“对用户友好的”接口，它使

用的是一种更易理解和更为方便的 API，这种 API 在幕后被转换为诸如 JDBC 这样的低级接口。在编写本文时，正在开发

两种基于 JDBC 的高级 API：

一种用于 Java 的嵌入式 SQL。至少已经有一个提供者计划编写它。DBMS 实现 SQL：一种专门设计来与数据库联合使用的

语言。JDBC 要求 SQL 语句必须作为 String 传给 Java 方法。相反，嵌入式 SQL 预处理器允许程序员将 SQL 语句直接与

Java 混在一起使用。例如，可在 SQL 语句中使用 Java 变量，用以接受或提供 SQL 值。然后，嵌入式 SQL 预处理器将通

过 JDBC 调用把这种 Java/SQL 的混合物转换为 Java。

关系数据库表到 Java 类的直接映射。JavaSoft 和其它提供者都声称要实现该 API。在这种“对象/关系”映射中，表中的

每行对应于类的一个实例，而每列的值对应于该实例的一个属性。于是，程序员可直接对 Java 对象进行操作；存取数据所

需的 SQL 调用将在“掩盖下”自动生成。此外还可提供更复杂的映射，例如将多个表中的行结合进一个 Java 类中。

随着人们对 JDBC 的兴趣日益增涨，越来越多的开发人员一直在使用基于 JDBC 的工具，以使程序的编写更加容易。程序员

也一直在编写力图使最终用户对数据库的访问变得更为简单的应用程序。例如，应用程序可提供一个选择数据库任务的菜单。

任务被选定后，应用程序将给出提示及空白供填写执行选定任务所需的信息。所需信息输入后，应用程序将自动调用所需的

SQL 命令。在这样一种程序的协助下，即使用户根本不懂 SQL 的语法，也可以执行数据库任务。

1.1.3 JDBC 与 ODBC 和其它 API 的比较

目前，Microsoft 的 ODBC（开放式数据库连接）API 可能是使用最广的、用于访问关系数据库的编程接口。它能在几乎所

有平台上连接几乎所有的数据库。为什么 Java 不使用 ODBC？

对这个问题的回答是：Java 可以使用 ODBC，但最好是在 JDBC 的帮助下以 JDBC-ODBC 桥的形式使用，这一点我们稍后再

说。现在的问题已变成：“为什么需要 JDBC”？ 回答如下：

ODBC 不适合直接在 Java 中使用，因为它使用 C 语言接口。从 Java 调用本地 C 代码在安全性、实现、坚固性和程序的

自动移植性方面都有许多缺点。

从 ODBC C API 到 Java API 的字面翻译是不可取的。例如，Java 没有指针，而 ODBC 却对指针用得很广泛（包括很容易

出错的指针 "void *"）。您可以将 JDBC 想象成被转换为面向对象接口的 ODBC，而面向对象的接口对 Java 程序员来说较

易于接收。

ODBC 很难学。它把简单和高级功能混在一起，而且即使对于简单的查询，其选项也极为复杂。相反，JDBC 尽量保证简单功

能的简便性，而同时在必要时允许使用高级功能。

启用“纯 Java ”机制需要象 JDBC 这样的 Java API。如果使用 ODBC，就必须手动地将 ODBC 驱动程序管理器和驱动程序

安装在每台客户机上。如果完全用 Java 编写 JDBC 驱动程序则 JDBC 代码在所有 Java 平台上（从网络计算机到大型机）

都可以自动安装、移植并保证安全性。

总之，JDBC API 对于基本的 SQL 抽象和概念是一种自然的 Java 接口。它建立在 ODBC 上而不是从零开始。因此，熟悉

ODBC 的程序员将发现 JDBC 很容易使用。JDBC 保留了 ODBC 的基本设计特征；事实上，两种接口都基于 X/Open SQL CLI

（调用级接口）。它们之间最大的区别在于：JDBC 以 Java 风格与优点为基础并进行优化，因此更加易于使用。

最近，Microsoft 又引进了 ODBC 之外的新 API： RDO、 ADO 和 OLE DB。这些设计在许多方面与 JDBC 是相同的，即它们

都是面向对象的数据库接口且基于可在 ODBC 上实现的类。但在这些接口中，我们未看见有特别的功能使我们要转而选择它

们来替代 ODBC，尤其是在 ODBC 驱动程序已建立起较为完善的市场的情况下。它们最多也就是在 ODBC 上加了一种装饰而

已。这并不是说 JDBC 不需要从其最初的版本再发展了；然而，我们觉得大部份的新功能应归入诸如前一节中所述的对象/

关系映射和嵌入式 SQL 这样的高级 API。

1.1.4 两层模型和三层模型

JDBC API 既支持数据库访问的两层模型，同时也支持三层模型。

在两层模型中，Java applet 或应用程序将直接与数据库进行对话。这将需要一个 JDBC 驱动程序来与所访问的特定数据库

管理系统进行通讯。用户的 SQL 语句被送往数据库中，而其结果将被送回给用户。数据库可以位于另一台计算机上，用户

通过网络连接到上面。这就叫做客户机/服务器配置，其中用户的计算机为客户机，提供数据库的计算机为服务器。网络可

以是 Intranet（它可将公司职员连接起来），也可以是 Internet。

在三层模型中，命令先是被发送到服务的“中间层”，然后由它将 SQL 语句发送给数据库。数据库对 SQL 语句进行处理并

将结果送回到中间层，中间层再将结果送回给用户。MIS 主管们都发现三层模型很吸引人，因为可用中间层来控制对公司数

据的访问和可作的的更新的种类。中间层的另一个好处是，用户可以利用易于使用的高级 API，而中间层将把它转换为相应

的低级调用。最后，许多情况下三层结构可提供一些性能上的好处。

到目前为止，中间层通常都用 C 或 C++ 这类语言来编写，这些语言执行速度较快。然而，随着最优化编译器（它把 Java

字节代码转换为高效的特定于机器的代码）的引入，用 Java 来实现中间层将变得越来越实际。这将是一个很大的进步，它

使人们可以充分利用 Java 的诸多优点（如坚固、多线程和安全等特征）。JDBC 对于从 Java 的中间层来访问数据库非常

重要。

1.1.5 SQL 的一致性

结构化查询语言 (SQL) 是访问关系数据库的标准语言。困难之处在于：虽然大多数的 DBMS （数据库管理系统）对其基本

功能都使用了标准形式的 SQL，但它们却不符合最近为更高级的功能定义的标准 SQL 语法或语义。例如，并非所有的数据

库都支持储存程序或外部连接，那些支持这一功能的数据库又相互不一致。人们希望 SQL 中真正标准的那部份能够进行扩

展以包括越来越多的功能。但同时 JDBC API 又必须支持现有的 SQL。

JDBC API 解决这个问题的一种方法是允许将任何查询字符串一直传到所涉及的 DBMS 驱动程序上。这意味着应用程序可以

使用任意多的 SQL 功能，但它必须冒这样的风险：有可能在某些 DBMS 上出错。事实上，应用程序查询甚至不一定要是

SQL，或者说它可以是个为特定的 DBMS 设计的 SQL 的专用派生物（例如，文档或图象查询）。

JDBC 处理 SQL 一致性问题的第二种方法是提供 ODBC 风格的转义子句。这将在 4.1.5 节“语句对象中的 SQL 转义语法”

中讨论。

转义语法为几个常见的 SQL 分歧提供了一种标准的 JDBC 语法。例如，对日期文字和已储存过程的调用都有转义语法。

对于复杂的应用程序，JDBC 用第三种方法来处理 SQL 的一致性问题。它利用 DatabaseMetaData 接口来提供关于 DBMS 的

描述性信息，从而使应用程序能适应每个 DBMS 的要求和功能。

由于 JDBC API 将用作开发高级数据库访问工具和 API 的基础 API，因此它还必须注意其所有上层建筑的一致性。“符合

JDBC 标准TM" 代表用户可依赖的 JDBC 功能的标准级别。要使用这一说明，驱动程序至少必须支持 ANSI SQL-2 Entry

Level（ANSI SQL-2 代表美国国家标准局 1992 年所采用的标准。Entry Level 代表 SQL 功能的特定清单）。驱动程序开

发人员可用 JDBC API 所带的测试工具包来确定他们的驱动程序是否符合这些标准。

“符合 JDBC 标准TM” 表示提供者的 JDBC 实现已经通过了 JavaSoft 提供的一致性测试。这些一致性测试将检查 JDBC

API 中定义的所有类和方法是否都存在，并尽可能地检查程序是否具有 SQL Entry Level 功能。当然，这些测试并不完全，

而且 JavaSoft 目前也无意对各提供者的实现进行标级。但这种一致性定义的确可对 JDBC 实现提供一定的可信度。随着越

来越多的数据库提供者、连接提供者、Internet 提供者和应用程序编程员对 JDBC API 的接受，JDBC 也正迅速成为 Java

数据库访问的标准。

1.2 JDBC 产品

在编写本文时，有几个基于 JDBC 的产品已开发完毕或正在开发中。当然，本节中的信息将很快成为过时信息。因此，有

关最新的信息，请查阅 JDBC 的网站，可通过从以下 URL 开始浏览找到：

http://java.sun.com/products/jdbc

1.2.1 JavaSoft 框架

JavaSoft 提供三种 JDBC 产品组件，它们是 Java 开发工具包 (JDK) 的组成部份：

JDBC 驱动程序管理器，

JDBC 驱动程序测试工具包，和

JDBC-ODBC 桥。

JDBC 驱动程序管理器是 JDBC 体系结构的支柱。它实际上很小，也很简单；其主要作用是把 Java 应用程序连接到正确的

JDBC 驱动程序上，然后即退出。

JDBC 驱动程序测试工具包为使 JDBC 驱动程序运行您的程序提供一定的可信度。只有通过 JDBC 驱动程序测试包的驱动程

序才被认为是符合 JDBC 标准TM 的。

JDBC-ODBC 桥使 ODBC 驱动程序可被用作 JDBC 驱动程序。它的实现为 JDBC 的快速发展提供了一条途径，其长远目标提

供一种访问某些不常见的 DBMS（如果对这些不常见的 DBMS 未实现 JDBC） 的方法。

1.2.2 JDBC 驱动程序的类型

我们目前所知晓的 JDBC 驱动程序可分为以下四个种类：

JDBC-ODBC 桥加 ODBC 驱动程序：JavaSoft 桥产品利用 ODBC 驱动程序提供 JDBC 访问。注意，必须将 ODBC 二进制代码

（许多情况下还包括数据库客户机代码）加载到使用该驱动程序的每个客户机上。因此，这种类型的驱动程序最适合于企业

网（这种网络上客户机的安装不是主要问题），或者是用 Java 编写的三层结构的应用程序服务器代码。

本地 API - 部份用 Java 来编写的驱动程序： 这种类型的驱动程序把客户机 API 上的 JDBC 调用转换为 Oracle、

Sybase、Informix、DB2 或其它 DBMS 的调用。注意，象桥驱动程序一样，这种类型的驱动程序要求将某些二进制代码加载

到每台客户机上。

JDBC 网络纯 Java 驱动程序：这种驱动程序将 JDBC 转换为与 DBMS 无关的网络协议，之后这种协议又被某个服务器转换

为一种 DBMS 协议。这种网络服务器中间件能够将它的纯 Java 客户机连接到多种不同的数据库上。所用的具体协议取决于

提供者。通常，这是最为灵活的 JDBC 驱动程序。有可能所有这种解决方案的提供者都提供适合于 Intranet 用的产品。为

了使这些产品也支持 Internet 访问，它们必须处理 Web 所提出的安全性、通过防火墙的访问等方面的额外要求。几家提

供者正将 JDBC 驱动程序加到他们现有的数据库中间件产品中。

本地协议纯 Java 驱动程序：这种类型的驱动程序将 JDBC 调用直接转换为 DBMS 所使用的网络协议。这将允许从客户机机

器上直接调用 DBMS 服务器，是 Intranet 访问的一个很实用的解决方法。由于许多这样的协议都是专用的，因此数据库提

供者自己将是主要来源，有几家提供者已在着手做这件事了。

最后，我们预计第 3、4 类驱动程序将成为从 JDBC 访问数据库的首选方法。第 1、2 类驱动程序在直接的纯 Java 驱动程

序还没有上市前将会作为过渡方案来使用。对第 1、2 类驱动程序可能会有一些变种（下表中未列出），这些变种要求有连

接器，但通常这些是更加不可取的解决方案。第 3、4 类驱动程序提供了 Java 的所有优点，包括自动安装（例如，通过使

用 JDBC 驱动程序的 applet applet来下载该驱动程序）。

下表显示了这 4 种类型的驱动程序及其属性：

驱动程序种类 纯 JAVA？ 网络协议

1 - JDBC-OCBC 桥 非 直接

2 - 基于本地 API 的 非 直接

3 - JDBC 网络的 是 要求连接器

4 - 基于本地协议的 是 直接

1.2.3 JDBC 驱动程序的获取

在编写本文时，已有几十个属于种类的驱动程序，即可与 Javasoft 桥联合使用的 1: ODBC 驱动程序的驱动程序。有大约

十多个属于种类 2 的驱动程序是以 DBMS 的本地 API 为基础编写的。只有几个属于种类 3 的驱动程序。目前至少有 2 个

属于种类 4 的驱动程序，但到 1997 年底，我们预计主要的 DBMS 都会有种类 4 的驱动程序。

要获取关于驱动程序的最新信息，请查阅 JDBC 的网站，其网址为： http://java.sun.com/products/jdbc。提供第 3 种

驱动程序的首批提供者是 SCO、Open Horizon、Visigenic 和 WebLogic。JavaSoft 和数据库连接的领先提供者 Intersolv

合作研制了 JDBC-ODBC 桥和 JDBC 驱动程序测试工具包。

1.2.4 其它产品

各种 JDBC 应用程序的开发工具正在开发中。请注意查阅 JavaSoft 网页以得到更新信息。


JDBCTM 指南：入门2 - 连接

作者：jeru

日期：2000-12-9 19:10:29

JDBCTM 指南：入门2 - 连接

    2 - 连接

    本概述是从《JDBCTM Database Access from JavaTM: A Tutorial and Annotated Reference 》这本书中摘引来的。JavaSoft 目前正在准备这本书。这本书是一本教程，同时也是 JDBC 的重要参考手册，它将作为 Java 系列的组成部份在 1997 年春季由 Addison-Wesley 出版公司出版。

    2.1 概述

    Connection 对象代表与数据库的连接。连接过程包括所执行的 SQL 语句和在该连接上所返回的结果。一个应用程序可与单个数据库有一个或多个连接，或者可与许多数据库有连接。

    2.1.1 打开连接

    与数据库建立连接的标准方法是调用 DriverManager.getConnection 方法。该方法接受含有某个 URL 的字符串。DriverManager 类（即所谓的 JDBC 管理层）将尝试找到可与那个 URL 所代表的数据库进行连接的驱动程序。DriverManager 类存有已注册的 Driver 类的清单。当调用方法 getConnection 时，它将检查清单中的每个驱动程序，直到找到可与 URL 中指定的数据库进行连接的驱动程序为止。Driver 的方法 connect 使用这个 URL 来建立实际的连接。

用户可绕过 JDBC 管理层直接调用 Driver 方法。这在以下特殊情况下将很有用：当两个驱动器可同时连接到数据库中，而用户需要明确地选用其中特定的驱动器。但一般情况下，让 DriverManager 类处理打开连接这种事将更为简单。

下述代码显示如何打开一个与位于 URL "jdbc:odbc:wombat" 的数据库的连接。所用的用户标识符为 "oboy" ，口令为 "12Java"：

String url = "jdbc:odbc:wombat";

    Connection con = DriverManager.getConnection(url, "oboy", "12Java");

2.1.2 一般用法的 URL

    由于 URL 常引起混淆，我们将先对一般 URL 作简单说明，然后再讨论 JDBC URL。

URL（统一资源定位符）提供在 Internet 上定位资源所需的信息。可将它想象为一个地址。

URL 的第一部份指定了访问信息所用的协议，后面总是跟着冒号。常用的协议有 "ftp"（代表“文件传输协议”）和 "http" （代表“超文本传输协议”）。如果协议是 "file"，表示资源是在某个本地文件系统上而非在 Internet 上（下例用于表示我们所描述的部分；它并非 URL 的组成部分）。

ftp://javasoft.com/docs/JDK-1_apidocs.zip

    http://java.sun.com/products/jdk/CurrentRelease

    file:/home/haroldw/docs/books/tutorial/summary.html

URL 的其余部份（冒号后面的）给出了数据资源所处位置的有关信息。如果协议是 file，则 URL 的其余部份是文件的路径。对于 ftp 和 http 协议，URL 的其余部份标识了主机并可选地给出某个更详尽的地址路径。例如，以下是 JavaSoft 主页的 URL。该 URL 只标识了主机：

http://java.sun.com

从该主页开始浏览，就可以进到许多其它的网页中，其中之一就是 JDBC 主页。JDBC 主页的 URL 更为具体，它看起来类似：

http://java.sun.com/products/jdbc

2.1.3 JDBC URL

    JDBC URL 提供了一种标识数据库的方法，可以使相应的驱动程序能识别该数据库并与之建立连接。实际上，驱动程序编程员将决定用什么 JDBC URL 来标识特定的驱动程序。用户不必关心如何来形成 JDBC URL；他们只须使用与所用的驱动程序一起提供的 URL 即可。JDBC 的作用是提供某些约定，驱动程序编程员在构造他们的 JDBC URL 时应该遵循这些约定。

由于 JDBC URL 要与各种不同的驱动程序一起使用，因此这些约定应非常灵活。首先，它们应允许不同的驱动程序使用不同的方案来命名数据库。例如， odbc 子协议允许（但并不是要求） URL 含有属性值。

第二，JDBC URL 应允许驱动程序编程员将一切所需的信息编入其中。这样就可以让要与给定数据库对话的 applet 打开数据库连接，而无须要求用户去做任何系统管理工作。

第三， JDBC URL 应允许某种程度的间接性。也就是说，JDBC URL 可指向逻辑主机或数据库名，而这种逻辑主机或数据库名将由网络命名系统动态地转换为实际的名称。这可以使系统管理员不必将特定主机声明为 JDBC 名称的一部份。网络命名服务（例如 DNS、 NIS 和 DCE ）有多种,而对于使用哪种命名服务并无限制。

JDBC URL 的标准语法如下所示。它由三部分组成，各部分间用冒号分隔：

jdbc::

JDBC URL 的三个部分可分解如下：

    jdbc ─ 协议。JDBC URL 中的协议总是 jdbc。

     ─ 驱动程序名或数据库连接机制（这种机制可由一个或多个驱动程序支持）的名称。子协议名的典型示例是 "odbc"，该名称是为用于指定 ODBC 风格的数据资源名称的 URL 专门保留的。例如，为了通过 JDBC-ODBC 桥来访问某个数据库，可以用如下所示的 URL：

    jdbc:odbc:fred

本例中，子协议为 "odbc"，子名称 "fred" 是本地

    ODBC 数据资源。

如果要用网络命名服务（这样 JDBC URL 中的数据库名称不必是实际名称），则命名服务可以作为子协议。例如，可用如下所示的 URL ：

jdbc:dcenaming:accounts-payable

本例中，该 URL 指定了本地 DCE 命名服务应该将

    数据库名称 "accounts-payable" 解析为更为具体的

    可用于连接真实数据库的名称。

     ─ 一种标识数据库的方法。子名称可以依不同的子协议而变化。它还可以有子名称的子名称（含有驱动程序编程员所选的任何内部语法）。使用子名称的目的是为定位数据库提供足够的信息。前例中，因为 ODBC 将提供其余部份的信息，因此用 "fred" 就已足够。然而，位于远程服务器上的数据库需要更多的信息。例如，如果数据库是通过 Internet 来访问的，则在 JDBC URL 中应将网络地址作为子名称的一部份包括进去，且必须遵循如下所示的标准 URL 命名约定：

    //主机名:端口/子协议

假设 "dbnet" 是个用于将某个主机连接到 Internet 上的协议，则 JDBC URL 类似：

jdbc:dbnet://wombat:356/fred

2.1.4 "odbc" 子协议

    子协议 odbc 是一种特殊情况。它是为用于指定 ODBC 风格的数据资源名称的 URL 而保留的，并具有下列特性：允许在子名称（数据资源名称）后面指定任意多个属性值。odbc 子协议的完整语法为：

    jdbc:odbc:[;=]*

因此，以下都是合法的 jdbc:odbc 名称：

jdbc:odbc:qeor7

    jdbc:odbc:wombat

    jdbc:odbc:wombat;CacheSize=20;ExtensionCase=LOWER

    jdbc:odbc:qeora;UID=kgh;PWD=fooey

2.1.5 注册子协议

    驱动程序编程员可保留某个名称以将之用作 JDBC URL 的子协议名。当 DriverManager 类将此名称加到已注册的驱动程序清单中时，为之保留该名称的驱动程序应能识别该名称并与它所标识的数据库建立连接。例如，odbc 是为 JDBC- ODBC 桥而保留的。示例之二，假设有个 Miracle 公司，它可能会将 "miracle" 注册为连接到其 Miracle DBMS 上的 JDBC 驱动程序的子协议，从而使其他人都无法使用这个名称。

JavaSoft 目前作为非正式代理负责注册 JDBC 子协议名称。要注册某个子协议名称，请发送电子邮件到下述地址：

jdbc@wombat.eng.sun.com

2.1.6 发送 SQL 语句

    连接一旦建立，就可用来向它所涉及的数据库传送 SQL 语句。JDBC 对可被发送的 SQL 语句类型不加任何限制。这就提供了很大的灵活性，即允许使用特定的数据库语句或甚至于非 SQL 语句。然而，它要求用户自己负责确保所涉及的数据库可以处理所发送的 SQL 语句，否则将自食其果。例如，如果某个应用程序试图向不支持储存程序的 DBMS 发送储存程序调用，就会失败并将抛出异常。JDBC 要求驱动程序应至少能提供 ANSI SQL-2 Entry Level 功能才可算是符合 JDBC 标准TM 的。这意味着用户至少可信赖这一标准级别的功能。

JDBC 提供了三个类，用于向数据库发送 SQL 语句。Connection 接口中的三个方法可用于创建这些类的实例。下面列出这些类及其创建方法：

    Statement ─ 由方法 createStatement 所创建。Statement 对象用于发送简单的 SQL 语句。

    PreparedStatement ─ 由方法 prepareStatement 所创建。PreparedStatement 对象用于发送带有一个或多个输入参数（ IN 参数）的 SQL 语句。PreparedStatement 拥有一组方法，用于设置 IN 参数的值。执行语句时，这些 IN 参数将被送到数据库中。PreparedStatement 的实例扩展了 Statement ，因此它们都包括了 Statement 的方法。PreparedStatement 对象有可能比 Statement 对象的效率更高，因为它已被预编译过并存放在那以供将来使用。

    CallableStatement ─ 由方法 prepareCall 所创建。CallableStatement 对象用于执行 SQL 储存程序 ─ 一组可通过名称来调用（就象函数的调用那样）的 SQL 语句。CallableStatement 对象从 PreparedStatement 中继承了用于处理 IN 参数的方法，而且还增加了用于处理 OUT 参数和 INOUT 参数的方法。

以下所列提供的方法可以快速决定应用哪个 Connection 方法来创建不同类型的 SQL 语句：

    createStatement 方法用于：

    简单的 SQL 语句（不带参数）

    prepareStatement 方法用于：

    带一个或多个 IN 参数的 SQL 语句

    经常被执行的简单 SQL 语句

    prepareCall 方法用于：

    调用已储存过程

    2.1.7 事务

    事务由一个或多个这样的语句组成：这些语句已被执行、完成并被提交或还原。当调用方法 commit 或 rollback 时，当前事务即告就结束，另一个事务随即开始。

缺省情况下，新连接将处于自动提交模式。也就是说，当执行完语句后，将自动对那个语句调用 commit 方法。这种情况下，由于每个语句都是被单独提交的，因此一个事务只由一个语句组成。如果禁用自动提交模式，事务将要等到 commit 或 rollback 方法被显式调用时才结束，因此它将包括上一次调用 commit 或 rollback 方法以来所有执行过的语句。对于第二种情况，事务中的所有语句将作为组来提交或还原。

方法 commit 使 SQL 语句对数据库所做的任何更改成为永久性的，它还将释放事务持有的全部锁。而方法 rollback 将弃去那些更改。

有时用户在另一个更改生效前不想让此更改生效。这可通过禁用自动提交并将两个更新组合在一个事务中来达到。如果两个更新都是成功的，则调用 commit 方法，从而使两个更新结果成为永久性的；如果其中之一或两个更新都失败了，则调用 rollback 方法，以将值恢复为进行更新之前的值。

大多数 JDBC 驱动程序都支持事务。事实上，符合 JDBC 的驱动程序必须支持事务。DatabaseMetaData 给出的信息描述 DBMS 所提供的事务支持水平。

    2.1.8 事务隔离级别

    如果 DBMS 支持事务处理，它必须有某种途径来管理两个事务同时对一个数据库进行操作时可能发生的冲突。用户可指定事务隔离级别，以指明 DBMS 应该花多大精力来解决潜在冲突。例如，当事务更改了某个值而第二个事务却在该更改被提交或还原前读取该值时该怎么办？ 假设第一个事务被还原后，第二个事务所读取的更改值将是无效的，那么是否可允许这种冲突？ JDBC 用户可用以下代码来指示 DBMS 允许在值被提交前读取该值（“dirty 读取”），其中 con 是当前连接：

con.setTransactionIsolation(TRANSACTION_READ_UNCOMMITTED);

事务隔离级别越高，为避免冲突所花的精力也就越多。Connection 接口定义了五级，其中最低级别指定了根本就不支持事务，而最高级别则指定当事务在对某个数据库进行操作时，任何其它事务不得对那个事务正在读取的数据进行任何更改。通常，隔离级别越高，应用程序执行的速度也就越慢（由于用于锁定的资源耗费增加了，而用户间的并发操作减少了）。在决定采用什么隔离级别时，开发人员必须在性能需求和数据一致性需求之间进行权衡。当然，实际所能支持的级别取决于所涉及的 DBMS 的功能。

当创建 Connection 对象时，其事务隔离级别取决于驱动程序，但通常是所涉及的数据库的缺省值。用户可通过调用 setIsolationLevel 方法来更改事务隔离级别。新的级别将在该连接过程的剩余时间内生效。要想只改变一个事务的事务隔离级别，必须在该事务开始前进行设置，并在该事务结束后进行复位。我们不提倡在事务的中途对事务隔离级别进行更改，因为这将立即触发 commit 方法的调用，使在此之前所作的任何更改变成永久性的。


JDBCTM 指南：入门3 - DriverManager

作者：jeru

日期：2000-12-9 19:11:15

内容:

3 - DriverManager

3.1 概述

DriverManager 类是 JDBC 的管理层，作用于用户和驱动程序之间。

它跟踪可用的驱动程序，并在数据库和相应驱动程序之间建立连接。

另外，DriverManager 类也处理诸如驱动程序登录时间限制及登录和

跟踪消息的显示等事务。

对于简单的应用程序，一般程序员需要在此类中直接使用的唯一方法

是 DriverManager.getConnection。正如名称所示，该方法将建立与

数据库的连接。JDBC 允许用户调用 DriverManager 的方法

getDriver、getDrivers 和 registerDriver 及 Driver 的方法

connect。但多数情况下，让 DriverManager 类管理建立连接的细

节为上策。

3.1.1 跟踪可用驱动程序

DriverManager 类包含一列 Driver 类，它们已通过调用方法

DriverManager.registerDriver 对自己进行了注册。所有 Driver

 类都必须包含有一个静态部分。它创建该类的实例，然后在加载该

实例时 DriverManager 类进行注册。这样，用户正常情况下将不会

直接调用 DriverManager.registerDriver；而是在加载驱动程序时

由驱动程序自动调用。加载 Driver 类，然后自动在 DriverManager

 中注册的方式有两种：

通过调用方法 Class.forName。这将显式地加载驱动程序类。由于这

与外部设置无关，因此推荐使用这种加载驱动程序的方法。以下代码

加载类 acme.db.Driver：

Class.forName("acme.db.Driver");

如果将 acme.db.Driver 编写为加载时创建实例，并调用以该实例为

参数的 DriverManager.registerDriver（本该如此），则它在

DriverManager 的驱动程序列表中，并可用于创建连接。

通过将驱动程序添加到 java.lang.System 的属性 jdbc.drivers 中

这是一个由 DriverManager 类加载的驱动程序类名的列表，由冒号

分隔：初始化 DriverManager 类时，它搜索系统属性 jdbc.drivers，

如果用户已输入了一个或多个驱动程序，则 DriverManager 类将试图加载它们。

以下代码说明程序员如何在 ~/.hotjava/properties 中输入三个驱动程序类（

启动时，HotJava 将把它加载到系统属性列表中）：

jdbc.drivers=foo.bah.Driver:wombat.sql.Driver:bad.test.ourDriver;

对 DriverManager 方法的第一次调用将自动加载这些驱动程序类。

注意：加载驱动程序的第二种方法需要持久的预设环境。如果对这一点不能

保证，则调用方法 Class.forName 显式地加载每个驱动程序就显得更为

安全。这也是引入特定驱动程序的方法，因为一旦 DriverManager 类被初始化，它

将不再检查 jdbc.drivers 属性列表。

在以上两种情况中，新加载的 Driver 类都要通过调用 DriverManager.registerDriver

类进行自我注册。如上所述，加载类时将自动执行这一过程。

由于安全方面的原因，JDBC 管理层将跟踪哪个类加载器提供哪

个驱动程序。这样，当 DriverManager 类打开连接时，它仅使用

本地文件系统或与发出连接请求的代码相同的类加载器提供的驱动程序。

3.1.2 建立连接

加载 Driver 类并在 DriverManager 类中注册后，它们即可用来与数

据库建立连接。当调用 DriverManager.getConnection 方法发出连接

请求时，DriverManager 将检查每个驱动程序，查看它是否可以建立连接。

有时可能有多个 JDBC 驱动程序可以与给定的 URL 连接。例如，与

给定远程数据库连接时，可以使用 JDBC-ODBC 桥驱动程序、JDBC 到

通用网络协议驱动程序或数据库厂商提供的驱动程序。在这种情况下

测试驱动程序的顺序至关重要，因为 DriverManager 将使用它所找到

的第一个可以成功连接到给定 URL 的驱动程序。

首先 DriverManager 试图按注册的顺序使用每个驱动程序

（jdbc.drivers 中列出的驱动程序总是先注册）。它将跳过代码不可

信任的驱动程序，除非加载它们的源与试图打开连接的代码的源相同。

它通过轮流在每个驱动程序上调用方法 Driver.connect，并向它们传

递用户开始传递给方法 DriverManager.getConnection 的 URL 来对驱

动程序进行测试，然后连接第一个认出该 URL 的驱动程序。

这种方法初看起来效率不高，但由于不可能同时加载数十个驱动程序，

因此每次连接实际只需几个过程调用和字符串比较。

以下代码是通常情况下用驱动程序（例如 JDBC-ODBC 桥驱动程序）

建立连接所需所有步骤的示例：

Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); //加载驱动程序

String url = "jdbc:odbc:fred";

DriverManager.getConnection(url, "userID", "passwd");


JDBCTM 指南：入门4 - Statement

作者：jeru

日期：2000-12-9 21:25:37

4 - Statement

本概述是从《JDBCTM Database Access from JavaTM: A Tutorial and Annotated Reference 》这本书中摘引来的。JavaSoft 目前正在准备这本书。这是一本教程，同时也是 JDBC 的重要参考手册，它将作为 Java 系列的组成部份在 1997 年春季由 Addison-Wesley 出版公司出版。

4.1 概述

Statement 对象用于将 SQL 语句发送到数据库中。实际上有三种 Statement 对象，它们都作为在给定连接上执行 SQL 语句的包容器：Statement、PreparedStatement（它从 Statement 继承而来）和 CallableStatement（它从 PreparedStatement 继承而来）。它们都专用于发送特定类型的 SQL 语句： Statement 对象用于执行不带参数的简单 SQL 语句；PreparedStatement 对象用于执行带或不带 IN 参数的预编译 SQL 语句；CallableStatement 对象用于执行对数据库已存储过程的调用。

Statement 接口提供了执行语句和获取结果的基本方法。PreparedStatement 接口添加了处理 IN 参数的方法；而 CallableStatement 添加了处理 OUT 参数的方法。

4.1.1 创建 Statement 对象

建立了到特定数据库的连接之后，就可用该连接发送 SQL 语句。Statement 对象用 Connection 的方法 createStatement 创建，如下列代码段中所示：

Connection con = DriverManager.getConnection(url, "sunny", "");

Statement stmt = con.createStatement();

为了执行 Statement 对象，被发送到数据库的 SQL 语句将被作为参数提供给 Statement 的方法：

ResultSet rs = stmt.executeQuery("SELECT a, b, c FROM Table2");

4.1.2 使用 Statement 对象执行语句

Statement 接口提供了三种执行 SQL 语句的方法：executeQuery、executeUpdate 和 execute。使用哪一个方法由 SQL 语句所产生的内容决定。

方法 executeQuery 用于产生单个结果集的语句，例如 SELECT 语句。

方法 executeUpdate 用于执行 INSERT、UPDATE 或 DELETE 语句以及 SQL DDL（数据定义语言）语句，例如 CREATE TABLE 和 DROP TABLE。INSERT、UPDATE 或 DELETE 语句的效果是修改表中零行或多行中的一列或多列。executeUpdate 的返回值是一个整数，指示受影响的行数（即更新计数）。对于 CREATE TABLE 或 DROP TABLE 等不操作行的语句，executeUpdate 的返回值总为零。

方法 execute 用于执行返回多个结果集、多个更新计数或二者组合的语句。因为多数程序员不会需要该高级功能，所以本概述后面将在单独一节中对其进行介绍。

执行语句的所有方法都将关闭所调用的 Statement 对象的当前打开结果集（如果存在）。这意味着在重新执行 Statement 对象之前，需要完成对当前 ResultSet 对象的处理。

应注意，继承了 Statement 接口中所有方法的 PreparedStatement 接口都有自己的 executeQuery、executeUpdate 和 execute 方法。Statement 对象本身不包含 SQL 语句，因而必须给 Statement.execute 方法提供 SQL 语句作为参数。PreparedStatement 对象并不将 SQL 语句作为参数提供给这些方法，因为它们已经包含预编译 SQL 语句。CallableStatement 对象继承这些方法的 PreparedStatement 形式。对于这些方法的 PreparedStatement 或 CallableStatement 版本，使用查询参数将抛出 SQLException。

4.1.3 语句完成

当连接处于自动提交模式时，其中所执行的语句在完成时将自动提交或还原。语句在已执行且所有结果返回时，即认为已完成。对于返回一个结果集的 executeQuery 方法，在检索完 ResultSet 对象的所有行时该语句完成。对于方法 executeUpdate，当它执行时语句即完成。但在少数调用方法 execute 的情况中，在检索所有结果集或它生成的更新计数之后语句才完成。

有些 DBMS 将已存储过程中的每条语句视为独立的语句；而另外一些则将整个过程视为一个复合语句。在启用自动提交时，这种差别就变得非常重要，因为它影响什么时候调用 commit 方法。在前一种情况中，每条语句单独提交；在后一种情况中，所有语句同时提交。

4.1.4 关闭 Statement 对象

Statement 对象将由 Java 垃圾收集程序自动关闭。而作为一种好的编程风格，应在不需要 Statement 对象时显式地关闭它们。这将立即释放 DBMS 资源，有助于避免潜在的内存问题。

4.1.5 Statement 对象中的 SQL 转义语法

Statement 可包含使用 SQL 转义语法的 SQL 语句。转义语法告诉驱动程序其中的代码应该以不同方式处理。驱动程序将扫描任何转义语法，并将它转换成特定数据库可理解的代码。这使得转义语法与 DBMS 无关，并允许程序员使用在没有转义语法时不可用的功能。

转义子句由花括号和关键字界定：

{keyword . . . parameters . . . }

该关键字指示转义子句的类型，如下所示。

escape 表示 LIKE 转义字符

字符“%”和“_”类似于 SQL LIKE 子句中的通配符（“%”匹配零个或多个字符，而“_”则匹配一个字符）。为了正确解释它们，应在其前面加上反斜杠（“\”），它是字符串中的特殊转义字符。在查询末尾包括如下语法即可指定用作转义字符的字符：

{escape 'escape-character'}

例如，下列查询使用反斜杠字符作为转义字符，查找以下划线开头的标识符名：

stmt.executeQuery("SELECT name FROM Identifiers

WHERE Id LIKE `\_%' {escape `\'};

fn 表示标量函数

几乎所有 DBMS 都具有标量值的数值、字符串、时间、日期、系统和转换函数。要使用这些函数，可使用如下转义语法：关键字 fn 后跟所需的函数名及其参数。例如，下列代码调用函数 concat 将两个参数连接在一起：

{fn concat("Hot", "Java")};

可用下列语法获得当前数据库用户名：

{fn user()};

标量函数可能由语法稍有不同的 DBMS 支持，而它们可能不被所有驱动程序支持。各种 DatabaseMetaData 方法将列出所支持的函数。例如，方法 getNumericFunctions 返回用逗号分隔的数值函数列表，而方法 getStringFunctions 将返回字符串函数，等等。

驱动程序将转义函数调用映射为相应的语法，或直接实现该函数。

d、t 和 ts 表示日期和时间文字

DBMS 用于日期、时间和时间标记文字的语法各不相同。JDBC 使用转义子句支持这些文字的语法的 ISO 标准格式。驱动程序必须将转义子句转换成 DBMS 表示。

例如，可用下列语法在 JDBC SQL 语句中指定日期：

{d `yyyy-mm-dd'}

在该语法中，yyyy 为年代，mm 为月份，而 dd 则为日期。驱动程序将用等价的特定于 DBMS 的表示替换这个转义子句。例如，如果 '28- FEB-99' 符合基本数据库的格式，则驱动程序将用它替换 {d 1999-02-28}。

对于 TIME 和 TIMESTAMP 也有类似的转义子句：

{t `hh:mm:ss'}

{ts `yyyy-mm-dd hh:mm:ss.f . . .'}

TIMESTAMP 中的小数点后的秒（.f . . .）部分可忽略。

call 或 ? = call 表示已存储过程

如果数据库支持已存储过程，则可从 JDBC 中调用它们，语法为：

{call procedure_name[(?, ?, . . .)]}

或（其中过程返回结果参数）：

{? = call procedure_name[(?, ?, . . .)]}

方括号指示其中的内容是可选的。它们不是语法的必要部分。

输入参数可以为文字或参数。有关详细信息，参见 JDBC 指南中第 7 节，“CallableStatement”。

可通过调用方法 DatabaseMetaData.supportsStoredProcedures 检查数据库是否支持已存储过程。

oj 表示外部连接

外部连接的语法为

{oj outer-join}

其中 outer-join 形式为

table LEFT OUTER JOIN {table / outer-join} ON search-condition

外部连接属于高级功能。有关它们的解释可参见 SQL 语法。JDBC 提供了三种 DatabaseMetaData 方法用于确定驱动程序支持哪些外部连接类型：supportsOuterJoins、supportsFullOuterJoins 和 supportsLimitedOuterJoins。

方法 Statement.setEscapeProcessing 可打开或关闭转义处理；缺省状态为打开。当性能极为重要时，程序员可能想关闭它以减少处理时间。但通常它将出于打开状态。应注意： setEscapeProcessing 不适用于 PreparedStatement 对象，因为在调用该语句前它就可能已被发送到数据库。有关预编译的信息，参见 PreparedStatement。

4.1.6 使用方法 execute

execute 方法应该仅在语句能返回多个 ResultSet 对象、多个更新计数或 ResultSet 对象与更新计数的组合时使用。当执行某个已存储过程或动态执行未知 SQL 字符串（即应用程序程序员在编译时未知）时，有可能出现多个结果的情况，尽管这种情况很少见。例如，用户可能执行一个已存储过程（使用 CallableStatement 对象 - 参见第 135 页的 CallableStatement），并且该已存储过程可执行更新，然后执行选择，再进行更新，再进行选择，等等。通常使用已存储过程的人应知道它所返回的内容。

因为方法 execute 处理非常规情况，所以获取其结果需要一些特殊处理并不足为怪。例如，假定已知某个过程返回两个结果集，则在使用方法 execute 执行该过程后，必须调用方法 getResultSet 获得第一个结果集，然后调用适当的 getXXX 方法获取其中的值。要获得第二个结果集，需要先调用 getMoreResults 方法，然后再调用 getResultSet 方法。如果已知某个过程返回两个更新计数，则首先调用方法 getUpdateCount，然后调用 getMoreResults，并再次调用 getUpdateCount。

对于不知道返回内容，则情况更为复杂。如果结果是 ResultSet 对象，则方法 execute 返回 true；如果结果是 Java int，则返回 false。如果返回 int，则意味着结果是更新计数或执行的语句是 DDL 命令。在调用方法 execute 之后要做的第一件事情是调用 getResultSet 或 getUpdateCount。调用方法 getResultSet 可以获得两个或多个 ResultSet 对象中第一个对象；或调用方法 getUpdateCount 可以获得两个或多个更新计数中第一个更新计数的内容。

当 SQL 语句的结果不是结果集时，则方法 getResultSet 将返回 null。这可能意味着结果是一个更新计数或没有其它结果。在这种情况下，判断 null 真正含义的唯一方法是调用方法 getUpdateCount，它将返回一个整数。这个整数为调用语句所影响的行数；如果为 -1 则表示结果是结果集或没有结果。如果方法 getResultSet 已返回 null（表示结果不是 ResultSet 对象），则返回值 -1 表示没有其它结果。也就是说，当下列条件为真时表示没有结果（或没有其它结果）：

((stmt.getResultSet() == null) && (stmt.getUpdateCount() == -1))

如果已经调用方法 getResultSet 并处理了它返回的 ResultSet 对象，则有必要调用方法 getMoreResults 以确定是否有其它结果集或更新计数。如果 getMoreResults 返回 true，则需要再次调用 getResultSet 来检索下一个结果集。如上所述，如果 getResultSet 返回 null，则需要调用 getUpdateCount 来检查 null 是表示结果为更新计数还是表示没有其它结果。

当 getMoreResults 返回 false 时，它表示该 SQL 语句返回一个更新计数或没有其它结果。因此需要调用方法 getUpdateCount 来检查它是哪一种情况。在这种情况下，当下列条件为真时表示没有其它结果：

((stmt.getMoreResults() == false) && (stmt.getUpdateCount() == -1))

下面的代码演示了一种方法用来确认已访问调用方法 execute 所产生的全部结果集和更新计数：

stmt.execute(queryStringWithUnknownResults);

while (true) {

int rowCount = stmt.getUpdateCount();

if (rowCount > 0) { // 它是更新计数

System.out.println("Rows changed = " + count);

stmt.getMoreResults();

continue;

}

if (rowCount == 0) { // DDL 命令或 0 个更新

System.out.println(" No rows changed or statement was DDL

command");

stmt.getMoreResults();

continue;

}

// 执行到这里，证明有一个结果集

// 或没有其它结果

ResultSet rs = stmt.getResultSet;

if (rs != null) {

. . . // 使用元数据获得关于结果集列的信息

while (rs.next()) {

. . . // 处理结果

stmt.getMoreResults();

continue;

}

break; // 没有其它结果


JDBCTM 指南：入门5 - ResultSet

作者：jeru

日期：2000-12-9 21:26:52

5 - ResultSet

本概述是从《JDBCTM Database Access from JavaTM: A Tutorial and Annotated Reference 》这本书中摘引来的。JavaSoft 目前正在准备这本书。这是一本教程，同时也是 JDBC 的重要参考手册，它将作为 Java 系列的组成部份在 1997 年春季由 Addison-Wesley 出版公司出版。

5.1 概述

ResultSet 包含符合 SQL 语句中条件的所有行，并且它通过一套 get 方法（这些 get 方法可以访问当前行中的不同列）提供了对这些行中数据的访问。ResultSet.next 方法用于移动到 ResultSet 中的下一行，使下一行成为当前行。

结果集一般是一个表，其中有查询所返回的列标题及相应的值。例如，如果查询为 SELECT a, b, c FROM Table1，则结果集将具有如下形式：

a b c

-------- --------- --------

12345 Cupertino CA

83472 Redmond WA

83492 Boston MA

下面的代码段是执行 SQL 语句的示例。该 SQL 语句将返回行集合，其中列 1 为 int，列 2 为 String，而列 3 则为字节数组：

java.sql.Statement stmt = conn.createStatement();

ResultSet r = stmt.executeQuery("SELECT a, b, c FROM Table1");

while (r.next())

{

// 打印当前行的值。

int i = r.getInt("a");

String s = r.getString("b");

float f = r.getFloat("c");

System.out.println("ROW = " + i + " " + s + " " + f);

}

5.1.1 行和光标

ResultSet 维护指向其当前数据行的光标。每调用一次 next 方法，光标向下移动一行。最初它位于第一行之前，因此第一次调用 next 将把光标置于第一行上，使它成为当前行。随着每次调用 next 导致光标向下移动一行，按照从上至下的次序获取 ResultSet 行。

在 ResultSet 对象或其父辈 Statement 对象关闭之前，光标一直保持有效。

在 SQL 中，结果表的光标是有名字的。如果数据库允许定位更新或定位删除，则需要将光标的名字作为参数提供给更新或删除命令。可通过调用方法 getCursorName 获得光标名。

注意：不是所有的 DBMS 都支持定位更新和删除。可使用 DatabaseMetaData.supportsPositionedDelete 和 supportsPositionedUpdate 方法来检查特定连接是否支持这些操作。当支持这些操作时，DBMS/驱动程序必须确保适当锁定选定行，以使定位更新不会导致更新异常或其它并发问题。

5.1.2 列

方法 getXXX 提供了获取当前行中某列值的途径。在每一行内，可按任何次序获取列值。但为了保证可移植性，应该从左至右获取列值，并且一次性地读取列值。

列名或列号可用于标识要从中获取数据的列。例如，如果 ResultSet 对象 rs 的第二列名为“title”，并将值存储为字符串，则下列任一代码将获取存储在该列中的值：

String s = rs.getString("title");

String s = rs.getString(2);

注意列是从左至右编号的，并且从列 1 开始。同时，用作 getXXX 方法的输入的列名不区分大小写。

提供使用列名这个选项的目的是为了让在查询中指定列名的用户可使用相同的名字作为 getXXX 方法的参数。另一方面，如果 select 语句未指定列名（例如在“select * from table1”中或列是导出的时），则应该使用列号。这些情况下，用户将无法确切知道列名。

有些情况下，SQL 查询返回的结果集中可能有多个列具有相同的名字。如果列名用作 getXXX 方法的参数，则 getXXX 将返回第一个匹配列名的值。因而，如果多个列具有相同的名字，则需要使用列索引来确保检索了正确的列值。这时，使用列号效率要稍微高一些。

关于 ResultSet 中列的信息，可通过调用方法 ResultSet.getMetaData 得到。返回的 ResultSetMetaData 对象将给出其 ResultSet 对象各列的编号、类型和属性。

如果列名已知，但不知其索引，则可用方法 findColumn 得到其列号。

5.1.3 数据类型和转换

对于 getXXX 方法，JDBC 驱动程序试图将基本数据转换成指定 Java 类型，然后返回适合的 Java 值。例如，如果 getXXX 方法为 getString，而基本数据库中数据类型为 VARCHAR，则 JDBC 驱动程序将把 VARCHAR 转换成 Java String。getString 的返回值将为 Java String 对象。

下表显示了允许用 getXXX 获取的 JDBC 类型及推荐用它获取的 JDBC 类型（通用 SQL 类型）。小写的 x 表示允许 getXXX 方法获取该数据类型；大写的 X 表示对该数据类型推荐使用 getXXX 方法。例如，除了 getBytes 和 getBinaryStream 之外的任何 getXXX 方法都可用来获取 LONGVARCHAR 值，但是推荐根据返回的数据类型使用 getAsciiStream 或 getUnicodeStream 方法。方法 getObject 将任何数据类型返回为 Java Object。当基本数据类型是特定于数据库的抽象类型或当通用应用程序需要接受任何数据类型时，它是非常有用的。

可使用 ResultSet.getXXX 方法获取常见的 JDBC 数据类型。

“x”表示该 getXXX 方法可合法地用于获取给定 JDBC 类型。

“X”表示推荐使用该 getXXX 方法来获取给定 JDBC 类型。

   T

I

N

Y

I

N

T S

M

A

L

L

I

N

T I

N

T

E

G

E

R B

I

G

N

T R

E

A

L F

L

O

A

T D

O

U

B

L

E D

E

C

I

M

A

L N

U

M

E

R

I

C B

I

T C

H

A

R V

A

R

C

H

A

R

L

O

N

G

V

A

R

C

H

A

R B

I

N

A

R

Y V

A

R

B

I

N

A

R

Y L

O

N

G

V

A

R

B

I

N

A

R

Y D

A

T

E T

I

M

E T

I

M

E

S

T

A

M

P

getByte X x x x x x x x x x x x x

getShort x X x x x x x x x x x x x

getInt x x X x x x x x x x x x x

getLong x x x X x x x x x x x x x

getFloat x x x x X x x x x x x x x

getDouble x x x x x X X x x x x x x

getBigDecimal x x x x x x x X X x x x x

getBoolean x x x x x x x x x X x x x

getString x x x x x x x x x x X X x x x x x x x

getBytes 　 　 　 　 　 　 　 　 　 　 　 　 　 X X x

getDate 　 　 　 　 　 　 　 　 　 　 x x x 　 　 　 X 　 x

getTime 　 　 　 　 　 　 　 　 　 　 x x x 　 　 　 　 X x

getTimestamp 　 　 　 　 　 　 　 　 　 　 x x x 　 　 　 x 　 X

getAsciiStream 　 　 　 　 　 　 　 　 　 　 x x X x x x

getUnicodeStream 　 　 　 　 　 　 　 　 　 　 x x X x x x

getBinaryStream 　 　 　 　 　 　 　 　 　 　 　 　 　 x x X

getObject x x x x x x x x x x x x x x x x x x x

5.1.4 对非常大的行值使用流

ResultSet 可以获取任意大的 LONGVARBINARY 或 LONGVARCHAR 数据。方法 getBytes 和 getString 将数据返回为大的块（最大为 Statement.getMaxFieldSize 的返回值）。但是，以较小的固定块获取非常大的数据可能会更方便，而这可通过让 ResultSet 类返回 java.io.Input 流来完成。从该流中可分块读取数据。注意：必须立即访问这些流，因为在下一次对 ResultSet 调用 getXXX 时它们将自动关闭（这是由于基本实现对大块数据访问有限制）。

JDBC API 具有三个获取流的方法，分别具有不同的返回值：

getBinaryStream 返回只提供数据库原字节而不进行任何转换的流。

getAsciiStream 返回提供单字节 ASCII 字符的流。

getUnicodeStream 返回提供双字节 Unicode 字符的流。

注意：它不同于 Java 流，后者返回无类型字节并可（例如）通用于 ASCII 和 Unicode 字符。

下列代码演示了 getAsciiStream 的用法：

java.sql.Statement stmt = con.createStatement();

ResultSet r = stmt.executeQuery("SELECT x FROM Table2");

// 现在以 4K 块大小获取列 1 结果：

byte buff = new byte[4096];

while (r.next()) {

Java.io.InputStream fin = r.getAsciiStream(1);

for (;;) {

int size = fin.read(buff);

if (size == -1) { // 到达流末尾

break;

}

// 将新填充的缓冲区发送到 ASCII 输出流：

output.write(buff, 0, size);

}

}

5.1.5 NULL 结果值

要确定给定结果值是否是 JDBC NULL，必须先读取该列，然后使用 ResultSet.wasNull 方法检查该次读取是否返回 JDBC NULL。

当使用 ResultSet.getXXX 方法读取 JDBC NULL 时，方法 wasNull 将返回下列值之一：

Java null 值：对于返回 Java 对象的 getXXX 方法（例如 getString、getBigDecimal、getBytes、getDate、getTime、getTimestamp、getAsciiStream、getUnicodeStream、getBinaryStream、getObject 等）。

零值：对于 getByte、getShort、getInt、getLong、getFloat 和 getDouble。

false 值：对于 getBoolean。

5.1.6 可选结果集或多结果集

通常使用 executeQuery（它返回单个 ResultSet）或 executeUpdate（它可用于任何数据库修改语句，并返回更新行数）可执行 SQL 语句。但有些情况下，应用程序在执行语句之前不知道该语句是否返回结果集。此外，有些已存储过程可能返回几个不同的结果集和/或更新计数。

为了适应这些情况，JDBC 提供了一种机制，允许应用程序执行语句，然后处理由结果集和更新计数组成的任意集合。这种机制的原理是首先调用一个完全通用的 execute 方法，然后调用另外三个方法，getResultSet、getUpdateCount 和 getMoreResults。这些方法允许应用程序一次一个地研究语句结果，并确定给定结果是 ResultSet 还是更新计数。

用户不必关闭 ResultSet；当产生它的 Statement 关闭、重新执行或用于从多结果序列中获取下一个结果时，该 ResultSet 将被 Statement 自动关闭。


JDBCTM 指南：入门6 - PreparedStatement

作者：jeru

日期：2000-12-9 21:28:13

6 - PreparedStatement

本概述是从《JDBCTM Database Access from JavaTM: A Tutorial and Annotated Reference 》这本书中摘引来的。JavaSoft 目前正在准备这本书。这是一本教程，同时也是 JDBC 的重要参考手册，它将作为 Java 系列的组成部份在 1997 年春季由 Addison-Wesley 出版公司出版。

6.1 概述

该 PreparedStatement 接口继承 Statement，并与之在两方面有所不同：

PreparedStatement 实例包含已编译的 SQL 语句。这就是使语句“准备好”。

包含于 PreparedStatement 对象中的 SQL 语句可具有一个或多个 IN 参数。IN 参数的值在 SQL 语句创建时未被指定。相反的，该语句为每个 IN 参数保留一个问号（“？”）作为占位符。每个问号的值必须在该语句执行之前，通过适当的 setXXX 方法来提供。

由于 PreparedStatement 对象已预编译过，所以其执行速度要快于 Statement 对象。因此，多次执行的 SQL 语句经常创建为 PreparedStatement 对象，以提高效率。

作为 Statement 的子类，PreparedStatement 继承了 Statement 的所有功能。另外它还添加了一整套方法，用于设置发送给数据库以取代 IN 参数占位符的值。同时，三种方法 execute、 executeQuery 和 executeUpdate 已被更改以使之不再需要参数。这些方法的 Statement 形式（接受 SQL 语句参数的形式）不应该用于 PreparedStatement 对象。

6.1.1 创建 PreparedStatement 对象

以下的代码段（其中 con 是 Connection 对象）创建包含带两个 IN 参数占位符的 SQL 语句的 PreparedStatement 对象：

PreparedStatement pstmt = con.prepareStatement(

"UPDATE table4 SET m = ? WHERE x = ?");

pstmt 对象包含语句 "UPDATE table4 SET m = ? WHERE x = ?"，它已发送给 DBMS，并为执行作好了准备。

6.1.2 传递 IN 参数

在执行 PreparedStatement 对象之前，必须设置每个 ? 参数的值。这可通过调用 setXXX 方法来完成，其中 XXX 是与该参数相应的类型。例如，如果参数具有 Java 类型 long，则使用的方法就是 setLong。setXXX 方法的第一个参数是要设置的参数的序数位置，第二个参数是设置给该参数的值。例如，以下代码将第一个参数设为 123456789，第二个参数设为 100000000：

pstmt.setLong(1, 123456789);

pstmt.setLong(2, 100000000);

一旦设置了给定语句的参数值，就可用它多次执行该语句，直到调用 clearParameters 方法清除它为止。

在连接的缺省模式下（启用自动提交），当语句完成时将自动提交或还原该语句。

如果基本数据库和驱动程序在语句提交之后仍保持这些语句的打开状态，则同一个 PreparedStatement 可执行多次。如果这一点不成立，那么试图通过使用 PreparedStatement 对象代替 Statement 对象来提高性能是没有意义的。

利用 pstmt（前面创建的 PreparedStatement 对象），以下代码例示了如何设置两个参数占位符的值并执行 pstmt 10 次。如上所述，为做到这一点，数据库不能关闭 pstmt。在该示例中，第一个参数被设置为 "Hi"并保持为常数。在 for 循环中，每次都将第二个参数设置为不同的值：从 0 开始，到 9 结束。

pstmt.setString(1, "Hi");

for (int i = 0; i < 10; i++) {

pstmt.setInt(2, i);

int rowCount = pstmt.executeUpdate();

}

6.1.3 IN 参数中数据类型的一致性

setXXX 方法中的 XXX 是 Java 类型。它是一种隐含的 JDBC 类型（一般 SQL 类型），因为驱动程序将把 Java 类型映射为相应的 JDBC 类型（遵循该 JDBC Guide中§8.6.2 “映射 Java 和 JDBC 类型”表中所指定的映射），并将该 JDBC 类型发送给数据库。例如，以下代码段将 PreparedStatement 对象 pstmt 的第二个参数设置为 44，Java 类型为 short：

pstmt.setShort(2, 44);

驱动程序将 44 作为 JDBC SMALLINT 发送给数据库，它是 Java short 类型的标准映射。

程序员的责任是确保将每个 IN 参数的 Java 类型映射为与数据库所需的 JDBC 数据类型兼容的 JDBC 类型。不妨考虑数据库需要 JDBC SMALLINT 的情况。如果使用方法 setByte ，则驱动程序将 JDBC TINYINT 发送给数据库。这是可行的，因为许多数据库可从一种相关的类型转换为另一种类型，并且通常 TINYINT 可用于 SMALLINT 适用的任何地方。然而，对于要适用于尽可能多的数据库的应用程序，最好使用与数据库所需的确切的 JDBC 类型相应的 Java 类型。如果所需的 JDBC 类型是 SMALLINT，则使用 setShort 代替 setByte 将使应用程序的可移植性更好。

6.1.4 使用 setObject

程序员可使用 setObject 方法显式地将输入参数转换为特定的 JDBC 类型。该方法可以接受第三个参数，用来指定目标 JDBC 类型。将 Java Object 发送给数据库之前，驱动程序将把它转换为指定的 JDBC 类型。

如果没有指定 JDBC 类型，驱动程序就会将 Java Object 映射到其缺省的 JDBC 类型（参见第 8.6.4 节中的表格），然后将它发送到数据库。这与常规的 setXXX 方法类似；在这两种情况下，驱动程序在将值发送到数据库之前，会将该值的 Java 类型映射为适当的 JDBC 类型。二者的差别在于 setXXX 方法使用从 Java 类型到 JDBC 类型的标准映射（参见第 8.6.2 节中的表格），而 setObject 方法使用从 Java Object 类型到 JDBC 类型的映射（参见第 8.6.4 节中的表格）。

方法 setObject 允许接受所有 Java 对象的能力使应用程序更为通用，并可在运行时接受参数的输入。这种情况下，应用程序在编译时并不清楚输入类型。通过使用 setObject，应用程序可接受所有 Java 对象类型作为输入，并将其转换为数据库所需的 JDBC 类型。第 8.6.5 节中的表格显示了 setObject 可执行的所有可能转换。

6.1.5 将 JDBC NULL 作为 IN 参数发送

setNull 方法允许程序员将 JDBC NULL 值作为 IN 参数发送给数据库。但要注意，仍然必须指定参数的 JDBC 类型。

当把 Java null 值传递给 setXXX 方法时（如果它接受 Java 对象作为参数），也将同样把 JDBC NULL 发送到数据库。但仅当指定 JDBC 类型时，方法 setObject 才能接受 null 值。

6.1.6 发送大的 IN 参数

setBytes 和 setString 方法能够发送无限量的数据。但是，有时程序员更喜欢用较小的块传递大型的数据。这可通过将 IN 参数设置为 Java 输入流来完成。当语句执行时，JDBC 驱动程序将重复调用该输入流，读取其内容并将它们当作实际参数数据传输。

JDBC 提供了三种将 IN 参数设置为输入流的方法：setBinaryStream 用于含有未说明字节的流， setAsciiStream 用于含有 ASCII 字符的流，而 setUnicodeStream 用于含有 Unicode 字符的流。因为必须指定流的总长度，所以这些方法所采用的参数比其它的 setXXX 方法要多一个。这很有必要，因为一些数据库在发送数据之前需要知道其总的传送大小。

以下代码例示了使用流作为 IN 参数来发送文件内容：

java.io.File file = new java.io.File("/tmp/data");

int fileLength = file.length();

java.io.InputStream fin = new java.io.FileInputStream(file);

java.sql.PreparedStatement pstmt = con.prepareStatement(

"UPDATE Table5 SET stuff = ? WHERE index = 4");

pstmt.setBinaryStream (1, fin, fileLength);

pstmt.executeUpdate();

当语句执行时，将反复调用输入流 fin 以传递其数据。


JDBCTM 指南：入门7 - CallableStatement

作者：jeru

日期：2000-12-9 21:29:10

7 - CallableStatement

本概述是从《JDBCTM Database Access from JavaTM: A Tutorial and Annotated Reference 》这本书中摘引来的。JavaSoft 目前正在准备这本书。这本书是一本教程，同时也是 JDBC 的重要参考手册，它将作为 Java 系列的组成部份在 1997 年春季由 Addison-Wesley 出版公司出版。

7.1 概述

CallableStatement 对象为所有的 DBMS 提供了一种以标准形式调用已储存过程的方法。已储存过程储存在数据库中。对已储存过程的调用是 CallableStatement 对象所含的内容。这种调用是用一种换码语法来写的，有两种形式：一种形式带结果参数，另一种形式不带结果参数（有关换码语法的信息，参见第 4 节“语句”）。结果参数是一种输出 (OUT) 参数，是已储存过程的返回值。两种形式都可带有数量可变的输入（IN 参数）、输出（OUT 参数）或输入和输出（INOUT 参数）的参数。问号将用作参数的占位符。

在 JDBC 中调用已储存过程的语法如下所示。注意，方括号表示其间的内容是可选项；方括号本身并不是语法的组成部份。

{call 过程名[(?, ?, ...)]}

返回结果参数的过程的语法为：

{? = call 过程名[(?, ?, ...)]}

不带参数的已储存过程的语法类似：

{call 过程名}

通常，创建 CallableStatement 对象的人应当知道所用的 DBMS 是支持已储存过程的，并且知道这些过程都是些什么。然而，如果需要检查，多种 DatabaseMetaData 方法都可以提供这样的信息。例如，如果 DBMS 支持已储存过程的调用，则 supportsStoredProcedures 方法将返回 true，而 getProcedures 方法将返回对已储存过程的描述。

CallableStatement 继承 Statement 的方法（它们用于处理一般的 SQL 语句），还继承了 PreparedStatement 的方法（它们用于处理 IN 参数）。CallableStatement 中定义的所有方法都用于处理 OUT 参数或 INOUT 参数的输出部分：注册 OUT 参数的 JDBC 类型（一般 SQL 类型）、从这些参数中检索结果，或者检查所返回的值是否为 JDBC NULL。

7.1.1 创建 CallableStatement 对象

CallableStatement 对象是用 Connection 方法 prepareCall 创建的。下例创建 CallableStatement 的实例，其中含有对已储存过程 getTestData 调用。该过程有两个变量，但不含结果参数：

CallableStatement cstmt = con.prepareCall(

"{call getTestData(?, ?)}");

其中 ? 占位符为 IN、 OUT 还是 INOUT 参数，取决于已储存过程 getTestData。

7.1.2 IN 和 OUT 参数

将 IN 参数传给 CallableStatement 对象是通过 setXXX 方法完成的。该方法继承自 PreparedStatement。所传入参数的类型决定了所用的 setXXX 方法（例如，用 setFloat 来传入 float 值等）。

如果已储存过程返回 OUT 参数，则在执行 CallableStatement 对象以前必须先注册每个 OUT 参数的 JDBC 类型（这是必需的，因为某些 DBMS 要求 JDBC 类型）。注册 JDBC 类型是用 registerOutParameter 方法来完成的。语句执行完后，CallableStatement 的 getXXX 方法将取回参数值。正确的 getXXX 方法是为各参数所注册的 JDBC 类型所对应的 Java 类型（从 JDBC 类型到 Java 类型的标准映射见 8.6.1 节中的表）。换言之， registerOutParameter 使用的是 JDBC 类型（因此它与数据库返回的 JDBC 类型匹配），而 getXXX 将之转换为 Java 类型。

作为示例，下述代码先注册 OUT 参数，执行由 cstmt 所调用的已储存过程，然后检索在 OUT 参数中返回的值。方法 getByte 从第一个 OUT 参数中取出一个 Java 字节，而 getBigDecimal 从第二个 OUT 参数中取出一个 BigDecimal 对象（小数点后面带三位数）：

CallableStatement cstmt = con.prepareCall(

"{call getTestData(?, ?)}");

cstmt.registerOutParameter(1, java.sql.Types.TINYINT);

cstmt.registerOutParameter(2, java.sql.Types.DECIMAL, 3);

cstmt.executeQuery();

byte x = cstmt.getByte(1);

java.math.BigDecimal n = cstmt.getBigDecimal(2, 3);

CallableStatement 与 ResultSet 不同，它不提供用增量方式检索大 OUT 值的特殊机制。

7.1.3 INOUT 参数

既支持输入又接受输出的参数（INOUT 参数）除了调用 registerOutParameter 方法外，还要求调用适当的 setXXX 方法（该方法是从 PreparedStatement 继承来的）。setXXX 方法将参数值设置为输入参数，而 registerOutParameter 方法将它的 JDBC 类型注册为输出参数。setXXX 方法提供一个 Java 值，而驱动程序先把这个值转换为 JDBC 值，然后将它送到数据库中。

这种 IN 值的 JDBC 类型和提供给 registerOutParameter 方法的 JDBC 类型应该相同。然后，要检索输出值，就要用对应的 getXXX 方法。例如，Java 类型为 byte 的参数应该使用方法 setByte 来赋输入值。应该给 registerOutParameter 提供类型为 TINYINT 的 JDBC 类型，同时应使用 getByte 来检索输出值 （第 8 节“JDBC 和 Java 类型之间的映射”将给出详细信息和类型映射表）。

下例假设有一个已储存过程 reviseTotal，其唯一参数是 INOUT 参数。方法 setByte 把此参数设为 25，驱动程序将把它作为 JDBC TINYINT 类型送到数据库中。接着，registerOutParameter 将该参数注册为 JDBC TINYINT。执行完该已储存过程后，将返回一个新的 JDBC TINYINT 值。方法 getByte 将把这个新值作为 Java byte 类型检索。

CallableStatement cstmt = con.prepareCall(

"{call reviseTotal(?)}");

cstmt.setByte(1, 25);

cstmt.registerOutParameter(1, java.sql.Types.TINYINT);

cstmt.executeUpdate();

byte x = cstmt.getByte(1);

7.1.4 先检索结果，再检索 OUT 参数

由于某些 DBMS 的限制，为了实现最大的可移植性，建议先检索由执行 CallableStatement 对象所产生的结果，然后再用 CallableStatement.getXXX 方法来检索 OUT 参数。

如果 CallableStatement 对象返回多个 ResultSet 对象（通过调用 execute 方法），在检索 OUT 参数前应先检索所有的结果。这种情况下，为确保对所有的结果都进行了访问，必须对 Statement 方法 getResultSet、getUpdateCount 和 getMoreResults 进行调用，直到不再有结果为止。

检索完所有的结果后，就可用 CallableStatement.getXXX 方法来检索 OUT 参数中的值。

7.1.5 检索作为 OUT 参数的 NULL 值

返回到 OUT 参数中的值可能会是 JDBC NULL。当出现这种情形时，将对 JDBC NULL 值进行转换以使 getXXX 方法所返回的值为 null、0 或 false，这取决于 getXXX 方法类型。对于 ResultSet 对象，要知道 0 或 false 是否源于 JDBC NULL 的唯一方法，是用方法 wasNull 进行检测。如果 getXXX 方法读取的最后一个值是 JDBC NULL，则该方法返回 true，否则返回 flase。第 5 节“ResultSet”将给出详细信息。


JDBC和ODBC (转）

作者：blackwhites

日期：2000-10-31 11:16:27

异构数据库系统是由多个异构的成员数据库系统组成的数据库系统，异构性体现为各个成员数据库之间在硬件平台、操作系统或数据库管理系统等方面的不同。

----Internet上大量信息必须通过数据库系统才能有效管理。由于历史等原因，Internet上的数据库系统不少是异构的。为了在Internet环境下，基于异种系统平台实现对异构数据库的查询和联合使用，必须提供一个独立于特定的数据库管理系统的统一编程界面。

----目前许多数据库系统均支持SQL，对于由SQL数据库组成的异构数据库系统，JDBC和ODBC为访问其异构成员提供了统一的方式，也为各异构成员之间的协作和多个成员之上的操作打下了基础。本文简单介绍了JDBC和ODBC对各个异构的数据库进行统一访问和协作的原理及其应用。

ODBC

----ODBC（Open DataBase Connectivity）是微软倡导的、当前被业界广泛接受的、用于数据库访问的应用程序编程接口（API），它以X/Open和 ISO/IEC的调用级接口(CLI)规范为基础，并使用结构化查询语言（SQL）作为其数据库访问语言。 ODBC总体结构有四个组件：

----· 应用程序　执行处理并调用ODBC API函数，以提交 SQL语句并检索结果。

----· 驱动程序管理器（Driver Manager）　根据应用程序需要加载/卸载驱动程序，处理ODBC函数调用，或把它们传送到驱动程序。

----· 驱动程序　处理ODBC函数调用，提交SQL请求到一个指定的数据源，并把结果返回到应用程序。如果有必要，驱动程序修改一个应用程序请求，以使请求与相关的DBMS支持的语法一致。

----· 数据源　包括用户要访问的数据及其相关的操作系统、DBMS及用于访问DBMS的网络平台。

----ODBC驱动程序的使用把应用程序从具体的数据库调用中隔离开来，驱动程序管理器针对特定数据库的各个驱动程序进行集中管理，并向应用程序提供统一的标准接口，这就为ODBC的开放性奠定了基础。

----数据库独立性

----ODBC是为最大的互用性而设计的，要求一个应用程序有用相同的源代码（不用重新编译或重新链接）访问不同的数据库管理系统(DBMS)的能力。

----ODBC定义了一个标准的调用层接口（CLI）。这包含 X/Open和ISO/IEC的CLI规范中的所有函数，并提供应用程序普遍需要的附加函数。每个支持ODBC的DBMS需要不同的库或驱动程序，驱动程序实现ODBC API中的函数。当需要改变驱动程序时，应用程序不需要重新编译或者重新链接，只是动态加载新的驱动程序，并调用其中的函数即可。如果要同时访问多个DBMS系统，应用程序可加载多个驱动程序。如何支持驱动程序取决于操作系统，例如，在Windows操作系统上，驱动程序是动态链接库（DLL）。

----使用相同源代码的应用程序访问不同的DBMS，体现了 ODBC的数据库独立性。

----对数据库特殊功能的支持

----各个DBMS参照的标准、提供的功能不尽相同，应用程序如何以统一的接口使用各个DBMS特有的功能呢？

----一方面，ODBC为所有DBMS功能都定义了公共接口。这些DBMS功能比多数DBMS支持的更多，但只要求驱动程序实现这些功能的一个子集。另一方面，ODBC定义了API和SQL语法一致层，它规定驱动程序应支持的基本功能。 ODBC还提供两个函数（SQLGetInfo和SQLGetFunctions）返回关于驱动程序和DBMS能力的一般信息及驱动程序支持的函数列表。因此，应用程序可以检查DBMS支持的特殊功能。

----这样，编写应用程序时，就可以检查并自动使用各个驱动程序对应的DBMS所支持的特殊功能。这样做的优点是当增加DBMS支持的功能时，应用程序不需要改变，只需安装更新的驱动程序，应用程序便可以自动发现并使用这些功能。

----互操作能力

----通过使用多个驱动程序可以同时访问多个DBMS系统。 ODBC提供的Driver Manager实现所有的ODBC函数，多数是传递调用给驱动程序中的ODBC 函数，并静态链接应用程序，或在应用程序运行时加载它。这样，应用程序在Driver Manager 中按名调用驱动ODBC函数，而不是通过每个驱动程序中的指针。当应用程序需要通过特定的驱动程序时，它首先需要一个标识驱动程序的连接句柄。Driver Manager加载驱动程序，并存储每个驱动程序中的函数地址。要使驱动程序调用一个ODBC函数，可在应用程序调用 Driver Manager中的函数，并为驱动程序传送连接句柄，然后Driver Manager使用以前存储的地址来调用函数。

----ODBC可以同时连接到多个DBMS，解决了同时访问多个DBMS的问题，提供了异构成员数据库之间互操作的能力。

JDBC

----JDBC(Java DataBase Connectivity)是Java与数据库的接口规范，JDBC定义了一个支持标准SQL功能的通用低层的应用程序编程接口(API)，它由Java 语言编写的类和接口组成，旨在让各数据库开发商为Java程序员提供标准的数据库API。 JDBC API定义了若干Java中的类，表示数据库连接、SQL指令、结果集、数据库元数据等。它允许Java程序员发送SQL指令并处理结果。通过驱动程序管理器,JDBC API可利用不同的驱动程序连接不同的数据库系统。

----JDBC与ODBC都是基于X/Open的SQL调用级接口， JDBC的设计在思想上沿袭了ODBC，同时在其主要抽象和SQL CLI实现上也沿袭了ODBC，这使得JDBC容易被接受。JDBC的总体结构类似于ODBC，也有四个组件：应用程序、驱动程序管理器、驱动程序和数据源。

----JDBC保持了ODBC的基本特性，也独立于特定数据库。使用相同源代码的应用程序通过动态加载不同的JDBC驱动程序，可以访问不同的DBMS。连接不同的DBMS时，各个DBMS之间仅通过不同的URL进行标识。JDBC的 DatabaseMetaData接口提供了一系列方法，可以检查DBMS对特定特性的支持，并相应确定有什么特性，从而能对特定数据库的特性予以支持。与ODBC一样，JDBC也支持在应用程序中同时建立多个数据库连接，采用JDBC可以很容易地用SQL语句同时访问多个异构的数据库，为异构的数据库之间的互操作奠定基础。

----但是，JDBC除了具有ODBC的上述特点外，更具有对硬件平台、操作系统异构性的支持。这主要是因为ODBC使用的是C语言，而JDBC使用的是Java语言。Java语言具有与平台无关、移植性强、安全性高、稳定性好、分布式、面向对象等众多优点，而JDBC确保了“100％纯Java”的解决方案，利用Java的平台无关性， JDBC应用程序可以自然地实现跨平台特性，因而更适合于Internet上异构环境的数据库应用。

----此外，JDBC驱动程序管理器是内置的，驱动程序本身也可通过Web浏览器自动下载，无须安装、配置；而ODBC驱动程序管理器和ODBC驱动程序必须在每台客户机上分别安装、配置。

JDBC和ODBC在Internet上的应用

----JDBC和ODBC由于具有数据库独立性甚至平台无关性，因而对Internet上异构数据库的访问提供了很好的支持。

----在Internet上访问数据库通常采用三层模式。以JDBC为例，在三层模式中客户端的Java Applet主要作为用户界面，它不直接与数据库交换信息，而是通过自定义的应用层网络协议与应用服务器交互，应用服务器通过JDBC与数据库服务器交换信息,并实现应用逻辑。DM3的JDBC和ODBC驱动程序支持目前流行的ASP和JSP技术，可以分别借助ODBC和JDBC同时访问Internet上多个异构的数据库。

----ASP是面向Web服务器的技术，客户端浏览器不需要任何附加的软件支持。ASP使用VBScript之类的脚本语言，它在HTML代码中嵌入某种程序代码，由HTML代码负责描述信息的显示样式，由嵌入的程序代码来描述处理逻辑。在ASP 下，VBScript代码被ASP引擎在Web服务器端解释执行，执行结果被重新嵌入到HTML代码中，然后一起发送给浏览器。这里，VBScript代码可以通过ODBC访问多个异构的数据库。

----JSP是一种基于Java Servlet的Web开发技术，它和ASP 非常相似，但又有区别：在JSP下，嵌入HTML页面的程序代码是Java代码；页面中嵌入的程序代码被编译成Servlet（这种编译操作仅在对JSP页面的第一次请求时发生）并由Java 虚拟机执行。这里Java代码可以通过JDBC访问多个异构的数据库，其平台无关性特别好。当前，Internet上的数据库应用已越来越多，JDBC和ODBC必将在Internet上的异构数据库访问中发挥重要的作用。


JDBC技术介绍 转

作者：有所得

日期：2000-11-16 10:35:22

1. 介绍

    许多开发者和用户都在寻找Java程序中访问数据库的便捷方法。由于Java是一个健壮，安全，易于使用的，易于理解且可以从网络中自动download ，所以它成为开发数据库应用的一种良好的语言基础。它提供了C,C++,Smalltalk, BASIC, COBOL, and 4GLs的许多优点。许多公司已经开始在Java与DBMS的连接方面做工作。

    许多Java应用开发者都希望能够编写独立于特定DBMS的程序，而我们也相信一个独立于DBMS的接口将使得与各种各样DBMS连接变得最为便捷，开发更加迅速。所以我们认为定义一个通用的SQL数据库存取框架，在各种各样的提供数据库连接模块上提供统一的界面是十分有意义的。这使程序员可以面对单一的数据库界面，使数据库无关的Java工具和产品成为可能，使得数据库连接的开发者可以提供各种各样的连接方案。我们看到我们定义一个通用低层的，支持基本SQL功能的JavaDataBase Connectivity (JDBC)API的紧迫任务。

    幸运的是我们不必从头设计一个SQL API。我们可以把我们的工作建立在 X/Open SQL CLI (调用层接口)之上(它也是Microsoft's ODBC 的基础)。

    我们主要任务是定义一个自然的Java接口来与X/Open CLI中定义的基本的抽象层和概念连接。

    JDBC API得到数据库开发厂商，连接开发厂商，ISV,以及应用开发者的支持是十分重要的。我们相信把我们的工作建立在ODBC抽象层的基础上将JDBC更加容易得到大家的接受。而且从技术上来说，ODBC是我们设计工作的一个良好基础。

    因为ODBC是一个C语言接口，所以ODBC在Java中直接使用不适当。从Java中来调用C代码在安全性，健壮性，实现的方便，可移植性等等方面有许多不便。它使得Java在这些方面的许多优点得不到发挥。

    我们已经在短期里面实现了一个建立在ODBC上的API。长远来看，我们可以通过其他方式提供实现。

1. 1. 注意

    我们非常感谢在数据库，数据库连接和数据库工具领域的许多早期的工作者。他们为JDBC的早期草案提供了很好的意见和建议。他们的工作对本规范起了不可估量的作用。

2. 目标与哲学

    这个部分描述了指引这个API开发的目标以及哲学。

2. 1. SQL 级 API

    我们的主要目标是为Java定义一个“调用级”(call-level)的SQL接口。着意味着我们主要的注意力集中在执行原原本本的SQL语句并且取回结果。我们预计高层的API也将被定义，这些可能将建立在基层的接口上。

    这些高层接口包括象直接地、透明地把表里面的数据影射到Java类里面，用语法树表示更加通用的查询，以及Java内嵌的SQL语法。

    我们希望大量的应用开发工具将使用我们的API。然而我们也希望程序员能够使用我们的API，尤其是目前这样在Java里没有任何其他手段(应该是说数据库访问手段)的情况下。

2. 2. 遵循SQL

    数据库系统支持各式各样的SQL语法和语义，它们相互之间在比较高级的功能例如外部连接，内嵌过程等方面并不一致，尽管我们能够盼望着随时间的推移这些部分的SQL可以获得标准化。同时我们采取这样的态度与立场：

    In fact, an application query need not even be SQL, or it may be a specialized derivative of SQL, e.g. for document or image queries, designed for specific DBMSs.

    In order to pass JDBC compliance tests and to be called "JDBC COMPLIANT " we require that a driver support at least ANSI SQL-2 Entry Level. This gives applications that want wide portability a guaranteed least common denominator. We believe ANSI SQL-2 Entry Level is reasonably powerful and is reasonably widely supported today.

* JDBC允许查询表达式直接传递到底层的数据驱动，这样一个程序可以获得尽量多的SQL功能，但是可能被DBMS拒绝。事实上，一个程序的查询甚至可以不是SQL的，或者是SQL的一个特殊演化，例如：为专门数据库设计的文本或者图形查询。

* 为了通过JDBC兼容的测试，并且能够被称为JDBC兼容，我们要求一个驱动至少支持ANSI SQL-2的标准。这使得那些需要广泛移植性的程序获得一个最小的分母(这句话的原文是：This gives applications that want wide portability a guaranteed least common denominator.)。我们相信ANSI SQL-2是足够强大的，并且是得到足够支持的。

2. 3. JDBC必须可以建立在现有的数据库接口上

    我们必须能够保证 JDBC SQL API 能够建立在普通的SQL API上，尤其是ODBC。这些要求已经对这个规范的一些部分产生了影响，尤其是对传出参数(OUT parameter)和大数据块的处理。

2. 4. 必须保证这个接口与JAVA系统的其他部分保持一致

  目前对JAVA的积极回应已经十分热烈。很大程度上是由于这个语言标准以及标准运行时库被认为是一致，简单和强大的。我们将尽我们所能，提供这个Java数据库接口，这个接口将建立在Java内核现有的这种风格，并且将进一步加强它。

2. 5. 保持简单

    We would prefer to keep this base API as simple as possible, at least initially. In general we would prefer to provide a single mechanism for performing a particular task, and avoid provid-ing duplicate mechanisms. We will extend the API later if any important functionality is miss-ing.

    我们将力争使得基本的API尽量简单，至少开始的时候是这样的。一般来说，我们希望对实现每个特定的任务只提供一种方案，而避免提供多种方案。如果一些重要的功能遗漏了，那么我们在晚些时候将扩充这个API。

2. 6. 尽量保持强的、静态的类型

    我们希望这个JDBC API保持尽量强的类型检查，使得尽可能多的类型信息可以静态地表达。着使得尽可能多的错误可以在编译的时候被发现。

    由于SQL本身是动态类型的，所以我们可能会在程序运行的时候遇到类型不能匹配的问题。例如：当一个程序员在希望SELECT返回一个整数，但是实际返回的是一个字符串“foo”. 但是我们依然希望程序员把他们所希望的类型在编译的时候就能够表达清楚，这样我们可以做尽可能多的静态检查。我们也希望在必要的时候能够支持动态类型接口（见第四章）

2. 7. 使普通任务简化

    我们希望普通的任务能够是简单的，而不一般的工作是可行的。

    一个普通任务是指一个程序员执行一个简单的没有参数的SQL语句（例如:SELECT,INSERT,UPDATE,DELETE），然后(例如SELECT)处理返回的具有简单类型的元组。一个具有传入参数（IN parameter）的SQL语句也是普通的。

    不那么普通但是也是十分重要的情形是当程序员使用有INOUT,OUT参数的SQL语句。我们也需要支持读写几兆字节对象的SQL语句，更特别一些的情形包括一个语句返回了多个结果集合。

    我们希望元数据(Meatdata)的使用很少的，只是那些熟练的程序员以及开发工具才需要处理的问题。元数据存取函数以及动态类型数据存取函数在这个文档末尾，一般的程序员可以不必关心这些章节。

2. 8. 不同的功能让不同的方法（函数）来实现（“方法”的原文是：method，这样翻译是跟VB的）

    一种界面设计风格是使用很少的过程，提供许多作为参数传递的控制标志，这样它们可以用来影响很大一个范围内的各种行为。来表达不同的功能。这趋向与使用很多的方法，但是每个方法都比较同意理解。

    一般来说，Java内核类使用不同的方法（method）。这个步骤的主要优点是开始学习基本界面的程序员可以不必被那些与复杂功能相关的参数所困扰。我们力图在JDBC接口上也采用相同的策略。一般来说采用不同的方法而不是采用不同的标志和多用途的方法。

3. 接口概貌

    接口分为两个层次，一个是面向程序开发人员的JDBC API。另外一个是底层的JDBC Driver API。

3. 1. JDBC API

    JDBC API 被描述成为彝族抽象的Java接口，似的应用程序远可以对某个数据库打开连接，执行SQL语句并且处理结果。最重要的接口是：

* java.sql.DriverManager 处理驱动的调入并且对产生新的数据库连接提供支持。

* java.sql.Connection 代表对特定数据库的连接。

* java.sql.Statement 　代表一个特定的容器，来对一个特定的数据库执行SQL语句。

* java.sql.ResultSet 　控制对一个特定语句的行数据的存取。

其中java.sql.Statement又有两个子类型：

1. java.sql.PreparedStatement 　用于执行预编译的SQL语句。

2. java.sql.CallableStatement 　用于执行对一个数据库内嵌过程的调用。

    下面的章节对JDBC是如何运行的提供了更多描述，整个定义见第13章。另外第15章描述了系统如果获取数据库的元数据信息。

3. 2. JDBC Driver API

    java.sql.Driver在第9章有完整的定义了.大部分JDBC驱动只需要完成这些JDBC API所定义的抽象类就可以了。特别地，所有的driver必须提供对java.sql.Connection, java.sql. State-ment, java.sql.Prepared－Statement, and java.sql.ResultSet的实现。如果目标DBMS提供有OUT参数的内嵌过程，那么还必须提供java.sql.CallableStatement 接口。 每个database driver必须提供一个类：java.sql.Driver以使得系统可以由 java.sql.DriverManager来管理。

    一个显然的driver是在ODBC之上提供对JDBC的实现，从而提供与ODBC接口的JDBC-ODBC 桥，就象前面的图所显示的.由于JDBC放在ODBC之后，所以实现起来简单而且高效。

    另外一个有用的驱动直接接触数据库无关的网络协议。发布一个协议允许多个服务器实现的方法，例如在ODBC或者特定的DBMS上（尽管已经有了一些使用固定协议的产品，但是我们不打算对它们实现标准化。），是可取的。

4. JDBC使用场合

    Before looking at specifics of the JDBC API, an understanding of typical use scenarios is help-ful. There are two common scenarios that must be treated differently for our purposes: applets and applications.

    在看JDBC API之前了解一下典型的使用场合是有帮助的。通常有两种情形必须分别对待：applet和application.

4. 1. Applet

    目前Java使用的最多的从网络中下载的applet，它们作为web文件的一个部分。当中有数据库存取applet和能够使用JDBC来接触数据库的applet。例如，一个用户可能下载一个显示股票历史价格图的applet。这个applet通过internet来从关系数据库中获得股票历史价格。

    最一般的情况里面，对applet的使用是通过不可靠的边界的。例如从另外一个公司或者Internet上获得这些applet。于是称这个情况为"Internet"场合。然而applet也可能通过局域网下载。在这个情况里面，客户机的安全都还是一个问题。

典型的applet在几个方面与传统的数据库应用程序有所不同：

1). 不可靠的applet被严格地限制在他们被允许执行的的操作上。特别地，不允许他们存取本地的文件，切不允许他们对任意的数据库建立网络连接。

2). 就标识和连接网上数据库来说，Internet环境里面的applet面临新的问题。

3). 当数据库可能与你相隔万里的时候，效率的考虑也有所不同了。与局域网相比，Internet上数据库applet可能会碰到十分不同的反应时间。

4. 2. Application

    Java也可以用来建立普通的应用，从而想一般的应用一样在客户机上使用。我们相信随着开发工具越来越多，人们开始认识到提高程序生产效率的必要性，以及Java的其他优点，Java的这种用法将越来越流行。在这种方式里面，Java的代码是可以信赖的，且被允许读写文件打开网络连接等等，就想其他的应用程序代码一样。

    也许这些Java应用使用的最多的是在一个公司内部或者在Intranet上，所以不妨成为Intranet场合。例如一个公司希望利用Java及其GUI构件工具来建立他的基于合作数据模式的合作软件。这些应用程序将存取局域网或者广域网的数据。Java应用可以作到这些。

    Java应用程序场合和Intranet场合与applet场合有诸多不同。例如标定一个数据库最自然的方式是用一个数据库的名字，就象"Customers" 和"Personnel"这样。然后用户希望系统能够定位具体的机器，DBMS，JDBC driver，和Java应用程序。

4. 3. 其他场合

还有其他一些有趣的场合：

1). 已验证的applet（Trusted applets）是指那些已经被Java虚拟机器认定是可以信赖的applet。他们之所以被认为是可信的是因为他们已经对上了特定的密匙，或者用户认为从特定来源来的applet是可信的。在安全的方面上他们与应用（appliction）相同，但是其他方面（例如定位一个数据库）与则与applet相似。

2). 与直接从Java GUI出发用客户/服务器模式来度曲DBMS服务器不同，三层存取方式可能被使用。在这个场合里面，Java应用程序对中间层的服务发出调用，中间层的服务在网上，它又再去调用数据库。这些调用可能通过RPC (remote procedure call)或者ORB (object request broker )。在这两种场合里面，中间层最好使用一个对象变化。我们希望三层结构会变得越来越普遍，因为对于MIS管理者来说，这可以使得他们有机会在公共数据库上显式地定义合法操作等。同时三层结构可以提供许多效率上的好处。

    目前中间层一般用C或者C++这样的语言来完成。通过优化编译器把把Java 字节代码翻译成为高效的机器代码，中间层也可以用Java来实现。Java有许多优良特性（健壮性，安全性，多线程）可以达到中间层需要达到的目的。

5. 安全性考虑

    作为网络上的语言JAVA必须十分注安全性的考虑。基于上面的讨论，JDBC的两种主要使用场合里面，我们必须考虑安全性问题：

* 在Java applications的场合里面Java代码是本地的，所以也是"trusted"

* 没有验证的Java applet代码不可以存取本地的以及其他网络的数据。

5. 1. JDBC 和未验证的applet

JDBC首先必须符合JAVA的一般安全规则。另外：

* JDBC 必须认为没有验证的applets是不可靠的。

* JDBC 不可以让不可靠的applets存取本地数据库。

* 一个已经向JDBC DriverManager注册的是JDBC Driver只能存取它所来的数据源。

* 一个applet也只能向它所Download来的服务器来存取数据。

    如果JDBC驱动层如果完全确信对一个数据库服务器打开连接不会引起认证或者权限问题（可能由网上随机主机上运行的程序引起），那么它就允许applet打开这样的连接。数据库服务器不通过IP地址来限制存取是相当少的，主要是为了举例。（当心，这一段话我可能翻译反了！！！大家看看原文。）这些限制是相当烦琐的。不过他们与对一般applet的限制是一致的我们没有必要放开这些限制。

5. 2. JDBC 和Java应用程序

    对于一个普通的Java应用程序（例如全部用Java代码而不是不可靠的applet ）JDBC将从本地的类路径里面获得驱动，并且允许应用程序自由存取文件，远程服务器等等。

    但是和applet一样，如果由于某些原因一个没有验证的sun.sql.Driver类从远程的来源里面获得，那么这个驱动只能和相同地方来的代码配合。

5. 3. Driver的安全责任

    JDBC driver可能在各种情况下使用，所以驱动的编制者遵循一定的简单的安全规则，从而避免applet做非法的数据库连接。

    如果所有的驱动都象applet一样从网上下载，那么这些原则将是不必要的，因为普通的安全规则已经对它做了限制。但是驱动的编写者必须记住一旦他们的驱动获得成功，用户将在本地磁盘安装这些驱动，那么驱动将成为Java环境中一个被信任的部分，所以必须确信它不会被来访的applet所滥用。所以我们鼓励所有的驱动编写者必须遵循一定安全原则。

    所有这些原则都是在连接打开的时候使用。这正式驱动和虚拟机器检查当前调用者是否真的可以与指定的数据库连接的时刻。一旦连接建立就不必做更多的检查了。

5. 3. 1. 分享TCP/IP连接的时候必须谨慎 　　如果一个JDBC驱动试图打开一个 TCP 连接，那么这个打开会被Java 安全管理机制自动检查。这个机构会检查当前调用栈里面有没有applet，如果有那么就限定它可以访问的机器集合。所以一般地JDBC驱动可以把TCP建立检查留给Java虚拟机。

    但是如果一个JDBC驱动试图在多个数据库连接之间共享一个TCP连接，那么驱动就必须自己负责检查每个调用者是否真的被允许与目标数据库联系。例如如果我们为applet A打开了一个通往机器foobah 的TCP连接，这并不意味着applet B被自动允许来共享这个连接。applet B可能没有任何访问机器foobah的权力。所以在允许某个程序重用一个现成的TCP连接之前，JDBC 驱动必须通过安全机构来检查当前的的调用者是否可以访问这个连接。通过下面的代码可是实现这个功能。

SecurityManager security = System.getSecurityManager();

if (security != null)

{

security.checkConnect(hostName, portNumber);

}

    如果连接是不允许的，那么Security.checkConnect方法将产生一个java.lang.SecurityException。 5. 3. 2. 检查所有的本地文件访问

    如果一个JDBC取得需要访问本地机器上的数据，那么他必须确信调用者是被允许打开这个文件的。例如：

SecurityManager security = System.getSecurityManager();

if (security != null)

{

security.checkRead(fileName);

}

    如果对特定文件的访问是不允许的，那么Security.checkRead方法将产生一个java.lang.SecurityException。

5. 3. 3. 作好最坏的准备

    一些驱动可能使用本地的方法来桥接底层数据库程序。则这些情况里面判断那些本地文件将被底层函数所访问是困难的。

    在这些环境里面用户必须作好最坏的打算，并且否决所有下载applet所发出的数据库存取，除非驱动可能完全确信将要做存取是没有问题的。

    例如一个JDBC-ODBC桥接器必须检查ODBC数据源的的名称，确保applet只可以访问它的"生源地"。如果对有的名字中不能判断出数据源的主机名，那么只能否决这个访问。

    为了决定一个当前的调用者是可以信赖的应用还是一个applet，JDBC驱动必须能够检查这个调用者是否可以写一个随机的文件：

SecurityManager security = System.getSecurityManager();

if (security != null)

{

security.checkWrite("foobaz");

}


JDBC接口技术 转

作者：有所得

日期：2000-11-16 10:37:26

作者：郑林峰 张公忠

    JDBC是一种可用于执行SQL语句的JavaAPI（ApplicationProgrammingInterface应用程序设计接口）。它由一些Java语言编写的类和界面组成。JDBC为数据库应用开发人员、数据库前台工具开发人员提供了一种标准的应用程序设计接口，使开发人员可以用纯Java语言编写完整的数据库应用程序。

一、ODBC到JDBC的发展历程

说到JDBC，很容易让人联想到另一个十分熟悉的字眼“ODBC”。它们之间有没有联系呢？如果有，那么它们之间又是怎样的关系呢？

ODBC是OpenDatabaseConnectivity的英文简写。它是一种用来在相关或不相关的数据库管理系统（DBMS）中存取数据的，用C语言实现的，标准应用程序数据接口。通过ODBCAPI，应用程序可以存取保存在多种不同数据库管理系统（DBMS）中的数据，而不论每个DBMS使用了何种数据存储格式和编程接口。

1．ODBC的结构模型

ODBC的结构包括四个主要部分：应用程序接口、驱动器管理器、数据库驱动器和数据源。

应用程序接口：屏蔽不同的ODBC数据库驱动器之间函数调用的差别，为用户提供统一的SQL编程接口。

驱动器管理器：为应用程序装载数据库驱动器。

数据库驱动器：实现ODBC的函数调用，提供对特定数据源的SQL请求。如果需要，数据库驱动器将修改应用程序的请求，使得请求符合相关的DBMS所支持的文法。

数据源：由用户想要存取的数据以及与它相关的操作系统、DBMS和用于访问DBMS的网络平台组成。

虽然ODBC驱动器管理器的主要目的是加载数据库驱动器，以便ODBC函数调用，但是数据库驱动器本身也执行ODBC函数调用，并与数据库相互配合。因此当应用系统发出调用与数据源进行连接时，数据库驱动器能管理通信协议。当建立起与数据源的连接时，数据库驱动器便能处理应用系统向DBMS发出的请求，对分析或发自数据源的设计进行必要的翻译，并将结果返回给应用系统。

2．JDBC的诞生

自从Java语言于1995年5月正式公布以来，Java风靡全球。出现大量的用java语言编写的程序，其中也包括数据库应用程序。由于没有一个Java语言的API，编程人员不得不在Java程序中加入C语言的ODBC函数调用。这就使很多Java的优秀特性无法充分发挥，比如平台无关性、面向对象特性等。随着越来越多的编程人员对Java语言的日益喜爱，越来越多的公司在Java程序开发上投入的精力日益增加，对java语言接口的访问数据库的API的要求越来越强烈。也由于ODBC的有其不足之处，比如它并不容易使用，没有面向对象的特性等等，SUN公司决定开发一Java语言为接口的数据库应用程序开发接口。在JDK1．x版本中，JDBC只是一个可选部件，到了JDK1．1公布时，SQL类包（也就是JDBCAPI）就成为Java语言的标准部件。

二、JDBC技术概述

JDBC是一种可用于执行SQL语句的JavaAPI（ApplicationProgrammingInterface，应用程序设计接口）。它由一些Java语言写的类、界面组成。JDBC给数据库应用开发人员、数据库前台工具开发人员提供了一种标准的应用程序设计接口，使开发人员可以用纯Java语言编写完整的数据库应用程序。

通过使用JDBC，开发人员可以很方便地将SQL语句传送给几乎任何一种数据库。也就是说，开发人员可以不必写一个程序访问Sybase，写另一个程序访问Oracle，再写一个程序访问Microsoft的SQLServer。用JDBC写的程序能够自动地将SQL语句传送给相应的数据库管理系统（DBMS）。不但如此，使用Java编写的应用程序可以在任何支持Java的平台上运行，不必在不同的平台上编写不同的应用。Java和JDBC的结合可以让开发人员在开发数据库应用时真正实现“WriteOnce，RunEverywhere！”

Java具有健壮、安全、易用等特性，而且支持自动网上下载，本质上是一种很好的数据库应用的编程语言。它所需要的是Java应用如何同各种各样的数据库连接，JDBC正是实现这种连接的关键。

JDBC扩展了Java的能力，如使用Java和JDBCAPI就可以公布一个Web页，页中带有能访问远端数据库的Applet。或者企业可以通过JDBC让全部的职工（他们可以使用不同的操作系统，如Windwos，Machintosh和UNIX）在Intranet上连接到几个全球数据库上，而这几个全球数据库可以是不相同的。随着越来越多的程序开发人员使用Java语言，对Java访问数据库易操作性的需求越来越强烈。

MIS管理人员喜欢Java和JDBC，因为这样可以更容易经济地公布信息。各种已经安装在数据库中的事务处理都将继续正常运行，甚至这些事务处理是存储在不同的数据库管理系统中；而对新的数据库应用来说，开发时间将缩短，安装和版本升级将大大简化。程序员可以编写或改写一个程序，然后将它放在服务器上，而每个用户都可以访问服务器得到最新的版本。对于信息服务行业，Java和JDBC提供了一种很好的向外界用户更新信息的方法。

1．JDBC的任务

简单地说，JDBC能完成下列三件事：

1）同一个数据库建立连接；

2）向数据库发送SQL语句；

3）处理数据库返回的结果。

2．JDBC—一种底层的API

JDBC是一种底层API，这意味着它将直接调用SQL命令。JDBC完全胜任这个任务，而且比其他数据库互联更加容易实现。同时它也是构造高层API和数据库开发工具的基础。高层API和数据库开发工具应该是用户界面更加友好，使用更加方便，更易于理解的。但所有这样的API将最终被翻译为象JDBC这样的底层API。目前两种基于JDBC的高层API正处在开发阶段。

1）SQL语言嵌入Java的预处理器。虽然DBMS已经实现了SQL查询，但JDBC要求SQL语句被当作字符串参数传送给Java程序。而嵌入式SQL预处理器允许程序员将SQL语句混用：Java变量可以在SQL语句中使用，来接收或提供数值。然后SQL的预处理器将把这种Java／SQL混用的程序翻译成带有JDBCAPI的Java程序。

2）实现从关系数据库到Java类的直接映射。Javasoft和其他公司已经宣布要实现这一技术。在这种“对象／关系”映射中，表的每一行都将变成这类的一个实例，每一列的值对应实例的一个属性。程序员可以直接操作Java的对象；而存取所需要的SQL调用将在内部直接产生。还可以实现更加复杂的映射，比如多张表的行在一个Java的类中实现。

随着大家对JDBC兴趣的不断浓厚，越来越多的开发人员已经开始利用JDBC为基础的工具进行开发。这使开发工作变得容易。同时，程序员也正在开发对最终用户来说访问数据库更加容易的应用程序。

3．JDBC和ODBC及其他API的比较

到目前为止，微软的ODBC可能是用得最广泛的访问关系数据库的API。它提供了连接几乎任何一种平台、任何一种数据库的能力。那么，为什么不直接从Java中直接使用ODBC呢？

回答是可以从Java中使用ODBC，但最好在JDBC的协助下，用JDBC－ODBC桥接器实现。那么，为什么需要JDBC呢？要回答这个问题，有这么几个方面：

1）ODBC并不适合在Java中直接使用。ODBC是一个C语言实现的API，从Java程序调用本地的C程序会带来一系列类似安全性、完整性、健壮性的缺点。

2）其次，完全精确地实现从C代码ODBC到JavaAPI写的ODBC的翻译也并不令人满意。比如，Java没有指针，而ODBC中大量地使用了指针，包括极易出错的空指针“void＊”。因此，对Java程序员来说，把JDBC设想成将ODBC转换成面向对象的API是很自然的。

3）ODBC并不容易学习，它将简单特性和复杂特性混杂在一起，甚至对非常简单的查询都有复杂的选项。而JDBC刚好相反，它保持了简单事物的简单性，但又允许复杂的特性。

4）JDBC这样的JavaAPI对于纯Java方案来说是必须的。当使用ODBC时，人们必须在每一台客户机上安装ODBC驱动器和驱动管理器。如果JDBC驱动器是完全用Java语言实现的话，那么JDBC的代码就可以自动的下载和安装，并保证其安全性，而且，这将适应任何Java平台，从网络计算机NC到大型主机Mainframe。

总而言之，JDBCAPI是能体现SQL最基本抽象概念的、最直接的Java接口。它建构在ODBC的基础上，因此，熟悉ODBC的程序员将发现学习JDBC非常容易。JDBC保持了ODBC的基本设计特征。实际上，这两种接口都是基于X／OPENSQL的调用级接口（CLI）。它们的最大的不同是JDBC是基于Java的风格和优点，并强化了Java的风格和优点。

最近，微软又推出了除了ODBC以外的新的API，如RDO，ADO和OLEDB。这些API事实上在很多方面上同JDBC一样朝着相同的方向努力，也就是努力成为一个面向对象的，基于ODBC的类接口。然而，这些接口目前并不能代替ODBC，尤其在ODBC驱动器已经在市场完全形成的时候，更重要的是它们只是ODBC的“漂亮的包装”。

4．JDBC两层模型和三层模型

JDBC支持两层模型，也支持三层模型访问数据库。

如图3所示，两层模型中，一个java Appple或者一个JA－va应用直接同数据库连接。这就需要能直接被访问的数据库进行连接的JDBC驱动器。用户的SQL语句被传送给数据库，而这些语句执行的结果将被传回给用户。数据库可以在同一机器上，也可以另一机器上通过网络进行连接。这被称为“Client/Server”结构，用户的计算机作为Client,运行数据库的计算机作为Server。这个网络可是intranet，比如连接全体雇员的企业内部网，当然也可以是internet。

如图4所示，在三层模型中，命令将被发送到服务的“中间层”，而“中间层”将SQL语句发送到数据库。数据库处理SQL语句并将结果返回“中间层”，然后“中间层”将它们 返回用户。MIS管理员将发现三层模型很有吸引力，因为“中间层”可以进行对访问的控制并协同数据库的更新，另一个优势就是如果有一个“中间层”用户就可以使用一个易用的高层的API，这个API可以由“中间层”进行转换，转换成底层的调用。而且，在许多情况下，三层模型可以提供更好的性能。

到目前为止，“中间层”通常还是用C或C++实现，以保证其高性能。但随着优化编译器的引入，将java的字节码转换成高效的机器码，用java来实现“中间层”将越来越实际。而JDBC是允许从一个java“中间层”访问数据库的关键。


JDK1.2关于JDBC中文问题的解决方案

作者：whitefox

email: whitefox.jiang@corp.elong.com

日期：2000-07-18 14:40:14

发信人: hht (Duke), 信区: Java

标  题: JDK1.2关于JDBC中文问题的解决方案&Re yzhang

发信站: 网易虚拟社区 (Mon Jan 11 13:29:43 1999), 转信

忙了几天,终于对JDK1.2的JDBC中文问题有了初步的了解,虽然还有问题存在,但我

想大家可能都着急了吧?:)所以就把我的初步想法先贴出来,欢迎大家补充.

JDK1.2的中文问题主要是由于引入了Unicode引起的(其实在1.1版本中就有了),U

nicode的字符由16bit构成,关于Unicode更详细的信息有兴趣者可以到http://www.unico

de.org查阅,在1.0版本中,一个中文是由两个char(8bit)组成的,而1.1以上版本中

是由一个char(16bit)组成的.这一点大家可以用System.out.println(s.length(

))语句来证实,其中s为中文字串.

现在来说说解决办法,首先声明这些结果只是在我的环境里测试通过,如果在各位

的环境里有问题,请务必告知.

我的环境是JDK1.2+Win95+Sybase JDBC Driver(PowerJ2.5附带)+Oracle JDBC D

river(Oracle8.0.3附带).

我的第一个体会是用JDBC-ODBC桥接访问数据库中文输入有问题,我还没解决,我用

的JDBCODBC Driver是JDK1.2自带的.

第二个体会是用JDBC直接联数据库,这时可能有两种情况:

1,中文输入没问题(Oracle8),即可以在SQL语句中直接写中文,如:

statement.executeUpdate("inser into tablename values("测试",....)

2,中文输入有问题(Sybase11),看错误可知是Unicode转换引起的,解决办法是将U

nicode转换为ASCII形式,即一个汉字拆成两个char,用到两个类ByteToCharConve

rter和CharToByteConverter,在sun.io.*中,可以在JDK home\jre\lib\i18n.jar

中找到,没有文档,我也实在网上找到用法的.

在输入时,用ChineseStringToAscii转换为ASCII码字串,查询时用AsciiToChines

eString转换为Unicode字串.源代码如下:

public static String AsciiToChineseString(String s) {

    char[] orig = s.toCharArray();

    byte[] dest = new byte[orig.length];

    for (int i=0;i<orig.length;i++)

      dest[i] = (byte)(orig[i]&0xFF);

    try {

      ByteToCharConverter toChar = ByteToCharConverter.getConverter("g

b2312");

      return new String(toChar.convertAll(dest));

    }

    catch (Exception e) {

      System.out.println(e);

      return s;

    }

  }

  public static String ChineseStringToAscii(String s) {

    try {

      CharToByteConverter toByte = CharToByteConverter.getConverter("g

b2312");

      byte[] orig = toByte.convertAll(s.toCharArray());

      char[] dest = new char[orig.length];

      for (int i=0;i<orig.length;i++)

        dest[i] = (char)(orig[i] & 0xFF);

      return new String(dest);

    }

    catch (Exception e) {

      System.out.println(e);

      return s;

    }

  }

主程序片断如下:

try{

Statement stmt;

Class.forName("com.sybase.jdbc.SybDriver");

Connection conn =DriverManager.getConnection ("jdbc:sybase:Tds:202.9

7.228.249:5000/todo","sa","");

stmt = conn.createStatement ();

String s="何海涛";

s=ChineseStringToAscii(s);

     stmt.executeUpdate ("insert into russia values('"+s+"',1,1,'"+s+

"')");

}catch(Exception e){

System.out.println(e);

}

在读数据时用String s=AsciiToChineseString(rs.getString(1))

--------------------

因为手头只能对这两种数据库进行测试,所以对别的数据库可能出现的情况不能保

证.不过我估计基本是上面这两种情况.另外,用JDK1.2有的公司提供的Driver也升

级了,比如Sybase原来的Driver就不好使.原理就是这样,在实际操作中要靠自己摸

索了.上面两个方法参考了台湾某BBS的一篇文章.

另外,yzhang不知你要的是什么版本,你可以在jconnect软件安装的目录下找JDBC

 Driver包,可能的格式是zip或jar,把他拷出来就行了.我的就是从PowerJ中拷出

来的


Jive 中的设计模式 (续)

作者：jeru

email: jeru@163.net

日期：7/13/2001 5:48:41 PM

二, 结构型模式 (Structural Patterns)

这一类的模式关心类和对象之间怎么组织起来形成大的结构. 主要使用继承来组织接口或实现.

我们再接着思考一下, 用户之间应该有所区别, 有 Guest 用户, 可以让他来看一看, 但不能发贴子, 正式用户可以发贴子, 查看自己的个人信息, 版面管理者(称之为版主)应该可以控制贴子, 比如加上适当的标记, 收入精华区, 甚至删除贴子等等, 而系统管理者应该具有更高的权限, 比如开新的版面, 删除用户等操作. 怎么实现这个功能呢? 我们知道, Jive 中所有实际的操作都是由 database 目录下的类所实现的,如果把权限控制加到数据库这一层的话, 这一层不但臃肿, 而且写好以后, 如果要改的话, 需要修改的地方很多, 还容易出错, 所以可以在这一层之上再加一层, 单独进行权限控制. 这样就把 "该不该做" 和 "怎么做" 分割开来, 利于以后修改. 其实这也是面象对象的一个思想 -- 一个对象不要负担太多的责任. 这种方法在设计模式中称为 Proxy (代理) 模式. 好比生产厂家和代理商的关系. (当然, 在 Jive 中这个比喻不太合适). Proxy 的目的就是给另一个对象提供一个代理来控制对它的访问.

Proxy 模式一直贯穿 Jive 的始终, 几乎所涉及到的对象都需要. 其结构如图 2所示.

从前面已经知道, ForumFactory 是整个系统的开始. 再来看看 ForumFactory 的代码: From ForumFactory.java

ForumFactory.getInstance() 的最后:

        ForumFactoryProxy proxy = new ForumFactoryProxy(

                                    factory,

                                    authorization,

                                    factory.getPermissions(authorization)

                                  );

        return proxy;

前面得到的 factory 是 DbForumFactory 的实例, 这里把这个实例又用ForumFactoryProxy 封装起来. 最后返回一个 ForumFactoryProxy 的实例. 也就是说 jsp skin 的设计者所用的 ForumFactory 实际上是 ForumFactoryProxy. 接着看看 ForumFactoryProxy 里发生了什么事, 那一个小片段做例子:

其构造函数中的 Factory 就是一个 DbForumFactory 的实例, 由它来做具体的工作. Authorization 可以认为是一个认证过的当前用户(指实际的浏览器的使用者),ForumPermissions 可以认为是当前用户的权限.

    public Forum createForum(String name, String description)

            throws UnauthorizedException

    {

        //这里就对权限进行了检查, 具有系统管理员权限, 则可以进行相应的操作,

        //否则抛出异常.

        if (permissions.get(ForumPermissions.SYSTEM_ADMIN)) {

            Forum newForum = factory.createForum(name, description);

            return new ForumProxy(newForum, authorization, permissions);

        }

        else {

            throw new UnauthorizedException();

        }

    }

    public Forum getForum(int ID) throws ForumNotFoundException,

            UnauthorizedException

    {

        Forum forum = factory.getForum(ID);

        ForumPermissions forumPermissions = forum.getPermissions(authorization);

        //Create a new permissions object with the combination of the

        //permissions of this object and tempPermissions.

        ForumPermissions newPermissions =

                new ForumPermissions(permissions, forumPermissions);

        //Check and see if the user has READ permissions. If not, throw an

        //an UnauthorizedException.

        if (!(

            newPermissions.get(ForumPermissions.READ) ||

            newPermissions.get(ForumPermissions.FORUM_ADMIN) ||

            newPermissions.get(ForumPermissions.SYSTEM_ADMIN)

            ))

        {

            throw new UnauthorizedException();

        }

        // 同上所述.

        // 这里得到的 forum, 是一个 DbForum 的实例, 跟 ForumFactory 一样,

        // 返回一个封装过的代理对象, 来对 forum 进行权限控制.

        return new ForumProxy(forum, authorization, newPermissions);

    }

其他所有的对象都是类似的. 这里就不再赘述.

三, 行为型模式 (Behavioral Patterns)

这一类的模式关心的是算法以及对象之间的任务分配. 它所描述的不仅仅是对象或类的设计模式, 还有它们之间的通讯模式.

1, 下来看看怎么从一个 Forum 中得到一些 Thread. 当然这里要涉及到数据库, 我们先设计一个最简单的数据库表, 表名: thread, 字段 ThreadID int, ForumID int, 其他内容我们不关心. 然后比如 Forum 中的一个方法, getThreads() 来返回当前 Forum 所有的 Thread. 然后就可以这样做:            public ForumThread[] getThreads()

           {

                1, 从数据库里面查询, 取出所有的 ThreadID,

                2, 根据 ThreadID 构造 ForumThread 对象,

                3, 返回一个数组.

           }

这样做最省事, 最简单了, 但好不好呢? 还得看需求, 比如我要求根据时间排序,就还得修改这个方法, 也就是说需要修改 DbForum 对象. 那为什么不把取 Thread 这个操作单独拿出来呢? 这样的好处就是功能独立化, 使 DbForum 更简单, 符合前面我们所提到的不要让对象负担太多的责任这个原则. 也许你会说, 如果要修改的话, 不是都得修改吗? 放哪里是一样的, 这样没错, 但只限于很小的系统, 如果系统一大, 那么就可能做 DbForum 中的简单查询和一些比较复杂的查询的程序员就不是一个人, 这样牵扯到需要改动的地方较多, 但分离以后, 只需要一个人改很少的地方就可以完成. 回过头来再看看问题, 这里要返回一群 ForumThread 对象, 而且它们之间还可能有一定的先后关系, 怎么来做这个工作呢? Iterator 设计模式是一个合适的选择. Iterator 模式提供了一个连续访问一大群对象的方法, 而不需要知道它们的表现形式, 比如按什么方式排序等等.

好了, 来看看 Jive 的具体实现. 由于 Java 本身已经有这样的接口, Iterator 接口, 所以只要实现这个接口就可以了.

From DbForum:

    public Iterator threads() {

        return new DbForumIterator(this, factory);

    }

From DbForumIterator:  (做了改动)

public class DbForumIterator implements Iterator {

     public DbForumIterator(...)

     {

        ...

     }

     public boolean hasNext()       //是否还有元素

     {

       ...

     }

     public Object next()      // 得到下一个元素

     {

       ...

     }

     ...

}

那么 jsp 中可以这样访问:     Iterator threads = aForum.threads();

    while (threads.hasNext())

    {

        ForumThread thread = (ForumThread)threads.next();

        做一些操作.

    }

从中可以看出, 通过使用 Iterator 把 Threads 的一些具体细节进行了封装, 提供统一的接口. Jive 中这个设计模式也是用的非常多, 多个用户显示, 多个版面显示, 多个线索, 多个贴子都需要由它来实现.

小结:

上面我们一起探讨了一下设计模式在 Jive 中的应用情况, 当然只是很简单, 很肤浅, 也很片面, 不过总算能对设计模式有些认识. 实际上, 设计模式就是吸收许多前人的经验, 把设计中一些重要的和重复出现的一些模式总结起来, 给出一个系统的命名,给出相应的解释和评价, 这个工作最先由 4 位软件大师所做, 他们合写了一本书 --Design Pattern: Elements of Reusable Object-Oriented Software, 后来, 人们把他们称为 GoF (Gang Of Four).

对于设计模式, 可能在我们的实际项目中自觉不自觉地在使用着, 比如 Factory Method 模式, Abstract 模式, Singleton 模式, Iterator 模式, 等等, 只是概念不是非的明确, 设计可能还有不太合理的地方, 处于一种跟着感觉走的状态, 相信很多有经验的设计者, 原来没有接触设计模式, 一旦接触以后, 会有一种恍然大悟的想法, 哈, 原来是这么回事. 学习设计模式, 能很好地帮助我们设计, 在相同的问题, 相同的背景下,可以直接使用它, 有的时候不知道该选择哪种好, 就需对问题进行更深一层的分析, 进行综合权衡, 对设计模式也要进行更深刻的理解, 才能得到好的结果, 这也是一个进步的过程.

对于笔者来说, 刚刚接触设计模式, 有了一点粗浅的理解, 就冒昧写了这篇算是一点心得的东西, 也是对自己的挑战, 中间犯的一些错误, 还请指正, 谢谢.

参考文献:

Design Pattern: Elements of Reusable Object-Oriented Software,

Jive 源代码


JMS资料（译文）(-)

作者：netkey

email: sztj1@263.net

日期：8/2/2001 10:42:44 AM

JMS资料（译文）

作者：HaoGe

发表时间：2001-7-30

第一章

概述

这是关于Java消息服务应用程序编程接口的概述，它包括以下主题：

什么是消息？

什么是JMS API?

怎么使用JMS API?

JMS API是怎样在在Java2企业平台下运作的

1.1 什么是消息？

消息系统是一种在软件组件或者应用程序之间通信的一种方法。一个消息

系统是一种点对点的程序，一个消息系统客户端能发送消息给其他一些客

户端或者从其它一些客户端接受消息。每一个连到消息代理的客户连接提

供产生，发送接受消息的通道。

消息机制使分散连接的分布式通信成为可能，一个成员发出消息给目的

地，接受者在目的地接收消息，然而发送者和接收者不需要同时有效，实

际上，发送者不需要知道任何关于接收者的信息，接收者也不需要了解任

何关于发送者的信息，他们只需要知道消息的格式和哪个目的地。

从这方面考虑，消息系统不同于紧连接技术如RMI,RMI需要一个应用程序知

道远端程序的方法。

消息系统也不同与email,email是人们通信的一种方式或者人和计算机软件

应用程序通信的方式，消息系统则是用于软件应用程序或者软件组件之间

的通信。

1.2  什么是 JMS API?

Java消息服务是一种Java API它允许应用程序去建立，发送接受和读取消

息。 由Sun公司和一些合作伙伴定义设计，JMS API定义了一个公共接口和

关联语意集合，它可以让有Java语言编写的程序和其他的消息系统进行通

信。

JMS API最小化了一个程序员编写使用消息的产品需要了解的概念集。但是

提供了足够的功能去支持复杂的消息应用。

It also strives to maximize the portability of JMS applications

across JMS providers in the same messaging domain.

JMS API使得通信不仅仅只是可以在松耦合的情况下。而且还具有：

异步性： 一个JMS提供者可以在客户端抵达的时候传递消息给它们；一个

客户端不必为了接收消息去请求消息。

可靠性：JMS API可以保证一个消息只被传递一次，对可靠性要求比较低的

系统可以承受丢失消息或接受到重复的消息的后果。

JMS规范在1998年的八月首次发布，最新版本是1。0。2，在1999年的11月

发布

1.3  怎样使用JMS API?

一个企业应用的提供者在消息系统和紧耦合系统（如 RPC）中更乐意选择

前者，当：

提供者希望组件不要依赖别的组件的接口的信息，以便组件可以很方便的

替代。

提供者希望应用程序在组件没有全部执行或没有同时执行的时候运行.

应用程序商业模型允许一个组件向另一个组件发送信息并且能够在没有得

到实时响应的情况下继续运行。

例如，一个汽车制造业的企业应用程序组件能够在如下的情况下使用JMS

API:

库存组件可以在某个产品的库存水平小于某个水平的时候向工厂组件发送

消息，工厂就开始制造更多的汽车。

工厂组件可以发送一个消息给部件组件，以便工厂可以用它需要的部件组

装汽车。

部件组件发送消息给它们自己的库存和订购组件以更新他们的库存和向供

应商订购新的部件

工厂和不见组件都可以向财会组件发送消息去更新它们的预算。

企业发布更新了的产品目录给他们的市场部门和网站。

使用消息服务可以使不同的组件有效地进行交互而不需要尝试网络或其它

资源，

制造业只是企业怎样使用JMS API的一个例子，其它象零售业，金融业，健

康服务应用都可以很好的利用消息系统。

1.4 JMS API是如何和J2EE平台一起工作的?

JMS API 最早出现在1998年，使用它的主要目的是允许JAVA 应用程序可以

和现存的面向消息的中间件系统（如IBM的MQ Series 和TIBCO的

TIB/Rendezvous 进行交互.

从那时起，许多提供商采用和实现了JMS API以便JMS产品可以提供企业级

的消息服务的能力

在J2EE的1.2 release版本中，一个基于J2EE技术的服务提供者提供了JMS

API的接口，但是没有要求全部实现它们。现在，在J2EE的1.3的release

中，JMS API是J2EE平台的一个完整的部分。应用程序开发者可以使用

J2EE APIs利用组件使用消息服务。

在J2EE 1.3平台中JMS API有以下功能:

任何J2EE组件（ejb 或wen组件）能够发送及同时接收一个JMS消息

一种新的EJB,消息驱动的Bean,使得异步的消息通信成为可能。 另外JMS提

供者可以实现消息驱动的BEANS去并发地处理消息

消息发送和接收可以参与Java Transaction API事务

JMS API增强了J2EE平台，使地企业应用的开发大大的简化了，允许松耦

合，可靠，可以和 J2EE组件与老系统进行异步交互。开发者可以很方便的

给基于J2EE技术的商业应用添加新的操作，只需要添加一个新的消息驱动

的针对特定商业事件的Bean就可以了。

另外一个J2EE 1。3平台的新技术： J2EE  connectoe体系，使J2EE 应用

和现寸的企业信息系统的可以紧密集成。 JMS API则允许在J2EE应用和现

有的EIS 系统间进行非常松散连接的交互


Jrun 快速定位编译时期错误位置

作者：蜜蜂

email: chuweihua@netease.com

日期：2001-7-4 13:37:44

1    Jsp错误查找方法

1.1    显示的错误行数为<= 1047  & > 1032

1.1.1    确定在全局变量声明之前的行数（包括空格）

例如：main.jsp

<%@ page contentType="text/html;charset=gb2312"%>

<%@ page import="lulusoft.common.*"%>

<%@ page import="lulusoft.database.*"%>

<%@ page import="lulusoft.ef2.BCDeliverProcess.*" %>

<%@ page import="lulusoft.ef2.instock.CBCInStock" %>

<%@ page import="lulusoft.web.common.*"%>

<%@ include file="/web/web/CWebTool.jsp"%>

<%!

此处的行数为9

1.1.2    计算包含文件的行数

CwebTool.jsp的行数为1032行

1.1.3    计算表达式

x = 当前显示的错误行数 + 9 – 1032

X为main.jsp中的实际错误行数

该表达式的有效范围为 当前显示的错误行数 <= 1047  & > 1032

1.2    显示的错误行数为 >9 & < 260-(1047-1032)

1.2.1    计算表达式

X = 当前显示的错误行数 + (1047 - 1032)

X为main.jsp中的实际错误行数

例如变量声明部分最后一行的行数为260则x为>9 & x<15

1.3    显示的错误行数为  > 主程序体的起始行数

则显示的错误行数与实际错误行数相同

如果提示的错误为编译后的java产生则在web-inf/jsp目录中找到相关java文件，找到提示的错误信息行即可。


JRun常见问题回答 (JRun 中文 FAQ)

作者：jeru

email: jeru@163.net

日期：2000-12-31 12:15:46

JRun常见问题回答

什么是并发（Concurrency）？

并发指JRun在同一时间运行多个servlet的能力。JRun通过运用Java内建的多线程能力支持并发。多任务或多处理（Multi-tasking or Multi-processing）是并发的其他说法。该功能赋予JRun可以同时负载多个用户的并发请求。

我需要什么程度的并发处理功能？

这取决于你准备支持多少个Web用户，JRun支持多大的流量以及Web服务器的速度。JRun带有一个监控功能帮助你估算出负载的量。

JRun的免费版可以支持什么程度的并发？

最多同时5个请求。

JRun Pro支持什么程度的并发？

无限。

JRun Pro Unlimited支持什么程度的并发？

无限。

当并发请求超过我所使用的JRun负载量的时候会发生什么问题？

JRun将对这些请求进行排序并按顺序分别处理。当用户的请求处于排队状态时，他们会感觉轻微的延迟。

过去JRun版本支持多少个并发连接？

免费版和Pro版都支持无限多个连接。

如果我的并发处理数是有限的，为什么我应该升级？

JRun2.3.3不但比以前版本更快，而且也更强壮，它还包括了原来只在JRun Pro中提供的，对JavaServer Pages(JSP)的全面支持以及其他功能。

标准的JRun 2.3.3卖多少钱？

JRun 标准版是免费的。

JRun Pro2.3和JRun Pro Unlimited 2.3卖多少钱？

价格信息请浏览我们的在线报价页面。

JRun Pro2.3和JRun Pro Unlimited 2.3之间有哪些区别？

这两个版本的产品都可以运行无限多个并发的连接。但是，JRun Pro2.3和 JRun Pro Unlimited之间还有2个主要的区别。

版权结构：JRun Pro的版权以每中央处理器计算（例如，如果你在一台装有3个中央处理器的机器上安装JRun Pro，你需要买3个JRun Pro）。而JRun Pro Unlimited是以每台机器计算的，如果你有一台带4个中央处理器的机器，显然买JRun Pro Unlimited就更划算。

并发Java 虚拟机的数量：JRun Pro可以运行最多5个Java虚拟机而JRun Pro Unlimited并不限制Java虚拟机的数量。

若想了解更多JRun个版本的区别，请访问版本比较列表页面。

对于已经购买了JRun Pro 2.2.1/JST 1.0.1捆绑产品的用户，升级到JRun Pro2.3要花多少钱？

免费。

对于已经购买了JRun Pro 2.2.1或更早版本的用户，升级到JRun Pro2.3要花多少钱？

有关所有价格信息，请访问我们的在线价格页面查询。

现在是否还卖JRun Scripting Toolkit(JST)?

JST已经不再是一个单独的产品了，我们已经将它包含到JRun 和 JRun Pro中去了。

已经有了JRun Scripting Toolkit1.0.1(或更早版本)的用户来说，升级到JRun Pro2.3的价格是多少？

免费。

我已被授权可获得一个免费升级到JRun Pro2.3的名额，我怎么拿到属于我的产品呢？

正式用户如需要升级，请访问我们的下载产品页面，如果你过去未注册过，请先注册。你会发现在允许下载的产品中有JRun Pro 2.3.3供您直接下载。

我是否可以测试JRun Pro?

是的。可接受无限量并发连接的的JRun Pro 30日试用版可以直接从我们的站点下载。这样你就可以估计出你一般可能需要多少数量的并发连接。现在就下载吧。

我是一家电子杂志并且需要使用JRun 2.3.3，我是否必须购买JRun Pro?

是的。在授权使用声明中已经指出，电子杂志在实施应用或站点、包括内联网的时候，必须购买JRun Pro。免费的JRun只能供你在开发和测试阶段进行使用。

JRun2.3.3中是否包括了JavaServer Pages?

是的。在JRun2.3.3和JRun Pro2.3中包括了一些服务器端 Java scripting 技术，其中包括JavaServer Pages，赠送的模板以及taglets(普通JRun Scripting Toolkit)

更多相关文章请访问  爪哇人--》http://www.cyberlabs.com/~jeru/


JSP - FAQ (1)

作者：jspfuns

日期：2000-8-10 17:48:55

Java Server Pages Frequently Asked Questions

Maintainer: Richard Vowles, rvowles@esperanto.org.nz (http://www.esperanto.org.nz

Questions

What is JSP?

What version is the current version of JSP?

Where can I get the specification for JSP?

Who supports JSP?

What books are available for JSP?

Is JSP better than ASP (Microsoft's Active Server Pages)?

What HTML editors support JSP?

Yes, but how does it perform?

How to invoke a JSP page directly from by browser?

How do you invoke a JSP page from a servlet?

How do you pass data (including beans) to a JSP from a servlet?

How can I pool connections to my database?

How do I use other languages in my JSP?

How can I set a cookie in JSP?

Can JSP and Servlet share same Session and Beans?

How do I plug JSP into Microsoft's IIS Web Server?

Are there any newsgroups that discuss JSP?

What do the differing levels of bean storage (page, session, app) mean?

Where can I find the mailing list archives?

What are the important steps in using JDBC in JSP?

How does variable scope work in JSP?

How do I forward to an HTML page?

Are there any white papers or documents explaining how JSP fits?

How to I create dynamic GIFs for my JSP?

Do you know where I could get some code that would encode something to the HTML DTD standard?

What is page compilation?

How are servlets and JSP pages related?

Any good web sites for up to date activities in the Java/JSP/Servlet world?

How do I force a user to log in?

So how can a newbie get started with JSP?

How can I ensure that session objects stay in existence when the web server restarts?

How can I include one JSP inside another JSP?

Is there some sort of event that happens when a session object gets bound or unbound to the session?

Is there a way to execute a JSP from the comandline or from my own application?

What should I use, Model 1 or Model 2?

How do I delete a cookie with JSP?

Are there tips on things to watch out for in JSP?

How do you get started with JSP on the Macintosh?

What Application Servers support JSP?

What happened to the LOOP and DISPLAY tags from JSP 0.92?

Does JSP have a bean providing File Upload features like ActiveX upload control?

So how do I use EJB from JSP?

Can I just abort processing a JSP?

"I'm trying to pass an object between a servlet and a JSP page, but my object is null on the JSP page. What could be wrong?"

1) What is JSP? TOC

JSP is a dynamic scripting capability for web pages that allows Java as well as a few special tags to be embedded into a web file (HTML/XML, etc). The suffix traditionally ends with .jsp to indicate to the web server that the file is a JSP file. JSP is a server side technology - you can't do any client side validation with it.

JSP files actually get compiled into Servlets, so what is the point? Why not just write Servlets?

For most people, the benefit is twofold:

The focus is on HTML. Java and the JSP extensions assist in making the HTML more

functional. Servlets on the other hand allow outputting of HTML but it is a tedious

process.

It is easy to make a change and then let the JSP capability of the Web Server you are

using deal with compiling it into a Servlet and running it.

In the past, people have used servlets because of the problem of redirection - because by the time the JSP engine gets around to writing information back down the HTTP pipe, you could not do a redirect effectively in JSP - leading to the concepts of Model 1 vs Model 2 (see question 38). This has been fixed in JSP 1.0 with the addition of buffered output streams.

JavaSoft also has a FAQ, covering different questions, and it is located at http://java.sun.com/products/jsp/faq.html.

2) What version is the current version of JSP? TOC

The current version of JSP is 1.1. It is available from http://java.sun.com/products/jsp.

The JSP 1.1 is available from http://java.sun.com/products/jsp/techinfo.html.

The 0.92 spec is available from http://java.sun.com/products/jsp/jsp092.html

The 0.91 spec is a popular version though, and much software that supports the JSP pre-standard actually supports 0.91 of JSP.

The 0.91 spec is available from http://www.burridge.net/jsp/Spec91/jsp_spec.html

3) Where can I get the specification for JSP? TOC

You can get the current spec from http://java.sun.com/products/jsp.This is along with the reference implementation which works with its own web server.

4) Who supports JSP? TOC

There are the following implementations available:

Name

Version of JSP Supported

Cost

Comments

JSP Reference 1.1 free TOMCAT - Now included as part of the J2EE Reference Implementation, but also managed by the Jakarta Project as part of Apache. This implementation has a way to go before being ready for production.

GNU JSP 1.0 free, open source, located in CVS at the Giant Java Tree

Sun's Java Web Server 2.0 1.0 commercial Sun's own Java Web Server. Being replaced with iPlanet (Alliance)

PolyJSP 0.92 free, open source PolyJsp is based on XML/XSL and has been designed to be extensible. Now supports WebL Hasn't been updated for a while.

Resin 1.0, 1.1 in beta Unknown Now Open Source.

SJSP 1.1 free for personal use Uses compile time introspection, allows you to replace your existing JSP engine in your web server.

zJSP 0.91 Last updated, Sept 3, 1998

JRUN 0.92, 1.0 commercial JSP 1.1 support now in beta.

Orion 1.1 commercial Orion is a web application server that implements JSP internally. It also has a lot of JSP vs other JSP and vs ASP stats on its pages. Also supports Servlet 2.2

ServletExec 1.1 commercial Supports large number of web servers

RocketJSP 1.0 GPL Located in the Giant Java Tree

5) What books are available for JSP? TOC

There are now a number of books available on JSP from various publishers.

A quick search of Amazon comes up with

Professional Java Server Programming: with Servlets, JavaServer Pages (JSP), XML, Enterprise JavaBeans (EJB), JNDI, CORBA, Jini and Javaspaces

Professional Java XML Programming with servlets and JSP

Pure JSP Java Server Pages

Web Development with JavaServer Pages

There is a group of people working on a Web book for JSP - information is located at http://www.esperanto.org.nz/jspbook.There is another at http://www.aptura.com/technology/jspBook_Architectures.html and yet another at Brainopolis.

6) Is JSP better than ASP (Microsoft's Active Server Pages)? TOC

This is a matter of great debate. Many punters think that JavaSoft has the benefit of hindsight - what works well and what does't. ASP is very complex but supports multiple scripting languages and the ActiveX model of Microsoft. Although ASP _is_ available on many platforms, the ActiveX library support that makes it as powerful as it is is missing and thus makes ActiveX only feasible for an NT platform.

According to recent statistics (see the RedHat site), 22% of web servers are NT, (21% are Linux), that means there is 78% needing something else, and JSP advocates believe that JSP fits the bill nicely that you very much.

7) What HTML editors support JSP? TOC

Most of the visual developers do not support JSP in any way, shape or form, they mess up the tags totally.

The following HTML editors have been known to work with JSP:

Product

Location

Comments

HomeSite http://www.allaire.com/JSP colour coding will be in HS 4.0.1, but you can get a plug in now from http://www.burridge.net/jsp/homesite.html

NetObjects ScriptBuilder 3.0 http://www.netobjects.com/products/html/nsb3.htmlWin32 product supports JSP

Any text editor

Borland JBuilder 3.5 http://www.borland.com/jbuilderJava Development tool, supports JSP internally, and allows running and debugging of JSPs

DreamWeaver 3 http://www.dreamweaver.com/Ignores JSP tags, and allows you to edit JSP files as if they are HTML files given you customise the product slightly (you need to edit a special file)

FrontPage 2000 http://www.microsoft.com/frontpageSome reports that Front Page now ignores JSP tags.

8) Yes, but how does it perform? TOC

I have been picking up some comments:

Brian Burridge, brian_burridge@VALPAK.COM

"Our extranets for 300 franchises, 500+ users, our Intranet for thousands of employees,

and eventually all of our other web sites. New Atlanta has been running for 6 weeks

now without even one problem. Programming in JSP has turned out to be as easy as Perl,

yet all our tests show it to be up to 10x faster."

Also available are a set of benchmarks from the people who make the Orion server, located at http://orion.evermind.net/,and also from the people who do Resin (comparing their JSP implementation with Orion)

9) How to invoke a JSP page directly from by browser? TOC

JSP files are just like HTML files, so if your index.jsp file is in your root directory of your web site, use http://localhost/index.jsp


JSP - FAQ (2)

作者：jspfuns

日期：2000-8-10 17:49:53

10) How do you invoke a JSP page from a servlet? TOC

(Contributed by: Thomas-Bernhard.O-Hare@Dresdner-Bank.com)

After scanning through archives of the JSP mailing list to no effect I finally remembered that I'd pasted this example into a document I wrote. It was originally sent by Satish Dharmaraj of Sun to show the model 2 approach (as described in the 0.92 specification): how to pass data from a servlet

to a JSP.

Create a directory called model1/ under the samples/ directory. Place foo.jsp and Foo.java inside this directory.

Compile FooServlet.java and place FooServlet.class in TOP/servlets/directory.

Then invoke using http://host:8080/servlet/FooServlet

In this example, FooServlet creates a list and then stores the result in Foo.class. Foo.Class is then passed as a datasource to foo.jsp.

The sources are:

1) FooServlet.java

import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

import model1.Foo;

public class FooServlet extends HttpServlet

{

public void service(HttpServletRequest req, HttpServletResponse res)

throws ServletException, IOException

{

String s[] = new String[] {"blue", "green", "red"};

Foo f = new Foo(s);

req.setAttribute("foo", f);

getServletContext().getRequestDispatcher("/samples/model1/foo.jsp").forward

(req, res);

}

}

2) foo.jsp

<html>

<usebean name=foo type=model1.Foo lifespan=page>

</usebean>

<ul>

<loop property=foo:list propertyelement=x>

<li> <display property=x>

</loop>

</ul>

</html>

3) Foo.java

package model1;

public class Foo {

String s[];

public String[] getList() { return s; }

public Foo(String s[]) { this.s = s; }

}

(from "O碒are, Thomas Bernhard" <Thomas-Bernhard.O-Hare@Dresdner-Bank.com>)

11) How do you pass data (including beans) to a JSP from a servlet? TOC

There are actually three different ways to do it, depending on how long the reference should last, and which JSP pages (and servlets, for that matter) should be able to see it. In each of the cases, assume that "myBean" is a reference to the bean you want to send, and that "theBean" is the key I'm going to use to store the bean under (from the servlet perspective), and use as the identity of the bean in the JSP page.

These techniques are portable to any environment compliant with the servlet API 2.1 and JSP 1.0 specifications. In each case, the passing

works from servlet->JSP, servlet->servlet, JSP->JSP, or JSP->servlet transitions.

(1) Request Lifetime

Use this technique to pass beans that are relevant to this particular request to a bean you are calling through a request dispatcher (using either "include" or "forward"). This bean will disappear after processing this request has been completed.

SERVLET:

request.setAttribute("theBean", myBean);

RequestDispatcher rd =

getServletContext().getRequestDispatcher('/thepage.jsp");

rd.forward(request, response);

JSP PAGE:

<jsp:useBean id="theBean" scope="request" class="....." />

(2) Session Lifetime

Use this technique to pass beans that are relevant to a particular session (such as in individual user login) over a number of requests. This bean will disappear when the session is invalidated or it times out, or when you remove it.

SERVLET:

HttpSession session = request.getSession(true);

session.putValue("theBean", myBean);

/* You can do a request dispatcher here,

or just let the bean be visible on the

next request */

JSP PAGE:

<jsp:useBean id="theBean" scope="session" class="..." />

(3) Application Lifetime

Use this technique to pass beans that are relevant to all servlets and JSP pages in a particular app, for all users. For example, I use this to make a JDBC connection pool object available to the various servlets and JSP pages in my apps. This bean will disappear when the servlet engine is shut down, or when you remove it.

SERVLET:

getServletContext().setAttribute("theBean", myBean);

JSP PAGE:

<jsp:useBean id="theBean" scope="application" class="..." />

Craig McClanahan

12) How can I pool connections to my database? TOC

Controlling connections to the database is a desirable thing - having to connect to the database for each page is is expensive, and keeping a connection in a session variable is far too expensive in terms of client connections to the database. Thus, people often create pools for connections to the database that the client comes in and gets and then returns when complete (making sure a try/catch is used to ensure the connection is returned!).

My personal bias indicates that you shouldn't pool connections to your database inside JSP, you should be using a middleware layer and communicating to it (like RMI or CORBA). However, people do write entire applications in JSP and beans that reside in the web server, so how do you do it?

From: Bradley Wood <Brad@MARKETING.CO.UK>

probably instantiate this as a singleton in the global.jsa and then call it.

for more on this and how its called check the downloadable code examples for this book at http://www.ora.com

import java.sql.*;

import java.util.*;

public class ConnectionPool {

private Hashtable connections;

private int increment;

private String dbURL, user, password;

public ConnectionPool(String dbURL,

String user,

String password,

String driverClassName,

int initialConnections,

int increment)

// int max?

throws SQLException, ClassNotFoundException {

// Load the specified driver class

Class.forName(driverClassName);

this.dbURL = dbURL;

this.user = user;

this.password = password;

this.increment = increment;

connections = new Hashtable();

// Put our pool of Connections in the Hashtable

// The FALSE value indicates they're unused

for(int i = 0; i < initialConnections; i++) {

connections.put(DriverManager.getConnection(dbURL, user, password),

Boolean.FALSE);

}

}

public Connection getConnection() throws SQLException {

Connection con = null;

Enumeration cons = connections.keys();

synchronized (connections) {

while(cons.hasMoreElements()) {

con = (Connection)cons.nextElement();

Boolean b = (Boolean)connections.get(con);

if (b == Boolean.FALSE) {

// So we found an unused connection.

// Test its integrity with a quick setAutoCommit(true) call.

// For production use, more testing should be performed,

// such as executing a simple query.

try {

con.setAutoCommit(true);

}

catch(SQLException e) {

// Problem with the connection, replace it.

con = DriverManager.getConnection(dbURL, user, password);

}

// Update the Hashtable to show this one's taken

connections.put(con, Boolean.TRUE);

// Return the connection

return con;

} // if

} // while

} // synchro

// If we get here, there were no free connections.

// We've got to make more.

for(int i = 0; i < increment; i++) {

connections.put(DriverManager.getConnection(dbURL, user, password),

Boolean.FALSE);

}

// Recurse to get one of the new connections.

return getConnection();

}

public void returnConnection(Connection returned) {

Connection con;

Enumeration cons = connections.keys();

while (cons.hasMoreElements()) {

con = (Connection)cons.nextElement();

if (con == returned) {

connections.put(con, Boolean.FALSE);

break;

}

}

} // returnConnection

} // class

From: Andre Richards <AndreRic@mweb.co.za>

A very good example on connection pooling when using Servlets can be found at :

http://webdevelopersjournal.com/columns/connection_pool.html

I succesfully used it in JSP 0.91 as follows :

<script RUNAT="SERVER">

DBConnectionManager connMgr = DBConnectionManager.getInstance();

</script>

<%

Connection con = connMgr.getConnection("freetds");

if (con == null) {

out.println("Can't get connection");

return;

}

try {

Statement stmt = con.createStatement();

ResultSet rs = stmt.executeQuery ("SELECT Hello FROM World");

while(rs.next()) {

out.println(rs.getString("Hello"));

}

stmt.close();

rs.close();}

catch (SQLException e) {

e.printStackTrace(out)

}

connMgr.freeConnection("freetds", con);

%>

13) How do I use other languages in my JSP? TOC

JSP is *Java* Server Pages, and the tags for other languages were taken out in 0.92. That said, two implementations (as of writing) support other languages:

- PolyJsp, 0.92, http://www.plenix.org/polyjsp,free + open source

- Resin. A JSP 0.92 implementation for compiled JavaScript, http://www.caucho.com/,free for personal use

14) How can I set a cookie in JSP? TOC

This should work:

response.setHeader("Set-Cookie", "cookie string");

To give the response-object to a bean, write a method setResponse

(HttpServletResponse response)

- to the bean, and in jsp-file:

<%

bean.setResponse (response);

%>

(from Aapo Kyr鰈?<aapo.kyrola@SATAMA.COM>)

15) Can JSP and Servlet share same Session and Beans? TOC

Example: I used Beans and Session with my servlet, JSP can use same Beans and Session or not?

From: Robert Hodges <hodges@TILDENPARK.COM>

This can be done, but you are likely to run into problems with class loaders. For instance, we have Apache/JServ which uses the AdaptiveClassLoader along with GNU-JSP which has a different class loader. If you just casually allocate objects in a servlet and then pick them up in JSP pages, you'll most likely get the dreaded ClassCastException, which signals the VM's pleasure when you try to cast a class that was brought in by a different class loader.

Note that there are sometimes problems even within JSP, as GNU-JSP drops the class loader every time you recompile a page, so if you allocated a class instance using one version of the page, then recompiled and tried to fish that instance back again, you would either (1) not find it or (2) get the ClassCastException (but see para #1 above).

If you really need to pass information around, the best way for Apache and GNU-JSP is to do the following:

Have one server per person when developing.

Make sure that all your Java classes, including servlet code, load through the system class path. This means they load through the primordial loader, which does not go away or change.

Make sure that your compiled JSP pages go to another location than your regular Java classes. That way, the JSP loader will just pick them up through the primordial loader.

In this scheme, you will need to reboot the Web server each time you make a change to the regular Java classes or else great confusion will ensue. (And possibly outrage among your users, I might add.)

If you need to share a Web Server between multiple people, or cannot reboot whenever you make class changes, the solution is much more complex. I can post a treatise on one approach at a later time (big project, deadline Friday) if there is interest.

16) How do I plug JSP into Microsoft's IIS Web Server? TOC

IBM's WebSphere, LiveSoftware's JRun and New Atlanta's ServletExec all provide plug ins for IIS 4.0.

Tomcat, the "reference" implementation of JSP 1.1 from the Jakarta Project also has an ISAPI plug-in. Tomcat does not (as of 3.1) support multi-homing.

Resin also provides support for multi-homing (very well at that!)

17) Are there any newsgroups that discuss JSP? TOC

Live Software has a new newsgroup for JSP discussion at news://news.livesoftware.com/livesoftware.jsp


JSP - FAQ (3)

作者：jspfuns

日期：2000-8-10 17:50:50

18) What do the differing levels of bean storage (page, session, app) mean? TOC

From: Joe Shevland <J_Shevland@TurnAround.com.au>

The spec is not clear on what the Application level scope actually means, but the general discussion has it that an Application is a single JSP whose beans persist from call to call - unlike those beans which have the "page" scope. That said, the 0.92 spec still stores "application" beans at the servlet level so they can actually be used by multiple servlets.

(From Gabriel Wong <gabrielw@EZWEBTOOLS.COM>)

In purely Servlet terms, they mean:

page - NO storage

session - servletrequest.getSession(true).putValue("myobjectname",myobject);

application - getServletConfig().getServletContext().setAttribute("myobjectname",myobject);

request - The storage exists for the lifetime of the request, which may be forwarded between jsp's and servlets.

19) Where can I find the mailing list archives? TOC

Archives of the JSP mailing list are available at http://archives.java.sun.com/archives/jsp-interest.html

These archives are searchable.

20) What are the important steps in using JDBC in JSP? TOC

1) Instantiate an instance of the JDBC driver you're trying to use (jdbc-odbc bridge name from memory so pls check):

Class.forName( "sun.jdbc.odbc.JdbcOdbcDriver" ).newInstance();

This determines if the class is available and instantiates a new instance of it, making it available for the next step.

2) Ask the DriverManager for a Connection object based on the JDBC URL you are using:

Connection connDB = DriverManager.getConnection( "jdbc:odbc:MyDSN",

"username", "password" );

DriverManager searches through any registered drivers (instantiating a new instance above is enough to register a driver with the DriverManager, as each implementation is required to) and, based on the JDBC URL you are using, returns the appropriate implementation of Connection.

3) Create a Statement object to retrieve a ResultSet

Statement smentDB = connDB.createStatement();

ResultSet rsDB = connDB.executeQuery( "SELECT * FROM Foo" );

or

rsDB = connDB.executeUpdate( "UPDATE Foo SET Bar = NULL" );

4) Close down connections to free resources:

rsDB.close();

smentDB.close();

connDB.close();

Note smentDB.close() closes the rsDB object, and connDB will close smentDB, cascading down, so you can really just: connDB.Close(). Also note there's no exception handling given here.

With the release of the JDBC 2.0 API, there is a CachedResultSet capability which would provide some assistance in making your pages perform better:

From: DIGNE Marc <jmdigne@MEUDON.NETCABLE.TM.FR>

I tested the JDBC CachedRowSet http://developer.java.sun.com/developer/earlyAccess/crs/index.html

"JDBCTM CachedRowSet is an implementation of the Rowset interface. The Rowset interface is part of the JDBC 2.0 Standard Extension API.

CachedRowSet provides a disconnected, serializable, scrollable container for tabular data. A CachedRowSet object can be thought of as a disconnected set of rows that are being cached outside of a data source.

Data contained in a CachedRowSet may be updated and then resynchronized with the underlying tabular data source. "

21) How does variable scope work in JSP? TOC

From: Alexander Yavorskiy <Alexander_Yavorskiy@VANTIVE.COM>

Hi,

An interesting observation about the variables declared in JSP pages.

Any variable declared inside <% .... %> is local to the page and is not visible to outside functions, even those declare on the same JSP.

Example:

<%

int evilVariable = "666";

%>

...

function testFunction() {

// do not see evilVariable from here

}

Why? evilVariable eventually becomes a local variable in the service() method of the resulting servlet and so is not accessible by other methods of that servlet.

Any variable declared inside <%! %> become global for any function declared in the servlet.

Example:

<%!

int evilVariable = "666";

%>

...

function testFunction() {

int x = evilVariable; //can get to it

}

Why? evilVariable declared this way becomes a private member variable of the resulting servlet and so is accessible by all other methods of that servlet.

Conclusion

It is important to understand this difference because in servlet environment there will only be a single(!!!) instance of the resulting servlet running and serving all requests for a particular page. Thus, potentially all of the member variables of that servlet will be share across the requests as opposed to variables local to the service() method that will be recreated for each request. So, we should be careful about putting none constant variables in <SERVER></SERVER>. At the same time, it might be useful to do so in some situations.

22) How do I forward to an HTML page? TOC

The method forward() in the Servlet API works for JSP pages, but this is only true for resources with _active_ content, like JSP pages.

If you wish to forward to an HTML page, you have to use a different method:

From: Volker Stiehl <stiehl@ZNNBG.SIEMENS.DE>

In order to access HTML files you have to use the new "resource abstraction" feature of 2.1.

Try the following:

URL url = getServletContext().getResource("/abc/xyz.html");

out.println(url.getContent());

23) Are there any white papers or documents explaining how JSP fits? TOC

From: "Craig R. McClanahan" <cmcclanahan@mytownnet.com>

http://www.software.ibm.com/ebusiness/pm.html

It is titled "The Web Application Programming Model", and provides a nice overview of the basic architecture IBM proposes for web applications (essentially the "Model 2" approach from the JSP specification). There are few IBM-specific product references in this document -- simply translate their term "dynamic server pages" into JSP, and generalize "WebSphere" to any useful combination of web server, servlet engine, and app server components. There are more IBM-specific references in several of the other white papers, but they still provide a useful overview of the technology basis for large scale web-based application development and deployment. The white paper index is at:

http://www.software.ibm.com/ebusiness/library.html

24) How to I create dynamic GIFs for my JSP? TOC

From: Matti Kotsalainen <matti@RAZORFISH.COM>

If you want to create GIFs, use ACME labs excellent free gifencoder(http://www.acme.com/ ,and then do something like this:

Frame frame = null;

Graphics g = null;

FileOutputStream fileOut = null;

try {

//create an unshown frame

frame = new Frame();

frame.addNotify();

//get a graphics region, using the frame

Image image = frame.createImage(WIDTH, HEIGHT);

g = image.getGraphics();

//manipulate the image

g.drawString("Hello world", 0, 0);

//get an ouputstream to a file

fileOut = new FileOutputStream("test.gif");

GifEncoder encoder = new GifEncoder(image, fileOut);

encoder.encode();

} catch (Exception e) {

;

} finally {

//clean up

if (g != null) g.dispose();

if (frame != null) frame.removeNotify();

if (fileOut != null) {

try { fileOut.close(); }

catch (IOException ioe) { ; }

}

}

25) Do you know where I could get some code that would encode something to the HTML DTD standard? TOC

As a matter of fact...

(NB: This is an implementation of the HTMLEncode function that ASP has)

From: Eric Lunt <elunt@YAHOO.COM>

Somewhere in my net-travels I picked up this version which performs pretty well:

/**

* Returns an HTML rendition of the given <code>String</code>. This was

* written by <a href=mailto:kimbo@biddersedge.com>Kimbo Mundy</a>.

* @param text A <code>String</code> to be used in an HTML page.

* @return A <code>String</code> that quotes any HTML markup

* characters. If no quoting is needed, this will be

* the same as <code>text</code>.

*/

public static String asHTML(String text) {

if (text == null)

return "";

StringBuffer results = null;

char[] orig = null;

int beg = 0, len = text.length();

for (int i = 0; i < len; ++i){

char c = text.charAt(i);

switch (c){

case 0:

case '&':

case '<':

case '>':

case '"':

if (results == null){

orig = text.toCharArray();

results = new StringBuffer(len+10);

}

if (i > beg)

results.append(orig, beg, i-beg);

beg = i + 1;

switch (c){

default: // case 0:

continue;

case '&':

results.append("&");

break;

case '<':

results.append("<");

break;

case '>':

results.append(">");

break;

case '"':

results.append(""");

break;

}

break;

}

}

if (results == null)

return text;

results.append(orig, beg, len-beg);

return results.toString();

}

26) What is page compilation? TOC

See (30)


JSP - FAQ (4)

作者：jspfuns

日期：2000-8-10 17:54:16

27) How are servlets and JSP pages related? TOC

JSP pages are focused around HTML (or XML) with Java codes and JSP tags inside them. When a web server that has JSP support is asked for a JSP page, it checks to see if it has already compiled the page into a servlet. Thus, JSP pages become servlets and are transformed into pure Java and then compiled, loaded into the server and executed. Different JSP implementations do this in more or less efficient ways.

28) Any good web sites for up to date activities in the Java/JSP/Servlet world? TOC

The following web sites contain information about JSP:

An IBM Tutorial on JSP: http://www.software.ibm.com/developer/education/java/online-courses.html

An IBM Red Book : http://www.redbooks.ibm.com/abstracts/sg245423.html

Other IBM Information: http://www.software.ibm.com/webservers/appserv/doc/v20dcadv/doc/index.html

JSP-Resource Information - http://www.jspin.com/is quite comprehensive on sites and articles.

JSP Tags is a site for Taglibs - http://jsptags.com/

The following web sites focus on JSP solutions

Servlets Taverne - http://www.interpasnet.com/JSS/

Oi Servlet World - http://i.am/servletforme

Web Development with JSP - http://www.burridge.net/jsp/

29) How do I force a user to log in? TOC

From: Andre Richards <AndreRic@MWEB.CO.ZA>

I did as follows:

On every page which must be authenticated, I check for a user ID in the session object - if it doesn't exit, I do a redirect to a login page, passing the url the user was trying to access as a parameter.

On the login page, if the user successfully logs in, I create a session for him/her, and add the user ID to the session. I then redirect back to the original page the user tried to access. This way, even if the user bookmarks a page, he/she will be asked to login once the session has become invalid.

Some code:

On every page I add the following:

HttpSession session = request.getSession(true);

if (session.getValue("CustomerID") == null) {

response.sendRedirect (response.encodeRedirectUrl

("Login.jsp?Origin=SharePortfolio.jsp"));

}

else {

// the rest of the page ...

In Login.jsp once the user has provided the correct logon credentials:

session.putValue("CustomerID", CustomerID);

response.sendRedirect(response.encodeRedirectUrl(request.getParameter("Origin")));

--------------------------------------------------------------------------------

Another developer has a different approach:

From: Christopher Cobb <ccobb@usgs.gov>

After researching several approaches, I have finally settled on the following approach. I would like to hear how others

are solving this problem. (FAQ maintainers note: This syntax won't work with JSP 1.0)

1. User accesses GuardedPage.jsp via

http://localhost/path/to/GuardedPage.jsp

2. GuardedPage.jsp includes a login checking page:

<!--#include file="/admin/" file="LoginChecker.jsp" -->

Every page that needs to be login-protected should include this file (which, depending on how your site is set up, may be

every page.)

3. LoginChecker.jsp accesses a bean that does the login checking:

<USEBEAN lifespan="session" name ="loginChecker" type="package.LoginChecker">

<setfromrequest beanproperty = "*">

</USEBEAN>

4. The LoginChecker bean has a property 'loggedIn'. (It also has properies for Username and Password, and a

processRequest() method, which are used later).

LoginChecker.jsp checks the value of the loggedIn property. If it is not true (i.e., the user is not logged in), a login

page is displayed:

<excludeif property ="loginChecker:loggedIn" value = "true">

<FORM action="/servlet/DBAccess/path/to/GuardedPage.jsp" method="post">

Username: <input name="userName" size="15" maxlength="15" >

Password: <input type="password" name="password" size="15" maxlength="15">

<input type="submit" name="loginUser" value="Submit">

</FORM>

</excludeif>

The first time through, this bean will be 'empty' and the loggedIn property will not be set. The login form will therefore

be displayed.

5. There is a little trick in the action clause above. When the user types in his login info and presses submit, the

invoked URL is

/servlet/DBAccess/path/to/GuardedPage.jsp

The action passes through the servlet DBAccess, then continues on to our original page. This servlet does nothing more

than attach an open database connection to the current session:

session.putValue("open.connection", connection);

The servlet then picks up the trailing part of the URL with:

String trailingURL = request.getPathInfo();

It then calls forward() to pass control back to the requested page. In this example, the new page happens to be the same

as the page we came from.

getServletConfig(

).getServletContext(

).getRequestDispatcher(response.encodeURL(trailingURL)

).forward(request,response);

6. Now we are back to our original page and the logginChecker bean gets invoked again. Because of the:

<setfromrequest beanproperty = "*">

in the loginChecker USEBEAN tag, and because our username and password field names in the LoginChecker.jsp page match our

bean's property names, the username and password that the user typed in get 'magically' populated in the corresponding

properties of the bean.

7. The LoginChecker bean has a processRequest() method which checks to see if a username and password has been supplied.

If so (and if we are not logged in), it performs a database lookup to log the user in. If the lookup is successful, the

loggedIn property is set to true.

8. We are finally back to our GuardedPage.jsp page. It will probably not want to display itself unless the user is logged

in. The page should therefore only be included if loggedIn is true:

<includeif property="loginChecker:loggedIn" value="true" >

The contents of GuardePage.jsp are displayed only if loggedIn is true.

</includeif>

We're done! GuardedPage.jsp is only displayed if the user is logged in. If the user is not logged in, a login page is

displayed, which if successful, returns the user to the original page.

9. There is one small cleanup which is needed in Step 4. As coded above, a passthrough servlet is used to attach a

database connection to the session. If the user repeatedly fails to login, the servlet prefix will get repeatedly

pre-pended to the URL. Furthermore, the 'current page' is hardcoded into the LoginChecker.jsp page which restricts it's

reusability. A little JavaScript fixes both of these problems. The following JavaScript should be used in place of the

<FORM> tag in Step 4. above.

<script language="JavaScript">

<!--

if (document.location.pathname.indexOf("/servlet/package.DBAccess") == 0)

document.write(

'<FORM action="' +

document.location.pathname +

'"method="post">');

else

document.write(

'<FORM action="/servlet/package.DBAccess' +

document.location.pathname +

'" method="post">');

//-->

</script>

30) So how can a newbie get started with JSP? TOC

See the QuickStart section of the JSP Book at http://www.esperanto.org.nz/jspbook

31) How can I ensure that session objects stay in existence when the web server restarts? TOC

There is no requirement that a session object will stay around as far as I can tell, but some web servers will serialize objects if they support the serialization interface.

32) How can I include one JSP inside another JSP? TOC

JRUN, ServletExec and GNUJSP allow you to specify (it was in the 0.91 spec):

<%@ include="./header.jsp" %> - where header.jsp is the file you want to include.

The spec does say that it supports NCSA style includes as in

<!--#include virtual="/pathfromdocdir/" file="copyright.html" -->

<!--#include file="data/table.html" -->

But there is no requirement that they support JSP.


JSP - FAQ (5)

作者：jspfuns

日期：2000-8-10 17:54:55

33) Is there some sort of event that happens when a session object gets bound or unbound to the session? TOC

From: "Kirkdorffer, Daniel" <daniel.kirkdorffer@ATTWS.COM>

HttpSessionBindingListener will hear the events When an object is added and/or remove from the session object, or when the session is invalidated, in which case the objects are first removed from the session, whether the session is invalidated manually or automatically (timeout).

34) Is there a way to execute a JSP from the comandline or from my own application? TOC

There is a little tool called JSPExecutor that allows you to do just that. The developers (Hendrik Schreiber <hs@webapp.de> & Peter Rossbach <pr@webapp.de>) aim was not to write a full blown servlet engine, but to provide means to use JSP for generating source code or reports. Therefore most HTTP-specific features (headers, sessions, etc) are not implemented, i.e. no reponseline or header is generated. Nevertheless you can use it to precompile JSP for your website. JSPExecutor is GPLed and available at no charge at:

http://www.webapp.de/jspexecutor.html

35) What should I use, Model 1 or Model 2? TOC

Whichever one works for you. Model 2 can give you more control, especially if you are doing things such as filtering the actual web pages a client receives because of language. Model 1 is generally more simple. A more full description of Model 1 vs Model 2 is at http://www.aptura.com/technology/jspBook_Architectures.html.

36) How do I delete a cookie with JSP? TOC

From: Chris Fesler <cfesler@InstantObjects.com>

A little more experimentation, and I came up with a method to delete cookies. Say that I have a cookie called "foo," that I set a while ago & I

want it to go away. I simply:

<%

Cookie killCookie = new Cookie("foo", null);

killCookie.setPath("/");

killCookie.setMaxAge(0);

response.addCookie(killCookie);

%>

Note that this is -not- how the JSDK documentation indicates cookies are to be deleted, but what the heck -- it seems to work.

37) Are there tips on things to watch out for in JSP? TOC

There are a number of things to beware of I'm sure, I have found one in particular so far:

From John Langley (langley@NEOTEK.MV.COM), don't have directories in your path that are not valid Java identifiers (for example, they start with a number, or are a number). A number of JSP implementations use the directory your JSP is in as part of the package name and if that is in a directory with an invalid identifier then you are out of luck.

Beware of server side includes not recompiling when you have changed them. Some JSP engines won't check into your JSP or create dependency lists to ensure that the entire content of your JSP will be recompiled if a dependency has changed.

Many servlet engines don't notice that you change your beans, so that if you change your JSP and change a bean that it uses, although the JSP will reload, the bean will not. This will require you to restart your server.

The Virtual Machine has some intrinsic limitations as well (contributed by Costin Manolache), and these affect your JSP. From Virtual Machine Spec:

64k entries in constant pool ( that means the count of strings you can use in out.println(), plus all other constants < 64k )

64k as the size of a method ( that mean the service() method can't have unlimited number of instructions )

constant string limit is 64k (bytes - not characters!) That may explain the 32K ( since Unicode chars are ~ 2 bytes - sometimes).

( it's not the limit of a String - just constant String ).

Other tips appreciated! Email me at rvowles@esperanto.org.nz

38) How do you get started with JSP on the Macintosh? TOC

From: Niels Peter Strandberg <nps@HEM1.PASSAGEN.SE>

This mail is for all Mac users that want to get started with Java Server Pages on the Mac.

This list is base on the mails I got from Mike Engelhart <mengelhart@earthtrip.com>. Who helped me to get my server started. Thanks Mike!

Java Server Pages is the Java worlds answer to Active Server Pages on NT. Sun has made a free version witch includes a WebServer and an JSP

engine. The package is made in 100% pure Java. So it runs on every Java platform, and it is free.

Before you can cook you own Java Server Pages, You need to build the "JSP machine". And here is how to do it.

You need the latest MRJ from Apple. MRJ2.1 can be found here: http://www.apple.com/java/.After download run the installer and install

the MRJ.

You also need the latest MRJ SDK 2.1 (Im using 2.0 and that works great), that can be found here: http://developer.apple.com/java/text/download.html#sdk.After download run the installer and install the MRJ SDK.

You also need the "JavaServer WDK EA (JavaServer Web Development Kit 1.0 EA)" from Sun. This can be found here: http://developer.java.sun.com/developer/earlyAccess/jsp/.I downloaded the Win version, but there should be no problem if you download the

Solaris version. After you have downloaded the Zip, unzip it using ZipIt or StuffIt. You have to be a registered member of developer connection

to get access to the files, but that it's free. So go ahead an register yourself.

After you have unziped the zip file, place the unziped folder "jswdk-1.0-ea" on the desktop. Now locate the "MRJ SDK 2.1" folder and

open it. Inside that folder is another folder named "JBindery" and inside that folder an app with the same name.

Now launche the app "jBindery". Click on the"Command" icon and add this to the Class name field: "com.sun.web.shell.Startup". Leave the

optional parameters blank.

Click on the "Classpath" icon. Click on the "add .zip file..." button and add the following files: "webserver.jar" which is in the root of your "jswdk-1.0-ea" folder. Also add the following files which are in the "lib" folder: "jsp.jar" and "jspengine.jar" and "servlet.jar"

Click on the "add folder..." button and ad the following folder "beans" locate in examples/WEB-INF/jsp/

Now you are ready to save these setting as an click able application. Click on the "Save Settings..." button, and give your "app" a name (ex.

JSP Server) and make sure the check box "save as App" is checked. Make sure that you save the app in the root of the "jswdk-1.0-ea" folder.

Now you have embedded these setting in an application file. Now locate your "JSP Server" application and double click on it. Wait a few seconds, and your server should be up an running. The app will quit if it cant find a IP number. Go into your TCP/IP control panel and assign a permanent IP number (ex. 127.0.0.1). Then try to run the app again.

The JSP Server will run at port 8080 by default. You can change that by opening the "default.cfg" file in a text editor and change the "server.port" from 8080 to 80, witch is the default port for WebServers.

Now here is where the fun begins. Open your web browser and type in the IP number you assigned to you mac in the TCP/IP control panel. (http://127.0.0.1or (http://127.0.0.1:8080) if you didn't change the port setting in the "default.cfg" file.

Now, explore the example and when your are ready, cook your own JSP.

39) What Application Servers support JSP? TOC

Originally contributed by Daniel Kirkdorffer, daniel.kirkdorffer@attws.com. See also http://www.interpasnet.com/JSS/textes/jsp2.htmor http://www.flashline.com/components/appservermatrix.jsp

40) What happened to the LOOP and DISPLAY tags from JSP 0.92? TOC

They were left out in JSP 1.0 onwards. There was no explanation given. They may be able to be expressed as tag extensions but I have not investigated the 1.1 API yet to determine how that could be done.

41) Does JSP have a bean providing File Upload features like ActiveX upload control? TOC

Get a look at the MultipartRequest object from the author of "Java Servlet Programming" (O'Reilly). Home page is http://www.servlets.com/index.html.The zip file for this class and other useful classes is at http://www.servlets.com/resources/com.oreilly.servlet/cos.zip.

42) So how do I use EJB from JSP? TOC

43) Can I just abort processing a JSP? TOC

Yes. Because your JSP is just a servlet method, you can just put (whereever necessary) a

<% return; %>

44) "I'm trying to pass an object between a servlet and a JSP page, but my object is null on the JSP page. What could be wrong?" TOC

From: Timothy Owen Reilly

HttpSession objects are limited in scope to a particular servlet zone. In my case, I saw this behaviour because my servlet was in one servlet zone, and my jsp engine (GnuJSP) was in another. Try printing out the session id in both your servlet and your JSP page [use: HttpSession.getId() ], if it's different in both, then obviously each side of the equation is using a different session framework, and you could have the same problem as I did.

Running both my servlets and GnuJSP in the same servlet zone solved this problem.

Note: The observed behaviour and solution were under Apache JServ 1.1 and GnuJSP 1.0


Jsp + JavaBean循序渐进教程(二)

作者：运气

email: webmaster@chinaspx.com

日期：2001-7-3 14:31:37

JavaBeans 和 JavaServer Pages的结合

    通过上面的学习，大家对JavaBean应该有了一个基本的了解，对于在JavaServer Pages中调用JavaBeans我们还需要了解一些特定的jsp的内容，让我们来看看吧。

    JavaServer Pages中的JavaBean相关标签

    在JavaServer Pages中调用JavaBean有三个标准的标签，那就是$#@60;jsp:useBean$#@62;, $#@60;jsp:setProperty$#@62;，以及$#@60;jsp:getProperty$#@62;。

    $#@60;jsp:useBean$#@62;标签

    $#@60;jsp:useBean$#@62;可以定义一个具有一定生存范围以及一个唯一id的JavaBean的实例，这样JavaServer Pages通过id来识别JavaBean，也可以通过id.method类似的语句来操作JavaBean。

    在执行过程中，$#@60;jsp:useBean$#@62;首先会尝试寻找已经存在的具有相同id和scope值的JavaBean实例，如果没有就会自动创建一个新的实例。

    其具体语法如下

$#@60;jsp:useBean id=“name”

scope=“page|request|session|application”

typeSpec$#@62;

body

$#@60;/jsp:useBean$#@62;

    其中，typeSpec定义如下

typeSpec ::=class=“className”

| class=“className” type=“typeName”

| type=“typeName” class=“className”

| beanName=“beanName” type=“typeName”

| type=“typeName” beanName=“beanName”

| type=“typeName”

    下面的表格是$#@60;jsp:useBean$#@62;标签中相关属性的含义

    属性及定义

Id

    id属性是JavaBean对象的唯一标志，代表了一个JavaBean对象的实例。它具有特定的存在范围(page|request|session|application)。在JavaServer Pages中通过id来识别JavaBean。

Scope

    Scope属性代表了Javabean对象的生存时间，可以是page, request, session, 和 application中的一种。

Class

    代表了JavaBean对象的class名字，特别注意大小写要完全一致。

beanName

    BeanName属性代表了Bean的名字，通常通过java.beans.Beans class的 tantiate() 方法来初始化。.

Type

    Type属性指定了脚本变量定义的类型，默认为脚本变量定义和class中的属性一致，一般我们都采用默认值

    $#@60;jsp:setProperty$#@62;标签

    另一个标准的标签就是$#@60;jsp:setProperty$#@62;标签了，它主要用于设置bean的属性值。JavaServer Pages中调用的语法如下：

    $#@60;jsp:setProperty name=“beanName” last_syntax /$#@62;

    其中，name属性代表了已经存在的并且具有一定生存范围(scope)的JavaBean实例。last_syntax代表的语法如下： property=“*” |

property=“propertyName” |

property=“propertyName” param=“parameterName” |

property=“propertyName” value=“propertyValue”

    下面是$#@60;jsp:setProperty$#@62;标签的基本属性以及含义

    属性及定义

Name

    Name代表通过$#@60;jsp:useBean$#@62; 标签定义的JavaBean对象实例。

Property

    这是个很重要的属性，代表了你想设置值的属性property名字。如果使用property=”*”，程序就会反复的查找当前的ServletRequest所有参数，并且匹配JavaBean中相同名字的属性property，并通过JavaBean中属性的set方法赋值value给这个属性。如果value属性为空，则不会修改Javabean中的属性值。

Param

    Param属性代表了页面请求的参数名字，$#@60;jsp:setProperty$#@62;标签不能同时使用param和value。

Value

    Value属性代表了赋给Bean的属性property的具体值。

    $#@60;jsp:getProperty$#@62;标签

    最后一个标签就是$#@60;jsp:getProperty$#@62;标签了.它可以得到JavaBean实例的属性值，并将他们转换为 java.lang.String, 最后放置在隐含的Out对象中. JavaBean的实例必须在$#@60;jsp:getProperty$#@62;前面定义.

    $#@60;jsp:getProperty$#@62;标签的语法如下：

    $#@60;jsp:getProperty name=“name” property=“propertyName” /$#@62;

    下面是$#@60;jsp:getProperty$#@62;标签的基本属性以及含义

    属性及定义

Name

    Name属性代表了想要获得属性值的Bean的实例，Bean实例必须在前面用$#@60;jsp:useBean$#@62;标签定义．

Property

    Property属性代表了想要获得值的那个property的名字。


Jsp + JavaBean循序渐进教程(三)

作者：运气

email: webmaster@chinaspx.com

日期：2001-7-3 14:32:40

<font color="#FF0000">Jsp与JavaBean结合的简单例子

    上面讲了这么多，到现在还没有看到具体应用，那好，现在我们看看具体的JavaServer Pages+JavaBean的例子吧，首先让我们看看一个简单的计数器程序。

    本例程共包含３个文件

    JavaBean--counter.java文件，JavaServer Page-- counter.jsp文件, counter1.jsp文件其中，counter.java主要用来进行计数器的计数操作，counter.jsp和counter1.jsp文件主要用来显示网页的计数。

    counter.java文件

package count;

/**

* Title: test

* Description: counter Bean

* @author LiuYufeng

* @version 1.0

*/

public class counter {

    //初始化JavaBean的成员变量

int count = 0;

    // Class构造器

public counter() {

}

    // 属性Count的Get方法

public int getCount() {

    //计数操作，每一次请求都进行计数器加一

count++;

return this.count;

}

    //属性Count的Set方法

public void setCount(int count) {

this.count = count;

}

}

    counter.jsp文件

$#@60;HTML$#@62;

$#@60;HEAD$#@62;

$#@60;TITLE$#@62;

counter

$#@60;/TITLE$#@62;

$#@60;/HEAD$#@62;

$#@60;BODY$#@62;

$#@60;H1$#@62;

JBuilder Generated JSP

$#@60;/H1$#@62;

$#@60;!-初始化counter这个Bean，实例为bean0--$#@62;

$#@60;jsp:useBean id="bean0" scope="application" class="count.counter" /$#@62;

$#@60;%

    //显示当前的属性count的值，也就是计数器的值，这里我们使用out.println方法，下面的counter1.jsp将使用另一种方法

out.println("The Counter is : " + bean0.getCount() + "$#@60;BR$#@62;");

%$#@62;

$#@60;/BODY$#@62;

$#@60;/HTML$#@62;

    counter1.jsp文件

$#@60;HTML$#@62;

$#@60;HEAD$#@62;

$#@60;TITLE$#@62;

counter

$#@60;/TITLE$#@62;

$#@60;/HEAD$#@62;

$#@60;BODY$#@62;

$#@60;H1$#@62;

JBuilder Generated JSP

$#@60;/H1$#@62;

$#@60;!-初始化counter这个Bean，实例为bean0--$#@62;

$#@60;jsp:useBean id="bean0" scope="application" class="count.counter" /$#@62;

$#@60;!-使用jsp:getProperty 标签得到count属性的值，也就是计数器的值--$#@62;

The Counter is :

$#@60;jsp:getProperty name="bean0" property="count" /$#@62;$#@60;BR$#@62;

$#@60;/BODY$#@62;

$#@60;/HTML$#@62;

    从这个例子我们不难看出Jsp和JavaBean应用的一般操作方法，首先在Jsp页面中要声明并初始化JavaBean，这个JavaBean有一个唯一的id标志，还有一个生存范围scope(设置为application是为了实现多个用户共享一个计数器的功能，如果要实现单个用户的计数功能，可以修改scope为session)，最后还要制定JavaBean的class来源count.counter：

    $#@60;jsp:useBean id="bean0" scope="application" class="count.counter" /$#@62;

    接着我们就可以使用JavaBean提供的public方法或者直接使用$#@60;jsp:getProperty$#@62;标签来得到JavaBean中属性的值：

    out.println("The Counter is : " + bean0.getCount() + "$#@60;BR$#@62;");

或者

    $#@60;jsp:getProperty name="bean0" property="count" /$#@62;

    OK，现在运行一下程序看看，然后多刷新几次，注意看计数器的变化。上面的程序在Jbuilder4.0下面调试通过。

    如果要直接在一些jsp环境(如Tomcat、IAS、Weblogic等)下调试，请注意各自的文档，正确的放置JavaBean文件。如在Tomcat环境中，本例子JavaBean编译后的文件就需要放在$#@60;Server Root$#@62;\WEB-INF\ClASSES\ count\ counter.class。


Jsp + JavaBean循序渐进教程(四)上

作者：运气

email: webmaster@chinaspx.com

日期：2001-7-3 14:33:33

JavaServer Pages+JavaBeans的数据库操作应用

    上面已经讲了一个简单的JavaBean应用的计数器例子，当然在实际程序过程中，涉及的更多的还是和数据库相关的操作，所以在这一节我们将重点阐述JavaServer Pages和JavaBeans如何对数据库进行操作。这里我们选取了一个比较有代表性比较实用的例子，那就是用户注册管理，因为这在网上使用比较的频繁，不管是注册Email、有奖调查、购买物品或者加入社区等等都会涉及到一个用户注册的问题；另外一方面，它又比较有代表性，涉及到了数据库的记录增加，记录显示等常见操作，所以我们就拿用户注册开刀了。

    程序采用Oracle Jdeveloper3.1开发，运行环境为Wiin2000+Tomcat3.1，数据库系统采用了Oracle8.16i。

    首先我们建立一个数据库demodb，其字段如下面所示

username VARCHAR2(20) 用户名

password VARCHAR2(20) 密码

email VARCHAR2(30) Email地址

homepage VARCHAR2(50) 主页

signs VARCHAR2(200) 签名

regtime DATE 注册时间

    接着我们建立几个JavaBeans和JavaServer Pages文件

db.java文件(封装数据库连接及一些底层操作)

adduser.java文件(进行用户数据的读取以及添加操作)

newuser.jsp文件(用户新增页面，用于输入用户注册信息)

donewuser.jsp文件(进行用户注册信息添加)

listuser.jsp文件(所有的注册用户信息列表)

    为了方便大家看代码，在很多地方都进行了详细的注释和讲解，至于JavaBean中涉及到Java语法结构的东西，请大家参考Java书籍。

    db.java文件

    说明：这个JavaBean封装数据库连接及一些底层操作，派生出的类可以直接调用这些方法，另外提供了一个toChinese方法，主要用来进行中文数据的处理。

    // Copyright (c) 2000 http://jspbbs.yeah.net

package lyf;

/**

* A Class class.

* $#@60;P$#@62;

* @author liuyufeng

*/

    //声明类库文件

import oracle.jdbc.driver.*;

import java.net.*;

import java.sql.*;

import java.lang.*;

import java.io.*;

import java.util.*;

public class db br> 　　//成员变量初始化

Connection conn = null; //数据库连接

ResultSet rs = null; //记录集

String Username=""; //用户名

String Password=""; //密码

String Email=""; //email

String Homepage=""; //主页

String Signs=""; //签名

    //db的构建器

public db() {

try {

    //注册数据库驱动程序为Oracle驱动

Class.forName(new oracle.jdbc.driver.OracleDriver());

}

catch(java.lang.ClassNotFoundException e) {

    //这样写是为了方便调试程序，出错打印mydb()就知道在什么地方出错了

System.err.println("mydb(): " + e.getMessage());

}

}

    //executeQuery方法用于进行记录的查询操作

    //入口参数为sql语句，返回ResultSet对象

public ResultSet executeQuery(String sql) {

rs = null;

try {

    //建立数据库连接，使用Oracle的一种thin连接方式，demo为主机名字，demodb为数据库，后面的两个demo为用户名和密码

conn = DriverManager.getConnection("jdbc:oracle:thin:@demo:1521:demodb","demo","demo");

Statement stmt = conn.createStatement();

    //执行数据库查询操作

rs = stmt.executeQuery(sql);

}

catch(SQLException ex) {

System.err.println("db.executeQuery: " + ex.getMessage());

}

return rs;

}

    //executeUpdate方法用于进行add或者update记录的操作

    //入口参数为sql语句，成功返回true，否则为false

public boolean executeUpdate(String sql) {

boolean bupdate=false;

rs = null;

try {

    //建立数据库连接，其它参数说明同上面的一样

conn = DriverManager.getConnection("jdbc:oracle:thin:@demo:1521:demodb","demo","demo");

Statement stmt = conn.createStatement();

int rowCount = stmt.executeUpdate(sql);

    //如果不成功，bupdate就会返回0

if(rowCount!=0)bupdate=true;

}

catch(SQLException ex) {

    //打印出错信息

System.err.println("db.executeUpdate: " + ex.getMessage());

}

return bupdate;

}

    //toChinese方法用于将一个字符串进行中文处理

    //否则将会是???这样的字符串

public static String toChinese(String strvalue) {

try{

if(strvalue==null)

{

return null;

}

else

{

strvalue = new String(strvalue.getBytes("ISO8859_1"), "GBK");

return strvalue;

}

}catch(Exception e){

return null;

}

}

}


Jsp + JavaBean循序渐进教程(四)下

作者：运气

email: webmaster@chinaspx.com

日期：2001-7-3 14:34:21

  <font color="#0000FF">adduser.java文件

    说明：主要进行用户数据的读取以及添加操作，从db派生出来，addNewUser方法用来进行用户数据的添加，checkUser()方法用来检查用户名是否重复，另外还有一些set/get方法用来对属性进行处理，dousernew.jsp文件将进行条用。

    // Copyright (c) 2000 http://jspbbs.yeah.net

package lyf;

/**

* A Class class.

* $#@60;P$#@62;

* @author liuyufeng

*/

    //导入java类库

import java.sql.*;

import java.lang.*;

import oracle.jdbc.driver.*;

    //adduser由db派生出来，拥有db的成员变量和方法

public class adduser extends db {

    //构建器

public boolean addNewUser(){

boolean boadduser=false;

try {

    //进行用户注册的记录添加操作，生成sql语句

String sSql=new String("insert into user(regtime,username,password,email,homepage,signs)");

sSql=sSql+ " values(SYSDAYE,""+Username+"",""+Password+"",""+Email+"",""+Homepage+"",""+Signs+"")";

    //一种调试的方法，可以打印出sql语句，以便于查看错误

System.out.println(sSql);

    //调用父类的executeUpdate方法，并根据成功以否来设置返回值

if(super.executeUpdate(sSql))boadduser=true;

}

catch(Exception ex) {

    //出错处理

System.err.println("adduser.addNewUser: " + ex.getMessage());

}finally{

    //无论是否出错，都要返回值

return boadduser;

}

}

    //checkUser()方法用来检查用户名是否重复

    //如果重复返回一个false

public boolean checkUser(){

boolean boadduser=false;

try {

    //构建sql查询语句

String sSql="select * from user where username=""+Use me+""";

    //调用父类的executeQuery方法

if((super.executeQuery(sSql)).next()){

    //查询出来的记录集为空

boadduser=false;

}else{

boadduser=true;

}

}

catch(Exception ex) {

    //出错处理

System.err.println("adduser.addNewUser: " + ex.getMessage());

}finally{

    //返回值

return boadduser;

}

}

    //属性的set/get方法，同请求的参数一致

/*

    其实下面的所有get/set方法都是重复性劳动，为了避免重复性的拷贝粘贴工作，我写了个软件Jsp Code Faster，只要输入一系列的字段名，所有的get/set方法都可以自动生成，大家可以在我的网站http://jspbbs.yeah.net

*/

    //属性用户名Username的get/set方法

public String getUsername(){

return Username;}

public void setUsername(String newUsername){

    //用户名有可能是中文，需要进行转换

Username =db.toChinese(newUsername);}

    //属性密码Password的get/set方法

public String getPassword(){

return Password;}

public void setPassword(String newPassword){

Password = newPassword;}

    //属性Email的get/set方法

public String getEmail(){

return Email;}

public void setEmail(String newEmail){

Email = newEmail;}

    //属性主页Homepage的get/set方法

public String getHomepage(){

return Homepage;}

public void setHomepage(String newHomepage){

Homepage = newHomepage;}

    //属性主页Signs的get/set方法

public String getSigns(){

return Signs;}

public void setSigns(String newSigns){

    //签名有可能是中文，需要进行转换

Signs = db.toChinese(newSigns);}

}

    好了，到这里，Javabean程序基本上写完了，要注意的是中文处理一定要经过转换，还有不一定所有的属性都需要set/get方法，视情况而定，最后还需要编译为class文件，可以使用一些可视化的软件如Jbuilder或者VisualAge等等来编译。编译后会发现有两个文件db.class和adduser.class文件，都在lyf子目录下。这两个文件就可以让后面的jsp页面调用了。


Jsp + JavaBean循序渐进教程(一)

作者：运气

email: webmaster@chinaspx.com

日期：2001-7-3 14:30:40

  Jsp作为一个很好的动态网站开发语言得到了越来越广泛的应用，在各类Jsp应用程序中，Jsp + Java Bean的组合成为了一种事实上最常见的Jsp程序的标准，就让我们来看看具体的Jsp是如何与Java Bean结合在一起的吧。

    本教程将阐述JavaBean的原理，接着将阐述JavaBean在JavaServer Page下的特定语法，然后演示一个使用Jsp+JavaBean的简单的计数器，最后将详细的讲解一个有数据库功能的JavaBean+jsp的用户注册程序。

Java Bean原理与应用

    1、什么是JavaBean？

    JavaBean是描述Java的软件组件模型，有点类似于Microsoft的COM组件概念。在Java模型中，通过JavaBean可以无限扩充Java程序的功能，通过JavaBean的组合可以快速的生成新的应用程序。对于程序员来说，最好的一点就是JavaBean可以实现代码的重复利用，另外对于程序的易维护性等等也有很重大的意义。

    JavaBean通过Java虚拟机(Java Virtual Machine)可以得到正确的执行，运行JavaBean最小的需求是JDK1.1或者以上的版本。

    JavaBean传统的应用在于可视化的领域，如AWT下的应用。自从Jsp诞生后，JavaBean更多的应用在了非可视化领域，在服务器端应用方面表现出来了越来越强的生命力。在这里我们主要讨论的是非可视化的JavaBean，可视化的JavaBean在市面上有很多Java书籍都有详细的阐述，在这里就不作为重点了。

    2、非可视化的JavaBean

    非可视化的JavaBean，顾名思义就是没有GUI界面的JavaBean。在Jsp程序中常用来封装事务逻辑、数据库操作等等，可以很好地实现业务逻辑和前台程序(如jsp文件)的分离，使得系统具有更好的健壮性和灵活性。

    一个简单的例子，比如说一个购物车程序，要实现购物车中添加一件商品这样的功能，就可以写一个购物车操作的JavaBean，建立一个public的AddItem成员方法，前台Jsp文件里面直接调用这个方法来实现。如果后来又考虑添加商品的时候需要判断库存是否有货物，没有货物不得购买，在这个时候我们就可以直接修改JavaBean的AddItem方法，加入处理语句来实现，这样就完全不用修改前台jsp程序了。

    当然，也可以把这些处理操作完全写在jsp程序中，不过这样的jsp页面可能就有成百上千行，光看代码就是一个头疼的事情，更不用说修改了。如果您使用过asp开发过程序的话，相信对这就深有体会? 涫凳褂肁SP+COM组件完全可以实现同jsp+javabean同样的架构，但不知道某种原因网上常见的都是全部写在asp页面中，所以使得维护修改等极为不方便，当然这是题外话了)。由此可见，通过JavaBean可以很好地实现逻辑的封装、程序的易于维护等等。

    如果您使用Jsp开发程序，一个很好的习惯就是多使用JavaBean。

    3、JavaBean的简单例子

    创建JavaBean并不是一件困难的事情，如果您写过Java程序的话那就很容易了，要注意一点的就是在非可视化JavaBean中，常用>get或者>set这样的成员方法来处理属性>(properties>)。>

    下面让我们来看一个简单的JavaBean

FirstJavaBean.java

import java.io.*;

public class FirstJavaBean {

private String FirstProperty = new String("");

public FirstJavaBean() {

}

public String getFirstProperty() {

return FirstProperty;

}

public void setFirstProperty(String value) {

FirstProperty = value;

}

public static void main(String[] args)

{

System.out.println("My First JavaBean!");

}

}

如果运行这个程序，就会出现下面的结果：

First JavaBean!

    这是一个很典型的JavaBean的代表，简单地解释一下，FirstProperty是其中的一个属性(Property)，外部通过get/set方法可以对这个属性进行操作，如果您写过VB的class的话，对这个就再也熟悉不过了。Main方法是为了测试程序用的，写JavaBean可以先不必加入到Jsp程序中调用，而直接用main方法来进行调试，调试好以后就可以在Jsp程序中调用了。


JSP Insider 5 在JSP中使用JavaMail--1

作者：caoli

email: caoli@dicp.ac.cn

日期：2001-7-4 17:50:39

你对用JSP来构造邮件工具感兴趣吗？如果是的，那你就来对了地方。本教程将演示邮件工具后面的所隐藏的基本概念，包括搜集email数据的HTML代码，JavaMail API的几个重要特性，以及如何把这些特性集成到JSP中。这个JavaMail的初级教程包括一个在JSP下成功地实现 JavaMail的例子，一旦掌握之后，JavaMail就成为JSP应用程序中的一个功能强大的API工具。

学习本教程需具有JSP 、JavaBeans和JavaMail API的初步知识，如果你打算了解JSP或者JavaBean的基本知识，请参阅JSP Insider archive，在那里，我们提供了introduction to Java Server Pages和其他很好的教程。你也可以去访问Sun's product information page。要了解更多的JavaBeans的信息，请访问Sun's JavaBean Tutorial。关于JavaMail的知识，在本课及后续课程中都有介绍。

如果你对上述三个知识点一无所知，但是你的服务器支持JSP和JavaMail，你可以把该工具拷贝到你的服务器中，直接使用即可。除了本教程之外，你还可以获得非正式的JavaMail 标记库。

 JavaMail是Sun开发的用于email处理的API，该API预置了一些最流行的邮件传输协议的实现，使用方便。要了解更多的背景知识，请参见Sun's product information page。

虽然JavaMail是Sun公司开发的API，但目前它并不包含在标准的Java Development Kit中。这意味着你需要下载JavaMail的类文件。要访问JavaMail的网址，请点击此处http://java.sun.com/products/javamail/index.htmlActivation Framework (JAF)http://java.sun.com/products/javabeans/glasgow/jaf.html。没有JAF的参与，JavaMail将不能工作。一旦你下载完所有必须的文件，务必要让Java编译器可以访问到这些类文件。例如，在Windows 2000中，只需要在CLASSPATH 环境变量中，添加类文件的路径。在其他的操作系统中，也需要做类似的工作。

唉！

到现在为止，你还没有编写一行代码，就已经大汗淋漓了？不要太担心，这是本教程的最难部分，确信上面的一切已经安装就绪，之后，让我们接着进行下去吧。

教程的剩余部分将分成三个部分：

第一部分：HTML表单

第二部分：关于JavaMail

第三部分：JavaMail和JSP 协同使用

第一部分提供了一个HTML表单的例子，用来搜集典型的email数据，第二部分，将介绍更多关于JavaMai的功能，第三部分，把HTML表单和JavaMai组织在一起，让JavaMail和JSP协同工作，创建一个基本的邮件工具。


JSP Insider 5 在JSP中使用JavaMail---2

作者：caoli

email: caoli@dicp.ac.cn

日期：2001-7-4 17:53:06

第一部分， HTML表单

分治

JSP的最好的功能之一就是可以把项目分解成组件。这个邮件工具也具有上述特点，我们的组件包括：

一个HTML表单，用来把email信息post给JSP。

一个用来处理和发送该mail的JSP页面。

第一步，是创建一个使用HTML的可以发送给JSP页面的表单。你可以把列表A中的HTML源代码拷贝到你的机器中。

<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>

该HTML表单创建了一个文档，用来搜集诸如To, From, Subject 和Message的基本的email信息。当然，创建自己的邮件表单的好处是可以自行控制搜集的信息，或多或少自己可以决定。请看此处生成的HTML表单样本。

该HTML文档有两项主要需求：首先，表单的action必须是：post给下面将要介绍的工具sendmail.jsp，但在你的应用程序中，可用合适的URL来取代sendmail.jsp。同样，你需要规定一些希望包含的域，以便可以让用户发送这些信息。本节课的下一部分将介绍JavaMail的一些特点，以便为掌握第三部分扫清障碍。所以，现在让我们来了解一下JavaMail。


JSP Insider 5 在JSP中使用JavaMail---3

作者：caoli

email: caoli@dicp.ac.cn

日期：2001-7-4 17:54:02

第二部分，关于JavaMail

使用文档

下载JavaMail API 之后，其中所包含的文档非常有用，文档存放在JavaMail所在目录的子目录/docs/javadocs/中。下面将介绍邮件工具中所需用到的一些组件。请参阅JavaMail文档，以便了解更多相关信息。

使用JavaMail发送email时需要的组件

JavaMail的结构体系，使得处理email尤为简单。下面为我们所需要的一些类。

Properties（属性）：JavaMail在创建一个会话对象时，需要用到Properties。该对象将寻找名为“mail.smtp.host”的属性串，其属性值是发送mail的主机。

Properties props = new Properties ();

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

Session（会话）： Session对象表示JavaMail中的一个mail对话。每个基于JavaMail的工具至少需要一个session，但可以有多个session。本例中， Session对象需要知道处理邮件的SMTP服务器。创建Session 对象可以这样进行：

Session sendMailSession;

sendMailSession = Session.getInstance(props, null);

Transport（传输）：邮件要么是发送出去，要么是接收进来。JavaMail 用两个不同的对象表示这两个动作，即Transport和Store。Transport对象用于发送信息，Store用于接收信息。在本教程中，我们需要用到Transport对象。

Transport transport;

transport = sendMailSession.getTransport("smtp");

可以使用JavaMail 的Session对象的getTransport 方法来初始化Transport 对象。传递的字符串参数定义了返回对象中的使用的协议。这里，我们可以看到，使用JavaMail节省了很多的时间，否则我们得自己编程序完成SMTP的全部实现过程，而JavaMail已经把这些工作预先做好了。

注意：JavaMail并不是什么都支持，目前仅支持IMAP，SMTP和POP3。除此之外，如果还需要支持其他的协议，你就需要等待JavaMail的下一版本，或者你自己构造有关协议。

Message（消息）：Message对象表示要发送的实际的email消息。创建的Message 对象为MimeMessage，该对象需要知道选择哪一个JavaMail 会话。

Message newMessage = new MimeMessage(sendMailSession);

这就完了，我们需要的就是上面介绍的这四个对象。下一步，将介绍如何把这些对象放到JSP中。


JSP Insider 5 在JSP中使用JavaMail---4

作者：caoli

email: caoli@dicp.ac.cn

日期：2001-7-4 17:57:26

第三部分：在JSP中使用JavaMail

构造JSP

现在，将介绍如何把这些对象放到一起，需要用页面指示语把这些对象导入，这点是很关键的。注意：也要用到java.util.date，因为email的时戳要用到java.util.date。

<%@ page

 import= " javax.mail.*, javax.mail.internet.*,

  =>javax.activation.*, java.util.*"

%>

下一步要创建一个特定的信息，表示邮件已经被发送。你可以自己设定该信息，通常是：“Your mail has been sent。”

如何创建和发送消息

创建Message对象的代码在第二部分已经出现了。现在我们可以处理该消息。很简单，只要在对象中使用get和 set properties方法即可。使用post过来的信息作为参数，例如request.getParameter()就是个很有用的方法，可以获取post发送来的参数。参见Listing B。

Listing B:

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"));

最后就是发送消息了，JavaMail工具使得这件事变得很简单：

transport.send(newMessage);

把它们组合在一起

刚才我们介绍的是一个个的分开的组件，现在可以把它们组合在一起，放在一个JSP脚本中。注意，要加入捕捉错误并显示给用户的脚本语句。JSP脚本请见Listing C。要使用该脚本，只要把脚本拷贝到你的电脑中即可。

Listing C:

<%@ 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>

这就行了！如果你打算了解JavaMail所做的一步步的动作，就使用'sendMailSession.setDebug(true);'。

现在你已经对JSP 和JavaMail的使用有所入门。如果想了解更多信息，请参见Sun's JavaMail links。

如何做得更好？

最重要的是这个工具能够运行。但我们可以改进它。后续教程将介绍：

为什么不把表单放在JSP中?

客户端的验证

如何使用beans来代替那些杂乱的不能重用的代码。

通过JSP，把功能和设计分开。


JSP 的模板

作者：jspfuns

日期：2000-8-10 17:27:55

By Scott Ferguson

引论

样板的框架: Hello, World

Servlet 评论

展示留言板

留言板的模式

作为应用属性的留言板

留言板的逻辑

结论

引论

JSP的强大优势在于把一种应用的商务逻辑和它的介绍分离开来。用 Smalltalk的面向对象的术语来说， JSP鼓励MVC(model-view-controller)的web应用。JSP的classes 或 beans 是模型, JSP 是这个视图, 而Servlet是控制器。

这个例子是一个简单的留言板。用户登录和留言。 It is also available in the Resin demos

Role Implementation

Model A GuestBook of Guests.

View login.jsp for new users

add.jsp for logged-in users.

Controller GuestJsp, a servlet to manage the state.

样板的框架: Hello, World

GuestJsp的框架把 "Hello, World" 这个字符串传给login.jsp页面。这个框架为留言板设立结构。具体细节将在下面补充。

这个例子被编译后可以浏览到：

http://localhost:8080/servlet/jsp.GuestJsp

你可以看到这样的页面:

Hello, world

JSP模板是以Servlet的处理开始然后把处理结果传给JSP页进行格式化。

Forwarding uses a Servlet 2.1 feature of the ServletContext, getRequestDispatcher(). The request dispatcher lets servlets forward and include any subrequests on the server. It's a more flexible replacements for SSI includes. The RequestDispatcher can include the results of any page, servlet, or JSP page in a servlet's page. GuestJsp will use dispatcher.forward() to pass control to the JSP page for formatting.

GuestJsp.java: Skeleton package jsp.GuestJsp;

import java.io.*;

import java.util.*;

import javax.servlet.*;

import javax.servlet.http.*;

/**

* GuestJsp is a servlet controlling user

* interaction with the guest book.

*/

public class GuestJsp extends HttpServlet {

/**

* doGet handles GET requests

*/

public void doGet(HttpServletRequest req,

HttpServletResponse res)

throws ServletException, IOException

{

// Save the message in the request for login.jsp

req.setAttribute("message", "Hello, world");

// get the application object

ServletContext app = getServletContext();

// select login.jsp as the template

RequestDispatcher disp;

disp = app.getRequestDispatcher("login.jsp");

// forward the request to the template

disp.forward(req, res);

}

}

The servlet and the jsp page communicate with attributes in the HttpRequest object. The skeleton stores "Hello, World" in the "message" attribute. When login.jsp starts, it will grab the string and print it.

Since Resin's JavaScript understands extended Bean patterns, it translates the request.getAttribute("message") into the JavaScript equivalent request.attribute.message.

login.jsp: Skeleton <%@ page language=javascript %>

<head>

<title><%= request.attribute.message %></title>

</head>

<body bgcolor='white'>

<h1><%= request.attribute.message %></h1>

</body>

Servlet Review

For those coming to JSP from an ASP or CGI background, Servlets replace CGI scripts taking advantage of Java's strength in dynamic class loading. A servlet is just a Java class which extends Servlet or HttpServlet and placed in the proper directory. Resin will automatically load the servlet and execute it.

doc

index.html

login.jsp

add.jsp

WEB-INF

classes

jsp

GuestJsp.class

GuestBook.class

Guest.class

The url /servlet/classname forwards the request to the Servlet Invoker. The Invoker will dynamically load the Java class classname from doc/WEB-INF/classes and try to execute the Servlet's service method.

Resin checks the class file periodically to see if the class has changed. If so, it will replace the old servlet with the new servlet.

Displaying the Guest Book

The next step, after getting the basic framework running, is to create the model.

The GuestBook model

The guest book is straightforward so I've just included the API here. It conforms to Bean patterns to simplify the JavaScript. The same API will work for HashMap, file-based, and database implementations.

JSP files only have access to public methods. So a JSP file cannot create a new GuestBook and it can't add a new guest. That's the responsibility of the GuestJsp servlet.

jsp.Guest.java API package jsp;

public class Guest {

Guest();

public String getName();

public String getComment();

}

Resin's JavaScript recognizes Bean patterns. So JSP pages using JavaScript can access getName() and getComment() as properties. For example, you can simply use guest.name and guest.comment

jsp.GuestBook.java API package jsp;

public class GuestBook {

GuestBook();

void addGuest(String name, String comment);

public Iterator iterator();

}

Resin's JavaScript also recognizes the iterator() call, so you can use a JavaScript for ... each to get the guests:

for (var guest in guestBook) {

...

}

GuestBook as application attribute

To keep the example simple, GuestJsp stores the GuestBook in the application (ServletContext). As an example, storing data in the application is acceptable but for full-fledged applications, it's better just to use the application to cache data stored elsewhere.

jsp.GuestJsp.java // get the application object

ServletContext app = getServletContext();

GuestBook guestBook;

// The guestBook is stored in the application

synchronized (app) {

guestBook = (GuestBook) app.getAttribute("guest_book");

// If it doesn't exist, create it.

if (guestBook == null) {

guestBook = new GuestBook();

guestBook.addGuest("Harry Potter", "Griffindor rules");

guestBook.addGuest("Draco Malfoy", "Slytherin rules");

app.setAttribute("guest_book", guestBook);

}

}

RequestDispatcher disp;

disp = app.getRequestDispatcher("login.jsp");

// synchronize the Application so the JSP file

// doesn't need to worry about threading

synchronized (app) {

disp.forward(req, res);

}

The JSP file itself is simple. It grabs the guest book from the application and displays the contents in a table. Normally, application objects need to be synchronized because several clients may simultaneously browse the same page. GuestJsp has taken care of synchronization before the JSP file gets called.

login.jsp: Display Guest Book <%@ page language=javascript %>

<head>

<title>Hogwarts Guest Book</title>

</head>

<body bgcolor='white'>

<h1>Hogwarts Guest Book</h1>

<table>

<tr><td width='25%'><em>Name</em><td><em>Comment</em>

<%

var guestBook = application.attribute.guest_book

for (var guest in guestBook) {

out.writeln("<tr><td>" + guest.name + "<td>" + guest.comment);

}

%>

</table>

</body>

Hogwarts Guest Book

Name Comment

Harry Potter Griffindor Rules

Draco Malfoy Slytherin Rules

Guest book logic

The guest book logic is simple. If the user has not logged in, she sees comments and a form to log in. After login, she'll see the comments and a form to add a comment. login.jsp formats the login page and add.jsp formats the add comment page.

GuestJsp stores login information in the session variable.

Form Variable Meaning

action 'login' to login or 'add' to add a comment

name user name

password user password

comment comment for the guest book

Guest book logic ...

// name from the session

String sessionName = session.getValue("name");

// action from the forms

String action = request.getParameter("action");

// name from the login.jsp form

String userName = request.getParameter("name");

// password from the login.jsp form

String password = request.getParameter("password");

// comment from the add.jsp form

String comment = request.getParameter("comment");

// login stores the user in the session

if (action != null && action.equals("login") &&

userName != null &&

password != null && password.equals("quidditch")) {

session.putValue("name", userName);

}

// adds a new guest

if (action != null && action.equals("add") &&

sessionName != null &&

comment != null) {

guestBook.addGuest(sessionName, comment);

}

String template;

// if not logged in, use login.jsp

if (session.getValue("name") == null)

template = "login.jsp";

// if logged in, use add.jsp

else

template = "add.jsp";

RequestDispatcher disp;

disp = app.getRequestDispatcher(template);

...

login.jsp and add.jsp just append different forms to the display code in the previous section.

login.jsp <%@ page language=javascript %>

<head>

<title>Hogwarts Guest Book: Login</title>

</head>

<body bgcolor='white'>

<h1>Hogwarts Guest Book</h1>

<table>

<tr><td width='25%'><em>Name</em><td><em>Comment</em>

<%

var guestBook = application.attribute.guest_book

for (var guest in guestBook) {

out.writeln("<tr><td>" + guest.name + "<td>" + guest.comment);

}

%>

</table>

<hr>

<form action='GuestJsp' method='post'>

<input type=hidden name='action' value='login'>

<table>

<tr><td>Name:<td><input name='Name'>

<tr><td>Password:<td><input name='Password' type='password'>

<tr><td><input type=submit value='Login'>

</table>

</form>

</body>

Conclusion

The Resin demo shows a few ways to extend the guest book, including adding some intelligence to the form processing. However, as forms get more intelligent, even JSP templates become complicated. There is a solution: XTP templates.

--------------------------------------------------------------------------------

Home | Resin | Download | Sales | FAQ | Site Map

Copyright ? 1998-2000 Caucho Technology. All rights reserved.

Last modified: Sat, 11 Mar 2000 20:22:52 -0800 (PST)


JSP 构 架--2种方式

作者：八脚鱼

email: o@test.com

日期：2001-6-19 10:37:35

文章名 JSP 构 架--2种方式：Model I和Model II

作者：Lance Lavandowska   编译：blueski

如果你经常去Servlet或JSP的新闻组或者邮件列表，那么一定会看到不少关于Model I 和Model II 方法的讨论。究竟采用哪一种，这取决于你的个人喜好、团队工作策略以及是否采用正统的OOP。

简单地说，Model I将事务逻辑（business logic）和表示代码（presentation code）融合在一起（如在HTML中）；Model II则提倡最大限度地将所有的代码放到内容表示之外。

[B]Model I： 简单的单层次应用[/B]

如果是在一个人人都精通Java和HTML的环境中，或者你独自做着所有的工作，假如每个人都有清晰的编程结构和思路，那么这种方法会很有效，不过这样的假设不在本文讨论范围之内。这种方法的第一个优点是如果你的应用改变了，你只需维护一个文件。而最大的缺陷是可读性！除非十分小心，否则你的HTML和Java代码会相互混杂，从而难以维护。

在下面这个例子中，我们将增加一个 TimeZone 元素，从而使它变成JSP文件，它会返回基于时间的所期待的TimeZone。如果没有提交 TimeZone，那么缺省的是服务器的缺省时间。

=====================================================================

<xml version="1.0" ?>

<H1>Time JSP</H1>

<jsp:scriptlet>

//the parameter "zone" shall be equal to a number between 0 and 24 (inclusive)

TimeZone timeZone = TimeZone.getDefault(); //returns the default TimeZone for the server

if (request.getParameterValues("zone") != null)

{

String timeZoneArg = request.getParameterValues("zone")[0];

timeZone = TimeZone.getTimeZone("GMT+" + timeZoneArg + ":00");

// gets a TimeZone. For this example we're just going to assume

// its a positive argument, not a negative one.

}

//since we're basing our time from GMT, we'll set our Locale to Brittania, and get a Calendar.

Calendar myCalendar = Calendar.getInstance(timeZone, Locale.UK);

</jsp:scriptlet>

<%= myCalendar.get(Calendar.HOUR_OF_DAY) %>:

<%= myCalendar.get(Calendar.MINUTE) %>:

<%= myCalendar.get(Calendar.SECOND) %>

======================================================================

相应地，数据也可以从JavaBean取得并加以显示。在下一个例子中我们就可以看到。

[B]Model II: 重定向请求（Redirecting Requests）[/B]

在一个团队开发环境中，有些是HTML设计者，另一些则是Java程序员，这时这一方法显得非常重要。Java程序员可以集中精力创建可重用代码，而HTML设计师可以集中精力于内容表示，彼此相对对立，可以分别动态地修改自己的内容，只要总体的输入输出不变。

现在我们可以使用Model II来表示Model I的那个例子。这一方法遵循了Model-View-Controller (MVC) 范例 (cite Design Patterns book)。 在这个例子中，我们只有一个类(页或者servlet) 处理请求(Controller)，取得TimeZone，设置所有用于表示的变量，并将控制传递到表示页(View)。作为如此简单的应用，可以没有 "Model"。

[B]Controller: timeByZone.jsp[/B]

controller可以是一个servlet或一个JSP页。我推荐使用JSP，因为这样我不必担心每当我做修改时要对类重新编译，但是，你将因此失去granularity（颗粒性），以后要扩展该类也比较困难。

======================================================================

<xml version="1.0" ?>

<!--Worker Class, nobody should see me-->

<jsp:scriptlet>

//the parameter "zone" shall be equal to a number between 0 and 24 (inclusive)

TimeZone timeZone = TimeZone.getDefault(); //returns the default TimeZone for the server

if (request.getParameterValues("zone") != null)

{

String timeZoneArg = request.getParameterValues("zone")[0];

timeZone = TimeZone.getTimeZone("GMT+" + timeZoneArg + ":00");

// gets a TimeZone. For this example we're just going to assume

// its a positive argument, not a negative one.

}

TimeBean timeBean = new TimeBean();

timeBean.setHours = myCalendar.get(Calendar.HOUR_OF_DAY);

timeBean.setMinutes = myCalendar.get(Calendar.MINUTE);

timeBean.setSeconds = myCalendar.get(Calendar.SECOND);

HttpSession mySession = request.getSession();

mySession.putValue("tempTimeBean", timeBean);

</jsp:scriptlet>

<jsp:forward page="displayTime.jsp" />

======================================================================

[B]View: displayTime.jsp[/B]

同样地，这个view既可以是一个servlet也可以是一个jsp文件。这里我们从Session中取得并显示它的值。实际上我们会将这做两次，来示范Bean是如何被使用的。

======================================================================

<xml version="1.0" ?>

<H1>Time JSP</H1>

<jsp:useBean class="TimeBean" id="tempTimeBean" scope="session" />

<jsp:getProperty name="tempTimeBean" property="hours">:

<jsp:getProperty name="tempTimeBean" property="minutes">:

<jsp:getProperty name="tempTimeBean" property="seconds">

<!-- these would have printed "null" if tempTimeBean was not instantiated by timeByZone.jsp -->

<jsp:scriptlet>

HttpSession mySession = request.getSession();

TimeBean timeBean = mySession.getValue("tempTimeBean");

if (timeBean != null)

{ // check to make sure its not null, to avoid NullPointerExceptions

out.print(timeBean.getHours());

out.print(":");

out.print(timeBean.getMinutes());

out.print(":");

out.print(timeBean.getSeconds());

}

else

{

out.println("Press your Back button and select a TimeZone");

}

</jsp:scriptlet>

======================================================================

第二种方法（在内部使用了代码）可能有些笨重，但允许开发者确保输出不至于很糟糕（例如"null:null:null null")，假定Session bean还没有被实例化以及没有进行值的设置。 这种情况发生在客户端直接调用了View页。问题是使用脚本scriptlets可以允许更强的控制。如果你确信你可以控制url存取，那么bean方法当然更适合于开发，并使 View页更方便于HTML设计者的协同工作。

上面的是"传统的" Model II设计。所有的变量都包装了并放在Session对象中。这有2个不足：

 1) 如果客户端拒绝参与的话，Session是不可得到的。

 2) 除非Session变量被显式地移走，否则它回一直存在，直到Session被破坏或过期。

第一种案例很可能发生在这样的场合，即使用了cookies作为声明的结构（mechanism）而开发者没有能够提供声明的结构的替代表单（form），即URL改写。

第二个案例甚至更为严重，因为它可能引起很大的内存消耗，如果Sessions被定义为保存比标准存留时间更长的话（(标准存留时间是30分钟）。即使是30分钟的Session，这种Model也可能在大的应用中引起灾难性的内存泄露。为什么呢？在Session对象内部设置的对象被实例化了，并且在Session终止以前一直没有被移去。因为它们仍然有关联references（Session对象）指向它们，所以无法被垃圾收集（garbage-collected）。在Model II 模型中，很多对象被放到Session中（要么直接地，要么通过JavaBean）。随着Session的进行，更多的页被存取，内存使用会增加并持续下去直到客户端终止了Session或者Session过期。要一直等到Session变得非法，放在那的对象才能被垃圾收集，而那些损失的内存本可以用于任何其它的用途。.

改进的方法之一是将Beans或者其它变量放到Request对象中去，并使用RequestDispatcher.include()而不是RequestDispatcher.forward()。这样做以后，View 页具有和Controller一样的存取请求的对象。传统的Model II设计的不足可以被排除。

一个最后的评注：尽管有如上所述，我个人仍有些不喜欢Model II 的范例，如果它用通常方法开发的话。 客户端被引送到某一个地址，然后又被转向到另一个不同的类，我不喜欢创建这样的系统。基于这样的原因，我修改了设计，使它变成了以下的样子：

[B]Controller: timeByZone2.jsp[/B]

和前面一样，controller使用Request值来取得必要的数据，并且将数据放到请求的对象中去。这回的区别是View页将使用RequestDispatcher.include()来调用Controller。在这种方法中，客户端再也不做重定向，请求不是“链接chained”的。相当于class/jsp请求了另一方来为它做一些工作，然后继续。

======================================================================

<xml version="1.0" ?>

<!--Worker Class, nobody should see me-->

<jsp:scriptlet>

//the parameter "zone" shall be equal to a number between 0 and 24 (inclusive)

TimeZone timeZone = TimeZone.getDefault(); //returns the default TimeZone for the server

if (request.getParameterValues("zone") != null)

{

String timeZoneArg = request.getParameterValues("zone")[0];

timeZone = TimeZone.getTimeZone("GMT+" + timeZoneArg + ":00");

// gets a TimeZone. For this example we're just going to assume

// its a positive argument, not a negative one.

}

TimeBean timeBean = new TimeBean();

timeBean.setHours = myCalendar.get(Calendar.HOUR_OF_DAY);

timeBean.setMinutes = myCalendar.get(Calendar.MINUTE);

timeBean.setSeconds = myCalendar.get(Calendar.SECOND);

request.setAttribute("tempTimeBean", timeBean);

</jsp:scriptlet>

======================================================================

[B]View: displayTime2.jsp[/B]

和displayTime.jsp非常相似，但timeByZone2.jsp在也的顶部被调用。请注意 <jsp:useBean /> 中的"scope"已经被换成了"request"。

======================================================================

<xml version="1.0" ?>

<H1>Time JSP</H1>

<jsp:include page="timeByZone2.jsp" />

<jsp:useBean class="TimeBean" id="tempTimeBean" scope="request" />

<jsp:getProperty name="tempTimeBean" property="hours">:

<jsp:getProperty name="tempTimeBean" property="minutes">:

<jsp:getProperty name="tempTimeBean" property="seconds">

<!-- these would have printed "null" if tempTimeBean was not instantiated by timeByZone2.jsp -->

======================================================================

在一个在建系统中，我们已经使用这种方法来创建类的链，每一个都只对它所处理的工作负责。通过辨别公用的表示格式，我们创建了一个View对象，即使在很高层次的JSP中它也可以重复使用。我们的目标就是建立一些可重用的页，同时减少用于表示的类的数量。


JSP 项目设计中的方法论(1)转

作者：运气

email: webmaster@chinaspx.com

日期：6/27/2001 12:23:13 PM

在设计JSP项目的时候，现行的方法学提供的更多是限制而不是帮助。按照Casey Kochmer的观点，成功的运行一个JSP项目的关键是项目管理而不是设计

。

与一般的想法相反，在运行一个项目的时候，最好的设计方法学并不是那种正式的方法。多数设计方法学都是臃肿而不切实际的。如果一种设计方法需要200页的手册才能说明，那只能说明它在实际应用的时候显得太复杂了。我认为，设计方法的本质应该是简单和整体的。实际上，对于一个成功的设计方法，最关键的甚至可以说是与设计无关的东西，而是项目管理策略。如果管理不当，即使你有最好的设计也有可能失败。在设计方法中，最重要的一点是必须提供一个简单的框架，这个框架要能把任何成功设计中广泛存在的对立和矛盾包容在一起。

在下面的指南中，我们将解释这个问题，讲述项目管理中最基本的组成原则。

项目管理原则

有几个主要的因素可以导致项目失败。我们在下面列出最主要的10个，还包含对每个因素的简单解释。

项目过于死板，不能按照用户需要进行必要的改动。

项目毫无原则，经常因用户的意愿进行改变，因而无法在合理的时间内完成。

在编程人员和客户之间缺乏沟通或者沟通很差。

有不切实际的预期目标。

时间表是不切实际的。

项目过大，无法进行成功的管理。

没有测试或者测试过多。

使用错误的工具。

项目使用的技术对于项目和用户来说太过先进，超前。

项目进行不尊重项目成员。

下面的多数原则就是为了解决这些问题而提出的。当然，每个项目都有其自身的平衡点。因此每个项目经理和主程序员都要按照自己项目的内部特色进行调整。

在项目的设计过程中，必须允许用户提出改变设计的要求。但是同时一个项目又要有一定的“刚性”，要使设计的改变尽量少。平衡这个矛盾需要非常好的设计艺术，而且每个项目的平衡点都是不一样的。

在项目进行过程中，团队需要直接与客户沟通，至少也要保证最低限度的项目回顾和问题澄清/分析过程。

一个项目的时间不要超过一年，以6到9个月为最佳。任何更大更长的项目最好切割为小的子项目。

项目经理与程序设计主管一定要是不同的两个人。将者两个角色合一使一个人的负担过大，两个角色都作不好。

一个项目的人数不要超过7个，以5个为最佳。

一个项目小组最好能混合资深的和年轻的开发者

我发现，如果一个开发小组全是资深的开发者，那么小组很容易陷入陈腐和习惯化的情况。而一个完全又年轻的开发者组成的队伍又明显的缺乏经验。团队中的年轻成员可以消除老的资深人员的惰性，年轻的新手可能经常会问，这个为什么要这样作？这种问题经常带来良好的改进。同时，资深的开发者可以训练新手，让他们经常对设计进行检查，这也可以带来改进。 7.项目所使用的工具对项目成员来说必须是容易使用和控制的，或者在这方面能够提供帮助的人必须是容易找到的。

开始的时候就要制定比较现实的时间表。如果时间表在开始后发现是不合理的，就要尽快对人员或者是时间表进行调整。多数项目的错误在于一味的增加资源以加速进度。这通常都是错误的。如果发现一个时间表是不合理的，其错误之处多数不仅仅是缺乏资源。在检查时间表的同时也要检查一下项目目标，方法和选择。确保你在可靠的前提和信息下工作。在完成这种重新审查后，按照自己的想法重新调整项目。

项目中的主要参与者必须感觉舒适，可以自由的提问，自由的进行沟通。缺乏有效沟通的项目通常会迅速失败。出现问题的第一个信号通常就是在交换信息的时候有问题。沉默并不是项目要完成的信号，而是说明你的成员在无法沟通的真空中工作。

项目小组中的所有成员都要明白这些原则，以便经常对项目情况进行检查。如果一个项目不符合这些原则，那么所有的成员都有义务尽快找出问题之所在。我在项目中也经常弄错点什么，但是也尽量将这些错误迅速找出。当问题在爆发前被发现，或者是在项目的初始阶段被发现，通常解决问题的方法也简单。但是，忽略这些问题则经常导致更严重的问题，导致项目失败。如果有项目不能体现这些原则，我是不会接受这种项目的。

这些原则是我为项目成功总结的一些基本点。我个人的经验告诉我忽视上面任何一个原则都很可能导致严重的结果。


JSP 项目设计中的方法论(2)转

作者：运气

email: webmaster@chinaspx.com

日期：6/27/2001 12:23:34 PM

设计原则

虽然在设计上设置过多刚性的原则是有害的，但是这里我们还是提出一些可以遵循的基本原则。

避免成为使用新技术的群体。一定要等到技术和产品的支持信息成熟了再考虑。但是如何判断一个技术是否足够成熟呢？看看在Internet上的支持信息的丰富程度和深度。一个新的工具，只有在你可以很容易地找到其帮助信息的时候才是好的。

如果你不得不使用比较新的技术，记住一定要准备有后备的方案，以便在新技术实施中出现问题的时候使用。我的感觉是新技术在你项目的关键点上有大概50%的机会会出问题。从一开始就准备好后备的方案可以避免日后问题扩大。在使用新技术的时候，一定要在项目的早期使用，以争取更多的时间对其可行性进行评估。

保证用户的技术水平可以使用你的项目中的各种技术，例如如果用户使用的是3.x版本的浏览器，你就不要采用客户端的XML/XSL。当然，你还是可以使用服务器端的XML/XSL，因为用户不会因此受到影响。

以满足最小限度的需求为目的，这样做可以防止项目变的臃肿，同时可以加快程序编写速度，也更容易测试。而用户只应该要求他们真正需要的功能。

在编码的时候，最主要的目标是制作可维护的代码；第二个目标是制作可重用的代码。借助Java，面向对象是我们达到成功的最重要工具。但是也不要完全依赖面向对象技术，借助最简单的模板，函数库或者是良好设计方法也可以很好地对代码进行重用。面向对象只是我们在编程的时候可以选择众多技巧中的一种。

测试和试用是成功的重要部分。试用是非常重要的部分，一定要给以充足的时间以便有机会对发现的错误进行修正。

在主要项目完成后，可以给出一个小的第二阶段，在这个阶段中可以将项目中不够完善，没有完全达到预期水准的部分进行修改。

想想多重项目的概念。一个项目小组经常要面对多个项目。项目人员在不同的项目中，要不断的变换职责，一方面这样的作为为以后的人员使用增加了后备。而且由于每个项目小组成员都不断作新的事情，也减少了人员产生倦怠情绪的可能(这意味着你的项目小组可以长时间保持相对稳定)

预先作好计划，使多个人可能不断对一段代码进行加工。为了作到这一点，我在不同的项目之间进行代码重用。其实，我们做的不仅仅是代码重用。在每个项目中，都可能有个新的人在使用现有的一部分代码。新的人可能会不断的对这些代码进行修正和优化。因此这些代码可以不断的增加效率，同时出现问题的机会也很少。代码的效率可以得到提高，另外文档也可以不断完善。不仅仅是代码本身可以不断被修改，提高，随着新技术的出现，代码也可以不断应用新的技术，从而得到提高。

如果可能，尽量使用公开源码

将JSP的分布式环境变成你的优势。使用客户端脚本来利用客户机的能力。真正依靠数据库的存储进程，将数据处理逻辑集中保存。使用J2EE服务器生成XML和XSL数据模板来产生HTML输出。避免将处理集中在一点，将处理工作分布开来是完成工作最有效的方式。

避免把太多的逻辑放到一个单一的JSP页中。一个JSP页作的事情越多，当你需要升级或者是修改项目的时候，影响就越大。尽量使每个JSP页只完成一个最基本的小操作。也可以使用Tag和JavaBean库的优势来建立可重用的模块。这些手段有助于使JSP页便于维护。


jsp+beans实现读取数据库的例子

作者：asp3000

email: coolknight@263.net

日期：2000-8-7 17:50:30

jsp+beans实现读取数据库的例子

Select.jsp

<html>

<head>

<title>Select everything from a database</title>

</head>

<body>

<jsp:useBean id="select" class="DataBaseSelect" scope="request">

</bean>

<% out.print(select.connect()); %>

<br>

<% out.print(select.select()); %>

<p>Format results

<br>

<%@ page import="java.util.Vector" %>

<% Vector aResult = select.getResult(); %>

<table>

<% for (int i=0; i < aResult.size(); i++) { %>

   <tr>

       <td>

           <% out.print(aResult.elementAt(i)); %>

       </td>

   </tr>

<% } %>

</table>

</body>

</html>

DataBaseSelect.java

import java.sql.*;

import java.util.Vector;

/**

 * DataBaseSelect.java

 * Written by Morgan Catlin

 *   August 19, 1999

 *

 * Variables:

 *   Vector result = where I put the results of a select query

 *

 * Methods:

 *   Vector getResult() = returns result

 *   void setResult() = sets result

 *   String connect() = connects to a database

 *   String select() = selects information from a database

 */

public class DataBaseSelect {

   private Vector result;

   public DataBaseSelect() {

      result = new Vector();

   } // constructor DataBaseSelect

   public String connect() {

      try {

     Class.forName("a jdbc driver").newInstance();

     return "Driver Loaded!";

      } catch (Exception E) {

     return "Unable to load driver.";

      }

   }

   public String select() {

      try {

     Connection C = DriverManager.getConnection("database connection string");

     Statement Stmt = C.createStatement();

     ResultSet myResult = Stmt.executeQuery("SELECT acolumn from atable");

     while (myResult.next()) {

        result.addElement(myResult.getString(1));

     }

         // Clean up

         myResult.close();

         Stmt.close();

         C.close();

     return "Connection Success!";

      } catch (SQLException E) {

     return "SQLException: " + E.getMessage();

      }

   }

   /**

    * Accessor for result

    **/

   public Vector getResult() {

      return result;

   }

   /**

    * Mutator for result

    **/

   public void setResult(Vector avector) {

     result = avector;

   }

} // class DataBaseSelect


Jsp+JavaBean循序渐进教程(六)

作者：运气

email: webmaster@chinaspx.com

日期：2001-7-3 14:36:02

    这一节涉及到两个页面，一个donewuser.jsp文件用来实现记录添加操作，另一个文件listuser.jsp文件用来显示所有的已经注册的用户信息。这两个页面都涉及到了JavaBean的具体调用，还是来看看文件吧，对于文件中关键代码，都会添加上注释，以方便大家理解。

    donewuser.jsp文件

    说明：用户注册操作页面，并根据用户注册成功否显示相应的反馈信息，这个页面主要的特点就是使用了lyf.adduser这个JavaBean的addNewUser()方法来进行记录的添加。

$#@60;%@ page contentType="text/html;charset=gb2312"%$#@62;

$#@60;% response.setHeader("Expires","0"); %$#@62;

$#@60;!--生成一个JavaBean:lyf.adduser的实例，id为adduser，生存范围为page--$#@62;

$#@60;jsp:useBean id="adduser" class="lyf.adduser" scope="page"/$#@62;

    $#@60;!--设置JavaBean中各个属性的值，这会调用JavaBean中各个属性的set方法，以便JavaBean得到正确的属性值，”*”代表进行所有属性的匹配--$#@62;

$#@60;jsp:setProperty name="adduser" property="*"/$#@62;

$#@60;html$#@62;

$#@60;head$#@62;

$#@60;meta http-equiv="Content-Language" content="zh-cn"$#@62;

$#@60;meta http-equiv="Content-Type" content="text/html; charset=gb2312"$#@62;

$#@60;meta name="GENERATOR" content="Microsoft FrontPage 3.0"$#@62;

$#@60;meta name="ProgId" content="FrontPage.Editor.Document"$#@62;

$#@60;title$#@62;用户添加$#@60;/title$#@62;

$#@60;/head$#@62;

$#@60;body bgcolor="#FFEBBD"$#@62;

$#@60;div align="center"$#@62;$#@60;center$#@62;

$#@60;%

    //调用lyf.adduser的checkUser()方法检查是否有重复的用户名

    //如果有重复就显示对应的信息

if(!adduser.checkUser())

{

    //页面文字输出信息，使用jsp内置对象out的println方法，相当于asp中的response.write方法

out.println("对不起，这个用户名"+adduser.getUsername()+"已经被申请了，请重新选择！");

    //return代表返回，运行时候碰到return就不会进行下面的处理了，功能相当于asp中的response.end

return;

}

%$#@62;

$#@60;%

    //如果没有用户名重复的问题，调用lyf.adduser的addNewUser()方法来将用户数据添加到数据库中，并根据数据添加成功否来显示对应的信息

if(adduser.addNewUser()){

%$#@62;

$#@60;H2$#@62;添加用户成功！$#@60;/P$#@62;

$#@60;%}else{%$#@62;

$#@60;H2$#@62;添加用户失败，请和管理员联系！$#@60;/P$#@62;

$#@60;%}%$#@62;

$#@60;/BODY$#@62;

$#@60;/HTML$#@62;

    listuser.jsp文件

    说明：用户信息列表页面，用于显示注册的所有用户信息，对数据进行了分页显示。

    为了方便大家使用，采用了通用的分页代码，如果是JDBC2.0以上或者其他支持TYPE_SCROLL_INSENSITIVE游标的数据库驱动程序，可以有更简洁的分页方法。

    和前面jsp页面类似的语句就不讲解了，$#@60;%@ page import="java.sql.ResultSet" %$#@62;代表在这个页面中导入java.sql.ResultSet的这个类库，因为Jsp页面中间要声明ResultSet；oracle.jdbc.driver.*类库是Oracle专用的Jdbc驱动程序，让Jsp页面可以用来进行Oracle的数据库操作。

$#@60;%@ page contentType="text/html;charset=gb2312"%$#@62;

$#@60;% response.setHeader("Expires","0"); %$#@62;

$#@60;%@ page import="java.sql.ResultSet" %$#@62;

$#@60;%@ page import="oracle.jdbc.driver.*" %$#@62;

$#@60;!--生成一个JavaBean:lyf.db的实例--$#@62;

$#@60;jsp:useBean id="db" class="lyf.db" scope="request"/$#@62;

$#@60;jsp:setProperty name="db" property="*"/$#@62;

$#@60;%

    java.lang.String strSQL; //SQL语句

int intPageSize; //一页显示的记录数

int intRowCount; //记录总数

int intPageCount; //总页数

int intPage; //待显示页码

java.lang.String strPage;

int i,j,k;

    //设置一页显示的记录数

intPageSize = 15;

    //取得待显示页码

strPage = request.getParameter("page");

if(strPage==null){//表明在QueryString中没有page这一个参数，此时显示第一页数据

intPage = 1;

}

else{//将字符串转换成整型

intPage = java.lang.Integer.parseInt(strPage);

if(intPage$#@60;1) intPage = 1;

}

    //获取记录总数

strSQL = "select count(*) from user";

ResultSet result = db.executeQuery(strSQL); //执行SQL语句并取得结果集

result.next(); //记录集刚打开的时候，指针位于第一条记录之前

intRowCount = result.getInt(1);

result.close(); //关闭结果集

    //记算总页数

intPageCount = (intRowCount+intPageSize-1) / intPageSize;

    //调整待显示的页码

if(intPage$#@62;intPageCount) intPage = intPageCount;

strSQL="select * from user order by id desc";

    //执行SQL语句并取得结果集

result = db.executeQuery(strSQL);

    //将记录指针定位到待显示页的第一条记录上

i = (intPage-1) * intPageSize;

for(j=0;j$#@60;i;j++) result.next();

%$#@62;

$#@60;html$#@62;

$#@60;head$#@62;

$#@60;meta http-equiv="Content-Language" content="zh-cn"$#@62;

$#@60;meta http-equiv="Content-Type" content="text/html; charset=gb2312"$#@62;

$#@60;meta name="GENERATOR" content="Microsoft FrontPage 3.0"$#@62;

$#@60;meta name="ProgId" content="FrontPage.Editor.Document"$#@62;

$#@60;title$#@62;用户列表$#@60;/title$#@62;

$#@60;/head$#@62;

$#@60;body bgcolor="#FFEBBD"$#@62;

$#@60;div align="center"$#@62;$#@60;center$#@62;

$#@60;table border="1"

borderColorDark="#FFFFFF" borderColorLight="#000000" cellspacing="0" height="22"

width="100%"$#@62;

$#@60;tr bgcolor="#FFEBAD"$#@62;

$#@60;td height="1" width="691" class="main"$#@62;

第$#@60;%=intPage%$#@62;页 共$#@60;%=intPageCount%$#@62;页

$#@60;a href="listuser.jsp?page=0"$#@62;首页$#@60;/a$#@62;

$#@60;%if(intPage$#@62;1){%$#@62;$#@60;a href="listuser.jsp?page=$#@60;%=intPage-1%$#@62;"$#@62;上一页$#@60;/a$#@62;$#@60;%}%$#@62;

$#@60;%if(intPage$#@60;=1){%$#@62;上一页$#@60;%}%$#@62;

$#@60;%if(intPage$#@60;intPageCount){%$#@62;$#@60;a href="listuser.jsp?page=$#@60;%=intPage+1%$#@62;"$#@62;下一页$#@60;/a$#@62;$#@60;%}%$#@62;

$#@60;%if(intPage$#@62;=intPageCount){%$#@62;下一页$#@60;%}%$#@62;

$#@60;a href="listuser.jsp?page=$#@60;%=intPageCount%$#@62;"$#@62;尾页$#@60;/a$#@62;

第$#@60;input type="text" class="main" name="page" size="3" value="$#@60;%=intPage%$#@62;" tabindex="1"$#@62;页$#@60;input type="submit" class="main" value="go" name="B1" tabindex="2"$#@62;$#@60;class="main"$#@62;

$#@60;/td$#@62;$#@60;/tr$#@62;$#@60;/table$#@62;$#@60;/form$#@62;

$#@60;table border="1" width="100%" cellspacing="0" bordercolorlight="#000000"

bordercolordark="#FFFFFF" class="main"$#@62;

$#@60;tr bgcolor="#FFEBAD"$#@62;

$#@60;td $#@62;

$#@60;div align="left"$#@62;用户名$#@60;/div$#@62;

$#@60;/td$#@62;

$#@60;td $#@62;

$#@60;p align="center"$#@62;Email

$#@60;/td$#@62;

$#@60;td $#@62;

$#@60;p align="center"$#@62;主页

$#@60;/td$#@62;

$#@60;td$#@62;

$#@60;p align="center"$#@62;登记时间

$#@60;/td$#@62;

$#@60;td$#@62;

$#@60;p align="center"$#@62;说明

$#@60;/td$#@62;

$#@60;/tr$#@62;

$#@60;%

    //显示数据

i = 0;

while(i$#@60;intPageSize && result.next()){

%$#@62;

$#@60;tr bgcolor="#FFEBAD"$#@62;

$#@60;td$#@62;

$#@60;div align="left"$#@62;$#@60;%=result.getString("username") %$#@62;$#@60;/div$#@62;$#@60;/td$#@62;

$#@60;td$#@62;$#@60;div align="center"$#@62;$#@60;%=result.getString("email") %$#@62;$#@60;/a$#@62;$#@60;/div$#@62;$#@60;/td$#@62;

$#@60;td$#@62;$#@60;div align="center"$#@62;$#@60;font color="#0000CC"$#@62;$#@60;%=result.getString("homepage") %$#@62;$#@60;/font$#@62;$#@60;/div$#@62;$#@60;/td$#@62;

$#@60;td$#@62;$#@60;div align="center"$#@62;$#@60;font color="#FF6666"$#@62;$#@60;%=result.getDate("regtime") %$#@62;$#@60;/font$#@62;$#@60;/div$#@62;$#@60;/td$#@62;

$#@60;td$#@62;$#@60;div align="center"$#@62;$#@60;font color="#0000FF"$#@62;$#@60;%=result.getString("signs") %$#@62;$#@60;/font$#@62;$#@60;/div$#@62;$#@60;/td$#@62;$#@60;/tr$#@62;

$#@60;%

i++;

}

%$#@62;

$#@60;/table$#@62;

$#@60;% result.close(); //关闭结果集%$#@62;

$#@60;/body$#@62;

$#@60;/html$#@62;

    先运行程序newuser.jsp文件，进行用户注册操作，然后运行listuser.jsp文件，看看是否已经添加到数据库中。对于具体jsp文件和class文件放在什么目录下的问题，请看具体jsp服务器软件的参考，一个最简单的方法就是用Jbuilder4.0直接运行，因为它自带了Tomcat服务器软件。

    好了，到这里Jsp+JavaBean的介绍就基本上结束了，通过上面的学习，大家应该对JavaBean如何应用于JSP程序中有了一个基本的了解，剩下的就是在具体程序中去应用和发挥了。

    后说一点的是，为了方便大家，上面的所有程序代码可以在我的网站“JSP爱好者http://jspbbs.yeah.net


Jsp+JavaBean循序渐进教程(五)

作者：运气

email: webmaster@chinaspx.com

日期：2001-7-3 14:35:11

    newuser.jsp文件

    说明：用户注册页面，有用户名、密码、主页、Email、个人签名等信息，主要用于提供用户输入注册信息，提交Action后的处理页面为donewuse.jsp文件，所有参数将会传递到donewuser.jsp文件进行处理。文件开头$#@60;%@ page contentType="text/html;charset=gb2312"%$#@62;语句表明这个页面使用gb2312中文字符集，如果不声明的的话在中文显示上就出现乱码。$#@60;% response.setHeader("Expires","0"); %$#@62;语句是为了让页面能够自动刷新，这句话不要也没有太大影响。后面的基本上就是html语句了，大家都比较熟悉，所以在这里没有必要浪费篇幅了。

$#@60;%@ page contentType="text/html;charset=gb2312"%$#@62;

$#@60;% response.setHeader("Expires","0"); %$#@62;

$#@60;html$#@62;

$#@60;head$#@62;

$#@60;meta http-equiv="Content-Type" content="text/html; charset=gb2312"$#@62;

$#@60;meta name="GENERATOR" content="Microsoft FrontPage 4.0"$#@62;

$#@60;meta name="ProgId" content="FrontPage.Editor.Document"$#@62;

$#@60;title$#@62;新用户注册$#@60;/title$#@62;

$#@60;/head$#@62;

$#@60;body bgcolor="#FFFAD9"$#@62;

$#@60;p align="center"$#@62;$#@60;font color="#8484FF"$#@62;$#@60;span class="main1"$#@62;$#@60;strong$#@62;$#@60;big$#@62;新个人用户注册$#@60;/big$#@62;$#@60;/strong$#@62;$#@60;/span$#@62;$#@60;/font$#@62;$#@60;/p$#@62;

$#@60;span class="main1"$#@62;

$#@60;form method="POST" name="formreg" action="donewuser.jsp"$#@62;

$#@60;div align="center"$#@62;$#@60;center$#@62;$#@60;table

border="1" width="50%" borderColorDark="#FFFFFF" borderColorLight="#000000"

cellspacing="0"$#@62;

$#@60;tr$#@62;

$#@60;td width="27%" bgcolor="#DDDDFF" align="center"$#@62;$#@60;span class="main1"$#@62;用户名：$#@60;/td$#@62;

$#@60;td width="73%" bgcolor="#DDDDFF"$#@62;$#@60;span class="main1"$#@62;$#@60;input type="text" name="username" size="20"

tabindex="2"$#@62;$#@60;font color="#FF0000"$#@62;$#@60;b$#@62;*$#@60;/b$#@62;$#@60;/font$#@62;$#@60;/span$#@62;$#@60;/td$#@62;

$#@60;/tr$#@62;

$#@60;tr$#@62;

$#@60;td width="27%" bgcolor="#DDDDFF" align="center"$#@62;$#@60;span class="main1"$#@62;输入密码：$#@60;/span$#@62;$#@60;/td$#@62;

$#@60;td width="73%" bgcolor="#DDDDFF"$#@62;$#@60;input type="password" name="password" size="20"

tabindex="2"$#@62;$#@60;font color="#FF0000"$#@62;$#@60;b$#@62;*$#@60;/b$#@62;$#@60;/font$#@62;$#@60;/td$#@62;

$#@60;/tr$#@62;

$#@60;tr$#@62;

$#@60;td width="27%" bgcolor="#DDDDFF" align="center"$#@62;$#@60;span class="main1"$#@62;校验密码：$#@60;/span$#@62;$#@60;/td$#@62;

$#@60;td width="73%" bgcolor="#DDDDFF"$#@62;$#@60;input type="password" name="Password1" size="20"

tabindex="3"$#@62;$#@60;font color="#FF0000"$#@62;$#@60;b$#@62;*$#@60;/b$#@62;$#@60;/font$#@62;$#@60;/td$#@62;

$#@60;/tr$#@62;

$#@60;tr$#@62;

$#@60;td width="27%" bgcolor="#DDDDFF" align="center"$#@62;$#@60;span class="main1"$#@62;E-mail$#@60;/td$#@62;

$#@60;td width="73%" bgcolor="#DDDDFF"$#@62;$#@60;input type="text" name="email" size="20" tabindex="4"$#@62;$#@60;font

color="#FF0000"$#@62;$#@60;b$#@62;*$#@60;/b$#@62;$#@60;/font$#@62;$#@60;/td$#@62;

$#@60;/tr$#@62;

$#@60;tr$#@62;

$#@60;td width="27%" bgcolor="#DDDDFF" align="center"$#@62;$#@60;span class

="main1"$#@62;主页地址：$#@60;/span$#@62;$#@60;/td$#@62;

$#@60;td width="73%" bgcolor="#DDDDFF"$#@62;$#@60;input type="text" name="homepage" size="20"

tabindex="6" value="http://"$#@62;$#@60;/td$#@62;

$#@60;/tr$#@62;

$#@60;tr$#@62;

$#@60;td width="100%" bgcolor="#DDDDFF" colspan="2"$#@62;

$#@60;BR$#@62;

$#@60;center$#@62;$#@60;span class="main1"$#@62;$#@60;Font color="red"$#@62;$#@60;B$#@62;介绍自己：$#@60;/B$#@62;$#@60;/font$#@62;$#@60;span class="main"$#@62;（介绍自己，不能超过120字）$#@60;/center$#@62;$#@60;/span$#@62;$#@60;/span$#@62;$#@60;/td$#@62;

$#@60;/tr$#@62;

$#@60;tr$#@62;

$#@60;td width="100%" bgcolor="#DDDDFF" colspan="2"$#@62; $#@60;textarea rows="6"

name="signs" cols="66" tabindex="9"$#@62;$#@60;/textarea$#@62;$#@60;/td$#@62;

$#@60;/tr$#@62;

$#@60;tr$#@62;

$#@60;td width="30%" bgcolor="#DDDDFF" colspan="2"$#@62;$#@60;div align="center"$#@62;$#@60;center$#@62;$#@60;p$#@62;$#@60;input

type="submit" value="递交" onclick="return checkmsg();" name="B1" tabindex="10"$#@62; $#@60;input type="reset"

value="清除" name="B2" tabindex="11"$#@62;$#@60;/td$#@62;

$#@60;/tr$#@62;

$#@60;/table$#@62;

$#@60;/center$#@62;$#@60;/div$#@62;

$#@60;/form$#@62;

$#@60;hr size="1" color="#FF0000"$#@62;

$#@60;span class="main"$#@62;

$#@60;p align="center"$#@62;Better View:800*600 Best View:1024x768

为了本系统能够更好的为您服务，请使用IE4.0或以上版本浏览器$#@60;br$#@62;

$#@60;font color="#000000"$#@62;$#@60;a href="javascript:%20newGuide("copyright.htm")" target="_self"$#@62;版权所有$#@60;/a$#@62;$#@60;/font$#@62;

$#@60;/span$#@62;$#@60;/span$#@62;

$#@60;span class="main"$#@62;

$#@60;a href="http://jspbbs.yeah.net"target="_blank"$#@62;JSP爱好者$#@60;/a$#@62;$#@60;span class="main1"$#@62;

$#@60;/span$#@62;$#@60;/span$#@62;$#@60;span class="main1"$#@62;($#@60;a href="mailto:coolknight@263.net"$#@62;刘玉锋$#@60;/a$#@62;)$#@60;span class="main"$#@62;制作 Copyright&copy; $#@60;font face="Arial"$#@62;2000 $#@60;/font$#@62;$#@60;/p$#@62;

$#@60;/body$#@62;

$#@60;/html$#@62;

    注册页面做完了，看看后面怎么对这个页面的数据进


JSP9种基本内置组件（可与ASP的6种内部组件相对应）（转）

作者：asp2001

email: asp2001@163.net

日期：2000-8-11 19:19:47

基本组件

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>


JSPServlet 中的汉字编码问题(转)

作者：zhang_yu

日期：2001-4-18 13:02:40

. 问题的起源

    每个国家（或区域）都规定了计算机信息交换用的字符编码集，如美国的扩展 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 汉字字符集及 Encoding

   其实解决 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 的支持，这是现阶段和将来汉化的工作内容。

Unicode 的介绍......就免了吧。

JAVA 支持的encoding中与中文编程相关的有：(有几个在JDK文档中未列出) ASCII 7-bit, 同 ascii7

ISO8859-1 8-bit, 同 8859_1,ISO-8859-1,ISO_8859-1,latin1...

GB2312-80 同gb2312,gb2312-1980,EUC_CN,euccn,1381,Cp1381, 1383, Cp1383, ISO2022CN,ISO2022CN_GB......

GBK (注意大小写),同MS936

UTF8 UTF-8

GB18030 (现在只有IBM JDK1.3.?有支持), 同Cp1392,1392

   JAVA 语言采用Unicode处理字符. 但从另一个角度来说，在java程序中也可以采用非Unicode的转码，重要的是保证程序入口和出口的汉字信息不失真。如完全采用ISO-8859-1来处理汉字也能达到正确的结果。网络上流行的许多解决方法，都属于这种类型。为了不致引起混淆，本文不对这种方法作讨论。

3.  中文转码时'?'、乱码的由来

两个方向转换都有可能得到错误的结果：

Unicode-->Byte,  如果目标代码集不存在对应的代码，则得到的结果是0x3f.

如：

"\u00d6\u00ec\u00e9\u0046\u00bb\u00f9".getBytes("GBK") 的结果是 "?ìéF?ù", Hex 值是3fa8aca8a6463fa8b4.

仔细看一下上面的结果，你会发现\u00ec被转换为0xa8ac, \u00e9被转换为\xa8a6... 它的实际有效位变长了！ 这是因为GB2312符号区中的一些符号被映射到一些公共的符号编码，由于这些符号出现在ISO-8859-1或其它一些SBCS字符集中，故它们在Unicode中编码比较靠前，有一些其有效位只有8位，和汉字的编码重叠(其实这种映射只是编码的映射，在显示时仔细不是一样的。Unicode 中的符号是单字节宽，汉字中的符号是双字节宽) . 在Unicode\u00a0--\u00ff 之间这样的符号有20个。了解这个特征非常重要！由此就不难理解为什么JAVA编程中，汉字编码的错误结果中常常会出现一些乱码(其实是符号字符), 而不全是'?'字符, 就比如上面的例子。

Byte-->Unicode,  如果Byte标识的字符在源代码集不存在，则得到的结果是0xfffd.

如：

Byte ba[] = {(byte)0x81,(byte)0x40,(byte)0xb0,(byte)0xa1}; new String(ba,"gb2312");

结果是"?啊", hex 值是"\ufffd\u554a". 0x8140 是GBK字符，按GB2312转换表没有对应的值，取\ufffd. (请注意：在显示该uniCode时，因为没有对应的本地字符，所以也适用上一种情况，显示为一个"?".)

实际编程中，JSP/Servlet 程序得到错误的汉字信息，往往是这两个过程的叠加，有时甚至是两个过程叠加后反复作用的结果.

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

4.1 常见的 encoding 问题的现象

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

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

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

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

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

JSP 页面中内嵌在<%...%>,<%=...%>等Tag包含的 JAVA code 中的中文成了乱码，但页面的其它汉字是对的。

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

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

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

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

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

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

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

Java 需要被编译为 .class 才能在 JVM 中执行，这个过程存在与a.同样的 file.encoding 问题。从这里开始 servlet 和 jsp 的运行就类似了，只不过 Servlet 的编译不是自动进行的。对于JSP程序, 对产生的JAVA 中间文件的编译是自动进行的(在程序中直接调用sun.tools.javac.Main类). 因此如果在这一步出现问题的话, 也要检查encoding和OS的语言环境，或者将内嵌在JSP JAVA Code 中的静态汉字转为 Unicode, 要么静态文本输出不要放在 JAVA code 中。 对于Servlet, javac 编译时手工指定-encoding 参数就可以了。

Servlet 需要将 HTML 页面内容转换为 browser 可接受的 encoding 内容发送出去。依赖于各 JAVA App Server 的实现方式，有的将查询 Browser 的 accept-charset 和 accept-language 参数或以其它猜的方式确定 encoding 值，有的则不管。因此采用固定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 应当能够确保在这个阶段将嵌入的汉字正确传送出去。

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

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

4.3 IBM Websphere Application Server 中的解决方法

WebSphere Application Server 对标准的 Servlet API 2.x 作了扩展，提供较好的多语言支持。运行在中文的操作系统中，可以不作任何设置就可以很好地处理汉字。下面的说明只是对WAS是运行在英文的系统中，或者需要有GBK支持时有效。

上述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。

上面列出的问题中还有一个关于Tag<%...%>,<%=...%>中的 JAVA 代码里包含的静态文本未能正确显示的问题，在WAS中的解决方法是除了设置正确的file.encoding, 还需要以相同方法设置-Duser.language=zh -Duser.region=CN。这与JAVA locale的设置有关。

4.4 数据库读写时的 encoding 问题

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

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

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

4.5 定位问题时常用的技巧

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

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

5. 结束语

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


jspservlet中文问题的解决（转贴，好东西）

作者：bigeagle

email: bigeagle@163.net

日期：2001-7-12 10:44:53

JSP/Servlet 中的汉字编码问题

（作者：张建芳，转自IBM DeveloperWorks 中国网站　2001年04月18日 15:08）

    网上就 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 编程，中文信息处理中的问题还是会存在一段时间的。

5.参考文章

1) Character Problem Review

2) Java 编程技术中汉字问题的分析及解决

3) NLS Characters in WebSphere: SBCS/DBCS display on same page

4) GB18030

5) Setting language encoding in web applications: Websphere applications Server

作者简介

    张建芳，软件工程师，毕业于北京理工大学计算机应用学院，有多年中文本地化经验。您可通过 jfzhang@usa.net 与他联系。

转自IBM


JSP标签库介绍 （1）转

作者：zhang_yu

日期：2001-2-27 14:59:54

作者: Casey Kochmer / 翻译：廖铮

日期: 2001年02月27日

<!—还不太了解Java Servlet Page的强大功能？那好，不妨跟我一道看看JSP最令人瞩目的强大工具之一：JSP标签库。-->

笔者使用JSP和ASP从事编程工作也有很长一段时间了，我越来越觉得，在这两种服务器端编程解决方案中，JSP的功能要比ASP强大得多。我为什么要把JSP选作自己主要的服务器端应用程序编写工具呢？当然，JSP迷人的特性和功能不少，但标签库是让我做出这一决定的最重要诱因之一。

为什么这样说呢？原因有两个：维护和开发速度。服务器端脚本语言其实就像是开发Internet的熔炉。在一个服务器页面上，你可以很方便地混合各种不同的脚本方法和对象。这种页面简直就是建筑Web的混凝土。正是这种“材料”的混合给予了服务器端脚本强大的信息处理能力。它可以让服务器端程序设计人员开发出动态的、灵活的Web页面。但是，另一方面，脚本的自由混合却很难得到维护，特别是随着项目规模的不断增长而显得尤其严重。我们将不得不让有经验的程序员来建立和维护最终产品，结果让这些程序员成为了一般的Web设计人员。服务器端应用程序在最终的图形设计和实现这两方面上就弱化了。更糟糕的是，因为这些页面对代码而言变得很复杂，开发速度也就相应降低了。最后，许多中等规模或者大型的服务器端Web应用程序很晚才得以推出而且成本也无法控制。此外，在实现了这些应用程序之后，许多销售商还必须去寻找合格的程序员来维护这种乱得如同一盆意大利面条似的代码。

我们谁都不愿意看到这样的结果，所以，为了克服这个问题，ASP引进了COM对象技术，而JSP则提供了J2EE作为对策。这些解决方案都是建立在集中的、可重用代码库的机制之上。但是，他们使用起来可就太难了，耗费的时间也很多。还有，这些解决方案并没有减少建立混乱代码的诱惑，结果，我们只能组织起大型的、内部结构良好的开发团队来使用这些技术。这就意味着，尽管我们目前都已经有了建立大型项目的以上方法，中等规模的项目却没有被这些技术所光顾。

可现实是，中等规模的项目才是Web应用程序中的主体部分。所以，许多项目不得不面对其开发和维护环境不能满足其需要的局面。

幸好，JSP为我们提供了解决这一问题的最佳方案，标签库（Tag libraries）提供了建立可重用代码块的简单方式。但是和COM和J2EE不同的是，你不需要掌握任何额外的技能就可以建立标签库：如果你会编写JSP页面，你就会建立标签库。最后，标签库还改进了Web应用程序的维护性。这种对维护性的改进表现在：轻易地在JSP页面上就实现了基于XML的可定制接口。结果可想而知，Web设计人员可以建立JSP Web应用程序而无需知道JSP是怎么回事。这样一来，Web开发就成为一项非常富有效率的团队开发任务了。JSP程序员可以建立定制的标签和后端代码模块，而Web设计人员则可以使用定制标签并且全力关注于Web设计本身。标签库解决了代码混乱的问题，而且做得干净漂亮（事实上，XML才是解决这些问题的本质所在，但是标签库还是起到了相当关键的作用）。


JSP标签库介绍 （2）转

作者：zhang_yu

日期：2001-2-27 15:03:39

什么是标签库？

JSP标签库（也被称作定制标签）是一种通过JavaBean生成基于XML的脚本的方法。从概念上将，标签就是很简单而且可重用的代码结构。比方说，在我们最新发布的JSPKit（在JSP Insider内）中，我们就使用XML标签实现了对XML文档的轻松访问。请看以下的清单A。

清单A：执行XML/XSL 转换的示例标签及其所在的HTML页面

<%@ taglib uri="http://www.jspinsider.com/jspkit/JAXP"prefix="JAXP"%>

  <JAXP:TransformerTag>

  <JAXP:XMLFile>c:/xml/example.xml</JAXP:XMLFile>

  <JAXP:XSLFile>c:/xml/example.xsl</JAXP:XSLFile>

</JAXP:TransformerTag>

以上的示例使用了简单的标签来访问处在幕后的更强大代码。在以上的事例中首先装载了一个XML文件，然后应用了一个XSL文件来创建一个将被发送到客户端的结果——这一切不过仅仅只是用了一个很简单的标签。

定制标签使得JSP项目中很容易创建重用的开放源代码模块。而你的全部需要不过就是标签库及其文档。标签库的重要特性如下所示：

易于安装在多个项目上

标签很容易从一个JSP项目迁移到其他项目。一旦建立了一个标签库，你只需要把这个个标签库包装成一个JAR文件就可以在其他JSP项目中重新使用了。不能重用的是你作为程序员在建立标签时所加进标签的内容。因为标签可以重新使用，所以标签库可以轻松地用于你自己的项目。目前，最好的标签资源可以在JSPTags.com这个站点找到。.

扩展JSP

标签库可以具备JSP规范(JSP 1.2)中的任何特性和功能。这也意味着你拥有了无限的能力可以扩展和增加JSP的强大功能却无需等待新版本JSP的发布。所以说，你完全可以取消页面上的JSP include调用了——只需用include标签建立自己的规范就可以了。

易于维护

标签库使得JSP Web应用程序变得很容易维护。主要有以下几个原因：

标签对任何人而言都很容易使用、易于理解。

你的所有逻辑都驻留在处于中心的标签处理器和JavaBean内。这样一来，如果你不得不更新你的代码，你只需要处理这些中心文件而无需修正使用这些代码的其他页面。

如果你需要增加新的功能，你不必改变任何已经存在的页面。你可以把额外的属性包含到你的标签内从而引进新的行为同时保留以前的属性，实现旧页面的正常运行。

比方说，你可能有如下的一个标签，它让你的文本都显示蓝色：

<BlueText>My Text</BlueText>

但是随着项目的进行，你又想让蓝色变暗。于是你保留自己的标签但是为其增加了一个新的属性：shade

<BlueText shade="teal">My Text</BlueText>

所有的老标签继续显示蓝色，但现在你可以使用同一标签来产生变暗的蓝色文本了。

标签提升了代码的重用性。那些经过多次测试和使用的代码肯定具有更少的bug。所以，使用定制标签的JSP页面也同样具有更少的缺陷，维护起来自然方便多了。

更快的开发速度

标签库是一种重用代码的好办法。我们知道，服务器端语言标准的重用代码方式是使用模版。标签库和模版库这种方式相比则好得多。采用模版库，你就需要针对每个项目修改模版或者建立生硬的接口。标签库则没有这些限制，而其所具有的面向对象特性则让标签库不仅用法灵活而且扩展能力极为强大。还有，因为你重用代码，结果在项目开发上花费的时间就大大降低了，而更多的时间则可以用来设计自己的Web应用程序。标签库的简单接口使得这些代码用法简单、易于调试。

虽然标签库用起来特别简单，但是，建立其内部支持层次可比建立简单的JavaBean复杂多了。这种复杂性的主要原因在于标签库包含了好几个部分。而你所需要掌握的技能不过是理解、熟悉Java和JSP。


JSP标签库介绍 （3）转

作者：zhang_yu

日期：2001-2-27 15:04:49

定制标签内幕

一个简单的定制标签包括了以下几个要素：

JavaBean：为了充分利用Java的面向对象特性，重用代码应当放置在独立的代码容器内。这些JavaBean可不是标签库的一部分。他们是标签库用来执行所分配任务的基础代码模块。

标签处理器：这是标签库的真正核心。标签处理器（tag handler）引用它所需要的任何外部材料（JavaBean）并且负责访问JSP页面的信息（PageContext对象）。而JSP页面则把页面上设置的标签属性都传递给标签处理器，JSP页面上的标签标示的内容也是这样处理的。当标签处理器完成其处理过程，它就会把处理后的输出结果回送给JSP页面做进一步处理。

标签库描述符（TLD文件）：这是一种很简单的XML文件，TLD文件描述和说明了属性、信息和标签处理器文件位置等信息。JSP 容器使用这一文件来映射被调用标签库的位置和用法。

Web站点的web.xml文件：其实这就是你的Web站点上的初始化文件。在这个文件内你可以定义Web应用程序中使用的定制标签，还可以定义用来描述每个定制标签的TLD文件。

发布文件（WAR或JAR文件）：如果你打算重用定制标签，那么你肯定需要想个简便的办法把标签从一个项目迁移到其他项目中去。把标签库打包成一个JAR文件就是这种既方便又高效的标签库发布方式。我们在以上的事例中没有建立JAR文件，但是如果你打算更进一步地了解JAR文件的细节，你不妨阅读“JSP WAR文件简介”。

JSP页面上的标签库声明：仅需使用标签库标示符即可方便地声明页面上存在的标签。标签库在页面上声明之后，你就可以随意使用它们了。

看起来会让你忙乎一阵子，实际上没那么糟糕。刚开始用的时候当然会有点棘手。其实真正关键的并不是编码而是把以上各个部分正确地组织起来。这种层次性的结构是很重要的，也正是如此才可以说标签库及灵活又易于使用。更重要的事，这些层次可以让整个建立标签库的过程都能通过JSP IDE自动完成。今后的JSP IDE更可以自动完成创建定制标签的大部分工作，而你自己则只需要负责建立代码和标签处理器。

（注意：一个标签处理器只定义一个定制标签；而一个标签库则是很多个标签处理器的集合，它们执行同一任务。）


JSP标签库介绍 （4）转

作者：zhang_yu

日期：2001-2-27 15:07:06

建立你的第一个标签

以下的进阶指南将介绍如何把JSP扩展为具有自己的HTML编码功能。这种编码方法用<和 >符号代替HTML代码，而且很容易经过扩展以执行其他编码操作。整个示例虽然简单，但它确实向你说明了定制标签的最基础要素。

创建JavaBean

代码中的任何可重用部分都应该包含在JavaBean内。这一点是很重要的。这样你就可以在其他项目中重用这些代码了。由于任何放置在标签处理器内的代码都不会在标签以外被重用，所以代码的重用部分要与之分离。在我们的示例中，HTML编码逻辑很一般，所以我们直接把他们放在了JavaBean内，请参看清单B。

创建标签处理器

标签处理器使用了清单C的代码。

这些代码很简单，其中包括：

读取包含在打开和关闭标签内的文本。

调用HTML编码函数。

将结果返回给JSP页面。

创建标签描述符

我们需要描述自己的标签以便系统知道他们到底具有什么含义。TLD文件通常就以标签处理器命名并被存放在"/WEB-INF/"目录之下。该文件的文件扩展名是.tld。请参看清单D。

更新Web XML文件

现在你就可以告诉JSP容器使用标签库了。为此你需要修改web.xml文件：增加一个taglib项以注册标签库并为标签分配一个URI。URI是Web站点上唯一应用于这一特定标签的索引。由于标签今后还可能用在不同的Web站点上，所以你最好采用完整的URL和/或者包名字（package name）来保证这一唯一性。示例代码请参看清单E。


JSP传递参数方法

作者：whitefox

email: whitefox.jiang@corp.elong.com

日期：2000-7-5 23:14:28

<jsp:forward page="url">

        <jsp:param name="parametername" value="parametervalue"/>

</jsp:forward>

or

response.sendRedirect("url?name=value.....")


JSP的login程序代码

作者：jspfuns

日期：2000-8-10 17:37:25

<!-- Login Manager -->

<JAVA TYPE="class">

public static boolean checkUserPermission(HttpSession mySession, HttpServletRequest request, HttpServletResponse response)

{

if (mySession.getValue("hasPermission") == null || !((Boolean) mySession.getValue("hasPermission")).booleanValue())

{

String requestedUrl = HttpUtils.getRequestURL(request).toString();

String queryString = request.getQueryString();

if (queryString != null)

{

requestedUrl = requestedUrl + "?" + queryString;

}

requestedUrl = response.encodeUrl(requestedUrl);

mySession.putValue("requestedUrl", requestedUrl);

return false;

}

else

{

return true;

}

}

</JAVA>

<%

// THIS IS A JSP FILE

String goodUser = "bill";

String goodPass = "bobo";

HttpSession mySession = request.getSession(true);

String errorMessage = "Please Login To Access The Page You Requested";

boolean loginAttempt = false;

String myMethod = request.getMethod();

if (request.getParameterValues("click") != null && request.getParameterValues("click")[0].trim().equals("Log In"))

{

loginAttempt = true;

}

if (loginAttempt)

{

String username = request.getParameterValues("user")[0].trim();

String password = request.getParameterValues("pass")[0].trim();

//out.println("username = |" + username + "| & password = |" + password + "|<BR>");

if (goodUser.equals(username) && goodPass.equals(password))

{

response.sendRedirect((String)mySession.getValue("requestedUrl"));

mySession.putValue("hasPermission", new Boolean(true));

errorMessage = "Unable to redirect: " + (String)mySession.getValue("requestedUrl");

}

else

{

errorMessage = "You did not get the Username or Password right";

}

}

else

{

errorMessage = "Haven't tried logging in yet.";

if (mySession.getValue("requestedUrl") == null)

{

mySession.putValue("requestedUrl", "/index.jsp");

}

//out.println("set userReferrer to " + mySession.getValue("redirectTo") + "<BR>");

}

%>

<CENTER>

<font color=red><%=errorMessage%></font>

<TABLE align=center>

<FORM action="adminLogin2.jsp" method=post name="login">

<TR><TD>Username:</TD><TD><input type=text name=user value=""></TD></TR>

<TR><TD>Password:</TD><TD><input type=password name=pass value=""></TD></TR>

<TR><TD colspan=2 align=center><input type=submit name=click value="Log In"></TD></TR>

</FORM>

</TABLE>

</CENTER>

Original URL: <%= (String)mySession.getValue("requestedUrl") %>

<script language="javascript">

// <!--

if (document.forms.login != null)

document.forms.login.user.focus();

// -->

</script>


JSP的mysql_jdbc驱动程序

作者：zergman

email: zergman@chinaasp.com

日期：6/12/2001 1:16:41 PM

作者：jjx,转载请指明出处 如有不当之处，敬请指出

mm.mysql是个类型4(即纯java),符合jdbc 2规范的mysql jdbc驱动程序。当前版本为2.0 pre 5(beta)

你可以从http://www.worldserver.com/mm.mysql/

本文只是简单描述一下mm.mysql的安装和使用

安装

1、下载mm.mysql

2、解压到某驱动器，如c:

3、修改classpath,加入mm.mysql的路径，如你原来的classpath可能是:

.;c:\jdk1.2.2\lib\tools.jar;c:\jdk1.2.2\lib\dt.jar;

修改后可能是

.;c:\jdk1.2.2\lib\tools.jar;c:\jdk1.2.2\lib\dt.jar;C:\mm.mysql.jdbc-2.0pre5;

编程

1、用DriverManager登记mm.mysql

mm.mysql的class名为org.gjt.mm.mysql.Driver,登记时必须写成

Class.forName("org.gjt.mm.mysql.Driver").newInstance();

2、jdbc url参数说明

url格式：jdbc:mysql://[hostname][:port]/dbname[?param1=value1][&para;m2=value2]...

参数名 取值 缺省

user 数据库用户名 无

password 数据库用户口令 无

autoReconnect 当数据库连接丢失时是否自动连接，取值true/false false

maxReconnects 如果autoReconnect为true,此参数为重试次数，缺省为3次 3

initialTimeout 如果autoReconnect为true,此参数为重新连接前等待的秒数 2

maxRows 设置查询时返回的行数，0表示全部 0

useUnicode 是否使用unicode输出，true/false false

characterEncoding 如果useUnicode,该参数制定encoding类型，建议使用8859_1 无

提示

同时使用useUnicode,characterEncoding,能解决数据库输出时的中文问题

如:jdbc:mysql://localhost/test?user=root&useUnicode=true;characterEncoding=8859_1

实际例子，举一个简单的例子

这个jsp例子使用一个库，该库只有一个表（地址簿)

create database addressbook

use addressbook

create table addressbook (id int auto_increment primary key,name varchar(30),address varchar(255),phone varchar(20));

插入记录

insert into addressbook (name,address,phone) values ('jjx','zhejiang yuyao','0574-2222222');

jsp代码：代码中使用了absolute,ResultSet.TYPE_SCROOL_SENSITIVE等的目录是为了测试mm.mysql是否符合jdbc 2.0的规范

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

<%

out.println("通讯录！");

try {

Class.forName("org.gjt.mm.mysql.Driver").newInstance();

}

catch (Exception E) {

out.println("Unable to load driver.");

}

try {

Connection C = DriverManager.getConnection("jdbc:mysql://localhost/addressbook?user=root&password=jjx&useUnicode=true&characterEncoding=8859_1");

Statement s=C.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_READ_ONLY);

ResultSet rs=s.executeQuery("select * from addressbook");

out.println("<table border=1>");

int i=1;

for (i=10;i<20;i++)

{

if (rs.absolute(i))

{

out.println("<tr><td>");

out.println(rs.getString(1));

out.println("</td>");

out.println("<td>");

out.print(rs.getString(2));

out.println("</td>");

out.println("<td>");

out.print(rs.getString(3));

out.println("</td></tr>");

}

else

{

break;

}

}

out.println("</table>");

rs.close();

s.close();

C.close();

}

catch (SQLException E) {

out.println("SQLException: " + E.getMessage());

out.println("SQLState: " + E.getSQLState());

out.println("VendorError: " + E.getErrorCode());

}

%>


JSP的内部对象--JSP开发入门四

作者：jeru

日期：2000-12-20 11:31:04

最后一个与JSP语法有关的组件叫做内部对象.在JSP小型指令文件内，你可以存取这些内部对象来与执行JSP网页的servlet环境相互作用。许多对内部对象的存取应该要简化。然而，这些是范例，它们的存取都是可接受的，要完整的利用内部对象设定则需要对最新的Java Servlet API有所了解。

    下表列出你可以使用的内部对象。

    内部对象说明

    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); %>。

    当你以JSP写了许多的应用程序之后，如果你建立了JavaBeans或者发现你自己将太多的Java原始码放入你的JSP档案，你需要建立支持的Java类别，这样可以鼓励重复使用并且降低JSP网页转换时所需要的时间。当你需要建立Java类别时，你必须:

    将JDSWK的安装目录\bin目录加到你的PATH。在你的autoexec.bat档案的PATH行的最后，加入C:\1.2.2\bin; 。

    以下面的指令将JAR档案复制到\jre\lib\ext目录:

    copy c:\jswdk-1.0.1\lib\servlet.jar c:\jdk1.2.2\jre\lib\ext.


jsp调用oracle的存储过程

作者：zergman

email: zergman@chinaasp.com

日期：6/12/2001 1:23:23 PM

jsp调用oracle的存储过程

http://www.netqu.com中华技术网会员 Wuxuehui 发布

假定存储过程名字为 sp_yourprocedurename，有两个参数

CallableStatement cstmt = con.prepareCall("{call sp_yourprocedurename(?,?}");

cstmt.registerOutparameter(2,java.sql.Types.FLOAT);

cstmt.setint(1,parameter1);

cstmt.execute();

out.println("Procedure output is :"+cstmt.getFloat(2));


jsp发送邮件实例（高手必读）

作者：blackwhites

日期：2000-10-19 15:49:47

 看看我从老外那骗到的

vishal_donth gave this response on 10/18/2000:

//these are the pakages to be imported from

// Java Mail

//The Java Mail PAckage either be dowloaded

//seperately

//or else is Available in the J2sdkee1.2

// (Java Enterprise Edition)

import javax.mail.*;

import javax.mail.internet.*;

import java.util.*;

//This function can be used to send the mail

// with the parameters given to it

//U have to specify the smtp server through

//which u have to send the mail

//since i was trying with a homenetmail

//account i directly sent the mail its server

//For sending this mail u need a mail server

//which lets u to relay the messages

//Try this thing for sending to a

//http://www.homenetmail.comaccount because it lets

//u send

//mails to the accounts like example try

//sending it to a "abc@homenetmail.com"

//account.Create the mail account in homenet

//mail first. If u get any other server which

//supports relaying u can try this on that

//also.

//Use this function in ur Servlet to send

//mail by calling the function with the

//parameters

public void sendMail(String toAddr, String subject, String body, String fromAddr)throws RemoteException{

try{

Properties props = new Properties();

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

//Here we specify the SMTP server through

//which the mail should be delivered

Session session = Session.getDefaultInstance(props, null);

Message msg = new MimeMessage(session);

msg.setFrom(new InternetAddress(fromAddr));

//Specify the From Address

InternetAddress[] tos =InternetAddress.parse(toAddr);

//Specify the To Address

msg.setRecipients(Message.RecipientType.TO,tos);

msg.setSubject(subject);

//Specify the Subject

msg.setText(body);

//Specify the Body

Transport.send(msg);

System.out.println("Message is Sent");

}

catch(Exception e){

System.out.println(e);

}

}

// U have to run this function on a computer

//which is directly connected

// to internet but not through a

//proxy......or else use a proxy which

//supports SMTP


JSP高级技术如何开发动态网站

作者：jspfuns

日期：2000-8-10 17:23:22

近年来，Jsp技术现在已经成为一种卓越的动态网站开发技术。Java开发者出于各种理由喜爱使用jsp。有人喜爱其“一次开发，处处使用”的性能，另外的人觉得jsp使java成为一种易学的服务器端scripting语言。但是，jsp最大的长处在它将页面的表现和页面的商业逻辑分开了。本章中，我们将深入地讨论如何使用jsp模式2体系结构来开发网站。这一模式可以被看作是通用模式浏览控制模式（popular Model-View-Controller,MVC）模式的服务器端实现。

Servlets有何缺陷？

当jsp成为开发动态网站的主要技术时，可能有人会问为何在jsp技术中我们不强调servlets。Servlets的应用是没有问题的。它们非常适于服务器端的处理和编程，并且它们会长期驻留在他们现在的位置。但是，从结构上说，我们可以将jsp看作是servlet的一个高层的抽象实现，特别是在servlet 2.2 API下。但是，你仍然不能无拘束地使用servlet；它们并不适合每一个人。例如，页面设计者可以方便地使用html或者xml工具开发jsp页面，但servlet却更适合于后端开发者使用，他们的工具是ide——一个需要更多编程训练的开发领域。当发布servlet时，每个开发者必须小心地确定在页面表现和页面逻辑之间没有紧密的关联出现。你可以使用第三方html包装工具，如htmlkona来混合html和servlet代码。即使如此，这点灵活性还不足以让你自由地改变风格本身。例如，你希望从html改变到dhtml，则包装本身需要被小心地测试，以确保新的格式可以正确使用。在最坏的情况下，包装不可用，你就需要应变马来表现动态内容。所以，需要一种新的解决方案。你将会看到，一种方案就是混合jsp和servlet的使用。

不同的方式

早期的jsp标准给出了两种使用jsp的方式。这些方式，都可以归纳为jsp模式1和jsp模式2，主要的差别在于处理大量请求的位置不同。在模式1中（图1），jsp页面独自响应请求并将处理结果返回客户。这里仍然有表现和内容的分离，因为所有的数据依靠bean来处理。尽管模式1 可以很好的满足小型应用的需要，但却不能满足大型应用的要求。大量使用模式1，常常会导致页面被嵌入大量的script或者java代码。特别是当需要处理的商业逻辑很复杂时，情况会变得严重。也许对于java程序员来说，这不算大的问题。但如果开发者是前端界面设计人员——在大型项目中，这非常常见，——则代码的开发和维护将出现困难。在任何项目中，这样的模式多少总会导致定义不清的响应和项目管理的困难。

在图2中显示的模式2 结构，是一种面向动态内容的实现，结合了servlet 和jsp技术。它利用了两种技术原有的优点，采用jsp来表现页面，采用servlets来完成大量的处理。这里，servlet扮演一个控制者的角色，并负责响应客户请求。接着，servlet创建jsp需要的bean和对象，再根据用户的行为，决定将那个jsp页面发送给用户。特别要注意，jsp页面中没有任何商业处理逻辑；它只是简单地检索servlet先前创建的bean 或者对象，再将动态内容插入预定义的模版。从开发的观点看，这一模式具有更清晰的页面表现，清楚的开发者角色划分，可以充分地利用开发小组中的界面设计人员。事实上，越是复杂的项目，采用模式2 的好处就越突出。

为了清楚地了解模式2 的开发过程，我们举一个网上音乐商店的例子。

我们创建一个叫”音乐无国界”的销售音乐制品的商店。“音乐无国界”在线商店的主界面，是一个叫“音乐无国界”的页面（代码1）。你会看到，这个页面完全着眼于用户界面，与处理逻辑无关。另外，注意另外一个jsp页面，Cart.jsp（在代码2中），用<jsp:include page="Cart.jsp" flush="true" />.嵌入Eshop.jsp中。

Listing 1:

EShop.jsp

<%@ page session="true" %>

<html>

<head>

<title>Music Without Borders</title>

</head>

<body bgcolor="#33CCFF">

<font face="Times New Roman,Times" size="+3">

Music Without Borders

</font>

<hr><p>

<center>

<form name="shoppingForm"

action="/examples/servlet/ShoppingServlet"

method="POST">

<b>CD:</b>

<select name=CD>

<option>Yuan | The Guo Brothers | China | $14.95</option>

<option>Drums of Passion | Babatunde Olatunji | Nigeria | $16.95</option>

<option>Kaira | Tounami Diabate| Mali | $16.95</option>

<option>The Lion is Loose | Eliades Ochoa | Cuba | $13.95</option>

<option>Dance the Devil Away | Outback | Australia | $14.95</option>

<option>Record of Changes | Samulnori | Korea | $12.95</option>

<option>Djelika | Tounami Diabate | Mali | $14.95</option>

<option>Rapture | Nusrat Fateh Ali Khan | Pakistan | $12.95</option>

<option>Cesaria Evora | Cesaria Evora | Cape Verde | $16.95</option>

<option>Ibuki | Kodo | Japan | $13.95</option>

</select>

<b>Quantity: </b><input type="text" name="qty" SIZE="3" value=1>

<input type="hidden" name="action" value="ADD">

<input type="submit" name="Submit" value="Add to Cart">

</form>

</center>

<p>

<jsp:include page="Cart.jsp" flush="true" />

</body>

</html>

Listing 2:

Cart.jsp

<%@ page session="true" import="java.util.*, shopping.CD" %>

<%

Vector buylist = (Vector) session.getValue("shopping.shoppingcart");

if (buylist != null && (buylist.size() > 0)) {

%>

<center>

<table border="0" cellpadding="0" width="100%" bgcolor="#FFFFFF">

<tr>

<td><b>ALBUM</b></td>

<td><b>ARTIST</b></td>

<td><b>COUNTRY</b></td>

<td><b>PRICE</b></td>

<td><b>QUANTITY</b></td>

<td></td>

</tr>

<%

for (int index=0; index < buylist.size();index++) {

CD anOrder = (CD) buylist.elementAt(index);

%>

<tr>

<td><b><%= anOrder.getAlbum() %></b></td>

<td><b><%= anOrder.getArtist() %></b></td>

<td><b><%= anOrder.getCountry() %></b></td>

<td><b><%= anOrder.getPrice() %></b></td>

<td><b><%= anOrder.getQuantity() %></b></td>

<td>

<form name="deleteForm"

action="/examples/servlet/ShoppingServlet"

method="POST">

<input type="submit" value="Delete">

<input type="hidden" name= "delindex" value='<%= index %>'>

<input type="hidden" name="action" value="DELETE">

</form>

</td>

</tr>

<% } %>

</table>

<p>

<form name="checkoutForm"

action="/examples/servlet/ShoppingServlet"

method="POST">

<input type="hidden" name="action" value="CHECKOUT">

<input type="submit" name="Checkout" value="Checkout">

</form>

</center>

<% } %>

这里，Cart.jsp按照MVC的模式1处理基于SESSION的购物车的表现。请看Cart.jsp开始处的代码：

<%

Vector buylist = (Vector) session.getValue("shopping.shoppingcart");

if (buylist != null && (buylist.size() > 0)) {

%>

本质上，这段代码从SESSION中取出“购物车”。如果“购物车”为空或者没有被创建，它就什么也不显示。所以，在用户第一次访问应用时，其界面如图：

如果“购物车”不为空，用户选择的商品从车中取出，依次显示在页面上：

<%

for (int index=0; index < buylist.size(); index++) {

CD anOrder = (CD) buylist.elementAt(index);

%>

一旦生成一个物品的说明，就使用JSP按照事先设定的模板将其插入静态HTML页面。下图显示了用户选购一些物品后的界面：

需要注意的一个重要的地方是所有关于Eshop.jsp,Cart.jsp的处理有一个控制SERVLET，ShoppingServlet.java，代码在源程序3中：

Listing 3:

ShoppingServlet.java

import java.util.*;

import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

import shopping.CD;

public class ShoppingServlet extends HttpServlet {

public void init(ServletConfig conf) throws ServletException {

super.init(conf);

}

public void doPost (HttpServletRequest req, HttpServletResponse res)

throws ServletException, IOException {

HttpSession session = req.getSession(false);

if (session == null) {

res.sendRedirect("http://localhost:8080/error.html" ;

}

Vector buylist=

(Vector)session.getValue("shopping.shoppingcart");

String action = req.getParameter("action");

if (!action.equals("CHECKOUT")) {

if (action.equals("DELETE")) {

String del = req.getParameter("delindex");

int d = (new Integer(del)).intValue();

buylist.removeElementAt(d);

} else if (action.equals("ADD")) {

//any previous buys of same cd?

boolean match=false;

CD aCD = getCD(req);

if (buylist==null) {

//add first cd to the cart

buylist = new Vector(); //first order

buylist.addElement(aCD);

} else { // not first buy

for (int i=0; i< buylist.size(); i++) {

CD cd = (CD) buylist.elementAt(i);

if (cd.getAlbum().equals(aCD.getAlbum())) {

cd.setQuantity(cd.getQuantity()+aCD.getQuantity());

buylist.setElementAt(cd,i);

match = true;

} //end of if name matches

} // end of for

if (!match)

buylist.addElement(aCD);

}

}

session.putValue("shopping.shoppingcart", buylist);

String url="/jsp/shopping/EShop.jsp";

ServletContext sc = getServletContext();

RequestDispatcher rd = sc.getRequestDispatcher(url);

rd.forward(req, res);

} else if (action.equals("CHECKOUT")) {

float total =0;

for (int i=0; i< buylist.size();i++) {

CD anOrder = (CD) buylist.elementAt(i);

float price= anOrder.getPrice();

int qty = anOrder.getQuantity();

total += (price * qty);

}

total += 0.005;

String amount = new Float(total).toString();

int n = amount.indexOf('.');

amount = amount.substring(0,n+3);

req.setAttribute("amount",amount);

String url="/jsp/shopping/Checkout.jsp";

ServletContext sc = getServletContext();

RequestDispatcher rd = sc.getRequestDispatcher(url);

rd.forward(req,res);

}

}

private CD getCD(HttpServletRequest req) {

//imagine if all this was in a scriptlet...ugly, eh?

String myCd = req.getParameter("CD");

String qty = req.getParameter("qty");

StringTokenizer t = new StringTokenizer(myCd,"|");

String album= t.nextToken();

String artist = t.nextToken();

String country = t.nextToken();

String price = t.nextToken();

price = price.replace('$',' ').trim();

CD cd = new CD();

cd.setAlbum(album);

cd.setArtist(artist);

cd.setCountry(country);

cd.setPrice((new Float(price)).floatValue());

cd.setQuantity((new Integer(qty)).intValue());

return cd;

}

}

每次用户用Eshop.jsp增加一个商品，页面就请求控制SERVLET。控制SERVLET决定进一步的行动，并处理增加的商品。接着，控制SERVLET实例化一个新的BEAN CD代表选定的商品，并在返回SESSION前更新购物车对象。

Listing 4:

CD.java

package shopping;

public class CD {

String album;

String artist;

String country;

float price;

int quantity;

public CD() {

album="";

artist="";

country="";

price=0;

quantity=0;

}

public void setAlbum(String title) {

album=title;

}

public String getAlbum() {

return album;

}

public void setArtist(String group) {

artist=group;

}

public String getArtist() {

return artist;

}

public void setCountry(String cty) {

country=cty;

}

public String getCountry() {

return country;

}

public void setPrice(float p) {

price=p;

}

public float getPrice() {

return price;

}

public void setQuantity(int q) {

quantity=q;

}

public int getQuantity() {

return quantity;

}

}

注意，我们的SERVLET中具有附加的智能，如果一个物品被重复选择，不会增加新的记录，而是在以前的记录上更新计数。控制SERVLET也响应Cart.jsp中的行为，如修改数量，删除商品，还有结帐。如果结帐，控制通过下述语句转向Checkout.jsp页面（源程序5）：

String url="/jsp/shopping/Checkout.jsp";

ServletContext sc = getServletContext();

RequestDispatcher rd = sc.getRequestDispatcher(url);

rd.forward(req,res);

Listing 5:

Checkout.jsp

<%@ page session="true" import="java.util.*, shopping.CD" %>

<html>

<head>

<title>Music Without Borders Checkout</title>

</head>

<body bgcolor="#33CCFF">

<font face="Times New Roman,Times" size=+3>

Music Without Borders Checkout

</font>

<hr><p>

<center>

<table border="0" cellpadding="0" width="100%" bgcolor="#FFFFFF">

<tr>

<td><b>ALBUM</b></td>

<td><b>ARTIST</b></td>

<td><b>COUNTRY</b></td>

<td><b>PRICE</b></td>

<td><b>QUANTITY</b></td>

<td></td>

</tr>

<%

Vector buylist = (Vector) session.getValue("shopping.shoppingcart");

String amount = (String) request.getAttribute("amount");

for (int i=0; i < buylist.size();i++) {

CD anOrder = (CD) buylist.elementAt(i);

%>

<tr>

<td><b><%= anOrder.getAlbum() %></b></td>

<td><b><%= anOrder.getArtist() %></b></td>

<td><b><%= anOrder.getCountry() %></b></td>

<td><b><%= anOrder.getPrice() %></b></td>

<td><b><%= anOrder.getQuantity() %></b></td>

</tr>

<%

}

session.invalidate();

%>

<tr>

<td> </td>

<td> </td>

<td><b>TOTAL</b></td>

<td><b>$<%= amount %></b></td>

<td> </td>

</tr>

</table>

<p>

<a href="/examples/jsp/shopping/EShop.jsp">Shop some more!</a>

</center>

</body>

</html>

结帐页面简单地从SESSION中取出购物车，然后显示每个物品和总金额。这里的关键是要结束SESSION，因此在页面中有一个session.invalidate()调用。这一处理有两个原因。首先，如果不结束SESSION，用户的购物车不会被初始化，如果用户要继续购买，车中会保留他已经支付过的商品。另外，如果用户不结帐就离开了，则SESSION会继续占用有效的资源直到过期。过期时间一般是30分钟，在一个大的站点上，这样的情况会很快导致资源耗尽。当然，这是我们不愿看到的。

注意，所有的资源分配在这个例子中是基于SESSION的。所以，你必须确保控制SERVLET不被用户访问，即使是意外的访问也不允许。这可以在控制检查到一个非法访问时用一个简单的重定向错误页面来处理。见源代码6。

Listing 6:

error.html

<html>

<body>

<h1>

Sorry, there was an unrecoverable error!

Please try <a href="/examples/jsp/shopping/EShop.jsp">again</a>.

</h1>

</body>

</html>

小结

本章的讨论显示，使用模式2，JSP和SERVLET可以在功能上最大限度的分开。正确地使用模式2，导致一个中心化的控制SERVLET，以及只完成显示的JSP页面。另一方面，模式2的实现很复杂。因此，在简单的应用中，可以考虑使用模式1。


jsp或者说JAVA倒底有多快？这里有一个计时类，可以帮你的忙。同时支持JAVA和JSP。内有例子。

作者：sonymusic

email: sonymusic@china.com

日期：2001-5-30 12:16:20

//作者:sonymusic

//原载于豆腐技术站（http://www.asp888.net

package sony.utils;

import java.util.*;

import javax.servlet.jsp.*;

/**

* 一个计时类。

* 创建日期：(2000-11-6 13:09:38)

* 作者：SonyMusic(sonymusic@china.com)

*/

public class Timing{

private ArrayList nameArray=new ArrayList();

private ArrayList timeArray=new ArrayList();

private javax.servlet.jsp.JspWriter out;

private boolean inJsp=false;

/**

* 这个构造器是在JAVA中用的。

*/

public Timing() {

super();

this.inJsp=false;

}

/**

* Timing 构造，这个构造　器是在JSP中用的。

*/

public Timing(javax.servlet.jsp.PageContext pageContext) {

super();

this.out=pageContext.getOut();

this.inJsp=true;

}

/**

* 此处插入方法说明。

* 创建日期：(2000-11-6 13:40:36)

* @param name java.lang.String

*/

public void add(String name) {

if(name==null || name.length()==0) name="Default";

//if(name.length()>7) name=name.substring(0,7);

nameArray.add(name);

timeArray.add(Calendar.getInstance());

}

/**

* 这里是在JAVA中用法。

* 创建日期：(2000-11-6 14:39:43)

* @param args java.lang.String[]

*/

public static void main(String[] args) {

Timing time=new Timing();

time.add("Start");

Random ran=new Random();

int i=0,j=0;

for(i=0;i<10000000;i++)

j+=ran.nextInt();

time.add("After 100");

for(i=0;i<100000;i++)

j+=ran.nextInt();

time.add("After 100000");

for(i=0;i<100000;i++)

j+=ran.nextInt();

time.add("After 100000");

time.show();

}

/**

* 此处插入方法说明。

* 创建日期：(2000-11-6 13:43:24)

*/

public void show() {

if(inJsp)

showInJsp();

else

showInJava();

}

/**

* 此处插入方法说明。

* 创建日期：(2000-11-6 13:46:06)

*/

private void showInJava() {

if(nameArray.isEmpty()){

System.out.println("尚未增加计时点！");

return;

}

Calendar oldCal=null,cal=null,firstCal;

//=Calendar.getInstance()

String name;

firstCal=(Calendar)timeArray.get(0);

System.out.println("序号\t\t名称\t\t\t\t时间\t\t\t\t耗时(秒)");

for(int i=0;i<nameArray.size();i++){

name=(String)nameArray.get(i);

cal=(Calendar)timeArray.get(i);

System.out.print(i+1);

System.out.print("\t\t\t");

System.out.print(name+"\t\t\t\t");

System.out.print(cal.get(Calendar.HOUR_OF_DAY)+":"+cal.get(Calendar.MINUTE)+":"+cal.get(Calendar.SECOND)+"."+cal.get(Calendar.MILLISECOND));

System.out.print("\t\t\t\t");

if(oldCal==null){

System.out.println("无");

}

else{

long diffTime=cal.getTime().getTime()-oldCal.getTime().getTime();

System.out.println((double)diffTime/1000);

}

oldCal=cal;

}

System.out.println("");

if(nameArray.size()==1){

System.out.println("只增加了一个计时点，无法统计时间！");

}

else{

long diffTime=oldCal.getTime().getTime()-firstCal.getTime().getTime();

System.out.println("总计耗时： "+(double)diffTime/1000+" 秒");

}

}

/**

* 此处插入方法说明。

* 创建日期：(2000-11-6 13:57:56)

*/

private void showInJsp() {

try {

if (nameArray.isEmpty()) {

return;

}

Calendar oldCal = null, cal = null, firstCal;

String name;

long diffTime;

firstCal = (Calendar) timeArray.get(0);

System.out.println("序号\t\t名称\t\t\t\t时间\t\t\t\t耗时(秒)");

String str = "";

str += "<table width=\"400\" border=\"1\" cellspacing=\"0\" cellpadding=\"0\">";

str += "<tr><td>序号</td><td>名称</td><td>时间</td><td>耗时(秒)</td></tr>";

for (int i = 0; i < nameArray.size(); i++) {

str += "<tr>";

name = (String) nameArray.get(i);

cal = (Calendar) timeArray.get(i);

str += "<td>";

str += i + 1;

str += "</td>";

str += "<td>" + name + "</td>";

str += "<td>"

+ cal.get(Calendar.HOUR_OF_DAY)

+ ":"

+ cal.get(Calendar.MINUTE)

+ ":"

+ cal.get(Calendar.SECOND)

+ "."

+ cal.get(Calendar.MILLISECOND)

+ "</td>";

if (oldCal == null) {

//System.out.println("无");

str += "<td>无</td>";

} else {

diffTime = cal.getTime().getTime() - oldCal.getTime().getTime();

str += "<td>" + (double) diffTime / 1000 + "</td>";

}

oldCal = cal;

str += "</tr>";

}

str += "<tr><td colspan=\"4\">";

if (nameArray.size() == 1) {

str += "只增加了一个计时点，无法统计时间！";

} else {

diffTime = oldCal.getTime().getTime() - firstCal.getTime().getTime();

str += "总计耗时： " + (double) diffTime / 1000 + " 秒";

}

str += "&nbsp;</td></tr>";

str += "</table>";

out.println(str);

} catch (java.io.IOException e) {

}

}

}

以下是在JSP中的用法

<%@ page contentType="text/html;charset=gb2312"%>

<%@ page import="sony.utils.*,java.util.*"%>

<html>

<head>

<title>Untitled</title>

</head>

<body>

<%

Timing time=new Timing(pageContext);

time.add("Start");

Random ran=new Random();

int i=0,j=0;

for(i=0;i<1000000;i++)

j+=ran.nextInt();

time.add("After 100");

for(i=0;i<100000;i++)

j+=ran.nextInt();

time.add("After 100000");

for(i=0;i<100000;i++)

j+=ran.nextInt();

time.add("After 100000");

time.show();

%>

</body>

</html>


jsp简介

作者：blackwhites

日期：2000-10-23 17:19:02

JSP语法基础

在完成了安装之后，我们来看一看JSP的语法。为了方便学习，你可以下载一个语法卡。但是，Web开发人员一般不应做过多的Java开发工作。除了几个方法调用外，你的JSP Web页面中的Java代码应该是很少的。

知道了这些之后，我们先来看一看JSP指示符和脚本元素，然后再解释一下JavaBean和JSP隐含对象。共有五种JSP指示符和脚本元素。在JSP 1.0中，大多数JSP是包含在由“<%”开始由“%>”结束的单一标记中的。在更新的JSP 1.1规范中，还有符合XML要求的版本。

JSP指示符和脚本元素 指示符 <%@ direcive %>

声明 <%! declaration %>

表达式 <%= expression %>

代码段/ Scriptlet <% code fragment %>

注释 <%-- comment --%>

指示符

JSP的指示符供JSP引擎使用。它们不产生任何可见的输出，而是告诉引擎如何处理JSP页面的其它部分。它们总是包含在<%@ ?%>标签中。两个主要的指示符是page和include。在这里不讨论Taglib指示符，在JSP 1.1中，Taglib可以用来创建自定义标记。 你几乎可以在差不多所有JSP页面的顶部找到Page指示符。尽管不是必需的，但它可以让你做一些设置，如到哪里查找支持的Java的类：

<%@ page import="java.util.Date" %>

在出现运行时间Java问题时，把浏览者引入哪个页面：

<%@ page errorPage="errorPage.jsp" %>

以及是否需要在会话级别上管理用户信息，这有可能需要跨载多个Web页面（更多信息将在后面的JavaBean会话中讲到）：

<%@ page session="true" %> include指示符让你把内容分割成更多的可管理元素，例如某些元素就用于引用共用的页眉和页脚。引用的页面可以是固定的HTML页，也可以是更多的JSP内容：

<%@ include file="filename.jsp" %>

声明

JSP的声明可以定义页面级的变量来保存信息，或者是定义JSP页面的其它部分需要的方法。如果你发现引用了太多的代码，通常使用一个单独的Java类会更好。声明写在<%! ?%>标记中。由于任何内容都必须是合法的Java语句，因此变量声明总是用分号来结束：<%! int i=0; %>。

表达式

在JSP的表达式中，表达式的结果被转换成字符串并被直接引用到输出页面中。JSP的表达式包含在<%= ?%>标记中。表达式不需要分号，除非分号是作为引号中的字符串的一部分。

<%= i %>

<%= "Hello" %>

代码段/Scriptlet

JSP代码段或Scriptlet包含在<% ?%>标记中。这些Java代码在Web服务器接收请求时运行。在Scriptlet的周围是原始的HTML或XML，代码段可以让你创建有条件的可执行代码，或者是创建使用另外一段代码的元素。例如，下面这段在H1、H2、H3和H4标记中显示字符串“Hello”的代码，就结合了表达式和scriptlet的使用。Scriptlet并不限制在一行源代码中：

<% for (int i=1; i<=4; i++) { %>

<H<%=i%>>Hello</H<%=i%>>

<% } %>

注释

最后这个JSP关键元素是注释。尽管你总是可以在文件中使用HTML注释，但如果用户查看页面的源文件，他们就可以看到这些注释。如果你不想让用户看到你的注释，你就需要把它们写在<%-- ?--%>标记里：

<%-- comment for server side only --%>


jsp简介3

作者：blackwhites

日期：2000-10-23 17:20:29

JSP和JavaBean

尽管你可以在scriptlet中放置大块的代码，但大多数Java代码是放在可重用的、被称为JavaBean的组件中的。JavaBean就象ActiveX控件：它们提供已知的功能并被设计成非定制可重用。

JavaBean的价值体现在它的一系列属性里，使用这些属性可以访问JavaBean的设置。用一个人来做类比，如果这个人是一个JavaBean，那么他的姓名、身份证号码和地址就是这些属性。在JSP Web站点中，你只要“连接粒媒”就可以使站点成为动态的。

假设JavaBean是在建立网站之前就做好了，你要做的第一件事就是告诉JSP页面它将要使用粒媒，这可以通过<jsp:useBean>标记来实现：

<jsp:useBean id="localName" class="com.jguru.Person" scope="application" />。

标记需要你用id属性来确定粒媒。在这里，你用名字来确定处于JSP页面其它部分的粒媒。除了id属性，你还要告诉页面到哪里去找粒媒，或粒媒的Java类名。

JavaBean的class属性具有这一功能，除此之外，还有其他几种确定粒媒的方法。最后所需要的是scope属性。使用scope属性，你可以告诉粒媒为这个单独的页面保存信息（缺省设置）[scope="page"]；为这一次请求保存信息[scope="request"]；为这次会话过程保存信息[scope="session"]；或是为整个应用程序保存信息[scope="application"]。把scope设为“session”，你可以在JSP页面中轻松地保存诸如购物车之类的内容。

在你声明了一个JavaBean之后，你就可以访问它的属性来进行定制。你可以使用<jsp:getProperty>标记来得到一个属性的值。在使用<jsp:getProperty>标记时，你需要指定要使用的粒媒名称（使用useBean的id域）以及要取值的属性。然后，所需要的实际值就会被放到输出中：

<jsp:getProperty id="localName" property="name" />.

改变JavaBean的属性需要使用<jsp:setProperty>标记。使用这个标记时，你还需要要确定粒媒和属性，以便修改，另外，你还需要提供新的值。如果你给出的名字是正确的，这些信息就可以从一个提交的表单直接获得：

<jsp:setProperty id="localName" property="*" />

或者是从参数获取，在参数里你需要直接给出属性和参数的名字：

<jsp:setProperty id="localName" property="address" param="parameterName" />

或者，你也可以直接用名字和值进行设置：

<jsp:setProperty id="localName" property="serialNumber" value="string" />

或<jsp:setProperty id="localName" property="serialNumber" value= <%= expression %> />.

最后，为了让Web服务器找到JavaBean，你需要把它们的类文件放在一个特殊的地方。如果使用JSWDK，则最简便的地方就是安装目录下的“classes”目录，如\jswdk-1.0.1\classes。


jsp简介4

作者：blackwhites

日期：2000-10-23 17:22:43

尽管你可以在scriptlet中放置大块的代码，但大多数Java代码是放在可重用的、被称为JavaBean的组件中的。JavaBean就象ActiveX控件：它们提供已知的功能并被设计成非定制可重用。

JavaBean的价值体现在它的一系列属性里，使用这些属性可以访问JavaBean的设置。用一个人来做类比，如果这个人是一个JavaBean，那么他的姓名、身份证号码和地址就是这些属性。在JSP Web站点中，你只要“连接粒媒”就可以使站点成为动态的。

假设JavaBean是在建立网站之前就做好了，你要做的第一件事就是告诉JSP页面它将要使用粒媒，这可以通过<jsp:useBean>标记来实现：

<jsp:useBean id="localName" class="com.jguru.Person" scope="application" />。

标记需要你用id属性来确定粒媒。在这里，你用名字来确定处于JSP页面其它部分的粒媒。除了id属性，你还要告诉页面到哪里去找粒媒，或粒媒的Java类名。

JavaBean的class属性具有这一功能，除此之外，还有其他几种确定粒媒的方法。最后所需要的是scope属性。使用scope属性，你可以告诉粒媒为这个单独的页面保存信息（缺省设置）[scope="page"]；为这一次请求保存信息[scope="request"]；为这次会话过程保存信息[scope="session"]；或是为整个应用程序保存信息[scope="application"]。把scope设为“session”，你可以在JSP页面中轻松地保存诸如购物车之类的内容。

在你声明了一个JavaBean之后，你就可以访问它的属性来进行定制。你可以使用<jsp:getProperty>标记来得到一个属性的值。在使用<jsp:getProperty>标记时，你需要指定要使用的粒媒名称（使用useBean的id域）以及要取值的属性。然后，所需要的实际值就会被放到输出中：

<jsp:getProperty id="localName" property="name" />.

改变JavaBean的属性需要使用<jsp:setProperty>标记。使用这个标记时，你还需要要确定粒媒和属性，以便修改，另外，你还需要提供新的值。如果你给出的名字是正确的，这些信息就可以从一个提交的表单直接获得：

<jsp:setProperty id="localName" property="*" />

或者是从参数获取，在参数里你需要直接给出属性和参数的名字：

<jsp:setProperty id="localName" property="address" param="parameterName" />

或者，你也可以直接用名字和值进行设置：

<jsp:setProperty id="localName" property="serialNumber" value="string" />

或<jsp:setProperty id="localName" property="serialNumber" value= <%= expression %> />.

最后，为了让Web服务器找到JavaBean，你需要把它们的类文件放在一个特殊的地方。如果使用JSWDK，则最简便的地方就是安装目录下的“classes”目录，如\jswdk-1.0.1\classes。


JSP简明教程：行为标签与实例(转)

作者：hxfwsk

email: hxfwsk@hotmail.com

日期：2001-6-15 19:08:20

在学习如何用Java编写你的JSP页之前，你需要了解叫做“行为（actions）”的服务器端的JSP元素，它执行服务器端的任务而不需我们编写Java代码。action标签可以被高级的页面设计者使用，那些可能并不熟悉Java而又想显示存于JavaBeans组件中的值的脚本编写者也可以使用它。正如前面提到的，大多数标签都是基于以组件为中心的web开发模型的。首先，我要描述一些JSP提供的action标签，然后展示一个仅仅使用标签来从一个JavaBean中显示信息的JSP页的例子――不需要编写Java代码。

载入一个JavaBean

    记住，JSP模型离不开JavaBeans，因此多数JSP标签假定你将用到存于bean中的信息。在你用到一个JavaBean之前，你必须调用标签<jsp:usebean>来声明你将使用它。后面你将学到，此标签是否会在页面内生成一个新的bean实例（这个实例可能已经在前面的会话或应用中生成了），取决于你为这个bean声明的作用域（生命周期）。

    <jsp:usebean>标签要包含几个参数，它们分别说明：

    该JavaBean所从属的类

    该JavaBean实例的名称

    该JavaBean的作用域（生命周期）

    例如：

    <jsp:usebean ID="myBeanInstance" CLASS="com.myPackage.myBeanClass" SCOPE="request">

     ...body...

    </jsp:usebean>

    在这里<jsp:usebean>标签后是一段在bean创建后被调用的主体部分，然后是一个结束标签</jsp:usebean>。如果这个主体是空的，你也可以选择下面这种简单的形式：

    <jsp:usebean ID="myBeanInstance" CLASS="com.myPackage.myBeanClass" SCOPE="request"/>

    这个示例程序会生成一个在com.myPackage.myBeanClass 类中定义过的bean的实例，在该页中命名为myBeanInstance；它只在其生命周期――对此JSP页的一个HTTP请求之内存在。这个bean现在就可以被该页使用了。

初始化一个JavaBean

    并不是所有的JavaBeans都可以被简单的创建然后使用，有一些还需要在使用之前进行初始化。在<jsp:useBean> 和</jsp:useBean>标签的主体部分中，你可以用<jsp:setProperty>标签初始化bean的属性。你可以把bean的属性设置为指定的值或从HTTP请求中传送来的值，比如来自一个已提交的表单。

    要初始化bean 的属性myProperty 为一个指定值，你可以用下面的格式：

    <jsp:usebean ID="myBeanInstance" CLASS="com.myPackage.myBeanClass" SCOPE="request">

     <jsp:setProperty NAME="myBeanInstance" PROPERTY="myProperty" VALUE="123"/>

    </jsp:usebean>

    下面的格式用来把同样的bean属性初始化为从HTML表单元素或一个URL查询字串中传送来的值：

    <jsp:usebean ID="myBeanInstance" CLASS="com.myPackage.myBeanClass" SCOPE="request">

     <jsp:setProperty NAME="myBeanInstance" PROPERTY="myProperty"

         PARAM="myFormElementName"/>

    </jsp:usebean>

    注意，不能在一个<jsp:setProperty>标签中同时使用VALUE和PARAM属性标志。

    另外，在<jsp:usebean>行为的主体中使用<jsp:setProperty>时，在页面内你可以单独使用它。在此之前，必需先用<jsp:useBean>标签定义一个有适当作用域的bean。

Bean的作用域

    <jsp:useBean>标签的SCOPE属性完成很简单的功能：它设置相关的bean的作用域，有四个可能的值，如下表（表1）所示。你可以根据情况在你的JSP应用中使用具有作用域的JavaBeans。

    表1. JSP中的作用域及相应的维持情况

作用域 描述

Page 对象只可被来自它所在页的一个客户机程序存取。

request 对象在一个客户请求的生存时间内被一个客户机程序存取。

session 对象在整个用户会话的生存时间内被来自该应用程序中任何地方的一个客户机程序存取。

application 对象在应用程序的生存时间内可以被应用程序内来自任何页面的客户机程序存取。

    不同的对象生存周期会影响到<jsp:useBean>标签如何创建或重新获取bean实例。当客户请求结束，输出被送回浏览器后，page bean和request bean就会被销毁。所以<jsp:useBean>标签必须为每个新的请求创建一个新的实例。尽管如此，当你创建一个新的session bean，这个bean的实例会保留到会话的生存周期结束，或直到你明确销毁它为止。因而<jsp:useBean>标签会在当前会话中不存在实例时创建bean的一个新的实例；否则，它就只是简单地重新取得当前实例。同样的规则也使用于应用程序的JavaBeans，除非它们在应用程序重新载入或服务器重新启动时终止。

    如果你是SSJS开发者，汲取一些SSJS会话管理的相似之处是很有帮助的。在SSJS中，page bean与request对象的作用域相同；session bean与client对象作用域相同；application bean与project对象作用域相同。例如，如果你将数值存储在一个session bean中，之后你就可以单用户地从任何一个JSP页存取这个数值，就像在SSJS中把数值存在client对象中的性质一样。然而，JSP提供了比SSJS更加灵活的状态保持机制，因为你能够定义任何数量的page、request、session和application beans。而在SSJS中，request、client 和 project 对象都是单一的。

    注意，在SSJS中没有与request bean相对应的对象。这是因为与SSJS不同，JSP允许在一个客户请求内执行多个页面。后面将对这一特性作更多讨论。

显示动态内容

    一旦创建了一个bean,你就可以用它在JSP页中生成动态内容。JSP定义了一个<jsp:getProperty>标签用来显示bean的属性，这个bean可以是在页面内用<jsp:useBean>标签定义的，也可以是先前在应用程序中被定义的session bean 或 application bean。<jsp:getProperty>标签有两个参数：NAME和PROPERTY。NAME指明先前在<jsp:useBean>标签中定义的源对象，PROPERTY表示要显示的该对象的属性值。例如：

    <jsp:usebean ID="myBeanInstance" CLASS="com.myPackage.myBeanClass" SCOPE="request"/>

    <H2> myProp=<jsp:getProperty NAME="myBeanInstance" PROPERTY="myProp"> </H2>

    正如你看到的，你可以把HTML标签与JSP标签混合使用来动态地产生和安排HTML内容。

重定向到一个外部页面

    JSP定义了一个标签<jsp:request>,你可以使用它来重定向到一个外部页面，有两种方法可供选择：指定FORWARD参数或指定INCLUDE参数。

    使用FORWARD参数，可以重定向到一个有效的URL。这种方法可以有效地在重定向发生处中止当前页的处理过程，但在这之前的处理还要进行。这一点与 CGI、SSJS、ASP和JavaScript中使用的典型的重定向十分相似。

    使用INCLUDE参数，你不仅可以重定向到另一网页，而且当完成了被调用页内的处理后还能重新返回调用页。举个例子，你调用另一个可动态生成HTML的JSP页，让它生成HTML代码，而当返回时，这些HTML会被插入调用页中的<jsp:request>标签处。实际上，被调用页并不知道它正被另一JSP页调用，它只是看到一个HTTP请求，然后返回一些HTML文本作为响应。

    请记住，你可以使用INCLUDE方法来访问静态的HTML页、JSP页、Java Servlets、SSJS页和ASP页等任何响应HTTP请求的资源，以生成一个你想要包含于你的网页中的响应。但是注意，如果你所访问的资源返回一个完整的HTML页，包含有<HTML>和<BODY>标签，那么你很可能得不到你想要的结果。

一个简单的例子

    在例1中举了一个类型为jsp.beans.samples.SuperSimpleBean的bean的例子，命名为ssb。由于已将其作用域设定为session，所以它对于其余的user session是可用的。换句话说，在它创建后，我可以在应用程序的任何一个页中用名称来访问它。我还将它的属性初始化为counter。然后就可以用<jsp:getProperty>标签在HTML页中显示counter的值了。在给定一些特定的bean属性名称后，下面的代码对于HTML设计者来说是很容易编写的。

    例1

    <HTML>

    <HEAD>

    <META NAME="GENERATOR" Content="NetObjects ScriptBuilder 2.01">

    <TITLE>Counter Page</TITLE>

    </HEAD>

    <BODY>

    <jsp:useBean ID="ssb" SCOPE="session" CLASS="jsp.beans.samples.SuperSimpleBean"/>

    <jsp:setProperty NAME="ssb" PROPERTY="counter" VALUE="2"/>

    <h2>Counter: <jsp:getProperty NAME="ssb" PROPERTY="counter"/></h2>

    </BODY>

    </HTML>


JSP里request变量列表

作者：asp2001

email: asp2001@163.net

日期：2000-8-9 12:45:42

<%

out.println("Protocol: " + request.getProtocol() + "<br>");

out.println("Scheme: " + request.getScheme() + "<br>");

out.println("Server Name: " + request.getServerName() + "<br>" );

out.println("Server Port: " + request.getServerPort() + "<br>");

out.println("Protocol: " + request.getProtocol() + "<br>");

out.println("Server Info: " + getServletConfig().getServletContext().getServerInfo() + "<br>");

out.println("Remote Addr: " + request.getRemoteAddr() + "<br>");

out.println("Remote Host: " + request.getRemoteHost() + "<br>");

out.println("Character Encoding: " + request.getCharacterEncoding() + "<br>");

out.println("Content Length: " + request.getContentLength() + "<br>");

out.println("Content Type: "+ request.getContentType() + "<br>");

out.println("Auth Type: " + request.getAuthType() + "<br>");

out.println("HTTP Method: " + request.getMethod() + "<br>");

out.println("Path Info: " + request.getPathInfo() + "<br>");

out.println("Path Trans: " + request.getPathTranslated() + "<br>");

out.println("Query String: " + request.getQueryString() + "<br>");

out.println("Remote User: " + request.getRemoteUser() + "<br>");

out.println("Session Id: " + request.getRequestedSessionId() + "<br>");

out.println("Request URI: " + request.getRequestURI() + "<br>");

out.println("Servlet Path: " + request.getServletPath() + "<br>");

out.println("Accept: " + request.getHeader("Accept") + "<br>");

out.println("Host: " + request.getHeader("Host") + "<br>");

out.println("Referer : " + request.getHeader("Referer") + "<br>");

out.println("Accept-Language : " + request.getHeader("Accept-Language") + "<br>");

out.println("Accept-Encoding : " + request.getHeader("Accept-Encoding") + "<br>");

out.println("User-Agent : " + request.getHeader("User-Agent") + "<br>");

out.println("Connection : " + request.getHeader("Connection") + "<br>");

out.println("Cookie : " + request.getHeader("Cookie") + "<br>");

out.println("Created : " + session.getCreationTime() + "<br>");

out.println("LastAccessed : " + session.getLastAccessedTime() + "<br>");

%>

运行结果：

Protocol: HTTP/1.1

Scheme: http

Server Name: 192.168.0.1

Server Port: 8080

Protocol: HTTP/1.1

Server Info: JavaServer Web Dev Kit/1.0 EA (JSP 1.0; Servlet 2.1; Java 1.2; Windows NT 5.0 x86; java.vendor=Sun Microsystems Inc.)

Remote Addr: 192.168.0.106

Remote Host: abc

Character Encoding: null

Content Length: -1

Content Type: null

Auth Type: null

HTTP Method: GET

Path Info: null

Path Trans: null

Query String: null

Remote User: null

Session Id: To1010mC466113890241879At

Request URI: /c.jsp

Servlet Path: /c.jsp

Accept: */*

Host: 192.168.0.1:8080

Referer : null

Accept-Language : zh-cn

Accept-Encoding : gzip, deflate

User-Agent : Mozilla/4.0 (compatible; MSIE 5.0; Windows 98; DigExt)

Connection : Keep-Alive

Cookie : SESSIONID=To1010mC466113890241879At

Created : 965764522168

LastAccessed : 965775587088


JSP例程 - 获取各种CGI环境变量(1.1版本)

作者：何志强

email: hhzqq@21cn.com

日期：2000-9-19 12:07:52

<%--

作者：何志强[hhzqq@21cn.com]

日期：2000-09-19

版本：1.1

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

--%>

<%@ page session="false" import="java.util.*" %>

<%

String strEnvs[][] =

        {

            {"CONTENT_LENGTH",String.valueOf(request.getContentLength())},

            {"CONTENT_TYPE",request.getContentType()},

            {"SERVER_PROTOCOL",request.getProtocol()},

            {"SERVER_SOFTWARE",getServletContext().getServerInfo()},

            {"REMOTE_ADDR",request.getRemoteAddr()},

            {"REMOTE_HOST",request.getRemoteHost()},

            {"REMOTE_USER",request.getRemoteUser()},

            {"SERVER_NAME",request.getServerName()},

            {"SERVER_PORT",String.valueOf(request.getServerPort())},

            {"AUTH_TYPE",request.getAuthType()},

            {"REQUEST_METHOD",request.getMethod()},

            {"PATH_INFO",request.getPathInfo()},

            {"PATH_TRANSLATED",request.getPathTranslated()},

            {"QUERY_STRING",request.getQueryString()},

            {"REQUEST_URI",request.getRequestURI()},

            {"SCRIPT_NAME",request.getServletPath()},

            {"DOCUMENT_ROOT",getServletContext().getRealPath("/")}

        };

Enumeration enumNames;

String strName,strValue;

int i;

%>

<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>

<%

for(i=0;i<strEnvs.length;i++){

%>

<tr>

  <td>&nbsp;<%=strEnvs[i][0]%></td>

  <td>&nbsp;<%=strEnvs[i][1]%></td>

</tr>

<%

}

%>

</table>

</body>

</html>


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

作者：何志强

email: hhzqq@21cn.com

日期：2000-9-19 10:35:21

<%--

作者：何志强[hhzqq@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中定义函数

作者：何志强

email: hhzqq@21cn.com

日期：2000-9-19 9:43:21

<%--

作者：何志强[hhzqq@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其他相关资源--JSP开发入门五

作者：jeru

日期：2000-12-20 11:31:51

JSP其他相关资源：

Servlets and JavaServer Pages (JSP) 1.0: A Tutorial

http://www.apl.jhu.edu/~hall/java/Servlet-Tutorial/

JavaServer PagesTM: A Developer's Perspective

http://developer.java.sun.com/developer/technicalArticles/Programming/jsp/

JAVASERVER PAGESTM NEWS & ARTICLES

http://java.sun.com/products/jsp/news.html

JAVASERVER PAGESTM TECHNICAL RESOURCES

http://java.sun.com/products/jsp/technical.html

SERVLETS TAVERNE

http://www.interpasnet.com/JSS/

servlets.com

http://www.servlets.com/


JSP入门

此电子文档属C#乐园QQ群3256045整理。
JSP入门

上一页 目录 下一页

JSP入门

作者：caoli

email: caoli@dicp.ac.cn

日期：2001-2-21 9:42:25

1） JSP（JavaServer Pages）简介：在当今世界，流行的Internet应用程序开发主要有：ASP，PHP，CGI，JSP……等多种方案，技术上各有优缺点，但是JSP以其简单易学，跨平台的特性，在众多程序中独树一帜，在短短几年中已经形成了一套完整的规范，并广泛的应用于电子商务等各个领域中。在国内JSP现在也得到了比较广泛的重视

    2） 如何才能快速掌握JSP：JSP实际上是JSP定义的一些标记和JAVA程序段，以及HTML文件的混合体。所以，如果要掌握JSP首先必须对HTML有一定的了解（不然怎么设计网页），然后，也必须有JAVA程序的基础（要知道JSP是基于JAVA语言的），最后就是对JSP标识的一些必要的了解（它使你知道如何用JAVA语言及HTML组合成完整的JSP），而此篇文章我们在这里主要是对JSP标识及语法规则进行介绍。所以最好对HTML及JAVA语言有一点了解，如果没有学过也没有关系，下面的介绍会让你对JSP有一个比较直观的了解。

    3） 体会简单易学的JSP：JSP简单而易学，如果你有了HTML的基础，要学会就很容易的了，不信？先拿一段JSP程序给你看看吧（简单而经典的HelloWord.jsp）：

    < %page language="java"% >

    < HTML>

    < head >

    < title>Hello World!< /title>

    < /head>

    < body bgcolor="#FFFFFF">

    < %String msg="JSP Example";//定义字符串对象

    out.println("Hello World!"); %>

    < %=msg%> < !-显示变量值- >

    < /body>

    < /HTML>

    运行之后，在窗口上的输出字符串Hello World! JSP Example，怎么样，一看就懂了吧，的确很简单的，除去"< %% >"标识，其他的都是普通的超文本，如果你有了超文本的基础，一看便知道了；不过他和超文本还是有了一点区别，那就是加入了"< %% >"标识，标识之中使用的是JAVA程序，由它来控制动态数据的显示的，并直接输出到标识符所嵌入的位置，整个结构显得相当的直观，以后如果页面发生了变化，修改也变得十分的容易。至于程序第一行的标识，我们暂时可以不必管他，在后面，我们会对这类语法做相应的介绍。

    看了上面的JSP程序，你对JSP应该也有了一点了解了吧，是的，学习JSP是并不是一件难事，而且以后，你会越发感受到JSP的优势，他的跨平台特性在Internet开发程序中可谓是独树一帜，因为JSP技术是构建于JAVA语言之上的，他的很多特性和应用都来自JAVA语言，所以如果你要学好JSP，需要有JAVA编程的基础（关于JAVA语言，我这里就不多介绍了，可以参看相应的书籍，向大家推荐：《JAVA2程序设计》（清华大学出版社））

    4） JSP从头做起：

    a) 从http://www.java.sun.comb) 组建JSP运行　　环境：要使JSP运行起来首先要建造JSP运行环境。

    现在我们常用到的应用程序服务器软件主要有WebLogic Application Server，Apache Tomcat，Allarie Jrun。WebLogic是现在市场上占有率最高的一种服务器软件，功能强大，配置也很简单，作为商业应用的服务器是很好的选择，不过作为初学应用，建议还是不要的好，因为这是商业软件，需要money的。Jrun是由Allarie公司开发的服务器软件，也有比较高的声誉，和WebLogic一样也是很好的选择，也是商业软件。初学，还是找个便宜点（最好是不要钱的：）），Tomcat便是你最好的选择，他是一个免费的服务器软件，而且他的源代码可以免费获得，当你对它有深入的了解之后，可以自由的对他进行扩充，以适应你自己的需要，虽然配置麻烦了一点，但以上的这么多优点，它是我们当之无愧的最佳选择。下面我们就以Tomcat为实例，向大家介绍win2000下JSP环境的配置：

    1） 首先，从apache网站（http://java.apache.org

    2） 进入tomcat的目录里，找到startup.bat文件，打开编辑。在行call tomcat start的前面加上两行： SET TOMCAT_HOME=c:\tomcat 和 SET JAVA_HOME=c:\jdk1.3 注：c:\jdk1.3是jdk1.3安装的位置。

    3） 然后鼠标右键点击"我的电脑"，选择属性。在系统属性中打开高级选项，点击"环境变量"，在"用户变量"和"系统变量"中都加入两个变量classpath和path，它们的值是：classpath的值　　c:\jdk1.3\bin\tools.jar;C:\jdk1.3\lib\dt.jar path的值为 c:\jdk1.3\bin;c:\jdk1.3

    4） 执行jakarta-tomcat\bin\startup.bat，会看到一个打开的Tocmat启动窗口，

    5） 打开浏览器，输入：http://localhost:8080，回车就可以看到TOMCAT的主页面了

    c) JSP的基本语法简介：

        i. 注释语句："< %-- comment --% >"在编译的时候将跳过此标识；

        ii. 插入一个有效的程序段 ：< % code % >

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

    < %@ page

    [ language="java" ] //声明脚本语言的种类

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

    [ import="{package.class / package.*}, ..." ] //需要导入的Java包的列表

    [ session="true / false" ] //设定客户是否需要HTTP Session

    [ buffer="none / 8kb / sizekb" ] //buffer的大小被out对象用于处理执行后的JSP对客户浏览器的输出

    [ 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" ] //设置此页是否为出错页，如果被设置为true,你就能使用exception对象

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

    [ isErrorPage="true / false" ] %>

    iv. 包含一个文件："< %@ include file="relativeURL" %> "，在编译的时候可以（静态的）插入文件；

    v. 包含一个表达式："< %= expression %>"在运行的时候显示的是表达式的值；

    vi. 申明变量：" < %! declaration; [ declaration; ]+ .. % >"；如"< %!int I;% >"

    vii. 创建一个Bean实例并设置其属性：""；class属性：从一个class中示例一个对象；id属性：指示此class对象的引用名；scope属性： Bean存在的范围以及id变量名的有效范围，如果为page则只在此页面类有效(下次调用该页面时时将重新创建class对象)；如为session则在session有效期类，此class对象只创建一次；如为Application则class在整个程序运行工程中始终有效，并且只创建一次（相当于静态量）

    d) Tomcat的JSP例程介绍：

    < !-- Copyright (c) 1999 The Apache Software Foundation. All rights

    reserved.

    Number Guess Game

    Written by Jason Hunter < jasonh@kasoftware.com>, CTO, K&A Software 　　　　　　　　　　http://www.servlets.com-->

    < %@ page import = "num.NumberGuessBean" %>

    < ! -导入的Java包- >

    < jsp:useBean id="numguess" class="num.NumberGuessBean" scope="session"/ >

    < ! -创建对象并设置其应用名为numguess并申明此对象在session有效期内始终有效-->

    < html>

    < head>

    < title>Number Guess< /title>

    < /head>

    < body bgcolor="white">

    < font size=4>

    < % if (numguess.getSuccess()) { %> //调用NumberGuessBean对象的getSuccess方法

    Congratulations! You got it.

    And after just

    < %= numguess.getNumGuesses()%>

    tries.< p>

    < % numguess.reset(); %>

    Care to < a href="numguess.jsp">try again?

    < % } else if (numguess.getNumGuesses() == 0) { %> Welcome to the Number Guess game.

     I'm thinking of a number between 1 and 100.

     < form method=get>

    What's your guess?

    < input type=text name=guess>

    < input type=submit value="Submit">

    < /form>

    < % } else { %>//

    Good guess, but nope. Try < b>

    < %= numguess.getHint() %>< /b>.

    You have made < %= numguess.getNumGuesses() %> guesses.< p>

    I'm thinking of a number between 1 and 100.

    < form method=get> What's your guess?

    < input type=text name=guess>

    < input type=submit value="Submit">

    < /form> < % } %>

    < /font>

    < /body>

    < /html>

    从例子我们可以看到，JSP是通过程序的逻辑来控制页面的显示的，通过程序来得到显示的动态属性。

    5） 相关网站介绍

    a) http://www.java.sun.com

    b) http://www.servlets.com

    c) http://www.21icsearch.com/aspfans/jspfans/default.jsp

    d) http://java.apache.org


JSP实现在浏览器关闭cookies情况下的会话管理

作者：jspfuns

日期：2000-8-25 21:24:30

  通常，会话管理是通过服务器将 Session ID 作为一个 cookie 存储在用户的 Web 浏览器中来唯一标识每个用户会话。如果浏览器不支持 cookies，或者将浏览器设置为不接受 cookies，我们可以通过 URL 重写来实现会话管理。

    实质上 URL 重写是通过向 URL 连接添加参数，并把 session ID 作为值包含在连接中。然而，为使这生效，你需要为你的 servlet 响应部分的每个连接添加 session ID 。

     把 session ID 加到一个连接可以使用一对方法来简化：response.encodeURL() 使 URL 包含 session ID，如果你需要使用重定向，可以使用 response.encodeRedirectURL () 来对 URL 进行编码。

    encodeURL () 及 encodeRedirectedURL () 方法首先判断 cookies 是否被浏览器支持；如果支持，则参数 URL 被原样返回，session ID 将通过 cookies 来维持。

    来看下面的例子，两个 JSP 文件：hello1.jsp 和 hello2.jsp，及它们之间的影响。我们在 hello1.jsp 中简单的创建一个会话，并在 session 中存储一个对象实例。接着用户可以点击页面的连接到达 hello2.jsp。在 hello2.jsp 中，我们从 session 中获取原先放置的对象并显示它的内容。注意，我们在 hello1.jsp 中调用了 encodeURL() 方法来获得 hello2.jsp 的链接，使得在浏览器停用 cookies 的情况下，session ID 自动添加到 URL，hello2.jsp 仍能得到 session 对象。

    首先在启用 cookies 的情况下运行。然后关闭对 cookie 的支持，重启浏览器，再运行一次。每次你都可以看到会话管理在起作用，并能在页之间传递信息。

注意，如果你想让这个例子能在关闭了 cookies 的浏览器中工作，你的 JSP 引擎必须支持 URL 重写。

hello1.jsp

<%@ page session="true" %>

<%

Integer num = new Integer(100);

session.putValue("num",num);

String url =response.encodeURL("hello2.jsp");

%>

<a href='<%=url%>'>hello2.jsp</a>

hello2.jsp

<%@ page session="true" %>

<%

Integer i= (Integer)session.getValue("num");

out.println("Num value in session is "+i.intValue());

%>


JSP数据库操作例程 - 存储过程 - JDBC-ODBC - SQL Server - 1.1版本

作者：何志强

email: hhzqq@21cn.com

日期：2000-8-16 16:12:47

<%--

作者：何志强[hhzqq@21cn.com]

日期：2000-08-04

      2000-08-16

版本：1.1

功能：JSP数据库操作例程 - 存储过程 - JDBC-ODBC - SQL Server

SQL Server的存储过程如下：

    create procedure sp_jsptest

       @yourname  varchar(50),

       @myname varchar(50) output

    as

       select "您好,"+@yourname+",非常高兴认识您，^_^"

       set @myname = "何志强"

       return 1

    go

--%>

<%@ page contentType="text/html;charset=gb2312"%>

<%

//变量声明

java.lang.String strName; //姓名

//取得用户输入的数据

strName = request.getParameter("name");

if(strName==null){//用户没有输入姓名

%>

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=gb2312">

<title>JSP数据库操作例程 - 存储过程 - JDBC-ODBC - SQL Server</title>

</head>

<body>

<form action="jdbc-odbc.jsp" method="post">

您尊姓大名：<input type="text" name="name" maxlength="50">

<input type="submit" value="提交">

</form>

</body>

</html>

<%

}

else{

   //对用户输入的数据作必要的字符编码转换

   strName = new java.lang.String(strName.getBytes("iso-8859-1"));

   //变量声明

   java.sql.Connection sqlCon;         //数据库连接对象

   java.sql.CallableStatement sqlStmt; //可调用语句对象

   java.sql.ResultSet sqlRst;          //结果集对象

   java.lang.String strCon;            //数据库连接字符串

   java.lang.String strSQL;            //SQL语句

   java.lang.String strWelcome;        //欢迎词

   java.lang.String strMyName;         //我的姓名

   int intReturn;                      //返回值

   //装载JDBC-ODBC驱动程序

   Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");

   //设置数据库连接字符串

   strCon = "jdbc:odbc:jspdemo";

   //连接数据库

   sqlCon = java.sql.DriverManager.getConnection(strCon,"sa","");

   //准备SQL语句

   strSQL = "{? = call sp_jsptest(?,?)}";

   //准备可调用语句对象

   sqlStmt = sqlCon.prepareCall(strSQL);

   //设置输入参数

   sqlStmt.setString(2,strName);

   //登记输出参数

   sqlStmt.registerOutParameter(1,java.sql.Types.INTEGER);

   sqlStmt.registerOutParameter(3,java.sql.Types.VARCHAR);

   //执行该存储过程并返回结果集

   sqlRst = sqlStmt.executeQuery();

   //获取来自结果集中的数据

   sqlRst.next();

   strWelcome = sqlRst.getString(1);

   //获取输出参数的值

   strMyName = sqlStmt.getString(3);

   //获取返回值

   intReturn = sqlStmt.getInt(1);

   //关闭记录集

   sqlRst.close();

   //关闭可调用语句对象

   sqlStmt.close();

   //关闭数据库对象

   sqlCon.close();

%>

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=gb2312">

<title>JSP数据库操作例程 - 存储过程 - JDBC-ODBC - SQL Server</title>

</head>

<body>

<%=strWelcome%><br>

我是<%=strMyName%><br>

返回值是<%=intReturn%>

</body>

</html>

<%

}

%>


JSP数据库操作例程 - 数据分页显示 - JDBC 2.0 - Oracle

作者：令少爷

email: dj12@8848.net

日期：2001-7-10 10:41:08

<%@ page contentType="text/html;charset=8859_1" %>

<%

//变量声明

java.sql.Connection sqlCon; //数据库连接对象

java.sql.Statement sqlStmt; //SQL语句对象

java.sql.ResultSet sqlRst; //结果集对象

java.lang.String strCon; //数据库连接字符串

java.lang.String strSQL; //SQL语句

int intPageSize; //一页显示的记录数

int intRowCount; //记录总数

int intPageCount; //总页数

int intPage; //待显示页码

java.lang.String strPage;

int i;

//设置一页显示的记录数

intPageSize = 2;

//取得待显示页码

strPage = request.getParameter("page");

if(strPage==null){//表明在QueryString中没有page这一个参数，此时显示第一页数据

intPage = 1;

}

else{//将字符串转换成整型

intPage = java.lang.Integer.parseInt(strPage);

if(intPage<1) intPage = 1;

}

//装载JDBC驱动程序

java.sql.DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());

//设置数据库连接字符串

strCon = "jdbc:oracle:thin:@linux:1521:ora4cweb";

//连接数据库

sqlCon = java.sql.DriverManager.getConnection(strCon,"hzq","hzq");

//创建一个可以滚动的只读的SQL语句对象

sqlStmt = sqlCon.createStatement(java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE,java.sql.ResultSet.CONCUR_READ_ONLY);

//准备SQL语句

strSQL = "select name,age from test";

//执行SQL语句并获取结果集

sqlRst = sqlStmt.executeQuery(strSQL);

//获取记录总数

sqlRst.last();

intRowCount = sqlRst.getRow();

//记算总页数

intPageCount = (intRowCount+intPageSize-1) / intPageSize;

//调整待显示的页码

if(intPage>intPageCount) intPage = intPageCount;

%>

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=gb2312">

<title>JSP数据库操作例程 - 数据分页显示 - JDBC 2.0 - Oracle</title>

</head>

<body>

<table border="1" cellspacing="0" cellpadding="0">

<tr>

<th>姓名</th>

<th>年龄</th>

</tr>

<%

if(intPageCount>0){

//将记录指针定位到待显示页的第一条记录上

sqlRst.absolute((intPage-1) * intPageSize + 1);

//显示数据

i = 0;

while(i<intPageSize && !sqlRst.isAfterLast()){

%>

<tr>

<td><%=sqlRst.getString(1)%></td>

<td><%=sqlRst.getString(2)%></td>

</tr>

<%

sqlRst.next();

i++;

}

}

%>

</table>

第<%=intPage%>页  共<%=intPageCount%>页  <%if(intPage<intPageCount){%><a href="jdbc20-oracle.jsp?page=<%=intPage+1%>">下一页</a><%}%>  <%if(intPage>1){%><a href="jdbc20-oracle.jsp?page=<%=intPage-1%>">上一页</a><%}%>

</body>

</html>

<%

//关闭结果集

sqlRst.close();

//关闭SQL语句对象

sqlStmt.close();

//关闭数据库

sqlCon.close();

%>


JSP文件操作例程 - 读文件

此电子文档属C#乐园QQ群3256045整理。
JSP文件操作例程 - 读文件

上一页 目录 下一页

JSP文件操作例程 - 读文件

作者：何志强

email: hhzqq@21cn.com

日期：2000-08-07 14:26:10

<%--

作者：何志强[hhzqq@21cn.com]

日期：2000-08-07

版本：1.0

功能：JSP文件操作例程 - 读文件

--%>

<%@ page contentType="text/html;charset=gb2312"%>

<%

//变量声明

java.lang.String strFileName;     //文件名

java.io.File objFile;             //文件对象

java.io.FileReader objFileReader; //读文件对象

char[] chrBuffer = new char[10];  //缓冲

int intLength;                    //实际读出的字符数(一个中文为一个字符)

//设置待读文件名

strFileName = "d:\\test.txt";

//创建文件对象

objFile = new java.io.File(strFileName);

//判断文件是否存在

if(objFile.exists()){//文件存在

   //创建读文件对象

   objFileReader = new java.io.FileReader(objFile);

   //读文件内容

   while((intLength=objFileReader.read(chrBuffer))!=-1){

      //输出

      out.write(chrBuffer,0,intLength);

   }

   //关闭读文件对象

   objFileReader.close();

}

else{//文件不存在

   out.println("下列文件不存在："+strFileName);

}

%>


jsp文件操作之读取篇

此电子文档属C#乐园QQ群3256045整理。
jsp文件操作之读取篇

上一页 目录 下一页

jsp文件操作之读取篇

作者：asp3000

email: coolknight@263.net

日期：2000-8-8 12:00:20

jsp文件操作之读取篇(本站文章)

转载请注明来源

来源 jsp中国论坛 http://jspbbs.yeah.net

Read.jsp

<html>

<head>

<title>Read a file</title>

</head>

<body bgcolor="#000000">

<jsp:useBean id="reader" class="DelimitedDataFile" scope="request">

  <jsp:setProperty name="reader" property="path" value="/path/to/afile.txt" />

</jsp:useBean>

<h3>Contents of the file:</h3>

<p>

<% int count = 0; %>

<% while (reader.nextRecord() != -1) { %>

  <% count++; %>

  <b>Line <% out.print(count); %>:</b> <% out.print(reader.returnRecord()); %><br>

<% } %>

</p>

</body>

</html>

import java.io.*;

import java.util.StringTokenizer;

public class DelimitedDataFile

{

   /**

    * DelimitedDataFile.java

    * Written by Morgan Catlin  Email: mfcatlin@csclub2.stthomas.edu

    *   April 6, 1999

    *

    * Variables:

    *   String currentRecord = the record the bean is currently working on

    *   BufferedReader file = the file the bean is working with

    *   String path = the path to the file (ie. /home/you/afile.txt)

    *   StringTokenizer token = the currentRecord tokenized

    *

    * Methods:

    *   public void setPath() - creates a BufferedReader that reads the file in path

    *   public String getPath() - returns path

    *   public void fileClose() - closes the file that is being read

    *   public int nextRecord() - reads the next record(line) in the file,

    *                              and returns the number of tokens in the record

    *                              or else returns -1 if there aren't anymore records

    *   public double returnDouble() - returns the next token as a double

    *   public int returnInt() - returns the next token as an int

    *   public String returnString() - returns the next token as a String

    *   public String returnRecord() - returns the entire record as a String

    */

          private String           currentRecord = null;

          private BufferedReader   file;

          private String           path;

          private StringTokenizer  token;

          public DelimitedDataFile()

                {

                        file = new BufferedReader(new InputStreamReader(System.in),1);

                } // constructor 1

          public DelimitedDataFile(String filePath) throws FileNotFoundException

                {

                        // gets file

                        path = filePath;

                        file = new BufferedReader(new FileReader(path));

                } // constructor DelimitedDataFile

          public void setPath(String filePath)

                {

                        // sets the file

                        path = filePath;

                        try {

                             file = new BufferedReader(new

                             FileReader(path));

                        } catch (FileNotFoundException e) {

                        System.out.println("file not found");

                        }

                } // method setPath

               public String getPath() {

                return path;

          } // method getPath

          public void fileClose() throws IOException

                {

                        // closes file

                        file.close();

                } // method fileClose

          public int nextRecord()

                {

                        // this method reads the next record and returns the number of

                        // tokens or else returns -1

                        int returnInt = -1;

                        try

                            {

                                currentRecord = file.readLine();

                            } // end try

                        catch (IOException e)

                            {

                                System.out.println("readLine problem, terminating.");

                            } // end catch

                        if (currentRecord == null)

                            returnInt = -1;

                        else

                            {

                                 token = new StringTokenizer(currentRecord);

                                 returnInt = token.countTokens();

                            } // end else

                        return returnInt;

                } // method nextRecord

          public double returnDouble()

                {

                        // this method returns the next token as a double

                        double doubleReturn = Double.valueOf(token.nextToken()).doubleValue();

                        return doubleReturn;

                } // method returnDouble

          public int returnInt()

                {

                        // this method returns the next token as an int

                        int returnint = Integer.parseInt(token.nextToken());

                        return returnint;

                } // method returnInt

          public String returnString()

                {

                        // this method returns the next token as a String

                        String stringReturn = token.nextToken();

                        return stringReturn;

                } // method returnString

          public String returnRecord()

               {

                       // this method returna the entire record as a string

                       return currentRecord;

               }  // method returnRecord

    } // class DelimitedDataFile

转载请注明来源

来源 jsp中国论坛 http://jspbbs.yeah.net


jsp文件操作之写入篇

作者：asp3000

email: coolknight@263.net

日期：2000-8-8 12:01:15

jsp文件操作之写入篇(本站文章)

转载请注明来源

来源 jsp中国论坛 http://jspbbs.yeah.net

WriteOver.Jsp

<html>

<head>

<title>Write over a file</title>

</head>

<body bgcolor="#000000">

<jsp:useBean id="writer" class="WriteOver" scope="request">

<jsp:setProperty name="writer" property="path" value="/path/to/afile.txt" />

<jsp:setProperty name="writer" property="something" value="Something already set as a property in WriteOver" />

</jsp:useBean>

<h3>Write to the file</h3>

<p>

<% writer.setSomething("Something to write to the file"); %>

<% out.print(writer.getSomething()); %>

<% out.print(writer.writeSomething()); %>

</p>

</body>

</html>

import java.io.*;

/**

* WriteOver.java

* Written by  Morgan Catlin Email: mfcatlin@csclub2.stthomas.edu

*   August 19, 1999

*

* Variables:

*   String path = path to file to write to (ie. /you/afile.txt)

*   String something = something to write to the file

*

* Methods:

*   void setPath() = sets path

*   String getPath() = returns path

*   void setSomething() = sets something

*   String getSomething() = returns something

*   String writeSomething() = writes something to the path,

*      returns a message that indicates success or failure(an Exception)

*/

public class WriteOver {

   private String path;

   private String something;

   public WriteOver() {

      path = null;

      something = "Default message";

   } // constructor WriteOver

   /**

    * Mutator for the path property

    */

   public void setPath(String apath) {

      path = apath;

   } // mutator setPath

   /**

    * Accessor for the path property

    */

   public String getPath() {

      return path;

   } // accessor getPathClient

   /**

    * Mutator for the something property

    */

   public void setSomething(String asomething) {

      something = asomething;

   } // mutator setSomething

   /**

    * Accessor for the something property

    */

   public String getSomething() {

      return something;

   } // accessor getSomething

   /**

    * This method writes something to the path

    */

   public String writeSomething() {

      try {

         File f = new File(path);

         PrintWriter out = new PrintWriter(new FileWriter(f));

         out.print(this.getSomething() + "\n");

         out.close();

         return "Alles ist Gut.";

      } catch (IOException e) {

         return e.toString();

      }

   } // method writeSomething

} // class WriteOver

转载请注明来源

来源 jsp中国论坛 http://jspbbs.yeah.net


jsp文件操作之追加篇

作者：asp3000

email: coolknight@263.net

日期：2000-8-8 12:01:54

jsp文件操作之追加篇(本站文章)

转载请注明来源

来源 jsp中国论坛 http://jspbbs.yeah.net

如何用jsp将数据追加到一个文件中，下面是实现方法

writeAppend.jsp

<html>

<head>

<title>Append a file</title>

</head>

<body bgcolor="#000000">

<jsp:useBean id="writer" class="WriteAppend" scope="request">

  <jsp:setProperty name="writer" property="path" value="/path/to/afile.txt" />

  <jsp:setProperty name="writer" property="something" value="Something already set as a property in WriteAppend" />

</jsp:useBean>

<h3>Write to the file</h3>

<p>

<% writer.setSomething("Something to write to the file"); %>

<% out.print(writer.getSomething()); %>

<% out.print(writer.writeSomething()); %>

</p>

</body>

</html>

WriteAppend.java

import java.io.*;

/**

* WriteAppend.java

* Written by  Morgan Catlin Email: mfcatlin@csclub2.stthomas.edu

*   August 19, 1999

*

* Variables:

*   String path = path to file to write to (ie. /you/afile.txt)

*   String something = something to write to the file

*

* Methods:

*   void setPath() = sets path

*   String getPath() = returns path

*   void setSomething() = sets something

*   String getSomething() = returns something

*   String writeSomething() = writes something to the path,

*      returns a message that indicates success or failure(an Exception)

*/

public class WriteAppend {

   private String path;

   private String something;

   public WriteAppend() {

      path = null;

      something = "Default message";

   } // constructor WriteAppend

   /**

    * Mutator for the path property

    */

   public void setPath(String apath) {

      path = apath;

   } // mutator setPath

   /**

    * Accessor for the path property

    */

   public String getPath() {

      return path;

   } // accessor getPathClient

   /**

    * Mutator for the something property

    */

   public void setSomething(String asomething) {

      something = asomething;

   } // mutator setSomething

   /**

    * Accessor for the something property

    */

   public String getSomething() {

      return something;

   } // accessor getSomething

   /**

    * This method writes something to the path

    */

   public String writeSomething() {

      try {

        FileWriter theFile = new FileWriter(path,true);

        PrintWriter out = new PrintWriter(theFile);

        out.print(something + "\n");

        out.close();

        theFile.close();

        return "Das war sehr gut!";

      } catch (IOException e) {

         return e.toString();

      }

   } // method writeSomething

} // class WriteAppend

转载请注明来源

来源 jsp中国论坛 http://jspbbs.yeah.net


JSP由浅入深(1)—— 熟悉JSP服务器

作者：风之彩

日期：2001-6-8 17:27:03

熟悉JSP服务器

本“JSP由浅入深” 系列教程是面向中级和高级用户的，它需要HTML和Java的基础。你应该会将HTML网页连接到一起，并且会利用Java来进行编程。如果你还没有这个基础，建议你还是先打好基础为好。这个系列教程将通过编制简单的例子到复杂的例子来教会你JSP。本系列教程是采用循序渐进的方法来进行阐述的，即由浅入深。为了使你能够获得最大的进步，建议你再学习的过程中将所有的例子自己进行调试。开始的例子可能会很简单，所以开始的时候你要特别耐心，不要认为太简单而跳过。如果你仔细地调试例子，那么你就会很快地熟悉JSP的本质方法。

好吧，开始我们的第一个教程：熟悉JSP服务器。

如果你没有一个JSP网络服务器，那么你在开始教程之前还是先下载它吧。以下的几个服务器可以免费下载或者进行开发：

Blazix (1.5 Megabytes, JSP, Servlets and EJBs)

来自http://www.blazix.com/blazix.html

ServletExec (3.8 Megabytes, JSP and Servlets)

来自http://www.unifyeware.com/servletExec/

JRun (11 Megabytes, JSP, Servlets and EJBs)

来自http://www.jrun.com/

WebLogic(44 Megabytes, JSP, Servlets and EJBs)

来自http://www.beasys.com/

WebSphere (105 Megabytes, JSP, Servlets and EJBs)

来自www-4.ibm.com/sofeware/webservers/

如果你还没有服务器，那我建议你下载Blazix，因为它包含了标签库（可以用于以后关于标签库的教程）。Blazix同样也是很小的并且它可以很容易地下载，而且可以运行于所有的操作系统，包括处于主流的Windows98。还有一个优点，就是它的安装的速度更块。

为了真正学习JSP，最重要的是，你要在一个真实的服务器来调试教程的例子。最好的方法是通过自己的实践来学习JSP的技巧。如果你现在还没有服务器，那就先下载一个安装它就行了。

装上网络服务器以后，你应该学会以下关于网络服务器的的一些知识：应该在哪里放置文件？怎样访问来自浏览器的文件（是以http:开头的，而不是file:开头）？

你首先要创建以下的简单文件，比如：

<HTML>

<BODY>

Hello, world

</BODY>

</HTML>

怎样放置文件并在浏览器（以http://）中浏览它，这个步骤对应不同的网络服务器是不同的，所以你需要参看网络服务器的文档并找出答案。


JSP由浅入深(4)—— Scriptlets（2）

作者：风之彩

日期：2001-6-8 17:33:31

另外一个被预定义非常有用的变量是“request”。它是一种javax.servlet.http.HttpServletRequest。

在服务器端“request”处理浏览器和服务器之间的工作。当用户点击连接或者键入一个URL，浏览器就会为那个URL发送一个“request”给服务器并且显示返回的数据。作为这个“request”的一部分，各种数据是可利用的，这些数据包括浏览器想从服务器中取得的文件；并且如果这个请求（request）是通过按下SUBMIT(提交)按钮发来的，这些数据还包括用户键入到表单区域的信息。

JSP的“request”变量用于获得从由浏览器发送的请求的信息。举个例子，你可以知道客户主机的名字，或者只能获得此主机IP地址。下面我们对以前教程的代码修改如下：

<HTML>

<BODY>

<%

// This scriptlet declares and initializes "date"

System.out.println( "Evaluating date now" );

java.util.Date date = new java.util.Date();

%>

Hello! The time is now

<%

out.println( date );

out.println( "<BR>Your machine's address is " );

out.println( request.getRemoteHost());

%>

</BODY>

</HTML>

还有一个类似的变量，“response”。这个变量用于影响被发送到浏览器的响应。为了更好的理解这句话的意思，我们来举个例子，你可以调用response.sendRedirect( anotherUrl )来发送一个响应给浏览器，接着这个浏览器就会发送一个不同的请求给“anotherUrl”。这看起来跟一些我们遇到的JSP机理有点不同，比如它包括了另外的网页或者转发浏览器给其它的网页。

本教程的特点是每个教程结束后都要作个练习。这次的练习为：编写一个JSP来输出完整的行“Hello! The time is now.”，当然还要为这个字符串编写一个scriptlet，包括HTML标记。这里还需要再提醒一下，这些练习对巩固所学知识和加深理解都是很有帮助，所以我希望所有学员都要认真地作练习。


JSP由浅入深(4)—— Scriptlets

此电子文档属C#乐园QQ群3256045整理。
JSP由浅入深(4)—— Scriptlets

上一页 目录 下一页

JSP由浅入深(4)—— Scriptlets

作者：风之彩

日期：2001-6-8 17:32:08

上面的教程我们已经学会了如何在JSP网页中通过在<%= and %>字符系列之间嵌入Java表达式。但是通过将Java表达式放置到HTML进行大量的编程工作是很困难的。

JSP另外允许你编写Java代码块嵌入到JSP中。具体做法是：将Java代码段放置于<% and %>字符之间。其实跟表达式是一样的，只不过在开始的地方没有等号而已。

代码块就是我们熟知的“scriptlet”。如果单单是scriptlet，对于HTML起不了任何作用。而每次scriptlet包含了被执行的Java代码的时候，JSP就被调用。

下面的代码是对上一节JSP教程进行修改后的代码，我们增加了一个scriptlet：

<HTML>

<BODY>

<%

// This is a scriptlet. Notice that the "date"

// variable we declare here is available in the

// embedded expression later on.

System.out.println( "Evaluating date now" );

java.util.Date date = new java.util.Date();

%>

Hello! The time is now <%= date %>

</BODY>

</HTML>

如果你运行上面的例子，你将会注意到“System.out.println”输出在服务器日志中。这是一个方便的方法来作一些简单的调试。有些服务器在IDE中有调试JSP的功能，你可以参见服务器文档来查看是否有这种功能。


JSP由浅入深(5)—— Scriptlets和HTML的混合

作者：风之彩

日期：2001-6-8 17:35:58

在前面的教程中我们已经在一个scriptlet中使用了“out”变量来产生HTML输出。对于更复杂的HTML，如果我们还是使用“out”变量那就会失去JSP编程的许多优势。其实我们可以很简单地就实现Scriptlets 和 HTML的混合。

假如你要在HTML产生一个表格。产生表格是一个普通的操作，在实践中你可能想从一个SQL表格或者从文件的行产生一个表格。为了是所举的例子尽量的简单，我们产生一个表格，使这个表格包含从1到N的数字。虽然以下的例子不是很有用，但是你从中可以学到一些技巧的。以下就是JSP的程序片段：

<TABLE BORDER=2>

<%

for ( int i = 0; i < n; i++ ) {

%>

<TR>

<TD>Number</TD>

<TD><%= i+1 %></TD>

</TR>

<%

}

%>

</TABLE>

在编制代码之前，你应该首先定义一个整型（int）变量“n”，利用这个变量我们要输出“n”行的表格。从上面的代码中我们可以注意到%> 和 <%字符出现在“for”循环中，原来这是为了使你退到HTML然后又回到scriptlet，其实这正是Scriptlets 和 HTML的混合的技巧。

上面的代码其实很简单：可以退出scriptlet的时候，就编写HTML；然后又回到scriptlet中去。任何的循环控制表达式，比如“while”或者“for”循环以及“if”语句都可以控制HTML。如果HTML处在一个循环中它就会在每一次的循环中执行一次。


jsp与applet通信

此电子文档属C#乐园QQ群3256045整理。
jsp与applet通信

上一页 目录 下一页

jsp与applet通信

作者：jspfuns

日期：2000-8-9 11:36:50

作者/出处：Blueski编译

很简单，你可以使用 java.net.URLConnection 和 java.net.URL类来打开一个标准的 HTTP连接，与web 服务器取得连接。服务器随后以通常的方式将这一信息传送给servlet。基本上，applet可以看作是一个a web浏览器，servlet并不能区分这2者。对servlet来说，applet仅仅是另一个HTTP客户。

(当然，你可以写一个servlet，专门由你的applet对它进行调用，在这种情况下servlet确实能知道2者的差别。你也可以在一个客户段TCP端口打开一个ServerSocket，然后让你的applet打开一个 Socket连接。然后你必须设计和实现一个客户段socket-level协议来对通信进行处理。这就是你可以写的东西。例如，一个聊天applet和一个servlet进行通信。总之，做一个客户端协议相比用HTTP要花费更多工作量，但更灵活。但是，客户端协议要花费长时间来通过防火墙。)

如要看更详细资料，你可以参考下列页面：

http://www.sun.com/software/jwebserver/faq/faq.html

Questions C8

http://www.sun.com/software/jwebserver/faq/faq.html#c8

和 C9

http://www.sun.com/software/jwebserver/faq/faq.html#c9

Chad Darby有一篇带源程序的文章在：

http://www.j-nine.com/pubs/applet2servlet/index.htm.

Netscape DevEdge Online 也有类似文章 - 企业应用中Applet-to-Servlet通信

http://developer.netscape.com/viewsource/index_frame.html?content=fields_servlet/fields_servlet.html


jsp与ejb通信 （转）

作者：jeru

日期：2001-3-6 11:23:09

jsp与ejb通信

Blueski编译

以下是一个snippet代码，演示了JSP页面如何与 EJB session bean进行相互作用。

<%@ page import="javax.naming.*, javax.rmi.PortableRemoteObject,

foo.AccountHome, foo.Account" %>

<%!

//declare a "global" reference to an instance of the home interface of the session bean

AccountHome accHome=null;

public void jspInit() {

//obtain an instance of the home interface

InitialContext cntxt = new InitialContext( );

Object ref= cntxt.lookup("java:comp/env/ejb/AccountEJB");

accHome = (AccountHome)PortableRemoteObject.narrow(ref,AccountHome.class);

}

%>

<%

//instantiate the session bean

Account acct = accHome.create();

//invoke the remote methods

acct.doWhatever(...);

// etc etc...

%>

在JSP中java代码应该越少越好。

在以上例子中，JSP设计者不得不处理和理解存取EJB的机理。 代替 在一个Mediator中对EJB机制的压缩以及将EJB方法作为Mediator的方法，

可以在jsp中使用 Mediator。Mediator通常由EJB设计者编写。Mediator可以提供附加的值如attribute caching等.

*****

JSP scriptlet代码必须最小化。如果要在jsp中直接请求ejb可能要在jsp 中写许多代码，包括try...catch等函数块来进行操作。

使用一个标准的JavaBean作为一个jsp和EJB服务器的中介可以减少在jsp中的java代码的数量，并可提高可重用性。这个JavaBean必须是一个你所存取的EJB的覆盖(wrapper)。

如果你使用标准的JavaBean，你可以使用 jsp:useBean标记来初始化EJB参数，如服务器URL和服务器

安全参数等。

自定义标记也可以是一种选择。但是，这要求比一个简单JavaBean wrapper更多的编码。

该处必须被重新写为尽可能小的代码并保证JSP脚本内容尽可能轻(light)。


jsp与ejb通信

作者：jspfuns

日期：2000-8-12 2:32:37

Blueski编译

以下是一个snippet代码，演示了JSP页面如何与 EJB session bean进行相互作用。

<%@ page import="javax.naming.*, javax.rmi.PortableRemoteObject,

foo.AccountHome, foo.Account" %>

<%!

//declare a "global" reference to an instance of the home interface of the session bean

AccountHome accHome=null;

public void jspInit() {

//obtain an instance of the home interface

InitialContext cntxt = new InitialContext( );

Object ref= cntxt.lookup("java:comp/env/ejb/AccountEJB");

accHome = (AccountHome)PortableRemoteObject.narrow(ref,AccountHome.class);

}

%>

<%

//instantiate the session bean

Account acct = accHome.create();

//invoke the remote methods

acct.doWhatever(...);

// etc etc...

%>

在JSP中java代码应该越少越好。

在以上例子中，JSP设计者不得不处理和理解存取EJB的机理。 代替 在一个Mediator中对EJB机制的压缩以及将EJB方法作为Mediator的方法，

可以在jsp中使用 Mediator。Mediator通常由EJB设计者编写。Mediator可以提供附加的值如attribute caching等.

*****

JSP scriptlet代码必须最小化。如果要在jsp中直接请求ejb可能要在jsp 中写许多代码，包括try...catch等函数块来进行操作。

使用一个标准的JavaBean作为一个jsp和EJB服务器的中介可以减少在jsp中的java代码的数量，并可提高可重用性。这个JavaBean必须是一个你所存取的EJB的覆盖(wrapper)。

如果你使用标准的JavaBean，你可以使用 jsp:useBean标记来初始化EJB参数，如服务器URL和服务器

安全参数等。

自定义标记也可以是一种选择。但是，这要求比一个简单JavaBean wrapper更多的编码。

该处必须被重新写为尽可能小的代码并保证JSP脚本内容尽可能轻(light)。


JSP与JavaBean --JSP开发入门三

作者：jeru

日期：2000-12-20 11:30:16

虽然你可以在小型指令文件里放入一大块的程序代码，但是大多数的Java程序代码是属于可以重复使用的组件，称为JavaBean。JavaBean就跟ActiveX控件一样:它们提供已知的功能，并且是为了可随时重复使用的目的而设计的。

    JavaBean的价值在于它可以经由一组特性来使用，而这些特性则提供对JavaBean设定的存取。以人来作范例，此人就是JavaBean，而他的姓名，社会福利安全号码，以及住址可以是特性。对于JSP网站，基本上你是将'JavaBean'动态的连接到你的网站。

    假设JavaBean是在建立网站之前建好的，你要做的第一件事是告诉JSP网页它所需要使用JavaBean.这工作可以用<jsp:useBean>卷标来完成：<jsp:useBean id="localName" class="com.jguru.Person" scope="application" />.

    <jsp:useBean>卷标需要你以id 属性来辨识豆子.在这里，你提供一个名称让JSP网页来辨识豆子，除了id 属性之外，你也必须告诉网页要到哪里去找这个豆子，或者是它的Java类别名称。类别属性提供如何在各式方法之中找到它，最后一个需要的组件是scope 属性.有了范围属性的帮助，你可以告诉豆子，要它为单一网页(预设)[scope="page"]；为一个被请求的网页[scope="request"]；为通话期[scope="session"]；或为整个应用程序[scope="application"]来维护它自己的信息.对于通话期范围，你可以很容易的维护JSP网页里的项目，例如购物车。

    一但你宣告了JavaBean之后，你就可以存取它的特性来订定它。要取得一特性的值，使用<jsp:getProperty>卷标。有了<jsp:getProperty>卷标，你可以指定要使用的豆子名称(从useBean的id字段)，以及你要取得值的特性。接着，真正的值就会放在输出里：<jsp:getProperty id="localName" property="name" />.

    要更改JavaBean的特性，你需要使用<jsp:setProperty>卷标.对这个卷标，你也需要辨认豆子以及要修正的特性，除此之外，你还需要提供新值.如果命名正确，这些可以直接经由规定的格式取得：<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 %> />.

    有关JavaBean的最后一点:要让网络服务器可以找到JavaBean，你需要将它们的类别档案放在特别位置。对JSWDK而言，最简单的地方是在安装目录里的类别目录，例如 \jswdk-1.0.1\classes.


JSP与Servlets的区别

作者：hxfwsk

email: hxfwsk@hotmail.com

日期：2001-7-6 10:41:58

JSP和SERVLET到底在应用上有什么区别，很多人搞不清楚。我来胡扯几句吧。简单的说，SUN首先发展出SERVLET，其功能比较强劲，体系设计也很先进，只是，它输出HTML语句还是采用了老的CGI方式，是一句一句输出，所以，编写和修改HTML非常不方便。

  后来SUN推出了类似于ASP的镶嵌型的JSP，把JSP TAG镶嵌到HTML语句中，这样，就大大简化和方便了网页的设计和修改。新型的网络语言如ASP，PHP，JSP都是镶嵌型的SCRIPT语言。

  从网络三层结构的角度看，一个网络项目最少分三层：data layer,business layer, presentation layer。当然也可以更复杂。SERVLET用来写business layer是很强大的，但是对于写presentation layer就很不方便。JSP则主要是为了方便写presentation layer而设计的。当然也可以写business layer。写惯了ASP，PHP，CGI的朋友，经常会不自觉的把presentation layer和business layer混在一起。就象前面那个朋友，把数据库处理信息放到JSP中，其实，它应该放在business layer中。

  根据SUN自己的推荐，JSP中应该仅仅存放与presentation layer有关的东东，也就是说，只放输出HTML网页的部份。而所有的数据计算，数据分析，数据库联结处理，统统是属于business layer，应该放在JAVA BEANS中。通过JSP调用JAVA BEANS，实现两层的整合。

  实际上，微软前不久推出的DNA技术，简单说，就是ASP+COM/DCOM技术。与JSP+BEANS完全类似，所有的presentation layer由ASP完成，所有的business layer由COM/DCOM完成。通过调用，实现整合。

为什么要采用这些组件技术呢？因为单纯的ASP/JSP语言是非常低效率执行的，如果出现大量用户点击，纯SCRIPT语言很快就到达了他的功能上限，而组件技术就能大幅度提高功能上限，加快执行速度。

  另外一方面，纯SCRIPT语言将presentation layer和business layer混在一起，造成修改不方便，并且代码不能重复利用。如果想修改一个地方，经常会牵涉到十几页CODE，采用组件技术就只改组件就可以了。

综上所述，SERVLET是一个早期的不完善的产品，写business layer很好，写presentation layer就很臭，并且两层混杂。

  所以，推出JSP+BAEN，用JSP写presentation layer,用BAEN写business layer。SUN自己的意思也是将来用JSP替代SERVLET。

  可是，这不是说，学了SERVLET没用，实际上，你还是应该从SERVLET入门，再上JSP，再上JSP+BEAN。

  强调的是：学了JSP，不会用JAVA BEAN并进行整合，等于没学。大家多花点力气在JSP+BEAN上。

在补充几句：

  我们可以看到，当ASP+COM和JSP+BEAN都采用组件技术后，所有的组件都是先进行编译，并驻留内存，然后快速执行。所以，大家经常吹的SERVLET/JSP先编译驻内存后执行的速度优势就没有了。

反之，ASP+COM+IIS+NT紧密整合，应该会有较大的速度优势呈现。而且，ASP+COM+IIS+NT开发效率非常高，虽然BUG很多。

  那么，为什么还用JSP+BEAN？因为JAVA实在前途远大。微软分拆后，操作系统将群雄并起，应用软件的开发商必定要找一个通用开发语言进行开发，JAVA一统天下的时机就到了。如果微软分拆顺利，从中分出的应用软件公司将成为JAVA的新领导者。目前的JAVA大头SUN和IBM都死气沉沉，令人失望。希望新公司能注入新活力。不过，新公司很有可能和旧SUN展开JAVA标准大战，双方各自制定标准，影响JAVA夸平台。

  另外，现在的机器速度越来越快，JAVA的速度劣势很快就可以被克服。


JSP语法（1）——HTML注释

作者：风之彩

email: zhonggb@163.net

日期：2001-6-29 14:27:13

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语法（10）——jspinclude

作者：风之彩

email: zhonggb@163.net

日期：2001-6-29 14:32:05

<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语法（11）——jspplugin

作者：风之彩

email: zhonggb@163.net

日期：2001-6-29 14:32:31

<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语法（12）——jspuseBean

作者：风之彩

email: zhonggb@163.net

日期：2001-6-29 14:34:02

<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语法（13）——jspuseBean

作者：风之彩

email: zhonggb@163.net

日期：2001-6-29 14:34:51

<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语法（2）——隐藏注释

作者：风之彩

email: zhonggb@163.net

日期：2001-6-29 14:27:49

隐藏注释

写在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语法（3）——声明

作者：风之彩

email: zhonggb@163.net

日期：2001-6-29 14:28:23

声明

在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语法（4）——表达式

作者：风之彩

email: zhonggb@163.net

日期：2001-6-29 14:28:49

表达式

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

JSP 语法

<%= expression %>

例子

<font color="blue"><%= map.size() %></font>

<b><%= numguess.getHint() %></b>.

描述

表达式元素表示的是一个在脚本语言中被定义的表达式，在运行后被自动转化为字符串，然后插入到这个表达示在JSP文件的位置显示。因为这个表达式的值已经被转化为字符串，所以你能在一行文本中插入这个表达式（形式和ASP完全一样).

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

你不能用一个分号（"；"）来作为表达式的结束符.但是同样的表达式用在scriptlet中就需要以分号来结尾了！查看Scriptlet 这个表达式元素能够包括任何在Java Language Specification中有效的表达式.

有时候表达式也能做为其它JSP元素的属性值.一个表达式能够变得很复杂，它可能由一个或多个表达式组成，这些表达式的顺序是从左到右。


JSP语法（5）——Scriptlet

作者：风之彩

email: zhonggb@163.net

日期：2001-6-29 14:29:13

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对象中。


JSP语法（6）——Page指令

作者：风之彩

email: zhonggb@163.net

日期：2001-6-29 14:29:36

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文件的顶部.


JSP语法（7）——Taglib指令

作者：风之彩

email: zhonggb@163.net

日期：2001-6-29 14:30:40

Taglib 指令

定义一个标签库以及其自定义标签的前缀.

JSP 语法

<%@ taglib uri="URIToTagLibrary" prefix="tagPrefix" %>

例子

<%@ taglib uri="http://www.jspcentral.com/tags"prefix="public" %>

<public:loop>

.

.

</public:loop>

描述

<% @ taglib %>指令声明此JSP文件使用了自定义的标签，同时引用标签库，也指定了他们的标签的前缀。

这里自定义的标签含有标签和元素之分。因为JSP文件能够转化为XML,所以了解标签和元素之间的联系很重要。标签只不过是一个在意义上被抬高了点的标记，是JSP元素的一部分。JSP元素是JSP语法的一部分，和XML一样有开始标记和结束标记。元素也可以包含其它的文本，标记，元素。比如，一个jsp:plugin元素有<jsp:plugin>开始标记和</jsp:plugin>结束标记，同样也可以有<jsp:params>和<jsp:fallback>元素.

你必须在使用自定义标签之前使用<% @ taglib %>指令，而且你可以在一个页面中多次使用，但是前缀只能使用一次

属性

uri="URIToTagLibrary"

Uniform Resource Identifier (URI)根据标签的前缀对自定义的标签进行唯一的命名，URI可以是以下的内容:

Uniform Resource Locator (URL), 由 RFC 2396 定义, 查看 http://www.hut.fi/u/jkorpela/rfc/2396/full.html

Uniform Resource Name (URN), 由 RFC 2396定义

一个相对或绝对的路径

prefix="tagPrefix"

在自定义标签之前的前缀，比如，在<public:loop>中的public，如果这里不写public，那么这就是不合法的。请不要用jsp, jspx, java, javax, servlet, sun, 和sunw做为你的前缀


JSP语法（8）——jspforward

作者：风之彩

email: zhonggb@163.net

日期：2001-6-29 14:31:13

<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语法（9）——jspgetProperty

作者：风之彩

email: zhonggb@163.net

日期：2001-6-29 14:31:45

<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语法，英文版，PDF格式

此电子文档属C#乐园QQ群3256045整理。
JSP语法，英文版，PDF格式

上一页 目录 下一页

JSP语法，英文版，PDF格式

作者：何志强

email: hhzqq@21cn.com

日期：2000-8-10 14:57:15

[url]http://java.sun.com/products/jsp/syntax.pdf[/url]

真得学JSP，还得看看JAVA相关的资料哦，：）


JSP语法的基本原理--JSP开发入门二

作者：jeru

日期：2000-12-20 11:29:21

安装之后，接下来我们要讨论JSP的语法.如果要偷懒，你可以下载语法卡而如果你不熟悉 Java的程序设计，你可能会想要参考Sun的使用手册；然而，网站建立者不应该做太多的Java发展。除了几个函式呼叫之外，出现在你JSP网页上的Java程序代码应该将它减到最少;

    记住这点之后，现在让我们先来看看JSP的编译器指引与指令组件，之后我们将解释JavaBeans与内部对象. JSP编译器指引与指令组件有五种型态.JSP 1.0之后，大部分的JSP是包含在以<% 作为开始%>作为结束的单一卷标里.新的 JSP 1.1规格已经发表了，它同时也与XML兼容.

JSP的编译器指引与指令组件

    编译器指示　　　　　　　<%@ 编译器指示 %>

    声明　　　　　　　　　　<%! 声明 %>

    表达式 　　　　　　　　<%= 表达式 %>

    程序代码段/小型指令　　　<% 程序代码片段 %>

    注释　　　　　　　　　　<%-- 注释 --%>

    编译器指示

    JSP的编译器指示是针对JSP引擎。它们并不会直接产生任何看得见的输出；相反的，它们是在告诉引擎如何处理其它的JSP网页。它们永远包含在 <%@ ?%>卷标里。两个主要的指引是 page与include。我们不会讨论taglib编译器指引但它可以在JSP1.1里用来建立自订卷标。

    你几乎可以在你所有的JSP网页最上面找到page编译器指示。虽然这不是必须的，但它可以让你指定到哪里可以找到支持的Java类别这类的事：

    <%@ page import="java.util.Date" %>，

    当发生Java问题的事件时应该将讯息传送到哪里：

    <%@ page errorPage="errorPage.jsp" %>，

    以及你是?需要为使用者管理通话期的信息，可能存取多个网页(稍后在JavaBeans里会有更多通话期的讨论):

    <%@ page session="true" %>。

  include编译器指示让你将你的内容分成几个可管理的组件，就像那些有表头或脚注的网页。所包含的网页可以是固定格式的HTML网页或者是JSP内容的网页:

    <%@ include file="filename.jsp" %>。

    宣告

    JSP声明让你定义网页层的变量，来储存信息或定义支持的函式，让JSP网页的其余部分能够使用。如果你发现自己有太多的程序代码，你最好将它们放在不同的Java类别里。你可以在 <%! ?%>卷标里找到声明。记住要在变量声明的后面加上分号，就跟任何有效的Java叙述的形式一样: <%! int i=0; %>。

    表达式

    JSP里有表达式，评估表达式的结果可以转换成字符串并且直接使用在输出网页上。JSP运算是属于 <%= ?%> 卷标里，并不包含分号，加引号字符串的无用部分。

    <%= i %>

    <%= "Hello" %> 。

    程序代码段/小型指令文件

    JSP程序代码片段或小型指令文件是包含在<% ?%> 卷标里。当网络服务器接受这段请求时，这段Java程序代码会执行。小型指令文件可以是原始的HTML或XML，其内部的程序代码片段可以让你建立有条件的执行程序代码，或者只是一些使用另一块程序代码的东西。举例来说，下列的程序代码结合了表达式与小型指令文件，在H1，H2，H3，以及H4卷标里显示字符串"Hello"。小型指令文件不限于一行的原始程序代码 :

    <% for (int i=1; i<=4; i++) { %>

    <H<%=i%>>Hello</H<%=i%>>

    <% } %>。

    注释

    最后一个主要JSP组件是嵌入式注释。虽然你可以在你的档案里包含HTML注释，如果使用者检视网页的原始码，他们也会看到这些注释。如果你不要让使用者看到你的批注，你可以将它放在<%-- ?--%>卷标里:

    <%-- 针对服务器端的注释 --%>。


jsp源码实例1(输出）

作者：blackwhites

日期：2000-9-30 10:04:54

package coreservlets;

import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

/** Simple servlet that generates HTML.

 *  <P>

 *  Taken from Core Servlets and JavaServer Pages

 *  from Prentice Hall and Sun Microsystems Press,

 *  http://www.coreservlets.com/.

 *  &copy; 2000 Marty Hall; may be freely used or adapted.

 */

public class HelloWWW2 extends HttpServlet {

  public void doGet(HttpServletRequest request,

                    HttpServletResponse response)

      throws ServletException, IOException {

    response.setContentType("text/html");

    PrintWriter out = response.getWriter();

    String docType =

      "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 " +

      "Transitional//EN\">\n";

    out.println(docType +

                "<HTML>\n" +

                "<HEAD><TITLE>Hello WWW</TITLE></HEAD>\n" +

                "<BODY>\n" +

                "<H1>Hello WWW</H1>\n" +

                "</BODY></HTML>");

  }

}


jsp源码实例2(获取表单参数）

作者：blackwhites

日期：2000-9-30 10:06:33

package coreservlets;

import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

import java.util.*;

/** Shows all the parameters sent to the servlet via either

 *  GET or POST. Specially marks parameters that have

 *  no values or multiple values.

 *  <P>

 *  Taken from Core Servlets and JavaServer Pages

 *  from Prentice Hall and Sun Microsystems Press,

 *  http://www.coreservlets.com/.

 *  &copy; 2000 Marty Hall; may be freely used or adapted.

 */

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 = "Reading All Request Parameters";

    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>Parameter Name<TH>Parameter Value(s)");

    Enumeration paramNames = request.getParameterNames();

    while(paramNames.hasMoreElements()) {

      String paramName = (String)paramNames.nextElement();

      out.print("<TR><TD>" + paramName + "\n<TD>");

      String[] paramValues =

        request.getParameterValues(paramName);

      if (paramValues.length == 1) {

        String paramValue = paramValues[0];

        if (paramValue.length() == 0)

          out.println("<I>No Value</I>");

        else

          out.println(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);

  }

}


jsp源码实例3(获取jsp各种参数）

此电子文档属C#乐园QQ群3256045整理。
jsp源码实例3(获取jsp各种参数）

上一页 目录 下一页

jsp源码实例3(获取jsp各种参数）

作者：blackwhites

日期：2000-9-30 10:08:35

package coreservlets;

import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

import java.util.*;

/** Creates a table showing the current value of each

 *  of the standard CGI variables.

 *  <P>

 *  Taken from Core Servlets and JavaServer Pages

 *  from Prentice Hall and Sun Microsystems Press,

 *  http://www.coreservlets.com/.

 *  &copy; 2000 Marty Hall; may be freely used or adapted.

 */

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 = "Servlet Example: Showing CGI Variables";

    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>");

  }

  /** POST and GET requests handled identically. */

  public void doPost(HttpServletRequest request,

                     HttpServletResponse response)

      throws ServletException, IOException {

    doGet(request, response);

  }

}


jsp源码实例4(cookie）

作者：blackwhites

日期：2000-9-30 10:11:45

package coreservlets;

import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

/** Sets six cookies: three that apply only to the current

 *  session (regardless of how long that session lasts)

 *  and three that persist for an hour (regardless of

 *  whether the browser is restarted).

 *  <P>

 *  Taken from Core Servlets and JavaServer Pages

 *  from Prentice Hall and Sun Microsystems Press,

 *  http://www.coreservlets.com/.

 *  &copy; 2000 Marty Hall; may be freely used or adapted.

 */

public class SetCookies extends HttpServlet {

  public void doGet(HttpServletRequest request,

                    HttpServletResponse response)

      throws ServletException, IOException {

    for(int i=0; i<3; i++) {

      // Default maxAge is -1, indicating cookie

      // applies only to current browsing session.

      Cookie cookie = new Cookie("Session-Cookie-" + i,

                                 "Cookie-Value-S" + i);

      response.addCookie(cookie);

      cookie = new Cookie("Persistent-Cookie-" + i,

                          "Cookie-Value-P" + i);

      // Cookie is valid for an hour, regardless of whether

      // user quits browser, reboots computer, or whatever.

      cookie.setMaxAge(3600);

      response.addCookie(cookie);

    }

    response.setContentType("text/html");

    PrintWriter out = response.getWriter();

    String title = "Setting Cookies";

    out.println

      (ServletUtilities.headWithTitle(title) +

       "<BODY BGCOLOR=\"#FDF5E6\">\n" +

       "<H1 ALIGN=\"CENTER\">" + title + "</H1>\n" +

       "There are six cookies associated with this page.\n" +

       "To see them, visit the\n" +

       "<A HREF=\"/servlet/coreservlets.ShowCookies\">\n" +

       "<CODE>ShowCookies</CODE> servlet</A>.\n" +

       "<P>\n" +

       "Three of the cookies are associated only with the\n" +

       "current session, while three are persistent.\n" +

       "Quit the browser, restart, and return to the\n" +

       "<CODE>ShowCookies</CODE> servlet to verify that\n" +

       "the three long-lived ones persist across sessions.\n" +

       "</BODY></HTML>");

  }

}


jsp源码实例4(搜索引擎）

作者：blackwhites

日期：2000-9-30 10:10:33

package coreservlets;

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 {

    String searchString = request.getParameter("searchString");

    if ((searchString == null) ||

        (searchString.length() == 0)) {

      reportProblem(response, "Missing search string.");

      return;

    }

    // The URLEncoder changes spaces to "+" signs and other

    // non-alphanumeric characters to "%XY", where XY is the

    // hex value of the ASCII (or ISO Latin-1) character.

    // Browsers always URL-encode form values, so the

    // getParameter method decodes automatically. But since

    // we're just passing this on to another server, we need to

    // re-encode it.

    searchString = URLEncoder.encode(searchString);

    String numResults = request.getParameter("numResults");

    if ((numResults == null) ||

        (numResults.equals("0")) ||

        (numResults.length() == 0)) {

      numResults = "10";

    }

    String searchEngine =

      request.getParameter("searchEngine");

    if (searchEngine == null) {

      reportProblem(response, "Missing search engine name.");

      return;

    }

    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;

      }

    }

    reportProblem(response, "Unrecognized search engine.");

  }

  private void reportProblem(HttpServletResponse response,

                             String message)

      throws IOException {

    response.sendError(response.SC_NOT_FOUND,

                       "<H2>" + message + "</H2>");

  }

  public void doPost(HttpServletRequest request,

                     HttpServletResponse response)

      throws ServletException, IOException {

    doGet(request, response);

  }

}


JSP中变量的定义域

作者：jspfuns

日期：2000-8-25 21:27:30

在<% .... %>定义的变量是JSP的本地变量，它们对于外部函数是不可见的，即使这些函数和变量在同一页面中声明。如：

<%

int evilVariable = "666";

%>

...

function testFunction() {

//在函数内部，变量evilVariable是不可见的

}

为什么呢？这是因为变量evilVariable最终成为结果servlet中service()方法的一个本地变量，因此它不能被结果servlet的其它方法存取。

对于在<%! %>中声明的变量将作为全局变量。如：

<%!

int evilVariable = "666";

%>

...

function testFunction() {

int x = evilVariable; //能够使用变量evilVariable

}

这是因为通过这种方式定义的evilVariable变量变为了结果servlet的一个私有成员变量，因此它可以被servlet中的所有的方法存取。

结论

理解以上的概念是很重要的，因为在servlet环境中，对于一个JSP页只允许有一个结果servlet的实例在运行和响应所有的页面请求。因此，在结果servlet中所有的成员变量可以被所有的请求所共享，而service()方法中的本地变量只能每响应一次请求就重建一次。故我们将变量放置到<SERVER></SERVER>标签中时，应注意以上的规则。


Jsp中的内部对象，及其所对应的JAVA对象

作者：sonymusic

email: sonymusic@china.com

日期：7/27/2001 11:59:53 AM

很多人都会问，JSP中的SESSION对象怎么用啊，什么的问题，在我看来，这并不是什么问题，因为SERVLET API中都有详细说明的。这张表格列出Jsp中的内部对象，及其所对应的JAVA对象。具体使用的时候就可以直接查找相应的SERVLET API啦。[TABLE][TR][TD]request [/TD][TD]javax.servlet.http.HttpServletRequest [/TD][/TR][TR][TD]response[/TD][TD]javax.servlet.http.HttpServletResponse[/TD][/TR][TR][TD]out[/TD][TD]javax.servlet.jsp.JspWriter[/TD][/TR][TR][TD]page[/TD][TD]javax.servlet.jsp.JspPage[/TD][/TR][TR][TD]pageContext[/TD][TD]javax.servlet.jsp.PageContext[/TD][/TR][TR][TD]session[/TD][TD]javax.servlet.http.HttpSession[/TD][/TR][TR][TD]application[/TD][TD]javax.servlet.ServletContext[/TD][/TR][TR][TD]config[/TD][TD]javax.servlet.ServletConfig[/TD][/TR][/TABLE]


jsp中同时应用“存储过程”和“javabean”的例子

作者：asp2001

email: asp2001@163.net

日期：2000-8-12 11:14:26

存储过程和javabean的应用

存储过程sp_shop_base：

Create Procedure sp_shop_base (@shop_id int,@msg char(50) output)

As

  SELECT shop_name, shop_phone, shop_email, shop_cash, shop_address

  FROM shop_shop

  WHERE shop_id = @shop_id

  SELECT @msg = 'Retrieval Finished'

  return 1

程序：

<%@ page contentType="text/html;charset=gb2312"%>

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=gb2312">

<title>JSP 存储过程 - javabean - JDBC-ODBC - SQL Server </title>

</head>

<body>

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

<jsp:useBean id="bka" scope="page" class="shop.bka" />

<%

ResultSet RS = bka.executeQuery("{call sp_shop_base('2','')}");

while (RS.next()) {

out.println(RS.getString(1));

out.println(RS.getString(2));

out.println(RS.getString(3));

out.println(RS.getString(4));

out.println(RS.getString(5));

}

RS.close();

%>

__________________________________________________


JSP中文留言簿

作者：jspfuns

日期：2000-8-10 17:29:28

<html>

<head>

<title>My First Jsp GuestBook</title>

<meta http-equiv="Content-Type" content="text/html; charset=gb2312">

</head>

<body bgcolor="#CCCCFF" vlink="#000000" alink="#666666">

<div align="center">

<p><font size="7"><b><font size="+6">Jsp GuestBook</font></b></font></p>

<form method="post" action="index.jsp">

<table width="75%" align="center" border=1>

<tr>

<td width="30%">

<div align="right">Name:</div>

</td>

<td width="70%">

<input type="text" name="name">

</td>

</tr>

<tr>

<td width="30%">

<div align="right">E-mail:</div>

</td>

<td width="70%">

<input type="text" name="email">

</td>

</tr>

<tr>

<td width="30%">

<div align="right">Site Url:</div>

</td>

<td width="70%">

<input type="text" name="url" value="http://">

</td>

</tr>

<tr>

<td width="30%">

<p>What do you want to</p>

<p>say?</p>

</td>

<td width="70%">

<textarea name="body" rows="5" cols="30"></textarea>

</td>

</tr>

<tr>

<td width="30%"> </td>

<td width="70%"> </td>

</tr>

<tr>

<td width="30%"> </td>

<td width="70%">

<table width="75%">

<tr>

<td>

<input type="submit" name="send" value="Send" >

</td>

<td>

<input type="reset" name="reset" value="Reset">

</td>

</tr>

</table>

</td>

</tr>

</table>

</form>

<hr>

<%@ page contentType="text/html;charset=gb2312"%>

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

<jsp:useBean id="sqlbean" scope="page" class="jsp.sql_data" />

<%! String name,email,url,body,sql; %>

<%! int id; %>

<%

name = null;

name = request.getParameter("name");

//name = new String(request.getParameter("name").getBytes("8859_1"));

%>

<%= name%>

<%

if (name != null&&name !="")

{

name = new String(request.getParameter("name").getBytes("8859_1"));

email = new String(request.getParameter("email").getBytes("8859_1"));

url = new String(request.getParameter("url").getBytes("8859_1"));

body = new String(request.getParameter("body").getBytes("8859_1"));

ResultSet RSet = sqlbean.executeQuery("SELECT * FROM guestbook");

RSet.last();

id = RSet.getRow()+1;

RSet.close();

sqlbean.sqlclose();

sql = "insert into guestbook (Recordid,Name,Email,Url,Body) values('"+id+"','"+name+"','"+email+"','"+url+"','"+body+"')";

sqlbean.executeQuery(sql);

sqlbean.sqlclose();

}else{

out.print("<b><font color=red> Please enter your name,email and body!!!</font></b>");

}

%>

<p><font size="4"><b><a href="./index.jsp#see">See My Guestbook</a></b></font></p>

<p> </p>

<%

ResultSet RS = sqlbean.executeQuery("SELECT * FROM guestbook ORDER BY RecordID DESC");

while (RS.next()){

name = new String(RS.getString("Name").getBytes("8859_1"));

email = new String(RS.getString("Email").getBytes("8859_1"));

url = new String(RS.getString("Url").getBytes("8859_1"));

body = new String(RS.getString("Body").getBytes("8859_1"));

%>

<table border=1 width=80%>

<tr>

<td width=30%>Name:</td><td width=70%><%= name %>

</tr>

<tr>

<td width=30%>Email:</td><td width=70%><a href="mailto:<%= email %>"><%= email %></a></td>

</tr>

<tr>

<td width=30%>SiteUrl:</td><td width=70%><a href="<%= url %>"><%= url %></a></td>

</tr>

<tr>

<td width=30%>Body:</td><td width=70%><%= body %></td>

</tr>

</table>

<hr>

<%

}

RS.close();

sqlbean.sqlclose();

%>

</div>

</body>

</html>


JSP中文问题解决方案。

作者：sonymusic

email: sonymusic@china.com

日期：2001-5-23 12:32:18

只需要保持下面几处地方设置统一，你的JSP就不会有中文问题

1、JSP的charset，

2、你的JSP　SERVER编译JSP是使用的字符集，默认和操作系统是一样的。你可能需要进行修改

3、你的BEAN编译时候所采用的字符集，一般用JAVAC编译，都是和操作系统一样的，但你可以通过带参数来改变。如果用其它的编程工具，可以编译的话，你可能需要修改其默认参数。

4、和数据库连接时，大多数JDBC驱动程序都可以设置其字符集的，这样JDBC驱动会自己进行转换，也不会出现汉字问题。

以jConnect为例，就有这样一个属性参数：

CHARSET

 Specifies the character set for strings passed through TDS. If you specify a CHARSET, it must match a CHARSET listed in syscharsets.

If null, jConnect uses the server’s default CHARSET.

明白？jdbc-odbc桥也有这样的参数的(charSet)。 不同的驱动，请参阅文档。

一般来说，只要你做到了以上几点，你的JSP几乎（根本各人的配置吧）不会再有中文问题了！

不过我还是LINUX下的RESIN中碰到一个小问题，那就是URL中不能传递中文。我没有想通。


js与applet的相互调用。

作者：wflm

email: wflm@263.net

日期：6/22/2001 1:57:45 AM

希望与大家多多交流！8-）

js调用java

可以在网页里使用

<script language=javascript>

document.applets[0].java中的方法名

</script>

可以对applet进行控制，也可以从applet返回值。

返回值无需关心值得类型，js会隐式的转换。

传值到applet中应考虑类型匹配。

java调用js

需要导入netscape.javascript.jsobject这个类

比如：

JSObject window=JSObject.getWindow(this);

JSObject window=(JSObject)window.getMember("document");

来获取对当前窗口的控制权。


Language Fundamentals

作者：jeru

email: jeru@163.net

日期：7/3/2001 9:54:36 AM

4) Language Fundamentals

Objective 1)

Identify correctly constructed package declarations import statements class declarations (of all forms including inner classes) interface declarations and implementations (for java.lang.Runnable or other interface described in the test) method declarations (including the main method that is used to start execution of a class) variable declarations and identifiers.

1.    Source file’s elements (in order)

a.    Package declaration

b.    Import statements

c.    Class definitions

2.    Importing packages doesn’t recursively import sub-packages.

3.    Sub-packages are really different packages, happen to live within an enclosing package. Classes in sub-packages cannot access classes in enclosing package with default access.

4.    Comments can appear anywhere. Can’t be nested.(No matter what type of comments)

5.    At most one public class definition per file. This class name should match the file name. If there are more than one public class definitions, compiler will accept the class with the file’s name and give an error at the line where the other class is defined.

6.    It’s not required having a public class definition in a file. Strange, but true. J In this case, the file’s name should be different from the names of classes and interfaces (not public obviously).

7.    A file can contain multiple non public classes, but bear in mind that this will produce separate .class output files for each class.

8.    Even an empty file is a valid source file.

9.    In order to be run by JVM, a class should have a main method with the following signature.

public static void main(String args[])

static public void main(String[] s)

10.    args array’s name is not important. args[0] is the first argument. args.length gives no. of arguments.

11.    main method can be overloaded.

12.    main method can be final.

13.    A class with a different main signature or w/o main method will compile. But throws a runtime error.

14.    A class without a main method can be run by JVM, if its ancestor class has a main method. (main is just a method and is inherited)

15.    An identifier must begin with a letter, dollar sign ($) or underscore (_). Subsequent characters may be letters, $, _ or digits.

16.    Currency symbols are treated as any other alphabetic character for the sake of naming identifiers (e.g., &pound;347_00, &micro;18, and &yen;Price are acceptable).

17.    An identifier cannot have a name of a Java keyword. Embedded keywords are OK. true, false and null are literals (not keywords), but they can’t be used as identifiers as well.

18.    const and goto are reserved words, but not used.

19.    Unicode characters can appear anywhere in the source code. The following code is valid.

    ch\u0061r a = 'a';

    char \u0062 = 'b';

    char c = '\u0063';

19. \u000a and \u000d will give compile error, use \n and \r instead.

Interfaces

·    All methods in an interface are implicitly public, abstract, and never static.(cannot has other modifier)

·    All variables in an interface are implicitly static, public, final. They cannot be transient or volatile. A class can shadow the variables it inherits from an interface, with its own variables.

·    A top-level interface itself cannot be declared as static or final since it doesn’t make sense.

·    A nested interface is any interface whose declaration occurs within the body of another class or interface.  In class, it can be private, but in interface, it has to be public.

·    A compile-time error occurs if an interface has the same simple name as any of its enclosing classes or interfaces.

·    Classes cannot implement an interface method with a static method, and the method has to be public.

·    If an interface specifies an exception list for a method, then the class implementing the interface need not declare the method with the exception list. But if the interface didn’t specify any exception list for a method, then the class cannot throw any exceptions.

·    A class can implement two interfaces that have a method with the same signature or variables with the same name.

·    An interface can extends more than one interfaces.

·    interfaces are not instantiated with new, but you can declare as,

test t = new A(); // class A  implements test

Objective 2)

State the correspondence between index values in the argument array passed to a main method and command line arguments.

This objective can catch out the more experienced C/C++ programmer because the first element of argv[] is the first string after the name of the program on the command line.

Objective 3)

Identify all Java programming language keywords.

A new keyword:  strictfp

true, false and null are literals (not keywords), but they can’t be used as identifiers as well.

Abstract        boolean        break        byte        case        catch

Char        class        const        continue        default        do

Double        else        extends        final        finally        float

For        goto        if         implements    import        instanceof

Int        interface        long         native        new         package

Private        protected     public        return         short        static

super         switch        synchronized     this         throw         throws

transient        try         void         volatile        while

Objective4)

State the effect of using a variable or array element of any kind when no explicit assignment has been made to it.

The default values for numeric types is zero, a boolean is false and an object reference is the only type that defaults to a null.

The value of the elements of an array of any base type will always be initialised to a default value, wherever the array is defined.

Objective 5)

State the range of all primitive data types and declare literal values for String and all primitive types using all permitted formats, bases and representations.

1.    Java has 8 primitive data types.

Data Type    Size (bits)    Initial Value    Min Value    Max Value

boolean    1    false     false    true

byte    8    0    -128 (-27)    127 (27 – 1)

short    16    0    -215     215 - 1

char    16    ‘\u0000’    ‘\u0000’ (0)    ‘\uFFFF’ (216 – 1)

int    32    0    -231     231 - 1

long    64    0L    -263     263 - 1

float    32    0.0F    1.4E-45    3.4028235E38

double    64    0.0    4.9E-324    1.7976931348623157E308

2.    All numeric data types are signed. char is the only unsigned integral type.

3.    The char type can be used to denote a Unicode character. Unicode is an alternative to ASCII that stores characters in 2 bytes instead of the 1 byte of ASCII(7 bits). This gives you 65K worth of characters,

4.    Octal literals begin with zero. Hex literals begin with 0X or 0x.

5.    Char literals are single quoted characters or unicode values (begin with \u).

6.    A number is by default an int literal, a decimal number is by default a double literal.

7.    1E-5d is a valid double literal, E2d is not (since it starts with a letter, compiler thinks that it’s an identifier)

8.    Primitives are passed by value.

9.    Objects (references) are passed by reference. The object reference itself is passed by value. So, it can’t be changed. But, the object can be changed via the reference.

·    Every primitive type has a wrapper class (some names are different – Integer, Boolean, Character)

·    Wrapper class objects are immutable.

·    All Wrapper classes are public, final, seriazible.

·    Integer, Long, Byte, Short, Double and Float extends java.lang.Number implements Comparable.

·    Character implements Comparable, Boolean doesn’t.

·    All wrapper classes, except Character, have a constructor accepting string. A Boolean object, created by passing a string, will have a value of false for any input other than “true” (case doesn’t matter).

·    Numeric wrapper constructors will throw a NumberFormatException, if the passed string is not a valid number. (empty strings and null strings also throw this exception)

·    equals also tests the class of the object, so even if an Integer object and a Long object are having the same value, equals will return false.

·    NaN’s can be tested successfully with equals method.

Float f1 = new Float(Float.NaN);

Float f2 = new Float(Float.NaN);

System.out.println( ""+ (f1 == f2)+"  "+f1.equals(f2)+ "  "+(Float.NaN == Float.NaN) );

The above code will print false true false.

·    Numeric wrappers have 6 methods to return the numeric value – intValue(), longValue(), etc.

·    valueOf method parses an input string (optionally accepts a radix in case of int and long) and returns a new instance of wrapper class, on which it was invoked. It’s a static method. For empty/invalid/null strings it throws a NumberFormatException. For null strings valueOf in Float and Double classes throw NullPointerException.

·    parseInt and parseLong return primitive int and long values respectively, parsing a string (optionally a radix). Throw a NumberFormatException for invalid/empty/null strings.

·    Numeric wrappers have overloaded toString methods, which accept corresponding primitive values (also a radix in case of int,long) and return a string.

·    Void class represents void primitive type. It’s not instantiable. Just a placeholder class.


Linux-JSP-JDBC-MySQL测试 - Insert

作者：何志强

email: hhzqq@21cn.com

日期：2000-8-10 18:03:15

<%--

作者：何志强[hhzqq@21cn.com]

日期：2000-08-04

功能：Linux-JSP-JDBC-MySQL测试 - Insert

--%>

<%

//声明

java.sql.Connection sqlConn;//数据库连接对象

java.sql.Statement sqlStmt;//语句对象

java.lang.String strName;//来自客户端的数据：姓名

long lngAge;//来自客户端的数据：年龄

//登记JDBC驱动程序

Class.forName("org.gjt.mm.mysql.Driver").newInstance();

//连接数据库

sqlConn = java.sql.DriverManager.getConnection("jdbc:mysql://localhost/test","test","test");

//创建语句对象

sqlStmt = sqlConn.createStatement();

//获取来自客户端的数据

strName = request.getParameter("name");

lngAge = java.lang.Long.parseLong(request.getParameter("age"),10);

//执行SQL语句

sqlStmt.executeUpdate("insert into test(name,age) values('"+strName+"',"+lngAge+")");

//关闭语句对象

sqlStmt.close();

//关闭数据库连接

sqlConn.close();

%>

<jsp:forward page="select.jsp" />


Linux-JSP-JDBC-MySQL测试 - Select

作者：何志强

email: hhzqq@21cn.com

日期：2000-8-10 18:02:27

<%--

作者：何志强[hhzqq@21cn.com]

日期：2000-08-04

功能：Linux-JSP-JDBC-MySQL测试 - Select

--%>

<%@ page contentType="text/html;charset=8859_1" %>

<%

//声明

java.sql.Connection sqlConn;//数据库连接对象

java.sql.Statement sqlStmt;//语句对象

java.sql.ResultSet sqlRst;//结果集对象

//登记JDBC驱动程序

Class.forName("org.gjt.mm.mysql.Driver").newInstance();

//连接数据库

sqlConn = java.sql.DriverManager.getConnection("jdbc:mysql://localhost/test","test","test");

//创建语句对象

sqlStmt = sqlConn.createStatement(java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE,java.sql.ResultSet.CONCUR_READ_ONLY);

//执行SQL语句

sqlRst = sqlStmt.executeQuery("select name,age from test");

%>

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=gb2312">

<title>Linux-JSP-JDBC-MySQL测试 - Select</title>

</head>

<body>

<table border="1" cellspacing="0" cellpadding="0" align="center">

<tr>

   <th>姓名</th>

   <th>年龄</th>

</tr>

<%while(sqlRst.next()){%>

<tr>

   <td><%=sqlRst.getString(1)%></td>

   <td><%=sqlRst.getLong(2)%></td>

</tr>

<%}%>

</table>

</body>

</html>

<%

//关闭结果集对象

sqlRst.close();

//关闭语句对象

sqlStmt.close();

//关闭数据库连接

sqlConn.close();

%>


MD5算法（看在你是女孩的份上，给你一个吧）

作者：hbworld

日期：2001-4-26 12:30:20

  public String MD5(String sInput)

   throws Exception{

     String algorithm="";

     //输入不能为空

     if(sInput.trim()==null){

    return "null";

     }

     //指定采用MD5算法

     try{

        algorithm=System.getProperty("MD5.algorithm","MD5");

     }catch(SecurityException se){

     }

     //定义MessageDigest对象

     MessageDigest md=MessageDigest.getInstance(algorithm);

     //按照系统缺省的字符编码方式把sInput 转换成字节，并把结果存到一新的字节数组buffer中

     byte buffer[]=sInput.getBytes();

     //从指定的字节数组buffer的偏移量0开始，用指定的字节数组修改由sInput生成摘要

     //count为从 0 开始用的字节数长度。

     for(int count=0;count<sInput.length();count++)

     {

    md.update(buffer,0,count);

     }

     //通过执行最后的诸如填充的操作完成散列码的计算。 在调用之后复位该摘要

     //返回存放结果散列值的字节数组bDigest

     byte bDigest[]=md.digest();

     //将bDigest转换为大整数bi

     BigInteger bi=new BigInteger(bDigest);

     //返回bi字符串表示，即最终的编码结果

     return(bi.toString(16));

   }


MySQL的JDBC驱动程序下载地址

作者：何志强

email: hhzqq@21cn.com

日期：2000-8-10 18:05:19

[url]http://www.worldserver.com/mm.mysql/[/url]


NT下安装JSP

作者：运气

email: webmaster@chinaspx.com

日期：2001-7-3 14:23:22

Windows NT 4.0，Windows 2000 professional,windows 2000 server

jdk1_2_2-001-win.exe

tomcat.zip(3.0)

一、软件下载

    JDK

        http://java.sun.com/products/jdk/1.2/

    Tomcat

        http://jakarta.apache.org/builds/tomcat/release/v3.0/tomcat.zip

二、软件安装

    （一）JDK

        1、双击jdk1_2_2-001-win.exe文件进行安装，使用缺省配置进行安装，JDK的缺省安装目录为

           C:\jdk1.2.2，JRE的缺省安装目录为C:\Program Files\JavaSoft\JRE\1.2；

        2、重启计算机；

        3、更新下列环境变量：把C:\jdk1.2.2\bin目录追加到PATH中；把

           .;C:\jdk1.2.2\lib\tools.jar;C:\jdk1.2.2\lib\dt.jar加入到CLASSPATH中。更新

           方法：控制面板－＞系统－＞环境－＞系统变量；

    （二）Tomcat

        1、用WinZIP等解压缩软件把tomcat.zip解压缩到一个目录下，我把它解压缩到C:\，它会自动

           创建tomcat子目录，这样在C盘就多了一个目录C:/tomcat，我用$TOMCAT标识；

        3、修改Tomcat运行的端口号，注意Tomcat自己有一个独立的HTTP服务器，它必须使用一个还未被使用的端口号，我这里的8081还未被占用，我用$TOMCAT_PORT标识，分配给Tomcat：

               1)打开$TOMCAT/server.xml；

               2)修改ContextManager：

        4、双击$TOMCAT目录下的startup.bat来启动Tomcat；

        5、在浏览器上输入http://localhost:$TOMCAT_PORT/Version 3.0这一页就表示Tomcat安装成功了；


Operators and Assignments(1)

作者：jeru

email: jeru@163.net

日期：7/3/2001 9:55:32 AM

5)Operators and Assignments

Objective 1)

Determine the result of applying any operator including assignment operators and instanceof to operands of any type class scope or accessibility or any combination of these.

1.    Unary operators.

1.1    Increment and Decrement operators ++ --

We have postfix and prefix notation. In post-fix notation value of the variable/expression is modified after the value is taken for the execution of statement. In prefix notation, value of the variable/expression is modified before the value is taken for the execution of statement.

x = 5; y = 0; y = x++;  Result will be x = 6, y = 5

x = 5; y = 0; y = ++x;  Result will be x = 6, y = 6

Implicit narrowing conversion is done, when applied to byte, short or char.

1.2    Unary minus and unary plus + -

+ has no effect than to stress positivity.

- negates an expression’s value. (2’s complement for integral expressions)

1.3    Negation !

Inverts the value of a boolean expression.

1.4    Complement ~

Inverts the bit pattern of an integral expression. (1’s complement – 0s to 1s and 1s to 0s)

(~i=-i-1)

Cannot be applied to non-integral types.

1.5    Cast ()

Persuades compiler to allow certain assignments. Extensive checking is done at compile and runtime to ensure type-safety.

2.    Arithmetic operators - *, /, %, +, -

·    Can be applied to all numeric types.

·    Can be applied to only the numeric types, except ‘+’ – it can be applied to Strings as well.

·    All arithmetic operations are done at least with ‘int’. (If types are smaller, promotion happens. Result will be of a type at least as wide as the wide type of operands)

·    Accuracy is lost silently when arithmetic overflow/error occurs. Result is a nonsense value.

·    Integer division or % by zero throws ArithmeticException.

·    Floating point arithmetic always loses precision. The following code fragment returns false.

float f = 1.0 F / 3.0 F;

if ( A * 3.0 F == 1.0 F ) return true;

else return false;

·    % - sign of the result entirely determined by sign of LHS

·    Floating point calculations can produce NaN (square root of a negative no. or % by zero) or Infinity/-Infinity( division by zero). Float and Double wrapper classes have named constants for NaN and infinities.

·    NaN’s are non-ordinal for comparisons. X ==  Float.NaN won’t work. Use Float.IsNaN(x) and Double.IsNaN(x). But equals method on wrapper objects(Double or Float) with NaN values compares Nan’s correctly.

·    Infinities are ordinal. X == Double.POSITIVE_INFINITY will give expected result.

·    + also performs String concatenation (when any operand in an expression is a String). The language itself overloads this operator. toString() method of non-String object operands are called to perform concatenation. In case of primitives, a wrapper object is created with the primitive value and toString method of that object is called. (“Vel” + 3 will work.)

·    Be aware of associativity when multiple operands are involved.

System.out.println( 1 + 2 + “3” ); // Prints 33

System.out.println( “1” + 2 + 3 ); // Prints 123

3.    Shift operators - <<, >>, >>>

·    << performs a signed left shift. 0 bits are brought in from the right. Sign bit (MSB) is preserved.(?) Value becomes old value * 2 ^ x where x is no of bits shifted.

·    >> performs a signed right shift. Sign bit is brought in from the left. (0 if positive, 1 if negative. Value becomes old value / 2 ^ x where x is no of bits shifted. Also called arithmetic right shift.

·    >>> performs an unsigned logical right shift. 0 bits are brought in from the left. This operator exists since Java doesn’t provide an unsigned data type (except char). >>> changes the sign of a negative number to be positive. So don’t use it with negative numbers, if you want to preserve the sign. Also don’t use it with types smaller than int. (Since types smaller than int are promoted to an int before any shift operation and the result is cast down again, so the end result is unpredictable.)

·    Shift operators can be applied to only integral types.

·    -1 >> 1 is –1, not 0.

·    1 << 31 will become the minimum value that an int can represent.

·    Negative numbers are represented in two’s complement notation. (Take one’s complement and add 1 to get two’s complement)

·    Shift operators never shift more than the number of bits the type of result can have. ( i.e. int 32, long 64) RHS operand is reduced to RHS % x where x is no of bits in type of result.

int x;

x == x >> 32 // ture

x =  x >> 33;  // Here actually what happens is x >> 1

·    if right side is negative, for int, use low 5 bits; for long, use low 6 bits.

4.    Comparison operators – all return boolean type.

4.1    Ordinal comparisons - <, <=, > , >=

·    Only operate on numeric types. Test the relative value of the numeric operands.

·    Arithmetic promotions apply. char can be compared to float.

4.2    Object type comparison – instanceof

·    Tests the class of an object at runtime. Checking is done at compile and runtime same as the cast operator.

·    Returns true if the object denoted by LHS reference can be cast to RHS type.

·    LHS should be an object reference expression, variable or an array reference.

·    RHS should be a class (abstract classes are fine), an interface or an array type, castable to LHS object reference. Compiler error if LHS & RHS are unrelated.

·    Can’t use java.lang.Class or its String name as RHS.(?)

·    Returns true if LHS is a class or subclass of RHS class

·    Returns true if LHS implements RHS interface.

·    Returns true if LHS is an array reference and of type RHS.

·    x instanceof Component[] – legal.

·    x instanceof [] – illegal. Can’t test for ‘any array of any type’

·    Returns false if LHS is null, no exceptions are thrown.

·    If x instanceof Y is not allowed by compiler, then Y y = (Y) x is not a valid cast expression. If x instanceof Y is allowed and returns false, the above cast is valid but throws a ClassCastException at runtime. If x instanceof Y returns true, the above cast is valid and runs fine.

4.3    Equality comparisons - ==, !=

·    For primitives it’s a straightforward value comparison. (promotions apply)

·    For object references, this doesn’t make much sense. Use equals method for meaningful comparisons. (Make sure that the class implements equals in a meaningful way, like for X.equals(Y) to be true, Y instance of X must be true as well)

·    For String literals, == will return true, this is because of compiler optimization.

5.    Bit-wise operators - &, ^, |

·    Operate on numeric and boolean operands.

·    & - AND operator, both bits must be 1 to produce 1.

·    | - OR operator, any one bit can be 1 to produce 1.

·    ^ - XOR operator, any one bit can be 1, but not both, to produce 1.

·    In case of booleans true is 1, false is 0.

·    Order:  AND, XOR, OR.

·    Can’t cast any other type to boolean.

6.    Short-circuit logical operators - &&, ||

·    Operate only on boolean types.

·    RHS might not be evaluated (hence the name short-circuit), if the result can be determined only by looking at LHS.

·    false && X  is always false.

·    true || X is always true.

·    RHS is evaluated only if the result is not certain from the LHS.

7.    Ternary operator

·    Format a = x ? b : c ;

·    x should be a boolean expression.

·    Based on x, either b or c is evaluated. Both are never evaluated.

·    b will be assigned to a if x is true, else c is assigned to a.

·    b and c should be assignment compatible to a.

·    b and c are made identical during the operation according to promotions.

8.    Assignment operators.

·    Simple assignment =.

·    op= calculate and assign operators(extended assignment operators)

·    *=, /=, %=, +=, -=

·    x += y means x = x + y. But x is evaluated only once. Be aware.

·    Assignment of reference variables copies the reference value, not the object body.

·    Assignment has value, value of LHS after assignment. So a = b = c = 0 is legal. c = 0 is executed first, and the value of the assignment (0) assigned to b, then the value of that assignment (again 0) is assigned to a.

·    Extended assignment operators do an implicit cast. (Useful when applied to byte, short or char)

byte b = 10;

b = b + 10; // Won’t compile, explicit cast reqd since the expression evaluates to an int

b += 10; // OK, += does an implicit cast from int to byte

9.    General

·    In Java, No overflow or underflow of integers happens. i.e. The values wrap around. Adding 1 to the maximum int value results in the minimum value.

·    Always keep in mind that operands are evaluated from left to right, and the operations are executed in the order of precedence and associativity.

·    Unary Postfix operators and all binary operators (except assignment operators) have left to right assoiciativity.

·    All unary operators (except postfix operators), assignment operators, ternary operator, object creation and cast operators have right to left assoiciativity.

·    Inspect the following code.

public class Precedence {

  final public static void main(String args[]) {

    int i = 0;

    i = i++;

    i = i++;

    i = i++;

    System.out.println(i); // prints 0, since = operator has the lowest precedence.

    int array[] = new int[5];

    int index = 0;

array[index] = index = 3; // 1st element get assigned to 3, not the 4th element

    for (int c = 0; c < array.length; c++)

      System.out.println(array[c]);

    System.out.println("index is " + index); // prints 3

  }

}

Type of Operators    Operators    Associativity

Postfix operators    [] . (parameters) ++ --    Left to Right

Prefix Unary operators    ++  -- + - ~ !    Right to Left

Object creation and cast    new (type)    Right to Left

Multiplication/Division/Modulus    * / %    Left to Right

Addition/Subtraction    + -    Left to Right

Shift    >> >>> <<    Left to Right

Relational    < <= > >= instanceof    Left to Right

Equality    == !=    Left to Right

Bit-wise/Boolean AND    &    Left to Right

Bit-wise/Boolean XOR    ^    Left to Right

Bit-wise/Boolean OR    |    Left to Right

Logical AND (Short-circuit or Conditional)    &&    Left to Right

Logical OR (Short-circuit or Conditional)    ||    Left to Right

Ternary    ? :    Right to Left

Assignment    = += -= *= /= %= <<= >>= >>>= &= ^= |=    Right to Left


Operators and Assignments(2)

作者：jeru

email: jeru@163.net

日期：7/3/2001 9:56:15 AM

Objective 2)

Determine the result of applying the boolean equals(Object) method to objects of any combination of the classes java.lang.String java.lang.Boolean and java.lang.Object.

The equals method can be considered to perform a deep comparison of the value of an object, whereas the == operator performs a shallow comparison. The equals method compares what an object points to rather than the pointer itself (if we can admit that Java has pointers).

The equals method applied to a String, however that String was created, performs a character by character comparison.

Objective 3)

In an expression involving the operators & | && || and variables of known values state which operands are evaluated and the value of the expression.

Objective 4)

Determine the effect upon objects and primitive values of passing variables into methods and performing assignments or other modifying operations in that method.

Unary Numeric Promotion

Contexts:

·    Operand of the unary arithmetic operators + and –

·    Operand of the unary integer bit-wise complement operator ~

·    During array creation, for example new int[x], where the dimension expression x must evaluate to an int value.

·    Indexing array elements, for example table[‘a’], where the index expression must evaluate to an int value.

·    Individual operands of the shift operators.

Binary numeric promotion

Contexts:

·    Operands of arithmetic operators *, / , %, + and –

·    Operands of relational operators <, <= , > and >=

·    Numeric Operands of equality operators == and !=

·    Integer Operands of bit-wise operators &, ^ and |

Conversion of Primitives

1.    3 types of conversion – assignment conversion, method call conversion and arithmetic promotion

2.    boolean may not be converted to/from any non-boolean type.

3.    Widening conversions accepted. Narrowing conversions rejected.

4.    byte, short can’t be converted to char and vice versa.

5.    Arithmetic promotion

5.1    Unary operators

·    if the operand is byte, short or char  {

convert it to int;

           }

            else {

do nothing; no conversion needed;

}

5.2    Binary operators

·    if one operand is double {

all double; convert the other operand to double;

           }

           else if one operand is float {

all float; convert the other operand to float;

}

else if one operand is long {

all long; convert the other operand to long;

}

else {

all int; convert all to int;

}

6.    When assigning a literal value to a variable, the range of the variable’s data type is checked against the value of the literal and assignment is allowed or compiler will produce an error.

char c = 3; // this will compile, even though a numeric literal is by default an int since the range of         char will accept the value

int a = 3;

char d = a; // this won’t compile, since we’re assigning an int to char

char e = -1; // this also won’t compile, since the value is not in the range of char

float f = 1.3; // this won’t compile, even though the value is within float range. Here range is not  important, but precision is. 1.3 is by default a double, so a specific cast or f = 1.3f will work.

float f = 1/3; // this will compile, since RHS evaluates to an int.

float f = 1.0 / 3.0; // this won’t compile, since RHS evaluates to a double.

7.    Also when assigning a final variable to a variable, even if the final variable’s data type is wider than the variable, if the value is within the range of the variable an implicit conversion is done.

byte b;

final int a = 10;

b = a; // Legal, since value of ‘a’ is determinable and within range of b

final int x = a;

b = x; // Legal, since value of ‘x’ is determinable and within range of b

int y;

final int z = y;

b = z; // Illegal, since value of ‘z’ is not determinable

8.    Method call conversions always look for the exact data type or a wider one in the method signatures. They will not do narrowing conversions to resolve methods, instead we will get a compile error.

Here is the figure of allowable primitive conversion.

byte à short à int à long à float à double

                            &shy;

           char

Casting of Primitives

9.    Needed with narrowing conversions. Use with care – radical information loss. Also can be used with widening conversions, to improve the clarity of the code.

10.    Can cast any non-boolean type to another non-boolean type.

11.    Cannot cast a boolean or to a boolean type.

Conversion of Object references

12.    Three types of reference variables to denote objects - class, interface or array type.

13.    Two kinds of objects can be created – class or array.

14.    Two types of conversion – assignment and method call.

15.    Permitted if the direction of the conversion is ‘up’ the inheritance hierarchy. Means that types can be assigned/substituted to only super-types – super-classes or interfaces. Not the other way around, explicit casting is needed for that.

16.    Interfaces can be used as types when declaring variables, so they participate in the object reference conversion. But we cannot instantiate an interface, since it is abstract and doesn’t provide any implementation. These variables can be used to hold objects of classes that implement the interface. The reason for having interfaces as types may be, I think, several unrelated classes may implement the same interface and if there’s a need to deal with them collectively one way of treating them may be an array of the interface type that they implement.

17.    Primitive arrays can be converted to only the arrays of the same primitive type. They cannot be converted to another type of primitive array. Only object reference arrays can be converted / cast.

18.    Primitive arrays can be converted to an Object reference, but not to an Object[] reference. This is because all arrays (primitive arrays and Object[]) are extended from Object.

Casting of Object references

19.    Allows super-types to be assigned to subtypes. Extensive checks done both at compile and runtime. At compile time, class of the object may not be known, so at runtime if checks fail, a ClassCastException is thrown.

20.    Cast operator, instanceof operator and the == operator behave the same way in allowing references to be the operands of them. You cannot cast or apply instanceof or compare unrelated references, sibling references or any incompatible references.

Compile-time Rules

·    When old and new types are classes, one class must be the sub-class of the other.

·    When old and new types are arrays, both must contain reference types and it must be legal to cast between those types (primitive arrays cannot be cast, conversion possible only between same type of primitive arrays).

·    We can always cast between an interface and a non-final object.

Run-time rules

·    If new type is a class, the class of the expression being converted must be new type or extend new type.

·    If new type is an interface, the class of the expression being converted must implement the interface.

An Object reference can be converted to: (java.lang.Object)

·    an Object reference

·    a Cloneable interface reference, with casting, with runtime check

·    any class reference, with casting, with runtime check

·    any array referenece, with casting, with runtime check

·    any interface reference, with casting, with runtime check

A Class type reference can be converted to:

·    any super-class type reference, (including Object)

·    any sub-class type reference, with casting, with runtime check

·    an interface reference, if the class implements that interface

·    any interface reference, with casting, with runtime check (except if the class is final and doesn’t implement the interface)

An Interface reference can be converted to:

·    an Object reference

·    a super-interface reference

·    any interface/class reference with casting, with runtime check (except if the class is final and doesn’t implement the interface)

A Primitive Array reference can be converted to:

·    an Object reference

·    a Cloneable interface reference

·    a primitive array reference of the same type

An Object Array reference can be converted to:

·    an Object reference

·    a  Cloneable interface reference

·    a super-class Array reference, including an Object Array reference

·    any sub-class Array reference with casting, with runtime check

Examples

Q1 Given these class definitions:

class Superclass { }

class Subclass1 extends Superclass { }

and these objects:

Superclass a = new Superclass();

Subclass1 b = new Subclass1();

which of the following explains the result of the statement:

b = (Subclass1)a;

Select the one right answer.

a) Illegal at compile time

b) Legal at compile time but possibly illegal at runtime  // throw a ClassCastException

c) Definitely legal at runtime


Oracle中ID自增得方法(转自JSP001)

此电子文档属C#乐园QQ群3256045整理。
Oracle中ID自增得方法(转自JSP001)

上一页 目录 下一页

Oracle中ID自增得方法(转自JSP001)

作者：jeru

email: jeru@163.net

日期：6/27/2001 10:00:04 AM

由 amtd 发布于: 2001-03-09 08:28

如何在Oracle 中实现类似自动增加 ID 的功能?

整理编辑：China ASP

我们经常在设计数据库的时候用一个系统自动分配的ID来作为我们的主键，但是在ORACLE 中没有这样的

功能，我们可以通过采取以下的功能实现自动增加ID的功能

1.首先创建 sequence

create sequence seqmax increment by 1

2.使用方法

select seqmax.nextval ID from dual

就得到了一个ID

如果把这个语句放在 触发器中，就可以实现和ms sql 的自动增加ID相同的功能！


orcale测试实例

作者：blackwhites

日期：2000-11-9 15:13:59

/*

   OracleTestServlet -  This is a sample servlet which

connects to an Oracle instance.  It includes some code to

log various points of invocation and execution into

the WebSphere Application Server's log.  It also sets a log

file (in the current directory where the servlet is

invoked) for the JDBC driver.  This JDBC driver log can be

examined along with the servlet's log output in order to

determine configuration problems.  Create the

corresponding class file from this source and move it to the

appropriate servlets directory.

Marc Connolly

Oracle Corporation

Email: mdconnol@us.oracle.com

*/

import   java.io.File;

import   java.io.IOException;

import   java.lang.IllegalStateException;

import   java.io.PrintWriter;

import   java.io.PrintStream;

import   java.io.FileWriter;

import   java.io.FileOutputStream;

import   java.sql.DriverManager;

import   java.sql.Connection;

import   java.sql.SQLException;

import   java.sql.Statement;

import   java.sql.ResultSet;

import   java.util.Date;

import   javax.servlet.Servlet;

import   javax.servlet.ServletConfig;

import   javax.servlet.ServletException;

import   javax.servlet.http.HttpServlet;

import   javax.servlet.http.HttpServletRequest;

import   javax.servlet.http.HttpServletResponse;

import   javax.servlet.http.HttpUtils;

public class OracleServlet extends HttpServlet

{

   private String                thisServletName =

"OracleTestServlet";

   private String                thisServletNameDesc = "

Oracle JDBC Test Servlet";

   private boolean               servletInitialized = false;

   private long                  servletStartTimeStamp;

   public void init(ServletConfig config) throws

ServletException

   {

      super.init(config);

      /*

      Record the servlet's initialization time

      */

      servletStartTimeStamp = System.currentTimeMillis();

      log(thisServletName + ":Init Completed");

      /*

      Mark servlet init complete

      */

      servletInitialized = true;

   }

   protected void doGet(HttpServletRequest request,

HttpServletResponse response) throws IOException,

ServletException

   {

      log("Start Menu Visual List()");

      log(thisServletName + "doGet (Start)");

      long startTimeStamp = System.currentTimeMillis();

      response.setContentType("text/html");

      PrintWriter out = response.getWriter();

      out.println("<HTML><TITLE>OracleTestServlet: Using

Oracle JDBC Driver</TITLE><BODY>");

      out.println("<BR><BR<BR<HR><CENTER>Begin Employee

List</CENTER><BR><BR>");

      out.println("<PRE>");

      /*

      Enable JDBC Driver logging

      */

      String jdbcDriverlogFileName =

"./OracleServletJDBC.log";

      log(thisServletName + "JDBC Log: " +

jdbcDriverlogFileName);

      /*

      Make the log file

      */

      File file = new File(jdbcDriverlogFileName);

      if (file.exists())

      {

         if (!file.canWrite())

         {

            log(thisServletName + "JDBC Driver Log file (" +

file.getAbsolutePath() + ") is not writable");

         }

      }

      else

      {

         try

         {

            File parent = new File(file.getParent());

            if (!parent.canWrite())

            {

               log(thisServletName + "JDBC Driver Log file

directory (" + parent.getAbsolutePath() + ") is not

writable");

            }

         }

         catch (NullPointerException e)

         {

              // no parent.

         }

      }

      try

      {

            DriverManager.setLogStream(new PrintStream(new

FileOutputStream(file.getAbsolutePath(), true)));

      }

      catch (IOException ioE)

      {

         log(thisServletName + "JDBC Driver Log - exception

thrown during setLogStream:" + ioE);

      }

      try

      {

         log(thisServletName + "Registering

Driver(before)");

         DriverManager.registerDriver(new

oracle.jdbc.driver.OracleDriver());

         log(thisServletName + "Registering Driver(after)");

         /*

         Note regarding connection url: the ip address is

the address of the host upon which the Oracle instance is

running followed by

         the port which the Oracle instance's listener (the

TNS address space) is using as specified in the

.PARMLIB(MPMTNS) member

         of the Oracle's MPM started task.

         */

         Connection conn =  DriverManager.getConnection

("jdbc:oracle:thin:@9.12.2.10:1621:MPM5","system",

"manager");

         log(thisServletName + "Get Connection(After)");

         Statement stmt = conn.createStatement ();

         log(thisServletName + "Create Statement(After)");

         ResultSet rset = stmt.executeQuery ("select EMPNO,

ENAME from scott.EMP");

         log(thisServletName + "Execute Query(After)");

         while (rset.next ())

            out.println("<BR>EMPNO:" + rset.getString (1) +

",NAME:" + rset.getString (2));

        log(thisServletName + "Close Connection(Before)");

         conn.close();

         log(thisServletName + "Close Connection(Before)");

      }

      catch (Exception e)

      {

         log(thisServletName + "Exception:" + e);

      }

      out.println("</PRE><BR><HR><CENTER>End Employee

List</CENTER></   FORM></BODY></HTML>");

      out.close();

      log(thisServletName + "doGet (End)");

   }

   public void destroy()

   {

      log(thisServletName + ":Terminated");

      super.destroy();

   }

   public boolean isInitialized()

   {

      return servletInitialized;

   }

   public String getServletInfo()

   {

      return thisServletNameDesc;

   }

}


Orion下自定义Tag

作者：东方一蛇

日期：2000-12-7 22:22:52

一、前言 (本文译自Orion官方站点)

     本文将一步一步介绍在Orion Application Server下定义自己的标签，然后，实现该标签的功能，最后用一个jsp例子测试。

二、建立标签，实现该标签的功能。

1.命名package为：com.acme.mytags(为了和原文相同，我不做改动)

package com.acme.mytags;

2.import相关class

import javax.servlet.jsp.*;

import javax.servlet.jsp.tagext.*;

3.实现javax.servlet.jsp.tagext.Tag接口：

public class HelloWorld implements Tag

{

4.定义局部变量

private PageContext pageContext;

private Tag parent;

5.调用标签开始的实现方法

public int doStartTag() throws javax.servlet.jsp.JspException

{

return SKIP_BODY;

}

注意：这个方法返回 SKIP_BODY，就是标签的body为空的时候，将返回这个值，否则将返回：EVAL_BODY_INCLUDE

6.调用标签结束的实现方法

public int doEndTag() throws javax.servlet.jsp.JspException

{

try

{

pageContext.getOut().write("Hello World!");

}

catch(java.io.IOException e)

{

throw new JspException("IO Error: " + e.getMessage());

}

return EVAL_PAGE;

}

这样将在jsp文件的自定义标签结束地方输出“Hello World!”

7.还不够，我们还要写下面的方法：

public void release() {}

对于本简单的例子，上面的方法不需要任何实现。

8.JSP的容器要调用下面的方法：

public void setPageContext(final javax.servlet.jsp.PageContext pageContext) {

this.pageContext=pageContext;

}

JSP容器通过上面的方法调用标签，而且上面的方法用来设置标签的PageContext.

9.JSP的容器还要调用下面的方法：

public void setParent(final javax.servlet.jsp.tagext.Tag parent)

{

this.parent=parent;

}

JSP容器通过上面的方法设置标签的parent-Tag，因为每个标签的PageContext都要保留它的parent标签。

10.最后，实现方法：

public javax.servlet.jsp.tagext.Tag getParent()

{

return parent;

}

}

11. 编译标签。

三、描叙标签

现在将要写一个描叙文件，描叙该标签。

1.建立一个taglib.tld文件，

2.taglib.tld是一个XML格式的文本文件，XML的头如下：

<?xml version="1.0" encoding="ISO-8859-1" ?>

<!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN"

"http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd">

3.描叙标签库

<taglib>

<tlibversion>1.0</tlibversion>

<jspversion>1.1</jspversion>

<shortname>mt</shortname> <uri>http://www.orionserver.com/tutorials/tagtut/lesson1/mytags.jar</uri>

<info>My first Tag library</info>

4.描叙标签

<tag>

<name>helloWorld</name>

<tagclass>com.acme.mytags.HelloWorld</tagclass>

<bodycontent>empty</bodycontent>

<info>A Hello world Tag</info>

</tag>

5.结束

</taglib>

四、打包

名称为：mytags.jar

其目录结构为：

com/acme/mytags/HelloWorld.class

META-INF/taglib.tld

五、在jsp文件中使用自定义标签

建立hello.jsp如下：

<%@ taglib uri="mytags.jar" prefix="mt" %>

<HTML>

<HEAD>

<TITLE>Hello World!</TITLE>

</HEAD> <BODY BGCOLOR="#FFFFFF">

<HR>

<mt:helloWorld/>

<HR>

</BODY>

</HTML>

六、测试运行

在orion目录下，建立如下结构，其中，tag是自己建立的，前面的目录是本来就有的。

E:\orion\default-web-app\tag

将jar文件和jsp文件全部放到这个目录下。

然后，访问：

http://localhost:[port]/tag/hello.jsp

将出现：

--------------------------------------------------------------------------------

Hello World!

--------------------------------------------------------------------------------

七、恭喜，你已经成功了！

附：mytags.jar和hello.jsp 文件本站下载地址：

http://www.wodejia.net/softdownload/java/orion_tag01.zip

本站文章归东方一蛇所有，如要转载，请注名原文作者和网站(http://www.wodejia.net


Overloading overriding runtime type and object orientation (1)

作者：jeru

email: jeru@163.net

日期：7/3/2001 10:01:58 AM

6)Overloading overriding runtime type and object orientation

Objective 1)

State the benefits of encapsulation in object oriented design and write code that implements tightly encapsulated classes and the relationships "is a" and "has a".

Encapsulation involves hiding data of a class and allowing access only through a public interface.

The separation of interface and implementation makes it easier to modify the code within a class without breaking any other code that uses it.

encapsulation, inheritance, polymorphism are three principal characteristics of Object Oriented programming.

Implementing OO relationships

·    “is a” relationship is implemented by inheritance (extends keyword)

·    “has a” relationship is implemented by providing the class with member variables.

Overloading and Overriding

·    Overloading is an example of polymorphism. (operational / parametric)

·    Overriding is an example of runtime polymorphism (inclusive)

·    A method can have the same name as another method in the same class, provided it forms either a valid overload or override

Objective 2)

Write code to invoke overridden or overloaded methods and parental or overloaded constructors; and describe the effect of invoking these methods.

Overloading    Overriding

Signature has to be different. Just a difference in return type is not enough.    Signature has to be the same. (including the return type)

Accessibility may vary freely.    Overriding methods cannot be more private than the overridden methods.

Exception list may vary freely.    Overriding methods may not throw more checked exceptions than the overridden methods.  But can throw no exception.

Just the name is reused. Methods are independent methods. Resolved at compile-time based on method signature.    Related directly to sub-classing. Overrides the parent class method. Resolved at run-time based on type of the object.

Can call each other by providing appropriate argument list.    Overriding method can call overridden method by super.methodName(), this can be used only to access the immediate super-class’s method. super.super won’t work. Also, a class outside the inheritance hierarchy can’t use this technique.

Methods can be static or non-static. Since the methods are independent, it doesn’t matter. But if two methods have the same signature, declaring one as static and another as non-static does not provide a valid overload. It’s a compile time error.    static methods don’t participate in overriding, since they are resolved at compile time based on the type of reference variable. A static method in a sub-class can’t use ‘super’ (for the same reason that it can’t use ‘this’ for)Remember that a static method can’t be overridden to be non-static and a non-static method can’t be overridden to be static. In other words, a static method and a non-static method cannot have the same name and signature (if signatures are different, it would have formed a valid overload)

There’s no limit on number of overloaded methods a class can have.    Each parent class method may be overridden at most once in any sub-class. (That is, you cannot have two identical methods in the same class)

·    Variables can also be overridden, it’s known as shadowing or hiding. But, member variable references are resolved at compile-time. So at the runtime, if the class of the object referred by a parent class reference variable, is in fact a sub-class having a shadowing member variable, only the parent class variable is accessed, since it’s already resolved at compile time based on the reference variable type. Only methods are resolved at run-time.

public class Shadow {

  public static void main(String s[]) {

    S1 s1 = new S1();

    S2 s2 = new S2();

    System.out.println(s1.s); // prints S1

    System.out.println(s1.getS()); // prints S1

    System.out.println(s2.s); // prints S2

    System.out.println(s2.getS()); // prints S2

    s1 = s2;

    System.out.println(s1.s); // prints S1, not S2 -

                      // since variable is resolved at compile time

    System.out.println(s1.getS()); // prints S2 -

                     // since method is resolved at run time

  }

}

class S1 {

  public String s = "S1";

  public String getS() {

    return s;

  }

}

class S2 extends S1{

  public String s = "S2";

  public String getS() {

    return s;

  }

}

In the above code, if we didn’t have the overriding getS() method in the sub-class and if we call the method from sub-class reference variable, the method will return only the super-class member variable value. For explanation, see the following point.

·    Also, methods access variables only in context of the class of the object they belong to. If a sub-class method calls explicitly a super class method, the super class method always will access the super-class variable. Super class methods will not access the shadowing variables declared in subclasses because they don’t know about them. (When an object is created, instances of all its super-classes are also created.) But the method accessed will be again subject to dynamic lookup. It is always decided at runtime which implementation is called. (Only static methods are resolved at compile-time)

public class Shadow2 {

  String s = "main";

  public static void main(String s[]) {

    S2 s2 = new S2();

    s2.display();  // Produces an output – S1, S2

    S1 s1 = new S1();

    System.out.println(s1.getS()); // prints S1

    System.out.println(s2.getS()); // prints S1 – since super-class method always

     // accesses super-class variable

  }

}

class S1 {

  String s = "S1";

  public String getS() {

    return s;

  }

  void display() {

    System.out.println(s);

  }

}

class S2 extends S1{

  String s = "S2";

  void display() {

    super.display();   // Prints S1

    System.out.println(s); // prints S2

  }

}

·    With OO languages, the class of the object may not be known at compile-time (by virtue of inheritance). JVM from the start is designed to support OO. So, the JVM insures that the method called will be from the real class of the object (not with the variable type declared). This is accomplished by virtual method invocation (late binding). Compiler will form the argument list and produce one method invocation instruction – its job is over. The job of identifying and calling the proper target code is performed by JVM.

·    JVM knows about the variable’s real type at any time since when it allocates memory for an object, it also marks the type with it. Objects always know ‘who they are’. This is the basis of instanceof operator.

·    Sub-classes can use super keyword to access the shadowed variables in super-classes. This technique allows for accessing only the immediate super-class. super.super is not valid. But casting the ‘this’ reference to classes up above the hierarchy will do the trick. By this way, variables in super-classes above any level can be accessed from a sub-class, since variables are resolved at compile time, when we cast the ‘this’ reference to a super-super-class, the compiler binds the super-super-class variable. But this technique is not possible with methods since methods are resolved always at runtime, and the method gets called depends on the type of object, not the type of reference variable. So it is not at all possible to access a method in a super-super-class from a subclass.

public class ShadowTest {

    public static void main(String s[]){

        new STChild().demo();

    }

}

class STGrandParent {

    double wealth = 50000.00;

    public double getWealth() {

        System.out.println("GrandParent-" + wealth);

        return wealth;

    }

}

class STParent extends STGrandParent {

    double wealth = 100000.00;

    public double getWealth() {

        System.out.println("Parent-" + wealth);

        return wealth;

    }

}

class STChild extends STParent {

    double wealth = 200000.00;

    public double getWealth() {

        System.out.println("Child-" + wealth);

        return wealth;

    }

    public void demo() {

        getWealth(); // Calls Child method

        super.getWealth(); // Calls Parent method

        //super.super.getWealth(); // Compiler error, GrandParent method cannot be accessed

        ((STParent)this).getWealth(); // Calls Child method, due to dynamic method lookup

    ((STGrandParent)this).getWealth(); // Calls Child method, due to dynamic method

      // lookup

        System.out.println(wealth); // Prints Child wealth

        System.out.println(super.wealth); // Prints Parent wealth

        System.out.println(((STParent)(this)).wealth); // Prints Parent wealth

        System.out.println(((STGrandParent)(this)).wealth); // Prints GrandParent wealth

    }

}

·    An inherited method, which was not abstract on the super-class, can be declared abstract in a sub-class (thereby making the sub-class abstract). There is no restriction. In the same token, a subclass can be declared abstract regardless of whether the super-class was abstract or not.

·    Private members are not inherited, but they do exist in the sub-classes. Since the private methods are not inherited, they cannot be overridden. A method in a subclass with the same signature as a private method in the super-class is essentially a new method, independent from super-class, since the private method in the super-class is not visible in the sub-class.

public class PrivateTest {

    public static void main(String s[]){

        new PTSuper().hi();     // Prints always Super

        new PTSub().hi();     // Prints Super when subclass doesn't have hi method

                 // Prints Sub when subclass has hi method

        PTSuper sup;

        sup = new PTSub();

        sup.hi();     // Prints Super when subclass doesn't have hi method

            // Prints Sub when subclass has hi method

    }

}

class PTSuper {

    public void hi() { // Super-class implementation always calls superclass hello

        hello();

    }

    private void hello() {     // This method is not inherited by subclasses, but exists in them.

            // Commenting out both the methods in the subclass show this.

            // The test will then print "hello-Super" for all three calls

            // i.e. Always the super-class implementations are called

        System.out.println("hello-Super");

    }

}

class PTSub extends PTSuper {

    public void hi() { // This method overrides super-class hi, calls subclass hello

        try {

            hello();

        }

        catch(Exception e) {}

    }

    void hello() throws Exception {     // This method is independent from super-class hello

                // Evident from, it's allowed to throw Exception

        System.out.println("hello-Sub");

    }

}

·    Private methods are not overridden, so calls to private methods are resolved at compile time and not subject to dynamic method lookup. See the following example.

public class Poly {

    public static void main(String args[]) {

        PolyA ref1 = new PolyC();

        PolyB ref2 = (PolyB)ref1;

        System.out.println(ref2.g());     // This prints 1

                    // If f() is not private in PolyB, then prints 2

    }

}

class PolyA {

    private int f() { return 0; }

    public int g() { return 3; }

}

class PolyB extends PolyA {

    private int f() { return 1; }

    public int g() { return f(); }

}

class PolyC extends PolyB {

    public int f() { return 2; }

}


Overloading overriding runtime type and object orientation (2)

作者：jeru

email: jeru@163.net

日期：7/3/2001 10:02:33 AM

Objective 3)

Write code to construct instances of any concrete class including normal top level classes inner classes static inner classes and anonymous inner classes.

Inner Classes

·    A class can be declared in any scope. Classes defined inside of other classes are known as nested classes. There are four categories of nested classes.

Top-level nested classes / interfaces

·    Declared as a class member with static modifier.

·    Just like other static features of a class. Can be accessed / instantiated without an instance of the outer class. Can access only static members of outer class. Can’t access non-static instance variables or methods.

·    Very much like any-other package level class / interface. Provide an extension to packaging by the modified naming scheme at the top level.

·    Classes can declare both static and non-static members.

·    Any accessibility modifier can be specified.

·    Nested interfaces are implicitly static (static modifier also can be specified). They can have any accessibility modifier. There are no non-static inner, local or anonymous interfaces.

Non-static inner classes

·    Declared as a class member without static.

·    An instance of a non-static inner class can exist only with an instance of its enclosing class. So it always has to be created within a context of an outer instance.

                Outer.Inner i = new Outer().new Inner();

·    Just like other non-static features of a class. Can access all the features (even private) of the enclosing outer class. Have an implicit reference to the enclosing instance.

·    Cannot have any static members.

·    Can have any access modifier.

Local classes

·    Defined inside a block (could be a method, a constructor, a local block, a static initializer or an instance initializer). Cannot be specified with static modifier.

·    Cannot have any access modifier (since they are effectively local to the block)

·    Cannot declare any static members.(Even declared in a static context)

·    Can access all the features of the enclosing class (because they are defined inside the method of the class) but can access only final variables defined inside the method (including method arguments). This is because the class can outlive the method, but the method local variables will go out of scope – in case of final variables, compiler makes a copy of those variables to be used by the class. (New meaning for final)

·    Since the names of local classes are not visible outside the local context, references of these classes cannot be declared outside. So their functionality could be accessed only via super-class references (either interfaces or classes). Objects of those class types are created inside methods and returned as super-class type references to the outside world. This is the reason that they can only access final variables within the local block. That way, the value of the variable can be always made available to the objects returned from the local context to outside world.

·    Cannot be specified with static modifier. But if they are declared inside a static context such as a static method or a static initializer, they become static classes. They can only access static members of the enclosing class and local final variables. But this doesn’t mean they cannot access any non-static features inherited from super classes. These features are their own, obtained via the inheritance hierarchy. They can be accessed normally with ‘this’ or ‘super’.

Anonymous classes

·    Anonymous classes are defined where they are constructed. They can be created wherever a reference expression can be used.

·    An anonymous class is never abstract . An anonymous class is always an inner class; it is never static . An anonymous class is always implicitly final.

·    Anonymous classes cannot have explicit constructors. Instance initializers can be used to achieve the functionality of a constructor.

·    Anonymous classes can implement an interface (implicit extension of Object) or explicitly extend a class. Cannot do both.

Syntax: new interfacename() { } or new classname(can take arg.) { }

·    Keywords implements and extends are not used in anonymous classes.

·    Abstract classes can be specified in the creation of an anonymous class. The new class is a concrete class, which automatically extends the abstract class.

·    Discussion for local classes on static/non-static context, accessing enclosing variables, and declaring static variables also holds good for anonymous classes. In other words, anonymous classes cannot be specified with static, but based on the context, they could become static classes. In any case, anonymous classes are not allowed to declare static members. Based on the context, non-static/static features of outer classes are available to anonymous classes. Local final variables are always available to them.

·    E.g.

   btn.addActionListener(

     new ActionListener() {

       void ActionPerformed() {System.out.println(s);}

     }

   )

·    One enclosing class can have multiple instances of inner classes.

·    Inner classes can have synchronous methods. But calling those methods obtains the lock for inner object only, not the outer object.  If you need to synchronize an inner class method based on outer object, outer object lock must be obtained explicitly. Locks on inner object and outer object are independent.

·    Nested classes can extend any class or can implement any interface. No restrictions.

·    All nested classes (except anonymous classes) can be abstract or final.

·    Classes can be nested to any depth. Top-level static classes can be nested only within other static top-level classes or interfaces. Deeply nested classes also have access to all variables of the outer-most enclosing class (as well the immediate enclosing class’s)

·    Member inner classes can be forward referenced. Local inner classes cannot be.(?)

·    Outer class variables are accessible within the inner class, but they are not inherited. They don’t become members of the inner class. This is different from inheritance. (Outer class cannot be referred using ‘super’, and outer class variables cannot be accessed using ‘this’)

·    An inner class variable can shadow an outer class variable. If the inner class is sub-classed within the same outer class, the variable has to be qualified explicitly in the sub-class. To fully qualify the variable, use (classname.this.variablename). If we don’t correctly qualify the variable, a compiler error will occur. (Note that this does not happen in multiple levels of inheritance where an upper-most super-class’s variable is silently shadowed by the most recent super-class variable or in multiple levels of nested inner classes where an inner-most class’s variable silently shadows an outer-most class’s variable. Problem comes only when these two hierarchy chains (inheritance and containment) clash.)

·    If the inner class is sub-classed outside of the outer class (only possible with top-level nested classes) explicit qualification is not needed (it becomes regular class inheritance)

·    Inner class cannot have some name as any of its enclosing class.

Entity    Declaration Context    Accessibility Modifiers    Outer instance    Direct Access to enclosing context    Defines static or non-static members

Package level class    As package member    Public or default    No    N/A    Both static and non-static

Top level nested class (static)    As static class member    All    No    Static members in enclosing context    Both static and non-static

Non static inner class    As non-static class member    All    Yes    All members in enclosing context    Only non-static

Local class (non-static)    In block with non-static context    None    Yes    All members in enclosing context + local final variables    Only non-static

Local class (static)    In block with static context    None    No    Static members in enclosing context + local final variables    Only non-static

Anonymous class (non-static)    In block with non-static context    None    Yes    All members in enclosing context + local final variables    Only non-static

Anonymous class (static)    In block with static context    None    No    Static members in enclosing context + local final variables    Only non-static

Package level interface    As package member    Public or default    No    N/A    Static variables and non-static method prototypes

Top level nested interface (static)    As static class member    All    No    Static members in enclosing context    Static variables and non-static method prototypes

// Example 1

public class InnerInnerTest {

  public static void main(String s[]) {

    new Outer().new Inner().new InnerInner().new InnerInnerInner().doSomething();

    new Outer().new InnerChild().doSomething();

    new Outer2().new Inner2().new InnerInner2().doSomething();

    new InnerChild2().doSomething();

  }

}

class Outer {

  String name = "Vel";

  class Inner {

    String name = "Sharmi";

    class InnerInner {

      class InnerInnerInner {

        public void doSomething() {

          // No problem in accessing without full qualification,

          // inner-most class variable shadows the outer-most class variable

          System.out.println(name); // Prints "Sharmi"

          System.out.println(Outer.this.name); // Prints "Vel", explicit reference to Outer

// error, variable is not inherited from the outer class, it can be just accessible

//          System.out.println(this.name);

//          System.out.println(InnerInner.this.name);

//          System.out.println(InnerInnerInner.this.name);

// error, super cannot be used to access outer class.

// super will always refer the parent, in this case Object

//          System.out.println(super.name);

          System.out.println(Inner.this.name); // Prints "Sharmi", Inner has declared 'name'

        }

      }

    }

  }

  /* This is an inner class extending an inner class in the same scope */

  class InnerChild extends Inner {

    public void doSomething() {

// compiler error, explicit qualifier needed

// 'name' is inherited from Inner, Outer's 'name' is also in scope

//      System.out.println(name);

      System.out.println(Outer.this.name); // prints "Vel", explicit reference to Outer

      System.out.println(super.name); // prints "Sharmi", Inner has declared 'name'

      System.out.println(this.name); // prints "Sharmi", name is inherited by InnerChild

    }

  }

}

class Outer2 {

  static String name = "Vel";

  static class Inner2 {

    static String name = "Sharmi";

    class InnerInner2 {

      public void doSomething() {

        System.out.println(name); // prints "Sharmi", inner-most hides outer-most

        System.out.println(Outer2.name); // prints "Vel", explicit reference to Outer2's static variable

//        System.out.println(this.name); // error, 'name' is not inherited

//        System.out.println(super.name); // error, super refers to Object

      }

    }

  }

}

/* This is a stand-alone class extending an inner class */

class InnerChild2 extends Outer2.Inner2 {

    public void doSomething() {

      System.out.println(name); // prints "Sharmi", Inner2's name is inherited

      System.out.println(Outer2.name); // prints "Vel", explicit reference to Outer2's static variable

      System.out.println(super.name); // prints "Sharmi", Inner2 has declared 'name'

      System.out.println(this.name); // prints "Sharmi", name is inherited by InnerChild2

    }

}

// Example 2

public class InnerTest2 {

  public static void main(String s[]) {

    new OuterClass().doSomething(10, 20);

// This is legal

//    OuterClass.InnerClass ic = new OuterClass().new InnerClass();

//    ic.doSomething();

// Compiler error, local inner classes cannot be accessed from outside

//    OuterClass.LocalInnerClass lic = new OuterClass().new LocalInnerClass();

//    lic.doSomething();

    new OuterClass().doAnonymous();

  }

}

class OuterClass {

  final int a = 100;

  private String secret = "Nothing serious";

  public void doSomething(int arg, final int fa) {

    final int x = 100;

    int y = 200;

    System.out.println(this.getClass() + " - in doSomething");

    System.out.print("a = " + a + " secret = " + secret + " arg = " + arg + " fa = " + fa);

    System.out.println(" x = " + x + " y = " + y);

// Compiler error, forward reference of local inner class

//    new LocalInnerClass().doSomething();

    abstract class AncestorLocalInnerClass { } // inner class can be abstract

    final class LocalInnerClass extends AncestorLocalInnerClass { // can be final

      public void doSomething() {

        System.out.println(this.getClass() + " - in doSomething");

        System.out.print("a = " + a );

        System.out.print(" secret = " + secret);

//        System.out.print(" arg = " + arg);  // Compiler error, accessing non-final argument

        System.out.print(" fa = " + fa);

        System.out.println(" x = " + x);

//        System.out.println(" y = " + y); // Compiler error, accessing non-final variable

      }

    }

    new InnerClass().doSomething(); // forward reference fine for member inner class

    new LocalInnerClass().doSomething();

  }

  abstract class AncestorInnerClass { }

  interface InnerInterface { final int someConstant = 999;} // inner interface

  class InnerClass extends AncestorInnerClass implements InnerInterface {

    public void doSomething() {

      System.out.println(this.getClass() + " - in doSomething");

      System.out.println("a = " + a + " secret = " + secret + " someConstant = " + someConstant);

    }

  }

  public void doAnonymous() {

    // Anonymous class implementing the inner interface

    System.out.println((new InnerInterface() { }).someConstant);

    // Anonymous class extending the inner class

    ( new InnerClass() {

public void doSomething() {

  secret = "secret is changed";

  super.doSomething();

}

} ).doSomething();

  }

}

Thus an abstract class cannot be instantiated and so an object reference cannot be created. Remember that a class that contains any abstract methods the class itself is abstract and cannot be instantiated.

A local class is visible only within it's code block or method.

For class inside interface:(?)

1.    The class is always public.

2.    The class is always static.

3.    the class methods cannot call the methods declared in the interface.

Q1

public class MyClass1 {

public static void main(String argv[]){ }

/*Modifier at XX */ class MyInner {}

}

What modifiers would be legal at XX in the above code?

1) public

2) private

3) static

4) friend


PreparedStatement

作者：罗亭

email: tingluo@263.net

日期：7/30/2001 11:59:54 AM

From: http://www.theserverside.com-->SMTH

翻译: SuperMMX

     为什么 PreparedStatement 很重要, 以及怎样"正确"使用他们.

数据库有一个艰苦的工作. 它们不断地从许多客户端读取 SQL 查询, 对数据进行尽

可能高效的 查询. 处理语句可能成为一个代价较高的操作, 但是现在数据库都是很

好的设计, 这样这个困难 被减到最小. 但是这些优化需要应用程序开发者的协助,

这篇文章给你展示一下怎样正确使用 PreparedStatement 来漂亮地帮助数据库执行

这些优化.

一个数据库怎样执行一条语句?

显然, 不要希望这里有许多细节; 我们只看一下对这篇文章比较重要的部分. 当一个

数据库接收 到一条语句的时候, 数据库引擎首先解析这条语句, 查看语法错误. 一

旦语句解析了, 数据库 需要找出最有效的方法来执行这条语句. 这个计算起来代价

很大. 数据库检查什么索引(如果有 的话)能有所帮助, 或者它是否能全部读出一张

表中所有的记录. 数据库根据这些关于数据库所 存数据的统计数字来找出最好的

办法. 一旦制订出查询方案, 就可以由数据库引擎来执行.

需要 CPU 来产生访问方案. 理想的情况, 如果我们把相同的语句给数据库发送两

次, 我们期望 数据库重用第一条记录的访问方案. 这会比第二次重新产生方案要使

用较少的 CPU.

语句缓冲

数据库可以进行调节来做语句缓冲. 通常包含一些类型的语句缓冲. 缓冲使用语句

本身作为关键 字, 访问方案和相应的语句存储在缓冲区中. 这样就允许数据库引擎

对以前执行过的语句所使用 的访问方案进行重用. 举个例子来说, 如果我们向数据

库发送这样一条语句 "select a, b from t where c = 2", 计算好的访问方案就放入缓冲

区了. 如果我们以后再使用同样的语 句, 数据库就能重用以前的访问方案, 这样就

能节省 CPU.

但是要注意, 整条语句是一个关键字. 例如, 如果我们后来发送的语句是 "select a,b

from t where c = 3", 那么就不会找出以前的访问方案. 因为 "c=3" 和 "c=2" 是不一

样的. 所以, 例如:

     For(int I = 0; I < 1000; ++I)

{

PreparedStatement ps = conn.prepareStatement("select a,b from t

where c = " + I);

ResultSet rs = Ps.executeQuery();

Rs.close();

Ps.close();

}

这里不会用到缓冲. 每次循环向数据库发送一条不同的 SQL 语句. 每次循环都重新

计算新的访问 方案, 用这种方法我们会浪费大量的 CPU 周期. 但是, 看看下一个片

段:

PreparedStatement ps = conn.prepareStatement("select a,b from t where c

= ?");

For(int I = 0; I < 1000; ++I)

{

ps.setInt(1, I);

ResultSet rs = ps.executeQuery();

Rs.close();

}

ps.close();

这样就会高效得多. 发送给数据库的语句在 sql 中使用 '?' 符号来参数化. 这意味着

每次循环 发送的是同一条语句, 在 "c=?" 部分带有不同的参数. 这样就允许数据库

重用语句的访问方案, 是程序在数据库内部运行得更高效. 这基本上能使你的程序

运行得更快, 或者使数据库用户能更多 地使用 CPU.

PreparedStatement 和 J2EE 服务器

当我们使用 J2EE 服务器的时候, 事情会变得更加复杂. 通常情况下, 一个预先准备

好的语句 (prepared statement) 是和一个单独的数据库连接相关联的. 当连接关闭时,

语句就被丢弃 了. 一般来说, 一个胖客户端应用程序在得到一个数据库连接后会一

直保持到程序结束. 它会使用 两种方法创建所有的语句: 急切创建(eagerly) 或者 懒

惰创建(lazily). Eagerly是说, 当程序启动时全部创建. Lazily是说随用随创建. 急切

的方法会在程序启动时有些延时, 但是一旦程序启动以后, 运行很好. 懒惰的方法启

动很快, 但是当程序运行时, 预先准备的语句在第一次使用是创建. 这就会造成性能

不平衡, 知道所有的 语句都准备好了, 但是最终程序会和急切方法一样快. 哪一种

最好要看你需要的是快速启动还是 均衡的性能.

一个 J2EE 应用程序所带来的问题就是它不能像这样工作. 它只在一个请求的生存

时间中保持一个 连接. 这意味着在他处理每一个请求时都会重新创建语句, 就不象

胖客户端只创建一次, 而不是每 个请求都创建那样有效,

当 J2EE 服务器给你的程序一个连接时, 并不是一个真正的连接, 而是一个经过包装

的. 你可以 通过查看那个连接的类的名字来检验一下. 它不是一个数据库的 JDBC

连接, 是你的服务器创建 的一个类. 通常, 如果你调用一个连接的 close 方法, 那么

jdbc 驱动程序会关闭这个连接. 我们希望的是当 J2EE 应用程序调用 close 的时候,

连接会返回到连接池中. 我们通过设计一个 代理的 jdbc 连接类来做这些, 但看起来

就象是实际的连接. 当我们调用这个连接的任何方法时, 代理类就会把请求前递给

实际的连接. 但是, 当我们调用类似 close 的方法时, 并不调用实际 连接的 close 方

法, 只是简单地把连接返回给连接池, 然后把代理连接标记为无效, 这样当它 被应

用程序重新使用时, 我们会得到异常.

包装是非常有用的, 因为它帮助 J2EE 应用程序服务器实现者比较聪明地加上预先

准备语句的 支持. 当程序调用 Connection.prepareStatement 时, 由驱动程序返回一

个 PreparedStatement 对象. 当应用程序得到它时, 保存这个句柄, 并且在请求完成

时, 关闭 请求之前关闭这个句柄. 但是, 在连接返回到连接池之后, 以后被同样或者

另一个应用程序重用时, 那么, 我们就理论上希望同样的 PreparedStatement 返回给

应用程序.

J2EE PreparedStatement 缓冲

J2EE PreparedStatement 缓冲由 J2EE 服务器内部的连接池管理器使用一个缓冲区

来 实现. J2EE 服务器在连接池中保存一个所有数据库的预先准备语句的一个列表.

当一个程序 调用一个连接的 prepareStatement 方法时, 服务器先检查这个语句是否

已经有了, 如果 是, 相应的 PreparedStatement 就在缓冲区内, 就返回给应用程序, 如

果不是, 请求就 会传递给 jdbc 驱动程序, 请求/预先准备语句 对象就会加入到缓冲

区里.

对于每一个连接我们需要一个缓冲区, 因为这是 jdbc 驱动程序的工作要求. 任何返

回的 preparedStatement 都是针对这个连接的.

如果我们要利用缓冲区的优势, 要使用和前面相同的规则. 我们需要使用参数话的

查询, 这样 它们就会和已经在缓冲区的某一个匹配. 大多数应用程序服务器都允许

你调整缓冲区的大小.

概要

总之, 对于预先准备语句, 我们应该使用参数化的查询. 这样允许数据库重用已经存

在的访问 方案, 从而减轻数据库的负担. 这样的缓冲区是这个数据库范围的, 所以

你可以安排你所有的 应用程序, 使用相似的参数化的 SQL, 就会提高这样的缓冲区

方案的效率, 因为一个应用程序 可以使用另一个应用程序的语句. 一个应用服务器

的优势也在于此, 因为访问数据库的逻辑应该 集中在数据访问层上(OR 映射, 实体

bean 或者直接 JDBC).

最后, 预先准备语句的正确使用也让你利用应用程序服务器的预先准备语句的缓冲

区的好处. 会提高你的应用程序的性能, 因为应用程序通过对以前的预先准备语句

的重用减少 JDBC 驱动程序调用的次数. 这样使它能和胖客户端的效率竞争, 并且

去掉了不能保持一个长期 连接的坏处.

如果你使用参数化的预先准备语句, 就可以提高数据库和你的服务器端的代码的效

率. 这些提高 都会允许你的应用程序提高性能.


Reading and Writing a Properties File(From Sun)

作者：jeru

email: jeru@163.net

日期：8/5/2001 5:16:09 PM

Reading and Writing a Properties File

 // Read properties file.

 Properties properties = new Properties();

 try {

     properties.load(new FileInputStream(

       "infilename"));

 } catch (IOException e) {

 }

 // Write properties file.

 try {

     properties.store(new FileOutputStream(

       "outfilename"), null);

 } catch (IOException e) {

 }

Here is an example of the contents of a properties file:

     # a comment

     ! a comment

     a = a string

     b = a string with escape sequences \t

              \n \r \\ \" \' \ (space) \u0123

     c = a string with a continuation line \

         contination line

     d.e.f = another string


redhat6.1+apache+tomcat安装指南-jsp使用 (转)

作者：blackwhites

日期：2000-10-25 18:06:02

redhat6.1+apache+tomcat安装指南

下载说明：

在java.sun.com取得jdk1_2_2-linux-i386.tar.gz

在http://java.apache.org/builds/

在http://jakarta.apache.org/builds/tomcat/release/v3.1/bin/

在http://java.apache.org/jserv/dist/ApacheJServ-1.1-2_RH6x.i386.rpm

1、安装jdk1.2.2

#cp jdk1_2_2-linux-i386.tar.gz /usr/local

#tar xvzf jdk1_2_2-linux-i386.tar.gz

#ln -s jdk1.2.2 jdk

#ln -s jdk/jre jre

设置$JAVA_HOME,$CLASSPATH

#vi /etc/profile

加入：

JAVA_HOME=/usr/local/jdk

export JAVA_HOME

CLASSPATH=/usr/local/jdk/lib:/usr/local/jre/lib

export CLASSPATH

PATH=$PAHT:/usr/local/jdk/bin:/usr/local/jre/bin

2、安装tomcat

#cp jakarta-tomcat.tar.gz /usr/local

#tar xvzf jakarta-tomcat.tar.gz

#cd /usr/local/jakarta-tomcat/bin

运行tomcat服务器

#./startup.sh start(用./shutdown.sh stop结束tomcat服务器)

#lynx http://localhost:8080/

如果可以运行它的例子程序，那么tomcat服务器就安装成功了

3、安装jserv

#rmp -ivh　ApacheJServ-1.1-2_RH6x.i386.rpm

4、和apache连接

在httpd.conf中加入

Include /your/apache/conf/path/httpd/tomcat-apache.conf

(这个文件在你第一次运行tomcat时候生成)

注释掉

<IfModule mod_jserv.c>

Include /etc/httpd/conf/jserv/jserv.conf

</IfModule>

这几行

5、测试

重新启动你的apache, 然后运行tomcat服务器

#lynx http://localhost/examples/

如果你看见了jsp、servlet目录，你就成功了


RedHat7.0下安装Oracle8i

作者：star2002

email: star2002@263.net

日期：2001-7-17 10:23:23

Redhat7.0带的glibc2.2有问题，装不上Oracle8i

glibc2.1版本（GNU C Library，18M）下载地址：

ftp://ftp.kernel.org/pub/software/libs/glibc/hjl/sdk/2.1/i386-glibc-2.1-linux.tar.gz

下载后都放到了/backup目录

1.安装glibc 2.1包

RedHat7.0安装的是glibc2.2。Oracle 8I不能使用glibc 2.2进行编译，而需要glibc2.1的SDK。

以root帐号登录

[root@only_you /root] cd /

[root@only_you /] tar zxvf  /backup/i386-glibc-2.1-linux.tar.gz

隐藏/usr/bin 目录下的gcc,cc,ld，避免被Oracle installer使用

[root@only_you /] cd /usr/bin

[root@only_you bin] mkdir saved

[root@only_you bin] mv gcc cc ld saved

[root@only_you bin] ln -s  /usr/i386-glibc-2.1-linux/bin/i386-glibc21-linux-gcc gcc

[root@only_you bin] ln -s  gcc cc

[root@only_you bin] ln -s   /usr/i386-glibc-2.1-linux/bin/i386-glibc21-linux-ld ld

隐藏/usr/lib目录下的libc.so,libdl.so,libm.so和libpthread.so文件，避免被Oracle installer使用

[root@only_you bin] cd  ../lib

[root@only_you lib] mkdir saved

[root@only_you lib] mv libc.so (libdl.so libm.so libpthread.so  saved no these files--cgs)

[root@only_you lib] mv libc.a libdl.a libm.a libpthread.a  saved

2.创建oracle帐号

 以root帐号登录,开启一个terminal窗口,创建oracle帐号

  [root@only_you /root]useradd oracle

   [root@only_you /root]passwd oracle (设置oralce帐号的密码)

系统自动创建了/home/oracle目录，属主为oracle。我的Oracle就准备装在此目录下。安装文档上说要创建dba组，我看就免了。

3.解压缩oracle8161_tar.gz

以root帐号登录，执行：

[root@only_you /root] cd /backup

[root@only_you /backup] tar zxvf  oracle8161_tar.gz

在/backup/目录下生成Oralce8iR2目录，内含解包后的文件

4.设置oracle用户环境

使用oracle帐号登录，开启terminal窗口， 使用文本编辑器打开/home/oracle/.bash_profile最后加入如下环境变量的设置行：

ORACLE_HOME=/home/oracle; export ORACLE_HOME

LD_LIBRARY_PATH=/home/oracle/lib; export  LD_LIBRARY_PATH

ORACLE_BASE=/home/oracle; export ORACLE_BASE

ORACLE_SID=ORCL; export ORACLE_SID

ORA_NLS33=/home/oracle/ocommon/nls/admin/data; export  ORA_NLS33

NLS_LANG=american_america.zhs16cgb231280;export NLS_LANG

#其中，zhs16cgb231280是简体中文的代码。也是数据库的字符集。 american_america是区域代码，关系不大。

  Log out,重新以oracle帐号登录，键入env指令查看刚才设置的环境变量。

   [oracle@only_you oracle]env

5.创建oracle数据文件目录

我想把数据文件放在/home/oracle/data目录下，于是

[oracle@only_you oracle] mkdir data

6.安装Oracle 8i 数据库软件

（1）用oracle帐号登录，进入Xwindows,开启terminal窗口

（2）[oracle@only_you oracle] cd /backup/Oracle8iR2

（3）[oracle@only_you Oralcle8iR2]./runInstaller

（4）出错，显示信息为

     Xlib: connection to “:0.0” refused by server

     Xlib: Client is not authorized to connect to server

  说明xhost没有配置

（5）设置xhost

   开启一个新的terminal窗口

     [oracle@only_you oracle]su - (输入root密码)

     [root@only_you /root] xhost +

  保留此窗口

（6）在oracle用户窗口下继续安装

[oracle@only_you Oralcle8iR2] ./runInstaller

在出现的基于JAVA的Oracle installer界面上，点击Next。

（7） 在File Locations界面上，检查Destination Path为/home/oracle，点击Next

（8）在Unix Group Name窗口中，键入"oracle"，点击Next;

（9）一个窗口会弹出，要求以root身份运行/home/oracle/orainstRoot.sh

（10）切换到刚才保留的root用户窗口

   [root@only_you /root] cd /home/oracle

   [root@only_you oracle]./orainstRoot.sh

（11）返回刚才弹出的窗口，点击Retry。

（12）在出现的"Available products"窗口上选择"Oracle8I enterprise edition8.1.6.1.0"，点击Next。

（13）下一个画面上选择Typical ，点击Next

（14）在Privileged Operating System Groups界面上，点击Next

（15）提示输入global database name，自己喜欢什么就写什么，我写了个wap ，验证ORACLE_SID为ORCL，点击Next

（16）提示输入db files localtion,输入/home/oracle/data，点击Next

（17）在最后的Summary窗口上，点击Install

（18）现在正式开始安装了

    我看的文章上说这是个漫长的过程，至少需要 1~2个小时。为了保持清醒，我冲了杯咖啡，开始上网。没想到不到20分钟就完成了。

（19）在文件被拷贝和联接后，弹出一个要求运行root.sh的窗口

    切换到root用户窗口，

       [root@only_you oracle]./root.sh

    在询问local bin directory时按下Enter键

（20）返回要求运行root.sh的窗口上，点击OK

（21）Oracle Net8 Configuration和Oracle Database Configuration Assistants启动， 安装Net8和创建数据库。 这一步又花了20分钟左右的时间。

（22）Oracle Database Configuration Assistants结束后，在弹出的完成数据库创建窗 口上点击OK

（23）在End of Installation画面上，点击Exit ，在弹出的Do you reallywant to exit?窗口上点击yes

    现在所有的Oralce 8i Enterprise就安装好了！ 之后可以使用Oracle数据库管理工具svrmgrl、sql*plus来对安装好的数据库进行测试！

7．测试

[oracle@only_you oracle] svrmgrl

Oracle Server Manager Release 3.1.6.0.0 - Production

Copyright (c) 1997, 1999, Oracle Corporation.  All Rights Reserved.

Oracle8i Enterprise Edition Release 8.1.6.1.0 - Production

With the Partitioning option

JServer Release 8.1.6.0.0 - Production

SVRMGR>connect internal

Connected.

SVRMGR> shutdown

Database closed.

Database dismounted.

ORACLE instance shut down.

SVRMGR>startup

ORACLE instance started.

Total System Global Area                         56012784 bytes

Fixed Size                                          69616 bytes

Variable Size                                    38993920 bytes

Database Buffers                                 16777216 bytes

Redo Buffers                                       172032 bytes

Database mounted.

Database opened.

SVRMGR> quit

Server Manager complete.

OK。用sqlplus试试

[oracle@only_you oracle]$ sqlplus

SQL*Plus: Release 8.1.6.0.0 - Production on Sun Apr 15 09:20:12 2001

(c) Copyright 1999 Oracle Corporation.  All rights reserved.

Enter user-name: scott

Enter password: tiger

Connected to:

Oracle8i Enterprise Edition Release 8.1.6.1.0 - Production

With the Partitioning option

JServer Release 8.1.6.0.0 - Production

SQL> select * from emp;

应该出来一堆数据，有14条，没问题了。

SQL>quit

8．恢复以前的glibc

[root@only_you oracle] cd /usr/bin

[root@only_you bin] rm –f  gcc cc ld

[root@only_you bin] mv ./saved/* .

[root@only_you bin] rmdir saved

[root@only_you bin] cd /usr/lib

[root@only_you lib] mv ./saved/* .

[root@only_you lib] rmdir saved


Resin服务器平台介绍

作者：jspfuns

日期：2000-8-10 17:04:30

简介

Resin提供了最快的jsp/servlets运行平台。在java和javascript的支持下，Resin可以为任务灵活选用合适的开发语言。Resin的一种先进的语言XSL(XML stylesheet language)可以使得形式和内容相分离。

如果您选用jsp平台作为internet商业站点的支持，那么速度、价格和稳定性都是要考虑到的，resin十分出色，表现更成熟，很具备商业软件的要求。而且，它是全免费的。从站点下载的就是完整版本。所以值得向您推荐！

相对于tomcat3.1,后者更象是一个正在研究的项目。目前resin可以支持sun的j2ee，而tomcat不能直接支持，而j2ee是基于java服务器端大系统的基础。但tomcat结构非常合理，而且是apache组织的产品，因此有着很好的远景。

Resin1.1的特性包括：

1 支持jsp1.1和在服务器端编译的javascript.

2 比mod_perl,mod_php更快，比Jakarta Tomcat快3倍。您可以在Caucho的站点看到很全面的各种测试报告。

3 使用servlet2.2引擎。

4 自动的servlet/bean编译！

5 Servlet,XSL Filtering

6 支持IIS,Apache,Netscape和其它内置了HTTP/1.1的Web服务器。

7 XSLT和XPath1.0引擎。

8 企业级的共享软件（基于一个开放源码的协议）

安装与启动：

安装特别简单，只需将resin-1.1.3.zip解压缩到某个目录即可。

在运行之前需要有jdk的支持。

启动方式是执行：

resin-1.1.3/bin/httpd (在NT)

系统已经按照缺省设置启动，系统将监听8080端口。

jsp文件可放在resin-1.1.3/doc目录下；servlet可以放在resin-1.1.3/doc/web-inf/classes下面。


Resin与IIS结合的一些补充技巧

作者：jian

email: wjian111@163.net

日期：2001-5-22 2:54:07

软件环境：

JDK1.3 http://java.sun.com

resin1.2.0 http://www.caucho.com

Winnt 4.0 & IIS4

Win2000 & IIS5

    简单来说，直接运行Resin的bin目录下的setup.exe即可自动完成Resin与多种WEB服务器的结合，其原理是将ISAPI文件isapi_srun.dll拷贝至IIS的scripts目录并创建针对所有站点的ISAPI筛选器，对于IIS默认站点，一般已经建立了虚拟目录scripts，如果需要为同一机器上多站点中的某一个网站提供Resin服务，按Resin文档所说，可以通过为此站点创建同样的scripts虚拟目录实现。

    经过实验，我发现，完全可以绕过上述所有步骤：

一、在任意地方建立scripts目录，将Resin的bin目录下的isapi_srun.dll拷贝过来；

二、进入IIS管理器，在目标站点下建立具有可执行权限的虚拟目录scripts，对到刚刚创建的scripts物理目录；

三、打开目标站点的属性，创建新的ISAPI筛选器，名为Resin，其可执行文件对应到同一scripts目录下的isapi_srun.dll；

四、用命令net stop iisadmin停止WEB服务，用net start w3svc重启。

    这样，也可以将Resin与IIS结合起来。

    需要注意的是，最好不要将scripts目录放到系统所在的区，因为IIS搞不好就有可能出现一个CGI文件目录解码的漏洞，这个虚拟的scripts目录就会成为被攻击的罪魁祸首，我就是疏忽了这一点，参照Resin的文档进行配置，结果导致一台服务器遭遇骇客捣蛋，很是头痛了一番。

    谬误之处，请高手佐正。

---------------------------------------------

转载请注明出处：jian(http://www.21active.com


Resin中JSP的范例

作者：jspfuns

日期：2000-8-12 2:31:15

By Scott Ferguson

Blueski编译

目录如下：

1 引论

2 范例的框架: Hello, World

3 Servlet 评论

4 展示留言本

5 留言本的模式

6 作为应用属性的留言本

7 留言本的逻辑

8 结论

1 引论

JSP的强大优势在于把一种应用的商务逻辑和它的介绍分离开来。用 Smalltalk的面向对象的术语来说， JSP鼓励MVC(model-view-controller)的web应用。JSP的classes 或 beans 是模型, JSP 是这个视图, 而Servlet是控制器。

这个例子是一个简单的留言本，包括用户登录和留言。它被作为Resin平台的示范：

--执行角色

--模型 A 留言本

--用于新用户的login.jsp

--用于已注册用户的add.jsp

--控制器 GuestJsp, 一个用来管理状态的servlet

2 样板的框架: Hello, World

GuestJsp servlet的框架把 "Hello, World" 这个字符串传给login.jsp页面。这个框架为留言本设立结构。具体细节将在下面补充。

这个例子被编译后可以浏览到：

http://localhost:8080/servlet/jsp.GuestJsp

你可以看到页面上有这样的显示: Hello, world

JSP模板是以Servlet的处理开始然后把处理结果传给JSP页进行格式化。

以下使用了一个Servlet2.1 ServletContext的特性 getRequestDispatcher()。

请求的调度器在服务器上让servlets直接向前传送并包括了任何可能的子请求。对SSI包含来说这是一个更灵活的取代做法。

在servlet文件中请求的调度器可以包含任何页面,servlet,或JSP的结果。 GuestJsp将使用dispatcher.forward()来将控制传给JSP页进行格式化。

GuestJsp.java: Skeleton package jsp.GuestJsp;

import java.io.*;

import java.util.*;

import javax.servlet.*;

import javax.servlet.http.*;

/**

* GuestJsp is a servlet controlling user

* interaction with the guest book.

*/

public class GuestJsp extends HttpServlet {

/**

* doGet handles GET requests

*/

public void doGet(HttpServletRequest req,

HttpServletResponse res)

throws ServletException, IOException

{

// Save the message in the request for login.jsp

req.setAttribute("message", "Hello, world");

// get the application object

ServletContext app = getServletContext();

// select login.jsp as the template

RequestDispatcher disp;

disp = app.getRequestDispatcher("login.jsp");

// forward the request to the template

disp.forward(req, res);

}

}

servlet和jsp页使用HttpRequest对象中的属性进行通信。skeleton在"message"属性中保存了"Hello, World"。

当login.jsp启动时，它将捕捉到该字符串并将其打印出来。

由于Resin的JavaScript能够读取扩充的Bean模型，它可以将request.getAttribute("message")转换成为

JavaScript的对应物 request.attribute.message。

login.jsp: Skeleton <%@ page language=javascript %>

<head>

<title><%= request.attribute.message %></title>

</head>

<body bgcolor='white'>

<h1><%= request.attribute.message %></h1>

</body>

3 Servlet的复习

对于来自于ASP或CGI背景并转向jsp的人来说，

Servlets代替CGI脚本体现了Java在动态类加载方面的优势。servlet就是一个Java类，

它对Servlet或HttpServlet进行了扩展并放置到适当的路径中。Resin将自动加载servlet并执行它。

url /servlet/classname将request提交给Servlet请求器。请求器会从doc/WEB-INF/classes自动加载Java类的类名

并试图执行Servlet的service方法。

Resin将定期检查类文件以判断是否被修改过。如果被修改过，则将用新的servlet取代旧的。

4 显示留言本

在基本框架已经运行后, 下一步是创建model。

5 留言本模型

留言本是很直接的，这里知识包含了一下API。它遵从Bean模型以简化JavaScript。

同样的API可以工作于HashMap, 基于文件,以及数据库应用。

JSP文件只能存取public方法。所以JSP文件无法创建一个新的留言本或者增加一个新用户。

这是GuestJsp servlet的责任。

jsp.Guest.java API package jsp;

public class Guest {

Guest();

public String getName();

public String getComment();

}

Resin的JavaScript能读取Bean模型。所以使用JavaScript的JSP页面可以存取getName()和getComment()

作为属性。例如，你可以简化使用guest.name和guest.comment。

jsp.GuestBook.java API package jsp;

public class GuestBook {

GuestBook();

void addGuest(String name, String comment);

public Iterator iterator();

}

Resin的JavaScript同样可以读取iterator()调用，所以你可以使用JavaScript用于 ... 任何一个来取得用户：

for (var guest in guestBook) {

...

}

GuestBook作为application属性

为了使得例子保持简单，GuestJsp在application (ServletContext)中存取GuestBook。作为例子，

在application中保存数据是可以接受的，但对于完全成熟的应用，最好仅使用application将数据放到其它地方。

jsp.GuestJsp.java // get the application object

ServletContext app = getServletContext();

GuestBook guestBook;

// The guestBook is stored in the application

synchronized (app) {

guestBook = (GuestBook) app.getAttribute("guest_book");

// If it doesn't exist, create it.

if (guestBook == null) {

guestBook = new GuestBook();

guestBook.addGuest("Harry Potter", "Griffindor rules");

guestBook.addGuest("Draco Malfoy", "Slytherin rules");

app.setAttribute("guest_book", guestBook);

}

}

RequestDispatcher disp;

disp = app.getRequestDispatcher("login.jsp");

// synchronize the Application so the JSP file

// doesn't need to worry about threading

synchronized (app) {

disp.forward(req, res);

}

JSP文件本身是简单的。它从application获取留言本并在表中显示内容。通常，

application对象需要同步，因为一些客户端可能同时浏同一页面。

GuestJsp在jsp文件被调用之前小心处理了同步情况。

login.jsp: Display Guest Book <%@ page language=javascript %>

<head>

<title>Hogwarts Guest Book</title>

</head>

<body bgcolor='white'>

<h1>Hogwarts Guest Book</h1>

<table>

<tr><td width='25%'><em>Name</em><td><em>Comment</em>

<%

var guestBook = application.attribute.guest_book

for (var guest in guestBook) {

out.writeln("<tr><td>" + guest.name + "<td>" + guest.comment);

}

%>

</table>

</body>

Hogwarts Guest Book

Name Comment

Harry Potter Griffindor Rules

Draco Malfoy Slytherin Rules

6 留言本的规则（logic）--作为应用属性的留言本

留言本的规则是很简单的。如果用户没有登录，他会看到一个提示和登录表。

登录后他会看到提示并在一个表中加入留言。 login.jsp给出了登录的页面，add.jsp给出了

增加流言的页面。

GuestJsp在session变量中保存了规则信息。

执行'login'来登录或 'add'来增加留言。 其中

name： 用户名

password： 口令

comment：留言

7 留言本规则 ...

// name from the session

String sessionName = session.getValue("name");

// action from the forms

String action = request.getParameter("action");

// name from the login.jsp form

String userName = request.getParameter("name");

// password from the login.jsp form

String password = request.getParameter("password");

// comment from the add.jsp form

String comment = request.getParameter("comment");

// login stores the user in the session

if (action != null && action.equals("login") &&

userName != null &&

password != null && password.equals("quidditch")) {

session.putValue("name", userName);

}

// adds a new guest

if (action != null && action.equals("add") &&

sessionName != null &&

comment != null) {

guestBook.addGuest(sessionName, comment);

}

String template;

// if not logged in, use login.jsp

if (session.getValue("name") == null)

template = "login.jsp";

// if logged in, use add.jsp

else

template = "add.jsp";

RequestDispatcher disp;

disp = app.getRequestDispatcher(template);

...

login.jsp和add.jsp仅加上了不同forms在前一个section中显示代码。

login.jsp <%@ page language=javascript %>

<head>

<title>Hogwarts Guest Book: Login</title>

</head>

<body bgcolor='white'>

<h1>Hogwarts Guest Book</h1>

<table>

<tr><td width='25%'><em>Name</em><td><em>Comment</em>

<%

var guestBook = application.attribute.guest_book

for (var guest in guestBook) {

out.writeln("<tr><td>" + guest.name + "<td>" + guest.comment);

}

%>

</table>

<hr>

<form action='GuestJsp' method='post'>

<input type=hidden name='action' value='login'>

<table>

<tr><td>Name:<td><input name='Name'>

<tr><td>Password:<td><input name='Password' type='password'>

<tr><td><input type=submit value='Login'>

</table>

</form>

</body>

8 结论

Resin示例演示了扩充留言本的一些方法，包括加入一些智能的东西用于form处理。然而，由于forms取得更多的只能，即使是JSP模板也变得复杂化了。

有一个结论：XTP模板。


Servlet Step by Step (1)

作者：haohe

email: hao-he@263.net

日期：2000-11-14 20:02:14

Servlet step by step

（在安装好 JDk, Tomcat 后）

 1．直接运行servlet :

     A. 将编译好的 *.class放在webapps 下的 test, admin, examples, root, 任一目录下的\web-inf\classes目录中，(如test目录，下同)

     B. URL栏中输入：http://localhost:8080/test/servlet/classname

       即可运行servlet!

  2. 由Form传数据给servlet：

     A; 将 html 文件放在 test目录下 ，form中的                 ACTION="/test/servlet/getForm"

        method=get 方法对应 servlet 的doGet方法：

        public void doGet( HttpServletRequest req,

                      HttpServletResponse response)

        post 对doPost方法。

   3.中文问题

       javac -encoding ISO8859_1 myservlet.java    (ISO大写)


Servlet 和 XML：互为补充（二）（转）

作者：zergman

email: zergman@chinaasp.com

日期：2001-6-15 13:58:03

[b]清单 5. printDOMTree[/b] [TABLE][TR][TD][PRE]    [#993333]if[/#] (requestedSubtree.equalsIgnoreCase([#0000ff]"author"[/#]))      printDOMTree(author, out);    [#993333]else[/#] [#993333]if[/#] (requestedSubtree.equalsIgnoreCase([#0000ff]"lines"[/#]))      printDOMTree(lines, out);    [#993333]else[/#] [#993333]if[/#] (requestedSubtree.equalsIgnoreCase([#0000ff]"title"[/#]))      printDOMTree(title, out);    [#993333]else[/#]       printDOMTree(doc, out);[/PRE][/TD][/TR][/TABLE]

如果 subtree 参数是 author，则结果是：[TABLE][TR][TD][PRE]  <author>[#0000ff]    [/#]<last-name>[#0000ff]Shakespeare[/#]</last-name>[#0000ff]    [/#]<first-name>[#0000ff]William[/#]</first-name>[#0000ff]    [/#]<nationality>[#0000ff]British[/#]</nationality>[#0000ff]    [/#]<year-of-birth>[#0000ff]1564[/#]</year-of-birth>[#0000ff]    [/#]<year-of-death>[#0000ff]1616[/#]</year-of-death>[#0000ff]  [/#]</author>[/PRE][/TD][/TR][/TABLE]

如果 subtree 参数是 title，则结果是：[TABLE][TR][TD][PRE]<title>[#0000ff]Sonnet 130[/#]</title>[/PRE][/TD][/TR][/TABLE]

您可以查看[url href=http://www.cn.ibm.com/developerWorks/java/servlets-and-xml/xmlfromdom-src.html]HTML 视图[/url]或直接查看 [url href=http://www.cn.ibm.com/developerWorks/java/servlets-and-xml/xmlfromdom.java]Java 源文件[/url]。

[b]与数据库对接[/b]

我们的最后一个示例是根据数据库查询生成 XML。有许多方法可做到这一点（请参阅 developerWorks 的文章 [url href=http://www-4.ibm.com/software/developer/library/buildappl/genxml.html]GeneratingXML from a Data Store[/url]）；对于本例而言，我们将使用 IBM 的 XML Extender for DB2（请参阅[url href=http://www.cn.ibm.com/developerWorks/java/servlets-and-xml/index.shtml#resources]参考资料[/url]）。这个免费产品使您能够在 DB2 中存储 XML 文档。我们的查询从 DB2 中提取这些文档，然后将其传送给用户。

如果您使用 Oracle 8i 代替 DB2，您将会发现它自称具有类似的功能（请参阅[url href=http://www.cn.ibm.com/developerWorks/java/servlets-and-xml/index.shtml#resources]XML 的数据库，您可以将 XML 文档存储为字符大对象 (CLOB)，并以文本块的方式检索文档。

但是，在安装数据库以后，您需要完成以下三件事情才能使此代码工作：[OL][LI]首先，将 DbOwner、DbUserid 和 DbPasswd 变量改为适合系统的适当值。

[TABLE][TR][TD][PRE]   [#666666]/////////////////////////////////////////////////////////////////[/#]       [#666666]// 一定要正确更改这三个字符串，否则                            //[/#]   [#666666]// servlet 不会工作。                                          //[/#]   [#666666]/////////////////////////////////////////////////////////////////[/#]       [#006600]DbUserid[/#] = [#0000ff]"xxxxxxxx"[/#];   [#006600]DbPasswd[/#] = [#0000ff]"xxxxxxxx"[/#];   [#006600]DbOwner[/#] = [#0000ff]"xxxxxxxx"[/#];   [/PRE][/TD][/TR][/TABLE]

[LI]下一步，使用适合您的系统的 JDBC 驱动程序。我们在使用 DB2。

[TABLE][TR][TD][PRE]    [#993333]static[/#] [#006600]String[/#] [#006600]JDBCDriver[/#] = [#0000ff]"COM.ibm.db2.jdbc.app.DB2Driver"[/#];    ...    [#993333]try[/#]    {      Class.forName([#0000ff]"COM.ibm.db2.jdbc.app.DB2Driver"[/#]).newInstance();    }    [#993333]catch[/#] ([#006600]Exception[/#] [#000066]e[/#])    {      System.out.println([#0000ff]"Can't get the driver!"[/#]); e.printStackTrace();    }[/PRE][/TD][/TR][/TABLE]

[LI]如果你愿意，可以改掉下面的 SQL 查询语句。为了简化示例，此处仅检索 sales_order_view 表的 order 列中的全部 XML 文档。

[TABLE][TR][TD][PRE]    [#666666]// 我们在此处对 SQL 语句进行硬编码；如果根据用户输入[/#]    [#666666]// 限制查询，则情况会更为复杂。[/#]    [#006600]String[/#] [#000066]query[/#] = [#0000ff]"select order from "[/#] + [#006600]DbOwner[/#] + [#0000ff]".sales_order_view"[/#];[/PRE][/TD][/TR][/TABLE][/LI][/OL]

在 service 方法中，我们的 servlet 连接 DB2，执行一个查询（其结果为一组 XML 文档），分析查询结果，并将分析过的数据写入输出流中。清单 6 显示了与此关系最密切的代码部分：

[b]清单 6. xmlfromdb2.java[/b] [TABLE][TR][TD][PRE]    [#666666]// 我们在此处对 SQL 语句进行硬编码；如果根据用户输入[/#]    [#666666]// 限制查询，则情况会更为复杂。[/#]    [#006600]String[/#] [#000066]query[/#] = [#0000ff]"select order from "[/#] + [#006600]DbOwner[/#] + [#0000ff]".sales_order_view"[/#];    res.setContentType([#0000ff]"text/xml"[/#]);    [#993333]try[/#]      {        [#006600]ConInfo[/#] [#000066]index[/#] = [#993333]new[/#] [#006600]ConInfo[/#]();        [#006600]Connection[/#] [#000066]con[/#] = getCon(index);        [#006600]Statement[/#] [#000066]stmt[/#] = con.createStatement();        [#006600]ResultSet[/#] [#000066]rs[/#] = stmt.executeQuery(query);        ...        [#666666]// 显示结果集。我们从每行取出 XML 文档，[/#]        [#666666]// 对其进行分析，然后打印 DOM 树。当没有更多的行时，rs.next() 返回[/#]        [#666666]// false。[/#]        [#993333]while[/#] (rs.next())          {             [#006600]String[/#] [#000066]nextOrder[/#] = rs.getString(1).trim();            [#006600]Document[/#] [#000066]doc[/#] = [#990099]null[/#];            [#006600]StringReader[/#] [#000066]sr[/#] = [#993333]new[/#] [#006600]StringReader[/#](nextOrder);            [#006600]InputSource[/#] [#000066]iSrc[/#] = [#993333]new[/#] [#006600]InputSource[/#](sr);                        [#993333]try[/#]              {                parser.parse(iSrc);                doc = parser.getDocument();              }            [#993333]catch[/#] ([#006600]Exception[/#] [#000066]e[/#])              {                System.err.println([#0000ff]"Sorry, an error occurred: "[/#] + e);              }                        [#993333]if[/#] (doc != [#990099]null[/#])              printDOMTree(doc, out);          }[/PRE][/TD][/TR][/TABLE]

要了解全部细节，您可以查看[url href=http://www.cn.ibm.com/developerWorks/java/servlets-and-xml/xmlfromdb2-src.html]HTML 视图[/url]或直接查看[url href=http://www.cn.ibm.com/developerWorks/java/servlets-and-xml/xmlfromdb2.java]Java 源文件[/url]。

[b]小结[/b]

尽管这些 servlet 示例中没有一个可以改变世界，但它们确实展示了 XML 和 servlet 配合得有多么好。Servlet 是向客户发送内容的一种伟大机制，而 XML 是发送结构化数据的一种完美机制。您还可以使用 servlet 处理服务器上的 XML 文档，并将它们的内容发送给客户机。最重要的是，这两种技术都是跨平台技术，可为您的应用程序带来更大的灵活性和可移值性。

[b]参考资料[/b]

[UL][LI]了解有关 [url href=http://www-4.ibm.com/software/data/db2/extenders/xmlext/index.html]DB2XML Extender[/url] 的详细信息。产品附带的文档中有一个深入的教程，它可帮助您将 XML 文档带入关系数据库的世界中。 [LI]了解有关 [url href=http://www.oracle.com/database/oracle8i/]Oracle 数据库产品[/url]的详细信息。 [LI]试用 WebSphere 附带的免费 [url href=http://www.software.ibm.com/webservers/appserv/tutorial.html]servlet 教程[/url]。 [LI]通过 developerWorks 的 [url href=http://www.cn.ibm.com/developerWorks/education/xml/xmljava/index.html]XML programming in Java[/url] 教程深入研究如何分析 XML 文件。 [LI]下载作者为 City Java 用户组制作的演示文稿的 [url href=ftp://www6.software.ibm.com/software/developer/library/xmlservlet.pdf]PDF[/url] 版本。 [LI]在 [url href=http://www.gnu.org/software/emacs/emacs.html]www.gnu.org[/url] 网站学习使用 Emacs 彩色编码代码清单。HTMLize 程序包可从网址 [url href=http://jagor.srce.hr/~hniksic/htmlize.el]jagor.srce.hr[/url] 获得。[/LI][/UL]


Servlet 和 XML：互为补充（一）（转）

作者：zergman

email: zergman@chinaasp.com

日期：2001-6-15 13:57:18

[TABLE]                                 [TD]                                 [#FFFFFF]                                 [b]Download it now![/b][/#]                                 [/TD]                             [/TR]                             [TR]                                 [TD]                                                          [url href=ftp://www6.software.ibm.com/software/developer/library/servlets-and-xml.pdf][B]PDF[/B][/url]                                 (79.4 KB)

                                 [url href=http://www.adobe.com/prodindex/acrobat/readstep.html]FreeAcrobat&#153; Reader[/url]                                 [/TD]                             [/TR]-->[TR][TD][url href=http://www.cn.ibm.com/developerWorks/java/servlets-and-xml/index_eng.shtml][img]http://www.cn.ibm.com/developerWorks/i/src.jpg[/img][/url] [/TD][/TR][/TABLE]

[b]Servlet 和 XML：互为补充[/b]

Doug Tidwell

developerWorks 职员

2000 年 4 月

[TABLE][TR][TD][#ffffff][B]内容：[/B][/#][/TD][/TR][TR][TD][img]http://www.cn.ibm.com/developerWorks/i/c.gif[/img][/TD][/TR][TR][TD][img]http://www.cn.ibm.com/developerWorks/i/c.gif[/img][/TD][/TR][TR][TD][url href=http://www.cn.ibm.com/developerWorks/java/servlets-and-xml/index.shtml#2]我们的第一个 servlet 示例[/url][/TD][/TR][TR][TD] [url href=http://www.cn.ibm.com/developerWorks/java/servlets-and-xml/index.shtml#3]一个基本的 servlet[/url][/TD][/TR][TR][TD] [url href=http://www.cn.ibm.com/developerWorks/java/servlets-and-xml/index.shtml#4]生成 XML 段[/url][/TD][/TR][TR][TD] [url href=http://www.cn.ibm.com/developerWorks/java/servlets-and-xml/index.shtml#5]与数据库对接[/url][/TD][/TR][TR][TD] [url href=http://www.cn.ibm.com/developerWorks/java/servlets-and-xml/index.shtml#6]小结[/url][/TD][/TR][TR][TD] [url href=http://www.cn.ibm.com/developerWorks/java/servlets-and-xml/index.shtml#resources]参考资料[/url][/TD][/TR][TR][TD] [url href=http://www.cn.ibm.com/developerWorks/java/servlets-and-xml/index.shtml#author]作者简介[/url][/TD][/TR][TR][TD][img]http://www.cn.ibm.com/developerWorks/i/c.gif[/img][/TD][/TR][/TABLE]

[BLOCKQUOTE]研究 Java servlet 和 XML 如何共同生成 XML 文档和 DOM 树，以及它们如何与数据库对接。本文包括几项有用的技术：使用 HTTP 参数控制 DOM 树的处理与生成，而无需 XML 源文档。[/BLOCKQUOTE]

对 Java 程序员来说，Servlet 和 XML 是最令人振奋的两项技术。本文是为 2000 年 2 月 17 日旧金山 City Java 用户组准备的演示文稿。在本文中您将看到如何使用 servlet 组成一个简单的 XML 文档，构建一个 DOM 树，并将部分内容显示在用户屏幕上，最后您将看到如何从一个数据库查询生成 XML。

对于本文讨论的示例来说，我们将扩展 HTTPServlet 类。HTTPServlet 类提供了通常与 CGI 程序相关的功能。它支持 put 和 get，并且使您的代码具有对 HTTP 请求标头的完全访问权，包括 UserAgent 域。我们将创建一些简单的 servlet，并说明它们如何处理以 XML 标签标记的信息。在这一过程中我们还将说明文档对象模型 (DOM) 的一些方法。这些简单的应用程序将使您了解当将 servlet 和 XML 组合在一起时您能够做哪些事情。

[b]第一个 servlet 示例[/b]

作为开始，我们将编写一个用来生成 XML 文档的 10 行 servlet。在构建理解 XML 的 servlet 时，我们将按以下三个步骤进行： [OL][LI]将内容类型设为 text/xml。 [LI]创建 XML 文档。 [LI]将 XML 文档写回客户机。 [/LI][/OL]

在我们的大多数 Servlet 中，主要精力放在第二步。我们可能根据数据库查询创建一个 XML 文档，也可能基于从客户传送来的 HTTP 参数生成它，或者也可能使用其他类型的数据检索或生成方法。在本文的示例中，将主要考虑 HTTP 参数和数据库查询。

[b]一个基本的 servlet[/b]

对于第一个示例，第二步“创建 XML 文档”[I]不是[/I]我们所关心的；我们只想生成一个有效的 XML 文档。我们已将文档硬编码到源代码中，如清单 1 所示。[TABLE][TR][TD]Color-coding our colorful coding[/b] -->

[B]彩色编码[/B]

彩色编码清单是本文的一个特色，我们正在 dW 进行试验。为了生成我们的彩色编码清单，我正在使用一些开放源代码的工具。首先，我将文档（Java、HTML、XML 等）载入 Emacs 中。Emacs 定义了关键字、注释、函数名以及其他编程语言构件（大约有十多种）的颜色。在 Emacs 载入文件并为其加上颜色以后，我使用 HTMLize 程序包，这是一种用曾经流行的 Emacs Lisp 语言编写的开放源代码实用工具。HTMLize 接收一个清单（这个清单看起来与在 Emacs 中完全一样），然后将其转换为 HTML。结果将是一个完全彩色编码的文件，它突出显示关键字、注释、函数名等。

请告拆我们您对这些新的、改进的代码清单的想法。

如果您也想这样做，请参阅[url href=http://www.cn.ibm.com/developerWorks/java/servlets-and-xml/index.shtml#resources]

[b]清单 1. xmlfromscratch.java[/b] [TABLE][TR][TD][PRE][#993333]public[/#] [#993333]class[/#] [#006600]xmlfromscratch[/#] [#993333]extends[/#] [#006600]HttpServlet[/#] {  [#993333]public[/#] [#006600]void[/#] [#666633]service[/#]([#006600]HttpServletRequest[/#] [#000066]request[/#],                      [#006600]HttpServletResponse[/#] [#000066]response[/#])    [#993333]throws[/#] [#006600]IOException[/#], [#006600]ServletException[/#]  {    response.setContentType([#0000ff]"text/xml"[/#]);    [#006600]PrintWriter[/#] [#000066]out[/#] = response.getWriter();        out.println([#0000ff]"<?xml version=\"1.0\"?>"[/#]);    out.println([#0000ff]"<greeting language=\"en_US\">"[/#]);    out.println([#0000ff]"  Hello, World!"[/#]);    out.println([#0000ff]"</greeting>"[/#]);  }}[/PRE][/TD][/TR][/TABLE]

这一段令人兴奋的代码生成的结果如下所示：

[b]清单 2. xmlfromscratch.java 的结果[/b] [TABLE][TR][TD][PRE][#993333]<?[/#][#006600]xml version="1.0"[/#][#993333]?>[/#]<greeting>[#0000ff]  Hello, World![/#]</greeting>[/PRE][/TD][/TR][/TABLE]

您可以查看[url href=http://www.cn.ibm.com/developerWorks/java/servlets-and-xml/xmlfromscratch-src.html]HTML 视图[/url]或直接查看 [url href=http://www.cn.ibm.com/developerWorks/java/servlets-and-xml/xmlfromscratch.java]Java 源文件[/url]。

[b]生成 XML 段[/b]

现在，我们已经创建了一个 servlet，它通过硬编码生成一个没有意义的简单 XML 文档。在下一个 servlet 中，我们从零开始生成一个 DOM 树，然后将 DOM 树的一部分显示在请求者的屏幕上。向请求者发送回的 DOM 树部分取决于 servlet 接收到的 HTTP 参数。本例展示了几项有用的技术：使用 HTTP 参数控制 DOM 树的处理与生成，而无需 XML 源文档。

清单 3 显示了处理 HTTP 参数的代码段：

[b]清单 3. xmlfromdom.java[/b] [TABLE][TR][TD][PRE]  [#993333]public[/#] [#006600]void[/#] [#666633]service[/#]([#006600]HttpServletRequest[/#] [#000066]request[/#],                      [#006600]HttpServletResponse[/#] [#000066]response[/#])    [#993333]throws[/#] [#006600]IOException[/#], [#006600]ServletException[/#]  {    response.setContentType([#0000ff]"text/xml"[/#]);    [#006600]PrintWriter[/#] [#000066]out[/#] = response.getWriter();        [#006600]Enumeration[/#] [#000066]keys[/#];    [#006600]String[/#] [#000066]key[/#];    [#006600]String[/#] [#000066]requestedSubtree[/#] = [#0000ff]""[/#];    keys = request.getParameterNames();    [#993333]while[/#] (keys.hasMoreElements())    {      key = ([#006600]String[/#]) keys.nextElement();      [#993333]if[/#] (key.equalsIgnoreCase([#0000ff]"subtree"[/#]))        requestedSubtree = request.getParameter(key);    }[/PRE][/TD][/TR][/TABLE]

正如在上一个示例中那样，我们将内容类型设置为 text/xml。在此之后，我们使用 HttpServletRequest.getParameterNames 方法从 HTTP 请求中检索所有参数。

在处理完这些参数以后，我们需要查找用户所请求的信息。我们使用的信息从对象中构建 DOM 树；该 DOM 树包含了莎士比亚十四行诗的文本，以及关于这首十四行诗的其他信息。我们将根据 HTTP subtree 参数返回 DOM 树的一部分。清单 4 显示了构建 DOM 树的部分代码：

[b]清单 4. 构建 DOM 树[/b] [TABLE][TR][TD][PRE]  [#006600]Document[/#] [#000066]doc[/#] = [#990099]null[/#];  [#006600]Element[/#] [#000066]author[/#] = [#990099]null[/#];  [#006600]Element[/#] [#000066]lines[/#] = [#990099]null[/#];  [#006600]Element[/#] [#000066]title[/#] = [#990099]null[/#];   [#993333]public[/#] [#006600]void[/#] [#666633]initialize[/#]()  {      doc = ([#006600]Document[/#])Class.        forName([#0000ff]"org.apache.xerces.dom.DocumentImpl"[/#]).        newInstance();    [#993333]if[/#] (doc != [#990099]null[/#])      {        [#006600]Element[/#] [#000066]root[/#] = doc.createElement([#0000ff]"sonnet"[/#]);        root.setAttribute([#0000ff]"type"[/#], [#0000ff]"Shakespearean"[/#]);          author = doc.createElement([#0000ff]"author"[/#]);          [#006600]Element[/#] [#000066]lastName[/#] = doc.createElement([#0000ff]"last-name"[/#]);        lastName.appendChild(doc.createTextNode([#0000ff]"Shakespeare"[/#]));        author.appendChild(lastName);[/PRE][/TD][/TR][/TABLE]

我们创建了一个 Java 类的实例，该类实现了 DOM Document 接口，然后我们要求那个节点为我们创建各种节点。您很容易重新编写这个应用程序，使它通过分析 XML 文件生成 DOM 树。为了简化这个示例（并减少我的工作量），我们定义了一些实例变量来保存准备为其提供服务的节点的值。这些值在类声明顶部声明，并在 initialize 方法中初始化。

最后一步是将被请求的 DOM 树部分发送给用户。为了实现这一任务，我们使用一个递归方法，printDOMTree，它处理节点及其所有子节点。因为这个方法是递归的，所以我们从文档根节点还是从 DOM 树的其他节点开始并不重要。如果所请求的是我们知道的一个节点，则可以将这个节点传递给方法 printDOMTree。否则，我们可以传递 Document 节点。清单 5 显示了这一步骤。


servlet2.3(续)

作者：八脚鱼

email: o@test.com

日期：2001-6-29 9:54:44

标记库

新的规范中几个有关标记库的概念得到了澄清，而且规范的许多地方作了整理。对于标记库，新的规范还有一个重要的补充——迭代（iterator）标记。在当前的JSP 1.1规范中，如果标记要执行“循环”它就必须是一个BodyTag。然而，标记可能不必处理它的标记体，定义BodyTag导致容器生成效率低下的代码（setBodyContent()和其他一些方法仍将被调用）。为解决这个问题，JSP 1.2引入了迭代标记。

JSP 1.2规范引入了两个新的常量，即EVAL_BODY_BUFFERED和EVAL_BODY_AGAIN。EVAL_BODY_BUFFERED作为一个具有更明确含义的名字取代EVAL_BODY_TAG，两者的值是一样的。它意味着以前编写的代码仍旧可以运行。另外，它也意味着现在实现doStartTag()时应该返回EVAL_BODY_BUFFERED而不是EVAL_BODY_TAG。

EVAL_BODY_AGAIN也有与EVAL_BODY_BUFFERED同样的值，但它的名字同样具有更明确的含义。当标记想要让容器迭代（即反复执行）它的标记体时doAfterBody()方法应该返回这个值。

javax.servlet.jsp.IterationTag是JSP 1.2规范定义的新接口，要定义迭代行为就必须实现这个接口。IterationTag接口扩展SimpleTag并增加了一个doAfterBody()方法，BodyTag现在从IterationTag而不是SimpleTag继承。

实现迭代时，在BodyTag或者IterationTag中，doAfterBody()方法应该返回EVAL_BODY_AGAIN以强制容器再次执行标记，返回SKIP_BODY则终止迭代。为IterationTag编写的代码很简单，如Listing 5所示。

【Listing 5】

public class SimpleIterator extends TagSupport implements IterationTag {

  int loopCount;

}

public void setLoopCount(int loopCount) {

  this.loopCount = loopCount;

}

public int doStartTag() {

  return EVAL_BODY_INCLUDE;

}

public int doAfterBody() {

  while(--loopCount > 0) return EVAL_BODY_AGAIN;

  return SKIP_BODY;

}

}

    doStartTag()返回EVAL_BODY_INCLUDE，doAfterBody()返回EVAL_BODY_AGAIN以实现迭代，迭代完成时则返回SKIP_BODY。注意当前还没有与TagSupport、BodyTagSupport相配的IterationTagSupport类，不过这并不会真正带来什么麻烦。

    标记库的当前版本支持非常有限的合法性检验机制。TagExtraInfo类里面可以定义一个isValid方法，这个方法在页面编译的时候被调用，你可以用它检查属性名字、值、类型等。JSP 1.2规范提供了更强大的合法性检验机制。你可以定义一个检验器类，在它里面定义一个方法，这个方法通过参数得到包含标记库内一个或者多个标记的整个页面的XML描述，参见Listing 6的实例。这个方法在编译页面的时候被调用（它是一次性的调用。也就是说，你只付出一次调用的代价，而不是每次用户请求页面的时候都要调用）。

【Listing 6】

public class SimpleIteratorValidator extends TagLibraryValidator {

public String validate( String prefix, String uri, PageData page) {

    java.io.InputStream is = page.getInputStream();

    boolean b = validateJSP(is);

    // 一切正常

    if(b) return null;

     else return "Some Validation Error";

}

}

Listing 6的检验器扩展了javax.servlet.jsp.tagext.TagLibraryValidator，而且它覆盖了validate()。validate()方法的参数中传入了待检验标记的前缀以及相关的URI。第三个参数PageData允许你访问一个InputStream，这个InputStream包含XML格式的JSP页面。如果一切正常，validate方法返回null；如果检验不能通过，则它返回一个错误字符串。注意这是一个在页面编译时出现的错误，它只可能在开发阶段出现。

新的规范还涉及了其他一些问题。例如，JSP 1.2规范最终将定义一个JSP页面的XML描述。JSP页面的XML描述不仅对合法性检验很有用（如上面例子所示），而且对于工具软件供应商来说也很有用，因为它简化了JSP编辑器的开发。

新的JSP规范还允许我们把监听器定义成标记，而不是本文前面所出现的独立的类。JSP规范现在包含了一章关于本地化的内容，例如，它定义了为页面指定字符编码（Encoding）方式的能力。

新的规范中还加入了一个TryCatchFinally接口。这个接口允许标记在出现意料不到的异常时更好地进行资源清理工作。

JSP 1.2和Servlet 2.3规范实现了两个目标。它们澄清了当前规范中容易混淆的几个问题，增加了一些开发者需要的新功能。主要的变化包括监听器、过滤器以及经过修改的标记库模型。新的规范放在http://java.sun.com/aboutJava/communityprocess/first/jsr053/index.html


servlet2.3

作者：八脚鱼

email: o@test.com

日期：2001-6-29 9:53:50

明天的Java开发什么样？

（仙人掌工作室　2001年06月19日 14:44）

和当前的Servlet 2.2规范相比，Servlet 2.3 建议最终草案（PFD）最大的不同之处在于把filter（过滤器）和event（事件）加入了Servlet模型。过滤器提供了一种让应用程序在任何Servlet运行之前或运行之后执行标准动作的方法。很长时间以来，Servlet规范中都没有事件。事件提供的是特定动作的通知，特别是应用程序的启动/结束与会话的启动/结束，使得应用程序能够进行某些一次性的初始化/清理操作。

事件句柄

在许多场合，应用程序一启动就必须进行一些初始化操作。例如，创建和初始化JDBC连接时，或者创建JDBC连接池时，或者读取在应用程序的整个生命周期内都要用到的资源时，或者进行简单的检查以确保应用程序要用到的某些资源确实存在时，这种情况都会发生。在遵从2.2规范的容器上，应用程序没有保证能够执行这类初始化操作的办法。

在2.2规范中，部署Servlet时可以在web.xml文件中设置<load-on-startup>项目。这个元素应该包含一个正数，它决定了Servlet启动的次序，有关该标记的更多说明，请参见Servlet规范中的web.xml DTD说明。采用这种方案有几个问题。首先，Servlet规范说：“load-on-startup元素表示该Servlet应该（should）在Web应用程序启动的时候装入。”Servlet容器开发者可能把它理解成“应该装入，但不是必须装入”（因此，当应用程序启动时Servlet可能没有被装入）。另外一个问题在于，我们是在用一种与Servlet设计意图不同的方式使用Servlet。

Servlet有一个“请求”级的作用范围。也就是说，按照设计，Servlet对于每个客户端的请求执行一次。但在这里，Servlet有了“应用”级作用范围，它执行一次然后就挂起。

在2.2规范中，Servlet还可以被卸载——容器检查初始化Servlet，判断出Servlet在一段时间内已经没有使用，于是就卸载它。如果这个Servlet“占据”了一些启动的时候它初始化的资源，那么它应该释放这些资源。但是，Servlet不会被告知应用程序什么时候结束。Servlet的拆除方法会在应用程序关闭的时候被调用，但它无法知道是否所有其他Servlet也被卸载。当其他Servlet被卸载时，它们可能需要用到由初始化Servlet创建的资源。如果先卸载的是初始化Servlet，那么其他Servlet的处境就不妙了。

使用会话的时候也会出现完全同样的问题，因为应用程序完全有理由在建立/拆除每一个会话的时候，做一些初始化或者清理工作。

为了解决这些问题，Servlet 2.3规范引入了事件句柄的概念。事件句柄是一些回应和处理由容器初始化的特定事件的代码。Servlet 2.3规范定义了两种类型的事件：应用程序事件，会话事件。对于所有这两种事件类型，事件句柄能够捕获启动和结束事件，能够捕获属性改变事件。

我们将把注意力主要集中到应用程序的启动和结束事件上，但对于其他事件类型来说，代码也是相似的。

我们来看看事件句柄的代码。应用程序的事件句柄必须扩展javax.servlet.ServletContextListener接口。这个接口有两个方法，即contextDestroyed()和contextInitialized()。两个方法都有一个ServletContextEvent参数。正如你可以猜想到的，contextInitialized()方法在应用程序启动的时候被调用，contextDestroyed()方法在应用程序结束的时候被调用。ServletContextEvent类有一个方法，它能够返回正在被创建或拆除的ServletContext的引用。

    举例来说，假设你正在设计一个基于Web的简单白页（White Page）应用。在这个应用中，用户通过email查找姓名和地址，如Listing 1所示。这个例子在一个Hashtable（散列表）中保存白页的内容，散列表必须在应用程序启动的时候创建，而且它必须保存到应用程序的ServletContext。

【Listing 1】

import javax.servlet.ServletContextListener;

  public class WhitePagesListener implements ServletContextListener {

   Hashtable whitePages;

     public WhitePagesServletListener() {

       whitePages = new Hashtable();

   }

    public void contextDestroyed(ServletContextEvent sce) { }

    public void contextInitialized(ServletContextEvent sce) {

        System.out.println("contextInitialized");

        WhitePageEntry wpe = new WhitePageEntry("Kevin Jones", "555-1234");

        whitePages.put("kev@dev.com", wpe);

        wpe = new WhitePageEntry( "Simon Horrell", "555-8765");

        whitePages.put("simon@dev.com", wpe);

        wpe = new WhitePageEntry( "Don Box", "555-2137");

        whitePages.put("don@dev.com", wpe);

        // 把散列表保存到ServletContext

        sce.getServletContext().setAttribute( "addrbook", whitePages);

}

}

代码非常简单。WhitePagesListener类实现了ServletContextListener。散列表由类的构造方法创建。contextDestroyed()方法什么事情也不错，因为应用程序是被完全卸载，我们不需要清理过程。令人感兴趣的部分是在contextInitialized()方法内。这个方法把我们需要的数据保存到散列表，然后把散列表保存到ServletContext。当contextInitialized()被调用时，它的参数中传入了一个对ServletContextEvent对象的引用。ServletContextEvent类有一个getServletContext()方法，它返回的是对当前应用程序的ServletContext的引用。获得ServletContext的引用之后，散列表就被保存到了ServletContext之中。

应用程序必须经过配置才能使用这个事件句柄，具体通过修改应用程序的部署描述器（即web.xml文件）完成：

<web-app>

<listener>

  <listener-class>

   WhitePagesListener

  </listener-class>

</listener>...

用这种方法可以配置任意数量的监听器（Listener）。调用监听器的次序就是它们在部署描述器中出现的次序。监听器属于Singleton，开发者必须负责进行同步。这一点对于本例来说不是特别重要，但对于所有其它类型的应用来说，它可能很重要。

过滤器

过滤器是Servlet 2.3规范中最主要的新增功能。过滤器使得Servlet开发者能够在请求到达Servlet之前截取请求，在Servlet处理请求之后修改应答。正如我们即将看到的，过滤器的创建和安装都很简单。

所有的过滤器实现javax.filter.Filter接口。这个接口有三个方法：setFilterConfig()方法，在最后的规范中，这个方法很可能被两个方法取代，取代的方法可能是init(Filterconfig config)和destroy()；getFilterConfig()方法，在规范的稍后版本中这个方法可能消失；doFilter()方法。setFilterConfig()方法在第一次创建过滤器实例以及从调用链删除过滤器时被调用。创建实例时，传入过滤器的参数是一个非null的FilterConfig对象。这个对象使得过滤器能够访问它的名字和当前的ServletContext。初始化参数可以作为过滤器配置的一部分指定，这些参数也可以通过FilterConfig访问。当过滤器被删除时，传入setFilterConfig()的是一个值为null的FilterConfig。

配置过滤器时，我们可以把过滤器关联到一个或者多个Servlet上。只要请求传递到了与某个过滤器关联的Servlet上，该过滤器就会执行。过滤器以调用链中一部分的形式执行，执行次序由过滤器在部署描述器（即web.xml文件）中的次序决定。容器通过调用过滤器的doFilter()方法执行过滤器。doFilter()方法的参数包括一个ServletRequest对象、一个ServletResponse对象、一个javax.servlet.FilterChain对象。

FilterChain只有一个方法doFilter()，过滤器通过调用该方法把请求和应答继续向调用链的下一环传递。当然这也意味着，过滤器可以不再向调用链传递它，从而阻塞调用。此时，当前过滤器必须负责生成合适的应答内容。

举一个例子，我们来看一个在调试Servlet时很有用的Servlet，如Listing 2所示。这个过滤器将输出“下游”Servlet和过滤器设置的HttpRequest头和HttpResponse头。如果让这个过滤器成为调用链中的第一个过滤器，它可能能够让我们捕获所有的应答头，不过容器可能决定以过滤器永远不能了解的方式设置应答头：

【Listing 2】下面清单中的Servlet将输出由“下游”Servlet和 过滤器设置的HttpRequest头和HttpResponse头

public class DumpHeadersimplements

javax.servlet.Filter {

  private FilterConfig fc;

  private ServletContext ctx;

  ... ...

public FilterConfig getFilterConfig() {

  return fc;

}

publicvoid setFilterConfig( FilterConfig filterConfig) {

fc = filterConfig;

if(fc !=null) ctx = fc.getServletContext();

}

}

这个过滤器实现了javax.servlet.Filter接口，所以它必须按照前面所显示的实现setFilterConfig()和getFilterConfig()。setFilterContext()保存了一个对ServletContext的引用以便以后使用。

doFilter()方法的实现需要一点技巧。doFilter()的第一部分很简单：获取request对象，分析HTTP方法和查询字符串，然后枚举请求头。代码如Listing 3所示。

【Listing 3】下面显示了doFilter()第一部分的实现，它获取request对象， 分析HTTP方法和查询字符串。

public void doFilter(

  ServletRequest request,

  ServletResponse response, chain)

  throws java.io.IOException,

  ServletException {

   // 获得request对象

   HttpServletRequest req = (HttpServletRequest)request;

   // 重新构造HTTP请求行

   String temp;

   temp = req.getMethod() + req.getRequestURI();

   if(req.getQueryString() != null)

   temp += "?"+req.getQueryString();

   temp += " " + req.getProtocol();

   // 用ServletContext记录HTTP请求行

   ctx.log(temp); temp = "";

   // 枚举请求头

   Enumeration names = req.getHeaderNames();

   while(names.hasMoreElements()) {

      String name = (String)names.nextElement();

      temp += name + ": " ;

      Enumeration values = req.getHeaders(name);

      while (values.hasMoreElements()) {

         String value = (String)values.nextElement();

         temp += value + " ";

       } // 记录 ctx.log(temp); temp = "";

}

但处理应答比较复杂，而且它涉及到Servlet的一个新概念——封装request和response对象。在Servlet规范以前的版本中，调用任何一个要求有[Http]ServletRequest或[Http]ServletResponse的方法时，你必须把传递给Servlet service方法的request和response对象传递给该方法。最明显的例子就是分派请求的时候：

RequestDispatch rd = getServletContext().getRequestDispatcher("foo"); rd.forward(request, response); // or rd.include(request, response);

假设你想执行一个包含操作，然后，在把应答发送给客户端之前又要对它进行处理，或者在把应答发送给客户端之前从RequestDispatcher.forward()查看应答。在以前的Servlet规范中，这是不可能的。但在Servlet 2.3规范中这一切都成为可能。

新的规范定义了两个新类，即javax.servlet.http.HttpServletRequestWrapper和javax.servlet.http.HttpServletResponseWrapper（这两个类从它们各自的非HTTP版本继承）。这两个类的构造方法如下：

HttpServletRequestWrapper(HttpServletRequest request) HttpServletResponseWrapper(HttpServletResponse response)

在这些类中，这两个方法的默认行为是把调用传递给它们所封装的对象。使用这些类的时候，你一般要派生这些类并覆盖那些感兴趣的方法。对于本文的过滤器，我们必须创建一个应答封装器，记录对addXXXHeader()方法和setXXXHeader()方法的调用。另外，我们还想要捕获对setStatus()、setContentLength()、setContentType()和setLocale()方法的调用。代码如Listing 4所示，它简单地记录了对各个方法的调用。

【Listing 4】应答封装器。下面的代码为过滤器构造了一个应答封装器， 它将记录对addXXXHeader()和setXXXHeader()方法的调用。另外， 它还要捕获对setStatus()、setContentLength()、setContentType()和 setLocale()方法的调用。

class HeaderResponseWrapper extends

HttpServletResponseWrapper {

  ServletContext ctx;

  public HeaderResponseWrapper(

    HttpServletResponse response,

    ServletContext ctx) {

      super(response);

      this.ctx = ctx;

   }

  public void addCookie(Cookie cookie) {

    ctx.log("Set-Cookie: " + cookie.getName() + ":" + cookie.getValue());

    super.addCookie(cookie);

   }

  public void addHeader(String name, String value) {

    ctx.log(name + ": " + value); super.addHeader(name, value);

   }

  public void addIntHeader(String name, int value) {

    ctx.log(name + ": " + value); super.addIntHeader(name, value);

   }

  public void addDateHeader(String name, long value) {

    ctx.log(name + ": " + value); super.addDateHeader(name, value);

   }

  public void setHeader( String name, String value) {

    ctx.log(name + ": " + value); super.setHeader(name, value);

   }

  public void setIntHeader( String name, int value) {

    ctx.log(name + ": " + value); super.setIntHeader(name, value);

   }

  public void setDateHeader( String name, long value) {

    ctx.log(name + ": " + value); super.setDateHeader(name, value);

   }

  public void setStatus(int sc) {

    ctx.log("status: " + sc); super.setStatus(sc);

   }

  public void setStatus( int sc, java.lang.String sm) {

    ctx.log("status: " + sc); super.setStatus(sc, sm);

   }

  public void setContentLength(int len) {

    ctx.log("Content-Length: " + len); super.setContentLength(len);

   }

  public void setContentType(java.lang.String type) {

    ctx.log("Content-Type: " + type); super.setContentType(type);

   }

  public void setLocale(java.util.Locale loc) {

    ctx.log("locale: " + loc); super.setLocale(loc);

   }

}

在过滤器中，我们按照如下方式使用这个对象：

HttpServletResponse resp = (HttpServletResponse)response;

HeaderResponseWrapper hrespw = new HeaderResponseWrapper(resp, ctx);

System.out.println("********");

chain.doFilter(request, hrespw);

注意创建好封装器之后chain.doFilter()方法就被调用。

定义好过滤器之后就应该安装它。为此，web.xml中应该定义一个filter元素：

<filter>

<filter-name>

  Headers Filter

</filter-name>

<filter-class>

  DumpHeaders

</filter-class>

<!- optional <init-params> ->

</filter>

完成这一步之后，你还要把过滤器和你想要过滤的资源关联起来。这时你有两种选择：在web.xml中，把过滤器关联到单个命名的Servlet，或者把过滤器关联到一个URL。如下所示：

<filter-mapping>

<filter-name>

  Headers Filter

</filter-name>

<servlet-name>

AddressBookServlet

</servlet-name>

</filter-mapping>

或者：

<filter-mapping>

<filter-name>

  Header Filter

</filter-name>

<url-pattern>/*</url-pattern>

</filter-mapping>

输出的请求头如下所示：

GET/AddressBook/Browse.jsp HTTP/1.1

accept: */*

referer: http://localhost/ AddressBook/

accept-language: en-gb

accept-encoding: gzip, deflate

user-agent: Mozilla/4.0 ( compatible; MSIE 5.5; Windows NT 5.0)

host: localhost

connection: Keep-Alive

cookie: JSESSIONID= E0F9646772F4448004C16122020664F1

输出的应答头如下所示：

Content-Type: text/html;charset=8859_1 Content-Type: text/plain

如果进行网络跟踪，你会发现这里少了一些东西。例如，状态代码没有显示，这是因为有一些应答头由容器在过滤器链执行之前或之后设置。

过滤器有很多用途，比如验证、转换、加密/解密。但有一点必须注意：你可以把过滤器关联到任意资源，而不仅仅是Servlet。如果你使用的是一个插入到其他Web服务器的Servlet引擎，Web服务器很可能会不依赖于Servlet容器独立地提供服务。在这种情况下，Servlet容器将接收不到所有的请求，所以过滤器也就不会总是被执行。

JSP 1.1中有一个问题涉及到请求分派。如果你有一个页面执行include操作，jsp:include有一个必须设置为true的强制性flush（刷新）属性：

<jsp:include page="somePage" flush="true" />

这个属性强制容器把当前缓冲区内容刷新到客户端。这样，JSP页面不能再设置任意HTTP应答头。JSP 1.2规范已经修正这个问题，jsp:include标记中现在已经可以指定flush="false"。


Servlet和JSP的通信

作者：jspfuns

日期：2000-8-9 11:28:38

以下的代码片段演示了一个servlet，它例示(instantiates)一个bean并且用由浏览器发送的Form数据将它初始化。此后bean被加入request,而这一要求被送到JSP页面（Bean1.jsp）,JSP通过request取得并做进一步处理。

public void doPost (HttpServletRequest request,

HttpServletResponse response) {

try {

govi.FormBean f = new govi.FormBean();

String id = request.getParameter("id");

f.setName(request.getParameter("name"));

f.setAddr(request.getParameter("addr"));

f.setAge(request.getParameter("age"));

//use the id to compute

//additional bean properties like info

//maybe perform a db query, etc.

// . . .

f.setPersonalizationInfo(info);

request.setAttribute("fBean",f);

getServletConfig().getServletContext().getRequestDispatcher

("/jsp/Bean1.jsp").forward(request, response);

} catch (Exception ex) {

. . .

}

}

在将它从通过useBean操作中的缺省的请求范围，即scope首次执行后，即可在JSP文件的Bean1.jsp处理fBean。

<jsp:useBean id="fBean" class="govi.FormBean" scope="request"/>

<jsp:getProperty name="fBean" property="name" />

<jsp:getProperty name="fBean" property="addr" />

<jsp:getProperty name="fBean" property="age" />

<jsp:getProperty name="fBean" property="personalizationInfo" />


Servlet基础例程 - HelloServlet - Linux版本

作者：何志强

email: hhzqq@21cn.com

日期：2000-8-10 17:07:04

/*

作者：何志强[hhzqq@21cn.com]

日期：2000-08-10

版本：1.0

功能：Servlet基础例程 - HelloServlet

*/

import java.io.*;

import java.text.*; //MessageFormat

import javax.servlet.*;

import javax.servlet.http.*;

public class HelloServlet extends HttpServlet{

   //页面标题

   protected static final String strTitle = "Servlet基础例程 - HelloServlet";

   //页眉

   protected static final String strHeader =

      "<html>"+

      "<head>"+

      "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=gb2312\">"+

      "<title>{0}</title>"+

      "</head>"+

      "<body>";

   //页脚

   protected static final String strFooter =

      "</body>"+

      "</html>";

   //表单

   protected static final String strForm =

      "<form action=\"{0}\" method=\"post\">"+

      "您尊姓大名：<input type=\"text\" name=\"name\">"+

      "<input type=\"submit\" name=\"submit\" value=\"提交\">"+

      "</form>";

   protected static final String strHello =

      "您好，{0}，欢迎来到Servlet/JSP世界！";

   //出错信息

   protected static final String strError =

      "<h2><font color=\"#ff0000\">{0}</font></h2>";

   protected void doGet(HttpServletRequest req,HttpServletResponse resp) throws ServletException,IOException{

      process(req,resp);

   }

   protected void doPost(HttpServletRequest req,HttpServletResponse resp) throws ServletException,IOException{

      process(req,resp);

   }

   protected void process(HttpServletRequest req,HttpServletResponse resp) throws ServletException,IOException{

      try{

         String submit = req.getParameter("submit");

         if(submit==null)

           printForm(req,resp);

         else

           printHello(req,resp);

      }

      catch(Exception e){

         printError(e.toString(),req,resp);

      }

   }

   protected void printForm(HttpServletRequest req,HttpServletResponse resp) throws ServletException,IOException{

      //在使用PrintWriter前得先设置Content Type

      resp.setContentType("text/html");

      PrintWriter out = resp.getWriter();

      //输出页眉

      out.print(MessageFormat.format(strHeader,new Object[]{strTitle+" - 请输入尊姓大名"}));

      //输出表单

      out.print(MessageFormat.format(strForm,new Object[]{req.getContextPath()+req.getServletPath()}));

      //输出页脚

      out.print(strFooter);

      out.flush();

   }

   protected void printHello(HttpServletRequest req,HttpServletResponse resp) throws ServletException,IOException{

      //获取用户输入的数据

      String name = req.getParameter("name");

      if(name==null)

         name = "无名氏";

      //在使用PrintWriter前得先设置Content Type

      resp.setContentType("text/html");

      PrintWriter out = resp.getWriter();

      //输出页眉

      out.print(MessageFormat.format(strHeader,new Object[]{strTitle+" - 欢迎您"}));

      //输出欢迎信息

      out.print(MessageFormat.format(strHello,new Object[]{name}));

      //输出页脚

      out.print(strFooter);

      out.flush();

   }

   protected void printError(String error, HttpServletRequest req,HttpServletResponse resp) throws ServletException,IOException{

      //在使用PrintWriter前得先设置Content Type

      resp.setContentType("text/html");

      PrintWriter out = resp.getWriter();

      //输出页眉

      out.print(MessageFormat.format(strHeader,new Object[]{strTitle+" - 出错信息"}));

      //输出出错信息

      out.print(MessageFormat.format(strError,new Object[]{error}));

      //输出页脚

      out.print(strFooter);

      out.flush();

   }

}


Servlet基础例程 - HelloServlet - NT版本

作者：何志强

email: hhzqq@21cn.com

日期：2000-8-10 17:06:20

/*

作者：何志强[hhzqq@21cn.com]

日期：2000-08-10

版本：1.0

功能：Servlet基础例程 - HelloServlet

*/

import java.io.*;

import java.text.*; //MessageFormat

import javax.servlet.*;

import javax.servlet.http.*;

public class HelloServlet extends HttpServlet{

   //页面标题

   protected static final String strTitle = "Servlet基础例程 - HelloServlet";

   //页眉

   protected static final String strHeader =

      "<html>"+

      "<head>"+

      "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=gb2312\">"+

      "<title>{0}</title>"+

      "</head>"+

      "<body>";

   //页脚

   protected static final String strFooter =

      "</body>"+

      "</html>";

   //表单

   protected static final String strForm =

      "<form action=\"{0}\" method=\"post\">"+

      "您尊姓大名：<input type=\"text\" name=\"name\">"+

      "<input type=\"submit\" name=\"submit\" value=\"提交\">"+

      "</form>";

   protected static final String strHello =

      "您好，{0}，欢迎来到Servlet/JSP世界！";

   //出错信息

   protected static final String strError =

      "<h2><font color=\"#ff0000\">{0}</font></h2>";

   protected void doGet(HttpServletRequest req,HttpServletResponse resp) throws ServletException,IOException{

      process(req,resp);

   }

   protected void doPost(HttpServletRequest req,HttpServletResponse resp) throws ServletException,IOException{

      process(req,resp);

   }

   protected void process(HttpServletRequest req,HttpServletResponse resp) throws ServletException,IOException{

      try{

         String submit = req.getParameter("submit");

         if(submit==null)

           printForm(req,resp);

         else

           printHello(req,resp);

      }

      catch(Exception e){

         printError(e.toString(),req,resp);

      }

   }

   protected void printForm(HttpServletRequest req,HttpServletResponse resp) throws ServletException,IOException{

      //在使用PrintWriter前得先设置Content Type

      resp.setContentType("text/html;charset=gb2312");

      PrintWriter out = resp.getWriter();

      //输出页眉

      out.print(MessageFormat.format(strHeader,new Object[]{strTitle+" - 请输入尊姓大名"}));

      //输出表单

      out.print(MessageFormat.format(strForm,new Object[]{req.getContextPath()+req.getServletPath()}));

      //输出页脚

      out.print(strFooter);

      out.flush();

   }

   protected void printHello(HttpServletRequest req,HttpServletResponse resp) throws ServletException,IOException{

      //获取用户输入的数据

      String name = req.getParameter("name");

      if(name==null)

         name = "无名氏";

      else

         //对用户输入的数据作必要的字符编码转换

         name = new String(name.getBytes("iso-8859-1"));

      //在使用PrintWriter前得先设置Content Type

      resp.setContentType("text/html;charset=gb2312");

      PrintWriter out = resp.getWriter();

      //输出页眉

      out.print(MessageFormat.format(strHeader,new Object[]{strTitle+" - 欢迎您"}));

      //输出欢迎信息

      out.print(MessageFormat.format(strHello,new Object[]{name}));

      //输出页脚

      out.print(strFooter);

      out.flush();

   }

   protected void printError(String error, HttpServletRequest req,HttpServletResponse resp) throws ServletException,IOException{

      //在使用PrintWriter前得先设置Content Type

      resp.setContentType("text/html;charset=gb2312");

      PrintWriter out = resp.getWriter();

      //输出页眉

      out.print(MessageFormat.format(strHeader,new Object[]{strTitle+" - 出错信息"}));

      //输出出错信息

      out.print(MessageFormat.format(strError,new Object[]{error}));

      //输出页脚

      out.print(strFooter);

      out.flush();

   }

}


soap规范（1）

作者：bdsc

email: bdscca@yahoo.ca

日期：2001-7-13 17:02:54

SOAP:简单对象访问协议

(2001-1-1)

摘要

SOAP是用在分散或分布的环境中交换信息的简单的协议，它是一个基于XML的协议，包括三个部分：封装定义了一个描述消息中包含什么内容以及如何处理它们的框架，编码规则用于表示应用程序定义的数据类型的实例，另外还有一个表示远程过程调用和应答的协定。SOAP被设计为可以与各种其它协议结合使用；但这篇文章仅描述如何将SOAP和HTTP及HTTP扩展框架相结合。

目录

1. 简介

1.1 设计目标

1.2 符号协定

1.3 SOAP消息举例

2. SOAP消息交换模型

3. 与XML的关系

4. SOAP封装

4.1.1 SOAP encodingStyle属性

4.1.2 封装版本模型

4.2 SOAP头

4.2.1 使用SOAP头属性

4.2.2 SOAP actor属性

4.2.3 SOAP mustUnderstand属性

4.3 SOAP体

4.3.1 SOAP头和体的关系

4.4 SOAP 错误

4.4.1 SOAP错误代码

5. SOAP编码

5.1 XML编码类型规则

5.2 简单类型

5.2.1 字符串

5.2.2 枚举

5.2.3 字符数组

5.3 多态 Accessor

5.4 复合类型

5.4.1 复合值和对值的引用

5.4.2 数组

5.4.2.1 PartiallyTransmitted Arrays

5.4.2.2 稀疏数组

5.4.3 一般复合类型

5.5 缺省值

5.6 SOAP root属性

6. 在HTTP中使用SOAP

6.1 SOAP HTTP请求

6.1.1 HTTP头中的SOAPAction域

6.2 SOAP HTTP应答

6.3 HTTP扩展框架

6.4 SOAP HTTP举例

7. 用SOAP表示RPC

7.1 RPC和SOAP体

7.2 RPC和SOAP头

8. 安全考虑

9. 参考文献

A. SOAP封装举例

A.1 请求编码举例

A.2 应答编码举例

1. 简介

SOAP以XML形式提供了一个简单、轻量的用于在分散或分布环境中交换结构化和类型化信息的机制。SOAP本身并没有定义任何应用程序语义，如编程模型或特定语义的实现；实际上它通过提供一个有标准组件的包模型和在模块中编码数据的机制，定义了一个简单的表示应用程序语义的机制。这使SOAP能够被用于从消息传递到RPC的各种系统。

SOAP包括三个部分

SOAP封装（见第4节）结构定义了一个整体框架用来表示消息中包含什么内容，谁来处理这些内容以及这些内容是可选的或是必需的。

SOAP编码规则（见第5节）定义了用以交换应用程序定义的数据类型的实例的一系列机制。

SOAP RPC表示（见第7节）定义了一个用来表示远程过程调用和应答的协定。

虽然这三个部分都作为SOAP的一部分一起描述，但它们在功能上是相交的。特别的，封装和编码规则是在不同的名域中定义的，这种模块性的定义方法增加了简单性。

在SOAP封装，SOAP编码规则和SOAP RPC协定之外，这个规范还定义了两个协议的绑定，描述了在有或没有HTTP扩展框架[6]的情况下，SOAP消息如何包含在HTTP消息[5]中被传送。

1.1 设计目标

SOAP的主要设计目标是简单性和可扩展性，这意味着传统的消息系统和分布对象系统的某些性质不是SOAP规范的一部分。这些性质包括：

分布式碎片收集

成批传送消息

对象引用（要求分布式碎片收集）

激活机制（要求对象引用）

1.2 符号约定

这篇文章中的关键字 "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", 和"OPTIONAL"的解释在RFC-2119 [2]中。

这篇文章中用到的名域前缀 "SOAP-ENV" 和 "SOAP-ENC"分别与"http://schemas.xmlsoap.org/soap/envelope/"和"http://schemas.xmlsoap.org/soap/encoding/"关联。

整篇文档中，名域前缀“xsi”被假定为与URI "http://www.w3.org/1999/XMLSchema-instanceSchema规范[11]定义）相连。类似的，名域前缀”xsd“被假定为与URI "http://www.w3.org/1999/XMLSchema"（在 [10]中定义）相连。名域前缀”tns“用来表示任意名域。所有其它的名域前缀都只是例子。

名域URI的基本形式”some－URI“表示某些依赖于应用程序或上下文的URI[4]。

这个规范用扩展BNF（在RFC－2616[5] 描述）描述某些结构。

1.3 SOAP消息举例

在这个例子中，GetLastTradePrice SOAP 请求被发往 StockQuote服务。这个请求携带一个字符串参数和ticker符号，在SOAP应答中返回一个浮点数。XML名域用来区分SOAP标志符和应用程序特定的标志符。这个例子说明了在第6节中定义的HTTP绑定。如果SOAP中管理XML负载的规则完全独立于HTTP是没有意义的，因为事实上该负载是由HTTP携带的。

在Appendix A中有更多的例子。

例1 在HTTP请求中嵌入SOAP消息

POST /StockQuote HTTP/1.1

Host:

http://www.stockquoteserver.com

Content-Type: text/xml;

charset="utf-8"

Content-Length: nnnn

SOAPAction:

"Some-URI"

<SOAP-ENV:Envelope

  xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"

  SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">

   <SOAP-ENV:Body>

       <m:GetLastTradePrice xmlns:m="Some-URI">

           <symbol>DIS</symbol>

       </m:GetLastTradePrice>

   </SOAP-ENV:Body>

</SOAP-ENV:Envelope>

下面是一条应答消息，包括HTTP消息，SOAP消息是其具体内容：

例2 在HTTP应答中嵌入SOAP消息

HTTP/1.1 200 OK

Content-Type: text/xml;

charset="utf-8"

Content-Length:

nnnn

<SOAP-ENV:Envelope

  xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"

  SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>

   <SOAP-ENV:Body>

       <m:GetLastTradePriceResponse xmlns:m="Some-URI">

           <Price>34.5</Price>

       </m:GetLastTradePriceResponse>

   </SOAP-ENV:Body>

</SOAP-ENV:Envelope>

2. SOAP消息交换模型

SOAP消息从发送方到接收方是单向传送，但正如上面显示的，SOAP消息经常以请求/应答的方式实现。

SOAP实现可以通过开发特定网络系统的特性来优化。例如，HTTP绑定（见第6节）使SOAP应答消息以HTTP应答的方式传输，并使用同一个连接返回请求。

不管SOAP被绑定到哪个协议，SOAP消息采用所谓的”消息路径“发送，这使在终节点之外的中间节点可以处理消息。

一个接收SOAP消息的SOAP应用程序必须按顺序执行以下的动作来处理消息：

识别应用程序想要的SOAP消息的所有部分 （见4.2.2节）

检验应用程序是否支持第一步中识别的消息中所有必需部分并处理它。如果不支持，则丢弃消息（见4.4节）。在不影响处理结果的情况下，处理器可能忽略第一步中识别出的可选部分。

如果这个SOAP应用程序不是这个消息的最终目的地，则在转发消息之前删除第一步中识别出来的所有部分。

为了正确处理一条消息或者消息的一部分，SOAP处理器需要理解：所用的交换方式（单向，请求/应答，多路发送等等），这种方式下接收者的任务，RPC机制（如果有的话）的使用（如第7节中所述），数据的表现方法或编码，还有其它必需的语义。

尽管属性比如SOAP encodingstyle（见4.1.1节）可以用于描述一个消息的某些方面，但这个规范并不强制所有的接收方也必须有同样的属性并取同样的属性值。举个例子，某一特定的应用可能知道一个元素表示一条遵循第7节约定的RPC请求，但是另外一些应用可能认为指向该元素的所有消息都用单向传输，而不是类似第7节的请求应答模式。

（译者注：交互双方的SOAP消息并不一定要遵循同样的格式设定，而只需要以一种双方可理解的格式交换信息就可以了）

3. 与XML的关系

所有的SOAP消息都使用XML形式编码（更多有关XML的信息请见[7]）

一个SOAP应用程序产生的消息中，所有由SOAP定义的元素和属性中必须包括正确的名域。SOAP应用程序必须能够处理它接收到的消息中的SOAP名域（见4.4节），并且它可以处理没有SOAP名域的SOAP消息，就象它们有正确的名域一样。

SOAP定义了两个名域（更多有关XML名域的信息请见[8]）

SOAP封装的名域标志符是"http://schemas.xmlsoap.org/soap/envelope/"

SOAP的编码规则的名域标志符是"http://schemas.xmlsoap.org/soap/encoding/"

SOAP消息中不能包含文档类型声明，也不能包括消息处理指令。[7]

SOAP使用"ID"类型"id"属性来指定一个元素的唯一的标志符，同时该属性是局部的和无需校验的。SOAP使用"uri-reference"类型的"href"属性指定对这个值的引用，同时该属性是局部的和无需校验的。这样就遵从了XML规范[7]，XML Schema规范[11]和XML连接语言规范[9]的风格。

除了SOAP mustUnderstand 属性(见4.2.3节）和SOAP actor属性(见4.2.2节）之外，一般允许属性和它们的值出现在XML文档实例或Schema中（两者效果相同）。也就是说，在DTD或Schema中声明一个缺省值或固定值和在XML文档实例中设置它的值在语义上相同。

4. SOAP封装

SOAP消息是一个XML文档，包括一个必需的SOAP封装，一个可选的SOAP头和一个必需的SOAP体。在这篇规范剩余部分中，提到SOAP消息时就是指这个XML文档。这一节中定义的元素和属性的名域标志符为："http://schemas.xmlsoap.org/soap/envelope/"。一个SOAP消息包括以下部分：

在表示这个消息的XML文档中，封装是顶层元素。

应用SOAP交换信息的各方是分散的且没有预先协定，SOAP头提供了向SOAP消息中添加关于这条SOAP消息的某些要素(feature)的机制。SOAP定义了少量的属性用来表明这项要素（feature）是否可选以及由谁来处理。（见4.2节）

SOAP体是包含消息的最终接收者想要的信息的容器（见4.3节）。SOAP为SOAP体定义了一个Fault元素用来报告错误信息。

语法规则如下所示：

封装

元素名是 "Envelope"

在SOAP消息中必须出现。

可以包含名域声明和附加属性。如果包含附加属性，这些属性必须限定名域。类似的，"Envelope"可以包含附加子元素，这些也必须限定名域且跟在SOAP体元素之后。

SOAP头 （见4.2节）

元素名是"Header"

在SOAP消息中可能出现。如果出现的话，必须是SOAP 封装元素的第一个直接子元素。

SOAP头可以包含多个条目，每个都是SOAP头元素的直接子元素。所有SOAP头的直接子元素都必须限定名域。

SOAP体 （见4.3节）

元素名是"Body"

在SOAP消息中必须出现且必须是SOAP封装元素的直接子元素。它必须直接跟在SOAP头元素（如果有的话）之后。否则它必须是SOAP封装元素的第一个直接子元素。

SOAP体可以包括多个条目，每个条目必须是SOAP体元素的直接子元素。SOAP体元素的直接子元素可以限定名域。SOAP定义了SOAP Fault元素来表示错误信息。(见4.4节).

4.1.1 SOAP encodingStyle 属性

EncodingStyle全局属性用来表示SOAP消息的序列化规则。这个属性可以在任何元素中出现，作用范围与名域声明的作用范围很相似，为这个元素的内容和它的所有没有重载此属性的子元素。SOAP消息没有定义缺省编码。

属性值是一个或多个URI的顺序列表，每个URI确定了一种或多种序列化规则，用来不同程度反序列化SOAP消息，举例如下：

"http://schemas.xmlsoap.org/soap/encoding/"

"http://my.host/encoding/restrictedhttp://my.host/encoding/"

""

第5节中定义的序列化规则由URI"http://schemas.xmlsoap.org/soap/encoding/"确定。使用这个特定序列化规则的消息应该用encodingStyle属性说明这一点。另外，所有以"http://schemas.xmlsoap.org/soap/encoding/"开头的URI中的序列化规则与第5节中定义的SOAP编码规则相一致。

一个零长度的URI（""）明确显示所含元素没有任何编码形式。这可以用来取消上一级元素的所有编码声明。

4.1.2 封装版本模型

SOAP没有定义常规的基于主版本号和辅版本号的版本形式。SOAP消息必须有一个封装元素与名域"http://schemas.xmlsoap.org/soap/envelope/"VersionMismatch 错误信息（见4.4节）。

4.2 SOAP头

SOAP为相互通信的团体之间提供了一种很灵活的机制：在无须预先协定的情况下，以分散但标准的方式扩展消息。可以在SOAP头中添加条目实现这种扩展，典型的例子有认证，事务管理，支付等等。

头元素编码为SOAP封装元素的第一个直接子元素。头元素的所有直接子元素称作条目。

条目的编码规则如下：

一个条目有它的完整的元素名（包括名域URI和局部名）确定。SOAP头的直接子元素必须有名域限制。

SOAP encodingStyle属性可以用来指示条目所用的编码形式（见4.1.1节）

SOAP mustUnderstand属性（见4.2.3节）和SOAP actor属性（见4.2.2节）可以用来指示如何处理这个条目以及由谁来处理。（见4.2.1节）

4.2.1 使用头属性

这一节中定义的SOAP头属性确定了SOAP消息的接收者应该怎样按第2节中所述的方式处理消息。产生SOAP消息的SOAP应用程序，应该仅仅在SOAP头元素的直接子元素中使用这些SOAP头属性。SOAP消息的接收者必须忽略所有不在SOAP头元素的直接子元素中SOAP头属性。

下面的例子是一个SOAP头，包括一个元素标志符"Transaction"，"mustUnderstand"取值为"1"和数值5。这应该以如下方式编码：

<SOAP-ENV:Header>

   <t:Transaction

      xmlns:t="some-URI" SOAP-ENV:mustUnderstand="1">

          5

   </t:Transaction>

</SOAP-ENV:Header>

4.2.2 SOAP actor属性

一个SOAP消息从始节点到终节点的过程中，可能沿着消息路径经过一系列SOAP中间节点。一个SOAP中间节点是一个可以接收转发SOAP消息的应用程序。中间节点和终节点由URI区分。

可能SOAP消息的终节点并不需要所有部分，而在消息路径上的一个和几个中间节点可能需要这些内容。头元素的接收者扮演的角色类似于一个过滤器，防止这些只发给本接受者的消息部分扩散到其它节点。即一个头元素的接收者必须不转发这些头元素到SOAP消息路径上的下一个应用程序。同样的，接收者可能插入一个相似的头元素。

SOAP actor全局属性可以用于指示头元素的接收者。SOAP actor属性的值是一个URI。URI "http://schemas.xmlsoap.org/soap/actor/next"

省略SOAP actor属性表示接收者是SOAP消息的终节点。

如果这个属性要生效，它必须出现在SOAP消息实例中。（见第3节和4.2.1节）

4.2.3 SOAP mustUnderstand属性

SOAP mustUnderstand全局属性用来指示接受者在处理消息时这个条目是否必须处理。条目的接收者由SOAP actor属性定义（见4.2.2节）。MustUnderstand属性的值是"1" 或 "0"。缺少SOAP mustUnderstand属性在语义上等同于它的值为"0"。

如果一个头元素的SOAP mustUnderstand属性的值是"1",那么条目的接受者必须或者遵守语义（如以元素的全名传送）并按照语义正确的处理，或者放弃处理消息（见4.4节）。

SOAP mustUnderstand 属性考虑了消息演变的准确性（robust evolution）。必须假定包含SOAP mustUnderstand属性且值为"1"的元素以某种方式修改了它们的父元素或同层元素的语义。以这种方式连接元素确保了语义上的变化不会被那些不能完全理解它的接收者忽略。

如果这个属性要生效，它必须出现在SOAP消息实例中。（见第3节和4.2.1节）

4.3 SOAP体

SOAP体元素提供了一个简单的机制，使消息的最终接收者能交换必要的信息。使用体元素的典型情况包括配置RPC请求和错误报告。

体元素编码为SOAP封装元素的直接子元素。如果已经有一个头元素，那么体元素必须紧跟在头元素之后，否则它必须是SOAP封装元素的第一个直接子元素。

体元素的所有直接子元素称作体条目，每个体条目在SOAP体元素中编码为一个独立的元素。

条目的编码规则如下：

一个条目由它的元素全名（包括名域URI和局部名）确定。SOAP体元素的直接子元素可能是名域限制的。

SOAP encodingStyle属性可能用来指示条目（见4.1.1节）的编码方式。

SOAP定义了一个Fault条目用来报告错误信息。（见4.4节）

4.3.1 SOAP头和体的关系

虽然头和体定义为独立的元素，它们实际上是有关系的。体条目和头条目的关系如下：体条目在语义上等同于actor属性为缺省值且mustUnderstand属性值为"1"的头条目。不使用actor属性则表示缺省的actor。（见4.2.2节）

4.4 SOAP错误

SOAP错误元素用于在SOAP消息中携带错误和（或）状态信息。如果有SOAP错误元素，它必须以以体条目的方式出现，并且在一个体元素中最多出现一次。

SOAP错误元素定义了以下四个子元素：

faultcode

faultcode元素给软件提供了一个识别此错误的算法机制。SOAP错误元素必须有faultcode子元素，并且它的值必须是一个合法的名（在[8]节定义）。SOAP定义一些SOAP faultcode描述基本的SOAP错误（见4.4.1节）。

faultstring

faultstring元素提供了一个错误解释，而不是为了软件处理。faultstring元素类似于HTTP中定义(见[5]，第6.1节）的'Reason-Phrase'。SOAP错误元素必须有faultstring子元素，并且它应该提供一些错误本质的解释信息。

faultactor

faultactor元素提供了在消息路径上是谁导致了错误发生的信息（见第2节）。它类似于SOAP actor属性（见4.2.2节），只是SOAP actor指的是头条目的目的地，faultactor指的是错误的来源。faultactor属性的值是用来区分错误来源的URI。不是SOAP消息的最终目的地的应用程序必须在SOAP Fault元素中包含faultactor元素。消息的最终目的地可以使用faultactor元素明确的指示是它产生了这个错误（参见下面的detail元素）

detail

detail元素用来携带与Body元素有关的应用程序所要的错误信息。如果Body元素的内容不能被成功的处理，则必须包含detail子元素。它不能用来携带属于头条目的错误信息。头条目的详细出错信息必须由头条目携带。

Fault元素中没有detail元素表示这个错误与Body元素的处理无关。在有错误的时候，这可以用来区分Body元素有没有被正确的处理。

detail元素的所有直接子元素称作detail条目，并且每个detail条目在detail元素中编码为独立的元素。

detail条目的编码规则如下（参见例10）：

一个detail条目由它的元素全名（包括名域URI和局部名）确定。SOAP体元素的直接子元素可能是名域限制的。

SOAP encodingStyle属性可能用来指示detail条目（见4.1.1节）的编码方式。

也可以有其它的Fault子元素，只要它们是名域限制的。


soap规范（2）

作者：bdsc

email: bdscca@yahoo.ca

日期：2001-7-13 17:05:30

4.4.1 SOAP 错误代码

在描述这个规范中定义的错误时，这一节中定义的Faultcode值必须用在faultcode元素中。这些faultcode值得名域标志符为"http://schemas.xmlsoap.org/soap/envelope/"

缺省的SOAP faultcode值以可扩展的方式定义，允许定义新的SOAP faultcode值，并与现有的faultcode值向后兼容。使用的机制类似于HTTP中定义的1xx, 2xx, 3xx等基本的状态类（见[5]第10节），不过，它们定义为XML合法名（见 [8] 第3节 ），而不是整数。字符"."（点）作为faultcode的分隔符，点左边的错误代码比右边的错误代码更为普通。如：

Client.Authentication

这篇文档中定义的faultcode值是：

名称 含义

VersionMismatch 处理方发现SOAP封装元素有不合法的名域（见4.1.2节）

MustUnderstand 处理方不理解或者不服从一个包含值为"1"的mustUnderstand属性的 SOAP头元素的直接子元素。（见4.2.3节）

Client Client错误类表示消息的格式错误或者不包含适当的正确信息。例如，消息可能缺少正确的认证和支付信息。一般地，它表示消息不能不作修改就重发。参见4.4节SOAP Fault detail子元素的描述。

Server Server错误类表示由于消息的处理过程而不是消息的内容本身使得消息消息不能正确的处理。例如，处理消息时可能要与其它处理器通信，但它没有响应。这个消息可能在迟一点的时间处理成功。 SOAP Fault子元素的详细信息参见4.4节

5. SOAP编码

SOAP编码格式基于一个简单的类型系统，概括了程序语言，数据库和半结构化数据等类型系统的共同特性。一个类型或者是一个简单的（标量的）类型，或者是由几个部分组合而成的复合类型，其中每个部分都有自己的类型。以下将详细描述这些类型。这一节定义了类型化对象的序列化规则。它分两个层次。首先，给定一个与类型系统的符号系统一致的Schema（译者注：这里的schema不是符合XML语法的schema，而仅仅表示广义的用于表示消息结构的定义方式），就构造了XML语法的Schema。然后，给定一个类型系统的Schema和与这个Schema一致的特定的值，就构造了一个XML文档实例。反之，给定一个依照这些规则产生的XML文档实例和初始的Schema，就可以构造初始值的一个副本。

这一节中定义的元素和属性的名域标志符为"http://schemas.xmlsoap.org/soap/encoding/"

鼓励使用这一节中描述的数据模型和编码方式，但也可以在SOAP中使用其他的数据模型和编码方式。（见4.1.1节）

5.1 XML中的编码类型规则

XML允许非常灵活的数据编码方式。SOAP定义了一个较小的规则集合。这一节在总的层次上定义了这些编码规则，下一节将描述特定类型的编码规则的细节。这一节定义的编码规则可以与第7节中所述的RPC调用和应答映射结合使用。

下面的术语用来描述编码规则:

一个"value"是一个字符串，类型（数字，日期，枚举等等）的名或是几个简单值的组合。所有的值都有特定的类型。

一个"simple value"没有名部分， 如特定的字符串，整数，枚举值等等。

一个"compound value"是相关的值的结合，如定单，股票报表，街道地址等等。

在"compound value"中，每个相关的值都潜在的以名，序数或这两者来区分。这叫作"accessor"。复合值的例子有定单和股票报表等等。数组也是复合值。在复合值中，多个accessor有相同的名是允许的，例如RDF就是这样做的。

一个"array"是一个复合值，成员值按照在数组中的位置相互区分。

一个"struct"也是一个复合值，成员值之间的唯一区别是accessor名，accessor名互不相同。

一个"simple type"是简单值的类，如叫做"string" "integer"的类，还有枚举类等等。

一个"compound type"是复合值的类。复合类型的例子有定单类，它们有相同的accessor名（shipTo, totalCost等），但可能会有不同的值（可能以后被设置为确定的值）。

在复合类型中，如果类型内的accessor名互不相同，但是可能与其他类型中的accessor名相同，即，accessor名加上类型名形成一个唯一的标志符，这个名叫作"局部范围名"。如果名是直接或间接的基于URI的一部分，那么不管它出现在什么类型中，这个名本身就可以唯一标志这个accessor，这样的名叫作"全局范围名"。

给定了schema中相关的值的序列化信息，就可能确定某些值只与某个accessor的一个实例有关。其它情况下则无法确定。当且仅当一个accessor引用一个值，这个值才能被视为"single-reference"，如果有不止一个accessor引用它，那么就将它视为"multi-reference"。注意，可能一个确定的值在一个schema中是"single-reference"，而在另一个schema中是"multi-reference"。

在语句构成上，一个元素可能是"independent" 或 "embedded"。一个独立的元素指出现在序列化最顶层的任何元素。所有其它元素都是嵌入元素。

虽然用xsi:type属性可以使值的结构和类型变为自描述的，但是序列化规则允许值的类型仅仅参照schema而定。这样的schema可能使用"XML Schema Part 1: Structures" [10]和"XML Schema Part 2: Datatypes" [11]中描述的符号系统，也可能使用其它符号系统。注意，虽然序列化规则可以用于除了数组和结构之外的复合类型，但是许多schema仅仅包含数组和结构类型。

序列化规则如下:

所有的值以元素内容的形式表示。一个multi-reference值必须表示为一个独立元素的内容，而一个single-reference值最好不要这样表示（也可以这样表示）。

对于每个具有值的元素，值的类型时必须用下述三种方式之一描述：(a)所属元素实例有xsi:type属性 (b)所属元素是一个有SOAP-ENC:arrayType 属性（该属性可能是缺省的）的元素的子元素，或者(c)所属元素的名具有特定的类型，类型可以由schema确定。

一个简单值表示为字符数据，即没有任何子元素。每个简单值必须具有一个类型，这个类型或者是XML Schemas Specification, part 2 [11]有的类型，或者具有源类型（参见5.2节）。

一个复合值编码成一个元素的序列，每个accessor用一个嵌入元素表示，该元素的元素名和accessor的名一致。如果accessor的名是局部于其所属的类型的，则该元素的元素名不是合格的，否则对应的元素名是合格的。(参见5.4节)

一个multi-reference的简单值或复合值编码成一个独立的元素，这个元素包含一个局部的无需校验的属性，属性名为"id"，类型为"ID"（依照XML Specification [7]）。值的每个accessor对应一个空元素，该元素有一个局部的，无需校验的属性，属性名为"href"，类型为" uri-reference "（依照XML Schema Specification [11]），"href"属性的值引用了相对应的独立元素的URI标志符。

字符串和字符数组表示为multi-reference的简单类型，但是特殊的规则使它们在普通的情况下能被更有效的表示（参见5.2.1节和5.2.3节）。字符串和字符数组值的accessor可能有一个名字为"id"，类型为"ID"（依照XML Specification [7]）的属性。如果这样，所有这个值的所有其它accessor编码成一个空元素，这个元素有一个局部的，无需校验的属性，属性名为"href"，类型为" uri-reference "（依照XML Schema Specification [11]），"href"属性的值引用了包含这个值的元素的URI标志符。

编码时允许一个值有多个引用，就像多个不同的值有多个引用一样，但这仅在从上下文可以知道这个XML文档实例的含义没有改变时才可使用。

数组是复合值（参见5.4.2节）。SOAP数组定义为具有类型"SOAP-ENC:Array"或从它衍生的类型.

SOAP数组可以时一维或多维，它们的成员以序数位置相互区分。一个数组值表示为反映这个数组的一系列元素，数组成员按升序出现。对多维数组来说，右边的这一维变化最快。每个成员元素命名为一个独立元素。（见规则2）

SOAP数组可以是single-reference 或multi-reference值，因此可以表示为嵌入元素或独立元素的内容。

SOAP数组必须包含一个"SOAP-ENC:arrayType"属性，它的值指定了包含元素的类型和数组的维数。"SOAP-ENC:arrayType"属性的值定义如下：

  arrayTypeValue = atype asize

  atype          = QName *( rank )

  rank           = "[" *( "," ) "]"

  asize          = "[" #length "]"

  length         = 1*DIGIT

"atype"结构是被包含元素的类型名，它表示为QName并且作为类型限制在XML元素声明的"type"属性中出现（这意味着被包含元素的所有值都要与该类型一致，即在SOAP-ENC:arrayType中引用的类型必须是每个数组成员的类型或超类型）。在arrays of arrays or "jagged arrays"的情况下，类型组件编码为"innermost"类型且在从第一层开始的嵌套数组的每一层中，类型名后都跟随一个rank结构。多维数组编码时从第一维起，每一维之间用逗号隔开。

"asize"结构包含一个以逗号分隔的列表，数值0，1或其它整数表示数组每一维的长度。整数0表示没有指定详细的大小，但是可能在检查数组实际成员的大小后确定。

例如，一个5个成员的整型数组的arrayTypeValue值为"int[][5]"，它的atype值是int[]"，asize值是"[5]"。同样，一个3个成员的两维整型数组的arrayTypeValue值为"int[,][3]"，它的atype值是int[,]"，asize值是"[3]"。

一个SOAP数组成员可能包含一个"SOAP-ENC:offset"属性表示这一项在整个数组中的位置偏移值。这被用来指示一个部分储值数组（见5.4.2.1节）的位置偏移值。同样，一个数组成员可能包含一个"SOAP-ENC:position"属性表示这一项在整个数组中的位置，这被用来描述稀疏数组（见5.4.2.2节）的成员。"SOAP-ENC:offset" 和"SOAP-ENC:position"属性值的定义如下：

arrayPoint = "[" #length "]"

偏移值和位置从0开始

NULL值或缺省值可能通过省略accssor元素来表示。NULL值也可能通过一个包含值为'1'的xsi:null属性的accssor元素来表示，其它的依赖于应用程序的属性和值也可能用来表示NULL值。

注意，规则2允许独立的元素和数组成员名不同于值类型的元素。

5.2 简单类型

SOAP采用了"XML Schema Part 2: Datatypes"规范[11]"Built-in datatypes"节中的所有类型作为简单类型，包括值和取值范围。例如：

类型  举例

int 58502

float 314159265358979E+1

negativeInteger  -32768

string Louis "Satchmo" Armstrong

在XML Schema规范中声明的数据类型可以直接用在元素schema中，也可以使用从这些类型衍生的新类型。一个schema和对应的具有这些类型的元素的数据实例的例子如下所示：

<element name="age" type="int"/>

<element name="height" type="float"/>

<element name="displacement" type="negativeInteger"/>

<element name="color">

  <simpleType base="xsd:string">

    <enumeration value="Green"/>

    <enumeration value="Blue"/>

  </simpleType>

</element>

<age>45</age>

<height>5.9</height>

<displacement>-450</displacement>

<color>Blue</color>

所有简单值必须编码为元素的内容，它的类型或者在"XML Schema Part 2: Datatypes"规范[11]中定义过，或者是基于一个用XML Schema规范提供的机制能推衍生出的类型。

如果一个简单值编码为独立元素或异质数组成员，那么有一个对应于数据类型的元素声明将会很方便。因为"XML Schema Part 2: Datatypes"规范[11]包括了类型定义，但是不包括对应的元素声明，SOAP-ENC schema和名域为每个简单数据类型声明了一个元素，如

<SOAP-ENC:int id="int1">45</SOAP-ENC:int>

5.2.1 字符串

字符串数据类型的定义在"XML Schema Part 2: Datatypes"规范[11]中。注意，这不同于许多数据库和程序语言中的"string"类型，特别的，字符串数据类型可能禁止某些在那些语言中允许的字符。（这些值必须用xsd:string之外的数据类型表示）

一个字符串可能编码为一个single-reference 或 multi-reference值。

包含字符串值的元素可能有一个"id"属性。附加的accessor元素可能有对应的"href"属性。

例如，同一字符串的两个accessor可能以如下形式出现：

<greeting id="String-0">Hello</greeting>

<salutation href="#String-0"/>

但是，如果两个accessor参考同一字符串实例（或字符串的子类型），这不是一个实质问题，它们可以编码为两个single-reference值，如下所示：

<greeting>Hello</greeting>

<salutation>Hello</salutation>

这个例子的schema片断如下所示：

<element name="greeting" type="SOAP-ENC:string"/>

<element name="salutation" type="SOAP-ENC:string"/>

在这个例子中，SOAP-ENC:string类型用作元素的类型，这是声明数据类型是"xsd:string"且允许"id" 和"href"属性的元素的简便方法。精确定义参见SOAP编码schema。Schemas可以使用这些源自SOAP编码schema的声明，但也可以不这样做。

5.2.2 Enumerations

"XML Schema Part 2: Datatypes"规范 [11] 定义了"enumeration."机制。SOAP数据模型直接采用了这种机制。但是，由于程序语言和其它语言在定义枚举时通常有些不同，所以我们在这里详细阐述了它的概念并描述了一个列表成员的可能取的值是如何编码的。

"Enumeration"作为一个概念表示不同的名字的集合。一个特定的枚举就是对应于特定的基类型的不同的值的列表。例如，颜色集合("Green", "Blue", "Brown")可以定义为基于字符串类型的枚举，("1", "3", "5")可能是一个基于整型数的枚举，等等。"XML Schema Part 2: Datatypes" [11]支持除了布尔型以外所有简单类型的枚举。"XML Schema Part 1: Structures"规范[10]的语言可以用来定义枚举类型。如果schema由另一个没有特定基类型适用的符号系统生成，就使用"string"。在下面schema的例子中，"EyeColor"定义为字符串，可能的值是"Green", "Blue", 或"Brown"的枚举，数据实例按照schema显示如下。

<element name="EyeColor" type="tns:EyeColor"/>

<simpleType name="EyeColor" base="xsd:string">

   <enumeration value="Green"/>

   <enumeration value="Blue"/>

   <enumeration value="Brown"/>

</simpleType>

<Person>

   <Name>Henry Ford</Name>

   <Age>32</Age>

   <EyeColor>Brown</EyeColor>

</Person>

5.2.3 字符数组

一个字符数组可能编码为single-reference 或multi-reference值。字符数组的编码规则与字符串的编码规则类似。

特别的，包含字符数组的元素值可能由一个"id"属性，附加的accssor元素可能有相应的"href"属性。

推荐使用定义在XML Schemas [10][11]中的'base64'编码（使用在2045 [13]中定义的base64编码算法）表示模糊字符数组。不过，由于行长度（line length）的限制，通常在MIME中应用base64编码，SOAP中一般不应用base64编码。但是提供了"SOAP-ENC:base64"子类型使之能用于SOAP。

<picture xsi:type="SOAP-ENC:base64">

   aG93IG5vDyBicm73biBjb3cNCg==

</picture>

5.3 多态accessor

许多语言允许能够多态访问多种类型值的accessor，每种类型在运行时可用。一个多态accessor实例必须包含一个"xsi:type"属性描述实际值的类型。

例如，一个名为"cost"类型值为"xsd:float"的多态accessor编码如下：

<cost xsi:type="xsd:float">29.95</cost>

与之对比，类型值不变的accessor编码如下：

<cost>29.95</cost>

5.4 Compound types复合类型

SOAP定义了与下列常在程序语言中出现的结构性模式对应的类型：

结构

一个"struct"是一个复合值，它的成员值的唯一区别是accessor名称，任意两个accessor名称都不相同。

数组

一个"array"是一个复合值，它的成员值的唯一区别是序数位置。

SOAP也允许结构和数组之外的其它数据的序列化，例如Directed-Labeled-Graph Data Model之类的数据中，单个节点有许多不同的accssor，有些不止出现一次。SOAP序列化规则不要求底层的数据模型在accssor之间区分次序，但如果有这样的次序的话，这些accssor必须按照这个顺序编码。

5.4.1 复合值，结构和值引用

复合值的成员编码为accessor元素。当accessor由名区分时（如结构），accessor名即作为元素名。名局部于类型的accessor有不受限的名，其它的accessor则有受限的名。

下面的例子是类型为"Book"的结构：

<e:Book>

   <author>Henry Ford</author>

   <preface>Prefatory text</preface>

   <intro>This is a book.</intro>

</e:Book>

以下是描述上面结构的schema片断：

<element name="Book">

<complexType>

  <element name="author" type="xsd:string"/>

  <element name="preface" type="xsd:string"/>

   <element name="intro" type="xsd:string"/>

</complexType>

</e:Book>


soap规范（3）

作者：bdsc

email: bdscca@yahoo.ca

日期：2001-7-13 17:06:07

以下是一个同时具有简单和复杂成员类型的例子。它显示两层引用。注意"Author"accssor元素的"href"属性是对相应具有"id"属性的值的引用。"Address"与之类似。

<e:Book>

   <title>My Life and Work</title>

   <author href="#Person-1"/>

</e:Book>

<e:Person id="Person-1">

   <name>Henry Ford</name>

   <address href="#Address-2"/>

</e:Person>

<e:Address id="Address-2">

   <email>mailto:henryford@hotmail.com</email>

   <web>http://www.henryford.com</web>

</e:Address>

当"Person"的值和"Address"的值是multi-reference时，上面的形式是正确的。如果它们是single-reference，就必须用嵌入的形式，如下所示：

<e:Book>

   <title>My Life and Work</title>

   <author>

       <name>Henry Ford</name>

       <address>

      <email>mailto:henryford@hotmail.com</email>

      <web>http://www.henryford.com</web>

       </address>

   </author>

</e:Book>

如果添加一个限制，任意两个人都不会有相同的地址，并且地址可以是街道或Email地址，一本书可以有两个作者，编码如下：

<e:Book>

   <title>My Life and Work</title>

   <firstauthor href="#Person-1"/>

   <secondauthor href="#Person-2"/>

</e:Book>

<e:Person id="Person-1">

   <name>Henry Ford</name>

   <address xsi:type="m:Electronic-address">

       <email>mailto:henryford@hotmail.com</email>

       <web>http://www.henryford.com</web>

   </address>

</e:Person>

<e:Person id="Person-2">

   <name>Samuel Crowther</name>

   <address xsi:type="n:Street-address">

       <street>Martin Luther King Rd</street>

       <city>Raleigh</city>

       <state>North Carolina</state>

   </address>

</e:Person>

序列化可以包含对不在同一个资源的值的引用：

<e:Book>

   <title>Paradise Lost</title>

   <firstauthor href="http://www.dartmouth.edu/~milton/"/>

</e:Book>

以下是描述上面结构的schema片断：

<element name="Book" type="tns:Book"/>

<complexType name="Book">

   <!-- Either the following group must occur or else the

        href attribute must appear, but not both. -->

   <sequence minOccurs="0" maxOccurs="1">

       <element name="title" type="xsd:string"/>

       <element name="firstauthor" type="tns:Person"/>

       <element name="secondauthor" type="tns:Person"/>

   </sequence>

   <attribute name="href" type="uriReference"/>

   <attribute name="id" type="ID"/>

   <anyAttribute namespace="##other"/>

</complexType>

<element name="Person" base="tns:Person"/>

<complexType name="Person">

   <!-- Either the following group must occur or else the

        href attribute must appear, but not both. -->

   <sequence minOccurs="0" maxOccurs="1">

       <element name="name" type="xsd:string"/>

       <element name="address" type="tns:Address"/>

   </sequence>

   <attribute name="href" type="uriReference"/>

   <attribute name="id" type="ID"/>

   <anyAttribute namespace="##other"/>

</complexType>

<element name="Address" base="tns:Address"/>

<complexType name="Address">

   <!-- Either the following group must occur or else the

        href attribute must appear, but not both. -->

   <sequence minOccurs="0" maxOccurs="1">

       <element name="street" type="xsd:string"/>

       <element name="city" type="xsd:string"/>

       <element name="state" type="xsd:string"/>

   </sequence>

   <attribute name="href" type="uriReference"/>

   <attribute name="id" type="ID"/>

   <anyAttribute namespace="##other"/>

</complexType>


soap规范（4）

作者：bdsc

email: bdscca@yahoo.ca

日期：2001-7-13 17:06:42

5.4.2 数组

SOAP数组定义为具有"SOAP-ENC:Array"类型或一个从"SOAP-ENC:Array"衍生的类型（参见规则8）。数组表示为元素值，对元素的名没有特别的约束（正如元素值并不约束它们所属的元素）。

数组可以包含任意类型的元素，包括嵌套数组。可以创建新的类型（受SOAP-ENC:Array类型限制）来表示数组，如整数数组或某些用户定义的枚举。

数组值表示为组成这个数组的项的元素的规则序列。在数组值中，元素名对于区分accesor并不重要。元素可以有任意的名。实际上，元素常常用它们在schema中暗示或确定的数组类型来命名元素。并且一般情况下对于复合值来说，如果数组中数组项的值是single-reference值，则这个数组项包含它的值，否则，该数组项通过"href"属性引用这个值。

下面的例子是一个整型数组的schema片断：

<element name="myFavoriteNumbers"

        type="SOAP-ENC:Array"/>

<myFavoriteNumbers

  SOAP-ENC:arrayType="xsd:int[2]">

   <number>3</number>

   <number>4</number>

</myFavoriteNumbers>

在这个例子中，数组"myFavoriteNumbers"包括几个成员，每个成员是一个类型为SOAP-ENC:int的值。注意SOAP-ENC:Array允许不受限制的元素名，它们不传达任何类型信息，所以在使用时，或者它们有xsi:type属性，或者它们所属的元素有SOAP-ENC:arrayType属性。自然，由SOAP-ENC:Array衍生的类型可以声明局部元素，但这种情况下要包括类型信息。

上面已经提到，SOAP-ENC schema包含了元素的声明，元素名与"XML Schema Part 2: Datatypes"规范[11]中的简单类型一致。其中包括了对"Array"的声明。于是，我们可以这样写：

<SOAP-ENC:Array SOAP-ENC:arrayType="xsd:int[2]">

   <SOAP-ENC:int>3</SOAP-ENC:int>

   <SOAP-ENC:int>4</SOAP-ENC:int>

</SOAP-ENC:Array>

数组可以包含特定arrayType的任意子类型的实例。即，数组成员可以是arryType属性值指定的类型的任意子类型，这个类型对于arrayType属性中指定的类型来说是可替换的（根据schema中的替换规则）。例如，一个整型数组可以包含从整型衍生的任意类型（如"int"或任意用户定义的从整型衍生的类型）。同样，一个"address"数组可能包含一个address的受限类型或扩展类型如"internationalAddress"。因为提供的SOAP-ENC:Array类型允许任意类型的成员，所以可以包含任意类型的混合除非使用arrayType属性加以特别的限制。

在实例中，可以使用xsi:type指定成员元素的类型，或通过schema中成员元素的声明来指定。下面是两个例子。

<SOAP-ENC:Array SOAP-ENC:arrayType="SOAP-ENC:ur-type[4]">

   <thing xsi:type="xsd:int">12345</thing>

   <thing xsi:type="xsd:decimal">6.789</thing>

   <thing xsi:type="xsd:string">

      Of Mans First Disobedience, and the Fruit

      Of that Forbidden Tree, whose mortal tast

      Brought Death into the World, and all our woe,

   </thing>

   <thing xsi:type="xsd:uriReference">

      http://www.dartmouth.edu/~milton/reading_room/

   </thing>

</SOAP-ENC:Array>

<SOAP-ENC:Array SOAP-ENC:arrayType="SOAP-ENC:ur-type[4]">

   <SOAP-ENC:int>12345</SOAP-ENC:int>

   <SOAP-ENC:decimal>6.789</SOAP-ENC:decimal>

   <xsd:string>

      Of Mans First Disobedience, and the Fruit

      Of that Forbidden Tree, whose mortal tast

      Brought Death into the World, and all our woe,

   </xsd:string>

   <SOAP-ENC:uriReference>

      http://www.dartmouth.edu/~milton/reading_room/

   </SOAP-ENC:uriReference >

</SOAP-ENC:Array>

数组值可以是结构或其它复合值。例如"xyz:Order"结构数组：

<SOAP-ENC:Array SOAP-ENC:arrayType="xyz:Order[2]">

   <Order>

       <Product>Apple</Product>

       <Price>1.56</Price>

   </Order>

   <Order>

       <Product>Peach</Product>

       <Price>1.48</Price>

   </Order>

</SOAP-ENC:Array>

数组成员值也可以是数组。下例是两个字符串数组组成的数组：

<SOAP-ENC:Array SOAP-ENC:arrayType="xsd:string[][2]">

   <item href="#array-1"/>

   <item href="#array-2"/>

</SOAP-ENC:Array>

<SOAP-ENC:Array id="array-1" SOAP-ENC:arrayType="xsd:string[2]">

   <item>r1c1</item>

   <item>r1c2</item>

   <item>r1c3</item>

</SOAP-ENC:Array>

<SOAP-ENC:Array id="array-2" SOAP-ENC:arrayType="xsd:string[2]">

   <item>r2c1</item>

   <item>r2c2</item>

</SOAP-ENC:Array>

包含数组的元素无需命名为"SOAP-ENC:Array"。它可以有任意的名，只要元素的类型是SOAP-ENC:Array或由之衍生的类型。例如，下面是一个schema片断和与之一致的数组实例。

<simpleType name="phoneNumber" base="string"/>

<element name="ArrayOfPhoneNumbers">

  <complexType base="SOAP-ENC:Array">

    <element name="phoneNumber" type="tns:phoneNumber" maxOccurs="unbounded"/>

  </complexType>

  <anyAttribute/>

</element>

<xyz:ArrayOfPhoneNumbers SOAP-ENC:arrayType="xyz:phoneNumber[2]">

   <phoneNumber>206-555-1212</phoneNumber>

   <phoneNumber>1-888-123-4567</phoneNumber>

</xyz:ArrayOfPhoneNumbers>

数组可能是多维的。在这种情况下，在arrayType属性的asize部分将不止有一个值：

<SOAP-ENC:Array SOAP-ENC:arrayType="xsd:string[2,3]">

   <item>r1c1</item>

   <item>r1c2</item>

   <item>r1c3</item>

   <item>r2c1</item>

   <item>r2c2</item>

   <item>r2c3</item>

</SOAP-ENC:Array>

虽然上面的例子把数组编码为独立的元素，但元素值也可以是嵌入形式，而且若元素值是single reference时，必须编码为嵌入形式。


soap规范（5）

作者：bdsc

email: bdscca@yahoo.ca

日期：2001-7-13 17:07:24

下例是一个schema片断，电话号码数组嵌入到一个类型为"Person"的结构中，并且通过accessor "phone-numbers"访问它：

<simpleType name="phoneNumber" base="string"/>

<element name="ArrayOfPhoneNumbers">

  <complexType base="SOAP-ENC:Array">

    <element name="phoneNumber" type="tns:phoneNumber" maxOccurs="unbounded"/>

  </complexType>

  <anyAttribute/>

</element>

<element name="Person">

  <complexType>

    <element name="name" type="string"/>

    <element name="phoneNumbers" type="tns:ArrayOfPhoneNumbers"/>

  </complexType>

</element>

<xyz:Person>

   <name>John Hancock</name>

   <phoneNumbers SOAP-ENC:arrayType="xyz:phoneNumber[2]">

       <phoneNumber>206-555-1212</phoneNumber>

       <phoneNumber>1-888-123-4567</phoneNumber>

   </phoneNumbers>

</xyz:Person>

下面的例子中，数组值为single-reference，被编码为嵌入元素，包含它的元素名即为入口名：

<xyz:PurchaseOrder>

   <CustomerName>Henry Ford</CustomerName>

   <ShipTo>

       <Street>5th Ave</Street>

       <City>New York</City>

       <State>NY</State>

       <Zip>10010</Zip>

   </ShipTo>

   <PurchaseLineItems SOAP-ENC:arrayType="Order[2]">

       <Order>

           <Product>Apple</Product>

           <Price>1.56</Price>

       </Order>

       <Order>

           <Product>Peach</Product>

           <Price>1.48</Price>

       </Order>

   </PurchaseLineItems>

</xyz:PurchaseOrder>

5.4.2.1 部分储值（partially transmitted）数组

SOAP提供了对部分储值（partially transmitted）数组的支持，如某些上下文中的可变数组。一个partially transmitted 数组由一个"SOAP-ENC:offset"属性（从第一个transmitted的元素开始的偏移量，基于0）指示。如果省略，偏移量取0。

下面的例子中数组的大小为5，但只有从0起，第三和第四个元素被储值。

<SOAP-ENC:Array ;SOAP-ENC:arrayType="xsd:string[5]" ;SOAP-ENC:offset="[2]">

  <item>The third element</item>

  <item>The fourth element</item>

</SOAP-ENC:Array>

5.4.2.2 稀疏数组Sparse Arrays

SOAP提供了对稀疏数组的支持。每个表示成员值的元素包含一个"SOAP-ENC:position"属性，用来指示它在数组中的位置。下例是两维字符串稀疏数组的例子，数组大小是4，但只用到第2个。

<SOAP-ENC:Array SOAP-ENC:arrayType="xsd:string[,][4]">

   <SOAP-ENC:Array href="#array-1" SOAP-ENC:position="[2]"/>

</SOAP-ENC:Array>

<SOAP-ENC:Array id="array-1" SOAP-ENC:arrayType="xsd:string[10,10]">

   <item SOAP-ENC:position="[2,2]">Third row, third col</item>

   <item SOAP-ENC:position="[7,2]">Eighth row, third col</item>

</SOAP-ENC:Array>

如果对array-1的引用仅发生在数组内部，上例也可以编码如下：

<SOAP-ENC:Array SOAP-ENC:arrayType="xsd:string[,][4]">

  <SOAP-ENC:Array SOAP-ENC:position="[2]" SOAP-ENC:arrayType="xsd:string[10,10]>

    <item SOAP-ENC:position="[2,2]">Third row, third col</item>

    <item SOAP-ENC:position="[7,2]">Eighth row, third col</item>

  </SOAP-ENC:Array>

</SOAP-ENC:Array>

5.4.3 一般复合类型

在这里提到的编码规则不仅仅限于accessor名已知的情况，如果accessor名是运行环境下实时获得的，编码规则同样适用，也就是说accessor编码成一个元素名与accessor名匹配的元素，同时accessor可能包含或者引用该元素的值。如果accessor包含类型不能事先确定的值，它必须包含一个合适的属性xsi:type 。

类似地，上述引用的规则已经足够用于复合类型的序列化，这些复合类型可能包含用名区分的accessors（结构）和用名及序数位置区分的accessors。（可能包含重复的accessor）实际上这并不要求任何schema模式包含这些类型，但更为准确的说法是：一个类型模型（type-model）schema如果有这些类型，就可以构造一个符合XML句法规则的schema和XML文档实例。

<xyz:PurchaseOrder>

   <CustomerName>Henry Ford</CustomerName>

   <ShipTo>

       <Street>5th Ave</Street>

       <City>New York</City>

       <State>NY</State>

       <Zip>10010</Zip>

   </ShipTo>

   <PurchaseLineItems>

       <Order>

           <Product>Apple</Product>

           <Price>1.56</Price>

       </Order>

       <Order>

           <Product>Peach</Product>

           <Price>1.48</Price>

       </Order>

   </PurchaseLineItems>

</xyz:PurchaseOrder>

类似地，将一个结构上类似数组但实际上不是一个 SOAP-ENC:Array类型或者 SOAP-ENC:Array子类型的复合值序列化同样是允许的，例如：

<PurchaseLineItems>

    <Order>

        <Product>Apple</Product>

        <Price>1.56</Price>

    </Order>

    <Order>

        <Product>Peach</Product>

        <Price>1.48</Price>

    </Order>

</PurchaseLineItems>

5.5 缺省值

省略accessor元素意味着或者有一个缺省值或者值不知道。具体细节依靠这个accessor，方法和上下文。例如，对于多态accessor，省略accessor一般意味着一个Null值。同样，省略布尔accessor一般意味着False值或者值不知道，省略数字accessor一般意味着值为零或者值不知道。

5.6 SOAP root属性

SOAP root 属性可用于标记一个序列化root，从而一个对象可以反序列化（deserialized），而实际上该root并不是真正的对象root。这个属性有两个可选值"1" or "0"。对象真正的roots属性值为“1” ，序列化root但不是真正的root属性值也为“1”，元素如果要显式地指定不能为序列化root，只需将该属性设置为“0” SOAP root属性可以出现在SOAP头和SOAP体元素的任意子元素中。（译者注：SOAP root属性为0的元素不是一个独立的实体，外部的应用不能访问到该元素，但该元素可以被SOAP文档本身的其它元素访问到）

SOAP root属性可以出现在SOAP头和SOAP体元素的任意子元素中。这个属性没有缺省值。


soap规范（完）

作者：bdsc

email: bdscca@yahoo.ca

日期：2001-7-13 17:08:10

6. 在HTTP中使用SOAP

这一节讲述了如何在HTTP中使用SOAP。把SOAP绑定到HTTP，无论使用或不用HTTP扩展框架，都有很大的好处：在利用SOAP的形式化和灵活性的同时，使用HTTP种种丰富的特性。在HTTP中携带SOAP消息，并不意味着SOAP改写了HTTP已有的语义，而是将构建在HTTP之上SOAP语义自然地对应到HTTP语义。

SOAP自然地遵循HTTP的请求/应答消息模型使得SOAP的请求和应答参数可以包含在HTTP请求和应答中。注意，SOAP的中间节点与HTTP的中间节点并不等同，即，不要期望一个根据HTTP连接头中的域寻址到的HTTP中间节点能够检查或处理HTTP请求中的SOAP消息。

在HTTP消息中包含SOAP实体时，按照RFC2376[3] HTTP应用程序必须使用媒体类型 "text/xml"。

6.1 SOAP HTTP请求

虽然SOAP可能与各种HTTP请求方式相结合，但是绑定仅定义了在HTTP POST请求中包含SOAP消息。（第7节中描述了如何在RPC中使用SOAP，第6.3节描述了如何使用HTTP扩展框架）

6.1.1 HTTP头中SOAPAction域

一个HTTP请求头中的SOAPAction域用来指出这是一个SOAP HTTP请求，它的值是所要的URI。在格式、URI的特性和可解析性上没有任何限制。当HTTP客户发出SOAP HTTP请求时必须使用在HTTP头中使用这个域。

soapaction    = "SOAPAction" ":" [ <"> URI-reference <"> ]

URI-reference = <as defined in RFC 2396 [4]>

HTTP头中SOAPAction域使服务器（如防火墙）能正确的过滤HTTP中SOAP请求消息。如果这个域的值是空字符串（""），表示SOAP消息的目标就是HTTP请求的URI。这个域没有值表示没有SOAP消息的目标的信息。

例子:

SOAPAction: "http://electrocommerce.org/abc#MyMessage"

SOAPAction: "myapp.sdl"

SOAPAction: ""

SOAPAction:

6.2 SOAP HTTP应答

SOAP HTTP遵循HTTP 中表示通信状态信息的HTTP状态码的语义。例如，2xx状态码表示这个包含了SOAP组件的客户请求已经被成功的收到，理解和接受。

在处理请求时如果发生错误，SOAP HTTP服务器必须发出应答HTTP 500 "Internal Server Error"，并在这个应答中包含一个SOAP Fault元素（见4.4节）表示这个SOAP处理错误。

6.3 HTTP扩展框架

一个SOAP消息可以与HTTP扩展框架 [6]一起使用以区分是否有SOAP HTTP请求和它的目标。

是使用扩展框架或是普通的HTTP关系到通信各方的策略和能力。通过使用一个必需的扩展声明和"M-"HTTP方法名前缀，客户可以强制使用HTTP扩展框架。服务器可以使用HTTP状态码510 "Not Extended"强制使用HTTP扩展框架。也就是说，使用一个额外的来回，任何一方都可以发现另一方的策略并依照执行。

用来表示SOAP使用了扩展框架的扩展标志符是：

http://schemas.xmlsoap.org/soap/envelope/

6.4 SOAP HTTP举例

例3 使用POST的SOAP HTTP

POST /StockQuote HTTP/1.1

Content-Type: text/xml; charset="utf-8"

Content-Length: nnnn

SOAPAction: "http://electrocommerce.org/abc#MyMessage"

<SOAP-ENV:Envelope...

HTTP/1.1 200 OK

Content-Type: text/xml; charset="utf-8"

Content-Length: nnnn

<SOAP-ENV:Envelope...

例4 使用扩展框架的SOAP HTTP

M-POST /StockQuote HTTP/1.1

Man: "http://schemas.xmlsoap.org/soap/envelope/";ns=NNNN

Content-Type: text/xml; charset="utf-8"

Content-Length: nnnn

NNNN-SOAPAction: "http://electrocommerce.org/abc#MyMessage"

<SOAP-ENV:Envelope...

HTTP/1.1 200 OK

Ext:

Content-Type: text/xml; charset="utf-8"

Content-Length: nnnn

<SOAP-ENV:Envelope...

7. 在RPC中使用SOAP

设计SOAP的目的之一就是利用XML的扩展性和灵活性来封装和交换RPC调用。这一节定义了远程过程调用和应答的统一表示形式。

虽然可以预计到这种表示形式最可能被用于与第5节中定义的编码方式相结合，但也可能有其它的表示形式。SOAP的encodingStyle属性（见4.3.2节）可以用来表明方法调用和应答都使用这一节所指定的表示方式。

在RPC中使用SOAP和SOAP协议绑定（见第6节）是紧密相关的。在使用HTTP作为绑定协议时，一个RPC调用自然地映射到一个HTTP请求，RPC应答同样映射到HTTP应答。但是，在RPC中使用SOAP并不限于绑定HTTP协议。

要进行方法调用，以下的信息是必需的：

目标对象的URI

方法名

方法signature（可选）

方法的参数

头数据（可选）

SOAP依靠协议绑定提供传送URI的机制。例如，对HTTP来说，请求的URI指出了调用的来源。除了必须是一个合法的URI之外，SOAP对一个地址的格式没有任何限制。（更多URI的信息参见 [4]）

7.1 RPC和SOAP体

RPC方法调用和应答都包含在SOAP Body元素中（见4.3节），它们使用如下的表示形式：

一个方法调用用一个结构表示

一个方法调用被看作一个单个的结构，每个[in]和[in/out]参数有一个accessor。结构的名和类型与方法相同。

每个[in]和[in/out]参数都被看作一个accessor，这个accessor的名和类型与参数的名和类型相对应。它们的出现顺序和方法中定义的参数顺序相同。

一个方法应答用一个结构表示。

一个方法应答被看作一个单个的结构，返回值和每个[in]和[in/out]参数有一个accessor。第一个accessor是返回值，之后是参数accessor，参数accessor的出现顺序和方法中定义的参数顺序相同。

每个参数accessor的名称和类型与参数的名称和类型相对应。返回值accessor的名称并不重要。同样，结构的名称也不重要，不过，通常在方法名称的后面加上字符串"Response"作为结构的名称。

方法错误使用SOAP Fault元素（见4.4节）表示。如果绑定的协议有额外的规则表示错误，则这些规则也必须要遵从。

正如上面所述，方法调用和应答结构可以按照第5节中规则编码，或者用encodingStyle属性（见4.1.1节）指定编码方式。

应用程序可以处理缺少参数的请求，但是可能返回一个错误。

因为返回结果表示调用成功，错误表示调用失败，所以，在方法应答中同时包含返回结果和错误是错误的。

7.2 RPC和SOAP头

在RPC编码中，可能会有与方法请求有关但不是正规的方法signature的附加信息。如果这样，它必须作为SOAP头元素的子元素。

使用这种头元素的一个例子是在消息中传递事务ID。由于事务ID不是方法signature的一部分，通常由底层的组件而不是应用程序代码控制，所以没有一种直接的方法在调用中传递这个必要的信息。通过在头中添加一个给定名字的条目，接收方的事务管理器就可以析取这个事务ID，而且不影响远程过程调用的代码。

8. 安全性考虑

这篇文档中没有涉及完整性和保密性，这些问题将在以后的版本中描述。

9. 参考文献

[1] S. Bradner, "The Internet Standards Process -- Revision 3", RFC2026, Harvard University, October 1996

[2] S. Bradner, "Key words for use in RFCs to Indicate Requirement Levels", RFC 2119, Harvard University, March 1997

[3] E. Whitehead, M. Murata, "XML Media Types", RFC2376, UC Irvine, Fuji Xerox Info. Systems, July 1998

[4] T. Berners-Lee, R. Fielding, L. Masinter, "Uniform Resource Identifiers (URI): Generic Syntax", RFC 2396, MIT/LCS, U.C. Irvine, Xerox Corporation, August 1998.

[5] R. Fielding, J. Gettys, J. C. Mogul, H. Frystyk, T. Berners-Lee, "Hypertext Transfer Protocol -- HTTP/1.1", RFC 2616, U.C. Irvine, DEC W3C/MIT, DEC, W3C/MIT, W3C/MIT, January 1997

[6] H. Nielsen, P. Leach, S. Lawrence, "An HTTP Extension Framework", RFC 2774, Microsoft, Microsoft, Agranat Systems

[7] W3C Recommendation "The XML Specification"

[8] W3C Recommendation "Namespaces in XML"

[9] W3C Working Draft "XML Linking Language". This is work in progress.

[10] W3C Working Draft "XML Schema Part 1: Structures". This is work in progress.

[11] W3C Working Draft "XML Schema Part 2: Datatypes". This is work in progress.

[12] Transfer Syntax NDR, in "DCE 1.1: Remote Procedure Call"

[13] N. Freed, N. Borenstein, "Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies", RFC2045, Innosoft, First Virtual, November 1996

A. SOAP封装举例

A.1 请求编码举例

例5 类似于例1，但有一个必要的头

POST /StockQuote HTTP/1.1

Host: http://www.stockquoteserver.com

Content-Type: text/xml; charset="utf-8"

Content-Length: nnnn

SOAPAction: "Some-URI"

<SOAP-ENV:Envelope

  xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"

  SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>

   <SOAP-ENV:Header>

       <t:Transaction

           xmlns:t="some-URI"

           SOAP-ENV:mustUnderstand="1">

               5

       </t:Transaction>

   </SOAP-ENV:Header>

   <SOAP-ENV:Body>

       <m:GetLastTradePrice xmlns:m="Some-URI">

           <symbol>DEF</symbol>

       </m:GetLastTradePrice>

   </SOAP-ENV:Body>

</SOAP-ENV:Envelope>

例6 类似于例1，但有多个请求参数

POST /StockQuote HTTP/1.1

Host: http://www.stockquoteserver.com

Content-Type: text/xml; charset="utf-8"

Content-Length: nnnn

SOAPAction: "Some-URI"

<SOAP-ENV:Envelope

  xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"

  SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>

   <SOAP-ENV:Body>

       <m:GetLastTradePriceDetailed

         xmlns:m="Some-URI">

           <Symbol>DEF</Symbol>

           <Company>DEF Corp</Company>

           <Price>34.1</Price>

       </m:GetLastTradePriceDetailed>

   </SOAP-ENV:Body>

</SOAP-ENV:Envelope>

A.2 应答编码举例

例7 与例2类似，但有必要的头部

HTTP/1.1 200 OK

Content-Type: text/xml; charset="utf-8"

Content-Length: nnnn

<SOAP-ENV:Envelope

  xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"

  SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>

   <SOAP-ENV:Header>

       <t:Transaction

         xmlns:t="some-URI"

         xsi:type="xsd:int" mustUnderstand="1">

           5

       </t:Transaction>

   </SOAP-ENV:Header>

   <SOAP-ENV:Body>

       <m:GetLastTradePriceResponse

         xmlns:m="Some-URI">

           <Price>34.5</Price>

       </m:GetLastTradePriceResponse>

   </SOAP-ENV:Body>

</SOAP-ENV:Envelope>

例8 与例2类似，但有一个结构

HTTP/1.1 200 OK

Content-Type: text/xml; charset="utf-8"

Content-Length: nnnn

<SOAP-ENV:Envelope

  xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"

  SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>

   <SOAP-ENV:Body>

       <m:GetLastTradePriceResponse

         xmlns:m="Some-URI">

           <PriceAndVolume>

               <LastTradePrice>

                   34.5

               </LastTradePrice>

               <DayVolume>

                   10000

               </DayVolume>

           </PriceAndVolume>

       </m:GetLastTradePriceResponse>

   </SOAP-ENV:Body>

</SOAP-ENV:Envelope>

例9 与例2类似，但处理必要的头出错

HTTP/1.1 500 Internal Server Error

Content-Type: text/xml; charset="utf-8"

Content-Length: nnnn

<SOAP-ENV:Envelope

  xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">

   <SOAP-ENV:Body>

       <SOAP-ENV:Fault>

           <faultcode>SOAP-ENV:MustUnderstand</faultcode>

           <faultstring>SOAP Must Understand Error</faultstring>

       </SOAP-ENV:Fault>

   </SOAP-ENV:Body>

</SOAP-ENV:Envelope>

例10 与例2类似，但处理Body出错

HTTP/1.1 500 Internal Server Error

Content-Type: text/xml; charset="utf-8"

Content-Length: nnnn

<SOAP-ENV:Envelope

  xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">

   <SOAP-ENV:Body>

       <SOAP-ENV:Fault>

           <faultcode>SOAP-ENV:Server</faultcode>

           <faultstring>Server Error</faultstring>

           <detail>

               <e:myfaultdetails xmlns:e="Some-URI">

                 <message>

                   My application didn't work

                 </message>

                 <errorcode>

                   1001

                 </errorcode>

               </e:myfaultdetails>

           </detail>

       </SOAP-ENV:Fault>

   </SOAP-ENV:Body>

</SOAP-ENV:Envelope>


Sorry,我刚找到的：配置J2EE支持Mysql （转）

作者：大哥

email: programmers@sina.com

日期：2001-7-9 10:07:02

这一段时间在学习J2EE＆EJB，我最感兴趣的还是做JDBC的EJB，但是由于我手头上没有J2EE默认的Cloudscape，也不敢装Oracle，我的机器太次，跑J2EE已是满负荷运转，绝不能再加大家伙！因此我想继续使用小巧玲珑的Mysql。

首先当然是系统环境软件的安装，我就不多说了，在下面我给出了几种软件的下载地址。需要特别强调的是，请一定要注意你安装的Mysql的版本，我们平常用的大都是“mysql-shareware-3.22.30-win”这个版本，但J2EE需要3.23.15以上的Mysql，这个是我花费大半天时间得到的教训。

经我的多次实验，J2EE的配置文件default properties中应按如下设置：

jdbc.drivers=org.gjt.mm.mysql.Driver

jdbc.datasources=jdbc/TestDb|jdbc:mysql://localhost:3306/test?user=jian&password=jian

以上语句很简单，用过Mysql的jdbc driver的人一看就明，所要注意的是其中的“jdbc/TestDb”实际上就是JNDI用来标识该数据库的名字。

在EJB中调用就很简单了:

DataSource ds = (DataSource) ic.lookup("jdbc/TestDb");

其实配置正确后回过头来再看，觉得真是很简单，但正是正确之前的一段摸索历程耗费了我很多的精力，所谓黎明前的黑暗吧。

从我使用的情况来看，这个版本的J2EE的错误提示机制特差，看上去不知所云，而且并不稳定，比如用它的装配工具生成的JAR包，虽然可用，但每次总被系统弹出窗口警告说是“无效的Windows NT映像”，弄得烦不胜烦，还有，明明按我上述配置已经能顺利运行连接Mysql的EJB，但error.log和dos窗口总是会记载说“Cannot find principal mapping information for data source with JNDI name jdbc/TestDb”。不过虽然有警告和错误日志记载，但只要你程序能够顺利地运行，尽可不去理会那么多，毕竟，J2EE还只是一个新生事物，没有经过长时间广范围的评测和使用。

由于本人水平有限，谬误之处烦劳予以纠正。

运行环境

Platform: Winnt 4.0

J2EE: JavaTM 2 SDK, Enterprise Edition Version 1.2.1

JDK: j2sdk1.3.0

Mysql: mysql-3.23.23-beta-win

Driver: mm.mysql-2.0.2


SSL與數位認證

作者：whitefox

email: whitefox.jiang@corp.elong.com

日期：2000-07-20 09:44:02

SSL與數位認證

--------------------------------------------------------------------------------

[ 本文重點 | SSL安全資料傳輸 | SSL運作步驟與設定 | 金鑰(key)產生與管理 ]

[ Microsoft Certificate Server | 用戶端認證(Client Certificate) ]

--------------------------------------------------------------------------------

本文重點

介紹熱門的SSL與數位認證方法，包括SSL安全連線的伺服器認證、SSL運作步驟與設定、金鑰（key）產生與管理，以及IIS 4.0的Microsoft Certificate Server之伺服器認證、用戶端認證等方法。

Windows NT Option Pack中文版軟體所包含的「Microsoft Certificate Server」，可自行建立一個認證中心（Certificate Authority），以管理數位認證的授與、廢除及更新，數位認證用於公開金鑰加解密之應用，譬如SSL的伺服器認證與用戶端認證，讓您透過Intranet或 Internet 安全地傳送資料。

SSL安全資料傳輸

SSL（Secure Sockets Layer）

SSL（Secure Sockets Layer）加密方法，可以對資料傳輸做安全上的保護。

當使用者傳送私有資料（譬如信用卡或銀行帳號）到網站時，可能害怕資料傳送當中被攔截。SSL（Secure Sockets Layer）3.0協定，提供了傳輸安全上的保護，於使用者傳送資料前先做「加密」成亂碼，網站接收資料後再「解密」出原來的資料。資料傳送當中即使被攔截，也只是看到一堆亂碼，看不到原來的資料。

使用SSL安全方法時，使用者產生一把對稱金鑰（session key ），將待傳送的資料「加密」成亂碼，網站接收資料後再使用同一把對稱金鑰「解密」出原來的資料。

為了將使用者產生的這一把加解密用的對稱金鑰安全地傳給伺服器，SSL於使用者傳送這一把對稱金鑰前，先使用伺服器的公開金鑰（public key）將對稱金鑰「加密」成亂碼，網站接收資料後再使用伺服器的私密金鑰（private key）「解密」出原來的對稱金鑰。

本文針對與SSL相關的伺服器認證（Server Certificates）、SSL運作的步驟與設定、金鑰（key）產生與管理，詳細介紹如下：

伺服器認證（Server Certificates）

使用SSL安全方法時，為了確保使用者拿到的伺服器公開金鑰（public key）為正確的，伺服器必須由具公信力的認證中心（Certificate Authority，簡稱CA），核發伺服器認證（Server Certificates）。

一個認證就像一個印鑑證明，證明這個印章屬於您所有，不是別人假造的。

伺服器認證包括提供網站內容者的組織、和認證中心的名稱，以及伺服器的公開金鑰。

認證中心CA

使用SSL安全方法連線時，必須先取得伺服器認證。

伺服器認證是否成功，取決於使用者是否信任認證中心核發的伺服器認證資料是否為正確有效。

譬如一個使用者與您的網站伺服器建立連線時，即使看到您伺服器的認證，可能仍然猶豫是否要將信用卡資料提供給您，尤其是您的公司是新的或沒有甚麼知名度時，會更加地猶豫不決。

基於這個理由，有時認證係由具有公信力的第三者認證中心（Certificate Authority，簡稱CA）所核發。認證中心有責任確認認證的資料是否為正確有效。

有許多的第三者認證中心，提供IIS核發伺服器認證（server certificate）的服務，如下：

Verisign Inc.：http://www.verisign.com

GTE CyberTrust Solutions：http://www.cybertrust.gte.com

Thawte Consulting：http://www.thawte.com

CertiSign Certificado Digital Ltda.：http://www.certisign.com.br

BelSign NV-SA：http://www.belsign.be

Keywitness Canada：http://www.keywitness.ca

BankGate CA：http://www.bankgate.com

可連線到http://www.microsoft.com/security/取得最新提供IIS伺服器認證（server certificate）服務的認證中心CA名單。

另外一個替代方式，取決於您公司與網站伺服器使用者的關係如何，您能夠自己充當認證中心，核發伺服器認證（Server Certificates）給自己。譬如，於大公司的企業內網路當中，處理員工的薪資和福利時，公司管理當局可以決定是否自己維護一個認證伺服器（certificate server）以核發伺服器認證給自己的伺服器。

使用Windows NT Option Pack中的Microsoft Certificate Server，可以建立一個認證伺服器，自己充當認證中心，核發伺服器認證。

SSL運作步驟與設定

SSL運作的步驟

SSL運作的步驟如下：

使用者的瀏覽器使用「https」通訊方式與網站伺服器建立連線。

網站伺服器傳送包括伺服器公開金鑰的伺服器認證（Server Certificates）給使用者。

使用者的瀏覽器產生一把加解密用的對稱金鑰（session key），使用伺服器的公開金鑰（public key）將對稱金鑰「加密」成亂碼，傳送給網站伺服器。

網站伺服器使用伺服器的私密金鑰（private key），以「解密」出瀏覽器產生的對稱金鑰。

網站伺服器與瀏覽器使用這一把對稱金鑰，將資料做加解密以相互傳送資料。

伺服器加入SSL功能

於伺服器加入SSL（Secure Sockets Layer）的功能，可以確保資料傳輸的安全性。要如何於伺服器加入SSL功能呢？有以下數個步驟：

產生一對Key（金鑰）：第一步驟，首先使用 [識別碼管理員]（Key Manager）於NT上建立一對新的Key（金鑰），將產生一個認證申請檔案（certificate request file），預設為NewKeyRq.txt檔。您再將此認證申請檔案傳給認證中心（Certificate Authority，簡稱CA），或使用 [識別碼管理員]（Key Manager）傳送。CA可由具有公信力的第三者扮演，Microsoft Certificate Server也可扮演此CA的角色。CA核準後將核發伺服器認證（server certificate），傳送給您一個認證檔案（certificate file）。

安裝伺服器認證檔案：當您收到CA核發的伺服器認證檔案時，使用 [識別碼管理員]（Key Manager）安裝這個檔案。

於IIS設定安全傳輸port 443和啟動SSL：執行 IIS 4.0的 [Internet 服務管理員] ，選擇要使用SSL的網站，如預設的Web站台，按下滑鼠右鍵，選擇 [內容]，選擇 [目錄安全] ，按下 [安全通訊] 的 [編輯] 按鈕，選擇 [存取這個資源時，必須使用安全通道] 。若如此設定，則整個設定的站台，如上預設的Web站台，都必須使用「https://」的SSL方式。

設定須使用SSL的方法

這個方法設定「整個」的網站、目錄、或檔案須使用SSL的方法將傳送的資料做加解密。

於 [Internet 服務管理員] ，選擇一個網站（Web site）、目錄、或檔案，按下滑鼠右鍵開啟 [內容] 。

選擇 [目錄安全] 或 [檔案安全] 頁，於 [安全通訊] 下按下 [編輯] 按鈕（若未產生一對key，須先按下 [識別碼管理員]（Key Manager）產生之）。

於 [安全通訊] 對話盒當中 ，選擇 [存取這個資源時，必須使用安全通道]。

使用者的瀏覽器使用「https://」通訊方式與網站伺服器建立連線。

設定加解密金鑰的長度

加解密資料所用的對稱金鑰（session key）長度愈長，愈不容易被破解。

您能夠設定網站伺服器需要使用128位元長度的對稱金鑰（session key），而非預設的40位元的長度。若您設定為至少128位元長度，則想要與伺服器建立安全連線的使用者，必須採用能夠產生128位元對稱金鑰的瀏覽器。

基於美國法律的限制，128位元金鑰長度僅僅可以使用於美國和加拿大。

您必須先從認證中心取得伺服器認證（Server Certificates），才可以建立安全的SSL連線。

設定加解密金鑰長度的方法，如下：

於 [Internet 服務管理員] ，選擇一個網站（Web site）、目錄、或檔案，按下滑鼠右鍵開啟 [內容] 。

選擇 [目錄安全] 或 [檔案安全] 頁，於 [安全通訊] 下按下 [編輯] 按鈕（若未產生一對key，須先按下 [識別碼管理員]（Key Manager）產生之）。

於 [安全通訊] 對話盒當中 ，選擇 [存取這個資源時，必須使用安全通道] 後，按下 [加密設定] 按鈕。

於 [加密設定] 對話盒當中 ，選擇 [需要128位元加密] 後按下 [確認] 按鈕。

金鑰（key）產生與管理

識別碼管理員（Key Manager）

SSL所使用的一對金鑰（key），包括公開金鑰（public key）和私密金鑰（private key），可以使用NT的「識別碼管理員」（Key Manager）來產生、匯入（import）、和匯出（export）這一對唯一的金鑰（key），以供申請伺服器認證（Server Certificates）之用。

您也可以使用「識別碼管理員」（Key Manager），來申請和安裝伺服器認證（Server Certificates），以及為遠端的伺服器產生一對金鑰（key）。

一個domain name只能安裝一個伺服器認證（Server Certificates）。

一對金鑰（key）只能使用於一個伺服器認證（Server Certificates）。

一對金鑰（key）只能使用於一個domain name，但是多個IP address可因共用同一個domain name而共用同一對金鑰（key）。

執行「識別碼管理員」（Key Manager）有兩個方法，當您 於 [Internet 服務管理員] 選擇一個網站（Web site）、目錄、或檔案時，方法如下：

按下工具列的 [識別碼管理員]（Key Manager）圖示。

按下滑鼠右鍵開啟 [內容] ，選擇「 [目錄安全] 」或 [檔案安全] 頁，於 [安全通訊] 下按下 [編輯] 按鈕，再按下 [識別碼管理員]（Key Manager）按鈕。

產生金鑰（key）方法

產生一對金鑰（key）的方法如下：

於 [Internet 服務管理員] 按下工具列的 [識別碼管理員]（Key Manager）圖示。

於 [識別碼] 選單選擇 [建立識別碼]，依照指示即可產生一對金鑰（key）。

可以選擇將所產生的一對金鑰（key）儲存成一個檔案以傳送給認證中心，或向Microsoft Certificate Server線上自動申請伺服器認證。

產生一對金鑰（key）時，可向認證中心提出伺服器認證（Server Certificates）的申請。若您接收到認證中心提供的一個與Microsoft Certificate Server相容的application plug-in，即可線上自動申請伺服器認證。

您應該將 [識別碼管理員]（Key Manager）所產生的一對金鑰（key），當做您家裏的金鑰一般地好好保存起來，不要掉了或讓別人拿到。

建議您將金鑰（key）做一份備份到磁碟片，並將此磁碟片放置於安全的地方。當您更新或重新安裝IIS時可能刪除而共用同一對金鑰（key）和伺服器認證（Server Certificates），此時就需要使用這個金鑰（key）備份磁碟片了。

備份金鑰方法如下：

於 [識別碼管理員]（Key Manager）選擇待備份的金鑰。

於 [識別碼] 選單選擇 [匯出識別碼] 和 [備份檔] 等，依照指示即可備份金鑰（key）到一個磁碟片的檔案。

安裝伺服器認證

產生金鑰（key）向認證中心申請伺服器認證（Server Certificates）後，將獲得核發的認證。您可以將所核發的認證，複製儲存到一個檔案，再使用 [識別碼管理員]（Key Manager）安裝認證到網站伺服器。

安裝伺服器認證，方法如下：

將認證中心核發的認證，儲存成一個文字檔案，附檔名為.txt。

於 [Internet 服務管理員] 按下工具列的 [識別碼管理員]（Key Manager）圖示，選擇待安裝認證的金鑰（key）。

於key選單選擇Install Key Certificate，於「Open 」對話盒當中選擇認證的.txt檔案，按下Open ，輸入認證檔案密碼後，按下OK按鈕即可以安裝好伺服器認證。

新增或編輯金鑰（key）的指定IP

同一個domain name的多個IP address可共用同一對金鑰（key）。您也可以指定一對金鑰（key）只能使用於一個IP address。

使用的是新增或編輯金鑰（key）的指定IP address方法，如下：

於 [Internet 服務管理員] 按下工具列的 [識別碼管理員]（Key Manager）圖示，選擇待設定的金鑰（key）。

於 [識別碼] 選單選擇 [內容] ，於 [伺服器連接] 對話盒當中選擇 [新增] 或 [編輯]。

於 [編輯連接] 對話盒輸入IP address（可由...按鈕選擇之）。

若未指定一個IP Address，則同一個domain name當中的多個IP address將都使用此金鑰（key）。

金鑰設定IP address的原則如下：

一個IP address不能設定多個伺服器認證（Server Certificates）。

一個IP address不能設定多個網站。但是一個網站能設定多個IP address。

一個網站不能設定多個SSL ports。

Microsoft Certificate Server

Windows NT Option Pack中Microsoft Certificate Server，讓您的網站成為一個認證中心（Certificate Authority，簡稱CA），可以：

核定伺服器認證（server certificate）：給需求建立SSL安全連線的網站。

核定用戶端認證（Client Certificate）：給每位使用者以讓伺服器自動驗證使用者的身份。

伺服器認證

為了使用SSL確保資料傳輸的安全性，伺服器首先必須從認證中心（Certificate Authority，簡稱CA）獲得一個伺服器認證（server certificate）。

Microsoft Certificate Server，可扮演核發伺服器認證（server certificate）的認證中心（Certificate Authority，簡稱CA）。

要如何使用Windows NT Option Pack中的Microsoft Certificate Server核發伺服器認證，於伺服器加入SSL功能呢？步驟如下：

第一步驟：產生一對Key和提出伺服器認證申請。

第二步驟：於瀏覽器安裝認證中心認證（Certificate Authority Certificate），將Microsoft Certificate Server的認證中心於瀏覽器登記為「可信任」的認證中心，接受此認證中心所核發之所有認證。

第三步驟：重新啟動IIS。

產生一對Key和提出伺服器認證申請

第一步驟，首先於NT上建立一個新的金鑰（Key），方法為執行Windows NT Option Pack中的 [Internet 服務管理員] ，於 [識別碼管理員]（Key Manager）按下滑鼠左鍵。

滑鼠按下「WWW」，於 [識別碼] 選單選擇 [建立識別碼]，選擇「自動將您的要求傳送到線上授權單位」為「Microsoft Certificate Server」，即線上向Microsoft Certificate Server提出伺服器認證申請。

輸入金鑰（key）名稱、密碼、和長度。

輸入申請公司名稱（Organization）、部門（Organizational Unit）、伺服器URL網址（Common Name）。

輸入國別（Country）、省份（State/Province）、城市（City/Locality） ，最後按下「完成」按鈕，即可以產生一個新的金鑰（Key），並將此一對新Key傳給認證中心Microsoft Certificate Server。

即可產生即可產生一對金鑰（key）。

出現 [伺服器連接的畫面] ，按下 [新增] 按鈕，選擇「未指定IP Address」和「未指定PORT」，最後按下 [確認] 按鈕。

請確認所產生的新金鑰（Key）位於「WWW」下。

選擇 [電腦] 選單的 [立即確認變更] ，並確認所做的變更。

產生新的金鑰（Key）和提出伺服器認證申請後，將從Microsoft Certificate Server取得伺服器認證（server certificate）並自動安裝之。

於瀏覽器登記「可信任」的認證中心

伺服器取得並安裝伺服器認證後，第二步驟於瀏覽器安裝認證中心認證（Certificate Authority Certificate），將Microsoft Certificate Server的認證中心於瀏覽器登記為「可信任」的認證中心，接受此認證中心所核發之所有認證。

安裝認證中心認證方法，為使用者使用IE瀏覽器，輸入Microsoft Certificate Server的網頁http://localhost/CertSrv，其中localhost為Microsoft Certificate Server安裝的URL位址。選擇Certificate Enrollment Tools的Install Certificate Authority Certificates以安裝認證中心認證。

於瀏覽器安裝認證中心認證，將認證中心於瀏覽器登記為「可信任」的認證中心後，可於IE瀏覽器「檢視」選單的「Interrnet選項」的「內容」頁中的「機構」按鈕當中，查到已經安裝的可信任認證中心資料。

重新啟動IIS

第三步驟，重新啟動IIS，於DOS下的WINNT\system32\inetsrv下執行命令「iisca」，並執行命令「net stop iisadmin /y」以停止World Wide Web Publishing Service，再執行命令「net start w3svc」以開啟World Wide Web Publishing Service（或重新開機），如下：

C:\WINNT\system32\inetsrv> iisca

List of valid Certifiying Authorities ( CA ) successfuly transferred to IIS

C:\WINNT\system32\inetsrv> net stop iisadmin /y

下面的服務關係到 IIS Admin Service 服務。

停止 IIS Admin Service 服務也會停止這些服務。

World Wide Web Publishing Service

World Wide Web Publishing Service 服務正在停止..

World Wide Web Publishing Service 服務已經成功停止。

.....

IIS Admin Service 服務已經成功停止。

C:\WINNT\system32\inetsrv> net start w3svc

World Wide Web Publishing Service 服務正在啟動 ............

World Wide Web Publishing Service 服務已經啟動成功。

如此即可使用SSL了。

檢查已經安裝的認證中心資料

若要檢查已經安裝的認證中心資料，可使用IE瀏覽器來查看。

當IE瀏覽器使用SSL的「https://」，安裝認證中心所核發之認證後，可於IE瀏覽器「檢視」選單的「Internet選項」的「內容」頁中的「機構」按鈕當中，查到已經安裝的認證中心資料。

用戶端認證（Client Certificate）

用戶端認證（Client Certificate），讓網站伺服器自動辨識使用者的身份，以讓使用者簽入一個受限制管制的網站，不需要輸入「密碼」即可自動簽入。

用戶端認證（Client Certificate）包括使用者個人資料、和核發的認證中心、有效期間、序號、以及建立安全連線的金鑰檔案等資訊加密而成。

申請和安裝用戶端認證（Client Certificate）

每個使用者，可以使用Microsoft Certificate Server，來申請和安裝用戶端認證（Client Certificate）。

申請和安裝用戶端認證的方法如下：

首先使用瀏覽器瀏覽Microsoft Certificate Server的用戶端認證申請網頁：

http://localhost/CertSrv/CertEnroll/ceenroll.asp

其中localhost為Microsoft Certificate Server安裝的URL位址。

或瀏覽Microsoft Certificate Server的網頁http://localhost/CertSrv，選擇Certificate Enrollment Tools的Request a Client Authentication Certificate。

輸入申請用戶端認證時所須的個人詳細資料。

填好後，按下Submit Request按鈕以提出用戶端認證（Client Certificate）的申請，將顯示下載Microsoft Certificate Server所核發的用戶端認證。

按下Download按鈕將下載Microsoft Certificate Server所核發的用戶端認證，並自動安裝完成。

安裝認證中心所核發之用戶端認證後，可於IE瀏覽器「檢視」選單的「Interrnet選項」的「內容」頁中的「個人」按鈕當中，查到已經安裝的用戶端認證資料。按下「個人」按鈕，顯示已經安裝的用戶端認證資料，可看到申請用戶端認證時所填的個人詳細資料。


Stream Tokenizing（分解字符串）

作者：blackwhites

日期：2000-10-23 16:59:32

从sun网站看到的Stream Tokenizing

In Tech Tips: June 23, 1998, an example of string tokenization was presented, using the class java.util.StringTokenizer.

There's also another way to do tokenization, using java.io.StreamTokenizer. StreamTokenizer operates on input streams rather than strings, and each byte in the input stream is regarded as a character in the range '\u0000' through '\u00FF'.

StreamTokenizer is lower level than StringTokenizer, but offers more control over the tokenization process. The class uses an internal table to control how tokens are parsed, and this syntax table can be modified to change the parsing rules. Here's an example of how StreamTokenizer works:

import java.io.*;

import java.util.*;

public class streamtoken {

  public static void main(String args[])

  {

    if (args.length == 0) {

      System.err.println("missing input filename");

      System.exit(1);

    }

    Hashtable wordlist = new Hashtable();

    try {

      FileReader fr = new FileReader(args[0]);

      BufferedReader br = new BufferedReader(fr);

      StreamTokenizer st = new StreamTokenizer(br);

      //StreamTokenizer st =

      //    new StreamTokenizer(new StringReader(

      //    "this is a test"));

      st.resetSyntax();

      st.wordChars('A', 'Z');

      st.wordChars('a', 'z');

      int type;

      Object dummy = new Object();

      while ((type = st.nextToken()) !=

        StreamTokenizer.TT_EOF) {

          if (type == StreamTokenizer.TT_WORD)

            wordlist.put(st.sval, dummy);

        }

        br.close();

      }

      catch (IOException e) {

        System.err.println(e);

      }

      Enumeration enum = wordlist.keys();

      while (enum.hasMoreElements())

        System.out.println(enum.nextElement());

   }

}

In this example, a StreamTokenizer is created on top of a FileReader / BufferedReader pair that represents a text file. Note that a StreamTokenizer can also be made to read from a String by using StringReader as illustrated in the commented-out code shown above (StringBufferInputStream also works, although this class has been deprecated).

The method resetSyntax is used to clear the internal syntax table, so that StreamTokenizer forgets any rules that it knows about parsing tokens. Then wordChars is used to declare that only upper and lower case letters should be considered to form words. That is, the only tokens that StreamTokenizer recognizes are sequences of upper and lower case letters.

nextToken is called repeatedly to retrieve words, and each resulting word is found in the public instance variable "st.sval". The words are inserted into a Hashtable, and at the end of processing the contents of the table are displayed, using an Enumeration as illustrated in Tech Tips: June 23, 1998. So the action of this program is to find all the unique words in a text file and display them.

StreamTokenizer also has special facilities for parsing numbers, quoted strings, and comments. It's a useful alternative to StringTokenizer, and is especially applicable if you are tokenizing input streams, or wish to exercise finer control over the tokenization process


Sun Java Web服务器远程可执行命令漏洞

作者：飞天蜘蛛

日期：2000-07-18 08:31:50

Sun Java Web服务器远程可执行命令漏洞

漏洞发布时间：2000-7-13 17:41:00

漏 洞 描 述:

在Solaris and Windows NT的Sun Java Web Server默认安装设置下。通过公告版的样板程序的漏洞，可以远程执行任意命令。

漏洞测试方法如下：

下面的例子将显示如何上载和执行该代码，而在服务器上显示"Hello World"。

输入下面的JSP代码将打印出"Hello World":

<% String s="Hello World"; %>

<%=s %>

通过下面的公告版连接Post这段代码:

http://jws.site/examples/applications/bboard/bboard_frames.html

检验是否该代码真正的上载了:

http://jws.site/board.html

通过执行下面的连接编译和执行该代码:

http://jws.site/servlet/com.sun.server.http.pagecompile.jsp.runtime.JspServlet/board.html

解 决 方 法:

仔细按照Java Web Server手册中的"How to secure a web site that uses the Java Web Server" 一章进行设置，或者参考下面连接:

http://www.sun.com/software/jwebserver/faq/jwsca-2000-02.html

或者简单的移除默认安装的这些样板文件。


Threads

作者：jeru

email: jeru@163.net

日期：7/3/2001 10:03:12 AM

7) Threads

Objective 1)

Write code to define, instantiate and start new threads using both java.lang.Thread and java.lang.Runnable

·    Java is fundamentally multi-threaded.

·    Every thread corresponds to an instance of java.lang.Thread class or a sub-class.

·    A thread becomes eligible to run, when its start() method is called. Thread scheduler co-ordinates between the threads and allows them to run.

·    When a thread begins execution, the scheduler calls its run method.

Signature of run method – public void run()

·    When a thread returns from its run method (or stop method is called – deprecated in 1.2), its dead. It cannot be restarted, but its methods can be called. (it’s just an object no more in a running state)

·    If start is called again on a dead thread, IllegalThreadStateException is thrown.

·    When a thread is in running state, it may move out of that state for various reasons. When it becomes eligible for execution again, thread scheduler allows it to run.

·    There are two ways to implement threads.

1.    Extend Thread class

public class Thread extends Object implements Runnable

    Thread()

    Thread(Runnable target)

    Thread(Runnable targe, String name)

    Thread(String name)

    Thread(ThreadGroup group, Runnable target)

    Thread(ThreadGroup group, Runnable target, String name)

    Thread(ThreadGroup group, String name)

·    Create a new class, extending the Thread class.

·    Provide a public void run method, otherwise empty run in Thread class will be executed.

·    Create an instance of the new class.

·    Call start method on the instance (don’t call run – it will be executed on the same thread)

2.    Implement Runnable interface

public interface Runnable

    only one method:   public void run

·    Create a new class implementing the Runnable interface.

·    Provide a public void run method.

·    Create an instance of this class.

·    Create a Thread, passing the instance as a target – new Thread(object)

·    Target should implement Runnable, Thread class implements it, so it can be a target itself.

·    Call the start method on the Thread.

·    JVM creates one user thread for running a program. This thread is called main thread. The main method of the class is called from the main thread. It dies when the main method ends. If other user threads have been spawned from the main thread, program keeps running even if main thread dies. Basically a program runs until all the user threads (non-daemon threads) are dead.

·    A thread can be designated as a daemon thread by calling setDaemon(boolean) method. This method should be called before the thread is started, otherwise IllegalThreadStateException will be thrown.

·    A thread spawned by a daemon thread is a daemon thread.

·    Threads have priorities. Thread class have constants MAX_PRIORITY (10), MIN_PRIORITY (1), NORM_PRIORITY (5)

·    A newly created thread gets its priority from the creating thread. Normally it’ll be NORM_PRIORITY.

·    getPriority and setPriority are the methods to deal with priority of threads.

·    Java leaves the implementation of thread scheduling to JVM developers. Two types of scheduling can be done.

1.    Pre-emptive Scheduling.

Ways for a thread to leave running state -

·    It can cease to be ready to execute ( by calling a blocking i/o method)

·    It can get pre-empted by a high-priority thread, which becomes ready to execute.

·    It can explicitly call a thread-scheduling method such as wait or suspend.

·    Solaris JVM’s are pre-emptive.

·    Windows JVM’s were pre-emptive until Java 1.0.2

2.    Time-sliced or Round Robin Scheduling

·    A thread is only allowed to execute for a certain amount of time. After that, it has to contend for the CPU (virtual CPU, JVM) time with other threads.

·    This prevents a high-priority thread mono-policing the CPU.

·    The drawback with this scheduling is – it creates a non-deterministic system – at any point in time, you cannot tell which thread is running and how long it may continue to run.

·    Mactinosh JVM’s

·    Windows JVM’s after Java 1.0.2

Of the two methods of creating a new thread the use of Runnable is probably more common. The other method for creating a thread is to create a class that is descended from Thread. This is easy to do but it means you cannot inherit from any other class,

Objective 2)

Recognize conditions that might prevent a thread from executing.

·    Different states of a thread:

1.    Yielding

·    Yield is a public static void method. Operates on current thread.  For static method, call Thread.yield() is ok, don’t need call t.yield(), where t is instance of Thread, but compiler will pass it.

·    Moves the thread from running to ready state.

·    If there are no threads in ready state, the yielded thread may continue execution, otherwise it may have to compete with the other threads to run.

·    Run the threads that are doing time-consuming operations with a low priority and call yield periodically from those threads to avoid those threads locking up the CPU.

2.    Sleeping

·    Sleep is also a public static void method.

·    Sleeps for a certain amount of time. (passing time without doing anything and w/o using CPU)

·    Two overloaded versions – one with milliseconds, one with milliseconds and nanoseconds.

·    Throws an InterruptedException.(must be caught)

·    After the time expires, the sleeping thread goes to ready state. It may not execute immediately after the time expires. If there are other threads in ready state, it may have to compete with those threads to run. The correct statement is the sleeping thread would execute some time after the specified time period has elapsed.

·    If interrupt method is invoked on a sleeping thread, the thread moves to ready state. The next time it begins running, it executes the InterruptedException handler.

3.    Suspending

·    Suspend and resume are instance methods and are deprecated in 1.2

·    A thread that receives a suspend call, goes to suspended state and stays there until it receives a resume call on it.

·    A thread can suspend it itself, or another thread can suspend it.

·    But, a thread can be resumed only by another thread.

·    Calling resume on a thread that is not suspended has no effect.

·    Compiler won’t warn you if suspend and resume are successive statements, although the thread may not be able to be restarted.

4.    Blocking

·    Methods that are performing I/O have to wait for some occurrence in the outside world to happen before they can proceed. This behavior is blocking.

·    If a method needs to wait an indeterminable amount of time until some I/O takes place, then the thread should graciously step out of the CPU. All Java I/O methods behave this way.

·    A thread can also become blocked, if it failed to acquire the lock of a monitor.

5.    Waiting

·    wait, notify and notifyAll methods are not called on Thread, they’re called on Object. Because the object is the one which controls the threads in this case. It asks the threads to wait and then notifies when its state changes. It’s called a monitor.

·    Wait puts an executing thread into waiting state.(to the monitor’s waiting pool)

·    Notify moves one thread in the monitor’s waiting pool to ready state. We cannot control which thread is being notified. notifyAll is recommended.

·    NotifyAll moves all threads in the monitor’s waiting pool to ready.

·    These methods can only be called from synchronized code, or an IllegalMonitorStateException will be thrown. In other words, only the threads that obtained the object’s lock can call these methods.

The sleep method is static and pauses execution for a set number of milliseconds. There is a version that is supposed to pause for a set number of nanoseconds,  Here is an example of putting a Thread to sleep, note how the sleep method throws InterruptedException.

public class TSleep extends Thread{

public static void main(String argv[]){

       TSleep t = new TSleep();

       t.start();

       }

      public void run(){

          try{

             while(true){

                  this.sleep(1000);

                  System.out.println("looping while");

                 }

            }catch(InterruptedException ie){}

       }

}

Objective 3)

Write code using synchronized wait notify and notifyAll to protect against concurrent access problems and to communicate between threads. Define the interaction between threads and between threads and object locks when executing synchronized wait notify or notifyAll.

Locks, Monitors and Synchronization

·    Every object has a lock (for every synchronized code block). At any moment, this lock is controlled by at most one thread.

·    A thread that wants to execute an object’s synchronized code must acquire the lock of the object. If it cannot acquire the lock, the thread goes into blocked state and comes to ready only when the object’s lock is available.

·    When a thread, which owns a lock, finishes executing the synchronized code, it gives up the lock.

·    Monitor is an object that can block and revive threads, an object that controls client threads. Asks the client threads to wait and notifies them when the time is right to continue, based on its state. In strict Java terminology, any object that has some synchronized code is a monitor.

·    2 ways to synchronize:

1.    Synchronize the entire method

·    Declare the method to be synchronized - very common practice.

·    Thread should obtain the object’s lock.

2.    Synchronize part of the method

·    Have to pass an arbitrary object which lock is to be obtained to execute the synchronized code block (part of a method).

Synchronized(target) {statements}

·    We can specify “this” in place object, to obtain very brief locking – not very common

·    If target is null, then the NullPointerException is thrown.

·    wait – points to remember

§    calling thread gives up CPU

§    calling thread gives up the lock

§    calling thread goes to monitor’s waiting pool

§    wait also has a version with timeout in milliseconds. Use this if you’re not sure when the current thread will get notified, this avoids the thread being stuck in wait state forever.

·    notify – points to remember

§    one thread gets moved out of monitor’s waiting pool to ready state

§    notifyAll moves all the threads to ready state

§    Thread gets to execute must re-acquire the lock of the monitor before it can proceed.

·    Note the differences between blocked and waiting.

Blocked    Waiting

Thread is waiting to get a lock on the monitor.(or waiting for a blocking i/o method)    Thread has been asked to wait. (by means of wait method)

Caused by the thread tried to execute some synchronized code. (or a blocking i/o method)    The thread already acquired the lock and executed some synchronized code before coming across a wait call.

Can move to ready only when the lock is available. ( or the i/o operation is complete)    Can move to ready only when it gets notified (by means of notify or notifyAll)

·    Points for complex models:

1.    Always check monitor’s state in a while loop, rather than in an if statement.

2.    Always call notifyAll, instead of notify.

·    wait and sleep must be enclosed in a try/catch for InterruptedException.

·    A single thread can obtain multiple locks on multiple objects (or on the same object)

·    A thread owning the lock of an object can call other synchronous methods on the same object. (this is another lock) Other threads can’t do that. They should wait to get the lock.

·    Non-synchronous methods can be called at any time by any thread.

·    Synchronous methods are re-entrant. So they can be called recursively.

·    Synchronized methods can be overrided to be non-synchronous. synchronized behavior affects only the original class.

·    Locks on inner/outer objects are independent. Getting a lock on outer object doesn’t mean getting the lock on an inner object as well, that lock should be obtained separately.

·    Locks on static synchronized method called Class wide lock.  The class wide lock and the instance lock being independent of each other.

·    wait and notify should be called from synchronized code. This ensures that while calling these methods the thread always has the lock on the object. If you have wait/notify in non-synchronized code compiler won’t catch this. At runtime, if the thread doesn’t have the lock while calling these methods, an IllegalMonitorStateException is thrown.

·    Deadlocks can occur easily. e.g, Thread A locked Object A and waiting to get a lock on Object B, but Thread B locked Object B and waiting to get a lock on Object A. They’ll be in this state forever.

·    It’s the programmer’s responsibility to avoid the deadlock. Always get the locks in the same order.

·    While ‘suspended’, the thread keeps the locks it obtained – so suspend is deprecated in 1.2

·    Use of stop is also deprecated, instead use a flag in run method. Compiler won’t warn you, if you have statements after a call to stop, even though they are not reachable.

A typical example of using the wait/notify protocol to allow communication between Threads appears to involve apparently endless loops such as

//producing code

while(true){

try{

        wait();

        }catch (InterruptedException e) {}

}

//some producing action goes here

notifyAll();

Conditions that might prevent a thread from executing :

   x  The thread is not the highest priority thread and so cannot get CPU time.

   x  The thread is waiting on a condition because someone invoked wait() for the thread.

   x  The thread has explicitly yielded control by invoking yield() to allow another thread of the same priority to run.

   x  The thread has been put to sleep using the sleep() method

   x  Someone has suspended the thread using the suspend() method. (deprecated in Java 2)

   x  It is blocked for file I/O

   x  There is more than one thread with the same highest priority and JVM is switching between these threads, at the moment, the thread in question is awaiting CPU time.


Timer Task 的简单范例(相当触发器,转自Sun)

作者：jeru

email: jeru@163.net

日期：8/5/2001 5:22:16 PM

Scheduling a Timer Task to Run at a Certain Time

==============================================

 Date timeToRun =

   new Date(System.currentTimeMillis()+

   numberOfMillisecondsInTheFuture);

 Timer timer = new Timer();

 timer.schedule(new TimerTask() {

         public void run() {

             // Task here

         }

     }, timeToRun);

============================================

Scheduling a Timer Task to Run Repeatedly

=============================================

 // delay for 5 sec.

 int delay = 5000;

 // repeat every sec.

 int period = 1000;

 Timer timer = new Timer();

 timer.scheduleAtFixedRate(new TimerTask() {

         public void run() {

             // Task here

         }

 }, delay, period);


Tomcat IIS HowTo将Tomcat装入IIS全攻略

作者：重粒子

日期：2000-7-21 14:05:03

http://baryon.giveme.net


TOMCAT+IIS配置方法

作者：allsky

email: zhangyan_@163.com

日期：2001-4-24 14:44:14

一.说明：

配置如下：Win2000 Server + IIS 5.0（Win2000自带）+ Tomcat 3.1 binary release.

我使用的Tomcat 3.1是编译过的版本，在这个版本里面没有配置IIS和NT Service

的说明，但是在Tocmcat 3.2 source版里面有比较详细的帮助文件。根据这些帮助文件 可以顺利的把Tomcat作为插件插入IIS。

二.启动tomcat

安装完tomcat之后（具体安装过程略），要启动Tomcat很简单，首先可以查看tomcat\bin\startup.bat文件，利用控制面板里面的系统－>高级->环境变量对话框，新建环境变量TOMCAT_HOME和JAVA_HOME，使它们各自指向TOMCAT和JDK的根目录即可。另外为保险起见，可以在path里面把二者的路径添加进去。然后开启一个DOS窗口执行startup.bat。 启动Tomcat后，会有一个DOS窗口显示启动情况。此时访问http://localhost:8080

三.配置

下一步工作就是如何把Tomcat插入IIS,使二者协同工作。主要分以下几个步骤：

1、在Windows注册表中注册：

将下面的begin和end之间的Jakarta.reg文件装入机器，我的Tomcat装在c:\\jakarta-tomcat，你可根据自己的环境修改Jakarta.reg文件

Jakarta.reg:

---begin---

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Apache Software Foundation\Jakarta Isapi Redirector\1.0]

"extension_uri"="/jakarta/isapi_redirect.dll"

"log_file"="c:\\jakarta-tomcat\\logs\\isapi.log"

"log_level"="debug"

"worker_file"="c:\\jakarta-tomcat\\conf\\workers.properties"

"worker_mount_file"="c:\\jakarta-tomcat\\conf\\uriworkermap.properties"

---end---

注：其中的路径需改成你的tomcat的安装路径。

2、打开IIS管理器，在"默认WEB站点”中建立一个虚拟目录jakarta，让它的路径指向isapi_redirect.dll所在目录。（在3.1 bin中可能没有这个dll，你可以从3.2的bin\iis\nt4\i386目录中拷贝一个过来。该虚拟目录应被设置为"可执行”。

3、利用IIS管理器，把isapi_redirect.dll设置为"默认WEB站点”的ISAPI筛选器，名称可以任起。

4、重启IIS，确认刚才添加的ISAPI筛选器前面标了一个绿色向上的箭头然后重启Tomcat（执行tomcat\bin\shutdown.bat可关闭Tomcat） 此时，IIS应该已经可以处理*.jsp文件了。由于Tomcat设置中，默认的Root是 tomcat\webapps\ROOT，所以你可以在这个目录中放置一个jsp文件（例如 index.jsp），然后访问http://localhost/index.jsp如果按照上面的方法还是无法使IIS处理jsp文件，可以尝试在IIS管理器－>默认WEB站点属性－>主目录－>配置中添加一个"应用程序映射”，把*.jsp文件映射到isapi_redirect.dll上。

四.作为NT的服务

作为NT service的设置比较简单。

首先从3.2拷贝过来jk_nt_service.exe，然后把下面的内容保存起来，起名

为wrapper.properties

#

# $Header: /home/cvs/jakarta-tomcat/src/etc/wrapper.properties,v 1.1

#2000/05/03 11:54:49 shachor Exp $

# $Revision: 1.1 $

# $Date: 2000/05/03 11:54:49 $

#

#

# jk_service.properties - a bootstrup file for the Tomcat NT service.

#

# This file provides jk_nt_service with the needed information to

# start tomcat at a different process.

#

# As a general note, the characters $( and ) are used internally to define

# macros. Do not use them!!!

#

# Whenever you see a set of lines such as:

# x=value

# y=$(x)\something

#

# the final value for y will be value\something

#

# Normaly all you will need to modify is the first two properties, i.e.

# wrapper.tomcat_home and wrapper.java_home. Most of the configuration

# is derived from these two.

#

#

# wrapper.tomcat_home should point to the location where you

# installed tomcat. This is where you have your conf, webapps and lib

# directories.

#

wrapper.tomcat_home=d:\tomcat

#

# wrapper.java_home should point to your Java installation. Normally

# you should have a bin and lib directories beneath it.

#

wrapper.java_home=d:\JBuilder35\jdk1.2.2

#

#------ ADVANCED MODE ------------------------------------------------

# Make sure that you read the how-to before making too many changes.

#---------------------------------------------------------------------

#

#

# Defining where the service is going to put the standard

# output of Tomcat. This is where System.out.println and

# System.err.println goes to.

#

wrapper.stdout=$(wrapper.tomcat_home)\jvm.stdout

wrapper.stderr=$(wrapper.tomcat_home)\jvm.stderr

#

# Additions to the path. put here directories where you store DLLs for

# native methods etc.

#

wrapper.ld_path=d:\

wrapper.ld_path=c:\

#

# Defining the classpath. All the rows that belongs to the class_path

# property are concatenated to create the classpath for Tomcat.

#

# If you have additional locations that you would like to add to the

# claspath you should add a new wrapper.class_path=<location> line.

#

wrapper.class_path=$(wrapper.tomcat_home)\classes

wrapper.class_path=$(wrapper.tomcat_home)\lib\xml.jar

wrapper.class_path=$(wrapper.tomcat_home)\lib\webserver.jar

wrapper.class_path=$(wrapper.tomcat_home)\lib\servlet.jar

wrapper.class_path=$(wrapper.tomcat_home)\lib\jasper.jar

#

# This is where Javac is located in JDK1.2.x

#

wrapper.class_path=$(wrapper.java_home)\lib\tools.jar

#

# and a tribute to JDK1.1.x

#

wrapper.class_path=$(wrapper.java_home)\lib\classes.zip

#

# This is the Java interpreter used for running Tomcat

#

wrapper.javabin=$(wrapper.java_home)\bin\java.exe

#

# This is Tomcat's startup class (the class that contains Tomcat's

# starting point.

#

wrapper.startup_class=org.apache.tomcat.startup.Tomcat

#

# This is the location where tomcat's server.xml configuration file

# is located.

#

wrapper.server_xml=$(wrapper.tomcat_home)\conf\server.xml

#

# The NT service uses AJP12 to shutdown Tomcat. The wrapper.shutdown_port

# tells the service the identity of the port that is used by AJP12.

#

wrapper.shutdown_port=8007

#

# This is the command line that is used to start Tomcat. You can *add* extra

# parameters to it but you can not remove anything.

#

wrapper.cmd_line=$(wrapper.javabin) -classpath $(wrapper.class_path) $(wrapp

er.startup_class) -config $(wrapper.server_xml) -home $(wrapper.tomcat_home)

保存后，只需修改里面的wrapper.tomcat_home和wrapper.java_home使它们各自指向tomcat根目录和JDK根目录即可。 然后运行jk_nt_service -I <服务名称> <wrapper.properties的路径> 服务名称可以随便起，例如tomcat或者jakarta等等都行。wrapper.properties的路径应为完整路径（包括wrapper.properties这个文件名）示例：jk_nt_service -I Jakarta d:\wrapper.properties 这样在控制面板－>服务里面，我们可以看到一个名为Jakarta的服务，可以 在这里启动或者中止它


TomCat和Jakarta计划

作者：jspfuns

日期：2000-8-10 17:01:01

Tomcat很可能成为下一代Java Web Server的主流。 Tomcat受到Sun公司的全力支持，并由非常强大的开发组织apache来进行发展，这一工程被称为Jakarta计划。从战略上看，Sun 现在正借助apache的影响来开发server端的java技术，这就是tomcat。因此可以相信tomcat已经或者即将是一个较理想的jsp & servlet开发和支撑平台。相对地，JSWDK只是一个简化的服务器平台，性能和稳定程度都比较有限，而且实际上SUN并不许可将它作为internet上的商业平台。

tomcat的功能比jws或jswdk强大得多，您可以访问tomcat的站点 http://jakarta.apache.org查看详细介绍，或者订一个tomcat的maillist，还可以加入到他们的开发组织中去。

tomcat有不同的工具来共同实现强大的功能。

tomcat的jakarta工程有一些相当不错的相关子项目：

ant: 基于java的跨平台开发工具，支持XML.

Regexp: 100%纯Java表达与调试工具包，可是debug和测试java兼容性。

Slide:是一个包含了Servlet API内容管理，用以推进WebDAV协议(web-based Distributed Authoring and Versioning)，从而使servlet可以在任何支持api 2.2或以上版本的容器内运行。

struts: 用于制作jsp/servlet的web通用应用开发框架，即MVC(Model-View-Controller).

taglibs: 是一个与JSP1.1标准兼容的自定义标记的库。作为一个强有力的jsp结构特性，这将为jsp开发注入更多功能，开发也变得更友好。

watchdog:包含在tomcat3.1版中，用于检测servlet和jsp的兼容性。

tomcat在NT下的安装比较简单，您也可以参考INSTALL文件的指导.

其中包括：

1 安装 JDK1.2.2/1.3；

2 将文件拷贝到硬盘；

3 设置JAVA_HOME指向JDK安装目录；在classpath中加入%JAVA_HOME%/bin；设置TOMCAT_HOME指向tomcat的安装目录。

4 从tomcat/bin中执行startup.bat进行测试


Tomcat中文手册(1)_转

作者：luoxh

email: luoxh@371.net

日期：8/23/2001 9:21:02 AM

Jakarta-Tomcat 简明中文版用户指南第一部分

本文档提供关于Tomcat的基础信息.主要内容如下:

Tomcat二进制版本安装

与Tomcat相关的脚本的主要内容

与server.xml相关的主要内容,Tomcat的主要配置文件

如何设置Tomcat与宿主web服务器一起工作的说明

如何应用Tomcat配置一个现实中的web站点

希望此文档足以使新用户可以开始使用Tomcat.如找不到某方面的内容请(按以下顺序)

查找Tomcat faq

查找Tomcat包清单

向Tomcat用户邮件列表发出问题

如此疑问的答案不存在,我们鼓励把对疑问的解答放入Tomcat faq或此文档.如对此文档有意见或建议,请发送到Tomcat的邮件列表.

Getting Started

Tomcat是一个带有jsp环境的servlet容器.servlet容器是一个根据用户的行为可以管理和激活servlet的运行时的shell.

粗略地可以将servlet容器分为如下几类:

独立的servlet容器

内置有web服务器的一部分.指当使用基于Java的web服务器的情形,例如servlet容器是JavaWebServer的一个部分. 独立的servlet容器是Tomcat的默认模式.

大多数的web服务器并非基于Java,因此,我们可以得出如下两种容器的模式.

进程内的servlet容器

servlet容器作为web服务器的插件和Java容器的实现.Web服务器插件在内部地址空间打开一个JVM(java virtual machine)使Java容器得以在内部运行.如有某个需要调用servlet的请求,,插件将取得对此请求的控制并将他传递(使用JNI)给Java容器.进程内容器对于多线程,单进程的服务器非常合适并且提供很好的运行速度,但伸缩性有所不足.

进程外的servlet容器

servlet容器运行于web服务器之外的地址空间且作为web服务器的插件和Java容器的实现的结合.web服务器插件和Java容器 JVM使用IPC机制(通常是TCP/IP)进行通讯.当一个调用servlet的请求到达时,插件将取得对此请求的控制并将其传递(使用IPC等)给Java容器,进程外容器的反应时间或进程外容器引擎不如进程内容器,但进程外容器引擎在许多其他可比的范围内更好(伸缩性,稳定性等).

Tomcat既可作为独立的容器(主要是用于开发与调试)又可作为对现有服务器的附加(当前支持Apache,IIS和Netscape服务器).即任何时候配置Tomcat你都必须决定如何应用他,如选择第二或第三种模式,你还需要安装一个web服务器接口.

Tomcat与Jserv有何区别?Tomcat是Jserv吗?

这是个常见的误解.Jserv是Servlet API2.0兼容并与Apache一起使用的容器.Tomcat是一个完全重写的并与Servlet API2.2和JSP1.1兼容的容器.

Tomcat使用了一些为Jserv而写的代码,特别是Jserv的Apache接口,但这是唯一的相同之处.

怎样安装Tomcat的二进制版本?

非常简单,只需:

下载 zip/tar.gz 任何压缩文件,从http://jakarta.apche.org/download/binindex.html

解压缩此文件到某目录(如:foo).将会生成一子目录,名为”tomcat”.

转换到”tomcat”目录设置一新的环境变量(TOMCAT_HOME)指向你安装的tomcat的目录

WIN32平台,键入:

“set TOMCAT_HOME=foo\tomcat”

Unix平台:

如是bash/sh环境, 键入:”TOMCAT_HOME=foo/tomcat;export TOMCAT_HOME”

如是tcsh环境, 键入:”setenv TOMCAT_HOME foo/tomcat”

设置环境变量JAVA_HOME指向你JDK的目录,然后添加JAVA解释器到你的PATH环境变量.

好了!现在可以运行TOMCAT并作为一个独立的Servlet容器(模式一)

启动与关闭Tomcat

使用”bin”目录中的脚本启动与关闭Tomcat.

启动:

uinx:bin/startup.sh

win32:bin\startup

关闭:

unix:bin/shutdown.sh

win32:bin\shutdown

Tomcat目录结构

假设你已将Tomcat解压,你已得到下列目录结构:

目录名--描述

bin

包含启动/关闭脚本

conf

包含不同的配置文件,

包括 server.xml(Tomcat的主要配置文件)和为不同的Tomcat配置的web应用设置缺省值的文件web.xml

doc

包含各种Tomcat文档

lib

包含Tomcat使用的jar文件.unix平台此目录下的任何文件都被加到Tomcat的classpath中

logs

Tomcat摆放日志文件的地方

src

ServletAPI源文件.先别高兴,这些只有些必须在Servlet容器内实现的空接口和抽象类

webapps

包含web项目示例

此外你可以Tomcat会创建如下目录:

work

Tomcat自动生成,放置Tomcat运行时的临时文件(如编译后的JSP文件).如在Tomcat运行时删除此目录.JSP页面将不能运行.

classes

你可以创建此目录来添加一些附加的类到类路径中.任何你加到此目录中的类都可在Tomcat的类路径中找到自身.

Tomcat的脚本

Tomcat是一个Java程序,因此在设置好几个环境变量后即可用命令行的方式运行.然而设置Tomcat使用的每个环境变量和如下的命令行参数乏味且易错.因此,Tomcat开发组提供了一些脚本使启动和关闭Tomcat变得轻松.

注意:这些脚本仅仅是个便利的方法去启动和关闭Tomcat.你可修改他们来定制CLASSPATH,环境变量如PATH,LD_LIBRARY_PATH,等等,只要是生成一个正确的命令行即可.

这些脚本是什么呢?下表列出对一般用户最重要的脚本.

tomcat

主脚本.设置合适的环境变量,包括CLASSPATH,TOMCAT_HOME和JAVA_HOME和用适合的命令行参数启动Tomcat

startup

在后台启动Tomcat.”tomcat start”命令的替换方式

shutdown

关闭Tomcat.”tomcat stop”命令的替换方式

对用户最重要的脚本是tomcat(tomcat.bat/tomcat.sh).其他Tomcat相关的脚本作为一个简单的面向单任务的指向Tomcat脚本(设置不同的命令行参数等)的入口.

仔细考察tomcat.bat/tomcat.sh,它按以下步骤运行:

在Unix下的步骤：

如未指定,推测 TOMCAT_HOME

如未指定,推测 JAVA_HOME

设置CLASS_PATH包含:

1.${TOMCAT_HOME}/classes目录(如果存在)

2.${TOMCAT_HOME}/lib的一切内容

3.${JAVA_HOME}/lib/tools.jar(此jar文件包含工具javac,我们需要javac处理jsp文件.

运行带有设定Java环境变量的命令行参数的java命令,调入tomcat.home,和org.apache.tomcat.startup.Tomcat 作为启始类.同时也传递命令行参数到org.apache.tomcat.startup.Tomcat ,例如:

执行start/stop/run 等的操作

此Tomcat进程使用指向server.xml的路径,

例如server.xml放置于etc/server_1.xml并且用户意图在后台启动apache,键 入如下命令行:

bin/tomcat.sh start –f /etc/server_1.xml

在Win32下的步骤：(略)

由此可见,win32版的tomcat.bat与Unix版的几乎一致.尤其在它不推测TOMCAT_HOME和JAVA_HOME的值,并且不将所有的jar文件放入类路径中.

Tomcat的配置文件

Tomcat的配置基于两个配置文件:

1.server.xml - Tomcat的全局配置文件

2.web.xml - 在Tomcat中配置不同的关系环境

这一部分将讲述如何使用这些文件.我们不会包含web.xml的内部机制,这些内部机制深入到了Servlet API的细节,因此,我们将讨论涵盖servler.xml内容及web.xml在Tomcat关系环境中的用法.

server.xml

server.xml是Tomcat的主配置文件.完成两个目标:

1 提供Tomcat组件的初始配置.

2 说明Tomcat的结构,含义,使得Tomcat通过实例化组件完成起动及构建自身, 如在server.xml所指定的

下表描述server.xml种的重要元素:


Tomcat中文手册(2)_转

作者：luoxh

email: luoxh@371.net

日期：8/23/2001 9:21:55 AM

Jakarta-Tomcat 简明中文版用户指南第二部分

元素及其描述

Server

server.xml文件中最重要的元素.Server定义了一个Tomcat服务器.一般你不用对他担心太多.Server元素能包含Logger和ContextManager元素类型

Logger

此元素定义一个Logger对象,每个Logger都有一个名字去标识,也有一个纪录Logger的输出和冗余级别(描述此日志级别)和包含日志文件的路径.通常有servlet的Logger(ServletContext.log()处),JSP和Tomcat运行时的Logger.

ContextManager

ContextManager说明一套ContextInterceptor, RequestInterceptor , Context和他们的Connectors的配置及结构.ContextManager有几个随同提供的特性:

1. 用来纪录调试信息的调试级别

2. webapps/,conf/,logs/和所有已定义的环境的基本位置.用来使Tomcat可以在TOMCAT_HOME外的其他目录启动.

3. 工作目录的名字

ContextInterceptor&RequestInterceptor

这些侦听器(interceptors)侦听具体发生在ContextManager中的事件.例如,ContextInterceptor侦听Tomcat的启动及终止事件,RequestInterceptor监视在它服务过程中用户请求需要通过的不同阶段.Tomcat的管理员不必知道太多关于侦听器的知识;另外,开发者应该知道这是如何在Tomcat中实现一个”全局”型的操作(例如安全性及每个请求日志)

Connector

Connector表示一个到用户的联接,不管是通过web服务器或直接到用户浏览器(在一个独立配置中).Connector负责管理Tomcat的工作线程和 读/写 连接到不同用户的端口的 请求/响应.Connector的配置包含如下信息:

1.句柄类

2.句柄监听的TCP/IP端口

3.句柄服务器端口的TCP/IP的backlog.

稍后我们将在此文档中描述如何配置Connector.

Context

每个Context提供一个指向你放置你Web项目的Tomcat的下属目录。每个Context包含如下配置：

1. Context放置的路径,可以是与ContextManager主目录相关的路径.

2.纪录调试信息的调试级别

3.可重载的标志.开发Servlet时,重载更改后的Servlet,这是一个非常便利的特性,你可以调试或用Tomcat测试新代码而不用停止或重新启动Tomcat.要打开重载,把reloadable设为真即可.这虽花费时间但可检测所发生的变化;更重要的事,鉴于,在一个装载类对象装入一个新的servlet时,类装载触发器可能会掷出一些错误.为避免这些问题,你可以设置可重载为假,这将停止重载功能.

从另一个目录中启动Tomcat

作为缺省值将使用TOMCAT_HOME/conf/server.xml作为配置文件.缺省配置将使用TOMCT_HOME作为关系环境的基础.

使用 “-f/你的/目录/server.xml”选项你可改变这种情况,使用另一个服务器配置文件和设置关系环境管理器的目录属性你需要在主目录内设置以下几个文件:

&Oslash; 一个 webapps/目录(如果你已生成) – 所有war文件奖杯界压倒此目录而且所有子目录将作为关系环境添加.

&Oslash; conf/目录 - 你可保存一个特殊的web.xml文件和其他配置文件

&Oslash; logs/ - 所有日志文件将代替TOMCAT_HOME/logs/纪录到此目录中

&Oslash; work/ - 关系环境的工作目录

如server.xml中的ContextManager.home属性有关联,将关联到到当前工作目录.

web。xml

关于web。xml和web项目结构（包括目录服务及配置）的详细描述可在Servlet API Spec的第9，10，14章中找到。

然而有一个与Tomcat有关的小“特性“与web.xml有关。Tomcat可以让用户通过将缺省的web.xml放入conf目录中来定义所有关系环境的web.xml的缺省值.建立一个新的关系环境时,Tomcat使用缺省的web.xml文件作为基本设置和应用项目特定的web.xml(放在应用项目的WEB-INF/web.xml文件)来覆盖这些缺省值.

设置Tomcat与Apache Web 服务器

服务器

到现在为止,我们未讨论作为服务器扩展的Tomcat,只讨论了作为独立运行的服务器.但有一些问题需要说明:

1. 当处理静态页面时,Tomcat不如Apache迅速.

2. Tomcat不象Apache一样可配置.

3. Tomcat不象Apache一样强壮.

4. 有很多网站已在某一特定web server上投入了很长时间,例如,使用CGI脚本/Server API模组/perl/php…我们不能假设这些遗留下来的东西都会被丢弃.

基于以上原因,一个现实的网站建议使用一个Web服务器,如Apache,为网站的静态页面请求提供服务,并使用Tomcat作为一个Servlet/JSP插件.

我们不准备深入的讨论每个不同的配置,我们将:

1. 涵盖Web服务器的基本行为

2. 解释需要何种配置

3. 在Apache上的实例

Web 服务器操作

简单说来，web服务器总是等待来自客户端的HTTP请求。当请求到达时，服务器会提供一切必要的内容来满足此请求。加入一个Servlet容器某种程度上会改变此行为。但服务器仍需处理如下因素：

载入servlet容器接口库并初始化（处理请求之前）。

当收到一个请求时，检查是否属于某Servlet，如是，则接口库接收此请求并处理。

另一方面，接口库需要知道他将服务某种请求，通常是基于请求的URL的某种模式和将此请求导向何处。

当用户想要设置使用虚拟主机的配置时，事情会变得更加复杂，或者想多个开发者在一个服务器上进行开发但使用不同的Servlet容器的JVMs。以下我们将讨论这两个问题。

必需进行的配置

应该考虑的是最明显的配置是servlet URL具有对servlet容器中的servlet具有表示的责任。很明显，你必须知道传递什么到Servlet容器。我们仍需提供附加的配置项目到web-server或servlet-container的结合体中.

l 考虑到Tomcat进程是否可取得，我们仍需提供配置和Tomcat正在监听的TCP/IP 主机名/端口号。

l 需要告知web服务器接口库的位置（因此我们可以在起始时装入）

l 需要设置接口内部信息如将日志记录在何处和如何纪录，等等。

所有此类信息必须出现在web服务器配置里或被接口使用的私有配置文件中。下面将讲述如何在Apache中如何实现这些配置。

Apache上的实现

这一部分演示如何配置Apache与Tomcat一起工作；并试图解释深入到可能会用到的配置规范。在jserv 安装页上可找到其他信息。


url安全在 netscape6.0中

作者：Lihailong

日期：2001-4-17 11:06:04

当我在APPLET中使用url连接时在netscape6.0中提示如下的安全错误：

java.security.AccessControlException: access denied (java.net.SocketPermission 192.168.0.121 resolve)

java.security.AccessControlException: access denied (java.net.SocketPermission 192.168.0.121 resolve)

    at java.security.AccessControlContext.checkPermission(Unknown Source)

    at java.security.AccessController.checkPermission(Unknown Source)

    at java.lang.SecurityManager.checkPermission(Unknown Source)

    at java.lang.SecurityManager.checkConnect(Unknown Source)

    at sun.plugin.protocol.jdk12.http.HttpURLConnection.connectStep1(Unknown Source)

    at sun.plugin.protocol.jdk12.http.HttpURLConnection.getInputStream(Unknown Source)

    at com.ynet.ads.applet.Booking.loadObject(Booking.java:539)

    at com.ynet.ads.applet.Booking.loadCustomer(Booking.java:258)

    at java.lang.reflect.Method.invoke(Native Method)

    at sun.plugin.liveconnect.PrivilegedCallMethodAction.run(Unknown Source)

    at java.security.AccessController.doPrivileged(Native Method)

    at sun.plugin.liveconnect.SecureInvocation.CallMethod(Unknown Source)

但此程序在IE下运行正常。（ns6.0使用的是内嵌的jdk1.3_01）


Use Java to implement the UBB future.

作者：iceant

email: iceant@21cn.com

日期：9/4/2001 2:25:47 PM

UBB & JAVA

    ----Use Java to implement the UBB future.

[Author] pizer.chen -- iceant -- 陈鹏

[email ] iceant@21cn.com

[icq   ] 77777369

These days,I write some publish system,just like BBS/news etc.

The client asked me ,whether they can insert the image and href url to the plain text when they published them.

so i write this.

I use xml file for dynamic UBB extends.

U can make the UBB rule of yourself.

The only thing that u need to do is modify the UBB.xml config file.

Best Regards

===============

                                                                    pizer.chen/2001-9-4

===========================

    Resource Link

===========================

It is based on xml & RegularExpressions tech.

about xml & regularExpression u can find them here:

http://www.w3.org/XML/

http://xml.apache.org

http://www.meurrens.org/ip-Links/java/regex/navi.html

http://www.savarese.org/oro/

http://jakarta.apache.org/oro

about ubb u can find them here:

http://www.ubbdesign.com

========================

it used :

    jakarta-regexp-1.2              (download form http://jakarta.apache.org

    dom4j-0.6(over 0.6 version)     (download form http://sourceforge.net

    jdk(over 1.2 version)           (download form http://java.sun.com

==========================

        UBB.XML

it must be stored in CLASSPATH

==========================

the regularExpression & replace string map file.

<!--============================-->

<?xml version="1.0" encoding="UTF-8"?>

<UBB-map>

    <map ubb-code="\[b\](.+?)\[\/b\]" map-to="&lt;b&gt;$1&lt;/b&gt;"/>         <!--<b>$1</b>-->

    <map ubb-code="\[i\](.+?)\[\/i\]" map-to="&lt;i&gt;$1&lt;/i&gt;"/>         <!--<i>$1</i>-->

    <map ubb-code="\[h1\](.+?)\[\/h1\]" map-to="&lt;h1&gt;$1&lt;/h1&gt;"/>     <!--<h1>$1</h1>-->

    <map ubb-code="\[url +href=(http\:\/\/.+?)\](.+?)\[\/url\]" map-to="&lt;a href=&quot;$1&quot; target=&quot;_blank&quot;&gt;$2&lt;/a&gt;"/>        <!--<a href="$1" target="_blank">$2</a>-->

    <!--...-->

</UBB-map>

=========================

        UBB.java

=========================

/*

 * UBB.java

 *

 * Created on 2001年9月3日, 下午4:01

 */

package com.wacos.util.ubb;

import org.dom4j.*;

import org.dom4j.io.*;

import java.io.*;

import java.util.*;

import org.apache.regexp.*;

/**

 *

 * @author  Pizer.chen -- iceant -- 陈鹏

 * @version 0.1

 */

public class UBB {

    private static final String XML_CONFIG_FILE = "UBB.xml";

    private static org.dom4j.Document doc = null;

    /** Creates new UBB */

    public UBB() {

    }

    public static String parse(String inStr){

        try{

            List list = getUBBCodeList();

            String ubbCode="";

            String mapStr="";

            Attribute attribute=null;

            for (Iterator iter = list.iterator(); iter.hasNext(); ) {

                attribute = (Attribute) iter.next();

                ubbCode = attribute.getValue();

                mapStr= getMapValue(ubbCode);

                inStr=REReplace.replace(ubbCode,mapStr,inStr);

            }

            return inStr;

        }catch(Exception err){

            System.out.println(err);

            return err.toString();

        }

    }

    /**

     * parse the xml file to Document object

    **/

    private static Document xml2Document(){

        try{

            InputStream is = Thread.currentThread().getContextClassLoader()

                            .getResourceAsStream(XML_CONFIG_FILE);

            SAXReader reader = new SAXReader();

            Document document = reader.read(is);

            return document;

        }catch(Exception err){

            System.out.println(err);

            return null;

        }

    }

    /**

     * use xpath to get the map-to value of the ubb-code.

    **/

    private static String getMapValue(String ubbCode){

        try{

            if(doc==null){

                doc=xml2Document();

            }

            Node node = doc.selectSingleNode("//map[@ubb-code='"+ubbCode+"']");

            return node.valueOf( "@map-to" );

        }catch(Exception err){

            System.out.println(err);

            return err.toString();

        }

    }

    /**

     * get the <map ubb-code="..." map-to=".."> ubb-code List

    **/

    private static List getUBBCodeList(){

        try{

            if(doc==null){

                doc=xml2Document();

            }

            return doc.selectNodes( "//map/@ubb-code" );

        }catch(Exception err){

            System.out.println(err);

            return null;

        }

    }

    /**

     * get the <map ubb-code="..." map-to=".."> map-to List

    **/

    private static List getUBBMapList(){

        try {

            if(doc==null){

                doc=xml2Document();

            }

            return doc.selectNodes("//map//@map-to");

        }

        catch (Exception e) {

            System.out.println(e);

            return null;

        }

    }

}

========================

     REReplace.java

========================

package com.wacos.util.ubb;

import java.io.*;

import java.util.*;

import org.apache.regexp.*;

public class REReplace

{

    /**

     * replace the inStr with pattern1 & pattern2

    **/

    public static String replace(String pattern1,String pattern2,String inStr){

        try {

            RE re = new RE(pattern1);

            int point=0;

            while(re.match(inStr)){

                RE re2 = new RE("\\$([0-9])");

                while(re2.match(pattern2)){

                    point = Integer.parseInt(re2.getParen(1));

                    pattern2=re2.subst(pattern2,re.getParen(point),RE.REPLACE_FIRSTONLY);

                }

                inStr = re.subst(inStr,pattern2);

            }

            return inStr;

        }

        catch (Exception e) {

            System.out.println(e);

            return e.toString();

        }

    }

}

=============================

        UBBTest

=============================

package com.wacos.util.ubb;

public class UBBTest

{

    public static void main(String[] args)

    {

        try{

            String test ="a[b][i]test[/i][/b][h1]H1 Font[/h1]\r\n [url href=http://www.21cn.com]

            System.out.println(UBB.parse(test));

        }catch(Exception err){

            System.out.println(err);

        }

    }

}


vaj 和 jbuid使用感受

作者：大脸猫

email: danielbs@yeah.net

日期：2001-7-5 16:50:22

刚才有人问想比价vaj和jb,小弟我有点感受：

1.  2者优点都有，如果是想做企业级程序，包括ejb,servlet，vaj绝对比jb方便，尤其是做ejb，做界面用vaj在功能、使用方便性比jb高出很多，尤其是一些高级功能，jb必须在代码中实现。

2.  但是，vaj最烂的就是吃内存，我的机器512m，但vaj就从来就没正常退出过，都是ctrl+alt+del退出的，启动很慢。

3.  所以，内存不够多，机器不够好就别用vaj，再有vaj做的ejb包只能给websphere布置。还有，用vaj做界面时，经常出现可视化界面不能可视化（以前提过），尤其烂！！！！

4.  jb我没做过ejb，但是据功能可以给websphere,weblogic等不同服务器布置，做界面总的还算方便，它最大的好处就是吃内存比vaj小很多很多很多，最大的缺点就是生成代码很乱很乱，结构没有vaj清晰。

呵呵：）


Vector在Java编程中的应用

作者：NOVEMBER RAIN

日期：2001-1-11 8:41:19

Vector 类提供了实现可增长数组的功能，随着更多元素加入其中，数组变的更大。在删除一些元素之后，数组变小。

Vector 有三个构造函数，

public Vector(int initialCapacity,int capacityIncrement)

                  public Vector(int initialCapacity)

                  public Vector()

    Vector 运行时创建一个初始的存储容量initialCapacity，存储容量是以capacityIncrement 变量定义的增量增长。初始的存储容量和capacityIncrement 可以在Vector 的构造函数中定义。第二个构造函数只创建初始存储容量。第三个构造函数既不指定初始的存储容量也不指定capacityIncrement。

    Vector 类提供的访问方法支持类似数组运算和与Vector 大小相关的运算。类似数组的运算允许向量中增加，删除和插入元素。它们也允许测试矢量的内容和检索指定的元素，与大小相关的运算允许判定字节大小和矢量中元素不数目。

    现针对经常用到的对向量增，删，插功能举例描述：

addElement(Object obj)

    把组件加到向量尾部，同时大小加1，向量容量比以前大1

insertElementAt(Object obj, int index)

    把组件加到所定索引处，此后的内容向后移动1 个单位

setElementAt(Object obj, int index)

    把组件加到所定索引处，此处的内容被代替。

    removeElement(Object obj) 把向量中含有本组件内容移走。

    removeAllElements() 把向量中所有组件移走，向量大小为0。

    例如：

         import java.lang.System;

         import java.util.Vector;

         import java.util.Emumeration;

         public class Avector{

                                 public static void main(String args[])

                                        {

0.　　 Vector v=new Vector();

1. v.addElement("one");

2. addElement("two");

3. v.addElement("three");

4. v.insertElementAt("zero",0);

5. v.insertElementAt("oop",3);

6. v.setElementAt("three",3);

7. v.setElementAt("four",4);

8. v.removeAllElements();

}

}

Vector中的变化情况：

1. one　　 2. one　　 3. one　　 4. zero　　 5.zero　　 6. zero　　7. zero

8.

                    two　　 two　 one　　 one　　one　　 one

                          three　　two　　 two　　two　　 two

                                  three　　oop　　three　　three

                                          three　　 three　　four

    另外，Vector 在参数传递中发挥着举足轻重的作用。

    在Applet 中有一块画布(Canvas) 和一个(Panel), 而Panel 中放着用户要输入的信息，根据这些信息把参数传递到canvas 中，这时在Java 中用一个接口（Interface）, 而在接口中需用一个Vector 去传递这些参数。另外，在一个类向另一个类参数传递就可以用这种方法。

    例如：

import java.util.Vector

interface codeselect{

                      Vector codeselect=new Vector();

                         }

显示数学信息

Vector(0)存入学生编号

Vector(1)存入学科

    在Panel 中当用户在TextField 和Choice 中选择自己所要求的内容，程序中

    通过事件响应把值传到向量Vector 中。

    假若在Panel 类中：

public void　　codepanel extends Panel{

    public void init()

    {

        **.

TextField　　s=new TextField();

Choice c=new Choice();

c. addItem("语文");

c.addItem("数学");

c.addItem("政治");

add(s);

add (c);

**

}

public boolean handleEvent(Event event){

if(event.id==Event.ACTION_EVENT){

if(event.target.instanceof Textfield)

{

coderesult.setElementAt(s.getText(),0);

}

else if(event.target intanceof Choice)

{

coderesult.setElementAt(new Integer(c.getSelectedIndex()),1);

}

}

}

}

    这时，向量中已经存入学生编号和学科索引号（0 为语文，1 为数学，2 为政治）。

    而在Canvas 中得到此值，

public class codecanvas extends Canvas{

public void code{

                        }

public void paint{

String str;

int t;

str=(String)coderesult.elementAt(0);

t=(new Integer(codeselect.elementAt(1).toString())).intValue();

if(t==0)

{

显示语文信息

}

else if(t==1)

{

显示数学信息

}

else if(t==2)

{

显示政治信息

}

}

}


weblogic6.0使用心得

作者：blackwhites

日期：2001-6-6 18:02:05

weblogic默认应用程序的配置

1.在以下目录存放jsp文件

C:\bea\wlserver6.0\config\mydomain\applications\DefaultWebApp_myserver\test.jsp

在browser中浏览http://localhost:7001/test.jsp

在C:\bea\wlserver6.0\config\mydomain\applications\DefaultWebApp_myserver\WEB-INF\_tmp_war_myserver_myserver_DefaultWebApp_myserver\jsp_servlet生成_test.class

2.在以下目录存放servlet

C:\bea\wlserver6.0\config\mydomain\applications\DefaultWebApp_myserver\WEB-INF\classes\HelloWorldServlet.class

配置web.xml

<!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>

  <display-name>Examples Web Application</display-name>

  <servlet>

    <servlet-name>HelloWorldServlet</servlet-name>

    <servlet-class>HelloWorldServlet</servlet-class>

  </servlet>

  <servlet-mapping>

    <servlet-name>HelloWorldServlet</servlet-name>

    <url-pattern>/HelloWorldServlet/*</url-pattern>

  </servlet-mapping>

</web-app>

在browser中浏览http://localhost:7001/HelloWorldServlet

weblogic自建立应用程序的配置

假设目录为C:\bea\wlserver6.0\config\examples\applications\examplesWebApp

1.JSP的配置

在以上目录存放jsp文件ShowDate.jsp，并在以下目录存放相应的字节码文件

C:\bea\wlserver6.0\config\examples\applications\examplesWebApp\WEB-INF\classes\jsp_servlet\_showdate.class

你可以自行建立、编译，不过推荐使用weblogic.jspc，请参照examples\jsp\build.cmd

在browser中浏览http://localhost:7001/examplesWebApp/ShowDate.jsp

2.servlet的配置

C:\bea\wlserver6.0\config\examples\applications\examplesWebApp\WEB-INF\classes\HelloWorldServlet.class

配置web.xml

<!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>

  <display-name>Examples Web Application</display-name>

  <servlet>

    <servlet-name>HelloWorldServlet</servlet-name>

    <servlet-class>HelloWorldServlet</servlet-class>

  </servlet>

  <servlet-mapping>

    <servlet-name>HelloWorldServlet</servlet-name>

    <url-pattern>/HelloWorldServlet/*</url-pattern>

  </servlet-mapping>

</web-app>

在browser中浏览http://localhost:7001/examplesWebApp/HelloWorldServlet

>>==全文完==<<

>>==作者：陈海峰==<<


weblogic文档大全（URL）

作者：123go

email: haippo@elong.com

日期：7/5/2001 12:37:25 AM

http://e-docs.bea.com/wls/docs61/index.html


WebSphere快速入门(1)

作者：easyinfonet

email: easyinfonet@sina.com.cn

日期：2001-2-16 11:18:17

WebSphere快速入门

瞿裕忠 张剑锋 王丛刚 陈峥

东南大学计算机科学与工程系

摘要

我们已经走进电子商务时代，本书将带您进入电子商务应用开发的世界。本书第一章介绍电子商务理念及有关技术基础，包括Internet、Web和Java。第二章介绍IBM电子商务应用框架，包括WebSphere。第三章给您带来WebSphere Studio应用开发技术。第四、五章分别介绍新一代Web应用的关键技术：Java Servlet和JSP。第六章介绍新一代Web应用服务器软件：WebSphere应用服务器。第七章带给您新一代Web应用编程技术：WebSphere应用编程技术。本书附录中的实验指导带您一起走过使用WebSphere技术开发电子商务应用的过程。

本书特别适合于信息技术类的高年级大学生和研究生，以及从事电子商务应用开发的技术人员。通过学习本书，您将了解电子商务应用开发的先进技术，并快速掌握WebSphere电子商务应用开发的基本技术。

第一章 电子商务基础知识

Web正改变着我们生活的方方面面，但任何领域都没有象商务运作方式那样经历着一场快速而巨大的变革。众多商家正在准备或已经走向转变到电子商务的道路，即使用Internet技术转变关键的商务过程。本章介绍电子商务理念及有关技术基础，包括Internet、Web和Java。

1.1 电子商务理念

从20世纪70年代以来，很多机构依靠电子数据交换（EDI、Electronic Data Interchange）实现业务处理的自动化。EDI着重于商业伙伴之间的事务处理标准化，但是EDI标准缺乏灵活性和可扩展性。进入20世纪90年代，随着Web技术的发展，尤其是在1995年Java出现以后，许多机构开始采用Web应用系统来支持电子商务。电子商务（e-business）是指借助Internet及相关技术进行商务活动，而一个电子商务(an e-business)是这样的一个机构，它通过内部网、外部网和Web将关键业务系统直接连接到客户、员工、供应商和业务伙伴。一个机构要转型为一个电子商务，就要使用Internet技术转变关键的业务过程，如客户关系管理、电子商贸、供应链管理、企业内部管理。这个转变过程也是一个综合使用Internet技术、信息技术、商务技术转变业务方式的过程。

然而，电子商务不仅仅是技术更新。转变到电子商务必须对需要做什么有一个明确的蓝图，以及对实现这个蓝图有一个清晰的发展指南。电子商务周期模型正是为企业开展电子商务提供了一个模型。电子商务周期（参见图1-1）由四个组成阶段，包括转变、构造、运行和利用。想要转变到电子商务的机构无论何时均可以从任何一个阶段开始。这也是一个重复的过程。

图1-1：电子商务周期模型

1)    转变阶段是关于转变核心的商务过程，也就是要将现有的商务模型扩展到网络世界以创造一个电子商务模型。应用Internet技术为商务创造最大限度的价值，电子商务改变着客户关系管理、供应链和电子商贸的传统准则。在转变商务过程时，每一个商务过程应该放在整体环境中加以考虑。否则，充其量只是离散的各个更好的商务过程，无法带来期望的改善客户服务和提高电子商务价值的效果。

2)    构造阶段是关于构造新的应用系统。转变核心的商务过程需要新一代的应用系统。构造阶段也包括使用一个基于开放标准的途径将已有应用系统迁移到Web上。要求电子商务应用系统是基于标准的、以服务器为中心的、可伸缩的、可快速部署、易用和易管理的。

3)    运行阶段涉及一个可伸缩的、可用的、安全的运行环境。围绕着商务和应用系统通常有一个基础设施。基础设施提供的服务要求是可用的、可伸缩的、易管理的和安全的。

4)    利用阶段是关于知识和信息的利用。这里的焦点是知识管理，也就是说利用我们知道的东西。与信息管理不同，知识管理包括对显式知识和隐式知识的管理。传统的IT系统处理的是显式知识，即能写下来并能编程处理的。而隐式知识是人们知道的但没有被写下来的东西，它基于直觉、经验和洞察力。

从简单的开始，快速地增长。从现有的基础上构造电子商务应用系统，逐步将核心业务扩展到Internet上，最终实现电子商务带来的巨大的投资回报。然而，电子商务的技术基础主要包括Internet、Web和Java，本章以下各节介绍这些技术的基础知识。

1.2 Internet基础知识

Internet已经成为企业、政府和研究机构共享信息的基础设施，同时也是开展电子商务的基础。Internet 的先驱是ARPANET。美国国防高级研究计划局 （Defense Advanced Research Project Agency、简称DARPA)于20 世纪 60 年代后期资助开发了一种叫做 ARPANET的实验性通讯系统。起初，它仅用于连接美国军事机构的计算机网络，但随后不久，这个网络很快扩展到与国防有关的公司和研究机构。从此，面向特定应用需求的不同网络协议及网络技术相继出现，同时也带来了网络互连的问题。如果两个网络使用不同的协议，即使采用某种网络技术将它们在物理上互相连接起来，应用系统（如邮件系统）之间也无法相互沟通。为此，很多组织，如CCITT（Consultative  Committee  on  International  Telephony  and  Telegraphy，现在成为 ITU-T，即International  Telecommunications  Union -  Telecommunication Standardization Sector）和ISO(国际标准化组织)，开始考虑定义一套分层协议族，使得应用系统之间能相互通信，即使这些应用系统运行在不同的网络环境中（如不同的操作系统和不同的网络技术）。DARPA从1970年左右开始研究一套称之为TCP/IP的分层协议族，于1978年左右基本定形。ARPANET于1980年左右开始采用TCP/IP协议族，并于1983年全面实现。与此同时，TCP/IP协议族在 UNIX操作系统中也得到了实现（由加利福尼亚大学伯克利分校完成），并免费分发。从此，TCP/IP在大学和研究机构中迅速传播，并成为连接UNIX系统的标准协议。这些互相连接的网络广泛深入到大学和其它组织（一开始主要是非赢利组织）。由于个人计算机的迅速普及，该网络扩展到全球大部分地方，并且开始吸引成千上万的个人以及赢利组织加入，由此逐渐形成了所谓的 Internet（因特网）。Internet指的是全球性互连网络。它由下列网络群构成：

1)    主干网：通常为大规模网络，这些网络主要用来与其它网络互连，如美国的NSFNET（NSF是指美国国家自然科学基金会）、欧洲的EBONE、大型的商用主干网。

2)    区域网：如连接大专院校的区域网。

3)    商用网络：为客户提供连接骨干网的服务的网络，或只供公司内部使用且连接到Internet的网络。

4)    局域网：如校园网。

90年代，Internet发展十分迅猛，这与1991年HTML的问世和Gopher的免费发放有关，也与1993年Mosaic的问世有关，Internet的商用也加速了Internet的扩张。到20世纪末，Internet已经成为一种通过服务器将小型网络连接起来的错综复杂的网络结构。大部分情况下，服务器通过专门进行 Internet 通讯的线路来传送数据。个人计算机则通过直接线路，或者通过电话线和调制解调器连接到这些服务器上。直接线路一般是高速的电讯线路，专门用于在建筑物之间或组织之间传送数据。而标准的电话线路，现在主要是综合业务数字网络（ISDN）的线路，则通常用于连接个人计算机。


WebSphere快速入门(10)

作者：easyinfonet

email: easyinfonet@sina.com.cn

日期：2001-2-16 11:30:30

3.5 VisualAge for Java

如果您熟悉 Java 程序设计，可以使用VisualAge for Java(VAJ)来创建和定制 JavaBean。在您使用Studio的向导构建Servlet时很可能需要复杂的JavaBean，这些JavaBean一般可使用VAJ来开发。也可使用VAJ来修改与完善由 Studio 向导生成的 Java Servlet 和 Bean。

VAJ是一个全功能的Java开发环境，包含一个智能化集成开发环境，内含丰富的Java类库、高性能Java编译器和虚拟机，集成了包括向导和调试器在内的多种工具，支持JavaBeans的开发，通过先进的版本控制技术和贮存（Repository）机制支持团队开发(team development)。

VAJ的工作台为你提供各种视图，每个视图作为笔记薄（Notebook）中的一页

l    项目－在工作区中所有项目及所含包、类的树状视图

l    包－所有包、类、方法的树状视图

l    类－工作区中的所有类的列表

l    接口－工作区的所有接口及其方法的列表

l    所有问题－所有出错元素及相应错误信息的列表

项目页允许你为一个元素作标签，这样只需简单的一次单击就可定位到它。另外，当你保存文件时，将进行编译和错误标志，更正错误无需长时间编译，从而体现了应用的快速开发。使用VAJ的工作台进行Java 编程的步骤如下：

l    创建项目（和包）

l    创建类（可使用向导）

l    添加方法（可使用向导）

l    在源代码页中编辑方法，在菜单上单击保存－将进行快速编译

l    单击“运行”按钮测试代码

使用VAJ的导入／导出功能可以将有关文件（JAR、class、java）导入或导出VAJ的开发环境。值得关注的是，WebSphere可在整个VAJ中运行，允许你在VAJ环境中编写和调试servlet和JSP。对于VAJ2.0来说，需要下列步骤：

l    安装VisualAge for Java update

l    载入JSP 执行监视器，改正语法错误

l    在com.ibm.servlet包中启动SERunner web服务

而VAJ3.0已经和WebSphere Application Server、WebSphere Studio、DB2 Universal Database紧密集成，从而成为IBM电子商务应用框架的关键点。VAJ3.0的新的特性主要包括：

l    WebSphere 测试环境

l    JSP/Servlet 开发环境

l    存贮过程构建器

l    SQLJ工具

l    EJB 开发环境（VAJ3.0企业版）

l    Domino企业存取构建器（VAJ3.0企业版）

l    XML元数据交换(XML Metadata Interchange、XMI)工具包（VAJ3.0企业版-技术预演）


WebSphere快速入门(11)

作者：easyinfonet

email: easyinfonet@sina.com.cn

日期：2001-2-16 11:31:14

第四章 Java Servlet技术

4.1 Servlet简介

    1．Servlet 是什么？

Servlet是使用Java Servlet 应用程序设计接口（API）及相关类和方法的 Java 程序。除了 Java Servlet API，Servlet 还可以使用用以扩展和添加到 API 的 Java 类软件包。Servlet 在启用 Java 的 Web 服务器上或应用服务器上运行并扩展了该服务器的能力。Java servlet对于Web服务器就好象Java applet对于Web浏览器。Servlet装入Web服务器并在Web服务器内执行，而applet装入Web浏览器并在Web浏览器内执行。Java Servlet API 定义了一个servlet 和Java使能的服务器之间的一个标准接口，这使得Servlets具有跨服务器平台的特性。

    Servlet 通过创建一个框架来扩展服务器的能力，以提供在 Web 上进行请求和响应服务。当客户机发送请求至服务器时，服务器可以将请求信息发送给 Servlet，并让 Servlet 建立起服务器返回给客户机的响应。 当启动 Web 服务器或客户机第一次请求服务时，可以自动装入 Servlet。装入后， Servlet 继续运行直到其它客户机发出请求。Servlet 的功能涉及范围很广。例如，Servlet 可完成如下功能：

    (1) 创建并返回一个包含基于客户请求性质的动态内容的完整的 HTML页面。

    (2) 创建可嵌入到现有 HTML 页面中的一部分 HTML 页面（HTML 片段）。

    (3) 与其它服务器资源（包括数据库和基于 Java 的应用程序）进行通信。

    (4) 用多个客户机处理连接，接收多个客户机的输入，并将结果广播到多个客户机上。例如，Servlet 可

以是多参与者的游戏服务器。

    (5) 当允许在单连接方式下传送数据的情况下，在浏览器上打开服务器至applet的新连接，并将该连

接保持在打开状态。当允许客户机和服务器简单、高效地执行会话的情况下，applet也可以启动客户浏览器和服务器之间的连接。可以通过定制协议或标准（如 IIOP）进行通信。

    (6) 对特殊的处理采用 MIME 类型过滤数据，例如图像转换和服务器端包括（SSI）。

    (7) 将定制的处理提供给所有服务器的标准例行程序。例如，Servlet 可以修改如何认证用户。

    2．Servlet 的生命周期

    Servlet 的生命周期始于将它装入 Web 服务器的内存时，并在终止或重新装入 Servlet 时结束。

    (1) 初始化

    在下列时刻装入 Servlet：

    =如果已配置自动装入选项，则在启动服务器时自动装入

    =在服务器启动后，客户机首次向 Servlet 发出请求时

    =重新装入 Servlet 时

    装入 Servlet 后，服务器创建一个 Servlet 实例并且调用 Servlet 的 init() 方法。在初始化阶段，Servlet 初始化参数被传递给 Servlet 配置对象。

    (2) 请求处理

    对于到达服务器的客户机请求，服务器创建特定于请求的一个“请求”对象和一个“响应”对象。服务器调用 Servlet 的 service() 方法，该方法用于传递“请求”和“响应”对象。service() 方法从“请求”对象获得请求信息、处理该请求并用“响应”对象的方法以将响应传回客户机。service() 方法可以调用其它方法来处理请求，例如 doGet()、doPost() 或其它的方法。

    (3) 终止

    当服务器不再需要 Servlet, 或重新装入 Servlet 的新实例时，服务器会调用 Servlet 的 destroy() 方法。

3.    Java Servlet API

    Java Servlet 开发工具（JSDK）提供了多个软件包，在编写 Servlet 时需要用到这些软件包。其中包括两个用于所有 Servlet 的基本软件包：javax.servlet 和 javax.servlet.http。可从sun公司的Web站点下载 Java Servlet 开发工具。 下面主要介绍javax.servlet.http提供的HTTP Servlet应用编程接口。

    HTTP Servlet 使用一个 HTML 表格来发送和接收数据。要创建一个 HTTP Servlet，请扩展 HttpServlet 类， 该类是用专门的方法来处理 HTML 表格的 GenericServlet 的一个子类。 HTML 表单是由 <FORM> 和 </FORM> 标记定义的。表单中典型地包含输入字段（如文本输入字段、复选框、单选按钮和选择列表）和用于提交数据的按钮。当提交信息时，它们还指定服务器应执行哪一个Servlet（或其它的程序）。 HttpServlet 类包含 init()、destroy()、service() 等方法。其中 init() 和 destroy() 方法是继承的。

    (1)  init() 方法

    在 Servlet 的生命期中，仅执行一次 init() 方法。它是在服务器装入 Servlet 时执行的。 可以配置服务器，以在启动服务器或客户机首次访问 Servlet 时装入 Servlet。 无论有多少客户机访问 Servlet，都不会重复执行 init() 。

    缺省的 init() 方法通常是符合要求的，但也可以用定制 init() 方法来覆盖它，典型的是管理服务器端资源。 例如，可能编写一个定制 init() 来只用于一次装入 GIF 图像，改进 Servlet 返回 GIF 图像和含有多个客户机请求的性能。另一个示例是初始化数据库连接。缺省的 init() 方法设置了 Servlet 的初始化参数，并用它的 ServletConfig 对象参数来启动配置， 因此所有覆盖 init() 方法的 Servlet 应调用 super.init() 以确保仍然执行这些任务。在调用 service() 方法之前，应确保已完成了 init() 方法。

    (2) service() 方法

    service() 方法是 Servlet 的核心。每当一个客户请求一个HttpServlet 对象，该对象的service() 方法就要被调用，而且传递给这个方法一个“请求”（ServletRequest）对象和一个“响应”（ServletResponse）对象作为参数。 在 HttpServlet 中已存在 service() 方法。缺省的服务功能是调用与 HTTP 请求的方法相应的 do 功能。例如， 如果 HTTP 请求方法为 GET，则缺省情况下就调用 doGet() 。Servlet 应该为 Servlet 支持的 HTTP 方法覆盖 do 功能。因为 HttpServlet.service() 方法会检查请求方法是否调用了适当的处理方法，不必要覆盖 service() 方法。只需覆盖相应的 do 方法就可以了。

    = 当一个客户通过HTML 表单发出一个HTTP POST请求时，doPost（）方法被调用。与POST请求相关的参数作为一个单独的HTTP 请求从浏览器发送到服务器。当需要修改服务器端的数据时，应该使用doPost()方法。

    = 当一个客户通过HTML 表单发出一个HTTP GET请求或直接请求一个URL时，doGet()方法被调用。与GET请求相关的参数添加到URL的后面，并与这个请求一起发送。当不会修改服务器端的数据时，应该使用doGet()方法。

    Servlet的响应可以是下列几种类型：

     一个输出流，浏览器根据它的内容类型（如text/HTML）进行解释。

     一个HTTP错误响应, 重定向到另一个URL、servlet、JSP。

    (3)  destroy() 方法

    destroy() 方法仅执行一次，即在服务器停止且卸装Servlet 时执行该方法。典型的，将 Servlet 作为服务器进程的一部分来关闭。缺省的 destroy() 方法通常是符合要求的，但也可以覆盖它，典型的是管理服务器端资源。例如，如果 Servlet 在运行时会累计统计数据，则可以编写一个 destroy() 方法，该方法用于在未装入 Servlet 时将统计数字保存在文件中。另一个示例是关闭数据库连接。

    当服务器卸装 Servlet 时，将在所有 service() 方法调用完成后，或在指定的时间间隔过后调用 destroy() 方法。一个Servlet 在运行service() 方法时可能会产生其它的线程，因此请确认在调用 destroy() 方法时，这些线程已终止或完成。

    (4) GetServletConfig（）方法

    GetServletConfig（）方法返回一个 ServletConfig 对象，该对象用来返回初始化参数和ServletContext。ServletContext 接口提供有关servlet 的环境信息。

    (5) GetServletInfo（）方法

    GetServletInfo（）方法是一个可选的方法，它提供有关servlet 的信息，如作者、版本、版权。

当服务器调用sevlet 的Service（）、doGet（）和doPost（）这三个方法时，均需要 “请求”和“响应”对象作为参数。“请求”对象提供有关请求的信息，而“响应”对象提供了一个将响应信息返回给浏览器的一个通信途径。javax.servlet 软件包中的相关类为ServletResponse和ServletRequest，而javax.servlet.http 软件包中的相关类为HttpServletRequest 和 HttpServletResponse。Servlet 通过这些对象与服务器通信并最终与客户机通信。Servlet 能通过调用“请求”对象的方法获知客户机环境，服务器环境的信息和所有由客户机提供的信息。Servlet 可以调用“响应”对象的方法发送响应，该响应是准备发回客户机的。


WebSphere快速入门(12)

作者：easyinfonet

email: easyinfonet@sina.com.cn

日期：2001-2-16 11:33:19

4.3 调用 Servlet

    要调用 Servlet 或 Web 应用程序，请使用下列任一种方法：由 URL 调用、在 <FORM> 标记中调用、在 <SERVLET>标记中调用、在 JSP 文件中调用、在 ASP 文件中调用。

    1. 由 URL 调用 Servlet

    这里有两种用 Servlet 的 URL 从浏览器中调用该 Servlet 的方法：

    (1) 指定 Servlet 名称：当用 WebSphere应用服务器 管理器来将一个 Servlet 实例添加（注册）到服务器配置中时，必须指定“Servlet 名称”参数的值。例如，可以指定将 hi 作为 HelloWorldServlet 的 Servlet 名称。要调用该 Servlet，需打开 http://your.server.name/servlet/hiServlet 和类使用同一名称（HelloWorldServlet）。在这种情况下，将由 http://your.server.name/servlet/HelloWorldServlet 来调用 Servlet 的实例。

    (2) 指定 Servlet 别名：用 WebSphere应用服务器 管理器来配置 Servlet 别名，该别名是用于调用 Servlet 的快捷 URL。快捷 URL 中不包括 Servlet 名称。

    2. 在 <FORM> 标记中指定 Servlet

    可以在 <FORM> 标记中调用 Servlet。HTML 格式使用户能在 Web 页面（即从浏览器）上输入数据，并向 Servlet 提交数据。例如：

<FORM METHOD="GET" ACTION="/servlet/myservlet">

  <OL>

    <INPUT TYPE="radio" NAME="broadcast" VALUE="am">AM<BR>

    <INPUT TYPE="radio" NAME="broadcast" VALUE="fm">FM<BR>

  </OL>

（用于放置文本输入区域的标记、按钮和其它的提示符。）

</FORM>

     ACTION 特性表明了用于调用 Servlet 的 URL。关于METHOD 的特性，如果用户输入的信息是通过 GET 方法向 Servlet 提交的，则 Servlet 必须优先使用 doGet() 方法。反之，如果用户输入的信息是通过 POST 方法向 Servlet 提交的，则 Servlet 必须优先使用 doPost() 方法。使用 GET 方法时，用户提供的信息是查询字符串表示的 URL 编码。无需对 URL 进行编码，因为这是由表单完成的。然后 URL 编码的查询字符串被附加到 Servlet URL 中，则整个 URL 提交完成。URL 编码的查询字符串将根据用户同可视部件之间的交互操作，将用户所选的值同可视部件的名称进行配对。例如，考虑前面的 HTML 代码段将用于显示按钮(标记为 AM 和 FM)，如果用户选择 FM 按钮，则查询字符串将包含 name=value 的配对操作为broadcast=fm。因为在这种情况下，Servlet 将响应 HTTP 请求，因此 Servlet 应基于 HttpServlet 类。Servlet 应根据提交给它的查询字符串中的用户信息使用的 GET 或 POST 方法，而相应地使用 doGet() 或 doPost() 方法。

    3．在 <SERVLET> 标记中指定 Servlet

    当使用 <SERVLET> 标记来调用 Servlet 时，如同使用 <FORM> 标记一样，无需创建一个完整的 HTML 页面。作为替代，Servlet 的输出仅是 HTML 页面的一部分，且被动态嵌入到原始 HTML 页面中的其它静态文本中。所有这些都发生在服务器上，且发送给用户的仅是结果 HTML 页面。建议在 Java 服务器页面（JSP）文件中使用 <SERVLET> 标记。请参阅有关 JSP 技术

    原始 HTML 页面中包含 <SERVLET> 和 </SERVLET> 标记。 Servlet 将在这两个标记中被调用，且 Servlet 的响应将覆盖这两个标记间的所有东西和标记本身。如果用户的浏览器可以看到 HTML 源文件，则用户将看不到 <SERVLET> 和 </SERVLET> 标记。要在 Domino Go Webserver 上使用该方法，请启用服务器上的服务器端包括功能。部分启用过程将会涉及到添加特殊文件类型 SHTML。当 Web 服务器接收到一个扩展名为 SHTML 的 Web 页面请求时，它将搜索 <SERVLET> 和 </SERVLET> 标记。对于所有支持的 Web 服务器，WebSphere应用服务器 将处理 SERVLET 标记间的所有信息。下列 HTML 代码段显示了如何使用该技术。

<SERVLET NAME="myservlet" CODE="myservlet.class" CODEBASE="url" initparm1="value">

<PARAM NAME="parm1" VALUE="value">

</SERVLET>

    使用 NAME 和 CODE 属性带来了使用上的灵活性。可以只使用其中一个属性，也可以同时使用两个属性。 NAME 属性指定了 Servlet 的名称（使用 WebSphere应用服务器 管理器配置的），或不带 .class 扩展名的 Servlet 类名。CODE 属性指定了 Servlet 类名。使用 WebSphere应用服务器 时，建议指定 NAME 和 CODE，或当 NAME 指定了 Servlet 名称时，仅指定 NAME。如果仅指定了 CODE，则会创建一个 NAME=CODE 的 Servlet 实例。装入的 Servlet 将假设 Servlet 名称与 NAME 属性中指定的名称匹配。然后，其它 SHTML 文件可以成功地使用 NAME 属性来指定 Servlet 的名称，并调用已装入的 Servlet。NAME 的值可以直接在要调用 Servlet 的 URL 中使用。如果 NAME 和 CODE 都存在，且 NAME 指定了一个现有 Servlet，则通常使用 NAME 中指定的 Servlet。由于 Servlet 创建了部分 HTML 文件，所以当创建 Servlet 时，将可能会使用 HttpServlet 的一个子类，并优先使用 doGet() 方法（因为 GET 方法是提供信息给 Servlet 的缺省方法）。另一个选项是优先使用 service() 方法。另外，    CODEBASE 是可选的，它指定了装入 Servlet 的远程系统的 URL。请使用 WebSphere应用服务器 管理器来从 JAR 文件配置远程 Servlet 装入系统。

    在上述的标记示例中，initparm1 是初始化参数名，value 是该参数的值。可以指定多个“名称-值”对的集合。利用 ServletConfig 对象（被传递到 Servlet 的 init() 方法中）的 getInitParameterNames() 和 getInitParameter() 方法来查找参数名和参数值的字符串数组。在示例中，parm1 是参数名，并在初始化 Servlet 后被才被设置某个值。因为只能通过使用“请求”对象的方法来使用以 <PARAM> 标记设置的参数，所以服务器必须调用 Servlet service() 方法，以从用户处传递请求。要获得有关用户的请求信息，请使用 getParameterNames()、getParameter() 和 getParameterValues() 方法。

    初始化参数是持续的。假设一台客户机通过调用一个包含某些初始化参数的 SHTML 文件来调用 Servlet。并假设第二台客户机通过调用第二个 SHTML 文件来调用同一个 Servlet，且该 SHTML 中未指定任何初始化参数。那么第一次调用 Servlet 时所设置的初始化参数将一直可用，并且通过所有其它 SHTML 文件而调用的所有后继 Servlet 都不会更改该参数。直到 Servlet 调用了 destroy() 方法后，才能重新设置初始化参数。例如，如果另一个 SHTML 文件指定了另一个不同的初始化参数值，虽然已此时已装入了 Servlet，但该值仍将被忽略。

    4．在 JSP 文件中调用 Servlet

可以从 JavaServer 页面（JSP）文件中调用 Servlet。请参阅JSP技术部分。

5．在 ASP 文件中调用 Servlet

    如果在 Microsoft Internet Information Server（IIS）上有遗留的 ASP 文件，并且无法将 ASP 文件移植成 JSP 文件时，可用 ASP 文件来调用 Servlet。在 WebSphere应用服务器 中的 ASP 支持包括一个用于嵌入 Servlet 的 ActiveX 控制，下面介绍ActiveX 控制AspToServlet 的方法和属性。

该方法说明如下：

    (1) String ExecServletToString(String servletName)；执行 ServletName，并将其输出返回到一个字符串中。

    (2) ExecServlet(String servletName)；执行 ServletName，并将其输出直接发送至 HTML 页面。

     (3) String VarValue(String varName)；获得一预置变量值（其它格式）。

     (4) VarValue(String varName, String newVal)；设置变量值。变量占据的总大小应小于 0.5 个千字

节（Kbyte）。且仅对配置文件使用这些变量。

    其属性如下：

    = Boolean WriteHeaders；若该属性为真，则 Servlet 提供的标题被写入用户处。缺省值为假。

    = Boolean OnTest；若该属性为真，服务器会将消息记录到生成的 HTML 页面中。缺省值为假。

下列ASP 脚本示例是以 Microsoft Visual Basic Scripting（VBScript）书写的。

<%

    ' Small sample asp file to show the capabilities of the servlets and the ASP GateWay ...

%>

<H1> Starting the ASP->Java Servlet demo</H1>

<%

    ' Create a Servlet gateway object and initialize it ...

    Set javaasp = Server.CreateObject("AspToServlet.AspToServlet")

    ' Setting these properties is only for the sake of demo.

    ' These are the default values ...

    javaasp.OnTest = False

    javaasp.WriteHeaders = False

    ' Add several variables ...

    javaasp.VarValue("gal") = "lag"

    javaasp.VarValue("pico")= "ocip"

    javaasp.VarValue("tal") = "lat"

    javaasp.VarValue("paz") = "zap"

    javaasp.VarValue("variable name with spaces") = "variable value with spaces"

%>

<BR>

Lets check the variables

<%

    Response.Write("variable gal = ")

    Response.Write(javaasp.VarValue("gal"))

%>

<BR>

<%

    Response.Write("variable pico = " & javaasp.VarValue("pico"))

%>

<BR>

<HR>

<%

    galout = javaasp.ExecServletToString("SnoopServlet")

    If javaasp.WriteHeaders = True Then

%>

Headers were written <%

    Else

%>

Headers were not written <%

    End If

    Response.Write(galout)

%>

<H1> The End ...</H1>


WebSphere快速入门(13)

作者：easyinfonet

email: easyinfonet@sina.com.cn

日期：2001-2-16 11:34:11

第五章 JSP技术

5.1 JSP简介

     JSP（JavaServer Pages）是一种基于Java的脚本技术。在JSP 的众多优点之中，其中之一是它能将 HTML 编码从 Web 页面的业务逻辑中有效地分离出来。用 JSP 访问可重用的组件，如 Servlet、JavaBean 和基于 Java 的 Web 应用程序。JSP 还支持在 Web 页面中直接嵌入 Java 代码。可用两种方法访问 JSP 文件：浏览器发送 JSP 文件请求、发送至 Servlet 的请求。

    1. JSP 文件访问 Bean 或其它能将生成的动态内容发送到浏览器的组件。图5-1说明了该 JSP 访问模型。当 Web 服务器接收到一个 JSP 文件请求时，服务器将请求发送至 WebSphere应用服务器。WebSphere应用服务器 对 JSP 文件进行语法分析并生成 Java 源文件（被编译和执行为 Servlet）。Java 源文件的生成和编译仅在初次调用 Servlet 时发生，除非已经更新了原始的 JSP 文件。在这种情况下，WebSphere应用服务器 将检测所做的更新，并在执行它之前重新生成和编译 Servlet。

图5-1：浏览器发送 JSP 文件请求

    2. 发送至 Servlet 的请求生成动态内容，并调用 JSP 文件将内容发送到浏览器。图5-2说明了该访问模型。该访问模型使得将内容生成从内容显示中分离出来更为方便。WebSphere应用服务器 支持 HttpServiceRequest 对象和 HttpServiceResponse 对象的一套新方法。这些方法允许调用的 Servlet 将一个对象放入（通常是一个 Bean）请求对象中，并将该请求传递到另一个页面（通常是一个 JSP 文件）以供显示。调用的页面从请求对象中检索 Bean, 并用 JSP 来生成客户机端的 HTML。

图5-2：发送至 Servlet 的请求

5.2 JSP示例

    浏览器通过一个Web 页面中的HTML表单请求一个servlet（PopulateBeanServlet），该servlet创建一个名为 dataBean 的DataBean 实例，并调用 JSP 文件将内容发送到浏览器。Servlet 示例和 JSP 文件示例说明了启用内容分离的 JSP 访问模型。

A.    Servlet 是由下列Web 页面中的 HTML 表单来调用的。

<HTML>

<BODY>

<H1>运行 PopulateBeanServlet</H1>

<P>您是否希望运行 PopulateBeanServlet?

<FORM action="/servlet/PopulateBeanServlet" method="GET">

<INPUT type="SUBMIT" value="Yes">

<INPUT type="SUBMIT" value="No">

</FORM>

</BODY>

</HTML>

B.    被请求的servlet为PopulateBeanServlet， 其源代码如下：

/******************************************************************

*Servlet 示例：PopulateBeanServlet.java

*这个servlet创建一个名为 dataBean 的DataBean 实例，设置dataBean的若干个属性，

*将dataBean放置在当前“请求”对象中，

*调用 JSP 文件（DisplayData.jsp）来格式化并显示dataBean的数据

********************************************************************/

import java.io.*;

import java.beans.Beans;

import javax.servlet.*;

import javax.servlet.http.*;

import javax.ejb.CreateException;

public class PopulateBeanServlet extends HttpServlet {

   public void Service(HttpServletRequest req, HttpServletResponse res)

       throws ServletException, IOException {

   try {

         dataBean = (DataBean) Beans.instantiate(this.getClass().getClassLoader(), "DataBean");

      }

   catch (Exception ex) {

         throw new ServletException("Can't create BEAN of class DataBean: "

              metaData.setSQL(getSQLString());

   }

   // Set some Bean properties (content generation)

   dataBean.setProp1("Value1");

   dataBean.setProp2("Value2");

   dataBean.setProp3("Value3");

   // To send the Bean to a JSP file for content formatting and display

   // 1) 将dataBean放置在当前“请求”对象中，

   ((com.sun.server.http.HttpServiceRequest) req).setAttribute("dataBean", dataBean);

   // 2) 使用callPage 方法调用JSP文件，文件名为DisplayData.jsp，并把请求对象传递给JSP。

   ((com.sun.server.http.HttpServiceResponse) res).callPage("/DisplayData.jsp", req);

 } //end of service mehtod

} /* end of class PopulateBeanServlet */

C.    被调用的JSP文件为DisplayData.jsp，其内容如下：

<!-- 该 JSP 文件获得在请求对象中传递的 dataBean，并显示该 Bean 的属性。 -->

<html>

<head>

<title>Bean Data Display</title>

</head>

<!-- Get the Bean using the BEAN tag   -->

<bean name="dataBean" type="DataBean" introspect="no" create="no" scope="request">

</bean>

<body>

<!-- There are three ways to access Bean properties -->

<!--    Using a JSP scriptlet  -->

<% out.println("The value of Bean property 1 is " + dataBeans.getProp1());

%>

<!-- Using a JSP expression -->

<p>The value of Bean property 2 is

<%= dataBean.getProp2()  %> </p>

<!--Using the INSERT tag   -->

<p>The value of Bean property 3 is

<insert bean=dataBean property=prop3 default="No property value" >

</insert></p>

</body>

</html>


WebSphere快速入门(14)

作者：easyinfonet

email: easyinfonet@sina.com.cn

日期：2001-2-16 11:35:11

5.3 JSP 语法

    JSP 文件（扩展名为 .jsp）可以包含指令（或称为指示语句）、Class-wide 变量和方法、直接插入的 Java 代码（scriptlet）、访问 JavaBean、变量数据的 HTML 模型、变量数据的 Java 表达式的组合。

1．JSP 指令

    使用 JSP 指令（在<%@ 和 %>内的）来指定所使用的脚本语言、Servlet 实现的接口、Servlet 扩展的类、Servlet 导入的软件包。JSP 指令的一般语法形式为：<%@ 指令名 ="值" %>。其中有效的指令名为：

    (1) language：文件中所使用的脚本语言。此时对于 Java 程序设计语言来说，仅 java 为有效值和缺省值。该指令作用于整个文件。当多次使用该指令时，只有第一次使用是有效的。示例如下：<%@ language ="java" %>。

    (2) Method：由嵌入的 Java 代码（scriptlet）生成的方法的名称。生成的代码会成为指定方法名的主体。缺省的方法是service。当多次使用该指令时，只有第一次使用是有效的。示例如下：<%@ method ="doPost" %>。

    (3) Import：Servlet 导入的 Java 语言软件包名或类名列表，该列表是用逗号分隔的。在 JSP 文件中，可以多次指定该指令来导入不同的软件包。示例如下：<%@ import ="java.io.*,java.util.Hashtable" %>。

    (4) content_type ：生成的响应的 MIME 类型。缺省值为 text/html。当多次使用该指令时，只有第一次使用是有效的。 该指令可用以指定在其中对页面进行编码的字符集。示例如下：<%@ content_type ="text/html; charset=gb2312" %>。

    (5) mplements：用于生成 Servlet 实现的 Java 语言接口列表，该列表是用逗号分隔的。可以在一个 JSP 文件中多次使用该命令，以实现不同的接口。示例如下：<%@ implements = "javax.servlet.http.HttpSessionContext" %>。

    (6) extends：Servlet 扩展的 Java 语言类的名称。该类必须是有效的，且不能是一个 Servlet 类。该指令作用于整个 JSP 文件。当多次使用该指令时，只有第一次使用是有效的。示例如下：<%@ extends ="javax.servlet.http.HttpServlet" %>。

    2．class-wide 变量和方法

    使用 <SCRIPT> 和 </SCRIPT> 标记来说明 Servlet 类的 class-wide 变量和 class-wide 方法。一般语法如下：

<script runat=server>。

 // class-wide 变量和方法的代码

 </script>

属性 runat=server 是必需的，它表明该标记是用于服务器端处理的。指定 class-wide 变量和方法的示例如下：

<script runat=server>

// class-wide 变量

init i = 0;

String foo = "Hello";

// class-wide 方法

private void foo() {

      // 该方法的代码

}

</script>

    3．访问 JavaBean

    JSP 对 JavaBean 的支持使您能通过 Web 站点重复使用组件。JavaBean 可以是类文件或串行化 Bean，也可由 Servlet 动态生成。请使用 <BEAN> 标记来创建一个 Bean 实例，以使从 JSP 文件的任何地方都可对该实例进行访问。标记 <BEAN> 的语法为：

<bean name="Bean_name" varname="local_Bean_name"

   type ="class_or_interface_name" introspect="yes|no"

   beanName="ser_filename" create="yes|no"

   scope="request|session|userprofile" >

   <param property_name="value">

</bean>

    其中的属性及其含义如下：

    (1) name：用于在相应的范围（由 scope 属性指定）内查找 Bean 的名称。例如，这可能是用于存储 Bean 的会话（session）关键值。该值是分大小写的。

    (2) varname：用于在 JSP 文件中的任何地方引用 Bean 的名称。该属性是可选的。缺省值为 name 属性值。该值是分大小写的。

    (3) type：Bean 的类文件名。该名称用于以代码说明 Bean 实例。缺省值为 Object 类型。该值是分大小写的。

    (4) Introspect：当该值为真时，JSP 处理器检查将所有的请求属性，并调用与请求属性匹配的 set 属性方法集（该属性是在 BeanInfo 中传递的）。该属性的缺省值为是。

    (5) BeanName：Bean 的 .class 文件名、Bean 软件包名或包含 Bean 的串行化文件（.ser 文件）的文件名。（该名称是给 Bean 实例程序的）。仅当 Bean 不在指定的范围中，且创建属性被设置成是时，才使用该属性。该值是分大小写的。文件路径必须在应用服务器的Java 类路径中指定，除非文件在 applicationserver_root\servlets 目录下。

     (6) Create：当该值为真时，如果处理器在指定的范围内没有找到 Bean，则 JSP 将创建一个 Bean 实例。缺省值为真。

     (7)Scope：Bean 的生命期。该属性是可选的，其缺省值为 request 。有效值为：

      = request - 由 Servlet 将 Bean 设置成请求的上下文，该 Servlet 使用 JSP API 中描述的 API 调

用 JSP 文件。如果 Bean 不是请求上下文的一部分，则将创建该 Bean，并将其存储在请求上下文中，除非创建属性已设置为否。

 = session - 如果当前对话中存在 Bean，则 Bean 已被重用。如果不存在 Bean，且创建属性设置

为是，则它已被创建并作为会话的一部分存储起来。

 = userprofile - 由 Servlet 请求对象检索、造型成指定的类型，并进行内省的用户简要表文件。（在

IBM WebShere应用服务器中，缺省类型为 com.ibm.servlet.personalization.userprofile.UserProfile）。

    (8) param：属性和值配对的列表。属性是在 Bean 中用内省自动设置的。只在实例化 Bean 时，才对属性进行设置。

    除了使用 <param> 属性来设置 Bean 属性外，还可以使用其它三种方法：第一，在请求包含 Bean 的 Web 页面（JSP 文件）的 URL 时，指定查询参数。必须将内省属性设置成“是”。其示例如下：http://www.myserver.com/signon.jsp?name=jones&password=d13xBean 属性名将设置为 jones。第二种方法，将属性指定成由 HTML <FROM> 标记提交的参数。必须将 mothod 属性设置成 post。将 action 属性设置成调用 Bean 的 JSP 文件的 URL。必须将 introspect 属性设置成“是”。其示例如下：

<form action="http://www.myserver.com/SearchSite"method="post">

  <input type="text" name="Search for: ">

  <input type="submit">

</form>

第三中方法是使用 JSP 语法设置 Bean 属性。

    在指定了 <BEAN> 标记后，就可以从 JSP 文件中的任何地方访问 Bean。这里有三种访问 Bean 属性的方法：使用 JSP scriptlet、使用 JSP 表达式、使用 <INSERT> 标记（如同 变量数据的 HTML 模板语法中所说明的）。请参阅JSP示例（DisplayData.jsp 文件）以获取三个访问 Bean 方法的每个方法示例。

    4．直接插入的 Java 代码（scriptlet）

    可以在 JSP 文件的 <% 和 %> 标记间直接嵌入任何有效的 Java 语言代码。这样嵌入的代码称为 scriptlet。如果没有指定 method 指令，则生成的代码将成为 service 方法的主体。用于 Servlet 的 scriptlet 可以使用一个预定义的变量集，该变量集符合基本的 Servlet、输出和输入类：

    (1) request：由 javax.servlet.http.HttpServletRequest 定义的 Servlet 请求类

     (2) responses：由 javax.servlet.http.HttpServletRequest 定义的 Servlet 响应类

     (3) out：由 java.io.PrintWriter 定义的输出转录程序类

     (4) in：由 java.io.BufferedReader 定义的输入阅读程序类

其示例如下：

<%

foo = request.getParameter("Name");

out.println(foo);

%>

    5．变量数据的 HTML 模板语法

    当页面被返回给浏览器时，应用服务器HTML 模板语法使您能将变量字段放在 HTML 页面上，并使 Servlet 和 JavaBean 可利用数据库的值动态地替换变量。该功能是 JSP 的一个 IBM 扩展，它使引用变量数据变得十分容易。该语法只能用于 JSP 文件中。HTML 模板语法包括：

    = 基本 HTML 模板语法；

    = 替代 HTML 模板语法；

    = <REPEAT>标记。

    这些标记通过 HTML 编写工具被设计成传递交互操作的标记。每一个标记有一个相应的结束标记。每一个标记是分大小写的，它们的一些属性也是分大小写的。IBM WebSphere Studio 使开发 JSP 文件以包含 HTML 模板语法变得更为容易。

    (1) 基本 HTML 模板语法

<INSERT> 标记是用于指定变量字段的基本标记。一般的语法为：

<insert requestparm=pvalue requestattr=avalue bean=name

  property=property_name(optional_index).subproperty_name(optional_index)

  default=value_when_null>

</insert>

    其中的属性及其含义如下：

   = requestparm：要在请求对象内进行访问的参数。该属性是分大小写的，并且不能与Bean 和property属性一起使用。

   = Requestattr：要在请求对象内进行访问的属性。属性应使用 setAttribute 方法设置。该属性是分大小写的，并且不能与 Bean 和property属性一起使用。

   = Bean：由 <BEAN> 标记在 JSP 文件中说明的 JavaBean 名。请参阅访问 JavaBean 以获得 <BEAN> 标记的解释。该属性的值是分大小写的。当指定了 Bean 属性，但未指定property属性时，在替换中将使用完整的 Bean 。例如，如果 Bean 是类型 String 且未指定属性，则将替代 string 的值。

   = Property：访问替换的 Bean 的属性。该属性的值是分大小写的，并且是属性的独立场所名。该属性不能与 requestparm 属性一起使用。

   = Default：当 Bean 属性值为空时，显示可选的字符串。如果字符串包含多个字，则该字符串必须包含在一对双引号中（例如 "HelpDesk number"）。该属性的值是分大小写的。如果未指定值，则当属性值为空时，用空字符串替代。

基本语法的示例如下：

<insert bean=userProfile property=username></insert>

<insert requestparm=company default="IBM Corporation"></insert>

<insert requestattr=ceo default="Company CEO"></insert>

<insert bean=userProfile property=lastconnectiondate.month></insert>

在大多数情况下，property属性的值就是属性名。但是，可以通过指定property属性的全格式来访问属性的某一属性（子属性）。这个全格式也提供选择项给您来指定索引属性的一个索引。该可选的索引可以是一个常数（例如 2）或如重复设置 HTML 标记中描述的索引。使用属性标记的全格式示例如下：

<insert bean=staffQuery property=address(currentAddressIndex)></insert>

<insert bean=shoppingCart property=items(4).price></insert>

<insert bean=fooBean property=foo(2).bat(3).boo.far></insert>

    (2) 替代 HTML 模板语法

    HTML 标准不允许在 HTML 标记中嵌入 HTML 标记。因而，就无法在另一个 HTML 标记中嵌入<INSERT>标记。作为代替，请使用 HTML 模板替代语法。要使用替代语法：

   = 请使用<INSERT>和</INSERT>来包含 HTML 标记，在该标记中指出替代内容。

   = 指定 Bean 和property属性：

   &frac34; 要指定 Bean 和属性特性，请使用下列格式：$(bean=b property=p default=d)，其中 b、p 和 d

作为描述基本语法的值。

   &frac34; 要指定 requestparm 属性，请使用下列格式：$(requestparm=r default=d)，其中 r 和 d 作为

描述基本语法的值。

   &frac34; 要指定 requestattr 属性，请使用下列格式：$(requestattr=r default=d)，其中 r 和 d 作为描述

基本语法的值。

替代 HTML 模板语法的示例如下：

<insert>

  <img src=$(bean=productAds property=sale default=default.gif)>

</insert>

<insert>

  <a href="http://www.myserver.com/map/showmap.cgi?country=$ requestparm=countrydefault=usa)

  &city$(requestparm=city default="Research Triangle Park")&email=

     $(bean=userInfo property=email)>Show map of city</a>

</insert>

   (3) <REPEAT>标记

    <REPEAT>标记的语法为：

<repeat index=name start=starting_index end=ending_index>

</repeat>

其中：

    = index：是用于标识该重复数据块的一个可选的名称。该值是分大小写的。

    = Start：是用于该重复数据块的一个可选的开始索引值。缺省值为 0 。

    = End：是用于该重复数据块的一个可选的结束索引值。最大值是 2,147,483,647。如果结束属性的值小于开始属性的值，则忽略结束属性。

    下面的示例 1、2 和 3 显示了如何使用<REPEAT>标记。如果所有的索引属性拥有 300 个或更少的元素，则这些示例将产生相同的输出。如果拥有的元素多于 300 个，示例 1 和示例 2 将显示所有的元素，而示例 3 将只显示前 300 个元素。示例 1 用缺省开始和结束索引显示了隐式索引：使用最小索引属性数的 bean 限制了循环重复的次数。

<table>

<repeat>

  <tr><td><insert bean=serviceLocationsQuery property=city></insert></tr></td>

  <tr><td><insert bean=serviceLocationsQuery property=address></insert></tr></td>

  <tr><td><insert bean=serviceLocationsQuery property=telephone></insert></tr></td>

</repeat>

</table>

示例 2 显示了索引、开始索引和结束索引：

<table>

<repeat index=myIndex start=0 end=2147483647>

  <tr><td><insert bean=serviceLocationsQuery property=city(myIndex)></insert></tr></td>

  <tr><td><insert bean=serviceLocationsQuery property=address(myIndex)></insert></tr></td>

  <tr><td><insert bean=serviceLocationsQuery property=telephone(myIndex)></insert></tr></td>

</repeat>

</table>

示例 3 用隐式开始索引显示了显式索引和结束索引。虽然指定了索引属性，仍可对索引过的属性城市进行隐式索引，因为不需要（i）。

<table>

<repeat index=myIndex end=299>

  <tr><td><insert bean=serviceLocationsQuery property=city></insert></tr></td>

  <tr><td><insert bean=serviceLocationsQuery property=address(myIndex)></insert></tr></td>

  <tr><td><insert bean=serviceLocationsQuery property=telephone(myIndex)></insert></tr></td>

</repeat>

</table>

可以嵌套<REPEAT>数据块。独立索引每个数据块。该能力对交叉两个 bean 上的交错属性或含有子属性的属性非常有用。在示例中，将两个<REPEAT>数据块嵌套，用以显示用户购物手推车上每一张压缩光盘上的歌曲列表。

<repeat index=cdindex>

  <h1><insert bean=shoppingCart property=cds.title></insert></h1>

    <table>

  <repeat>

    <tr><td><insert bean=shoppingCart property=cds(cdindex).playlist></insert>

    </td></tr>

    </table>

</repeat>

</repeat>

    6．变量数据的 Java 表达式

    当处理 JSP 文件时，要指定分解一个 Java 语言表达式，请使用 JSP 表达式标记 <%= 和 %>。该表达式被评估和转换成一个字符串，并加以显示。原语类型，如 int 和 float 都自动转换成字符串表示法。在该示例中，foo 是在 <SCRIPT> 中加以说明的 class-wide 变量，示例请见 Class-wide 变量和方法：

    <p>翻译欢迎辞<%= foo %>.</p>

    当获得 JSP 文件时，文本为：翻译欢迎辞 Hello。


WebSphere快速入门(15)

作者：easyinfonet

email: easyinfonet@sina.com.cn

日期：2001-2-16 11:35:57

5.4 JSP API

    有两种接口支持 JSP 技术。这些 API 提供了一种将内容生成（业务逻辑）从内容演示（HTML 格式）中分离出来的方法。这种分离使 Servlet 能生成内容并将它（如在 Bean 中）存储在请求的上下文中。生成上下文的 Servlet 通过将请求的上下文传递至 JSP 文件来生成一个响应，该 JSP 文件包含 HTML 格式。<BEAN> 标记便提供了对业务逻辑的访问。支持 JSP 的接口有：

    = com.sun.server.http.HttpServiceRequest：该类实现了 javax.servlet.http.HttpServletRequest 接口和用于设置根据名称定义的属性的方法 setAttribute() 。

    = com.sun.server.http.HttpServiceResponse：该类实现了 javax.servlet.http.HttpServletResponse 接口，并添加了一个使 Servlet 能调用 JSP 文件和可选地传递上下文的 callPage() 方法。

    1．callPage() 方法

    用 callPage() 方法在 Servlet 中对 JSP 进行服务。所服务的页面（一个 JSP 文件）作为响应返回到浏览器中。调用 Servlet 还可以通过请求对象传递某些上下文。应该对所服务的页面标题进行编码，以将用于告诉浏览器不要对文件执行高速缓存的指令包含在内。callPage() 方法的语法如下：

public void callPage(String fileName, HttpServletRequest req) throws ServletException, IOException

其中：

   = fileName：用于标识文件（该文件用于生成输出并表示内容）的 URL 名。如果文件名以斜杠（/）开头，则可以假设文件位置与文档根目录有关。如果文件名不是以斜杠开头，则可假设文件位置与当前调用的请求有关。callPage() 方法不支持调用文件扩展名为 .html 的页面。如果需要使用 callPage() 方法来调用 HTML 页面，则必须首先将 HTML 文件重命名成扩展名为 .jsp 的文件。

   = Req：调用该方法的是 Servlet 的 HttpServletRequest 对象。最通常的是，将上下文作为 Bean，在请求对象的上下文中进行传递。

要使用 callPage() 方法，必须将响应对象造型成com.sun.server.http.HttpServiceResponse类型。

    2．使用 setAttribute() 方法

    用 setAttribute() 方法来将一个特性存储在请求的上下文中。语法是：public void setAttribute(String key, Object o),其中，key代表存储的特性名，而o表示用 key 来存储的上下文对象。要使用 setAttribute() 方法，必须将请求对象造型成com.sun.server.http.HttpServiceRequest类型。


WebSphere快速入门(16)

作者：easyinfonet

email: easyinfonet@sina.com.cn

日期：2001-2-23 9:33:56

6.1 基本特性

WebSphere应用服务器使您能够为Servlet 的开发实现 “一次写成，各处使用”的目的。该产品包括一个基于 Java 的 Servlet 引擎，独立于 Web 服务器和它所基于的操作系统。WebSphere应用服务器提供了服务器插件的选项，与大多数流行的应用程序设计接口（API）兼容。所支持的 Web 服务器有:

    l IBM HTTP 服务器

    l Apache Server

    l Domino

    l Lotus Domino Go Webserver

    l Netscape Enterprise Server

    l Netscape FastTrack Server

    l Microsoft Internet Information Server

    除了 Servlet 引擎及插件外，WebSphere应用服务器还提供：

    1. JavaSoft Java Servlet API 实现，以及这些 API 的扩展和附加。

    2. 示例和文档，示例 Servlet 和 Web 站点应用程序演示了简单和高级技术。联机和可打印文档供您

快速入门，并帮助掌握服务器高级功能的使用演示基本类及其扩展的示例应用程序。

    3. WebSphere应用服务器的管理器，使用图形界面，易于:

   (1) 为装入本地和远程的 Servlet 设置选项

   (2) 设置初始化参数

   (3) 管理 Servlet

   (4) 指定 Servlet 别名

   (5) 创建 Servlet 链和过滤

   (6) 启用轻量级目录访问协议（LDAP）目录支持

   (7) 记录 Servlet 消息

   (8) 启用 JVM 调试

   (9) 监控 WebSphere应用服务器 使用的资源

  (10) 监控已装入 Servlet、活动 Servlet 会话和 JDBC 连接

  (11) 控错误、事件、异常情况和日志输出

  (12) 创建转储和数据快照

  (13) 动态地启用和禁用跟踪

    4. 缓存和再使用与 JDBC-从属数据库连接的连接管理功能。当一个 Servlet 需要数据库连接时，它

可从可用连接的缓冲池获得，从而消除了为每个请求打开一个新连接的所需花费的系统开销。

    5. 附加的 Java 类，遵循 JavaBeans 规范，允许程序员访问 JDBC-从属数据库。当隐藏使用关系数

据库的复杂度时，这些数据访问 Bean 可提供增强功能。它们可用于可视集成开发环境中。

    6. JSP的动态页面内容支持。JSP 技术使您能够通过服务器端脚本生成动态 Web 页面。其结果是将

呈示逻辑（例如，定义 Web 站点结构和外观的 HTML 代码）从业务逻辑（例如，访问数据库以获得要显示在 Web 站点上的信息的 Java 编码信息）中分隔出来。灵活性方面，JSP 文件可包括任何直接插入的 Java 的组合：<SERVLET> 标记、NCSA 标记和 JavaBean。

    7. 启用 LDAP（轻量级目录访问协议）支持的目录服务。

    8. 用于集成WebSphere应用服务器和Web服务器（如Apache Server） 到Tivoli 管理环境（Tivoli

Management Environment）以获得分布式监控和操作的模块和命令行界面。Tivoli 模块并不与WebSphere应用服务器一起发行，请访问Web 站点（http://www.software.ibm.com/webservers/appserv/

    WebSphere应用服务器对JSP的支持是通过JSP处理器来实现的。在 Web 服务器上安装 WebSphere应用服务器时，Web 服务器的配置被设置成将对 JSP 文件（即文件扩展名为 .jsp）的 HTTP 请求传递至 WebSphere应用服务器。WebSphere应用服务器配置则被设置成将 JSP 文件传递至其 JSP 处理器（pageCompile）。

    JSP 处理器对每一个 JSP 文件创建并编译 Servlet。该处理器还为每个 JSP 文件产生两个文件：

    (1)  java 文件：包含可用于 Servlet 的 Java 语言代码；

        (2)  class 文件：编译过的 Servlet。

JSP 处理器把 .java 和 .class 文件放在目录 servlets\pagecompile\JSP_file_path 下，其中 JSP_file_path 是 JSP 源文件所在的目录路径。例如，若JSP 文件在WebSphere\AppServer\samples\Web

Bank 目录下，则.java 和.class 文件就在WebSphere\AppServer\servlets\pagecompile\WebSphere\App Server\

samples\WebBank 目录下。.java 和 .class 文件的文件名相同。处理器使用了命名约定，该命名约定包括将下划线字符和后缀添加到 JSP 文件名中。例如，如果 JSP 文件名是 login.jsp，则生成的文件为 _login_xjsp.java 和 _login_xjsp.class。

如同所有的 Servlet，由 JSP 文件生成的 Servlet 是 javax.servlet.http.HttpServlet的子类或子孙类。如果 Servlet 类是软件包的一部分，则 Servlet Java 代码包含了用于一些必需类和软件包语句的导入语句。如果 JSP 文件包含 JSP 语法（例如指令和 scriptlets），则 JSP 处理器会将 JSP 语法转换成等价的 Java 代码。如果 JSP 文件包含 HTML 标记，则处理器添加 Java 代码，以使 Servlet 能一个接一个字符地输出 HTML。


WebSphere快速入门(17)

作者：easyinfonet

email: easyinfonet@sina.com.cn

日期：2001-2-23 9:35:20

6.2 安装与配置

对于任何软件，都需要一些计划和具体步骤以确保成功安装。对于安装与配制 WebSphere应用服务器及其组件也是如此。下面介绍在Windows NT 上安装与配置WebSphere应用服务器

    1．内存和软件要求

(1) 内存：至少 128 MB RAM，建议 256 MB。WebSphere应用服务器安装光盘包括 Java 开发工具（JDK）。

(2) 操作系统：Microsoft NT 版本 4.0 与服务包3 。

         (3) Web 服务器：WebSphere应用服务器需要下列服务器之一。

l IBM HTTP 服务器版本 1.3.3 Windows NT 版（WebSphere应用服务器安装光盘包括IBM HTTP 服

务器）

l Apache Server 版本 1.3.2 Windows NT 版

l Domino 版本 5.0 Windows NT 版

l Lotus Domino Go Webserver 版本 4.6.2.5 Windows NT 版

l Microsoft Internet Information Server 版本 3.x 和 版本 4.0 Windows NT 版

l Netscape Enterprise Server 版本 3.01 和 版本 3.51 Windows NT 版（建议使用版本 3.5.1）

l Netscape FastTrack Server 版本 3.01 Windows NT 版

    (4) Java 开发组件（JDK）：建议使用与 WebSphere应用服务器一起提供的 JDK1.1.6。

    (5) Java servlet API：WebSphere应用服务器包括 JSDK 版本 2.0 或更高版本。

    (6) Web 浏览器：WebSphere应用服务器的管理器是用来管理 servlet 的用户界面。要运行管理器，

需要 appletviewer 或支持 JDK1.1 的浏览器，例如：

l Netscape Navigator 4.06 或 4.0.7，包括集成的 JDK 1.1 支持并由以下 Web 站点提供：

http://developer.netscape.com/software/jdk/download.html

l 或带有修正包的 Microsoft Internet Explorer 4.01 或更高版本。

l Sun HotJava 1.1 或更高版本

一些旧的浏览器不能够正确地处理启用本机语言支持的文本。如果在用户界面上看到外来字符，例如“sEnable”，而不是“Enable”，可以通过升级浏览器校正。

    2．安装Web服务器

如果计划安装 IBM HTTP 服务器，请在安装 WebSphere应用服务器之前安装它。可以从 WebSphere应用服务器安装光盘安装 IBM HTTP 服务器。安装WebSphere应用服务器会更改 Web 服务器 httpd.conf 文件。如果安装 Web 服务器是在安装 WebSphere应用服务器之后安装的，将不会进行更改，并且 WebSphere应用服务器将不能够正确运行。安装 IBM HTTP 服务器之后，需要一些配置以启用 SSL 支持。请参阅 IBM HTTP 服务器文档以获得指示信息。

对于计划安装其它的Web 服务器（如Apache Server），也请在安装 WebSphere应用服务器之前安装它。理由同上。

    3．安装 WebSphere应用服务器之前

    安装WebSphere应用服务器版本 2.0之前，请卸装所有以前的版本。卸装之前，备份 WebSphere应用服务器版本 1.x 文件。

(1) 文件备份

从Windows NT 卸装以前版本的 WebSphere应用服务器之前，确保要移植的文件已经或者将要保存。安装 WebSphere应用服务器版本 2.0 时显示出的图形用户界面备份 WebSphere应用服务器目录中的文件，包括类、领域、Servlet、属性文件，其中，属性文件包括servlet.properties、admin_port.properties、rules.properties、jvm.properties、aliases.properties、connmgr.properties、userprofile.properties。如果有文件驻留在这四个目录之外（例如，如果在 WebSphere应用服务器 安装中创建自己的目录），请在安装 WebSphere应用服务器 版本 2.0. 之前，在 WebSphere应用服务器 安装之外的位置备份文件。

此处包括移植进程的第一部分。第二部分必须在安装 WebSphere应用服务器版本 2.0 之后执行。请参阅有关文档以获得详细信息。

(2) 卸载前一版本

对于 Windows NT，使用开始 --> 控制面板中的添加/删除选项，或从开始 --> 程序 --> IBM WebSphere --> WebSphere应用服务器版本1.x 卸装。

注意：当已安装了某版本的 WebSphere应用服务器，它将复制Web 站点配置文件作为备份文件，然后修改原始配置文件。当使用 Web 服务器时这个已被修改过的文件就成为活动的配置文件。当卸装 WebSphere应用服务器时，不会恢复以前的配置文件，它仍然是备份文件。为了使这些设置再次活动，必须将它们从备份文件转换为活动的 Web 服务器配置文件。

另外，需要清除 CLASSPATH。如果在 CLASSPATH 中已经有来自以前版本的 WebSphere应用服务器的信息，安装版本2.0之前请删除这些信息。对于在 Windows NT 上使用Go Webserver 的Web服务器来说，当安装 WebSphere应用服务器时能自动卸装Go Webserver 上的 Java 支持。其它的请查阅有关文档。

    4．安装WebSphere应用服务器

在即将安装 WebSphere应用服务器之前，请确保已经：

(1) 备份所有未通过安装程序自动备份的文件（从以前的 WebSphere应用服务器进行安装）。

(2) 安装您计划使用的 Web 服务器。Web 服务器必须在安装 WebSphere应用服务器之前安装。

(3) 如果 Web 服务器正在运行，请停止它。

注意：在WebSphere应用服务器的安装期间，如果指定使用 IBM HTTP 服务器或 Apache Server，将提示您确认 Web 服务器 httpd.conf 文件的位置。

安装在 Windows NT 上，插入 WebSphere应用服务器安装光盘，转至以 Windows NT 操作系统命名的子目录，运行可执行安装程序（setup.exe）。一系列面板将指导您完成安装。

5．配置 WebSphere应用服务器

下面介绍启用 WebSphere应用服务器和它的组件以使之协同工作的必要配置。完成这些任务之后，WebSphere应用服务器便能够主要通过使用缺省设置运行所有功能。

(1) 配置 Apache Server

如果使用Apache Server作为Web服务器，请确保httpd.conf 文件包含此行：AddModule mod_app_server.c。

(2) 使用数据库。

要确保 WebSphere应用服务器 维护和使用与关系数据库（如 Oracle 或 DB2）的连接，请添加数据库.zip 文件到位于文件 <as_root >/properties/bootstrap.properties 中的 java.classpath 属性，或使用 WebSphere应用服务器的管理器界面的 Java 引擎页面来指定文件。同样，确保 java.classpath 包含用于数据库连接的有效的驱动程序。请查看产品 Web 站点上的 WebSphere应用服务器 版本 2.0 自述文件可得到附加的技巧。

(3) 运行模式

ose.mode 属性控制 WebSphere应用服务器是作为 Web 服务器的一部分（进程内），还是在独立模式下运行（进程外）。该属性位于 <as_root> /properties/bootstrap.properties 文件中。对于所有的服务器，ose.mode 缺省值是 out。如果使用 Apache Server 或 IBM HTTP 服务器，必须设置 ose.mode 属性为 out，这意味着 WebSphere应用服务器在独立模式下运行。对于其它 Web 服务器，可以（但不建议）更改 ose.mode 为 in 并且作为 Web 服务器的一部分运行 WebSphere应用服务器。 无论何时如有必要请复位 ose.mode。例如，如果从作为 Webserver 一部分运行的 Netscape Enterprise Server （ose.mode=in）转换为 IBM HTTP 服务器（要求 ose.mode=out），不要忘记在运行 IBM HTTP 服务器之前，将 ose.mode 属性更改为 out。

与手工编辑包含 ose.mode 属性的 bootstrap.properties 文件相对比，管理器界面的 Java 引擎页面提供了一个简单的方法来锁住该属性值。 Java 引擎页面提供了一个可用来指示是以 Web 服务器的一部分（ose.mode=in）或以独立模式（ose.mode=out）运行 WebSphere应用服务器的单选按钮。请参阅有关文档学习如何访问管理器。

ose.mode 的值影响。作为 Web 服务器一部分运行 WebSphere应用服务器为Servlet 和其它应用程序提供较高的性能，但安全性较差。作为 Web 服务器的一部分运行 WebSphere应用服务器，允许当关闭 Web 服务器时 WebSphere应用服务器自动停止。在独立模式下运行 WebSphere应用服务器需要其它步骤。 请参有关文档获得更多的详细信息。

6．启动和停止 WebSphere应用服务器

当启动 Web 服务器时WebSphere应用服务器自动启动。WebSphere应用服务器的管理器，是通过 Web 浏览器访问的，为查看和更改 WebSphere应用服务器的设置和性能提供了界面。请参阅定制配置设置。

如果将 WebSphere应用服务器 作为 Web 服务器的一部分运行，当关闭 Web 服务器时 WebSphere应用服务器 将自动停止。如果以独立模式运行，WebSphere应用服务器不会自动停止。如果使用 Apache Server 或 IBM HTTP 作为Web服务器， WebSphere应用服务器必须在独立模式下运行。

当在 Windows NT 上进程外运行 WebSphere应用服务器 时，停止 Web 服务器之后，请停止 WebSphere Servlet 服务以停止 WebSphere应用服务器。从开始 --> 设置 --> 控制面板 --> 中选择 WebSphere Servlet 服务，并按“停止”按钮。

   7．安装的检查和故障寻找

要验证 WebSphere应用服务器已安装好并正确配置，可调用 WebSphere应用服务器提供的 snoop servlet。使用 Web 浏览器打开 servlet URL: http://your.server.name/servlet/snoopServlet 应回送客户机发送的 HTTP 请求及 servlet 的初始化参数。SnoopServlet 和其它 servlet 的代码位于<as_root>/servlets 目录。如果Servlet失败，请尝试下列步骤:

    (1) 如果已通过手工编辑 .properties 文件或使用 WebSphere应用服务器的管理器界面更改了任何 WebSphere应用服务器的配置设置，请检查这些文件以确保未引入任何非法的或不正确的值。特别要检查<as_root> /properties/bootstrap.properties 文件。文档中心包含关于手工配置这些属性的资料和关于每一配置的可接受性及缺省值的讨论资料。

    (2) 为Web 服务器打开本地日志和跟踪。找到 WebSphere应用服务器 bootstrap.properties 文件（在 <as_root> /properties 目录中）。设置 ose.trace.enabled 属性为true，设置 ose.trace.to.webserver 属性为true。停止 Web 服务器并重启动。记住如果 WebSphere应用服务器运行在独立模式，当停止 Web 服务器时它不会相应停止。检查 Web 服务器出错日志及 WebSphere应用服务器 <as_root>/logs 目录下的日志以查看错误。

    (3) 启用调试控制台并重新启动 Web 服务器。WebSphere应用服务器的调试控制台为收集和查看跟踪及监控数据提供了集中场所。例如，从调试控制台，可以作为一组启动和停止列在收集和监控服务器数据中的监控程序。调试控制台的服务器控制台标签允许查看 servlet 的 stdout 和 stderr 流。缺省情况下，调试控制台未启用。启用该控制台。在 WebSphere应用服务器 debug.properties 文件中设置 debug.server.console.enabled 属性为true并重新启动 Web 服务器使得改动生效。或者，运行 http://your.server.name/servlet/DebugConsoleServletWindows NT 上，要成功地查看调试控制台，必须配置 Windows NT 以允许一个或多个服务与 Windows 桌面交互。如果使用作为 Windows NT 服务运行的 Web 服务器：

l 选择开始 --> 设置 --> 控制面板 --> 服务。

l 选择 Web 服务器相应的服务。

l 单击启动按钮。

l 在结果对话框中，选择允许服务与桌面交互的复选框。

l 重新启动 Web 服务器以使更改生效。

对于 Microsoft Internet Information Server，对与 Web 服务器相关的每个服务（如 Web 发布和 FTP

服务），执行以上过程。这些服务必须允许与 Windows NT 桌面交互。如果 WebSphere应用服务器运行时未启动任何 Web 服务器相关的进程，则为 WebSphere Servlet 服务执行以上过程，允许服务与桌面交互。

8．从 版本 1.x 移植到版本 2.0

在安装 WebSphere应用服务器之后，通过下列步骤完成移植：

    (1) 请检查用户 Servlet、领域、类、JavaServer 页面（JSP）和其它在第一部分的移植过程中保存的与编程相关的文件已经被安放在它们所属的 WebSphere应用服务器 版本 2.0 目录中。安装程序应已为您完成了这些步骤。如果已备份了您创建在 WebSphere应用服务器 安装中用户文件或目录，请将它们安放在现在的新安装处。

    (2) 传输 jvm.properties 文件设置到 WebSphere应用服务器版本 2.0 bootstrap.properties 文件。

l 对于 WebSphere应用服务器 类路径，仅移植用户指定的 类路径部分到 bootstrap.properties 中 –

不要传输 JVM 库或 WebSphere应用服务器 版本 1.x .jar 文件路径，因为这些是 WebSphere应用服务器 版本 1.x 缺省配置的一部分，而您未曾自行设置过它们。换言之，只传输您添加的部分类路径。不要除去 WebSphere应用服务器 版本 2.0 缺省类路径；简单地加入 版本 1.x 项目即可。

l 对于 Java 库路径和路径，也只移动用户指定的部分。不要除去 WebSphere应用服务器 版本 2.0

缺省的库路径和路径设置；简单地加入 版本 1.x 项目即可。

l 对于其它属性，仅当它们有定义在 bootstrap.properties 中的对应者时才移植。

要使用 WebSphere应用服务器的管理器来管理在安装 WebSphere应用服务器之前已存在于 Web 服务器中的 Servlet，必须首先移植这些 Servlet。为了移植现有的 Servlet，将 Servlet 从它们目前的位置移动到 <as_root> \servlets 目录。WebSphere应用服务器 监控该目录并且当 Servlet 更改时自动重新装入 Servlet。如果有 Servlet 在其它目录中并且不想将它们移动到 <as_root> \servlets 目录，可使用管理器界面中 Java 引擎页面的“可重装 Servlet 类路径”字段来指定其余要监控的目录。使用 WebSphere应用服务器中的配置页面来重新配置以前的Servlet 参数。请参阅定制配置设置。


WebSphere快速入门(18)

作者：easyinfonet

email: zhujs@cec.gov.cn

日期：2001-2-23 9:36:04

6.3 定制配置

本节介绍如何启动和使用 WebSphere应用服务器的管理器（一个图形界面）为 Servlet 活动和 WebSphere应用服务器的组件定制基本设置参数。

   1．启动 WebSphere应用服务器的管理器

要启动 WebSphere应用服务器的管理器，在 Web 浏览器中输入 URL：http://your.server.name:9527/your.server.name 是主机的全限定名。注意：如果在安装了 WebSphere应用服务器的同一机器的浏览器中启动 WebSphere应用服务器的管理器，使用 http://localhost:9527/ 以获得更高性能。

管理器启动并显示登录页面。若是首次登录至管理器，请使用 admin 作为登录用户标识符和口令。单击“确定”。为安全起见，应该更改登录口令。要运行管理器，需要支持 Java 开发组件（JDK）1.1.6 的 appletviewer 或浏览器。请参阅准备安装 WebSphere应用服务器中的软件要求，查看选项。

    2．使用 WebSphere应用服务器的管理器

管理器左边的浏览区域允许: 为不同 WebSphere应用服务器 组件定制设置、配置 Servlet 和设置别名及过滤、建立和维护安全性、收集和监控 WebSphere应用服务器、连接和 Servlet 数据。

    (1) 定制基本属性

在使用 WebSphere应用服务器管理 Servlet 之前，请为 Servlet 活动和 WebSphere应用服务器的组件属性配置基本设置参数。在管理器浏览区域，单击“设置”显示可定制不同设置的项目页面。

    l 管理页面：更改登录至管理器的用户标识符和口令，并为管理器指定新的端口号。

    l 连接管理页面：设置连接缓冲池，从而减少用于维护与数据服务器（如 IBM DB2 关系数据库）的连接所花费的资源。

    l 目录管理页面：为目录服务器指定设置，允许从 Web 服务器、操作系统、WebSphere应用服务器 及其它软件产品的中央位置管理安全性数据。

    l Java 引擎页面：指定 Java 编译器设置并指出 WebSphere应用服务器 是作为 Web 服务器一部分运行以获得较高性能，还是作为独立模式运行以获得更好安全性。

    l 会话跟踪页面：指定维护用户会话（来自于相同浏览器的相关用户请求系列）的状态信息的设置。

    l 用户简要表文件页面：指定维护关于 Web 站点访问者永久信息的设置。

    l 虚拟主机页面：指定 Servlet 的替代路径，允许 Web 服务器根据 Servlet 请求期间客户机指定的不同域提供不同文档。

    (2) 配置 Servlet、别名和过滤

放置在<as_root>\servlets 目录下的 servlet 在请求时自动装入和重新装入（如果更新过）。也可以使用 WebSphere应用服务器 管理器，通过初始化参数和创建 Servlet 别名和过滤更为直接地管理 Servlet。要管理 Servlet，单击管理浏览区域中的 Servlet 并选择页面：

    l 配置页面：为个别 Servlet 定义配置信息和初始化参数，如关联的类文件，是否在启动时装入 Servlet 以及 Web 服务器是否从远程装入 servlet。

    l 别名页面：指定路径映射规则，允许用户输入快捷 URL 来调用特定 servlet。

    l 过滤页面：联系 Servlet 和 MIME-类型，从而每当生成一个特定的 MIME-类型响应时，调用一个特殊的 Servlet。

    (3) 维护安全性

通过定义用户、组、资源和存取控制表建立和维护安全性。通过为每个用户、组和资源指定特定访问设置，可精确地控制如何使用服务的资源，及由谁使用。单击管理器浏览区域中的安全性显示这些页面:：

    l 用户页面：指定允许谁访问由 WebSphere应用服务器 提供的 Web 页面及其它资源，如 servlet。

    l 组页面：将用户与命名列表相关联，允许同时对整个组赋予访问权限。

    l 存取控制表页面：为用户和组指定访问许可。

    l 资源页面：为特定目录、文件和 WebSphere应用服务器 上的 servlet 指定安全性参数。

    (4) 收集和监控服务器数据

通过查看日志文件监控 Servlet 活动、已装入的 Servlet 的状态和资源的实时使用。单击监视器浏览区域中的“服务器执行分析”显示页面列表：

    l JVM 调试页面：启用 JVM 调试和指定调试设置。

    l 事件页面（在日志文件下）：监控事件日志中收集的信息 。

    l 出错页面（在日志文件下）：监控出错日志中收集的信息。

    l 活动会话页面（在监控下）：监控关于 Web 服务器上当前活动的用户会话的信息，包括关于个别会话的信息和所有活动会话的摘要信息。

    l 数据库缓冲池连接页面（在监控下）：监控连接缓冲池信息，包括关于缓冲池和个别连接的统计数字。

    l 转储面板页面（在监控下）：指定何时及在何处创建 Servlet 转储和活动快照。

    l 异常情况状态（在监控下）：监控来自于 Java 引擎和 servlet 的异常数据 。

    l 已装入 Servlet 页面（在监控下）：监控个别 servlet 的状态和统计数字。

    l 日志输出页面（在监控下）：查看事件或出错日志输出的记录。

    l 资源使用页面（在监控下）：监控服务资源如何被使用，包括内存、请求句柄对象缓冲池、服务请求和服务响应时间。

    l 跟踪页面：监控跟踪数据，通常由 IBM 服务人员提出请求。

    l 注销：要快速注销，单击管理器浏览区域中的“注销”，返回管理器登录屏幕。

6.4 部署Servlet

在WebSphere应用服务器上部署 Servlet需要四个步骤：编译 Servlet 或 Web 应用程序、将类文件放到 WebSphere应用服务器上、将相关的 HTML、JSP 和 SHTML 文件放到WebSphere应用服务器上、用WebSphere应用服务器的管理器来配置初始化参数，并设置其它选项。

    1．编译 Servlet 和 Web 应用程序

编译 Servlet，有下列注意事项：

   (1) 确保系统 CLASSPATH 环境变量中包括 JDK classes.zip 文件和相应的 WebSphere应用服务器 JAR 文件。 lib 目录中有几个 WebSphere应用服务器 JAR 文件。根据所需导入的类的不同，可能还需要将那些未列在该示例中的 JAR 文件包括在内：

如果使用的是 Windows NT，请分别输入下列命令（在同一行中）：

set CLASSPATH=.;JAVA_HOME\lib\classes.zip;

applicationserver_root\lib\ibmwebas.jar;

applicationserver_root\lib\jst.jar;applicationserver_root\lib\jsdk.jar;

applicationserver_root\lib\xml4j.jar;

applicationserver_root\lib\databeans.jar;%CLASSPATH%

   (2) 用下列命令将 PATH 环境变量设置成包括 java/bin 目录的变量：

对于 Windows NT，该命令为：set PATH=JAVA_HOME\bin;%PATH%

   (3) 通过发出下列命令来测试相应的 Java Development Kit (JDK) 是否在路径中：

   java -version

该命令应该返回 JDK 版本的状态消息。

   (4) 通过发出下列命令来编译 Servlet

   javac filename.java

    2．将类文件放到 WebSphere应用服务器上

缺省情况下，WebSphere应用服务器在 Servlet 根目录 applicationserver_root\servlets 下查找 Servlet 类文件。请将编译过的 Servlet 类文件复制到该目录下。要从替代 Servlet 目录中装入 Servlet，请配置可重装 Servlet 目录。要从远程系统中装入 Servlet，请在用 WebSphere应用服务器的管理器配置 Servlet 时，指定该远程系统。

   (1) 如果 Servlet 在软件包中，则将软件包结构镜像成 servlet\ 或可重装 Servlet 目录下的子目录。例如，如果 Servlet SignonServlet.class 和 AccountBean.class 在名为 WebBank 中的软件包中，请将 Servlet 放在目录 servlet\WebBank下。

   (2) 如果 Servlet 导入您所开发的非 Servlet 类，建议将那些类复制到 applicationserver_root\servlets 下。

    根据jvm.properties 文件中的设置，将决定所有的 Servlet 标准输出是到 applicationserver_root\logs\ncf.log 文件还是 Java 控制台窗口。请参阅 启用 Java 控制台中的有关的说明。

    3．将HTML、JSP和SHMTL文件放到WebSphere应用服务器上

将与Servlet有关的HTML、JSP 和 SHMTL文件复制到 Web 服务器的 HTML 文档根目录 server_root\HTML_directory 下。该目录是由特定服务器配置（传递、别名和虚拟主机规则的设置）所决定的。

    4．配置 Servlet

    若要从远程系统上的 JAR 或 SER 文件装入 Servlet，或设置初始化参数，请使用 WebSphere应用服务器的管理器来配置 Servlet 或使用XML Servlet 配置。


WebSphere快速入门(19)

作者：easyinfonet

email: zhujs@cec.gov.cn

日期：2001-2-23 9:38:08

6.5 连接管理器

    连接管理器使您可以控制并减少由 Web 应用程序使用的资源。相对于非 Web 应用程序，基于 Web 的应用程序对数据服务器的访问会导致更高的和不可预料的系统开销，这是由于 Web 用户更为频繁的连接和断开。通常连接与断开连接所用的资源大于交互所用的资源。由于 Internet 的“冲浪”性质，用户的交互过程一般都较短。通常，公司外（Internet，而非 intranet）的用户会将使用卷变得很大，并难以预料。连接管理器通过建立用户 Servlet 可用的连接缓冲池将连接的系统开销分摊给多个用户请求。换言之，每个用户请求仅占用连接／断开连接所用系统开销成本的一小部分。在使用初始资源建立缓冲池中的连接后，其余连接／断开连接所用的系统开销就不大了，因为这只是重复使用已有的连接而已。

    Servlet 以如下方式使用连接缓冲池：当一个用户通过 Web 向 Servlet 请求时， Servlet 从缓冲池使用一个已有的连接，这意味着用户请求不会引起数据服务器的连接系统开销。当满足请求时，Servlet 将连接返回至连接管理器缓冲池供其它 Servlet 使用。因而用户请求不会引起数据服务器的断开连接的系统开销。

    连接管理器还使您能控制到数据服务器产品的并发连接数。当数据服务器的许可证协议限定用户数量时，这一特性是非常有用的。可以为数据服务器创建一个缓冲池，并将连接管理器缓冲池的“最多连接数”参数设成数据服务器产品许可证中限定的最大用户数。如果用其它程序而不用连接管理器连接到数据服务器，则不能保证该方法有效。

1. 连接管理器结构

连接管理器维护一个连接到特定数据服务器产品处于打开状态的数据服务器缓冲池。每个数据服务器可以有一个或多个等同的或非等同的缓冲池。连接管理器的一个运行实例可以支持多个数据服务器。图6-1说明了在连接管理器与一个正在连接管理器的连接缓冲池中寻找可使用的连接的 Servlet 之间的典型交互作用。

    (1) 当第一个 Servlet 试图与连接管理器通信时，由WebSphere应用服务器装入运行在WebSphere应用服务器下的连接管理器。只要WebSphere应用服务器在运行，连接管理器就一直被装入。

    (2) WebSphere应用服务器将用户请求传递给一个 Servlet。

     (3) Servlet 用连接管理器使用的方法从缓冲池中请求一个连接。

     (4) 缓冲池给 Servlet 分配一个连接。

    (5) Servlet 使用连接与数据服务器直接对话，这一过程中使用的是特定数据服务器的标准 API。

    (6) 数据服务器通过与 Servlet 的连接返回数据。

    (7) 当 Servlet 结束与数据服务器通信时，Servlet 把连接归还给连接管理器缓冲池，以供其它

Servlet 使用。

    (8) Servlet 通过WebSphere应用服务器向用户发回响应。

    在 Servlet 请求一个连接时，缓冲池中不一定有可用的连接。在这种情况下，连接管理器直接与数据服务器通信。连接管理器将：

    l 请求一个新的连接（参见图6-1中的9 ）。

    l 将连接添加到缓冲池中（参见图6-1中的10）。如果缓冲池中的连接数达到了规定的上限，连接服务器将不会把新的连接加入缓冲池中。

l 将新的连接交给 Servlet（参见图6-1中的4）。

    2．性能特性

为缓冲池创建一个新的连接是一项系统开销很高的任务，新的连接将使用数据服务器上的资源。因此连接管理器尽量用缓冲池中的现有连接来满足 Servlet 的请求。 同时，连接管理器必须尽可能地最小

图6-1：连接管理器与Servlet 之间的交互

化缓冲池中的空闲连接，因为这是对系统资源的极大浪费。连接管理器与 Servlet 一同执行这些最小化和最大化任务。对于可选的性能，请适当地设置连接管理器参数。

连接管理器维护每个连接的验证时间标记、最近使用时间标记和正在使用标志。当某个 Servlet 第一次获得连接时，连接的验证时间标记和最近使用时间标记被设置为当前时间，连接的正在使用标志则被设置为真。

可将连接服务器配置成从某个 Servlet 中移走一个长时间未使用的连接。这个时间长度是由连接管理器的 “最长周期”参数指定的。如果 Servlet 准备在较长一段时期内使用连接与数据服务器多次通信，可能希望将代码加入 Servlet 中，以便在每次使用连接之前，确认它仍占有这个连接。连接管理器从缓冲池中除去空闲的连接，因为它们会浪费资源。为了确定哪个连接是空闲的，连接管理器将检查连接标志和时间标记，这个操作是通过周期性地获取连接缓冲池信息来实现的：

    (1) 连接管理器查看正在使用连接的最近使用时间标记。如果最近使用时间和当前时间之间的时间差大于“最长周期” 配置参数，则本连接将被认为是一个残留连接，这就表明占有它的 Servlet 已终止或者没有响应。残留连接将被归还给缓冲池以供其它 Servlet 使用，它的正在使用标志被设置为假，且验证和最近使用时间标记被设置为当前时间。

    (2) 连接管理器检查未被任何 Servlet 使用的连接（这些连接的正在使用标志为假）。如果最近使用时间与当前时间的时间差超过了“最长空闲时间”配置参数，将认为本连接是空闲的。空闲连接将被从缓冲池中除去，降至“最少连接数”配置参数指定的较低限定值。

    3．监控连接管理器

    WebSphere应用服务器管理器为名为数据库缓冲池连接的连接管理器提供了一个监控程序。可以使用这些信息来查看如何执行连接缓冲池，并建议对连接缓冲池参数进行可能的更改。可以在更改参数之后对缓冲池执行监控，从而查看缓冲池特性的更改并帮助进一步对缓冲池参数进行调整。可以选择特定缓冲池，以从选择列表中对其进行监控。


WebSphere快速入门(2)

作者：easyinfonet

email: easyinfonet@sina.com.cn

日期：2001-2-16 11:19:33

1.3 Web基本知识

Web（World Wide Web、万维网）技术是电子商务的核心技术。Web的思想可追溯到Tim Berners-Lee于1989年3月在CERN (Centre  European pour la Recherche Nucleaire，或称European Laboratory for Particle Physics、欧洲粒子物理实验室)写的一个关于信息管理的项目建议书（Information Management: A Proposal）。该建议书提出了分布式超文本系统的设想，旨在将CERN已有的几个信息服务器一体化，并提供一个简单的用户界面来存取各种形式的信息。1990年Web浏览器和Web服务器使用面向对象技术相继在CERN实现。Berners-Lee 和他的合作伙伴成功引入了构成Web体系结构的基本元素：Web服务器、Web浏览器、浏览器与服务器之间的通信协议HTTP(Hypertext Transfer Protocol 、超文本传输协议)、写Web文档的语言HTML（Hypertext Markup Language、超文本标记语言)、以及用来标识Web上资源的URL（Universal Resource Locator 、统一资源定位器)。1993年，美国伊利诺斯大学国家超级计算应用中心NCSA (National Center for Supercomputing Applications)的Marc Andreesen及其合作者发布了称为Mosaic的浏览器，这是第一个较健壮的易用的浏览器，它具有友善的图形用户界面。从此，Web迅速成长为全球范围内的信息宝库。1994 年，W3 联盟在Tim Berners-Lee的领导下成立，该组织通过制定技术规范与提供参考软件来发展Web的技术标准并促进Web产品之间的互操作性。

    URL（Universal Resource Locator 、统一资源定位器)用来唯一标识Web上的资源，包括Web 页面、图象文件(如gif 格式文件和jpeg格式文件)、音频文件(如au格式)、视频文件 (如mpeg格式文件)。URL的格式为：协议://主机名<:端口号>/标识符(例如 http://www.seu.edu.cn:80/index.html)。协议可以是HTTP、HTTPS（安全的超文本传输协议）、FTP；主机名用来标识被请求的服务器；端口通常为不同协议保留，例如FTP和HTTP守护进程侦听不同的端口，FTP缺省的端口号为21，HTTP缺省的端口号为80；标识符说明被请求的是什么，可以是文件名（含路径）或一个应用关键字 (如/cgi-bin/和/servlet/)加上一些信息（如一个脚本的名字和servlet的名字）。例如，用户键入URL格式的地址(例如 http://www.seu.edu.cn:80/index.html )；浏览器请求主机www.seu.edu.cn 在80端口提供的HTTP服务，并要求取得该服务器上的index.html文件；服务器接受请求，取得该文件；服务器把文件返回浏览器，并告诉浏览器这是一个HTML文件；浏览器在显示器上显示这个页面。在浏览器和web服务器之间使用的协议是HTTP。

    HTTP（Hyper Text Transfer Protocol、超文本传输协议)是用来在互连网上传输文档的协议，它是Web上最常用也是最重要的协议，也是Web服务器和Web客户（如浏览器）之间传输Web页面的基础。HTTP是建立在TCP/IP之上的应用协议，但并不是面向连接的，而是一种请求/应答（Request/Response）式协议。浏览器通常通过HTTP向Web服务器发送一个HTTP请求，其中包括一个方法、可能的几个头、一个体。常用的方法类型包括：GET（请求一个网页）、POST（传送一个表单中的信息）、PUT（存入这个信息、类似于FTP中的PUT）和DELETE（删除这个信息）。Web服务器接受到HTTP请求之后，执行客户所请求的服务，生成一个HTTP应答返回给客户。HTTP应答有一个状态行、可能的几个头、一个体。在头中可以定义返回文档的内容类型（MIME类型）、Cache控制、失效时间。MIME类型包括：“text/html”（HTML文本）、“image/jpeg”（JPEG图）、“audio/ra”（RealAudio文件）。HTTP本身也在不断完善和发展，目前，常用的是HTTP1.1，它更好地利用TCP的特性，对HTTP1.0作了改进。

HTML (Hypertext Markup Language、超文本标记语言)是Web诞生与发展的要素之一，它旨在使得Web页面能显示在任何HTML使能的浏览器中，而与连网的机器平台无关。HTML并不是一个程序设计语言，而是一个标记语言，它所提供的标记是由SGML（Standard Generalized Markup Language，标准的通用标记语言）定义的。SGML是ISO（国际标准化组织）在1986年推出的一个用来创建标记语言的语言标准，它源自IBM早在1969年开发的GML（Generalized Markup Language），该语言的名称也正好包含了三位创始人姓字的第一个字母，他们分别是Charles F. Goldfarb, Edward Mosher，Raymond Lorie。SGML是一种元语言，即用来定义标记语言的语言，它提供了一种将数据内容与显示分离开来的数据表示方法，使得数据独立于机器平台和处理程序。这些特性促使Tim Berners-Lee 采用SGML来创建称之为HTML的标记语言。1993年形成HTML 1.0，以后不断完善，HTML 4.0发表于1997年。特别需要指出的是HTML提供的链接机制是Web的本质特性之一。但是，HTML更多的关注Web浏览器如何在页面上安排文本、图象和按钮等，过多地考虑外观使其缺乏对结构化数据的表示能力。另外，HTML中有限的标记不能满足很多Web应用的需要，如基于Web的大型出版系统和新一代的电子商务，而为各种应用需要不断地往HTML中增加标记显然不是最终的解决方法，究其原因是HTML缺乏可扩展性。解决方案应该是简化SGML使之能应用到Web上。为此，从1996年开始，W3C（World Wide Web Consortium） 的一个工作组在Jon Bosak的领导下致力于设计一个超越HTML能力范围的新语言，这个语言后来被命名为XML（Extensible Markup Language，可扩展标记语言）。1998年2月，W3C发布了XML 1.0作为其推荐标准。现在，W3C已经用XML设计出一个与HTML4.01功能等价的语言，称为XHTML1.0 （Extensible HyperText Markup Language）。

Web客户通常指的是Web浏览器，如Netscape Navigator和Microsoft Internet Explorer。这种浏览器能理解多种协议，如HTTP、HTTPS、FTP；也能理解多种文档格式，如text、HTML、JPEG（一种图象文件格式）、XML(有的尚未支持)；也具备根据对象类型调用外部应用的功能。需要指出的是HTML文档中的链接在Web浏览器中通常以带下划线的方式显示，用户点击某个链接就能浏览到所链接的Web资源，这也是Web的魅力所在。

Web服务器（或称HTTP服务器）提供HTTP服务。本来Web服务器只提供“静态”内容，即返回在URL里指定的文件的内容，一般具备将URL名映射到文件名的功能，并能实施某种安全策略。现在，可采用CGI（通用网关接口）技术或Java Servlet技术从一个运行的程序里得出“动态”内容，可以采用应用关键字（如/cgi-bin/和/servlet/）来组织脚本文件和Servlet文件，而且现在的Web服务器通常还具备连接数据库的功能，这些形成了Web应用的出现。通常，一个Web服务器还提供其它服务，如FTP服务。有的还可作为代理服务器。一个代理服务器是一个可以从别的服务器上为它的客户取文件的服务器。代理服务器可以通过缓存应答（页面）使得响应时间更快，也可以降低网络流量，对外能隐藏内部网信息。

总之，URL、HTTP、HTML(以及XML)、Web服务器和Web浏览器是构成Web的五大要素。Web的本质内涵是一个建立在Internet基础上的网络化超文本信息传递系统，而Web的外延是不断扩展的信息空间。Web的基本技术在于对Web资源的标识机制（如URL）、应用协议（如HTTP和HTTPS）、数据格式（如HTML和XML）。这些技术的发展日新月异，同时新的技术不断涌现，因此Web的发展前景不可限量。

1.4 Java 简介

现代技术的发展，尤其是网络技术，给现代企业带来了许多新的机遇和挑战，如改善客户服务、全球化和信息检索等，这些在技术上反映为信息的获取、系统管理、系统集成、新技术的开发、Internet、Intranet等等与商业的结合。而这些要求一个随处可用的开放的结构和在不同的平台之间低成本的信息传递方式，Java正好满足这些要求。

Java是由Sun Microsystems公司于1995年5月推出的Java程序设计语言（以下简称Java语言）和Java平台的总称。用Java实现的HotJava浏览器（支持Java applet）显示了Java的魅力：跨平台、动感的Web、Internet计算。从此，Java被广泛接受并推动了Web的迅速发展，常用的浏览器现在均支持Java applet。另一方面，Java技术也不断更新。

Java平台由Java虚拟机（Java Virtual Machine）和Java 应用编程接口（Application Programming Interface、简称API）构成。Java 应用编程接口为Java应用提供了一个独立于操作系统的标准接口，可分为基本部分和扩展部分。在硬件或操作系统平台上安装一个Java平台之后，Java应用程序就可运行。现在Java平台已经嵌入了几乎所有的操作系统。这样Java程序可以只编译一次，就可以在各种系统中运行。Java应用编程接口已经从1.1.x版发展到1.2版。目前常用的Java平台基于Java1.2。

Java语言是一个支持网络计算的面向对象程序设计语言。Java语言吸收了Smalltalk语言和C++语言的优点，并增加了其它特性，如支持并发程序设计、网络通信、和多媒体数据控制等。主要特性如下：

1)    Java语言是简单的。Java语言的语法与C语言和C++语言很接近，使得大多数程序员很容易学习和使用Java。另一方面，Java丢弃了C++ 中很少使用的、很难理解的、令人迷惑的那些特性，如操作符重载、多继承、自动的强制类型转换。特别地，Java语言不使用指针，并提供了自动的废料收集，使得程序员不必为内存管理而担忧。

2)    Java语言是一个面向对象的。Java语言提供类、接口和继承等原语，为了简单起见，只支持类之间的单继承，但支持接口之间的多继承，并支持类与接口之间的实现机制（关键字为implements）。Java语言全面支持动态绑定，而C++ 语言只对虚函数使用动态绑定。总之，Java语言是一个纯的面向对象程序设计语言。

3)    Java语言是分布式的。Java语言支持Internet应用的开发，在基本的Java应用编程接口中有一个网络应用编程接口（java.net），它提供了用于网络应用编程的类库，包括URL、URLConnection、Socket、 ServerSocket等。Java的RMI(远程方法激活)机制也是开发分布式应用的重要手段。

4)    Java语言是健壮的。Java的强类型机制、异常处理、废料的自动收集等是Java程序健壮性的重要保证。对指针的丢弃是Java的明智选择。Java的安全检查机制使得Java更具健壮性。

5)    Java语言是安全的。Java通常被用在网络环境中，为此，Java提供了一个安全机制以防恶意代码的攻击。除了Java语言具有的许多安全特性以外，Java对通过网络下载的类具有一个安全防范机制（类ClassLoader），如分配不同的名字空间以防替代本地的同名类、字节代码检查，并提供安全管理机制（类SecurityManager）让Java应用设置安全哨兵。

6)    Java语言是体系结构中立的。Java程序（后缀为java的文件）在Java平台上被编译为体系结构中立的字节码格式（后缀为class的文件）, 然后可以在实现这个Java平台的任何系统中运行。这种途径适合于异构的网络环境和软件的分发。

7)    Java语言是可移植的。这种可移植性来源于体系结构中立性，另外，Java还严格规定了各个基本数据类型的长度。Java系统本身也具有很强的可移植性，Java编译器是用Java实现的，Java的运行环境是用ANSI C实现的。

8)    Java语言是解释型的。如前所述，Java程序在Java平台上被编译为字节码格式， 然后可以在实现这个Java平台的任何系统中运行。在运行时，Java平台中的Java解释器对这些字节码进行解释执行，执行过程中需要的类在联接阶段被载入到运行环境中。

9)    Java是高性能的。与那些解释型的高级脚本语言相比，Java的确是高性能的。事实上，Java的运行速度随着JIT(Just-In-Time)编译器技术的发展越来越接近于C++。

10)    Java语言是多线程的。在Java语言中，线程是一种特殊的对象，它必须由Thread类或其子（孙）类来创建。通常有两种方法来创建线程：其一，使用型构为Thread(Runnable) 的构造子将一个实现了Runnable接口的对象包装成一个线程，其二，从Thread类派生出子类并重写run方法，使用该子类创建的对象即为线程。值得注意的是Thread类已经实现了Runnable接口，因此，任何一个线程均有它的run方法，而run方法中包含了线程所要运行的代码。线程的活动由一组方法来控制。 Java语言支持多个线程的同时执行，并提供多线程之间的同步机制（关键字为synchronized）。

11)    Java语言是动态的。Java语言的设计目标之一是适应于动态变化的环境。Java程序需要的类能动态地被载入到运行环境，也可以通过网络来载入所需要的类。这也有利于软件的升级。另外，Java中的类有一个运行时刻的表示，能进行运行时刻的类型检查。

Java语言的优良特性使得Java应用具有无比的健壮性和可靠性，这也减少了应用系统的维护费用。Java对对象技术的全面支持和Java平台内嵌的API能缩短应用系统的开发时间并降低成本。Java的编译一次，到处可运行的特性使得它能够提供一个随处可用的开放结构和在多平台之间传递信息的低成本方式。特别是Java企业应用编程接口（Java Enterprise APIs）为企业计算及电子商务应用系统提供了有关技术和丰富的类库。

1)    JDBC（Java Database Connectivity）提供连接各种关系数据库的统一接口。

2)    EJB(Enterprise JavaBeans)使得开发者方便地创建、部署和管理跨平台的基于组件的企业应用。

3)    Java RMI(Java Remote Method Invocation)用来开发分布式Java应用程序。一个Java对象的方法能被远程Java虚拟机调用。这样，远程方法激活可以发生在对等的两端，也可以发生在客户端和服务器之间，只要双方的应用程序都是用Java写的。

4)    Java IDL(Java Interface Definition Language) 提供与CORBA(Common Object Request Broker Architecture)的无逢的互操作性。这使得Java能集成异构的商务信息资源。

5)    JNDI(Java Naming and Directory Interface)提供从Java平台到的统一的无逢的连接。这个接口屏蔽了企业网络所使用的各种命名和目录服务。

6)    JMAPI（Java Management API）为异构网络上系统、网络和服务管理的开发提供一整套丰富的对象和方法。

7)    JMS(Java Message Service)提供企业消息服务，如可靠的消息队列、发布和订阅通信、以及有关推拉（Push/Pull）技术的各个方面。

8)    JTS(Java transaction Service)提供存取事务处理资源的开放标准，这些事务处理资源包括事务处理应用程序、事务处理管理及监控。

在Java技术中，值得关注的还有JavaBeans，它是一个开放的标准的组件体系结构，它独立于平台，但使用Java语言。一个JavaBean是一个满足JavaBeans规范的Java类，通常定义了一个现实世界的事物或概念。一个JavaBean的主要特征包括属性、方法和事件。通常，在一个支持JavaBeans规范的开发环境（如Sun Java Studio 和IBM VisualAge for Java）中，可以可视地操作JavaBean，也可以使用JavaBean构造出新的JavaBean。JavaBean的优势还在于Java带来的可移植性。现在，EJB (Enterprise JavaBeans) 将JavaBean概念扩展到Java服务端组件体系结构，这个模型支持多层的分布式对象应用。除了JavaBeans，典型的组件体系结构还有DCOM和CORBA，关于这些组件体系结构的深入讨论超出了本书的范围。

第二章 电子商务应用框架

    要转变传统的业务过程，就需要开发和部署电子商务应用系统的一个基础，电子商务应用框架正是这样的一个基础。许多企业希望电子商务应用系统具备下列特征：

1)    基于标准；

2)    以服务器为中心；

3)    可伸缩；

4)    能利用已有的核心系统；

5)    可快速部署和易用；

6)    易管理。

本章介绍的电子商务应用框架能满足企业开发电子商务应用系统的上述需求。所谓一个框架是指一个可复用的设计，表示为一组抽象的元素范例以及元素范例之间合作的接口。框架是有针对性的，如一个用户界面框架只为软件系统的用户界面提供了一个设计，而一个应用框架为整个应用系统提供了一个设计。一个应用框架中的元素范例也可称之为组件。本节介绍的电子商务应用框架基于业界标准；它为开发和部署电子商务应用系统提供了一组完整的服务；它提供的Web应用编程模型定义了Web应用拓扑结构以及使用框架提供的服务来设计Web应用的一个模型。这个框架基于独立于平台和提供商的技术标准，包括关于客户端、应用服务器、网络、数据和基础设施的标准。这些标准使得客户能在任何时候在网络上的任何地方存取有关数据和服务，也使得开发的应用软件只需写一次就能到处运行，并能即插即用各种组件。下面介绍电子商务应用框架的基本系统模型、体系结构和Web应用编程模型。


WebSphere快速入门(20)

作者：easyinfonet

email: zhujs@cec.gov.cn

日期：2001-2-23 9:39:16

6.6 用户简要表

    Application Server 含有 com.ibm.servlet.personalization.userprofile 软件包中的类，这些类使维护关于 Web 站点访问者的持久信息和利用这些信息来定制 Web 页面变得更为容易。UserProfile 类包括了访问者的完整名称、邮政地址和电子邮件地址、电话号码等数据成员，并含有用于存储所选的语言、职业和用户定义的组信息的字段。另外，它还含有类属信息、购物手推车和剪贴板（一个 Java 散列表）。这就使您很容易地将其它您选择的对象合并到这些数据中，并将它们作为 UserProfile 类的一部分进行处理。因为这些对象在用户简要表文件的后继实例一直存在，所以它们必须是可串行化的。如果使用了一个 JDBC 数据库，则会将对象存储在数据库中。否则，会将对象作为文件存储。在 Application Server 版本 2.0 中，UserProfile 类使用了数据库连接管理器来维护 JDBC 连接。

    可以使用“用户简要表文件”页面配置 UserProfile 类，该类用于定义和维护有关 Web 站点访问者的信息。UserProfile 类使用连接管理器来维护与 JDBC-从属数据库的连接。用户简要表文件的基本设置如下。

1.    启用用户简要表文件

    (1) 查看“设置” -> “用户简要表文件”页面。

    (2) 单击“启用”标签。

    (3) 在“是否使用用户简要表文件?”字段中单击“是”。

    (4) 对于“类名”字段，可以使用缺省值“com.ibm.servlet.personalization.userprofile.UserProfile” 或为

UserProfile 类创建的扩展名。

    (5) 单击“保存”按钮。

    2．指定数据库信息

    (1) 查看“设置” -> “用户简要表文件”页面。

   (2) 单击“数据库”标签。

    (3) 指定数据库产品的名称（如 IBM DB2 的 db2）。

    (4) 指定数据库的 JDBC 驱动程序（如 COM.ibm.db2.jdbc.app.DB2Driver）。注意：这包括 Application

Server 的 Java 类路径中的驱动程序 .zip 或 .jar 文件（如 DB2 的 db2java.zip）。

    (5) 指定用户简要表文件的数据库名称以存储数据。如果不存在该数据库，将不会创建它。因此，可

以指定现有数据库的名称，或在使用该用户简要表文件之前用该名称创建一个数据库。无需在数据库中创建任何表格。

    (6) 指定数据库的所有者，即创建数据库的用户的标识符。

    (7) 指定保存用户简要表文件数据的数据库表格的名称。无需创建该表格，因为 UserProfile 类会在

初始化时创建它。

    (8) 指定用于访问数据库和其表格的用户标识符。

    (9) 指定与用户标识符相关的口令。

   (10) 单击“保存”按钮。

    3．配置连接管理属性

    (1) 查看“设置” -> “用户简要表文件”页面。

    (2) 单击“连接管理”标签。

    (3) 指定要使用的连接缓冲池。

    (4) 指定在“连接超时”指定的时间过后，连接仍不可用的情况下，指定是否等待并再试一次连接（与

“连接管理”页面中相同）。

    (5) 单击“保存”按钮。

UserProfile 类和 Application Server 的其余部分之间的唯一的耦合在于，UserProfile 类和 IBM 的会话实现（ IBMSessionData）含有同一个用户名称段，该用户名称段可用于在两个类之间创建一些增效作用。用户简要表文件对象持有关于用户的信息，并通过唯一的用户名与用户的 HttpSession 对象相关联。用户名的值由 SessionData类getUserName() 和 setUserName() 方法设置并返回。

也可以扩展 UserProfile 类以创建一个更适合业务需求的子类，并用 Application Server 管理器指定专门 UserProfile 子类，该子类可用于所有的 Web 应用程序。


WebSphere快速入门(21)

作者：easyinfonet

email: zhujs@cec.gov.cn

日期：2001-2-23 9:39:57

6.7会话跟踪

Web应用服务器具有会话跟踪（即跟踪用户请求）的功能。使用管理器的“会话跟踪”页面配置会话跟踪。会话跟踪程序可将同一用户的几个相关请求合并为单个会话（即一个 HttpSession对象）。会话跟踪程序也能使Web应用服务器的多个实例能共享会话的一个公共缓冲池（即一个会话群集器）。

    1．启用会话跟踪及其部件

查看 “会话跟踪”页面。单击“启用”标签。在“启用”标签中可以设置如下字段。

     (1) 启用会话：是否启用会话跟踪, 即请求和响应对象的相关会话方式是否有效。缺省值为是。

     (2) 启用 URL 重写：指会话跟踪是否启用重写 URL 来获取会话标识符。若被启用，则会话跟踪

程序将识别到达 URL 的会话标识符，若有必要的话，还将重写 URL 和发送该会话标识符。缺省值为。

     (3) 启用 Cookie：指会话跟踪是否使用 cookie 来携带会话标识符。若为“是”，则会话跟踪程序将

识别作为 cookie 到达的会话标识符，并用 cookie 作为发送会话标识符的方式。缺省值为是。

     (4) 启用协议转换重写：当 URL 要求从 HTTP 转换成 HTTPS，或从 HTTPS 转换成 HTTP 时，

是否将会话标识符添加到 URL 中。缺省值为否。

    2．创建携带会话标识符的 Cookie

指定 Cookie 的设置，会话跟踪程序使用该 Cookie 将相关的个别请求置入逻辑用户会话中。当 Servlet 请求一个 HttpSession 对象表示一个用户会话时，会话跟踪程序可以使用一个 Cookie 将唯一的会话标识符传回给发出请求的浏览器。当 Servlet 发出后继请求时，Cookie 则获准在 Servlet 和会话跟踪程序间的往返传送。会话跟踪程序使用它来查找用户 Servlet 的现有 HttpSession 对象。换而言之，会话跟踪程序使用 Cookie 中的标识符确定该请求与该用户的其它请求相关。

查看 “会话跟踪”页面。单击“Cookie”标签。指定 Cookie 的名称，缺省值为sesessionid。可选地指定下列字段：

    (1) “域”字段的值以限制 Cookie 的发送位置。

    (2) 会话 Cookie 在客户机浏览器上的活动时间（即“最长周期”字段，以毫秒计）。如果该字段为

空，则当关闭浏览器时将删除该 Cookie。

    (3) “路径”字段的值以指定 Cookie 发送所至的服务器路径。

    (4) 在“安全”字段中单击“是”以限制仅允许 HTTPS 会话的 Cookie 进行交换。

    3．设置会话跟踪活动的时限和参数

查看“会话跟踪”页面。单击“间隔”标签。

    (1) 指定会话跟踪程序检查会话是否空闲及估计会话是否无效的时间间隔（以毫秒计）。

    (2) 指定会话跟踪程序估计是否因为内存中会话过多，而将会话交换到磁盘的频率（以毫秒计）。

     (3) 指定一次允许保留在内存中的会话数（最多驻留数）。

     (4) 指定一个会话将允许在会话跟踪程序停止验证之前保持空闲的时间（以毫秒计）。

    4．保留持续数据

使用“持续性”标签指定在会话跟踪程序关闭重新并启动后，以验证会话数据是否交换到磁盘。如果“持续性”字段设置为否，则会话跟踪程序将在每次启动时删除旧的会话交换文件。查看“会话跟踪”页面。单击“持续性”标签。在“持续性”字段中单击“是”。指定会话跟踪程序用于存储要保持持续性的会话数据。注意：交换目录必须是一个空目录。

    5．指定会话群集和协议转换设置

可以将WebSphere应用服务器配置成以如下方式运行：独立主机、会话群集服务器、会话群集客户机、协议转换主机、独立主机。查看“会话跟踪”页面，单击“主机”标签。

    (1) 若WebSphere应用服务器实例为一个独立主机，则它不参与会话群集。在独立模式下, WebSphere应用服务器将维护它自己的会话信息，而对客户的会话信息请求将不予响应，并且不从服务器请求会话信息。缺省值：是。

    (2) 在用如 IBM eNetwork Dispatcher 之类的产品群集 Web 服务器的环境中，指定WebSphere应用服务器实例的主机名和 IP 地址，其中该实例为会话群集服务器。

    (3) 若将WebSphere应用服务器实例配置成 URL 重写和协议转换（在“启用会话跟踪”标签中），则指明群集主机的主机名或 IP 地址。例如，可能为 IBM eNetwork Dispatcher 主机。

要使更改生效，请在更改“独立主机”或“会话群集服务器”字段后，重新启动Web应用服务器。更改这些字段后对所有配置所做的更改都保存在配置文件中，直至重新启动后方才生效。


WebSphere快速入门(22)

作者：easyinfonet

email: zhujs@cec.gov.cn

日期：2001-2-23 9:41:20

6.8 安全性

WebSphere应用服务器具有很好的安全性支持。安全性简单地说就是确定谁可访问重要的系统资源，这些系统资源包括文件、目录、程序、连接和数据库。以独立模式运行WebSphere应用服务器比作为 Web 服务器的一部分运行具有更高的安全性。如果安全性要求超出了 Web 服务器提供的安全性，那么请以独立模式运行WebSphere应用服务器。下面介绍使用存取控制表保护资源、选择认证方案和协议、在WebSphere应用服务器中使用目录服务。

    1．使用存取控制表保护资源

建立了一个设置安全性的基本过程。

与安全性相关的概念包括用户、组、资源、许可权、领域、和存取控制表（ACL），这些安全性概念是紧密相关的，现说明如下：

    (1) 用户：一个可被 Web 服务器认证的身份。用户可以是人也可以是计算机。

    (2) 组：用户的集合。组提供了一个管理大量的用户的有效方法，这是因为管理员可以一次指定一个组的许可权。通常，组成员之间有一些共同特征。例如，一个公司中可能包括一个由管理级雇员组成的组，和另一个由非管理级雇员组成的组。其中，可能对非管理级用户赋予查看销售数据文件的访问权，而对管理级用户赋予查看和编辑销售数据文件的访问权。可以将一个人定义成单个用户，同时也将其定义成组的成员。例如，个人 Amy 可能代表单个用户 amy，也可能代表组 managers 的成员。

    (3) 资源：通过 Web 服务器进行存取的有价资源包括HTML 文件和目录（Web 页面）、其它文件和目录（如FTP文件）、Web 应用程序（Java Servlet 或 CGI 程序），而通过WebSphere应用服务器进行存取的资源包括Java Servlet、启用访问企业资源和应用程序（如数据库）的定制 Servlet、连接、套接字、文件和其它可由 Servlet 使用的资源。

    (4) 许可权：许可权表示请求访问资源的特权。管理员可通过建立存取控制表向用户和组授予许可权，以保护资源。许可权是与特定的资源相关的。想象一下，如果一个用户具有查看文件 A 和修改文件 B 的许可权。那么该用户不能用修改文件 B 的许可权来修改文件 A。每个许可权仅适用于特定的资源，且不能传递。单个用户的许可权优先于组的许可权。例如，如果用户 amy 对文件具有读、写权，但同时 amy 所属的组对该文件仅有读取权，那么 amy 仍对该文件具有读、写权，这是因为单个用户的许可权将覆盖具有更多限定的组许可权。（即使组的许可权限制性低于单个用户的许可权，但用户的许可权仍将覆盖组的许可权）。

    (5) 领域：领域是用户、组和存取控制表的数据库。为了使用户能访问领域中的资源，必须在该领域中定义需访问资源的用户。一个用户可以属于几个领域，但在同一领域中用户标识符不能重复。例如，用户 amy 可属于 fileRealm 和 anyRealm 领域。但每个领域中仅可包含一个名为 amy 的用户。可在每个领域中赋予用户 amy 访问不同资源的不同许可权。

    (6) 存取控制表：与资源关联的存取控制表指定了领域中的哪些用户和组可以访问资源。

    存取控制表（ACL）、领域和资源的关系如下：

l 一个领域可以包含许多 ACL。

l 一个领域可以包含许多资源。

l 一个 ACL 可以仅属于一个领域。

l 一个资源可以仅属于一个领域。

l 一个资源仅与一个 ACL 相关。

l 一个 ACL 可与许多资源相关。

WebSphere应用服务器附带了一些前期建立的领域：

l defaultRealm 定义了用户如何访问本地定义的资源。也可建立存取控制表，以确定哪些用户和组

对哪些资源具有访问权。

l NT 领域（NTRealm）和UNIX领域（UnixRealm）定义了在操作系统中拥有帐户的用户如何使用

WebSphere应用服务器资源。 操作系统中定义的用户可由WebSphere应用服务器共享。只要他们存在于下层系统中，这种共享就一直存在。WebSphere应用服务器管理器界面使您能查看该领域；但若要更改它，必须使用操作系统所提供的设施。目前，WebSphere应用服务器可以共享操作系统定义的用户，但不能共享组。

l servletMgrRealm 定义了 Servlet 如何访问远程定义的资源，如远程装入的 Servlet。 servletACL 是

该领域中的唯一一张存取控制表。当远程装入且带有数字签名的 Servlet 试图访问一个受保护的资源时，将对 Servlet 中的数字证书与 servletMgrRealm 中与用户关联的数字证书进行比较。 servletACL 定义了是否授予或拒绝许可权。例如，假设用户 X 的数字证书封装在 anyServlet.JAR 文件中。如果用户 X 被添加在 servletMgrRealm 中，则所有包含与该用户相同数字证书的 Servlet （anyServlet.JAR 文件中）可以执行和访问赋予该用户的资源。

l 最后，如果在WebSphere应用服务器管理器的“目录管理”页面中启用目录服务，将显示

LDAPRealm。WebSphere应用服务器可以共享定义在目录服务中的用户和组，且这种共享将一直持续下去，直至除去他们或禁用目录管理支持。WebSphere应用服务器管理器界面使您能查看该领域；但若要对它进行更改，必须使用 LDAP 服务器所提供的设施。如需更多有关 LDAP、目录服务和 LDAPRealm 的信息，请参阅有关使用目录服务的文档。

WebSphere应用服务器使您能设置各种许可权。例如，其中包括发送和接收文件、删除文件、读取和写入文件、装入 Servlet、链接至库文件、打开和侦听套接字等许可权。某些情况下，服务不需要其客户存在于存取控制表中。例如，许多 Web 页面（HTTP）服务向所有用户公开其文档，而不要求他们在存取控制表中进行注册。

可通过为每个资源在单个领域中建立单个存取控制表（ACL）来保护该资源（下面将进行讨论）。 ACL 将指定可以访问或修改资源的用户或组。对于要保护的每个资源，需要指定存取控制表、安全性领域、认证方案（用来验证访问资源的用户的方法）。下面介绍实现安全性的一个示例。

使用各种管理器页面，以用存取控制表来建立安全性的基本步骤显示如下。

    (1) 定义用户。

使用“安全性”-> 使用“用户”页面来定义用户，即定义被允许访问资源的个人和计算机。例如，定义名为 bopeep 的用户：

l 选择 defaultRealm。

l 单击页面左上部的“添加”按钮。

l 输入用户名 bopeep。

l 输入口令 sheep。再一次输入该口令以进行验证。

l 单击“确定”按钮。

l 验证 bopeep 显示在“定义的用户”列表中。

    (2) 可选地定义组

使用“安全性”-> 使用“组”页面来定义用户所属的组，以使管理更加容易。首先，选择一个领域。下一步，选择将组添加到该领域中。可以用您喜欢的名称来命名组，然后将用户从非成员状态转换为成员状态。例如，将用户 bopeep 添加到组 mypeeps 中：

l 选择 defaultRealm。

l 单击页面左上部的“添加”按钮。

l 输入组名 mypeeps。

l 在“非成员”列表中选择用户 bopeep。

l 单击“非成员”列表旁的“添加”按钮。

l 验证 bopeep 已转换到成员框中。

    (3) 创建 ACL

使用“安全性”-> 使用“存取控制表”页面来创建 ACL。例如，创建名为 sheepcontrol 的 ACL：

l 单击定义“定义的存取控制表”列表旁的“添加”按钮。

l 输入 ACL 名称 sheepcontrol。

l 单击“确定”按钮。

    (3) 定义资源

使用“安全性”-> 使用“资源”页面以将要保护的资源添加到领域中。（注意：必须在执行“创建 ACL”步骤后才能执行该步骤，这是因为必须指定新资源所属的 ACL）。例如，将 CheckMessage Servlet 添加到领域 defaultRealm 中的 sheepcontrol ACL 中：

l 选择 defaultRealm。

l 单击“添加”按钮。

l 选择基本方案作为认证方案。（“方案”选项将在下文中讨论）。

l 选择 sheepcontrol ACL。

l 选择 Servlet，并指定 CheckMessage Servlet。

l 单击“确定”按钮。

l 验证 CheckMessage 现已作为 ACL sheepcontrol 的资源列示出来。

    (4) 赋予许可权

返回 ACL 并赋予用户、组和计算机访问 ACL 资源的许可权。例如，赋予 bopeep具有 GET 和 PUT 许可权，以使它可以访问领域 defaultRealm 中sheepcontrol ACL的文件和文件夹：

l 确保已在“定义的存取控制表”列表框中选定 sheepcontrol。

l 单击页面底部“主许可权”字段旁的“添加”按钮。

l 当显示“在 defaultRealm 中的 ACL sheepcontrol 添加许可权”框时，单击“文件与文件夹”。

l 下一步，选择许可权选项为 bopeep。由于 bopeep 是一个用户，所以单击“用户”。在列表中选

择bopeep。（注意，如果选择的是“组”，则将看到“主名”列表中的组 mypeeps）。

l 使用复选框赋予 bopeep具有 GET 和 PUT 许可权。

l 单击“确定”按钮。

l 通过查看“主许可权”框来验证 bopeep 具有正确的许可权。如果没有显示许可权，请单击 bopeep

左边的加号。这时将显示一个列表，该列表显示了 bopeep 的许可权。

    2．选择认证方案和协议

首先讨论的是认证协议，即 HTTP 和 HTTPS。接下来将会讨论的是认证方案，即基本、摘要、定制和证书认证。最后讨论的是有关组合方案和协议的策略。

    (1) 关于认证协议

HTTPS 则是 HTTP 和 SSL 协议的组合。SSL（安全套接字层）是一个网络安全协议，它用来提供服务器和客户机之间必要的安全性。如果选择 HTTP，认证数据的接收无任何保护。如果选择 HTTPS，认证数据在每个 SSL 协议中加密。如果要使用证书认证，那么必须使用 HTTPS。

    (2) 关于认证方案

WebSphere应用服务器支持的认证方案包括基本认证、摘要认证、定制认证、证书认证。

l 基本认证：使用 HTTP 或 HTTPS 请求来自客户机的用户名和口令。 用普通文本将用于验证的

信息发送给服务器验证。所有浏览器都支持基本认证。如果一个用户标识符和口令提供了足够的认证，那么请考虑使用基本认证。

l 摘要认证：使用 HTTP 或 HTTPS 请求来自客户机的用户名和口令。将用于验证的用户名和口

令的加密形式（使用摘要）发送给服务器。并非所有浏览器都支持摘要认证。（当前仅 Sun HotJava 浏览器支持该认证方案）。如果浏览器不支持摘要认证，那么其用户将无法访问由该协议进行保护的资源。

l 定制认证：使用 HTTP 或 HTTPS 来请求使用 HTML 格式定制的客户机信息。由 CGI 和

Servlet 将这些用于验证的信息用普通文本发送到服务器上。当需要除了标识符和口令之外的用户认证时，可使用定制认证。例如，可以请求一个社会安全号的用户认证。使用该协议，可建立 HTML 格式以询问用户数据。认证是由服务器端代码（CGI 和 Servlet）执行的，而不是由 IBM WebSphere应用服务器运行时应用程序执行的。如果使用定制认证，请使用 HTTPS 保护数据。

l 证书认证：使用 HTTPS 以请求一个客户机证书。必须启用 SSL 客户机认证选项。将用于验证

的信息发送给服务器。认证使用的数字证书具有很高的安全性，且证书认证通常对用户是透明的。系统或站点管理员会对客户机证书进行管理。通常这些任务是由证书权威服务器软件授权的，例如 IBM Vault Registry 产品。

    (3) 组合认证方案和协议

正如前述，除非是在一个与安全无关的环境中，否则 HTTPS 通常更为可取。对于不同的安全性要求可以对方案和协议进行组合，策略如下：

l 对于基本安全性要求，使用基本、摘要或 HTTP 上的定制认证。

l 对于较高安全性要求，使用基本、摘要或 HTTPS 上的定制认证。

l 对于最高安全性要求，使用 HTTPS 上的证书认证。

    3．使用目录服务

一个目录服务是信息仓库、存取法和相关服务的组合。信息仓库通常是用于存储位置信息和其它有关资源（例如用户、打印机、文件服务器和WebSphere应用服务器）的详细信息的数据库。存取法是指轻量级目录访问协议（LDAP）或其它可用来与目录服务组件进行通信的存取法。相关服务是指目录服务提供的用于查询、操纵和认证数据库中信息的设施。

对集中式安全性数据通常需要使用目录服务。目录服务可以为整个网络提供单个管理点。使用目录服务，可以一次定义用于所有应用程序（包括WebSphere应用服务器）的用户和组，而无需为每个应用程序分别定义用户和组。目录服务对于实现安全性很有帮助，即通过认证用户和控制对资源的访问。没有目录服务，可能必须为不同的软件产品（例如WebSphere应用服务器、Web 服务器和操作系统）重复定义相同的用户和组，这是因为这些软件产品不共享安全性数据。

WebSphere应用服务器版本 2.0 支持使用轻量级目录访问协议（LDAP）V2 的目录服务。这些目录服务包括 Domino Directory 版本 4.6.x 和 Netscape Directory Server 版本 3.x。WebSphere应用服务器版本 2.02 （本地语言版本）也支持 IBM Suites（不包括 IBM Suites 版本 1.0）的 eNetwork Directory 组件。使用目录服务的基本步骤如下：

(1) 设置目录服务，或使用一个现有的目录服务。

(2) 使用WebSphere应用服务器的管理器界面的“目录管理” 页面，以标识至WebSphere应用服务

器的目录服务，并指定基本设置。

(3) 查看管理器中的目录服务用户和组信息。

(4) 在WebSphere应用服务器中定义存取控制表（或已有的 ACL），以利用目录服务用户和组信息。

(5) 用 ACL 保护WebSphere应用服务器资源。

    使用“目录管理” 页面指定目录服务的步骤如下：

l 查看“设置” -> “目录管理”页面。

l 在“配置”标签中，请在“是否启用目录?”字段中单击“是”

l 使用下拉列表选择目录类型。选择：

&frac34;“netscape”使用 Netscape Directory Server；

&frac34; “domino4.6”使用 Domino Server；

&frac34; “enetworkibmsuite”使用 IBM Suite 的 eNetwork Directory Server 组件。

l 输入驻留目录服务的计算机主机名。

l 输入服务的端口号。

l 输入将作为目录服务的 LDAP 搜索起始点的基本分辨名（Base DN）。例如，o=raleigh.ibm.com.

l 可选功能。输入联编分辨名和联编口令。这些字段空白表示WebSphere应用服务器匿名联编目录

服务。如果指定了联编分辨名和口令，则请确认将目录服务配置成使用同一分辨名和口令来认证WebSphere应用服务器，否则认证将告失败。

    当使用“目录管理”指定目录服务时，管理器将提供的功能有：

l 动态地建立、初始化和维护与 LDAP 目录服务相关联的 ldapRealm。

l 使用管理器的“安全性”页面关联WebSphere应用服务器资源和 ldapRealm。

l 将WebSphere应用服务器存取控制表（ACL）与 ldapRealm 的资源关联起来。

    IdapRealm 是一个包含已定义在目录服务中的用户和组的WebSphere应用服务器安全性领域。可以使用WebSphere应用服务器的管理器界面的“安全性”页面，以建立存取控制表和 IdapRealm 之间的关联。 ldapRealm 对资源进行保护，并且存取控制表允许属于该领域的用户和组（在这种情况下，用户和组在目录服务中）访问资源。

ldapRealm 使用非 SSL LDAP V2 协议来与目录服务进行通信，并根据在“目录管理”页面中指定的设置进行联编。当受 IdapRealm 保护的WebSphere应用服务器资源需要认证时，将根据IdapRealm 来验证客户机的用户标识符和口令。该过程如下：

l 客户机向WebSphere应用服务器发出 Servlet 请求。

l WebSphere应用服务器确定请求的资源是否受 ACL 保护。

l 因此WebSphere应用服务器将一个认证要求发送给客户机。

l 客户机输入用户标识符和口令。

l 目录服务验证用户标识符和口令与保护资源的 IdapRealm 是否相符合：目录服务发出 LDAP 查

找，以查找与指定用户标识符相符的人。为该用户创建一个分辨名（DN）。

l 目录服务使用该用户分辨名和口令执行一个 LDAP 连接。

l 当目录服务认证用户后，WebSphere应用服务器Java 引擎将调用 Servlet 以处理与客户机获得的

许可权一致的客户机请求。

可以通过访问ldap://act:389/o=raleigh.ibm.com来测试目录服务是否正在运行，其中：act 是运行目录服务的机器的主机名，389 是服务的端口号，o=raleigh.ibm.com 是基本分辨名，它还代表了目录服务中的搜索起始点。


WebSphere快速入门(3)

作者：easyinfonet

email: easyinfonet@sina.com.cn

日期：2001-2-16 11:21:26

2.1 基本系统模型

    电子商务应用框架为设计电子商务解决方案提供了一个模型。这个框架基于一个多层的分布式环境，在这个环境中，任何多的各层应用逻辑和商业服务分离为各种组件，这些组件通过网络相互通信。在它的最基本的形式中，可以被描述为一个“逻辑上”的3层计算模型，即分层是在逻辑上的，并不要求是物理上的。这个基本的3层系统模型包括客户、Web 应用服务器、服务器（见图2-1）。在这3个逻辑层中的应用元素通过一组业界标准的协议、服务和软件连接器互相连接起来。

图2-1:基本系统模型

1.    客户。这个应用框架支持广泛的客户端设备，从个人数字助理（PDA）、智能卡（smartcard）、数字无绳电话等大众普遍使用的设备到网络计算机和个人计算机。将这些客户端设备连结到Web应用服务器的思路是使用一组被广泛支持的基于Internet的技术和协议。客户端的主要作用是将应用产生的结果信息显示给用户。正因为如此，这种客户通常被称为“瘦客户”，也就是说在客户端执行的应用逻辑很少或没有，这样，很小的软件（如Web浏览器）需要在客户端安装。

2.    Web应用服务器。Web应用服务器是这样的一个平台，它为应用的业务逻辑提供了一个运行环境。它包括HTTP服务器和企业Java服务，支持分布式网络环境下应用软件的快速开发和部署。应用软件在Web应用服务器及其内嵌的JVM（Java Virtual Machine，Java虚拟机）中运行。这些服务端的组件通过HTTP或IIOP（Internet Inter-ORB Protocol）与客户和其它组件通信，并利用网络基础架构提供的目录和安全服务。这些组件还可以利用数据库、事务处理、和群件等设施。

3.    连到外部服务的连接器。外部服务通常是企业在信息技术上多年投资的结果，是人们日常工作所依赖的应用和数据。这些应用和数据是重要的商务资源，需要以一种安全且可控的方式连接到Web上，使得企业充分发挥它们的作用为顾客、业务伙伴和员工服务。连接器就是使得它成为现实的一种机制。连接器将中间层内新增的业务逻辑连结到企业已有的应用和数据，从而将Internet的力量无缝地连接到企业中来。

这个电子商务基本系统模型集中体现了面向Web的网络计算风格，并结合了显示、业务逻辑、数据存贮这3层应用元素。这个基本系统模型的特性如下：

1.    基于Web浏览器/Java applet使能的广泛的客户连接。

2.    易管理的客户，通过配置需要很少或无须本地的软件安装和数据备份。

3.    写一次、到处可运行的应用软件的快速开发及即时部署。

4.    提倡软件复用，使得新添程序量最小化、生产效率最大化，并提高软件质量。

5.    与外部服务的连接，在这些外部服务系统中驻留着已有业务应用和数据，充分发挥它们的作用为顾客、业务伙伴和员工服务。

2.2 体系结构

    电子商务应用框架的体系结构为开发和部署电子商务应用系统提供了一组完整的服务。这个体系结构由下列关键元素构成（参见图2-2）：

图2-2：应用框架的体系结构

1)    客户基于Web浏览器/Java applet模式，使得对应用系统的存取变得很普遍，并且应用组件能即时发送。

2)    网络基础设施提供了如TCP/IP、目录和安全等服务，这些服务的能力可通过开放且标准的接口和协议来存取。

3)    应用服务器软件为电子商务应用系统提供了一个平台，包括HTTP服务器、数据库和事务处理服务、邮件和群件服务、和消息服务。

4)    应用集成使得异构应用系统间相互通信，使得Web能存取现有数据和应用系统。

5)    Web应用编程环境为创建动态和健壮的电子商务应用系统提供了服务端Java编程环境。

6)    电子商务应用服务为方便电子商务解决方案的创建提供了高层应用的特定功能。

7)    系统管理用来满足网络计算的管理需求，系统中的元素包括用户、应用、服务、基础构架、和硬件。

8)    开发工具用来创建、组装、部署、和管理应用系统。

1.    客户

    客户通常为“瘦客户”，也就是说在客户端执行的应用逻辑很少或没有，这样，很小的软件（如Web浏览器）需要在客户端安装。在这个模型中，应用被安排在服务器上并动态地即时（on-demand ）下载到发出请求的客户。正因为如此，新一代应用的客户端部分应该用HTML、DHTML、XML、Java applets来实现。这种新一代应用支持广泛的客户端设备，从大众普遍使用的个人数字助理（PDA）和智能卡（smartcard）等设备到网络计算机和个人计算机。

2.    网络基础设施

    它为整个体系结构提供了一个安全的可伸缩的分布式网络平台，包括下列均基于开放标准的服务：

1)    TCP/IP和网络服务。如DHCP和WAP，DHCP为进入网络的设备动态地分配IP地址，而WAP（Wireless Application Protocol、无绳应用协议）将信息和电话服务发送到移动电话和其它的无绳设备。

2)    安全服务。基于公开密钥技术的安全服务支持用户辨认和鉴别、存取控制、保密、数据完整性和交易的无否认性。

3)    目录服务。基于LDAP(Lightweight Directory Access Protocol、轻型目录存取协议)的目录服务定位网络中的用户、服务和资源。

4)    文件和打印服务。可以通过Web浏览器来存取和管理文件和打印服务

3.    应用服务器软件

    应用服务器软件层为开发和支撑运行在Web应用服务器上的电子商务应用系统的商务逻辑提供了核心功能。包括下列服务：

1)    HTTP服务器。它协调、收集并组合包含静态和动态内容的Web页面，并将它们发送给客户。

2)    邮件和社团服务。它们提供e-mail、工作日历、小组工作安排、聊天、新闻组讨论等服务。

3)    群件服务。它提供一个丰富的共享的虚拟工作间，并支持业务工作流程的协调。

4)    数据库服务。它将一个面向对象数据库的特性及功能与Web应用服务器集成起来。

5)    事务处理服务。它通过提供一个高可用的、健壮的、可扩充的、安全的事务处理应用执行环境来扩展Web应用服务器的功能。

6)    消息发送服务。它提供健壮的异步通信和消息代理设施来支持通信的发表/订阅模型和消息转换。

4.    应用集成

    应用集成部分使得异构应用系统间在企业内或跨越企业相互通信，这些异构应用系统可能是用不同的程序设计语言实现的，也可能建立在不同的体系结构之上。企业目前的大量关键数据和应用程序（特别是事务处理程序）驻留在已有的系统中。应用集成使得Web客户以及服务器能与企业已有系统中的数据和程序一起工作，将Internet的力量无缝地连接到企业中来。所支持的集成方法包括连接器、应用消息发送服务、商务过程集成与工作流服务、组件集成服务。

5.    Web应用编程环境

    Web应用编程环境基于Java servlets、Java Server Pages (JSP)、Enterprise Java services 和 Enterprise JavaBean 组件模型, 为创建在Web应用服务器上的动态和健壮的商务应用提供了编程环境。该环境提供了有关服务来鼓励业务逻辑与显示的分离，使得应用能按用户兴趣和客户端设备来动态剪裁内容。

6.    电子商务应用服务

    电子商务应用服务部分便利电子商务解决方案的创建。该部分包括的是面向高层应用的组件。它们建立在应用服务器软件及网络基础设施的基础上，并面向特定应用类型的功能需求按照应用框架的编程模型来实现的。比如支付服务和定单管理服务。

7.    系统管理

    对于企业内部，系统管理服务为支持端到端的管理提供了核心功能，提供的有关工具和服务来支持应用系统整个生命周期的管理，从安装和配置到运作特性的监控。跨越企业，系统管理服务提供一个协作管理途径，包括策略管理和数据仓库等。

8.    开发工具

开发工具用来创建、组装、部署、和管理应用系统。


WebSphere快速入门(4)

作者：easyinfonet

email: easyinfonet@sina.com.cn

日期：2001-2-16 11:22:45

2.3 Web应用编程模型

    Web应用是这样的应用系统，它利用Web客户（如Web浏览器）、Web服务器和标准的Internet协议。通常，Web应用也能利用来自外部非Web服务的应用系统和数据。Web应用编程模型定义了Web应用拓扑结构以及使用框架提供的服务来设计Web应用的模型。

图2-3：Web应用的拓扑结构

图2-3显示了Web应用的拓扑结构（包括主要的元素）。值得注意的是，Web应用服务器和外部服务是不同逻辑层，它们可能运行在同一台机器上。另外，Web应用服务器的功能可能分散在多台机器上。通常，一个Web应用中的前端和业务逻辑部分运行在不同的机器上。Web应用的拓扑结构包含的元素有客户、Web应用服务器、基础设施服务和外部服务。

1.    客户

    客户使用Internet技术标准(如TCP/IP、HTTP、HTML和XML)与Web应用服务器通信来存取业务逻辑和数据。客户端的基本功能是接受并验证用户输入，显示从Web应用服务器到用户的返回结果。客户可以是Internet 、Intranet（内部网）和Extranet（外部网）中的客户。Web应用编程模型的重要准则之一是Web应用的商务逻辑总是运行在服务端而不是在客户端。其优点如下：

1)    支持更广泛的客户端设备

2)    Web应用服务器能集成对资源（如数据库）的存取，从而简化应用的设计，增强可伸缩性，并提供对资源的更好保护。

3)    运行在服务端的商务逻辑容易得到保护、更新和维护。

4)    运行在服务端的商务逻辑使得用户的应用环境得到集中管理并能在不同的客户机上重建。

2.    Web应用服务器

Web应用服务器是 Web应用拓扑结构的核心，它为Web应用提供了广泛的程序设计、数据存取和应用集成等服务。我们可以把一个Web应用看作一个客户与Web站点之间一系列的交互作用。整个交互过程从显示在Web浏览器中一个页面开始。用户单击该页面上的一个按钮或链接就产生一个请求，该请求被送到Web应用服务器。Web应用服务器对这个请求进行处理，产生新的页面，并送回到客户端。在Web浏览器中显示的新页面就是这一次请求的结果，可能也是下一次请求的开始。所以说，Web应用包含了一组交互或处理步骤，每一步必须产生一个页面形式的响应，这个页面作为后继交互作用的入口。

图2-4：Web应用的交互模型

深究单个交互的细节，不难发现这里有三个共用的处理要求，包括业务逻辑层、用户界面层和交互控制器层（参见图2-4）。具体来讲：

1)    业务逻辑层。记录并处理用户输入的部分。比如：对应于在购物篮中添加一项商品的数据库更新操作，或从一个银行帐户到另一个的资金转帐操作。

2)    用户界面层。构造HTML页面的部分。构造出来的页面将被送回给用户，它决定了交互结果的显示形式和风格。

3)    交互控制器层。控制其它元素的部分。处理HTTP请求，从业务逻辑层选择要运行的组件，根据业务逻辑的运行结果从用户界面层选择相应的组件，以构造响应页面。

这三层正好对应到经典的M/V/C（Model/View/Controller，模型/视图/控制器）范例。这一点很重要，因为不同层通常需要不同的开发技术和工具。应用框架为各层提供使用各种组件的支持，这些组件之间有一个良定的接口。

3.    基础设施服务

除了上述程序设计机制, 应用开发者还需要运行时刻的服务来支撑Web应用。方便地定位应用组件、保证它们的可用性以及对它们安全的存取和执行等等都是在Internet、Intranet和Extranet环境中实施Web应用的关键要素。基础设施服务为支持Web应用提供下列设施：

1)    目录服务，本框架支持使用JNDI来存取基于LDAP协议的安全目录服务。

2)    认证授权，这个设施能产生用来鉴别用户和服务器的证书。公开密钥技术已经成为电子商务首选的可信赖的机制，而认证授权是公开密钥技术中的一个关键部分，它提供了数据保密、数据完整性、签名验证和用户鉴别等特性。

3)    防火墙，它作为可信赖的内部网络和不可信赖的外部网络之间的一个接口。通常，防火墙使用报文过滤器根据报文的源地址、宿地址、和服务类别（即端口号）来过滤报文流。使用防火墙来控制外部网络中的那些（IP级）能允许存取内部网络、能使用那种类别的应用服务；同样的模式能控制内部网络对外部网络的存取。

4)    代理服务器，它起到为多个浏览器检索Internet上数据的作用。作为客户和Web应用服务器之间的一个管道，代理服务器将客户的请求递交给有关的服务器并把服务器的响应返回给客户。这种工作方式还可以提供附加功能，如地址安全性和缓冲功能。

4.    外部服务

在这些外部服务系统中驻留着企业已有的应用和数据、以及商业伙伴的服务系统，如支付服务和外部信息服务。通常，这些应用和服务系统控制着关键的商务过程，需要与Web服务器集成起来为顾客、业务伙伴和员工服务。

使用电子商务应用框架开发和部署电子商务应用系统带来下列优势：

1)    极大地方便电子商务应用系统的快速开发和部署。

2)    适应任何客户端设备。

3)    确保应用系统能移植到各种不同的服务器环境中。

4)    利用并扩展已有的信息技术资源。

这也是形成这个电子商务应用框架的重要原则。


WebSphere快速入门(5)

作者：easyinfonet

email: easyinfonet@sina.com.cn

日期：2001-2-16 11:24:37

2.4 WebSphere

电子商务的灵魂是软件，而电子商务应用系统通常采用以Web服务器为中心的计算模式（参见基本系统模型），因此，Web应用服务器软件是电子商务市场的焦点。IBM WebSphere Application Server(WebSphere应用服务器)是众多Web应用服务器软件中的杰出代表。

事实上，IBM WebSphere产品系列是一套典型的电子商务应用开发工具及运行环境。该产品系列包括：

1)    NetObject Fusion提供许多构建和组织站点的工具，可用来建立和管理整个Web站点；

2)    NetObject BeanBuilder是一个构建applets的可视化写作工具；

3)    NetObjects ScriptBuilder提供了Smart HTML、JavaScript、JSP等的编辑器；

4)    Lotus Domino Go Webserver是一个Servlet使能的Web服务器；

5)    IBM WebSphere Studio提供了项目工作台和有关向导，这些向导可用来存取数据库、创建JavaBean和servlet等等；

6)    IBM WebSphere Application Server (WebSphere应用服务器)是一个Web应用服务器（内含IBM Http Server）,它本质上是适合于servlet的Web服务器插件，提供了增强的Servlet API和Servlets管理工具，并集成了JSP技术和数据库连接技术。

第三章 WebSphere Studio应用开发

本章结合IBM WebSphere Studio产品的使用，介绍电子商务应用开发技术。

3.1 Lotus Domino Go Webserver

    Lotus Domino Go Webserver是一个Web服务器软件。它具有下列特性：

    = 支持CGI、Servlet和一些其它的应用编程接口。这些应用编程接口用来创建动态页面。

    = 具备代理服务器（Proxy Server）的功能。一个代理服务器是一个请求代理（Broker）。浏览器把请求发到代理服务器，代理服务器从网上或从缓存里取这个页。

    = 支持SSL 3.0。SSL用来确保在web服务器和web浏览器之间安全通信。

    = 支持PICS（Platform for Internet Content Selection）。PICS使得用户可以过滤他们遇到的材料，按照材料的分级（由Web管理员设置）接受或者拒绝材料。

    = 集成了搜索引擎。集成的搜索引擎是NetQuestion.

    = 易维护性。如很容易地维护用户和访问控制列表

    下面就Domino Go Webserver的特性及有关使用技术加以详述，包括Web服务器的配置、启动Web服务器、URL与目录映射规则、服务器日志、访问控制、代理服务器。

    1．Web服务器的配置

配置Lotus Domino Go Webserver有两种方式，一种是使用Web浏览器来配置和管理，另一种是直接编辑配置文件。第一种方法更简单。安装Lotus Domino Go Webserver 之后，启动Web服务器，使用web浏览器打开http://127.0.0.1/frntpage.htmlRules）。

    2．启动Widows NT上的Web服务器

    有两种方式来启动Widows NT上的Web服务器。第一种是从服务面板启动(NT Service Panel->StartUp Panel)，有3个可选的启动类型（Auto、Manual、Disable），并提供了从系统帐号启动的方式，当选择从系统帐号启动Web服务器时，还可以选择在桌面上启动图形用户界面来显示日志或跟踪信息。另一种是用命令行启动，命令的语法为：whttpg [-p port] [-r config_file]。其中，端口号（port）指定Web服务器侦听的端口，而配置文件名（config_file）指定Web服务器的配置文件，通常不是缺省的配置文件（在WINNT目录下的httpd.cnf文件）。

需要指出的是在一个Widows NT上可以运行多个Web服务器，但是有两个注意事项。第一，同时运行的Web服务器必须使用不同的端口。第二，最多有一个Web服务器可以从服务面板启动，也就是说，附加的Web服务器需要用命令行启动，并使用不同的端口，但可以使用同一个配置文件（建议使用不同的配置文件为妥）。另外，一个Web服务器可以有多个IP地址，每个IP地址有不同的主机名，这称为多IP服务器。一个Web服务器也可以只有一个IP地址，但是有多个主机名，这称为虚拟主机。多IP和虚拟主机可以为不同的虚拟主机提供不同的信息，尽管不同的虚拟主机是由同一个Web服务器服务的。

    3．URL与目录映射规则

    URL（Uniform Resource Locator、统一资源定位器）是指定Web上资源项的地址的约定规则。它包括协议、后面跟着完整的主机名、和请求。一个URL的语法时：

协议：//主机名[：端口][/[目录名[/...][/文件名[？变量名= 值[&...]]]]]。

协议指的是提供http、https、ftp等服务的有关协议，Go WebServer服务器提供自己的http和https服务，并代理ftp、gopher、wais等服务。通常，Web服务器把URL的主机名和端口后面的部分映射到一个路径和文件名，即所谓的目录映射。Go WebServer的Web文档根目录缺省为c:\WWW\HTML，在安装该Web服务器软件时可以指定Web文档根目录。当服务器收到的URL请求没有规定文件名字时，Web服务器会在相应的目录里搜索某个文件名列表，该文件名列表称为欢迎页面(Welcome Pages)列表。欢迎页面列表通常包括Welcome.html和index.htm等。关于目录映射，Go WebServer使用URL传递规则来实现所需的目录映射。传递模板及示例如下：

    传递模板          动作                 请求模板                      替代的文件路径

      示例            Pass                   /test/*                        c:\myhome\test\*

其中，请求模板代表被请求的URL，可以包含（*）通配符。当一个URL匹配请求模板时，Web服务器按照替代的文件路径访问系统资源。这些规则可以用来维护物理文件系统的独立性。而动作类型及其含义如下:

    (1) Map：Web服务器按照替代的文件路径改变URL，继续比较。

    (2) Pass：Web服务器接受请求。

    (3) Fail：Web服务器拒绝请求。

    (4) Exec：Web服务器接受请求，并按照替代的文件路径运行一个CGI程序。

    (5) Redirect：Web服务器使用一个由替代的文件路径确定的URL来响应。

    (6) NameTrans：Web服务器接受请求，在处理请求的名字转换步骤中，运行一个由替代的文件路径

确定的API应用。

    (7) Service：Web服务器接受请求，在处理请求的服务步骤中，运行一个由替代的文件路径确定的API

应用。

    如果设置了下列传递规则：

    = Map    /beta/*      /text/*

    = Exec   /cgi-bin/*    c:\www\cgi-bin\*

    = Pass   /*           c:\www\html\*

    = Pass   /abc.html     c:\abc.html

URL请求与实际的系统资源之间的对应举例如下：

表3-1：URL到系统资源之间的对应举例

     URL请求                            对应的系统资源    应用的规则

    /cgi-bin/search.exe                    c:\www\cgi-bin\serch.exe    规则1）、2）

    /index.html                            c:\www\html\index.html    规则3）

    /beta/download.html                    c:\www\html\test\download.html    规则1）、3）

    /abc.html                                c:\www\html\abc.html    规则3）

在匹配了Map规则以后，服务器会继续比较余下的规则。服务器会在Exec和Pass规则后停止比较。第4个规则永远不会被使用，因为任何可以匹配第4个规则的都会匹配第3个规则。因此，Pass /* 规则通常应该放在最后。


WebSphere快速入门(6)

作者：easyinfonet

email: easyinfonet@sina.com.cn

日期：2001-2-16 11:25:27

4．服务器日志

    缺省地web服务器会在c:\www\logs目录下记录所有东西，Web服务器每天在午夜启动一个新的日志文件（如果那时它在运行的话）。否则，Web服务器会在某天首次启动时开始一个新的日志文件。日志文件通常包括下列类型：

    (1) httpd-log：记录Web被访问的统计情况。

     (2) agent-log：记录客户使用的Web浏览器的情况。

     (3) referer-log：记录引用到请求页面的页面的情况。

     (4) httpd-error：记录Web服务器内部错误的情况

     (5) cgi-error：记录CGI错误的情况。

    日志的删除可以选择在若个天后删除，或在若个MB存储空间被用完后删除，或运行“User Exit” 删除。也可以通过配置:对某些请求不作记录。

    5．访问控制

    Web服务器中的资源通常需要各种不同程度的保护。可以在Web服务器的配置文件里进行保护设置，也可以在每一个目录里设置ACL（Access Control List），ACL是一个包含保护规则列表的文件，用来保护同一个目录的文件。保护的类型有用户名保护和地址保护等方式，用户名保护决定谁可以访问这个资源（可以要求口令验证），地址保护使用地址模板来决定哪些客户机（IP地址）可以访问这个资源。

    6．代理服务器

    代理服务器是一个可以为它的客户从别的服务器那里取得文件的服务器。一个代理可以降低网络流量，对Internet隐藏内部网信息。可以代理http、ftp、gopher、wais和SSL隧道（如https和snews），通常通过设置URL传递规则来实现。为启用SSL代理，还必须启用connect方法。要使用Web服务器提供的代理功能，浏览器必须设置该Web服务器为代理服务器并指定这个Web服务器的端口。

启用的代理缓存功能可以提高运行效率。代理缓存是在代理服务器端保存http、https或ftp等缓存文件以减少不必要的重复的请求处理。有几个设置参数需要注意：缓存文件的一个给定的存储空间称为缓存容量，存放缓存文件的目录称为缓存文件的根目录，对某些URL进行缓存（其它的不需要）称为缓存过滤。另外，未被使用的缓存文件的时间限制用来规定服务器保存未被使用的缓存文件的最长时间，而存储空间回收用来移走过期的缓存文件。


WebSphere快速入门(7)

此电子文档属C#乐园QQ群3256045整理。
WebSphere快速入门(7)

上一页 目录 下一页

WebSphere快速入门(7)

作者：easyinfonet

email: easyinfonet@sina.com.cn

日期：2001-2-16 11:26:26

3.2 IBM WebSphere Studio

    Web站点的内容是由静态和动态内容组成的。静态内容以HTML、图片等形式出现。除了在客户端的动态效果以外，在一个Web服务器上动态内容通常以服务器端程序的形式存在，它们在被请求时产生动态内容。许多站点已经使用applet来达到在web上的动态效果。虽然这个方法能做到这一点，但是java和网络性能的问题阻碍了这种方法的大规模应用。服务器端生成动态内容在大多数情况下是更好的方法。因为：

    = 更容易支持瘦客户。

    = 提供更大的灵活性和安全性。

    = 在大多数情况下动态内容来自数据库。

    = 服务器端程序可以执行数据库操作，然后向浏览器报告结果。

    服务器端生成动态内容符合瘦客户或超瘦客户设计模式，它使得软件开发者在一个web应用的客户端放尽量少的商务或数据访问逻辑。而胖客户是指在一个应用的客户端放置了太多商务逻辑和访问。这会影响网页的下载时间，可能引起其它问题。服务器端数据库访问是一个好的解决方案，可以更好地利用服务器，从客户减轻客户端的负担。通过Java访问数据库是利用JDBC(Java Database Connectivity)应用编程接口进行的。总之，在为一个网站提供动态内容时，会涉及到服务器端应用程序。CGI和/或Servlets将是典型的部署载体。Servlet是生成动态内容的焦点。然而，大多数动态内容会涉及到数据库。

WebSphere Studio提供了项目工作台和有关向导。项目工作台用来把各种生成的组件集成进一个项目，而各种向导可用来存取数据库、创建JavaBean和servlet等等。

    1．项目工作台

    项目工作台是一个在资源控制管理下的Web站点对象管理工具，其站点管理能力与NetObject Fusion大致雷同。项目工作台具有下列特性：

    (1) 同任何网站开发工具互操作。通过将站点对象的文件类型注册到相关的应用，项目工作台可用来启动应用（工具）来操作（打开，显示）站点对象。从而能有效的管理那些与站点有关的对象资源，与资源控制环境的交互使得Web站点的小组开发得以有效地进行。因此它是能集成很多网站开发工具的理想的工具集成环境。

(2) 提供灵活的站点发布能力。项目工作台为两类对象资源提供各自独立的发布路径，一类是Servlets和JavaBeans，另一类是所有其它的东西。项目工作台提供了网站的本地发布或远程发布两种方式。

WebSphere Studio 有一个双框图形用户界面，还有许多与桌面应用程序相似的功能特性。以下分别介绍框架、分解视图、具栏和按钮、过滤器。

(1)     框架

左边的框架显示所选项目及其文件夹的图形分层。右边的框架显示在左边框架中选择的一些项目的细节。

l    一个项目可包含文件和文件夹。

l    文件夹可包含文件或其它文件夹。

l    文件包含数据。

您可以按喜欢的方法来组织文件，并使它容易地将一个项目中的有关文件作为一个单元进行管理，比如，公布一个项目中的若干个文件或所有文件至目标服务器上（如 WebSphere 应用服务器）。

(2) 分解视图

当您从视图菜单中选择分解视图表，文件夹及子文件夹中的所有文件将显示在详细视图表中。表格包括附加的路径信息以帮助您区分它们。文件夹本身不显示。 快捷方式按钮位于工作台的右下方，您可对分解视图的开或关进行切换。

(3) 工具栏和按钮

此界面包括菜单栏、工具栏、状态栏和过滤器按钮的垂直行。

l    菜单栏允许您访问所有的可用功能。

l    工具栏允许您快速访问大多数的普通功能和向导。

l    过滤器通过文件扩展名或文件类型来控制显示文件。

l    状态栏列出一般信息，例如您的视图的大小和对象的数目，也包括被过滤器隐藏的文件数。

(4) 过滤器

过滤器按钮位于主窗口的右边。它通过文件类型和文件扩展名来控制哪些文件显示在详细框架中。单击它可进行开和关的切换。打开过滤器，请单击过滤器按钮，在详细视图中将显示此种文件类型的文件。过滤器类别是：HTML 文件、图像文件、SQL 文件、Java 文件、Java 类文件、文本文件。同时要打开多个过滤器，请在单击每个过滤器按钮时按下 Ctrl 键。要暂时关闭所有过滤器，请先从视图菜单中选择“过滤器”，然后选择“显示全部”。

在分解视图上使用过滤器能够管理在分组中选择文件类型的所有文件，而不考虑它们的相对位置。 例如，若要打开一个包含文件夹及子文件夹的项目，但其中只有 HTML 文件， 您就可打开分解视图和 HTML 文件过滤器来显示整个项目中的所有 HTML 文件。

    2．向导

    有关向导及其基本功能如下：

    (1) SQL 向导：创建一个可被数据库访问 Servlet 使用的 SQL 语句。语句可查询关系数据库，并从表中选择数据显示在 Web 页面上。SQL向导要求连接数据库，以验证连接参数，提取元数据来支持SQL语句的开发。

    (2) 数据库访问向导：创建 Servlet 来访问关系数据库。使用一个生成的SQL文件，数据库访问sevlet向导生成5个文件：servlet、servletBean、输入HTML表格、输出页面（JSP）、和可选的错误页面（JSP）。其中，可选的输入表格（HTML）用来为SQL命令传参数，ServletBean提供一个可执行方法来调用SQL命令，Servlet创建那个ServletBean的一个实例，执行它的方法，然后决定调用有关结果页面。

    (3) 基本 Servlet向导：创建使用指定的 JavaBean（包括 Navigator 和为 VisualAge for Java Enterprise Access Builder 建立 VisualAge 时的命令）的 Servlet。它同时产生一个调用 Servlet 及捕捉用户输入的输入页面和显示结果的输出页面。

    (4) 到期 HTML Servlet向导：创建随日期而更改内容的Servlet。则它生成 Servlet 以及包括 HTML 的变量部分的页面。

    (5) 注册Servlet向导：创建一个可在 WebSphere应用服务器 的 UserProfile 特性中设置或获得数据的Servlet。这使可维护 Web 访问器的详细信息，并且此访问器对于用户注册和其它的 Web 应用程序都是一样的。 向导同时产生一个调用 Servlet 及捕捉用户输入的输入页面和显示结果的输出页面。

    (6) JavaBean向导：创建能够进行编辑和扩展的JavaBean的基本实现。 指定在 Bean 中所需的属性，向导将创建方法来设置和获得其值。 一旦完成 Bean，就可在基本向导中使用它。

上述向导对快速地制作用于创建和显示动态内容的中间层（和客户层）元素的原型有用，对提供模板代码来定制有用。而且与WebSphere应用服务器的“编程模型”紧密集成。下面以使用数据库访问向导为例来创建访问数据库的Servlet。数据库访问向导帮助创建Java Servlet，它用来从关系数据库中检索指定数据并在 HTML 页面中显示信息。

    向导需要 SQL 语句文件，Studio SQL 向导将帮助创建它。该 SQL 语句告诉数据库访问 Servlet 连接哪个数据库、搜索哪个表格、选择哪个数据以及如何排序。实际上，无需知道 Java 或 SQL 语法，就可创建数据库访问 Servlet。浏览至想放置 .jsp 文件的文件夹。

    从“工具”菜单，选择“Studio 向导”。

   (1) 选择“数据库访问 Servlet”。

    (2) 单击“下一步”启动向导，继续标记的页面。

    (3) 当完成所有要求后单击“完成”。

    (4) 当完成后，可“照原样”使用文件或根据需要进行定制。

    向导将创建：

   (1) 调用 Servlet 的输入页面（.html）

    (2) 提供实现指定 SQL 语句数据库访问逻辑的 JavaBean 文件（.class 和 .java）

    (3) 配置 Servlet 的 Servlet 配置文件（.servlet）

    (4) 包含选中数据库列 JSP 和 HTML 的输出标记的 JSP 文件（.jsp）

    (5) 按照下列步骤完成数据库访问 Servlet 向导：

    注意：可使用这样的方式建立和测试数据库Servlet，使用某一现有数据库，通过修改 .servlet 配置文件中的一个或多个以下值，将其创建为另一个 Servlet：

    = URL

    = driver

    = userID

    = password

    此方法可能需要数据库服务器的模式别名。请参阅 Servlet 配置文件，了解更多关于 .servlet 文件的详细信息。

    3．脚本编辑

    IBM WebSphere Studio能与NetObjects ScriptBuilder很好的集成起来。从WebSphere Studio的项目工作台项可以启动ScriptBuilder来对有关文件进行编辑，可编辑的文件类型包括HTML、JavaScript、Java、JSP和XML等。

    NetObjects ScriptBuilder是适合于Web的脚本开发工具，为Web站点的客户端和服务器端脚本提供了一个快速的开发方法。它将强大的脚本编辑器与丰富的可视化开发工具有机结合，以加速脚本的开发。它也是架构Netscape,、Microsoft、 IBM、Sun Microsystems等各大公司的不同Web脚本环境的桥梁。它支持CFML (Cold Fusion Markup Language)、Dynamic HTML、 ECMAScript、 HTML、JSP、Java、LotusScript、ASP、CDF (Channel Definition Format)、DOM (Document Object Model)、Jscript、VBScript、JavaScript、LiveWire、Perl、和WML (Wireless Markup Language)。使用NetObjects ScriptBuilder, 开发者能够：

   (1) 检查脚本的语法错误

    (2) 创建面向对象的脚本组件。

    (3) 访问语言的参考书目

    (4) 使用XML定制语言参考书

    (5) 检查文档与浏览器的兼容性

    (6) 把所需的语言元素拖放到文档中。

    (7) 使用标准的Web语言或其它语言

    (8) 快速导航到嵌入的函数和组件

    (9) 自动化重复性的任务

    (10) 预览文档


WebSphere快速入门(8)

作者：easyinfonet

email: easyinfonet@sina.com.cn

日期：2001-2-16 11:27:31

3.3 NetObjects Fusion

    NetObject Fusion是一个可视化的Web站点的构建和管理工具。NetObjects Fusion是Web站点构建过程的中心。NetObjects Fusion是一个面向站点的工具，而不是仅仅处理单个页面。在Web站点开发的过程中，会创建许多页面，页面之间有很多链接。手工地更改这个结构是相当困难的。用NetObjects Fusion可以设计Web站点的通用元素，创建并维护Web站点的结构，构建各个页面，并能容易地维护这些页面。

总之，使用NetObject Fusion，可以设计Web站点、构建各个页面、容易地维护Web站点。下面介绍NetObject Fusion 中的站点视图（Site view）、页面视图（Page view）、样式视图（Style view）、对象集视图（Assets View、或称资源视图）、和发布视图（Publishing view）。

    1．站点视图

    站点的结构视图（参见图3-1）是站点视图的一种，单击“轮廓视图”标签就显示站点的轮廓视图。在结构视图中，页面的颜色是可以由开发者选择，通常用来为页面分组，但是页面颜色对实际页面没有影响。用户站点的文件格式为“*.nod”，模板文件格式为“*.nft”， 样式文件格式为“*.ssf”。使用站点视图，可以创建（或修改）一个站点的层次结构、操纵页面、引入/导出（Import/Export）站点、引入模板以改变站点的风格、打印站点的结构图等等。例如，可以引入一个已有的站点将它转成NetObjects Fusion的格式或保留为外部HTML页面。如果这些被引入的页面不会被改变，通常可以将引入的页面保留为外部HTML页面。

图3-1：站点视图

    2．页面视图

站点的页面视图（见图3-2）包括页面布局（Page Layout）和主控边框（MasterBorder），并提供面板和工具栏。面板包括属性面板和对象树，工具栏包括标准工具栏、表单工具栏、组件工具栏、和高级工具栏。对象树是关于当前页面中所有对象的树状结构，开发者可以通过对象树来定位目标对象，也可以直接选择目标对象。一旦选定目标对象，属性面板就显示该对象的属性，对象属性依赖于对象的类型，不同类型的对象（如文本和applet）有不同的属性，可以使用属性面板来设置对象属性，当然有的属性可以可视地编辑。

在页面视图中，可以可视地或通过属性面板来定义主控边框和页面布局。主控边框勾画出一个页面的五个区域：中间的布局区域和四个（左、右、上、下）边缘区域。这些边缘区域包含多个页面共有的元素，如水平标注和导航条，可以通过属性面板设置主控边框的自动边框（AutoFrame）属性，把某些边缘设置成框架（frame）。关于布局，而布局区域用来放置该页面的内容。需要注意的是页面布局包括页面的实际内容，不仅仅是页面的布局，也许一个页面有好几个布局或版本，例如有框架（frame）的和没有框架的版本。NetObjects Fusion使用表格（table）来组织它生成的HTML页的内容，也可以通过设置布局属性来选择布局是表单。也可以通过弹出菜单或单击属性面板中的“HTML”按扭在来显示生成的HTML文档。

图3-2  页面视图

工具栏通常在窗口的边上，也可以拖进窗口里面（如图3-2）。工具栏包括标准工具栏、表单工具栏、组件工具栏、和高级工具栏。这些工具栏为页面的制作提供了许多实用工具。

    (1) 标准工具条

   = 选择（Select）：选择一个特定的组件并移到另一个地点或重新设置大小。

   = 缩放（Zoom In/Out）：使页面的视图围绕鼠标击中的区域放大或缩小。

   = 文本（Text）：向页面增加一个包含一些文本的组件。

   = 图片（Picture）：向页面增加一个图片。可以是一个.gif或.jpg文件。Fusion接受BMP，PCX，PICT，GIF，JPEG的图。

   = 热点（Hotspots）：把一个图片中的某个区域标记为可单击（链接到某个URL），从而使该区域成为热点。这个区域可以是圆形、矩形、和多边形

   = 布局区域（Layout Region）：在布局区域或主控边框里创建一个布局区域，它可以拥有与包含元素不同的布局属性（例如，一个不同的背景色）。

   = 表格（Table）：创建一个HTML表格，可以规定表格的行数和列数。

   = 表单区域（Form Area）：创建一个或多个表单。可以选择创建基于布局区域的表单、基于表格的表单、或基于文本的表单。也可以把一个表单加到边缘区域，从而能在多个页面里显示。

   = 导航条（Navigation Bar）：用来创建导航条。可以使用按钮或文字形式，从当前页面链向站点里的别的页面，比如在Web站点结构里的父层、当前层或子层。

   = 水平标注（Banner）。用来在页面的任何地方创建一个水平标注，反映这个页面的名字。通常在页面的顶部，来标志这个页面。

   = 绘制（Draw）：绘制工具，这些工具用来在一个页面里画圆形、矩形、圆角矩形和多边形。

   = 直线（Line）：画直线的工具，这些工具用来在一个页面里画水平线、任意角度的在线、站点样式的水平线。

    需要注意的是Hotspots、Draw、Line这三个工具还包括可选的子工具，按下按扭不放就会显示可选的子工具，保持按下按扭并移动就可选择所要的工具。

   (2) 表单工具栏

    这些工具用来创建表单。表单可以用来收集信息，然后送回服务器端CGI程序或Java Servlet。

   = 表单按扭（Button）：一个表单按钮通常被用来启动一个后端服务程序或来重置表单。

   = 表单复选框（Check Box）：一个复选框代表一个布尔值，选中表示真值，否则表示假。

   = 表单单选按扭（Radio Button）：一个单选按钮是一组单选按扭的一部分，这组单选按扭是互斥的，即只能有一个为真，而别的都为假互斥的。

   = 表单编辑字段（Edit Field）：表单上的一个区域，用户可以输入一行信息如名或姓。

   = 表单多行文本（Multi-Line）：表单上的一个区域，用户可以输入多行信息，如对一个产品的说明或目录里的一项。

   = 表单组合框（Combo box）：一个下拉列表，用户可以选择。一个例子是：头发颜色的列表。用户可以选一个。

   (3) 组件工具栏

    = 动态按扭（DynaButtons）：对按钮使用站点风格

    = 磁带（Ticker Tape）：一个水平滚动文字区域，不断重复。

    = 站点映射器（Site Mapper）：一个用来显示站点结构的按扭。

    = 消息板（Message Board）：一个BBS 设施

    = 表单句柄（Form Handler）：实现一个简单的CGI程序。用来检查表单中的字段

    = 图片反转（Picture Rollover）：一个反转按扭。当鼠标滚过图片，图片改变成指定的图片反转。当= 鼠标在图片上单击，图片会改变，一个URL链接会显示。

    = 基于时间的图片（Time Based Picture）：在这个地点的不同时间显示不同的图。

    = 图片加载器（Picture Loader）：使用其它资源中的图片

    = 旋转图片（Rotating Picture）：简单动画。通常用来显示广告。

   (4)高级工具栏

    = Media：一个子工具条，包括了一些项目象Shockwave、Quicktime、Video、Sound和别的插件

    = Java：允许往页面上加一个Java applet。

    = ActiveX Control：允许往页面上加一个ActiveX控件。

    = Data：一个子工具条，允许你访问内部和外部数据对象。

    = External HTML：包括一些需要特殊的、手工写的HTML页面。可以在外部引用HTML文件并把

它们放在站点的任何地方，而不是引入并转换这个HTML。NetObjects Fusion在发布Web站点时不分析这个HTML。

    上述工具栏提供的有关工具可用来增加页面的互动性。如可以在页面上增加脚本、放置ActiveX控件、增加Java applets等等。脚本可以加到一个Fusion生成的HTML元素的前面、后面或里面。增加Java applets时使用Java对象属性设置有关参数，Fusion将增加的class文件作为对象集(assets)中的元素。

    3．样式视图

样式视图（参见图3-3）可用来选择站点样式、编辑样式、或创建新的样式。Fusion提供的样式适合于简单的站点或者可用来取得用户的反馈信息，重要的站点通常需要创建新的样式。Fusion用样式中指定的字体生成水平标注和按钮上的文字。因此，应该为水平标注和按钮选择任何你的系统里的字体，Fusion将使用指定的字体生成有关图象，浏览器只看到一个图象。对于在HTML里创建的文字组件，浏览器可以控制自己使用的字体。

图3-3：样式视图

4.    对象集视图

对象集视图（参见图3-4）是一个管理所有对象的地方，包括文件、链接、数据对象、变量，对象通过别名来访问。Fusion提供预定义的变量，如日期、时间和站点名等等。开发者可以创建自己的变量，并在对象集视图中管理。每当开发者需要选择图象或别的文件时，打开文件对话框（Open File dialog）上会有一个对象集标签（Assets tab），单击该标签就显示对象集中的有关文件，开发者就可以选择所需要的文件。这种就对象进行统一管理的方式支持“一次更新，每个地方都更新”。例如标识公司的图案（logo），如果一个公司改变了它的标识图案，只要在对象集视图中改变有关标识图案的文件，Fusion可以改变这个站点里的每一页。

图3-4：对象集视图

5．发布视图

发布视图（参见图3-5）用来配置发布选项，并进行发布。发布选项包括本地发布位置和远程发布位置等。本地发布是指发布到本机的文件系统里，远程发布需要使用FTP把文件传输到服务器。如果你选择发布到一个远程站点，这个站点必须支持FTP，发布者必须知道所需的用户名和口令。通常在正式发布之前需要发布到一个临时服务器上进行测试，并需要使用不同平台上的不同浏览器进行测试。发布时，Fusion将生成HTML页面以及目录结构，拷贝所有的对象到指定的目录，这些目录里包含HTML文件、图象、Java 类等等。目录结构可以是单一的（flat）、基于对象类型的（asset）、或基于站点区域的（section）。所有生成的HTML使用相对路径。在发布之前还可以使用预览功能。预览创建HTML文件，生成任何需要的图象，但它不拷贝任何别的对象，从而不显示嵌入的组件。在按下CTRL 键时单击预览按扭就可以只预览当前页。

图3-5：发布视图

总之，Fusion是一个Web站点的组装工具，对管理站点范围的格式模板有强力支持，支持丰富的“对象”，链接和对象集管理使得部署到多个站点变得很容易。可以使用Fusion设计站点结构、构建和管理站点页面、设计和写页面内容、设计站点的图形化风格、管理和测试站点的对象集、把站点发布到本地或远程服务器、更新和维护站点。


WebSphere快速入门(9)

作者：easyinfonet

email: easyinfonet@sina.com.cn

日期：2001-2-16 11:28:36

3.4 NetObjects BeanBuilder

    NetObjects BeanBuilder是创建多媒体Java applet的可视化写作工具。它的前身是Lotus BeanMachine。NetObjects BeanBuilder使得开发者能用多媒体、特效、灵巧的表单和动态数据来丰富网站，而不要写一行Java代码，或者只需写一些简单的Java代码。BeanBuilder是通过把JavaBean结合进applet的可视化编辑工具来实现这一强大的功能。开发者只要从BeanBuilder的样式库（Palette）里选择丰富的可重用的JavaBean，设置几个属性，就完成了多媒体Java applet的开发。如果在样式库里没有需要的那个JavaBean，可以用BeanBuilder的部件引入向导（Import Part Wizzard）引入JavaBean或Java 类。甚至也可以引入已有的applet。

    1．Applet 概述

    Applet是Panel的一个子类，可以包含GUI组件。与一般的应用程序不同的是，applet只能在浏览器里运行。Applet使用称之为沙箱（“sandbox”）的安全模型。在这个模型中，网上下载的applet缺省地是不可信代码（不可信Applet），不可信Applet有下列约束：

    (1) 不能读或写本地文件系统；

     (2) 不能进行网络操作，除非是对于applet的源主机；

     (3) 不能装载某些类，如SecurityManage子类和URLContentFactory等；

     (4) 不能访问它所运行的线程组以外的线程或线程组；

     (5) 不能使用广播字节套（multicast sockets）；

     (6) 不能访问java.security包

    这个安全模型使得用户放心地使用applet。然而，这些安全限制使编程的任务变得更困难。在JDK1.1里，Applets可以被签署（sign）。如果运行时环境的安全政策认为“签署者”是可信任的话，这些被签署的applets可以被认为是“可信任的”代码。在JDK1.2里，所有的Java代码都是能被限制的或签署的。

    Applets通常被嵌入HTML文档中，applet标记是在一个Web页面里使用applet的标准机制。举例如下：

<Applet

        CODE= Register.class

        WIDTH=500

        HEIGHT=500

          CODEBASE= classes

          ARCHIVE= Register.jar>

        <PARAM NAME= fullName VALUE= "Henry Burns">

         <PARAM NAME= age VALUE= "12">

</Applet>

CODE，WIDTH，HEIGHT属性是必需的选项，其它的属性和包含的标记是可选的，如ARCHIVE属性和CODEBASE属性和PARM标记。有关属性和标记，及其它们的含义如下：

    (1) CODEBASE= Java代码所在的URL（不是document base）。CODEBASE允许规定.class文件所在的路径，使用页面服务器上的路径或URL。

    (2) CODE= Applet类文件的名字。CODE属性可以指定为在页面服务器上的路径或URL，值对大小写敏感，而且必需包括.class扩展名。

    (3) WIDTH= applet显示区域的宽度。

    (4) HEIGHT= applet显示区域的高度。WIDTH和HEIGHT用像素点规定浏览器定位的空间。

    (5) ALT=如果浏览器不能理解标记的话，要使用的文字

    (6) ARCHIVE=允许使用JAR文件，作为发现applet文件的地方

    (7) NAME= applet的名字。多个applets之间的通信要用到这个。

    (8) ALIGN= 告诉浏览器怎样在显示区域里排列applet

    (9) VSPACE= applet和别的组件在垂直方向上的间隔

    (10) HSPACE= applet和别的组件在水平方向上的间隔

    (11) PARM标记，要求一个NAME和一个VALUE成对出现。使用PARAM标记来指定一个applet

的参数。PARM标记必须在<APPLET>和</APPLET>之间出现。NAME部分是大小写不敏感的，而VALUE部分是大小写敏感的。所有传给applet的参数都被当作字符串。

     在上述嵌入HTML文档的applet例子中，Applet类由Register.class定义，该文件存放在Web服务器中HTML文档目录的classes子目录中，而且Register.class 文件很可能被包含在Register.jar 文件中。Jar文件是包括一个附加的”manifest”文件的压缩ZIP文件，通常用来把一个applet需要的二进制文件、源文件和资源文件等有关文件结合进一个文件。浏览器取得jar文件之后，需要解压缩得到所需的.class文件及可能的相关资源文件。Jar文件也可以用于Java应用程序或Servlet，但一般用于Applet。如果一个Jar文件用于一个应用程序或Servlet，在运行它们以前会有一个额外的解压缩的过程。对applets而言，这个额外的解压缩处理是值得的，因为节约了网络传输时间。最后，jar文件也可以被用作一个分发Java软件包的机制。

在applet代码中，程序员可以使用getDocumentBase()方法来获得包含applet标记的HTML页面的URL。使用getCodeBase（）来找出CODEBASE属性选项的设置。如果CODEBASE属性选项是空白的，getCodeBase()方法返回与getDocumentBase()一样的值。使用getParameter(String)方法取得参数的值，通常需要对传进Applet的VALU还要进行差错处理。

    2．标准样式库

    样式库中包含了许多用于组成applet的 bean。BeanBuilder 提供了缺省的样式库，为了把样式库中的某个 bean 放置在applet中，可以在样式库中单击这个 bean 的图标。此操作可将 bean 装载到光标上，并将光标变成十字指针形状，然后在设计器上再次单击鼠标放置 bean。样式库上的beans包括附件Beans、控件Beans、多媒体Beans、和网络Beans四种类型。

    A.附件分类

     (1) 布尔求值：用于对布尔值进行 AND 和 OR 运算；

     (2) 数学公式：用于执行基本的数学运算；

     (3) 数字求值：用于比较两个数字的大小；

     (4) 文本源：用于访问文本文件的内容，对于一个远程文件可以使用一个URL表示，或对于一个在

可信任的applet里的本地文件使用一个文件名表示。

    B.控件分类

    (1) 按钮：按钮 bean。

    (2) 复选框：复选框 bean。

    (3) 选择框：下拉列表bean。

    (4) 标签：显示单行文本的bean。

    (5) 列表框：列表框 bean。

    (6) 面板：可以包容其他 bean 并且具有布局样式的 bean。

    (7) 文本区：可以输入多行文本的bean。

    (8) 文本域：只能输入单行文本的bean。

   这些是标准的AWT控件，但是，这些控件提供的有关事件更丰富。

   C.多媒体分类

   (1) 动画：用于显示一组连续的图片。

    (2) 声音：用于播放声音剪辑。

    (3) 时钟：用于显示日期和时间。

    (4) 图像：用于显示图片文件。

    (5) 动作：用于沿着路径移动其它bean。

    (6) 跳跃文本：用于显示在屏幕上跳动的文本。

    (7) 感应按钮：当鼠标经过时可以改变图像。

    (8) 阴影文本：显示带有阴影的文本。

    (9) 打字机：用于显示文本字符并伴随播放声音。

    (10) 文本：用于显示文本。

    (11) 行走文本：用于在屏幕上显示移动的文本。

    (12) 计时器：用于计算时间。

   D.网络分类

   (1) 数据库：用于显示数据库中的数据。

    (2) 电子邮件：用于组织和发送电子邮件而不用浏览器邮件对话框。

    (3) 新闻摘要：用于显示包含 URL 链接的滚动文本列表。

    (4) HTML参数：用于读取 HTML 参数。

    (5) 邮件链接：用于显示浏览器的邮件对话框。

    (6) URL 链接：用于连接到其他 Web 页面。生成的URL以./开头的，即使用相对于HTML页面的

相对路径，正斜杠和反斜杠都是一样的。

    (7) 大字标题；用于显示带URL链接的滚动文字列表

    BeanBuilder提供的控件同VisualAge for Java提供的不一样。例如，BeanBuilder有出色的多媒体和动画控件。


WIN2000 下JSP环境的建立(IIS+resin+j2sdk)

作者：cityboy

email: 21cn@371.net

日期：2001-4-25 2:01:32

WIN2000 下JSP环境的建立(IIS+resin+j2sdk)

一：下载所需要的软件

1 - resin 1.2.1:

http://summer.cnjsp.net/download/resin/resin-1.2.1.zip

2 - j2sdk-1_3_0_01 for win:

http://summer.cnjsp.net/download/jdk/j2sdk1_3_0-win.exe

二：安装软件

1 - 安装j2sdk-1_3_0_01 for win，不用做其他的设置，安装完毕即可

    如：安装到c:\j2sdk130 目录之下

2 - 安装resin 1.2.1: 解压缩所下载的resin-1.2.1.zip文件包至相关目录

    如：安装到c:\resin121 目录之下

三：设置运行环境

1 - 找到resin目录下的bin目录，双击运行srun.exe，确定即可，该步骤完成

将isapi_srun.dll文件拷贝至c:\intepub\scripts目录下，并将resin加载进

IIS中，此时，观察Internet信息服务-〉服务器属性-〉ISAPI筛选器已经有

resin加载，但尚未启动

2 - 点击 开始-〉运行，输入C:\resin121\bin\srun.exe -install，将srun加

载为服务，此时观察 服务，将会看到resin servlet runner服务已经加载，但

尚未启动。如不作此步骤，需运行Srun.exe，将打开两个窗口，虽同样可以调试，

但非常之不方便。

3 - 修改resin.conf配置文件，找到

<http-server>

  <app-dir>doc</app-dir>

修改为：

<http-server>

  <app-dir>c:\inetpub\wwwroot</app-dir>

4 - 重新启动IIS，启动resin servlet runner服务

至此，安装完毕。

四：测试--建立test.jsp

<HTML>

<HEAD>

<TITLE>Simple Example</TITLE>

</HEAD>

<BODY>

<% out.println("Hello, world!"); %>

</BODY>

</HTML>

在浏览器地址栏键入http://localhost:8080/test.jspworld!

就在眼前

五：相关错误信息：

Cannot contact Servlet Runner at localhost:6802

这是由于srun.exe未运行所致


windows环境下安装tomcat 修订版本

作者：飞鸟

email: flybird@chinaasp.com

日期：2000-07-10 18:43:51

[H1]在windows环境下安装tomcat[/H1]

修订版

[b]安装jdk1.3[/b]

[b]注意:[/b] 如果你安装过1.3之前的java版本，请删除该版本的所有文件，并用rregedit删除 HKEY_LOCAL_MACHINE\software\javasoft下面所有内容。

然后再安装jdk1.3

请这里下载jdk1.3：

[url]http://www.chinaasp.com/download/readFolder.asp?folder=JspDev&name=JSP%BF%AA%B7%A2%B0%FC[/url]

然后注意如果你的系统是中文系统，系统注册表会有问题，请用用regedit打开注册表查javasoft,

位置为hkey_local_machine -> software -> javasoft -> 找到 “Java 运行时环境”

把这个东东 导出到文件 1.reg....

然后用notepad编辑它，把"Java 运行时环境"替换成"Java Runtime Environment"然后导入。

[b]下载tomcat

[/b]win32版本 [url]http://jakarta.apache.org/builds/tomcat/release/v3.1/bin/jakarta-tomcat.zip[/url]2.26mb

[b]安装tomcat[/b]

将jakarta-tomcat.zip包内文件解压缩到 x:\tomcat。x是驱动器名

[b]设置环境变量[/b]

win9x在autoexec.bat里面用set 语句来设定环境变量，如：set TOMCAT_HOME=d:\tomcat

winnt/win2k可以选择我的电脑，右键点出菜单，选择属性，弹处对话框"系统特性", 选择"高级"tab,然后点按钮"环境变量"，即可以编辑系统的环境变量。

TOMCAT_HOME 值:x:\tomcat (用TOMCAT_HOME指示tomcat的根目录)

JAVA_HOME 值:x:\jdk1.3 (用JAVA_HOME指示jdk1.3安装目录)

CLASSPATH 值:x:\jdk1.3\lib\tools.jar;x:\tomcat\classes;x:\tomcat\lib

[b]测试tomcat

[/b]运行 x:\tombat\bin\startup.bat

然后在浏览器中键入 [url]http://localhost:8080/[/url]3.1的画面，那就说明你成功的配置了tomcat3.1

[b]测试JSP

[/b]现在我们建立目录 D:\myJSP

里面放了一个文件：test.jsp,内容如下：

<%@ page contentType="text/html;charset=gb2312" %>

<%

out.println("这是我的第一个jsp程序");

%>

修改 tomcat\conf\server.xml, 在这个文件的最后，你可以看到一些形如

<Context path="/test" docBase="webapps/test" debug="0" reloadable="true" >

</Context>

等句子，我们在这句后面加上这句：

<Context path="/myJSP" docBase="d:\myJSP" debug="0" reloadable="true" >

</Context>

path是web的虚拟目录，docBase是硬盘上的路径。

然后我们在浏览器地址栏输入: [url]http://localhost:8080/myJSP/test.jsp[/url]

（注意:tomcat区分大小写，如果写错大小写，将得到一个404错误）

浏览器将显示如下内容：

[#808080]这是我的第一个jsp程序[/#]

http://www.chinaasp.com飞鸟 [email]flybird@chinaasp.com">http://www.chinaasp.com飞鸟 [email]flybird@chinaasp.com[/email] 原创.


XML和J2EE的组合技术 (转)

作者：jeru

email: jeru@163.net

日期：7/3/2001 1:06:10 PM

XML和J2EE的组合技术

2001-05-09· 布老虎·小龙亭

    当前，Java 2平台企业版（J2EE）架构在厂商市场和开发者社区中倍受推崇。作为一种工具，可扩展标记语言（XML）简化了数据交换、进程间消息交换这一类的事情，因而对开发者逐渐变得有吸引力，并开始流行起来。自然，在J2EE架构中访问或集成XML解决方案的想法也很诱人。因为这将是强大系统架构同高度灵活的数据管理方案的结合。

    XML的应用似乎是无穷无尽的，但它们大致上可以分为三大类：

    * 简单数据的表示和交换（针对XML的简单API（SAX）和文档对象模型（DOM）语法解析，不同的文档类型定义（DTDs）和概要（schemas））

    * 面向消息的计算（XML-RPC（远程过程调用），SOAP协议，电子化业务XML（ebXML））

    * 用户界面相关、表示相关的上下文（可扩展样式表语言（XSL），可扩展样式表语言转换（XSLT））

    这几类应用在J2EE架构中恰好有天然的对应：数据表示和交换功能是EJB组件模型中持久化服务（persistence services）的一部分，基于消息的通讯由Java消息服务（JMS）API来处理，而界面表示正是Java服务器页面（JSP）和Java Servlets的拿手好戏。

    在本文中，我们将看到当今基于J2EE的应用里，XML是如何在上述几个方面进行应用的，以及在相关标准的未来版本中这些应用将会如何发展。

    基础：数据的表示和交换

    原型化的XML应用（假设有的话）的内容通常是：数据以XML格式存放，为了进行显示、修改甚至写入某个XML文档而经常被读入到某个对象模型中。作为例子，假定我们正处理多种类型的媒体（图品、视频、文本文档等等），并且用下面这个简单的XML DTD来描述这些媒体的元数据：

    ＜!-- DTD for a hypothetical media management system --＞

    ＜!-- Media assets are the root of the object hierarchy. Assets are also

        hierarchical - they can contain other assets. --＞

    ＜!ELEMENT media-asset (name, desc?, type*, media-asset*, urn)＞

    ＜!-- Metadata about the asset --＞

    ＜!ELEMENT name (#PCDATA)＞

    ＜!ELEMENT desc (#PCDATA)＞

    ＜!ELEMENT type (desc, mime-type?)＞

    ＜!ELEMENT mime-type (#PCDATA)＞

    ＜!ELEMENT urn (#PCDATA)＞

    以下是一个基于上述媒体DTD的XML文档，描述了与某个课程讲座相关的内容：

    ＜?xml version="1.0" ?＞＜!DOCTYPE media-asset PUBLIC "-//Jim Farley//DTD Media Assets//EN" "http://localhost/Articles/Sun/dtds/media.dtd"

    ＜media-asset＞

    ＜name＞第14讲＜/name＞

    ＜desc＞与第14讲相关的所有内容＜/desc＞

    ＜!-- 内容对象"lecture 14"的一套子组件 --＞

    ＜media-asset＞

    ＜name＞讲座的幻灯片＜/name＞

    ＜type＞

    ＜desc＞MS PowerPoint＜/desc＞

    ＜mime-type＞application/vnd.ms-powerpoint＜/mime-type＞

    ＜/type＞

    ＜urn＞http://javatraining.org/jaf/E123/lecture-14/slides.ppt

    ＜/media-asset＞

    ＜media-asset＞

    ＜name＞讲座的视频片断＜/name＞

    ＜type＞

    ＜desc＞RealPlayer streaming video＜/desc＞

    ＜mime-type＞video/vnd.rn-realvideo＜/mime-type＞

    ＜/type＞

    ＜urn＞http://javatraining.org/jaf/E123/lecture-14/lecture.rv

    ＜/media-asset＞

    ＜!-- 讲座开始 --＞

    ＜urn＞http://javatraining.org/jaf/E123/lecture-14/index.jsp

    ＜/media-asset＞

    从Web或者企业级应用的角度看，能以这种方式访问数据真是一种福音，因为它体现了高度的可移动性，使我们与元数据的实际资源本身隔离。这些资源可能来自一个关系数据库系统、某种活动媒体服务器或者Web服务器上的一个静态XML文档，等等。如果想把这些数据加载到Java应用中，我们可以从当前众多的Java语言XML解析器中选用一个，通过它将XML数据装入一个DOM文档，最后遍历文档，将所有这些数据转换到我们应用系统的对象模型中。


XP 精华----如何使 Java 项目获得更大成功

作者：friendcn

日期：2001-5-9 9:22:09

Roy W. Miller (rmiller@rolemodelsoft.com)

软件开发人员，RoleModel Software, Inc.

Christopher T. Collins (ccollins@rolemodelsoft.com)

高级软件开发人员，RoleModel Software, Inc.

2001 年 3 月

内容：

企业问题

一种解决方案：灵活方法

XP 的 12 种方法

一起工作的方法

为什么 XP 很重要

参考资料

关于作者

评价这篇文章

使用 Java 语言所进行的面向对象编程变得空前普及。它使软件开发发生了某种程度上的变革，但最近的研究表明，有半数的软件开发项目滞后，而三分之一的项目则超出预算。问题不在于技术，而是开发软件所使用的方法。所谓的“轻量型”或“灵活”方式，与如 Java 这样的面向对象语言的威力和灵活性结合起来，提供了一种很有意思的解决方案。最常见的灵活方式称为极端编程（Extreme Programming）或者 XP，但许多人并不真正了解它。对 Java 项目使用 XP 可以大大增加成功的机会。本文提供了 XP 的概述，并解释了它为什么很重要 -- 不是传言，也没有骗局。

在过去的十年中，CEO 们在产生稳步增加的收入方面面临巨大的压力。他们通过在许多方面采取一系列举措来解决这一问题，例如缩小公司规模、外包、再工程、企业资源规划 (ERP) 等等。这些对低效率的解决措施让 S&P 500 中的许多企业在 90 年代末能够连续几年保持两位数的收入增长。但这种方式也带来了一些负面影响。

在 Gary Hamel 所著的 Leading the Revolution（请参阅参考资料）一书中，他声称已有一些迹象证明传统企业模式的优势已不那么明显。我们必须找到一些替代方法来为收入的持续增长提供动力。他建议唯一能让公司继续增长的办法是进行一次彻底的创新。我们认为在软件开发领域中尤其需要这样。

企业问题

如果使用标准软件开发方法，那么即使在 Java 平台上进行开发，也要做好失望的准备。如图 1 所示，最近的研究表明，有一半项目将滞后，而三分之一的项目将超过预算。这一推测比 1979 年由美国总审计局的研究结果好不了多少。

图 1. 软件项目成功和失败，过去和现在

如果我们希望这些数字有显著提高，则需要彻底创新的方法来开发软件。有两个主要因素影响现有的方法：

惧怕失败

对软件本质的误解

没有人打算失败。具有讽刺意味的是为使失败最小化而创建的方法是失败的。对软件的误解是问题的根源。恐惧实际上只是一种症状。现有的方法是由那些有良好愿望但忘记了软件中的“软”的那些聪明人所创建的。他们假定开发软件就象造桥。因此他们从各种设计规范中借鉴了一些适用于“硬”物体（例如桥梁）的最优方法。结果是基于 Big Design Up-front (BDUF) 思想的反映迟钝的开发方法，软件不堪一击，人们无法使用它们。

一种解决方案：灵活方法

最近发生了一些转变，从所谓的“重量型”方法转向了“轻量型”或“灵活”方法，例如 Crystal 方法、适应性软件开发和（当前最流行的）XP。所有这些过程都有这样一个事实，即需要人们共同来开发软件。成功的软件过程必须将人们的长处最大化，将他们的缺点最小化，因为优点和缺点毋庸质疑都存在。在我们看来，XP 最出色的地方在于它能够解决所有影响参加人员的互补力量。

XP 提供了十年来最大的一次机会，给软件开发过程带来彻底变革。就象 Peopleware 作家 Tom DeMarco 所说，“XP 是当今我们所处领域中最重要的一项运动。预计它对于目前一代的重要性就象 SEI 及其能力成熟度模型对上一代的重要性一样。”

XP 规定了一组核心价值和方法，可以让软件开发人员发挥他们的专长：编写代码。XP 消除了大多数重量型过程的不必要产物，通过减慢开发速度、耗费开发人员的精力（例如干特图、状态报告，以及多卷需求文档）从目标偏离。我们认识到一个称为“极端编程”的东西可能很难作为正式的开发过程推荐给管理层，但如果您的公司从事软件行业，您应该帮助管理层绕过其名称认识到 XP 可以提供的竞争优势。

Kent Beck 在他所著的 Extreme Programming Explained: Embrace Change 一书中概括了 XP 的核心价值（请参阅参考资料）。我们对它们进行了总结：

交流。 项目的问题往往可以追溯到某人在某个时刻没有和其他人一起商量某些重要问题上。使用 XP，不交流是不可能的事。

简单。 XP 建议您总是尽可能围绕过程和编写代码做最简单的事情。按照 Beck 的说法，“XP 就是打赌。它打赌今天最好做些简单的事...而不是做更复杂但可能永远也不会用到的事。”

反馈。 更早和经常来自客户、团队和实际最终用户的具体反馈意见为您提供更多的机会来调整您的力量。反馈可以让您把握住正确的方向，少走弯路。

勇气。 勇气存在于其它三个价值的环境中。它们相互支持。需要勇气来相信一路上具体反馈比预先知道每样事物来得更好。需要勇气来在可能暴露您的无知时与团队中其他人交流。需要勇气来使系统尽可能简单，将明天的决定推到明天做。而如果没有简单的系统、没有不断的交流来扩展知识、没有掌握方向所依赖的反馈，勇气也就失去了依靠。

XP 的方法将这些价值转换成开发人员每天应做的事情。这里没什么新鲜内容。多年以来，行业认识到 XP 方法是“最优方法”。实际上，XP 中的“极端”来自两方面：

XP 采取经过证明的业界最优方法并将其发挥到极致。

XP 将这些方法以某种方式进行结合，使它们产生的结果大于各部分的总和。

这是怎样的情景呢？代码复查是个好的做法，因此始终通过成对地编写代码来做到。测试也是个好的做法，因此总是通过在编写代码之前编写测试来做到。文档很少与代码保持一致，因此只做那些最需要的事，余下的部分则取决于明确编写的代码和测试。XP 不保证人们总做正确的事，但它允许人们这样做。它将这些“极端”方法以一种相互支持的方式结合起来，显著提高了速度和有效性。

XP 的十二种方法

XP 的十二种方法（如图 2 所示）将其定义为规则。让我们仔细研究每一个方法来对“执行 XP”表示什么有个更全面的理解。

图 2. XP 的十二种方法

规划策略

有些人会指责 XP 是一种美其名的剽窃，只是一群牛仔在没有任何规则的情况下将一个系统拼凑在一起。错。XP 是为数不多的几种承认您在开始时不可能事事通晓的方法之一。无论是用户还是开发人员都是随着项目的进展过程才逐步了解事物的。只有鼓励和信奉这种更改的方法才是有效方法。状态限定方法忽视更改。而 XP 则留心更改。它倾听所用的方法就是“规划策略”，一个由 Kent Beck 创造的概念。

这一方法背后的主要思想是迅速地制定粗略计划，然后随着事物的不断清晰来逐步完善。规划策略的产物包括：一堆索引卡，每一张都包含一个客户素材，这些素材驱动项目的迭代；以及对下一两个发行版的粗略计划，如 Kent Beck 和 Martin Fowler 在他们的 Planning Extreme Programming 中描述的那样（请参阅参考资料）。让这种形式的计划得以发挥作用的关键因素是让用户做企业决策，让开发小组做技术决策。如果没有这一前提，整个过程就会土崩瓦解。

开发小组要决定：

估计开发一个素材要花多长时间

使用各种技术选项所花费的成本

团队组织

每个素材的“风险”

迭代中素材开发的顺序（先开发风险最大的那一个可以减轻风险）

客户需要决定：

范围（一个发行版的素材和每一次迭代的素材）

发行日期

优先级（根据企业价值先开发哪些特性）

规划经常发生。这样，在客户或开发人员学习新事物的同时，就为他们调整计划提供了频繁机会。

成对编程

使用 XP，成对的开发人员编写所有产品代码。这种方式听上去好象缺乏效率。Martin Fowler 说，“当人们说成对编程降低生产力时，我回答，‘那是因为大多数耗费时间的编程部分是靠输入来完成的。’”实际上，成对编程无论在经济还是其它方面都提供了许多好处：

所有设计决策都牵涉到至少两个人。

至少有两个人熟悉系统的每一部分。

几乎不可能出现两个人同时疏忽测试或其它任务。

改变各对的组合在可以在团队范围内传播知识。

代码总是由至少一人复查。

研究还显示成对的编程实际上比单独编程更有效（有关详细信息，请参阅参考资料中 Alistair Cockburn 和 Laurie Williams 所著的 The Costs and Benefits of Pair Programming）。

测试

在 XP 中有两种测试：

单元测试

验收测试

开发人员在他们编写代码的同时编写单元测试。客户在他们定义了素材后编写验收测试。单元测试及时告诉开发人员系统在某一点上是否“工作”。验收测试告诉团队系统是否执行用户希望它执行的操作。

假设团队使用的是如 Java 这样的面向对象语言，开发人员在为一些方法编写代码之前为每种有可能失败的方法编写单元测试。然后他们编写足够的代码使之能通过测试。有时人们会发现这有点奇怪。答案很简单。编写测试首先为您提供：

一组可能最完整的测试

可能能工作的最简单的代码

代码意图的明确景象

开发人员只有在通过所有单元测试后才可以将代码检入到源代码资源库中。单元测试使开发人员有信心相信他们的代码能够工作。这为其他开发人员留下线索，可以帮助他们理解最早的开发人员的意图（实际上，这是我们看到过的最好的文档）。单元测试还给了开发人员勇气重新划分代码，因为测试失败可以立刻告诉开发人员存在错误。应该将单元测试自动化，并提供明确的通过／失败结果。xUnit 框架（请参阅参考资料）做到的远不止这些，因此大多数 XP 小组都使用它们。

用户负责确保每个素材都有验收测试来确认它们。用户可以自己编写测试、可以征募组织中的其他成员（例如 QA 人员或业务分析员）编写它们，也可以将这两种方法结合起来。测试告诉他们系统是否具有应该具有的那些特性，以及是否可以正确工作。理想情况下，用户在迭代完成之前就应该写好迭代中那些素材的验收测试了。应该将验收测试自动化，并要经常运行来确保开发人员在实现新特性时没有破坏任何现有的特性。通常情况下，客户需要来自开发团队的某些帮助来编写验收测试。我们对一个项目开发一个可重用的自动验收测试框架，可以让用户在简单编辑器中输入他们的输入和所期望的输出。框架将输入转换成 XML 文件、运行文件中的测试，然后为每个测试显示“通过”或“失败”。客户喜欢这一做法。

不是所有验收测试都必须在所有情况下通过。问题是验收测试帮助客户衡量项目“完成”的情况如何。它们还可以让客户获悉有关某些事物是否可以发行的决定。

重新划分

重新划分是在不更改功能性的前提下对代码加以改进。XP 小组在进行重新划分时毫不手软。

开发人员重新划分有两个重要时机：实现特性之前和之后。开发人员尝试确定更改现有代码是否可以让新特性的开发更容易。他们查看刚刚写好的代码，看是否有方法可以对它进行简化。例如，如果他们认为有抽象的机会，就会进行重新划分来从具体实现中除去重复的代码。

XP 建议您应该编写可能运行的最简单的代码，但同时也建议您应该不断学习。重新划分让您将学到的知识加入到代码中，同时又不会破坏测试。它使您的代码简练。这意味着它可以存在相当长的时间、为以后的开发人员引入更少问题，并为他们指引正确的方向。

简单的设计

XP 的诽谤者说该过程忽略了设计。事实不是这样。问题是重量型方法建议您做的不过是提前完成大部分琐碎的设计任务。这就象是拍一张静态的地平线的照片，静止不动，然后尝试画一张如何到达那里的完美的地图。XP 说设计不应该在事物将保持不变的前提下预先仓促进行。XP 认为设计非常重要，因此应该是一个持续的事务。我们总是先尝试使用能够工作的最简单的设计，然后随着现实的不断显现来更改它。

什么是可能工作的最简单的设计？它是符合以下条件的设计（感谢 Kent Beck 为我们一一列出）：

运行所有测试

不包含重复代码

明确陈述程序员对所有代码的意图

包含尽可能最少的类和方法

对简单设计的需求并不是说所有设计都很小，也不表示它们是无足轻重的。它们只不过需要尽可能简单，但是仍能工作。不要包括不使用的额外特性。我们称这样的事物为 YAGNI，表示“您将不需要它（You Aren't Going to Need It）。”不要让 YAGNI 破坏您成功的机会。

集合体代码所有权

小组中的任何人都应该有权对代码进行更改来改进它。每个人都拥有全部代码，这意味着每个人都对它负责。这种技术可以让人们对部分代码进行必要的更改而不用经过代码拥有者个人的瓶颈。每个人都负责这一事实消除了无代码所有权所带来的混乱。

“每人拥有所有代码”与“没人拥有代码”的说法并不一样。没人拥有代码时，人们可以随处进行破坏而不必负任何责任。而 XP 说，“如果是您破坏的，应该您来弥补。”我们有一些必须在每次集成之前和之后运行的单元测试。如果您破坏了某些事物，您要负责进行修补，无论它位于代码的哪一部分。这需要极端规则。可能这是名称中带有“极端”的另一个原因。

持续的集成

经常进行代码集成可以帮助您避免集成梦魇。XP 团队在一天中集成了代码几次，每次都在所有单元测试对系统运行后执行。

传统方法工作方式如下：编写大量代码后执行一次大爆炸式的集成，然后花费相当长的时间来改正问题。这种笨拙的形式的确使项目速度减缓。大爆炸式的集成给团队立即带来大量问题，并且这些问题通常都有几百种可能的原因。

如果经常进行集成，任何特定集成失败的原因都会非常明显（以前运行过测试，因此错误一定是新事物犯下的）。使用这种方法，当遇到问题时，可能的原因就相当有限。修改起来更容易，花的时间少得多，使团队保持最快速度前进。

现场客户

要使功能最理想，XP 小组需要在现场有一位客户来明确素材，并做出重要的企业决策。开发人员是不允许单独做这些事情的。让客户随时在场可以消除开发人员等待决策时出现的瓶颈。

XP 不会假装素材卡是开发人员交付必要代码所需的唯一指示。素材是对以后在客户和开发人员之间填写细节的对话的一项承诺。与将所有要求写在一个静态文档中不同，其思想是进行面对面的交流，减少产生误解的机会。

我们发现让客户在现场是可能最好的一种情形，但这不是解决问题的唯一方案。底线是客户必须随时在需要回答问题和根据企业价值为团队提供指示时有空。如果客户并非在现场专职陪伴团队的情况下就能做到这些，那很好。如果能和团队待在一起，这会更方便，但只是建议而已。

小发行版

发行版应该尽可能地小，同时仍然提供足够的企业价值以证明它们值得。

只要觉得有意义就可以发布系统。这样就尽可能早为用户提供了价值（请记住，今天的钱比明天的钱来得值钱）。小发行版将为开发人员提供具体的反馈意见，告诉他们哪些符合客户需要，哪些不符合客户需要。然后，小组可以将这些经验教训包括在其下一发行版的规划中。

一周 40 小时

Kent Beck 说他希望“...每天早晨都感到有活力有激情，每天晚上都感到疲惫而满足。”一周 40 小时工作可以让您做到这一点。确切的小时数并不重要，重要的是原则。长时间地持续工作会扼杀工作绩效。疲劳的开发人员会犯更多错误，从长期来说，将比按“正常”时间表进行的开发慢得多。

即使开发人员可以在长时间很好工作，这也不意味着他们应该这样。最终他们会厌倦，会离开他们的工作，或者产生影响他们工作绩效的非工作问题。如果您打乱了人们的生活，将会尝到它所带来的恶果。加班并不是解决项目问题的答案。实际上，它是更大问题的症状。如果您要走向灭亡，就无药可救了。

编码标准

拥有编码标准有两个目的：

防止团队被一些例如事物没有以最大速度发展这种无关紧要的愚蠢争论搞得不知所措。

它支持其它方法。

如果没有编码标准，重新划分代码会更加困难，按应当的频度交换对更困难，快速前进也更困难。目标应该是团队中没有人辨认得出是谁写的哪一部分代码。以团队为单位对某一标准达成协议，然后遵守这一标准。目标不是创建一个事无巨细的规则列表，而是提供将确保您的代码可以清晰交流的指导方针。编码标准开始时应该很简单，然后根据团队经验逐步进化。不要预先花费太多时间。创建能够工作的最简单标准，然后逐步发展。

系统比喻

体系结构是做什么用的？它提供了系统各种组件以及它们是如何交互的画面 -- 一种映射，可以让开发人员了解新的代码部分适合放在哪里。

XP 中的系统比喻与大多数方法称作的体系结构差不多。比喻为团队提供了一致的画面，他们可以用它来描述现有系统的工作方式、新部件适合的位置，以及它们应该采取的形式。

重要的是要记住，关键要让每个人理解系统是如何组合在一起的，而不是美丽的比喻。有时您就是无法想到一个好的比喻。想到时就太好了。

一起工作的方法

整体大于各个部分之和。您可以实现单一方法或一小部分方法，比不使用任何方法得到更大收益。但您只能在实现所有方法的情况下获得最大收益，因为它们的力量来自它们之间的交互。

最初时按照书籍来执行 XP，作为基准。一旦理解了如何进行交互，就拥有了将它们适应于自身环境所需的知识。请记住，“进行 XP”不是目的，而是到达终点的一种手段。目标是快速地开发高级软件。如果您的过程有一些变异，已称不上是在进行 XP，但结果仍能让您战胜所有竞争对手，您已经成功了。

为什么 XP 很重要

坦率地说，XP（或者任何其它灵活方法）根本就不重要。它能够产生的结果才是关键。如果如 XP 这样的灵活方式可以帮助您更快地开发更好的软件而少受痛苦，那么它值得考虑。

还记得我们在这篇文章开始时提到的那些令人生畏的数字吗？我们相信使用 XP 开发面向对象软件可以有机会将它们变得更好。目前我们的经验已经证实了这一信念。

参考资料

在 RoleModel Software 的 XP Portal 中了解有关 XP 的详细信息。

可以在 Alistair Cockburn 和 Laurie Williams (XP2000 submission, 2000) 所著的 The Costs and Benefits of Pair Programming 中了解到有关成对编程所具有的经济方面的意义。

在 Pairprogramming.com 上可以知道成对编程的常规详细信息。

下载 xUnit 测试工具。

如果您希望了解有关 XP 的详细信息，请务必选取一本本书中引用的书籍：

Planning Extreme Programming，由 Kent Beck 和 Martin Fowler 著 (Addison-Wesley, 2000)

Extreme Programming Explained: Embrace Change，由 Kent Beck 著 (Addison-Wesley, 1999)

Leading the Revolution 由 Gary Hamel 著 (Harvard Business School, 2000)

Jeff Canna 有关单元和功能测试的文章 （developerWorks，2001 年 3 月）将 XP 哲学用在测试上。

关于作者

Roy W. Miller 是 RoleModel Software, Inc 的软件开发人员。在 RoleModel 期间，Roy 开发了基于 Java/XML 的自动验收测试框架，并为 Dallas Semiconductor 的 TINI Java 平台创建了几个应用的原型。在加盟 RoleModel 之前，他在 Andersen Consulting（现在是 Accenture）中服务了六年，用过他们的专用重量型商业集成方法 (MIB) 及其变体。自从加盟 RoleModel 后，他获得了许多有关 XP 以及对该方法的本地适应的经验。Roy 与他人合著了 Addison-Wesley XP 系列中的一本书（XP Applied，将于 2001 年 10 月出版），并是在 XP2001 "Business of XP" 展示会上的一名特邀专题讨论小组成员。可以通过 rmiller@rolemodelsoft.com 与 Roy 联系。

Christopher T. Collins 是 RoleModel Software, Inc 的高级软件开发人员。在 RoleModel 期间，Chris 参与了运行将近两年的一个 XP 项目，为新的摩托罗拉蜂窝式电话平台开发了一个嵌入式 Java 应用程序，并将 JUnit 移植到 Sun 的 J2ME 平台上运行。在加盟 RoleModel 之前，他花了 5 年的时间使用许多不同的语言为一些组织开发软件，最近的一次是为美国国防部开发应用程序。他拥有使用和适应涉及几种灵活和重量型的不同开发方法的经验，包括 RUP 和 XP。Chris 拥有美国西佛罗里达大学计算机科学和软件工程的硕士学位，目前在北卡罗来纳州立大学教授 Java 编程课程。他曾是杜克大学有关 XP 的特邀演讲人，并将在 XP2001 上介绍有关过程适应的论文。通过 ccollins@rolemodelsoft.com 与 Chris 联系。


安装 JRun

作者：jeru

email: jeru@163.net

日期：2000-12-31 12:19:29

安装 JRun

在安装Generator的联机版本时, 安装程序总是在Macromedia文件夹下的一个子文件夹中安装Live Software JRun.在Windows中, 此子文件夹为Macromedia\Generator 2;在UNIX系统中,此子目录为Macromedia\Generator2（数字2之前无空格）.Generator安装JRun.dll文件到你的Web服务器的Scripts目录（若此目录中已有此文件的话,它将被覆盖）.

在UNIX系统中,JRun也被编译进Apache, 或是作为Netscape Enterprise的一个模块被列入清单.

更多相关文章请访问  爪哇人--》http://www.cyberlabs.com/~jeru/

管理 JRun

为了监控和记录它的活动JRun拥有一个管理工具.例如,你能够纪录JRun发出的任何错误和警告.在JRun Admin对话框有两个属于 Generator的标签（Generator和Mappings ）.请查阅 JRun文档来了解更多的其他JRun Admin工具标签的信息.（你可以通过 http://www.livesoftware.comSoftware Web站点来获得这些文档.）

如何运用JRun Admin工具:

1 选择 开始 > 程序 > Macromedia Generator 2 > JRun Administration.

2 选择 jsm-default,然后单击配置.

3 选择 jse,然后单击服务配置.

4 单击 Mappings标签来察看Generator servlet列出的特殊文件类型.

5 单击 Generator标签并选择logging选项.

注意:当Generator运行在一个成品环境下时,你应该选择logging level为None or Errors.

选择 None,关闭logging选项.

选择 Warnings来纪录在Generator运行时JRun可能发出的任何警告.这些警告提供了任何一个非致命性errors的有关信息.

选择 Errors来纪录任何一个Generator可能发出的错误.这个设置提供了相对Warnings较少的信息.

选择 Verbose来纪录当它运行于 Generator servlets时Generator可能发出的所有warnings和errors.

6 单击 Aliases选项来显示JRun和Generator servlets的配置信息.

7 单击 Save All.

你可以通过Macromedia\Generator\logs\generator.logl文件来察看你在JRun Admin对话框中为Generator页面所作的任何改动.

调试JRun的活动

如果你要配置Generator来存入管理信息,你可以通过文本编辑器来察看在Macromedia\Generator\logs\位置下的文件中已纪录的信息.在这些文件中,你可以察看关于应用过程的信息, 例如:电影是否已经完全生成,或者是服务器能否连接数据源.

例如,你可以通过察看JrunServletEvent.log文件来检查JRun是否已初始化Generator servlets.下面例子中给出了log文件的部分,列出了已经完全初始化的servlets:

Fri Apr 02 15:18:32 PST 1999: com.livesoftware.jrun.plugins.ssi.JRunSSI: init

Fri Apr 02 15:18:33 PST 1999: FlashGenerator: init

Fri Apr 02 15:18:33 PST 1999: GifGenerator: init

Fri Apr 02 15:18:33 PST 1999: PngGenerator: init

Fri Apr 02 15:18:33 PST 1999: JpegGenerator: init

下面的log文件的部分列出了JRun还未预加载的servlets:

Wed Apr 21 17:36:01 PDT 1999: Could not pre-load servlet 'FlashGenerator'.

Wed Apr 21 17:36:02 PDT 1999: Could not pre-load servlet 'GifGenerator'.

Wed Apr 21 17:36:02 PDT 1999: Could not pre-load servlet 'PngGenerator'.

Wed Apr 21 17:36:02 PDT 1999: Could not pre-load servlet 'JpegGenerator'.


安装好你的机器来使用JSP（转）

作者：虹

日期：2000-7-22 9:26:03

安装好你的机器来使用JSP

你将会需要Java 2软件开发工具(JSDK)，它原来的名称是Java发展工具(JDK)以及JavaServer网站发展工具(JSWDK)，Tomcat，或是其它支持JSP的网络服务器。Sun免费提供JSDK与JSWDK来供Windows，Solaris，以及Linux平台使用。

如果你想要在你目前的网络服务器上使用JSP，但服务器本身并不支持JSP与Java servlets，你可以试试看Allaire的Jrun，它的作用就像是针对Netscape企业版与FastTrack服务器、微软的网际网络信息服务器(IIS)与个人网络服务器(PWS)、Apache、以及其它服务器的网络服务器附加设备。你也可以使用Apache网络服务器的Java版本，最新的JSWDK里有提供。

下载与安装你需要的组件

目前发表的1.2.2-001，JSDK可下载的版本是以可安装的压缩档形式。下载的档案大约是20MB，可提供完整的Java发展环境，让你能建立利用标准API为核心的Java解决之道。然而，你的网络服务器需要应用到JSP的唯一一件事是Java编译器。要让网络服务器知道编译器的位置，将环境变量JAVA.HOME设到JSDK的安装目录。如果你是在Windows上安装并且接受预设目录，将这行程序代码set JAVA.HOME=C:\1.2.2加到你的autoexec.bat档案并且重新开机。

在安装好JSDK之后，下载 并且安装JSWDK或beta版的Tomcat，以Java为主的Apache网络服务器。安装在哪里并不重要，重要的是你可以找到它。一般而言，它会放在上层目录，这种方式可以让你取代JSWDK或 JSDK的网络服务器，不需要移动其它的网络服务器。在你安装好这个档案之后，你就可以准备发展JSP了。

在你正确的安装JSWDK之后，执行startserver指令文件来激活网络服务器，预设通讯端口 为 8080。要看你在激活服务器之后是均C有正确的安装工具，你可以加载范例JSP档案中的任何一个(http://localhost:8080/examples/jsp/

开始

解释JSP语法 之前，先建立一个显示目前日期与时间的快速网页并且将它储存成sample.jsp:

<html>

<head>

<title>First Page</title>

</head>

<body>

<H3>Today is:

<%= new java.util.Date() %>

</H3>

</body>

</html>.

将这个档案与你所有的HTML与JSP网页放在你JSWDK安装目录下的网页目录里. 你可以在http://localhost:8080/sample.jspservlet程序代码，那你就会看到目前的日期与时间.

现在你已经下载，安装，并且架构好发展环境，你已经准备好要了解JSP语法与建立你自己的JSP为主的解决之道.

JSP语法的基本原理

安装之后，接下来我们要讨论JSP的语法.如果要偷懒，你可以下载语法卡 而如果你不熟悉 Java的程序设计，你可能会想要参考Sun的使用手册;然而，网站建立者不应该做太多的Java发展.除了几个函式呼叫之外，出现在你JSP网页上的Java程序代码应该将它减到最少;

记住这点之后，现在让我们先来看看JSP的编译器指引与指令组件，之后我们将解释JavaBeans与内部对象. JSP编译器指引与指令组件有五种型态.JSP 1.0之后，大部分的JSP是包含在以<% 作为开始%>作为结束的单一卷标里.新的 JSP 1.1规格已经发表了，它同时也与XML兼容.

JSP的编译器指引与指令组件

  编译器指引     <%@ 编译器指引 %>

  宣告     <%! 宣告 %>

  表达式     <%= 表达式 %>

  程序代码片段/小型指令     <% 程序代码片段 %>

  批注     <%-- 批注 --%>

编译器指引

JSP的编译器指引是针对JSP引擎。它们并不会直接产生任何看得见的输出；相反的，它们是在告诉引擎如何处理其它的JSP网页。它们永远包含在 <%@ ?%>卷标里。两个主要的指引是 page与include。我们不会讨论taglib编译器指引但它可以在JSP1.1里用来建立自订卷标。

你几乎可以在你所有的JSP网页最上面找到page编译器指引。虽然这不是必须的，但它可以让你指定到哪里可以找到支持的Java类别这类的事：

<%@ page import="java.util.Date" %>，

当发生Java问题的事件时应该将讯息传送到哪里：

<%@ page errorPage="errorPage.jsp" %>，

以及你是?需要为使用者管理通话期的信息，可能存取多个网页(稍后在JavaBeans里会有更多通话期的讨论):

<%@ page session="true" %>。

 include编译器指引让你将你的内容分成几个可管理的组件，就像那些有表头或脚注的网页。所包含的网页可以是固定格式的HTML网页或者是JSP内容的网页:

<%@ include file="filename.jsp" %>。

宣告

JSP宣告让你定义网页层的变量，来储存信息或定义支持的函式，让JSP网页的其余部分能够使用。如果你发现自己有太多的程序代码，你最好将它们放在不同的Java类别里。你可以在 <%! ?%>卷标里找到宣告。记住要在变量宣告的后面加上分号，就跟任何有效的Java叙述的形式一样: <%! int i=0; %>。

表达式

JSP里有表达式，评估表达式的结果可以转换成字符串并且直接使用在输出网页上。JSP运算是属于 <%= ?%> 卷标里，并不包含分号，加引号字符串的无用部分。

<%= i %>

<%= "Hello" %> 。

程序代码片段/小型指令文件

JSP程序代码片段或小型指令文件是包含在<% ?%> 卷标里。当网络服务器接受这段请求时，这段Java程序代码会执行。小型指令文件可以是原始的HTML或XML，其内部的程序代码片段可以让你建立有条件的执行程序代码，或者只是一些使用另一块程序代码的东西。举例来说，下列的程序代码结合了表达式与小型指令文件，在H1，H2，H3，以及H4卷标里显示字符串"Hello"。小型指令文件不限于一行的原始程序代码 :

<% for (int i=1; i<=4; i++) { %>

<H<%=i%>>Hello</H<%=i%>>

<% } %>。

批注

最后一个主要JSP组件是嵌入式批注。虽然你可以在你的档案里包含HTML批注，如果使用者检视网页的原始码，他们也会看到这些批注。如果你不要让使用者看到你的批注，你可以将它放在<%-- ?--%>卷标里:

<%-- 针对伺服端的批注 --%>。

 JSP与JavaBean

虽然你可以在小型指令文件里放入一大块的程序代码，但是大多数的Java程序代码是属于可以重复使用的组件，称为JavaBean。JavaBean就跟ActiveX控件一样:它们提供已知的功能，并且是为了可随时重复使用的目的而设计的。

JavaBean的价值在于它可以经由一组特性来使用，而这些特性则提供对JavaBean设定的存取。以人来作范例，此人就是JavaBean，而他的姓名，社会福利安全号码，以及住址可以是特性。对于JSP网站，基本上你是将'JavaBean'动态的连接到你的网站。

假设JavaBean是在建立网站之前建好的，你要做的第一件事是告诉JSP网页它所需要使用JavaBean.这工作可以用<jsp:useBean>卷标来完成：

<jsp:useBean id="localName" class="com.jguru.Person" scope="application" />.

<jsp:useBean>卷标需要你以id 属性来辨识豆子.在这里，你提供一个名称让JSP网页来辨识豆子，除了id 属性之外，你也必须告诉网页要到哪里去找这个豆子，或者是它的Java类别名称。类别属性提供如何在各式方法之中找到它，最后一个需要的组件是scope 属性.有了范围属性的帮助，你可以告诉豆子，要它为单一网页(预设)[scope="page"]；为一个被请求的网页[scope="request"]；为通话期[scope="session"]；或为整个应用程序[scope="application"]来维护它自己的信息.对于通话期范围，你可以很容易的维护JSP网页里的项目，例如购物车。

一但你宣告了JavaBean之后，你就可以存取它的特性来订定它。要取得一特性的值，使用<jsp:getProperty>卷标。有了<jsp:getProperty>卷标，你可以指定要使用的豆子名称(从useBean的id字段)，以及你要取得值的特性。接着，真正的值就会放在输出里：

<jsp:getProperty id="localName" property="name" />.

要更改JavaBean的特性，你需要使用<jsp:setProperty>卷标.对这个卷标，你也需要辨认豆子以及要修正的特性，除此之外，你还需要提供新值.如果命名正确，这些可以直接经由规定的格式取得：

<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 %> />.

有关JavaBean的最后一点:要让网络服务器可以找到JavaBean，你需要将它们的类别档案放在特别位置。对JSWDK而言，最简单的地方是在安装目录里的类别目录，例如 \jswdk-1.0.1\classes.

JSP的内部对象

最后一个与JSP语法有关的组件叫做内部对象.在JSP小型指令文件内，你可以存取这些内部对象来与执行JSP网页的servlet环境相互作用。许多对内部对象的存取应该要简化。然而，这些是范例，它们的存取都是可接受的，要完整的利用内部对象设定则需要对最新的Java Servlet API有所了解。

下表列出你可以使用的内部对象。

内部对象说明

  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); %>。

当你以JSP写了许多的应用程序之后，如果你建立了JavaBeans或者发现你自己将太多的Java原始码放入你的JSP档案，你需要建立支持的Java类别，这样可以鼓励重复使用并且降低JSP网页转换时所需要的时间。当你需要建立Java类别时，你必须:

将JDSWK的安装目录\bin目录加到你的PATH。在你的autoexec.bat档案的PATH行的最后，加入C:\1.2.2\bin; 。

以下面的指令将JAR档案复制到\jre\lib\ext目录:

copy c:\jswdk-1.0.1\lib\servlet.jar c:\jdk1.2.2\jre\lib\ext.

建立一JSP网站

现在到了将所有的JSP语法放在一起使用的时候了。我们将会建立一个具有输入格式的网站，让使用者来输入股票代号并且获得目前股票的价格(大约延迟20分钟)。如果有错误，则错误网页会显示。

quote.jsp

首先，以这段程序代码建立quote.jsp并且将它储存在JSWDK安装目录的网页目录里。网页的大部分内容是标准的HTML格式，而JSP程序代码分散各处。第6行是网页编辑器指引，指示将任何错误传送到errorPage.jsp档案。第13行到15行是小型指示码，基本上是说只有当"symbol"参数存在时才显示表格。这个if区段包含了第32行到34行的程序代码。第17行定义要使用的 JavaBean，而第18行从参数加载它的符号特性。第27行到29行指示显示豆子的特性。除了if区段之外，并没有其它的Java程序代码。

errorPage.jsp

接着，将下列的JSP原始码储存到网页目录下的档案errorPage.jsp.这是一个错误网页的暗示是在第一行，此行将网页编译器指引isErrorPage属性设为真。虽然前一页说明了错误网页在哪里，这页是说它是一个错误网页。JSP档案里其它特定的JSP程序代码是对内部例外对象的存取。此页只是显示它的值而已:

<%@ page isErrorPage="true" %>

<html>

<head>

<title>Error Page</title>

</head>

<body>

<h1>Our Error Page</h1></font>

<!-- Print Exception -->

We got ourselves an exception:

    <%= exception %>

<a href="quote.jsp">Restart</a>

</body>

</html>.

<span lang="EN-US" style="font-size:10.5pt;mso-bidi-font-size:12.0pt; font-family:"Times New Roman";mso-fareast-font-family:宋体;mso-font-kerning:1.0pt; mso-ansi-language:EN-US;mso-fareast-lang


安装好你的机器来使用JSP--JSP开发入门一

作者：jeru

日期：2000-12-20 11:28:18

你将会需要Java 2软件开发工具(JSDK)，它原来的名称是Java发展工具(JDK)以及JavaServer网站发展工具(JSWDK)，Tomcat，或是其它支持JSP的网络服务器。Sun免费提供JSDK与JSWDK来供Windows，Solaris，以及Linux平台使用。

如果你想要在你目前的网络服务器上使用JSP，但服务器本身并不支持JSP与Java servlets，你可以试试看Allaire的Jrun，它的作用就像是针对Netscape企业版与FastTrack服务器、微软的网际网络信息服务器(IIS)与个人网络服务器(PWS)、Apache、以及其它服务器的网络服务器附加设备。你也可以使用Apache网络服务器的Java版本，最新的JSWDK里有提供。

    下载与安装你需要的组件

    目前发布的1.2.2-001，JSDK可下载的版本是以可安装的压缩形式。下载的文件大约是20MB，可提供完整的Java发展环境，让你能建立利用标准API为核心的Java解决之道。然而，你的网络服务器需要应用到JSP的唯一一件事是Java编译器。要让网络服务器知道编译器的位置，将环境变量JAVA.HOME设到JSDK的安装目录。如果你是在Windows上安装并且接受预设目录，将这行程序代码set JAVA.HOME=C:\1.2.2加到你的autoexec.bat档案并且重新开机。

在安装好JSDK之后，下载并且安装JSWDK或beta版的Tomcat，以Java为主的Apache网络服务器。安装在哪里并不重要，重要的是你可以找到它。一般而言，它会放在上层目录，这种方式可以让你取代JSWDK或 JSDK的网络服务器，不需要移动其它的网络服务器。在你安装好这个档案之后，你就可以准备发展JSP了。

    在你正确的安装JSWDK之后，执行startserver指令文件来激活网络服务器，预设通讯端口 为 8080。要看你在激活服务器之后是均C有正确的安装工具，你可以加载范例JSP档案中的任何一个(http://localhost:8080/examples/jsp/

    开始

    解释JSP语法之前，先建立一个显示目前日期与时间的快速网页并且将它储存成sample.jsp:

    <html>

    <head>

    <title>First Page</title>

    </head>

    <body>

    <H3>Today is:

    <%= new java.util.Date() %>

    </H3>

    </body>

    </html>.

将这个档案与你所有的HTML与JSP网页放在你JSWDK安装目录下的网页目录里. 你可以在http://localhost:8080/sample.jspservlet程序代码，那你就会看到目前的日期与时间.

    现在你已经下载，安装，并且架构好发展环境，你已经准备好要了解JSP语法与建立你自己的JSP为主的解决之道.


版主强烈推荐的问问题的方式。(因为很多朋友问的问题都很不清楚。)

作者：罗亭

email: tinluo@263.net

日期：2001-3-5 11:22:21

我曾经请教过一位Flash高手，以求得如何在用Flash+asp做一个BBS之类的东西

结果，他总共回答没有超过十句，而且大部分的回答只有一个字，"对"or"错"；最后问题便解决了， 我对Flash可是一点也不懂。

我推荐的最佳问问题的方式是--->

我想要做………

我是这么做的………

结果在某处出错………

我猜想是这些原因………（我的偿试1，2……）

结果不成，我想可能是另外某个原因吧，是吗？………

如果是，将怎么解决呢？最好的资料是在哪？………

附，我的Code，及出错处标记

保证一切OK


本人在玩JB4的时候一些发现：(SONY老大。希望这是我的第一篇进JAVA版精华区的文章)

作者：大自然

日期：2001-3-28 12:37:47

一、在调试运行JSP或SERVLET的时候，用WEB RUN为什么会每次都打开一个新端口？

解决方法：选择Run-Configurations，在Runtime Configurations框中选择<Default>，选择Edit，在JSP/SERVLET栏中选择Server options，你会看到有一个Port number，这个表示你调试的时候TOMCAT打开的端口，把右边的Search for unused port的勾去掉，这样每次调试JSP/SERVLET的时候就不会打开N个端口了。：)

二、为什么装好JB4以后，新建文件的时候选择Enterprise，里面的关于EJB、CORBA的项目都是虚的？

解决方法：那是因为你没有指定AppSERVER的路径，建议你先安装一个IAS(当然也可以是服务器上已经装好的)，然后在JB4里选择Tools-Enterprise Setup，在Application Server的Tab框里，设置你的IAS installation directory，如果安装在本地，则只需要直接设置好路径，如果是安装在服务器上，则需要先把服务器上的IAS安装路径共享出来，然后在本地设置该安装路径为网络驱动器就行了(因为发现JB4里用JAVA写的资源管理器里居然没有网上邻居的)，当然WebLogic也可以这样设置。

三、怎样在JB4里安装MYSQL或其它的数据库JDBC驱动。(以http://mmmysql.sourceforge.net/

解决方法：

1、打开JDBC Explorer，在View菜单里选择Options，然后选择Drivers，这里列出了所有的JDBC驱动，然后按 Add新增一个驱动，在弹出的Driver Class里输入org.gjt.mm.mysql.Driver。

2、然后在JDBC Explorer的File菜单里选择New，新建一个数据库连接，在弹出的New Url里选择Driver，发现刚才的org.gjt.mm.mysql.Driver驱动是红的，表明驱动没装上。于是装第三步

3、在JB4的Tools菜单里选择Enterprise Setup，然后选择Database Drivers，按Add，在弹出的Select libraries里按New，然后在弹出的New Library的Name框中输入:mysql driver，在location里选择Jbuilder，在library path里加入mysql_comp.jar这个文件的路径，如c:\mysql-jdbc\mysql_comp.jar，然后按OK完成。

4、这时候要重启JB4后才能使设置生效，然后再重复2步骤。发现原来是红的org.gjt.mm.mysql.Driver现在已经变成和系统预带的sun.jdbc.odbc.JdbcOdbcDriver一样的颜色了，这表明MYSQL驱动已经安装成功。

5、在New Url的框里选择Driver:org.gjt.mm.mysql.Driver，在URL里输入jdbc:mysql://localhost/myphpbbs?user=root?password=test，按OK，这样就配置好一个和MYSQL数据库的连接，然后按OK。

四、为什么在JB4里编译的JSP或SERVLET都不能显示中文？

解决方法：点击JB4的左上角的项目，如xxx.jpx，右键选Properties，在General框的Encoding里选择ISO8859_1，这样用JB4对SERVLET进行编译的时候就可以显示中文了。也就省了你在DOS窗口下用javac -encoding ISO8859_1 xxx.java来编译了。：)


编写跨平台Java程序注意事项

作者：令少爷

email: dj12@8848.net

日期：2001-7-10 10:51:26

使用Java语言编写应用程序最大的优点在于“一次编译，处处运行”，然而这并不是说所有的Java程序都具有跨平台的特性，事实上，相当一部分的Java程序是不能在别的操作系统上正确运行的，那么如何才能编写一个真正的跨平台的Java程序呢？下面是在编写跨平台的Java程序是需要注意的一些事情：

1． 编写Java跨平台应用程序时，你可以选择JDK1.0，1.1，1.2或支持它们的GUI开发工具如：Jbuilder,Visual Age for Java 等等，但是必须注意你的Java程序只能使用Java核心API包，如果要使用第三方的类库包，则该类库包也要由Java核心包开发完成，否则在发布你的程序的时候还得将支持该Java类库包的JVM发布出去。也就是说，你的程序需要是100%纯Java的。举一个例子，Visual J++ 就不是纯Java的，由Visual J++编写的程序也就不具有平台无关性。

2． 无论你使用的是JDK或其他开发工具，在编译时都要打开所有的警告选项，这样编译器可以尽可能多的发现平台相关的语句，并给出警告。虽然不能保证没有编译时警告错误的程序一定是跨平台的，但含有警告错误的程序却很有可能是非平台无关的。

3． 在程序中使用任何一个方法的时候，要详细察看文档，确保你使用的方法不是在文档中已经申明为过时的方法（Deprecated method）,也不是文档中未标明的隐含方法（Undocumented method）。

4． 退出Java程序时尽量不要使用java.lang.System的exit方法。Exit 方法可以终止JVM，从而终止程序，但如果同时运行了另一个Java程序，使用exit方法就会让该程序也关闭，这显然不是我们希望看到的情况。事实上要退出Java程序，可以使用destory()退出一个独立运行的过程。对于多线程程序，必须要关闭各个非守护线程。只有在程序非正常退出时，才使用exit方法退出程序。

5． 避免使用本地方法和本地代码，尽可能自己编写具有相应功能的Java类，改写该方法。如果一定要使用该本地方法，可以编写一个服务器程序调用该方法，然后将现在要编写的程序作为该服务器程序的客户程序，或者考虑CORBA（公共对象请求代理）程序结构。

6． Java中有一个类似于Delphi中的winexec的方法，java.lang.runtime类的exec方法，作为该方法本身是具有平台无关性的，但是给方法所调用的命令及命令参数却是与平台相关的，因此，在编写程序时要避免使用，如果一定要调用其他的程序的话，必须要让用户自己来设置该命令及其参数。比如说，在windows中可以调用notepad.exe程序，在linux 中就要调用vi程序了。

7． 程序设计中的所有的信息都要使用ASCII码字符集，因为并不是所有的操作系统都支持Unicode字符集，这对于跨平台的Java中文软件程序不能不说是一大噩耗。

8． 在程序中不要硬性编码与平台相关的任何常量，比如行分隔符，文件分隔符，路径分隔符等等，这些常量在不同的平台上是不同的，比如文件分隔符，在UNIX和MAC中是“/”，在windows中是“\”，如果要使用这些常量，需要使用jdava.util.Properties类的getProperty方法，如java.util.Properties.getProperty(“file.separator”)可以获得文件分隔符，getProperty (“line.separator”)返回行分隔符，getProperty(“path.separator”)返回路径分隔符。

9． 在编写跨平台的网络程序时，不要使用java.net.InetAddress类的getHostName方法得到主机名，因为不同的平台的主机名格式是不同的，最好使用getAddress得到格式相同的IP地址，另外，程序中所有的主机名都要换成IP地址，比如http://www.263.net

10． 涉及文件操作的程序需要注意：不要在程序中硬性编码文件路径，理由和8中一样，只是这一点特别重要，因此单独提出。而且，不同平台对于文件名使用的字符及最大文件名长度的要求不同，编写你的程序的时候要使用一般的ASCII码字符作为文件的名字，而且不能与平台中已存在的程序同名，否则会造成冲突。

11． 如果您写的程序是GUI程序，在使用AWT组件时不能硬性设置组件的大小和位置而应该使用Java的布局管理器（layout manager）来设置和管理可视组件的大小和位置，否则有可能造成布局混乱。

12． 由于不同的操作系统，不同的机器，系统支持的颜色和屏幕的大小和分辨率都不同，如何获得这些属性呢？使用java.awt.Systemcolor类可以获得需要的颜色，如该类的inactiveCaption 就是窗口边框中活动标题的背景颜色，menu则是菜单的背景颜色。使用java.awt.Toolkit的getScreenResolution可以以“象素每英寸”为单位显示屏幕的分辨率。该类的getScreenSize可以得到屏幕大小（英寸），loadSystemColors可以列出所有的系统颜色


补充两个有用的关于时间的函数

作者：chenm

日期：2001-5-15 14:45:31

/*

Time Functions.

cnmuse@chinaren.com

*/

import java.lang.*;

import java.util.*;

import java.text.ParsePosition;

import java.text.SimpleDateFormat;

public class formatDateTime{

  static String TimeFormat1="yyyy.MM.dd HH:mm:ss";

  public formatDateTime(){

  }

/*

Function name:    lTimeCompare

Description:      Compare two times

Input:            String CompTime1,String CompTime2 (Format: yyyy.MM.dd HH:mm:ss)

Output:           time diffrent by seconds

*/

  public long lTimeCompare(String CompTime1,String CompTime2){

      long iReturn=-1;

      SimpleDateFormat sdf = new SimpleDateFormat(TimeFormat1);

      Date DateTime1=sdf.parse(CompTime1,new ParsePosition(0));

      Date DateTime2=sdf.parse(CompTime2,new ParsePosition(0));

      iReturn=DateTime2.getTime()/1000L-DateTime1.getTime()/1000L;

      return iReturn;

  }

/*

Function name:    lTimeCompareNow

Description:      Compare one time with now

Input:            String CompTime1(Format: yyyy.MM.dd HH:mm:ss)

Output:           time diffrent by seconds

*/

  public long lTimeCompareNow(String CompTime1){

      long iReturn=-1;

      SimpleDateFormat sdf = new SimpleDateFormat(TimeFormat1);

      Date DateTime1=sdf.parse(CompTime1,new ParsePosition(0));

      iReturn=java.lang.System.currentTimeMillis()/1000L-DateTime1.getTime()/1000L;

      return iReturn;

  }

}


不难，请看javamail 发送附件的例子

作者：jeru

email: jeru@163.net

日期：2001-1-9 18:37:32

Here is the code to send an attachment:

import java.util.Properties;

import javax.mail.*;

import javax.mail.internet.*;

import javax.activation.*;

public class AttachExample {

  public static void main (String args[])

      throws Exception {

    String host = args[0];

    String from = args[1];

    String to = args[2];

    String fileAttachment = args[3];

    // Get system properties

    Properties props = System.getProperties();

    // Setup mail server

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

    // Get session

    Session session =

      Session.getInstance(props, null);

    // Define message

    MimeMessage message =

      new MimeMessage(session);

    message.setFrom(

      new InternetAddress(from));

    message.addRecipient(

      Message.RecipientType.TO,

      new InternetAddress(to));

    message.setSubject(

      "Hello JavaMail Attachment");

    // create the message part

    MimeBodyPart messageBodyPart =

      new MimeBodyPart();

    //fill message

    messageBodyPart.setText("Hi");

    Multipart multipart = new MimeMultipart();

    multipart.addBodyPart(messageBodyPart);

    // Part two is attachment

    messageBodyPart = new MimeBodyPart();

    DataSource source =

      new FileDataSource(fileAttachment);

    messageBodyPart.setDataHandler(

      new DataHandler(source));

    messageBodyPart.setFileName(fileAttachment);

    multipart.addBodyPart(messageBodyPart);

    // Put parts in message

    message.setContent(multipart);

    // Send the message

    Transport.send( message );

  }

}


不需ODBC可由IP地址与端口号建立与SQLSERVER的连接

作者：jspfuns

日期：2000-8-9 11:30:11

package jsp;

import java.sql.*; // JDBC package

public class sql_data {

String url = "jdbc:inetdae:192.168.2.70?sql7=true"; // use your hostname and port number here

String login = "sa"; // use your login here

String password =""; // use your password here

public Connection connection = null;

public Statement st = null;

public ResultSet rs = null;

public sql_data(){

try {

Class.forName("com.inet.tds.TdsDriver").newInstance();

DriverManager.setLoginTimeout(10);

// connection = DriverManager.getConnection(url,login,password);

// DatabaseMetaData conMD = connection.getMetaData();

// connection.setCatalog( "register");

} catch(Exception e) {

e.printStackTrace();

}

}

public void sqlclose() {

try {

st.close();

connection.close();

}catch(SQLException ex) {

System.err.println("sqlclose: " + ex.getMessage());

}

}

public ResultSet executeQuery(String sql) {

try {

connection = DriverManager.getConnection(url,login,password);

connection.setCatalog( "register");

st = connection.createStatement();

rs = st.executeQuery(sql);

}

catch(SQLException ex) {

System.err.println("aq.executeQuery: " + ex.getMessage());

}

return rs;

}

}


初学jsp心得(一)

作者：urus

日期：00-5-22 下午 11:41:54

废话不多说！

测试环境为 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有相同的参数名称呢？下面是一段测试代码：

<form method="POST" action="query.jsp?id=2">

  <input type="text" name="id" value="1" size="60">

</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.*"  %>

<%@ page contentType="text/html; charset=gb2312" %>

<%@ include file="conn.jsp" %>

<%

.......

int pages=0;

int pagesize=10;

ResultSet result = null;

ResultSet rcount = null;

pages = new Integer(request.getParameter("pages")).intValue();

if (pages>0)

{

String sql=" state='我不傻'";

int count=0;

try {

rcount = stmt.executeQuery("SELECT count(id) as id from user where "+sql);

catch(SQLException ex) {

  out.print("aq.executeQuery: " + ex.getMessage());

  }

if(rcount.next())

  count = rcount.getInt("id");

rcount.close();

if (count>0)

{

sql="select * from user where  "+sql;

try {

result = stmt.executeQuery(sql);

    }

catch(SQLException ex) {

  out.print("aq.executeQuery: " + ex.getMessage());

  }

int i;

String name;

//  result.first();

//  result.absolute((pages-1)*pagesize);

//  此方法jdbc2.0支持。编译通过，但执行不过，不知是不是跟驱动有关，只好用下面的笨办法。

for(i=1;i<=(pages-1)*pagesize;i++)

  result.next();

for(i=1;i<=pagesize;i++) {

  if (result.next()) {

  name=result.getString("name");

  out.print(name);

  }

result.close();

int n= (int)(count/pagesize);

if (n*pagesize<count) n++;

if (n>1)

 {

 for(i=1;i<=n;i++)

   out.print("<a href=query.jsp?pages="+i+">"+i+"&nbsp;</a>");

 }

}

}

%>

              http://www.guanshui.com   Urus 2000-5-22


从数据库中读取并生成图片的Servlet

作者：邵望

日期：2000-12-24 21:44:55

大体思路

1）创建ServletOutputStream对象out,用于以字节流的方式输出图像

2）查询数据库，用getBinaryStream方法返回InputStream对象in

3）创建byte数组用作缓冲，将in读入buf[],再由out输出

注：下面的例程中数据库连接用了ConnectionPool,以及参数的获得进行了预处理

package net.seasky.music;

import javax.servlet.*;

import javax.servlet.http.*;

import java.io.*;

import java.util.*;

import java.sql.*;

import net.seasky.util.*;

import net.seasky.database.DbConnectionManager;

public class CoverServlet extends HttpServlet {

  private static final String CONTENT_TYPE = "image/gif";

  public void init(ServletConfig config) throws ServletException {

    super.init(config);

  }

  public void doGet(HttpServletRequest request, HttpServletResponse response

) throws ServletException, IOException {

    response.setContentType(CONTENT_TYPE);

    int albumID;

    ServletOutputStream out = response.getOutputStream();

    try {

      albumID = ParamManager.getIntParameter(request,"albumID",0);

    }

    catch (Exception e) {

      response.sendRedirect("../ErroePage.jsp");

      return;

    }

    try {

      InputStream in=this.getCover(albumID);

      int len;

      byte buf[]=new byte[1024];

      while ((len=in.read(buf,0,1024))!=-1) {

        out.write(buf,0,len);

      }

    }

    catch (IOException ioe) {

      ioe.printStackTrace() ;

    }

  }

  private InputStream getCover(int albumID) {

    InputStream in=null;

    Connection cn = null;

    PreparedStatement pst = null;

    try {

      cn=DbConnectionManager.getConnection();

      cn.setCatalog("music");

      pst=cn.prepareStatement("SELECT img FROM cover where ID =?");

      pst.setInt(1,albumID);

      ResultSet rs=pst.executeQuery();

      rs.next() ;

      in=rs.getBinaryStream("img");

    }

    catch (SQLException sqle) {

      System.err.println("Error in CoverServlet : getCover()-" + sqle);

      sqle.printStackTrace() ;

    }

    finally {

      try {

        pst.close() ;

        cn.close() ;

      }

      catch (Exception e) {

        e.printStackTrace();

      }

    }

    return in;

  }

  public void destroy() {

  }

}


带JavaBean 的JSP ：

作者：天马

日期：00-4-30 7:43:13

虽然你可以把大段的代码放在脚本片段（scriptlet）内，但是绝大多数的Java代码属于可重复使用的名为JavaBea的组件。JavaBean类似于ActiveX控件：他们都能提供常用功能并且可以重复使用。

JavaBean的值是通过一些属性获得的，你可通过这些属性访问JavaBean设置。以一个人来打比方，这个人就是一个JavaBean，这个人的名字、身份证号码以及住址就是其属性。在JSP网站，你基本上就是通过玩“Connect the beans）”来使你的网站动态化。

假设JavaBean先于网站被创建出来，你需要做的第一件事就是告诉JSP页面它将要用到一个“Bean”。你可以用“<jsp:useBean>”标记来做到这一点：

<jsp:useBean id="localName" class="com.jguru.Person" scope="application" />。

“<jsp:useBean>”标记要求你用“id”属性来识鉴别Bean。这里你提供一个名字来JSP页面其余部分的Bean。除了“id”属性，你还须告诉网页面从何处查找Bean，或者它的Java类别名是什么。这种类别属性提供确 认Bean的功能，其他一些方法也可以做到这一点。最后一个必需的元素是“scope”属性。有了“scope”属性的帮助，你就能告诉Bean为单一页面（默认情况）[scope="page"]、为[scope="request"]请求为会话[scope="session"]，或者为整个应用程序[scope="application"]保持留信息。有了会话范围，你就能非常容易地在JSP网页面上维护购物车等项目。

一旦你声名了一个JavaBean，你就可以访问它的属性来定制它。要获得属性值，请用“<jsp:getProperty>”标记。有了这个标记，你就能指定将要用到的Bean名称（从useBean的"id"字段得到）以及你想得到其值的属性。实际的值被放在输出中：

<jsp:getProperty id="localName" property="name" />。

要改变JavaBean属性，你必须使用“<jsp:setProperty>”标记。对这个标记，你需要再次识别Bean和属性，以修改并额外提供新值。如果命名正确，这些值可以从一个已提交的表中直接获得：参数获得：

<jsp:setProperty id="localName" property="*" />;

可以从一个参数获得，但你必须直接命名属性和参数：

<jsp:setProperty id="localName" property="serialNumber" value="string" />

或者直接用一个名字称和值来设置： <jsp:setProperty id="localName" property="serialNumber" value= <%= expression %> />.

关于JavaBean 的最后一件事： 为了Web服务器能找到JavaBean，你需要将其类别文件放在一个特殊位置。用JSWDK，最 方便的位置是安装目录下的类别目录，如\jswdk-1.0.1\classes。


第二部分：EJB 编程模型（转）

作者：zergman

email: zergman@chinaasp.com

日期：2001-6-15 13:38:17

[TABLE][TR][TD]什么是 Enterprise JavaBeans 组件？[/TD][TD][img]http://www.cn.ibm.com/developerWorks/i/c.gif[/img][/TD][TD][urlhref=http://www.cn.ibm.com/developerWorks/java/what-are-ejbs/part2/index_eng.shtml][img]http://www.cn.ibm.com/developerworks/i/src.gif[/img][/url][/TD][TD][img]http://www.cn.ibm.com/developerWorks/i/c.gif[/img][/TD][/TR][TR][TD][img]http://www.cn.ibm.com/developerWorks/i/c.gif[/img][/TD][/TR][TR][TD][img]http://www.cn.ibm.com/developerWorks/i/c.gif[/img][/TD][/TR][/TABLE][TABLE][TR][TD][img]http://www.cn.ibm.com/developerWorks/i/c.gif[/img][/TD][TD][TABLE][TR][TD][img]http://www.cn.ibm.com/developerWorks/i/c.gif[/img][/TD][TD][TABLE][TR][TD][img]http://www.cn.ibm.com/developerWorks/i/c.gif[/img][/TD][/TR][TR][TD][B]内容：[/B][/TD][/TR][TR][TD][img]http://www.cn.ibm.com/developerWorks/i/c.gif[/img][/TD][/TR][TR][TD][TABLE][TR][TD][url href=http://www.cn.ibm.com/developerWorks/java/what-are-ejbs/part2/index.shtml#1]接口、委托和代理[/url][/TD][/TR][TR][TD][url href=http://www.cn.ibm.com/developerWorks/java/what-are-ejbs/part2/index.shtml#2]服务器和容器[/url][/TD][/TR][TR][TD][url href=http://www.cn.ibm.com/developerWorks/java/what-are-ejbs/part2/index.shtml#3]Bean 风格[/url][/TD][/TR][TR][TD][url href=http://www.cn.ibm.com/developerWorks/java/what-are-ejbs/part2/index.shtml#4]第三部分内容预告[/url][/TD][/TR][TR][TD][url href=http://www.cn.ibm.com/developerWorks/java/what-are-ejbs/part2/index.shtml#resources]参考资料[/url][/TD][/TR][TR][TD][url href=http://www.cn.ibm.com/developerWorks/java/what-are-ejbs/part2/index.shtml#author]作者简介[/url][/TD][/TR][/TABLE][/TD][/TR][/TABLE][TABLE][TR][TD][img]http://www.cn.ibm.com/developerWorks/i/c.gif[/img][/TD][/TR][TR][TD][B]相关内容：[/B][/TD][/TR][TR][TD][img]http://www.cn.ibm.com/developerWorks/i/c.gif[/img][/TD][/TR][TR][TD][TABLE][TR][TD][url href=http://www.cn.ibm.com/developerWorks/java/what-are-ejbs/part1/index.shtml]第一部分：体系结构的历史和目标[/url][/TD][/TR][TR][TD][url href=http://www.cn.ibm.com/developerWorks/java/what-are-ejbs/part3/index.shtml]第三部分：部署和使用 EJB 组件[/url][/TD][/TR][/TABLE][/TD][/TR][/TABLE][TABLE][TR][TD][img]http://www.cn.ibm.com/developerWorks/i/c.gif[/img][/TD][/TR][TR][TD][img]http://www.cn.ibm.com/developerWorks/i/c.gif[/img][/TD][/TR][/TABLE][/TD][/TR][/TABLE]第二部分：EJB 编程模型

Ken Nordby

IBM 软件工程师

2000 6 月

[BLOCKQUOTE]本文的第二部分说明创建 Enterprise JavaBean 组件所需的 Java 接口和类的作用。除了对 bean 类本身进行编码外，EJB 开发人员还必须为 bean 定义一个本地接口和一个远程接口。这些接口的实现类通常由容器生成，因此部署 EJB 组件是开发人员和 EJB 容器的合作行为。第二部分还区分了 enterprise bean 的两种主要类型，即会话 bean 和实体 bean，并说明了 EJB 容器和 EJB 服务器之间的关系。[/BLOCKQUOTE]

enterprise bean 的编程模型的三个关键特征是：面向对象、对象的分布式和使用代理对象。由于此编程模型使用 Java 技术，因此它在本质上就是面向对象的。此模型也是分布式的，这是指 bean 在理论上是位置透明的。根据 Enterprise JavaBeans (EJB) 规范，“一般说来，EJB 类和 EJB 容器的实际位置对客户机是透明的。”在客户机想要访问 EJB 组件时使用代理对象。bean 本身对于客户机是不可访问的，对 bean 方法的访问则由 helper 类提供。

[b]接口、委托和代理[/b]

当 Java 程序员编写一个 Enterprise JavaBeans 组件时，他们所创建的类必须实现一个 EJB 接口，并且它必须包含一个名为 ejbCreate() 的方法。一个 EJB 接口 -- 例如 SessionBean 接口 -- 指定了一些方法，它们包括以下各项： [UL][LI]ejbActivate() [LI]ejbPassivate() [LI]ejbRemove() [LI]setSessionContext()[/LI][/UL]

ejbActivate() 和 ejbPassivate() 方法通知一个 bean，管理该 bean 的容器组件正在主动和被动之间切换 bean 的状态（这通常是指在内存中还是交换到磁盘）。ejbRemove() 方法使 bean 知道它已被从容器中删除。setSessionContext() 方法使 bean 与一个上下文对象相关联，此上下文对象是为了便于 bean 与其容器进行通信。

ejbCreate() 方法并不是从零做起创建 enterprise bean 的。当客户机想要创建新的 enterprise bean 时，bean 的容器将调用这个 bean 的类的 newInstance() 方法，来实例化新的 bean 对象。然后容器调用 setSessionContext() 方法来建立上下文对象，用于与 bean 进行通信。最后，容器调用新 bean 中的 ejbCreate() 方法。像 ejbCreate()、ejbActivate() 和 ejbPassivate() 这样的方法有时称为[I]对象生存周期[/I]方法，以区别于[I]业务逻辑[/I]方法。

当开发人员设计一个新的 EJB 组件时，编写组成 enterprise bean 类的代码本身是不够的。EJB 程序员还必须编写两个将由 helper 类使用的 Java 接口。这些强制性接口必须扩展标准的 EJBObject 和 EJBHome 接口，而这两个接口则都是 java.rmi.Remote marker 接口的扩展。扩展标准 EJBObject 接口的接口被称为 enterprise bean 的[I]远程接口[/I]，它指定在 bean 自身中定义的业务方法。当应用程序调用 enterprise bean 中的业务方法时，应用程序并不访问 bean 本身。实际上，方法调用被传递给实现 EJBObject 接口扩展的那个对象。这种做法称为[I]委托[/I]，它是 EJB 体系结构中的一个设计要点：

[BLOCKQUOTE]“客户机从来不直接访问 enterprise bean 类的实例。客户机总是使用 enterprise bean 的远程接口来访问 enterprise bean 的实例。实现 enterprise bean 的远程接口的类由容器提供。此类所实现的分布式对象称为 [I]EJB 对象[/I]。”([I]Enterprise JavaBeans Specification 1.0)[/I][/BLOCKQUOTE]

bean 对 EJBObject 接口的扩展称为其[I]远程接口[/I]，而实现远程接口的对象则称为 [I]EJB 对象[/I]。

enterprise bean 还必须具有本地接口。此接口是标准 EJBHome 接口的扩展。实现 bean 的本地接口的对象称为[I]本地对象[/I]。本地对象包含一个 create() 方法，此方法由应用程序调用，而应用程序则必须创建一个 bean 实例。本地对象中的 create() 方法创建一个新的 EJB 对象。它并不直接创建新的 enterprise bean 实例，因为不允许直接访问 bean。

EJB 对象和本地对象充当 bean 对象的代理，因为它们代表 bean 接收方法调用。EJB 对象主要为 bean 业务方法充当代理；本地对象主要为 bean 生存周期方法充当代理。

为 EJB 组件使用 create() 方法并不一定要实例化新的 bean。容器确定如何最好地满足创建请求，对于某些类型的 bean，它可以重用现有的实例：

[BLOCKQUOTE]“客户机使用会话 bean 本地接口上的 [I]create[/I] 和 [I]remove[/I] 方法。虽然客户机以为它正在控制着 EJB 实例的生存周期，但是，是容器在处理 [I]create[/I] 和 [I]remove[/I] 调用，而不一定要创建和删除 EJB 实例。在客户机和...实例之间不存在固定的映射。容器只是将客户机的工作委托给任何一个方法已经就绪的可用实例而已。”([I]Enterprise JavaBeans Specification 1.0)[/I][/BLOCKQUOTE]

创建新的 bean 实例受容器的控制，并可以与客户机发布 create() 方法异步。

当创建一个 EJB 组件时，开发人员负责定义 EJBObject 接口和 EJBHome 接口，但是无需编写实现这些接口的类的代码。EJB 容器软件组件自动创建这些类。

下面的代码段说明客户机应用程序可能怎样使用称为 CartBean 的 enterprise bean 来进行在线购物： [TABLE][TR][TD][PRE]CartHome cartHome = javax.rmi.PortableRemoteObject.narrow(initialContext.lookup("applications/shopping_cart"), CartHome.class);

Cart cart = cartHome.create();

cart.addItem(item29);

cart.addItem(item67);

cart.addItem(item91);

cart.purchase();

cart.remove();

[/PRE][/TD][/TR][/TABLE]

CartHome 是实现本地接口的类（EJBHome 接口的扩展）。Cart 是实现远程接口的类（EJBObject 接口的扩展）。当客户机调用应用程序方法（如 addItem() 和 purchase()）时，它们是在 cart 对象上调用的，此对象接着将这些方法的执行委托给 bean 自身。enterprise bean 的功能是通过其代理 EJB 对象（即 cart）来获得的。如果多台客户机同时访问 cart bean，将会发生什么事情呢？Enterprise bean 开发人员无需编写代码来支持并发访问。并发性由 EJB 容器支持。

下图说明各 EJB 对象之间的关系：

[img]http://www.cn.ibm.com/developerWorks/java/what-are-ejbs/part2/ejb1.gif[/img]

[b]服务器和容器[/b]

EJB 体系结构包括 EJB 服务器和 EJB 容器两个概念。EJB 服务器充当一种组件执行系统，正如 EJB 白皮书中所述：

[BLOCKQUOTE]“Enterprise JavaBeans 规范为每个支持完全可移植性的 Java 应用程序服务器定义了一个标准模型。任何厂商都可以使用此模型来实现对 Enterprise JavaBeans 组件的支持。多种系统（如 TP 监视器、CORBA 运行时系统、COM 运行时系统、数据库系统、Web 服务器系统或其它基于服务器的运行时系统）都可以调整到能够支持可移植的 Enterprise JavaBeans 组件。”(Thomas, [I]Enterprise JavaBeans Technology: Server Component Model for the Java Platform[/I])[/BLOCKQUOTE]

EJB 服务器为使用 EJB 组件的应用程序提供操作环境，并供应所有必需的服务，来支持 EJB 体系结构。打包 EJB 服务器软件并没有预先规定的方式。一种方法是将它作为一项功能增强包括到应用程序服务器中，这就是在 IBM WebSphere Application Server, Advanced Edition, Version 2.0 中采用的方法。

EJB 组件并不在 EJB 服务器的顶部直接执行。一个称为 EJB 容器的中间软件组件在 EJB 服务器环境中运行，从而又为这些 bean 自身提供操作环境。EJB 容器对 EJB 应用程序是完全透明的，但是在支持 bean 操作方面起着关键性的作用。

为了使 enterprise bean 能充当可重用的软件组件，它们对特定的服务器或平台功能不能有内建的相关性。服务器端功能的几种常见类型已经被从 bean 设计中“分离出去”，而将此功能的责任转移给了容器组件。例如，容器将被用来接管安全性、并发性、事务处理、交换到辅助存储器和其它服务的责任，从而使 bean 免受服务器相关性的制约，并将按业务逻辑来优化，而不是按服务逻辑来优化。

EJB 白皮书这样描述容器的作用：

[BLOCKQUOTE]“EJB 容器管理部署于其中的 enterprise bean。客户机应用程序并不直接与 enterprise bean 进行交互。相反，客户机应用程序通过由容器生成的两个封装接口（ EJB Home 接口和 EJB Object 接口）与 enterprise bean 进行交互。当客户机使用封装接口调用各种操作时，容器截获每个方法调用，并插入管理服务。”(Thomas, [I]Enterprise JavaBeans Technology: Server Component Model for the Java Platform[/I])[/BLOCKQUOTE]

可以期望 EJB 容器软件一般都会随 EJB 服务器软件一起提供，尽管规范允许分离这些组件。除了提供对运行时服务（如事务处理和安全性）的访问以外，还期望 EJB 容器包括各种必要工具，来支持 enterprise bean 的安装、操作和管理。例如，需要有工具解释 EJB jar 文件的内容，有工具生成数据库访问，来获得容器提供的持久性，有工具监视正在运行的 bean 的行为，以及实现安全性等。

[b]Bean 风格[/b]

EJB 组件分为两种主要类别 -- [I]会话 bean[/I] 和[I]实体 bean[/I]。根据 bean 处理状态、事务和持久性的方式这些类别还可以进一步细分。会话 bean 通常具有以下属性： [UL][LI]代表单个客户机执行 [LI]可以是事务性的 [LI]可以更新共享数据库中的数据 [LI]生存期相对较短 [LI]其生存期通常就是客户机的生存期 [LI]任何持久性数据都由 bean 管理 [LI]可以依容器的判断予以删除 [LI]会在 EJB 服务器失败时被删除 [/LI][/UL]

实体 bean 通常具有以下属性：[UL][LI]代表数据库中的数据 [LI]是事务性的 [LI]允许多个用户共同访问 [LI]可以长期存在 [LI]持久性数据可以由容器管理 [LI]在 EJB 服务器失败后能继续生存[/LI][/UL]

EJB 规范对会话 bean 和实体 bean 的说明如下：

[BLOCKQUOTE]“对于客户机，会话 enterprise bean 是一种非持久性的对象，它实现某些在服务器上运行的业务逻辑。想像一个会话对象的一种方式是：会话对象是运行在服务器上的客户机程序的逻辑扩展。会话对象不在多台客户机之间共享。[/BLOCKQUOTE]

[BLOCKQUOTE]“对于客户机，实体 enterprise bean 是一种持久性对象，它代表一个存储在持久性存储器（例如，一个数据库）中的实体的对象视图，或者是一个由现有企业应用程序实现的实体。”([I]Enterprise JavaBeans Specification 1.0[/I]) [/BLOCKQUOTE]

用一种粗略的说法，[I]会话 bean 代表这样的操作，它检索或存储数据以满足用户请求；而实体 bean 则代表一种数据集，可以访问这些数据集来满足用户请求。[/I]

[B]会话 bean[/B]

最简单的一种 Enterprise JavaBeans 组件就是[I]无状态的会话 bean[/I]。因为这些 bean 没有可以区分它们的状态，所有的实例都是完全相同的。容器管理无状态会话 bean 的生存周期，其方式是通过创建足够数目的此种 bean 来适应客户机工作负荷，并在不需要它们时将其删除。[I]钝化[/I]，即将闲置的 bean 写到磁盘上，不用于无状态的会话。要调用 bean，客户机程序调用本地接口中的 standard create() 方法，尽管此操作不一定导致实例化新的 bean 实例。容器可以选择将客户机请求发送给现有的对象。反之，容器则可以按它的选择创建新的实例，且独立于由客户机发布的 create() 方法。

在 EJB 本地对象上发布的 create() 调用返回一个对 EJB 对象的引用，这个 EJB 对象代表 enterprise bean。一旦客户机有了 EJB 对象引用，它就可以将业务方法发布到 EJB 对象上，容器随之会将这些方法委托给 bean 自身。负责管理会话 bean 的容器组件无需推断会话 bean 是否是无状态的。会话 bean 是无状态的还是有状态的在安装时声明。

如果会话 bean 在方法调用之间保留状态信息，则它是[I]有状态的[/I]。通过调用 ejbPassivate() 方法，容器可以依其判断将有状态会话 bean 钝化，或写到辅助存储器中。EJB 规范并不要求容器在钝化 bean 时使用 Java 串行化协议，但是它们必须提供等价的功能。当容器决定将一个非活动的会话 bean 交换回到内存中时，它会取消被动 bean 的串行化，并调用 ejbActivate() 方法。有状态会话 bean 的开发人员负责确保状态数据是可串行化的。在集群的应用程序服务器环境中实现有状态会话 bean 时务必要小心，因为并不是所有的服务器都支持集群的有状态会话 bean 的同步化。

有状态会话 bean 可以是事务性的。通过使用 javax.transaction.UserTransaction 接口中的方法，如 begin()、commit() 和 rollback()，bean 可以控制事务；通过实现 javax.ejb.SessionSynchronization 接口，bean 可以接收有关事务状态的通知。EJB 容器无需推断哪些 bean 需要事务支持；UserTransaction 接口仅可用于那些在安装时被标记为事务性的 bean。

[B]实体 bean[/B]

实体 bean 在体系结构上与会话 bean 类似，但它们提供对企业数据的访问，而不是支持用户会话。一个实体 bean 可以支持多个并发用户，而容器则使访问和事务同步化。实体 bean 还具有支持本地对象中的 finder 方法的主键。知道实体 bean 的主键的客户机可以通过调用本地对象上的 findBy PrimaryKey() 方法获得对象引用。与会话 bean 不同，实体 bean 的本地对象除了具有 create 方法外还具有 finder 方法。

持久性是实体 bean 的一个基本属性。EJB 规范允许两种形式的实体持久性：bean 管理的持久性和容器管理的持久性。对于代表关系数据库中的数据的实体 bean，bean 对持久性的管理意味着，对数据库访问的调用是直接编写在企业 bean 的方法中的（使用 JDBC 或 SQLJ）。这种方法是直截了当的，但它降低了可移植性。容器对持久性的管理意味着 bean 不受数据库调用的影响。在安装时告知容器有关 bean 数据所需的持久性，而容器负责生成实现持久性的代码。这种方法允许 bean 的可移植性更高，甚至达到持久性可使用不同数据源的程度。然而，此方法要求容器中要有复杂功能。

当实体 bean 对象与 EJB 对象相关联时，前者处于[I]就绪[/I]状态；否则将认为它们处于[I]共享[/I]状态。当客户机调用 EJB 对象中的方法时，容器查找关联的实体 bean 的实例（如果存在的话），或者从共享状态中传送出一个实例。处于就绪状态的实体 bean 可以接收到通过委托从客户机传播给它们的业务方法调用。它们还可以在容器请求时执行 ejbLoad() 和 ejbStore() 方法。load 方法和 store 方法旨在维持实体 bean 和基础数据存储之间数据的一致性。

实体 bean 支持多个用户并发地访问数据。EJB 规范声明，维持数据完整性是容器的责任：

[BLOCKQUOTE]“enterprise bean 开发人员在编写业务方法时无需担心来自多个事务的并发访问。enterprise bean 开发人员在编写方法时可以假定，对于被多个事务同时访问的各个实体 bean，将能确保适当的同步化。”([I]Enterprise JavaBeans Specification 1.0[/I]) [/BLOCKQUOTE]

容器完成这一任务通常是通过锁定数据库中的数据，并使访问串行化，或通过创建实体 bean 的多个实例，并允许在基础数据存储中使用并发控制，这样来管理访问。

[b]第三部分内容预告[/b]

“什么是 Enterprise JavaBeans 组件？”的第三部分将讨论安装 EJB 组件的特殊部署过程。它还将说明 CORBA 是否是 EJB 组件的竞争对手（答案是“否” -- 请参阅 EJB 技术是如何补充 CORBA 的）。最后，您将看到一种基于 EJB 的三层编程模型的使用情况。

[b]参考资料[/b]

[UL][LI]查阅 [url href=ftp://ftp.javasoft.com/docs/ejb/ejb.10.pdf]Enterprise JavaBeans Specification 1.0[/url]。 [LI]要了解有关 EJB 体系结构的更多内容，请参阅 Anne Thomas 的 [url href=http://www.javasoft.com/products/ejb/pdf/white_paper.pdf][I]EnterpriseJavaBeans Technology: Server Component Model for the Java Platform[/I][/url]，Patricia Seybold Group。 [LI]要了解有关 Java 技术和 EJB 体系结构的更多内容，请访问 [url href=http://www.java.sun.com/products/ejb]Sun 的网站[/url]。 [/LI][/UL]

[b]作者简介[/b]

Ken Nordby 是 IBM 软件开发实验室的软件工程师，该实验室位于 Research Triangle Park，North Carolina。作为 SWG Product Affinity Services 业务小组的成员，Ken 与这样一些 IBM 人共事，他们为 IBM 的 WebSphere Application Server（即 Enterprise JavaBeans 技术的 IBM 实现）从事开发和咨询工作。可以通过 [email]nordby@us.ibm.com[/email] 与 Ken 联系。

[/TD][/TR][/TABLE]


第三部分：布署和使用 Enterprise JavaBeans 组件（二）（转）

作者：zergman

email: zergman@chinaasp.com

日期：2001-6-15 13:43:16

[b]使用 EJB 组件[/b]

因为 EJB 体系结构被设计为高度灵活的，并支持使用任意复杂的方式连接企业级 bean，所以可构建许多不同的方案，来说明应用程序可怎样使用企业级 bean。一个有用的方案提出将 EJB 组件表示为三层信息系统的关键组件，该系统将企业数据、事务和应用程序资源连接到 Web 上。

基于 EJB 的三层编程模型视 Web 浏览器为第一层，视支持应用程序的 Web 服务器为第二层，视企业信息资源为第三层。在此编程模型中，除了 EJB 技术外，还实现了 Java servlet 技术、JavaBeans 技术和 Java Server Page (JSP) 技术。下图显示了各层的排列情况：

[b][/b]

[img]http://www.cn.ibm.com/developerWorks/java/what-are-ejbs/part3/ejb2.gif[/img]

第一层是瘦客户机 -- 通常是 Web 浏览器，它可以处理普通 Web 数据类型，如 HTML 和 GIF，并支持 HTTP 通信。第二层是 Web 应用程序服务器，它是用代码扩充的 Web 服务器，用来对能够通过 Web 服务器调用的应用程序提供运行时支持。现有的 Web 应用程序都沿用 CGI-BIN 编程模型，但预计第二层应用程序开发将转向 Java servlet 编程模型，后者提供大幅改善的性能和可移植性。除支持 Java servlet 外，Web 应用程序服务器还将添加 EJB 服务器功能，以支持使用 EJB 组件的应用程序。第三层代表企业级信息资源，可以包括关系数据库和面向对象的数据库、事务监视器和定制的应用程序。EJB 技术在这一设计中扮演着关键角色，因为，它使驻留在第二层上的应用程序组件，与组成第三层的企业资源之间的接口，得以标准化。

Java servlet、Java beans 和 Java server page 不是 EJB 应用程序方案的必需元素，但它们可与 EJB 组件一起工作，以提供基于 Java 的内聚性的应用程序环境。此处描绘的环境将以下职责指定给参与工作的 Java 组件：[UL][LI]给 Java servlet 指定了应用程序“控制器”的角色 [LI]JSP 页面处理数据表示和用户界面的任务 [LI]Java bean 充当“数据封装器”，存储中间结果的数据 [LI]EJB 组件提供访问企业信息资源的机制[/LI][/UL]

客户可以使用一个假定的、基于 EJB 的 Web 应用程序更新现有的库存，并用容器管理式持久性和容器管理式事务，将访问库存数据库的位置封装在实体 EJB 组件的内部。库存票据可通过 Web 浏览器输入，浏览器提供一个 HTML 表单来捕获产品编号、供应商，等等，并在提交时调用一个 servlet。servlet 代码充当应用程序控制器角色，确定哪些企业数据库需要更新，需要用户追加什样的信息。servlet 可通过代表它的实体 bean 访问主库存数据库，并调用 JNDI 接口获取对此 bean 的本地对象的引用，然后使用 finder 方法定位所请求产品编号的远程对象。此时，通过调用远程对象的方法，servlet 可更新库存计数，接着容器将此方法委托给 EJB 组件。因为容器根据数据库更新，以对 bean 透明的方式划分一个事务，而且以对 bean 透明的方式将数据写入数据库来保证数据持久性，所以也就保持了数据的完整性。

从 EJB 组件返回到 servlet 的任何结果信息，都可以使用 setter 方法存储在一个（非企业的） Java bean 的属性中。此时 servlet 可将控制权转让给一个适当的 JSP 页面，以便将这些结果组合到表示格式中，并将结果返回给用户。JSP 页面很可能主要由静态文本和有关单个事务的可变信息占位符组成。在向浏览器发送表示数据之前，JSP 页面使用 getter 方法从 Java bean 的属性中获得可变数据元素。

基于 EJB 的三层设计提供了几个好处，包括：[UL][LI]访问企业数据的业务逻辑可封装在可重用、可移植的企业级 bean 中。 [LI]现有的企业系统只需很少修改或者根本不需要修改，就可以集成为企业级 bean。 [LI]企业应用程序所需的运行时服务，如事务和持久性，可以从 bean 中分解出来，并指定给此 bean 的容器。 [LI]无须更改 EJB 组件，即可修改控制应用程序流程的 Servlet。 [LI]Servlet 代码可将注意力集中在应用程序控制逻辑上，而无须考虑数据表示。 [LI]JSP 页面可将静态和动态内容混合在一起，生成表示信息。 [LI]用 Java 语言编写的系统组件，对于具有 JVM 的任何平台都是可移植的。[/LI][/UL]

[b]结论[/b]

在开发能够支持关键任务的企业级信息系统的过程中，EJB 规范代表了 Java 技术的下一个发展阶段。EJB 组件将随 JavaBeans 规范引入的 Java 组件模型，扩展到服务器领域，从而使业务逻辑组件的开发可以跨企业应用程序重用，并且可以跨支持 Java 的平台移植。由于包含了基于 RMI 技术的对象分布，所以支持跨多层的可执行组件的分立，从而允许最大的实现灵活性和高度可伸缩性。如果将常规的企业应用程序运行时服务重新定义为可指定给容器抽象的对象服务，则允许 EJB 组件的开发人员将精力集中在业务逻辑上，从而减小了通常与运行时服务相关的复杂性和平台相关性。

增强 Java 运行环境，以包括命名和目录服务的标准接口、关系数据库访问、事务服务和远程对象访问，使 Java 开发人员能够编写强健的企业应用程序，而不必离开 Java 编程环境。将其它 Java 技术 -- 如 Java servlet 和 JavaServer Pages 技术 -- 与 EJB 组件一起使用，可创建一个对于大型企业系统来说足够强健的紧凑编程模型，但由于使用了巧妙的接口，从而简化了开发工作。而且，因为 EJB 体系结构是 JavaBeans 组件模型的逻辑扩展，所以作为 EJB 组件开发的业务逻辑可跨多个企业应用程序重用。

企业级 bean 体系结构的另一个好处是，提供了现有企业信息系统的直接集成通道，此通道可能与 Java 编程语言或 bean 编程模型毫无共同之处。因为现有的企业信息资源 -- 如关系数据库、事务监视器和业务应用程序的定制新品种 -- 可通过将它们封装在 EJB 组件中连接到 Web 前端，而无须替换应用程序或重写主要代码段，所以，客户可保护其现有的 IT 投资。

考虑到 EJB 技术的巨大前景，IT 业界以相当大的兴趣欢迎 EJB 规范，就不是什么令人惊讶的事了。[I]EJB 体系结构提供的一个最大好处可能是，把业务逻辑编程与将业务逻辑和企业级服务器端运行环境的复杂集成分离开来[/I]。如果部署了 EJB 组件的容器承担了管理运行时服务（如持久性、事务和并发数据库访问）的职责，则 bean 的开发人员就可以自由地将精力集中在开发封装业务逻辑的软件组件上。JavaSoft 副总裁表述了 EJB 技术的重要性（引自 Sun Microsystems 网站）：

[BLOCKQUOTE]“‘Enterprise JavaBeans API 将为企业开发人员和解决方案提供商提供一种新的战略武器，供他们建立下一代行业领先的、基于关键业务的应用程序，’Sun Microsystems 的 JavaSoft 软件产品部副总裁，Jon Kannegaard 说：‘因为用 Enterprise JavaBeans API 设计的应用程序将与现有的企业系统一起工作，所以企业利用 Java 平台会获得新的竞争优势，同时还保留他们对现有技术的投资，’Kannegaard 继续说。 [/BLOCKQUOTE]

[BLOCKQUOTE]“使用 Enterprise JavaBeans API，开发人员将能够消除应用程序开发过程中的复杂性。这是可能的，因为每个 Enterprise JavaBeans 组件都封装了一个基本的业务功能。目前开发人员必须懂得如何编写业务逻辑和专门的系统级程序，以控制诸如安全性功能部件和处理多个事务的能力 -- 一项既枯燥又复杂的任务。Enterprise JavaBeans API 使全体开发人员能够将精力集中在编写解决业务问题的逻辑上，而不是将精力集中在编写用以简化不同技术间交互作用的代码上。”（[I]新闻稿：Sun Releases Draft Enterprise JavaBeans Specification for Public Review[/I]）[/BLOCKQUOTE]

[b]参考资料[/b]

[UL][LI]通过 [url href=http://java.sun.com/products/ejb/]Sun网站[/url]的 [url href=http://java.sun.com/products/ejb/faq.html]associated FAQs[/url] 深入了解 Java 技术和 EJB 体系结构。 [LI]查阅 [url href=ftp://ftp.javasoft.com/docs/ejb/ejb.10.pdf]Enterprise JavaBeans Specification 1.0[/url]。 [LI]请参阅 Enterprise JavaBeans 规范的完整[url href=http://www.javasoft.com/pr/1997/dec/pr971210-01.html]新闻稿[/url]。 [LI]请参阅 [url href=http://www.cn.ibm.com/developerWorks/java/what-are-ejbs/part3/appendix.html]IBM WebSphere Application Server 如何使用 EJB 技术[/url]。 [/LI][/UL]

[b]作者简介[/b]

Ken Nordby 是 IBM 软件开发实验室的软件工程师，该实验室位于北卡罗来纳州 Research Triangle Park。作为 SWG Product Affinity Services 业务小组的成员，Ken 与为 IBM WebSphere Application Server （Enterprise JavaBeans 技术的 IBM 实现）进行开发和咨询的 IBM 人一起工作。可以通过 [email]nordby@us.ibm.com[/email] 与 Ken 联系


第三部分：布署和使用 Enterprise JavaBeans 组件（一）（转）

作者：zergman

email: zergman@chinaasp.com

日期：6/15/2001 1:42:42 PM

[TABLE][TR][TD]什么是 Enterprise JavaBeans 组件？[/TD][TD][img]http://www.cn.ibm.com/developerWorks/i/c.gif[/img][/TD][TD][urlhref=http://www.cn.ibm.com/developerWorks/java/what-are-ejbs/part3/index_eng.shtml][img]http://www.cn.ibm.com/developerworks/i/src.gif[/img][/url][/TD][TD][img]http://www.cn.ibm.com/developerWorks/i/c.gif[/img][/TD][/TR][TR][TD][img]http://www.cn.ibm.com/developerWorks/i/c.gif[/img][/TD][/TR][TR][TD][img]http://www.cn.ibm.com/developerWorks/i/c.gif[/img][/TD][/TR][/TABLE][TABLE][TR][TD][img]http://www.cn.ibm.com/developerWorks/i/c.gif[/img][/TD][TD][TABLE][TR][TD][img]http://www.cn.ibm.com/developerWorks/i/c.gif[/img][/TD][TD][TABLE][TR][TD][img]http://www.cn.ibm.com/developerWorks/i/c.gif[/img][/TD][/TR][TR][TD][B]内容：[/B][/TD][/TR][TR][TD][img]http://www.cn.ibm.com/developerWorks/i/c.gif[/img][/TD][/TR][TR][TD][TABLE][TR][TD][url href=http://www.cn.ibm.com/developerWorks/java/what-are-ejbs/part3/index.shtml#0a]部署[/url][/TD][/TR][TR][TD][url href=http://www.cn.ibm.com/developerWorks/java/what-are-ejbs/part3/index.shtml#0]容器服务[/url][/TD][/TR][TR][TD][url href=http://www.cn.ibm.com/developerWorks/java/what-are-ejbs/part3/index.shtml#1]CORBA[/url][/TD][/TR][TR][TD][url href=http://www.cn.ibm.com/developerWorks/java/what-are-ejbs/part3/index.shtml#2]使用 EJB 组件[/url][/TD][/TR][TR][TD][url href=http://www.cn.ibm.com/developerWorks/java/what-are-ejbs/part3/index.shtml#3]结论[/url][/TD][/TR][TR][TD][url href=http://www.cn.ibm.com/developerWorks/java/what-are-ejbs/part3/index.shtml#resources]参考资料[/url][/TD][/TR][TR][TD][url href=http://www.cn.ibm.com/developerWorks/java/what-are-ejbs/part3/index.shtml#author]作者简介[/url][/TD][/TR][/TABLE][/TD][/TR][/TABLE][TABLE][TR][TD][img]http://www.cn.ibm.com/developerWorks/i/c.gif[/img][/TD][/TR][TR][TD][B]相关内容：[/B][/TD][/TR][TR][TD][img]http://www.cn.ibm.com/developerWorks/i/c.gif[/img][/TD][/TR][TR][TD][TABLE][TR][TD][url href=http://www.cn.ibm.com/developerWorks/java/what-are-ejbs/part1/index.shtml]第一部分：EJB 体系结构的历史和目标[/url][/TD][/TR][TR][TD][url href=http://www.cn.ibm.com/developerWorks/java/what-are-ejbs/part2/index.shtml]第二部分：编程模型[/url][/TD][/TR][/TABLE][/TD][/TR][/TABLE][TABLE][TR][TD][img]http://www.cn.ibm.com/developerWorks/i/c.gif[/img][/TD][/TR][TR][TD][img]http://www.cn.ibm.com/developerWorks/i/c.gif[/img][/TD][/TR][/TABLE][/TD][/TR][/TABLE]第三部分：布署和使用 Enterprise JavaBeans 组件

Ken Nordby

软件工程师，IBM

2000 年 7 月[BLOCKQUOTE]本文的第 3 部分说明 Enterprise JavaBeans 组件的部署过程，部署并不仅仅是安装，因为它通常还涉及代码生成。部署还使用了一个特殊的部署描述符文件，此文件支持控制企业级 bean 行为（如某个 bean 是否需要事务）的参数。bean 部署的这一特性支持 bean 行为的说明性、纲领性规范的 EJB 目标。第 3 部分还比较了持久性的两种主要类型，bean 管理式持久性和容器管理式持久性，并讨论了 EJB 组件与 CORBA 的关系。同时还给出了一个简单的三层 EJB 应用程序。[/BLOCKQUOTE]

[b]部署过程[/b]

Enterprise JavaBeans (EJB) 组件是在称为[I]部署[/I]的特定过程中安装的。由容器组件提供对部署过程的支持。在高级别上，部署由下列步骤组成： [UL][LI]bean 的开发人员创建必需的类文件、接口文件和控制信息。 [LI]容器分析输入文件并生成必要的类。 [LI]容器将条目添加到指向本地对象的 JNDI 命名空间中。[/LI][/UL]

EJB 组件的开发人员编写 bean 的 Java 源文件，此文件包含为这个 bean 提供功能的业务逻辑方法，还包括 ejbCreate() 方法。bean 类还必须实现 javax.ejb.SessionBean 接口或 javax.ejb.EntityBean 接口。此外，bean 的开发人员编写接口文件，定义对 javax.ejb.EJBHome 接口和 javax.ejb.EJBObject 接口的扩展。EJBHome 接口的扩展，称为 bean 的[I]本地接口[/I]，包含一个创建方法，并且如果 bean 是一个实体 bean，它还会包含一个 finder 方法。EJBObject 接口的扩展，称为 bean 的[I]远程接口[/I]，指定在 bean 本身中定义的业务逻辑方法。

bean 的开发人员提供由部署描述符、环境属性和清单式文件组成的控制信息。 [UL][LI]部署描述符是 javax.ejb.deployment.SessionDescriptor 对象或 javax.ejb.deployment.EntityDescriptor 对象的序列化实例。 [LI]环境属性作为键-值对存储在一个文件中，可通过 java.util.Properties 对象访问此文件。 [LI]清单式文件是标识企业级 bean 及其相关文件所必需的。 [/LI][/UL]企业级 bean 的类文件、这两个接口的类文件、部署描述符文件、环境属性文件和清单式文件都是使用名为 ejb-jar 的文件格式归档的。所生成的 ejb-jar 文件提供给容器，作为部署过程的输入。

在部署时，容器分析 ejb-jar 文件的内容，并采取必要的操作使此 bean 可用。这些操作包括：生成实现 bean 的本地和远程接口的新 Java 类，将本地接口实现绑定到 JNDI 命名空间中，生成桩模块和 skeleton helper 类，后者是支持 RMI 通信所必需的。容器也可以生成 bean 的子类，并入容器专用的代码，以方便对 bean 的管理。部署时由容器生成的类通常是容器专用的，而不像 EJB 组件本身那样具有可移植性。

[b]持久性、事务和安全[/b]

在为 EJB 组件提供持久性、事务和安全服务方面，EJB 容器可扮演主要角色。是将这些服务的职责指定给容器，还是假定职责由 bean 自身负责，EJB 规范为 bean 的开发人员提供了灵活性。例如，对实体 bean 的持久性支持既可以由 bean 管理，也可以由容器管理。如果 EJB 组件开发人员选择使用容器管理式持久性，他们就会在部署描述符中添加一个称为 containerManagedFields 的属性。根据 EJB 规范：

[BLOCKQUOTE]“containerManagedFields 属性的值是一个实例字段列表，企业级 bean 提供者希望，容器通过从数据库加载或将其存储到数据库，来管理这些实例字段。企业级 bean 代码不应该包含任何数据库访问调用 -- 数据库访问调用将由容器工具在部署时生成。[/BLOCKQUOTE]

[BLOCKQUOTE]“专用于提供容器管理式持久性支持的容器，通常将提供丰富的部署时工具，以允许企业级 bean 部署者建立实例字段到基础数据源的映射。一般认为，尽管容器提供者的工具简化了映射进程，但映射进程仍可能涉及到 bean 部署者（即映射进程不是全自动的）。”([I]Enterprise JavaBeans Specification 1.0[/I])[/BLOCKQUOTE]

除了支持容器管理式持久性以外，EJB 体系结构还支持容器对事务的管理。该规范规定：

[BLOCKQUOTE]“Enterprise JavaBeans 是一种高级组件框架，它试图使应用程序开发人员不面对系统的复杂性。因此，大多数企业级 bean 及其客户机不需要通过程序访问事务管理。”([I]Enterprise JavaBeans Specification 1.0[/I])[/BLOCKQUOTE]

当 bean 的开发人员依赖容器进行事务管理时，就称为[I]容器管理式定界[/I]，容器使用在部署时提供的事务属性：

[BLOCKQUOTE]“无论客户机何时调用企业级 bean，容器都会介入这个方法调用。这种介入允许容器通过事务属性显式控制事务定界。例如，如果企业级 bean 部署了 TX_REQUIRED 事务属性，则无论何时，只要客户机调用支持事务的企业级 bean，容器就会自动启动事务，而客户机并不与任何事务上下文相关联。”([I]Enterprise JavaBeans Specification 1.0[/I])[/BLOCKQUOTE]

如果开发人员选择在 bean 内支持事务，则他们在部署描述符中指定 TX_BEAN_MANAGED 事务属性，然后就可以在 bean 自身内部自由使用 javax.transaction.UserTransaction 接口划分事务边界。通过认出 TX_BEAN_MANAGED 事务属性，容器就能知道不必介入事务支持。

通过增强 AccessControlEntry 对象和 RunAs 安全标识中指定的限制，容器为 EJB 组件提供安全支持。AccessControlEntry 对象在 bean 级别上或针对单个方法，将 Identity 对象与企业级 bean 相关联。Identity 对象反映允许调用 bean 的方法的用户或角色。当容器试图访问数据源或另一个 bean 时，它们也会将 RunAs 安全身份应用于 EJB 组件。可将 RunAs 身份设置为等同于某个特定用户帐户、有权限的系统帐户或客户机安全身份。访问控制和 RunAs 的信息是 bean 的开发人员在部署描述符中指定的，将影响容器管理 bean 的与安全有关的行为方式。

虽然 EJB 1.0 规范也提到安全问题，但更详细的安全功能定义，见该规范的后续版本。

[b]CORBA 和 EJB 技术的关系[/b]

公用对象请求代理程序体系结构 (CORBA) 为分布式对象的平台中立和语言中立的计算环境奠定了基础。在 CORBA 环境中，功能驻留于对象之中，而客户机可通过对象请求代理程序 (ORB) 访问这些对象。完整的 CORBA 实现提供 ORB，外加称为 CORBA 对象服务和 CORBA 公用工具的几个运行时服务。也可只提供 ORB，不提供相关联的对象服务和公用工具（例如，IBM 就提供这样的两种独立 ORB）。实现基本 ORB 功能的软件称为 [I]ORB 核心[/I]。为了支持语言无关性，CORBA 应用程序是用接口定义语言 (IDL) 编写的。该语言在语法上类似于 C++，但不包含语义：IDL 中指定的操作是操作接口，而不是操作实现。由于它对多种平台和多种语言的支持，以及源自其分布式特征的可伸缩性，CORBA 非常适合于管理企业规模的信息系统。

设计 EJB 规范也是为了支持企业信息系统。这样说来，CORBA 是一个竞争者吗？根据 [I]Frequently Asked Questions for Enterprise JavaBeans[/I]，答案是否定的：

[BLOCKQUOTE]“实际上，EJB 技术很好地补充了 CORBA。CORBA 提供了一个强大的基于标准的基础结构，可在此结构之上构建 EJB 服务器。EJB 技术使得在 CORBA 基础结构的顶层构建应用程序变得更为容易。”（[I]Enterprise JavaBeans 常见问题解答[/I]）[/BLOCKQUOTE]

虽然 EJB 规范和 CORBA 规范说明的是不同的技术，但 EJB 实现目前利用 CORBA 技术的某些方面。一个例子就是 RMI/IIOP。EJB 规范要求 EJB 组件及其容器使用 Remote Method Invocation (RMI) 技术，实现分布式对象之间的方法调用。 RMI 规定远程方法的语法和语义，但并不规定应使用何种传输协议提供网络连接。CORBA Internet 对象请求代理程序间协议 (IIOP) 基本上定义了通过 TCP/IP 传输 CORBA 消息的一种方法。开发使用 IIOP 消息形式交换 RMI 数据的 EJB 实现，说明了 EJB 应用程序怎样才能有效地使用 CORBA 技术的各部分。这种网络也支持与 CORBA 应用程序的互操作性，后者使用 IIOP 发送本地 CORBA 消息，与 RMI 无关。IBM 的 EJB 实现，即 WebSphere Application Server，优化了 IIOP 的使用，方法是，弄清楚分布式对象何时驻留在同一台服务器上，并且只在对象确实在远程时才调用 IIOP。

为了方便既并入 EJB 技术，又并入 CORBA 技术的企业系统的开发，Sun Microsystems 在 EJB 规范和 CORBA 之间创建了一种映射。将 EJB 体系结构映射到 CORBA，影响到 EJB 技术的几个方面，包括对象分布、命名和事务。CORBA 映射的主要目的是，保证不同厂商构建的 EJB 服务器之间的互操作性。互操作性提供以下好处： [UL][LI]CORBA 客户机可以访问部署在基于 CORBA 的 EJB 服务器上的 EJB 组件 [LI]客户机程序在事务中可以将对 CORBA 对象的调用，与对企业级 bean 的调用混合在一起 [LI]事务可以跨多个 bean，而这些 bean 又位于来自不同厂商的基于 CORBA 的多台 EJB 服务器上 [LI]使用来自某个厂商的 ORB 的客户机，可以访问另一个厂商基于 CORBA 的 EJB 服务器上的 bean [/LI][/UL]

对于要访问 EJB 组件的 CORBA 客户机来说，bean 接口被映射到 IDL。例如，可将股票交易 bean 中定义的 buy() 和 sell() 方法，指定为 IDL 文件中的 CORBA 操作。非 bean 的 CORBA 客户机，如 C++ 客户机，可以访问这个 bean，并用标准 CORBA 调用来调用 bean 的方法。如果容器使用 IIOP 作为它的分布式对象协议，则该容器的职责是，生成与企业级 bean 及其接口对应的 IDL。

EJB 命名服务，它以“CORBA 对象服务”命名服务为基础，使 EJB 组件可用于 CORBA 客户机。Java Naming and Directory Interface (JNDI) 可提供到 CORBA 命名服务的接口，同时，客户机既可以通过 JNDI 调用间接访问基础命名服务，也可以通过“CORBA 对象服务” (COS) 命名 API 直接访问该服务。

EJB 事务支持依赖于 CORBA Transaction Service，即 Object Transaction Service (OTS)。Java Transaction Service (JTS) 代表 OTS 的 Java 绑定，它是语言中立的。基于 CORBA 的 EJB 容器必须识别 CORBA 客户机通过 OTS 接口发出的事务边界，以及 EJB 应用程序通过 Java Transaction API (JTA) 接口发出的事务，JTA 是到 JTS 的应用程序级接口。JTA 还代表 Open Group XA 接口的 Java 绑定，以便将应用程序资源连接到外部事务管理器。JIA 中含存的 javax.transaction.UserTransaction 接口，为事务边界的应用程序级控制提供 API。UserTransaction 接口，既可由其事务属性设置为 TX_BEAN_MANAGED 的 bean 使用，以可由 Java 客户机使用。[/TD][/TR][/TABLE]


第一部分：EJB 体系结构的历史和目标 （二）（转）

作者：zergman

email: zergman@chinaasp.com

日期：2001-6-15 13:37:10

[b]使用 EJB 技术的好处[/b]

这些设计目标会使企业和开发人员得到什么好处呢？下面列出了可望从采用 Enterprise JavaBeans 环境获得的好处：

[U]EJB 组件使编写应用程序更为简单[/U]。尽管 EJB 体系结构复杂，但应用程序开发人员一般都不必再编写用于访问系统服务的代码。一种称为 EJB 容器的系统组件使系统服务可用于 EJB 组件的任务。

[U]服务器端商务逻辑可以移植[/U]。除了 Java 语言固有的可移植性外，EJB 体系结构还在 bean 和支持该 bean 的容器之间提供了一套标准化的应用程序编程接口。这使开发人员能够将 bean 从一种操作环境移植到另一种操作环境，而无须重新编写其源代码。

[U]可以从现有的软件组件装配出服务器端应用程序[/U]，这与从现有的 Java bean 可以装配出客户端应用程序一样，从而使软件能够重用。

[U]EJB 体系结构内置了对典型企业级系统服务的支持[/U]，包括分布式对象、事务处理、数据库、安全和全局命名。

[U]多家 IT 供应商都采纳 EJB 体系结构[/U]，这是由于有这样的承诺：客户将能够从选定的供应商那里选购软件组件，如 EJB 组件、容器及 EJB 服务器；也由于承诺了不同供应商的产品，只要符合 EJB 体系结构，就都是可互操作的。

[U]用 EJB 组件构建的应用程序可以从一个服务器移植到另一个服务器，从而支持可伸缩性[/U]，这是因为在 EJB 模型中，各个软件组件都是严格分离的。

[U]EJB 体系结构能保障原有的 IT 投资[/U]，这是通过允许将现有的信息系统和资产“包裹”在这些应用程序中，而不要求客户更换现有技术。事实上，在关系数据库中存储数据的企业已经有了一套已有雏形的实体 bean，正等着通过 EJB 外壳去访问。

[b]进一步考察 JNDI[/b]

Enterprise JavaBeans 组件使用 Java Naming and Directory Interface (JNDI) 来访问各种目录服务。JNDI 分两部分：应用程序编程接口 (API) 和服务供应商接口 (SPI)：

[BLOCKQUOTE]“JNDI 体系结构由 JNDI API 和 JNDI SPI 组成。JNDI API 允许 Java 应用程序访问各种命名和目录服务。JNDI SPI 则是设计来供任意一种服务的供应商（也包括目录服务供应商）使用。这使得各种各样的目录服务和命名服务能够透明地插入到使用 JNDI API 的 Java 应用程序中。（见 JavaSoft，“JNDI: Java Naming and Directory Interface”）[/BLOCKQUOTE]

JNDI API 并不同某种专用的命名技术或目录技术连在一起，也不同任何供应商的目录服务连在一起，因此它对 EJB 组件的可移植性有所贡献。例如，客户可以从多种不同的技术中选择，来为其 EJB 应用程序提供目录服务，这些技术包括：[UL][LI]LDAP：Sun 的 LDAP 服务供应商支持 LDAP 协议的第 2 版和第 3 版。 [LI]NIS：Sun 提供一个 NIS 服务供应商（NIS 即网络信息服务，以前称为黄页）。 [LI]COS 命名：Sun 的 COS 命名服务供应商提供对 CORBA 命名服务的访问。 [LI]文件系统：Sun 提供一个服务供应商来访问文件系统。 [LI]RMI 注册：Sun 为 RMI 注册提供一个服务供应商。 [LI]Novell：有几个服务供应商可提供对目录服务 NDS 的访问以及 NetWare 3X 连接库、Novell 文件系统和其他 Novell 服务（如扩展 NCP）的访问。 [/LI][/UL]

虽然 JNDI 规范对供应商是中立的，但不应认为，实现 JNDI 接口的应用程序服务器一定要能访问来自多个供应商的服务供应商代码。

JNDI 命名体系结构的关键概念包括： [UL][LI]对象和名称之间的绑定。 [LI]若干称为命名上下文的绑定集。 [LI]命名系统，即若干组命名上下文。 [LI]命名空间，指一个命名系统中的所有名称。 [LI]名称分类为原子名称、复合名称和合成名称。[I]原子名称[/I]是不可分割的，可以绑定到一个对象上。[I]复合名称[/I]是原子名称的组合，而[I]合成名称[/I]则跨越多个命名系统。[/LI][/UL]

命名上下文特别重要：所有的命名操作均是在上下文对象上进行的，并且名称解析过程总是从最初的命名上下文开始。

EJB 应用程序是如何使用 JNDI 的呢？JNDI 的主要用途是检索对 EJB 组件的引用。因为 EJB 框架是一个分布式对象框架，所以 EJB 应用程序不应当假定 EJB 组件的位置。JNDI 就是获取对 bean 的起始引用的一种机制。当一个 bean 安装到一个 enterprise bean 服务器上时，一个被称为 EJB 容器的软件组件就负责创建各个名称-对象绑定，使所需的 Java 类文件能使用这个 bean。应用程序使用 JNDI 的查找方法来检索对象引用，如下例所示：[TABLE][TR][TD][PRE]Context initialContext = new InitialContext( );CartHome cartHome = javax.rmi.PortableRemoteObject.narrow( initialContext.lookup("applications/shopping_cart"), CartHome.class);[/PRE][/TD][/TR][/TABLE]

应用程序有责任知道外部名称，应用程序就是通过这个名称才得以引用一个 enterprise bean，并通过 JNDI 来获取对该 bean 的引用的。

[b]进一步考察 JTA[/b]

除 JNDI 以外，Enterprise JavaBeans 体系结构还使用 Java Transaction API (JTA)。因为事务对维护数据完整性和可靠性很重要，所以支持事务处理是 EJB 体系结构的一个基本部分。如果企业应用程序是分布式的，事务处理就会更加重要：

[BLOCKQUOTE]“事务的概念是一个重要的编程范例，其目的在于简化既要求可靠性又要求可用性的应用程序结构，特别是那些需要同时访问共享数据的应用程序。事务的概念最早是用在商务运作的应用程序中，其中它被用于保护集中式数据库中的数据。后来，事务的概念已扩展到分布式计算的更广泛的环境中。今天，事务是构建可靠的分布式应用程序的关键，这一点已得到广泛承认。”（见对象管理组的“Transaction Service Specification”）[/BLOCKQUOTE]

有时将事务描述为具有下列特征的工作单元： [UL][LI]原子性 — 如果因故障而中断，所有结果均撤销 [LI]一致性 — 事务的结果保留不变的特性 [LI]孤立性 — 中间状态对其他事务是不可见的 [LI]持久性 — 已完成的事务的结果是持久的 [/LI][/UL]

事务的终止有两种方式：[I]提交[/I]一个事务会使其所有的更改永久不变，而[I]回滚[/I] (rolling back) 一个事务则撤销其所有的更改。

对象管理组织 (OMG) 为一种面向对象的事务服务，即对象事务服务 (OTS)，创建了一种规范。OTS 是 EJB 体系结构内的事务服务的基础。下列事务规范就是为 enterprise bean 所采用的事务模型而设：[UL][LI]OMG 的对象事务服务 (OTS) [LI]Sun Microsystems 的 Transaction Service (JTS) [LI]Sun Microsystems 的 Java Transaction API (JTA) [LI]开放组 (X/Open) 的 XA 接口[/LI][/UL]

这种与语言无关的对象事务服务，为一个强健的分布式事务服务提供了基本概念、定义和功能。

Java Transaction Service 是 OTS 的 Java 映射，在 org.omg.CosTransactions 和 org.omg.CosTSPortability 这两个包中定义。JTS 对事务分界和事务环境的传播之类的服务提供支持。JTS 功能由应用程序通过 Java Transaction API 访问。

Java Transaction API 指定事务管理器与分布式事务中涉及的其他系统组件之间的各种高级接口，这些系统组件有应用程序、应用程序服务器和资源管理器等。JTA 功能允许事务由应用程序本身、由应用程序服务器或由一个外部事务管理器来管理。JTA 接口包含在 javax.transaction 和 javax.transaction.xa 这两个包中。

XA 接口定义了资源管理器和分布式事务环境中外部事务管理器之间的约定。外部事务管理器可以跨多个资源协调事务。XA 的 Java 映射包含在 Java Transaction API 中。

[b]内容预告[/b]

“什么是 Enterprise JavaBeans 组件？”的第二部分将讨论 EJB 编程模型。

[b]参考资料[/b]

[UL][LI]要了解更多有关 Java 技术和 EJB 体系结构的内容，请访问 [url href=http://java.sun.com/products/ejb]Sun的网站[/url]。 [LI]关于从 applet 和应用程序的角度看 Java 编程，请阅读 David Flanagan 所著的 [url href=http://search.borders.com/fcgi-bin/db2www/search/search.d2w/Details?&mediaType=Book&prodID=40633633][I]Java in a Nutshell[/I][/url]。 [LI]下载一份 [url href=http://www.javasoft.com/products/servlet/download.html]Java Servlet API Specification[/url] 的副本。 [LI]下载 [url href=http://www.sys-con.com/java/index2.html]Java Developer's Journal[/url] 第 3 卷第 1 期中 Tom Kim 所写的“Looking for a 3-Tier App Builder?”(PDF)。 [LI]参加 JavaSoft 的[url href=http://www.javasoft.com/beans/docs/Tutorial-Sep97.pdf]“Using the Beans Development Kit 1.0”[/url]课程。 [LI]从 [url href=http://www-4.ibm.com/software/developer/library/jb-guidelines.html]“JavaBeans Guidelines”[/url]中可以发现一些补充的指导原则，它们使您能开发出性能优良的 Bean，这些 Bean 能够在大多数环境中表现良好，包括流行的各种 IDE 和各种浏览器。 [LI]阅读 Patricia Seybold Group 的 Anne Thomas 所写的 [url href=http://www.javasoft.com/j2ee/j2eeAnneThomas.pdf]“Java 2 Platform, Enterprise Edition: Ensuring Consistency, Portability, and Interoperability”[/url] 中对 J2EE 的详细说明。 [LI]查阅 [url href=ftp://ftp.javasoft.com/docs/jndi/jndi.pdf]“JNDI: Java Naming and Directory Interface”[/url] 中的 JNDI 体系结构和接口的概述，以及各种情况和示例。 [LI]了解 [url href=http://www.java.sun.com/j2ee/overview.html]Java 2 Platform, Enterprise Edition[/url] 中包含的以下 Java 技术： [UL][LI][url href=http://www.javasoft.com/products/jdk/idl/index.html]Java IDL [LI][url href=http://www.java.sun.com/products/jms/][/url]Java Message Service (JMS)[/url] API [LI][url href=http://www.java.sun.com/products/jndi]Java Naming and Directory Interface (JNDI)[/url] [LI][url href=http://www.java.sun.com/products/rmi/]Java Remote Method Invocation (RMI)[/url] [LI][url href=http://www.java.sun.com/products/jta/]Java Transaction API (JTA)[/url] [LI][url href=http://www.java.sun.com/products/jts/]Java Transaction Service (JTS)[/url] [LI][url href=http://www.java.sun.com/products/jsp/]JavaServer Pages (JSP) 技术[/url] [LI][url href=http://www.java.sun.com/products/jdbc/]JDBC 数据访问 API[/url][/LI][/UL][LI]要了解常见问题、与 LDAP 相关的 RFC 以及更多信息，请访问 [url href=http://www.umich.edu/~dirsvcs/ldap/doc/]Mark Wahl 的 LDAP 网站[/url]。 [LI]要了解如何将 Linux 配置为 NIS(YP) 或 NIS+ 客户机及如何安装成 NIS 服务器，请查阅 [url href=http://metalab.unc.edu/mdw/HOWTO/NIS-HOWTO.html]Linux NIS(YP)/NYS/NIS+ HOWTO[/url]。 [LI]下载 Java IDL 的命名服务 [url href=http://spock.bf.rmit.edu.au/javadoc/api/org/omg/CosNaming/package-summary.html]COS naming[/url]。 [LI]详细了解 [url href=http://www.javasoft.com/products/jdk/rmi/index.html]Java Remote Method Invocation (RMI)[/url]，包括规范、示例和常见问题。 [LI]了解 [url href=http://www.novell.com/]Novell[/url] 提供的大量的产品和解决方案。 [LI]访问 [url href=http://www.omg.org/]Object Management Group (OMG) 的网站[/url]。 [LI]查阅开放组的 [url href=http://www.opengroup.org/pubs/catalog/s423.htm]XA interface[/url] 规范。 [/LI][/UL]

[b]作者简介[/b]

Ken Nordby 是位于 Research Triangle Park，North Carolina 的 IBM 软件开发实验室的软件工程师。作为 SWG Product Affinity Services 工作组的成员，Ken 和他的 IBM 同事从事 IBM WebSphere Application Server（Enterprise JavaBeans 的 IBM 实现）的开发及咨询工作。可以通过 [email]nordby@us.ibm.com[/email] 与 Ken 联系。


第一次jsp接触(win32安装版)

作者：jeru

日期：2000-11-9 15:50:41

一. 安装jdk

1. 下载jdk1.2.2的win32版本,你可以由以下这个链接得到。

http://java.sun.com/products/jdk/1.2.2/download-windows.html

2. 测试。

打开msdos窗口，set JAVA_HOME = C:\JDK1.2.2，然后编辑一个名为HelloWorld.java

的文件(注意大小写)。

class HelloWorld {

    public static void main(String[] args) {

        // Display "Hello World!"

        System.out.println("Hello World!");

    }

}

存盘后用jdk1.2.2/bin/javac.exe 来编译它----javac HelloWorld.java。

得到一个新的文件HelloWorld，再用jdk1.2.2/bin/java.exe 来运行她----java HelloWorld,

看到运行结果了吗？恭喜，我们可以做第二步了！

二. 安装Tomcat

Tomcat 无须安装，随便放到什么目录，比如说C:\Tomcat吧。

在dos下敲如以下命令：

set CLASSPATH = C:\JDK1.2.2\lib\tools.jar

set TOMCAT_HOME = C:\Tomcat

当然最好是把这些设置环境变量的语句放到autoexec.bat中去拉，^*_*^

运行C:\tomcat\bin\startup.bat(关闭时运行shutdown.bat)

进入C:\tomcat\webapps\examples\jsp\,这里是存放jsp文件的目录，当然你也可以自己

修改配置的，编辑一个名为time.jsp的文件,由于众所周知的原因，请自行去掉html标记里的空格

< html >

< head >

< title >First Page< /title >

< /head >

< body >

< h3>Today is:

<%= new java.util.Date() %>

< /h3 >

< /body >

< /html >

这是一个显示时间的jsp程序，也是你的第一个jsp程序。

存盘关闭，打开浏览器，url栏输入http://localhost:8080/examples/time.jsp

大功告成,看到显示的当前时间了吗?


多线程程序设计 (转)

作者：jeru

日期：2001-3-7 11:11:58

多线程程序设计

◆ 多线程简介

    线程（thread）是操作系统分配 CPU 时间的基本实体。每一个应用程序至少有一个线程，也可以拥有多个线程。线程是程序中的代码流。多个线程可以同时运行，并能共享资源。

    线程与进程不同，每个进程都需要操作系统为其分配独立的地址空间。而同一进程中的各个线程是在同一块地址空间中工作。

    在 Java 程序中，一些动态效果（如动画的实现、动态的字幕等）常利用多线程技术来实现。

    在 Java 语言中可以非常方便地使用多线程。和 Visual C++ 相比，Java 的多线程既容易学习，又容易使用。

◆ 创建多线程的两种办法：

    （1）建立类 Thread 的子类

    （2）实现接口 Runnable

    第二个办法比第一个使用得更为广泛。本讲座重点讲解第二个办法。

◆ 接口

    Java 语言取消了 C++ 的多重继承（“多重继承”常常使 C++ 程序员陷入混乱之中）。Java 增加了“接口”（interface）的概念，使 Java 在取消多重继承后，并未使功能下降。

    “接口”（interface）是一种特殊的类。当你定义一个类时，可以“实现”（implements）一个（或多个）接口。语法如下：

    class 类名 extends 超类名 implements 接口名

◆ 例 1.5.1 一个最简单的多线程小应用程序

import java.applet.*;

import java.awt.*;

public class k04a extends Applet implements Runnable

{

    private Thread m_k04a = null;

    public k04a()

    {

    }

    public void paint(Graphics g)

    {

        g.drawString("Running: " + Math.random(), 10, 20);

    }

    public void start()

    {

        m_k04a = new Thread(this);

        m_k04a.start();

    }

    public void stop()

    {

        m_k04a.stop();

        m_k04a = null;

    }

    public void run()

    {

        while (true)

        {

            try

            {

                repaint();

                Thread.sleep(200);

            }

            catch (InterruptedException e)

            {

                stop();

            }

        }

    }

}

◆ 控制线程的生命周期

    （1）start()方法 启动一个线程

    （2）run()方法 定义该线程的动作

    （3）sleep()方法 使线程睡眠一段时间，单位为毫秒

    （4）suspend()方法 使线程挂起

    （5）resume()方法 恢复挂起的线程

    （6）yield()方法 把线程移到队列的尾部

    （7）stop()方法 结束线程生命周期并执行清理工作

    （8）destroy()方法 结束线程生命周期但不做清理工作

    其中最常用的是start(),run(),sleep(),stop()。

◆ try —— catch 语句

    用于对“异常”的处理。和“错误”相比，“异常”是比较轻微的。它是指程序在运行中发生的意外情况。（try - catch 语句在 C++ 中也有）。

    在执行 try 后面的语句时，如果发生异常，则执行 catch 后面的语句。

◆ 例 1.5.2 流动的标题

    该程序在运行时，三个标题在由下而上不断变换。

HTML 文件中的写法：（三个图片要事先做好）

JAVA 源程序：

import java.awt.*;

import java.applet.Applet;

public class testani extends Applet implements Runnable

{

    Thread runner;

    Image imgs[];

    int high, y1, y2, y3;

    public void init()

    {

        high = size().height;

        y1 = high;

        y2 = high*2;

        y3 = high*3;

        imgs = new Image[10];

        for(int i=0;i<3;i++)

            imgs[i]=getImage(getCodeBase(),getParameter("image"+(i+1)));

    }

    public void start()

    {

        runner = new Thread(this);

        runner.start();

    }

    public void stop()

    {

        runner.stop();

        runner = null;

    }

    public void run()

    {

        while (runner != null)

        {

            try

            {

                Thread.sleep(100);

                repaint();

                y1--;

                if(y1==0)

                {

                    Thread.sleep(3000);

                    y2=high;

                }

                y2--;

                if(y2==0)

                {

                    Thread.sleep(3000);

                    y3=high;

                }

                y3--;

                if(y3==0)

                {

                    Thread.sleep(3000);

                    y1 = high;

                }

            }

            catch (InterruptedException e){}

        }

    }

    public void paint(Graphics g)

    {

        g.drawImage(imgs[0], 0, y1, this);

        g.drawImage(imgs[1], 0, y2, this);

        g.drawImage(imgs[2], 0, y3, this);

    }

    public void update(Graphics g)

    {

        paint(g);

    }

}


发邮件时终于可以通过sina的smtp验证了（附代码）

作者：halfhour

日期：2001-5-21 11:10:33

import javax.mail.*;

import javax.mail.internet.*;

import java.util.*;

public class sendMail

{

    public static void main(String args[]) throws Exception

    {

        String host = "smtp.sina.com.cn";

        String from =  "javamail@sina.com";

        String to = "javamail@china.com";

        String username = "javamail";

        String password = "password";

        // Get system properties

        // Properties props = System.getProperties(); 很多例子中是这样的，其实下面这句更好，可以用在applet中

        Properties props = new Properties();

        // Setup mail server

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

        props.put("mail.smtp.auth", "true"); //这样才能通过验证

        // Get session

        Session session = Session.getDefaultInstance(props);

        // watch the mail commands go by to the mail server

        session.setDebug(true);

        // Define message

        MimeMessage message = new MimeMessage(session);

        message.setFrom(new InternetAddress(from));

        message.addRecipient(Message.RecipientType.TO,

          new InternetAddress(to));

        message.setSubject("Hello JavaMail");

        message.setText("Welcome to JavaMail");

        // Send message

        message.saveChanges();

        Transport transport = session.getTransport("smtp");

        transport.connect(host, username, password);

        transport.sendMessage(message, message.getAllRecipients());

        transport.close();

    }

}


发主流技术的评价之JSP(转帖）

作者：blackwhites

日期：2000-10-23 9:30:25

JSP又是什么原因出现的呢？Java在推向开发人员时出现了重重困难，首先是Java缺少主流系统的广泛支持，而在所有的平台和技术上都缺少可以直接参照的应用方式。这时java就走向了在各个应用层上的积极进取，在自己擅长的与不擅长的领域都走出了坚定的脚步。其中最让Web技术人员兴奋的就是Java Servlet的出现和JSP的渐渐成熟，这一步步都让Java正式的走向了Web开发技术的舞台。Java利用了它的跨平台技术，使java的应用能快速的部署在各个平台上，而真的可以一处编写随处运行。它有较的利用了这个优势，巧妙的将java的通用性、jsp的易用性、servlet的速度结合在一起，利用平台部署上的技巧有效的屏蔽java的速度问题，所以Java迅速的在Web开发人员的团员中推广开来。携着Java Bean、EJB、Java Application Server、Java事务服务器等这样的强大后台走向了真正的成熟应用。

    Jsp到底是一个什么样的语言呢？实际上JSP就是Java，只是它是一个特别的Java语言，加入了一个特殊的引擎，这个引擎将HTTPServlet这个类的一些对象自动进行初始化好让用户使用，而用户不用再去操心前面的工作。同时这个引擎又引入了<jsp:*、<%@等一系列的特别语法，使用这些语法来进行一些特定的操作，如引用文件、URL转向、JavaBean引用等这样的操作，这些都是由引擎自动帮你完成初始化的工作。我们可以将这个引擎看作一个Jsp到Java Servlet的生成器或是翻译器，让用户不用再面对复杂的Servlet中的还没开始工作就要写的几十行代码。这就是JSP--一个Java Servlet生成器。JSP->Java Servlet Code(.java文件)->Java Runtime Bin Code(.class文件)，这就是为什么我们第一次运行JSP时CPU运行能达到很高的原因，它实际上是调用了jsp引擎来生成java文件，再用javac来编译它到class文件，这才能去执行它。同时也说明了在第一次运行以后，速度为什么哪么快的原因，只要这个jsp文件不改变哪么这个操作就不再会进行了(除非你删除了class文件)。所以说，java有什么jsp就有什么样的功能，与asp一样，jsp的真正力量是我们永远看不到头的！

    强势：

    1、一处编写随处运行。这是一个程序员的梦想，也是从前的程序员的恶梦，为了在不同的平台间运行，使许多程序员一行行的重写代码。在这一点上java已经给了我最完美的答案它做的要比php更出色，除了系统之外，你的代码不用做一点更改的。

    2、系统的多台平支持。我已经在差不多所有平台上见到了一致的java servlet/jsp的Web Server/Application Server……系统它可以让你在任意环境做开发，在任意环境进行系统部署，在任意环境扩展。相比asp/php的局限性是显面易见的。

    3、强大的的可伸缩性。从只有一个小的jar文件就可以运行servlet/jsp到由多台服务器进行集群和负载均横，到多台Application进行事务处理、消息处理......一台服务器到无数以服务器，Java显示了一个巨人的生命力。

    4、多样化和功能强大的开发工具支持。这一点与asp很像，java已经有了许多非常优秀的开发工具而且有许多可以免费得到，并且其中的许多已经可以顺利的运行于多种平台之下。如果你细心的使用它们会发现比自己第一面看到它们时的功能要强大的多。

    弱势：

    1、与ASP一样，Java的一些优势正是它致命的问题所在。正是由于为了跨平台的功能，为了极度的伸缩能力，所以极大的增加了产品的复杂性。这么说，它在扩展时需要的分成多少块，哪么java系统中就有多少种产品。所以你要以看到jre、jdk、jsdk、jswdk……，实际上它们只要有效的搭配在一起，就可以产生强大的效能，密不可分，但是对于一位初学者来说：“有没有搞错！难道让我运行一个jsp就要这么多乱七八糟的东西！”

    2、从出现的第一天起就被对手痛骂的：“慢如老牛”。真的，我在第一次运行我的jsp时，我竟然以为我的机器死机而将哪个进程给kill了！第一次编绎、运行对于使用者来说真是一场恶梦，在开发时我经常晚上做梦在看WinNT的任务管理器CPU格全绿时等着降下来的样子和感觉。

    3、技巧还是技巧。一个短处就一定要付出代价的。Java的运行速度是用将class常驻内存来完成的，所以它在一些情况下所使用的内存比起用户数量来说却实是“最低性能价格比”了。另一方面来说，它还需要硬盘空间来存储一系列的.java文件和.class文件以及对应的版本文件。

    4、缺少系统性的资料。这也许不该说，但是sun在国内做的却实很差劲，java的资烊在国内极少，到现在我还没有在书店里看到一本jsp的书，而对java体系的论述书更是少而又少，与java相关的书(中文版)到现在不过三位数。而再看看之中大量的具然还在讲Visual J++或是Java 1.1这样的阵年旧货。

    总结：

    适用人群：具有较强学习能力和耐心的开发人员。投入低但是希望拥有较大的伸缩能力的应用。

    适用平台：……(太多而只不再一一列说)适用应用：Internet/Intranet高可靠性应用、Internet/Intranet与Client/Server结合的应用系统

    学习方式：Java的Doc文档和java站点中的一系文档Internet的源代码资源(对不起大家，真的不能提出更好的建议了，再有的就是大家的能力和运气，以及Inetnet上网友的帮助了)

    推荐开发工具：JBuilder/Visual Age for java(这个我没用过，但是有人强力推荐)/Sun Forte for JavaKAWA

    推荐开发环境：Windows NT/Windows 2000开发系统JBuilder 3.5(它能成为Web Server，甚至不再需要其它的工具)Tomcat 3.1JDK 1.3(可以不安装)Access 2000/P II 266/128M RAM/9G HD

    推荐应用环境(最少)：RedHat Linux 6.2/MySQL 3.22/Apache 1.3.12/Tomcat 3.1/JDK 1.3Beta/PIII 500/512M RAM/9G HD

    推荐应用环境(最佳)：Solaris 8/Sybase 11/iPlanet Enterprise Web Server 4.1/JDK 1.3Beta/Sun Netra T1/1G RAM/18G HD


分别用java Application 及 JSP调用javaBean的例子

作者：webwing

email: webwing@21cn.com

日期：2001-1-12 11:24:45

//java bean

//file name: faq.java

import java.sql.*;

public class faq {

    String sDBDriver = "sun.jdbc.odbc.JdbcOdbcDriver";

    String sConnStr = "jdbc:odbc: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;

    }

}

//java Application

//file name: testfaq.java

import java.sql.*;

class getRs

{

    public static String Answer;

    public static String Subject;

    public static String[][] rsArray = new String[9][2];;

    public static int n = 0;

    getRs(){

        try    {

            faq workM = new faq();

            ResultSet oRs = workM.executeQuery("SELECT * FROM faq");

            //System.out.println(oRs.getCount());

            while (oRs.next()){

            System.out.println(oRs.getString("Answer"));

            System.out.println(oRs.getString("Subject"));

            n=n+1;

            }

        }catch (SQLException ex)

        {

            System.out.println("error:"+ex);

        }

    }

}

public class testfaq {

    public static void main(String[] args) {

        getRs aa = new getRs();

    }

}

//jsp

//file name: testfaq.jsp

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=gb2312">

<title>我的FAQ !</title>

</head>

<body>

<p><b>这是我的FAQ!</b></p>

<%@ page contentType="mimeType; charset=gb2312"%>

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

<%

faq workM = new faq();

ResultSet RS = workM.executeQuery("SELECT * FROM faq");

String tt;

while (RS.next()) {

tt = RS.getString("Answer");

out.print("<LI>" + RS.getString("Subject") + "</LI>");

out.print("<pre>" + tt + "</pre>");

}

RS.close();

%>

</body>

</html>


分布式对话服务器的管理（1）

作者：运气

email: webmaster@chinaspx.com

日期：7/4/2001 1:40:53 PM

    摘要：

    通过使用JDK 1.3中引入的RMI和Proxy API，本篇文章讨论了一种允许一台或多台servlet服务器在一台或多台对话服务器上维护对话信息的技术，采用这种技术后，单一点故障就不会再出现了。

    如果系统中有一台或多台servlet服务器，对话信息只存在于运行着JVM的一台servlet服务器上，而不会被传输给其他servlet服务器。如果该servlet服务器当机或因为维护而被关机，任何保存在对话中的信息都会丢失。如果一个系统中有多台servlet服务器，一个带有对话的用户需要访问对话中的任何信息，都需要被重新定向到同一台servlet服务器。曾经有专家建议采用关系数据库保存所有的对话信息，但这仍然存在单一点故障的危险，那就是运行关系数据库的服务器。而且如果数据库出了故障，所有的servlet服务器就都不能再访问对话信息了。另外，在数据库中保存可串行化的对象在有些数据库中是比较难以实现的。

    多服务器对话管理的另一个可能的途径是利用JavaSpaces API来维护对话对象中的记录。当然，如果运行JavaSpaces的服务器由于维护或故障而被关机，也会丢失所有的对话信息，我们再一次遇到了单一点故障的问题。

    要实现带有N个节点的分布式对话服务器，我们必须解决如下的三个问题：

    ━━如何建立一个库来存贮对话信息。

    ━━如何对分布式对话信息存贮库进行同步。

    ━━在一个对话信息存贮库脱离网络后，如何使该服务器从下一个对话信息存贮库中访问对话信息。

    Mnemosyne的简介

    我们用来存贮对话信息的库是Mnemosyne界面的执行。执行Mnemosyne的对象负责管理对话信息存贮库中的所有对象，任何试图写、访问或删除库中对象的对象都必须调用Mnemosyne的相应的方法来实现相应的操作。

    一个对象要存贮在Mnemosyne，就必须执行Memory界面，该界面定义了equalsMemory()操作来探测二个内存对象是否相同，这就使 Mnemosyne判断出应当把哪个对象返回给read要求或take要求。Memory界面也可以进行串行化扩充，以便我们可以用RMI在网络上传输该对象。


分布式对话服务器的管理（2）

作者：运气

email: webmaster@chinaspx.com

日期：7/4/2001 1:41:17 PM

    Mnemosyne使用三种界面表达其状态。

    1、CommonContext界面存贮Mnemosyne的全部信息。每个Mnemosyne都有一个CommonContext对象的实例，以便read、write、take Memory对象时在各个方法之间进行同步。在write或者take说Memory对象时，CommonContext对象既定义“silent”方法也定义“loud”方法，当在不进行事件通知的情况下添加对象时，就会用到“silent”方法。例如，当Mnemosyne对象接收到WriteRemoteEvent（向一个远程Mnemosyne对象写对象的告示。）事件后，如果它也希望向CommonContext对象写另一个对象，它就无需通知其他的远程CommonContext对象，最初的Mnemosyne已经通知了它们，因此这种写是通过调用CommonContext对象的silentWrite()方法“静悄悄”地完成的。而“loud”方法则在有对象第一次被添加时将这一事件的详细信息通知所有被调用的监听对象。

    2、Transaction对象用于在read、write、take Memory对象时进行分布式事务处理，这意味在Mnemosyne对象上可能会有多步骤的操作。

    3、TransactionContext界面管理分布式事务，使得系统可以中止或提交一个事务。

    保持Mnemosynes的同步是通过synchronize()和notify()这二个由 Mnemosyne定义的方法完成的。synchronize()可以使一个本地的Mnemosyne与其他Mnemosyne的Vector保持同步（这些Mnemosyne可以是本地的或远程的，为简明起见，我们假定它们是远程的。）下面是synchronize()的一个例子：

public void synchronize(Vector Mnemosynes)

throws RemoteException, TransactionException

{

file:// MatchAllMemory对象是一个有效类

file://对任何二个Memory对象进行比较

MatchAllMemory matchAllMemory = new MatchAllMemory();

file:// 从Primary中获得所有Memory

Mnemosyne Mnemosyne = (Mnemosyne) Mnemosynes.firstElement();

Vector allMemories = Mnemosyne.readAll(matchAllMemory,null);

commonContext.silentWriteAll(allMemories);

// 注册进行发送、接收事件

Enumeration enumeration = Mnemosynes.elements();

while(enumeration.hasMoreElements())

{

Mnemosyne nextMnemosyne = (Mnemosyne) enumeration.nextElement();

file://注册接收通知

nextMnemosyne.addTakeRemoteEventListener(this, matchAllMemory);

nextMnemosyne.addWriteRemoteEventListener(this, matchAllMemory);

file:// 注册发送通知

addTakeRemoteEventListener(nextMnemosyne, matchAllMemory);

addWriteRemoteEventListener(nextMnemosyne, matchAllMemory);

}

// ...

}

    本地的Mnemosyne对象读取Vector中第一个Mnemosyne对象的所有Memory对象，并采用“silent”方法将它们写到其CommonContext对象中。然后，本地Mnemosyne将自己作为TakeRemoteEventListener和WriteRemoteListener添加到所有的远程Mnemosyne中，这就意味着任何对远程Mnemosynes的take或read操作都将调用本地Mnemosyne的notify()方法。最后，本地Mnemosyne将远程Mnemosyne添加到其TakeRemoteEventListeners和WriteRemoteListeners队列中，确保对本地Mnemosyne的write或take操作都会通知远程Mnemosyne。


分布式对话服务器的管理（3）

作者：运气

email: webmaster@chinaspx.com

日期：7/4/2001 1:41:45 PM

    当添加或删除一个Memory对象时，经过同步的本地Mnemosyne对象需要对所有的Mnemosyne进行更新，可以通过notify()方法来完成这一任务。无论是发生write或take事件，Mnemosyne都会针对发生的事件调用适当的监听者的notify()方法。在synchronize()方法中，我们把本地Mnemosyne注册为所有远程Mnemosyne的take和write事件的监听者，一旦远程Mnemosyne上有take和write事件发生，就会调用本地 Mnemosyne的notify()方法。然后，本地Mnemosyne必须对事件作出反应。下面是Mnemosyne如何与远程Mnemosyne进行同步的例子：

public void notify(RemoteEvent remoteEvent) throws RemoteException

{

// 回写被写的内存，但无需通知所有的Mnemosyne

if(remoteEvent instanceof WriteRemoteEvent)

{

WriteRemoteEvent wre = (WriteRemoteEvent) remoteEvent;

commonContext.silentWrite(wre.getMemory());

}

file:// 取被写的Memory，但无需通知所有的Mnemosyne

if(remoteEvent instanceof TakeRemoteEvent)

{

TakeRemoteEvent tre = (TakeRemoteEvent) remoteEvent;

commonContext.silentTake(tre.getMemory());

}

}

    现在已经创建了一个控制所有memory对象的Mnemosyne，它自动与远程Mnemosyne保持同步，如果任何一个远程Mnemosynes得到或失去一个Memory对象时，都可以使它保持最新的状态。

    要通过Mnemosyne管理HTTP对话，servlet需要创建HttpSession的实例（从HttpServletRequest中使用getSession()），在实现Memory对象的类中封装对话，并调用Mnemosyne对象的write()方法把封装类写到一个Mnemosyne中。

    通过调用write()方法，封装着对话的Memory对象沿着网络传送给Mnemosyne，并通知远程机器。当对象被写到Mnemosyne时，WriteRemoteEvent被发送给在Mnemosyne上注册的所有WriteRemoteEventListeners，这样，所有其他的Mnemosynes就能将新的对象作为Mnemosynes添加到它们的对话信息存贮库中。

    要对存贮的对话进行查询，servlet调用read()方法查找包含对话的Memory对象，如果Mnemosyne找到了要查找的对象，则该对象通过RMI返回到servlet服务器。

    最后，要删除对话，servlet就会调用Mnemosyne的take()方法，Mnemosyne将象有read事件发生那样退还Memory对象，同时从其存贮对象库中删除该Memory对象。同时，向其所有TakeRemoteEventListeners发送TakeRemoteEvent事件，通知所有的远程Mnemosynes该Memory对象已经被删除了。

    建立对话服务器

    上面我们已经讨论了如何在多服务器上维护对话存贮库，下面我们将讨论如何建立对话服务器。在初始化过程中，对话服务器完成下列任务：

    ━━创建本地Mnemosyne对象。

    ━━把本地Mnemosyne绑定到RMI。

    ━━把本地Mnemosyne与其他的远程Mnemosyne进行同步。


分布式对话服务器的管理（4）

作者：运气

email: webmaster@chinaspx.com

日期：7/4/2001 1:42:32 PM

    首先，对话服务器将获得Mnemosyne对象的一个实例，该实例被绑定到对话服务器的本地IP上。

protected void bindMnemosyne()

{

file://得到Mnemosyne

Mnemosyne Mnemosyne = null;

try

{

Mnemosyne = MnemosyneFactory.getMnemosyne();

}

catch(RemoteException remoteException)

{

System.out.println("Internal error:");

System.out.println("Can't create a Mnemosyne");

System.exit(1);

}

// 把Mnemosyne绑定到MnemosyneImpl

try

{

String rmiURL = "//" + _localIP + "/MnemosyneImpl";

Naming.rebind(rmiURL, Mnemosyne);

}

catch(ArrayIndexOutOfBoundsException ArrayIndexOutOfBoundsException)

{

throw new IllegalArgumentException("LocalIP is invalid");

}

catch(MalformedURLException malformedURLException)

{

throw new IllegalArgumentException("LocalIP is invalid");

}

catch(RemoteException remoteException)

{

System.out.println("Internal error:");

System.out.println("Can't rebind a Mnemosyne to MnemosyneImpl");

System.exit(1);

}

}

    通过把本地Mnemosyne上一系列代表RMI名字符号的URL赋予远程对话服务器，就能引发同步操作，这些URL存贮在一个被称作rmiURL的字符串数组中。在SessionServer的符号中，URL是作为参数从命令行命令中获得的，但它可以来自其他渠道：

protected void synchronizeMnemosyne()

{

file://获得本地Mnemosyne

Mnemosyne localMnemosyne = null;

try

{

localMnemosyne = (Mnemosyne) Naming.lookup(_localIP);

}

catch(Exception exception)

{

System.out.println("Internal error:");

System.out.println("Can't lookup local MnemosyneImpl");

System.exit(1);

}

file://获得同步用的远程Mnemosynes

Vector remoteMnemosynes = new Vector();

// _rmiURLS对象是代表需要进行同步的远程服务器的字符串数组

for(int index = 1;index < _rmiURLS.length;index++)

{

try

{

remoteMnemosynes.add(Naming.lookup(_rmiURLS[index]));

}

catch(Exception exception)

{

}

}

file:// 同步

try

{

if(remoteMnemosynes.size() > 1)

localMnemosyne.synchronize(remoteMnemosynes);

}

catch(Exception exception)

{

System.out.println("Internal error:");

System.out.println("Can't synchronize local MnemosyneImpl");

System.exit(1);

}

}


分布式对话服务器的管理（5）

作者：运气

email: webmaster@chinaspx.com

日期：7/4/2001 1:43:04 PM

    远程访问Mnemosyne

    下面我们来讨论在servlet服务器上访问远程Mnemosyne的方法。要在无需特定服务器在线的情况下加载一个包含对话信息的Mnemosyne，需要创建一个FailoverHandler的实例，FailoverHandler利用JDK 1.3中的Proxy API处理对话服务器当机的问题。FailoverHandler把一个代表访问远程对话服务器的RMI URL的字符串数组作为参数，然后，从Proxy类中获取Mnemosyne实例。下面的SessionManager类中的initializeMnemosyne()方法可以显示出这一切是如何完成的：

public static void initializeMnemosyne(String[] rmiURLs)

{

// 设置当机服务器的处理程序

FailoverHandler fh = new FailoverHandler(null, rmiURLs);

// 得到Mnemosyne. 的一个实例

_Mnemosyne =

(Mnemosyne)Proxy.newProxyInstance(Mnemosyne.class.getClassLoader(),

new Class[] { Mnemosyne.class },

fh );

}

    如果用Proxy类获取Mnemosyne的实例，所有的方法调用必须通过FailoverHandler的 invoke()方法进行。当有方法访问Mnemosyne时，FailoverHandler将试着调用该方法访问一个远程对象。如果方法调用失败（例如服务器关机），FailoverHandler将从提供给构造器的URL清单中再取得下一个URL，这样就会无缝地转向下一个对话服务器。

// 建立远程加载类的URL清单

public FailoverHandler(Remote delegate, String[] delegateURLS)

{

this.delegateURLS = delegateURLS;

// 如果这个URL无效，则获取下一个有效的URL

try {

this.delegate =

((delegate == null)?getNextValidDelegate():delegate);

} catch (RemoteException ex) {

// 如果发生远程意外错误，则该URL不能使用，向调用者发送一个 //IllegalArgumentException事件

throw new IllegalArgumentException("Remote URLs could not "

+ "be found");

}

}

public Object invoke(Object proxy,

Method method,

Object[] arguments)

throws Throwable

{

while(true)

{

try

{

file:// 尝试对获得的最后一个URL调用被调用的方法

return method.invoke(delegate, arguments);

}

catch(InvocationTargetException invocationTargetException)

{

file://如果获得的URL无效，则取下一个URL

try

{

throw invocationTargetException.getTargetException();

}

catch(RemoteException remoteException)

{

delegate = getNextValidDelegate();

}

}

}

}

file://从构造器中的URL清单中获得下一个URL

protected Remote getNextValidDelegate() throws RemoteException

{

for(int i = 0; i < delegateURLS.length;i++)

{

try

{

return Naming.lookup(delegateURLS[i]);

}

catch(Exception exception)

{

}

}

throw new RemoteException("All lookup failed");

}

    当使用FailoverHandler对象时，从一个对话服务器向另一个对话服务器的转换对于调用Mnemosyne的任何用户端机器都是透明的。


分布式对话服务器的管理（6）

作者：运气

email: webmaster@chinaspx.com

日期：7/4/2001 1:43:27 PM

    尽管我们已经能够访问对话服务器，而且可以避免单一点故障，我们还必须为HttpSession建立一个封装对象，而SessionWrapper就是这样一个对象，而且，它还假定HttpSession的执行也是串行化的。如果它不是串行化的，可以很方便地修改封装对象将对话的信息转移到一个哈希表中并在其他成员变量中保留其他信息（ID、创作时间等信息。）。

public interface SessionWrapper extends Memory

{

/**

* 得到HttpSession的信息。

*/

public HttpSession getSession();

}

public class SessionWrapperImpl implements SessionWrapper

{

/**识别该对话的关键字 */

protected String _id;

/** 当前HttpSession的信息。 */

protected HttpSession _sess;

/**

* 建立ID，但没有建立对话的其他信息，可用于通过read寻找一个对话。

*/

public SessionWrapper(String id) {

_id = id;

}

/**

* 建立一个带对话的SessionWrapper。其ID与对话的ID相同。

*/

public SessionWrapper(HttpSession sess) {

_sess = sess;

_id = sess.getId();

}

/**

* 如果Memory对象是SessionWrapper的一个实例，当前的SessionWrapper

* 已经建立了与对象相同的ID，则此方法返回的值为真。

*/

public boolean equalsMemory(Memory m) {

return (m instanceof SessionWrapper

&& _id != null

&& _id.equals(((SessionWrapper)m)._id));

}

/**

* 得到HttpSession的信息。

*/

public HttpSession getSession() {

return _sess;

}

}

    SessionWrapper类执行了Memory的界面，因此，HttpSession对象的ID可以与远程对话的ID进行比较。

    最后需要创建read()、write()和delete()，以对远程对话进行管理。我们向SessionManager类添加三个静态类：

/**

* 从在初始化时建立的Mnemosyne中得到HttpSession信息。

*/

public static HttpSession getSession(String id)

throws RemoteException

{

try {

SessionWrapper result

= (SessionWrapper)_Mnemosyne.read(new SessionWrapper(id),

null);

return result.getSession();

} catch (TransactionException ex) {

// 由于没有处理事物，因此不会有事务意外被放弃。

ex.printStackTrace();

}

return null;

}

/**

* 在初始化时指定的Mnemosyne中保存对话信息。

*/

public static void saveSession(HttpSession sess)

throws RemoteException

{

try {

_Mnemosyne.write(new SessionWrapper(sess), null);

} catch (TransactionException ex) {

file://由于没有处理事物，因此不会有事务意外被放弃。

ex.printStackTrace();

}

}

/**

* 从在初始化时指定的Mnemosyne中删除一个对话。

*/

public static void removeSession(String id)

throws RemoteException

{

try {

_Mnemosyne.take(new SessionWrapper(id), null);

} catch (TransactionException ex) {

// /由于没有处理事物，因此不会有事务意外被放弃。

ex.printStackTrace();

}

}

    在servlet中，可以以如下方式管理对话：

public void init(ServletConfig conf) throws ServletException {

// 调用一个方法得到指示对话服务器位置的RMI URL清单

// 例如：//server1.foo.com/MnemosyneImpl, //server2.foo.com/MnemosyneImpl，等

String[] urls = getURLs(conf); // Method to get the URLs from properties for the session servers

SessionManager.initializeMnemosyne(urls)

}

public void doGet(HttpServletRequest req, HttpServletResponse rsp) throws IOException {

file:// 得到存贮在cookie中的对话，仅仅是为了得到其ID。

HttpSession baseSess = req.getSession()

file://根据得到的ID，从Mnemosyne中得到真正的对话

HttpSession realSess = SessionManager.getSession(base.getId());

SessionManager.saveSession(realSess);

}

    结论

    尽管这篇文章讨论了一个分布式对话管理的例子，但我们可以将这一技术用于管理必须容忍任一节点出错的分布式内存管理系统中。Mnemosyne还可以用在成员不断加入和离开的P2P应用中。通过使用Mnemosyne，任何一个成员都可以与系统进行快速同步，而无需要求为保持系统有效而必须保证某一结点不出故障。


给javabeans的礼物一篇：JSP基本语法

作者：随意

日期：00-4-28 20:53:30

JSP基本语法

讲完安装问题后，现在进入来讨论JSP语法。一个投机取巧的办法是下载语法卡片。如果你不熟悉Java编程，也许你想看一下Sun的指南 。然而网页创建者并不需要进行太多的Java开发。除了一些方法调用，在你的JSP网页里应尽可能少用Java代码。

记住以上提示，我们首先来看一看JSP指令和脚本元素，之后我们会解释JavaBeans和隐含对象。共有五种JSP指令和脚本元素。在JSP 1.0版中，大部分JSP被一个以“<%” 开头和以“%>”结尾的的标记括在其中。在更新的JSP 1.1规范出台后，就有了与XML兼容的版本。

JSP指令和脚本元素

  Directives <%@ directive %>

  Declarations <%! declaration %>

  Expressions <%= expression %>

  Code Fragment/Scriptlet <% code fragment %>

  Comments <%-- comment --%>

指令

JSP指令是为JSP引擎而设计的。他们并不直接产生任何可见输出，而只是告诉引擎如何处理其余JSP页面。这些指令始终被括在 “<%@ ?%>”标记中。两个最重要的指令是“pagePage”和“Include”。“Taglib”指令不在讨论之列，但是在用JSP1.1创建自定义标记可以使用它。

几乎在所有JSP页面顶部都会看到“Page”指令。尽管不是必须的，但它可以让你指定：

到何处查找起支持作用的Java类别：

<%@ page import="java.util.Date" %>,

出现Java运行问题时，将网上冲浪者引向何处：

<%@ page errorPage="errorPage.jsp" %>,

还有你是否需要管理用户的会话级信息，这些信息可能来自多个网页（在下面的JavaBeans一节将对此做进一步讲）述：

<%@ page session="true" %>.

“Include”指令可以把你的内容分成更多可管理的元素，比如包括一个普通页面页眉或页脚的元素。包含的网页可以是一个固定的HTML 页面或更多的JSP内容：

<%@ include file="filename.jsp" %>

声明

JSP声明用来让你定义页面级变量，以保存信息或定义JSP页面的其余部分可能需要的支持方法。如果你发现代码太多，通常最好把它们写成一个独立的Java类别。声明一般都在“<%! ?%>”标记中。一定要以分号（；）结束变量声明，因为任何内容都必须是有效的Java语句：

<%! int i=0; %>。

表达式

有了JSP表达式，表达式评估结果会被转换成一个字符串，并且被直接包括在输出页面之内。JSP 表达式包含在“<%= ?%>” 标记中，没有分号，除非在加引号的字符串部分使用分号。

<%= i %>

<%= "Hello" %> .

代码片段/脚本片段

代码片段/脚本片段（Scriptlets） JSP代码片段或脚本片段是嵌在“<% ?%>”标记中的。这种Java代码在Web服务器响应请求时就会运行。在脚本片段周围可能是原始的HTML或XML语句，在这些地方，代码片段可以使你创建条件执行代码，或要用到另外一段代码的代码。例如，以下的代码组合使用表达式和代码片段，显示H1、H2、H3和H4标记中的字符串“Hello”。代码片段并不局限于一行源代码：

<% for (int i=1; i<=4; i++) { %>

<H<%=i%>>Hello</H<%=i%>>

<% } %>.

注释

最后一个主要的JSP元素是嵌入式注释。尽管你始终可以在文件中加入HTML注释，但用户在查看页面源代码时会看到这些注释。如果你不想让用户看到它，你就应该将其嵌入“<%-- ?--%>” 标记中：

<%-- comment for server side only --%>.


给你个比较全的关于时间的操作的东西------

作者：javaboy

email: lqhboy@l63.com

日期：2001-7-11 9:06:52

import java.text.DateFormat;

import java.util.*;

public class JspCalendar {

    Calendar  calendar = null;

    public JspCalendar() {

    calendar = Calendar.getInstance();

    Date trialTime = new Date();

    calendar.setTime(trialTime);

    }

    public int getYear() {

    return calendar.get(Calendar.YEAR);

    }

    public String getMonth() {

    int m = getMonthInt();

    String[] months = new String [] { "January", "February", "March",

                    "April", "May", "June",

                    "July", "August", "September",

                    "October", "November", "December" };

    if (m > 12)

        return "Unknown to Man";

    return months[m - 1];

    }

    public String getDay() {

    int x = getDayOfWeek();

    String[] days = new String[] {"Sunday", "Monday", "Tuesday", "Wednesday",

                      "Thursday", "Friday", "Saturday"};

    if (x > 7)

        return "Unknown to Man";

    return days[x - 1];

    }

    public int getMonthInt() {

    return 1 + calendar.get(Calendar.MONTH);

    }

    public String getDate() {

    return getMonthInt() + "/" + getDayOfMonth() + "/" +  getYear();

    }

    public String getTime() {

    return getHour() + ":" + getMinute() + ":" + getSecond();

    }

    public int getDayOfMonth() {

    return calendar.get(Calendar.DAY_OF_MONTH);

    }

    public int getDayOfYear() {

    return calendar.get(Calendar.DAY_OF_YEAR);

    }

    public int getWeekOfYear() {

    return calendar.get(Calendar.WEEK_OF_YEAR);

    }

    public int getWeekOfMonth() {

    return calendar.get(Calendar.WEEK_OF_MONTH);

    }

    public int getDayOfWeek() {

    return calendar.get(Calendar.DAY_OF_WEEK);

    }

    public int getHour() {

    return calendar.get(Calendar.HOUR_OF_DAY);

    }

    public int getMinute() {

    return calendar.get(Calendar.MINUTE);

    }

    public int getSecond() {

    return calendar.get(Calendar.SECOND);

    }

    public static void main(String args[]) {

    JspCalendar db = new JspCalendar();

    p("date: " + db.getDayOfMonth());

    p("year: " + db.getYear());

    p("month: " + db.getMonth());

    p("time: " + db.getTime());

    p("date: " + db.getDate());

    p("Day: " + db.getDay());

    p("DayOfYear: " + db.getDayOfYear());

    p("WeekOfYear: " + db.getWeekOfYear());

    p("era: " + db.getEra());

    p("ampm: " + db.getAMPM());

    p("DST: " + db.getDSTOffset());

    p("ZONE Offset: " + db.getZoneOffset());

    p("TIMEZONE: " + db.getUSTimeZone());

    }

    private static void p(String x) {

    System.out.println(x);

    }

    public int getEra() {

    return calendar.get(Calendar.ERA);

    }

    public String getUSTimeZone() {

    String[] zones = new String[] {"Hawaii", "Alaskan", "Pacific",

                       "Mountain", "Central", "Eastern"};

    return zones[10 + getZoneOffset()];

    }

    public int getZoneOffset() {

    return calendar.get(Calendar.ZONE_OFFSET)/(60*60*1000);

    }

    public int getDSTOffset() {

    return calendar.get(Calendar.DST_OFFSET)/(60*60*1000);

    }

    public int getAMPM() {

    return calendar.get(Calendar.AM_PM);

    }

}


给你看看我的一个方法

作者：diego lynn

email: Hello.Diego@elong.com

日期：2001-6-29 15:32:06

    /**

     * <p>获取表列表的 sql 语句

     */

    private String getTableName( HttpPresentationComms inComms, String inTableResult ) throws HttpPresentationException

    {

        String _sql = I_EMPTY_STRING;

        String _tempString = I_EMPTY_STRING;

        String[] _relationship = inComms.request.getParameterValues( "relationship" );

        _sql = "select TABLENAME from (" + inTableResult + ") where SQLLEVEL = '0' ";

        for ( int i=0; i<_relationship.length-1; i++ )

        {

            if ( _relationship[i].equals("and") )

                _tempString = "intersect";

            else if ( _relationship[i].equals("or") )

                _tempString = "union";

            _sql += _tempString + " select TABLENAME from (" + inTableResult + ") where SQLLEVEL = '" +

                    (i+1) + "' ";

        }

        return _sql;

    }


给你一个SERVLET保治百病

此电子文档属C#乐园QQ群3256045整理。
给你一个SERVLET保治百病

上一页 目录 下一页

给你一个SERVLET保治百病

作者：zergman

email: greensoft@kali.com.cn

日期：2001-2-20 11:57:23

import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

import java.util.*;

public class ShowAllFormData extends HttpServlet {

  public void doGet(HttpServletRequest request,

                    HttpServletResponse response)

      throws ServletException, IOException {

    response.setContentType("text/html");

    PrintWriter out = response.getWriter();

    String title = "显示所有的Form变量的值";

    out.println("<HTML><HEAD><TITLE>"+title+"</TITLE></HEAD>"+

                "<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例子，JSP差不多

作者：whitefox

email: whitefox.jiang@corp.elong.com

日期：2000-7-6 11:32:15

来源水木清华

public class UploadServlet extends HttpServlet

{

  //default maximum allowable file size is 100k

  static final int MAX_SIZE = 102400;

  //instance variables to store root and success message

  String rootPath, successMessage;

  /**

   * init method is called when servlet is initialized.

   */

  public void init(ServletConfig config) throws ServletException

  {

    super.init(config);

    //get path in which to save file

    rootPath = config.getInitParameter("RootPath");

    if (rootPath == null)

    {

      rootPath = "/";

    }

    /*Get message to show when upload is complete. Used only if

      a success redirect page is not supplied.*/

    successMessage = config.getInitParameter("SuccessMessage");

    if (successMessage == null)

    {

      successMessage = "File upload complete!";

    }

  }

  /**

   * doPost reads the uploaded data from the request and writes

   * it to a file.

   */

  public void doPost(HttpServletRequest request,

    HttpServletResponse response)

  {

    ServletOutputStream out=null;

    DataInputStream in=null;

    FileOutputStream fileOut=null;

    try

    {

      /*set content type of response and get handle to output

        stream in case we are unable to redirect client*/

      response.setContentType("text/plain");

      out = response.getOutputStream();

    }

    catch (IOException e)

    {

      //print error message to standard out

      System.out.println("Error getting output stream.");

      System.out.println("Error description: " + e);

      return;

    }

    try

    {

      //get content type of client request

      String contentType = request.getContentType();

      //make sure content type is multipart/form-data

      if(contentType != null && contentType.indexOf(

        "multipart/form-data") != -1)

      {

        //open input stream from client to capture upload file

        in = new DataInputStream(request.getInputStream());

        //get length of content data

        int formDataLength = request.getContentLength();

        //allocate a byte array to store content data

        byte dataBytes[] = new byte[formDataLength];

        //read file into byte array

        int bytesRead = 0;

        int totalBytesRead = 0;

        int sizeCheck = 0;

        while (totalBytesRead < formDataLength)

        {

          //check for maximum file size violation

          sizeCheck = totalBytesRead + in.available();

          if (sizeCheck > MAX_SIZE)

          {

            out.println("Sorry, file is too large to upload.");

            return;

          }

          bytesRead = in.read(dataBytes, totalBytesRead,

            formDataLength);

          totalBytesRead += bytesRead;

        }

        //create string from byte array for easy manipulation

        String file = new String(dataBytes);

        //since byte array is stored in string, release memory

        dataBytes = null;

        /*get boundary value (boundary is a unique string that

          separates content data)*/

        int lastIndex = contentType.lastIndexOf("=");

        String boundary = contentType.substring(lastIndex+1,

          contentType.length());

        //get Directory web variable from request

        String directory="";

        if (file.indexOf("name=\"Directory\"") > 0)

        {

          directory = file.substring(

            file.indexOf("name=\"Directory\""));

          //remove carriage return

          directory = directory.substring(

            directory.indexOf("\n")+1);

          //remove carriage return

          directory = directory.substring(

            directory.indexOf("\n")+1);

          //get Directory

          directory = directory.substring(0,

            directory.indexOf("\n")-1);

          /*make sure user didn't select a directory higher in

            the directory tree*/

          if (directory.indexOf("..") > 0)

          {

            out.println("Security Error: You can't upload " +

              "to a directory higher in the directory tree.");

            return;

          }

        }

        //get SuccessPage web variable from request

        String successPage="";

        if (file.indexOf("name=\"SuccessPage\"") > 0)

        {

          successPage = file.substring(

            file.indexOf("name=\"SuccessPage\""));

          //remove carriage return

          successPage = successPage.substring(

            successPage.indexOf("\n")+1);

          //remove carriage return

          successPage = successPage.substring(

            successPage.indexOf("\n")+1);

          //get success page

          successPage = successPage.substring(0,

            successPage.indexOf("\n")-1);

        }

        //get OverWrite flag web variable from request

        String overWrite;

        if (file.indexOf("name=\"OverWrite\"") > 0)

        {

          overWrite = file.substring(

            file.indexOf("name=\"OverWrite\""));

          //remove carriage return

          overWrite = overWrite.substring(

            overWrite.indexOf("\n")+1);

          //remove carriage return

          overWrite = overWrite.substring(

            overWrite.indexOf("\n")+1);

          //get overwrite flag

          overWrite = overWrite.substring(0,

            overWrite.indexOf("\n")-1);

        }

        else

        {

          overWrite = "false";

        }

        //get OverWritePage web variable from request

        String overWritePage="";

        if (file.indexOf("name=\"OverWritePage\"") > 0)

        {

          overWritePage = file.substring(

            file.indexOf("name=\"OverWritePage\""));

          //remove carriage return

          overWritePage = overWritePage.substring(

            overWritePage.indexOf("\n")+1);

          //remove carriage return

          overWritePage = overWritePage.substring(

            overWritePage.indexOf("\n")+1);

          //get overwrite page

          overWritePage = overWritePage.substring(0,

            overWritePage.indexOf("\n")-1);

        }

        //get filename of upload file

        String saveFile = file.substring(

          file.indexOf("filename=\"")+10);

        saveFile = saveFile.substring(0,

          saveFile.indexOf("\n"));

        saveFile = saveFile.substring(

          saveFile.lastIndexOf("\\")+1,

          saveFile.indexOf("\""));

        /*remove boundary markers and other multipart/form-data

          tags from beginning of upload file section*/

        int pos; //position in upload file

        //find position of upload file section of request

        pos = file.indexOf("filename=\"");

        //find position of content-disposition line

        pos = file.indexOf("\n",pos)+1;

        //find position of content-type line

        pos = file.indexOf("\n",pos)+1;

        //find position of blank line

        pos = file.indexOf("\n",pos)+1;

        /*find the location of the next boundary marker

          (marking the end of the upload file data)*/

        int boundaryLocation = file.indexOf(boundary,pos)-4;

        //upload file lies between pos and boundaryLocation

        file = file.substring(pos,boundaryLocation);

        //build the full path of the upload file

        String fileName = new String(rootPath + directory +

          saveFile);

        //create File object to check for existence of file

        File checkFile = new File(fileName);

        if (checkFile.exists())

        {

          /*file exists, if OverWrite flag is off, give

            message and abort*/

          if (!overWrite.toLowerCase().equals("true"))

          {

            if (overWritePage.equals(""))

            {

              /*OverWrite HTML page URL not received, respond

                with generic message*/

              out.println("Sorry, file already exists.");

            }

            else

            {

              //redirect client to OverWrite HTML page

              response.sendRedirect(overWritePage);

            }

            return;

          }

        }

        /*create File object to check for existence of

          Directory*/

        File fileDir = new File(rootPath + directory);

        if (!fileDir.exists())

        {

          //Directory doesn't exist, create it

          fileDir.mkdirs();

        }

        //instantiate file output stream

        fileOut = new FileOutputStream(fileName);

        //write the string to the file as a byte array

        fileOut.write(file.getBytes(),0,file.length());

        if (successPage.equals(""))

        {

          /*success HTML page URL not received, respond with

            generic success message*/

          out.println(successMessage);

          out.println("File written to: " + fileName);

        }

        else

        {

          //redirect client to success HTML page

          response.sendRedirect(successPage);

        }

      }

      else //request is not multipart/form-data

      {

        //send error message to client

        out.println("Request not multipart/form-data.");

      }

    }

    catch(Exception e)

    {

      try

      {

        //print error message to standard out

        System.out.println("Error in doPost: " + e);

        //send error message to client

        out.println("An unexpected error has occurred.");

        out.println("Error description: " + e);

      }

      catch (Exception f) {}

    }

    finally

    {

      try

      {

        fileOut.close(); //close file output stream

      }

      catch (Exception f) {}

      try

      {

        in.close(); //close input stream from client

      }

      catch (Exception f) {}

      try

      {

        out.close(); //close output stream to client

      }

      catch (Exception f) {}

    }

  }

}


给你一篇ibm的介绍Structs的文章（1）

作者：friendcn

日期：2001-2-22 12:46:17

ibm公司的一篇介绍Struts的文章，

觉的不错，我没时间翻译，你要是E文好的话自己看看吧

网址http://www-106.ibm.com/developerworks/library/j-struts/?dwzone=java

Struts, an open-source MVC implementation

Manage complexity in large Web sites with this servlets and JSP framework

Malcolm Davis

Consultant

February 2001

Contents:

 Introduction

 JSP is a servlet

 No more Java in my HTML

 MVC

 MVC Model 2

 Struts

 Struts details

 Mailing list sample

 Before and after

 Future of Struts

  Resources

 About the author

This article introduces Struts, a Model-View-Controller implementation that uses servlets and JavaServer Pages (JSP) technology. Struts can help you control change in your Web project and promote specialization. Even if you never implement a system with Struts, you may get some ideas for your future servlets and JSP page implementations.

Introduction

Kids in grade school put HTML pages on the Internet. However, there is a monumental difference between a grade school page and a professionally developed Web site. The page designer (or HTML developer) must understand colors, the customer, product flow, page layout, browser compatibility, image creation, JavaScript, and more. Putting a great looking site together takes a lot of work, and most Java developers are more interested in creating a great looking object interface than a user interface. JavaServer Pages (JSP) technology provides the glue between the page designer and the Java developer.

If you have worked on a large-scale Web application, you understand the term change. Model-View-Controller (MVC) is a design pattern put together to help control change. MVC decouples interface from business logic and data. Struts is an MVC implementation that uses Servlets 2.2 and JSP 1.1 tags, from the J2EE specifications, as part of the implementation. You may never implement a system with Struts, but looking at Struts may give you some ideas on your future Servlets and JSP implementations.

In this article, I will begin with a JSP file that uses elements you may be familiar with and discuss the pros and cons of such a page. I will then cover Struts and how it can control change in your Web project and promote specialization. Finally, I will re-develop the simple JSP file with the page designer and change in mind.

A JSP file is a Java servlet

A JavaServer Page (JSP) file is nothing more than another way to view a servlet. The concept of a JSP file is to allow us to see a Java servlet as an HTML page. This view eliminates all of the ugly print() statements that normally show up in Java code. The JSP file is pre-processed into a .java file, then compiled into a .class. If you are using Tomcat, you can view your pre-processed .java files in the work directory. Other containers may store the .java and .class files elsewhere; the location is container specific. Figure 1 demonstrates the JSP file-to-servlet flow.

Figure 1. JSP file-to-servlet flow

(This is significantly different from a Microsoft Active Server Page (ASP). An ASP is compiled into memory, not into a separate file.)

The simple self-contained JSP file

In a small JSP application, it is common to see the data, business logic, and the user interface combined into one module of code. In addition, the application generally contains the logic that controls the flow of the application. Listing 1 and Figure 2 demonstrate a simple JSP file that allows a user to join a mailing list.

Listing 1. join.jsp -- a simple request and response JSP file <%@ page language="java" %>

<%@ page import="business.util.Validation" %>

<%@ page import="business.db.MailingList" %>

<%

String error = "";

String email = request.getParameter("email");

// do we have an email address

if( email!=null ) {

    // validate input...

    if( business.util.Validation.isValidEmail(email) ) {

        // store input...

        try {

            business.db.MailingList.AddEmail(email);

        } catch (Exception e) {

            error = "Error adding email address to system.  " + e;

        }

        if( error.length()==0 ) {

%>

            // redirect to welcome page...

            <jsp:forward page="welcome.html"/>

<%

        }

    } else {

        // set error message and redisplay page

        error = email + " is not a valid email address, please try again.";

    }

} else {

    email = "";

}

%>

<html>

<head>

<title>Join Mailing List</title>

</head>

<body>

<font color=red><%=error%></font><br>

<h3>Enter your email to join the group</h3>

<form action="join.jsp" name="joinForm">

    <input name="email" id="email" value=<%=email%>></input>

    <input type=submit value="submit">

</form>

</body>

</html>

Figure 2. In a simple request and response, the JSP file sets the data, controls the flow to the next page, and creates the HTML

The mailing list JSP file is a self-contained, do-it-all module. The only things not contained in the JSP file are the actual code for validation that is contained in isValidEmail() and the code that puts the e-mail address in the database. (Separating the isValidEmail() method into reusable code might seem like an obvious thing to do, but I have seen the code for isValidEmail() embedded directly into the page.) The advantage of the single-page approach is that it is easy to understand and initially easy to build. In addition, with all the graphical development tools, it is easy to get started.

Activities of join.jsp

Display opening input page.

Read the email value from the form parameter.

Validate the email address.

If email address is valid:

Add the address to the database.

Redirect to the next page.

If email address is invalid:

Set an error message.

Redisplay join.jsp with the error message.

Consequences of the single-page approach

Heavy HTML and Java coupling

The coder of the JSP file must be both a page designer and a Java developer. The result is often either terrible Java code or an ugly page, or sometimes both.

Java and JavaScript blur

As the pages become larger, there can be a tendency to implement some JavaScript. When the JavaScript appears in a page, the script can get confused with the Java code. An example of a possible point of confusion is using client-side JavaScript to validate the email field.

Embedded flow logic

To understand the entire flow of the application, you have to navigate all of the pages. Imagine the spaghetti logic on a 100-page Web site.

Debugging difficulties

In addition to being ugly to look at, HTML tags, Java code, and JavaScript code all in one page makes it difficult to debug problems.

Tight coupling

Changes to business logic or data means possibly touching every page involved.

Aesthetics

Visually, in large pages, this type of coding looks messy. When I was doing Microsoft ASP development, I would commonly see 1000-line pages. Even with syntax coloring, it was still difficult to read and understand.

No more Java code in my HTML, please

In Listing 1, instead of having a lot of HTML in Java code, I have a lot of Java code in an HTML file. From this standpoint, I really have not accomplished much, other than permit page designers to write Java code. However, all is not lost; with JSP 1.1, we got a new feature called tags.

A JSP tag is simply a way of abstracting out code from a JSP file. Some people think of JSP tags as macros for JSP files, where the code for the tag is contained in the servlet. (The macro perspective is almost true.) For the same reason I do not want to see HTML tags in Java code, I do not want to see Java code in a JSP file. The entire point of JSP technology is to allow the page designer to create servlets without being distracted with Java code. Tags allow Java programmers to extend JSP files by making Java code look like HTML. Figure 3 displays the general concept of pulling the code from the JSP page and putting into a JSP tag.

Figure 3. JSP tag

An example of Struts tag capability is in Listing 2. In Listing 2, the normal HTML <form> tag is replaced with the Struts <form:form> tag. Listing 3 shows the resulting HTML that the browser receives. The browser gets the HTML <form> tag, but with additional code, such as the JavaScript. The additional JavaScript sets the focus on the email address field. The server side <form:form> tag code created the appropriate HTML and abstracts the JavaScript away from the page designer.

Listing 2. Struts form tag

<form:form action="join.do" focus="email" >

    <form:text   property="email" size="30" maxlength="30"/>

    <form:submit property="submit" value="Submit"/>

</form:form>

Listing 3. Resulting HTML sent to the browser

<form name="joinForm" method="POST" action="join.do;jsessionid=ndj71hjo01">

    <input type="text" name="email" maxlength="30" size="30" value="">

    <input type="submit" name="submit" value="Submit">

</form>

<script language="JavaScript">

<!--

    document.joinForm.email.focus()

// -->

</script>

Notes about JSP tags:

JSP tags require a container that runs JSP 1.1 or later.


给所有刚学JAVA以及常来此版的兄弟

作者：sonymusic

email: sonymusic@china.com

日期：2001-5-10 10:44:40

给所有刚学JAVA以及常来此版的兄弟

    在坛子上，经常（注意，是经常，而且是非常经常！）碰到一些并不难的问题，甚至同一天就有很多类似的问题，我想作为版主，我不能光是给大家回答问题了（更何况还有很多问题我也不懂），而需要讲一下应该如何学习JAVA的问题了。我不想大家碰到了什么小问题，第一个想到的就是问人，这样是很难得到提高的！

    以这两天的例子来说吧！是一个关于如何格式化日期的问题，有问如何将一个Date对象按自己需要的格式来以String输出。也有问，如何将一个给定格式的String类转换成Date对象的。

    昨天我刚加了一个帖子进精华区，内容如下：

[hr]【chenm】 于 2001-5-8 14:57:29 加贴在 JAVA技术 ↑：

/*

file: timeformat.java

*/

import java.lang.*;

import java.text.*;

import java.util.*;

public class timeformat{

  public timeformat(){

  }

  static public String getChineseFormatTime(){

      SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");

      return sdf.format(new Date());

  }

  static public String getSimpleFormatTime(){

      SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmm", Locale.US);

      return sdf.format(new Date());

  }

}[hr]

    只要仔细看，想要自己解决问题的朋友一定看出，这段代码虽然简单，但却已经说明了Date到String的转换。SimpleDateFormat所在的包是java.text。

    那么由String到Date的转换呢？

    学JAVA的人，机器上不会没有JAVA　API吧！只需要查一下SimpleDateFormat类的方法，就会找两个返回值为Date的parse方法（其中有一个是继承的DateFormat类的），这样转换还难吗？

    刚才回答了一个帖子，问题是这样的：“我要用  2000-09-03  这各字符串来构造一个Date，怎么写直截 Date("2000-09-03")好像不行啊”，其实我觉得这位朋友已经走到了解决方法的前面了，但却没有自己想办法继续走下去。Date类根本没有这样的构造方法呀！

    但Date类却有其它的方法呀，事实上Date类只有一个以String作为参数的方法static long parse(String s) ，但API中说：[b][i]Deprecated. As of JDK version 1.1, replaced by DateFormat.parse(String s).[/i][/b]，那么好了，看一下java.text.DateFormat类好了。当然DateFormat类的parse方法所支持的日期格式并不很多，但不是还子类SimpleDateFormat吗？这些在API中都是一目了然的呀！

    在其它的坛子里，经常看到很多朋友在讨论，中国的项目管理，.....的不行不行，缺乏团队意识什么的，那个时候我很难过，因为我不是一个项目高手，我只知道老老实实的写程序。但今天我更难过，因为我发现我的朋友们很多时候，连[#ff0000][b]学习[/b][/#]都不会了！

    不是没事干想找人批，也不是不要大家提问题，而是真的希望大家都能提高，如同版面说明中所写！


根据dtd读写xml实例

作者：javajava

email: franzy@163.net

日期：2000-12-11 20:46:01

package franzy;

import org.w3c.dom.Element;

import oracle.xml.parser.v2.XMLElement;

import oracle.xml.parser.v2.XMLDocument;

//import com.biztunnel.util.tools.ToolKit;

public class Agency

{

    private String agencyID;

    private String agencyOther;

    private String a_dtype;

    private Element element;

    public static final String AGENCYCODE_OTHER = "Other";

    public static final String AGENCYCODE_COMMERCEONE = "CommerceOne";

    public static final String AGENCYCODE_ISO = "ISO";

    public static final String AGENCYCODE_EAN = "EAN";

    public static final String AGENCYCODE_ASSIGNEDBYSUPPLIER = "AssignedBySupplier";

    public static final String AGENCYCODE_ASSIGNEDBYBUYER = "AssignedByBuyer";

    public static final String AGENCYCODE_ANSI = "ANSI";

    public static final String AGENCYCODE_GBABA = "GBABA";

    private static final String AGENCY_ATTRIBUTE_A_DTYPE="%Agency-Dtypes;";

    /**

     * Creates a Agency Element according to the outer element

     */

    private void init()

    {

        this.element=new XMLElement("Agency");

        agencyID = null;

        agencyOther = null;

        a_dtype = this.AGENCY_ATTRIBUTE_A_DTYPE;

    }

    public Agency(Element newElement)

    {

        init();

        //Element newElement = (Element)newElement1.getLastChild();

        if(!newElement.getNodeName().equals("Agency"))

        //if(!newElement1.getLastChild().getNodeName().equals("Agency"))

        {

            System.out.println("not it!!!");

        }

        //get attribute

        this.setAgencyID(newElement.getAttribute("AgencyID "));

        this.setAgencyOther(newElement.getAttribute("AgencyOther "));

        this.agencyID = newElement.getAttribute("a-dtype");

    }

    /**

     * Create an empty Agency element according to the buginfo.dtd

     */

    public Agency()

    {

        //initialize the empty node

        this.init();

    }

    /**

     * convert this element to W3c Element format

     */

    public Element getElement()

    {

      if(this.getAgencyID()!=null)

      {

          this.element.setAttribute("AgencyID",this.getAgencyID());

      }

      if(this.getAgencyOther()!=null)

      {

          this.element.setAttribute("AgencyOther",this.agencyOther);

      }

      if(this.getA_dtype()!=null)

      {

          this.element.setAttribute("a-dtype",this.a_dtype);

      }

      return this.element;

    }

    public void setAgencyID(String newAgencyID)

    {

        agencyID = newAgencyID;

    }

    public String getAgencyID()

    {

        return agencyID;

    }

    public void setAgencyOther(String newAgencyOther)

    {

        agencyOther = newAgencyOther;

    }

    public String getAgencyOther()

    {

        return agencyOther;

    }

    public String getA_dtype()

    {

        return a_dtype;

    }

    public void setFixedA_dtype(boolean isSet)

    {

        if(isSet)

        {

            a_dtype = this.AGENCY_ATTRIBUTE_A_DTYPE;

        }

        else

        {

            a_dtype = null;

        }

    }

    public static void main(String[] args)

    {

        try

        {

            //test 1

            Agency element= new Agency();

            element.setFixedA_dtype(true);

            element.setAgencyID(Agency.AGENCYCODE_ASSIGNEDBYBUYER);

            element.setAgencyOther("other");

            XMLDocument doc = new XMLDocument();

            doc.appendChild(element.getElement());

            System.out.println(ToolKit.getXmlString(doc));

            //test 2

            Agency element1= new Agency(element.getElement());

            System.out.println(element1.getA_dtype());

        }catch(Exception e)

        {

            e.printStackTrace();

        }

    }

}

有问题来找我


关于［开心就好］提出的“如何杀死一只鸡”的问题的讨论意见

作者：sonymusic

email: sonymusic@china.com

日期：7/10/2001 12:59:38 AM

[H3][#ff0000]关于［开心就好］提出的“如何杀死一只鸡”的问题的讨论意见 [/#][/H3]

[B]他的问题如下：[/B]

一个方法：kill()

希望传递一个鸡的参数，然后在kill方法内将这只鸡杀死，并希望在方法外，这只鸡也是“真”的被杀死了。

他给出的代码如下，并依据这段代码得出结论：JAVA不是好厨师，杀一只鸡都杀不死。先看代码吧。

public class test{

public static void main(String[] args){

int i=0;

int j=1;

Chicken chicken=new Chicken();

int k=chicken.killChicken(i,j);

System.out.println(i+"\n"+k);

}

}

class Chicken{

public int killChicken(int i,int j){

i++;

return i+j;

}

};

//the Result is "0,2",it's why?

[B]这段代码有什么问题呢？[/B]

嗯，相当的多，就找最严重的吧，其实也是他为什么得不出正确答案的原因。

开心就好提出的问题是如何杀鸡，可是从他的代码所表示的意思来看，其实是先“造”了一只鸡，然后用这只鸡去杀两个数字。这能杀得死吗？

这里至少犯了两个错误：1，这段代码没有体现出一点OO的思想，既然是“杀鸡”，怎么能“造”一只鸡，来杀数字呢？2，这段代码理解错了JAVA中参数传递的概念。

OK，再来看看我根据罗亭的代码所改写的代码吧。

public class Chicken {

private boolean IsLiving = true;//鸡还活吗？

/** 判断鸡是否还活着 */

public boolean isLiving(){

return this.IsLiving;

}

/** 鸡被捅了一刀 */

public void killed(){

this.IsLiving = false;

}

}

public class Killer {

/** 准备吃鸡肉，如果没杀死，当然是吃不到手了 ^o^ */

public void killChicken(Chicken _Chicken){

_Chicken.killed();

}

public static void main(String[] args){

Chicken chicken=new Chicken();//这个时候还是活着的

System.out.println("活着么？"+chicken.isLiving());

Killer killer=new Killer();//弄把刀来

killer.killChicken(chicken);//我杀杀杀!

System.out.println("活着么？"+chicken.isLiving());//死啦!

}

}

[B]运行结果如下：[/B]

活着么？true

活着么？false

[B]原因：[/B]

在JAVA中，如果方法的参数是基础类型，如int,float等，那么是以传值的方式传递参数的(即，在方法内部改变参数，不会影响方法外的值)。这就是开心所做的测试不成功的原因。

如果方法的参数是对象的话，就是以传引用的方式传递参数(即，在方法内部，你可以改变参数的值，方法返回后参数的值的改变可以被反映出来)，这就是杀鸡的原理。

[B]解决：[/B]

那么，JAVA中如何应对另外两种情况(即，以引用方式来传基础类型，和传值方式来传递对象)呢？

第一种情况中，需要构造一个对象，

public class IntegerImpl{

int value;

public IntegerImpl(int newValue){

this.value=newValue;

}

public int getInt(){

return value;

}

public void setInt(int newValue){

value=newValue;

}

}

在方法调用的时候，使用这样的类来作为参数传递即可。注意，java.lang.Integer类并不适用。 第二种情况中，需要在方法一开始，先复制一个参数的复本，以后的操作只对复本进行。如下：

public killChicken(Chicken oldChicken){

chicken=oldChicken.clone();

kill(chicken);

//方法返回后，这只鸡还是活的。

}

[B]结论：[/B]

不管是需要传值还是传引用，在JAVA中当然都是可以做到的。虽然要将int等类型作为引用传递比较麻烦，不过这种情况并不是很多见。

而在.net中，ref关键字的作用看似很大，其实并没什么用处，如boss_ch所说，只是MS公司为了保持与C++的某些相似性而设置的。尤其是在传递对象的时候，当传递一只鸡给一个方法去“杀死”的时候，难道默认的居然是先造另一只鸡去杀么？不符合人的常规思维。 最后，以罗亭的话来结尾，

[#330033][I]你看你的代码中杀的是什么？

眼中无鸡，岂能杀得了鸡？[/I][/#]


关于cachedRowSet的一篇文章(java.sun.com)

作者：sonymusic

email: sonymusic@china.com

日期：2001-3-6 18:28:14

JDBCTM RowSet

This Early Access release includes three implementations of the JDBCTM RowSet interface. The RowSet interface provides a serializable, scrollable container for tabular data that may or may not be connected to its data source via a JDBC technology-enabled driver.

A disconnected RowSet object, such as a CachedRowSet object or a WebRowSet object, can be thought of as a set of rows that are being cached outside of a data source. Because they are lightweight and serializable, disconnected rowsets can be passed between different components of a distributed application, such as Enterprise JavaBeansTM components. This is one of the most important uses for a RowSet object. The data in a RowSet object may be updated and then resynchronized with the underlying tabular data source.

The RowSet Early Access Technology Release contains the rowset jar file, preliminary documentation and code examples. Additional documentation on RowSets is contained in the JDBC 2.0 Optional Package API specification. The Java Developer Connection(SM) also provides on-line access to the RowSet tutorial chapter of the new JavaTM series JDBC book.

Early Access Release 4 addresses the following areas:

fixed problem inserting rows with non-nullable columns

re-ordered code in writer to allow multiple changes to a single row to be propogated back to a data source

reader/writer now only close connects that they create. This change should allow RowSets to be used with connection pool implementations based on JDBC 1.x drivers

included missing EchoListener.java for example 5

The three JDBCTM RowSet implementations in this release demonstrate some of the many possibilities for implementing the javax.sql.RowSet interface, which is part of the JDBC 2.0 Optional Package API.

The three example implementations in this release are:

CachedRowSet -- a rowset that can be used to

Send a set of rows across a network

Send data to a thin client

Add scrollability and updatability to result set data

WebRowSet -- an extension of CachedRowSet that can be used to send a rowset across a network in XML format

JDBCRowSet -- a rowset that can be used to encapsulate a JDBCTM driver as a Java BeansTM component

Developers cannot redistribute the binary software in commercial products. Please see license for terms and conditions.

The file Release.notes in the download package contains instructions for installation and setup.

NOTE: The three RowSet implementations contained in this release are experimental, unsupported technology. The APIs in this release may change before they become an official part of the JavaTM 2 platform. The sun.jdbc.rowset package, which is contained in this release, is NOT a committed API for the Java platform.


关于finalize()方法的解释。

作者：sonymusic

email: sonymusic@163.net

日期：8/9/2001 11:36:21 PM

[b]原题：[/b]

我在thinking in java中看见以下一段代码。

//:Garbage.java

//Demonstration of the garbage

//collector and finalization

class Chair {

    static boolean gcrun=false;

    static boolean f=false;

    static int created=0;

    static int finalized=0;

    int i;

    Chair(){

        i=++created;

        if(created==47)

            System.out.println("Created 47");

    }

    protected void finalize(){

        if(!gcrun){

            gcrun=true;

            System.out.println(

                "Beginning to finalize after"+created+

                "Chairs have been created");

        }

    if(i==47){

        System.out.println(

            "Finalizing Chair #47,"+

            "Setting flag to stop Chair creation");

        f=true;

    }

    finalized++;

    if(finalized>=created)

        System.out.println(

            "All"+finalized+"finalized");

    }

}

public class Garbage{

    public static void main(String[] args){

      if(args.length==0){

          System.out.println("Usage:\n"+

              "java Garbage before\n or:\n"+

              "java Garbage after");

          return;

      }

      while(!Chair.f){

          new Chair();

          new String("To take up space");

      }

      System.out.println(

          "After all chairs have been created:\n"+

          "total created ="+Chair.created+

          ",total finalized ="+Chair.finalized);

      if(args[0].equals("before")){

          System.out.println("gc():");

          System.gc();

          System.out.println("runFinalization():");

          System.runFinalization();

      }

      System.out.println("bye");

      if(args[0].equals("after"))

          System.runFinalizersOnExit(true);

    }

}///:-

为什么执行java Gerbage before

以后，当所有对象创建完（比如8000个），这时只清除了2000个（不定）

应该只能创建47个对象啊

[b]分析：[/b]

首先，在一个循环当中创建对象，并且只是创建，而不引用，也就是说这个对象会自动的被系统当作垃圾处理掉。但请注意，finalize()方法并不是立刻就会执行的，执行的时间完全由系统来决定。所以很有可能的情况是已经创建了20000个对象，才开始其中的某一个对象的清除工作（这可能和时间或者系统内容的占用有关）。看finalize()方法中的一段代码：

    if (!gcrun) {

        gcrun = true;

        System.out.println(

            "\nBeginning to finalize after" + created + "Chairs have been created\nat ");

    }

就会出现这样的结果：

Beginning to finalize after 25038 Chairs have been created

这时对象的创建过程仍在继续（因为已经Finalize的对象还不满47个，Chair.f还是false）。所以Chair.created会继续增加。

直到有47个对象被清除了，Chair.f被置成true了，创建对象的循环才结束。看main方法中的一段代码：

    System.out.println(

        "\nAfter all chairs have been created:\n"

            + "total created ="

            + Chair.created

            + ",total finalized ="

            + Chair.finalized+"\n");

如上所说，Chair.created是不断增加的，而在这段代码执行之前，又会有N个对象被释放掉了，所以finalized也增加了。

结果可能是这样的：

total created =29096,total finalized =73

其实这一过程和你使用的JVM有很大关系，执行结果可能会很不相同。但有一点是可以肯定的，那就是我们无法确定JVM什么时候做对象的清除工作（这也是Thinking in java中这段代码的想要说明的），可能会在这个对象刚刚“无用”的时候就清除掉了，也可能滞后几秒，甚至永远不清除。

如果你的开发环境支持跟踪的话，你可以以STEP的方式运行，你会看到运行结果大不相同。给你看看我的运行结果：

Created 47

Beginning to finalize after25038Chairs have been created

Finalizing Chair #47,Setting flag to stop Chair creation

After all chairs have been created:

total created =45993,total finalized =1800

gc():

runFinalization():

bye

All45993finalized

注意最后一行，因为人为的停顿，JVM有足够的时间将所有的对象都清除了。

[b]结论：[/b]

不能指望finalize()方法能稳定的工作，尤其不能依靠它来做文件的关闭等操作，由于finalize()的不确定性，往往得不到你想要的结果。事实上我们只需要知道所有无用的对象，JVM会自己清除就够了。


关于java 的 Classpath全解java入门之二

作者：丑丑牛

日期：00-4-30 9:20:21

关于java 的 Classpath全解<之二>

作者 丑丑牛

声明： 欢迎转载， 如果能不把俺的little大名删掉最好

注意，开发环境往往不去读系统路径， 这也就是在外面运行的东东挺溜的进了开发环境就死菜的原因喽。

比如说jbuilder, jdevoloper从菜单project->project properties...->paths标签->Add...->添加定义好的library点ok或者new->输入name和classpath->ok

这个东东就被添加到了classpath中（运行时可在message view中看到）

再比如说visualAge for java , 点中你要运行的类->鼠标右击选中特性->类路径标签1。->编辑->全选，好，开发环境中的所有类都能找到了

    2。->立即计算（只选中你需要的类， 如果有用class.forName之类通过程序显式装载的类的话，需要自己从编辑中手工家入了， 开发环境每能力帮你算出来了，如果内存没有问题的话，直接编辑->全选更方便吆）

    3。如果你的类并不想引入开发环境的话，点中添加额外路径，编辑（下面的一个）， 我不喜欢这样， 就不详细介绍了

高手的话，直接用jdk, 那么只好麻烦你要么加到系统classpath中（unix下为$CLASSPATH）, 要么编译运行时显式指明classpath

下面介绍一个初学者最容易犯的错误

还以上节介绍的例子

假定版本一在 c:\mylib\com\shinechina\MyClass.java

编译后c:\mylib\com\shinechina\MyClass.class

怎么指定classpath呢

常见毛病

1。c:\mylib\com\shinechina\

2。c:\mylib\com\

正确

1。c:\mylib\

2。c:\mylib

上节中介绍了找class的规则，这里就不说了

＜未完待续＞


关于java 的 Classpath全解java入门之一

作者：丑丑牛

日期：00-4-29 22:15:09

关于java 的 Classpath全解<之一>

作者 丑丑牛

声明： 欢迎转载， 如果能不把俺的little大名删掉最好

java 的新入门者对classpath往往比较困惑，为何在开发环境中能运行的东东出去就不好，或在外面运行的东东挺溜的进了开发环境就死菜，烦死了！

看完俺这篇文章包您万事无忧（夸张点吧） ：）呵呵

java的优点就是他是一个自动支持网络功能的东东，举一个简单例子来将

比方有一个Application他用到了java.lang.String(事实上要是有一个程序没用到它，太难以想象了 ：）)， 和com.shinechina.MyClass（我自己写的两个版本） 这两个类

版本1：

package com.shinechina;

public class MyClass

{

public static void main(String args[])

{

    String str = "版本1： hello, welcome to chinaasp java 技术";

    System.out.println(str);

}

}

版本2：

package com.shinechina;

public class MyClass

{

public static void main(String args[])

{

    String str = "版本2：hello, welcome to chinaasp java 技术";

    System.out.println(str);

}

}

现在我要运行它了

java com.shinechina.MyClass

现在问题来了

也许com.shinechina.Myclass在c:\, d:\, 甚至在阿龙老兄，javabeans老兄，的机器上也有这么个东东，里面是些很cool的

java怎么知道到何处找呢， 他到底该怎么办

假定版本一在 c:\mylib\com\shinechina\MyClass.java

假定版本二在 d:\mylib\com\shinechina\MyClass.java

注意：不要忘了编译成class文件吆, java文件是不能执行的

cd c:\mylib\com\shinechina

javac *.java

cd d:\mylib\com\shinechina

javac *.java

有了：

他先到机器的CLASSPATH去找

假设是windows系统 c:\mylib;d:\mylib;d:\jdk1.2.2\lib\rt.jar;

或unix系统    /home/local/:/usr/local/:/usr/local/jdk1.2.2/lib/rt.jar

好， c:\mylib下他找到了com目录，有戏， 又往下找又有shinechina目录，目录下找到了MyClass.class, Ok 就是他了， 至于d:\mylib我的版本2的MyClass他就不理会了， 至于String是在rt.jar压缩包中待会再讲他

执行的结果就是

版本1： hello, welcome to chinaasp java 技术

反过来d盘的路径在c:之前，执行的结果就是

版本2： hello, welcome to chinaasp java 技术

依次类推， 只有在类路径上第一个找到的才会执行，你要控制他执行那一个须如此:

java -classpath d:\mylib;%classpath% com.shinechina.MyClass

执行的结果就是

版本2： hello, welcome to chinaasp java 技术

开发环境中的执行就是这样，　总有一个地方指定类路径的

＜未完待续＞


关于Servlet、Jsp中的多国语言显示 -- 转自高人

作者：罗亭

email: tinluo@263.net

日期：2001-3-8 13:56:04

/**

*有朋友有如此苦心之作，实在感谢，相信它不会怪我转贴吧，他的email是vividq@china.com

*/

关于Servlet、Jsp中的多国语言显示

    因为一直不信Java竟会有不能混排显示多国语言的BUG，这个周末研究了一下Servlet、Jsp的多国语言显示的问题，也就是Servlet的多字符集问题，由于我对字符集的概念还不是很清晰所以写出的东西未必是准确的，我是这样理解Java中的字符集的：在运行时，每个字符串对象中存储的都是编码为UNICODE内码的（我觉得所有的语言中都是有相应编码的，因为在计算机内部字符串总是用内码来表示的，只不过一般计算机语言中的字符串编码时平台相关的，而Java则采用了平台无关的UNICODE）。

    Java从一个byte流中读取一个字符串时，将把平台相关的byte转变为平台无关的Unicode字符串。在输出时Java将把Unicode字符串转变为平台相关的byte流，如果某个Unicode字符在某个平台上不存在，将会输出一个'?'。举个例子：在中文Windows中，Java读出一个"GB2312"编码的文件（可以是任何流）到内存中构造字符串对象，将会把GB2312编码的文字转变为Unicode编码的字符串，如果把这个字符串输出又将会把Unicode字符串转化为GB2312的byte流或数组："中文测试"----->"\u4e2d\u6587\u6d4b\u8bd5"----->"中文测试"。

如下例程：

byte[] bytes = new byte[]{(byte)0xd6, (byte)0xd0, (byte)0xce, (byte)0xc4, (byte)0xb2, (byte)0xe2, (byte)0xca, (byte)0xd4};//GBK编码的"中文测试"

java.io.ByteArrayInputStream bin = new java.io.ByteArrayInputStream(bytes);

java.io.BufferedReader reader = new java.io.BufferedReader(new java.io. InputStreamReader (bin,"GBK"));

String msg = reader.readLine();

System.out.println(msg)

    这段程序放到包含"中文测试"这四个字的系统（如中文系统）中，可以正确地打印出这些字。msg字符串中包含了正确的"中文测试"的Unicode编码："\u4e2d\u6587\u6d4b\u8bd5"，打印时转换为操作系统的默认字符集，是否可以正确显示依赖于操作系统的字符集，只有在支持相应字符集的系统中，我们的信息才能正确的输出，否则得到的将会是垃圾。

    话入正题，我们来看看Servlet/Jsp中的多语言问题。我们的目标是，任一国家的客户端通过Form向Server发送信息，Server把信息存入数据库中，客户端在检索时仍然能够看到自己发送的正确信息。事实上，我们要保证，最终Server中的SQL语句中保存的时包含客户端发送文字的正确Unicode编码；DBC与数据库通讯时采用的编码方式能包含客户端发送的文字信息，事实上，最好让JDBC直接使用UNICODE/UTF8与数据库通讯！这样就可以确保不会丢失信息；Server向客户端发送的信息时也要采用不丢失信息的编码方式，也可以是Unicode/Utf8。

    如果不指定Form的Enctype属性，Form将把输入的内容依照当前页面的编码字符集urlencode之后再提交，服务器端得到是urlencoding的字符串。编码后得到的urlencoding字符串是与页面的编码相关的，如gb2312编码的页面提交"中文测试"，得到的是"%D6%D0%CE%C4%B2%E2%CA%D4"，每个"％"后跟的是16进制的字符串；而在UTF8编码时得到的却是"%E4%B8%AD%E6%96%87%E6%B5%8B%E8%AF%95"，因为GB2312编码中一个汉字是16位的，而UTF8中一个汉字却是24位的。中日韩三国的ie4以上浏览器均支持UTF8编码，这种方案肯定包涵了这三国语言，所以我们如果让Html页面使用UTF8编码那么将至少可以支持这三国语言。

    但是，如果我们html/Jsp页面使用UTF8编码，因为应用程序服务器可能不知道这种情况，因为如果浏览器发送的信息不包含charset信息，至多Server知道读到Accept-Language请求投标，我们知道仅靠这个投标是不能获知浏览器所采用编码的，所以应用程序服务器不能正确解析提交的内容，为什么？因为Java中的所有字符串都是Unicode16位编码的，HttpServletRequest.request(String)的功能就是把客户端提交的Urlencode编码的信息转为Unicode字符串，有些Server只能认为客户端的编码和Server平台相同，简单地使用URLDecoder.decode(String)方法直接解码，如果客户端编码恰好和Server相同，那么就可以得到正确地字符串，否则，如果提交地字符串中包含了当地字符，那么将会导致垃圾信息。

    在我提出的这个解决方案里，已经指定了采用Utf8编码，所以，可以避免这个问题，我们可以自己定制出decode方法：

public static String decode(String s,String encoding) throws Exception {

StringBuffer sb = new StringBuffer();

for(int i=0; i<s.length(); i++) {

char c = s.charAt(i);

switch (c) {

case '+':

sb.append(' ');

break;

case '%':

try {

sb.append((char)Integer.parseInt(

s.substring(i+1,i+3),16));

}

catch (NumberFormatException e) {

throw new IllegalArgumentException();

}

i += 2;

break;

default:

sb.append(c);

break;

}

}

// Undo conversion to external encoding

String result = sb.toString();

byte[] inputBytes = result.getBytes("8859_1");

return new String(inputBytes,encoding);

}

    这个方法可以指定encoding，如果把它指定为UTF8就满足了我们的需要。比如用它解析："%E4%B8%AD%E6%96%87%E6%B5%8B%E8%AF%95"就可以得到正确的汉字"中文测试"的Unicode字符串。

现在的问题就是我们必须得到客户端提交的Urlencode的字符串。对于method为get的form提交的信息，可以用HttpServletRequest.getQueryString()方法读到，而对于post方法的form提交的信息，只能从ServletInputStream中读到，事实上标准的getParameter方法被第一次调用后，form提交的信息就被读取出来了，而ServletInputStream是不能重复读出的。所以我们应在第一次使用getParameter方法前读取并解析form提交的信息。

    我是这么做的，建立一个Servlet基类，覆盖service方法，在调用父类的service方法前读取并解析form提交的内容，请看下面的源代码：

package com.hto.servlet;

import javax.servlet.http.HttpServletRequest;

import java.util.*;

/**

* Insert the type's description here.

* Creation date: (2001-2-4 15:43:46)

* @author: 钱卫春

*/

public class UTF8ParameterReader {

Hashtable pairs = new Hashtable();

/**

* UTF8ParameterReader constructor comment.

*/

public UTF8ParameterReader(HttpServletRequest request) throws java.io.IOException{

super();

parse(request.getQueryString());

parse(request.getReader().readLine());

}

/**

* UTF8ParameterReader constructor comment.

*/

public UTF8ParameterReader(HttpServletRequest request,String encoding) throws java.io.IOException{

super();

parse(request.getQueryString(),encoding);

parse(request.getReader().readLine(),encoding);

}

public static String decode(String s) throws Exception {

StringBuffer sb = new StringBuffer();

for(int i=0; i<s.length(); i++) {

char c = s.charAt(i);

switch (c) {

case '+':

sb.append(' ');

break;

case '%':

try {

sb.append((char)Integer.parseInt(

s.substring(i+1,i+3),16));

}

catch (NumberFormatException e) {

throw new IllegalArgumentException();

}

i += 2;

break;

default:

sb.append(c);

break;

}

}

// Undo conversion to external encoding

String result = sb.toString();

byte[] inputBytes = result.getBytes("8859_1");

return new String(inputBytes,"UTF8");

}

public static String decode(String s,String encoding) throws Exception {

StringBuffer sb = new StringBuffer();

for(int i=0; i<s.length(); i++) {

char c = s.charAt(i);

switch (c) {

case '+':

sb.append(' ');

break;

case '%':

try {

sb.append((char)Integer.parseInt(

s.substring(i+1,i+3),16));

}

catch (NumberFormatException e) {

throw new IllegalArgumentException();

}

i += 2;

break;

default:

sb.append(c);

break;

}

}

// Undo conversion to external encoding

String result = sb.toString();

byte[] inputBytes = result.getBytes("8859_1");

return new String(inputBytes,encoding);

}

/**

* Insert the method's description here.

* Creation date: (2001-2-4 17:30:59)

* @return java.lang.String

* @param name java.lang.String

*/

public String getParameter(String name) {

if (pairs == null || !pairs.containsKey(name)) return null;

return (String)(((ArrayList) pairs.get(name)).get(0));

}


关于sessionListener

作者：罗亭

email: tinluo@263.net

日期：2001-5-11 13:50:44

此文建议加入精华区

//问题

谢谢您，可是用session怎么能将客户端的正在浏览的url不断发送到服务器端呢？难道要在客户端做个什么东西，在嵌个浏览器？please give me a hint，thx！

//回答，用sessionListener[Session监听器来实现]

//下边是一个监器的源码，实现了在线人数，依此原理，你能做你想要做的

package core;

/**

 * Title:        core

 * Description:

 * Copyright:    Copyright (c) 2001

 * Company:  ****

 * @author 罗亭

 * @version 1.0

 */

import javax.servlet.*;

import javax.servlet.http.*;

public final class CounterListener implements HttpSessionListener {

    private int count = 10;

    private ServletContext context = null;

    public synchronized void sessionCreated(HttpSessionEvent se) {

        count++;

        log("sessionCreated('" + se.getSession().getId() + "'LuoTing's Log)"+"  count="+count);

        se.getSession().setAttribute("count",new Integer(count));

    }

    public synchronized void sessionDestroyed(HttpSessionEvent se) {

        count--;

        se.getSession().setAttribute("count",new Integer(count));

    }

    public int getCount() {

        return this.count;

    }

    public void addCount(){

      count++;

    }

    private void log(String message) {

    if (context != null)

        context.log("SessionListener: " + message);

    else

        System.out.println("SessionListener: " + message);

    }

}

//这就是一个监听器的源码，已经在Tomcat4.01上测试过[Tomcat3.1是不成的，其余app server的实现情况，得看它的DOCS]

//这个监听器需在你的web.xml中注册

//like this

    <listener>

        <listener-class>core.SessionListener</listener-class>

    </listener>

//放置的位置你放到它的示例后边即可

Good Lucky


关于回答网友有关servlet和servlet之间通信的问题（建议放入精华区）

作者：blackwhites

日期：2000-11-13 17:50:11

以下是几种常调用的方法

     Servlet to Servlet Communication

     Listing 1: ServletBase

     public class ServletBase extends HttpServlet{

     static Connection databaseConnection = null;

     public void init(ServletConfig _config) throws ServletException{

     super.init(_config);

     if ( databaseConnection == null )

     //- Open up the database connection

     }

     protected boolean isLoggedOn( String _username ){

     return true;

     }

     protected boolean logUserOn( String _username ){

     return true;

     }

     }

     Listing 2: Using the NewSerletBase Class

     public class logonServlet extends ServletBase{

     public void service(HttpServletRequest _req, HttpServletRe-

     sponse _res) throws ServletException{

     if ( isLoggedOn( _req.getParameter(襏SERNAME? ){

     //- Display a message indicating they are already logged on

     }else{

     logUserOn( _req.getParameter(襏SERNAME? );

     }

     }

     }

     Listing 3: Storing an Object

     public class logonServlet extends HttpServlet{

     public void service(HttpServletRequest _req, HttpServletRe-

     sponse _res) throws ServletException{

     ServletContext thisContext = getServletContext();

     //-- Assume some method creates a new connection class

     Connection newConnection = createConnection();

     thisContext.setAttribute( database.connection? newConnection );

     //-- Return some output to the client

     }

     }

     Listing 4: retrieving an Object

     public class logoffServlet extends HttpServlet{

     public void service(HttpServletRequest _req, HttpServletRe-

     sponse _res) throws ServletException{

     ServletContext thisContext = getServletContext();

     //-- Assume some method creates a new connection class

     Connection newConnection = thisContext.getAttribute(

     database.connection?;

     if ( newConnection == null )

     //- Database has not been opened yet

     //-- Return some output to the client

     }

     }

     Listing 5: Looking at All the Objects

     public class allServlet extends HttpServlet{

     public void service(HttpServletRequest _req, HttpServletRe-

     sponse _res) throws ServletException{

     ServletContext thisContext = getServletContext();

     //-- Assume some method creates a new Connection class

     Enumeration E = thisContext.getAttributeNames();

     while ( E.hasMoreElements() ){

     String name = (String)E.nextElement();

     System.out.println( "Object: " + name );

     }

     }

     }

     Listing 6: Retrieving Remote Contexts

     public class otherServlet extends HttpServlet{

     public void service(HttpServletRequest _req, HttpServletRe-

     sponse _res) throws ServletException{

     ServletContext otherContext =

     getServletContext(http://<otherdomain>/servlet/allServlet?;

     //-- Assume some method creates a new Connection class

     Enumeration E = otherContext.getAttributeNames();

     while ( E.hasMoreElements() ){

     String name = (String)E.nextElement();

     System.out.println( "Object: " + name );

     }

     }

     }

     Listing 7: Forwarding a Request

     public class forwardServlet extends HttpServlet{

     public void service(HttpServletRequest _req, HttpServletRe-

     sponse _res) throws ServletException{

     ServletContext xt = getServletContext();

     RequestDispatcher xyzServlet =

     xt.getRequestDispatcher(http://<domain>/servlet/xyzServlet?;

     //- Do any preliminary processing

     _req.setAttribute( database.results? new Results() );

     xyzServlet.forward( _req, _res );

     }

     }

     Listing 8: Inserting Content

     public class insertServlet extends HttpServlet{

     public void service(HttpServletRequest _req, HttpServletRe-

     sponse _res) throws ServletException{

     ServletContext xt = getServletContext();

     RequestDispatcher xyzServlet =

     xt.getRequestDispatcher(http://<domain>/servlet/xyzServlet?;

     PrintWriter Out = _res.getWriter();

     Out.println( this is from the insertServlet ?);

     for(int x=0; x < 10; x++ )

     xyzServlet.insert( _req, _res );

     Out.println( this is the end of the print servlet ?);

     }

     }


关于扰乱器与反编译 (转载)

作者：icelight

日期：2001-3-8 9:23:00

以下摘录于Delphibbs中Dr.yysun 的回答，希望对你有帮助

用 Java 编程的人, 如果不想公开源程序, 就得与反编译器作斗争,

必须使用反编译器,先看看别人将会如何看你的程序,再看看Obfuscators(扰乱器)的效果.

JAD 是 Windows 下效果极佳的反编译器 http://www.geocities.com/SiliconValley/Bridge/8617/jad.html

围绕这个引擎, 有很多图形界面, 例如 FrontEnd. (我常用)

JODE 是免费的 Java 反编译器和优化器.

http://jode.sourceforge.net/

Intr@byte 是个 JBuilder 的外挂工具 (OpenTool), 使得您在 JB 中能打开 *.class

http://www.webappcabaret.com/bjb/index.jsp

它也使用 JAD 引擎, 并限制为只反编译器类的定义部分.

有些网站可以帮您反编译, 您给个 URL, 它分析出其中有多少个 applet,

然后, 再反编译掉. http://www.jreveal.org/

最后, 您想了解反编译原理, 想自己编个反编译器和扰乱器, 这里有本免费得书 .

http://www.riis.com/depile.html

其他到 yahoo 里找吧, 太多了.

JJams_King: 扰乱器就是把 bytecode 中的类名，方法名，变量名等变成无意义的符号。

例如，以下源程序：

package temp;

public class HelloWorld {

  String helloWorld = "Hello World";

  String byeWorld = "Bye World";

  public static void main(String[] args) {

      new HelloWorld(true);

      new HelloWorld(false);

  }

  HelloWorld(boolean isHappy) {

      if (isHappy) {

        System.out.println(helloWorld);

      }

      else {

        System.out.println(byeWorld);

      }

  }

}

加扰后，再反编译出来是：

package a;

import java.io.PrintStream;

public class a

{

    String a;

    String b;

    a(boolean flag)

    {

        a = "Hello World";

        b = "Bye World";

        if (flag)

            System.out.println(a);

        else

            System.out.println(b);

    }

    public static void main(String astring[])

    {

        new a(true);

        new a(false);

    }

}

而更高级的扰乱器，还能加密字符常量和扰乱程序流。加扰后，再反编译后变成这样：

package a;

import java.io.PrintStream;

public class a

{

    String a;

    String b;

    public static boolean c;

    a(boolean flag)

    {

        a = a("q\0041\033/\03162\005,]");

        b = a("{\0308W\027V\0231\023");

        if (!flag) goto 42 else 26;

        System.out.println(a);

        if (!c) goto 52 else 42;

        System.out.println(b);

    }

    public static void main(String astring[])

    {

        new a(1);

        new a(0);

    }

    private static String a(String string)

    {

        char ach[];

        int i;

        int j;

        int k;

        ach = string.toCharArray();

        i = ach.length;

        j = 0;

        k = 0;

        expression 57

        expression ach

        expression j

        expression 97

        expression ach

        expression j

        expression 93

        expression ach

        expression j

        expression 119

        expression ach

        expression j

        expression 64

        expression ach

        expression j

        dup 2 over 1

        push []

        ^

        pop []

        j++;

        k++;

        if (i == j)

            return new String(ach);

        expression k

        switch

case 1: goto 21

case 2: goto 29

case 3: goto 37

case 4: goto 45

case 5: goto 10

default: goto 54

    }

}

你想破译其中的思路可就难了 :)

以上资料摘自:http://www.zelix.com/klassmaster/


关于中间件的问答实录（MIDWARE FAQ)

作者：byping

email: hdp@javaunion.org

日期：2001-1-7 15:01:17

关于中间件的问答实录（MIDWARE FAQ)

发言者：地老鼠

时  间：2001/01/02 19:56:48    来  自：http://www.javaunion.org

--------------------------------------------------------------------------------

关于中间件的问答实录（MIDWARE FAQ)

1、什么地方可以找到关于中间件的资料？

    请参考！

    发言者：hdp

    时　间：1970/01/01 07:59:59　　来　自：http://www.javaunion.org

    目前主要的中间件厂商网站有BEASYS.COM、IBM.COM、TIBCO.COM、NASOTF.COM.CN、TONGTECH.COM等等，

    当然微软也有企中间件产品MSMQ、MSTS等。所有中间件提供商网站上一般都能够找到中间件相关的资料。

    另外，messgeq.com是一个关于于消息中间件讨论的论坛，人气不错哦。

2、关于中间件的定义

    上下之间、两头之间、左右之间等等中间的都是中间件

    发言者：hdp

    时　间：1970/01/01 07:59:59　　来　自：http://www.javaunion.org

    不过最近所谓的中间件主要指：

    1、消息传输中间件　负责点点传输，通过配置形成屏蔽操作系统的底层传输平台；

    2、交易中间件　 负责管理客户端与数据库的交易逻辑部分；

    3、数据库中间件 提供客户端之间对数据库操作途径；

    4、分布计算中间件 如为Corba提供支持实现

    5、应用服务器　　如Java应用服务器，提供webserver后端交易处理、系统整合、负载均衡、资源调度等功能

    6、其它

    呵呵，大概还有！

    三层当中间的都是中间件

    发言者：小狐狸精

    时　间：1970/01/01 07:59:59　　来　自：http://www.javaunion.org

    恐怕给不出什么更好的乐

3、中间件的用途？中间件的利弊何在？

    1)请教，三层结构的缺点？(空) a_fan_ti

    re:

    发言者：hdp　　 主　页：Java联盟

    时　间：2000/11/30 17:10:33　　来　自：http://www.javaunion.org

    a.怕中间层过于复杂或脆弱；

    b.虽然灵活当一般会增加系统复杂性；

    c.系统的模块化、分层化越细，出现问题可能会越多且不易诊断！

    2)技术不好的人玩不了：）(空) Jspinage

    re:不过只是趋势，社会分工细化的现实表象...

    由于部分模块（中间件）已经被产品化，因此开发、使用和维护都更简单了，但

    发言者：hdp　　 主　页：Java联盟

    时　间：2000/11/30 17:31:21　　来　自：http://www.javaunion.org

    带来节省开发时间的同时，也产生了一些不方便的地方。一般中间件开发者往往会在简单、易用、可维护和灵活、可扩展之间难以取舍。因此出现了MQ这样的较为底层的中间件产品、基于MQ开发的一些中间件平台和国产中间件产品（如清华MT、东方通TONGLINK）。国产策略一般以贴近客户、简单易用为首要原则。

    可以说模块化、层次化的出现是使一部分人更精、使大部分人更笨的社会发展趋势的产物，是为了节省开支（本意)、加快进展和培养懒人的途径，同时也使单位时间效率增大、生命相对延长...

    3)哈哈，我想我还是想作生命相对不怎么长，但聪明的人。(空) Jspinage

    re: 确切的说，有奉献精神、想象丰富、颇具幽默却爱钱的计算机狂能做好中间层：）(空) hdp

    4） 以后还有4层，5层，n层。。。

    发言者：OnlyYou

    时　间：2000/12/01 10:11:14　　来　自：http://www.javaunion.org

    我以为，随着社会的进步，知识的极度膨胀，还有市场的快速变化，都要要求迅速开发一套使用方便、功能完善的可靠的系统。这些为中间件软件商提供了巨大的机会，尤其IBM等公司酷爱包装，最喜欢把一些底层的东西，如网络细节，事务处理等封装起来，让开发人员作一个傻子，只需要调用他们提供的数量不多的接口，便可以实现许多原来你需要大量代码来作的事情。

    这不知道对开发人员来说是好事，还是坏事，不过这倒是社会进步的一种表现。

    三层结构的确是很正确的思想，将来的分工必然越来越细。不管是从开发难易程度，安全性，稳定，可靠性，可扩展性，负载均衡等各种角度来说，都是很多公司发展的方向。不过，目前，依然有很多公司在作着C/S两层的软件。

    它的优点多多，但是也有一些缺点。首先，层增加了，就必然得有人了解这种和熟悉这种中间件，对它的配置、调测、管理等非常熟悉。这就必然需要公司去培养这种人力，改变原来的模式。还有，因为中间的链接越多，出问题的可能性越大，对中间件不熟悉的话，根本找不到原因。这种复杂性也相对增加了。还有，现在的中间件，比如应用服务器等，需要较高的硬件配置和软件投入，是许多小公司难以享用的...

   5）以后还有4层，5层，n层。。。

    发言者：bluebenben　　 邮　箱：lqh_98@163.net

    时　间：2000/12/01 11:34:23　　来　自：http://www.javaunion.org

    每一层，每一个模块。

    封装的越透明越好

    那末以后做产品只需要把这些模块拼接起来就可以了

    程序员嘛。

    只需要在已有的模块基础上，再开发上一层模块。

    然后提供给其他人使用，别人只看到一个透明的接口，而不会知道内部

    是如何实现的

    我觉得B/S C/S最终可能会统一起来

    用户只需要在终端上输入相关url

    那么就可以看到想要的东西

    而这个过程的实现可能是终端内置浏览器

    也可能是实时的把Client端软件下栽安装

    反正网速越来越快。

    你也感觉不到慢

4、中间件与应用服务器是否是一回事？

   目前应用服务器主要是指全面提供Java解决企业应用方案的应用开发运行环境的技术底层平台，

   本身支持Java标准。如：servlet engine、jasper engine、ejb server等等，一般产商喜欢把

   这些东东集成在一起，称为应用服务器，即遵循J2EE框架。从纯的意义上看，应用服务器也包括

   提供ASP、PHP全面解决企业应用的底层技术支撑平台，不过目前不多见而已。应用服务器与中间

   件不是一个概念范畴，当然如果应用服务器用来构建多次应用则是中间件的一种了，可称之为"应　 用中间件"。


果果来看(一个封装了基本JDBC操作的类)

作者：zergling

email: onlydream@eastday.com

日期：7/27/2001 1:15:48 PM

odbc.java

---------------------------------------------

package bbs;

/*

database operation class, test by odbc

This javabean is written by zergling

It is my first javabean :o

version 1.01

*/

import java.sql.*;

import java.lang.*;

import java.io.*;

import java.util.*;

import sun.io.*;

public class odbc

    {

    Connection sqlCon;

    ResultSet rstSql;

    Statement stmS;

    String strCon;

    String strSql;

    boolean status;

    long rowcount;

    int page;

    int pagesize;

    long pagecount;

    long firstrecord;

    //connect to the default database

    public boolean connect()

        {

        //Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");

        this.strCon = "jdbc:odbc:jspbbs";                                                  //replace with your default database

        try

            {

            Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");

            this.sqlCon = java.sql.DriverManager.getConnection(this.strCon,"sa","");    //replace with your default database connection configure option

            this.status = true;

            return true;

            }

        catch(Exception e)

            {

            this.status = false;

            return false;

            }

        }

    //connect to the custom database

    public boolean connect(String conName,String username,String password)

        {

        //Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");

        this.strCon = conName;

        try

            {

            Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");

            this.sqlCon = java.sql.DriverManager.getConnection(this.strCon,username,password);

            this.status = true;

            return true;

            }

        catch(Exception e)

            {

            this.status = false;

            return false;

            }

        }

    //execute sql(insert,update,delete,...)

    public boolean execute(String s)

        {

        try

            {

            this.stmS = this.sqlCon.createStatement();

            this.stmS.executeUpdate(s);

            this.status = true;

            return true;

            }

        catch(Exception e)

            {

            this.status = false;

            return false;

            }

        }

    //query the data from database

    public boolean query(String s)

        {

        try

            {

            this.rowcount = 0;

            this.stmS = this.sqlCon.createStatement();

            this.rstSql = this.stmS.executeQuery(s);

            while (this.nextrecord())

                {

                this.rowcount++;

                }

            this.rstSql = this.stmS.executeQuery(s);

            this.status = true;

            return true;

            }

        catch(Exception e)

            {

            this.status = false;

            return false;

            }

        }

    //return the row count

    public long getrowcount()

        {

        return this.rowcount;

        }

    //return the pagecount

    public long getpagecount()

        {

        return this.pagecount;

        }

    //return the resultset of data

    public String getstring(String s)

        {

        try

            {

            return this.rstSql.getString(s);

            }

        catch(Exception e)

            {

            return "not exists";

            }

        }

    public int getint(String s)

        {

        try

            {

            return Integer.parseInt(this.rstSql.getString(s));

            }

        catch(Exception e)

            {

            return 0;

            }

        }

    //resultset move forward

    public boolean nextrecord()

        {

        try

            {

            return this.rstSql.next();

            }

        catch(Exception e)

            {

            return false;

            }

        }

    //set current page (recall first)

    public boolean setpage(int size,int no)

        {

        this.pagesize = size;

        this.page = no;

        this.pagecount = Math.round((this.rowcount - 1) / this.pagesize)+1;

        this.firstrecord = this.pagesize * ( this.page - 1 );

        try

            {

            for(int i=0;i<this.firstrecord;i++)

                if (!this.nextrecord()) break;

            return true;

            }

        catch(Exception e)

            {

            return false;

            }

        }

    //get string from database and change it into chinese

    public String readChinese(String s)

        {

        try

            {

            String temp = new String(s.getBytes("GB2312"),"8859_1");

            return temp;

            }

        catch(Exception e)

            {

            return s;

            }

        }

    //write string to the database's chinese transform

    public static String writeChinese(String s)

        {

        char[] orig =s.toCharArray();

        byte[] dest =new byte[orig.length];

        for(int i=0;i<orig.length;i++)

        dest[i] =(byte)(orig[i]&0xFF);

        try

            {

            ByteToCharConverter toChar =ByteToCharConverter.getConverter("gb2312");

            return new String(toChar.convertAll(dest));

            }

        catch(Exception e)

            {

            return s;

            }

        }

    //string's search and replace

    public String replace(String con ,String tag,String rep){

        int j=0;

        int i=0;

        int k=0;

        String RETU="";

        String temp =con;

        int tagc =tag.length();

        while(i<con.length()){

            if(con.substring(i).startsWith(tag)){

                temp =con.substring(j,i)+rep;

                RETU+= temp;

                i+=tagc;

                j=i;

            }

            else{

                i+=1;

            }

        }

        RETU +=con.substring(j);

        return RETU;

    }

        public Vector listValue(String con ,String tag){

        int j=0;

        int i=0;

        int k=0;

        Vector vv=new Vector();

        String temp =con;

        int tagc =tag.length();

        while(i<con.length()){

            if(con.substring(i).startsWith(tag)){

                temp =con.substring(j,i);

                vv.addElement(temp);

                i+=tagc;

                j=i;

            }

            else{

                i+=1;

            }

        }

        vv.addElement(con.substring(j));

        return vv;

    }

    //filt the html code & sql symbol

    public String htmlencode(String s)

        {

        try

            {

            String temp = this.replace(s,"<","&lt;");

            temp = this.replace(temp,">","&gt;");

            temp = this.replace(temp,"'","&quot;");

            temp = this.replace(temp,"\"","&quot;");

            temp = this.replace(temp," ","&nbsp;");

            temp = this.replace(temp,"\n","<br>");

            return temp;

            }

        catch(Exception e)

            {

            return s;

            }

        }

    //return the status of last operation

    public boolean getstatus()

        {

        return this.status;

        }

    //close all object

    public boolean close()

        {

        try

            {

            if (this.sqlCon != null) this.sqlCon.close();

            if (this.rstSql != null) this.rstSql.close();

            if (this.stmS != null) this.stmS.close();

            this.status = true;

            return false;

            }

        catch(Exception e)

            {

            this.status = false;

            return true;

            }

        }

    }


好东西介绍，直接用 java 命令行动态生成jpg文件

作者：jeru

email: jeru@163.net

日期：2001-2-9 11:09:49

/**

* jeruGraphics v 1.0

*

* 看到一些动态生成图象的例子都是servlet完成的，

* 而且程序很长，觉得不是无论从实用性还是可读性来说都不是太好。

* 这里给了段代码，命令行生成图象文件。这样是不是简单易用些呢？

*

* 创建一个 BufferedImage 对象，将你的“画”放到这个缓冲里，

* 再打开一个文件，将图像流编码后输入这个文件，这样就有一个

* jpg文件出现了，试试吧。。。

*

* Mender :

*   Jeru Liu

* Homepage :

*   http://javaren.126.com

* Email: jeru@163.net

*

* 这仅仅是一个范例程序,没什么实用，却极具参考价值。

*

*/

import java.io.*;

import java.util.*;

import com.sun.image.codec.jpeg.*;

import java.awt.image.*;

import java.awt.*;

public class jeruGraphics {

  BufferedImage image;

  // 创建 jpg 文件到指定路径下

  public void createJpg(String path) {

    try {

      FileOutputStream fos = new FileOutputStream(path);

      BufferedOutputStream bos = new BufferedOutputStream(fos);

      JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(bos);

      encoder.encode(image);

      bos.close();

    } catch(FileNotFoundException fnfe) {

      System.out.println(fnfe);

    } catch(IOException ioe) {

      System.out.println(ioe);

    }

  }

  public static void main(String[] args) {

    int width=400, height=200;

    int xLength=300, yLength=150;

    int count=5;

    Vector data=new Vector();

    data.addElement(new Integer(100));

    data.addElement(new Integer(120));

    data.addElement(new Integer(150));

    data.addElement(new Integer(40));

    data.addElement(new Integer(5));

    jeruGraphics jg = new jeruGraphics();

    jg.image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

    Graphics g = jg.image.getGraphics();

    // 画坐标

    g.setColor(Color.white);

    g.fillRect(0, 0, width, height);

    g.setColor(Color.blue);

    g.drawLine(10,height-10,10,height-10-yLength);

    g.drawLine(10,height-10,10+xLength,height-10);

    // 连线

    int yTo;

    int yFrom = ((Integer)(data.elementAt(0))).intValue();

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

      yTo=((Integer)(data.elementAt(i))).intValue();

      g.drawLine(10+i*xLength/count,height-10,10+i*xLength/count,height-15);

      g.drawLine(10+(i-1)*xLength/count,yFrom,10+i*xLength/count,yTo);

      yFrom=yTo;

    }

    jg.createJpg("d:\\aaa.jpg");

  }

}


好久没来了，大家下载几个带源码的例子吧，BBS,Counter,guestbook,and so on

作者：whitefox

email: 9foryou@21cn.com

日期：2000-8-10 15:16:57

http://www.9foryou.com/java/counter.zip

http://www.9foryou.com/java/counter1.zip

http://www.9foryou.com/java/doubleboard2000-oracle.zip

http://www.9foryou.com/java/guestbook.zip

http://www.9foryou.com/java/jinybbs093jsp.zip


好文--Jive 中的设计模式 (转自IBM DeveloperWorks)

作者：jeru

email: jeru@163.net

日期：7/13/2001 5:48:02 PM

Jive 中的设计模式

(Design Pattern)

马旋

2001年 5月

摘要:Jive 是一个开放源码的论坛项目, 也就是我们所常见的 BBS, 采用了 SUN 公司的 JSP 技术, 相比起 j2ee 这个庞大的体系结构, 其整个的设计思想非常精炼, 适用于中小型网站, 建立自己的论坛系统. 这篇文章我们就一起来看一看 Jive 中所应用的设计模式(Design Pattern).

关于设计模式, 这篇文章并不详细解释, 只是结合 Jive 来看看设计模式在一个实际项目中的应用及其整体的设计思想. 所以在读这篇文章前, 假设您对设计模式有一个感性的认识, 对其具体应用以及实现方法有些疑问, 并渴望了解其思想,并使用过 Jive. 本文将一同来探讨这个问题. 为什么选择 Jive 而不是选择一个新的例子重新开始呢? 有以下两个原因: 1, 我们很多人对 bbs 这样一个事物比较熟悉,很清楚 bbs 所具有的一些基本功能, 如果自己作为设计者来设计这样一个 web bbs,会怎么想, 再看看别人是怎么实现的, 有对比才能明白自己设计上的缺点, 看到别人的优点才能更快地进步. 2, Jive 并不是非常地复杂, 并且包括了一个完整的实现方案, 从底层到高层, 从后端到前端, 都有很好的文档, 这些都能更好地帮助我们理解它.

这里我们所用的 Jive 的版本采用其开发者作为正式发布的 1.0 版, 其最新版为 1.21, 对其结构作了少量改动, 主要增加了 jsp tag 的支持, 这种技术不属于我们的讨论范围, 以后有机会可以共同学习.

Jive 中所使用的设计模式, 对设计模式的三种类型 -- 创建型, 结构型,行为型 -- 都有涉及, 这样也能比较全面地了解设计模式. 我们先来自己设计一下,运用面向对象的思想, 可以很容易知道, 整个系统主要需要这几个对象:

Forum　　 --　　一个讨论区, 也就是一个版面.

Thread　　--　　一条线索, 也就是有关同一个主题的所有的回文.

Message　 --　　一条消息, 也就是一个用户发的一篇贴子.(以后我们就用"贴子"这个叫法)

User　　　--　　一个用户, 也就是讨论区的使用者.

好了, 我们需要的东西都在了, 它们之间的关系十分复杂, 怎么把它们组织地很符合我们的思路又能容易扩充呢? 我想大家都有自己的想法了, "我能这么这么做","我可以这样这样设计", 我们一起来看看 Jive 是怎么做的. 下面是其整体结构:

                    |~~~~~~~~~~~~~~~~~~|

                    |   Skin 设计者    |

                    |__________________|

                            | |

                            | |  使用

                            \ /

                    |~~~~~~~~~~~~~~~~~|

                    | 各种对象的接口  |

                    |_________________|

                            | |

                            | |  被实现

                            \ /

                       |~~~~~~~~~~~~|

                       |  权限控制  |

                       |____________|

                            | |

                            | |  控制

                            \ /

                |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|

                | 对数据库进行操作的各种对象  |

                |_____________________________|

                            | |

                            | |  取连接

                            \ /

                     |~~~~~~~~~~~~~~~~|

                     |  数据库连接池  |

                     |________________|

                     (图 1)

下面是其类的大概的继承情况:

                 |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|

                 |          Interface  A             |

                 |___________________________________|

                         |                    |

                         |    implements      |

                         |                    |

                 |~~~~~~~~~~~~~~~~~|          |

                 |   Proxy   A     |          |

                 |_________________|          |

                                              |

                                              |

                                     |~~~~~~~~~~~~~~~~~~|

                                     |    Database A    |

                                     |__________________|

                  (图 2)

好了看到这里, 如果您对设计模式有了解的话, 从上面所写的伪名字中, 可以看到一些熟悉的东西. 请让我做一些解释. 上面的图表示的是类的继承关系, A 代表上面所提到的四种对象, Interface A 表示名为 A 的一个接口, 相信大家对接口都不陌生, 接口在 java 中有着重要的作用. Proxy A 表示一个名为 ProxyA 的类,实现 A 接口. Database A 表示名为 DbA 的一个类, 实现 A 接口. 但设计模式并没有从中体现出来,设计模式所要表现的是怎么样更好地组织对象之间的逻辑关系,怎么样才能更好地扩充现有的东西而不需要作很大的改动, 而不仅仅是类的继承.

还有一点需要说明的是, 设计模式总的原则是针对接口编程, 而不关心其具体实现, 这样搭起来的是一个架子, 还需要作许多具体的编程才能真正的完成系统.

下面, 我们就分别从设计模式的三种类型来看 Jive 使用了其中的哪些.

一, 创建型模式 (Creational Patterns)

这一类型的设计模式, 所要表现的是对象的创建过程及和用户所使用的对象之间的关系.

Jive 中在 Forum 之上又加了一层, ForumFactory, 来实现对 Forum 的一些控制, 比如创建新的讨论区, 删除一个讨论区等等. 这个类实际上是整个系统的入口,jsp 中所做的一切都要从得到这个类的一个实例开始. 它的一些子类和它的关系如下:

                        |~~~~~~~~~~~~~~~~~|

                        |  ForumFactory   | abstract

                        |_________________|

                           |           |

                           |  extends  |

                           |           |

              |~~~~~~~~~~~~~~~~~~~~| |~~~~~~~~~~~~~~~~~|

              | ForumFactoryProxy  | |  DbForumFactory |

              |____________________| |_________________|

                (图 3)

我们来看一下得到一个 ForumFactory 实例的过程:

FactoryForum factory = ForumFactory.getInstance(aAuthorization);就得到了 ForumFactory 的实例, 这个最终用户(skin 设计人员)所使用的是它的子类 ForumFactoryProxy 的实例, (其中涉及到另一个模式, 后面将会提到), 但实际上真正在做实际工作的是 DbForumFactory 或者是一个指定的类的实例, 相关代码如下:

From ForumFactory.java

    private static String className = "com.coolservlets.forum.database.DbForumFaactory";

    // 系统缺省的 ForumFactory 的一个具体的子类.

    private static ForumFactory factory = null;

ForumFactory.getInstance()

    String classNameProp = PropertyManager.getProperty("ForumFactory.className")

    // 可以通过配制文件来选择其他的具体的子类.

    if (classNameProp != null) {

          className = classNameProp;

    }

    try {

       //Load the class and create an instance.

       Class c = Class.forName(className);

       factory = (ForumFactory)c.newInstance();

    }

    catch (Exception e) {

       System.err.println("Failed to load ForumFactory class "

            + className + ". Jive cannot function normally.");

       e.printStackTrace();

       return null;

    }

它使用的是 Abstract Factory (抽象工厂)设计模式. 给用户一个使用一系列相关对象的接口, 而不需要指定其具体的类. 也就是说, skin 设计人员写的 jsp 中不应该出现new DbForumFactory 之类的语句. Jive 中 AuthorizationFactory 也使用了这个设计模式

Jive 中有一个很不错的想法, 就是对贴子的内容和标题可以进行过滤, 比如过滤 html过滤一些脏话, 对附加的代码进行高亮显示, 转换链接等等. 如果我要实现这样的功能, 有有下几种方法: (1) 在 Message.getBody() getSubject() 中进行控制, (2) 在 Thread 中得得Message 后进行转换. 还需要考虑的问题是这些过滤的操作必须能够很方便地添加删除. 不不的目标所用的设计方法是不一样的, Jive 是这样做的: 以版面为主, 把这些过滤器看作是鞍婷的属性, 过滤器只对其所属的版面有效, 所以 Jive 中使用了 (2), 这并不是主要的, 重要要是这些过滤器该怎么来组织. 我们先来看看需求: 能动态添加删除, 功能类似, 贴子的显示示其具体怎么创建, 如何表现无关. 似乎目标只有一个 -- Prototype(原型) 设计模式. 看看Jive 的具体实现.                                   |~~~~~~~~~~~~~~~~~~~~|

                                  |    ForumMessage    |

                                  |____________________|

                                           |

                                           | implements

                                           |

   |~~~~~~~~~~~~~~~~| Prototype   |~~~~~~~~~~~~~~~~~~~~~|

   |  ForumThread   |-----------> |  ForumMessageFilter |

   |----------------|             |---------------------|

   | getMessage() o |             |     clone()         |

   |______________|_|             |_____________________|

                  |                 /             |

   |~~~~~~~~~~~~~~~~|      |~~~~~~~~~~~~~~~| |~~~~~~~~~~~~~|

   | aFilter.clone()|      | HighlightCode | |    HTML     |

   |________________|      |---------------| |-------------| ......

                           |  clone()  o   | |   clone() o |

                           |___________|___| |___________|_|

                                       |                 |

                           |~~~~~~~~~~~~~~~| |~~~~~~~~~~~~~~~|

                           |  返回一个实例 | |  返回一个实例 |

                           |_______________| |_______________|

                           (图 4)

上图作了少许的简化. Jive 用的时候是把这些过滤器存在数据库中, 可以动态设置属性, 比较方便. 来看一些代码:

From: DbForumThread.java

    public ForumMessage getMessage(int messageID)

            throws ForumMessageNotFoundException

    {

        ForumMessage message = factory.getMessage(messageID);

        //Apply filters to message.

        message = forum.applyFilters(message);

        //通过 Forum 来实现, 因为 Filter 是 Forum 的属性,

        //Thread 只能通过 Forum 的接口来访问.

        return message;

    }

From: DbForum.java

    public ForumMessage applyFilters(ForumMessage message) {

        for (int i=0; i < filters.length; i++) {

            message = filters[i].clone(message);

        }

        //可能会有多个过滤器, 依次来操作.

        return message;

    }


回答：为什么执行JAVA程序时，会出现Exception in threadmain java.lang.NoClassDefFoundError的错？（太多人问了，烦！建议放入精华区！）

作者：sonymusic

email: sonymusic@china.com

日期：2001-2-15 13:29:54

应该这样检查你的环境和你的操作：

1、你的文件名对吗？

JAVA要求你的文件和你的类名严格对应的。

比如 public class HelloWorld{ .....

那么文件名一定只能是HelloWorld.java，看仔细了！

2、你的程序中有main方法吗？参数什么的对吗？

main方法的声明应该是这样子的：

public static void main(String[] args)

事实上如果你用开发工具来做的话，是不会错的。如果你用UltraEdit之类的来写程序的话，就一个字不要改的照抄吧！

3、记住，javac后面跟的参数是文件名，但java 后面跟的是类名！

这是我开始学JAVA是犯的错，java HelloWorld.class，然后就报错。后来才弄明白，应该是java HelloWorld。

4、设置了classpath了吗？

不仅是在安装JDK时要设置的内容，一般来说最好还要自己设置一个目录来存放自己的类文件的。比如d:\class目录。

设置classpath=%classpath%;d:\class

那么，现在将HelloWorld.class文件COPY到d:\class目录下，再java HelloWorld应该就OK了。

5、结合第3点和第4点

如果你的类是属于某一个包的，比如这样一个类sony.test.HelloWorld，那么按照我在第4点中说的目录来说，就应该将HelloWorld.class文件存放到d:\class\sony\test\目录下。

执行的时候应该参考第3点中说的，按类名执行：java sony.test.HelloWorld。

明白了吗？一直我也想不出还会有什么理由造成这个出错信息了！如果有哪位兄弟想到了就补充一下吧！

其实如果你使用了一些比较好的编程工具的话，根本就没有机会碰到这个错误的，至少我在使用了VisualAge for Java之后，就没有这样的事情了。

好啦，不说了，祝同志们好运吧！

过了这第一道门，后面的就比较简单啦！


回答以前网友提出时间的问题

作者：blackwhites

email: blackwhites@sohu.com

日期：2000-10-12 11:19:06

源码如下import java.util.*;

public class ShowDate {

public static void main(String[] args) {

Calendar calendar = new GregorianCalendar();

Date trialTime = new Date();

calendar.setTime(trialTime);

// print out a bunch of interesting things

System.out.println("ERA: " + calendar.get(Calendar.ERA));

System.out.println("YEAR: " + calendar.get(Calendar.YEAR));

System.out.println("MONTH: " + calendar.get(Calendar.MONTH));

System.out.println("WEEK_OF_YEAR: " + calendar.get(Calendar.WEEK_OF_YEAR));

System.out.println("WEEK_OF_MONTH: " + calendar.get(Calendar.WEEK_OF_MONTH));

System.out.println("DATE: " + calendar.get(Calendar.DATE));

System.out.println("DAY_OF_MONTH: " + calendar.get(Calendar.DAY_OF_MONTH));

System.out.println("DAY_OF_YEAR: " + calendar.get(Calendar.DAY_OF_YEAR));

System.out.println("DAY_OF_WEEK: " + calendar.get(Calendar.DAY_OF_WEEK));

System.out.println("DAY_OF_WEEK_IN_MONTH: " + calendar.get(Calendar.DAY_OF_WEEK_IN_MONTH));

System.out.println("AM_PM: " + calendar.get(Calendar.AM_PM));

System.out.println("HOUR: " + calendar.get(Calendar.HOUR));

System.out.println("HOUR_OF_DAY: " + calendar.get(Calendar.HOUR_OF_DAY));

System.out.println("MINUTE: " + calendar.get(Calendar.MINUTE));

System.out.println("SECOND: " + calendar.get(Calendar.SECOND));

System.out.println("MILLISECOND: " + calendar.get(Calendar.MILLISECOND));

System.out.println("ZONE_OFFSET: " + (calendar.get(Calendar.ZONE_OFFSET)/(60*60*1000)));

System.out.println("DST_OFFSET: " + (calendar.get(Calendar.DST_OFFSET)/(60*60*1000)));

System.out.println("Current Time, with hour reset to 3");

calendar.clear(Calendar.HOUR_OF_DAY); // so doesn't override

calendar.set(Calendar.HOUR, 3);

System.out.println("ERA: " + calendar.get(Calendar.ERA));

System.out.println("YEAR: " + calendar.get(Calendar.YEAR));

System.out.println("MONTH: " + calendar.get(Calendar.MONTH));

System.out.println("WEEK_OF_YEAR: " + calendar.get(Calendar.WEEK_OF_YEAR));

System.out.println("WEEK_OF_MONTH: " + calendar.get(Calendar.WEEK_OF_MONTH));

System.out.println("DATE: " + calendar.get(Calendar.DATE));

System.out.println("DAY_OF_MONTH: " + calendar.get(Calendar.DAY_OF_MONTH));

System.out.println("DAY_OF_YEAR: " + calendar.get(Calendar.DAY_OF_YEAR));

System.out.println("DAY_OF_WEEK: " + calendar.get(Calendar.DAY_OF_WEEK));

System.out.println("DAY_OF_WEEK_IN_MONTH: " + calendar.get(Calendar.DAY_OF_WEEK_IN_MONTH));

System.out.println("AM_PM: " + calendar.get(Calendar.AM_PM));

System.out.println("HOUR: " + calendar.get(Calendar.HOUR));

System.out.println("HOUR_OF_DAY: " + calendar.get(Calendar.HOUR_OF_DAY));

System.out.println("MINUTE: " + calendar.get(Calendar.MINUTE));

System.out.println("SECOND: " + calendar.get(Calendar.SECOND));

System.out.println("MILLISECOND: " + calendar.get(Calendar.MILLISECOND));

System.out.println("ZONE_OFFSET: " + (calendar.get(Calendar.ZONE_OFFSET)/(60*60*1000))); // in hours

System.out.println("DST_OFFSET: " + (calendar.get(Calendar.DST_OFFSET)/(60*60*1000))); // in hours

}

}


回复★★★各位高手，win2000server下安装tomcat后，再安装apache有什么好处？？？

作者：chinesealbert

日期：2001-5-25 10:41:13

设置Tomcat与Apache Web 服务器

服务器

到现在为止,我们未讨论作为服务器扩展的Tomcat,只讨论了作为独立运行的服务器.但有一些问题需要说明:

1.    当处理静态页面时,Tomcat不如Apache迅速.

2.    Tomcat不象Apache一样可配置.

3.    Tomcat不象Apache一样强壮.

4.    有很多网站已在某一特定web server上投入了很长时间,例如,使用CGI脚本/Server API模组/perl/php…我们不能假设这些遗留下来的东西都会被丢弃.

基于以上原因,一个现实的网站建议使用一个Web服务器,如Apache,为网站的静态页面请求提供服务,并使用Tomcat作为一个Servlet/JSP插件.

我们不准备深入的讨论每个不同的配置,我们将:

1.      涵盖Web服务器的基本行为

2.      解释需要何种配置

3.      在Apache上的实例

Web 服务器操作

简单说来，web服务器总是等待来自客户端的HTTP请求。当请求到达时，服务器会提供一切必要的内容来满足此请求。加入一个Servlet容器某种程度上会改变此行为。但服务器仍需处理如下因素：

载入servlet容器接口库并初始化（处理请求之前）。

当收到一个请求时，检查是否属于某Servlet，如是，则接口库接收此请求并处理。

另一方面，接口库需要知道他将服务某种请求，通常是基于请求的URL的某种模式和将此请求导向何处。

当用户想要设置使用虚拟主机的配置时，事情会变得更加复杂，或者想多个开发者在一个服务器上进行开发但使用不同的Servlet容器的JVMs。以下我们将讨论这两个问题。

必需进行的配置

应该考虑的是最明显的配置是servlet URL具有对servlet容器中的servlet具有表示的责任。很明显，你必须知道传递什么到Servlet容器。我们仍需提供附加的配置项目到web-server或servlet-container的结合体中.

l         考虑到Tomcat进程是否可取得，我们仍需提供配置和Tomcat正在监听的TCP/IP 主机名/端口号。

l         需要告知web服务器接口库的位置（因此我们可以在起始时装入）

l         需要设置接口内部信息如将日志记录在何处和如何纪录，等等。

所有此类信息必须出现在web服务器配置里或被接口使用的私有配置文件中。下面将讲述如何在Apache中如何实现这些配置。

Apache上的实现

      这一部分演示如何配置Apache与Tomcat一起工作；并试图解释深入到可能会用到的配置规范。在jserv 安装页上可找到其他信息。


回复java新手的问题！

作者：jean777

email: jean263@263.net

日期：2001-3-20 16:43:16

Java/jsp 好的入门文章共赏

--------------------------------------------------------------------------------

 【丑丑牛】 于 00-4-28 18:02:12 加贴在 JAVA技术 ↑：

用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、在 http://java.sun.com/jdk/2 SDK、Standard Edition、v 1.2.2)。

2、在http://java.sun.com/products/jsp/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:8080JSWDK 的欢迎页就说明JSP实验环境已经建成，可进入下一步实验。 要关闭Web服务器则运行stopserver.bat。

三、 JSP简单示例 JSWDK中包含的Web服务器的文档目录在缺省状态下为\jswdk-1.0.1\webpages，主文档在缺省状态下为index.html和index.jsp。也就是说访问http://localhost:8080用文本编辑器，如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文件中用<%以及%>括起来的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" %>

五、 服务器参数设置

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

<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目录下，其内容如下：

在JSP网页中，使用 <jsp:useBean ... />语法来创建JavaBean对象，并命名为helloBean。读者可从这个简单的例子中看出设置、获取JavaBean属性，以及调用JavaBean方法的做法。在浏览器的地址栏中键入http://localhost:8080/test/hi-bean.jsp

注意，如果修改和重新编译了JavaBean程序，则需要关闭和重新启动JSWDK的Web服务器以后修改的结果才会有效。如果仅仅修改JSP文件，则不用重新启动JSWDK的Web服务器.

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

附文

CLASS文件的存放位置

--------------------------------------------------------------------------------

 【何志强】 于 2000-8-11 9:01:57 加贴在 JAVA技术 ↑：

在Linux平台下的Tomcat中，只需把相关的类库(*.zip、*.jar)放到$TOMCAT_HOME/lib目录下，Tomcat启动时就会自动将这些类库放到CLASSPATH环境变量中，Windows平台下的Tomcat不行，必须手工添加到CLASSPATH环境变量中。

回答：为什么执行JAVA程序时，会出现Exception in thread"main" java.lang.NoClassDefFoundError的错？（太多人问了，烦！建议放入精华区！）

--------------------------------------------------------------------------------

 【sonymusic】 于 2001-2-15 13:29:54 加贴在 JAVA技术 ↑：

应该这样检查你的环境和你的操作：

1、你的文件名对吗？

JAVA要求你的文件和你的类名严格对应的。

比如 public class HelloWorld{ .....

那么文件名一定只能是HelloWorld.java，看仔细了！

2、你的程序中有main方法吗？参数什么的对吗？

main方法的声明应该是这样子的：

public static void main(String[] args)

事实上如果你用开发工具来做的话，是不会错的。如果你用UltraEdit之类的来写程序的话，就一个字不要改的照抄吧！

3、记住，javac后面跟的参数是文件名，但java 后面跟的是类名！

这是我开始学JAVA是犯的错，java HelloWorld.class，然后就报错。后来才弄明白，应该是java HelloWorld。

4、设置了classpath了吗？

不仅是在安装JDK时要设置的内容，一般来说最好还要自己设置一个目录来存放自己的类文件的。比如d:\class目录。

设置classpath=%classpath%;d:\class

那么，现在将HelloWorld.class文件COPY到d:\class目录下，再java HelloWorld应该就OK了。

5、结合第3点和第4点

如果你的类是属于某一个包的，比如这样一个类sony.test.HelloWorld，那么按照我在第4点中说的目录来说，就应该将HelloWorld.class文件存放到d:\class\sony\test\目录下。

执行的时候应该参考第3点中说的，按类名执行：java sony.test.HelloWorld。

明白了吗？一直我也想不出还会有什么理由造成这个出错信息了！如果有哪位兄弟想到了就补充一下吧！

其实如果你使用了一些比较好的编程工具的话，根本就没有机会碰到这个错误的，至少我在使用了VisualAge for Java之后，就没有这样的事情了。

好啦，不说了，祝同志们好运吧！

过了这第一道门，后面的就比较简单啦！

Java 的執行環境

本節將介紹 Java 的執行環境並說明此環境中的一些元件與相關指令。首先我們必須先安裝 Java 環境。你先上網連結至 Sun Microsystems, Inc.的 Java 專屬網站， http://java.sun.com/products/jdk/1.1/download-jdk-windows.htmlJDK(Java Development Kit)軟體 (jdk_1_1_8-win.exe)。然後執行該軟體，就可安裝以下幾個元件：

程式庫(CoreClasses, classes.zip)是位於你所安裝 JDK 主目錄的 LIB 次目錄下(如 C:\JDK1.1.8\LIB)，且以壓縮的方式存在，千萬不要將此檔案 解壓縮。它是在執行 Java 編譯器(Compiler)或解譯器(Interpreter)時會 使用到的檔案。

原始檔案位於你所安裝 JDK 主目錄的 SRC 次目錄下，是上述程式庫的 Java 原始檔。

位於你所安裝 JDK 主目錄的 BIN 次目錄下，是編譯或執行應用程式 (Applications)或小程式 (Applets)所需要的工具和指令，主要有：

編譯器(Java Compiler, javac)─將 Java 原始檔(*.java)編譯為 位元檔(*.class)

解譯器(Java Interpreter, java)─執行 Java 程式(*.java)

執行器(Java Runtime Interpreter, jre)─也是執行 Java 程式， 一般使用者(end users) 使用

檢視器(Java AppletViewer, appletviewer)─用來執行與測試 Java 小程式(applet)

除錯器(Java Debugger, jdb)─幫助發現程式的蟲蟲(bugs)

文件器(Java Documentation Generator, javadoc)─由 java 原始檔中製作可由導覽器 瀏覽之文件說明檔

壓縮器(Java Archive Tool, jar)─將多個java程式檔(*.class) 壓縮為 ZIP 格式的檔案 (*.jar)

Java示範程式集位於你所安裝 JDK 主目錄的 DEMO 次目錄下， 你可以變換到此次目錄的再下一層次目錄，然後執行小程式，例如：

>cd c:\jdk1.1.8\demo\TicTacToe

>c:\jdk1.1.8\bin\appletviewer .\example1.html

<練習> 請由導覽器中開啟Java示範小程式

為了簡化指令長度(如上面執行示範小程式)，在執行 Java 指令前可先設定環境變數，讓系統能夠找到指令和相關程式庫所在。

set CLASSPATH=C:\jdk1.1.8\lib\classes.zip;.\.;%CLASSPATH%

set PATH=C:\jdk1.1.8\bin;%PATH%

以上設定完畢後，只需敲入以下指令：

>appletviewer .\example1.html

[PREVIOUS][TOC][NEXT]


回复XML 和Java

作者：luflying

email: zhang_yng@citiz.net

日期：2000-11-24 10:18:53

    XML在许多方面增强了Java。然而，XML也发展了一种对象传输协议，该协议与Java声称的跨平台性格格不入。XML将基于网络的信息置标化，使得开发者和电脑易于辨认。这是有必要的，因为HTML除了隐藏的URL外缺少有效的途径来说明内容的含义。XML的目标就为网络的对象添加那些含义，而这曾经就是Java的任务之一。

"XML让开发者能够在建立网络应用和Java系统之间选择."

-- Adam Berrey, Allaire产品市场部主任

现在开发先进的网络应用的所有需要仅一台客户机(Client)而已。它获取XML信息后直接提交给客户端浏览器。因此，基于Java虚拟机(JVM)的客户机不再是唯一的选择。

" XML将最终消除在客户端建立Java虚拟机的必要."

-- Doug Pollack, GemStone Systems市场部副总裁

推动Java的Sun公司承认没有Java虚拟机XML也能与客户端通讯，但是它认为XML需要Java来发挥它的潜力.

"XML不能单独使用;它是对Java的完善.Java为XML提供易于使用的代码,XML为Java提供数据."

-- Nancy Lee, Sun 公司XML 产品经理.

XML能创建不依赖于平台、语言或限制性格式化协定的开放数据。如果广泛地被采用, XML能变成为内容以及下至客户端对象通讯的广泛标准。这似乎十分类似Java,至少作为内容平台. 正是XML这样一种角色吸引了一向鄙弃Java的微软公司。

"XML能在不同平台间交换信息，它不会与跨平台的应用程序混淆不清."

-- Dave Wascha, 微软公司XML产品经理

但从许多方面来讲，这并不是选择Java还是XML这回事。许多网络开发者得出结论：XML和Java是完美的一对，因为彼此十分相辅相成。XML有助于独立平台、易提取信息的文档和数据. Java有助于独立平台、易于处理的面向对象的应用软件解决方案.

能驱动XML广泛接受的应用是在HTML的限制下难以实现的那些应用。可分为下列的四大类:

需要网络客户端协调多种不同类型数据库的应用；

力图将比例可观的处理工作从服务器端分布于客户端的应用；

要求客户端将同一数据以不同的形式展现给不同的使用者的应用；

智能网络代理应个别用户的需要搜索定制信息的应用。

"我们正在利用XML和 Java进行开发，两者真是完美的补充 !"

-- David Skok, SilverStream软件公司主席和创始人

"缺少XML的Java战略是不全面的."

-- Eric Brown, 剑桥大学Forrester研究中心分析研究员

这很符合IBM（同时支持XML和Java发展的强大后盾）似乎在考虑的想法。

"XML和 Java是并行和共生的.在新的千年里,两者都将是计算处理的关键技术."

-- Simon Phipps, IBM 公司XML和Java市场发展负责人


回复奇怪问题：我把resin和iis结合起来以后，asp程序就不能用了，把源码都显示出来了？？怎么解决阿

作者：87zd

email: jspsky@8848.net

日期：2001-5-6 16:26:05

--------------------------------------------------------------------------------

    IIS WEB以其管理操作简单和对ASP的支持而受不少人的喜欢。这里介绍怎么用resin使IIS支持jsp和servlet。

    一、分别安装配置iis和resin1.1使各自都单独能正常运行。iis和resin1.1的安装后路径如下：c:\apache 和 c:\resin1.1。

    二、配置IIS

        1.拷贝iis_srun.dll 到IIS scripts目录"c:\inetpub\scripts";

        2.在c:\inetpub\scripts目录下创建配置文件(resin.ini)用于指定resin的配置文件的位置。

          resin.ini内容如下：

               CauchoConfigFile c:/resin1.1/conf/resin.conf

    三、配置resin

        <caucho.com>

        <http-server app-dir='C:\Inetpub\wwwroot'>

        <servlet-mapping rl-pattern='/servlets/*' servlet-name='invoker'/>

        <servlet-mapping rl-pattern='*.xtp' ervlet-name='com.caucho.jsp.XtpServlet'/>

        <servlet-mapping rl-pattern='*.jsp' servlet-name='com.caucho.jsp.JspServlet'/>

        </http-server>

        </caucho.com>

    四、测试

        1、 运行IIS,启动resin的jsp引擎（运行c:\resin1.1\bin\srun.exe）

        2、 把一个jsp文件(test.jsp )放入IIS的C:\Inetpub\wwwroot目录下.

        test.jsp如下：

          <%@ page language=java?>

          2+2=<%=2+2%>

浏览http://localhost

注：port为端口号，调试环境nt4.0+iis+resin1.1


回复请教各位,如何区分鼠标事件中的左键点击还是右键点击谢过先!

作者：jeru

email: jeru@163.net

日期：7/4/2001 3:47:09 PM

5.9a 怎样判断使用者按下的是滑鼠哪一个按钮 ?

A. 请参考底下这个例子:

public void mouseClicked(MouseEvent e) {

if(e.getModifiers()==MouseEvent.BUTTON1_MASK){

System.out.println("您按了滑鼠第一个按钮!");

}

if(e.getModifiers()==MouseEvent.BUTTON2_MASK){

System.out.println("您按了滑鼠第二个按钮!");

}

if(e.getModifiers()==MouseEvent.BUTTON3_MASK){

System.out.println("您按了滑鼠第叁个按钮!");

}

}

通常第一个按钮指的是用来 "选择" 的按钮, 第二, 叁个按钮则视不同的平台而定, 有些系统并不提供这两个按钮给使用者, 因此程式设计者必须谨慎地考量到这两个按钮缺席的 场合.


回复请问哪里有jboss有关配置的教程，最好是中文的，先谢了

作者：大哥

日期：2001-5-10 11:59:39

Jboss下配置EJB

转载 东方一蛇

〖返回〗〖转发〗

一、前言

     Jboss为Open Source，完全免费的Appliaction Server，对于J2EE有很好的支持，可以和Tomcat完美结合。

二、下载，安装

     下载地址为Jboss官方站点：http://www.jboss.org/

     下载完，解压到一个目录，我这里用E:jboss_tomcat作为例子。下面有子目录:

E:jboss_tomcatjboss2

和

E:jboss_tomcat omcat32b7

注意，我这里的目录稍微做了改动的，这样如果你不将TOMCAT_HOME加到CLASSPATH中，E:jboss_tomcatjboss2in un_with_tomcat.bat这个文件要做相应改动才能正常运行。

三、配置运行

     基本上不要什么配置的，只要安装好了JDK，当然，最好将TOMCAT_HOME加到CLASSPATH中。

这样，E:jboss_tomcatjboss2in un_with_tomcat.bat这个文件前的TOMCAT_HOME就可以去掉了。

运行E:jboss_tomcatjboss2in un_with_tomcat.bat这个文件，这样，Tomcat端口在8080，Jboss为8083，

运行http://localhost:8080/将出现tomcat首页，

运行http://localhost:8083/将出现无错误的空白页，

四、测试EJB

     (1)启动JBOSS：

方法见上：

     (2)下载代码：

http://www.wodejia.net/softdownload/java/interestejb.zip

解开，出现如下目录：

interest/com

interest/docs

...

全部copy到jboss2下examples目录下，如果没有examples目录，建立一个，结构如下：

E:jboss_tomcatjboss2examplesinterest...

将文件E:jboss_tomcatjboss2examplesinterestinterest.jar复制到：

E:jboss_tomcatjboss2deploy下：

在CLASSPATH中加入如下文件：

E:jboss_tomcat omcat32b7libservlet.jar;E:jbossclientjboss-client.jar;E:jbossclientjnp-client.jar;E:jbosslibextejb.jar;E:jbossexamplesinterestinterest-client.jar

为了测试Client通过servlet调用EJB，必须将： E:jboss_tomcatjboss2examplesinterest下EJB.class和EJB.java复制到：

E:jboss_tomcat omcat32b7webappsROOTWEB-INFclasses目录下

重新启动JBOSS_TOMCAT

     (3)command下client测试运行：

command下，进入目录E:jboss_tomcatjboss2examplesinterest

java InterestClient

将出现：

Got context

Got reference

Interest on 1000 units, at 10% per period, compounded over 2 periods is: 210.00000000000023

jboss窗口将出现：

[Interest] Someone called `calculateCompoundInterest!'

     (4)web下client通过Servlet测试运行：

http://cgreen:8080/servlet/EJB

将出现：

Interest on 1000 units, at 10% per period, compounded over 2 periods is: 210.00000000000023

jboss窗口将出现：

[Interest] Someone called `calculateCompoundInterest!'

到此已经成功了。


回复如何在Web页上实现文件上传

作者：jspfuns

日期：2000-8-11 18:02:09

public class UploadServlet extends HttpServlet

{

  //default maximum allowable file size is 100k

  static final int MAX_SIZE = 102400;

  //instance variables to store root and success message

  String rootPath, successMessage;

  /**

   * init method is called when servlet is initialized.

   */

  public void init(ServletConfig config) throws ServletException

  {

    super.init(config);

    //get path in which to save file

    rootPath = config.getInitParameter("RootPath");

    if (rootPath == null)

    {

      rootPath = "/";

    }

    /*Get message to show when upload is complete. Used only if

      a success redirect page is not supplied.*/

    successMessage = config.getInitParameter("SuccessMessage");

    if (successMessage == null)

    {

      successMessage = "File upload complete!";

    }

  }

  /**

   * doPost reads the uploaded data from the request and writes

   * it to a file.

   */

  public void doPost(HttpServletRequest request,

    HttpServletResponse response)

  {

    ServletOutputStream out=null;

    DataInputStream in=null;

    FileOutputStream fileOut=null;

    try

    {

      /*set content type of response and get handle to output

        stream in case we are unable to redirect client*/

      response.setContentType("text/plain");

      out = response.getOutputStream();

    }

    catch (IOException e)

    {

      //print error message to standard out

      System.out.println("Error getting output stream.");

      System.out.println("Error description: " + e);

      return;

    }

    try

    {

      //get content type of client request

      String contentType = request.getContentType();

      //make sure content type is multipart/form-data

      if(contentType != null && contentType.indexOf(

        "multipart/form-data") != -1)

      {

        //open input stream from client to capture upload file

        in = new DataInputStream(request.getInputStream());

        //get length of content data

        int formDataLength = request.getContentLength();

        //allocate a byte array to store content data

        byte dataBytes[] = new byte[formDataLength];

        //read file into byte array

        int bytesRead = 0;

        int totalBytesRead = 0;

        int sizeCheck = 0;

        while (totalBytesRead < formDataLength)

        {

          //check for maximum file size violation

          sizeCheck = totalBytesRead + in.available();

          if (sizeCheck > MAX_SIZE)

          {

            out.println("Sorry, file is too large to upload.");

            return;

          }

          bytesRead = in.read(dataBytes, totalBytesRead,

            formDataLength);

          totalBytesRead += bytesRead;

        }

        //create string from byte array for easy manipulation

        String file = new String(dataBytes);

        //since byte array is stored in string, release memory

        dataBytes = null;

        /*get boundary value (boundary is a unique string that

          separates content data)*/

        int lastIndex = contentType.lastIndexOf("=");

        String boundary = contentType.substring(lastIndex+1,

          contentType.length());

        //get Directory web variable from request

        String directory="";

        if (file.indexOf("name=\"Directory\"") > 0)

        {

          directory = file.substring(

            file.indexOf("name=\"Directory\""));

          //remove carriage return

          directory = directory.substring(

            directory.indexOf("\n")+1);

          //remove carriage return

          directory = directory.substring(

            directory.indexOf("\n")+1);

          //get Directory

          directory = directory.substring(0,

            directory.indexOf("\n")-1);

          /*make sure user didn't select a directory higher in

            the directory tree*/

          if (directory.indexOf("..") > 0)

          {

            out.println("Security Error: You can't upload " +

              "to a directory higher in the directory tree.");

            return;

          }

        }

        //get SuccessPage web variable from request

        String successPage="";

        if (file.indexOf("name=\"SuccessPage\"") > 0)

        {

          successPage = file.substring(

            file.indexOf("name=\"SuccessPage\""));

          //remove carriage return

          successPage = successPage.substring(

            successPage.indexOf("\n")+1);

          //remove carriage return

          successPage = successPage.substring(

            successPage.indexOf("\n")+1);

          //get success page

          successPage = successPage.substring(0,

            successPage.indexOf("\n")-1);

        }

        //get OverWrite flag web variable from request

        String overWrite;

        if (file.indexOf("name=\"OverWrite\"") > 0)

        {

          overWrite = file.substring(

            file.indexOf("name=\"OverWrite\""));

          //remove carriage return

          overWrite = overWrite.substring(

            overWrite.indexOf("\n")+1);

          //remove carriage return

          overWrite = overWrite.substring(

            overWrite.indexOf("\n")+1);

          //get overwrite flag

          overWrite = overWrite.substring(0,

            overWrite.indexOf("\n")-1);

        }

        else

        {

          overWrite = "false";

        }

        //get OverWritePage web variable from request

        String overWritePage="";

        if (file.indexOf("name=\"OverWritePage\"") > 0)

        {

          overWritePage = file.substring(

            file.indexOf("name=\"OverWritePage\""));

          //remove carriage return

          overWritePage = overWritePage.substring(

            overWritePage.indexOf("\n")+1);

          //remove carriage return

          overWritePage = overWritePage.substring(

            overWritePage.indexOf("\n")+1);

          //get overwrite page

          overWritePage = overWritePage.substring(0,

            overWritePage.indexOf("\n")-1);

        }

        //get filename of upload file

        String saveFile = file.substring(

          file.indexOf("filename=\"")+10);

        saveFile = saveFile.substring(0,

          saveFile.indexOf("\n"));

        saveFile = saveFile.substring(

          saveFile.lastIndexOf("\\")+1,

          saveFile.indexOf("\""));

        /*remove boundary markers and other multipart/form-data

          tags from beginning of upload file section*/

        int pos; //position in upload file

        //find position of upload file section of request

        pos = file.indexOf("filename=\"");

        //find position of content-disposition line

        pos = file.indexOf("\n",pos)+1;

        //find position of content-type line

        pos = file.indexOf("\n",pos)+1;

        //find position of blank line

        pos = file.indexOf("\n",pos)+1;

        /*find the location of the next boundary marker

          (marking the end of the upload file data)*/

        int boundaryLocation = file.indexOf(boundary,pos)-4;

        //upload file lies between pos and boundaryLocation

        file = file.substring(pos,boundaryLocation);

        //build the full path of the upload file

        String fileName = new String(rootPath + directory +

          saveFile);

        //create File object to check for existence of file

        File checkFile = new File(fileName);

        if (checkFile.exists())

        {

          /*file exists, if OverWrite flag is off, give

            message and abort*/

          if (!overWrite.toLowerCase().equals("true"))

          {

            if (overWritePage.equals(""))

            {

              /*OverWrite HTML page URL not received, respond

                with generic message*/

              out.println("Sorry, file already exists.");

            }

            else

            {

              //redirect client to OverWrite HTML page

              response.sendRedirect(overWritePage);

            }

            return;

          }

        }

        /*create File object to check for existence of

          Directory*/

        File fileDir = new File(rootPath + directory);

        if (!fileDir.exists())

        {

          //Directory doesn't exist, create it

          fileDir.mkdirs();

        }

        //instantiate file output stream

        fileOut = new FileOutputStream(fileName);

        //write the string to the file as a byte array

        fileOut.write(file.getBytes(),0,file.length());

        if (successPage.equals(""))

        {

          /*success HTML page URL not received, respond with

            generic success message*/

          out.println(successMessage);

          out.println("File written to: " + fileName);

        }

        else

        {

          //redirect client to success HTML page

          response.sendRedirect(successPage);

        }

      }

      else //request is not multipart/form-data

      {

        //send error message to client

        out.println("Request not multipart/form-data.");

      }

    }

    catch(Exception e)

    {

      try

      {

        //print error message to standard out

        System.out.println("Error in doPost: " + e);

        //send error message to client

        out.println("An unexpected error has occurred.");

        out.println("Error description: " + e);

      }

      catch (Exception f) {}

    }

    finally

    {

      try

      {

        fileOut.close(); //close file output stream

      }

      catch (Exception f) {}

      try

      {

        in.close(); //close input stream from client

      }

      catch (Exception f) {}

      try

      {

        out.close(); //close output stream to client

      }

      catch (Exception f) {}

    }

  }

}


回复谁能讲一讲setproperty(name=)的用法或介绍相关资料谢了!!

作者：msdn

日期：00-6-9 上午 11:13:08

<jsp:setProperty>

Sets a property value or values in a Bean.

JSP Syntax

<jsp:setProperty

        name="beanInstanceName"

        {

            property= "*"   |

            property="propertyName" [ param="parameterName" ]   |

            property="propertyName" value="{string | <%= expression %>}"

        }

/>

Examples

<jsp:setProperty name="mybean" property="*" />

<jsp:setProperty name="mybean" property="username" />

<jsp:setProperty name="mybean" property="username" value="Steve" />

Description

The <jsp:setProperty> element sets the value of one or more properties in a Bean, using the Bean's setter methods. You must declare the Bean with <jsp:useBean> before you set a property value with <jsp:setProperty>. Because <jsp:useBean> and <jsp:setProperty> work together, the Bean instance names they use must match (that is, the value of name in <jsp:setProperty> and the value of id in <jsp:useBean> must be the same).

You can use <jsp:setProperty> to set property values in several ways:

By passing all of the values the user enters (stored as parameters in the request object) to matching properties in the Bean

By passing a specific value the user enters to a specific property in the Bean

By setting a Bean property to a value you specify as either a String or an expression that is evaluated at runtime

Each method of setting property values has its own syntax, as described in the next section.

Attributes and Usage

name="beanInstanceName"

The name of an instance of a Bean that has already been created or located with a <jsp:useBean> element. The value of name must match the value of id in <jsp:useBean>. The <jsp:useBean> element must appear before <jsp:seProperty> in the JSP file.

property="*"

Stores all of the values the user enters in the viewable JSP page (called request parameters) in matching Bean properties. The names of the properties in the Bean must match the names of the request parameters, which are usually the elements of an HTML form. A Bean property is usually defined by a variable declaration with matching getter and setter methods (for more information, see the JavaBeans API Specification available at http://java.sun.com/beans .

The values of the request parameters sent from the client to the server are always of type String. The String values are converted to other data types so they can be stored in Bean properties. The allowed Bean property types and their conversion methods are shown in TABLE 1-1.

How <jsp:setProperty> Converts Strings to Other Values

Property Type

String Is Converted Using

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)

You can also use <jsp:setProperty> to set the value of an indexed property in a Bean. The indexed property must be an array of one of the data types shown in TABLE 1-1. The array elements are converted using the conversion methods shown in the table.

If a request parameter has an empty or null value, the corresponding Bean property is not set. Likewise, if the Bean has a property that does not have a matching request parameter, the property value is not set.

property="propertyName" [ param="parameterName" ]

Sets one Bean property to the value of one request parameter. In the syntax, property specifies the name of the Bean property and param specifies the name of the request parameter by which data is being sent from the client to the server.

If the Bean property and the request parameter have different names, you must specify both property and param. If they have the same name, you can specify property and omit param.

If a parameter has an empty or null value, the corresponding Bean property is not set.

property="propertyName" value="{string | <%= expression %>}"

Sets one Bean property to a specific value. The value can be a String or an expression that is evaluated at runtime. If the value is a String, it is converted to the Bean property's data type according to the conversion rules shown above in TABLE 1-1. If it is an expression, its value must have a data type that matches the the data type of the value of the expression must match the data type of the Bean property.

If the parameter has an empty or null value, the corresponding Bean property is not set. You cannot use both the param and value attributes in a <jsp:setProperty> element.

See Also

<jsp:useBean>

<jsp:getProperty>

Tips

When you use property="*", the Bean properties are not necessarily set in the order in which they appear in the HTML form or the Bean.

In Sun's JSP 1.0 or JSP 1.1 Tomcat, the Bean properties are set in the order in which they are presented to the JSP container by the Beans introspector. If the order in which the properties are set is important to how your Bean works, use the syntax form property="propertyName" [ param="parameterName" ]. Better yet, rewrite your Bean so that the order of setting properties is not important.


回复谁有用java mail 收 html 信件的程序呀？

作者：easyinfonet

email: easyinfonet@sina.com

日期：2001-6-5 16:01:04

在JSP中使用JavaMail（上）

内容：

 什么是JavaMail

 第一部分：HTML表格

 划分组件

 用来发送email的HTML源代码

你希望在jsp中建立一个邮件发送收取工具吗？下面将介绍的就是在jsp中建立一个邮件发送收取工具。在这篇文章中你可以了解到JavaMail API的一些要点以及如何在JSP中使用它。本文中还包括了在JSP中使用JavaMail的实例。JavaMail是JSP应用软件中相当强大的API。

阅读这篇文章需要对JSP、JavaBeans和JavaMail有一定的初步了解。当然，有关于JavaMail的知识你可以通过阅读这篇文章来获得。如果你对于以上三项一无所知，但你所使用的服务器支持JSP和JavaMail的话，你可以仅仅通过复制/粘贴来使用它们。

什么是JavaMail

JavaMail是Sun发布的用来处理email的API。它可以方便地执行一些常用的邮件传输。

虽然JavaMail是Sun的API之一，但它目前还没有被加在标准的java开发工具包中（Java Development Kit），这就意味着你在使用前必须另外下载JavaMail文件。除此以外，你还需要有Sun的JavaBeans Activation Framework （JAF）。JavaBeans Activation Framework的运行很复杂，在这里简单的说就是JavaMail的运行必须得依赖于它的支持。在Windows 2000下使用需要指定这些文件的路径，在其它的操作系统上也类似。

接下来要讲解的是这篇指南的最难理解的部分。

这篇指南包括三部分：HTML表格、关于JavaMail、JavaMail和JSP的结合。

第一部分：HTML表格

第一部分提供了一个最基本的基于HTML的email发送收取程序的例子。第二部分则讲述JavaMail的工作原理。第三部分则介绍将JavaMail加入JSP，创建一个基本的email发送收取程序。

划分组件

JSP最重要的特性是能将整个网页划分成一些细小的组件。这里使用的组件包括：

●一个用来将email的信息发送给JSP的HTML表格；

●一个JSP页面用来处理和发送信件。

第一步，就是创建一个HTML表格用来给JSP页面发送信息。你可以将以下的HTML代码复制到你的电脑上：

用来发送email的HTML源代码

     <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>

以上这段程序将创建一个包含email基本信息（例如：收件地址、发送地址、主题和内容）的文件。当然你可以根据你自己的需要来决定这个文件包含那些信息。

这个HTML文件的使用有两点要求：第一点是生成的文件必须发送给接下来将介绍的程序使用。在这个例子中就是sendmail.jsp，但在你使用时，必须用这个文件在系统里的URL来代替它；第二点是必须有空间来允许用户发送email。

第二部分将对JavaMail的特征进行一些分析，为在第三部分创建JSP程序作准备。所以接下来我们将学习JavaMail

在JSP中使用JavaMail（下）

内容：

 第二部分：关于JavaMail

 第三部分：JavaMail和JSP的结合

第二部分：关于JavaMail

文档的使用

下载的JavaMail API中带的文档是很有用的。你可以在JavaMail下的/docs/javadocs/index.html找到它。第二部分主要将分析邮件程序的组件。你可以通过阅读文档来获得更多这方面的信息。

组件发送邮件需要使用JavaMail，它使对邮件的操作变得简单易用。

属性对象

JavaMail需要创建一个格式为"mail.smtp.host"的文件用来发送信息。

Properties props = new Properties ();

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

所有的基于JavaMail的程序都至少需要一个或全部的对话目标。

Session sendMailSession;

sendMailSession = Session.getInstance(props, null);

传输

邮件的传输只有送出或受到两种状态。JavaMail将这两种不同状态描述为传输和储存。传输将送出邮件，而储存将收取邮件。

Transport transport;

transport = sendMailSession.getTransport("smtp");

使用JavaMail能为我们节约大量的时间。JavaMail能代替所有的SMTP的工作。

注意：JavaMail并不能完全的支持所有的邮件发送收取工作。它目前仅支持IMAP、SMTP和POP3，除此以外你只有等待新的JavaMail版本或自己开发协议。

信息对象

信息对象将把你所发送的邮件真实的反映出来。

Message newMessage = new MimeMessage(sendMailSession);

这就是我们所需要的全部四个对象。下一步将是如何将对象加入到JSP中。

第三部分：JavaMail和JSP的结合

创建JSP

下面我们将开始将他们结合在一起。最重要的一点是要确认根据页面指示分类。还要记得在邮件上标注java.util.date。

     <%@ page

       import= " javax.mail.*, javax.mail.internet.*, javax.activation.*, java.util.*"

     %>

其次，创建邮件发送的确认信息。确认信息可以是任意的，一般常用"你的邮件已经发送出去了（Your mail has been sent）。"

信息是如何创建和发送的

我们在第二部分里已经讨论过信息对象的创建。我们下面将对信息进行操作。这就和设置信息对象的属性一样简单。你可以通过下面的程序来实现这项操作。

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"));

现在将开始发送信息。通过JavaMail来实现它非常简单。

transport.send(newMessage);

将所有的组件结合在一起

现在所有的组件都已经齐全了。现在将它们都放在JSP里面。要注意每一个错误信息，并将它反馈给用户。代码如下，你可以通过复制它们直接使用：

    Sample JSP email Utility Using JavaMail

    <%@ 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>


回复有没有现成的class实现crypt算法的，或者源代码也可以的。

此电子文档属C#乐园QQ群3256045整理。
回复有没有现成的class实现crypt算法的，或者源代码也可以的。

上一页 目录 下一页

回复有没有现成的class实现crypt算法的，或者源代码也可以的。

作者：whitefox

email: henry.jiang@corp.elong.com

日期：2000-12-13 14:24:37

package com.elong.application.encrypt;

public class Crypt

{

    static int sub[] = new int[48];

// TABLES

/* Expansion table (32 to 48) */

int E_p[] = {

       32, 1, 2, 3, 4, 5, 4, 5, 6, 7, 8, 9,

        8, 9,10,11,12,13,12,13,14,15,16,17,

       16,17,18,19,20,21,20,21,22,23,24,25,

       24,25,26,27,28,29,28,29,30,31,32, 1

};

/* Permutation Choice 1 for subkey generation (64/56 to 56) */

int PC1_p[] = {

       57,49,41,33,25,17, 9, 1,58,50,42,34,26,18,

       10, 2,59,51,43,35,27,19,11, 3,60,52,44,36,

       63,55,47,39,31,23,15, 7,62,54,46,38,30,22,

       14, 6,61,53,45,37,29,21,13, 5,28,20,12, 4

};

/* Permutation Choice 2 for subkey generation (56 to 48) */

int PC2_p[] = {

       14,17,11,24, 1, 5, 3,28,15, 6,21,10,

       23,19,12, 4,26, 8,16, 7,27,20,13, 2,

       41,52,31,37,47,55,30,40,51,45,33,48,

       44,49,39,56,34,53,46,42,50,36,29,32

};

/* Number of rotations for the iteration of key scheduling */

/* The concept of a table here doesn't fit our behavioral model */

/* This will be logic in our final design */

int keyrots[] = {1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1};

/* Selection blocks

 * There are 8 sblocks, each of which is referenced by a 2 bit value

 * which picks the row, and a 4 bit value which picks the column

 * This number is then the 4 bit output for that select block

 */

int sblocks[][][] = {

        { { 14,  4, 13,  1,  2, 15, 11,  8,  3, 10,  6, 12,  5,  9,  0,  7 },

          {  0, 15,  7,  4, 14,  2, 13,  1, 10,  6, 12, 11,  9,  5,  3,  8 },

          {  4,  1, 14,  8, 13,  6,  2, 11, 15, 12,  9,  7,  3, 10,  5,  0 },

          { 15, 12,  8,  2,  4,  9,  1,  7,  5, 11,  3, 14, 10,  0,  6, 13 }

        },

        { { 15,  1,  8, 14,  6, 11,  3,  4,  9,  7,  2, 13, 12,  0,  5, 10 },

          {  3, 13,  4,  7, 15,  2,  8, 14, 12,  0,  1, 10,  6,  9, 11,  5 },

          {  0, 14,  7, 11, 10,  4, 13,  1,  5,  8, 12,  6,  9,  3,  2, 15 },

          { 13,  8, 10,  1,  3, 15,  4,  2, 11,  6,  7, 12,  0,  5, 14,  9 }

        },

        { { 10,  0,  9, 14,  6,  3, 15,  5,  1, 13, 12,  7, 11,  4,  2,  8 },

          { 13,  7,  0,  9,  3,  4,  6, 10,  2,  8,  5, 14, 12, 11, 15,  1 },

          { 13,  6,  4,  9,  8, 15,  3,  0, 11,  1,  2, 12,  5, 10, 14,  7 },

          {  1, 10, 13,  0,  6,  9,  8,  7,  4, 15, 14,  3, 11,  5,  2, 12 }

        },

        { {  7,  13, 14,  3,  0,  6,  9, 10,  1,  2,  8,  5, 11, 12,  4, 15 },

          { 13,  8,  11,  5,  6, 15,  0,  3,  4,  7,  2, 12,  1, 10, 14,  9 },

          { 10,  6,   9,  0, 12, 11,  7, 13, 15,  1,  3, 14,  5,  2,  8,  4 },

          {  3, 15,   0,  6, 10,  1, 13,  8,  9,  4,  5, 11, 12,  7,  2, 14 }

        },

        { {  2, 12,   4,  1,  7, 10, 11,  6,  8,  5,  3, 15, 13,  0, 14,  9 },

          { 14, 11,   2, 12,  4,  7, 13,  1,  5,  0, 15, 10,  3,  9,  8,  6 },

          {  4,  2,    1, 11, 10, 13,  7,  8, 15, 9, 12,  5,  6,  3,  0, 14 },

          { 11,  8,  12,  7,  1, 14,  2, 13,  6, 15,  0,  9, 10,  4,  5,  3 }

        },

        { { 12,  1, 10, 15,  9,  2,  6,  8,  0, 13,  3,  4, 14,  7,  5, 11 },

          { 10, 15,  4,  2,  7, 12,  9,  5,  6,  1, 13, 14,  0, 11,  3,  8 },

          {  9, 14, 15,  5,  2,  8, 12,  3,  7,  0,  4, 10,  1, 13, 11,  6 },

          {  4,  3,  2, 12,  9,  5, 15, 10, 11, 14,  1,  7,  6,  0,  8, 13 }

        },

        { {  4, 11,  2, 14, 15,  0,  8, 13,  3, 12,  9,  7,  5, 10,  6,  1 },

          { 13,  0, 11,  7,  4,  9,  1, 10, 14,  3,  5, 12,  2, 15,  8,  6 },

          {  1,  4, 11, 13, 12,  3,  7, 14, 10, 15,  6,  8,  0,  5,  9,  2 },

          {  6, 11, 13,  8,  1,  4, 10,  7,  9,  5,  0, 15, 14,  2,  3, 12 }

        },

        { { 13,  2,  8,  4,  6, 15, 11,  1, 10,  9,  3, 14,  5,  0, 12,  7 },

          {  1, 15, 13,  8, 10,  3,  7,  4, 12,  5,  6, 11,  0, 14,  9,  2 },

          {  7, 11, 4,   1,  9, 12, 14,  2,  0,  6, 10, 13, 15,  3,  5,  8 },

          {  2,  1, 14,  7,  4, 10,  8, 13, 15, 12,  9,  0,  3,  5,  6, 11 }

        }

      };

/* Permutation P for after sblocks */

int P_p[] = {

       16, 7,20,21,29,12,28,17, 1,15,23,26, 5,18,31,10,

        2, 8,24,14,32,27, 3, 9,19,13,30, 6,22,11, 4,25

};

/* Inverse permutation of IP for end

 * Temporary - the true behavior will be implemented in a shift out register

 * (Look at the pattern obvious in an 8x8 layout)

 */

int IPinv_p[] = {

    40,  8, 48, 16, 56, 24, 64, 32,

    39,  7, 47, 15, 55, 23, 63, 31,

    38,  6, 46, 14, 54, 22, 62, 30,

    37,  5, 45, 13, 53, 21, 61, 29,

    36,  4, 44, 12, 52, 20, 60, 28,

    35,  3, 43, 11, 51, 19, 59, 27,

    34,  2, 42, 10, 50, 18, 58, 26,

    33,  1, 41,  9, 49, 17, 57, 25

};

// CODE

private void pr_bits (int[] s, int amt)

{

    amt /= 8;

    for (int i=0; i<amt; i++)

    {

        int x=0;

        for (int j=0; j<8; j++)

             x |= s[i*8+j] << (7-j);

        Integer integ = new Integer(x);

        System.out.print(integ.toString(x,16)+" ");

    }

    System.out.println();

};

private void permute (int[] by, int amt, int[] in, int[] out)

{

  for (; --amt>=0; )

    out[amt] = in[by[amt]-1];

};

private void do_sblocks (int[] in, int[] out)

{

    for (int i=0; i<8; i++)

    {

      int val = sblocks[i]

                       [in[i*6] << 1 | in[i*6+5]]

                       [in[i*6+1] << 3 |

                        in[i*6+2] << 2 |

                        in[i*6+3] << 1 |

                        in[i*6+4] << 0 ];

      out[i*4+0] = val >> 3 & 1;

      out[i*4+1] = val >> 2 & 1;

      out[i*4+2] = val >> 1 & 1;

      out[i*4+3] = val >> 0 & 1;

    }

};

private int ascii_to_bin (char c)

{

  if (c>='a') return(c-59);

  if (c>='A') return(c-53);

  return(c-'.');

};

private  char bin_to_ascii (int c)

{

  if (c>=38) return (char)(c-38+'a');

  if (c>=12) return (char)(c-12+'A');

  return (char)(c+'.');

};

private void load_salt (int[] saltmask, char[] salt)

{

    int tot = ascii_to_bin (salt[0]) | (ascii_to_bin (salt[1]) << 6);

    for (int i=0; i < 12; i++)

      saltmask[i] = tot >> i & 1;

};

private void do_salt (int[] bits, int[] saltmask)

{

    for (int i=0; i<12; i++)

        if (saltmask[i]!=0)

        {

          int t = bits[i];

          bits[i] = bits[24+i];

          bits[24+i] = t;

        }

};

private void load_key (int[] ikey, char[] password)

{

    int tmp[] = new int[64];

    for (int i=0; i<8; i++)

      for (int j=0; j<8; j++)

        tmp[i*8+j] = (password[i] >> (6-j)) & 1;

    permute(PC1_p, 56, tmp, ikey);

};

private void subkey (int[] ikey, int iter)

{

    int rots=keyrots[iter];

    int tmp0l=ikey[0];

    int tmp1l=ikey[1];

    int tmp0r=ikey[28];

    int tmp1r=ikey[29];

    for (int i=0; i<28-rots; i++)

    {

      ikey[i] = ikey[i+rots];

      ikey[28+i] = ikey[28+i+rots];

    }

    if(rots==2)

    {

      ikey[26] = tmp0l;

      ikey[27] = tmp1l;

      ikey[54] = tmp0r;

      ikey[55] = tmp1r;

    }

    else

    {

      ikey[27] = tmp0l;

      ikey[55] = tmp0r;

    }

    permute (PC2_p,48,ikey,sub);

};

private void xor(int[] src1, int[] src2, int index, int num)

{

    for (int i=0; i<num; i++)

      src1[i] = (src1[i] ^ src2[index+i]) & 1;

};

private void print_bits(int[] s, int amt )

{

    for (int i=0; i<amt; i++)

      System.out.print (""+s[i]);

    System.out.println ("");

};

private void do_f(int[] in,int index, int[] out, int iter, int[] ikey, int[] saltmask)

{

  int tmp48[] = new int[48], tmp32[] = new int[32], skey[];

    for (int i=0; i<32; i++)

      tmp32[i] = in[i+index];

    permute (E_p,48,tmp32, tmp48);

    do_salt (tmp48, saltmask);

    subkey (ikey,iter);

    skey = sub;

    xor (tmp48,skey,0,48);   // Goed

    do_sblocks (tmp48, tmp32);

    permute (P_p, 32, tmp32,out);

};

private char[] mycrypt (char[] password, char[] salt)

{

    int bits[] = new int[64];

    int outl[] = new int[32];

    int outr[] = new int[32];

    int done[] = new int[66]; // In c-code array-size was 64 !?!

    int ikey[] = new int[56];

    int saltmask[]=new int[12];

    char[] answer= new char[14];

    for (int i=0; i<64; i++)

      bits[i]=0;

    load_key(ikey,password);

    load_salt(saltmask,salt);

    for (int dess=0; dess<25; dess++)

    {

        for (int iters=0; iters<16; iters+=2)

        {

            do_f(bits,32,outl,iters,ikey,saltmask);

            xor(outl,bits,0,32);

            do_f(outl,0,outr,iters+1,ikey,saltmask);

            xor(outr,bits,32,32);

            if (iters != 14)

                for (int i=0; i<32; i++)

                {

                  bits[i] = outl[i];

                  bits[i+32] = outr[i];

                }

            else

                for (int i=0; i<32; i++)

                {

                  bits[i] = outr[i];

                  bits[i+32] = outl[i];

                }

        }

    }

    permute(IPinv_p, 64, bits, done);

    answer[0] = (char)salt[0];

    answer[1] = (char)salt[1];

    for (int i=0; i<11; i++)

    {

        char c=0;

        for (int j=0; j<6; j++)

          c |= done[6*i+j] << (5-j);

        answer[i+2] = bin_to_ascii (c);

    }

    return answer;

};

/**

 * This method decodes the given password

 * Encryption is based on the one way DES encryption

 * @return The encrypted password

 * @param passwd is the password to encrypt

 * @param s is salt

 */

public String decode (String passwd, String s)

{

    char[] password = new char[9];

    char[] salt = new char[3];

    String uitkomst;

    passwd.getChars (0,passwd.length()>=8 ? 8 : passwd.length(),password,0);

    s.getChars (0,2,salt,0);

    uitkomst = new String ().valueOf (mycrypt (password,salt));

    return (uitkomst.substring (0,13));

}

};


回复在jsp中作HTTP认证的方法,（希望版主可以把它贴到专题文章中）

作者：unixboy_xu

email: unixboy@yeah.net

日期：2000-12-18 15:36:40

最近研究了jsp中作HTTP认证的问题，它的工作方式如下：

1、server发送一个要求认证代码401和一个头信息WWW-authenticate，激发browser弹出一个认证窗口

2、server取得browser送来的认证头"Authorization",它是加密的了，要用Base64方法解密，取得明文的用户名和密码

3、检查用户名和密码，根据结果传送不同的页面

以下是jsp的片断，你也可以把它做成include文件。和Base64的加解密的class源码。

如有兴趣可与我联系：unixboy@yeah.net

<jsp:useBean id="base64" scope="page" class="Base64"/>

<%

if(request.getHeader("Authorization")==null){

   response.setStatus(401);

   response.setHeader("WWW-authenticate", "Basic realm=\"unixboy.com\"");

}else{

   String encoded=(request.getHeader("Authorization"));

   String tmp=encoded.substring(6);

   String up=Base64.decode(tmp);

   String user="";

   String password="";

   if(up!=null){

        user=up.substring(0,up.indexOf(":"));

    password=up.substring(up.indexOf(":")+1);

   }

   if(user.equals("unixboy")&&password.equals("123456")){

        //认证成功

   }else{

        //认证失败

   }

}

%>

//消息加解密class

public class Base64

{

        /** decode a Base 64 encoded String.

          * <p><h4>String to byte conversion</h4>

          * This method uses a naive String to byte interpretation, it simply gets each

          * char of the String and calls it a byte.</p>

          * <p>Since we should be dealing with Base64 encoded Strings that is a reasonable

          * assumption.</p>

          * <p><h4>End of data</h4>

          * We don't try to stop the converion when we find the "=" end of data padding char.

          * We simply add zero bytes to the unencode buffer.</p>

        */

        public static String decode(String encoded)

        {

                StringBuffer sb=new StringBuffer();

                int maxturns;

                //work out how long to loop for.

                if(encoded.length()%3==0)

                maxturns=encoded.length();

                else

                maxturns=encoded.length()+(3-(encoded.length()%3));

                //tells us whether to include the char in the unencode

                boolean skip;

                //the unencode buffer

                byte[] unenc=new byte[4];

                byte b;

                for(int i=0,j=0; i<maxturns; i++)

                {

                        skip=false;

                        //get the byte to convert or 0

                        if(i<encoded.length())

                        b=(byte)encoded.charAt(i);

                        else

                        b=0;

                        //test and convert first capital letters, lowercase, digits then '+' and '/'

                        if(b>=65 && b<91)

                        unenc[j]=(byte)(b-65);

                        else if(b>=97 && b<123)

                        unenc[j]=(byte)(b-71);

                        else if(b>=48 && b<58)

                        unenc[j]=(byte)(b+4);

                        else if(b=='+')

                        unenc[j]=62;

                        else if(b=='/')

                        unenc[j]=63;

                        //if we find "=" then data has finished, we're not really dealing with this now

                        else if(b=='=')

                        unenc[j]=0;

                        else

                        {

                                char c=(char)b;

                                if(c=='\n' || c=='\r' || c==' ' || c=='\t')

                                skip=true;

                                else

                                //could throw an exception here? it's input we don't understand.

                                ;

                        }

                        //once the array has boiled convert the bytes back into chars

                        if(!skip && ++j==4)

                        {

                                //shift the 6 bit bytes into a single 4 octet word

                                int res=(unenc[0] << 18)+(unenc[1] << 12)+(unenc[2] << 6)+unenc[3];

                                byte c;

                                int k=16;

                                //shift each octet down to read it as char and add to StringBuffer

                                while(k>=0)

                                {

                                        c=(byte)(res >> k);

                                        if ( c > 0 )

                                        sb.append((char)c);

                                        k-=8;

                                }

                                //reset j and the unencode buffer

                                j=0;

                                unenc[0]=0;unenc[1]=0;unenc[2]=0;unenc[3]=0;

                        }

                }

                return sb.toString();

        }

        /** encode plaintext data to a base 64 string

          * @param plain the text to convert. If plain is longer than 76 characters this method

          *             returns null (see RFC2045).

          * @return the encoded text (or null if string was longer than 76 chars).

        */

        public static String encode(String plain)

        {

                if(plain.length()>76)

                return null;

                int maxturns;

                StringBuffer sb=new StringBuffer();

                //the encode buffer

                byte[] enc=new byte[3];

                boolean end=false;

                for(int i=0,j=0; !end; i++)

                {

                        char _ch=plain.charAt(i);

                        if(i==plain.length()-1)

                        end=true;

                        enc[j++]=(byte)plain.charAt(i);

                        if(j==3 || end)

                        {

                                int res;

                                //this is a bit inefficient at the end point

                                //worth it for the small decrease in code size?

                                res=(enc[0] << 16)+(enc[1] << 8)+enc[2];

                                int b;

                                int lowestbit=18-(j*6);

                                for(int toshift=18; toshift>=lowestbit; toshift-=6)

                                {

                                        b=res >>> toshift;

                                        b&=63;

                                        if(b>=0 && b<26)

                                        sb.append((char)(b+65));

                                        if(b>=26 && b<52)

                                        sb.append((char)(b+71));

                                        if(b>=52 && b<62)

                                        sb.append((char)(b-4));

                                        if(b==62)

                                        sb.append('+');

                                        if(b==63)

                                        sb.append('/');

                                        if(sb.length()%76==0)

                                        sb.append('\n');

                                }

                                //now set the end chars to be pad character if there

                                //was less than integral input (ie: less than 24 bits)

                                if(end)

                                {

                                        if(j==1)

                                        sb.append("==");

                                        if(j==2)

                                        sb.append('=');

                                }

                                enc[0]=0; enc[1]=0; enc[2]=0;

                                j=0;

                        }

                }

                return sb.toString();

        }

}


基础知识 -- JavaDoc用法

作者：罗亭

email: tingluo@263.net

日期：2001-7-24 11:43:07

/** 很基础的东东，但一定是很好的东东 */

C:\JBuilder4\jdk1.3\bin>javadoc

用法：javadoc [options] [packagenames] [sourcefiles] [@files]

-overview <file>          读取 HTML 格式的概述文档

-public                   仅显示 public 类和成员

-protected                显示 protected/public 类和成员（缺省）

-package                  显示 package/protected/public 类和成员

-private                  显示所有类和成员

-help                     显示命令行选项

-doclet <class>           通过候选 doclet 生成输出

-docletpath <path>        指定 doclet 类文件的查找位置

-1.1                      利用 JDK 1.1 模仿 doclet 生成输出

-sourcepath <pathlist>    指定源文件的查找位置

-classpath <pathlist>     指定用户类文件的查找位置

-bootclasspath <pathlist> 覆盖自举类加载器所加载的

                          类文件的位置

-extdirs <dirlist>        覆盖已安装的扩展的位置

-verbose                 有关 Javadoc 所做工作的输出信息

-locale <name>            所用的 Locale，例如 en_US 或 en_US_WIN

-encoding <name>          源文件编码名称

-J<flag>                  将 <flag> 直接传给运行时系统

由标准 doclet 提供：

-d <directory>            输出文件的目标目录

-use                      创建类和包的用法页

-version                  包含 @version 段

-author                   包含 @author 段

-splitindex               将索引分为每个字母对应一个文件

-windowtitle <text>       文档的浏览器窗口标题

-doctitle <html-code>     包含包索引页（首页）的标题

-header <html-code>       包含每一页的页眉文本

-footer <html-code>       包含每一页的页脚文本

-bottom <html-code>       包含每一页的页底文本

-link <url>               创建到 javadoc 输出的链接（位于 <url>）

-linkoffline <url> <url2> 利用位于 <url2> 的包列表链接到位于 <url> 的文档

-group <name> <p1>:<p2>.. 将概览页中指定的包分组

-nodeprecated             不包含 @deprecated 信息

-nodeprecatedlist         不生成不鼓励使用的列表

-notree                   不生成类层次

-noindex                  不生成索引

-nohelp                   不生成帮助链接

-nonavbar                 不生成导航栏

-helpfile <file>          包含帮助链接功能链接到目标的文件

-stylesheetfile <path>    改变所生成文档的样式的文件

-docencoding <name>       输出编码名称


基于MySQL的高性能数据库应用开发(使用C＋＋操作MYSQL)

作者：sonymusic

email: sonymusic@china.com

日期：2001-2-16 22:35:32

[h1]基于MySQL的高性能数据库应用开发[/h1]

[b]一、高性能数据库的选择[/b]

---- 在数据库的应用开发中，常常会遇到性能和代价的之间矛盾。以作者在开发股市行

情查询和交易系统中遇到的问题为例，要在实时记录1000多只股票每分钟更新一次的行

情数据的同时，响应大量并发用户的数据查询请求。考虑到性价比和易维护性，系统又

要求在基于PC服务器，Windows NT平台的软硬件环境下实现。开始，我们采用了MS SQL

 Server 6.5 作为数据库系统，用Visual C++ 6.0开发了访问数据库的前端，应用ODBC

数据接口，在进行了大量的数据库配置和程序优化后，发现仍不能满足性能要求。后采

用SQL Server的DB-Library接口，绕过了ODBC解释层，可以每秒更新行情数据30次，同

时支持20－30个左右的并发用户进行行情查询，基本满足要求（单台PC服务器，单PII3

50 CPU，内存128M，SCSI硬盘）。有没有可能进一步提高系统的性能和负载能力呢？经

过分析，数据库服务器是系统的瓶颈。当然，可以采用UNIX服务器＋大型数据库的系统

平台，但其开发、运行、维护的费用比微机＋Windows NT平台的费用高出数倍。我们在

其它一些系统的开发中，也经常遇到这样的矛盾。如何在微机平台上建立大容量、高效

率、易维护、高性价比的数据库系统呢？

---- 考察国内基于微机平台的数据库应用系统，典型的如网易的分布式邮件系统，采用

了FreeBSD+MySQL的平台，其容量、负载能力和响应速度都很优秀。作者查阅了MySQL的

相关文档，发现MySQL是GNU软件（即OpenSource自由软件）中非常优秀的数据库系统，

它完全符合SQL92 （Entry level）和 ODBC（ level 0-2）规范，在符合POSIX规范的操

作系统上实现了非常高效的关系型数据库管理系统。根据MySQL提供的文档，它的数据操

作堪称所有数据库中最高效的，Benchmark如下表：

Reading 2000000 rows by index

Database Seconds

mysql 367

mysql_odbc 464

db2_odbc 1206

informix_odbc 121126

ms-sql_odbc 1634

oracle_odbc 20800

solid_odbc 877

sybase_odbc 17614

Inserting (350768) rows

Database Seconds

mysql 381

mysql_odbc 619

db2_odbc 3460

informix_odbc 2692

ms-sql_odbc 4012

oracle_odbc 11291

solid_odbc 1801

sybase_odbc 4802

(run on the same NT 4.0 machine)

---- 从MySQL的Benchmark中可以看到，MySQL的性能非常出众（当然，测试的MySQL系统

可能作了优化，被测数据可能是针对MySQL选择的），而且MySQL提供了对Windows NT的

支持。Windows NT＋MySQL能否成为构建高性能数据库应用的理想选择呢？作者用MySQL

的数据接口改写了程序，经过一段时间的运行，证明MySQL确实是高效而稳定的数据库，

非常适合构建大容量、高效率、易维护、高性价比的数据库应用系统。现将MySQL的安装

、运行、开发的心得与大家共享。

[b]二、MySQL的安装和运行[/b]

---- 首先从http://www.mysql.com/

irror/mysql/）下载MySQL的执行代码及源代码。注意，Windows NT用户要选择NT下的执

行代码，我下载的是mysql-shareware-3.22.32-win.zip。解包后执行Setup，按屏幕提

示即可完成安装。

---- 拷贝MySQL根目录（c:\mysql）下的my-example.cnf到c:\my.cnf，按文件中的提示

编辑my.cnf（如果MySQL的根目录是c:\mysql，可暂不改动my.cnf）。在NT的控制台窗口

中，进入MySQL的执行目录（c:\mysql\bin），执行

---- C:\mysql\bin\mysqld-shareware --standalone

---- 则MySQL的数据库引擎启动。打开另一NT控制台窗口，执行

---- C:\mysql\bin\mysql mysql

---- 建立数据库连接，出现“mysql>”提示符后，执行

---- mysql > DELETE FROM user WHERE Host='localhost' AND User='';

---- mysql > QUIT

---- 删除所有的非授权用户。

---- 然后执行

---- C:\mysql\bin\mysqladmin reload

---- C:\mysql\bin\mysqladmin -u root password your_password

---- 其中，your_password是你选择的数据库管理员的口令，必须妥善保管。

---- 如果要每次都以管理员身份连接数据库，则编辑c:\my.cnf，在[client]段中加入

：

user=root

password= your_password

如果要停止MySQL的数据库引擎，可以执行

C:\mysql\bin\mysqladmin -u=root -p shutdown

按提示输入管理员口令后，MySQL的数据库引擎停止。

[b]三、MySQL客户端应用的开发[/b]

---- MySQL提供了丰富的数据接口API，包括C、C++、Perl、PHP、Python、TCL等API和

JDBC，ODBC接口。出于性能考虑，我们采用了MySQL的C API进行开发。现以Visual C++

环境为例，作一简单介绍。

---- 新建一Win32 Console Application的Project，把“c:\mysql\include”添加到编

译选项的包含路径中（在Project Options中加入 /I "d:\mysql\include"）。新建一m

ain.c文件，主体如下：

#include <windows.h>

#include <stdio.h>

#include <string.h>

#include <mysql.h>

int main( int argc, char * argv[] )

{

   char   szTargetDSN[] = "test";

  char   szSqlText[500]="";

  char   aszFlds[ 25 ][ 25 ];

  MYSQL  * myData ;

  MYSQL_RES * res ;

  MYSQL_FIELD * fd ;

  MYSQL_ROW row ;

  int   i,j,k;

  BOOL  bCreate ＝ TRUE;

  if ( (myData = mysql_init((MYSQL*) 0))

   //初始化数据结构

   && mysql_real_connect( myData, NULL,

   //连接数据库

  "root", " your_password ", szTargetDSN,

  MYSQL_PORT, NULL, 0 ) )

    {

 if(bCreate)

 {

 sprintf(szSqlText, //构造SQL语句

 "create table mytable "

//新建一张表

 "(time datetime, s1 char(6), "

 "s2 char(11), s3 int, s4 int)");

 if (mysql_query( myData, szSqlText))

 //执行SQL语句

 {//执行SQL语句出错

 ErrLog( "Can't create table") ;

 mysql_close( myData ) ;

 return FALSE ;

  }

  ｝

 sprintf(szSqlText,

 "insert into mytable "

     //向表中插入数据

 "values('2000-3-10 21:01:30',"

  //注意时间的格式

 "'Test','MySQLTest',2000,3)");

 if (mysql_query( myData, szSqlText))

   {//执行SQL语句出错

 ErrLog( "Can't insert data to table") ;

mysql_close( myData ) ;

 return FALSE ;

   }

  sprintf(szSqlText, "select * from mytable ");

   if (mysql_query( myData, szSqlText))

  //进行数据检索

   {

  //执行SQL语句出错

mysql_close( myData ) ;

 return FALSE ;

   }

   else

   {

 res = mysql_store_result( myData ) ;

//取得查询结果

 i = (int) mysql_num_rows( res ) ;

//取得有效记录数

 printf( "Query: %s\n%ld records found:

     \n", szSqlText, i ) ;

     for ( i = 0 ; fd = mysql_fetch_field( res ) ;

          i++ )

 strcpy( aszFlds[ i ], fd->name ) ;

 //取得各字段名

      for (i=1; row = mysql_fetch_row( res ); )

//依次读取各条记录

 {j = mysql_num_fields( res ) ;

 //取得记录中的字段数

 printf( "Record #%ld:-\n", i++ ) ;

 for ( k = 0 ; k < j ; k++ )

    //输出各字段的值

 printf( "  Fld #%d (%s): %s\n", k + 1, aszFlds[ k ],

      (((row[k]==NULL)||

         (!strlen(row[k])))?"NULL":row[k])) ;

 puts( "==============================\n" ) ;

  }

  mysql_free_result( res ) ;

   }

  }

  else

  {//连接数据库出错

 ErrLog( "Can't connect to the mysql server ") ;

 mysql_close( myData ) ;

 return FALSE ;

  }

mysql_close( myData ) ;

   return TRUE ;

}

---- 对其中几个函数作简单说明，详细说明，可参考MySQL文档：

---- 1. MYSQL *mysql_init(MYSQL *mysql)

---- 初始化一个类型为MYSQL的数据结构，为执行mysql_real_connect()做准备。参数

mysql为指向该结构的指针，如果mysql为NULL，则新建并初始化一个MYSQL的数据结构。

新建的结构将在mysql_close()中释放。

---- 若成功，返回初始化的MYSQL数据结构的指针，否则返回NULL。

---- 2. MYSQL *mysql_real_connect(MYSQL *mysql, const char *host,

---- const char *user, const char *passwd, const char *db,

---- unsigned int port, const char *unix_socket, unsigned int client_flag)

---- 与MySQL数据库引擎建立连接。在执行进一步的数据操作之前，必须保证mysql_re

al_connect()成功返回。

---- 参数mysql是mysql_init()的返回值；

---- 参数host是运行MySQL数据库引擎的机器的TCP/IP主机名，如为NULL则默认为“lo

calhost”；

---- 参数user和passwd是MySQL数据库的合法用户和口令；

---- 参数db是连接的数据库名；

---- 参数port，unix_socket和client_flag一般取默认值。

---- 3. int mysql_query(MYSQL *mysql, const char *query)

---- 执行query字符串中的SQL语句，query必须以0结尾。如果成功，返回0。

---- 4. MYSQL_RES *mysql_store_result(MYSQL *mysql)

---- 返回SELECT，SHOW，DESCRIBE, EXPLAIN等语句执行的结果。函数新建一个MYSQL_

RES的数据结构，把结果存储在该结构中。如果查询没有匹配的结果，则返回空数据集。

处理完结果集后，必须调用mysql_free_result()。

---- 如果出错，返回NULL，否则返回MYSQL_RES结构的指针。

---- 5. MYSQL_ROW mysql_fetch_row(MYSQL_RES *result)

---- 取回结果集中的下一条记录，如果没有记录或出错，返回NULL。一条记录中的字段

数可以用mysql_num_fields(result)获得，各字段的值可以用row[0] 到 row[mysql_nu

m_fields(result)-1]的数组来访问。

---- 在工程的链接选项中，加入c:\mysql\lib\libmysql.lib的接口库，把libmysql.d

ll复制到操作系统的system目录下（c:\winnt\system32），就可以编译运行了。

---- 到这里，一个简单的数据库应用就开发完成了。当然，MySQL有一套功能丰富的AP

I，大家可以查看文档。另外，如果需要跨平台的移植性，可以考虑用MySQL的ODBC接口

。可以自己配置MySQL的ODBC连接属性，也可以下载myodbc-2.50.29-nt.zip工具包来配

置。

[b]四、总结[/b]

---- 作者用MySQL在Windows NT上构建了高性能、高稳定性的数据库系统。这样，既可

以利用Windows NT和Visual C++的友好界面，又可以获得MySQL的强大功能。实践证明，

这种方案成本低（别忘了MySQL是免费的）、效率高、开发周期短、运行稳定（MySQL的

稳定性在Yahoo、163、263等站点获得了极高的评价）。

---- 目前，我的股市行情查询和交易系统已经获得了每秒钟记录70－100条股票行情数

据，同时响应50个并发用户进行数据查询的性能，而系统的开发、运行成本却大幅降低

。如果您正为PC上的数据库效率发愁，不妨也来试试MySQL吧。


几则JSP入门知识总结---像我这样的菜鸟朋友看过来：P

此电子文档属C#乐园QQ群3256045整理。
几则JSP入门知识总结---像我这样的菜鸟朋友看过来：P

上一页 目录 下一页

几则JSP入门知识总结---像我这样的菜鸟朋友看过来：P

作者：87zd

日期：2001-3-25 13:31:29

来源：http://www.jspsky.com

    从去年9月份，我就开始着手学JSP，以前也只有一点程序的意识，一路上摸索过来，经过了很多磨难，终于有一天，我就像一个旱鸭子学会游泳一样,心里无比高兴，熬了几天夜，终于写成了这个纯JSP的文章发布程序。

相信下面的几则小知识对向我这样水平的菜鸟有一定的帮助！

    另外，http://www.jspsky.comoicq:1941017,email:jspsky@8848.net)

==============================================================================

1.传递表单参数：

String name = new String(request.getParameter("name"));

2.数据库连接：

~~MYSQL

//设置数据库的URL

String url = "jdbc:mysql://localhost:3306/jspsky";

try

//加载驱动程序

Class.forname("org.gjt.mm.mysql.Driver").newInstance();

//建立连接

java.sql.Connection connection = java.sql.DriverManager.getConnection(url);

java.sql.Statement statement = connection.createStatement();

//SQL语句

String sqlStringi ="insert into commu(name,tel,mobile,oicq,email)values(‘"+name+"’,‘"+tel+"’,‘"+mobile+"’,‘"+oicq+"’,‘"+email+"’)";

//运行SQL语句，并建立结果集

java.sql.ResultSet rsi = statement.executeQuery(sqlStringi);

//在屏幕上输出库中的内容

while(rss.next())

{

String a_name = rss.getString(1);

out.println(a_name);

{}

//关闭连接

connection.close();

}

//捕捉异常

catch(java.sql.SQLException e)

out.println(e.getMessage());

{}

catch(ClassNotFoundException e)

out.println(e.getMessage());

{}

~~DB2

//定义数据库的URL

String url = "jdbc:db2:portal";

try

//加载驱动程序

Class.forName("COM.ibm.db2.jdbc.app.DB2Driver");

//建立连接，

java.sql.Connection connection = java.sql.DriverManager.getConnection(url,"user","password");

java.sql.Statement statement = connection.createStatement();

//SQL语句

String sqlString = "select * from client";

//执行SQL语句

java.sql.ResultSet rs = statement.executeQuery(sqlString);

//在屏幕上显示所连表中的内容

while(rs.next())

{

String name = rs.getString(1);

out.println(name);

{}

//关闭连接

connection.close();

}

//捕捉异常

catch(java.sql.SQLException e)

out.println(e.getMessage());

{}

catch(ClassNotFoundException e)

out.println(e.getMessage());

{}

3.文件操作

~~将一个字符串写到一个指定的文件中，如果该文件不存在，则新建一个文件，并完成写入；如果存在，则用此字符串覆盖原文件的所有内容

import java.io.*;

String str = "print me 雪峰!";

//定义好打印的目标文件名

//取得当前主机存放WEB页面的绝对路径

String hostdir = System.getProperty("user.dir");

//取得当前主机所采用的路径分隔符

String fileBar = System.getProperty("file.separator");

//书写完整的目标文件存放路径

String nameOfFile=hostdir+fileBar+"test.html";

try

//实例化一个文件输出流对象

FileOutputStream afile = new FileOutputStream(nameOfFile);

//将文件输出流，创建一个打印输出流对象

PrintWriter pw = new PrintWriter(afile);

pw.println(str);

//clean up

pw.close();

{}

catch(IOException e)

out.println(e.getMessage());

{}

~~列出指定目录下的文件列表

import java.io.*;

String cdur = System.getProperty("user.dir");

String fileBar = System.getProperty("file.separator");

String mydir =cdur+fileBar+"doc"+fileBar+"jspsky";

File my = new File(mydir);

String d[] = my.list();

int i;

int l=d.length;

for(i=0;i out.print(d[i]);

{}

4.计数器

Integer count = null;

synchronized (application)

count =(Integer) application.getAttribute("d");

if (count ==null)

count =new Integer("0");

count = new Integer(count.intValue()+1);

application.setAttribute("d",count);

{}

out.println(count);

// 首先定义一个整形对象，并初始化为：NULL，

// 取回APPLICATION对像的属性D的值，并强制转化为整形对象，赋给COUNT

// 判断COUNT是否为空，为空时，将O赋给COUNT对象，

// 否则，通过COUNT。INTVALUE（）方法，实现COUNT对象加1，并赋值给COUNT

// 最后，将COUNT对象保存在APPLICATION对象的D变量中。

+++++++++++++++++++

下一步学习重点

文件的删除

文件内容的修改

图片的上传

邮件列表

JAVABEANS

EJB

XML

JAVASCRIPT

对数据库的操作及维护

了解商业项目开发流程

实例练习

留言板

聊天室

发送邮件

新闻系统

截取网页内容

购物车

多做练习，在实践中不断熟悉JAVA包的使用！

                        \|///

                       \ - - //

                       ( @ @ )

┏━━━━━━━━━oOOo-(_)-oOOo━┓

┃欢迎光临JSP学习小站：jspsky.com  ┃

┃新世纪，新起点，让我们做得更好！ ┃

┃nick:87zd   OICQ:1941017 　   　 ┃

┃Email:jspsky@8848.net  　Oooo　  ┃

┗━━━━━━━━━ oooO--(   ) ━┛

                     (   )　) /

                      (  / (_/

                       \_)


纪念在chinaasp积分过一百呕心原创一篇（Java 中对文件的读写操作之比较）拿分好难呀，555~~~，不知道那些几千分的老妖们是怎么灌水的。

作者：jeru

日期：2000-11-29 11:52:43

Java 中对文件的读写操作之比较

作者：Jeru Liu

日期：November 29,2000

版本：1.0

Java 对文件进行读写操作的例子很多，让初学者感到十分困惑，我觉得有必要将各种方法进行

一次分析，归类，理清不同方法之间的异同点。

一．在 JDK 1.0 中，通常是用 InputStream & OutputStream 这两个基类来进行读写操作的。

InputStream 中的 FileInputStream 类似一个文件句柄，通过它来对文件进行操作，类似的，在

OutputStream 中我们有 FileOutputStream 这个对象。

用FileInputStream 来读取数据的常用方法是：

FileInputStream fstream = new FileInputStream(args[0]);

DataInputStream in = new DataInputStream(fstream);

用 in.readLine() 来得到数据，然后用 in.close() 关闭输入流。

完整代码见 Example 1。

用FileOutputStream 来写入数据的常用方法是：

FileOutputStream out out = new FileOutputStream("myfile.txt");

PrintStream p = new PrintStream( out );

用 p.println() 来写入数据，然后用 p.close() 关闭输入。

完整代码见 Example 2。

二．在 JDK 1.1中，支持两个新的对象 Reader & Writer, 它们只能用来对文本文件进行操作，而

JDK1.1中的 InputStream & OutputStream 可以对文本文件或二进制文件进行操作。

用FileReader 来读取文件的常用方法是：

FileReader fr = new FileReader("mydata.txt");

BufferedReader br = new BufferedReader(fr);

用 br.readLing() 来读出数据，然后用br.close() 关闭缓存，用fr.close() 关闭文件。

完整代码见 Example 3。

用 FileWriter 来写入文件的常用方法是：

FileWriter fw = new FileWriter("mydata.txt");

PrintWriter out = new PrintWriter(fw);

在用out.print 或 out.println 来往文件中写入数据，out.print 和 out.println的唯一区别是后者写

入数据或会自动开一新行。写完后要记得 用out.close() 关闭输出，用fw.close() 关闭文件。

完整代码见 Example 4。

-------------------------------------------------------------- following is the source code of examples------------------------------------------------------

Example 1:

// FileInputDemo

// Demonstrates FileInputStream and DataInputStream

import java.io.*;

class FileInputDemo {

  public static void main(String args[]) {

    // args.length is equivalent to argc in C

    if (args.length == 1) {

      try {

        // Open the file that is the first command line parameter

        FileInputStream fstream = new FileInputStream(args[0]);

        // Convert our input stream to a DataInputStream

        DataInputStream in = new DataInputStream(fstream);

        // Continue to read lines while there are still some left to read

        while (in.available() !=0) {

          // Print file line to screen

          System.out.println (in.readLine());

        }

        in.close();

      } catch (Exception e) {

        System.err.println("File input error");

      }

    }

    else

      System.out.println("Invalid parameters");

  }

}

Example 2:

// FileOutputDemo

// Demonstration of FileOutputStream and PrintStream classes

import java.io.*;

class FileOutputDemo

{

  public static void main(String args[])  {

  FileOutputStream out; // declare a file output object

    PrintStream p; // declare a print stream object

try {

  // connected to "myfile.txt"

      out = new FileOutputStream("myfile.txt");

      // Connect print stream to the output stream

      p = new PrintStream( out );

      p.println ("This is written to a file");

      p.close();

    } catch (Exception e) {

      System.err.println ("Error writing to file");

    }

  }

}

Example 3:

// FileReadTest.java

// User FileReader in JDK1.1 to read a file

import java.io.*;

class FileReadTest {

  public static void main (String[] args) {

    FileReadTest t = new FileReadTest();

    t.readMyFile();

}

  void readMyFile() {

    String record = null;

    int recCount = 0;

    try {

FileReader fr = new FileReader("mydata.txt");

       BufferedReader br = new BufferedReader(fr);

       record = new String();

       while ((record = br.readLine()) != null) {

         recCount++;

         System.out.println(recCount + ": " + record);

}

br.close();

fr.close();

     } catch (IOException e) {

         System.out.println("Uh oh, got an IOException error!");

         e.printStackTrace();

     }

}

}

Example 4:

// FileWriteTest.java

// User FileWriter in JDK1.1 to writer a file

import java.io.*;

class FileWriteTest {

  public static void main (String[] args) {

    FileWriteTest t = new FileWriteTest();

    t.WriteMyFile();

}

  void WriteMyFile() {

    try {

FileWriter fw = new FileWriter("mydata.txt");

PrintWriter out = new PrintWriter(fw);

out.print(“hi,this will be wirte into the file!”);

out.close();

fw.close();

     } catch (IOException e) {

         System.out.println("Uh oh, got an IOException error!");

         e.printStackTrace();

     }

}

}


简单的例子

作者：whitefox

email: whitefox.jiang@corp.elong.com

日期：2000-7-27 11:49:57

public class OracleConnectionManager {

    private DBConnectionManager connMgr;

    private final String poolName="Oracle";

    private Connection conn;

    public OracleConnectionManager() {

        String userID = "system";

    String password = "manager";

        String JDBCDriver = "oracle.jdbc.driver.OracleDriver";

     String JDBCDriverType = "jdbc:oracle:thin";

    String DBHost = "127.0.0.1";

    String Port = "1521";

    String SID="sid";

    String url = JDBCDriverType+":@"+DBHost + ":" + Port + ":" +SID;

        connMgr = DBConnectionManager.getInstance();

        if (DBConnectionManager.clients == 1){

            connMgr.init(poolName, JDBCDriver, url, userID, password);

                       connMgr.init("egongOracle", JDBCDriver, JDBCDriverType+":@110.7.6.22:1521:egong", "abcusername", "abcpasswd");

                                                     }

    }

    public Connection getConnection() {

        return (conn=connMgr.getConnection(poolName));

    }

        public Connection getConnection(String newOracle) {

        return (conn=connMgr.getConnection(newOracle));

    }

    public void freeConnection() {

        connMgr.freeConnection(poolName,conn);

    }

        public void freeConnection(String newOracle) {

        connMgr.freeConnection(newOracle,conn);

    }

}

public class DBConnectionManager {

    static private DBConnectionManager instance=null;       // The single instance

    static public int clients=0;

    private Vector drivers = new Vector();

    private PrintWriter log;

    private Hashtable pools = new Hashtable();

    private final int MAX_CONN = 1000;

    /**

     * Returns the single instance, creating one if it's the

     * first time this method is called.

     *

     * @return DBConnectionManager The single instance.

     */

    static synchronized public DBConnectionManager getInstance() {

        if (instance == null) {

            instance = new DBConnectionManager();

        }

        clients++;

        return instance;

    }

    /**

     * A private constructor since this is a Singleton

     */

    private DBConnectionManager() {

//        init();

    }

    /**

     * Returns a connection to the named pool.

     *

     * @param name The pool name as defined in the properties file

     * @param con The Connection

     */

    public void freeConnection(String name, Connection con) {

        DBConnectionPool pool = (DBConnectionPool) pools.get(name);

        if (pool != null) {

            pool.freeConnection(con);

        }

    }

    /**

     * Returns an open connection. If no one is available, and the max

     * number of connections has not been reached, a new connection is

     * created.

     *

     * @param name The pool name as defined in the properties file

     * @return Connection The connection or null

     */

    public Connection getConnection(String name) {

        DBConnectionPool pool = (DBConnectionPool) pools.get(name);

        if (pool != null) {

            return pool.getConnection();

        }

        return null;

    }

    /**

     * Returns an open connection. If no one is available, and the max

     * number of connections has not been reached, a new connection is

     * created. If the max number has been reached, waits until one

     * is available or the specified time has elapsed.

     *

     * @param name The pool name as defined in the properties file

     * @param time The number of milliseconds to wait

     * @return Connection The connection or null

     */

    public Connection getConnection(String name, long time) {

        DBConnectionPool pool = (DBConnectionPool) pools.get(name);

        if (pool != null) {

            return pool.getConnection(time);

        }

        return null;

    }

    /**

     * Closes all open connections and deregisters all drivers.

     */

    public synchronized void release() {

        // Wait until called by the last client

        if (--clients != 0) {

            return;

        }

        Enumeration allPools = pools.elements();

        while (allPools.hasMoreElements()) {

            DBConnectionPool pool = (DBConnectionPool) allPools.nextElement();

            pool.release();

        }

        Enumeration allDrivers = drivers.elements();

        while (allDrivers.hasMoreElements()) {

            Driver driver = (Driver) allDrivers.nextElement();

            try {

                DriverManager.deregisterDriver(driver);

                log("Deregistered JDBC driver " + driver.getClass().getName());

            }

            catch (SQLException e) {

                log(e, "Can't deregister JDBC driver: " + driver.getClass().getName());

            }

        }

        log.close();

    }

    /**

     * Creates instances of DBConnectionPool based on the properties.

     * A DBConnectionPool can be defined with the following properties:

     * <PRE>

     * &lt;poolname&gt;.url         The JDBC URL for the database

     * &lt;poolname&gt;.user        A database user (optional)

     * &lt;poolname&gt;.password    A database user password (if user specified)

     * &lt;poolname&gt;.maxconn     The maximal number of connections (optional)

     * </PRE>

     *

     * @param props The connection pool properties

     */

    private void createPools(String poolName, String url, String user, String password, int max) {

/*

        Enumeration propNames = props.propertyNames();

        while (propNames.hasMoreElements()) {

            String name = (String) propNames.nextElement();

            if (name.endsWith(".url")) {

                String poolName = name.substring(0, name.lastIndexOf("."));

                String url = props.getProperty(poolName + ".url");

                if (url == null) {

                    log("No URL specified for " + poolName);

                    continue;

                }

                String user = props.getProperty(poolName + ".user");

                String password = props.getProperty(poolName + ".password");

                String maxconn = props.getProperty(poolName + ".maxconn", "0");

                int max;

                try {

                    max = Integer.valueOf(maxconn).intValue();

                }

                catch (NumberFormatException e) {

                    log("Invalid maxconn value " + maxconn + " for " + poolName);

                    max = 0;

                }

                DBConnectionPool pool =

                    new DBConnectionPool(poolName, url, user, password, max);

                pools.put(poolName, pool);

                log("Initialized pool " + poolName);

            }

        }

*/

        DBConnectionPool pool =

            new DBConnectionPool(poolName, url, user, password, max);

        pools.put(poolName, pool);

        log("Initialized pool " + poolName);

    }

    public void init(String poolName, String driver, String url, String user, String passwd) {

        init(poolName, driver, url, user, passwd, MAX_CONN);

    }

    /**

     * Loads properties and initializes the instance with its values.

     */

    public void init(String poolName, String driver, String url, String user, String passwd, int maxconn) {

/*

        InputStream is = getClass().getResourceAsStream("/db.properties");

        Properties dbProps = new Properties();

        try {

            dbProps.load(is);

        }

        catch (Exception e) {

            System.err.println("Can't read the properties file. " +

                "Make sure db.properties is in the CLASSPATH");

            return;

        }

        String logFile = dbProps.getProperty("logfile", "DBConnectionManager.log");

        String logFile = "." + File.separator + "logs" + File.separator + "dbpool";

        try {

            log = new PrintWriter(new FileWriter(logFile, true), true);

        }

        catch (IOException e) {

            System.err.println("Can't open the log file: " + logFile);

            log = new PrintWriter(System.err);

        }

*/

        log = new PrintWriter(System.err);

        loadDrivers(driver);

        createPools(poolName, url, user, passwd, maxconn);

    }

    /**

     * Loads and registers all JDBC drivers. This is done by the

     * DBConnectionManager, as opposed to the DBConnectionPool,

     * since many pools may share the same driver.

     *

     * @param props The connection pool properties

     */

    private void loadDrivers(String driverClassName) {

/*

        String driverClasses = props.getProperty("drivers");

        StringTokenizer st = new StringTokenizer(driverClasses);

        while (st.hasMoreElements()) {

            String driverClassName = st.nextToken().trim();

            try {

                Driver driver = (Driver)

                    Class.forName(driverClassName).newInstance();

                DriverManager.registerDriver(driver);

                drivers.addElement(driver);

                log("Registered JDBC driver " + driverClassName);

            }

            catch (Exception e) {

                log("Can't register JDBC driver: " +

                    driverClassName + ", Exception: " + e);

            }

        }

*/

        try {

            Driver driver = (Driver)

                Class.forName(driverClassName).newInstance();

            DriverManager.registerDriver(driver);

            drivers.addElement(driver);

            log("Registered JDBC driver " + driverClassName);

        }

        catch (Exception e) {

            log("Can't register JDBC driver: " +

                driverClassName + ", Exception: " + e);

        }

    }

    /**

     * Writes a message to the log file.

     */

    private void log(String msg) {

        log.println(new Date() + ": " + msg);

    }

    /**

     * Writes a message with an Exception to the log file.

     */

    private void log(Throwable e, String msg) {

        log.println(new Date() + ": " + msg);

        e.printStackTrace(log);

    }

    /**

     * This inner class represents a connection pool. It creates new

     * connections on demand, up to a max number if specified.

     * It also makes sure a connection is still open before it is

     * returned to a client.

     */

    class DBConnectionPool {

        private int checkedOut;

        private Vector freeConnections = new Vector();

        private int maxConn;

        private String name;

        private String password;

        private String URL;

        private String user;

        /**

         * Creates new connection pool.

         *

         * @param name The pool name

         * @param URL The JDBC URL for the database

         * @param user The database user, or null

         * @param password The database user password, or null

         * @param maxConn The maximal number of connections, or 0

         *   for no limit

         */

        public DBConnectionPool(String name, String URL, String user, String password,

                int maxConn) {

            this.name = name;

            this.URL = URL;

            this.user = user;

            this.password = password;

            this.maxConn = maxConn;

        }

        /**

         * Checks in a connection to the pool. Notify other Threads that

         * may be waiting for a connection.

         *

         * @param con The connection to check in

         */

        public synchronized void freeConnection(Connection con) {

            // Put the connection at the end of the Vector

            freeConnections.addElement(con);

            checkedOut--;

            notifyAll();

        }

        /**

         * Checks out a connection from the pool. If no free connection

         * is available, a new connection is created unless the max

         * number of connections has been reached. If a free connection

         * has been closed by the database, it's removed from the pool

         * and this method is called again recursively.

         */

        public synchronized Connection getConnection() {

            Connection con = null;

            if (freeConnections.size() > 0) {

                // Pick the first Connection in the Vector

                // to get round-robin usage

                con = (Connection) freeConnections.firstElement();

                freeConnections.removeElementAt(0);

                try {

                    if (con.isClosed()) {

                        log("Removed bad connection from " + name);

                        // Try again recursively

                        con = getConnection();

                    }

                }

                catch (SQLException e) {

                    log("Removed bad connection from " + name);

                    // Try again recursively

                    con = getConnection();

                }

            }

            else if (maxConn == 0 || checkedOut < maxConn) {

                con = newConnection();

            }

            if (con != null) {

                checkedOut++;

            }

            return con;

        }

        /**

         * Checks out a connection from the pool. If no free connection

         * is available, a new connection is created unless the max

         * number of connections has been reached. If a free connection

         * has been closed by the database, it's removed from the pool

         * and this method is called again recursively.

         * <P>

         * If no connection is available and the max number has been

         * reached, this method waits the specified time for one to be

         * checked in.

         *

         * @param timeout The timeout value in milliseconds

         */

        public synchronized Connection getConnection(long timeout) {

            long startTime = new Date().getTime();

            Connection con;

            while ((con = getConnection()) == null) {

                try {

                    wait(timeout);

                }

                catch (InterruptedException e) {}

                if ((new Date().getTime() - startTime) >= timeout) {

                    // Timeout has expired

                    return null;

                }

            }

            return con;

        }

        /**

         * Closes all available connections.

         */

        public synchronized void release() {

            Enumeration allConnections = freeConnections.elements();

            while (allConnections.hasMoreElements()) {

                Connection con = (Connection) allConnections.nextElement();

                try {

                    con.close();

                    log("Closed connection for pool " + name);

                }

                catch (SQLException e) {

                    log(e, "Can't close connection for pool " + name);

                }

            }

            freeConnections.removeAllElements();

        }

        /**

         * Creates a new connection, using a userid and password

         * if specified.

         */

        private Connection newConnection() {

            Connection con = null;

            try {

                if (user == null) {

                    con = DriverManager.getConnection(URL);

                }

                else {

                    con = DriverManager.getConnection(URL, user, password);

                }

                log("Created a new connection in pool " + name);

            }

            catch (SQLException e) {

                log(e, "Can't create a new connection for " + URL);

                return null;

            }

            return con;

        }

    }

}


簡易物件存取協定(转) -- 告诉你什么是SOAP,繁体

作者：jeru

email: jeru@163.net

日期：2001-7-13 16:56:11

簡易物件存取協定

Simple Object Access Protocol (SOAP)

作者: 恆逸資訊 胡百敬

什麼是簡易物件存取協定(SOAP)，簡而言之就是利用現存的網際網路架構讓應用程式之間可以彼此溝通，而不會被防火牆阻礙。在分散式的架構下，使用 XML 的環境中，SOAP提供兩個電腦系統之間交換的架構與資料型別。

在過去五年來透過網際網路存取已經變成是較進步社會的基礎需求。在其上執行著各式各樣的通訊協定。但是直至目前為止最廣泛被接受的通訊協定依然是Hypertext Transfer Protocol(HTTP)，它是瀏覽器與Web伺服器之間溝通時使用，對於文字、圖形以及其他資訊的傳輸很有效率與彈性，而且它簡單易懂。

你我所撰寫的應用程式利用網際網路在遠端互動已經變得越來越重要。現今在網際網路上提供eService已經是大勢所趨。舉個例子來說，你可能在某一家網路公司所提供的行事曆上註冊，當快遞公司要送貨品給你時，它的系統會自動與提供行事曆的網路公司合作，查閱你的在家的時間，自動排定送貨的行程。或著是你想寫一個入門網站，但覺得某個網站所提供的交通資訊或是天氣預報系統很好，你想直接讓你的使用者透過系統之間的合作，可以在你的網站線上查詢別的網站上這些資料。而那些提供服務的網站也可以查詢的次數向你收費。

以上這些動作都需要系統自動完成合作，不再有人工參與。且彼此的系統是各自以他所熟悉的技術完成，這代表著系統不會遵循特殊的架構。有可能我的系統是Win32，使用的是COM+﹔而你的是UNIX作業系統，利用CORBA提供服務。

讓兩個系統透過網際網路溝通，僅僅用HTTP通訊協定本身提供的功能是不夠的，雖然HTTP本身的彈性很大，但它基本的設計並不適合呼叫遠端的程式物件。這種互動在區域網路內一般是使用Remote Procedure Call(RPC)，也就是使用者端傳出一些參數，並由伺服端回傳一些結果。

現今已有許多分散式物件通訊協定(distributed object protocols) 提供遠端程式間的溝通。例如微軟的Distribured Component Object Model(DCOM)、Object Management Group的Internet Inter-ORB Protocol(IIOP)等等。所有這些服務都提供相同的服務，也就是讓使用者端可以觸發RPC到伺服端應用程式，並接到回傳結果。 在企業內部網路(Intranet)上使用分散式物件傳輸協定有很好的效果。但在公眾的網際網路上使用這些協定就有很多問題。任何連上網際網路的伺服器基本上都可以被任何網際網路的使用者存取，這導致需要較嚴謹的安全考量。為了安全，大部分的企業都在它們內部與外部網路之間加裝防火牆以防止網際網路上的大眾存取企業內部的伺服器。這些防火牆，例如微軟的Proxy伺服器，可以經由條件設定以阻止一些想進企業內部來的公眾網路需求，這可以大幅提昇內部系統的安全。

雖然防火牆是提供接上網際網路安全的基礎機制，但它卻會降低分散式物件通訊協定的使用效能。為了要解決這個問題，有識之士紛紛提出了各自的解決方案。在 1998 年，UserLand 公司的執行總裁 Dave Winner 提出透過 XML 讓 RPC 的通訊方式透過 HTTP 協定在網際網路上執行。

這個想法經由微軟公司加以改良，提出了實際可行的Simple Object Access Protocol(SOAP)通訊協定。現今正在W3C審議中，已經有IBM等大廠表態支持。不久的未來即將可能成為在網際網路上提供電子服務的標準協定。

SOAP是一個像DCOM或其他分散式物件通訊協定的協定，讓使用者端與伺服端的RPCs可以溝通。但與其他類似協定不一樣的地方是，它支援防火牆的使用。同樣重要地，SOAP不是只設計用來針對某種物件技術的協定，它不像一些時下的分散式物件通訊協定會被綁死在某一種特定的物件規格上，這個協定將可以被任何的物件使用。所以它將是兩大物件陣營COM 和 CORBA 最好的溝通橋樑，讓彼此的物件程式可以跨平台透過網際網路呼叫。

簡易物件存取協定如其名稱所言，要求定義要"簡易"，所以它只訂出物件溝通基礎規範，如

讓物件透過網際網路提出需求的方式標準化，以 HTTP 當傳輸的方式，以 XML 描述溝通的內容

建立可延伸的傳遞物件呼叫格式的承載 但它不定義一些一般分散式物件系統需要定義的

分散式系統資源回收(garbage collection)

雙向的 HTTP 溝通

物件參照

物件初始化

以上這些不明確定義的規格都交由各系統廠商自行實作。

使用防火牆所造成的問題以及 SOAP 所提供的解決方案

要了解為何防火牆會造成分散式物件通訊協定的問題必須先了解到防火牆是如何分辨協定之間的不同。在TCP/IP的架構下，每一個被廣泛使用的協定都被賦予一個特殊的埠號(port number)而每一個使用該協定的需求封包都帶著這個埠號。例如HTTP協定的埠號是80、FTP是21等等。大部分的防火牆可以用來防止某個特殊協定的方式就是針對埠號拒絕某種協定的通訊。通常防火牆是被設定成允許埠號80的運作的-如果該公司不拒絕使用HTTP的話。

但大部分的防火牆會擋住其他的埠，因為它們假定利用其他的埠對公司內部網路的運作都是有危險的。 但這也正是造成分散式物件通訊協定無法運行的原因。不像HTTP、FTP等其他著名的通訊協定，分散式物件通訊協定通常沒有使用一個著名的大家都知道的埠號來溝通。相反地，這些通訊協定通常動態地被賦予埠號，埠號碼在被需求時任意產生。如果沒有防火牆擋在使用者端與伺服端之間，這種方式將可以很有效地運作。但若加了防火牆，則該通訊協定會因為防火牆不允許兩端任意使用任何埠號來溝通而中斷。

當下存在很多種解決方式，例如某些防火牆可以被設定成允許某個範圍的埠號碼可以進行溝通。若該分散式物件通訊協定也可以被設定成只用這個範圍的埠號碼，則這個方案便可行，使用者端與伺服端之間可以進行溝通。但比較注重安全的網路管理者將不會贊成開放任意一組埠號碼而導致這個方案並不完美。另一個選擇是採用COM網際網路服務，這讓傳統的DCOM封包在TCP上透過埠號80來傳遞。這在某些方面很有用，但這項技術只有微軟的Internet Information Server和DCOM在使用，而不是一項完整的解決方案，所以我們需要一個更普遍一般性的解決方案。

因為幾乎所有的防火牆都允許透過埠號80來溝通，所以透過埠號80來溝通的分散式物件通訊協定將是一個較好的方案。但這並不是說說那麼容易，因為埠號80已經被設定給HTTP協定。所以SOAP這個分散式物件通訊協定是架在HTTP協定之上的。HTTP通訊協定相當簡單，僅僅以少數基礎的動詞所組成，如GET、PUT、POST等等，而這些動詞在瀏覽器與伺服器之間傳遞。而每一個動詞之後跟著一些資訊，而這些資訊通常以簡易的字串方式傳遞。

SOAP不能改變任何的現狀，也不能要求增加HTTP現有的動詞。替代方案是SOAP將使用Extensible Markup Language(XML)來定義需求與回應訊息的格式，並允許使用正常的HTTP POST命令來傳遞這些訊息。所有的SOAP通訊都使用80埠，這也代表著在網際網路上SOAP可以透過任何的Web伺服器來溝通-防火牆將不再是一個問題。 SOAP主要的設計目的之一就是保證它可以有效地使用既有的網際網路架構-也就是HTTP、防火牆、代理伺服器(proxy)以及其他種種。例如SOAP可以使用Secure Sockets Layer(SSL)通訊協定以加密維護安全，使用到HTTP的連線管以機制，等等更多更多的部分。SOAP讓分散式物件通訊協定透過網際網路的溝通像使用瀏覽器來存取網頁一樣方便。

在現今這個世界上已經有許多的許多分散是物件通訊協定，但還沒有一個可以不被改變就用在現今的網際網路上。藉由提供一個架在HTTP之上，簡單、且有彈性的機制來傳送需求與回應，SOAP讓當下觸發遠端函數不需要有任何改變。讓應用程式可以透過網際網路存取不是一件小事。且它並未被綁死在任何一個單一的物件模型，這項新的技術擁有可以用在許多不同情況的潛力。基於以上的論述，SOAP對於今日的通訊協定是一項值得增加的投資。


建议用JUNIT建立测试类

作者：javabean白痴

email: cape@163.net

日期：2001-7-24 16:43:01

JUNIT Getting Started

概念：

关于JUNIT的概念可以参见Thinking in Java 2的第一章里的Extreme programming

以junit3.5为例

将classpath指向junit.jar

HelloWorld:

在junit3.5下(或者其他地方)建立一个工作目录mypro,然后建立项目目录hello

其中有文件:HelloWorld.java

package mypro.hello;

public class HelloWorld{

    public String sayHello(){

        return "Hello World";

        }

public static void main(String[] args){

    HelloWorld world=new HelloWorld();

    System.out.println(world.sayHello());

    }

}

编译,运行.

现在写测试类,来测试HelloWorld类里的sayHello方法

//在hello目录里建立一个test目录用来放测试类,其中有HelloWorldTest.java

package mypro.hello;

//import mypro.hello.*;         推荐测试类和原来的被测试类(HelloWorld)同包

import junit.framework.*;

public class HelloWorldTest extends TestCase{

    public HelloWorldTest(String name){

        super(name);

        }

    public static void main(String args[]){

        junit.textui.TestRunner.run(HelloWorldTest.class);

        }

    public void testSayHello(){

        HelloWorld world=new HelloWorld();

        assert(world!=null);                                          //当结果是-1时,assert将报错

        assertEquals("Hello World",world.sayHello());

        }

}

HelloWorldTest继承了TestCase类,写法固定,main可以不要.

主要是testSayHello()实现了对SayHello方法的测试.

Assert,的参数是-1时junit会报错,assertEquals的参数不匹配时,junit会报错.详见JUNIT的API文档.

编译后,可以java HelloWorldTest来得到结果   //因为有main

更经常的运行方式: java  junit.swingui.TestRunner  mypro.hello.HelloWorldTest   //图形界面

                             java junit.textui.TestRunner mypro.hello.HelloWorldTest           //字符界面

制造一个错误:

把HelloWorld里的return “Hello World” 改成return “Hell0 World”

编译后

运行测试,看到结果报告错误.

更进一步执行多个测试,一个测试成为Test Case,而一组Test Case构成Test suite

看junit里money例子和AllTests.java文件.

注意事项:

   1.将测试类和被测试类放在同一个目录里(即同包之内)将会是很方便的.

   2.命名规则:一般被测试类写作XXXXTest,而其中的方法写作testXXX


将 Microsoft 的 Internet Information Server 用作 Java servlet 引擎　二　（给学过ASP的朋友）

作者：sonymusic

email: sonymusic@china.com

日期：2000-12-6 23:51:09

现在这个工具极大地简化了您的工作。而 getParameterNames 方法就具有了类似下面的形式：

public Enumeration getParameterNames()

{

    return(

        new EnumerationComposite(

            new RequestDictionary[] {

                request.getQueryString(),

                request.getForm() } ) );

}

响应对象的下一个最常用的方法是 getSession。会话对象是另一个核心对象，它在 ASP 和 servlet 之间互为映像。因此，您提供的会话就必须拥有自己的适配器，稍后我会对此加以说明。但在我说明之前，请先看这个请求方法：

public HttpSession getSession( boolean flag )

{

    return( new SessionAdapter() );

}

在本文中，需要改写的请求对象的最后一个方法是 getCookies。顾名思义，它返回客户机所提供的 cookie 的集合。ASP 版本的 cookie 对象使我感到为难，它似乎用作自身的一个集合，但又引出了许多具有莫明其妙的功能的方法。但是我能够对脚本进行充分的剖析来改写 servlet。由于 ASP 版本中返回 Enumeration，而 servlet 版本中则返回一个数组，这样就可以使用 Vector 类中一个不常用的方法 copyInto，来实现这种转换，这是唯一的小窍门。另外请注意，由于在 com.ms.iis.asp 包和 javax.servlet.http 包中，类名是完全相同的，因此我们不得不明确指明每个 Cookie 对象的包名。代码如下：

public javax.servlet.http.Cookie[] getCookies()

{

    Vector tmpList = new Vector();

    CookieDictionary aspCookies = this.request.getCookies();

    IEnumerator e = aspCookies.keys();

    while( e.hasMoreItems() )

    {

        String key = (String) e.nextItem();

        String val = aspCookies.getCookie( key ).getValue();

        tmpList.addElement( new javax.servlet.http.Cookie( key, val ) );

    }

    javax.servlet.http.Cookie[] cookies = new javax.servlet.http.Cookie

         [ tmpList.size() ];

    tmpList.copyInto( cookies );

    return( cookies );

}

会话适配器

现在，既然完成了请求适配器，就需要退回去讨论会话适配器。无论在 ASP 中还是在 servlet 中，会话实际上是一个散列表，您只是将对象置入会话或者从会话获取对象。这些值的用法几乎就等同于相应的响应参数规则，这些规则上面已有讨论。会话适配器的实现很复杂，这里就不介绍了。完整的源代码可从参考资料中获得。

响应适配器

下一个主要的难题是响应适配器。正像请求适配器一样，响应适配器也需要几个巧妙的办法。但是在讨论较难的内容之前，我先偏离正题讨论一点较容易的内容。下面是两个较流行的响应方法的极简单的代码：

public void sendRedirect( String str )

{

    this.response.redirect( str );

}

public void setContentType( String str )

{

    // ASP 自动设置内容的类型！

}

setContentType 用来干什么？它什么事情也没做！没错，IIS 毕竟没有创建出完美的 servlet 引擎。到执行 servlet 时，ASP 引擎已定义了内容类型以及其它标准的 HTTP 标头。但是按经验来讲，多数 servlet 不需要将内容类型设置为纯文本或 HTML 以外的任何其它类型。

正如前面提到的那样，您并不需要一个适配器来处理 cookie。响应对象的 addCookie 方法只须根据所提供的 Sun 的 cookie 的内容，来创建 Microsoft 的 cookie 的实例。Microsoft 和 Sun 都同意，cookie 只不过是将数据的名称和值配成对而已。但是，双方对于 API 中应如何表示 cookie 有效期的方式，则意见不同。

Sun 版本中表示 cookie 有效期的方法是使用一个整型值，它指定 cookie 的最长寿命（以秒为单位）。此值被传递给 Cookie 对象的 setMaxAge 方法。零值表示立即到期，而负值（一种特殊情况）表示 cookie 应在用户浏览器退出时废弃。

Microsoft 版本表示 cookie 有效期的方式有所不同。缺省情况下，Microsoft 的 cookie 设置为在用户浏览器退出时到期。因此，如果 Sun 版本中 cookie 具有负的有效期，转为 Microsoft 版本时不做修改；如果 Sun 版本中 cookie 的最长有效期大于或等于零，须将此有效期转换为 Microsoft 的 Time 对象，并将其传递给 Microsoft 版本的 cookie 对象，作为有效期。请注意，月份值在 Java 的 Calendar 类中是从零开始的，而在 Microsoft 的 Time 类中是从 1 开始的，所以在转换时必须将此值加 1。

public void addCookie( javax.servlet.http.Cookie cookie )

{

    com.ms.iis.asp.Cookie aspCookie = this.response.getCookies().getCookie

        ( cookie.getName() );

    aspCookie.setValue( cookie.getValue() );

    int age = cookie.getMaxAge();

    if( age < 0 )

    {

        // expire on browser exit

    }

    else

    {

        GregorianCalendar date = new GregorianCalendar();

        Date time = new Date( System.currentTimeMillis() + ( 1000 * age ) );

        date.setTime( time );

        Time aspTime = new Time(

            date.get( Calendar.YEAR ),

            1 + date.get( Calendar.MONTH ),

            date.get( Calendar.DAY_OF_MONTH ),

            date.get( Calendar.HOUR ),

            date.get( Calendar.MINUTE ),

            date.get( Calendar.SECOND )

        );

        aspCookie.setExpires( aspTime );

    }

}

最流行的响应方法碰巧也是最难以实现的，这正是我将它留到最后的原因。我所指的方法就是 getWriter。此方法返回 PrintWriter 对象，它让 servlet 将信息写到客户机的显示屏上。在大多数情况下，servlet 只是编制 HTML，HTML 在全部被发送到客户机之前将被存入缓冲区中。为什么要存入缓冲区呢？因为 servlet 在将大量信息转储到 PrintWriter 以后，servlet 可能通过调用 sendRedirect 方法判断出有某种出错和异常终止的情况。重定向代码一定是浏览器接收的第一条信息；显然，一旦发出了重定向，就再不需要将任何缓冲的信息发送给客户机了。

考虑到以上情况，您就必须再创建一个适配器类。这个新的适配器将封装 PrintWriter 对象。它将把它的所有内容都存入缓冲区中，直到调用了 close 方法为止。下面是相应的响应方法：

public PrintWriter getWriter()

{

    return( new PrintWriterAdapter() );

}

而下面是 PrintWriter 适配器的完整代码：

public class PrintWriterAdapter extends PrintWriter

{

    private static final String CR = "\n";

    private StringBuffer sb = new StringBuffer();

    public PrintWriterAdapter()

    {

        super( System.err );

    }

    public void print ( String str ){ sb.append( str ); }//response.write

        ( str ); }

    public void println( String str ){ print ( str + CR ); }

    public void print ( Object obj ){ print ( obj.toString() ); }

    public void println( Object obj ){ println( obj.toString() ); }

    public void print ( char[] chr ){ print ( new String( chr ) ); }

    public void println( char[] chr ){ println( new String( chr ) ); }

    public void close()

    {

        AspContext.getResponse().write( sb.toString() );

    }

}

结论

Microsoft 的 Internet Information Server 没有创造出完美的 servlet 引擎，但它已经相当接近完美了。根据我有关 servlet 的全部经验，IIS 和这些适配器类的组合已表明足以满足开发和部署商业性应用程序的需要。如果您碰巧被锁进只能选 Microsoft 产品的商店，这些工具能帮您另辟蹊径，领略 Java servlet 的奇妙之处。同时，我将一如既往地乐意倾听您的评论、批评以及改进这些代码的comments。

我在本文中引入的所有类的源代码，包括一些我没有谈及的功能，都可以在参考资料中找到。请注意，许多方法（尤其是那些我还不需要的方法）仍未实现。如果您敢于完成这一工作，请给我发一份副本（眨眼）。

对 Microsoft 或有帮助的读者的一项正式请求

我已将本文中说明的技术成功地部署到我实验室的大多数系统上。但它在几台机器上却就是不能工作。ASP 页对于对适配器对象的任何引用都报告这一条错误： "No object for moniker"。这无疑是由 Microsoft 的 Java SDK 4.0、 Microsoft 的 Internet Information Server (Windows NT Option Pack 4)、Visual J++ 和某个 Service Pack 的莫明其妙的组合引起的。我搜索了 Microsoft Developer's Network (MSDN)，但徒劳无益，我已无计可施了。如果您知道问题所在并有解决办法，请拿出来与我分享。谢谢。


将 Microsoft 的 Internet Information Server 用作 Java servlet 引擎　一　（给学过ASP的朋友）

作者：sonymusic

email: sonymusic@china.com

日期：2000-12-6 23:50:12

将 Microsoft 的 Internet Information Server 用作 Java servlet 引擎

用 Microsoft 的 IIS 运行 Java servlet -- 而不牺牲可移植性

Thomas E. Davis

Nutrio.com 首席技术官

2000 年 7 月

内容：

 IIS

 适配器设计模式

 Servlet

 ASP

 Java SDK

 请求适配器

 会话适配器

 响应适配器

 结论

 正式请求

 参考资料

 作者简介

您是陷在仅有 Microsoft 产品的商店中的 Java 狂热分子吗？仅使用 Microsoft 的 Internet Information Server 和纯 Java，您就可以运行 Java servlet，而无须任何第三方产品的帮助。请不要马上就放弃 Microsoft 产品 -- 不妨试试本文中说明的方法，并仍然保持为无专有权的可移植代码。

您知不知道，无须第三方产品，就可以用 Microsoft 的 Internet Information Server (IIS) 运行 JavaServlet？您的全部所需仅仅是简明的旧式 IIS 和纯 Java。尽管您需要使用 Microsoft 的 Java SDK（原因我将在下面解释），但尽可放心，您的代码将不需要任何专用权扩展，同时又保持对其它 servlet 引擎的完全可移植性。

Microsoft 的 Internet Information Server

但您为什么要做这样的傻事，让 Java servlet 运行在并非为它设计的环境中呢？首先，由于我们无法控制的情况，许多像我们这样顽固的 Java 狂热分子都陷在了仅有 Microsoft 产品的商店里。我们全都把 Linux 软件盒子藏在桌子下面，运行 IBM 最新的 JDK 和 Apache 最新的 servlet 引擎，但是，要是老板不让我们在这样一个系统上部署一些产品，这时可就真的不好过了。您当然可以找到运行于 Microsoft 平台上的商业性 servlet 引擎，但购买它们要花大把大把的钱。试一试向您的老板解释：您需要几千美金购买一种新的 Web 服务器，因为您想要废弃操作系统附带的那个免费产品（或者只将它用作简单的传递代理，这正是许多产品目前的工作方式）。然后，等您的老板诅咒完毕，您就不妨问一问自己，您是不是有点太急于放弃 Microsoft 的产品了？Microsoft 和 Sun 都有他们的问题，但这并不能改变这样一个事实：IIS 是一种值得尊敬的软件。并且既然您知道它可以运行 Java servlet，它的吸引力也就更大了一点。

适配器设计模式

将这两种技术粘合在一起的魔法是适配器设计模式的一种简单的应用程序。不妨引用一下一个不大出名的四人团伙（Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides）所写的书《Design Patterns: Elements of Reusable Object-Oriented Software》（参考资料），里面说：适配器模式的意图就是将一个类的接口转换为客户机期望的另一种接口。但哪些类是您必须改编的呢？答案是 Java Servlet 用来与其环境交互的几个核心类 -- 尤其是 Request、Response 和 Session 对象。幸运的是，您不必改编 Cookie 类 -- 这一转换将由其它适配器内部处理。

IIS，或者更具体地说，它的 Active Server Page (ASP) 环境，包含了一组核心类，它们实际上反映了 Java Servlet 规范的类。事实上我应该说，servlet 反映了 ASP 的框架，因为 IIS 在 servlet 规范制定以前早就有了产品，但我不想在 Microsoft 与 Sun 之间的争论中推波助澜。

两种框架中都存在 Request、Response、Session 和 Cookie 对象。唯一的问题是，这些对象的接口在两种环境之间不兼容。这正是适配器设计模式起作用的地方。您必须改编（或包装）对象的 IIS 版本，使它们的外观和行为都类似于 servlet 版本。

一个快捷而不够清晰的 servlet 概览

按最低要求，servlet 只须实现单个方法： public void doGet( HttpServletRequest request, HttpServletResponse response );

从技术上讲，如果 servlet 希望处理的客户机请求，使用的是 HTTP 的 POST 命令而不是 GET 命令，则 servlet 还必须实现 doPost 方法。但为了使本文保持简洁，您可以假定所有客户机请求都属于 GET 类型。

doGet 方法取得两个对象：请求和响应。请求对象将客户机发送给服务器的所有数据，以及有关客户机本身的一些元信息，都封装在一起。使用响应对象将数据发回客户机。这是非常抽象的说明，但本文并不是 servlet 的引论，所以我不想涉及更多的细节。有关 servlet 的优秀初级读本，我推荐 Jason Hunter，William Crawford 和 Paula Ferguson 合著的《Java Servlet Programming》 (O'Reilly & Associates)。

Active Server Page

当您从 ASP 中调用 servlet 时，您正是在调用 doGet 方法，并传入适当的请求和响应对象。从这时起，servlet 就具有了完全的控制权。ASP 脚本用作 servlet 的引导程序。但在您可以传入请求和响应对象之前，您必须用相应的适配器类来包装它们（稍后我对此将有详细分析）。

我将从头开始讲下去。客户机要请求的 URL 看起来与 http://localhost/servlet.asp类似。.asp 扩展名意味着所请求的文档是一个 Active Server Page 脚本。下面就是完整的 servlet.asp 脚本：

dim requestAdapter

set requestAdapter = getObject( "java:com.nutrio.asp.RequestAdapter" )

dim responseAdapter

set responseAdapter = getObject( "java:com.nutrio.asp.ResponseAdapter" )

dim servlet

set servlet = getObject( "java:com.nutrio.servlet.HelloWorldServlet" )

servlet.doGet requestAdapter, responseAdapter

分析上一段脚本，您将发现，它是从声明一个称为 requestAdapter 的变量入手的。dim 是 Visual Basic 中对变量进行声明的命令。在 Visual Basic 中，变量没有固定类型，各种变量实际上是由 Variant 对象进行包装的，这可使变量以调用代码所要求的任何一种类型（例如，数字、字符串，等等）出现。这样做确实非常方便，但有可能使代码容易混淆，而且也不安全。这正是为什么要发明“匈牙利表示法”(Hungarian Notation) 的原因（请参阅参考资料）。不过，这完全是另外一场争论。

声明变量以后，就应该使用 ASP 的 getObject 方法实例化您的第一个适配器类，并相应地为其赋值。getObject 方法是 IIS 版本 4 中新增的。它被称为 moniker （一种 COM 对象，用于创建其它对象的实例，请参阅参考资料），但它使您能够访问 Java 对象，而没有注册任何组件对象模型（COM，请参阅参考资料）这类令人头痛的事。然后，您依次声明、实例化响应封装并为其赋值，然后对 servlet 做同样的事情。最后，您调用 servlet 的 doGet 方法，并传入改编了的请求和响应对象。

这个特定的脚本具有相当大的局限性，因为它只启动一个特定的 servlet。您很可能希望将其扩展，以启动一整套 servlet，这样您就需要做几处小小的修改。假定您的所有 servlet 都在同一个包内，您就可以将目标 servlet 的类名作为变量传递给 URL，例如 http://localhost/servlet.asp?class=HelloWorldServlet

dim className

set className = Request.QueryString( "class" )

dim servlet

set servlet = getObject( "java:com.nutrio.servlet." & className )

servlet.doGet requestAdapter, responseAdapter

这就行了！您已经将 Microsoft 的 Internet Information Server 变成了 Java Servlet 引擎。正如您将在下面看到的那样，它虽然不是一个完美的引擎，但已相当接近完美。剩下要讨论的全部内容就是适配器类的本质了。

为简洁起见，在每种适配器中，我将只讨论如何实现那些较流行的方法。对流行程度的度量是以我的个人经验和看法为依据的；没有比这更科学的了（这是我引用的原话）。

Microsoft 的 Java SDK

从请求的封装开始，对象必须做的第一件事情就是，获取其 ASP 对应物的一个引用。这是通过 com.ms.iis.asp 包中的 AspContext 对象完成的。您问是什么包吗？对了，这里我正要解释您为什么需要安装 Microsoft 的 Java SDK。

Microsoft 的 Java SDK 可以免费下载（请参阅参考资料）。要确保您获得的是最新版本，我写这篇文章时为 4.0。按照简单的安装说明进行操作，并在出现提示时重新启动（令人叹息！）。安装 SDK 之后，相应调整您的 PATH 和 CLASSPATH 环境变量。采取点聪明的小技巧，在您的系统中搜索 jview.exe 的全部实例，然后确保最新版本在您的 PATH 中最先解析。

不幸的是，Microsoft 的 Java SDK 附带的文档和样例代码中涉及 IIS/ASP 集成的内容非常少。当然有许多夸夸其谈 -- 您得到了有关这一主题的整个已编译的 HTML 文档，但在大多数地方，它给人的感觉与其说是说明性的，不如说是自相矛盾的和含混不清的。谢天谢地，SDK 的 Samples 目录中有一个 aspcomp 包，它实际上反映了 com.ms.iis.asp 包，并提供了源代码。您在安装 SDK 时确实安装了这些样例文件，对吗？这个 aspcomp 包帮助我反向设计出许多 API 逻辑。

请求适配器

既然您有了可随意使用的 Microsoft 的 SDK，您就可以回头继续实现适配器类了。下面是请求适配器的最基本的版本。我已省略了包声明和 import 语句，以便您可将注意力集中在代码的实质部分。

public class RequestAdapter implements HttpServletRequest

{

    private Request request;

    public RequestAdapter()

    {

        this.request = AspContext.getRequest();

    }

请注意，该类引出了一个单一的 public 构造函数，它不带任何参数。这是 ASP 脚本将该类实例化为一个 moniker 所必需的（通过 getObject 方法）。此构造函数只须从 AspContext 对象取得 ASP 的请求对象的一个引用，并存储指向它的一个指针。此适配器实现 HttpServletRequest 接口，它允许您在一种真实的 servlet 环境的外观之下，将其传递给您的 servlet。

请求对象的最常用方法是 getParameter。此方法用于检索客户机预计要提供的一段数据。例如，如果客户机刚填好了一个表单并将其提交给服务器，则 servlet 将调用 getParameter 来检索每个表单项的值。

在请求对象的 ASP 版本中，Microsoft 将区分通过 GET 到达的参数和通过 POST 到达的参数，您必须分别调用 getQueryString 或 getForm。在 servlet 版本中，在请求级别上不存在这种差别，因为用 GET 模式还是用 POST 模式，是在调用 doGet 或 doPost 时才规定的。因此，当您改编 getParameter 方法时，对于所需的值，您必须既查看查询字符串，又查看表单集合。

还有一点需要注意，当参数不存在时，Microsoft 版本中将返回一个空字符串，而 Sun 版本中将返回 null。考虑到这一点，您必须检查空字符串并在相应位置使返回值为 null。

public String getParameter( String str )

{

    String result = request.getQueryString().getString( str );

    if( ( result != null ) && result.trim().equals( "" ) )

    {

        result = request.getForm().getString( str );

        if( ( result != null ) && result.trim().equals( "" ) )

        {

            return( null );

        }

    }

    return( result );

}

这样做相当简单，但期望值不要太高，因为事情即将变得更为复杂。这是因为，在 servlet 中，请求对象也引出一个称为 getParameterNames 的方法，对于客户机提供的每段数据，它都会返回关键字的一个 Enumeration。如上所述，就 servlet 而论，它是一个单一入口点，但是 ASP 则要区分 GET 提供的数据和 POST 提供的数据。为了向 servlet 返回单一的 Enumeration，必须将 ASP 请求对象的查询字符串和表单集合这两种 Enumeration 组合起来。下面是我为了解决这个问题临时编写的一个方便的小工具。此工具称为 EnumerationComposite（请不要将它与 Composite 设计模式相混淆），它使用一个 RequestDictionary（ASP 版本的 Hashtable）数组，并将这两种 Enumeration 连接起来，形成一个大的 Enumeration。下面是完整的代码：

public class EnumerationComposite implements Enumeration

{

    private RequestDictionary[] array;

    private int stackPointer = 0;

    public EnumerationComposite( RequestDictionary[] array )

    {

        this.array = array;

    }

    public boolean hasMoreElements()

    {

        if( this.stackPointer >= this.array.length )

        {

            return( false );

        }

        else if( this.array[ this.stackPointer ].hasMoreItems() )

        {

            return( true );

        }

        else

        {

            this.stackPointer += 1;

            return( this.hasMoreElements() );

        }

    }

    public Object nextElement()

    {

        return( this.array[ this.stackPointer ].nextItem() );

    }

}

未完.....


将 Microsoft 的 Internet Information Server 用作 Java servlet 引擎（转）

作者：zergman

email: zergman@chinaasp.com

日期：2001-6-15 13:32:31

[b]将 Microsoft 的 Internet Information Server 用作 Java servlet 引擎[/b]

[b]用 Microsoft 的 IIS 运行 Java servlet -- 而不牺牲可移植性[/b]

Thomas E. Davis

Nutrio.com 首席技术官

2000 年 7 月

[TABLE][TR][TD][#ffffff][B]内容：[/B][/#][/TD][/TR][TR][TD][img]http://www.cn.ibm.com/developerWorks/i/c.gif[/img][/TD][/TR][TR][TD][img]http://www.cn.ibm.com/developerWorks/i/c.gif[/img][/TD][/TR][TR][TD][url href=http://www.cn.ibm.com/developerWorks/java/jw-iis/index.shtml#1]IIS[/url][/TD][/TR][TR][TD] [url href=http://www.cn.ibm.com/developerWorks/java/jw-iis/index.shtml#2]适配器设计模式[/url][/TD][/TR][TR][TD] [url href=http://www.cn.ibm.com/developerWorks/java/jw-iis/index.shtml#3]Servlet[/url][/TD][/TR][TR][TD] [url href=http://www.cn.ibm.com/developerWorks/java/jw-iis/index.shtml#4]ASP[/url][/TD][/TR][TR][TD] [url href=http://www.cn.ibm.com/developerWorks/java/jw-iis/index.shtml#5]Java SDK[/url][/TD][/TR][TR][TD] [url href=http://www.cn.ibm.com/developerWorks/java/jw-iis/index.shtml#6]请求适配器[/url][/TD][/TR][TR][TD] [url href=http://www.cn.ibm.com/developerWorks/java/jw-iis/index.shtml#7]会话适配器[/url][/TD][/TR][TR][TD] [url href=http://www.cn.ibm.com/developerWorks/java/jw-iis/index.shtml#8]响应适配器[/url][/TD][/TR][TR][TD] [url href=http://www.cn.ibm.com/developerWorks/java/jw-iis/index.shtml#9]结论[/url][/TD][/TR][TR][TD] [url href=http://www.cn.ibm.com/developerWorks/java/jw-iis/index.shtml#10]正式请求[/url][/TD][/TR][TR][TD] [url href=http://www.cn.ibm.com/developerWorks/java/jw-iis/index.shtml#resources]参考资料[/url][/TD][/TR][TR][TD] [url href=http://www.cn.ibm.com/developerWorks/java/jw-iis/index.shtml#author]作者简介[/url][/TD][/TR][TR][TD][img]http://www.cn.ibm.com/developerWorks/i/c.gif[/img][/TD][/TR][/TABLE]

[BLOCKQUOTE]您是陷在仅有 Microsoft 产品的商店中的 Java 狂热分子吗？仅使用 Microsoft 的 Internet Information Server 和纯 Java，您就可以运行 Java servlet，而无须任何第三方产品的帮助。请不要马上就放弃 Microsoft 产品 -- 不妨试试本文中说明的方法，并仍然保持为无专有权的可移植代码。[/BLOCKQUOTE]

您知不知道，无须第三方产品，就可以用 Microsoft 的 Internet Information Server (IIS) 运行 JavaServlet？您的全部所需仅仅是简明的旧式 IIS 和纯 Java。尽管您需要使用 Microsoft 的 Java SDK（原因我将在下面解释），但尽可放心，您的代码将不需要任何专用权扩展，同时又保持对其它 servlet 引擎的完全可移植性。

[b]Microsoft 的 Internet Information Server[/b]

但您为什么要做这样的傻事，让 Java servlet 运行在并非为它设计的环境中呢？首先，由于我们无法控制的情况，许多像我们这样顽固的 Java 狂热分子都陷在了仅有 Microsoft 产品的商店里。我们全都把 Linux 软件盒子藏在桌子下面，运行 IBM 最新的 JDK 和 Apache 最新的 servlet 引擎，但是，要是老板不让我们在这样一个系统上部署一些产品，这时可就真的不好过了。您当然可以找到运行于 Microsoft 平台上的商业性 servlet 引擎，但购买它们要花大把大把的钱。试一试向您的老板解释：您需要几千美金购买一种新的 Web 服务器，因为您想要废弃操作系统附带的那个免费产品（或者只将它用作简单的传递代理，这正是许多产品目前的工作方式）。然后，等您的老板诅咒完毕，您就不妨问一问自己，您是不是有点太急于放弃 Microsoft 的产品了？Microsoft 和 Sun 都有他们的问题，但这并不能改变这样一个事实：IIS 是一种值得尊敬的软件。并且既然您知道它可以运行 Java servlet，它的吸引力也就更大了一点。

[b]适配器设计模式[/b]

将这两种技术粘合在一起的魔法是适配器设计模式的一种简单的应用程序。不妨引用一下一个不大出名的四人团伙（Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides）所写的书《[I]Design Patterns: Elements of Reusable Object-Oriented Software[/I]》（[url href=http://www.cn.ibm.com/developerWorks/java/jw-iis/index.shtml#resources]Java Servlet 用来与其环境交互的几个核心类 -- 尤其是 Request、Response 和 Session 对象。幸运的是，您不必改编 Cookie 类 -- 这一转换将由其它适配器内部处理。

IIS，或者更具体地说，它的 Active Server Page (ASP) 环境，包含了一组核心类，它们实际上反映了 Java Servlet 规范的类。事实上我应该说，servlet 反映了 ASP 的框架，因为 IIS 在 servlet 规范制定以前早就有了产品，但我不想在 Microsoft 与 Sun 之间的争论中推波助澜。

两种框架中都存在 Request、Response、Session 和 Cookie 对象。唯一的问题是，这些对象的接口在两种环境之间不兼容。这正是适配器设计模式起作用的地方。您必须改编（或包装）对象的 IIS 版本，使它们的外观和行为都类似于 servlet 版本。

[b]一个快捷而不够清晰的 servlet 概览[/b]

按最低要求，servlet 只须实现单个方法： [TABLE][TR][TD][PRE]public void doGet( HttpServletRequest request, HttpServletResponse response );[/PRE][/TD][/TR][/TABLE]

从技术上讲，如果 servlet 希望处理的客户机请求，使用的是 HTTP 的 POST 命令而不是 GET 命令，则 servlet 还必须实现 doPost 方法。但为了使本文保持简洁，您可以假定所有客户机请求都属于 GET 类型。

doGet 方法取得两个对象：请求和响应。请求对象将客户机发送给服务器的所有数据，以及有关客户机本身的一些元信息，都封装在一起。使用响应对象将数据发回客户机。这是非常抽象的说明，但本文并不是 servlet 的引论，所以我不想涉及更多的细节。有关 servlet 的优秀初级读本，我推荐 Jason Hunter，William Crawford 和 Paula Ferguson 合著的《[I]Java Servlet Programming[/I]》 (O'Reilly & Associates)。

[b]Active Server Page[/b]

当您从 ASP 中调用 servlet 时，您正是在调用 doGet 方法，并传入适当的请求和响应对象。从这时起，servlet 就具有了完全的控制权。ASP 脚本用作 servlet 的[I]引导程序[/I]。但在您可以传入请求和响应对象之前，您必须用相应的适配器类来包装它们（稍后我对此将有详细分析）。

我将从头开始讲下去。客户机要请求的 URL 看起来与 http://localhost/servlet.asp类似。.asp 扩展名意味着所请求的文档是一个 Active Server Page 脚本。下面就是完整的 servlet.asp 脚本：[TABLE][TR][TD][PRE]dim requestAdapterset requestAdapter = getObject( "java:com.nutrio.asp.RequestAdapter" )dim responseAdapterset responseAdapter = getObject( "java:com.nutrio.asp.ResponseAdapter" )dim servletset servlet = getObject( "java:com.nutrio.servlet.HelloWorldServlet" )servlet.doGet requestAdapter, responseAdapter[/PRE][/TD][/TR][/TABLE]

分析上一段脚本，您将发现，它是从声明一个称为 requestAdapter 的变量入手的。dim 是 Visual Basic 中对变量进行声明的命令。在 Visual Basic 中，变量没有固定类型，各种变量实际上是由 Variant 对象进行包装的，这可使变量以调用代码所要求的任何一种类型（例如，数字、字符串，等等）出现。这样做确实非常方便，但有可能使代码容易混淆，而且也不安全。这正是为什么要发明“匈牙利表示法”(Hungarian Notation) 的原因（请参阅[url href=http://www.cn.ibm.com/developerWorks/java/jw-iis/index.shtml#resources]

声明变量以后，就应该使用 ASP 的 getObject 方法实例化您的第一个适配器类，并相应地为其赋值。getObject 方法是 IIS 版本 4 中新增的。它被称为 [I]moniker[/I] （一种 COM 对象，用于创建其它对象的实例，请参阅[url href=http://www.cn.ibm.com/developerWorks/java/jw-iis/index.shtml#resources]Java 对象，而没有注册任何组件对象模型（COM，请参阅[url href=http://www.cn.ibm.com/developerWorks/java/jw-iis/index.shtml#resources]参考资料[/url]）这类令人头痛的事。然后，您依次声明、实例化响应封装并为其赋值，然后对 servlet 做同样的事情。最后，您调用 servlet 的 doGet 方法，并传入[I]改编了的[/I]请求和响应对象。

这个特定的脚本具有相当大的局限性，因为它只启动一个特定的 servlet。您很可能希望将其扩展，以启动一整套 servlet，这样您就需要做几处小小的修改。假定您的所有 servlet 都在同一个包内，您就可以将目标 servlet 的类名作为变量传递给 URL，例如 http://localhost/servlet.asp?class=HelloWorldServletclassNameset className = Request.QueryString( "class" )dim servletset servlet = getObject( "java:com.nutrio.servlet." & className )servlet.doGet requestAdapter, responseAdapter[/PRE][/TD][/TR][/TABLE]

这就行了！您已经将 Microsoft 的 Internet Information Server 变成了 Java Servlet 引擎。正如您将在下面看到的那样，它虽然不是一个完美的引擎，但已相当接近完美。剩下要讨论的全部内容就是适配器类的本质了。

为简洁起见，在每种适配器中，我将只讨论如何实现那些较流行的方法。对流行程度的度量是以我的个人经验和看法为依据的；没有比这更科学的了（这是我引用的原话）。

[b]Microsoft 的 Java SDK[/b]

从请求的封装开始，对象必须做的第一件事情就是，获取其 ASP 对应物的一个引用。这是通过 com.ms.iis.asp 包中的 AspContext 对象完成的。您问是[I]什么[/I]包吗？对了，这里我正要解释您为什么需要安装 Microsoft 的 Java SDK。

Microsoft 的 Java SDK 可以免费下载（请参阅[url href=http://www.cn.ibm.com/developerWorks/java/jw-iis/index.shtml#resources]4.0。按照简单的安装说明进行操作，并在出现提示时重新启动（令人叹息！）。安装 SDK 之后，相应调整您的 PATH 和 CLASSPATH 环境变量。采取点聪明的小技巧，在您的系统中搜索 jview.exe 的全部实例，然后确保最新版本在您的 PATH 中最先解析。

不幸的是，Microsoft 的 Java SDK 附带的文档和样例代码中涉及 IIS/ASP 集成的内容非常少。当然有许多夸夸其谈 -- 您得到了有关这一主题的整个已编译的 HTML 文档，但在大多数地方，它给人的感觉与其说是说明性的，不如说是自相矛盾的和含混不清的。谢天谢地，SDK 的 Samples 目录中有一个 aspcomp 包，它实际上反映了 com.ms.iis.asp 包，并提供了源代码。您在安装 SDK 时确实安装了这些样例文件，对吗？这个 aspcomp 包帮助我反向设计出许多 API 逻辑。

[b]请求适配器[/b]

既然您有了可随意使用的 Microsoft 的 SDK，您就可以回头继续实现适配器类了。下面是请求适配器的最基本的版本。我已省略了包声明和 import 语句，以便您可将注意力集中在代码的实质部分。[TABLE][TR][TD][PRE]public class RequestAdapter implements HttpServletRequest{    private Request request;    public RequestAdapter()    {        this.request = AspContext.getRequest();    }[/PRE][/TD][/TR][/TABLE]

请注意，该类引出了一个单一的 public 构造函数，它不带任何参数。这是 ASP 脚本将该类实例化为一个 moniker 所必需的（通过 getObject 方法）。此构造函数只须从 AspContext 对象取得 ASP 的请求对象的一个引用，并存储指向它的一个指针。此适配器实现 HttpServletRequest 接口，它允许您在一种真实的 servlet 环境的外观之下，将其传递给您的 servlet。

请求对象的最常用方法是 getParameter。此方法用于检索客户机预计要提供的一段数据。例如，如果客户机刚填好了一个表单并将其提交给服务器，则 servlet 将调用 getParameter 来检索每个表单项的值。

在请求对象的 ASP 版本中，Microsoft 将区分通过 GET 到达的参数和通过 POST 到达的参数，您必须分别调用 getQueryString 或 getForm。在 servlet 版本中，在请求级别上不存在这种差别，因为用 GET 模式还是用 POST 模式，是在调用 doGet 或 doPost 时才规定的。因此，当您改编 getParameter 方法时，对于所需的值，您必须既查看查询字符串，又查看表单集合。

还有一点需要注意，当参数不存在时，Microsoft 版本中将返回一个空字符串，而 Sun 版本中将返回 null。考虑到这一点，您必须检查空字符串并在相应位置使返回值为 null。[TABLE][TR][TD][PRE]public String getParameter( String str ){    String result = request.getQueryString().getString( str );    if( ( result != null ) && result.trim().equals( "" ) )    {        result = request.getForm().getString( str );        if( ( result != null ) && result.trim().equals( "" ) )        {            return( null );        }    }    return( result );}[/PRE][/TD][/TR][/TABLE]

这样做相当简单，但期望值不要太高，因为事情即将变得更为复杂。这是因为，在 servlet 中，请求对象也引出一个称为 getParameterNames 的方法，对于客户机提供的每段数据，它都会返回关键字的一个 Enumeration。如上所述，就 servlet 而论，它是一个单一入口点，但是 ASP 则要区分 GET 提供的数据和 POST 提供的数据。为了向 servlet 返回单一的 Enumeration，必须将 ASP 请求对象的查询字符串和表单集合这两种 Enumeration 组合起来。下面是我为了解决这个问题临时编写的一个方便的小工具。此工具称为 EnumerationComposite（请不要将它与 Composite 设计模式相混淆），它使用一个 RequestDictionary（ASP 版本的 Hashtable）数组，并将这两种 Enumeration 连接起来，形成一个大的 Enumeration。下面是完整的代码：[TABLE][TR][TD][PRE]public class EnumerationComposite implements Enumeration{    private RequestDictionary[] array;    private int stackPointer = 0;     public EnumerationComposite( RequestDictionary[] array )    {        this.array = array;    }    public boolean hasMoreElements()    {        if( this.stackPointer >= this.array.length )         {            return( false );        }        else if( this.array[ this.stackPointer ].hasMoreItems() )        {            return( true );        }        else        {            this.stackPointer += 1;            return( this.hasMoreElements() );        }    }    public Object nextElement()    {        return( this.array[ this.stackPointer ].nextItem() );    }}[/PRE][/TD][/TR][/TABLE]

现在这个工具极大地简化了您的工作。而 getParameterNames 方法就具有了类似下面的形式：[TABLE][TR][TD][PRE]public Enumeration getParameterNames(){    return(        new EnumerationComposite(            new RequestDictionary[] {                request.getQueryString(),                request.getForm() } ) );}[/PRE][/TD][/TR][/TABLE]

响应对象的下一个最常用的方法是 getSession。会话对象是另一个核心对象，它在 ASP 和 servlet 之间互为映像。因此，您提供的会话就必须拥有自己的适配器，稍后我会对此加以说明。但在我说明之前，请先看这个请求方法：[TABLE][TR][TD][PRE]public HttpSession getSession( boolean flag ){    return( new SessionAdapter() );}[/PRE][/TD][/TR][/TABLE]

在本文中，需要改写的请求对象的最后一个方法是 getCookies。顾名思义，它返回客户机所提供的 cookie 的集合。ASP 版本的 cookie 对象使我感到为难，它似乎用作自身的一个集合，但又引出了许多具有莫明其妙的功能的方法。但是我能够对脚本进行充分的剖析来改写 servlet。由于 ASP 版本中返回 Enumeration，而 servlet 版本中则返回一个数组，这样就可以使用 Vector 类中一个不常用的方法 copyInto，来实现这种转换，这是唯一的小窍门。另外请注意，由于在 com.ms.iis.asp 包和 javax.servlet.http 包中，类名是完全相同的，因此我们不得不明确指明每个 Cookie 对象的包名。代码如下：[TABLE][TR][TD][PRE]public javax.servlet.http.Cookie[] getCookies(){    Vector tmpList = new Vector();    CookieDictionary aspCookies = this.request.getCookies();    IEnumerator e = aspCookies.keys();    while( e.hasMoreItems() )    {        String key = (String) e.nextItem();        String val = aspCookies.getCookie( key ).getValue();        tmpList.addElement( new javax.servlet.http.Cookie( key, val ) );    }    javax.servlet.http.Cookie[] cookies = new javax.servlet.http.Cookie         [ tmpList.size() ];    tmpList.copyInto( cookies );    return( cookies );}[/PRE][/TD][/TR][/TABLE]


精彩专题，不能不看《微软 vs Sun》!!

作者：小狮子

日期：2001-4-16 19:54:38

 微软和Sun看来永远就是死对头。为了避免JAVA技术授权的问题，微软发布了类似于JAVA的C#，而微软刚发布.Net战略不到8个月，Sun也推出了他们的发展新战略Sun ONE。看来微软和Sun的竞争已经升级。新的较量开始了！

专题网址：http://www.itpeople.com.cn/tech/windows_vs.asp

内容提要：

透析Sun的软件发展新战略Sun ONE

--------------------------------------------------------------

关于Sun公司“Sun ONE”战略的介绍。

关于Java的点滴看法

--------------------------------------------------------------

关于JAVA的一些介绍，可以对java有一个初步的认识。

且看微软的.Net和Sun公司的J2EE如何对垒

--------------------------------------------------------------

面对微软推出的.Net FRAMEWORK，你可能会有以下疑问：

准确地讲.Net平台是什么？

如何将.Net的体系结构和J2EE对比？

从.Net的体系结构演绎出的一整套关于企业软件开发方案中我们能学到此什么？

在本文中作者将为你解开这些疑问。

微软vs Sun中盘较量

---------------------------------------------------------------

一篇非常精彩的文章，介绍了微软和Sun之间竞争的渊源。并对微软的.net战略和Sun的Sun ONE战略做了一些分析和比较。

ASP.NET 与JAVA 的比较

 --------------------------------------------------------------

ASP.NET提供了WEB FORM、WEB CONTROL、WEB SERVIEC等等面向WEB 应用的改进；JAVA 则以其出色的跨平台特性，良好的面向对象属性获得了今天WEB开发的主流地位。这二者谁将在明天的竞争中获胜？

C#和Java之比较

----------------------------------------------------------------

C#是微软用来和Java抗衡的武器，因为二者在很大程度上有着惊人的相似，尽管如此，两者不同的地方也很多，所谓“于细微处见差异”。那么两者的相似和区别都在什么地方呢？我们从各个角度来对比C#和Java的特点，希望能对正在学习、使用C#的朋友有所帮助。

[英文] C#程序编译及运行机制

----------------------------------------------------------------

本文对比JAVA，阐述了C#的基本编译和运行机制。

JSP 和 ASP.NET的比较

----------------------------------------------------------------

一个ASP和JSP的程序员，对ASP.NET和JSP的认识和比较。

专题网址：http://www.itpeople.com.cn/tech/windows_vs.asp


具体见内！

作者：我心激扬

email: ldzsl@china.com

日期：2000-11-7 17:03:14

A:在server.xml

这是它的例程配置

        <Context path="/examples" docBase="webapps/examples" debug="0" reloadable="true" >

        </Context>

所以我就

        <Context path="这里是要设的别名，我用/jsp" docBase="这里是你的要设的目录名" debug="0" reloadable="true" >

        </Context>

还有apache+tomcat的连接，我没有连成功，所以，我是把哪个连接文件加在apache的http.conf

文件后面

这是连接文件的例程配置

Alias /examples "C:\tomcat\webapps\examples"

<Directory "C:\tomcat\webapps\examples">

    Options Indexes FollowSymLinks

</Directory>

ApJServMount /examples/servlet /examples

<Location /examples/WEB-INF/ >

    AllowOverride None

    deny from all

</Location>

所以我做的改动是

Alias /jsp "这里是你的要设的目录名"

<Directory "这里是你的要设的目录名">

    Options Indexes FollowSymLinks

</Directory>

ApJServMount /jsp/servlet /jsp

<Location /jsp/WEB-INF/ >

    AllowOverride None

    deny from all

</Location>


看看String类的构造方法

作者：sonymusic

email: sonymusic@china.com

日期：2001-7-1 23:24:49

String

public String(byte[] bytes,

              String enc)

       throws UnsupportedEncodingException

Construct a new String by converting the specified array of bytes using the specified character encoding. The length of the new String is a function of the encoding, and hence may not be equal to the length of the byte array.

Parameters:

bytes - The bytes to be converted into characters

enc - The name of a supported character encoding

Throws:

UnsupportedEncodingException - If the named encoding is not supported

Since:

JDK1.1


看看也好：Java vs .NET 非官方观点

作者：大哥

email: programmers@sina.com

日期：6/27/2001 8:10:19 PM

最近这半年的讲师生涯之中,遇到不少从VB转到Java的学生,绝大大部分

的工程师或新进软件开发领域的朋友都会问:"我该学.NET 还是Java ?"

"该学C# 还是Java ?"相关的主题在网路上可谓暗潮汹涌,随时可能擦枪

走火,在发表本文之前, 自己把C# Spec 深入地读了一次 ,也大致研读了

.NET的相关技术文章所以想想在此发表一心得,没有什么预设立场,请大

家多多包含。

本文适用对象:

"本文内容只适用一般工程师,特殊情况不算!"

何谓特殊情况呢?以我个人为例,明明知道Delphi的原生语言是Object

Pascal, 所以要用Object Pascal才能和Delhpi融为一体的感觉.可是,

开发Project 的时候,我就是莫名其妙地喜欢用C++Builder.当"程式语

言基本教义派"或"程式语言民族主义"遇到正常行为的时候,是怎么说

也说不清的。(以上是李敖先生说的话,我只把关键字换了,希望不会被

他告)另外一种特殊情况就是,如果您的思考方式与普通人不同,那么,

本文亦不适用於您。

首先想讨论的是架构的问题:

笔者并没有深入研究过架构,也实在不清楚网路上诸位先贤所谓的架

构指的是什么.但是我凭一个工程师的直觉可以笃定,".NET就根基架

构上一定比Java的根基架构还要好!"因为.NET的推出比Java晚了近5

年,中间还有J++与WFC的发展,如果搞了个比Java还鸟的东西,像话吗?

只要Sun在这次帝国大反击之後,没有被MS弄得节节败退,从此消失於

市场,相信它若干年後还会推出个比.NET还好的东西,然後若干年後MS

又搞出一个更好的东西.事实上,科技的演进就是如此.所以谁强谁不

强,纯粹是历史必然的结果.所以没有什么好争论的.至於将来谁会是

市场的霸主,就看两家龙头太厂谁比较会行销,谁比较亲近工程师.虽

然资讯界过去曾经好几次重复"好东西被干掉,次级品占领市场"的历

史.但是仍然没有人敢断言。

其次是压宝谁的问题:

Sun的标语是"网路即电脑",所以在Java的策略上,Sun假设这个世界

上充满异质平台,所以设计Java 的时候希望以单一一个Java程式语

言为中心,让它可以在各种平台上执行.因此JVM要采stack machine

的设计方式,因为不管你是谁,都可以实作出stack machine.但是这

毕竟是一个不理想的世界,连Sun自己Solaris都要好几个patch才能

使用新版的JDK.连Borland也花了三年以上的努力才让 JBuilder可

以同时推出Windows , Linux , Solaris , Mac OS X 四种版本,可

见"Write once, run anywhere"的理想真的有其实际上的难度.

Borland除了证明Sun 的理想是有机会实现的礼运大同篇,也证明经

过精心的架构设计和最佳化,加上工程师累积了许多经验後,仍然可

以突破stack machine 先天上速度慢的原罪(虽然还是不理想,需要

用很多RAM来填补理想).Sun把其官方工具JDK搞的很难用,网路上老

是有人抱怨它是Java Developer Killer,虽然.NET Framework SDK

一样难用,可是微软至少还有Visual Studio.NET如果说微软的Visual

Studio.NET让工程师感觉像在开Benz,那么Forte就很像路上随便一

台前方贴著Benz标记的烂公车.所以Sun实在需要多多加强.不过这

也难怪,MS是纯软件起家,Sun是纯硬体起家,Sun做软件做不过MS是

正常的事情.

反观MS,它假设世界上只有x86的CPU,或者广泛的说是Wintel 平台,

并假设世界上存在许多会各种语言的工程师们.因为以此假设为策

略中心,所以设计.NET的时候CLR可以针对x86 CPU 作最佳设计,有

些指令甚至可以和op code达到1 to 1的mapping,所以速度自然跑

的快.但是叫他移到SPARC或AS/400上就麻烦大了..

就工程师的角度,自然会偏向.NET,因为选择很多(喔,这点有待商榷,

後面会提到),但是就3rd party 厂商的利益和老板不想被特定平台

绑死的观点来说,会偏向Java。

各位想, .NET的WinForm设计的那么漂亮,很可能累积了Anders离开

Borland之後的许多好点子,如果.NET出来,Borland大概真的只剩下

IDE可以做了.所以最後谁的利益胜出,很可能就会决定输赢,

因此笔者曰:"你想信耶稣就信耶稣,想信佛教就信佛教,你心中的神

自然会带领你走向他口中的天堂."喔,对了,如果两边都要压宝的人

当然没问题,但是请一定要小心,不要到时候上不了其中一个神所谓

的天堂,却同时下了两个神口中的地狱啊!

再来是Programming Model的问题:

记忆中,上一次给我惊艳感觉的时候是Delphi出来的时候,让我第一

次觉得写程式是很美好,很简单的事情,可是自从那次的美好经验之

後,一直到目前为止,都没有任何新意让人有耳目一新的感觉,总让我

觉得软件的开发方式是不是已经走到一个瓶颈了呢?Anders搞VCL的

时候,Borland说它是VB Killer, Anders 搞.NET,MS 当.NET 是Java

Killer,怎么每次Anders出现的时候都会出现杀手呢? 我建议以後

改叫他enders算了,大家觉得如何?

总之,元件架构不管是VCL, JavaBean, WFC, .NET Framework,搞了

搞去都是setXXX()与getXXX()那一套,进步也只是小迈步,不是大跃

进.以前语言里头不支援,所以要搞特殊语法,现在搞个新语言来支援,

然後把他标准化罢了.自从Jordon退休之後,NBA就变得有点不热闹了,

但我还真希望既Anders之後,有新一代的鬼才提出另外让我们工程师

轻松的Programming Model呀!!

最後是语言上的问题:

C#比Java复杂多了,多了很多关键字,让我觉得很复杂,先说型态好了,

C#提供value type与reference type,value type还分成simple type

,enum type , struct type,reference type还分成class type ,

interface type , delegate type , array type.这些型态如果在配

上传递参数时可以选用纯粹的by value , by reference(ref) , ouput

(out) , parameter array感觉上还真是热闹哩!!c#之中我们还可以利

用unsafe关键字对指标直接做存取.存取权限修饰字除了固有的public,

protected , private之外,也加了internal , protected internal.

在多型上,除了以前的virtual之外,也加了override关键字.喔,当然有

人会说我何必庸人自扰,但是如果您的汽车有类似霹雳车可以Turbo的功

能,我想任何有好奇心的人都会想找机会试看看他的极限在哪里,才不管

会不会粉身碎骨.Java程式语言的功能有限,工程师遇到特定问题通常会

找其他特定的解决方案,这么一来程式在维护上会比较容易.但是语言复

杂了,同一件事情有好多种做法,自由度变大了,工程师发挥的好,就可以

突破很多限制,实现自己梦想.但是维护上呢?

君不见有些天才可以用C简单几行程式写出求PI值的程式,但是有多少人

看的懂?个人主义和团队合作之间是有一些gap存在。语言的简单与复杂

之间,实在是个dilemma.如果程式语言里头都是宣告,回圈,函式呼叫这

三种基本要素,那么要设计出很复杂,功能超强的程式语言都没问题的,

不是吗?Sun大可在Java里头开始引进很多C++的功能,可是它没有,原因

何在?C#的复杂度,如果加上operator和template,那么和C++可有的拼哩!

每次上Java课程时有好多好多的VB Programmer跟我说他无法提升上来,

虽然我要负大部分的责任,但是我开始怀疑如何把VB Programmer提升到

.NET的层级呢?我说的VB Programmer当然不是少数精通各种语言,用VB

只是纯粹好玩的工程师,而是指目前台湾资讯业界的许多中坚分子和SOHO

族,当然,有人会叫我去用VB.NET, COBOL.NET,问题是,大家有没有想过

为什么C++那么强大,可是还是很多人喜欢用C ?为什么开始有人改用Java ?

为什么虽然Pascal不是主流,可是大家还是喜欢用Delphi ?

现在打出MS的行销广告告诉所有工程师一个讯息:"各种语言的工程师都

可以广纳在.NET之下."相信去参加过微软或其他研讨会之後的朋友,除了

发现.NET很不错,很棒,有很多功能是自己过去梦寐以求的功能之外,也发

现他们被MS骗了(有人跟我说他觉得他被MS抛弃了),因为他们发现过去学

的根本没用,啥米VB.NET,要他去学那种VB,他宁愿去学C#,反正比起VB.NET

的语法,C#也没难到哪里.

http://www.vbqa.com/discuss/vb.net/read.asp?id=11

这个论坛里头,ChrisTorng先生说的很好:

"况且 vb.net与c#的相似度，依我看比vb.net与vb6的相似度还高…^_^"

任何语言,要能够在CLR上发挥功能,就必须符合CLS的规范,意思就是说,

VB.NET不会因为它的前面有VB两个字,就可以让过去用VB的朋友仍然有著

绝佳的生产力.如果语法没有扩充,您相信VB.NET写的component可以被C#

用吗?我是指"优美的用法",而不是旁门走道绕了一大圈之後的用法.

MS在.NET Framework SDK beta 2里头,据说把VB改回来了,虽然不知道修

正的幅度多大,那么这个新生代的VB可以发挥多少CLR的能力呢? 大家就

拭目以待罗!

我只能打个比喻,同样的马力,扭力以及内部装潢,载女朋友的时候你要用

的车子是长的像BMW的样子? 还是长的像电子花车?用VB用到很精通的情况

之下,所以的工程师都会发现,要打破任督二脉,就必须去了解COM,否则永

远无法突破,这个在.NET里头一样适用,要发挥.NET的能力,最後一定往C#走.

所以要在.NET上发展程式,我绝对选原生语言C#,不会用VB.NET.因为,车子

也不会因为他长的像飞碟,就可以不用轮子行走呀!

那么,同样是交通工具,台北车站到天珑,你会选飞机还是计程车?

所以有些事情,在Windows平台上,

Java或许做得到,但是做起来很复杂又不美观的时候,我用.NET.

写GUI程式的时候我会选VB或Delphi,至少不会像JBuilder和Visual.NET

一样,只是个简单的GUI,改了点property,竟然跑出一大堆让我不知道从

何改起的程式码.每次看到这些无聊又重复的程式码,让我久久无法言语。

结语:

不管是大陆还是台湾的工程师,都存在著"广义中国人"的劣根性,东西还

没出来,就可以诸多猜测,跟我们电视上那些算命的半仙没啥两样..而且

,大家还真喜欢算命耶!难怪我妈看到我在读The Art of Computer

Programming的时候跟我说,程式设计师和巫师没啥两样,只不过他们靠

乌龟壳和咒语,而我们靠的是程式语言.我们总是人家在煮米粉,我们再

喊烧,一大堆会写病毒的高手,一大票善於破解的高手.但是就是做不出

世界级的软件.当然,这也骂到我自己了,但是我真的是对软件业没啥贡

献就是了。

非官方观点,欢迎大家来信讨论.

moli.mt88g@nctu.edu.tw


困扰JSP的一些问题

作者：jspfuns

日期：2000-8-12 2:29:45

Blueski编译

（编者按：此文通过jsp和template engines的比较，提示了目前jsp的一些不足。后者也是一项基于servlets的技术，有些类似于宏，可以简化开发，但后来没有得到应有的重视和发展。）

如今每一个使用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的解决方案了。


利用Java实现zip压缩解压缩

作者：五一

日期：00-7-7 14:06:26

利用Java实现zip压缩/解压缩

（作者：　2000年07月06日 13:30）

    由于网络带宽有限,所以数据文件的压缩有利于数据在Internet上的快速传输,同时也节

省服务器的外存空间。

    Java 1.1实现了I/O数据流与网络数据流的单一接口,因此数据的压缩、网络传输和解

压缩的实现比较容易,下面介绍利用ZipEntry、ZipInputStream和ZipOutputStream三个Java

类实现zip数据压缩方式的编程方法。

    zip压缩文件结构:一个zip文件由多个entry组成,每个entry有一个唯一的名称,entry的

数据项存储压缩数据。

    与zip文件有关的几个Java类

    ·类ZipEntry

    public ZipEntry(String name);

    name为指定的数据项名。

    ·类ZipOutputStream

    ZipOutputStream实现了zip压缩文件的写输出流,支持压缩和非压缩entry。下面是它的

几个函数:

    public ZipOutputStream(OutputStream out);

    ∥利用输出流out构造一个ZIP输出流。

    public void setMethod(int method);

    ∥设置entry压缩方法,缺省值为DEFLATED。

    public void putNextEntry(ZipEntry newe);

    ∥如果当前的entry存在且处于激活状态时,关闭它,在zip文件中写入新的entry-newe

并将数据流定位于entry数据项的起始位置,压缩方法为setMethod指定的方法。

    ·类ZipInputStream

    ZipInputStream实现了zip压缩文件的读输入流,支持压缩和非压缩entry。下面是它的

几个函数:

    public ZipInputStream(InputStream in);

    ∥利用输入流in构造一个ZIP输出流。

    public ZipEntry getNextEntry();

    ∥返回ZIP文件中的下一个entry,并将输出流定位在此entry数据项的起始位置。

    public void closeEntry();

    ∥关闭当前的zip entry,并将数据流定位于下一个entry的起始位置。

    程序代码及其注释

    下列的程序实现了数据文件zip方式的压缩和解压缩方法。randomData()函数随机生成

50个double数据,并放在doc字符串变量中;openFile()函数读取ZIP压缩文件;saveFile()函数

将随机生成的数据存到ZIP格式的压缩文件中。

    import java.util.zip.*;

    import java.awt.event.*;

    import java.awt.*;

    import java.lang.Math;

    import java.io.*;

    public class TestZip extends Frame implements ActionListener {

    TextArea textarea; ∥显示数据文件的多行文本显示域

    TextField infotip; ∥显示数据文件未压缩大小及压缩大小单行文本显示域

    String doc; ∥存储随机生成的数据

    long doczipsize = 0;∥压缩数据文件的大小

    public TestZip(){

    ∥生成菜单

    MenuBar menubar = new MenuBar();

    setMenuBar(menubar);

    Menu file = new Menu("File",true);

    menubar.add(file);

    MenuItem neww= new MenuItem("New");

    neww.addActionListener(this);

    file.add(neww);

    MenuItem open=new MenuItem("Open");

    open.addActionListener(this);

    file.add(open);

    MenuItem save=new MenuItem("Save");

    save.addActionListener(this);

    file.add(save);

    MenuItem exit=new MenuItem("Exit");

    exit.addActionListener(this);

    file.add(exit);

    ∥随机生成的数据文件的多行文本显示域

    add("Center",textarea = new TextArea());

    ∥提示文本原始大小、压缩大小的单行文本显示域

    add("South",infotip = new TextField());

    }

    public static void main(String args[]){

    TestZip ok=new TestZip();

    ok.setTitle("zip sample");

    ok.setSize(600,300);

    ok.show();

    }

    private void randomData(){

    ∥随机生成50个double数据,并放在doc字符串变量中。

    doc="";

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

     double rdm=Math.random()*10;

     doc=doc+new Double(rdm).toString();

     if(i%5 == 0) doc=doc+"\n";

     else doc=doc+" ";

    }

    doczipsize = 0;

    showTextandInfo();

    }

    private void openFile(){

    ∥打开zip文件,将文件内容读入doc字符串变量中。

    FileDialog dlg=new FileDialog(this,"Open",FileDialog.LOA D);

    dlg.show();

    String filename=dlg.getDirectory()+dlg.getFile();

    try{

    ∥创建一个文件实例

    File f=new File(filename);

    if(!f.exists()) return; ∥文件不存在,则返回

    ∥用文件输入流构建ZIP压缩输入流

    ZipInputStream zipis=new ZipInputStream(new FileInputStream(f));

    zipis.getNextEntry();

    ∥将输入流定位在当前entry数据项位置

    DataInputStream dis=new DataInputStream(zipis);

    ∥用ZIP输入流构建DataInputStream

    doc=dis.readUTF();∥读取文件内容

    dis.close();∥关闭文件

    doczipsize = f.length();∥获取ZIP文件长度

    showTextandInfo();∥显示数据

    }

    catch(IOException ioe){

    System.out.println(ioe);

    }

    }

    private void saveFile(){

    ∥打开zip文件,将doc字符串变量写入zip文件中。

    FileDialog dlg=new FileDialog(this,"Save",FileDialog.SAVE);

    dlg.show();

    String filename=dlg.getDirectory()+dlg.getFile();

    try{

    ∥创建一个文件实例

    File f=new File(filename);

    if(!f.exists()) return; ∥文件不存在,则返回

    ∥用文件输出流构建ZIP压缩输出流

    ZipOutputStream zipos=new ZipOutputStream(new FileOutputStream(f));

    zipos.setMethod(ZipOutputStream.DEFLATED); ∥设置压缩方法

    zipos.putNextEntry(new ZipEntry("zip"));

    ∥生成一个ZIP entry,写入文件输出流中,并将输出流定位于entry起始处。

    DataOutputStream os=new DataOutputStream(zipos);

    ∥用ZIP输出流构建DataOutputStream;

    os.writeUTF(doc);∥将随机生成的数据写入文件中

    os.close();∥关闭数据流

    doczipsize = f.length();

    ∥获取压缩文件的长度

    showTextandInfo();∥显示数据

    }

    catch(IOException ioe){

    System.out.println(ioe);

    }

    }

    private void showTextandInfo(){

    ∥显示数据文件和压缩信息

    textarea.replaceRange(doc,0,textarea.getText().length());

    infotip.setText("uncompressed size: "+doc.length()+"compressed size: "+dc zipsize);

    }

    public void actionPerformed(ActionEvent e){

    String arg = e.getActionCommand();

    if ("New".equals(arg)) randomData();

    else if ("Open".equals(arg)) openFile();

    else if ("Save".equals(arg)) saveFile();

    else if ("Exit".equals(arg)){

     dispose();∥关闭窗口

     System.exit(0);∥关闭程序

    }

    else {

     System.out.println("no this command!");

    }

    }

    }


利用Java实现串口全双工通讯 (转载)

作者：wflm

日期：2001-6-2 13:40:04

利用Java实现串口全双工通讯 (投稿)

Qingye Jiang (John)

SMTH ID: qyjohn

E-mail : qjiang@tsinghua.edu

一个嵌入式系统通常需要通过串口与其主控系统进行全双工通讯，譬如一个流水线

控制系统需要不断的接受从主控系统发送来的查询和控制信息，并将执行结果或查

询结果发送回主控系统。本文介绍了一个简单的通过串口实现全双工通讯的Java类

库，该类库大大的简化了对串口进行操作的过程。

本类库主要包括：SerialBean.java (与其他应用程序的接口), SerialBuffer.java

(用来保存从串口所接收数据的缓冲区), ReadSerial.java (从串口读取数据的程序)。

另外本类库还提供了一个例程SerialExample.java 作为示范。在下面的内容中将逐

一对这几个部分进行详细介绍。

1. SerialBean

SerialBean是本类库与其他应用程序的接口。该类库中定义了SerialBean的构造方

法以及初始化串口，从串口读取数据，往串口写入数据以及关闭串口的函数。具体

介绍如下：

public SerialBean(int PortID)

本函数构造一个指向特定串口的SerialBean，该串口由参数PortID所指定。

PortID = 1 表示COM1，PortID = 2 表示COM2，由此类推。

public int Initialize()

本函数初始化所指定的串口并返回初始化结果。如果初始化成功返回1，否

则返回-1。初始化的结果是该串口被SerialBean独占性使用，其参数被设置

为9600, N, 8, 1。如果串口被成功初始化，则打开一个进程读取从串口传

入的数据并将其保存在缓冲区中。

public String ReadPort(int Length)

本函数从串口(缓冲区)中读取指定长度的一个字符串。参数Length指定所返

回字符串的长度。

public void WritePort(String Msg)

本函数向串口发送一个字符串。参数Msg是需要发送的字符串。

public void ClosePort()

本函数停止串口检测进程并关闭串口。

SerialBean的源代码如下：

package serial;

import java.io.*;

import java.util.*;

import javax.comm.*;

/**

*

* This bean provides some basic functions to implement full dulplex

* information exchange through the srial port.

*

*/

public class SerialBean

{

static String PortName;

CommPortIdentifier portId;

SerialPort serialPort;

static OutputStream out;

static InputStream in;

SerialBuffer SB;

ReadSerial RT;

/**

*

* Constructor

*

* @param PortID the ID of the serial to be used. 1 for COM1,

* 2 for COM2, etc.

*

*/

public SerialBean(int PortID)

{

PortName = "COM" + PortID;

}

/**

*

* This function initialize the serial port for communication. It starts a

* thread which consistently monitors the serial port. Any signal captured

* from the serial port is stored into a buffer area.

*

*/

public int Initialize()

{

int InitSuccess = 1;

int InitFail = -1;

try

{

portId = CommPortIdentifier.getPortIdentifier(PortName);

try

{

serialPort = (SerialPort)

portId.open("Serial_Communication", 2000);

} catch (PortInUseException e)

{

return InitFail;

}

//Use InputStream in to read from the serial port, and OutputStream

//out to write to the serial port.

try

{

in = serialPort.getInputStream();

out = serialPort.getOutputStream();

} catch (IOException e)

{

return InitFail;

}

//Initialize the communication parameters to 9600, 8, 1, none.

try

{

serialPort.setSerialPortParams(9600,

SerialPort.DATABITS_8,

SerialPort.STOPBITS_1,

SerialPort.PARITY_NONE);

} catch (UnsupportedCommOperationException e)

{

return InitFail;

}

} catch (NoSuchPortException e)

{

return InitFail;

}

// when successfully open the serial port, create a new serial buffer,

// then create a thread that consistently accepts incoming signals from

// the serial port. Incoming signals are stored in the serial buffer.

SB = new SerialBuffer();

RT = new ReadSerial(SB, in);

RT.start();

// return success information

return InitSuccess;

}

/**

*

* This function returns a string with a certain length from the incoming

* messages.

*

* @param Length The length of the string to be returned.

*

*/

public String ReadPort(int Length)

{

String Msg;

Msg = SB.GetMsg(Length);

return Msg;

}

/**

*

* This function sends a message through the serial port.

*

* @param Msg The string to be sent.

*

*/

public void WritePort(String Msg)

{

int c;

try

{

for (int i = 0; i < Msg.length(); i++)

out.write(Msg.charAt(i));

} catch (IOException e) {}

}

/**

*

* This function closes the serial port in use.

*

*/

public void ClosePort()

{

RT.stop();

serialPort.close();

}

}

2. SerialBuffer

SerialBuffer是本类库中所定义的串口缓冲区，它定义了往该缓冲区中写入数据和

从该缓冲区中读取数据所需要的函数。

public synchronized String GetMsg(int Length)

本函数从串口(缓冲区)中读取指定长度的一个字符串。参数Length指定所

返回字符串的长度。

public synchronized void PutChar(int c)

本函数望串口缓冲区中写入一个字符，参数c 是需要写入的字符。

在往缓冲区写入数据或者是从缓冲区读取数据的时候，必须保证数据的同

步，因此GetMsg和PutChar函数均被声明为synchronized并在具体实现中采

取措施实现的数据的同步。

SerialBuffer的源代码如下：

package serial;

/**

*

* This class implements the buffer area to store incoming data from the serial

* port.

*

*/

public class SerialBuffer

{

private String Content = "";

private String CurrentMsg, TempContent;

private boolean available = false;

private int LengthNeeded = 1;

/**

*

* This function returns a string with a certain length from the incoming

* messages.

*

* @param Length The length of the string to be returned.

*

*/

public synchronized String GetMsg(int Length)

{

LengthNeeded = Length;

notifyAll();

if (LengthNeeded > Content.length())

{

available = false;

while (available == false)

{

try

{

wait();

} catch (InterruptedException e) { }

}

}

CurrentMsg = Content.substring(0, LengthNeeded);

TempContent = Content.substring(LengthNeeded);

Content = TempContent;

LengthNeeded = 1;

notifyAll();

return CurrentMsg;

}

/**

*

* This function stores a character captured from the serial port to the

* buffer area.

*

* @param t The char value of the character to be stored.

*

*/

public synchronized void PutChar(int c)

{

Character d = new Character((char) c);

Content = Content.concat(d.toString());

if (LengthNeeded < Content.length())

{

available = true;

}

notifyAll();

}

}

3. ReadSerial

ReadSerial是一个进程，它不断的从指定的串口读取数据并将其存放到缓冲区中。

public ReadSerial(SerialBuffer SB, InputStream Port)

本函数构造一个ReadSerial进程，参数SB指定存放传入数据的缓冲区，参

数Port指定从串口所接收的数据流。

public void run()

ReadSerial进程的主函数，它不断的从指定的串口读取数据并将其存放到

缓冲区中。

ReadSerial的源代码如下：

package serial;

import java.io.*;

/**

*

* This class reads message from the specific serial port and save

* the message to the serial buffer.

*

*/

public class ReadSerial extends Thread

{

private SerialBuffer ComBuffer;

private InputStream ComPort;

/**

*

* Constructor

*

* @param SB The buffer to save the incoming messages.

* @param Port The InputStream from the specific serial port.

*

*/

public ReadSerial(SerialBuffer SB, InputStream Port)

{

ComBuffer = SB;

ComPort = Port;

}

public void run()

{

int c;

try

{

while (true)

{

c = ComPort.read();

ComBuffer.PutChar(c);

}

} catch (IOException e) {}

}

}

4. SerialExample

SerialExample是本类库所提供的一个例程。它所实现的功能是打开串口COM1，对

其进行初始化，从串口读取信息对其进行处理后将处理结果发送到串口。

import serial.*;

import java.io.*;

/**

*

* This is an example of how to use the SerialBean. It opens COM1 and reads

* six messages with different length form the serial port.

*

*/

class SerialExample

{

public static void main(String[] args)

{

//TO DO: Add your JAVA codes here

SerialBean SB = new SerialBean(1);

String Msg;

SB.Initialize();

for (int i = 5; i <= 10; i++)

{

Msg = SB.ReadPort(i);

SB.WritePort("Reply: " + Msg);

}

SB.ClosePort();

}

}

5. 编译与调试

本类库中使用了Java Communication API (javax.comm)。这是一个Java扩展类库，

并不包括在标准的Java SDK当中。如果你尚未安装这个扩展类库的话，你应该从Sun

公司的Java站点下载这个类库并将其安装在你的系统上。在所下载的包里面包括一个

安装说明，如果你没有正确安装这个类库及其运行环境的话，运行这个程序的时候你

会找不到串口。

正确安装Java Communication API并将上述程序编译通过以后，你可以按如下方法测

试这个程序。如果你只有一台机器，你可以利用一条RS-232电缆将COM1和COM2连接起

来，在COM1上运行SerialExample，在COM2上运行Windows提供的超级终端程序。如果

你有两台机器的话，你可以利用一条RS-232电缆将两台机器的COM1(或者是COM2)连接

起来，在一端运行例程，另外一端运行Windows提供的超级终端程序。如果有必要的

话，可以对SerialExample中所声明的串口进行相应改动。

本程序在Windows 2000 + Java SDK 1.3环境下编译通过并成功运行。


利用RamdonAccessFile来实现文件的追加！

作者：jeru

日期：2000-11-29 14:44:48

RamdonAccessFile 是个很好用的类，功能十分强大，可以利用它的

length()和seek()方法来轻松实现文件的追加，相信我下面这个例子是

很容易看懂的，先写入十行，用length()读出长度（以byte为单位），

在用seek()移动到文件末尾，继续添加，最后显示记录。

import java.io.*;

public class IOStreamDemo {

  public static void main(String[] args) {

    try{

      RandomAccessFile rf1 = new RandomAccessFile("d:\\jeru.txt","rw");

      for (int i = 0; i < 10; i ++ )  {

        rf1.writeBytes("xixi,this is line "+i+"\n");

      }

      rf1.close();

      int i = 0;

      String record = new String();

      RandomAccessFile rf2 = new RandomAccessFile("d:\\jeru.txt","rw");

      rf2.seek(rf2.length());

      rf2.writeBytes("lala,append line"+"\n");

      rf2.close();

      RandomAccessFile rf3 = new RandomAccessFile("d:\\jeru.txt","r");

      while ((record = rf3.readLine()) != null) {

        i ++;

        System.out.println("Value "+i+":"+record);

      }

      rf3.close();

   }catch(Exception e){}

}

}


利用UDP Sockets技术实现IP多点传送1（转中华技术网）

作者：studyboy

email: studyboy@21cn.com

日期：2001-7-22 21:54:48

杨绍方

摘　要　本文介绍了UDP Sockets的基本概念和IP多点传送的原理，详细讨论了Java中的相关类及使用方法，提供了一个IP多点传送的开发流程。

关键词　Java　UDP Sockets

    IP　多点传送　MulticastSocket　类IP多点传送(multicast delivery)是针对点到点的传送和广播传送两种方式而言的，它是指在一定的组内对其成员进行的广播，是一种有限的广播。组中的某个成员发出的信息，组中的其它所有成员都能收到。它是UDP Sockets的一个分支。

    IP多点传送特别适合与高带宽的应用，例如在网络上发送视频和音频。随着网络带宽的不断提高和网络通讯质量的不断改善，IP多点传送还将广泛地被应用于网上聊天及网上会议，分布式数据存储，联机事务处理，交互式游戏等方面。另外，多点传送还可以被客户机用于在网络上寻找相应的服务器，客户机发送一个多点传送的请求，任何监听服务器都可以与客户机连接并开始一个事务。

UDP Socket基础

    使用用户数据报协议(User Datagram Protocol,简称UDP)进行会话必须将信息装配成一定尺寸的小报文，当发送一条信息，接收方能否收到并返回信息永远是不确定的，如果无法收到返回信息，我们就无法确定我们发送的信息是否被接收——它可能在途中丢失，接收者返回的响应信息也可能丢失，另外，接收者也可能忽略我们的信息，因此，UDP被描述为不可靠的、无连接的和面向消息的。

    创建UDP sockets非常象创建一个邮箱。邮箱是使用地址来识别的，但是，我们不需要为每个发送信息的人构造一个新的邮箱，可以在含有发送信息的明信片上写上目的地址，将其放在邮箱中并发送出去。接收者可能会长久的等待，直到含有信息的明信片到达它的邮箱，而明信片上标识了发送者的返回地址。

IP多点传送的原理

    为了支持IP多点传送，某些范围的IP地址被单独留出专门用于这个目的，这些IP地址是D类地址，其地址的最高四比特的位模式为“1110”，即IP地址的范围在224.0.0.0和239.255.255.255之间。它们中的每一个IP地址都可以被引用作为一个多点传送组，任何以该IP地址编址的IP报文将被该组中的其它所有机器接收，也就是说，一个IP地址就相当于一个邮箱。另外，组中的成员是动态的并随时间而改变。

    对于IP多点传送，网间网组管理协议(Internet Group Management Protocol,简称IGMP)，用于管理多点传送组中的成员。支持多点传送的路由可以使用IGMP决定本地的机器是否赞成加入某个组，一个多点传送路由可以决定是否转发一个多点传送报文。

    影响多点传送报文的一个重要参数是time－to－live(TTL)。TTL用于描述发送者希望传送的信息能通过多少不同的网络。当报文被路由器转发，报文中的TTL将减一，当TTL为零时，报文将不再向前发送。

    在实际使用中，我们必须注意下面几点：

    1.这些IP地址只能作为信宿地址使用，绝对不能出现在任何信源地址域中，也不能出现在源路径或记录路径选项中：

    2.由于IP多点传送是一对多的传送，因此，不能利用差错与控制报文协议(Internet Control Message Protocol，简称ICMP)产生出错报文。

    3.发送一个信息到一个组，发送主机可以不是组中的成员；

    4.一些组被Internet Assigned Numbers Authority(IANA)分配，保留用于特殊的目的，详情参见：ftp://ftp.internic.net/rfc/rfc1700.txt

    5.如果我们选取的组已经被使用，与其他机器的通讯将会混乱，一旦发生，可以退出应用，试试其他的地址。

    6.当一个机器加入一个多点传送组，它将开始接收该IP多点传送地址的信息。如果多点传送报文分发到网络上，任何监听该信息的机器都会有机会接收它。对于IP多点传送，没有一个机制对相同网络上的机器能否加入该多点传送组加以限制。因此，安全性是我们必须考虑的问题之一。

    7.选择的TTL参数应尽可能小。一个大的TTL值会不必要地占用Internet带宽。此外，还可能破坏不同区域使用相同组的其它的多点传送通讯。

Java中与IP多点传送相关的类

    java.net包中含有UDP通讯所需要的工具，其中包括IP多点传送。


利用UDP Sockets技术实现IP多点传送2（转中华技术网）

作者：studyboy

email: studyboy@21cn.com

日期：2001-7-22 21:55:31

DatagramPacket类

    我们可以使用DatagramPacket类创建一个用于发送的数据报，而当接收UDP数据报时，可以使用DatagramPacket类读取数据报中的数据，发送者及其它信息。

    为了创建一个数据报并发送到远地系统，可以使用下面的构造器：

    Public DatagramPacket(byte ibuf,int length,InetAddress iaddr,int iport,);

    ibuf是编码信息数据的字节数组，它的长度length就是数据报放在其中的字节数组的长度，iaddr是一个InetAddress对象，存储着接收方的主机名和IP地址等信息，iport标识数据报发送到接收主机的端口。

    为了接收数据报，必须使用DatagramPacket构造器，其原型为：public DatagramPacket(byte ibuf,int ilength);ibuf是指接收的数据报的数据部分， ilength是该部分数据的长度。如果 ilength 小于机器接收的UDP数据报的尺寸，多余的字节将被Java忽略。

    另外，类中有一些方法(method)可以让我们得到一些相关的信息：

    public int getLength();　//得到数据报中数据块的字节尺寸

    public bytegetData();//得到接收数据报中的数据

    public InetAddress getAddress();　//为发送者提供一个 InetAddress对象

    public int getPort();　//得到UDP端口

    值得注意的是，TCP sockets的编程中，我们无须将传送的数据分块，然而，当我们创建一个基于UDP的网络通讯应用程序时，必须创建一套方法，在运行时刻决定需分割的数据报的长度。对于TCP/IP，最大的数据报可以含有65507字节的数据，然而，主机仅能接收最多548字节的数据，支持8192字节的大数据报的平台是利用IP层对数据报进行分割的。如果在传送期间，任何含有IP报文的一个数据块丢失，都会造成整个UDP数据报的丢失，因此，我们在确定应用中数据报尺寸时，对其尺寸的合理性一定要谨慎。

    下面就是分割数据的一个例子：

    //循环地从输入流input中读一行数据

    while((nextLine=input.readLine())!=null){

    //定义一个空数据报，其尺寸为512

    mcastBuffer=new byte[512];

    //如果读入的数据的长度大于定义的数据报的长度，

    //则使用定义的长度，否则使用读入数据的长度

    if(nextLine.length()>mcastBuffer.length){

    sendLength=mcastBuffer.length;

    }else {

    sendLenth=nextLine.length();

    }

    //将读入的数据转换为byte类型

    lineData=nextLine.getBytes();

    //将数据复制到用于创建数据报的byte数组

    for(int i=0;i<sendLength;i＋＋){

    mcastBuffer[i]=lineData[i];

    }

    ……创建数据报，发送或接收……

    }

MulticastSocket类

    Java的 MulticastSocket类是实施IP多点传送网络特征的关键，它允许我们使用多点传送IP发送或接收UDP数据报。 MulticastSocket的构造器为：

    public MulticastSocket () throws IOException;　　//创建一个多点传送socket

    public MulticastSocket(int port)throws IOException;//在指定端口创建一个多点传送socket

    另外，类中其它常用的方法有：

    public void joinGroup(InetAddress mcastaddr)throws IOException{} //加入多点传送组

    public void leaveGroup(InetAddress mcastaddr)throws IOException{} //离开多点传送组

    public synchronized void send(DatagramPacket p,byte ttl) throws IOException{}　//发送数据报

    public synchronized void receive(DatagramPacket p,byte ttl) throws IOException{} 　//接收数据报

    创建一个DatagramPacket对象之后，我们必须相应地创建一个 MulticastSocket对象，这样，数据报就可以使用send()方法发送了。下面的代码演示了如何创建 MulticastSocket、发送和接收IP多点传送数据报：

    int multiPort=2345;　//定义端口号，非超级用户应使用1024以上的端口

    int ttl=1;　//设定TTL值

    InetAddress multiAddr=InetAddress.getByName(″224.0.1.100″)；　//设定多点传送IP

    byteSmultiBytes={'H','e','1','1','O'}; //定义一个内容为“Hello”的数据报

    //创建多点传送数据报

    DatagramPacket SmultiDatagram new Datagram Packet(SmultiBytes,SmultiBytes,length,multiAddr,multiPort);

    MulticastSocket multiSocket=new MulticastSocket(); 　//创建多点传送socket

    multiSocket.send(SmultiDatagram,ttl)；　//发送数据报(不加入到组中)

    ……

    byteRmultiBytes=new byte[256];　//定义一个空数据报，长度为256字节

    //创建接收数据报

    DatagramPacket RmultiDatagram=new DatagramPacket(RmultiBytes,RmultiBytes.length);

    multiSocket.joinGroup(multiAddr);　//加入到多点传送组中

    multiSocket.receive(RmultiDatagram);//接收UDP数据报

    ……

    multiSocket.leaveGroup(multiAddr);　//离开多点传送组

    multiSocket.close(); 　//关闭多点传送 socket

    当调用joinGroup()方法时，机器将关注沿着网络传送属于特定多点传送组的任何IP报文，也就是说,机器拥有了一个邮箱。主机还应使用IGMP相应地报告组的使用。对于多IP地址的机器，应配置数据报发送的接口：setInterface(oneOfMyLocalAddrs);

    在DatagramSocket中没有类似 setSo Timeout()的方法设置超时。

IP多点传送应用程序的开发流程

    由于IP多点传送主要用于同组中成员的交流，因此，应用程序的开发流程大体如下：

    1.创建一个需发送的按规定编址的数据报DatagramPacket;

    2.建立一个用于发送和接收的MulticastSocket;

    3.加入一个多点传送组；

    4.将数据报放入MulticastSocket中传送出去；

    5.等待从MulticastSocket接收数据报；

    6.解码数据报提取信息；

    7.根据得到的信息作出回应；

    8.重复5—7步；

    9.离开该多点传送组，关闭MulticastSocket。

结束语

    在实际应用中，发送和接收数据建议分别以单独的线程同时运行，另外，如果需在屏幕上显示，建议使用两个线程在两个不同的窗口分别负责显示发送的数据和接收的数据。□

作者单位：建设银行云南保山地区分行　(云南保山678000)


两种INCLUDE方式的不同. (为什么出错,你自己看吧.)

作者：sonymusic

email: sonymusic@163.net

日期：8/7/2001 1:50:57 PM

问:

为什么用<jsp:include page="aa.txt"/>不可以显示？却用<%@ include file="aa.txt" %>却可以？

答:

这两种INCLUDE是按不同的方式一为包含的.

前者是将AA.TXT通过服务器处理后,将HTML包含进来.

后者是先将文件包含进来,再处理.

所以,如果用前者,就必须保证文件中的JSP语句是正确的.所有的变量定义都是独立的.

如果是后者,则可以在AA.TXT中使用外部在INCLUDE它之前所定义的变量.

两都还有一个区别, 就是前者是动态更新的,也就是说AA.TXT如果发生了变量, 所有包含它的文件都会变.而后者, 就只有将所有包含AA.TXT文件的先改变了,重新编译才能看到新的效果.


了解Java，便了解了微软.NET

作者：lovefan

email: lovefan@cmmail.com

日期：8/2/2001 3:40:39 PM

了解Java，便了解了微软.NET

http://www.javaunion.org 2001-3-12 15:39:21

人们对于美国微软在经过精心策划后于6月宣布的“Microsoft.NET”的设想是见仁见智。在传媒中“目前还很难说”的反应居多。实际上，由于具体产品开始出笼还是2～3年以后的事，现在说三道四确实为时尚早。

    这里有一把可以用来快速理解Microsoft.NET的钥匙。那就是先了解Java技术。这是因为微软自己就说过，“只要知道Java就可以更快地理解Microsoft.NET”。

    Microsoft.NET有几个关键点。其中最重要的一点是改革商务模型。微软公司感觉到只靠销售软件包的商务模型没有什么前途，该公司打算今后将中心转移到可以在网络上使用的“服务”型商务。

    这样，首要的问题就是解决网络上用来开发并执行“服务”的平台，这就是Microsoft.NET。这一平台应该是一个继承了目前的Windows资产，同时又是一个不比与其竞争的技术（即Java技术）逊色的现代化的东西。

    新的平台试图提高开发生产效率（再也不想为内存溢出错误（Memory Leak Error）而烦恼了！），并且试图使应用软件的发布更为容易（再也不想因为DLL版本不同而烦恼，希望不用重新启动电脑就能够安装应用软件）。为此，就需要将平台升级为现代化的内容。而且，Microsoft.NET的蓝图与Java十分相似。

    Microsoft.NET平台将推动程序执行环境的虚拟化进程。使用编程语言编写的代码，首先转换成中间代码“IL”后，在虚拟机“Common Language Runtime”上执行。在执行时利用“JIT（Just In Time）编译器”，通过将中间代码转换为本地代码（Native Code）来提高执行速度。此外，还配备实现内存管理自动化的GC(垃圾收集器，Garbage Collector)以及验证中间代码的机制。此外还有可以在虚拟机环境中利用的共同层次程序库（Class Liblaory）“Microsoft.NET Framework”

所有这些特征与Java平台惊人地相似。均具有提高开发生产效率，容易发布软件（安装时只需单纯复制即可）等特征。这些都与Java技术一致。

    当然，Java与Microsoft.NET除了相似之处还有很多不同的地方。通过这些区别，就可以发现这两个技术在今后角逐时的力学关系。以下是主要的区别。

(1)Java技术目前就可以利用。Microsoft.NET的登台还要等2～3年。

(2)Java是采用一种语言（Java语言）开发的。而Microsoft.NET则可以接受利用Visual Basic和新的语言C#，以及所有的编程语言开发软件。

(3)Java对应多个OS。而Microsoft.NET虽从原理上讲可以对应多个OS，不过估计目前暂时只以Windows环境为对象。

(4)在XML方案方面微软的构思领先。为了实现对应网络的“服务”，采用了基于XML的远隔工程（Project）调入协议SOAP。对应XML的协议，原则上不能局限于特定的平台。这一点其他竞争公司也给与很高的评价。

    基于Java技术且对应网络、用来提供服务的协议还有Jini。不过，Jini是以Java为前提的技术，很难说它是用来分布不同种类的技术。

微软公司有若干他所不擅长的领域。尤其是在高端服务器OS、高端数据库、大规模Web应用软件服务器以及Transaction中间软件领域中，已经难以同美国Sun Microsystems、美国IBM、美国Oracle以及美国BEA Systems等竞争对手一争高低。而另一方面，在桌面领域中微软依然占有压倒性的市场占有率。该公司所擅长的领域，说到底还是以Visual Basic为代表的容易使用的开发工具群，和以Office为代表的桌面环境。

    只要在桌面环境中的膨大用户支持该公司，就可以在网络中占有一方领地。如果应用Java的某个大规模Web网站提供优秀的服务，则只要开发出将其整合，便于使用即可。

    其结果，微软公司为了继续生存的最有效战略，是将桌面环境中的优势运用到网络时代--这便是听了Microsoft.NET说明后使人感受到的。

    不过话又说回来，要想开发既继承现有环境，又支持多个语言的Microsoft.NET，其难度将比开发Java技术要大。而另一方面，Java技术的开发正在稳步前进。展现在微软公司前面的路，的确是很不平坦


另一种bbs设计的思路，请各位高手给予评价。

作者：wflm

日期：2000-12-21 22:32:18

    最近一段时间研究了一下bbs方面的一些程序，也拜读了本站bbs的源程序，自己觉得受益匪浅，

很多程序都堪称典范。在主要的论坛贴子处理方面，很多的论坛也都模仿此作。因为我不想总拿别

人的东西照抄，所以自己用了另一种处理方法来做的。至于方法如何还请大家评断。（我当然觉得

自己的好了 8-））

1。贴子的排序

    数据库 Id,SortId  ,Title,Uname,Stime,BackTimes,ReadTimes,Face,Content

        1 999999   .........................................

        2 999998   .........................................

        3 999997   .........................................

         id为自动编号，也即显示的贴子号

    引入一个SortId,50位字符型，

    设根贴子为六位（如999999，你也可以更多，我想已经够了）

    设第一贴为999999 第二贴为999998 第三贴为999997 .............

    （注意这都是根贴，是六位长）

    为什么第一贴为999999，是为了以后便于输出贴子。

    要在999999下面回复一贴即第二层贴子就在数据库中写入

           Id,SortId  ,Title,Uname,Stime,BackTimes,ReadTimes,Face,Content

        4 999999z   .........................................

    在999999后面加了一个“z” (增加一位可以用来区分贴子所在层数)

    要在999999下面再回复一贴也即第二层的贴子就在数据库中写入

           Id,SortId  ,Title,Uname,Stime,BackTimes,ReadTimes,Face,Content

        5 999999y   .........................................

    要在发贴，依此类推

        Id,SortId  ,Title,Uname,Stime,BackTimes,ReadTimes,Face,Content

        6 999999x   .........................................

    要在第二层贴子下面再回复贴子 如贴子999999z下 即第三层贴子就在数据库中写入

        Id,SortId  ,Title,Uname,Stime,BackTimes,ReadTimes,Face,Content

        7 999999zz   .........................................

    再回复999999z贴子就在数据库中写入

        Id,SortId  ,Title,Uname,Stime,BackTimes,ReadTimes,Face,Content

        8 999999zy   .........................................

    现在的贴子情况即为：

        Id,SortId  ,Title,Uname,Stime,BackTimes,ReadTimes,Face,Content

        1 999999   .........................................

        2 999998   .........................................

        3 999997   .........................................

        4 999999z   .........................................

        5 999999y   .........................................

        6 999999x   .........................................

        7 999999zz   .........................................

        8 999999zy   .........................................

    我向大家也看出了我的思路至于输出的时候使用

     sql = "select * from [plan] where boardid="+ strboardid+" ORDER BY SortId ";

         rs = conn.executeQuery(sql);

    根据SortId排序后的结果：

        1 999997   .........................................

        2 999998   .........................................

        3 999999   .........................................

            4 999999x   .........................................

            5 999999y   .........................................

            6 999999z   .........................................

                7 999999zy   .........................................

                8 999999zz   .........................................

    现在大家应该明白为什么前六位贴子号和字母是递减的了.(当然也能是递增的)

2.贴子的显示输出,主要是处理<UL></UL>.程序如下

    /*

    取得数据集rs

    */

    String Id,SortId,Title,Uname,Stime,BackTimes,ReadTimes,Face,Content;

                int length=6,ContentLength;//初始化第一贴为根贴

                boolean isnew=false;

        out.println("<UL>");

             while (rs.next()) {

            Id=rs.getString("Id");

             SortId=rs.getString("SortId");

                    Title=china.asHTML(rs.getString("Title"));

                   Uname=china.asHTML(rs.getString("Uname"));

                    Stime=china.asHTML(rs.getString("Stime").substring(0,19));

            //if (System.currentTimeMillis() - rs.getDate("Stime").getTime()< 86400000){isnew =true;} else {isnew =false;}

                       BackTimes=rs.getString("BackTimes");

                    ReadTimes=rs.getString("ReadTimes");

            Face=rs.getString("Face");

            ContentLength = rs.getString("Content").length();

                    String dot = ",";

            String yh = "\"";

            //控制部分

            if(length>SortId.length())//用来计算上贴到本贴需要输出多少个</UL>

            while(length>SortId.length())

            { length--;

            out.println("</UL>");

            }

        //举例 从3到4

        //<ul>1 .......................SortId=6

        //    <ul>2 .......................SortId=7

        //        <ul>3.......................SortId=8

        //        </ul>//此标记是由if(BackTimes.equals("0"))out.println("</UL>");输出的

        //    </ul>

        //</ul>//此标记和上一个</UL>是由if(length>SortId.length())......这一部分生成的

        //<ul>4........................SortId=6</ul>

        //

        //if (8>6)//条件成立进入循环

        //while(8>6)//开始循环输出两个</UL>

            out.println("<UL>");//因为任何一贴前都有<UL>

            out.println(china.turnGb("<SCRIPT>o("+yh+Title+yh+dot+yh+Uname+yh+dot+yh+Stime+yh+dot+Id+dot+ContentLength+dot+BackTimes+dot+ReadTimes+dot+isnew+dot+Face+");</SCRIPT>"));

            if(BackTimes.equals("0"))out.println("</UL>");//因为任何回复次数为0的贴子后都有</UL>

            length=SortId.length();//记录贴子SortId的长度，用来与下一贴比较

        }//循环结束

            while((length-6)!=0)//因为最后一条记录不能再与下一条比较，所以要补齐最后的</UL>

            { length--;

            out.println("</UL>");

            }

            out.println("</UL>");

    }

            catch(SQLException ex) {out.println(ex.getMessage());}

3。发贴子

    发根贴按SortId升序排序取第一条减一为新贴子的SortId(要转整数)

    回复贴子根据回复次数0时为z，1时为y,.......加到你所要回复的贴子的SortId后。增加回复次数。

    大体上这就是我的做法，上面的例子仅仅为了说明输出，所以没有涉及其他问题。如分页，删贴子，中文问题等。可能

其中会有问题，还请大家发表意见，给予我提示。wflm@263.net


浏览器中显示JApplet(可解决引入Swing包的问题))

作者：瞌睡虫

email: kika2000@sina.com

日期：7/31/2001 4:21:52 PM

//把JApplet1换成你自己的JApplet名就可以了,代码包含了自动下载

所须插件的功能

//JApplet.html

<html><head><title>JApplet1</title></head><hr>

<OBJECT

  classid="clsid:8AD9C840-044E-11D1-B3E9-00805F499D93"

  width="100" height="50" align="baseline"  codebase="http://java.sun.com/products/plugin/1.2.2/jinstall-1_2_2-win.cab#Version=1,2,2,0">

<PARAM NAME="code" VALUE="JApplet1.class">

<PARAM NAME="codebase" VALUE=".">

<PARAM NAME="type" VALUE="application/x-java-applet;version=1.2.2">

<COMMENT>

  <EMBED type=

    "application/x-java-applet;version=1.2.2"

    width="200" height="200" align="baseline"

    code="JApplet1.class" codebase="."

pluginspage="http://java.sun.com/products/plugin/1.2/plugin-install.html">

  <NOEMBED>

</COMMENT>

   No Java 2 support for APPLET!!

  </NOEMBED>

</EMBED>

</OBJECT>

<hr></body></html>


论坛搜索《sm版》

作者：超九流

日期：2001-5-16 10:00:28

昨天不是已经回答了吗？这一段英文（incompatible types; found: java.lang.Object, required）看不懂吗？这个都要教的话，不要学JAVA了！！ <无内容> - 【sonymusic】 2001-5-15 13:01:50

奇怪，今天我想发言怎么不行呢？只有回复才行！真是见鬼了！ <无内容> - 【sonymusic】 2001-5-9 17:55:43

穷的没钱买书啊！ <无内容> - 【sonymusic】 2001-5-8 16:46:43

日剧最恶！无聊，长篇大论！ - 【sonymusic】 2001-4-26 11:45:09

是啊！这里是慢了许多，什么时候我也做一个论坛，不知道大家会不会赏光啊！ <无内容> - 【sonymusic】 2001-4-24 17:19:38

这个也要问？查api嘛！ - 【sonymusic】 2001-4-23 13:14:26

天啦，这么长！？ - 【sonymusic】 2001-4-19 23:23:31

只要你能把MS，ORACLE都收购过来，什么都好卖了。 <无内容> - 【sonymusic】 2001-4-19 15:01:31

用金山词霸嘛！猜也猜得出。 <无内容> - 【sonymusic】 2001-3-23 19:03:22

也不知道好不好用。反正我正下的过瘾呢。呵呵！ <无内容> - 【sonymusic】 2001-3-17 17:51:50

帅哥。 - 【sonymusic】 2001-3-16 20:39:55

不错。死活搞不定。：） <无内容> - 【sonymusic】 2001-3-12 9:49:02

你的签名图片显示的越来越慢了。：） - 【sonymusic】 2001-3-10 14:59:14

 今天我的机器换了一块主板，结果发现内存有问题。害的我的机器全部重装了。 - 【sonymusic】 2001-3-5 2:31:59

KAO！这么多啊！分一下类啊！做DOC给大家下载嘛！ - 【sonymusic】 2001-3-1 18:32:23

和用什么东西做无关的。关键是要有电信的卧底吧。 <无内容> - 【sonymusic】 2001-2-22 16:25:18

这样问题也要问吗？if else不会写吗？夸张！ <无内容> - 【sonymusic】 2001-2-22 11:43:13

老人家！！！ - 【sonymusic】 2001-2-21 12:46:22

 很臭的。 - 【sonymusic】 2001-2-20 9:37:08

 不好意思，这个帖子发错了。：） <无内容> - 【sonymusic】 2001-2-16 23:59:14

多新鲜哪！ - 【sonymusic】 2001-2-12 13:53:20

 怎么用都爽！不爽就自己写嘛！ <无内容> - 【sonymusic】 2001-2-5 17:23:48

什么叫合理搭配？你用的爽就行了。 <无内容> - 【sonymusic】 2001-2-2 17:06:03

 终于合并了。人民的呼声还是有一点儿用的嘛！ <无内容> - 【sonymusic】 2001-1-13 14:42:25

蛇兄，多日不见啦！ <无内容> - 【sonymusic】 2000-12-23 14:20:48

曲高则和寡！没什么的。 - 【sonymusic】 2000-12-19 16:57:30

 拍卖啊！ <无内容> - 【sonymusic】 2000-11-22 23:37:55

好玩极了！呵呵1 <无内容> - 【sonymusic】 2000-11-11 17:39:43

他前面太难过了，现在还没缓过劲来呢！：） <无内容> - 【sonymusic】 2000-11-9 14:07:17

 TOMCAT没有用过哦。算了，我帮不了你了。 <无内容> - 【sonymusic】 2000-11-7 15:45:05

 我也是看的那个，明天就疯了！：） <无内容> - 【sonymusic】 2000-10-16 17:52:50

 唉，Jsp的中文问题什么时候才能解决啊！希望明天上帝能保佑我。 <无内容> - 【sonymusic】 2000-10-12 1:50:52

 讨厌，为什么要分两个JAVA版面！？害我还要再贴一次。版主莫怪！：） - 【sonymusic】 2000-10-8 15:38:51

 哪位帅哥能贴一个JAVA中文的详细解决方案？ <无内容> - 【sonymusic】 2000-9-22 0:18:08

你要连什么数据库？我要连MYSQL，真烦人！ <无内容> - 【sonymusic】 2000-9-6 16:12:30

谁能给我一段通过JDBC连接MYSQL并存取数据的代码？我头大！！！ <无内容> - 【sonymusic】 2000-9-6 15:54:54

我申请一个aspno1，我就是老大了！：） <无内容> - 【sonymusic】 2000-8-9 22:00:50


内有一篇文章或许有用

作者：yuhj

日期：2001-5-6 11:59:57

如何用 servlet 打开非 HTML 格式的文档

一种向 Web 客户端发送非 HTML 格式文档的简单方法

摘要

Java Servlet 编程可以很方便地将 HTML 文件发送到客户端 Web 浏览器。然而许多站点还允许访问非 HTML 格式的文档，包括 Adobe PDF、Microsoft Word 和 Micorsoft Excel 等。事实上这些非 HTML 格式只要能用 MIME 类型表示，就可以利用 servlet 来发送。本文将以 PDF 和 Microsoft Word 文件为例，向你介绍如何使用 servlet 传送非 HTML 格式文件，以及与防火墙交互的方法。

你只要将文件写到 servlet 的输出流中，就可以利用 servlet 在浏览器中打开一个文件。尽管这看起来非常简单，在打开非 HTML 格式文档（比如二进制数据或多媒体文件）的时候，仍要注意一些要点。

首先从获得 servlet 的输出流开始：

    ServletOutputStream out = res.getOutputStream();

互联网上使用 MIME (multipurpos Internet mail extension 多目的互联网邮件扩展协议）来传送混合格式、多媒体和二进制数据文件。如果要在 servlet 的 response 对象中打开某个文档，就必须设置该文档的 MIME 类型。下面这个例子中我们将打开 PDF 文档。

MIME 类型

Web 浏览器使用 MIME 类型来识别非 HTML 文档，并决定如何显示该文档内的数据。将插件 (plug-in) 与 MIME 类型结合使用，则当 Web 浏览器下载 MIME 类型指示的文档时，就能够启动相应插件处理此文档。某些 MIME 类型还可以与外部程序结合使用，浏览器下载文档后会启动相应的外部程序。

MIME 类型非常有用。它们允许 Web 浏览器处理不同格式的文档，却不需要事先嵌入相关知识。Java Servlets 可以使用 MIME 类型来向浏览器传送非 HTML 文件，比如 Adobe PDF 和 Micorsoft Word。使用正确的 MIME 类型能够保证这些非 HTML 文件被正确的插件或外部程序显示。本文末的资料部分提供了一些网址，指向一些已定义 MIME 类型列表和关于 MIME 类型的文章。

PDF 文件的 MIME 类型是 "application/pdf"。要用 servlet 来打开一个 PDF 文档，需要将 response 对象中 header 的 content 类型设置成 "application/pdf":

    // MIME type for pdf doc

    res.setContentType( "application/pdf" );

若要打开一个 Microsoft Word 文档，你就要将 response 对象的 content 类型设置成 "application/msword":

    // MIME type for MSWord doc

    res.setContentType( "application/msword" );

如果是一个 Excel 文档，则使用 MIME 类型 "application/vnd.ms-excel"。其中 vnd 表示该应用程序的制造者，必须将它包含在 MIME 类型里才能够打开该类型文档。

有时候浏览器不能识别文档的 MIME 类型。通常这是由于没有安装这些文档需要的插件而导致的。这种情况下，浏览器会弹出一个对话框，询问用户是否需要打开该文件或是将它保存到本地磁盘上。

Content disposition

一种叫做 content-disposition 的 HTTP response header 允许 servlet 指定文档表示的信息。使用这种 header ，你就可以将文档指定成单独打开（而不是在浏览器中打开），还可以根据用户的操作来显示。如果用户要保存文档，你还可以为该文档建议一个文件名。这个建议名称会出现在 Save As 对话框的“文件名”栏中。如果没有指定，则对话框中就会出现 servlet 的名字。更多关于 content-disposition header 的信息，可以参阅资料。

在 servlet 中，你需要将 header 设置成下面这样：

    res.setHeader("Content-disposition",

                  "attachment; filename=" +

                  "Example.pdf" );

    // attachment - since we don't want to open

    // it in the browser, but

    // with Adobe Acrobat, and set the

    // default file name to use.

如果你要打开的是 Microsoft Word 文件，你可以设成：

    res.setHeader("Content-disposition",

                  "attachment; filename" +

                  "Example.doc" );

封装非 HTML 文档

完成上述工作后，剩下的就非常简单了。你需要根据待传送文件的名字，创建一个 java.net.URL 对象。交给 URL 构造器的字符串必须是指向该文件的一个有效 URL 地址。在这个例子中，我要打开 Adobe employment 格式的文档：

    String fileURL =

"http://www.adobe.com/aboutadobe/careeropp/pdfs/adobeapp.pdf;"

你的 URL 字符串也可以类似于 http://www.gr.com/pub/somefile.doc或 http://www.gr.com/pub/somefile.xls。但必须确保待传送文件类型与先前在 HTTP response 对象中设置的 MIME 类型一致。

    URL url = new URL ( fileURL );

防火墙

如果需要通过防火墙，最后一件要考虑的事情就是你的 URL 链接。首先应当搜集所用代理服务器的相关信息，例如主机名称和端口号等。更多关于如何通过防火墙建立链接的信息，可以参看下面的资料部分。

如果使用的是 Java 2，你应该从 URL 对象类中创建一个 URLConnection 对象，并设置下列系统属性：

    URLConnection conn = url.openConnection();

    // Use the username and password you use to

    // connect to the outside world

    // if your proxy server requires authentication.

    String authentication = "Basic " + new

sun.misc.BASE64Encoder().encode("username:password".getBytes());

    System.getProperties().put("proxySet", "true");

    System.getProperties().put("proxyHost", PROXY_HOST); // your proxy host

    System.getProperties().put("proxyPort", PROXY_PORT); // your proxy port

    conn.setRequestProperty("Proxy-Authorization", authentication);

如果你使用的是 JDK 1.1，则不能设置这些系统属性。这种情况下，你可以根据所用代理服务器的信息创建 java.net.URL 对象：

    url = new URL("http", PROXY_HOST,

                  Integer.parseInt(PROXY_PORT),

                  fileURL );

    // assumes authentication is not required

深入工作

开始阅读你传送的文档之前，首先要从 URLConnection (或 URL) 对象中获得输入流 InputStream。在这个例子中，用 BufferedInputStream 将 InputStream 封装起来。

如果你采用 URLConnection，可以尝试如下代码：

    BufferedInputStream bis = new

        BufferedInputStream(conn.getInputStream());

如果你使用 URL，则可用下列代码：

    BufferedInputStream bis = new

        BufferedInputStream(url.openStream());

一旦你完成上述操作，就只要简单地将 InputStream 中的字节，写入到 servlet 的输出流 OutputStream 中：

    BufferedOutputStream bos = new

        BufferedOutputStream(out);

    byte[] buff = new byte[2048];

    int bytesRead;

    // Simple read/write loop.

    while(-1 != (bytesRead = bis.read(buff, 0, buff.length))) {

        bos.write(buff, 0, bytesRead);

    }

在最后的代码块中，关闭这些流。

这个例子是利用 doPost 来实现的（doPost 是 HttpServlet 子类的一个方法）：

public void doPost(HttpServletRequest req,

                   HttpServletResponse res)

   throws ServletException, IOException

{

    ServletOutputStream out =

        res.getOutputStream ();

//---------------------------------------------------------------

// Set the output data's mime type

//---------------------------------------------------------------

    res.setContentType( "application/pdf" ); // MIME type for pdf doc

//---------------------------------------------------------------

// create an input stream from fileURL

//---------------------------------------------------------------

    String fileURL =

        "http://www.adobe.com/aboutadobe/careeropp/pdfs/adobeapp.pdf";

//------------------------------------------------------------

// Content-disposition header - don't open in browser and

// set the "Save As..." filename.

// *There is reportedly a bug in IE4.0 which ignores this...

//------------------------------------------------------------

    res.setHeader("Content-disposition",

                  "attachment; filename=" +=

                  "Example.pdf" );

//-----------------------------------------------------------------

// PROXY_HOST and PROXY_PORT should be your proxy host and port

// that will let you go through the firewall without authentication.

// Otherwise set the system properties and use URLConnection.getInputStream().

//-----------------------------------------------------------------

    BufferedInputStream bis = null;

    BufferedOutputStream bos = null;

    try {

        URL url = new URL( "http", PROXY_HOST,

                           Integer.parseInt(PROXY_PORT), fileURL );

        // Use Buffered Stream for reading/writing.

        bis = new BufferedInputStream(url.openStream());

        bos = new BufferedOutputStream(out);

        byte[] buff = new byte[2048];

        int bytesRead;

        // Simple read/write loop.

        while(-1 != (bytesRead = bis.read(buff, 0, buff.length))) {

            bos.write(buff, 0, bytesRead);

        }

    } catch(final MalformedURLException e) {

        System.out.println ( "MalformedURLException." );

        throw e;

    } catch(final IOException e) {

        System.out.println ( "IOException." );

        throw e;

    } finally {

        if (bis != null)

            bis.close();

        if (bos != null)

            bos.close();

    }

}

结论

正如你所读到的，用 servlet 来打开非 html 文档相当简单。即使是要通过防火墙也是如此。只要设置了正确的 MIME 类型，就可以使用同样的代码来打开图片或其他多媒体文件。当今的互联网上包含了大量信息，其中许多数据被存储为非 HTML 格式。使用 servlet 能够克服 HTML 的限制，简单方便地向用户传送这些非 HTML 格式的信息。


内置加密软件的嵌入用途Java软件今秋问世

此电子文档属C#乐园QQ群3256045整理。
内置加密软件的嵌入用途Java软件今秋问世

上一页 目录 下一页

内置加密软件的嵌入用途Java软件今秋问世

作者：罗亭

email: tinluo@263.net

日期：2001-6-19 13:51:22

/** 我看好j2me，你呢？ */

内置加密软件的嵌入用途Java软件今秋问世

    Aplix（ http://www.aplix.co.jp/）和RSA Security公司（ http://www.rsasecurity.com/japan/ ）据定针对开发嵌入用途Java的安全性功能联手合作。Aplix将在该公司的嵌入用途Java执行环境“Jblend”中作为可选功能导入RSA Security公司的加密软件“RSA BSAFE SSL-C”，并决定于今年秋季上市销售。该公司将把该产品推销给生产配备Java执行功能的手机电话制造商以及各种家电设备的制造商。

    目前，包括个人电脑在内，配备RSA的BSAFE SSL-C的设备已经超过了10亿台，使得其成为因特网加密软件的事实标准。另一方面，Aplix是日本国内最大的嵌入用途Java开发商。这两个领域的两强牵手合作，将会加快在包括手机在内的各种对应Java的设备配备加密功能的步伐。

    BSAFE SSL-C是用来使用SSL（安全接口层，Secure Socket Layer）协议加密通信数据的工具组件。通过使用嵌入该软件的JBlend，在对应Java的手机与通信服务商以及因特网邮购网站之间进行通信时可以进行128bit的加密。

    虽然目前也有配备JBlend和BSAFE SSL-C的手机，但是到目前为止设备制造商需要从Aplix和RSA Security分别购买这两个软件以后在本公司内部验证是否运行正常。今后由于Aplix销售内置BSAFE SSL-C的JBlend。Aplix的郡山龙社长在对此进行说明时表示，“由于可以做为一个整体销售并且保证产品的质量，因此设备制造商无须进行运行验证”。

    RSA Security还向Java的鼻祖美国Sun Microsystems提供BSAFE SSL-C的专利许可，不过，包括国外的公司在内，Aplix是第一家与RSA Security联手合作的嵌入用途Java供应商。今后，除了Aplix以外，“如果有机会也将与其它嵌入用途Java供应商联手合作”（RSA Security的山野修社长）。


评论（选自CSDN)

作者：lyb2001

日期：2001-3-14 16:49:26

    我没有说过要任何人跟自己的钱过不去,如果java目前是谋生的手段,用就是了.理想和生存是两回事.

我不是在批评sun,他不配,我根本不指望它会有所改变,只是希望他早点倒闭,这家公司歧视中国人已经不是一次两次了.他妈的就是不可一世的MS也没敢这么嚣张,作为一个中国人,如果你有选择的余地,就尽量不用java,这难道有错吗,只要你愿意,并有这个能力,任何人都可以开发pascal,c++编译器,但只要有sun一天在,中国人就一辈子别想开发虚拟机.

对,我口才不行,要不也不会做程序员,所以我不想跟你争逻辑问题,sun这样对中国人,难道我们难道不应该针锋相对吗.虚心学习,他根本就剥夺了你学习的权利,你能学到什么,充其量也就是自己编两个bean吧.这难道和VC,DEPHI高手有区别吗,可很多用java的人却总是说ms和borland的程序员落伍.ms和borland封锁技术是针对全世界,仅仅是商业利益的驱动,sun的做法却是对中国大陆的歧视,看看那个国家列表吧,台湾我也不想说了,把香港也列为国家是何居心,虽然国外这做的很多,但标题上至少是国家和地区.知识产权保护,放他的狗屁,上面的东欧国家那个不是海盗王国,台湾人的解密技术世界闻名,也没有听说印度人做了那么多软件在国内卖了多少,真正因此原因被排除在外的大概只有波兰.

至于有没有人把java看成中国软件的未来,这个版面这样的文章还少吗.有多少人打着反微软,民族主义的大旗劝大家java.如果单纯为了钱途去学java,我不反对,振新民族软件业得先有钱,但有不少人仅仅因为看不惯MS或是为了振新民族软件业上了java这条贼船,是不是该醒醒了.我非常尊敬这些人,他们是敢于为理想而放弃名利的人(在国内做java日子并不好过),这一点我做不到,所以我跑到国外去做廉价劳动力,中国软件要有未来,就得靠这些热血青年,我所做的,就是不希望他们至今还蒙在鼓里.

未来的路很多,除了ms,并不只有java


签名一个使用插件1。3版的Java小程序 (作者：山水 欢迎转载 哈哈！~!@#$%^&）

作者：wflm

日期：2001-6-6 16:15:48

Subject: 签名一个使用插件1。3版的Java小程序 (作者：山水 欢迎转载！ 哈哈！~!@#$%^&*）

Date: Sat, 14 Apr 2001 13:33:24 +0800

From: "山水" <wflm@263.net>

Newsgroups: ChinaFCU.JAVA

签名一个使用插件1。3版的Java小程序 (作者：山水 欢迎转载 哈哈！~!@#$%^&*）

为了签署一个小程序，有下面几件事情需要完成：

1。签名工具

2。一个RAS keypair 和一个公钥的证书链。

3。把小程序用到的所有类文件打包成一个JAR文件。

签名工具

现在有两种工具支持签署使用插件的小程序

1。Jarsigner --可以在jdk里找到2。Netscape Signing Tool 网景公司提供的一个签

署小程序的工具，用来签名在网景浏览器中使用的小程序。最新的版本可以到

http://developer.netscape.com/software/signedobj/jarpack.html下载

注意：网景公司已经不再提供旧版本的签名工具的下载

RSA 证书

RSA 证书需要向一个支持RAS的CA公司购买。如Thawte

为了使用RSA证书签名你的小程序，你需要从Thawte或是其它CA获得Java代码签名证

书。在你登记你的证书的时候可能需要你提供证书签名请求文件（CSR）你可使用以下

步骤：

---------------------------------------------------------

1。使用keytool建立一个RSA keypair

2. C:\>C:\jdk1.3\bin\keytool -genkey -keyalg rsa -alias MyCert

3. Enter keystore password: ********* file://键入你keystore的密码

4. What is your first and last name? file://以下是键入你的信息

   [Unknown]: XXXXXXX YYY

5. What is the name of your organizational unit?

6. [Unknown]: Java Software

7. What is the name of your organization?

8. [Unknown]: Sun Microsystems

9. What is the name of your City or Locality?

10. [Unknown]: Cupertino

11. What is the name of your State or Province?

12. [Unknown]: CA

13. What is the two-letter country code for this unit?

14. [Unknown]: US

15. Is <CN=XXXXXXX YYY, OU=Java Software, O=Sun Microsystems, L=Cupertino,

ST=CA, C=US> correct?

16. [no]: yes file://确认后按y结束

17. Enter key password for <MyCert> file://键入你key的密码。若按回车次密码与

键入的keystore的密码相同

18. (RETURN if same as keystore password): *********

19。使用 "keytool -certreq" 建立一个证书请求文件，拷贝并粘贴到Thawte的

webform上。例如：

20. C:\>C:\jdk1.3\bin\keytool -certreq -alias MyCert

21. Enter keystore password: *********

-----BEGIN NEW CERTIFICATE REQUEST-----

MIIBtjCCAR8CAQAwdjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRIwE

AYDVQQHEwlDdXBlcnRpbm8xGTAXBgNVBAoTEFN1biBNaWNyb3N5c3RlbX

MxFjAUBgNVBAsTDUphdmEgU29mdHdhcmUxEzARBgNVBAMTClN0YW5sZXk

gSG8wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALTgU8PovA4y59eb

oPjY65BwCSc/zPqtOZKJlaW4WP+UhmebE+T2Mho7P5zXjGf7elo3tV5uI

3vzgGfnhgpf73EoMow8EJhly4w/YsXKqeJEqqvNogzAD+qUv7Ld6dLOv0

CO5qvpmBAO6mfaI1XAgx/4xU/6009jVQe0TgIoocB5AgMBAAGgADANBgk

qhkiG9w0BAQQFAAOBgQAWmLrkifKiUYtd4ykhBtPWSwW/IKkgyfIuNMML

dF1DH8neSnXf3ZLI32f2yXvs7u3/xn6chnTXh4HYCJoGYOAbB3WNbAoQR

i6u6TLLOvgv9pMNUo6v1qB0xly1faizjimVYBwLhOenkA3Bw7S8UIVfdv

84cO9dFUGcr/Pfrl3GtQ==

-----END NEW CERTIFICATE REQUEST-----

22。你可以从邮箱里收到Thawte发给你的证书链，拷贝她并存处在一个文件中。可以使

用 "keytool -import" 把它导入你的keystore中

23. C:\>C:\jdk1.3\bin\keytool -import -alias MyCert -file VSSStanleyNew.cer

24。导入成功后你的证书及证书支持的链将生效。你现在可以使用Jarsigner签名你的

Jar文件了。注意：必须所有的步骤使用同样的别名或是不使用别名（缺省的别名是

"mykey"）

打包Java小程序

使用Jarsigner和你的RSA证书签名你的小程序，小程序必须打包成JAR文件。JAR工具也

可以在JDK里找到。

例如 C:>C:\jdk1.3\bin\jar cvf C:\TestApplet.jar .

added manifest

adding: TestApplet.class (in = 94208) (out= 20103)(deflated 78%)

adding: TestHelper.class (in = 16384) (out= 779)(deflated 95%)

这个例子将建立一个名为TestApplet.jar的JAR文件，它包含当前目录下的以及其子目

录下的所有文件

当JAR文件建立后，你需要验证它的完整性。

C:>C:\jdk1.3\bin\jar tvf TestApplet.jar

0 Mon Mar 06 18:02:54 PST 2000 META-INF/

68 Mon Mar 06 18:02:54 PST 2000 META-INF/MANIFEST.MF

94208 Wed Mar 10 11:48:52 PST 2000 TestApplet.class

16384 Wed Mar 10 11:48:52 PST 2000 TestHelper.class

这样可以保证存储在JAR里的类文件的是正确的

签名小程序

使用下面的步骤来签名小程序：

1。使用Jarsigner签名JAR文件，使用先前步骤中建里在你的keystore文件中的RSA证

书，确保别名相同。

2. C:\>C:\jdk1.3\bin\jarsigner C:\TestApplet.jar MyCert

3. Enter Passphrase for keystore: ********

4. Use "jarsigner -verify -verbose -certs" to verify the jar files

5. C:>C:\jdk1.3\bin\jarsigner -verify -verbose -certs d:\TestApplet.jar

245 Wed Mar 10 11:48:52 PST 2000 META-INF/manifest.mf

187 Wed Mar 10 11:48:52 PST 2000 META-INF/MYCERT.SF

968 Wed Mar 10 11:48:52 PST 2000 META-INF/MYCERT.RSA

smk 943 Wed Mar 10 11:48:52 PST 2000 TestApplet.class

smk 163 Wed Mar 10 11:48:52 PST 2000 TestHelper.class

X.509, CN=XXXXXXX YYY, OU=Java Software,

O=Sun Microsystems, L=Cupertino,

ST=CA, C=US (mycert)

X.509, CN=Sun Microsystems, OU=Java Plug-in QA,

O=Sun Microsystems, L=Cupertino, ST=CA, C=US

X.509, EmailAddress=server-certs@thawte.com,

CN=Thawte Server CA, OU=Certification

Services Division, O=Thawte Consulting cc,

L=Cape Town, ST=Western Cape, C=ZA

s = signature was verified

m = entry is listed in manifest

k = at least one certificate was found in keystore

i = at least one certificate was found in identity scope

jar verified.

6。现在你的小程序已经被正确的签名。可以发布你的这个小程序了。

发布签名过的小程序

1。在 EMBED/OBJECT 标记中加入 ARCHIVE=xyz.jar

2。把JAR文件及其HTML页放到web服务器上。


浅谈4种类型的JDBC驱动程序

作者：麻麻雀

email: water@sz.china.com

日期：2000-12-8 9:40:20

在网上下载JDBC驱动程序，常看见Type4字样，开始以为是厂商自己的标准，近来翻看资料，原来是SUN定义的JDBC标准。

Type1   JDBC-ODBC桥

作为JDK1.1后的一部分，是sun.jdbc.odbc包的一部分

Application--->JDBC-ODBC Bridge(Type1 jdbc driver)---->JDBC-ODBC Library--->ODBC Driver-->Database

适用于快速的原型系统，没有提供JDBC驱动的数据库如Access

Type2   JAVA to Native API

利用开发商提供的本地库来直接与数据库通信。

Application--->JDBC Driver(Type2 jdbc driver)---->Native Database library---->Database

比Type1性能略好。

Type3   Java to net

Application--->Jdbc Driver(Type3 jdbc driver)----->java middleware--->JDBC Driver---->Database

具有最大的灵活性，通常由那些非数据库厂商提供，是四种类型中最小的。

Type4 JAVA to native dababase

Application--->Jdbc driver(type4 jdbc driver)----->database engine--->database

最高的性能，通过自己的本地协议直接与数据库引擎通信，具备在Internet装配的能力。


取时间的小类

作者：瞌睡虫

email: kika2000@sina.com

日期：7/27/2001 3:20:52 PM

package pub.dates;

import java.text.DateFormat;

import java.util.*;

public class Dates

{

 Date param=null;

 Calendar ca=Calendar.getInstance();

 public Dates()

 {

  param=new Date();

  ca.setTime(param);

 }

 public Dates(Date vars)

 {

  param=vars;

  ca.setTime(param);

 }

 public Dates(int y,int m,int d)

 {

  y=y-1900;m=m-1;

  param=new Date(y,m,d);

  ca.setTime(param);

 }

 //

 public int getYear()

 {

  return ca.get(Calendar.YEAR);

 }

 public int getMonth()

 {

  return 1+ca.get(Calendar.MONTH);

 }

 public int getDay()

 {

  return ca.get(Calendar.DAY_OF_MONTH);

 }

 public int getHour()

 {

  return ca.get(Calendar.HOUR_OF_DAY);

 }

 public int getMinute()

 {

  return ca.get(Calendar.MINUTE);

 }

 public int getSecond()

 {

  return ca.get(Calendar.SECOND);

 }

 public String getDate()

 {

  return getYear()+"-"+getMonth()+"-"+getDay();

 }

 public String getTime()

 {

  return getHour() + ":" + getMinute() + ":" + getSecond();

 }

}

//package部分自己随意.

构造方法:(3个)

Dates();

Dates(Date date);

Dates(int y,int m, int d); y,m,d是年.月.日

方法:

getYear();  getMonth();  getDay(); getHour();  getMinute(); getSecond(); getDate(); getTime()


全面研读 EJB 2.0

作者：罗亭

email: tinluo@263.net

日期：2001-5-28 15:33:19

http://www.umlchina.com/CBD/ejb20.htm


让JSP页面过期, 即保证每次的JSP页面都是最新的.

作者：sonymusic

email: sonymusic@163.net

日期：8/7/2001 2:13:53 PM

response.addHeader("Cache-Control", "no-cache");

response.addHeader("Expires", "Thu,  01 Jan   1970 00:00:01 GMT");


让我来教你吧，看这个例子：JSP数据库操作例程 - 数据分页显示 - JDBC 2.0 - Oracle

作者：何志强

email: hhzqq@21cn.com

日期：2000-8-11 12:34:19

<%--

作者：何志强[hhzqq@21cn.com]

日期：2000-08-03

版本：1.0

功能：JSP数据库操作例程 - 数据分页显示 - JDBC 2.0 - Oracle

--%>

<%@ page contentType="text/html;charset=8859_1" %>

<%

//变量声明

java.sql.Connection sqlCon; //数据库连接对象

java.sql.Statement sqlStmt; //SQL语句对象

java.sql.ResultSet sqlRst;  //结果集对象

java.lang.String strCon;    //数据库连接字符串

java.lang.String strSQL;    //SQL语句

int intPageSize;           //一页显示的记录数

int intRowCount;           //记录总数

int intPageCount;          //总页数

int intPage;               //待显示页码

java.lang.String strPage;

int i;

//设置一页显示的记录数

intPageSize = 2;

//取得待显示页码

strPage = request.getParameter("page");

if(strPage==null){//表明在QueryString中没有page这一个参数，此时显示第一页数据

   intPage = 1;

}

else{//将字符串转换成整型

   intPage = java.lang.Integer.parseInt(strPage);

   if(intPage<1) intPage = 1;

}

//装载JDBC驱动程序

java.sql.DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());

//设置数据库连接字符串

strCon = "jdbc:oracle:thin:@linux:1521:ora4cweb";

//连接数据库

sqlCon = java.sql.DriverManager.getConnection(strCon,"hzq","hzq");

//创建一个可以滚动的只读的SQL语句对象

sqlStmt = sqlCon.createStatement(java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE,java.sql.ResultSet.CONCUR_READ_ONLY);

//准备SQL语句

strSQL = "select name,age from test";

//执行SQL语句并获取结果集

sqlRst = sqlStmt.executeQuery(strSQL);

//获取记录总数

sqlRst.last();

intRowCount = sqlRst.getRow();

//记算总页数

intPageCount = (intRowCount+intPageSize-1) / intPageSize;

//调整待显示的页码

if(intPage>intPageCount) intPage = intPageCount;

%>

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=gb2312">

<title>JSP数据库操作例程 - 数据分页显示 - JDBC 2.0 - Oracle</title>

</head>

<body>

<table border="1" cellspacing="0" cellpadding="0">

<tr>

   <th>姓名</th>

   <th>年龄</th>

</tr>

<%

if(intPageCount>0){

   //将记录指针定位到待显示页的第一条记录上

   sqlRst.absolute((intPage-1) * intPageSize + 1);

   //显示数据

   i = 0;

   while(i<intPageSize && !sqlRst.isAfterLast()){

      %>

<tr>

   <td><%=sqlRst.getString(1)%></td>

   <td><%=sqlRst.getString(2)%></td>

</tr>

      <%

      sqlRst.next();

      i++;

   }

}

%>

</table>

第<%=intPage%>页&nbsp;&nbsp;共<%=intPageCount%>页&nbsp;&nbsp;<%if(intPage<intPageCount){%><a href="jdbc20-oracle.jsp?page=<%=intPage+1%>">下一页</a><%}%>&nbsp;&nbsp;<%if(intPage>1){%><a href="jdbc20-oracle.jsp?page=<%=intPage-1%>">上一页</a><%}%>

</body>

</html>

<%

//关闭结果集

sqlRst.close();

//关闭SQL语句对象

sqlStmt.close();

//关闭数据库

sqlCon.close();

%>


认 识 JINI (1 )

作者：luflying

email: zhang_yng@citiz.net

日期：2000-11-24 11:46:15

一、前言

长久以来许多工程师们，都一直期盼著一个超大型的计算系统，这系统是由网路上许多机器，从超大型主机乃至嵌入在某些设备上的微小晶片，彼此相互合作而成。所有的工作，都可以在任何时间被分散到网路上的任何地方、任何种类的机器上去处理。这样的系统有很好的弹性，并且可以藉由不断地更新设备，来解决更多问题。

然而，我们　要一致的语言，以及更强的通讯协定（ superprotocols）等，让网路上众多繁杂的成员，能彼此顺利地沟通。但是就目前的情况，要达到这样的目的很难，因此我们可以考虑把一种特殊的程式码（genetic code）嵌入到许多软硬体之服务中，使得网路上的成员可以轻易的相互分享彼此资源，这就是 Jini 所要达成的目标 [1] 。

Jini 是由 SUN 公司 R&D 的Bill Joy 所提出的一项技术。我们可以藉著使用Jini，创造出一个富有弹性、容易管理、且可随时随地使用各种服务的网路计算环境。为了让使用者知道网路上有哪些服务可被使用，我们必须建立一个联盟( federation )，让网路上的服务主动加入。当有某个服务加入联盟时，也等於同时向整个网路告知：「我可以被使用，有谁　要我的服务？」。使用者透过联盟，可以得知有哪些服务可以使用。然而，我们要如何才能很简单、很轻易地使用这些服务关於这个问题，也就是 Jini 的工作重点之一。

让我们来看看Jini 是怎堋做的。网路上的服务要能够成为联盟的一份子，不管是硬体服务也好、软体服务也好，都必须嵌入Jini 的程式码。此外，网路上还必须提供 JVM的环境，才能执行Jini程式以获得这些服务。Jini的程式码是利用 SUN 公司所提供的 Jini package 撰写而成。Jini package使用的语言是Java，并且包含了许多处理网路上资源的相关功能。例如：如何帮助网路服务寻找（discovery）、以及加入（join）联盟；或者帮助网路服务处理分散式的承租（leasing）、交易（ transactions)等的问题。而这些网路服务彼此沟通时，所应用的技术是 RMI（Java Remote Method Invocation ）。简言之，网路上的服务藉由 Jini 所提供的介面，得以相互沟通、合作，来完成使用者的要求。


认 识 JINI （2）

作者：luflying

email: zhang_yng@citiz.net

日期：2000-11-24 11:51:07

二、体系结构

Jini system 是由基础建设（ infrastructure）、程式设计模型（ programming model ）、服务（ services） 三方面所构成。

基础建设（ infrastructure）

Jini 基础建设的核心包含以下几个部分：

Discovery Protocol：

提供了如何让网路上任何种类的资源加入联盟的方式。

eXtended RMI ：

Jini的元件彼此沟通时所使用的机制。

Distributed Security：

定义了Jini 联盟成员的使用权限。

Lookup Service：

用来展现联盟中的所有成员，以及帮助使用者寻找网路资源，或者负责提供联盟中的资源给使用者用。

程式设计模型（ programming model ）

Jini 提供一些分散式的程式设计模型，而 Jini 的基础构造，就是利用这些模型来组合。模型所提供的介面（Interface），包括以下几个类型：

Leasing Interface：

负责管理物件被使用的时间。

Two Phase Commit Interface ：

是一个轻量级的（light-weight）、物件导向的（ object-oriented）介面。负责管理分散式交易（ transaction）的动作，如：roll back 、roll forward 等。

Events Interface：

在分散式计算的环境中，必须确保程式执行的先後顺序，利用事件的观念可以帮助我们解决这个问题。

服务（services）

有了Jini的架构以及程式设计模型後，我们可以利用这些技术来设计管理网路资源的服务，以促进分散式计算的发展。例如：JavaSpaces、Two Phase Commit Manager。


认 识 JINI （3）

作者：luflying

email: zhang_yng@citiz.net

日期：2000-11-24 11:52:08

三、JavaSpace

Jini提供了在分散式环境中寻找（ look-up）、注册（ registration）、租借（leasing）等功能。而 JavaSpaces则负责管理分散式物件的处理程序（ processing）、分享（sharing）、以及流通（migration ）等。因此 Jini 与 JavaSpaces 彼此存在著相互合作的关系[3] 。以军队作比喻， Jini扮演的是军　官的角色，负责分派许多武器装备给军队。JavaSpaces则扮演军队的角色，负则使用那些被分派的武器以执行命令。简单的说， JavaSpaces就好像网路上的一个市场，它提供一个简单、快速、统一的介面，让网路上分散的资源可以被分享、协调与流通[4] 。

JavaSpaces是用 Java所发展的技术，并且以RMI实作其网路通讯的功能，一般应用在n-tiers 架构的中间层（ middle tiers）[5] 。JavaSpaces虽然能提供　求者与供应者之间查询与沟通的机制，但它并不是资料库，而是以简单的messaging system为基础，进而提供更强大的功能。

除了Jini之外， JavaSpaces技术也可被应用在其它系统与服务中，如： Workflow systems 、Customer management systems、Supply chain management 、Intelligent rich data distribution、 Trading services 、Auction systems 、Resource allocation and management systems、Agent Systems、以及 Publish and subscribe services等[4] 。

四、应用

Jini技术有一个主要目的：让许多电子产品可以容易的被加入至网路上，也就是随插即用（plug-and-play） [6] 。任何物件，如： DVD、CD 、VCR、桌上型电脑、可携式电脑、印表机、扫描器、磁碟机、播放器、电话、电视机、警报系统、医疗器材、空调系统、厨具、汽车引擎、汽车的丁器板等，只要利用Jini技术，就可以结合各类的电子产品成为网路上的资源服务[6] ，而使用者只　透过电话或无限通讯器材就能使用这些服务。

想像一个剧情：一个使用者坐在公园里，想要透过掌上型电脑来使用网路上的印表机，但是他并没有任何印表机的驱动程式。首先，Jini会启动 Lookup服务（类似 JavaSpaces），到联盟（federation ）中搜寻符合使用者条件的印表机。找到之後，Jini会跟那台印表机沟通，取得它的驱动程式，并且传至使用者的掌上型电脑。使用者於是可以轻易的使用网路上的印表机。

我们也可以把家中的电器用品都结合到网路上，这样可以让我们在任何时间、任何地点来控制家里的电器，这将是多堋令人期待的一个理想。

目前已经有许多公司正在发展相关的技术，这些公司包括Axis、Canon、 Computer Associates、Datek 、Enocanto、Epson 、Ericsson、FedEx 、Mitsubishi、 Norwest Mortgage 、Novell、ODI、 Oki、Quantum、 Salomon Brother、 Seagate、以及 Toshiba等。

五、结论

Jini让许多电器设备、网路服务结成联盟（ federation）。 Jini的最上层以 Lookup机制为基础的目录服务（ directory service ），让许多含有 Jini技术的资源，来注册以及被公布於网路上。下一层则利用JavaSpaces 技术来管理联盟的资源，以让使用者或其他应用程式使用资源。底层则是以RMI为基础的协定，用来规范物件之间在网路上的沟通。

如同SUN公司所说：「网路就是电脑（The network is the computer）」。网路的出现提供了无限的计算资源，让我们可望达成一个超大型的计算系统。Java技术使得分散式计算变得容易可行，Jini 则实现网路就是一个超大型电脑的理想。


如何捕获视频。

作者：wflm

email: wflm@263.net

日期：6/22/2001 1:44:29 AM

捕获当前屏幕图像。

希望多多交流。8-）

package com.borland.samples.welcome;

/**

 * Title:

 * Description:

 * Copyright:    Copyright (c) 2001

 * Company:

 * @author

 * @version 1.0

 */

import java.awt.*;

import java.awt.image.*;

import com.sun.image.codec.jpeg.*;

class ImageCanvas extends Canvas{

  private Image image;

  private Dimension prefSize;

  public ImageCanvas(Image image){

    this.image = image;

    calculatePreferredSize();

  }

  public void setImage(Image image){

    this.image = image;

    calculatePreferredSize();

    repaint();

  }

  private void calculatePreferredSize(){

    prefSize = new Dimension(image.getWidth(this),image.getHeight(this));

    System.out.println(image.getWidth(this));

    setSize(prefSize);

  }

  public Dimension getPreferredSize() {

     return prefSize;

  }

  public Dimension getMinimumSize() {

     return prefSize;

  }

  public void update(Graphics g){

     paint(g);

  }

  public void paint(Graphics g){

     g.drawImage(image,0,0,null);

  }

}

class ImageUtils{

  public static Image getScreenImage(){

    Robot robot;

    try {

      robot = new Robot();

    }catch(Exception e) {

      throw new RuntimeException("unable to construct Robot");

    }

    Dimension screenDims =Toolkit.getDefaultToolkit().getScreenSize();

    Image screen =robot.createScreenCapture(new Rectangle(450,350,screenDims.width,screenDims.height)).getScaledInstance(400,300,Image.SCALE_SMOOTH);

    MediaTracker tracker =new MediaTracker(new Label());

    tracker.addImage(screen,1);

    try {

      tracker.waitForID(0);

    }catch(InterruptedException e) { /** ... */ }

    return screen;

  }

}

public class ScreenCapture extends Frame{

  private ImageCanvas canvas =new ImageCanvas(ImageUtils.getScreenImage());

  public ScreenCapture(){

    add(canvas);

    setSize(400,300);

    setVisible(true);

    Thread imageThread =new UpdateThread();

    imageThread.setDaemon(true);

    imageThread.start();

}

  class UpdateThread extends Thread{

   public void run(){

      while(true){

        try {

          Thread.currentThread().sleep(1000);

        }

        catch (Exception ex) {

        }

        canvas.setImage(ImageUtils.getScreenImage());

        validate();

     }

    }

  }

  public static void main(String[] args){

   new ScreenCapture();

  }

}


如何捕获音频及输出音频。

作者：wflm

email: wflm@263.net

日期：6/22/2001 2:04:31 AM

这是我原来用过的两段代码，输出音频和捕获音频。

构造器里的socket是用来接受来自网络的音频数据。不做网络音频可以去掉它。

希望能与大家分享经验。8-）

import java.io.*;

import javax.sound.sampled.*;

import java.net.*;

/**

* Title:        VoiceChat

* Description:  输出音频（放音程序）

* Copyright:    Copyright (c) 2001

* Company:

* @author       你猜！

* @version 1.0

*/

class Playback implements Runnable {

       final int bufSize = 16384;

       SourceDataLine line;

       Thread thread;

       Socket s;

       Playback(Socket s){//构造器 取得socket以获得网络输入流

         this.s=s;

       }

       public void start() {

           thread = new Thread(this);

           thread.setName("Playback");

           thread.start();

       }

       public void stop() {

           thread = null;

       }

       public void run() {

           AudioFormat format =new AudioFormat(8000,16,2,true,true);//AudioFormat(float sampleRate, int sampleSizeInBits, int channels, boolean signed, boolean bigEndian）

           BufferedInputStream playbackInputStream;

           try {

             playbackInputStream=new BufferedInputStream(new AudioInputStream(s.getInputStream(),format,2147483647));//封装成音频输出流，如果网络流是经过压缩的需在此加套解压流

           }

           catch (IOException ex) {

               return;

           }

           DataLine.Info info = new DataLine.Info(SourceDataLine.class,format);

           try {

               line = (SourceDataLine) AudioSystem.getLine(info);

               line.open(format, bufSize);

           } catch (LineUnavailableException ex) {

               return;

           }

           byte[] data = new byte[1024];//此处数组的大小跟实时性关系不大，可根据情况进行调整

           int numBytesRead = 0;

           line.start();

           while (thread != null) {

              try{

                 numBytesRead = playbackInputStream.read(data);

                 line.write(data, 0,numBytesRead);

              } catch (IOException e) {

                   break;

               }

           }

           if (thread != null) {

               line.drain();

           }

           line.stop();

           line.close();

           line = null;

       }

}

import java.io.*;

import javax.sound.sampled.*;

import java.net.*;

/**

* Title:        VoiceChat

* Description:  音频捕捉（录音程序）

* Copyright:    Copyright (c) 2001

* Company:

* @author       你猜！

* @version 1.0

*/

class Capture implements Runnable {

       TargetDataLine line;

       Thread thread;

       Socket s;

       BufferedOutputStream captrueOutputStream;

       Capture(Socket s){//构造器 取得socket以获得网络输出流

         this.s=s;

       }

       public void start() {

           thread = new Thread(this);

           thread.setName("Capture");

           thread.start();

       }

       public void stop() {

           thread = null;

       }

       public void run() {

           try {

             captrueOutputStream=new BufferedOutputStream(s.getOutputStream());//建立输出流 此处可以加套压缩流用来压缩数据

           }

           catch (IOException ex) {

               return;

           }

           AudioFormat format =new AudioFormat(8000,16,2,true,true);//AudioFormat(float sampleRate, int sampleSizeInBits, int channels, boolean signed, boolean bigEndian）

           DataLine.Info info = new DataLine.Info(TargetDataLine.class,format);

           try {

               line = (TargetDataLine) AudioSystem.getLine(info);

               line.open(format, line.getBufferSize());

           } catch (Exception ex) {

               return;

           }

           byte[] data = new byte[1024];//此处的1024可以情况进行调整，应跟下面的1024应保持一致

           int numBytesRead=0;

           line.start();

           while (thread != null) {

               numBytesRead = line.read(data, 0,1024);//取数据（1024）的大小直接关系到传输的速度，一般越小越快，

               try {

                 captrueOutputStream.write(data, 0, numBytesRead);//写入网络流

               }

               catch (Exception ex) {

                   break;

               }

           }

           line.stop();

           line.close();

           line = null;

           try {

               captrueOutputStream.flush();

               captrueOutputStream.close();

           } catch (IOException ex) {

               ex.printStackTrace();

           }

       }

}


如何成为一个优秀的jsp程序员

作者：运气

email: webmaster@chinaspx.com

日期：7/3/2001 2:26:17 PM

    在网上看到很多问题是关于如何学习jsp的，正好网上看到一篇关于学习jsp的文章，就摘了一部分翻译过来，希望能对大家学习jsp有点指导。

    一个普通的错误是把JSP当作简化的 Java。它不是，（事实上, JSP 是简化的 servlets 。）程序员通常试着没有学习要求的支持技巧而直接学习 JSP 。JSP 是一个衔接技术，并且成功地连接你需要理解的另外的技术。如果你已经知道 Java ， HTML 和 Javascript，这意味着JSP 将确实是简单的。

    需要成为一个成功的 JSP 程序员可以参考这个时间表。请注意下列：

    *忽略你已经熟悉的步骤。

    *训练的时间只是代表学习好足够的基础时间，这样才能转移到下一步。

    １、建立并且理解你的Web Server。

    因为Apache 是免费的并且在大多数平台上工作，为训练目的推荐 Apache。安装时间：2 天。

    ２、 保证你理解 HTML / XHTML 。

    你将需要了解html基础, 特别是 HTML 布局中的table的使用。XHTML 不久将代替 HTML ，学习 XHTML 的基础是一个好主意。许多程序员通过 HTML IDE 学习 HTML ( 集成开发环境 ) 。因为大多数 HTML IDE产生混乱的HTMl语法，所以花时间学习手工写作html是很有必要的。因为你将会使用 JSP 和 HTML 混合编程，精通HTML语法是重要的。所以，你必须能流利地写 HTML 。

    训练时间：2 ～ 4 个星期。

    ３、开始学习 Java 。

    开始学习 Java 1.3 理解 Java 基础是很重要的。不用担心学习Swing或 Java 的图形方面，因为在JSP 中你不会使用这些特征。集中精力在 Java 工作的细节，学习 Java 的逻辑，也在 Java Bean上花时间。学习Applet是好的, 但是就象Swing， JSP 的大多数应用将不使用小程序。

    训练时间：3 ～ 6 个星期。

    ３、学习 JavaScript

    学习怎么将 JavaScript在HTML中验证输入的Form元素。也学习 JavaScript怎么能在一 HTML 页以内修改Form的元素。最后要求你能从一HTML 页内的事件中触发 JavaScript Function。

    训练时间：一～ 2 个星期。

    ４、学习并且理解你的Web Server的更好的细节。

    熟悉Web Server的特征，这是很重要的。

    训练时间：2 天。

    ５、建立你的 JSP Server

    我推荐以Tomcat开始。它可以很好地运行JSP程序。当你不能在生产使用Tomcat时，学习尽可能多的知识以便于更好的运行程序。另外, 许多 JSP 程序员使用Tomcat。因此当你遇到一个问题时，你将容易发现帮助。

    安装时间：一～ 2 天。

    ６、开始学习 JSP 。

    基本的 JSP 学习通过的步骤 1到步骤6可以完成, 然后使用 JSP 对象和脚本写 JSP 程序来联系。学习 JSP 的另外一个方面可以学习怎么创建一个分布式的应用程序。

    训练时间：4 ～ 6 个星期。

    ７、学习更多的 JSP server。

    没有关于更多的 JSP Server当然也可以运行jsp程序。然而, 许多 JSP server都由自己特殊的特征，可以让你更好的理解你的JSP 工程。

    学习更多的Jsp server如何处理jsp程序是有必要的。同样也可以优化你的 JSP 应用程序，并且使之运行得更快而不出任何问题。

    训练时间：2 ～ 7 天。

    ８、 学习 JDBC 。

    JSP 大多数应用将使用数据库，JDBC 被用于数据库连接。经常忽略的一个事实就是，每个 JDBC Driver 所支持的东西是相当不同的。了解并熟悉在jsp工程上被使用的 JDBC driver的细节是很重要的。（有时这部分的学习被包含在前面 Java 或JSP的学习中了 。）

    训练时间：1～ 2 个星期。

    到现在，你已经成为了熟练的 JSP 程序员。仍然有很多需要学习，你可以考虑扩展你的知识比如 DHTML ， XML ，java证书， JSP Tag Libraries 或 Servlets , 看你想要造什么类型的网站而决定了。

    这些训练是JSP 的核心。你不必都学习上面所有的, 取决于你在工程中分配到什么任务和你已经有什么知识。但是这是我成功地训练程序员的时间表。关键的单元是时间。平均的说, 5 个月时间确实能够训练一个人 ( 从开始到完成 ) 成为一个对jsp熟悉程序员。5 个月时间似乎很长，但要成为一个资深的WEB程序员所学的东西远远不止这一些。

    也许你认为这样学习一种语言花费的时间太长了，因为学 ASP 会更快、时间会更短。 但是学习 ASP 不需要学习java的。

    下面是部分比较好的jsp学习书籍，可以直接在网上找到：

Servlets and JavaServer Pages (JSP) 1.0: A Tutorial (Marty Hall. 1999)

http://www.apl.jhu.edu/~hall/java/Servlet-Tutorial/

JSP: The Short Course (Ray Carnes 8.26.2000)

http://www.jspinsider.com/tutorials/jsp/Ray/JSPB_Intro.html

JavaServer Pages Fundamentals (Govind Seshadri 9.13.2000)

http://developer.java.sun.com/developer/onlineTraining/JSPIntro/

The Java Tutorial (Sun)

http://java.sun.com/docs/books/tutorial/

JSP Tag Extensions (Wrox 2000)

http://www.jspinsider.com/tutorials/tagextensions/wrox/4656_Content.html

JSP Product Page (Sun)

http://java.sun.com/products/jsp/


如何从mail server删除一条消息的例子

作者：jeru

email: jeru@163.net

日期：2001-1-9 18:35:34

Question  How do you delete a message from the mail server?

Derived from  An unanswered question originally posed by Benjamin Alejandro Rodriguez Rengifo

Topics  Java:API:JavaMail:IMAP4, Java:API:JavaMail:POP3

Author  John Zukowski

Created  23-Feb-00  Modified  25-Jul-00

Answer

The basic process of deleting a message is to call setFlag() on the message and set the Flags.Flag.DELETED flag to true.

message.setFlag(Flags.Flag.DELETED, true);

Then, when you close the folder, deleted messages will be removed.

Be sure to open the folder for read/write access:

folder.open(Folder.READ_WRITE);

The following program demonstrates listing each message in the folder and prompting for deletion:

import java.io.*;

import javax.mail.*;

import javax.mail.internet.*;

public class DeleteMessageExample {

  public static void main (String args[]) throws Exception {

    String host = args[0];

    String username = args[1];

    String password = args[2];

    // Get session

    Session session = Session.getInstance(

      System.getProperties(), null);

    // Get the store

    Store store = session.getStore("pop3");

    store.connect(host, username, password);

    // Get folder

    Folder folder = store.getFolder("INBOX");

    folder.open(Folder.READ_WRITE);

    BufferedReader reader = new BufferedReader (

      new InputStreamReader(System.in));

    // Get directory

    Message message[] = folder.getMessages();

    for (int i=0, n=message.length; i<n; i++) {

       System.out.println(i + ": " + message[i].getFrom()[0]

         + "\t" + message[i].getSubject());

       System.out.println("Do you want to delete message? [YES to delete]");

       String line = reader.readLine();

       // Mark as deleted if appropriate

       if ("YES".equals(line)) {

         message[i].setFlag(Flags.Flag.DELETED, true);

       }

    }

    // Close connection

    folder.close(true);

    store.close();

  }

}

You can also expunge() the Folder. However, the POP3 server from Sun does not support this operation.


如何设置application 变量

作者：jspfuns

日期：2000-8-12 12:23:32

application变量是对一个web application有效的全局变量，在保存一些全局有效的值时非常有用。与session变量不同，application变量可以用application.getAttribute()和application.setAttribute()来访问。

在JSP中，可以自动引用application变量，指代当前的web 应用。（有关web 应用，我将在另外的地方讨论。）

在引用application 变量时，必须使用同步。并且，需要测试一个application属性是否已经存在。请仔细研究下面的代码，在下面，设置了一个叫“change"的application 属性，保存一个整型值。关于在servlet中使用application变量，方法与下面的代码一致，不过，需要先说明一个application如下:

servletContext application=getServletContext();

后面的一段函数是env.java的片断，可以看看在servlet中如何使用application变量的。

Integer count = null;

synchronized (application) {

  count = (Integer) application.getAttribute("change");

  if (count == null)

    count = new Integer(0);

  count = new Integer(count.intValue() + 1);

  application.setAttribute("change", count);

}

private void printApplication(PrintWriter pw, HttpServletRequest req)

    throws IOException

  {

   ServletContext application = getServletContext();

pw.println("<h1>Application Information:</h1>");

    pw.println("<table>");

    pw.print("<tr><td>Major Version<td>");

    pw.println(application.getMajorVersion());

  pw.print("<tr><td>Minor Version<td>");

  pw.println(application.getMinorVersion());

pw.print("<tr><td>Server Info<td>");

    pw.println(application.getServerInfo());

    pw.print("<tr><td>Real Path (of pathinfo)<td>");

    pw.println(application.getRealPath(req.getPathInfo()));

    pw.print("<tr><td>Mime-type (of pathinfo)<td>");

    pw.println(application.getMimeType(req.getPathInfo()));

    pw.println("</table>");

    pw.println("<h1>Application Variables:</h1>");

    pw.println("<table>");

  Enumeration e = application.getAttributeNames();

while (e.hasMoreElements()) {

      String name = (String) e.nextElement();

    pw.print("<tr><td>");

      pw.print(name);

      pw.print("<td>");

      pw.println(application.getAttribute(name));

    }

    pw.println("</table>");

}

%>

<html>

<head><title>Counter</title></head>

<body bgcolor=#ffffff>

<h1>Welcome, visitor: <%= count %></h1>

</body>

</html>


此电子文档属C#乐园QQ群3256045整理。
如何迅速成为Java高手

作者：Tomjava 来自：未知

很多网友问我学习Java有没有什么捷径,我说“无他，唯手熟尔”。但是我却很愿意将自己学习的一些经验写出来，以便后来者少走弯路,帮助别人是最大的快乐嘛！

要想学好Java，首先要知道Java的大致分类。我们知道，自从Sun推出Java以来，就力图使之无所不包，所以Java发展到现在，按应用来分主要分为三大块：J2SE,J2ME和J2EE,这也就是Sun ONE(Open Net Environment)体系。J2SE就是Java2的标准版，主要用于桌面应用软件的编程；J2ME主要应用于嵌入是系统开发，如手机和PDA的编程；J2EE是Java2的企业版，主要用于分布式的网络程序的开发，如电子商务网站和ERP系统。J2SE开发桌面应用软件比起VC,VB,DEPHI这些传统开发语言来说，优势好象并不明显。J2ME对于初学者来说，好象又有点深奥，而且一般开发者很难有开发环境。所以现在应用最广泛又最好学的就是J2EE了。J2EE又包括许多组件，如Jsp,Servlet,JavaBean,EJB,JDBC,JavaMail等。要学习起来可不是一两天的事。

那么又该如何学习J2EE呢？当然Java语法得先看一看的，I/O包，Util包，Lang包你都熟悉了吗？然后再从JSP学起。如果你学过HTML，那么事情要好办的多，如果没有，那你快去补一补HTML基础吧。其实JSP中的Java语法也不多，它更象一个脚本语言，有点象ASP。然后你就该学一学Servlet了。Servlet就是服务器端小程序，他负责生成发送给客户端的HTML文件。JSP在执行时，也是先转换成Servlet再运行的。虽说JSP理论上可以完全取代Servlet，这也是SUN推出JSP的本意，可是Servlet用来控制流程跳转还是挺方便的，也令程序更清晰。接下来你应该学习一下Javabean了，可能你早就看不管JSP在HTML中嵌Java代码的混乱方式了，这种方式跟ASP又有什么区别呢？还好，SUN提供了Javabean可以把你的JSP中的Java代码封装起来，便于调用也便于重用。接着就是EJB了，EJB就是Enterprise JavaBean,看名字好象它是Javabean，可是它和Javabean还是有区别的。它是一个体系结构，你可以搭建更安全、更稳定的企业应用。它的大量代码已由中间件（也就是我们常听到的Weblogic,Websphere这些J2EE服务器）完成了，所以我们要做的程序代码量很少，大部分工作都在设计和配置中间件上。至于JDBC，就不用我多说了，你如果用java编过存取数据库的程序，就应该很熟悉。还有，如果你要用Java编发送电子邮件的程序，你就得看看Javamail了。

好了，对Java和J2EE有了一些基本概念之后，你就应该编一些程序了，千万不要纸上谈兵哦。最好找一些有实例且带光盘的书来看，这样看到好的程序就可以直接Ctrl+C再Ctrl+V，也不用劳您老大架再亲自把它 再输一遍吧，再说直接复制还不用怕出错，何乐而不为呢！还有就是要经常上一些好的Java编程文章，有好的文章要Cut下来，有问题尽管问，只要问题不是太傻，一般高手都会回答你的。

下面介绍几个好的Java方面的编程网站：
CSDN论坛 http://www.csdn.net/ 中国最有名的技术论坛，《程序员》杂志就是他们出版的，你可以在上面提出问题，马上就有人回答你，如果你觉得好，你可以给那人加分；
Java研究组织http://www.javaresearch.org/ 上面有很多原创文章，高手还是挺多的；
Java开发者 http://www.chinajavaworld.com/ 那里Java资料比较全；
java.com.cn http://www.java.com.cn/ 看这域名就知道有多牛，注册用户快接近一万了，同时在线人数也在一千左右，人气很旺的；
IBM的开发者网络http://www-900.ibm.com/developerWorks/cn/java/index.shtml IBM永远的蓝色巨人；

那么我书也看了，程序也做了，别人问我的问题我都能解决了，是不是就成为高手了呢？当然没那么简单，这只是万里长征走完了第一步。不信？那你出去接一个项目，你知道怎么下手吗，你知道怎么设计吗，你知道怎么组织人员进行开发吗？你现在脑子里除了一些散乱的代码之外，可能再没有别的东西了吧！你现在最缺的是实际的工作经验，而不是书本上那些凭空想出来的程序。所以你快去找一份Java的编程工作来做吧（如果是在校学生可以去做兼职啊），在实践中提高自己，那才是最快的。不过你得祈祷在公司里碰到一个高手，而且他还愿意不厌其烦地教你，这样好象有点难哦！

还有一个办法就是读开放源码的程序了。我们知道开放源码大都出自高手，他们设计合理，考虑周到，再加上有广大的程序员参与，代码的价值自然是字字珠叽，铿锵有力（对不起，偶最近《金装四大才子》看多了）。学Java必读的两个开源程序就是Jive和Pet Store.。Jive是国外一个非常著名的BBS程序，完全开放源码。论坛的设计采用了很多先进的技术，如Cache、用户认证、Filter、XML等，而且论坛完全屏蔽了对数据库的访问，可以很轻易的在不同数据库中移植。论坛还有方便的安装和管理程序，这是我们平时编程时容易忽略的一部份（中国程序员一般只注重编程的技术含量，却完全不考虑用户的感受，这就是我们与国外软件的差距所在）。Jive的资料在很多网站上都有，大家可以找来研究一下。相信你读完代码后，会有脱胎换骨的感觉。遗憾的是Jive从2.5以后就不再无条件的开放源代码，同时有licence限制。不过幸好还有中国一流的Java程序员关注它，外国人不开源了，中国人就不能开源吗？这里向大家推荐一个汉化的Jive版本—J道。Jive(J道版)是由中国Java界大名鼎鼎的banq在Jive 2.1版本基础上改编而成, 全中文，增加了一些实用功能，如贴图，用户头像和用户资料查询等，而且有一个开发团队在不断升级。你可以访问banq的网站http://www.jdon.com/ 去下载，或到同济技术论坛的服务器上ftp://nro.shtdu.edu.cn/去下，安装上有什么问题，可以到论坛上去提问。Pet Store.（宠物店）是SUN公司为了演示其J2EE编程规范而推出的开放源码的程序，应该很具有权威性，想学J2EE和EJB的朋友不要错过了。

你一定会高兴地说，哈哈，原来成为Java高手就这么简单啊!记得Tomjava也曾碰到过一个项目经理，号称Java很简单，只要三个月就可以学会。其实说这种话的人就如当年小日本号称“三个月拿下中国”一样大言不惭。不是Tomjava泼你冷水，你现在只是学到了Java的骨架，却还没有学到Java的精髓。接下来你得研究设计模式了。设计模式是高级程序员真正掌握面向对象核心思想的必修课。设计模式并不是一种具体"技术",它讲述的是思想，它不仅仅展示了接口或抽象类在实际案例中的灵活应用和智慧，让你能够真正掌握接口或抽象类的应用，从而在原来的Java语言基础上跃进一步，更重要的是，设计模式反复向你强调一个宗旨：要让你的程序尽可能的可重用。关于设计模式的资料，还是向大家推荐banq的网站http://www.jdon.com/ ，他把GOF的23种模式以通俗易懂的方式诠释出来，纯Java描述，真是经典中的经典。有时间再研究一下MVC结构（把Model-View-Control分离开的设计思想）吧，现在很流行的Structs就是它的一种实现方式，不过Structs用起来实在是很繁，我们只要学习其精髓即可，我们完全可以设计自己的MVC结构。然后你再研究一下软件Refactoring(重整)和极限XP编程，相信你又会上一个台阶。

做完这些，你不如整理一下你的Java代码，把那些经典的程序和常见的应用整理出来，再精心打造一番，提高其重用性和可扩展性。你再找几个志同道合的朋友成立一个工作室吧，你可以去承接一些项目做了，一开始可能有些困难，可是你有技术积累，又考虑周全，接下项目来可以迅速作完，相信大家以后都会来找你的，所以Money就哗啦啦的来了。。。。。。

当然你也可以参加一些开源项目，一方面可以提高自己，另一方面也是为中国软件事业做贡献嘛！开发者在互联网上用CVS合作开发，用QQ,MSN,E-mail讨论联系，天南海北的程序员分散在各地却同时开发同一个软件，是不是很有意思呢？

下面介绍两个好的开源项目网站：
湖北省软件公共开发平台http://gro.clinux.org/
共创联盟http://cosoft.org.cn/

哇，好高兴哦，我终于成为高手了！非也，非也。古人云：“识时务者为俊杰”。你知道计算机界现在的发展形势吗？你知道微软的.NET蓝图和SUN ONE计划之间的明争暗斗吗?你知道计算机技术将向何处发展吗?其实从各大计算机厂商最近的动作,都可以看出来“Web服务将是下一代互联网应用的制高点”，而微软的.NET蓝图和SUN ONE计划的斗争焦点，也就是Web服务。Web服务就是一个崭新的分布式计算模型，它是一系列标准的综合（XML,SOAP,UDDI,WSDL和WSFL等）。它使得不同语言编写的软件能够轻易的集成起来，使网络资源和Web站点变成一种服务而不是混乱的垃圾场。不远的将来，我们就可以在家里点击一下鼠标，就可以完成出门旅游的全部准备工作，包括定飞机票，定旅游线路，定好房间等。请注意，这所有的一切都是Web站点间自动完成的，再也不用象现在一样，表面上是电子商务，实际上很多环节都是人工操作。也许你会觉得这是天方夜谈，不过就近的说，你也很有可能承接一个项目，要集成两个企业的ERP系统。很有可能上游企业的系统是用Dephi编的，而下游企业的系统是用Java编的。你说你是Java高手，大家都看者你怎么做呢。所以啊，你还得学习新技术，如Web服务，而且你Dephi也要懂一点吧（Dephi6现在已经提供Web服务的控件了）。 你编的Java系统，可能要和.NET集成，所以你.NET要懂一点吧？到最后，你可能发现你已经成为Java高手了，但很多时间却在搞别的技术。太极张三丰里说，最厉害的招式就是没有招式，可能就是这个道理吧！

因为刚刚兴起，所以网上Web服务的资料不是很多，我还是给大家推荐几个网站吧：
中国UDDI技术联盟http://www.uddi-china.org/
CSDN的柴晓路专栏http://www.csdn.net/develop/author/ColumnAuthor/fennivel/ （注：柴晓路也是互联网上一个有名的人物，他发表过很多关于Web服务的文章，还出了一书，应该称的上是中国Web服务技术的先行者）
IBM的开发者网络的XML&Web Service专栏: http://www-900.ibm.com/developerWorks/cn/xml/index.shtml?csdn IBM可是Web服务的力推者

呜…你费劲千心万苦，总算成为Java高手了，怎叫人不由喜极而泣呢！是啊，真不容易，真不简单，真叫人感动啊！那么打个电话告诉我吧，什么？我的电话号码是多少？昏到，你打电话问不就知道了吗，真是的……


如何用 servlet 打开非 HTML 格式的文档（转）

作者：zergman

email: zergman@chinaasp.com

日期：2001-6-15 13:26:01

[b]如何用 servlet 打开非 HTML 格式的文档

一种向 Web 客户端发送非 HTML 格式文档的简单方法[/b]

[b]By Marla Bonar([email]marla.bonar@javaworld.com[/email])[/b]

[BLOCKQUOTE][I][/I][b]摘要[/b]

Java Servlet 编程可以很方便地将 HTML 文件发送到客户端 Web 浏览器。然而许多站点还允许访问非 HTML 格式的文档，包括 Adobe PDF、Microsoft Word 和 Micorsoft Excel 等。事实上这些非 HTML 格式只要能用 MIME 类型表示，就可以利用 servlet 来发送。本文将以 PDF 和 Microsoft Word 文件为例，向你介绍如何使用 servlet 传送非 HTML 格式文件，以及与防火墙交互的方法。[/i][/I][/BLOCKQUOTE]你只要将文件写到 servlet 的输出流中，就可以利用 servlet 在浏览器中打开一个文件。尽管这看起来非常简单，在打开非 HTML 格式文档（比如二进制数据或多媒体文件）的时候，仍要注意一些要点。

首先从获得 servlet 的输出流开始：

    ServletOutputStream out = res.getOutputStream();

互联网上使用 MIME (multipurpos Internet mail extension 多目的互联网邮件扩展协议）来传送混合格式、多媒体和二进制数据文件。如果要在 servlet 的 response 对象中打开某个文档，就必须设置该文档的 MIME 类型。下面这个例子中我们将打开 PDF 文档。

[b]MIME 类型[/b]

Web 浏览器使用 MIME 类型来识别非 HTML 文档，并决定如何显示该文档内的数据。将插件 (plug-in) 与 MIME 类型结合使用，则当 Web 浏览器下载 MIME 类型指示的文档时，就能够启动相应插件处理此文档。某些 MIME 类型还可以与外部程序结合使用，浏览器下载文档后会启动相应的外部程序。

MIME 类型非常有用。它们允许 Web 浏览器处理不同格式的文档，却不需要事先嵌入相关知识。Java Servlets 可以使用 MIME 类型来向浏览器传送非 HTML 文件，比如 Adobe PDF 和 Micorsoft Word。使用正确的 MIME 类型能够保证这些非 HTML 文件被正确的插件或外部程序显示。本文末的[url href=http://www.cn.ibm.com/developerWorks/java/jw-tips/tip094/index.shtml#resources]MIME 类型列表和关于 MIME 类型的文章。

PDF 文件的 MIME 类型是 "application/pdf"。要用 servlet 来打开一个 PDF 文档，需要将 response 对象中 header 的 content 类型设置成 "application/pdf":

    // MIME type for pdf doc

    res.setContentType( "application/pdf" );

若要打开一个 Microsoft Word 文档，你就要将 response 对象的 content 类型设置成 "application/msword":

    // MIME type for MSWord doc

    res.setContentType( "application/msword" );

如果是一个 Excel 文档，则使用 MIME 类型 "application/vnd.ms-excel"。其中 vnd 表示该应用程序的制造者，必须将它包含在 MIME 类型里才能够打开该类型文档。

有时候浏览器不能识别文档的 MIME 类型。通常这是由于没有安装这些文档需要的插件而导致的。这种情况下，浏览器会弹出一个对话框，询问用户是否需要打开该文件或是将它保存到本地磁盘上。

[b]Content disposition[/b]

一种叫做 content-disposition 的 HTTP response header 允许 servlet 指定文档表示的信息。使用这种 header ，你就可以将文档指定成单独打开（而不是在浏览器中打开），还可以根据用户的操作来显示。如果用户要保存文档，你还可以为该文档建议一个文件名。这个建议名称会出现在 Save As 对话框的“文件名”栏中。如果没有指定，则对话框中就会出现 servlet 的名字。更多关于 content-disposition header 的信息，可以参阅[url href=http://www.cn.ibm.com/developerWorks/java/jw-tips/tip094/index.shtml#resources]

在 servlet 中，你需要将 header 设置成下面这样：

    res.setHeader("Content-disposition",

                  "attachment; filename=" +

                  "Example.pdf" );

    // attachment - since we don't want to open

    // it in the browser, but

    // with Adobe Acrobat, and set the

    // default file name to use.

如果你要打开的是 Microsoft Word 文件，你可以设成：

    res.setHeader("Content-disposition",

                  "attachment; filename" +

                  "Example.doc" );

[b]封装非 HTML 文档[/b]

完成上述工作后，剩下的就非常简单了。你需要根据待传送文件的名字，创建一个 java.net.URL 对象。交给 URL 构造器的字符串必须是指向该文件的一个有效 URL 地址。在这个例子中，我要打开 Adobe employment 格式的文档：

    String fileURL =

"http://www.adobe.com/aboutadobe/careeropp/pdfs/adobeapp.pdf;"

你的 URL 字符串也可以类似于 http://www.gr.com/pub/somefile.doc或 http://www.gr.com/pub/somefile.xls。但必须确保待传送文件类型与先前在 HTTP response 对象中设置的 MIME 类型一致。

    URL url = new URL ( fileURL );

[b]防火墙[/b]

如果需要通过防火墙，最后一件要考虑的事情就是你的 URL 链接。首先应当搜集所用代理服务器的相关信息，例如主机名称和端口号等。更多关于如何通过防火墙建立链接的信息，可以参看下面的[url href=http://www.cn.ibm.com/developerWorks/java/jw-tips/tip094/index.shtml#resources]

如果使用的是 Java 2，你应该从 URL 对象类中创建一个 URLConnection 对象，并设置下列系统属性：

    URLConnection conn = url.openConnection();

    // Use the username and password you use to

    // connect to the outside world

    // if your proxy server requires authentication.

    String authentication = "Basic " + new

sun.misc.BASE64Encoder().encode("username:password".getBytes());

    System.getProperties().put("proxySet", "true");

    System.getProperties().put("proxyHost", PROXY_HOST); // your proxy host

    System.getProperties().put("proxyPort", PROXY_PORT); // your proxy port

    conn.setRequestProperty("Proxy-Authorization", authentication);

如果你使用的是 JDK 1.1，则不能设置这些系统属性。这种情况下，你可以根据所用代理服务器的信息创建 java.net.URL 对象：

    url = new URL("http", PROXY_HOST,

                  Integer.parseInt(PROXY_PORT),

                  fileURL );

    // assumes authentication is not required

[b]深入工作[/b]

开始阅读你传送的文档之前，首先要从 URLConnection (或 URL) 对象中获得输入流 InputStream。在这个例子中，用 BufferedInputStream 将 InputStream 封装起来。

如果你采用 URLConnection，可以尝试如下代码：

    BufferedInputStream bis = new

        BufferedInputStream(conn.getInputStream());

如果你使用 URL，则可用下列代码：

    BufferedInputStream bis = new

        BufferedInputStream(url.openStream());

一旦你完成上述操作，就只要简单地将 InputStream 中的字节，写入到 servlet 的输出流 OutputStream 中：

    BufferedOutputStream bos = new

        BufferedOutputStream(out);

    byte[] buff = new byte[2048];

    int bytesRead;

    // Simple read/write loop.

    while(-1 != (bytesRead = bis.read(buff, 0, buff.length))) {

        bos.write(buff, 0, bytesRead);

    }

在最后的代码块中，关闭这些流。

这个例子是利用 doPost 来实现的（doPost 是 HttpServlet 子类的一个方法）：

public void doPost(HttpServletRequest req,

                   HttpServletResponse res)

   throws ServletException, IOException

{

    ServletOutputStream out =

        res.getOutputStream ();

//---------------------------------------------------------------

// Set the output data's mime type

//---------------------------------------------------------------

    res.setContentType( "application/pdf" ); // MIME type for pdf doc

//---------------------------------------------------------------

// create an input stream from fileURL

//---------------------------------------------------------------

    String fileURL =

        "http://www.adobe.com/aboutadobe/careeropp/pdfs/adobeapp.pdf";

//------------------------------------------------------------

// Content-disposition header - don't open in browser and

// set the "Save As..." filename.

// *There is reportedly a bug in IE4.0 which ignores this...

//------------------------------------------------------------

    res.setHeader("Content-disposition",

                  "attachment; filename=" +=

                  "Example.pdf" );

//-----------------------------------------------------------------

// PROXY_HOST and PROXY_PORT should be your proxy host and port

// that will let you go through the firewall without authentication.

// Otherwise set the system properties and use URLConnection.getInputStream().

//-----------------------------------------------------------------

    BufferedInputStream bis = null;

    BufferedOutputStream bos = null;

    try {

        URL url = new URL( "http", PROXY_HOST,

                           Integer.parseInt(PROXY_PORT), fileURL );

        // Use Buffered Stream for reading/writing.

        bis = new BufferedInputStream(url.openStream());

        bos = new BufferedOutputStream(out);

        byte[] buff = new byte[2048];

        int bytesRead;

        // Simple read/write loop.

        while(-1 != (bytesRead = bis.read(buff, 0, buff.length))) {

            bos.write(buff, 0, bytesRead);

        }

    } catch(final MalformedURLException e) {

        System.out.println ( "MalformedURLException." );

        throw e;

    } catch(final IOException e) {

        System.out.println ( "IOException." );

        throw e;

    } finally {

        if (bis != null)

            bis.close();

        if (bos != null)

            bos.close();

    }

}

[b]结论[/b]

正如你所读到的，用 servlet 来打开非 html 文档相当简单。即使是要通过防火墙也是如此。只要设置了正确的 MIME 类型，就可以使用同样的代码来打开图片或其他多媒体文件。当今的互联网上包含了大量信息，其中许多数据被存储为非 HTML 格式。使用 servlet 能够克服 HTML 的限制，简单方便地向用户传送这些非 HTML 格式的信息。

[TABLE][TR][TD][b]关于作者[/b]

Marla Bonar([email]marla.bonar@javaworld.com[/email]),亚利桑那州 Greenbrier & Russel in Phoenix 的一位顾问，自 JDK 1.0.2 出现以来就一直从事 Java 编程工作。她是面向对象体系结构和设计以及软件模式的忠实拥护者。在她父亲的鼓励下，成为一名软件工程师。 [/TD][/TR][/TABLE]

[b]资料[/b] [UL][LI]更多关于 MIME 的信息可以在 RFC：2045，2046，2047，822中找到。要查看这些 RFC，你可以访问：

[url]http://www.rfc-editor.org/rfcsearch.html[/url][LI]更多关于 content-disposition 头部的信息，参看 RFC 2183：

[url href=http://www.cn.ibm.com/developerWorks/java/jw-tips/tip094/rfc2183.txt]rfc2183.txt[/url][/LI][/UL][b]Java 要点： [/b][UL][LI]"Java Tip 42: 编写通过基于代理服务器工作的防火墙的 Java 应用程序," Ron Kurr [i](JavaWorld):[/i]

[url]http://www.javaworld.com/javaworld/javatips/jw-javatip42.html[/url][LI]"Java Tip 46: 使用 Java 1.2 的 Authenticator 类," John Zukowski [i](JavaWorld):[/i]

[url]http://www.javaworld.com/javaworld/javatips/jw-javatip46.html[/url][LI]"Java Tip 47: URL 的再确认," John Zukowski [i](JavaWorld):[/i]

[url href=http://www.cn.ibm.com/developerWorks/java/jw-tips/tip047/index.shtml]http://www.ibm.com/developerWorks/cn/java/jw-tips/tip047/index.shtml[/url][/LI][/UL]


如何在 Java 应用程序中读取 8 位和 24 位 Microsoft Windows 位图（转）

作者：zergman

email: zergman@chinaasp.com

日期：2001-6-15 13:28:17

[b]如何在 Java 应用程序中读取 8 位和 24 位 Microsoft Windows 位图

在 Java 应用程序中加载位图文件的逐步指南[/b]

[b]作者：[/b]Jeff West 和 John D. Mitchell

[BLOCKQUOTE][i][b]摘要[/b]

目前，标准的 getImage() 方法仅支持 GIF 和 JPEG 图像。尽管存在用于读取 PNG（可移植网络图形）格式的 Java 例程，但我们还没听说过有用于读取 Microsoft Windows 位图图像的阅读程序。Jeff West 撰写的这篇技巧提供了加载 Windows 位图图像的代码。[/i] [/BLOCKQUOTE]Java 的当前发行版并不正式支持在 Java 应用程序中读取 Microsoft Windows 位图文件。但别担心，我们有办法解决这个问题！这篇技巧将说明如何完成这一任务 -- 我们首先说明读取 Microsoft Windows 文件格式的基本步骤。

Windows DIB（设备独立的位图）文件格式比较简单。与纯位图格式不同，DIB 格式保留着用于在内存中存储图像的明确信息。问题是图像格式的变体如此之多（1 位、4 位、8 位和 16 位，以及其他格式）。本篇 Java 技巧中提供的解决方案只处理 8 位和 24 位两种格式。这两种格式代表了最常见的变体。

不管是哪种 Windows DIB 子类型，这种文件格式总是由 14 位文件头和 40 位信息头组成。这两个标头精确包含有关文件的存储内容和存储次序的信息。有关标头中每一项的确切含义，请参考 Microsoft Software Development Kit (SDK)。文件其余部分的内容随信息头中数据的不同而不同。

我们看一下本文要处理的两种子类型。24 位格式很简单：RGB（红-绿-蓝）颜色值（3 个字节，并按 BGR 排序）紧接在信息头之后。但是，每个扫描行都被补足到 4 个字节。按照说明文档（请参阅 Microsoft SDK）的说法，这种“补足”是为了优化 Windows 位图绘图 API。同时，底部的扫描行是文件中的第一项内容 -- 因此相对普通的图形坐标系统（其矢量方向的正向分别为向下和向右）而言，必须从后向前读取图像。

8 位子类型由于在信息头和象素数据之间插入调色板信息而复杂化。因此，每个象素条目只是进入 24 位 RGB 颜色的调色板数组的一个 8 位索引。在象素信息中，每个扫描行同样被补足到 4 个字节。

请注意，本文提供的位图图像加载方法不支持对压缩位图图像进行解压缩。实际上，这个例程甚至不寻求这种可能性！如果遇到压缩 Windows DIB 文件，该例程肯定会产生异常。Windows SDK 中有对压缩 Windows DIB 格式的说明。

至于性能，在运行 Microsoft Windows 95 的 486-DX2-66MHz 系统上，该例程读取 24 位 640 x 480 的文件（大约 920 千字节）所需的时间不超过 10 秒。使用 BufferedInputStream 而不是 FileInputStream 可明显提高性能。

以下例程读取两种文件格式中的任一种，并生成一个 Image 图像。以下代码并未包含全面的错误和异常处理，以避免使该例程更加复杂。您总可用 Windows Paint 程序对不支持的 Windows DIB 子类型进行转换。

    /**

 loadbitmap() 方法由 Windows C 代码转换而来。

 只能读取未压缩的 24 位和 8 位图像。已在

 Windows 95 上用 Microsoft Paint 保存的图像

对它进行了测试。如果图像不是 24 位或 8 位图像，

 该程序拒绝进行任何尝试。我猜测如果先用 1100，

 然后用 0011 对字节执行掩码操作，则也可将 4 位

 图像包括在内。我实际上对这些图像不感兴趣。

 如果尝试读取压缩图像，该例程可能失败，并产生

 一个 IOException 异常。如果变量 ncompression

 不为 0，则表示已经过压缩。

 参数：

     sdir 和 sfile 是 FileDialog 的

     getDirectory() 和 getFile() 方法的结果。

 返回值：

     Image 对象，切记要检查 (Image)null !!!!

    */

    public Image loadbitmap (String sdir, String sfile)

 {

 Image image;

 System.out.println("loading:"+sdir+sfile);

 try

     {

     FileInputStream fs=new FileInputStream(sdir+sfile);

     int bflen=14; // 14 字节 BITMAPFILEHEADER

     byte bf[]=new byte[bflen];

     fs.read(bf,0,bflen);

     int bilen=40; // 40 字节 BITMAPINFOHEADER

     byte bi[]=new byte[bilen];

     fs.read(bi,0,bilen);

     // 解释数据。

     int nsize = (((int)bf[5]&0xff)<<24)

  | (((int)bf[4]&0xff)<<16)

  | (((int)bf[3]&0xff)<<8)

  | (int)bf[2]&0xff;

     System.out.println("File type is :"+(char)bf[0]+(char)bf[1]);

     System.out.println("Size of file is :"+nsize);

     int nbisize = (((int)bi[3]&0xff)<<24)

  | (((int)bi[2]&0xff)<<16)

  | (((int)bi[1]&0xff)<<8)

  | (int)bi[0]&0xff;

     System.out.println("Size of bitmapinfoheader is :"+nbisize);

     int nwidth = (((int)bi[7]&0xff)<<24)

  | (((int)bi[6]&0xff)<<16)

  | (((int)bi[5]&0xff)<<8)

  | (int)bi[4]&0xff;

     System.out.println("Width is :"+nwidth);

     int nheight = (((int)bi[11]&0xff)<<24)

  | (((int)bi[10]&0xff)<<16)

  | (((int)bi[9]&0xff)<<8)

  | (int)bi[8]&0xff;

     System.out.println("Height is :"+nheight);

     int nplanes = (((int)bi[13]&0xff)<<8) | (int)bi[12]&0xff;

     System.out.println("Planes is :"+nplanes);

     int nbitcount = (((int)bi[15]&0xff)<<8) | (int)bi[14]&0xff;

     System.out.println("BitCount is :"+nbitcount);

     // 查找表明压缩的非零值

     int ncompression = (((int)bi[19])<<24)

  | (((int)bi[18])<<16)

  | (((int)bi[17])<<8)

  | (int)bi[16];

     System.out.println("Compression is :"+ncompression);

     int nsizeimage = (((int)bi[23]&0xff)<<24)

  | (((int)bi[22]&0xff)<<16)

  | (((int)bi[21]&0xff)<<8)

  | (int)bi[20]&0xff;

     System.out.println("SizeImage is :"+nsizeimage);

     int nxpm = (((int)bi[27]&0xff)<<24)

  | (((int)bi[26]&0xff)<<16)

  | (((int)bi[25]&0xff)<<8)

  | (int)bi[24]&0xff;

     System.out.println("X-Pixels per meter is :"+nxpm);

     int nypm = (((int)bi[31]&0xff)<<24)

  | (((int)bi[30]&0xff)<<16)

  | (((int)bi[29]&0xff)<<8)

  | (int)bi[28]&0xff;

     System.out.println("Y-Pixels per meter is :"+nypm);

     int nclrused = (((int)bi[35]&0xff)<<24)

  | (((int)bi[34]&0xff)<<16)

  | (((int)bi[33]&0xff)<<8)

  | (int)bi[32]&0xff;

     System.out.println("Colors used are :"+nclrused);

     int nclrimp = (((int)bi[39]&0xff)<<24)

  | (((int)bi[38]&0xff)<<16)

  | (((int)bi[37]&0xff)<<8)

  | (int)bi[36]&0xff;

     System.out.println("Colors important are :"+nclrimp);

     if (nbitcount==24)

  {

  // 24 位格式不包含调色板数据，但扫描行被补足到

  // 4 个字节。

  int npad = (nsizeimage / nheight) - nwidth * 3;

  int ndata[] = new int [nheight * nwidth];

  byte brgb[] = new byte [( nwidth + npad) * 3 * nheight];

  fs.read (brgb, 0, (nwidth + npad) * 3 * nheight);

  int nindex = 0;

  for (int j = 0; j < nheight; j++)

      {

      for (int i = 0; i < nwidth; i++)

   {

   ndata [nwidth * (nheight - j - 1) + i] =

       (255&0xff)<<24

       | (((int)brgb[nindex+2]&0xff)<<16)

       | (((int)brgb[nindex+1]&0xff)<<8)

       | (int)brgb[nindex]&0xff;

   // System.out.println("Encoded Color at ("

       +i+","+j+")is:"+nrgb+" (R,G,B)= ("

       +((int)(brgb[2]) & 0xff)+","

       +((int)brgb[1]&0xff)+","

       +((int)brgb[0]&0xff)+")");

   nindex += 3;

   }

      nindex += npad;

      }

  image = createImage

      ( new MemoryImageSource (nwidth, nheight,

          ndata, 0, nwidth));

  }

     else if (nbitcount == 8)

  {

  // 必须确定颜色数。如果 clrsused 参数大于 0，

  // 则颜色数由它决定。如果它等于 0，则根据

  // bitsperpixel 计算颜色数。

  int nNumColors = 0;

  if (nclrused > 0)

      {

      nNumColors = nclrused;

      }

  else

      {

      nNumColors = (1&0xff)<<nbitcount;

      }

  System.out.println("The number of Colors is"+nNumColors);

  // 某些位图不计算 sizeimage 域，请找出

  // 这些情况并对它们进行修正。

  if (nsizeimage == 0)

      {

      nsizeimage = ((((nwidth*nbitcount)+31) & ~31 ) >> 3);

      nsizeimage *= nheight;

      System.out.println("nsizeimage (backup) is"+nsizeimage);

      }

  // 读取调色板颜色。

  int npalette[] = new int [nNumColors];

  byte bpalette[] = new byte [nNumColors*4];

  fs.read (bpalette, 0, nNumColors*4);

  int nindex8 = 0;

  for (int n = 0; n < nNumColors; n++)

      {

      npalette[n] = (255&0xff)<<24

   | (((int)bpalette[nindex8+2]&0xff)<<16)

   | (((int)bpalette[nindex8+1]&0xff)<<8)

   | (int)bpalette[nindex8]&0xff;

      // System.out.println ("Palette Color "+n

   +" is:"+npalette[n]+" (res,R,G,B)= ("

   +((int)(bpalette[nindex8+3]) & 0xff)+","

   +((int)(bpalette[nindex8+2]) & 0xff)+","

   +((int)bpalette[nindex8+1]&0xff)+","

   +((int)bpalette[nindex8]&0xff)+")");

      nindex8 += 4;

      }

  // 读取图像数据（实际上是调色板的索引）

  // 扫描行仍被补足到 4 个字节。

  int npad8 = (nsizeimage / nheight) - nwidth;

  System.out.println("nPad is:"+npad8);

  int ndata8[] = new int [nwidth*nheight];

  byte bdata[] = new byte [(nwidth+npad8)*nheight];

  fs.read (bdata, 0, (nwidth+npad8)*nheight);

  nindex8 = 0;

  for (int j8 = 0; j8 < nheight; j8++)

      {

      for (int i8 = 0; i8 < nwidth; i8++)

   {

   ndata8 [nwidth*(nheight-j8-1)+i8] =

       npalette [((int)bdata[nindex8]&0xff)];

   nindex8++;

   }

      nindex8 += npad8;

      }

  image = createImage

      ( new MemoryImageSource (nwidth, nheight,

          ndata8, 0, nwidth));

  }

     else

  {

  System.out.println ("Not a 24-bit or 8-bit Windows Bitmap, aborting...");

  image = (Image)null;

  }

     fs.close();

     return image;

     }

 catch (Exception e)

     {

     System.out.println("Caught exception in loadbitmap!");

     }

 return (Image) null;

 }

您已掌握了读取位图文件的技巧。很容易对此方法进行扩展，使它能够读取单色和 16 色（4 位）格式。

[TABLE][TR][TD][b]作者简介[/b]

Jeff West 是加州圣地亚哥市的一名工程学研究生。在研究燃烧和火焰扩张的闲暇之余，他沉迷于 Java。 [/TD][/TR][/TABLE]


如何在 jdbc 印出中文

作者：zergman

email: zergman@chinaasp.com

日期：6/12/2001 1:22:19 PM

1. 第一个方式: 以 Java Applet -> socket -> Java Application -> database 为例:

byte[] buff = new byte[512]; //buffer size 随你高兴.

Socket sock = //这里做 socket 的初始化工作 ..

ResultSet rs = //这里做 ResultSet 的初始化工作 ..

DataOutputStream dataout =

new DataOutputStream(new BufferedoutputStream(sock.getOutputStream()));

InputStream in = rs.getAsciiStream(column_index); //column_index = Column number.

for (;;) {

int size = in.read(buff);

if (size == -1) break;

dataout.write(buff, 0, size);

}

dataout.flush();

这样就能把中文送过去.

2. 第二个方法: 把第六行改成

InputStream in = Runtime.getRuntime().getLocalizedInputStream( rs.getAsciiStream(column_index)) 试试.

3. 因 getLocalizedInputStream() 在 JDK1.1 已成为 deprecated method, 有可能不再 适用於以後新的 JDK 版本. 建议改用 BufferedReader 取代之.

例如: BufferedReader br = new BufferedReader(new InputStreamReader( rs.getAsciiStream(column_index)));


如何制作透明组件。

作者：wflm

email: wflm@263.net

日期：6/22/2001 1:41:12 AM

懒得写说明了，大家应该能看懂。这是一个透明面板的例子，可仿此法制作其它透明组件。有问题给我来信。8-）

希望与大家分享经验。

package com.borland.samples.welcome;

import java.awt.*;

public class MyPanel extends Panel {

  private Image bi; // offscreen image

  private Graphics big; // Graphics for the offscreen image

  public void update(Graphics g) {

    paint(g);

  }

  public void paint(Graphics g) {

    if (bi == null) { // you can't do this from the constructor

      bi = createImage(getSize().width,getSize().height);

      big = bi.getGraphics();

    }

    Rectangle area = g.getClipBounds(); // this is the area that needs to be (re)painted (no need to repaint everything)

    big.setClip(area);

    big.clearRect(area.x, area.y, area.width, area.height); // fills the area with the background color        // the next statement will call the paint methods for the other panels/components you have added to this panel        // and draw them to the offscreen image

    super.paint(big);// now draw the offscreen image to the panel

    g.drawImage(bi,area.x, area.y, area.x+area.width, area.y+area.height,area.x, area.y, area.x+area.width, area.y+area.height,this);

}

}


三种Web开发主流技术ASP-PHP-JSP的评价[转载] --希望给大家作为参考

作者：Legendboy

日期：2000-9-10 21:38:15

三种主流技术是哪三种呢？我主要讲一下ASP(ActiveX Server Page)、PHP、Java这三

种技术。主要是基于我到现在已经使用的以下产品而谈：

ASP

MS IIS 4/Visuale Interdev 6

PHP

Apache 1.2.x/HomeSite 4.5/PHP 4

Java

iPlanet Web Server 4.1/JBuilder 3.5/JDK 1.2.2

以上技术本人却实使用过，而且已经使用它们做过项目，基于以前的工作经验给初学者

和正在对自己应用系统进行选择的同仁谈谈自己对它们的评价，以做参考，同时也请大

家提出自己的想法和意见 ，多交流。但是我有一个这样的看法：“一个产品它存在就

有存在的理由，它不可能一无是处，而是需要你去选择一个最合适你的技术、产品”。

一个好的技术要用在最合适它的地方，所以本文可以做为你参考的一个文章，而不会告

诉你任何一个技术的“最”。

ASP

我们先来谈谈它，因为ASP是到现在开发人郡最广的一个技术了，所以想来大家也会对

它最关注。ASP是一个近乎完美的想法，可是因为MS一个大的环境的问题所以在一些方

面反回使它缩小在了一个小的框架中，如果它能更好的跳出这个框架，我想由它现在的

基础将无人可比。

ASP更精确的说是一个中间件，这个中间件将Web上的请求转入到一个解释器中，在这个

解释器中将所有的ASP的Script进行分析，再进行执行，而这时可以在这个中间件中去

创建一个新的COM对象，对这个对象中的属性和方法进行操作和调用，同时再通过这些

COM组件再完成更多的工作。所以说，ASP强大的不在于它的VBScript， 而在于它后台

的COM组件，这些组件来无限的扩充了ASP的能力。

强势：

1、简单易学。Basic有谁不会？很少会有人举手，所以有人在说这个开发基础是最广

的。而且MS有一向做的非常好的联机手册，本地化语言的帮助，同时非常快的出现教学

书籍，这样会大大方便开发人员的学习和对技术的深入研究。

2、安装使用方便。装好一个Windows 2000，只要你安装了IIS哪么ASP就可以使用了，

从没有什么需要你花心思去配置的。而说实话，ASP的调优也不难做，而要做到最细时

也不是一般人能做的了的。

3、开发工具强大而多样。这个我不用说了，可以说的就是我以前使用VB中的Web Class

开发出的Web应用一点不差，当时让许多人大吃一惊，VB也能做ASP上的应用！！！其实

这正说明了ASP开发工具的多样化，同时它们的功能也很强，这一点已经被许多程序员

所津津乐道。易用、简单、人性化，这本就是微软的强项哟。

4、效率高。在低的访问量下，ASP能体现出一定的效率，这时它对机器的要求并不高。

弱势：

1、Windows本身的所有问题都会一成不变的也累加到了它的身上。安全性、稳定性、跨

平台性（Win2K已经不再支持Alpha了）都会因为与NT的捆绑而显现出来。

2、ASP由于使用了COM组件所以它会变的十分强大，但是这样的强大由于Windows NT系

统最初的设计问题而会引发大量的安全问题。只要在这样的组件或是操作中一不注意，

哪么外部攻击就可以取得相当高的权限而.....

3、由于ASP还是一种Script语言所没除了大量使用组件外，没有办法提高其工作效率。

它必须面对即时编绎的时间考验，同时我们还不知其背后的组件会是一个什么样的状

况。

4、无法实现跨操作系统的应用。当然这也是微软的理由之一，只有这样才能发挥ASP最

佳的能力。可是我却认为正是Windows限制了ASP，ASP的概念本就是为一个能让系统运

行于一个大的多样化环境而设计的。

5、还无法完全实现一些企业级的功能：完全的集郡、负载均横。

总结：

适用人群：

熟悉MS架构的人员。想用最短的时间学习，同时想用最简单的方式完成自己的应用。

适用平台：

Windows系列平台

适用应用：

企业内部管理信息系统、中小Internet应用、快速数据库开发的应用。

学习方式：

IIS 4 Online Help(中文)

MSDN Lib相关知识

市场上众多的ASP书籍

推荐开发工具：

Visual InterDev

Home Site

Front Page

推荐开发环境：

Windows 2000 Server

MS SQL

推荐应用环境（最少）：

Windows 2000 Server/MS SQL 7

PIII 500/256M RAM/18G HD

PHP

PHP是一个什么原因出现的呢？它是一个技术人员希望能用一个快速的方面来完成他自

己的个人主页中的一个小应用。而在他一发不可收时出现了php，它使一个人能在多种

操作系统下迅速的完成一个轻型的Web应用。所以在短短的几年里，php迅速的升级了它

的版本，同时在GNU的世界里找到了一个青梅竹马的伙伴----Apache。这终于让php有了

基础，能在一个最佳的环境中运行，同时由于两个产品都是公开源代码的产品，所以大

家可以使用它们的源代码将这两个产品用最完美的方式结合到一起，使用起来就如一个

产品一样。个人认为php是自由软件在web应用平台上一个决胜的工具，如果没有php的

出现，还会有许多人认为自由软件就代表了对开发人员的高技术要求、难记的代码、复

杂的工具.....php使一个会使用basic的人员在极短的时间里快迅学习并完成用户需要

的应用。

怎么说php呢？如果说ASP是一个COM，哪么php说的更精确了可以说php是一个纯粹的

Script翻译器。这也是php由3升级为4的一个重大原因，在4这个版本里它重写了语法分

析器，从而加快了php整体的效能。而且从中你也就会明白为什么php需要支持这种或是

哪种扩充功能时它一定要与相应系统的lib库进行再编绎的原因。正因为它是正宗的

“翻译器”，所以它是将script翻译成为需要执行的函数，再去执行它们，外部扩充不

能由一些“动态加载”的方式进行，所以只能静态的编绎进php中（这只能在windows中

除外）。

强势：

1、一种能快速学习、跨平台、有良好数据库交互能力的开发语言。ASP比不上它的就是

这种跨平台能力了，而正是它的这种能力让Unix/Linux有了一种与ASP媲美的开发语

言。语法简单、书写容易、现在市面上也有了大量的书，同时Internet上也有大量的代

码可以共享，对于一个初学者想学些“高深的Unix”下的开发来说是一个决好的入手

点。

2、与Apache及其它扩展库结合紧密。php与Apache可以以静态编绎的方式结合起来，而

与其它的扩展库也可以用这样的方式结合（除外的就是Windows平台了）。这样的方式

的最大的好处就是最大化的利用了cpu时和内存，同时极为有效的利用了apache的高性

能的吞吐能力。同时外部的扩展也是静态连编，从而达到了最快的运行速度。由于与数

据库的接口也使用了这样的方式，所以使用的是本地化的调用，这也让数据库发挥了最

佳效能。

3、良好的安全性。由于php本身的代码开放所以它的代码在许多工程师手中进行了检

测，同时它与apache编绎在一起的方式也可以让它具有灵活的安全设定。所以到现在为

止，php具有了公认的安全性能。

弱势：

1、数据库支持的极大变化。由于php的所有的扩展接口都是独立团队开发完成的，同时

在开发时为了形成相应数据的个性化操作，所以php虽然支持许多数据库，可是针对每

种数据库的开发语言都完全不同。这样形成针对一种数据库的工发工作，在数据库进行

升级后需要开发人员进行几乎全部的代码更改工作。而为了让应用支持更多种的数据

库，就需要开发人员将同样的数据库操作使用不同的代码写出n种代码库出来，让程序

员的工作量大大增大。

2、安装复杂。由于php的每一种扩充模块并不是完全由php本身来完成，需要许多外部

的应用库，如图形需要gd库、LDAP需要LDAP库......这样在安装完成相应的应用后，再

联编进php中来。这也就是我以前在代码联盟新闻组中对网友所说的一定要在

FreeBSD/Linux/Unix下运行php的原因。只有在这些环境下才能方便的编绎对应的扩展

库。这些都是一般开发人员在使用php前所先要面对的问题，正是这样的问题让许多开

发人员转而使用其它的开发语言，必竟Unix没有哪么多的用户。

3、缺少企业级的支持。没有组件的支持，哪么所有的扩充就只能依靠php开发组所给出

的接口，事实上这样的接口还不够多。同时难以将集群、应用服务器这样的特性加入到

系统中去。而一个大型的站点或是一个企业级的应用一定需要这样的支持的。注：在

php的4.0版本以后加入了对servlet/javabean的支持，也许这样的支持会在以后的版本

中更加增强，也许这样的支持会是php以后的企业级支持的起点。

4、缺少正规的商业支持。这也是自由软件一向的缺点，我想在国内php的开发人员正在

快速增加，相信在不久的将来，这样的支持能多起来。

5、无法实现商品化应用的开发。由于php没有任何编绎性的开发工作，所有的开发都是

基于脚本技术来完成的。所以所有的源代码都无法编译，所以做完成的应用只能是自己

或是内部使用，无法实现商品化。

总结：

适用人群：

熟悉Unix环境，需要在最少的投入下完成快速的应用开发。

适用平台：

FreeBSD/Linux/Unix操作系统、Apache服务器

适用应用：

Internet高访问量、快速数据库开发的应用。

学习方式：

Internet上国内丰富的资源

Internet上丰富的源代码资源，完全可以参考这样的例程来完成自己的应用。

市场上最近出版的好几本php手册

推荐开发工具：

Home Site

PHPEd

推荐开发环境：

FreeBSD 4.1/RedHat 6.1操作系统

Apache 1.3.12

MySQL 3.22

P 200/64M RAM/9G HD

推荐应用环境（最少）：

FreeBSD 3.5/MySQL 3.22/Apache 1.3.12

P 200/128M RAM/9G HD

推荐应用环境（最佳）：

FreeBSD 3.5/Sybase 11/Apache 1.3.12

P III 500/512M RAM/18G HD

JSP

JSP又是什么原因出现的呢？Java在推向开发人员时出现了重重困难，首先是Java缺少

主流系统的广泛支持，而在所有的平台和技术上都缺少可以直接参照的应用方式。这时

java就走向了在各个应用层上的积极进取，在自己擅长的与不擅长的领域都走出了坚定

的脚步。其中最让Web技术人员兴奋的就是Java Servlet的出现和JSP的渐渐成熟，这一

步步都让Java正式的走向了Web开发技术的舞台。Java利用了它的跨平台技术，使java

的应用能快速的部署在各个平台上，而真的可以一处编写随处运行。它有较的利用了这

个优势，巧妙的将java的通用性、jsp的易用性、servlet的速度结合在一起，利用平台

部署上的技巧有效的屏蔽java的速度问题，所以Java迅速的在Web开发人员的团员中推

广开来。携着Java Bean、EJB、Java Application Server、Java事务服务器等这样的

强大后台走向了真正的成熟应用。

Jsp到底是一个什么样的语言呢？实际上JSP就是Java，只是它是一个特别的Java语言，

加入了一个特殊的引擎，这个引擎将HTTPServlet这个类的一些对象自动进行初始化好

让用户使用，而用户不用再去操心前面的工作。同时这个引擎又引入了〈jsp:*、〈%@等

一系列的特别语法，使用这些语法来进行一些特定的操作，如引用文件、URL转向、

JavaBean引用等这样的操作，这些都是由引擎自动帮你完成初始化的工作。我们可以将

这个引擎看作一个Jsp到Java Servlet的生成器或是翻译器，让用户不用再面对复杂的

Servlet中的还没开始工作就要写的几十行代码。这就是JSP---一个Java Servlet生成

器。JSP-〉Java Servlet Code(.java文件)-〉Java Runtime Bin Code(.class文件)，这

就是为什么我们第一次运行JSP时CPU运行能达到很高的原因，它实际上是调用了jsp引

擎来生成java文件，再用javac来编译它到class文件，这才能去执行它。同时也说明了

在第一次运行以后，速度为什么哪么快的原因，只要这个jsp文件不改变哪么这个操作

就不再会进行了（除非你删除了class文件）。所以说，java有什么jsp就有什么样的功

能，与asp一样，jsp的真正力量是我们永远看不到头的！

强势：

1、一处编写随入运行。这是一个程序员的梦想，也是从前的程序员的恶梦，为了在不

同的平台间运行，使许多程序员一行行的重写代码。在这一点上java已经给了我最完美

的答案它做的要比php更出色，除了系统之外，你的代码不用做一点更改的。

2、系统的多台平支持。我已经在差不多所有平台上见到了一致的java servlet/jsp的

Web Server/Application Server......系统它可以让你在任意环境做开发，在任意环

境进行系统部署，在任意环境扩展。相比asp/php的局限性是显面易见的。

3、强大的的可伸缩性。从只有一个小的jar文件就可以运行servlet/jsp到由多台服务

器进行集群和负载均横，到多台Application进行事务处理、消息处理......一台服务

器到无数以服务器，Java显示了一个巨人的生命力。

4、多样化和功能强大的开发工具支持。这一点与asp很像，java已经有了许多非常优秀

的开发工具而且有许多可以免费得到，并且其中的许多已经可以顺利的运行于多种平台

之下。如果你细心的使用它们会发现比自己第一面看到它们时的功能要强大的多。

弱势：

1、与ASP一样，Java的一些优势正是它致命的问题所在。正是由于为了跨平台的功能，

为了极度的伸缩能力，所以极大的增加了产品的复杂性。这么说，它在扩展时需要的分

成多少块，哪么java系统中就有多少种产品。所以你要以看到jre、jdk、jsdk、

jswdk......，实际上它们只要有效的搭配在一起，就可以产生强大的效能，密不可

分，但是对于一位初学者来说：“有没有搞错！难道让我运行一个jsp就要这么多乱七

八糟的东西！”

2、从出现的第一天起就被对手痛骂的：“慢如老牛”。真的，我在第一次运行我的jsp

时，我竟然以为我的机器死机而将哪个进程给kill了！第一次编绎、运行对于使用者来

说真是一场恶梦，在开发时我经常晚上做梦在看WinNT的任务管理器CPU格全绿时等着降

下来的样子和感觉。

3、技巧还是技巧。一个短处就一定要付出代价的。Java的运行速度是用将class常驻内

存来完成的，所以它在一些情况下所使用的内存比起用户数量来说却实是“最低性能价

格比”了。另一方面来说，它还需要硬盘空间来存储一系列的.java文件和.class文件

以及对应的版本文件。

4、缺少系统性的资料。这也许不该说，但是sun在国内做的却实很差劲，java的资烊在

国内极少，到现在我还没有在书店里看到一本jsp的书，而对java体系的论述书更是少

而又少，与java相关的书（中文版）到现在不过三位数。而再看看之中大量的具然还在

讲Visual J++或是Java 1.1这样的阵年旧货。

总结：

适用人群：

具有较强学习能力和耐心的开发人员。投入低但是希望拥有较大的伸缩能力的应用。

适用平台：

........（太多而只不再一一列说）

适用应用：

Internet/Intranet高可靠性应用、Internet/Intranet与Client/Server结合的应用系

统

学习方式：

Java的Doc文档和java站点中的一系文档

Internet的源代码资源

（对不起大家，真的不能提出更好的见议了，再有的就是大家的能力和运气，以及

Inetnet上网友的帮助了）

推荐开发工具：

JBuilder

Visual Age for java（这个我没用过，但是有人强力推荐）

Sun Forte for Java

KAWA

推荐开发环境：

Windows NT/Windows 2000开发系统

JBuilder 3.5(它能成为Web Server，甚至不再需要其它的工具)

Tomcat 3.1

JDK 1.3（可以不安装）

Access 2000

P II 266/128M RAM/9G HD

推荐应用环境（最少）：

RedHat Linux 6.2/MySQL 3.22/Apache 1.3.12/Tomcat 3.1/JDK 1.3Beta

PIII 500/512M RAM/9G HD

推荐应用环境（最佳）：

Solaris 8/Sybase 11/iPlanet Enterprise Web Server 4.1/JDK 1.3Beta

Sun Netra T1/1G RAM/18G HD


C#乐园

此电子文档属C#乐园QQ群3256045整理。


什么是Servlet以及Servlet的优越性

作者：运气

email: webmaster@chinaspx.com

日期：7/3/2001 2:27:31 PM

    Servlet是用Java编写的Server端程序，它与协议和平台无关。Servlet运行于Java－enabled Web Server中。Java Servlet可以动态地扩展Server的能力，并采用请求－响应模式提供Web服务。

    最早支持Servlet技术的是JavaSoft的Java Web Server。此后，一些其它的基于Java的Web Server开始支持标准的Servlet API。Servlet的主要功能在于交互式地浏览和修改数据，生成动态Web内容。这个过程为：

    客户端发送请求至服务器端；

    服务器将请求信息发送至Servlet

    Servlet生成响应内容并将其传给Server。响应内容动态生成，通常取决于客户端的请求

    服务器将响应返回给客户端

    Servlet看起来像是通常的Java程序。Servlet导入特定的属于Java Servlet API的包。因为是对象字节码，可动态地从网络加载，可以说Servlet对Server就如同Applet对Client一样，但是，由于Servlet运行于Server中，它们并不需要一个图形用户界面。从这个角度讲，Servlet也被称为Faceless Object。

    JAVA Servlet的优势：

    Servlet可以和其他资源(文件、数据库、Applet、Java应用程序等)交互，以生成返回给客户端的响应内容。如果需要，还可以保存请求－响应过程中的信息。

    采用Servlet，服务器可以完全授权对本地资源的访问(如数据库)，并且Servlet自身将会控制外部用户的访问数量及访问性质

    Servlet可以是其它服务的客户端程序，例如，它们可以用于分布式的应用系统中，可以从本地硬盘，或者通过网络从远端硬盘激活Servlet。

    Servlet可被链接(chain)。一个Servlet可以调用另一个或一系列Servlet，即成为它的客户端。

    采用Servlet Tag技术，可以在HTML页面中动态调用Servlet。

    Servlet API与协议无关。它并不对传递它的协议有任何假设。

    像所有的Java程序一样，Servlet拥有面向对象Java语言的所有优势

    Servlet提供了Java应用程序的所有优势——可移植、稳健、易开发。使用Servlet 的Tag技术，Servlet能够生成嵌于静态HTML页面中的动态内容。

    一个Servlet被客户端发送的第一个请求激活，然后它将继续运行于后台，等待以后的请求。每个请求将生成一个新的线程，而不是一个完整的进程。多个客户能够在同一个进程中同时得到服务。一般来说，Servlet进程只是在Web Server卸载时被卸载。

    Servlet生命周期：

    装载Servlet。这项操作一般是动态执行的。然而，Server通常会提供一个管理的选项，用于在Server启动时强制装载和初始化特定的Servlet。

    Server创建一个Servlet的实例

    Server调用Servlet的init()方法

    一个客户端的请求到达Server

    Server创建一个请求对象

    Server创建一个响应对象

    Server激活Servlet的service()方法，传递请求和响应对象作为参数

    service()方法获得关于请求对象的信息，处理请求，访问其他资源，获得需要的信息

    service()方法使用响应对象的方法，将响应传回Server，最终到达客户端。service()方法可能激活其它方法以处理请求，如doGet()或doPost()或程序员自己开发的新的方法

    对于更多的客户端请求，Server创建新的请求和响应对象，仍然激活此Servlet的service()方法，将这两个对象作为参数传递给它。如此重复以上的循环，但无需再次调用init()方法。一般Servlet只初始化一次 ,当Server不再需要Servlet时(一般当Server关闭时)，Server调用Servlet的Destroy()方法。

    在美国，EJB+Servlet+JSP几乎成为电子商务的开发标准。本来 ASP 也很有希望，但微软最近可能把精力都集中在打官司和Win2000上， 因此电子商务发展得太少。PHP由于其模式和一些天生缺陷，毫无希望。 在美国，很少有商业站点用PHP的。


实体对象的抽象以及一种基于数据库的实现(转)

作者：sure160

email: sure160@china.com

日期：6/13/2001 12:46:36 PM

简介：本文探讨了关系数据库中的实体对象在面向对象语言中如何抽象、实现，并提出一种实现方案。

一、为什么要使用实体对象的概念

实体对象指的是可永久存储的数据对象，通常可以用关系数据库的一张数据表或一张主表和与之连接的几张子表来表示。为什么要引入实体对象的概念呢？我可以肯定您是看过关于面向对象优点的长篇大论的，不过我觉得有必要再唠叨两句，因为不是所有用C++或JAVA编程的人都是使用面向对象的方式想问题的。

在现实世界中，对象比它的属性要稳定，所以数据要抽象成对象。比如由于需求的修改，一个图书管理系统要显示更多的书目详细信息，图书的属性发生了变化，而图书这个对象并没有变，和其他对象（例如书架、借阅者）之间的关系也没有变。用一个数据对象来保存书目信息的程序修改起来要简单的多。

把实体对象如何永久储存的方法封装到对象中可以实现使用对象的外部程序的与数据库无关，便于程序的移植。

在程序设计中使用实体对象概念把面向对象分析、面向对象设计、面向对象编码着几个步骤连贯和一致。

可以减少外部程序中复杂性，使用这些实体对象的程序只需要简单的调用insert()或update()的方法，而不用去写讨厌的SQL语句。

但是，由于历史原因，即使象Java这样的新兴语言，也没有在所有地方完全利用面向对象的思想。让我们看看JDBC吧，这是一个号称用对象封装的数据库接口。可是它封装的是什么呢？连接、语句、结果集和存储过程。这其实并不是我们设计系统所关心的东西，我们关心的是数据对象本身，而不是它从数据库中提取的方法。当然，JDBC本身也不该受到太多的指责，因为他本来的目标就是封装、屏蔽关系型数据库之间的差异，而不是企图实现一个面向对象的数据库。

EJB第一次引起我的注意就是因为它提出的Entity Bean，也就是实体对象。它对实体对象提供了一套完整的实现思路，但是我认为它太复杂了，主要原因是EJB想要做的事太多了。大部分情况下我并不需要分布式处理，我也不需要把实体对象存储到文件中去。我需要的仅仅是在一个本地运行的基于数据库的程序。

二、EJB如何实现的实体对象

刚才提到了EJB已经实现了实体对象，那么让我们看看它是如何实现的。EJB中Entity Bean的对象实际上是对数据对象的一种完美的抽象，在这里我们几乎看不到数据库管理系统的作用。一个实体对象有几种状态，在内存中、在磁盘缓存中、或者在数据库中，实体对象的这些状态通常我们是不关心的，EJB的容器在必要的情况下会自动转换对象的状态，也就是说自动把它存到数据库中，或从数据库中取出。我们要访问一个数据对象，要向一个对象容器提出申请，由它返回一个对象实例供我们使用。一个Entity Bean对应于数据表中的一行。如果我们访问的是同一行数据，对象容器返回的是不同的对象，但都指向同一个Entity Bean，并把我们所有的方法请求都发送给这个Entity Bean。实际上EntityBean的容器基本实现了一个面向对象的数据库。它这种实现方法带来几个显著的问题：

运行效率非常低。JAVA的速度慢是个老问题了，EJB的速度慢不光是因为大量代码用JAVA实现，而且由于它的结构，要根据数据表中的某一个属性查出一行数据，必须首先用SQL查询查找到这一行的主键（Primary Key），然后通过主键来找到这个Bean，如果这个Bean不在内存中--很不幸，这种情况经常发生，那么实际上是执行了两次SQL查询才找到一行数据。

容器本身要管理事务，以防数据的污读、污写、死锁等等一系列问题。本来DBMS管理这类问题已经有很多年经验了，已经相当完美的解决了这些问题，可是EJB不得不通过一个Transcation Server来管理这些问题。这使得容器的代码变得极其复杂，另外编程人员也不得不重新熟悉这些接口。

由于bean 中的数据是否存储在数据库里是由容器管理的，那么其他程序访问数据库会带来数据同步的问题。因此，在EJB架构中，外部程序不能直接访问数据库，只能通过EJB访问。

三、我们实现的目标

我们只想把数据库对象更好的封装起来，为什么要购买别人昂贵的代码？为什么要为我们不会用到的分布式去牺牲大量的性能。我们想要尽量使用DBMS的功能，以实现最佳的性能和最简化的代码。

事务处理最好还是用DBMS来管理，因为它一向管得很好，而且事务处理的代码很复杂，我不打算自己来完成。

我不打算用同一个对象来指向数据库的一行。数据库的一行数据在程序中可能有多个对象，这些对象都应该是临时对象，而不是永久对象。他们的共享问题由DBMS本身加锁来解决。

由于我们只是为访问DBMS提供了一个接口，外部程序完全可以不通过这个接口来访问数据库。

实现后，使用实体对象的代码要简单。例如，数据库里有一个表account，它只有两个字段accountid和name，我们把它作为一个实体对象Account，假设我们要完成从数据库查询、修改、插入，使用它的代码片段如下：

Connection conn=ConnectionPool.getConn(); //也可以通过标准的DriverManager得到数据库连接，这是完全一样的

  Conn.setAutoCommit(false); //如果不使用事务，这一行可以省去

Account a1=new Account(conn);

a1.getByAccountId(1); //查找到相应记录

System.out.println(a1.name);

a1.getByAccountIdForUpdate(1); //如果企图修改一个对象，必须通过forUpdate系列的方法得到这个对象

  a1.name="new name";

  a1.update();  //修改原有记录

Account a2=new Account(conn);

a2.accountId=3;

  a2.name="姚大";

  a2.insert();  //插入一条新记录

  conn.commit();

实体对象的属性通过方法修改是比较理想的方式，如用getName()，setName()两个方法访问Name属性。这还可以解决属性之间相互关联的问题。例如，表中有一个地区代码和地区名称两个域，必须保持一致，这就可以考虑在属性设置方法中实现。在这里我们为了简单，直接通过属性修改，在一般情况下，我觉得也是可以接受到。

有一点必须注意的就是多线程程序所带来的数据完整性问题。对于我们常使用的从数据库读取数据到对象中->修改对象属性->更新到数据库这个流程，非常容易出现数据完整性破坏问题。比如一个进程中甲对象读取数据后，另一个进程中乙对象又修改了同一数据，这时甲对象再次更新数据库会带来污写。解决的办法是对象增加一个方法给数据库的这条记录加锁。例如Account.getByAccountId函数改为Account.getByAccountIdForUpdate，这个函数中相应的sql语句（ORACLE数据库）改为"select AccountId,name from account where accountId=? for update"，这样数据取出后就自动加锁，这个锁将会在事务提交或回滚时释放。for update在SQLServer相应的语法为holdlock。

说到数据库加锁的问题就不能不考虑到数据库死锁的可能。想完全避免死锁是很困难的，只有尽量降低这种可能性。方法是：1、尽量少使用forUpdate这种函数，只有在更改数据库数据时才使用。查询的时候不要用，如果查询后根据某个条件有可能修改，那么在查询时不加锁，在修改前重新调用forUpdate函数加锁。2、尽量以某个特定的顺序加锁。例如有表A和表B，两个程序都要同时更新这两个表，最好都是先访问表A，再访问表B。

Oracle有个很好的功能就是自动检测死锁。如果发生死锁，会回滚一个事务，并返回一个SQL错误，我们的程序要检测这个异常，处理程序中可能的错误。

上面说到的对象中，只有getByXXX（）这样的方法，这种方法只会返回唯一的对象，如果想要返回一组对象，这时需要一个辅助类来实现。这个辅助类称为对象浏览器EntityBrowser。再实体对象中返回一个对象浏览器的方法一般命名为getAllByXXX()

使用对象浏览器EntityBrowser要列出所有account表中的id和name的代码如下，其实这个对象浏览器和Java定义的Enumeration接口的最大区别就是它有一个close方法。请看下面的代码片断，它输出所有的Accout对象。

       Connection conn=ConnectionPool.getConn();

       Account a1=new Account(conn);

       EntityBrowser browser=a1.getAll();

       System.out.println("==Account List==");

       While (browser.hasMoreElement()) {

              Account a=(Account)browser.nextElement();

              System.out.print(a.accountId);

              System.out.print("--");

              System.out.println(a.name);

       }

       browser.close();

以上就是我们要实现的实体对象，下面介绍如何编写一个实体对象。

四、实现的代码解释

首先，下面的代码定义一个所有实体对象的基础类EntityObject，这是一个抽象类，不能直接使用，但为其它实体对象定出了一个结构。

Import java.sql.*;

/**所有实体对象基础类*/

abstract public class EntityObject {

       protected java.sql.Connection _conn;

       private boolean _dbStored;

       /**实体对象需要用一个数据库连接初始化，这样可以利用这个连接做事务提交或回滚*/

       public EntityObject(Connection conn) {_conn=conn;_dbStored=false;}

       /**这个方法用来插入新记录，子类必须重定义这个方法*/

       public void insert() throws SQLException{ _dbStored=true;}

       /**这个方法用来修改数据库原有记录，子类必须重定义这个方法*/

       public void update() throws SQLException {_dbStored=true; }

       /**这个方法用来删除数据库原有记录，子类必须重定义这个方法*/

       public void delete() throws SQLException {_dbStored=false; }

       /**这个方法用来把数据库select语句得出的结果映射到对象的属性中去，子类必须重定义这个方法*/

       public void _setAttribute(ResultSet rs) throws SQLException {_dbStored=true; }

       /**这个方法可以判断这个对象是否已存在数据库中*/

       public boolean isDbStored() {return _dbStored;}

       /**这个方法将实体对象设为不存在数据库中，所有的getByxxx()方法首先要调用这个方法，使本对象无效，才能用select语句得到符合条件的对象*/

       public void clearDbStored() {_dbStored=false;}

}

下面我们就可以定义一个实际的实体类。考虑一种简单情况，实体对象在关系数据库中只用一张表表示。这样的实体对象定义最简单。例如，数据库里有一个表account，它只有两个字段accountid和name，下面定义它的实体类。实体类必须重载insert,update,_setAttribute方法，然后根据需要增加getByXXX()方法，根据某个属性从数据库查找一个对象。

Import java.sql.*;

public class Account extends EntityObject {

/*这里定义实体对象类所有的属性*/

       public int accountId;

       public String name;

//最好将实体对象中的属性全部定义成私有，然后定义getXXX(), setXXX()两个方法来访问这个属性，这样的封装性最好，但略过于麻烦。如果数据结构预料会经常修改，最好用这个办法。否则，定义成公有属性也可。

       Public Account(Connection conn){ super(conn); }

       //重定义这个方法，执行实际的sql命令

public void insert() throws SQLException {

              PreparedStatement ps=_conn.prepareStatement ("insert into account(accountid,name) values(?,?)");

              ps.setInt (1,accountId);

              ps.setString (2,name);

              ps.execute();

              ps.close();

              super.insert();  //注意，重载insert方法必须要有这一句

       }

//重定义这个方法，执行实际的sql命令

       public void update() throws SQLException {

              PreparedStatement ps=_conn.prepareStatement ("update account set accountid=?, name=? Where accountid=?");

              ps.setInt(1,accountId);

              ps.setString(2,name);

              ps.setInt(3,accountId);

              ps.execute();

              ps.close();

              super.update();  //注意，重载update方法必须要有这一句

       }

//重定义这个方法，执行实际的sql命令

       public void delete() throws SQLException {

              if (isDbStored()) {

                     PreparedStatement ps=_conn.prepareStatement("delete account where accountid = ? ");

                     ps.setInt(1,accountId);

                     ps.execute();

                     ps.close();

              }

              super.delete();

       }

    //下面这个方法把一个ResultSet转换成对象中的属性，下面所有的getByXXX方法都会用到这个方法。

       public void _setAttribute(java.sql.ResultSet rs) throws SQLException {

               accountId=rs.getInt(1);

               name=rs.getString(2);

               super._setAttribute(rs);  //注意，重载_setAttribute方法必须要有这一句

       }

//根据条件取得对象

       public boolean getByAccountId(int id) throws SQLException {

              clearDbStored();

              PreparedStatement ps=_conn.prepareStatement ("select AccountId,Name from account where AccountId=?");

             //这里的select语句得到的结果集必须与上面的_setAttribute方法假定的结果集一致

        ps.setInt(1,id);

              ResultSet rs=ps.executeQuery();

              if (rs.next())

                     _setAttribute(rs);

              rs.close();

              ps.close();

              return (isDbStored());

       }

//根据条件取得对象并企图修改

       public boolean getByAccountIdForUpdate(int id) throws SQLException {

              clearDbStored();

              PreparedStatement ps=_conn.prepareStatement ("select AccountId,Name from account where AccountId=? for update");

        ps.setInt(1,id);

              ResultSet rs=ps.executeQuery();

              if (rs.next())

                     _setAttribute(rs);

              rs.close();

              ps.close();

              return (isDbStored());

       }

}

上面说到的对象中，只有getByXXX（）这样的方法，这种方法只会返回唯一的对象，这时，也不用保存数据库结果集，如果出现返回一组对象的情况，就不能不保存结果集了，这时需要一个辅助类来实现。这个辅助类称为对象浏览器，下面定义对象浏览器。

import java.sql.*;

/**所有实体对象的浏览器的父类*/

abstract class EntityBrowser {

       protected ResultSet _rs;

       protected Connection _conn;

       protected boolean _hasMoreElement;

       /**浏览器是否还有下一个记录*/

       public boolean hasMoreElement() throws SQLException{

             return _hasMoreElement;

       }

       /**返回下一个记录，子类必须重载这个函数*/

       abstract public Object nextElement() throws SQLException;

       /**关闭这个浏览器*/

       public void close () throws SQLException{

              _rs.getStatement().close();

       }

       /**浏览器构造函数，指定一个数据库连接*/

       public EntityBrowser(ResultSet rs) throws SQLException {

              _rs=rs;

              _conn=_rs.getStatement().getConnection();

              _hasMoreElement=_rs.next();

       }

       //子类重载nextElement（）时调用这个方法

       protected Object _nextEntityObj(EntityObject eo) throws SQLException {

              if (_hasMoreElement) {

                     eo._setAttribute(_rs);

              } else eo=null;

              _hasMoreElement=_rs.next();

              return (eo);

       }

}

有了EntityBrowser类后，我们来修改刚才定义的Account类。第一步是要在Account类里定义一个私有的EntityBrowser子类，这个子类只要定义一个构造函数并重载nextElement()方法即可。这里使用了在一个类里定义另一个类的技巧，这个技巧在java的容器类库中经常使用。第二步是在Account类中增加一个方法getAllByXX()，返回一组实体对象的方法名常用getAll打头。请看以下修改的代码片段。

import java.sql.*;

public class Account extends EntityObject {

 。。。

/*以下是新修改的代码*/

    //定义一个子类

    private class AccountBrowser extends EntityBrowser {

              AccountBrowser (ResultSet rs) throws SQLException { super(rs); }

              Public Object nextElement() throws SQLException{

                     Account ac=new Account(_conn);

                     return (_nextEntityObj(ac));

              }

}

//下面是实体对象的方法

       public EntityBrowser getAll() throws SQLException {

              PreparedStatement ps=_conn.prepareStatement("select AccountId,Name from account ");

        ResultSet rs=ps.executeQuery();

              return (new AccountBrowser(rs));

}

public EntityBrowser getAllByName(String name) throws SQLException {

    PreparedStatement ps=_conn.prepareStatement(“select AccountId,Name from account where name like ?”;

    ps.setString(1,name);

    ResultSet rs=ps.executeQuery();

    return (new AccountBrowser(rs));

}

接下来，讨论一下数据表的连接关系，关系数据库主要有一对一，一对多，多对多，多对一的关系。比如教师和学生之间的教课关系，这是一种多对多的关系。有的关系是有自己的属性，比如教课时间，我们可以把所有的关系都看作一个实体对象。但是为了简化程序，对于那些没有自己属性的一对一，一对多关系我们可以把它简化成父、子表的关系。表示数据库中的主表和子表有两种方法。

一种方法把子表和主表看作一个实体对象，子表用Vector或HashTable这样的Collection定义成主表的一个属性，在_setAttribute方法中，再执行一次sql查询，把查询出的子表数据放入collection中。同时还要修改insert,update方法，使得主表的实体对象每次数据插入或修改时都要删除并重新插入子表的数据。这里实际上把子表看作主表的一种附属数据结构，而不是独立的对象，子表的数据库操作全部由主表来完成。

另一种方法是把子表看作一个单独的实体类，实际上这时不存在主表和子表的概念了。只有两个实体对象，他们之间是一种关联的关系。主表通过getXXByXX（）的方法来返回一个子表类的Browser。可以通过定义主表的一个方法insertXX（）, 调用子表的insert()来插入一个新的子表项目;定义deleteXXX()来删除子表项目。至于更新子表数据，可以直接调用子表实体类的update()方法，不需要使用主表类的任何方法。

对于一个视图，也可以参照定义实体类的方法定义一个视图类，不过要尽量少用视图类，因为视图类和其他实体对象虽然语法上看不出关联，但语义上实际上是有关联的。一个实体类的修改常常要修改所有相关的视图类，这对于数据封装是很不利的。只有出于性能的考虑我们才使用它。

最后，探讨一下数据库事务的概念。事务是建立在数据库连接的基础上的，可以一次提交或回滚一系列操作。要实现事务，必须把数据库连接的自动提交属性设为false。java缺省的连接都是自动提交的，实现事务必须强制执行一个conn.setAutoCommit(false)。如果连接不是自动提交的，那么要注意，每当一个事务完成时必须执行commit或rollback，就算是select语句也必须提交事务。而且事务最好能尽早提交，比如每次select后提交，这样可以减少数据库资源的占用。


使用 Java 连接处于proxy(or 防火墙)之外的HTTP服务器

作者：friendcn

email: friedcn@163.net

日期：8/2/2001 10:59:07 AM

我到sun站点上，发现他们的java Applet聊天室能够突破proxy,

我在公司的局域网可以使用的，而一般的java程序是不能够的。

大家可以去分析一下他们的程序

本技巧将向您讲述如何编写可通过代理访问因特网上的 Web 服务器的 Java 应用程序。在 Java 应用程序中加入代理支持只需额外编写几行代码，且不依赖任何安全性“漏洞”。

几乎所有的公司都十分关注保护自己的内部网络，以防黑客及入窃者。一种常见的安全措施是完全断开与因特网的连接。如果黑客们不能连接到您的任何一台机器，他们就不能非法进入您的系统。这种策略产生的不利副作用是，内部用户无法访问外部的因特网服务器，如 Yahoo 或 JavaWorld。为了解决这一问题，网络管理员通常安装“代理服务器”。实际上，代理是安装于因特网和内部网之间的一种服务，用来管理这两个领域之间的连接。代理有助于减少安全性的外部威胁，同时还允许内部用户访问因特网服务。尽管 Java 使得编写因特网客户机不再困难，但是如果客户机不能通过代理，则它们毫无用处。幸运的是，Java 使得使用代理支持不再困难 -- 如果您知道密诀，这就是事实。

将 Java 和代理结合起来的秘诀即在 Java 运行时激活特定的系统属性。这些属性未被写入正式文件，只是作为 Java 传说的一部分在 Java 编程人员中秘传。为了支持代理，Java 应用程序不仅需要指定代理本身的信息，而且需要指定用于认证的用户信息。在开始使用网际协议之前，您需要在程序中添加以下几行代码：

System.getProperties().put( "proxySet", "true" );

System.getProperties().put( "proxyHost", "myProxyMachineName" );

System.getProperties().put( "proxyPort", "85" );

上面的第一行通知 Java 您要通过代理进行连接，第二行指定代理所在的机器，第三行指定代理监听的端口。有些代理在授权用户访问因特网之前，要求用户输入用户名和口令。如果您使用位于防火墙之内的 Web 浏览器，您就可能碰到过这种情况。以下是执行认证的方法：

URLConnection connection = url.openConnection();

String password = "username:password";

String encodedPassword = base64Encode( password );

connection.setRequestProperty( "Proxy-Authorization", encodedPassword );

这段代码的思想是，您必须调整 HTTP 标头以发出用户信息。这是通过调用 setRequestProperty() 来实现的。这种方法允许您在发出请求之前处理 HTTP 标头。HTTP 要求用 base64 对用户名和口令进行编码。幸运的是，有一组公用域 API，它们将代您执行编码（请参阅参考资源部分）。

如您所见，在 Java 应用程序中加入代理支持并不需要做多少工作。有了现在的知识，再做一点研究（您必须查明您的代理是如何处理您感兴趣的协议以及如何进行用户认证的），您就能用其他协议实现代理。

FTP 代理

Scott D. Taylor 提出这个秘诀来处理 FTP 协议代理：

defaultProperties.put( "ftpProxySet", "true" );

defaultProperties.put( "ftpProxyHost", "proxy-host-name" );

defaultProperties.put( "ftpProxyPort", "85" );

接下来您便可以通过以下代码使用 "ftp" 协议访问文件 URL：

URL url = new URL("ftp://ftp.netscape.com/pub/navigator/3.04/windows/readme.txt");

如果有人有使用其他网际协议代理的例子，我很想看看。

注意：代码示例 (Example.java) 仅在 JDK 1.1.4 下测试过。

后续技巧！

来自 Marla Bonar：

对于仍在使用 JDK 1.1.7（配合 WebSphere 3.0）的开发人员而言，将 proxyHost 和 proxyPort 设为系统属性不起作用；conn.getInputStream() 或者返回连接超时，或者是找不到主机路径。但是，我使用接受 Host 和 Port 为参数的 URL 构造函数解决了这一问题（使用我的代理主机和端口）：

public URL(String protocol, String host, int port, String file).

来自 Dylan Walsh：

借助用户名和口令进行认证的方法不起作用。应将 "Basic " 置于认证字符串的开头；例如：

String encodedPassword = base64Encode( password );

应该是：

String encodedPassword = "Basic " + base64Encode( password );

您也不必用一个单独的程序来进行 64 位编码。您可以使用 sun.misc.BASE64Encoder() 类。下面是完成这两处改动之后的代码：

System.getProperties().put("proxySet", "true");

System.getProperties().put("proxyHost", proxyHost);

System.getProperties().put("proxyPort", proxyPort);

String authString = "userid:password";

String auth = "Basic " + new sun.misc.BASE64Encoder

().encode(authString.getBytes());

URL url = new URL("http://java.sun.com/" ;

URLConnection conn = url.openConnection();

conn.setRequestProperty("Proxy-Authorization", auth);

来自 Marcel Oerlemans：

下面是使用 socks 4 代理服务器的方法：

System.getProperty("socksProxySet", true);

System.getProperty("socksProxyHost", proxyHostName);

System.getProperty("socksProxyPort", proxyPort);

Usually the proxyPort for Socks 4 is port 1080

接下来您就可以用 Socks 4 进行连接了。


使用 XML 小服务程序配置文件配置小服务程序

作者：87zd

日期：2000-12-1 21:07:13

使用 XML 小服务程序配置文件配置小服务程序

这种替代的小服务程序配置方法涉及到创建一个 XML 小服务程序配置文件（该配置文件是一个 XML 文档，它的名称为 servlet_instance_name.servlet），它包括：

小服务程序类文件的文件名

小服务程序说明

小服务程序初始化参数

包含小服务程序可以调用的 JavaServer Pages（JSPs）的 URL（统一资源定位器）的页面列表。该页面列表可以包括一个缺省页面、一个出错页面、一个或多个已装入的目标页面（如果它们的名称出现在 HTTP 请求中）。

在 Application Server 版本 3 中，.servlet 文件存储在应用程序的类路径中。

当 Application Server 接收到一个对小服务程序实例的请求时，它将在其 .servlet 文件中获得该小服务程序的配置信息。为了调用 JSP，XML 小服务程序配置文件的 page-list 元素和 PageListServlet 类（将在下一章节中讨论）消除对所调用 JSPs 的 URL 硬编码。如果引用的页面发生更改时，您只需更新 .servlet 文件，而无需更新小服务程序代码和重新编译该小服务程序。每当 .servlet 文件发生更改时，Application Server 就会自动装入此小服务程序实例。

WebSphere Application Studio 提供了用于生成小服务程序的向导。这些向导为每个 Studio 生成的小服务程序创建一个 XML 小服务程序配置文件（即 .servlet 文件)。如果您不能访问 Studio，并且希望手工实现 XML 小服务程序配置，则请：

创建一个扩展 PageListServlet 类的小服务程序。

使用 XMLServletConfig类创建小服务程序实例的 XML 小服务程序配置文件

展开经编译的小服务程序及其 XML 小服务程序配置文件于 Application Server 上。

使用 PageListServlet 类

PageListServlet 是小服务程序的超级类，这些小服务程序装入 XML 小服务程序配置文件的 <page-list> 元素中包含的页面。该页面列表使您能避免对所调用 JSP 的 URI 进行硬编码。该列表可以包含一个缺省页面、出错页面和其它根据 HTTP 请求而调用的 JavaServer Page。

PageListServlet 包含一种 callPage() 方法，该方法调用了一个 JavaServer Page，并将其作为对页面列表中某个页面的 HTTP 请求的响应。当输入 XML 配置文件的 page-list 元素中的页面名称时，PageListServlet.callPage() 方法接收 HttpServletRequest 对象和 HttpServletResponse 对象。

调用 PageListServlet.callPage() 方法后并不退出。紧跟 callPage() 方法调用之后的所有小服务程序代码将在调用后运行。当您架构您的小服务程序代码时请考虑这一点。

SimplePageListServlet 是一个扩展 PageListServlet 类和使用其 callPage() 方法来调用 JSP 的小服务程序示例：

public class SimplePageListServlet extends com.ibm.servlet.PageListServlet {

   public void service(HttpServletRequest req, HttpServletResponse resp)

       throws ServletException, IOException

   {

      try{

         setRequestAttribute("testVar", "test value", req);

         setRequestAttribute("otherVar", "other value", req);

         String pageName = getPageNameFromRequest(req);

         callPage(pageName, req, resp);

      }

      catch(Exception e){

         handleError(req, resp, e);

      }

   }

}

使用 XMLServletConfig 类

使用 XMLServletConfig 类创建 XML 小服务程序配置文件（servlet_instance_name.servlet）。如下为一个配置文件：

<?xml version="1.0" ?>

<servlet>

  <code>SimplePageListServlet</code>

  <description>Shows how to use PageListServlet class</description>

  <init-parameter name="name1" value="value2"/>

  <page-list>

    <default-page>

      <uri>/index.jsp</uri>

    </default-page>

    <error-page>

      <uri>/error.jsp</uri>

    </error-page>

    <page>

      <uri>/TemplateA.jsp</uri>

      <page-name>page1</page-name>

    </page>

    <page>

      <uri>templateB.jsp</uri>

      <page-name>page2</page-name>

    </page>

  </page-list>

</servlet>

元素 <servlet> 是 XML 配置文件的基本元素。每个 XML 配置文件必须是良好格式的 XML 文档。这些文件未根据文档类型定义（DTD）进行验证。

为了使 Application Server 能使用 XML 小服务程序配置文件装入小服务程序实例，该配置文件中至少必须包含 code 元素。对于 PageListServlet，XML 配置文件则至少必须包含 code 元素和 page-list 元素。虽然其中没有 DTD，但仍建议按元素在示例中显示的顺序来显示所有元素。这些元素（也称为标记）为：

servlet

基本元素。该元素是由 XMLServletConfig 类自动生成的。

code

小服务程序（无 .class 扩展名）的类名, 即使该小服务程序位于 JAR 文件中

描述

用户定义的小服务程序描述

init-parameter

该元素的属性指定了一个作为初始化参数的“名称-值”对。一个小服务程序可以有多个初始化参数，每个参数都有它自己的 init-parameter 元素。

page-list

它根据 HTTP 请求中的路径信息来标识要调用的 JavaServer Page。page-list 元素可以包含下列子元素：

default-page：如果 HTTP 请求中未包含路径信息，那么该元素中会包含一个 uri 元素，此元素指出了装入页面的位置

error-page：如果 handleError() 方法将请求属性设置为“error”，那么该元素中会包含一个 uri 元素，此元素指出了装入页面的位置

page：如果 HTTP 请求中包含了页面名称的话，那么该元素中会包含一个 uri 元素，此元素指出了要装入页面的位置。一个 page-list 元素中可以包含多个 page 元素。

您可以编写一个使用 XMLServletConfig 类来生成小服务程序配置文件的 Java 程序。XMLServletConfig 类提供了关于设置和获取文件元素及其内容的方法。

展开服务器上的小服务程序和 .servlet 文件

为了在 Application Server 上使用经编译的小服务程序和其 XML 小服务程序配置文件：

将经编译的小服务程序和其 .servlet 文件放置在应用程序类路径中。如果该小服务程序有一个软件包名，请确保该小服务程序被放置在该类路径的正确子目录下。

请确保在页面列表中引用的 JavaServer Page（JSP 文件）位于该应用程序的文档根目录中。


使用Java Applet访问数据库（转载）

作者：wflm

日期：2001-6-5 14:28:58

使用Java Applet访问数据库

    学习任何的程序语言，当然都得与数据库，Java刚刚诞生的时候，对数据库的支持并不是很好，经过这几年的发展，它对数据库的支持也已经完全达到了成熟的境地。由于这里主要是介绍Java Applet小程序， 因此，不可能用大的篇幅去给大家介绍数据库的知识了，怎么样去建立与设计数据库，还是请大家自己找点书看看吧！这儿，对不住了。

    我们这儿以Microsoft Access数据库为例子来说明怎么实现一个数据库的打开与读取的知识。

第一步：

    使用Microsoft Access创建一个数据库,也就是我的这个例子要用到的，内容就随便了。

第二步：也就是编程序了。

1)定义变量，变量的定义如下：

  TextArea theVisits=new TextArea (6,80);

                              //显示数据库的内容

  TextField theStatus=new TextField ("",80);

                            //显示打开数据库的信息。相当于程序的状态栏

  Connection theConnection; //数据库的连接方法

  Statement theStatement;   //代表一个发送到数据库来执行的数据库命令

  ResultSet theResult;         //读取的数据结果，也就是数据库返回的结果

  ResultSetMetaData theMetaData;

                        //包含了有关数据库命令执行后 返回结果的有用信息。

  String theDataSource; //包含了被访问数据库或者数据源的名称，用URL形式表示 .

  String theUser;           //数据库的用户名

  String thePassword;      //数据库的密码

2)实现界面，可以用开始时给大家介绍的方法来实现，界面的实现相对来说，

比较的简单了，如下：

public void init()

  {

      add(theVisits);

      add(theStatus);

      theVisits.setEditable (false);   //设置文本区域不可以被用户写入

      theStatus.setEditable (false);   //设置文本区域不可以被用户写入

      openConnection();                //打开数据库的连接

      execSqlCommand("select * from MyTable");  //从数据库中读取内容

      closeConnection();               //关闭已经打开的数据库

    }

3)打开某个数据库的连接

public void openConnection()

    {

    theDataSource="jdbc:odbc:MyAccess";

    theUser="";

    thePassword="";

    try

    {

    Class.forName("Sun.jdbc.odbc.JdbcOdbcDriver");

theConnection=DriverManager.getConnection(theDataSource,theUser,thePassword);

        theStatus.setText("Status:OK");

    }

    catch (Exception e)

        {

        handleException(e);

        }

    }

     包含数据源名称的字符串格式为一个特殊的语句：

jdbc:<subpotocol>:<subname>

     协议 名jdbc必须提供，子协议和子名称根据使用数据源类型而变化。

JDBC DriverManager使用子协议来选择书记源响应的驱动程序。子协议

的通常值为“odbc”和“oracle“，子名称包含了该驱动程序使用的附加

信息。通常情况，该子名称是某个网络名称，例如，用于其它Web服务：

jdba:<subpotocol>://<host.domin><port>/<databasename>

    驱动程序的说明应该指定子协议和子名称的正确形式。使用ODBC时，主机

和端口信息没有必要提供，因为数据源总是配置在本地主机上。用于ODBC数据源的形式是

jdbc:odbc:<data source name>

    为了打开数据库连接，用户必须使用具体的驱动程序另外，驱动程序应该指

定如何这样做。如果正在使用JDBC-ODBC桥程序，那么可以通过使用名为Class类

的forName()方法来使用具体的JDBC-ODBC桥驱动程序。

class.forName("sun.jdbc.JdbcOdbcDriver");

    然后，拥护可以告诉rManager打开数据源的连接，使用下列方法：

theConnection=DriverManager.getConnection(theDataSource,theUser,thePassword);

    getConnection()方法返回一个Connection对象，用户应该保存这个对象，

因为它是访问数据源的方法。这里，该对象保存到名为theConnection的字段中。

    如果在打开连接过程中出现错误，则SQLexception作废，通过在try-catch

块内打开连接的语句，拥护可以处理这种异常。通过使用后面要讲的handleException()，

在TextField内显示一个简短的相应信息。

4)执行SQL命令

public void execSQLCommand(String command)

{

    try{

        theStatement=theConnection.createStatement();

        theResult=theStatement.executeQuery (command);

        theMetaData=theResult.getMetaData ();

        int columnCount=theMetaData.getColumnCount ();

        theVisits.setText("");

        while(theResult.next ())

        {

            for(int i =1;i<=columnCount;i++)

            {

                String colValue=theResult.getString(i);

                if(colValue==null)colValue="";

                theVisits.append (colValue+";");

            }

            theVisits.append ("\n");

        }

    }catch(Exception e)

    {

        handleException(e);

    }

}

    为了执行该命令，程序使用Connection对象的createStatement()方法创建

一个可以有查询的Statement。然后，它激活Statement对象的executiveQuery()

方法，用来传递包含SQL查询语句的字符串。从init()中传送过来的参数——

Slect * from MyTable是一个简单的数据库查询语言，如果你还不知道什么意思，

那么请你还是查看一本有关数据库的书吧，这些内容很多，不是我这儿一下子能说

清楚的了。它的意思简单的说，就是查找表——MyTable中的所有（*）信息。它会

返回数据库中的所有内容，并把它作为ResultSet访问的结果。接着该程序激活ResultSet

对象的getMetaData()方法。这时，返回一个ResultSetMetaData值，该值存储在

一个称为theMetaData的变量中。使用ResultSetMetaData对象，可以获得很多有用的

数据；这里该程序使用getColumnCount()方法获得结果表中列的数量。最后，该程序反

复使用该结果表，激活theResult的next()方法获得结果表中的每一条记录，只到该方法

使用完全部记录返回flase为止。

5)关闭数据库连接

public void closeConnection()

    {

    try{

        theConnection.close ();

        }

        catch(Exception e)

        {

        handleException(e);

        }

    }

    当用户访问完某个数据库时，应该关闭数据库连接，释放与连接有关的资源。用户创建

的任何打开的ResultSet或者Statement对象将 自动关闭。另外，在关闭连接时可能发

生一个SQLException，也放到后面处理。

6)处理异常和错误

public void handleException(Exception e)

    {

    theStatus.setText("Error:"+e.getMessage ());

    e.printStackTrace ();

    if(e instanceof SQLException)

        {

        while((e=((SQLException)e).getNextException ())!=null)

         {

                System.out.println(e);

            }

        }

    }

    handleException()方法用来处理以上的错误和异常。该方法设置状态TextField包含与

异常有关的错误消息，使用getMessage()方法获得该消息。它还可以在System.out打印

堆栈轨迹。

三 安装ODBC数据源

为了运行这个应用程序，用户必须 首先创建一个与MyTable数据库相对应的 ODBC数据源。

为了通过ODBC使用Access,用户必须安装Access Odbc驱动程序。如果用户没有安装该驱动

程序，那么还是先安装吧，什么，不会，不会也不行了，找一本书好好看看吧，这是肯定要

看的。我这儿简单的给你说一下吧：

1.从控制面板中选择ODBC-32，双击（要是没有这个图标，那我真的没有办法了，它在安装

数据库的时候，应该会安装上的，否则就只能找一张数据库系统的安装盘，找到ODBC驱动程

序进行安装）。

2.单击添加，选择Microsoft Access Driver。（如果这个图标也没有，那么你的机器是严

重缺乏营养，赶紧装数据库必须的软件，先装Access，这儿要用）

3.输入你要使用的数据源的名称（如果没有，那就得先创建，我这儿就不说Access的使用方

法了）。描述就随便你了。

4.单击选择按钮选择你已经创建好了的Access数据库的文件。

5.点确定就可以了。如果你想为你数据库保密的话，可以加上用户名和密码。（在高级选项里面）。

        要说明一点的是，如果你用VJ++编辑的此程序，要是运行不了，那就是数据库的连接没有设

置好，怎么将数据库设计好，以后再说吧。另外，VJ++有它自己的数据库驱动程序。很适合与

VJ++一起使用，可惜介绍这方面的书好象不是很多。


使用JavaBean高效处理JSP(1)

作者：Eazily

email: Eazily@yeah.net

日期：6/12/2001 2:48:14 PM

摘要：JavaServer Pages Model II的一个基本思想就是将表现（HTML）和处理逻辑分开。这篇文章介绍的是一个高效、可重用的设计方法，将动态的内容、处理和验证由JavaServer Page中分离出来，放到一个相应的JavaBean中。它使用Template Method的设计方法，可提高代码在整个Web应用中的重用性。此外，它还介绍了在一个HTTP Session中bean间如何通信的简单技巧。

    JavaServer Pages（JSP）技术提供了许多的特性，可让你简易和快速地开发Web应用。不过，如果你无计划和结构地使用这些技术，你的JSP代码将会是各种HTML标记、JSP标记和Java代码的混合，很难于看懂、调试和维护。

    这里的目标是将全部的JSP处理代码封装到JavaBean中。这种方法的好处是HTML编程者和美工可以做表现的开发（使用HTML编辑器），而Java编程者可以集中开发编程逻辑。此外，这种方法可便于你为同一个Web应用提供不同的外观。

    我将介绍的架构使用Template Method方法，可集中实现整个应用的共有设计部分和实现每个JSP的共有处理。就这个例子，共有的处理包括有页面状态管理、共有的页面处理、共有的错误处理，以及在页面间共享信息的技术。所有这些仅是定义一次，你可在页面设计时再处理特定的细节。

    我将以一个简单的"投票"应用为例，介绍如何使用这个架构。你应有基本的JSP和Java知识，并且最好懂得一些UML知识。

    总的静态结构

    这个部分将介绍该架构核心部分的概观，以及投票应用的例子。图一显示了该架构的一个UML框图：

**************图一*******************

（UML类框图）

    该架构的中心部分由两个共有的JSP包含文件和两个类组成，下面将会谈到。它们的作用是完成共有的处理。

      includeheader.jsp：这个JSP文件必须被静态包含在每个JSP文件的开头。

      includefooter.jsp：这个JSP文件必须被静态包含在每个JSP文件的末尾。

    AbstractJSPBean：这是一个抽象类，你应该将它作为所有JSP JavaBean类的一个超类使用。它是该架构的核心类。

      SharedSessionBean：在一个HTTP session中，为所有的JSP JavaBean对象提供关联。

    JSP网页只是用作表现的。每个JSP页面都应该使用一个相应的JavaBean来处理特定页面的逻辑。每个JSP页面都应该静态包含includeheader.jsp和includefooter.jsp。每个JavaBean都必须扩展AbstractJSPBean，该抽象类包含有模板方法，可完成共有的处理。

    这个投票的应用包含有以下的JSP和相应的JavaBean：

      login.jsp, LoginJSPBean:投票者认证和登录

      vote.jsp, VoteJSPBean: 执行投票

      confirmation.jsp, ConfirmationJSPBean: 显示确认和投票的结果

    我将不会详细讨论数据库和商业逻辑的部分（(Voter, Candidate和VoteDB)，不过它们对于这个例子是必需的。

    以上我们已经对整体有了一个概观，接着我们将讨论例子中的每个JSP页面。


使用JavaBean高效处理JSP(2)

作者：Eazily

email: Eazily@yeah.net

日期：6/12/2001 2:49:38 PM

    JSP例子

    每个页面都必须使用指定的结构，以符合整个架构。

    列表1。login.jsp

＜%@ page import = "lbm.jsputil.*" %＞

＜jsp:useBean id="_loginJSPBean" class="lbm.examples.LoginJSPBean"

scope="session"/＞

＜jsp:setProperty name="_loginJSPBean" property="*"/＞

＜% AbstractJSPBean _abstractJSPBean = _loginJSPBean; %＞

＜%@ include file="includeheader.jsp" %＞

＜html＞

＜head＞＜title＞Vote Login＜/title＞＜/head＞

＜body bgcolor="white"＞

＜font size=4＞

Please enter your Voter ID and Password

＜/font＞

＜font size="3" color="Red"＞

＜jsp:getProperty name="_loginJSPBean" property="errorMsg"/＞

＜/font＞

＜font size=3＞

＜form method=post＞

Voter ID ＜input type=text name=voterId value=＜jsp:getProperty

name="_loginJSPBean" property="voterId"/＞＞

Password ＜input type=password name=password value=＜jsp:getProperty

name="_loginJSPBean" property="password"/＞＞

＜input type=submit value="Login"＞

＜/form＞

＜/font＞

＜/body＞

＜/html＞

＜%@ include file="includefooter.jsp" %＞

    该JSP页面的架构如下：由几个JSP语句开始。接着的HTML代码将没有多少JSP指令、语句和脚本等。除了几个必要的指令，它们负责由bean中得到动态的内容。最后，页面使用了一个JSP include指令。

    我们讨论其中一些重要的JSP语句：

＜jsp:useBean id="_loginJSPBean" class="lbm.examples.LoginJSPBean" scope="session"/＞

＜jsp:setProperty name="_loginJSPBean" property="*"/＞

以上的代码在JSP和相应的bean间建立了一个连接。第二个语句显式传送全部的form字段（存储为HTTP request参数）到bean中匹配的属性中。代码中使用了bean的setter方法。

＜% AbstractJSPBean _abstractJSPBean = _loginJSPBean; %＞

＜%@ include file="includeheader.jsp" %＞

    第一个语句让includeheader.jsp可执行共有的处理。第二个语句将includeheader.jsp静态包含进来。要注意到loginJSPBean和_abstractJSPBean现在指向同样的对象，只是带有不同的接口。

    列表2：includeheader.jsp

＜%-- Set the SharedSessionBean --%＞

＜jsp:useBean id="_sharedSessionBean" class="lbm.jsputil.SharedSessionBean" scope="session"/＞

＜% _abstractJSPBean.setSharedSessionBean(_sharedSessionBean); %＞

＜%-- Set implicit Servlet objects --%＞

＜% _abstractJSPBean.setRequest(request); %＞

＜% _abstractJSPBean.setResponse(response); %＞

＜% _abstractJSPBean.setServlet(this); %＞

＜%-- Perform the processing associated with the JSP --%＞

＜% _abstractJSPBean.process(); %＞

＜%-- If getSkipPageOutput equals false, do not output the JSP page --%＞

＜% if (! _abstractJSPBean.getSkipPageOutput()) { %＞

    includeheader.jsp是模板的核心元素之一。所有的JSP页面都使用这个共有的元素。

    列表2的前两个语句令不同页面但在同一HTTP session中的JSP bean之间互相进行通信。基本上，每个JSP将有两个与它关联的JavaBean：一个指定的JSP JavaBean（例如，LoginJSPBean）和共有的SharedSessionBean。SharedSessionBean作为一个共有的元素使用，用来连接所有的页面；我将在后面继续讨论它。

    includeheader.jsp中接着的三个语句与固有的Servlet对象有关。

＜% _abstractJSPBean.setRequest(request); %＞

＜% _abstractJSPBean.setResponse(response); %＞

＜% _abstractJSPBean.setServlet(this); %＞

    JSP规范提供访问Java Servlet规范中的固有对象，例如页面处理中常用到的request、response和servlet对象。因此它们被传送到JSP bean。

    ＜% _abstractJSPBean.process(); %＞

    最后，通过上面的语句来触发相关JSP页面的处理。你看到我们调用的是抽象JSP bean上的方法，而不是实类LoginJSPBean上的。为什么？我将在以下的部分解释。

    运用Template Method设计方法

    AbstractJSPBean是Template Method设计的主体。每个实际的JSP JavaBean都必须继承这个类。

    列表 3. AbstractJSPBean.java

package lbm.jsputil;

import java.util.*;

import javax.servlet.http.*;

import javax.servlet.*;

public abstract class AbstractJSPBean {

/* constants used for _state */

public static final int NEW = 0;

public static final int FIRSTPASS = 1;

public static final int PROC = 2;

public static final int ERR = -1;

private int _state; // current state

private String _errorMsg; // current message that is being appended during validation

private boolean _skipPageOutput; // should the page output be skipped

private SharedSessionBean _sharedSessionBean; // used for associating the JSP Bean with the HTTP Session

/* standard Servlet objects that need to be setup for each JSP Bean */

protected HttpServletRequest _request;

protected HttpServletResponse _response;

protected Servlet _servlet;

public AbstractJSPBean () {

setState(NEW);

}

protected abstract void beanProcess() throws java.io.IOException;

protected abstract void beanFirstPassProcess() throws java.io.IOException;

protected abstract void beanFooterProcess() throws java.io.IOException;

protected abstract String getJSPCode();

public void process() throws java.io.IOException {

setSkipPageOutput(false); // by default do not skip page output. Specific bean process

// methods can override it.

if (getState() == NEW) {

setState(FIRSTPASS);

beanFirstPassProcess();

} else {

resetErrorMsg();

setState(PROC);

beanProcess();

}

// validation that all common fields have been properly set by the application

// this is actually checking that the code has been written properly

String l_err = "";

if (_sharedSessionBean == null) l_err = l_err + "; SharedSessionBean must be set";

if (_request == null) l_err = l_err + "; Request must be set";

if (_response == null) l_err = l_err + "; Response must be set";

if (_servlet == null) l_err = l_err + "; Servlet must be set";

if ( ! l_err.equals("")) throw new IllegalStateException(l_err);

}

public void footerProcess() throws java.io.IOException {

beanFooterProcess();

}

protected void addErrorMsg (String addErrorMsg) {

if (_errorMsg == null) _errorMsg = addErrorMsg;

else _errorMsg = _errorMsg + " ＜br＞\n" + addErrorMsg;

setState(ERR);

}

protected void resetErrorMsg () {

_errorMsg = null;

}

public String getErrorMsg () {

if (_errorMsg == null) return "";

else return _errorMsg;

}

protected void setState (int newState) {

_state = newState;

}

public int getState () {

return _state;

}

public void setSharedSessionBean (SharedSessionBean newSharedSessionBean) {

if (_sharedSessionBean == null) {

_sharedSessionBean = newSharedSessionBean;

_sharedSessionBean.putJSPBean(getJSPCode(), this);

} else {

if (_sharedSessionBean != newSharedSessionBean) {

throw new IllegalStateException("SharedSessionBean is not set properly. SharedSessionBean must be the same for all PageBeans within the session");

}

}

}

public SharedSessionBean getSharedSessionBean () {

return _sharedSessionBean;

}

public void setSkipPageOutput (boolean newSipPageOutput) {

_skipPageOutput = newSipPageOutput;

}

public boolean getSkipPageOutput () {

return _skipPageOutput;

}

protected void redirect (String redirectURL) throws java.io.IOException {

// skip the page output since we are redirecting

setSkipPageOutput(true);

_response.sendRedirect(redirectURL);

}

public void setRequest (HttpServletRequest newRequest) {

_request = newRequest;

}

public void setResponse (HttpServletResponse newResponse) {

_response = newResponse;

}

public void setServlet (Servlet newServlet) {

_servlet = newServlet;

}

}

AbstractJSPBean包含有以下的抽象方法：beanFirstPassProcess(), beanProcess(), and beanFooterProcess()。这些方法被称为primitive方法。你必须在实际的JSP JavaBean子类中实现它们。每个都在JSP处理的一个特定阶段中执行。

    beanFirstPassProcess()--在页面被首次调用时进行的处理，它发生在页面开始输出之前。它适合用来初始化动态的内容和验证对页面的访问。可参见VoteJSPBean中该方法的实现，该Bean中用它来验证页面的访问，并且进行应用的流程控制。

    beanProcess()--发生在第二和后来的页面调用期间的处理，在页面输出开始之前。你可以用它来作HTML form验证和数据库更新。在LoginJSPBean类中，该方法被用作HTML form处理，在VoteJSPBean类中，用来保存信息到数据库中。

    beanFooterProcess()--在页面输出完成后进行的处理。你可以使用它来令session无效。在ConfirmationJSPBean类中，当投票完成后，通过实现该方法令session无效，并且显示确认的页面。

    接着我们将看一下process()方法：

public void process() throws java.io.IOException {

setSkipPageOutput(false); // by default do not skip page output. Specific bean process

// methods can override it.

if (getState() == NEW) {

setState(FIRSTPASS);

beanFirstPassProcess();

} else {

resetErrorMsg();

setState(PROC);

beanProcess();

}

....

    process()首先检查JSP的状态；然后，根据状态，它调用相应的primitive方法。它还设置了JSP相应的状态。

    process()和footerProcess()方法被称为template方法。它们由JSP中真正调用（在includeheader.jsp和includefooter.jsp中）。实体的bean不应该覆盖它们。template方法包含有共有的框架算法。一个典型模板方法的框架算法执行一个共有的处理，并且调用primitive（抽象）方法（beanFirstPassProcess()、beanProcess()和beanFooterProcess()），这些方法的实现在每个实际的JSP JavaBean中都是不同的。框架算法也可以称为AbstractJSPBean中实现的实体方法。以上的规则是Template Method设计方法的基本点。

    这种方法的好处是：

    1、通过在模板方法中分解出共有的处理，你可以做到代码重用

    2、你可以对整个应用进行共有的设计和处理

除了处理逻辑外，AbstractJSPBean还包含有以下具体的方法来帮助子类（实际的JSP JavaBean）来实现它们的处理任务，你不应该覆盖这些实际的方法。

    1、与用户错误管理相关的方法（addErrorMsg(), resetErrorMsg(), and getErrorMsg()）

    2、与页面状态管理相关的方法（setState(), getState()）

    3、管理与SharedSessionBean关联的方法

    4、控制JSP页面的HTML部分是否输出的方法（setSkipPageOutput(), getSkipPageOutput()）

    5、重定向的方法

    6、访问Servlet对象的方法: request, response和servlet


使用JavaBean高效处理JSP(3)

作者：Eazily

email: Eazily@yeah.net

日期：6/12/2001 2:50:16 PM

form处理、动态内容和bean通信

    列表4展示了一个具体的JSP JavaBean--LoginJSPBean，用来实现特定的页面处理

    列表4。LoginJSPBean

package lbm.examples;

import lbm.jsputil.*;

import java.util.*;

public class LoginJSPBean extends AbstractJSPBean {

public static final String PAGE_CODE = "login";

private String _voterId;

private String _password;

private Voter _voter = null;

public LoginJSPBean() {

}

public void setVoterId (String newVoterId) {

_voterId = newVoterId;

}

public String getVoterId() {

if (_voterId == null) return "";

else return _voterId;

}

public void setPassword (String newPassword) {

_password = newPassword;

}

public String getPassword() {

if (_password == null) return "";

else return _password;

}

public Voter getVoter () {

return _voter;

}

protected void beanProcess () throws java.io.IOException {

if (_voterId == null || _voterId.equals("")) {

addErrorMsg("Voter must be entered");

}

if (_password == null || _password.equals("")) {

addErrorMsg("Password must be entered");

}

if (getState() != ERR) {

file://If all the fields are entered, try to login the voter

Voter voter = VoteDB.login(_voterId, _password);

if (voter == null) {

addErrorMsg("Unable to authenticate the Voter. Please try again.");

}

else {

_voter = voter;

if (_voter.getVotedForCandidate() != null) {

// if the voter has already voted, send the voter to the last page

redirect("confirmation.jsp");

}

else {

// go to the Vote page

redirect("vote.jsp");

}

}

}

}

protected void beanFirstPassProcess() throws java.io.IOException {

}

protected void beanFooterProcess() throws java.io.IOException {

}

protected String getJSPCode() {

return PAGE_CODE;

}

}

    观察LoginJSPBean类中的set和get方法。就象上面提及的，它们用作动态的匹配，并且用来在form字段（request参数）和bean属性间传送值。

    列表4中的beanProcess()方法，展示了form处理的一些基本点。这个方法发生在页面输出前，在第二和全部后来的页面调用期间执行。这意味着它将仅在用户按下登录按钮并且提交form后执行。

    你首先要验证voteId和password的输入，产生的错误将通过addErrorMsg方法记录下来。这个方法设置AbstractJSPBean类的errorMsg属性。该属性可被JSP用来显示用户的错误：

      ＜jsp:getProperty name="_loginJSPBean" property="errorMsg"/＞

    如果数据的输入成功通过，beanProcess()方法将会调用数据库来验证用户。最后，它通过调用AbstractJSPBean类中实现的redirect()方法，将请求重定向到相应的页面。

    以下我们将讨论VoteJSPBean类中的一些方法。它们将可以解释该架构的一些其它方面，例如JSP JavaBean之间的通信和应用的流程控制。

    列表5。VoteJSPBean类中的beanFirstPassProcess()

protected void beanFirstPassProcess() throws java.io.IOException {

// get the Voter from Login page

_voter = null;

LoginJSPBean loginJSPBean =

(LoginJSPBean) getSharedSessionBean().getJSPBean(LoginJSPBean.PAGE_CODE);

if (loginJSPBean != null) {

_voter = loginJSPBean.getVoter();

}

if (_voter == null) {

// voter is not logged in yet. Send it to Login page

setState(NEW);

redirect("login.jsp");

}

}

以上的方法使用了AbstractJSPBean类中_sharedSessionBean对象。SharedSessionBean类通过使用一个简单的方法，让所有的JSP JavaBean对象在一个HTTP session中进行通信。它保存有一个session内的全部JSP JavaBean中的一个Map。Map是Java Collections框架的一个接口，它是Java 1.2推出的。对熟悉Java 1.1的人来说，它与Hashtable非常类似。一个JSP JavaBean的主键是它的PAGE_CODE，它作为一个常数存储在每个JSP JavaBean类中。

    在这个例子中，beanFirstPassProcess()方法首先定位到LoginJSPBean对象。接着，它由LoginJSPBean对象中得到Voter对象，并且存储一个到它的引用，以便以后使用。如果Voter为null，这意味着用户没有首先登录就进入Voter页面，因此它重定向到登录页面。这是一个应用流程控制的简单例子。你可以设计更复杂的方法，例如使用一个智能的调度程序，不过这些讨论已经超出了本文的范围。

    列表6。VoteJSPBean类的getCandidateList()方法

public String getCandidateList () {

StringBuffer candidateList = new StringBuffer();

Candidate candidate;

Iterator candidates = VoteDB.getCandidates();

while (candidates.hasNext()) {

candidate = (Candidate) candidates.next();

candidateList.append("＜input type=radio name=\"candidateName\" value=\"");

candidateList.append(candidate.getName());

candidateList.append("\"＞ ");

candidateList.append(candidate.getName());

candidateList.append("＜br＞\n");

}

return candidateList.toString();

}

    以上的getCandidateList()方法被vote.jsp调用，通过以下的方法：

      ＜jsp:getProperty name="_voteJSPBean" property="candidateList"/＞

    根据由数据库得到的内容不同，该方法提供不同的动态HTML内容输出。它需要开发JavaBean的Java编程者懂得一些HTML知识。

    你也可以使用一个利用HTML的独立库来格式化HTML，它可以接受一个预定义的输入。例如Iterator，然后以预定义的格式产生HTML输出。另一个方法是使用标签库。


使用JavaBean高效处理JSP(4)

作者：Eazily

email: Eazily@yeah.net

日期：6/12/2001 2:51:13 PM

    最后的要点：框架

    通过将表现和逻辑分离开来，该架构可让你独立地修改表现（JSP）和逻辑（bean）。这意味着你可以修改bean中的逻辑而无需改动JSP，只要你保持bean的属性不变就行了。相反也是成立的，你可以将JSP代码交给HTML开发者和美工来改变站点的外观，而不会影响其中的Java代码。

    你可以稍微修改该框架的核心元素来满足你应用的特别需要。你可以加入新的或者修改现有的方法，或者修改模板的方法。重要的一点是，在你的整个应用中，全部的JSP和JSP JavaBean都应该使用这个架构。

    在刚开始时，这个架构看来比较复杂，特别是对于一个只有三页的例子应用来说。不过，如果你开始写你的应用，你将会发现当应用变复杂时，代码量的增长却没有预期的大。

    这个架构并没有谈到Web应用中通常用到的多层设计。它主要集中在JSP的表现层。要建立真正的三层或者多层的系统，JSP JavaBeamn将需要调用Enterprise JavaBean或者一些其它的商业逻辑实现。

    例子还介绍了让应用跟踪HTTP session的架构。不过，如果你不想依赖session的话，该架构仍然可以工作得很好。应用的页面和流程应该是分别设计的。你可能将不需要SharedSessionBean。你的页面只是用作表现，而独立的页面将仅用作处理和验证，而不会有任何的HTML输出。为此，你将主要使用beanFirstPassProcess() 方法。

    最后的要点：例子

    我使用Tomcat3.1来测试这个例子，Tomcat3.1符合JSP1.1和Servlet2.2规范。这篇文章将不会讨论如何在Tomcat中配置JSP应用的详细情况。

    在测试该例子时，你可以在VoteDB类的源代码中得到Voter ID，以便可以进行登录测试（密码和ID是一样的）。

    你必须在浏览器中允许cookies，否则不能成功运行例子。如果你需要在禁止cookies时仍然可以运行这个应用，你必须重新写URL（使用the javax.servlet.http.HttpServletResponse类的encodeURL()方法）。你需要重写全部应用中的URL，包括你的JSP中的链接，form标记中的action，以及在JSP JavaBean中用来重定向URL的HTTP请求。

    结论

    这篇文章介绍的架构为JSP的应用设计提供了一个全面的解决办法。它改进了代码重用，确定了应用的体系，并且便于扩展。一个最大的好处是将表现和逻辑分开，你可以独立改变它们，而不会影响另一方。


使用JAVA开发CORBA应用

作者：爬虫

email: xxx@xxx.xx

日期：2001-5-8 17:42:40

■CORBA技术简介

简单地说，CORBA允许应用之间相互通信，而不管它们存在于哪里以及是谁设计的。CORBA1.1于1991年由OMG发布，其中定义了接口定义语言（IDL）以及在对象请求代理（ORB）中实现客户对象与服务器对象之间交互的应用编程接口（API）。CORBA2.0于1994年发布，规定了各个供应商之间的ORB的通信规则。

CORBA标准主要分为三个部分：接口定义语言（IDL）、对象请求代理（ORB）以及ORB之间的互操作协议IIOP。

ORB是对象之间建立Client/Server关系的中间件。使用ORB，客户可以透明地调用一个服务对象上的方法，这个服务对象可以在本地，也可以在通过网络连接的其他机器上。ORB截获这一调用同时负责查找实现服务的对象并向其传递参数、调用方法返回最终结果。客户并不知道服务对象位于什么地方，它的编程语言和操作系统是什么，也不知道不属于对象接口的其他系统部分。这样，ORB在异构分布环境下为不同机器上的应用提供了互操作性，并无缝地集成了多种对象系统。

在开发传统的Client/Server应用时，开发者使用他们自己设计的或一个公认的标准来定义用于设备之间通信的协议。协议的定义依赖于实现语言、网络传输和许多其他因素，而ORB的出现简化了这一过程。使用ORB时，协议是使用接口定义语言（IDL）定义的，而IDL是独立于语言的。并且ORB提供很强的灵活性，它使程序员选择最适合的操作系统、执行环境，甚至系统各个组件也可以采用不同的编程语言实现。更重要的是，它允许现有组件的集成。在一个基于ORB的解决方案中，开发者可以使用与创建新对象一样的IDL对遗留系统进行建模，他们创建“包装”代码以在标准化的软件总线与遗留系统接口之间传递信息。

使用CORBA，用户可以透明地访问信息，并不需要知道信息存在于什么软件中、使用什么硬件平台，以及位于企业网络的什么地方。作为面向对象系统的通信核心，CORBA为今天的计算环境带来了真正的互操作性。

■CORBA与JAVA的相互关系

CORBA不只意味着对象请求代理（ORB），它还是非常全面的分布式对象平台。CORBA使JAVA应用可以跨越网络、语言以及操作系统，并为JAVA提供了一组分布服务，如分布式自我观察、动态发现、事务、关系、安全和命名等。

JAVA不仅是一种语言，它还是一个动态代码系统，它对运行对象来说是一个可移植的虚拟机（JVM）。JAVA为开发、管理、发布Client/Server应用提供了更简单的方式。人们可以通过将应用放在一个Web服务器上将这一应用发布给成千上万个用户，而不必关心它的安装和升级。JAVA还非常适合服务器的开发，它可以动态地将服务代码移向最需要它们的地方。

JAVA将会使CORBA对象能够运行在从主机、网络计算机到蜂窝电话等可编程的各种机器上，并简化了大型CORBA系统的代码发布。对客户和服务对象来说JAVA是很理想的编程语言，JAVA内置的多线程、垃圾收集和错误处理使编写健壮的网络对象变得很容易。

这两种对象模型可以很好地相互补充，CORBA处理网络的透明性，JAVA处理实现的透明性，CORBA为JAVA可移植应用环境提供了一个分布式的结构。

■使用JAVA开发CORBA应用

下面让我简要介绍一下开发CORBA的步骤。

使用JAVA开发CORBA应用需要如下五个步骤：

使用IDL创建接口 (About.idl)

    下面的OMG IDL描述一个CORBA对象。

    module About

    {

     interface Show

     {

     string ShowName();

     };

    };

    将其存为Show.idl。

编译接口并生成CORBA支持文件

    我们用以下命令编译这个 IDL 接口：

     idltojava Show.idl

    idltojava是SUN公司的IDL编译器，可以免费从SUN公司站点上下载。

    因为idltojava在编译IDL文件之前，需要进行预编译，而如果你的机器上没有预编译器，可以使用以下命令：

    idltojava -fno-cpp Show.idl

    编译后将在当前目录下生成About子目录，其中会包括一些支持文件，如有兴趣可以看一下，但一定不要修改。

实现服务器 (ShowServer.java)

    ShowServer的main() 方法，可完成以下任务：

创建一个 ORB 实例

创建一个服务对象实例（CORBA About 对象的实现）并通知 ORB

获取一个命名上下文的CORBA对象引用，在该命名上下文中注册新的CORBA对象

在命名上下文中将新对象注册在“About”名下

等待对新对象的调用

    实现服务器源程序如下：

    import About.;

    import org.omg.CosNaming.;

    import org.omg.CosNaming.NamingContextPackage.;

    import org.omg.CORBA.;

    class ShowObject extends _ShowImplBase

    {

     public String ShowName()

     {

     return "\nMy name is Seymour!!\n";

     }

    }

    public class ShowServer {

     public static void main(String args[])

     {

     try{

     // 创建和初始化 ORB

     ORB orb = ORB.init(args, null);

     // 创建服务对象并将其向 ORB 注册

     ShowObject ShowRef = new ShowObject();

     orb.connect(ShowRef);

     // 获取根命名上下文

     org.omg.CORBA.Object objRef = orb.resolve_initial_references("NameService");

     NamingContext ncRef = NamingContextHelper.narrow(objRef);

     // 绑定命名中的对象引用

     NameComponent nc = new NameComponent("About", "");

     NameComponent path[] = {nc};

     ncRef.rebind(path, ShowRef);

     // 等待来自客户机的调用

     java.lang.Object sync = new java.lang.Object();

     synchronized (sync) {

     sync.wait();

     }

     } catch (Exception e) {

     System.err.println("ERROR: " + e);

     e.printStackTrace(System.out);

     }

     }

    }

实现客户机 (ShowClient.java)

    下面的应用程序客户机将完成以下任务：

创建一个ORB；

获取一个指向命名上下文的引用；

在命名上下文中查找“Show”并获得指向该 CORBA 对象的引用；

调用对象的 ShowName() 操作并打印结果。

    import About.;

    import org.omg.CosNaming.;

    import org.omg.CORBA.;

    public class ShowClient

    {

     public static void main(String args[])

     {

     try{

     // 创建和初始化 ORB

     ORB orb = ORB.init(args, null);

     // 获取根命名上下文

    org.omg.CORBA.Object objRef = orb.resolve_initial_references("NameService");

    NamingContext ncRef = NamingContextHelper.narrow(objRef);

    //解析命名中的对象引用

    NameComponent nc = new NameComponent("About", "");

    NameComponent path[] = {nc};

    About.Show ShowRef = ShowHelper.narrow(ncRef.resolve(path));

    // 调用 Show 服务对象并打印结果

     String show = ShowRef.ShowName();

     System.out.println(show);

     } catch (Exception e) {

     System.out.println("ERROR : " + e) ;

     e.printStackTrace(System.out);

     }

     }

    }

构建和运行ShowName程序

编译 .java 文件，包括 stub 和 skeleton（在About目录中）：

javac .java About/.java

启动一个MS-DOS命令解释器，输入以下命令，确保名字服务器处于运行状态：

tnameserv -ORBInitialPort 1050

启动另一个MS-Dos命令解释器，输入以下命令，启动Show服务器：

java ShowServer -ORBInitialPort 1050

再启动一个MS-Dos命令解释器Show应用程序客户机：

java ShowClient -ORBInitialPort 1050

这时屏幕上会出现“My name is Seymour!”的字样，说明实验已经成功了。

    本示例程序在Windows 98、Java 1.2.2平台下实现。


使用JSP读取客户端信息

作者：jspfuns

日期：2000-8-10 17:16:28

作者/出处：Blueski编译

请阅读以下代码。如果你的使用要求不同，可对这些代码加以很方便的修改。这些代码可以使你获得：

公司company, 用户name，版本version，main version，minor version

操作系统(未完成!)，语言language，locale等。

建立一个新的JSP文件:

<%@ page language="java" import="de.hunsicker.http.util.*"%>

<%

Browser eins = new Browser(request, session);

out.println(eins.getVersion());

%>

请将下列class文件加入classpath (你要建立同样的目录结构-- de.hunsicker.http.util，当然也可以自己调节包的名称。!):

package de.hunsicker.http.util;

import java.util.*;

import javax.servlet.*;

import javax.servlet.http.*;

public class Browser extends HttpServlet

{

protected HttpServletRequest request;

protected HttpSession session;

protected String userAgent;

protected String company; // Firmenname des Herstellers

protected String name; // Bezeichnung des Browsers

protected String version; // Version

protected String mainVersion; // Hauptversion

protected String minorVersion; // Unterversion

protected String os; // Betriebssystem

protected String language = "de"; // Sprachcode Standard

protected Locale locale; // Locale-Objekt mit den aktuellen

// Spracheinstellungen

private Hashtable supportedLanguages; // Untersttzte Sprachen

public Browser(HttpServletRequest request, HttpSession session)

{

this.initialize();

this.request = request;

this.session = session;

this.setUserAgent(this.request.getHeader("User-Agent"));

this.setCompany();

this.setName();

this.setVersion();

this.setMainVersion();

this.setMinorVersion();

this.setOs();

this.setLanguage();

this.setLocale();

}

public void initialize()

{

this.supportedLanguages = new Hashtable(2);

this.supportedLanguages.put("en", "");

this.supportedLanguages.put("de", "");

}

public void setUserAgent(String httpUserAgent)

{

this.userAgent = httpUserAgent.toLowerCase();

}

private void setCompany()

{

if (this.userAgent.indexOf("msie") > -1)

{

this.company = "Microsoft";

}

else if (this.userAgent.indexOf("opera") > -1)

{

this.company = "Opera Software";

}

else if (this.userAgent.indexOf("mozilla") > -1)

{

this.company = "Netscape Communications";

}

else

{

this.company = "unknown";

}

}

/**

* Liefert den Firmennamen des Herstellers des verwendeten Browsers.

*/

public String getCompany()

{

return this.company;

}

private void setName()

{

if (this.company == "Microsoft")

{

this.name = "Microsoft Internet Explorer";

}

else if (this.company == "Netscape Communications")

{

this.name = "Netscape Navigator";

}

else if (this.company == "Operasoftware")

{

this.name = "Operasoftware Opera";

}

else

{

this.name = "unknown";

}

}

/**

* Liefert den Namen des verwendeten Browsers.

*/

public String getName()

{

return this.name;

}

private void setVersion()

{

int tmpPos;

String tmpString;

if (this.company == "Microsoft")

{

String str = this.userAgent.substring(this.userAgent.indexOf("msie") + 5);

this.version = str.substring(0, str.indexOf(";"));

}

else

{

tmpString = (this.userAgent.substring(tmpPos = (this.userAgent.indexOf("/")) + 1, tmpPos + this.userAgent.indexOf(" "))).trim();

this.version = tmpString.substring(0, tmpString.indexOf(" "));

}

}

/**

* Liefert die Versionsnummer des verwendeten Browsers.

*/

public String getVersion()

{

return this.version;

}

private void setMainVersion()

{

this.mainVersion = this.version.substring(0, this.version.indexOf("."));

}

/**

* Liefert die Hauptversionsnummer des verwendeten Browsers.

*/

public String getMainVersion()

{

return this.mainVersion;

}

private void setMinorVersion()

{

this.minorVersion = this.version.substring(this.version.indexOf(".") + 1).trim();

}

/**

* Liefert die Unterversionsnummer des verwendeten Browsers.

*/

public String getMinorVersion()

{

return this.minorVersion;

}

private void setOs()

{

if (this.userAgent.indexOf("win") > -1)

{

if (this.userAgent.indexOf("windows 95") > -1 || this.userAgent.indexOf("win95") > -1)

{

this.os = "Windows 95";

}

if (this.userAgent.indexOf("windows 98") > -1 || this.userAgent.indexOf("win98") > -1)

{

this.os = "Windows 98";

}

if (this.userAgent.indexOf("windows nt") > -1 || this.userAgent.indexOf("winnt") > -1)

{

this.os = "Windows NT";

}

if (this.userAgent.indexOf("win16") > -1 || this.userAgent.indexOf("windows 3.") > -1)

{

this.os = "Windows 3.x";

}

}

}

/**

* Liefert den Namen des Betriebssystems.

*/

public String getOs()

{

return this.os;

}

private void setLanguage()

{

String prefLanguage = this.request.getHeader("Accept-Language");

if (prefLanguage != null)

{

String language = null;

StringTokenizer st = new StringTokenizer(prefLanguage, ",");

int elements = st.countTokens();

for (int idx = 0; idx elements; idx++)

{

if (this.supportedLanguages.containsKey((language = st.nextToken())))

{

this.language = this.parseLocale(language);

}

}

}

}

/*

* Hilfsfunktion fr setLanguage().

*/

private String parseLocale(String language)

{

StringTokenizer st = new StringTokenizer(language, "-");

if (st.countTokens() == 2)

{

return st.nextToken();

}

else

{

return language;

}

}

/**

* Liefert das L?nderkürzel der vom Benutzer

* bevorzugten Sprache.

*/

public String getLanguage()

{

return this.language;

}

private void setLocale()

{

this.locale = new Locale(this.language, "");

}

/**

* Liefert ein Locale-Objekt mit der Sprach-Prferenz des verwendeten Browsers

*/

public Locale getLocale()

{

return this.locale;

}

}


使用servlet和jsp上载文件

作者：sager

email: sagerk@263.net

日期：2000-8-9 22:12:45

Use JSP and Servlet to Upload file to server is becoming a

common task now.

Below is a simple example use the jspsmart free component which

implements it.

You can download the free package from JSPSMART

1. Html File

<html>

<body>

<script LANGUAGE="javascript" SRC="JSFunction.js">

</script>

<script language="JavaScript">

<!--

function checkForm(){

if (document.uploadform.adsImage.value==""){

alert("You must choose what image file to upload!");

return false;

}

document.uploadform.submit();

}

function cancelUpload(){

window.close();

window.opener.focus();

}

//-->

</script>

<form name="uploadform" method="post"

action="uploadImage.jsp" ENCTYPE="multipart/form-data"

target=_self>

<center>

<table border="1" width="65%">

<tr>

<td colspan=2 nowrap align=center>广告图片上载</td>

</tr>

<tr>

<td width="15%" nowrap >广告图片:</td>

<td width="50%" nowrap><input type="file"

name="adsImage" size="20"></td>

</tr>

<tr>

<td align="center" colspan=2 >

<input type="button" value="upload" onclick="return

checkForm();" >

&nbsp;&nbsp;&nbsp;

<input type="button" value="Cancel" onclick="return

cancelUpload();">

</td>

</tr>

</form>

</table>

<!--

<a href="Javascript:window.close();">close this window</a>

-->

<script language="JavaScript">

<!--

this.focus();

//-->

</script>

</body>

2. JSP File

<%@ page language="java" import="com.jspsmart.upload.*,

java.sql.*,java.util.* "%>

<%-- use SmartUpload bean --%>

<jsp:useBean id="mySmartUpload" scope="page"

class="com.jspsmart.upload.SmartUpload" />

<%

//Initialization

mySmartUpload.init(config);

mySmartUpload.service(request,response);

//Set Restriction

mySmartUpload.setAllowedFilesList("gif,bmp,jpeg,jpg");

mySmartUpload.setTotalMaxFileSize(51200);

// Upload

try{

mySmartUpload.upload();

}

catch(Exception e){

out.println("<font color=red>Upload File Fail!</font>

<a href=

\"JavaScript:window.history.back();\">back</a><br>");

out.println("<UI>Notes:</UI>");

out.println("<UL>You must set correct File

Name.</UL>");

out.println("<UL>You file size must be less than

50K.</UL>");

out.println("<UL>You can only upload .Gif .jpeg .jpg and

.bmp files.</UL>");

return;

}

//save file to disk

mySmartUpload.getFiles().getFile(0).saveAs("/" +

"FileName");

%>

3. Servlet File (you can do it yourself)


试验win2k在apache1.3上配置tomcat3.1

作者：飞鸟

日期：2000-07-13 00:39:59

为什么要把tomcat配置成为Apache的in-process部件？

1. Tomcat 处理静态页面没有apache快

2. Tomcat不如apache容易配置

3. Tomcat没有apache稳定健壮

4. apache支持CGI 脚本和API，perl,php等等

下面让我来尝试配置一下

1。review一下我apache和tomcat的安装情况先

我的机器是windows 2000 server 中文版本，已经安装了jdk1.3(在d:\jdk1.3)

apache我安装在D:\Program Files\Apache Group\Apache

我设置apache服务器的ServerName是spf,我的机器名是spf

设置为端口81,80已经给iis5了

tomcat我安装在D:\tomcat

2。在着手配置之前，让我来看一下tomcat的文档。

D:\tomcat\doc\uguide\tomcat_ug.html

3。然后follow该文档开始配置，先下载ApacheModuleJServ.dll

http://jakarta.apache.org/builds/tomcat/release/v3.1/bin/win32/i386/ApacheModuleJServ.dll

放到D:\Program Files\Apache Group\Apache\modules目录下

4。然后编辑D:\Program Files\Apache Group\Apache\conf\httpd.conf

在末尾添加这句

include d:/tomcat/conf/tomcat-apache.conf

5。重新启动apache

命令行输入：

net stop "apache_svr"

net start "apache_svr"

（我把apache配置成win2k的服务了。）

6。然后在浏览器中输入http://localhost:81/examples/

得到一个404错误。。。

十分不解，就再次察看文档

D:\tomcat\doc\uguide\tomcat_ug.html

发现一句，说apache自动启动tomcat 还没有实现 :(((

 Automatically starting Tomcat is not implemented yet

气死我了。。。

运行 d:\tomcat\bin\startup.bat 启动tomcat

然后浏览器就可以看到http://localhost:81/examples/的目录列表了。

总结：

  apache不能自动启动tomcat 真是不爽，也许下一版本就可以了。不过如果是出于研究和学习JSP的目的，目前这一点不是十分重要。

  如果要建立新的虚拟目录，可以修改d:\tomcat\conf\server.xml,加上相关内容,然后重新启动tomcat,重新启动apache就可以了，而且我发现虚拟目录的物理路径必须放在d:\tomcat\webapps\目录下，真是很奇怪的事情。

附：

   上次我找linux资料的时候，当了一份apache的中文文档，已经上传到chinaasp.com/dowload栏目上了，详细地址是：[url]http://www.chinaasp.com/download/readItem.asp?id=342[/url]供大家参考


数据类型对软件性能的影响

作者：Eazily

email: Eazily@yeah.net

日期：6/12/2001 2:56:17 PM

BadRegExpMatcher要求MailBot将输入文本由字符数组转换为一个String对象，从而导致了不必要的对象创建。然而，滑稽的是为了更方便地使用输入文本，BadRegExpMatcher会立即将String对象转换为一个字符数组，这样不但会生成另一个对象，还意味着你在费了九牛二虎之力得到的结果，跟调用程序最初提供的数据没有什么二样，MailBot和BadRegExpMatcher都不要求使用String对象，String对象似乎仅仅是为也在组件之间交换文本数据而存在的。

    在上面的BadRegExpMatcher例子中，String仅仅是一种供交换用的类型，无论是调用程序还是被调用程序都不需要使用供交换用的数据类型来表示数据，但它们可以很方便地把它与其他的数据类型进行转换。尽管在定义接口时采用供交换用的数据类型可以在保持灵活性的前提下降低复杂性，但有时会带来性能上的损失。

    供交换用数据类型的一个最常见的例子是JDBC ResultSet接口，没有一种数据库的接口会用JDBC ResultSet表示返回的结果，但JDBC驱动程序可以很方便地把数据库返回的结果转换为JDBC ResultSet型数据。同样，也没有一种客户端软件用DBC ResultSet型数据表示数据记录，但你也可以不费什么劲儿就将ResultSet型数据转换为所需要的数据类型。在JDBC中我们可以接受这种"多此一举"，因为它具有标准性和跨数据库的可移植性等好处。无论如何，由供交换用数据类型所带来的性能损失都是一个值得注意的问题。

    在RegExpMatcher中，在定义接口时使用String对象对减少复杂性或增加可移植性的帮助并不大，对性能却有着相当大的影响，这么做是得不偿失的。在设计接口时使用可互换类型往往很有吸引力，因为这样能够使接口看起来更"干净"一些，但你应该确信你为此所作的折衷是明智的。有时━━就象RegExpMatcher那样，调用程序可以使用多种输入、输出格式，应该考虑如何能够更方便地满足它们的需求。

    值得指出的是，对使用可互换类型对性能的影响进行量化不是一件容易的事儿。如果对调用BadRegExpMatcher的代码进行分析，它将指派运行时间库创建输入用的String对象，这个String对象是为满足BadRegExpMatcher的需求而生成的。如果要衡量一个组件对性能的真正影响，不仅需要评估代码的资源利用情况，还需要评估建立调用的代码以及调用返回的代码的资源利用情况，要使用标准的分析工具完成这些分析工作是非常困难的。


台湾出的JSP电子版,内容很详细

作者：whitefox

email: whitefox.jiang@corp.elong.com

日期：00-7-4 20:02:21

http://web.wzptt.zj.cn/~flyinfo/JSP.pdf


提高Java代码可重用性的三个措施(1)

作者：五一

日期：2001-6-7 16:38:42

提高Java代码可重用性的三个措施(1)

松下客　　2001-05-29 09:35:18

    本文介绍了三种修改现有代码提高其可重用性的方法，它们分别是：改写类的实例方法，把参数类型改成接口，选择最简单的参数接口类型。

    措施一：改写类的实例方法

    通过类继承实现代码重用不是精确的代码重用技术，因此它并不是最理想的代码重用机制。换句话说，如果不继承整个类的所有方法和数据成员，我们无法重用该类里面的单个方法。继承总是带来一些多余的方法和数据成员，它们总是使得重用类里面某个方法的代码复杂化。另外，派生类对父类的依赖关系也使得代码进一步复杂化：对父类的改动可能影响子类；修改父类或者子类中的任意一个类时，我们很难记得哪一个方法被子类覆盖、哪一个方法没有被子类覆盖；最后，子类中的覆盖方法是否要调用父类中的对应方法有时并不显而易见。

    任何方法，只要它执行的是某个单一概念的任务，就其本身而言，它就应该是首选的可重用代码。为了重用这种代码，我们必须回归到面向过程的编程模式，把类的实例方法移出成为全局性的过程。为了提高这种过程的可重用性，过程代码应该象静态工具方法一样编写：它只能使用自己的输入参数，只能调用其他全局性的过程，不能使用任何非局部的变量。这种对外部依赖关系的限制简化了过程的应用，使得过程能够方便地用于任何地方。当然，由于这种组织方式总是使得代码具有更清晰的结构，即使是不考虑重用性的代码也同样能够从中获益。

    在Java中，方法不能脱离类而单独存在。为此，我们可以把相关的过程组织成为独立的类，并把这些过程定义为公用静态方法。

    例如，对于下面这个类：

    class Polygon {

    .

    .

    public int getPerimeter() {...}

    public boolean isConvex() {...}

    public boolean containsPoint(Point p) {...}

    .

    .

    }

    我们可以把它改写成：

    class Polygon {

    .

    .

    public int getPerimeter() {return pPolygon.computePerimeter(this);}

    public boolean isConvex() {return pPolygon.isConvex(this);}

    public boolean containsPoint(Point p) {return pPolygon.containsPoint(this, p);}

    .

    }

    其中，pPolygon是：

    class pPolygon {

    static public int computePerimeter(Polygon polygon) {...}

    static public boolean isConvex(Polygon polygon) {...}


提高Java代码可重用性的三个措施(2)

作者：五一

日期：2001-6-7 16:39:30

提高Java代码可重用性的三个措施(2)

松下客　　2001-05-29 09:35:18

    static public boolean

    containsPoint(Polygon polygon, Point p) {...}

    }

    从类的名字pPolygon可以看出，该类所封装的过程主要与Polygon类型的对象有关。名字前面的p表示该类的唯一目的是组织公用静态过程。在Java中，类的名字以小写字母开头是一种非标准的做法，但象pPloygon这样的类事实上并不提供普通Java类的功能。也就是说，它并不代表着一类对象，它只是Java语言组织代码的一种机制。

    在上面这个例子中，改动代码的最终效果是使得应用Polygon功能的客户代码不必再从Polygon继承。Polygon类的功能现在已经由pPolygon类以过程为单位提供。客户代码只使用自己需要的代码，无需关心Polygon类中自己不需要的功能。但它并不意味着在这种新式过程化编程中类的作用有所削弱。恰恰相反，在组织和封装对象数据成员的过程中，类起到了不可或缺的作用，而且正如本文接下来所介绍的，类通过多重接口实现多态性的能力本身也带来了卓越的代码重用支持。然而，由于用实例方法封装代码功能并不是首选的代码重用手段，所以通过类继承达到代码重用和多态性支持也不是最理想的。

    措施二：把参数类型改成接口

    正如Allen Holub在《Build User Interfaces for Object-Oriented Systems》中所指出的，在面向对象编程中，代码重用真正的要点在于通过接口参数类型利用多态性，而不是通过类继承：

    “……我们通过对接口而不是对类编程达到代码重用的目的。如果某个方法的所有参数都是对一些已知接口的引用，那么这个方法就能够操作这样一些对象：当我们编写方法的代码时，这些对象的类甚至还不存在。从技术上说，可重用的是方法，而不是传递给方法的对象。”

    在“措施一”得到的结果上应用Holub的看法，当某块代码能够编写为独立的全局过程时，只要把它所有类形式的参数改为接口形式，我们就可以进一步提高它的可重用能力。经过这个改动之后，过程的参数可以是实现了该接口的所有类的对象，而不仅仅是原来的类所创建的对象。由此，过程将能够对可能存在的大量的对象类型进行操作。

    例如，假设有这样一个全局静态方法：

    static public boolean contains(Rectangle rect, int x, int y) {...}

    这个方法用于检查指定的点是否包含在矩形里面。在这个例子中，rect参数的类型可以从Rectangle类改变为接口类型，如下所示：

    static public boolean contains(Rectangular rect, int x, int y) {...}


提高Java代码可重用性的三个措施(3)

作者：五一

日期：2001-6-7 16:40:08

提高Java代码可重用性的三个措施(3)

松下客　　2001-05-29 09:35:18

    而Rectangular接口的定义是：

    public interface Rectangular {Rectangle getBounds();}

    现在，所有可以描述为矩形的类（即，实现了Rectangular接口的类）所创建的对象都可以作为提供给pRectangular.contains()的rect参数。通过放宽参数类型的限制，我们使方法具有更好的可重用性。

    不过，对于上面这个例子，Rectangular接口的getBounds方法返回Rectangle，你可能会怀疑这么做是否真正值得。换言之，如果我们知道传入过程的对象会在被调用时返回一个Rectangle，为什么不直接传入Rectangle取代接口类型呢？之所以不这么做，最重要的原因与集合有关。让我们假设有这样一个方法：

    static public boolean areAnyOverlapping(Collection rects) {...}

    该方法用于检查给定集合中的任意矩形对象是否重叠。在这个方法的内部，当我们用循环依次访问集合中的各个对象时，如果我们不能把对象cast成为Rectangular之类的接口类型，又如何能够访问对象的矩形区域呢？唯一的选择是把对象cast成为它特有的类形式（我们知道它有一个方法可以返回矩形），它意味着方法必须事先知道它所操作的对象类型，从而使得方法的重用只限于那几种对象类型。而这正是前面这个措施力图先行避免的问题！

    措施三：选择最简单的参数接口类型

    在实施第二个措施时，应该选用哪一种接口类型来取代给定的类形式？答案是哪一个接口完全满足过程对参数的需求，同时又具有最少的多余代码和数据。描述参数对象要求的接口越简单，其他类实现该接口的机会就越大——由此，其对象能够作为参数使用的类也越多。从下面这个例子可以很容易地看出这一点：

    static public boolean areOverlapping(Window window1, Window window2) {...}

    这个方法用于检查两个窗口（假定是矩形窗口）是否重叠。如果这个方法只要求从参数获得两个窗口的矩形坐标，此时相应地简化这两个参数是一种更好的选择：

    static public boolean areOverlapping(Rectangular rect1, Rectangular rect2) {...}

    上面的代码假定Window类型实现了Rectangular接口。经过改动之后，对于任何矩形对象我们都可以重用该方法的功能。

    有些时候可能会出现描述参数需求的接口拥有太多方法的情况。此时，我们应该在全局名称空间中定义一个新的公共接口供其他面临同一问题的代码重用。

    当我们需要象使用C语言中的函数指针一样使用参数时，创建唯一的接口描述参数需求是最好的选择。例如，假设有下面这个过程：

    static public void sort(List list, SortComparison comp) {...}

    该方法运用参数中提供的比较对象comp，通过比较给定列表list中的对象排序list列表。sort对comp对象的唯一要求是要调用一个方法进行比较。因此，SortComparison应该是只带有一个方法的接口：

    public interface SortComparison {

    boolean comesBefore(Object a, Object b);

    }

    SortComparison接口的唯一目的在于为sort提供一个它所需功能的钩子，因此SortComparison接口不能在其他地方重用。

    总而言之，本文三个措施适合于改造现有的、按照面向对象惯例编写的代码。这三个措施与面向对象编程技术结合就得到了一种可在以后编写代码时使用的新式代码编写技术，它能够简化方法的复杂性和依赖关系，同时提高方法的可重用能力和内部凝聚力。

    当然，这里的三个措施不能用于那些天生就不适合重用的代码。不适合重用的代码通常出现在应用的表现层。例如，创建程序用户界面的代码，以及联结到输入事件的控制代码，都属于那种在程序和程序之间千差万别的代码，这种代码几乎不可能重用。


提高Java代码可重用性的三个措施

作者：Eazily

email: Eazily@yeah.net

日期：2001-7-14 21:37:38

提高Java代码可重用性的三个措施

本文介绍了三种修改现有代码提高其可重用性的方法，它们分别是：改写类的实例方法，把参数类型改成接口，选择最简单的参数接口类型。

措施一：改写类的实例方法

通过类继承实现代码重用不是精确的代码重用技术，因此它并不是最理想的代码重用机制。换句话说，如果不继承整个类的所有方法和数据成员，我们无法重用该类里面的单个方法。继承总是带来一些多余的方法和数据成员，它们总是使得重用类里面某个方法的代码复杂化。另外，派生类对父类的依赖关系也使得代码进一步复杂化：对父类的改动可能影响子类；修改父类或者子类中的任意一个类时，我们很难记得哪一个方法被子类覆盖、哪一个方法没有被子类覆盖；最后，子类中的覆盖方法是否要调用父类中的对应方法有时并不显而易见。

任何方法，只要它执行的是某个单一概念的任务，就其本身而言，它就应该是首选的可重用代码。为了重用这种代码，我们必须回归到面向过程的编程模式，把类的实例方法移出成为全局性的过程。为了提高这种过程的可重用性，过程代码应该象静态工具方法一样编写：它只能使用自己的输入参数，只能调用其他全局性的过程，不能使用任何非局部的变量。这种对外部依赖关系的限制简化了过程的应用，使得过程能够方便地用于任何地方。当然，由于这种组织方式总是使得代码具有更清晰的结构，即使是不考虑重用性的代码也同样能够从中获益。

在Java中，方法不能脱离类而单独存在。为此，我们可以把相关的过程组织成为独立的类，并把这些过程定义为公用静态方法。

例如，对于下面这个类：

class Polygon {

.

.

public int getPerimeter() {...}

public boolean isConvex() {...}

public boolean containsPoint(Point p) {...}

.

.

}

我们可以把它改写成：

class Polygon {

.

.

public int getPerimeter() {return pPolygon.computePerimeter(this);}

public boolean isConvex() {return pPolygon.isConvex(this);}

public boolean containsPoint(Point p) {return pPolygon.containsPoint(this,

p

);}

.

}

其中，pPolygon是：

class pPolygon {

static public int computePerimeter(Polygon polygon) {...}

static public boolean isConvex(Polygon polygon) {...}

static public boolean

containsPoint(Polygon polygon, Point p) {...}

}

从类的名字pPolygon可以看出，该类所封装的过程主要与Polygon类型的对象有关。名字前面的p表示该类的唯一目的是组织公用静态过程。在Java中，类的名字以小写字母开头是一种非标准的做法，但象pPloygon这样的类事实上并不提供普通Java类的功能。也就是说，它并不代表着一类对象，它只是Java语言组织代码的一种机制。

在上面这个例子中，改动代码的最终效果是使得应用Polygon功能的客户代码不必再从Polygon继承。Polygon类的功能现在已经由pPolygon类以过程为单位提供。客户代码只使用自己需要的代码，无需关心Polygon类中自己不需要的功能。但它并不意味着在这种新式过程化编程中类的作用有所削弱。恰恰相反，在组织和封装对象数据成员的过程中，类起到了不可或缺的作用，而且正如本文接下来所介绍的，类通过多重接口实现多态性的能力本身也带来了卓越的代码重用支持。然而，由于用实例方法封装代码功能并不是首选的代码重用手段，所以通过类继承达到代码重用和多态性支持也不是最理想的。

措施二：把参数类型改成接口

正如Allen Holub在《Build User Interfaces for Object-Oriented Systems》中所指出的，在面向对象编程中，代码重用真正的要点在于通过接口参数类型利用多态性，而不是通过类继承：

“……我们通过对接口而不是对类编程达到代码重用的目的。如果某个方法的所有参数都是对一些已知接口的引用，那么这个方法就能够操作这样一些对象：当我们编写方法的代码时，这些对象的类甚至还不存在。从技术上说，可重用的是方法，而不是传递给方法的对象。”

在“措施一”得到的结果上应用Holub的看法，当某块代码能够编写为独立的全局过程时，只要把它所有类形式的参数改为接口形式，我们就可以进一步提高它的可重用能力。经过这个改动之后，过程的参数可以是实现了该接口的所有类的对象，而不仅仅是原来的类所创建的对象。由此，过程将能够对可能存在的大量的对象类型进行操作。

例如，假设有这样一个全局静态方法：

static public boolean contains(Rectangle rect, int x, int y) {...}

这个方法用于检查指定的点是否包含在矩形里面。在这个例子中，rect参数的类型可以从Rectangle类改变为接口类型，如下所示：

static public boolean contains(Rectangular rect, int x, int y) {...}

而Rectangular接口的定义是：

public interface Rectangular {Rectangle getBounds();}

现在，所有可以描述为矩形的类（即，实现了Rectangular接口的类）所创建的对象都可以作为提供给pRectangular.contains()的rect参数。通过放宽参数类型的限制，我们使方法具有更好的可重用性。

不过，对于上面这个例子，Rectangular接口的getBounds方法返回Rectangle，你可能会怀疑这么做是否真正值得。换言之，如果我们知道传入过程的对象会在被调用时返回一个Rectangle，为什么不直接传入Rectangle取代接口类型呢？之所以不这么做，最重要的原因与集合有关。让我们假设有这样一个方法：

static public boolean areAnyOverlapping(Collection rects) {...}

该方法用于检查给定集合中的任意矩形对象是否重叠。在这个方法的内部，当我们用循环依次访问集合中的各个对象时，如果我们不能把对象cast成为Rectangular之类的接口类型，又如何能够访问对象的矩形区域呢？唯一的选择是把对象cast成为它特有的类形式（我们知道它有一个方法可以返回矩形），它意味着方法必须事先知道它所操作的对象类型，从而使得方法的重用只限于那几种对象类型。而这正是前面这个措施力图先行避免的问题！

措施三：选择最简单的参数接口类型

在实施第二个措施时，应该选用哪一种接口类型来取代给定的类形式？答案是哪一个接口完全满足过程对参数的需求，同时又具有最少的多余代码和数据。描述参数对象要求的接口越简单，其他类实现该接口的机会就越大——由此，其对象能够作为参数使用的类也越多。从下面这个例子可以很容易地看出这一点：

static public boolean areOverlapping(Window window1, Window window2) {...}

这个方法用于检查两个窗口（假定是矩形窗口）是否重叠。如果这个方法只要求从参数获得两个窗口的矩形坐标，此时相应地简化这两个参数是一种更好的选择：

static public boolean areOverlapping(Rectangular rect1, Rectangular rect2)

{

...}

上面的代码假定Window类型实现了Rectangular接口。经过改动之后，对于任何矩形对象我们都可以重用该方法的功能。

有些时候可能会出现描述参数需求的接口拥有太多方法的情况。此时，我们应该在全局名称空间中定义一个新的公共接口供其他面临同一问题的代码重用。

当我们需要象使用C语言中的函数指针一样使用参数时，创建唯一的接口描述参数需求是最好的选择。例如，假设有下面这个过程：

static public void sort(List list, SortComparison comp) {...}

该方法运用参数中提供的比较对象comp，通过比较给定列表list中的对象排序list列表。sort对comp对象的唯一要求是要调用一个方法进行比较。因此，SortComparison应该是只带有一个方法的接口：

public interface SortComparison {

boolean comesBefore(Object a, Object b);

}

SortComparison接口的唯一目的在于为sort提供一个它所需功能的钩子，因此SortComparison接口不能在其他地方重用。

总而言之，本文三个措施适合于改造现有的、按照面向对象惯例编写的代码。这三个措施与面向对象编程技术结合就得到了一种可在以后编写代码时使用的新式代码编写技术，它能够简化方法的复杂性和依赖关系，同时提高方法的可重用能力和内部凝聚力。

当然，这里的三个措施不能用于那些天生就不适合重用的代码。不适合重用的代码通常出现在应用的表现层。例如，创建程序用户界面的代码，以及联结到输入事件的控制代码，都属于那种在程序和程序之间千差万别的代码，这种代码几乎不可能重


通过socket访问数据库

作者：zergman

email: zergman@chinaasp.com

日期：6/12/2001 1:15:04 PM

发布者:flyfox

Tip:通过socket访问数据库,分 Clinet, Display,sqlServer三个类

Client.java

import java.awt.*;

import java.io.*;

import java.net.*;

import java.applet.*;

public class Client extends Applet

{

public TextArea chat_txt;

private TextField sql_txt;

private Button connect,execute;

private Socket soc= null;

private PrintStream ps= null;

Listen listen;

public void init()

{

chat_txt= new TextArea();

sql_txt= new TextField(20);

connect= new Button("Connect");

execute= new Button("Execute");

execute.disable();

Panel pp= new Panel();

pp.setLayout(new FlowLayout());

pp.add(sql_txt);

pp.add(connect);

pp.add(execute);

add("North",pp);

add("Center",chat_txt);

}

public boolean action(Event evt,Object obj)

{

if(evt.target instanceof Button)

{

String label= (String)obj;

if(label.equals("Connect"))

{

try{

soc= new Socket(InetAddress.getLocalHost(),4700);

ps= new PrintStream(soc.getOutputStream());

ps.println("HELLO");

ps.flush();

listen= new Listen(this,soc);

listen.start();

}catch(Exception e)

{

chat_txt.setText(e.toString());

disconnect();

}

connect.setLabel("Disconnect");

execute.enable();

}else if(label.equals("Disconnect"))

disconnect();

else if(label.equals("Execute"))

{

if(sql_txt.getText()!= null)

{

ps.println("FIND");

ps.flush();

ps.println(sql_txt.getText());

ps.flush();

}

}

}

return false;

}

public void disconnect()

{

if(soc!= null)

{

try{

listen.stop();

ps.println("QUIT");

ps.flush();

soc.close();

}catch(Exception e){}

finally{

listen.stop();

listen= null;

soc= null;

}

execute.disable();

connect.setLabel("Disconnect");

}

}

}

class Listen extends Thread

{

Socket socket= null;

DataInputStream dis= null;

Client parent= null;

public Listen(Client p,Socket s)

{

parent= p;

socket= s;

try{

dis= new DataInputStream(socket.getInputStream());

}catch(Exception e){}

}

public void run()

{

String line= null;

while(true)

{

try{

line= dis.readLine();

}catch(Exception e){}

if(line!= null)

parent.chat_txt.appendText(line);

}

}

}

Display.java

/********************************************

格式化输出数据库记录,出自<<使用java进行SQL数据库程序设计>>

返回值为格式化的字符串

********************************************/

import java.sql.*;

class Display extends Object

{

ResultSet theResultSet;

String theResult;

public void display()

{}

public void setResult(ResultSet result)

{

theResultSet= result;

}

public String getString()

throws SQLException,NoClassDefFoundError

{

theResult= "";

ResultSetMetaData metaData= theResultSet.getMetaData();

int colcount = metaData.getColumnCount();

int colSize[]= new int[colcount];

String colLabel[]= new String[colcount];

int colType[]= new int[colcount];

String colTName[]= new String[colcount];

int colPrec[]= new int[colcount];

int colScale[]= new int[colcount];

theResult +="\n";

for(int i= 1;i<= colcount;i++)

{

colSize[i-1] = metaData.getColumnDisplaySize(i);

colLabel[i-1]= metaData.getColumnLabel(i);

colType[i-1] = metaData.getColumnType(i);

colTName[i-1]= metaData.getColumnTypeName(i);

colPrec[i-1] = metaData.getPrecision(i);

colScale[i-1]= metaData.getScale(i);

if(colSize[i-1]<1+ colLabel[i-1].length())

colSize[i-1]= 1+colLabel[i-1].length();

theResult+= rightPad(colLabel[i-1],colSize[i-1]);

}

theResult +="\n\n";

int row= 0;

while(theResultSet.next())

{

row++;

for(int i=1;i<= colcount;i++)

{

String colvalue= theResultSet.getString(i);

if(colvalue== null)

colvalue="";

theResult+= rightPad(colvalue,colSize[i-1]);

}

theResult+="\n";

}

theResult+="\n(" +row+ "rows included)\n";

return theResult;

}

private String rightPad(String s,int len)

{

int curlen= s.length();

if(curlen>len)

return repString("*",len);

return (s+repString(" ",(len-curlen)));

}

private String repString(String s,int times)

{

String result="";

for(int i=0;i<times;i++)

result+= s;

return result;

}

}

sqlServer.java

import java.awt.*;

import java.sql.*;

import java.io.*;

import java.net.*;

import Display;

public class sqlServer

{

public static void main(String[] args)

{

System.out.println("Waiting for connection");

try{

ServerSocket session= new ServerSocket(4700);

handleRequests handler= null;

System.out.println("Waiting for connection");

while(true)

{

Socket socket= null;

socket= session.accept();

if(socket== null)

continue;

System.out.println("Connection made");

handler= new handleRequests(socket);

handler.start();

}

}catch(Exception e)

{

System.out.println("客户连接失败"+e);

}

}

}

class handleRequests extends Thread

{

private DataInputStream in= null;

private PrintStream out= null;

private Socket socket= null;

Connection theConnection= null;

Statement theStatement= null;

ResultSet theResultSet= null;

Display display= null;

public handleRequests(Socket s)

throws IOException

{

socket= s;

in= new DataInputStream(socket.getInputStream());

out= new PrintStream(socket.getOutputStream());

}

public void openConnection()

{

try{

Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");

if(theConnection!= null)

theConnection.close();

theConnection= DriverManager.getConnection("jdbc:odbc:test","admin","1234");

theStatement= theConnection.createStatement();

display= new Display();

}catch(Exception e)

{

System.out.println(e);

}

}

public void run()

{

openConnection();

try{

String line= null;

while(true)

{

line = in.readLine();

if(line!= null)

line= line.trim();

if(line.equals("FIND"))

{

line = in.readLine();

line= line.trim();

theResultSet= theStatement.executeQuery(line);

display.setResult(theResultSet);

out.print(display.getString());

out.flush();

}

if(line.equals("QUIT"))

break;

if(line.equals("HELLO"))

{

out.println("Welcome to join us");

out.flush();

}

}

in.close();

out.close();

socket.close();

}catch(Exception e)

{

System.out.println(e);

}

}

}


通用数据库操作BEAN

作者：zergman

email: zergman@chinaasp.com

日期：6/12/2001 1:25:34 PM

package pkgCDB;

import java.text.*;

import java.util.*;

import java.sql.*;

public class clsCDB

{

String sDBDriver = "";

    String sConnStr = "";

    String sHint = "";

    ResultSet　rs = null;

    Connection conn = null;

    Statement　stmt = null;

    public String getHint()

    {

return sHint;

    }

    public void setDBDriver(String s)

    {

        sDBDriver = "" + s;

    }

    public String getDBDriver()

    {

        return sDBDriver;

    }

    public String getConnStr()

    {

        return sConnStr;

    }

    public void setConnStr(String s)

    {

        sConnStr = "" + s;

    }

    public ResultSet getResultSet()

    {

        return rs;

    }

    //Initialize the database

    public boolean Initialization(String dbDrive,String dbConn)

    {

        sDBDriver = dbDrive;

        sConnStr = dbConn;

return Initialization();

    }

    public boolean Initialization()

    {

try

        {

            Class.forName(sDBDriver);

            sHint = "Initialization sucessfully";

            return true;

}

catch(java.lang.ClassNotFoundException e) {

sHint = "Initialization : " + e.getMessage();

            return false;

}

    }

//A common function perhaps usable

    public String chkStr(String inStr)

    {

        StringBuffer result = new StringBuffer("");

        char cc;

        if (inStr != null)

        {

         for (int i = 0; i < inStr.length(); i++)

         {

             cc = inStr.charAt(i);

             if (cc == '\'')

                {

                 result.append('\'');

             }

             result.append(cc);

         }

}

return result.toString();

    }

//execute a SQL statement with recordset returned

    public ResultSet executeQuery(String sql)

    {

        rs = null;

        try

        {

         conn = DriverManager.getConnection(sConnStr);

            stmt = conn.createStatement();

            rs = stmt.executeQuery(sql);

        }

        catch(SQLException ex)

        {

         sHint = "executeQuery: " + ex.getMessage();

        }

        return rs;

    }

//execute a SQL statement without returning recordset

    public boolean executeUpdate(String sql)

    {

        try

        {

         conn = DriverManager.getConnection(sConnStr);

            stmt = conn.createStatement();

            stmt.executeUpdate(sql);

            return true;

        }

        catch(SQLException ex)

        {

         sHint = "executeUpdate: " + ex.getMessage();

            return false;

        }

    }

//Close connections

    public boolean closeConn()

    {

   try

        {

   if (rs!=null)

             rs.close();

   if (stmt!=null)

             stmt.close();

     if (conn!=null)

             conn.close();

            return true;

     }

        catch(SQLException ex)

        {

sHint = "closeConn: " + ex.getMessage();

            return false;

        }

    }

}

测试用JSP文件

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

<%@ page import = "pkgCDB.clsCDB"%>

<jsp:useBean id="CommonDBBean" class="pkgCDB.clsCDB" scope="session"/>

<html>

<head><title>Common Database Bean Test</title></head>

<body>

<font size=4>

<%

if ( (request.getParameter("DBDriver") == null)

     // (request.getParameter("ConnStr") == null)

        // (request.getParameter("SQLStr") == null) )

    { %>

   <form method=get>

   Input DBDriver: <input type=text name=DBDriver value="sun.jdbc.odbc.JdbcOdbcDriver">

        <BR>

   Input Connection String: <input type=text name=ConnStr value="jdbc:odbc:testBean">

   Input SQL Query: <input type=text name=SQLStr value="select * from jobs">

        <BR>

   <input type=submit value="Submit">

   </form>

<%　}

else

    { %>

<% if (!CommonDBBean.Initialization(request.getParameter("DBDriver"),request.getParameter("ConnStr")))

     { %>

     <%=CommonDBBean.getHint() + "<p></p>" %>

<% }

else

     { %>

     <% ResultSet rs; %>

         <% rs = CommonDBBean.executeQuery(request.getParameter("SQLStr")); %>

         <%int j = rs.getMetaData().getColumnCount();　%>

         <table BORDER = "1">

<tr>

         <%for (int i = 1; i <= j; ++i)

         { %>

         <td>

             <%=rs.getMetaData().getColumnName(i) %>

             </td>

<% } %>

</tr>

         <%while(rs.next())

         { %>

         <tr>

         <%for (int i = 1;i <= j; ++i)

             { %>

<td>

                 <%=rs.getString(i)%>

                 </td>

<% } %>

</tr>

<% } %>

</table>

<% }

}

    if (!CommonDBBean.closeConn())

    { %>

     <%=CommonDBBean.getHint() + "<p></p>" %>

<% } %>

</font>

</body>

</html>


推出weblogic+oracle 分页

作者：javaboy

日期：2001-4-10 15:31:31

import java.sql.*;

import java.util.*;

import weblogic.db.jdbc.*;

public class bandlistBean {

  private String driver;

  private String dbURL;

  private Bean2 b=new Bean2();

public bandlistBean(){

   globalBean globalstr = new globalBean();

   driver = globalstr.getDBDriver();

   dbURL = globalstr.getDBURL();

}

public String getKey(String href,int recordPerPage,int startRecord) {

  Connection conn = null;

  QueryDataSet dSet = null;

  String shortkeystr ="";

  try{

   Class.forName(driver).newInstance();

   conn=DriverManager.getConnection(dbURL);

   String countsql = "Select count(*) as count from TABLE where yijlmbm=1 and riq=to_char(sysdate,'yyyymmdd')";

   dSet = new QueryDataSet(conn,countsql);

   dSet.fetchRecords();

   Record countRecord = dSet.getRecord(0);

   int dSetAllSize = countRecord.getValue("count").asInt();

   int pages = dSetAllSize/recordPerPage;

   if(dSetAllSize%recordPerPage !=0) pages++;

   for(int i=0;i<pages;i++) {

   if(startRecord == i*recordPerPage) {

    shortkeystr = shortkeystr + "<Font size=+1>";   //    被选中页数字字体增大

    shortkeystr = shortkeystr + href + "?startRecord=" + Integer.toString(i*recordPerPage) + ">";

    shortkeystr = shortkeystr + Integer.toString(i+1) + "</a></Font>" + "\n";

     }else {

   shortkeystr = shortkeystr + href + "?startRecord=" + Integer.toString(i*recordPerPage) + ">";

   shortkeystr = shortkeystr + Integer.toString(i+1) + "</a>" + "\n";

   }

   }

  }catch(Exception e) { System.out.println("Exception:"+e);}

    finally{

    if (dSet!= null) try {dSet.close();}catch(Exception e){}

    if (conn!= null) try {conn.close();}catch(Exception e){}

    }

    return shortkeystr;

    }

public String getYjlmList(String href1,String href2,String href3,int recordPerPage,int startRecord) {

     String newsstr = "";

     Connection conn = null;

     QueryDataSet dSet = null;

     String zy="";

      try {

      Class.forName(driver).newInstance();

      conn=DriverManager.getConnection(dbURL);

      String sqlstr = "Select xinxibm,biaot,weight from xinx where yijlmbm=1 and riq=to_char(sysdate,'yyyymmdd') order by xinxibm desc";

      dSet = new QueryDataSet(conn,sqlstr);

       dSet.fetchRecords(startRecord,recordPerPage);

      int dSetSize = dSet.size();

      for(int x=0;x<dSetSize;x++){

      Record curRecord = dSet.getRecord(x);

      String xinxibm = new Integer(curRecord.getValue("xinxibm").asInt()).toString().trim();

      String biaot = b.x2u(curRecord.getValue("biaot").asString().trim());

      String weight = new Integer(curRecord.getValue("weight").asInt()).toString().trim();

      if(weight.compareTo("1")==0){

          zy="重要新闻";

          }else{

        zy="普通新闻";

        }

     newsstr = newsstr + "<tr><td bgcolor='#E1DFD2'>" + href1 + "?Item=";

     newsstr = newsstr + xinxibm + ">" ;

     newsstr = newsstr + biaot + "</a></td>";

     newsstr = newsstr + "<td bgcolor='#E1DFD2'>" + href2 + "?Item1=";

     newsstr = newsstr + xinxibm +">普通新闻</a>";

     newsstr = newsstr + "&nbsp;&nbsp;" + href3 + "?Item1=";

     newsstr = newsstr + xinxibm + ">头条新闻</a></td>";

     newsstr = newsstr + "<td bgcolor='#E1DFD2' >" + zy + "</td></tr>";

      }

      dSet.close();

      conn.close();

      }catch(Exception e) {}

      finally{

      if (dSet!= null) try {dSet.close();}catch(Exception e){}

      if (conn!= null) try {conn.close();}catch(Exception e){}

      }

      return newsstr;

   }

}


推荐几个好资源

作者：jeru

email: jeru@163.net

日期：6/25/2001 3:22:01 PM

1. java.sun.com

为什么要推荐这个站点呢,大家都知道,似乎我说的是废话.....

其实知道的人多,重视的不多,说句不为过的话,你所遇到的

和你没遇到的问题,99%都可以在这里找到答案,这个java老家的

文档是一流的,也是最完善的.

2. http://www.javaworld.com

这里的java tips相当不错,如果你英文不好,可以去

http://www.cn.ibm.com/developerworks/

3. http://www.jspin.com

非常适合学jsp的人去看,分类做的很好.

4. http://www.jguru.com

最全的java faq,你有什么问题,先来这里看看吧

5. http://www.yahoo.com

善于搜索,这是一个优秀程序员所需具备的基本素质

6. 你所使用的产品的相关文档.很多东西其实早就有答案了.

7. 这里的旧贴和精华区,你的问题可能早就有很多人都问过了呢.

希望大家多留心我所推荐的这些资源,问问题之前先自己想想解决的办法,

这样可以节约大家的时间,也对自己的进步有利,谢谢.


往JRE里增加字体

作者：kevin佳

email: cgjmail@163.net

日期：7/27/2001 4:37:25 PM

                      Adding Fonts to the Java Runtime

要加一种亚洲字体进JRE，请按以下步骤进行：

1.装入字体

首先，你必须装载中文、日文、韩文或传统的中文字体（楷书、宋体等）到你的系统里面

2.复制字体进font.properties

装好了字体后，复制你感兴趣字体的descriptio到font.properties,最简捷的方法是把

font.properties.<locale>直接改名为font.properties

例如：要使用中文字体，你可以复制或者改名font.properties.zh为font.properties.

现行的WIN32 JDK提供如下font properties 文件

./lib/font.properties

./lib/font.properties.ja

./lib/font.properties.ko

./lib/font.properties.zh

./lib/font.properties.zh_TW

并且你可以在JRE里使用多于一种的亚洲字体，所必须做的就是编辑font.properties

文件。

例如你要加三种SERIF字体；

serif.0=Times New Roman,ANSI_CHARSET

serif.1=WingDings,SYMBOL_CHARSET,NEED_CONVERTED

serif.2=Symbol,SYMBOL_CHARSET,NEED_CONVERTED

<virtual font name>.<index number> = <platform font name>, attributes

后面有三个参数，如Times New Roman、WingDings就是字体名

第二个参数(SYMBOL_CHARSET)是相应字符集的处理接口

第三个参数是表明相应字符集不能直接同过UNICODE编码处理，需要与UNICODE间的转换器

通过以下语句设不同FONTS的转换器

fontcharset.serif.1=sun.awt.windows.CharToByteWingDings

fontcharset.serif.2=sun.awt.CharToByteSymbol

用户也可以定制自己的字体、字符集与自定义的UNICODE转换器

例如说你想增加你自己定制的字体到JAVA的serif    系列字体里去，

假设你的字体共有256个，从0到0Xff.正对应于UNICODE里的OXE000-0XEOFF。

首先你要设计你的转换类

package mypkg.converter;

import sun.io.CharToByteISO8859_1;

import sun.io.CharToByteConverter;

import sun.io.ConversionBufferFullException;

public class CharToByteMyFont extends sun.io.CharToByteISO8859_1 {

   /*

    * This method indicates the range this font covers.

    */

   public boolean canConvert(char ch) {

      if (ch >= 0xe000 && ch <= 0xe0ff) {

         return true;

      }

      return false;

   }

   /*

    * This method converts the unicode to this font index.

    */

   public int convert(char[] input, int inStart, int inEnd,

                      byte[] output, int outStart, int outEnd)

                      throws ConversionBufferFullException {

      int outIndex = outStart;

      for (int i = inStart; i < inEnd; i++) {

         char ch = input[i];

         if (ch >= 0xe000 && ch <= 0xe0ff) {

            if (outIndex >= outEnd)

            throw new ConversionBufferFullException();

            output[outIndex++] = (byte)(ch - 0xe000);

         }

      }

      return outIndex - outStart;

   }

第二步，加你自己的字体和转换器进Properties文件

你先要增加你的字体的名子进文件里，

如

serif.3=<your own font name>

然后定义相应的converter

fontcharset.serif.3=mypkg.converter.CharToByteMyfont

为了确定JRE能够找到你的转换器，你的application classpath 必须包含转换器的路径

以上是我看了sun里的相关文章后觉得很不错而粗略整理出来的，目的只在于把相关内容大概介绍一下，请不要见笑，最好参考英文原文

http://java.sun.com/products/jdk/1.1/docs/guide/intl/fontprop.html

或到http://java.sun.com/products/jdk/1.1/docs/guide/intl/index.html

本人正在做多国语的网版词典，所以有在找相关资料，这是我看到的比较有用的叙述JAVA中字符集处理的文章，如果各位看到更好的java对unicode支持的资料请告知我，谢谢


为什么jsp不能取代servlet

作者：zergman

email: zergman@chinaasp.com

日期：6/12/2001 12:58:04 PM

为什么jsp不能取代servlet

Blueski编译

JSP动态Web内容可能是动态Web内容的一项伟大的技术，并可将内容和表示presentation相分离，有些人仍然觉得奇怪，为什么servlets仍然有必要和JSP同时实施。

其实servlets的实用性并不是一个问题。它们在服务器端处理方面的表现非常优秀，而且，由于它们引人注目的安装方式而继续存在着。事实上，你可以将JSP看作一种servlets的高级抽象，它以Servlet 2.1 API扩展的形式来实现。

你仍然不应该不加选择地使用servlets；它们可能并不适用于每个人。例如，当一个页面设计者可以很简单地使用方便的HTML或XML工具来编写JSP页面时，servlets可能更适合那些在后端的开发者，因为他们经常使用某种IDE来编写-- 这是一种通常需要较高编程经验的处理工作。

当发布servlets时，甚至开发者都不得不小心确保在presentation和内容之间不存在紧密结合。你通常可以通过增加一个第三方发行包如htmlKona来做到这一点。但即使可以做到这一点，虽然可以通过简单的屏幕上的调整提供一些灵活性，仍无法为你保证在presentation自身格式上的变化。例如，如果你的presentation从HTML改变到DHTML，你将仍然需要确保发行包和新的格式相一致，在最坏的case -- scenario中，如果一个发行包无法得到，你将结束在动态web的内容中对presentation使用手写代码。


我的面向对象程序观 (转)

作者：大哥

email: programmers@sina.com

日期：8/7/2001 5:30:26 PM

/**

* title:        我的面向对象程序观

* description:  描述我对面向对象的一些理解，思想上并不成熟，文章随时都在更新，希望大家都来参与讨论

* copyright:    copyright (c) 2001

* company:      matrixfox

* @author 莫特探员

* @version 本文章还在继续更新中。

*/

我的面向对象程序观

1)面向对象概念的一些误解

“面向对象”是一个如今被人叫烂的词汇，就像去年人们都喜欢把自己的公司打上“.com”的标记一样。其实有多少人能真正理解这个词汇呢，很难说。我喜欢这样来比喻人们对“对象”一词的滥用。“对象”就好比人们经常说的“酷”和“爽”，很多人并不仔细考虑这两个词的差别，在很多情况下他们是通用的，“酷”和“爽”通常表达“心情不错”的意思，你可以在你玩的高兴的时候，大声的叫嚷“太酷了，太爽了”，这个时候两个词汇是通用的。但是你可以说“这个人很酷啊”，但是你不能说“这个人很爽啊”。人们对“对象”这个词汇的滥用就发生在这里，“面向对象”和“基于对象”就好比“酷”和“爽”,这是两个不同的概念，但是人们通常将这两个词汇混为一谈，一律用“面向对象”来表达。常见的错误可以在此列举一些：

1）有个人兴高采烈的和你说“我不喜欢 flash 4 的脚本语言，flash 5 版本的 action script 采用了新的面向对象的“.”语法，写起来很舒服。我现在一直用 flash 5 来做东西。”（同样的话语也发生在 director 的 lingo 脚本语言中）

2）visual basic 采用了面向对象的属性和方法，比起过去的 basic 语言有了很大的提高。

3）javascript 是面向对象的。

等等。

通常听到类似的话，你都要认真审视说话之人，分析他说的话。可以肯定一点，他并非真正懂得什么是面向对象的思想。很多人没有区分“面向对象”和“基于对象”两个不同的概念。面向对象的三大特点（封装，继承，多态）却一不可，通常“基于对象”使用对象，但是无法利用现有的对象模板产生新的对象类型，继而产生新的对象，也就是说“基于对象”没有继承的特点，而“多态”是表示为父类类型的子类对象实例，没有了继承的概念也就无从谈论“多态”。现在的很多流行技术都是基于对象的，它们使用一些封装好的对象，调用对象的方法，设置对象的属性。但是它们无法让程序员派生新对象类型。他们只能使用现有对象的方法和属性。所以当你判断一个新的技术是否是面向对象的时候，通常可以使用后两个特性来加以判断。“面向对象”和“基于对象”都实现了“封装”的概念，但是面向对象实现了“继承和多态”，而“基于对象”没有实现这些，的确很饶口。

2）java 比 c++  在贯彻面向对象的思想方面更加彻底。

我最近上的学习班的老师对我说：“c++ 是打着面向对象的幌子，干着过程编程的勾当”，这句话我非常的赞同，而且我一直以来也是这么认为的。但是仔细听他讲解后，我才发现，我是只是理解了这句话前两层的意思。但是还有一层意思我没有理解。你可能要问，“难道 c++ 不是面向对象的吗？”。事实上 c++ 是真正的面向对象编程语言。但是它也是过程编程语言。为什么怎么说呢， c++ 的产生不但考虑了面向对象的特性，而且也更多的考虑了对 c 语言的向后兼容，使得 c++ 这种杂合语言表现出“过程”和“对象”编程的双重性。你通常既可以继续用 c++ 编译器来编写传统的 c 程序，也可以使用 c ++ 的类库或者编写自己的类来作面向对象编程。这种“两面性”使得人们可以继续保留原有的 c 代码，同时也阻碍了面向对象思想的推广。

举个简单的例子，94 年的时候，我开始学习 c++ ，当时是学习 turbo c++ 自己带的一个叫作 turbo vision 的类库来做项目。我的同学也用 turbo c++ ，但是他一点也没有学习过 c++ 这个语言，他只是用 turbo c++ 编译器来重新编译他以前用 turbo c 写的程序，然后他自豪的对我说：“瞧，我用 c++ 做的东西”，好像意思是说“我用 c++ 开发项目了”，在那个 c 比 pascal 高档， pascal 比 foxbase 高档的年代里， c++ 的标签绝对是个很"酷"的标志。我其实很清楚他的行为。这就是“c++ 是打着面向对象的幌子，干着过程编程的勾当”的第一重意思，也就是说， c++ 编译器对 c 程序的兼容性造成了最底层的“过程勾当”。在国内有很长一段时间，人们都是在用 c++ 编译器做 c 编程。 我当时在想，比起我那个同学，我才是真正懂得面向对象的人。 我学习了 c++ 语言，我懂得封装，继承和多态，我学习了 turbo vision 的类库，我派生了 turbo vision 的类库并编写了自己的类，所以我是懂得面向对象的。从某种意义上说，我这么想是对的。但是从面向对象程序员的分类来说，我又不完全懂得面向对象的思想。从事面向对象编程的人按照分工来说，可以分为“类库的创建者”和“类库的使用者”，通常创建类库的人才是真正懂得面向对象思想的人，他们创建类库供给那些“客户程序员”来使用，他们从头开始制作类库，他们进行面向对象的分析，设计，实现的全过程。当学习完 c++ 后，我的第一个感觉是，从头创建一个类库真是很麻烦的事情，通常用 c 过程编程很容易实现的功能，你必须按照类的思想来从新建立对象，但是一旦类库建立好后，使用类库和派生类，以及维护代码都是非常舒服的事情。使用类库的人并不都是具备了面向对象思想的人，通常知道如何继承和派生新对象就可以使用类库了，然而我们的思维并没有真正的转过来，使用类库只是在形式上是面向对象，而实质上只是库函数的一种扩展。这就是我理解的“c++ 是打着面向对象的幌子，干着过程编程的勾当”的第二重意思，实际上用 c++ 编程的人，大部分不自己创建类，而是使用类库，这样就造成了他们把类库作为一种高级的库函数库来理解，并没有真正理解面向对象的思想。

c++ 的面向对象的思想推广很慢，直到 mfc ,owl ，vcl 这样的类库出来以后，人们才渐渐的接受了一些面向对象的思想。为什么这条面向对象的道路那么难走？我的答案是“因为 c++ 给了我们第二条道路去走过程编程”，当然原因是为了考虑兼容 c 语言，然而正是由于有了第二条老路才使得使得我们不会再去考虑新的面向对象编程思维方式。

直到出现了 java ，才真正迎来了面向对象的曙光。java 真正是革命性的东西吗？不是，所有现有的 java 的思想都是继承自其他的语言和技术，没有什么革命的地方，虚拟机的概念早在 20 年前的 ucsd pascal 中就采用了，只是当时的机器速度太慢，虚拟机仿真造成的性能降低对于当时的硬件来说是相当严重的。java 本身的很多东西也借鉴了 c++ 语言，连它的创始人也说，java 是 "c++--" 也就是说 java 去除了 c++ 的一些不太好的地方。所以说 java 本质上没有什么革命的东西，所以那些对 java 的赞美之词都是 sun 公司的宣传伎俩。没有一种语言会长久的存在下去，你很难说你的孩子在二十年后还会继续使用 c++ 或 java，所以我们要抛开这些浮华词汇的背后，找寻真正我们需要学习的东西。否则今天我们大家都是微软的傀儡，明天 sun 公司起来了，我们就都是 sun 的傀儡。仔细研究美国电脑技术发展的历史，美国人一向喜欢象第三世界兜售他们的过时技术，而他们始终可以自豪的说，我们将永远领先你们第三世界国家二十年。我们始终在跟在美国人的后面学习他们的东西，这的确让人担忧。我说着说着又跑题了。java 虽然没有什么真正的革命性的东西，但是 java 在真正推动面向对象编程思想方面是功不可末的。使用 java 编程，你无需考虑到向后兼容什么语言的问题，它是重新建立的语言，你在掌握这门语言之前，你必须将自己的编程思想由过程编程彻底转向面向对象编程，因为 每个 java 程序，本身就是一个类，你编写任何 java 程序，你都不自觉的在构造一个对象模板，没有第二条道路，只能是面向对象编程。（ 我非常喜欢 java 将所有类定义和类声明放在一个文件中，而不是象 c++ 那样，cpp 和 .h 文件是分开的。通常刚刚开始学习 java 的人不习惯，不过学习一段时间，你就会体会到它的好处了。）， 使用 java 编写程序（我更喜欢说---编写类，而不是程序）的时候，你会必须从一个对象的角度来考虑问题，因为你的程序就是一个类，你必须考虑把哪些东西作为成员变量，哪些作为方法，要生成几个类，哪些变量是成员变量，哪些是静态的变量和方法，等等。通常做完一个项目，你就已经将面向对象的思想运用其中了。之所以说 java 在面向对象的贯彻方面要比 c++ 彻底，就是因为你只能使用面向对象的方式来编写 java 程序，而 c++ 却可以采用另外一条非面向对象的方式来编写程序。java 并没有什么真正革命性的东西，它最大的贡献当然是推广面向对象的思想了。

java 总的来说是降低了继续过程编程的可能性，但是并没有完全消除它。面向对象是一种思想，是我们考虑事情的方法，通常表现为为我们是将问题的解决按照过程方式来解决呢，还是将问题抽象为一个对象来解决它。很多情况下，我们会不知不觉的按照过程方式来解决它，因为我们通常习惯于考虑解决问题的方法，而不是考虑将要解决问题抽象为对象去解决它。很多新的技术使我们更加趋向于过程而非对象的思想。最明显的就是 rad (快速应用程序开发）可视技术的出现，人们可以通过设置属性和编写事件函数来快速开发应用程序，编写事件函数就是典型的按照过程编程的思想（至少我是这么认为的），试问有多少人能区分vb 和 delphi ,c++ builder ，java 的事件函数编写有什么本质的区别， 后三者都采用了 delegation （委托模型），在 java 中是通过 anonymous 类（无名类），或者 adapter 类来实现delegation，这是一种面向对象的构想，但是 vb 不是，所以这底层的区别都被上层的相似性所抹杀了，使用 jbuilder 编程的时候，我们如果没有仔细研究 java 的 awt.event 编程方式的话，通常也是把它当作普普通通的“类 vb"的编写方式来写程序，而实际分析它在后台所生成的代码，你才能明白这些代码是真正的面向对象的，但是如果你只是简单的把它当作可视编程工具，你是永远不会明白什么是”委托模型“，什么是面向对象编程的。这是我理解的 “c++ 是打着面向对象的幌子，干着过程编程的勾当”第三重意思。无论是 c++ 还是 java 都有可能走过程编程的老路， java 降低的过程编写的可能性，但是如果你没有具备面向对象的编程思想，你还是可能走面向过程的老路的。

（未完待续）


我认为JSP有问题（上）

作者：redact

email: redact@163.net

日期：2000-12-26 14:30:01

我认为JSP有问题（上）

（作者：小龙亭主Blueski编译　2000年12月22日 14:22）

    （编者：这篇文章的原文首次在国外出现时，JSP还只是一种刚刚崭露头角的技术，并没有像现在这样如日中天。现在看来这篇文章的某些观点可能会有一定的局限性，但我不得不承认这是一篇很大气的作品，其中涉及很多JSP的内在原理。因此，我想还是有必要把这篇文章介绍给大家，以便各位从另一个侧面更深入的了解JSP技术。）

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

    本文将比较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，他们的引擎是免费的。

    开发者应该明了，选定一个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的语法。其它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代码，这将具有一定的价值，但前提是要有一个广泛知晓的，全功能的，可以免费得到的，标准化的标记库。目前还没有出现这样的标记库。


我认为JSP有问题（下）

作者：redact

email: redact@163.net

日期：2000-12-26 14:30:55

我认为JSP有问题（下）

（作者：小龙亭主Blueski编译　2000年12月22日 14:22）

    （续上篇）

    问题 #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的解决方案了。

    <全文完>


无状态Enterprise JavaBeans的观点

作者：运气

email: webmaster@chinaspx.com

日期：7/4/2001 1:43:50 PM

    在Component-Base和N-Tier潮流下，无状态（stateless）物件扮演着重要的角色。stateless物件是 OO 和Transaction Service两项技术结合的产物。OO 技术强调的是弹性（felxibility）、重用性（reusability）和分散性(distribution)，Transaction Service技术强调的是效率（performance）和延展性（scalability）。 Stateless物件是这两项技术结合的重要支柱。

    传统的OO技术大多着重于 Stateful 物件，然而在今天的MiddleWare（如EJB和 COM+/MTS）里面，Stateless物件的地位却远远高于Stateful物件。尤其是在EJB的架构里，Stateless有着非常重要的用途。例如Stateless的Session物件不需要长期保存自身的状态值，也不专属于特定的用户（client），所以占用的服务器资源非常之少，也由于不隶属于特定的用户，所以许多EJB物件可以共用少数的Stateless物件。

    Stateless物件的特性

    就Client而言，在意的是server物件的外在行为，而行为又和物件的状态无关。所以client物件不必在意物件状态的变化，而不是此 种物件真的没有状态。比如说stateless的限制并不意味着stateless的session beans不能拥有属性变量，所以，这些session beans一样会拥有自己的内部状态，然而重要的是，client不会看到这些内部状态。

    在Internet时代里，资讯系统的扩展性（scalability）显得无比重要，而高度扩展性的前提是必须有效运用Server的资源，而无状态的物件的资源共用性极大地减少了服务器的负载，因此，在开发N-Tier系统的时候，活用Stateless物件是系统开发人员必须具有的技能之一。

    使用Stateless支持交易

    当企业资讯系统的Client数目大幅度增加的时候，每个Client能够享有的共用资源（shared resources）自然会更加稀少。面对这个问题，除了投入更多的资源之外，好好协调以避免资源的浪费，达到充分共享，也是重要的解决之道。

    其中，数据库是最典型的稀有共享资源，交易是协调这项共享资源的可靠技术，如果加上Stateless物件，交易就会更加具有威力--更有效运用数据库的connection，并能跨越不同的数据库（即分布式交易）。

    交易是商业往来或异动的事件（event） 。事件一旦发生，企业资讯系统就会参与进行一连串的作业过程（process），在这个过程中，会导致共享资源（比如数据库的资料）的一连串异动。为了确保在异动过程中，不受到别的交易的干扰，也为了确保异动过程中的例外处理（比如roll back），“交易”就成为用来管理和协调共用资源的工具。

    在交易过程中，需要数据库提供服务（即资源），以便能够异动数据库里面的资料，完毕后就把资源释放，供彼得交易使用。在像EJB等系统的交易服务（transaction service）里 ，都必须具备有ACID（atomic,consistency,isolation,duration）四个特性，来做到上述的资源管理功能。

    一般来说，必须先取得和数据库的沟通管道（connection） ，才能获得数据库的服务，而数据库的connection又很有限，所以协调connection成为管理数据库资源的重心。让每个client能尽快释放connection成为重要的管理工作。

    在交易的进行过程中，会取得所需的数据库连接，一直到交易结束才会释放掉，所以充分运用连接一定要注意两点，一是交易的时间越短越好，交易越短就能越快速地释放被占用的connection。二是交易所需要的connection越少越好，这样就有更多的共享机会了。

    在component-based环境中，一个交易通常由一群物件所共同合作，每个物件可能由不同人所设计开发，每个物件也都可能向数据库索取connection。如果一个交易的参与物件越多，而且每个物件占用的connection时间越长，则这个交易就需要足够多的connection才能完成任务。

    因此，“让物件迅速释放connection” 是提升数据库资源运用效率的重要策略，stateless物件则是实现这个策略的重要技术。


小议如何在 Applet 中显示图象

作者：jeru

日期：2000-12-20 12:58:02

小议如何在 Applet 中显示图象

*************************************************************

**                                        **

** 作者：刘湛        一天进步一小步             **

** 日期：2000-12-19        ==============              **

** jeru@163.net        充实我的每一天             **

**                          **

*************************************************************

一。在 Applet 中显示图象需要引入以下包,并调用ImageObserver接口.：

import java.awt.image.*;

import java.awt.image.ImageObserver;

import java.net.URL;

public class MyApplet extends Applet implements ImageObserver { ...

二。定义图象路径

URL imgURL = getDocumentBase()；

将路径设为和你的html文件在相同。

URL imgUrl = getClassBase()；

将路径设为和你的html文件在相同。

三。获取图象

img = getImage(imgURL,imgName);

当然，有可能你想将自己的图象文件放到一个目录里去，那么你可以这样做

img = getImage(imgURL, "img" + imgName);

四。显示图象

最基本方法是调用 drawImage(),这个方法在Graphics类中有定义,最简单的调用形式如下：

g.drawImage(image, xPosition, yPosition, this);

最后一个参数呼叫了ImageObserver.

五，最后的补充

以上所说的都是最基本的，当然有时候你甚至想把你的图象文件打成jar包，这时，你就需要用到

getResourceAsStream() 方法了。

InputStream in = getClass().getResourceAsStream("image.gif");

byte buffer[] = new byte[in.available()];

for (int i = 0; n = in.available(); i < n; i ++)

  buffer[i] = (byte)in.read();

Image img = createImage(buffer);

六。 一个完整的小实例

import java.awt.*;

import java.applet.*;

import java.awt.image.*;

import java.awt.image.ImageObserver;

import java.net.URL;

//使用ImageObserver接口

public class MyApplet extends Applet implements ImageObserver{

  Image img;

  String imgPath = "";

  int xpoint = 100, ypoint = 100;

  public void init() {

    setBackground(Color.white); //设置背景色

    setForeground(Color.blue); //设置前景色

    imgPath = "img/" + "test.gif"; //设置图象文件路径

  }

  public void paint(Graphics g) {

    URL imgURL = getDocumentBase();

    img = getImage(imgURL, imgPath);

    g.drawImage(img,xpoint,ypoint,this);

  }

}


小议如何在 Application 中显示图象

作者：jeru

日期：2000-12-20 12:58:53

小议如何在 Application 中显示图象

*************************************************************

**

** 作者：刘湛 ------------------- 一天进步一小步

** 日期：2000-12-20 ------------ ==============

** jeru@163.net ----------------- 充实我的每一天

**

*************************************************************

在 application 中显示图象有些人用的方法比较麻烦，要用到 Toolkit 类。

这里我发现了一个教为简单的办法，就是用getImage()方法来从一个 ImageIcon 对象获取 Image 对象。

具体做法如下：

//获取图象文件路径

//getResource()方法会自动的去CLASSPATH中找你的图象文件，这不失为一中好的办法

//即使你的图象文件在jar包中，我们也可以很轻易的找到它

URL imgURL = getClass().getResource("img/test.gif");

//建立ImageIcon 类

ImageIcon icon = new ImageIcon(imgURL);

//由icon得到img

Image img = icon.getImage();

这样一来，把我上一篇文章<小议如何在 Applet 中显示图象>的代码稍微改动一点就可以在application中显示

图象了，连附例程如下：

import javax.swing.*;

import java.awt.*;

import java.net.URL;

import java.awt.image.*;

public class MyFrame extends JFrame {

  int xpoint = 100, ypoint = 100;

  public MyFrame() {

    //Do frame stuff.

    super("MyFrame");

  }

  public void paint(Graphics g) {

    URL imgURL = getClass().getResource("img/test.gif");

    ImageIcon icon = new ImageIcon(imgURL);

    g.drawImage(icon.getImage(),xpoint,ypoint,this);

  }

// main function

  public static void main(String[] args) {

    MyFrame frame = new MyFrame();

    frame.pack();

    frame.setVisible(true);

  }

}


小议如何在组件上显示图象

作者：jeru

email: jeru@163.net

日期：2000-12-22 10:14:34

小议如何在组件上显示图象

●○●○●○●○●○●○●○●○●○●○●○●○●○●○

○●作者:刘湛○●○日期:2000-12-22●○●jeru@163.net○●

●○●○●○●○●○●○●○●○●○●○●○●○●○●○

○●一天进步一小步●○●○●○●○●○充实我的每一天○●

●○●○●○●○●○●○●○●○●○●○●○●○●○●○

首先要说明的是，在组件上方便的显示图象是 swing 比 AWT 优越的特性之一，暂时我还没看到如何

在 AWT 中实现这一方法，如果你知道，请来信告诉我。

在 swing 中,button, lable 等组件都可轻易的用图象来改变它的外观，以 button 为例，当创建一

个 button 对象的时候通常是这样的：

JButton button = new JButton("Click me");

这样就创建了一个 button 对象，上面显示的文字是 Click me.

现在我们来稍做改动：

ImageIcon buttonIcon = new ImageIcon("button.gif", "a background of button");

JButton button = new JButton(buttonIcon);

这两行代码就达到了目的，在 button 上显示了图象 button.gif,注意我们用的是 ImageIcon 类，而

不是 Image 类。ImageIcon 中的第二个参数是对 ImageIcon 对象的描述，这个参数以后可能会被

assistive 技术用到。

如果想在 button 上同时显示文字和图象，还可以这样：

JButton button = new JButton("Click me", buttonIcon);

另外，还可以定义图象在 组件上的位置，如：

JButton button = new JButton("Click me", buttonIcon, JButton.CENTER);

最后，在顺便的讲一下如何改变你的 applet/application 上角的那个小图标，怎么样，看那杯咖啡厌烦了把？

让我们来换上自己的图标吧,我用的是setIconImage方法。

在 applet中，定义了一个image对象后就可以直接以它为参数来使用 setIconImage() 方法了，

而在 application 中，还需要由 ImageIcon 类转换到 Image 对象去。可能是我笨，不知道如何在application中

有什么简单的办法可以直接使用 Image 类，谁知道请写信给我，十分感谢。

URL imgURL = getClass().getResource("title.gif");

ImageIcon icon = new ImageIcon(imgURL);

setIconImage(icon.getImage());


新官上任，转贴一篇：Java做一个最简单的通话程序

作者：javabeans

日期：00-4-28 20:38:59

Java中的网络编程是一个很重要的部分，也是其编程优越性的地方之一。在Java中有一个专门的Java.net类库来管理网络编程的有关方法。

    下面先介绍在Java中怎样用socket进行客户与服务器通信。最后再介绍一个一个最简单的通话程序。

一.怎样用socket进行客户与服务器通信

    在Java中用socket进行客户/服务器之间的通信编程。Socket是两个实体之间进行通信的有效端点。通过socket可以获得源IP地址和源端口、终点IP地址和终点端口。用户可以将多个socket连入同一个端口，以便对于单个端口可以有多个连接。通过socket客户/服务器编程可以创建一个能被许多人使用的分布式程序，并且所有客户均可以用统一的前端进行工作，并与服务器进行通信。

    要想与服务器通信必须具备三个条件：服务器程序、客户程序和连接它们的socket程序。这三个部分缺一不可。但是，客户与服务器之间的通信有很多的方式，其中另一个方法是把客户作为索取者，把服务器作为给予者。下面我们看一看Java的服务器编程。

    在Java中，服务器有3个主要的功能：

    1.在Java.net类库中通过构造一个ServerSocket类的实例使服务器能够检测到指定端口的信息。用ServerSocke中的accept()方法可以使服务器检测到指定端口的活动。另外，服务器还负责检测要求与它连接的客户。

·Socket类的实例代表客户与服务器连接成功。通过编程可以使多个用户通过同一个端口与服务器相连，其中都是Socket 类的实例。

    2.可以分别用Socket类的getInputStream()和getOutStream()方法来发送和捕捉数据。其使用方法如下：

    try{

        ServerSocket myServerSocket=new ServerSocket(100);

        Socket my100Socket=myServerSocket.accept();

    }catch(Exception e){}

    在上面的代码中，首先构造一个ServerSocket类的实例，并传递给它一个整数作为服务器指定可以使用的给定端口，如下：

    ServerSocket myServerSocket=new ServerSocket(100)；

    在Java程序中如果每次构造ServerSocket时都能保持捕捉异常事件，则就随时指定了准备使用的端口。下面的代码使用accept()方法来检测端口的活动。

    Socket my100Socket=myServerSocket.accept();

    Accept()方法直到接收到用户的连接请求，才继续执行中断的执行程序。一旦客户的连接成功，my100Socket就代表该连接，并且可以发送和接收数据。

最后，我们看一看客户是怎样请求连接的。其连接方法如下：

    try{

        Socket mySocket=new Socket("http://www.cpcw.com",100);

    }catch(Exception e ){}

    通过上面的代码可能看出，也是通过Socket类来实现的。下面我们通过一个网络编程的实例来说明如何进行网络通信。

二.一个最简单的通话程序

通话器服务器:

import java.net.*;

import java.io.*;

import java.lang.*;

public class myserver{

public static void main(String args[]){

ServerSocket server;

Socket socket;

String s;

InputStream Is;

OutputStream Os;

DataInputStream DIS;

PrintStream PS;

try{

//在端口4321注册服务

server=new ServerSocket(4321);

socket=server.accept(); 　　//监听窗口,等待连接

System.out.println("server ok");

System.out.println("************************************************");

System.out.println("");

//获得对应Socket的输入/输出流

Is=socket.getInputStream();

Os=socket.getOutputStream();

//建立数据流

DIS=new DataInputStream(Is);

PS=new PrintStream(Os);

DataInputStream in=new DataInputStream(System.in);

while(true){

System.out.println("");

System.out.println("please wait client's message...");

System.out.println("");

s=DIS.readLine(); //读入从client传来的字符串

System.out.println("client said:"+s); //打印字符串

if(s.trim().equals("BYE"))break; //如果是"BYE",就退出

System.out.print("you say:");

s=in.readLine(); //读取用户输入的字符串

PS.println(s); //将读取得字符串传给client

if(s.trim().equals("BYE"))break; //如果是"BYE",就退出

}

//关闭连接

DIS.close(); //关闭数据输入流

PS.close(); //关闭数据输出流

Is.close(); //关闭输入流

Os.close(); //关闭输出流

socket.close(); //关闭sockey

}

catch(Exception e){

System.out.println("Error:"+e);

}

}

}

通话器客户端

import java.net.*;

import java.io.*;

import java.lang.*;

public class myclient{

public static void main(String args[]){

if (args.length<1){ //判断命令加参数没有

System.out.println("you forget the name of the server!");

System.out.println("see also: myclient yxf");

System.exit(1); //如果没加参数就退出

}

Socket socket;

String s="yxfsoft@263.net";

String len;

InputStream Is;

OutputStream Os;

DataInputStream DIS;

PrintStream PS;

try{

//向主机名为args[0]的服务器申请连接

//注意端口号要与服务器保持一致:4321

socket=new Socket(args[0],4321);

System.out.println("client ok");

System.out.println("************************************************");

System.out.println("");

//获得对应socket的输入/输出流

Is=socket.getInputStream();

Os=socket.getOutputStream();

//建立数据流

DIS=new DataInputStream(Is);

PS=new PrintStream(Os);

DataInputStream in=new DataInputStream(System.in);

while(true){

System.out.print("you say:");

s=in.readLine(); //读取用户输入的字符串

PS.println(s); //将读取得字符串传给server

if(s.trim().equals("BYE"))break; //如果是"BYE",就退出

else

{

System.out.println("");

System.out.println("please wait server's message...");

System.out.println("");

}

s=DIS.readLine(); //从服务器获得字符串

System.out.println("server said:"+s); //打印字符串

if(s.trim().equals("BYE"))break; //如果是"BYE",就退出

}

//关闭连接

DIS.close(); //关闭数据输入流

PS.close(); //关闭数据输出流

Is.close(); //关闭输入流

Os.close(); //关闭输出流

socket.close(); //关闭socket

}

catch(Exception e){

System.out.println("Error:"+e);

}

}

}

    下载源文件:客户端工程,服务器工程。编程环境为VisualJ++6.0。

    请读者先在一台机器上运行myserver.exe(myserver.exe在服务器工程内),然后在同一台机器或与第一台机器连了网的机器上打开控制台(Dos窗口),然后转到myclient.exe(myclient.exe在客户端工程内)所在的目录,如下运行客户端程序: myclient serverhostName 或 myclient serverhostIp ,serverhostName为运行服务器程序的机器名, serverhostIp为运行服务器程序的机器的IP地址。运行后就可以相互通话了。这个通话程序只能轮换着说话，读者可以在它的基础上丰富其功能。

原作者：小锋


新书快讯：Enterprise JAVABEANS（第二版）

此电子文档属C#乐园QQ群3256045整理。
新书快讯：Enterprise JAVABEANS（第二版）

上一页 目录 下一页

新书快讯：Enterprise JAVABEANS（第二版）

作者：大哥

日期：2001-5-9 15:24:21

 书名：  Enterprise JAVABEANS（第二版）

编号：  4338

ISBN：

作者：  Richard Monson

出版社：  中国电力出版社

出版日期：  2001 年 3 月

印刷日期：  2001 年 3 月

页数：  482

浏览人数：  205

原价：  ￥69

让利：  ￥10.4

优惠价：  ￥58.6

    本书是讲述Enterprise JavaBeans（EJB）技术的权威指南。EJB技术使你能够将为商务对象及过程建模的组合起来，建立复杂、关键性的系统。EJB在对象持久性、安全性和事务管理等方面提供了自动支持，从而极大程度地简化了开发过程。

    EJB 1.1实现了几年前令人吃惊的预言：EJB不仅可以无需修改地应用在任何操作系统上，而且可以无需修改地运用在任何EJB 1.1企业级服务器上。EJB 1.0实现了最低目标，它证明移植性是可能的；而EJB 1.1则向前迈进了一大步，它使服务器端组件更为强大，更容易移植，配置也更容易。

    本书指导你如何开发EJB，为你的商务建模，以及如何在客户端使用这些组件。本书内容包括：

     ·EJB 1.1和EJB 1.0

    ·如何开发entity bean和session bean

    ·容器管理的持久性和bean管理的持久性

    ·XML配置描述器

    ·JNDI ENC（企业级命名背景）

    ·事务管理

    ·设计策略

    ·bean的生命周期

    ·EJB和Java 2企业版（J2EE）的关系

    如果在过去几年开发过企业级软件，你一定知道EJB所改变的领域正在不断扩展。阅读本书有助于你把握它最新的发展动向。如果你是企业级软件开发的新手，或者没有应用过EJB，本书将带你迅速掌握这一激动人心的技术，快速高效地建立商务系统。

    Richard Monson-Haefel是EJB领域顶尖的专家。他现在是OpenEJB（一个开源EJB服务器）的总设计师，还是Java开发者的门户站点jGuru.com的EJB专家。多年来，Richard一直从事分布式企业级计算技术方面的咨询顾问工作，涉及EJB、CORBA、Java RMI和其他Java项目。Richard维护着一个有关EJB和分布计算机技术的网站http://www.EJBNow.com


续 太长了

作者：罗亭

email: tinluo@263.net

日期：2001-3-8 13:57:47

续

---

/**

* Insert the method's description here.

* Creation date: (2001-2-4 17:28:17)

* @return java.util.Enumeration

*/

public Enumeration getParameterNames() {

if (pairs == null) return null;

return pairs.keys();

}

/**

* Insert the method's description here.

* Creation date: (2001-2-4 17:33:40)

* @return java.lang.String[]

* @param name java.lang.String

*/

public String[] getParameterValues(String name) {

if (pairs == null || !pairs.containsKey(name)) return null;

ArrayList al = (ArrayList) pairs.get(name);

String[] values = new String[al.size()];

for(int i=0;i<values.length;i++)

values[i] = (String) al.get(i);

return values;

}

/**

* Insert the method's description here.

* Creation date: (2001-2-4 20:34:37)

* @param urlenc java.lang.String

*/

private void parse(String urlenc) throws java.io.IOException{

if (urlenc == null) return;

StringTokenizer tok = new StringTokenizer(urlenc,"&");

try{

while (tok.hasMoreTokens()){

String aPair = tok.nextToken();

int pos = aPair.indexOf("=");

String name = null;

String value = null;

if(pos != -1){

name = decode(aPair.substring(0,pos));

value = decode(aPair.substring(pos+1));

}else{

name = aPair;

value = "";

}

if(pairs.containsKey(name)){

ArrayList values = (ArrayList)pairs.get(name);

values.add(value);

}else{

ArrayList values = new ArrayList();

values.add(value);

pairs.put(name,values);

}

}

}catch(Exception e){

throw new java.io.IOException(e.getMessage());

}

}

/**

* Insert the method's description here.

* Creation date: (2001-2-4 20:34:37)

* @param urlenc java.lang.String

*/

private void parse(String urlenc,String encoding) throws java.io.IOException{

if (urlenc == null) return;

StringTokenizer tok = new StringTokenizer(urlenc,"&");

try{

while (tok.hasMoreTokens()){

String aPair = tok.nextToken();

int pos = aPair.indexOf("=");

String name = null;

String value = null;

if(pos != -1){

name = decode(aPair.substring(0,pos),encoding);

value = decode(aPair.substring(pos+1),encoding);

}else{

name = aPair;

value = "";

}

if(pairs.containsKey(name)){

ArrayList values = (ArrayList)pairs.get(name);

values.add(value);

}else{

ArrayList values = new ArrayList();

values.add(value);

pairs.put(name,values);

}

}

}catch(Exception e){

throw new java.io.IOException(e.getMessage());

}

}

}

这个类的功能就是读取并保存form提交的信息，并实现常用的getParameter方法。

package com.hto.servlet;

import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

/**

* Insert the type's description here.

* Creation date: (2001-2-5 8:28:20)

* @author: 钱卫春

*/

public class UtfBaseServlet extends HttpServlet {

public static final String PARAMS_ATTR_NAME = "PARAMS_ATTR_NAME";

/**

* Process incoming HTTP GET requests

*

* @param request Object that encapsulates the request to the servlet

* @param response Object that encapsulates the response from the servlet

*/

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

performTask(request, response);

}

/**

* Process incoming HTTP POST requests

*

* @param request Object that encapsulates the request to the servlet

* @param response Object that encapsulates the response from the servlet

*/

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

performTask(request, response);

}

/**

* Insert the method's description here.

* Creation date: (2001-2-5 8:52:43)

* @return int

* @param request javax.servlet.http.HttpServletRequest

* @param name java.lang.String

* @param required boolean

* @param defValue int

*/

public static java.sql.Date getDateParameter(HttpServletRequest request, String name, boolean required, java.sql.Date defValue) throws ServletException{

String value = getParameter(request,name,required,String.valueOf(defValue));

return java.sql.Date.valueOf(value);

}

/**

* Insert the method's description here.

* Creation date: (2001-2-5 8:52:43)

* @return int

* @param request javax.servlet.http.HttpServletRequest

* @param name java.lang.String

* @param required boolean

* @param defValue int

*/

public static double getDoubleParameter(HttpServletRequest request, String name, boolean required, double defValue) throws ServletException{

String value = getParameter(request,name,required,String.valueOf(defValue));

return Double.parseDouble(value);

}

/**

* Insert the method's description here.

* Creation date: (2001-2-5 8:52:43)

* @return int

* @param request javax.servlet.http.HttpServletRequest

* @param name java.lang.String

* @param required boolean

* @param defValue int

*/

public static float getFloatParameter(HttpServletRequest request, String name, boolean required, float defValue) throws ServletException{

String value = getParameter(request,name,required,String.valueOf(defValue));

return Float.parseFloat(value);

}

/**

* Insert the method's description here.

* Creation date: (2001-2-5 8:52:43)

* @return int

* @param request javax.servlet.http.HttpServletRequest

* @param name java.lang.String

* @param required boolean

* @param defValue int

*/

public static int getIntParameter(HttpServletRequest request, String name, boolean required, int defValue) throws ServletException{

String value = getParameter(request,name,required,String.valueOf(defValue));

return Integer.parseInt(value);

}

/**

* Insert the method's description here.

* Creation date: (2001-2-5 8:43:36)

* @return java.lang.String

* @param request javax.servlet.http.HttpServletRequest

* @param name java.lang.String

* @param required boolean

* @param defValue java.lang.String

*/

public static String getParameter(HttpServletRequest request, String name, boolean required, String defValue) throws ServletException{

if(request.getAttribute(UtfBaseServlet.PARAMS_ATTR_NAME) != null) {

UTF8ParameterReader params = (UTF8ParameterReader)request.getAttribute(UtfBaseServlet.PARAMS_ATTR_NAME);

if (params.getParameter(name) != null) return params.getParameter(name);

if (required) throw new ServletException("The Parameter "+name+" Required but not provided!");

else return defValue;

}else{

if (request.getParameter(name) != null) return request.getParameter(name);

if (required) throw new ServletException("The Parameter "+name+" Required but not provided!");

else return defValue;

}

}

/**

* Returns the servlet info string.

*/

public String getServletInfo() {

return super.getServletInfo();

}

/**

* Insert the method's description here.

* Creation date: (2001-2-5 8:52:43)

* @return int

* @param request javax.servlet.http.HttpServletRequest

* @param name java.lang.String

* @param required boolean

* @param defValue int

*/

public static java.sql.Timestamp getTimestampParameter(HttpServletRequest request, String name, boolean required, java.sql.Timestamp defValue) throws ServletException{

String value = getParameter(request,name,required,String.valueOf(defValue));

return java.sql.Timestamp.valueOf(value);

}

/**

* Initializes the servlet.

*/

public void init() {

// insert code to initialize the servlet here

}

/**

* Process incoming requests for information

*

* @param request Object that encapsulates the request to the servlet

* @param response Object that encapsulates the response from the servlet

*/

public void performTask(HttpServletRequest request, HttpServletResponse response) {

try

{

// Insert user code from here.

}

catch(Throwable theException)

{

// uncomment the following line when unexpected exceptions

// are occuring to aid in debugging the problem.

//theException.printStackTrace();

}

}

/**

* Insert the method's description here.

* Creation date: (2001-2-5 8:31:54)

* @param request javax.servlet.ServletRequest

* @param response javax.servlet.ServletResponse

* @exception javax.servlet.ServletException The exception description.

* @exception java.io.IOException The exception description.

*/

public void service(ServletRequest request, ServletResponse response) throws javax.servlet.ServletException, java.io.IOException {

String content = request.getContentType();

if(content == null || content != null && content.toLowerCase().startsWith("application/x-www-form-urlencoded"))

request.setAttribute(PARAMS_ATTR_NAME,new UTF8ParameterReader((HttpServletRequest)request));

super.service(request,response);

}

}

    这个就是Servlet基类，它覆盖了父类的service方法，在调用父类service前，创建了UTF8ParameterReader对象，其中保存了form中提交的信息。然后把这个对象作为一个Attribute保存到Request对象中。然后照样调用父类的service方法。

    对于继承这个类的Servlet，要注意的是，"标准"getParameter在也不能读到post的数据，因为在这之前这个类中已经从ServletInputStream中读出了数据了。所以应该使用该类中提供的getParameter方法。

    剩下的就是输出问题了，我们要把输出的信息，转为UTF8的二进制流输出。只要我们设置Content-Type时指定charset为UTF8，然后使用PrintWriter输出，那么这些转换是自动进行的，Servlet中这样设置：

    response.setContentType("text/html;charset=UTF8");

Jsp中这样设置：

    <%@ page contentType="text/html;charset=UTF8"%>

    这样就可以保证输出是UTF8流，客户端能否显示，就看客户端的了。

    对于multipart/form-data的form提交的内容，我也提供一个类用来处理，在这个类的构造子中可以指定页面使用的charset，默认还是UTF-8，限于篇幅不贴出源码，如果感兴趣可以mail to：vividq@china.com和我探讨。


学习JAVA的第一方案 （转贴）

作者：shark

email: sqlserver@163.net

日期：2000-12-31 23:27:27

发信人: linuxandjava (linuxANDjava), 信区: Java

标  题: 學習JAVA的第一方案

发信站: 网易虚拟社区北京站 (Thu Apr 20 09:59:51 2000), 站内信件

國內雖然商品化的JAVA作品不多,但是學習JAVA的人數卻扶搖直上.

為了幫助更多的人能夠少走彎路,花最少的錢,用最少的時間,

最大限度的掌握Java, 本人提出如下方案,供初學者參考:

1. 第一階段; 入門

   這個時候,最主要的任務是對Java有一個大致的了解,掌握基本的語法,

能夠編一些簡單的如HelloWorld之類的程序.

   這時,買上一本20塊左右的Java入門書就足夠了.有利與整體把握,

而又不會死扣書上的例子,應為例子都特別簡單.有些人一開始就買上一本

Java大全,Java高級技巧等等,結果要麼感覺苦澀難懂,要麼基本功不扎實.

看了幾章以後,就覺得寸步難行.遭受如此挫折以後,一部分人就會束之高閣.

有的人以後興趣來了,還會繼續看下去,有些人可能從此要遠離Java了.

2. 第二階段; 進階

   這時候,你已經能夠使用Java開發一般的程序了,只是性能上差一點.

   有的時候,還感覺無從下手,因為你對Java 的 API還不熟悉, 很多已經

可以用一個函數實現的功能,你去在那兒折騰半天,聽人家一說才恍然大悟.

所以, 這一階段的主要目標就是, 熟悉API, 盡可能的多看類的說明,

了解已經提供了那些函數,可以做那些事.慢慢的,你發現你的程序中引入了

很多包,很精簡的程序功能上也已經相當強了.環顧四週,儼然已是一個高手,

不禁沾沾自喜,學習的勁頭也更足了.

    這個階段,你已經開發了很多程序,需要注意的是應該考慮一下程序的

結構了,你會發現改一下結構以後,你公用了很多代碼,甚至會行成你自己的

類庫,擁有了自己的開發包. 應為JDK的Document中已經包含了大量信息,

有事沒事多看看, 就像翻字典一樣, 注意, 不要忘了吃飯睡覺喔.

    API的內容相當多,而且有的也不是一下就能明白的.如果旁邊有個高手

指點一下,那就太好了.要是沒有, 對不起, 你會經常被莫名其妙的問題搞的

頭暈腦漲. 這時, 買一本Java大全之類的書好像能節省很多時間, 但是如果你

英文好, 到國外查查資料, 很多問題都可以迎刃而解. 而且大全之類的書

大都很貴,講的還不透, 就比蜻蜓點水好那麼一點點,沒有沈麼收藏價值,

所以, 也不要盲目去買.

3. 第三階段 ;高級

   擁有了這麼多經驗值的你,總想開發一些專業的應用,以賣弄一二. 寫沈麼呢?

想來想去, 開發一個圖形處理的吧, 最好能夠趕上PhotoShop. 或者開發一個編輯

器.

開始寫的時候,雄心壯志,傲視天下,絕對結構化, 擁有良好的可擴展姓...

結果, 你發現你的圖形總不能如你所願,要麼顯示不出來,要麼邊上的一個像素

不見了,弄得你好傷心,信心十足的你熬了幾個通宵,卻依然無濟於事,最後,

你長嘆一聲,嗚呼,天亡我!

   這個階段的你遇到的都是細節上的問題,需要專家級的支持,周圍的人也

都幫不了你了,這時候你就要經常到書店轉轉,看看有沒有專題書,如Java圖形處理

,

Java AWT等等,轉注於某一個領域的書了, 而且這些書大都很深, 反覆看都看不膩

.

機具收藏價值. 不必猶豫,不必為錢心疼,買之而後快.

4. 第四階段: 一代宗師

   你應該到java.sun.com註冊一下,獲取JDK的源代碼,然後...

   然後...

   你的專題演講在全球直撥,題目是"學習JAVA的最佳方案".

   然後出版了一本書叫"超越JAVA".

   然後C消失了...


也谈新郎、sohu新闻系统的技术，当然是怎么用jsp实现的!!!!!

作者：asp3000

email: coolknight@263.net

日期：2000-12-19 19:08:49

   我这两天刚好完成了这样一个类似的系统，希望和大家探讨探讨jsp实现的技术。

   新浪 sohu这样的系统应该是类似的，就是后台动态生成前台的html页面，不管后台用什么工具。

   因为公司逼下来，要做这样的新闻系统，没有办法，连着做了两天，总算搞定了，很快就会发布到公司主页上面

   当然我只做了一个后台管理的东西，用jsp+javabean来动态生成html文件

我是这样实现的

１、首先要一个写好的htm文件模板，后台用Writer对象来写文件，然后几个需要替换的地方如新闻标题、新闻内容直接根据输入的来替换，文件名字就用日期如2000-12-18-1.htm类似的；

２、文件上传用jspsmartupload组件，主要用来传新闻图片，然后将生成的图片路径替换模板中的img src标签；

３、数据库中同时保存文件标题、文件名、日期等信息，新闻内容等其他的信息就没有必要保留了；

４、前台调用直接从数据库中取XXX.htm文件名、文件标题就行了；

   另外还用了几个小技巧，因为jspsmartupload组件不支持文件上传重新命名，所以我设了一个临时的目录存放上传的图片文件，然后写了一个方法将文件通过IO操作拷贝到指定的目录下，当然名字也换成了和htm文件对应的名字。这样是为了防止文件以多名字相同了，一般都以日期或者自动增加变量等方式来命名。

   初步测试了一下，速度还比较快，因为新闻都是静态的htm文件。

   苦干了两天，今晚总算可以稍微休息一下了:)

   如果大家也用jsp做过类似的系统，欢迎一起交流了。


一份 tomcat vs resin 的测试报告。。。

作者：jeru

email: jeru@163.net

日期：2001-2-9 18:53:49

----- Original Message -----

From: Griggs, James <griggsj@netgenshopper.com>

To: <tomcat-dev@jakarta.apache.org>

Sent: Tuesday, February 08, 2000 3:37 PM

Subject: Tomcat vs. Resin

> I ran an informal test for 9 hours last night on a Win98 PC comparing the

> latest tomcat(apache.org) code and the latest resin

> (caucho.com) both using Suns JDK 1.2.  I used 2 instances of socrates to

> simulate 100 users requesting a hello.jsp file at random intervals.

> Tomcat and Resin were running at the same time on different ports.

>

> Result: Tomcat: Resin:

> Total Requests Handled 26094 6546375

> Total Requests Dropped 4691 0

> Requests per Second .1 195

> Average Latency 98.17 .15

>

> I'm assuming the dramatic difference in requests handled is due to a 60

> second timeout on dropped requests.

>

> I'm wondering if anyone can explain the dramatic difference in the

results?

> Am I doing something wrong here to cripple Tomcat?  (... and since I know

> the question will be asked: I have no affiliation with Caucho ;o)

>

> Jim Griggs

> jim@netgenShopper.com

>


一个 Java，各自表述

作者：blackwhites

日期：2000-12-4 18:05:30

在本周的"新闻焦点"里有两则新闻和微软的Java策略有关。一则是微软公

开宣称.NET不会支援Java，现在学Java的人是浪费时间; 另外一则是微软

和HP交换条件，将使用HP的Chai技术(详见下面"新闻焦点")。有趣的是微

软在明处放话抵制Java，在暗地又支援精简版Java ── HP的Chai。Java

这个东西，在各大厂商的眼中实在是千变万化，充满各种爱恨情仇。我们

就来看看各种不同角度的Java。

在微软眼中，Java简直是眼中钉肉中刺。有了Java，代表唯我独尊的

Windows 平台不再是唯一的选择。用Java开发出来的东西可以在各种平台

上执行，原本想藉著数量庞大的应用程式一步步扩大 Windows版图的计划

就此泡汤，再加上Linux的出现，Windows大好江山眼看情势不妙。所以微

软一看到Java就有气，.NET将支援十几种程式语言，就是死也不支援Java

。还自立门户弄了一套"全新程式语言"叫做C#，骨子里根本是Java，嘴上

却说跟Java一点关系也没有。Java之於微软，好比是紧箍咒之於孙悟空，

一听到就头痛眼花浑身不对劲。

对IBM来说，Java 也不是顶愉快的东西。这种跨平台的好技术不是出自老

大哥IBM，而是来自小老弟 Sun，而且得到业界的支援，这让IBM实在脸上

无光，不过为了对抗独大的微软，IBM也投下庞大人力财力在 Java上面，

希望藉Java找到自己的第二春。不料Sun推出Java 2，把JDBC、Servlets

、EJB、JSP等Server端技术统包成所谓的J2EE (Java 2 Enterprise

Edition)，荣耀集於Sun一身。IBM 不爽Sun这样整碗捧去，不过为了顾全

大局，还是继续在 Java阵营扮演重要角色。对IBM来说，Java好比是蒋介

石眼中的台湾，虽然不尽满意，但是也无路可去，只有暂时全心投入建设

复兴基地，希望有朝一日当成反攻跳板光复神州。

而对Java的创始者Sun来说，Java就是未来的一切。如果没有Java，Sun只

是一个建造Unix伺服主机的公司。有了Java，让 Sun成为业界龙头，三天

两头上报纸科技版头条。Sun的野心当然不止於此，在Java 2推出後，Sun

要藉Java扩展版图的计划已经人尽皆知。藉J2SE (Java 2 Standard

Edition)稳住Java在电脑软体上的应用，藉J2ME (Java 2 Micro Edition)

跨足更广大的家电、智慧卡、电子装置等市场，再藉J2EE (Java 2

Enterprise Edition )一统伺服主机运算环境，阵仗一字排开，实在军容

壮盛。未来能否继续开疆拓土，就看 Sun能不能以更好的技术，加上更开

放的环境，吸引更多各产业的厂商加入这行列。所以在当前，也许UNIX伺

服主机是Sun主要营收来源，不过放眼未来，Java 技术才是永续发展的


一个applet---servlet通讯的例子---applet文件

作者：climbeNET

日期：2000-11-6 11:30:03

import java.applet.Applet;

import java.awt.*;

import java.awt.event.*;

import java.net.*;

import java.io.*;

public class sendapplet extends Applet implements ActionListener

{

    Label sendlable;

    Label messageInfo;

    Button sendButton;

    TextField sendText;

    TextField messageText;

    URLConnection connect;

    String message;

    URL chatURL;

    public String getAppletInfo()

    {

        return "this is a applet--servlet";

    }

    public synchronized void init()

    {

        super.init();

        resize(500,300);

        sendlable=new Label("Please enter Massage");

        messageInfo=new Label("What you hava send:");

        sendText=new TextField(40);

        sendButton=new Button("Send");

        messageText=new TextField(40);

        messageText.setEditable(false);

        //

        //

        Panel mainp=new Panel();

        GridBagLayout gbl=new GridBagLayout();

        GridBagConstraints gbc=new GridBagConstraints();

        gbc.weightx=0;

        gbc.weighty=0;

        gbc.gridy=0;

        gbc.gridx=0;

        gbc.gridwidth=10;

        gbc.gridheight=1;

        gbc.anchor=GridBagConstraints.CENTER;

        gbc.fill=GridBagConstraints.NONE;

        mainp.setLayout(gbl);

        gbl.setConstraints(sendlable,gbc);

        mainp.add(sendlable);

        gbc.gridy=1;

        gbc.gridwidth=9;

        gbc.fill=GridBagConstraints.HORIZONTAL;

        gbl.setConstraints(sendText,gbc);

        mainp.add(sendText);

        gbc.gridx=9;

        gbc.gridwidth=1;

        gbc.fill=GridBagConstraints.NONE;

        gbl.setConstraints(sendButton,gbc);

        mainp.add(sendButton);

        gbc.gridx=0;

        gbc.gridy=2;

        gbc.gridwidth=10;

        gbl.setConstraints(messageInfo,gbc);

        mainp.add(messageInfo);

        gbc.gridy=3;

        gbc.weighty=100;

        gbc.gridheight=10;

        gbc.fill=GridBagConstraints.BOTH;

        gbl.setConstraints(messageText,gbc);

        mainp.add(messageText);

        sendButton.addActionListener(this);

        //构造的panel加入到applet中

        setLayout(new BorderLayout());

        add("Center",mainp);

        chatURL=getCodeBase();

    }

    public synchronized void start()

    {

    }

    public synchronized void stop()

    {

    }

    public synchronized void destroy()

    {

    }

    //

    private void Send()

    {

        message=sendText.getText();

        sendText.setText("");

        //showStatus("Message send!");

        p("Message send!");

        messageText.setText("message");

        String queryString="/servlet/Receive?message"+URLEncoder.encode(message);

        p("Attemping to send :"+message);

        try{

            connect=(new URL(chatURL,queryString)).openConnection();

            showStatus("Open Connection!");

            connect.setDefaultUseCaches(false);

            connect.setUseCaches(false);

            connect.setDoInput(true);

            connect.setDoOutput(false);

            connect.connect();

            p("Make connection to "+connect);

            showStatus("Open Sream!");

            DataInputStream in=new DataInputStream(connect.getInputStream());

            showStatus("reading");

            message=in.readLine();

            while(message!=null)

            {

                messageText.setText(message);

                message=in.readLine();

            }//end while

        }//end try

        catch(MalformedURLException e2)

        {

              System.err.println("Malformedurlexception:"+e2.toString());

        }//end catch

        catch(IOException e1)

        {

              System.err.println(e1.toString());

        }

    }//end Send

    public void actionPerformed(ActionEvent e)

    {

        if(e.getSource()==sendButton)

        {

            Send();

        }

    }

    private void p(String debug)

    {

         messageText.setText(debug);

        //System.out.println("app-servlet:"+debug);

    }

}


一个applet---servlet通讯的例子---servlet文件

作者：climbeNET

日期：2000-11-6 11:31:36

import java.io.*;

import java.util.Date;

import javax.servlet.*;

import javax.servlet.http.*;

public void Riceive extends HttpServlet

{

    public void service(HttpServletRequest req,HttpServletResponse res) throws ServletException,IOException

    {

         res.setContenType("text/plain");

         ServletOutputStream out=res.getOutputStream();

         out.print("recive user message:");

         out.print(req.getParameter("message"));

    }//end service

}//end receive


一个JDBC－ODBC桥的字符集参数的例子。

作者：sonymusic

email: sonymusic@china.com

日期：7/7/2001 11:07:13 PM

//这个例子，是从SUN的站点上找来的。当时也找了好久啊。呵呵！

// accepts sql statments thru command line

import java.sql.*;

import java.util.*;

public class test {

public static void main(String args[]){

try

{

Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");

Properties prop = new Properties();

prop.put("user", "");

prop.put("password","");

prop.put("charSet","gb2312");

Connection conn=DriverManager.getConnection("jdbc:odbc:chinese",prop);

Statement stmt=conn.createStatement();

stmt.execute(args[0]);

}

catch(ClassNotFoundException e)

{

// JOptionPane.showMessageDialog(null,"Unable To Load The Driver Class","Login Message", JOptionPane.ERROR_MESSAGE);

}

catch(SQLException e)

{

e.printStackTrace();

System.out.println(e.getErrorCode());

}

}

}


一个进行Base64编码的类。

作者：sonymusic

email: sonymusic@china.com

日期：2001-3-12 15:01:35

package sony.utils;

import java.io.*;

import java.net.*;

public final class Codes {

public final static byte[] base64Encode(byte[] byteData) {

    if (byteData == null)

        return null;

    int iSrcIdx; // index into source (byteData)

    int iDestIdx; // index into destination (byteDest)

    byte byteDest[] = new byte[((byteData.length + 2) / 3) * 4];

    for (iSrcIdx = 0, iDestIdx = 0; iSrcIdx < byteData.length - 2; iSrcIdx += 3) {

        byteDest[iDestIdx++] = (byte) ((byteData[iSrcIdx] >>> 2) & 077);

        byteDest[iDestIdx++] =

            (byte) ((byteData[iSrcIdx + 1] >>> 4) & 017 | (byteData[iSrcIdx] << 4) & 077);

        byteDest[iDestIdx++] =

            (byte) ((byteData[iSrcIdx + 2] >>> 6)

                & 003

                | (byteData[iSrcIdx + 1] << 2)

                & 077);

        byteDest[iDestIdx++] = (byte) (byteData[iSrcIdx + 2] & 077);

    }

    if (iSrcIdx < byteData.length) {

        byteDest[iDestIdx++] = (byte) ((byteData[iSrcIdx] >>> 2) & 077);

        if (iSrcIdx < byteData.length - 1) {

            byteDest[iDestIdx++] =

                (byte) ((byteData[iSrcIdx + 1] >>> 4) & 017 | (byteData[iSrcIdx] << 4) & 077);

            byteDest[iDestIdx++] = (byte) ((byteData[iSrcIdx + 1] << 2) & 077);

        } else

            byteDest[iDestIdx++] = (byte) ((byteData[iSrcIdx] << 4) & 077);

    }

    for (iSrcIdx = 0; iSrcIdx < iDestIdx; iSrcIdx++) {

        if (byteDest[iSrcIdx] < 26)

            byteDest[iSrcIdx] = (byte) (byteDest[iSrcIdx] + 'A');

        else

            if (byteDest[iSrcIdx] < 52)

                byteDest[iSrcIdx] = (byte) (byteDest[iSrcIdx] + 'a' - 26);

            else

                if (byteDest[iSrcIdx] < 62)

                    byteDest[iSrcIdx] = (byte) (byteDest[iSrcIdx] + '0' - 52);

                else

                    if (byteDest[iSrcIdx] < 63)

                        byteDest[iSrcIdx] = (byte) '+';

                    else

                        byteDest[iSrcIdx] = (byte) '/';

    }

    for (; iSrcIdx < byteDest.length; iSrcIdx++)

        byteDest[iSrcIdx] = (byte) '=';

    return byteDest;

}

public final static String base64Encode(String strInput) {

    if (strInput == null)

        return null;

    return base64Encode(strInput,"GB2312");

}

public final static String base64Encode(String strInput,String charSet) {

    if (strInput == null)

        return null;

    String strOutput=null;

    byte byteData[] = new byte[strInput.length()];

    try {

        //strInput.getBytes(0, strInput.length(), byteData, 0);

        byteData = strInput.getBytes(charSet);

        strOutput=new String(base64Encode(byteData),charSet);

        //strOutput=new String(base64Encode(byteData),0);

    } catch (UnsupportedEncodingException e) {

        return null;

    }

    return strOutput;

}

/**

 * 此处插入方法说明。

 * 创建日期：(2000-11-4 18:27:35)

 * @param steam java.io.InputStream

 * @param charSet java.lang.String

 */

public final static String base64Encode(InputStream in, String charSet) {

    try {

        int c;

        byte[] buff = new byte[1024];

        ByteArrayOutputStream out = new ByteArrayOutputStream(2048);

        while ((c = in.read(buff, 0, 1024)) != -1) {

            out.write(buff, 0, c);

            //index+=1024;

            //out.write(c);

            //attachContent+=ss;

        }

        in.close();

        out.flush();

        byte[] tmp2 = Codes.base64Encode(out.toByteArray());

        out.close();

        return new String(tmp2,charSet);

    }

    catch (IOException e) {

        return "";

    }

}/**

 * 此处插入方法说明。

 * 创建日期：(2000-11-3 23:31:04)

 * @return java.lang.String

 * @param strIn java.lang.String

 */

public final static String chunkSplit(String strIn) {

    return chunkSplit(strIn,76);

}/**

 * 此处插入方法说明。

 * 创建日期：(2000-11-3 23:31:04)

 * @return java.lang.String

 * @param strIn java.lang.String

 */

public final static String chunkSplit(String strIn,int splitLen) {

    int index=0;

    String strOut="";

    while(index+splitLen<strIn.length()){

        strOut+=strIn.substring(index,index+splitLen)+"\n";

        index+=splitLen;

    }

    if(index<strIn.length()){

        strOut+=strIn.substring(index);

    }

    return strOut;

}

}


一个连接池的例子（来自JIVE）（1）

作者：sonymusic

email: sonymusic@china.com

日期：2001-5-17 11:32:45

//文件：DbConnectionDefaultPool.java的第一部分

//请注意看里面注明的一处需要修改连接参数的地方

package com.qingtuo.db.pool;

import java.sql.*;

import java.util.*;

import java.io.*;

import java.text.*;

import java.util.Date;

/**

 * Default Jive connection provider. It uses the excellent connection pool

 * available from http://www.javaexchange.com.This connection provider is a

 * a good choice unless you can use a container-managed one.

 */

public class DbConnectionDefaultPool extends DbConnectionProvider {

    private static final String NAME = "Default Connection Pool";

    private static final String DESCRIPTION = "The default connection provider "

        + "that uses the connection pool from javaexchange.com. It works with "

        + "almost any database setup, is customizable, and offers good performance. "

        + "Use this connection provider unless you have your own or can use a "

        + "container managed connection pool.";

    private static final String AUTHOR = "CoolServlets.com";

    private static final int MAJOR_VERSION = 1;

    private static final int MINOR_VERSION = 0;

    private static final boolean POOLED = true;

    private ConnectionPool connectionPool = null;

    private Properties props;

    private Properties propDescriptions;

    private Object initLock = new Object();

    public DbConnectionDefaultPool() {

        //this.manager = manager;

        props = new Properties();

        propDescriptions = new Properties();

        //Initialize all property values

        initializeProperties();

        //Load any existing property values

        loadProperties();

    }

    /**

     * Returns a database connection.

     */

    public Connection getConnection() {

        if (connectionPool == null) {

            //block until the init has been done

            synchronized(initLock) {

                //if still null, something has gone wrong

                if (connectionPool == null) {

                    System.err.println("Warning: DbConnectionDefaultPool.getConnection() was " +

                    "called when the internal pool has not been initialized.");

                    return null;

                }

            }

        }

        return new ConnectionWrapper(connectionPool.getConnection(), connectionPool);

    }

    /**

     * Starts the pool.

     */

    protected void start() {

        //acquire lock so that no connections can be returned.

        synchronized (initLock) {

            //Get properties

            String driver = props.getProperty("driver");

            String server = props.getProperty("server");

            String username = props.getProperty("username");

            String password = props.getProperty("password");

            int minConnections = 0, maxConnections = 0;

            double connectionTimeout = 0.0;

            try {

                minConnections = Integer.parseInt(props.getProperty("minConnections"));

                maxConnections = Integer.parseInt(props.getProperty("maxConnections"));

                connectionTimeout = Double.parseDouble(props.getProperty("connectionTimeout"));

            }

            catch (Exception e) {

                System.err.println("Error: could not parse default pool properties. " +

                    "Make sure the values exist and are correct.");

                e.printStackTrace();

                return;

            }

            String logPath = props.getProperty("logPath");

            try {

                connectionPool = new ConnectionPool(driver, server, username, password,

                    minConnections, maxConnections, logPath, connectionTimeout);

            }

            catch (IOException ioe) {

                System.err.println("Error starting DbConnectionDefaultPool: " + ioe);

                ioe.printStackTrace();

            }

        }

    }

    /**

     * Restarts the pool to take into account any property changes.

     */

    protected void restart() {

        //Kill off pool.

        destroy();

        //Reload properties.

        loadProperties();

        //Start a new pool.

        start();

    }

    /**

     * Destroys the connection pool.

     */

    protected void destroy() {

        if (connectionPool != null) {

            try {

                connectionPool.destroy(1);

            }

            catch (Exception e) {

                e.printStackTrace();

            }

        }

        //Release reference to connectionPool

        connectionPool = null;

    }

    /**

     * Returns the value of a property of the connection provider.

     *

     * @param name the name of the property.

     * @returns the value of the property.

     */

    public String getProperty(String name) {

        return (String)props.get(name);

    }

    /**

     * Returns the description of a property of the connection provider.

     *

     * @param name the name of the property.

     * @return the description of the property.

     */

    public String getPropertyDescription(String name) {

        return (String)propDescriptions.get(name);

    }

    /**

     * Returns an enumeration of the property names for the connection provider.

     */

    public Enumeration propertyNames() {

        return props.propertyNames();

    }

    /**

     * Sets a property of the connection provider. Each provider has a set number

     * of properties that are determined by the author. Trying to set a non-

     * existant property will result in an IllegalArgumentException.

     *

     * @param name the name of the property to set.

     * @param value the new value for the property.

     *

     */

    public void setProperty(String name, String value) {

        props.put(name, value);

        saveProperties();

    }

    /**

     * Give default values to all the properties and descriptions.

     */

    private void initializeProperties() {

        props.put("driver","");

        props.put("server","");

        props.put("username","");

        props.put("password","");

        props.put("minConnections","");

        props.put("maxConnections","");

        props.put("logPath","");

        props.put("connectionTimeout","");

        propDescriptions.put("driver","JDBC driver. e.g. 'oracle.jdbc.driver.OracleDriver'");

        propDescriptions.put("server","JDBC connect string. e.g. 'jdbc:oracle:thin:@203.92.21.109:1526:orcl'");

        propDescriptions.put("username","Database username. e.g. 'Scott'");

        propDescriptions.put("password","Database password. e.g. 'Tiger'");

        propDescriptions.put("minConnections","Minimum # of connections to start with in pool. Three is the recommended minimum");

        propDescriptions.put("maxConnections","Maximum # of connections in dynamic pool. Fifteen should give good performance for an average load.");

        propDescriptions.put("logPath","Absolute path name for log file. e.g. 'c:\\logs\\jiveDbLog.log'");

        propDescriptions.put("connectionTimeout","Time in days between connection resets. e.g. '.5'");

    }

    /**

     * Load whatever properties that already exist.

     */

    private void loadProperties() {

        //在这里修改一些连接参数

        //in 2000

        /*

        String driver="org.gjt.mm.mysql.Driver";

        String server="jdbc:mysql://192.100.100.11/pcc";

        String username="pcc";

        String password="pcc123";

        String minConnections="3";

        String maxConnections="10";

        String logPath="c:\\temp\\qingtuoDbLog.log";

        String connectionTimeout="0.5";

        */

        //in Linux

        String driver="org.gjt.mm.mysql.Driver";

        String server="jdbc:mysql://192.100.100.1/qingtuo";

        //String server="jdbc:mysql://192.168.0.1/qingtuo";

        String username="qingtuo";

        String password="qingtuo";

        String minConnections="3";

        String maxConnections="20";

        String logPath="c:\\temp\\qingtuoDbLog.log";

//        String logPath="/tmp/qingtuoDbLog.log";

        String connectionTimeout="0.5";

        if (driver != null) {  props.setProperty("driver", driver);  }

        if (server != null) {  props.setProperty("server", server);  }

        if (username != null) {  props.setProperty("username", username);  }

        if (password != null) {  props.setProperty("password", password);  }

        //if (database != null) {  props.setProperty("database", database);  }

        if (minConnections != null) {  props.setProperty("minConnections", minConnections);  }

        if (maxConnections != null) {  props.setProperty("maxConnections", maxConnections);  }

        if (logPath != null) {  props.setProperty("logPath", logPath);  }

        if (connectionTimeout != null) {  props.setProperty("connectionTimeout", connectionTimeout);  }

    }

    private void saveProperties() {

        PropertyManager.setProperty("DbConnectionDefaultPool.driver", props.getProperty("driver"));

        PropertyManager.setProperty("DbConnectionDefaultPool.server", props.getProperty("server"));

        PropertyManager.setProperty("DbConnectionDefaultPool.username", props.getProperty("username"));

        PropertyManager.setProperty("DbConnectionDefaultPool.password", props.getProperty("password"));

        PropertyManager.setProperty("DbConnectionDefaultPool.minConnections", props.getProperty("minConnections"));

        PropertyManager.setProperty("DbConnectionDefaultPool.maxConnections", props.getProperty("maxConnections"));

        PropertyManager.setProperty("DbConnectionDefaultPool.logPath", props.getProperty("logPath"));

        PropertyManager.setProperty("DbConnectionDefaultPool.connectionTimeout", props.getProperty("connectionTimeout"));

    }

    private class ConnectionPool implements Runnable {

        private Thread runner;

        private Connection[] connPool;

        private int[] connStatus;

        private long[] connLockTime, connCreateDate;

        private String[] connID;

        private String dbDriver, dbServer, dbLogin, dbPassword, logFileString;

        private int currConnections, connLast, minConns, maxConns, maxConnMSec;

        //available: set to false on destroy, checked by getConnection()

        private boolean available=true;

        private PrintWriter log;

        private SQLWarning currSQLWarning;

        private String pid;

        /**

         * Creates a new Connection Broker<br>

         * dbDriver:        JDBC driver. e.g. 'oracle.jdbc.driver.OracleDriver'<br>

         * dbServer:        JDBC connect string. e.g. 'jdbc:oracle:thin:@203.92.21.109:1526:orcl'<br>

         * dbLogin:         Database login name.  e.g. 'Scott'<br>

         * dbPassword:      Database password.    e.g. 'Tiger'<br>

         * minConns:        Minimum number of connections to start with.<br>

         * maxConns:        Maximum number of connections in dynamic pool.<br>

         * logFileString:   Absolute path name for log file. e.g. 'c:\temp\mylog.log' <br>

         * maxConnTime:     Time in days between connection resets. (Reset does a basic cleanup)<br>

         */

        public ConnectionPool (String dbDriver, String dbServer, String dbLogin,

                String dbPassword, int minConns, int maxConns,

                    String logFileString, double maxConnTime) throws IOException

        {

            connPool = new Connection[maxConns];

            connStatus = new int[maxConns];

            connLockTime = new long[maxConns];

            connCreateDate = new long[maxConns];

            connID = new String[maxConns];

            currConnections = minConns;

            this.maxConns = maxConns;

            this.dbDriver = dbDriver;

            this.dbServer = dbServer;

            this.dbLogin = dbLogin;

            this.dbPassword = dbPassword;

            this.logFileString = logFileString;

            maxConnMSec = (int)(maxConnTime * 86400000.0);  //86400 sec/day

            if(maxConnMSec < 30000) {  // Recycle no less than 30 seconds.

                maxConnMSec = 30000;

            }

            try {

                log = new PrintWriter(new FileOutputStream(logFileString),true);

                // Can't open the requested file. Open the default file.

            }

            catch (IOException e1) {

                System.err.println("Warning: DbConnectionDefaultPool could not open \""

                    + logFileString + "\" to write log to. Make sure that your Java " +

                    "process has permission to write to the file and that the directory exists."

                );

                try {

                    log = new PrintWriter(new FileOutputStream("DCB_" +

                        System.currentTimeMillis() + ".log"), true

                    );

                }

                catch (IOException e2) {

                    throw new IOException("Can't open any log file");

                }

            }

            // Write the pid file (used to clean up dead/broken connection)

            SimpleDateFormat formatter

                = new SimpleDateFormat ("yyyy.MM.dd G 'at' hh:mm:ss a zzz");

            java.util.Date nowc = new java.util.Date();

            pid = formatter.format(nowc);

            BufferedWriter pidout = new BufferedWriter(new

                                    FileWriter(logFileString + "pid"));

            pidout.write(pid);

            pidout.close();

            log.println("Starting ConnectionPool:");

            log.println("dbDriver = " + dbDriver);

            log.println("dbServer = " + dbServer);

            log.println("dbLogin = " + dbLogin);

            log.println("log file = " + logFileString);

            log.println("minconnections = " + minConns);

            log.println("maxconnections = " + maxConns);

            log.println("Total refresh interval = " + maxConnTime + " days");

            log.println("-----------------------------------------");

            // Initialize the pool of connections with the mininum connections:

            // Problems creating connections may be caused during reboot when the

            //    servlet is started before the database is ready.  Handle this

            //    by waiting and trying again.  The loop allows 5 minutes for

            //    db reboot.

            boolean connectionsSucceeded=false;

            int dbLoop=20;

            try {

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

                    try {

                        for(int j=0; j < currConnections; j++) {

                            log.println("Create Conn "+j);

                            createConn(j);

                        }

                        connectionsSucceeded=true;

                        break;

                    }

                    catch (SQLException e){

                        log.println("--->Attempt (" + String.valueOf(i) +

                                " of " + String.valueOf(dbLoop) +

                                ") failed to create new connections set at startup: ");

                        log.println("    " + e);

                        log.println("    Will try again in 15 seconds...");

                        try { Thread.sleep(15000); }

                        catch(InterruptedException e1) {}

                    }

                }

                if(!connectionsSucceeded) { // All attempts at connecting to db exhausted

                    log.println("\r\nAll attempts at connecting to Database exhausted");

                    throw new IOException();

                }

            }

            catch (Exception e) {

                throw new IOException();

            }

            // Fire up the background housekeeping thread

            runner = new Thread(this);

            runner.start();

        } //End ConnectionPool()


一个连接池的例子（来自JIVE）（2）

作者：sonymusic

email: sonymusic@china.com

日期：2001-5-17 11:33:47

//文件：DbConnectionDefaultPool.java的第二部分

        /**

         * Housekeeping thread.  Runs in the background with low CPU overhead.

         * Connections are checked for warnings and closure and are periodically

         * restarted.

         * This thread is a catchall for corrupted

         * connections and prevents the buildup of open cursors. (Open cursors

         * result when the application fails to close a Statement).

         * This method acts as fault tolerance for bad connection/statement programming.

         */

        public void run() {

            boolean forever = true;

            Statement stmt=null;

            String currCatalog=null;

            while(forever) {

                // Make sure the log file is the one this instance opened

                // If not, clean it up!

                try {

                    BufferedReader in = new BufferedReader(new

                        FileReader(logFileString + "pid"));

                    String curr_pid = in.readLine();

                    if(curr_pid.equals(pid)) {

                        //log.println("They match = " + curr_pid);

                    }

                    else {

                        //log.println("No match = " + curr_pid);

                        log.close();

                        // Close all connections silently - they are definitely dead.

                        for(int i=0; i < currConnections; i++) {

                            try {

                                connPool[i].close();

                            }

                            catch (SQLException e1) {} // ignore

                        }

                        // Returning from the run() method kills the thread

                        return;

                    }

                    in.close();

                }

                catch (IOException e1) {

                    log.println("Can't read the file for pid info: " +

                        logFileString + "pid");

                }

                // Get any Warnings on connections and print to event file

                for(int i=0; i < currConnections; i++) {

                    try {

                        currSQLWarning = connPool[i].getWarnings();

                        if(currSQLWarning != null) {

                            log.println("Warnings on connection " +

                                String.valueOf(i) + " " + currSQLWarning);

                            connPool[i].clearWarnings();

                        }

                    }

                    catch(SQLException e) {

                        log.println("Cannot access Warnings: " + e);

                    }

                }

                for(int i=0; i < currConnections; i++) { // Do for each connection

                    long age = System.currentTimeMillis() - connCreateDate[i];

                    synchronized(connStatus) {

                        if(connStatus[i] > 0) { // In use, catch it next time!

                            continue;

                        }

                        connStatus[i] = 2; // Take offline (2 indicates housekeeping lock)

                    }

                    try {  // Test the connection with createStatement call

                        if(age > maxConnMSec) {  // Force a reset at the max conn time

                            throw new SQLException();

                        }

                        stmt = connPool[i].createStatement();

                        connStatus[i] = 0;  // Connection is O.K.

                        //log.println("Connection confirmed for conn = " +

                        //             String.valueOf(i));

                        // Some DBs return an object even if DB is shut down

                        if(connPool[i].isClosed()) {

                            throw new SQLException();

                        }

                        // Connection has a problem, restart it

                    }

                    catch(SQLException e) {

                        try {

                            log.println(new Date().toString() +

                            " ***** Recycling connection " +

                            String.valueOf(i) + ":");

                            connPool[i].close();

                            createConn(i);

                        }

                        catch(SQLException e1) {

                            log.println("Failed: " + e1);

                            connStatus[i] = 0;  // Can't open, try again next time

                        }

                    }

                    finally {

                        try {

                            if(stmt != null) {

                                stmt.close();

                            }

                        }

                        catch(SQLException e1){};

                    }

                }

                try {

                    Thread.sleep(20000);

                }  // Wait 20 seconds for next cycle

                catch(InterruptedException e) {

                    // Returning from the run method sets the internal

                    // flag referenced by Thread.isAlive() to false.

                    // This is required because we don't use stop() to

                    // shutdown this thread.

                    return;

                }

            }

        } // End run

        /**

         * This method hands out the connections in round-robin order.

         * This prevents a faulty connection from locking

         * up an application entirely.  A browser 'refresh' will

         * get the next connection while the faulty

         * connection is cleaned up by the housekeeping thread.

         *

         * If the min number of threads are ever exhausted, new

         * threads are added up the the max thread count.

         * Finally, if all threads are in use, this method waits

         * 2 seconds and tries again, up to ten times.  After that, it

         * returns a null.

         */

        public Connection getConnection() {

            Connection conn=null;

            if(available){

                boolean gotOne = false;

                for(int outerloop=1; outerloop<=10; outerloop++) {

                    try  {

                        int loop=0;

                        int roundRobin = connLast + 1;

                        if(roundRobin >= currConnections) roundRobin=0;

                        do {

                            synchronized(connStatus) {

                                if((connStatus[roundRobin] < 1) &&

                                        (! connPool[roundRobin].isClosed()))

                                {

                                    conn = connPool[roundRobin];

                                    connStatus[roundRobin]=1;

                                    connLockTime[roundRobin] =

                                        System.currentTimeMillis();

                                    connLast = roundRobin;

                                    gotOne = true;

                                    break;

                                }

                                else {

                                    loop++;

                                    roundRobin++;

                                    if(roundRobin >= currConnections) roundRobin=0;

                                }

                            }

                        }

                        while((gotOne==false)&&(loop < currConnections));

                    }

                    catch (SQLException e1) {}

                    if(gotOne) {

                        break;

                    }

                    else {

                        synchronized(this) {  // Add new connections to the pool

                            if(currConnections < maxConns) {

                                try {

                                    createConn(currConnections);

                                    currConnections++;

                                }

                                catch(SQLException e) {

                                    log.println("Unable to create new connection: " + e);

                                }

                            }

                        }

                        try { Thread.sleep(2000); }

                        catch(InterruptedException e) {}

                        log.println("-----> Connections Exhausted!  Will wait and try " +

                            "again in loop " +

                            String.valueOf(outerloop));

                    }

                } // End of try 10 times loop

            }

            else {

                log.println("Unsuccessful getConnection() request during destroy()");

            } // End if(available)

            return conn;

        }

        /**

         * Returns the local JDBC ID for a connection.

         */

        public int idOfConnection(Connection conn) {

            int match;

            String tag;

            try {

                tag = conn.toString();

            }

            catch (NullPointerException e1) {

                tag = "none";

            }

            match=-1;

            for(int i=0; i< currConnections; i++) {

                if(connID[i].equals(tag)) {

                    match = i;

                    break;

                }

            }

            return match;

        }

        /**

         * Frees a connection.  Replaces connection back into the main pool for

         * reuse.

         */

        public String freeConnection(Connection conn) {

            String res="";

            int thisconn = idOfConnection(conn);

            if(thisconn >= 0) {

                connStatus[thisconn]=0;

                res = "freed " + conn.toString();

                //log.println("Freed connection " + String.valueOf(thisconn) +

                //            " normal exit: ");

            }

            else {

                log.println("----> Could not free connection!!!");

            }

            return res;

        }


一个连接池的例子（来自JIVE）（3）

作者：sonymusic

email: sonymusic@china.com

日期：2001-5-17 11:34:25

//文件：DbConnectionDefaultPool.java的第三部分

        /**

         * Returns the age of a connection -- the time since it was handed out to

         * an application.

         */

        public long getAge(Connection conn) { // Returns the age of the connection in millisec.

            int thisconn = idOfConnection(conn);

            return System.currentTimeMillis() - connLockTime[thisconn];

        }

        private void createConn(int i) throws SQLException {

             Date now = new Date();

             try {

                Class.forName (dbDriver);

                Properties dbProp = new Properties();

                //log.println("Creating.....");

                dbProp.put("user", dbLogin);

                dbProp.put("password", dbPassword);

                dbProp.put("characterEncoding","gb2112");

                //dbProp.put("useUnicode", "true");

                connPool[i] = DriverManager.getConnection

                          (dbServer,dbProp);

                //log.println("Created Ok...");

                connStatus[i]=0;

                connID[i]=connPool[i].toString();

                connLockTime[i]=0;

                connCreateDate[i] =  now.getTime();

            }

            catch (ClassNotFoundException e2) {}

            log.println(now.toString() + "  Opening connection " + String.valueOf(i) +

                    " " + connPool[i].toString() + ":");

        }

        /**

         * Shuts down the housekeeping thread and closes all connections

         * in the pool. Call this method from the destroy() method of the servlet.

         */

        /**

         * Multi-phase shutdown.  having following sequence:

         * <OL>

         * <LI><code>getConnection()</code> will refuse to return connections.

         * <LI>The housekeeping thread is shut down.<br>

         *    Up to the time of <code>millis</code> milliseconds after shutdown of

         *    the housekeeping thread, <code>freeConnection()</code> can still be

         *    called to return used connections.

         * <LI>After <code>millis</code> milliseconds after the shutdown of the

         *    housekeeping thread, all connections in the pool are closed.

         * <LI>If any connections were in use while being closed then a

         *    <code>SQLException</code> is thrown.

         * <LI>The log is closed.

         * </OL><br>

         * Call this method from a servlet destroy() method.

         *

         * @param      millis   the time to wait in milliseconds.

         * @exception  SQLException if connections were in use after

         * <code>millis</code>.

         */

        public void destroy(int millis) throws SQLException {

            // Checking for invalid negative arguments is not necessary,

            // Thread.join() does this already in runner.join().

            // Stop issuing connections

            available=false;

            // Shut down the background housekeeping thread

            runner.interrupt();

            // Wait until the housekeeping thread has died.

            try { runner.join(millis); }

            catch(InterruptedException e){} // ignore

            // The housekeeping thread could still be running

            // (e.g. if millis is too small). This case is ignored.

            // At worst, this method will throw an exception with the

            // clear indication that the timeout was too short.

            long startTime=System.currentTimeMillis();

            // Wait for freeConnection() to return any connections

            // that are still used at this time.

            int useCount;

            while((useCount=getUseCount())>0 && System.currentTimeMillis() - startTime <=  millis) {

                try { Thread.sleep(500); }

                catch(InterruptedException e) {} // ignore

            }

            // Close all connections, whether safe or not

            for(int i=0; i < currConnections; i++) {

                try {

                    connPool[i].close();

                }

                catch (SQLException e1)

                {

                    log.println("Cannot close connections on Destroy");

                }

            }

            if(useCount > 0) {

                //bt-test successful

                String msg="Unsafe shutdown: Had to close "+useCount+

                    " active DB connections after "+millis+"ms";

                log.println(msg);

                // Close all open files

                log.close();

                // Throwing following Exception is essential because servlet authors

                // are likely to have their own error logging requirements.

                throw new SQLException(msg);

            }

            // Close all open files

            log.close();

        }//End destroy()

        /**

         * Less safe shutdown.  Uses default timeout value.

         * This method simply calls the <code>destroy()</code> method

         * with a <code>millis</code>

         * value of 10000 (10 seconds) and ignores <code>SQLException</code>

         * thrown by that method.

         * @see     #destroy(int)

         */

        public void destroy() {

            try {

                destroy(10000);

            }

            catch(SQLException e) {}

        }

        /**

         * Returns the number of connections in use.

         */

        // This method could be reduced to return a counter that is

        // maintained by all methods that update connStatus.

        // However, it is more efficient to do it this way because:

        // Updating the counter would put an additional burden on the most

        // frequently used methods; in comparison, this method is

        // rarely used (although essential).

        public int getUseCount() {

            int useCount=0;

            synchronized(connStatus) {

                for(int i=0; i < currConnections; i++) {

                    if(connStatus[i] > 0) { // In use

                        useCount++;

                    }

                }

            }

            return useCount;

        }//End getUseCount()

        /**

         * Returns the number of connections in the dynamic pool.

         */

        public int getSize() {

            return currConnections;

        }//End getSize()

    }

    /**

     * An implementation of the Connection interface that wraps an underlying

     * Connection object. It releases the connection back to a connection pool

     * when Connection.close() is called.

     */

    public class ConnectionWrapper  implements Connection {

        private Connection connection;

        private ConnectionPool connectionPool;

        public ConnectionWrapper(Connection connection, ConnectionPool connectionPool) {

            this.connection = connection;

            this.connectionPool = connectionPool;

        }

        /**

         * Instead of closing the underlying connection, we simply release

         * it back into the pool.

         */

        public void close() throws SQLException {

            connectionPool.freeConnection(this.connection);

            //Release object references. Any further method calls on the

            //connection will fail.

            connection = null;

            connectionPool = null;

        }

        public Statement createStatement() throws SQLException {

            return connection.createStatement();

        }

        public PreparedStatement prepareStatement(String sql) throws SQLException {

            return connection.prepareStatement(sql);

        }

        public CallableStatement prepareCall(String sql) throws SQLException {

            return connection.prepareCall(sql);

        }

        public String nativeSQL(String sql) throws SQLException {

            return connection.nativeSQL(sql);

        }

        public void setAutoCommit(boolean autoCommit) throws SQLException {

            connection.setAutoCommit(autoCommit);

        }

        public boolean getAutoCommit() throws SQLException {

            return connection.getAutoCommit();

        }

        public void commit() throws SQLException {

            connection.commit();

        }

        public void rollback() throws SQLException {

            connection.rollback();

        }

        public boolean isClosed() throws SQLException {

            return connection.isClosed();

        }

        public DatabaseMetaData getMetaData() throws SQLException {

            return connection.getMetaData();

        }

        public void setReadOnly(boolean readOnly) throws SQLException {

            connection.setReadOnly(readOnly);

        }

        public boolean isReadOnly() throws SQLException {

            return connection.isReadOnly();

        }

        public void setCatalog(String catalog) throws SQLException {

            connection.setCatalog(catalog);

        }

        public String getCatalog() throws SQLException {

            return connection.getCatalog();

        }

        public void setTransactionIsolation(int level) throws SQLException {

            connection.setTransactionIsolation(level);

        }

        public int getTransactionIsolation() throws SQLException {

            return connection.getTransactionIsolation();

        }

        public SQLWarning getWarnings() throws SQLException {

            return connection.getWarnings();

        }

        public void clearWarnings() throws SQLException {

            connection.clearWarnings();

        }

        public Statement createStatement(int resultSetType, int resultSetConcurrency)

                throws SQLException

        {

            return connection.createStatement(resultSetType, resultSetConcurrency);

        }

        public PreparedStatement prepareStatement(String sql, int resultSetType,

                int resultSetConcurrency) throws SQLException

        {

            return connection.prepareStatement(sql, resultSetType, resultSetConcurrency);

        }

        public CallableStatement prepareCall(String sql, int resultSetType,

                int resultSetConcurrency) throws SQLException

        {

            return prepareCall(sql, resultSetType, resultSetConcurrency);

        }

        public Map getTypeMap() throws SQLException {

            return connection.getTypeMap();

        }

        public void setTypeMap(Map map) throws SQLException {

            connection.setTypeMap(map);

        }

    }

}


一个连接池的例子（来自JIVE）（4）

作者：sonymusic

email: sonymusic@china.com

日期：2001-5-17 11:35:03

//文件：DbConnectionManager.java

package com.qingtuo.db.pool;

import java.sql.*;

import java.io.*;

import java.util.*;

/**

 * Central manager of database connections.

 */

public class DbConnectionManager {

    private static DbConnectionProvider connectionProvider;

    private static Object providerLock = new Object();

    /**

     * Returns a database connection from the currently active connection

     * provider.

     */

    public static Connection getConnection() {

        if (connectionProvider == null) {

            synchronized (providerLock) {

                if (connectionProvider == null) {

                    //Create the connection provider -- for now, this is hardcoded. For

                    //the next beta, I'll change this to load up the provider dynamically.

                    connectionProvider = new DbConnectionDefaultPool();

                    connectionProvider.start();

                }

            }

        }

        Connection con = connectionProvider.getConnection();

        if (con == null) {

            System.err.println("WARNING: DbConnectionManager.getConnection() failed to obtain a connection.");

        }

        return con;

    }

    /**

     * Returns the current connection provider. The only case in which this

     * method should be called is if more information about the current

     * connection provider is needed. Database connections should always be

     * obtained by calling the getConnection method of this class.

     */

    public static DbConnectionProvider getDbConnectionProvider() {

        return connectionProvider;

    }

    /**

     * Sets the connection provider. The old provider (if it exists) is shut

     * down before the new one is started. A connection provider <b>should

     * not</b> be started before being passed to the connection manager.

     */

    public static void setDbConnectionProvider(DbConnectionProvider provider) {

        synchronized (providerLock) {

            if (connectionProvider != null) {

                connectionProvider.destroy();

                connectionProvider = null;

            }

            connectionProvider = provider;

            provider.start();

        }

    }

}


一个连接池的例子（来自JIVE）（5）

作者：sonymusic

email: sonymusic@china.com

日期：2001-5-17 11:36:03

//文件：DbConnectionProvider.java

package com.qingtuo.db.pool;

import java.sql.*;

import java.util.*;

public abstract class DbConnectionProvider {

    /** Dummy values. Override in subclasses. **/

    private static final String NAME = "";

    private static final String DESCRIPTION = "";

    private static final String AUTHOR = "";

    private static final int MAJOR_VERSION = 0;

    private static final int MINOR_VERSION = 0;

    private static final boolean POOLED = false;

    /**

     * Returns the name of the connection provider.

     */

    public String getName() {

        return NAME;

    }

    /**

     * Returns a description of the connection provider.

     */

    public String getDescription() {

        return DESCRIPTION;

    }

    /**

     * Returns the author of the connection provider.

     */

    public String getAuthor() {

        return AUTHOR;

    }

    /**

     * Returns the major version of the connection provider, i.e. the 1 in 1.0.

     */

    public int getMajorVersion() {

        return MAJOR_VERSION;

    }

    public int getMinorVersion() {

        return MINOR_VERSION;

    }

    /**

     * Returns true if this connection provider provides connections out

     * of a connection pool.

     */

    public boolean isPooled() {

        return POOLED;

    }

    /**

     * Returns a database connection. When a Jive component is done with a

     * connection, it will call the close method of that connection. Therefore,

     * connection pools with special release methods are not directly

     * supported by the connection provider infrastructure. Instead, connections

     * from those pools should be wrapped such that calling the close method

     * on the wrapper class will release the connection from the pool.

     */

    public abstract Connection getConnection();

    /**

     * Starts the connection provider. For some connection providers, this

     * will be a no-op. However, connection provider users should always call

     * this method to make sure the connection provider is started.

     */

    protected abstract void start();

    /**

     * This method should be called whenever properties have been changed so

     * that the changes will take effect.

     */

    protected abstract void restart();

    /**

     * Tells the connection provider to destroy itself. For many connection

     * providers, this will essentially result in a no-op. However,

     * connection provider users should always call this method when changing

     * from one connection provider to another to ensure that there are no

     * dangling database connections.

     */

    protected abstract void destroy();

    /**

     * Returns the value of a property of the connection provider.

     *

     * @param name the name of the property.

     * @returns the value of the property.

     */

    public abstract String getProperty(String name);

    /**

     * Returns the description of a property of the connection provider.

     *

     * @param name the name of the property.

     * @return the description of the property.

     */

    public abstract String getPropertyDescription(String name);

    /**

     * Returns an enumeration of the property names for the connection provider.

     */

    public abstract Enumeration propertyNames();

    /**

     * Sets a property of the connection provider. Each provider has a set number

     * of properties that are determined by the author. Trying to set a non-

     * existant property will result in an IllegalArgumentException.

     *

     * @param name the name of the property to set.

     * @param value the new value for the property.

     */

    public abstract void setProperty(String name, String value);

}


一个连接池的例子（来自JIVE）（6）

作者：sonymusic

email: sonymusic@china.com

日期：2001-5-17 11:37:22

//文件：PropertyManager.java

//这个类其实没什么用了，可以去掉，但需要去掉前面几个类中对这个类的引用。

package com.qingtuo.db.pool;

import java.util.*;

import java.io.*;

/**

 * Manages properties for the entire Jive system. Properties are merely

 * pieces of information that need to be saved in between server restarts.

 * <p>

 * At the moment, properties are stored in a Java Properties file. In a version

 * of Jive coming soon, the properties file format will move to XML. XML

 * properties will allow hierarchical property structures which may mean the

 * API of this class will have to change.

 * <p>

 * Jive properties are only meant to be set and retrevied by core Jive classes.

 * Therefore, skin writers should probably ignore this class.

 * <p>

 * This class is implemented as a singleton since many classloaders seem to

 * take issue with doing classpath resource loading from a static context.

 */

public class PropertyManager {

    private static PropertyManager manager = null;

    private static Object managerLock = new Object();

    private static String propsName = "/pcc_2000.properties";

    /**

     * Returns a Jive property

     *

     * @param name the name of the property to return.

     * @returns the property value specified by name.

     */

    public static String getProperty(String name) {

        if (manager == null) {

            synchronized(managerLock) {

                if (manager == null) {

                    String sysname=System.getProperty("os.name").toUpperCase();

                    if(sysname.indexOf("WIN")!=-1){

                        propsName=propsName2000;

                    }

                    else{

                        propsName=propsNameLinux;

                    }

                    manager = new PropertyManager(propsName);

                }

            }

        }

        return manager.getProp(name);

    }

    /**

     * Sets a Jive property.

     *

     * @param name the name of the property being set.

     * @param value the value of the property being set.

     */

    public static void setProperty(String name, String value) {

        if (manager == null) {

            synchronized(managerLock) {

                if (manager == null) {

                    manager = new PropertyManager(propsName);

                }

            }

        }

        manager.setProp(name, value);

    }

    /**

     * Returns true if the properties are readable. This method is mainly

     * valuable at setup time to ensure that the properties file is setup

     * correctly.

     */

    public static boolean propertyFileIsReadable() {

        if (manager == null) {

            synchronized(managerLock) {

                if (manager == null) {

                    manager = new PropertyManager(propsName);

                }

            }

        }

        return manager.propFileIsReadable();

    }

    /**

     * Returns true if the properties are writable. This method is mainly

     * valuable at setup time to ensure that the properties file is setup

     * correctly.

     */

    public static boolean propertyFileIsWritable() {

        if (manager == null) {

            synchronized(managerLock) {

                if (manager == null) {

                    manager = new PropertyManager(propsName);

                }

            }

        }

        return manager.propFileIsWritable();

    }

    /**

     * Returns true if the jive.properties file exists where the path property

     * purports that it does.

     */

    public static boolean propertyFileExists() {

        if (manager == null) {

            synchronized(managerLock) {

                if (manager == null) {

                    manager = new PropertyManager(propsName);

                }

            }

        }

        return manager.propFileExists();

    }

    private Properties properties = null;

    private Object propertiesLock = new Object();

    private String resourceURI;

    /**

     * Singleton access only.

     */

    private PropertyManager(String resourceURI) {

        this.resourceURI = resourceURI;

    }

    /**

     * Gets a Jive property. Jive properties are stored in jive.properties.

     * The properties file should be accesible from the classpath. Additionally,

     * it should have a path field that gives the full path to where the

     * file is located. Getting properties is a fast operation.

     */

    public String getProp(String name) {

        //If properties aren't loaded yet. We also need to make this thread

        //safe, so synchronize...

        if (properties == null) {

            synchronized(propertiesLock) {

                //Need an additional check

                if (properties == null) {

                    loadProps();

                }

            }

        }

        return properties.getProperty(name);

    }

    /**

     * Sets a Jive property. Because the properties must be saved to disk

     * every time a property is set, property setting is relatively slow.

     */

    public void setProp(String name, String value) {

        //Only one thread should be writing to the file system at once.

        synchronized (propertiesLock) {

            //Create the properties object if necessary.

            if (properties == null) {

                loadProps();

            }

            properties.setProperty(name, value);

            //Now, save the properties to disk. In order for this to work, the user

            //needs to have set the path field in the properties file. Trim

            //the String to make sure there are no extra spaces.

            String path = properties.getProperty("path").trim();

            OutputStream out = null;

            try {

                out = new FileOutputStream(path);

                properties.store(out, "jive.properties -- " + (new java.util.Date()));

            }

            catch (Exception ioe) {

                System.err.println("There was an error writing jive.properties to " + path + ". " +

                    "Ensure that the path exists and that the Jive process has permission " +

                    "to write to it -- " + ioe);

                ioe.printStackTrace();

            }

            finally {

                try {

                    out.close();

                } catch (Exception e) { }

            }

        }

    }

    /**

     * Loads Jive properties from the disk.

     */

    private void loadProps() {

        properties = new Properties();

        InputStream in = null;

        try {

            in = getClass().getResourceAsStream(resourceURI);

            properties.load(in);

        }

        catch (IOException ioe) {

            System.err.println("Error reading Pcc properties" + ioe);

            ioe.printStackTrace();

        }

        finally {

            try {

                in.close();

            } catch (Exception e) { }

        }

    }

    /**

     * Returns true if the properties are readable. This method is mainly

     * valuable at setup time to ensure that the properties file is setup

     * correctly.

     */

    public boolean propFileIsReadable() {

        try {

            InputStream in = getClass().getResourceAsStream(resourceURI);

            return true;

        }

        catch (Exception e) {

            return false;

        }

    }

    /**

     * Returns true if the jive.properties file exists where the path property

     * purports that it does.

     */

    public boolean propFileExists() {

        String path = getProp("path");

        File file = new File(path);

        if (file.isFile()) {

            return true;

        }

        else {

            return false;

        }

    }

    /**

     * Returns true if the properties are writable. This method is mainly

     * valuable at setup time to ensure that the properties file is setup

     * correctly.

     */

    public boolean propFileIsWritable() {

        String path = getProp("path");

        File file = new File(path);

        if (file.isFile()) {

            //See if we can write to the file

            if (file.canWrite()) {

                return true;

            }

            else {

                return false;

            }

        }

        else {

            return false;

        }

    }

    private static String propsName2000 = "/pcc_2000.properties";

    private static String propsNameLinux = "/pcc_linux.properties";

}


一个连接池的例子（说明）

作者：sonymusic

email: sonymusic@china.com

日期：2001-5-17 11:45:45

这个连接池是直接从JIVE中取出来的，进行了一下修改，使得连接参数直接在程序中设定而不是从属性文件中读取。

[b]用法：[/b]

先设定自己的连接参数，在DbConnectionDefaultPool.java文件的loadProperties方法中。注意你也需要设定连接池的log文件的存放位置。

String driver="org.gjt.mm.mysql.Driver";//这是使用的JDBC驱动

String server="jdbc:mysql://192.100.100.1/qingtuo";//使用的URL

//String server="jdbc:mysql://192.168.0.1/qingtuo";

String username="qingtuo";

String password="qingtuo";

String minConnections="3";//最小连接数

String maxConnections="20";//最大连接数

String logPath="c:\\temp\\qingtuoDbLog.log";//日志文件位置

//String logPath="/tmp/qingtuoDbLog.log";

String connectionTimeout="0.5";//定时清除无用连接间隔（以天为单位）

[b]然后[/b]

在你的程序中只需要将这个包com.qingtuo.db.pool import进来，再向下面这样用就行了。

    Connection con = null;

    PreparedStatement pstmt = null;

    ResultSet rs=null;

    try {

        con = DbConnectionManager.getConnection();

        //这里写你的SQL语句

    }

    catch (SQLException sqle) {

        throw sqle;

    }

    finally {

        try {

            pstmt.close();

        }

        catch (Exception e) {

            e.printStackTrace();

        }

        try {

            con.close();

        }

        catch (Exception e) {

            e.printStackTrace();

        }

    }

[b]优点[/b]

虽然很多应用服务器也有连接池，但这样做的好处是，更加通用一些。


一个区分Bean的作用域的例子，即jspuseBean scope=xx的scope属性

作者：yukuang

email: yukuang@163.net

日期：7/27/2001 4:18:43 PM

以前很不明白Bean的scope属性的含义，今天做了一个实验，终于差不多搞明白了。贴出来与大家共享。

例子很简单，Bean的功能为取当前时间，其代码为：

Common.java:

--------------------

package XX;

import java.util.Date;

import java.text.*;

public class Common

{

    Date d=new Date();

    public String now(String s)

    {

        SimpleDateFormat formatter = new SimpleDateFormat(s);

        return formatter.format(d);

    }

}

测试页面的代码为：

date.jsp

-----------------------------------------------------------

<jsp:useBean id="cm" scope="XX" class="XX.Common"/>

<%=cm.now("yyyy-MM-dd HH:mm:ss")  %>

测试结果：

1、当scope=application时，我们浏览date.jsp，这时显示出了系统时间。可是不管我们怎么刷新，重新打开一次浏览器，甚至换台机器，它显示的时间始终不变，都是当初的时间(即bean刚创建时得到的系统时间)，因为scope=application，所以Bean的实例在内存中只有一份，此时只要不重新启动WEB服务，输出不会变化。

2、当scope=session时，浏览date.jsp，刷新时显示也不会变化。可是当我们重新打开一浏览器，即一个新的session，系统便再次创建Bean的实例，取得当前系统时间，这时将得到正确的时间。同样，再次刷新新打开的页面(新的session)，显示也不会变化。

3、当scope=page/request(它们的区别只在于包含静态文件时，此处无区别)时，不断刷新页面将不断得到当前系统时间。

以上可以看出不同的scope，Bean有不同的作用域。使用时一定注意，不要将得到经常变动的Bean的scope设为application或session，那将得到不正确的结果.


一个最简单的 JavaBeanMaker(原创)

作者：jeru

email: jeru@163.net

日期：7/3/2001 10:38:00 AM

只实现最简单功能,自动生成setter,getter当bean中属性较多时可以节省时间:

Usage: java JavaBeanMaker aaa.txt bbb

1. aaa.txt is the text file in following style,you can get a example--jeru.txt in attachment

   ======================

   int id

   String name

   int age

   ======================

2  bbb is the file name of your javabean without .java,so if you want a Test.java

   just type "Java JavaBeanMaker aaa.txt Test"

================= jeru.txt ==========================

int id

String name

int age

================= JavaBeanMaker.java ================

import java.io.*;

import java.util.*;

public class JavaBeanMaker {

  public static void main(String[] args) {

    System.out.println("Reading datas......");

      try {

        // read properties of source text file

    int i = 0;

    String record = new String();

    Vector property = new Vector();

        RandomAccessFile source = new RandomAccessFile(args[0],"r");

        while ((record = source.readLine()) != null) {

          i ++;

          property.addElement(record);

        }

        source.close();

    RandomAccessFile destine = new RandomAccessFile(args[1],"rw");

        String content = "// This JavaBean is make by Jeru's JavaBeanMaker" + "\r\n\r\n";

        content += "public class " + args[1] + " {" + "\r\n\r\n";

    String[] tmp = new String[3];

    for (i=0; i<property.size(); i++) {

      String str = (String)property.elementAt(i);

      //System.out.println("Value " + i + ":" + str);

      StringTokenizer tokens = new StringTokenizer(str);

          for (int j=0; j<=1; j++) {

        tmp[j] = tokens.nextToken();

        System.out.println("token: " + tmp[j]);

      }

         // capital tmp[1]

      tmp[2] = tmp[1].substring(0,1).toUpperCase() + tmp[1].substring(1);

      System.out.println(tmp[2]);

      content += "  " + tmp[0] + " " + tmp[1] + ";" + "\r\n";

      content += "  " + "public void set" + tmp[2] + "(" + tmp[1] + ") {" + "\r\n";

      content += "    " + "this." + tmp[1] + " = " + tmp[1] + ";" + "\r\n";

      content += "  " + "}" + "\r\n";

      content += "  " + "public "  + tmp[0] + " get" + tmp[2] + "() {" + "\r\n";

      content += "    " + "return " + tmp[1] + ";" + "\r\n";

          content += "  " + "}" + "\r\n\r\n";

       }

       System.out.println("the last content is " + content);

       content += "}";

       destine.writeBytes(content);

       destine.close();

     } catch(Exception e){ e.getMessage(); }

  }

}


一些非常有用的JAVA常用方法( 2)

作者：netkey

日期：2001-6-2 17:07:33

int cInt(float stsid){

     int cInt = 0;

     try{

      int stsid1 = 0;

   if(stsid > 0){

      if((stsid - (int)stsid > 0) && (stsid - (int)stsid != 0.5))

         stsid1 = (int)(stsid + 0.5);

      if(stsid - (int)stsid == 0.5){

      if(((int)(stsid-0.5))%2 == 0)

         stsid1 = (int)(stsid - 0.5);

      else

            stsid1 = (int)(stsid + 0.5);

      }

            if(stsid - (int)stsid ==0)

      stsid1 = (int)stsid;

   }else{

      stsid = -stsid;

   if((stsid - (int)stsid > 0) && (stsid - (int)stsid != 0.5))

         stsid1 = - (int)(stsid + 0.5);

      if(stsid - (int)stsid == 0.5){

      if(((int)(stsid-0.5))%2 == 0)

         stsid1 = - (int)(stsid - 0.5);

      else

            stsid1 = - (int)(stsid + 0.5);

      }

            if(stsid - (int)stsid ==0)

      stsid1 = - (int)stsid;

   }

   cInt = stsid1;

  }catch(Exception e){}

  return cInt;

  }

 long cLng(float numb){

     int nu = 0;

     long cLng = 0;

     try{

        nu = cInt(numb);

        cLng = (long) nu;

     }catch(Exception e){}

     return cLng;

 }

 float dealFloat(String s){

    s=dealNull(s);

    if (s.equals(""))

 return 0;

    return Float.parseFloat(s);

}

String[] split(String source,String div){

      int arynum = 0,intIdx=0,intIdex=0,div_length = div.length();

      if(source.compareTo("")!=0){

        if(source.indexOf(div)!=-1){

         intIdx = source.indexOf(div);

         for(int intCount =1 ; ; intCount++){

           if(source.indexOf(div,intIdx+div_length)!=-1){

             intIdx= source.indexOf(div,intIdx+div_length);

             arynum = intCount;

             }

           else {arynum+=2;break;}

            }

       }else arynum =1;

      }else arynum = 0;

      intIdx=0;

      intIdex=0;

      String[] returnStr = new String[arynum];

      if(source.compareTo("")!=0){

       if(source.indexOf(div)!=-1){

        intIdx = (int)source.indexOf(div);

        returnStr[0]= (String)source.substring(0,intIdx);

        for(int intCount =1 ; ; intCount++){

           if(source.indexOf(div,intIdx+div_length)!=-1){

             intIdex=(int)source.indexOf(div,intIdx+div_length);

             returnStr[intCount] = (String)source.substring(intIdx+div_length,intIdex);

             intIdx = (int)source.indexOf(div,intIdx+div_length);

             }

           else {

                returnStr[intCount] = (String)source.substring(intIdx+div_length,source.length());

                break;

                }

            }

      }

      else {returnStr[0] = (String)source.substring(0,source.length());return returnStr;}

     }

     else {return returnStr;}

    return returnStr;

}

public class stc_RSYB_Name

  {

    public int pos = -1;

    public String NAME = "";

    public String host = "";

    public String home1 = "";

    public String home2 = "";

    public String home3 = "";

  }

  public class stc_SYB_INF

  {

    String reg_name  = "";

    String G_SYB = "";

    String R_SYB = "";

  }

file://输出空格

void showBlank(int num,JspWriter out){

try{

 for(int i=0;i<=num;i++)

  out.print("&nbsp;");

}

catch(Exception e){}

}

   String g_syb_mk(){

     String g_syb_mk = null;

     String NIS_M_O = "1???????????????????????????????????????????????????????????????";

  String LOCAL_M = "?1??????????????????????????????????????????????????????????????";

  String WINNT_DC = "????1???????????????????????????????????????????????????????????";

  String NIS_LOCAL = "11??????????????????????????????????????????????????????????????";

  int Ret = 0,RecCount = 0,K = 0;

  String Set_Ret = "ssy";

  String[] DOMEIN = null;

  String[] Group_SYB = null;

  int WK_SYB[];

  try{

    String sql = "select count(*) from 寁嶼婡 where 话兽庬暿 like '"+ NIS_M_O +"' or 话兽庬暿 like '" + LOCAL_M +"' or 话兽庬暿 like '"+WINNT_DC +"'";

    rs = stmt.executeQuery(sql);

    if(rs.next()){

      RecCount = rs.getInt(1);

   rs = null;

    }

    String sql2 = "select * from 寁嶼婡 where 话兽庬暿 like '"+ NIS_M_O +"' or 话兽庬暿 like '" + LOCAL_M +"' or 话兽庬暿 like '"+WINNT_DC +"'";

    while(rs.next()){

      int i = 0;

         DOMEIN[i] = rs.getString("霓也輺?);

   i++;

    }

    for(int j = 0 ; j < RecCount ; j++){

      String sql3 = "select * from 僌儖乕僾庬暿 where 霓也輺?= '" + DOMEIN[j] +"'";

   rs = stmt.executeQuery(sql3);

   if(rs.next()){

     K = K + 1;

     Group_SYB[K] = rs.getString("皋侔踢庬暿");

   }

    }

    for(int l = 0; l < 32; l++){

      for(int m = 0; m < K; m++){

     if(Group_SYB[m].substring(l,l+1).equals("1")){

       if(l==1){

      Set_Ret = "1";

    }else{

      Set_Ret = Set_Ret +  "1";

    }

    break;

     }else{

       if(m==K){

      if(l==1){

        Set_Ret = "0";

      }else{

        Set_Ret = Set_Ret + "0";

      }

    }

     }

   }

    }

    g_syb_mk = Set_Ret;

  }catch(Exception e) { }

   return  g_syb_mk;

  }

int reCount(String strtable,String Sel)

{

 String mySt="";

 int t = 0;

 ResultSet rsd=null;

 try

 {

  mySt= "select count(*) ads from " + strtable  + " " + Sel;

  rsd= conn.createStatement().executeQuery(mySt);

  if (rsd.next())

  {

   t=rsd.getInt("ads");

  }

 }

 catch(SQLException sqe)

 {

  System.out.println("ODBC Wrong :" + sqe +"+" +mySt);

 }

 rsd = null;

 return t;

}

int dCount(String duan,String strTable,String strSel)

{

 int t=0;

 ResultSet rss=null;

 try

 {

  String mySql="select count(*) ads from "  + strTable + " where " + strSel + " and "+ duan +" is not null ";

  rss= conn.createStatement().executeQuery(mySql);

  if (rss.next())

  {

   t=rss.getInt("ads");

  }

 }

 catch(Exception sqe)

 {

  System.out.println(sqe);

 }

 rss=null;

 return t;

}

String dLookup(String duan,String strTable,String strSel)

{

        String strReturn = null;

 ResultSet rss = null;

 try{

              String strsql = "select "+duan+" from "+strTable+" where "+ strSel;

       rss = conn.createStatement().executeQuery(strsql);

       if(rss.next()){

       strReturn = rss.getString(duan);

       }

 }catch(java.sql.SQLException sqle){

     System.out.println("dLookup_Exception:"+sqle);

 }

        rss = null;

 return strReturn;

}

String rTrim(String str){

     String rstr = str;

  if(str == null)

         {

    rstr = "";

  }

         else

         {

    if(str.length() == 0) rstr = "";

    if(str.length() > 0)

    {

      for(int i = str.length() ; i  >  0 ; i--)

             {

     if(str.substring(i-1,i).equals(" "))

       rstr = rstr.substring(0,i-1);

     else break;

   }

     }

  }

  return rstr;

   }

 void sybinf_set(stc_RSYB_Name in_data, stc_SYB_INF out_data) throws java.sql.SQLException{

    String NEW_SYB = "00000000000000000000000000000000";

    out_data.reg_name = in_data.NAME;

    String sql = "";

    String rdomein1 = "";

    String rdomein2 = "";

    String rdomein3 = "";

    ResultSet rsf = null;

    Statement stf = conn.createStatement();

    try {

      sql = "Select 皋侔踢庬暿 From 僌儖乕僾庬暿 Where 霓也輺?= '" + in_data.host + "'";

      rsf = conn.createStatement().executeQuery(sql);

      if (rsf.next()) {

        out_data.G_SYB = dealNull(rsf.getString("皋侔踢庬暿"));

      } else {

        out_data.G_SYB = "";

      }

    } catch(java.sql.SQLException sqle) {

      System.out.println("111111" + sqle);

    }

    try {

      sql = "Select 棙梡庬暿 From 棙梡庬暿 Where 霓也輺?= '" + in_data.home1 + "'";

      rsf = stf.executeQuery(sql);

      if (rsf.next())

        rdomein1 = dealNull(rsf.getString("棙梡庬暿"));

    } catch(java.sql.SQLException sqle) {

      System.out.println("22222" + sqle);

    }

    try {

      sql = "Select 棙梡庬暿 From 棙梡庬暿 Where 霓也輺?= '" + in_data.home2 + "'";

      rsf = stf.executeQuery(sql);

      if (rsf.next())

        rdomein2 = dealNull(rsf.getString("棙梡庬暿"));

    } catch(java.sql.SQLException sqle) {

      System.out.println("333333" + sqle);

    }

    try {

      sql = "Select 棙梡庬暿 From 棙梡庬暿 Where 霓也輺?= '" + in_data.home3 + "'";

      rsf = stf.executeQuery(sql);

      if (rsf.next())

        rdomein3 = dealNull(rsf.getString("棙梡庬暿"));

    } catch(java.sql.SQLException sqle) {

      System.out.println("444444" + sqle);

    }

    rsf = null;

    stf = null;

    String p = Char_OR(rdomein1, rdomein2);

    if (p.equals("")) p = NEW_SYB;

    String q = Char_OR(p, rdomein3);

    if (q.equals("")) q = NEW_SYB;

    out_data.R_SYB = q;

  }

  String Char_OR(String arg1, String arg2) {

    int len1 = -1, len2 = -1, max_len = -1, min_len = -1;

    String ret_sts = "";

    int long_f = -1;

    len1 = arg1.length();

    len2 = arg2.length();

    if (len1 == 0 && len2 > 0)

      return arg2;

    else if (len1 > 0 && len2 == 0)

      return arg1;

    else if (len1 == 0 && len2 == 0)

      return "";

    ret_sts = "";

    if (len1 > len2) {

      max_len = len1;

      min_len = len2;

      long_f = 1;

    } else if (len1 < len2) {

       max_len = len2;

       min_len = len1;

       long_f = 2;

    } else {

       max_len = len1;

       min_len = len2;

       long_f = 0;

    }

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

      if (i <= min_len) {

        if (arg1.substring(i-1, i).equals("1") || arg2.substring(i-1, i).equals("1"))

          ret_sts = ret_sts + "1";

        else

          ret_sts = ret_sts + "0";

      } else {

        if (long_f == 1)

          ret_sts = ret_sts + arg1.substring(i-1, i);

        else if (long_f == 2)

          ret_sts = ret_sts + arg2.substring(i-1, i);

      }

    }

    return ret_sts;

  }

%>

<%!

 void writeFile(String myString,String target_file){

     try {

   java.io.PrintWriter pw = new java.io.PrintWriter(new java.io.FileOutputStream(target_file));

   pw.println(myString);

   pw.close();

   myString="";

   } catch(java.io.IOException e) {

     System.out.println(e.getMessage());

 }

  }

 %>


一些非常有用的JAVA常用方法(1)

作者：netkey

日期：2001-6-2 17:06:13

  作者:xiaoyou

 出处: http://www.csdn.com

<%!

stc_RSYB_Name stcRSYName1[];

stc_SYB_INF stcSYBINF1[];

%>

<%/*

   String p = (String) session.getValue("access");

   if (p==null || !p.equals("1")) {

     response.sendRedirect("error.htm");

     return;

   }*/

%>

<%!

file://得到机器名字或IP

String get_Myhostname(javax.servlet.http.HttpServletRequest request){

  String myhostname = null;

    try{

      myhostname = request.getRemoteHost();

      myhostname = myhostname.toUpperCase();

    }catch(Exception e){}

  return myhostname;

}

file://处理空字符串

  String dealNull(String str) {

    String returnstr = null;

    if (str == null) returnstr = "";

    else returnstr = str;

    return returnstr;

  }

file://处理空对象

  Object dealNull(Object obj){

    Object returnstr = null;

    if (obj == null) returnstr = (Object)("");

    else returnstr = obj;

    return returnstr;

  }

  int dealEmpty(String s) {

    s = dealNull(s);

    if (s.equals("")) return 0;

    return Integer.parseInt(s);

  }

String replace(String str,String substr,String restr){

  String[] tmp = split(str,substr);

  String returnstr = null;

  if(tmp.length!=0) {

    returnstr = tmp[0];

    for(int i = 0 ; i < tmp.length - 1 ; i++)

      returnstr =dealNull(returnstr) + restr +tmp[i+1];

  }

  return dealNull(returnstr);

}

String htmlEncode(String txt){

  txt = replace(txt,"&","&amp;");

  txt = replace(txt,"&amp;amp;","&amp;");

  txt = replace(txt,"&amp;quot;","&quot;");

  txt = replace(txt,"\"","&quot;");

  txt = replace(txt,"&amp;lt;","&lt;");

  txt = replace(txt,"<","&lt;");

  txt = replace(txt,"&amp;gt;","&gt;");

  txt = replace(txt,">","&gt;");

  txt = replace(txt,"&amp;nbsp;","&nbsp;");

  txt = replace(txt," ","&nbsp;");

  return txt;

}

String unHtmlEncode(String txt){

  txt = replace(txt,"&amp;","&");

  txt = replace(txt,"&quot;","\"");

  txt = replace(txt,"&lt;","<");

  txt = replace(txt,"&gt;",">");

  txt = replace(txt,"&nbsp;"," ");

  return txt;

}

file://ALERT Function

  void sAlert(String title,String msg,String focus, JspWriter out){

   try {

      out.println("<script language='javascript'>");

      out.println("alert(\"" + title + "\" + \"\\r\\n\" + \"" + replace(replace(msg,"''","'"),"\"","\\\"") +"\");");

      out.println(focus);

      out.println("</script>");

   } catch(Exception e) { System.out.println(e);}

  }

file://CONFIRM Function

  void sConfirm(String title,String msg ,String location,JspWriter out){

    try{

        out.println("<script language='javascript'>");

  out.println("if(confirm(\"" + title + "\" + \"\\r\\n\" + \"" + replace(msg,"\"","\\\"") + "\")){" );

        out.println("var d = (new Date()).toString();");

  out.println(location + "+ d;");

        out.println("}");

        out.println("parent.document.form1.text1.select();");

        out.println("</script>");

   } catch (Exception e) { }

  }

file://得到字符串长度

  int getStringLength(String s) {

   int p = 0;

    try {

      int iLength = 0;

      int irealLength = 0;

      int j = 0;

      p = s.getBytes("Shift_JIS").length;

    } catch(java.io.UnsupportedEncodingException e) {}

    return p;

  }

 void formatString(String s,int b,JspWriter out){

    b--;

    String t = "";

    try {

        int leng = getStringLength(s);

        int j = 0;

 if (leng > b) {

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

            t = s.substring(0,i);

     if (t.getBytes("Shift_JIS").length <= b ) {

       j++;

     } else break;

   }

          file://j = j+1;

          file://System.out.println("b=" + b + "----" + "j=" + j);

          for(int i = 0;i < j; i++){

            if(s.substring(i,i+1).equals(" "))

              out.print("&nbsp;");

            if(!s.substring(i,i+1).equals(" "))

       out.print(htmlEncode(s.substring(i,i+1)));

           }

          if (s.substring(0,j).getBytes("Shift_JIS").length  == b){

            out.print("|");

          }else{

            out.print("&nbsp;|");

          }

        } else {

   for (int  i = 0; i < b - leng ; i++)

            t = t + "&nbsp;";

          for (int i = 0; i < s.length(); i++){

            if(s.substring(i,i+1).equals(" "))

              out.print("&nbsp;");

            if(!s.substring(i,i+1).equals(" "))

              out.print(htmlEncode(s.substring(i,i+1)));

          }

   out.print(htmlEncode(t + "|"));

       }

    } catch(Exception e){System.out.println("----" + e);}

  }

file://判断是否为数字

boolean isNumeric(String number)

{

 try

 {

  Integer.parseInt(number);

  return true;

 }

 catch(NumberFormatException sqo)

 {

  return false;

 }

}

file://判断是否为日期

String mk_date(String c_date,int c_mode)

{

 String wk_buf= "";

 String ToDay = "";

 try

 {

  ToDay = Integer.toString(java.util.Calendar.getInstance().get(java.util.Calendar.YEAR));

  switch(c_mode)

  {

   case 0:

   {

    wk_buf = c_date.substring(2, 4) + "/" + c_date.substring(4, 6);

             if (c_date.length() == 8)

    {

                 wk_buf = wk_buf + "/" + c_date.substring(6, 8);

             }

    break;

   }

   case 1:

   {

    for (int i=0; i<c_date.length();i++)

    {

              if (c_date.substring(i, i+1) == " ")

     {

                     wk_buf = wk_buf + "0";

                 }

     else

     {

                     wk_buf = wk_buf + c_date.substring(i, i+1);

                 }

    }

             c_date = wk_buf;

    wk_buf = "";

             if (c_date.substring(0, 1) != "0" )

    {

                 wk_buf = "19";

             }

    else

    {

                 wk_buf = "20";

             }

             wk_buf = wk_buf + c_date.substring(0, 2) + c_date.substring(3, 5);

             if (c_date.length() == 8)

    {

                  wk_buf = wk_buf + c_date.substring(6, 8);

             }

    break;

   }

   case 2:

   {

    wk_buf = c_date.substring(0, 4) + c_date.substring(5, 7) + c_date.substring(8, 10);

    break;

   }

   case 3:

   {

    for(int i=0; i<c_date.length();i++ )

    {

                 if (c_date.substring(i, i+1) == " ")

     {

                     wk_buf = wk_buf + "0";

                 }

     else

     {

                     wk_buf = wk_buf + c_date.substring(i, i+1);

                 }

             }

             c_date = wk_buf;

             wk_buf = "";

             if (Integer.parseInt(c_date.substring(0,2)) > Integer.parseInt(ToDay.substring(2, 4)))

    {

                 wk_buf = Integer.toString(Integer.parseInt(ToDay.substring(0,2)) - 1);

             }

    else

    {

                 wk_buf = ToDay.substring(0,2);

             }

             wk_buf = wk_buf + c_date.substring(0,2) + c_date.substring(3,5);

             if (c_date.length() == 8)

    {

                 wk_buf = wk_buf + c_date.substring(6,8);

              }

    break;

   }

   case 4:

   {

    wk_buf = c_date.substring(0,4) + "/" + c_date.substring(4,6) + "/" + c_date.substring(6, 8);

    break;

   }

   case 5:

   {

    wk_buf = c_date.substring(0,4) + c_date.substring(5, 7);

    break;

   }

  }

 }

 catch(Exception e)

 {

  System.out.println("wrong : " + e);

 }

 return wk_buf;

}


以下这段servlet代码变为jsp大致的写法是。。。。。。

作者：nnn

email: 1112@s.s

日期：2001-4-3 14:05:56

import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

import java.util.*;

/** Shows all the parameters sent to the servlet via either

* GET or POST. Specially marks parameters that have

* no values or multiple values.

* <P>

* Taken from Core Servlets and JavaServer Pages

* from Prentice Hall and Sun Microsystems Press,

* http://www.coreservlets.com/.

* &copy; 2000 Marty Hall; may be freely used or adapted.

*/

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 = "Reading All Request Parameters";

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>Parameter Name<TH>Parameter Value(s)");

Enumeration paramNames = request.getParameterNames();

while(paramNames.hasMoreElements()) {

String paramName = (String)paramNames.nextElement();

out.print("<TR><TD>" + paramName + "\n<TD>");

String[] paramValues =

request.getParameterValues(paramName);

if (paramValues.length == 1) {

String paramValue = paramValues[0];

if (paramValue.length() == 0)

out.println("<I>No Value</I>");

else

out.println(paramValue);

} else {

out.println("<UL>");

for(int i=0; i<paramValues.length; i++) {

out.println("<LI>" + paramValues);

}

out.println("</UL>");

}

}

out.println("</TABLE>\n</BODY></HTML>");

}

public void doPost(HttpServletRequest request,

HttpServletResponse response)

throws ServletException, IOException {

doGet(request, response);

}

}


异常处理优劣观(转载)

作者：运气

email: webmaster@chinaspx.com

日期：7/1/2001 10:10:46 PM

Java编程中的异常处理是一个很常见的话题了，几乎任何一门介绍性的Java课程都会提到异常处理。不过，我认为很多人其实并没有真正掌握正确处理异常情况的方法和策略，最多也就不过了解个大概，知道点概念。本文就对三种不同程度和质量的Java异常处理进行了讨论，所阐述的处理异常的方式按手法的高下分为：好，不好和恶劣三种。同时向你提供了一些解决这些问题的技巧。

好

异常处理提供了处理程序错误的统一机制。事实上，Java语言通过向调用者提出异常警告的方式而显著地提升了软件开发中的异常处理能力。这种方式把Java语言中的“方法（method）”进行了扩展和增强，使之包括了自身的错误条件。下面就让我们看一个例子，这个例子说明了这种情况。

以下是FileInputStream构造器之一的原型：

public FileInputStream(String name) throws FileNotFoundException

这个原型和C或者C++语言中的同类原形都不一样，Java的方法和构造器必须声明他们在被调用时可能“扔出”的异常，采用的关键字就是“throws”。这种在方法原型中出现的异常提示增加了编程的可靠性。显而易见，这种方式是向方法的调用者提示意了可能出现的异常条件，这样调用者就可以对这些异常作出适当的相应处理。

以下代码示意我们是如何捕获并且处理FileNotFoundException 这一异常的：

try

 {

 FileInputStream fis = new FileInputStream(args[0]);

 // other code here ...

 } catch (FileNotFoundException fnfe)

 {

 System.out.println("File: " + args[0] + " not found. Aborting.");

 System.exit(1);

 }

Java异常处理还有其他一些优秀的特性，这就是可检查异常、用户定义异常和在JDK 1.4中推出的新型Java记录API（Java Logging API）。

java.lang.Exception的所有子类都属于可检查异常。可检查异常（checked exception）是扔出该异常的方法所必须提示的异常，这种异常必须被捕获或者向调用者提示。

用户定义异常（User-defined exceptions）是定制的异常类，这种异常类扩展了java.lang.Exception类。优良的Java程序规定定制异常封装、报告和处理他们自己独有的情况。最新的Java记录API（logging API）则可以集中记录异常。

不好

Java异常处理不好的一面包括两种情况：滥用非检查异常（unchecked exceptions）和滥用catchall构造器等。这两种方式都使得问题变得复杂起来。

有一种类别的异常属于RuntimeException的子类，这种异常不会受到编译器的检查。比如，NullPointerException和 ArrayStoreException就是这种类型异常的实例。程序员可以对RuntimeException进行子类化以回避检查异常的限制，从而便于产生这些异常的方法为其调用者所使用。专业的开发团队应当只允许在很少的情况下才可以这样做。第2种异常处理的陋习是catchall构造器。所谓的“catchall 构造器”就是一种异常捕获代码模块，它可以处理所有扔给它的可能异常。

以下是catchall处理器的实例：

try

 {

 // code here with checked exceptions

 } catch (Throwable t)

 {

 t.printStackTrace();

 }

我得承认，我自己在编写一般程序的时候就曾经用过这种技术；但是，在编写关键程序的时候这种类型的构造器一定要避免使用，除非他们被授权可以和中央错误处理器联合使用才可以这样做。除此之外，catchall构造器不过只是一种通过避免错误处理而加快编程进度的机制。

异常处理的一个不足之处是难以采用优良的错误处理策略。从低容内存状态恢复、写入错误和算法错误等异常情况都不是轻易能得到解决的。你可以尝试一下循环、垃圾收集和提醒用户等常用技术来应付以上的局面。

恶劣

和许多Java特性及其API类似，Java的异常处理机制也有“霸王硬上弓”类的滑稽错误。比方说，为了扔出某个异常竟然毫不犹豫地用“new”关键词为其分配内存就是这样的例子。我自己不知道有多少次就因为犯了这种错误而在严肃的编译器面前屡屡碰壁。在这种情况下，我们其实都是在伺候语言而不是让语言为我们所用。

在我编写的Java Pitfalls一书中探讨了几个异常处理方面的缺陷，比如处理OutOfMemoryErrors就是其一。这一处理过程是：使用finally模块关闭文件，解析异常以得到出现问题的方法和代码行。在这一过程之内最大的缺陷是需要捕获OutOfMemoryError，而这一异常却并不是可检查异常！想想看，内存耗尽是相当常见的情况。任何与内存使用状态紧密相关的程序都应当捕获和处理这一错误。

最后，让我们考虑两个尚不属于Java异常处理机制的特性。第一，目前的情况是，在你成功地处理了异常之后你没有办法回到你刚才离开的地方。而C语言下的setjmp() 和longjmp()函数则可以实现这一特性，Java理应有这两个函数的对应版本，而我们也应该可以用“resume”关键词实现它。

第二个没有实现的特性是全局异常处理器，对比物就是C++语言中的set_new_handler()。比方说，在编程的时候，即便OutOfMemoryError异常比java.lang.Error 的子类更常见，但我们也可以针对OutOfMemoryError异常而采用全局处理器，这样的效果会比对其采用通常的可检查异常要好得多。如果你觉得对这些特性非常在意，那么你可以在Java Developer Connection那里提出自己的建议：在Java平台内增加这些特性吧！


用 Java 保存位图文件（转）

作者：zergman

email: zergman@chinaasp.com

日期：2001-6-15 13:27:17

[b]用 Java 保存位图文件

教程 -- 其中包括将图像对象写入位图文件的全部代码[/b]

[b]作者 Jean-Pierre Dubé[/b]

[BLOCKQUOTE][I][b]摘要[/b]

虽然 Java 提供了几种打开图像的机制，但保存图像并不是它的强项。这篇技巧将讲述如何将图像保存在 24 位位图文件中。另外，Jean-Pierre 还提供了将图像文件写入位图文件所需的全部代码。 [i][/i][/I][/BLOCKQUOTE]这篇技巧是 [url href=http://www.cn.ibm.com/developerWorks/java/jw-tips/tip043/index.shtml]Java技巧 43[/url] 的补充，那篇技巧说明了在 Java 应用程序中加载位图文件的过程。本月我再提供一篇教程，说明如何将图像保存在 24 位位图文件中，其中还包含将图像对象写入位图文件的代码片断。

如果您在 Microsoft Windows 环境中工作，那么创建位图文件的功能将为您提供许多方便。例如，在我的上一个项目中，我必须将 Java 与 Microsoft Access 对接。Java 程序允许用户在屏幕上绘图。这幅图随后被打印到 Microsoft Access 报表中。由于 Java 不支持 OLE，我的唯一选择就是创建该图的一个位图文件，并通知 Microsoft Access 报表在何处能找到这个位图文件。如果您写过向剪贴板发送图像的应用程序，则这个技巧可能对您有用 -- 尤其是当您将这个信息传递给另一个应用程序时。

[b]位图文件的格式[/b]

位图文件格式支持 4 位 RLE（行程长度编码）以及 8 位和 24 位编码。因为我们只处理 24 位格式，所以下面我们查看一下该文件的结构。

位图文件分为三个部分。我已将它们列在下面。

[b]第 1 部分：位图文件的标头[/b]

标头包含位图文件的类型大小信息和版面信息。结构如下（摘自 C 语言结构定义）：

typedef struct tagBITMAPFILEHEADER {

   UINT bfType;

   DWORD bfSize;

   UINT bfReserved1;

   UINT bfReserved2;

   DWORD bfOffBits;

}BITMAPFILEHEADER;

下面是对这个清单中的代码元素的说明：

[UL][LI]bfType：指定文件类型，其值始终为 BM。

[LI]bfSize：指定整个文件的大小（以字节为单位）。

[LI]bfReserved1：保留 -- 必须为 0。

[LI]bfReserved2：保留 -- 必须为 0。

[LI]bfOffBits：指定从 BitmapFileHeader 到图像首部的字节偏移量。 [/LI][/UL]

现在您已经明白位图标头的用途就是标识位图文件。读取位图文件的每个程序都使用位图标头来进行文件验证。

[b]第 2 部分：位图信息标头[/b]

随后的标头称为[i]信息标头[/i]，其中包含图像本身的属性。

下面说明如何指定 Windows 3.0（或更高版本）设备独立位图 (DIB) 的大小和颜色格式:

typedef struct tagBITMAPINFOHEADER {

    DWORD biSize;

    LONG biWidth;

    LONG biHeight;

    WORD biPlanes;

    WORD biBitCount;

    DWORD biCompression;

    DWORD biSizeImage;

    LONG biXPelsPerMeter;

    LONG biYPelsPerMeter;

    DWORD biClrUsed;

    DWORD biClrImportant;

} BITMAPINFOHEADER;

以上代码清单的每个元素说明如下：

[UL][LI]biSize：指定 BITMAPINFOHEADER 结构所需的字节数。

[LI]biWidth：指定位图的宽度（以象素为单位）。

[LI]biHeight：指定位图的高度（以象素为单位）。

[LI]biPlanes：指定目标设备的位面数。这个成员变量的值必须为 1。

[LI]biBitCount：指定每个象素的位数。其值必须为 1、4、8 或 24。

[LI]biCompression：指定压缩位图的压缩类型。在 24 位格式中，该变量被设置为 0。

[LI]biSizeImage：指定图像的大小（以字节为单位）。如果位图的格式是 BI_RGB，则将此成员变量设置为 0 是有效的。

[LI]biXPelsPerMeter：为位图指定目标设备的水平分辨率（以“象素/米”为单位）。应用程序可用该值从最符合当前设备特征的资源群组中选择一个位图。

[LI]biYPelsPerMeter：为位图指定目标设备的垂直分辨率（以“象素/米”为单位）。

[LI]biClrUsed：指定位图实际所用的颜色表中的颜色索引数。如果 biBitCount 设为 24，则 biClrUsed 指定用来优化 Windows 调色板性能的参考颜色表。

[LI]biClrImportant：指定对位图的显示有重要影响的颜色索引数。如果此值为 0，则所有颜色都很重要。 [/LI][/UL]

现在已定义了创建图像所需的全部信息。

[b]第 3 部分：图像[/b]

在 24 位格式中，图像中的每个象素都由存储为 BRG 的三字节 RGB 序列表示。每个扫描行都被补足到 4 位。为了使这个过程稍复杂一点，图像是自底而上存储的，即第一个扫描行是图像中的最后一个扫描行。下图显示了标头 (BITMAPHEADER) 和 (BITMAPINFOHEADER) 以及部分图像。各个部分由垂线分隔：

0000000000 4D42 B536 0002 0000 0000 0036 0000 | 0028

0000000020 0000 0107 0000 00E0 0000 0001 0018 0000

0000000040 0000 B500 0002 0EC4 0000 0EC4 0000 0000

0000000060 0000 0000 0000 | FFFF FFFF FFFF FFFF FFFF

0000000100 FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF

*

[b]现在，我们开始检视代码[/b]

现在我们已经知道了 24 位位图文件的结构，下面就是您期待已久的内容：用来将图像对象写入位图文件的代码。

import java.awt.*;

import java.io.*;

import java.awt.image.*;

public class BMPFile extends Component {

  //--- 私有常量

  private final static int BITMAPFILEHEADER_SIZE = 14;

  private final static int BITMAPINFOHEADER_SIZE = 40;

  //--- 私有变量声明

  //--- 位图文件标头

  private byte bitmapFileHeader [] = new byte [14];

  private byte bfType [] = {'B', 'M'};

  private int bfSize = 0;

  private int bfReserved1 = 0;

  private int bfReserved2 = 0;

  private int bfOffBits = BITMAPFILEHEADER_SIZE + BITMAPINFOHEADER_SIZE;

  //--- 位图信息标头

  private byte bitmapInfoHeader [] = new byte [40];

  private int biSize = BITMAPINFOHEADER_SIZE;

  private int biWidth = 0;

  private int biHeight = 0;

  private int biPlanes = 1;

  private int biBitCount = 24;

  private int biCompression = 0;

  private int biSizeImage = 0x030000;

  private int biXPelsPerMeter = 0x0;

  private int biYPelsPerMeter = 0x0;

  private int biClrUsed = 0;

  private int biClrImportant = 0;

  //--- 位图原始数据

  private int bitmap [];

  //--- 文件部分

  private FileOutputStream fo;

  //--- 缺省构造函数

  public BMPFile() {

  }

  public void saveBitmap (String parFilename, Image parImage, int

parWidth, int parHeight) {

     try {

        fo = new FileOutputStream (parFilename);

        save (parImage, parWidth, parHeight);

        fo.close ();

     }

     catch (Exception saveEx) {

        saveEx.printStackTrace ();

     }

  }

  /*

   * saveMethod 是该进程的主方法。该方法

   * 将调用 convertImage 方法以将内存图像转换为

   * 字节数组；writeBitmapFileHeader 方法创建并写入

   * 位图文件标头；writeBitmapInfoHeader 创建

   * 信息标头；writeBitmap 写入图像。

   *

   */

  private void save (Image parImage, int parWidth, int parHeight) {

     try {

        convertImage (parImage, parWidth, parHeight);

        writeBitmapFileHeader ();

        writeBitmapInfoHeader ();

        writeBitmap ();

     }

     catch (Exception saveEx) {

        saveEx.printStackTrace ();

     }

  }

  /*

   * convertImage 将内存图像转换为位图格式 (BRG)。

   * 它还计算位图信息标头所用的某些信息。

   *

   */

  private boolean convertImage (Image parImage, int parWidth, int parHeight) {

     int pad;

     bitmap = new int [parWidth * parHeight];

     PixelGrabber pg = new PixelGrabber (parImage, 0, 0, parWidth, parHeight,

                                         bitmap, 0, parWidth);

     try {

        pg.grabPixels ();

     }

     catch (InterruptedException e) {

        e.printStackTrace ();

        return (false);

     }

     pad = (4 - ((parWidth * 3) % 4)) * parHeight;

     biSizeImage = ((parWidth * parHeight) * 3) + pad;

     bfSize = biSizeImage + BITMAPFILEHEADER_SIZE +

BITMAPINFOHEADER_SIZE;

     biWidth = parWidth;

     biHeight = parHeight;

     return (true);

  }

  /*

   * writeBitmap 将象素捕获器返回的图像转换为

   * 所需的格式。请记住：扫描行在位图文件中是

   * 反向存储的！

   *

   * 每个扫描行必须补足为 4 个字节。

   */

  private void writeBitmap () {

      int size;

      int value;

      int j;

      int i;

      int rowCount;

      int rowIndex;

      int lastRowIndex;

      int pad;

      int padCount;

      byte rgb [] = new byte [3];

      size = (biWidth * biHeight) - 1;

      pad = 4 - ((biWidth * 3) % 4);

      if (pad == 4) // <==== 错误修正

         pad = 0; // <==== 错误修正

      rowCount = 1;

      padCount = 0;

      rowIndex = size - biWidth;

      lastRowIndex = rowIndex;

      try {

         for (j = 0; j < size; j++) {

            value = bitmap [rowIndex];

            rgb [0] = (byte) (value & 0xFF);

            rgb [1] = (byte) ((value >> 8) & 0xFF);

            rgb [2] = (byte) ((value >> 16) & 0xFF);

            fo.write (rgb);

            if (rowCount == biWidth) {

               padCount += pad;

               for (i = 1; i <= pad; i++) {

                  fo.write (0x00);

               }

               rowCount = 1;

               rowIndex = lastRowIndex - biWidth;

               lastRowIndex = rowIndex;

            }

            else

               rowCount++;

            rowIndex++;

         }

         //--- 更新文件大小

         bfSize += padCount - pad;

         biSizeImage += padCount - pad;

      }

      catch (Exception wb) {

         wb.printStackTrace ();

      }

   }

  /*

   * writeBitmapFileHeader 将位图文件标头写入文件中。

   *

   */

  private void writeBitmapFileHeader () {

     try {

        fo.write (bfType);

        fo.write (intToDWord (bfSize));

        fo.write (intToWord (bfReserved1));

        fo.write (intToWord (bfReserved2));

        fo.write (intToDWord (bfOffBits));

     }

     catch (Exception wbfh) {

        wbfh.printStackTrace ();

     }

  }

  /*

   *

   * writeBitmapInfoHeader 将位图信息标头

   * 写入文件中。

   *

   */

  private void writeBitmapInfoHeader () {

     try {

        fo.write (intToDWord (biSize));

        fo.write (intToDWord (biWidth));

        fo.write (intToDWord (biHeight));

        fo.write (intToWord (biPlanes));

        fo.write (intToWord (biBitCount));

        fo.write (intToDWord (biCompression));

        fo.write (intToDWord (biSizeImage));

        fo.write (intToDWord (biXPelsPerMeter));

        fo.write (intToDWord (biYPelsPerMeter));

        fo.write (intToDWord (biClrUsed));

        fo.write (intToDWord (biClrImportant));

     }

     catch (Exception wbih) {

        wbih.printStackTrace ();

     }

  }

  /*

   *

   * intToWord 将整数转换为单字，返回值

   * 存储在一个双字节数组中。

   *

   */

  private byte [] intToWord (int parValue) {

     byte retValue [] = new byte [2];

     retValue [0] = (byte) (parValue & 0x00FF);

     retValue [1] = (byte) ((parValue >> 8) & 0x00FF);

     return (retValue);

  }

  /*

   *

   * intToDWord 将整数转换为双字，返回值

   * 存储在一个 4 字节数组中。

   *

   */

  private byte [] intToDWord (int parValue) {

     byte retValue [] = new byte [4];

     retValue [0] = (byte) (parValue & 0x00FF);

     retValue [1] = (byte) ((parValue >> 8) & 0x000000FF);

     retValue [2] = (byte) ((parValue >> 16) & 0x000000FF);

     retValue [3] = (byte) ((parValue >> 24) & 0x000000FF);

     return (retValue);

  }

}

[b]小结[/b]

这就是所要做的全部工作。我确信您将会发现这个类很有用，因为到 JDK 1.1.6 为止，Java 不支持用任何常用的格式保存图像。JDK 1.2 将支持创建 JPEG 图像，但不支持创建位图。所以这个类仍将填补 JDK1.2 中的空白。

如果您使用这个类并发现了改进它的方法，请通知我！下面的个人简历中有我的电子邮件地址。

[TABLE][TR][TD][b]作者简介[/b]

jeanpierre.dube Jean-Pierre Dubé 是一名自由 Java 咨询者。他于 1988 年注册创办了 Infocom公司。从那时起，Infocom 已承接并开发了几套应用程序，涉及的领域包括制造业、文档管理和大规模输电线管理。他有丰富的 C 语言、Visual Basic 和 Java（最近）编程经验，他的公司现在主要使用 Java 语言。 Infocom 最近的一个项目是一种图形 API，其测试版很快就会面市。 [/TD][/TR][/TABLE]


用 Java 保存位图文件

作者：五一

日期：2001-6-9 13:28:42

用 Java 保存位图文件

5/23/2001 9:28:10· Jean-Pierre Dubé·jdeveloper

--------------------------------------------------------------------------------

1 2  下一页

摘要

    虽然 Java 提供了几种打开图像的机制，但保存图像并不是它的强项。这篇技巧将讲述如何将图像保存在 24 位位图文件中。另外，Jean-Pierre 还提供了将图像文件写入位图文件所需的全部代码。

    这篇技巧是 "在 Java 应用程序中加载位图文件的逐步指南" 的补充，那篇技巧说明了在 Java 应用程序中加载位图文件的过程。本月我再提供一篇教程，说明如何将图像保存在 24 位位图文件中，其中还包含将图像对象写入位图文件的代码片断。

    如果您在 Microsoft Windows 环境中工作，那么创建位图文件的功能将为您提供许多方便。例如，在我的上一个项目中，我必须将 Java 与 Microsoft Access 对接。Java 程序允许用户在屏幕上绘图。这幅图随后被打印到 Microsoft Access 报表中。由于 Java 不支持 OLE，我的唯一选择就是创建该图的一个位图文件，并通知 Microsoft Access 报表在何处能找到这个位图文件。如果您写过向剪贴板发送图像的应用程序，则这个技巧可能对您有用 -- 尤其是当您将这个信息传递给另一个应用程序时。

    位图文件的格式

    位图文件格式支持 4 位 RLE（行程长度编码）以及 8 位和 24 位编码。因为我们只处理 24 位格式，所以下面我们查看一下该文件的结构。

    位图文件分为三个部分。我已将它们列在下面。

    第 1 部分：位图文件的标头

    标头包含位图文件的类型大小信息和版面信息。结构如下（摘自 C 语言结构定义）：

typedef struct tagBITMAPFILEHEADER {

      UINT bfType;

      DWORD bfSize;

      UINT bfReserved1;

      UINT bfReserved2;

      DWORD bfOffBits;

    }BITMAPFILEHEADER;

  下面是对这个清单中的代码元素的说明：

    bfType：指定文件类型，其值始终为 BM。

    bfSize：指定整个文件的大小（以字节为单位）。

    bfReserved1：保留 -- 必须为 0。

    bfReserved2：保留 -- 必须为 0。

    bfOffBits：指定从 BitmapFileHeader 到图像首部的字节偏移量。

    现在您已经明白位图标头的用途就是标识位图文件。读取位图文件的每个程序都使用位图标头来进行文件验证。

    第 2 部分：位图信息标头

    随后的标头称为信息标头，其中包含图像本身的属性。

    下面说明如何指定 Windows 3.0（或更高版本）设备独立位图 (DIB) 的大小和颜色格式:

    typedef struct tagBITMAPINFOHEADER {

        DWORD biSize;

        LONG biWidth;

        LONG biHeight;

        WORD biPlanes;

        WORD biBitCount;

        DWORD biCompression;

        DWORD biSizeImage;

        LONG biXPelsPerMeter;

        LONG biYPelsPerMeter;

        DWORD biClrUsed;

        DWORD biClrImportant;

    } BITMAPINFOHEADER;

    以上代码清单的每个元素说明如下：

      biSize：指定 BITMAPINFOHEADER 结构所需的字节数。

      biWidth：指定位图的宽度（以象素为单位）。

      biHeight：指定位图的高度（以象素为单位）。

      biPlanes：指定目标设备的位面数。这个成员变量的值必须为 1。

      biBitCount：指定每个象素的位数。其值必须为 1、4、8 或 24。

      biCompression：指定压缩位图的压缩类型。在 24 位格式中，该变量被设置为 0。

      biSizeImage：指定图像的大小（以字节为单位）。如果位图的格式是 BI_RGB，则将此成员变量设置为 0 是有效的。

      biXPelsPerMeter：为位图指定目标设备的水平分辨率（以“象素/米”为单位）。应用程序可用该值从最符合当前设备特征的资源群组中选择一个位图。

      biYPelsPerMeter：为位图指定目标设备的垂直分辨率（以“象素/米”为单位）。

      biClrUsed：指定位图实际所用的颜色表中的颜色索引数。如果 biBitCount 设为 24，则 biClrUsed 指定用来优化 Windows 调色板性能的参考颜色表。

      biClrImportant：指定对位图的显示有重要影响的颜色索引数。如果此值为 0，则所有颜色都很重要。

    现在已定义了创建图像所需的全部信息。

    第 3 部分：图像

    在 24 位格式中，图像中的每个象素都由存储为 BRG 的三字节 RGB 序列表示。每个扫描行都被补足到 4 位。为了使这个过程稍复杂一点，图像是自底而上存储的，即第一个扫描行是图像中的最后一个扫描行。下图显示了标头 (BITMAPHEADER) 和 (BITMAPINFOHEADER) 以及部分图像。各个部分由垂线分隔：

0000000000 4D42 B536 0002 0000 0000 0036 0000 | 0028

0000000020 0000 0107 0000 00E0 0000 0001 0018 0000

0000000040 0000 B500 0002 0EC4 0000 0EC4 0000 0000

0000000060 0000 0000 0000 | FFFF FFFF FFFF FFFF FFFF

0000000100 FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF

*

在，我们开始检视代码

    现在我们已经知道了 24 位位图文件的结构，下面就是您期待已久的内容：用来将图像对象写入位图文件的代码。

import java.awt.*;

import java.io.*;

import java.awt.image.*;

public class BMPFile extends Component {

file://--- 私有常量

private final static int BITMAPFILEHEADER_SIZE = 14;

private final static int BITMAPINFOHEADER_SIZE = 40;

file://--- 私有变量声明

file://--- 位图文件标头

private byte bitmapFileHeader [] = new byte [14];

private byte bfType [] = {'B', 'M'};

private int bfSize = 0;

private int bfReserved1 = 0;

private int bfReserved2 = 0;

private int bfOffBits = BITMAPFILEHEADER_SIZE + BITMAPINFOHEADER_SIZE;

file://--- 位图信息标头

private byte bitmapInfoHeader [] = new byte [40];

private int biSize = BITMAPINFOHEADER_SIZE;

private int biWidth = 0;

private int biHeight = 0;

private int biPlanes = 1;

private int biBitCount = 24;

private int biCompression = 0;

private int biSizeImage = 0x030000;

private int biXPelsPerMeter = 0x0;

private int biYPelsPerMeter = 0x0;

private int biClrUsed = 0;

private int biClrImportant = 0;

file://--- 位图原始数据

private int bitmap [];

file://--- 文件部分

private FileOutputStream fo;

file://--- 缺省构造函数

public BMPFile() {

}

public void saveBitmap (String parFilename, Image parImage, int

parWidth, int parHeight) {

try {

fo = new FileOutputStream (parFilename);

save (parImage, parWidth, parHeight);

fo.close ();

}

catch (Exception saveEx) {

saveEx.printStackTrace ();

}

}

/*

* saveMethod 是该进程的主方法。该方法

* 将调用 convertImage 方法以将内存图像转换为

* 字节数组；writeBitmapFileHeader 方法创建并写入

* 位图文件标头；writeBitmapInfoHeader 创建

* 信息标头；writeBitmap 写入图像。

*

*/

private void save (Image parImage, int parWidth, int parHeight) {

try {

convertImage (parImage, parWidth, parHeight);

writeBitmapFileHeader ();

writeBitmapInfoHeader ();

writeBitmap ();

}

catch (Exception saveEx) {

saveEx.printStackTrace ();

}

}

/*

* convertImage 将内存图像转换为位图格式 (BRG)。

* 它还计算位图信息标头所用的某些信息。

*

*/

private boolean convertImage (Image parImage, int parWidth, int parHeight) {

int pad;

bitmap = new int [parWidth * parHeight];

PixelGrabber pg = new PixelGrabber (parImage, 0, 0, parWidth, parHeight,

bitmap, 0, parWidth);

try {

pg.grabPixels ();

}

catch (InterruptedException e) {

e.printStackTrace ();

return (false);

}

pad = (4 - ((parWidth * 3) % 4)) * parHeight;

biSizeImage = ((parWidth * parHeight) * 3) + pad;

bfSize = biSizeImage + BITMAPFILEHEADER_SIZE +

BITMAPINFOHEADER_SIZE;

biWidth = parWidth;

biHeight = parHeight;

return (true);

}

/*

* writeBitmap 将象素捕获器返回的图像转换为

* 所需的格式。请记住：扫描行在位图文件中是

* 反向存储的！

*

* 每个扫描行必须补足为 4 个字节。

*/

private void writeBitmap () {

int size;

int value;

int j;

int i;

int rowCount;

int rowIndex;

int lastRowIndex;

int pad;

int padCount;

byte rgb [] = new byte [3];

size = (biWidth * biHeight) - 1;

pad = 4 - ((biWidth * 3) % 4);

if (pad == 4) // <==== 错误修正

pad = 0; // <==== 错误修正

rowCount = 1;

padCount = 0;

rowIndex = size - biWidth;

lastRowIndex = rowIndex;

try {

for (j = 0; j < size; j++) {

value = bitmap [rowIndex];

rgb [0] = (byte) (value & 0xFF);

rgb [1] = (byte) ((value >> 8) & 0xFF);

rgb [2] = (byte) ((value >> 16) & 0xFF);

fo.write (rgb);

if (rowCount == biWidth) {

padCount += pad;

for (i = 1; i <= pad; i++) {

fo.write (0x00);

}

rowCount = 1;

rowIndex = lastRowIndex - biWidth;

lastRowIndex = rowIndex;

}

else

rowCount++;

rowIndex++;

}

file://--- 更新文件大小

bfSize += padCount - pad;

biSizeImage += padCount - pad;

}

catch (Exception wb) {

wb.printStackTrace ();

}

}

/*

* writeBitmapFileHeader 将位图文件标头写入文件中。

*

*/

private void writeBitmapFileHeader () {

try {

fo.write (bfType);

fo.write (intToDWord (bfSize));

fo.write (intToWord (bfReserved1));

fo.write (intToWord (bfReserved2));

fo.write (intToDWord (bfOffBits));

}

catch (Exception wbfh) {

wbfh.printStackTrace ();

}

}

/*

*

* writeBitmapInfoHeader 将位图信息标头

* 写入文件中。

*

*/

private void writeBitmapInfoHeader () {

try {

fo.write (intToDWord (biSize));

fo.write (intToDWord (biWidth));

fo.write (intToDWord (biHeight));

fo.write (intToWord (biPlanes));

fo.write (intToWord (biBitCount));

fo.write (intToDWord (biCompression));

fo.write (intToDWord (biSizeImage));

fo.write (intToDWord (biXPelsPerMeter));

fo.write (intToDWord (biYPelsPerMeter));

fo.write (intToDWord (biClrUsed));

fo.write (intToDWord (biClrImportant));

}

catch (Exception wbih) {

wbih.printStackTrace ();

}

}

/*

*

* intToWord 将整数转换为单字，返回值

* 存储在一个双字节数组中。

*

*/

private byte [] intToWord (int parValue) {

byte retValue [] = new byte [2];

retValue [0] = (byte) (parValue & 0x00FF);

retValue [1] = (byte) ((parValue >> 8) & 0x00FF);

return (retValue);

}

/*

*

* intToDWord 将整数转换为双字，返回值

* 存储在一个 4 字节数组中。

*

*/

private byte [] intToDWord (int parValue) {

byte retValue [] = new byte [4];

retValue [0] = (byte) (parValue & 0x00FF);

retValue [1] = (byte) ((parValue >> 8) & 0x000000FF);

retValue [2] = (byte) ((parValue >> 16) & 0x000000FF);

retValue [3] = (byte) ((parValue >> 24) & 0x000000FF);

return (retValue);

}

}

    小结

    这就是所要做的全部工作。我确信您将会发现这个类很有用，因为到 JDK 1.1.6 为止，Java 不支持用任何常用的格式保存图像。JDK 1.2 将支持创建 JPEG 图像，但不支持创建位图。所以这个类仍将填补 JDK1.2 中的空白。


用 javabean 来实现 MySQL 的分页显示

作者：jeru

email: jeru@163.net

日期：2001-1-4 17:13:10

用 javabean 来实现 MySQL 的分页显示

●○●○●○●○●○●○●○●○●○●○●○●○●○●○

○  作者:刘湛      日期:2000-02-04      jeru@163.net  ●

●  http://www.cyberlabs.com/~jeru/                  ○

○  欢迎访问爪哇人，获取更多资料                      ●

●○●○●○●○●○●○●○●○●○●○●○●○●○●○

今天写了个 MySQL 分页的 javabean,是用 MySQL 里的 LIMIT 来实现的.

sql = "SELECT * FROM Test LIMIT 5, 10";

这句话的意思就是从第五条记录开始往下读 10 条记录出来，这个 bean 没有连接数据库的功能，

你可以使用自己的类来链接数据库，当然可以用我那个写的很烂的 dbClass.java 来连，^_^

这里给出三个程序的源代码。

dbClass.java -- 用来连接 MySQL 数据库。

PageQuery.java -- 重写了 dbClass 返回的 ResultSet,使其具备分页功能。

example.jsp -- jsp 文件，可以看到，我只用了两行就实现了分页的功能，当然，

sql 语句是不鼓励直接写在 jsp 里的，这里为了让大家看清楚，所以这么做了。

自知水平不高，只想抛砖引玉，有什么错漏之处还望高手指出。

=========================== example.jsp ===================================

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

<%@ page contentType="text/html; charset=gb2312" %>

<jsp:useBean id="pq" scope="page" class="dbclass.PageQuery" />

<html>

<body bgcolor="#8BA9C9">

<table bgcolor="#fecda9" cellspacing=0>

<%

String query = "SELECT * FROM systempass";   // 注意这个" FROM "一定要大写

ResultSet rs = pq.myQuery(query, request);

String bar = pq.PageLegend();  //读取分页提示栏

out.println("<tr><td colspan=2>"+bar+"</td></tr>");

out.println("<tr><td colspan=2><hr size=1 color=blue></td></tr>");

while (rs.next())  { %>

<tr><td><%=rs.getString(9)%></td><td><%=rs.getString(10)%></td></tr>

<% } %>

</table>

</body>

</html>

=========================== PageQuery.java ===================================

package dbclass;

/**

 * PageQuery v 1.0

 * 这个类原名叫 TViewPage ,作者 sharetop ,用 php 写的。

 * 同事 Macro 曾用 PHP 改写过这个类，添加了不少功能。

 * 我感觉封装的很好，使用十分方便，使用 JSP 后，便有了

 * 想法用 JSP 来改写，这次为了简明起见，我省去了很多功能,

 * 尽量让它好读，以后有空添加更多的功能，

 *

 * Mender :

 *     Jeru Liu

 * Homepage :

 *     http://www.cyberlabs.com/~jeru/

 * Email: jeru@163.net

 *

 * 本类没有提供连接数据库的功能，所以需在外部打开相应的数据库。

 * 需在外部自定义数据显示格式。

 */

import java.util.*;

import java.sql.*;

import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

public class PageQuery  {

  int Offset; // 记录偏移量

  int Total; // 记录总数

  int MaxLine; // 记录每页显示记录数

  ResultSet rs; // 读出的结果

  int TPages; // 总页数

  int CPages; // 当前页数

  String PageQuery; // 分页显示要传递的参数

  String Query;     // query 语句

  String QueryPart; // " FROM " 以后的 query 部分

  String FilePath;

  dbClass db;    // object of dbclass

  //constructer do nothing

  public PageQuery() {

    // 每页显示十行

    MaxLine = 10;

    db = new dbClass();

  }

  //********读取记录***************

  //　主要工作函数，根据所给的条件从表中读取相应的记录

  public ResultSet myQuery(String query, HttpServletRequest req) throws SQLException {

    String query_part, os;

    int begin, offset;

    // 截取 " FROM " 以后的 query 语句

    begin = query.indexOf(" FROM ");

    query_part = query.substring(begin, query.length()).trim();

    // 计算偏移量

    os = req.getParameter("offset");

    if (os == null) Offset = 0;

    else Offset = Integer.parseInt(os);

    // 获取文件名

    FilePath = req.getRequestURI();

    Query = query;

    QueryPart = query_part;

    // 计算总的记录条数

    String SQL = "SELECT Count(*) AS total " + this.QueryPart;

    rs = db.executeQuery(SQL);

    if (rs.next())

    Total = rs.getInt(1);

    // 设置当前页数和总页数

    TPages = (int)Math.ceil((double)this.Total/this.MaxLine);

    CPages = (int)Math.floor((double)Offset/this.MaxLine+1);

    // 根据条件判断，取出所需记录

    if (Total > 0) {

      SQL = Query + " LIMIT " + Offset + " , " + MaxLine;

      rs = db.executeQuery(SQL);

    }

    return rs;

  }

  // 显示总页数

  public int getTotalPages() {

    return TPages;

  }

  //显示当前所在页数

  public int getCurrenPages() {

    return CPages;

  }

  //**********显示翻页提示栏*************

  // 显示首页、下页、上页、尾页

  // 你可以改成你喜欢的样式

  public String PageLegend() {

    String str = "";

    int first, next, prev, last;

    first = 0;

    next = Offset + MaxLine;

    prev = Offset - MaxLine;

    last = (this.TPages - 1) * MaxLine;

    if(Offset >= MaxLine)

      str +=  " <A href=" + FilePath + "?offset=" + first + ">首页</A> ";

    else str += " 首页 ";

    if(prev >= 0)

      str +=  " <A href=" + FilePath + "?offset=" + prev + ">前页</A> ";

    else str += " 前页 ";

    if(next < Total)

      str +=  " <A href=" + FilePath + "?offset=" + next + ">后页</A> ";

    else str += " 后页 ";

    if(TPages != 0 && CPages < TPages)

      str +=  " <A href=" + FilePath + "?offset=" + last + ">尾页</A>";

    else str += " 尾页 ";

    str += " 页次：" + getCurrenPages() + "/" + getTotalPages() + "页 ";

    str += MaxLine + "条/页 " + "共" + Total + "条";

    return str;

  }

}

=========================== dbClass.java ===================================

/**

 * a class use to connect the MySQL database and do some query

 * use mm.MySQL.Drive

 * Jeru Liu ,November 2, 2000 , ver - 1.1

 *

 */

package dbclass;

import java.sql.*;

public class dbClass  {

  // public: connection parameters

  String dbName = "Kernel";

  String Login = "root";

  String Password = "MySQL";

  String DBDriver = "org.gjt.mm.MySQL.Driver";

  String ConnStr = "jdbc:MySQL://localhost/"+dbName+"?user="+Login+";password="+Password;

  Connection con = null;

  Statement stmt = null;

  ResultSet rs = null;

  ResultSetMetaData resultsMeta =null;

  int rows = 0;

  // public: constructor to load driver and connect db

  public dbClass()  {

        // load mm.MySQL.driver

      try

      {

          Class.forName("org.gjt.mm.MySQL.Driver");

      }

      // display corresponding error message when onload error occur

      catch (java.lang.ClassNotFoundException e)

      {

          System.out.println("Class not found exception occur. Message is:");

          System.out.println(e.getMessage());

      }

      // establish connection to the database throught driver

      try

      {

          con = DriverManager.getConnection(ConnStr);

      }

      // display sql error message

      catch (SQLException e)

      {

          System.out.print("SQL Exception occur. Message is:");

          System.out.print(e.getMessage());

      }

  }

  // perform a query with records returned

  public ResultSet executeQuery(String sql)  throws SQLException

  {

      ResultSet rs = null;

      try

      {

          stmt = con.createStatement();

          rs = stmt.executeQuery(sql);

          while(rs.next())

              this.rows ++;

          rs = stmt.executeQuery(sql);

      }

      catch (SQLException e)

      {

          System.out.print("Query:"+e.getMessage());

      }

      this.rs = rs;

      return rs;

  }

  // perform a query without records returned

  public boolean executeUpdate(String sql)

  {

      try

      {

          stmt = con.createStatement();

          stmt.executeUpdate(sql);

          return true;

      }

      catch(SQLException e)

      {

          System.out.print("Update:"+e.getMessage());

          return false;

      }

  }

  // return the num of columns

  public int getColumns()

  {

     int columns = 0;

     try

     {

          this.resultsMeta = this.rs.getMetaData();

          columns = this.resultsMeta.getColumnCount();

     }

     catch (SQLException e)  {}

     return columns;

  }

  // return the num of rows

  public int getRows()

  {

     return this.rows;

  }

  public String getDBName() {

      return this.dbName;

  }

}


用 JSP 在客户端生成 JavaScript 代码来实现表单校验

作者：jeru

email: jeru@163.net

日期：2001-1-5 18:10:04

用 JSP 在客户端生成 JavaScript 代码来实现表单校验

●○●○●○●○●○●○●○●○●○●○●○●○●○●○

○　作者:刘湛　　　日期:2000-01-05　　　jeru@163.net　●

●　http://www.cyberlabs.com/~jeru/○

○　欢迎访问爪哇人，获取更多资料　　　　　　　　　　　●

●○●○●○●○●○●○●○●○●○●○●○●○●○●○

今天费了一天时间就是做这个东西，原理很简单，就是用 JSP 在页面的开始部分生成一段代码，

如 errorcheck.jsp 中所示，但程序太长，还是费了我不少时间来改写。

主程序是名为 ErrorCheck.java ，有了这个 ErrorCheck 的 Bean，我们就再也不用为了表单校验去

写那一大堆烦人的 JavaScript 代码了。ErrorCheck 类已帮我们生成了几乎所有你将会用到的校验方法，

如是否为数字，长度的校验，是否为合法email等，你只需在 jsp 页面里调用相应的函数就可以了。

目前一共有七个函数：

一 检测是否为数字

//输入输入框名和错误提示信息

numericCheck(String inputName,String errorMsg);

二 检测email是否合法

//输入输入框名和错误提示信息

emailCheck(String inputName,String errorMsg);

三 检测电话号码是否合法

//输入输入框名和错误提示信息

telCheck(String inputName,String errorMsg);

四 检测字串长度是否在规定范围那内

//输入输入框名,错误提示信息,最小长度，最大长度

lengthCheck(String inputName,String errorMsg,int min,int max);

五 检测字串中是否不含禁止的字串

//输入输入框名,错误提示信息,禁止字串

denyStrCheck(String inputName,String errorMsg,String str);

六 检测字串中是否含给定字串

//输入输入框名,错误提示信息,指定字串

stringCheck(String inputName,String errorMsg,String str);

七 检测日期格式是否为 "yyyy-mm-dd"

//输入输入框名和错误提示信息

dateCheck(String inputName,String errorMsg);

只要调用一下这个bean，然后用setFromName()设定你的表单名，再调用以上函数，

最后 out.println(yourID.ErrorCheckScript()),就输出了一段 JavaScript 代码了，当然了，

别忘了这个 <form name=myForm onsubmit="return errorCheck();">

ok,just enjoy it,今天太累，不想多少，有任何意见请写信给我或在我主页上留言。

注：我调试 errorcheck.jsp 的时候因服务器的问题不知为何不能用 usebean,setProperty 的方法，

只好 new 了一下，我想你们是应该可以用useBean和setProperty的,自己改一下吧。

===================================== errorcheck.jsp =====================================

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

<%@ page contentType="text/html; charset=gb2312" %>

<jsp:useBean id="cc" scope="page" class="dbclass.ErrorCheck" />

<%

ErrorCheck ec = new ErrorCheck();

ec.setFormName("myForm");

ec.numericCheck("number","The Number you input is invalid!");

ec.emailCheck("email","The Email you input is invalid!");

ec.telCheck("tel","The telephone you input is invalid!");

ec.lengthCheck("strlen","The string you input in the fourth field in not between 6-8",6,8);

ec.denyStrCheck("nojeru","The fifith field must not contain 'jeru'","jeru");

ec.stringCheck("jeru","The sixth field must not null and contian 'jeru'","jeru");

ec.dateCheck("date","The date you input is invalid,should be yyyy-mm-dd");

out.println(ec.ErrorCheckScript());

%>

<html>

<body style="font-size:9pt; font-family:Arial;">

<h1>Errocheck Test</h1>

<hr>

<form name=myForm onsubmit="return errorCheck();">

input a number:<br>

<input type="text" name="number"><p>

input a emial:<br>

<input type="text" name="email"><p>

input a telephone:<br>

<input type="text" name="tel"><p>

input a string (length should between 6-8):<br>

<input type="text" name="strlen"><p>

input a string (shoulde not contain "jeru"):<br>

<input type="text" name="nojeru"><p>

input a string (must contain "jeru"):<br>

<input type="text" name="jeru"><p>

input a date (yyyy-mm-dd):<br>

<input type="text" name="date"><p>

<br><input type="submit" name="submit" value="go">

</form>

</body>

</html>

===================================== ErrorCheck.java =====================================

package dbclass;

/**

 * ErrorCheck v 1.0

 *

 * 这个类是用来在客户端生成 JavaScript 代码来校验表单的

 * 原是版本是同事 Macro 用 PHP 写的，我感觉十分好用，再也

 * 不用再为那些表单区写烦人的 javascript 代码拉，感谢他！

 * 这次我用 Java 改写，封装成一个类，并修复了少许的 bug,加

 * 多了一条校验的功能,它的扩展性很好，以后可能会继续完善。

 *

 * Mender :

 *     Jeru Liu

 * Homepage :

 *     http://www.cyberlabs.com/~jeru/

 * Email: jeru@163.net

 *

 */

import java.io.*;

public class ErrorCheck    {

 /* public: the javascript string */

 String errorCheckStr;

 /* public: the form name you used */

 public String formName;

 public void setFormName(String formName) {

   this.formName = formName;

 }

 /***************************************************************************\

  *        public: constructor functions

  *        构造函数

 \***************************************************************************/

 public ErrorCheck()    {

   this.errorCheckStr =

     "<script ID=clientEventHandlersJS language=javascript>" + "\n" +

     "<!--" + "\n";

   this.neededFunction();    // load the needed functions

   this.errorCheckStr +=

     "function errorCheck() {" + "\n";

  }

  /***************************************************************************\

   *        public: export javascript script

   *        输出 JAVASCRIPT 脚本

  \***************************************************************************/

  public String ErrorCheckScript()    {

    this.errorCheckStr +=

      "}" + "\n" +

      "-->" + "\n" +

      "</script>" + "\n";

    return this.errorCheckStr;

  }

  /***************************************************************************\

   *        public: check the numeric

   *        检查录入框值是否是数字

  \***************************************************************************/

  public void numericCheck(String inputName, String errorMsg)    {

    this.errorCheckStr +=

      "  if(fucCheckNUM(document."+formName+"."+inputName+".value) == 0) {" + "\n" +

      "    alert(\""+errorMsg+".\");" + "\n" +

      "    document."+formName+"."+inputName+".focus();" + "\n" +

      "    return(false);" + "\n" +

      "  }" + "\n\n";

  }

  /***************************************************************************\

   *        public: check the length

   *        检查录入框值的长度

  \***************************************************************************/

  public void lengthCheck(String inputName, String errorMsg, int MinLength, int MaxLength) {

    this.errorCheckStr +=

      "  if(fucCheckLength(document."+formName+"."+inputName+".value)<"+MinLength+" || " + "\n" +

      "    fucCheckLength(document."+formName+"."+inputName+".value)>"+MaxLength+") {" + "\n" +

      "    alert(\""+errorMsg+".\");" + "\n" +

      "    document."+formName+"."+inputName+".focus();" + "\n" +

      "    return(false);" + "\n" +

      "  }" + "\n\n";

  }

  /***************************************************************************\

   *        public: check the email

   *        检查录入框值是否是正确的EMAIL格式

  \***************************************************************************/

  public void emailCheck(String inputName, String errorMsg)    {

    this.errorCheckStr +=

      "  if(chkemail(document."+formName+"."+inputName+".value) == 0) {" + "\n" +

      "    alert(\""+errorMsg+".\");" + "\n" +

      "    document."+formName+"."+inputName+".focus();" + "\n" +

      "    return(false);" + "\n" +

      "  }" + "\n\n";

  }

  /***************************************************************************\

   *        public: check the telephone number

   *        检查录入框值是否是电话号码

  \***************************************************************************/

  public void telCheck(String inputName, String errorMsg)    {

    this.errorCheckStr +=

      "  if(fucCheckTEL(document."+formName+"."+inputName+".value) == 0) {" + "\n" +

      "    alert(\""+errorMsg+".\");" + "\n" +

      "    document."+formName+"."+inputName+".focus();" + "\n" +

      "    return(false);" + "\n" +

      "  }" + "\n\n";

  }

  /***************************************************************************\

   *        public: check if the input value contian the prefered string

   *        检查录入框值是否是包含给定字串

  \***************************************************************************/

  public void stringCheck(String inputName, String errorMsg, String string)    {

    this.errorCheckStr +=

      "  if(document."+formName+"."+inputName+".value.indexOf(\""+string+"\") != 0) {" + "\n" +

      "    alert(\""+errorMsg+".\");" + "\n" +

      "    document."+formName+"."+inputName+".focus();" + "\n" +

      "    return(false);" + "\n" +

      "  }" + "\n\n";

  }

  /***************************************************************************\

   *        public: check if the input value contain the denyed string

   *        检查录入框值是否是包含给禁止的字串

  \***************************************************************************/

  public void denyStrCheck(String inputName, String errorMsg, String string)    {

    this.errorCheckStr +=

      "  if (document."+formName+"."+inputName+".value.length == 0 || " + "\n" +

      "    document."+formName+"."+inputName+".value.indexOf(\""+string+"\") != -1) {" + "\n" +

      "    alert(\""+errorMsg+".\");" + "\n" +

      "    document."+formName+"."+inputName+".focus();" + "\n" +

      "    return(false);" + "\n" +

      "  }" + "\n\n";

  }

  /***************************************************************************\

   *        public: check the YYYY-MM-DD format date

   *        检查录入框值是否是YYYY-MM-DD的日期格式

  \***************************************************************************/

  public void dateCheck(String inputName, String errorMsg)    {

    this.errorCheckStr +=

      "  if(chkdate(document."+formName+"."+inputName+".value) == 0) {" + "\n" +

      "    alert(\""+errorMsg+".\");" + "\n" +

      "    document."+formName+"."+inputName+".focus();" + "\n" +

      "    return(false);" + "\n" +

      "  }" + "\n\n";

  }

  public void neededFunction()    {

    this.errorCheckStr +=

      "//函数名：fucCheckNUM" + "\n" +

      "//功能介绍：检查是否为数字" + "\n" +

      "//参数说明：要检查的数字" + "\n" +

      "//返回值：1为是数字，0为不是数字" + "\n" +

      "function fucCheckNUM(NUM) {" + "\n" +

      "  var i,j,strTemp;" + "\n" +

      "  strTemp=\"0123456789\";" + "\n" +

      "  if ( NUM.length == 0) return 0;" + "\n" +

      "  for (i=0;i<NUM.length;i++)    {" + "\n" +

      "    j = strTemp.indexOf(NUM.charAt(i));" + "\n" +

      "    if (j==-1) {" + "\n" +

      "      //说明有字符不是数字" + "\n" +

      "      return 0;" + "\n" +

      "    }" + "\n" +

      "  }" + "\n" +

      "  //说明是数字" + "\n" +

      "  return 1;" + "\n" +

      "}" + "\n\n" +

      "//函数名：fucCheckLength" + "\n" +

      "//功能介绍：检查字符串的长度" + "\n" +

      "//参数说明：要检查的字符串" + "\n" +

      "//返回值：长度值" + "\n" +

      "function fucCheckLength(strTemp) {" + "\n" +

      "  var i,sum;" + "\n" +

      "  sum=0;" + "\n" +

      "  for(i=0;i<strTemp.length;i++) {" + "\n" +

      "    if ((strTemp.charCodeAt(i)>=0) && (strTemp.charCodeAt(i)<=255))" + "\n" +

      "      sum=sum+1;" + "\n" +

      "    else" + "\n" +

      "      sum=sum+2;" + "\n" +

      "  }" + "\n" +

      "  return sum;" + "\n" +

      "}" + "\n\n" +

      "//函数名：chkemail" + "\n" +

      "//功能介绍：检查是否为Email Address" + "\n" +

      "//参数说明：要检查的字符串" + "\n" +

      "//返回值：0：不是  1：是" + "\n" +

      "function chkemail(a)    {" + "\n" +

      "  var i=a.length;" + "\n" +

      "  var temp = a.indexOf('@');" + "\n" +

      "  var tempd = a.indexOf('.');" + "\n" +

      "  if (temp > 1) {" + "\n" +

      "    if ((i-temp) > 3) {" + "\n" +

      "      if (tempd!=-1) {" + "\n" +

      "        return 1;" + "\n" +

      "      }" + "\n" +

      "    }" + "\n" +

      "  }" + "\n" +

      "  return 0;" + "\n" +

      "}" + "\n\n" +

      "//函数名：fucCheckTEL" + "\n" +

      "//功能介绍：检查是否为电话号码" + "\n" +

      "//参数说明：要检查的字符串" + "\n" +

      "//返回值：1为是合法，0为不合法" + "\n" +

      "function fucCheckTEL(TEL) {" + "\n" +

      "  var i,j,strTemp;" + "\n" +

      "  strTemp=\"0123456789-()#\";" + "\n" +

      "  if (TEL.length == 0) return 0;" + "\n" +

      "  for (i=0;i<TEL.length;i++) {" + "\n" +

      "    j=strTemp.indexOf(TEL.charAt(i));" + "\n" +

      "    if (j==-1) {" + "\n" +

      "      //说明有字符不合法" + "\n" +

      "      return 0;" + "\n" +

      "    }" + "\n" +

      "  }" + "\n" +

      "  //说明合法" + "\n" +

      "  return 1;" + "\n" +

      "}" + "\n\n" +

      "//函数名：chkdate    (YYYY-MM-DD)" + "\n" +

      "//功能介绍：检查是否为日期" + "\n" +

      "//参数说明：要检查的字符串" + "\n" +

      "//返回值：0：不是日期  1：是日期" + "\n" +

      "function chkdate(datestr) {" + "\n" +

      "  var lthdatestr" + "\n" +

      "  if (datestr != \"\")" + "\n" +

      "    lthdatestr= datestr.length ;" + "\n" +

      "  else" + "\n" +

      "    lthdatestr=0;" + "\n" +

      "  var tmpy=\"\";" + "\n" +

      "  var tmpm=\"\";" + "\n" +

      "  var tmpd=\"\";" + "\n" +

      "  //var datestr;" + "\n" +

      "  var status;" + "\n" +

      "  status=0;" + "\n" +

      "  if ( lthdatestr== 0)" + "\n" +

      "    return 0;" + "\n" +

      "  for (i=0;i<lthdatestr;i++) {" + "\n" +

      "    if (datestr.charAt(i)== '-') {" + "\n" +

      "      status++;" + "\n" +

      "    }" + "\n" +

      "    if (status>2) {" + "\n" +

      "      return 0;" + "\n" +

      "    }" + "\n" +

      "    if ((status==0) && (datestr.charAt(i)!='-')) {" + "\n" +

      "      tmpy=tmpy+datestr.charAt(i)" + "\n" +

      "    }" + "\n" +

      "    if ((status==1) && (datestr.charAt(i)!='-')) {" + "\n" +

      "      tmpm=tmpm+datestr.charAt(i)" + "\n" +

      "    }" + "\n" +

      "    if ((status==2) && (datestr.charAt(i)!='-')) {" + "\n" +

      "      tmpd=tmpd+datestr.charAt(i)" + "\n" +

      "    }" + "\n" +

      "  }" + "\n" +

      "  year=new String (tmpy);" + "\n" +

      "  month=new String (tmpm);" + "\n" +

      "  day=new String (tmpd)" + "\n" +

      "  if ((tmpy.length!=4) || (tmpm.length>2) || (tmpd.length>2)) {" + "\n" +

      "    return 0;" + "\n" +

      "  }" + "\n" +

      "  if (!((1<=month) && (12>=month) && (31>=day) && (1<=day)) ) {" + "\n" +

      "    return 0;" + "\n" +

      "  }" + "\n" +

      "  if (!((year % 4)==0) && (month==2) && (day==29)) {" + "\n" +

      "    return 0;" + "\n" +

      "  }" + "\n" +

      "  if ((month<=7) && ((month % 2)==0) && (day>=31)) {" + "\n" +

      "    return 0;" + "\n" +

      "  }" + "\n" +

      "  if ((month>=8) && ((month % 2)==1) && (day>=31)) {" + "\n" +

      "    return 0;" + "\n" +

      "  }" + "\n" +

      "  if ((month==2) && (day==30)) {" + "\n" +

      "    return 0;" + "\n" +

      "  }" + "\n" +

      "  return 1;" + "\n" +

      "}" + "\n\n";

  }

  /*public static void main(String[] args) {

    ErrorCheck ec = new ErrorCheck("testFrom");

    String script = ec.ErrorCheckScript();

    System.out.println(script);

  } */

}


用 Servlets 动态生成图片

作者：NOVEMBER RAIN

email: haoy@dongyu.com.cn

日期：2000-11-24 17:25:25

Image I/O 包及对 JDK 的要求

    如果你的 servlet 要动态生成图象，首先你需要 image I/O，明确的说，你需要生成一图片来响应HTTP请求。核心 Java API 没有直接提供用于保持任何图象的功能。然而，你可以用 Sun 的 Java 1.1 类库来做 image I/O。而且 Sun 的 1.2 版的 image I/O 提供了对 JPEG 图象进行编码和解码的包。因为这些代码在 com.sun 包中，不是核心API的一部分，也不是标准的扩展包，因此，会影响代码的可移植性。

    使用 jdk 1.2 将比 jdk 1.1 版容易的多，本文介绍 jdk 1.2 下的实现方法。示例有三个步骤：创建、绘制、保存。在本例仅仅示范如何画一个椭圆，当然你可以根据你的需要修改。例子使用了 com.sun.image.codec.jpeg 包生成 JPEG 文件。如果要生成 GIF 文件，你可以使用第三方的包，如 Acme Labs 的 GIF Encoder。

1. 使用适当的尺寸创建一个 BufferedImage 对象：

BufferedImage

  image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

2. 在 image 对象上绘制你想要的图象。假设你想要一个白色的背景，你需要画一个白色填充的矩形。否则背景将是缺省的黑色：

  Graphics g = image.getGraphics();

    g.setColor(Color.white);

    g.fillRect(0,0, width, height);

    g.setColor(Color.red);

    g.drawOval(0, 0, width, height);

3. 使用 com.sun.image.codec.jpeg 包输出 BufferedImage：

  response.setContentType(”image/jpeg”);

  ServletOutputStream out = response.getOutputStream();

  JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);

  encoder.encode(image);

  out.close();


用javamail发邮件(含附件),用jBuilder3实现

作者：◆后街男孩◆

日期：2000-12-14 17:21:16

//Title:        Your Product Name

//Version:

//Copyright:    Copyright (c) 1999

//Author:       Your Name

//Company:      Your Company

//Description:  Your description

package Mail;

import java.awt.*;

import javax.swing.*;

import java.awt.event.*;

import java.util.*;

import java.io.*;

import javax.mail.*;

import javax.mail.internet.*;

import javax.activation.*;

public class SendMail extends Frame {

  Label label1 = new Label();

  TextField textField1 = new TextField();

  Label label2 = new Label();

  TextField textField2 = new TextField();

  Label label3 = new Label();

  TextArea textArea1 = new TextArea();

  Label label4 = new Label();

  TextField textField3 = new TextField();

  Button button1 = new Button();

  Button button2 = new Button();

  public SendMail() {

   enableEvents(AWTEvent.WINDOW_EVENT_MASK);

    try  {

      jbInit();

    }

    catch(Exception e) {

      e.printStackTrace();

    }

  }

    protected void processWindowEvent(WindowEvent e) {

    super.processWindowEvent(e);

    if(e.getID() == WindowEvent.WINDOW_CLOSING) {

      System.exit(0);

    }

  }

  public static void main(String[] args) {

    SendMail sendMail1 = new SendMail();

    sendMail1.setSize (400,400);

    sendMail1.show (true);

  }

  private void jbInit() throws Exception {

    label1.setBounds(new Rectangle(41, 38, 45, 23));

    label1.setText("收信人");

    this.setLayout(null);

    this.setSize (400,400);

    textField1.setBounds(new Rectangle(110, 36, 174, 23));

    label2.setBounds(new Rectangle(42, 75, 38, 23));

    label2.setText("主题");

    textField2.setBounds(new Rectangle(110, 76, 173, 23));

    label3.setBounds(new Rectangle(43, 148, 38, 23));

    label3.setText("内容");

    textArea1.setBounds(new Rectangle(110, 155, 256, 170));

    label4.setBounds(new Rectangle(45, 111, 44, 23));

    label4.setText("附件");

    textField3.setBounds(new Rectangle(110, 115, 173, 23));

    button1.setBounds(new Rectangle(70, 348, 88, 24));

    button1.setLabel("发送");

    button1.addActionListener(new java.awt.event.ActionListener() {

      public void actionPerformed(ActionEvent e) {

        button1_actionPerformed(e);

      }

    });

    button2.setBounds(new Rectangle(244, 348, 88, 24));

    button2.setLabel("重填");

    button2.addActionListener(new java.awt.event.ActionListener() {

      public void actionPerformed(ActionEvent e) {

        button2_actionPerformed(e);

      }

    });

    this.add(label1, null);

    this.add(textField1, null);

    this.add(textField2, null);

    this.add(textField3, null);

    this.add(textArea1, null);

    this.add(label2, null);

    this.add(label4, null);

    this.add(label3, null);

    this.add(button2, null);

    this.add(button1, null);

  }

  void button2_actionPerformed(ActionEvent e) {

    textField1.setText ("");

    textField2.setText ("");

    textField3.setText ("");

    textArea1.setText ("");

  }

  void button1_actionPerformed(ActionEvent e) {

    String to,from,subject,message,attachment;

    from="toone@mail.com";

    to=textField1.getText ();

    if(to.trim ().equals ("")){

      JOptionPane.showMessageDialog(this, "收信人不能为空!", "错误", JOptionPane.ERROR_MESSAGE);

      return;

      }

    message=textArea1.getText();

    attachment=textField3.getText ();

    if(message.trim ().equals ("")&&attachment.trim ().equals ("")){

      JOptionPane.showMessageDialog(this, "内容和附件不能都为空!", "错误", JOptionPane.ERROR_MESSAGE);

      return;

    }

    if(to.indexOf ("@")==-1)  {

      JOptionPane.showMessageDialog(this, "无效的收信人地址!", "错误", JOptionPane.ERROR_MESSAGE);

      return;

     }

     subject=textField2.getText ().trim ();

     if(subject.equals (""))

       if(JOptionPane.showConfirmDialog(this,"你不需要设置主题吗？","系统提示",0)!=0)

            return;

   File file=new File(attachment);

    if(!attachment.equals ("")){

        if(!file.isFile ()){

        JOptionPane.showMessageDialog(this, "无效的附件名!", "错误", JOptionPane.ERROR_MESSAGE);

        return;

      }

      }

    //以上程序是检验输入的有效性

  // create some properties and get the default Session

    Properties props = System.getProperties();

    props.put("mail.smtp.host", "192.168.0.1");

    Session session = Session.getDefaultInstance(props, null);

    session.setDebug(false);

  try{

   // create a message

        MimeMessage msg = new MimeMessage(session);

        msg.setFrom(new InternetAddress(from));

        InternetAddress[] address = {new InternetAddress(to)};

        msg.setRecipients(Message.RecipientType.TO, address);

        msg.setSubject(subject);

      msg.addHeader ("toone","fangjianhua");

      if(attachment.equals ("")){

         System.out.println ("This is plain mail");

          msg.setText (message);

      }

      else {

        System.out.println ("this is a multipart mail");

       // create and fill the first message part

          MimeBodyPart mbp1 = new MimeBodyPart();

          mbp1.setText(message);

        // create the second message part

          MimeBodyPart mbp2 = new MimeBodyPart();

     // attach the file to the message

        FileDataSource fds = new FileDataSource(file);

       mbp2.setDataHandler(new DataHandler(fds));

       mbp2.setFileName(fds.getName());

        // create the Multipart and its parts to it

        Multipart mp = new MimeMultipart();

        mp.addBodyPart(mbp1);

        mp.addBodyPart(mbp2);

        // add the Multipart to the message

        msg.setContent(mp);

      }

      msg.setSentDate(new Date());

        // send the message

      //for(int i=0;i<10;i++)

         Transport.send(msg);

      //System.out.println ("Send a mail success");

      JOptionPane.showMessageDialog(this, "邮件发送成功", "系统提示",JOptionPane.INFORMATION_MESSAGE );

     }

     catch(Exception ex){

       JOptionPane.showMessageDialog(this, "发送邮件失败", "错误", JOptionPane.ERROR_MESSAGE);

     }

  }

}


用Java绘制K线 (转)

作者：asp2001

email: asp2001@163.net

日期：2000-8-9 18:49:59

用Java绘制K线

黑龙江省牡丹江王哲

---- Java语言中的Applet（Java小程序）和Application（Java应用程序）是在结构和功能上都存在很大差异的两种不同的编程方式。Applet应用于Web页上，可做出多姿多彩的页面特效，给网站增辉添色；Application则与其他编程语言（如VB、VC）一样，可编制各种应用程序。

---- 本文要讨论的是第一种情况，在Web页上用Java Applet绘制K线图。

---- K线是股市行情分析中的一种参数指标，用股票每日的开盘价、最高价、最低价、收盘价及成交量等数据进行作图，配合五日、十日均线便可反映出一个阶段内该支股票的涨跌走势。

---- Java Applet所做的K线图多应用于证券类型的网站，以方便网上股民的分析操作。不过由于我国网速的关系，目前有些证券类网站把K线图做成了图片以节省下载的时间。当然，其绘制K线所用的工具（Java Applet）及绘制方法还是相同的。

---- 在编写绘制K线的Java Applet时，有几处关键问题要解决，即：1）多线程的使用；2）布局管理器的选择；3）数据输入、输出流的应用；4）设置、监听鼠标与键盘事件。

---- 下面通过一个实例，来详细阐述整个编程过程和方法。

---- 一、使用多线程

---- 一个完整的Java Applet包含四个方法，Init()、Start()、Stop()、Destroy()（即：初始化、开始、停止、清除），构成一个完整的生命周期，其运行次序也是由上而下顺序执行。

---- 在绘制K线图的过程中，除了要绘制窗体及代表股票升降的阴线、阳线矩形外，还要监听鼠标事件并同时绘制一个自由移动的“十字”游标，来定位显示所检索股票每日的各种价格数值（如开盘价、收盘价等）；这时，为了避免闪烁和减少重新计算的等待时间，除了重绘跟随鼠标移动的“十字”游标外，对页面的布局及阴线、阳线矩形等不进行重绘；这就需要应用多线程来完成该项任务。

---- 1．关于多线程的一些基本概念：

---- 多线程实际上就是启动另一个进程，其运行的过程独立于主程序之外，并从主程序的Start()方法载入、由Run()方法调用执行。实现多线程的方法有两种，即：创建Thread类的子类（类的继承）和实现runnable接口。

---- 为便于使用，Java把所有有关线程的功能封装成Java类库中的一个类：Java.lang.Thread。通过这个类，Java可以创建、控制和终止线程，完成有关多线程的所有操作。

---- 在Java语言中，一个子类只能继承一个超类（父类），由于我们所要编写的Java Applet是应用于网页中的，首先必须继承浏览器类（java.applet）；因此，在本例中我们通过实现runnable接口的方法来实现多线程，实现的语句如下：

---- public class StockApplet extends java.applet.Applet implements Runnable

---- 2．多线程的应用：

---- 首先，在Init()方法中对要创建的线程（M_pointThread）进行定义及初始化：

    Thread M_pointThread=null;

---- 然后，在Start()和Stop()方法中加入以下代码：

---- //当打开浏览器页面开始载入Java Applet代码时，执行start()方法

    public void start()

    {

if (M_pointThread==null)   //如果线程尚未产生

{

M_pointThread=new Thread(this);  //则创建一个新线程

M_pointThread.start();   //启动该线程

          }

     }

//在当前页面关闭或转向其他Web页面时，

调用stop()方法，以释放系统资源

     public void stop()

     {

             if (M_pointThread!=null)

 //如果线程尚在运行

              {

            M_pointThread.stop();

  //停止该线程

            M_pointThread=null;

//释放线程所占资源

               }

      }

---- 3．编写线程运行的代码：

public void run()

{

   M_graphics=getGraphics();

   M_graphics.setXORMode(Color.white);

 //采用异或方法绘制“十字”游标

   while(true)

   {

       try

      {

if(MouseMove==true)   //侦测到鼠标的移动后，

执行以下代码

{   //当鼠标位于以下区域（Java Applet布局）

内时，执行画线操作

if(x0 >50 && x0< 600){M_graphics.drawLine

(x0,30,x0,380);}

if(y0 >30 && y0 <380){M_graphics.drawLine

(50,y0,600,y0);}

if(X >50 && X< 600){M_graphics.drawLine

(X,30,X,380);}

if(Y >30 && Y< 380){M_graphics.drawLine

(50,Y,600,Y);}

MouseMove=fase;

x0=X;     //传递当前座标参数

           y0=Y;

        }

}catch(NullPointerException npe){}

  //捕获可能出现的异常

  }

---- 二、布局管理器

---- 本例中的Java Applet运行时，要在页面上Java Applet的运行区域内绘制相应的“标签”来显示不同的数值，并要加入一个“文本框”来接收输入的股票代码。对这些“标签”和“文本框”位置及大小的设定，便属于布局管理的应用范畴。

---- Java语言中提供的布局管理器种类有：边界式布局、卡片式布局、流式布局和网格式布局等，各有不同的特点，可根据实际需要选用；但有最大自由设计空间的是“无布局管理器”——即不使用任何布局格式，而通过手工方式添加组件到页面布局的绝对位置上。本例中使用的便是“无布局管理器”。

---- 在使用“无布局管理器”时，首先要作出声明，即：

setLayout(null);

然后用reshape()方法指定组件的具体位置和尺寸，

基本语句如下所示：

Label label1=new Label();

      //定义标签

    this.add(label1);

              //把标签加入布局管理器

    label1.reshape(10,10,30,30);

    //在指定位置绘制标签

---- 对于多个标签和文本框，参照此格式添加即可。

---- 三、输入、输出数据流

---- Java Applet在客户端浏览器上运行时，要从服务器端读取股票的相关数据进行做图，这就涉及到Java数据流的运用。

---- Java.io包提供了多个用于与各种I/O设备交换信息的类，其最顶层的两个类便是输入流类（InputStream）和输出流类（OutputStream）。

---- 为了便于实现，在本例中我们先在服务器端把股票数据库（如：show128.dbf）中的有关字段写入一文本文件中（其格式为：第一行为股票的汉字名称，以后每一行内包括：开盘日期、开盘价、最高价、最低价、收盘价、成交量），并以该支股票的代码作为文件名（如：600001.txt）；然后，再用Java Applet从服务器端的文本文件中读取数据。这样，就把对服务器端数据库的访问转化为对文件的I/O操作，实现的语句如下：

URL urlc=new URL("http://127.0.0.1/temp

/"+FileName+".txt");

    BufferedReader

bis=new BufferedReader

(new InputStreamReader(urlc.openStream()));

---- 注：第一条语句中的http://127.0.0.1/temp/为在本机执行调试操作的URL地址，在上传到服务器上后要做相应的修改；FileName为要读取的文本文件名。

---- 第二条语句定义了读取输入数据流的方法。

---- 四、监听键盘及鼠标事件

---- 在Java Applet小程序中，单击按钮、键入文本、使用鼠标或执行任何与界面相关的动作时，就发生一个事件，小程序就会作出适当的反应。

---- 在本例中，我们通过使用action()方法来获得Java Applet小程序运行时所发生的事件。语句格式如下：

public boolean action(Event evt,Object arg)

    {

        FileName=text1.getText();

//提取文本框中输入的参数

        ReadData();

//调用读数据流数据的方法

        return true;

     //处理完毕，不需要其他方法再做处理

    }

---- 上面的action()方法中含有两个参数：一个是Event类的一个对象evt；另一个是Object类的一个对象arg。Event对象告诉我们发生了哪种事件，而Object对象将进一步告诉我们有关该事件的情况。每当有Event监听的事件发生时，Java Applet便自动调用该action()方法。

---- 至此，在掌握了上述编程中的一些要点后，我们便可以成功地编制出一个完整的绘制K线的Java Applet小程序了（完整的源代码附后）。

---- 五、编制HTML文档（Stock.html）

---- 程序编制完成后，另外要做的是设置在Web页面上调用Java Applet的页面，即编制HTML文档。Stock.html文档的示例代码如下：

< HTML >< HEAD >< TITLE >股票K线图< /TITLE >< /HEAD >

< body bgcolor=#00ffff >

< APPLET

code=StockApplet.class codeBase=C:\javatemp\Project1\

 name=StockApplet

style="FONT-FAMILY: sans-serif;

 HEIGHT: 410px; WIDTH: 610px"  >

    < /APPLET >

    < /body >

< /HTML >

---- 在< APPLET >< /APPLET >标签对内，是对Java Applet小程序的调用代码。其中的StockApplet.class是经编译后生成的class文件，codeBase指向的是class文件所存放的路径。如果class文件与HTML文档存放在同一目录下，则可以省略这一项。

---- 至此，编制Java Applet小程序的工作便全部完成。

---- 在个人计算机上进行调试时，首先要安装个人Web服务，然后，把StockApplet.class与HTML文档一起拷贝到本机的WWW服务目录下（如：C:\Inetpub\wwwroot\），打开浏览器，在地址栏内输入http://127.0.0.1/Stock.html

---- 调试通过后，便可上传至服务器投入运行了。


用Java绘制K线 (转2)

作者：asp2001

email: asp2001@163.net

日期：2000-8-9 18:50:37

---- 附：StockApplet.java

import java.applet.*;

import java.awt.*;

import java.io.*;

import java.net.*;

public class StockApplet extends java.applet.Applet implements Runnable

{ //以下是公用变量的声明部分

    int Move_Length=0,Move_Sum=0;

    String FileName,Name_Str,Content_Date;

    int SP[]=new int[2000];

    int KP[]=new int[2000];

    int JD[]=new int[2000];

    int JG[]=new int[2000];

    int Mid_Worth[]=new int[2000];

    String myDate[]=new String[2000];

    double CJL[]=new double[2000];

    double MaxCJL,MidCJL;

    Label label[]=new Label[10];

    int MaxWorth,MinWorth;

int x_move0,x_move1,MaxLength=0;

    int x0,y0,X,Y,Record_Num;

    boolean Mouse_Move,Name_Change=true;

    int JX_Five1,JX_Five2,JX_Ten1,JX_Ten2;

public void init()

{

  TextField text1=new TextField();

  Thread M_pointThread=null;

      setLayout(null);                  //声明布局管理器

      this.setBackground(Color.white);    //设置背景色

  this.setForeground(Color.black);     //设定文字颜色

      for(int i=1;i< 10;i++)      //以下循环用于向布局中添加标签

      {

        label[i]=new Label();

        this.add(label[i]);

        label[i].reshape(i*80-65,10,50,15);

        if(i==2){label[i].reshape(80,10,70,15);}

        if(i==7){label[i].reshape(510,10,80,15);}

        if(i >7){label[i].reshape((i-8)*490+45,380,70,15);}

      }

      FileName="000001";        //程序启动时的默认股票代码

      Name_Str="上证指数";

      this.add(text1);             //向布局中添加文本框

      text1.reshape(150,385,70,20);

      text1.getText();             //取得文本框中输入的内容

}

public void start()

{    //创建并启动多线程

    if (M_pointThread==null)

    {

        M_pointThread=new Thread(this);

        M_pointThread.start();

    }

}

public void run()

{    //运行多线程

    Graphics M_graphics;

    M_graphics=getGraphics();

    M_graphics.setXORMode(Color.white);

    while(true)  //绘制“十字”游标

    {

        try

        {

            if(Mouse_Move==true)

            {

                if(x0 >50 & x0< 600){M_graphics.drawLine(x0,30,x0,380);}

                if(y0 >30 & y0< 380){M_graphics.drawLine(50,y0,600,y0);}

                if(X >50 & X< 600){M_graphics.drawLine(X,30,X,380);}

                if(Y >30 & Y< 380){M_graphics.drawLine(50,Y,600,Y);}

                Mouse_Move=false;

                x0=X;

                y0=Y;

            }

        }catch(NullPointerException npe){}

    }

}

public boolean action(Event evt,Object arg)   //监测事件

    {

        FileName=text1.getText();    //取得新文件名

        repaint();

     //将各变量恢复初始状态

        Name_Change=true;

        Move_Length=0;

        Move_Sum=0;

        MaxLength=0;

        MaxCJL=0;

        return true;

    }

public boolean keyDown(Event evt,int key)  //检测键盘事件

    {

        if(key==10)    //检测是否是回车键

        {

            Event event=new Event(text1,Event.ACTION_EVENT,"text1");

            deliverEvent(event);

            return true;

        }

        return false;

    }

public boolean mouseDown(Event evt,int x_d,int y_d)   //检测按下鼠标按键事件

{

    x_move0=x_d;     //记录鼠标当前的横座标

    x0=x_d;          //把当前座标传递给另一线程

    y0=y_d;

    return true;

}

public boolean mouseUp(Event evt,int x_up,int y_up)  //检测释放鼠标按键事件

{

    int Screen=520;      //定义在页面上的显示区域

    x_move1=x_up;      //记录鼠标当前的横座标

    Move_Length=((int)(x_move1-x_move0)/10)*10;    //计算鼠标移动距离

    Move_Sum=Move_Sum+Move_Length;

    if(Move_Sum< 0){Move_Sum=0;}    //控制K线的拖动范围

    if(Move_Sum >MaxLength-Screen){Move_Sum=MaxLength-Screen-10;}

    x0=x_up;         //把当前座标传递给另一线程

    y0=y_up;

    MaxWorth=JG[Move_Sum];

    MinWorth=JD[Move_Sum];

    MaxCJL=CJL[Move_Sum];

//以下循环用于查找页面显示区域内的最高、最低值及成交量

    for(int i=Screen+Move_Sum;i >Move_Sum;i-=10)

    {

        if(MaxWorth< JG[i]){MaxWorth=JG[i];}

        if(MinWorth >JD[i]){MinWorth=JD[i];}

        if(MaxCJL< CJL[i]){MaxCJL=CJL[i];}

    }

    MidCJL=MaxCJL/2;

    for(int i=1;i< 7;i++)            //计算出在纵座标上显示的中间值

    {

        Mid_Worth[i]=MinWorth+(MaxWorth-MinWorth)/7*(7-i);

    }

    repaint();

    return true;

}

public boolean mouseMove(Event evt,int x_m,int y_m)   //检测鼠标移动事件

{

   int Axsis,MNumberR;

   Mouse_Move=true;

   Axsis=560+Move_Sum-((int)(x_m/10))*10;        //设定横座标范围

   if(Axsis >MaxLength){Axsis=MaxLength;}

   //在以下区域内，把鼠标当前横座标处的各种股票参数显示在相应的标签上

   if(x_m >50&x_m< 600)

   {

       label[2].setText(myDate[Axsis]);

       label[3].setText(String.valueOf((float)KP[Axsis]/100));

       label[4].setText(String.valueOf((float)JG[Axsis]/100));

       label[5].setText(String.valueOf((float)JD[Axsis]/100));

       label[6].setText(String.valueOf((float)SP[Axsis]/100));

       label[7].setText(String.valueOf((int)CJL[Axsis]));

       label[8].setText(myDate[510+Move_Sum]);

       MNumberR=Move_Sum;

       if(Move_Sum >30){MNumberR=Move_Sum-30;}

       label[9].setText(myDate[MNumberR]);

       X=x_m;        //把当前座标传递给另一线程

       Y=y_m;

   }

   return true;

}

public void stop()        //当页面关闭或转向其他页面时，释放系统资源

{

    if (M_pointThread!=null)

    {

        M_pointThread.stop();

        M_pointThread=null;

    }

}

public void paint(Graphics g)  //绘图方法

{

    if(Name_Change==true)  //若改变了查询股票的代码

    {

        ReadData();        //则重新读取数据

        Name_Change=false;

    }

    PaintFrame(g);          //调用绘制页面区域方法

    PaintData(g);           //调用绘制K线数据方法

    x0=-1;

    y0=-1;

}

private void PaintFrame(Graphics g)  //绘制页面区域方法

{

int nWidth =600;

int nHeight=260;

g.setColor(Color.white);

g.fillRect(0,0,600,420);

g.setColor(Color.black);

g.fillRect(50,30,550,350);

g.setColor(Color.white);

g.drawLine(50,nHeight,nWidth,nHeight);    //画出上下两区域间的分隔线

g.setColor(Color.black);      //设置字体颜色

    for(int i=1;i< 7;i++)      //绘出纵座标中间数值

    {

        g.drawString(String.valueOf((float)Mid_Worth[i]/100),10,55+i*30);

    }

    g.drawString(String.valueOf((int)MaxCJL),2,270);

    g.drawString(String.valueOf((int)MidCJL),8,325);

    g.drawString("0",40,380);

}

public void PaintData(Graphics g)      //绘制K线数据方法

{

    int SPPoint[]=new int[2000];

    int KPPoint[]=new int[2000];

    int JGPoint[]=new int[2000];

    int JDPoint[]=new int[2000];

    double CJLPoint[]=new double[2000];

    int Left_Start=40,reDraw=250;        //设置绘图左边界及参数

    int i=0;

    int JxTemp1=0,JxTemp2=0;

//以下语句在布局区域内绘出固定信息

    g.setColor(Color.red);

    g.drawString("牡丹江",5,45);

    g.drawString("信息港",5,60);

    g.setColor(Color.black);

    g.drawString("开盘",150,20);

    g.drawString("最高",230,20);

    g.drawString("最低",310,20);

    g.drawString("收盘",390,20);

    g.drawString("成交量",470,20);

    g.drawString("要查询其他股票,请输入股票代码,然后回车。",230,400);

    for(int N=MaxLength;N >=0;N-=10)        //该循环用于在绘图区域内作图

    {            //计算股票各参数在绘图区域内要显示的相对值

        KPPoint[N]=((KP[N]-MinWorth)*200/(MaxWorth-MinWorth));

        SPPoint[N]=((SP[N]-MinWorth)*200/(MaxWorth-MinWorth));

        JGPoint[N]=((JG[N]-MinWorth)*200/(MaxWorth-MinWorth));

        JDPoint[N]=((JD[N]-MinWorth)*200/(MaxWorth-MinWorth));

        CJLPoint[N]=(CJL[N]*120/MaxCJL);

        for(int Num=0;Num< 5;Num++)        //计算五日均线

        {

            JxTemp1=JxTemp1+(SPPoint[N+(Num+1)*10]);

            JxTemp2=JxTemp2+(SPPoint[N+Num*10]);

        }

        JX_Five1=JxTemp1/5;

        JX_Five2=JxTemp2/5;

        JxTemp1=0;

        JxTemp2=0;

        for(int Num=0;Num< 10;Num++)        //计算十日均线

        {

            JxTemp1=JxTemp1+(SPPoint[N+(Num+1)*10]);

            JxTemp2=JxTemp2+(SPPoint[N+Num*10]);

        }

        JX_Ten1=JxTemp1/10;

        JX_Ten2=JxTemp2/10;

        JxTemp1=0;

        JxTemp2=0;

        if(N< 520+Move_Sum)        //若位于显示区域内，则绘制K线

        {

          i=i+10;                  //横座标按10象素递增

          g.setColor(Color.white);

          g.drawLine(i+Left_Start,220-JX_Five1,i+Left_Start+10,220-JX_Five2);

          g.setColor(Color.yellow);

          g.drawLine(i+Left_Start,220-JX_Ten1,i+Left_Start+10,220-JX_Ten2);

          if(KP[N]< =SP[N])          //如果开盘价小于收盘价，则绘制阳线

          {       //绘制空心矩形

         g.setColor(Color.red);

             g.drawRect(i+Left_Start,reDraw-SPPoint[N],8,(SPPoint[N]-KPPoint[N]));

              //绘制最高、最低价

             if(SPPoint[N]==KPPoint[N]){g.drawRect(i+Left_Start,reDraw-SPPoint[N],8,1);}

            if(JGPoint[N] >SPPoint[N]){g.drawLine(i+Left_Start+4,reDraw-SPPoint[N],  i+Left_Start+4,reDraw-JGPoint[N]);}

        if(JDPoint[N]< KPPoint[N]){g.drawLine(i+Left_Start+4,reDraw-JDPoint[N],  i+Left_Start+4,reDraw-KPPoint[N]);}

              //在下方对应位置绘制成交量实心矩形

               g.fillRect(i+Left_Start,380-(int)CJLPoint[N],8,(int)CJLPoint[N]);

     }

        else       //如果开盘价大于收盘价，则绘制阴线

        {         //绘制实心矩形

          g.setColor(Color.green);

      g.fillRect(i+Left_Start,reDraw-KPPoint[N],8,(KPPoint[N])-SPPoint[N]);

               //绘制最高、最低价

          g.drawLine(i+Left_Start+4,reDraw-KPPoint[N],i+Left_Start+4,reDraw-JGPoint[N]);

          g.drawLine(i+Left_Start+4,reDraw-SPPoint[N],i+Left_Start+4,reDraw-JDPoint[N]);

               //在下方对应位置绘制成交量实心矩形

          g.fillRect(i+Left_Start,380-(int)CJLPoint[N],8,(int)CJLPoint[N]);

        }

        if(i >540){N=0;}  //如果显示区域已全部绘制完毕，则退出循环

    }

}

}

public void ReadData()              //读取股票数据的方法

{

    int piont=1,Piont_Num;

Integer tempInteger;

    Float tempFloat , Content_CJL;

    Float content_Num[]=new Float[5];

    int Count_Number[]=new int[10];

String content;

    try

    {    //定位文件的URL地址

        URL urlc=new URL("http://127.0.0.1/temp/"+FileName+".txt" ;

    //打开数据流

        BufferedReader bis=new BufferedReader(new InputStreamReader(urlc.openStream()));

        Name_Str=bis.readLine();            //读取股票的汉字名称

        label[1].setText(Name_Str);          //在相应标签内显示

        while((content=bis.readLine())!=null)   //按行读取整个文本文件内容

        {

           Piont_Num=content.indexOf(' ',piont);        //查找空格位置

           Content_Date=content.substring(1,Piont_Num);    //取出日期字符串

           myDate[MaxLength]=Content_Date;

           piont=Piont_Num+1;

           for(int i=1;i< 5;i++)      //从读入的一行中分离出对应数值

           {

               Piont_Num=content.indexOf(' ',piont);

               content_Num[i]=new Float(content.substring(piont,Piont_Num));

               piont=Piont_Num+1;

           }

           KP[MaxLength]=(int)(content_Num[1].floatValue()*100);

           JG[MaxLength]=(int)(content_Num[2].floatValue()*100);

           JD[MaxLength]=(int)(content_Num[3].floatValue()*100);

           SP[MaxLength]=(int)(content_Num[4].floatValue()*100);

          //最后取出成交量的数值

           Content_CJL=new Float(content.substring(piont,content.length()-1));

           CJL[MaxLength]=(int)Content_CJL.floatValue();

           piont=1;

           Piont_Num=0;

           MaxLength=MaxLength+10;    //读取下一行（记录数以10为单位递增）

        }

        MaxWorth=JG[Move_Sum];

        MinWorth=JD[Move_Sum];

        MaxCJL=CJL[Move_Sum];

    //找出在页面布局内的最高、最低值及最大成交量

        for(Record_Num=540+Move_Sum;Record_Num >Move_Sum;Record_Num-=10)

        {

            if(MaxWorth< JG[Record_Num]){MaxWorth=JG[Record_Num];}

            if(MinWorth >JD[Record_Num]){MinWorth=JD[Record_Num];}

            if(MaxCJL< CJL[Record_Num]){MaxCJL=CJL[Record_Num];}

        }

        MidCJL=MaxCJL/2;

        for(int i=1;i< 7;i++)      //计算中间值

        {

            Mid_Worth[i]=MinWorth+(MaxWorth-MinWorth)/7*(7-i);

        }

        bis.close();              //关闭数据流

        repaint();

    }catch(NullPointerException npe){  //捕捉可能出现的异常

    }catch(IOException e){}

}

}

__________________________

我用javac StockApplet.java编译出来8个错误，想想也是，没人把完全正确的程序帖出来的，靠这个吃饭的，总要保留一点。


用java中的canendal 如何计算两个日期之间的每一日？

作者：sonymusic

email: sonymusic@china.com

日期：2001-5-15 13:16:09

其实很简单

因为JAVA会自动将不合法的日期转换成合法的。

比如：2000-02-28，日期再加1天，就会变成2000-03-01，所以你只需要从起始日期开始，循环加（或减）1天，直到等于终止日期就可以得到中间的每一天了。


用JAVA转换简繁体的基础知识 (不用对照表，用算法实现)

作者：姜丝

日期：2001-3-9 18:04:56

汉字编码标准与识别(一)

代码页(Code Page)初识

本节是根据以下文章编写出来的，建议认真研读这些专家的高论。

参考1 <> 张 轴 材

<<计算机世界>>周报 97-1-17

参考2 <<张轴材 谈汉字交换码标准建立历程>> <<计算机世界>>周

报记者 黄伟敏 肖春江 99-8-30

参考3 <<中文平台把住“根”留住>> 吴健 <<中国计算机报>>

出版日期：1998-12-21　总期号：348　本年期号：51

参考4 <<为种种UNIX中文平台号脉>> 孙玉芳 <<中国计算机用户>>

出版日期：1998-07-06　总期号：323　本年期号：26

参考5 CJK.INF:ftp://ftp.ora.com/pub/examples/nutshell/ujip/

doc/cjk.inf

因为本人只是业余水平，不是专家，对于参考资料中许多术语还不

理解，更没有见过任何一种标准的正式文本，错误和模糊之处再所

难免。同时，因为国家有关部门对于宣传，推广和贯彻国家标准方

面力度不够，致使象我这样的初学者或初涉该领域的小企业因信息

资源不足而处于不利的竞争地位。

ASCII制订的时候，并没有考虑对多语种，特别是对象中国汉字这样

的象形文字的支持。为此后来又提出了不少解决方案，其中代码页

体系(ISO2022)是现在普遍实行的方案，而ISO10646/GB13000/Unicode

是今后发展的方向。

中国的汉字编码标准GB2312是7bits标准，具体说是双7位字节标准。

而ASCII是单7位字节标准,计算机怎么区分呢?一种是在第八位置"1",

提示计算机转入双字节编码,这是最常见的一种实现,也叫EUC

(Extended Unix Code)编码.另一种是用特殊标记提示计算机转入双

字节编码,如HZ编码就是用开始，用结束的块标识双字节编码区.它们

都是GB2312的一种实现.对象中国汉字这样的象形文字体系，代码页

是根据各个国家，地区或行业标准，按照EUC方式编码。代码页向下

兼容ASCII，是一种不等长编码。会带来代码的复杂性，同时还会引

起因代码页切换而带来的乱码问题。

Unicode是一种多字节等长编码。ISO10646/GB13000/Unicode现已在

UCS2上实现一致，也就是已实现双字节编码标准。下面所讨论的

ISO10646/GB13000/Unicode，就只是指UCS2这种情况。Unicode对

ASCII采取前面加"0"字节的策略实现等长兼容。如"A"的ASCII码为0x41,

Unicode码就为0x00,0x41。

这里主要从国家标准(GB)系列入手了解Unicode。如果不是看了参考5

(英文)，我还不知道国家关于汉字编码的标准如此之多。中国人居然

要从英文资料里了解汉字编码标准，实在是很无奈的事情。

常用中文编码标准 资料来源:CJK.INF

GB2312-1980(GB0)(简体) GB7589-1987(GB2)(简体)

GB7590-1987(GB4)(简体) GB13000-1993

GB6345.1-1986(GB0修正)

GB8565.2-1988(GB8,GB0扩充)

GB/T12345-90(GB1)(繁体) GB/T13131-9X(GB3)(繁体)

GB/T13132-9X(GB5)(繁体)

其中横向表示字符集系列。纵向表示各个系列的发展标准。其中

GB2312是基本集，也就是目前最常用的标准。GB7589/GB7590是扩展

集，使用时可能不能和GB2312共存，需要切换使用。GB7589/GB7590

是按部件(部首)和笔顺(笔画)排列，但具体有什么字，怎么排列，

用在什么领域，不清楚。GB2312系列经过两次修正和扩充，已和原

始的GB2312-1980标准有些不同(参考5)。因为没有标准文本，不知

道正在使用的字体是属于哪个标准。根据最新的Unicode3.0，国家

标准最新的是GB16500-95 ，更不知是哪个系列的了。ISO/IEC 10646

等同于GB13000-1993/JIS0221-1995/KSC5000-1995这些国家标准。

制订的目标是包容各语种的文字，其中以汉字最多(Unicode2.0有

20902个汉字)。关于标准的特点可以看参考1，制订过程中的风风

雨雨，可以看参考2。总之，这是一个我们国家参与并占主导地位

的国际标准。

GBK是GB2312向GB13000过渡的一个中间产物。它是GB2312的一次大

的扩展，编码向下兼容GB2312的EUC编码，字汇(字符集)和GB13000

相同，是GB2312的3倍。所以说GBK也包含BIG5，Shift-JIS，KSC的

字汇。注意只是包含字汇，而编码与原始的标准是不同的。在具体

应用中，用GBK字体就可以同时显示GB2312，BIG5，Shift-JIS，KSC

的字符串。但除了GB2312字符串，其它都要转换(convert)。

因为语焉不详，不清楚制订GBK时是谁占主导地位。因为有些英文资

料说是Microsoft制订了GBK，而国家方面也没有进行说明。目前从

这些参考资料只知道，94年ISO/IEC 10646发布后，Microsoft开发

Windows95中文版，要制订中文扩展编码。96年《汉字扩展内码规范》

GBK发布(参考1~3)。按标准发布比制定晚一年推算，这是95年的事。

Windows95及后续版本中文版支持GBK。

GB2312的EUC编码范围是第一字节0xA1~0xFE(实际只用到0xF7)，第

二字节0xA1~0xFE。GBK对此进行扩展。第一字节为0x81~0xFE，第二

字节分两部分，一是0x40~0x7E，二是0x80~0xFE。其中和GB2312相

同的区域，字完全相同。扩展部分大概是按部件(部首)和笔顺(笔画)

从GB13000中取出再排列入GBK中。因此GBK并不是GB13000，虽然两者

字汇相同，但编码体系不同。一个是ISO2022系列不等长编码，一个

是等长编码，并且编码区域也不同。注意到GBK实际上不是国家标准。

在此之前有一个GB2312基本集，在它之上是一个技术更先进的GB13000。

GBK只是一种过渡和扩展规范。所以在Unicode里有GB2312->Unicode,

GB12345->Unicode的转换表格，而没有GBK->Unicode转换表格。只有

Microsoft制作的Code Page 936(CP936.TXT)可以算作GBK->Unicode

转换表格。但要注意这是一个商业公司制作的文件，而不是国家或

国际标准组织制作的，有可能与标准有不一致的地方。最近在方正字

体网站发现一些有用的标准文件,有兴趣可以下载看看.但要注意

Gbk-big5.tab和Gb-big5.tab这两个文件有点瑕疵.

http://www.founderpku.com/fontweb/download/Gbk-big5.tab

http://www.founderpku.com/fontweb/download/Gb-big5.tab

http://www.founderpku.com/fontweb/gb2312.htm

http://www.founderpku.com/fontweb/gbk.htm

在使用这些转换表制作其它标准的相互转换表，会和传统的转换表

有所不同。如用GBK<=>Unicode<=>BIG5制作GBK<=>BIG5转换表，就

会和传统的GB<=>BIG5转换表有所不同。主要是汉字有简体和繁体。

前者是GBK(中的繁体字)<=>BIG5(繁体字)，后者是GB(简体)<=>BIG5(繁体)。

还有就是对一些制表符选用不同。对汉字繁简转换有兴趣的读者，可以看

http://www.basistech.com/articles/c2c.html

http://www.cjk.org

内码与字体的关系

虽然没有标准文本，但还是可以大致了解常用标准有那些字。TLC4.0的

字库带有GB2312，GB12345，BIG5，GBK标准的pcf字体。可以用xfd实用

程序查看。在http://www.debian.org/chinese

的pcf字体。如果安装了FreeType，可以使用xmbdfed软件查看TTF字体。

如果用MS WORD，可能会更简单些。

在日常使用中，我们实际上熟悉的是字码(内码).在中文WIN9X下,我们输

入一个双八位字节,就得到一个汉字,就会认为这双八位字节就是对应这

样的字形.这是错误的.其实内码对于字库来说,只是查找字形的索引.如

果换另一个编码标准的字体，同一个字符串就会呈现不同的字形，也就

是乱码。我见过GB2312,BIG5和ISO10646/GB13000的TTF字库.对于操作系

统和应用程序来说,最喜欢的自然是ISO10646/GB13000的TTF字库了.因为

这时只需提供一套代码和一套字库,修改外部配置文件,就可以用在不同的

语种环境.这就是国际化和本地化.其中有个技巧就是ISO10646/GB13000的

TTF字库可以在使用时可以通过重映射变成其它标准的字库.这时需要的是

GBK->Unicode,Big5->Unicode这些转换表.一个系统要升级支持Unicode3.0,

也难也不难.简单的地方是只需修改转换表就行了(如windows

ls*.*).

难的是要升级字库.开发字库是很困难的,可以到方正字库网站看看开发字

库的步骤.WIN9X使用的是北京中易公司的TTF字库,MS是不可能开发一套中

文字库的.我所见过的ISO10646/GB13000的TTF字库,最新的是99年版,Unicode2.1

,

方正字库.要想见到Unicode3.0的所有字形,也只有等这些专业字库开发商

做出来才行.如果现在就想看,只有问张轴材了.因为每通过一次新标准,中

国方面就要提供所有汉字的48x48高精密字形.使用TTF字体始终是诱人的话

题。但现在了解不多，只能简单谈谈从TTF字体生成bdf/pcf字体的问题。

因为现在中文pcf字体很少，只有宋体，仿宋，黑体，楷体四种。要想有更

多的字体，有个取巧的方法就是使用freetype库。用ttftobdf程序生成bdf

字体，再用bdftopcf程序生成pcf字体。但这种方法生成的字体缩放后比较

难看，而且不宜控制。这可能是ttf->bdf的转换过程丢失了信息，高宽比

也和标准的不一样。机器生成的东西就是机械，是不能和手绘的字体相比

的。同时，因为TTF技术已成熟，所以也没有必要继续开发更多的pcf字体。

X window将接受和大量使用TTF字体。而pcf字体今后主要用在标准字型

（如宋体），小点阵，网上快速下载传输方面。只有实际在X Window下用

过Unicode和TTF的字体，才会体会到使用Unicode和TTF，既是一种能力，

也是一种负担。因为不论是什么格式的字体文件，最后在使用时都转化为

内存里固定点阵字体。如果是16x16点阵，一个汉字就用32字节。Unicode3.0

有27786个汉字,至少需要868kb的内存。如果要中文英文美观一致，还得装

载大量的中文字体，所需内存可想而知。如果再使用TTF，还需要另一块内

存来运算和存储。因此，就算X Window提供了字体cache和deferglyphs，

还是于事无补。而我们常用的汉字其实很少。根据统计，常用汉字的频率，

前165个汉字频率和>50%，前1000个汉字频率和>95%；按小学教学经验，识

字900个左右，基本可以读书，看报，写作文；按小学教学大纲，小学毕业

识字2500字；GB2312的一级字库的频率和已>99%。我想我自己识字大约为

4000~5000，对比Unicode的汉字，好象一个文盲:-)。因此是用GB2312，还

是用GB13000，真是一个两难决择，我们也要为我们的选择付出代价。

最后通过内码与字体的关系，讨论UTF8的作用。

UTF8是现有ASCII系统转向Unicode系统的一个过渡解决方案。UTF8是保证

ASCII兼容性，再向大字符集方向扩展。这是Unicode推荐的方案。但是因

为解决问题的角度不同，对现有的中文系统不是好的解决方案。

CJK字符编码标准目前都为一字/两字节。中文在UCS2中的编码范围是

U+4E00~U+9FFFF。按照UTF8的编码规则，为一字/三字节，增加1/3的空间。

同时和现有的CJK系统不兼容。CJK系统要使用UTF8，先转换为UCS2，再转换

为UTF8。后一步简直是多此一举。因为从字库的角度看，字的编码只是字形

在字库中的索引。UTF8是变长码，不能直接做索引，需要转换为UCS2才能使

用字库。

随着GUI的发展，字库逐渐转向TTF。TTF字库的编码标准，有GB2312/GB2312

的EUC标准；BIG5标准；ISO10646标准。没有见过UTF8的TTF，也不知道CJK

这些国家有哪些系统使用了UTF8编码。

目前Unicodde有一个特点就是内核代码(CoreCode)。用户表面上可以继续使

用原有的编码标准，系统内部使用UCS2进行运算和操作。系统使用用户可改

变的标志或模块，以识别用户需要的编码标准，然后进行转换。这样，系统

只需提供一套ISO10646的TTF，不修改内部代码，就可以为多个用户同时提供

中文，日文，韩文的支持。Windows95及后面的中文版就是采用这个方案。现

有的X window的TTF服务器，X-TT和xfsft也可以使用这个方案。

前者在TurboLinux中文版里得到了实现，后者我试验过，效果还不错。还有

一个有趣的现象，就是红旗Linux1.1版所带的那个12点阵的pcf字体

/usr/X11R6/lib/X11/fonts/misc/gb12st.pcf.gz。这已不是严格意义上的

GB2312编码的字库了。用xfd实用程序查看，好象是从Unicode编码的TTF字体

转换来的，有些GBK的字，可惜太少。如果他们能出些GBK编码标准的pcf字体

就好了。

CJK系统转向UCS2与ASCII系统转向UTF8，两者的代码修改量是相当的。只是前

者多了转换表，需要内存多些。不过ASCII系统使用UCS2，需要增加50%的空间。

目前计算机里大多数还是ASCII的信息，看来这也是一个问题。


用JDBC访问一个数据库

作者：zergman

email: zergman@chinaasp.com

日期：6/12/2001 1:06:45 PM

发布者:flyfox

在这个练习里你将学习使用JDBC访问数据库和插入一个记录到数据库的基础知识。

在这个练习里，你将写RegistrationServlet类的register方法。这个类和你在Servlet1A练习里用的一样。在这个练习里，你将使用数据库的功能。这个练习将展示实现用户需求的步骤。

第一部分：理解regsiter()方法在RegistrationServlet中的工作原理

在这个练习里，你将在com.ibm.waslab.JDBC包上工作。扩展RegistrationServlet类，理解register()。这个方法抛一个SQLException。完整的方法声明是：

public synchronized void register(Properties formInput)

throws SQLException{

}

register()方法在servlet取得控制时从doPost()方法里被调用。register()方法实际上写一个新的记录到数据库。我们将在第二部分写register()方法。同时，我们需要确信一些设置步骤已经完成。

1．打开init()方法，在super.init()的调用后面输入以下的代码：

//Load JDBC driver for DB2

try

{

Class.forName(JKToysDBInfo.gerDriver());

}

catch (ClassNotFoundException e)

{

erroLog(“JDBC Driver not found”+e);

}

保存init()方法。记住，在驱动器管理可以得到一个连接以前，一个正确的数据库驱动器必须被servlet装载。每一个servlet只在servlet的init()方法里作一次。这个方法并不真地建立一个连接，它只是允许连接被建立。

注意：在这个情况下，我们装载DB2 app驱动器。这个驱动器是在数据库装在servlet将要运行的同一台机器上时使用的。如果你在访问另一台机器上的数据库，你要使用net驱动器：

COM.ibm.db2.jdbc.net.DB2Driver

当用net驱动器得到一个连接，你需要在getConnection()里使用的URL里提供更多的信息。数据库所在的机器名或URL，和DB2 Java Gateway侦听的端口号。一个有效的URL参数看上去是这样的：

jdbc:db2://servrid:8888/databasename

第二部分

1．现在，回去，重新打开register（）方法。代码的第一行创建一个数据库连接：

Connection conn=DriverManager.getConnection(URL,USER,PASSWORD);

你用驱动器管理器的静态方法getConnection()，把数据库的URL，一个有效的用户ID和口令传递给它。url,userid和password在类被装载时从一个属性文件里得到(看静态变量声明)。

2．现在到了有挑战性的部分了。我们要作的第一件事是找到已经被分配掉的最后一个客户号码，这样我们可以分配给正在注册的新客户一个新的客户号码（比以前的最高的还要高）。我们要存储这个新的客户号码在一个int变量名为nextId。为了做到这个，你会需要确信使用Statement类和ResultSet类。以下的SQL；

“SELECT MAX(CUSTNO) FORM”+DBOWNER+”.CUSTOMER”

将允许你获得当前最高的客户号。试试自己写这个代码，基于课程笔记的例子。如果你需要帮助，看答案页。

3．接着你将用一个prepared statement对象来把客户属性对象里的信息插入数据库。输入以下的行：

//Insert record in the database

PreparedStatement insertStatement=

conn.prepareStatement(“INSERT INTO”+DBOWNER+”.CUSTOMER(FNAME,LNAME,ADDR,CITY,STATE,AGE,ZIP,CUSTNO)”+”VALUES(?,?,?,?,?,?,?,?)”);

上面的行要求连接创建一个prepared statement对象叫insertStatement。SQL语句作为参数被传递。数据库行的每一列的值用问号代表。每一个问号必须被一个正确类型的值代替。输入以下行：

insertStatement.setString(1,formInput.getProperty(“FNAME”));

insertStatement.setString(2,formInput.getProperty(“LNAME”));

insertStatement.setString(3,formInput.getProperty(“ADDR”));

insertStatement.setString(4,formInput.getProperty(“CITY”));

insertStatement.setString(5,formInput.getProperty(“STATE”));

insertStatement.setString(6,formInput.getProperty(“AGE”));

insertStatement.setString(7,formInput.getProperty(“ZIP”));

insertStatement.setInt(8,nextId);

4．insertStatement执行对数据库的更新。输入下面两行：

insertStatement.executeUpdate();

如果有错误出现在创建连接时，语句或访问数据库，这个方法会抛出一个SQLException。

5．最后你需要存储客户号码到formInput属性对象。输入下面行：

formInput.put(“CUSTNO”,new Integer(nextId).toString());

6．保存方法。应该没有错误。

7．接着你将需要为JKRegister servlet编辑WebSphere配置文件。(编辑”[x:]\IBMVJava\ide\project_resources\IBM WebSphere Test Environment\properties\server\servlet\servletservice\serrvlets.properties”.)

改变看上去这样的行：

servlet.JKRegister.code=com.ibm.waslab.servlet1.RegistrationServlet

为这样：

servlet.JKRegister,code=com.ibm.waslab.JDBC.RegistrationServlet

8．如果你的webserver在运行，关掉它。在VAJ，启动SERunner。打开你的web浏览器，到JK Toys网站的注册页面。在表格里输入注册信息，按注册按钮。你的新的servlet将被调用。登录进JKToys站点确信注册已经发生。你的新的客户号码应该能在数据库找到。

在这个练习你作了什么

在这个练习，你编写了Registration Servlet的register方法。这个方法用JDBC来创建一个新的客户号码，注册一个新的客户到数据库。现在你有了在你的servlets里访问数据库的基本知识。

答案

以下的代码创建一个SQL语句，执行在前一页里定义的SQL查询。

//Get next customer number

Statement sqlStatement=conn.createStatement();

ResultSet result=sqlStatement.executeQuery(“SELECT MAX(CUSTNO) FROM”+DBOWNER+”.CUSTOMER”);

查询的结果被放在ResultSet对象叫result里面。结果的集合包含一个游标，最初指向返回的第一行前面。为了得到查询的值游标必须用next()往前移动。用这个方法，游标只能向前移动。下面几行移动游标到返回的值，增加1。

int nextId=0;

if(result.next()= =true)

{

nextId=result.getInt(1)+1;

}

if检查确定是否有行被返回。如果没有，结果为false。我们不处理错误的情况，但是它可以简单地用把nextId设置为1来处理。我们还可以用ResultSet的方法getInt(String)，列的名字为”CUSTNO”，但是因为我们知道只有一列会被返回，我们选择用getInt(int)..


用JSP构建动态网站 (1)

作者：运气

email: webmaster@chinaspx.com

日期：2001-7-3 14:37:04

一、 什么是JSP

    JSP(JavaServer Pages) JSP由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、在 http://java.sun.com/jdk/2 SDK、Standard Edition、v 1.2.2)。

    2、在http://java.sun.com/products/jsp/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:8080JSWDK 的欢迎页就说明JSP实验环境已经建成，可进入下一步实验。 要关闭Web服务器则运行stopserver.bat。

    三、 JSP简单示例

    JSWDK中包含的Web服务器的文档目录在缺省状态下为\jswdk-1.0.1\webpages，主文档在缺省状态下为index.html和index.jsp。也就是说访问http://localhost:8080用文本编辑器，如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文件中用＜%以及%＞括起来的Java程序语句，其中out.print是将文字输出到网页，语句＜%= 变量 | 表达式 %＞的作用是将Java Scriptlet中变量或表达式的值输出到网页，执行结果如图1。 图1 将变量Msg赋值为中文字符串，用＜%= %＞输出，或者用out.print输出中文字符串，则在英文版NT4及Redhat 6.1下实验运行结果正常，而在中文NT 4.0和中文98下，则反而会出现乱码。


用JSP构建动态网站 (2)

作者：运气

email: webmaster@chinaspx.com

日期：2001-7-3 14:37:27

    四、 统一的网站界面

    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这样网站的界面就能统一起来，而设计者可以集中精力在功能模块上处理用户登录、连接数据库、发送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

    ＜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程序。


用JSP构建动态网站 (3)

作者：运气

email: webmaster@chinaspx.com

日期：2001-7-3 14:37:49

    六、 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

    注意，如果修改和重新编译了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）、answers（备注型）。这个表中可以存放一些编程知识的常见问题及答案。

    然后，在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:odbc: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

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


用JSP构建动态网站 (4)

作者：运气

email: webmaster@chinaspx.com

日期：2001-7-3 14:38:12

    八、 技术分析

    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/

    从一个平台移植到另外一个平台，JSP和JavaBean甚至不用重新编译，因为Java字节码都是标准的与平台无关的。笔者将在NT下实验的JSP网页原封不动地拿到Linux下就运行起来了，感觉非常满意。

    九、 结论

    综上所述，JSP可谓是构建动态网站的一个利器，故推荐给读者，祝愿你们掌握JSP，开发出优秀的网站。ASP程序员也请试一下，JSP里也有session、request、response/out等对象，亲自试试看如何。

    附文

    这里是关于JSP的重要参考资料，有兴趣的读者可以进一步阅读。

http://java.sun.com/products/jsp/faq.html

http://www.esperanto.org.nz/jsp/jspfaq.html

http://www.zdnet.com/pcweek/stories/news/0,4153,410709,00.html

http://developer.netscape.com/viewsource/kuslich_jsp

/kuslich_jsp.html

http://web2.java.sun.com/products/jsp/jsp-asp.html

http://www.asptoday.com/articles/19991022.htm


用JSP和数据库做的购物车的源程序：（欢迎大家指点！）

作者：asp2001

email: asp2001@163.net

日期：2000-8-10 12:12:37

下面是我用JSP和数据库做的购物车的源程序：

注意：

1、重新计费部分还没有做好，大家自己动手吧！

2、下一版本将用session做。

//shop_cart.jsp

<%@ page contentType="text/html;charset=gb2312"%>

<%@ page session="true" %>

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

<jsp:useBean id="bka" scope="page" class="shop.bka" />

<%

String product_type;

String action;

int product_id;

int curpage;

//商品类型

if (request.getParameter("product_type")==null){

product_type="all";

}else{

product_type=request.getParameter("product_type");

}

//页数和商品类型参数，可以在“继续购物”时返回到上次购物的页面

if (request.getParameter("curpage")==null){

curpage=1;

}else{

curpage=java.lang.Integer.parseInt(request.getParameter("curpage"));

}

//动作

if (request.getParameter("action")==null){

action="view";

}else{

action=request.getParameter("action");

}

//商品编号

if (request.getParameter("product_id")==null){

product_id=0;

}else{

product_id=java.lang.Integer.parseInt(request.getParameter("product_id"));

}

int bbb;

bbb=1;

Integer num = new Integer(bbb);

//商店编号

session.putValue("shop_id",num);

//顾客username

session.putValue("guest_name","asp2001");

String guest_name=(String)session.getValue("guest_name");

Integer shop_id=(Integer)session.getValue("shop_id");

java.lang.String sql;

java.sql.ResultSet rs;

if (action.compareTo("add")==0) {

sql="select cart_quantity from shop_cart where cart_shop_id=" + shop_id + " and cart_guest_id='" + guest_name + "' and cart_product_id=" + product_id ;

rs = bka.executeQuery(sql);

if (rs.next()){

int cart_quantity;

cart_quantity=java.lang.Integer.parseInt(rs.getString("cart_quantity"))+1;

sql="update shop_cart set cart_quantity=" + cart_quantity + " where cart_shop_id=" + shop_id + " and cart_guest_id='" + guest_name + "' and cart_product_id=" + product_id ;

rs = bka.executeQuery(sql);}

else

{

sql="insert into shop_cart (cart_shop_id,cart_guest_id,cart_product_id,cart_quantity) values ('" + shop_id + "','" + guest_name + "','" + product_id + "',1)";

rs = bka.executeQuery(sql);

}

}

if (action.compareTo("clear")==0) {

sql="delete from shop_cart where cart_shop_id=" + shop_id + " and cart_guest_id='" + guest_name + "'";

rs = bka.executeQuery(sql);

}

if (action.compareTo("delete")==0) {

sql="delete from shop_cart where cart_shop_id=" + shop_id + " and cart_guest_id='" + guest_name + "' and cart_product_id=" + product_id ;

rs = bka.executeQuery(sql);

}

%>

<div align="center"><center>

<table border="0" cellpadding="0" cellspacing="0" width="610"

height="2">

  <tr>

    <td><form method="POST"

action="shop_cart.jsp?action=update&product_type=<%=product_type%>&curp

age=<%=curpage%>">

      <table border="0" cellpadding="0" cellspacing="0" width="610"

height="2">

        <tr>

          <td width="122" height="7"><div align="center"><center><table

border="1" cellpadding="2" cellspacing="0" width="100%" bordercolorlight="#FFB468"

bordercolordark="#FFFFFF" bgcolor="#FFB468">

            <tr>

              <td width="100%"><div

align="center"><center><p>购物车</td>

            </tr>

          </table>

          </center></div></td>

          <td width="122" height="7" style="border: medium"

align="center"><div align="center"><center><table

          border="1" cellpadding="2" cellspacing="0" width="100%"

bordercolorlight="#FFB468"

          bordercolordark="#FFFFFF" bgcolor="#FFB468">

            <tr>

              <td width="100%"><div align="center"><center><p><a

href="shop_list.jsp?shop_id=<%=shop_id%>&product_type=<%=product_type%>

&curpage=<%=curpage%>">继续购物</a></td>

            </tr>

          </table>

          </center></div></td>

          <td width="122" height="7" style="border: medium"

align="center"><div align="center"><center><table

          border="1" cellpadding="2" cellspacing="0" width="100%"

bordercolorlight="#FFB468"

          bordercolordark="#FFFFFF" bgcolor="#FFB468">

            <tr>

              <td width="100%"><div align="center"><center><p><a

href="javascript: document.forms[0].submit()">重新计费</a></td>

            </tr>

          </table>

          </center></div></td>

          <td width="122" height="7" style="border: medium"

align="center"><div align="center"><center><table

          border="1" cellpadding="2" cellspacing="0" width="100%"

bordercolorlight="#FFB468"

          bordercolordark="#FFFFFF" bgcolor="#FFB468">

            <tr>

              <td width="100%"><div align="center"><center><p><a href="shop_cart.jsp?action=clear&product_type=<%=product_type%>&curpage=<%=curpage%>">清空购物车</a></td>

            </tr>

          </table>

          </center></div></td>

          <td width="122" height="7" style="border: medium" align="center"><div align="center"><center><table

          border="1" cellpadding="2" cellspacing="0" width="100%" bordercolorlight="#FFB468"

          bordercolordark="#FFFFFF" bgcolor="#FFB468">

            <tr>

              <td width="100%"><div align="center"><center><p><a href="shop_order.asp">确认购买</a></td>

            </tr>

          </table>

          </center></div></td>

        </tr>

        <tr align="center">

          <td width="610" height="1" colspan="5"><div align="center"><center>

<table border="1" cellpadding="2" cellspacing="0" width="100%" bgcolor="#FDFEE2" bordercolorlight="#FFB468" bordercolordark="#FFFFFF" height="40">

            <tr>

              <td width="20%" height="8" align="left">商品名称</td>

              <td width="10%" height="8" align="left">市场价</td>

              <td width="10%" height="8" align="left">优惠价</td>

              <td width="10%" height="8" align="left">数量</td>

          <td width="14%" height="8" align="left">小计</td>

          <td width="12%" height="8" align="left">定金比例</td>

          <td width="17%" height="8" align="left">定金小计</td>

              <td width="17%" height="8" align="left">删除</td>

            </tr>

<%

sql="select shop_product.product_id,shop_product.product_name,shop_product.product_price,shop_product.product_discount,shop_cart.cart_quantity,shop_product.product_first from shop_cart,shop_product where shop_cart.cart_shop_id=" + shop_id + " and shop_cart.cart_guest_id='" + guest_name + "' and shop_cart.cart_product_id=shop_product.product_id";

rs = bka.executeQuery(sql);

int total;

int total_first;

total=0;

total_first=0;

String product_name;

int product_price;

int product_discount;

int product_first;

int cart_quantity;

if (rs.next()){

while (rs.next()) {

product_id=java.lang.Integer.parseInt(rs.getString(1));

product_name=rs.getString(2);

product_price=java.lang.Integer.parseInt(rs.getString(3));

product_discount=java.lang.Integer.parseInt(rs.getString(4));

cart_quantity=java.lang.Integer.parseInt(rs.getString(5));

product_first=java.lang.Integer.parseInt(rs.getString(6));

%>

<tr>

<td width="10%" height="1" align="left"><%=product_name%></td>

<td width="10%" height="1" align="left"><%=product_price%></td>

<td width="10%" height="1" align="left"><%=product_discount%></td>

<td width="10%" height="1" align="left"><input type="text" name="<%= "t" + product_id %>" size="3" value="<%=cart_quantity%>"></td>

<td width="14%" height="1" align="left"><%=product_discount*cart_quantity%></td>

<td width="12%" height="1" align="left"><%=product_first + "%"%></td>

<td width="17%" height="1" align="left"><%=product_first*product_discount*cart_quantity/100.0%></td>

<td width="17%" height="1"><div align="center"><center><p><a href="shop_cart.jsp?action=delete&product_id=<%=product_id%>">delete</a></td>

</tr>

<%

total=total+product_discount*cart_quantity;

total_first=total_first+product_discount*cart_quantity*product_first/100;

}

%>

<tr align="center">

<td width="72%" colspan="6" height="16"><div align="right"><p>总计</td>

<td width="36%" colspan="2" height="16"><div align="left"><%=total%></td>

</tr>

<tr align="center">

<td width="72%" colspan="6" height="16"><div align="right"><p>定金总计</td>

<td width="36%" colspan="2" height="16"><div align="left"><%=total_first%></td>

</tr>

<tr align="center">

<td width="72%" colspan="6" height="16"><div align="right"><p>结余</td>

<td width="36%" colspan="2" height="16"><div align="left"><%=total-total_first%></td>

</tr>

          </table>

          </center></div>

<%

}else{

%>

<p align="center">购物车为空!</p>

<%

}

%>

数据库操作部分

程序用到两个表：

1 shop_cart表

    cart_id int  购物车编号  自动编号

    cart_shop_id  nvarchar     商店编号

    cart_product_id   nvarchar         商品编号

    cart_quantity      int         商品数量

临时存放购物车数据

2 shop_product表

    product_id    int      商品编号   自动编号

    shop_id       nvarchar    商店编号

    product_name    nvarchar     商品名称

    product_bb       nvarchar     商品介绍

    product_price      int         市场价

    product_discount    int         优惠价

    product_img           img        图片

    product_status         nvarchar        状态

    product_first           int        定金比例

    product_type            nvanchar     商品类型

存放商品资料

使用bka.java制成的javabean：bka.class可以提供对数据库的操作。

另外，需在控制面板的系统DSN中注册bka.dsn，

从而可使JSP通过JDBC-ODBC来调用sql数据库。

在页面中调用javabean，基本上可采用以下方式：

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

<jsp:useBean id="RegisterBean" scope="page" class="shop.bka" />

<%

String sql="select * from xxx";

ResultSet rs = RegisterBean.executeQuery(sql);

if(rs.next()) {

rs.close();

RegisterBean.closeStmt();

session.putValue("register_message","duplicate name found!");

}

%>

注意应在使用后将rs关闭。

以下是bka.java的源程序。注意在使用前需用javac加以编译成为class文件即javabean.

--shop/bka.java--

package shop;

import java.sql.*;

public class bka {

String sDBDriver = "sun.jdbc.odbc.JdbcOdbcDriver";

String sConnStr = "jdbc:odbc:bka";

Connection conn = null;

ResultSet rs = null;

public bka() {

try {

Class.forName(sDBDriver);

}

catch(java.lang.ClassNotFoundException e) {

System.err.println("bka(): " + e.getMessage());

}

}

public ResultSet executeQuery(String sql) {

rs = null;

try {

conn = DriverManager.getConnection(sConnStr,"xxx","yyy");

Statement stmt = conn.createStatement();

rs = stmt.executeQuery(sql);

}

catch(SQLException ex) {

System.err.println("aq.executeQuery: " + ex.getMessage());

}

return rs;

}

}


用JSP链接到数据库

作者：zergman

email: zergman@chinaasp.com

日期：6/12/2001 1:26:47 PM

使用起来非常简单，CUT然后PASTE，原代码中我给出了详细的注释。

<!--首先导入一些必要的packages-->

<!--开始导入packages-->

<%@ page info="database handler"%>

<%@ page import="java.io.*"%>

<%@ page import="java.util.*"%>

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

<%@ page import="javax.servlet.*"%>

<%@ page import="javax.servlet.http.*"%>

<!--导入结束-->

<%

try

{

//加载驱动程序

//下面的代码为加载JDBD-ODBC驱动程序

Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");

//建立连接

//第二步是用适当的驱动程序连接到DBMS，看下面的代码：

String url="jdbc:odbc:test";

//"test"是系统dsn名

Connection con=DriverManager.getConnection(url, "administrator", "password");

//"administrator","password"是用户名和口令

//然后创建一个JDBC声明

Statement stmt = con.createStatement();

String query="CREATE TABLE COFFEES" +

"(COF_NAME VARCHAR(32),"+

"SUP_ID INTEGER,"+

"PRICE FLOAT,"+

"SALES INTEGER,"+

"TOTAL INTEGER)";

//执行声明

stmt.executeUpdate(query);

}

catch (Exception e) {}

//输出确认信息

out.println("table coffees created");

%>


用JSP实现的一个日历程序

作者：whitefox

email: whitefox.jiang@corp.elong.com

日期：2000-07-21 15:07:51

作 者: delighter (孤独的人无所谓) 2000.07.08 转贴 打包 回复

<!-- File name:calendar.jsp -->

<!-- 显示任意年、月的日历，可选择不同的年、月。author:wildfield -->

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

<%! String year;

   String month;

%>

<% month=request.getParameter("month");

   year =request.getParameter("year");

%>

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=gb2312">

<title>日</title>

<script Language="JavaScript">

<!--

function changeMonth()

{

 var mm="calendar.jsp?month="+document.sm.elements[0].selectedIndex+"&year="

+<%=year%>;

 window.open(mm,"_self");

}

//--></script>

</head>

<%! String days[]; %>

<%

 days=new String[42];

 for(int i=0;i<42;i++)

  {

   days[i]="";

  }

%>

<%

 Calendar thisMonth=Calendar.getInstance();

  if(month!=null&&(!month.equals("null")))

   thisMonth.set(Calendar.MONTH, Integer.parseInt(month) );

  if(year!=null&&(!year.equals("null")))

   thisMonth.set(Calendar.YEAR, Integer.parseInt(year) );

  year=String.valueOf(thisMonth.get(Calendar.YEAR));

 month=String.valueOf(thisMonth.get(Calendar.MONTH));

 thisMonth.setFirstDayOfWeek(Calendar.SUNDAY);

 thisMonth.set(Calendar.DAY_OF_MONTH,1);

 int firstIndex=thisMonth.get(Calendar.DAY_OF_WEEK)-1;

 int maxIndex=thisMonth.getActualMaximum(Calendar.DAY_OF_MONTH);

 for(int i=0;i<maxIndex;i++)

  {

   days[firstIndex+i]=String.valueOf(i+1);

  }

%>

<body>

<FORM name="sm" method="post" action="calendar.jsp">

   <%=year%>年  <%=Integer.parseInt(month)+1%>月

<table border="0" width="168" height="81">

<div align=center>

  <tr>

    <th width="25" height="16" bgcolor="#FFFF00"><font color="red">日</font>

</th>

    <th width="25" height="16" bgcolor="#FFFF00">一</th>

    <th width="25" height="16" bgcolor="#FFFF00">二</th>

    <th width="25" height="16" bgcolor="#FFFF00">三</th>

    <th width="25" height="16" bgcolor="#FFFF00">四</th>

    <th width="25" height="16" bgcolor="#FFFF00">五</th>

    <th width="25" height="16" bgcolor="#FFFF00"><font color="green">六</fon

t></th>

  </tr>

<% for(int j=0;j<6;j++) { %>

 <tr>

      <% for(int i=j*7;i<(j+1)*7;i++) { %>

     <td width="15%" height="16" bgcolor="#C0C0C0" valign="middle" align="ce

nter">

     <a href="jump.jsp?year=<%=year%>&month=<%=Integer.parseInt(month)+1%>&d

ate=<%=days[i]%>" target="main"><%=days[i]%></a></td>

    <% }  %>

  </tr>

<% }  %>

</div>

</table>

<table border="0" width="168" height="20">

 <tr>

  <td width=30%><select name="month" size="1" onchange="changeMonth()" >

    <option value="0">一月</option>

    <option value="1">二月</option>

    <option value="2">三月</option>

    <option value="3">四月</option>

    <option value="4">五月</option>

    <option value="5">六月</option>

    <option value="6">七月</option>

    <option value="7">八月</option>

    <option value="8">九月</option>

    <option value="9">十月</option>

    <option value="10">十一月</option>

    <option value="11">十二月</option>

  </select></td>

 <td width=28%><input type=text name="year" value=<%=year%> size=4 maxlength

=4></td>

 <td>年</td>

 <td width=28%><input type=submit value="提交"></td>

 </tr>

</table>

</FORM>

<script Language="JavaScript">

<!--

  document.sm.month.options.selectedIndex=<%=month%>;

//-->

</script>

</body>

</html>


用Swing实现数据表格功能（加入精华区吧）

此电子文档属C#乐园QQ群3256045整理。
用Swing实现数据表格功能（加入精华区吧）

上一页 目录 下一页

用Swing实现数据表格功能（加入精华区吧）

作者：大脸猫

email: danielbs@yeah.net

日期：8/10/2001 4:09:54 PM

最近有个项目是delphi来做的，之中有很多地方要用到数据表格。这种功能在delphi和vb中能很方便的做出来，java没

有提供这项功能，但是可以用Jtable 来实现，不过就是麻烦了点：）。

下面是我用applet实现的一个简单数据表格程序代码。

package com.applet.cat10;

import java.awt.*;

import java.awt.event.*;

import java.applet.*;

import javax.swing.*;

import com.util.*;

import java.util.*;

import javax.swing.table.*;

import java.sql.*;

/**

 * Title:        Cat工程

 * Description:  BCat

 * Copyright:    Copyright (c) 2001

 * Company:      smartcomm

 * @author daniel

 * @version 1.0

 */

public class TestDatabase extends JApplet {

  boolean isStandalone = false;

  JButton jButton1 = new JButton(); //触发时间查询按扭

  Database db=new Database();

  JTable table1 = new JTable();

  JScrollPane scroll = new JScrollPane();

  JTextField text1 = new JTextField();

DefaultTableModel dtm; //定义表格的数据模型

    Vector vCdata=null;  //定义表的列名（以vector存储）

    ResultSet rsRow=null; //查询表的数据集合

  /**Construct the applet*/

  public TestDatabase() {

  }

  /**Initialize the applet*/

  public void init() {

    try {

      jbInit();

      userInit(); //自定义的操作都在次方法中

    }

    catch(Exception e) {

      e.printStackTrace();

    }

  }

  /**Component initialization*/

  private void jbInit() throws Exception {

    jButton1.setText("jButton1");

    jButton1.setBounds(new Rectangle(26, 225, 79, 29));

    jButton1.addActionListener(new java.awt.event.ActionListener() {

      public void actionPerformed(ActionEvent e) {

        jButton1_actionPerformed(e);

      }

    });

    this.setSize(new Dimension(664, 300));

    this.getContentPane().setLayout(null);

    scroll.setBounds(new Rectangle(12, 24, 644, 189));

    text1.setBounds(new Rectangle(16, 271, 365, 22));

    this.getContentPane().add(scroll, null);

    this.getContentPane().add(jButton1, null);

   // this.getContentPane().add(text1, null);

    scroll.getViewport().add(table1, null);

  }

  /*userInit() 数据表格的初始化*/

  public void userInit()

  {

  db.connect();   //数据库连接

  vCdata=db.getFieldNames("T_REGISTRATION");  //得出列名（vector存储）

  dtm=new DefaultTableModel(); //定义模式

  table1.setModel(dtm);  //设定表模式

/**for 列出标题烂**/

      for(int i=0;i<vCdata.size();i++)

        dtm.addColumn((String)vCdata.elementAt(i));

    rsRow=db.executeQueryTable("T_REGISTRATION"); //得出数据集合

  }

  /**Start the applet*/

  public void start() {

  }

  /**Stop the applet*/

  public void stop() {

  }

  /**Destroy the applet*/

  public void destroy() {

  }

  //static initializer for setting look & feel

  static {

    try {

      //UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());

      //UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());

    }

    catch(Exception e) {

    }

  }

//触发查询事件

  void jButton1_actionPerformed(ActionEvent e)

  {

      try

      {

            while(rsRow.next())

            {

            Vector vRdata=new Vector();

                for(int i=0;i<vCdata.size();i++)

                {

                vRdata.addElement(rsRow.getString((String)vCdata.elementAt(i))); //列举列数据（vector存

储）

                }

             dtm.addRow(vRdata);   //向表中添家数据

            }

          db.close();

      }catch(Exception ei)

      {

      System.out.println("error at jButton1_actionPerformed! in TestDatabase" + ei.toString());

      }

  }

}

这个程序简单的实现了数据表格功能，初始化状态是数据表格只有标题，当click 按扭显示数据。

1。在userInit()方法中做了数据表格的初始化，关键的方法：

建立表格模型： DefaultTableModel dtm=new DefaultTableModel()，

设定表模型：table1.setModel(dtm)

设定表格标题：（一切操作可以在模型上做） dtm.addColumn(Vector arg),arg是表的标题，也就是field名字数组

得出数据的结果集合：rsRow=db.executeQueryTable("T_REGISTRATION")，这就是一般的数据查询结果哦

2。在 void jButton1_actionPerformed(ActionEvent e)中显示数据，关键方法：

以行的方式加入数据：dtm.addRow(Vector arg)。

3。Jtable有很多的实例化方式，这里采用的是DefaultTableModel模型，对表的操作都可以建立在模型上。

4。以后如果更改表中的数据的话可以在模型上修改如：

更新3行4列的数据：dtm.setValueAt("bigCat",3,4),也可以table1.setValueAt("bigCat",3,4).

得出3行4列数据：Object rs=dtm.getValueAt(3,4);

删除第3行数据：dtm.removeRow(3)

具体数据表格的展现方式大家可以自己修改设定。


用Swing制作欢迎屏幕 (转自赛迪网)

此电子文档属C#乐园QQ群3256045整理。
用Swing制作欢迎屏幕 (转自赛迪网)

上一页 目录 下一页

用Swing制作欢迎屏幕 (转自赛迪网)

作者：jeru

email: jeru@163.net

日期：7/25/2001 10:20:54 AM

用Swing制作欢迎屏幕

（松下客　2001年05月30日 18:49）

几乎所有时髦的应用都有一个欢迎屏幕。欢迎屏幕既是宣传产品的方法之一，而且在长时间的应用启动过程中，欢迎屏幕还用来表示应用正在准备过程中。

下面是一个最简单的欢迎屏幕实现：

class SplashWindow1 extends JWindow

{

    public SplashWindow1(String filename, Frame f)

    {

        super(f);

        JLabel l = new JLabel(new ImageIcon(filename));

        getContentPane().add(l, BorderLayout.CENTER);

        pack();

        Dimension screenSize =

          Toolkit.getDefaultToolkit().getScreenSize();

        Dimension labelSize = l.getPreferredSize();

        setLocation(screenSize.width/2 - (labelSize.width/2),

                    screenSize.height/2 - (labelSize.height/2));

        setVisible(true);

        screenSize = null;

        labelSize = null;

    }

}

SplashWindow1类从Swing的JWindow派生。JWindow是一个容器，它没有其他窗口所具有的各种窗口元素，如标题条、窗口管理按钮，甚至连突出显示的边框也没有。因此，JWindow对于制作欢迎屏幕来说是非常合适的。上面的代码假定图形文件在当前目录。图形通过ImageIcon装入内存，然后它就被放到了JWindow的中心。接着，窗口被pack()，这使得Swing把窗口调整到适当的大小，最后窗口被移到了屏幕的中心。

如果我们运行上面的程序，可以发现虽然欢迎画面确实出现在屏幕中央，但遗憾的是，它却不会关闭！要关闭欢迎画面，我们需要加入更多的代码：

class SplashWindow2 extends JWindow

{

    public SplashWindow2(String filename, Frame f)

    {

        super(f);

        JLabel l = new JLabel(new ImageIcon(filename));

        getContentPane().add(l, BorderLayout.CENTER);

        pack();

        Dimension screenSize =

          Toolkit.getDefaultToolkit().getScreenSize();

        Dimension labelSize = l.getPreferredSize();

        setLocation(screenSize.width/2 - (labelSize.width/2),

                    screenSize.height/2 - (labelSize.height/2));

        addMouseListener(new MouseAdapter()

            {

                public void mousePressed(MouseEvent e)

                {

                    setVisible(false);

                    dispose();

                }

            });

        setVisible(true);

    }

}

和原先的SplashWindow1类相比，这个SplashWindow2类唯一的区别在于多出了一个安装到JWindow上的匿名MouseListener。经过这个改动之后，用户可以点击欢迎屏幕关闭它。

现在我们有了一个很不错的欢迎屏幕，它可以通过点击的方法关闭，但它不会自己消失。接下来我们要加入代码，使得欢迎屏幕在显示一定的时间之后自动消失。这里我们要考虑到运用线程。

class SplashWindow3 extends JWindow

{

    public SplashWindow3(String filename, Frame f, int waitTime)

    {

        super(f);

        JLabel l = new JLabel(new ImageIcon(filename));

        getContentPane().add(l, BorderLayout.CENTER);

        pack();

        Dimension screenSize =

          Toolkit.getDefaultToolkit().getScreenSize();

        Dimension labelSize = l.getPreferredSize();

        setLocation(screenSize.width/2 - (labelSize.width/2),

                    screenSize.height/2 - (labelSize.height/2));

        addMouseListener(new MouseAdapter()

            {

                public void mousePressed(MouseEvent e)

                {

                    setVisible(false);

                    dispose();

                }

            });

        final int pause = waitTime;

        final Runnable closerRunner = new Runnable()

            {

                public void run()

                {

                    setVisible(false);

                    dispose();

                }

            };

        Runnable waitRunner = new Runnable()

            {

                public void run()

                {

                    try

                        {

                            Thread.sleep(pause);

                            SwingUtilities.invokeAndWait(closerRunner);

                        }

                    catch(Exception e)

                        {

                            e.printStackTrace();

                            // 能够捕获InvocationTargetException

                            // 能够捕获InterruptedException

                        }

                }

            };

        setVisible(true);

        Thread splashThread = new Thread(waitRunner, "SplashThread");

        splashThread.start();

    }

}

这里的基本思路是利用一个在一定时间内暂停等待的Thread对象。在上面的代码中，线程的暂停时间是4秒。当这个线程唤醒时，它将关闭欢迎屏幕。由于Swing是非线程安全的，除非代码在事件分派线程上执行，否则它就不应该影响任何UI组件的状态。所谓事件分派线程，就是Swing中负责绘图和事件处理的线程。

为了解决这个问题，Swing设计者赋予我们安全地把Runnable对象加入UI事件队列的能力。在本例中，我们用可运行对象closerRunner完成最关键的工作。我们把可运行对象传入SwingUtilities.invokeAndWait()静态方法，然后SwingUtilities.invokeAndWait()进行所有未完成的UI操作，并执行传递给该方法的可运行对象closerRunner的run方法。通过运用一个独立的线程负责欢迎屏幕的关闭操作，应用担负起了显示和关闭欢迎屏幕之间的所有操作。

如果要让欢迎屏幕总是显示且用户不能关闭它，你必须删除那些隐藏欢迎屏幕的代码。如果要让欢迎屏幕只能由用户手工关闭，你可以象使用任何其他JWindow对象一样调用SplashWindow3对象上的setVisible(false)和dispose()方法。

总而言之，借助于SwingUtilities.invokeAndWait()方法，我们可以安全地创建出多线程欢迎屏幕。具体实现时，欢迎屏幕可以由用户点击关闭，也可以在一定的时间之后自动关闭。Swing所支持的线程模型使得应用在显示欢迎屏幕之后仍能够响应其他事件和处理其他任务。

责任编辑:小李(lisz@staff.ccidnet.com)


用连接池提高Servlet访问数据库的效率 (-)

此电子文档属C#乐园QQ群3256045整理。
用连接池提高Servlet访问数据库的效率 (-)

上一页 目录 下一页

用连接池提高Servlet访问数据库的效率 (-)

作者：jeru

日期：2000-12-7 11:45:06

Java Servlet作为首选的服务器端数据处理技术，正在迅速取代CGI脚本。Servlet超越CGI的优势之一在于，不仅多个请求

可以共享公用资源，而且还可以在不同用户请求之间保留持续数据。本文介绍一种充分发挥该特色的实用技术，即数据库连

接池。

一、实现连接池的意义

动态Web站点往往用数据库存储的信息生成Web页面，每一个页面请求导致一次数据库访问。连接

数据库不仅要开销一定的通讯和内存资源，还必须完成用户验证、安全上下文配置这类任务，因而往往成为最为耗时的操

作。当然，实际的连接时间开销千变万化，但1到2秒延迟并非不常见。如果某个基于数据库的Web应用只需建立一次初始连

接，不同页面请求能够共享同一连接，就能获得显著的性能改善。

Servlet是一个Java类。Servlet引擎（它可能是Web服务软件的一部分，也可能是一个独立的附加模块）在系统启动或Servlet

第一次被请求时将该类装入Java虚拟机并创建它的一个实例。不同用户请求由同一Servlet实例的多个独立线程处理。那些要

求在不同请求之间持续有效的数据既可以用Servlet的实例变量来保存，也可以保存在独立的辅助对象中。

用JDBC访问数据库首先要创建与数据库之间的连接，获得一个连接对象（Connection），由连接对象提供执行SQL语句的方法。

本文介绍的数据库连接池包括一个管理类DBConnectionManager，负责提供与多个连接池对象（DBConnectionPool类）之间

的接口。每一个连接池对象管理一组JDBC连接对象，每一个连接对象可以被任意数量的Servlet共享。

类DBConnectionPool提供以下功能：

1) 从连接池获取（或创建）可用连接。

2) 把连接返回给连接池。

3) 在系统关闭时释放所有资源，关闭所有连接。

此外， DBConnectionPool类还能够处理无效连接（原来登记为可用的连接，由于某种原因不再可用，如超时，通讯问题）

，并能够限制连接池中的连接总数不超过某个预定值。

管理类DBConnectionManager用于管理多个连接池对象，它提供以下功能：

1) 装载和注册JDBC驱动程序。

2) 根据在属性文件中定义的属性创建连接池对象。

3) 实现连接池名字与其实例之间的映射。

4) 跟踪客户程序对连接池的引用，保证在最后一个客户程序结束时安全地关闭所有连接池。

本文余下部分将详细说明这两个类，最后给出一个示例演示Servlet使用连接池的一般过程。

二、具体实现

DBConnectionManager.java程序清单如下：

001 import java.io.*;

002 import java.sql.*;

003 import java.util.*;

004 import java.util.Date;

005

006 /**

007 * 管理类DBConnectionManager支持对一个或多个由属性文件定义的数据库连接

008 * 池的访问.客户程序可以调用getInstance()方法访问本类的唯一实例.

009 */

010 public class DBConnectionManager {

011 static private DBConnectionManager instance; // 唯一实例

012 static private int clients;

013

014 private Vector drivers = new Vector();

015 private PrintWriter log;

016 private Hashtable pools = new Hashtable();

017

018 /**

019 * 返回唯一实例.如果是第一次调用此方法,则创建实例

020 *

021 * @return DBConnectionManager 唯一实例

022 */

023 static synchronized public DBConnectionManager getInstance() {

024 if (instance == null) {

025 instance = new DBConnectionManager();

026 }

027 clients++;

028 return instance;

029 }

030

031 /**

032 * 建构函数私有以防止其它对象创建本类实例

033 */

034 private DBConnectionManager() {

035 init();

036 }

037

038 /**

039 * 将连接对象返回给由名字指定的连接池

040 *

041 * @param name 在属性文件中定义的连接池名字

042 * @param con 连接对象

043 */

044 public void freeConnection(String name, Connection con) {

045 DBConnectionPool pool = (DBConnectionPool) pools.get(name);

046 if (pool != null) {

047 pool.freeConnection(con);

048 }

049 }

050

051 /**

052 * 获得一个可用的(空闲的)连接.如果没有可用连接,且已有连接数小于最大连接数

053 * 限制,则创建并返回新连接

054 *

055 * @param name 在属性文件中定义的连接池名字

056 * @return Connection 可用连接或null

057 */

058 public Connection getConnection(String name) {

059 DBConnectionPool pool = (DBConnectionPool) pools.get(name);

060 if (pool != null) {

061 return pool.getConnection();

062 }

063 return null;

064 }

065

066 /**

067 * 获得一个可用连接.若没有可用连接,且已有连接数小于最大连接数限制,

068 * 则创建并返回新连接.否则,在指定的时间内等待其它线程释放连接.

069 *

070 * @param name 连接池名字

071 * @param time 以毫秒计的等待时间

072 * @return Connection 可用连接或null

073 */

074 public Connection getConnection(String name, long time) {

075 DBConnectionPool pool = (DBConnectionPool) pools.get(name);

076 if (pool != null) {

077 return pool.getConnection(time);

078 }

079 return null;

080 }

081

082 /**

083 * 关闭所有连接,撤销驱动程序的注册

084 */

085 public synchronized void release() {

086 // 等待直到最后一个客户程序调用

087 if (--clients != 0) {

088 return;

089 }

090

091 Enumeration allPools = pools.elements();

092 while (allPools.hasMoreElements()) {

093 DBConnectionPool pool = (DBConnectionPool) allPools.nextElement();

094 pool.release();

095 }

096 Enumeration allDrivers = drivers.elements();

097 while (allDrivers.hasMoreElements()) {

098 Driver driver = (Driver) allDrivers.nextElement();

099 try {

100 DriverManager.deregisterDriver(driver);

101 log("撤销JDBC驱动程序 " + driver.getClass().getName()+"的注册");

102 }

103 catch (SQLException e) {

104 log(e, "无法撤销下列JDBC驱动程序的注册: " + driver.getClass().getName());

105 }

106 }

107 }

108

109 /**

110 * 根据指定属性创建连接池实例.

111 *

112 * @param props 连接池属性

113 */

114 private void createPools(Properties props) {

115 Enumeration propNames = props.propertyNames();

116 while (propNames.hasMoreElements()) {

117 String name = (String) propNames.nextElement();

118 if (name.endsWith(".url")) {

119 String poolName = name.substring(0, name.lastIndexOf("."));

120 String url = props.getProperty(poolName + ".url");

121 if (url == null) {

122 log("没有为连接池" + poolName + "指定URL");

123 continue;

124 }

125 String user = props.getProperty(poolName + ".user");

126 String password = props.getProperty(poolName + ".password");

127 String maxconn = props.getProperty(poolName + ".maxconn", "0");

128 int max;

129 try {

130 max = Integer.valueOf(maxconn).intValue();

131 }

132 catch (NumberFormatException e) {

133 log("错误的最大连接数限制: " + maxconn + " .连接池: " + poolName);

134 max = 0;

135 }

136 DBConnectionPool pool =

137 new DBConnectionPool(poolName, url, user, password, max);

138 pools.put(poolName, pool);

139 log("成功创建连接池" + poolName);

140 }

141 }

142 }

143

144 /**

145 * 读取属性完成初始化

146 */

147 private void init() {

148 InputStream is = getClass().getResourceAsStream("/db.properties");

149 Properties dbProps = new Properties();

150 try {

151 dbProps.load(is);

152 }

153 catch (Exception e) {

154 System.err.println("不能读取属性文件. " +

155 "请确保db.properties在CLASSPATH指定的路径中");

156 return;

157 }

158 String logFile = dbProps.getProperty("logfile", "DBConnectionManager.log");

159 try {

160 log = new PrintWriter(new FileWriter(logFile, true), true);

161 }

162 catch (IOException e) {

163 System.err.println("无法打开日志文件: " + logFile);

164 log = new PrintWriter(System.err);

165 }

166 loadDrivers(dbProps);

167 createPools(dbProps);

168 }

169

170 /**

171 * 装载和注册所有JDBC驱动程序

172 *

173 * @param props 属性

174 */

175 private void loadDrivers(Properties props) {

176 String driverClasses = props.getProperty("drivers");

177 StringTokenizer st = new StringTokenizer(driverClasses);

178 while (st.hasMoreElements()) {

179 String driverClassName = st.nextToken().trim();

180 try {

181 Driver driver = (Driver)

182 Class.forName(driverClassName).newInstance();

183 DriverManager.registerDriver(driver);

184 drivers.addElement(driver);

185 log("成功注册JDBC驱动程序" + driverClassName);

186 }

187 catch (Exception e) {

188 log("无法注册JDBC驱动程序: " +

189 driverClassName + ", 错误: " + e);

190 }

191 }

192 }

193

194 /**

195 * 将文本信息写入日志文件

196 */

197 private void log(String msg) {

198 log.println(new Date() + ": " + msg);

199 }

200

201 /**

202 * 将文本信息与异常写入日志文件

203 */

204 private void log(Throwable e, String msg) {

205 log.println(new Date() + ": " + msg);

206 e.printStackTrace(log);

207 }

208

209 /**

210 * 此内部类定义了一个连接池.它能够根据要求创建新连接,直到预定的最

211 * 大连接数为止.在返回连接给客户程序之前,它能够验证连接的有效性.

212 */

213 class DBConnectionPool {

214 private int checkedOut;

215 private Vector freeConnections = new Vector();

216 private int maxConn;

217 private String name;

218 private String password;

219 private String URL;

220 private String user;

221

222 /**

223 * 创建新的连接池

224 *

225 * @param name 连接池名字

226 * @param URL 数据库的JDBC URL

227 * @param user 数据库帐号,或 null

228 * @param password 密码,或 null

229 * @param maxConn 此连接池允许建立的最大连接数

230 */

231 public DBConnectionPool(String name, String URL, String user, String password,

232 int maxConn) {

233 this.name = name;

234 this.URL = URL;

235 this.user = user;

236 this.password = password;

237 this.maxConn = maxConn;

238 }

239

240 /**

241 * 将不再使用的连接返回给连接池

242 *

243 * @param con 客户程序释放的连接

244 */

245 public synchronized void freeConnection(Connection con) {

246 // 将指定连接加入到向量末尾

247 freeConnections.addElement(con);

248 checkedOut--;

249 notifyAll();

250 }

251

252 /**

253 * 从连接池获得一个可用连接.如没有空闲的连接且当前连接数小于最大连接

254 * 数限制,则创建新连接.如原来登记为可用的连接不再有效,则从向量删除之,

255 * 然后递归调用自己以尝试新的可用连接.

256 */

257 public synchronized Connection getConnection() {

258 Connection con = null;

259 if (freeConnections.size() > 0) {

260 // 获取向量中第一个可用连接

261 con = (Connection) freeConnections.firstElement();

262 freeConnections.removeElementAt(0);

263 try {

264 if (con.isClosed()) {

265 log("从连接池" + name+"删除一个无效连接");

266 // 递归调用自己,尝试再次获取可用连接

267 con = getConnection();

268 }

269 }

270 catch (SQLException e) {

271 log("从连接池" + name+"删除一个无效连接");

272 // 递归调用自己,尝试再次获取可用连接

273 con = getConnection();

274 }

275 }

276 else if (maxConn == 0 || checkedOut < maxConn) {

277 con = newConnection();

278 }

279 if (con != null) {

280 checkedOut++;

281 }

282 return con;

283 }

284

285 /**

286 * 从连接池获取可用连接.可以指定客户程序能够等待的最长时间

287 * 参见前一个getConnection()方法.

288 *

289 * @param timeout 以毫秒计的等待时间限制

290 */

291 public synchronized Connection getConnection(long timeout) {

292 long startTime = new Date().getTime();

293 Connection con;

294 while ((con = getConnection()) == null) {

295 try {

296 wait(timeout);

297 }

298 catch (InterruptedException e) {}

299 if ((new Date().getTime() - startTime) >= timeout) {

300 // wait()返回的原因是超时

301 return null;

302 }

303 }

304 return con;

305 }

306

307 /**

308 * 关闭所有连接

309 */

310 public synchronized void release() {

311 Enumeration allConnections = freeConnections.elements();

312 while (allConnections.hasMoreElements()) {

313 Connection con = (Connection) allConnections.nextElement();

314 try {

315 con.close();

316 log("关闭连接池" + name+"中的一个连接");

317 }

318 catch (SQLException e) {

319 log(e, "无法关闭连接池" + name+"中的连接");

320 }

321 }

322 freeConnections.removeAllElements();

323 }

324

325 /**

326 * 创建新的连接

327 */

328 private Connection newConnection() {

329 Connection con = null;

330 try {

331 if (user == null) {

332 con = DriverManager.getConnection(URL);

333 }

334 else {

335 con = DriverManager.getConnection(URL, user, password);

336 }

337 log("连接池" + name+"创建一个新的连接");

338 }

339 catch (SQLException e) {

340 log(e, "无法创建下列URL的连接: " + URL);

341 return null;

342 }

343 return con;

344 }

345 }

346 }


邮件例程 - JavaMail - 发送HTML邮件

作者：何志强

email: hhzqq@21cn.com

日期：2000-8-16 15:21:51

form.htm

========

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=gb2312">

<title>邮件例程 - JavaMail - 发送HTML邮件</title>

</head>

<body>

<table border="0" cellspacing="0" cellpadding="0">

<form method="post" action="send.jsp">

<tr>

  <td>SMTP主机：</td>

  <td><input type="text" name="smtp" size="80"></td>

</tr>

<tr>

  <td>发信人：</td>

  <td><input type="text" name="from" size="80"></td>

</tr>

<tr>

  <td>收信人：</td>

  <td><input type="text" name="to" size="80"></td>

</tr>

<tr>

  <td>抄送人：</td>

  <td><input type="text" name="cc" size="80"></td>

</tr>

<tr>

  <td>暗送人：</td>

  <td><input type="text" name="bcc" size="80"></td>

</tr>

<tr>

  <td>主题：</td>

  <td><input type="text" name="subject" size="80"></td>

</tr>

<tr>

  <td valign="top">内容：</td>

  <td><textarea name="body" rows="5" cols="80"></textarea></td>

</tr>

<tr>

  <td colspan="2" align="center"><input type="submit" value="发送"></td>

</tr>

</form>

</table>

</body>

</html>

send.jsp

========

<%--

作者：何志强[hhzqq@21cn.com]

日期：2000-08-16

版本：1.0

功能：邮件例程 - JavaMail - 发送HTML邮件

--%>

<%

//变量声明

java.lang.String smtp,from,to,cc,bcc,subject,body;

//获得用户输入数据

smtp = request.getParameter("smtp");

from = request.getParameter("from");

to = request.getParameter("to");

cc = request.getParameter("cc");

bcc = request.getParameter("bcc");

subject = request.getParameter("subject");

if(subject!=null){

   subject = new java.lang.String(subject.getBytes("iso-8859-1"));

}

body = request.getParameter("body");

//发送邮件

pipi.mail.HTML.send(smtp,from,to,cc,bcc,subject,body);

%>

pipi.jaf.StringDataSource.java

==============================

/*

作者：何志强[hhzqq@21cn.com]

日期：2000-08-16

功能：字符串型数据源

*/

package pipi.jaf;

public class StringDataSource implements javax.activation.DataSource{

   private java.lang.String data;

   private java.lang.String type;

   public StringDataSource(java.lang.String data,java.lang.String type){

      this.data = data;

      this.type = type;

   }

   public java.io.InputStream getInputStream() throws java.io.IOException{

      return new java.io.StringBufferInputStream(data);

   }

   public java.io.OutputStream getOutputStream() throws java.io.IOException{

      throw new java.io.IOException("it does not support this method now!");

   }

   public java.lang.String getContentType(){

      return type;

   }

   public java.lang.String getName(){

      return "pipi";

   }

}

pipi.mail.HTML.java

===================

/*

作者：何志强[hhzqq@21cn.com]

日期：2000-08-16

功能：发送HTML邮件

*/

package pipi.mail;

public final class HTML{

   public static void send(

      java.lang.String smtp,    /*SMTP主机地址*/

      java.lang.String from,    /*发信人*/

      java.lang.String to,      /*收信人*/

      java.lang.String cc,      /*抄送人*/

      java.lang.String bcc,     /*暗送人*/

      java.lang.String subject, /*主题*/

      java.lang.String body     /*内容*/

   ) throws java.lang.Exception{

      //变量声明

      java.util.Properties props;              //系统属性

      javax.mail.Session mailSession;          //邮件会话对象

      javax.mail.internet.MimeMessage mimeMsg; //MIME邮件对象

      //设置系统属性

      props = java.lang.System.getProperties(); //获得系统属性对象

      props.put("mail.smtp.host",smtp); //设置SMTP主机

      //获得邮件会话对象

      mailSession = javax.mail.Session.getDefaultInstance(props,null);

      //创建MIME邮件对象

      mimeMsg = new javax.mail.internet.MimeMessage(mailSession);

      //设置发信人

      mimeMsg.setFrom(new javax.mail.internet.InternetAddress(from));

      //设置收信人

      if(to!=null){

         mimeMsg.setRecipients(javax.mail.Message.RecipientType.TO,javax.mail.internet.InternetAddress.parse(to));

      }

      //设置抄送人

      if(cc!=null){

         mimeMsg.setRecipients(javax.mail.Message.RecipientType.CC,javax.mail.internet.InternetAddress.parse(cc));

      }

      //设置暗送人

      if(bcc!=null){

         mimeMsg.setRecipients(javax.mail.Message.RecipientType.BCC,javax.mail.internet.InternetAddress.parse(bcc));

      }

      //设置邮件主题

      //mimeMsg.setSubject(subject);

      mimeMsg.setSubject(subject,"gb2312");

      //设置邮件内容

      mimeMsg.setDataHandler(new javax.activation.DataHandler(new pipi.jaf.StringDataSource(body,"text/html")));

      //发送邮件

      javax.mail.Transport.send(mimeMsg);

   }

}

本套程序使用到JavaMail和JAVABEANS(TM) ACTIVATION FRAMEWORK(JAF)：

    JavaMail

        [url]http://java.sun.com/products/javamail/[/url]

    JAVABEANS(TM) ACTIVATION FRAMEWORK(JAF)

        [url]http://java.sun.com/products/javabeans/glasgow/jaf.html[/url]


杂谈 By Haha

作者：redact

email: redact@163.net

日期：2001-3-22 19:22:17

By Haha

伪软的操作系统越来越不错，但伪软的开发工具并没有多少长进，典型的VC++，除了功能资料齐全庞大无比外也没太多起眼的东西，而且伪软似乎从来做不好调试器，CodeView或WinDbg可以说摆得上桌的最差的调试器了。用WinDbg调试DeviceDriver简直是不想让人活，很奇怪伪软连那么复杂的OS也做得出，为什么就做不出象SoftIce之类的Debuger？

以前的Borland很可怜，它先C++Builder之前推出Delphi肯定是个错误，pascal和C/C++有一定距离，最起码不能紧跟伪软OS的发展潮流，伪软出一套SDK，就得有一套pascal版本来对应，SDK还不算什么，C++到pascal的翻译毕竟只要不怕麻烦还是很容易，关键是伪软时不时来一个所谓的新技术，当初BC++支持VBX，后来支持OCX，现在支持COM都是比较滞后的。现在的BC好一点，直接支持MFC，伪软出什么东西拿来直接用就是了，Delphi不会这么方便的。伪软在骑着马跑呢。不过现在谁还会用BC呢？！

究竟是什么导致C++Builder的连接速度奇慢？当然不是我们能知道的，不过我想C++Builder用的VCL库一定是Delphi的，说不定还是用Delphi编译出来的代码，如果CB的VCL库全是C代码，不知道会不会好一些。

Borland的程序员一定是比伪软的天才们更天才，因为他们从来都是懒得写help，懒得给大家多少资料，那些天才天才们一定是想“这很简单呀，那也很简单呀。。。”，我们可不是天才天才，最多最多有人或许算天才。

不大明白PowerBuilder为什么还有那么多人在使用，VB的数据库功能或许没有PB强，但起码和OS关系还是亲密一些。VB上手也很快，为什么大家一想到数据库就想PB呢？PB上WEB还要个Plugin，可是用VC,VB,delphi之类做个ActiveXcontrol也是简单得不得了的呀。反正有想次让PB打印个条码，简直是辛苦死了，最后还是用Delphi做了个OCX了事。

伪软把OS押到windows上，真的UNIX就会被消灭了吗？UNIX各种版本漫天飞，伪软为什么不拿几个人也去做个像样点的UNIX？哦，以前的XENIX倒是伪软的UNIX，不过好像已经灭绝了吧。

美国司法部最好能象强行解散AT&T一样把伪软也解散了，伪软现在有多少人？一万？二万？把这些人分开了美国大概能增加10万个新就业机会吧。

下面所有的叙述中只用到“程序”而不是“软件”，因为觉得自己做的东西和“软件”有一定距离，我想并不是仅对自己如此，对许多现在做程序的都是如此。大概因为自己是个“职业”的程序员的缘故（我并不是学计算机的科班出身，自动化专业，这样的专业除了在前苏联，其他国家多是没这个系没这个专业的），时常会想什么样什么样的人才能算真正的程序员，现在做程序的真的很“泛滥”，在学校里的学生没一个毕业的时候没写过程序的，多的上万行的代码，少的也会有几百行。我们本科在校的时候流行Ｃ语言。很感谢那时候流行的是Ｃ而不是现在学校里流行的ＦｏｘＰｒｏ和ＰＢ，因为直到现在也还流行Ｃ语言才是程序员使用的语言的说法，自己很虚荣，当然也以自己Ｃ多少通一些感到高兴和骄傲。

在ＤＯＳ时代，真正的“高手”必须精通汇编，大家以做ＴＳＲ、加密解密、未公开ＤＯＳ核心、病毒以及在越来越高档的显示卡编程作为自己进阶的见证。在当时，ＤＯＳ没有秘密，整个ＤＯＳ完整的反汇编代码随处可见，几乎人人都能说出点ＤＯＳ所谓的秘密。我见过一个真正的汇编高手，见过他在短时间内做出了一部当时很流行的弹出式词典（ＤＯＳ的），各种各样的屏幕取词自不消说，而且包罗了所有ＤＯＳ命令，计算机知识，当时的Ｍｉｃｒｏｓｏｆｔ和Ｂｏｒｌａｎｄ　Ｃ＋＋的所有帮助。这样还算不上什么，让我比较佩服的是他的程序在Ｗｉｎｄｏｗｓ运行的时候也能正常弹出，虽然自己的程序也曾几乎替换掉整个ＩＮＴ２１，但对他做到的这点真的很佩服。

Ｗｉｎｄｏｗｓ对所有的ＤＯＳ高手们是沉重的打击，尤其是从９５开始。高手在ＤＯＳ下惯用的伎俩开始处处受制约。高手们所乐道的未公开核心到９５基本止步，病毒也只能拿ＷＯＲＤ文档出出气了。ＴＳＲ也失去了意义，在显示卡上做出的各种特效也成为历史。汇编在程序员级基本灭迹。ＢＢＳ上的加密解密、病毒板慢慢地成为了ＤＯＳ时代的遗迹。Ｗｉｎｄｏｗｓ下面的加密说实话都很臭，连我这样的ＤＯＳ下水水的水平也能很轻易地用ＳｏｆｔＩＣＥ见１０个干掉７，８个。中国程序员们的网络时代快开始了。

高手们新的对象是网络，ＴＣＰ／ＩＰ是第一个目标，但Ｗｉｎｓｏｃｋ编程很快也没有了意思。善意的、恶意的网络攻击开始泛滥。对网络我的知识一直很有限。但可能整个中国的网络水平也不是很高，这和中国的网络条件有关，不过我想如果中国的网络现在就和国外一样流行、大规模的网络互联、我们对网络的依赖也和美国人一样强的话，凭目前国内网络的安全状况，中国的高手会迅速滋生，整个中国的网络将时时处于瘫痪。现在不行，毕竟网络抓包还比较初级。

Ｗｉｎｄｏｗｓ下如果能精通Ｃ，并且能够紧跟得上伪软的跑动，那绝对就是高手了，会不会汇编并不重要，本来也是，即便是设备驱动，直至Ｗｉｎｄｏｗｓ本身也绝大部份是Ｃ写的。但Ｃ高手们的好日子也不长久。操作系统的进化使高手平常化，业余程序员的数量开始剧增。开发工具日益繁多，刚开始做程序的人们不管使用什么工具、不管他们的代码质量、也不管他们的程序结构如何，他们也能很快做出至少表面上和高手们做出的东西一样好看甚至更漂亮的程序来。懂不懂软件工程无所谓，懂不懂多少数据结构无所谓，懂不懂ＯＳ无所谓，越来越方便的开发工具快让傻瓜也能写程序了。这样对出品ＯＳ的公司肯定很得意，因为不管是谁写程序，程序都在它们的ＯＳ上跑。当然它们的ＯＳ也必须做得越来越稳定，保证无论你的代码写得多烂，都不会把ＯＳ整烂。

ＯＳ已经基本上做到这一点了（当然对设备驱动程序例外，稍微烂一点就能把ＯＳ整熄火）。

不过对个人高手来说，又多了一个新的生财之道。软构件重用越来越频繁使用，高手们可以不停地做构件来卖钱，别小看这个，一般构件的价格最少也在２０ＵＳＤ左右，真的是很赚钱的业务。不过这只对国外有效，中国人的习惯不是用了再买，而是用了就开始想办法解开。不要钱的东西在中国流行很快。

应该说自己还是很有点“天才”的，似乎还早在２８６ＤＯＳ时代就预见了计算机的软硬件会飞速发展，当时的时尚过不久就会是昨日黄花，所以自己对汇编一直不曾刻苦过，对直接操作显示卡之类的的程序也做得很烂。因此工夫全放在Ｃ上。从ＢＣ到ＶＣ一直紧跟不舍，中间又顺带开始用Ｄｅｌｐｈｉ。现在自己又在开始“天才”地预计ＪＡＶＡ也不会长久，即便有朝一日ＪＡＶＡ一统江湖，想必也是我发苍视茫的时候了。但愿这个预见不会差太远，否则就亏惨了，到现在对ＪＡＶＡ的了解也全是来自Ｃ＋＋的。

窃以为ＪＡＶＡ的所谓跨平台是假的，目前的ＪＡＶＡ多是需要解释器的，这个解释器当然是跨不了平台的，有多种ＵＮＩＸ版本、也有ＯＳ／２、Ｍａｃ　ＯＳ、Ｗｉｎｄｏｗｓ版本。如果将ＪＡＶＡ程序编译成机器指令代码，跨平台更是无从谈起。总之ＪＡＶＡ的跨平台是要借助工具的。我对ＪＡＶＡ认识不深，也许我说的是一派胡言。

深有同感.计算机技术发展迅速,3年前的技术,现在已经淘汰了.所以,很多人,包括我.对学习计算机语言一直处于一种徘徊,从汇编到C,到vfp,到pb,无所不学.也就不所谓精通了.凡是将要用的,就立刻学,而且往往能立竿见影.然后,又徘徊.如此.如此.不想haha运气好,能做专职程序员,紧跟microsoft,我等闲人,只好感慨,计算机是年轻人的世界.虽然,我好像还不太老.

接下来想说说ＶＢ，真正的程序员从来对ＢＡＳＩＣ都是不屑一顾，但是这种情形在逐渐改变。原始时代的程序员都是天才，写出的程序也只有天才才能看懂，他们甚至精细到单条机器指令来考虑自己的程序结构和算法，这些程序员们这样的手段到现在已经没多少用武之地了。今天大家要用Ｃ语言才能深入系统底层，但说不定到某一天ＶＢ也能做到了，程序员们轻视ＶＢ有一点原因就是ＢＡＳＩＣ太简单，但遵循简单就是美的原则再加上伪软的不懈努力，ＶＢ可能某一天也会开始直接支持对象了。等到Ｗｉｎｄｏｗｓ真正完成大一统的时候，大概只会存在两种开发语言了－ＶＣ和ＶＢ，而且９０％的人在用ＶＢ。伪软真的很过份，它让我们失去了摸索的机会，真不知道是福还是祸。对ＶＢ，我也一直不曾用心过，不过曾经做过一个ＢＡＳＩＣ的解释器。

到目前为止，绝大多数程序员们依然看不起ＶＢ。但喜欢ＶＢ的人大概是是比ＰＢ多一点。至少我是如此，在我眼里，ＰＢ是最没有前途的目前却又有很多的业余拥有者。ＰＢ有ＶＢ的所有缺点却没多少ＶＢ的优点。ＰＢ的可视化是所有可视化语言里面最差的一种，也是最不开放的一种。真的很少见到过有计算机科班出身的会去用ＰＢ的。ＰＢ的前景暗淡，因为它在构建数据库应用程序方面的优势正在以越来越快的速度丧失。

既然说到这，有一个东西是不能不说的，那就是Notes，以前在学校的时候的确没听说过这玩意，第一次接触到是在华为，人人都有一个帐号，大家的交流主要通过Notes邮件，包括会议统治，使用是如此频繁，以致很多经理从上班一打开Ｎｏｔｅｓ，不到下班走人是不会关闭的，他们的耐心真的很好，可以忍受由此而降低的系统效率，要知道Ｎｏｔｅｓ也是和臭名昭著的ＷＯＲＤ一样，都是吃内存的好手。华为的Ｎｏｔｅｓ系统已经很庞大了。但一直不清楚是哪些人来维护的，但自己想大概就是那些秘书小姐们吧,从此自己的印象就是Ｎｏｔｅｓ就是秘书小姐们做的，做Ｎｏｔｅｓ的就应该是她们。:)，后来又回到武汉，才发现Ｎｏｔｅｓ的应用其实相当广泛，国内几乎所有的办公自动化都是Ｎｏｔｅｓ做的，做Ｎｏｔｅｓ的也几乎都不是秘书小姐们。和Ｎｏｔｅｓ比起来，ExchangeServer真的还只是个小弟弟，不过伪软家的小孩长得都很快的。

以前小时侯我们总说是沐浴着什么什么成长起来的，那么作为程序员，我们现在可以说是沐浴着Ｂｏｒｌａｎｄ而成长的。以前在一些文章里也提到过多次Ｂｏｒｌａｎｄ，作为一个中国的程序员，自己真的很同情Ｂｏｒｌａｎｄ，因为从ＴＣ２.０到ＢＣ＋＋５.０，几乎用过它的每个版本，但没给过Ｂｏｒｌａｎｄ一分钱，所以在同情之余，应该还有点内疚，不只是自己，我想中国的程序员们都要有这种想法:)。ＢＣ版本到4.52都一直是最优秀的Ｃ＋＋编译器，Ｂｏｒｌａｎｄ停止继续开发ＢＣ是明智的，目前即使在国内，除非是要维护以前的老版本，是没有人再继续使用ＢＣ的。

说起Ｂｏｒｌａｎｄ真是很无奈，因为它的东西最开始的时候总是最好的，但慢慢的结局都是一样，被淘汰。ＢＣ是很好的例子，很不幸地，各种各样的迹象表明Delphi很可能就是下一个被淘汰的对象。Delphi2很不错，所以吸引了一大批原Ｃ语言程序员。Delphi3依然很经典，但到了Delphi4就开始出问题了。Ｂｏｒｌａｎｄ如果想要和伪软竞争，就不应该想着怎么和伪软去比大和全，它没这个精力和本事。恩，发现了，Ｂｏｒｌａｎｄ的东西版本到３就是快到极限了。。。好可怕。这样看来Delphi,C++Builder都快不行了。。。

Delphi真的很好，但自己不想陷得太深，BC就是前车之鉴，所以每次用Delphi做的都是自己的小玩具，以后也不用维护的。如果是正儿八经的西，我还是会用VC去做的。

Ｂｏｒｌａｎｄ当然已经不叫Ｂｏｒｌａｎｄ了，不过出于对它的怀念，我总会叫它Ｂｏｒｌａｎｄ而不是什么Ｉｎｐｒｉｓｅ。

网络的发展速度甚至超过了你眨眼的速度，自己的网络知识起源于教育网的开通。网络的发展现在正处于混战，我觉得以后一种比较理想的应用程序模式就是Ｗｅｂ化（条件是网络速度足够快），未来的软件应该不存在客户应用软件的说法了，客户就只有浏览器，浏览器就是操作系统，客户一边下载一边使用，当然下载的都是一个个功能独立的模块。这样的好处就是大家再也不用为应用软件的版本更新而头疼了，软件公司也省去了庞大的售后服务费用和销售费用。而且这样对回收资金也是很有利的，不给钱就不让访问。加强服务器的安全性总比在软件上做上一大堆的加密措施来得方便、简单和有效。


在JAVA应用程序中如何实现FTP的功能 (转)

作者：jeru

email: jeru@163.net

日期：8/6/2001 9:50:36 AM

在JAVA应用程序中如何实现FTP的功能

大连捷通电脑技术有限公司

王 淼

---- 在JAVA的编程中，您也许会遇到FTP方面的编程，本文就来演示如何实现它。

---- 本程序是由JBUILDER2.0来开发的，为了节约篇幅我只列出主要的三个部份。FtpList 部分是用来显示FTP服务器上的文件（附图略）。GetButton部分为从FTP服务器下传一个文件。PutButton 部分为向FTP服务器上传一个文件。别忘了在程序中还要引入两个库文件（import sun.net.*，import sun.net.ftp.*）。以下是这三部分的JAVA源程序。

---- 1）显示FTP服务器上的文件

void ftpList_actionPerformed(ActionEvent e) {

    String server=serverEdit.getText();

//输入的FTP服务器的IP地址

    String user=userEdit.getText();

//登录FTP服务器的用户名

    String password=passwordEdit.getText();

//登录FTP服务器的用户名的口令

    String path=pathEdit.getText();

//FTP服务器上的路径

       try {

       FtpClient ftpClient=new FtpClient();

//创建FtpClient对象

       ftpClient.openServer(server);

//连接FTP服务器

       ftpClient.login(user, password);

//登录FTP服务器

       if (path.length()!=0) ftpClient.cd(path);

       TelnetInputStream is=ftpClient.list();

       int c;

       while ((c=is.read())!=-1) {

System.out.print((char) c);}

       is.close();

       ftpClient.closeServer();//退出FTP服务器

    } catch (IOException ex) {;}

  }

2）从FTP服务器上下传一个文件

 void getButton_actionPerformed(ActionEvent e) {

    String server=serverEdit.getText();

    String user=userEdit.getText();

    String password=passwordEdit.getText();

    String path=pathEdit.getText();

    String filename=filenameEdit.getText();

      try {

       FtpClient ftpClient=new FtpClient();

       ftpClient.openServer(server);

       ftpClient.login(user, password);

       if (path.length()!=0) ftpClient.cd(path);

       ftpClient.binary();

       TelnetInputStream is=ftpClient.get(filename);

       File file_out=new File(filename);

       FileOutputStream os=new

       FileOutputStream(file_out);

       byte[] bytes=new byte[1024];

       int c;

       while ((c=is.read(bytes))!=-1) {

          os.write(bytes,0,c);

       }

       is.close();

       os.close();

       ftpClient.closeServer();

    } catch (IOException ex) {;}

  }

3）向FTP服务器上上传一个文件

 void putButton_actionPerformed(ActionEvent e) {

    String server=serverEdit.getText();

    String user=userEdit.getText();

    String password=passwordEdit.getText();

    String path=pathEdit.getText();

    String filename=filenameEdit.getText();

      try {

       FtpClient ftpClient=new FtpClient();

       ftpClient.openServer(server);

       ftpClient.login(user, password);

       if (path.length()!=0) ftpClient.cd(path);

       ftpClient.binary();

       TelnetOutputStream os=ftpClient.put(filename);

       File file_in=new File(filename);

       FileInputStream is=new FileInputStream(file_in);

       byte[] bytes=new byte[1024];

       int c;

       while ((c=is.read(bytes))!=-1){

    os.write(bytes,0,c);}

       is.close();

       os.close();

       ftpClient.closeServer();

    } catch (IOException ex) {;}

  }

}

---- 本程序在WIN95/98/NT,Jbuilder2.0 的环境下编译通过。


在Java中获取系统属性

作者：五一

日期：2001-6-9 13:27:20

在Java中获取系统属性

大庆油田有限责任公司 苏洋

01-5-22 上午 11:12:51

--------------------------------------------------------------------------------

Java语言以其面向对象、跨平台、可移植性好、安全性高等优点，受到众多编程人员的青睐，越来越多的人将其作为应用软件开发语言的首选。

在Java应用程序运行时，特别是需要在跨平台工作环境下运行时，需要确定操作系统类型、用户JDK版本和用户工作目录等随工作平台变化的信息，来保证程序正确运行。一般情况下，可以利用JDK提供的系统属性类（Properties）中的方法，快速地获取工作环境信息。另外，程序开发人员还可以定义与应用程序相关的系统属性文件，在用户程序执行过程中动态地加载程序员定义的属性文件来控制程序运行。

本文通过对系统属性类的分析，结合实际例子介绍如何定义系统属性文件，并讨论安全策略文件的定义。

属性类

Java Properties类的继承关系如下：

java.lang.Object

＋－－java.util.Dictionary

＋－－java.util.Hashtable

＋－－java.util.Properties

在应用程序开始执行时，程序首先读取系统的默认属性。如果定义了用户属性文件，则应用程序加载该属性文件。程序运行过程中可以根据执行情况动态地修改属性定义，并在程序结束运行前，保存属性文件。

获取属性的方法：

●contains(Object value)、containsKey(Object key)：如果给定的参数或属性关键字在属性表中有定义，该方法返回True，否则返回False；

●getProperty(String key)、getProperty(String key, String default)：根据给定的属性关键字获取关键字值；

●list(PrintStream s)、list(PrintWriter w)： 在输出流中输出属性表内容；

●size()：返回当前属性表中定义的属性关键字个数。

设置属性的方法：

●put(Object key, Object value) ：向属性表中追加属性关键字和关键字的值；

●remove(Object key) ：从属性表中删除关键字。

获取系统属性

系统属性是指与用户程序相关的操作系统配置信息以及软件信息。通常与用户程序相关的属性关键字包括：

●file.separator : 文件分隔符， Windows环境下为“\"，Unix环境下为“/”；

●user.home ：用户主目录；

●java.home ：Java实时运行环境的安装目录；

●java.ext.dirs ：JDK的安装目录；

●os.name ：操作系统名称；

●user.name ：用户登录名称；

●os.version ：操作系统版本；

●path.separator ：当前操作系统的路径分隔符；

●user.dir ：当前用户程序所在目录。

下面结合例子说明获取系统属性的方法。

/＊getSystemProperties.java＊/

import java.util.Properties;

public class getSystemProperties

{

public static void main(String args[])

{

//通过获得系统属性构造属性类 prop

Properties prop = new Properties(

System.getProperties() );

//在标准输出中输出系统属性的内容

prop.list(System.out);

}

//根据获取的系统属性确定程序执行流程

……

}

上述程序执行后，在Windows环境中产生类似如下的输出：

user.home=C:\WIN95

java.home=D:\JDK1.2\JRE

java.ext.dirs=D:\JDK1.2

os.name=Windows 95

user.name=Office

java.vm.name=Classic VM

os.version=4.10

path.separator=;

file.separator=\

user.dir=D:\JavaTest

属性文件的操作方法

Java程序开发人员可以通过定义属性文件来设置程序运行的参数。属性文件是程序外部文件，在应用程序初始运行时，可以通过读取属性文件来获取有关程序运行的参数。例如，在程序运行时需要在程序外部控制程序的执行流程，这时可以通过定义属性文件的方法达到目的。下面结合例子说明属性文件的操作方法：

/＊PropertyFile.java＊/

//引入相关类

import java.io.＊;

import java.util.Properties;

public class PropertyFile

{

//定义文件输入和输出流

static FileInputStream fis;

static FileOutputStream fos;

public static void main(String args[])

{

//生成新的属性对象

Properties prop = new Properties();

try

{

//生成文件输入和输出流，输入流指向用户已经定义的属性文件，输出流指向由应用程序新定义的属性文件

fis = new FileInputStream

(“firstProp.txt”);

fos = new FileOutputStream

(“secondProp.txt”);

}

catch(FileNotFoundException e)

{

System.out.println(“Cannot create the file stream”);

}

try

{

//从输入文件加载系统属性

prop.load(fis);

//根据程序执行情况更改属性关键字的值

prop.put(“Switch”,“1”);

//输出新属性文件secondProp.txt

prop .save ( fos ,“－－ a new properties file－”);

}

catch(IOException e)

{

System.out.println(“Exception in repleace the keyword”);

}

}

}

在程序执行之前，用户必须首先定义属性文件firstProp.txt，该文件内容如下：

Switch= 0

Version= 1.0

Directory= JavaTest

程序运行后，输出新的属性文件secondProp.txt，该文件内容如下（注意观察两个文件关键字Switch内容的区别）：

＃－－a new properties file－－

＃Sun Mar 11 21:22:40 CST 2001

Switch= 1

Version= 1.0

Directory= JavaTest

从上例可以看出，通过JDK提供的属性类Properties可以方便地在程序外部对应用程序的执行流程进行控制，从而简化了编程难度，使程序流程可控性更好。

安全策略文件

Java提供的应用程序环境的安全策略，使得不同的代码对系统资源拥有不同的访问许可。Java应用程序安全策略由Policy对象来表达，通过定义安全策略文件来实现。Java 1.2的安全策略文件分为三级：系统安全策略文件、用户安全策略文件和缺省安全策略文件。当Java应用程序启动后，按照先后顺序加载安全策略内容。下面结合典型的安全策略文件内容对安全策略文件的定义进行说明：

grant

{

//对系统和用户目录设置“读”的权限

permission java.util.PropertyPermission “user.dir”,

“read”;

permission java.util.PropertyPermission “user.home”,“read”;

permission java.util.PropertyPermission “java.home”,“read”;

permission java.util.PropertyPermission “java.class.

path”,“read”;

permission java.util.PropertyPermission “user.name”,“read”;

//其他安全策略内容

};

上述安全策略文件定义了应用程序对用户目录、用户登录目录、JDK安装目录、用户名称等系统内容具有读的权限。

（网页编辑：徐向阳）


在JSP编译的时候，服务器内部做了什么？

作者：jspfuns

日期：2000-8-9 11:32:12

作者/出处：Blueski编译

在JSP第一次获得请求时，不管请求来自于客户端浏览器还是服务器上的servlet, JSP文件将被JSP引擎（JSP engine)转换成为一个servlet。而这个引擎本身也是一个servlet，在JSWDK，它就是 JspServlet。

在编译时候如果发现jsp文件有任何语法错误，转换过程将中断，并向客户端发出出错信息；而如果编译成功了，则所转换产生的servlet代码被编译，然后该servlet被JSP引擎加载到内存中。此时JSP引擎还请求了jspInit()方法的执行，并对此servlet做初始化。jspInit()方法在servlet的生命周期中只被请求一次。然后jspService()方法被调用来处理客户端的请求和回复操作。对于所有的随后而来的对该JSP文件的请求，服务器将检查该.jsp文件自最后一次被存取后是否经过修改。如果没有修改则请求将交给还在内存中的servlet的jspService()方法以一种同时发生的方式加以处理。注意，由于 servlet始终驻于内存，所以响应是非常快的。

如果.jsp文件被修改了，服务器将自动地对文件重新编译，并将结果取代内存中的servlet，并继续上述处理过程。

虽然JSP效率很高，但在第一次调用时由于需要转换和编译而有一些轻微的延迟。 此外，如果在任何时候如果由于系统资源不足的原因，JSP引擎将以某种不确定的方式将servlet从内存中移去。当这种情况发生时jspDestroy()方法首先被调用, 然后servlet实例便被标记加入“垃圾收集”处理。

相关内容：

jsp预编译器：

Jakarata Tomcat3.1 JSP服务器 (http://jakarta.apache.orgJSP编译器(JSPC)，可以进行预编译，既可以定义一个web应用的文件目录，也可以指定某个特定jsp文件来进行编译。

另外，oracle也有jspc


在JSP页面中实现检索数据的分页显示

作者：zergman

email: zergman@chinaasp.com

日期：6/12/2001 1:05:13 PM

发布者:flyfox

在页面中，当检索的数据很多时，通常需要分页显示数据，并要实现翻页。

下面将通过一些例程来说明实现JSP页面翻页技术的实现。

首先，在JSP中，通过JAVA servlet 来检索数据，而用JSP来调用结果来显示。

因而，此技术可分为两个部分（依赖关系）：

1. 在服务器端的servlet 中的实现

要点：

& 将查询条件保存到session中，取session中的查询条件

& 设置Statement对象的MaxRows（确定一页显示多少行数据）

& 顺序地通过执行SQL语句查询数据，按maxRows 来检索一个maxRows的数据，

下一页再检索下一maxRows的数据，以此类推。

2. 在JSP中的显示实现

要点：

& 显示maxRows条数据

& 通过“下一页”按钮或超链再次调用刚才的servlet查询下一maxRows的数据

流程如图所示：

下面通过一个例程来说明（一个servlet程序和一个JSP程序）：

l querymedicine. java (Medicine. querymedicine)

package Medicine;

import javax.servlet.*;

import javax.servlet.http.*;

import java.io.*;

import java.util.*;

import java.sql.*;

import Medicine.medicinelist;

import Medicine.searchData;

////////////////////////////////////////////////////

// 接---mutiquery.jsp页面，并从request中得到5条查询条件 ///

// 按条件查询药品，并将结果存进session的“medicinelist”中。 ///

// 张乾 2000年3月15日 ///

////////////////////////////////////////////////////

public class querymedicine extends HttpServlet {

private DBConnectionManager connMgr;

//Initialize global variables

public void init(ServletConfig config) throws ServletException {

super.init(config);

connMgr = DBConnectionManager.getInstance();

}

//========================处理 HTTP Get 请求============================

public void doGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

Statement stmt=null;

Connection con = connMgr.getConnection("medicine");

if (con == null) {

response.sendRedirect("/medicine/con_error.html");

return;

}

// ==================== 创建数据库Statement =============================

try {

stmt=con.createStatement();

}

catch (Exception e)

{

connMgr.freeConnection("medicine",con);

response.sendRedirect("/medicine/stmt_error.html");

return;

}

//-------------------------------------------------------------

long all_count=0; //存满足条件的药品总数

//===================从request中得到session======================

try{

HttpSession session = request.getSession(false);

if (session == null) {

connMgr.freeConnection("medicine",con);

response.sendRedirect("/medicine/session_error.html");

return;

}

//-----------------------------------------------------------

//======从“下一页”提交来的参数取得前页最后一条数据的药品编码=======

String ll_pos=request.getParameter("pos");

//如果pos=-1则表示要从头开始查

//----------------------------------------------------------

//=====================查询用的SQL语句串==========================

String sqlstatment="SELECT medicines.ypbm, medsmalltypes.zlmc, medsupertypes.clmc,

medtypes.dlmc, medicines.ypm, medicines.zyyx, medicines.ypzy FROM medicines,

medsmalltypes,medsupertypes,medtypes WHERE medicines.ypbm>"+ll_pos+"

and (( medsmalltypes.zlbm = medicines.zlbm ) and ( medsupertypes.clbm = medicines.clbm )

and ( medtypes.dlbm = medicines.dlbm ) and ( ( medicines.del_flag = 0 ) ";

//-----------------------------------------------------

String zlbm;

String dlbm;

String clbm;

String zyyx;

String ypm;

searchData slist=new searchData();

synchronized (session) {

slist=(searchData)session.getAttribute("searchList");

}

if(ll_pos.equals("-1")){

//===表示不是由“下一页”过来要从头开始查数据==========

if(slist!=null){

synchronized (session) {

session.removeAttribute("searchList");

}

}

slist=new searchData();

//=============从设置查询条件页面取得查询条件的参数===================

zlbm=request.getParameter("zlbm");

dlbm=request.getParameter("dlbm");

clbm=request.getParameter("clbm");

zyyx=request.getParameter("zyyx");

ypm=request.getParameter("ypm");

//------------------------------------------------------

//=================将查询条件参数通过Vector存到session中==========

slist.setZlbm (zlbm);

slist.setDlbm (dlbm);

slist.setClbm (clbm);

slist.setZyyx (zyyx);

slist.setYpm (ypm);

synchronized (session) {

session.setAttribute ("searchList",slist);

}

//---------------------------------------------------------

}

//--------------------------------------------------------

else

{

//============================取出查询条件参数====================

if(slist!=null){

zlbm=slist.getZlbm();

dlbm=slist.getDlbm();

clbm=slist.getClbm();

zyyx=slist.getZyyx();

ypm=slist.getYpm();

}

else{

if(stmt!=null) stmt.close();

stmt.setMaxRows(0);

connMgr.freeConnection("medicine",con);

response.sendRedirect("/medicine/session_error.html");

return;

}

//-------------------------------------------------

}

String sql2="select count(*) from medicines where del_flag=0";

//=====================根据条件参数设置SQL语句=======================

if(!(zlbm.trim().equals("0"))) {

sqlstatment+=(" and ( medicines.zlbm='"+zlbm.trim()+"' ) ");

sql2+=(" and zlbm='"+zlbm.trim()+"'");

}

if(!(dlbm.trim().equals("0"))) {

sqlstatment+=(" and ( medicines.dlbm='"+dlbm.trim()+"' )");

sql2+=(" and dlbm='"+dlbm.trim()+"'");

}

if(!(clbm.trim().equals("0"))) {

sqlstatment+=(" and ( medicines.clbm='"+clbm.trim()+"' ) ");

sql2+=(" and clbm='"+clbm.trim()+"'");

}

if(!(zyyx.trim().length ()==0)) {

sqlstatment+=(" and ( medicines.zyyx like '%"+zyyx.trim()+"%' ) ");

sql2+=(" and zyyx like '%"+zyyx.trim()+"%'");

}

if(!(ypm.trim().length ()==0)) {

sqlstatment+=(" and ( medicines.ypm like '%"+ypm.trim()+"%' ) ");

sql2+=(" and ypm like '%"+ypm.trim()+"%'");

}

sqlstatment+=" )) ORDER BY medicines.ypbm ASC ";

//-------------------------------------------------------------

Vector list = new Vector();

//================= 设置一页显示的数据条数（一次检索出的数据条数）===========

stmt.setMaxRows(25);

//----------------------------------------------------------------------

//===================执行查询将结果放到ResultSet中================

ResultSet rs = stmt.executeQuery(sqlstatment);

ResultSet rs2 = stmt.executeQuery(sql2);

//------------------------------------------------

if(rs==null){ //如果没有查询结果数据

if(stmt!=null) stmt.close();

stmt.setMaxRows(0);

connMgr.freeConnection("medicine",con);

response.sendRedirect("/medicine/no_medicine.html"); //定向到一个页面

return;

}

//====================将药品信息填入数据对象并存入Vector中================

if(rs2.next()){

all_count=rs2.getLong(1); //取得总条数

}

if(rs2!=null)rs2.close();

while(rs.next()){

medicinelist m = new medicinelist();

m.setYpbm(rs.getInt("ypbm"));

m.setZlmc(rs.getString("zlmc"));

m.setClmc(rs.getString("clmc"));

m.setDlmc(rs.getString("dlmc"));

m.setYpm(rs.getString("ypm"));

m.setZyyx(rs.getString("zyyx"));

m.setYpzy(rs.getString("ypzy"));

m.setClbm(clbm);

m.setDlbm(dlbm);

m.setZlbm(zlbm);

list.addElement(m);

}

//----------------------------------------------------------

if(rs!=null)rs.close();

stmt.setMaxRows(0);

if(stmt!=null)stmt.close();

connMgr.freeConnection("medicine",con);

//========================存入session中===========================

synchronized (session) {

session.setAttribute("medicinelist",list);

}

//-----------------------------------------------

}catch (SQLException e){

connMgr.freeConnection("medicine",con);

response.sendRedirect("/Medicine/sql_error.html");

return;}

//=======================重定向到一个JSP页面==========================

String url="/medicine/querymedicine.jsp?all_count="+all_count;

ServletContext sc = getServletContext();

RequestDispatcher rd = sc.getRequestDispatcher(url);

rd.forward(request, response);

//-------------------------------------------------------

}

public void destroy() {

// =================== 在Servlet退出时终止数据库连接,取消Statement对象

if(connMgr!=null) connMgr.release();

//------------------------------------------------------------------------

}

//========================处理HTTP Post 请求 ============================

public void doPost(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

doGet(request,response);

}

//==========================取得 Servlet 信息 ============================

public String getServletInfo() {

return "medicine.querymedicine Information";

}

}

l querymedicine.jsp

<%@ page language="java" session="true" import="java.util.*,

Medicine.method, Medicine.medicinelist"contentType="text/html;charset=gbk" %>

<script language="javascript">

function next(){

var ls_pos=document.form2.maxpos.value;

document.location="/servlet/Medicine.querymedicine?pos="+ls_pos;

}

function detail(ypbm){

document.location="/servlet/Medicine.Detail?ypbm="+ypbm;

}

</script>

<!--add head-->

<div align="center">

<body topmargin="0" leftmargin="10" rightmargin="10” bgcolor="#FFFFFF">

<div align="center">

<center><IFRAME WIDTH=760 height=130 NORESIZE SCROLLING=no FRAMEBORDER=0

MARGINHEIGHT=0 MARGINWIDTH=0 SRC="..\medicinetitle.html"></iframe>

</div>

<!--finish-->

<title>商品列表</title>

<%

method md = new method();

Vector sklist;

synchronized (session) {

sklist = (Vector) session.getAttribute("medicinelist");

}

%>

<!----------------------------- 显示表头 -------------------------------->

<%

if (sklist == null or sklist.size() <= 0)

{

response.sendRedirect("/medicine/no_medicine.html");

return;

}

else

{%>

<center>您好！以下是商品的列表，共有<font color=#ff0000>

<%=request.getParameter("all_count")%></font>条符合条件的药品信息

</center>

<div align="center">

<center>

<table border="1" cellpadding="0" cellspacing="0" width="840"

bordercolorlight="#FFFFFF" bordercolordark="#000000">

<tr bgcolor="#339933">

<td width=42 align="center"><font color="#339933"><b>明细</b></font></td>

<td width=160 align="center"><font color="#FFFFFF"><b>商品名称</b></font></td>

<td width=52 align="center"><font color="#FFFFFF"><b>超类</b></font></td>

<td width=145 align="center"><font color="#FFFFFF"><b>大类</b></font></td>

<td width=145 align="center"><font color="#FFFFFF"><b>子类</b></font></td>

<td width=270 align="center"><font color="#FFFFFF"><b>描述</b></font></td>

</tr>

<!------------------------------------------------------------------------------------------------------------------->

<%

medicinelist d=new medicinelist();

for (int index=0; index < sklist.size();index++)

{

d = (medicinelist)sklist.elementAt(index);

String lshref;

if (d.getYpzy()==null or d.getYpzy().equals("http://"or d.getYpzy().equals(""))

{

lshref="";

}else{

lshref="<a href="+d.getYpzy()+">";

}

out.println("<form name=form1 method=post action=/servlet/Medicine.Detail?ypbm="+d.getYpbm()+">");

out.println("<tr bgcolor=#F3F3E9><td align=center><font color=#000000 size=2>

<input type=submit name=submit1 value=明细></font></td><td align=center><font color=#000000 size=2>");

out.println(lshref+d.getYpm()+"</a></font></td><td align=center><font color=#000000 size=2>");

out.println(d.getClmc()+"</font></td><td align=center><font color=#000000 size=2>"

+d.getDlmc()+"</font></td><td align=center><font color=#000000 size=2>");

out.println(d.getZlmc()+"</font></td><td align=center><font color=#000000 size=2>"

+md.notNull(d.getZyyx())+"</font></td></tr></form>");

out.flush();

}

%>

<form name=form2 id=form2>

<input type=hidden name=maxpos id=maxpos value=<%=d.getYpbm()%>>

</form>

<%

}

%>

</table>

<p></p>

<%

String ssll=request.getParameter("all_count");

Long temp=new Long(ssll);

long cou= temp.longValue();

if(sklist.size()==25&&cou>25)

{%>

<input type=button value=" 下页 " onclick="next(1);">

<%}%>

<%

synchronized (session) {

if(session.getAttribute("medicinelist")!=null){

session.removeValue("medicinelist");

}

}

%>

</center>

<p align="center"><input type="button" value=" 返回 " name="B3" onclick="history.back()">

<br><br><br>

<!--add bottom-->

<div align="center">

<center><IFRAME WIDTH=760 height=140 NORESIZE SCROLLING=no FRAMEBORDER=0

MARGINHEIGHT=0 MARGINWIDTH=0 SRC="..\bottom.html"></iframe>

</div>

<!--finish-->


在jsp中发送email !

作者：jspfuns

日期：2000-8-9 13:12:39

作者/出处：Blueski编译（包含了2篇文章，前篇用了sun.net.smtp，后一篇用了javamail.

一、我们可以通过任何支持sun规范中的sun.net.smtp包的JSP引擎(如JSWDK)发送mail。

(警告:使用内置的internal Sun规范包，这将影响到你的jsp程序的可移植性。)

以下scriptlet利用SmtpClient类在jsp文件中发送email。

<%@ page import="sun.net.smtp.SmtpClient, java.io.*" %>

<%

String from="gseshadri@hotmail.com";

String to="govind@jguru.com, govi@bigfoot.com";

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 beans! :-)");

message.println();

message.println("Govind Seshadri");

message.println("jGuru.com");

message.println();

client.closeServer();

}

catch (IOException e){

System.out.println("ERROR SENDING EMAIL:"+e);

}

%>

二、 JavaMail是官方的 Java mail API，可参考 http://java.sun.com/products/javamail/sun.net.smtp.SmtpClient更丰富或者说更复杂，但它是可移植的。这里重新创建了一个 MailSender类，它包含了 JavaMail API。如下所示：

// ms_ prefix is for MailSender class variables

// str prefix is for String

// astr prefix is for array of Strings

// strbuf prefix is for StringBuffers, etc.

public MailSender(

String strFrom, // sender

String[] astrTo, // recipient(s)

String[] astrBCC, // bcc recipient(s), optional

String strSubject, // subject

boolean debugging)

{

ms_strFrom = strFrom; // who the message is from

ms_astrTo = astrTo; // who (plural) the message is to

ms_debugging = debugging; // who (plural) the message is to

// set the host

Properties props = new Properties();

props.put("mail.smtp.host", ms_strSMTPHost);

// create some properties and get the default Session

Session session = Session.getDefaultInstance(props, null);

session.setDebug(ms_debugging);

try {

// create a message

ms_msg = new MimeMessage(session);

// set the from

InternetAddress from = new InternetAddress(strFrom);

ms_msg.setFrom(from);

// set the to

InternetAddress[] address = new InternetAddress[astrTo.length];

for (int i = 0; i astrTo.length; ++i)

{

address[i] = new InternetAddress(astrTo[i]);

}

ms_msg.setRecipients(Message.RecipientType.TO, address);

// set the bcc recipients

if (astrBCC != null)

{

address = new InternetAddress[astrBCC.length];

for (int i = 0; i astrBCC.length; ++i)

{

eh.dbg("astrBCC[" + i + "] is: '" + astrBCC[i] + "'");

address[i] = new InternetAddress(astrBCC[i]);

}

ms_msg.setRecipients(Message.RecipientType.BCC, address);

}

// set the subject

ms_msg.setSubject(strSubject);

// set up the string buffer which will hold the message

ms_strbufMsg = new StringBuffer();

} catch (MessagingException mex) {

mex.printStackTrace(System.err);

} catch (Exception ex) {

ex.printStackTrace(System.err);

}

}

public void ms_add(String strText)

{

ms_strbufMsg.append(strText);

}

public void ms_send()

{

try {

// set the content as plain text

ms_msg.setContent(new String(ms_strbufMsg), "text/plain");

// and away

Transport.send(ms_msg);

} catch (Exception ex) {

System.out.println("Caught exception in MailSender.ms_send: " + ex);

}

}


在JSP中写text文件

作者：jspfuns

日期：2000-8-9 11:34:09

作者：Blueski编译

使用PrintWriter对象即可写Text文件。

请参考以下示例：

<%@ page import="java.io.*" %>

<%

String str = "print me";

//always give the path from root. This way it almost always works.

String nameOfTextFile = "/usr/anil/imp.txt";

try {

PrintWriter pw = new PrintWriter(new FileOutputStream(nameOfTextFile));

pw.println(str);

//clean up

pw.close();

} catch(IOException e) {

out.println(e.getMessage());

}

%>

现在，打开imp.txt加以查看，字符串"print me"应该已经写入。

还有另一种方法，使用IN16标记库的file tag，可参考http：//sourceforge.net/project/?group_id=1282.

其示例的语法为:

<ext:file action="write|append|create|copy|move|delete|rename|read" from="<%= myfilename %>">


在Linux上安装Tomcat

作者：何志强

email: hhzqq@21cn.com

日期：2000-8-10 17:05:05

作者：何志强[hhzqq@21cn.com]

日期：2000-08-10

版本：1.0

一、软件下载

    JDK 1.2.2

        http://www.blackdown.org/java-linux.html

        http://www.mirror.ac.uk/sites/ftp.blackdown.org/java-linux/

        http://www.mirror.ac.uk/sites/ftp.blackdown.org/java-linux/JDK-1.2.2/i386/rc4/jdk-1.2.2-RC4-linux-i386-glibc-2.1.2.tar.bz2

　　Tomcat 3.1

        http://jakarta.apache.org/tomcat/

        http://jakarta.apache.org/builds/tomcat/release/v3.1/bin/jakarta-tomcat.tar.gz

二、安装JDK

    1、将下载的JDK解压到JDK安装目录，例：

          cd /usr/local/

          tar xIvf /software/jdk/jdk-1.2.2-RC4-linux-i386-glibc-2.1.2.tar.bz2

    2、设置好JAVA_HOME环境变量，它的值为JDK安装目录，如编辑/etc/profile，增加下面几行：

          JAVA_HOME=/usr/local/jdk1.2.2

          export JAVA_HOME

    3、将JDK的bin目录追加到PATH环境变量中，如编辑/etc/profile，增加下面几行：

          PATH=/usr/local/jdk1.2.2/bin:$PATH

          export PATH

三、安装Tomcat

    1、将下载的Tomcat解压到Tomcat安装目录，例：

          cd /usr/local

          tar xzvf /software/tomcat/jakarta-tomcat.tar.gz

    2、设置好TOMCAT_HOME环境变量，它的值为Tomcat安装目录，如编辑/etc/profile，增加下面几行：

          TOMCAT_HOME=/usr/local/jakarta-tomcat

          export TOMCAT_HOME

    3、Tomcat安装目录的bin子目录下的startup.sh用于启动服务,shutdown.sh为停止服务,服务的缺省端口为8080

    4、退出并重新登陆，启动服务，然后在浏览器上输入http://localhost:8080/来检查是否已经安装成功，例：

          cd $TOMCAT_HOME/bin

          ./startup.sh

          lynx http://localhost:8080/


在windows2000 server中文版上安装apache 原创

作者：飞鸟

日期：00-7-12 0:03:00

在windows2000 server中文版上安装apache

1。下载apache1.3

http://www.apache.org/dist/binaries/win32/apache_1_3_12_win32.exe

2。安装apache

我把apache安装到了D:\Program Files\Apache Group\Apache\下

然后我用notepad打开conf目录下的httpd.conf文件进行编辑

找到Port 80 改成 Port 81 ,因为我的机器已经安装了IIS 5 , 80端口已经被占用了。

找到#ServerName new.host.name,去掉#,改成 ServerName spf 我的机器名叫spf

4。运行apache

直接运行D:\Program Files\Apache Group\Apache\apache.exe

3。打开浏览器，输入http://localhost:81/,看到一个页面

说：

If you can see this, it means that the installation of the Apache web server software on this system was successful.。。。。。

说明我安装成功了。

4。然后根据这个页面的连接我寻找，把apache设置成win2k一个service服务。

幸运的是我在5minutes里面找到了方法。

先把刚才运行的那个apache程序关掉，在那个dos窗口里面按ctrl + c

我在命令行内输入

D:\Program Files\Apache Group\Apache\apache.exe -i -n "apache_svr"

net start apache_svr

----------------------------------------------------------------------

搞定！hehe 还是蛮简单的嘛。

明天晚上我们继续研究apache的进一步设置和使用。


在Windows上安装Tomcat

此电子文档属C#乐园QQ群3256045整理。
在Windows上安装Tomcat

上一页 目录 下一页

在Windows上安装Tomcat

作者：何志强

email: hhzqq@21cn.com

日期：2000-8-10 17:04:25

者：何志强[hhzqq@21cn.com]

日期：2000-07-19

      2000-08-10

版本：1.0

一、软件下载

    JDK 1.2.2

        http://java.sun.com/products/jdk/1.2/download-windows.html

        ftp://ftp.java.sun.com/pub/jdk/1.2.2/nn234097-122006/jdk-1_2_2_006-win.exe

    JDK 1.2.2 Documentation

        http://java.sun.com/products/jdk/1.2/download-docs.html

        ftp://ftp.java.sun.com/pub/jdk/1.2/aslwei001/jdk1_2_2-001-doc.zip

    Tomcat 3.1

        http://jakarta.apache.org/tomcat/

        http://jakarta.apache.org/builds/tomcat/release/v3.1/bin/jakarta-tomcat.zip

二、安装JDK

    1、运行下载的JDK可执行文件，如果是Windows 9x，请注意目录名不可以超过8位，并且不应有.字符，例如目录名不可以为jdk1.2.2，这会影响到环境变量的设置，可以将其改为jdk

    2、设置环境变量JAVA_HOME，它的值为JDK安装目录完整路径(不要以\结尾)

    3、将JDK的BIN目录追加到PATH环境变量中

三、安装Tomcat

    1、将下载的Tomcat压缩文档解压到安装目录，如果是Windows 9x，请注意目录名不可以超过8位，例如目录名不可以为jakarta-tomcat，这会影响到环境变量的设置，可以将其改名为tomcat

    2、设置环境变量TOMCAT_HOME，它的值为Tomcat安装目录完整路径(不要以\结尾)

    3、Tomcat安装目录的bin子目录下的startup.bat用于启动服务,shutdown.bat为停止服务,服务的缺省端口为8080

    4、启动服务，然后在浏览器上输入http://localhost:8080/来检查是否已经安装成功

四、环境变量设置

    Windows 9x：

        在C:\autoexec.bat中设置，如：

            set JAVA_HOME=C:\JDK

            set PATH=%JAVA_HOME%\bin;%PATH%

            set TOMCAT_HOME=C:\TOMCAT

        设置完后请重新启动计算机

    NT：

        在“控制面板”的“系统”窗口中的“环境”栏中设置，设置完后不用重启计算机


在按钮上放图标

此电子文档属C#乐园QQ群3256045整理。
在按钮上放图标

上一页 目录 下一页

在按钮上放图标

作者：令少爷

email: dj12@8848.net

日期：7/9/2001 5:08:56 PM

首先要说明的是，在组件上方便的显示图象是 swing 比 AWT 优越的特性之一，暂时我还没看到如何

在 AWT 中实现这一方法，如果你知道，请来信告诉我。

在 swing 中,button, lable 等组件都可轻易的用图象来改变它的外观，以 button 为例，当创建一

个 button 对象的时候通常是这样的：

JButton button = new JButton("Click me");

这样就创建了一个 button 对象，上面显示的文字是 Click me.

现在我们来稍做改动：

ImageIcon buttonIcon = new ImageIcon("button.gif", "a background of button");

JButton button = new JButton(buttonIcon);

这两行代码就达到了目的，在 button 上显示了图象 button.gif,注意我们用的是 ImageIcon 类，而

不是 Image 类。ImageIcon 中的第二个参数是对 ImageIcon 对象的描述，这个参数以后可能会被

assistive 技术用到。

如果想在 button 上同时显示文字和图象，还可以这样：

JButton button = new JButton("Click me", buttonIcon);

另外，还可以定义图象在 组件上的位置，如：

JButton button = new JButton("Click me", buttonIcon, JButton.CENTER);


在浏览器关闭cookie情况下进行会话管理

此电子文档属C#乐园QQ群3256045整理。
在浏览器关闭cookie情况下进行会话管理

上一页 目录 下一页

在浏览器关闭cookie情况下进行会话管理

作者：wondercn

日期：2001-3-2 11:28:01

如果浏览器不支持 cookies，或者将浏览器设置为不接受 cookies，我们可以通过 URL 重写来实现会话管理。

    实质上 URL 重写是通过向 URL 连接添加参数，并把 session ID 作为值包含在连接中。然而，为使这生效，你需要为你的 servlet 响应部分的每个连接添加 session ID 。 把 session ID 加到一个连接可以使用一对方法来简化：response.encodeURL()

使 URL 包含 session ID，如果你需要使用重定向，可以使用 response.encodeRedirectURL () 来对 URL 进行编码。

encodeURL () 及 encodeRedirectedURL () 方法首先判断 cookies 是否被浏览器 支持；如果支持，则参数 URL 被原样返回，session ID 将通过 cookies 来维持。

 下面有个例子，两个 JSP 文件：hello1.jsp 和 hello2.jsp，及它们之间的影响 。我们在 hello1.jsp 中简单的创建一个会话，并在 session 中存储一个对象实例。接着用户可以点击页面的连接到达 hello2.jsp。在 hello2.jsp 中，我们从 session 中获取原先放置的对象并显示它的内容。注意，我们在 hello1.jsp 中调用了 encodeURL() 方法来获得 hello2.jsp 的链接，使得在浏览器停用 cookies 的情况下，session ID 自动添加到 URL，hello2.jsp 仍能得到 session 对象。

  首先在启用 cookies 的情况下运行。然后关闭对 cookie 的支持，重启浏览器，再运行一次。每次你都可以看到会话管理在起作用，并能在页之间传递信息。

注意，如果你想让这个例子能在关闭了 cookies 的浏览器中工作，你的 JSP 引擎必须支持 URL 重写。

hello1.jsp

<%@ page session="true" %>

<%

Integer num = new Integer(100);

session.putValue("num",num);

String url =response.encodeURL("hello2.jsp");

%>

<a href='<%=url%>'>hello2.jsp</a>

hello2.jsp

<%@ page session="true" %>

<%

Integer i= (Integer)session.getValue("num");

out.println("Num value in session is "+i.intValue());

%>


在网页上发布统计曲线

作者：allsky

email: zhangyan_@163.com

日期：2001-4-24 14:47:39

一、引言

    在熙熙攘攘的Internet上制作极富个性化的主页、及时发布令人心动的信息

,以便吸引全世界不同肤色、不同职业的人们驻"足"观看,以致流连忘返,甚者慷慨

解囊,是Internet冲浪者心中的梦想。通过合理运用HTML脚本语言提供的表格、列

表、字体和段落的格式化、多媒体支持等,能使网页内容清晰、层次分明、可读性

强,这些已有众多文献论述,在此不再赘述。

    我们知道,在各种信息中,统计信息堪称是用得较多、具有相当说服力的信息

,而表达统计数据最直观的方式是根据这些数据绘制曲线。文献[1]用相当的篇幅

介绍了如何在Windows操作系统中编写一个具有OLE服务器能力的统计曲线绘制工

具,而本文的主题则是:如何利用Java对绘图的支持,编写一个根据统计数据绘制曲

线的小程序(Applet)Plot,通过把Plot编译生成的字节码文件Plot.class嵌入到H

TML脚本中,从而实现在网页上发布统计曲线的目的。

    二、Java编程概述

    1.Java语言规范

    Java继承了C++的基本语法,摒弃了一些过时的特征,比如预编译的头文件、宏

、条件编译,数据结构的结构体、枚举体和联合体,容易导致安全性问题的指针,以

及晦涩难懂的多重继承等,借鉴了Objective C的接口和Ada的包(package),以及内

置了Internet网络所需要的基本特征:结构中立(任何芯片、任何操作系统可以运

行同一版本的Java程序)、安全性(避免感染病毒和防止非法访问)、多线程和网络

通信等。事实上,上述被Java抛弃的特征,如指针和类的多重继承,都是由于太复杂

而遭C++程序员诅咒的;而新发展的特征,如结构中立、安全性、多线程和网络通信

等,是C++程序员极想实现而又非常难实现的特征。

    2.JDK

    Windows程序员一定都知道做Windows开发要用Windows SDK(软件开发工具),

现在,做Ja va开发的程序员也都知道编写Java小程序要用JDK(Java开发工具)了。

然而,与SDK主要采用结构体和函数等面向过程的方式提供Windows编程接口不同,

JDK采用包、界面和类等面向对象的方式提供Java编程接口。从这种意义上说,JD

K与Microsoft Visual C++的MFC,或者Bor land C++的OWL相似:它是一个类库,一

个应用框架,一个无缝集成的解决方案。

    最先发布的JDK应该算JDK 1.0.3α,它是1995年Sun公司在全球范围内进行Ja

va程序设计大奖赛时随HotJava 1.0.3α发放的。在该版本中,通用包实现得比较

少,只有java.lang、j ava.util和java.io,而其他有关用户界面和网络通信的包

都在HotJava中提供。尔后发行了1.0版,这是提供给各Java平台开发公司的,其中

,包全部独立出来,共八个:java.applet、ja va.awt 、java.awt.image 、java.

awt.peer 、java.io、java.lang、java.net 和java. util 以及一个sun.tools

.debug。本文的程序基于1.0版本编写。由于1.0.3α和1.0两个版本的包安排差别

很大,因而用前一个版本编写的程序在后一个版本的编译工具中编译有可能通不过

,要作修改。在1996年8月底,Sun公司在Internet上发布了1.1版,1.1版在1.0版基

础上稍有改进,两个版本保持完全的兼容。

    三、设计目标和程序构思

    现在我们回到本文的主题上。首先,考虑编写一个Java小程序,它的界面和功

能可以这样来描述:

    统计标题和统计数据由HTML的param标注提供,统计数据的个数没有限制;

    根据数据的取值画出带刻度的X、Y轴;

    在用线连接每个数据点的同时标出该点的坐标值;

    提供绘制三组曲线的选择。

    针对上述要求,考虑构造两个类来完成:Curve类用于实现绘制统计曲线的各个

方面;Plo t类则派生于Applet,它是本程序的小程序类,通过使用Curve类来完成统

计曲线的绘制。鉴于篇幅,下面给出实现要点及其程序。如需要全部源代码的读者

,请与笔者联系。

    1.Curve类

    Curve类构造主要基于以下几点考虑。

    (1)基类。Curve从Java所有类的基类Object派生,那么就可以不显式声明了。

class Curve {

......

}

    (2)绘图环境。根据Windows等窗口操作系统的编程经验,向显示器、打印机等

输出设备绘图是通过绘图环境来实现的。由于Java要面向所有平台,包括Windows

、Unix和Macintosh 等,它们的绘图环境(如显示器)概念很不一样。那么,Java如

何来实现绘图呢?原来,Java在java.awt包中通过Graphics类来提供了对各种绘图

设备的设备环境的抽象类封装。用Window s编程来作对照,Graphics所代表的概念

就是Windows GDI的设备环境(Device Context),也就是MFC中CDC类或OWL的TDc类

。有了这样的比较,对Graphics就比较好理解了。也就是,所有绘制直线、矩形、

椭圆、多边形、设置字体、绘制文本等操作,调用Graphics类中的相应方法即可。

下面代码是通过Curve构造器将小程序类Plot的设备环境对象g传递给Curve,以被

其他各个绘制方法使用:

public Curve(Graphics g)

{

myGC = g;

......

}

    (3)绘制算法。绘制算法就是绘制统计曲线的实现方法,包括绘制标题、坐标

轴、数据联线、标出数据点坐标等,以及其逆过程:清除这些绘制(因为当绘制其他

曲线时,得保证刷新前一次绘制的所有内容)。鉴于篇幅,下面仅给出显示数据的方

法实现showData.

public synchronized void showData(Vector v)

{

float maxY=0;

myGC.setColor(Color.red);

int xPoint;

int yPoint;

//确定Vector中的最大值。

for(int i = 0; i < v.size(); i++)

{

float temp = ((Float)v.elementAt(i)).floatValue();

if(temp > maxY)

{

maxY = temp;

}

}

//确定第一个数据点的X坐标。

xPoint = xOrigin + (xSpacing/2);

int oldX = 0;

int oldY = 0;

//在数据点处绘制一个小圆圈。

for(int j = 0; j < v.size(); j++)

{

yPoint = yOrigin(int)((axisH/maxY) *

((Float)v.elementAt(j)).floatValue());

myGC.fillRect(xPoint, yPoint, 3, 3);

//在数据点之间联线。

if((oldX != 0) && (oldY != 0))

{

myGC.drawLine(oldX-xSpacing, oldY, xPoint, yPoint)

;

}

myGC.setColor(Color.blue);

//在数据点边上写出数据值。

String coordString =

Float.toString(((Float)v.elementAt(j)).floatValue(

));

myGC.drawString(coordString, xPoint+5, yPoint - 4);

myGC.setColor(Color.red);

xPoint += xSpacing;

oldX = xPoint;

oldY = yPoint;

}

}

    2.Plot类

    Plot类构造主要基于以下几点考虑。

    (1)派生于Applet

    一个Java小程序有且只有一个类派生于Applet,这就好像在MFC中,必须有且只

有一个类派生于CWinApp一样。另外,与MFC程序不一样的是,不管包含派生于Appl

et类的源程序文件( .java)命名为什么,生成的供网页使用的字节码文件(.class

)只与该派生类同名,而与源文件名无关,这一点容易引起混淆。打个比方,如果包

含Plot类的文件为PlotTest.java,而编译生成的字节码文件名为Plot.class,而不

是PlotTest.class。最后,Plot必须声明为public,这是由于一个编辑单元必须有

且只有一个类声明为public。

public class Plot extends Applet

{

......

}

    (2)用户界面

    目前,Java在java.awt中提供小程序的用户界面所需要的接口和类(共42个类

和2个接口),其中实现了我们在Windows中见到的各种用户界面,比如,菜单、对话

框,以及各种控件,如按钮、列表框、复选框、单选框、编辑框、组合框等。鉴于

本程序要求从3组数据中选择一组进行绘制,需要选用单选框控件。单选框在java

.awt中用Checkbox和CheckboxGroup两个类联合实现:当Checkbox单独使用时,它是

复选框;而把它添加到CheckboxGroup中则变成了单选框。

public void init()

{

......

cbg = new CheckboxGroup();

//单选按钮。

cb1 = new Checkbox("数据1", cbg, false);

cb2 = new Checkbox("数据2", cbg, false);

cb3 = new Checkbox("数据3", cbg, false);

}

    (3)布局管理器

    在Java中,为了使得小程序在各种操作系统中的用户界面具有一致的外观,采

用布局管理器(Layout Manager)对用户界面的相对位置进行管理。Java在java.a

wt包中包含了流布局管理器(FlowLayout)、边界布局管理器(BorderLayout)、卡

片布局管理器(CardLayout)、网格布局管理器(GridLayout)和网格包布局管理器

(GridBagLayout)5个布局管理器。如果希望3 个单选框按钮放在绘制图形的下面

按一行排列,该如何编写代码呢?可以这样来实现:首先设置Plot小程序为边界布局

,然后为3个单选框按钮创建一个Panel对象cbPanel,用于组织3个控件,并设置cbP

anel为网格布局,最后将cbPanel添加到Plot类中,并设置其方向为南(South)。

public void init()

{

......

//设置Plot为边界布局。

setLayout(new BorderLayout());

//为3个控件申请面板对象cbPanel。

cbPanel = new Panel();

//设置网格布局管理器,并按1×3方式排列。

cbPanel.setLayout(new GridLayout(1, 3));

//为各按钮申请1个面板。

cb1Panel = new Panel();

cb1Panel.add(cb1);

cb2Panel = new Panel();

cb2Panel.add(cb2);

cb3Panel = new Panel();

cb3Panel.add(cb3);

//添加到统一的1个面板cbPanel中。

cbPanel.add(cb1Panel);

cbPanel.add(cb2Panel);

cbPanel.add(cb3Panel);

//添加cbPanel到Plot中,方向为:图形在北,按钮在南。

add("South", cbPanel);

}

    (4)读取HTML参数

    因为本程序要用到统计数据和统计标题作为小程序的参数存放在HTML文档中

,所以在开始绘制图形之前,需要从HTML读取这些参数,然后才开始显示标题和绘制

统计曲线。其中,读取统计标题和统计数据分别实现。

    ①统计标题,其格式可以是:

        <param value="1-DESC" value="每月访问者统计">

    1-DESC表示第一组数据的统计标题,"每月访问者统计"是标题内容。

public String readStringData(String s)

{

String tempString = null;

Integer param;

boolean datapresent = true;

int i = 0;

try

{

tempString = getParameter(s + "-DESC");

} catch (Exception e)

{

System.out.println(e);

}

return tempString;

}

    ②统计数据,格式可以是:

        <param value="1-2" value="14">

    1-2表示第一组数据的第二个数据点,14表示该数据点的值。

public Vector readData(String s)

{

Vector tempVector = new Vector();

Float param;

String tempData = null;

boolean datapresent = true;

int i = 0;

while(datapresent)

{

try {

tempData = getParameter(s + "-" + (i+1));

}

catch(Exception e)

{

System.out.println(e);

}

if(tempData == null)

{

datapresent = false;

} else {

param = Float.valueOf(tempData);

tempVector.addElement(param);

i += 1;

}

}

return tempVector;

}

    (5)消息循环

根据Windows编程经验,有用户界面就有消息循环,以组织消息处理函数句柄对用户

界面发出的消息进行响应。遗憾的是,在JDK 1.0中,尚没有类似MFC中的消息映射

表之类的东西组织消息循环。不过,Java发展势头迅猛,不久的将来就会有公司将

它实现!现在,我们需要自己来组织它:Applet类有一个action方法,在其中可以通

过if...then语句来对不同的消息进行响应。

public boolean action(Event e, Object o)

{

Vector actionVector = new Vector();

String actionString = new String();

if(e.target instanceof Checkbox)

{

//"数据1"发出的消息

if(cbg.getCurrent() == cb1)

{

actionVector = readData("1");

actionString = readStringData("1");

System.out.println("1");

}

//数据2发出的消息

if(cbg.getCurrent() == cb2)

{

actionVector = readData("2");

actionString = readStringData("2");

}

//数据3发出的消息

if(cbg.getCurrent() == cb3)

{

actionVector = readData("3");

actionString = readStringData("3");

}

int count = actionVector.size();

c.clearHashMarks();

c.clearScreen();

c.drawAxes(curveWidth, curveHeight);

c.makeHashMarks(count);

c.showData(actionVector);

c.makeTitle(actionString);

repaint();

return true;

}

return false;

}

    3.HTML文档和程序演示

    下面列出了一个HTML实例,用于观察上述编制的小程序,结果参见附图。

<html><body><center>

<applet code="Plot.class" width = 350 height = 200>

<param name="1-DESC" value="每月访问者统计">

<param name="1-1" value="10">

<param name="1-2" value="4">

<param name="1-3" value="17">

<param name="1-4" value="24">

<param name="1-5" value="9">

<param name="1-6" value="7">

<param name="1-7" value="30">

<param name="2-DESC" value="每年访问者统计">

<param name="2-1" value="17">

<param name="2-2" value="13">

<param name="3-DESC" value="一次购买税统计">

<param name="3-1" value="10000">

<param name="3-2" value="130000">

<param name="3-3" value="16100">

<param name="3-4" value="14000">

</applet></center></body>

<address><IMG SRC="\image\Li Zhenwen.jpg">

Li Zhenwen, Zhenwen@Websoft.com

</address></html>


怎样避免NullPointerExceptions（原创）

作者：friendcn

日期：2001-5-30 17:38:21

写JAVA程序，NullPointerExceptions会经常陪伴着你

因为JAVA中到处都是对象，而每一个对象都可能为Null

怎样能减少这种错误呢？

我觉得应该尽量调用那些已经确定被实例话的对象的方法

比如

private String name = "";  // 给它初始化一个值不让它为null

public boolean isNameEqual(String paraName) {

    return name.equals(paraName);

  }

就比下面这种要好

  public boolean isNameEqual(String paraName) {

    return newName.equals(name);  // 这样如果paraName为空的话就会抛出一个 NullPointerException }

当然，如果在程序中name可能被赋值为null的话

那就要这样写了

public boolean isNameEqual(String paraName) {

    if (name != null) {

      return name.equals(paraName);

    }

    else if (paraName != null) {  // name 为 null 而paraName 不是

      return false;

    }

    else {  // 都为空

return true;  // 根据你的需要，返回false也行

    }

  }

至于

public class Animator extends java.applet.Applet implements Runnable{

Vector images;

int imgNumber;

int currentImage=1;

Thread thisThread;

public void init(){

imgNumber=new Integer(getParameter("imgNumber")).intValue();

for (int x=0;x<imgNumber;x++)

{Image img=getImage(getDocumentBase(),"img"+(x+1)+".jpg");

images.addElement(img);

}

}

我觉得可以先给imgNumber=0；

然后还要判断

getParameter("imgNumber")是否为null

这样更好些


这是我刚才做出来的一个查询sqlserver，大家或许会体会一些东西

作者：easyinfonet

email: easyinfonet@sina.com.cn

日期：2000-11-8 16:12:32

<html>

<head>

<title>jdbc test</title>

</head>

<body>

<%@ page contentType="text/html;charset=gb2312"%>

<%@ page import = "java.net.URL" %>

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

<%

    String url   = "jdbc:odbc:admin";

    Class.forName ("sun.jdbc.odbc.JdbcOdbcDriver");                 // 加载 jdbc-odbc 桥驱动程序

    //DriverManager.setLogStream(System.out);

    Connection con = DriverManager.getConnection (url, "sa", "super");   // 试图与某一驱动程序连接。 每个已注册驱动程序都将被加载，直到找到一个可以处理此 URL 的驱动程序

    DatabaseMetaData dma = con.getMetaData ();                      // 获得 DatabaseMetaData 对象并显示一些有关连接的信息

    out.print("\nConnected to " + dma.getURL());

    System.out.println("Driver       " +dma.getDriverName());

    System.out.println("Version      " +dma.getDriverVersion());

    System.out.println("\n");

    Statement stmt = con.createStatement ();                        // 创建一个 Statement 对象，使我们可以向驱动程序提交 SQL 语句

    String query = "SELECT * FROM Information";

    ResultSet rs = stmt.executeQuery (query);                       // 提交查询，创建 ResultSet 对象

    int i;

    ResultSetMetaData rsmd = rs.getMetaData ();                     // 获得 ResultSetMetaData。它将用于列标题

    int numCols = rsmd.getColumnCount ();                           // 获得结果集中的列数

    for (i=1; i<=numCols; i++) {                                    // 显示列标题

        if (i > 1) out.print(",");

        out.print(rsmd.getColumnLabel(i));

    }

    out.println("\n");

    boolean more = rs.next ();                      // 显示数据并获取它，直到结果集末尾

    while (more) {

//        for (i=1; i<=numCols; i++) {                // 在每列中循环获得并显示列数据

//            if (i > 1) out.print(",");

//            out.print(rs.getString(i));

//        }

        //out.println(rs.getString("Comname"));

        System.out.println(rs.getString("Comname"));

        //System.out.print(rs.getString("Comtypename"));

        out.print("\n");

        more = rs.next ();                          // 获取结果集的下一行

    }

    rs.close();                                     // 关闭结果集

    stmt.close();                                   // 关闭语句

    con.close();                                    // 关闭连接

%>

</body>

</html>


真巧, 我刚好做了一个扫描整个网段的多线程程序, 不过利用ping加参数

此电子文档属C#乐园QQ群3256045整理。
真巧, 我刚好做了一个扫描整个网段的多线程程序, 不过利用ping加参数

上一页 目录 下一页

真巧, 我刚好做了一个扫描整个网段的多线程程序, 不过利用ping加参数

作者：steeven

email: phpme@citiz.net

日期：8/15/2001 9:28:39 PM

扫描500个IP10秒钟左右, 一个IP等待0.3秒

说来惭愧, 我觉得是用java写的过程编程, 少贴一点仅供参考:

package steeven;

import java.sql.*;

import java.io.*;

import java.util.*;

import java.text.*;

import javax.servlet.http.*;

public class Ip extends Common implements Runnable{

  public String ip;           // IP, 用户名, 主机名

  ResultSet list;                 // 分页显示的记录集

  public Ip cur;               // 分页显示的当前记录

  static public Hashtable ping = new Hashtable(); //ping 后的结果集

  static int threadCount = 0;  //当前线程的数量, 防止过多线程摧毁电脑

  public Ip() {}

  public Ip(String ip){

    this.ip=ip;

    Thread r = new Thread(this);

    r.start();

  }

  public static void Ping(String ip) throws Exception{

    //最多30个线程

    while(threadCount>30)

      Thread.sleep(50);

    threadCount +=1;

    Ip p = new Ip(ip);

  }

  public void PingAll() throws Exception{

    threadCount =0;

    ping = new Hashtable();

    while(next())  //next()对所有局域网Ip放到cur

      Ping(cur.ip);

    //等着所有Ping结束

    while(threadCount>0)

      Thread.sleep(50);

  }

  public void run(){

    try{

      Process p= Runtime.getRuntime().exec ("ping "+ip+ " -w 300 -n 1");

      InputStreamReader ir = new InputStreamReader(p.getInputStream());

      LineNumberReader input = new LineNumberReader (ir);

      //读取结果行

      for (int i=1 ; i<7; i++)

        input.readLine();

      String line= input.readLine();

      if (line.length()<17 || line.substring(8,17).equals("timed out"))

        ping.put(ip,new Boolean(false));

      else

        ping.put(ip,new Boolean(true));

      //线程结束

      threadCount -= 1;

    }catch (IOException e){}

  }

  public static void main(String[] args) throws Exception{

    Ip ip= new Ip();

    ip.PingAll();

    java.util.Enumeration key = ping.keys();

    String k;

    while((k = (String)key.nextElement()) != null)

      System.out.println(k+": "+ping.get(k));

  }

}

1. 利用Ping比较愚蠢, 但是相对简单些

2. 如果Ping 成功后, 用nbtstat还可以得到主机名, 当前用户名, MAC地址...一切尽在掌握中  :)


真正的apache，tomcat，mod_jk安装指南 原创

作者：heyzz

日期：2000-12-24 11:00:11

网上那帮混蛋整天胡说八道，把你弄得迷迷糊糊，其实这个安装真是很简单，跟我做，几步搞定：

假设：

apache安装目录为APACHE_HOME

tomcat安装目录为TOMCAT_HOME （要设到环境变量中）

jdk安装目录为JAVA_HOME（要设到环境变量中）

1.安装apache：（假定安装目录为APACHE_HOME，根据你实际安装自行替换）

废话不多说，安装后修改APACHE_HOME/CONF/httpd.conf 以下6个地方：

1） Port 8080 或者80 随你

2） ServerName localhost

3)  DocumentRoot "e:/www"  (为了发布方便，不如自己建个目录作为文档根)

4)  把原来的<Directory "APACHE_HOME/htdocs">改为

    <Directory "e:/www">

5)  添加index.jsp为缺省的主索引文件

<IfModule mod_dir.c>

    DirectoryIndex index.html index.jsp

</IfModule>

6） 这是最关键一步，在httpd.conf最后加上

#tomcat支持

include TOMCAT_HOME/conf/mod_jk.conf-auto

只需要这一句就够了，别听那些白痴的话，加上什么tomcat-apache.conf，那没用.

把mod_jk.dll拷贝进APACHE_HOME/module目录

这会先不管apache了，来设置tomcat3.2

步骤：

把tomcat解压缩，将它真正的目录jarakar-tomcat拷贝到你要的目录名TOMCAT_HOME

修改TOMCAT_HOME下的conf目录中的server.xml

1) 如果你不想看TOMCAT启动信息，这么做：

    <Logger name="tc_log"

            path="logs/tomcat.log"

            verbosityLevel = "INFORMATION"

    />

增加一个PATH="logs/tomcat.log" 就可以了

2) 让tomcat改到8081上，一般来说www服务在80或者8080上：

<!-- Normal HTTP -->

<Connector className="org.apache.tomcat.service.PoolTcpConnector">

<Parameter name="handler"

value="org.apache.tomcat.service.http.HttpConnectionHandler"/>

<Parameter name="port" value="8081"/>

</Connector>

3)增加APJ13协议支持

<!-- Apache AJP13 support. This is also used to shut down tomcat.  -->

<Connector className="org.apache.tomcat.service.PoolTcpConnector">

  <Parameter name="handler" value="org.apache.tomcat.service.connector.Ajp13ConnectionHandler"/>

  <Parameter name="port" value="8009"/>

</Connector>

就放在原来的APJ12连接器后面就可以了

4)配合APACHE修改文档根目录

<!-- 文档根目录 -->

<Context path="/" docBase="e:/www" >

</Context>

5)为了用例子测试，还得改一下EXAMPLES的CONTEXT:

<Context path="/examples"

 docBase="TOMCAT_HOME/webapps/examples" crossContext="false" >

</Context>

要使用MOD_JK还得改一下同一目录下的WORKERS.PROPERTIES文件：

1） workers.tomcat_home=TOMCAT_HOME

2)  workers.java_home=JAVA_HOME

3)  ps=\  我爱windows 赫赫

现在ok了，开始启动！

别慌，还有启动顺序问题，注意啊，先启动tomcat让它自己修改一下配置，第一次一定要先tomcat，以后除非修改tomcat的设置，都可以先启动apache了。

ok 来启动tomcat：运行TOMCAT_HOME/bin下面的startup.bat

看到:Startting tomcat. Check logs/tomcat.log for error messages.那就对了

再来检查tomcat的情况

先随便在e:\www里面放个index.jsp文件，内容是

<%="啊哈！jsp第一次运行！"%>

用浏览器打开http://localhost:8081

如果看到上面的话出现，那么根文档就成功了，再来用例子测试一下，用浏览器打开：

http://localhost:8081/examples/jsp

运行猜数字游戏，如果正确了，说明咱们的examples上下文也关联正确。这就简单了。剩下就是运行apache了

运行apache，看到这么一行：

Apache/1.3.14 (win32) with mod_jk running...那就对了

再来测试它们的组合，用浏览器打开http://localhost:8080 (我假设你用8080端口，如果是80端口，那就用http://localhost)

还是看到那句“啊哈！jsp第一次运行！”

这就说明apache和tomcat连上了！

你可以再用http://localhosy:8080/examples/jsp测试一下例子。

就这么简单，真正的mod_jk搞定了！


值得关注：Java 1.4 即将发布版本的性能和新功能一览(转自IBM developerWorks)

作者：sonymusic

email: sonymusic@china.com

日期：7/27/2001 11:25:16 AM

[TABLE][TR][TD]dW at JavaOne: 下一波 -- Java 1.4[/TD][TD][img]http://www.cn.ibm.com/developerWorks/i/c.gif[/img][/TD][TD][urlhref=http://www.cn.ibm.com/developerWorks/java/j-j1merlin/index_eng.shtml][B]英文原文[/B][/url][/TD][TD][img]http://www.cn.ibm.com/developerWorks/i/c.gif[/img][/TD][/TR][TR][TD][img]http://www.cn.ibm.com/developerWorks/i/c.gif[/img][/TD][/TR][TR][TD][img]http://www.cn.ibm.com/developerWorks/i/c.gif[/img][/TD][/TR][/TABLE][TABLE][TR][TD][img]http://www.cn.ibm.com/developerWorks/i/c.gif[/img][/TD][TD][TABLE][TR][TD][img]http://www.cn.ibm.com/developerWorks/i/c.gif[/img][/TD][TD][TABLE][TR][TD][img]http://www.cn.ibm.com/developerWorks/i/c.gif[/img][/TD][/TR][TR][TD][B]内容：[/B][/TD][/TR][TR][TD][img]http://www.cn.ibm.com/developerWorks/i/c.gif[/img][/TD][/TR][TR][TD][TABLE][TR][TD][url href=http://www.cn.ibm.com/developerWorks/java/j-j1merlin/index.shtml#1]JSR 在 1.4 版本中的删除部分[/url][/TD][/TR][TR][TD][url href=http://www.cn.ibm.com/developerWorks/java/j-j1merlin/index.shtml#2]XML API 发展得太快[/url][/TD][/TR][TR][TD][url href=http://www.cn.ibm.com/developerWorks/java/j-j1merlin/index.shtml#3]用户有一个参数选择[/url][/TD][/TR][TR][TD][url href=http://www.cn.ibm.com/developerWorks/java/j-j1merlin/index.shtml#4]断言培训[/url][/TD][/TR][TR][TD][url href=http://www.cn.ibm.com/developerWorks/java/j-j1merlin/index.shtml#5]记录新世界[/url][/TD][/TR][TR][TD][url href=http://www.cn.ibm.com/developerWorks/java/j-j1merlin/index.shtml#6]"N" 代表“新的”[/url][/TD][/TR][TR][TD][url href=http://www.cn.ibm.com/developerWorks/java/j-j1merlin/index.shtml#7]数据库的库到达内核[/url][/TD][/TR][TR][TD][url href=http://www.cn.ibm.com/developerWorks/java/j-j1merlin/index.shtml#8]进入所需的认证路径[/url][/TD][/TR][TR][TD][url href=http://www.cn.ibm.com/developerWorks/java/j-j1merlin/index.shtml#9]利用 WebStart 的快速开始[/url][/TD][/TR][TR][TD][url href=http://www.cn.ibm.com/developerWorks/java/j-j1merlin/index.shtml#10]"Tiger" 的眼睛[/url][/TD][/TR][TR][TD][url href=http://www.cn.ibm.com/developerWorks/java/j-j1merlin/index.shtml#11]超越 JSR[/url][/TD][/TR][TR][TD][url href=http://www.cn.ibm.com/developerWorks/java/j-j1merlin/index.shtml#12]考虑转向 1.4[/url][/TD][/TR][TR][TD][url href=http://www.cn.ibm.com/developerWorks/java/j-j1merlin/index.shtml#resources]参考资料[/url][/TD][/TR][TR][TD][url href=http://www.cn.ibm.com/developerWorks/java/j-j1merlin/index.shtml#author1]关于作者[/url][/TD][/TR][TR][TD][url href=http://www.cn.ibm.com/developerWorks/java/j-j1merlin/index.shtml#rating]对本文的评价[/url][/TD][/TR][/TABLE][/TD][/TR][/TABLE][TABLE][TR][TD][img]http://www.cn.ibm.com/developerWorks/i/c.gif[/img][/TD][/TR][TR][TD][B]相关内容：[/B][/TD][/TR][TR][TD][img]http://www.cn.ibm.com/developerWorks/i/c.gif[/img][/TD][/TR][TR][TD][TABLE][TR][TD][url href=http://www.cn.ibm.com/developerWorks/java/j-secure/index.shtml]一个识别用户的三向解决方案[/url][/TD][/TR][TR][TD][url href=http://www-106.ibm.com/developerWorks/java/library/j-genjava.html]使用类属在编译时找出更多的错误[/url][/TD][/TR][TR][TD][url href=http://www.cn.ibm.com/developerWorks/java/j-merlin/index.shtml]Merlin 的魔力：Swing 的新 Spinner 组件[/url][/TD][/TR][/TABLE][/TD][/TR][/TABLE][TABLE][TR][TD][img]http://www.cn.ibm.com/developerWorks/i/c.gif[/img][/TD][/TR][TR][TD][img]http://www.cn.ibm.com/developerWorks/i/c.gif[/img][/TD][/TR][/TABLE][/TD][/TR][/TABLE]即将发布版本的性能和新功能一览

[url href=http://www.cn.ibm.com/developerWorks/java/j-j1merlin/index.shtml#author1]JohnZukowski[/url] ([email]jaz@zukowski.net[/email])

总裁，JZ Ventures, Inc

2001 年 6 月[BLOCKQUOTE]JavaOne 2001 会议进行了几个议程，来描述新近发布的 Java beta 版本 1.4 的新功能。在这篇文章中，我们集中讨论会议中的某些议程以及新版本的性能。[/BLOCKQUOTE][/i] and not [i][/i].  Use  and not .              use

 and not

   Use all lower case for tags.            -->

Graham Hamilton 是 J2SE Merlin 发行规范制定的带头人，他对最近发布的 Java 1.4 beta 的新性能提供了一个概述。在他的议程和其他议程的详细内容中，开发人员能够充满自信地开始把他们的开发工作移向 Merlin 的世界，预计在年底完成的这个版本的发行工作。

Eenie、meenie、minee、mo: JSR 在 1.4 版本中的删除部分

质量第一，数量第二，1.4 版将为 Java 平台带来一些期待已久的性能。 用于 Merlin 的 Java 规范要求 (JSR-059) 通过 Java Community Process (JCP) 整合了一些其他的 JSR。假如调试稳定的话，这些 JSR 将逐步进入已发布的版本。这些 JSR 包括:

[UL][LI]JSR-005 XML 语法分析 [LI]JSR-010 参数选择 API [LI]JSR-031 XML 数据绑定 [LI]JSR-041 一个简单的断言工具 [LI]JSR-047 记录 API [LI]JSR-051 新的 IO API [LI]JSR-054 JDBC 3.0 [LI]JSR-055 认证路径 API [LI]JSR-056 网络启动 API [LI]JSR-057 JavaBeans 的长期持续性 [LI]JSR-063 XML 处理 [/LI][/UL]

[TABLE][TR][TD]

[B]IBM 参考资料[/B]

为了让您获得更多的会议背景知识，请利用这些丰富的[url href=http://www-106.ibm.com/developerWorks/java/library/j-j1preview/j1resources.html]-- 论文、案例研究、工具和 developerWorks、alphaWorks 及所有 IBM 的教程 -- 按类似会议议程的组分类。

[/TD][/TR][/TABLE]

某些功能尚未发布，要么因为没有准备，要么因为 JCP 社团觉得 JSR 不适合于广泛的 Java 开发社团。没有包含的 JSR 有:

[UL][LI]JSR-014 在 Java 编程语言中增加类属类型 [LI]JSR-031 XML 数据绑定规范 [LI]JSR-051 printf/scanf 性能 [LI]JSR-076 J2SE 的 RMI 安全性 [LI]JSR-078 RMI 自定义远程参考 [/LI][/UL]

XML API 发展得太快

对 Merlin 的 XML 支持实际上正在倒退。因为 XML 技术仍在不断发展中，我们在 1.4 版中采用目前稳定的 JAXP API; 以后的 XML API 会像现在的 JAXP API 一样作为标准扩展提供。

这意味着 Merlin 可以提供 SAX 1.0/2.0、DOM 1.0/2.0 和 XSLT 支持，但不提供 XML 消息传递 (JAXM)、XML 注册表 (JAXR) 或许多会议中提及的其它 JAX 的支持。XMLEncoder/XMLDecoder 可用于长期的 JavaBeans 持续性。

用户有一个参数选择

新的参数选择 API 增加了方便存储和恢复用户配置数据和应用程序配置数据的能力。为了用轻便和线程安全机制替换 Properties 类，Sun 工程师 Josh Bloch 提供了一个新库的预览，它提供了类似于 Windows 注册表的特定执行的后备存储器。

语言级的断言培训

会议上，Bloch 还忙于描述植入 1.4 版的唯一的语言级功能，这样断言工具有了一个新的 assert 关键字。

断言能在代码里增加布尔检查，它一经要求就可以启动来确保程序的正确。比如您有一个 [I]if-else[/I] 程序块，其中整数只能是 "1"、"2" 或 "3"，最后的 [I]else[/I] 程序块可能不检查 "3"。你可以增加一个断言以保证是 "3"，如清单 1 所示。

缺省情况下，断言是被禁用的，但是可以通过专门命令行开关来启动断言，让您确保在用户产生问题时的环境正确。

[B]清单 1. 使用 assert 关键字[/B] [TABLE][TR][TD][PRE]if (foo == 1) {} else if (foo == 2) {} else {   assert (foo == 3);}[/PRE][/TD][/TR][/TABLE]

记录新世界

1.4 版中的新的记录工具由 Graham Hamilton 在星期四上午的单独议程中介绍。工具通过单个系统范围的记录管理器，用自定义的格式器，提供不同记录级别的处理。这个有用的小 API 可存储，比方说，多达 500 条在 foobar.xml 文件中用 XML 格式化的信息（请参阅清单 2）。

[B]清单 2. 记录工具处理自定义记录级别[/B] [TABLE][TR][TD][PRE]Logger logger = Logger.getLogger("com.ibm.utils");logger.log(Level.WARNING, "message", exception);[/PRE][/TD][/TR][/TABLE]

这个新工具确实使得广受好评的开放源代码的 log4j 包的前途未卜。当然，它仍然被版本 1.4 以前的 Java 语言版本使用。

"N" 代表 “新的”

在 Merlin 中，名字最简单的包是新的 I/O 库，它在 java.nio 包中。（是的， [I]n[/I] 代表“新的”。）

Sun 工程师 Mark Reinhold 在 6 月 4 日的议程中描述了新库的功能。您会得到简单的缓冲区，这些缓冲区是为原始类型、字符集编码器和解码器以及类 Perl 的规则表达式和模式匹配工具而设的。但新库最有趣的功能是内存映射文件和非模块化的 I/O。

数据库的库到达内核

当使用 Kestrel 发行版（版本 1.3）时，在标准 Java 平台和几个扩展 API 上有一些库。而 Merlin 中，所有扩展数据库 API 都被认为是内核 API。

最后，与别的比较，CachedRowSet 是标准的。星期三的会议阐述了这些新功能，主持那个会议的人都叫 John -- Sun 公司的 Jonathan Bruce 和 Jon Ellis，以及 Merant 的 John Goodson。

注意：为了支持 JDBC 3.0，在内核库中需要添加一小部分 Java 事务处理 API (JTA)。

进入所需的认证路径

新的认证路径 API 是加在 Merlin 中的一系列安全变化中的一部分。由于新的和更宽松的美国出口限制，以前可选择的和大多数国家没有的安全 API 如今在 Java 平台上已经是成为标准了。

在标准 Merlin 内核中，您会得到 Java 加密扩展 (JCE)、Java 安全套接字扩展 (JSSE) 和 Java 认证和授权服务 (JAAS) API。您还会得到在 Java 普通安全性服务应用程序界面 (GSS-API) 下的 Kerberos V5 支持。

利用 WebStart 的快速开始

Merlin 将网络启动的 Java WebStart 产品添加入 deliverables 的标准集合。虽然您还能标记小应用程序以摆脱 Java 沙盒（如果想在浏览器里保留小应用程序），WebStart 却允许您启动成熟的应用程序，让用户在客户端高速缓存程序，而不必每次运行程序时都下载。

几个议程介绍了产品的最主要部分。Sun 高级工程师 Rene Schmidt 阐述了最技术性的有关产品性能的概述，其它议程将此技术与象 JavaServer Pages 和 Java 插件这样的其它客户端选项进行比较。

"Tiger" 的眼睛

Java 1.4 在 Java 平台上添加了一些新性能。接下来是代号名为 "tiger" 的 Java 1.5 平台。假设版本发布周期为 18 个月的话，我们期待 2003 年的某个时候能见到新版本中出现类似类属的功能。

Gilad Bracha 简介了目前可用的原型执行情况，其本质主要是添加了类型安全集合，并取消了（从集合中）取回元素时需要执行造型的需要。（请参阅[url href=http://www.cn.ibm.com/developerWorks/java/j-j1merlin/index.shtml#resources]developerWorks 上有最近关于类属的文章。）

类属的类型检查被移到了编译时间中，不再实时进行，这真是件好事情。如清单 3 所示，它们让代码变得更加简洁了。

[B]清单 3. 类属代码变简洁了[/B] [TABLE][TR][TD][PRE]public static void main (String args[]) {...   List<String> list = Arrays.asList(args);   String head = list.get(0);...}[/PRE][/TD][/TR][/TABLE]

对 "Tiger" 的另一期待是 scanf/printf 格式化工具，它没有被包含在 Merlin I/O 变化中。

超越 JSR

所有 1.4 库的变化并不都通过单独的 JSR 产生。像 Hamilton 总结的那样，很多新变化只在个别议程中谈到，或在其它议程中偶尔提及。

例如，Java 1.4 将支持 IPv6，但这需要支持它的操作系统（如 Windows XP 或 Solaris 8）。同时，Bloch 主办了一个关于 Collections Framework 的 BOF，这个 Collections Framework 描述了新库的功能：在 Collections 类中的 3 个新的执行方法、1 个标记接口和 6 个新的支持方法。

考虑转向 1.4

如果是一个尖端和前沿的项目，并期待在 6 至 9 个月内发布，请考虑转向 Java 1.4 平台。这个即将到来的版本包含了开发商期盼的几个功能。如想了解如何使用这些新功能的详细信息，请阅读我很快就会在 developerWorks Java 技术专区上的开设的全新 [I]Merlin 的魔力[/I]专栏。

顺便提一下，如果你在寻找会议的幻灯片，Sun 好像正努力要从 250 万没有参加会议的 Java 开发商那儿赚点钱。你可能要等到 2001 年 6 月 17 日去浏览 http://java.sun.com/learning获取信息。

当然你要为每个议程支付约 30 美元，也可以花 1,500 美元获取整个会议的信息。学生享受 40% 的折扣。

参考资料 [UL][LI]欲知更多 Merlin 版本变化的信息，请参阅附带在开发商套件中的[url href=http://java.sun.com/j2se/1.4/docs/relnotes/features.html]

[LI]欲知安全性和 Java 平台的详细信息，请参阅 Joseph Sinclair 的文章：“[url href=http://www.cn.ibm.com/developerWorks/java/j-secure/index.shtml]年 6 月）。

[LI][url href=http://www-106.ibm.com/developerWorks/java/library/jw-log4j/index.html]Log4j[/url]提供快速高效的记录服务。

[LI]欲知更多开放源代码 API 的详细信息，请查看 [url href=http://jakarta.apache.org/log4j/docs/index.html]log4j主页[/url]，它是 Apache Jakarta 项目的一部分。

[LI]在这篇 2001 年 3 月 developerWorks 的文章中，Keith Turner 研究了使用[url href=http://www-106.ibm.com/developerWorks/java/library/j-genjava.html]

[LI]IBM Education 提供您关于 Java 语言尖端实现的[url href=http://www-106.ibm.com/developerWorks/cgi-bin/click.cgi?url=http://www-3.ibm.com/servlet/com.ibm.ls.lsow.servlets.SearchByChapterServlet?CATALOG_ID=6%26CHAPTER_ID=55%26ROW_COLOR1=C8D8F8%26ROW_COLOR2=FFFFFF&origin=j][/LI][/UL]

[TABLE][TR][TD]关于作者

John Zukowski 为 [url href=http://www.jzventures.com/]JZVentures, Inc.[/url] 做战略性的 Java 咨询。他最新的著作是 Apress 的 [url href=http://www.amazon.com/exec/obidos/ASIN/1893115925/johnzukowshomefo/][I]Java Collections[/I][/url] 和 [url href=http://www.amazon.com/exec/obidos/ASIN/189311578X/johnzukowshomefo/][I]Definitive Guide to Swing for Java 2[/I][/url] （第 2 版）。可通过 [email]jaz@zukowski.net[/email] 联系 John。 [/TD][/TR][/TABLE][/TD][/TR][/TABLE]


中间件与jsp

作者：easyinfonet

email: easyinfonet@sina.com.cn

日期：2000-11-14 23:14:08

第五部分  中间件与JSP

5.1  中间件技术

随着企业的IT环境越益复杂，企业中可能会拥有多种操作系统、不同的数据库、异构的网络环境以及若干应用等，那么如何把它们结合成一个有机的协同工作整体，真正实现企业跨平台分布式应用呢？中间件便是解决之道，它用自己的复杂换取了企业应用的简单。那么，复杂的中间件到底包含哪些种类？分别完成什么功能呢？

九十年代初客户机/服务器计算模式开始成为主流技术，将数据统一存储在数据服务器上，而有关的业务逻辑都在客户端实现，即所谓胖终端的解决方案，这种两层结构的模式大大阻碍着系统的发展，单一的服务器结构紧密地依赖供应商；数据存取受到限制；难以扩展到大企业广域网或国际互联网；也难以管理客户端的机群。随着用户业务需求的增长及Internet/Intranet的普及，将以三层或四层体系结构取代。三层结构就是把用户端的业务逻辑独立出来，并与数据库服务器中存储过程合并在一起，构成应用层，以提高计算能力，实现灵活性。在这种结构中用户端仅仅是处理图形用户界面(GUI)，而目前趋势是采用具有交互功能的浏览器，即形成瘦终端的工作方式，为此，中间又增加了一层，称为Web服务器层，形成了四层体系结构。

    这类多层结构的分布系统，各服务器和终端机之间都是通过网络连接起来的，并有大量信息和数据进行传递。对每个应用系统而言，在设计和实现时需要开发的，仅是在应用服务器上的业务逻辑部分的软件，除此之外，还必须要设计处理分布系统所特有的功能的软件，而目前的系统软件(操作系统和支撑软件)都不支持。为此出现了中间件，它是处于系统软件和应用软件之间的一批软件。使设计者集中设计与应用有关的部分，大大简化了设计和维护工作。通过五、六年的大量应用和实践，中间件已有一批成熟的产品，并成为设计分布系统时不可缺少的软件。仅仅几年时间，中间件迅猛发展，每年的市场销售额均是成倍地增长。

5.1.1  什么是中间件

    中间件是处于操作系统和应用程序之间的软件，也有人认为它应该属于操作系统中的一部分。人们在使用中间件时，往往是一组中间件集成在一起，构成一个平台(包括开发平台和运行平台)，但在这组中间件中必需要有一个通信中间件，因此，

    中间件=平台＋通信

    这个定义也限定了只有用于分布式系统中才能称为中间件，同时还可以把它与支撑软件和实用软件区分开来。

5.5.2  中间件的作用和分类

要说清这个问题我们用一个生活中的实例来比喻。把分布式系统看作北京市区的交通系统，网络看作市区马路，通过交通工具(汽车)实现通信，每分钟将有几万辆车在马路上行驶，如果没有相应的交通设施和管理规划，北京市将会乱成一团，发生各种交通事故。

1. 通信处理（消息）中间件 首先要修好马路，安装红绿灯，设立交通管理机构，制定出交通规则，也就是我们要建网和制定出通信协议，能在不同平台之间通信，实现分布式系统中可靠的、高效的、实时的跨平台数据传输(如TongLINK、BEA eLink 、IBM的MQSeries等)，称为消息中间件。这是中间件中唯一不可缺少的，是需求量最大的中间件产品，目前在Windows 2000操作系统中已包含了其部分功能。

2. 事务处理（交易）中间件 在分布式事务处理系统中要处理大量事务，常常在系统中要同时进行上万笔事务。例如在北京市就要设置各种运载汽车，完成日常的运载，同时要随时监视汽车运行，出现故障时，要有排除措施，发生堵塞时要进行调度。在联机事务处理系统(OLTP)中，每笔事务常常要多台服务器上的程序顺序地协调完成，一旦中间发生某种故障时，不但要完成恢复工作，而且要自动切换系统，达到系统永不停机，实现高可靠性运行；同时要使大量事务在多台应用服务器能实时并发运行，并进行负载平衡地调度，实现昂贵的可靠性机和大型计算机系统同等的功能，为了实现这个目标，要求系统具有监视和调度整个系统的功能。BEA的 Tuxedo由此而著名，它成为增长率最高的厂商。一个事务处理平台，根据X/OPEN的参数模型规定，应由事务处理中间件、通信处理中间件以及数据存取管理中间件三部分组成。东方通科技公司的TongLINK 和TongEASY实现了这个参考模型规定。

3. 数据存取管理中间件 在分布式系统中，重要的数据都集中存放在数据服务器中，它们可以是关系型的、复合文档型、具有各种存放格式的多媒体型，或者是经过加密或压缩存放的，该中间件将为在网络上虚拟缓存、格式转换、解压等带来方便。

4. Web服务器中间件 浏览器图形用户界面已成为公认规范，然而它的会话能力差、不能作数据写入、受HTTP协议的限制等，就必需进行修改和扩充，形成了 Web服务器中间件，如 SilverStream公司的产品。

5. 安全中间件 一些军事、政府和商务部门上网的最大障碍是安全保密问题，而且不能使用国外提供的安全措施(如防火墙、加密、认证等)，必需用国产的产品。产生不安全因素是由操作系统引起的，但必需要用中间件去解决，以适应灵活多变的要求。

6. 跨平台和构架的中间件 当前开发大型应用软件通常采用基于构架和构件技术，在分布系统中，还需要集成各节点上的不同系统平台上的构件或新老版本的构件，由此产生了构架中间件，功能最强的是CORBA，可以跨任意平台，但是太庞大；JavaBeans较灵活简单，很适合于做浏览器，但运行效率差;DCOM模型主要适合 Windows平台，已广泛使用。实际上国内新建系统主要是UNIX(包括LINUX)和 Windows，因此针对这两个平台建立相应的中间件要实用得多。

7. 专用平台中间件 为特定应用领域设计参考模式，建立相应构架，配置相应的构件库和中间件，为应用服务器开发和运行特定领域的关键任务(如电子商务、网站等)。

    8. 网络中间件 它包括网管、接入、网络测试、虚拟社区、虚拟缓冲等，也是当前最热门的研发项目。

5.5.3 中间件模型和形态

中间件从诞生起，仅仅是五年时间，但发展极其迅速，但在技术上还处于成长阶段，还没有统一的标准和模型，通常都是用C＋＋语言以面向对象的技术来实现的，但是它的特性已超出面向对象的表达能力，由于它属于可重用构件，目前趋向于用构件技术来实现。然而中间件要涉及软件的所有标准、规范和技术，它含有更多的内涵，因为它包括平台功能，自身具有自治性、自主性、隔离性、社会化、激发性、主动性、并发性、认识能力等特性，是近似于Agent(代理)的结构，采用Agent的概念和技术更合适一些，建立模型和规范是促进技术发展的核心措施。


转：《学不会的JAVA，消不了的忧愁》之二——献给那些所有酷爱计算机编程的人们

作者：NOVEMBER RAIN

email: haoy@dongyu.com.cn

日期：2001-5-9 17:18:18

接着昨天的内容写吧，各位网友请先看昨天（03/11)的内容。

对于一个编程员来说，不懂VB、VC、DELPHI，在当今VB、VC横行霸道的HUGE CHINA，显然是很可怜的。我从Dbase3到FoxBase,再从FoxPro到现在的Visual FoxPro6.0,用这个桌面数据库管理系统不断地为公司或工厂开发一些数据库应用系统。在计算机水平还很低下的中国，用这样的简单系统开发出来的东西还是能够满足那些不太懂计算机的企业高层要求的，所以一直以来，快6年啦，我的生活过得还是挺滋润的，可爱的狐狸(Fox)帮我挣了大约10万元吧，最为重要的是，我心爱的Girl Friend(女友)也是她做的媒。

按理说，既然我对狐狸这么了理，狐狸也对我做出不少贡献，我应该好好继续和它发展下去才对，可君是否知道，狐狸的能力有限，在网络如火如荼的当今网络计算的时代，VFP前途黯然。很长的时间，我都在用着它，编程的思想也被它定型了，对“可视化编程”我能够深入了解，然而面向对象（OOP)、网络编程的概念一直没有深深感受过，我不是在说VFP没有这方面的概念和没有这方面的能力，只是用它去完成网络计算的工作，就好像用一台486的电脑安装win98一样。

搞了这么久的VFP，发现自己越来越木了，（vfp会让人学笨的），除了它什么也不会，在一个小企业里，它能完成所有办公需要（在后面我会结合我开发的应用软件举一个详细的例子来解释），大企业里呢，很多人还不屑于和一只狐狸打交道呢！人不能总吊在一颗树上，万一树倒了，就惨了，因此，我准备学另外一种编程语言，也准备学学网络，这是98年的想法。

受97年电脑报上发表的一篇文章影响，那篇文章刊登了比尔.盖次对java的看法，说java是一种最优秀的网络编程语言。那时对网络编程还不很了解，但知道网络是怎么回事，考虑到今后的发展，也明白网络是今后的发展趋势，我选择了自学java,第一种网络编程的语言，从此开始了学习java的历史，我想，在我们国家，一定有很多人像我一样，没有很好的计算机基础知识，也没有很好的编程基本功，却走上了编程的道路，我的背景和我的学历我在前面已讲得很清楚，这里再总结一下，学历：97年中专毕业；懂的计算机操作：vfp(一种可视化数据库开发语言）、win98、office97、WindowsNT4.0（不精）。这就是我当时（98年）的知识水平，在这么低的水平下，我是怎么自学java的呢？其间有多少辛酸的故事，我会把它详细写下来，让大家一起来分享。我的愿望只有一个，让别人也让自己一起来把java学好。限于一张贴子的篇幅，我这次不写太多，请看我下张贴子《学不会的JAVA，消不了的忧愁》之三——献给那些所有酷爱计算机编程的人们，

如果大家对我有什么看法，也可以写信给我：jigenghua@163.net


转：《学不会的JAVA，消不了的忧愁》之三

作者：NOVEMBER RAIN

email: haoy@dongyu.com.cn

日期：2001-5-9 17:32:14

《学不会的JAVA，消不了的忧愁》之三——献给那些所有酷爱计算机编程的人们

发言者：jigenghua

时 间：2001/03/12 15:42:34 来 自：http://www.javaunion.org

--------------------------------------------------------------------------------

各位网友，这已是“学不会的JAVA，消不了的忧愁”连载之三了，如果你没有看过前面两贴的话，还请先读读吧，虽然每幅贴子之间关系不是那么密切，但当你看完了所有贴子之后，我相信你会有所收获的。

或许是JAVA流入中国的历史还很短吧，在网上，关于JAVA的网站很多，可就是没有VC、VB多。现在（包括前几年），大家都一股脑儿去搞VC、VB去了，在这两方面China Man也研究出了一些名堂，出了一些很好的作品，据说那只伟大的蚂蚁（网络蚂蚁NetAnt——一种在网络上COPY文件的工具软件），还有那个好聊天的网络BiBi机——OICQ，都是用VC开发出来的。有一位同事，在深圳网天技术服务公司（据说现在被合并了）做过网站的，他略带夸张地说：“我们老板（原中华网的一位技术总监）说过，只要精通VC，一个礼拜，就可以把OICQ所有代码搞熟”，多么诱人的能力，由此可见，在我们国家，懂VC的人的技术不会比美国佬差。可对JAVA的研究呢，我至今还没有用过或听说过国人用它开发出来的很好作品，或许是我孤陋寡闻吧，我想，也不会啊，我天天在Internet里面游玩，尤其对有关java的技术讨论，鼻子一向是挺管用的。如果是一种没有什么前途的东西，比美国佬落后并不代表着什么，可对一种大家都想象得到的、有前途的、生命力旺盛的东西，比鬼佬差那么一大截就问题大大的了。一位网友在网上发过一篇文章《中国人不谈JAVA》，说美国的VC、VB时代已过去了，大家都在发疯似的搞java,而我们国家呢，哈哈，不想说了。

就是因为这样的原因，很难找到一本java技术学习的好书。在深圳，在现在，所有的书城里面的java图书不是没有，而是多得不得了，可你随手抽出一本，厚一点的，在封面上都会有一句八九不离十的话——美：ABC,XYZ著；张三、李四译。不知大家有没有看过有关计算机图书方面的译本，我看过好几本，对译本的印象实在是不佳，一句很长很长的话，用心去读、咬文嚼字去读，可还是不明其意何在，用中国人的一把锋利宝剑（缩句），把那些多余的枝条统统捎掉，结果发现：“对象就是对象，方法就是方法”之类的下定义，众所周知，在我们国家，这样的下定义是“违法”的，是要被老师拷叉的。对于一般的概念，只要能明了其含义即可，但对那些关键性的概念，像对象、面向对象，类、抽象类，方法、类方法、实例方法，等等等等，太多太多，是一点都不能含糊的，也不能似懂非懂的。有时候，遇到一些很重要的术语和程序，因为书上说得模里模糊，越看越不懂，真是气得吐血，恨不得把那个翻译的狗官丢进牢房——误人青春、误人子弟、罪该把牢底坐串！！（真心希望各位译大侠在把别人的作品译完之后自己能读上一两篇。）

在网上，很多爪哇技术网站，包括我现在所在的这个，里面的内容也逃脱不了翻译的痕迹——一句话：不好懂！

记得我第一本爪哇教科书是在江西吉安新华书店二楼花了39元买的，书名叫《21天学通Visual J++》，当然也是本译本，我很想能快点掌握java，所以选择了《21天...》，也希望能在21天学会它，就算我笨，我呆，21天学不会，那么42天呢，63天又怎样？

书的前面几页译得不错，打算从第一面看到最后一面，奇怪的是，第一章就把一幅Visual J++的编程窗口的插图搞错了，害得我看了老半天，看到第二章才发现正确的插图。

这样下去看了几章，越看到后面，就越看不懂，我深信自己的自学能力，也深信自己国语水平，我不怀凝自己，却开始怀凝这本书是不是值得看下去了。

于是，我开始四处查找资料，好在我读了四年中专，对英语还是有那么一点功底的，资料上的、书上的一些重要概念，实在看不懂，我就打开全是英文的JAVA说明书，借助于金山词霸的帮忙，尽量把它译成自己能明了的一句话。这样搞了一段时间，自己的计算机英语水平反而有一点点进步，现在没有“词爸”现场指挥，我也能看懂不少文章，但看得还是很累。

在断断续续学了两年JAVA后，才发现自己这辈子是不可能把爪哇学精学全的了，在以后的贴子里，我会把我已经懂得差不多的一些概念用纯中国话、最好懂的中国话告诉大家，不分什么章节，也不讲什么JAVA的发展史（讲了有个鸟用），想说什么就说什么，请大家对我的错误之处严加批评。

最后我会把我用VFP（一种很简单的数据库管理系统）帮一家外资手袋厂开发的计算机管理系统的功能实现告诉大家，为什么我会这么做？原因只有一个，我很想用java来重新实现这套系统。我把它写下来，算是我向大家提出的一个课题吧。在我开发的这套系统里面，所有的“JAVA 2代”新技术都有用武之地，你想真正地、务实地掌握JAVA的话，你有这个能力的话，大家一起来实现它。不要再向别人炫耀啊啊，我的Applet水平有多高；啊啊，我用JSP用了8个月；啊啊，我精通JDBC、Servlet、EJB,还有什么JINI、JNDS...只要你能用这些新技术实现我那套系统，那么，叫我以身相许我也——愿意！我崇拜真正有能力的人！（不过呢，哈哈，本人非靓女，实乃靓仔，江西的男孩不靓才怪！）

以上文稿没有仔细校过，可能会有错别字，莫见怪。限于贴子篇幅，就此搁笔，请看下贴《学不会的JAVA，消不了的忧愁》之四——献给那些所有酷爱计算机编程的人们

【帖子管理】 用BBS风格观看 << 返回 返回论坛首页

--------------------------------------------------------------------------------

本帖版权归原作者，其它网站转载须注明出处，传统媒体转载须事先与原作者和中国Java阵线联盟论坛联系。


转：《学不会的JAVA，消不了的忧愁》之四

作者：NOVEMBER RAIN

email: haoy@dongyu.com.cn

日期：2001-5-9 17:34:09

《学不会的JAVA，消不了的忧愁》之四——献给那些酷爱计算机编程的人们

发言者：jigenghua

时 间：2001/03/12 23:01:47 来 自：http://www.javaunion.org

--------------------------------------------------------------------------------

这一回，我不想谈那么多的感概了，我用自己的话来说说java，请大家来做几道判断题，认为对的，你可以不开声，认为错的，可不要忘了指出来。所有这样问题，面向像我一样的菜鸟，大虾也可以看看。

学习一和编程语言，不外乎两种背景：其一，兴趣爱好；其二，工作需要。在学校里，可能还有第三种背景——那就是必修课程。

在我看来，凭兴趣爱好学的东西学得很快，印象也很深，但没有很好地结合实践应用，自己也想不出什么真正有价值的课题去实现它，所以这种知识水平并不是很高。我见过很多这样的朋友，他们对VFP、VC的道理懂得真不少，可一问到一些真正有用的东西，他们回答得总是那么不尽人意。我曾经深爱着C，也会用它编一些简单的小程序，不过，从来就没有用它做过什么工程，所以，在外面应聘的时候，我不敢说我精通C，虽然知道自己的C水平很不错了。不过呢，话又说回来，一旦有了理论水平和基本功，真正遇到难题的时候，往往都能很快解决，所以凭兴趣爱好掌握的东西用途也是挺大的。

相反，因为工作的需要，自己不得不去掌握的东西一旦自己搞懂了，印象会更深刻。由于所有的“知识”都是建立在以“用”的基础上，掌握到了的东西就是活生生用得到的、看得到的东西，所以谈不上什么学不致用的问题。这种知识处处以实践为基础，一分钱一分货，最能解决实际问题，这也就是用工单位为什么那么注重实际工作经验的真正原因：招到有经验的老手，新工作上手很快，工作质量也很高！（我没有什么学历，中专生一个，但我早在95年的时候就开始用Fox真枪实刀地干，半工半读，为工商所、商场、酒店、制造厂、甚至我的校图书馆免费开发了一个又一个的数据库应用软件，这些软件，在我们江西内地，大多数至今还活得挺好的。这么多的软件，为我98年四月份在深圳能找到一份别人看来复杂、而我看来一般般的数据库网络编程工作打下了了不起的基础。来到这里不到半年，一家很大的电子厂，不知是通过什么渠道，连打三次电话给我，叫我过去帮他们搞编程，我想学java,不想再搞VFP了，所以没有答应，虽然对方开的报酬比我现在要高一个档次。）我的经历告诉我，学历并不是很重要的，关键是真正的水平，你能带着你亲手制作出来的CPU芯片到联想去应聘，哪怕你从来没有进过校门，柳传志也会叫你一声干爹。明白了能力的“妹”力以后，我不再刻意追求什么本科学历、研究生学历，我只想好好学点有用的东西，用能力向别人展现自己的风采！（有点吃不到葡萄就不吃葡萄的心态）

至于第三种学习知识的方法——为了修完课程而学，通过这种方式来掌握知识，基本上没有什么好谈的了，一句话，没有什么用啦！或许我讲的太武断太偏激。

我学JAVA，既不完全是凭兴趣爱好，也不是因为工作压得非学不可，只是基于学JAVA的一种潮流，和不久的将来的工作需要，错过了学VC、VB的疯狂时代，也错过了学CGI、ASP的关键岁月，我不能再错过JAVA，我输不起！

正是因为以上的原因，我把JAVA建立在将来的应用上，所以学得不是那么的累，人没有压力学东西往往很慢。一个面向对象的概念，看了一遍又一遍，不但弄不明“面向对象”是何物，连对象是什么东东，都似懂非懂，还以为面向对象是把脸面向着对象（Face to object)，就算这样理解，面向着对象干什么？发神经啊？很多很多的书，标题写得不错——“什么叫面向对象”，抑或“面向对象的概念”，可看完之后，还是一头雾水。

最令人恼火的是很多书对类（Class)的定义：具有相同特性的多个对象的一个实体。去他奶奶的，老子连对象还不太懂，就用对象来说明类的定义，还加上一个怪异的词语——实体。这样的定义能说明什么问题？也许是写书的人太聪明了，以为大家的头脑都是抽象的，不会想木头、石头、狼和兔子之类的具体东西。

他完全可以想到这个词：模板，用模板代替实体，就成了类是具有相同特性的多个对象的一个模板，这样呆板的解释，当然不能让初学者对类这个重中之重的概念留下难于忘怀的印象。假如他能像我这样解释，请看下文：

    传说中上帝造人是用模子来造的，上帝造人用的模子就是类（Class)，当然模子也不是天生的，由上帝根据自己的想象制作，作好了模子以后，造人可就方便啦，几乎不用动脑筋，泥巴往模子里一丢，一个人就出来了，出来了的这个人就有了眼情啦、鼻子啦、耳朵啦，等等，最重要的是，这个人会说话，会唱歌、跳舞......

    以上这个例子中，类就是模子，专门用来制作一些有很多共同点的实物，用模子造东西的最大好处就是可以偷懒，一劳永逸，用类来开发软件的妙处也正在于此。模子造出来（对“造出来”这三个字打上着重号，引起读者的注意）的东西有一个响亮的名字：对象或实例，因此，在java里面，我们讲的对象或实例，都是指已经用类制作好了的。用类制作对象这个动作，也有一个各位不太好懂的名字——类的实例化，或叫类的一个实现，好听吗？你们可要记在心里。

    不知大家是否注意到，我上面说过，上帝用类造出人这种对象具有很多很多的特性，像眼情有大、眉毛有多长、牙齿有几颗、人长得是否靓仔等等叫做对象的属性（Attribute)，一个对象视它简单与复杂，属性可以没有一个，也可以有多个。而人会说话、会打架、会谈恋爱……这些涉及到动作的行为叫做对象的能力（Ability)，但在JAVA里面叫做对象的方法（Method)，假如你的思想被VC化，你把它暂时理解为函数也不是不可以的。

       通常我们讲的“面向对象编程”这种方法，或说这种思想，就是要求大家学习上帝造人这种方式、这种思想，把所有程序分成一个一个的功能块，有些功能块不仅你正在编写的这个程序用得到，将来编写的其它程序也可能用得到，正是你会这么想，把一些通用的功能块抽出来、概括出来，用一个独立的、能被其它类制造时调用的名字命名它。用这种思想去设计程序，叫什么面向对象编程（OOP)。

这一贴，我讲了学知识的三种背景，不同的背景学知识会有不同的方法，从面产生不同的效果。

我也介绍了类、对象、面向对象编程、对象的属性、对象的方法这些基本的概念，大家懂了吗？

下一贴，我将接着介绍类库的概念，还有父类、子类、类与类之间的遗传（继承），并教大家怎样快速用JDK这个工具

开发小程序。虽然有很多的可视化开发工具可以用来开发JAVA软件，但不懂使用JDK工具的JAVA开发人员，跟只懂Linux的X Windows不懂它的shell操作一样，属于低能的操作员。

所有这些贴子，都是出自于个人爱好，连载出来，目的只有一个：为了别人，也为了自己把JAVA学好！

请看下贴《学不会的JAVA，消不了的忧愁》之五——献给那些酷爱计算机编程的人们。


转：第一部分：EJB 体系结构的历史和目标

作者：老好人

日期：2000-11-30 18:24:24

什么是 Enterprise JavaBeans 组件？

第一部分：EJB 体系结构的历史和目标

本文概述 Enterprise JavaBeans (EJB) 技术，旨在让读者快速理解基本概念。第 1 部分讲述 EJB 技术的历史和某些目标、优点和技术。为了简洁明了，有选择地讲述 EJB 技术的一些关键要素。请注意，虽然 EJB 组件依赖于一些基础的 Java 服务（如 Java Transaction Service），但使用 EJB 组件及认识这些组件的好处并不需要掌握这些相关技术的知识。

Enterprise JavaBeans 技术自 1998 年 3 月问世以来很受好评。下面这段话就是一个例子：

“自从两年多以前问世以来，Enterprise JavaBeanstm 技术在平台供应商和企业的开发小组中，同样都保持着空前的发展势头。这是因为 EJBtm 的服务器端组件模型简化了中间件组件的开发，这些中间组件都是事务性的、可伸缩的和可移植的。Enterprise JavaBeans 服务器通过为中间件服务（如事务处理、安全性、数据库连接及其他）提供自动支持，降低了开发中间件的复杂程度。”（Sun Microsystems 网站）

Enterprise JavaBeans 这一名称利用了 Java bean — 这种可移植、可重用的 Java 软件组件的声望。Enterprise JavaBeans 技术把 Java 组件的概念从客户机域扩展到了服务器域：这是 Java 技术成长过程中有重大意义的一步，它使 Java 技术发展成为一种强健的、可伸缩的环境，能够支持以任务为关键的企业信息系统。

服务器上的 Java 应用程序

Java 编程语言最初在 Web 开发人员中获得好评的一个原因是，它支持称为 applet 的可下载 Java 程序。对 Applet 的支持以 Applet 类的形式内置到了 1.0 版的 Java Development Kit (JDK) 中。按照 1.0 版的时间框架，Java 开发是以 applet 和应用程序作为中心的。基于 JDK 1.0 版的 Java 读物都是从 applet 和应用程序的角度来描述 Java 编程的：

“Java 程序由更多的类定义中的某一个组成，每个类定义均已编译成它自已的 Java 虚拟机对象代码的 .class 文件。这些类之一必须定义一个叫做 main() 的方法，程序就是从这个方法开始运行的。想调用一个 Java 程序，需要运行 Java 解释器 java，并指定包含 main() 方法的类的名称。请注意 Java applet 并不是一个应用程序 — 它是一个由已在运行的 Java 应用程序（如 Web 浏览器或 applet 查看器）装入并运行的 Java 类。”（见 Flanagan 所著的 Java in a Nutshell）

Java 应用程序可以在服务器上运行，但是不管是在客户机-服务器环境下，还是在基于 Web 的环境下，JDK 中都没有提供让 Java 应用程序专用于服务器机器的接口或包。认识到 Java 在 Web 环境下作为一种服务器语言的潜力，Sun Microsystems 编写了 Java Servlet 规范。servlet 在许多方面与 applet 相似，它是专门为在 Web 服务器机器上运行而设计的 Java 程序：

“servlet 是由容器管理的 Web 组件，可产生动态内容。servlet 是一种小型的、与平台无关的 Java 类，被编译成体系结构中立的字节代码，这种代码可以动态地加载到一个 web 服务器上，并由此 web 服务器运行。servlet 通过一种由 servlet 容器实现的请求-响应模型与 Web 客户机进行交互。这种请求-响应模型建立在超文本传输协议 (HTTP) 行为的基础之上。”（见 JavaSoft 的“Java Servlet API Specification”）

在一台 Web 服务器控制下，在多台服务器上运行若干小型用户程序，这种想法并不新鲜 — 一段时间以来，公共网关接口 (CGI) 程序（常被称为 CGI 脚本）一直起着这种作用，并推动了 Web 的普及。但 Java servlet 可以以更高的效率和可移植性来实现这一目的，因而可望最终会取代 CGI 程序。为 servlet 提供运行时环境的软件（通常被称为 servlet 引擎）可以添加到现有的、本身并不支持 Java 可执行程序的 Web 服务器上。

Java servlet 的出现，为应用程序员使用 Java 来创建 Web 应用程序开辟了新的途径。但是，仅有 servlet 还不能为真正的企业计算提供完整的模型。CGI 应用程序本身往往不是完整的应用程序，在处理接收自 Web 浏览器上用户的信息请求时，CGI 只是整个处理过程中的一个中间步骤。例如，CGI 应用程序的一种常见用途是访问数据库。将它用于这种任务时，CGI 程序提供一种方法，将用户的数据请求连接到能满足这种请求的企业数据库。CGI 程序常常充当一种中间软件，从 Web 浏览器接收请求，决定必须调用哪些计算资源来满足这些请求，并向浏览器发回响应。Java servlet 与 CGI 程序一样，最适合充当连接前端 Web 请求与后端数据资源的中间层组件。

三层体系结构

Web 编程向服务器端 Java 应用程序的演化，也带来了体系结构的演化，使它脱离了常规的客户机-服务器两层模型，而向一种三层方法发展。两层模型当时曾经具有创新意义，因为它将一些计算任务从主处理器上卸载到灵巧的客户机。常规的基于 LAN 的数据库应用程序就是一个例子，其中数据库管理器服务器软件驻留在一个专用的服务器机器上，而用户则通过他们的工作站上的客户机代码来访问数据库。随着客户机-服务器模型成长到能付诸使用，就出现了对服务器可伸缩性和对客户机代码大小和复杂性的关注。于是提出了一种三层的体系结构，以避免在两层模型中已察觉到的弱点，使 Web 能成为一个计算平台：

“许多人...断言，传统的客户机/服务器两层体系结构不会有好的可伸缩性，因为用户连接和数据访问的数量无法预测，而且在一些系统管理上也存在问题。为处理两层体系结构的限制，许多开发集体都在转向三层体系结构。这种体系结构大致可以定义为：客户机层上的表示层、中间的服务器和后端的某种数据库。这种设想的目的就是缓和客户机或数据库服务器上的代码膨胀，集中管理业务逻辑，更灵活地使用数据库，而不仅是使用所存储的过程和触发器。”（见 Kim 的“Looking for a 3-Tier App Builder?”）

一个三层结构模型通常被想像成有一个 Web 浏览器作为客户层。Web 浏览器由于有可能成为一种真正的通用客户机，使它从观念上取代了两层结构的“胖客户机”。如果浏览器作为 Web 应用程序体系结构的标准瘦客户机获得认可，那么以前驻留在两层模型的胖客户机中的功能会怎么样呢？现在，应用程序专用的功能并不移植回服务器（例如数据库管理器），而是有意将它驻留在一个新的中间层上。中间层支持应用程序服务器软件，这种软件是中间件的一种形式，它处于第一层上瘦客户机的最小功能和第三层上服务器端业务系统的丰富功能之间。由于三层体系结构与 Web 处理模型有密切关系，所以中间层应用程序服务器常被视为 Web 服务器的一种功能扩展。现有的 Web 应用程序利用 CGI 程序，将来自 Web 浏览器的用户请求传送到不基于 Web 的业务系统，并向浏览器返回响应，就是三层模型的一种实现。这些应用程序逐渐向 servlet 技术的转移说明三层模型正在增强。

JavaBeans 组件

JavaBeans 规范将“组件软件”的概念引入到 Java 编程的领域。组件是自含的、可重用的软件单元；而 JavaBeans 组件，则可以使用可视的应用程序开发工具，可视地将它们编写到 Java 程序中。JavaBeans 规范为 Java 开发人员提供了一种“组件化”其 Java 类的方法：

Bean 是一些 Java 类，可在一个可视的构建器工具中操作它们，并且可以将它们一起编写到应用程序中。任何具有某种特性和事件接口约定的 Java 类都可以是一个 Bean。（见 JavaSoft，“Using the Beans Development Kit 1.0”）

如果软件重用是一个好主意，那么是否应该让每一个 Java 类都成为 Java bean 呢？如果 Java 类满足某些准则，它们就适于充当 bean 的角色：

在开发任何新软件之前，都值得考虑是否用 JavaBean 的形式来开发它。如果软件模块要既能够可视地操作，又能够定制以达到某些效果，则这种软件模块就可能适于做成一个 JavaBean。为帮助您确定要开发的软件是否应该是一个 JavaBean，假定它应该是用 Java 编写的，请向您自已提出以下问题，并相应地作出决定：

是否打算让它可重用？或者，它会是可重用的吗？

是否希望将它与其他可重用的 Java 组件一起使用？

是否预计会在 IDE 工具中使用它？

如果上述问题的答案都是肯定的，则它应该作为 JavaBean 来开发。（见 developerWorks 的“JavaBeans Guidelines”）

JavaBean 概念是为了在 Java 编程环境中支持可重用的组件，它是一种一般性的设计方法，适用于客户机或服务器机器上运行的 Java 程序。由于对可视的构建器工具的强调，也由于许多 Java bean 都是图形用户界面 (GUI) 组件，所以 JavaBean 组件可能被视为一种客户端技术。但是，并不要求 Java bean 都是可视的，并且它们也可以用于服务器环境中。

编码为 Java bean 的 Java 类通常具有以下特征：

使用设计模式。这些模式就是方法和接口的编码约定。

支持可视的软件开发工具。类必须将变量（称为属性）、方法和事件展示出来。

可以定制。定制包括能支持缺省的属性编辑器，或者提供单一的定制规则。定制使开发人员得以在不更改源代码的情况下更改 bean 的行为。

支持自省 (introspection)。这指的是将属性、方法和事件公开给其他类，可以通过设计模式或通过创建 BeanInfo 类来完成这种自省。

是持久的。这就允许在一个可视构建器中定制一个 bean，然后以其定制后的状态加以保存。

Java 2 Platform, Enterprise Edition

Sun Microsystems 发起了一项称为 Java 2 Platform, Enterprise Edition (J2EE) 的技术创新，旨在将 Java 平台的范围扩展到大规模服务器环境：

“1997 年 4 月 12 日，Sun 宣布了一项为企业环境开发 Java 平台的创新成果。使用开放式的 Java Community Process，Sun 促进了一组标准的 Java 扩展的开发，称为 Enterprise Java API。这些应用程序编程接口 (API) 为各种各样的中间件的实现提供了不依赖供应商的编程接口。Enterprise Java API 的要点是 Enterprise JavaBeans API，后者为 Java 应用程序服务器定义了一个服务器端组件模型，以及一个不依赖供应商的编程接口。”（见 Thomas 的“Java 2 Platform, Enterprise Edition: Ensuring Consistency, Portability, and Interoperability”）

J2EE 为 Enterprise JavaBeans 技术提供了工作环境。事实上，Sun 把若干项软件技术都设想为这样的构件块，它们将使大型企业能够把以任务为关键的业务系统移植到 Java 环境中，而 Enterprise JavaBeans 技术不过是这些技术之一。EJB 组件是按它们自己的规范定义的，但 EJB 技术并不是一项独立的技术。它建立在其他 Java 技术之上，这些技术由 Sun 和其他 IT 公司联合规定，它们一起提供了这个框架的内容，该框架就称为 Java 2 Platform, Enterprise Edition。

J2EE 中包括以下技术：

Enterprise JavaBeans (EJB) 技术

Java Interface Definition Language (IDL)

Java Message Service (JMS) API

Java Naming and Directory Interface (JNDI)

Java Remote Method Invocation (RMI) 和 Object Serialization

Java Servlet API

Java Transaction API (JTA)

Java Transaction Service (JTS)

JavaServer Pages (JSP) 技术

JDBC 数据库访问 API

参与到这个企业 Java 框架中，并不意味着每项技术都依赖于所有其他技术。单独的规范文档指出每项技术的相关性。例如，Enterprise JavaBeans 规范 1.0 发行版就指明了在定位各个组件时与 JNDI 的相关性，以及在编程中启动和停止事务处理时与 JTA 的相关性。

EJB 技术的设计目标

EJB 规范的第一版以初稿形式于 1997 年 12 月公布，并于 1998 年 3 月作为 1.0 版发行。规范作者为 EJB 体系结构制定了以下目标：

Enterprise JavaBeans 体系结构将是标准的组件体系结构，用于以 Java 编程语言构建分布式的面向对象的商务应用程序。通过把使用不同供应商提供的工具开发出来的组件组合在一起，Enterprise JavaBeans 体系结构将有可能构建分布式的应用程序。

Enterprise JavaBeans 体系结构将使编写应用程序变得容易：应用程序开发人员将不必了解低层次的事务和状态管理的细节、多线程、资源共享和其他复杂的低级 API。但是，将允许专家级的程序员直接访问低级 API。

Enterprise JavaBeans 应用程序将遵循 Java 编程语言的“一次编写，随处运行”的原则。EJB 组件可以只开发一次，然后在多个平台上部署，而不需要重新编译或修改源代码。

Enterprise JavaBeans 体系结构将处理企业应用程序生命周期中的开发、部署和运行等方面。

Enterprise JavaBeans 体系结构将定义一些约定，这些约定使多个供应商提供的工具能够开发并部署可在运行时互操作的组件。

Enterprise JavaBeans 体系结构将与现有的服务器平台兼容。供应商将能够扩展它们的现有产品，以支持 Enterprise JavaBeans 组件。

Enterprise JavaBeans 体系结构将与 Java 编程语言编写的其他 API 兼容。

Enterprise JavaBeans 体系结构将提供 EJB 组件和非 Java 编程语言应用程序之间的互操作性。

Enterprise JavaBeans 体系结构将与 CORBA 兼容。

使用 EJB 技术的好处

这些设计目标会使企业和开发人员得到什么好处呢？下面列出了可望从采用 Enterprise JavaBeans 环境获得的好处：

EJB 组件使编写应用程序更为简单。尽管 EJB 体系结构复杂，但应用程序开发人员一般都不必再编写用于访问系统服务的代码。一种称为 EJB 容器的系统组件使系统服务可用于 EJB 组件的任务。

服务器端商务逻辑可以移植。除了 Java 语言固有的可移植性外，EJB 体系结构还在 bean 和支持该 bean 的容器之间提供了一套标准化的应用程序编程接口。这使开发人员能够将 bean 从一种操作环境移植到另一种操作环境，而无须重新编写其源代码。

可以从现有的软件组件装配出服务器端应用程序，这与从现有的 Java bean 可以装配出客户端应用程序一样，从而使软件能够重用。

EJB 体系结构内置了对典型企业级系统服务的支持，包括分布式对象、事务处理、数据库、安全和全局命名。

多家 IT 供应商都采纳 EJB 体系结构，这是由于有这样的承诺：客户将能够从选定的供应商那里选购软件组件，如 EJB 组件、容器及 EJB 服务器；也由于承诺了不同供应商的产品，只要符合 EJB 体系结构，就都是可互操作的。

用 EJB 组件构建的应用程序可以从一个服务器移植到另一个服务器，从而支持可伸缩性，这是因为在 EJB 模型中，各个软件组件都是严格分离的。

EJB 体系结构能保障原有的 IT 投资，这是通过允许将现有的信息系统和资产“包裹”在这些应用程序中，而不要求客户更换现有技术。事实上，在关系数据库中存储数据的企业已经有了一套已有雏形的实体 bean，正等着通过 EJB 外壳去访问。

进一步考察 JNDI

Enterprise JavaBeans 组件使用 Java Naming and Directory Interface (JNDI) 来访问各种目录服务。JNDI 分两部分：应用程序编程接口 (API) 和服务供应商接口 (SPI)：

“JNDI 体系结构由 JNDI API 和 JNDI SPI 组成。JNDI API 允许 Java 应用程序访问各种命名和目录服务。JNDI SPI 则是设计来供任意一种服务的供应商（也包括目录服务供应商）使用。这使得各种各样的目录服务和命名服务能够透明地插入到使用 JNDI API 的 Java 应用程序中。（见 JavaSoft，“JNDI: Java Naming and Directory Interface”）

JNDI API 并不同某种专用的命名技术或目录技术连在一起，也不同任何供应商的目录服务连在一起，因此它对 EJB 组件的可移植性有所贡献。例如，客户可以从多种不同的技术中选择，来为其 EJB 应用程序提供目录服务，这些技术包括：

LDAP：Sun 的 LDAP 服务供应商支持 LDAP 协议的第 2 版和第 3 版。

NIS：Sun 提供一个 NIS 服务供应商（NIS 即网络信息服务，以前称为黄页）。

COS 命名：Sun 的 COS 命名服务供应商提供对 CORBA 命名服务的访问。

文件系统：Sun 提供一个服务供应商来访问文件系统。

RMI 注册：Sun 为 RMI 注册提供一个服务供应商。

Novell：有几个服务供应商可提供对目录服务 NDS 的访问以及 NetWare 3X 连接库、Novell 文件系统和其他 Novell 服务（如扩展 NCP）的访问。

虽然 JNDI 规范对供应商是中立的，但不应认为，实现 JNDI 接口的应用程序服务器一定要能访问来自多个供应商的服务供应商代码。

JNDI 命名体系结构的关键概念包括：

对象和名称之间的绑定。

若干称为命名上下文的绑定集。

命名系统，即若干组命名上下文。

命名空间，指一个命名系统中的所有名称。

名称分类为原子名称、复合名称和合成名称。原子名称是不可分割的，可以绑定到一个对象上。复合名称是原子名称的组合，而合成名称则跨越多个命名系统。

命名上下文特别重要：所有的命名操作均是在上下文对象上进行的，并且名称解析过程总是从最初的命名上下文开始。

EJB 应用程序是如何使用 JNDI 的呢？JNDI 的主要用途是检索对 EJB 组件的引用。因为 EJB 框架是一个分布式对象框架，所以 EJB 应用程序不应当假定 EJB 组件的位置。JNDI 就是获取对 bean 的起始引用的一种机制。当一个 bean 安装到一个 enterprise bean 服务器上时，一个被称为 EJB 容器的软件组件就负责创建各个名称-对象绑定，使所需的 Java 类文件能使用这个 bean。应用程序使用 JNDI 的查找方法来检索对象引用，如下例所示：

Context initialContext = new InitialContext( );

CartHome cartHome = javax.rmi.PortableRemoteObject.narrow(

initialContext.lookup("applications/shopping_cart"), CartHome.class);

应用程序有责任知道外部名称，应用程序就是通过这个名称才得以引用一个 enterprise bean，并通过 JNDI 来获取对该 bean 的引用的。

进一步考察 JTA

除 JNDI 以外，Enterprise JavaBeans 体系结构还使用 Java Transaction API (JTA)。因为事务对维护数据完整性和可靠性很重要，所以支持事务处理是 EJB 体系结构的一个基本部分。如果企业应用程序是分布式的，事务处理就会更加重要：

“事务的概念是一个重要的编程范例，其目的在于简化既要求可靠性又要求可用性的应用程序结构，特别是那些需要同时访问共享数据的应用程序。事务的概念最早是用在商务运作的应用程序中，其中它被用于保护集中式数据库中的数据。后来，事务的概念已扩展到分布式计算的更广泛的环境中。今天，事务是构建可靠的分布式应用程序的关键，这一点已得到广泛承认。”（见对象管理组的“Transaction Service Specification”）

有时将事务描述为具有下列特征的工作单元：

原子性 — 如果因故障而中断，所有结果均撤销

一致性 — 事务的结果保留不变的特性

孤立性 — 中间状态对其他事务是不可见的

持久性 — 已完成的事务的结果是持久的

事务的终止有两种方式：提交一个事务会使其所有的更改永久不变，而回滚 (rolling back) 一个事务则撤销其所有的更改。

对象管理组织 (OMG) 为一种面向对象的事务服务，即对象事务服务 (OTS)，创建了一种规范。OTS 是 EJB 体系结构内的事务服务的基础。下列事务规范就是为 enterprise bean 所采用的事务模型而设：

OMG 的对象事务服务 (OTS)

Sun Microsystems 的 Transaction Service (JTS)

Sun Microsystems 的 Java Transaction API (JTA)

开放组 (X/Open) 的 XA 接口

这种与语言无关的对象事务服务，为一个强健的分布式事务服务提供了基本概念、定义和功能。

Java Transaction Service 是 OTS 的 Java 映射，在 org.omg.CosTransactions 和 org.omg.CosTSPortability 这两个包中定义。JTS 对事务分界和事务环境的传播之类的服务提供支持。JTS 功能由应用程序通过 Java Transaction API 访问。

Java Transaction API 指定事务管理器与分布式事务中涉及的其他系统组件之间的各种高级接口，这些系统组件有应用程序、应用程序服务器和资源管理器等。JTA 功能允许事务由应用程序本身、由应用程序服务器或由一个外部事务管理器来管理。JTA 接口包含在 javax.transaction 和 javax.transaction.xa 这两个包中。

XA 接口定义了资源管理器和分布式事务环境中外部事务管理器之间的约定。外部事务管理器可以跨多个资源协调事务。XA 的 Java 映射包含在 Java Transaction API 中。


转：学不会的JAVA，消不了的忧愁！（1）

作者：NOVEMBER RAIN

email: haoy@dongyu.com.cn

日期：2001-5-9 17:16:20

学不会的JAVA，消不了的忧愁！

发言者：jigenghua

时 间：2001/03/11 17:23:51 来 自：http://www.javaunion.org

--------------------------------------------------------------------------------

亲爱的网友，和大家一样，来这里看贴子的，都会有一个共同的地方，那就是对JAVA有着或深或浅的爱好。

或许是我太笨吧，有不少书上明明写着，JAVA是一种很容易学习的编程语言，但我断断续续学过近两年的JAVA了，至今，还是写不出一个像样的、有实用价值的程序。总是一边学，一边忘。

哎，压死人的包（J2EE好像有60个）、成千上万的类（Classes)、接口（Interfaces)、异常（Exceptions)，还有无穷无尽的方法（Methods)和类变量，一个又一个深奥得不得了、怪得不得了的专业术语，弄得我头昏脑涨，气得我咬牙切齿。也许你们会说：“既然你自己这么笨，那就叫JAVA滚一边去吧，学学其它简单一些的不行吗？”是的，几番番，我也会这样想的，但想到，未来的编程世界是JAVA的世界，（这句话不是我随口说说的，只要对网络和程序稍具了解的人都会这么想），为了一口饭吃，为了挣钱娶老婆生孩子养家，我除了走编程这条路，实在没有什么好路可走，（我乃一介中专生，97年自动化专业毕业）既然要走编程的路，不懂JAVA能行吗？不行！绝对不行！在深圳，在现在，懂VC、VB、DELPHI再加上任意一种数据库系统（SQL SERVER/ORACLE/SYBASE）的温工者，找一份3、4千元的企业编程工作的基本时间是10天左右。我不懂VC也不懂VB和DELPHI，但值得庆幸的是，我对VFP比较熟，能用VFP编各种企业用的数据库应用程序。我错过了学习VB、VC的时间（不久的将来，不懂JAVA的人很可能找不到编程有关的工作），现在学了，也是落后的......我现在下班了，明天接着写。BYE！


转－关于多线程

作者：lovefan

email: lovefan@cmmail.com

日期：8/1/2001 4:07:09 PM

枫叶国游子 2001-07-04 16:32:33

我这里可以大概给你介绍一下，但对于每一种编程模型要看具体的示例是什么，而且我不可能给你罗列所有的代码，请谅解。

其实我们编程只要尽量站到比较高的层次，很多道理其实你会发现你已经懂了。

就多线程来说，我们开始设想只有两个线程（>2时是不是算数学归纳法？）那么如果两个独立的线程会发生什么呢？

1。当一个线程进入moniter（也就是说站用一个object），另一个线程只有等待或返回，而我们把返回就称为一种模式，这种模式的英文是Balking。

2。这两个线程可以是有序的执行，而不是让OS来调度，这时我们要用一个object来调度，这种模式称为Scheduler。（这个词及其含义其实OS中就有）。

3。如果这两个线程同时读一个资源，我们可以让他们执行，但如果同时写的话，你闭着眼睛都会知道可能出现问题，这时我们就要用另一种模式（Read/Write Lock）。

4。如果一个线程是为另一个线程服务的话，比如IE中负责数据传输的线程和界面显示的线程，当一个图片没有传完时，另一个线程就无法显示，至少是部分没有传完。那么这时我们要用一个模式称为生产者和消费者，英文是Producer-Consumer。

5。两个线程的消亡也可以不是完全又OS来控制的，这时我们需要给出一个条件，使得每个线程在符合条件是才消亡，也就是有序的消亡，我们称为Two-Phase Termination。

那么有这5个线程模型，基本上可以用到大多数编程任务中。我需要指出的三点是：

1。从高层次上我们可以再验证是否含盖了所有的情况。

2。其实模式不是完全固定的或者说象定律一样，而模式可以为不同的情况进行适当  的调整和组合，目的是为了简洁和高效。

3。学习模式是为了具备更好的分析问题的能力。

而似乎这些来自西方的技术，并且是目前的，我们有没有呢？其实我个人有个大胆的推测，我认为我们祖先的《孙子兵法》就是很好设计模式，因为它符合设计模式需要的基本特征，就是在特定的条件下，用某种特定的方式合理且高效的解决问题。只不过一是用在军事上，二是完备性方面我们还没研究。但我认为我们至少没有很好的扩展和进行类比式的应用，否则今天可能是我们中国人教外国人什么是设计模式。

类比的方法实际上是发明或发现的常用方法。不知能否让你感觉到其实外国的技术并不是那么的神秘，也许我们从自身的文化当中挖掘出的东西太少了。

P.S.更深一步的研究你可以参考Doug Lea. <<Concurrent Programming in Java>>.


转－类的深入研究

作者：lovefan

email: lovefan@cmmail.com

日期：8/8/2001 2:26:55 PM

类的深入研究

（印佛西地　2001年08月07日 10:29）

1：在Java中使用继承

    面向对象的程序设计中最为强大的功能是类的继承，类的继承允许你在一个已经存在的类之上编写新的程序，例如，你想建立一个可在屏幕上显示并能填充它的矩形类，你可以从头开始或者利用旧的矩形类，下面的部分将向你介绍如何继承已存在的Rectangle类，而不需重写其中的代码。

    比如建立一个fillRect类，该类可以使用Rectangle类中所有已定义的数据和成员函数，如：width、height等数据和getArea等成员函数，就可是使用继承的方法来实现。使用extands关键字让Java程序员能够继承已有类的成员函数，为了继承Rectangle类，你必须引用旧的Rectangle类，你必须引用旧的Rectangle类，并且在新类的说明中引用它，比如：

import Shapes.Rectangle;

class fillRect extands Rectangle

{

   .....

}

2：成员函数的重载

    继承之后，如何使fillRect类比Rectangle类有所提高呢？我们可以用如下代码来实现一个新的drawRect成员函数，它将大大缩短代码，并能填充矩形，而不是仅仅画出矩形的轮廓：

private String makeString(chr ch,int num)

{

  StringBuffer str=new StringBuffer();

  for(int i=num;i>0;i--)

  str.append(ch);

  return str.toString();

}

public void drawRect()

{

  for(int i=height;i>0;i--)

  System.out.println(makeString("#",width));

}

    注意这里我们使用了StringBuffer类。之所以使用StringBuffer是因为String只能产生一个静态类型－－它的大小是不能改变的，而StringBuffer能够产生一个可变长度的字符串类型。

    在这里，drawRect成员函数被重载了，通过使用相同的成员函数名字，你可以用新的成员函数来代替旧的成员函数。不过，那些被说明为final的成员函数是不能被重载的。

    注意，你不必在新的类中包含那些与被继承类相同的代码，而只需要加入你想要的东西，但你必须建立一个新的构造成员函数，以区分这两个不同的类。

    新类的全貌如下所示，你可以发现通过继承Rectangle类，代码变得非常简单明了。

class fillRect extands Rectangle

{

   public fillRect(int w,int h)

  {

      supper(w,h);

      private String makeString(char ch,int num)

      {

         StringBuffer str=new StringBuffer();

         for(int i=num;i>0;i--)

         str.append(ch);

         return str.toString();

      }

      public void drawRect()

      {

         for(int i=height;i>0;i--)

         System.out.printlm(makeString("#",width));

      }

  }

}

3：使用接口

    Java可以创建一种称作接口(interface)的类，在这个类中，所有的成员函数都是抽象的，也就是说它们都只有说明没有定义，你可以如下所示来说明一个接口。

public interface interfaceName

//成员函数说明

    接口中成员函数的缺省引用类型是private，接口（interface）的内部变量是不可更改的，并且总是static和final。

    通过使用关键字implement，你可以在定义类时继承一个接口。不过与extends不同的是，一个类可以同时继承多个接口。

    使用接口的优点在何处呢？通过创建一个接口，你可以说明一整套抽象的成员函数而无须去具体实现它，所有继承了这个接口的类，都将有着具有相同原形的成员函数。例如，你想所有的shapes都有一个draw()成员函数，你可以创建一个接口并且用Shape命名：

public interface Shape

void draw();

    现在，无论你何时创建一个继承自Shape的类，都将拥有一个成员函数draw()。

4：类的转换

    类的转换，与不同类型的变量之间的转换有相似之处，但并不一样。

    我们可以把一个父类的对象转换成一个子类对象，下面的代码说明一个类转换的例子，其中Y类是从X类继承而来的：

Y y=new Y();

X x;

x=y;

    需要注意的是两个子类之间的转换是不可以的。

5：null,this和supper变量

    所有的类都有三种变量：null、this和supper。

    null变量不指向任何实际对象,而是指向一个空对象,如下例所示:

    Rectangle rect=null;

    该例产生一个矩形类的变量，但不创建一个实际的对象。另外，假如一个成员函数需要一个对象作为参数时，你同样可以用null代替。

    this变量指向对象本身，一个类可以通过this变量来获得一个代表它自身的对象变量。

    supper变量是一个指向类父类构造成员函数的变量，你可以通过调用它，来迅速完成对子类的构造成员函数的设计。


转－如何学习java

作者：lovefan

email: lovefan@cmmail.com

日期：8/1/2001 4:08:34 PM

：（如何学习Java呢？） 枫叶国游子 2001-07-04 12:37:58

我可以结合自己的经验大致给你说一说，希望对你有所帮助，少走些弯路。

学习Java其实应该上升到如何学习程序设计这种境界，其实学习程序设计又是接受一种编程思想。每一种语言的程序设计思想大同小异，只是一些由语言特性的而带来的细微差别，比如Java中的Interface，你几乎在以前的学习中没有碰到过。以下我仔细给你说几点：

1。我们必须明确一个大方向，也就是说现在面向对象的编程范畴。尽管人工智能曾经有所浪潮（看看Borland为什么有Turbo Prolog），但未来5-10年工业界广泛承认并接受的将是面向对象式的编程。

2。工业界目前最流行的面向对象编程语言就是C++和Java。所以基本上锁定这两个方向就可以了。而且完全可以同时掌握。

3。掌握Java的精华特性而且一定要知道为什么。比如，Interface和multi-thread。用interface是更好的多继承的模型，而多线程则是设计到语言一级的重要特性。要完全理解interface是为什么，用多线程又有几种常用的编程模型。

4。理解了语言的特性是为什么了之后，就可以试着上升到设计这个层次，毕竟学习语言是要用的。目前比较好的开发模式是采用自定向下的面向对象的设计，加上MVC的模式（你可以看一下我介绍的关于MVC的内容）。首先要找出最顶层的对象（这往往是最难的），然后一层一层往下递归，记住每次应符合7+/-2的原则，因为我们人的短记忆就是这样。一般有图形用户界面的应从界面开始设计。

5。有了基本设计模型后，可以学一些设计模式（Design Pattern)。这是目前证明很有效的。比如体系结构模式（Layering分层，Pipe/Filter管道或过滤器），设计模式（有很多，比如对象池Object Pool、缓冲池Cache等），编程模式（比如Copy-on-Write）。懂了这些模式之后，就会对系统的整体结构有很好的把握，而学术上也有倾向一个系统完全可以由各种模式组合而成。前面提到的MT实际上就有好几种模式，掌握后就不用自己花很多时间去试了。另外一个很重要的领域就是并行和分布式计算领域，大概有20种左右。

6。接下来就不能纸上谈兵了，最好的方法其实是实践。一般教科书上的例子并不能算是实践，只能算是让你掌握语言特性用的。而提倡做实际的Project也不是太好，因为你还没有熟练的能力去综合各种技术，这样只能是你自己越来越迷糊。我认为比较好的方法是找一些比较经典的例子，每个例子比较集中一种编程思想而设计的，比如在我的实践当中，我曾经学习过一个很经典的例子就是用Java实现的HotDraw（源自SmallTalk），你可以用rolemodel或hotdraw在搜索引擎上找一下，我记不大清楚了。好象rolemodel.com是个网站，上面有原代码和一些基本设计的文档。另一个来源可以到http://www.umlchina.com

7。结合前面学到的设计模式你就可以很好的理解这些经典的例子。并且自己可以用他来实现一些简单的系统。如果可以对他进行进一步的修改，找出你觉得可以提高性能的地方，加上自己的设计，那就更上一个层次了，也就会真正地感到有所收获。

8。好象以上谈的跟Java没什么关系，其实我们早就应该从单纯的学习语言到真正的学习好编程的领域。学习技术是没有止境的，你学习第一种语言可能要半年时间，以后每种语言都不应该超过两个月，否则你会觉得学习语言是包袱，是痛苦。

9。学习是为了用的，是为了让你的程序产生价值，把握住这个原则会比较轻松点。

没有第10点了，因为没有东西是十全十美的，哈哈~~。

Happy Programming!


转帖：JAVA无用论

作者：climbeNET

日期：2000-10-27 8:45:27

JAVA无用论

2000-10-24

1. 写这篇文章的目的

学了一些关于Java的东西，个人认为它很不好用。为了减少上当人数

特写此文。

尽管 java 宣称它是100%面向对象，而C++不是。但是宣称100%面向

对象本身就不是什么面向对象的思想。面向对象的本质目的是提高生

产率。讨论一种语言是否100%面向对象就好像讨论我们的社会性资性

社一样滑稽。其实学习面向对象不一定非要使用java不可。我反而认

为120%面向对象的C++是不错的选择。

Java号称跨平台，其实这是只一个梦想，也不妨用骗局这个词来形容

。软件的移植性仅仅和虚机相关吗？照此理论是不是可以说符合IA32

体系的软件在任何IA32体系上都能正常运行。那我怎么没看见SCO：

：lf 在 NT上运行？显然，除了虚机，软件二进制规范（执行文件格

式、连接方法）、运行时间库和系统调用也是软件移植问题因素。而

java规范中显然不包括完整的运行时间库和系统调用，甚至二进制规

范也有不兼容的地方（比如本原接口和固有接口）。软件总不能脱离

它的运行环境，不兼容就从这里开始。

要提高软件生产效率。应当认认真真的研究系统结构、二进制规范、

系统调用结构才是解决问题的方法。靠概念游戏这种投机取巧的方法

是行不通的。

我说java不好，甚至不必对其侧目，你信吗？我估计信的人不多。我

只是想说“上当之后别怪我没说”。

另外如果您在Java中发现了甜头也不妨告诉我。互相学习吗:)。

2. Java究竟是什么？

Java是一种虚拟硬件系统结构，是一种软件二进制规范，是一种编程

语言。当我们看到Java一词时，它可能表达上述三个含义钟的任何一

个。尽管这三个东西可以独立存在，不过实际上他们总是在一起出现

。所以也很少有人分的那么明确。使用Fortran语言编写Java虚机上

的代码理论上是可行的。但是有谁乐意做这件事呢？

作为虚拟硬件体系结构，

Java虚机和ALPHA、PowerPC、SPARC、IA32、51、960是同一概念上的

东西。也许您会奇怪？他们怎是同样的东西呢？IA32不就是PIII、PI

V那一大块吗？其实不然、我们知道386、P5、P6、P7的内部结构非常

不同。属不同的微体系。但是他们实现了相同的IA32指令集合。IA32

也不一定非要通过硬件实现，通过软件也可以。这种软件CPU产品广

泛出现在操作系统和CPU密集软件的开发、调试工具上。主要用于在C

PU出现之前调试软件。Java指令集同样也可以通过硬件实现。只是在

半导体工业激烈竞争的时代，没有大公司（含SUN）愿意把这么做。

SUN常常说M$很害怕java。他们之间的确存在很强的竞争关系。倒不

是Java高级语言的问题，我认为是Java二进制规范和 Windows操作系

统存在竞争。不妨考察Java中的(.class文件格式、装入方法、执行

过程、JDBC接口、J3D接口)和Windows中的(PE文件格式、DLL文件格

式、装入、连接方法、ODBC、Direct3D)。就知道竞争多么强烈。和N

T/IA不同。Java.exe执行程序包含虚机规范和二进制规范。在NT/IA

中是分离的产品。

人们总喜欢把Java高级语言和C++类比，他们之间的确存在很过相似

的地方。不过我认为C++和Java不存在什么竞争。它们使用在不同的

场合。他们的区别（内存管理、对象类型、线程规范）决定他们的用

途。用形似而神不似来形容C++和Java吧。仅从高级语言方面来看。J

ava真正竞争来自ECMAScript 。也就是JScript和VBScript。

3. Java虚机和IA32比较

Java VM和IA32最大的区别是Java VM的主要实现不考虑效率问题。由

此派生出寄存器少，指令结构简单（同样功能生成的程序代码长），

寻址空间小，寻址方法少、数据类型少等。除了上述问题，Java VM

没有涉及指令（显式或隐式）并行问题。

3.1. 基本特性差别

3.1.1. 数据类型比较

Java VM

IA32

整数类型

byte

short(16bit) char(UnionCode)

int

long (64bit)

{不存在无符号算术运算指令,另外注意这里没有boolean类型}

byte

Word（16bit）

DoubleWord(32bit)

QuotaWord(64bit)

DoubleQuotaWord(2*64bit)

BCD/PackedBCD

BitField

{存在有符号和无符号两种运算指令}

浮点类型

float(32bit)

double(64bit)

float(32bit)

double(64bit)

long double(80bit)

Packed double(2*64bit)

地址

returnAddress*

8bit , 16bit , 32bit , 48bit都有

没有显示的看到returnAddress

类型的宽度，不过。通过指令goto(0xa7)和goto_w(0xc8)来看。retu

rnAddress是可以是16位或32位的。

关于double和Long的原子问题：在目前的java规范中，double和long

作为两个独立的32 位数据独立存取。这样可能产生同步问题。历史

上Intel的CMPXCHG8B（Pentum pro以上CPU提供）指令就存在过类似

问题，一度被指责是CPU设计问题。好在CMPXCHG8B很少使用，没有造

成什么事故。

3.1.2. 指令集

指令类型

JavaVM

IA32

Load/Store

1） 数据和运算栈之间单个移动

2） 常数移入运算栈

1） MOVE

2） CMOVEXX

3） 交换，琐操作

4） PUSH/POP

5） PUSHA/POPA

6） IN/OUT

元算指令

add , sub , mul , div , neg , rem , shl , or , and , xor

比Java多多了，主要多在显式并行上

类型转换

基本类型之间可以互相转换

有（由于类型很多，转换指令也有很多）

方法调用

4条指令

一大堆，主要多在权限切换（调用门）、长短跳传不同上。

3.1.3. 异常

虽然JavaVM有异常的支持，不过很简单。它不区分中断、陷阱和故障

。也不能明确指出出错代码的PC，并返回那里重新执行。

3.1.4. 执行态

Java VM没有用户态和系统态执行区分。当然也没有MMU(管理内存分

页、分段的内存管理单元)．除非被Java高级语言限制，java VM汇编

代码也能完成一些恶意操作。

3.1.5. 小结

总而言之，Java VM很简单。SUN总是认为通过编译优化Java可以达到

C++速度。显然是谎言。如果要编译成本地代码，显然要丢失移植性

。如果不编译成本地代码，效率问题如何解决？有一种及时编译的设

计。对于很小的应用也许可行。对于规模的应用呢？要知道优化编译

很费CPU和内存。像IA64体系的优化方案，要编译很多遍才能优化完

成。要运行时刻编译是不是天方夜谭。

4. Java高级语言和C++、JScript比较

4.1. 效率，谎言

很多书上写Java是很快的语言。这时一定要看清楚是比谁快。语句传

了几本书难免有些误差，比较对象经常有意无意丢失。一般认为Java

比JScript快。的确快很多，不只十倍。我也承认Java比JScript快。

但有人说Java和C++效率接近。这就是骗人了。他们也能做出试验证

明，试验结果好像也能证明结论。他们的实验大概都是形如这样。

Java, C++测试代码：

for ( I = 0 ; I<100 ; I++ )

{

RemoteCall( I );　 //外部调用，调用一次用1s。

｝

结果：

Java 102s , C++ 100s。

结论：

Java达到了C++ 98.0%的效率。

这种测试违背了测量学中要把测试对象放到主要矛盾上这一基本原

理。方法的不正确当然带来了测试结果的不正确。有本事比比

for ( I = 0 ; I<10000; I++)

{

y = x * z;　　//由Java语言或C++语言实现执行体

}

我的结果是170倍。也就是PIII变成了286。如果使用指令并行优化

，C++和Java的速度差异可以达到千倍以上。想用一下8088吗？

4.2. 功能缺失导致代码膨胀

简单的说，Java比C++缺少编译预处理、缺少运算符重载、多继承和

类模板。我认为取消这些概念很粗暴。和C++不同。对于大规模编程

来说，Java的代码量不会比C少多少。甚至由于类型约束太强。有可

能比C的代码量还大。

Java常称多继承比较困难，所以要取消。而且使用实现接口的方法可

以代替C++的多继承。但是，实现接口要自己写所有的接口实现代码

。不嫌烦就慢慢写吧。

为什么要取消运算符重载？真是不可理解，既简单又好用。它居然说

难，且容易造成混乱。

总之，处理这几个大方向的问题，还有很多小问题。据说AWT不好用,

感觉他们为了完成任务写程序。

4.3. 简单的骗局

自古便宜没好货。Java总是宣扬Java如何简单，主要宣扬的好处是垃

圾自动收集和线程同步。其实不然，由于Java没有释放关键字。使得

不能及时释放或定时释放不必的数据。要知道能够分配的不只是内存

，还有数据库句柄、文件句柄、结果集等。这样不得不绕来绕去的完

成这些任务。其实C++的析构函数是自动调用的，也不用自己手动掉

用。遇到一些特殊情况，C++也能很好的解决。

也许java的线程比较简单吧，不过C++中也可以继承线程类。由于本

人没有涉及线程编程。这里不好乱说。

5. Java的兼容性

相对本地代码来说，Java程序的确移植性比较好。归其原因不外有二

。一来Java虚机只一种（不像天下有很多种CPU），二来Java二进制

规范只有一种(不像这么多种操作系统)。但是，Java代码真的可以随

心所欲的移植吗？其实不然，上述两条中的任何一种不再满足，都会

影响ByteCode的移植性。

现在，Java的主要版本已经有1.0 , 1.1.7 , 1.2(2.0)三个。更残酷

的是1.1.7和1.2不兼容。一个很著名的例子。Websphere不能在Java1

.2上运行。另外WebLogic不能在1.0上运行。

java的二进制规范呢？jview( MS java)就自立门户。他的.class装

入方法就和java for win32不同。另外它提出了其他的本地代码连接

方法。显然这里也会造成不兼容。

除了上述两条重要的原因，还有就是程序的运行环境。比如操作系统

是否区分文件名的大小写。数据库是否中调用都能返回执行成功。EJ

B的代码是否有EJB 的运行服务器。等等。

6. 在精通Java之前否定Java

本人只是对Java略知一二。为什么就非要否定java呢。其实，从一般

科学观点审视java。就会发现其中的问题。

6.1. Java代码不符合从简单到复杂的认识规律

代码比较：

Java Code Example:

import java.applet.*;

import java.awt.Gaphics;

public class HelloWorldApplet extends java.applet.Appple

{

public void init(){

resize( 200 , 150 );

}

public void paint( Graphics g){

g.drawString("Hello World" , 50 , 100);

}

}

C++ Code Example:

#include

class Hello :public Applet

{

public void init();

public void paint(Graphics g);

};

Hello::init()

{

resize( 200 , 150 );

}

......

我们看到，java开始就要求写函数的实现。而C++先要描述轮廓。再

写函数实现。

6.2. Java对从一般到特殊的思维方法支持的不好

从一般到特殊，是重要的思维方法。对应到计算机学科就是继承。而

今多继承的概念已经广为流传。比如我们说“小花猫”，就是继承“

小”、“花”、“猫”三个概念。如果在字典上看到小花猫这个词，

它一定不再描述“小”、“花”、“猫”这三个概念，而是继承他们

。但是java只能继承其中的一个。对另外连个还要仔细的描述。写起

来是不是很烦。

6.3. Java设计不尊重“任何事物都有产生、发展、消亡的过程”这

一自然规律

Java没有析构函数。这一点很讨厌。我只想重复一遍“对象不都在内

存空间中”。

6.4. Java 不能表达少数类之间的紧密相关

java要求每个类使用一个文件。这种实现可能和它的自动编译执行方

式有关。注意这种限制不是实现上问题，而是规范中的语句。但是这

种方法合适吗？请看下面C++的例子。将这两个类分开为两个文件是

否真的好读？

struct menuitem{

String　strX;

int　　　iIndex;

};

class menu{

menuitem item[100];

... ...

};

和这种情况类似，javadoc也不能对紧密相关的数据进行描述。

至于类模板。java更是不支持。自己一个一个的慢慢写吧。

6.5. 批判Java的批判

Java要诞生，必须批判原有C/C++语言的缺陷。列举其中一二。

“C/C++语言中的整形数据不限制长度。这样一来当程序从32位系统

移植到16 位系统时会造成错误”。可是仔细想想。这种移植的概率

究竟有多少？

“Java不像C/C++，它完全不支持指针”。按此理论，同样不支持指

针的Fortran77和basic是不是也很强壮。

“Java不允许隐式类型转换”。如果整形和浮点数据之间的转换都要

写一下，是不是很烦。

“联合体、全局变量他们都不是面向对象的东西”。面向对象一直就

没有什么定义。本着好用的东西就是好东西的原则，保留这些概念也

不错。像M_PI这类的常量或者项目中的确全局相关的准常量，使用全

局变量是不是很方便？至于联合体，是优化存储效率用的，像 Java

这种不考虑效率的语言自然不会支持。

“java存在比C++多的运算符”。好像是多一些，他们是(字符串 +),

> >> , （boolean）& and | , instanceof。但是我们分析一下。由

于java不支持运算符重载，只好单独实现(字符串 +)。由于java 不

区分有符号和无符号数据，所以使用>>>来表达无符号右移、由于jav

a中的boolean和整形之间没有联系，又不支持运算符重载，所以对bo

olean类型单独实现& and |。至于instanceof的确是Java 独创，不

过这个运算符的用处还要考虑。我在引用某变量时居然能不知道它的

类型？

6.6. 兼容以不兼容为代价

使用java 必须一切从头开始。java不能继承原有的工作。而且它也

不能很好的和其他语言混合编程。这就和C++区别大了。首先C++继承

了大部分C语言代码。其次，C++可以和Pascal , Fortran , Basic，

T-SQL等其他语言方便混合。尤其方便的是ASM语言和 C++混合。发挥

不同语言的长处。俗话说“朋友多，天地宽”吗。而 java只能孤军

奋战了。

7. 结束语

写这篇文章用了一天半的时间。又在Java上花了这么多时间，真是心

痛。但愿有些效果。

7/7


转载：java的文件对象操作

作者：lovefan

email: lovefan@cmmail.com

日期：2001-7-6 11:02:54

                      java文 件对象操作

在 我 们 进 行 文 件 操 作 时， 需 要 知 道 一些关 于 文 件 的信 息。File类 提供了 一些 成 员 函 数 来 操 纵 文 件 和 获得 一些文 件 的 信 息。

1、创 建 一 个 新 的 文 件 对 象

你 可 用 下 面 三 个 方 法 来 创 建 一 个 新 文 件 对 象：

File myFile; myFile = new File("etc/motd");

或

myFile = new File("/etc","motd"); //more useful if the directory or filename are variables

或

File myDir = new file("/etc"); myFile = new File(myDir,"motd");

这 三 种 方 法 取 决 于 你 访 问 文 件 的 方 式。 例 如， 如 果 你 在应 用 程 序 里 只 用 一 个 文 件， 第 一 种 创 建 文 件 的 结 构 是 最容 易 的。 但 如 果 你 在 同 一 目 录 里 打 开 数 个 文 件， 则 第 二 种或 第 三 种 结 构 更 好 一些。

2、 文 件 测 试 和 使 用

一 旦 你 创 建 了 一 个 文 件 对 象， 你 便 可 以 使 用 以 下 成员 函 数 来 获 得 文 件 相 关 信 息：

文件名 ： String getName()

路径：    String getPath()

         String getAbslutePath()

         String getParent()

         boolean renameTo(File newName)

文 件 测 试 ： boolean exists() 、 boolean canWrite() 、 boolean canRead() 、 boolean isFile() 、 boolean isDirectory() 、 boolean isAbsolute()

一 般 文 件 信 息 l long lastModified() l long length()

目 录 用 法 l boolean mkdir() l String[] list()

3、 文 件 信 息 获 取 例 子 程 序

这 里 是 一 个 独 立 的 显 示 文 件 的 基 本 信 息 的 程 序， 文 件通 过 命 令 行 参 数 传 输：

import java.io.*; class fileInfo{

     File fileToCheck;

     public static void main(String args[]) throws IOException{

     if (args.length>0){

          for (int i=0;i<args.length;i++){

               fileToCheck = new File(args[i]);

               info(fileToCheck);

          }

     }

     else

         {

         System.out.println("No file given.");

     }

  }

  public void info (File f) throws IOException {

      System.out.println("Name: "+f.getName());

      System.out.println("Path: "=f.getPath());

      if (f.exists()) {

         System.out.println("File exists.");

         System.out.print((f.canRead() ?" and is Readable":"")); System.out.print((f.cnaWrite()?" and is Writeable":"")); System.out.println("."); System.out.println("File is " + f.lenght() = " bytes."); } else { System.out.println("File does not exist."); } } }


转载--微软98年关于MTS和EJB的比较说明文件.其实Sun也有类似的对比文件,我忘记在那里了!(1)

作者：zybersx

email: zybersx3@21cn.com

日期：2000-7-23 23:22:24

http://www.microsoft.com/com/wpaper/mts-ejb.asp

Comparing Microsoft Transaction Server to Enterprise JavaBeans

Microsoft Corporation

Contents

Introduction

A Brief Overview

Types of Objects

Supporting Transactions

  Transactions in Microsoft Transaction Server

  Transactions in Enterprise JavaBeans

Controlling Complexity

Portability

Interoperability

Standardization by Specification

Choices for a Development Language

Administering a Distributed Transaction Environment

Summary

Introduction

Microsoft&reg; Transaction Server (MTS) is the transaction service in the Windows NT&reg; operating system. First available in 1996, MTS is now in use at many organizations. Enterprise JavaBeans (EJB) is a specification for a Java-based transaction service. Created by a group of companies led by Sun Microsystems Inc., the initial specification for EJB was released in spring 1998. Both Microsoft Transaction Server and Enterprise JavaBeans target the creation of component-based, transaction-oriented applications. This paper provides a comparison of these two technologies. More specifically, the paper analyzes the similarities and differences between the ways the two component models handle objects, transaction support, controlling complexity, portability, interoperability, language choice and administration.

Back to contents

A Brief Overview

Put simply, a transaction can be thought of as a set of events that are committed or rolled back as a unit—either all of the events happen, or none of them do. For example, transferring $100 from your savings account to your checking account requires both subtracting the money from your savings account and adding it to your checking account. To achieve a consistent result, either both operations should occur or neither one should. Transaction-oriented applications like this are very common, and so many vendors provide software that makes writing transactional applications easier. And because transactional applications often need to handle hundreds or thousands of clients (i.e., users at workstations), services that make it easier to build scalable applications are commonly built into products that support transactions.

Both Microsoft Transaction Server and Enterprise JavaBeans support transactional applications and provide services for scalability. Because the two technologies attempt to solve many of the same problems, they have much in common. The easiest way to illustrate their similarities is to describe the basic architecture and terminology of each.

Figure 1

MTS is based on the Component Object Model (COM), and an MTS application is implemented as one or more components. As shown in Figure 1, these components are managed by the MTS Executive. COM objects that run under the control of MTS are called MTS objects. As is usually the case with COM, MTS relies on a class factory object to create specific instances of each COM class. To allow MTS to provide extra services, the MTS Executive transparently inserts a wrapper object between each object it manages and that object's client. For example, when a client uses the COM IClassFactory interface to create an instance of a MTS object, that call is actually made on a factory wrapper object implemented by MTS, which in turn passes on the call to the real class factory. Similarly, every method call a client makes on the business methods of a MTS object is first handled by a MTS-supplied context wrapper object. The MTS Executive also supplies a context object for each MTS object. This context object maintains information specific to the MTS object, such as what transaction it belongs to (if any) and whether it has completed its work. Every context object implements the IObjectContext interface, through which a MTS object can access the services that MTS provides.

The general architecture of Enterprise JavaBeans is very similar to MTS; little more than the terminology is changed. As shown in Figure 2, an EJB container acts like the MTS Executive, hosting enterprise beans rather than MTS objects. Each bean exposes its own Home interface, analogous to the COM IclassFactory interface, allowing a client to create instances of specific classes. And, like MTS, the EJB container provides a wrapper for each bean it manages. An EJB Home object wraps the bean's Home interface, while an EJB Object wraps the bean's business methods. Also, an EJB container provides either the javax.ejb.SessionContext interface or javax.ejb.EntityContext interface, both of which offer services similar to MTS's IObjectContext. And although it's not shown in the diagram, an enterprise bean is required to implement one or more additional interfaces that the EJB container uses to interact with the bean.

Figure 2

Both MTS and EJB rely on a transaction coordinator, a logically separate service responsible for ensuring that all operations in a transaction are successful or that none of them are. In MTS, this function is implemented by the Distributed Transaction Coordinator (DTC), a service included in Windows NT. The MTS Executive communicates with the DTC through OLE Transactions, a COM-based interface. The Enterprise JavaBeans specification does not define how a transaction coordinator is implemented or how an EJB container communicates with the transaction coordinator. Instead, each vendor is allowed to implement these functions in any way it wishes.

Back to contents

Types of Objects

MTS objects are typically designed to encapsulate some set of business functionality. For example, an MTS object might allow a client to transfer money between two accounts or build and submit an order of some kind. Because MTS objects are written as distinct components, business functions encapsulated into these objects can be combined in arbitrary ways, allowing them to be flexibly reused. Although it's not strictly required, an MTS object is typically accessed by one client at a time, and it may (but isn't required to) use transactions.

The EJB specification takes a more complex view of objects. Unlike MTS, EJB explicitly defines two different types of enterprise beans. The first, called session beans, are much like MTS objects. Session beans encapsulate some segment of business logic, are typically accessed by one client at a time, and optionally use transactions. The EJB specification also defines a type of persistent object called an entity bean. Unlike session beans, entity beans represent a specific set of persistent data stored in a database, such as an employee record or a particular order. Each entity bean has a unique key that identifies it, and each one is intended to be accessible by many clients simultaneously. Furthermore, entity beans are required to use transactions.

An MTS client need do nothing special to create and work with transactional objects; to a client, an MTS object looks like an ordinary COM object. An EJB client, on the other hand, must understand and correctly use new interfaces and methods to create, manage and destroy enterprise beans. Because MTS is built on COM, it automatically benefits from the richness and maturity of the COM environment. Enterprise JavaBeans, by contrast, is forced to invent much of its own infrastructure, including such basics as persistent object support and interfaces, allowing clients to create remote beans.

Back to contents

Supporting Transactions

A transaction defines a set of events that are guaranteed to be committed or rolled back as a unit. Accordingly, every transaction has a beginning, some number of events that are part of the transaction (such as database updates), and an end. Exactly how a transaction's boundaries are demarcated can vary. A common approach in traditional client/server transaction products is to require the client to make a specific call to the transaction coordinator to begin the transaction. The client then makes calls to the components that carry out this transaction's work, and finally ends the transaction with another explicit call to the transaction coordinator. This final, transaction-ending call can indicate that the transaction should be committed, making all of its changes permanent, or aborted, causing all of its changes to be rolled back. It's the job of the transaction processing system to ensure that exactly one of these two outcomes occurs.

Back to contents

Transactions in Microsoft Transaction Server

When combining transactions with components, the traditional client-controlled model isn't usually the best approach. Instead, MTS introduced a new way to demarcate transaction boundaries. Called automatic transactions, it allows clients to remain unaware of when transactions begin and end—they never need to make explicit calls to begin or end a transaction. Instead, when a transaction begins depends on the value of that component's transaction attribute. This value can be set to one of four possibilities

If a component is marked as Requires New, the MTS Executive will always begin a transaction when its caller first invokes a method in one of that component's objects.

If a component is marked as Required, the MTS Executive will begin a transaction when its caller first invokes a method in one of that component's objects unless the caller is already part of a transaction. In this case, any methods invoked in the object will become part of the existing transaction.

If a component is marked as Supported, the MTS Executive will never begin a transaction when a caller invokes methods in one of the component's objects. If the caller is already part of a transaction, the work this object does will become part of that transaction. If the caller is not part of a transaction, this object's work will execute without being wrapped inside a transaction.

If a component is marked as Not Supported, the MTS Executive will never begin a transaction when a caller invokes methods in one of the component's objects. Even if the caller is part of an existing transaction, this object's work will not become part of that transaction.

When it has finished its work, an MTS object can call either SetComplete, if it wishes to commit that work, or SetAbort, if the work it has done should be rolled back. If neither is called by the time this object's transaction ends, the MTS Executive behaves as though the object called SetComplete—the default behavior is to commit the transaction. These calls are made by a method in the MTS object itself, not by the client (again, the client need not be aware that transactions are being used). If every object participating in the transaction agrees to commit, the MTS Executive will tell the transaction coordinator to commit the transaction. If any MTS object in this transaction calls SetAbort, however, the MTS Executive will tell the transaction coordinator to abort the transaction.

Back to contents

Transactions in Enterprise JavaBeans

The Enterprise JavaBeans specification defines three approaches to demarcating transaction boundaries:

Client-managed transactions, where a client makes explicit calls to begin and end each transaction

Container-managed transactions, which allow a client to rely on the EJB container to begin and end transactions automatically

Bean-managed transactions, where an enterprise bean makes explicit calls to begin and end transactions

Given that its creators are trying to ensure that Enterprise JavaBeans can be retrofitted onto existing legacy products, they have chosen to support the traditional client-managed approach to transaction demarcation. EJB has also adopted MTS's innovation of automatic transactions. In fact, EJB's container-managed transactions are nearly an exact clone of MTS's automatic transactions. An enterprise bean can be labeled as TX_REQUIRES_NEW, TX_REQUIRED, TX_SUPPORTS or TX_NOT_SUPPORTED, all of which have the same meaning as in MTS. And like MTS, Enterprise JavaBeans assumes that a bean wishes to commit the transaction unless it explicitly invokes the setRollbackOnly method (EJB does not provide an analog to the MTS SetComplete call, however).

At first glance, EJB's plethora choices can appear attractively flexible. But they are also very complex. What happens, for instance, when different approaches to transaction demarcation are used simultaneously? Suppose an application begins a client-managed transaction, then executes a method in an enterprise bean that's configured to require a new container-managed transaction. And suppose this bean creates yet another bean that's using bean-managed transactions. What happens when the client commits its transaction? Understanding all the possible combinations (legal and illegal) and their outcomes will be very challenging for developers. And if developers can't easily understand the outcome of their transactions, how can they write correct applications? Compared to the simple, unified model that MTS provides, EJB offers developers a confusing and potentially error-prone array of choices for controlling transaction boundaries.

And in fact, MTS provides all three transaction demarcation options found in EJB, but in a less confusing way. First, as previously described, EJB container-managed transactions are virtually identical to MTS automatic transactions. MTS also supports client-managed transactions, allowing a client to determine explicitly when a transaction should begin and end. But unlike EJB, MTS builds this service on top of automatic transactions rather than introducing an entirely separate mechanism. This eliminates the possibility of confusion between automatic transactions and client-managed transactions. And, finally, an MTS object with its transaction attribute set to Not Supported can make direct calls on the DTC using OLE Transactions. By doing this, an MTS object can demarcate transactions on its own, just like an EJB bean-managed transaction.

Back to contents

Controlling Complexity

A primary goal of any transaction processing platform should be to control the complexity inherent in writing transactional applications. Understanding difficult business problems, then creating the correct code to solve them is hard enough without being forced to spend time wrestling with a complex environment. Yet the creators of Enterprise JavaBeans apparently did not have simplicity as a goal. Instead, EJB looks like the union of everyone's ideas about how to do transaction processing.

For example, as already described, EJB transaction demarcation is very complex, with three different options spelled out in the specification and a myriad of possible ways to combine those options. Enterprise JavaBeans also defines two types of enterprise beans: session beans, representing business logic, and entity beans, intended to represent persistent data stored in a database. These two bean types must be treated quite differently by developers. (Support for entity beans is not required by the current version of the EJB specification, so an EJB-compliant vendor can implement only session beans if desired. This adds another variable to EJB's already-complex set of options.) A session bean must be configured to be either stateful or stateless, depending on the choices its creator makes. Entity beans can have either bean-managed persistence, with the bean itself responsible for issuing database calls to load and save its persistent data, or container-managed persistence, where the EJB container does this on the bean's behalf. And entity beans can be labeled as either reentrant or non-reentrant, depending on whether the developer has made provisions for bean reentrancy. Furthermore, because entity beans are designed to be accessible to many clients at once, there must be some way to control concurrent access to their data. The EJB specification gives two illustrative examples of how this might be done but leaves the door open for any other approach.


转载--微软98年关于MTS和EJB的比较说明文件.其实Sun也有类似的对比文件,我忘记在那里了!(2)

作者：zybersx

email: zybersx3@21cn.com

日期：2000-7-23 23:23:38

Furthermore, both session and entity beans can be accessed with either the Java Remote Method Protocol (JRMP), which is the original protocol defined for Java's Remote Method Invocation (RMI) interface, or through the Internet Inter-ORB Protocol (IIOP) defined by the Object Management Group (OMG). And vendors can also use proprietary protocols to access an EJB container. The truth is that even a casual reading of the EJB specification makes clear that this is a technology created by committee. The authors of EJB suffer from the difficulty of pleasing a diverse group of vendors, many of whom plan to retrofit existing transaction products to support this new technology. Accordingly, EJB attempts to support products that are both component-based and not component-based, database-oriented and not database-oriented, and more. This results in significant complexity and a diffuse approach to the problem.

Compare this complexity with the approach taken by MTS technology. MTS defines one style of component—a COM object—and one powerful but simple model for defining transaction boundaries. MTS components need not be configured as stateful or stateless, and they're always accessed in the same way: through COM calls. (Remote clients that speak Distributed COM (DCOM) can directly invoke methods in MTS objects on other machines. For access from Internet-enabled clients via HTTP, the MTS Executive is loaded into Internet Information Server (IIS), the Web server built into Windows NT. An IIS application such as an ASP script can then make COM calls into the same MTS objects.) The creators of MTS understood the importance of simplicity, and its easy-to-understand programming model is one of MTS's most attractive characteristics.

Back to contents

Portability

Portability of enterprise beans across implementations from different vendors is frequently cited as a primary goal of EJB. In fact, the EJB specification states "An enterprise bean can be developed once and then deployed on multiple platforms without recompilation or source code modification." Yet the specification also states that "specialized containers can provide additional services beyond those defined by the Enterprise JavaBeans specification. An enterprise bean that depends on such a service must be deployed only in a container that supports the service." In practice, this is all but certain to lead to significant differences among products supporting Enterprise JavaBeans, which brings into question its validity as a standard.

This approach to standardization is reminiscent of the OMG's Common Object Request Broker Architecture (CORBA), where vendors introduced significant differences in their products to gain proprietary advantage. Even though the Enterprise JavaBeans specification repeats the familiar claim that enterprise beans will be "Write Once, Run Anywhere," this cannot be true in a world of specialized, proprietary EJB containers. While the specification does define a common core that every product must support, the history of ensuring portability based on nothing but paper specifications isn't encouraging (CORBA is only the most recent failed attempt at achieving this). The bottom line is that true portability of enterprise beans is likely to be elusive.

Back to contents

Interoperability

An important aspect of an effective distributed environment is seamless, secure communication among machines. MTS accomplishes this by defining one way to access MTS objects—COM calls—and specifying standard solutions for security and directory protocols. It is less clear how this will be accomplished with Enterprise JavaBeans.

As already noted, the EJB specification allows either the native RMI/JRMP protocol or IIOP to be used for communication, with perhaps other choices possible. But JRMP is a very simple technology. It makes no provision, for example, for carrying a client's identity to the server, nor does it define a standard way to convey information about any transaction that might be in progress. The result is that even though JRMP appears to be a core protocol for Enterprise JavaBeans, this protocol cannot reasonably be used for interoperability among different vendors. Instead, using JRMP for communication from a client to an EJB server, or from one EJB server to another, requires either going without fundamental EJB, such as security and distributed transaction support, or adding proprietary extensions.

For multivendor communication, the EJB specification assumes that IIOP will be used. Yet using IIOP to access enterprise beans raises several questions. First, there are well-known technical issues still outstanding with this protocol, such as how servers learn of client's untimely deaths. Equally important, the creators of Enterprise JavaBeans seem to place a great deal of faith in IIOP, stating in the EJB specification that EJB's CORBA mapping "enables on-the-wire interoperability among multiple vendors' implementations of an EJB server." Given that products implementing CORBA's Object Transaction Service (OTS) over IIOP don't interoperate today even among themselves, how can we expect them to interoperate with a completely foreign technology such as EJB? And the EJB specification doesn't require using the CORBA OTS to communicate with a transaction coordinator. This means that even if IIOP interoperates flawlessly among different vendors' products, EJB-compliant products may still not work together because they use incompatible transaction coordinators. Promises of interoperability are easy to make as long as there are no actual EJB products. Once there is shipping code from multiple vendors, however, customers are likely to find themselves with less than what they've been promised.

In fact, EJB's attempt to merge the Java environment with CORBA is inherently problematic. The EJB specification is owned by Sun, a company that has largely exited the pure CORBA business. Many of their EJB partners have not, however. The perceived need to merge the Java world with CORBA stems as much from the political needs of the vendors involved as from real user requirements. And, as was true with CORBA, the creators of EJB clearly want to allow differentiation among their "standard" products. EJB vendors appear to want to have it both ways: They wish to say they have a standard but still retain substantial proprietary elements in their products.

Furthermore, JRMP and IIOP provide only basic communication services. What about protocols for distributed security and directory services? Both the Java and CORBA camps define interfaces for these services, but interfaces alone are not sufficient to ensure interoperability; well-defined wire protocols are required. But agreements on what security and directory protocols should be used with EJB (or with CORBA) have yet to appear. Contrast this with MTS: By using standard protocols in a consistent way, MTS makes it easy to build an effective distributed environment, one that's capable of supporting the kinds of mission-critical uses targeted by transaction-oriented applications.

Back to contents

Standardization by Specification

Writing specifications is a useful exercise, but the rubber meets the road when those specifications are actually implemented. To define a standard, write and publicize the specification, and only then build it and see if it works is not a good idea, as has been shown by many failed standardization efforts. In fact, today's most successful standardization organization, the Internet Engineering Task Force (IETF), requires working implementations before finalizing a standard.

Yet it is doubtful that anybody actually built a complete implementation of Enterprise JavaBeans before the final specification was released. When this specification actually is implemented, the first EJB developers will probably find things that don't work or can't be effectively mapped to existing products. The result will be either changes to or more loosening of the specification.

And as might be expected from a specification-only technology, EJB doesn't define every detail. It's hard to imagine that multiple vendors can build interoperable implementations or that third parties can create a market for truly portable components with only the EJB specification to guide them. Time after time, paper-only standards have proved to be an ineffective means of achieving interoperability and portability. Without a reference implementation, there's just too much room for ambiguity.

MTS, by contrast, is a mature product, available since late 1996 and currently in use at hundreds of customer sites. And because MTS is an actual implementation rather than just a specification, its behavior is by definition unambiguous. Shipping software is much more effective than shipping specifications.

Back to contents

Choices for a Development Language

Choosing a programming language is one of the most important decisions made on any project. Striking the right balance between power, ease of use and long-term support is critical. Because no one language can meet every need, MTS allows building applications in any language that supports COM. This list includes the Visual Basic&reg; development system, C++, Java, COBOL, and many more.

By design, however, Enterprise JavaBeans allows development only in Java. To the Java proponents at many vendors, most of whom have roots in C++, Java seems simple. And compared to C++, Java is simple. But the users of transaction processing software are primarily business developers, very few of whom know C++. Instead, their experience is likely to be in COBOL or Visual Basic, languages that are much more common among end users.

Both COBOL and Visual Basic can be used to write MTS applications, but neither can be used with EJB. If an organization does wish to use Java, MTS has excellent support for developing Java components. Yet Java is probably too complex for many business developers and, in any case, the entire industry will never settle on a single programming choice. By tying its fate exclusively to one language, EJB greatly limits its usefulness. And by making that language Java, EJB leaves behind a substantial percentage of its target developer market.

Back to contents

Administering a Distributed Transaction Environment

A component-based transaction system relies heavily on its administrator. For example, both MTS automatic transactions and EJB container-managed transactions allow setting a component's transaction requirements administratively. Both technologies also allow a component's security requirements to be set by an administrator. Other important functions, such as how in-doubt transactions should be resolved, must be performed administratively as well.

MTS provides standard graphical tools that allow an administrator to perform these types of functions. But while the EJB specification acknowledges the importance of the administrator's role, it does not explain how that role should be carried out. Without standards for administration, every EJB product will be different. Setting a component's transaction or security requirements, for example, is likely to require entirely different processes in different vendors' products. In practice, these kinds of differences will make it difficult for an organization to install and effectively use EJB containers from different companies. As is true in the CORBA world today, customers will be strongly motivated to choose and stick with a single vendor. To a very large degree, this will obviate the motivation for defining a multivendor standard in the first place. Instead, users who obtain a "standard" EJB product will find that changing to another vendor's EJB implementation is too painful to contemplate—there's just too much effort required.

Back to contents

Summary

The Enterprise JavaBeans specification attempts to define a platform for building transaction-oriented applications. The reality, however, is that this technology is rife with problems. The most significant are as follows:

EJB is remarkably complex, with many options.

EJB is defined by a committee-created specification only; there is no reference implementation to help insure interoperability.

EJB very likely won't allow true portability of enterprise beans, despite claims to the contrary.

EJB-based applications will have problems with interoperability because of the looseness and diversity of protocols allowed.

EJB allows application development in only one language, a language that is probably not suitable for a large part of its intended user base.

EJB leaves important parts of the problem undefined, such as how administration is accomplished, allowing vendors to create differentiation in what are touted as standard products.

MTS suffers from none of these problems. Its simple programming model makes it easy to create components in any popular language, and those components will work in any MTS environment. MTS is also much more mature, a critical attribute when choosing a foundation for mission-critical transactional applications. And, most important, MTS is available today as a part of Windows NT. Given all these advantages, it is clear that Microsoft Transaction Server is the right choice for building enterprise applications.

Back to contents

Last Updated: 7/30/98

&copy; 2000 Microsoft Corporation. All rights reserved. Terms of Use.


准备翻译j2ee的doc，不知道有没有意义，请大家讨论。先贴一个：配置指南

此电子文档属C#乐园QQ群3256045整理。
准备翻译j2ee的doc，不知道有没有意义，请大家讨论。先贴一个：配置指南

上一页 目录 下一页

准备翻译j2ee的doc，不知道有没有意义，请大家讨论。先贴一个：配置指南

作者：freehero

email: jdxmoon@163.net

日期：2000-11-29 14:28:02

声明：本文档由freehero翻译，未经作者同意不能用于商业用途，您可以自由的复制和传播，文档只供您理解j2ee，因作者能力有限，建议您同时阅读英文文档，对于因为翻译的错误代来的后果作者不负任何责任，欢迎您来信讨论freehero@163.net

j2ee配置指南

1。介绍

    本文介绍j2ee环境的配置，所有涉及的文件都在j2ee安装目录下的oncfig目录，这些文件都是文本文件，

你可以有任意的文本编辑器来编辑它们。

    通常情况下，你没有改变这些文件的必要。但是如果你使用的数据库驱动不是cloudscape的时候，你需要

遵循本章的内容进行配置。

2。JDBC驱动

    默认的j2ee安装后支持cloudscape数据库（一个sun公司提供的演示数据库），如果你的ejb使用的jdbc不

是连接cloudscape数据库，那么你需要按照下面的知道进行配置。（如果你不能确定本版本的j2ee支持你使用

的jdbc驱动，请到 11111 看相关信息。）

3。驱动的位置

   你必须把驱动文件（.jar文件）copy到 $J2EE_HOME/lib/system目录下。并且保证该文件包含在

J2EE_CLASSPATH 的环境变量中。

4。J2EE_CLASSPATH 环境变量

   j2ee server通过JDBC驱动连接数据库。通通过J2EE_CLASSPATH 的环境变量定位驱动文件（.jar文件）的位

置。你可以在启动j2ee前在命令行设置这个变量。但是我们推荐你在脚本中设置J2EE_CLASSPATH 变量。编辑用

户配置文件在安装的过程中是必须的一步。在unix中，用户配置文件是 /bin/userconfig.sh，windows nt中是

bin/userconfig.bat

    注意：你应该同时在CLASSPATH环境变量中去掉驱动文件（.jar文件）。

5。JDBC 1.0 驱动

    为了配置jdbc1.0驱动，编辑config/default.properties 文件的jdbc.drivers 和 jdbc.datasources两个

属性值。再次要注意的是驱动文件必须包含在J2EE_CLASSPATH环境变量中。

    jdbc.drivers 属性

    jdbc.drivers 属性的值是有一系列的冒号分割的jdbc驱动的类名。格式如下：

    jdbc.drivers=<classname>:<classname>: . . .

    例如：

    jdbc.drivers=oracle.jdbc.driver.OracleDriver:testutil.MyDriver

    jdbc.datasources 属性值

    jdbc.datasources 属性值由两部分组成，一个是datasource的jndi的名字，一个是数据库的url。典型的

情况是：jndi名字是数据库的逻辑名字，rul指定数据库的实际位置。jndi名字和url都是在ejb编码的过程中指

定的。rul的格式，请参照jdbc驱动提供商的文档。

    jdbc.datasources 的语法如下：

    jdbc.datasources=<jndi_datasource>|<url>|<jndi_datasource>|<url>. . .

    |符号有两个作用。第一，它隔开几个jndi_datasource；第二，它隔开每一个jndi_datasource中的

jndi_datasource> 和 <url> 元素.

    jndi_datasource元素的语法如下：

    jdbc/<jndi_name>

    由jdbc关键字开头，jndi_name是在jndi目录中指定的datasouce名字。

    例如：

    jdbc.datasources=jdbc/Oracle|jdbc:oracle:thin@rtc:1521:acct|jdbc/MyDB|jdbc:test

6。支持JTA的JDBC2.0驱动

    配置JTA的JDBC2.0的驱动，你需要设置以下属性的值：

    jdbc20.datasources

    xadatasource.<n>.jndiname

    xadatasource.<n>.classname

    xadatasource.<n>.dbuser

    xadatasource.<n>.dbpassword

    xadatasource.<n>.prop.<property-name>

    同时你也必须在J2EE_CLASSPATH 环境变量中包括jdbc驱动文件。

    jdbc20.datasources 属性

    jdbc20.datasources 属性的值和jndi名字连接，用于定位datasouce的位置（它就是lookup方法的参数）

。

    jdbc20.datasources 的语法如下：

jdbc20.datasources=<jndi_datasource>|<jndi_XA_datasource>|<jndi_datasource>|<jndi_XA_datasource>

    |的作用有两个，第一，隔开jndi_datasource 和<jndi_XA_datasource>并组成一对，第二，隔开每一对的

jndi_datasource> 、<jndi_XA_datasource>元素。

    <jndi_datasource> 和 jndi_XA_datasource有相同的语法：

    jdbc/<jndi_name>

    jdbc关键字开头。jndi_name是datasource名字，这个名字指向jndi目录。

    例如：

    jdbc20.datasources=jdbc/Merant|jdbc/XAMerant|jdbc/Finch|jdbc/XAFinch

    xadatasource属性

    xadatasource.<n>.jndiname 属性指定XA DataSource的jndi名字。语法如下：

    xadatasource.<n>.jndiname=jdbc/<jndi_XA_datasource>

    n代表每一组xadatasource。第一个jdbc2.0驱动n为0，下一个是1，2等等。jndi_XA_datasource元素和

jdbc20.datasources 指定的值对应。

    The xadatasource.<n>.dbuser 和 xadatasource.<n>.dbpassword 属性指定数据库用户和密码。通常该用

户具有管理员的权限（更多的信息请看   2222  The distributed.transaction.recovery ）。

    xadatasource.<n>.prop.<property_name> 指定和特定的属性值关联的值。实际的值可能是jdbc驱动。

    例如：

    xadatasource.0.jndiname=jdbc/XAMerant

    xadatasource.0.classname=com.merant.sequelink.jdbcx.datasource.SequeLinkDataSource

    xadatasource.0.prop.url=jdbc:sequelink://mypc:5000/[Oracle]

7。事务

    你可以编辑config/default.properties 文件控制事务发生和超时设置。

    distributed.transaction.recovery 属性

    这个属性控制分布事物是否发生。入这些事物可以发生的化，下面的条件必须满足：

        跨库操作时的事物范围。

        j2ee程序通过jdbc2.0 JTA驱动访问数据库

        当故障发生的时候，事物的各个的组成部分可以按照符合两段提交协议的方式提交或回滚。

        事物发生的时候，server重新启动后事物可以提交或回滚。

    distributed.transaction.recovery的值为true或者false。j2ee安装后默认为false。

    distributed.transaction.recovery=false

    transaction.time属性

    在应用container-managed 事物的ejb中，你可以控制事物超时的间隔通过设置transaction.timeout的值

。例如：

    transaction.timeout=5

    在设置以后，如果事物在5秒内没有完成，j2ee事物管理器将回滚它。

    当j2ee第一次安装的时候，timeout的值时0代表没有超时。

    只有使用container-managed 管理事物的ejb受到timeout的影响。至于在bean-managed和JTA

transactions 中你可以引用 UserTransaction 的 setTransactionTimeout方法。同时你也可以在其他组建中

引用setTransactionTimeout方法：比如servlets和jsp。

8。端口

    j2ee需要tcp/ip端口。修改config目录下的相应文件可以修改端口号。下表是端口号和相应的文件。

    EJB                 9191    ejb.properties  http.port=9191

    HTTP                8000    web.properties  port=8000

    HTTPS               7000    web.properties  https.port=7000

  Naming and Directory  1050    orb.properties  port=1050

    9191 端口用于提供client下载需要的stub类

    8000 提供http服务

    7000 提供https服务

    1050 基于orb的jndi名字服务使用的端口

9。日志文件

    j2ee server有几个日志文件。默认这些文件在logs目录下。编辑config/default.properties 文件中的

log.directory 属性可以更改默认目录。

    log.directory=logs

    同时你也可以编辑default.properties 文件更改日志文件的名字。例如：你可以修改log.output 属性的

值来修改the output.log 文件的名字。

    一般来说，日志文件决定于启动j2ee的模式。（single还是multiple   VM (virtual machine) 模式）

    Single VM 日志文件

    默认的情况是Single VM 模式。在这种情况下，日志文件位于

    $J2EE_HOME/<logs>/<host>/ejb

    logs是在default.properties 文件中log.directory 属性指定的。host元素是计算机名称。

    生成下列文件。

    system.out

    system.err

    event.log

    output.log

    error.log

    system.out 和system.err文件包括ejb中 System.out 和 System.err的输出。如果你运行j2ee -verbose

参数，输出被写到stdout（标准输出） 和stderr（标准错误。输出）。system.out 和 system.err日志文件不

创建。只有config/auth.properties 文件中audit属性值为true时audit.lo才创建。

    Multiple VM 日志

    如果你用j2ee -mutiVM启动server，ejb和http服务在自己的虚拟机以单独的服务启动。每一个depploy的

应用程序也在自己的虚拟机中运行。日志文件和上述的一样，但是存放的目录不同。

    目录如下：

    EJB                      $J2EE_HOME/<logs>/ejb/ejbd

    HTTP                     $J2EE_HOME/<logs>/ejb/httpd

    deployed application     $J2EE_HOME/<logs>/ejb/<app-name>

    web server日志

    编辑web.properties文件中的内容，可以改变由web server创建的日志文件的名字。

10。安全

    未授权用户

    对于j2ee来说，当有未授权的用户试图调用ejb container的时候，j2ee必须存在一般意义上的未授权用户

存在。在j2ee中这个用户名字是guest，密码是guest123。你可以在auth.properties 文件中修改用户名和密码

。

    default.principal.name=guest

    default.principal.password=guest123

    Keystore密码

    如果用户使用公钥访问https，认证信息放在<user-home>/.keystore 文件中。同时user-home是

System.getProperty("user.home")的返回值。

    keystore是一个典型的密码文件。默认的密码是changeit。可以在web.properties 文件中修改

keystore.password 属性的值来改变密码。

    ANYONE 角色

    在application  deployment工具的security面板中，默认的情况下方法分配给ANYONE 角色。这样的结果

是所有的用户和组都可以调用它。如果你没有影射到一个特定的角色，每一个用户和用户都可以调用它。可以

在auth.properties 文件中设置anyone.role.name 的值来改变默认anyone角色。

    钝化发生的条件：内存限制

    当ejb container开始钝化一个ejb的时候，它把ejb保存在附属存储中并且试图重新分配内存。默认的情况

下，如果内存使用超过128m的时候钝化开始发生。可以编辑config/default.properties 文件中

passivation.threshold.memory 属性的值来修改默认值。

    passivation.threshold.memory=128000000

    值必须是一个正的整数。当你减少这个值的时候钝化现象回经常发生。

    jndi name  server host

    如果jndi name server和j2ee server不在同一个机器上，你必须改变config/orb.properties 文件中host

属性的值。

    host=localhost

    HTTP 文档根

    默认的情况下，http和https服务的文档是public_html。你可以编辑web.properties 文件中documentroot

属性的值来改变它。

    documentroot=public_html/


最大化JAVA代码的可重用性 (转)

作者：jeru

email: jeru@163.net

日期：8/6/2001 5:35:46 PM

最大化JAVA代码的可重用性

——克服传统OO方法在重用方面的缺陷

出处: http://www.javaworld.com

mashy    翻译

摘要：不要放弃编写可重用代码的努力！本文介绍了三种对现有代码进行修改以提高其可重用性的方法。

在程序员中似乎存在着一种日益普遍的观点，认为重用只是一个神话。或许是传统的面向对象编程方法中所存在的不足增加了重用的困难。本文介绍了从另外一种不同的途径使重用成为可能的三个步骤。

第一步：将功能实现从类实例的方法中移出

由于缺乏精确性，类继承不是非常理想的代码重用机制。换句话说，如果不继承一个类的数据成员和其他的方法，那么你就无法重用这个类的某个单独的方法。这些额外的不必要的负担使方法重用的代码变得复杂。派生类对其父类的依赖性也以入了额外的复杂性：对父类的改动会对子类造成影响；当修改任意一个类的时候，我们很难记得清哪个方法被覆盖，哪个没有；而且被覆盖的方法是否会调用父类中相应的方法并不非常清晰地显现。

任何执行单一概念任务的方法应该能够成为代码用的首选而独立存在。为了达到这个目标，我们必须会到过程化的编程模式，将代码从类实例的方法中移出，形成具有全局可见性的过程。为了提高这种过程的可重用性，过程代码应该象静态的通用方法一样编写：每个过程只能使用自己的输入参数，只能调用其他全局性的过程完成其工作，不能使用任何非本地的变量。这种对外部依赖的简化降低了过程使用的复杂性，也增加了在其他地方使用此过程的可能性。当然，由于其结构通常会变得更为清晰，即使抛开重用的目的不谈我们也可以从这种代码的组织方式中受益。

在Java中，方法不能脱离类而单独存在。因此，你可以对相关的过程进行组织并使它们成为一个独立的类中的公共静态方法。例如，对于如下所示的一个类：

class Polygon{

…

public int getPerimeter(){…}

public boolean isConvex(){…}

public Boolean containsPoint(Point p){…}

…

}

可以将它改写成下面的形式：

class Polygon {

…

public int getPerimeter() { return pPolygon.computePerimeter(this);}

public boolean isConvex() { return pPolygon.isConvex(this);}

public boolean containsPoint() { return pPolygon.containsPoint(this, p);}

…

}

在此处，nPolygon应该是这个样子：

class pPolygon {

static public int computePerimeter(Polygon polygon) {...}

static public boolean isConvex(Polygon polygon) {...}

static public boolean containsPoint(Polygon polygon, Point p) {...}

｝

从类的名字pPolygon可以看出，该类所封装的过程主要与Polygon类型的对象有关。名字前面的p表示该类的唯一目的是组织公共静态过程。在Java中，类的名字以小写字母开头不是一种标准的做法，但象pPloygon这样的类事实上并不执行普通类的功能。也就是说，它并不代表着一类对象，它只是语言本身所需要的用于代码组织的实体。

  在上面这个例子中，改动代码的总体影响是使得客户代码不必为了重用其功能而从Polygon继承。Polygon类的功能现在已经由pPolygon类以过程为单位提供。客户代码只使用自己需要的代码，无需关心自身并不需要的功能。

这并不意味着在这种新型的过程化编程模式中，类不服务于更有用的目的。恰恰相反，类执行组织和封装对象数据成员的必要工作。而且它们通过多重接口实现多态性的能力也为代码重用提供了显著的支持，这将在下一个步骤中讨论。然而，由于将功能实现包含在实例方法中无法实现理想的代码重用，所以通过类继承实现代码重用和多态性支持也不应成为最佳的技术选择。

在一本被广为阅读的书《Design Patterns》中曾简要地提及一种略有不同的技术。策略模式（Strategy Pattern）提倡将相关算法的每个成员封装在一个通用的接口下，以便于客户端代码可交换地使用其算法。由于一个算法通常被作为一个或几个独立的过程进行编码，这种封装更注重执行单独任务的过程的重用，而不是执行多种任务的、包含代码和数据的对象的重用。这一步骤体现了相同的基本思想。

然而，将一个算法封装在一个接口下意味着将算法作为实现接口的对象进行编码。这意味着我们仍然依赖于一个与所包装的对象的数据和其他方法相耦合的过程，这样便会使其复用变得复杂。此外还存在这样一个问题，每次需要使用这个算法的时候都必须实例化这些对象，这便会降低程序的性能。值得庆幸的是，设计模式提供了针对这两个问题的解决方法。可以在对策略对象进行编码时应用享元模式(Flyweight Pattern，译者注：还存在一种译法为轻量模式)，这样每个对象只会存在一个被共知共享的实例(这针对程序性能的问题)，而且每个共享对象在访问间隔中并不维持状态(于是对象将没有数据成员，这针对大多数的耦合问题)。由此产生的享元--策略模式非常类似于在这一步骤中所提到的将功能实现封装在全局可见的、无状态的过程中的技术。(译者注：以上这两段文字读起来可能有些晦涩难解，建议有兴趣的读者参阅文中所提到《设计模式》一书，Erich Gamma等著、李英军等译、机械工业出版社出版。)

第二步：将非原始的输入参数类型改为接口类型

在面向对象编程中，代码重用的真正基础在于通过接口参数类型利用多态性，而不是通过类继承，正如Allen Holub在 “Build User Interfaces for Object-Oriented System, Part 2”中所述：

“……你应该通过对接口而不是类编程实现重用。如果一个方法的所有参数都是某个已知接口的引用，这个接口由一些你所不知道的类实现，那么这个方法就能够操作这样一些对象：当编写方法的代码时，这些对象的类甚至还不存在。从技术上讲，可重用的是方法，而不是传递给方法的对象。”

将Holub所讲的方法应用于第一步所得到的结果，只要某块功能代码能够作为一个全局可见的过程而独立存在，你就可以将其每个类类型(class-type)的输入参数改为一个接口类型，这样便能进一步提高其重用的潜力。那么，实现此接口类型的任何类的对象都可以作为参数使用，而不仅仅局限于原始类。由此，这个过程对可能存在的大量的对象类型都成为可用的。

例如，有这样一个全局可见的静态过程

static public boolean contains(Rectangle rect, int x, int y) {…}

这个方法用于检查给定的矩形是否包含某个给定的点。在这个例子中，rect参数的类型可以从Rectangle类改变为接口类型，如下所示：

static public boolean contains(Rectangular rect, int x, int y){…}

Rectangular可以是下面形式的接口：

public interface Rectangular{

       Rectangle getBounds();

}

现在，所有可以被描述为矩形的类(也就意味着实现了Rectangular接口)的对象都可以作为传递给pRectangular.contains()的rect参数。通过放宽所传递的参数类型的限制，我们使方法具有更好的可重用性。

不过，在上面这个例子中，Rectangular接口的getBounds方法返回一个Rectangle类型，你可能会怀疑使用这个接口是否具有真正的价值；换句话说，如果我们知道传入过程的对象会在被调用时返回一个Rectangle，为什么不直接传入Rectangle取代接口类型呢？不这样做的最重要原因与集合有关，假设有这样一个方法：

static public boolean areAnyOverlapping(Collection rects) {…}

这个方法的目的在于检查给定集合中的任意矩形对象是否存在重叠。那么，在方法内部遍历集合中的每个对象时，如果无法将对象造型(cast)成如Rectangular这样的接口类型，那么将如何能够访问对象的矩形区域呢？唯一的选择是将对象造型成为其特定的类型(我们直到它有一个能够返回rectangle的方法)，这意味着方法必须事先知道其所要操作的是什么类型。这恰恰是这一步骤力图首先要避免的问题！

第三步：选择低耦合的输入参数接口类型

完成第二步之后，应该选择什么样的接口类型来取代给定的类型呢？答案是能够通过参数完全描述过程的需求，同时又具有最少的额外负担的接口类型。参数对象所要实现的接口越简单，其他特定类实现此接口的机会就越大——由此，其对象可以作为参数使用的类也就越多。通过下面的例子可以很容易地看到这点：

static public boolean areOverlapping(Window window1, Window window2) {...}

这个方法用于检查两个窗口（假定是矩形窗口）是否重叠，如果这个方法只要求从参数获得两个窗口的矩形坐标，那么简化参数的类型使其能反映这个事实是一种更好的选择：

static public boolean areOverlapping(Rectangular rect1, Rectangular rect2) {...}

以上的代码假设先前的Window类型的对象同样可以实现Rectangular接口。现在对于所有的矩形对象，都可以重用第一个方法所包含的功能了。

你可能多次体验到当一个接口能够完全确定需要通过参数获哪那些内容时，会存在太多不必要的方法。在这种情况下，应该在全局命名空间中定义一个新的公共接口以供其他可能面临同一困境的方法重用。

你可能还会不止一次地发现，在确定需要通过单一过程的一个参数获取哪些内容时，最好创建一个单独的接口。你应该只为这个参数使用此接口。这通常会在你希望如同C语言中的函数指针一样使用参数的情况下出现。例如下面的过程：

static public void sort(List list, SortComparison comp) {...}

此过程使用参数所提供的比较对象comp，通过比较给定列表中的所有对象而对其进行排序，sort对comp的全部要求是调用一个单独的方法进行比较。因此，SortComparison应该是只带有一个方法的接口：

public interface SortComparison {

boolean comesBefore(Object a, Object b);

}

这个接口的唯一目的是为sort提供一个与其完成任务所需功能相联系的钩子(hook)，因此SortComparison无法在其他地方重用。

结束语

以上所述的三个步骤用于现有的、按照相对传统的面向对象方法所编写的代码。这些步骤与面向对象编程技术结合就形成了一种可以运用于今后代码编写中的新方法，它可以提高代码的可重用性和内聚性，同时降低了耦合度及复杂性。

很显然，这些步骤无法运用于那些在本质上就不适合于重用的代码。这类代码通常出现在应用程序的表示层(presentation layer)。例如程序中用于创建用户界面的代码，以及将输入事件与完成实际工作的过程相联系的控制代码，都是属于那种其功能在不同的程序中差别很大的代码，这种代码的重用几乎是不可能的。


最大限制地提高代码的可重用性（转）

此电子文档属C#乐园QQ群3256045整理。
最大限制地提高代码的可重用性（转）

上一页 目录 下一页

最大限制地提高代码的可重用性（转）

作者：zergman

email: zergman@chinaasp.com

日期：2001-6-15 13:28:57

[b]最大限制地提高代码的可重用性

克服传统面向对象编程方法在可重用性方面的不足[/b]

[BLOCKQUOTE][b]摘要[/b]

不要放弃编写可重用代码的努力！本技巧总结了三种修改您的现有代码以增强其可重用性的方法。[i][/i] [/BLOCKQUOTE][b]作者：Jeff Mather([email]jeff.mather@javaworld.com[/email])[/b]

重用是一种神话，这似乎正在日渐成为编程人员的一种共识。然而，重用可能难以实现，因为传统面向对象编程方法在可重用性方面存在一些不足。本技巧说明了组成支持重用的一种不同方法的三个步骤。

[b]第一步：将功能移出类实例方法[/b]

由于类继承机制缺乏精确性，因此对于代码重用来说它并不是一种最理想的机制。也就是说，如果您要重用某个类的单个方法，就必须继承该类的其他方法以及数据成员。这种累赘不必要地将要重用此方法的代码复杂化了。继承类对其父类的依赖性引入了额外的复杂性：对父类的更改会影响子类；当更改父类或子类中的任一方时，很难记住覆盖了哪些方法（或者没有覆盖哪些方法）；而且是否应该调用相应的父类方法也不明朗。

执行单一概念性任务的任何方法都应该是独立的，并应将其作为要重用的首选方法。要实现这一点，我们必须返回到过程式编程，将代码移出类实例方法并将其移入全局可见的过程中。为了提高这类过程的可重用性，您应该像编写静态实用方法那样编写这类方法：每个过程只使用其自身的输入参数和/或对其他全局可见过程的调用完成其工作，而且不应该使用任何非局部变量。这种外部依赖性的减弱降低了使用该过程的复杂性，从而可促进在别处对它的重用。当然，即便那些不计划重用的代码也会从这种结构中受益，因为它的结构总是相当清晰。

在 Java 中，方法不能脱离类而存在。但是，您可以采取相关步骤，使方法成为单个类的、公共可见的静态方法。作为示例，您可以采用类似下面这样的一个类：

class Polygon {

      .

      .

      public int getPerimeter() {...}

      public boolean isConvex() {...}

      public boolean containsPoint(Point p) {...}

      .

      .

}

并将其更改为类似以下的形式：

class Polygon {

      .

      .

      public int getPerimeter() {return pPolygon.computePerimeter(this);}

      public boolean isConvex() {return pPolygon.isConvex(this);}

      public boolean containsPoint(Point p) {return pPolygon.containsPoint(this, p);}

      .

      .

}

其中，pPolygon 如下所示：

class pPolygon {

    static public int computePerimeter(Polygon polygon) {...}

    static public boolean isConvex(Polygon polygon) {...}

    static public boolean containsPoint(Polygon polygon, Point p) {...}

}

类名 pPolygon 反映了该类所封装的过程主要与类型 Polygon 的对象有关。类名前的 p 表示该类的唯一用途就是将公共可见的静态过程组织起来。然而，在 Java 中类名以小写字母开头是不规范的，像 pPolygon 这样的类并不完成正常的类功能。这就是说，它不代表一类对象；它只是该语言所需的一个组织实体。

在以上事例中所作更改的全部效果就是，客户端代码不再非要通过继承 Polygon 来重用其功能。现在这一功能在 pPolygon 类中是以过程为单位提供的。客户端代码仅使用它所需的功能，而不必关心它不需要的功能。

这并不意味着类不会在新的过程式编程风格中发挥积极作用。恰恰相反，类要执行必要的分组任务，并封装它们所代表的对象的数据成员。此外，类通过实现多个接口而具备的多态性使其具备了卓越的可重用性，请参阅第二步中的说明。但是，您应该将通过类继承获得可重用性和多态性的方法归类到优先级较低的技术中，因为将功能包含在实例方法中并不是实现可重用性的最佳选择。

四人合著的畅销书 [url href=http://www.cn.ibm.com/developerWorks/java/jw-tips/tip107/index.shtml#resources][i]DesignPatterns[/i][/url] 简要提及了一种与这一技术只有细微差别的技术。那本书中的 [i]Strategy[/i] 模式提倡用一个共公接口将相关算法的每个系列成员都封装起来，以便客户端代码可互换这些算法。因为一种算法通常被编写为一个或几个独立的过程，因而这种封装强调重用执行单一任务（即一个算法）的过程，而不强调重用包含代码和数据、执行多项任务的对象。本步骤也体现了同样的基本思想。

然而，用接口封装算法意味着将算法编写为实现该接口的一个对象。这意味着我们仍然被束缚在与数据耦合在一起的过程及其封装对象的其他方法上，因而使重用变得复杂。每次使用算法时必须实例化这些对象也是个问题，这将降低程序的性能。幸运的是，[i]Design Patterns[/i] 提供的一种解决方案可解决这两个问题。在编写 Strategy 对象时您可使用 [i]Flyweight[/i] 模式，以使每个对象仅有一个众所周知的共享实例（该实例处理执行问题），这样每个共享对象就不会在两次访问之间维护状态（因此该对象不包含任何成员变量，从而解决了许多耦合问题）。生成的 Flyweight-Strategy 模式将本步骤中封装功能的技术高度集成在全局可用的无状态过程中。

[b]第二步：将非基本数据类型的输入参数类型转换为接口类型[/b]

通过接口参数类型而非通过类继承利用多态性，这是在面向对象编程方法中实现可重用性的真正基础，正如 Allen Holub 在 [url href=http://www.cn.ibm.com/developerWorks/java/jw-tips/tip107/index.shtml#resources]"BuildUser Interfaces for Object-Oriented Systems, Part 2"[/url] 中所讲的那样。

[BLOCKQUOTE]“... 可重用性是通过编写接口，而不是通过编写类来实现的。如果一个方法的所有参数均为一些已知接口的引用，而这些接口又是由您从未听过的一些类实现的，那么该方法可对编写代码时还不存在的类的对象进行操作。从技术上讲，可重用的是方法，而不是传递给该方法的对象。” [/BLOCKQUOTE]

将 Holub 的论述应用到第一步的结果，一旦某个功能块可作为一个全局可见的独立过程，您就可以通过将它的每个类级输入参数类型转换为接口类型，从而进一步提高它的可重用性。这样，实现该接口类型的任何类的对象都符合该参数的要求，而不仅仅是符合原始类的要求。这样，该过程便潜在地可用于更多的对象类型。

例如，假定您有一个全局可见的静态方法：

static public boolean contains(Rectangle rect, int x, int y) {...}

该方法旨在判断给定的矩形是否包含给定的位置。此处您应该将 rect 参数的类型从类类型 Rectangle 更改为接口类型，如下所示：

static public boolean contains(Rectangular rect, int x, int y) {...}

Rectangular could be the following interface:

public interface Rectangular {

      Rectangle getBounds();

}

现在，可描述为 Rectangular 的类（即可实现 Rectangular 接口）的对象都可作为 rect 的参数传递给 pRectangular.contains()。我们通过放宽对可传递给方法的参数的约束来提高方法的可重用性。

但是，就以上示例而言，当 Rectangle 接口的 getBounds 方法返回一个 Rectangle 时，您可能不知道使用 Rectangular 接口会有什么实际的好处；也就是说，如果我们知道我们要传入的对象在被请求时能返回 Rectangle；为什么不传入 Rectangle 类型而要传入接口类型呢？最重要的原因与集合有关。假定有这样一个方法：

static public boolean areAnyOverlapping(Collection rects) {...}

该方法旨在判断给定集合中的 rectangular 对象是否有重叠。接下来，在方法体中，当您依次处理集合中的每个对象时，如果无法将对象转换为诸如 Rectangular 这样的接口类型，如何才能访问那个对象的 rectangle 呢？唯一的选择是将对象转换为特定的类类型（我们已知该类中有一个方法能提供 rectangle），这意味着该方法必须事先知道它要对何种类类型进行操作，因此重用它时只能使用这些类型。这就是这一步首先要避免的问题！

[b]第三步：选择耦合性较小的输入参数接口类型[/b]

在执行第二步时，应该选择何种接口类型来替代给定的类类型呢？答案是：能充分描述过程对参数的要求且累赘最少的任何接口。参数对象要实现的接口越小，任一特定类能实现该接口的机会就越大 -- 因而其对象可用作该参数的类的数量也就越多。很容易看出，如果您有如下这样一个方法：

static public boolean areOverlapping(Window window1, Window window2) {...}

该方法旨在判断两个（假定为 rectangular）窗口是否重叠，如果该方法仅要求它的两个参数提供它们各自的 rectangular 坐标，则最好[i]简化[/i]这两个参数的类型以反映这一事实：

static public boolean areOverlapping(Rectangular rect1, Rectangular rect2) {...}

以上代码假定前面的 Window 类型对象也能实现 Rectangular。现在您就可以重用任何 rectangular 对象的第一个方法中所包含的功能。

您可能有过多次这样的经历，即充分指定了参数要求的可用接口包含过多不必要的方法。碰到这种情况时，您就应在全局名称空间中定义一个新的公共接口，以便其他可能面临同样窘境的方法重用这个接口。

您也可能有过多次这样的经历，即最好创建一个独特的接口来指定单个过程对一个参数的要求。您所创建的接口只会用于那个参数。当您希望将参数当作 C 中的函数指针处理时经常会出现这种情况，例如，假定有这样一个过程：

static public void sort(List list, SortComparison comp) {...}

该过程通过使用给定的比较对象 comp 对列表的所有对象进行比较，从而对给定的列表进行排序，sort 对 comp 的全部要求就是调用其单个方法执行比较。因此，SortComparison 应该是仅包含一个方法的接口：

public interface SortComparison {

      boolean comesBefore(Object a, Object b);

}

该接口的唯一用途就是为 sort 提供一种访问完成其工作所需功能的方法，因此 SortComparison 不应在别处重用。

[b]小结[/b]

以上三步旨在改进用更传统的面向对象方法编写的现有代码。将这三个步骤与面向对象编程结合使用即可构建一种新的方法，您可用这种新方法编写以后的代码，这样编写代码将提高方法的可重用性和内聚性，同时也会减少方法的相互耦合及复杂性。

很明显，您不应该对本质上不适合重用的代码执行这些步骤。这种代码通常存在于程序的表示层。创建程序用户界面的代码及将输入事件绑定到完成实际操作的控制代码是不可重用的两个例子，因为它们的功能随程序的不同而相差甚远，根本无法实现可重用性。[img]http://a301.g.akamai.net/7/301/1403/v001/www.javaworld.com/javaworld/icons/dingbat.gif[/img]

[TABLE][TR][TD][b]作者简介 [/b]

Jeff Mather([email]jeff.mather@javaworld.com[/email]) 为基于 Tucson，Ariz. 的 [url href=http://www.eblox.com/]eBlox.com[/url]工作，在那里他为一些公司创建有关宣传材料和生物工艺行业的 applet。他还在业余时间编写[url href=http://geocities.com/silmar_game/]共享游戏[/url]。 [/TD][/TR][/TABLE]

[b]参考资源[/b] [UL][LI]欲了解 Allen Holub 对重用的见解，请阅读他在 [i]JavaWorld[/i] 发表的文章，"Build User Interfaces for Object-Oriented system, Part 2"：

[url href=http://www.javaworld.com/javaworld/jw-09-1999/jw-09-toolbox.html]http://www.javaworld.com/javaworld/jw-09-1999/jw-09-toolbox.html[/url][LI]Allen Holub 的 [b]Toolbox[/b] 专栏的完整清单：

[url href=http://www.javaworld.com/javaworld/topicalindex/jw-ti-toolbox.html]http://www.javaworld.com/javaworld/topicalindex/jw-ti-toolbox.html[/url][LI][i]Design Patterns：Elements of Reusable Object-Oriented Software[/i]，Erich Gamma 等 (Addison-Wesley，1995)：

[url href=http://www.amazon.com/exec/obidos/ASIN/0201633612/javaworld]http://www.amazon.com/exec/obidos/ASIN/0201633612/javaworld[/url][LI]查看以前的所有 [b]Java 技巧[/b]并提交您自己的技巧：

[url href=http://www.javaworld.com/javatips/jw-javatips.index.html]http://www.javaworld.com/javatips/jw-javatips.index.html[/url][LI]Java 专家在 [i]JavaWorld[/i] 的 [b]JavaQ&A[/b] 专栏解答您最棘手的 Java 问题：

[url href=http://www.javaworld.com/javaworld/javaqa/javaqa-index.html]http://www.javaworld.com/javaworld/javaqa/javaqa-index.html[/url][LI]通过 [i]JavaWorld[/i] 的 [b]Java101[/b] 专栏从基础开始学习 Java：

[url href=http://www.javaworld.com/javaworld/topicalindex/jw-ti-java101.html]http://www.javaworld.com/javaworld/topicalindex/jw-ti-java101.html[/url][LI]访问 [i]JavaWorld[/i] 的主题索引：

[url href=http://www.javaworld.com/javaworld/topicalindex/jw-ti-index.html]http://www.javaworld.com/javaworld/topicalindex/jw-ti-index.html[/url][LI]要了解更多的 Java 窍门，请订阅 ITworld.com 的免费 [i]Java Tutor[/i] 时事通讯：

[url href=http://reg.itworld.com/cgi-bin/subcontent12.cgi]http://reg.itworld.com/cgi-bin/subcontent12.cgi[/url][LI]参与 [b]Java Beginner[/b] 的讨论，由 [i]JavaWorld[/i] 作者 Geoff Friesen 负责协调：

[url]http://www.itworld.com/jump/jw-javatip107/forums.itworld.com/webx?14@@.ee6b804/1195!skip=1125[/url][/LI][/UL]


最简单的时间格式类

作者：chenm

日期：2001-5-8 14:57:29

/*

 file: timeformat.java

*/

import java.lang.*;

import java.text.*;

import java.util.*;

public class timeformat{

  public timeformat(){

  }

  static public String getChineseFormatTime(){

      SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");

      return sdf.format(new Date());

  }

  static public String getSimpleFormatTime(){

      SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmm", Locale.US);

      return sdf.format(new Date());

  }

}


最近比较烦（程序员版）转载（版权所有：gtth 原作 ）

作者：Scaler

日期：2000-8-11 17:08:40

最近比较烦（程序员版）转载

(H)最近比较烦，~~，~~，总觉得英文帮助有一些零乱，我想我还是不习惯： 从系统维护到编程人员；

(Z)最近比较烦，~~，~~，总觉得模块一个比一个缓慢 ，朋友常常有意无意调侃：也许有一天我应该去微软；

(L)最近比较烦，~~，~~ ，我写的程序怎么也编译不完，那个代码得有一半要设中断，AI-U，写一段没有 BUG的程序越来越难；

(H)最近比较烦，~~，~~，客户的需求没有我想的简单，受尽了他们的责难，现在的我总觉得难堪；

(Z)AI!最近比较烦，~~，~~，非法操作和蓝屏是接二连三，我问盖茨说怎么办，他说，基本上，这个，很难； 哦--最近比较烦，我比较烦，比你烦，也比你烦，

(L)我梦见和总经理一起晚餐，梦中的他说下月涨工钱，我按他指示又去买了单；

(Z)程序总有大的小的循环，老板总是嫌我拖延时间；

(H)部门经理嫌我进度太慢，虽然我已每天苦干实干； 管他什么天大头衔，久而久之我会翻脸，天下没有那么傻的职员；

(L)老板发现D盘装有仙剑，他就扣我八十块的工钱；

(Z)公司太黑周末不想加班；