JBoss AS 7 uses the Stax API to parse the xml files. This is initialized in SubsystemExtension by mapping our parser onto our namespace:
We then need to write the parser. The contract is that we read our subsystem's xml and create the operations that will populate the model with the state contained in the xml. These operations will then be executed on our behalf as part of the parsing process. The entry point is the readElement() method.
So in the above we always create the add operation for our subsystem. Due to its address /subsystem=tracker defined by SUBSYSTEM_PATH this will trigger the SubsystemAddHandler we created earlier when we invoke /subsystem=tracker:add. We then parse the child elements and create an add operation for the child address for each type child. Since the address will for example be /subsystem=tracker/type=sar (defined by TYPE_PATH ) and TypeAddHandler is registered for all type subaddresses the TypeAddHandler will get invoked for those operations. Note that when we are parsing attribute tick we are using definition of attribute that we defined in TypeDefintion to parse attribute value and apply all rules that we specified for this attribute, this also enables us to property support expressions on attributes.
The parser is also used to marshal the model to xml whenever something modifies the model, for which the entry point is the writeContent() method:
Then we have to implement the SubsystemDescribeHandler which translates the current state of the model into operations similar to the ones created by the parser. The SubsystemDescribeHandler is only used when running in a managed domain, and is used when the host controller queries the domain controller for the configuration of the profile used to start up each server. In our case the SubsystemDescribeHandler adds the operation to add the subsystem and then adds the operation to add each type child. Since we are using ResourceDefinitinon for defining subsystem all that is generated for us, but if you want to customize that you can do it by implementing it like this.
|Changes to tests between 7.0.0 and 7.0.1|
The testing framework was moved from the archetype into the core JBoss AS 7 sources between JBoss AS 7.0.0 and JBoss AS 7.0.1, and has been improved upon and is used internally for testing JBoss AS 7's subsystems. The differences between the two versions is that in 7.0.0.Final the testing framework is bundled with the code generated by the archetype (in a sub-package of the package specified for your subsystem, e.g. com.acme.corp.tracker.support), and the test extends the AbstractParsingTest class.
From 7.0.1 the testing framework is now brought in via the org.jboss.as:jboss-as-subsystem-test maven artifact, and the test's superclass is org.jboss.as.subsystem.test.AbstractSubsystemTest. The concepts are the same but more and more functionality will be available as JBoss AS 7 is developed.
Now that we have modified our parsers we need to update our tests to reflect the new model. There are currently three tests testing the basic functionality, something which is a lot easier to debug from your IDE before you plug it into the application server. We will talk about these tests in turn and they all live in com.acme.corp.tracker.extension.SubsystemParsingTestCase. SubsystemParsingTestCase extends AbstractSubsystemTest which does a lot of the setup for you and contains utility methods for verifying things from your test. See the javadoc of that class for more information about the functionality available to you. And by all means feel free to add more tests for your subsystem, here we are only testing for the best case scenario while you will probably want to throw in a few tests for edge cases.
The first test we need to modify is testParseSubsystem(). It tests that the parsed xml becomes the expected operations that will be parsed into the server, so let us tweak this test to match our subsystem. First we tell the test to parse the xml into operations
There should be one operation for adding the subsystem itself and an operation for adding the deployment-type, so check we got two operations
Now check that the first operation is add for the address /subsystem=tracker:
Then check that the second operation is add for the address /subsystem=tracker, and that 12345 was picked up for the value of the tick parameter:
The second test we need to modify is testInstallIntoController() which tests that the xml installs properly into the controller. In other words we are making sure that the add operations we created earlier work properly. First we create the xml and install it into the controller. Behind the scenes this will parse the xml into operations as we saw in the last test, but it will also create a new controller and boot that up using the created operations
The returned KernelServices allow us to execute operations on the controller, and to read the whole model.
Now we make sure that the structure of the model within the controller has the expected format and values
The last test provided is called testParseAndMarshalModel(). It's main purpose is to make sure that our SubsystemParser.writeContent() works as expected. This is achieved by starting a controller in the same way as before
Now we read the model and the xml that was persisted from the first controller, and use that xml to start a second controller
Finally we read the model from the second controller, and make sure that the models are identical by calling compare() on the test superclass.
We then have a test that needs no changing from what the archetype provides us with. As we have seen before we start a controller
We then call /subsystem=tracker:describe which outputs the subsystem as operations needed to reach the current state (Done by our SubsystemDescribeHandler)
Then we create a new controller using those operations
And then we read the model from the second controller and make sure that the two subsystems are identical
ModelNode modelB = servicesB.readWholeModel();
To test the removal of the the subsystem and child resources we modify the testSubsystemRemoval() test provided by the archetype:
We provide xml for the subsystem installing a child, which in turn installs a TrackerService
Having installed the xml into the controller we make sure the TrackerService is there
This call from the subsystem test harness will call remove for each level in our subsystem, children first and validate
that the subsystem model is empty at the end.
Finally we check that all the services were removed by the remove handlers
For good measure let us throw in another test which adds a deployment-type and also changes its attribute at runtime. So first of all boot up the controller with the same xml we have been using so far
Now create an operation which does the same as the following CLI command /subsystem=tracker/type=foo:add(tick=1000)
Execute the operation and make sure it was successful
Read the whole model and make sure that the original data is still there (i.e. the same as what was done by testInstallIntoController()
Then make sure our new type has been added:
Then we call write-attribute to change the tick value of /subsystem=tracker/type=foo:
To give you exposure to other ways of doing things, now instead of reading the whole model to check the attribute, we call read-attribute instead, and make sure it has the value we set it to.
Since each type installs its own copy of TrackerService, we get the TrackerService for type=foo from the service container exposed by the kernel services and make sure it has the right value