[[Example_subsystem]]
= Example subsystem

Our example subsystem will keep track of all deployments of certain
types containing a special marker file, and expose operations to see how
long these deployments have been deployed.

[[create-the-skeleton-project]]
== Create the skeleton project

To make your life easier we have provided a maven archetype which will
create a skeleton project for implementing subsystems.

[source,options="nowrap"]
----
mvn archetype:generate \
    -DarchetypeArtifactId=wildfly-subsystem \
    -DarchetypeGroupId=org.wildfly.archetypes \
    -DarchetypeVersion=8.0.0.Final \
    -DarchetypeRepository=https://repository.jboss.org/nexus/content/groups/public
----

Maven will download the archetype and it's dependencies, and ask you
some questions:

[source,options="nowrap"]
----
$ mvn archetype:generate \
    -DarchetypeArtifactId=wildfly-subsystem \
    -DarchetypeGroupId=org.wildfly.archetypes \
    -DarchetypeVersion=8.0.0.Final \
    -DarchetypeRepository=https://repository.jboss.org/nexus/content/groups/public
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Stub Project (No POM) 1
[INFO] ------------------------------------------------------------------------
[INFO]
 
.........
 
Define value for property 'groupId': : com.acme.corp
Define value for property 'artifactId': : acme-subsystem
Define value for property 'version':  1.0-SNAPSHOT: :
Define value for property 'package':  com.acme.corp: : com.acme.corp.tracker
Define value for property 'module': : com.acme.corp.tracker
[INFO] Using property: name = WildFly subsystem project
Confirm properties configuration:
groupId: com.acme.corp
artifactId: acme-subsystem
version: 1.0-SNAPSHOT
package: com.acme.corp.tracker
module: com.acme.corp.tracker
name: WildFly subsystem project
 Y: : Y
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1:42.563s
[INFO] Finished at: Fri Jul 08 14:30:09 BST 2011
[INFO] Final Memory: 7M/81M
[INFO] ------------------------------------------------------------------------
$
----

[cols=",",options="header"]
|=======================================================================
| |Instruction

|1 |Enter the groupId you wish to use

|2 |Enter the artifactId you wish to use

|3 |Enter the version you wish to use, or just hit Enter if you wish to
accept the default 1.0-SNAPSHOT

|4 |Enter the java package you wish to use, or just hit Enter if you
wish to accept the default (which is copied from groupId ).

|5 |Enter the module name you wish to use for your extension.

|6 |Finally, if you are happy with your choices, hit Enter and Maven
will generate the project for you.
|=======================================================================

We now have a skeleton project that you can use to
implement a subsystem. Import the ﻿ `acme-subsystem` project into your
favourite IDE. A nice side-effect of running this in the IDE is that you
can see the javadoc of WildFly classes and interfaces imported by the
skeleton code. If you do a `mvn install` in the project it will work if
we plug it into WildFly, but before doing that we will change it to do
something more useful.

The rest of this section modifies the skeleton project created by the
archetype to do something more useful, and the full code can be found in
link:downloads/acme-subsystem.zip[acme-subsystem.zip].

If you do a `mvn install` in the created project, you will see some
tests being run

[source,options="nowrap"]
----
$mvn install
[INFO] Scanning for projects...
[...]
[INFO] Surefire report directory: /Users/kabir/sourcecontrol/temp/archetype-test/acme-subsystem/target/surefire-reports
 
-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running com.acme.corp.tracker.extension.SubsystemBaseParsingTestCase
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.424 sec
Running com.acme.corp.tracker.extension.SubsystemParsingTestCase
Tests run: 6, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.074 sec
 
Results :
 
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0
[...]
----

We will talk about these later in the
<<testing-the-parsers,#Testing the
parsers>> section.

[[create-the-schema]]
== Create the schema

First, let us define the schema for our subsystem. Rename
`src/main/resources/schema/mysubsystem.xsd` to
`src/main/resources/schema/acme.xsd`. Then open `acme.xsd` and modify it
to the following

[source,xml,options="nowrap"]
----
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
            targetNamespace="urn:com.acme.corp.tracker:1.0"
            xmlns="urn:com.acme.corp.tracker:1.0"
            elementFormDefault="qualified"
            attributeFormDefault="unqualified"
            version="1.0">
 
   <!-- The subsystem root element -->
   <xs:element name="subsystem" type="subsystemType"/>
   <xs:complexType name="subsystemType">
      <xs:all>
         <xs:element name="deployment-types" type="deployment-typesType"/>
      </xs:all>
   </xs:complexType>
   <xs:complexType name="deployment-typesType">
      <xs:choice minOccurs="0" maxOccurs="unbounded">
         <xs:element name="deployment-type" type="deployment-typeType"/>
      </xs:choice>
   </xs:complexType>
   <xs:complexType name="deployment-typeType">
      <xs:attribute name="suffix" use="required"/>
      <xs:attribute name="tick" type="xs:long" use="optional" default="10000"/>
   </xs:complexType>
</xs:schema>
----

Note that we modified the `xmlns` and `targetNamespace` values to ﻿
`urn.com.acme.corp.tracker:1.0`. Our new `subsystem` element has a child
called `deployment-types`, which in turn can have zero or more children
called `deployment-type`. Each `deployment-type` has a required `suffix`
attribute, and a `tick` attribute which defaults to `true.`

Now modify the ﻿ `com.acme.corp.tracker.extension.SubsystemExtension`
class to contain the new namespace.

[source,java,options="nowrap"]
----
public class SubsystemExtension implements Extension {
 
    /** The name space used for the {@code substystem} element */
    public static final String NAMESPACE = "urn:com.acme.corp.tracker:1.0";
    ...
----

[[design-and-define-the-model-structure]]
== Design and define the model structure

The following example xml contains a valid subsystem configuration, we
will see how to plug this in to WildFly later in this tutorial.

[source,xml,options="nowrap"]
----
<subsystem xmlns="urn:com.acme.corp.tracker:1.0">
   <deployment-types>
      <deployment-type suffix="sar" tick="10000"/>
      <deployment-type suffix="war" tick="10000"/>
   </deployment-types>
</subsystem>
----

Now when designing our model, we can either do a one to one mapping
between the schema and the model or come up with something slightly or
very different. To keep things simple, let us stay pretty true to the
schema so that when executing a `:read-resource(recursive=true)` against
our subsystem we'll see something like:

[source,options="nowrap"]
----
{
    "outcome" => "success",
    "result" => {"type" => {
        "sar" => {"tick" => "10000"},
        "war" => {"tick" => "10000"}
    }}
}
----

Each `deployment-type` in the xml becomes in the model a child resource
of the subsystem's root resource. The child resource's child-type is
`type`, and it is indexed by its `suffix`. Each `type` resource then
contains the `tick` attribute.

We also need a name for our subsystem, to do that change
`com.acme.corp.tracker.extension.SubsystemExtension`:

[source,java,options="nowrap"]
----
public class SubsystemExtension implements Extension {
    ...
    /** The name of our subsystem within the model. */
    public static final String SUBSYSTEM_NAME = "tracker";
    ...
----

Once we are finished our subsystem will be available under
`/subsystem=tracker`.

The `SubsystemExtension.initialize()` method defines the model,
currently it sets up the basics to add our subsystem to the model:

[source,java,options="nowrap"]
----
@Override
    public void initialize(ExtensionContext context) {
        //register subsystem with its model version
        final SubsystemRegistration subsystem = context.registerSubsystem(SUBSYSTEM_NAME, 1, 0);
        //register subsystem model with subsystem definition that defines all attributes and operations
        final ManagementResourceRegistration registration = subsystem.registerSubsystemModel(SubsystemDefinition.INSTANCE);
        //register describe operation, note that this can be also registered in SubsystemDefinition
        registration.registerOperationHandler(DESCRIBE, GenericSubsystemDescribeHandler.INSTANCE, GenericSubsystemDescribeHandler.INSTANCE, false, OperationEntry.EntryType.PRIVATE);
        //we can register additional submodels here
        //
        subsystem.registerXMLElementWriter(parser);
    }
----

The `registerSubsystem()` call registers our subsystem with the
extension context. At the end of the method we register our parser with
the returned `SubsystemRegistration` to be able to marshal our
subsystem's model back to the main configuration file when it is
modified. We will add more functionality to this method later.

[[registering-the-core-subsystem-model]]
=== Registering the core subsystem model

Next we obtain a `ManagementResourceRegistration` by registering the
subsystem model. This is a *compulsory* step for every new subsystem.

[source,java,options="nowrap"]
----
final ManagementResourceRegistration registration = subsystem.registerSubsystemModel(SubsystemDefinition.INSTANCE);
----

Its parameter is an implementation of the `ResourceDefinition`
interface, which means that when you call
`/subsystem=tracker:read-resource-description` the information you see
comes from model that is defined by `SubsystemDefinition.INSTANCE`.

[source,java,options="nowrap"]
----
public class SubsystemDefinition extends SimpleResourceDefinition {
    public static final SubsystemDefinition INSTANCE = new SubsystemDefinition();
 
