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.
Related
I'm doing some unit test but I'm having problems trying to test a class. I have a class with a static builder method which returns the class instance:
public class MessageCaller {
public static MessageCaller builder() {
return new MessageCaller();
}
//Other methods
public String publish() {
//publishing to some Messages
return "something";
}
public MessageCaller withAttribute(String key, String value) {
//Some code
return this;
}
}
public class MessageCallerExtended extends MessageCaller {
private Map<String, String> attributes;
#Override
public MessageCaller withAttribute(String key, String value) {
if (this.attributes == null) {
this.attributes = new HashMap();
}
this.attributes.put(key, value);
return this;
}
//It's not working because it's calling the base class builder and is not possible to be Overriten
//because it's a static method.
public static MessageCallerExtended builder() {
return new MessageCallerExtended();
}
#Override
public String publish() {
return "test";
}
}
This is the method which I would like to test, the problem is that is calling the real publish method taking some time to finalize.
public void sendMessages(#Nonnull String group, #Nonnull String state) {
this.message.builder()
.toTopic(xxxx)
.withAttribute(xxx, xxx)
.withAttribute(xxx, xxx)
.withAttribute(xxx,xxx)
.publish();
}
I'm sending the message object in the constructor of the class.
I've created a Wrapper class to use in the unit test but the problem is that the builder method is static and for that reason is not possible to #Override, if I don't use the #Override tag I'll invoke the real builder method and then the real publish method and it is taking too much time to be processed, causing some problems, because is invoked for several unit test.
With Mockito I having similar issues with the static builder method, in fact it's not possible to mock static methods with Mockito. I'm not allowed to use another library like PowerMock for instance.
Any ideas?
I've been struggling for a while trying to find a solution to this problem. Hope you can help me out.
I'm trying to generate a method that calls a static method from another class using some already defined fields:
class Test {
private String someField;
private String otherField;
}
Expected result:
class Test {
private String someField;
private String otherField;
public String getCacheKey() {
return SimpleCacheKey.of(this.someField, this.otherField);
}
}
class SimpleCacheKey {
public static String of(final Object... values) {
// Some Operations
return computed_string;
}
}
I've tried several things, closest one:
public class ModelProcessor implements Plugin {
#Override
public Builder<?> apply(final Builder<?> builder,
final TypeDescription typeDescription,
final ClassFileLocator classFileLocator) {
return builder.defineMethod("getCacheKey", String.class, Visibility.PUBLIC)
.intercept(new SimpleCacheKeyImplementation());
}
#Override
public void close() throws IOException {
}
#Override
public boolean matches(final TypeDescription typeDefinitions) {
return true;
}
}
public class SimpleCacheKeyImplementation implements Implementation {
private static final MethodDescription SIMPLE_CACHE_KEY_OF = getOf();
#SneakyThrows
private static MethodDescription.ForLoadedMethod getOf() {
return new MethodDescription.ForLoadedMethod(SimpleCacheKey.class.getDeclaredMethod("of", Object[].class));
}
#Override
public InstrumentedType prepare(final InstrumentedType instrumentedType) {
return instrumentedType;
}
#Override
public ByteCodeAppender appender(final Target implementationTarget) {
final TypeDescription thisType = implementationTarget.getInstrumentedType();
return new ByteCodeAppender.Simple(Arrays.asList(
// first param
MethodVariableAccess.loadThis(),
this.getField(thisType, "someField"),
// second param
MethodVariableAccess.loadThis(),
this.getField(thisType, "otherField"),
// call of and return the result
MethodInvocation.invoke(SIMPLE_CACHE_KEY_OF),
MethodReturn.of(TypeDescription.STRING)
));
}
private StackManipulation getField(final TypeDescription thisType, final String name) {
return FieldAccess.forField(thisType.getDeclaredFields()
.filter(ElementMatchers.named(name))
.getOnly()
).read();
}
}
However, generated code is as follows (decompiled with Intellij Idea):
public String getCacheKey() {
String var10000 = this.name;
return SimpleCacheKey.of(this.someValue);
}
Changing the signature of SimpleCacheKey.of and trying to workaround the problem with a List is not an option.
You are calling a vararg method, java bytecode doesnt have that. So you need to create an actual array of the correct type to call the method.
#Override
public ByteCodeAppender appender(final Target implementationTarget) {
final TypeDescription thisType = implementationTarget.getInstrumentedType();
return new ByteCodeAppender.Simple(Arrays.asList(ArrayFactory.forType(TypeDescription.Generic.OBJECT)
.withValues(Arrays.asList( //
new StackManipulation.Compound(MethodVariableAccess.loadThis(),
this.getField(thisType, "field1")),
new StackManipulation.Compound(MethodVariableAccess.loadThis(),
this.getField(thisType, "field2")))
), MethodInvocation.invoke(SIMPLE_CACHE_KEY_OF) //
, MethodReturn.of(TypeDescription.STRING)));
}
Maybe byte-buddy has a special builder for that, but at least thats one way of doing that.
Imo: it is often a good approach to write a java version of the bytecode you want to generate. That way you can compare the javac bytecode and bytebuddy bytecode.
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.
I have these two methods
private void saveBaseLineLatency(E2EResultShort e2EResultShort) {
LatencyHistogram latencyHistogram = getLatencyHistogram(e2EResultShort);
latencyDrillDownRepository.saveBaseLine(latencyHistogram);
saveLatencyTable(latencyHistogram);
}
private void saveFreshTestLatency(E2EResultShort e2EResultShort) {
System.out.println("log: before saveLatencyHistogram");
LatencyHistogram latencyHistogram = getLatencyHistogram(e2EResultShort);
latencyDrillDownRepository.save(latencyHistogram);
saveLatencyTable(latencyHistogram);
}
How can I refactor out their common code?
I thought to use Callable but its call() is parameterless.
Consumer is the interface you want. It's part of the new java.util.function package in Java 8, so this won't work if you're on an older version of Java. (The package also has a number of similar other interfaces, like a BiConsumer that takes two arguments, and interfaces for consuming various primitive types as well.)
So, your helper method would be something like:
private void doLatencyOperation (E2EResultShort newThing, Consumer<LatencyHistogram> consumer) {
LatencyHistogram lh = getLatencyHistogram(newThing);
consumer.accept(lh);
saveLatencyTable(lh);
}
and you could call it thusly
private void saveBaseLineLatency(E2EResultShort e2EResultShort) {
doLatencyOperation(e2EResultShort, (lh) -> latencyDrillDownRepository.saveBaseLine(lh));
}
Create an abstract class and move all the common code there.
public abstract class MyClass{
public MyClass(E2EResultShort result, latencyDrillDownRepository){
this.result = result;
}
public void execute() {
LatencyHistogram latencyHistogram = getLatencyHistogram(e2EResultShort);
callMe(latencyHistogram, latencyDrillDownRepository);
saveLatencyTable(latencyHistogram);
}
public abstract void callMe(LatencyHistogram latencyHistogram, latencyDrillDownRepository);
}`
Now in your method, create concrete MyClass:
private void saveFreshTestLatency(E2EResultShort e2EResultShort) {
System.out.println("log: before saveLatencyHistogram");
MyClass myClass = new MyClass(e2EResultShort, latencyDrillDownRepository){
public void callMe(LatencyHistogram latencyHistogram, latencyDrillDownRepository){
latencyDrillDownRepository.save(latencyHistogram);
}
}
myClass.execute();
}
Given the following abstract class:
public abstract class BaseVersionResponse<T extends BaseVO> {
public abstract void populate(T versionVO);
}
and the following child class:
public class VersionResponseV1 extends BaseVersionResponse<VersionVOV1>
{
protected String testFieldOne;
protected String testFieldTwo;
public String getTestFieldOne() {
return testFieldOne;
}
public void setTestFieldOne(String value) {
this.testFieldOne = value;
}
public String getTestFieldTwo() {
return testFieldTwo;
}
public void setTestFieldTwo(String value) {
this.testFieldTwo = value;
}
#Override
public void populate(VersionVOV1 versionVO) {
this.setTestFieldOne(versionVO.getFieldOne());
this.setTestFieldTwo(versionVO.getFieldTwo());
}
I desire to do something like this from a calling method:
public void getVersionInfo(String version) {
BaseVO versionVO = null;
BaseVersionResponse<? extends BaseVO> baseVersionResponse = null;
baseVersionResponse = createVersionResponse(version);
versionVO = createVersionVO(version);
baseVersionResponse.populate(versionVO);
}
where createVersionResponse(...) and createVersionVO(...) look like this:
public BaseVersionResponse<? extends BaseVO> createVersionResponse(String version) {
BaseVersionResponse<? extends BaseVO> specificVersionResponse = null;
if (version.equalsIgnoreCase("V1")) {
specificVersionResponse = new VersionResponseV1();
} else if (version.equalsIgnoreCase("V2"))
specificVersionResponse = new VersionResponseV2();
return specificVersionResponse;
}
public BaseVO createVersionVO(String version) {
BaseVO versionVO = null;
if (version.equalsIgnoreCase("V1")) {
versionVO = new VersionVOV1();
} else if (version.equalsIgnoreCase("V2"))
versionVO = new VersionVOV2();
return versionVO;
}
and VersionVOV1 looks like this:
public class VersionVOV1 extends BaseVO {
private String fieldOne = null;
private String fieldTwo = null;
private String fieldThree = null;
public String getFieldOne() {
return fieldOne;
}
public void setFieldOne(String fieldOne) {
this.fieldOne = fieldOne;
}
public String getFieldTwo() {
return fieldTwo;
}
public void setFieldTwo(String fieldTwo) {
this.fieldTwo = fieldTwo;
}
public String getFieldThree() {
return fieldThree;
}
public void setFieldThree(String fieldThree) {
this.fieldThree = fieldThree;
}
}
My problem arises when I try to compile this line of code:
baseVersionResponse.populate(versionVO);
in getVersionInfo(...). I'm getting a message that looks like this:
The method populate(capture#3-of ?) in the type BaseVersionResponse is not applicable for the arguments (BaseVO)
on the populate method above.
My thought was (which is apparently incorrect) that since the baseVersionResponse is, at this point in the code, actually a specific child instance, that the class would know exactly which populate method to call from that specific child class.
What am I doing wrong here? Is there a better way to do this if this isn't the correct approach?
Thank you for your time!
Ok, I took a better look at this today. The problem is that the wildcard, while the right way to go, precludes you from doing:
BaseVO versionVO = createVersionVO(version);
Because the populate call wants an extension of BaseVO, not an actual BaseVO, which doesn't qualify. That means you can't pass that versionVO variable directly.
So, to keep the type checking in place, which I think is good because you'll always want an implementation, leave pretty much everything as-is above, and change your BaseVersionResponse class to something like:
public abstract class BaseVersionResponse<T extends BaseVO> {
public T getVersion(BaseVO versionVO) {
try {
return (T) versionVO;
} catch (ClassCastException e) {
throw new IllegalArgumentException();
}
}
public abstract void populate(BaseVO versionVO);
}
So, populate method now takes a BaseVO, and there's a new getVersion method to do some explicit casting for us. This should be ok since we know that the factory will always supply the right thing, but if another caller doesn't, an IllegalArgumentException is thrown.
Now, in your response class implementation, change the populate method accordingly:
public void populate(BaseVO version) {
VersionVOV1 versionVO = getVersion(version);
this.setTestFieldOne(versionVO.getFieldOne());
this.setTestFieldTwo(versionVO.getFieldTwo());
}
So, we've changed the populate method to take BaseVO, and the getVersion method does the casting for us. All the other type checks still apply, and we're good to go.
The casting makes it feel not as clean, but for the factory approach you're using, it's really the only way (I can think of) to keep the guarantees made by the type declarations and the code pattern in tact.
Hope that helps!
If you just take out the capture of type (the "<?>"), and leave it unchecked, it should work just fine. Even using type Object would have compiled.
But, given your specific example, what you probably want is the method:
public BaseVersionResponse<?> createVersionResponse(String version)
Changed to:
public BaseVersionResponse<? extends BaseVO> createVersionResponse(String version)
Then, instead of using
BaseVersionResponse<?>
use
BaseVersionResponse<? extends BaseVO>
Since you know that the return type will be one of those things that implements the interface/class.