A Look at Event Handlers - ColdBox Series Part 3

In my last post, we set up a base ColdBox application using the Application Template. So now we have a basic application that welcomes us to ColdBox. Not very useful.

So let's look at our Event Handlers and see if we can get them to do something more interesting.

What is an Event?

Since ColdBox is an Event-Driven framework, and since we are going to be "handling" events with our event handlers (clever name), we probably need to understand what an event is.

As I understand it, an event is an Object that is dispatched by the controller (ColdBox) and passed to the event handler when a request for the event is made. In ColdBox, the event object itself is of type coldbox.system.beans.requestContext and comes with a lot of built it methods and properties, plus it holds the request collection which:

...is a central repository of information that is refreshed on every user request with the request's information. This is how data gets moved around from event handlers to views and layouts to plugins and anything running inside of the framework and in the MVC layers.

- From the ColdBox Website



So an end user makes a request for an event like this:


http://todolists:81/index.cfm?event=lists.getAllLists

The controller (ColdBox), then receives this request and create a event object (requestContext) and dispatches it to the event handler. The event handler in this case is the method getAllLists() inside of lists.cfc which is inside of the /handlers folder. In other words, it calls the getAllLists() method and passes in the event object: getAllLists(event)

The event handler, then receives the event object and can add or remove data using the built in event methods, like getCollection(), getValue() and setValue(). The event handler can then dispatch another event, or call a view for display (passing along the event object for both).

One important thing to note, any POST or GET requests sent to ColdBox are added to the Request Collection inside of the event object. So in our event handlers, we will never be accessing the FORM or URL variables directly. Their values are automatically placed in the Event Object's Request Collection.

For more on events, event handling and the RequestContext object, check here:

Event Handlers

So now to look at the event handlers in our Application Template. By default, two event handler CFCs are in our /handlers directory. general.cfc and main.cfc. Main.cfc contains event handling methods for the implicit events that occur during the applications lifecycle. These are onApplicationStart(), onRequestStart(), etc. I personally, have only needed to change these to add Transfer to an application I was working on. Perhaps some of our expert ColdBox users would leave some comments about why else we might change anything in here, but for right now, since we are not using Transfer, I am just going to leave this file alone.

Which brings us to general.cfc. General.cfc is a regular old ColdBox event handler, and we do not "have" to use it. Personally, I don't like the name general, but that is a matter of preference. To keep things simple, we will leave the name the way it is.

Here is the default code for general.cfc


<cfcomponent name="general" extends="coldbox.system.eventhandler" output="false">
    
    <!--- This init is mandatory, including the super.init(). --->
    <cffunction name="init" access="public" returntype="general" output="false">
        <cfargument name="controller" type="any">
        <cfset super.init(arguments.controller)>
        <!--- Any constructor code here --->
        <cfreturn this>
    </cffunction>

    <cffunction name="index" access="public" returntype="void" output="false">
        <cfargument name="Event" type="any">
        <!--- Do Your Logic Here to prepare a view --->
        <cfset Event.setValue("welcomeMessage","Welcome to ColdBox!")>    
        <!--- Set the View To Display, after Logic --->
        <cfset Event.setView("home")>
    </cffunction>
    
    <cffunction name="doSomething" access="public" returntype="void" output="false">
        <cfargument name="Event" type="any">
        <!--- Do Your Logic Here, call to models, etc.--->

        <!--- Set the next event to run, after Logic, this relocates the browser--->
        <cfset setNextEvent("general.index")>
    </cffunction>
    
</cfcomponent>

So here we have two event handler methods, index() and doSomething(). All doSomething() does is call index() using the setNextEvent() method, so let's delete it right now.

Now, let's look at the index() handler. Since the index() method is inside of the general.cfc file, we would request the event like this:


http://todolists:81/index.cfm?event=general.index

So when the request is made, ColdBox passes the event object (request context) to the event handler method, which accepts it as an argument.

Then it adds a value called "welcomeMessage" to the event object with this line:


<cfset Event.setValue("welcomeMessage","Welcome to ColdBox!")>    

