I am trying to add a advice in my application so that the onEnter and onExit gets called when a method CassandraFunctions.loadObjectByKey is invoked during execution flow. I used below code to register a advice.
protected void instrument(boolean t) {
Instrumentation instrument = null;
// Get loader initialized in premain class
try {
Class<?> c = Class.forName("my.loader.InstrumentLoader");
java.lang.reflect.Method m = c.getMethod("getInstrument");
instrument = (Instrumentation) m.invoke(null);
} catch (Exception e) {
e.printStackTrace();
}
if(instrument == null) {
return;
}
// Add an advice
String clzName = CassandraFunctionsAdvice.class.getName();
new AgentBuilder.Default()
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.type(ElementMatchers.named("my.functions.CassandraFunctions"))
.transform(
new AgentBuilder.Transformer.ForAdvice()
.include(Class.class.getClassLoader())
.advice(ElementMatchers.named("loadObjectByKey"), clzName))
.installOn(instrument);
}
And the advice class looks like below:
public class CassandraFunctionsAdvice {
#Advice.OnMethodEnter
public static void onEnter(#Advice.Argument(0) String key) {
String debugText = "OnMethodEnter|loadObjectByKey|key=" + key;
System.out.println(debugText);
}
#Advice.OnMethodExit
public static void onExit(#Advice.Thrown Throwable throwable) {
String debugText = "OnMethodExit|loadObjectByKey";
System.out.println(debugText);
}
}
The class that is being instrumented looks like below:
public class CassandraFunctions {
public static Object loadObjectByKey(String key) {
....
return object;
}
}
The instrumented class my.functions.CassandraFunctions is loaded much before the function loadObjectByKey is called on a user request. I am not sure what is missing and why the advice is not getting invoked.
I have already answered your question on the GitHub issue:
The advice code is just a template. The private field is not visible to the code once it is inlined by Byte Buddy.
The question you need to ask yourself is: Could I copy-paste this code to the target class and would it still compile? If no, then you need to change your advice. If you want to manage shared state, you would need to move it to a class that is accessible to the class loader(s) in question and inject it into an appropriate location.
Related
I have a simple scenario in which am trying to verify some behavior when a method is called (i.e. that a certain method was called with given parameter, a function pointer in this scenario). Below are my classes:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
AppBootStrapper bootStrapper = context.getBean(AppBootStrapper.class);
bootStrapper.start();
}
}
#Component
public class AppBootStrapper {
private NetworkScanner networkScanner;
private PacketConsumer packetConsumer;
public AppBootStrapper(NetworkScanner networkScanner, PacketConsumer packetConsumer) {
this.networkScanner = networkScanner;
this.packetConsumer = packetConsumer;
}
public void start() {
networkScanner.addConsumer(packetConsumer::consumePacket);
networkScanner.startScan();
}
}
#Component
public class NetworkScanner {
private List<Consumer<String>> consumers = new ArrayList<>();
public void startScan(){
Executors.newSingleThreadExecutor().submit(() -> {
while(true) {
// do some scanning and get/parse packets
consumers.forEach(consumer -> consumer.accept("Package Data"));
}
});
}
public void addConsumer(Consumer<String> consumer) {
this.consumers.add(consumer);
}
}
#Component
public class PacketConsumer {
public void consumePacket(String packet) {
System.out.println("Packet received: " + packet);
}
}
#RunWith(JUnit4.class)
public class AppBootStrapperTest {
#Test
public void start() throws Exception {
NetworkScanner networkScanner = mock(NetworkScanner.class);
PacketConsumer packetConsumer = mock(PacketConsumer.class);
AppBootStrapper appBootStrapper = new AppBootStrapper(networkScanner, packetConsumer);
appBootStrapper.start();
verify(networkScanner).addConsumer(packetConsumer::consumePacket);
verify(networkScanner, times(1)).startScan();
}
}
I want to verify that bootStrapper did in fact do proper setup by registering the packet consumer(there might be other consumers registered later on, but this one is mandatory) and then called startScan. I get the following error message when I execute the test case:
Argument(s) are different! Wanted:
networkScanner bean.addConsumer(
com.spring.starter.AppBootStrapperTest$$Lambda$8/438123546#282308c3
);
-> at com.spring.starter.AppBootStrapperTest.start(AppBootStrapperTest.java:24)
Actual invocation has different arguments:
networkScanner bean.addConsumer(
com.spring.starter.AppBootStrapper$$Lambda$7/920446957#5dda14d0
);
-> at com.spring.starter.AppBootStrapper.start(AppBootStrapper.java:12)
From the exception, clearly the function pointers aren't the same.
Am I approaching this the right way? Is there something basic I am missing? I played around and had a consumer injected into PacketConsumer just to see if it made a different and that was OK, but I know that's certainly not the right way to go.
Any help, perspectives on this would be greatly appreciated.
Java doesn't have any concept of "function pointers"; when you see:
networkScanner.addConsumer(packetConsumer::consumePacket);
What Java actually compiles is (the equivalent of):
networkScanner.addConsumer(new Consumer<String>() {
#Override void accept(String packet) {
packetConsumer.consumePacket(packet);
}
});
This anonymous inner class happens to be called AppBootStrapper$$Lambda$7. Because it doesn't (and shouldn't) define an equals method, it will never be equal to the anonymous inner class that the compiler generates in your test, which happens to be called AppBootStrapperTest$$Lambda$8. This is regardless of the fact that the method bodies are the same, and are built in the same way from the same method reference.
If you generate the Consumer explicitly in your test and save it as a static final Consumer<String> field, then you can pass that reference in the test and compare it; at that point, reference equality should hold. This should work with a lambda expression or method reference just fine.
A more apt test would probably verify(packetConsumer, atLeastOnce()).consumePacket(...), as the contents of the lambda are an implementation detail and you're really more concerned about how your component collaborates with other components. The abstraction here should be at the consumePacket level, not at the addConsumer level.
See the comments and answer on this SO question.
I am writing a unit test for my below code
public class Class1 {
protected void execute(String a, String b) {
try{
process(a,b);
}
catch(Exception E){
Class2.write(e,Class1.class.getSimpleName())
}
}
private void process(String a, String b) {
validate(a,b);
// Doing some processing on a and b values
}
private void validate (String a, String b) {
if(a==null || a.isEmpty() || b==null || b.isEmpty())
throw new IllegalArgumentException("Input value cannot be null or empty");
}
}
For the above code, I am trying to write a UT which covers the exception use case. Below is my UT code,
#Test
public void test1(){
try {
PowerMockito.mockStatic(Class2.class);
PowerMockito.when(Class2.class, "write", Mockito.anyObject(), Mockito.anyString())
.thenCallRealMethod();
Class1 class1 = new Class1();
Class2.write(new IllegalArgumentException("Input value cannot be null or empty"),Class1.class.getSimpleClassName());
PowerMockito.verifyStatic(Class2.class, VerificationModeFactory.times(1));
class1.execute(Mockito.anyString(),Mockito.anyString());
} catch (Exception e) {
e.printStackTrace();
Assert.fail(e.getMessage());
}
}
I am getting the below exception when I execute the above test
Argument(s) are different! Wanted:
Class2.write{
java.lang.IllegalArgumentException:Input value cannot be null or empty,
Class1
}
Actual invocation has different arguments:
Class2.write{
java.lang.IllegalArgumentException:Input value cannot be null or empty,
Class1
}
Can someone please help me on resolving this issue?
I really appreciate your help and time
Thanks in Advance
Your Problem:
IllegalArgumentException does not use the string message for equality. It would be safer to test the string message or the class type. I would prefer that the test detect the type rather than the message, as the string message should not be used for control flow, it is an implementation detail.
System.out.println(Objects.equals(
new IllegalArgumentException(),
new IllegalArgumentException()));
// false
System.out.println(Objects.equals(
new IllegalArgumentException().getClass(),
new IllegalArgumentException().getClass()));
// true
So to mock this I would use matchers:
any(IllegalArgumentException.class), eq(Class1.class.getSimpleName())
Issues with your design:
I'm going to end with an argument against how this code is structured, being that it is not built around dependency injection. Rather than calling the static method Class2::write, you could be calling an instance method.
For example, create the interface:
public interface Writer {
void write(Exception e, String source);
}
You can now refactor the class to provide two ctors, one that accepts any writer, and one that defaults to Class2.
public class Class1 {
private final Writer writer;
public Class1() {
this(Class2::write);
}
public Class1(Writer writer) {
this.writer = writer;
}
protected void execute(String a, String b) {
try {
process(a,b);
}
catch (Exception E) {
writer.write(e, Class1.class.getSimpleName());
}
}
...
}
Using this strategy you can now simply create an instance mock of Writer. This avoids having to mock as static method which changes the bytecode of your application, and also make your class more flexible as it can support many different writer implementations now. Anything that is modifying the bytecode of the application should be used very sparingly, such as replacing static method calls, does not truly validate the runtime execution of your code.
In my opinion, the majority of the PowerMockito/PowerMock only help verify code which was not built with testability / flexibility in mind. You shouldn't need to use anything outside of the Mockito/EasyMock tool-set for well structured code. There are some exceptions but the tool-set should be used very sparingly.
I am having troubles while trying to refactor exception handling logic in an helper class.
My code uses a repository which accesses a database and might throw the custom exception RepositoryException. If such exception is thrown by the repository, I want my code to catch it and set an error label in the graphical user interface (view):
... // more code
try {
existingCourse = repository.findByTitle(course.getTitle()); // <- throws RepositoryException
} catch (RepositoryException e) {
view.showError(e.getMessage(), course);
return;
}
... // some more code
The point is that this code is repeated several times and I would prefer to have it refactored in an helper class.
This is what I came up to after some experiments:
A custom FunctionalInterface called ThrowingSupplier, which represent the code that throws the exception.
A TransactionManager helper class, with a catcher methods that accepts a ThrowingSupplier
This is the related code (BaseEntity is just a base class for entities in my domain, as you might guess):
// ThrowingSupplier.java
#FunctionalInterface
public interface ThrowingSupplier<T extends BaseEntity> {
T get() throws RepositoryException;
}
/* ------------------------------------------------------ */
// ExceptionManager.java
public final class ExceptionManager<T extends BaseEntity> {
private T result;
private String exceptionMessage;
ExceptionManager() {
}
public boolean catcher(ThrowingSupplier<T> supplier) {
try {
clearResult();
clearExceptionMessage();
result = supplier.get();
return true;
} catch (RepositoryException e) {
exceptionMessage = e.getMessage();
}
return false;
}
// public getters and 'clearers' for attributes
...
}
And this is how I am using this now:
...
em = new ExceptionManager();
... // more code
if (!em.catcher(() -> repository.findByTitle(course.getTitle()))) {
view.showError(em.getExceptionMessage(), course);
return;
}
existingCourse = em.getResult();
... // some more code
Now it seems to me that this does not give any advantages with respect to using directly the try catch in every repository invocation. This is mainly because I need both the return value of the repository method and a way to tell the caller if the repository call has been successful. As a variation I tried to add the showError call inside catcher, but then I must pass view and entity in every invocation of catcher, which I do not like very much as it makes the code less readable.
Is there another way to accomplish this in an elegant manner or it is better to leave the try catch in every call to the repository? Also, what is the standard way to deal with this problem?
I am trying to automate the construction of some objects in java.
To do this, I have these sample classes:
class TestInjected extends CommonAncestor {
TestInjected() {
System.out.println("I am Test Injected");
}
void exist() {
System.out.println("Hey there, I exist");
}
}
class CommonAncestor {
CommonAncestor() {
super();
init();
}
void init() {
try {
Field f = this.getClass().getDeclaredField("x");
f.set(this, f.getType().newInstance());
} catch (NoSuchFieldException e) {
} catch (IllegalAccessException e) {
} catch (InstantiationException e) {
}
}
}
public class TestInjection extends CommonAncestor{
TestInjected x;
private TestInjected y;
private TestInjected getY() {
if (y == null) {
y = new TestInjected();
}
return y;
}
public TestInjection() {
super();
}
public void test() {
x.exist();
}
public void test2() {
getY().exist();
}
}
And I also have a testing class:
public class TestInjectionTest {
#Test
public void test1() {
TestInjection t = new TestInjection();
t.test();
t.test2();
}
}
What I am doing here is, on constructor, I check for the Field x, and I initialize it via reflection. This way, I make sure that whenever a method is called, like in this case, test(), Field x has already been initialized, and therefor, it works.
The second approach, is to force programmers to use a getter, in this case, for Field y, where this getter method makes sure to initialize the object.
However, I am wondering, if hava has any way to execute reflection, when a variable is accessed. Let's say, instead of having to execute reflection code on constructor, if somehow, that code could be executed whenever "x" is required.
i.e:
x.exist()
--> check x is getting called, initialize it, and then call exist()
Any reflection method, or any library, that gives me this?
I can't really understand what problem you are trying to solve, but I'm sure there is a better solution. Work with the platform, not against it. Having said that the answer is no in the general case. You could run something that rewrites the byte codes (essentially adding a getter behind the scenes) but you can't intercept field accesses out of the box.
Make the fields private and expose them with methods if you need to initialize them. Or do it in the constructor.
EDIT: based on your comments I think what you are really looking for is dependency injection. Take a look at CDI (https://docs.oracle.com/javaee/6/tutorial/doc/giwhl.html) or Spring (https://spring.io) or Guice (https://github.com/google/guice).
I'm using Mockito and want to do a hopefully simple thing. How do I mock a void method for a particular class? I tried ...
CacheService cs = mock(CacheService.class);
when(cs.startCache()).then( PopulateCache.addTestEntriesToCache() );
But I'm getting the compile error
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:2.3.2:testCompile (default-testCompile) on project cme-productplus-web: Compilation failure: Compilation failure:
[ERROR] \Documents and Settings\E18538\workspace\cme-productplus-web\src\test\java\com\cme\clearing\product\server\PopulateCacheServiceImpl.java:[32,65] 'void' type not allowed here
[ERROR] \Documents and Settings\E18538\workspace\cme-productplus-web\src\test\java\com\cme\clearing\product\server\PopulateCacheServiceImpl.java:[32,20] 'void' type not allowed here
My intention is instead of calling the normal code of CacheService.startCache, I want to call my own method, "PopulateCache.addTestEntriesToCache()". How can I do this?
Edit: Per the response given, I tried editing my class where I implement the mock, but the mock method (the doAnswer, presumably) isn't getting called ...
public class PopulateCacheServiceImpl extends RemoteServiceServlet implements PopulateCacheService {
/**
*
*/
private static final long serialVersionUID = 1L;
public Boolean initCache() {
boolean ret = false;
try {
setupMockCache();
CacheService.getInstance().startCache();
ret = true;
} catch (Exception e) {
e.printStackTrace(System.err);
ret = false;
} // try
return ret;
} // initCache
private void setupMockCache() {
CacheService cs = mock(CacheService.class);
try {
doAnswer(new Answer<Object>() {
public Object answer(InvocationOnMock invocation) throws Throwable {
PopulateCache.addTestEntriesToCache();
return null;
}
}).when(cs).startCache();
} catch (SQLException e) {
e.printStackTrace();
}
} // setupMockCache
}
Thanks, - Dave
You are making a mock for the CacheService, but you are still not returning it and using it anywhere. Instead, you are calling the real static CacheService.instance() method which will not return your mock. Make you setupMockCache() return the CacheService and use it directly rather than going through the instance() method.
Also in the question title/summary, you said "leave everything else the same". If you mean you want the rest of CacheService to behave the same as it normaly would, then perhaps you want a partial mock, which you can do with Mockito's spy() instead of mock().
Put the call to your cache in the anwser-method of this http://docs.mockito.googlecode.com/hg/latest/org/mockito/Mockito.html#12
Mockito.doAnswer(new Answer<Object>() {
public Object answer(InvocationOnMock invocation) throws Throwable {
PopulateCache.addTestEntriesToCache()
return null;
}
}).when(cs).startCache();
Of course it doesn't work : in setupMockCache you are creating a the cache mock CacheService cs = mock(CacheService.class); on which you define the stub. But the cs instance is never passed.
And in initCache you are calling the setup method, but you don't get the CacheService instance, right after you wrote this statement CacheService.getInstance().startCache(); that will certainly create a real CacheService instance and fo course it won't use the mocked instance.
I don't know what you want to do, this seems weird and wrong to mock partially a Cache in your production code! If I were you I would create my own set of classes that will return your custom cache backed by an inherited CacheService class if necessary (this class will explicitly overide the startCache method).
Hope that helps!