微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

使用DWR更新Table

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 举报,一经查实,本站将立刻删除。

相关推荐