一、Web服務(wù)元數(shù)據(jù)
二、腳本語言支持
三、嵌入式數(shù)據(jù)庫(kù)
四、在Java SE6(Mustang)中使用Desktop API
五、使用Compiler API
六、輕量級(jí)Http Server
七、用Console開發(fā)控制臺(tái)程序
一、Web服務(wù)元數(shù)據(jù) top
.Net的Web Services元數(shù)據(jù)
早在.Net Framework 1.0中,微軟就用元數(shù)據(jù)功能(.net的attribute特性)來標(biāo)注要暴露成Web Service的方法,下面是用C#演示的利用.net的元數(shù)據(jù)功能暴露Web Service方法的代碼片斷.
public class TestWS{
[WebMethod]
public String sayHi(){
return "Hi!";
}
public int add(int d1,int d2){
return d1+d2;
}
}
上面的[WebMethod]是加在方法sayHi上面的元數(shù)據(jù),用來告訴Web Services引擎(一般是ASP.NET Runtime), 我這個(gè)方法需要暴露為一個(gè)Web Service,你需要幫我生成相應(yīng)的WSDL描述及相關(guān)支持文件.而另一個(gè)方法add沒有加這個(gè)元數(shù)據(jù),所以Web Services引擎就不會(huì)為該方法生成WSDL及相關(guān)支持文件
Java的Web Services元數(shù)據(jù)
Java 里的Web服務(wù)元數(shù)據(jù)跟微軟的方案基本沒有語義上的區(qū)別,自從JDK5添加了元數(shù)據(jù)功能(Annotation)之后,SUN幾乎重構(gòu)了整個(gè)J2EE體 系, 由于變化很大,干脆將名字也重構(gòu)為Java EE, Java EE(當(dāng)前版本為5.0)將元數(shù)據(jù)納入很多規(guī)范當(dāng)中,這其中就包括Web Services的相關(guān)規(guī)范, 加入元數(shù)據(jù)之后的Web Services服務(wù)器端編程模型就跟上面看到的C#片斷差不多了, 這顯然比以前的JAX-RPC編程模型簡(jiǎn)單(當(dāng)然, Axis的編程模型也很簡(jiǎn)單).這里要談的Web服務(wù)元數(shù)據(jù)(JSR 181)只是Java Web 服務(wù)規(guī)范中的一個(gè),它跟Common Annotations, JAXB2, StAX, SAAJ和JAX-WS等共同構(gòu)成Java EE 5的Web Services技術(shù)堆棧.
JSR-181的元數(shù)據(jù)清單
下面介紹JSR-181里面各個(gè)元數(shù)據(jù)的相關(guān)參數(shù)及用途
Annotation Retention Target Description
WebService Runtime Type
標(biāo)注要暴露為Web Services的類或接口
WebParam Runtime Parameter 自定義服務(wù)方法參數(shù)到WSDL的映射
WebResult Runtime Method 自定義服務(wù)方法返回值到WSDL的映射
WebMethod Runtime Method 自定義單個(gè)服務(wù)方法到WSDL的映射
Oneway Runtime Method 必須與@WebMethod連用,表明被標(biāo)注方法只有輸入沒有輸出,這就要求被標(biāo)注方法不能有返回值,也不能聲明checked exception
HandlerChain Runtime Type,Method,Field 將Web服務(wù)與外部Handler chain關(guān)聯(lián)起來
SOAPBinding Runtime Type,Method 自定義SOAPBinding
JSR-181元數(shù)據(jù)使用示例
package WebServices;
import java.io.File;
import java.io.IOException;
import javax.jws.Oneway;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.xml.ws.Endpoint;
/**
* @author chinajash
*/
@WebService(targetNamespace="public class WSProvider {
@WebResult(name="Greetings")//自定義該方法返回值在WSDL中相關(guān)的描述
@WebMethod
public String sayHi(@WebParam(name="MyName") String name){
return "Hi,"+name; //@WebParam是自定義參數(shù)name在WSDL中相關(guān)的描述
}
@Oneway //表明該服務(wù)方法是單向的,既沒有返回值,也不應(yīng)該聲明檢查異常
@WebMethod(action="printSystemTime",operationName="printSystemTime")//自定義該方法在WSDL中相關(guān)的描述
public void printTime(){
System.out.println(System.currentTimeMillis());
}
public static void main(String[] args) {
Thread wsPublisher = new Thread(new WSPublisher());
wsPublisher.start();
}
private static class WSPublisher implements Runnable{
public void run() {
//發(fā)布WSProvider到http://localhost:8888/chinajash/WSProvider這個(gè)地址,之前必須調(diào)用wsgen命令
//生成服務(wù)類WSProvider的支持類,命令如下:
//wsgen -cp . WebServices.WSProvider
Endpoint.publish("http://localhost:8888/chinajash/WSProvider",new WSProvider());
}
}
}
如果想看到Web Services Engine生成的WSDL文件是否遵守上面的元數(shù)據(jù), 我們沒有必要將上面的WSProvider部署到支持JSR-181的應(yīng)用服務(wù)器或Servlet形式的Web Services Engine,現(xiàn)在JDK6已經(jīng)提供了一個(gè)很簡(jiǎn)單的機(jī)制可以用來測(cè)試和發(fā)布Web Services,下面講講如何在JDK6環(huán)境下發(fā)布Web Services和查看生成的WSDL
1.將<JDK_HOME>/bin加入path環(huán)境變量
2.在命令行下切換當(dāng)前目錄到WSProvider的class文件所在的目錄,運(yùn)行下面命令
wsgen -cp . WebServices.WSProvider
在這個(gè)例子中會(huì)生成以下3個(gè)類的源代碼文件及class文件
SayHi
SayHiResponse
PrintTime
3.執(zhí)行如下代碼發(fā)布WSProvider到http://localhost:8888/chinajash/WSProvider,在這里可以執(zhí)行WSProvider類的main方法就可以
Endpoint.publish("http://localhost:8888/chinajash/WSProvider",new WSProvider());
4.在瀏覽器輸入http://localhost:8888/chinajash/WSProvider?wsdl就可以看到生成的WSDL文件,為了節(jié)省篇幅,這里就不把生成的WSDL文件貼上了,大家可以自己動(dòng)手試試.
--------------------------------------------------------------------------------------------------------
二、腳本語言支持 top
JDK6增加了對(duì)腳本語言的支持(JSR 223),原理上是將腳本語言編譯成bytecode,這樣腳本語言也能享用Java平臺(tái)的諸多優(yōu)勢(shì),包括可移植性,安全等,另外,由于現(xiàn)在是編譯成bytecode后再執(zhí)行,所以比原來邊解釋邊執(zhí)行效率要高很多。加入對(duì)腳本語言的支持后,對(duì)Java語言也提供了以下好處。
1、許多腳本語言都有動(dòng)態(tài)特性,比如,你不需要用一個(gè)變量之前先聲明它,你可以用一個(gè)變量存放完全不同類型的對(duì)象,你不需要做強(qiáng)制類型轉(zhuǎn)換,因?yàn)檗D(zhuǎn)換都是自動(dòng)的。現(xiàn)在Java語言也可以通過對(duì)腳本語言的支持間接獲得這種靈活性。
2、 可以用腳本語言快速開發(fā)產(chǎn)品原型,因?yàn)楝F(xiàn)在可以Edit-Run,而無需Edit-Compile-Run,當(dāng)然,因?yàn)镴ava有非常好的IDE支持,我 們完全可以在IDE里面編輯源文件,然后點(diǎn)擊運(yùn)行(隱含編譯),以此達(dá)到快速開發(fā)原型的目的,所以這點(diǎn)好處基本上可以忽略。
3、通過引入腳本語言可以輕松實(shí)現(xiàn)Java應(yīng)用程序的擴(kuò)展和自定義,我們可以把原來分布在在Java應(yīng)用程序中的配置邏輯,數(shù)學(xué)表達(dá)式和業(yè)務(wù)規(guī)則提取出來,轉(zhuǎn)用JavaScript來處理。
Sun的JDK6實(shí)現(xiàn)包含了一個(gè)基于Mozilla Rhino的 腳本語言引擎,支持JavaScript,這并不是說明JDK6只支持JavaScript,任何第三方都可以自己實(shí)現(xiàn)一個(gè)JSR-223兼容的腳本引擎 使得JDK6支持別的腳本語言,比如,你想讓JDK6支持Ruby,那你可以自己按照J(rèn)SR 223的規(guī)范實(shí)現(xiàn)一個(gè)Ruby的腳本引擎類,具體一點(diǎn),你需要實(shí)現(xiàn)javax.script.ScriptEngine(簡(jiǎn)單起見,可以繼承javax.script.AbstractScriptEngine)和javax.script.ScriptEngineFactory兩個(gè)接口。當(dāng)然,在你實(shí)現(xiàn)自己的腳本語言引擎之前,先到scripting.dev.java.net project 這里看看是不是有人已經(jīng)幫你做了工作,這樣你就可以直接拿來用就行。
Scripting API
Scripting API是用于在Java里面編寫腳本語言程序的API, 在Javax.script中可以找到Scripting API,我們就是用這個(gè)API來編寫JavaScript程序,這個(gè)包里面有一個(gè)ScriptEngineManager類,它是使用Scripting API的入口,ScriptEngineManager可以通過jar服務(wù)發(fā)現(xiàn)(service discovery)機(jī)制尋找合適的腳本引擎類(ScriptEngine),使用Scripting API的最簡(jiǎn)單方式只需下面三步
1、創(chuàng)建一個(gè)ScriptEngineManager對(duì)象
2、通過ScriptEngineManager獲得ScriptEngine對(duì)象
3、用ScriptEngine的eval方法執(zhí)行腳本
下面是一個(gè)Hello World程序
/** * @author chinajash */public class HelloScript {public static void main(String[] args) throws Exception { ScriptEngineManager factory = new ScriptEngineManager();//step 1 ScriptEngine engine = factory.getEngineByName("JavaScript");//Step 2 engine.eval("print('Hello, Scripting')");//Step 3 } }運(yùn)行上面程序,控制臺(tái)會(huì)輸出Hello, Scripting上面這個(gè)簡(jiǎn)單的Scripting程序演示了如何在Java里面運(yùn)行腳本語言,除此之外,我們還可以利用Scripting API實(shí)現(xiàn)以下功能1、暴露Java對(duì)象為腳本語言的全局變量2、在Java中調(diào)用腳本語言的方法3、腳本語言可以實(shí)現(xiàn)Java的接口4、腳本語言可以像Java一樣使用JDK平臺(tái)下的類下面的類演示了以上4種功能package Scripting;import java.io.File;import javax.script.Invocable;import javax.script.ScriptEngine;import javax.script.ScriptEngineManager;import javax.script.ScriptException;/** * @author chinajash */public class ScriptingAPITester { public static void main(String[] args) throws Exception { ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("JavaScript"); testScriptVariables(engine);//演示如何暴露Java對(duì)象為腳本語言的全局變量 testInvokeScriptMethod(engine);//演示如何在Java中調(diào)用腳本語言的方法 testScriptInterface(engine);//演示腳本語言如何實(shí)現(xiàn)Java的接口 testUsingJDKClasses(engine);//演示腳本語言如何使用JDK平臺(tái)下的類 } public static void testScriptVariables(ScriptEngine engine) throws ScriptException{ File file = new File("test.txt"); engine.put("f", file); engine.eval("println('Total Space:'+f.getTotalSpace())"); } public static void testInvokeScriptMethod(ScriptEngine engine) throws Exception{ String script = "function hello(name) { return 'Hello,' + name;}"; engine.eval(script); Invocable inv = (Invocable) engine; String res = (String)inv.invokeFunction("hello", "Scripting" ); System.out.println("res:"+res); } public static void testScriptInterface(ScriptEngine engine) throws ScriptException{ String script = "var obj = new Object(); obj.run = function() { println('run method called'); }"; engine.eval(script); Object obj = engine.get("obj"); Invocable inv = (Invocable) engine; Runnable r = inv.getInterface(obj,Runnable.class); Thread th = new Thread(r); th.start(); } public static void testUsingJDKClasses(ScriptEngine engine) throws Exception{ //Packages是腳本語言里的一個(gè)全局變量,專用于訪問JDK的package String js = "function doSwing(t){var f=new Packages.javax.swing.JFrame(t);f.setSize(400,300);f.setVisible(true);}"; engine.eval(js); Invocable inv = (Invocable) engine; inv.invokeFunction("doSwing", "Scripting Swing" ); }}Scripting Tool
SUN提供的JDK6中有一個(gè)命令行工具??jrunscript,你可以在<JDK6_Home>/bin下面找到這個(gè)工具,jrunscript是一個(gè)腳本語言的解釋程序,它獨(dú)立于腳本語言,但默認(rèn)是用JavaScript,我們可以用jrunscript來測(cè)試自己寫的腳本語言是否正確,下面是一個(gè)在命令行運(yùn)行jrunscript的簡(jiǎn)單例子
jrunscript
js>println("Hello,JrunScript");
Hello,JrunScript
js>9*8
72.0
js>
--------------------------------------------------------------------------------------------------------
三、嵌入式數(shù)據(jù)庫(kù)Derby top
Derby并不是一個(gè)新的數(shù)據(jù)庫(kù)產(chǎn)品,它是由IBM捐獻(xiàn)給Apache的DB項(xiàng)目的一個(gè)純Java數(shù)據(jù)庫(kù),JDK6.0里面帶的這個(gè)Derby的版本是10.2.1.7,支持存儲(chǔ)過程和觸發(fā)器;有兩種運(yùn)行模式,一種是作為嵌入式數(shù)據(jù)庫(kù),另一種是作為網(wǎng)絡(luò)數(shù)據(jù)庫(kù),前者的數(shù)據(jù)庫(kù)服務(wù)器和客戶端都在同一個(gè)JVM里面運(yùn)行,后者允許數(shù)據(jù)庫(kù)服務(wù)器端和客戶端不在同一個(gè)JVM里面,而且允許這兩者在不同的物理機(jī)器上.值得注意的是JDK6里面的這個(gè)Derby支持JDK6的新特性JDBC 4.0規(guī)范(JSR 221),現(xiàn)在我們?nèi)绻毩?xí)JDBC的用法,沒有必要單獨(dú)裝一個(gè)數(shù)據(jù)庫(kù)產(chǎn)品了,直接用Derby就行.安裝完JDK6.0后,Derby會(huì)被安裝到<JDK6_HOME>/db下面,在<JDK6_HOME>/db/demo/programs下面還有一些示例程序,演示了如何啟動(dòng),連接Derby數(shù)據(jù)庫(kù)以及JDBC API的使用.下面分兩種情況演示一下如何用代碼操作Derby數(shù)據(jù)庫(kù),一種是嵌入式數(shù)據(jù)庫(kù),一種是網(wǎng)絡(luò)數(shù)據(jù)庫(kù).
一.嵌入式數(shù)據(jù)庫(kù)
/**
* @author chinajash
*/
public class EmbeddedDerbyTester {
public static void main(String[] args) {
String driver = "org.apache.derby.jdbc.EmbeddedDriver";//在derby.jar里面
String dbName="EmbeddedDB";
String dbURL = "jdbc:derby:"+dbName+";create=true";//create=true表示當(dāng)數(shù)據(jù)庫(kù)不存在時(shí)就創(chuàng)建它
try {
Class.forName(driver);
Connection conn = DriverManager.getConnection(dbURL);//啟動(dòng)嵌入式數(shù)據(jù)庫(kù)
Statement st = conn.createStatement();
st.execute("create table foo (FOOID INT NOT NULL,FOONAME VARCHAR(30) NOT NULL)");//創(chuàng)建foo表
st.executeUpdate("insert into foo(FOOID,FOONAME) values (1,'chinajash')");//插入一條數(shù)據(jù)
ResultSet rs = st.executeQuery("select * from foo");//讀取剛插入的數(shù)據(jù)
while(rs.next()){
int id = rs.getInt(1);
String name = rs.getString(2);
System.out.println("id="+id+";name="+name);
}
} catch(Exception e){
e.printStackTrace();
}
}
}
運(yùn)行上面程序后,會(huì)在當(dāng)前目錄生成名為EmbeddedDB的文件夾,既是EmbeddedDB數(shù)據(jù)庫(kù)的數(shù)據(jù)文件存放的地方,控制臺(tái)將輸出
id=1;name=chinajash
二.網(wǎng)絡(luò)數(shù)據(jù)庫(kù)
/**
* @author chinajash
*/
public class NetworkServerDerbyTester {
public static void main(String[] args) {
String driver = "org.apache.derby.jdbc.ClientDriver";//在derbyclient.jar里面
String dbName="NetworkDB";
String connectionURL = "jdbc:derby://localhost:1527/" + dbName + ";create=true";
try {
/*
創(chuàng)建Derby網(wǎng)絡(luò)服務(wù)器,默認(rèn)端口是1527,也可以通過運(yùn)行
<Derby_Home>\frameworks\NetworkServer\bin\startNetworkServer.bat
來創(chuàng)建并啟動(dòng)Derby網(wǎng)絡(luò)服務(wù)器,如果是Unix,用startNetworkServer.ksh
*/
NetworkServerControl derbyServer = new NetworkServerControl();//NetworkServerControl類在derbynet.jar里面
PrintWriter pw = new PrintWriter(System.out);//用系統(tǒng)輸出作為Derby數(shù)據(jù)庫(kù)的輸出
derbyServer.start(pw);//啟動(dòng)Derby服務(wù)器
Class.forName(driver);
DriverManager.getConnection(connectionURL);
//do something
derbyServer.shutdown();//關(guān)閉Derby服務(wù)器
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
運(yùn)行上面程序后,會(huì)在當(dāng)前目錄生成名為NetworkDB的文件夾
關(guān)于Derby的詳細(xì)情況,請(qǐng)參考http://db.apache.org/derby
----------------------------------------------------------------------------------------------------
四、在Java SE6(Mustang)中使用Desktop API top
伴隨著默認(rèn)的圖形用戶界面(GUI)的外觀,打印和性能,Java平臺(tái)在縮小平臺(tái)差異和整合本地程序與Java程序的道路上已經(jīng)走了很長(zhǎng)一段路。
Java平臺(tái)標(biāo)準(zhǔn)版(Java SE)第6版,代號(hào)為Mustang(野馬),繼續(xù)用新增的系統(tǒng)托盤功能,對(duì)JTable更好的打印支持和現(xiàn)在的Desktop API
(java.awt.Desktop)來填補(bǔ)這個(gè)空隙。本文描述了新的Desktop API,它們?cè)试SJava程序來和主機(jī)平臺(tái)與特定文件類型相關(guān)聯(lián)的默認(rèn)應(yīng)用程序進(jìn)
行交互。為了更有效地描述這些API,本文也展示了一個(gè)簡(jiǎn)單的應(yīng)用程序DesktopDemo。
Desktop 概述
這個(gè)新功能是由java.awt.Desktop類提供的。這個(gè)API取自桌面綜合組件(JDIC)項(xiàng)目。這個(gè)項(xiàng)目的目標(biāo)是“使以Java技術(shù)為基礎(chǔ)的應(yīng)用程序成為
桌面的一級(jí)公民”,實(shí)現(xiàn)無縫整合。特別的,新的Desktop API允許Java應(yīng)用程序?qū)崿F(xiàn)以下功能:
1.通過一個(gè)指定的統(tǒng)一資源定位符(URI)啟動(dòng)主機(jī)系統(tǒng)的默認(rèn)瀏覽器
2.啟動(dòng)主機(jī)系統(tǒng)的默認(rèn)郵件客戶端
3.啟動(dòng)應(yīng)用程序來打開,編輯或打印和這些應(yīng)用程序相關(guān)聯(lián)的文件
Desktop API使用主機(jī)操作系統(tǒng)的文件關(guān)聯(lián)來啟動(dòng)和特定文件類型相關(guān)聯(lián)的應(yīng)用程序。例如,如果OpenDocument text(.odt)文件擴(kuò)展名與
OpenOffice Writer應(yīng)用程序相關(guān)聯(lián),你的Java應(yīng)用程序就能夠根據(jù)這個(gè)關(guān)聯(lián)啟動(dòng)OpenOffice Writer來打開,編輯或者甚至輸出.odt文件。依
靠你的主機(jī)系統(tǒng),不同的應(yīng)用程序可能會(huì)和不同的動(dòng)作相關(guān)聯(lián)。
運(yùn)行DesktopDemo應(yīng)用程序
DesktopDemo是使用Mustang的Desktop API的一個(gè)簡(jiǎn)單應(yīng)用程序。這個(gè)程序有一個(gè)展示了完整API的主窗口,允許你做三件事:
1.通過一個(gè)指定的URI啟動(dòng)默認(rèn)的瀏覽器
2.通過一個(gè)郵件接收者啟動(dòng)默認(rèn)的郵件客戶端
3.啟動(dòng)相關(guān)聯(lián)的應(yīng)用程序來打開,編輯或打印一個(gè)文件
圖1顯示了用戶界面(UI)

你可以通過下載源程序和JAR文件來運(yùn)行這個(gè)應(yīng)用程序,把控制臺(tái)轉(zhuǎn)到工程的dist目錄,在Mustang JDK環(huán)境下用以下的命令來運(yùn)行:
java -jar DesktopDemo.jar
判定對(duì)Desktop API的支持
在啟動(dòng)瀏覽器,郵件客戶端或者任何應(yīng)用程序之前,DesktopDemo必須判定你的平臺(tái)是否支持Desktop API。首先,DesktopDemo會(huì)禁用所有的文
本域和按鈕。它會(huì)在判定你的平臺(tái)支持它們以后再激活它們。
在展示了用戶界面以后,應(yīng)用程序的構(gòu)造函數(shù)很快地用如下所示的代碼禁用了這個(gè)應(yīng)用程序的組件。
public DesktopDemo() {
// Initiate all GUI components.
initComponents();
// Disable buttons that launch browser and email client.
// Disable buttons that open, edit, and print files.
disableActions();
...
}
/**
* Disable all graphical components until we know
* whether their functionality is supported.
*/
private void disableActions() {
txtBrowserURI.setEnabled(false);
btnLaunchBrowser.setEnabled(false);
txtMailTo.setEnabled(false);
btnLaunchEmail.setEnabled(false);
rbEdit.setEnabled(false);
rbOpen.setEnabled(false);
rbPrint.setEnabled(false);
txtFile.setEnabled(false);
btnLaunchApplication.setEnabled(false);
}
...
public javax.swing.JTextField txtBrowserURI;
public javax.swing.JButton btnLaunchBrowser;
public javax.swing.JTextField txtMailTo;
public javax.swing.JButton btnLaunchEmail;
public javax.swing.JRadioButton rbEdit;
public javax.swing.JRadioButton rbOpen;
public javax.swing.JRadioButton rbPrint;
public javax.swing.JTextField txtFile;
public javax.swing.JButton btnLaunchApplication;
用Desktop.isDesktopSupported()方法判定Desktop API是否可用。在Solaris操作系統(tǒng)和Linux平臺(tái)上,這個(gè)API依賴于Gnome庫(kù)。如果這些庫(kù)不
可用,這個(gè)方法會(huì)返回false。在判定了系統(tǒng)支持Desktop API,也就是isDesktopSupported()方法返回true時(shí),應(yīng)用程序就會(huì)使用一個(gè)靜態(tài)
(static)方法getDesktop()重新獲得一個(gè)Desktop實(shí)例。
Desktop desktop = null;
// Before more Desktop API is used, first check
// whether the API is supported by this particular
// virtual machine (VM) on this particular host.
if (Desktop.isDesktopSupported()) {
desktop = Desktop.getDesktop();
...
如果你的應(yīng)用程序沒有在調(diào)用getDesktop()方法之前用isDesktopSupported()方法檢查系統(tǒng)對(duì)API的支持,那就必須準(zhǔn)備捕捉
UnsupportedOperationException異常,該異常會(huì)在你的應(yīng)用程序請(qǐng)求一個(gè)Desktop實(shí)例而平臺(tái)不支持的時(shí)候拋出。另外,如果你的應(yīng)用程序在
一個(gè)沒有鍵盤,鼠標(biāo)或者監(jiān)聽器的環(huán)境(一個(gè)“無領(lǐng)導(dǎo)者”的環(huán)境)中運(yùn)行,getdesktop()方法會(huì)拋出一個(gè)java.awt.HeadlessException異常。
一旦重新獲得,Desktop實(shí)例允許你的應(yīng)用程序?yàn)g覽,發(fā)郵件,打開、編輯甚至打印文件或者URI,只要重新獲得的Desktop實(shí)例支持這些動(dòng)作。
每個(gè)這種動(dòng)作被稱為一個(gè)action,而每一個(gè)Desktop.Action枚舉實(shí)例代表一個(gè)action:
BROWSE:代表一個(gè)通過主機(jī)瀏覽器執(zhí)行的瀏覽動(dòng)作
MAIL:代表一個(gè)通過默認(rèn)郵件客戶端執(zhí)行的發(fā)郵件動(dòng)作
OPEN:代表一個(gè)通過相關(guān)聯(lián)的應(yīng)用程序打開一種特定文件類型的打開動(dòng)作
EDIT:代表一個(gè)通過相關(guān)聯(lián)的應(yīng)用程序編輯一種特定文件類型的編輯動(dòng)作
PRINT:代表一個(gè)通過相關(guān)聯(lián)的應(yīng)用程序打印一種特定文件類型的打印動(dòng)作
在調(diào)用這些動(dòng)作之前,應(yīng)用程序應(yīng)該判定Desktop實(shí)例是否支持它們。這和判定Desktop實(shí)例是否可用是不同的。Desktop.isDesktopSupported
()方法告訴我們能否創(chuàng)建一個(gè)實(shí)例。一旦獲得了一個(gè)Desktop對(duì)象,你可以查詢這個(gè)實(shí)例來找出支持哪些特定的動(dòng)作。如果Desktop對(duì)象不支持
特定的動(dòng)作,或者Desktop API本身不被支持,DesktopDemo會(huì)簡(jiǎn)單地禁用受影響的那些圖形組件。如圖2所展示的,在禁用狀態(tài)下這些組件不能
被用來調(diào)用Desktop功能。

使用一個(gè)新的Desktop實(shí)例,下面的代碼檢查了每個(gè)Desktop.Action的支持情況并且激活了適當(dāng)?shù)膱D形組件:
public DesktopDemo() {
...
// Before more Desktop API is used, first check
// whether the API is supported by this particular
// VM on this particular host.
if (Desktop.isDesktopSupported()) {
desktop = Desktop.getDesktop();
// Now enable buttons for actions that are supported.
enableSupportedActions();
}
...
}
/**
* Enable actions that are supported on this host.
* The actions are the following: open browser,
* open email client, and open, edit, and print
* files using their associated application.
*/
private void enableSupportedActions() {
if (desktop.isSupported(Desktop.Action.BROWSE)) {
txtBrowserURI.setEnabled(true);
btnLaunchBrowser.setEnabled(true);
}
if (desktop.isSupported(Desktop.Action.MAIL)) {
txtMailTo.setEnabled(true);
btnLaunchEmail.setEnabled(true);
}
if (desktop.isSupported(Desktop.Action.OPEN)) {
rbOpen.setEnabled(true);
}
if (desktop.isSupported(Desktop.Action.EDIT)) {
rbEdit.setEnabled(true);
}
if (desktop.isSupported(Desktop.Action.PRINT)) {
rbPrint.setEnabled(true);
}
if (rbEdit.isEnabled() || rbOpen.isEnabled() || rbPrint.isEnabled()) {
txtFile.setEnabled(true);
btnLaunchApplication.setEnabled(true);
}
}
一旦應(yīng)用程序判定了可支持的動(dòng)作,它會(huì)激活適當(dāng)?shù)膱D形組件。如果所有組件都被激活,用戶界面應(yīng)該如圖3所示:

