I need to log many classes in some packages in a project which I can not change its source code.
So I need a solution which I can specify package name, and with spring aop add logging to that package's classes without change them but I dont know how can I do that.
How can I do that?
With Spring AOP you can only log these classes if they are used as Spring Beans, and even then you can only log public method executions.
Here is an aspect in #AspectJ notification (this is the style that is compatible with both "real AspectJ" and Spring AOP, read about the difference in the spring reference) that you can use in both Spring AOP and AspectJ byte code weaving:
#Aspect
public class LoggingAspect{
#Pointcut("execution(* com.mycompany.myproject.*.*(..))")
public void methodToLog(){
};
#Around("methodToLog()")
public Object logMethod(final ProceedingJoinPoint joinPoint) throws Throwable{
final StaticPart staticPart = joinPoint.getStaticPart();
final String sig =
"" + staticPart.getSignature() + " with args: "
+ Arrays.deepToString(joinPoint.getArgs());
System.out.println("Entering method " + sig);
final Object result = joinPoint.proceed();
System.out.println("Leaving method " + sig);
return result;
}
}
Here is a stupid class with some methods:
package com.mycompany.myproject;
public class Dummy1{
public static void main(final String[] args){
final Dummy1 dummy = new Dummy1();
dummy.doSomeStuff();
dummy.doSomeStuffWithSomeArgs("Hello", 123);
}
private void doSomeStuff(){}
public void doSomeStuffWithSomeArgs(final String firstArg,
final int secondArg){}
}
When you start this class in Eclipse / AJDT as Java/AspectJ application, you get the following output:
Entering method void com.mycompany.myproject.Dummy1.main(String[]) with args: [[]]
Entering method void com.mycompany.myproject.Dummy1.doSomeStuff() with args: []
Leaving method void com.mycompany.myproject.Dummy1.doSomeStuff() with args: []
Entering method void com.mycompany.myproject.Dummy1.doSomeStuffWithSomeArgs(String, int) with args: [Hello, 123]
Leaving method void com.mycompany.myproject.Dummy1.doSomeStuffWithSomeArgs(String, int) with args: [Hello, 123]
Leaving method void com.mycompany.myproject.Dummy1.main(String[]) with args: [[]]
To test this in Spring AOP would involve more work (the main method approach won't work, you will have to create an ApplicationContext and register a bean of type Dummy1, on which you will call the methods), so I'll leave that to you, but I am pretty sure the private method call will not be logged.
If you download the SpringSource Tool Suite, you get nice tools for aspect visualisation and testing. You should also read the AspectJ book, even if you only want to use Spring AOP. It's a great book.
BTW: you will obviously want to use a real logger, not system.out. You can either define one per aspect, or (only with real aspectj) you can introduce it as a static member in the target class to get per-class logging. A killer feature of AspectJ in my opinion.
Related
May be I'm not thinking hard enough or the answer is really elusive. Quick scenario (Try the code out. It compiles).
Consider a legacy interface
public interface LegacyInterfaceNoCodeAvailable{
void logInfo(String message);
}
The consider a legacy implementation of the interface above
public abstract class LegacyClassNoCodeAvailable implements LegacyInterfaceNoCodeAvailable{
public abstract void executeSomething();
public void rockItOldSchool(){
logInfo("bustin' chops, old-school style");
}
#Override
public void logInfo(String message){
System.out.println(message);
}
}
Now I come in as this ambitious person and writes a class for a 'New' system but that runs inside the 'Legacy' framework, hence I have to extend the legacy base class.
public class lass SpankingShiny extends LegacyClassNoCodeAvailable{
public void executeSomething(){
rockItOldSchool();
logInfo("I'm the King around here now");
System.out.println("this new stuff rocks!!");
}
}
Everything works great, just like you would expect:
SpankingShiny shiny = new SpankingShiny();
shiny.executeSomething();
The above code yields (as expected):
bustin' chops, old-school style
I'm the King around here now
this new stuff rocks!!
Now as you can see, the 'System.out.println()' faithfully prints the desired output. But I wish to replace the 'System.out.println()' with a logger.
Problem:
I'm unable to have the CGLIB proxy intercept the method to 'logInfo(string)' and have it print out my desired message through a logger (I have done the logging configuration right by the way). That method invocation 'apparently' does not hit the proxy.
Code:
public class SpankingShinyProxy implements MethodInterceptor{
private SpankingShiny realShiny;
private final Logger logger = Logger.getLogger(SpankingShinyProxy.class);
public SpankingShinyProxy(SpankingShiny realShiny) {
super();
this.realShiny = realShiny;
}
#Override
public Object intercept(Object proxyObj, Method proxyMethod, Object[] methodParams, MethodProxy methodProxy) throws Throwable {
String methodName = proxyMethod.getName();
if("logInfo".equals(methodName)){
logger.info(methodParams[0]);
}
return proxyMethod.invoke(realShiny, methodParams);
}
public static SpankingShiny createProxy(SpankingShiny realObj){
Enhancer e = new Enhancer();
e.setSuperclass(realObj.getClass());
e.setCallback(new SpankingShinyProxy(realObj));
SpankingShiny proxifiedObj = (SpankingShiny) e.create();
return proxifiedObj;
}
}
Main method:
public static void main(String... args) {
SpankingShiny shiny = new SpankingShiny();
shiny.executeSomething();
SpankingShiny shinyO = SpankingShinyProxy.createProxy(shiny);
shinyO.executeSomething();
}
The above code yields (NOT as expected):
bustin' chops, old-school style
I'm the King around here now
this new stuff rocks!!
bustin' chops, old-school style
I'm the King around here now
this new stuff rocks!!
Where would I be going wrong?
Thanks!
I had the same problem. In my case, the realObj was a proxy itself (a Spring Bean - a #Component).
So what I had to do was change the .setSuperClass() part in:
Enhancer e = new Enhancer();
e.setSuperclass(realObj.getClass());
e.setCallback(new SpankingShinyProxy(realObj));
SpankingShiny proxifiedObj = (SpankingShiny) e.create();
I changed:
e.setSuperclass(realObj.getClass());
To:
e.setSuperclass(realObj.getClass().getSuperClass());
This worked because, as said, realObj.getClass() was a CGLIB proxy itself, and that method returned a crazy-name-CGLIB-generated class, such as a.b.c.MyClass$$EnhancerBySpringCGLIB$$1e18666c. When I added .getSuperClass() it returned the class it should have been returning in the first place.
Well, first of all, you are lucky that your proxy is not hit. If you were referencing the actual proxy within intercept, you would end up with an endless loop since your reflective method incocation would get dispatched by the same SpankingShinyProxy. Again and again.
The proxy is not working since you simply delegate the method call executeSomething on your proxy to some unproxied object. You must not use realObj. All method calls must be dispatched by your proxy, also those method calls that are invoked by the must hit the proxy itself!
Change the last line in your intercept method to methodProxy.invokeSuper(proxyObj, args). Then, construct your object by using the Enhancer. If your constructor for SpankingShiny does not need arguments, calling create without any arguments if fine. Otherwise, supply the objects you would normally supply to the constructor to the create method. Then, only use the object that you get from create and you are good.
If you want more information on cglib, you might want to read this blog article: http://mydailyjava.blogspot.no/2013/11/cglib-missing-manual.html
I am kinda new to Aspects and I am trying to surround a function with an aspect but I can't get it called.
The method signature is as follows:
public <T> T get(String uri, List<BasicNameValuePair> nameValuePairs, final Class<T> clazz)
and it's defined in class with fully-qualified name:
com.X.Y.infrastructure.rest.RestClient
And the Aspect
#Aspect
public class WebRequestTimeLoggerAspect {
#Around("execution(* com.X.Y.infrastructure.rest.RestClient.get(..))")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("hijacked method : " + joinPoint.getSignature().getName());
System.out.println("hijacked arguments : " + Arrays.toString(joinPoint.getArgs()));
System.out.println("Around before is running!");
Object ret = joinPoint.proceed();
System.out.println("Around after is running!");
return ret;
}
}
I just cant seem to find the problem.
Everything is declared and spring loads both beans into the container.
If possible. How do I add another function to the same execute deceleration?
Thanks.
Update:
So I have managed to do some progress, apparently I needed to Change project configuration to support Aspectj (in eclipse) but now I am getting the following error:
applying to join point that doesn't return void: method-execution(java.lang.Object com.X.Y.infrastructure.rest.RestClient.get(java.lang.String, java.util.List, java.lang.Class))
I will assume that i need to specify the return argument. I thought * will catch all, but that seems to be for void.
So how does the point cut should look for generics like that?
Thanks again
Another update
Following the comment I have added a return value to the method. But the aspect is still not firing.
By request the spring configuration:
<aop:aspectj-autoproxy />
<bean id="webRequestTimeLoggerAspect" class="com.X.Y.infrastructure.rest.WebRequestTimeLoggerAspect" />
And in fact I don't believe I need to declare a bean for that aspect because I have package scan. But just in case.
I happen to have an #Aspect that declares a method that is intercepted by a pointcut of another aspect. The aspects are created with compile-time weaving and the container is instantiated using Spring.
I annotated my aspect with #Configurable to tell Spring that the component is being created outside the container. I happen to have a static reference to a Log object in this aspect too. To summarize, the code looks something like this
#Aspect
#Configurable
public class MyAspect {
private static final Log log = LogFactory.getClass(MyAspect.class);
// Autowired dependencies
// Pointcuts and advice
// Happens to be a pointcut of some other Advice
#Asynchronous
private Object someMethod(...) {
}
}
During AspectJ compilation, I do not see the message I expect, which looks something like this:
weaveinfo Join point 'method-call(java.lang.Object mypackage.someMethod(...))' in Type 'mypackage.MyAspect' (MyAspect.java:30) advised by around advice from 'anotherpackage.AsynchronousAspect' (from AsynchronousAspect.java))
As expected, the third-party advice is never invoked at this point. However, if I add a simple log entry to my advice, something like
log.debug("Join point invoked!");
Then the compilation happens correctly and all the aspects are wired (including my third party dependencies) and invoked correctly.
What does adding a log entry do to change my assumptions?
What you want to do is pretty straightforward and not dangerous at all if you know what you are doing. Please apologise that I am not a Spring user and that I prefer native AspectJ syntax to #AspectJ. This little sample runs just fine:
public class Application {
public static void main(String[] args) {
System.out.println("Hello world!");
someMethod();
}
private static void someMethod() {
System.out.println("Doing something ...");
}
}
public aspect FirstAspect {
void around() : execution(void *..main(..)) {
System.out.println(thisJoinPointStaticPart + ": " + someMethod("before", "main"));
proceed();
System.out.println(thisJoinPointStaticPart + ": " + someMethod("after", "main"));
}
private Object someMethod(String position, String methodName) {
return position + " " + methodName;
}
}
public aspect SecondAspect {
Object around() : execution(* *..someMethod(..)) {
System.out.println(thisJoinPointStaticPart + ": before someMethod");
Object result = proceed();
System.out.println(thisJoinPointStaticPart + ": after someMethod");
return result;
}
}
The result is as expected:
execution(Object FirstAspect.someMethod(String, String)): before someMethod
execution(Object FirstAspect.someMethod(String, String)): after someMethod
execution(void Application.main(String[])): before main
Hello world!
execution(void Application.someMethod()): before someMethod
Doing something ...
execution(void Application.someMethod()): after someMethod
execution(Object FirstAspect.someMethod(String, String)): before someMethod
execution(Object FirstAspect.someMethod(String, String)): after someMethod
execution(void Application.main(String[])): after main
If furthermore you are concerned with thhe order in which aspects are applied/executed, please use declare precedence.
If you experience problems with accessing e.g. private members, you need to use a privileged aspect.
Update: Changed usage of thisEnclosingJoinPointStaticPart to thisJoinPointStaticPart. That was just a copy & paste error. The result is the same on execution join points, but anyway the correction shows better the code's intent.
Its being a few months since I am working with java legacy code, this are some of the things I am dealing with:
0% test coverage.
Huge functions in occasions I even saw some with more than 300 lines of code.
Lots of private methods and in occasions static methods.
Highly tight coupled code.
At the beginning I was very confused, I found difficult to use TDD in the legacy. After doing katas for weeks and practicing my unit testing and mocking skills, my fear has decreased and I feel a bit more confident. Recently I discovered a book called: working effectivelly with legacy, I didn't read it, I just had a look at the table of contents and I discovered something that is new for me, The Seams. Apparently this is very important when working in the legacy.
I think that this Seams could help me alot in breaking dependencies and make my code testeable so I can increase the code coverage and make my unit testing more precise.
But I have a lot of doubts:
Can somebody explain me the difference between a seam and a mock?
Do Seams, break TDD rules in what regards not touching production code, before is tested?
Could you show me some simple example that compares a Seam and a Mock?
Below I would like to paste an example I did today where I tried to break a dependency with the goal of making the code testeable and finally increasing test coverage. I would appreciate if you could comment a bit if you see some mistakes?
This is how the legacy code looked like at the beginning:
public class ABitOfLegacy
{
private String sampleTitle;
String output;
public void doSomeProcessing(HttpServletRequest request) {
String [] values = request.getParameterValues(sampleTitle);
if (values != null && values.length > 0)
{
output = sampleTitle + new Date().toString() + values[0];
}
}
}
If I just add a unit test that calls that method and asserts that variable output, has a certain value after the call,then I would be making a mistake, because I am not unit testing, I would be doing integration testing. So what I need to do, Is get rid of the dependency I have in the parameter. To do So, I replace the parameter with an interface:
public class ABitOfLegacy
{
private String sampleTitle;
String output;
public void doSomeProcessing(ParameterSource request) {
String [] values = request.getParameters(sampleTitle);
if (values != null && values.length > 0)
{
output = sampleTitle + new Date().toString() + values[0];
}
}
}
This is how the interface looks like:
public interface ParameterSource {
String[] getParameters(String name);
}
The next thing I do, is create my own implementation of that interface but I include the HttpServletRequest as a global variable and I implement the method of the interface using the method/s of HttpServletRequest:
public class HttpServletRequestParameterSource implements ParameterSource {
private HttpServletRequest request;
public HttpServletRequestParameterSource(HttpServletRequest request) {
this.request = request;
}
public String[] getParameters(String name) {
return request.getParameterValues(name);
}
}
Until this point, I think that all the modifications on the production code were safe.
Now I create the Seam in my test package. If I understood well, now I am able to safely change the behavoir of the Seam. This is how I do it:
public class FakeParameterSource implements ParameterSource {
public String[] values = {"ParamA","ParamB","ParamC"};
public String[] getParameters(String name) {
return values;
}
}
And the final step, would be to get support from the Seam, to test the original behavoir of the method.
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import code.ABitOfLegacyRefactored;
import static org.hamcrest.Matchers.*;
public class ABitOfLegacySpecification {
private ABitOfLegacy aBitOfLegacy;
private String EMPTY = null;
#Before
public void initialize() {
aBitOfLegacy = new ABitOfLegacy();
}
#Test
public void
the_output_gets_populated_when_the_request_is_not_empty
() {
FakeParameterSource fakeParameterSource = new FakeParameterSource();
aBitOfLegacy.doSomeProcessing(fakeParameterSource);
assertThat(aBitOfLegacy.output,not(EMPTY));
}
#Test(expected=NullPointerException.class)
public void
should_throw_an_exception_if_the_request_is_null
() {
aBitOfLegacy.doSomeProcessing(null);
}
}
This will give me 100% test coverage.
I appreciate your thoughts:
Did I break the dependency correctly?
Are the unit tests missing something?
What could be done better?
Is this example good enough to help me understand the difference between a Seam and a Mock?
How could a mock help me here if I don't use the Seam?
A seam is a place in the code that you can insert a modification in behavior. You created a seam when you setup injection of your dependency.
One way to take advantage of a seam is to insert some sort of fake. Fake's can be hand-rolled, as in your example, or be created with a tool, like Mockito.
So, a mock is a type of fake, and a fake is often used by taking advantage of a Seam.
As for your tests and the way you broke the dependency, that's pretty much how I would have done it.
Seams
A seam is a place that allows you to modify the behavior without modifying the code.
In your example, the following is an example of an Object seam (if i'm not mistaken). It allows you to pass in a different object without having to change the code. hence it is a type of seam.
public void doSomeProcessing(ParameterSource request) {..}
By making the parameter an abstract type (instead of a concrete class), you have introduced a seam. The seam now allows you to modify the behavior of the method without editing its code - i.e. at the place of invokation, I can pass in a different object and make the method do something else.
Mocks
Now instead of creating your custom fake (creating a subtype of the interface), you could using a Mock framework to do something like this
Mocks also support asserting whether specific methods were called on it, argument matching and other nifty functionality to be consumed by tests. Less test code to maintain. Mocks are primarily used to assert that a specific call is being made to a dependency. In your example, you seem to be in need of a Stub, you just want to return a canned value.
Pardon my rusty JMock..
#Test
public void
the_output_does_not_get_populated_when_the_request_is_empty
() {
Mockery context = new Mockery();
final ParameterSource mockSource = context.mock(ParameterSource.class)
context.checking(new Expectations(){{
oneOf(mockSource).getParameters();
will(returnValue(new string[]{"ParamA","ParamB","ParamC"} );
}});
aBitOfLegacy.populate(mockSource);
assertThat(aBitOfLegacy.output,not(EMPTY));
}
in .Net
var mockSource = new Mock<ParameterSource>();
mockSource.Setup(src => src.GetParameters())
.Returns(new []{"ParamA","ParamB","ParamC"});
I have integration tests (load context) and unit tests running together. My code does aspectj compile time weaving using spring.
My problem is that my declared advises also run during some of my unit tests. This kills the notion of a unit test, which is why I would like to disable them.
Is there something I can put on the pointcut declaration, some method I can call, some spring configuration, or maven command that disables these advises for something like all *UnitTest.java?
Thanks for the help.
example:
I have the following unit test:
#RunWith(MockitoJUnitRunner.class)
public class CompanyServiceImplTest {
#Test
public void createCampaignTest() throws Exception {
when(companyDaoMock.saveCompany(any(Campaign.class))).thenReturn(77L);
Long campaignId = companyService.createCampaign(campaignMock);
assertEquals(Long.valueOf(77L), Long.valueOf(campaignId));
}
}
and following service method:
#Override
#Transactional
#EventJournal(type = EventType.CAMPAIGN_CREATE, owner = EventOwner.TERMINAL_USER)
public Long createCampaign(Campaign campaign) {
return companyDao.saveCompany(campaign);
}
aspect:
#Aspect
public class EventJournalAspect {
#Autowired
private EventJournalService eventJournalService;
#Pointcut(value="execution(public * *(..))")
public void anyPublicMethod() {}
#Pointcut("within(com.terminal.service..*)")
private void inService() {}
#AfterReturning(pointcut = "anyPublicMethod() && inService() && #annotation(eventJournal) && args(entity,..)", returning = "id")
public void process(Object id, EventJournal eventJournal, AbstractDomainEntity entity)
throws Throwable {
if (eventJournal.type() != EventType.CAMPAIGN_PAYMENT || id != null) {
saveEvent(eventJournal, EventStatus.SUCCESS, entity, (Long) id);
}
}
#AfterThrowing(pointcut = "anyPublicMethod() && inService() && #annotation(eventJournal) && args(entity,..)", throwing="ex")
public void processException(EventJournal eventJournal, AbstractDomainEntity entity, Exception ex) throws Throwable {
saveEvent(eventJournal, EventStatus.FAILURE, entity, null);
}
private void saveEvent(EventJournal eventJournal, EventStatus status, AbstractDomainEntity entity, Long persistentId) {
EventType type = eventJournal.type();
EventOwner owner = eventJournal.owner();
eventJournalService.saveEvent(type, owner, EventStatus.SUCCESS, entity, persistentId);
}
}
When test executes - eventJournalService is null. Thus I see NullPointerException
The answer is simple: You want to use an if() pointcut expression.
Update (after the question has also been updated): The originally provided link above should contain enough information, but for what it is worth, a short explanation and a simple example:
An if() pointcut is a static aspect method returning a boolean. If the return value is true, it means that any combined pointcut like myPointcut() && if() matches as long as myPointcut() matches. For a return value of false the whole combined pointcut does not match, effectively deactivating any advice connected to the pointcut.
So what can you do in a static if() pointcut?
evaluate a static boolean member of some tool class like TestMode.ACTIVE which is only true during unit or integration testing
evaluate an environment variable which is only set during testing
evaluate a Java system property which is only set during testing
and many more things
If you want to do something fancier (and trickier) and performance is not so important, you can also try to dynamically determine whether the auto-wired aspect member variable equals null or not and only activate your pointcuts if the injected object is actually present. The only problem here is how to determine a member variable from a static method. I have no idea about Spring AOP, but in plain AspectJ there is the helper class Aspects with several overloaded methods named aspectOf(..). Assuming that your aspect is instantiated as a singleton, you could do something like this:
#Pointcut("if()")
public static boolean isActive() {
return Aspects.aspectOf(PerformanceMonitorAspect.class).eventJournalService != null;
}
// ...
#AfterReturning(pointcut = "isActive() && anyPublicMethod() && inService() && #annotation(eventJournal) && args(entity,..)", returning = "id")
// ...
#AfterThrowing(pointcut = "isActive() && anyPublicMethod() && inService() && #annotation(eventJournal) && args(entity,..)", throwing="ex")
// ...
I can only guess:
The first thing is to have a separate Spring applicationContext-test.xml,
without component-scan;
In maven you can add a phase runtime excluding weaving jars for test.
Compile time weaving would inline the advice calls in the targeted methods identified by the pointcuts that you have. I personally feel that it is good to unit test with the compile time weaving in place, because at runtime your unit does include the class with the advice inlined?
The thought I have to not include advice would be to have two different compile targets, one with compile time weaving, and one without, you should be able to do this through maven profiles, a dev profile not weaving advice in and a prod profile to weave the aspects in.
You can write a method that returns if current execution was launched using JUnit framework.
The method can check stack trace with Thread.currentThread().getStackTrace() and search for MockitoJUnitRunner presence.
I tested this solution using a SpringJUnit4ClassRunner but I think could work with MockitoJUnitRunner.
Also, you can have got a static boolean field like:
private static boolean TEST_ENVIRONMENT = false;
In a class present in your project (not in your tests) and check the value in the control method instead of use stack trace.
When you run your tests, you can use #BeforeClass annotation to set TEST_ENVIRONMENT = true.
This solution only gives you a way to know if your code is running from a test or not.