WildFly subsystems and the WildFly core can expose various capabilities that other subsystems can utilize. For example, the remoting subsystem provides a remote communication capability that the ejb subsystem uses to provide its remote ejb capability. Various subsystem use the infinispan subsystem's caching capabilities. In releases prior to WildFly 9, the way that subsystems used capabilities provided by other subsystems and the core has been ad hoc. In order to make WildFly a more easily customizable platform, we need to solidify the contracts for the relationship between capabilities so users can safely configure a system that has the capabilities they require. This document is the design document for how WildFly 9 capabilities should be delivered.
A capability is a coherent set of functionality that a subsystem or the core exposes to end users and to other subsystems.
There are two primary use cases for the capability and requirement data exposed by WildFly core and its subsystems: provisioning of new servers/domains and runtime interaction between capabilities.
A capability is associated with one or more resources or attributes in the provider's management model. The capability is only provided if the relevant items are configured. As a result, the capabilities desired by the user are not statically known.
A capability will be represented in a type that provides the follow set of data:
- name. Uniquely identifies the capability. Cannot be null. A simple string, but must be namespaced to prevent conflicts between features provided by different groups of people. The org.wildfly namespace and all subspaces thereof is reserved for use by the maintainers of WildFly itself.
- description. Internationalized description of the capability, for display in tooling.
- requirements. A Set<name> of other capabilities that this capability needs to function. Cannot be null but can be an empty set.
- optional-requirements. A Set<name> of other capabilities that this capability needs in order to provide an optional aspect of its functionality. If at provisioning time or at runtime the configuration provided by the user indicates the optional aspect of the functionality is desired, the capability resolution system will be informed of this and the requirement will be treated as non-optional. (TODO work out how to also provide some internationalized text about what the consequences are if a required capability is not present, for use in the provisioning use case.)
Cycles are supported.
A core capability cannot depend on subsystem capabilities. (TODO see if this rule is really needed.)
A base capability for a subsystem cannot depend on other capabilities of that subsystem.
The base core capability (i.e. the one provided by WildFly core, name TBD) cannot depend on any other capabilities.
All capabilities implicitly depend on the base core capability and do not need to include this in their set of requirements.
A user wishing to provision a server should be able to do so by enumerating desired capabilities, without having enumerate subsystems. Therefore, every subsystem should be associated with a capability.
The following additional attributes of a capability relate to one or the other primary use cases for capabilities discussed below, and thus may end up being expressed in use-case-specific subtypes.
- default. boolean indicating whether a capability is provided by default if the module that provides it is used. Usage would be for the provisioning use case described below, to reduce the amount of information a user would need to provide to get a typical configuration. TBD whether this make sense.
- runtime API. An Object that encapsulates the API the capability exposes for use by other capabilities outside the subsystem. Intended for the runtime use case discussed below. Not an end user API, except to the extent end users are providing their own custom capabilities. Ideally all interaction with the capability from callers outside the subsystem should be via this API, eliminating much of the arbitrary code used in WF 8 and earlier. May be null if the capability provides no API.
See Core and Subsystem Capability and Requirement Listing for an effort to identify the various capabilities exposed in the current WildFly code base and the requirements thereof.
Primary Use Cases
WildFly feature packs will include configuration elements that can be included in an overall server/domain configuration when a provisioning tool makes use of that feature pack. The capability and requirement information that is exposed by WildFly core and any extension module can be used by the provisioning tool to compose a finer grained set of configuration for the feature pack; e.g. only include in the ejb subsystem configuration the elements related to remote ejb support if the user has requested that capability.
The provisioning tool can also do finer grained validation with this information. For example if the user has requested distributed caching capability but hasn't requested the jgroups capability, the tool can either fail the build or possibly assist the user by bringing jgroups into the configuration.
How this will all work needs clarification. My initial thinking on this is the tool would use a ServiceLoader to read capability information from modules in a service pack, do necessary requirement resolution, and then ask the feature pack to provide a config that supports the required capabilities.
Usage of capabilities at runtime is mediated by the core WildFly management layer, specifically the OperationContext object that is provided to all OperationStepHandlers.
OperationStepHandlers will register the capabilities they provide with the OperationContext at Stage.MODEL. This registration will include an object providing the runtime API associated with the capability. However, it is forbidden for another capability to attempt to access that runtime API at Stage.MODEL. During boot, Stage.MODEL operations associated with different subsystems execute in parallel, so it is not possible for a subsystem OSH to check reliably that some other subsystem has provided a capability.
If a capability needs another capability that it has declared as being optional, during Stage.MODEL the OSH will notify the OperationContext that the optional capability is required.
Subsystems are only required to register a base capability if it either depends on some other capability or exposes a runtime API. Otherwise, a default base capability will be registered automatically at the end of Stage.MODEL. (TBD: not certain is this default registration provides any value.)
At the end of Stage.MODEL, resolution of all capabilities and their requirements will be performed by the management layer. If any required capability is not present the operation will be failed. This will occur with Stage.MODEL semantics; i.e. setting the rollback-on-runtime-failure=false header will not prevent the operation failing.
At Stage.RUNTIME and later an OSH can access the runtime API provided by a required capability by requesting it from the OperationContext. The OSH knows this can safely be done because all configured capabilities and their requirements were registered and validated in Stage.MODEL.
A module that provides multiple capabilities may depend on modules associated with required capabilities, in particular to provide the type(s) that provides the runtime API for that capability. However, the requiring module only needs access to those types if the user configuration actually creates the requirement. So, whether the module is actually required is not statically known.
This can be handled at the jboss-modules level by using the optional=true attribute on the dependency. For example:
<module xmlns="urn:jboss:module:1.1" name="org.jboss.as.ejb3">
<module name="org.jboss.as.remoting" optional="true"/>
However, for this to work properly it is critical that the dependent module only reference classes from the depended upon module if it is known that the capability is really required and that all required capabilities across the system have been resolved. This means:
- Don't reference classes in the depended upon module until Stage.RUNTIME
- Only reference them in code that only executes if the capability is really required.
A management resource or attribute that enables a particular capability must be annotated with management metadata that indicates the identifier of the capability. If a parent resource includes metadata declaring a particular capability, all child resources are implicitly considered to be part of that capability and do not need to declare such in their metadata.
Appropriate API for adding this metadata will be added to the ResourceDefinition and AttributeDefinition interfaces.
This information allows the management layer to provide information to users as to what specific model elements are providing a given capability.
It is possible that whether or not a capability is configured will depend on the value of an attribute (e.g. true or false), and not just its existence. The API for adding this metadata will need to account for this case.
Relationship to "Model References"
A model reference is an attribute in the WildFly management model whose value identifies some other resource in the management model. A common case of this is an attribute whose value is the name of a socket binding resource. The value of the attribute is typically the value of the last element in the referent resource's address. The rest of the referent resource's address is implicit in the nature of referring attribute. (Properly specifying this in the attribute's metadata is another goal of WF 9.)
Capabilities and requirements differ from model references in that a model reference indicates a specific instance of some capability; e.g. an attribute like "security-domain=other" indicates a requirement for a specific instance of the security-subsystem's "security domain" capability.