Search code examples
javaparallel-processingcdiweld

Parallel webservices access in a Weld CDI environment


We're developing a Web Frontend using JSF 2 and Weld Cdi on Tomcat.
Now I've a problem executing multiple webservices in parallel to optimize the request time.
The user may select mutliple items form a list.
For each selected item, the process gathers it's information from one webservice using the list key as parameter.

My current approach is using a Producer, that returns the webservice port interface, which is injected into the bean. The bean calls this webservie in a loop for each selected key.

@Inject
private WSAnzeigeAssetsummen serviceAccess;
:

for ( Integer pfNr : sessionKeys.getPfnrList() ) {
   summaryTable = serviceAccess.execute(snr, pfnr, requestType, "", desiredRows, userName);
   processResult(summaryTable):
}

To get faster, I tried to use a ExecutorService and as many workers as needed, which are returning Futures.

The problem of this construct is, that I can't inject the service port into the worker, cause the worker is not managed. Creating the service port by hand, works but is not appreciated, cause it ignores the producer class.

Also when testing, it's not possible to inject a dummy service port, which delivers predefined result sets.

Since I did not find anything, about parallel execution in a tomcat-weld enviroment, there must be something wrong with my approach.

What is the correct approach to solve such a situation ?

Edit: To be more clear what I tried...

public class DataCollector implements ISumRequest<Integer, Integer, String, FutureResult> {

ExecutorService pool = Executors.newCachedThreadPool();
@Inject
SessionBean sessionBean;

public Future<FutureResult> collectInformation(Integer snr, Integer pfnr, String requestType) {

    CollectWorker worker = new CollectWorker (snr,pfnr,requestType,sessionBean.getUserName());     
    return pool.submit(worker);        
}

}

When doing like this, the worker is not managed.


Solution

  • You can wrap your created worker in a CDI creational context, something like this:

    @Inject
    private BeanManager beanManager;
    
    public <T extends Object> T performInjection(final T obj) {
        if (this.beanManager != null) { // only do injection if the bean manager is present.
            // Create a creational context from the BeanManager
            final CreationalContext creationalContext = this.beanManager.createCreationalContext(null);
            // Create an injection target with the Type of the instance we need to inject into
            final InjectionTarget injectionTarget = this.beanManager.createInjectionTarget(this.beanManager.createAnnotatedType(obj.getClass()));
            // Perform injection into the instance
            injectionTarget.inject(obj, creationalContext);
            // Call PostConstruct on instance
            injectionTarget.postConstruct(obj);
        }
        return obj;
    }