随笔-16  评论-54  文章-0  trackbacks-0
  2006年7月24日
 

 

1      基本介绍

可以说CSV格式的文件经常碰到,何为CSV格式,CVS全称comma-separated values,就是典型的用逗号隔开的文件,比如下面这种文件格式

Name,company

zhangsan,ibm

lisi,oracle

这种就是典型的CSV格式文件。不过也可以扩展到其它符号隔开的字符,比如

Name#company

Zhangsan#ibm

Lisi#oracle

这种也算CSV格式

Java开源框架CVSReader提供了一个轻量级、简单方便的统一操作接口可用,下面具体讲解如何操作CVS格式

2      安装和使用

2.1下载

目前CSV reader的最新发布版本是1.8。我们可以从

http://opencsv.sourceforge.net/

上面下载到最新的csvreader包。

2.2安装

直接把jar包分别存放到开发工程的类路径下面即可使用。

3      读取CSV格式文件

3.1基本简介

首先,读取CSV格式的文件需要创建一个CSVReader,如下

CsvReader reader = new CsvReader(Reader r,  char c);

其中第一个参数为读取文件,第二个参数为分割符,比如“,”,或者“#

另外,也有其它几个参数,可以查阅API,比如

CsvReader reader = new CsvReader(InputStream r,  char c, Charset charset);等等

其次,一般需要读取头信息,如下:

reader.readHeaders();

String[] headers = reader.getHeaders();

读取了后,指针就会移动到下一行,也就是可以开始读取文件内容

假如,有多行的话,可以用一个循环套入,例如下面:

             while (reader.readRecord()) {

                    for (int i = 0; i < headers.length; i++) {

                           String value = reader.get(headers[i]);

                           System.out.print(value+" ");

                    }

                    System.out.println("");

             }

3.2综合例子

C盘下创建一个测试文件test.cvs,内容如下:

a#b#c

1#2#3

4#5#6

下面是解析代码:

      public static void main(String[] args) throws Exception {

CsvReader reader = new CsvReader(new FileReader("c://csv.txt"), '#');

             reader.readHeaders();

             String[] headers = reader.getHeaders();

             while (reader.readRecord()) {

                    for (int i = 0; i < headers.length; i++) {

                           String value = reader.get(headers[i]);

                           System.out.print(value+" ");

                    }

                    System.out.println("");

             }

      }

运行以上程序,可以看到输出

 1 2 3

 4 5 6

4      CSV格式文件

CSV格式文件也比较简单,写每一列只要直接调用

csvWriter.write()即可

另外,写完每行结束后,都要调用 csvWriter.endRecord();

表示结束一行

文件写完毕后,要记得刷新一下并关掉,如下:

       csvWriter.flush();

       csvWriter.close();

代码如下:

    publicstaticvoid main(String[] args) throws Exception {

       CsvWriter csvWriter = new CsvWriter(new FileWriter("c://test.text"), '#');

       csvWriter.write("name");

       csvWriter.write("company");

       csvWriter.endRecord();

       csvWriter.write("11");

       csvWriter.write("12");

       csvWriter.endRecord();

       csvWriter.write("21");

       csvWriter.write("22");

       csvWriter.flush();

       csvWriter.close();

   }

}

运行以上程序,可以看到C盘下面创建了一个文件

test.text

内容如下:

name#company

11#12

21#22

posted @ 2008-10-31 14:42 jspark 阅读(11787) | 评论 (3)编辑 收藏
     摘要: (本文档的全篇可以到博客下面的文件列表下载,地址下面)  http://www.blogjava.net/jspark/admin/Files.aspx 为了让尽快对jbossRules有一个感官的认识,下面先开发一个HelloWorld的程序。 建立一个java工程,目录如下:     如上所示,建立一个com包,然后在下面分别建立一个Sa...  阅读全文
posted @ 2008-10-28 15:54 jspark 阅读(1627) | 评论 (0)编辑 收藏
 

1      Java规则系统简介

在大型商业系统中,业务规则、商业逻辑等等都会比较复杂。而且在很多大型系统当中,很多业务规则、商业逻辑并不是一成不变的。甚至当系统进入生产阶段时,客户的业务规则、商业逻辑也会改变。某些系统要求甚至更高,要求能24小时不停机,并且能够实时修改商业规则。这就对商业系统提出了较大的挑战。如果将这些可变的规则直接编写到代码里面的话,业务规则一旦改变,就要修改代码。并由此带来编译、打包、发布等等问题。这对于生产系统来说是极不方便的。因此,如何考虑把一些可变的业务规则抽取到外面,使这些业务规则独立于程序代码。并最好是能够实时的修改业务规则,这样就可以做到不用打包编译发布等等。

值得庆幸的是现在出现了一些Java规则引擎(Rule Engine),专门解决以上所述的问题。利用它,我们就可以在应用系统中分离客户的商业决策逻辑和应用开发者的技术决策,并把这些商业规额则放在中心数据库或其他统一的地方,让它们能在运行时可以动态地管理和修改。

JbossRules是一个优秀的JAVA规则引擎,其前身是Drools3,后来被Jboss合并并改名为JbossRules

1.1基于规则的专家系统简介

人工智能是一个新兴的学科,它是想让计算机模拟人脑的思维和推理模式。人工智能分成如下几个主要的分学科:

知识表示

神经网络

基因算法

决策树

专家系统

等等几个学科

知识表示是人工智能中的一个基础领域,其目的是如何更好的在计算机当中描述已存在的事实。专家系统就是使用知识表示,来做规则推理,得出最后的结论来。

Java规则引擎起源于基于规则的专家系统,而基于规则的专家系统又是专家系统的其中一个分支。专家系统属于人工智能的范畴,它模仿人类的推理方式,使用试探性的方法进行推理,并使用人类能理解的术语解释和证明它的推理结论。为了更深入地了解Java规则引擎,下面简要地介绍基于规则的专家系统。RBES包括三部分:Rule Baseknowledge base)、Working Memoryfact base)和Inference Engine。它们的结构如下系统所示:

如上图所示,推理引擎包括三部分:模式匹配器(Pattern Matcher)、议程(Agenda)和执行引擎(Execution Engine)。推理引擎通过决定哪些规则满足事实或目标,并授予规则优先级,满足事实或目标的规则被加入议程。模式匹配器决定选择执行哪个规则,何时执行规则;议程管理模式匹配器挑选出来的规则的执行次序;执行引擎负责执行规则和其他动作。

和人类的思维相对应,推理引擎存在两者推理方式:演绎法(Forward-Chaining)和归纳法(Backward-Chaining)。演绎法从一个初始的事实出发,不断地应用规则得出结论(或执行指定的动作)。而归纳法则是根据假设,不断地寻找符合假设的事实。Rete算法是目前效率最高的一个Forward-Chaining推理算法,许多Java规则引擎都是基于Rete算法来进行推理计算的。

正向推理:

 

正向推理图形如下:

正向推理引擎的推理步骤如下:

将初始数据(fact)输入Working Memory

使用Pattern Matcher比较规则库(rule base)中的规则(rule)和数据(fact)。

如果执行规则存在冲突(conflict),即同时激活了多个规则,将冲突的规则放入冲突集合。

解决冲突,将激活的规则按顺序放入Agenda

使用执行引擎执行Agenda中的规则。重复步骤25,直到执行完毕所有Agenda中的规则。

直到得出最终的结果为止

反向推理:

 

反向推理是目标驱动的推理方式。从目标出发,找出所有能满足该目

标的子目标。这样一直推导下去,直到所有的子目标都已经满足为止。

1.2Java规则引擎

Java规则引擎是一种嵌入在Java程序中的组件,它的任务是把当前提交给引擎的Java数据对象与加载在引擎中的业务规则进行测试和比对,激活那些符合当前数据状态下的业务规则,根据业务规则中声明的执行逻辑,触发应用程序中对应的操作。

一般来说,一条规则的形式如下:

when

    <conditions>

then

    <actions>

也就是说,当conditions成立的话,就做下面的actions。其中actions可以为生成新的事实、或者做其他动作,比如,发送email通知、执行一些本地任务等等。

1.3    JAVA规则引擎的优点

声明式编程

声明式编程,规则引擎让我们直到“做什么”,而不用直到“怎么做”。我们只要把一系列规则表示出来后。具体的推理动作就交给规则引擎来处理。

逻辑和数据分开

将可变的业务逻辑和数据分开。虽然,这违背了面向对象原则。面向对象强调数据和业务逻辑耦合。但是,对于一些易变而复杂的业务规则。如果散步在程序的各个地方、各个层次。那么一旦业务规则更改的话,就会出现“牵一发而动全身”的局面。因此,将可变的业务逻辑独立出来管理,将有助于后面的业务变更。

性能

Rete算法的性能比较高。

知识集中表示

通过使用规则,我们把规则集中存放起来,从而使系统知识能够集中表示。

可读性

规则的可读性比较高。对于熟悉业务规则。但不会程序开发的业务专家,只要熟悉规则的标示,也可以编写和修改业务规则。

1.4    使用JAVA规则系统的场合

那么,在那些场合下适合应用JAVA规则系统呢?总而言之,可以用一句话来概括:当用传统的程序开发,无法得到一种优雅的解决方法的时候,就可以考虑使用规则系统。如下的一些场合:

用传统的代码开发比较复杂、繁琐

问题虽然不复杂,但是用传统的代码开发比较脆弱,也就是经常修改

没有优雅的算法

业务规则频繁改变

有很多业务专家、不懂技术开发

1.5    不适合使用JAVA规则系统场合

虽然规则系统看起来比较不错,但是并不是任何地方都可以使用规则系统。很多简单、固定的业务系统,可以不用使用规则系统。规则系统也不能用来作为标示重要的业务流程、不能用来作为工作流引擎。

有很多程序员把JAVA规则系统当成是一种动态修改配置。也就是把一部分代码逻辑抽取到外面,统一存放起来。这样,当一些配置修改的话,通过修改规则,就能修改代码的一部分逻辑。如果把JAVA规则仅仅用在这个场合下的话,可以考虑采用脚本引擎。比如BeanShellJEXLGroovy等等。

posted @ 2008-10-28 12:15 jspark 阅读(1556) | 评论 (1)编辑 收藏

grant {
    permission java.lang.RuntimePermission  
"loadLibrary.*";
    permission java.lang.RuntimePermission  
"queuePrintJob";
    permission java.lang.RuntimePermission  
"setContextClassLoader";
    permission java.lang.RuntimePermission  
"getProtectionDomain";
    permission java.lang.reflect.ReflectPermission 
"suppressAccessChecks";
    };

grant {
    permission java.util.PropertyPermission   
"*" ,  " read,write " ;

};  

     最近一个项目需要用到SUN ONE APPSERVER8.1,本人在WINDOWS SERVER 2003中安装,碰到一些问题,不过比较幸运的是都解决了,下面大概描述一下个人碰到的问题,期望能给别人带来帮助

   一、DNS服务器问题
    安装SUN ONE APPSERVER8.1必须要在服务器上安装,而且必须要将该服务器设置为DNS服务器。关于WINDOWS SERVER 2003
    如何设置DNS服务器,网上很多资料,可以查阅

   二、文件系统权限访问问题
 也许SUN ONE APPSERVER8.1服务器在文件访问方面控制比较严格,如果按照默认安装上去的系统。对于一些文件夹、文件读取是会有一些控制的。比如说,当将应用部署上去,然后访问应用,会抛出SecurityException。这是因为需要编译jsp页面,生成class文件,由于没有写权限,所以会出错。解决的方法是为SUN ONE APPSERVER增加文件访问权限。修改方法如下:
 找到安装路径,下面以本人的安装路径为例子:
 c\sunjes\ApplicationServer\domains\domain1\config
 该目录下面有一个文件叫server.policy,打开该页面,可以看到里面是一些关于文件访问权限的例子

    
