Seiten

Montag, 3. Februar 2014

One way to deadlock a JBoss AS 7

Take a standard JBoss AS 7 and create/deploy a simple remote stateless session bean with two methods. One of them should be asynchronous. As shown in the following example the asynchronous method creates a little bit of load and the second one simulates a login.
public interface IMyBean
{
 Future<Void> doNothing();
 void login();
}
@Stateless
@Remote(IMyBean.class)
public class MyBean implements IMyBean
{
 @Override
 @Asynchronous
 public Future<Void> doNothing()
 {
  //Creates Load
  int lo = 0;
  for (int i = 0; i < 5000; i++)
  {
   lo += i;
  }
  System.out.println("nothing done");
  return new AsyncResult<Void>(null);
 }
 @Override
 public void login()
 {
  System.out.println("logged in");
 }
}
In the next step besides 10 EJB-Remote-Clients which access the asynchronous method parallel we create an additional client which repeatly tries to login. (Note: Classpath contains jboss-ejb-client.properties and jboss-client.jar)
public class HangTest
{
 public static void main(String args[]) throws NamingException
 {
  //EJB-Remote-Access
  Properties jndiProps = new Properties();
  jndiProps.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
  Context context = new InitialContext(jndiProps);
  final IMyBean myBean = (IMyBean) context.lookup("ejb:/hangtest/MyBean!IMyBean");

  //Create and start asynchronous method threads
  final List<Thread> threadList = new ArrayList<>();
  for (int i = 0; i < 10; i++)
  {
   Thread thread = new Thread()
   {
    @Override
    public void run()
    {
     try
     {
      myBean.doNothing().get();
     }
     catch(Exception e)
     {
      e.printStackTrace();
     }
     finally
     {
      threadList.remove(this);
     }
    }
   };
   threadList.add(thread);
   thread.start();
  }

  //Repeated Login
  while(!threadList.isEmpty())
  {
   System.out.println("Try login...");
   myBean.login();
  }
 }
}
The code above works as expected which means that random server messages ("nothing done", "logged in") are printed out and that the login client can login. It also can be executed as many times as you want without a change in behavior.
BUT if you increase the number of asynchronous method clients for example to 20-30 (depending on your computer) then nothing is printed out and the login client can not login anymore. The JBoss or - to be more specific - some components of it are in a deadlock (Note: JBoss shutdown does not work anymore). So what has happened?

To answer this question we have to look into the standalone.xml, especially in the subsystem "urn:jboss:domain:ejb3". This section contains ejb3-related configurations and among them threadpools. In this context it is important that the default threadpool is shared between asynchronous calls, timers and ejb-remote.
<subsystem xmlns="urn:jboss:domain:ejb3:1.2">
...
 <async thread-pool-name="default"/>
 <timer-service thread-pool-name="default">
  <data-store path="timer-service-data" relative-to="jboss.server.data.dir"/>
        </timer-service>
 <remote connector-ref="remoting-connector" thread-pool-name="default">
  <thread-pools>
   <thread-pool name="default">
    <max-threads count="10"/>
    <keepalive-time time="100" unit="milliseconds"/>
   </thread-pool>
  </thread-pools>
   </remote>
</subsystem>

So if you stumble upon that problem either increase the max-thread count property or even better seperate the thread-pools for asynchronous and ejb-remote components.