Invoke of a method - logging as method declaring class - java

I'm currently using JBoss interceptors and Proxy classes for wrapping method invoking at runtime and log some statistics.
So said, having this code:
public class ProxyLoggingInterceptor <T> implements InvocationHandler {
private Logger logger = LoggerFactory.getLogger(ProxyLoggingInterceptor.class);
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
logger.info("%s.%s", t.getClass().getSimpleName(), method.getName());
}
}
the log will produce something like this:
12-11-2018 11:41.09,728 INFO (ProxyLoggingInterceptor) - [ANALYTICS]: MyClass.myMethod
However I'd like to show the logging declaring class as the logger entry, that is MyClass.
The desired result would be like:
12-11-2018 11:41.09,728 INFO (MyClass) - [ANALYTICS]: MyClass.myMethod
Is there any way that would not considered as a bad practice ?

Actually I am not into AOP based on dynamic proxies, I always use AspectJ where this kind of problem does not exist and it is easy to get the information you want because the original classes get modified. But having found the question anyway due to its aop tag and having played around a bit, I am trying to answer it:
package de.scrum_master.app;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.function.Function;
public class ProxyLoggingInterceptor<T> implements InvocationHandler {
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.printf(
"%s.%s%n",
proxy.getClass().getGenericInterfaces()[0].getTypeName(),
method.getName()
);
return null;
}
public static void main(String[] args) {
ClassLoader classLoader = ProxyLoggingInterceptor.class.getClassLoader();
Map mapProxy = (Map) Proxy.newProxyInstance(
classLoader,
new Class[] { Map.class },
new ProxyLoggingInterceptor<Map>()
);
mapProxy.put("foo", 11);
Function functionProxy = (Function) Proxy.newProxyInstance(
classLoader,
new Class[] { Function.class },
new ProxyLoggingInterceptor<Function>()
);
functionProxy.apply("x");
Runnable runnableProxy = (Runnable) Proxy.newProxyInstance(
classLoader,
new Class[] { Runnable.class },
new ProxyLoggingInterceptor<Runnable>()
);
runnableProxy.run();
}
}
Console output:
java.util.Map.put
java.util.function.Function.apply
java.lang.Runnable.run
Is that what you want?

Related

How to get Method annotated with given annotation in springboot / java app

