exclusion of some fields/parameters from logging via Spring AOP - java

In my spring project I have such an aspect class for logging
#Aspect
#Component
public class BaseLoggingAspect {
private static final Logger logger = LoggerFactory.getLogger(BaseLoggingAspect.class);
#Target({ ElementType.FIELD, ElementType.PARAMETER })
public #interface NonLoggingField {
}
#Pointcut("execution(public * *(..))")
private void allPublicMethods() {
}
#Pointcut("within(img.imaginary.service.*)")
private void inServices() {
}
#Pointcut("within(img.imaginary.dao.*)")
private void inDao() {
}
#Before("allPublicMethods() && inServices() || inDao()")
public void logBeforeCall(JoinPoint joinPoint) {
if (logger.isDebugEnabled()) {
logger.debug("begin method {} in {} class with arguments: {}", joinPoint.getSignature().getName(),
joinPoint.getTarget().getClass().getSimpleName(), joinPoint.getArgs());
}
}
}
this aspect simply catches all the public methods of the service and dao layers and outputs to the log at the beginning of execution the name of the method, the name of the class, and the masi of the values of the arguments of the method
in this aspect, I created a NonLoggingField annotation that I want to apply to some fields of classes of those objects that can be passed to the parameters of these logged methods, for example this:
public class User {
#NonLoggingField
public String userEmail;
public name;
public User(String userEmail, String name) {
this.userEmail = userEmail;
this.name= name;
}
public String tiString() {
return String.format("user name: %s and his email: %s", name, userEmail);
}
}
the fact is that such objects will be written to the log through its toString method, but it is necessary that the email somehow does not get into the log using the notLoggingField annotation, while there are thoughts in my head to do through reflection, but there is no clarity how to do this without over difficult code using reflection, especially considering that objects may have objects of other types inside, which may have the same fields with annotations or collections with objects with such fields. perhaps the AspectJ library can help, but I can't find such mechanisms in it. Please help me come up with something

During runtime, a method parameter is just a value. The JVM does not know at this point if the caller called the method using constants, literals, fields or results of other method calls. That kind of information, you only see in the source code. In byte code, whatever dereferencing operation or computation necessary to determine the parameter's value (or a reference to the corresponding object) is done before calling the method. So there is no connection to the field annotation.
Would annotating method parameters be an alternative for you?
If your requirement is very specific, e.g. intercept field accesses from toString methods and return dummy values instead, if the field is annotated, that would be possible. But this would not be fool-proof. Imagine for example that toString calls a getter method instead of directly accessing the field or that a method other than toString logs the field. You do not always want to falisfy the field value on read access, because other parts of the application might rely on it working correctly. Not every toString call is made in order to log something.
I think you should solve the problem in another way, e.g. by applying filter rules for the logging tool you use. Or if you really want solve it at the application level, you could create an interface like
public interface PrivacyLogger {
String toStringSensitive();
}
and make each class containing sensitive information implement that interface. The logging aspect could then for each printed object determine if it is instanceof toStringSensitive(). If so, it would log the result of toStringSensitive() instead of toString(), i.e. in the simplest case something like
Object toBeLogged = whatever();
logger.log(
toBeLogged instanceof PrivacyLogger
? ((PrivacyLogger) toBeLogged).toStringSensitive()
: toBeLogged
);
Of course, you need to iterate over getArgs() and determine the correct log string for each object. Probably, you want to write a utility method doing that for the whole parameters array.
Moreover, in a complex class, the toStringSensitive() implementation should of course also check if its own fields are PrivacyLogger instances and in that case fold the values of their resapctive toStringSensitive() methods into itw own, so that it works recursively.
I am sorry I have no better news for you, but privacy is something which needs too be built into an application from the ground. There is no simple, fool-proof way to do that with one little aspect. The aspect can utilise the existing application infrastructure and avoid scattering and tangling, but it cannot decide on its own what needs to be prohibited from getting logged and what not.

Related

Different parameter while overriding method in inherited class in java

In the interface I have an abstract method
Server launchInstance(
Instance instance,
String name,
Set<String> network,
String userData)
throws Exception;
Now in my class that implements the previous interface, I am overriding this method but I do not need all the parameters because that will cause a lot of unnecessary tasks. In my implemented class I want to do something like-
#override
Server launchInstance(Instance instance, String name) throws Exception;
How can I remove some unnecessary parameters in my implemented(from Interface) class while overriding?
That's not possible with Java.
An interface defines method that all implementing classes must support, in order to have a unifying API.
One purpose is to be able to exchange implementations.
I see a couple of options:
Add a second method to the interface with fewer parameters.
But this requires, of course, that all implementations support this.
This may therefore not be viable for you.
Implement an additional second interface, which defines the method with two parameters.
if (x instanceof Server2)
// short-cut: do not need to compute network and userData
((Server2) x).launchInstance(instance, name)
else {
Set<String> network = …;
x.launchInstance(instance, name, network, userData)
}
Simply ignore the additional parameters.
If you desperately need a unified interface and want to avoid computation costs of the additional arguments, wrap the optional arguments of type T using lazy evaluation (e.g. in a Callable<T>). If you do not need the values, simply never call the Callable.
Interface is a common API for number of classes. By design you don't want interface implementations to change API.
However, you can omit unused parameters:
#Override
Server launchInstance(Instance instance, String name, Set<String> network, String userData) throws Exception {
launch(instance, name);
}
private Server launch(Instance instance, String name) throws Exception {
...
}
or provide Data object:
class Data {
private Instance instance;
private String name;
private Set<String> network;
private String userData;
}
#Override
Server launchInstance(Data data) {
...
}
Also interface(read data transfer) could be simlified using Dependency Injection.
nope, you just can break the compromisse behind the override... you will need to redesign the method signature for something a little bit more abstract...

