| At line 1 removed 23 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. |
|
| At line 26 changed 64 lines. |
| /** |
| * 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); |
| } |
| } |
| class MyTagLib{ |
| def isAdmin= { attrs, body-> |
| def user = attrs["user"] |
| if (user!= null && checkUserPrivs(user)) |
| body() |
| } |
| } |
| At line 91 removed 63 lines. |
|
| </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. |