ptg 242 Chapter 10 Event Processing from the receiverUrl value in the event handler. The next thing to note is that the map may only consist of string values. The kicker does not perform any object morphing and will balk if not presented with all proper strings. Finally, we have the kick method itself. We’ve surrounded it by logger statements including timestamps. The kick method holds until the POST has either completed successfully or fails after the defined number of retries and delay times. Therefore, in theory at least, the kick method could take a fair amount of time to complete. The issue you could run into is that if your timer ticks occur faster than the kick method under failure/retries, you could easily end up with an ever-growing stack of kicks attempting to fire concurrently. As a gen- eral rule of thumb, ensure that your timer tick delay is greater than the number of retries times the max retry delay time. This will keep you out of this sticky mess. Let’s take a mental trip over to the receiving application for a moment, forgetting that it’s actually residing within the same application on this sample. As you can see in the kicker event configuration, we are calling ”/resources/kickReceiver”, which in this sample is another groovy script, shown in Listing 10.8. All we are doing here is dumping out the various parameters we received. Listing 10.8 /resource/kickReceiver.groovy def onCreate() { logger.INFO{"Got kicked by: " + zget("/request/params/appName") } logger.INFO{"Parameters found: " + zlist("/request/params") } zlist("/request/params", false).each{ key -> logger.INFO{">> " + key + ": " + zget("/request/params/${key}") } } } The log output is shown in Listing 10.9. We can see the two expected parameters that we supplied to the kick method, but we also see two extra parameters that were provided that tell us the kicker and the application name that originated the kick. Listing 10.9 Log Output of /app/resources/kickReceiver.groovy 2009-09-01 21:22:18 app.resources.kickReceiver.groovy::onCreate Thread-2 INFO [ Got kicked by: Book.Async.SimpleKicker ] 2009-09-01 21:22:18 app.resources.kickReceiver.groovy::onCreate Thread-2 INFO [ Parameters found: [/request/params/appName, /request/params/arg1, /request/params/kickerName, /request/params/number1] ] 2009-09-01 21:22:18 app.resources.kickReceiver.groovy::onCreate Thread-2 Download from www.wowebook.com ptg File Kicker and Receiver 243 INFO [ appName: Book.Async.SimpleKicker ] 2009-09-01 21:22:18 app.resources.kickReceiver.groovy::onCreate Thread-2 INFO [ arg1: Test message ] 2009-09-01 21:22:18 app.resources.kickReceiver.groovy::onCreate Thread-2 INFO [ kickerName: myKicker ] 2009-09-01 21:22:18 app.resources.kickReceiver.groovy::onCreate Thread-2 INFO [ number1: 234 ] That’s about all there is to the simple kicker. Next, we’ll go over some slightly more advanced kickers that can help us check files and mailboxes and work with message queuing systems. File Kicker and Receiver A file kicker/receiver is built into WebSphere sMash. This built-in kicker/receiver gives your application the opportunity to respond to changes in the file system. The file kicker periodically examines the file system and sends an HTTP POST request when the specified element in the file system has changed. The HTTP POST is received by the built-in receiver, which in turn fires a fileUpdate event and places data about the file system change into the global context. Applica- tion logic to respond to the event should be placed in the fileUpdate event handler. To set up the file kicker, we need to modify the zero.config file to add the file kicker timer handler. In Listing 10.10, we configure the file kicker to watch ”c:/zero/fileKicker/file1” whenever this file is modified; the service defined in the receiverURL will be called. Listing 10.10 File Kicker Configuration /config/handlers += [{ "events" : "timer", "handler" : "zero.file.kicker.FileKicker.class", "conditions" : "/event/_taskName =~ myFileKicker", "instanceData" : { "fileName" : "C:/zero/fileKicker/file1", "receiverURL" : http://localhost:8080/file/FileReceiver } }] Next, we need to configure the file receiver that will handle the HTTP POST sent by the file kicker. We demonstrate this in Listing 10.11. Download from www.wowebook.com ptg 244 Chapter 10 Event Processing Listing 10.11 File Receiver Configuration /config/handlers += [{ "events" : "POST", "handler" : "zero.file.receiver.FileReceiver.class", "conditions" : "/request/path =~ /file/FileReceiver(/.*)?" }] The receiver is configured to handle any HTTP POST event that is sent to the FileReceiver in the file virtual directory. When an HTTP POST is made to this path, the receiver is activated and then fires a fileUpdate event for which we need to configure a handler. To configure a handler for this event, we can do as shown in Listing 10.12. Listing 10.12 File Update Event Handler Configuration /config/handlers += [{ "events" : "fileUpdate", "handler" : "FileUpdateHandler.groovy" }] As you can see, the FileUpdateHandler.groovy is configured to respond to the fileUpdate event. The FileUpdateHandler.groovy file should be placed in our app/scripts directory. In this file, the onFileUpdate method will be called when the event is triggered by the file receiver. In this method, we need to put whatever application logic is needed to appropriately respond to the file being updated. In our case, we’ll just print out the data that is returned from the receiver to the info logger, as seen in Listing 10.13. Listing 10.13 File Update Event Handler def onFileUpdate(){ def kickData = zget("/event/kickData") logger.INFO {"kickData="+kickData} } As you can see, the data from the kicker is received by the receiver and placed in the ”/event/kickData” global context entry. This entry contains a variety of information, such as when the file was updated, the filename that was updated, the name of the kicker, and so on. See the output of our event handler in Listing 10.14. Download from www.wowebook.com ptg Events 245 Listing 10.14 File Update Event Handler Output 2009-10-01 04:43:24 app.scripts.FileUpdateHandler.groovy::onFileUpdate Thread-11 INFO [ kickData=[ fileData:[fileName:C:/zero/fileKicker/file1, msg:file found, lastModified:1254244143625], fileName:C:/zero/fileKicker/file1, appName:fileKicker, kickerName:myFileKicker] ] So, to go over the course of events once again, the file is updated, which activates the file kicker. The file kicker is a timer that watches a particular file or directory. The file kicker calls the file receiver, which converts the POST data from the kicker into a global context entry and then generates a fileUpdate event. This event is handled by our FileUpdateHandler.groovy, which outputs the data into the info log file. As you can see, this enables us to create a great many file-oriented handlers with just a few lines of configuration and whatever code you need to handle the file changes. Events WebSphere sMash has several built-in events for requests. We’ve created handlers for GET and POST requests in this and previous chapters. There are also events that surround every GET, PUT, POST, and DELETE request. Those built-in events let you execute code before a request and after a request, and log when a request has been serviced. To hook into these events, we simply need to configure handlers for the specific events. For demonstration purposes, we’ll cre- ate a handler that handles all three of these events and logs a message. The events are named requestBegin, log, and requestEnd. Listing 10.15 shows how to configure a handler for all of these events. Listing 10.15 Configure Event Handlers for the requestBegin, log, and requestEnd Events /config/handlers += [{ "events" : "requestBegin", "handler" : "LogEvent.groovy", "conditions" : "/request/path =~ /foo(/.*)?" }] /config/handlers += [{ "events" : "log", Download from www.wowebook.com ptg 246 Chapter 10 Event Processing "handler" : "LogEvent.groovy", "conditions" : "/request/path =~ /foo(/.*)?" }] /config/handlers += [{ "events" : "requestEnd", "handler" : "LogEvent.groovy", "conditions" : "/request/path =~ /foo(/.*)?" }] Notice that we’ve defined the same handler for all three events. We can do this because each event will call its own on<EventName> method. Note also that we’re restricting our handlers to firing only when we execute something in the /foo path. You can modify this condition to be any particular path or any path if desired. We create the LogEvent.groovy handler in the app/scripts directory with the code shown in Listing 10.16. Listing 10.16 LogEvent.groovy Handler to Handle Several Events def onRequestBegin(){ logger.INFO {"onRequestBegin"} def tmp = zget("/event/_name") logger.INFO {"/event/_name="+tmp} } def onLog(){ logger.INFO {"onLog"} def tmp = zget("/event/_name") logger.INFO {"/event/_name="+tmp} } def onRequestEnd(){ logger.INFO {"onRequestEnd"} def tmp = zget("/event/_name") logger.INFO {"/event/_name="+tmp} } The code in Listing 10.16 handles all three events with the onRequestBegin, onLog, and onRequestEnd methods. All we’re doing in this demonstration is logging to the info logger, but we do access the event zone of the global context to retrieve the event name. We could also access any of the other event zone properties for the fired event. Just to be complete, the logged output of this code is shown in Listing 10.17. Download from www.wowebook.com ptg Custom Events 247 Listing 10.17 LogEvent.groovy Handler Output 2009-10-01 16:14:41 app.scripts.LogEvent.groovy::onRequestBegin Thread-11 INFO [ onRequestBegin ] 2009-10-01 16:14:41 app.scripts.LogEvent.groovy::onRequestBegin Thread-11 INFO [ /event/_name=requestBegin ] 2009-10-01 16:14:42 app.scripts.LogEvent.groovy::onLog Thread-11 INFO [ onLog ] 2009-10-01 16:14:42 app.scripts.LogEvent.groovy::onLog Thread-11 INFO [ /event/_name=log ] 2009-10-01 16:14:42 app.scripts.LogEvent.groovy::onRequestEnd Thread-11 INFO [ onRequestEnd ] 2009-10-01 16:14:42 app.scripts.LogEvent.groovy::onRequestEnd Thread-11 INFO [ /event/_name=requestEnd ] As we can see, the requestBegin event is fired, followed by the log event and finally the requestEnd event. This all assumes there is some sort of handler behind /foo—for example, an HTTP GET handler. One thing to note is that these events can be configured and handled by mul- tiple handlers. For example, you may have a couple of different log event handlers: One could log accesses to a local log, and another could call a remote logging application to record live statistics about your application. However, the HTTP GET, PUT, POST, and DELETE events may only have one handler. In the next section, we take a look at custom events, which enable you to extend the cast of events to couple together your handlers with events of your choosing. Custom Events WebSphere sMash provides a complete event infrastructure for loosely coupling handlers. As we saw in the “File Kicker and Receiver” section, a fileUpdate event was fired by the file receiver and handled by another handler. This gives advanced developers tremendous flexibility and extensibility of WebSphere sMash by allowing developers to add their own event types and con- figuring multiple handlers to listen for those events. Let’s write a resource that kicks off an event of its own and a couple of handlers to handle the event. To start with, we’ll write an HTTP GET handler named Foo.groovy in Listing 10.18. Listing 10.18 Foo.groovy HTTP GET Event Handler import zero.core.events.EventEngine def onGET() { logger.INFO {"scripts/Foo onGET called"} Download from www.wowebook.com ptg 248 Chapter 10 Event Processing def eventData = ["fooData": "value, could be a map of data"] EventEngine.fire("foo", eventData); } Foo.groovy goes into the scripts directory. We start by importing the EventEngine class, which gives us the ability to fire events. For our new event, we create some event data called fooData that will be the payload we’ll retrieve in our event handlers. The event we’re fir- ing is called ”foo” and is fired by calling the EventEngine.fire method. This is all kicked off by the built-in HTTP GET event. We configure our Foo.groovy handler to be triggered when the /foo path is called, as can be seen in the configuration in Listing 10.19. Listing 10.19 Foo Event Configuration /config/handlers += [{ "events" : "GET", "handler" : "Foo.groovy", "conditions" : "/request/path =~ /foo(/.*)?" }] /config/handlers += [{ "events" : "foo", "handler" : "FooHandler.groovy" }] /config/handlers += [{ "events" : "foo", "handler" : "FooHandlerTwo.groovy" }] When the request path has /foo in it, the Foo.groovy handler is triggered, which in turn fires the foo event. The foo event is then handled by two other handlers that we have configured: FooHandler.groovy and FooHandlerTwo.groovy. When these two handlers are triggered, the onFoo method in each is executed. For the purpose of our example, they both simply log to the info logger, as shown in Listing 10.20. Listing 10.20 Foo Event Handler Code FooHandler.groovy: def onFoo (){ logger.INFO {"onFoo invoked for FooHandler"} def fooData = zget ("/event/fooData") Download from www.wowebook.com ptg References 249 logger.INFO {"fooData="+fooData} } FooHandlerTwo.groovy: def onFoo (){ logger.INFO {"onFoo invoked for FooHandlerTwo"} def fooData = zget ("/event/fooData") logger.INFO {"fooData="+fooData} } Both foo event handlers have access to the payload data and can act on it accordingly. Finally, if we hit the /foo URL of our application, we’ll see what’s listed in Listing 10.21. Listing 10.21 Foo Event Output 2009-10-01 15:39:37 app.scripts.Foo.groovy::onGET Thread-11 INFO [ scripts/Foo onGET called ] 2009-10-01 15:39:37 app.scripts.FooHandler.groovy::onFoo Thread-11 INFO [ onFoo invoked for FooHandler ] 2009-10-01 15:39:38 app.scripts.FooHandler.groovy::onFoo Thread-11 INFO [ fooData=value, could be a map of data ] We see that we handle the HTTP GET event and generate our foo event, which is handled by a couple of different handlers. Don’t forget that you can have at most one handler for GET, PUT, POST, and DELETE events, but custom events and the other built-in events allow multiple handlers to be defined. So, using an HTTP GET handler to generate a custom event would allow you to use multiple handlers for every GET event. Conclusion We covered a variety of ways to communicate and process data asynchronously. The manner in which these events occur is based largely on the situation and reliability requirements of your application. We hope that this chapter has provided you with the tools needed to expand the capabilities of your application through the use of a variety of built-in and custom timers, kickers, and events. References For more information on setting up SSL, refer to the following resources: For IBM Java key management using the Ikeyman tool, refer to http://download.boulder.ibm.com/ibmdl/pub/software/dw/jdk/security/50/GSK7c_SSL_IKM_Guide.pdf. Download from www.wowebook.com ptg 250 Chapter 10 Event Processing For Sun’s keytool utility, refer to http://java.sun.com/j2se/1.5.0/docs/guide/security/jsse/JSSERefGuide. html#CreateKeystore. For another good resource for defining a secure environment using key stores, see the “Using the Java Secure Socket Extension in WebSphere Application Server” article from the IBM WebSphere Developer Technical Journal by Messaoud Benantar at http://www.ibm.com/developerworks/websphere/techjournal/0502_benan- tar/0502_benantar.html. While we are providing references to more information on SSL, see the following articles for IBM at http:/ /www.ibm.com/developerworks/java/jdk/security/50/secguides/jsse2Docs/JSSE2RefGuide.html, and from Sun at http://java.sun.com/j2se/1.5.0/docs/guide/security/jsse/JSSERefGuide.html. Download from www.wowebook.com ptg 251 In this chapter, we introduce some WebSphere sMash components that are useful, if not neces- sary, for developing robust WebSphere sMash applications. We touch on these components only to make you aware that they are there for you to use. We encourage you to seek more information in the product documentation for the components that you find most useful in your application. In particular, we touch on URIUtils (which are used to create URIs that reference your WebSphere sMash application), validators (which assist in validating cached content), and Active Content Filtering (which helps to secure your site against cross-site scripting). Finally, we introduce Assemble Flow, which allows nonprogrammers to create conversation applications in Web- Sphere sMash. URIUtils URIUtils are a set of cross-language utility methods for creating relative and absolute URIs for your application. These URIs can be used to create references to images, cascading style sheets, hyperlinks, and other page resources within your application. Generally, you should use relative URIs so that they are robust enough to survive the rigors of security and client-side manipulation such as reverse proxy servers and mashups. We look at the particulars of each language in turn to reveal any nuisances between them and learn what the API can do to help you with your application development. Java APIs To get access to the URIUtils class, you’ll need to import it: import zero.core.utils.URIUtils; C H A P T E R 11 Framework Components Download from www.wowebook.com . ] 200 9-1 0-0 1 16:14:42 app.scripts.LogEvent.groovy::onLog Thread-11 INFO [ onLog ] 200 9-1 0-0 1 16:14:42 app.scripts.LogEvent.groovy::onLog Thread-11 INFO [ /event/_name=log ] 200 9-1 0-0 1 16:14:42 app.scripts.LogEvent.groovy::onRequestEnd. Extension in WebSphere Application Server” article from the IBM WebSphere Developer Technical Journal by Messaoud Benantar at http://www .ibm. com/developerworks /websphere/ techjournal/0502_benan- tar/0502_benantar.html. While. 15:39:37 app.scripts.Foo.groovy::onGET Thread-11 INFO [ scripts/Foo onGET called ] 200 9-1 0-0 1 15:39:37 app.scripts.FooHandler.groovy::onFoo Thread-11 INFO [ onFoo invoked for FooHandler ] 200 9-1 0-0 1