I have a Java SE project that accepts will take some command line arguments are perform processing in a separate thread for specified argument. I am using the following weld microprofile config dependencies for injection
<dependency>
<groupId>org.jboss.weld.se</groupId>
<artifactId>weld-se-core</artifactId>
<version>3.1.0.Final</version>
</dependency>
<dependency>
<groupId>org.wildfly</groupId>
<artifactId>wildfly-microprofile-config-implementation</artifactId>
<version>1.2.1</version>
</dependency>
Here is my beans.xml
<?xml version="1.0"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
version="1.1" bean-discovery-mode="all">
<decorators>
<class>org.jboss.weld.environment.se.threading.RunnableDecorator</class>
</decorators>
</beans>
My project starts with the following main Class
@ApplicationScoped
public class Main {
@Inject
private Scheduler scheduler;
public void process(List<String> types) throws InterruptedException {
scheduler.schedule(types);
}
public static void main(String[] args) throws InterruptedException {
SeContainerInitializer initializer = SeContainerInitializer.newInstance();
try (SeContainer container = initializer.initialize()) {
Main main = container.select(Main.class).get();
List<String> argsList = Arrays.asList(args);
final List<String> types = parseArguments(argsList);
main.process(types);
}
}
}
Here is the code for my Scheduler class
@ApplicationScoped
public class Scheduler {
private static final Duration DEFAULT_WAIT_TIME = Duration.ofSeconds(30);
@Inject
@ConfigProperty(name = "POOL_SIZE", defaultValue = "10")
@Getter
private int poolSize = 5;
@Inject
@ConfigProperty(name = "WAIT_DURATION", defaultValue = "PT30S")
@Getter
private String durationStr;
@Getter
private Duration waitDuration;
private ThreadPoolExecutor executor;
@Inject
private Instance<ExportRunner> exports;
@PostConstruct
public void init() {
executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(poolSize);
try {
waitDuration = Duration.parse(durationStr);
} catch (DateTimeException | NullPointerException e) {
waitDuration = DEFAULT_WAIT_TIME;
}
}
public void schedule(Collection<String> types) throws InterruptedException {
if (types != null && !types.isEmpty()) {
//Spawn a new thread for each type
for(String type : types) {
ExportRunner runner = exports.get();
runner.setType(type);
executor.submit(runner);
}
} else {
throw new IllegalArgumentException("No FileTypes provided. Not performing export");
}
//Wait until every thread has completed
while(getTotalThreads() > 0) {
Thread.sleep(waitDuration.toMillis());
}
//shutdown executor which effectively ends the program
executor.shutdownNow();
}
public int getTotalThreads() {
return getActiveCount() + getQueueSize();
}
public int getActiveCount() {
return executor.getActiveCount();
}
public int getQueueSize() {
return executor.getQueue().size();
}
}
Here is the skeleton of the Runnable
@Dependent
public class ExportRunner implements Runnable {
@Setter
private FileType type;
//Has a custom producer and disposer
@Inject
@SEDataSource
private EntityManager em;
//Has a custom producer and disposer
@Inject
@SEDataSource
AlertService alertService;
//Has a custom producer and disposer
@Inject
@SEDataSource
HistoryService historyService;
@PostConstruct
private void init() {
//Set to same entity manager so that
//everythings happen inside single transaction
alertService.setEm(em);
historyService.setEm(em);
}
@PreDestroy
public void cleanup() {
log.info("ExporterRunner @PreDestroy was called");
}
public void run() {
try {
//do processing
} finally {
log.info("Processing Complete");
}
}
}
The issue that I am running into is that the injected Objects (the Runnable and the contained services and EntityManager) and never getting released until all of the threads have completed and the executor.shutdown() command is executed.
I believe that since the Runner is marked as @Dependent, it is using the Scope of the Object that injected it; which would make it @ApplicationScoped. I tried to mark the class with the @ThreadScoped (org.jboss.weld.environment.se.contexts.ThreadScoped) as follows
@ThreadScoped
public class ExportRunner implements Runnable {
...
}
But that causes the following exception
org.jboss.weld.contexts.ContextNotActiveException: WELD-001303: No active contexts for scope type org.jboss.weld.environment.se.contexts.ThreadScoped
I feel like I need to use the @ActivateThreadScope (org.jboss.weld.environment.se.contexts.activators.ActivateThreadScope) annotation, but I haven't found any examples of how to use it. Does anyone know how I make my Runnable not @ApplicationScoped?