AspectJ logs with Log4j - spring

I am trying to implement AspectJ logging to module to analyze it better. My application is already using log4J framework.
If I run AspectJ class in a separate application it works fine but it doesn't work when I integrate it with application. Here what I have done
applicationContext.xml
<aop:aspectj-autoproxy/>
<bean id="logAspectj" class="com.test.aspect.LoggingAspect"/>
LoggingAspect class
#Component
#Aspect
public class LoggingAspect {
private final Log log = LogFactory.getLog(this.getClass());
#Around("execution(public void com.db.TestMarshaller.saveDoc(..))")
public Object logTimeMethod(ProceedingJoinPoint joinPoint) throws Throwable {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
Object retVal = joinPoint.proceed();
stopWatch.stop();
StringBuffer logMessage = new StringBuffer();
logMessage.append(joinPoint.getTarget().getClass().getName());
logMessage.append(".");
log.info(logMessage.toString());
return retVal;
}
}
Note: Here TestMarshaller is not exposed via Spring.
Is there some specific AspectJ setting for Log4j?

since your com.db.TestMarshaller is not "spring managed" (there is no Spring bean of that type in your context), the used spring namespace will NOT create a proxy which calls your Aspect.
In this case you'll either have to compile your sources with the AspectJ Compiler (Compile Time Weaving) or use the aspectj runtime to modify your bytecode when your class is loaded (Load Time Weaving).
It really depends on your use case which way you go - if your Aspect is part of your business logic and can not change during runtime, use the aspectj compiler, since it will only be done once. I case you need a more dynamic application of your aspect, use load time weaving instead.
Hope this helps,
Jochen

Related

Adding legacy singleton to Spring ApplicationContext for Injection

