As many of you know, the management web interface is implemented in GWT. The means we cross compile from Java to Javascript, but all development is done in Java. Many of the default GWT components like tables, trees and lists expect a strongly typed Java model, since this would be logical choice for most GWT implementations. For us, working on the management web interface this means we have to bridge the gap between the detyped model the application server uses and the strongly typed model the GWT components rely on.
In this post I am going to explain some of the building blocks we use within the management interface, to reduce the amount of boilerplate we have to provide converting between the two model representations.
The detyped model
A typical model representation we get from the AS management layer looks like this:
[domain@localhost:9999 /] /subsystem=datasources/data-source=ExampleDS:read-resource
{
"outcome" => "success",
"result" => {
"connection-url" => "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1",
"driver-name" => "h2",
"enabled" => true,
"jndi-name" => "java:jboss/datasources/ExampleDS",
"jta" => true,
"password" => "sa",
"use-ccm" => true,
"user-name" => "sa",
}
}
In this case we are looking at a data source.
In order create and modify datasource, the management client would need to send a detyped representation like the one above and will get a response of the same content type.
GWT entities
Entities in GWT are represented as Java interfaces with getter and setter methods (AutoBean API). The corresponding representation for the data source above, would look like this:
@Address("/subsystem=datasources/data-source={0}")
public interface DataSource {
@Binding(detypedName = "jndi-name")
String getJndiName();
void setJndiName(String name);
@Binding(detypedName = "user-name")
String getUsername();
void setUsername(String user);
String getPassword();
void setPassword(String password);
[...]
}
Coverting between two models
Converting between the two model representation is the job of the EntityAdapter. An EntityAdapter works on the meta data declared on the entity class (@Address & @Binding annotations). The metadata itself is extracted during the compile time phase (deferred binding) .
The current meta data structure is divided into two distinct concepts: Entity addressing and the property binding. The address is required to perform operations on the detyped model ("/subsystem=datasource/data-source=ExampleDS:read-resource"). The property binding is used to get and set values within both models.
Let's take a look at an example:
// Create an EntityAdapter for a specific type
EntityAdapteradapter = new EntityAdapter (DataSource.class, metaData);
// Convert from entity to DMR representation
DataSource datasource = ...;
ModelNode operation = adapter.fromEntity(datasource);
// execute the operation (HTTP request) ...
First of all, we create an EntityAdapter for the DataSource.class type. This adapter allows us to convert an entity instance (DataSource datasource) into a ModelNode (detyped) representation. We can use this model to execute an operation against the AS management layer (HTTP Post).
Let's do it vice versa: Reading a detyped model and convert it into an entity.
// Create an EntityAdapter for a specific type
EntityAdapteradapter = new EntityAdapter (DataSource.class, metaData);
ModelNode detyped = ...; // HTTP response
// Convert form DMR to entity
DataSource datasource = adapter.fromDMR(detyped);
// Work on the entity ....
Addressing of resources
As you can see in the above example, the entity carries an @Address annotation. In order to read from or write the management layer you would need to know how to address a resource properly. For datasources this would be:
/subsystem=datasources/data-source=ExampleDS
There are many cases where we need an address. It makes sense to associate this information with the entity itself and use it as a template for subsequent requests. I.e an address template like "@Address("/subsystem=datasources/data-source={0}")"
AddressBinding address = metaData.getBeanMetaData(DataSource.class).getAddress();
ModelNode operation = address.asSubresource("ExampleDS");
// further specify the operation (OP, RECURSIVE,ETC)
operation.get(OP).set(READ_RESOURCE);
// execute the operation (HTTP request) ...
What's next?
We have seen how two specific problems are solved: Addressing of resources and converting between two model types. In the next part of this series, I am going to explain how the actual data binding (mapping to HTML forms) actually works.
No comments:
Post a Comment