    private SubsystemDefinition() {
        super(SubsystemExtension.SUBSYSTEM_PATH,
                SubsystemExtension.getResourceDescriptionResolver(null),
                //We always need to add an 'add' operation
                SubsystemAdd.INSTANCE,
                //Every resource that is added, normally needs a remove operation
                SubsystemRemove.INSTANCE);
    }
 
    @Override
    public void registerOperations(ManagementResourceRegistration resourceRegistration) {
        super.registerOperations(resourceRegistration);
        //you can register aditional operations here
    }
 
    @Override
    public void registerAttributes(ManagementResourceRegistration resourceRegistration) {
        //you can register attributes here
    }
}
----

Since we need child resource `type` we need to add new
ResourceDefinition,

The `ManagementResourceRegistration` obtained in
`SubsystemExtension.initialize()` is then used to add additional
operations or to register submodels to the `/subsystem=tracker` address.
Every subsystem and resource *must* have an `ADD` method which can be
achieved by the following line inside `registerOperations` in your
`ResourceDefinition` or by providing it in constructor of your
`SimpleResourceDefinition` just as we did in example above.

[source,java,options="nowrap"]
----
//We always need to add an 'add' operation
        resourceRegistration.registerOperationHandler(ADD, SubsystemAdd.INSTANCE, new DefaultResourceAddDescriptionProvider(resourceRegistration,descriptionResolver), false);
----

The parameters when registering an operation handler are:

1.  *The name* - i.e. `ADD`.
2.  The handler instance - we will talk more about this below
3.  The handler description provider - we will talk more about this
below.
4.  Whether this operation handler is inherited - `false` means that
this operation is not inherited, and will only apply to
`/subsystem=tracker`. The content for this operation handler will be
provided by `3`.

Let us first look at the description provider which is quite simple
since this operation takes no parameters. The addition of `type`
children will be handled by another operation handler, as we will see
later on.

There are two way to define `DescriptionProvider`, one is by defining it
by hand using ModelNode, but as this has show to be very error prone
there are lots of helper methods to help you automatically describe the
model. Following example is done by manually defining Description
provider for ADD operation handler

[source,java,options="nowrap"]
----
/**
     * Used to create the description of the subsystem add method
     */
    public static DescriptionProvider SUBSYSTEM_ADD = new DescriptionProvider() {
        public ModelNode getModelDescription(Locale locale) {
            //The locale is passed in so you can internationalize the strings used in the descriptions
 
            final ModelNode subsystem = new ModelNode();
            subsystem.get(OPERATION_NAME).set(ADD);
            subsystem.get(DESCRIPTION).set("Adds the tracker subsystem");
 
            return subsystem;
        }
    };
----

Or you can use API that helps you do that for you. For Add and Remove
methods there are classes `DefaultResourceAddDescriptionProvider` and
`DefaultResourceRemoveDescriptionProvider` that do work for you. In case
you use `SimpleResourceDefinition` even that part is hidden from you.

[source,java,options="nowrap"]
----
resourceRegistration.registerOperationHandler(ADD, SubsystemAdd.INSTANCE, new DefaultResourceAddDescriptionProvider(resourceRegistration,descriptionResolver), false);
resourceRegistration.registerOperationHandler(REMOVE, SubsystemRemove.INSTANCE, new DefaultResourceRemoveDescriptionProvider(resourceRegistration,descriptionResolver), false);
----

For other operation handlers that are not add/remove you can use
`DefaultOperationDescriptionProvider` that takes additional parameter of
what is the name of operation and optional array of
parameters/attributes operation takes. This is an example to register
operation " `add-mime`" with two parameters:

[source,java,options="nowrap"]
----
container.registerOperationHandler("add-mime",
                MimeMappingAdd.INSTANCE,
                new DefaultOperationDescriptionProvider("add-mime", Extension.getResourceDescriptionResolver("container.mime-mapping"), MIME_NAME, MIME_VALUE));
----

[NOTE]

When descriping an operation its description provider's `OPERATION_NAME`
must match the name used when calling
`ManagementResourceRegistration.registerOperationHandler()`

Next we have the actual operation handler instance, note that we have
changed its `populateModel()` method to initialize the `type` child of
the model.

[source,java,options="nowrap"]
----
class SubsystemAdd extends AbstractBoottimeAddStepHandler {
 
    static final SubsystemAdd INSTANCE = new SubsystemAdd();
 
    private SubsystemAdd() {
    }
 
    /** {@inheritDoc} */
    @Override
    protected void populateModel(ModelNode operation, ModelNode model) throws OperationFailedException {
        log.info("Populating the model");
        //Initialize the 'type' child node
        model.get("type").setEmptyObject();
    }
    ....
----

`SubsystemAdd` also has a `performBoottime()` method which is used for
initializing the deployer chain associated with this subsystem. We will
talk about the deployers later on. However, the basic idea for all
operation handlers is that we do any model updates before changing the
actual runtime state.

The rule of thumb is that every thing that can be added, can also be
removed so we have a remove handler for the subsystem registered +
in `SubsystemDefinition.registerOperations` or just provide the
operation handler in constructor.

[source,java,options="nowrap"]
----
//Every resource that is added, normally needs a remove operation
        registration.registerOperationHandler(REMOVE, SubsystemRemove.INSTANCE, DefaultResourceRemoveDescriptionProvider(resourceRegistration,descriptionResolver) , false);
----

`SubsystemRemove` extends `AbstractRemoveStepHandler` which takes care
of removing the resource from the model so we don't need to override its
`performRemove()` operation, also the add handler did not install any
services (services will be discussed later) so we can delete the
`performRuntime()` method generated by the archetype.

[source,java,options="nowrap"]
----
class SubsystemRemove extends AbstractRemoveStepHandler {
 
    static final SubsystemRemove INSTANCE = new SubsystemRemove();
 
    private final Logger log = Logger.getLogger(SubsystemRemove.class);
 
    private SubsystemRemove() {
    }
}
----

The description provider for the remove operation is simple and quite
similar to that of the add handler where just name of the method
changes.

[[registering-the-subsystem-child]]
=== Registering the subsystem child

The `type` child does not exist in our skeleton project so we need to
implement the operations to add and remove them from the model.

First we need an add operation to add the `type` child, create a class
called `com.acme.corp.tracker.extension.TypeAddHandler`. In this case we
extend the `org.jboss.as.controller.AbstractAddStepHandler` class and
implement the `org.jboss.as.controller.descriptions.DescriptionProvider`
interface. `org.jboss.as.controller.OperationStepHandler` is the main
interface for the operation handlers, and `AbstractAddStepHandler` is an
implementation of that which does the plumbing work for adding a
resource to the model.

[source,java,options="nowrap"]
----
class TypeAddHandler extends AbstractAddStepHandler implements DescriptionProvider {
 
    public static final TypeAddHandler INSTANCE = new TypeAddHandler();
 
    private TypeAddHandler() {
    }
----

Then we define subsystem model. Lets call it `TypeDefinition` and for
ease of use let it extend `SimpleResourceDefinition` instead just
implement `ResourceDefinition`.

[source,java,options="nowrap"]
----
public class TypeDefinition extends SimpleResourceDefinition {
 
 public static final TypeDefinition INSTANCE = new TypeDefinition();
 
