| 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 127 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); | 
|     } | 
| } | 
| ]} | 
 | 
| 		</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.  | 
| class MyTagLib{  | 
|     def isAdmin= { attrs, body->  | 
|         def user = attrs["user"]  | 
|         if (user!= null && checkUserPrivs(user))  | 
|             body()  | 
|     }  | 
| }  | 
| }] |