Monthly Archives: August 2008

另类加水印——根据明暗度分别加不同的水印


  1. package image;   

  2.   

  3. import java.awt.AlphaComposite;   

  4. import java.awt.Color;   

  5. import java.awt.Font;   

  6. import java.awt.Graphics;   

  7. import java.awt.Graphics2D;   

  8. import java.awt.Image;   

  9. import java.awt.image.BufferedImage;   

  10. import java.awt.image.ColorModel;   

  11. import java.awt.image.PixelGrabber;   

  12. import java.io.File;   

  13. import java.io.FileOutputStream;   

  14.   

  15. import javax.imageio.ImageIO;   

  16.   

  17. import com.sun.image.codec.jpeg.JPEGCodec;   

  18. import com.sun.image.codec.jpeg.JPEGImageEncoder;   

  19.   

  20. public class WaterMark {   

  21.   

  22.     /**  

  23.      * 获取指定矩形中的像素的矩阵  

  24.      *   

  25.      * @param imageSrc  

  26.      * @param startX  

  27.      * @param startY  

  28.      * @param w  

  29.      * @param h  

  30.      * @return  

  31.      */  

  32.     private int[] getPixArray(Image imageSrc, int startX, int startY,   

  33.             int w, int h) {   

  34.         int[] pix = new int[(w – startX) * (h – startY)];   

  35.            

  36.         /*下面是别人程序中的一段,我实在不明白为何要加这一段,因为我去掉也没有问题,加上还会报错*/  

  37.         PixelGrabber pg = null;   

  38.         try {   

  39.             pg = new PixelGrabber(imageSrc, startX, startY, w-startX, h-startY, pix, 0, w);   

  40.             if (pg.grabPixels() != true) {   

  41.                 try {   

  42.                     throw new java.awt.AWTException(“pg error” + pg.status());   

  43.                 } catch (Exception eq) {   

  44.                     eq.printStackTrace();   

  45.                 }   

  46.             }   

  47.         } catch (Exception ex) {   

  48.             ex.printStackTrace();   

  49.         }   

  50.         return pix;   

  51.     }   

  52.   

  53.     /**  

  54.      * 将1张图片和另1张图片的指定区域重合。可用于制作水印。图片的左上角坐标为0,0  

  55.      *   

  56.      * @param lightnessWaterImg  

  57.      *            颜色比较亮的水印图片,适合底色比较暗的情况  

  58.      * @param darknessWaterImg  

  59.      *            颜色比较暗的水印图片,适合底色比较亮的情况,如果不想区分,则输入null,平均灰度边界同时失效。  

  60.      * @param targetImg  

  61.      *            源图片  

  62.      * @param startX  

  63.      * @param startY  

  64.      * @param x  

  65.      * @param y  

  66.      * @param alpha  

  67.      *            透明度,0f为全透明,1f为完全不透明,0.5f为半透明  

  68.      * @param averageGray  

  69.      *            平均灰度边界(0-255),大于此值,则打暗的水印图片,小于此值则打亮的水印图片。  

  70.      *            默认值128。超过范围,按默认值进行。  

  71.      */  

  72.     private final void pressImage(String lightnessWaterImg,   

  73.             String darknessWaterImg, String targetImg, int startX, int startY,   

  74.             int x, int y, float alpha, float averageGray) {   

  75.         try {   

  76.             // 先判断亮水印和源文件的值是否为null,否则抛出异常   

  77.             if (lightnessWaterImg == null || lightnessWaterImg == “”  

  78.                     || targetImg == null || targetImg == “”) {   

  79.                 throw new Exception(“亮水印或者源图片的地址不能为空”);   

  80.             }    

  81.             // 再判断平均灰度边界是否越界   

  82.             if (averageGray>255||averageGray<0) {   

  83.                 averageGray = 128;   

  84.             }   

  85.                

  86.   

  87.             // 装载源图片   

  88.             File _file = new File(targetImg);   

  89.             // 图片装入内存   

  90.             BufferedImage src = ImageIO.read(_file);   

  91.             // 获取图片的尺寸   

  92.             int width = src.getWidth(null);   

  93.             int height = src.getHeight(null);   

  94.             // 根据源图片尺寸,设置预装载的一个图片,默认是RGB格式的   

  95.             BufferedImage image = new BufferedImage(width, height,   

  96.                     BufferedImage.TYPE_INT_RGB);   

  97.             Graphics2D graphics = image.createGraphics();   

  98.             // 绘制内存中的源图片至指定的矩形内   

  99.             graphics.drawImage(src, 00, width, height, null);   

  100.             // 在已经绘制的图片中加入透明度通道   

  101.             graphics.setComposite(AlphaComposite.getInstance(   

  102.                     AlphaComposite.SRC_ATOP, alpha));   

  103.                

  104.   

  105.             // 获取源图片中和设定的同样大小的区域内的像素集合   

  106.             int[] pixels = getPixArray(src, startX, startY, x, y);   

  107.   

  108.             //查询此集合的平均灰度   

  109.             float average = getAverageGrap(x-startX,y-startY,pixels);   

  110.   

  111.             // 如果平均灰度大于130,则说明此区域比较亮,否则则比较暗   

  112.             System.out.println(average);   

  113.   

  114.                

  115.             //装载水印图片所需参数   

  116.             File water;   

  117.             BufferedImage bufferwater;   

  118.                

  119.             // 根据设定的平均灰度边界来装载不同的水印   

  120.             if (darknessWaterImg == null||average>=averageGray) {   

  121.                 // 装载亮水印文件   

  122.                 water = new File(darknessWaterImg);   

  123.             }else{   

  124.                 // 装载暗水印文件   

  125.                 water = new File(lightnessWaterImg);   

  126.             }   

  127.             // 装入内存   

  128.             bufferwater = ImageIO.read(water);   

  129.                            

  130.             graphics.drawImage(bufferwater, startX, startY, x, y,   

  131.                     null);   

  132.             // 水印文件结束   

  133.             graphics.dispose();   

  134.             FileOutputStream out = new FileOutputStream(targetImg);   

  135.             JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);   

  136.             // 绘制新的文件   

  137.             encoder.encode(image);   

  138.             out.close();   

  139.                

  140.         } catch (Exception e) {   

  141.             e.printStackTrace();   

  142.         }   

  143.     }   

  144.            

  145.     /**  

  146.      * 查询某个区域的平均灰度  

  147.      * @param width  

  148.      * @param height  

  149.      * @param pixels  

  150.      * @return  

  151.      */  

  152.     private float getAverageGrap(int width,int height,int[] pixels){   

  153.         /* 下面是开始算这个区域的亮度了,灰度等同于亮度 */  

  154.         ColorModel colorModel = ColorModel.getRGBdefault();   

  155.         int i = 0;   

  156.         int j = 0;   

  157.         int k = 0;   

  158.         int r = 0;   

  159.         int g = 0;   

  160.         int b = 0;   

  161.         int gray = 0;   

  162.         float average = 0;// 平均灰度   

  163.         for (i = 0; i < height; i++) {   

  164.             for (j = 0; j < width; j++) {   

  165.                 // 定位像素点   

  166.                 k = i * width + j;   

  167.                 r = colorModel.getRed(pixels[k]);   

  168.                 g = colorModel.getGreen(pixels[k]);   

  169.                 b = colorModel.getBlue(pixels[k]);   

  170.   

  171.                 // 计算灰度值   

  172.                 gray = (r * 38 + g * 75 + b * 15) >> 7;   

  173.   

  174.                 average = average + gray;   

  175.             }   

  176.         }   

  177.         // 计算平均灰度   

  178.         average = average / ((i – 1) * (j – 1));   

  179.         return average;   

  180.     }   

  181.     public static void main(String[] args) {   

  182.         WaterMark waterMark = new WaterMark();   

  183.   

  184.         waterMark.pressImage(“F:\\Mine\\My Pictures\\素材\\w2.png”“F:\\Mine\\My Pictures\\素材\\w1.png”,   

  185.                 “F:\\Mine\\My Pictures\\素材\\2.jpg”5205009008000.5f, 50);   

  186.         System.out.print(“添加成功”);   

  187.     }   

  188.   

  189. }  