Then it calls for a view (which we will look at in just a moment) named "home":


<cfset Event.setView("home")>

This will show the view to the end user. We can look at the view by looking in the /views directory that was part of the Application Template. It is at /views/home.cfm

Here is what the home.cfm looks like:


<cfoutput>
<h1>#Event.getValue("welcomeMessage")#</h1>
<h5>You are running #getSetting("codename",1)# #getSetting("version",1)# (#getsetting("suffix",1)#)</h5>
</cfoutput>

In the <h1> tag we get the welcome message that we created in the event handler by calling event.getValue("welcomeMessage"). Then the view calls the getSetting() method to get some settings out of the coldbox.xml file, which we will look at in my next post.

Pretty simple, huh?

Views

Wait, wait, wait, wait, wait. THAT's our view? An <h1> and an <h5>? Where's the <html>? Where's the <head>? Where's the <body>? That's not a very well-formed document, is it?

Well, let's go to the page in our browser and see what is actually getting rendered. There is a lot of debugging crap in there, but here is the important part:


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
<title>Welcome to Coldbox!!</title>
<script language="javascript" src="/index.cfm?sbContent=js" type="text/javascript"></script><link rel="stylesheet" href="/index.cfm?sbContent=css" type="text/css" media="screen"></head>
<body>

<h1>Welcome to ColdBox!</h1>
<h5>You are running ColdBox 2.6.1 (FAITH)</h5>

</body>
</html>

Looks like valid XHTML to me. But where did all that other code come from? It came from our default layout. Which you will find in the /layouts directory. It is called Layout.Main.cfm. We will discuss layouts more in another post, I just wanted you to understand where that code is coming from.

Modifying our Event Handler

So let's modify our index() event handler method and our home.cfm view to show us something other than a simple welcome message.

Our index() handler method will change to:


<cffunction name="index" access="public" returntype="void" output="false">
    <cfargument name="Event" type="any">
    
    <!--- Do Your Logic Here to prepare a view --->
    <cfset Event.setValue("pageTitle","My To-Do List")>    
    
    <cfset Event.setValue("today", Now())>
    
    <!--- Set the View To Display, after Logic --->
    <cfset Event.setView("home")>
</cffunction>

And our view will change to:


<cfoutput>
    <h1>#Event.getValue("pageTitle")#</h1>
    <h5>Today is #DateFormat(Event.getValue("today"), 'mmmm d, yyyy')#</h5>
</cfoutput>

I have deleted the "welcomeMessage" value and created a "pageTitle" value instead, I have also added a "today" value and inserted a value using the Now() function.

In our view I have replaced the getValue() call for the "welcomeMessage" value and asked for the "pageTitle" value instead. And I have replace all of those getSetting() calls with a getValue() call for the "today" value and formatted it with the DateFormat() function.

Conclusion

This has been a very superficial look at event handlers and views. We will be looking at them in much more detail and we will be doing some more advanced things with them in the near future. In my next post I will probably discuss the ColdBox.xml file or "Convention over Configuration", we'll see.

Comments
Ernst van der Linden's Gravatar Great stuff Jason! Keep up the good work!
# Posted By Ernst van der Linden | 10/2/08 4:57 AM
Ben Nadel's Gravatar Jason, nice walk through.
# Posted By Ben Nadel | 10/3/08 7:25 AM
Jason Dean's Gravatar @ernst & @ben - Thanks. I really hope people are getting something out of these. I will continue posting, there is a lot more to cover. I really want to hear what people think of these posts.
# Posted By Jason Dean | 10/3/08 8:11 AM
Ben Nadel's Gravatar I know for me, personally, as I am fooling around with OOP and MVC theories, things like this are really helpful to understand how people see and handle the page request event. It's all helping to make my overall journey clearer.
# Posted By Ben Nadel | 10/3/08 8:13 AM
Chris Martinez's Gravatar This is great stuff!! I'm trying to get going with ColdBox myself, thanks for sharing your knowledge. Keep it up!!
# Posted By Chris Martinez | 10/4/08 5:37 AM
Doug's Gravatar Good info. Please don't stop now, like so many other tutorials I've looked at for Fusebox.
# Posted By Doug | 10/6/08 4:33 PM
Jason Dean's Gravatar Thanks for the comments everyone. I love the feedback, and it motivates me to do more.

