posts - 17, comments - 70, trackbacks - 0, articles - 0

置顶随笔

    通常情况下,用户应该在简单字段(例如文本字段或组合框)中提供文本信息。虽然用来填充这些字段的应用程序代码通常比用来填充复杂窗口小部件(例如表或树)的代码简单得多,但这些“简单”字段通常会给用户带来更多负担。用户必须确定哪些字段需要内容、某个字段是否包含有效内容以及应该选择哪些选项。JFace 的字段辅助支持提供了一些类来帮助指导用户完成输入任务。
    org.eclipse.jface.fieldassist包提供了二种方式的辅助。带修饰字段支持允许您提供图像修饰,以便向用户提供有关特定字段状态的提示。内容建议支持允许您提供内容辅助弹出窗口,以便向用户提供内容选项。
    下面了解一下关于内容建议部分。
    一般的IDE工具都有内容建议的功能,比如很常见的Java编辑器,输入“.”之后就会激活一个窗口,从里面可以选择方法或字段,这个功能可以快速高效地完成代码,而且可以减少很多输入错误。jface的fieldassist提供了对这个功能的支持。
    我们做一个demo,先看一下这个demo的效果。
     
     当按下Alt + '/'的时候,弹出内容辅助的窗口。从中可以选择相应的建议,进行快速输入。
    下面是相应的代码,非常简单。
    

sShell = new Shell();
        sShell.setText(
"Shell");
        GridLayout gridLayout 
= new GridLayout(1false);
        sShell.setLayout(gridLayout);
        sShell.setSize(
new Point(300200));
        
        
final Text t = new Text(sShell, SWT.BORDER | SWT.MULTI);
        t.setLayoutData(
new GridData(GridData.FILL_BOTH));
        autoActivationCharacters 
= new char[] '#''(' };
        
try {
            keyStroke 
= KeyStroke.getInstance("Alt+/");
        }
 catch (ParseException e1) {
            e1.printStackTrace();
        }

        ContentProposalAdapter adapter 
= new ContentProposalAdapter(t,
                
new TextContentAdapter(), new SimpleContentProposalProvider(
                        
new String[] "建议1""建议2""建议3" }), keyStroke,
                autoActivationCharacters);

    按照帮助文档的描述如下:为了在用户从弹出窗口中选择建议时获取和设置控件内容,必须向适配器提供 IControlContentAdapter 实例,该实例可以检索和设置特定类型控件的内容。对于文本字段来说,可以使用 TextContentAdapter 类。但是,也可以灵活地实现 IControlContentAdapter,以便将内容建议适配器与任何其他类型的控件配合使用。
    关键是定义ContentProposalAdapter类,它有几个必须的参数,第一个是需要进行内容辅助的控件,第二个是IControlContentAdapter 实例,jface默认提供了二个实现:ComboContentAdapter, TextContentAdapter。按照上面的建议,如果你需要更加高级的功能,那么得实现IControlContentAdapter第四个是IContentProposalProvider的实例,这是提供内容辅助窗口中的内容提供器的接口,jface只实现了一个SimpleContentProposalProvider,这是最简单的只提供文本内容的内容提供器。同样的,你可以继承IContentProposalProvider来实现更高级的功能。
    keyStroke是定义按下什么键时激活该提示。autoActivationCharacters是定义当输入遇到什么符号时会激活提示的字符数组。
   按照帮助文档的说明,可以在任意控件上安装 ContentProposalAdapter 以提供此行为。需要什么样的高级功能,就必须具体实践一下以上几个接口的实现。一般的编辑器都会配合SoucreViewer来提供这些功能。

posted @ 2007-11-02 17:28 寒武纪 阅读(869) | 评论 (4)编辑 收藏

2008年8月29日

Java环境安装
    1.  从sun主页下载JDK for Linux版本。这里下载的是jdk-6u6-linux-i586.bin。
    2. 用root用户登录ubuntu,或是在普通用户下用su命令切换用户。切换到所需的安装目录。类型:cd <目录路径名>   例如,要在 /usr/java/ 目录中  安装软件,请键入:cd /usr/java/,把jdk-6u6-linux-i586.bin文件拷贝这个目录里面,设置权限为可执行类型:chmod a+x jre-6u6-linux-i586.bin
    3. 启动安装过程。键入:./jre-6u<version>-linux-i586.bin。接下来会提示二进制的许可协议,键入yes回车即可。安装过程如果遇到一些问题,都同样键入yes就可以。
    4. 一路下来,最后看到Done字样,就完成了Java环境的安装。安装的位置就是当前目录 /usr/java,当然你可以选择在别的位置。可以用ls命令查看一下是否正常。