JavaRebel 1.2发布,充分支持Spring

JavaRebel是一个JVM插件(-javaagent),能够即时重载java class更改,因此不需要重新部署一个应用或者重启容器,节约开发者时间。它是一个Java EE和Java 独立应用的常用解决方案。最新的稳定版本JavaRebel代码加载包含一个Spring的插件,能重新加载Spring配置快速的促进生产力的发展,甚至更多。查看Screencast ,下载JavaRebel插件

这个稳定的JavaRebel主要致力于两个主题:稳定和可扩展性。下面是它与1.1版本的区别:

core:增强了core的JavaRebel现在可以处理任何复杂的代码或容器。
SDK:您可以对应用程序和库的任何部分进行增值,无论您身在何处或怎么设置。SDk 为开放源代码。
JavaRebel插件:现在支持自定义的classloaders更容易,容器和框架只需登记一个小插件即可。
Eauinox OSGi容器,现在支持IBM WebSphere 和 Atlassian 混合插件。

查看完整的更新列表 或现在就开始下载JavaRebel

我们已经准备了一个Screencast来说明Spring插件的功能。查看该插件的安装手册 ,了解更多的详情或现在就下载

一个体现Java接口及工厂模式优点的例子

     随着模式概念的普及,了解模式和使用模式的程序员越来越多,很多人在学习模式的时候,都会有这样一种疑惑:“有必要搞得这么复杂吗?”。的确,因为教程的例子过于简单化(这样方便读者学习),或者是作者选例子的时候并没有很好体现所讲模式的优点,很多情况下如果仅就其例子的问题来说,用模式是太复杂了。因此才导致这样的误解:“模式就是把简单的问题复杂化吗?”。当然不是,随着你开发实践的不断丰富,你终会发现模式强大威力,而且模式也并非贵族化的编程方式,它就是一些经过提炼了的解决问题的方法技巧。

 

      通过学习模式,程序员开始告别过去准直线式的代码方式,模式开扩了我们的视野,强化了我们面向对象编程的思维方式。然而现在又出现了另一个普遍的问题,盲目应用模式。模式是问题的解决方案,先有问题才有模式,模式是依附于所要解决的问题的而生的。必须了解模式在很多情况下是以提高代码的复杂度为代价来增强灵活性、可复用性。如果在自已的代码中使用某一模式仅只提高了代码的复杂度,而其它方面收效甚微,或者某部份代码根本就不存在灵活性及高复用性的需求,那么我们就没有必要为使用模式而放弃更直观简单的代码写法。

 

      一流的高手90%精力关注问题的解决方案,因为找到了好的解决方案,再写起代码会很轻松代码也简洁流畅,看这样的代码是一种享受和提高;二流的熟手90%精力关注代码实现,因为问题的解决方案并非最佳,实现的代码也会比较复杂;三流菜鸟记流水帐,90%精力在敲键盘,常常做了大半才发现行不通,回过头来再用90%的时间敲键盘,根本不会用到任何模式,写出来的代码的只有他自已才能看懂。做出来的软件也是支离破碎,做一丁点改动都要大费周折,而且你还不知道改动后会产生什么问题,大有住危房里的感觉。

 

      在这里还是举一个滥用模式的例子吧。我曾参与过一个大集团公司OA系统的第二期开发,开发沿用原有代码架构并增加新的功能模块。文档很少我读原代码时就被它程序里的代码转来转去搞得头大如斗,最后读懂了:原代码架构总体采用工厂模式,而且是最复杂的抽象工厂模式。它把所有模块类都通过工厂生成还工厂套工厂,并且每一个模块类都有一个接口,每个接口也只有一个模块现实类,因为涉及权限控制还用了代理(proxy)模式。 读懂代码后我开始嵌入代码,发现每新增一个类,都要到六个Java文件中去增加相应代码,而在类中每增加一个方法,也要到它的接口等四个Java文件中去增加相应代码。天呀!!!记得当时我的小姆指常会不听使唤,就是因为频繁的使用Ctrl+C 、Ctrl+V,小姆指按着Ctrl键给累的。整个项目组苦不堪言,真烦透了。项目结束后我回顾发现:代理模式用得还对(现在针对权限这类横向控制有AOP编程这种新的解决办法了)但工厂模式在这里根本就是画蛇添足,不仅没有解决什么问题,反而增加代码复杂度和耦合性,降低了开发效率连维护难度都提高了。而且那种每个类简单的加一个接口的方式,更是没有道理,这让我很想说周星驰说过的一句话:“球~~~不是这么踢~~~~的,接口~~~不是这么用~~~的”。言归正传,我们先来看这样一个常见问题:某系统需要支持多种类型的数据库。用过Oracle、MSSQL等数据库的人都知道,它们的SQL编写方式都各有些不同。比如说Oracle的唯一标识自动+1字段用的是序列,MSSQL改一下字段属性就成了,还有各种各自特有的SQL用法。为了支持多数据库,难道我们要开发多套系统?当然NO。请看下面的解决方案。

 

      即然数据库存在多种,我们可以将系统中所有对数据库的操作抽象出来,写成一个个方法组合到一个类中,有几种数据库我们就写几个这样的类。具体设计类图如下:

