3. Example: Scripting Resource Deployments (JBoss EAP 5)

A common use case for management tools is to automate deployments of new or existing applications. This example creates an easy script for basic management tasks:
  1. Find all JBoss EAP instances for a specified JBoss ON group.
  2. Shut down each EAP instance.
  3. Update binaries for existing deployed applications or create new deployments.
  4. Restart the EAP instance.
  5. End the loop.

3.1. Declaring Custom Functions

This script will use two custom functions to deploy the packages to create new resources.
function usage() {
	println("Usage: deployToGroup <fileName> <groupName>");
	throw "Illegal arguments";
}

function PackageParser(fullPathName) {
	var file = new java.io.File(fullPathName);
		
	var fileName = file.getName();
	var packageType = fileName.substring(fileName.lastIndexOf('.')+1);
	var tmp = fileName.substring(0, fileName.lastIndexOf('.'));
	var realName = tmp.substring(0, tmp.lastIndexOf('-'));
	var version = tmp.substring(tmp.lastIndexOf('-') + 1);			
	var packageName = realName + "." + packageType;
	
	this.packageType = packageType.toLowerCase();
	this.packageName = packageName;
	this.version     = version;
	this.realName    = realName;
}

3.2. Checking the JBoss ON Groups and Inventory

The script should have two command-line parameters. The first should be the path of the new application that is installed in the group. The second is the name of the group itself. These parameters are parsed in the script (as described in more detail in Section 5.3, “Passing Command and Script Arguments”).
For example:
if( args.length < 2 ) usage();
				
var fileName = args[0];
var groupName = args[1];
Next, check if the path is valid and if the current user can read it. This is done by using Java classes as shown here:
// check that the file exists and that we can read it
var file = new java.io.File(fileName);
				
if( !file.exists() ) {
    println(fileName + " does not exist!");
    usage();
}
				
if( !file.canRead() ) {
    println(fileName + " can't be read!");
    usage();
}
Verify that the group really exists on the JBoss ON server:
// find resource group
var rgc = new ResourceGroupCriteria();
rgc.addFilterName(groupName);
rgc.fetchExplicitResources(true);
var groupList = ResourceGroupManager.findResourceGroupsByCriteria(rgc);
The important part here is to fetch the resources.
rgc.fetchExplicitResources(true);
Check if there is a group found:
if( groupList == null || groupList.size() != 1 ) {
    println("Can't find a resource group named " + groupName);
    usage();
}
				
var group = groupList.get(0);
				
println("  Found group: " + group.name );
println("  Group ID   : " + group.id );
println("  Description: " + group.description);
After validating that there is a group with the specified name, check if the group contains explicit resources:
if( group.explicitResources == null || group.explicitResources.size() == 0 ) {
    println("  Group does not contain explicit resources --> exiting!" );
    usage();
}
var resourcesArray = group.explicitResources.toArray();
resourceArray now contains all resources which are part of the group. Next, check if there are JBoss AS 5 Server instances which need to be restarted before the application is deployed.
for( i in resourcesArray ) {
    var res = resourcesArray[i];
    var resType = res.resourceType.name;
    println("  Found resource " + res.name + " of type " + resType + " and ID " + res.id);
				
    if( resType != "JBossAS5 Server") {
        println("    ---> Resource not of required type. Exiting!");
        usage();
    }
				
    // get server resource to start/stop it and to redeploy application
    var server = ProxyFactory.getResource(res.id);
}
This requires a group with only JBoss AS 5 Server resource types as top level resources. Now server contains the JBoss AS 5 instance. This requires re-reading the server because it needs to be fully populated. Internally, the CLI is using simple JPA persistence, and it is necessary to not always fetch all dependent objects.
Next, traverse all the children of the server instance and find the resource name of the application:
var children = server.children;
for( c in children ) {
    var child = children[c];
				
    if( child.name == packageName ) {
    }
}
packageName is the name of the application without version information and path as shown in the JBoss ON GUI as deployed applications.
Create a backup of the original version of the application:
println("    download old app to /tmp");
child.retrieveBackingContent("/tmp/" + packageName + "_" + server.name + "_old");
A copy of the old application with the server name decoded in path is available in the /tmp/ directory.
Shut down the server and upload the new application content to the server.
println("    stopping " + server.name + "....");
try {
    server.shutDown();
}
catch( ex ) {
    println("   --> Caught " + ex );
}
				
				
println("    uploading new application code");
child.updateBackingContent(fileName);
				
println("    restarting " + server.name + "....." );
				
try {
    server.start();
}
catch( ex ) {
    println("   --> Caught " + ex );
}

3.3. Deploying the New Resource

At this point, existing application can be updated. The next step is to create the resource through the CLI and then deploy it to the JBoss server.
First, get the resource type for the application. This depends on several parameters:
  1. The type of the application (e.g., WAR or EAR)
  2. The type of the container the app needs to be deployed on (such as Tomcat or JBoss AS 5)

Note

All of the information about the resource type, such as the appType and appTypeName, is defined in the resource agent plug-in, in the rhq-plugin.xml descriptor. The attributes, configuration parameters, operations, and metrics for each default resource type are listed in the Resource Monitoring and Operations Reference.
For example:
var appType = ResourceTypeManager.getResourceTypeByNameAndPlugin( appTypeName, "JBossAS5" );
if( appType == null ) {
    println("  Could not find application type. Exit.");
    usage();
}
Then get the package type of the application.
var realPackageType = ContentManager.findPackageTypes( appTypeName, "JBossAS5" );
				
if( realPackageType == null ) {
    println("  Could not find JBoss ON's packageType. Exit.");
    usage();
}
Each resource in JBoss ON has some configuration parameters, including the WARs or EARs deployed on a JBoss AS 5 resource. As with the descriptive information, this is defined in the resource type's agent plug-in, in the rhq-plugin.xml descriptor. To be able to create a new resource, these parameters need to be filled in.
// create deployConfig 
var deployConfig = new Configuration();
deployConfig.put( new PropertySimple("deployExploded", "false"));
deployConfig.put( new PropertySimple("deployFarmed", "false"));
The property names can be retrieved by calling a list of supported properties by the package type by calling this method:
var deployConfigDef = ConfigurationManager.getPackageTypeConfigurationDefinition(realPackageType.getId());
Provide the package bits as a byte array:
var inputStream = new java.io.FileInputStream(file);
var fileLength = file.length();
var fileBytes = java.lang.reflect.Array.newInstance(java.lang.Byte.TYPE, fileLength);
for (numRead=0, offset=0; ((numRead >= 0) && (offset < fileBytes.length)); offset += numRead ) {
    numRead = inputStream.read(fileBytes, offset, fileBytes.length - offset); 	
}
Then, create the resource. The information is defined in the resource type's agent plug-in, in the rhq-plugin.xml descriptor. For example:
ResourceFactoryManager.createPackageBackedResource(
    server.id,
    appType.id,
    packageName,
    null,  // pluginConfiguration
    packageName,
    packageVersion,
    null, // architectureId        
    deployConfig,
    fileBytes,
    null // timeout
);
Make sure that the given JBoss AS 5 server instance is still running and that JBoss ON knows that it is running, or it will throw an exception saying that the JBoss ON agent is not able to upload the binary content to the server.