//  Core server classes get all permissions by default

grant codeBase  " file:${com.sun.aas.installRoot}/lib/- "  {
    permission java.security.AllPermission;
};
    
    下面为文件路径增加访问权限,个人把整个c盘设置为可读可写,如下

    grant codeBase 
" file:c:/- "
 {
    permission java.security.AllPermission;
};

grant {
    permission java.io.FilePermission 
" c:/- " " read,write,execute,delete "
;

}; 


 编辑完毕,保存,重启服务器,OK,该问题解决。 :)

  三、其他几个权限问题:
         编辑以上问题后,重新自动,可能还会发现以下几个异常,比如 permission java.util.PropertyPermission   "*" ,  " read,write " ;
      因此,分别加上如下几个权限设置即可
   



  四、ORACLE10.2.0.1驱动问题
 本人部署的应用是spring+hb架构,里面用到blog/clob大字段处理,因此驱动程序用最新的驱动程序10g,版本为10.2.0.1。在部署到SUN ONE APPSERVER8.1时,也抛出类访问异常,异常信息是:oracle.sql is sealed。没办法,上网搜索了一下,发现有很多人也遇过这个情况。主要是oracle10g.jar里面的Meta-inf定义,增加了sealed属性。打开该文件MANIFEST.MF,内容如下:
 

    Manifest - Version:  1.0
Specification
- Title:    Oracle JDBC driver classes  for  use with JDK14
Created
- By:  1.4
.2_08 (Sun Microsystems Inc.)
sealed:
true

Implementation
- Title:   ojdbc14.jar
Specification
-
Vendor:   Oracle Corporation
Specification
- Version:  Oracle JDBC Driver version  -   " 10.2.0.1.0 "

Implementation
- Version: Oracle JDBC Driver version  -   " 10.2.0.1.0 "
Implementation
- Vendor:  Oracle Corporation
Implementation
- Time:    Wed Jun  22   18 : 55 : 48   2005

 关于sealed属性网上也有
 很多资料介绍,有兴趣的网友可以参阅一下。网上同行的解决方法是下载10g,低点的版本。本人的解决方法是修改一下里面的MANIFEST.MF文件,把sealed:true去掉即可。


 四、包版本不兼容。
 解决完以上几个问题后,重新启动,本以为万事大吉,很不幸运的是,再次抛出异常:
 ClassNotFoundException: org.hibernate.hql.ast.HqlToken。同样,上网搜索了一下,发现是hibernate的antlr.jar和SUN ONE APPSERVER的antlr.jar存在冲突。hibernate3.0版本用
 的antlr.jar包版本是2.7.5,比SUN ONE APPSERVER的高。以前在weblogic部署应用时,也出现过类似的问题。由于这些服务器会优先装载自己的类,因此会出现一些问题。解决方法是把hibernate下较高版本的antlr.jar放在classpath的前面。在SUN ONE APPSERVER
 下最快捷的方式就是将antlr-2.7.5H3.jar拷贝到ApplicationServer\lib目录下面即可

 解决完以上几个问题后,再次重启,访问,OK,一切正常!好有成就感 :)

posted @ 2006-11-29 14:42 jspark 阅读(1674) | 评论 (1)编辑 收藏
Sun HotSpot 1.4.1 JVM堆大小的调整
    
    Sun HotSpot 1.4.1使用分代收集器,它把堆分为三个主要的域:新域、旧域以及永久域。Jvm生成的所有新对象放在新域中。一旦对象经历了一定数量的垃圾收集循环后,便获得使用期并进入旧域。在永久域中jvm则存储class和method对象。就配置而言,永久域是一个独立域并且不认为是堆的一部分。

    下面介绍如何控制这些域的大小。可使用-Xms和-Xmx 控制整个堆的原始大小或最大值。
    下面的命令是把初始大小设置为128M:
    java –Xms128m
     –Xmx256m为控制新域的大小,可使用-XX:NewRatio设置新域在堆中所占的比例。

   下面的命令把整个堆设置成128m,新域比率设置成3,即新域与旧域比例为1:3,新域为堆的1/4或32M:
   java –Xms128m –Xmx128m
    –XX:NewRatio =3可使用-XX:NewSize和-XX:MaxNewsize设置新域的初始值和最大值。

   下面的命令把新域的初始值和最大值设置成64m:
     java –Xms256m –Xmx256m –Xmn64m
   永久域默认大小为4m。运行程序时,jvm会调整永久域的大小以满足需要。每次调整时,jvm会对堆进行一次完全的垃圾收集。

   使用-XX:MaxPerSize标志来增加永久域搭大小。在WebLogic Server应用程序加载较多类时,经常需要增加永久域的最大值。当jvm加载类时,永久域中的对象急剧增加,从而使jvm不断调整永久域大小。为了避免调整,可使用-XX:PerSize标志设置初始值。
   下面把永久域初始值设置成32m,最大值设置成64m。
    java -Xms512m -Xmx512m -Xmn128m -XX:PermSize=32m -XX:MaxPermSize=64m

    默认状态下,HotSpot在新域中使用复制收集器。该域一般分为三个部分。第一部分为Eden,用于生成新的对象。另两部分称为救助空间,当Eden充满时,收集器停止应用程序,把所有可到达对象复制到当前的from救助空间,一旦当前的from救助空间充满,收集器则把可到达对象复制到当前的to救助空间。From和to救助空间互换角色。维持活动的对象将在救助空间不断复制,直到它们获得使用期并转入旧域。使用-XX:SurvivorRatio可控制新域子空间的大小。

    同NewRation一样,SurvivorRation规定某救助域与Eden空间的比值。比如,以下命令把新域设置成64m,Eden占32m,每个救助域各占16m:
    java -Xms256m -Xmx256m -Xmn64m -XX:SurvivorRation =2

    如前所述,默认状态下HotSpot对新域使用复制收集器,对旧域使用标记-清除-压缩收集器。在新域中使用复制收集器有很多意义,因为应用程序生成的大部分对象是短寿命的。理想状态下,所有过渡对象在移出Eden空间时将被收集。如果能够这样的话,并且移出Eden空间的对象是长寿命的,那么理论上可以立即把它们移进旧域,避免在救助空间反复复制。但是,应用程序不能适合这种理想状态,因为它们有一小部分中长寿命的对象。最好是保持这些中长寿命的对象并放在新域中,因为复制小部分的对象总比压缩旧域廉价。为控制新域中对象的复制,可用-XX:TargetSurvivorRatio控制救助空间的比例(该值是设置救助空间的使用比例。如救助空间位1M,该值50表示可用500K)。该值是一个百分比,默认值是50。当较大的堆栈使用较低的sruvivorratio时,应增加该值到80至90,以更好利用救助空间。用-XX:maxtenuring threshold可控制上限。

   为放置所有的复制全部发生以及希望对象从eden扩展到旧域,可以把MaxTenuring Threshold设置成0。设置完成后,实际上就不再使用救助空间了,因此应把SurvivorRatio设成最大值以最大化Eden空间,设置如下:
   java … -XX:MaxTenuringThreshold=0 –XX:SurvivorRatio=50000 …
