Spring AOP: How to pass argument from called method to advice method? - java

I am designing a system where I need to pass argument from the called method to the advice method. I am providing a simple code to make the point clear -
//AOPMain.java:
public class AOPMain {
public static void main(String[] args) {
ApplicationContext cxt = new ClassPathXmlApplicationContext("spring.xml");
Employee emp = cxt.getBean("employee", Employee.class);
emp.sayHello();
}
}
//Employee.java:
public class Employee {
private String name;
public String getName() {
System.out.println("getName");
return name;
}
public void setName(String name) {
System.out.println("setName");
this.name = name;
}
public void sayHello() {
System.out.println("Hello World!");
//How to pass argument to afterAdvice
}
}
//Logging.java:
#Aspect
public class Logging {
#Pointcut("execution(public void sayHello())")
public void doSomething(){}
#After("doSomething()")
public void afterAdvice() {
System.out.println("After Advice");
//Depending on the argument passed, send notification
}
}
How I can design this system? I know that there are ways to pass the argument to advice method from AOPMain itself using &&args(), but I am not able to find any sample code for this specific problem.
I know it's violating the basic design principle, that the advice method is not loosely coupled. So does Spring support this?
Thanks in advance.

There are two ways to get information from the advised method:
let it return a value and used that returned value in the advice:
public Arg sayHello() {
System.out.println("Hello World!");
//How to pass argument to afterAdvice
Arg arg = ...;
return arg;
}
#AfterReturning(pointcut="doSomething()", returning="retval")
public void afterAdvice(Object retval) {
System.out.println("After Advice");
// use retval here ...
}
use a JoinPoint to get access to the original object on which method was called, and pass arg as an object attribute:
public void sayHello() {
System.out.println("Hello World!");
//How to pass argument to afterAdvice
this.arg = ...;
}
#After("doSomething()")
public void afterAdvice(JoinPoint jp) {
System.out.println("After Advice");
Employee emp = (Employee) jp.getTarget();
// use emp.arg here ...
}
This one only makes sense if the advised object is statefull - do not considere to use it on a service or controller that are shared objects...

#After("doSomething()")
public void afterAdvice(JoinPoint joinPoint) {
System.out.println("After Advice");
//joinPoint.getArgs();
//Depending on the argument passed, send notification
}
Doesn't solve your problem? Refer get-method-arguments-using-spring-aop to know more.

Related

How do I test Function's code when it's passed as method parameter?

Is it possible to test code that is written in lambda function that is passed inside the method process?
#AllArgsConstructor
public class JsonController {
private final JsonElementProcessingService jsonElementProcessingService;
private final JsonObjectProcessingService jsonObjectProcessingService;
private final JsonArrayProcessingService jsonArrayProcessingService;
public void process(String rawJson) {
jsonElementProcessingService.process(json -> {
JsonElement element = new JsonParser().parse(json);
if (element.isJsonArray()) {
return jsonArrayProcessingService.process(element.getAsJsonArray());
} else {
return jsonObjectProcessingService.process(element.getAsJsonObject());
}
}, rawJson);
}
}
Since the lambda is lazy the function is not invoked (Function::apply) when I call JsonController::process so is there any way to check that jsonArrayProcessingService::process is called?
#RunWith(JMockit.class)
public class JsonControllerTest {
#Injectable
private JsonElementProcessingService jsonElementProcessingService;
#Injectable
private JsonObjectProcessingService jsonObjectProcessingService;
#Injectable
private JsonArrayProcessingService jsonArrayProcessingService;
#Tested
private JsonController jsonController;
#Test
public void test() {
jsonController.process("[{\"key\":1}]");
// how check here that jsonArrayProcessingService was invoked?
}
}
Just make it testable (and readable) by converting it to a method:
public void process(String rawJson) {
jsonElementProcessingService.process(this::parse, rawJson);
}
Object parse(String json) {
JsonElement element = new JsonParser().parse(json);
if (element.isJsonArray()) {
return jsonArrayProcessingService.process(element.getAsJsonArray());
} else {
return jsonObjectProcessingService.process(element.getAsJsonObject());
}
}
The relevant guiding principles I personally follow are:
anytime my lambdas require curly brackets, convert them to a method
organise code so that it can be unit tested
You may need to change the return type of the parse method to match whatever your processing services (which you didn’t show) return.
Given its relatively-basic redirection logic, don't you just want to confirm which of the #Injectables got called:
#Test
public void test() {
jsonController.process("[{\"key\":1}]");
new Verifications() {{
jsonArrayProcessingService.process(withInstanceOf(JsonArray.class));
}};
}

