使用IntrospectorCleanupListener 解决quartz引起的内存泄漏

“在服务器运行过程中,Spring不停的运行的计划任务和OpenSessionInViewFilter,使得Tomcat反复加载对象而产生框架并用时可能产生的内存泄漏,则使用IntrospectorCleanupListener作为相应的解决办法。”

对于这一句话,引用关于IntrospectorCleanupListener一段解释:

引用
spring中的提供了一个名为 org.springframework.web.util.IntrospectorCleanupListener的监听器。它主要负责处理由  JavaBeans Introspector的使用而引起的缓冲泄露。spring中对它的描述如下:它是一个在web应用关闭的时候,清除JavaBeans Introspector的监听器.web.xml中注册这个listener.可以保证在web 应用关闭的时候释放与掉这个web 应用相关的class loader 和由它管理的类如果你使用了JavaBeans Introspector来分析应用中的类,Introspector 缓冲中会保留这些类的引用.结果在你的应用关闭的时候,这些类以及web 应用相关的class loader没有被垃圾回收.不幸的是,清除Introspector的唯一方式是刷新整个缓冲.这是因为我们没法判断哪些是属于你的应用的引用.所以删 除被缓冲的introspection会导致把这台电脑上的所有应用的introspection都删掉.需要注意的是,spring 托管的bean不需要使用这个监听器.因为spring它自己的introspection所使用的缓冲在分析完一个类之后会被马上从javaBeans Introspector缓冲中清除掉.应用程序中的类从来不直接使用JavaBeans Introspector.所以他们一般不会导致内部查看资源泄露.但是一些类库和框架往往会产生这个问题.例如:Struts 和Quartz.单个的内部查看泄漏会导致整个的web应用的类加载器不能进行垃圾回收.在web应用关闭之后,你会看到此应用的所有静态类资源(例如单 例).这个错误当然不是由这个类自 身引起的.

用法很简单,就是在web.xml中加入:
<listener>
<listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>
</listener>

只知道servlet标准不允许在web容器内自行做线程管理,quartz的问题确实存在。  

对于Web容器来说,最忌讳应用程序私自启动线程,自行进行线程调度,像Quartz这种在web容器内部默认就自己启动了10线程进行异步job调度的框架本身就是很危险的事情,很容易造成servlet线程资源回收不掉,所以我一向排斥使用quartz。

quartz还有一个问题就是不支持cluster。导致使用quartz的应用都没有办法做群集。

如果是我的话,我采取的办法就是自己单独启动一个Job Server,来跑job,不会部署在web容器中。

MVC设置启动页

        设置启动页需要在路由中添加一段代码:

        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute(“{resource}.axd/{*pathInfo}”);

            routes.MapRoute(
                name: “Default”,
                url: “{controller}/{action}/{id}”,
                defaults: new { controller = “Login”, action = “Index”, id = UrlParameter.Optional }
            ).DataTokens.Add(“Area”, “PseronnalMenanger”);
        }

JNA(Java Native Access)学习入门

Java Native Access 项目 在Java.net上,你可以到这个网站上现在这个项目的代码和在线帮助文档。虽然在下载有5个相关的jar文件,在本文中你仅仅需要下载其中的jna.jar和example.jar。

Jna.jar 提供基本的、运行这些示例文件必需的jna运行环境。这个jna.jar文件除了有Unix、Linux、Windows和Mac OS X平台相关的JNT-friendly本地库外,还包含其他几个类包。每一个本地库都是用来访问相对应平台下的本地方法的。

         example.jar包含了不同的示例来表明JNA的用途。其中的一个例子是使用JNA来实现一个在不同平台下的透明视窗技术的API。在文章最后的示 例中将要展示如何使用这个API修复上个月的文章关于VerifyAge2应用中辨认透明效果的问题。

获取本地时间(Get local time)

如果你在Java Native Access 首页 看过“JNA如何入门”,你就会知道一个很简单的关于调用Windows 平台下的API函数:GetSystemTime()的JNA示例。这个不完整的例子只是展示了JNA的基本特点。(在例子的基础上,我做了一个更完整的 基于Windows的例子来介绍JNA)我在 Windows平台下完善了这个例子来介绍JNA。

第一例子基于Windows GetLocalTime() API函数返回本地当前的时间和日期。和GetSystemTime()不同的是,返回的时间/日期是协调通用时间 (UTC)格式的,GetLocalTime()返回的时间/日期信息的格式是根据当前时区来表示。

在一个Java程序中使用JNA调用GetLocalTime,你需要知道这个函数所在的Windows平台下的动态链接库(DLL)的名称(和可能所在 的地理区域)。我们发现GetLocalTime()和GetSystemTime在同一个DLL文件中:kernel32.dll。你还需要知道 GetLocalTime()在C语言环境中的申明。申明如下Listing 1:

Listing 1. GetLocalTime在C语言中的申明

typedef struct {     WORD wYear;     WORD wMonth;     WORD wDayOfWeek;     WORD wDay;     WORD wHour;     WORD wMinute;     WORD wSecond;     WORD wMilliseconds; } SYSTEMTIME, *LPSYSTEMTIME;  VOID GetLocalTime(LPSYSTEMTIME lpst);
这个基于C语言的申明表明传到这个函数的参数数目和类型。在这个例子中,只有一个参数---一个指向Windows SYSTEMTIME结构体的指针。而且,每个结构体成员的类型是16bit长度的无符号整型。根据这些信息,你能够创建一个完全描述 GetLocalTime()函数的接口,如Listing 2中所示:  Listing 2. Kernel32.java     
// Kernel32.java  import com.sun.jna.*; import com.sun.jna.win32.*;  public interface Kernel32 extends StdCallLibrary {     public static class SYSTEMTIME extends Structure     {        public short wYear;        public short wMonth;        public short wDayOfWeek;        public short wDay;        public short wHour;        public short wMinute;        public short wSecond;        public short wMilliseconds;     }      void GetLocalTime (SYSTEMTIME result); }