posted @ 2006-11-28 11:58 jspark 阅读(644) | 评论 (0)编辑 收藏
Assigning the target property requires the name of a window not the window itself.

Wecould try something like

window.opener.name="opener728";
form.target="opener728";

however, I suspect the window.name property is read-only.

Alternatively, if We are certain that the opener already has a name then this might work

form.target=window.opener.name;

It's also possible that browsers assign unique names to otherwise unnamed windows, so the above would always work - I've never checked this.


posted @ 2006-11-22 15:39 jspark 阅读(463) | 评论 (0)编辑 收藏

今天从网上找了一个读写csv格式的开源程序,还挺好用的。

下面是一个读取例子:

源文件格式:

 ProductID,ProductName,SupplierID,CategoryID,QuantityPerUnit,UnitPrice,UnitsInStock,UnitsOnOrder,ReorderLevel,Discontinued
 1,Chai,1,1,10 boxes x 20 bags,18,39,0,10,FALSE
 2,Chang,1,1,24 - 12 oz bottles,19,17,40,25,FALSE

 下面读取程序

 

 CsvReader reader  =   new  CsvReader( " products.csv " );

 reader.readHeaders();

 
while  (reader.readRecord())
 
{
  String productID 
=  reader.get( " ProductID " );
  String productName 
=  reader.get( " ProductName " );
  String supplierID 
=  reader.get( " SupplierID " );
  String categoryID 
=  reader.get( " CategoryID " );
  String quantityPerUnit 
=  reader.get( " QuantityPerUnit " );
  String unitPrice 
=  reader.get( " UnitPrice " );
  String unitsInStock 
=  reader.get( " UnitsInStock " );
  String unitsOnOrder 
=  reader.get( " UnitsOnOrder " );
  String reorderLevel 
=  reader.get( " ReorderLevel " );
  String discontinued 
=  reader.get( " Discontinued " );
  
  
//  perform program logic here

 }


 reader.close();




写CSV例子:

 CsvWriter writer = new CsvWriter(new FileWriter(new File("c:\\1.csv")),',');
  writer.write("aa");
  writer.write("bb");
  writer.write("cc");
  writer.endRecord();
  writer.write("1");
  writer.write("2");
  writer.write("3");
  writer.close();

posted @ 2006-11-07 12:05 jspark 阅读(6551) | 评论 (0)编辑 收藏

在spring中如何处理oracle大字段

在spring中采用OracleLobHandler来处理oracle大字段(包括clob和blob),则在程序中不需要引用oracle的特殊类,从而能够保证支持我们的代码支持多数据库。

1、首先数据表中的clob类型对应java持久化类的String类型;而blob类型对应byte[]类型
2、定义hibernate标签时,持久化类中对应clob类型的属性的hibernate type应为org.springframework.orm.hibernate.support.ClobStringType;而对应blob类型的属性的hibernate type应为org.springframework.orm.hibernate.support.BlobByteArrayType。
3、以后访问这些对应clob和blob类型的属性时,按普通属性处理,不需要特别编码。

java代码: 


< bean  id ="mySessionFactory2"  class ="org.springframework.orm.hibernate.LocalSessionFactoryBean" >  
        
< property  name ="dataSource" >  
                
< ref  bean ="myDataSource2" />  
         
</ property >  
         
< property  name ="lobHandler" >  
        
< ref  bean ="oracleLobHandle" />  
         
</ property >   
</ bean >  
< bean  id ="nativeJdbcExtractor"  class ="org.springframework.jdbc.support.nativejdbc.SimpleNativeJdbcExtractor" />  

< bean  id ="oracleLobHandle"  class ="org.springframework.jdbc.support.lob.OracleLobHandler"  Lazy-init ="true" >  
< property  name ="nativeJdbcExtractor" >  
    
< ref  local ="nativejdbcExtractor" />  
</ property >  
</ bean >