环境变量配置
   上面安装完毕后,直接在shell里面输入java是不起作用的,需要先配置一下环境变量。一般都会用export命令,不过这样设置只对当前shell起作用,重启或是切换到别的shell会话就不起作用了。可以选择配置 .bashr文件。用vi或是gedit打开,在末尾添加下面的内容
   export JAVA_HOME=/usr/java/jdk1.6.0_06
   export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
   export PATH=$PATH:$JAVA_HOME/bin
   然后保存。再在shell里面试验一下是否安装成功。echo一下各个变量是否正常,然后输入java -version看看。
  还有一种方式是修改/etc/profile,这样相当于修改系统配置文件,对所有用户都有影响,我在尝试的过程可能输入了一些异常字符,导致整个ubuntu无法用GUI登录,无奈只能用文本登录,然后再次把profile改回来才正常。
   好了之后可以先试一下用vi新建一个Hello的Java文件,然后编译一下试运行。

Eclipse安装
   Linux下面的Eclipse在ubuntu的界面渲染下看起来非常漂亮。先从Eclipse社区下载一个Linux版本的,这里下载的是europa版本的gz包。只要解压到一个目录就可以,这里选择/opt/eclipse下面。Eclipse是解压就可以使用的。不过为了方便,我们在桌面做一个启动器把启动目标指向到Eclipse的安装目的地,选择里面已有的图标文件,这样就完成了。不过默认Eclipse是找不到Java执行路径的,有网友写了这样一个脚本eclipse.sh 放到/usr/local/bin目录下,记得加上775权限。然后把启动器位置指到这里eclispe.sh。下面是eclipse.sh的内容:
    #!/bin/bash
    #
    # 执行 eclipse 2.1.1
    #
    export JAVA_HOME=/usr/java/XXX
    export CLASSPATH=/usr/java/XXX/lib
    /opt/eclipse/eclipse -vm /usr/java/XXX/bin/java -data ~/workspace &
    # -vm 参数用以指定使用哪一个 jvm 来执行Eclipse,
    # -date参数用以指定Eclipse的数据目录。在此指定其存在用户根目录(~)下的workspace目录中
  
   还有一个比较笨的方法,Eclipse默认会去找它自己目录下是不是有jre存在,如果有,它就可以启动,那么你可以直接把先前安装好的JDK里面的JRE目录整个复制到Eclipse里面。然后就可以直接运行了。下面看一下效果
  

posted @ 2008-08-29 17:07 寒武纪 阅读(2510) | 评论 (3)编辑 收藏

2008年8月21日

    ActionSet是Eclipse RCP里面一非常重要的概念,因为菜单、工具栏、上下文菜单、状态栏很多操作都是共享的,所以Action就是用来处理重复出现的东西。至于Eclipse里面定义ActionSet有非常多的技巧,可能无法一一列举,而且使用方法也多种多样。下面介绍的是RssOwl2项目的ui源代码部分的一小块。
   1.  菜单的插入点 -- GroupMarker和Separator的使用
        ApplicationActionBarAdvisor类是定义全局所有Action插入点和入口,查看fillMenuBar(IMenuManager)方法,为了简化,以其中的辅助方法createFileMenu(IMenuManager)为例,讲述一下实现菜单“文件”的内容,先看一下菜单的结构

       像Close,Import...之类的非常简单,看一下它是如何实现New这个子菜单的。首先看一下它的源代码如何定义插入点
     
/* Menu: File */
  
private void createFileMenu(IMenuManager menuBar) {
    MenuManager fileMenu 
= new MenuManager("&File", IWorkbenchActionConstants.M_FILE);
    menuBar.add(fileMenu);

    fileMenu.add(
new GroupMarker(IWorkbenchActionConstants.FILE_START));
    fileMenu.add(
new GroupMarker(IWorkbenchActionConstants.NEW_EXT));
    fileMenu.add(
new Separator());

    fileMenu.add(getAction(ActionFactory.CLOSE.getId()));
    fileMenu.add(getAction(ActionFactory.CLOSE_ALL.getId()));
    fileMenu.add(
new GroupMarker(IWorkbenchActionConstants.CLOSE_EXT));
    fileMenu.add(
new Separator());
    fileMenu.add(getAction(ActionFactory.SAVE_AS.getId()));
    fileMenu.add(
new GroupMarker(IWorkbenchActionConstants.SAVE_EXT));
    fileMenu.add(
new Separator());
    fileMenu.add(getAction(ActionFactory.PRINT.getId()));

    fileMenu.add(
new Separator());
    fileMenu.add(
new GroupMarker(IWorkbenchActionConstants.MB_ADDITIONS));

    fileMenu.add(fReopenEditors); 
// TODO Consider moving into a "Go" Menu!

    fileMenu.add(
new Separator());
    fileMenu.add(
new GroupMarker(IWorkbenchActionConstants.FILE_END));
    fileMenu.add(
new Separator());

    fileMenu.add(getAction(ActionFactory.QUIT.getId()));
  }
       其中有一行fileMenu.add(new GroupMarker(IWorkbenchActionConstants.NEW_EXT)); 这里是定义一个GroupMarker作为组标记,把子菜单New容纳进来。这个NEW_EXT的值是:new.ext
      现在跳回到plugin.xml去看一下它的ActionSet定义,结构如下:
