Announcement Announcement Module
Collapse
No announcement yet.
Can i change a proxy to point at a differnt target at runtime ? Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Can i change a proxy to point at a differnt target at runtime ?

    I have a use case as follows

    Entity ent = new Entity();

    Entity has methods - setAAA(), setBBB(), ....


    Service A - calls getEntity() - which returns entity object.

    When service A calls getEntity().setAAA() - i want to intercept this method and return a new entity object.


    So i am think of creating a proxy inside getEntity() - and return a proxy.

    When i intercept setAAA() - method i want to craete new Entity object and point make teh proxy to pint at this new Entity().

    Is this possible ?

    I am planning to use Proxyfactory to create spring proxies using API.

    In short - inside a proxy at runtime i want to cganhe its target object - ? Is this possible ?


    Are there better ways to do this ?

  • #2
    Hello

    Thanks to many implementations of TargetSource, you can do the following:

    Code:
        ProxyFactory pf = new ProxyFactory();
        pf.setExposeProxy(true);
        pf.addInterface(MyService.class);
        pf.setTargetSource(new HotSwappableTargetSource(new MyServiceImpl()));
        pf.addAdvice(new MethodInterceptor()
        {
          @Override
          public Object invoke(MethodInvocation invocation) throws Throwable
          {
            ((HotSwappableTargetSource)((Advised)AopContext.currentProxy()).getTargetSource()).swap(new MyServiceImpl());
            return "test";
          }
        });
        MyService service = (MyService)pf.getProxy();
    I hope this is what you need.

    regards
    Grzegorz Grzybek

    Comment


    • #3
      Amazing !!! . That was exactly what i was looking for.

      Can i use this in my production. Is the HotSwappableTargetSource very expensive to use ?

      Also one more confiormation - hope the previous target becomes eligible for garbage collection.

      Comment


      • #4
        Hi

        First - HotSwappableTargetSource is very lightweight - just a reference to the target.

        Second - swap() returns previous target, so it will be GCed when there's no reference left.

        Also - when you want to replace the target during particular method interception, you have to be careful if the target must be the original target while intercepting other methods. You can always write your own TargetSource, which will remember the first target.

        regards
        Grzegorz Grzybek

        Comment


        • #5
          Great. I have now implemented my own target source and everything seems fine.

          One question i had in your example is ..

          Is there any specific reason to create newAdvice() or new Advisor(). - [new MethodInterceptor() in your example]

          Can i use this advisor/advice as a singleton and inject in various usecases . Is it thread safe enough ?

          Comment


          • #6
            i am getting an issue with one particular usecase

            When i change the target at runtime , for that particular call , it doesnt change the target .

            i have written my own TargetSource which which just replaces the old target with the new one.

            When ever a setter method is called on this proxy , i just call the method on the targetSource to change the target.

            But now if i perform any setting of a value , this value is set on the old target object and not on the new object.

            Doesnt the change target work even for the current call ?

            [I am using before advice where i am changing the target]

            Comment


            • #7
              One thing I figured out that since i was using before advice i could not control the current calling thread which has already stored the target.

              So i tried implementing MethodInterceptor. Still for the current method invocation it uses the old target.


              One way i see is to use a around advice and i explicitly call the method using the new target by reflection.

              But is there a better way out ?

              Comment


              • #8
                Hi

                Can you put your code on the Github? I could see and try to help...

                regards
                Grzegorz Grzybek

                Comment


                • #9
                  Thanks a lot for having a look.

                  I have not used github before , i will paste the code here - i have created a small use case for this

                  This is the output of teh program

                  Get called from AccountDAO
                  creating proxy now
                  accountcom.test.entity.account.Account@e45076
                  Before advice called

                  com.test.entity.account.Account@fd68b1 :old a value100
                  com.test.entity.account.Account@10382a9:new a value0


                  That is the setter value has been set on the old objectrather than teh new one.

                  This is my spring xml

                  Code:
                      <bean id="accountDao" class="com.test.dao.account.AccountDAO">
                  		 <property name="advisor" ref="settersAdvisor" /> 
                  	</bean>
                  
                       <bean id="swapableAroundAdvice" class="com.test.framework.SwappableMethodInvoker"/>
                  
                  	<bean id="settersAdvisor"
                  		class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
                  		<property name="advice">
                  			<!-- <ref local="swapableBeforeAdvice" /> -->
                  			<ref local="swapableAroundAdvice" /> 
                  		</property>
                  		<property name="patterns">
                  			<list>
                  				<value>com.test.entity.account.*.set.*</value>
                  				<value>com.test.entity.account.*.increment.*</value>
                  			</list>
                  		</property>
                  	</bean>

                  This is my account class

                  [code]

                  public class Account implements IAccount {

                  private boolean writable;

                  private int a;

                  private String b;

                  public Account(int a, String b) {
                  super();
                  this.a = a;
                  this.b = b;
                  }

                  public int getA() {
                  return a;
                  }

                  public void setA(int a) {
                  this.a = a;
                  }

                  public String getB() {
                  return b;
                  }

                  public void setB(String b) {
                  this.b = b;
                  }

                  public boolean isWritable() {
                  return writable;
                  }

                  public void setWritable(boolean writable) {
                  this.writable = writable;
                  }

                  public void incrementa() {
                  this.a = a + 1;
                  // return this.a;
                  }

                  @Override
                  public void asset() {
                  // TODO Auto-generated method stub

                  }

                  public Object clone() {
                  try {
                  return super.clone();
                  } catch (CloneNotSupportedException e) {
                  return null;
                  }
                  }


                  [code]


                  This is my account dao

                  Code:
                  public class AccountDAO {
                  
                      private Advisor advisor;
                  
                      public IAccount get(long id) {
                          System.out.println("Get called from AccountDAO");
                          IAccount a = new Account(0, "suji");
                          ProxyFactory pf = new ProxyFactory();
                          pf.setExposeProxy(true);
                          pf.addInterface(IAccount.class);
                          // pf.setTargetSource(new SwappableTargetSource(a));
                          pf.setTargetSource(new HotSwappableTargetSource(a));
                          pf.addAdvisor(advisor);
                          System.out.println("creating proxy now");
                          return (IAccount) pf.getProxy();
                      }
                  
                      public void setAdvisor(Advisor advisor) {
                          this.advisor = advisor;
                      }

                  This is my swappable method invoker

                  Code:
                  public class SwappableMethodInvoker implements MethodInterceptor {
                  
                      @Override
                      public Object invoke(MethodInvocation invocation) throws Throwable {
                          // TODO Auto-generated method stub
                          System.out.println("Before advice called");
                  
                          Account clonedobj = (Account) ((Account) invocation.getThis()).clone();
                  
                          ((HotSwappableTargetSource) ((Advised) AopContext.currentProxy())
                                  .getTargetSource()).swap(clonedobj);
                  
                        
                    /* 
                  
                  If i uncomment these lines everything works fine
                  return invocation.getMethod().invoke(clonedobj,
                           invocation.getArguments());  */
                  
                          return invocation.proceed();
                  
                      }
                  }
                  An this is my main program

                  Code:
                      public static void main(String[] args) {
                          ApplicationContext ctx = new ClassPathXmlApplicationContext(
                                  "classpath:META-INF/spring/aop-poc-module-context.xml");
                          AccountDAO accountDao = (AccountDAO) ctx.getBean("accountDao");
                          IAccount account = accountDao.get(1);
                          System.out.println("account" + account);
                          IAccount accountOld = (Account) ((HotSwappableTargetSource) ((Advised) account)
                                  .getTargetSource()).getTarget();
                          account.setA(100);
                          IAccount accountnew = (Account) ((HotSwappableTargetSource) ((Advised) account)
                                  .getTargetSource()).getTarget();
                          System.out.println(accountOld + "  :old a value" + accountOld.getA());
                          System.out.println(accountnew + ":new a value" + accountnew.getA());
                      }

                  Comment


                  • #10
                    Hi

                    When you're in your interceptor the invocation object is already constructed and keeps a reference to the target obtained from target source. So changing the target in target source has no effect.

                    See org.springframework.aop.framework.JdkDynamicAopPro xy.invoke(Object, Method, Object[]) - the target is acquired from target source, ReflectiveMethodInvocation is constructed and eventually its "proceed()" method invoked. So using:
                    Code:
                    return invocation.getMethod().invoke(clonedobj,
                             invocation.getArguments());
                    is the only option in your case. Of course this is not equivalent of calling "invocation.proceed" (see org.springframework.aop.framework.ReflectiveMethod Invocation.proceed() where there are other interceptors invoked).

                    TargetSource should be used to manipulate the target from the code external to proxy invocation.

                    Hope this helps.

                    regards
                    Grzegorz Grzybek

                    Comment


                    • #11
                      Yes. I finally used the same code (commented in my example) to reflectively call the method.

                      Thanks Grzegorz for all the responses.

                      Comment


                      • #12
                        Sorry to pull out the old thread again.

                        Just required a clarification. Since we were using thread local , i was a bit hesitant in using the below approach. In my business usescases the thread which constructs the proxy , may be different from the one which will call a functionality trigering the advice. So

                        ((Advised)AopContext.currentProxy()) may give undesirable results.

                        Is there any other way i can access the proxy in method interceptor.

                        MethodInvocation invocation.getObject() is returning the target rather than the proxy which i expected :-(

                        Code:
                          ProxyFactory pf = new ProxyFactory();
                            pf.setExposeProxy(true);
                            pf.addInterface(MyService.class);
                            pf.setTargetSource(new HotSwappableTargetSource(new MyServiceImpl()));
                            pf.addAdvice(new MethodInterceptor()
                            {
                              @Override
                              public Object invoke(MethodInvocation invocation) throws Throwable
                              {
                                ((HotSwappableTargetSource)((Advised)AopContext.currentProxy()).getTargetSource()).swap(new MyServiceImpl());
                                return "test";
                              }
                            });
                            MyService service = (MyService)pf.getProxy();

                        Comment


                        • #13
                          Hi

                          Hmm. Look at org.springframework.aop.framework.JdkDynamicAopPro xy.invoke(Object, Method, Object[]) - the "current proxy" is set (in thread local variable) at invocation time, not at the time the proxy is constructed.

                          If opposite is true nothing in Spring AOP would work - almost always proxies are constructed at startup/injection phase.

                          So don't worry - unless you're dealing with threads inside interceptor, everything should work.

                          regards
                          Grzegorz Grzybek

                          Comment


                          • #14
                            Thats great . Sorry for me asking such a basic question :-).

                            And I also assume the thread local refrrence to the proxy is cleared soon after the invocation, leaving no scope for memory leak.

                            Comment


                            • #15
                              Hi

                              Very intelligent question - it is cleared (actually restored to previous value which at the beginning is null) in finally block

                              regards
                              Grzegorz Grzybek

                              Comment

                              Working...
                              X