Issues Getting AOP/1 to Assign An Interceptor to Every Bean Factory Aware Object

44 views
Skip to first unread message

Tony Junkes

unread,
Jan 13, 2018, 1:28:38 AM1/13/18
to framework-one
This is somewhat of a continuation of my initial conversation on the CFML Slack...

I originally asked this:

AOP/1 question... I'm trying to make use of AOP/1 to implement a logging interceptor. I want to apply it to all of my bean factory aware objects. I see @dbudde mentioned above that you might be able to do so with `interceptors:  [{interceptorName: "myInterceptor",  methods: "*"}]` but that doesn't appear so. Correct me if I'm wrong... So I attempted another route of achieving this. I created a function in Application.cfc that builds the array of structs with the values that need to be defined in `diConfig.interceptors`. Testing this function on its own is returning what I want however, when I set  `diConfig.interceptors = myFunction()`, it appears that an empty array ends up being the value based on dumping `getConfig().diConfig.interceptors`... Am I missing something with how FW/1 evaluates the framework settings? Thanks in advance.

Some side notes:

If I pass an actual declaration of an interceptor to the config, my interceptor is mapped and works as expected. (e.g. diConfig.interceptors = [{ "beanName = MyService", interceptorName = "LogInterceptor", methods = "*" }])

This application is being run in the latest stable release of Lucee 5 with FW/1 4.1 (I've tested on 4.5 as well). It's also using subsystems. For now, the default bean factory is set up to look for services/beans/controllers throughout the app so it does include the subsystem objects even though they define their own bean factories that add the default as the parent. Every bean factory is created as AOP (aop.cfc). None are the default DI (ioc.cfc).

So to elaborate on what I have been trying...

My DI specific framework settings look like this:

diEngine = "aop1",
diLocations
= "/src/model, /src/controllers, /src/modules",
diComponent
= "framework.aop",
diConfig
= {
  interceptors
= _setupLogInterceptorPaths()
}


And the function I have added in Application.cfc to build the array of structs to pass to diConfig:

private array function _setupLogInterceptorPaths() {
 
// Services
 
var services = [];
 
var servicePaths = directoryList(expandPath("/src"), true, "path", "services");
 
for (var sp in servicePaths) {
   
for (var service in directoryList(sp, true, "name")) {
      services
.append({ beanName = service.listFirst("."), interceptorName = "LogInterceptor" });
   
};
 
};
 
// Controllers
 
var controllers = [];
 
var controllerPaths = directoryList(expandPath("/src"), true, "path", "controllers");
 
for (var cp in controllerPaths) {
   
for (var controller in directoryList(cp, true, "name")) {
      controllers
.append({ beanName = controller.listFirst("."), interceptorName = "LogInterceptor" });
   
};
 
};
 
// Return merge of the collections
 
return arrayMerge(services, controllers);
}


The above settings and function paired up, result in an empty array being the value of diConfig.interceptors. No good. If I set the function to a variable and dump it out in the application, I see the results I'd expect.

So I thought of trying something else... I scrapped the framework config setting of the interceptors and modified the _setupLogInterceptorPaths() function above to call getBeanFactory().intercept() in order to "manually" pass in each object.

private void function _setupLogInterceptorPaths() {
 
// Services
 
var services = [];
 
var servicePaths = directoryList(expandPath("/src"), true, "path", "services");
 
for (var sp in servicePaths) {
   
for (var service in directoryList(sp, true, "name")) {
      getBeanFactory
().intercept(service.listFirst("."), "LogInterceptor", "*");
   
};
 
};
 
// Controllers
 
var controllers = [];
 
var controllerPaths = directoryList(expandPath("/src"), true, "path", "controllers");
 
for (var cp in controllerPaths) {
   
for (var controller in directoryList(cp, true, "name")) {
      getBeanFactory
().intercept(controller.listFirst("."), "LogInterceptor", "*");
   
};
 
};
}


I then attempted to call the above function in setupRequest() and setupApplication() to see what kind of results I'd get.

This resulted in the following errors:

Exception in onError
The action activities:main.index failed.
Unable to locate method in (beanProxy).
The method (setFramework) could not be found. Please verify the method exists and is publically accessible. (application)
 
Original exception in onRequest
The action activities:main.index failed.
Unable to locate method in (beanProxy).
The method (before) could not be found. Please verify the method exists and is publically accessible. (application) 


So to step back from the main app, which is fairly involved these days, I created a simple example to test. Right from the start, I am unable to get it working and receive the same "Unable to locate method in (beanProxy)." error. It seems there's possibly something going on with controller objects being added... I was able to declare a service with the interceptor, but not a controller. I did confirm that services and controllers get picked up by the bean factory itself.

I'm at a loss at this point...

I put the example on Github... It can be run in CommandBox by executing "box install && start" in a terminal. Otherwise, you'll need Lucee 5 / FW/1 4.1+ available (I have not tested this on ACF).

I'm open to any feedback/suggestions. Any help is appreciated.

Thanks!

Tony Junkes

unread,
Jan 13, 2018, 8:31:31 PM1/13/18
to framework-one
UPDATE

After going over this further with Sean Corfield, it was discovered that there was a bug with beanProxy.cfc's onMissingMethod() function. Sean pushed an update and my issues appear to be resolved in the demo I put together.
Reply all
Reply to author
Forward
0 new messages