20060910.gif

 

 

简要说明:


  • OracleDataOperate、SqlserverDataOperate、MysqlDataOperate,分别代表Oracle、Sqlserver、Mysql这三种数据库的操作类。继承自AbstractDataOperate
  • AbstractDataOperate是一个抽象类,包含了那些不同种类数据库都是一样代码的操作方法。继承自DataOperate
  • DataOperate是上面说的数据操作类的统一接口,只有两个方法:取得一条记录、插入一条记录。
  • DataOperateFactory是一个工厂方法,统一用它的方法来得到数据库操作类的实例。
  • SampleClass是我们系统的某个功能模块的类。
  • People是一个实体类,代表一条记录。三个字段 oid唯一标识符、name姓名、date生日。

 

详细说明:

1、所有系统功能模块类只认DataOperat这个接口还不必管具体的实现类是OracleDataOperate还SqlserverDataOperate。DataOperate源代码如下:

publicinterface DataOperate {

    //根据记录的唯一标识取出一条记录

    People getPeople(String oid);

    //插入一条记录

    boolean insertPeople(People people);

}

 

2、AbstractDataOperate、OracleDataOperate、SqlserverDataOperate、MysqlDataOperate都是继承DataOperate接口的,没什么好说的,省略。

 

3、DataOperateFactory。我们看看工厂方法怎么写的。

publicclass DataOperateFactory {

    publicstaticfinalint ORACLE = 0; //定义三个表示数据库类型的常量

    publicstaticfinalint MYSQL = 1;

    publicstaticfinalint SQLSERVER = 2;

 

    privatestatic DataOperate db;

    privatestaticint dataType = MYSQL;

    /**

     * 根据数据库类型(dataType)取得一个数据库操作类的实例,

     * 这里对DataOperate使用了单例模式,因为OracelDataOperate等都是无状态的工具类,

     * 所以整个系统只保留一个实例就行了。

     *

     * @return 返回的是接口,客户端不必关心具体是用那个实现类

     */

    publicstatic DataOperate getInstance() {

        if (db == null) {

            if (dataType == ORACLE) //根据dateType返回相应的实现类

                returnnew OracelDataOperate();

            if (dataType == MYSQL)

                returnnew MysqlDataOperate();

            if (dataType == SQLSERVER)

                returnnew SqlserverDataOperate();

        }

        return db;

    }

}

 

