1.简介
DWR是Direct Web Remoting的简写,它是一套RPC库,使服务器端的Java和浏览器端的Javascript能够方便地互相调用。官网地址:http://directwebremoting.org/dwr/index.html
DWR能够生成Javascript,使浏览器能够像调用本地API一样调用服务器端的Java API。它能够序列化任何数据类型,如Collections,POJOs,XML和二进制数据,例如图像和PDF文件。
使用Reverse AJAX,DWR能够让Java调用客户端API来更新任意页面。DWR支持Comet,Polling和Piggyback三种方式来推送内容到浏览器。
下图展示了DWR如何基于javascript事件更新页面的下拉列表
下图展示利用Reverse Ajax,服务器端能够监控不同客户端在打开哪些页面,将手工或者使用Java API生成的javascript发送给它们。
官网上能够下载用于演示的war包,里面有一些常见的功能演示。本文仅从学习的角度,自己从头搭建使用DWR动态更新Table的环境,完成后页面如图
2.环境搭建
环境介绍
windows 7,使用XAMPP中的Tomcat服务器,开发环境是eclipse
1).使用eclipse建立一个Dynamic Web Project。
2).下载相关jar包,包括dwr.jar、commons-logging-1.0.4.jar、log4j-1.2.12.jar, dwr.jar对commons-logging有依赖。下载完后放到WEB-INF\lib目录,修改build path包含上述jar包。
3).编辑web.xml和dwr.xml,文件位于WEB-INF\目录下
web.xml
<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems,Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app id="dwr"> <display-name>DWR (Direct Web Remoting)</display-name> <description>A Simple Demo DWR</description> <listener> <listener-class>org.directwebremoting.servlet.DwrListener</listener-class> </listener> <servlet> <servlet-name>dwr-invoker</servlet-name> <display-name>DWR Servlet</display-name> <description>Direct Web Remoter Servlet</description> <servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class> <!-- This should NEVER be present in live --> <init-param> <param-name>debug</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>accessLogLevel</param-name> <param-value>CALL</param-value> </init-param> <!-- Remove this unless you want to use active reverse ajax --> <init-param> <param-name>activeReverseAjaxEnabled</param-name> <param-value>true</param-value> </init-param> <!-- By default DWR creates application scope objects when they are first used. This creates them when the app-server is started --> <init-param> <param-name>initApplicationScopeCreatorsAtStartup</param-name> <param-value>true</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dwr-invoker</servlet-name> <url-pattern>/dwr/*</url-pattern> </servlet-mapping> </web-app>dwr.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 3.0//EN" "http://getahead.org/dwr/dwr30.dtd"> <dwr> <allow> <create creator="new" scope="application"> <param name="class" value="com.example.dwr.reverseajax.PeopleTable"/> </create> <convert match="com.example.dwr.people.Person" converter="bean"/> <!-- resources not in this war file: java.util.Date --> <create creator="new" javascript="JDate"> <param name="class" value="java.util.Date"/> <exclude method="getHours"/> <auth method="getMinutes" role="admin"/> <auth method="getMinutes" role="devel"/> <filter class="org.directwebremoting.filter.ExtraLatencyAjaxFilter"/> </create> <!-- this is a bad idea for live,but can be useful in testing --> <convert converter="exception" match="java.lang.Exception"/> <convert converter="bean" match="java.lang.StackTraceElement"/> </allow> </dwr>web.xml里面定义了DWR的servlet,包含一些初始化参数,这些参数不是必须的,有的仅仅是为了调试方便,比如debug和accessLogLevel。
dwr.xml可以看出,它的主要作用就是定义java类和javascript对象的映射关系。
4).编辑Java Code,实际上就是实现了Runnable接口,每10秒钟随机生成一个Person记录,推送到前端
PeopleTable.java
package com.example.dwr.reverseajax; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import org.directwebremoting.browser; import org.directwebremoting.ScriptSession; import org.directwebremoting.ScriptSessionFilter; import org.directwebremoting.ServerContextFactory; import org.directwebremoting.WebContextFactory; import org.directwebremoting.impl.DaemonThreadFactory; import org.directwebremoting.ui.dwr.Util; import org.directwebremoting.util.Logger; import com.example.dwr.people.Person; public class PeopleTable implements Runnable { Logger log = Logger.getLogger(this.getClass()); /** * Constructor - Creates a thread pool that runs every 10 seconds. */ public PeopleTable() { ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor( 1,new DaemonThreadFactory()); executor.scheduleAtFixedrate(this,1,10,TimeUnit.SECONDS); } /* * (non-Javadoc) * * @see java.lang.Runnable#run() */ @Override public void run() { updateTabledisplay(); } public void updateTabledisplay() { log.error("enter updateTabledisplay"); // Get the current page. String page = ServerContextFactory.get().getcontextpath() + "/index.html"; // Create a new AttributeScriptSessionFilter which will look for an // attribute on the ScriptSession ScriptSessionFilter attributeFilter = new AttributeScriptSessionFilter( SCRIPT_SESSION_ATTR); // Update the page,filters ScriptSessions using attributeFilter. If the // SCRIPT_SESSION_ATTR // has not been set on the ScriptSession the page in question will not // receive updates. browser.withPageFiltered(page,attributeFilter,new Runnable() { @Override public void run() { // Creates a new Person bean. Person person = new Person(true); // Creates a multi-dimensional array,containing a row and the // rows column data. String[][] data = { { person.getId(),person.getName(),person.getAddress(),person.getAge() + "",person.isSuperhero() + "" } }; // Call DWR's util which adds rows into a table. peopleTable is // the id of the tbody and // data contains the row/column data. Util.addRows("peopleTable",data); } }); } /** * Called from the client to add an attribute on the ScriptSession. This * attribute will be used so that only pages (ScriptSessions) that have set * this attribute will be updated. */ public void addAttributetoScriptSession() { ScriptSession scriptSession = WebContextFactory.get() .getScriptSession(); scriptSession.setAttribute(SCRIPT_SESSION_ATTR,true); } /** * Called from the client to remove an attribute from the ScriptSession. * When called from a client that client will no longer receive updates * (unless addAttributetoScriptSession) is called again. */ public void removeAttributetoScriptSession() { ScriptSession scriptSession = WebContextFactory.get() .getScriptSession(); scriptSession.removeAttribute(SCRIPT_SESSION_ATTR); } /** * This is the ScriptSessionFilter that will be used to filter out all * ScriptSessions unless they contain the SCRIPT_SESSION_ATTR attribute. */ protected class AttributeScriptSessionFilter implements ScriptSessionFilter { public AttributeScriptSessionFilter(String attributeName) { this.attributeName = attributeName; } /* * (non-Javadoc) * * @see * org.directwebremoting.ScriptSessionFilter#match(org.directwebremoting * .ScriptSession) */ @Override public boolean match(ScriptSession session) { Object check = session.getAttribute(attributeName); return (check != null && check.equals(Boolean.TRUE)); } private final String attributeName; } private final static String SCRIPT_SESSION_ATTR = "SCRIPT_SESSION_ATTR"; }Person.java
package com.example.dwr.people; import java.util.Random; import org.directwebremoting.datasync.ExposetoString; @ExposetoString public class Person { private String id; private String name; private String address; private int age; private boolean superhero; private static int nextId = 1; private static final Random random = new Random(); public Person() { this.id = getNextId(); } public Person(boolean withRandom) { if (withRandom) { this.name = RandomData.getFullName(); this.address = RandomData.getAddress(); this.age = RandomData.getAge(); this.superhero = (random.nextInt(100) == 1); } this.id = getNextId(); } public String getId() { return this.id; } public void setId(String id) { this.id = id; } public String getName() { return this.name; } public void setName(String name) { this.name = name; } public String getAddress() { return this.address; } public void setAddress(String address) { this.address = address; } public int getAge() { return this.age; } public void setAge(int age) { this.age = age; } public boolean isSuperhero() { return this.superhero; } public void setSuperhero(boolean superhero) { this.superhero = superhero; } public String toString() { return this.name; } public static synchronized String getNextId() { return "P" + nextId++; } }RandomData.java
package com.example.dwr.people; import java.util.Random; public class RandomData { private static final Random random = new Random(); private static final String[] FirsTNAMES = { "Fred","Jim","Shiela","Jack","Betty","Jacob","Martha","Kelly","Luke","Matt","Gemma","Joe","Ben","Jessie","Leanne","Becky","William","Jo","Jane","Joan","Jerry","Jason","Martin","Mark","Max","Mike","Molly","Sam","Shane","Dwane","Diane","Anne","Anna","Bill","Thomas","Oliver","Joshua","Harry","Charlie","Dan","Will","James","Alfie","Grace","Ruby","Olivia","Emily","Jessica","Sophie","Chloe","Lily","Ella","Amelia","Kimberly","Owen","Rhys","Layla","Jonny","Darren","Laura","Bridget","Carl","Josie" }; private static final String[] SURNAMES = { "Sutcliffe","MacDonald","Duckworth","Smith","Wisner","Jones","Nield","Turton","Trelfer","Wilson","Johnson","Daniels","Wilkinson","Wilton","Jackson" }; private static final String[] ROADS1 = { "Amaranth","Apricot","Aqua","Aquamarine","Beige","bronze","Buff","Burgundy","Cerise","Chestnut","Cobalt","Coral","Cream","Cyan","Denim","eggplant","Fuchsia","Grey","Gold","Indigo","Ivory","Jade","Khaki","Lemon","Lilac","Linen","magenta","magnolia","Maroon","Mustard","Ochre","Olive","Orange","Orchid","Peach","Pear","Pink","Scarlet","Silver","Sepia","Tangerine","Taupe","Tan","teal","Torquise","Ultramarine","Violet","Wheat","Green","Red","Yellow","brown","Blue","Black","White","Yellow" }; private static final String[] ROADS2 = { "Close","Drive","Street","Avenue","Crescent","Road","Place","Way","Croft","Lane" }; private static final String[] TOWNS = { "San Mateo","San Francisco","San Diego","New York","Atlanta","Sandford","York","London","Coventry","Exeter","KNowle","Rhyl","Stamford" }; public static String getPhoneNumber(boolean isUS) { String phoneNumber; if (isUS) { phoneNumber = "+1 (" + random.nextInt(9) + random.nextInt(9) + random.nextInt(9) + ") " + random.nextInt(9) + random.nextInt(9) + random.nextInt(9) + " - " + random.nextInt(9) + random.nextInt(9) + random.nextInt(9) + random.nextInt(9); } else { phoneNumber = "+44 (0) 1" + random.nextInt(9) + random.nextInt(9) + random.nextInt(9) + " " + random.nextInt(9) + random.nextInt(9) + random.nextInt(9) + random.nextInt(9) + random.nextInt(9) + random.nextInt(9); } return phoneNumber; } public static String getFirstName() { return FirsTNAMES[random.nextInt(FirsTNAMES.length)]; } public static String getSurname() { return SURNAMES[random.nextInt(SURNAMES.length)]; } public static String getFullName() { return getFirstName() + " " + getSurname(); } public static String getAddress() { String housenum = random.nextInt(399) + 1 + " "; String road1 = ROADS1[random.nextInt(ROADS1.length)]; String road2 = ROADS2[random.nextInt(ROADS2.length)]; int townNum = random.nextInt(TOWNS.length); String town = TOWNS[townNum]; return housenum + road1 + " " + road2 + "," + town; } public static String[] getAddressAndNumber() { String[] reply = new String[2]; String housenum = random.nextInt(399) + 1 + " "; String road1 = ROADS1[random.nextInt(ROADS1.length)]; String road2 = ROADS2[random.nextInt(ROADS2.length)]; int townNum = random.nextInt(TOWNS.length); String town = TOWNS[townNum]; reply[0] = (housenum + road1 + " " + road2 + "," + town); reply[1] = getPhoneNumber(townNum < 5 ? true : false); return reply; } public static int getAge() { return random.nextInt(80); } public static float getSalary() { return Math.round(10.0F + 90.0F * random.nextFloat()) * 1000; } }
注意:需要修改eclipse的default output folder为:dwrtest/WEB-INF/classes。
5).到这里,应该可以访问DWR的测试页面了(必须在web.xml里面配置了debug才能访问测试页面)。在浏览器中输入 http://localhost:8080/dwrtest/dwr/index.html如果一切OK的话,应该显示如下图
这些就是在Javascript端能够调用的Java API,点击PeopleTable进入
这个页面告诉我们,如果要使用提供的API,需要在web页面中包含前两个js文件,这两个URL相当于DWR提供的服务,本地并没有对应的文件。
6).完成html和javascript代码。
我们只有一个页面, 新建html文件命名为index.html并放到工程的根目录下
index.html
<!DOCTYPE html> <html> <head> <title>Reverse Ajax Table Update</title> <Meta http-equiv="Content-Type" content="text/html; charset=us-ascii" /> <script type='text/javascript' src='../dwrtest/dwr/engine.js'> </script> <script type='text/javascript' src='../dwrtest/dwr/util.js'> </script> <script type='text/javascript' src='../dwrtest/dwr/interface/PeopleTable.js'> </script> <script type='text/javascript' src='../dwrtest/js/onload.js'> </script> <link rel="stylesheet" type="text/css" href="../dwrtest/generic.css" /> </head> <body> <h1>Reverse Ajax Table Update</h1> <div id="tabContents"> <div id="demoDiv"> <div id="error"></div> <input type="button" id="enable" value="Enable page updates" onclick="addAttributetoScriptSession();" /> <input type="button" id="disable" value="disable page updates" onclick="removeAttributetoScriptSession();" /> <p> Server status: <span id="pollStatus"></span> </p> <table> <thead> <th>Id</th> <th>Name</th> <th>Address</th> <th>Age</th> <th>Is Superhero?</th> </thead> <tbody id="peopleTable"></tbody> </table> </div> </div> </body> </html>onload.js
window.onload=function() { dwr.engine.setActiveReverseAjax(true); // Initiate reverse ajax polling dwr.engine.setErrorHandler(errorHandler); // Called when a call and all retry attempts fail dwr.engine.setPollStatusHandler(updatePollStatus); // Optional function to call when the reverse ajax status changes (e.g. online to offline) updatePollStatus(true); // Optional - We are online right Now! Until DWR determines we are not! dwr.engine.setNotifyServerOnPageUnload(true); // Optional - When the page is unloaded,remove this ScriptSession. PeopleTable.updateTabledisplay(); // Make a call to the server to begin updating the table! addAttributetoScriptSession(); // Make a remote call to the server to add an attribute onto the ScriptSession which will be used in determining what pages receive updates! } function errorHandler(message,ex) { dwr.util.setValue("error","Cannot connect to server. Initializing retry logic.",{escapeHtml:false}); setTimeout(function() { dwr.util.setValue("error",""); },5000) } function updatePollStatus(pollStatus) { dwr.util.setValue("pollStatus",pollStatus ? "Online" : "Offline",{escapeHtml:false}); } // Make a remote call to add an attribute on the ScriptSession. // Only clients that have this attribute set will receive updates. function addAttributetoScriptSession() { PeopleTable.addAttributetoScriptSession(); } // Make a remote call to remove an attribute from the ScriptSession. // Clients that call this will no longer receive updates (unless addAttributetoScriptSession is called again). function removeAttributetoScriptSession() { PeopleTable.removeAttributetoScriptSession(); }7)访问 http://localhost:8080/dwrtest/,大功告成
3. 配置log4j
如果是生产环境,通常需要配置log4J。
新建log4j.xml并放到WEB-INF\classes\目录下,内容如下:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE log4j:configuration SYstem "log4j.dtd"> <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"> <appender name="console" class="org.apache.log4j.ConsoleAppender"> <param name="Target" value="System.out"/> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="%-5p %c{1} - %m%n"/> </layout> </appender> <appender name="dwrLogFile" class="org.apache.log4j.FileAppender"> <param name="File" value="d:/tools/xampp/tomcat/webapps/dwrtest/log/dwrAccess.log"/> <param name="Append" value="true"/> <param name="Threshold" value="DEBUG"/> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="%d %-5p [%c] %m%n"/> </layout> </appender> <appender name="otherFile" class="org.apache.log4j.FileAppender"> <param name="File" value="d:/tools/xampp/tomcat/webapps/dwrtest/log/other.log"/> <param name="Append" value="true"/> <param name="Threshold" value="DEBUG"/> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="%d %-5p [%c] %m%n"/> </layout> </appender> <!-- All application exceptions/errors will be written here --> <category name="org.directwebremoting.log.accessLog"> <priority value="INFO"/> <appender-ref ref="dwrLogFile" /> </category> <!-- All DWR startup information will be written here --> <category name="org.directwebremoting.log.startup"> <priority value="DEBUG"/> <appender-ref ref="dwrLogFile" /> </category> <!-- All DWR script information will be written here --> <category name="org.directwebremoting.log.scripts"> <priority value="DEBUG"/> <appender-ref ref="dwrLogFile" /> </category> <!-- All DWR session information will be written here --> <category name="org.directwebremoting.log.session"> <priority value="DEBUG"/> <appender-ref ref="dwrLogFile" /> </category> <!-- All other messages will be written here,including exceptions internal to DWR --> <root> <priority value="DEBUG" /> <appender-ref ref="otherFile" /> </root> </log4j:configuration>
附:eclipse工程截图
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。