Spring为处理数据库Lob字段,特别提供了LobHandler接口。在操作Oracle RDBMS过程中,由于Oracle JDBC Driver实现的问题,应用必须采用Oracle原生的数据库连接(比如,oracle.jdbc.OracleConnection)、LOB原生实现(比如,oracle.sql.BLOB、oracle.sql.CLOB)。因此,LobHandler接口存在上述两种实现。简而言之,为操作Oracle数据库,必须使用OracleLobHandler实现。如果操作其他RDBMS类型,则使用DefaultLobHandler。NativeJdbcExtractor是个接口,通过它能够抽象各种连接池。另外Spring还提供两个接口存取Blob,LobCreator及LobHandler
posted @ 2006-08-28 11:58 jspark 阅读(880) | 评论 (0)编辑 收藏

   jdk提供的正则表达式是非常强大的,只要用过正则表达式的程序员应该是为其功能叹为观止。不过,正则表达式中的一个group概念相信应该不多人熟悉。

    正则表达式中的group,主要是用来区分子序列的,所谓子序列是用()之内的表达式。下面以一段程序为例

        String regex = "\\$\\{(I)(love)(java)\\}";
        System.out.println(Pattern.compile(regex).matcher("${Ilovejava}P)").groupCount());

 运行上面的代码段,结果为:3
 其中(I)为一个组, (love)为一个组,(java)为一个组。

  
  也许有人觉得这只是一个小功能,但是正则表达式的group,还有一个更加强大的地方就是在String.replaceAll方法中。
  public StringreplaceAll(String regex,
                         String replacement)

 其中第一个参数当然是政则表达式,第二个一般是普通的文本;但是第二个参数可以应用group的地方,这个功能用在一些场合是非常方便的。
      比如,下面这个例子  <driverClass>${driverClass}</driverClass>,要将${}去掉,即将这个例子替换成<driverClass>driverClass</driverClass>,可以用下面的代码来替换。例如
        String text = "<driverClass>${driverClass}</driverClass>";
        String result = replaceStr(text,"\\$\\{(driverClass)\\}","$1");
        System.out.println("result is:"+result);

   运行结果:result is:<driverClass>driverClass</driverClass>
  从上面可以看出,$1就是正则表达式中匹配的第一个序列,同样$2...表示第几个序列。如果$index中的index超出了表达式中子序列的个数的话,将抛出异常信息。 $0表示整个正则表达式。
posted @ 2006-08-15 15:30 jspark 阅读(482) | 评论 (0)编辑 收藏
在tomcat5.5版本以前,可以说jndi配置相对是比较复杂的,而且据网友说用tomcat5.0的控制台配置数据库连接池经常有问题,而且文档写得又不详细。

tomcat5.5出来后,jndi的配置方法是大大地节省,而且很简洁,个人觉得比以前的版本好很多。这里大概给出一个配置例子。tomcat数据库连接池jndi配置有两种,一种是全局的,一种是context的,下面主要是讲全局的,并且以一个实例jdbc/byisdb为例子
   
一、tomcat5.0配置方法

1、首先在server.xml里面配置,找到下面的配置
  <!-- Global JNDI resources -->
  <GlobalNamingResources>
 </GlobalNamingResources>

2、在里面增加一个Resource
      <Resource name="jdbc/byisdb"
               auth
="Container"
               type
="javax.sql.DataSource"/>


3、在下面增加属性

  <ResourceParams name="jdbc/byisdb">
    
<parameter>
      
<name>factory</name>
      
<value>org.apache.commons.dbcp.BasicDataSourceFactory</value>
    
</parameter>

    
<!-- Maximum number of dB connections in pool. Make sure you
         configure your mysqld max_connections large enough to handle
         all of your db connections. Set to 
0 for no limit.
         
-->
    
<parameter>
      
<name>maxActive</name>
      
<value>100</value>
    
</parameter>

    
<!-- Maximum number of idle dB connections to retain in pool.
         Set to 
-1 for no limit.  See also the DBCP documentation on this
         and the minEvictableIdleTimeMillis configuration parameter.
         
-->
    
<parameter>
      
<name>maxIdle</name>
      
<value>30</value>
    
</parameter>

    
<!-- Maximum time to wait for a dB connection to become available
         in ms, in 
this example 10 seconds. An Exception is thrown if
         
this timeout is exceeded.  Set to -1 to wait indefinitely.
         
-->
    
<parameter>
      
<name>maxWait</name>
      
<value>10000</value>
    
</parameter>

    
<!-- MySQL dB username and password for dB connections  -->
    
<parameter>
     
<name>username</name>
     
<value>una_oa</value>
    
</parameter>
    
<parameter>
     
<name>password</name>
     
<value>una_oa</value>
    
</parameter>

    
<!-- Class name for the old mm.mysql JDBC driver - uncomment this entry and comment next
         
if you want to use this driver - we recommend using Connector/J though
    
<parameter>
       
<name>driverClassName</name>
       
<value>org.gjt.mm.mysql.Driver</value>
    
</parameter>
     
-->
    
    
<!-- Class name for the official MySQL Connector/J driver -->
    
<parameter>
       
<name>driverClassName</name>
       
<value>oracle.jdbc.driver.OracleDriver</value>
    
</parameter>
    
    
<!-- The JDBC connection url for connecting to your MySQL dB.
         The autoReconnect
=true argument to the url makes sure that the
         mm.mysql JDBC Driver will automatically reconnect 
if mysqld closed the
         connection.  mysqld by 
default closes idle connections after 8 hours.
         
-->
    
<parameter>
      
<name>url</name>
      
<value>jdbc:oracle:thin:@192.168.1.210:1521:byisdb</value>
    
</parameter>
  
</ResourceParams>

4、在你的应用的web.xml里面增加
<resource-ref>
 
<description>postgreSQL Datasource example</description>
 
<res-ref-name>jdbc/byisdb</res-ref-name>
 
<res-type>javax.sql.DataSource</res-type>
 
<res-auth>Container</res-auth>
</resource-ref>

OK,到此配置完毕,可以用下面的几段代码进行测试

