ptg 42 Chapter 3 Your First Handler and Beyond Figure 3.1 My Repository After you have added the zero.resource module as a dependency to your application, you must resolve your application. “Resolve” is an Ivy term that means, “find all of my applica- tion’s dependencies.” In AppBuilder and Eclipse, the resolve step happens automatically when you add the dependency to your application. From the command line, after editing your ivy.xml, you must execute zero resolve from your application’s root directory. The resolve command attempts to find the zero.resource module in your local repository. If it can’t find it, you get an error message and are instructed to run zero update instead. When you run zero update, the command finds the appropriate zero.resource module in the remote repository and downloads it. The application is resolved once the download is complete. When your application is resolved, it can make use of the ZRM libraries. on My Repository (see Figure 3.1). When searching for the remote file, specify “zero” as the organization and “zero.resource” as the module. Download from www.wowebook.com ptg Synchronizing a ZRM Model 43 Virtual Directories An important feature of WebSphere sMash’s dependency system is the concept of virtual directo- ries. When an application starts up, a virtual directory of the application and all of its dependen- cies are created. When WebSphere sMash needs to find a file in the application, it looks in the application’s directory first, followed by the same directory in each of the application’s depend- encies. For example, if an HTML file in the application’s /public directory attempts to load the file library.js from the server, WebSphere sMash’s static file server first looks in the applica- tion’s /public directory. If it can’t find the file there, it looks in the /public directory of all of the dependencies until the file is found. Synchronizing a ZRM Model As part of dependency resolution for the zero.resources module, the WebSphere sMash command line within your application is enhanced with some new features. The new commands added by the zero.resource dependency take this form: zero model <subcommand> You c an e xe cute zero model help within your app’s root directory to see a list of the subcommands (duplicated here for your con- venience): dumpdata Dumps data from the ZRM database into files loaddata Loads data from specified files into the ZRM database reset Restores the ZRM database to the initial state sql Writes the SQL that manages the database arti- facts sync Create database artifacts and load initial data The command that we are interested in at the moment is sync. The sync command synchro- nizes the model with the database. From a WebSphere sMash command-line interface in your application’s directory or from the command prompt in the App Builder, execute the following: zero model sync This command creates database tables from the bookmarks.json model file and popu- lates the tables with data from the initial_data.json file should it exist. By default, the tables are created using an embedded Derby database. Download from www.wowebook.com ptg Event Handling in Groovy We now have a database that reflects our model and contains some interesting data. So, how do we get a REST interface to our data? We have to create a resource handler. We mentioned han- dlers previously. “Handler” is a WebSphere sMash term for a piece of code that responds to events within the application’s lifecycle. A handler that responds to HTTP requests for resources is called a resource handler. Resource handlers, by default, are put into the /app/resources directory. Handlers can be written in Groovy, PHP, or Java. For writing simple handlers, Groovy is hard to beat. Because Groovy is a scripted language with minimal syntactic overhead, you can write very concise han- dlers. In fact, our first handler is going to have a single, short line. In your application’s /app/resources directory, create a file called bookmarks.groovy. In the file, put this line: ZRM.delegate() This line tells ZRM to handle all requests for the “bookmarks” resource, by filename con- vention. We don’t need to declare any imports. The dependency resolution step we performed earlier let Groovy know where to find the ZRM libraries. ZRM.delegate() is set up to handle the five LCRUD requests for our bookmarks resources. LCRUD is CRUD with an additional (L)ist semantic. With this one line, we are now able to process GET, PUT, POST, and DELETE requests for our bookmarks. Let’s try it out. Running the Application You can st art your boo kmar ks app lica tion as des cribe d in the last chapt er. We have n’t d one any - thing to create a client yet, but we can use the browser to interact with some of our new REST APIs. From your browser, browse to the following: http://localhost:8080/resources/bookmarks This URL issues a GET request to the bookmarks resource handler. Because “bookmarks” is a resource collection, it performs a “List” on the bookmarks resources. The results vary by browser, but you will either see the response displayed in the browser or be asked to save the response as a file. The result is a compact JSON-format representation of the bookmarks resources. If you would like a more human-readable result, you can edit your config/zero.config file and add the following line: /config/json/prettyPrint=true Note that there is no need to restart your application. Most configuration changes are detected on the next client request. Simply repeat the request for the bookmarks resource, and you get a response that looks like Listing 3.4. 44 Chapter 3 Your First Handler and Beyond Download from www.wowebook.com ptg Running the Application 45 Listing 3.4 Bookmarks Response [ { "url": "http://www.ibm.com", "name": "IBM", "category": "Software Companies", "id": 100, "updated": "2008-12-18 00:30:29" }, { "url": "http://projectzero.org", "name": "Project Zero", "category": "Development", "id": 101, "updated": "2008-12-18 00:30:29" }, { "url": "http://groovy.codehaus.org", "name": "Groovy", "category": "Development", "id": 102, "updated": "2008-12-18 00:30:29" } ] From the response, we can see that the ID of the first entry is 100. Note that the IDs were automatically created when the initial_data was populated. We can now request individual resources by using the ID in the URL, as follows: http://localhost:8080/resources/bookmarks/100 This URL issues a GET (“Retrieve”) request and returns Listing 3.5. Listing 3.5 Bookmark GET Response { "url": "http://www.ibm.com", "name": "IBM", "category": "Software Companies", "id": 100, "updated": "2008-12-18 00:30:29" } Download from www.wowebook.com ptg Figure 3.2 Posting with Poster If you’d like to try out the REST APIs using the browser, you can use the “Poster” Firefox Add-on (https://addons.mozilla.org/en-US/firefox/addon/2691). To create a new bookmark, you issue a POST using values like those shown in Figure 3.2. 46 Chapter 3 Your First Handler and Beyond To modify a bookmark, you specify the ID of the bookmark in the URL. The Action would be PUT, and the content would include all the nongenerated fields, as shown in Figure 3.3. To delete a resource, specify the bookmark ID in the URL and set the Action to Delete. No parameter body is needed. Explicit Event Handling In the preceding example, we took the “happy path” and let ZRM handle everything for us. Of course, there are situations where we need more control. You may need to handle your own GET, PUT, POST, and DELETE requests. For example, what if your REST interface needs to store the data in a file or a legacy database? In that case, we would need to implement our resource handler, bookmarks.groovy, using a pattern similar to Listing 3.6. Download from www.wowebook.com ptg Figure 3.3 Putting with Poster Explicit Event Handling 47 Listing 3.6 Resource Handler for Explicit Event Handling def onList() { // Retrieve the list of bookmarks from the // data store and put into a java.util.Map def bookmarks = // Create the response request.status = 200 request.json.output = bookmarks request.view = 'JSON' // The render method tells Zero to render // the output using the JSON renderer render() } Download from www.wowebook.com ptg def onCreate() { // Use the WebSphere sMash JSON decoder to // decode the JSON-formatted bookmark // request.input[] is a short-hand reference // to the input data stored with the request // in the Global Context. The Global Context // stores application state. def jsonBookmark = zero.json.Json.decode(request.input[]) // Create the id for the bookmark entry and // add the entry to the data store def bookmarkId = def data = // Set a response header in the request object in the // Global Context request.headers.out.Location = getRequestedUri(false) + '/' + \ bookmarkId // Set the HTTP response code request.status = 201 request.json.output = data request.view = 'JSON' render() } def onRetrieve() { def bookmarkId = request.params.booksmarksId[] // retrieve the bookmark from the data store based on the id def data = // Create the response if (data) { request.status = 200 request.json.output = data request.view = 'JSON' render() } else { // Not found request.status = 404 48 Chapter 3 Your First Handler and Beyond Download from www.wowebook.com ptg Event Handling in PHP 49 } } def onUpdate() { def bookmarkId = request.params.bookmarksId[] // Retrieve the requested bookmark from the // data store based on the Id def data = // Update the data in the data store // Set the response code request.status = 204 } def onDelete() { def bookmarkId = request.params.bookmarksId[] // Delete the entry based on the Id // Set the response code request.status = 204 } The preceding example shows five methods: onList, onCreate, onRetrieve, onUpdate, and onDelete. These five methods define the convention for a resource handler. You don’t need to have your handler inherit from a parent class; you just need to implement these methods. By implementing these methods, you have complete control over how the requests are processed. Event Handling in PHP WebSphere sMash also supports PHP, of course. PHP makes a fine alternative to Groovy for writing resource handlers. For ZRM-based resource handlers, use the dependency zero.resource.php rather than zero.resource: <dependency org="zero" name="zero.resource.php" rev="[1.0.0.0, 3.0.0.0["/> For our resource handler, we create /app/resources/bookmarks.php with the con- tent in Listing 3.7. Download from www.wowebook.com ptg Listing 3.7 PHP ZRM Resource Handler <?php zrm_delegate(); ?> If we want to handle events individually in PHP, we need to create a class with the same name as the resource in our resource handler file (with capitalization). So, for bookmarks.php, we would do something like Listing 3.8. Listing 3.8 Explicit Event Handling in PHP <?php class Bookmarks { function onList() { // Retrieve the list of bookmarks from the data store $bookmarks = // build the response zput('/request/status', 200); zput('/request/view', 'JSON'); zput('/request/json/output', $bookmarks); // The render_view method tells WebSphere sMash to render // the output using the JSON renderer render_view(); } function onCreate() { // Use the WebSphere sMash JSON decoder to // decode the JSON-formatted bookmark $bookmark = json_decode($HTTP_RAW_POST_DATA); // Create the id for the bookmark entry and // add the entry to the data store $bookmarkId = $data = // Set a response header in the request object // in the Global Context $location = get('/request/path') . "/" . $bookmarkId zput('/request/headers/out/Location', $location); zput('/request/headers/out/Content-Type', 'text/json'); echo json_encode($data); 50 Chapter 3 Your First Handler and Beyond Download from www.wowebook.com ptg Event Handling in PHP 51 zput('/request/status', 204); } function onRetrieve() { $bookmarkId = zget("/request/params/bookmarksId"); // retrieve the bookmark from the data store based on the id $data = // If data is found, create the response as JSON if (isset($data)) { zput('/request/headers/out/Content-Type', 'text/json'); echo json_encode($employeeRecord); zput('/request/status', 200); } else { // Page not found zput("/request/status", 404); } } function onUpdate() { $bookmarkId = zget("/request/params/bookmarksId"); $bookmark = json_decode($HTTP_RAW_POST_DATA); // Retrieve the requested bookmark // from the data store based on the Id // Update the data in the data store // Set the response code zput("/request/status", 204); } function onDelete() { $bookmarkId = zget("/request/params/bookmarksId"); // Delete the bookmark from the data store based on the Id // Set the response code zput("/request/status", 204); } } ?> Download from www.wowebook.com . response zput('/request/status', 200); zput('/request/view', 'JSON'); zput('/request/json/output', $bookmarks); // The render_view method tells WebSphere. that means, “find all of my applica- tion’s dependencies.” In AppBuilder and Eclipse, the resolve step happens automatically when you add the dependency to your application. From the command. get('/request/path') . "/" . $bookmarkId zput('/request/headers/out/Location', $location); zput('/request/headers/out/Content-Type', 'text/json');