How to create AOP interceptors for Mule classes? - spring

This is what I've tried so far and my interceptor is not triggered (no "TATATA" in my logs) :
My interceptor AopLoggingInterceptor.java :
package fr.mycompany.bus.flow.reco.ani.custom.interceptor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
#Aspect
public class AopLoggingInterceptor {
#Around("execution(* org.mule.api.transport.MessageReceiver.routeMessage(org.mule.api.MuleMessage))")
public Object addMonitor(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("TATATA Before");
Object object = pjp.proceed();
System.out.println("TATATA After");
return object;
}
}
META-INF/aop.xml :
<aspectj>
<aspects>
<aspect name="fr.mycompany.bus.flow.reco.ani.custom.interceptor.AopLoggingInterceptor" />
</aspects>
<weaver options="-verbose">
<!-- Weave types that are within the org.mule.* packages. -->
<include within="org.mule.*" />
</weaver>
</aspectj>
My Mule/Spring config file :
<?xml version="1.0" encoding="UTF-8"?>
<mule >
<spring:beans>
<context:component-scan base-package="fr.mycompany.bus" />
<context:annotation-config />
<aop:aspectj-autoproxy />
<!-- Aspect -->
<spring:bean name="aopLoggingInterceptor" class="fr.mycompany.bus.flow.reco.ani.custom.interceptor.AopLoggingInterceptor" />
</spring:beans>
</mule>
My Mule file config consists of one flow with one inbound endpoint, 2 outbound endpoints, loggers and transformers (valid flow widely tested).
VM args :
-XX:PermSize=128M -XX:MaxPermSize=256M -javaagent:D:\path\to\mule\opt\aspectjweaver-1.6.11.jar
Extract from mule file starting in Eclipse which shows weaving is created :
[MuleApplicationClassLoader#2934847] info AspectJ Weaver Version 1.6.11 built on Tuesday Mar 15, 2011 at 15:31:04 GMT
[MuleApplicationClassLoader#2934847] info register classloader org.mule.module.launcher.MuleApplicationClassLoader#2934847
[MuleApplicationClassLoader#2934847] info using configuration /D:/BusToolBox/workspaces/dev/.mule/apps/bus-esb-mrc-reco-ani/classes/META-INF/aop.xml
[MuleApplicationClassLoader#2934847] info register aspect fr.mycompany.bus.flow.reco.ani.custom.interceptor.AopLoggingInterceptor
EDIT
It works nicely with a class included in my project, but not with mule classes :
[MuleApplicationClassLoader#6ad5934d] debug generating class 'fr.mycompany.bus.flow.reco.ani.custom.transformer.CustomerDetailToSiebelRecoAniOutputTransformer$AjcClosure1'
EDIT 2
Here is the best result I can get (by using <context:load-time-weaver />), the loading process tries to look for more classes loaded by difference classloaders is , but it results in :
ERROR 2014-08-08 16:00:46,802 [main] org.mule.module.launcher.application.DefaultMuleApplication: null
java.lang.IllegalStateException: ClassLoader [org.mule.module.launcher.MuleApplicationClassLoader] does NOT provide an 'addTransformer(ClassFileTransformer)' method. Specify a custom LoadTimeWeaver or start your Java virtual machine with Spring's agent: -javaagent:org.springframework.instrument.jar
If I try to use spring-instrument-3.2.1.RELEASE.jar, I get same result as before (only main classloader is seen). Does it mean there is no hope with Mule ?

Have a look at this example for using Mule and Spring AOP. The example shows how to invoke Around advice for a component, but should be similar for the interceptor.

There is something very important when working with Spring AOP. In spring aop documentation is stated:
Use the simplest thing that can work. Spring AOP is simpler than using
full AspectJ as there is no requirement to introduce the AspectJ
compiler / weaver into your development and build processes. If you
only need to advise the execution of operations on Spring beans, then
Spring AOP is the right choice. If you need to advise objects not
managed by the Spring container (such as domain objects typically),
then you will need to use AspectJ. You will also need to use AspectJ
if you wish to advise join points other than simple method executions
(for example, field get or set join points, and so on).
So if you want that your AopLoggingInterceptor is invoked for MessageReceiver method calls, that is not going to work because the MessageReceiver object is not managed by the Spring container. The Spring container doesn't "see" this objects.
In other words Spring-AOP cannot add an aspect to anything that is not created by the Spring factory. I found that statement here.

Related

Inject Spring config file into Ejb using Annotation

Am trying to migrate project from ejb2.1 to ejb3.1. In my current project's ejb-jar.xml, i am using for loading the spring configuration xml file{which initializes the beans which going to be called in ejbbean class in onEjbCreate() method using "getBeanFactory().getBean("somespringclass") }.
Sample environment entry: <env-entry>
<env-entry-name>ejb/BeanFactoryPath</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>config/xyz.xml</env-entry-value>
</env-entry>
what is the ideal syntax for using annotation(ejb3.1) in ejbbean class so that i can remove in ejb-jar.xml.
Thanks in advance.
Just annotate your EJB-3.1 interface with #EJB and put the following in your Spring configuration XML:
<context:component-scan base-package="<your-ejb-package-e.g.-com.ejb.sample>" />

Spring AOP not working on all annotation methods

I have created a custom annotation in my spring mvc project.
The annotation is used to do an AOP
#Around("execution(#Cached * * (..)) && #annotation(cache)")
Here the annotation that I have created is "Cached", any method with the annotation is cached in couch base with the response as its value and the method argument as its key.
The problem is the annotation works (AOP works) on the controllers well. However from controllers, I am making call to different callable classes and utils. When I add the annotation" #Cached" on the callable classes or the util funcations the AOP doesn't work.
In the XML file, the following is what I have declared.
<aop:aspectj-autoproxy/>
<context:spring-configured/>
<context:component-scan base-package="com.abc.xyz">
<!--<context:include-filter type="annotation" expression="org.aspectj.lang.annotation.Aspect"/>-->
</context:component-scan>
<bean id="universalController" class="com.abc.xyz.misc.UniversalController"/>
<bean class="com.abc.xyz.api.metric.SystemTiming"/>
<bean class="com.abc.xyz.api.annotations.URLCacheImpl"/>
With Spring AOP, your classes which match the pointcut (where you have placed your #Cached annotation in this specific case) should be Spring beans. So the best guess that I can make is that your utility classes are very likely not Spring beans and that is reason why they are not getting woven in. You have two options that I can think of:
Make your utility classes also clean Spring beans
Use full Aspectj support - this way even though your utility classes are not Spring beans they would be woven with the advice.

How to register aspect in spring application on non-spring-managed object

I have a spring mvc application running in JBoss. I am using Spring AOP all over the place (using Annotations); they are working perfectly. I want, however, to also create an aspect for a couple of JBoss classes (specifically their login JaaS stuff).
I have created a POJO within my application that looks like the following:
package com.mycompany.aspect
(all imports here)
#Aspect
public class MyAspect{
#Pointcut("execution(* org.jboss.plugins.*.login(..))"
private void myPointCut();
#Around("myPointCut()")
public Object doSomething(){....}
}
I have also created an aop.xml file in my META-INF directory that looks like the following:
<aspectj>
<aspects>
<aspect name="com.mycompany.aspect.MyAspect"/>
</aspects>
<weaver>
<include within="org.jboss.plugins.*"/>
</weaver>
</aspectj>
I have also added the following jvm option:
-javaagent:pathto/aspectjweaver.jar (pathto is the right path to my aspectjweaver.jar file)
When my application deploys I get the following RuntimeException:
..... cannot register non aspect com$mycompany$aspect$MyAspect, com.mycompany.aspect.MyAspect
What could I possibly be doing wrong???
EDIT: I should mention I am using JBoss AS (4.2)... This I cannot change :(

Why doesn't just autowiring a field in a GWT servlet in Spring work?

Simply marking a field as #Autowired in a GWT servlet does not work as intended. The code will compile and the web application will start up - which means Spring was successfully able to autowire the field, but when the servlet is actually hit by client-side code, it will yield a NullPointerException - like there's a different, uninitialized copy of the servlet being hit.
I've found several ways on the web to get this working, one is by using a base servlet class that does some Spring logic but doing this means every GWT servlet must extend this base class. The other way was by using AspectJ and the #Configurable Spring annotation. There was very little configuration involved here and it just magically worked.
My question is why doesn't just autowiring the field just work as intended? What is GWT doing that causes this to break.
The code will compile and the web application will start up - which
means Spring was successfully able to autowire the field
Not necessarily. The web container can instantiate a servlet without any help from Spring. Which you could be experiencing:
but when the servlet is actually hit by client-side code, it will
yield a NullPointerException - like there's a different, uninitialized
copy of the servlet being hit.
try overriding Servlet's init():
#Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
WebApplicationContextUtils.getWebApplicationContext(config.getServletContext())
.getAutowireCapableBeanFactory().autowireBean(this);
}
When the RPC service is called from the client, the "server-side" looking at the called URL and the servlets mapping will find the class, will make the instance and it will serve the request. Meaning if you have #Autowired annotation, or you already have an instance of the RPC class in the spring context, it does not matter. The new instance will be created and it won't "know" about Spring.
I resolve this by implementing a class which extends RemoteServiceServlet and implements Controller (from Spring MVC) and ServletContextAware.
This way you can map every RPC service by URL using the Spring MVC approach, for ex:
<bean id="publicUrlMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/myFirstRpc">firstRpcServiceBeanRef</prop>
<prop key="/mySecondRpc">secondRpcServiceRef</prop>
</props>
</property>
</bean>
You also avoid the declarations for every single RPC servlet in web.xml, the mappings are clean and you have the Spring injection.
You declare only a single mapping in web.xml for org.springframework.web.servlet.DispatcherServlet which will serve all RPC calls.
There are couple of examples on the web with explanation about GWT RPC and Spring MVC controller integration.
Hope this will help.
It turns out that when using Spring at least, there's a MUCH simpler way to do this such that you can use #Autowired and it doesn't involve massive configuration or base classes. The caveat is that you must also use AspectJ. Here's what you need for your GWT servlet:
#Configurable
public class MyGwtServiceImpl extends RemoteServiceServlet implements MyGwtService
{
#Autowired
private MyService service;
// ...
}
And in your Spring config make sure you also have:
<!-- enable autowiring and configuration of non-spring managed classes, requires AspectJ -->
<context:spring-configured/>
One final note. If you are also using Spring security with your GWT application (and in your GWT servlets), you will need to make sure you define the correct mode to ensure the AspectJ weaving is done correctly (i.e., you get both #Secured annotation processing AND the #Autowired processing) you will need:
<!-- turn on spring security for method annotations with #Secured(...) -->
<!-- the aspectj mode is required because we autowire spring services into GWT servlets and this
is also done via aspectj. a server 500 error will occur if this is changed or removed. -->
<security:global-method-security secured-annotations="enabled" mode="aspectj"/>

Autowire Annotation in Spring without using Component Scanning

Is it possible to autowire beans using the #Autowired annotation without using component scanning?
Yes. <context-component-scan .. /> is responsible for discovering beans annotated with #Component, #Controller, #Service, #Respository, etc.
In order to have annotations processed (#Autowired, #Resource, etc) you need <context:annotation-config />. Thus annotations are processed on beans that are listed in applicationContext.xml.
As far as I know, <context-component-scan .. /> activates <context:annotation-config /> automatically.
This is true for both spring 2.5 and 3.0. (thanks skaffman)
I have never tried without component-scanning enabled, however I can confirm that #Autowire annotations works in Spring 3.0.x even with beans that are defined via XML.
When using AnnotationConfigApplicationContext, annotation config processors are always registered, meaning that any attempt to disable them at the #ComponentScan level would be ignored.
If it is meant in the question that you should explicitly state:
- <context:component-scan ...> in your xml file(it enables <context:annotation-config />)
or
- #ComponentScan in your java config
Then the answer is - Yes, it is possible to enable component scanning without any of the stated above statements in your code or xml file.
Another approach is to use AnnotationConfigApplicationContext :
AnnotationConfigApplicationContext context=
new AnnotationConfigApplicationContext("org.example.your.package");
Where "org.example.your.package" is your package for stereotyped annotated classes: #Component, #Repository, #Service, etc.
AnnotationConfigApplicationContext will search for your beans in the base package and inner packages.
No, we must use #ComponentScan if you are using java based configuration
(or) <context-component-scan .. /> for xml based configuration.
Note: If you are not using any of the approaches no corresponding instances are created in AplicationContext.
and when you try to access a resource (http://localhost:8080/customers) will end up with
WARNING: No mapping found for HTTP request with URI [/customers] in
DispatcherServlet with name 'dispatcher

Resources