Context initContext = new InitialContext();
Context envContext  
= (Context)initContext.lookup("java:/comp/env");
DataSource ds 
= (DataSource)envContext.lookup("jdbc/byisdb");
Connection conn 
= ds.getConnection();
out.println(
"conn is:"+conn);

二、tomcat5.5配置

1、打开conf/context.xml里面
  添加下面的配置

    <Resource name="jdbc/byisdb" auth="Container" type="javax.sql.DataSource" driverClassName="oracle.jdbc.driver.OracleDriver" url="jdbc:oracle:thin:@192.168.1.210:1521:byisdb" username="una_oa" password="una_oa" maxActive="20" maxIdle="10" maxWait="10000"/>

 

2在你的应用的web.xml里面增加

<resource-ref>
 
<description>postgreSQL Datasource example</description>
 
<res-ref-name>jdbc/byisdb</res-ref-name>
 
<res-type>javax.sql.DataSource</res-type>
 
<res-auth>Container</res-auth>
</resource-ref>

同样,可以用上面的代码进行测试。
posted @ 2006-08-11 14:03 jspark 阅读(2941) | 评论 (1)编辑 收藏

 最近由于需要用到ThreadLocal,在网上搜索了一些相关资料,发现对ThreadLocal经常会有下面几种误解

 一、ThreadLocal是java线程的一个实现
      ThreadLocal的确是和java线程有关,不过它并不是java线程的一个实现,它只是用来维护本地变量。针对每个线程,提供自己的变量版本,主要是为了避免线程冲突,每个线程维护自己的版本。彼此独立,修改不会影响到对方。

 二、ThreadLocal是相对于每个session的

        ThreadLocal顾名思义,是针对线程。在java web编程上,每个用户从开始到会话结束,都有自己的一个session标识。但是ThreadLocal并不是在会话层上。其实,Threadlocal是独立于用户session的。它是一种服务器端行为,当服务器每生成一个新的线程时,就会维护自己的ThreadLocal。对于这个误解,个人认为应该是开发人员在本地基于一些应用服务器测试的结果。众所周知,一般的应用服务器都会维护一套线程池,也就是说,对于每次访问,并不一定就新生成一个线程。而是自己有一个线程缓存池。对于访问,先从缓存池里面找到已有的线程,如果已经用光,才去新生成新的线程。所以,由于开发人员自己在测试时,一般只有他自己在测,这样服务器的负担很小,这样导致每次访问可能是共用同样一个线程,导致会有这样的误解:每个session有一个ThreadLocal

 三、ThreadLocal是相对于每个线程的,用户每次访问会有新的ThreadLocal

  理论上来说,ThreadLocal是的确是相对于每个线程,每个线程会有自己的ThreadLocal。但是上面已经讲到,一般的应用服务器都会维护一套线程池。因此,不同用户访问,可能会接受到同样的线程。因此,在做基于TheadLocal时,需要谨慎,避免出现ThreadLocal变量的缓存,导致其他线程访问到本线程变量

 四、对每个用户访问,ThreadLocal可以多用
        可以说,ThreadLocal是一把双刃剑,用得来的话可以起到非常好的效果。但是,ThreadLocal如果用得不好,就会跟全局变量一样。代码不能重用,不能独立测试。因为,一些本来可以重用的类,现在依赖于ThreadLocal变量。如果在其他没有ThreadLocal场合,这些类就变得不可用了。个人觉得ThreadLocal用得很好的几个应用场合,值得参考

  1、存放当前session用户:quake want的jert

  2、存放一些context变量,比如webwork的ActionContext

  3、存放session,比如Spring hibernate orm的session

posted @ 2006-08-01 12:09 jspark 阅读(30707) | 评论 (12)编辑 收藏

       说真的,对于spring提供AOP的功能,个人实在不敢太过于恭维。主要是Spring的AOP功能没那么强大,而且必须是对于spring容器管理的bean才能实施AOP功能,对于容器外的bean就无能为力了。而且spring没有提供属性的AOP功能。在这些方面,spring AOP真的不能和Aspectj相比。Aspectj的AOP功能才真的是真正意义的AOP框架,提供的功能非常强大,几乎可以实现任何类型的AOP。不过Aspectj的学习曲线相对要比spring AOP稍微陡峭一点,主要是spring AOP可以当成普通javabean来处理,而Aspectj还要另外做编译器,比较麻烦。不过,庆幸的是,eclipse下面有Aspectj插件,开发起来也是很方便。所以一般,复杂的AOP功能,还是推荐用Aspectj

     对于一般的J2EE开发来说,要实现一些比较常用的AOP,Spring 还是能满足的。比如事务、异常、日志、权限等等,在这些方面,spring AOP还是比较方便的,特别是事务处理,spring提供了相当好的集成。如果事务处理用Aspectj来实现,不见得好多少。

    一直以来,觉得spring AOP最好用的一个地方就是提供了BeanNameAutoProxyCreator,这个类真的非常方便,以至于个人一旦遇到要实现AOP,首先就是求组于BeanNameAutoProxyCreator,如果BeanNameAutoProxyCreator实现不了,再考虑别的。不过,一般情况来说,BeanNameAutoProxyCreator的确能满足需要了,除非你的需求真的千奇百怪。

   在应用spring AOP功能时,优先考虑用接口。因为如果用接口的话,那么spring会创建一个代理,并在代理里面实现AOP增强代码,并调用真正的实例对象。不过,spring AOP功能不一定非要用接口,一些普通类也是可以的。对于普通类,spring会用CGLIB来动态生成一个新类。并且CGLIB会保持一个生成类的cache,因此它不会一直生成新类。spring使用ProxyCallbackFilter对象把其它对象放进map进行管理。如果没有管理好cache,将会产生大量的java对象,直至出现OutOfMemoryErrors。因此使用springaop时,一定要正确实现equals and hashCode。

   
不过,不管怎么样,在应用spring AOP时,还是优先考虑接口方式,毕竟面向接口方式还是值得推荐的一个编程思想。