 //we define attribute named tick
protected static final SimpleAttributeDefinition TICK =
new SimpleAttributeDefinitionBuilder(TrackerExtension.TICK, ModelType.LONG)
  .setAllowExpression(true)
  .setXmlName(TrackerExtension.TICK)
  .setFlags(AttributeAccess.Flag.RESTART_ALL_SERVICES)
  .setDefaultValue(new ModelNode(1000))
  .setAllowNull(false)
  .build();
 
private TypeDefinition(){
   super(TYPE_PATH, TrackerExtension.getResourceDescriptionResolver(TYPE),TypeAdd.INSTANCE,TypeRemove.INSTANCE);
}
 
@Override
public void registerAttributes(ManagementResourceRegistration resourceRegistration){
   resourceRegistration.registerReadWriteAttribute(TICK, null, TrackerTickHandler.INSTANCE);
}
 
}
----

Which will take care of describing the model for us. As you can see in
example above we define `SimpleAttributeDefinition` named `TICK`, this
is a mechanism to define Attributes in more type safe way and to add
more common API to manipulate attributes. As you can see here we define
default value of 1000 as also other constraints and capabilities. There
could be other properties set such as validators, alternate names, xml
name, flags for marking it attribute allows expressions and more.

Then we do the work of updating the model by implementing the
`populateModel()` method from the `AbstractAddStepHandler`, which
populates the model's attribute from the operation parameters. First we
get hold of the model relative to the address of this operation (we will
see later that we will register it against `/subsystem=tracker/type=*`),
so we just specify an empty relative address, and we then populate our
model with the parameters from the operation. There is operation
`validateAndSet` on `AttributeDefinition` that helps us validate and set
the model based on definition of the attribute.

[source,java,options="nowrap"]
----
@Override
    protected void populateModel(ModelNode operation, ModelNode model) throws OperationFailedException {
         TICK.validateAndSet(operation,model);
    }
----

We then override the `performRuntime()` method to perform our runtime
changes, which in this case involves installing a service into the
controller at the heart of WildFly. (
`AbstractAddStepHandler.performRuntime()` is similar to
`AbstractBoottimeAddStepHandler.performBoottime()` in that the model is
updated before runtime changes are made.

[source,java,options="nowrap"]
----
@Override
    protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model,
            ServiceVerificationHandler verificationHandler, List<ServiceController<?>> newControllers)
            throws OperationFailedException {
        String suffix = PathAddress.pathAddress(operation.get(ModelDescriptionConstants.ADDRESS)).getLastElement().getValue();
        long tick = TICK.resolveModelAttribute(context,model).asLong();
        TrackerService service = new TrackerService(suffix, tick);
        ServiceName name = TrackerService.createServiceName(suffix);
        ServiceController<TrackerService> controller = context.getServiceTarget()
                .addService(name, service)
                .addListener(verificationHandler)
                .setInitialMode(Mode.ACTIVE)
                .install();
        newControllers.add(controller);
    }
}
----

Since the add methods will be of the format
`/subsystem=tracker/suffix=war:add(tick=1234)`, we look for the last
element of the operation address, which is `war` in the example just
given and use that as our suffix. We then create an instance of
TrackerService and install that into the `service target` of the context
and add the created `service controller` to the `newControllers` list.

The tracker service is quite simple. All services installed into WildFly
must implement the `org.jboss.msc.service.Service` interface.

[source,java,options="nowrap"]
----
public class TrackerService implements Service<TrackerService>{
----

We then have some fields to keep the tick count and a thread which when
run outputs all the deployments registered with our service.

[source,java,options="nowrap"]
----
private AtomicLong tick = new AtomicLong(10000);
 
    private Set<String> deployments = Collections.synchronizedSet(new HashSet<String>());
    private Set<String> coolDeployments = Collections.synchronizedSet(new HashSet<String>());
    private final String suffix;
 
    private Thread OUTPUT = new Thread() {
        @Override
        public void run() {
            while (true) {
                try {
                    Thread.sleep(tick.get());
                    System.out.println("Current deployments deployed while " + suffix + " tracking active:\n" + deployments
                       + "\nCool: " + coolDeployments.size());
                } catch (InterruptedException e) {
                    interrupted();
                    break;
                }
            }
        }
    };
 
    public TrackerService(String suffix, long tick) {
        this.suffix = suffix;
        this.tick.set(tick);
    }
----

Next we have three methods which come from the `Service` interface.
`getValue()` returns this service, `start()` is called when the service
is started by the controller, `stop` is called when the service is
stopped by the controller, and they start and stop the thread outputting
the deployments.

[source,java,options="nowrap"]
----
@Override
    public TrackerService getValue() throws IllegalStateException, IllegalArgumentException {
        return this;
    }
 
    @Override
    public void start(StartContext context) throws StartException {
        OUTPUT.start();
    }
 
    @Override
    public void stop(StopContext context) {
        OUTPUT.interrupt();
    }
----

Next we have a utility method to create the `ServiceName` which is used
to register the service in the controller.

[source,java,options="nowrap"]
----
public static ServiceName createServiceName(String suffix) {
        return ServiceName.JBOSS.append("tracker", suffix);
}
----

Finally we have some methods to add and remove deployments, and to set
and read the `tick`. The 'cool' deployments will be explained later.

[source,java,options="nowrap"]
----
public void addDeployment(String name) {
        deployments.add(name);
    }
 
    public void addCoolDeployment(String name) {
        coolDeployments.add(name);
    }
 
    public void removeDeployment(String name) {
        deployments.remove(name);
        coolDeployments.remove(name);
    }
 
    void setTick(long tick) {
        this.tick.set(tick);
    }
 
    public long getTick() {
        return this.tick.get();
    }
}//TrackerService - end
----

Since we are able to add `type` children, we need a way to be able to
remove them, so we create a
`com.acme.corp.tracker.extension.TypeRemoveHandler`. In this case we
extend `AbstractRemoveStepHandler` which takes care of removing the
resource from the model so we don't need to override its
`performRemove()` operationa. But we need to implement the
`DescriptionProvider` method to provide the model description, and since
the add handler installs the TrackerService, we need to remove that in
the `performRuntime()` method.

[source,java,options="nowrap"]
----
public class TypeRemoveHandler extends AbstractRemoveStepHandler {
 
    public static final TypeRemoveHandler INSTANCE = new TypeRemoveHandler();
 
    private TypeRemoveHandler() {
    }
 
 
    @Override
    protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException {
        String suffix = PathAddress.pathAddress(operation.get(ModelDescriptionConstants.ADDRESS)).getLastElement().getValue();
        ServiceName name = TrackerService.createServiceName(suffix);
        context.removeService(name);
    }
 
}
----

We then need a description provider for the `type` part of the model
itself, so we modify TypeDefinitnion to registerAttribute

[source,java,options="nowrap"]
----
class TypeDefinition{
...
@Override
public void registerAttributes(ManagementResourceRegistration resourceRegistration){
    resourceRegistration.registerReadWriteAttribute(TICK, null, TrackerTickHandler.INSTANCE);
}
 
}
----

Then finally we need to specify that our new `type` child and associated
handlers go under `/subsystem=tracker/type=*` in the model by adding
registering it with the model in `SubsystemExtension.initialize()`. So
we add the following just before the end of the method.

[source,java,options="nowrap"]
----
@Override
public void initialize(ExtensionContext context)
{
 final SubsystemRegistration subsystem = context.registerSubsystem(SUBSYSTEM_NAME, 1, 0);
 final ManagementResourceRegistration registration = subsystem.registerSubsystemModel(TrackerSubsystemDefinition.INSTANCE);
 //Add the type child
 ManagementResourceRegistration typeChild = registration.registerSubModel(TypeDefinition.INSTANCE);
 subsystem.registerXMLElementWriter(parser);
}
----

The above first creates a child of our main subsystem registration for
the relative address `type=*`, and gets the `typeChild` registration. +
To this we add the `TypeAddHandler` and `TypeRemoveHandler`. +
The add variety is added under the name `add` and the remove handler
under the name `remove`, and for each registered operation handler we
use the handler singleton instance as both the handler parameter and as
the `DescriptionProvider`.

Finally, we register `tick` as a read/write attribute, the null
parameter means we don't do anything special with regards to reading it,
for the write handler we supply it with an operation handler called
`TrackerTickHandler`. +
Registering it as a read/write attribute means we can use the
`:write-attribute` operation to modify the value of the parameter, and
it will be handled by `TrackerTickHandler`.

Not registering a write attribute handler makes the attribute read only.

`TrackerTickHandler` extends `AbstractWriteAttributeHandler` +
directly, and so must implement its `applyUpdateToRuntime` and
`revertUpdateToRuntime` method. +
This takes care of model manipulation (validation, setting) but leaves
us to do just to deal with what we need to do.

[source,java,options="nowrap"]
----
class TrackerTickHandler extends AbstractWriteAttributeHandler<Void> {
 