Kernel32 接口(The Kernel32 interface)

因为JNA使用通过一个接口来访问某个库中的函数,Listing 2表示了一个描述GetLocalTime()的接口。根据约定,我把接口命名为Kernel32是因为GetLocalTime()在Windows的kernel32.dll库。

这个接口必须继承com.sun..jna.Library接口。因为Windows API函数遵循stdcall调用协议(stdcall calling convention),为Windows API申明的接口也必须继承com.sun.jna.win32. StdCallLibrary接口。因此这个接口共继承了Library 和 com.sun.jna.win32.StdCall两个接口。

在前面,你已经知道了GetLocalTime() 需要一个指向SYSTEMTIME结构体的指针作为它唯一的参数。因为Java不支持指针,JNA是通过申明一个 com.sun.jna.Structure的子类来代替的。根据java文档中抽象类的概念,在参数环境中,Structure相当于C语言的 struct*。

在SYSTEMTIME类中的字段和C结构体中的相对应的属性字段的顺序是一一对应的。保证字段顺序的一致性是非常重要的。例如,我发现交换wYear和wMonth会导致wYear和wMonth值互换。

每个字段在java中是short integer类型的。按照JNA首页上 “默认类型映射”章节给出的提示,这个short integer分配类型是正确。然而,我们应该知道一个重要的区别:Windows平台下的WORD类型等同于C语言环境中的16-bit的无符号的 short integer,而java中short integer是16-bit有符号的short integer。

一个类型映射的问题

通过比较一个API 函数返回的整型值,你会发现Windows/C 语言的无符号整型和Java语言的有符号整型的JNA类型映射是有问题的。在比较的过程中,如果你不细心,那么错误的执行过程可能导致决定性情况。导致这种后果是因为忘记任何数值的符号位的确定是根据:在无符号整型的情况下会被解释为正号,而在有符号整型的进制中被理解为负号的。

通过Kernel32获取本地时间(Access the local time with Kernel32)

JNA 首页上的GetSystemTime()示例已经表明必须使用预先申明的接口为本地库分配一个实例对象。你可以通过com.sun.jna.Native 类中静态公用方法loadLibrary(String name, Class interfaceClass)来完成上述的目标。Listing 3 所示:

Listing 3. LocalTime.java

// LocalTime.java  import com.sun.jna.*;  public class LocalTime {     public static void main (String [] args)     {        Kernel32 lib = (Kernel32) Native.loadLibrary ("kernel32",                                                      Kernel32.class);        Kernel32.SYSTEMTIME time = new Kernel32.SYSTEMTIME ();        lib.GetLocalTime (time);        System.out.println ("Year is "+time.wYear);        System.out.println ("Month is "+time.wMonth);        System.out.println ("Day of Week is "+time.wDayOfWeek);        System.out.println ("Day is "+time.wDay);        System.out.println ("Hour is "+time.wHour);        System.out.println ("Minute is "+time.wMinute);        System.out.println ("Second is "+time.wSecond);        System.out.println ("Milliseconds are "+time.wMilliseconds);     } }

Listing 3 执行Kernel32 lib = (Kernel32) Native.loadLibrary ("kernel32", Kernel32.class);来分配一个Kernel32实例对象并且装载kernel32.dll。因为kernel32.dll是Windows 平台下标准的dll文件,所以不要指定访问这个库的路径。然而,如果找不到这个dll文件,loadLibrary()会抛出一个 UnsatisfiedLinkError异常。

Kernel32.SYSTEMTIME time = new Kernel32.SYSTEMTIME ();创建了一个SYSTEMTIME结构体的示例。初始化后下面是lib.GetLocalTime (time);,这句话使用本地的时间/日期来给这个实例赋值。几个System.out.println()语句是输出这些值。

编译和运行这个应用(Compile and run the application)

这部分很容易。假设jna.jar、Kernel32.java和LocalTime.java是放在当前文件夹中,调用java –cp jna.jar;. LocalTime.java来编译这个应用的源代码。如果在Windows平台下,调用invoke java –cp jna.jar;. LocalTime 来运行这个应用。你可以得到类似与Listing 4的输出结果:

Listing 4. 从LocalTime.java生成的输出

Year is 2007 Month is 12 Day of Week is 3 Day is 19 Hour is 12 Minute is 35 Second is 13 Milliseconds are 156

获取操纵杆信息(Accessing joystick device info)

上面的例子已经介绍了JNA,但是这个获取本地时间和日期的例子并没有很好的利用这个技术,甚至也没有体现JNI的价值。Java语言中的 System.currentTimeMillis()函数已经以毫秒的格式返回了这些信息。因为Java语言没有为游戏控制器提供API,所以获取操纵杆的信息更适合JNA的使用。

例如,你要构建一个平台无关的Java库,而且这些库使用JNA调用Linux, Mac OS X, Windwos和Unix平台中本地的操纵杆API。为了简洁和方便起见,这个例子仅仅是调用Windows平台下的操纵杆API。而且我将重点介绍这个 API很小的一部分。

类似GetLocalTime(),第一步是辨别出操作杆API的DLL,这个DLL是winmm.dll,和 kernel32.dll在同一个文件夹中,它包含了操作杆的API和其他的多媒体APIs。还需知道要被使用的操作杆函数基于C语言的声明。这些函数声明已经在Listing 5中列出来了。

Listing 5. C-based declarations for some Joystick API functions

#define MAXPNAMELEN 32  typedef struct {     WORD   wMid;                   // manufacturer identifier     WORD   wPid;                   // product identifier     TCHAR szPname  MAXPNAMELEN   ; // product name     UINT   wXmin;                  // minimum x position     UINT   wXmax;                  // maximum x position     UINT   wYmin;                  // minimum y position     UINT   wYmax;                  // maximum y position     UINT   wZmin;                  // minimum z position     UINT   wZmax;                  // maximum z position     UINT   wNumButtons;            // number of buttons     UINT   wPeriodMin;             // smallest supported polling interval when captured     UINT   wPeriodMax;             // largest supported polling interval when captured } JOYCAPS, *LPJOYCAPS;  MMRESULT joyGetDevCaps(UINT IDDevice, LPJOYCAPS lpjc, UINT cbjc);  UINT joyGetNumDevs(VOID);