posted @ 2006-07-31 19:37 jspark 阅读(4098) | 评论 (11)编辑 收藏
     最近在负责一个大项目,项目组成员包括项目经理大概10个人左右。项目技术用struts+spring+hibernate实现。项目的规模相对来说是比较大的,总共有10大模块,每个大模块又分为有十几个、甚至几十个小模块。开发工具用eclipse,由于在开发阶段,项目开发成员需要频繁重启服务器。在启动服务器的时候,每次启动时间总是会超过1分钟。记得以前在做另外一个项目时,启动时间不到5秒钟,相差了10倍,而且项目规模是差不多的。

    从初步分析来说,应该是hibernate解释hbm.xml时花费时间,或者可能是spring容器启动并解释所有的bean配置文件。诊断了一下,发现1分钟消耗的时间主要分布在hibernate解释hbm.xml花费5秒;spring容器从启动到解释bean配置文件竟然花了58秒,真是太嚣张了。当时非常怀疑spring的效率问题。企图从网上搜索相关资料,看看有什么优化措施。

    首先是找到了hibernate的启动优化 http://www.hibernate.org/194.html  里面的主要思想是通过将xml序列花到本地的文件里,每次读取的时候根据情况,从本地文件读取并反序列化,节省了hibernate xml的解析时间。按照这个方式测试了一下,发现hibernate的启动时间从5秒降低到3秒,但是这个优化对于整个启动过程是杯水车薪的,毫无用处。

    没办法,又仔细查看了spring的资料,终于发现spring的容器是提供了lazy-load的,即默认的缺省设置是bean没有lazy-load,该属性处于false状态,这样导致spring在启动过程导致在启动时候,会默认加载整个对象实例图,从初始化ACTION配置、到service配置到dao配置、乃至到数据库连接、事务等等。这么庞大的规模,难怪spring的启动时间要花将近1分钟。尝试了一下,把beans的default-lazy-init改为true就,再次启动,速度从原来的55秒,降到8秒钟!!Great!虽然是非常小一个改动,但是影响确实非常大。一个项目组10个人,假若每个人一天平均需要在eclipse下启动测试服务器50次。那么一天项目组需要重启500次,每次节省50秒的话,就是25000秒,将近几个小时,差不多一个工作日,多么可观的数字!

   不过在运行期间第一次点页面的时候,由于spring做了lazy-load,现在就需要启动一部分需要的beans,所以稍微慢2-3秒钟,但是明显比等几十秒要快很多,值得一鉴。

    以上是针对开发阶段的spring容器启动优化,在部署到实际环境中,倒是没必要设置为lazy-load。毕竟部署到实际环境中不是经常的事,每次启动1分钟倒不是大问题。