    public static final TrackerTickHandler INSTANCE = new TrackerTickHandler();
 
    private TrackerTickHandler() {
        super(TypeDefinition.TICK);
    }
 
    protected boolean applyUpdateToRuntime(OperationContext context, ModelNode operation, String attributeName,
              ModelNode resolvedValue, ModelNode currentValue, HandbackHolder<Void> handbackHolder) throws OperationFailedException {
 
        modifyTick(context, operation, resolvedValue.asLong());
 
        return false;
    }
 
    protected void revertUpdateToRuntime(OperationContext context, ModelNode operation, String attributeName, ModelNode valueToRestore, ModelNode valueToRevert, Void handback){
        modifyTick(context, operation, valueToRestore.asLong());
    }
 
    private void modifyTick(OperationContext context, ModelNode operation, long value) throws OperationFailedException {
 
        final String suffix = PathAddress.pathAddress(operation.get(ModelDescriptionConstants.ADDRESS)).getLastElement().getValue();
        TrackerService service = (TrackerService) context.getServiceRegistry(true).getRequiredService(TrackerService.createServiceName(suffix)).getValue();
        service.setTick(value);
    }
 
}
----

The operation used to execute this will be of the form
`/subsystem=tracker/type=war:write-attribute(name=tick,value=12345`) so
we first get the `suffix` from the operation address, and the `tick`
value from the operation parameter's `resolvedValue` parameter, and use
that to update the model.

We then add a new step associated with the `RUNTIME` stage to update the
tick of the TrackerService for our suffix. This is essential since the
call to `context.getServiceRegistry()` will fail unless the step
accessing it belongs to the `RUNTIME` stage.

[NOTE]

When implementing `execute()`, you *must* call `context.completeStep()`
when you are done.

[[parsing-and-marshalling-of-the-subsystem-xml]]
== Parsing and marshalling of the subsystem xml

WildFly uses the Stax API to parse the xml files. This is initialized in
`SubsystemExtension` by mapping our parser onto our namespace:

[source,java,options="nowrap"]
----
public class SubsystemExtension implements Extension {
 
    /** The name space used for the {@code subsystem} element */
    public static final String NAMESPACE = "urn:com.acme.corp.tracker:1.0";
    ...
    protected static final PathElement SUBSYSTEM_PATH = PathElement.pathElement(SUBSYSTEM, SUBSYSTEM_NAME);
    protected static final PathElement TYPE_PATH = PathElement.pathElement(TYPE);
 
   /** The parser used for parsing our subsystem */
    private final SubsystemParser parser = new SubsystemParser();
 
   @Override
    public void initializeParsers(ExtensionParsingContext context) {
        context.setSubsystemXmlMapping(NAMESPACE, parser);
    }
    ...
----

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.

[source,java,options="nowrap"]
----
public class SubsystemExtension implements Extension {
 
    /**
     * The subsystem parser, which uses stax to read and write to and from xml
     */
    private static class SubsystemParser implements XMLStreamConstants, XMLElementReader<List<ModelNode>>, XMLElementWriter<SubsystemMarshallingContext> {
 
        /** {@inheritDoc} */
        @Override
        public void readElement(XMLExtendedStreamReader reader, List<ModelNode> list) throws XMLStreamException {
            // Require no attributes
            ParseUtils.requireNoAttributes(reader);
 
            //Add the main subsystem 'add' operation
            final ModelNode subsystem = new ModelNode();
            subsystem.get(OP).set(ADD);
            subsystem.get(OP_ADDR).set(PathAddress.pathAddress(SUBSYSTEM_PATH).toModelNode());
            list.add(subsystem);
 
            //Read the children
            while (reader.hasNext() && reader.nextTag() != END_ELEMENT) {
                if (!reader.getLocalName().equals("deployment-types")) {
                    throw ParseUtils.unexpectedElement(reader);
                }
                while (reader.hasNext() && reader.nextTag() != END_ELEMENT) {
                    if (reader.isStartElement()) {
                        readDeploymentType(reader, list);
                    }
                }
            }
        }
 
        private void readDeploymentType(XMLExtendedStreamReader reader, List<ModelNode> list) throws XMLStreamException {
            if (!reader.getLocalName().equals("deployment-type")) {
                throw ParseUtils.unexpectedElement(reader);
            }
            ModelNode addTypeOperation = new ModelNode();
            addTypeOperation.get(OP).set(ModelDescriptionConstants.ADD);
 
            String suffix = null;
            for (int i = 0; i < reader.getAttributeCount(); i++) {
                String attr = reader.getAttributeLocalName(i);
                String value = reader.getAttributeValue(i);
                if (attr.equals("tick")) {
                    TypeDefinition.TICK.parseAndSetParameter(value, addTypeOperation, reader);
                } else if (attr.equals("suffix")) {
                    suffix = value;
                } else {
                    throw ParseUtils.unexpectedAttribute(reader, i);
                }
            }
            ParseUtils.requireNoContent(reader);
            if (suffix == null) {
                throw ParseUtils.missingRequiredElement(reader, Collections.singleton("suffix"));
            }
 
            //Add the 'add' operation for each 'type' child
            PathAddress addr = PathAddress.pathAddress(SUBSYSTEM_PATH, PathElement.pathElement(TYPE, suffix));
            addTypeOperation.get(OP_ADDR).set(addr.toModelNode());
            list.add(addTypeOperation);
        }
        ...
----

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:

[source,java,options="nowrap"]
----
private static class SubsystemParser implements XMLStreamConstants, XMLElementReader<List<ModelNode>>, XMLElementWriter<SubsystemMarshallingContext> {
        ...
        /** {@inheritDoc} */
        @Override
        public void writeContent(final XMLExtendedStreamWriter writer, final SubsystemMarshallingContext context) throws XMLStreamException {
            //Write out the main subsystem element
            context.startSubsystemElement(TrackerExtension.NAMESPACE, false);
            writer.writeStartElement("deployment-types");
            ModelNode node = context.getModelNode();
            ModelNode type = node.get(TYPE);
            for (Property property : type.asPropertyList()) {
 
                //write each child element to xml
                writer.writeStartElement("deployment-type");
                writer.writeAttribute("suffix", property.getName());
                ModelNode entry = property.getValue();
                TypeDefinition.TICK.marshallAsAttribute(entry, true, writer);
                writer.writeEndElement();
            }
            //End deployment-types
            writer.writeEndElement();
            //End subsystem
            writer.writeEndElement();
        }
    }
----

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.

[source,java,options="nowrap"]
----
private static class SubsystemDescribeHandler implements OperationStepHandler, DescriptionProvider {
        static final SubsystemDescribeHandler INSTANCE = new SubsystemDescribeHandler();
 
