現有項目組用SUN JDK1.5中自帶的示例:jnlp-servlet作為WEBSTART版本管理的SERVLET。它可以很好的實現JNLP相關資源(JAR,圖片等)的基于版本的管理和增量更新。并有幾個類似 $ $codebase, $ $name的可替換關鍵字。(原來寫了一個簡單的servlet,可以實現軟編碼,但沒法提供靈活的版本控制)
但現有項目的需求更高一些,即,JNLP中需要傳更多的參數,比如,服務端IP,端口,上下文,用戶登錄的SESSIONID等,由于jnlp-servlet有源代碼,我們很快修改了JnlpFileHandler和specializeJnlpTemplate方法,并加入了這幾個自定義關鍵字( $ $host, $ $port, $ $newcontext, $ $sessionid)。JNLP如下所示:
<?xml version="1.0" encoding="utf-8"?>
<jnlp spec="1.0 " codebase=" $ $codebase" href=” $ $name”>
<information>
<title>My System</title>
<vendor>CFR</vendor>
<icon href="indexbannerleft.jpg" kind="splash" />
<homepage href="index.html"/>
<description>My System</description>
<description kind="short">CFR Inc..</description>
</information>
<security><all-permissions/></security>
<resources>
<j2se version="1.4" initial-heap-size="32m"/>
<jar href="myapp.jar" version="1.1"/>
<nativelib href="swt-lib.jar" version="1.1"/>
</resources>
<resources os="Windows"><jar href="swt.jar" version="1.1"/></resources>
<resources os="Windows"><jar href="jface.jar" version="1.0"/></resources>
<resources os="Windows"><jar href="osgi.jar" version="1.0"/></resources>
<resources os="Windows"><jar href="runtime.jar" version="1.0"/></resources>
<application-desc main-class="com.cfr.app.main">
<argument> $ $session</argument>
<argument> $ $host</argument>
<argument> $ $port</argument>
<argument> $ $newcontext</argument>
<argument>0</argument>
<argument>aa</argument>
</application-desc>
</jnlp>
其中,SESSIONID是用其內部的request得到。
一切好象即簡單和明了,工作正常,但很快就發現了嚴重的問題。
首先說說該應用的使用模式如下:
1、 用戶從網頁登錄系統,然后在里面點擊:http://host:port/myapp/swt/index.jnlp鏈接。
2、 啟動應用(因為應用是跟當前登錄SESSION直接相關的)
按照設計本意,此時,用戶的應用應通過JNLP中的參數得到了該用戶登錄后的SESSIONID才對,但事實并非如此。這種情況只出現在WEBSTART第一次下載的時候,以后當用戶重新打開瀏覽器登錄后(此時當然是一個新的SESSION),在頁面中啟動WEBSTART后,發現,該SESSIONID還是以前的,并沒有想象中的將新SESSIONID傳了進來。
后來還是看看JnlpFileHandler中的源代碼,發現主要在getJnlpFile(客戶端是JRE1.5以下時調用)和getJnlpFileEx(客戶端是JDK1.5以上時用)進行了相關實現。原來,它每生成一個JNLP,就將其緩存在HASHMAP中,下一次請求,如果請求的URL相關并且文件的timestamp(就是Web服務器中的jnlp文件的修改時間,可想而知,除了升級,這個文件一般不會變)一樣,就從緩存中取,這樣一來,我們也可以推測,按照上面的設計,如果有一個用戶下載了應用,其它用戶從其它機器下載到的JNLP還是第一個用戶的JNLP,這是一個更加嚴重的問題。
由于URL是一樣的,現在考慮更改時間戳,即在getJnlpFile中顯式的將timesstamp改為當前的時間:
long lastModified = new java.util.Date().getTime();
這樣,任何一次請求都不會從緩存中獲得,而是重新生成一個,好象這能很好的解決這個問題了吧?
但事實遠沒有想象的那么簡單。這跟WEBSTART與服務端的交互過程有關系。
WEBSTART要通過多次從服務端交互才會真正下載JNLP文件,這主要是驗證一些時間等相關的屬性(具體我沒有看代碼)。大至是三次才會真正的把JNLP下載下來(其實服務端會生成三次,真正下載的是第三個),由于我們的SESSIONID是從SERVLET內部的REQUEST中直接得到,這樣一來,實際上只有第一個請求的SESSIONID是正確的(因為它是直接從瀏覽器中進入的),其它兩個都是WEBSTART用URLConnection建立的連接,SESSIONID都是新生成的,而下載的恰恰又是第三個,這樣一來,又黃了?。?!
接下來的想法是自己設法傳SESSIONID,而不是從當前REQUEST中取。所以,就剛才啟動WEBSTART的鏈接改為如下形式:
<a href="/myapp/swt/index.jnlp?sessionid=<%=request.getSession().getId()%>">啟動客戶端程序</a>
這樣,生成的URL會是:
http://localhost:8899/myapp/swt/index.jnlp?sessionid=F8864B2CDF60AE371CD6DFC189E80C78
按照前面的JNLP文件,下載下來的JNLP第一行是:
<jnlp spec="1.0 " codebase="http://127.0.0.1:8899/myapp/swt/" href=” http://localhost:8899/myapp/swt/index.jnlp?sessionid=F8864B2CDF60AE371CD6DFC189E80C78”>
既然有了SESSIONID(這意味著每個請求的URL都不會一樣?。覀兙陀貌恢鴮r間戳改為當前時間了,還按原來的做就行了。
到此為止,好像問題解決的很徹底哦!但不要高興的太早!?。。?!
我們同樣做實驗:
1、 將WEBSTART應用清空
2、 登錄系統,下載安裝應用并運行,一切OK!
3、 退出該系統
4、 打開新的瀏覽器并登錄
5、 點相應的鏈接啟動WEBSTART應用
6、 怪了:SESSIONID怎么還是前面的一個呢?
查看jnlp-servlet日志,剛才說了,要經過多次握手才會實際的下載JNLP,從流程中發現,客戶端發的請求,第一個是對的即是http://localhost:8899/myapp/swt/index.jnlp?sessionid=F8864B2CDF60AE371CD6DFC189E80C78
SESSION是最新的,但第二個請求,SESSION怎么就是以前的呢?
原來,WEBSTART在經過第一次握手之后,發現本地有該應用,就用該應用JNLP中的href字段發送下面的請求,導致了剛才的問題。
后面的解決辦法說起來就沒什么了,直接去掉href字段就行了,如下片段:
<?xml version="1.0" encoding="utf-8"?>
<jnlp spec="1.0 " codebase=" $ $codebase">
這樣,每次都用新的URL去請求了??!
說來說去,只是過程,其它代碼中改得并不多,主要是增加了SESSION參數。
在以后動態更改應用的MAIN類時,思路也差不多。
其它人需地做的改動:
更改JNLP,去掉href項
換成新的jnlp_servlet
清除當前已安裝的應用。
將鏈接改為有SESSIONID為參數的鏈接。
轉載:文章來自于
http://java.linuxjiaocheng.com/applet-api/sdk-tutorial/xml-jsp-programming2601.html