I am trying to create a lightweight web service around a legacy java library to expose it as a web service using Spring Boot. I am new to Spring, while I have a lot of java experiance writing libraries all my web service experiance is in ASP.NET.
I can instantiate an instance of my library object but I can't figure out how to then have that object be injected into my controllers via #Autowired when the application is spun up.
This is my main application:
#SpringBootApplication
public class ResolverWebServiceApplication {
private static ArgumentParser newArgumentParser() {
ArgumentParser parser = ArgumentParsers.newFor("Resolver").build();
// configuring the parser
return parser;
}
public static void main(String[] args) throws ArgumentParserException {
ArgumentParser parser = newArgumentParser();
Namespace ns = parser.parseArgs(args);
ResolverOptions options = new ResolverOptions.Builder(ns)
.build();
ResolverContext context = new ResolverContext(options);
// ^^^ I need to get this injected into my controllers ^^^
SpringApplication.run(ResolverWebServiceApplication.class, args);
}
}
And then a simple controller which needs the class injected:
#RestController
public class VersionController {
#Autowired
private ResolverContext context; // And here the instance needs to be injected.
#GetMapping(path = "/version", produces = MediaType.APPLICATION_JSON_VALUE)
public long version() {
return context.getResolver().getVersionAsLong();
}
}
I could make the context a singleton which the controllers just refer to but I want to be able to test my controllers by mocking the context. There is also obviously a lot of validation and error handeling that needs to be added.
I can't have it be a Bean since I only want to instantiate one for my entire application.
The closest question I have found is this one: Registering an instance as 'singleton' bean at application startup. But I can't put the options in the configuration files. The application might be spun up in a container or on a users machine and requires the ability to accept arguments to initialize the library class. It would be a real usability degradation if someone had to manually edit the application config for these options.
You need to tell spring to consider the required classes from your lib when initializing the application context i.e Configure and let spring know how to create a bean and then let spring handle dependency injection for you.
First of all, add required jar that you have in your build file, say pom.xml for maven, in your current project. Idea is to have it on your classpath when you build the project.
As you said it is legacy lib and I am assuming it is not a spring bean, then
In your configuration class, return it as a bean, using #Bean annotaion.
#Configuration
public class YourConfigurationClass {
#Bean
SomeBean returnSomeBeanFromLegacyLib() {
return new SomeClassFromLib();
}
Once you return this bean from your config, it should be available to Spring Context for dependency injection whereever you #Autowire the required dependency.

Is it possible to add PointCut to ModelAndView method?

I tried to use PointCut to perform some post action after ModelAndView.setViewName, but it seems that it never triggers:
#Aspect
#Component
public class TestAspect {
private Logger logger = LoggerFactory.getLogger(this.getClass());
#Pointcut("execution(* org.springframework.web.servlet.ModelAndView.*(..))")
public void testPointCut() {
}
#After("testPointCut()")
public void afterPointCut(JoinPoint joinPoint) {
logger.debug("afterPointCut");
}
}
If I change the execution part to some class of my own, this point cut works.
So what is the correct way to add PointCut to ModelAndView?
I am not a Spring user, but what I know about Spring AOP is that you can only apply it to Spring components. The class ModelAndView is not derived from any Spring core component class or annotated by anything making it such, it is a simple POJO. As such you cannot target it by Spring AOP pointcuts. You should rather target something within the reach of Spring AOP.
The alternative would be to unpack the big gun and use full AspectJ LTW (load-time weaving) which is not limited to Spring components.

CamelTestSupport with routes using classes with #Autowired

After help from experts over at question Camel unit test with cametestsupport, template is always null, I ended up with one more issue.
Below is my test class - a simple test that tests a route which has only ValidationProcessor POJO.
public class RouteTests extends CamelTestSupport {
#Override
protected RouteBuilder createRouteBuilder() {
return new RouteBuilder() {
#Override
public void configure() {
from("direct:start")
.filter().method(ValidationProcessor.class, "validate")
.to("mock:result");
}
};
}
#Test
public void testSendMatchingMessage() throws Exception {
ObjectMapper objectmapper = new ObjectMapper();
ClassLoader loader = Thread.currentThread().getContextClassLoader();
InputStream stream = loader.getResourceAsStream("test.json");
JSONObject testJson = new JSONObject(objectmapper.readValue(stream, Map.class));
MockEndpoint resultEndpoint = getMockEndpoint("mock:result");
resultEndpoint.expectedMessageCount(1);
template.sendBody("direct:start", testJson);
resultEndpoint.assertIsSatisfied();
}
}
The problem comes when this ValidationProcessor has an #Autowired component in it. My validation method needs data from Elasticsearch and hence I have an #Autowired for an elastic client.
When I run mvn clean test, I am getting a NullPointerException stating that this elastic client is null. I think the issue is that this test is devoid of anything to do with Spring and hence the issue, but I do not know how to overcome this.
My test needs to fetch data from Elasticsearch and hence the ValidationProcessor POJO does need #Autowired.
When you extend CamelTestSupport then it's not a Spring application. You need to extend CamelSpringTestSupport. That would create Camel in a Spring runtime, and then allow beans to have IoC via Spring. This kind of testing is often used with Camel XML routes where the routes are defined in XML files. However, you can have a plain XML file and refer to routes in Java DSL as well.
However, as Makoto answers, then vanilla Spring unit testing is of late often about using all those gazillion annotations. Camel has support for that as well as his answer shows. This is also how for example Spring Boot testing is done, etc.
You can find some unit tests in camel-test-spring you can use as inspiration as well.
What I've discovered is that it's unwise to extend CamelTestSupport when you want to use anything with Spring. In fact, there's a better way to do it - use the CamelSpringBootRunner instead.
Well...I say "better". You're going to find yourself attaching a ton of annotations. Of the things you'll need:
A boostrapper to ensure that you're bootstrapping Camel correctly
The routes you wish to add to the classpath (and all of the beans); this ensures that they get added to Camel's registry through Spring's registry
You have to dirty the context after every run or your tests get into a wonky state.
You can automock endpoints by specifying either #MockEndpoints or #MockEndpointsAndSkip. The former sends the data along to the actual route.
Below is just a start. Many of these annotations and their documentation can be found in the formal docs.
#RunWith(CamelSpringBootRunner.class)
#BootstrapWith(SpringBootTestContextBootstrapper.class)
#ActiveProfiles("test")
#SpringBootTest(classes = { YourEntryPointClass.class })
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
#UseAdviceWith
#MockEndpoints("direct:*")
public class RouteTests {
}

How to AOP aspectj in OSGi with Apache Felix

I'm currently working on an OSGi project.
Without many experiences in AOP combined with OSGi, I would like to know how to best do AOP in an OSGi environment?
We have implemented the AOP scenario to create a console that intercept the call to a bundle in order to store the elapsed time for each task started by this bundle.
Today, this aspect has been deployed on a jboss container using the LoadTimeWeaver provided by aspectj (adding an agent to the jboss start script in order to instrument the jars in the container -javaagent:%APP_HOME%\application\lib\aspectjweaver-1.6.11.jar).
I've read some articles about this problem, but did not find a solution that suits well for me. There is, for example, an Equinox Incubator project for AspectJ. But since I'm using Apache Felix and Bnd(tools) I want to avoid using something from Equinox. One requirement for the weaving process will be that it should be at load-time as well (a bundle for aspectj that instrument the method inside another bundle).
Someone can share experiences with such a use case using AOP aspectj with OSGI Felix ?
Here is a working example of minimal
felix aspectj setup
The basic pattern is:
register weaving hook on activation
public class Activator implements BundleActivator {
public void start(BundleContext context) throws Exception {
AspectWeaver weaver = new AspectWeaver();
servList.add(context.registerService(WeavingHook.class, weaver, null));
}
}
inject weaving definition context:
public class AspectContext extends DefaultWeavingContext {
#Override
public List<Definition> getDefinitions(final ClassLoader loader, final WeavingAdaptor adaptor) {
if (definitionList == null) {
definitionList = AspectSupport.definitionList(loader, rootConfig);
}
return definitionList;
}
}
provide weaving hook implementation
public class AspectWeaver implements WeavingHook {
#Override
public void weave(WovenClass woven) {
String name = woven.getClassName();
BundleWiring wiring = woven.getBundleWiring();
ClassLoaderWeavingAdaptor adaptor = ensureAdaptor(wiring);
final byte[] source = woven.getBytes();
final byte[] target;
// aspectj is single-threaded
synchronized (adaptor) {
target = adaptor.weaveClass(name, source);
}
woven.setBytes(target);
}

Spring AOP: advice is not triggered

Trying to design simple aspect,that will print word "logg" to console,when any of public methods executed.
aspect:
#Aspect
public class LoggingAspect {
#Pointcut("execution(public * *(..))")
public void publicServices() {
};
#Before("publicServices()")
public void logg() {
System.out.println("logg");
}
}
xml config:
<context:component-scan base-package="aspectlogging" />
<aop:aspectj-autoproxy/>
<bean id="loggingAspectHolder" class="aspectlogging.LoggingAspect"/>
simple bean:
package aspectlogging;
#Component
public class TestableBean {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
test:
public class TestLogging {
public static void main(String[] args) {
TestableBean tb = new TestableBean();
tb.setName("yes");
tb.getName();
}
}
I expect,that result of running of TestLogging will be "logg" word in console,and no output returned.
Do I understand AOP correctly in this case?
With #Around advice, you need to have a ProceedingJoinPoint pjp argument to the advising method and to call pjp.proceed() at the point in the advisor when you want the wrapped method to be called. It's easier to use #Before advice really, when what you've done will otherwise work just fine.
[EDIT]: Also, you must let Spring construct your beans for you instead of directly calling new. This is because the bean object is actually a proxy for your real object (which sits inside it). Because your target object doesn't implement an interface, you will need to have the cglib library on your classpath in addition to the Spring libraries. (Alternatively, you can go with using AspectJ fully, but that requires using a different compiler configuration.)
To create your beans, you first need to create a Spring context and then query that for the bean instance. This means you change from:
TestableBean tb = new TestableBean();
To (assuming you're using Spring 3, and that your XML config is in "config.xml" somewhere on your classpath):
ApplicationContext context = new ClassPathXmlApplicationContext("config.xml");
TestableBean tb = context.getBean(TestableBean.class);
The rest of your code remains the same (after adjusting for import statements and possibly additional dependencies).
Not quite sure on this one, but maybe you need to use a spring managed TestableBean to have spring AOP pick up the method call.
edit: of course, you can't use #Around the way that you provided - but this subject has been addressed by another answer, so it's omitted here.
edit2: If you need help on how to get a spring managed bean, please feel free to ask. but since you already got your aspect bean set up, I believe you can handle this :)
edit3: Hehe. Ok.. maybe not :)
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
will load your application context.
Load beans from there by calling:
TestableBean testableBean = (TestableBean )ctx.getBean("testableBean ");
Define the TestableBean just like you did with your Aspect bean.
edit4: Now I'm pretty sure that the fault is the non-spring managed bean.
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 domain objects, or any other object not managed by the Spring container, then you will need to use AspectJ.
Taken from: http://static.springsource.org/spring/docs/2.0.x/reference/aop.html

Resources