4、接下来就看看使用端是如何调用工厂方法和使用数据操作类的。

/**

 * 系统某个功能类

 */

publicclass SampleClass {

private DataOperate db; //声明一个数据库操作类,注意这里用的是接口噢

    /**某方法*/

    publicvoid sampleMethod() {

        db = DataOperateFactory.getInstance();//得到单一实例

        People p = db.getPeople(“123”); //取得一条记录

        db.insertPeople(p);//再插回去

    }

}

 

  我们发现SampleClass中根本没有出现OracelDataOperate、MysqlDataOperate等的影子,这就是接口的威力。客户端不必针对OracelDataOperate等写不同的代码,它只关心DataOperate即可,具体要取那个类的逻辑就由DataOperateFactory负责了。

 

总结:



  • 从例子中我们可以看到什么是面向接口的编程方式。SampleClass使用数据操作类可以不必关心具体是那个类,只要是符合接口的都行


  • 要实例?只须调用DataOperateFactory.getInstance()即可,其它的交于DataOperateFactory这个工厂来做吧,使用端什么都不用关心。


  • 我们要支持新的数据库类型,只须要象OracelDataOperate那样,再写一个继承AbstractDataOperate的类即可,比如SysbaseDataOperate。然后到DataOperateFactory中加入相应代码即可。


  • 如果我们想要可配置性更高,可以用privatestaticint dataType = MYSQL;中的值设置到一个文本文件中。

  对于开发支持多种数据库的系统,强烈建议使用hibernate,我现在做的系统就是用hibernate的,开发时用Mysql,到要给客户时将数据库换了DB2,程序不用做任何改动,真正的无逢移植。不过这样,本文所提到的方法就没什么用了.

使用JRockit Mission Control进行性能分析和调优


Mission ControlBEA JRockit JVM自带的一组以极低的开销来监控、管理和分析生产环境中的应用程序的工具。它包括三个独立的应用程序:内存泄漏监测器(Memory Leak Detector)、JVM运行时分析器(Runtime Analyzer)和管理控制台(Management Console)。BEAJRockit R26版本就开始捆绑这个工具套件,目前最新的版本是3.0。最近我们使用其中的Runtime Analyzer对国内某著名行业解决方案进行性分析和调优。


 


JRockit Runtime AnalyzerJRA)是一个JVM分析器,是一个随需应变的“动态记录器”。它记录了Java应用程序和JVM在一段预定的时间内的详细记录。然后通过JRA应用程序对记录下来的文件进行离线分析。所记录的数据包括对方法的调用跟踪、错误的同步、锁定的分析,还有垃圾收集统计信息,优化决策以及对象统计信息和其他重要的应用程序/JVM行为。它的目的是让JRockit开发人员能够找到良好的方法来基于现实应用程序优化JVM,对于帮助客户在生产和开发环境中解决问题十分有用。


 


2.性能数据分析和调优


 


在本次项目中,操作|A和操作B的百人并发脚本执行完成的时间接近两分钟,因此我们使用JRA进行了2分钟(120)的记录。在GC常规信息中,我们发现在短短两分钟时间内,垃圾收集的总数高达365次,而由此造成的暂停时间有42.5秒之多。也就是说35%的执行时间是在做垃圾收集。


 


因为最大堆尺寸已经设置成1024M,对于32位操作系统上的Java应用已经是足够大了(在IA32构架下,由于操作系统给每个进程的最大内存寻址空间为1.8G,因此最大堆尺寸不能超过1.8G),因此堆的大小并不是造成频繁垃圾收集的原因。那么在高并发度的场景下,可能的影响因素很可能是Nursery大小。


 


Nursery 也称为新代,是指运行分代式垃圾收集器时,在堆中分配 新对象 的可用块区域。 Nursery 变满时,会在新垃圾收集中单独对其进行垃圾收集。Nursery 大小决定了新收集的频率和持续时间。较大 Nursery 会降低收集的频率,但是会稍微增加每个新收集的持续时间。 Nursery 之所以具有价值,是因为 Java 应用程序中的大多数对象都是在新代中夭亡的。与收集整个堆相比,应首选从新空间中收集垃圾,因为该收集过程的开销更低,而且在触发收集时,新空间中的大多数对象均已死亡。在新收集过程中,JVM 首先确定 Nursery 中的哪些对象是活动的,此后将它们提升到旧空间,并释放 Nursery,供分配新的小对象使用


 


Nursery的默认缺省值是10M/CPU,对于我们Clovertown服务器来说,只有20M。由于出现频繁收集的情况,那么我们推断是由于Nursery的默认值太低的原因。一方面在高并发用户的场景下,肯定是有大量的新对象产生,那么Nursery的空闲空间很容易就被耗尽。因此Nursery发生垃圾收集频率就会比较高。另一方面更短的垃圾收集间隔会使得新对象在Nursery的存活率提高因为很多新对象可能还没来得及使用完毕就已经发生垃圾收集。这样更多的对象会被提升到旧代,使得旧代的对象也会急剧增加,从而使得旧代发生垃圾收集的频率也增加。


 


