Finding the annotated method call as a parameter to Logger methods - intellij-idea

Suppose I have below Person class with one getAccountNumber() method having #ShouldNotBeLogged custom annotation.
public class Person {
private String name;
private String accountNumber;
#ShouldNotBeLogged
public String getAccountNumber() {
return accountNumber;
}
}
Considering the above Person class I want to find the all occurrences where getAccountNumber() method is logged using the Logger class - error|warn|debug|info methods like in the below HelloWorld class logMessage method. Please note that there are many methods having ShouldNotBeLogged annotation in the actual code, so we cannot create search with name of the method in it.
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class HelloWorld {
private static final Logger logger = LogManager.getLogger(HelloWorld.class);
public void logMessage(Person person) {
logger.debug("logging Acc No. - {}", person.getAccountNumber()); // Occurence Type 1
LogManager.getLogger(HelloWorld.class).info("Account Number - {}", person.getAccountNumber()); // Occurence Type 2
}
}
I have tried using the Method call existing template and given the Text filter for $MethodCall$ with - error|warn|debug|info and it finds the Logger method with the error,warn,debug or, info names. Not able to create the filter for $Instance$ & $Parameter$ where Instance will be of Logger type (can be instantiated as a constant of a class or, directly with the Logger class method) and Parameter will be the call to #ShouldNotBeLogged annotated method.
$Instance$.$MethodCall$($Parameter$)
I'm using the IntelliJ IDEA 2022.2 (Ultimate Edition)

This is a difficult case. Hopefully this will help you.
First, create a search that will find #ShouldNotBeLogged annotated methods:
#ShouldNotBeLogged
$ReturnType$ $Method$($ParameterType$ $Parameter$);
With a count modifier [0,∞] on $Parameter$ (this template is based on the existing template "Deprecated methods"). Save this template under a name, for example "Methods that should not be logged".
$logger$.$call$($arg1$, $method$())
Modifiers:
$logger$: type=Logger
$method$: reference=Methods that should not be logged
This will find all logger calls with a call to a #ShouldNotBeLogged annotated method as the second argument.
Unfortunately, if you want to find logger calls with calls to a #ShouldNotBeLogged annotated method on a different position, you will have to construct a separate query template. For example:
$logger$.$call$($arg1$, $arg2$, $method$())

Related

Why does ByteBuddy tell me that there is an ambiguity in my interceptor methods when there is only one such method?

(Trying to keep this simple.)
I have a (partial) ByteBuddy recipe like this:
builder
.method(someMatcher())
.intercept(MethodDelegation.to(this.interceptor));
I have an "interceptor" class defined like this:
private static final class Interceptor {
private Interceptor() {
super();
}
#RuntimeType
private final Object doSomething(#This final Proxy<?> proxy,
#SuperCall final Callable<?> callable,
#Origin final String methodSignature) throws Exception {
final Object proxiedInstance = proxy.getProxiedInstance();
// TODO: logic
return callable.call(); // for now
}
}
(The interceptor method needs to be non-static for various reasons not important here.)
When I create an instance of this ByteBuddy-defined class and call a simple public void blork() method on it, I get:
Cannot resolve ambiguous delegation of public void com.foo.TestExplorations$Frob.blork() to net.bytebuddy.implementation.bind.MethodDelegationBinder$MethodBinding$Builder$Build#3d101b05 or net.bytebuddy.implementation.bind.MethodDelegationBinder$MethodBinding$Builder$Build#1a9efd25
How can there be ambiguity when there is only one interceptor? What have I done wrong?
Byte Buddy just adds a method call to the instrumented class which needs to be able to see the target class. If it is private, it is ignored and Byte Buddy searches further up the hierarchy where it finally consideres the methods of Object which are all equally unsuited but therefore an ambiguity exception is thrown instead of an exception that no method could be bound.

How to match a class with annotation inside?

I have an annotation named Metric
#Target({ElementType.FIELD, ElementType.METHOD})
public #interface Metric {
String name() default "";
}
I want to weave some logic when some methods with the #Metric annotation, like:
public class MethodWithMetricDemo{
#Metric
public void targetMethod(){
// do some thing
}
}
But how to match the class MethodWithMetricDemo in new AgentBuilder.Default().type(xxxxxxxxxxxxxxxx) ?
You would need to match your type based on the method annotatation. As methods are inherited virtually, you would however need to travers the entire class hierarchy by hasSuperType(declaresMethod(isAnnotatedWith(...))). This is possible but rather expensive. If you choose to use such a matcher, you should probably restrict your matching to a given namespace.