@Doug & @Chris - I have no intention of stopping. I enjoy this too much and I think I get just as much out of it as you do. I love teaching and will take any chance I can get to do more of it.
# Posted By Jason Dean | 10/6/08 10:37 PM
Christine Panus's Gravatar hey Jason, I'm really appreciating this series. I'm working on evaluating the design patters we will want to use at my company and this series is really helpful. We're going to use ColdBox and I've been looking at a design pattern for the model that is similar to yours... I'm planning on having both beans and DAOs, I'll be using Peter Bells IBO pattern with my beans... but then I get to the service, which I had originally setup in the UML as part of my model... but the more I look at it, the more I think it can actually be served by the handlers. Am I missing something, or is the ColdBox handler able to act as the Service API for the model?
What do you think?

Thanks,
Christine
# Posted By Christine Panus | 10/22/08 11:55 PM
Jason Dean's Gravatar Christine, Thanks for commenting. I'm glad to hear more and more about people learning ColdBox.

So your question about service layers is a good one. I asked the same question myself, and in one of my first apps, even though I did use a service layer, it was really a dumb one because I was doing most of my processing at the controller anyway.

So technically, yes, it is possible to have the controller be the service layer. The next question is, "should you?".

I would say no. I still feel the service layer is a very important piece in the OO puzzle for separating the model from the controller. By having your controller talk directly to your model you are tightly coupling those to pieces which breaks some of the modular aspects that OO offers.

These are just my opinions, and I am sure that there are some advocates out there that will say "if Ya Ain't Gonna Need It..." (YAGNI). But I personally like the service layer and I would get rid of the beans and DAO's (replaced with an ORM) before I would get rid of the service layer.

That said, whether or not you add service objects, ColdBox can still be used as a service layer for remote applications or AJAX apps by using the ColdBox Proxy. It's very cool and worth looking into. But I would still have service objects in my app.
# Posted By Jason Dean | 10/23/08 7:57 AM
Christine Panus's Gravatar Jason, Using handlers as my Serice layer will indeed tightly couple my model with Coldbox... but it seems that if I'm using Lightwire and/or Coldspring for my DI and use the framework's integration for those DI handlers it seems that I'd have to have my handlers act as the service layer. If I'm also going to use the unit testing support, that seems to almost make the tight coupling between the controller and the model a given... perhaps that is not true... but it seems that way right now... I think part of my issue here stems from the fact that I was already planning to use ColdBoxProxy which REALLY led me to think that I should just use my handlers for the service API based on the sample apps I was reviewing.

The ability to use ORM would be based on the assumption of a solid Relational Data Structure. At this point, given the data structure I am working with, I think I'd rather be tightly coupled with my controller than my database... which is why I'm adding the "extra" layer for the data model to the business model. In the interest of balancing real world needs and optimal OO design, I think YAGNI is more likely to apply to worries over the coupling between my controller and model than between my model and data structure...

The hardest part about figuring out the design patterns you should use for a specific real world scenario is that the ideal that we seem to spend so much time discussing often does not exist in reality... I love the black and white discussions of where and how good design principals should be applied as much as the next programmer, but my experience to this point leads me to believe that implementation is usually based in varying shades of grey.

Thanks again and I look forward to reading more from you!
Christine
# Posted By Christine Panus | 10/23/08 10:12 AM
Jason Dean's Gravatar @Christine - I guess I don't understand what you mean when you say:

"but it seems that if I'm using Lightwire and/or Coldspring for my DI and use the framework's integration for those DI handlers it seems that I'd have to have my handlers act as the service layer."

The way I have my handlers work is to have them receive the event object, and then have the handler instantiate the service object and pass data to it from the Event object.