因为JRockit JVM可以使用-Xns:<size>来设置Nursery的尺寸,我们要在保证垃圾回收停顿时间(garbage collection-pause)尽可能短的同时,尽量加大Nursery的尺寸,这在创建了大量的临时对象时尤其重要。推荐值是最大堆尺寸的10%,因此我们在JRockit的运行时参数上添加了 –Xns100m。再次运行脚本后,JRA收集的信息显示GC暂停时间骤降到15.3s,次数也有所减少,降到296


 




















Nursery大小


20M(默认值)


100M


GC暂停时间


42.5s


15.3s


垃圾收集的总数


365


296


平均暂停时间


116ms


52ms


 


此外,我们从方法信息中可以看到调用次数最多耗时间最长的两个方法分别是jrockit.vm.Locks.monitorEnterSecondStagecom.ABC.StateManager.makeState两个方法。展开前置任务后发现调用这两个方法最多的方法是com.ABC.SqlQueryAction.query。而jrockit.vm.Locks.monitorEnterSecondStage显然是JRockit实现锁机制的特定的API。因此我们怀疑是对数据库的操作时有资源互斥的现象发现。


 


考虑到高并发用户的场景下,对数据库操作的并发度也很高,因此对数据库连接的争用比较激烈。我们察看了一下当时WebLogic JDBC的配置,发现connection pool的大小只是缺省值20,相对来说偏小了,对性能会有一定的影响。因此我们增大connection pool的大小到100。重新运行测试脚本后发现性能有较大提升。


 


 



















 


JDBC connection size 20  w/ default nursery


JDBC connection size 100 w/ 100M nursery


Increase %


 


操作A


22.125


12.079


83%


操作B


35.195


21.773


62%


 


 


在性能调优完成后,我们又进行了功能测试(回归测试),以验证上述改动没有影响系统的功能性正确。


四、小结


其实利用Mission ControlJava应用进行调优并不难,对吧?希望本次性能分析调优的过程可以给大家一些启发,今后可以应用到日常工作中。

resin clustering

本文主要讲述resin的clustering,简单明了。由于条件限制,只使用一台服务器,运行多个resin来实现clustering,如果多台服务器,只要修改相应的ip地下就可以了。

 

步骤1:

配置好resin.conf,至少可以把web应用运行起来。

 

步骤2:

执行:cp resin.conf resin-web.conf

 

resin.conf作为clustering的配置文件;

resin-web.conf为作web-tier的配置文件;

 

修改resin.conf

把app-tier中的<http address=”*” port=”80″/> 修改为       <!–http address=”*” port=”80″/–>

 

在<!– define the servers in the cluster –>下面增加servers(ip和端口可以自行修改)

 <server id=”a” address=”192.168.1.65″ port=”6800″/>
 <server id=”b” address=”192.168.1.64″ port=”6801″/>

 

修改resin-web.conf


在<!– define the servers in the cluster –>下面增加servers(ip和端口可以自行修改)

 <server id=”a” address=”192.168.1.65″ port=”6800″/>
 <server id=”b” address=”192.168.1.64″ port=”6801″/>

 

把web-tier中的 <http address=”*” port=”9080″/>修改为 <http address=”*” port=”80″/>

 

步骤3:

启动app-tier

java -jar lib/resin.jar -conf conf/resin.conf -server a &

java -jar lib/resin.jar -conf conf/resin.conf -server b &

 

启动web-tier

java -jar lib/resin.jar -conf conf/resin-web.conf -server web-a &

 

打开浏览器输入localhost

看看输出什么?

 

试着把a关闭,再打开locahost看看?

 

成功了吧?

使用JRockit作为Eclipse的Java VM


早些天写了一篇关于调整Eclipse启动的blog,目的还是希望提升Eclipse(包括启动时)的总体性能。 今天,尝试了一下使用BEA JRockit来作为Eclipse的JVM,同时使用JRockit来编译所有的项目。


安装JRockit:


1. 从BEA下载最新的JRockit。 JRockit据说是Wintel平台下最快的Java编译器(以前还有IBM的JDK和Jike,IBM JDK现在是WebSphere的一部分,而最先支持增量编译的Jike已经贡献给Eclipse了,BEA也将会把JRockit贡献给Eclipse。 天下归一啊)。 BEA的JRockit主页在这里,从这里下载最新的JRockit 5.0(我下载的是R26.4.0),大约75MB。


2. 安装JRockit非常容易,不停的”Next”即可。 安装完以后,可以把JAVA_HOME设置为JRockit(可选项)。


配置Eclipse:


1. Eclipse启动参数。 其实就是像前一篇所说的那样,在Eclipse启动参数里面修改-vm的值,使用JRockit来启动Eclipse。比如我修改为:


C:\Java\eclipse\eclipse.exe -vm C:\java\jrockit-R26.4.0-jdk1.5.0_06\ bin\javaw.exe -vmargs -Xms256m -Xmx512m -XXsetgc:singleparpar -XXcompactratio:1


2. 在Eclipse里面,安装JRockit,即在Installed JRE中添加JRockit,并设置为默认。 这样,就可以用JRockit来编译项目了。


至于性能:


Eclipse(我的Eclipse是v3.2 Callisto,含所有的插件,以及Crystal Report, SWT Designer, PowerDesigner,Mylar, FindBugs等插件)启动的时候,比使用Sun JDK 1.5要快一些(但不是快很多很多,毕竟Sun JDK 1.5的性能已经提升了),大约能有10-15%的改进吧。 我的工程一共有95个plug-in项目和1个Web项目。


之后,我可能会尝试一下把JRockit用到Glassfish的服务器上,看看效果如何。


【参考文章】Running BEA Workshop/Eclipse with JRockit 5.0


Technorati : ,
Del.icio.us : ,

Lucene Query Parser

Lucene Query Parser


翻译这篇文章的初衷是希望能更系统的理解Lucene的用法,同时试试自己的翻译水平:)


原文:http://jakarta.apache.org/lucene/docs/queryparsersyntax.html


概述


虽然Lucene提供的API允许你创建你自己的Query(查询语句),但它同时也通过Query Parser(查询分析器)提供了丰富的查询语言。


这个页面提供的是Lucene的Query Parser的语法介绍:一个可通过用JavaCC把一个字符串解释成为Lucene的查询语句的规则。


在选择使用被提供的Query Parser前,请考虑一下几点:


1、如果你是通过编写程序生成一个查询语句,然后通过Query Parser分析,那么你需要认真的考虑是否该直接利用Query的API构造你的查询。换句话说,Query Parser是为那些人工输入的文本所设计的,而不是为了程序生成的文本。


2、未分词的字段最好直接加到Query中,而不要通过Query Parser。如果一个字段的值是由程序生成的,那么需要为这个字段生成一个Query Clause(查询子句)。Query Parser所用的Analyzer是为转换人工输入的文本为分词的。而程序生成的值,比如日期、关键字等,一般都由程序直接生成(?)。


3、在一个查询表单里,通常是文本的字段应该使用Query Parser。所有其他的,比如日期范围、关键字等等,最好是通过Query API直接加入到Query中。一个有有限个值的字段,比如通过下拉表单定义的那些,不应该被加到查询字串中(后面会分析到),而应该被添加为一个TermQuery子句。


分词


一个查询语句是有分词和操作符组成的。这里有两种类型的:单个的分词和短语。


一个单一分词就是一个简单的单词,比如”test”或”hello”。


一个短语就是一组被双引号包括的单词,比如”hello dolly”。


多个分词可以用布尔操作符组合起来形成一个更复杂的查询语句(下面会详细介绍)。


注意:用于建立索引的分析器(Analyzer)将被用于解释查询语句中的分词和短语。因此,合理的选择一个分析器是很重要的,当然这不会影响你在查询语句中使用的分词。


字段


Lucene支持字段数据。当执行一个搜索时,你可以指定一个字段,或者使用默认的字段。字段的名字和默认的字段是取决于实现细节的。


你可以搜索任何字段,做法是输入字段名称,结尾跟上一个冒号 “:” , 然后输入你想查找的分词。


举个例子,让我们假设Lucene的索引包含两个字段,标题和正文,正文是默认字段。如果你想标题为 “The Right Way” 并且正文包含文本 “don’t go this way”的记录的话,你可以输入:


  title:”The Right Way” AND text:go 


或者


  title:”Do it right” AND right


由于正文是默认字段,所以字段指示就没有必要了。


注意:字段的搜索值只能是紧跟在冒号后的一个分词,所以搜索字串:


 title:Do it right


只会找到标题含有 “Do” 的记录。它会在默认字段(这里是正文字段)里查找 “it” 和 “right”。


分词修饰语


Lucene支持对查询分词做修饰以提供一个更广的查询选项。
 
通配符搜索


ucene支持一个或多个字符的通配符搜索。


执行一个字符的通配符搜索可以用 “?”,比如你要搜索 “text” or “test” ,你可以用:


 te?t


执行多个字符的通配符搜索可以用 “*”,比如,搜索 “test” , “tests” 或 “tester” ,你可以用


 test*


你也可以把通配符放在分词中将进行搜索,比如


 te*t


注意:不可以使用 * 或 ? 作为搜索字串的第一个支付。


模糊搜索


Lucene支持基于Levenshtein Distance(一种字符串相似程度算法)或Edit Distance(一种字符串相似程度算法)算法的模糊查询。要执行模糊查询需要用到 “~” 符号,紧随单个分词。比如,用模糊查询搜索一个拼写上和 “roam” 类似的内容:


 roam~


这个搜索可以搜索到像 foam 和 roams 这样的分词。


 “jakarta apache”~10


 近似搜索


Lucene支持根据一个指定的近似程度查找。与模糊搜索类似,在短语的末尾使用 “~” 。


范围搜索