        public void execute(OperationContext context, ModelNode operation) throws OperationFailedException {
            //Add the main operation
            context.getResult().add(createAddSubsystemOperation());
 
            //Add the operations to create each child
 
            ModelNode node = context.readModel(PathAddress.EMPTY_ADDRESS);
            for (Property property : node.get("type").asPropertyList()) {
 
                ModelNode addType = new ModelNode();
                addType.get(OP).set(ModelDescriptionConstants.ADD);
                PathAddress addr = PathAddress.pathAddress(SUBSYSTEM_PATH, PathElement.pathElement("type", property.getName()));
                addType.get(OP_ADDR).set(addr.toModelNode());
                if (property.getValue().hasDefined("tick")) {
                   TypeDefinition.TICK.validateAndSet(property,addType);
                }
                context.getResult().add(addType);
            }
            context.completeStep();
        }
 
 
}
----

[[testing-the-parsers]]
=== Testing the parsers

Changes to tests between 7.0.0 and 7.0.1

[NOTE]

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

[source,xml,options="nowrap"]
----
@Test
    public void testParseSubsystem() throws Exception {
        //Parse the subsystem xml into operations
        String subsystemXml =
                "<subsystem xmlns=\"" + SubsystemExtension.NAMESPACE + "\">" +
                "   <deployment-types>" +
                "       <deployment-type suffix=\"tst\" tick=\"12345\"/>" +
                "   </deployment-types>" +
                "</subsystem>";
        List<ModelNode> operations = super.parse(subsystemXml);
----

There should be one operation for adding the subsystem itself and an
operation for adding the `deployment-type`, so check we got two
operations

[source,java,options="nowrap"]
----
///Check that we have the expected number of operations
        Assert.assertEquals(2, operations.size());
----

Now check that the first operation is `add` for the address
`/subsystem=tracker`:

[source,java,options="nowrap"]
----
//Check that each operation has the correct content
        //The add subsystem operation will happen first
        ModelNode addSubsystem = operations.get(0);
        Assert.assertEquals(ADD, addSubsystem.get(OP).asString());
        PathAddress addr = PathAddress.pathAddress(addSubsystem.get(OP_ADDR));
        Assert.assertEquals(1, addr.size());
        PathElement element = addr.getElement(0);
        Assert.assertEquals(SUBSYSTEM, element.getKey());
        Assert.assertEquals(SubsystemExtension.SUBSYSTEM_NAME, element.getValue());
----

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:

[source,java,options="nowrap"]
----
//Then we will get the add type operation
        ModelNode addType = operations.get(1);
        Assert.assertEquals(ADD, addType.get(OP).asString());
        Assert.assertEquals(12345, addType.get("tick").asLong());
        addr = PathAddress.pathAddress(addType.get(OP_ADDR));
        Assert.assertEquals(2, addr.size());
        element = addr.getElement(0);
        Assert.assertEquals(SUBSYSTEM, element.getKey());
        Assert.assertEquals(SubsystemExtension.SUBSYSTEM_NAME, element.getValue());
        element = addr.getElement(1);
        Assert.assertEquals("type", element.getKey());
        Assert.assertEquals("tst", element.getValue());
    }
----

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

[source,java,options="nowrap"]
----
@Test
    public void testInstallIntoController() throws Exception {
        //Parse the subsystem xml and install into the controller
        String subsystemXml =
                "<subsystem xmlns=\"" + SubsystemExtension.NAMESPACE + "\">" +
                "   <deployment-types>" +
                "       <deployment-type suffix=\"tst\" tick=\"12345\"/>" +
                "   </deployment-types>" +
                "</subsystem>";
        KernelServices services = super.installInController(subsystemXml);
----

The returned `KernelServices` allow us to execute operations on the
controller, and to read the whole model.

[source,java,options="nowrap"]
----
//Read the whole model and make sure it looks as expected
        ModelNode model = services.readWholeModel();
        //Useful for debugging :-)
        //System.out.println(model);
----

Now we make sure that the structure of the model within the controller
has the expected format and values

[source,java,options="nowrap"]
----
Assert.assertTrue(model.get(SUBSYSTEM).hasDefined(SubsystemExtension.SUBSYSTEM_NAME));
        Assert.assertTrue(model.get(SUBSYSTEM, SubsystemExtension.SUBSYSTEM_NAME).hasDefined("type"));
        Assert.assertTrue(model.get(SUBSYSTEM, SubsystemExtension.SUBSYSTEM_NAME, "type").hasDefined("tst"));
        Assert.assertTrue(model.get(SUBSYSTEM, SubsystemExtension.SUBSYSTEM_NAME, "type", "tst").hasDefined("tick"));
        Assert.assertEquals(12345, model.get(SUBSYSTEM, SubsystemExtension.SUBSYSTEM_NAME, "type", "tst", "tick").asLong());
    }
----

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

[source,java,options="nowrap"]
----
@Test
    public void testParseAndMarshalModel() throws Exception {
        //Parse the subsystem xml and install into the first controller
        String subsystemXml =
                "<subsystem xmlns=\"" + SubsystemExtension.NAMESPACE + "\">" +
                "   <deployment-types>" +
                "       <deployment-type suffix=\"tst\" tick=\"12345\"/>" +
                "   </deployment-types>" +
                "</subsystem>";
        KernelServices servicesA = super.installInController(subsystemXml);
----

Now we read the model and the xml that was persisted from the first
controller, and use that xml to start a second controller

[source,java,options="nowrap"]
----
//Get the model and the persisted xml from the first controller
        ModelNode modelA = servicesA.readWholeModel();
        String marshalled = servicesA.getPersistedSubsystemXml();
 
        //Install the persisted xml from the first controller into a second controller
        KernelServices servicesB = super.installInController(marshalled);
----

Finally we read the model from the second controller, and make sure that
the models are identical by calling `compare()` on the test superclass.

[source,java,options="nowrap"]
----
ModelNode modelB = servicesB.readWholeModel();
 
        //Make sure the models from the two controllers are identical
        super.compare(modelA, modelB);
    }
----

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

[source,java,options="nowrap"]
----
@Test
    public void testDescribeHandler() throws Exception {
        //Parse the subsystem xml and install into the first controller
        String subsystemXml =
                "<subsystem xmlns=\"" + SubsystemExtension.NAMESPACE + "\">" +
                "</subsystem>";
        KernelServices servicesA = super.installInController(subsystemXml);
----

We then call `/subsystem=tracker:describe` which outputs the subsystem
as operations needed to reach the current state (Done by our
`SubsystemDescribeHandler`)

[source,java,options="nowrap"]
----
//Get the model and the describe operations from the first controller
        ModelNode modelA = servicesA.readWholeModel();
        ModelNode describeOp = new ModelNode();
        describeOp.get(OP).set(DESCRIBE);
        describeOp.get(OP_ADDR).set(
                PathAddress.pathAddress(
                        PathElement.pathElement(SUBSYSTEM, SubsystemExtension.SUBSYSTEM_NAME)).toModelNode());
        List<ModelNode> operations = super.checkResultAndGetContents(servicesA.executeOperation(describeOp)).asList();
----

Then we create a new controller using those operations

[source,java,options="nowrap"]
----
//Install the describe options from the first controller into a second controller
        KernelServices servicesB = super.installInController(operations);
----

And then we read the model from the second controller and make sure that
the two subsystems are identical +
ModelNode modelB = servicesB.readWholeModel();

[source,java,options="nowrap"]
----
//Make sure the models from the two controllers are identical
        super.compare(modelA, modelB);
 
    }
----

To test the removal of the the subsystem and child resources we modify
the `testSubsystemRemoval()` test provided by the archetype:

[source,java,options="nowrap"]
----
/**
     * Tests that the subsystem can be removed
     */
    @Test
    public void testSubsystemRemoval() throws Exception {
        //Parse the subsystem xml and install into the first controller
----

We provide xml for the subsystem installing a child, which in turn
installs a TrackerService

[source,java,options="nowrap"]
----
String subsystemXml =
                "<subsystem xmlns=\"" + SubsystemExtension.NAMESPACE + "\">" +
                "   <deployment-types>" +
                "       <deployment-type suffix=\"tst\" tick=\"12345\"/>" +
                "   </deployment-types>" +
                "</subsystem>";
        KernelServices services = super.installInController(subsystemXml);
----

Having installed the xml into the controller we make sure the
TrackerService is there

[source,java,options="nowrap"]
----
//Sanity check to test the service for 'tst' was there
        services.getContainer().getRequiredService(TrackerService.createServiceName("tst"));
----

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.

[source,java,options="nowrap"]
----
//Checks that the subsystem was removed from the model
        super.assertRemoveSubsystemResources(services);
----

Finally we check that all the services were removed by the remove
handlers

[source,java,options="nowrap"]
----
//Check that any services that were installed were removed here
        try {
            services.getContainer().getRequiredService(TrackerService.createServiceName("tst"));
            Assert.fail("Should have removed services");
        } catch (Exception expected) {
        }
    }
----

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

[source,java,options="nowrap"]
----
@Test
    public void testExecuteOperations() throws Exception {
        String subsystemXml =
                "<subsystem xmlns=\"" + SubsystemExtension.NAMESPACE + "\">" +
                "   <deployment-types>" +
                "       <deployment-type suffix=\"tst\" tick=\"12345\"/>" +
                "   </deployment-types>" +
                "</subsystem>";
        KernelServices services = super.installInController(subsystemXml);
----

Now create an operation which does the same as the following CLI command
`/subsystem=tracker/type=foo:add(tick=1000)`

[source,java,options="nowrap"]
----
//Add another type
        PathAddress fooTypeAddr = PathAddress.pathAddress(
                PathElement.pathElement(SUBSYSTEM, SubsystemExtension.SUBSYSTEM_NAME),
                PathElement.pathElement("type", "foo"));
        ModelNode addOp = new ModelNode();
        addOp.get(OP).set(ADD);
        addOp.get(OP_ADDR).set(fooTypeAddr.toModelNode());
        addOp.get("tick").set(1000);
----

Execute the operation and make sure it was successful

[source,java,options="nowrap"]
----
ModelNode result = services.executeOperation(addOp);
        Assert.assertEquals(SUCCESS, result.get(OUTCOME).asString());
----

Read the whole model and make sure that the original data is still there
(i.e. the same as what was done by `testInstallIntoController()`

[source,java,options="nowrap"]
----
ModelNode model = services.readWholeModel();
        Assert.assertTrue(model.get(SUBSYSTEM).hasDefined(SubsystemExtension.SUBSYSTEM_NAME));
        Assert.assertTrue(model.get(SUBSYSTEM, SubsystemExtension.SUBSYSTEM_NAME).hasDefined("type"));
        Assert.assertTrue(model.get(SUBSYSTEM, SubsystemExtension.SUBSYSTEM_NAME, "type").hasDefined("tst"));
        Assert.assertTrue(model.get(SUBSYSTEM, SubsystemExtension.SUBSYSTEM_NAME, "type", "tst").hasDefined("tick"));
        Assert.assertEquals(12345, model.get(SUBSYSTEM, SubsystemExtension.SUBSYSTEM_NAME, "type", "tst", "tick").asLong());
----

Then make sure our new `type` has been added:

[source,java,options="nowrap"]
----
Assert.assertTrue(model.get(SUBSYSTEM, SubsystemExtension.SUBSYSTEM_NAME, "type").hasDefined("foo"));
        Assert.assertTrue(model.get(SUBSYSTEM, SubsystemExtension.SUBSYSTEM_NAME, "type", "foo").hasDefined("tick"));
        Assert.assertEquals(1000, model.get(SUBSYSTEM, SubsystemExtension.SUBSYSTEM_NAME, "type", "foo", "tick").asLong());
----

Then we call `write-attribute` to change the `tick` value of
`/subsystem=tracker/type=foo`:

[source,java,options="nowrap"]
----
//Call write-attribute
        ModelNode writeOp = new ModelNode();
        writeOp.get(OP).set(WRITE_ATTRIBUTE_OPERATION);
        writeOp.get(OP_ADDR).set(fooTypeAddr.toModelNode());
        writeOp.get(NAME).set("tick");
        writeOp.get(VALUE).set(3456);
        result = services.executeOperation(writeOp);
        Assert.assertEquals(SUCCESS, result.get(OUTCOME).asString());
----

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.

[source,java,options="nowrap"]
----
//Check that write attribute took effect, this time by calling read-attribute instead of reading the whole model
        ModelNode readOp = new ModelNode();
        readOp.get(OP).set(READ_ATTRIBUTE_OPERATION);
        readOp.get(OP_ADDR).set(fooTypeAddr.toModelNode());
        readOp.get(NAME).set("tick");
        result = services.executeOperation(readOp);
        Assert.assertEquals(3456, checkResultAndGetContents(result).asLong());
----

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

[source,java,options="nowrap"]
----
TrackerService service = (TrackerService)services.getContainer().getService(TrackerService.createServiceName("foo")).getValue();
        Assert.assertEquals(3456, service.getTick());
    }
----

TypeDefinition.TICK.

[[add-the-deployers]]
== Add the deployers

When discussing `SubsystemAddHandler` we did not mention the work done
to install the deployers, which is done in the following method:

[source,java,options="nowrap"]
----
    @Override
    public void performBoottime(OperationContext context, ModelNode operation, ModelNode model,
            ServiceVerificationHandler verificationHandler, List<ServiceController<?>> newControllers)
            throws OperationFailedException {
 
        log.info("Populating the model");
 
        //Add deployment processors here
        //Remove this if you don't need to hook into the deployers, or you can add as many as you like
        //see SubDeploymentProcessor for explanation of the phases
        context.addStep(new AbstractDeploymentChainStep() {
            public void execute(DeploymentProcessorTarget processorTarget) {
                processorTarget.addDeploymentProcessor(SubsystemDeploymentProcessor.PHASE, SubsystemDeploymentProcessor.priority, new SubsystemDeploymentProcessor());
 
            }
        }, OperationContext.Stage.RUNTIME);
 
    }
----

This adds an extra step which is responsible for installing deployment
processors. You can add as many as you like, or avoid adding any all
together depending on your needs. Each processor has a `Phase` and a
`priority`. Phases are sequential, and a deployment passes through each
phases deployment processors. The `priority` specifies where within a
phase the processor appears. See `org.jboss.as.server.deployment.Phase`
for more information about phases.

In our case we are keeping it simple and staying with one deployment
processor with the phase and priority created for us by the maven
archetype. The phases will be explained in the next section. The
deployment processor is as follows:

[source,java,options="nowrap"]
----
public class SubsystemDeploymentProcessor implements DeploymentUnitProcessor {
    ...
 
    @Override
    public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
        String name = phaseContext.getDeploymentUnit().getName();
        TrackerService service = getTrackerService(phaseContext.getServiceRegistry(), name);
        if (service != null) {
            ResourceRoot root = phaseContext.getDeploymentUnit().getAttachment(Attachments.DEPLOYMENT_ROOT);
            VirtualFile cool = root.getRoot().getChild("META-INF/cool.txt");
            service.addDeployment(name);
            if (cool.exists()) {
                service.addCoolDeployment(name);
            }
        }
    }
 
    @Override
    public void undeploy(DeploymentUnit context) {
        context.getServiceRegistry();
        String name = context.getName();
        TrackerService service = getTrackerService(context.getServiceRegistry(), name);
        if (service != null) {
            service.removeDeployment(name);
        }
    }
 
    private TrackerService getTrackerService(ServiceRegistry registry, String name) {
        int last = name.lastIndexOf(".");
        String suffix = name.substring(last + 1);
        ServiceController<?> container = registry.getService(TrackerService.createServiceName(suffix));
        if (container != null) {
            TrackerService service = (TrackerService)container.getValue();
            return service;
        }
        return null;
    }
}
----

The `deploy()` method is called when a deployment is being deployed. In
this case we look for the `TrackerService` instance for the service name
created from the deployment's suffix. If there is one it means that we
are meant to be tracking deployments with this suffix (i.e.
`TypeAddHandler` was called for this suffix), and if we find one we add
the deployment's name to it. Similarly `undeploy()` is called when a
deployment is being undeployed, and if there is a `TrackerService`
instance for the deployment's suffix, we remove the deployment's name
from it.

[[deployment-phases-and-attachments]]
=== Deployment phases and attachments

The code in the SubsystemDeploymentProcessor uses an _attachment_, which
is the means of communication between the individual deployment
processors. A deployment processor belonging to a phase may create an
attachment which is then read further along the chain of deployment unit
processors. In the above example we look for the
`Attachments.DEPLOYMENT_ROOT` attachment, which is a view of the file
structure of the deployment unit put in place before the chain of
deployment unit processors is invoked.

As mentioned above, the deployment unit processors are organized in
phases, and have a relative order within each phase. A deployment unit
passes through all the deployment unit processors in that order. A
deployment unit processor may choose to take action or not depending on
what attachments are available. Let's take a quick look at what the
deployment unit processors for in the phases described in
`org.jboss.as.server.deployment.Phase`.

[[structure]]
==== STRUCTURE

The deployment unit processors in this phase determine the structure of
a deployment, and looks for sub deployments and metadata files.

[[parse]]
==== PARSE

In this phase the deployment unit processors parse the deployment
descriptors and build up the annotation index. `Class-Path` entries from
the META-INF/MANIFEST.MF are added.

[[dependencies]]
==== DEPENDENCIES

Extra class path dependencies are added. For example if deploying a
`war` file, the commonly needed dependencies for a web application are
added.

[[configure_module]]
==== CONFIGURE_MODULE

In this phase the modular class loader for the deployment is created. No
attempt should be made loading classes from the deployment until *after*
this phase.

[[post_module]]
==== POST_MODULE

Now that our class loader has been constructed we have access to the
classes. In this stage deployment processors may use the
`Attachments.REFLECTION_INDEX` attachment which is a deployment index
used to obtain members of classes in the deployment, and to invoke upon
them, bypassing the inefficiencies of using `java.lang.reflect`
directly.

[[install]]
==== INSTALL

Install new services coming from the deployment.

[[cleanup]]
==== CLEANUP

Attachments put in place earlier in the deployment unit processor chain
may be removed here.

[[integrate-with-wildfly]]
== Integrate with WildFly

Now that we have all the code needed for our subsystem, we can build our
project by running `mvn install`

[source,options="nowrap"]
----
[kabir ~/sourcecontrol/temp/archetype-test/acme-subsystem]
$mvn install
[INFO] Scanning for projects...
[...]
main:
   [delete] Deleting: /Users/kabir/sourcecontrol/temp/archetype-test/acme-subsystem/null1004283288
   [delete] Deleting directory /Users/kabir/sourcecontrol/temp/archetype-test/acme-subsystem/target/module
     [copy] Copying 1 file to /Users/kabir/sourcecontrol/temp/archetype-test/acme-subsystem/target/module/com/acme/corp/tracker/main
     [copy] Copying 1 file to /Users/kabir/sourcecontrol/temp/archetype-test/acme-subsystem/target/module/com/acme/corp/tracker/main
     [echo] Module com.acme.corp.tracker has been created in the target/module directory. Copy to your JBoss AS 7 installation.
[INFO] Executed tasks
[INFO]
[INFO] --- maven-install-plugin:2.3.1:install (default-install) @ acme-subsystem ---
[INFO] Installing /Users/kabir/sourcecontrol/temp/archetype-test/acme-subsystem/target/acme-subsystem.jar to /Users/kabir/.m2/repository/com/acme/corp/acme-subsystem/1.0-SNAPSHOT/acme-subsystem-1.0-SNAPSHOT.jar
[INFO] Installing /Users/kabir/sourcecontrol/temp/archetype-test/acme-subsystem/pom.xml to /Users/kabir/.m2/repository/com/acme/corp/acme-subsystem/1.0-SNAPSHOT/acme-subsystem-1.0-SNAPSHOT.pom
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 5.851s
[INFO] Finished at: Mon Jul 11 23:24:58 BST 2011
[INFO] Final Memory: 7M/81M
[INFO] ------------------------------------------------------------------------
----

This will have built our project and assembled a module for us that can
be used for installing it into WildFly. If you go to the `target/module`
folder where you built the project you will see the module

[source,options="nowrap"]
----
$ls target/module/com/acme/corp/tracker/main/
acme-subsystem.jar  module.xml
----

The `module.xml` comes from `src/main/resources/module/main/module.xml`
and is used to define your module. It says that it contains the
`acme-subsystem.jar`:

[source,xml,options="nowrap"]
----
<module xmlns="urn:jboss:module:1.0" name="com.acme.corp.tracker">
    <resources>
        <resource-root path="acme-subsystem.jar"/>
    </resources>
----

And has a default set of dependencies needed by every subsystem created.
If your subsystem requires additional module dependencies you can add
them here before building and installing.

[source,xml,options="nowrap"]
----
    <dependencies>
        <module name="javax.api"/>
        <module name="org.jboss.staxmapper"/>
        <module name="org.jboss.as.controller"/>
        <module name="org.jboss.as.server"/>
        <module name="org.jboss.modules"/>
        <module name="org.jboss.msc"/>
        <module name="org.jboss.logging"/>
        <module name="org.jboss.vfs"/>
    </dependencies>
</module>
----

Note that the name of the module corresponds to the directory structure
containing it. Now copy the `target/module/com/acme/corp/tracker/main/`
directory and its contents to
`$WFLY/modules/com/acme/corp/tracker/main/` (where `$WFLY` is the root
of your WildFly install).

Next we need to modify `$WFLY/standalone/configuration/standalone.xml`.
First we need to add our new module to the `<extensions>` section:

[source,java,options="nowrap"]
----
    <extensions>
        ...
        <extension module="org.jboss.as.weld"/>
        <extension module="com.acme.corp.tracker"/>
    </extensions>
----

And then we have to add our subsystem to the `<profile>` section:

[source,xml,options="nowrap"]
----
    <profile>
    ...
 
