[jboss-as7-dev] Detyped API document feedback

David M. Lloyd david.lloyd at redhat.com
Tue Jan 4 12:02:03 EST 2011


This commentary is in reference to version 2 of the detyped API document
at:  http://community.jboss.org/wiki/AS7DetypedManagementAPI/version/2

========

Goals

> * Define a Java-based API that can be consumed by a remote java
> client that requires only a small number of types besides JDK
> classes to be on the client classpath. Any complex types involved in
> the API are expressed in terms of a fixed set of general purpose
> classes.

Right on

> The types needed on the client classpath are:
>
> - A library containing the small set of general purpose types (see
> discussion of MetaValue, MetaType below), plus some helper utilities
> (e.g. Builder classes) to assist in building up objects of those
> types.

OK

> 4 interface classes, each of which describes the methods exposed by
> one of the server side "controllers". The parameters and return types
> in these interfaces should be expressed in terms of the general
> purpose types. The methods in these interfaces will be limited and
> general purpose in nature, i.e. there will not be a myriad of
> detailed methods like "setHttpPort(int port)".  The 4 types of
> "controllers" are:

I agree on the lack of detailed methods.  However I strongly disagree on
having a method for each controller type.  There is no reason why we
cannot have a single protocol where the target object is distinguished
by address.  This allows users more code reuse because all they have to
change is the target address of what they're modifying.

We should be able to use a generic RPC mechanism like Remoting to send a
standard request type to a single protocol listener.

<trim>
> * On the server side, provide a straightforward mapping of the core
> detyped Java API to JMX / REST...
<trim>
> * Handle different types of management actions:

This brings up a good point.  So far on ServerController, the sole
interaction method is handleUpdate(), however updates are only one of
the things supported there.  So the method should be renamed to
something more generic, like handleRequest() or something.  Of course
the *Update classes are going away so we don't have to bother renaming
those.

<trim>
> * Provide clean handling for operations that inherently impact
> multiple servers (i.e. updates to the domain.xml configuration model
> and any host.xml configuration.) Allow the end user to specify how
> those changes are applied to various server groups and the servers
> within those groups in a "management operation plan". After the
> operation is executed, provide detailed information on how the
> operation was executed on the various controllers and servers
> involved.
<snip>
> * Management operation plans should include support for multiple
> operations that update persistent configuration that are to be
> performed as an atomic unit.
 >
> - TODO: is it necessary to support plans
> with multiple operations that are not atomic; i.e. where failure of
> one operation will not cause the others to be rolled back? We
> currently do, but this adds conceptual complexity and clutters the
> API a bit.

On the model level, no.  Either the whole update is applied or none of it.

At runtime, I think that any part of a multi-step ("composite") update 
failing is equivalent to the whole update failing.  Also, any update, be 
it simple or composite, should only have one single plan which applies 
to the whole works (in other words, the plan for application and/or 
rollback may only be present on the top-most update entity which was 
submitted to the controller).

This simplifies the whole issue.  I can say, "Run these 3 updates.  If 
they fail (at runtime), just keep it in there and I'll inspect and/or 
fix it by hand.", or "If they fail, roll them all back".  These are the 
only two sensible actions I can think of.

<more trimming, until we get to...>

========

MetaValues

I agree with some of these types but not others:

* SimpleValue
     + String - yes
     + primitives - some; maybe just boolean/int/long/float/double but 
not byte/short/char for example
     + Date - no, let's use long for now, and revisit once JSR-310 is 
done.  Date is basically worthless.
     + BigDecimal/BigInteger - yes
* CollectionValue/ArrayValue - No, I'd rather see byte[] added to the 
above list and otherwise have a list value type, each element of which 
can be whatever type
* MapValue - No, we don't need any map key type other than strings
* CompositeValue - too much work, why not just use a simple map of 
Strings to values?  By making the type dependent on the key it's 
basically impossible to use without metatype info
* TableMetaValue - too complex

I'd rather do something more dynamic where you can start with a node and 
just build up whatever you want from it:

node = new ModelNode();
node.get("operation").set("change-bind-port"); // string
node.get("address").add("base, "domain"); // a list
node.get("address").add("profile", "web");
node.get("address").add("subsystem", "web");
node.get("address").add("connector", "ssl");
node.get("bind-port").set(8433); // int
result = connection.execute(node); // goes over remoting
System.out.println("Result is: " + result);
// nicely formatted, JSON-style (but with more possible types)

I wipped up a demo of this style of API at: 
https://github.com/dmlloyd/jboss-dmr

Notice there's only one public class and one type enum.  (The Validation 
class isn't anything, just an experiment.)

The main point to this API is that things like Map and List are 
*completely inadequate* by themselves to define a detyped API.  The 
reason that languages like Perl, PHP, Python, Ruby, and JS are so good 
at this kind of thing is because they can take advantage of dynamic 
typing in a way that Java normally can't.  So the best solution in my 
view is to layer a simple dynamic-type layer on top of the static types 
we're used to dealing with.

========

MetaTypes

I gotta be honest, I'm not liking this API.  I don't see this as 
something users will consider to be a huge advantage over JMX.  I know 
there are notes in here about improving user-friendliness but I do not 
believe this is something that can be added in later; it has to be built 
in from the ground up.

If we want a real differentiator from JMX (i.e. a much lower barrier to 
entry), the first step is making requests easy to build.

I'd consider it a logical progression to use the same dynamic 
representation given above to represent the actual model data.  Instead 
of mirroring JMX with management entities combining operations and data, 
we should have a pure-data view of the model itself.

The registry of operations should be external to the model itself; it 
would be a map with a key that corresponds to the operation name plus a 
"path" specification to the applicable node type.  Some operations (like 
"read") would be generic and would apply to any node path, where some 
(like the example above) would be highly specific.

Querying a model element amounts to copying a chunk of this data 
verbatim, which is why more generic operations like "read" can work.

This means that we don't need to replicate common operations.  To look 
at it from a JMX perspective, we can dynamically build the mbean 
operation sets off of all the operations which can apply to the given 
model node.  Thus every mbean could automatically get a "read" method 
which can yield the model data.

This is also important because the whole set of all operable entities 
encapsulates more than the model in even the simplest scenarios.  Thus 
operations have to be able to reach outside of the model to do things 
like query runtime state and so on.

Of course this bypasses the other half of the problem which is 
describing the model and available the operations.  But, by having a 
special value type for representing a value type, this kind of dynamic 
representation can be fully self-describing:

{
     "operation-name" => "change-bind-port",
     "address" => [
         ("base"=>"domain"),
         ("profile"=>"*"),
         ("subsystem"=>"web"),
         ("connector"=>"*"),
     ],
     "request-properties" => {
         "bind-port" => {
             type => INTEGER,
             required => true,
             min-value => 0,
             max-value => 65535,
         }
     },
     "reply-properties" => {}
}

Thus we can have an operation whose purpose is itself to describe 
operations.  This also enables our domain controller to automatically 
remap server- and host-level operations to the domain level by adding 
address discrimination without having to replicate the code for these 
operations.

This means we can perform validation generically, if we want - even on 
the client side, optionally.  This should help reduce the "bogeyman 
effect" that may be associated with dynamic type systems.

========

In conclusion - I think if you look at the primary use case for this 
API, the thing it will be used for more than anything else is going to 
be custom scripting and special-purpose client programming.  A dynamic 
model is perfect for this use case because it has the absolute minimum 
barrier of entry possible for a detyped API.  Users already know what is 
valid, they just want to put in the properties and run it.

-- 
- DML



More information about the jboss-as7-dev mailing list