4

Ok. We have the need to @Autowire a different webservice on-the-fly (preferably by toggling a JNDI setting on the webserver) and I'm at a loss on how to do this. This is the way I was approaching the problems..

Two packages: com.mycomp.service.stub com.mycomp.service.impl

One package contains MyServiceStub.java while implement MyService The other package contains MyServiceImpl.java, which implements same

My controller, which requires MyService, has the bean defined as such

@Autowire
private MyService communicator;

My spring-context.xml has the following:

<context:component-scan base-package="com.mycomp" />

At this point I get a DuplicateBean exception during autowiring. Now, I can statically define which bean to autowire in spring-context.xml:

<bean id="communicator" class="com.mycomp.service.impl.MyServiceImpl" />

and everything works fine... But then, how to 'flip' the switch and change over to the Stub method on our QA server? It has no connection to that service, so we need to run with stubs enabled. A JNDI property would be best for this.. but I just can't get my head around how to toggle what bean spring autowires at runtime.

Any help??

Cheers, Chris

Chris Kannon
  • 5,931
  • 4
  • 25
  • 35

2 Answers2

5

@Profile solution

You definitely have to try Spring 3.1 @Profile:

@Autowire
private MyService communicator;

//...

@Service
@Profile("prd")
class MyServiceImpl //...

@Service
@Profile("qa")
class MyServiceStub //...

Now depending on which profile is enabled, either DefaultMyService will be initialized or MyServiceStub.

You can choose between profile in various ways:

Spring AOP (explicit around every method)

In this example the aspect wraps around every single MyService method separately and returns stubbed value:

@Aspect
@Service
public class StubAspect {

    @Around("execution(public * com.blogspot.nurkiewicz.MyService.foo(..))")
    public Object aroundFoo(ProceedingJoinPoint pjp) throws Throwable {
        if (stubMode()) {
            return //stub foo() result
        }
        return pjp.proceed();
    }

    @Around("execution(public * com.blogspot.nurkiewicz.MyService.bar(..))")
    public Object aroundBar(ProceedingJoinPoint pjp) throws Throwable {
        if (stubMode()) {
            return //stub bar() result
        }
        return pjp.proceed();
    }

    private boolean stubMode() {
        //whatever condition you want here
        return true;
    }

}

The code is pretty straightforward, unfortunately the return values are buried inside the aspect and you need a separate @Around for every target method. Finally, there is no place for MyServiceStub.

Spring AOP (automatically around all methods)

@Aspect
@Service
public class StubAspect {

    private MyServiceStub stub = //obtain stub somehow

    @Around("execution(public * com.blogspot.nurkiewicz.MyService.*(..))")
    public Object aroundFoo(ProceedingJoinPoint pjp) throws Throwable {
        if (stubMode()) {
            MethodSignature signature = (MethodSignature)pjp.getSignature();
            Method method = signature.getMethod();
            return method.invoke(stub, pjp.getArgs());
        }
        return pjp.proceed();
    }

    private boolean stubMode() {
        //whatever condition you want here
        return true;
    }

}

This approach is more implicit as it automatically wraps every target method, including new methods added in the future. The idea is simple: if stubMode() is off, run the standard method (pjp.proceed()). If it is on - run the exact same method with exact same parameters - but on a different object (stub in this case).

This solution is much better as it involves less manual work (at the price of using raw reflection).

Note that if both MyService implementations are Spring beans (even when one is annotated with @Primary), you might run into weird troubles. But it should be a good start.

See also:

Community
  • 1
  • 1
Tomasz Nurkiewicz
  • 334,321
  • 69
  • 703
  • 674
  • Doh! We're way too late in dev cycle to move to 3.1 from 3.0. Can you suggest any ways to deal with it in 3.0? Looks awesome though. – Chris Kannon Jan 26 '12 at 15:02
  • @ChrisKannon: You can use Spring AOP to wrap every call to `DefaultMyService` and if the JNDI flag is set, redirect to `MyServiceStub`. I will try to add some example later, this technique worked pretty well for me. BTW migration from Spring 3.0 to Spring 3.1 is not such a big step... – Tomasz Nurkiewicz Jan 26 '12 at 15:13
  • An example of AOP for this would be great. As for Spring 3.1, I'll suggest it in the next meeting, but I'd like to do this out of the box with our existing library if possible. – Chris Kannon Jan 26 '12 at 16:28
  • @ChrisKannon: I updated my answer, it should be a good starting point. Now you see why `@Profile` is so nice :-). – Tomasz Nurkiewicz Jan 26 '12 at 18:06
  • Tomasz I tested your strategy with AOP and it works perfectly, thanks ! Here is the sample project: https://docs.google.com/open?id=0B98r2XJCoNvnTjNpMlhYNTVSUEc3UUFwQmZCN2xKdw – Frederic Conrotte Mar 18 '12 at 00:55
0

Maybe you can replace the class with a property and deploy your application with different property files. The production version would contain the name of the real class while the QA version would contain the name of a stub.

Maybe this http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/beans.html#beans-factory-extension-factory-postprocessors can help you.