Evaluate pathparam arguments in Jersey using AspectJ

I have several APIs which retain a parameter "feature" from the url (path param). To avoid retrieving it in each method endpoint (eg.)
#GET
public void findAll(#PathParam("feature") String feature);
am trying to implement AOP using AspectJ.
Following is the implementation of the Aspect
#Aspect
public class FeatureAOP {
#Pointcut("execution(* x.y.z.rest.ModifiersFacadeWrapper.*(..)) && args(feature)")
public void pointCut(String feature) {
}
#Before("x.y.z.rest.aop.FeatureAOP.pointCut(feature)")
public void parseParams(JoinPoint jp, String feature) {
Object[] x = jp.getArgs();
System.out.println("Feature: " + feature);
}
}
The above method gives me the value of "feature" in the Aspect class but if I change the method findAll to following signature, it doesn't works.
#GET
public void findAll();
What I understand is the control is transferred to the Aspect after the parameters are resolved and removing it from the method definition is failing it.
Doing so, thus takes me to the same point where I have to define all method endpoints with the parameter in its signature. I would like to know if there is a way I can get the PathParams in the Aspect class without having to define my methods with the designated parameters.
I think you could probably do it by putting the resolved params in a globally accessible data structure (e.g. a Singleton having some sort of Map or Set), but
I wouldn't recommend that kind of approach. I don't know why you don't like having all the params in your method signatures, but that is the intended way of declaring rest services, e.g.
#GET
#Path("{feature}")
#Produces("text/plain")
public String getFeature(#PathParam("feature") String feature) {
return feature;
}
This way you don't have to write any code for retrieving the params, the rest library you are using (be it Jersey or a different one) will just do everything for you.

Where to validate String parameter

I have a Student class that has to have a property of a String ID, which has to be validated. I'm not sure whether to validate it inside the student class or the class that I'm implementing the Student class in. Does that make sense?
Assuming ID is final and immutable, then one approach is to have Student constructor throw an exception, probably new IllegalArgumentException("Invalid student ID");
You may additionally provide static method in Student class, which verifies if string is valid, in case you need to check it without creating Student object.
But the logic of determining if ID is valid or not should be in the Student class, I think.
If there are (or can be in future) different kind of student IDs, you could also consider abstract factory pattern, but sounds like that is bit of an overkill.
If Student already has any business inside use validate inside else use second one
Class Student
{
public boolean validate ()
{
//some logic to validation
}
}
Inside of Model or controller or Action
public boolean validate ()
{
//some logic to validation
}
One of the approach is to use validation object. For instance see the Validation approach uses in the Spring Framework. You create an object which implements the interface Validator with two methods: one to detect if the Validator can validate the instance to validate, and another one which validate it.
public class StudentValidator implements Validator<Student> {
public boolean supports(Student student) {
// ...
}
public void validate(Object target, Errors errors) {
// ...
}
}
This approach leads to separation of the code of the object and the way to validate it, offering more flexibility when combining validator:
you can combine several Validator even if the class hierarchy is not respected (POJO principle).
when you need to validate field with data from other system (for instance a database), this approach avoid to mix database / persistence code in the POJO domain class.
Please see the documentation of Spring about Validation.

Java Annotations values provided in dynamic manner

I want to provide annotations with some values generated by some methods.
I tried this so far:
public #interface MyInterface {
String aString();
}
#MyInterface(aString = MyClass.GENERIC_GENERATED_NAME)
public class MyClass {
static final String GENERIC_GENERATED_NAME = MyClass.generateName(MyClass.class);
public static final String generateName(final Class<?> c) {
return c.getClass().getName();
}
}
Thought GENERIC_GENERATED_NAME is static final, it complains that
The value for annotation attribute MyInterface.aString must be a constant expression
So how to achieve this ?
There is no way to dynamically generate a string used in an annotation. The compiler evaluates annotation metadata for RetentionPolicy.RUNTIME annotations at compile time, but GENERIC_GENERATED_NAME isn't known until runtime. And you can't use generated values for annotations that are RetentionPolicy.SOURCE because they are discarded after compile time, so those generated values would never be known.
The solution is to use an annotated method instead. Call that method (with reflection) to get the dynamic value.
From the user's perspective we'd have:
#MyInterface
public class MyClass {
#MyName
public String generateName() {
return MyClass.class.getName();
}
}
The annotation itself would be defined as
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface #MyName {
}
Implementing the lookup for both of these annotations is rather straight-forward.
// as looked up by #MyInterface
Class<?> clazz;
Method[] methods = clazz.getDeclaredMethods();
if (methods.length != 1) {
// error
}
Method method = methods[0];
if (!method.isAnnotationPresent(MyName.class)) {
// error as well
}
// This works if the class has a public empty constructor
// (otherwise, get constructor & use setAccessible(true))
Object instance = clazz.newInstance();
// the dynamic value is here:
String name = (String) method.invoke(instance);
There is no way to modify the properties of an annotation dynamically like others said. Still if you want to achieve that, there are two ways to do this.
Assign an expression to the property in the annotation and process that expression whenever you retrieve the annotation. In your case your annotation can be
#MyInterface(aString = "objectA.doSomething(args1, args2)")
When you read that, you can process the string and make the method invocation and retrieve the value. Spring does that by SPEL (Spring expression language). This is resource intensive and the cpu cycles are wasted every time we want to process the expression. If you are using spring, you can hook in a beanPostProcessor and process the expression once and store the result somewhere. (Either a global properties object or in a map which can be retrieved anywhere).
This is a hacky way of doing what we want. Java stores a private variable which maintains a map of annotations on the class/field/method. You can use reflection and get hold of that map. So while processing the annotation for the first time, we resolve the expression and find the actual value. Then we create an annotation object of the required type. We can put the newly created annotation with the actual value (which is constant) on the property of the annotation and override the actual annotation in the retrieved map.
The way jdk stores the annotation map is java version dependent and is not reliable since it is not exposed for use (it is private).
You can find a reference implementation here.
https://rationaleemotions.wordpress.com/2016/05/27/changing-annotation-values-at-runtime/
P.S: I haven't tried and tested the second method.