Is there a way to directly get hold of a method(non-static) annotated with given annotation present in a given object? I don't want to iterate over list of all methods and check if the given annotation present or not.
In below sample code, I have used dummy method(not exist) getMethodAnnotatedWith() . I need to replace this with actual method.
public class MyController {
#PostMapping("/sum/{platform}")
#ValidateAction
public Result sum(#RequestBody InputRequest input, #PathVariable("platform") String platform) {
log.info("input: {}, platform: {}", input, platform);
return new Result(input.getA() + input.getB());
}
}
class InputRequest {
#NotNull
private Integer a;
#NotNull
private Integer b;
#MyValidator
public boolean customMyValidator() {
log.info("From customMyValidator-----------");
return false;
}
}
#Aspect
#Component
#Slf4j
public class AspectClass {
#Before(" #annotation(com.example.ValidateAction)")
public void validateAspect(JoinPoint joinPoint) throws Throwable {
log.info(" MethodName : " + joinPoint.getSignature().getName());
Object[] args = joinPoint.getArgs();
log.info("args[0]==>"+args[0] +", args[1]==>"+args[1]);
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
Parameter[] parameters = method.getParameters();
Method customMyValidator = parameters[0].getType().getMethodAnnotatedWith(MyValidator.class); // InputRequest class type which has a method annotated with #MyValidator
customMyValidator.invoke(args[0]);
}
}
Here is my stand-alone AspectJ MCVE. I just imported some Spring classes. The syntax would be the same in Spring AOP.
Helper classes:
package de.scrum_master.app;
public class Result {
public Result(int i) {}
}
package de.scrum_master.app;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
#Retention(RUNTIME)
#Target(METHOD)
public #interface ValidateAction {}
Custom validator interface (not annotation):
package de.scrum_master.app;
public interface MyValidator {
boolean validate();
}
Class implementing custom validator interface:
package de.scrum_master.app;
public class InputRequest implements MyValidator {
private Integer a;
private Integer b;
public InputRequest(Integer a, Integer b) {
this.a = a;
this.b = b;
}
#Override
public boolean validate() {
System.out.println("Performing custom validation");
return false;
}
public Integer getA() {
return a;
}
public Integer getB() {
return b;
}
#Override
public String toString() {
return "InputRequest(a=" + a + ", b=" + b + ")";
}
}
See? Instead of annotating the validator method, you just override the interface method. It is just as simple as before, but more type-safe and feels more "standard-ish". It will also be much easier to handle by AOP, as you are going to find out below.
Controller:
The controller looks just the same as before. You still have the method annotated with #ValidateAction and taking InputRequest as its first parameter.
package de.scrum_master.app;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
public class MyController {
#PostMapping("/sum/{platform}")
#ValidateAction
public Result sum(#RequestBody InputRequest input, #PathVariable("platform") String platform) {
System.out.println("input: " + input + ", platform: " + platform);
return new Result(input.getA() + input.getB());
}
}
Sample driver application:
package de.scrum_master.app;
public class Application {
public static void main(String[] args) {
new MyController().sum(new InputRequest(11, 22), "foo");
}
}
Aspect:
The aspect is super simple now, like I said in my comment to your question. The pointcut checks for methods which
are annotated with #ValidateAction and
have a first parameter implementing MyValidator.
Then it binds the MyValidator parameter to an advice method argument by args().
Please note that you can omit the trailing && execution(* *(..)) in Spring AOP because it only supports method execution() joinpoints, while in AspectJ there are also call() joinpoints which here would lead to double validation and log output.
package de.scrum_master.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import de.scrum_master.app.MyValidator;
#Aspect
#Component
public class MyValidatorAspect {
#Before("#annotation(de.scrum_master.app.ValidateAction) && execution(* *(..)) && args(validator, ..)")
public void validateAspect(JoinPoint joinPoint, MyValidator validator) throws Throwable {
System.out.println(joinPoint);
validator.validate();
}
}
Console log:
execution(Result de.scrum_master.app.MyController.sum(InputRequest, String))
Performing custom validation
input: InputRequest(a=11, b=22), platform: foo
Update answering follow-up questions: Please read some documentation. The Spring manual is a good source.
what does it mean && args(validator, ..)?
It is called argument binding. This specific pointcut designator means: Match all target methods where the first argument matches the type of validator in the advice method arguments list and bind the value to that argument. As you see, the argument is declared as MyValidator validator. The , .. means that any subsequent target method arguments (if any) do not matter. For more information see this manual paragraph.
What would happen if more than one class implementing MyValidator interface . I mean how would FW figure out that which implementation has to passed while invoking current controller operation ?
FW meaning what? Maybe framework? Anyway, I do not see the problem. Why would the framework have to figure out which implementation there is? That is the beauty of OOP and virtual methods: You do not need to figure out anything because each implementation has a boolean validate() which then gets called. It is simple, type-safe, hassle-free. Your solution is neither of these. This approach just works. Instead of asking, why don't you just try?
MethodUtils (Apache Commons Lang) can be used to achieve the requirement.
API used in the example code : MethodUtils.getMethodsListWithAnnotation
Aspect Code
#Component
#Aspect
public class CallAnnotatedMethodAspect {
#Pointcut("within(rg.so.q64604586.service.*) && #annotation(rg.so.q64604586.annotation.ValidateAction)")
public void validateActionMethod() {
};
#Before("validateActionMethod() && args(request,..)")
public void adviceMethod(InputRequest request)
throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
List<Method> methodList = MethodUtils.getMethodsListWithAnnotation(request.getClass(), MyValidator.class);
for (Method m : methodList) {
m.invoke(request, new Object[] {});
}
}
}
Note : The Retention Policy of MyValidator annotation is RUNTIME for this code to work.
If the InputRequest instance obtained in the aspect is a proxy , the code will need to be modified to get the actual class of the same.

Wrapping a service in a SleepyProxy to simulate lag

I am attempting to wrap a service in a proxy to simulate lag during tests. The following class is meant to wrap an object and sleep the thread for 100ms for any invoked method.
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class SleepyProxy<T> implements InvocationHandler {
private T wrapped;
private SleepyProxy(T toWrap) {
this.wrapped = toWrap;
}
#SuppressWarnings({"unchecked", "rawtypes"})
public static <T> T createProxy(T toWrap) {
Object proxy = Proxy.newProxyInstance(
toWrap.getClass().getClassLoader(),
toWrap.getClass().getInterfaces(),
new SleepyProxy(toWrap));
return (T) proxy;
}
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(wrapped, args);
nap();
return result;
}
private void nap() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
From my test class:
private MyService service = SleepyProxy.createProxy(ServiceProvider.getMyService());
Produces the following error:
java.lang.ClassCastException: com.sun.proxy.$Proxy33 cannot be cast to com.example.service.MyService;
Please Note:
I am using Spring Framework and JUnit4
Test class annotated with #RunWith(SpringJUnit4ClassRunner.class)
I'm learning Spring; I'm unsure if I need to be using a Spring InvocationHandler / Proxy service
Why am I having issues casting to MyService? All object values seem to line up when debugging. Is there a better way I can go about simulating lag on my services? (Aside from making a 'test service' for each).
Thanks for your help!
Problem is that MyService is a class, and Proxy.newProxyInstance creates object that implements interface you provide in second argument. To have it working MyService needs to implement interface so:
class MyService implements ServiceInterface
And later use your proxy like this:
ServiceInterface service = SleepyProxy.createProxy(ServiceProvider.getMyService());
Proxy.newProxyInstance has nothing to do with MyService class, it will only create object that will run InvocationHandler.invoke when you will call method on it.

