我们要开发一个java类:其内容只有一句,输出"hello ant"字符串。并使用ant完成编译和运行工作,这个例子只是为了跑通ant,不附加多余的东西。
下图为文件组织,请建立相应的目录,并编写HelloAnt.java
按照人家老外的文件组织规则咱也照搬。
在项目根目录(hello-ant\)写1个文件:ant执行配置文件build.xml
ok,一切大功告成,哦,不,还没有运行它。
dos下进入hello-ant的目录,即build.xml所在的目录,我们要用ant工具执行它 ,
执行: %ant_home%/bin/ant -file build.xml 用ant工具执行当前目录下的配置文件build.xml
或 :ant -file build.xml 你如果设置%ant_home%/bin到path中
这次ok了,这是答案:
命令提示符窗口 |
D:\temp\hello-ant>ant -file build.xml Buildfile: build.xml
main: [javac] Compiling 1 source file to D:\temp\hello-ant\build\classes [java] hello ant,ant 的第一次接触,好棒!
BUILD SUCCESSFUL Total time: 2 seconds D:\temp\hello-ant> |
检查一下build/classes目录,哦,看到编译过的文件就在这里:
build/classes/hello/ant/HelloAnt.class.
hello ant 进级
(此段比较废话,可以略过)
你也许会说:这末简单的工作写个批处理不就得了,又xml又ant的,把我的时间都浪费完了,我用jbuild或
webShpere不就得了,怎末说你才明白呢?反正网上开源项目大多数都用ant,你总不能给人家个*.jpx吧,
而且这样的工具太贵,受不了(当然用D的兄弟不怕^_^ ),而且ant可以让你明确的管理和自动化所有的东西:
编译-实施-测试...,哎,稍微麻烦一点点,但节约你以前花在零碎的copy,paste上的时间.而且我发现管理
代码的质量有所提高.
我们要改进build.xml,让它做更多的事情:
- 定义全局变量
- 初始化,主要是建立目录
- 编译 (已有)
- 打包为jar
- 建立API documentation
- 生成distribution产品
凡事都讲究平衡,你要ant给你做更多事,当然要累一点点,不过只用累一次,以后的代码修改后的构建都是"一键式"完成,我们制作一个hello的简单例子,你可以自己做j2ee的练习。
我们要扩充目录结构,使它更像回事:
ant处理编译之前的目录:
ant处理之后的目录:
图中:\src,\docs,\lib是自己组织的文件结构,\build,\dist是ant动态生成的成品。
\src 源文件:java源,script源,jsp源,xml配置.....
\src\main java源
\src\script window,unix,liunx的执行script,我们的简单只有一个:
run.bat: java hello.ant.HelloAnt
\docs 手写说明文档
\lib 程序所需类库的jar,比如j2ee.jar,mail,jar...
\build 用ant动态生成的构建目录
\build\classes 编译的类文件
\build\docs copy "\docs"的手写说明文档,和ant生成的api文档
\build\lib 放置我们自己的HelloAnt.class打包成品hello-ant.jar
\dist\bin copy "\src\script" 得执行文件
\dist\docs copy "\build\docs" 的文档
\dist\lib 除了copy "\build\lib"下的hello-ant.jar外,
还应copy "\lib"的程序所需jar,这里我们没有。
以上是我学老外的文件组织,大家可以按照自己的爱好组织
我们编写必要的文件:
hello.ant. HelloAnt.java |
已有 |
\docs\index.html 随便写一个手写的文档 |
hello ant 软件项目手册docs
访问api文档 |
build.xml多了些,但其实很简单:(注释比较详细可以参照,这里再简单说一下)
一个build.xml包含一个工程的自动化处理的完整xml说明,并且基本由3种东东组成:
<project >
1.全局变量的定义
<property/>
2.任务组
<target>
3.许多单项任务... 像copy,delete,javac,jar...
<task1/>
<task2/>
<task3/>
</target>
</project>
参考及下载:
本文程序:第1个hello-ant
本文程序:第2个进阶的hello-ant
ant最新下载:
http://jakarta.apache.org/ant/index.html
ant具体的编写方法参考ant手册以下2部分就形,
http://jakarta.apache.org/ant/manual/using 使用说明
http://jakarta.apache.org/ant/manual/coretasklist.html 核心tasks
其他一大堆东西你要看也行。不过我觉得比较浪费时间。
http://jakarta.apache.org/ant/manual/index.html 手册index
huihoo.com翻译改编的ant/manual/using
http://www.huihoo.com/java/ant.html
用ANT构造Application作者:余斌斌
http://developer.ccidnet.com/pub/disp/Article?columnID=295&articleID=27619&pageNO=1
ibm 利用 Ant 和 JUnit 进行增量开发——使用单元测试来逐步改进代码
http://www-900.ibm.com/developerWorks/cn/java/j-ant/index.shtml
posted @
2005-12-07 14:33 surffish 阅读(571) |
评论 (1) |
编辑 收藏
本文以最新发布的Ant 1.5.1为例,介绍这款优秀的Build工具的安装配置、基本应用和一些高级话题。最新的Ant下载地址是
http://jakarta.apache.org/ant/ 。
Ant是一种基于Java的Build工具。理论上来说,它有些类似于C中的make,但比make优越。现在存在的大多数Build工具,如make、gnumake、nmake、jam等都存在这样或那样的不足,比如依赖于特定的平台、配置文件过于复杂或者对格式无法检查而容易出错等。与这些工具相比较,Ant的两个特性决定了它是一款优秀的Build工具:
1. 基于Java的实现。具有良好的跨平台性,同时可以通过增加新的Java类来扩展Ant的功能,而无需去了解不同平台上不同的脚本语言。
2.基于XML的配置文件。Ant以XML树来描述Target/Task的关系,文件结构清晰、易读易写,并且利用XML对格式的控制来避免由于配置文件的错误造成的Build操作失败。
安装与配置
Ant的安装非常简单,把从网上下载的jakarta-ant-1.5.1-bin.zip解开到一个目录下即可(以下假定安装在目录D:\jakarta-ant-1.5.1)。接下来需要进行环境变量配置:
SET ANT_HOME=D:\jakarta-ant-1.5.1 //注意是Ant的安装目录,不是bin子目录 SET PATH=%PATH%;%ANT_HOME%\bin; |
在配置环境变量之前,请确认已经正确设置了JAVA_HOME系统变量。输入ant命令,看到如下输出说明已成功安装了Ant工具:
Buildfile: build.xml does not exist! Build failed |
提示信息表明在当前目录不存在build.xml配置文件,但这本身已经说明Ant成功运行了。
快速入门
下面用一个最简单也是最经典的例子-HelloWorld来感受一下Ant吧。
//HelloWorld.java package com.sharetop.antdemo; public class HelloWorld { public static void main( String args[] ) { System.out.println("Hello world. "); } } |
要让Ant编译这个文件,首先需要编写一个Build配置文件。在一般情况下,这个文件被命名为build.xml。
<?xml version="1.0" encoding="UTF-8" ?> <project name="HelloWorld" default="run" basedir="." > <property name="src" value="src"/> <property name="dest" value="classes"/> <property name="hello_jar" value="hello.jar" /> <target name="init"> <mkdir dir="${dest}"/> </target> <target name="compile" depends="init"> <javac srcdir="${src}" destdir="${dest}"/> </target> <target name="build" depends="compile"> <jar jarfile="${hello_jar}" basedir="${dest}"/> </target> <target name="run" depends="build"> <java classname="com.sharetop.antdemo.HelloWorld" classpath="${hello_jar}"/> </target> </project> |
来看一下这个文件的内容,它描述了以下信息:工程的名字为HelloWorld,工程有四个target,分别是init、compil、build和run,缺省是run。compile只有一个任务javac,源文件位于src目录下,输出的类文件要放在classes目录下。build的任务是jar,生成的jar文件为hello.jar,它打包时以classes为根目录。而run则是执行这个HelloWorld类,用hello.jar作为classpath。这四个target之间有一个依赖关系,这种关系用depends来指定。即如果Target A依赖于Target B,那么在执行Target A之前会首先执行Target B。所以从下面运行缺省Target(run)的输出看,这四个Target的执行顺序是:init→compile→build→run。文件目录结构如图1所示。HelloWorld.java文件在src\com\sharetop\antdemo子目录下。
图1 ant_demo应用的目录结构
在命令行输入命令:ant,然后运行,可以看到如下输出:
如果配置文件名不是build.xml,比如是build_front.xml,那么,可以使用-buildfile命令参数指定:
G:\myDoc\ant_demo>ant -buildfile build_front.xml |
也可以单独执行指定的某个target,比如,只编译不打包执行,可以使用下面输入命令即可:
G:\myDoc\ant_demo>ant compile |
在相应的目录下会找到编译出的HelloWorld.class文件。
再看看上面的build.xml配置文件,文件开头定义了3个属性,分别指定了源文件输出路径、类文件输出路径和生成的Jar文件名,后面对这些路径的引用都通过一个${property name}来引用。所以,要注意这样一个原则“目录的定义与目录的引用应该分开”。
基本应用
建立工程的目录 一般要根据工程的实际情况来建立工程的目录结构。但是,有一些比较通用的组织形式可供参考,比如所有的jakarta项目都使用类似的目录结构。下面让我们来看一下这种目录结构的特点。
表1
目录 |
文件 |
bin |
公共的二进制文件,以及运行脚本 |
build |
临时创建的文件,如类文件等 |
dist |
目标输出文件,如生成Jar文件等。 |
doc/javadocs |
文档。 |
lib |
需要导出的Java包 |
src |
源文件 |
对于一个简单的工程,一般包括表1的几个目录。其中bin、lib、doc和src目录需要在CVS的控制之下。当然在这样的目录结构上,也可以做一些调整,例如,可以建立一个extra目录来放置需要发布的Jar文件、Inf文件及图像文件等。同样,如果开发Web应用可以建立一个Web目录放置JSP、HTML等文件。
如果我们开发的是一个比较复杂的项目,包括多个子项目,并且各个子项目是由不同的开发人员来完成的,那么要如何来设计它的目录结构?首先有一点是需要确定的,不同的子项目应该拥有不同的Build文件,并且整个项目也应该有一个总的Build文件。可以通过Ant任务或是AntCall任务调用子项目的Build文件,如下例:
<target name="core" depends="init"> <ant dir="components" target="core"/> <ant dir="waf/src" target="core"/> <ant dir="apps" target="core"/> </target> |
在各个子项目的耦合不是非常紧密的情况下,各个子项目应该有各自独立的目录结构,也就是说它们可以有自己的src、doc、build、dist等目录及自己的build.xml文件,但是可以共享lib和bin目录。而对于那些耦合紧密的子项目,则推荐使用同一个src目录,但是不同的子项目有不同的子目录,各个子项目的build.xml文件可以放在根目录下,也可以移到各个子项目的目录下。
编写Build文件 要用好Ant工具,关键是要编写一个build.xml文件。要编写出一个结构良好、灵活可扩展的Build文件,有两个问题要考虑,一是了解Build文件的基本结构,二是了解Ant定义的大量任务。
Ant的Build文件是一个标准的XML文件,它包含一个根节点Project,每个Project定义了至少一个或多个Target,每个Target又是一系列Task的集合。它们之间的关系如图2所示。
图2 build.xml文件的结构
每个Task是一段可被执行的代码,比如,前例中的javac、jar就是两个最常用的Task。Ant定义了大量的核心Task,我们要考虑的第二个问题正是如何去掌握这大量的Task。其实唯一的方法就是边学习边实践,这方面最好的参考就是官方的Ant使用手册。
外部文件的使用 使用外部的Property文件可以保存一些预设置的公共属性变量。这些属性可以在多个不同的Build文件中使用。
可以将一个外部的XML文件导入Build文件中,这样多个项目的开发者可以通过引用来共享一些代码,同样,这也有助于Build文件的重用,示例代码如下所示:
<!DOCTYPE project [ <!ENTITY share-variable SYSTEM "file:../share-variable.xml"> <!ENTITY build-share SYSTEM "file:../build-share.xml"> ]> <project name="main" default="complie" basedir="."> &share-variable; &build-share; ... ... |
在J2EE项目中的应用
只要掌握了Ant的使用方法,在J2EE项目中的应用与在其它项目中的应用并没有太大的不同,但是仍有几点是需要注意的。
一是要清楚War和Jar文件的目录结构,主要是War的配置文件web.xml文件的位置和EJB的配置文件(ejb-jar.xml和weblogic-ejb-jar.xml等)的位置,在调用Jar任务打包文件时一定要记得把它们也包含进来。一般在编译之前就要注意把这些需打包的文件拷入相应目录下。二是在J2EE项目中可能会涉及到一些特殊的任务,比如在Weblogic中会调用ejbc预编译EJB的代码存根,或者需要在Ant中同时发布Jar到相应的服务器中等。可以用两种途径实现这些任务,一是扩展Ant任务实现这些任务,二是直接用Java任务来执行这些命令。下面是打包、发布一个EJB的build.xml配置文件片断,代码如下:
<target name="deploy_HelloEJB" depends="compile"> <delete dir="${temp}/ejb_make"/> <!-- 首先删除临时目录 --> <delete file="${temp}/helloEJB.jar"/> <!-- 删除WebLogic域中老版本的EJB --> <delete file="${weblogic.deploy.dest}/helloEJB.jar"/> <!-- 创建META-INF目录,放置ejb-jar.xml和weblogic-ejb-jar.xml --> <mkdir dir="${temp}/ejb_make/META-INF"/> <!-- 拷贝ejb-jar.xml和weblogic-ejb-jar.xml 到临时目录--> <copy todir="${temp}/ejb_make/META-INF"> <fileset dir="etc/baseinfo"> <include name="*.xml"/> </fileset> </copy> <!-- 拷贝所有的helloEJB类到临时目录 --> <copy todir="${temp}/ejb_make/"> <fileset dir="${dest.classes}/"> <!-- dest.classes是输出的类文件目录 --> <include name="${dest.classes}/helloEJB/**"/> </fileset> </copy> <!-- 将所有这些文件打包成helloEJB.jar --> <jar jarfile="${temp}/helloEJB.jar" basedir="${temp}/ejb_make"/> <!-- 进行weblogic.ejbc编译 --> <java classpath="${wl_cp}" classname="weblogic.ejbc" fork="yes" > <classpath> <fileset dir="lib"> <include name="*.jar" /> </fileset> </classpath> <arg value="${temp}/helloEJB.jar" /> <arg value="${temp}/helloEJB_deploy.jar" /> </java> <!-- 拷贝/发布到WebLogic的{DOMAIN}\applications目录 --> <copy file="${temp}/helloEJB_deploy.jar" todir="${weblogic.deploy.dest}"/> </target> |
用Ant配合JUnit实现单元测试
Ant 提供了JUnit任务,可以执行单元测试代码。如何使用JUnit,以及如何编写测试用例(TestCase),感兴趣的读者可以参阅JUnit的相关文档。在Ant中使用JUnit的方法非常简单,首先需要把junit.jar拷入ANT_HOME\lib下,确认在这个目录下有optional.jar,因为JUnit是Ant的扩展任务,需要引用这个扩展包。然后就是在Build文件中加入JUnit的任务,代码如下:
<target name="run" depends="client"> <junit printsummary="yes" fork="yes" haltonfailure="yes"> <classpath> <pathelement location="client.jar" /> </classpath> <formatter type="plain" /> <test name="com.sharetop.antdemo.HelloWorldTest" /> </junit> </target> |
高级话题
为Ant开发扩展任务 为Ant实现扩展任务其实是非常容易的,只需按照以下几个步骤即可:
1. 创建一个Java类继承org.apache.tools.ant.Task类;
2. 对每个属性实现set方法。Ant会根据需要自动完成类型转换;
3. 如果扩展的任务需要嵌套其它的Task,那么这个Java类必需实现接口org.apache.tools.ant.TaskContainer;
4. 如果扩展的任务要支持Text,需要增加一个方法void addText(String);
5. 对每个嵌套的元素,实现create、add 或 addConfigured 方法;
6. 实现public void execute方法;
7. 在build.xml文件中使用 <taskdef> 来引用自定义的Task。
下面以一个简单的例子来说明如何为Ant增加一个hello任务,它可以连续打印多条信息,打印的次数由属性count指定,而打印的内容则由它内嵌的一个helloinfo任务的message属性指定,看上去这非常类似JSP中自定义标签的一些概念,实现代码如下:
//HelloInfoTask.java package com.sharetop.antdemo; import org.apache.tools.ant.*; public class HelloInfoTask { private String msg; public void execute() throws BuildException { System.out.println(msg); } public void setMessage(String msg) { this.msg = msg; } } |
下面是外部Task类的代码:
//HelloTask.java package com.sharetop.antdemo; import org.apache.tools.ant.*; public class HelloTask extends Task implements org.apache.tools.ant.TaskContainer { private Task info; private int count; public void execute() throws BuildException { for(int i=0;i<count;i++) info.execute(); } public void setCount(int c){ this.count=c; } public void addTask(Task t){ this.info=t; } } |
实现了这两个Task,在build.xml文件中定义它的task name,就可以在Target中执行它了。如果你不想使用 <taskdef> 标签来定义Task,也可以通过修改default.properties文件来实现引入新Task,这个文件位于org.apache.tools.ant.taskdefs 包里。下例是一个使用 标签来引入新Task的Build文件部分:
<target name="hello" depends="client"> <taskdef name="hello" classname="com.sharetop.antdemo.HelloTask" classpath="client.jar"/> <taskdef name="helloinfo" classname="com.sharetop.antdemo.HelloInfoTask" classpath="client.jar"/> <hello count="3" > <helloinfo message="hello world" /> </hello> </target> |
在自己的程序中调用Ant
Ant的任务其实就是一段功能代码。Ant内置的大量任务对于我们开发Java应用具有非常大的意义,为什么我们不能直接使用呢?
因为尽管在程序中调用Ant的任务并不复杂,而且我们知道Ant的任务其实都是一些Java类,调用的方法无非就是执行这些类而已,不过在执行之前需要对它做一些初始化的工作,所以我们需要引用一个Task类的子类来实现这个功能,比如如下代码:
package com.sharetop.antdemo; import org.apache.tools.ant.*; import org.apache.tools.ant.taskdefs.*; import java.io.File; public class RunAntTask { public RunAntTask() { } public static void main(String args[]){ AntJAR j = new AntJAR(); j.setBasedir(new File("./classes")); j.setJarfile(new File("aaa.jar")); j.execute(); } } final class AntJAR extends Jar { public AntJAR() { project = new Project(); project.init(); taskType = "jar"; taskName = "jar"; } } |
注意AntJAR类的构造方法,先创建了Project并初始化它,这是直接调用Task的必需条件。
如果要在自己的程序中执行Ant,需要了解的是Ant定义的几个BuildEvent,它包括:
◆ Build started
◆ Build finished
◆ Target started
◆ Target finished
◆ Task started
◆ Task finished
◆ Message logged
我们需要做的是实现BuildListener接口来处理各种事件,而执行Ant的方法与上面给的例子非常类似,以实际开发的AntBuilder软件的部分代码为例:
public void buildTarget(String targetName,String buildFileName) { try { Project p = new Project(); p.init(); File f = new File(buildFileName); p.setUserProperty("ant.file",f.getAbsolutePath()); ProjectHelper.configureProject(p,f); p.addBuildListener(this); if( targetName==null ) p.executeTarget(p.getDefaultTarget()); else p.executeTarget(targetName); } catch (Exception ex) { jTextArea1.append(ex.getMessage()); } } |
创建Project并初始化,设置它的配置文件(build.xml),执行它缺省的或指定的Target,然后在实现了BuildListenser接口的监听器类中对你感兴趣的事件作处理,代码如下:
public void buildStarted(BuildEvent event){ /* nothing*/ } public void buildFinished(BuildEvent event) { /* nothing*/ } public void targetStarted(BuildEvent event) { this.jTextArea1.append(event.getTarget().getName()+": \n\r"); } public void targetFinished(BuildEvent event) {/* nothing*/ } public void taskStarted(BuildEvent event) {/* nothing*/ } public void taskFinished(BuildEvent event) { /* nothing*/ } public void messageLogged(BuildEvent event) { int prior = event.getPriority(); switch(prior){ case Project.MSG_ERR : this.jTextArea1.append("["+event.getTask().getTaskName()+"]Err:" +event.getMessage()); break; case Project.MSG_INFO: this.jTextArea1.append("["+event.getTask().getTaskName()+"]"+event.getMessage ()); break; case Project.MSG_WARN: this.jTextArea1.append("["+event.getTask().getTaskName()+"]" +event.getMessage()); break; case Project.MSG_VERBOSE: this.jTextArea1.append(event.getMessage()); break; } } |
Build.xml文件的写法每个公司都有不同,这里没有太大的参考价值,所以略去。(全文完)
posted @
2005-12-07 14:29 surffish 阅读(1645) |
评论 (0) |
编辑 收藏
//服务器代码
/********************************Main******************************/
import java.io.*;
import java.net.*;
import java.util.*;
public class ServerMain{
public static Vector socketVector=new Vector();
public static void main(String[] args) throws IOException{
System.out.println("服务器启动........");
ServerSocket s = new ServerSocket(5000);
while(true){
Socket soc = s.accept();
SocketThread st=new SocketThread(soc);
socketVector.addElement(st);
st.start();
}
}
public static void sendEveryone(String msg){
Object object=null;
int len=socketVector.size();
for(int i=0;i<len;i++){
try {
object=socketVector.elementAt(i);
SocketThread st = (SocketThread)object;
st.sender.send(msg);
}
catch (Exception ex) {
socketVector.removeElement(object);
}
}
}
public static void removeObject(Object object){
socketVector.removeElement(object);
}
public static void removeObject(Sender sender) throws Exception{
int len=socketVector.size();
for(int i=0;i<len;i++){
Object object=socketVector.elementAt(i);
SocketThread st = (SocketThread)object;
if(st.sender==sender)
socketVector.removeElement(object);
}
}
}
/*********************************SocketThread **********************/
import java.io.*;
import java.net.*;
import java.util.*;
public class SocketThread implements Runnable{
public Socket socke;
public DataInputStream dis;
public DataOutputStream dos;
public Sender sender;
private boolean stop;
Calendar date;// = Calendar.getInstance(TimeZone.getTimeZone("Asia/ShangHai"));
public SocketThread(Socket sok){
socke=sok;
}
public void start(){
Thread t=new Thread(this);
t.start();
}
public void run() {
try {
socke.setKeepAlive(true);
dis = new DataInputStream(socke.getInputStream());
dos = new DataOutputStream(socke.getOutputStream());
sender = new Sender(dos);
while (true) {
StringBuffer sb = new StringBuffer();
char c;
while (((c = dis.readChar()) != '\n') && (c != -1)) {
sb.append(c);
}
if (c == -1) {
break;
}
date = Calendar.getInstance(TimeZone.getTimeZone("Asia/ShangHai"));
String ljnTime="("+date.get(date.YEAR)+"/"+(date.get(date.MONTH)+1)+"/"+date.get(date.DATE)+" "+date.get(date.HOUR_OF_DAY)+":"+date.get(date.MINUTE)+":"+date.get(date.SECOND)+")";
String acceptMsg=sb.toString();
System.out.println(ljnTime+acceptMsg);
ServerMain.sendEveryone(acceptMsg);
}
stop();
ServerMain.removeObject(this);
} catch (IOException ioe) {
} catch (Exception e) {
e.printStackTrace();
}
}
public void stop() {
try {
stop = true;
if(sender!=null){
sender.stop1();
}
if (dis != null) {
dis.close();
}
if (dos != null) {
dos.close();
}
if (socke != null) {
socke.close();
}
} catch (IOException ioe) {
}
}
}
/*********************************************Sender**************************/
import java.io.*;
public class Sender extends Thread {
private DataOutputStream dos;
private String message;
public Sender(DataOutputStream os) {
this.dos = os;
start();
}
public synchronized void send(String msg) {
message = msg;
notify();
}
public synchronized void run() {
while (true) {
if (message == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
if (message == null) {
break;
}
try {
dos.writeChars(message);
dos.writeChars("\r\n");
} catch (IOException ioe) {
try {
ServerMain.removeObject(this);
}
catch (Exception ex) {
}
}
message = null;
}
}
public synchronized void stop1() {
message = null;
notify();
}
}
//下面为手机客户端代码
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public class ChatClientMIDlet extends MIDlet {
private static ChatClientMIDlet instance;
public static ChatForm displayable = new ChatForm();
/** Constructor */
public ChatClientMIDlet() {
instance = this;
}
/** Main method */
public void startApp() {
Display.getDisplay(this).setCurrent(displayable);
}
/** Handle pausing the MIDlet */
public void pauseApp() {
}
/** Handle destroying the MIDlet */
public void destroyApp(boolean unconditional) {
}
public static void setCurrent(Displayable s){
Display.getDisplay(instance).setCurrent(s);
}
/** Quit the MIDlet */
public static void quitApp() {
instance.destroyApp(true);
instance.notifyDestroyed();
instance = null;
}
}
/********************************************************ChatForm ************************/
import com.nec.io.*;
import javax.microedition.lcdui.*;
import javax.microedition.io.*;
import java.io.*;
public class ChatForm extends Form implements CommandListener,Runnable {
public Form mainForm=new Form("聊天室");
public Command enter=new Command("进入",Command.OK,0);
public Command send=new Command("发送",Command.OK,1);
public Command exit=new Command("退出",Command.EXIT,0);
public DataInputStream dis;
public DataOutputStream dos;
public SocketConnection sc;
public Sender sender;
public boolean stop;
public TextField textField=new TextField("请输入昵称:", null, 10, TextField.ANY);
public TextField info=new TextField("请输入消息:", null,255, TextField.ANY);
public ChoiceGroup choiceGroup=new ChoiceGroup(null,ChoiceGroup.EXCLUSIVE);
public String MyName="游客";
public boolean isCurrent=false;;
public ChatForm() {
super("我的聊天室");
append(textField);
addCommand(enter);
mainForm.append(info);
mainForm.append(choiceGroup);
choiceGroup.append("www.modia.cn",null);
setCommandListener(this);
mainForm.addCommand(send);
mainForm.addCommand(exit);
mainForm.setCommandListener(this);
}
public void commandAction(Command c, Displayable dis) {
if(c==enter){
if(textField.getString().length()==0){
Alert alert=new Alert("警告","昵称不能为空!", null, AlertType.WARNING) ;
alert.setTimeout(3000);
ChatClientMIDlet.setCurrent(alert);
return;
}else
{
MyName = textField.getString();
append("正在进入......");
}
Thread t=new Thread(this);
t.start();
}else if(c==send){
if(info.getString().length()==0){
Alert alert=new Alert("警告","消息内容不能为空!", null, AlertType.WARNING) ;
alert.setTimeout(3000);
ChatClientMIDlet.setCurrent(alert);
return;
}
sender.send(MyName+"说:"+info.getString());
info.setString("");
}
else{
stop();
ChatClientMIDlet.quitApp();
}
}
public void run() {
try {
sc = (SocketConnection) Connector.open("socket://127.0.0.1:5000");
sc.setSocketOption(SocketConnection.LINGER, 5);
dis = new DataInputStream(sc.openInputStream());
dos = new DataOutputStream(sc.openOutputStream());
sender = new Sender(dos);
sender.send("欢迎"+MyName+"进入房间");
while (true) {
if(stop) break;
StringBuffer sb = new StringBuffer();
char c;
while (((c = dis.readChar()) != '\n') && (c != -1)) {
sb.append(c);
}
if (c == -1) break;
if(!isCurrent){
ChatClientMIDlet.setCurrent(mainForm);
isCurrent=true;
}
String msg=sb.toString();
msg=msg.substring(0,msg.length()-2);
choiceGroup.insert(0,msg,null);
choiceGroup.setSelectedIndex(0,true);
}
stop();
} catch (ConnectionNotFoundException cnfe) {
} catch (IOException ioe) {
if (!stop) {
ioe.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void stop() {
try {
stop = true;
if (sender != null) {
sender.stop1();
}
if (dis != null) {
dis.close();
}
if (dos != null) {
dos.close();
}
if (sc != null) {
sc.close();
}
} catch (IOException ioe) {
}
}
}
/**************************Sender*********************************/
同上
posted @
2005-11-22 12:54 surffish 阅读(1070) |
评论 (2) |
编辑 收藏
//怎样获取一个文件夹下中的所有文件File dir = new File("fileDirectory");
String[] filel= dir.list();
//oracle启动方法
--启动数据库
su - oracle
sqlplus /nolog
connect /as sysdba
startup
exit
--监听
lsnrctl
start
posted @
2005-11-21 09:57 surffish 阅读(226) |
评论 (0) |
编辑 收藏
telnet 10.194.132.1
ftp 192.168.10.1
ls
vi
i
esc
:wq
ctrl + c
cp
posted @
2005-11-10 15:23 surffish 阅读(282) |
评论 (0) |
编辑 收藏
在Weblogic 8.1 sp1 上调试《精通EJB 2》源代码
----连接池及数据源ejbPool的配置.
作者: naxin9999@163.com
--------------------------------------------------------------------------------
一. 首先在mysql数据库中新建一个me2库,再建一个sa用户(密码:222222).授予权限.再提交源代码中的EJB.sql文件 ( 在EJB.sql文件头加上use me2; )。
具体如下:
d:\mysql –u root –p
mysql> create database me2;
mysql> GRANT ALL PRIVILEGES ON me2.* TO sa@'localhost' IDENTIFIED BY '222222' WITH GRANT OPTION;
mysql> quit
d:\ mysql –u sa –p < EJB.sql
password: (输入222222)
完成.
二. 下面开始创建DS:
a. 把mysql的驱动程序mm.mysql-2.0.14-bin.jar复制到 D:\bea\weblogic81\server\lib 目录下.
b. 修改D:\bea\user_projects\domains\mydomain 目录下的startweblogic.cmd文件中的 CLASSPATH设置,加上%WL_HOME%\server\lib\mm.mysql-2.0.14-bin.jar;
c. 起动weblogic,登陆 http://localhost:7001/console
--------------------------------------------------------------------------------
1.先建个pool (下图)
--------------------------------------------------------------------------------
2. (下图)
--------------------------------------------------------------------------------
3. (下图)
--------------------------------------------------------------------------------
4. (下图)
--------------------------------------------------------------------------------
5. (下图)
--------------------------------------------------------------------------------
6. (下图)
--------------------------------------------------------------------------------
7. 再建JDBC DS (下图)
--------------------------------------------------------------------------------
8. (下图)
--------------------------------------------------------------------------------
9. (下图)
--------------------------------------------------------------------------------
10(下图)
--------------------------------------------------------------------------------
11. (下图)
--------------------------------------------------------------------------------
12. 部署你的EJB,可以进行测试了(下图)
--------------------------------------------------------------------------------
13. 运行cmp 2.0的例子. Client输出:
14. Console 上输出
1. 在http://localhost:7001/console 找到 Configure a new JMS Topic..
2.如下图输入:
3.Create成功后.
4.部署你的消息驱动Bean
5.运行客户端,run_client.bat
6.weblogic控制台上的输出.
posted @
2005-10-31 15:35 surffish 阅读(269) |
评论 (0) |
编辑 收藏
/*$T BYBARP~1.H GC 1.137 09/13/05 08:37:52 */
#ifndef ByBarPrinterH
#define ByBarPrinterH
#include "vcl.h"
#define BPLA_OK 1000 //一切正常
///
#define BPLA_COMERROR 1001 //通讯错或者未联接打印机
#define BPLA_PARAERROR 1002 //参数错误
#define BPLA_FILEOPENERROR 1003 //文件打开错误
#define BPLA_FILEREADERROR 1004 //文件读错误
#define BPLA_FILEWRITEERROR 1005 //文件写错误
#define BPLA_FILEERROR 1006 //文件不合要求
#define BPLA_NUMBEROVER 1007 //指定的接收信息数量过大
#define BPLA_IMAGETYPEERROR 1008 //图象文件格式不正确
#define BPLA_PAPERSHORT 1009 //缺纸
#define BPLA_RIBBIONSHORT 1010 //缺色带
#define BPLA_BUSY 1011 //解释器忙
#define BPLA_PAUSE 1012 //暂停
///中
#define BPLA_HEADHEAT 1013 //打印头过热
#define BPLA_HEADOVER 1014 //打印头抬起
#define BPLA_CUT 1015 //切刀错
#define BPLA_READERROR 1016 //read error
//打开端口
typedef int (__stdcall *mBY_Open_Port) (int devtype, int porttype, char *opencode, int codelength, int baudrate);
//打印
typedef int (__stdcall *mBY_Print_Label)
(
int LabelType,
const char *JiDaJu,
const char *FenFaJu,
const char *TiaoMaXinXi,
const char *HaoMa,
const char *JianShu,
const char *ZhongLiang,
const char *ZhuanKouJuDaiHao,
const char *XingHao,
const char *BeiZhu,
const char *BiaoPaiBianHao,
const char *ZhongLeiJianCheng,
const char *BenZhuan,
int Pieces
);
//查询状态
typedef int (__stdcall *mBY_Query_Status) ();
//关闭端口
typedef int (__stdcall *mBY_Close_Port) (char *closecode, long codelength);
class PACKAGE CByBarPrinter
{
public:
long OpenPort(long nPcIndex, long nPortIndex, long baudrate);
void ClosePort(void);
long PrintLabel
(
long LabelType,
String JiDaJu,
String FenFaJu,
String TiaoMaXinXi,
String HaoMa,
String JianShu,
String ZhongLiang,
String ZhuanKouJuDaiHao,
String XingHao,
String BeiZhu,
String BiaoPaiBianHao,
String ZhongLeiJianCheng,
String BenZhuan,
long Pieces
);
CByBarPrinter(void);
long GetStatus(void);
private:
HINSTANCE m_hDll;
bool LoadLib(void);
};
#endif
/*$T BYBARP~1.CPP GC 1.137 09/13/05 08:37:52 */
#pragma hdrstop
#include "ByBarPrinter.h"
/* ---------------------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------------------- */
CByBarPrinter::CByBarPrinter(void)
{
m_hDll = NULL;
}
/* ---------------------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------------------- */
bool CByBarPrinter::LoadLib(void)
{
if(m_hDll == NULL) m_hDll = ::LoadLibrary("byprndll.dll");
if(m_hDll == NULL)
return false;
else
return true;
}
/* ---------------------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------------------- */
long CByBarPrinter::OpenPort(long nPcIndex, long nPortIndex, long baudrate)
{
long retCode;
if(!LoadLib())
{
retCode = -2;
return retCode;
}
mBY_Open_Port BY_Open_Port;
BY_Open_Port = (mBY_Open_Port) GetProcAddress(m_hDll, "BY_Open_Port");
if(BY_Open_Port == NULL)
{
retCode = -1;
}
else
{
String szCom;
szCom = "COM";
szCom += IntToStr(nPortIndex);
int nLen = szCom.Length();
retCode = BY_Open_Port(nPcIndex, nPortIndex, szCom.c_str(), nLen, 19200);
}
return retCode;
}
/* ---------------------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------------------- */
void CByBarPrinter::ClosePort(void)
{
if(!LoadLib()) return;
mBY_Close_Port BY_Close_Port;
BY_Close_Port = (mBY_Close_Port) GetProcAddress(m_hDll, "BY_Close_Port");
char CloseCode[50];
int CodeLength = 0;
memset(CloseCode, 0, 50);
BY_Close_Port(CloseCode, CodeLength);
//FreeLibrary(m_hDll);
delete[] CloseCode;
return;
}
/* ---------------------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------------------- */
long CByBarPrinter::PrintLabel
(
long LabelType,
String JiDaJu,
String FenFaJu,
String TiaoMaXinXi,
String HaoMa,
String JianShu,
String ZhongLiang,
String ZhuanKouJuDaiHao,
String XingHao,
String BeiZhu,
String BiaoPaiBianHao,
String ZhongLeiJianCheng,
String BenZhuan,
long Pieces
)
{
long retCode;
if(!LoadLib())
{
retCode = -2;
return retCode;
}
//USES_CONVERSION;
mBY_Print_Label BY_Print_Label;
BY_Print_Label = (mBY_Print_Label) GetProcAddress(m_hDll, "BY_Print_Label");
if(BY_Print_Label == NULL)
{
retCode = -1;
return S_OK;
}
retCode = BY_Print_Label
(
LabelType,
JiDaJu.c_str(),
FenFaJu.c_str(),
TiaoMaXinXi.c_str(),
HaoMa.c_str(),
JianShu.c_str(),
ZhongLiang.c_str(),
ZhuanKouJuDaiHao.c_str(),
XingHao.c_str(),
BeiZhu.c_str(),
BiaoPaiBianHao.c_str(),
ZhongLeiJianCheng.c_str(),
BenZhuan.c_str(),
Pieces
);
return retCode;
}
/* ---------------------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------------------- */
long CByBarPrinter::GetStatus(void)
{
long state;
if(!LoadLib())
{
state = -1;
return state;
}
mBY_Query_Status BY_Query_Status;
BY_Query_Status = (mBY_Query_Status) GetProcAddress(m_hDll, "BY_Query_Status");
if(BY_Query_Status == NULL)
state = -1;
else
state = BY_Query_Status();
return state;
}
#pragma package(smart_init)
posted @
2005-10-31 13:54 surffish 阅读(243) |
评论 (0) |
编辑 收藏
substr(string,1,8)
1.
//将一个表中符合条件的记录批量更新到另外一张表。(假设表名为:toffice,tofficetemp)
update toffice a
set a.office_code = (select b.office_code from tofficetemp b where a.jgdm = b.jgdm)
where a.jgdm in (select jgdm from tofficetemp)
update tofficenexas set isuse = '0' where rowid in
(select rowid from tofficenexas a where
rowid !=(select max(rowid) from tofficenexas b where a.office_id = b.office_id and
a.father_office_id = b.father_office_id and a.big_kind_id =b.big_kind_id))
2
//查询某个时间段的值
select * from tdespatch t
where t.out_time between to_date('2005-10-18 15:47:42','yyyy-mm-dd hh24:mi:ss')
and to_date('2005-10-20 10:47:42','yyyy-mm-dd hh24:mi:ss')
3。
//复制表(只复制结构,源表名:a 新表名:b)
SQL: select * into b from a where 1<>1
4。
//拷贝表(拷贝数据,源表名:a 目标表名:b)
SQL: insert into b(a, b, c) select d,e,f from b;
5。
说明:显示文章、提交人和最后回复时间
SQL: select a.title,a.username,b.adddate from table a,(select max(adddate) adddate from table where table.title=a.title) b
说明:外连接查询(表名1:a 表名2:b)
SQL: select a.a, a.b, a.c, b.c, b.d, b.f from a LEFT OUT JOIN b ON a.a = b.c
说明:日程安排提前五分钟提醒
SQL: select * from 日程安排 where datediff('minute',f开始时间,getdate())>5
说明:两张关联表,删除主表中已经在副表中没有的信息
SQL:
delete from info where not exists ( select * from infobz where info.infid=infobz.infid )
说明:--
SQL:
SELECT A.NUM, A.NAME, B.UPD_DATE, B.PREV_UPD_DATE
FROM TABLE1,
(SELECT X.NUM, X.UPD_DATE, Y.UPD_DATE PREV_UPD_DATE
FROM (SELECT NUM, UPD_DATE, INBOUND_QTY, STOCK_ONHAND
FROM TABLE2
WHERE TO_CHAR(UPD_DATE,'YYYY/MM') = TO_CHAR(SYSDATE, 'YYYY/MM')) X,
(SELECT NUM, UPD_DATE, STOCK_ONHAND
FROM TABLE2
WHERE TO_CHAR(UPD_DATE,'YYYY/MM') =
TO_CHAR(TO_DATE(TO_CHAR(SYSDATE, 'YYYY/MM') ¦¦ '/01','YYYY/MM/DD') - 1, 'YYYY/MM') ) Y,
WHERE X.NUM = Y.NUM (+)
AND X.INBOUND_QTY + NVL(Y.STOCK_ONHAND,0) <> X.STOCK_ONHAND ) B
WHERE A.NUM = B.NUM
说明:--
SQL:
select * from studentinfo where not exists(select * from student where studentinfo.id=student.id) and 系名称='"&strdepartmentname&"' and 专业名称='"&strprofessionname&"' order by 性别,生源地,高考总成绩
说明:
从数据库中去一年的各单位电话费统计(电话费定额贺电化肥清单两个表来源)
SQL:
SELECT a.userper, a.tel, a.standfee, TO_CHAR(a.telfeedate, 'yyyy') AS telyear,
SUM(decode(TO_CHAR(a.telfeedate, 'mm'), '01', a.factration)) AS JAN,
SUM(decode(TO_CHAR(a.telfeedate, 'mm'), '02', a.factration)) AS FRI,
SUM(decode(TO_CHAR(a.telfeedate, 'mm'), '03', a.factration)) AS MAR,
SUM(decode(TO_CHAR(a.telfeedate, 'mm'), '04', a.factration)) AS APR,
SUM(decode(TO_CHAR(a.telfeedate, 'mm'), '05', a.factration)) AS MAY,
SUM(decode(TO_CHAR(a.telfeedate, 'mm'), '06', a.factration)) AS JUE,
SUM(decode(TO_CHAR(a.telfeedate, 'mm'), '07', a.factration)) AS JUL,
SUM(decode(TO_CHAR(a.telfeedate, 'mm'), '08', a.factration)) AS AGU,
SUM(decode(TO_CHAR(a.telfeedate, 'mm'), '09', a.factration)) AS SEP,
SUM(decode(TO_CHAR(a.telfeedate, 'mm'), '10', a.factration)) AS OCT,
SUM(decode(TO_CHAR(a.telfeedate, 'mm'), '11', a.factration)) AS NOV,
SUM(decode(TO_CHAR(a.telfeedate, 'mm'), '12', a.factration)) AS DEC
FROM (SELECT a.userper, a.tel, a.standfee, b.telfeedate, b.factration
FROM TELFEESTAND a, TELFEE b
WHERE a.tel = b.telfax) a
GROUP BY a.userper, a.tel, a.standfee, TO_CHAR(a.telfeedate, 'yyyy')
说明:四表联查问题:
SQL: select * from a left inner join b on a.a=b.b right inner join c on a.a=c.c inner join d on a.a=d.d where .....
说明:得到表中最小的未使用的ID号
SQL:
SELECT (CASE WHEN EXISTS(SELECT * FROM Handle b WHERE b.HandleID = 1) THEN MIN(HandleID) + 1 ELSE 1 END) as HandleID
FROM Handle
WHERE NOT HandleID IN (SELECT a.HandleID - 1 FROM Handle a)
6 根据父表 只取子表最新的一条记录{
SQL> select * from testa ;
F1
----------
1
2
3
SQL> select * from testb ;
F1 F2
---------- -------------------
1 2006-04-10 14:56:41
1 2006-04-10 14:56:53
1 2006-04-10 14:57:00
2 2006-04-10 14:57:08
3 2006-04-10 14:57:19
3 2006-04-10 14:57:25
6 rows selected.
SQL> select testa.f1,v1.f1,v1.f2 from testa ,(select f1,f2,row_number() over (partition by f1 order
by f2 desc) rn from testb) v1 where testa.f1 = v1.f1 and v1.rn = 1 ;
F1 F1 F2
---------- ---------- -------------------
1 1 2006-04-10 14:57:00
2 2 2006-04-10 14:57:08
3 3 2006-04-10 14:57:25
------------------------------------------
7 查询语句的优化
select t.* from acc$ t
where
not exists (在此不使用not in 是为了性能上的考虑)
(select 'a' from crm$ a
where
a.客户名=t.用户名称
--a.客户号=t.用户编号
)
posted @
2005-10-30 09:42 surffish 阅读(274) |
评论 (0) |
编辑 收藏
作者:[本站编辑] 来源:[CSDN] 浏览:[
] |
|
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 } 三、类DBConnectionPool说明
该类在209至345行实现,它表示指向某个数据库的连接池。数据库由JDBC URL标识。一个JDBC URL由三部分组成:协议标识(总是jdbc),驱动程序标识(如 odbc、idb、oracle等),数据库标识(其格式依赖于驱动程序)。例如,jdbc:odbc:de mo,即是一个指向demo数据库的JDBC URL,而且访问该数据库要使用JDBC-ODBC驱动程序。每个连接池都有一个供客户程序使用的名字以及可选的用户帐号、密码、最大连接数限制。如果Web应用程序所支持的某些数据库操作可以被所有用户执行,而其它一些操作应由特别许可的用户执行,则可以为两类操作分别定义连接池,两个连接池使用相同的JDBC URL,但使用不同的帐号和密码。 类DBConnectionPool的建构函数需要上述所有数据作为其参数。如222至238行所示,这些数据被保存为它的实例变量: 如252至283行、285至305行所示, 客户程序可以使用DBConnectionPool类提供的两个方法获取可用连接。两者的共同之处在于:如连接池中存在可用连接,则直接返回,否则创建新的连接并返回。如果没有可用连接且已有连接总数等于最大限制 数,第一个方法将直接返回null,而第二个方法将等待直到有可用连接为止。 所有的可用连接对象均登记在名为freeConnections的向量(Vector)中。如果向量中有多于一个的连接,getConnection()总是选取第一个。同时,由于新的可用连接总是从尾部加入向量,从而使得数据库连接由于长时间闲置而被关闭的风险减低到最小程度。 第一个getConnection()在返回可用连接给客户程序之前,调用了isClosed()方法验证连接仍旧有效。如果该连接被关闭或触发异常,getConnection()递归地调用自己以尝试获取另外的可用连接。如果在向量freeConnections中不存在任何可用连 接,getConnection()方法检查是否已经指定最大连接数限制。如已经指定,则检查当前连接数是否已经到达极限。此处maxConn为0表示没有限制。如果没有指定最大连接数限制或当前连接数小于该值,该方法尝试创建新的连接。如创建成功,则增加已使用连接的计数并返回,否则返回空值。 如325至345行所示,创建新连接由newConnection()方法实现。创建过程与是否已经指定数据库帐号、密码有关。 JDBC的DriverManager类提供多个getConnection()方法,这些方法要用到JDBC URL与其它一些参数,如用户帐号和密码等。 DriverManager将使用指定的JDBC URL确定适合于目标数据库的驱动程序及建立连接。 在285至305行实现的第二个getConnection()方法需要一个以毫秒为单位的时间参数,该参数表示客户程序能够等待的最长时间。建立连接的具体操作仍旧由第一个getConnection()方法实现。 该方法执行时先将startTime初始化为当前时间。在while循环中尝试获得一个连接。如果失败,则以给定的时间值为参数调用wait()。wait()的返回可能是由于其它线程调用notify()或notifyAll(),也可能是由于预定时间已到。为找出wait()返回的真正原因,程序用当前时间减开始时间(startTime),如差值大于预定时间则返回空值,否则再次调用getConnection()。 把空闲的连接登记到连接池由240至250行的freeConnection()方法实现,它的参数为返回给连接池的连接对象。该对象被加入到freeConnections向量的末尾,然后减少已使用连接计数。调用notifyAll()是为了通知其它正在等待可用连接的线程。 许多Servlet引擎为实现安全关闭提供多种方法。数据库连接池需要知道该事件以保证所有连接能够正常关闭。DBConnectionManager类负协调整个关闭过程,但关闭连接池中所有连接的任务则由DBConnectionPool类负责。在307至323行实现的release()方法供DBConnectionManager调用。该方法遍历freeConnections向量并关闭所有连接,然后从向量中删除这些连接。
四、类DBConnectionManager 说明
该类只能创建一个实例,其它对象能够调用其静态方法(也称为类方法)获得该唯一实例的引用。如031至036行所示,DBConnectionManager类的建构函数是私有的,这是为了避免其它对象创建该类的实例。 DBConnectionManager类的客户程序可以调用getInstance()方法获得对该类唯一实例的引用。如018至029行所示,类的唯一实例在getInstance()方法第一次被调用期间创建,此后其引用就一直保存在静态变量instance中。每次调用getInstance() 都增加一个DBConnectionManager的客户程序计数。即,该计数代表引用DBConnectionManager唯一实例的客户程序总数,它将被用于控制连接池的关闭操作。 该类实例的初始化工作由146至168行之间的私有方法init()完成。其中 getResourceAsStream()方法用于定位并打开外部文件。外部文件的定位方法依赖于类装载器的实现。标准的本地类装载器查找操作总是开始于类文件所在路径,也能够搜索CLASSPATH中声明的路径。db.properties是一个属性文件,它包含定义连接池的键-值对。可供定义的公用属性如下:
drivers 以空格分隔的JDBC驱动程序类列表 logfile 日志文件的绝对路径
其它的属性和特定连接池相关,其属性名字前应加上连接池名字:
<poolname>.url 数据库的 JDBC URL <poolname>.maxconn 允许建立的最大连接数,0表示没有限制 <poolname>.user 用于该连接池的数据库帐号 <poolname>.password 相应的密码
其中url属性是必需的,而其它属性则是可选的。数据库帐号和密码必须合法。用于Windows平台的db.properties文件示例 如下:
drivers=sun.jdbc.odbc.JdbcOdbcDriver jdbc.idbDriver logfile=D:\\user\\src\\java\\DBConnectionManager\\log.txt
idb.url=jdbc:idb:c:\\local\\javawebserver1.1\\db\\db.prp idb.maxconn=2
access.url=jdbc:odbc:demo access.user=demo access.password=demopw
注意在Windows路径中的反斜杠必须输入2个,这是由于属性文件中的反斜杠同时也是一个转义字符。 init()方法在创建属性对象并读取db.properties文件之后,就开始检查logfile属性。如果属性文件中没有指定日志文件,则默认为当前目录下的DBConnectionManager.log文件。如日志文件无法使用,则向System.err输出日志记录。 装载和注册所有在drivers属性中指定的JDBC驱动程序由170至192行之间的loadDrivers()方法实现。该方法先用StringTokenizer将drivers属性值分割为对应于驱动程序名称的字符串,然后依次装载这些类并创建其实例,最后在 DriverManager中注册 该实例并把它加入到一个私有的向量drivers。向量drivers将用于关闭服务时从DriverManager取消所有JDBC 驱动程序的注册。 init()方法的最后一个任务是调用私有方法createPools()创建连接池对象。如109至142行所示,createPools()方法先创建所有属性名字的枚举对象(即Enumeration对象,该对象可以想象为一个元素系列,逐次调用其nextElement()方法将顺序返 回各元素),然后在其中搜索名字以“.url”结尾的属性。对于每一个符合条件的属性,先提取其连接池名字部分,进而读取所有属于该连接池的属性,最后创建连接池对象并把它保存在实例变量pools中。散列表(Hashtable类 )pools实现连接 池名字到连接池对象之间的映射,此处以连接池名字为键,连接池对象为值。 为便于客户程序从指定连接池获得可用连接或将连接返回给连接池,类DBConnectionManager提供了方法getConnection()和freeConnection()。所有这些方法都要求在参数中指定连接池名字,具体的连接获取或返回操作则调用对应的连接池对象完成。它们的实现分别在051至064行、066至080行、038至049行。 如082至107行所示,为实现连接池的安全关闭,DBConnectionManager提供了方法release()。在上面我们已经提到,所有DBConnectionManager的客户程序都应该调用静态方法getInstance()以获得该管理器的引用,此调用将增加客户程序计数。 客户程序在关闭时调用release()可以递减该计数。当最后一个客户程序调用release(),递减后的引用计数为0,就可以调用各个连接池的release()方法关闭所有连接了。管理类release()方法最后的任务是撤销所有JDBC驱动程序的注册。
五、Servlet使用连接池示例
Servlet API所定义的Servlet生命周期类如:
1) 创建并初始化Servlet(init()方法)。 2) 响应客户程序的服务请求(service()方法)。 3) Servlet终止运行,释放所有资源(destroy()方法)。
本例演示连接池应用,上述关键步骤中的相关操作为:
1) 在init(),用实例变量connMgr 保存调用DBConnectionManager.getInstance()所返回的引用。 2) 在service(),调用getConnection(),执行数据库操作,用freeConnection()将连接返回给连接池。 3) 在destroy(),调用release()关闭所有连接,释放所有资源。
示例程序清单如下:
import java.io.*; import java.sql.*; import javax.servlet.*; import javax.servlet.http.*; public class TestServlet extends HttpServlet { private DBConnectionManager connMgr;
public void init(ServletConfig conf) throws ServletException { super.init(conf); connMgr = DBConnectionManager.getInstance(); }
public void service(HttpServletRequest req, HttpServletResponse res) throws IOException {
res.setContentType("text/html"); PrintWriter out = res.getWriter(); Connection con = connMgr.getConnection("idb"); if (con == null) { out.println("不能获取数据库连接."); return; } ResultSet rs = null; ResultSetMetaData md = null; Statement stmt = null; try { stmt = con.createStatement(); rs = stmt.executeQuery("SELECT * FROM EMPLOYEE"); md = rs.getMetaData(); out.println("<H1>职工数据</H1>"); while (rs.next()) { out.println("<BR>"); for (int i = 1; i < md.getColumnCount(); i++) { out.print(rs.getString(i) + ", "); } } stmt.close(); rs.close(); } catch (SQLException e) { e.printStackTrace(out); } connMgr.freeConnection("idb", con); }
public void destroy() { connMgr.release(); super.destroy(); } } |
posted @
2005-10-28 09:50 surffish 阅读(299) |
评论 (0) |
编辑 收藏
exp airport/airport@ems file=airport2.dmp owner=airport log=exp2.log
posted @
2005-10-26 09:56 surffish 阅读(1182) |
评论 (0) |
编辑 收藏