AspectJ: Access private fields? - java

I want to use an aspect to add getter and setter for a private id field. I know how to add a method through an aspect, but how can I access the private id field?
I thoght that I just have to make the aspect provileged. I tried the following code, but the aspect cannot access the id field.
public privileged aspect MyAspect {
public String Item.getId(){
return this.id;
}
A possibility would be to user reflection like shown in this blog post: http://blog.m1key.me/2011/05/aop-aspectj-field-access-to-inejct.html
Is reflection the only possibility or is there a way to do it with AspectJ?

Are you sure you can't ? I just tested and it ran. Here's my full code:
package com.example;
public class ClassWithPrivate {
private String s = "myStr";
}
==========
package com.example.aspect;
import com.example.ClassWithPrivate;
privileged public aspect AccessPrivate {
public String ClassWithPrivate.getS() {
return this.s;
}
public void ClassWithPrivate.setS(String str) {
this.s = str;
}
}
==========
package com.example;
public class TestPrivate {
public static void main(String[] args) {
ClassWithPrivate test = new ClassWithPrivate();
System.out.println(test.getS());
test.setS("hello");
System.out.println(test.getS());
}
}
If for some reason, that does not work for you, you can use reflection, or another way as described here:
https://web.archive.org/web/20161215045930/http://blogs.vmware.com/vfabric/2012/04/using-aspectj-for-accessing-private-members-without-reflection.html
However, according to the benchmarks, it may not be worth it.

Related

variable referencing in getter method

Is the following declaration in getter method legal?
App.java
package com.abc.xyz.db.main;
public class App
{
public static String zipfilename;
........................
}
PatchFileDetailsForIndividualFilesInPatch.java
import com.abc.xyz.db.main.App;
public class PatchFileDetailsForIndividualFilesInPatch {
private String patchFileName;
public String getPatchFileName() {
return patchFileName;
}
public void setPatchFileName(String patchFileName) {
this.patchFileName = App.zipfilename;
}
.....................
}
Even though IDE is not complaining, yet I am feeling confused whether is this the correct way of passing variables declared in one class to a getter method in another class.
I am new to Java. I know this is an extremely rudimentary question but your guidance would help me :)
Regards

Testing a static method which needs to mock a member method and suppress singleton constructor with PowerMockito

