Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating and wiring related instances at runtime with CDI

I have a Service in a Java SE application (without any application server) which creates Algorithm instances and run them.

  • Every Algorithm instance needs a new (separate) ActionExecutor and a new (separate) AlgorithmState.
  • ActionExecutor also needs an AlgorithmState instance and this instance have to be the same as Algorithm gets.

How can I achieve this with CDI? I've tried constructor injection and @New on both parameters of Algorithm but I guess this is not what I want.

Service class:

import java.util.ArrayList;
import java.util.List;

import javax.enterprise.inject.Instance;
import javax.inject.Inject;

public class Service {

    @Inject
    private Instance<Algorithm> algorithmInstance;

    public void run() {
        final List<Algorithm> algorithms = new ArrayList<>();
        for (int i = 0; i < 3; i++) {
            final Algorithm algorithm = algorithmInstance.get();
            algorithms.add(algorithm);
        }

        for (final Algorithm algorithm: algorithms) {
            algorithm.doSomething();
        }
    }

}

Algorithm class:

import java.util.concurrent.atomic.AtomicInteger;

import javax.enterprise.inject.New;
import javax.inject.Inject;

public class Algorithm {

    private static final AtomicInteger counter = new AtomicInteger(100);

    private final ActionExecutor actionExecutor;
    private final AlgorithmState algorithmState;

    private final int id;

    @Inject
    public Algorithm(@New final ActionExecutor actionExecutor, @New final AlgorithmState algorithmState) {
        this.actionExecutor = actionExecutor;
        this.algorithmState = algorithmState;
        id = counter.incrementAndGet();
        System.out.println("algorithm ctor#" + id);
    }

    public void doSomething() {
            System.out.printf("do something, algorithm id: #%d: executor id%sd, stateId: %d, executor->stateId: %d%n", id,
            actionExecutor.getId(), algorithmState.getId(), actionExecutor.getAlgorithmStateId());
    }
}

ActionExecutor class:

import java.util.concurrent.atomic.AtomicInteger;

import javax.inject.Inject;

public class ActionExecutor {

    private static AtomicInteger counter = new AtomicInteger(200);

    private final AlgorithmState algorithmState;

    private final int id;

    @Inject
    public ActionExecutor(final AlgorithmState algorithmState) {
        this.algorithmState = algorithmState;
        id = counter.incrementAndGet();
    }

    public int getId() {
        return id;
    }

    public int getAlgorithmStateId() {
        return algorithmState.getId();
    }
}

AlgorithmState class:

import java.util.concurrent.atomic.AtomicInteger;

import javax.annotation.PostConstruct;
import javax.inject.Inject;

public class AlgorithmState {

    private static final AtomicInteger counter = new AtomicInteger(300);

    private final int id;

    @Inject
    public AlgorithmState() {
        id = counter.incrementAndGet();
    }

    @PostConstruct
    public void start() {
        System.out.println("state start#" + id);
    }

    public int getId() {
        return id;
    }
}

And a ServiceMain class for testing:

import java.util.List;

import javax.enterprise.event.Observes;
import javax.inject.Inject;

import org.jboss.weld.environment.se.bindings.Parameters;
import org.jboss.weld.environment.se.events.ContainerInitialized;

public class ServiceMain {

    @Inject
    private Service service;

    public void printHello(
            @Observes final ContainerInitialized event,
            @Parameters final List<String> parameters) {
        System.out.println("ServiceMain:" + service);
        service.run();
    }

    public static void main(final String[] args) {
        org.jboss.weld.environment.se.StartMain.main(args);
    }

}

Currently it prints the following:

do something, algorithm id: #101: executor id201d, stateId: 302, executor->stateId: 301
do something, algorithm id: #102: executor id202d, stateId: 304, executor->stateId: 303
do something, algorithm id: #103: executor id203d, stateId: 306, executor->stateId: 305

What I need is that the stateId and executor->stateId are the same:

do something, algorithm id: #101: executor id201d, stateId: 301, executor->stateId: 301
do something, algorithm id: #102: executor id202d, stateId: 302, executor->stateId: 302
do something, algorithm id: #103: executor id203d, stateId: 303, executor->stateId: 303

edit:

Currently I'm getting the AlgorithmState from the AlgorithmExecutor but it messes up the model, I'd like to avoid that.

like image 683
palacsint Avatar asked Mar 13 '14 11:03

palacsint


1 Answers

First of all, the @New qualifier was replaced with the @Dependant scope, which is the default scope anyway. I'd imagine it was changed in CDI 1.1 due to the confusion that it causes. Plus, @New is a qualifier, not a scope, and is from a separate standard that can be used for multiple purposes (I don't think @New is really used by any standards).

It looks like you'd want to use @Produces in AlgorithmState instead of @Inject. Check out the Weld documentation on the subject for some more details. You might want to create a specific scoped type like so:

@ScopeType
@Retention(RUNTIME)
@Target({TYPE, METHOD, CONSTRUCTOR})
public @interface AlgorithmScoped {}

Then you could modify the AlgorithmState constructor:

@Produces @AlgorithmScoped
public AlgorithmState() {
    // ...
}

Add that scope to AlgorithmExecutor, and then also to Algorithm. I don't think you should be trying to inject AlgorithmState into Algorithm; get that from the AlgorithmExecutor instance! In fact, if you do that, that should solve the entire problem in a nutshell.

like image 159
Matt Avatar answered Oct 09 '22 15:10

Matt