Will calling an enum with an object reference as a value create an object every time it's called?

This became my concern mainly because of this:
public enum Method {
POST(new Host().getAssets()),
GET("GET"),
DELETE("DELETE"),
PUT("PUT");
private String method;
Method(String s) {
method = s;
}
private String getMethod() {
return method;
}
}
The Host class is Spring #ConfigurationProperties annotated to be injected with values from an application.properties file at runtime. If I write that as a value of an enum, will it create a new object instance of Host every time I use Method.POST?
No, It will create instance only once. This can be checked with a print statement like below. Here getAssets() and constructor has been called only once:
public class Host {
public static void main(String[] args) {
System.out.println("Hello World!");
System.out.println(Method.POST);
System.out.println(Method.POST);
System.out.println(Method.POST);
}
Host()
{
System.out.println("--------------");
}
String getAssets()
{
System.out.println("ssssssssssss");
return "eeee";
}
}
enum Method {
POST(new Host().getAssets()),
GET("GET"),
DELETE("DELETE"),
PUT("PUT");
private String method;
Method(String s) {
method = s;
}
private String getMethod() {
return method;
}
}
O/P:
Hello World!
--------------
ssssssssssss
POST
POST
POST
All values of an Enumerator are singletons, which means, they are initialized once and reused every time you access it. So you can see the "definition" of an Enumerator Value as 'Constructor'.
This also means: if you provide a setter for the property "method" and change its value at runtime, the next access will return the new value! singleton does not mean its value is final.
public enum Method {
POST(new Host().getAssets()), // definition
GET("GET"),// definition
DELETE("DELETE"),// definition
PUT("PUT");// definition
private String method;
// Constructor
Method(String s) {
method = s;
}
private String getMethod() {
return method;
}
}

Why java compile is not invoking a method which is present inside a method

