Since Brian Stansberry was kind enough to write an article on the AS 7 interal architechture, the aptly named "AS7 Internal Architecture Overview", https://community.jboss.org/wiki/AS7InternalArchitectureOverview, I thought I'd document my attempts of walking through it in the form of a tounge-in-cheek diary. Hopefully, he'll have time to correct my misunderstandings (as I'm sure there will be plenty). If nothing else, he will at least have proof that at least one person has read through his article.
The first part of this article will deal with setting up the debugging environment and stepping through the aquiring of the ServerEnvironment and the initializations of the BootStrapImpl and ServerContainerImpl instances involved. More will follow as I debug along.
Well, where to start? Since we all know by now that the AS7 is blazingly fast, we'll need a debugger to place some breakpoints so we'll have a chance of seeing what's going on. Grab Eclipse Juno SR1 from http://eclipse.org/downloads/ and JDK 7 from http://www.oracle.com/technetwork/java/javase/downloads/index.html. Install the JDK and Eclipse, you might also want to set JAVA_HOME to point at the JDK and set -vm C:\Java\jvm\jdk1.7.0_12\bin\javaw.exe (on Windows) in eclipse.ini. You'll also want JBoss Tools (since you're the 'living on the edge' kind of person, use the nightlies at http://download.jboss.org/jbosstools/updates/nightly/core/trunk/). Then we'll need some sources. Grab the source zip from https://github.com/jbossas/jboss-as or even better, install git and do a 'git clone git://github.com/jbossas/jboss-as.git'. Since you're in a hurry (and running tests is for CI-systems anyway, right?), build the as with 'build clean install -DskipTests' After the build has completed, start Eclipse and define a new server pointing at that runtime (it's under the build/target dir). Remember to set the startup timeout to a large number (integers are cheap).
The main method
In the beginning was Main.main(String...). And the return was void. And he thought he'd better roll up his sleeves and get to work. Since Brian told us bootstrapping starts at the server module, use the Eclipse "Import existing Maven project"-feature to pull in the jboss-as-server project from the "server" folder in the AS source. In the article we learn that the startup scripts point JBoss Modules to this very module and the Main server class so this is where we'll place our breakpoint and press the server debug button to boot it up in debug mode.
Takeoff! The breakpoint is hit and you can point the source lookup to the jboss-as-server project on your workspace. If you look at the import list of Main, you will notice references to projects and subprojects such as "controller", "process", "version", "logmanager", "modules", "msc" and "stdio". As this tutorial proceed, I might venture further in but for now I'll take the stance that as-subprojects (such as "controller") are imported and examined when stumbled upon but as-sibling projects (such as modules and threads) are stepped out of in the debugger. Those who can't wait are of course invited to check out the sibling projects also and step into them. The notable exception to the policy will be the MSC (Modular Service Container) which we will open up since, well, that's a large part of the AS.
The first thing the main module does is sets up the basic logging and capturing of the standard I/O streams. It also sets up JBoss Modules with the basic information it need to be able to do it's job (apparently access to the stuff in "org.jboss.vfs", the Virtual File System module) is required. The first real "AS"-work is done in determineEnvironment, where we determine the running environment by assembling a ServerEnviroment. You might also want to import the controller project at this point because we will see RunningMode there (although it's just a simple enum that tells if we're running in normal or admin mode).
The running environment
This method takes the parameters to main(String...), the system properties, the system environment and a flag that tells if this is a standalone instance booting. Both the fetching of the system properties and the environment checks for precense of a security manager and runs the call through AccessController.doPrivileged if one is found. In other cases, a straight call is performed. The main job of determineEnvironment is parsing through the arguments, sometimes just printing out usage or version information and exiting or setting various system/env values based in the incoming prameters.
In our case we hit the determineEnvironment with the "-b" "localhost", "--server-config=standalone.xml" and some system properties/env data. The args are looped and "-b, "localhost" is converted added to the system properties as "jboss.bind.address" => "localhost" and the serverConfig value is set to standalone.xml. A new ProductConfig is created (it's in the version project, import it), it looks for a system env JBOSS_PRODUCT_CONF and if that can't be found returns the path to a product.conf in the AS bin dir. We don't have any of that so we end up with null for all values, on an EAP, apparently the manifest data of the jar in the org.jboss.as.product module is read according to the slot property which was read from the for-us-fictional property file.
Equipped with a deeper knowledge of our three null values, we move on. The determineEnvironemt should return a ServerEnvironment and so it does in the last line by throwing all it got into the constructor of said class. The code in the constuctor is longish but fairly easy to step through, essentially what it does is determine host names and various directories (temp, work, deployments etc) to use based on the information passed in. All this is the encapsulated into the ServerEnvironment object. It
also places some of these values back into the system properties. At one point it also copies the original system properties and tucks them away, possibly to be restored when the server reloads or something. Anyways, at this point when the object is returned, the boot process has determined everything it needs to know about its running enviroment and can move on.
Bootstrapping the bootstrapping
At the end of the main loop we get an instance of BootstrapImpl and by doing so we also get a ServiceContainerImpl and here the simple part of the tutorial ends ;-) Import the jboss-msc project.
BootstrapImpl creates a ServiceContainerImpl which extends a ServiceTargetImpl. A ServiceTargetImpl is a hiearchial construct with a parent, a Map of ServiceListener with assoicated inheritance data and a Set of dependent service names. The ServiceTargetImpl has various methods for adding/removing dependencies and listeners and working with ServiceBuilders. The javadoc says it's a "Abstract base class used for ServiceTargets" but the implementation looks quite concrete (and central) to the MSC. A ServiceListener is an interfaces that can be used for listening for various events in the MSC, such as state transitions, services added, various dependencies becoming available/unavailable etc. The ServiceListener.Inheritance determines if the listener should be inherited to all sub-service-targets, only once or not at all. By using the subTarget() method you can get a new ServiceTargetImpl with the current instance as the parent (the hiearchial part). There is apparently also a batch-version
of the method but let's skip it for now. A ServiceName is an unique (complicated, hierarchial) identifier with a String name, a ServiceName parent and a hashcode.
But back to the ServiceContainerImpl. In the instance variables we have a 'registry' - a Map of ServiceName -> ServiceRegistrationImpl, 'start' (more on the ServiceRegistrationImpl later on) - timestamp of start time, 'shutdownInitiated' - timestamp of shutdown started, 'terminateListeners' - a list of TerminateListener instances interested in the shutdown event, 'profileOutput' - ?, 'terminateInfo' - timestamps of a possible shutdown in progress, 'down' - flag telling if container is down, 'executor' - the container thread pool executor, 'name' - the name of the container, 'mBeanServer' - a handle to the MBeanServer, 'objectName' - the MBean name of the container and 'containerMXBean' - a MBean interface implementation for the container,
'SERIAL' - an AtomicInteger used for getting a serial number for the container, 'PROFILE_OUTPUT' - the value of the system property jboss.msc.profile.output (or null), 'executorSeq' - an AtomicInteger used for getting a sequence number for the executor, 'HANDLER' - an exception handler for uncaught exceptions slipping through and 'POLICY' - a Thread pool executor policy. Phew!
There are also some nested classes available. The executor is a ContainerExecutor, which extends a ThreadPoolExecutor. When created, it grabs an id from executorSeq and creates a threadSeq sequence for threads. It offers a newThread(Runnable) method that returns an instance of ServiceThread extends Thread after setting the thread name (including the executors id and an incremented sequence number from the threadSeq). ServiceThread is a basic Thread extension that can keep a reference to a ServiceContainerImpl. The ContainerExecutor also uses HANDLER and POLICY defined in the parent class. LatchListener is a TerminateListener that increases in the constructor and decreases when a termination is observed, essentially keeping a count of running containers.
ShutdownHookHolder hold a boolean down flag that says if the container is shutting down (possibly preventing new containers from being registered). It also contains a set of known containers. A cleanup thread is registered with Runtime.getRuntime().addShutdownHook that sets the down to true, iterates over all known containers, attaches a LatchListener (set to the size of known containers) to them and shuts them down. The terminating containers will fire the termination listener and then the thread can just sit and wait until the latch is finshed (all known containers signalled termination).
So we are in the constructor of the ServiceContainerImpl (from BootstrapImpl). Well, first the static stuff is called, causing a greeting to be printed out, then field initialization occurs. But then we get ourselves a serialNo (1) and set the name (jboss-as). The ContainerExecutor is initialized. The objectName is intialized (jboss.msc:type=container,name=jboss-as), the mBeanServer is fetched from the ManagementFactory.getPlatformMBeanServer and the containerMXBean is registered with the server. The mBeanServer and objectName are stored to the instance variables. We register the container with the static Set in the ShutdownHookHolder, adding the container and a Reaper that will remove the registration. Finally, if the container was registered as a MBean, we add a termination listener that will unregister the container from the MBean server.
With the service container initialized, booting can begin.