At line 1 changed 154 lines. |
In my day job, we decided to use a little <a href="http://raibledesigns.com/page/rd?anchor=xmlhttprequest">XMLHttpRequest</a> lovin' |
to populate one drop-down from another. This is my review of <a href="http://oss.metaparadigm.com/jsonrpc/">JSON-RPC</a>, |
an open source JavaScript library and servlet for simplifying XMLHttpRequest. |
I considered integrate <a href="https://dwr.dev.java.net/">Direct Web Remoting</a> (DWR) |
as well, but its java.net site was down the day I needed it. I started out |
with JSON-RPC 0.7, which caused some conflicts with Commons Validator client-side |
validation. This was fixed in the 0.8 release. JSON-RPC takes a little more |
setup than I care for, but it's pretty easy nonetheless:</p> |
<ol> |
<li> Download the 0.8 release from <a href="http://oss.metaparadigm.com/jsonrpc-dist/json-rpc-java-0.8.tar.gz">http://oss.metaparadigm.com/jsonrpc-dist/json-rpc-java-0.8.tar.gz</a>.</li> |
<li>Add the JAR to your project and the webapps/jsonrpc/jsonrpc.js to your |
projects' "scripts" folder. Include this file in your <a href="http://www.opensympony.com/sitemesh">SiteMesh</a> decorator |
or <a href="http://struts.apache.org/userGuide/dev_tiles.html">Tiles</a> layout. If you're not using SiteMesh or Tiles, it's high time |
you started.</li> |
<li>JSON-RPC currently requires that you register each class you want call |
methods on. In our project, I registered a Spring bean (LookupHelper) |
that's a singleton with references to Maps in the ServletContext. Then |
we used JavaScript functions to call JSON-PRC and look up units for |
a plant, and vice versa. I'm not going to put the LookupHelper class |
here - you'll have to trust its methods return a single String |
or a comma-separated list of Strings. To register this bean with JSON-RPC, |
I created an HttpSessionListener and configured it in web.xml. |
|
[{Java2HtmlPlugin |
|
/** |
* UserListener class used to add/remove session attributes when |
* a user first logs in. Mainly for JavaScript Remote Scripting stuff. |
* |
* @author Matt Raible |
*/ |
public class UserListener implements HttpSessionListener, HttpSessionAttributeListener |
{ private final Log log = LogFactory.getLog(UserListener.class); |
public final static String BRIDGE_KEY = "JSONRPCBridge"; |
|
/** |
* Initializes LookupHelper singleton with values needed for lookup |
* |
* @param event the HttpSessionEvent to grab session information from |
*/ |
public void sessionCreated(HttpSessionEvent event) { |
// Find the JSONRPCBridge for this session or create one |
// if it doesn't exist. Note the bridge must be named BRIDGE_KEY |
// in the HttpSession for the JSONRPCServlet to find it. |
HttpSession session = event.getSession(); |
JSONRPCBridge jsonBridge = new JSONRPCBridge(); |
jsonBridge.setDebug(true); |
session.setAttribute(BRIDGE_KEY, jsonBridge); |
} |
|
/** |
* Destroys LookupHelper |
* |
* @param event the HttpSessionEvent to grab session information from |
*/ |
public void sessionDestroyed(HttpSessionEvent event) { |
if (event.getSession() != null) { |
event.getSession().removeAttribute(BRIDGE_KEY); |
} |
} |
|
public void attributeAdded(HttpSessionBindingEvent event) { |
if (event.getName().equals(BRIDGE_KEY)) { |
HttpSession session = event.getSession(); |
// register LookupHelper so we can call methods on it |
ApplicationContext ctx = |
WebApplicationContextUtils |
.getWebApplicationContext(session.getServletContext()); |
|
// check for null so we don't have to initialize Spring in tests |
if (ctx != null) { |
log.debug("Registering lookupHelper for XmlHttpRequest..."); |
JSONRPCBridge jsonBridge = |
(JSONRPCBridge) session.getAttribute(BRIDGE_KEY); |
jsonBridge.registerObject("lookupHelper", |
ctx.getBean("lookupHelper")); |
} |
} |
} |
|
public void attributeRemoved(HttpSessionBindingEvent event) { |
// don't care |
} |
|
public void attributeReplaced(HttpSessionBindingEvent event) { |
// same as attribute added |
attributeAdded(event); |
} |
} |
}] |
|
</li> |
<li>After this setup was complete, I was able to add the following JavaScript |
to the bottom of my JSP. These are functions that our drop-downs call |
to populate each other, and keep their options in synch. |
<pre> |
var jsonurl = "${ctx}/jsonrpc"; |
var jsonrpc = null; |
var unitDropDown = document.getElementById("equipmentName"); |
|
function filterUnits(plantDropDown) { |
var plantName = plantDropDown.options[[plantDropDown.selectedIndex].value; |
|
if (plantName == "") { |
reloadUnits(""); |
return; |
} |
|
try { |
jsonrpc = new JSONRpcClient(jsonurl); |
} catch(e) { |
alert(e); |
} |
|
// Call a Java method on the server |
var units = jsonrpc.lookupHelper.getUnitsForPlant(plantName); |
setUnits(units); |
} |
|
function reloadUnits(value) { |
|
|
if (value == "") { |
try { |
jsonrpc = new JSONRpcClient(jsonurl); |
} catch(e) { |
alert(e); |
} |
|
// Call a Java method on the server |
var units = jsonrpc.lookupHelper.getAllUnits(); |
setUnits(units); |
} |
} |
|
function setUnits(units) { |
var unitArray = units.split(","); |
|
unitDropDown.options.length = 1; // keep "All" option |
for (i=0; i < unitArray.length; i++) { |
unitDropDown.options[[unitDropDown.options.length] = |
new Option(unitArray[[i], unitArray[[i]); |
} |
} |
</pre> |
</li> |
</ol> |
<p>The hardest part of using JSON-RPC is setting it up. We only experienced |
minor issues with Commons Validator, but since the JSON-RPC 0.8 release - |
everything has worked great, on all browsers we need to support. The only |
thing I don't like about this library is that you have to register objects |
for each user's session. I briefly looked at DWR and it looks a little cleaner |
- especially b/c of its Spring integration. The next time we need XMLHttpRequest, |
we'll probably use DRW just to compare the two. |
文件 |