Monday, November 10, 2014

How to Tell Programmatically if OpenAM has Finished Starting Up

Update: this post and its thread eventually led to the correct and OpenAM specific mechanism. The short answer is: com.sun.identity.setup.AMSetupServlet (located in openam's core jar) has a public static method isCurrentConfigurationValid() that returns a boolean value. During startup it repeatedly returns false until OpenAM is ready for business and then returns true. Calling core classes after that point solves the problems that I originally outlined in this post. (2014.12.16)


I'm breaking a promise to flesh our more detail from my last post but do plan on coming back to provide those details later. I just couldn't wait to capture and share what I just found. As noted in my previous post I'm adding code to make OpenAM be a RADIUS Authentication server. I've spent some time getting configuration pieces into OpenAM's console. (Post a comment if those notes would be useful in a post.) Now I need to read them at startup time. Simple. Get a handle on the ServiceConfigManager class and go get what you need. Well, not quite.

It turns out that if you call some of these core pieces of OpenAM before they are all brought up you end up shooting OpenAM in the brain. It won't ever recover and no requests to the server will ever get an answer. OpenAM's docs says that the best way to know if OpenAM is up and running is to look at isAlive.jsp rooted at the web-app-root/isAlive.jsp. And it is true. If you request that URL as soon as you start tomcat the browser will spin for about 20 seconds on my box and finally the page will come up indicating Server is ALIVE.

So I thought, there must be some blocking code in isAlive.jsp that does that magic. So I tried using its few lines of code. No go. They too access sensitive ground and OpenAM plays dead. So I stepped back to calling it over http. Now, I can get the webapp's root path from ServletContext. But what port is my server running on? That is only available to servlet code when an incoming http request appears. Until then there is no standard way to get it. PLEASE correct me if I'm wrong there. I'd love to learn that secret. But the specs don't cover it except in the case of an http request.

Then I thought, hey, I can get a Dispatcher and call it without going down to the http protocol. Right? And that is where the magic just happened to appear. Or rather, it disappeared; into null.

In the following code the astute will note that I'm using SpringFramework constructs. Yes I have Spring's Dispatcher servlet defined in OpenAM's web.xml for dependency injection and some REST endpoints that I've added to my local code base. But what I'm doing here can be done with a simple servlet in its init method where you get the ServletContext from the ServletConfig passed to you. That being the case lets look at the code:

@Controller
public class RuntimeServiceController implements ServletContextAware {

    @Override
    public void setServletContext(ServletContext sc) {
        final String cp = sc.getContextPath();
        final String url = cp + "/isAlive.jsp";

        RequestDispatcher d = sc.getRequestDispatcher(url);
        HttpServletRequest req = createRequest();
        HttpServletResponse res = createResponse();
        try {
            d.forward(req, res);
        } catch (ServletException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        // then evaluate captured content
        if (isServerIsUp(res)) {
            // go look at config stuff here
        }
    };
}

When this code ran I got an NPE when calling d.forward(req, res). It doesn't take rocket science to tell that my dispatcher, d, was null. Hmmm. Weird. Maybe the JSP engine is not up yet. If I keep checking will I eventually get a dispatcher? After all, I know that the resource exists at that URL or will at some point in time. So I wrote another version like so:

@Controller
public class RuntimeServiceController implements ServletContextAware {

    @Override
    public void setServletContext(final ServletContext sc) {
        final String cp = sc.getContextPath();
        final String url = cp + "/isAlive.jsp";

        Runnable r = new Runnable() {

            @Override
            public void run() {
                RequestDispatcher d = null;
                long start = System.currentTimeMillis();

                while (d == null) {
                    d = sc.getRequestDispatcher(url);

                    if (d != null) {
                        // now call isAlive.jsp
                    }
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                        return;
                    }
                }
            }
        };
        Thread t = new Thread(r);
        t.setName("RuntimeServiceController isAlive.jsp Accessor");
        t.setDaemon(true);
        t.start();
    }

}

I'm launching a Thread to keep testing for the dispatcher so that I don't lock up the web container's start up processes. And this still ended up giving an error. But not until I attempted to call the dispatcher's forward. The dispatcher did indeed eventually appear after about 20 seconds on my box. And that was about how long it took for isAlive.jsp to appear in the browser. Hmmm. Was there a connection? Is OpenAM ready for business once the dispatcher is available?

I stripped out my code for calling isAlive.jsp and added config calls like the code in isAlive.jsp itself and guess what? It worked!!! Now to be fair, I don't know if that is guaranteed to always work. But so far it hasn't failed. Perhaps others familiar with OpenAM can answer that. Any Forgerock gurus around that can answer that? And what about other containers? I don't know. I'm running on tomcat8 on JDK 1.6. If you test it on your server post a comment here and let the rest of us know.

Enjoy.