显示标签为“SwingWorker”的博文。显示所有博文
显示标签为“SwingWorker”的博文。显示所有博文

2007年6月7日星期四

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 不得不看

2007年5月23日星期三

SwingWorker for you

想必大家已经知道 SwingWorker 已经加入到了 javax.swing 包中了。它的前身经过好几个阶段改进的,如果你阅读网上的例子就会发现你阅读的例子跟你下载的包不兼容。
SwingWorker 在被正式加入到JDK6中之前叫做:org.jdesktop.swingworker.SwingWorker.java, 除了这个类之外,还包括 org.jdesktop.swingworker.AccumulativeRunnable.java 和 org.jdesktop.swingworker.SwingPropertyChangeSupport.java. 我并不打算深入介绍这几个类的源码,而是利用实际的例子来描述 SwingWorker 给 GUI 程序带来的便利。

如果写过GUI程序的开发者肯定对界面的响应度及界面冻结会有所了解。首先我得说明的是,这个问题并不是SWING特有的,所有的GUI框架如果没有处理 好都会存在这种问题,举例来说,
1. 在网络不好的环境下,在 windows 的文件浏览器中请求一个FTP地址或任何服务器的地址时,我们可以看到“灰块”(所谓灰块,是由于界面元素刷新队列被某个长时间的任务给阻塞,造成本该立 即刷新的界面得不到处理,显示出来的效果就是一块被扯得扭曲了的区域。)
2. 用过 PLSQL Developer 的开发者肯定也体会过屏幕冻结的感受吧。


费话少说,进入我们的 SwingWorker 之旅。

SwingWorker有几个重要的概念:
  1. 初始线程(Initial threads) 一般来讲是应用的主线程 (运行main 方法的那个线程) 
  2. 工作者线程(Worker Thread) 在主线程中生成的,用于执行那些长时间操作的线程
  3. 事件调度线程(Event Dispath Thread) 所有界面相关的行为都应该在这个线程进行,并且一定要保持快速的响应。
如下图是利用 NetBeans Profile 监控到线程:
  • main - 初始线程
  • Our Swingworker #1 - 工作者线程
  • AWT-Envent-Queue-0 事件调度线程
  • 其它的线程不在我们感兴趣之列




我们要记住的是:
  • 所有长时间的操作都不应该放在事件调度线程(EDT-Event Dispatch Thread,专门用来处理与界面响应相关的操作)中,否则界面在这段时间内将变得无法响应。
  • 所有对SWING组件的更新(在其已经被显示出去后)都应当通过EDT来访问,否则有可能造成线程死锁或界面根本没有反映作出的更改。
不要被前面这几段弄晕了,只要弄清楚了 Swing 的线程体系,其实大部分工作JDK 已经为你做好了。我们所要做的就是按照范例实现我们的代码。这样就肯定安全可靠。


我们的例子是一个登录界面,这个界面要求输入用户名与密码,然后点击登录,正常情况下将与中心数据库进行通信。但考虑到文章的长度,我们将使用 Thread.sleep 来演示和长时间的网络操作。

最终的界面将如下所示,我们这里也不介绍怎样制作界面(是的,我的确是使用 NetBeans 的 Gui Builder 来制作的,但使用了一个自定义的 JImagePanel)




如果阅读过别人的代码或自己认真写过SWING方面的代码,在没有使用 SwingWorker 的 GUI 工程中,一定会有如下代码:

java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
// 长时间的操作
}
});


这样做的目的就是为了使长时间的操作在另外的线程中运行。

为了使用面象对象的方式来处理这种行为,我们现在使用 SwingWorker。下面是我们的例子的代码:


import org.jdesktop.swingworker.SwingWorker;
import org.pprun.interviewofprologic.db.domain.UserBusinessDelegate;

/**
* Worker thread for login operation.
* @author pprun
*/
public class LoginSwingWorker extends SwingWorker {
private String username;
private char[] password;

private Exception exception;

/** Creates a new instance of LoginSwingWorker */
public LoginSwingWorker(String aUserName, char[] aPassword) {
this.username = aUserName;
this.password = aPassword;
}

/**
* 所有的后台操作都在这里,如果要动态将处理的数据发出去的话(比如数据库查询的应用),
* 可以在这里调用 publish 方法.
*/
@Override
protected Boolean doInBackground() throws Exception {
try {
// 我们注释掉了这段代码,取而代之以假想的 Thread.sleep
// UserBusinessDelegate cbd = UserBusinessDelegate.getInstance();
// final boolean result = cbd.login(username, password);

//return result;

try {
Thread.sleep(5000);

} catch (InterruptedException interruptedException) {
interruptedException.printStackTrace();
}

return true; // 如果想看看登录失败时的效果,return false;

} catch( Exception anyException ) {
exception = anyException;
throw exception;
}
}


/**
* Returns the exception thrown by the method doInBackground, if any, or
* null if no exception was generated.
* @return The exception generated by the call to doInBackground, or null
* if no exception was generated.
*/
public Exception getDoInBackgroundException() {
return exception;
}
}



在触发端,点击 Ok 按钮时:


private void okJButtonActionPerformed(java.awt.event.ActionEvent evt) {
// ...

loginSwingworker = new LoginSwingWorker(username, password);

loginSwingworker.addPropertyChangeListener(this);
loginSwingworker.execute();

// ...
}



属性改变监听器的实现方法,监听在 SwingWorker 中的改变:


public void propertyChange(PropertyChangeEvent pce) {
if (pce.getSource() == loginSwingworker) {
// property change event coming from the loginSwingworker
if (pce.getPropertyName().equals("state") &&
loginSwingworker.getState() == SwingWorker.StateValue.DONE ) {
// loginSwingWorker 完成,但有可能抛出异常
loginSwingworker.removePropertyChangeListener( this );
if (loginSwingworker.getDoInBackgroundException() != null ) {
// 抛出异常
if (true) {
infoJLabel.setText("Error in login!");
} else {

infoJLabel.setText("Error in login!");
}
}

} else if (loginSwingworker.isCancelled()) {
// loginSwingowrker 被取消时的代码
}
}



关于在 Swing 中使用并发的详细资料,参见 Concurrency in Swing