,点击New(menu),它的path值为:file/new.ext,这个路径就是在createFileMenu方法定义的路径,第一个是“File”本身的ID。也就是把子菜单New(menu)插入到指定的那个GroupMarker,ID为new.ext。然后定义了三个ID分别为bookmark,newsbin,searchmark,的groupmarker和一个folder的separator,这个三ID分别就对应上面actionSet定义的三个action,以其中的Bookmark(action)为例,它的menubarPath为:file/new_sub/bookmark,代表插入到"File"主菜单中定义的new_sub子菜单中,new_sub是New(menu)的ID。因为folder是定义为separator,所以它会有一条分隔线。这只是RssOwl的定义方法,其实以前自己做开发的时候是没有这样定义的,而且把子菜单New也写在方法fillMenuBar中的,菜单把ID都写在里面,ActionSet的配置就没有子菜单出现了,但是这样定义看起来就比较乱。采用这种写法感觉比较简洁。
    2.  Action的实现
       仍以bookmark为例,它的实现类是NewBookMarkAction,实现了IWorkbenchWindowActionDelegate, IObjectActionDelegate二个接口,第一个是ActionSet指定实现接口,第二个是对象操作菜单要求实现的接口(但事实发现没有再定义它的配置,可能是internal版本的原因),也就是说这个Action是多功能,它将会出现在主菜单,工具栏,和局部的右键菜单上。主菜单和工具栏的位置都在ActionSet配置定义了,看看它的右键菜单实现是在哪里的,这个右键是在视图Bookmarks定义的,那么跳转到org.rssowl.ui.internal.views.explorer.BookMarkExplorer类去看看。里面有一个hookContextualMenu()方法,就是定义它的右键菜单的,看一下代码实现:
private void hookContextualMenu() {
    MenuManager manager 
= new MenuManager();

    
/* New Menu */
    MenuManager newMenu 
= new MenuManager("New");
    manager.add(newMenu);

    
/* New BookMark */
    newMenu.add(
new Action("Bookmark"{
      @Override
      
public void run() {
        IStructuredSelection selection 
= (IStructuredSelection) fViewer.getSelection();
        IFolder parent 
= getParent(selection);
        IMark position 
= (IMark) ((selection.getFirstElement() instanceof IMark) ? selection.getFirstElement() : null);
        
new NewBookMarkAction(fViewSite.getShell(), parent, position).run(null);
      }


      @Override
      
public ImageDescriptor getImageDescriptor() {
        
return OwlUI.BOOKMARK;
      }

    }
);

   
//其它定义
}
     原来实现也很简单,只是往MenuManager里面添加一个Action而已,而且run方法就是直接调用定义好的NewBookMarkAction的run方法,但是把选中对象做为参数传进去,因为这个new是涉及当前上下文选择对象的。
    3. 下拉类型的工具按钮定义
    非常常见的Dropdown类型的工具栏按钮可以把功能类型的按钮归为一类,做成一个下拉菜单形式,有默认的按下功能,也有可以选择其它类似功能的下三角形式,样子如下:

    这个dropdown的Action是定义在ActionSet配置里的。style是pulldown类型的,所以实现类NewTypeDropdownAction实现了IWorkbenchWindowPulldownDelegate接口,它的run方法就是定义默认点击不做选择时的事情,这个下拉菜单是实现getMenu(Control parent)方法而来,它定义了如何生成这个菜单,这就用到了最原始的SWT中的MenuItem了,并且为它们添加SelectionListener,方法实现,不用说都知道了,又是New一个先前定义好的NewBookMarkAction类,然后又是调用它的run方法。所以总结一下,Action的重用不一定是这个类的重用,关键是它的run方法的重用,在不同的场景下它的外在表现形式可能会多种多样,但是它的run内容是一致的。像添加这种添加的run大部分时候都是弹出一个对话框,而对话框大都又是Winzard类型的,因为Winzard可以共享放到dialog里面。所以这种复用的思想在Eclipse里面随处可见。
   归结一下,其实这些技巧都是次要的,因为做GUI一个比较痛苦的事情就是经常要写很多重复类似的代码,抽取的不好,可能就变得不伦不类了。怎么利用它的这种思想,把复用的代码都抽取在一起,而阅读起来又比较轻松才是关键。
   知道的就这些,先介绍到这里,下次再谈谈其它新的发现。

posted @ 2008-08-21 11:29 寒武纪 阅读(1042) | 评论 (0)编辑 收藏

2008年7月31日

     摘要:     许久没有弄RCP了,刚好近来闲暇一点,找来个RSSOwl的源代码看看,有点收获。RssOwl非常出名,只是可能很多人不知道它是用Java做的。以前看过RssOwl第一版的源码,没有详细研究,down下来之后放上公司的共享CVS服务器,倒是几个同事饶有兴趣地研究起来。第一个版本写得较早,可能Eclipse的RCP框架都还没有出来,所以全部采用的SWT/JFace...  阅读全文

posted @ 2008-07-31 15:13 寒武纪 阅读(1135) | 评论 (5)编辑 收藏

2008年7月16日

    最近一个程序出了点问题,对于中文参数的GET请求,服务器无法解析出正确的参数。刚好服务器的那端是另一个项目组负责,是异构系统,当初测试的时候也是走流程化,涉及到很多工作上的协调就比较麻烦,测试也不充分,像赶鸭子上架一样就上线了,催说是项目紧急。当然这是话外,不多废话。
    httpClient的GetMethod类加入参数的方法是如下:
void setQueryString(NameValuePair[] params)
          Sets the query string of this HTTP method.
 void setQueryString(String queryString)
   跟踪一下httpClient的GetMethod的源代码,继承自HttpMethodBase,源码如下:
  
public void setQueryString(String queryString) {
   
this.queryString = queryString;
}

    
public void setQueryString(NameValuePair[] params) {
   LOG.trace(
"enter HttpMethodBase.setQueryString(NameValuePair[])");
   queryString 
= EncodingUtil.formUrlEncode(params, "UTF-8");
}
   EncodingUtil是httpClient定义的一个编码工具类,由于默认设置的是UTF-8,所以对于一些系统可能就无法识别。可以在外部这样更改:
method.setQueryString(EncodingUtil.formUrlEncode(pair, "GB2312"));另外,注意请求头也要修改为对应的一致编码方式,method.addRequestHeader("Content-type" , "text/html; charset=GB2312");如果这二个编码不一致,就会引起乱码。
   刚开始的时候尝试过都使用一致的UTF-8,但是发现还是乱码,这应该是服务器的原因。IE默认的就是采用操作系统Windows的中文编码去进行Encoder的,服务器原先基本上都为IE服务的,所以改为GB2312就能正常识别得到。
   另外,上面提到的EncodingUtil这个工具是从apache的另一个组件codec包装而来的,而非SUN的URLEncoder。有兴趣的可以研读一下源代码。

posted @ 2008-07-16 10:31 寒武纪 阅读(1229) | 评论 (2)编辑 收藏

2008年6月11日

Jakarta的httpclient3.1是最新版本,项目中需要用程序模拟浏览器的GET和POST动作。在使用过程中遇到不少问题。
1. 带附件的POST提交
    最开始都是使用MultipartPostMethod这个类,现在已经废弃这个类了。API说明:Deprecated. Use MultipartRequestEntity in conjunction with PostMethod instead.   使用PostMethod可以实现的功能,就没有必要再弄一个MultipartPostMethod了。下面是一段最简单的示例:

PostMethod post = new PostMethod();
        NameValuePair[] pairs 
= new NameValuePair[2];
        pairs[
0= new NameValuePair("para1""value1");
        pairs[
0= new NameValuePair("para2""value2");
        post.setRequestBody(pairs);
        HttpClient client 
= new HttpClient();
        
try {
            client.executeMethod(post);
        }
 catch (HttpException e) {
            e.printStackTrace();
        }
 catch (IOException e) {
            e.printStackTrace();
        }
   这是针对一般的form形式的提交,而且这个form里面不带附件的。如果带附件,那么这种方法就不起作用,附件上传的参数和普通参数无法一同在服务器获取到。org.apache.commons.httpclient.methods.multipart 这个包就是为处理文件上传这种多形式参数的情况的。最主要的类是Part(代表一种post object),它有二个比较重要的子类:FilePart和StringPart,一个是文件的参数,另一个就是普通的文本参数。它的典型使用方法如下:
String url = "http://localhost:8080/HttpTest/Test";
         PostMethod postMethod 
= new PostMethod(url);
         
         StringPart sp 
= new StringPart("TEXT""testValue");
         FilePart fp 
= new FilePart("file""test.txt"new File("./temp/test.txt"));
         
         MultipartRequestEntity mrp
= new MultipartRequestEntity(new Part[]{sp, fp}, postMethod
                 .getParams());
         postMethod.setRequestEntity(mrp);
         
         
//执行postMethod
         HttpClient httpClient = new HttpClient();
         
try {
            httpClient.executeMethod(postMethod);
        }
 catch (HttpException e) {
            e.printStackTrace();
        }
 catch (IOException e) {
            e.printStackTrace();
        }
    在第二行PostMethod postMethod = new PostMethod();后面,有人说需要使用postMehtod.setRequestHeader("Content-type", "multipart/form-data"); Content-type的请求类型进行更改。但是我在使用过程没有加上这一句,查了一下httpCleint的默认Content-type是application/octet-stream。应该是没有影响的。对于MIME类型的请求,httpclient建议全用MulitPartRequestEntity进行包装,就是上面的用法。

2.  参数中文的处理问题
    httpclient的默认编码都是ISO-8859-1,那肯定就无法支持中文参数了。引用一下这篇文章:http://thinkbase.net/w/main/Wiki?HttpClient+POST+%E7%9A%84+UTF-8+%E7%BC%96%E7%A0%81%E9%97%AE%E9%A2%98 ,按照作者的说法,就可以正常解决中文编码的问题。其中最关键的是修改EncodingUtil这个类的一个方法实现。另外,FilePart和StringPart的构造方法都有一个带编码指定的参数,为了减少问题的出现,建议所有的都带上统一的编码,包括postMethod.getParams()。示例如下:
String url = "http://localhost:8080/HttpTest/Test";
         PostMethod postMethod 
= new PostMethod(url);
         
         StringPart sp 
= new StringPart("TEXT""testValue""GB2312");
         FilePart fp 
= new FilePart("file""test.txt"new File("./temp/test.txt"), null"GB2312");
         
         postMethod.getParams().setContentCharset(
"GB2312");
         MultipartRequestEntity mrp
= new MultipartRequestEntity(new Part[]{sp, fp}, postMethod
                 .getParams());
         postMethod.setRequestEntity(mrp);
         
         
//执行postMethod
         HttpClient httpClient = new HttpClient();
         
try {
            httpClient.executeMethod(postMethod);
        }
 catch (HttpException e) {
            e.printStackTrace();
        }
 catch (IOException e) {
            e.printStackTrace();
        }

posted @ 2008-06-11 15:18 寒武纪 阅读(2587) | 评论 (3)编辑 收藏

2008年6月3日

   1. 先下载VMWare Tools for linux,下面是一个下载链接 http://vmware.cn/Soft/UploadSoft9f4/VMware%20Workstation%206.02%C2%CC%C9%AB%BE%AB%BC%F2%D3%A2%CE%C4%B0%E6/vmware_tools_linux.rar
   2. 解压出一个linux.iso文件,这个就是tools工具的安装光盘镜像。事先你必须正确安装了Linux,我安装的ubuntu8.04,在VM上点击“编辑虚拟机设置”,CD-ROM方式改为“使用ISO镜像”,选择linux.iso,确定
   3. 启动你的虚拟机操作系统,然后切换出来鼠标,选择主菜单“虚拟机”--> “安装VMware Tools”,ubuntu会自动搜索到该CDROM,直接打个桌面的图标即可。可以看到二个文件:VMwareTools-xxx-i386.rpm和VMwareTools- xxx.tar.gz。rpm是RedHat的安装包,这里我们应该使用gz文件,把这个gz文件直接复制到桌面,解压,生成一个-tools-distrib 目录。
   4. 打开终端,跳桌面这个-tools-distrib 目录。输入下面的命令:$ sudo ./-install.pl(回车后会提示输入你的密码,并且密码不会显示出来,表明你将以更高级权限执行一个动作——安装软件;再次回车后安装开始)
   5. 安装过程会有一系列的问题确认,类似windows的安装向导提示,一路回车下去,采用默认方式就可以。
   6. 最后安装成功会提示选择桌面环境的默认分辨率。分辨率可以以后再调整。
   7. 安装后鼠标的滑轮可能不好使了。我们这样解决这个问题,还是打开终端,输入:$ sudo gedit /etc/X11/xorg.conf   这个命令使系统以root权限打开鼠标配置文件/etc/X11/xorg.conf。把文件中的 Option “Protocol” “ps/2”改成 Option “Protocol” “IMPS/2” 。保存,然后重新启动ubuntu。

   补充:关于VMware安装ubuntu8.04和VMware tools以后,真实系统和虚拟系统的文件共享仍存在问题的,无法直接从外部的windows拖文件放入虚拟系统里面。挂载U盘或是分区也比较麻烦。后来想可以利用光驱,自己把要共享的文件制作成Windows下面的ISO文件,然后装截入光驱,直接在虚拟光驱里面打开,就可以直接操作。

posted @ 2008-06-03 17:27 寒武纪 阅读(5359) | 评论 (3)编辑 收藏

2008年5月30日

   Eclipse3.3出来很久了,一直都使用英文版,刚好看到有网友介绍Eclipse的一个Project,叫Babel,官方的描述这样:Eclipse is a global community. It is in everyone's interest to ensure that Eclipse is available and translated in as many locales as possible. 项目的主页地址是:http://www.eclipse.org/babel/ 。按照说明从这里可以下载安装到语言包。
   直接从Eclipse3.3的菜单"Help --> Software Updates --> Find and Insatll...",新建一个远程站点,URL为 http://download.eclipse.org/technology/babel/update-site/ ,然后直接在线安装。在弹出的语言选择界面上选择中文简体。如下图:
   

    网络情况如果正常的话,安装应该不会有问题的,中间可能会弹出几次下载jar文件失败的对话框,继续retry就行。
   
    最后重启一下,可以看到都变成中文界面的。
   
 
  原来是3.3的Eclipse,怎么变成3.2呢?原因估计是语言插件的版本是3.2的导致的,所以你看到有一些地方汉化并不完全,像Error Log视图的标题,项目右键菜单,以及一些顶级菜单都没有完全汉化。
  希望以后Babel项目后面更新跟得上主版本的变化,不过习惯了英文版的,其实也是差不多的。

posted @ 2008-05-30 15:44 寒武纪 阅读(10199) | 评论 (13)编辑 收藏

2008年5月23日

   普通的程序交互方式有命令行和GUI形式。对于GUI样式,交互的设计可以多种多样,但是Java做命令行交互,似乎存在着一些不足。
   命令行交互是传统的交互方式,如果程序有时候需要在Unix或是Linux等系统上运动时,以这种方式出现的可能性就比较大。命令行包括
   输入和回显问题,一般是以行结束,或是以某个结束符为终命令终止标识。System.out 和System.err就用于标准的输出和错误输出,System.in用于标准的输入接受,一般情况下都是指键盘。
   如果接受参数输入,一般的程序结构如下:
  
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
System.out.println(
"==Please input password==:");
String pass 
= in.readLine();
   输入的时候显示屏自动进行输入回显。这个时候如果遇到输入密码等敏感数据的时候,就无法用*或是#这样的符号进行回显屏蔽,容易暴露安全问题。
   解决的可能想法:
   1.  通过监听键盘事件,对输入的回显进行截获取,把回显进行屏蔽,但是监听器如何知道何时是输入密码,何时是输入普通数据?比较难以控制,而且这种监听应该是线程化的,可能会存在一些意想不到的问题。
   2.  如果用纯Java难以实现,那么是否可以使用其它语言的功能进行补充,比如JNI,或是Windows下面的Dos脚本,Linux平台的Shell脚本来进行补充。不过这样就不太平台无关了。
   3.  可能sun发现了这个不足之处,从1.6版本开始,增加了一个java.io.Console类,代表与当前 Java 虚拟机关联的基于字符的控制台设备,这个Console是对原来System.in这种不足的补充,提供了像readPassword()等这样的实用方法,具体请参考API文档,就是专门用于对敏感信息的读取。但是这是基于Java1.6的,如果有些场景受限制,不能使用1.6,那么还是无法解决上面的问题。后来查阅了一下Console类的实现方式,想直接把它的实现方式移植到1.4.xx上是比较难的,因为用到了一些高版本的新特性。使用Console要注意的是:虚拟机是否具有控制台取决于底层平台,还取决于调用虚拟机的方式。如果虚拟机从一个交互式命令行开始启动,且没有重定向标准输入和输出流,那么其控制台将存在,并且通常连接到键盘并从虚拟机启动的地方显示。如果虚拟机是自动启动的(例如,由后台作业调度程序启动),那么它通常没有控制台。如果你在Eclipse里面启动程序调用Console,那么通常是没有控制台,还是得从外部的命令行方式才能调用得到。
   总体想一下,感觉应该还是从第2点出发,牺牲掉一点通用性,这样才能满足这种功能需求。

posted @ 2008-05-23 09:41 寒武纪 阅读(1594) | 评论 (1)编辑 收藏

2008年5月8日

    刚好最近项目中需要用到一点加密的东西,java安全类库提供了一个java.security.MessageDigest类,此 MessageDigest 类为应用程序提供信息摘要算法的功能,如 MD5 或 SHA 算法。信息摘要是安全的单向哈希函数,它接收任意大小的数据,并输出固定长度的哈希值。有现成的当然是最好的,省事省力。
    MD5的非常有实际应用性。有网友给出这样的描述,可以参照一下:http://blog.csdn.net/Daping_Zhang/archive/2005/05/28/382688.aspx
     该类的getInstance(String algorithm) 方法返回一个MessageDigest的实体,加密的一系统的digest()方法和update(byte input)方法。加密后返回一个byte[],16位,我们经常见到很多开源网站的下载地址会有一个[md5]的链接,打开其实就是一小段文本内容。例如:
    MD5 (commons-logging-1.1.1-bin.zip) = f88520ed791673aed6cc4591bc058b55
    这是Jakarta的logging组件下载时提供的MD5摘要信息,是对这个zip包进行全文加密生成的摘要,摘要码就是后面的f88520ed791673aed6cc4591bc058b55,如果你下载以后,按照MD5的算法生成自己的摘要,如果这二个摘要一样,就证明这个文件是没有被人篡改过的。
    遇到的问题是Java的MessageDigest类执行后返回的byte[16]得转换成十六进制的字符串,如果直接用new String(byte[]),得到的结果将是不正确的。算法有很多网友提供了,照搬了。比较有趣的是,commons-logging提供的那个MD5居然和我自己生成的不一样(难道文件被修改过?),后来尝试了其它地方提供的MD5码,都没有问题。
    有很多相关的现成代码,搜集了一下整理如下(经过验证):

public class MD5Builder {

    
static Logger logger = Logger.getLogger(MD5Builder.class);
     
// 用来将字节转换成 16 进制表示的字符
    static char hexDigits[] = '0''1''2''3''4''5''6''7''8',
            
'9''a''b''c''d''e''f' }

    
    
/**
     * 对文件全文生成MD5摘要
     * 
@param file   要加密的文件
     * 
@return MD5摘要码
     
*/

    
public static String getMD5(File file) {
        FileInputStream fis 
= null;
        
try {
            MessageDigest md 
= MessageDigest.getInstance("MD5");

            logger.info(
"MD5摘要长度:" + md.getDigestLength());
            fis 
= new FileInputStream(file);
            
byte[] buffer = new byte[2048];
            
int length = -1;
            logger.info(
"开始生成摘要");
            
long s = System.currentTimeMillis();
            
while ((length = fis.read(buffer)) != -1{
                md.update(buffer, 
0, length);
            }

            logger.info(
"摘要生成成功,总用时: "
                    
+ (System.currentTimeMillis() - s) + "ms");
            
byte[] b = md.digest();
            
return byteToHexString(b);
            
// 16位加密
            
// return buf.toString().substring(8, 24);
        }
 catch (Exception ex) {
            logger.error(ex);
            ex.printStackTrace();
            
return null;
        }
finally {
            
try {
                fis.close();
            }
 catch (IOException ex) {
                ex.printStackTrace();
            }

        }

    }


    
/**
     * 对一段String生成MD5加密信息
     * 
@param message 要加密的String
     * 
@return 生成的MD5信息
     
*/

    
public static String getMD5(String message){
        
try {
            MessageDigest md 
= MessageDigest.getInstance("MD5");
            logger.info(
"MD5摘要长度:" + md.getDigestLength());
            
byte[] b = md.digest(message.getBytes());
            
return byteToHexString(b);
        }
 catch (NoSuchAlgorithmException e) {
            logger.error(e);
            e.printStackTrace();
            
return null;
        }

    }

    
    
/**
     * 把byte[]数组转换成十六进制字符串表示形式
     * 
@param tmp    要转换的byte[]
     * 
@return 十六进制字符串表示形式
     
*/

    
private static String byteToHexString(byte[] tmp) {
        String s;
        
// 用字节表示就是 16 个字节
        char str[] = new char[16 * 2]; // 每个字节用 16 进制表示的话,使用两个字符,
        
// 所以表示成 16 进制需要 32 个字符
        int k = 0// 表示转换结果中对应的字符位置
        for (int i = 0; i < 16; i++// 从第一个字节开始,对 MD5 的每一个字节
            
// 转换成 16 进制字符的转换
            byte byte0 = tmp[i]; // 取第 i 个字节
            str[k++= hexDigits[byte0 >>> 4 & 0xf]; // 取字节中高 4 位的数字转换, 
            
// >>> 为逻辑右移,将符号位一起右移
            str[k++= hexDigits[byte0 & 0xf]; // 取字节中低 4 位的数字转换
        }

        s 
= new String(str); // 换后的结果转换为字符串
        return s;
    }

}

  

posted @ 2008-05-08 15:02 寒武纪 阅读(2036) | 评论 (2)编辑 收藏

2008年5月6日

    一般而言,我们平常接触的大多数项目都应该是单纯使用B/S或是C/S,除非在特殊场合,否则比较少混合使用B/S,C/S架构。首先说一下对这二种架构特点的一些个人理解。B/S应该是目前很多项目都应用的架构,浏览器的方式使得用户的使用十分方便,用户可以何时何地通过Internet访问URL而进行相应的工作,升级维护也能比较集中,缺点就是浏览器的表现能力受限以及常常受非议的安全性问题,如果软件的应用范围区域不集中,而且用户经常变换地点进行访问,那么这种架构是非常适合的。C/S架构的C端有非常强的处理能力,所以在交互表现和安全方面可以做得比浏览器强,但是缺点也是非常明显的,安装部署、升级维护、版本兼容都是比较头大的事情,一般的适用场景是集中的办公室场所,用户使用范围相对稳定,以及一些对业务处理非常复杂的场合,为了降低服务器的负荷,同样需要C模式的支持。
    以前接触过的电信领域,就有过混合架构的软件。但是都是非常宠大,一直都对其实现方案比较感兴趣,但是都没有机会进一步了解。最近搜索了一下相关的资料,总结一下混合应用的一些想法(只针对Java方向)。
    ①混合架构的问题集中点。服务端共享,客户端采用不同的表现方式,共享的应该是业务层接口,持久层应该是屏蔽的。应用层的消息传递就是整个应用的关键所在,虽然像Jakarta提供的httpClient这种模仿浏览器的组件,但是毕竟是模仿,在很多方面的功能还是缺失的。
    ②最传统的方式是采用EJB做为服务,这个宠然大物容易让人害怕,不过在分布式的系统中它还是有应用优势的,像电信和金融这种行业应用还是比较广的,而且现成的中间件和应用服务器商都比较多,像Oracel、BEA、IBM、Sun都有成熟的应用产品,当然开发的成本和人力投入也是恐龙级数据的。
    ③有网友说在C端直接访问数据库,B/S结构不变,也就是通过数据库进行共享。这种方式是不可取的,二个缺点:把服务器的业务逻辑搬到了C端上,严格上讲是不安全的,升级维护也非常麻烦;并发控制的压力都在数据库上。
    ④采用RMI,这个老古董相信应该很多人都不使用了,因为它的使用要一连串的手续,比如服务接口定义必须实现Remote接口,服务Server在实现时必须继承UnicastRemoteobject类,必须使用rmic指令产生stub和skeleton等,设置上繁杂。
    ⑤Spring 远程服务。这个应该说是比较可取的,大家都比较喜欢轻量级的东西。就如第一点所说的,通过远程服务,我们可以在客户直接调用服务端的服务接口,就像本地调用一样,Spring对远程服务提供了好几种实现方案。
    ⑥WebService。适合异构环境,但是WSDL的这种方式相对来说会比较耗费资料,因为标准定义除了业务内容外,还有许多另外的说明内容。
    Spring远程服务实现方案介绍:
    ⑴Spring + RMI。Spring把传统的RMI方式的繁杂设置去掉,只要配置Bean文件就和定义服务接口可以。RMI的服务启动和管理都交给Spring来处理。RMI访问的缺点就是对防火墙的穿透力比较差。
    ⑵Spring + Caucho的Hessian、Burlap。Hessian使用Http将对象以中性的二进制消息进行传送,而不像RMI使用Java的序列化格式(这种序列化是专制的,不是Sun提供的序列化机制),由于是二进制消息,所以不受限于某种实现语言,传输时所需要的带宽较小是其优点。Burlap是以XML文件格式传送对象,XML文件有较高可读性,应用程序只要能解释XML就能接收消息,当然也不限于某种语言,但是组装XML和解释XML都需要消耗资源,当传输大数据时性能应该存在问题。
    ⑶Spring + Http Invoker。由于Hessian的序列化机制不是正统的Java序列化机制,所以当遇到传输复杂的业务模型时,就会存在各种问题,为此,Spring又提供了Http Invoker,同样是使用Http传送对象,而且是使用Java的序列化机制。相比RMI,Http对防火墙的穿透力要强。
    后来尝试了最后的这种Http Invoker方式,是在Spring2.0版本下尝试的,开发非常简单,网上也有大量的资料介绍。应该说从这里入口可以做一些尝试。目前遇到的一个项目就需要混合架构,B/S采用Spring2 + Struts2 + Hiberntae3,浏览器只提供一些查询功能和数据展现,C端采用Eclipse的RCP平台,共享服务器的业务接口,调用就采用Http Invoker远程服务,复杂的业务功能都集中在C端上。

posted @ 2008-05-06 12:43 寒武纪 阅读(3150) | 评论 (21)编辑 收藏