In my case, the controller is really "dumb". All it is doing is receiving data, passing it to the service layer from processing and persistence, and then getting data back for display in a view. I do not have any business logic in the controller, it is all either in the service layer or in the model objects.

Hopefully, I will be demonstrating what I am doing in the coming weeks as I have time to blog about it.

Also, take a look at the ColdBoxReader sample application that is included with the ColdBox download.

Again, I am not saying that I am doing it the "right" way. Just trying to explain how I have been doing it.
# Posted By Jason Dean | 10/23/08 8:10 PM
Christine Panus's Gravatar @Jason, I see where you are coming from... and I agree the separation of concerns is important... it just seems like once the DI occurs in the handler you've already broken the rule. The Service Layer won't function without going through the handler since the DI occurs in the handler... I think it would be easier to refactor the code and get out of the framework if the only part of the app you needed to pull is the DI, but am I going to need to pull out of the framework and is the "extra" service layer worth it?

Thanks for your comments, you're really helping me think this through!
Christine
# Posted By Christine Panus | 10/24/08 5:47 PM
Jason Dean's Gravatar @Christine, I see what you are saying now. You're saying that since the handler is managing the Dependency Injection, that the service layer can't work without the handler, hence it cannot work without ColdBox. And that is where I disagree.

Just because the handler is taking care of DI in our application, does not mean that nothing else can handle DI, even in the same model. Since we have separated our service layer from our controller, our service object is completely oblivious about how it is being called, or how it gets the objects it needs. We set up our service layer so that it get instantiated and receives the objects it needs.

So the service object does not care how it receives the objects it depends on, so long as it receives them. Whether it comes from ColdBox, Mach-II, Mate for Flex, or a ColdSpring remote proxy.

So just because ColdBox happens to be handling DI at one moment that the service object is called, does not mean that something else could not handle it at another moment.

At least, that is my understanding.
# Posted By Jason Dean | 10/24/08 8:29 PM
Christine Panus's Gravatar @Jason, Yeah, I can see both sides, maybe I'm trying too hard to cut out another layer. We actually need a separate set of DAOs given our structure, so I couldn't combine those in to the business layer... so I was trying to decrease the hoops we're jumping through overall... but we may have the exception that proves the rule... each of the layers (DAO, Bean, Service, Controller) are needed with the data architecture I'm dealing with... I just keep reminding myself we CAN change it later... it's not immutable... we can always change it later...

Thanks again,
Christine
# Posted By Christine Panus | 10/26/08 2:59 PM
Christine Panus's Gravatar @Jason, Everytime I think I'm clear, I get back to this quandary. It appears to me that if I'm doing DI through ColdBox, all objects need to originate in the handler. I think that is what keeps drawing me back to using my handlers as my Service Layer... What am I missing?
# Posted By Christine Panus | 10/26/08 5:31 PM
Jason Dean's Gravatar @Christine, you certainly can use your handlers as your service layer. That is what the ColdBox proxy is for. The cool thing about using the ColdBox proxy as your service layer is that you get the added benefits of the controller's methods, plugins and interceptors.

I don't think you would be doing anything wrong by using your handlers as the service layer. But what I do think you would want to reconsider is not putting an additional service layer between your controller and your model. While technically your application would work just fine if your controller was talking directly with your model, it just doesn't feel right to me.

You mentioned that you were planning to use the ColdBox Proxy. Does that mean you have a Flex front end? Or is it heavy on the Ajax?
# Posted By Jason Dean | 10/26/08 6:58 PM
Jason Dean's Gravatar @Christine, If you feel like chatting about it in real time, hit me up in the chat window on the right.
# Posted By Jason Dean | 10/26/08 7:13 PM
Jon's Gravatar I know this is an older thread, but I'm just getting started with MVC and stumbled across it. So far - this is the only posting that I've found that breaks it down well enough to get a handle on it. Thank you!!!
I just wanted to point out for others that there is mention to home.cfc, which should probably be home.cfm - right?
# Posted By Jon | 2/23/09 4:38 PM
Jason Dean's Gravatar @Jon, Thank you for the kind words. I am glad some are still getting use out of older posts.