Method intercepted twice even though it was called once

In the following code snippet I'm calling the method doStuff once on an instance of Subclass. However it is intercepted twice.
Note that doStuff was defined in the parent class SuperClass. If doStuff was defined in SubClass the interception logic would work as expected: only one interception.
Am I using Byte Buddy incorrectly?
package com.test;
import static net.bytebuddy.matcher.ElementMatchers.any;
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;
import java.util.concurrent.Callable;
import net.bytebuddy.agent.ByteBuddyAgent;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType.Builder;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.SuperCall;
import org.junit.Test;
public class ReproBugTest {
#Test
public void reproBug() {
new AgentBuilder.Default().type(nameStartsWith("com.test"))
.transform(new AgentBuilder.Transformer() {
#Override
public Builder<?> transform(
Builder<?> builder,
TypeDescription td) {
return builder.method(any())
.intercept(
MethodDelegation.to(MethodInterceptor.class));
}
})
.installOn(
ByteBuddyAgent.installOnOpenJDK());
SubClass subClass = new SubClass();
subClass.doStuff();
}
}
class SuperClass {
public void doStuff() {
System.out.println("Doing stuff...");
}
}
class SubClass extends SuperClass {
}
class MethodInterceptor {
#RuntimeType
public static Object intercept(#SuperCall Callable<?> zuper)
throws Exception {
// Intercepted twice, bug?
System.out.println("Intercepted");
Object returnValue = zuper.call();
return returnValue;
}
}
You are intercepting the method call for every type, i.e. for both Subclass and SuperClass. You need to further specify your interceptor for what methods to intercept. In you case, you only want to intercept methods if they are declared by a given type.
This is easy to implement. Instead of builder.method(any()), you should intercept builder.method(isDeclaredBy(td)). This way, a method is only intercepted if it is declared by the intercepted type.
Finally, I can see from, your source code that you are using an older version of Byte Buddy. Version 0.7-rc6 runs stable, has additional features and fixes several bugs. (However, some APIs still need to be changed.)

Is it possible to mock a single method in an already existing object?

For an integration test, I need to mock a specific method in a java service client without destroying the rest of the information in it. It has no self-constructor, so a solution like this is out of the question:
private DBClient mockClient = new DBClient(alreadyExistingClient){
#Override
void deleteItem(Item i){
//my stuff goes here
}
};
Is there a way to mock the deleteItem method such that the credentials, endpoints, etc... are preserved in an existing DBClient object?
edit: mockito is not available for use in this case
You can use a Dynamic Proxy to intercept any method invocation you want, so you can decide between invoking the real method or do whatever you want instead.
This is an example of how to intercept the method Set.add(), you can do exactly the same for deleteItem()
package example.dynamicproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Set;
public class SetProxyFactory {
public static Set<?> getSetProxy(final Set<?> s) {
final ClassLoader classLoader = s.getClass().getClassLoader();
final Class<?>[] interfaces = new Class[] {Set.class};
final InvocationHandler invocationHandler = new InvocationHandler() {
#Override
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
if (method.getName().equals("add")) {
System.out.println("add() intercepted");
// do/return whatever you want
}
// or invoke the real method
return method.invoke(s, args);
}
};
final Object proxy = Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
return (Set<?>) proxy;
}
}
You could go lo-fi and create a sub-class of the DBClient class. To this subclass, pass the instance of DBClient you want to mock.
Use composition inside the sub-class, and delegate all method calls to the original DBClient, all except the one you want to mock. Add your mock implementation to the method you want.
This is not as reusable as a mocking framework, but should work.
DBClient mockDbClient = new DBClient() {
private DBClient dbClientDelegate;
public void setDelegate(DBClient dbClient) {
dbClientDelegate = dbClient;
}
//override all methods.
//delegate to the corresponding method of the dbClientDelegate instance
//overide the method you want to mock, add asserts for method arguments
//return mock data as appropriate
}
mockDbClient.setDelegate(preinstantiatedDbClient);
//inject mockDbClient to test class
//call test class / method
Hope this helps.
In Mockito 2+ you can use spy feature for this purpose:
PrintStream realSystemOut = System.out;
realSystemOut.println("XXX");
PrintStream mockedSystemOut = Mockito.spy(realSystemOut);
Mockito.doNothing().when(mockedSystemOut).println(Mockito.anyString());
mockedSystemOut.println("YYY");
Output:
XXX