posted @ 2006-07-29 13:27 jspark 阅读(3263) | 评论 (2)编辑 收藏
       上面说到代码混淆方法之混淆器使用,主要针对proguard进行了说明。其实,只要我们的类被其他地方的类调用到的话,那么代码混淆器就似乎没有办法了,因为代码混淆如果把代码的签名一起改了的话,其他地方是肯定调用不到,并会出错。而且,针对代码调用,有几点是我们肯定不能避免的:一是jsp页面,如果在jsp页面调用了某个类,那么如果类被混淆了的话,jsp页面肯定会出错;二是xml配置文件,比如在hibernate开发中,对于hbm.xml文件,就没办法了。不过,很庆幸的是,proguard的功能很强,可以通过配置,只针对私有方法、私有变量做混淆。但是,这种混淆效果肯定是不如所愿。下面将说明代码混淆方法之二(tomcat下面代码加密)

       应用服务器加密的方法不外就是通过修改该应用服务器的类转载器,来载入我们的类。首先我们通过一定算法,将我们的class文件加密,并部署到应用服务器。然后修改修改该应用服务器的类转载器,通过解密算法将class文件反编译并加载。从而达到class文件的加密。下面主要针对tomcat下面的加密、解密说明。

     首先得研究一下tomcat的类装载机制:tomcat的类装载机制主要分为下面几种:

   1、Bootstrap: 由虚拟机提供
   2、System:类路径等相关
   3、Common:tomcat下面的公共包
   4、Catalina:tomcat自己的包,比如server目录下面
   5、Shared:各个war包的共享包
   6、Webapp:各个war包自己的相关类包

   由于一般的典型情况是,我们是要加密自己的应用程序,那么,我们就要覆盖上面所说的Webapp类装载器。tomcat的Webapp类装载器位于${tomcat.home}\server\lib\catalina.jar下面的类org.apache.catalina.loader.WebappClassLoader。我们从tomcat的网站下面下载tomcat的源代码,WebappClassLoader的源代码位于目录\jakarta-tomcat-catalina\catalina\src\share\org\apache\catalina\loader下面,打开源代码,可以看到里面的调用机制是这样的:
     该类里面提供了很多诸如addRepository、addJar之类的方法,这是tomcat给类路径添加相应的目录和包,比如在启动时,tomcat会将我们的应用程序下面的WEB-INF/lib/*.jar和WEB-INF/classes/**.class添加到资源路径下面。
    
    首先,在加载类的时候,会调用loadClass方法。我们先定位到下面这个方法   
     public Class loadClass(String name, boolean resolve)
        throws ClassNotFoundException 

     仔细观察该方法,可以发现,tomcat提供了很多缓存机制,首先分别从各个级别的缓存加载类,如果加载到类,就直接返回,否则会调用下面的方法: clazz = findClass(name);

     继续定位到方法 public Class findClass(String name) throws ClassNotFoundException 
     该方法里面有一句调用 clazz = findClassInternal(name); 这个很关键,也就是我们最需要关心的一个方法了。再仔细阅读一下,就能发现,tomcat从本地的资源库里面找,并先查找资源缓存,如果找到的话,直接返回缓存类。若找不到,就会读取类文件的byte[]数组,并最后调用defineClass方法。defineClass文件是类装载的最后一个类定义方法。下面就是findClassInternal方法的代码小段

           if (entry.loadedClass == null) {
            synchronized (this) {
                if (entry.loadedClass == null) {
                 
                  clazz = defineClass(name, entry.binaryContent, 0,
                                        entry.binaryContent.length,
                                        codeSource);               
                 }
                    entry.loadedClass = clazz;
                    entry.binaryContent = null;
                    entry.source = null;
                    entry.codeBase = null;
                    entry.manifest = null;
                    entry.certificates = null;
                } else {
                    clazz = entry.loadedClass;
                }
            }
        } else {
            clazz = entry.loadedClass;
        }

      entry是一个存放类属性的bean,其中类的数组流存放在binaryContent,那么我们的加密解密就从这里入手。为了方便起见,我们用base64算法加密,加密方法很简单。通过调用base64加密方法,把原有的class文件加密;推荐的base64加密方法是commons包的codec工程。可以从apache上面下载。把加密后的class文件替换tomcat下面的部署类文件。 那么在类装载时就可以解密,并加载了。例如,为了测试,个人只对OrganizationServiceImp.class进行加密,那么通过修改以上的装载方法,就可以实现解密,修改后的代码为如下,其中黑体为修改的

        if (entry.loadedClass == null) {
            synchronized (this) {
                if (entry.loadedClass == null) {
                 
                 if(name.indexOf("OrganizationServiceImp") >=0)
                 {
                    byte[] base64Array = entry.binaryContent;
                    byte[] orginByteArray = Base64.decodeBase64(base64Array);
                    clazz = defineClass(name, orginByteArray, 0,
                      orginByteArray.length, 
                               codeSource);
                 }
                 else
                 {
                  clazz = defineClass(name, entry.binaryContent, 0,
                                        entry.binaryContent.length,
                                        codeSource);
                                       
                 }

                    entry.loadedClass = clazz;
                    entry.binaryContent = null;
                    entry.source = null;
                    entry.codeBase = null;
                    entry.manifest = null;
                    entry.certificates = null;
                } else {
                    clazz = entry.loadedClass;
                }
            }
        } else {
            clazz = entry.loadedClass;
        }
      
      
运行服务器,一切正常,说明解密、加密成功,到此完成tomcat服务器下面的解密、加密。可以看出,tomcat下面的类装载机制其实挺简单,不像其他服务器,比如weblogic、websphere服务器那么复杂。若能在这些服务器下面实现类似的效果,将是一个多么爽的事。个人在近几天将对weblogic服务器下面的部署应用进行类似的研究

posted @ 2006-07-25 12:25 jspark 阅读(9057) | 评论 (13)编辑 收藏

    我们做java开发的一般都会遇到如何保护我们开发的代码问题。java语言由于是基于jvm上面,所以反编译class文件很很容易。假如我们做了一个web程序,并把这个web程序发布给客户。实际上,客户是很容易反编译出我们的源代码出来,包括所有的src文件和jsp文件等等。

   那么,如何保护我们的源代码,实际上,应该有几种方法可以使用:1、使用代码混淆器  2、重载应用服务器的classloader

   对于第一种方法来说,现在外面有很多开源工具可以使用,个人认为最好用的当属proguard莫属。proguard主要是易用易学。而且提供的功能也挺多。下面是个人一点使用心得

   (1)、从网上download proguard工具,proguard工具主要包含是几个jar文件和一些example,下载地址http://proguard.sourceforge.net/

   (2)、将里面的几个jar文件添加到类路径下面。当然,也可以不添加,但是下面在做混淆的时候,必须指定classpath,使在做混淆的过程中,能否访问该类

   (3)、编写一个配置文件,主要是混淆器的一些参数。比如,下面是一个例子
-injars       platform.jar
-outjars      platform_out.jar
-libraryjars  <java.home>/lib/rt.jar
-libraryjars ibatis-common-2.jar
-libraryjars ibatis-dao-2.jar
-libraryjars ibatis-sqlmap-2.jar
-libraryjars junit-3.8.1.jar
-libraryjars d:/j2ee.jar
-libraryjars struts.jar
-libraryjars commons-lang.jar
-libraryjars D:/0working/coreproject/byislib/jasperreports-0.6.1.jar
-libraryjars  commons-beanutils.jar

-printmapping proguard.map
-overloadaggressively
-defaultpackage ''
-allowaccessmodification
-dontoptimize

-keep public class *
{
 public protected *;
}

-keep public class org.**

-keep public class it.**

各个参数的含义参考proguard文档,该文档非常详细,上手很容易

OK,到此就完成了代码混淆,打开产生的jar包可以看到,多了好多a、b、c之类的类文件。说明混淆结果已经成功。将原jar删除、运行产生的混淆jar包,一切正常!

常见问题:使用过程中个人遇到了几个问题,开始也是找了很久才解决
   a. 内存溢出异常: 主要是proguard在做混淆的时候,吃了很多内存,因此,在运行混淆器的时候,可以增加内存,比如 java -mx512m .....
  b.栈溢出异常: 主要是proguard在做混淆的时候,会对一些代码进行优化,若遇到一些相对复杂的方法时,可能会抛出此异常。对付的办法是增加配置参数-dontoptimize,如上面的配置例子所示

对于第二种方法,重载服务器的classloader的原理是这样。 首先我们通过一定算法把class文件加密; 然后写我们自己的classloader,替换服务器的classloader。 这样,我们可以读取class文件,通过我们自己的算法反加密成正确的class,然后再次进行load。这个方式还没应用起来,这几天个人正在研究,有什么新成果会在此做一些总结。

posted @ 2006-07-24 15:03 jspark 阅读(7900) | 评论 (6)编辑 收藏