A small yet powerful interception library that lets you manipulate existing objects and classes behavior runtime, It’s achieving this by using javassist to do bytecode manipulation,
Using Proxy can allow you to architecturally implement your code completely differently with features like:
This chapter will present some usage examples of this library. This list is in no way a full list of what you can do with this library.
Variant with a fluent style
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | package examples; import static com.ericsson.commonlibrary.proxy.Proxy.with; import java.lang.reflect.Method; import com.ericsson.commonlibrary.proxy.Interceptor; import com.ericsson.commonlibrary.proxy.Invocation; public class BuildingObjectsFluentExample { public static void main(String[] args) throws SecurityException, NoSuchMethodException { Algorithm algorithm = with(Algorithm. class ).delegate( new Sort1(), new Reverse1()).get(); System.out.println(algorithm); System.out.println(algorithm.algorithmReverse( "fluent" )); System.out.println(algorithm.algorithmSort( "fluent" )); Method method = Algorithm. class .getMethod( "algorithmSort" , String. class ); with(algorithm).interceptAll( new StringInterceptor()).interceptMethod( new StringInterceptor(), method); System.out.println(algorithm); System.out.println(algorithm.algorithmReverse( "fluent" )); System.out.println(algorithm.algorithmSort( "fluent" )); } public static class StringInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { if (invocation.getMethod().getReturnType() == String. class ) { return invocation.invoke() + "Intercepted" ; } return invocation.invoke(); } } public interface Algorithm { String algorithmSort(String arg); String algorithmReverse(String arg); } public static class Sort1 { public String algorithmSort(String arg) { return "sort1: " + arg; } } public static class Reverse1 { public String algorithmReverse(String arg) { return "reverse1: " + arg; } } } |
Variant with a typical static style
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | package examples; import com.ericsson.commonlibrary.proxy.Proxy; public class BuildingObjectsExample { public static void main(String[] args) { //Building all possible combinations of Algorithm. this would not be possible with inheritance without duplication Algorithm obj1 = Proxy.delegate(Algorithm. class , new Sort1(), new Reverse1()); Algorithm obj2 = Proxy.delegate(Algorithm. class , new Sort1(), new Reverse2()); Algorithm obj3 = Proxy.delegate(Algorithm. class , new Sort2(), new Reverse1()); Algorithm obj4 = Proxy.delegate(Algorithm. class , new Sort2(), new Reverse2()); System.out.println(obj2); System.out.println(obj2.algorithmReverse( "obj2" )); System.out.println(obj2.algorithmSort( "obj2" )); System.out.println( "-----------" ); System.out.println(obj4); System.out.println(obj4.algorithmReverse( "obj4" )); System.out.println(obj4.algorithmSort( "obj4" )); System.out.println( "-----------" ); //Object methods are handled a bit special. Here it will use the toString() impl in ToString class even if all the other delegate objects contain a toString() method inherited from class Object. Algorithm objWithSpecialToString = Proxy.delegate(Algorithm. class , new Sort2(), new ToString(), new Reverse1()); System.out.println(objWithSpecialToString); System.out.println(objWithSpecialToString.algorithmReverse( "objWithSpecialToString" )); System.out.println(objWithSpecialToString.algorithmSort( "objWithSpecialToString" )); System.out.println( "-----------" ); //You can replace specific parts of existing implementations Algorithm changeExistingImplClass = Proxy.delegate(AlgorithmImpl. class , new Sort2()); System.out.println(changeExistingImplClass); System.out.println(changeExistingImplClass.algorithmReverse( "changeExistingImplClass" )); System.out.println(changeExistingImplClass.algorithmSort( "changeExistingImplClass" )); System.out.println( "-----------" ); //You can even replace specific parts of existing objects Algorithm changeExistingImplObject = Proxy.delegate( new AlgorithmImpl(), new Reverse1()); System.out.println(changeExistingImplObject); System.out.println(changeExistingImplObject.algorithmReverse( "changeExistingImplObject" )); System.out.println(changeExistingImplObject.algorithmSort( "changeExistingImplObject" )); } public interface Algorithm { String algorithmSort(String arg); String algorithmReverse(String arg); } public static class AlgorithmImpl implements Algorithm { @Override public String algorithmSort(String arg) { return "sort3: " + arg; } @Override public String algorithmReverse(String arg) { return "reverse3: " + arg; } } public static class Sort1 { public String algorithmSort(String arg) { return "sort1: " + arg; } } public static class Sort2 { public String algorithmSort(String arg) { return "sort2: " + arg; } } public static class Reverse1 { public String algorithmReverse(String arg) { return "reverse1: " + arg; } } public static class Reverse2 { public String algorithmReverse(String arg) { return "reverse2: " + arg; } } public static class ToString { @Override public String toString() { return "special toString" ; } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | /* Copyright (c) 2018 Ericsson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package examples; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import com.ericsson.commonlibrary.proxy.Interceptor; import com.ericsson.commonlibrary.proxy.Invocation; import com.ericsson.commonlibrary.proxy.Proxy; public class CountDownCollectionRecursionExample { public static void main(String[] args) { //Anonymous class, should usually be put in a separate class. Interceptor countDown = new Interceptor() { @Override public Object intercept(Invocation invocation) throws Throwable { if (invocation.getMethodName().contains( "add" )) { invocation.invoke(); //invoke original Integer val = (Integer) invocation.getParameter0(); if (val > 0 ) { Collection list = (Collection) invocation.getThis(); return list.add(val - 1 ); //invoke proxy again. } return true ; } return invocation.invoke(); //invokes method as normal } }; List<Integer> list = Proxy.intercept( new ArrayList<Integer>(), countDown); list.add( 4 ); list.add( 6 ); list.add( 3 ); System.out.println(list); // [4, 3, 2, 1, 0, 6, 5, 4, 3, 2, 1, 0, 3, 2, 1, 0] Set<Integer> set = Proxy.intercept( new HashSet<Integer>(), countDown); set.add( 4 ); System.out.println(set); // [0, 1, 2, 3, 4] set.add( 6 ); System.out.println(set); // [0, 1, 2, 3, 4, 5, 6] set.add( 3 ); System.out.println(set); // [0, 1, 2, 3, 4, 5, 6] } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | package examples; import static com.ericsson.commonlibrary.proxy.Proxy.with; public class LambdaInterceptionExample { public static class SomeImpl { //sample class public void log(String log) { System.out.println(log); } } public static void main(String[] args) throws SecurityException, NoSuchMethodException { //lambda on class SomeImpl obj = with(SomeImpl. class ) .interceptAll(i -> { System.out.println( "before method: " + i.getMethodName() + " param: " + i.getParameter0()); return i.invoke(); }).get(); obj.log( "123" ); //Console output: // before method: log param: 123 // 123 //lambda on object SomeImpl obj2 = with( new SomeImpl()) .interceptAll(i -> { Object result = i.invoke(); System.out.println( "after method: " + i.getMethodName() + " param: " + i.getParameter0()); return result; }).get(); obj2.log( "321" ); //Console output: // 321 // after method: log param: 321 //lambda without return. // SomeImpl obj3 = with(SomeImpl.class) // .interceptAll((i) -> System.out.println("Replace method invocation: " + i.getMethodName())) // .get(); //TODO why does maven have problem with compiling this? // obj3.log("12345"); //Console output: // Replace method invocation: log } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | package examples; import com.ericsson.commonlibrary.proxy.Proxy; public class JavaBeanExample { public static void main(String[] args) { //Normally without the proxy library. JavaBean bean = new JavaBeanImpl(); // requires you to create JavaBeanImpl with implementation. bean.setName( "bean" ); System.out.println( "bean name:" + bean.getName()); //"Proxy.javaBean" will dynamically create a class that acts exactly like JavaBeanImpl. //This means that JavaBeanImpl is no longer needed can be removed. JavaBean proxyBean = Proxy.javaBean(JavaBean. class ); proxyBean.setName( "proxy" ); System.out.println( "bean proxy name:" + proxyBean.getName()); } public interface JavaBean { String getName(); void setName(String name); } //NOT needed when using the proxy solution public static class JavaBeanImpl implements JavaBean { private String name; @Override public String getName() { return name; } @Override public void setName(String name) { this .name = name; } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | package examples; import java.io.PrintStream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.ericsson.commonlibrary.proxy.Interceptor; import com.ericsson.commonlibrary.proxy.Invocation; import com.ericsson.commonlibrary.proxy.Proxy; public class RedirectConsoleExample { public static void main(String[] args) { //Anonymous interceptor class, should usually be put in a separate class. Interceptor redirectPrintStream = new Interceptor() { @Override public Object intercept(Invocation invocation) throws Throwable { if (invocation.getMethod().getName().contains( "println" )) { String classNameWhereItWasCalled = Thread.currentThread().getStackTrace()[ 1 ] .getClassName(); Logger logger = LoggerFactory.getLogger(classNameWhereItWasCalled); logger.info( "Console: " + invocation.getParameter0()); //The console would be empty because the println was intercepted. removing "return null;" in the interceptor would mean //that the original println would be called and 123 would appear in both slf4j and in the console. return null ; } return invocation.invoke(); //invokes other methods as normal } }; //add the interceptor to the current System.out PrintStream newOut = Proxy.intercept(System.out, redirectPrintStream); System.setOut(newOut); //you can use the interceptor on multiple object. PrintStream newErr = Proxy.intercept(System.err, redirectPrintStream); System.setErr(newErr); System.out.println( "123" ); System.err.println( "some error" ); } } |
See more info at: MDC logging
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | package examples; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.ericsson.commonlibrary.proxy.Proxy; public class MdcExample { public static void main(String[] args) { SomeImpl objectToLog = new SomeImpl(); objectToLog = Proxy.mdcLogging(objectToLog, "id" , "id001" ); objectToLog = Proxy.mdcLogging(objectToLog, "instance" , "inst01" ); objectToLog.log( "Hello World" ); SomeImpl objectToLog2 = new SomeImpl(); objectToLog2 = Proxy.mdcLogging(objectToLog2, "id" , "id001" ); objectToLog2 = Proxy.mdcLogging(objectToLog2, "instance" , "inst02" ); objectToLog2.log( "Hello World2" ); //Expected output: //2013-06-25 10:40:17,374 Logger:com.ericsson.commonlibrary.proxy.examples.MdcExample$SomeImpl [id001.inst01] Hello World //2013-06-25 10:40:17,376 Logger:com.ericsson.commonlibrary.proxy.examples.MdcExample$SomeImpl [id001.inst02] Hello World2 //log4j configured: log4j.appender.A1.layout.ConversionPattern=%d{ISO8601} Logger:%c [%X{id}.%X{instance}] %m\n } public static class SomeImpl { private static final Logger LOG = LoggerFactory.getLogger(MdcExample.SomeImpl. class ); public void log(String log) { LOG.info(log); } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | package examples; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.ericsson.commonlibrary.proxy.Proxy; public class MdcRecursiveExample { public static void main(String[] args) { SomeImpl objectToLog = new SomeImpl(); objectToLog = Proxy.mdcLogging(objectToLog, "id" , "id001" ); objectToLog.log( "1" ); SomeImpl2 impl = objectToLog.getImpl(); impl.log( "2" ); SomeImpl3 impl2 = impl.getImpl(); impl2.log( "3" ); } public static class SomeImpl { private final SomeImpl2 impl = new SomeImpl2(); private static final Logger LOG = LoggerFactory.getLogger(MdcRecursiveExample.SomeImpl. class ); public void log(String log) { LOG.info(log); } public SomeImpl2 getImpl() { return impl; } } public static class SomeImpl2 { private final SomeImpl3 impl = new SomeImpl3(); private static final Logger LOG = LoggerFactory.getLogger(MdcRecursiveExample.SomeImpl2. class ); public void log(String log) { LOG.info(log); } public SomeImpl3 getImpl() { return impl; } } public static class SomeImpl3 { private static final Logger LOG = LoggerFactory.getLogger(MdcRecursiveExample.SomeImpl3. class ); public void log(String log) { LOG.info(log); } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | package examples; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.ericsson.commonlibrary.proxy.Proxy; public class MdcDangersExample { public static void main(String[] args) { SomeImpl objectToLog = new SomeImpl(); objectToLog.log( "1" ); objectToLog = Proxy.mdcLogging(objectToLog, "id" , "id001" ); objectToLog.log( "1" ); } public static class SomeImpl { private static final Logger LOG = LoggerFactory.getLogger(SomeImpl. class ); //Not dangerous private static final String stringStaticFinal = "hello" ; private final String stringPrivate = "hello" ; //dangerous. Will prevent Proxy from from adding mdc // private static String stringStatic; // public static String stringStaticPublic; // public String stringPublic; // public final String stringPublicFinal = "final"; public void log(String log) { LOG.info(log); } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | package examples; import com.ericsson.commonlibrary.proxy.Interceptor; import com.ericsson.commonlibrary.proxy.Invocation; import com.ericsson.commonlibrary.proxy.Proxy; public class InterceptionInnerWorkingsExplaination { public static class SomeImpl { public void log(String log) { System.out.println(log); } } public static class MyInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { //possible do something before calling the original method Object returnObject = invocation.invoke(); //possible do something after calling the original method return returnObject; } } public static void main(String[] args) { SomeImpl proxy = Proxy.intercept( new SomeImpl(), new MyInterceptor()); proxy.log( "hello world" ); } public static class MyInterceptor2 implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { System.out.println( "Method entered: " + invocation.getMethodName()); Object returnObject = invocation.invoke(); //original method invocation. System.out.println( "Method exit: " + invocation.getMethodName()); return returnObject; } } //Roughly what Proxy creates: public class SomeImplCreatedByProxy extends SomeImpl { @Override public void log(String log) { //now it's up to the interceptors to decide what this method should do. getInterceptorList().invokeAll(); //delegation to original "SomeImpl" is the last interceptor } } //IGNORE these. public static Invoke getInterceptorList() { return null ; } public interface Invoke { void invokeAll(); } } |