On 06/30/2015 11:30 AM, Brian Stansberry wrote:
tl;dr; DUPs may need access to the integration APIs provided by
capabilities. I've got a first cut solution to that, but there are edge
cases that don't work well and we may need to restrict how capabilities
work a bit.
Long version.
WildFly Core capabilities are meant to provide a standardized way for
parts of the server to integrate with each other.[1]
Use of these is proceeding pretty nicely in the OperationStepHandlers
the kernel and subsystems use for executing management operations. But
in some cases, the code that needs to integrate with a capability isn't
in an OSH. It can be in a service start method, particularly in a
DeploymentUnitProcessor. (DUPs run in a service start method.)
A simple example would be a deployment descriptor or annotation includes
a chunk of config that specifies the use of a subsystem-provided
resource, say a cache. The DUP then wants to figure out the service name
of that resource, using capabilities.
Data sources are another good example. A data
source can be defined in
the datasource subsystem, in a deployment with an *-ds.xml descriptor or
a couple standard Java EE ways. I've opened a PR [1] to expose the
subsystem resource as a capability. I registered the legacy service name
as an alias to the capabilities resolved service name. I don't however
use the capability service name for the services added via the DUP's.
One reason being the support wasn't there for it, though it could be
done with some simple helpers. The second reason is some what of a
tangent and off topic, but I'll go ahead with it :)
For data source resources a dynamic RuntimeCapability is used. The
dynamic name for the capability is the name of resource, e.g. ExampleDS
for the default configuration. This is useful for things like the batch
subsystem that may need a data source but don't want to rely on a JNDI
name as it's messy. However deployments that define their own data
source, say via @DataSourceDefinition annotation, don't have a resource
so the name used for the service is the JNDI name. While that's
irrelevant to a subsystem that wants the capability it is relevant to a
say a JPA deployment that needs to know which data source to use based
on the JNDI name. That requires that a service always have an alias to
match the DUP dependency service name for the data source to avoid
duplicate entries.
After re-reading the above I'm not sure what I'm on about and it seems
very off topic for this subject. I'm leaving it however as just
something for others to think about when add capabilities.
[1]:
https://github.com/wildfly/wildfly/pull/7682
So I've started to give some thought to how to handle that. See
https://github.com/bstansberry/wildfly-core/commit/ba7321bc30374b2d0aa99c...
for a first cut.
Basically the OperationContext exposes a "CapabilityServiceSupport"
object that OSHs can provide to services they install. The services can
then use that to look up service names and any custom runtime API
provided by a capability.
Typical pattern would be the OSH for a "deploy" op would make the
CapabilityServiceSupport available to RootDeploymentUnitService, which
would expose it to DUPs via a DeploymentPhaseContext attachment. DUPs,
as they install services would use the CapabilityServiceSupport to look
up service names and add dependencies to that new service.
The service doesn't register any requirement for the capability. If it
tries to use a non-existent capability, an exception is thrown and the
service has to deal with it. *This is the main problem.* The caller is
unable to register a dependency (since it can't get the service name) so
the MSC behavior of letting the service be installed but fail to start
due to a missing dependency is short circuited.
If it's somewhat considered a
capability maybe short-circuiting the MSC
behavior is okay. I suppose in cases where that could be an issue an
alias could be used and then a standard ServiceName dependency added to
the DUP service.
In most cases, if a service is installed but fails to start due to a
missing dependency, the management op that installed it is rolled back,
so the end result is the same. But we do support the
"rollback-on-runtime-failure=false" management op header, and if that's
used our behavior would be changed for the worse.
A possible partial solution to this would be to tighten down the rules
for how service names are created from capability names. Basically, any
capability could only provide 1 service, and the name of the service
would be the result of passing the name of the capability to
ServiceName.parse(name). That's the default behavior now, but we support
other options.
That would fix the service name discovery problem, by making the name
discovery algorithm completely static. It wouldn't help with a case
where a DUP needs access to a custom runtime API exposed by a
capability. But that is much more of a corner case. Probably better to
discuss on a sub branch of this thread. :)
What would we lose if we tightened down the service name rules?
1) Capabilities would not be able to tell the service name discovery
logic to produce some legacy service naming pattern (i.e. keep service
names as they were in earlier releases.)
This one I don't think is a big deal, as the capability can always
register an alias for the service that uses the legacy service name.
2) A capability cannot provide services of > 1 value type. It's nice if
capabilities can represent something that's meaningful to an end user,
and there's no reason why something that's meaningful to an end user
might not expose more than one service to other capabilities. If we
limit capabilities to exposing a single service, then we may end up with
multiple capabilities. See [2] for an example case, where a proposed
"org.wildfly.iiop" (nice and simple for an end user to understand)
installs two services, an ORB and a NamingContextExt.
At this point though, capability names aren't really exposed to end
users, so perhaps we can put this problem aside for now. If it becomes
an issue later, there are solutions, e.g. a user-friendly
"org.wildfly.iiop" capability automatically registers the detailed
"org.wildfly.iiop.orb" and "org.wildfly.iiop.naming" capabilities,
helping the user ignore the details.
Having more than one service tied to a
capability is something I didn't
think of. I like the idea of a 1-1 relationship between a service and a
capability, but it does make sense what's happening here for IIOP.
I guess one idea might be to provide some kind of parent/child
relationship. Maybe not a deep hierarchy but something where I could say
as a consumer give me "org.wildfly.iiop" and I'd get "orb" and
"naming"
capabilities. Or just say give me the "org.wildfly.iiop.orb" only
capability. Though this will probably really complicate things.
Sorry for the long tome; by now I expect folks are used to it!
[1] Specifically, to validate that references to other parts of the
configuration are valid, to determine the names of MSC services provided
by other parts of the system, and to access any custom runtime
integration API exposed by another part of the system.
[2]
https://github.com/bstansberry/wildfly/commit/b9de64e046404531df466288cf4...
--
James R. Perkins
JBoss by Red Hat