I would like to test on a static method (e.g. HobbyUtil.java:shareReadContext(int lineNumber) ) which would ask a Singleton class (e.g. Shelf.java ) to get an object (e.g. a Book ) for further retrieving values (e.g. lines of context ) from that returned object.
However, during the mock case execution, the ctor of that Singleton class (i.e. Shelf.java ) would trigger several other Singleton classes (e.g. LibraryA.java ) in a chain; and one of them would trigger java.lang.ExceptionInInitializerError.
I think mocking/spying those irrelevant Singleton classes is out of the spirit of Mock Test as they are irrelevant to my testing scope.
So I decided to suppress the private ctor of this Singleton class (i.e. Shelf.java ) and just let the getter method in this class (i.e. getBook() ) to return a mocked object with my preferred behavior, which would perfectly match my test case.
The problem I've encountered is that I follow this link to suppress the ctor of Shelf.java successfully:
suppress a singleton constructor in java with powermock
but I couldn't figure out the way to let this getter method return what I want properly (i.e. return a mocked object).
The version of PowerMockito, hamcrest and JUnit I am using are listed here for a reference:
<dependency><groupId>org.powermock</groupId><artifactId>powermock-module-junit4</artifactId><version>1.7.0</version><scope>test</scope></dependency>
<dependency><groupId>org.powermock</groupId><artifactId>powermock-api-mockit02</artifactId><version>1.7.0</version><scope>test</scope></dependency>
<dependency><groupId>org.hamcrest</groupId><artifactId>hamcrest-all</artifactId><version>1.3</version><scope>test</scope></dependency>
<dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency>
I have also replicated the case with below sample classes to show what I have encountered (HobbyUtilTest.java is the class for my test case):
Supposed there are 5 Java classes in the application:
public interface Book {
public String getContext(int lineNumber);
}
public class Shelf {
private static final Shelf shelf = new Shelf();
private String BOOK_NAME = "HarryPotter";
private Book book = null;
private Shelf() {
book = LibraryA.getInstance().getBook(BOOK_NAME);
// many relatively complicated logics are here in actual class ...
// I want to suppress this ctor
}
public static Shelf getInstance() {
return shelf;
}
public Book getBook() {
return book; // I want to return my mocked object while calling this method in test case
}
}
public class HobbyUtil {
public static String shareReadContext(int lineNumber){
String context = "";
Book book = Shelf.getInstance().getBook();
for (int i = 0 ; i < lineNumber; i++) {
context += book.getContext(i);
}
return context;
}
private HobbyUtil() {}
}
public class LibraryA {
private static final LibraryA library = new LibraryA();
private Book book;
private LibraryA() {
throw new java.lang.ExceptionInInitializerError();
}
public static LibraryA getInstance() {
return library;
}
public Book getBook(String bookName) {
return book;
}
}
public class BookImpl implements Book{
private String bookName = null;
BookImpl(String bookName){
this.bookName = bookName;
}
#Override
public String getContext(int lineNumber) {
return lineNumber + ": Context";
}
}
And I would test the static method shareReadContext(int lineNumber) in HobbyUtil.java with HobbyUtilTest.java
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.powermock.api.mockito.PowerMockito.when;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
#RunWith(PowerMockRunner.class)
#PrepareForTest(value = { Shelf.class })
public class HobbyUtilTest {
#Test
public void testShareReadContext() throws Exception {
Book mockBook = PowerMockito.mock(Book.class);
when(mockBook.getContext(anyInt())).thenReturn("context for test");
PowerMockito.suppress(PowerMockito.constructor(Shelf.class));
//PowerMockito.spy(Shelf.class);
//when(Shelf.getInstance().getBook()).thenReturn(mockBook); // does not work
//PowerMockito.doReturn(mockBook).when(Shelf.getInstance().getBook()); // does not work
//PowerMockito.when(Shelf.class, "getBook").thenReturn(mockBook); // does not work
//TODO any approach for it?
String context = HobbyUtil.shareReadContext(1);
assertThat(context, is("context for test"));
}
}
Could anyone please help to suggest how could I let the line Book book = Shelf.getInstance().getBook(); in HobbyUtil.java return my mocked object (i.e. mockBook )?
Assuming that your Shelf class is in a package test, this should work:
#RunWith(PowerMockRunner.class)
#SuppressStaticInitializationFor("test.Shelf")
public class HobbyUtilTest {
#Test
public void testShareReadContext() throws Exception {
Book book = Mockito.mock(Book.class);
Mockito.when(book.getContext(Mockito.anyInt())).thenReturn("context for test");
Shelf shelf = Mockito.mock(Shelf.class);
Mockito.when(shelf.getBook()).thenReturn(book);
PowerMockito.mockStatic(Shelf.class);
PowerMockito.when(Shelf.getInstance()).thenReturn(shelf);
String context = HobbyUtil.shareReadContext(1);
Assert.assertEquals("context for test", context);
}
}
You'll want to suppress the initialization of the fields of Shelf (not only the constructor) and afterwards simply define the appropriate mocks for the Shelf.getInstance() (and subsequent) methods.
Ps.:
#SuppressStaticInitializationFor seems to be equivalent to:
#RunWith(PowerMockRunner.class)
#PrepareForTest(Shelf.class)
public class HobbyUtilTest {
#Test
public void testShareReadContext() throws Exception {
PowerMockito.suppress(PowerMockito.fields(Shelf.class));
PowerMockito.suppress(PowerMockito.constructor(Shelf.class));
// ...
}
}
Let me post an alternative approach here for a reference after searching over other resources again in the meanwhile.
After referencing this site:
https://blog.jayway.com/2009/10/28/untestable-code-with-mockito-and-powermock/
I found that by adding the following line of code to the TODO section of original test case could also help to achieve the purpose:
PowerMockito.stub(PowerMockito.method(Shelf.class,
"getBook")).toReturn(mockBook);
The downside of this approach is that the method name need to be hard coded as String value, IDE rename automation function would not be effective on this situation.

Importing two utility classes with same name. Feature or useless?