I am working on Cucumber java project and which is in very initial phase . While writing the code in step definition file. I found there is data required in Step N which is present only in Step N-1 i.e Sharing of data between cucumber test steps .As the project is in very initial phase . I thought implementation setter() and getter() method will work for me i.e is setter() I will set the value of variable and the call the getter() method whenever i need this data
Kindly find the actual implementation.
StepDefinition:
#Given("^recent destination list$")
public void list_of_recent_destinations_is_non_empty() throws Throwable {
gm.appLaucher();
gm.setValue_searchAddressOrName_HomeScreen("Wemmel");
gm.click_selectAddress_HomeScreen();
gm.click_driveButton_OnMapScreen();
gm.click_clearRouteButton_OnMapScreen();
gm.setValue_searchAddressOrName_HomeScreen("Ukkel, Beersel,1180");
gm.click_selectAddress_HomeScreen();
gm.click_driveButton_OnMapScreen();
gm.click_clearRouteButton_OnMapScreen();
gm.setValue_searchAddressOrName_HomeScreen("Sint-Jansplein Brussel, 1000");
gm.click_selectAddress_HomeScreen();
gm.click_driveButton_OnMapScreen();
gm.click_clearRouteButton_OnMapScreen();
gm.click_mainMenuButton_OnMapScreen();
gm.tap_recentDestinationButton_OnMainMenuScreen();
gm.tap_editListButton_RecentDestinationScreen();
List<MobileElement> em1= gm.get_recentDestinaList_EditListScreen();
System.out.println("____________________________________________"+em1.size());
int numOfElement=em1.size()-2;
boolean status =em1.size()>0;
Assert.assertEquals(true,status);
}
#When("^user selects one or more $")
public void user_selects_one_or_more_recent_destinations() throws Exception {
List<MobileElement> em1= gm.get_recentDestinaList_EditListScreen();
System.out.println("____________________________________________"+em1.size());
Iterator<MobileElement> it=em1.iterator();
while(it.hasNext())
{
System.out.println(it.next().getText());
}
String str= gm.getIndividualElement_recentDestinationList_EditListScreen(2);
System.out.println(str+"%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%");
}
#When("^user deletes address$")
public void user_deletes_selected_addresses() throws Exception {
gm.setValueForOtherSteps(gm.get_recentDestinaList_EditListScreen().size());
gm.deleteIndividualElememnt_recentDestinationList_EditListScreen(2);
}
#Then("^recent\\(s\\) is\\(are\\) removed from list$")
public void recent_destination_s_is_are_removed_from_list() throws Exception {
// Write code here that turns the phrase above into concrete actions
System.out.println(gm.getValueFromOtherStep()+"Intermidiate value from Step definition class");
int x=gm.getSize_recentDestinationList_EditListScreen();
System.out.println(x+"+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
String strLastElement=gm.getIndividualElement_recentDestinationList_EditListScreen(3);
String strSecondLast=gm.getIndividualElement_recentDestinationList_EditListScreen(2);
int y=gm.getSize_recentDestinationList_EditListScreen();
Assert.assertEquals(x, y);
// Assert.assertEquals(strLastElement, strSecondLast);
// gm.tap_editListButton_RecentDestinationScreen();
//
// gm.deleteAllElement_recentDestinationList_EditListScreen();
}
#Then("^recent desorted in temporal order$")
public void recent_destinations_list_is_sorted_in_temporal_order() throws Exception {
// Write code here that turns the phrase above into concrete actions
}
In Above gm is object of GMain class kindly find the implementation as below
class GMain{
public void setValueForOtherSteps(Object obj) throws IOException {
System.out.println(obj+"Intermediate values to verify +++++++++++++++++++++++++++++++++++++++");
getValueFromOtherStep();
}
public Object getValueFromOtherStep()
{
if(getValue() instanceof Integer) {
System.out.println((Integer)getValue()+" test");
return (Integer) getValue();
}
else if (getValue() instanceof String)
{
return (String) getValue();
}
else if (getValue() instanceof Boolean)
{
return (Boolean) getValue();
}
else {
System.out.print(getValue());
return "";
}
}
So as we call setValueForOtherSteps() from stepDefinition ,Control comes to GMain class method public void setValueForOtherSteps(Object obj) but why transfer goes back to caller without calling getValueFromOtherStep() method
I know this may be silly but Any kind of help will be appreciated
Thanks

restrict class type of annotation

is there any ClassDef like IntDef in annotation to restrict the type of annotation as my example below?
#ClassDef({
Integer, String, Long
})
public #interface MyRestrictedData {
}
As a result, I can use it as: public void showData(#MyRestrictedData Object myData)
This reqirement CAN'T be resolve by annotation processor.
It can only do by runtime container, like Spring.
But in fact, the container is just help you check it by proxy. Why can't you do it by yourself? Like this:
public class MyRestrictedData {
public static void check(Object o){
if(!(String.class.isInstance(o) || Integer.class.isInstance(o) || Long.class.isInstance(o)))
throw new IllegalArgumentException("Must be String, Integer or Long.");
}
}
public void showData(Object myData) {
MyRestrictedData.check(myData);
// then do your work
}
EDIT
If you really want check in compile period, the only way is what zhh said, override your method. I don't know what logic need handle String, Integer and Long together. But if you really need, you can do:
public void showData(String s){
showData((Object)s);
}
public void showData(Integer i){
showData((Object)i);
}
public void showData(Long l){
showData((Object)l).
}
private void showData(Object o){
// do your work here, note this method is PRIVATE
}

Override a method of an external object

I have a situation where i want to intercept/override a method of an external (i dont have access to them) object. An example of my situation:
I have an external object:
public class ExternalObject {
private String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
And an external class witch uses it:
public class ExternalClass {
public static void main(String[] args) {
ExternalObject o = new ExternalObject();
o.setName("Tom");
MyClass.doSomething(o);
o.setName("Jerry");
System.out.print(o.getName());
}
}
And then there is MyClass witch has an oppertunity to acces the external object:
public class MyClass {
public static void doSomething(ExternalObject o){
}
}
Is it possible to overwrite or modify the setName method in MyClass?
What i need is for the setName method to check if the name is "jerry" then change it back to "tom" and if not then do what original method does.
Something like this:
public void mySetName(String name){
if(name.equals("Jerry"){
name = "Tom";
}
doWhatOriginalMethodDoes();
}
So if someone runs the external class like it is now then Tom will be printed out two times.
The external (original) object and method is quite complex by the way.
I have searched around and this should be possible to do with reflect.Proxy but i cant get it to work.
Thanks for any help! :)
The easiest way would be to use inheritance:
public class MySubClass extends ExternalObject {
private ExternalObject obj;
public MySubClass(ExternalObject obj) {
this.obj = obj;
}
#Override
public void setName(String name){
if(name.equals("Jerry") {
super.setName("Tom");
} else {
super.setName(name);
}
}
// override all public method to call super method
#Override
public AClass otherMethod1(BClass arg){
return super.otherMethod1(arg);
}
#Override
public CClass otherMethod2(DClass arg){
return super.otherMethod2(arg);
}
}
And as MySubClass is a ExternalObject, you can call:
MySubClass subObject = new MySubClass(o);
MyClass.doSomething(subObject);
Or, if your class implements an interface you can use a Proxy:
First, define a InvocationHandler
public Class ExternalObjectInterfaceInvocationHandler implements java.lang.reflect.InvocationHandler {
// keep a reference to the wrapped object
private ExternalObjectInterface obj;
public ExternalObjectInterfaceInvocationHandler(ExternalObjectInterface obj) {
this.obj = obj;
}
public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
try {
if (m.getName().equals("setName")) {
// do something
}
} catch (InvocationTargetException e) {
throw e.getTargetException();
} catch (Exception e) {
throw e;
}
// return something
}
}
Then wrap your object with the proxy:
ExternalObjectInterface obj = ...
//wrap the obj with a proxy
ExternalObjectInterface wrappedInstance = (ExternalObjectInterface) Proxy.newProxyInstance(
ExternalObjectInterface.class.getClassLoader(),
new Class[] {ExternalObjectInterface.class},
new ExternalObjectInterfaceHandler( obj )
);
And then call:
MyClass.doSomething(wrappedInstance);
Third solution if your object do not implement an interface, is to use CGLib:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
To wrap your object you can use something like this:
public static <S,W> W createWrapper(final S source, final Class<W> wrapperClass) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(wrapperClass);
enhancer.setInterfaces(wrapperClass.getInterfaces());
enhancer.setCallback(new MethodInterceptor() {
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
if ("setName".equals(method.getName())) {
//do something
return null;
}
if (Arrays.asList(wrapperClass.getDeclaredMethods()).contains(method)) {
return methodProxy.invokeSuper(proxy, args);
}
return methodProxy.invoke(source, args);
}
});
return (W) enhancer.create();
}
Some examples of using CGLib here or here
This is not a part of the standard Java API. The only real need for that it may be testing such "external objects", or very corner cases when you need to "extend" the final classes from the third party library. Normally, you just create and use the derived class.
The functionality you request is provided by mocking frameworks like Mockito. They allow to replace the method implementations ("stubbing") or monitor calls of the actually existing methods ("spying"). Normally, these frameworks are used for testing. I have never heard anybody using them in production but might be possible if you cannot find another workaround.
If the "external object" can be represented as an interface, another approach would be to create and use a proxy class. However proxy can only substitute an interface.
If you don't have control over the creation of the object, then the only way that comes to my mind is to replace the class with your own subclass using instrumentation and a java agent. It will be a little ugly, but it will work (as long as the class is not already loaded at the time your agent code runs).
I would use javassist to create your replacement class, and then register a ClassFileTransformer for that class.
References can be found here
You can write a subclass and use that. It's a lot easier to write, use, and understand versus reflection.
public class MyExternalObject extends ExternalObject {
#Override
public void mySetName(String name){
if(name.equals("Jerry")
name = "Tom";
else
this.name = name;
}
}
you can do this:
ExternalObject.java:
public class ExternalObject {
private String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
ExternalClass.java:
public class ExternalClass {
public static void main(String[] args) {
ExternalObject object = new ExternalObject();
object.setName("tom");
System.out.println(object.getName());
MyClass.doSomeThing("jerry");
}
}
and MyClass.java:
public class MyClass {
public static void doSomeThing(String name) {
ExternalObject object = new ExternalObject() {
#Override
public void setName(String name) {
if (name.equals("jerry")) {
super.setName("tom");
} else {
super.setName(name);
}
}
};
object.setName(name);
System.out.println(object.getName());
}
}
You should just implement code in your myclass to perform the required action( you can call it as pre-processing) and then pass the value to the setname method, based on the result of your pre-processing.

Categories

Resources