操作杆API的函数(Functions of the Joystick API)

在Windows平台下是通过以joy作为函数名开始的函数以及被各种函数调用的结构体来实现操作杆API的。例如,joyGetNumDevs()返回的是这个平台下支持的操作杆设备最多的数目;joyGetDevCaps()返回的是每个连接上的操纵杆的质量。

joyGetDevCaps()函数需要3个参数:
* 处在0到joyGetNumDevs()-1之间的操作杆ID
* 保存返回的质量信息的JOYCAPS结构体的地址
* JOYCAPS结构体的字节大小
虽然它的结果不同,这个函数返回的是一个32位的无符号整型结果,而且0表示一个已经连接的操纵杆。

JOYCAPS 结构体有3种类型。Windows平台下的WORD(16位无符号短整型)类型对应的是Java语言中16位有符号短整型。除此之外,Windows下的 UINT(32位无符号整型)类型是和Java语言中32位有符号整型相对应的。而Windows平台上的text character就是TCHAR类型。

微软通过TCHAR类型使开发人员能够从ASCII类型的函数参数平滑的转移到Unicode字符类型的函数参数上。而且,拥有text类型参数的函数的实现是通过宏转变为对应的ASCII或者wide-character的函数。例如,joyGetDevCaps()是一个对应joyGetDevCapsA() 和 joyGetDevCapsW()的宏。

使用TCHAR(Working with TCHAR)

使用TCHAR和将TCHAR转变的宏会导致基于C语言的申明向基于JNA接口的转换
变得有点复杂—你在使用ASCII或者wide-character版本的操纵杆函数吗?两种版本都在如下的接口中展示了:

Listing 6. WinMM.java

// WinMM.java  import com.sun.jna.*; import com.sun.jna.win32.*;  public interface WinMM extends StdCallLibrary {     final static int JOYCAPSA_SIZE = 72;      public static class JOYCAPSA extends Structure     {        public short wMid;        public short wPid;        public byte szPname [] = new byte [32];        public int wXmin;        public int wXmax;        public int wYmin;        public int wYmax;        public int wZmin;        public int wZmax;        public int wNumButtons;        public int wPeriodMin;        public int wPeriodMax;     }      int joyGetDevCapsA (int id, JOYCAPSA caps, int size);      final static int JOYCAPSW_SIZE = 104;      public static class JOYCAPSW extends Structure     {        public short wMid;        public short wPid;        public char szPname [] = new char [32];        public int wXmin;        public int wXmax;        public int wYmin;        public int wYmax;        public int wZmin;        public int wZmax;        public int wNumButtons;        public int wPeriodMin;        public int wPeriodMax;     }      int joyGetDevCapsW (int id, JOYCAPSW caps, int size);      int joyGetNumDevs (); }

Listing 6没有介绍JNA的新特性。实际上,JNA强调了对本地库的接口命名规则。同时,还展示了如何将TCHAR映射到Java语言中的byte和char数组。最后,它揭示了以常量方式声明的结构体的大小。Listing 7展示了当调用joyGetDevCapsA() 和 joyGetDevCapsW()时如何使用这些常量。

Listing 7. JoystickInfo.java

// JoystickInfo.java  import com.sun.jna.*;  public class JoystickInfo {     public static void main (String [] args)     {        WinMM lib = (WinMM) Native.loadLibrary ("winmm", WinMM.class);        int numDev = lib.joyGetNumDevs ();         System.out.println ("joyGetDevCapsA() Demo");        System.out.println ("---------------------/n");         WinMM.JOYCAPSA caps1 = new WinMM.JOYCAPSA ();        for (int i = 0; i < numDev; i++)             if (lib.joyGetDevCapsA (i, caps1, WinMM.JOYCAPSA_SIZE) == 0)             {                 String pname = new String (caps1.szPname);                 pname = pname.substring (0, pname.indexOf ('/0'));                 System.out.println ("Device #"+i);                 System.out.println ("   wMid = "+caps1.wMid);                 System.out.println ("   wPid = "+caps1.wPid);                 System.out.println ("   szPname = "+pname);                 System.out.println ("   wXmin = "+caps1.wXmin);                 System.out.println ("   wXmax = "+caps1.wXmax);                 System.out.println ("   wYmin = "+caps1.wYmin);                 System.out.println ("   wYmax = "+caps1.wYmax);                 System.out.println ("   wZmin = "+caps1.wZmin);                 System.out.println ("   wZmax = "+caps1.wZmax);                 System.out.println ("   wNumButtons = "+caps1.wNumButtons);                 System.out.println ("   wPeriodMin = "+caps1.wPeriodMin);                 System.out.println ("   wPeriodMax = "+caps1.wPeriodMax);                 System.out.println ();             }         System.out.println ("joyGetDevCapsW() Demo");        System.out.println ("---------------------/n");         WinMM.JOYCAPSW caps2 = new WinMM.JOYCAPSW ();        for (int i = 0; i < numDev; i++)             if (lib.joyGetDevCapsW (i, caps2, WinMM.JOYCAPSW_SIZE) == 0)             {                 String pname = new String (caps2.szPname);                 pname = pname.substring (0, pname.indexOf ('/0'));                 System.out.println ("Device #"+i);                 System.out.println ("   wMid = "+caps2.wMid);                 System.out.println ("   wPid = "+caps2.wPid);                 System.out.println ("   szPname = "+pname);                 System.out.println ("   wXmin = "+caps2.wXmin);                 System.out.println ("   wXmax = "+caps2.wXmax);                 System.out.println ("   wYmin = "+caps2.wYmin);                 System.out.println ("   wYmax = "+caps2.wYmax);                 System.out.println ("   wZmin = "+caps2.wZmin);                 System.out.println ("   wZmax = "+caps2.wZmax);                 System.out.println ("   wNumButtons = "+caps2.wNumButtons);                 System.out.println ("   wPeriodMin = "+caps2.wPeriodMin);                 System.out.println ("   wPeriodMax = "+caps2.wPeriodMax);                 System.out.println ();             }     } }