For two utility classes with the same names, which contain only static methods, I proceeded as follows:
Simply imported the first
Created an instance of the second class.
Example:
package util1;
public class Utility {
public static void method() {
System.out.println("First Utility. static method");
}
}
package util2;
public class Utility {
public static void method() {
System.out.println("Second Utility. static method");
}
}
import util1.Utility;
public class Component {
private static final util2.Utility anotherUtility = new util2.Utility();
public static void usedByReflection() {
Utility.method();
anotherUtility.method();
}
}
Now I don't need to write a full second util-class name for invoke its methods, but maybe I did not foresee something...?
P.S:
The methods of the class Component are called through a reflection by a certain BlackBox. All the multithread-safe features are in BlackBox.
UPD: I have found better trick:
import util1.Utility;
public class Component {
private static final util2.Utility anotherUtility = null; // There are some changes
public static void usedByReflection() {
Utility.method();
anotherUtility.method();
}
}
Now I dont create new object, but is it possible to use it without any bugs?
IMO, this is confusing and could much more clearly be handled by something like:
public class CombinedUtilityComponent {
public static void usedByReflection() {
util1.Utility.method();
util2.Utility.method();
}
}
Or, better yet, in your code you can just fully qualify the class names and they become unique names without any confusing tricks.
Yes, this works. I wouldn't do it, though.
You're calling a static method as if it were an instance method. anotherUtility.method() has a useless reference to anotherUtility.
You also have an unnecessary instantiation of util2.Utility. This technique wouldn't work if the default constructor were disabled.

AspectJ annotation on field that triggers #Before advice