Yes, you are right, that should be home.cfm, not home.cfc. I have corrected the post. Thanks
# Posted By Jason Dean | 2/23/09 9:30 PM
Steve's Gravatar Jason-
This tutorial is awesome. I have been meaning to learn a ColdBox for a while now and this tutorial is really helping me out. Thanks!!!
# Posted By Steve | 5/15/09 10:15 PM
bill turner's Gravatar Hey Jason -

I was recently called back to one of my clients who is upgrading to CF9 and wanted to implement a framework. I suggested ColdBox because a few friends, notably Peter Bell and Rich DesCombaz, suggested it is perhaps the best choice. I have no experience with it, so this series is proving a god send.

I am building a proof of concept right now using CB 3.0M5. I want to let other readers know that some things have changed since your original post, chiefly the best practice of returning the collection to a variable and using that. Below is how my index looks:

   <cffunction name="index" returntype="void" output="false" hint="My main event">
      <cfargument name="event" required="true">
      <cfset var rc = event.getCollection()>
      
      <cfset rc.pageTitle = "My To-Do List">
      <cfset rc.today = Now()>   
      
      <cfset event.setView("home")>
   </cffunction>

One more thing I would say is that if anyone is in a position of making a technology choice, Grails should be strongly considered. It, too, is a great framework and has features I just don't see in ColdBox. If I had been talking with my client a month earlier, I would have strongly pushed them in that direction.
# Posted By bill turner | 5/19/10 2:04 PM
Jason Dean's Gravatar Bill,

Thanks for the kind words and the tip.

Actually, using the RC struct was an option in 2.6.x as well, I just never did it while I was writing these posts.

Yeah, Grails is a pretty awesome framework and is on my list of things to spend more time with.
# Posted By Jason Dean | 5/19/10 2:35 PM
bill turner's Gravatar I am not saying, btw, that CF should be abandoned for Grails, just that it should be considered. I believe it should be fairly easy for those that have taken advantage of CFCs and other OO development in CF to comprehend. For those still coding fall through CF (note that I said fall through and not procedural, there is a huge difference, C and COBOL are both procedural and generally are not written as fall through), it may be more difficult, but the move to a framework like ColdBox won't be particularly easy, either, for there are many concepts that should be practiced. Then again, in my opinion, and this is harsh, if you have not learned those features that have been available for many years now (assuming you have been using CF for some time), you are committing professional malpractice.
# Posted By bill turner | 5/20/10 9:31 AM
Pawan sharma's Gravatar Hey! Jason i am very satisfied after reading the contents about coldbox from your website but i am little bit confused about service layer. Where i do write y service layer logic if i don't want to use coldspring service layer and i just want to create my service layer using coldbox frame work.Could you lease help me.
# Posted By Pawan sharma | 9/5/14 12:11 AM
Jason Dean's Gravatar @Pawan,

A 'coldspring service layer' is not a thing. ColdSpring is a dependency injection/Inversion of Control (DI/IoC) framework (AOP as well, but that is a separate discussion), ColdSpring really has nothing to do with your service layer other than the fact that it it may instantiate it and provide it with dependencies or provide it as a dependency to other objects in your application.

Whether or not you choose to use ColdSpring (which at present I would not use) will not affect, in anyway, the way you create and use your service layer.

If you are using ColdBox you could just as easily (probably more easily) use WireBox for DI/IoC, but your service layer still would not change.

At this point I wonder if you are confused about what a service layer is. The service layer is the API to your model. It provides an interface to your controllers and views to the objects in your domain model (gateways, DAOs, and other persistence objects). It is a way for your application to talk to work with your persistence layer without talking directly to the persistence layer.

I don't know how else to help you without more information or without going into a full lesson on service layers. If you have follow up questions, please feel free to post them.
# Posted By Jason Dean | 9/5/14 11:37 AM
Pawan sharma's Gravatar Thanks a lot Jason for replying me.
# Posted By Pawan sharma | 9/11/14 1:14 PM
BlogCFC was created by Raymond Camden. This blog is running version 5.9.1. Contact Blog Owner