尽管和LocalTime这个示例类似,JoystickInfo执行WinMM lib = (WinMM) Native.loadLibrary ("winmm", WinMM.class);这句话来获取一个WinMM的实例,并且载入winmm.dll。它还执行WinMM.JOYCAPSA caps1 = new WinMM.JOYCAPSA (); 和 WinMM.JOYCAPSW caps2 = new WinMM.JOYCAPSW ();初始化必需的结构体实例。

编译和运行这个程序(Compile and run the application)

假如jna.jar,WinMM.java和JoystickInfo.java在同一个文件夹中,调用 javac -cp jna.jar;. JoystickInfo.java 来编译这个应用的源代码。
在windows平台下,调用java -cp jna.jar;. JoystickInfo就可以运行这个应用程序了。如果没有操纵杆设备,你应该得到Listing 8中的输出。

将C语言中的string类型转换为Java语言的String类型

pname = pname.substring (0, pname.indexOf ('/0')); 这段代码将一个C string 转换成了Java string. 如果不使用这个转换,C语言的string结束符’/0’和string后面的无用字符都会成为Java语言中String实例对象的内容。

Listing 8. 输出操纵杆信息(Output of JoystickInfo)

joyGetDevCapsA() Demo ---------------------  joyGetDevCapsW() Demo ---------------------

上面的输出是因为每次调用joyGetDevCap()返回的是一个非空值,这表示没有操纵杆/游戏控制器设备或者是出现错误。为了获取更多有意思的输出,将一个设备连接到你的平台上并且再次运行JoystickInfo。如下,将一个微软SideWinder即插即用游戏触摸板设备联上之后我获取了如下的输出:

Listing 9. 操纵杆连接上之后的运行结果(Output after running JoystickInfo with a joystick attached)

joyGetDevCapsA() Demo ---------------------  Device #0    wMid = 1118    wPid = 39    szPname = Microsoft PC-joystick driver    wXmin = 0    wXmax = 65535    wYmin = 0    wYmax = 65535    wZmin = 0    wZmax = 65535    wNumButtons = 6    wPeriodMin = 10    wPeriodMax = 1000  joyGetDevCapsW() Demo ---------------------  Device #0    wMid = 1118    wPid = 39    szPname = Microsoft PC-joystick driver    wXmin = 0    wXmax = 65535    wYmin = 0    wYmax = 65535    wZmin = 0    wZmax = 65535    wNumButtons = 6    wPeriodMin = 10    wPeriodMax = 1000

窗口透明度(Transparent windows)

在这系列文章中上篇文章是关于Bernhard Pauler's 气泡提示(balloontip)工程的。我构建了一个叫做VerifyAge的、包含有一个气泡提示的GUI应用。Figure 1中显示了这个GUI应用的一个小问题:这个气泡提示的没有经过修饰的对话框部分遮住了应用窗口的一部分边框,导致了无法点击这个边框的最小化和最大化按钮,并且使整个GUI很难看.
JNA(Java Native Access)学习入门
尽管未修饰部分的对话框不能显示气泡提示的透明度,java语言不支持窗口透明度。幸运的是,我们可以通过使用com.sun.jna.examples.WindowUtils类调用JNA的examples.jar文件来解决这个问题。
WindowUtils 提供在Unix,Linux,Mac OS X和Windows平台上使用JNA’s来实现窗口透明的工具方法。例如, public static void setWindowMask(final Window w, Icon mask) 让你根据像素而不是通过预定的掩罩(mask)参数来选取某部分的窗口。这个功能将在Listing 10中展示:

Listing 10. Using JNA to render a window transparent

 // Create a mask for this dialog. This mask has the same shape as the // dialog's rounded balloon tip and ensures that only the balloon tip // part of the dialog will be visible. All other dialog pixels will // disappear because they correspond to transparent mask pixels.  // Note: The drawing code is based on the drawing code in // RoundedBalloonBorder.  Rectangle bounds = getBounds (); BufferedImage bi = new BufferedImage (bounds.width, bounds.height,                                        BufferedImage.TYPE_INT_ARGB); Graphics g = bi.createGraphics (); g.fillRoundRect (0, 0, bounds.width, bounds.height-VERT_OFFSET,                   ARC_WIDTH*2, ARC_HEIGHT*2); g.drawRoundRect (0, 0, bounds.width-1, bounds.height-VERT_OFFSET-1,                   ARC_WIDTH*2, ARC_HEIGHT*2); int [] xPoints = { HORZ_OFFSET, HORZ_OFFSET+VERT_OFFSET, HORZ_OFFSET }; int [] yPoints = { bounds.height-VERT_OFFSET-1, bounds.height-VERT_OFFSET                     -1, bounds.height-1 }; g.fillPolygon (xPoints, yPoints, 3); g.drawLine (xPoints [0], yPoints [0], xPoints [2], yPoints [2]); g.drawLine (xPoints [1], yPoints [1], xPoints [2], yPoints [2]); g.dispose (); WindowUtils.setWindowMask (this, new ImageIcon (bi));

在Listing 10中的代码段是从本文代码文档(code archive )里的加强版的VerifyAge2 应用中的TipFrame的构造函数结尾部分摘录的。这个构造函数定义了围绕提示气泡的掩罩(mask)的形状,在这个形状范围里描绘不透明的像素。
假如你当前文件夹中有examples.jar, jna.jar, 和 VerifyAge2.java,调用 javac -cp examples.jar;balloontip.jar VerifyAge2.java 来编译源文件.然后调用java -Dsun.java2d.noddraw=true -cp examples.jar;balloontip.jar;. VerifyAge2运行这个应用. Figure 2 展示了透明示例.
JNA(Java Native Access)学习入门

