Česky   |  Deutsch   |  English   |  Español   |  Français   |  Indonesia   |  日本語   |  한글   |  Polski   |  Português (BR)   |  Türkçe   |  中文   |  正體中文   |  Your Language  
PlanetNetbeans
NetBeans星球是来自所有博客空间的NetBeans相关思索的集合。
BlogJava-Java桌面技术-随笔分类-NetBeans - January 06, 2012 09:30 AM
重返Swing

摘要: 我又干起了老本行,Swing。这次回归没有以往对Swing的着迷,取而代之的是对Swing的反感... ...  阅读全文

BlogJava-Java桌面技术-随笔分类-NetBeans - January 05, 2012 02:22 PM
NetBeans7.1正式版发布

摘要: 本来去年发布,可惜延期到了今年1月。看NetBeans路线图,NetBeans7.1.1也不远了,7.2版本也将于6月底发布。期待吧  阅读全文

BlogJava-Java桌面技术-随笔分类-NetBeans - October 06, 2011 03:00 PM
缅怀乔布斯

摘要: 有三个苹果改变了世界。夏娃的苹果让人有了道德,牛顿的苹果让人有了科学,乔布斯的苹果让人有了生活  阅读全文

BlogJava-何以解忧?唯有Java - June 02, 2011 03:33 AM
KDE 环境下的BlogJava客户端

今天找到一个在KDE 环境下写博客的软件 Blogilo ,现在准备测试一下是否支持blogjava 发布博客(经测试可以发布)

好久没有写博客了,今天先发一篇如何使用Blogilo 发布BlogJava博客的文章,接下来工作没那么忙了,会开始写一些NetBeans RCP开发的入门文章。废话不多说了,接下来开始讲解如何使用Blogilo发布博客。

第一步: 先检查你的Linux发行版是否安装了kdepim,因为blogilo是和kdepim绑定发布的,目前主流的包含KDE桌面的发行版都默认安装kdepim,也就是说默认安装了Blogilo,不排除一些发行版精简kdepim,把blogilo移除了。我目前使用的是Fedora15 KDE 发行版。Blogilo 目前支持 Blogger 1.0 API,MetaWeblog API,MovableType API,Wordpress,Blogspot.com 这五种博客API,如下图,你可以选择适合自己的博客API,当然今天写的是适合BlogJava的MetaWeblog API。

第二步:配置Blogilo,打开Blogilo,然后开始配置,如下图:

接着在弹出的界面中,选择Blogs,如下图:

点击add 按钮,开始添加BlogJava 博客API 配置 ,在Blog/Homepage URL 填写的博客服务地址,例如我的博客:http://www.blogjava.net/gml520 然后在这个地址的后边再

添加 /services/metaweblog.aspx 。 完整的地址:http://www.blogjava.net/gml520/services/metaweblog.aspx。 你可以根据自己的实际情况将gml520 替换成自己的用户名。然后添加用户名和密码,接着点击Advanced 选项卡,在API 列表中选择 MetaWeblog API ,Blog ID 填写自己的用户名,最后点击OK 完成配置。

这里有几个地方需要注意: (1)在Basic 选项卡里不要点击 Auto-Configure,除非你的博客的API 不是 MetaWeblog 类型。(2)在Advanced 选项卡下输入完 Bolg ID 后不要点击 Fetch ID。否则会出错的,原因同(1)一样。

配置过程如下图:

第三步: 配置完成后,你就可以开始写博客啦,这篇博客就是用 Blogilo 客户端写的。:)

最终界面:

=-=-=-=-=
Powered by Blogilo



BlogJava-何以解忧?唯有Java - May 31, 2011 08:51 AM
我的NetBeans 黑色主题

发图不解释


BlogJava-Java桌面技术-随笔分类-NetBeans - April 21, 2011 01:54 AM
NetBeans7.0正式发布

摘要: 经过漫长的等待,无数次延期,NetBeans7.0终于发布了。这次带来了众多改进,如JDK7的支持、GlassFish3.1的支持、Maven3的支持等,都是开发者们盼望已久的功能。  阅读全文

BlogJava-何以解忧?唯有Java - March 30, 2011 01:42 AM
NetBeans 7.0 RC1 发布

NetBeans 7.0 RC1 终于发布了,这个版本的发布时间因为Oracle 对Sun的收购而一直推迟,不过推迟的好处是:通过了更多的质量测试,使得NetBeans7.0 变得更加的可靠,有兴趣尝试NetBeans 7.0 的朋友可以到官方网站上下载:http://dlc.sun.com.edgesuite.net/netbeans/7.0/rc1/

  

  下面是 NetBeans Dzone的一段介绍:
  
   NetBeans 7.0 RC1 is out! But the real news is all the effort made to make this a great quality release. The NetCat (NetBeans Community Acceptance Testing) team revamped its approach and the results are outstanding! The biggest improvement, IMO, is the introduction of Testing Tribes:


更多关于NetBeans 的新特性介绍可以看看NetBeans 的wiki http://wiki.netbeans.org/NewAndNoteworthyNB70



BlogJava-何以解忧?唯有Java - December 31, 2010 12:25 PM
NetBeans 任务提醒插件(Linux 平台)

今天在Dzone 上看到一篇文章介绍,NetBeans 使用 Growl 来实现 NetBeans 执行任务时的本地化方式提醒,感觉很不错。文章链接地址:
http://netbeans.dzone.com/nbnotify-netbeans-growl   
          

         联想到Linux上也有类似的提醒,而且操作很简单,于是就想是不是自己也写个类似的插件来供自己使用呢? 说干就干,按照流程 新建一个NetBeans 插件项目,然后再新建一个 Installer ,这个Installer 是在模块被载入的时候执行的。所以在这个Installer中 注册一个监听器,用来监听 NetBeans 的任务操作。监听到任务后,接下来的事情就好办了,只要判断任务状态就可以了,然后根据任务的状态发送提醒信息。
           在Linux的各个发行版中使用提醒命令最多的是 notify-send 这个命令,在Fedora 和Ubuntu 的Gnome 环境中都是使用这个命令。我的机器上同时安装 Fedora 14 (KDE桌面)和 Ubuntu10.10(Gnome桌面),所以我就在这两个发行版上测试。在测试的过程中,发现在Ubuntu 10.10 中notify-send 这个命令的 -t 参数竟然不起作用,-t 参数是用来控制提醒保留的时间的,但是在Ubuntu10.10中,无论你设不设置 -t 参数,他都是保持10秒后隐藏提醒,于是在Ubuntu10.10 下使用这个插件就很悲剧的不能及时的展现通知,只能10秒后再显示下一条通知,相反在Fedora14 KDE 中却工作良好。下面四张截图就是分别在Fedora  14 KDE 和Ubuntu10.10 Gnome桌面环境下的效果图:

           在Ubuntu10.10 Gnome 中当 NetBeans 对项目执行“ 生成 ”任务的开始 和 结束 时的提醒状态。
     
              

            在Fedora14 KDE 中当 NetBenas 对项目执行 “清理并生成” 任务的 开始 和 结束 时的提醒状态。


        


  怎么样,效果还不错吧,其实在KDE中还有一个更加强大的提醒工具,那就是 kdialog ,kdialog 不仅拥有 notify-send 的所有功能,同时还拥有更加强大的提示功能,比如对话框形式,密码输入等等,如果想要了解更多的kdialog 提醒功能,请使用 kdiglog --help 这个命令获取更多的参数了解。
目前这个插件的功能还比较弱,只能对执行任务时 的开始和结束状态 做出提醒,对于执行项目出错等状态还不能提醒,不过目前的这个提醒还凑和,至少你可以在编译 一个耗时比较长的项目的时候去做其他的事情,编译完成后,NetBeans 会自动的提醒你编译任务完成了(无论是否成功)。

BTW: 明天就是 新年--2011 年了,祝大家 新年快乐!万事如意!财源滚滚! 同时也祝福自己!

插件下载:NbLocalNotify   (Linux only,源码将在下一篇博客中公布)。



BlogJava-何以解忧?唯有Java - November 21, 2010 08:48 AM
NetBeans 7.0 Beta 发布

摘要: NetBeans 最新版本NetBeans 7.0 Beta 发布了,想要体验新版本的童鞋们可以去官方网站上下载了。
http://dlc.sun.com.edgesuite.net/netbeans/7.0/beta/  阅读全文

BlogJava-何以解忧?唯有Java - October 14, 2010 02:00 AM
JavaFX Script 的替代品 Visage

    在今年的JavaOne 上Oracle 宣布停止JavaFX 脚本语言的开发,转而用Java API实现JavaFX 的功能,就我个人而言,我更倾向于JavaFX Script, JavaFX Script 当初使用自己的语言而不是使用Java 语言,就是为了减少 程序员的负担,以更简洁更人性化的语法公诸于世。然而Oracle 却放弃了JavaFX Script。 于是就有JavaFX Script 的爱好者开发了 Visage语言来延续JavaFX ,虽然名字不一样,但是语法却是一样的。 他的 项目主页 :http://code.google.com/p/visage/  

    原先用JavaFX 语言开发的应用可以继续开发了,不用再因为Oracle 放弃JavaFX而苦恼了,因为有开源的Visage,呵呵!有兴趣的朋友可以去 Visage 的项目主页上看看。


BlogJava-Java桌面技术-随笔分类-NetBeans - August 19, 2010 02:05 AM
NetBeans6.10发布计划

NetBeans下一个版本是6.10,NetBeans小组正在紧锣密鼓地进行了。http://wiki.netbeans.org/NetBeans_610?intcmp=925655

按照计划,6.10将于明年1月发布。



BlogJava-Java桌面技术-随笔分类-NetBeans - June 17, 2010 01:50 AM
NetBeans6.9正式发布

摘要: 今天一早来到公司,习惯性地打开www.netbeans.org发现期待已久的6.9版本正式发布。这是一个里程碑的版本,首次正式加入对OSGI的支持。其他编辑特性也明显增强  阅读全文

BlogJava-何以解忧?唯有Java - May 13, 2010 04:43 AM
发布一个 NetBeans Java ByteCode Viewer 插件

摘要: 昨天在JavaEye论坛上又一位仁兄用Jclasslib 查看 Java class byte code,http://www.javaeye.com/topic/663117#1489190
于是去下载 Jclasslib ,发现官方网站上在2005年以后就没有更新了,他的NetBeans 插件也没有更新,对于新版的NetBeans 支持不了,于是就萌生了重新开发 Jclasslib for NetBeans 插件,于是就有了今天的这篇博文,呵呵!
话不多说,先上图,正所谓一图胜千言,可能有些朋友会觉得使用Java 默认的界面比较丑陋,但是我觉得还行,哈哈!主要是因为这个外观的的Java运行速度比较快,Linux 下用这个外观很实用。
  阅读全文

BlogJava-何以解忧?唯有Java - April 16, 2010 05:22 AM
NetBeans6.9 开发版的几个新功能


这里只列举几个新特性:

1、内置 shell 控制台


本地,和远程的shell 视图:



2、 拼写检查


注释中的拼写检查



版本控制中的拼写检查


3、 链接地址显示



4、 Line wrap



效果图:



更多的功能请看http://wiki.netbeans.org/NewAndNoteWorthy





BlogJava-何以解忧?唯有Java - March 30, 2010 02:00 AM
解决ubuntu10.04 Firefox3.6 Java浏览器插件不工作的问题

这几天忍不住尝试了一下ubuntu10.04,感觉还不错。默认的我的显卡驱动就可以开3D 了,我的是A卡。
但是今天在安装java 浏览器插件的时候死活的都装不上,firefox 的插件列表里就是没有jre 插件,于是开始
google+baidu  试了很多种的方法,走了许多的弯路都没有解决,最后在
https://bugs.launchpad.net/ubuntu/+source/sun-java6/+bug/532174   这里找到的解决办法:

sudo update-alternatives --install /usr/lib/mozilla/plugins/mozilla-javaplugin.so mozilla-javaplugin.so /usr/lib/jvm/java-6-sun/jre/lib/i386/libnpjp2.so 1

只要在命令行下输入这条命令就可以了。前提是你已经安装好jre 的浏览器插件。



BlogJava-何以解忧?唯有Java - March 09, 2010 03:35 AM
NetBeans 6.9 可以将 NetBeans RCP 程序打包成 exe,sh,zip等格式发布

NetBeans 6.9 最新进展:

1. 已经可以支持OSGI 开发