How can I inject a class to 'Class<?> clazz' in Guice?

I have a class with a constructor, e.g.:
#Inject
public ClassTest(ITestInterface testInterface, Class<?> clazz){
...
}
The problem is how do I bind a class to an implementation which can be injected in this constructor and will the ClassTest binding pick the right class?
I want to inject different classes at different point of time. When I attempted to solve it Guice gives an error that it cannot find any suitable constructor on java.lang.Class.
I think you have to use assisted inject extension of Guice.
Basically, you define your ClassTest as it is, but mark 'varying' dependencies as #Assisted:
#Inject
public ClassTest(ITestInterface testInterface, #Assisted Class<?> clazz){
...
}
Then you create a factory interface for ClassTest objects which will accept Class argument and return ClassTests:
public interface ClassTestFactory {
ClassTest create(Class<?> clazz);
}
Then you install special kind of module which will create factories for you:
// Inside your module
install(new FactoryModuleBuilder().build(ClassTestFactory.class));
Then wherever you need ClassTest instances you should inject ClassTestFactory interface instead:
#Inject
YourLogicClass(ClassTestFactory ctFactory) {
this.ctFactory = ctFactory;
}
And finally you use it to create ClassTests for every class object you want:
ClassTest ct1 = ctFactory.create(SomeClass.class);
ClassTest ct2 = ctFactory.create(AnotherClass.class);
But if I were you, I would really reconsider the whole class architecture to avoid the need in such things.
This can be solved even without assisted inject, simply by using a TypeLiteral when creating the binding:
import javax.inject.Inject;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Module;
import com.google.inject.TypeLiteral;
public class ClassTest
{
static interface ITestInterface {}
#Inject
public ClassTest(ITestInterface testInterface, Class<?> clazz)
{
System.err.println("testInterface=" + testInterface);
System.err.println("clazz=" + clazz);
}
public static void main(String... argument)
{
ITestInterface testObject = new ITestInterface() {};
Module module = new AbstractModule()
{
#Override
protected void configure()
{
binder().bind(ITestInterface.class).toInstance(testObject);
binder().bind(new TypeLiteral<Class<?>>() {}).toInstance(testObject.getClass());
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
}
};
Injector injector = Guice.createInjector(module);
injector.getInstance(ClassTest.class);
}
}
The output when running this code is something like:
testInterface=ClassTest$1#3d921e20
clazz=class ClassTest$1
I have to agree with #VladimirMatveev though, that this is a somewhat unusual use case, and that the need for injection of java.lang.Class objects might be indicative of a design flaw. The only seemingly valid case of this type of injection that I've come across is for type checking, where an injected class needs the Class object to check the type of some other object (via Class.isInstance(...)) but it is not desirable to inject an instance (!) of that class (e.g., because it is not a singleton and might spawn all sorts of other undesired object creations). Still, even that scenario is somewhat hokey and might be solvable in a better way.
At the very least, I would use a more specific type argument, like Class<? extends ITestInterface> (which, I suspect, is what's intended by the OP).
To change injected value over time you could use the Provider bindings. And then it could look like this:
The module configuration:
public class SomeModule extends AbstractModule{
#Override
protected void configure() {
bind(Class.class).toProvider(SomeProvider.class);
}
}
The provider(not very elegant but may be for a start...):
public class SomeProvider implements Provider<Class<?>>{
private static Class<?> myClazz;
public static void setClass(Class<?> clazz){
myClazz = clazz;
}
#Override
public Class<?> get() {
return myClazz;
}
}
Some different classes:
public class SomeClass{
public int itWorks;
}
public class SomeOtherClass{
public int itWorksGreat;
}
Example client code:
public static void main(String[] args){
SomeProvider.setClass(SomeClass.class);
Injector injector = Guice.createInjector(new SomeModule());
printFields(injector.getInstance(Class.class));
SomeProvider.setClass(SomeOtherClass.class);
printFields(injector.getInstance(Class.class));
}
private static void printFields(Class clazz) {
Field[] declaredFields = clazz.getDeclaredFields();
for(Field field : declaredFields){
System.out.println(field.getName());
}
}
And finally the result:
itWorks
itWorksGreat

Categories

Resources