可以從 JavaScript 呼叫 Java 方法. 也可以從 Java 呼叫 JavaScript 函數 (a.k.a Reverse Ajax). DWR 由兩個部分組成 :
注意: 因為 utils.js 也使用到 $ 當作變數, 所以跟 jQuery/Protoype 有衝突到, 如果同時使用的情況下, 必須將 jQuery/Prototype 的引用放在 utils.js 之後. 而且 DWR 已經不建議繼續使用 $, 改成 byId() 來防止衝突了.
使用 DWR 有兩個方式. 加上一個 callback 或是加上一個 call meta-data object.
Simple Callback Functions
首先假設 Java 的方法是這樣:
public class Remote {
public String getData(int index) { ... }
}
我們在 JavaScript 這樣使用:
<script type="text/javascript" src="[WEBAPP]/dwr/interface/Remote.js"> </script>
<script type="text/javascript" src="[WEBAPP]/dwr/engine.js"> </script>
...
Remote.getData(42, function(str) { alert(str); });
其中的 42 是 Java 方法 getDate() 的參數.
Call Meta-Data Objects
另一種語法是使用 call meta-data object 來指定 callback 以及其他可選參數. 上一個例子可改寫成:
Remote.getData(42, {
callback: function(str) { alert(str); }
});
Timeouts and Handling Errors
除了 callback 之外, 還可以指定 timeout 與 errorHandler
Remote.getData(42, {
callback: function(str) { alert(str); },
timeout: 5000,
errorHandler: function(message) { alert("Oops: " + message); }
});
Finding the callback method
@TODO: 因為還沒看懂, 所以找 callback 的規則還沒寫上.
Creating Javascript objects to match Java objects
假設有個 Java 方法是這樣:
public class Remote {
public void setPerson(Person p) {
this.person = p;
}
}
且 Person 是一個 JavaBean:
public Person {
private String name;
private int age;
private Date[] appointments;
// getters and setters ...
}
你可以從 JavaScript 這樣做:
var p = {
name: "Fred Bloggs",
age: 42,
appointments: [ new Date(), new Date("1 Jan 2008") ]
};
Remote.setPerson(p);
補充: 因為有使用到 JavaBean 所以必須在 dwr.xml 加上一些設定:
<convert match="wxy.fun.dwr.Person" converter="bean"/>
允許從主機端傳送資料到瀏覽器. DWR 提供三種方法推送資料到瀏覽器: Piggyback, Polling and Comet.
Comparison
Polling 實作最簡單, 但是容易使主機超載. 相較之下 Comet 需要大量的 hack, 但是主機負荷會少很多, 且最少的延遲. 這兩個方法都需要額外的網路連線, 而 Piggyback 需要最少的花費, 但是延遲最大.
Active and Passive Reverse Ajax
使用 DWR 可以經由設定在這三種方法之中做挑選. 使用 DWR 可以經由設定在這三種方法之中做挑選. 當需要快速的回復與有足夠的負荷能力時可以選用 Comet/Polling. 這種模式稱為主動 Reverse Ajax. DWR 預設是關閉主動模式, 使用 Piggyback 機制.
使用被動模式的 Reverse Ajax (piggyback) 是不需要設定的. 主動模式則需要 2 個設定步驟.
第一步 - 主機設定允許使用主動模式, 在 WEB-INF/web.xml 加上:
<servlet>
<servlet-name>dwr-invoker</servlet-name>
<servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class>
<init-param>
<param-name>activeReverseAjaxEnabled</param-name>
<param-value>true</param-value>
</init-param>
...
</servlet>
第二步 - 瀏覽器提出請求. 這是 Comet/Polling 迴圈的開始. 只需要在網頁的 load 事件加上:
dwr.engine.setActiveReverseAjax(true);
經過了以上兩個步驟之後, Reverse Ajax 的設定就已經完成了. 看到這邊一定還有疑惑, 那要怎樣實作呢? 這時候只要稍微再了解一下 org.directwebremoting.proxy.dwr.Util 的用法, 就可以順利完成 Reverse Ajax. 例如:
public void sendMessage(String msg) {
// get sessions.
WebContext wc = WebContextFactory.get();
String page = wc.getCurrentPage();
Collection<?> sessions = wc.getScriptSessionsByPage(page);
// refresh all browser
Util u = new Util(sessions);
u.addFunctionCall("refreshChatRoom", msg);
}
上面這個聊天室, 當其中一個使用者送出訊息的時候, 依照條件(同在這個畫面上的人) 找出 sessions, 然後利用 Util 來執行 function call, 另外像是股價更新的情況則是另外再繼承 Thread 來達到目的.
Reverse Ajax 分成兩種模式, 主動與被動. 而主動模式還分成三種模式:
Full Streaming Mode
這模式特性是擁有最快的反應速度, 因為他會每 60 秒關一次連線, 並重新檢查瀏覽器是否處於活動狀態.
當大量的連線開啟時, 有可能造成伺服器執行緒短缺. 各種網頁伺服器都有特別的方式去中止執行緒. DWR 2.0 版的時候支援 Jetty, 預計在 3.0 版的時候支援 GlassFish 與 Tomcat. DWR 同時也試著在主機負荷過重的時候適當減少連線時間, 增加離線時間.
Full Streaming 需要 HTTP chunked mode. 所以在某些 network proxies, mod_jk, network scanners 會失效. 假如使用 Full Streaming 但是訊息卻是每 60 秒更新一次, 可能就是這個原因.
2.0.4 版之後如果要啟用 Full Streaming Mode, 加上這個 init-param:
<init-param>
<param-name>maxWaitAfterWrite</param-name>
<param-value>-1</param-value>
</init-param>
Early Closing Mode
這模式處理連線是跟 Full Streaming Mode 用一樣的方式, however it only holds the connection open for 60 seconds if there is no output to the browser. Once output occurs, DWR pauses for some configurable time before closing the connection, forcing proxies to pass any messages on. 在 2.0.3 版之前如果要使用這個模式, 需要一個 init-param, 指定第一次輸出與連線結束之間的延遲時間.
<init-param>
<param-name>maxWaitAfterWrite</param-name>
<param-value>500</param-value>
</init-param>
In this case DWR will keep the connection open for an additional 500 milliseconds after the first output in case there is additional output, before forcing a flush by closing the connection and requesting it to be re-opened.
The downside of Early Closing Mode is that for applications with a high rate of output, it can cause a large hit count. This can be countered by increasing the pause by setting maxWaitAfterWrite=1000 or similar.
In applications with a large number of connected browsers that are simultaneously sent a message they are likely to all try to reconnect at the same time. If you are affected by this type of problem, then you should consider investigating Full Streaming Mode.
Polling Mode
如果覺得保持連線是不明智的行為可以使用 Polling mode. 除了設定 activeReverseAjaxEnabled=true 之外, 還要加上:
<init-param>
<param-name>org.directwebremoting.extend.ServerLoadMonitor</param-name>
<param-value>org.directwebremoting.impl.PollingServerLoadMonitor</param-value>
</init-param>
Polling Mode 預設是每 5 秒鐘執行一次. 如果需要修改:
<init-param>
<param-name>disconnectedTime</param-name>
<param-value>60000</param-value>
</init-param>
這樣可以改成 60 秒(60,000 ms)執行一次.
Passive Mode
Passive Mode 是預設模式, 不需要上面的這些修改. 也叫做 Piggyback. 這是最慢的回覆速度, 但是也不需要額外的連線.
// code block
@TODO: config dwr.xml 的格式
@TODO: util.js 用法
@TODO: Fluent Configuration 寫法
@TODO: 同步與異步 RPC 的做法. 其實就是 engine.js 的一小部分 可以參考這邊 http://directwebremoting.org/dwr/browser/engine/options