打開瀏覽器
調(diào)用下面的實(shí)例方法會(huì)打開主機(jī)的默認(rèn)瀏覽器:
public void browse(URI uri) throws IOException
因?yàn)镈esktopDemo的用戶界面組件只有在Desktop.Action被支持的情況下才會(huì)被激活,所以這個(gè)簡(jiǎn)單的示例程序不需要在調(diào)用browse()方法之前
再次檢查支持情況。然而,在實(shí)際應(yīng)用程序中,在每個(gè)調(diào)用之前檢查支持情況可能會(huì)使程序更加健壯:
if (desktop.isSupported(Desktop.Action.BROWSE)) {
// launch browser
...
}
DesktopDemo為每個(gè)按鈕添加了java.awt.event.ActionListener。當(dāng)被激活時(shí),Launch Browser按鈕會(huì)通過它的ActionListener調(diào)用如下方法
:
private void onLaunchBrowser(java.awt.event.ActionEvent evt) {
URI uri = null;
try {
uri = new URI(txtBrowserURI.getText());
desktop.browse(uri);
}
catch(IOException ioe) {
ioe.printStackTrace();
}
catch(URISyntaxException use) {
use.printStackTrace();
}
...
}
browse()方法會(huì)拋出多種異常,包括當(dāng)URI為空時(shí)拋出的NullPointerException,當(dāng)BROWSE動(dòng)作不被支持時(shí)拋出的
UnsupportedOperationException,當(dāng)默認(rèn)瀏覽器或者應(yīng)用程序無法找到或者啟動(dòng)時(shí)拋出的IOException,以及當(dāng)安全管理器拒絕調(diào)用時(shí)拋出的
SecurityException。
如果進(jìn)展順利,監(jiān)聽器會(huì)從圖4中所示的相關(guān)聯(lián)的文本域中重新獲得文本,創(chuàng)建一個(gè)URI,然后調(diào)用browse()方法。上面的代碼啟動(dòng)了你系統(tǒng)上
默認(rèn)的瀏覽器來加載URI,如圖5所示。


