Tuesday, February 17, 2015

Open AM Version Number Source (in 12.0.0)

This post is mostly for me to remember how I found where Open AM's version comes from since I used a number of mechanisms to find it and I don't want to forget them and the trip was amazingly long and convoluted. But I did successfully arrive at an end. And if it helps others when troubleshooting Open AM then that is a bonus.

As shown below, Open AM has a Version button in the top left corner of its admin console when signed in as an administrator. 



When clicking that button you typically get a popup browser window showing Open AM's current version like so:



Upon recently updating one of our servers to version 12.0.0 the update failed to offer us the typical prompt asking if we wanted to upgrade the configuration store. And upon starting the server up we seemed to be in some kind of limbo state where most of the console worked but there were some inaccessible areas including the version. It came up empty as in no version was discernible. Support said that the version was what triggered the prompt to upgrade configuration. And that prompted the following steps to find out the source from which the version value originated.

First: Chrome Developer Tools

That page holds the value. So where is it getting it from. I opened Chrome's Developer Tools, selected the Network pane, and checked Preserve Log so it wouldn't clear after a redirect or loading of a new page. Then I clicked the Version button again. It turns out that the page is loaded from the URL where /sso is Open AM's root in my installation:

/sso/ccversion/Version?
&productNameSrc=../console/images/PrimaryProductName.png
&windowTitle=Version+Information+-+

But this is a frameset with the gray section containing the version number value being loaded from a URL of:

/sso/base/Version

which returns text/html content. Looking through the server's web.xml doesn't show anything mapping to that path directly. Having done some debugging before in Open AM's JATO conundrum led me to wonder if this was being handled by a JATO JSP. Knowing from previous experience with the Login page and its affiliates that JATO typically maps the last path item to a similarly named JSP file I grabbed my next two tools.

Next Up: *nix's Find & Open AM's source

I opened a shell window, changed directory to the root of Open AM version 12's source. If you don't have Open AM's source you can download it from http://openam.forgerock.org/. Then I used "find" to see if there was a file by the name of Version.jsp somewhere in Open AM's source:

find . -name "Version.jsp" 

I'll use find in a number of places below and in each instance I will always be located in the root of Open AM's source. Running this command found a likely candidate at:

/openam/openam-console/src/main/webapp/console/base/Version.jsp

Opening that file and comparing its divs and their classes with the content of the frame convinced me that was indeed the provider of that content. This JSP was a typical JATO JSP. It contained a view bean declaration where the view bean in JATO can be seen as the controller and source of the model backing the JSP:

<jato:useViewBean 
className="com.sun.identity.console.base.VersionViewBean" 
fireChildDisplayEvents="true">

And based upon the divs and their classnames in that page the JSP was injecting the version content using the following tag. In JATO, that name attribute typically ends up being the name of a value set on the view bean:

<cc:text name="txtVersion" />

So back to our good friend Find. I then ran the following command in the root:

find . -name "VersionViewBean.java"

That found the java file at:

/openam/openam-console
/src/main/java/com/sun/identity/console/base/VersionViewBean.java

Fortunately, this view bean class file was only 78 lines long and it did indeed have a line setting a txtVersion value:

setDisplayFieldValue("txtVersion", AMSystemConfig.version);

Looking at imports  and using find the AMSystemConfig class is found in the same subproject at:

/openam/openam-console
/src/main/java/com/sun/identity/console/base/model/AMSystemConfig.java

Now you'd think that would be completely obvious based upon a similar package structure. But be forewarned that that is not always the case. Often I've found similarly packages classes in other Open AM submodules. So when you can't find a file drop back to find and let it do the looking for you. 

AMSystemConfig shows an interesting characteristic of Open AM. Its admin console can be run locally or remotely and delegate to the real servers. If the console is not remote then it gets the value for its version variable from:

SystemProperties.get(Constants.AM_VERSION)

I'll come back to SystemProperties in a minute. If the console is running remotely then a call is made to:

/sso/SMSServlet?method=version

Again looking in my web.xml I see that path is handled by:

<servlet>
        <servlet-name>SMSServlet</servlet-name>
        <servlet-class>com.sun.identity.sm.SMServlet</servlet-class>
</servlet>
...
<servlet-mapping>
    <servlet-name>SMSServlet</servlet-name>
    <url-pattern>/SMSServlet</url-pattern>
</servlet-mapping>

The SMServlet is found in:

/openam/openam-core/src/main/java/com/sun/identity/sm/SMServlet.java

That class too is a small one, only 88 lines long, and shows that it too is getting the value from the same place that AMSystemConfig is when running locally:

SystemProperties.get(Constants.AM_VERSION)

So that confirms where the version number is ultimately coming from. Hence, back to SystemProperties. Again, looking at the package path of the class and using find, I found that SystemProperties is located in:

/openam/openam-core
/src/main/java/com/iplanet/am/util/SystemProperties.java

And now the code got a lot more involved. SystemProperties is over 700 lines long. Its get method looks in a couple different places for values. So that is when I brought in my next tool.

Remote Debugging

One of the things about java that I fell in love with when it came out was the Java Debug Wire Protocol allowing any JVM to be started with debugging turned on even supporting a "suspend" mode preventing any code from running until a debugger connected to the JVM after start up thus allowing us to step through fleeting startup situations as well.

I have Open AM running in Tomcat8. The catalina.sh script supports specifying a parameter of jpda followed by start to trigger launching the JVM in debug mode. The comments at the top of catalina.sh are extremely helpful. By default, it does not start the JVM in debug mode. By adding a setenv.sh script as a peer to catalina.sh you can add values as defined in the comments to override the defaults. 

So I created setenv.sh  with the following value:

export JPDA_SUSPEND="y"

Then I launch tomcat with the following command:

$TOMCAT_HOME/bin/catalina.sh jpda start

And sure enough, using tail on logs/catalina.out shows that the JVM started in debug mode and since no further activity is being logged it appears to be suspended waiting for a debugger to connect to that port.

Listening for transport dt_socket at address: 8000

I use Intellij currently. But used eclipse for a number of years. Both readily support remote debugging. With Intellij open on the POM of the Open AM source:

/openam/pom.xml

I then created a Debug Configuration of type Remote and set the host to be 127.0.0.1 and port 8000. Before starting it up I opened SystemProperties and set a breakpoint at the top of the get method. Then I started up that debug configuration. It connected and some time thereafter I was stepping through that code.

However, I first wanted to know where the version value was coming from. Recalling that the most direct way to trigger retrieval of the version from SystemProperties was through the URL called by AMSystemConfig when the console is running remotely, I entered that URL into the browser and immediately had the debugger stop execution in the method. The key being passed to the method was:

com.iplanet.am.version

The value ultimately was being returned from the props properties object in SystemProperties. To find where that we being loaded I added breakpoints in every location where props was being set or added to. Our desired value was being returned in the Properties object returned from this line in the initializeProperties method. 

ServerConfiguration.getDefaults(appToken)

Again using find I located the ServerConfiguration file in:

/openam/openam-core/src/main/java
/com/sun/identity/common/configuration/ServerConfiguration.java

Stepping through these lines is getting into the core of Open AM's configuration code. It turns out that it load a ServiceConfigManager instance for the iPlanetAMPlatformService, obtains its global configuration, then loads a ServiceConfig instance for its sub configuration of com-sun-identity-servers and from that instance loads another ServiceConfig instance for sub configuration server-defaults.  From that instance of  it then gets the serverconfig attribute which holds a Set of 155 key/value strings with the = character as a delimiter between keys and values. And one of those values is the one we are looking for.

So where does this last paragraph mean? I've had some experience with ServiceConfigManager and  ServiceConfig. If you look in Open DJ at the root of your configuration you'll see the following items. I'm using Apache Directory Studio here. Note that my configuration is at the default location. Your root DN may differ depending on how you configured your system.


Notice the services element. Expanding it and looking for an element with an ou of iPlanetAMPlatformService. This relates to the ServiceConfigManager instance that we acquired. From there each of the other steps relate nicely to the structure beneath that element as shown below. There is a GlobalConfig with a sub configuration of  com-sun-identity-servers  and a sub configuration of server-defaults



By selecting that last item we can see that it has 157 values for the sunKeyValue multivalued attribute. 


Expanding it and looking through the list we find the value that we are searching for. 


And interestingly, if we use Directory Studio to modify the value and refresh the browser we immediately see that change appear.


One Final Note

It should be noted that the relationship between LDAP values and the code should not be relied upon. Things could change from release to release. But the tools and steps to locate this information when problems arise will hopefully be of use to someone. Most likely of course is that person will be me when I come back to my own work six months from now. At which time I'll be grateful for this breadcrumb.

Enjoy.

No comments:

Post a Comment