总结(In conclusion)

JNA项目有很长的历史了(追溯到1999年),但是它第一次发布是在2006年11月。从此以后它慢慢的被需要将本地C代码整合到Java工程中的开发者注意到了。因为JNA能够用来解决JuRuby中常见一个问题:缺乏对POSIX调用的支持(lack of support for POSIX calls ),它也在JRuby程序员中掀起些波浪。JNA也同样被作为实现用低级C代码继承Ruby的一种解决方案(extending Ruby with low-level C code )。
我喜欢使用JNA来工作,相信你也会发现它比使用JNI来访问本地代码更简单、更安全。无需多言,JNA还有更多的特性在本文中没有体现出来。查阅它的资源部分:获取这个开源java项目更多的信息(learn more about this open source Java project )。用它做demo,而且在论坛(discussion forum )上共享你的经验。 下一个月我会带着另一个开源项目回来的,这个开源项目会给你每天的java开发带来益处。

附录:WindowUtils.setWindowMask()的替代品

在刚刚写完这篇文章后,我发现java语言支持在6u10版本中支持窗口的透明和形状定制。读完Kirill Grouchnikov的博客后,我用WindowUtils.setWindowMask()的替代品修改了VerifyAge2,如下:

// Create and install a balloon tip shape to ensure that only this part // of the dialog will be visible.  Rectangle bounds = getBounds (); GeneralPath gp; gp = new GeneralPath (new RoundRectangle2D.Double (bounds.x, bounds.y,                                                     bounds.width,                                                     bounds.height-                                                     VERT_OFFSET,                                                     ARC_WIDTH*2-1,                                                     ARC_HEIGHT*2-1)); gp.moveTo (HORZ_OFFSET, bounds.height-VERT_OFFSET); gp.lineTo (HORZ_OFFSET, bounds.height); gp.lineTo (HORZ_OFFSET+VERT_OFFSET+1, bounds.height-VERT_OFFSET); AWTUtilities.setWindowShape (this, gp);

这段代码使用新类AWTUtilities(在com.sun.awt包中),而且public void setWindowShape(Window w, Shape s)函数将TipFrame和JDialog窗口设置气泡形状。

作者资料

Jeff Friesen 是一名自由的软件开发人员,同时也是Java技术领域的的教育者。在他的javajeff.mb.ca网站上可以获取他发布的所有的Java文章和其他资料。

原文链接

Open source Java projects: Java Native Access

Injecting Spring Beans into Java Servlets

If you are working in a Java Web Application and you are using Spring IoC Container in your application, there is a chance that you might have to inject Spring Beans into a Java Servlet.

Since there is not a direct way to inject Spring Beans into a Java Servlet, you might try to lookup the Spring Beans from the Spring Context within your servlet and assign the dependencies which means that part of injection would no more be IoC and you would be looking for some concise way of doing this.

To solve this problem, Spring provides a trick which is a class called  org.springframework.web.context.support.HttpRequestHandlerServlet which consists of two behaviors:

  1. It is-a javax.servlet.http.HttpServlet – completes one of the requirements.
  2. and, it is a wrapper around org.springframework.web.HttpRequestHandler which has to be Spring Bean configured with bean injections, if any, to achieve the second goal (dependency injection). This is an interface which has a method called handleRequest(HttpServletRequest request, HttpServletResponse response) which the HttpRequestHandlerServlet delegates to while serving a request. So you need to implement this interface and have the dependencies wired in that class.

Note! Your servlet name (1) and you bean id (2) must match because HttpRequestHandlerServlet uses the servlet name to look up the beans form the context.

Now let’s look at an example:

  • Write your Spring bean (which is also a request handler / implements HttpRequestHandler). This bean should be configured to be component scanned. In the example below, this bean has a service called HelloService wired using Spring DI annotation @Autowired which will be injected by the Spring IoC container.
