I have a @Asynchronous
method in a singleton class which gets called from a EJB to do some cleanup in the system.
@Singleton
public class AuthTokenCleanup {
@Inject
AuthTokenService tokenService;
@Asynchronous
@Lock(LockType.READ)
@TransactionAttribute(NOT_SUPPORTED)
public void scheduleCleanupAuthTokens() {
try {
Thread.sleep(5000);
tokenService.cleanup();
} catch (InterruptedException ex) {
}
}
}
The snippet of code that is calling the singleton. It is inside a @Stateless
EJB
public boolean authenticate(String username, String password) {
boolean authenticated = false;
try{
Login login = loginLookup.findByUsername(username);
authenticated = login.equalPassword(password);
} finally {
tokenCleanup.scheduleCleanupAuthTokens(); //token cleanup happens here
}
return authenticated;
}
When undeploying the application it takes about 20-30 seconds. I see the following error in the catalina log file
SEVERE: The web application [/MyApp]
appears to have started a thread named [@Asynchronous MyApp-1.0-SNAPSHOT - 3]
but has failed to stop it. This is very likely to create a memory leak.
I'm running the application on Apache-tomee-1.7.1-jaxrs. A snippet of my pom.xml config
<properties>
<tomee.version>1.7.1</tomee.version>
<openejb.javaee.api>6.0-6</openejb.javaee.api>
<endorsed.dir>${project.build.directory}/endorsed</endorsed.dir>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
<compilerArguments>
<endorseddirs>${endorsed.dir}</endorseddirs>
</compilerArguments>
</configuration>
</plugin>
When I remove the @Asynchronous
call the undeploy takes 1-3 seconds and no error in log file.
Is the behavior and error expected in TomEE when using a @Asynchronous
method call if not what am I doing wrong?
The EJB spec requires that EJB components do not directly access thread primitives (including Thread.sleep()
). This is because the container needs to handle thread management, and won't be able to do this efficiently if the application is doing this.
If the clean-up task needs to be done regularly in a singleton, have you considered using a scheduled job, using the @Schedule
annotation? This will fit better with the EJB model, and also means that you're not starting a new asynchronous request for every call processed, so it's likely to scale much better too.