[JBoss AS7 Development] - Detyped Description of the AS 7 Management Model
by Brian Stansberry
Brian Stansberry [http://community.jboss.org/people/brian.stansberry] modified the document:
"Detyped Description of the AS 7 Management Model"
To view the document, visit: http://community.jboss.org/docs/DOC-16317
--------------------------------------------------------------
AS 7's management API will naturally include the ability for clients to query the management system to discover meta-information about the management model; i.e. what resources are available and what attributes and operations they expose. These details will be returned in a detyped form, either using the https://github.com/jbossas/jboss-dmr jboss-dmr library or JSON. (A JMX interface based on open mbeans will also be provided; most concepts in this article map naturally to JMX.) The purpose of this article is to describe the detyped representation of the API.
Readers are encouraged to look at the example detyped descriptions of parts of the management API at the bottom before reading the details.
Before getting into the details of the meta-information, first a couple quick examples on how to access it.
h5. Reading Management Model Descriptions via the raw Management API
The client must have maven artifact org.jboss.as:jboss-as-controller-client and its dependencies on the classpath.
1) Create a management client that can connect to your target process's native management socket (which can be an individual standalone mode server, or, in a domain mode environment, the Domain Controller or any Host Controller:
ModelControllerClient client = ModelControllerClient.Factory.create(InetAddress.getByName("localhost"), 9999);
The address and port are what is configured in the target process' <management-apis><native-api.../> element.
2) Create an operation request object using the org.jboss.dmr.ModelNode class:
ModelNode op = new ModelNode();
op.get("operation").set("read-resource-description");
op.get("recursive").set(true);
op.get("operations").set(true);
ModelNode address = op.get("address");
address.add("subsystem", "web");
address.add("connector", "http");
See http://community.jboss.org/docs/DOC-16336 http://community.jboss.org/docs/DOC-16336 for basic background on the format of an operation request. The key thing here is we are executing the "read-resource-description" operation. That operation can be targetted at any address in the management model; here we are targetting it at the resource for the web subsystem's http connector.
The request includes two optional parameters:
* recursive -- true means you want the description of child resources under this resource. Default is false
* operations -- true means you want the description of operations exposed by the resource to be included. Default is false.
3) Execute the operation and manipulate the result:
ModelNode returnVal = client.execute(op);
System.out.println(returnVal.get("result").toString());
See http://community.jboss.org/docs/DOC-16354 http://community.jboss.org/docs/DOC-16354 for general details on the structure of the "returnVal" ModelNode.
h5. Reading Management Model Descriptions via the CLI
See http://community.jboss.org/docs/DOC-16581 http://community.jboss.org/docs/DOC-16581 for basics on using the CLI.
Once you've launched the CLI the syntax for the command shown above would be:
[localhost:9999 /] /subsystem=web/connector=http:read-resource-description(recursive=true,operations=true)
h3.
h3. Description of addressable portions of the management model
All portions of the management model exposed by AS 7 are addressable via an ordered list of key/value pairs. For each addressable portion of the model, the following descriptive information will be available:
* description -- String -- text description of this portion of the model
* head-comment-allowed -- boolean -- indicates whether this portion of the model can store an XML comment that would be written in the persistent form of the model (e.g. domain.xml) before the start of the XML element that represents this portion of the model. This item is optional, and if not present defaults to true.
* tail-comment-allowed -- boolean -- similar to head-comment-allowed, but indicates whether a comment just before the close of the XML element is supported. A tail comment can only be supported if the element has child elements, in which case a comment can be inserted between the final child element and the element's closing tag. This item is optional, and if not present defaults to true.
* attributes -- Map of String (the attribute name) to complex structure -- the configuration attributes available in this portion of the model. See below for the representation of each attribute.
* operations -- Map of String (the operation name) to complex structure -- the operations that can be targetted at this address. See below for the representation of each operation.
* children -- Map of String (the type of child) to complex structure -- the relationship of this portion of the model to other addressable portions of the model. See below for the representation of each child relationship.
{
"description => "A managable resource",
"tail-comment-allowed" => false,
"attributes" => {
"foo" => {
.... details of attribute foo
}
},
"operations" => {
"start" => {
.... details of the start operation
}
},
"children" => {
"bar" => {
.... details of the relationship with children of type "bar"
}
}
}
h4. Description of an Attribute
An attribute is a portion of the management model that is not directly addressable. Instead, it is conceptually a property of an addressable part of the model. For each attribute portion of the model, the following descriptive information will be available:
* description -- String -- text description of the attribute
* type -- org.jboss.dmr.ModelType -- the type of the attribute value. One of the enum values BIG_DECIMAL, BIG_INTEGER, BOOLEAN, BYTES, DOUBLE, INT, LIST, LONG, OBJECT, PROPERTY, STRING. Most of these are self-explanatory. An OBJECT will be represented in the detyped model as a map of string keys to values of some other legal type, conceptually similar to a javax.management.openmbean.CompositeData. A PROPERTY is a single key/value pair, where the key is a string, and the value is of some other legal type.
* value-type -- ModelType or complex structure -- Only present if type is LIST or OBJECT. If type is LIST or all the values of the OBJECT type are of the same type, this will be one of the ModelType enums BIG_DECIMAL, BIG_INTEGER, BOOLEAN, BYTES, DOUBLE, INT, LIST, LONG, PROPERTY, STRING. Otherwise, value-type will detail the structure of the attribute value, enumerating the value's fields and the type of their value.
* required -- boolean -- true if the attribute will always exist in a representation of its portion of the model; false if it may not (implying a null value.) If not present, true is the default.
* head-comment-allowed -- boolean -- indicates whether the model can store an XML comment that would be written in the persistent form of the model (e.g. domain.xml) before the start of the XML element that represents this attribute. This item is optional, and if not present defaults to *false*. (This is a different default from is used for an addressable portion of the model, since model attributes often map to XML attributes, which don't allow comments.)
* tail-comment-allowed -- boolean -- similar to head-comment-allowed, but indicates whether a comment just before the close of the XML element is supported. A tail comment can only be supported if the element has child elements, in which case a comment can be inserted between the final child element and the element's closing tag. This item is optional, and if not present defaults to *false*. (This is a different default from is used for an addressable portion of the model, since model attributes often map to XML attributes, which don't allow comments.)
* arbitrary key/value pairs that further describe the attribute value, e.g. "max" => 2. See "Arbitrary Descriptors" below.
"foo" => {
"description" => "The foo",
"type" => INT,
"max" => 2
}
"bar" => {
"description" => "The bar",
"type" => OBJECT,
"value-type" => {
"size" => INT,
"color" => STRING
}
}
h3. Description of an Operation
An addressable portion of the model may have operations associated with it. The description of an operation will include the following information:
* operation-name -- String -- the name of the operation
* description -- String -- text description of the operation
* request-properties -- Map of String to complex structure -- description of the parameters of the operation. Keys are the names of the parameters, values are descriptions of the parameter value types. See below for details on the description of parameter value types.
* reply-properties -- complex structure, or empty -- description of the return value of the operation, with an empty node meaning void.
h4. Description of an Operation Parameter or Return value
* description -- String -- text description of the parameter or return value
* type -- org.jboss.dmr.ModelType -- the type of the parameter or return value. One of the enum values BIG_DECIMAL, BIG_INTEGER, BOOLEAN, BYTES, DOUBLE, INT, LIST, LONG, OBJECT, PROPERTY, STRING.
* value-type -- ModelType or complex structure -- Only present if type is LIST or OBJECT. If type is LIST or all the values of the OBJECT type are of the same type, this will be one of the ModelType enums BIG_DECIMAL, BIG_INTEGER, BOOLEAN, BYTES, DOUBLE, INT, LIST, LONG, PROPERTY, STRING. Otherwise, value-type will detail the structure of the attribute value, enumerating the value's fields and the type of their value.
* required -- boolean -- Only relevant to parameters. true if the parameter must be present in the request object used to invoke the operation; false if it can omitted. If not present, true is the default.
* nillable -- boolean -- true if null is a valid value. If not present, false is the default.
* arbitrary key/value pairs that further describe the attribute value, e.g. "max" =>2. See "Arbitrary Descriptors" below.
{
"operation-name" => "incrementFoo",
"description" => "Increase the value of the 'foo' attribute by the given amount",
"request-properties" => {
"increment" => {
"type" => INT,
"description" => "The amount to increment",
"required" => true
}},
"reply-properties" => {
"type" => INT,
"description" => "The new value",
}
}
{
"operation-name" => "start",
"description" => "Starts the thing",
"request-properties" => {},
"reply-properties" => {}
}
h2. Arbitrary Descriptors
The description of an attribute, operation parameter or operation return value type can include arbitrary key/value pairs that provide extra information. Whether a particular key/value pair is present depends on the context, e.g. a pair with key "max" would probably only occur as part of the description of some numeric type.
Following are standard keys and their expected value type. If descriptor authors want to add an arbitrary key/value pair to some descriptor and the semantic matches the meaning of one of the following items, the standard key/value type should be used.
* min -- int -- the minimum value of some numeric type. The absence of this item implies there is no minimum value.
* max -- int -- the maximum value of some numeric type. The absence of this item implies there is no maximum value.
* min-length -- int -- the minimum length of some string, list or byte[] type. The absence of this item implies a minimum length of zero.
* max-length -- int -- the maximum length of some string, list or byte[]. The absence of this item implies there is no maximum value.
* nillable -- boolean -- whether null is a legal value. The absence of this item implies false; i.e. null is not a legal value.
* allowed -- List -- a list of legal values. The type of the elements in the list should match the type of the attribute.
* default -- the default value for the attribute if not present in the model
* unit - The unit of the value - e.g. ns, ms, s, m, h, KB, MB, TB. Maybe similar allowed values like http://git.fedorahosted.org/git/?p=rhq/rhq.git;a=blob;f=modules/core/doma... MeasurmentUnits.
h2. Description of Parent/Child Relationships
The address used to target an addressable portions of the model must be an ordered list of key value pairs. The effect of this requirement is the addressable portions of the model naturally form a tree structure, with parent nodes in the tree defining what the valid keys are and the children defining what the valid values are. The parent node also defines the cardinality of the relationship. The description of the parent node includes a children element that describes these relationships:
{
....
"children" => {
"connector" => {
.... description of the relationship with children of type "connector"
},
"virtual-host" => {
.... description of the relationship with children of type "virtual-host"
}
}
The description of each relationship will include the following elements:
* description -- String -- text description of the relationship
* min-occurs -- int, either 0 or 1 -- Minimum number of children of this type that must exist in a valid model. If not present, the default value is 0.
* max-occurs -- int -- Maximum number of children of this type that may exist in a valid model. If not present, the default value is Integer.MAX_VALUE, i.e. there is no limit.
* allowed -- List of strings -- legal values for children names. If not present, there is no restriction on children names.
* model-description -- either "undefined" or a complex structure -- This is the full description of the child resource (its text description, attributes, operations, children) as detailed above. This may also be "undefined", i.e. a null value, if the query that asked for the parent node's description did not include the "recursive" param set to true.
Example with if the recursive flag was set to true:
{
"description" => "The connectors used to handle client connections",
"min-occurs" > 1,
"model-description" => {
"description" => "Handles client connections",
"attributes => {
... details of children as documented above
},
"operations" => {
.... details of operations as documented above
},
"children" => {
.... details of the children's children
}
}
}
If the recursive flag was false:
{
"description" => "The connectors used to handle client connections",
"min-occurs" > 1,
"model-description" => undefined
}
h2. Example Representation of a Portion of the Domain Controller API
{
"description" => "The root node of the domain-level management model.",
"attributes" => {
"namespaces" => {
"type" => OBJECT,
"value-type" => STRING,
"description" => "Map of namespaces used in the configuration XML document, where keys are namespace prefixes and values are schema URIs.",
"required" => false
},
"schema-locations" => {
"type" => OBJECT,
"value-type" => STRING,
"description" => "Map of locations of XML schemas used in the configuration XML document, where keys are schema URIs and values are locations where the schema can be found.",
"required" => false
}
},
"operations" => "TODO",
"children" => {
"extension" => {
"description" => "A list of extension modules.",
"model-description" => "TODO"
},
"path" => {
"description" => "A list of named filesystem paths. The paths may or may not be fully specified (i.e. include the actual paths.)",
"model-description" => {
"description" => "A named filesystem path, but without a requirement to specify the actual path. If no actual path is specified, acts as a placeholder in the model (e.g. at the domain level) until a fully specified path definition is applied at a lower level (e.g. at the host level, where available addresses are known.)",
"tail-comment-allowed" => false,
"attributes" => {
"name" => {
"type" => STRING,
"description" => "The name of the path. Cannot be one of the standard fixed paths provided by the system: <ul><li>jboss.home - the root directory of the JBoss AS distribution</li><li>user.home - user's home directory</li><li>user.dir - user's current working directory</li><li>java.home - java installation directory</li><li>jboss.server.base.dir - root directory for an individual server instance</li></ul> Note that the system provides other standard paths that can be overridden by declaring them in the configuration file. See the 'relative-to' attribute documentation for a complete list of standard paths.",
"required" => true
},
"path" => {
"type" => STRING,
"description" => "The actual filesystem path. Treated as an absolute path, unless the 'relative-to' attribute is specified, in which case the value is treated as relative to that path. <p>If treated as an absolute path, the actual runtime pathname specified by the value of this attribute will be determined as follows: </p>If this value is already absolute, then the value is directly used. Otherwise the runtime pathname is resolved in a system-dependent way. On UNIX systems, a relative pathname is made absolute by resolving it against the current user directory. On Microsoft Windows systems, a relative pathname is made absolute by resolving it against the current directory of the drive named by the pathname, if any; if not, it is resolved against the current user directory.",
"required" => false,
"min-length" => 1
},
"relative-to" => {
"type" => STRING,
"description" => "The name of another previously named path, or of one of the standard paths provided by the system. If 'relative-to' is provided, the value of the 'path' attribute is treated as relative to the path specified by this attribute. The standard paths provided by the system include:<ul><li>jboss.home - the root directory of the JBoss AS distribution</li><li>user.home - user's home directory</li><li>user.dir - user's current working directory</li><li>java.home - java installation directory</li><li>jboss.server.base.dir - root directory for an individual server instance</li><li>jboss.server.data.dir - directory the server will use for persistent data file storage</li><li>jboss.server.log.dir - directory the server will use for log file storage</li><li>jboss.server.tmp.dir - directory the server will use for temporary file storage</li><li>jboss.domain.servers.dir - directory under which a host controller will create the working area for individual server instances</li></ul>",
"required" => false
}
},
"operations" => {
"add" => {
"operation-name" => "add",
"description" => "Add a new 'path' child",
"request-properties" => {
"name" => {
"type" => STRING,
"description" => "The value of the path's 'name' attribute",
"required" => true,
"min-length" => 1,
"nillable" => false
},
"path" => {
"type" => STRING,
"description" => "The value of the path's 'path' attribute",
"required" => false,
"min-length" => 1,
"nillable" => true
},
"relative-to" => {
"type" => STRING,
"description" => "The value of the path's 'relative-to' attribute",
"required" => false,
"min-length" => 1,
"nillable" => true
}
},
"reply-properties" => {}
},
"remove" => {
"operation-name" => "remove",
"description" => "Remove a 'path' child",
"request-properties" => {"name" => {
"type" => STRING,
"description" => "The value of the path's 'name' attribute",
"required" => true,
"min-length" => 1,
"nillable" => false
}},
"reply-properties" => {}
},
"setPath" => {
"operation-name" => "setPath",
"description" => "Set the value of the 'path' attribute",
"request-properties" => {"path" => {
"type" => STRING,
"description" => "The new value of the 'path' attribute",
"required" => true,
"min-length" => 1,
"nillable" => true
}},
"reply-properties" => {}
},
"setRelativeTo" => {
"operation-name" => "setRelativeTo",
"description" => "Set the value of the 'relative-to' attribute",
"request-properties" => {"relative-to" => {
"type" => STRING,
"description" => "The new value of the 'relative-to' attribute",
"required" => true,
"nillable" => true
}},
"reply-properties" => {}
}
}
}
},
"profile" => {
"description" => "A list of profiles available for use in the domain",
"min-occurs" => 1,
"model-description" => {
"description" => "A named set of subsystem configurations.",
"attributes" => {"name" => {
"type" => STRING,
"description" => "The name of the profile",
"required" => true,
"min-length" => 1
}},
"operations" => {},
"children" => {
"subsystem" => {
"description" => "The subsystems that make up the profile.",
"min-occurs" => 1,
"model-description" => {}
},
"include" => {"model-description" => {
"description" => "Specifies that a contents of a named profile are to be included in the profile whose definition includes this item.",
"head-comment-allowed" => true,
"tail-comment-allowed" => false,
"attributes" => {"profile" => {
"type" => LIST,
"description" => "The name of the included profile",
"required" => true,
"value-type" => STRING
}},
"operations" => "TODO"
}}
}
}
},
"interface" => {
"description" => "A list of named network interfaces available for use in the domain. The interfaces may or may not be fully specified (i.e. include criteria on how to determine their IP address.",
"min-occurs" => 0,
"model-description" => "TODO"
},
"socket-binding-group" => {
"description" => "A list of socket binding groups available for use in the domain",
"min-occurs" => 0,
"model-description" => "TODO"
},
"system-property" => {
"description" => "A list of system properties to set on all servers in the domain.",
"min-occurs" => 0,
"model-description" => "TODO"
},
"deployment" => {
"description" => "A list of deployments available for use in the domain",
"min-occurs" => 0,
"model-description" => "TODO"
},
"server-group" => {
"description" => "A list of server groups available for use in the domain",
"min-occurs" => 0,
"model-description" => "TODO"
},
"host" => {
"description" => "Host controllers currently running in the domain",
"min-occurs" => 0,
"model-description" => "TODO"
}
}
}
h2. Example Representation of a Portion of the Host Controller API
{
"description" => "The root node of the host-level management model.",
"attributes" => {
"namespaces" => {
"type" => OBJECT,
"value-type" => STRING,
"description" => "Map of namespaces used in the configuration XML document, where keys are namespace prefixes and values are schema URIs.",
"required" => false
},
"schema-locations" => {
"type" => OBJECT,
"value-type" => STRING,
"description" => "Map of locations of XML schemas used in the configuration XML document, where keys are schema URIs and values are locations where the schema can be found.",
"required" => false
},
"management" => {
"description" => "Configuration of the host's management system.",
"type" => OBJECT,
"value-type" => {
"interface" => {
"type" => STRING,
"description" => "Interface on which the host's socket for intra-domain management communication should be opened.",
"required" => false
},
"port" => {
"type" => STRING,
"description" => "Port on which the host's socket for intra-domain management communication should be opened.",
"required" => false
}
},
"required" => true,
"head-comment-allowed" => true
},
"domain-controller" => {
"description" => "Configuration of how the host should interact with the Domain Controller",
"type" => OBJECT,
"value-type" => "TODO",
"required" => true,
"head-comment-allowed" => true,
"tail-comment-allowed" => true
}
},
"operations" => {
"start-server" => {
"operation-name" => "start-server",
"description" => "Start a server.",
"request-properties" => {"server" => {
"type" => STRING,
"description" => "The name of the server.",
"required" => true,
"min-length" => 1
}},
"reply-properties" => {
"type" => STRING,
"description" => "The status of the server following execution of this operation."
}
},
"restart-server" => {
"operation-name" => "restart-server",
"description" => "Restart a currently running server.",
"request-properties" => {"server" => {
"type" => STRING,
"description" => "The name of the server.",
"required" => true,
"min-length" => 1
}},
"reply-properties" => {
"type" => STRING,
"description" => "The status of the server following execution of this operation."
}
},
"stop-server" => {
"operation-name" => "stop-server",
"description" => "Stop a currently running server.",
"request-properties" => {"server" => {
"type" => STRING,
"description" => "The name of the server.",
"required" => true,
"min-length" => 1
}},
"reply-properties" => {
"type" => STRING,
"description" => "The status of the server following execution of this operation."
}
}
},
"children" => {
"extension" => {
"description" => "A list of extension modules.",
"min-occurs" => 0,
"model-description" => "TODO"
},
"path" => {
"description" => "A list of named filesystem paths.",
"min-occurs" => 0,
"model-description" => {
"description" => "A named filesystem path, but without a requirement to specify the actual path. If no actual path is specified, acts as a placeholder in the model (e.g. at the domain level) until a fully specified path definition is applied at a lower level (e.g. at the host level, where available addresses are known.)",
"tail-comment-allowed" => false,
"attributes" => {
"name" => {
"type" => STRING,
"description" => "The name of the path. Cannot be one of the standard fixed paths provided by the system: <ul><li>jboss.home - the root directory of the JBoss AS distribution</li><li>user.home - user's home directory</li><li>user.dir - user's current working directory</li><li>java.home - java installation directory</li><li>jboss.server.base.dir - root directory for an individual server instance</li></ul> Note that the system provides other standard paths that can be overridden by declaring them in the configuration file. See the 'relative-to' attribute documentation for a complete list of standard paths.",
"required" => true
},
"path" => {
"type" => STRING,
"description" => "The actual filesystem path. Treated as an absolute path, unless the 'relative-to' attribute is specified, in which case the value is treated as relative to that path. <p>If treated as an absolute path, the actual runtime pathname specified by the value of this attribute will be determined as follows: </p>If this value is already absolute, then the value is directly used. Otherwise the runtime pathname is resolved in a system-dependent way. On UNIX systems, a relative pathname is made absolute by resolving it against the current user directory. On Microsoft Windows systems, a relative pathname is made absolute by resolving it against the current directory of the drive named by the pathname, if any; if not, it is resolved against the current user directory.",
"required" => true,
"min-length" => 1
},
"relative-to" => {
"type" => STRING,
"description" => "The name of another previously named path, or of one of the standard paths provided by the system. If 'relative-to' is provided, the value of the 'path' attribute is treated as relative to the path specified by this attribute. The standard paths provided by the system include:<ul><li>jboss.home - the root directory of the JBoss AS distribution</li><li>user.home - user's home directory</li><li>user.dir - user's current working directory</li><li>java.home - java installation directory</li><li>jboss.server.base.dir - root directory for an individual server instance</li><li>jboss.server.data.dir - directory the server will use for persistent data file storage</li><li>jboss.server.log.dir - directory the server will use for log file storage</li><li>jboss.server.tmp.dir - directory the server will use for temporary file storage</li><li>jboss.domain.servers.dir - directory under which a host controller will create the working area for individual server instances</li></ul>",
"required" => false
}
},
"operations" => {
"add" => {
"operation-name" => "add",
"description" => "Add a new 'path' child",
"request-properties" => {
"name" => {
"type" => STRING,
"description" => "The value of the path's 'name' attribute",
"required" => true,
"min-length" => 1
},
"path" => {
"type" => STRING,
"description" => "The value of the path's 'path' attribute",
"required" => true,
"min-length" => 1
},
"relative-to" => {
"type" => STRING,
"description" => "The value of the path's 'relative-to' attribute",
"required" => false,
"min-length" => 1,
"nillable" => true
}
},
"reply-properties" => {}
},
"remove" => {
"operation-name" => "remove",
"description" => "Remove a 'path' child",
"request-properties" => {"name" => {
"type" => STRING,
"description" => "The value of the path's 'name' attribute",
"required" => true,
"min-length" => 1
}},
"reply-properties" => {}
},
"setPath" => {
"operation-name" => "setPath",
"description" => "Set the value of the 'path' attribute",
"request-properties" => {"path" => {
"type" => STRING,
"description" => "The new value of the 'path' attribute",
"required" => true,
"min-length" => 1
}},
"reply-properties" => {}
},
"setRelativeTo" => {
"operation-name" => "setRelativeTo",
"description" => "Set the value of the 'relative-to' attribute",
"request-properties" => {"relative-to" => {
"type" => STRING,
"description" => "The new value of the 'relative-to' attribute",
"required" => true,
"nillable" => true
}},
"reply-properties" => {}
}
}
}
},
"system-property" => {
"description" => "A list of system properties to set on all servers on the host.",
"min-occurs" => 0
"model-description" => "TODO"
},
"interface" => {
"description" => "A list of fully specified named network interfaces available for use on the host.",
"min-occurs" => 0
"model-description" => "TODO"
},
"jvm" => {
"description" => "A list of Java Virtual Machine configurations that can be applied ot servers on the host.",
"min-occurs" => 0
"model-description" => "TODO"
},
"server-config" => {
"description" => "Host-level configurations for the servers that can run on this host.",
"min-occurs" => 0
"model-description" => {}
},
"server" => {
"description" => "Servers currently running on the host",
"min-occurs" => 0
"model-description" => "TODO"
}
}
}
--------------------------------------------------------------
Comment by going to Community
[http://community.jboss.org/docs/DOC-16317]
Create a new document in JBoss AS7 Development at Community
[http://community.jboss.org/choose-container!input.jspa?contentType=102&co...]
13 years, 8 months
[JBoss AS7 Development] - Data Source Configuration in AS 7
by David Lloyd
David Lloyd [http://community.jboss.org/people/dmlloyd] modified the document:
"Data Source Configuration in AS 7"
To view the document, visit: http://community.jboss.org/docs/DOC-16657
--------------------------------------------------------------
**
#Using_DataSourceDefinition_to_configure_a_DataSource Using @DataSourceDefinition to configure a DataSource
**
#Defining_a_Managed_DataSource Defining a Managed DataSource
***
#Installing_a_JDBC_driver_as_a_deployment Installing a JDBC driver as a deployment
****
#Modify_the_JAR Modify the JAR
****
#Deploy_the_JAR_with_an_overlay Deploy the JAR with an overlay
***
#Installing_a_JDBC_driver_as_a_module Installing a JDBC driver as a module
In older versions of the application server, data source configuration was tied to a *-ds.xml file schema that you would deploy in the deploy directory of your configuration. In AS 7, the entire structure of the AS is different, and as you would expect, creating your own data sources is different as well.
h2. Using @DataSourceDefinition to configure a DataSource
Since Java EE6, the new annotation "@DataSourceDefinition" has existed to allow users to configure a data source directly from within their application. (Note that this annotation bypasses the management layer and as such it is recommended only for development and testing purposes.) This annotation requires that a data source implementation class (generally from a JDBC driver JAR) be present on the class path (either by including it in your application, or deploying it as a top-level JAR and referring to it via MANIFEST.MF's Class-Path attribute) and be named explicitly.
h2. Defining a Managed DataSource
In order for a data source to be managed by the application server (and thus take advantage of the management and connection pooling facilities it provides), you must perform two tasks. First, you must make the JDBC driver available to the application server; then you can configure the data source itself. Once you have performed these tasks you can use the data source via standard JNDI injection.
h3. Installing a JDBC driver as a deployment
The recommended way to install a JDBC driver into the application server is to simply deploy it as a regular JAR deployment. The reason for this is that when you run your application server in domain mode, deployments are automatically propagated to all servers to which the deployment applies; thus distribution of the driver JAR is one less thing for administrators to worry about.
Any JDBC 4-compliant driver will automatically be recognized and installed into the system by name and version. A JDBC JAR is identified using the Java service provider mechaism. Such JARs will contain a text a file named "META-INF/services/java.sql.Driver", which contains the name of the class(es) of the Drivers which exist in that JAR. If your JDBC driver JAR is not JDBC 4-compliant, it can be made deployable in one of a few ways.
h4. Modify the JAR
The most straightforward solution is to simply modify the JAR and add the missing file. You can do this from your command shell by:
1. Change to, or create, an empty temporary directory.
2. Create a "META-INF" subdirectory.
3. Create a "META-INF/services" subdirectory.
4. Create a "META-INF/services/java.sql.Driver" file which contains one line - the fully-qualified class name of the JDBC driver.
5. Use the "jar" command-line tool to update the JAR like this:
jar -uf jdbc-driver.jar META-INF/services/java.sql.Driver
h4. Deploy the JAR with an overlay
(TODO)
h3. Installing a JDBC driver as a module
Under the root directory of the application server, is a directory called modules (e.g. jboss-7.0.0.<release>/modules). In this example, I will create the MySQL module in the same tree as the H2 database. The H2 database, which comes preconfigured, like the old DefaultDS with Hypersonic, is under the com/h2database/h2 directory, under the modules directory. So, the first step is to create a directory structure simlar to that for MySQL. I created, under com, a mysql directory, plus a main directory. So, at this point it should like like the following:
jboss-7.0.0.<release>/modules/com/mysql/main
Under the main directory, you need to define your module with a module.xml file, and the actual jar file that contains your database driver. In my case, the mysql-connector-java-5.1.15.jar file. This is in contrast to putting the database driver jar file in the old lib directory under your configuration where you deployed your *-ds.xml file. Also, the jar file must have a META-INF/services/java.sql.Driver file. This is due to the way AS 7 will load the driver. Fortunately, the MySQL JDBC driver jar file has this. So, what's the content of the module.xml file.
It's fairly straightforward, and is as follows:
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ JBoss, Home of Professional Open Source.
~ Copyright 2010, Red Hat, Inc., and individual contributors
~ as indicated by the @author tags. See the copyright.txt file in the
~ distribution for a full listing of individual contributors.
~
~ This is free software; you can redistribute it and/or modify it
~ under the terms of the GNU Lesser General Public License as
~ published by the Free Software Foundation; either version 2.1 of
~ the License, or (at your option) any later version.
~
~ This software is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
~ Lesser General Public License for more details.
~
~ You should have received a copy of the GNU Lesser General Public
~ License along with this software; if not, write to the Free
~ Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
~ 02110-1301 USA, or see the FSF site: http://www.fsf.org.
-->
<module xmlns="urn:jboss:module:1.0" name="com.mysql">
<resources>
<resource-root path="mysql-connector-java-5.1.15.jar"/>
</resources>
<dependencies>
<module name="javax.api"/>
</dependencies>
</module>
As you can see from above, we give the module name, which in this example is com.mysql, which matches the directory structure we had created under the modules directory. If we had followed exactly the way the H2 database module was created we would have created something like com/mysqldatabase/mysql, and in that case the module name would have been com.mysqldatabase.mysql. I chose a shorted name in this case, but its just a matter of personal preference.
Besides the module name, we need to tell it where the implementation is, which is the resource-root tag with the path element. In that path element, we simply put the jar name. The path appears to be relative, and default to the main directory under the directory structure you created, which of course is com/mysql in our case.
Finally, you define any dependencies you might have. In this case, as the case with all JDBC data sources, we would be dependent on the Java JDBC API's, which in this case in defined in another module called javax.api, which you can find under modules/javax/api/main as you would expect.
That's really all there is to creating the module, but it will not be started as a service by AS 7, unless its referenced in the configuration. Now, just like everything else in AS 7, configuration is now completely different. There are two main configurations.
(TODO: let's put the driver config stuff first, then put the data source config in a separate Header 2 section...)
The first configuration is called domain, and has a domain directory under the root directory of the AS 7 distribution. This is a configuration that is geared toward multiple server instances and multiple server installations. The second is standalone, which is geared for a single instance of the server running on a single server, as you would expect. In regards to data source configuration, there really is no difference, as the datasource schema definition is the same in both cases. So regardless of which one you may be using in your particular case, the configuration is the same. For reference you can see the data source information here:
http://docs.jboss.org/ironjacamar/userguide/1.0/en-US/html/deployment.htm...
In either standalone.xml or domain.xml you add the reference to the MySQL module as follows:
<datasource jndi-name="java:/MySqlDS" pool-name="MySqlDS" enabled="true" use-java-context="true">
<connection-url>
jdbc:mysql://localhost:3306/EJB3
</connection-url>
<driver-class>
com.mysql.jdbc.Driver
</driver-class>
* <module>*
* com.mysql.jdbc.Driver#5.1*
* </module>*
<transaction-isolation>
TRANSACTION_READ_COMMITTED
</transaction-isolation>
<pool>
<min-pool-size>
200
</min-pool-size>
<max-pool-size>
300
</max-pool-size>
<prefill>
true
</prefill>
<use-strict-min>
false
</use-strict-min>
</pool>
<security>
<user-name>
test
</user-name>
<password>
test
</password>
</security>
<validation>
<validate-on-match>
false
</validate-on-match>
<background-validation>
false
</background-validation>
<useFastFail>
false
</useFastFail>
</validation>
<statement>
<prepared-statement-cache-size>
100
</prepared-statement-cache-size>
<share-prepared-statements/>
</statement>
</datasource>
As you can see from above this looks remarkable similar to the old *-ds.xml file, and in fact, I carried over all the attributes I had in my old data source definition. The thing to point out here, is that this is within an outer tag called <datasources>, so obviously there can be more than one, just like before, but unlike before, they are all included in the one configuration file, either standalone.xml or domain.xml depending on which you are using. In each case the directory structure is as follows:
jboss-7.0.0.<release>/domain/configuration/domain.xml or
jboss-7.0.0.<release>/standalone/configuration/standalone.xml
The other important things to point out, besides the standard JDBC parameters, is the module tag above, and a new section called drivers which is within the "datasources" subsystem in the schema, but below the data sources themselves.
In the module tag above, you should specify the fully qualtified name of the JDBC driver class, appended with a pound sign (#), with the major and minor version number. In my example above its 5.1, since I am using the 5.1.15 driver from MySQL. It does not support the micro version number, so if you are tempted, as I was, to add it, it will cause your module to fail loading, which will then cascade to your application using this data source. So, then the drivers tag.
<drivers>
<driver module="com.mysql"/>
</drivers>
As you can see, what we put in this is the actual module name we defined in module.xml under jboss-7.0.0.<release>/modules/com/mysql/main/module.xml.
That's all there is to it. I have also attached a zip archive of the MySQL module that I created, plus my standalone.xml file, that you can use to make your own. If you want to create a MySQL module and data source, you can simply unzip the com.mysql.tar.gz under the modules directory of your AS 7 distribution, and cut and past the datasource tag in standalone.xml into your configuration, and of course change the database you are connecting to, plus the username and password, and other relevant parameters to suit your needs, and you will have a working MySQL data source.
--------------------------------------------------------------
Comment by going to Community
[http://community.jboss.org/docs/DOC-16657]
Create a new document in JBoss AS7 Development at Community
[http://community.jboss.org/choose-container!input.jspa?contentType=102&co...]
13 years, 8 months
[JBoss AS7 Development] - Writing a AS7 Test Case in testsuite module
by Anil Saldhana
Anil Saldhana [http://community.jboss.org/people/anil.saldhana] created the document:
"Writing a AS7 Test Case in testsuite module"
To view the document, visit: http://community.jboss.org/docs/DOC-16664
--------------------------------------------------------------
h2. Background
h2.
I wanted to write a test case for web security using FORM authentication in the AS7 testsuite. I chose the testsuite/integration submodule.
h2.
h2. Requirements
* I wanted to be able to run the test case in Eclipse.
* I wanted to use JPDA to set breakpoints in the AS7 codebase.
h2.
h2. Choices of Technologies
* Arquilian
* Shrinkwrap
* JUnit
* Eclipse
*Note*: Both Arquilian and Shrinkwrap are available in the AS7 development environment by default.
Test Case
/*
* JBoss, Home of Professional Open Source.
* Copyright (c) 2011, Red Hat, Inc., and individual contributors
<snip> Copyright header
*/
package org.jboss.as.testsuite.integration.websecurity;
import static org.junit.Assert.assertEquals;
import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.StatusLine;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.cookie.Cookie;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.jboss.arquillian.api.Deployment;
import org.jboss.arquillian.api.Run;
import org.jboss.arquillian.api.RunModeType;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.junit.Test;
import org.junit.runner.RunWith;
/**
* Unit Test web security
*
* @author Anil Saldhana
*/
@RunWith(Arquillian.class)
@Run(RunModeType.AS_CLIENT)
public class WebSecurityFORMTestCase {
private static final String URL = "http://localhost:8080/web-secure/secured/";
@Deployment
public static WebArchive deployment() {
ClassLoader tccl = Thread.currentThread().getContextClassLoader();
URL webxml = tccl.getResource("web-secure.war/web.xml");
WebArchive war = ShrinkWrap.create(WebArchive.class, "web-secure.war");
war.addClass(SecuredServlet.class);
File userProp = new File(tccl.getResource("web-secure.war/users.properties").getPath());
File roleProp = new File(tccl.getResource("web-secure.war/roles.properties").getPath());
File loginJSP = new File(tccl.getResource("web-secure.war/login.jsp").getPath());
File errorJSP = new File(tccl.getResource("web-secure.war/error.jsp").getPath());
war.addResource(loginJSP);
war.addResource(errorJSP);
war.addResource(userProp, "/WEB-INF/classes/users.properties");
war.addResource(roleProp, "/WEB-INF/classes/roles.properties");
war.setWebXML(webxml);
return war;
}
/**
* Test with user "anil" who has the right password and the right role to access the servlet
*
* @throws Exception
*/
@Test
public void testSuccessfulAuth() throws Exception {
makeCall("anil", "anil", 200);
}
/**
* <p>
* Test with user "marcus" who has the right password but does not have the right role
* </p>
* <p>
* Should be a HTTP/403
* </p>
*
* @throws Exception
*/
@Test
public void testUnsuccessfulAuth() throws Exception {
makeCall("marcus", "marcus", 403);
}
protected void makeCall(String user, String pass, int expectedStatusCode) throws Exception {
DefaultHttpClient httpclient = new DefaultHttpClient();
try {
HttpGet httpget = new HttpGet(URL);
HttpResponse response = httpclient.execute(httpget);
HttpEntity entity = response.getEntity();
if (entity != null)
entity.consumeContent();
// We should get the Login Page
StatusLine statusLine = response.getStatusLine();
System.out.println("Login form get: " + statusLine);
assertEquals(200, statusLine.getStatusCode());
System.out.println("Initial set of cookies:");
List<Cookie> cookies = httpclient.getCookieStore().getCookies();
if (cookies.isEmpty()) {
System.out.println("None");
} else {
for (int i = 0; i < cookies.size(); i++) {
System.out.println("- " + cookies.get(i).toString());
}
}
// We should now login with the user name and password
HttpPost httpost = new HttpPost(URL + "/j_security_check");
List<NameValuePair> nvps = new ArrayList<NameValuePair>();
nvps.add(new BasicNameValuePair("j_username", user));
nvps.add(new BasicNameValuePair("j_password", pass));
httpost.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
response = httpclient.execute(httpost);
entity = response.getEntity();
if (entity != null)
entity.consumeContent();
statusLine = response.getStatusLine();
// Post authentication - we have a 302
assertEquals(302, statusLine.getStatusCode());
Header locationHeader = response.getFirstHeader("Location");
String location = locationHeader.getValue();
HttpGet httpGet = new HttpGet(location);
response = httpclient.execute(httpGet);
entity = response.getEntity();
if (entity != null)
entity.consumeContent();
System.out.println("Post logon cookies:");
cookies = httpclient.getCookieStore().getCookies();
if (cookies.isEmpty()) {
System.out.println("None");
} else {
for (int i = 0; i < cookies.size(); i++) {
System.out.println("- " + cookies.get(i).toString());
}
}
// Either the authentication passed or failed based on the expected status code
statusLine = response.getStatusLine();
assertEquals(expectedStatusCode, statusLine.getStatusCode());
} finally {
// When HttpClient instance is no longer needed,
// shut down the connection manager to ensure
// immediate deallocation of all system resources
httpclient.getConnectionManager().shutdown();
}
}
}
In this test case, I am specifying that the test case should run using Arquilian (via @RunWith) annotation. Plus I am telling Arquilian to run the test as a client test case and not inside AS7. [via @Run(RunModeType.AS_CLIENT) ] +If you want the test to run inside AS7, then you have to use IN_CONTAINER (which is default anyway).+
h2. Web Deployment
h2.
I need the following Items in my web archive
* Servlet
* web.xml
* Two properties files (users.properties and roles.properties)
Let us look at the servlet
/*
* JBoss, Home of Professional Open Source.
* Copyright (c) 2011, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
* <SNIP>
*/
package org.jboss.as.testsuite.integration.websecurity;
import java.io.IOException;
import java.io.Writer;
import javax.servlet.ServletException;
import javax.servlet.annotation.HttpConstraint;
import javax.servlet.annotation.ServletSecurity;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* A simple servlet that just writes back a string
*
* @author Anil Saldhana
*/
@WebServlet(urlPatterns = { "/secured/" })
@ServletSecurity(@HttpConstraint(rolesAllowed = { "gooduser" }))
public class SecuredServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Writer writer = resp.getWriter();
writer.write("GOOD");
}
}
My test resources were in a folder as
web-secure.war/
login.jsp
error.jsp
users.properties
roles.properties
web.xml
h2.
h2. Where should I look for the source code?
https://github.com/anilsaldhana/jboss-as/commit/0efdc673a45d6d17255918324...
https://github.com/anilsaldhana/jboss-as/commit/0efdc673a45d6d17255918324... https://github.com/anilsaldhana/jboss-as/commit/0efdc673a45d6d17255918324...
h2.
h2.
h2. Show me the test in action, buddy
anil@localhost:~/as7/jboss-as/testsuite/integration$ mvn clean install
Running org.jboss.as.testsuite.integration.websecurity.WebSecurityFORMTestCase
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.531 sec
--------------------------------------------------------------
Comment by going to Community
[http://community.jboss.org/docs/DOC-16664]
Create a new document in JBoss AS7 Development at Community
[http://community.jboss.org/choose-container!input.jspa?contentType=102&co...]
13 years, 8 months