2. NetBeans 6.9 可以将 NetBeans RCP 程序打包成 exe,sh,zip等格式发布
(消息来源:http://blogs.sun.com/geertjan/entry/generate_a_netbeans_platform_installer









3. Netbeans 6.9 中 woodstock 重新回来了,意味着NetBeans 默认又可以可视化开发了。
(该消息未验证,消息来源:http://netbeansboy.com/2010/03/08/netbeans-6-9-woodstock-is-back/





BlogJava-何以解忧?唯有Java - March 06, 2010 01:01 PM
NetBeans 6.8 Java Editor Reference Guid

摘要:
下面是来自Dzone 的一篇关于 NetBeans 6.8 Java Editor Reference Guid 文章介绍了NetBeans 6.8 核心的编辑器特性。

地址:http://netbeans.dzone.com/nb-java-editor-ref
  阅读全文

BlogJava-何以解忧?唯有Java - February 02, 2010 09:38 AM
对YOYOPlayer 的一点小小改进

其实也没什么,就是YOYOPlayer 默认的界面 播放列表里的 JSplitPane 使用的是系统提供的默认皮肤(在Java中提供的几个平台中的一个)。但是如果碰到Java不支持的桌面环境,界面就很难看了,于是就在网上找了点代码,学习了一下如何自定义一些组件,更改了YOYOPlayer默认界面中的那条白色的“竖条” ,如下图所示:

 

改进后的界面:(整体感觉是不是更好了点?



原理其实就是写个类 继承 BasicSplitPaneDivider,然后写个 UI类继承 BasicSplitPaneUI ,

在继承BasicSplitPaneUI 的类中,需要重写下面的方法:

 public BasicSplitPaneDivider createDefaultDivider() {
       
    }

最后写完以后,在代码中调用JSplitPane 的setUI() 方法就可以了。

最后在上一张完整无处理的图片:







BlogJava-Java桌面技术-随笔分类-NetBeans - January 14, 2010 01:31 AM
JDK6 update18发布

摘要: JDK6 update18发布了,这次更新包含了许多值得关注的新特性以及性能的提升  阅读全文

BlogJava-何以解忧?唯有Java - January 09, 2010 02:10 PM
写了一个快速搜索blogjava首页文章的NetBeans插件

摘要: 断断续续的学习NetBeans RCP 开发有一段时间了,也用NetBeans RCP开发了一个内部项目,在学习的过程中发现NetBeans真的很强大。 如果大家想学习NetBeans RCP开发的话,官方网站上的入门教程建议大家多看看,还有就是NetBeans Top10 API视频教程。官方网站上的NetBeans platform 教程有部分已经翻译成中文了,中文教程还在陆续的翻译中。
在学习的过程中,在NetBeans 官方网站上看到一篇关于NetBeans 快速搜索API使用教程,于是模仿着写了这个插件。这个插件功能比较弱,只能搜索到blogjava首页上的文章。有需要的可以拿去用用,开发的时候我用的是NetBeans6.8 。
  阅读全文

BlogJava-何以解忧?唯有Java - December 12, 2009 07:52 AM
JavaFX Designer for NetBeans6.8 即将到来

摘要: JavaFX 设计器很快就要发布了, JavaFX 一直被人诟病没有可视化的设计器,现在终于有了。

大家可以到 netbeans 的wiki上看看 这个设计器的教程,了解这个设计器更多的特性。

http://wiki.netbeans.org/JavaFXComposer

大家还可以关注一下这个人的博客,了解JavaFX 设计器。

http://blogs.sun.com/lukas/

废话不多说了,先睹为快:  阅读全文

Some reminiscences, some memories » NetBeans - December 11, 2009 03:02 AM
Netbeans 6.8 发布,首先需要解决的小bug

Oracle 对 Sun 的收购,还未真正尘埃落定,坊间就流言四起……Netbeans 6.8 的发布,无疑增强了大家对 Netbeans 在带着神谕的阳光下生存下去的信心。于是,还未上班前,趁着大家吃早餐的时间全速下回来了最新的 Netbeeans 6.8 的 j2se 开发版本。赏玩一番……

本来呢,由于工作的原因,Netbeans 我会用来做 PHP、C 的开发(无奈 python 插件还有许多 bug,python 的开发已经迁移到 pyDev 了),java 本身只是做做作业,随便玩以下的。但是完整的 Netbeans 版本体积直逼 Eclipse,下载起来实在让人心焦,所以就按照以往的习惯,下一基本的版本,再安装插件。

netbeans-6.8-splash新的启动闪屏还是很有感觉的,貌似,能提高编码的兴趣……我是瞎说的……

菜单中文字体过小的这个缺陷看来还没有被修正。或许 Netbeans 的开发人员不觉得菜单字太小是个麻烦吧。

打开了“工具->插件->可用插件”准备安装 PHP、Python、C/C++ 的支持插件。这时发现不对了,插件怎么这么少?只有 21 个?看错了?Bug?搜索了以下,PHP 插件只有“Selenuim Module for PHP”这个 beta 版的插件。正式版的 PHP 插件没有了。Python 这个 Beta 版的插件有。C/C++ 支持这个正式的插件没有。

这实在太诡异了……是不是插件目录加载不完全?点击“重新装入目录”让 Netbeans 重新加载下插件目录,这时出问题了:netbeans-6.8-warning

居然提示我无法连接……难道是给墙了?在浏览器里打开 Netbeans 默认的更新地址:http://dlc.sun.com.edgesuite.net/netbeans/updates/6.8/uc/final/stable/catalog_zh_CN.xml.gz,404 错误,原来是插件目录文件不存在。

访问 http://dlc.sun.com.edgesuite.net/netbeans/updates/6.8/uc/final/stable/ 得到插件目录文件 http://dlc.sun.com.edgesuite.net/netbeans/updates/6.8/uc/final/stable/catalog.xml.gz,通过“工具->插件->设置”添加这个插件目录作为更新中心的源。删除原来不存在的更新中心。PHP、C/C++ 的插件再次出现在可安装的插件列表里。

看来是多语言包出的问题啊!

跟进:https://netbeans.org/bugzilla/show_bug.cgi?id=178459 原来,更新中心的文件是对的,但是他们忘记发布了。

BlogJava-何以解忧?唯有Java - December 11, 2009 02:50 AM
NetBeans6.8 &&JavaEE 6 && GlassFish v3 正式发布了!

摘要: 经过漫长的等待,NetBeans6.8 &&JavaEE 6 && GlassFish v3 终于发布了!

NetBeans 6.8 一个你不可错过的版本。不仅功能上改进了非常多,性能上更是提升了不少。

NetBeans 6.8 的功能具体介绍:http://wiki.netbeans.org/NewAndNoteWorthy

NetBeans 6.8 视频教程 http://netbeans.org/kb/docs/intro-screencasts.html
  阅读全文

BlogJava-Java桌面技术-随笔分类-NetBeans - December 11, 2009 01:55 AM
NetBeans6.8发布了

摘要: NetBeans6.8正式发布,同时符合Java EE6规范的GlassFishV3也放出了正式版本。  阅读全文

BlogJava-何以解忧?唯有Java - December 08, 2009 03:37 AM
百度的文档分享平台 vs Google Book vs 豆瓣


今天邮箱里收到一封邮件,说是有一份文档与我分享,于是点了链接,跳出来的却是百度的文档分享平台,看了一下界面,和豆瓣,Google 的
文档显示界面基本上都是一样的,都是用Flex做的,到现在我才知道,原来百度 也开始涉水 文档服务了,就像Google book类似,不过听说最近
Google book 官司不断,不知道百度会如何应对将来发生的 图书纠纷呢?





BlogJava-何以解忧?唯有Java - November 27, 2009 09:47 AM
JXTaskPane 示例

JXTaskPane 类似XP 左边的导航栏,在开发Swing应用的时候很好用的。

下面两张截图反应了JXTaskPane的动画效果。可以自由的卷起和展开。

  

JXTaskPane的的使用步骤:

1、新建一个 JXTaskPaneContainer

2、新建{i}个JXTaskPane   (i>=1)

3、依次将 新建的JXTaskPane 添加到 JXTaskPaneContainer 中去。

4、最后将 JXTaskPaneContainer 添加到JFrame 中。

附上 SwingX doc中的示例代码


 JXFrame frame = new JXFrame();
 
 
// a container to put all JXTaskPane together
 JXTaskPaneContainer taskPaneContainer = new JXTaskPaneContainer();
 
 
// create a first taskPane with common actions
 JXTaskPane actionPane = new JXTaskPane();
 actionPane.setTitle(
"Files and Folders");
 actionPane.setSpecial(
true);
 
 
// actions can be added, a hyperlink will be created
 Action renameSelectedFile = createRenameFileAction();
 actionPane.add(renameSelectedFile);
 actionPane.add(createDeleteFileAction());
 
 
// add this taskPane to the taskPaneContainer
 taskPaneContainer.add(actionPane);
 
 
// create another taskPane, it will show details of the selected file
 JXTaskPane details = new JXTaskPane();
 details.setTitle(
"Details");
  
 
// add standard components to the details taskPane
 JLabel searchLabel = new JLabel("Search:");
 JTextField searchField 
= new JTextField("");
 details.add(searchLabel);
 details.add(searchField);
 
 taskPaneContainer.add(details);
 
 
// put the action list on the left 
 frame.add(taskPaneContainer, BorderLayout.EAST);
 
 
// and a file browser in the middle
 frame.add(fileBrowser, BorderLayout.CENTER);
 
 frame.pack();
 frame.setVisible(
true);


默认JXTaskPane 的动画功能是起用的,你也可以关闭 :

只要 myJXTaskPane.setAnimated(false);  就可以了。





BlogJava-何以解忧?唯有Java - November 25, 2009 07:41 AM
Fedora 11 3D环境下启动 永中Office

摘要: Linux 下永中Office 对 doc 文件的兼容性比较好,所以在Linux 我用的最多的office 软件就是永中Office。
最近操作系统换成了 Fedora 11,安装永中Office 的时候遇到了一些问题,现在把我遇到的问题和解决办法
写下:
第一个问题: 在 3D环境下不能安装 永中Office
解决:关闭3D效果,然后再安装永中Office ,OK。

第二个问题: 在正常模式下可以很快的启动永中Office,但是在3D模式下就启动不了。
解决:请安下面的步骤进行  阅读全文

Some reminiscences, some memories » NetBeans - November 10, 2009 03:50 AM
Netbeans Python 调试,郁闷的包导入问题

这几天在帮忙开发一个 python 项目。使用 netbeans 的时候,遇到一个诡异的问题。

如果使用“运行”来跑项目,一切正常。而如果使用“调试”来跑项目,当执行到 import 第三方的库的时候,就会产生“ImportError: No module named xxxx”。郁闷不已,上网查了半天,没找到可用的信息。

毛主席教育我们“自己动手,丰衣足食”。于是就祭出“观察、归纳和总结”的科学的研究方法,对这个灵异现象进行了一番探索。

出现导入问题的包都是用了 egg 打包的第三方包。在我的系统中,这几个包都是放在/usr/local/lib/python2.6/dist-packages/下。在命令行能正常执行,在 netbeans 中直接执行也无异常。说明包本身的安装没有问题。那么 netbeans 调试就出现找不到这个包,是不是 netbeans 的调试环境有什么特殊呢?看了一下 netbeans 的调试窗口,发现这个脚本~/.netbeans/6.7/config/nbPython/debug/nbpythondebug/jpydaemon.py。很明显,这个是 netbeans 为了集成调试功能而开发的一个 python 调试器。难道是这个netbeans自带的调试器的问题?祭出 sys.path 一看,果然正常执行的时候,dist-packages 下的 egg 包都被放到搜索路径里。而调试的时候,没有一个 egg 包在 sys.path 中。而是多了一些 netbeans 使用的目录而已。

郁闷啊,为什么调试器不能直接继承系统环境呢?

于是乎,将每个 egg 包都手工加入 Python Platforms 的 Python Path 里。问题解决了。可以带第三方 egg 包来调试项目了。

虽然问题解决了,不过还是不爽,如果我一个项目要是有 n 个 egg 包,那不是要每个都设置一下?

Hacking……

发现 jpydaemon.py 的 1588 行修改了sys.path,导致默认的路径信息丢失。

pythonPath = dbgutils.PythonPathHandler(None)
pythonPath.getPyPathFromEnv()

再检查 dbgutils.py ,原来 netbeans 的调试环境只读取了环境变量 PYTHONPATH,未添加默认的 sys.path。而我并没有使用 PYTHONPATH 环境变量,所以导致一些安装好的包无法导入。164 行直接覆盖了 sys.path。

    def getPyPathFromEnv( self ):
        "PYTHONPATH env and set sys.path out of it "
        pyPath = os.environ["PYTHONPATH"]
        # cleanly take care of previous ';' convention
        if os.pathsep != ';':
            pyPath.replace(';' , os.pathsep)
        if pyPath.find(os.pathsep) != -1:
            sys.path = pyPath.split(os.pathsep)
            # remove empty nodes first
            for element in sys.path:
                if ( len(element.strip())==0 ):
                    sys.path.remove(element)

修改 164 行

sys.path = pyPath.split(os.pathsep) + sys.path

这个世界又清爽了……

不知道会不会有什么问题。有问题,继续 fix 好了。

BlogJava-何以解忧?唯有Java - October 31, 2009 09:00 AM
翻译了一个Linux 下的 游戏管理软件 djl

摘要: Linux 下的游戏没有Windows 多,而且想找游戏玩也比较麻烦,也不知道有什么游戏可以玩。
最近在网上看了djl 这个游戏管理软件,里面有上百个的游戏可以提供你玩。真的很不错,默认没有中文的
我顺手翻译了一下,翻译的不是很完整,不过够大家用的了。  阅读全文

BlogJava-何以解忧?唯有Java - October 19, 2009 07:52 AM
使用Lucene 对JTable进行搜索

摘要: 经常会遇到一个表格中有许多的数据,想要找到一条符合自己要求的数据会比较麻烦,需要一条一条的找。数据量小还好,但是如果有几百条上千条的数据找起来就比较麻烦了。Lucene 提供了一个拓展包 lucene-swing,可以很方便的对JTable进行搜索。  阅读全文

BlogJava-何以解忧?唯有Java - October 14, 2009 03:49 AM
NetBeans 软件(代码)质量测试工具 SQE

摘要: SQE 是 software quality enviroment 的缩写, NetBeans 的SQE 插件 官方的介绍是这样的:

SQE is an attempt at providing first class NetBeans integration for different Software Quality Tools as

* Code Defect Analysis
o FindBugs
o PMD
o CheckStyle
o Lint4j
* Metrics
o Dependency Finder
* Dependency Analysis
o Dependency Finder


我把它叫做软件质量测试工具,它可以帮你找到你写的代码中潜在的问题 还会有一些建议。
PMD 的介绍: 阅读全文

BlogJava-何以解忧?唯有Java - August 29, 2009 05:24 AM
NetBeans 新的 Substance 皮肤插件!

摘要: 针对NetBeans的 Substance 官方皮肤插件,已经有很长时间没有更新了,这次 NetBeans 梦之队 成员重新写了个 NetBeans的
Substance 皮肤插件,虽然现在的可定制性不如 官方的版本,但是官方的版本只支持 NetBeans 6.1 而这个却可以支持
NetBeans 6.7.1最新的版本。
  阅读全文

BlogJava-何以解忧?唯有Java - August 04, 2009 01:43 AM
NetBeans6.8 M1发布了!添加了嵌入式浏览器支持和JavaEE6 支持

摘要: NetBeans6.8 M1发布了!添加了嵌入式浏览器支持和JavaEE6 支持 ,此外还有 ruby maven, C++ 等增强特性.
有图有真相:
嵌入浏览器功能默认不开起,需要自己手动开启: 在 工具->选项
  阅读全文

BlogJava-Java桌面技术-随笔分类-NetBeans - July 17, 2009 05:55 AM
期待NetBeans6.8的发布

摘要: 性能问题一向是历代NetBeans发布时的关注焦点。和发布6.7之前的情形一样,6.8版本包含很多值得自豪的改进。  阅读全文

BlogJava-何以解忧?唯有Java - July 02, 2009 06:44 AM
对SwingX 做了一些国际化翻译

今天用到了一些SwingX 的类库,但是显示的时候都是英文的,感觉挺别扭的,于是就对SwingX做了一些国际化!

下载了源代码,然后找到对应的Properties 文件,发现其实SwingX已经有很多个国家的语言版本了,就是少了中国版的,唉!谁叫中国的程序员使用Swing的少呢? 呵呵!

用NetBeans打开 项目文件,找到properties文件,为每个原始的Properties文件添加一个 zh_CN的locale 。然后就是翻译了。这里不得不赞美一下NetBeans 对properties 文件的编辑功能相当的好用,可以直接写中文,而不必转换为Unicode。NetBeans 默认已经帮你完成了。

这是我第一次翻译,因为能力有限,所以翻译难免会有一点小毛病,有什么翻译问题请留言,或者发邮件告诉我,让我好改正。



2009-11-04 更新:

翻译更新到SwingX1.6, SwingX1.6 是一个 Java 6 only 版本,也就是说这个版本需要用JRE1.6 。


2009-09-23 更新:

应朋友们的要求,今天放上来 SwingX-1.0 的国际化版本(并非本地化,仅仅只是国际化)。

修复LoginPanel 翻译的问题,原来翻译成中文有乱码,现保留为英文。

初步判断是SwingX 中使用了不支持中文的字体造成的。


2009-07-07 更新:
 第一次对SwingX进行翻译


------------------------------传说中华丽的分割线-----------------------------------

  (SwingX1.6)点这里下载(20091104)


(SwingX-1.0)点这里下载   (20090923)

(SwingX-0.9.7)点这里下载(20090702)





BlogJava-Java桌面技术-随笔分类-NetBeans - June 29, 2009 02:10 PM
NetBeans6.7正式版发布了

摘要: NetBeans6.7正式版发布了  阅读全文

BlogJava-何以解忧?唯有Java - June 09, 2009 01:59 PM
Swing 本地外观与Substance外观之间的切换问题及解决方案

摘要: Swing应用程序如果是在开源的Look&&Feel 之间切换,感觉很容易,但是如果把应用程序在开源外观下切换到系统默认的或者JDK自带的外观时,问题就来了。不是没有标题栏,就是标题栏的外观没有改变,用的是系统的窗口装饰。  阅读全文

BlogJava-何以解忧?唯有Java - May 31, 2009 02:13 PM
关于本人的一些信息

摘要:   阅读全文

BlogJava-Java桌面技术-随笔分类-NetBeans - May 29, 2009 10:54 AM
JDK 1.6.0_14 发布了

期待已久的JDK 1.6.0_14 发布了。http://java.sun.com/javase/6/webnotes/6u14.html
本次更新是继Update 10以来最大的一次更新,包含了众多新特性和bug的修复,喜欢尝鲜的开发人员可以试试最新版本的JDK带来的益处。

需要关注的是JRE的安装界面,Sun标志的背景改为了红色,但愿Oracle能使Java更加辉煌吧。



BlogJava-何以解忧?唯有Java - May 23, 2009 12:09 PM
用开源Look&Feel (Substance)写 漂亮的Swing应用程序

摘要: 今天用Swing 做了一个模仿QQ2009的登录界面,用到了开源的Look&Feel (Substance),在使用的过程中遇到了一些问题,也学到了一些技巧。
Substance (https://substance.dev.java.net)有很多的外观可以选择,而且都很漂亮,还提供了强大的API供开发者使用。Substance 现在已经
发展到5.2RC 版了,一开始我使用的是5.1版,但是在使用的过程中发现了一些bug,当我使用Substance中提供的水印功能时,出现了许多的异常,这个bug在5.0的时候是没有的,后来我又下载了最新的5.2RC版,这个问题在5.2版中已经解决了。  阅读全文

BlogJava-何以解忧?唯有Java - May 22, 2009 08:19 AM
Swing中Timer 的一个使用例子

摘要: 最近在学习Swing中的动画绘制,用到了Timer 这个类,于是写一点笔记,和大家分享。大家有什么好的例子不妨共享出来吧!!
计时器在java.swing包中的Timer类来创建,它可以看做是GUI的一个组件。与其他组件不一样的是,它没有可以显示在屏幕上的直观的外观。正如名字所表达的,它只帮我们来计时。
计时器对象按相等的时间间隔来产生动作事件。执行动画程序时,可以设置计时器来定期产生动作事件,然后在动作监听器中更新动画图形。

  阅读全文

Some reminiscences, some memories » NetBeans - May 22, 2009 05:55 AM
在游戏中使用脚本语言

这只是一个有趣的探索,demo 使用 java 编写。模拟了一个龙与地下城类 RPG 游戏中,在不同的房间内移动的简单游戏场景。

阅读本文前,首先下载使用 Netbeans 6.5 建立的完整项目代码:下载。然后,我会用 UML 图的方式来说明如何在游戏中使用脚本,其中可能还会简介一下游戏中实体对象的建立和管理(不知道值得不值得另外写一篇文章来介绍了)。

I have a dream…

现在要构建一个极为无聊的小世界。说它小,是因为我只打算让它有三个房间,三个房间之间两两互通的门。仅此而已。首先上图:三个房间

我们假设左边上面的房间叫 room-1,右边上面的房间叫 room-2,下面的房间叫 room-3。玩家在这三个房间中穿行,当然,不可能是穿墙。人,一定是要走门的。如图。就这么简单的逻辑而已,不用脚本语言也能轻松完成。不过如果希望多来一点拓展性呢?比如,room-3 不允许等级在5级以下玩家进入;room-2 当十级以上玩家进入后就会自动瞬间移动到 room-1;门锁住以后就不能通过,更夸张一点,门锁住以后如果不把锁打坏就不能通过……可能性太多了。不用脚本的情况下,如果要将这么多都实现,是一件非常繁琐的事情。

好吧,让我们来看看那些游戏公司是怎么解决这个问题的。哦,需要说明的是,这里的解决方案仅仅是一个 demo,只用来解释原理。真正的环境中,还要更复杂一点,不过也就是复杂一点点而已。

类图游戏中实体类如图设计,为了简化期间,没有实体管理器,所以也没有集成自统一的父类。

Ninny 类也就是玩家,有保存当前所在房间的成员变量。Room 类也就是房间,保存有在当前房间的玩家列表。Door 是描述门的类,保存了这个门连接的两个房间的列表。

为了简单期间,demo 中使用了 java 内置的 javascript 作为脚本语言,详情看这里

本来还想多写一点的,急着出门。算了,反正有代码,大家看看先。有空回头写。重点在 js 目录下的那几个脚本 enterDoor.js,exitDoor.js,enterRoom.js,exitRoom.js。

好,继续!

为了用起来方便,稍稍封装了一下Scripting的代码。大家直接看代码,不说话:

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class Script {

    private final static ScriptEngineManager factory = new ScriptEngineManager();
    private final static ScriptEngine engine = factory.getEngineByName("JavaScript");

    public void put(String key, Object value) {
        engine.put(key, value);
    }

    public Object get(String key) {
        return engine.get(key);
    }

    public Object eval(String fileName) throws ScriptException {
        try {
            return engine.eval(new FileReader(System.getProperty("user.dir", ".") + "/js/" + fileName + ".js"));
        } catch (FileNotFoundException ex) {
            Logger.getLogger(Script.class.getName()).log(Level.SEVERE, null, ex);
        }
        return null;
    }
}

从类图上可以看到,Door 和 Room 都实现了 Enterable 接口,两个方法:一个入 enter,一个出 exit。显然,当进入一个 Room 的时候,必须从另外一个房间出来。为了保证这个一致性,所以限定玩家不能穿墙只能走门:

        Room r1 = new Room("room-1");
        Room r2 = new Room("room-2");
        Door d12 = new Door();
        d12.add(r1);
        d12.add(r2);
        Ninny player = new Ninny("ninny", r1);
        d12.enter(player);

在不用脚本的情况下怎么写这个 Door 的 enter 方法呢?首先判断一下 ninny 是不是跟 Door 在同一个房间;获得 ninny 的当前房间后 exit;获得门另一端的房间 enter。就是这么简单。不过考虑到前面说说的那种种可能性,为了拓展让我们来看一看用脚本是如何处理的:

// Door 的 enter 方法
    public boolean enter(Ninny ninny) {
        Room currentRoom = ninny.getOwner();
        // 玩家在当前门所在的房间
        if(!member.containsKey(currentRoom.getName())) {
            return false;
        }
        // 门另一侧的房间
        Room nextRoom = getAnotherRoom(currentRoom);

        try {
            Script script = new Script();
            script.put("room1", currentRoom);
            script.put("room2", nextRoom);
            script.put("door", this);
            script.put("player", ninny);
            script.eval("enterDoor");
            System.out.println("enterDoor: " + script.get("message"));
            return Boolean.valueOf(script.get("result").toString());
        } catch (ScriptException ex) {
            Logger.getLogger(Door.class.getName()).log(Level.SEVERE, null, ex);
            return false;
        }
    }

这里实际上 enter 什么也没有做,只是传递了一些数据到 Script,然后执行了 enterDoor 这个脚本:

importClass(Packages.foobar.Room);
importClass(Packages.foobar.Ninny);
importClass(Packages.foobar.Door);

room1.exit(player);
room2.enter(player);

var message = player.getName() + " enter from the room " + room1.getName() +  " to the romm " + room2.getName() + "!";
var result = true;

脚本也很简单,只是让玩家推出当前房间,进入下一个房间。如果房间对进入的玩家有等级要求,则只需:

importClass(Packages.foobar.Room);
importClass(Packages.foobar.Ninny);
importClass(Packages.foobar.Door);

var result = false;
var message = '';
if(room2.needLevel >= player.level ) {
    room1.exit(player);
    room2.enter(player);

    message = player.getName() + " enter from the room " + room1.getName() +  " to the romm " + room2.getName() + "!";
    result = true;
} else {
    message = player.getName() + " can't enter the romm " + room2.getName() + "! Level" + room2.needLevel +" needed!";
}

在我的例子代码中并没有这部分代码,实际上 Room、Door、Ninny 这几个类都应该从一个父类 Entity 中继承。这个 Entity 有一个 Map 的成员变量。通过 setAttr(String, Object) 和 getAttr(String) 两个方法就可以分别设置和获取每个 Entity 的属性。这样就可以在脚本中自由的使用各种属性来实现各种功能。
通过 Enterable(可进入),Pickable(可捡起),Attackable(可攻击)等接口,调用对应的脚本来完成真正的游戏逻辑。
其实那神秘的游戏脚本化就是这么简单。本来还想画几个序列图说明一下脚本的调用,实在有些困了。
准备洗洗睡觉。尚未补充完整的内容,全当大家进阶学习吧。

BlogJava-何以解忧?唯有Java - May 20, 2009 04:44 AM
用HtmlParser 写个简单的 news爬虫

摘要: 有一段时间没写博客了,这几天我同学要赶着交毕业设计,让我帮他写个爬虫,专门抓搜狐的新闻,我用过爬虫,但是从来没有自己写过爬虫,于是Google了一下,找到了一篇不错的文章:使用 HttpClient 和 HtmlParser 实现简易爬虫 . 参考里面的代码,自己写了个简易的搜狐新闻爬虫。  阅读全文

APIDesign - Blogs - May 18, 2009 05:43 PM
API Podcast #2: Reentrancy

Listen to podcast #2: to learn about our take on Swing and its poor reentrancy. Find out what it may mean for your own API design and especially Runtime_Aspects_of_APIs that you create. Learn to fight with that problem by maximizing the declarative nature of your API. --JaroslavTulach 17:43, 18 May 2009 (UTC)

APIDesign - Blogs - May 12, 2009 07:50 PM
API PodCast #1

Listen to this: ! It is almost a year since we (me and Geertjan) started our regular API Design Tips podcasts. They used to be part of larger NetBeans podcasts, however recently I needed some promotion material for TheAPIBook and I decided to extract the API Tip parts. I am glad I can offer these sketches to you. Enjoy podcast #1. --JaroslavTulach 19:50, 12 May 2009 (UTC)

BlogJava-何以解忧?唯有Java - April 03, 2009 06:58 AM
JDK 7 中的新特性之客户端增强(Swing&Java2D)

Client
XRender pipeline for Java 2D
A new Java2D graphics pipeline based upon the X11 XRender extension, which provides access to much of the functionality of modern GPUs
Forward-port 6u10 features
Forward-port implementation-specific features from the 6u10 release: The new Java Plug-In, Java Kernel, Quickstarter, related installer features, and the Swing Nimbus look-and-feel
Milestone target: M4
Create new platform APIs for forward-ported 6u10 features
Create new platform APIs for features originally implemented in the 6u10 release: Translucent and shaped windows, heavyweight/lightweight mixing, and the improved AWT security warning
Milestone target: M4
JSR 296: Swing application framework
An API to define the basic structure of a typical Swing application, thereby eliminating lots of boilerplate code and providing a much-improved initial developer experience
Links: JSR 296
Milestone target: M5
Swing updates
Small additions to the Swing API including the JXLayer component decorator, JXDatePicker, and possibly CSS-based styling

 



BlogJava-何以解忧?唯有Java - March 31, 2009 09:12 AM
NetBeans 6.7中 我比较喜欢的改进!

摘要: netbeans 6.7 m3 基本开发完毕了,意味着netbeans6.7 的发布也不远了。6月份的 JavaOne 大会上,netbeans6.7
就会发布了。6.7 版本 重点的开发放到了Maven ,Test,Debug 等功能上的完善和增强。

我比较喜欢的几个功能的改进:
  阅读全文

BlogJava-何以解忧?唯有Java - March 30, 2009 01:16 AM
几个学习Swing& Java2D 的好地方!(2009年4月2日更新)

(2009年4月2日更新) 

最近在网上找到了一些比较好的学习Swing 的网站,不敢独享,发表上来大家一起学习。

 

1.第一个就不用说了,Sun官方的教程 相当的不错。(英语基础不一定要很好,安装个Lingoes 就可以了)

 

Swing :   http://java.sun.com/docs/books/tutorial/uiswing/TOC.html

 

Java2D:   http://java.sun.com/docs/books/tutorial/2d/index.html

 

btw:你也可以将教程下载到本地上看

http://cds.sun.com/is-bin/INTERSHOP.enfinity/WFS/CDS-CDS_Developer-Site/en_US/-/USD/VerifyItem-Start/tutorial-2008_03_14.zip?BundledLineItemUUID=Ga9IBe.oL0QAAAEgy6s0_EWl&OrderID=WkRIBe.oIjoAAAEguqs0_EWl&ProductID=D4NIBe.oo4wAAAEY3mU9OsN.&FileName=/tutorial-2008_03_14.zip

 

2. 第二个也是个英文的网站 代码和实例都很多,也是难得的学习Swing 和Java2D 的好地方

 

Swing:http://aplcenmp.apl.jhu.edu/~hall/java/Swing-Tutorial/


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

 

3.第三个还是英文的网站,这个网站上介绍的组件不多,但是代码很经典,而且还有几个简单的游戏教程。

Swing :http://www.zetcode.com/tutorials/javaswingtutorial/

 

Java2D:http://www.zetcode.com/tutorials/java2dtutorial/

 

Java2D Games:http://www.zetcode.com/tutorials/javagamestutorial/    (这个游戏教程不错

 

4.第四个还是英文的学习站点 ,这个个站点的学习文章比较多,涉及的方面也比较广,是个不错的学习java GUI 的

站点。 

 

Swing:http://life.csu.edu.au/java-tut/uiswing/

 

ps:为什么国外的Swing教程那么多呢?而且质量也不错!

       今年的工作不好找,只好再努力的巩固基础,厚积薄发!



APIDesign - Blogs - December 12, 2008 09:06 AM
2009: The Year of Annotations

As I noted recently, I see the year 2009 as the year of annotations. The NetBeans project is about to rely on them more heavily. Finally! We've been waiting for that for ages, but finally we can compile with JDK 1.6 JavaC and we can use compile time annotation processors. As a result we can replace our layer based registrations with annotations and benefit from compile type checking, code completion, from having the registrations in the same place as the code that is being registered, etc. Also we can offer our API users simple looking annotations and let associated annotation processors do more advanced and more effective processing. As a result the developers have simple API to deal with, while actual registration hidden behind can be as effective as possible, even at the cost of complexity, but without compromises to reliability (as the complexity is kept in the processing infrastructure, not exposed to API users).

The other project related to annotations that we are likely to incorporate during 2009 is our extended use of Annotations for Software Defect Detection. This is heavily based on the JSR 305, yet until it is stable we do not want to expose such unstable API to users of our stable APIs (more on that in Chapter 10, in section Beware of Using Other APIs). As such we are going to create our own annotations (still recognizable by FindBugs and co.). The hope is that our annotation will stay compatible even if the underlaying JSR 305 slightly changes. Please find our current patch and comment here or in the issue 137437.

Last project that deals with annotations is developed by our editor hints guru Jan Lahoda - its aim is to bring complex refactoring to masses! How? Why? We have observed that using @Deprecated annotation is good hint to help your API users recognize that some part of your API is obsolete and shall no longer be used, however that in no way helps users of your API with converting their code to new, non-deprecated style. We have a solution: Use Code Transformation Annotations! Dear [API] writers, let's adopt these annotations and use them in your API! They are completely standalone (read more), lightweight and we are ready to incorporate feedback of everyone interested in the project. Indeed, my plan is to bring these easy to use and flexible refactorings to NetBeans soon, hopefully for version 7.0.

So these are my three annotation related projects. I find them quite exciting and I cannot wait to see them being used. Annotations are here to simplify life of API users and developers. As soon as we have them, we will have full right to call the year 2009 the year of annotations!

Listen to our podcast or download it.

Name (required):

Website:

Comment:

--JaroslavTulach 09:06, 12 December 2008 (UTC)

平步星云 - December 10, 2008 05:08 PM
Mark Occurrences in NetBeans

Mark Occurrences (元素高亮)指的是当鼠标放在某个类元素上时,在当前文件高亮出该元素的 声明和引用。
如果你使用过 Find Usages 的话,你应该能知道这个功能的好处。但是 Mark Occurrences 比起 Find Usages 来属于轻量级的。因为:

  1. Mark Occurrences 只搜索当前文件
  2. Mark Occurrences 为动态的,不需要菜单来激活只需要将鼠标放在要查看的元素上即可。
  3. Mark Occurrences 懂得语义,比如将鼠标指向当前类的超类,它将显示所有被实现/覆盖的方法;放在方法的返回类型上,将显示方法的所有返回的语句上;...
如果你使用的是 NetBeans 6.0 的开发版的话,这个功能已经内置了,不过它被叫做 Highlights:

  • 成员作用域


  • 方法返回点

  • 特定异常抛出点




如果你使用的是 NetBeans 5.x 系列的话,你需要注册一个更新中心,然后下载插件,具体步骤如下:
  1. 选择 Tools | Options
  2. 单击 Advanced Options 按钮
  3. 选择 Options | IDE Configuration | System | Autoupdate Types
  4. 右击并选择 New | General Update Center
  5. Name 输入域中输入Sandip Chitale's Modules 然后单击 Finish
  6. (此步骤不必,如果你在完成上述步骤后立即进行下面的步骤时) 展开Autoupdate Types node, 选择 Sandip Chitale's Modules
  7. 在右边的窗口中的第一个属性值Server URL,输入: http://blogs.sun.com/roller/resources/scblog/update-center.xml
  8. 确保Enabled 属性勾上。然后关闭当前窗口

现在这个更新中心就可以用了,我们目标是得到 Mark Occurrences 插件,它就在这个更新中心中:
选择Tools | Update Center 菜单. 勾上 Sandip Chitale's Modules 项,如果它还没被勾上的话。依照更新向导在 Select Modules to Install 窗口中选择
Sandip Chitale's Modules 选择Make Occurrences

虽然作者声明:这个模块是个实验性的模块,但是到目前为止在我的工作中我一直在用它,而且没有出现任何问题(比起一些 Eclipse 的插件可强多了!)


注意: 安装完后(不记得要不要重启 NetBeans), 你应该能看到工具条上出现一个黄色的按钮(如下图),或者你可以从 View | Mark Occurrences 激活它:


平步星云 - December 10, 2008 05:08 PM
Hibernate Jpetstore 之四 表示层技术

文档内容

  • 概览
  • Struts 表示层组件 FormBean
    • FormBean 配置
    • FormBean 类层次
    • BaseActionForm 子类实例 AccountActionForm
  • 避免重复提交
    • Struts 的事务 Token
  • 我们还缺什么?
    • 客户端校验
    • 漂亮的页面
  • 总结



在阅读本篇文章之前,请先仔细阅读前面系列的相关内容。

概览


在各种框架欣欣向荣的今天,你能想象最初 Java WEB 开发者的日子吗?要知道,就算是JSP,当时都被寄予厚望,因为当时,开发者不得不在Servlet 中书写之如:out.println("<html><head><title>My God</title></head><body>");

你能想象,以这种方式做一个象样的页面是怎样的一种情形。这种情况下,是将“表示层”的内容(HTML标记)渗透进Java代码中了,你哪怕是修改页面上的一个文字,你都不得不在上述的 println 中修改 -> 编译 -> 测试-> ...

于是,JSP应运而生,可是很快,开发者发现,情况反过来了:在JSP页面代码中到处散布有之如:


<%

String amount =
request.getParameter("amount");

if
( amount != null &&
amount.length() > 0 ) {


...
%>


也就是说,此时表示逻辑的代码渗透进页面代码中了。

于是才有后来的 JavaBeans, <jsp:useBean>, Taglibs 等,以及术语 WEB MVC, MVC2 等。

毫无疑问,对于在JAVA Web 领域工作多年的老手,看到我这篇关于 Struts 的文章肯定会觉得好土,或者甚至老掉牙了! 的确,这也是我这段时间一直在考虑是否需要写这样一个系列的原因。

不管现在 JSF. WebWorks/Struts2, SpringMVC , JBoss Seam 被如何鼓吹,Struts 作为 Web
框架的先行者,还是有它的位置。尽管此例子中所采用的方法比起最新的 Struts (Struts 1.3.x 系列)也同样显得有些陈旧,但是正如 JAVA 领域中的一惯作法,“在引入新功能前先考虑向后兼容”,因此,新的功能尽管加入吧,你可以欣喜若狂,但我也同样可以一直运行已经稳定运行好几年的产品。

随便提一下,本人并不认为上述新的WEB框架使开发工作简化了多少,相反,倒是增加了不少复杂性。作为新手,很难保证在研究这些框架一周后能开发出一个稳
定可靠的方案。相反象几个简单的框架反而在引入面向 Page 的设计方法的同时,简化了开发的难度:Wicket Click ,而且更加符合当今的 Web2 的需求。

再有,由于 JSTL 的流行,几乎所有的 Web 框架都依靠它来排除JSP脚本。但是我们不会在这里介绍每个 JSTL 标记的用法,具体的用法见工程的JSP 源代码。

好了,一来就说了这么多,无非是为了引入主角 Struts,但是请原谅,关于整个Struts
的介绍是需要一整书才能介绍完的。所以我们还是以代码为依托,一步步来吧。
我们的主题是表示层的相关技术。

Struts 表示层组件 FormBean

FormBean 配置


FormBean 即是我们熟悉的 JSP + JavaBean 设置方式中的 JavaBean,只不过它作为 Struts 框架的组件担任起页面表单与 Struts Action的信息传递的使者。

为了弄清 FormBean 的工作原理,我们现在给出我们整个的 struts-config.xml 文件的内容

<?xml version="1.0"
encoding="GBK"?>

<!DOCTYPE struts-config PUBLIC
"-//Apache Software
Foundation//DTD Struts Configuration 1.1//EN"

"http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">

<struts-config>
<!-- 配置此应用中的所有 FormBean
-->

<form-beans>

<!-- 这种复用
FormBean 的方式值得讨论,见紧随其后的说明 1 -->


<!--
与注册帐户和帐户信息相关的页面使用的 FormBean -->


<form-bean name="accountForm"
type="org.springframework.samples.jpetstore.web.struts.AccountActionForm"/>

<!--
与购物车相关的页面使用的 FormBean -->


<form-bean name="cartForm"
type="org.springframework.samples.jpetstore.web.struts.CartActionForm"/>

<!-- 没有对应的页面元素的
FormBean, 例如点击一个链接或按下搜索按钮等等功能,被设计成不需要 FormBean 来收集用户输入 -->

<form-bean
name="emptyForm"
type="org.springframework.samples.jpetstore.web.struts.BaseActionForm"/>

<!-- 与帐户修改相关的页面使用的
FormBean,因为此时也许在当前的 session 已经存在了一个 accountForm -->

<form-bean
name="workingAccountForm"
type="org.springframework.samples.jpetstore.web.struts.AccountActionForm"/>

<!-- 与所有定单操作相关的页面使用的 FormBean -->
<form-bean
name="workingOrderForm"
type="org.springframework.samples.jpetstore.web.struts.OrderActionForm"/>

</form-beans>


<!-- 全局跳转声明,这此跳转可以被所有的
Action 中共享 -->


<global-forwards>

<forward
name="failure" path="/WEB-INF/jsp/struts/Error.jsp"
redirect="false"/>

<forward
name="unknown-error" path="/WEB-INF/jsp/struts/Error.jsp"/>

<forward
name="global-signon" path="/WEB-INF/jsp/struts/SignonForm.jsp"/>

</global-forwards>

<!-- 以下为所有的 Action 映射
-->


<action-mappings>

<!--
点击链接将一只宠物加入购物车 -->


<action path="/shop/addItemToCart"
type="org.springframework.samples.jpetstore.web.struts.AddItemToCartAction"


name="cartForm" scope="session" validate="false">


<forward name="success" path="/WEB-INF/jsp/struts/Cart.jsp"/>

</action>

<!-- 结算购物车
-->


<action path="/shop/checkout"
type="org.springframework.samples.jpetstore.web.struts.ViewCartAction"


name="cartForm" scope="session" validate="false">


<forward name="success" path="/WEB-INF/jsp/struts/Checkout.jsp"/>
</action>

<!--
修改帐号信息 -->


<action path="/shop/editAccount"
type="org.springframework.samples.jpetstore.web.struts.EditAccountAction"

name="workingAccountForm" scope="session" validate="true"
input="/WEB-INF/jsp/struts/EditAccountForm.jsp">
<forward name="success" path="/shop/index.do"/>
</action>
<action
path="/shop/editAccountForm"
type="org.springframework.samples.jpetstore.web.struts.EditAccountFormAction"
name="workingAccountForm" scope="session"
validate="false">

<forward name="success"
path="/WEB-INF/jsp/struts/EditAccountForm.jsp"/>

</action>
<action
path="/shop/index"
type="org.springframework.samples.jpetstore.web.struts.DoNothingAction"


validate="false">


<forward name="success" path="/WEB-INF/jsp/struts/index.jsp"/>

</action>
<action
path="/shop/help"
type="org.springframework.samples.jpetstore.web.struts.DoNothingAction"


validate="false">


<forward name="success" path="/WEB-INF/jsp/struts/help.jsp"/>

</action>
<action
path="/shop/listOrders"
type="org.springframework.samples.jpetstore.web.struts.ListOrdersAction"


name="accountForm" scope="session" validate="false">


<forward name="success"
path="/WEB-INF/jsp/struts/ListOrders.jsp"/>

</action>
<action
path="/shop/newAccount"
type="org.springframework.samples.jpetstore.web.struts.NewAccountAction"


name="workingAccountForm" scope="session" validate="true"
input="/WEB-INF/jsp/struts/NewAccountForm.jsp">


<forward name="success" path="/shop/index.do"/>

</action>
<action
path="/shop/newAccountForm"
type="org.springframework.samples.jpetstore.web.struts.NewAccountFormAction"


name="workingAccountForm" scope="session" validate="false">


<forward name="success"
path="/WEB-INF/jsp/struts/NewAccountForm.jsp"/>

</action>

<!--
进入结算中心页面后,点击继续进入此 -->



<action path="/shop/newOrderForm"
type="org.springframework.samples.jpetstore.web.struts.NewOrderFormAction"


name="workingOrderForm" scope="session" validate="false">


<forward name="success"
path="/WEB-INF/jsp/struts/NewOrderForm.jsp"/>

</action>

<!--

fixed by pprun: 将原先混在一起的逻辑打破成几个小部分,否则在多步向导式提交


页面中的任何一步出错都无理地返回到 NewOrderForm.jsp 页面,而不是真正的出错的页面


-->

<!--
<action
path="/shop/newOrder"
type="org.springframework.samples.jpetstore.web.struts.NewOrderAction"


name="workingOrderForm" scope="session" validate="true"
input="/WEB-INF/jsp/struts/NewOrderForm.jsp">


<forward name="confirm"
path="/WEB-INF/jsp/struts/ConfirmOrder.jsp"/>


<forward name="shipping"
path="/WEB-INF/jsp/struts/ShippingForm.jsp"/>


<forward name="success" path="/WEB-INF/jsp/struts/ViewOrder.jsp"/>

</action>
-->

<!--
填写定单信息的多页向导式页面 -->


<!--
当第一页校验失败时,需要跳回填写购物单的第一页 -->




<action path="/shop/newOrderStep1"
type="org.springframework.samples.jpetstore.web.struts.NewOrderAction"


name="workingOrderForm" scope="session" validate="true"
input="/shop/newOrderForm.do">


<forward name="confirm"
path="/WEB-INF/jsp/struts/ConfirmOrder.jsp"/>


<forward name="shipping"
path="/WEB-INF/jsp/struts/ShippingForm.jsp"/>

</action>

<!--
(只有页面上填写了将宠物送到不同的地址时,默认为送到当前用户的地址),
才会出现此面。此页校验失败,毫无疑问应该回到这个新地址填写页,
而不是整个流程的第一页。这就是原版中的BUG所在处,因为它将这个向导性的流程
处理放到了一个映射中,所以没法处理这种情况 -->

<action
path="/shop/newOrderStep2"
type="org.springframework.samples.jpetstore.web.struts.NewOrderAction"


name="workingOrderForm" scope="session" validate="true"
input="/WEB-INF/jsp/struts/ShippingForm.jsp">


<forward name="confirm"
path="/WEB-INF/jsp/struts/ConfirmOrder.jsp"/>

</action>

<!--
当在最后一步确认时出错,需要跳回填写购物单的第一页 -->

<action
path="/shop/newOrderStep3"
type="org.springframework.samples.jpetstore.web.struts.NewOrderAction"


name="workingOrderForm" scope="session" validate="true"
input="/WEB-INF/jsp/struts/NewOrderForm.jsp">


<forward name="success" path="/WEB-INF/jsp/struts/ViewOrder.jsp"/>

</action>
<!-- fixed
end -->



<action
path="/shop/removeItemFromCart"
type="org.springframework.samples.jpetstore.web.struts.RemoveItemFromCartAction"


name="cartForm" scope="session" validate="false">


<forward name="success" path="/WEB-INF/jsp/struts/Cart.jsp"/>

</action>
<action
path="/shop/searchProducts"
type="org.springframework.samples.jpetstore.web.struts.SearchProductsAction"


name="emptyForm" scope="session" validate="false">


<forward name="success"
path="/WEB-INF/jsp/struts/SearchProducts.jsp"/>

</action>
<action
path="/shop/signon"
type="org.springframework.samples.jpetstore.web.struts.SignonAction"


name="accountForm" scope="session" validate="false">


<forward name="success" path="/shop/index.do"/>

</action>
<action
path="/shop/signonForm"
type="org.springframework.samples.jpetstore.web.struts.DoNothingAction"


validate="false">


<forward name="success"
path="/WEB-INF/jsp/struts/SignonForm.jsp"/>

</action>
<action
path="/shop/updateCartQuantities"
type="org.springframework.samples.jpetstore.web.struts.UpdateCartQuantitiesAction"


name="cartForm" scope="session" validate="false">


<forward name="success" path="/WEB-INF/jsp/struts/Cart.jsp"/>

</action>
<action
path="/shop/viewCart"
type="org.springframework.samples.jpetstore.web.struts.ViewCartAction"


name="cartForm" scope="session" validate="false">


<forward name="success" path="/WEB-INF/jsp/struts/Cart.jsp"/>

</action>
<action
path="/shop/viewCategory"
type="org.springframework.samples.jpetstore.web.struts.ViewCategoryAction"


name="emptyForm" scope="session" validate="true"
input="/WEB-INF/jsp/struts/index.jsp">


<forward name="success" path="/WEB-INF/jsp/struts/Category.jsp"/>

</action>
<action
path="/shop/viewItem"
type="org.springframework.samples.jpetstore.web.struts.ViewItemAction"


name="emptyForm" scope="session" validate="true"
input="/WEB-INF/jsp/struts/Product.jsp">


<forward name="success" path="/WEB-INF/jsp/struts/Item.jsp"/>

</action>
<action
path="/shop/viewOrder"
type="org.springframework.samples.jpetstore.web.struts.ViewOrderAction"


name="accountForm" scope="session" validate="false">


<forward name="success" path="/WEB-INF/jsp/struts/ViewOrder.jsp"/>

</action>
<action
path="/shop/viewProduct"
type="org.springframework.samples.jpetstore.web.struts.ViewProductAction"


name="emptyForm" scope="session" validate="true"
input="/WEB-INF/jsp/struts/index.jsp">


<forward name="success" path="/WEB-INF/jsp/struts/Product.jsp"/>

</action>
</action-mappings>

</struts-config>



说明:

1. 从FormBean 的数量和每个使用了FormBean的 Action 映射可知,这里存在 FormBean
复用问题,即用一个来服务多个 Action, 这种方式可以大大减少 FormBean 的数量,
但是,在 Action中的逻辑变得复杂了。因为在 FormBean 中包括了所有 Action 的需求,
而在Action中不得不排除它所不需要的元素的干扰。这样使代码看起来很混乱。

2. 有了全局声明,在 Action 的代码中就可以随时发出 mapping.findForward("failure");
之类的代码,而不需要在对应的 Action 映射中配置该 <forward> 子元素.


3. 注释掉的 Action 映射是为了修复一个BUG:

因为在涉及到定单提交时,采用的是多页提交(也就向导页面)方式,即收集的信息是从连续多个
页面中获得的,而不是普通的从一个页面中得到的。这样就涉及到,当其中的一个页面出现校验
失败时,将要将流控跳转到出错的页面,通过将原先混在一起的逻辑打破成几个小部分,
否则在多步向导式提交页面中的任何一步出错都无理地返回到 NewOrderForm.jsp 页面,
而不是真正的出错的页面,请看相应的映射元素的注释说明。


4. 为了保持连贯性,关于每个映射元素的每个属性,我们重复 Hibernate
JPetstore 系列之三: 控制层技术
中的 ActionForm <-- struts-config.xml -->Action 部分的内容:

1. path=/shop/editAccount

所有要去住 /shop/editAccount (严格地讲,如果按照Struts 的方言是 /shop/editAccount.do)
的请求,都要遵循这里的配置

2. name="workingAccountForm"

都将绑定 workingAccountForm ,注意 form-bean 在 Action 中引用是通过 name
属性来引用的,它是在一开始定义的: <form-bean name="workingAccountForm"
type="org.springframework.samples.jpetstore.web.struts.AccountActionForm"/>

3. scope="session" 该 bean

将在整个会话其间始终有效 ,但注意这个配置是多余的,默认就是 session 上下文的,反而如果 bean 只在
request上下文时,才需要明确地声明。


4. validate="true" 将对表单的输入调用 form-bean 的 validate 方法。但我们发现在
AccountActionForm.java 中,只有 doValidate(...) 方法,并没有 validate()方法,
但细心的话,应该发现了,和所有的Action 都是从BaseAction 中派生而来一样,
所有的ActionForm 中都是从一个基类 BaseActionForm.java 中派生下来。

其中定义了所有 formbean都需要的东西,对于校验错误的处理。

其中就是 validate(...) 方法,并在其中调用了 doValidate() 方法,

而每个BaseActionForm 的子类只要override 这个doValidate() 方法,

如果 validate="true"声明了的话,那么子类中的 doValidate() 方法将会被调用。这是多态性的表现。


5. input="..." 一般来讲,一个表单在校验失败后都需要回去重纠正错误的输出项,
所以我们通过这个值来告诉 Struts该回到哪去纠错.

FormBean 类层次


与 Action 类一样,FormBean 也基于类的继承关系设计的,这样子类 FormBean
只需实现父类 FormBean指定的约束,所有的子类都复用父类中的功能并按照这种设计约束工作。

基类 BaseActionForm

该类本身又是从 抽象类 org.apache.struts.action.ActionForm 派生而来,
所以应用中的所有 FormBean只要从 BaseActionForm 派生即可:


public class BaseActionForm
extends ActionForm {

/**
*此乃最原始的错误处理方法,将所有错误信息加入到一个列表后,然后存入到
*Servlet 请求属性中供页面使用.
*更现代的方法是使用 Struts1.1 之后的 commons-validator,关于这个功能在
* 各种关于Struts 的参考或书籍中都有介绍。
*
*是否为调用此方法是通过属性 validate 来控制的,如:
*<action path="/shop/signon"type="org.springframework.samples.jpetstore.web.struts.SignonAction"
*name="accountForm" scope="session" validate="false">
*是不会调用的,因为 validate="false".
   
* 这是“模板方法”(Template method) 设计模式中的“方法”
*/

public ActionErrors validate(ActionMapping mapping,
HttpServletRequest request) {
ActionErrors errorMessages =
null;



// 整个系统的错误信息列表,通过调用doValidate(mapping, request, errorList);

// addErrorIfStringEmpty 会将错误信息加入到列表当中,并且它被存入了请求属性当中.


ArrayList errorList = new
ArrayList();

doValidate(mapping, request,
errorList);


request.setAttribute("errors", errorList);

if (!errorList.isEmpty()) {


errorMessages = new org.apache.struts.action.ActionErrors();


errorMessages.add(ActionErrors.GLOBAL_MESSAGE, new
ActionMessage("global.error"));

}

return errorMessages;

}



/**
*此方法被设计为供子类覆盖的(overriding).
*任何子类实现了这个方法,将自动被上面的 validate 方法调用。
*
* 这是“模板方法"模式中的默认钓子方法,由子类的实现方法来替换。
*
* @param mapping
* @param request
* @param
errors

*/

public void doValidate(ActionMapping mapping,
HttpServletRequest request, List errors) {

}


/**
*此辅助方法被用来给定的页面输入内容是否为空,如果是空的话,将显示给定的出错信息。
*
* @param errors 错误信息列表
* @param message 当 value 为空时,将显示这个错误信息
* @param value 页面元素对应的值
*/

protected void addErrorIfStringEmpty(List errors,
String message, String value) {

if (value == null ||
value.trim().length() < 1) {

errors.add(message);

}

}

}



BaseActionForm 子类实例 AccountActionForm


我仅介绍一个子类 AccountActionForm :


public class AccountActionForm
extends BaseActionForm {


/** 用于检验的常量定义,因为在新建帐户与修改帐户时检验逻辑是不一样的。
* 至少在修改帐户时,帐户名是已经存在了 */

public static final String VALIDATE_EDIT_ACCOUNT = "editAccount";
public static final String VALIDATE_NEW_ACCOUNT = "newAccount";

/** 用于存贮用户的首先语言的列表 */
private static final ArrayList LANGUAGE_LIST = new ArrayList();

/* Private Fields */

// 看起来好象与 Account 中的成员重复了,这是因为此 Form 被多个页面重复使用的

// 结果,因为在登录页面时,那时根本不存在 Account, 所以不可能通过

// account.getUsername() 和 account.getPassword() 来得到用户的输入值的,



// 下面两项即是在登录当时收集输入 信息,

// 其它情况(比如修改,新建帐户时)都是间接使用了 Account 中的成员,因当时都

// 已经在 session 中存放了一个 Account 的实例

// 所以重用是有代价的(使代码不那么直观了,如果是一个页面表单 Form 对应一个

// FormBean 的话,以下成员与页面中的输入元素是一一对应的)



// 供登录页面使用的 元素

private String username;
private String password;

// 登录后,与帐户相关的元素
private String repeatedPassword;
private List languages;
private List categories;

/**

* 这个成员的值是通过页面隐藏元素传入的:

* NewAccountForm.jsp 中: <html:hidden name="workingAccountForm" property="validate"
value="newAccount"/>

* EditAccountForm.jsp 中:<html:hidden name="workingAccountForm" property="validate" value="editAccount" />
*/
private String validate;

/**
* 用来记住用户是从哪里跳转过来的,因为准备对购物车进行结算时,如果没有登录

* 的话,首先将结算中心页面的地址存入此成员中,登录成功后再跳转过去。

* 如果没有这样一步操作的话,那么就会出现讨厌的将你送回首页面(也就是程序

* 的逻辑流程打扰了用户的进程,这是最应当避免的。)

*/

private String forwardAction;

/**

* 所有的帐号信息放在这个 POJO 中

*/

private Account account;

/**
* 用于显示标语,当你在用户信息页面选择显示标语时

*/

private String bannerName;

/**
* 用于显示根据用户的喜好被推荐的宠物列表,当你选择了显示该列表时。

*/

private PagedListHolder myList;

/**
* 用户最喜欢的宠物类别

*/

private String favCategoryName;

/* Static Initializer */

static {

LANGUAGE_LIST.add("english");


LANGUAGE_LIST.add("japanese");

}

public AccountActionForm() {

languages = LANGUAGE_LIST;

}

public PagedListHolder getMyList() {

return myList;

}
public void setMyList(PagedListHolder myList) {

this.myList = myList;

}

public String getForwardAction() {

return forwardAction;

}
public void setForwardAction(String forwardAction) {

this.forwardAction = forwardAction;

}

public String getUsername() {

return username;

}
public void setUsername(String username) {

this.username = username;

}

public String getPassword() {

return password;

}

public void setPassword(String password) {

this.password = password;

}

public String getRepeatedPassword() {

return repeatedPassword;

}

public void setRepeatedPassword(String repeatedPassword) {

this.repeatedPassword = repeatedPassword;

}

public Account getAccount() {

return account;

}

public void setAccount(Account account) {

this.account = account;

}

public List getLanguages() {

return languages;

}
public void setLanguages(List languages) {

this.languages = languages;

}

public List getCategories() {

return categories;

}
public void setCategories(List categories) {

this.categories = categories;

}

public String getBannerName() {

return bannerName;

}

public void setBannerName(String bannerName) {

this.bannerName = bannerName;

}

public String getFavCategoryName() {

return favCategoryName;

}

public void setFavCategoryName(String favCategoryName) {

this.favCategoryName = favCategoryName;

}

public String getValidate() {

return validate;

}

public void setValidate(String validate) {

this.validate = validate;

}

/**
* 覆盖父类中的方法”默认钓子“方法,用于特定于此子类的输入校验

*/

public void doValidate(ActionMapping mapping,

HttpServletRequest request, List errors) {


if (validate != null) {


if (VALIDATE_EDIT_ACCOUNT.equals(validate) ||


VALIDATE_NEW_ACCOUNT.equals(validate)) {


if (VALIDATE_NEW_ACCOUNT.equals(validate)) {


// 是新建帐户时,需要额外的校验


account.setStatus("OK");


addErrorIfStringEmpty(errors, "User ID is required.",


account.getUsername());




if (account.getPassword() == null ||


account.getPassword().length() < 1 ||


!account.getPassword().equals(repeatedPassword)) {


errors.add("Passwords did not match or were not provided. " +


"Matching passwords are required.");


}


}




if (account.getPassword() != null &&


account.getPassword().length() > 0) {


if (!account.getPassword().equals(repeatedPassword)) {


errors.add("Passwords did not match.");


}


}




addErrorIfStringEmpty(errors, "First name is required.",


this.account.getFirstname());


addErrorIfStringEmpty(errors, "Last name is required.",


this.account.getLastname());


addErrorIfStringEmpty(errors, "Email address is required.",


this.account.getEmail());


addErrorIfStringEmpty(errors, "Phone number is required.",


this.account.getPhone());


addErrorIfStringEmpty(errors, "Address (1) is required.",


this.account.getUserAddr().getAddr1());


addErrorIfStringEmpty(errors, "City is required.",


this.account.getUserAddr().getCity());


addErrorIfStringEmpty(errors, "State is required.",


this.account.getUserAddr().getState());


addErrorIfStringEmpty(errors, "ZIP is required.",


this.account.getUserAddr().getZipcode());


addErrorIfStringEmpty(errors, "Country is required.",


this.account.getUserAddr().getCountry());


}


}



}

/**

* 此方法是一个很重要的方法,我们看看基类中对该方法的描述:


*


* Reset bean properties to their default state, as needed.


* This method is called before the properties are repopulated by the
controller.


* 在需要时,复位 Bean 的属性值,此方法是在控制器重新组装Bean的属性值之前调用的。


*


* The default implementation does nothing. In practice, the only
properties


* that need to be reset are those which represent checkboxes on a


* session-scoped form. Otherwise, properties can be given initial values


* where the field is declared.


* 默认的实现,并没有做任何事。实际上,唯一需要重置的属性是那些基于


* session 作用域的复选框页面元素。否则这些元素将使用页面上声明的默认值。


* 是勾选还是未勾选。


*


* If the form is stored in session-scope so that values can be collected


* over multiple requests (a "wizard"), you must be very careful of
which properties,


* if any, are reset. As mentioned, session-scope checkboxes must be
reset to


* false for any page where this property is set. This is because the
client


* does not submit a checkbox value when it is clear (false).


* If a session-scoped checkbox is not proactively reset, it can never
be set to false.


* 假如表单是存贮在 Session 作用域中(如:


* <action
path="/shop/signon"
type="org.springframework.samples.jpetstore.web.struts.SignonAction"


* name="accountForm" scope="session"
validate="false">


* 即声明为 session 范围的 formBean)的话,表单元素的值可以在多个请求(即多页向导性页面)


* 中被收集,此时必须小心对等哪些输入域必须重置。象我们前面所述,session


* 作用域范围内的 checkbox(复选按钮),在为它们设置值之前必须重置为 false,


* 因为客户端(即浏览器)在复选按钮未被勾选时并不会发送任何值到服务器端。(否则,


* 就出现这样的问题:如果之前该复选按钮是勾选状态,并且用户请求这一页面


* 该按钮显示为勾选状态,在后续的操作中,用户取消选中状态。但是因为 checkbox


* 在取消选中状态后,浏览器并不发送任何关于这个控件的信息,但 ActionForm 中


* 要改变控制的状态,必须比较浏览器传上来的状态和当前状态,但因为浏览器并未


* 告知它,所以 ActionForm 认为这个控件的状态并未改变。因为从这时开始,无论


* 用户怎么做,这个控件将永远保持为选中状态)


*/

public void
reset(ActionMapping mapping, HttpServletRequest request) {


super.reset(mapping, request);


setUsername(null);


setPassword(null);


setRepeatedPassword(null);




// BUG here: by pprun


// 按照此方法的 api 文档说明,说 checkbox 的值必须在此复位,


//可是 NewAccountForm.jsp 中 Enable MyList 和 Enable MyBanner 却没有


// 所以当用户第一次选中后,以后想改为未选中是没门了,(除了象程序控制那样:


// 比如:
acctForm.getAccount().setDisplayMylist(


//
request.getParameter("account.displayMylist") != null);


// acctForm.getAccount().setDisplayBanner(


//
request.getParameter("account.displayBanner") != null);)


//


// 但是当输入错误时重新显示当前页面时,上次选为未选中状态被丢失了!


//


// 因为按照 api 的说明


// 当 checkbox 为未选中状态时,浏览器是不会发信息到服务器端的,所以


// struts 无法设置其值


// 解决办法:


if (getAccount() != null) {


getAccount().setDisplayMylist(false);


getAccount().setDisplayBanner(false);


}

}
}


基于 JSTL 和 Struts HTML Tag 的 JSP


我们主要介绍一下JSP文件的总体结构。



由于此应用在表示层来讲,大体上还是属于 Demo 级别的,所以并未采用 Struts Tile
技术来对页面布局进行管理。而是使用传统的JSP表态包含指令,来包含进公共部分,如页眉,页脚及导航区域等。

所有以 Include 前缀命名的JSP都用来被其它JSP页面包含的页面块。例如:



<%@ include
file="IncludeTop.jsp" %>


页面的具体内容

<%@ include
file="IncludeBottom.jsp" %>






在 IncludeTop.jsp 中声明:


<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>


<%@ taglib prefix="fmt" uri="http://java.sun.com/jstl/fmt" %>


<%@ taglib prefix="html" uri="http://jakarta.apache.org/struts/tags-html" %>


这样我们就不需要在所有用到JSTL的页面中重复声明。
同时,我们并没有使用 Struts 的 Bean 和 Logic 等标记库,因为在 Struts 网站上声明有:



Note: - Many of the features in this taglib are also
available in the
JavaServer Pages Standard Tag Library (JSTL).
The Apache Struts
group encourages the use of the standard tags over the Struts specific
tags when possible.


避免重复提交


避免重复提交是一项挑战性的工作,如果你曾经真正参与过一个基于 B/S 结构的项目的话,
甚至基于 C/S结构的界面也同样有这样的工作,正不过在那个领域叫做控制状态管理,
比如,当你按下一个登录按钮后,而按钮并没有变为disable/不可用状态,
你可能在不经意间又点了一次该按钮,那么在一瞬间你肯定登录了两次,这种情况还好,
因为登录并不伤害系统的其它情况,只不过统计系统或许会感觉到纳闷,
为什么在不到两秒钟内,你登录了两次?

但是如果这个操作是插入一条数据或者是删除一条数据呢?

对于插入一条数据,如果系统没设唯一性检查,则两条相同的数据生成了;

对于删除数据,则第二次删除必然会失败。



知道问题的重要性了,可是对B/S 开发人员来说,问题还不止这些:

1.典型的,网络状况不是很好时,为完成一个插入操作可有会等上好几十秒的时间,
用户此时会“再点”一次,还是会“回退”,甚至是忍无可忍关掉浏览器呢?

2. 对于回退,如果前一操作是删除操作,是否需要再次进行一次删除操作?

3. 如果用户收藏起了这一个进行删除或插入操作的URL,在他/她重新激活这一链接后,
该做何处理,如果这一操作需要授权呢?

我们要介绍的机制并不是完美的机制,事实上这些现实的问题并没有列入大多数的WEB
框架的设计议程中,所以做WEB应用开发是乏味的,甚至有时会让人冒火!



Struts 的事务 Token


通过使用Struts 的事务Token 来防止重复提交是可行的,
仔细阅读org.apache.struts.action.Action中的如下方法的 javadoc

  1. generateToken

  2. saveToken

  3. isTokenValid

  4. resetToken

  5. <html:link
    transaction="true">
    If set to true, any current transaction
    control token will be included in the generated hyperlink, so that it
    will pass an isTokenValid() test in the receiving Action.



我们通过提交定单的例子来看这个事务 Token 的工作流:

我们的例子中,是要在显示确认页面中,如果点 'Continue' 按钮,会将一个定单插入到数据库中,
显然,我们需避免重复点击该按钮。


解决方案

我们得看看这个过程的映射配置:


<!-- 进入结算中心页面后,点击继续进入此 -->


<action path="/shop/newOrderForm"
type="org.springframework.samples.jpetstore.web.struts.NewOrderFormAction"


name="workingOrderForm" scope="session" validate="false">


<forward name="success"
path="/WEB-INF/jsp/struts/NewOrderForm.jsp"/>

</action>


<!--
填写定单信息的多页向导式页面 -->


<!--
当第一页校验失败时,需要跳回填写购物单的第一页 -->


<action path="/shop/newOrderStep1"
type="org.springframework.samples.jpetstore.web.struts.NewOrderAction"


name="workingOrderForm" scope="session" validate="true"
input="/shop/newOrderForm.do">


<forward name="confirm"
path="/WEB-INF/jsp/struts/ConfirmOrder.jsp"/>


<forward name="shipping"
path="/WEB-INF/jsp/struts/ShippingForm.jsp"/>

</action>


<!--
(只有页面上填写了将宠物送到不同的地址时,默认为送到当前用户的地址),才会出现此面。
此页校验失败,毫无疑问应该回到这个新地址填写页,而不是整个流程的第一页。
这就是原版中的BUG所在处,因为它将这个向导性的流程处理放到了一个映射中,
所以没法处理这种情况 -->

<action
path="/shop/newOrderStep2"
type="org.springframework.samples.jpetstore.web.struts.NewOrderAction"


name="workingOrderForm" scope="session" validate="true"
input="/WEB-INF/jsp/struts/ShippingForm.jsp">


<forward name="confirm"
path="/WEB-INF/jsp/struts/ConfirmOrder.jsp"/>

</action>


<!--
当在最后一步确认时出错,需要跳回填写购物单的第一页 -->

<action
path="/shop/newOrderStep3"
type="org.springframework.samples.jpetstore.web.struts.NewOrderAction"


name="workingOrderForm" scope="session" validate="true"
input="/WEB-INF/jsp/struts/NewOrderForm.jsp">


<forward name="success" path="/WEB-INF/jsp/struts/ViewOrder.jsp"/>

</action>



1. 因为最后三个映射使用的是同一个 NewOrderAction,再有作重复提交检查就是在这个 Action 中,因此,不可能在这个Action 的 exeute 方法中调用 saveToken(request);,
一般来说,总是在进行重复提交检查的前一个Action 中放一个 Token, 即调用 saveToken(request);方法,因此根据这个流程,我们只能在
NewOrderFormAction
中生成:正如你可以在源码中看到一样:


//避免重复提交

saveToken(request);

return mapping.findForward("success");





2. 因为我们是要避免重复按 ConfirmOrder.jsp
中的 'Continue' 按钮,因此我们需要这样写:



<%-- prevent duplication submit --%>

<center><html:link
page="/shop/newOrderStep3.do?step=3&newOrder=true" transaction="true">

<img border="0"
src="../images/button_continue.gif" />

</html:link>

</center>



3. 最后,在处理的 Action 中(即 NewOrderAction) 进行有效性检查:


protected ActionForward doExecute(ActionMapping mapping, ActionForm form,


HttpServletRequest request, HttpServletResponse response) throws Exception {

if (!isTokenValid(request, false)
{

// 如果结果不是同一个令牌,为多重提交

//resetToken(request); // 判断完不自动销毁,留待下面的逻辑处理

request.setAttribute("message", "多重提交!");
request.getSession().removeAttribute("workingOrderForm");
request.getSession().removeAttribute("cartForm");

// Fixed by pprun for duplicate-submitand bug in the next time submit:
// 竟然不再需要确认了!

request.getSession().removeAttribute("orderForm");
return mapping.findForward("failure");

} else {

// 多页表单

OrderActionForm orderForm = (OrderActionForm) form;

// 是否要进入可选的 shipingAddress 页面

if
(orderForm.isShippingAddressRequired() && orderForm.getStep().equals("1")) {

// 需要将物品寄给别人,而不是自己

return mapping.findForward("shipping");

// 两种情况:
// 1.
从页面1直接进入确认页面(不需要寄到不同的地址时)

// 2. 从 shipingAddress
进入到确认页面

} else if
((orderForm.getStep().equals("1") && orderForm.isShippingAddressRequired() == false)
|| orderForm.getStep().equals("2")) {

// 进入确认页面

return mapping.findForward("confirm");

} else if (orderForm.getOrder() != null) {

// 最终处理

// 销毁事务标记(放在此处,最开始处很重要,

// 以保证不管再快的多重提交都会得到无效的判断的)


resetToken(request);


Order order = orderForm.getOrder();
// todo 这段逻辑应该放在 DAO 层?

getPetStore().insertOrder(order);

// 成功进行后,移除会话状态,
// 以便 NewOrderFormAction 中检查出是否用户后退操作

request.getSession().removeAttribute("workingOrderForm");
request.getSession().removeAttribute("cartForm");

// Fixed by pprun for duplicate-submit and bug in the next time submit:

// 竟然不再需要确认了!所以必须移除它
request.getSession().removeAttribute("orderForm");
request.setAttribute("order", order);
request.setAttribute("message", "Thank you, your order has been
submitted.");

// 选择 ViewOrder.jsp 中的显示方式

request.setAttribute("newOrder", true);
return mapping.findForward("success");

} else {

request.setAttribute("message",
"An error occurred processing your order (order was null).");

return mapping.findForward("failure");

}

}

}



调用
isTokenValid(request,false) 判断我们上述的 1, 2, 3 三处步骤是否是按顺序成功处理完,如果中途哪个步骤重新执行,比如在执行到第三步的 doExecute()的代码resetToken(request)之前,又来了一个请求,由于此时 Token 还在,未被 resetToken, 此时比较已经存在的 Token 和 link 带进的
Token,发现它们俩不同,因此
isTokenValid(request,false)将返回 false,告之多重提交,并跳到错误页面。



我们之所以调用
isTokenValid(request, false) 这个方法并传一个 false是因为我们使用的向导页面,在这个判断之后到最终的确认页面还有一个或两个页面要处理,因此我们不能在判断完后,立即销毁 Token,而是要等到真正处理完时才这样做。但是对简单逻辑的页面,可以直接调用isTokenValid(request) 或isTokenValid(request,true) 在判断完后,直接销毁 Token.


我们还缺什么?


客户端校验

基于 JavaScript 的检验方式。Struts支持这种处理方式,只不过我们没有把这一功能加入进来而已。因为客户端检验可以在第一时间发现输入数据的问题,而不至于浪费一个数据传输来回(提交错误数
据 -> 在 FormBean 中判断为无效 -> 以错误信息的形式显示给用户)。

但是,请记住!
服务端校验是一定要做的,因为有人总喜欢在中途拦截、篡改客户发来的数据而骗过客户端的校验器。而服务端是发生在服务器上,只要服务器没被攻破,黑客是不
可能篡改这段 FormBean 代码的。


漂亮的页面

现在的页面只是个原型,离最终的漂亮还有段距离。但是这是需要美工设计人员介入的,因为一个人总不可能样样在行的。


总结


对于新手而言,看基于 Struts 的实现代码,有时的确会失去方向。此时,最好将 Strut-config.xml
文件打印一份在手边,然后对应页面上的每一个动作(提交,链接点击等)得到其要去往的URL,然后在Strut-config.xml 中找到对应的Action 映射。例如:

在 SignonForm.jsp 页面中有:


<a href="<c:url
value="/shop/newAccountForm.do"/>">

<img
border="0" src="../images/button_register_now.gif" />

</a>



于是我们在struts-config.xml 文件中搜索“/shop/newAccountForm” 找到:



<!-- 修改帐号信息 -->

<action path="/shop/newAccountForm"
type="org.springframework.samples.jpetstore.web.struts.NewAccountFormAction"


name="workingAccountForm"
scope="session" validate="false">

<forward name="success"
path="/WEB-INF/jsp/struts/NewAccountForm.jsp"/>

</action>




这样我们得知:

1. 在页面 SignonForm.jsp 中,如果点击了 注册 按钮的话,Struts 将使用 workingAccountForm
(即,类AccountActionForm) 来收集页面的即将的输入值,



2. validate="false" 所以这时不需要做任何校验,因为此时,用户还没输入数据,只是在
注册页面上点了“注册”按钮被带到了注册信息填写页面。



3. 执行 NewAccountFormAction#execute()方法,在成功处理后,将前进到页面 NewAccountForm.jsp


4. 用户输入用户信息数据


我们看到在 NewAccountForm.jsp 页面中有:

<html:form action="/shop/newAccount.do"
styleId="workingAccountForm" method="post" >



5. 我们再次在 struts-config.xml 文件中找 "/shop/newAccount", 得到


<action path="/shop/newAccount"
type="org.springframework.samples.jpetstore.web.struts.NewAccountAction"

name="workingAccountForm" scope="session" validate="true"
input="/WEB-INF/jsp/struts/NewAccountForm.jsp">

<forward name="success" path="/shop/index.do"/>
</action>


6. 这一次还是利用同一个 FormBean(已经在前面收集了用户的输入数据),
因为这一次 在NewAccountAction#execute()方法中要用到 前面的输入值.


7. 判断输入数据的合法性

如果不合法,将跳转到同一页面,但此时将显示错误信息

如果合法,则继续向前,这一次是回到首页,即这一流程宣告结束。

平步星云 - December 10, 2008 05:08 PM
NetBeans 6.0 抢先试用

NetBeans 6.0 M9 (Preview) 已经出来好些天了,还剩下最后一个里程碑就要进入测试阶段。

对我来说,自从从 Oracle JDeveloper 转向 NetBeans 以来,在工作中我一直坚持用它。也许是的我工作表现迫使远在美国那边的同事放弃了说服我使用 Eclipse 吧。你也许想知道,作为一名NetBeans 的老用户,我期待 6.0 的什么,虽然它已经很好了。

1.不要因为整个 Editor 的体系重组,而使强“稳定性”的口碑打折扣 (基本上大部分模块都重新Retouche了),因为我拒绝使用 Eclipse 的原因之一就是: JVM OutOfMemoryError.

2.Occurrences Highlight 我在之前的文章中写过关于这一功能


3.Javadoc and Declaration View 我已经等了好几年,我甚至曾经自己利用 NetBeans 的API 实现过一个类似的模块,但是因为NetBeans本身的问题(java meta data record),功能不完整。有了这两个窗口,就永远不用 Go to Source, Show JavaDoc 了,所有的信息都在手边,只需将鼠标放在想看的类元素上。



4.Code Generation dialog 我的确很厌倦写类的构造器的实现,特别是类的成员很多的情况下。还有标准的、功能完善的、性能良好的 Common Methods: equals, hashcode 的实现等。
有了它,在右图中的例子中我只需要输入
private long id;
private String name;
private int age;

其它的代码都是由它产生的。

5. Local History

或许有些功能被我忽视了,如是这样的话,请见此处的详细列表。还有整个重写的 Editor 的功能都陆续地出现在


是的,我也保留些目前还未完善的功能,我本人也没有加紧试用。如果我认为有用的话,我会进一步写出来的。

平步星云 - December 10, 2008 05:08 PM
NetBeans多语言版本,更换默认 Locale 设置时的问题

update: (这个BUG只会在中文操作系统下出现,在英文版的操作系统上一切正常。看来是一些模块在编写时没有正确地加载对应locale 的 properties 文件)

在我看来,在日常的工作平台,JDK/NetBeans 的i18n 字体问题已经成为历史了。但是我前些天又登记了一个 BUG 到 NetBeans 的 IssueZilla 中:

1. 下载多语言版本的安装包,安装
2. 因为我不喜欢那些翻译的不准确的词,所以我打算回退到英文的 locale:
增加一个启动参数到 $NetBeans/etc/netbeans.conf 文件:
netbeans_default_options="$默认的配置 --locale en"
语法为: --locale language[:country[:variant]]

3. 重启后,你将看到“著名的字体问题”重新又出现了,显示为小方框:

当前,只有两种办法可以解决这一问题:

1. 去掉加上的 locale 参数,但必须忍受翻译得不佳的词了。

2. 下载英文版,继续用吧。但这样的话,在 Welcome 中你将看不到中文的 Blog.

平步星云 - December 10, 2008 05:08 PM
GlassFish/Sun App Server 配置 MySqlXADataSource 的问题

如果你打算使用 GlassFish/Sun App Server,并且打算使用 MySql 的 XA 数据源的话。在目前的配置过程中,会遇到如下问题:

重现步骤:
1. (如果之前没做这一步的话)将 Mysql 的 Connector/J 包 (如我的:mysql-connector-java-3.1.12-bin.jar)放入 GlassFish/Sun App Server 安装目录下子目录 \AppServer\lib\ 中

2. 启动 GlassFish/Sun App Server. 可通过 右击 Runtime | Servers | Sun App Server 选择 start

3. 启动后,右击 Sun App Server 选择 View Admin console

4. 登录WEB 管理后台

5. 在左侧导航器中 点击 Resources | JDBC | Connection Pool s,在表格的头部点击 New... 按钮

6. 在右侧中填入:
Name: MySql
Resource Type: javax.sql.XADataSource
Database Vendor: mysql

然后点击 next

7. 注意在 Datasource class name 中自动填入了: com.mysql.jdbc.jdbc2.optional.MysqlXaConnectionPoolDataSource,这个值是不对的。

你如果想试试的话,在最下面的 Properties 窗格中填入 :


点击 Finish

8. 在结果窗口中点击 Mysql

9. 在打开的页面中可以看到一个ping 按钮,点击是用来测试配置成功与否,点击一下,将出现如下错误:


解决的办法:
如果解开mysql-connector-java-3.1.12-bin.jar 文件,在包com.mysql.jdbc.jdbc2.optional 中可 看到:


com/mysql/jdbc/jdbc2/optional/
com/mysql/jdbc/jdbc2/optional/CallableStatementWrapper.class
com/mysql/jdbc/jdbc2/optional/ConnectionWrapper.class
com/mysql/jdbc/jdbc2/optional/MysqlConnectionPoolDataSource.class
com/mysql/jdbc/jdbc2/optional/MysqlDataSource.class
com/mysql/jdbc/jdbc2/optional/MysqlDataSourceFactory.class
com/mysql/jdbc/jdbc2/optional/MysqlPooledConnection.class
com/mysql/jdbc/jdbc2/optional/MysqlXAConnection.class
com/mysql/jdbc/jdbc2/optional/MysqlXADataSource.class
com/mysql/jdbc/jdbc2/optional/MysqlXAException.class
com/mysql/jdbc/jdbc2/optional/MysqlXid.class
com/mysql/jdbc/jdbc2/optional/PreparedStatementWrapper.class
com/mysql/jdbc/jdbc2/optional/StatementWrapper.class
com/mysql/jdbc/jdbc2/optional/SuspendableXAConnection.class

...
其中并没有默认填入的 com.mysql.jdbc.jdbc2.optional.MysqlXaConnectionPoolDataSource, 但是有 com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource

在页面Application Server > Resources > JDBC > Connection Pools > MySql 中:
1.将 Datasource class name 的值改为: com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource

2.点击 Save 按钮

3. 点击 Ping 按钮,成功显示:

平步星云 - December 10, 2008 05:08 PM
体验更多 NetBeans 的新功能

不喜欢 NetBeans 的开发者往往指出 NetBeans 没有这个功能,没有那个功能。无可厚非,当时他们是对的,但随着时间的推移,现在也许错了:

1. Last Edit (是近更改按钮,带星号的那个),将你带到最近更改的地方




2. Diff SideBar (差异侧条), 根据所在行代码是增加、更改还是删除,在侧条中显示不同的小条,右击可以使用进一步的功能



3. JUnit4 支持(也就是现在可以使用基于 JDK 5 Annotation 的单元测试了)




4. Find/Replace in Project (全工程范围内搜索),是的,这个功能我真的也非常需要:
看到左下方的"Replace" 按钮了吗?
值得注意的是,这个功能在 M9 中被屏蔽了,但在每日构建的版本中可以使用。

[nb-find-replace.png]



另外,大部分初次使用NetBeans 的开发者,不知道更改“自动完成弹出窗口”的键绑定,因为最常用的"Ctrl + 空格" 是不可工作的,因为在中文操作系统中被绑定到输入法的切换了。所以我一般把它改成 'Ctrl + Enter' , 如下进行:
  1. Tools | Options -> Keymap -> Other
  2. 找到 Show Code Completion Popup, 选中它,点击Add...
  3. 按下任何所你希望的键序列,但是如果直接按 Ctrl + Enter 的话,系统提示这个组合键已经被绑定到 Split Line,所以如果我们要使用这个组合键的话,要先把它与 Split Line 解除绑定
  4. 在 Show Code completion Popup 下方第六个即是 Split Line, 选中它,点击移除。你可以为这个功能提供另外的组合键,如果经常使用这一功能的话。
  5. 然后,按照上述把"Ctrl + Enter " 加到 Show Code Completion Popup 中去。

(期待下一篇)

平步星云 - December 10, 2008 05:08 PM
NetBeans6 功能介绍: 布置 declaration View 和 Javadoc View

此篇文章介绍一个在 NetBeans 6 中同时查看鼠标指针处的源代码和
Javadoc (不再Go to Source .../ Show JavaDoc)

1. 首先打开它们:Window | Other | Declaration View 和 Window | Other | Javadoc View, 它们都被搁浅在 Output 窗口的位置,但此时只能看到一个窗口的内容,因为无论切换到其中的任何一个,它们都占据整个下端窗口。如下:



2. 我们要将它们分开,点击其中任一窗口的上方(类似标题栏区域),按住不放,将其拖向左侧(或右侧也可),当出现一个红色的方框后释放,如下:



释放后的效果如下:




这些位置会被记录下来,只要你在设置之后正常退出了。在下次启动 NetBeans 后,你可以看到同样的布局。

这个截图中显示的是 Integer.toHexString 方法的 Javadoc 和 实现源码。


(期待下一篇)

平步星云 - December 10, 2008 05:08 PM
Java 安全拷贝协议 (JSCP: Java Secure Copy Protocol) NetBeans 插件

JSCP NetBeans 插件的作用


大家知道,SCP 广泛使用于SSH出现之前的 Unix 之类的平台上,它允许在 Client <-> Server 间进行双向的文件传输(ScpTo, ScpFrom)

JSCP NetBeans plugin 作为一个 TopComponet 插入到 NetBeans 的 Navigator 方位,通过 Tools | Java SCP 调用。

SCP 可进行文件双向传输的
  • 向支持 SCP 的 Unix/Linux 服务器上传文件(ScpTo)
  • 从支持SCP的 Unix/Linux 服务器获得文件(ScpFrom)

这两种工作模式是分别作为 JTabbedPane 的 两个 Tab 出现在 JScp 这个 TopComponent

JSCP NetBeans 插件的使用方法

从NetBeans PluginPortal 网站上获得一个压缩包,然后解压到一个目录供下面的步骤使用。

安装 .NBM 文件

  1. Tools | Update Center
  2. 选择 Install Manually Downloaded Modules (.nbm Files) 后,点击 Next
  3. 点击 Add... , 在 Select Directory or .nbm Files 对话框中,导航到此插件的两个 .nbm 文件(com-jcraft-jsch.nbm 和 org.pprun-jscp.nbm),同时选中它们后点击 Ok
  4. 点击 Next
  5. 点击 Next
  6. 点击 Next, 在View Certificates and Install Modules 界面点击 Include 列下面的多选框中打上勾。界面将出现版权及插件签名信息。(如果你希望使用计算机的所有用户都使用这个插件,可以将在 Global 列下打勾)
  7. Finish, 不出意外,将显示插件更新界面。
  8. 等到NetBeans 的状态条中显示 Turing on modules... done. 后,点击 Tools 菜单,此时将在菜单最底端看到 Java SCP 菜单项,如下:


使用说明

前提条件:

  • 保证网络可以访问到一台支持 SCP/ SSH1 的 Unix/Linux 服务器
  • 保证具有以上服务器上的一个帐户并且对其中的一个目录具有“写”权限(如果你只使用 ScpFrom 的话,此项可选)

ScpTo (文件上传)


(如果还没打开 JScp Window 的话)通过 Tools | Java SCP 打开,它会出现在左下角并停靠在 Navigator 所在的窗口中,如下图所示:

  1. LocalFile 上传的文件,通过右边的按钮来选择
  2. User@Host 用户名和主机名(或IP地址) 的组合
  3. Password 上述用户的密码
  4. RemoteDir 上传的文件在服务器上放置的目录


请注意在输入的过程中,会动态对输入域的值进行校验,如下,桔色的字显示没有指定服务器主机名(或IP地址):



如果所有的输入都合法的话,按钮 Scp 将可用,点击它将进行网络传输,进度条指示这一过程:



如果一切正常,最终进度条将停止指示。反之,如果后台操作出现错误的话,错误将显示:




ScpFrom (文件下载)


(如果还没打开 JScp Window 的话)通过 Tools | Java SCP 打开,它会出现在左下角并停靠在 Navigator 所在的窗口中,如下图所示:

  1. User@Host 用户名和主机名(或IP地址) 的组合
  2. Password 上述用户的密码
  3. RemoteFile 要下载的服务器上的文件
  4. LocalDir 下载的文件放置的目录,通过右边的按钮来选择




如果所有的输入都合法的话,按钮 Scp 将可用,点击它将进行网络传输。


总结


自从 NetBeans 5.0 开始,编写基于 NetBeans 的插件或平台应用已经变得非常简单。对于新来者,最大的障碍无非是一些NetBeans专用的术语及早期遗留下来的几个不大好理解的概念。不过还好, NetBeans 自己在快速前进的同时并没有忘记为开发者提供便利。
NetBeans wiki 是各种信息的大轮盘
planetnetbeans 则是全世界NetBeans开发者的乐园。大家为了 NetBeans 开怀畅谈。
Geertjan's Weblog 不得不看

平步星云 - December 10, 2008 05:08 PM
Hibernate Jpetstore 之五 部署

文档内容

  • 概览
  • 获得工程代码
  • 配置
    • 数据库初始化
      • MySQL 5.x
      • Oracle 9i, 10g, HsqlDB, Postgres 及其它
    • 应用服务器初始化
      • 本地数据源(非JNDI 数据源)
        • Tomcat 5.x,
        • Sun Application Server8.x, 9.x / GlassFish 1.x, 2.x
        • Jetty 6.1.3
      • JNDI 数据源
        • Sun Application Server8.x, 9.x / GlassFish 1.x, 2.x
        • JBoss 4.0.4 +
        • Jetty 6.1.3
  • 部署并运行
  • 总结
PS: 为什么这个系列的最后一篇这么长时间才出来?原因是,我跟大家说过,工程的所有源代码最终将发布。所以我要找一个合适的地方上载。
这个过程还是比较烦的,况且老外们的工作效率普通不如我们,每次交流至少等上一周左右才有回应。试想申请、审批,确认,上载等过程,其实令我这个急性子试 图想把自己家里的电脑搬到主机托管中心,申请一个域名了事!

还好,它终于出来了 (hjpetstore)!但愿没让你失望。

在阅读本篇文章之前,请先仔细阅读前面系列的相关内容。

概览

对于传统的J2EE 项目,当项目开发完后,其工作并未结束,紧接着的部署过程其实是很令开发者头痛的,特别是象重量级的应用服务 WebLogic, WebSphere 等。还好 NetBeans 现在抬简化了这一过程。
我们这个例子是基于无存在数据库的方案,所以在我们演示结果前,我们将要导入一些数据。但首先我们得创建相应的数据库用户和数据库方案(Schema).

获得工程代码

1.从 hjpetstore 得到工程源代码,具体的步骤网站上有说明,在 NetBeans 中就很简单了:

CVS | checkout:
cvs root: :pserver:username@cvs.dev.java.net:/cvs (这个 username 是你必须到 java.net 上注册的用户名称,目前 anonymous 好象不能工作了)
password:

下一页中, module: hjpetstore 下载完后,NetBeans 会问你是否打开该工程,选择是。

2. 你可能需要调整一下lib 的位置,这是 NetBeans 的一个缺陷,保存的路径不是相对路径。
右击工程 | properties
点 Libraries,在Compile 页中将所有 .jar 文件 选中后 'remove',
再加入下载下来的WEB-INF/lib 目录下的所有 jar 文件

3. 确保 Clean And Build project 成功


配置

数据库初始化

我这里只介绍 MySql 的 配置,其它的数据库配置列作 TBD. (待做,其实大部分脚本已经在工程中了,等待你的加入吧!因为我不是一个数据库专家,也没有太多时间去研究这个。)我成功配置过oracle 和 hsqldb.

注 意:因为下面的脚本会删除 'hjpetstore'数据库用户及其所有资源,请确保用户 'hjpetstore' (oracle) 或数据库 hjpetstore (mysql) 目前没被使用,如果使用了,请修改数据库脚本。所以最好的办法是使用你的个人数据库来作演示。

MySQL 5.x


1. 创建用户hjpetstore 和 数据库 hjpetstore

# 在命令行下以 root 身份运行创建脚本
# $hjpetstore 是用真实的工程路径代替
# 其它值根据你的设置作相应的改变,比如你如果连非本机的数据库,那 'localhost' 就是那个机器在 ip 了
> mysql -h localhost -u root -p < $hjpetstore\conf\jpetstore_mysql.sql Enter password: ******** 如果程序的输出显示了 hjpetstore, 则表明成功了: Database information_schema
hjpetstore

mysql
...

或者,如果有mysql query browser 的话,用它直接运行如下命令也可:
-- frist drop database hjpetstore and user hjpetstore
drop database if exists hjpetstore;

create database hjpetstore;

-- create user hjpetstore and give the password hjpetstore
grant all privileges on hjpetstore.* to hjpetstore identified by 'hjpetstore';

show databases;

2. 得用 hibernate.hbm2ddl.auto 自动生成数据库方案
确保 web/WEB-INF/dataAccessContext-hibernate.xml 中 设置了 update

这个属性的具体含义,我在前面的系列中已经讲过了,在产品初始化,你就可以安全地把它注释掉。

3. 在 NetBeans 右击工程 Run project
这一步将所有的数据库表创建出来, 只是没有数据。

4. 加裁数据
使用 NetBeans Sql Editor
4.1 注册mysql 驱动
Runtime | DataBases 右击 -> new Driver
Add ... -> 导航到工程WEB-INF/lib/下的 mysql-connector-java-3.1.12-bin 点 OK

4.2 创建连接
右击刚注册的驱动 MySql (Connector/J driver) -> Connect using ....
Database URL: jdbc:mysql://localhost:3306/hjpetstore?useUnicode=true&characterEncoding=UTF-8
user name: hjpetstore
password: hjpetstore

点 Ok 后,在Databases 下应该会出现一个新的连接。

4.3 执行 SQL 脚本
1. 在 Files 窗口中导航到 db/mhsql/jpetstore-mysql-dataload.sql 并双击打开它
2. 在编辑器的工具条中 Connection: 选择 刚创建的数据库连接:jdbc:mysql://localhost:3306/hjpetstore?useUnicode=true&amp;amp;amp;amp;amp;amp;amp;amp;ampamp;characterEncoding=UTF-8
3. 点击编辑器工具条上,紧挨着下拉框的 run sql

确保没有显示错误信息。


Oracle 9i, 10g, HsqlDB, Postgres 及其它


数据库脚本都已经在工程中了,你所要做的就是利用这些数据库提供的工具创建一个用户 'hjpetstore',
之后的步骤与上述相同。


应用服务器初始化

本地数据源(非JNDI 数据源)


Tomcat 5.x,
事实上,工程默认是使用 Tomcat 服务器的,所以现在你根本不需要改动什么就可以运行工程了。
有关数据源的配置是在 web/META-INF/ context.xml 文件中

Sun Application Server8.x, 9.x / GlassFish 1.x, 2.x
同样的配置,只不过要生成一个 sun-web.xml 文件,
很好,NetBeans 会帮你自动产生,如下:
右击工程 -> Run | Server: 选择注册的Sun App Server (如果你还没注册 Sun App Server 的话,你需要先注册一下,具体步骤见相关文档)

此时,文件已经产生,右击工程 -> Run Project

Jetty 6.1.3
所有的配置文件已经在 WEB-INF 下了: jetty-web.xml, jetty-env.xml, 所以要做的只剩下将dist 上下生成的 hibernateJpetstore.war
放到 Jetty 的部署目录,还好这个目录跟 Tomcat 的目录同名叫 webapps

在 Jetty 目录下运行:
java -jar start.jar
然后在浏览器中请求: http://localhost:8080/hjpetstore/

JNDI 数据源


使用JNDI数据源当然是为了使用其 JTA(包容器管理的事务及其数据库连接池的实现),
只需要按正确的名称 jdbc/hjpetstore 在管理界面配好数据库连接池和相应的数据源,运行起来还是挺方便的,

Sun Application Server8.x, 9.x / GlassFish 1.x, 2.x
1. 首先按照这篇文章介绍的步骤正确配置 mysql 数据源连接池
(中文) http://pprun.blogspot.com/2007/05/glassfishsun-app-server.html
(English) http://enpprun.blogspot.com/2007/05/problem-in-setting-mysql-xa-datasource.html
注意,我文章中介绍的是使用root/root 作为用户名/密码,此时可以设置成hjpetstore/hjpetstore

2. 配置数据源
在应用服务器的 管理 界面 导航Resources | JDBC | JDBC Resources
点击右边主页面中的 new 后进入配置页面,填入:
JNDI Name: jdbc/hjpetstore
Pool Name: 选择前面配置的数据源:连接池:mysql
完成后点击 Ok


JBoss 4.0.4 +

1. 使用 JBoss 也许是冲着所谓的 #1 应用服务器而来的吧,但其配置有一些变化:
第一它实现了自己的一套日志方式,所以需要把 web.xml 中的

org.springframework.web.util.Log4jConfigListener

注释掉。

2. 它的数据源的配法也不相同,只需要将相应的数据库的配置文件(如:mysql-ds.xml ,内容见随后)放到
jboss-4.0.4.GA\server\default\deploy 目录下,
再在 jboss-4.0.4.GA\server\default\conf\login-config.xml 中加入:
    <application-policy name = "MySqlDbRealm">
<authentication>
<login-module code
= "org.jboss.resource.security.ConfiguredIdentityLoginModule" flag =
"required">
<module-option name ="principal">hjpetstore</module-option>
<module-option name ="userName">hjpetstore</module-option>
<module-option name ="password">hjpetstore</module-option>

<module-option name
="managedConnectionFactoryName">jboss.jca:service=LocalTxCM,name=hjpetstore-mysql</module-option>
</login-module>
</authentication>
</application-policy>
mysql-ds.xml 相应的内容如下:
    <?xml version="1.0" encoding="UTF-8"?>
<datasources>
<local-tx-datasource>

<jndi-name>hjpetstore-mysql</jndi-name>
<connection-url>jdbc:mysql://localhost:3306/hjpetstore</connection-url>
<driver-class>com.mysql.jdbc.Driver</driver-class>
<user-name>hjpetstore</user-name>
<password>hjpetstore</password>
<exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter</exception-sorter-class-name>

<!-- should only be used on drivers after 3.22.1 with "ping" support

<valid-connection-checker-class-name>org.jboss.resource.adapter.jdbc.vendor.MySQLValidConnectionChecker</valid-connection-checker-class-name>
-->
<!-- sql to call when
connection is created

<new-connection-sql>some arbitrary sql</new-connection-sql>
-->
<!-- sql to call on an
existing pooled connection when it is obtained from pool -
MySQLValidConnectionChecker is preferred for newer drivers

<check-valid-connection-sql>some arbitrary
sql</check-valid-connection-sql>

-->

<!-- corresponding
type-mapping in the standardjbosscmp-jdbc.xml (optional for ejb) -->
<metadata>

<type-mapping>mySQL</type-mapping>
</metadata>
</local-tx-datasource>
</datasources>


3. 还有,就是JBoss 的 JNDI 的名称有些怪:
    <bean id="dataSource"
class="org.springframework.jndi.JndiObjectFactoryBean">

<!-- JBoss
-->

<property name="jndiName" value="java:/hjpetstore-mysql">

<!-- other standard Java EE server

<property name="jndiName"
value="java:comp/env/jdbc/hjpetstore">
-->
</bean>


看到区别了吗?它只需要java:/hjpetstore-mysql


Jetty 6.1.3
目前,还未测试成功!


部署并运行


在 NetBeans 中,只需要在 工程属性中选定所要运行的 服务器后,点 Run Project 即可运行在本地数据源配置上。
此外,还可以按照服务指定的自动部署目录,将生成的 dist/hibernateJpetstore.war 文件放到该目录,
如果服务器已经运行,一切就 ok 了,如果没有,启动服务器即可。

如果要运行在 JNDI 配置上,则需要改一下 web.xml :

<!--
- Location of the XML file that defines the root
application context.
- Applied by ContextLoaderServlet.
-->
<context-param>

<param-name>contextConfigLocation</param-name>

<!-- local datasource -->
<param-value>
/WEB-INF/dataAccessContext-hibernate.xml
/WEB-INF/applicationContext.xml
</param-value>
<!-- jndi datasource and JTA (for a transactional
JNDI DataSource)

<param-value>

/WEB-INF/dataAccessContext-hibernate-jndi.xml
/WEB-INF/applicationContext.xml
</param-value>
-->
</context-param>

这几行配置说的应该很明白了,上面的是默认情况下的本地数据源,如果使用JNDI数据源,是这样了:

<context-param>


<param-name>contextConfigLocation</param-name>



<!-- jndi datasource and JTA (for a
transactional JNDI DataSource) -->

<param-value>


/WEB-INF/dataAccessContext-hibernate-jndi.xml
/WEB-INF/applicationContext.xml

</param-value>



</context-param>


只要按照上述的步骤配置好了应用服务器的数据源,现在运行的效果应该跟本地数据源是一样的。


总结


NetBeans 对于 Java EE 的开发是全面的,除了几个服务器还未集成进来之外,其它的功能已经走在了所有IDE的最前列,
但这也不防碍开发者使用这些未集成的服务器,因为大部分服务器都支持热部署,当NetBeans 给你的工程生成了 WAR 文件后,
剩下的就是“将它放入热部署目录”了。


看运行在 Jetty6.1.3 上的效果 (注意脚注部分显示,当前运行在什么服务器上):

平步星云 - December 10, 2008 05:08 PM
NetBeans 6.0 beta1 终于出来了!

经过“漫长”的等待,beta1 终于出来了!其实我一直在用它的最近的 daily build, 因为 M10 实在是太不稳定了。

beta1 给人的第一印象是,更换了主题(桌面 icon, welcome 页,向导图案),使用的是接近于 Vista 类似的蓝绿色调,给人以清新的感觉!
加紧试用吧!



BlogJava-Java桌面技术-随笔分类-NetBeans - December 07, 2008 12:36 PM
《FilthyRichClients》读书笔记(二)-让Swing正确显示Gif

摘要: 显示GIF图片一直是swing的诟病,本文给出一个用swing处理gif类型图片的参考实现  阅读全文