I have already written AspectJ aspects that perform #Around advice triggered by method annotations. Now I want to do the same, but where fields are annotated instead of methods. So with each method invocation of the class below, it must set the accountSummary field to the correct implementation. Is there a way to accomplish this? I presume using #Before advice would be the best way of going about it. Using CDI is not an option - the solution must use AspectJ.
public class PoolableBusinessLogic {
#InjectServiceClientAdapter(legacy=LegacyAccountSummary.class,new=NewAccountSummary.class)
private AccountSummary accountSummary;
public void foo() {
// use correct accountSummary impl, decided in #Before code
}
public void bar() {
// use correct accountSummary impl, decided in #Before code
}
}
I am not sure what exactly you want to achieve, so I am presenting two alternative solutions.
First, let us create some application classes in order to have a fully compileable sample:
package de.scrum_master.app;
public interface AccountSummary {
void doSomething();
}
package de.scrum_master.app;
public class LegacyAccountSummary implements AccountSummary {
#Override
public void doSomething() {
System.out.println("I am " + this);
}
}
package de.scrum_master.app;
public class NewAccountSummary implements AccountSummary {
#Override
public void doSomething() {
System.out.println("I am " + this);
}
}
package de.scrum_master.app;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#Retention(RetentionPolicy.RUNTIME)
public #interface InjectServiceClientAdapter {
Class<?> legacyImpl();
Class<?> newImpl();
}
package de.scrum_master.app;
public class PoolableBusinessLogic {
#InjectServiceClientAdapter(legacyImpl = LegacyAccountSummary.class, newImpl = NewAccountSummary.class)
private AccountSummary accountSummary;
public void foo() {
accountSummary.doSomething();
}
public void bar() {
System.out.println("Account summary is " + accountSummary);
}
}
Now we need an entry point:
package de.scrum_master.app;
public class Application {
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
PoolableBusinessLogic businessLogic = new PoolableBusinessLogic();
businessLogic.foo();
businessLogic.bar();
System.out.println();
}
}
}
Obviously this yields an error because the member accountSummary has not been initialised:
Exception in thread "main" java.lang.NullPointerException
at de.scrum_master.app.PoolableBusinessLogic.foo(PoolableBusinessLogic.java:8)
at de.scrum_master.app.Application.main(Application.java:7)
Now we have two options, depending on what you want to achieve:
Option A: dynamic injection
Scenario: For each field access (even in the same PoolableBusinessLogic instance) decide dynamically what type of object instance to return. Here in this example I will just be randomising in order to simulate another if-else criterion.
BTW, I hope it is okay that I use the more expressive native AspectJ syntax. You can easily convert the aspect to annotation style.
package de.scrum_master.aspect;
import java.util.Random;
import org.aspectj.lang.SoftException;
import de.scrum_master.app.InjectServiceClientAdapter;
public aspect DynamicInjectionAspect {
private static final Random RANDOM = new Random();
Object around(InjectServiceClientAdapter adapterAnn) :
get(* *) && #annotation(adapterAnn)
{
try {
Class<?> implClass = RANDOM.nextBoolean() ? adapterAnn.legacyImpl() : adapterAnn.newImpl();
return implClass.newInstance();
} catch (Exception e) {
throw new SoftException(e);
}
}
}
This yields the following output:
I am de.scrum_master.app.LegacyAccountSummary#4d9cfefb
Account summary is de.scrum_master.app.NewAccountSummary#7e28388b
I am de.scrum_master.app.NewAccountSummary#2986e62
Account summary is de.scrum_master.app.LegacyAccountSummary#6576e542
I am de.scrum_master.app.NewAccountSummary#60c58418
Account summary is de.scrum_master.app.LegacyAccountSummary#4763754a
I am de.scrum_master.app.NewAccountSummary#52a971e3
Account summary is de.scrum_master.app.NewAccountSummary#7274187a
I am de.scrum_master.app.LegacyAccountSummary#23f32c4a
Account summary is de.scrum_master.app.LegacyAccountSummary#31e0c0b6
As you can see, within each of the five output groups (i.e. for each PoolableBusinessLogic instance) there are different account summary object IDs and sometimes (not always) even different class names.
Option B: static injection
Scenario: Per PoolableBusinessLogic instance decide dynamically what type of object instance to statically assign to the annotated member if its value is null. After that, do not overwrite the member anymore but return the previously initialised value. Again I will just be randomising in order to simulate another if-else criterion.
Attention: Do not forget to deactivate the first aspect, e.g. by prepending if(false) && to its pointcut. Otherwise the two aspects will be conflicting with each other.
package de.scrum_master.aspect;
import java.lang.reflect.Field;
import java.util.Random;
import org.aspectj.lang.SoftException;
import de.scrum_master.app.InjectServiceClientAdapter;
public aspect StaticInjectionAspect {
private static final Random RANDOM = new Random();
before(InjectServiceClientAdapter adapterAnn, Object targetObj) :
get(* *) && #annotation(adapterAnn) && target(targetObj)
{
try {
Field field = targetObj.getClass().getDeclaredField(thisJoinPoint.getSignature().getName());
field.setAccessible(true);
if (field.get(targetObj) != null)
return;
Class<?> implClass = RANDOM.nextBoolean() ? adapterAnn.legacyImpl() : adapterAnn.newImpl();
field.set(targetObj,implClass.newInstance());
} catch (Exception e) {
throw new SoftException(e);
}
}
}
This is a bit uglier because it involves using reflection for finding the member field. Because it might be (and in our example really is) private we need to make it accessible before doing anything with it.
This yields the following output:
I am de.scrum_master.app.NewAccountSummary#20d1fa4
Account summary is de.scrum_master.app.NewAccountSummary#20d1fa4
I am de.scrum_master.app.NewAccountSummary#2b984909
Account summary is de.scrum_master.app.NewAccountSummary#2b984909
I am de.scrum_master.app.LegacyAccountSummary#1ae3043b
Account summary is de.scrum_master.app.LegacyAccountSummary#1ae3043b
I am de.scrum_master.app.LegacyAccountSummary#2e2acb47
Account summary is de.scrum_master.app.LegacyAccountSummary#2e2acb47
I am de.scrum_master.app.LegacyAccountSummary#7b87b9fe
Account summary is de.scrum_master.app.LegacyAccountSummary#7b87b9fe
Now the output looks different: Within each of the five output groups (i.e. for each PoolableBusinessLogic instance) both output lines show exactly the same object ID.
For Option A: dynamic injection in kriegaex's answer, the annotation-style aspect will look like this:
#Aspect
public class InjectServiceClientAdapterAspect {
#Pointcut("get(* *) && #annotation(injectAnnotation)")
public void getServiceClientAdapter(InjectServiceClientAdapter injectAnnotation) {
}
#Around("getServiceClientAdapter(injectAnnotation)")
public Object injectServiceClientAdapter(final ProceedingJoinPoint joinPoint, final InjectServiceClientAdapter injectAnnotation) {
// injection code goes here
}

Can I test code that uses introspection with Mockito?

I'm writing unit tests for a block of code that uses introspection; specifically it calls getDeclaredField() on the class I want to mock and tries to get the value of the field. Is there a way to mock this with Mockito?
Mockito operates using the same introspection libraries you're trying to fool by creating a Mock. Even if you could cajole it to work, I'm not sure how easy it would be to understand or maintain.
I'd suggest creating a very small nested class and operating on it normally:
public class YourTest {
private static class SampleClass {
String field1;
int field2;
}
#Test public void introspectionWorks() {
yourSUT.process(new SampleClass());
}
}
Barring that, extract the difficult-to-mock call into a method you can stub easily:
public class YourSUT {
/* ... */
/* package */ Class<?> getFieldType(Object object, String fieldName) {
return object.getClass().getDeclaredField(fieldName).getType();
}
}
public class YourTest {
#Test public void introspectionWorks() {
YourSUT spy = Mockito.spy(yourSUT);
doReturn(String.class).when(spy).getFieldType(myObject, "someStringField");
}
}

Categories

Resources