范围搜索可以搜索到那些列的值在被范围搜索所指定的上限和下限之间的记录。范围搜索可以包括或不包括上限和下限。如果不是日期类型的字段,会根据字典的排序来决定上下限。


 mod_date:[20020101 TO 20030101]


这个会搜索 mod_date 字段的值在 20020101 和 20030101 之间的,包括上下限。注意,范围搜索不仅仅是为日期字段准备的,你也可以在非日期的字段上使用它:


 title:{Aida TO Carmen}


这个会搜索 title 字段的值在 Aida 和 Carmen (根据字典的顺序) 的记录,不包括 Aida 和 Carmen 本身。


包含的范围查询是用方括号指定的 “[” 、 “]” ,而不包含的范围查询是有波形括号指定的 “{” 、 “}”。


提高一个分词的相似度


Lucene提供基于被搜索的分词的匹配文档的相似度这一概念。为了提高一个分词的相似度,可以使用 “^”和一个相似因子(一个数字)跟在需要搜索的分词后面。这个匹配因子越高,这个分词所获得相似度就越高。


提高匹配允许你通过提高分词的相似程度来控制一个文档的相似程度。比如,如果你在搜索


 jakarta apache


并且,你希望 “jakarta” 有更高的相似程度,就用让 “^” 和一个匹配因子紧随它。你将输入:


 jakarta^4 apache


这样会使包含分词 jakarta 的文档有更高的相似度(Lucene默认是按照相似度对搜索结果排序的,所以带来的直接影响就是该搜索结果文档排位靠前)。你也可以对短语分词提高匹配度,比如:


 “jakarta apache”^4 “jakarta lucene”


默认的情况下,匹配因子是1。虽然匹配因子必须是正数,当它可以小于1 (比如 0.2)


布尔操作符


布尔操作符允许分词通过逻辑操作符被组合。Lucene支持 AND, “+”, OR, NOT and “-” 作为布尔操作符(注意:布尔操作符必须全部大写)。


OR


OR操作符是默认的连接操作。这意味着如果没有布尔值在两个分词之间的话,OR操作符将被使用。OR操作符连接两个分词,并且搜索包含任意一个分词的文档。这等价于与集合概念中的并集。 “||” 可以替代 OR。


搜索包含 “jakarta apache” 或者 只是 “jakarta” 的文档使用查询语句:


“jakarta apache” jakarta


或者


 “jakarta apache” OR jakarta


AND


AND操作符会匹配那些包含所有分词的文档。这等价与集合概念中的交集。”&&” 可以替代 AND。


搜索既包含 “jakarta apache” 又包含 “jakarta” 的文档使用查询语句:


 “jakarta apache” AND “jakarta lucene”
 
+作为必需操作符,需要 “+” 后的分词必须存在于被搜索文档的某个字段中。


搜索必须含有 “jakarta” 同时可能含有 “lucene” 的文档使用查询语句:


 +jakarta apache


NOT


NOT操作符会排除包含NOT后的分词的文档。这等价于集合概念中的补集。”!” 可以替代 NOT。


搜索包含”jakarta apache” 但不包含 “jakarta lucene”的文档使用查询语句:


 “jakarta apache” NOT “jakarta lucene”


注意:NOT操作符不能只和一个分词一起使用,下面这个查询不会返回任何结果:


 NOT “jakarta apache”


“-“作为禁止操作符将不匹配包含”-“所跟随的分词的文档。


搜索包含”jakarta apache” 而不包含 “jakarta lucene” 使用查询语句:


 “jakarta apache” -“jakarta lucene”
 
分组


Lucene支付使用圆括号把子句分组形成子查询。如果你想控制一个查询语句的布尔逻辑,那么这个将非常有用。


查找包含 “jakarta” 或者 “apache” , 同时包含 “website” 使用查询语句:


 (jakarta OR apache) AND website


这个将会排除任何混淆,并确定被搜索的文档必须包括 website 这个分词并且包括 jakarta 或者 apache 中的一个。
 
字段分组


Lucene支持针对单一字段使用圆括号对多个子句分组。


要搜索一个标题既包含分词”return” , 又包含短语 “pink panther” 使用查询语句:


特殊字符转换


作为查询语法的一部分,Lucene支持特殊字符。当前特殊支付包括:


+ – && || ! ( ) { } [ ] ^ ” ~ * ? : \


要转化一个特殊字符,就在要转化的字符前加上 “\” , 例如,搜索 (1+1):2 使用查询语句:


 \(1\+1\)\:2


原文:http://jakarta.apache.org/lucene/docs/queryparsersyntax.html


Google Guice Example

最近在研究Google 的 Guice,感觉用起来挺简单的,入门应该比Spring要容易的多,这也许是因为Guice刚刚出世,很功能还没有完善吧,但据说Google内部已经在用了,我想再过一段时间取Spring而代之也不是没有可能,至少也能跟Spring平分天下吧。

下面是一个简单的小实例

1.业务逻辑接口

package com.hawkunion.guice;

/**

*

* @author 杜庆明

*/

public interface Work {

     public void sayHello(String userName);

}

2.实现接口的业务逻辑

package com.hawkunion.guice;

/**

*

* @author 杜庆明

*/