How can I access private class members in Java?

I have data model classes that contain private fields which are meant to be read-only (via a getter function). These fields are set by my JPA persistence provider (eclipselink) during normal operation, using the contents of the database. For unit tests, I want to set them to fake values from a mockup of the persistence layer. How can I do that? How does eclipselink set these values, anyway?
Simplified example:
#Entity
class MyEntity
{
#Id
private Integer _ix;
public Integer ixGet()
{
return this._ix;
}
}
Can you just Mock the Entity itself, providing your own implemenations of the getters?
You could create an anonymous extension in your mock persistence layer:
MyEntity x = new MyEntity() {
public Integer ixGet() { return new Integer(88); }
};
You need to use the Reflection API. Use Class.getField() to get the field, then call setAccessable(true) on that field so that you may write to it, even though it is private, and finally you may call set() on it to write a new value.
For example:
public class A {
private int i;
}
You want to set the field 'i' to 3, even though it is private:
void forceSetInt(Object o, String fieldName, int value) {
Class<?> clazz = o.getClass();
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(o, value);
}
There are a number of exceptions that you will need to handle.
You can use a test library like Mockito to access objects internal state in read and write mode. For example with Mockito use:
//read
Integer i = Whitebox.getInternalState(myEntity,"_ix")
//Write
Whitebox.setInternalState(myEntity,"_ix", 123)
You can use a mocking framework like powermock to by pass encapsulation. In powermock you'd use Whitebox.setInternalState(..) to set a private member.
A less invasive method would be to mock the getter method. Whether this is feasible would depend on what else depends on the internal state but if it is enough, it's the cleaner solution.
Some methods I've used in the past:
Make _ix protected, create a subclass where you implement a setter
Make a constructor taking the value for _ix as a parameter
Use reflection
Another option, if you really hate to make things public, is to create a subclass for testing, and provide public access there.
You have a few options:
Create stubs to replace your entity (extract an interface first)
Use Reflection
Add a public setter for testing
Keep your tests within the package and use a default scope
For a bunch of useful techniques, have a look at Michael Feather's book, Working Effectively With Legacy Code
You can add constructor with parameter for your read-only variable. Don't forget to add a default (zero parameter) constructor.
#Entity
class MyEntity
{
#Id
private Integer _ix;
public MyEntity(Integer ix) {
_ix = ix;
}
public MyEntity() {
/*
* Default constructor
*/
}
public Integer ixGet()
{
return this._ix;
}
}
The constructor is a best way I think. If this entity has to be really readonly (not allowed to create new instances in production code at all) you can make constructor with package access and use it only within the tests. And there is a possibility that even if you make your default constructor private or with package access, your persistance provider still be able to work with such entity, but not sure though - check with eclipselink docs.

Categories