發(fā)送郵件
如果動(dòng)作被支持的話,應(yīng)用程序能夠啟動(dòng)主機(jī)的默認(rèn)郵件客戶端,通過調(diào)用Desktop的實(shí)例方法:
public void mail(URI uri) throws IOException
DesktopDemo有一個(gè)Launch Mail按鈕的ActionListener。在這種情況下,監(jiān)聽器調(diào)用如下方法:
private void onLaunchMail(java.awt.event.ActionEvent evt) {
String mailTo = txtMailTo.getText();
URI uriMailTo = null;
try {
if (mailTo.length() > 0) {
uriMailTo = new URI("mailto", mailTo, null);
desktop.mail(uriMailTo);
} else {
desktop.mail();
}
}
catch(IOException ioe) {
ioe.printStackTrace();
}
catch(URISyntaxException use) {
use.printStackTrace();
}
...
}
onLaunchMail()方法重新獲得和文本域相關(guān)的郵件接收者,如果接收者存在就使用mailto模式參數(shù)創(chuàng)建URI,然后調(diào)用mail()方法。mail()方法
已經(jīng)被重載,因此你可以通過使用或者不使用代表mailto接收者的URI來調(diào)用它(見圖6)。

你可以不僅僅使用一個(gè)簡(jiǎn)單郵件接收者來創(chuàng)建URI。mailto模式也支持CC,BCC,SUBJECT和BODY字段。例如,下面的文本可以被用來創(chuàng)建一個(gè)
mailto URI。
mailto:duke@sun.com?SUBJECT=Happy New Year!&BODY=Happy New Year, Duke!
圖7顯示了結(jié)果:

當(dāng)然,你也可以不使用參數(shù)調(diào)用mail()方法。在這種情況下,你的郵件客戶端將會(huì)啟動(dòng)一個(gè)新的沒有指定接收者、主題或者主體的郵件窗口。
打開,編輯和打印一個(gè)文件
Java應(yīng)用程序可以分別使用Desktop對(duì)象的open()、edit()、print()方法通過和文件相關(guān)聯(lián)的應(yīng)用程序打開、編輯和打印文件。(見圖8)而且,
DesktopDemo只有在Desktop實(shí)例支持的情況下允許這些動(dòng)作,因此在這個(gè)應(yīng)用程序的設(shè)定中,沒有必要再檢查這些支持情況。

每個(gè)DesktopDemo的單選按鈕也都有它們自己的ActionListener。在這種情況下,每個(gè)監(jiān)聽器設(shè)置一個(gè)實(shí)例變量的值來代表最近選擇的按鈕相關(guān)
聯(lián)的Desktop.Action:
Desktop.Action action;
private void onPrintAction(java.awt.event.ActionEvent evt) {
action = Desktop.Action.PRINT;
}
private void onEditAction(java.awt.event.ActionEvent evt) {
action = Desktop.Action.EDIT;
}
private void onOpenAction(java.awt.event.ActionEvent evt) {
action = Desktop.Action.OPEN;
}
當(dāng)你按下Launch Default Application按鈕時(shí),它觸發(fā)它自己的監(jiān)聽器,調(diào)用如下方法:
private void onLaunchDefaultApplication(java.awt.event.ActionEvent evt) {
String fileName = txtFile.getText();
File file = new File(fileName);
try {
switch(action) {
case OPEN:
desktop.open(file);
break;
case EDIT:
desktop.edit(file);
break;
case PRINT:
desktop.print(file);
break;
}
}
catch (IOException ioe) {
ioe.printStackTrace();
}
...
}
這個(gè)方法判定哪個(gè)Desktop.Action被選擇并且調(diào)用適當(dāng)?shù)腄esktop的實(shí)例方法,open(),edit()或者print()。每個(gè)方法要求一個(gè)File參數(shù)用來
執(zhí)行被請(qǐng)求的動(dòng)作。
有趣的是,在相同的文件類型上的不同動(dòng)作可能會(huì)被注冊(cè)為不同的應(yīng)用程序。例如,F(xiàn)irefox瀏覽器可能會(huì)執(zhí)行OPEN操作,Emacs執(zhí)行EDIT操作
,另一種不同的應(yīng)用程序執(zhí)行PRINT操作。你的主機(jī)桌面的關(guān)聯(lián)用來決定應(yīng)該調(diào)用哪個(gè)應(yīng)用程序。這種操作桌面文件關(guān)聯(lián)的能力對(duì)目前存在的
Mustang中的Desktop API來說還不可能,這樣這些關(guān)聯(lián)就只能通過與平臺(tái)相關(guān)的工具來創(chuàng)建或改變了。
摘要
桌面整合是一個(gè)重要的Mustang主題。Mustang支持這個(gè)主題的一種方法是通過java.awt.Desktop API。這個(gè)API允許Java應(yīng)用程序啟動(dòng)主機(jī)的默
認(rèn)的瀏覽器和郵件客戶端。另外,Java應(yīng)用程序能夠啟動(dòng)和文件相關(guān)的應(yīng)用程序來打開、編輯和打印文件。盡管Java應(yīng)用程序不能操作、創(chuàng)建
或者改變文件關(guān)聯(lián),Desktop API允許Java應(yīng)用程序啟動(dòng)默認(rèn)的相關(guān)聯(lián)的應(yīng)用程序。這篇文章提供了一個(gè)示范這些API的示例程序,你可以從站
點(diǎn)上下載這個(gè)程序。
注意:任何Java SE平臺(tái)規(guī)范的增加或改進(jìn)由JSR 270專家組回顧和正式批準(zhǔn)。
原文地址:http://java.sun.com/developer/technicalArticles/J2SE/Desktop/mustang/desktop_api/
-----------------------------------------------------------------------------------------
五、使用Compiler API top
現(xiàn)在我們可以用JDK6 的Compiler API(JSR 199)去動(dòng)態(tài)編譯Java源文件,Compiler API結(jié)合反射功能就可以實(shí)現(xiàn)動(dòng)態(tài)的產(chǎn)生Java代碼并編譯執(zhí)行這些代碼,有點(diǎn)動(dòng)態(tài)語言的特征。這個(gè)特性對(duì)于某些需要用到動(dòng)態(tài)編譯的應(yīng)用程序相當(dāng)有用, 比如JSP Web Server,當(dāng)我們手動(dòng)修改JSP后,是不希望需要重啟Web Server才可以看到效果的,這時(shí)候我們就可以用Compiler API來實(shí)現(xiàn)動(dòng)態(tài)編譯JSP文件,當(dāng)然,現(xiàn)在的JSP Web Server也是支持JSP熱部署的,現(xiàn)在的JSP Web Server通過在運(yùn)行期間通過Runtime.exec或ProcessBuilder來調(diào)用javac來編譯代碼,這種方式需要我們產(chǎn)生另一個(gè)進(jìn)程去做編譯工作,不夠優(yōu)雅而且容易使代碼依賴與特定的操作系統(tǒng);Compiler API通過一套易用的標(biāo)準(zhǔn)的API提供了更加豐富的方式去做動(dòng)態(tài)編譯,而且是跨平臺(tái)的。 下面代碼演示了Compiler API的使用
public class CompilerAPITester {
private static String JAVA_SOURCE_FILE = "DynamicObject.java";
private static String JAVA_CLASS_FILE = "DynamicObject.class";
private static String JAVA_CLASS_NAME = "DynamicObject";
public static void main(String[] args) {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
generateJavaClass();
try {
//將產(chǎn)生的類文件拷貝到程序的ClassPath下面,下面這一行代碼是特定于Windows+I(xiàn)ntelliJ IDEA 6.0項(xiàng)目,不具有移植性
Runtime.getRuntime().exec("cmd /c copy "+JAVA_CLASS_FILE+" classes\\production\\JDK6Features");
Iterable<? extends JavaFileObject> sourcefiles = fileManager.getJavaFileObjects(JAVA_SOURCE_FILE);
compiler.getTask(null, fileManager, null, null, null, sourcefiles).call();
fileManager.close();
Class.forName(JAVA_CLASS_NAME).newInstance();//創(chuàng)建動(dòng)態(tài)編譯得到的DynamicObject類的實(shí)例
} catch (Exception ex) {
ex.printStackTrace();
}
}
public static void generateJavaClass(){
try {
FileWriter fw = new FileWriter(JAVA_SOURCE_FILE);
BufferedWriter bw = new BufferedWriter(fw);
bw.write("public class "+JAVA_CLASS_NAME+"{");
bw.newLine();
bw.write("public "+JAVA_CLASS_NAME+"(){System.out.println(\"In the constructor of DynamicObject\");}}");
bw.flush();
bw.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
程序運(yùn)行后,會(huì)產(chǎn)生DynamicObject.java和DynamicObject.class兩個(gè)文件,并在控制臺(tái)輸出
In the constructor of DynamicObject
--------------------------------------------------------------------------------
六、輕量級(jí)Http Server top
JDK6提供了一個(gè)簡(jiǎn)單的Http Server API,據(jù)此我們可以構(gòu)建自己的嵌入式Http Server,它支持Http和Https協(xié)議,提供了HTTP1.1的部分實(shí)現(xiàn),沒有被實(shí)現(xiàn)的那部分可以通過擴(kuò)展已有的Http Server API來實(shí)現(xiàn),程序員必須自己實(shí)現(xiàn)HttpHandler接口,HttpServer會(huì)調(diào)用HttpHandler實(shí)現(xiàn)類的回調(diào)方法來處理客戶端請(qǐng)求,在這里,我們把一個(gè)Http請(qǐng)求和它的響應(yīng)稱為一個(gè)交換,包裝成HttpExchange類,HttpServer負(fù)責(zé)將HttpExchange傳給HttpHandler實(shí)現(xiàn)類的回調(diào)方法.下面代碼演示了怎樣創(chuàng)建自己的Http Server
/**
* Created by IntelliJ IDEA.
* User: Chinajash
* Date: Dec 30, 2006
*/
public class HTTPServerAPITester {
public static void main(String[] args) {
try {
HttpServer hs = HttpServer.create(new InetSocketAddress(8888),0);//設(shè)置HttpServer的端口為8888
hs.createContext("/chinajash", new MyHandler());//用MyHandler類內(nèi)處理到/chinajash的請(qǐng)求
hs.setExecutor(null); // creates a default executor
hs.start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class MyHandler implements HttpHandler {
public void handle(HttpExchange t) throws IOException {
InputStream is = t.getRequestBody();
String response = "<h3>Happy New Year 2007!--Chinajash</h3>";
t.sendResponseHeaders(200, response.length());
OutputStream os = t.getResponseBody();
os.write(response.getBytes());
os.close();
}
}
運(yùn)行程序后,在瀏覽器內(nèi)輸入http://localhost:8888/chinajash,瀏覽器輸出
Happy New Year 2007!--Chinajash
-------------------------------------------------------------------------------------------
七、用Console開發(fā)控制臺(tái)程序 top
JDK6中提供了java.io.Console類專用來訪問基于字符的控制臺(tái)設(shè)備. 你的程序如果要與Windows下的cmd或者Linux下的Terminal交互,就可以用Console類代勞. 但我們不總是能得到可用的Console, 一個(gè)JVM是否有可用的Console依賴于底層平臺(tái)和JVM如何被調(diào)用. 如果JVM是在交互式命令行(比如Windows的cmd)中啟動(dòng)的,并且輸入輸出沒有重定向到另外的地方,那么就可以得到一個(gè)可用的Console實(shí)例. 下面代碼演示了Console類的用法:
/**
* @author chinajash
*/
public class ConsoleTest {
public static void main(String[] args) {
Console console = System.console();//獲得Console實(shí)例
if(console!=null){//判斷console是否可用
String user = new String(console.readLine("Enter user:")); //讀取整行字符
String pwd = new String(console.readPassword("Enter passowrd:")); //讀取密碼,密碼輸入時(shí)不會(huì)顯示
console.printf("User is:"+user+"\n");
console.printf("Password is:"+pwd+"\n");
}else{
System.out.println("Console is unavailable");
}
}
}
如果在NetBean5.5里面運(yùn)行上面程序,會(huì)輸出
Console is unavailable
表示Console不可獲得,那是因?yàn)镴VM不是在命令行中被調(diào)用的或者輸入輸出被重定向了. 但是如果我們?cè)诿钚兄羞\(yùn)行上面程序(java ConsoleTest),程序能夠獲得Console實(shí)例,并執(zhí)行如下:
Enter user:chinajash
Enter passowrd:
User is:chinajash
Password is:chinajash
在這里可以看到輸入密碼時(shí),控制臺(tái)時(shí)不顯示這些密碼字符的,但是程序可以得到輸入的密碼字符串,這與Linux下面輸入密碼的情況是一樣的