Migrating filters from MachII to Coldbox.

We are slowly going through thr process of migrating our modules over to coldbox from Machii.

We have numerous filters within our machii module which generally conditionally redirect the users flow.

So for example… there is a RedirectProductView… which checks a group of criterium… and redirects to a specific MachII event depending on the current situation.

My question is how would I achieve this same functionality within coldbox (decoupled conditional flow control)… I think interceptors are the right area to be looking at?

Thanks guys!

if this business logic is part of the app/module, i would use aroundHandlers(), instead.

http://wiki.coldbox.org/wiki/EventHandlers.cfm#Around_Advice_Intercpetors

if you want the AOP goodies, i would use interceptors.

Thanks very much Aaron… I have been reading up on AOP within coldbox… I have to say it is very exciting!

Just been looking at this simple example on runnable.

Using this example… In the coldbox fortKnox event… If I wanted to add another aspect such as “secondarySecure” for some reason.

Do the aspects run depending on the order of the annotions defined in the fortKnox method.

function fortKnox(event, rc, prc) secure secondarySecure {
}

In my example above… would the code within the secondarySecure aspect run after the secure aspect.

This is very important in my application… since there situations where this might be needed.

I suppose for someone new to AOP it is going to be tricky deciding when to create an aspect instead of an interceptor.

Please forgive my current ignorance in this area.

Thanks

if you want to run multiple around handlers, you’ll probably want to use interceptors and start announcing custom interceptions points.

when one interception point is done, and the logic passes, raise another announcement in that point to another point.

this is now event driven behavior.

Alex,

Yes the annotations are run in the order they are defined, I did try to see if that was in the documentation. The best way to do this is create 2 basic AOP methods, and log info from them, which is what I did to test that theory.

Unlike what Aaron mentions, I agree AOP is not always the answer either. For example if you’re doing logging on certain methods and wish to get statistics on these handlers AOP is much better than Interception points, but it depends on what you’re trying to achieve.

But things like logging and statistics, as well as caching options can be added to any number of methods for the purpose they are designed for, what Aaron suggests can be a better alternative, but not if you require before and after processing on the method. You could do a pre/post interception point, but not my preferred choice.

The best way to look at what your trying to achieve, is if you require something to wrap around a handler as in logging and execution timing or even caching, then the obvious choice is going to be AOP. Otherwise going the route of Interception Points is what you need to look at. But having said that, I also use AOP more for development than production in my usage, as AOP is far better suited for that in my usage. About the only time I use AOP in production is security related.

Thanks guys.

Your responses have been very helpful…

Since we are going to be migrating an entire machii code base over to coldbox I thought it best a get a full understanding of the benefits of all the coldbox options.

It sounds like that in my first specific example (redirecting an event depending on the validation of form data) I would probably go for an interception point.

I think it’s time to get my hands dirty and start playing around with both options so I have a better understanding before asking you guys more questions.

Thanks again!

Yes in that case an interception point is the way to go, ColdBox has these at certain stages of the request. For example

preRequest / preHandler / preEvent / preProcess

All can be intercepted, but the exact one would depend on how early you want to do the interception.

Thanks… That makes sense to me.

Coldbox is great!

Btw andrew … I have been reading your ramblings blog.

Great information!

Thanks, just ignore the ones that are really ramblings :stuck_out_tongue:

No seriously… I have been reading the coldbox docs… and although they are thorough they don’t get straight into a simple example.
Instead they immediately explain the high level concepts around AOP/Interceptors… Your entries gave simple examples which explained the basics very quickly.

Im still trying to get my head around their example of AOP using coldbox.system.aop.MethodInvocation

http://wiki.coldbox.org/wiki/WireBox-AOP.cfm

I think I will tackle that tomorrow after playing around with some actual code… I can only read for so long!

The method invocation is the same as all of them, they are technically wrappers around the method you annotate or set it up for.

In the overview section, the basic example gives a save() method. What the AOP here is doing is saying that we wish to wrap a transaction around the save method. Now the tricky part here is whether you tie this to a cfc or mapped folder as to how it gets called.

For example in WireBox

binder.mapAspect(“cronusLogger”).to("#moduleMapping#.aop.cronusLoggingAOP")
binder.bindAspect(classes=binder.match().any(), methods=binder.match().annotatedWith(“cronusLogger”), aspects=“cronusLogger”);

