本系列的第一部分向Java程式員介紹了JavaFX腳本程式設計語言的文法及語義。本文在您對JavaFX腳本認知的基礎上進行描述,調用Java平台的遠端方法調用(Remote Method Invocation,Java RMI)類庫,使JavaFX圖形使用者界面(GUI)可以遠端通信。在JavaFX腳本中使用RMI是非常直接的,使用簡單快捷的方式就可以完成示範和測試用戶端-伺服器功能。
在NetBeans中建立一個JavaFX腳本工程(Creating a JavaFX Script Project in the NetBeans IDE)
1、從NetBeans的主菜單中選擇File菜單,接着選擇Net Project。
2、在New Project向導對話框中,在Categories面闆中選擇General,在Project面闆中選擇Java Application。點選Next。
3、在Project Name域中輸入JavaFXClient。
4、在Project Location中,點選Browse,然後指定存儲工程的位置。
5、取消對Set as Main Project和Create Main Class複選框的選擇,然後點選Finish。
現在,使用IDE建立一個名為HelloServer的JavaFX Script檔案。
1、在Projects視窗中,右鍵點選JavaFXClient > Source Packages節點,選擇New > File/Folder。
2、在Categories面闆中,選擇Other,接着在File Types面闆中,選擇JavaFX File。然後點選Next。現在,當你在Projects視窗中調用主工程節點的New或者Source Packages節點時,JavaFX檔案類型應該出現在可用的檔案類型清單中。
3、在New JavaFX File向導中的Name and Location頁中,File Name中輸入MyClient,Folder域中輸入src/client。注意,現在建立的檔案名會出現在Created File域中。
4、點選Finish完成工程的建立。
5、在Projects視窗中,右鍵點選JavaFXClient節點,選擇Properties。
6、在Properties對話框的Categories面闆中,選擇Run。
7、在Arguments域中輸入腳本的名稱:client.MyClient,點選OK。
此時将會在JavaFXClient工程的client目錄中建立一個名為MyClient.fx的空JavaFX Script檔案。此時,讓這個檔案為空,接着設定項目的另外一部分——完成RMI伺服器的設定後将重新傳回這裡。
使用遠端方法調用(RMI)進行用戶端--伺服器通信(Client-Server Communication with Remote Method Invocation(RMI))
Java遠端方法調用(Java RMI)是用來實作遠端等價對象調用的Java應用程式接口(API)。使用RMI開發分布式程式比用socket要簡單,因為開發者不需要定義協定,而定義協定往往是消耗時間又容易出錯的工作。
使用RMI,開發者有從本地類檔案調用本地方法的假象,其實,參數被發送到遠端目标并被解釋,然後結果又被發送回調用者。因為JavaFX Script可以調用Java對象,所有RMI就是一個讓JavaFX用戶端進行遠端通信的既快捷又簡單的工具。
使用RMI開發一個分布式JavaFX Script程式包含一下步驟:
在NetBeans中,按如下步驟操作:
1、從File菜單中選擇New Project。應該可以看到類似圖1的一個對話框。
圖1 在NetBeans5.5.1 中建立一個工程
2、在Categories清單中,選擇General。在Projects清單中,選擇Java Application。點選Next。應該可以看到一個類似圖2的對話框。
圖2 在NetBeans IDE中自定義新工程
3、在Project Name中輸入JavaRMIServer。選擇适當的檔案夾儲存項目,然後取消對Set as Main Project的選擇。最後,點選Finish按鈕。這就建立了一個名為JavaRMIServer的Java工程。
現在,在server包中建立一個名為ServerInterface的Java接口,如代碼示例1所示。接口ServerInterface包含了一個方法,ping(),它擷取一個String參數,傳回一個hello加上該參數組成的字元串。
代碼示例1
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLl52bO9CXzJ3b0F2YpRmbJdmbp5WasRXdP9CXn5Wa0h2ZpxGanlGa4FGdul3cvwFdl5mLuR2cj5ycldWYtl2Lc9CX6MHc0RHaiojIsJye.gif)
package server;
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLl52bO9CXzJ3b0F2YpRmbJdmbp5WasRXdP9CXn5Wa0h2ZpxGanlGa4FGdul3cvwFdl5mLuR2cj5ycldWYtl2Lc9CX6MHc0RHaiojIsJye.gif)
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLl52bO9CXzJ3b0F2YpRmbJdmbp5WasRXdP9CXn5Wa0h2ZpxGanlGa4FGdul3cvwFdl5mLuR2cj5ycldWYtl2Lc9CX6MHc0RHaiojIsJye.gif)
import java.rmi.Remote;
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLl52bO9CXzJ3b0F2YpRmbJdmbp5WasRXdP9CXn5Wa0h2ZpxGanlGa4FGdul3cvwFdl5mLuR2cj5ycldWYtl2Lc9CX6MHc0RHaiojIsJye.gif)
import java.rmi.RemoteException;
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLl52bO9CXzJ3b0F2YpRmbJdmbp5WasRXdP9CXn5Wa0h2ZpxGanlGa4FGdul3cvwFdl5mLuR2cj5ycldWYtl2Lc9CX6MHc0RHaiojIsJye.gif)
public interface ServerInterface extends Remote ...{
public String ping(String fileName) throws
RemoteException;
}
和任何一個RMI程式一樣,ServerInterface必須符合下述要求:
1、必須繼承Remote接口。
2、為了用戶端可以加載一個實作了Remote接口的遠端對象,必須聲明為public。
3、接口中的每一個方法(本例中隻包含了一個方法)必須抛出java.rmi.RemoteException。
接下來的步驟是建立一個實作了ServerInterface的類。一個示例實作如代碼示例2所示。注意,除了實作ServerInterface外,ServerImpl類還要繼承UnicasRemoteObject。這說明ServerImpl類是用來建立一個單一的、不可複制的遠端對象,它使用RMI預設的TCP傳輸進行通信。這可能是無論任何時候使用RMI時需要做的所有事情。
示例代碼2
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLl52bO9CXzJ3b0F2YpRmbJdmbp5WasRXdP9CXn5Wa0h2ZpxGanlGa4FGdul3cvwFdl5mLuR2cj5ycldWYtl2Lc9CX6MHc0RHaiojIsJye.gif)
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLl52bO9CXzJ3b0F2YpRmbJdmbp5WasRXdP9CXn5Wa0h2ZpxGanlGa4FGdul3cvwFdl5mLuR2cj5ycldWYtl2Lc9CX6MHc0RHaiojIsJye.gif)
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLl52bO9CXzJ3b0F2YpRmbJdmbp5WasRXdP9CXn5Wa0h2ZpxGanlGa4FGdul3cvwFdl5mLuR2cj5ycldWYtl2Lc9CX6MHc0RHaiojIsJye.gif)
import java.io.*;
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLl52bO9CXzJ3b0F2YpRmbJdmbp5WasRXdP9CXn5Wa0h2ZpxGanlGa4FGdul3cvwFdl5mLuR2cj5ycldWYtl2Lc9CX6MHc0RHaiojIsJye.gif)
import java.rmi.*;
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLl52bO9CXzJ3b0F2YpRmbJdmbp5WasRXdP9CXn5Wa0h2ZpxGanlGa4FGdul3cvwFdl5mLuR2cj5ycldWYtl2Lc9CX6MHc0RHaiojIsJye.gif)
import java.rmi.server.UnicastRemoteObject;
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLl52bO9CXzJ3b0F2YpRmbJdmbp5WasRXdP9CXn5Wa0h2ZpxGanlGa4FGdul3cvwFdl5mLuR2cj5ycldWYtl2Lc9CX6MHc0RHaiojIsJye.gif)
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLl52bO9CXzJ3b0F2YpRmbJdmbp5WasRXdP9CXn5Wa0h2ZpxGanlGa4FGdul3cvwFdl5mLuR2cj5ycldWYtl2Lc9CX6MHc0RHaiojIsJye.gif)
public class ServerImpl extends UnicastRemoteObject
implements ServerInterface ...{
private String name;
public ServerImpl() throws RemoteException...{
super();
}
public String ping(String s)...{
return "Hello " + s;
第三步是開發伺服器。這個類将成為伺服器程式的入口,因為它包含了main()方法。伺服器必須完成以下三個任務:
1、建立RMI注冊處(RMI registry)。
2、建立一個遠端的執行個體——本例中,是ServerImpl。
3、注冊使用RMI注冊處建立的對象。
一個示例實作如代碼示例3所示。
代碼示例3
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLl52bO9CXzJ3b0F2YpRmbJdmbp5WasRXdP9CXn5Wa0h2ZpxGanlGa4FGdul3cvwFdl5mLuR2cj5ycldWYtl2Lc9CX6MHc0RHaiojIsJye.gif)
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLl52bO9CXzJ3b0F2YpRmbJdmbp5WasRXdP9CXn5Wa0h2ZpxGanlGa4FGdul3cvwFdl5mLuR2cj5ycldWYtl2Lc9CX6MHc0RHaiojIsJye.gif)
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLl52bO9CXzJ3b0F2YpRmbJdmbp5WasRXdP9CXn5Wa0h2ZpxGanlGa4FGdul3cvwFdl5mLuR2cj5ycldWYtl2Lc9CX6MHc0RHaiojIsJye.gif)
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLl52bO9CXzJ3b0F2YpRmbJdmbp5WasRXdP9CXn5Wa0h2ZpxGanlGa4FGdul3cvwFdl5mLuR2cj5ycldWYtl2Lc9CX6MHc0RHaiojIsJye.gif)
import java.rmi.registry.LocateRegistry;
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLl52bO9CXzJ3b0F2YpRmbJdmbp5WasRXdP9CXn5Wa0h2ZpxGanlGa4FGdul3cvwFdl5mLuR2cj5ycldWYtl2Lc9CX6MHc0RHaiojIsJye.gif)
public class ServerMain ...{
public static void main(String argv[]) ...{
try ...{
LocateRegistry.createRegistry(1099);
ServerInterface s = new ServerImpl();
Naming.rebind("//127.0.0.1/Server", s);
} catch(Exception e) ...{
System.out.println("Server: "+e.getMessage());
e.printStackTrace();
}
Naming.rebind("//127.0.0.1/Server", s)語句假設RMI注冊處運作在預設的端口上,即代碼示例3中在LocateRegistry.createRegistry()語句定義的1099端口。可是,如果RMI注冊處運作在不同的端口上——例如,更改注冊位置建立語句為LocateRegistry.createRegistry(4500)——那麼你就要相應地将綁定語句過更改為:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLl52bO9CXzJ3b0F2YpRmbJdmbp5WasRXdP9CXn5Wa0h2ZpxGanlGa4FGdul3cvwFdl5mLuR2cj5ycldWYtl2Lc9CX6MHc0RHaiojIsJye.gif)
Naming.rebind("//127.0.0.1:4500/Server", s)
另外,特别需要注意的是,位址127.0.0.1是一個專用的localhost位址,它假定RMI注冊位置和伺服器運作在同一台機器上。如果它們不是運作在同一台機器上,隻需要簡單的更改rebind()方法中的位址,和RMI注冊處的IP位址相适應。
接下來步驟是在JavaFXClient工程中完成的,它和傳統RMI程式不同:現在必須建立一個Java連接配接助手。這個連接配接助手遠端調用遠端接口——ServerInterface——中指定任何方法,然後把結果傳回到調用它的JavaFX Script。為了完成這個任務,連接配接助手首先需要從RMI注冊處獲得遠端對象的引用。一旦獲得了引用,就可以調用适當的方法。
代碼示例4中給出了實作。注意,這隻是一個簡單的靜态方法,通過它JavaFX Script可以獲得遠端ServerInterfacde的實作對象的引用。
代碼示例4
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLl52bO9CXzJ3b0F2YpRmbJdmbp5WasRXdP9CXn5Wa0h2ZpxGanlGa4FGdul3cvwFdl5mLuR2cj5ycldWYtl2Lc9CX6MHc0RHaiojIsJye.gif)
package client;
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLl52bO9CXzJ3b0F2YpRmbJdmbp5WasRXdP9CXn5Wa0h2ZpxGanlGa4FGdul3cvwFdl5mLuR2cj5ycldWYtl2Lc9CX6MHc0RHaiojIsJye.gif)
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLl52bO9CXzJ3b0F2YpRmbJdmbp5WasRXdP9CXn5Wa0h2ZpxGanlGa4FGdul3cvwFdl5mLuR2cj5ycldWYtl2Lc9CX6MHc0RHaiojIsJye.gif)
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLl52bO9CXzJ3b0F2YpRmbJdmbp5WasRXdP9CXn5Wa0h2ZpxGanlGa4FGdul3cvwFdl5mLuR2cj5ycldWYtl2Lc9CX6MHc0RHaiojIsJye.gif)
import server.ServerInterface;
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLl52bO9CXzJ3b0F2YpRmbJdmbp5WasRXdP9CXn5Wa0h2ZpxGanlGa4FGdul3cvwFdl5mLuR2cj5ycldWYtl2Lc9CX6MHc0RHaiojIsJye.gif)
public class ConnectionHelper ...{
public static ServerInterface getConnection()
throws Exception
...{
return (ServerInterface)
Naming.lookup("rmi://127.0.0.1:1099/Server");
再一次需要注意,這段代碼假定RMI注冊處和用戶端運作在同一台機器上。如果不是這樣,修改IP位址——如果需要,同時修改端口——使二者比對。
然而,為了簡單期間,你可以隻完成其中的一個:
1、複制ServerInterface的源檔案到JavaFXClient工程的client中。
2、修改JavaFXClient工程的屬性,使其包含JavaRMIProject工程。
無論使用哪種方式,都要在源碼頂部中增加适當的導入語句,如代碼示例4所示。
最後,代碼示例5給出了尅用來建立JavaFX Script用戶端的代碼,該用戶端通過RMI和遠端伺服器通信。将代碼示例5中的代碼複制到MyClient.fx檔案中。
代碼示例5
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLl52bO9CXzJ3b0F2YpRmbJdmbp5WasRXdP9CXn5Wa0h2ZpxGanlGa4FGdul3cvwFdl5mLuR2cj5ycldWYtl2Lc9CX6MHc0RHaiojIsJye.gif)
import java.lang.*;
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLl52bO9CXzJ3b0F2YpRmbJdmbp5WasRXdP9CXn5Wa0h2ZpxGanlGa4FGdul3cvwFdl5mLuR2cj5ycldWYtl2Lc9CX6MHc0RHaiojIsJye.gif)
import javafx.ui.*;
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLl52bO9CXzJ3b0F2YpRmbJdmbp5WasRXdP9CXn5Wa0h2ZpxGanlGa4FGdul3cvwFdl5mLuR2cj5ycldWYtl2Lc9CX6MHc0RHaiojIsJye.gif)
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLl52bO9CXzJ3b0F2YpRmbJdmbp5WasRXdP9CXn5Wa0h2ZpxGanlGa4FGdul3cvwFdl5mLuR2cj5ycldWYtl2Lc9CX6MHc0RHaiojIsJye.gif)
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLl52bO9CXzJ3b0F2YpRmbJdmbp5WasRXdP9CXn5Wa0h2ZpxGanlGa4FGdul3cvwFdl5mLuR2cj5ycldWYtl2Lc9CX6MHc0RHaiojIsJye.gif)
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLl52bO9CXzJ3b0F2YpRmbJdmbp5WasRXdP9CXn5Wa0h2ZpxGanlGa4FGdul3cvwFdl5mLuR2cj5ycldWYtl2Lc9CX6MHc0RHaiojIsJye.gif)
import client.ConnectionHelper;
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLl52bO9CXzJ3b0F2YpRmbJdmbp5WasRXdP9CXn5Wa0h2ZpxGanlGa4FGdul3cvwFdl5mLuR2cj5ycldWYtl2Lc9CX6MHc0RHaiojIsJye.gif)
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLl52bO9CXzJ3b0F2YpRmbJdmbp5WasRXdP9CXn5Wa0h2ZpxGanlGa4FGdul3cvwFdl5mLuR2cj5ycldWYtl2Lc9CX6MHc0RHaiojIsJye.gif)
class ButtonClickModel ...{
attribute numClicks: Number;
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLl52bO9CXzJ3b0F2YpRmbJdmbp5WasRXdP9CXn5Wa0h2ZpxGanlGa4FGdul3cvwFdl5mLuR2cj5ycldWYtl2Lc9CX6MHc0RHaiojIsJye.gif)
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLl52bO9CXzJ3b0F2YpRmbJdmbp5WasRXdP9CXn5Wa0h2ZpxGanlGa4FGdul3cvwFdl5mLuR2cj5ycldWYtl2Lc9CX6MHc0RHaiojIsJye.gif)
var model = new ButtonClickModel();
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLl52bO9CXzJ3b0F2YpRmbJdmbp5WasRXdP9CXn5Wa0h2ZpxGanlGa4FGdul3cvwFdl5mLuR2cj5ycldWYtl2Lc9CX6MHc0RHaiojIsJye.gif)
var win = Frame ...{
width: 200
content: GridPanel ...{
border: EmptyBorder ...{
top: 30
left: 30
bottom: 30
right: 30
rows: 3
columns: 1
vgap: 10
cells:
[ Button ...{
text: "Click to make RMI connection!"
mnemonic: I
action: operation() ...{
try ...{
var remoteServer:ServerInterface =
ConnectionHelper.getConnection();
var results = remoteServer.ping("Test");
System.out.println("response: {results}");
model.numClicks++;
} catch (e:Exception) ...{
System.out.println("exception: {e}");
}
}
},
Label ...{
text: bind "Number of RMI connections: {model.numClicks}"
}
]
visible: true
};
現在,編譯和運作服務端工程。接着,運作用戶端工程。應該可以看到類似圖3所示的界面。點選“Click to make RMI connection”按鈕。一段時間後,檢查是否有響應資訊輸出到了NetBeans IDE底部的輸出控制台上。每連接配接成功一次,按鈕下面的計數器就會增加一。
圖3 JavaFX用戶端調用遠端伺服器
總結(Conclusion)
本文簡單介紹了使用JavaFX Script和RMI進行用戶端--伺服器程式設計的方法。在本文中,JavaFX平台應用在整個用戶端中。本系列的第三部分将讨論使用JavaFX平台和Java API for XML Web Services(JAX-WS)處理用戶端--伺服器通信的方式。以後的文章中将使用已有的用戶端-伺服器Java代碼內建到JavaFX Script平台中。
更多資訊參考(For More Information)
<a href="http://java.sun.com/developer/technicalArticles/scripting/javafxpart1/">Learning JavaFX Script, Part 1: An Introduction to JavaFX Script for Java Programmers.</a>