Java8 reports "cannot resolve constructor" by calling "::new"

In Intellij 15.0.3. and using Java8 I have a problem in using ::new.
In particular, I have a class with a default constructor
public class Container{
public Container(){}
}
I want to create a map from a list, as follows:
public class Test{
private final Map<Key, Container> map;
#Before
public void setUp(){
List<Key> keys=...//Init the list
map = keys.stream().collect(Collectors.toMap(Function.identity(), Container::new));
}
}
In Intellij, new is red and the tooltip says cannot resolve constructor Container
If I do () -> {new Container()} I also have cannot infer functional interface type Container
Any idea why?
Each mapping function is supposed to accept a Key argument. Function.identity() does, but Container::new takes no parameters. Same thing with () -> new Container(). You need a one-argument lambda. An argument that you'll ignore, as it happens.
map = keys.stream().collect(Collectors.toMap(Function.identity(), key -> new Container()));
it should be something like :
Collectors.toMap(Container::getMyUniqueField, Function.identity())
This will use getter for keys, and the object itself as the value, in the created hashmap.

how to mock a class's inner static class's private field

i'm test a MR class which has mapper/reducer as inner static classes. the mapper has a private field which consume too much memory to make the test failed, i want to use a mock object for that field, but not sure how to do that, here is my code:
public class Aggregator extends Configured implements Tool {
public static class AggregatorMapper extends Mapper<LongWritable, Text, GeneralKey, Text) {
private LookupService lookupService = null; <--- the object i want to mock
}
}
i tried to mockito but seems no way to mock it. any suggestions? thanks!
You can use reflection to access and modify any property of any object you want. There are several questions on SO that answer this quite well already, for example:
Change private static final field using Java reflection.
Accessing private variables in Java via reflection
Instantiate private inner class with java reflection

Dozer BeanFactory: How to implement it?

I have looked at the Dozer's FAQs and docs, including the SourceForge forum, but I didn't see any good tutorial or even a simple example on how to implement a custom BeanFactory.
Everyone says, "Just implement a BeanFactory". How exactly do you implement it?
I've Googled and all I see are just jars and sources of jars.
Here is one of my BeanFactories, I hope it helps to explain the common pattern:
public class LineBeanFactory implements BeanFactory {
#Override
public Object createBean(final Object source, final Class<?> sourceClass, final String targetBeanId) {
final LineDto dto = (LineDto) source;
return new Line(dto.getCode(), dto.getElectrified(), dto.getName());
}
}
And the corresponding XML mapping:
<mapping>
<class-a bean-factory="com.floyd.nav.web.ws.mapping.dozer.LineBeanFactory">com.floyd.nav.core.model.Line</class-a>
<class-b>com.floyd.nav.web.contract.dto.LineDto</class-b>
</mapping>
This way I declare that when a new instance of Line is needed then it should create it with my BeanFactory. Here is a unit test, that can explain it:
#Test
public void Line_is_created_with_three_arg_constructor_from_LineDto() {
final LineDto dto = createTransientLineDto();
final Line line = (Line) this.lineBeanFactory.createBean(dto, LineDto.class, null);
assertEquals(dto.getCode(), line.getCode());
assertEquals(dto.getElectrified(), line.isElectrified());
assertEquals(dto.getName(), line.getName());
}
So Object source is the source bean that is mapped, Class sourceClass is the class of the source bean (I'm ignoring it, 'cause it will always be a LineDto instance). String targetBeanId is the ID of the destination bean (too ignored).
A custom bean factory is a class that has a method that creates a bean. There are two "flavours"
a) static create method
SomeBean x = SomeBeanFactory.createSomeBean();
b) instance create method
SomeBeanFactory sbf = new SomeBeanFactory();
SomeBean x = sbf.createSomeBean();
You would create a bean factory if creating and setting up your bean requires some tricky logic, like for example initial value of certain properties depend on external configuration file. A bean factory class allows you to centralize "knowledge" about how to create such a tricky bean. Other classes just call create method without worying how to correctly create such bean.
Here is an actual implementation. Obviously it does not make a lot of sense, since Dozer would do the same without the BeanFactory, but instead of just returning an object, you could initialized it somehow differently.
public class ComponentBeanFactory implements BeanFactory {
#Override
public Object createBean(Object source, Class<?> sourceClass,
String targetBeanId) {
return new ComponentDto();
}
}
Why do you need a BeanFactory anyways? Maybe that would help understanding your question.