I use this to add logging to any method that I use the annotation cronusLogger, or I could do it like the following. But you could also do this.

binder.bindAspect(classes = binder.match().regex(‘handlers.*’), methods = binder.match().any(), aspects = “cronusLogger”);

Which is saying that the AOP can only be added to any method in any handler. As you can see it is fully extensible in any way you need it to be.

And thanks, I do my best to make it as simple and easy to follow as possible.

One other trick I use, is from this post here.

http://www.andyscott.id.au/blog/how-to-write-and-use-your-own-html-helper-in-coldbox

This interception point can also be used not only for what it does above, but I also use it to make changes to code without touching core features. For example in ContentBox there are issues that I need it to do, but until they get released as a patch I use this method to basically patch the system on a temporary basis as well.

I thought I would mention that, because if you have wire boxed your MachII business logic. You could migrate it one by one by replacing the wire boxed MachII model with the new ColdBox one on a temporary basis, if it causes issues youl could then just remove that interception and roll back to the MachII very quickly if you need too.

The other thing I did not mention, is that even though this

public function myMethod() AOP1 aop2 aop3 {}

Is run in that order, there it is chained as in recursive chaining goes. The best way to describe this, is that all the pre code is run for all the AOP annotations in that order then in reverse order when coming back, as they are wrappers to that method, but wrap in that order.

Can I also add while it is my head, as it just came to me. The AOP basically shows a way to do wrapping of methods as well. But you could easily do the same thing of using the AOP to replace methods or more.

For example if we look at the code template for AOP on a method.

	<---  invokeMethod --->    
    **<cffunction** name="invokeMethod" output="false" access="public" returntype="any" hint="Invoke an AOP method invocation">    
    	**<cfargument** name="invocation" required="true" hint="The method invocation object: coldbox.system.aop.MethodInvocation">
		**<cfscript>**
			var refLocal = {};
			var debugString = "target: #arguments.invocation.getTargetName()#,method: #arguments.invocation.getMethod()#,arguments:#serializeJSON(arguments.invocation.getArgs())#";
			
			// log incoming call
			log.debug(debugString);
			
			// proceed execution
			refLocal.results = arguments.invocation.proceed();
			
			// result logging and returns
			if( structKeyExists(refLocal,"results") ){ 
				if( instance.logResults ){
					log.debug("#debugString#, results:", refLocal.results);
				}
				return refLocal.results; 
			}
		**</cfscript>**
    **</cffunction>**

We could remove this from the above code.

			// proceed execution
			refLocal.results = arguments.invocation.proceed();

That would enable the AOP to not actually run the what we are annotating, but to instead actually replace the entire code. That might be a lot more tedious to setup, but would also provide a better control on what and when something is migrated across.

Hope that all helps.

Thanks for all your help.

Much appreciated!

No problem, you will see that it is not complicated and opens up doors to things you wouldn’t think about doing.

Just catching up on this thread and wanted to note that the order of function annotations should not be depended on. They may appear to be processed in the order declared now, but that functionality isn’t guaranteed. This is because CFML gives WireBox the annotations in a struct which does not guarantee order.

Now, that being said-- AOP aspects can still be bound in a specific order! You just have to use the binding DSL in your binder config like so:

mapAspect(aspect=“TransactionAspect”,autoBind=false).to(“model.aspects.MyTransactionAspect”);

// match only methods that start with the regex ^save
bindAspect(classes=match().any(),methods=match().regex("^save"),aspects=“TransactionAspect”);

So basically the annotation binding is quick and easy but it can’t guarantee order simply based on how the CFML language presents component metadata. For that, use explicit aspect bindings.

Thanks!

~Brad

ColdBox Platform Evangelist
Ortus Solutions, Corp

E-mail: brad@coldbox.org
ColdBox Platform: http://www.coldbox.org
Blog: http://www.codersrevolution.com

Brad,

I was under the impression that the only time you can’t rely on the order of a struct, is when it is being outputted via cfdump. I have never had an issue when normal CFML creates arrays and structures, they are always in the order they are created.

Have you got any reference to what you talk about, as I have never heard of this problem.