        <subsystem xmlns="urn:com.acme.corp.tracker:1.0">
            <deployment-types>
                <deployment-type suffix="sar" tick="10000"/>
                <deployment-type suffix="war" tick="10000"/>
            </deployment-types>
        </subsystem>
    ...
    </profile>
----

Adding this to a managed domain works exactly the same apart from in
this case you need to modify `$WFLY/domain/configuration/domain.xml`.

Now start up WildFly by running `$WFLY/bin/standalone.sh` and you should
see messages like these after the server has started, which means our
subsystem has been added and our `TrackerService` is working:

....
15:27:33,838 INFO  [org.jboss.as] (Controller Boot Thread) JBoss AS 7.0.0.Final "Lightning" started in 2861ms - Started 94 of 149 services (55 services are passive or on-demand)
15:27:42,966 INFO  [stdout] (Thread-8) Current deployments deployed while sar tracking active:
15:27:42,966 INFO  [stdout] (Thread-8) []
15:27:42,967 INFO  [stdout] (Thread-8) Cool: 0
15:27:42,967 INFO  [stdout] (Thread-9) Current deployments deployed while war tracking active:
15:27:42,967 INFO  [stdout] (Thread-9) []
15:27:42,967 INFO  [stdout] (Thread-9) Cool: 0
15:27:52,967 INFO  [stdout] (Thread-8) Current deployments deployed while sar tracking active:
15:27:52,967 INFO  [stdout] (Thread-8) []
15:27:52,967 INFO  [stdout] (Thread-8) Cool: 0
....

If you run the command line interface you can execute some commands to
see more about the subsystem. For example

[source,options="nowrap"]
----
[standalone@localhost:9999 /] /subsystem=tracker/:read-resource-description(recursive=true, operations=true)
----

will return a lot of information, including what we provided in the
`DescriptionProvider`s we created to document our subsystem.

To see the current subsystem state you can execute

[source,options="nowrap"]
----
[standalone@localhost:9999 /] /subsystem=tracker/:read-resource(recursive=true)
{
    "outcome" => "success",
    "result" => {"type" => {
        "war" => {"tick" => 10000L},
        "sar" => {"tick" => 10000L}
    }}
}
----

We can remove both the deployment types which removes them from the
model:

[source,options="nowrap"]
----
[standalone@localhost:9999 /] /subsystem=tracker/type=sar:remove
{"outcome" => "success"}
[standalone@localhost:9999 /] /subsystem=tracker/type=war:remove
{"outcome" => "success"}
[standalone@localhost:9999 /] /subsystem=tracker/:read-resource(recursive=true)
{
    "outcome" => "success",
    "result" => {"type" => undefined}
}
----

You should now see the output from the `TrackerService` instances having
stopped.

Now, let's add the war tracker again:

[source,options="nowrap"]
----
[standalone@localhost:9999 /] /subsystem=tracker/type=war:add
{"outcome" => "success"}
[standalone@localhost:9999 /] /subsystem=tracker/:read-resource(recursive=true)
{
    "outcome" => "success",
    "result" => {"type" => {"war" => {"tick" => 10000L}}}
}
----

and the WildFly console should show the messages coming from the war
`TrackerService` again.

Now let us deploy something. You can find two maven projects for test
wars already built at link:downloads/test1.zip[test1.zip] and
link:downloads/test2.zip[test2.zip]. If you download them and
extract them to `/Downloads/test1` and `/Downloads/test2`, you can see
that `/Downloads/test1/target/test1.war` contains a `META-INF/cool.txt`
while `/Downloads/test2/target/test2.war` does not contain that file.
From CLI deploy `test1.war` first:

[source,java,options="nowrap"]
----
[standalone@localhost:9999 /] deploy ~/Downloads/test1/target/test1.war
'test1.war' deployed successfully.
----

And you should now see the output from the war `TrackerService` list the
deployments:

....
15:35:03,712 INFO  [org.jboss.as.server.deployment] (MSC service thread 1-2) Starting deployment of "test1.war"
15:35:03,988 INFO  [org.jboss.web] (MSC service thread 1-1) registering web context: /test1
15:35:03,996 INFO  [org.jboss.as.server.controller] (pool-2-thread-9) Deployed "test1.war"
15:35:13,056 INFO  [stdout] (Thread-9) Current deployments deployed while war tracking active:
15:35:13,056 INFO  [stdout] (Thread-9) [test1.war]
15:35:13,057 INFO  [stdout] (Thread-9) Cool: 1
....

So our `test1.war` got picked up as a 'cool' deployment. Now if we
deploy `test2.war`

[source,options="nowrap"]
----
[standalone@localhost:9999 /] deploy ~/sourcecontrol/temp/archetype-test/test2/target/test2.war
'test2.war' deployed successfully.
----

You will see that deployment get picked up as well but since there is no
`META-INF/cool.txt` it is not marked as a 'cool' deployment:

....
15:37:05,634 INFO  [org.jboss.as.server.deployment] (MSC service thread 1-4) Starting deployment of "test2.war"
15:37:05,699 INFO  [org.jboss.web] (MSC service thread 1-1) registering web context: /test2
15:37:05,982 INFO  [org.jboss.as.server.controller] (pool-2-thread-15) Deployed "test2.war"
15:37:13,075 INFO  [stdout] (Thread-9) Current deployments deployed while war tracking active:
15:37:13,075 INFO  [stdout] (Thread-9) [test1.war, test2.war]
15:37:13,076 INFO  [stdout] (Thread-9) Cool: 1
....

An undeploy

[source,java,options="nowrap"]
----
[standalone@localhost:9999 /] undeploy test1.war
Successfully undeployed test1.war.
----

is also reflected in the `TrackerService` output:

....
15:38:47,901 INFO  [org.jboss.as.server.controller] (pool-2-thread-21) Undeployed "test1.war"
15:38:47,934 INFO  [org.jboss.as.server.deployment] (MSC service thread 1-3) Stopped deployment test1.war in 40ms
15:38:53,091 INFO  [stdout] (Thread-9) Current deployments deployed while war tracking active:
15:38:53,092 INFO  [stdout] (Thread-9) [test2.war]
15:38:53,092 INFO  [stdout] (Thread-9) Cool: 0
....

Finally, we registered a write attribute handler for the `tick` property
of the `type` so we can change the frequency

[source,options="nowrap"]
----
[standalone@localhost:9999 /] /subsystem=tracker/type=war:write-attribute(name=tick,value=1000)
{"outcome" => "success"}
----

You should now see the output from the `TrackerService` happen every
second

....
15:39:43,100 INFO  [stdout] (Thread-9) Current deployments deployed while war tracking active:
15:39:43,100 INFO  [stdout] (Thread-9) [test2.war]
15:39:43,101 INFO  [stdout] (Thread-9) Cool: 0
15:39:44,101 INFO  [stdout] (Thread-9) Current deployments deployed while war tracking active:
15:39:44,102 INFO  [stdout] (Thread-9) [test2.war]
15:39:44,105 INFO  [stdout] (Thread-9) Cool: 0
15:39:45,106 INFO  [stdout] (Thread-9) Current deployments deployed while war tracking active:
15:39:45,106 INFO  [stdout] (Thread-9) [test2.war]
....

If you open `$WFLY/standalone/configuration/standalone.xml` you can see
that our subsystem entry reflects the current state of the subsystem:

[source,xml,options="nowrap"]
----
        <subsystem xmlns="urn:com.acme.corp.tracker:1.0">
            <deployment-types>
                <deployment-type suffix="war" tick="1000"/>
            </deployment-types>
        </subsystem>
----

[[expressions]]
== Expressions

Expressions are mechanism that enables you to support variables in your
attributes, for instance when you want the value of attribute to be
resolved using system / environment properties.

An example expression is

....
${jboss.bind.address.management:127.0.0.1}
....

which means that the value should be taken from a system property named
`jboss.bind.address.management` and if it is not defined use
`127.0.0.1`.

[[what-expression-types-are-supported]]
=== What expression types are supported

* System properties, which are resolved using
`java.lang.System.getProperty(String key)`
* Environment properties, which are resolved using
`java.lang.System.getEnv(String name)`.
* Security vault expressions, resolved against the security vault
configured for the server or Host Controller that needs to resolve the
expression.

In all cases, the syntax for the expression is

....
${expression_to_resolve}
....

For an expression meant to be resolved against environment properties,
the `expression_to_resolve` must be prefixed with `env.`. The portion
after `env.` will be the name passed to
`java.lang.System.getEnv(String name)`.

Security vault expressions do not support default values (i.e. the
`127.0.0.1` in the `jboss.bind.address.management:127.0.0.1` example
above.)

[[how-to-support-expressions-in-subsystems]]
=== How to support expressions in subsystems

The easiest way is by using AttributeDefinition, which provides support
for expressions just by using it correctly.

When we create an AttributeDefinition all we need to do is mark that is
allows expressions. Here is an example how to define an attribute that
allows expressions to be used.

[source,java,options="nowrap"]
----
SimpleAttributeDefinition MY_ATTRIBUTE =
            new SimpleAttributeDefinitionBuilder("my-attribute", ModelType.INT, true)
                    .setAllowExpression(true)
                    .setFlags(AttributeAccess.Flag.RESTART_ALL_SERVICES)
                    .setDefaultValue(new ModelNode(1))
                    .build();
----

Then later when you are parsing the xml configuration you should use the
MY_ATTRIBUTE attribute definition to set the value to the management
operation ModelNode you are creating.

[source,java,options="nowrap"]
----
....
      String attr = reader.getAttributeLocalName(i);
      String value = reader.getAttributeValue(i);
      if (attr.equals("my-attribute")) {
          MY_ATTRIBUTE.parseAndSetParameter(value, operation, reader);
      } else if (attr.equals("suffix")) {
.....
----

Note that this just helps you to properly set the value to the model
node you are working on, so no need to additionally set anything to the
model for this attribute. Method parseAndSetParameter parses the value
that was read from xml for possible expressions in it and if it finds
any it creates special model node that defines that node is of type
`ModelType.EXPRESSION`.

Later in your operation handlers where you implement populateModel and
have to store the value from the operation to the configuration model
you also use this MY_ATTRIBUTE attribute definition.

[source,java,options="nowrap"]
----
 @Override
 protected void populateModel(ModelNode operation, ModelNode model) throws OperationFailedException {
        MY_ATTRIBUTE.validateAndSet(operation,model);
 }
----

This will make sure that the attribute that is stored from the operation
to the model is valid and nothing is lost. It also checks the value
stored in the operation `ModelNode`, and if it isn't already
`ModelType.EXPRESSION`, it checks if the value is a string that contains
the expression syntax. If so, the value stored in the model will be of
type `ModelType.EXPRESSION`. Doing this ensures that expressions are
properly handled when they appear in operations that weren't created by
the subsystem parser, but are instead passed in from CLI or admin
console users.

As last step we need to use the value of the attribute. This is usually
needed inside of the `performRuntime` method

[source,java,options="nowrap"]
----
 protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model, ServiceVerificationHandler verificationHandler, List<ServiceController<?>> newControllers) throws OperationFailedException {
       ....
        final int attributeValue = MY_ATTRIBUTE.resolveModelAttribute(context, model).asInt();     
        ...
 
    }
----

As you can see resolving of attribute's value is not done until it is
needed for use in the subsystem's runtime services. The resolved value
is not stored in the configuration model, the unresolved expression is.
That way we do not lose any information in the model and can assure that
also marshalling is done properly, where we must marshall back the
unresolved value.

Attribute definitinon also helps you with that:

[source,java,options="nowrap"]
----
 public void writeContent(XMLExtendedStreamWriter writer, SubsystemMarshallingContext context) throws XMLStreamException {
    ....
      MY_ATTRIBUTE.marshallAsAttribute(sessionData, writer);
      MY_OTHER_ATTRIBUTE.marshallAsElement(sessionData, false, writer);
    ...
}
----
