1

I've annotated a Thread class as follow:

@Transactional(propagation = Propagation.REQUIRED)
@Component
@Scope("prototype")
public class ThreadRetrieveStockInfoEuronext implements Runnable {
   ...
}

The Thread is wired into a Controller class, and called via Global.poolMultiple.execute(threadRetrieveStockInfoEuronext);

public class RetrievelController {

    @Autowired
    private ThreadRetrieveStockInfoEuronext threadRetrieveStockInfoEuronext;

    @RequestMapping(value = "/.retrieveStockInfo.htm", method = RequestMethod.POST)
    public ModelAndView submitRetrieveStockInfo(@ModelAttribute("retrieveStockInfoCommand") RetrieveStockInfoCommand command, BindingResult result, HttpServletRequest request) throws Exception {

        Global.poolMultiple.execute(threadRetrieveStockInfoEuronext);
        return new ModelAndView(".retrieveStockInfo", "retrieveStockInfoCommand", command);
    }

The Global Class:

@SuppressWarnings("unchecked")
@Component
public class Global {

    // create ExecutorService to manage threads
    public static ExecutorService poolMultiple = Executors.newFixedThreadPool(10);
    public static ExecutorService poolSingle = Executors.newFixedThreadPool(1);
...
}

When running the application, the following exception occurs:

java.lang.IllegalArgumentException: Can not set com.chartinvest.admin.thread.ThreadRetrieveStockInfoEuronext field

com.chartinvest.controller.RetrievelController.threadRetrieveStockInfoEuronext to com.sun.proxy.$Proxy52

1 Answer 1

3

That's because your Runnable implementation is a Spring component, annotated with @Transactional, and that declarative transactions are achieved, in Spring, by wrapping the actual instance of the class inside a JDK interface-based proxy that handles the transaction. In consequence, the actual object that is autowired is not an instance of ThreadRetrieveStockInfoEuronext, but an instance of its interface, Runnable, that delegates to an instance of ThreadRetrieveStockInfoEuronext.

The usual fix to this problem is to autowire the interface instead of autowiring the concrete type. But in this case, this class shouldn't be a Spring component in the first place, and it shouldn't be transactional either. BTW, making it a prototype gives you the illusion that a new instance will be created each time submitRetrieveStockInfo() is called, but that's incorrect: a single instance is created and autowired into the RetrievelController singleton, and the same runnable is thus used for all the requests to the controller.

Just make ThreadRetrieveStockInfoEuronext a simple class, instantiate it using new, passing a Spring bean as argument, and make its run() method call a transactional method of that Spring bean:

@Transactional
@Component
public class ThreadRetrieveStockInfoEuronextImpl implements ThreadRetrieveStockInfoEuronext {
    @Override 
    void doSomething() { ... }
}

public class RetrievelController {

    @Autowired
    private ThreadRetrieveStockInfoEuronext threadRetrieveStockInfoEuronext;

    @RequestMapping(value = "/.retrieveStockInfo.htm", method = RequestMethod.POST)
    public ModelAndView submitRetrieveStockInfo(@ModelAttribute("retrieveStockInfoCommand") RetrieveStockInfoCommand command, BindingResult result, HttpServletRequest request) throws Exception {

        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                threadRetrieveStockInfoEuronext.doSomething();
            }
        };

        Global.poolMultiple.execute(runnable);
        return new ModelAndView(".retrieveStockInfo", "retrieveStockInfoCommand", command);
    }
Sign up to request clarification or add additional context in comments.

1 Comment

Many thanks JB for your advise , The solution works perfectly !!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.