0 Replies Latest reply on Sep 9, 2014 8:44 AM by Marcel Šebek

    How a MDB startup can depend on web startup

    Marcel Šebek Newbie

      Hi.

       

      I have an application with two interfaces - REST and JMS. In one scenario a JMS message is received by a MDB, the MDB calls an external system, and the external system calls my application back via REST api. However, when a JMS message is present in the queue during application deployment, this mechanism does not work. MDB is started before REST api is available, and the external system fails to call this api.

       

      The first idea how to solve this problem is to instruct wildfly somehow to start MDB after web is available. But that is currently not possible. After some investigation, I've found an ugly workaround for this case, but it has some problems. I'm opening this topic to discuss how a proper solution should look like (perhaps some @Depends annotation?).

       

      In my current solution, I've created a servlet:

       

      @WebServlet(loadOnStartup = Integer.MAX_VALUE)
      public class StartupServlet extends HttpServlet {
         @Inject
          private WebStartup webUtils;
      
          @Override
          public void init(ServletConfig config) throws ServletException {
              webUtils.startWeb();
          }
      
          @Override
          public void destroy() {
              webUtils.stopWeb();
          }
      
      }
      

       

      and a bean containing a lock:

       

      @ApplicationScoped
      public class WebStartup {
          /** A simple way how to get "non-reentrant" rw-lock - read lock ticket is 1, write lock ticket is MAX. */
          private final Semaphore semaphore = new Semaphore(Integer.MAX_VALUE);
      
          public WebStartup() {
              stopWeb();
          }
      
          void startWeb() {
              semaphore.release(Integer.MAX_VALUE);
          }
      
          void stopWeb() {
              semaphore.acquireUninterruptibly(Integer.MAX_VALUE);
          }
      
          /**
           * After this call finishes successfully, the called obtains a "lock" ensuring that web is available.
           * The lock needs to be released by calling releaseWebStart().
           */
          void waitWebStart() throws InterruptedException {
              semaphore.acquire();
          }
      
          void releaseWebStart() {
              semaphore.release();
          }
      
      }
      

       

      and also a simple AutoCloseable wrapper for easier usage:

       

      public class WebStartupHandle implements AutoCloseable {
          private final WebStartup ws;
      
          public WebStartupHandle(WebStartup ws) throws InterruptedException {
              this.ws = ws;
              ws.waitWebStart();
          }
      
          @Override
          public void close() {
              ws.releaseWebStart();
          }
      }
      

       

      A MDB then just needs to inject WebStartup bean and wrap its code in a try-close block using WebStartupHandle:

       

      try (WebStartupHandle wsh = new WebStartupHandle(webStartup)) {
      // call external system here
      }
      

       

      This solution works, but it is neither nice nor problem-free. There is no guarantee that this servlet is started after REST api, it just works now, but that can change in future versions. It would be nice to have some mechanism to delay MDB startup. Currently, one can only specify startup order for singleton beans. I understand that for session beans, it can be tricky to delay their startup, but MDBs cannot be injected, they only process messages.

       

      I think this use case can be quite common and it definitely deserves support in wildfly. Once a solution is agreed upon, I'm ready to open a feature request for it.