/** * AnnotatedHttpServletRequestHandler.java * Feb 15, 2012 */  package  com.sourceallies.spring.noxml.demo.web.servlet.handler ;    import  java.io.IOException ;  import  java.io.PrintWriter ;  import  java.util.logging.Logger ;    import  javax.servlet.ServletException ;  import  javax.servlet.http.HttpServletRequest ;  import  javax.servlet.http.HttpServletResponse ;    import  org.springframework.beans.factory.annotation.Autowired ;  import  org.springframework.stereotype.Component ;  import  org.springframework.web.HttpRequestHandler ;    import  com.sourceallies.spring.noxml.demo.service.HelloService ;    /** * @author Lal * */  @Component ( "annotatedServletHandler" )  public  class  AnnotatedHttpServletRequestHandler implements  HttpRequestHandler {    private  static  final  Logger LOGGER =  Logger.getLogger ( AnnotatedHttpServletRequestHandler.class .getName ( ) ) ;    @Autowired private  HelloService helloService;    @Override public  void  handleRequest( HttpServletRequest request, HttpServletResponse response)  throws  ServletException, IOException  {  response.setContentType ( "text/html" ) ;  PrintWriter  writer =  response.getWriter ( ) ;  writer.write ( "<h1>Spring Beans Injection into Java Servlets!</h1><h2>"  +  helloService.sayHello ( "World" )  +  "</h2>" ) ;  } 

  • Write your Servlet. This servlet class extends org.springframework.web.context.support.HttpRequestHandlerServlet.
package  com.sourceallies.spring.noxml.demo.web.servlet ;    import  javax.servlet.annotation.WebServlet ;    import  org.springframework.web.context.support.HttpRequestHandlerServlet ;    /** * Servlet implementation class AnnotatedHttpServlet */  @WebServlet( description =  "Http Servlet using pure java / annotations" , urlPatterns =  {  "/annotatedServlet"  } , name =  "annotatedServletHandler" )  public  class  AnnotatedHttpServlet extends  HttpRequestHandlerServlet {    private  static  final  long  serialVersionUID =  1L;  } 

Notice the @Component(”annotatedServletHandler”) and @WebServlet(…, name = “annotatedServletHandler”). The bean id and the servlet name are exactly same.

Now, this will absolutely work and in fact you got access to Spring Beans from the Servlet provided that your Spring bean annotatedServletHandler was registered in the Spring Root Context (the context that is setup using org.springframework.web.context.ContextLoaderListener ). However, it could be possible that your Web related beans, annotatedServletHandler for instance, are registered in the Spring Dispatcher Context. If this is the case, the previous example would not work. This leads to a situation where you have to implement your own HttpRequestHandlerServlet that could lookup both root and dispatcher contexts.

Here is an implementation of such a HttpRequestHandlerServlet which is pretty much similar to what Spring provides but with added functionality to support dispatcher context as well.

package  com.sourceallies.spring.noxml.demo.web.servlet.framework ;    import  java.io.IOException ;  import  java.util.logging.Logger ;    import  javax.servlet.ServletException ;  import  javax.servlet.http.HttpServlet ;  import  javax.servlet.http.HttpServletRequest ;  import  javax.servlet.http.HttpServletResponse ;    import  org.springframework.beans.factory.NoSuchBeanDefinitionException ;  import  org.springframework.context.i18n.LocaleContextHolder ;  import  org.springframework.util.StringUtils ;  import  org.springframework.web.HttpRequestHandler ;  import  org.springframework.web.HttpRequestMethodNotSupportedException ;  import  org.springframework.web.context.WebApplicationContext ;  import  org.springframework.web.context.support.WebApplicationContextUtils ;  import  org.springframework.web.servlet.FrameworkServlet ;    import  com.sourceallies.spring.noxml.demo.initializer.ApplicationContextInitializer ;    @SuppressWarnings( "serial" )  public  class  MyHttpRequestHandlerServlet extends  HttpServlet {    private  static  final  Logger LOGGER =  Logger.getLogger ( MyHttpRequestHandlerServlet.class .getName ( ) ) ;    // Replace ApplicationContextInitializer.DISPATCHER_SERVLET_NAME with the  // name of your dispatcher servlet  private  static  final  String  DISPATCHER_CONTEXT_ATTRIBUTE_NAME =  FrameworkServlet.SERVLET_CONTEXT_PREFIX  +  ApplicationContextInitializer.DISPATCHER_SERVLET_NAME ;    private  HttpRequestHandler target;    @Override public  void  init( )  throws  ServletException {  WebApplicationContext wac =  WebApplicationContextUtils.getRequiredWebApplicationContext ( getServletContext( ) ) ;  try  {  this .target  =  ( HttpRequestHandler)  wac.getBean ( getServletName( ) , HttpRequestHandler.class ) ;  }  catch  ( NoSuchBeanDefinitionException e)  {  LOGGER.info ( "HTTP Request Handler bean was not found in Spring Root Context! Now looking up in the Dispatcher Context..." ) ;  WebApplicationContext context =  WebApplicationContextUtils.getWebApplicationContext ( getServletContext( ) , DISPATCHER_CONTEXT_ATTRIBUTE_NAME) ;  this .target  =  ( HttpRequestHandler)  context.getBean ( getServletName( ) , HttpRequestHandler.class ) ;  }  }    @Override protected  void  service( HttpServletRequest request, HttpServletResponse response)  throws  ServletException, IOException  {    LocaleContextHolder.setLocale ( request.getLocale ( ) ) ;  try  {  this .target .handleRequest ( request, response) ;  }  catch  ( HttpRequestMethodNotSupportedException ex)  {  String [ ]  supportedMethods =  ( ( HttpRequestMethodNotSupportedException)  ex) .getSupportedMethods ( ) ;  if  ( supportedMethods !=  null )  {  response.setHeader ( "Allow" , StringUtils.arrayToDelimitedString ( supportedMethods, ", " ) ) ;  }  response.sendError ( HttpServletResponse.SC_METHOD_NOT_ALLOWED , ex.getMessage ( ) ) ;  }  finally  {  LocaleContextHolder.resetLocaleContext ( ) ;  }  }  } 

The rest are normal Spring configurations.

References

Keystore概念,Keytool工具使用

近来由于项目需要做Single Sign On, 研究了一下CAS (具体配置等下篇再介绍), 而这个CAS 的配置最关键的不是CAS 本身,而是数字证书,如何配置多台服务器之间的信任链接。因此,有必要把keystore, keytool 的东西翻出来晒晒。

几个概念

keystore
是 一个密码保护的文件,用来存储密钥和证书(也就是说,keystore 中存储的有两类型entries );这个文件(默认的)位于你的home 目录,也就 是你登录到操作系统的用户名的那个目录。或者通过-keystore 参数设为你指定的位置。需要说明的是:如果不通过-keystore 来指定位置,将使 用home 目录中的默认keystore 文件。smilingleo 原创

通过-alias 来检索keystore 中的具体内容(keystore 中可能存有多个entry

如果想查看keystore 中每个entry 的详细信息,比如谁签发的,用-v 参数(verbose ),里面你还可以看到默认的有效期。

系统签署的证书其有效期默认为一年,通过-validity 来设定其具体的天数。

重要 JDK/jre /lib/security 目录下面有一个cacerts 的文件,就是一个keystore ,其默认密码是changeit 。如果一个App Server 想建立一个安全的链接到另外一个Server, 需要通过一个受信的数字证书,而这个证书就需要存储在cacerts 中。

keytool
用来import, export, list keystore 中内容的工具,还可以用来以测试为目的,生成自己签署的证书。

了解了上面的概念之后,你操作起来就比较容易,比如我们想从一个keystore (文件存在home 目录的.keystore 文件)中删除某个entry , 其aliastomcat5 ,那么keytool 命令就是:
keytool -keystore .keystore -delete -alias tomcat5
是不是很自然?

理解:签署
服务器上产生一个证书之后,用这个证书签署Applet 等,以表明这个applet 确实是来源于这个服务器,而不是其他,以实现其真实性,如果你信任服务器,那么你就可以信任这个applet

比如你可以通过jarsigner 工具,用keystore 中的某个key entry 来签署一个jar 文件。

SQL Server2012远程访问第二个实列

现在有一台A电脑和一台B电脑,A是公司的服务器,安装了两个数据库实例(Sql Server 2012 和Sql Server2008 R2),B电脑在家,安装了Sql Server数据库两台电脑不在一个局域网(我们考虑的是不同网络的两台数据库连接),比如A电脑在公司,B电脑在家里,现在我要在家里用B电脑连接到公司的A电脑里的数据库。 以下以Sql Server 2008 R2设置为例,Sql Server 2012设置类似(侦听端口不同)。

  首先,我们要将设置A电脑的Sql Server端口,使用路由器的端口转发功能,能够外部访问的到

  其次,B电脑使用花生壳绑定的域名进行远程访问,具体步骤如下:

  SQL Server 2008 R2默认是不允许远程连接的,如果想要在本地用SSMS连接远程服务器上的SQL Server 2008,远程连接数据库。需要做两个部分的配置:

  1、SQL Server Management Studio Express(简写SSMS)

  2、SQL Server 配置管理器/SQL Server Configuration Manager(简写SSCM)

  3、设置防火墙允许数据库端口开放

  4、路由器配置端口转发功能

第一步:开启数据库的远程连接功能

  1、这里我们以Sql Server 2008 R2为例,打开Sql Server 2008 R2登录。

 

SQL Server2012远程访问第二个实列

 

2、登录后,右键选择【属性】。左侧选择【安全性】,选中右侧的【SQL Server 和 Windows 身份验证模式】以启用混合登录模式,如果已经是就跳过这步。

SQL Server2012远程访问第二个实列

打开Sql Serve

SQL Server2012远程访问第二个实列

3、选择【连接】,勾选【允许远程连接此服务器】,然后点【确定】

SQL Server2012远程访问第二个实列

4、展开【安全性】—>【登录名】—>【sa】,右键选择【属性】

SQL Server2012远程访问第二个实列

5、左侧选择【常规】,右侧选择【SQL Server 身份验证】,并设置密码,如果已经设置了,跳过这步

SQL Server2012远程访问第二个实列

6、右击数据库选择【方面】

SQL Server2012远程访问第二个实列

7、在右侧的方面下拉框中选择“服务器配置”;将【RemoteAccessEnabled】属性设为“True”,点“确定”

SQL Server2012远程访问第二个实列

第二步:SQL Server 配置管理器配置

1、打开sql server配置管理器

SQL Server2012远程访问第二个实列

2、下面开始配置SSCM,选中左侧的【SQL Server服务】,确保右侧的【SQL Server】以及【SQL Server Browser】正在运行

3、在左则选择sql server网络配置节点下的sqlexpress的协议,在右侧的TCP/IP当中,右键启用或者双击打开设置面板将其修改为“是”,如果已经为“是”就不用修改了。

SQL Server2012远程访问第二个实列

4、选择【IP 地址】选项卡,设置TCP的端口为1433(默认),因我们有两个实例,端口是不同的。

SQL Server2012远程访问第二个实列

SQL Server2012远程访问第二个实列

 

5、将”客户端协议”的【TCP/IP】也修改为启用

SQL Server2012远程访问第二个实列

第三步:防火墙设置Sql Server端口允许外部访问

1、打开防火墙,选择【高级设置】

SQL Server2012远程访问第二个实列

2、【入站规则】当中点击【新建规则】SQL Server2012远程访问第二个实列

3、我们选择【端口】

SQL Server2012远程访问第二个实列

4、我们选择规则应用于【TCP】,在【特定本地端口】输入sql server的默认端口号1433,点击下一步

SQL Server2012远程访问第二个实列

5、选择允许连接

SQL Server2012远程访问第二个实列

 6、给该规则任意取一个名字

SQL Server2012远程访问第二个实列

7、添加规则设置sql server 2012 的端口 1433

第四步:路由器配置端口转发功能

  进入A电脑的网关端口,进行端口转发配置,以TL-WVR308路由器为例,在【转发规则】当中的【虚拟服务器】新增转发端口,其中外部端口可以改成其他端口,也可以和内部端口号一致,而内部端口一定为1433(sql server的默认端口),内部服务器IP地址就是A电脑的内网IP地址。

SQL Server2012远程访问第二个实列

SQL Server2012远程访问第二个实列

 

至此,A电脑的Sql Server允许访问的配置全部完成,接下来是,B电脑连接A电脑的数据库了。最后,我们使用B电脑的Sql Server连接A电脑的Sql Server。如图所示

SQL Server2012远程访问第二个实列

Sql Server 2012实例访问方式:

SQL Server2012远程访问第二个实列

这样就可以使用B电脑外部远程访问到A电脑的所有Sql server实例了~~

 

弹出窗体中加载页面

1、窗体代码定义

   <div id="WinLoad" class="easyui-dialog" style="width:1050px;height:530px;padding:15px" closed="true" iconCls="icon-app" title="5分钟电压曲线"          minimizable="true" maximizable="true" resizable="true"  buttons="#WinLoad-buttons">              <div id="pnl" class="easyui-panel" data-options="fit:true" border="false"/>         </div>           <div id="WinLoad-buttons">               <a href="javascript:void(0)" class="easyui-linkbutton" iconCls="icon-ok" onclick="javascript:$('#WinLoad').dialog('close')">确 定</a>               <a href="javascript:void(0)" class="easyui-linkbutton" iconCls="icon-cancel" onclick="javascript:$('#WinLoad').dialog('close')">关 闭</a>           </div>   

 

2、js代码

 function Chart() {             var row = $(dg).datagrid('getSelected');             if (row) {                 var jcdid, jcdmch, rq,title, url;                 jcdid = row.jcdid;                 jcdmch = row.jcdmch;                 title = '【' + jcdmch + "】5分钟电压曲线";                 $('#WinLoad').dialog({                     title: title                 });                  rq = $('#rq').val();                 url = "../Chart/MinuteDataCht.htm?tbName=" + tbName + "&&jcdid=" + jcdid + "&&rq=" + rq;                 $("#pnl").html('<iframe src="' + url + '" frameborder="0" height="100%" width="100%" id="dialogFrame" scrolling="no"></iframe>');                 WinShow('WinLoad');             }             else                 AlterInfo("您没有选择记录,不能查看电压曲线!");         } 

  

Grails结合FusionCharts Free制作统计图

1.因为工程需要对数据进行统计并且用图的形式显示出来,所以用到了FusionCharts Free。

首先展示下效果图。

Grails结合FusionCharts Free制作统计图

2.工欲善其事必先利其器,首先得下载FusionCharts Free,

 

http://www.fusioncharts.com/download/

上边这个网址可以下载到FusionCharts Free,但是得先注册下。ok,下载成功,将其解压

缩。解压后的文件目录如下:

Grails结合FusionCharts Free制作统计图

3.将上边目录中的Charts拷贝到工程中的web-app目录下,同时将上边目录中的JSClass中的

Grails结合FusionCharts Free制作统计图

拷贝到工程中的web-app目录下js文件夹中。ok,文件拷贝成功后,下边就是写代码了。

4.打开你需要编辑的gsp页面。在页面中加入如下代码:

<SCRIPT LANGUAGE=”Javascript” SRC=”${resource(dir:’js’,file:’FusionCharts.js’)}”></SCRIPT>

<%  
     String strXML=””;
      strXML =”<graph caption=’下载统计’ bgcolor=’F5FFFA’ subCaption=’By ${session.user.username}’ decimalPrecision=’0′ showNames=’1′ baseFontSize=’19′ numberSuffix=’本’ pieSliceDepth=’30′ formatNumberScale=’0′>”   
   
    strXML +=”<set name=’已下载’ value=’${params.loadnum} ‘/>”;
    strXML +=”<set name=’未下载’ value=’${params.unloadnum} ‘/>”;
    strXML += “</graph>”;
    %>

            <div id=’Div’ align=’center’>下载统计</div>
            <script type=”text/javascript”>
            var chart_sum = new FusionCharts(“${resource(dir:’Charts’,file:’FCF_Pie3D.swf’)}”, “sum”, “600”, “300”, “false”, “false”);
            chart_sum.setDataXML(“<%= strXML%>”);
            chart_sum.render(“Div”);
            </script>

 

保存代码,运行程序,会出现上边的饼图。

5.上边只是简单显示了了一个3D饼图,下边介绍下它的属性。

<graph>

(1)Background

bgColor=”HexColorCode”:顾名思义,它就是背景色,例如bgcolor=’F5FFFA’ ,记住不

需要在这个16进制字符前边加#.

bgAlpha=”NumericalValue(0-100)”:设置这个图的alpha。

bgSWF=”Path of SWF File”:你可以利用这个属性引用一个外部的.swf文件作为这个图的背

景。

(2)Chart and Axis Titles

caption=”String”:图的标题

subCaption=”String” :图的副标题

(3)Generic Properties

shownames=”1/0″:1表示显示这个值的名字,0不显示,默认是1

showValues=”1/0″:1表示显示这个值,0不显示,默认是1

showPercentageValues=”1/0″:1表示显示百分比,0显示实际值,默认值是0

showPercentageInLabel =”1/0″:0表示显示实际数值,1显示百分比,默认值是0

(3)Pie Properties

pieRadius=”Numeric Pixels”:设置饼图的半径,如果没有设置,FusionCharts会自动计算

出最佳的半径。

pieSliceDepth=”Numeric Value”:设置这个3D图形的高度。

(4)Font Properties

baseFont=”FontName”:字体样式

baseFontSize=”FontSize”:字体大小

baseFontColor=”HexColorCode”:字体大小

(5)Number Formatting Options

numberPrefix=”$”:数字前缀

numberSuffix=”p.a”:数字后缀

formatNumber=”1/0″:如果为1,那么40,000;如果为0,那么40000。

formatNumberScale=”1/0″:是否要在数字后面加K或者M,例如10434,如果设置为1,那

么就是1.04K,

decimalSeparator=”.”:小数点分隔符

thousandSeparator=”,”:千位分隔符

decimalPrecision=”2″:小数精度

(6)Hover Caption Properties

showhovercap=”1/0″:当鼠标停留在图上时是否显示提示框,1显示,0不显示,默认显

示,

hoverCapBgColor=”HexColorCode”:提示框的颜色

hoverCapBorderColor=”HexColorCode”:提示框边框颜色

hoverCapSepChar=”Char”:提示框中名字与值之间的分隔符

<set> 是<graph>的子属性

一个简单的例子如下:

<set name=”Jan” value=”54″ color=”3300FF” hoverText=”January” link=”ShowDetails.asp%3FMonth=Jan” showName=”1″/>

name=”string”:在饼图上显示的文字

value=”NumericalValue”:在饼图上显示的数值

color=”HexCode”:该数值所对应的颜色

hoverText=”String value”:当鼠标停留在该数值区域时显示的文本

link=”URL”:当点击该区域时,链接到的地址

ok,上边是3D饼图的一些属性,其他图的属性可以参考你下载的文档,打开解压后的文件,

Grails结合FusionCharts Free制作统计图

打开红色框选择的文件,里边会有关于各种图的使用事例还有一些图的属性介绍。

… other posts by 姜立

Jalita

Jalita (Java light terminal adapter) is a simple server framework to develop java applications for terminals (i.e. vt100) in a swing based manner, specialised for mobile devices typical used in business enviroments (i.e. barcode scanner) connected via tcp/ip.
Simpler, you could create Forms with Widgets which are concepted for an text-only device. You seperate your logic from the ui in Automations. And you deploy all this to an Server where your devices connect to.