public class WorkImpl implements Work{

   

     public void sayHello(String userName) {

         System.out.println(“Hello! 欢迎你:”+userName);

     }

   

}

3.要用到业务逻辑的类

package com.hawkunion.guice;

import com.google.inject.Inject;

/**

*

* @author 杜庆明

*/

public class RunWorkClass {

   

     private Work work = null;

   

     @Inject

     public void setWork(Work workimpl){

         this.work = workimpl;

     }

   

     public void runWork(){

         work.sayHello(“阿杜”);

     }   

}

4.Module

package com.hawkunion.guice;

import com.google.inject.Binder;

import com.google.inject.Module;

/**

*

* @author 杜庆明

*/

public class MyModule implements Module {

     public void configure(Binder binder) {

         binder.bind(Work.class).to(WorkImpl.class);

     }

}

5.测试

package com.hawkunion.guice;

import com.google.inject.Guice;

import com.google.inject.Injector;

import com.google.inject.Module;

/**

*

* @author 杜庆明

*/

public class TestWork {

   

     public static void main(String args[]){

         RunWorkClass runWorkClass = new RunWorkClass();

         Module module = new MyModule();

       

         Injector in = Guice.createInjector(module);//把module给Guice

         in.injectMembers(runWorkClass);//让Guice把需要注入的业务逻辑注入给对象

         runWorkClass.runWork();//   运行work

     }

}

linux下crontab时间的格式说明

cron 配置计划任务的书写格式


说明:

第一段应该定义的是:分钟,表示每个小时的第几分钟来执行。范围是从0-59
第二段应该定义的是:小时,表示从第几个小时来执行,范围是从0-23
第三段应该定义的是:日期,表示从每个月的第几天执行,范围从1-31
第四段应该定义的是:月,表示每年的第几个月来执行,范围从1-12
第五段应该定义的是:周,表示每周的第几天执行,范围从0-6,其中 0表示星期日。
每六段应该定义的是:用户名,也就是执行程序要通过哪个用户来执行,这个一般可以省略;
第七段应该定义的是:执行的命令和参数。

注:其中用户名可是省略,用户名定义的是程序用哪个用户来执行,比如mysql服务器,我们可以定义成以mysql用户来启动、停止、重新 启动,这时要写上用户名;不过对于cron来说意义不是太大,因为每个用户都有自己的cron配置文件。有些程序的启动必须用到root用户,这时我们就 可以修改root用户的cron配置文件就行了。在每个用户的配置文件中,不必指定用户名。


我们可以把计划任务写在全局性配置文件中,如果您想把一个计划放入全局性配置文件中,就得改发行版所对应的cron全局配置文件,比如Fedora 5的全局性配置文件是/etc/crontab文件;


每个用户也能定义自己的cron配置文件,用crontab -e 命令来定义;


举一例:让机器在每天8点30分重新启动;


关于时间格式中有7个字段。我们可以直接更改或者添加,当然这是系统任务调用。举个例子,比如我在每天的早上8点30分重新启动机器,就可以在
/etc/crontab中加入下面的两句,第一句就是注释了。以#号开始,后面写一个自己能知道这是什么任务的备注;


# reboot OS
30 8 * * * root /sbin/reboot

第一段应该定义的是:分钟,表示每个小时的第几分钟来执行。范围是从0-59
第二段应该定义的是:小时,表示从第几个小时来执行,范围是从0-23
第三段应该定义的是:日期,表示从每个月的第几天执行,范围从1-31
第四段应该定义的是:月,表示每年的第几个月来执行,范围从1-12
第五段应该定义的是:周,表示每周的第几天执行,范围从0-6,其中 0表示星期日。
每六段应该定义的是:用户名,也就是执行程序要通过哪个用户来执行,这个一般可以省略;
第七段应该定义的是:执行的命令和参数。


对比上面的例子就知道,30是不是分钟??8是不是小时?如果有*代表的地方,表示全部,也就是说,每个月,每天,每星期都要执行。root 表示用root用户执行,命令是/sbin/reboot ,也就是说,系统在每天 8点30分重新启动;


我们可以把每天8点30分重新启动的计划任务写入cron全局性配置文件中,也可以定义在root用户自己的cron配置文件中。如果定义在 root自己的配置文件,请用root身份来执行 crontab -e 来修改配置文件;crontab -e 进入修改配置文件的过程,其实和vi的用法一样.


64位linux编译软件注意事项

  64位的linux编译软件时最好指定/usr/lib64目录以提高系统性能
  ./configure LDFLAGS=-L/usr/lib64



An attempt to link the coNCePTuaL run-time library on one x86-64 Linux platform aborted with the following error:



     /usr/lib/libpopt.so: could not read symbols: File in wrong format
An attempt on another x86-64 Linux platform aborted with a similar error:



     /usr/lib/libpopt.so: could not read symbols: Invalid operation
The problem on both platforms turned out to be that the /usr/lib/libpopt.so is a 32-bit binary and could not be linked with a 64-bit library. The solution was to configure with ./configure LDFLAGS=-L/usr/lib64 to indicate that libraries should be read from /usr/lib64 instead of the default of /usr/lib64.