Pattern-type (Part 14): How to create a client using ServiceProvisioner for a shared service on IBM SmartCloud Application Workload Service

In this article, I explain how to create a simple client for the shared service created in the article Pattern-type (Part 13): How to create shared services for IBM SmartCloud Application Workload Service.
We can call APIs defined by a shared service either from an OSGI component or from python scripts. In this article I will demonstrate the OSGI component method.
We will use as base plugin, the Master-Slave plugin we developed along this education series.

1) Create a Service Provisioner to the ‘state’ API from the poolmanager in the createService and deleteService methods.
2) Update the vm topology to add a reference to the shared service.
3) Test

1) Create the Service Provisioner.

First, we will use the PDK wizard to create a new OSGi component in the plugin.
Right-client on the project, select ‘New’ -> ‘OSGI Service Component’.SCAWS SS PoolManager Client OSGI Create

The MANIFEST.MF has been updated with the new OSGI component, I set my imported-package as follow:

Import-Package:  com.ibm.json.java;version="1.0.1",
 com.ibm.maestro.common.http,
 com.ibm.maestro.common.services,
 com.ibm.maestro.common.utils,
 com.ibm.maestro.iaas,
 com.ibm.maestro.model.transform;version="1.0.0",
 com.ibm.maestro.model.transform.template,
 com.ibm.maestro.plugin.services,
 com.ibm.websphere.ras;version="1.0.0",
 com.ibm.websphere.ras.annotation;version="1.0.0",
 com.ibm.ws.ffdc;version="1.0.0",
 com.ibm.ws.security.utils;version="1.0.0",
 javax.ws.rs.core;version="1.0.1",
 org.apache.wink.client;version="1.1.0",
 org.osgi.framework,
 org.osgi.service.component;version="1.1.0"

In the java class, we will need to know the register service and thus we have to bind in the component the registry. For that we will modify the generated OSGI component xml by adding a reference.SCAWS SS PoolManager Client OSGI BindNow, we have to update the java class by adding the logic to retrieve the IP address of the shared service and call the ‘state’ API.

1.1) Add the binded methods:
In the java class add the following code:

	private static final Logger logger = Logger.getLogger(MasterSharedService.class.getCanonicalName());
	private static final String CLASS_NAME = MasterSharedService.class.getName();
	private RegistryService registrySvc = null;

	protected void setRegistryService(RegistryService svc) {
		this.registrySvc = svc;
	}

	protected void unsetRegistryService(RegistryService svc) {
		if (svc == this.registrySvc) {
			this.registrySvc = null;
		}
	}

The createService method will do the following steps:

1) Retrieve information in order to find the poolmanager IP address, this address has been stored in the registry while deploying the poolmanager (see Part 13)

2) Call the ‘state’ GET method of the RESTFul API to log the current state of the pool manger.
3) Call the ‘pool’ POST method of the RESTFul API to retrieve a resource from the poolmanager.

	public StateAndDescription createService(final String serviceReferenceName,
			JSONObject serviceDescription, RestClient restClient)
			throws Exception {
		// The deployment id and cloud group will be passed in automatically
		logger.info("createService() serviceDescription:"+serviceDescription); 
		String clientCloudGroup = (String) serviceDescription
				.get("cloud_group");
		String clientDeploymentId = (String) serviceDescription
				.get("deployment_id");

		// Retrieve the information from the vm topology
		JSONObject sharedserviceRef = (JSONObject) serviceDescription
				.get("sharedservice");
		String serviceName = (String) sharedserviceRef.get("name");
		String clientVersion = (String) sharedserviceRef.get("client-version");

		JSONObject registryInfo = (JSONObject) this.registrySvc.getRegistry(
				restClient, serviceName, clientCloudGroup, clientVersion,
				clientDeploymentId);
		String ip = (String) registryInfo.get("ip");
		logger.info("createService() ip:"+ip); 

		JSONObject body = new JSONObject();

		//Call the state API from the poolmanager, the body object is mandatory.
		OperationResponse response = this.registrySvc
				.callOperationOnSharedService(serviceName, clientCloudGroup,
						clientVersion, ip, "state", "GET", body);
		logger.info("createService() PoolManager State Response:"+response.getOperationResponse().toString()); 

		//Call the pool API from the poolmanager, the body object is mandatory.
		response = this.registrySvc
				.callOperationOnSharedService(serviceName, clientCloudGroup,
						clientVersion, ip, "pool", "POST", body);
		logger.info("createService() PoolManager POST Response:"+response.getOperationResponse().toString()); 

		return new StateAndDescription(State.AVAILABLE, serviceDescription);
	}

The deleteService is very similar, only few changes:
1) Retrieve information in order to find the poolmanager IP address, this address has been stored in the registry while deploying the poolmanager (see Part 13)

2) Call the ‘state’ GET method of the RESTFul API to log the current state of the pool manger.
3) Call the ‘pool’ DELETE method of the RESTFul API to release a resource to the poolmanager.

	public StateAndDescription deleteService(final String serviceReferenceName,
			JSONObject serviceDescription, RestClient restClient)
			throws Exception {
		// The deployment id and cloud group will be passed in automatically
		String clientCloudGroup = (String) serviceDescription
				.get("cloud_group");
		String clientDeploymentId = (String) serviceDescription
				.get("deployment_id");

		// Retrieve the information from the vm topology
		JSONObject sharedserviceRef = (JSONObject) serviceDescription
				.get("sharedservice");
		String serviceName = (String) sharedserviceRef.get("name");
		String clientVersion = (String) sharedserviceRef.get("client-version");

		JSONObject registryInfo = (JSONObject) this.registrySvc.getRegistry(
				restClient, serviceName, clientCloudGroup, clientVersion,
				clientDeploymentId);
		String ip = (String) registryInfo.get("ip");

		JSONObject body = new JSONObject();

		//Call the state API from the poolmanager, the body object is mandatory.
		OperationResponse response = this.registrySvc
				.callOperationOnSharedService(serviceName, clientCloudGroup,
						clientVersion, ip, "state", "GET", body);
		logger.info("deleteService() PoolManager State Response:"+response.getOperationResponse().toString()); 

		//Call the pool API from the poolmanager, the body object is mandatory.
		response = this.registrySvc
				          .callOperationOnSharedService(serviceName, clientCloudGroup,
						clientVersion, ip, "pool", "DELETE", body);
		                 logger.info("deleteService() PoolManager DELETE Response:"+response.getOperationResponse().toString()); 

		return new StateAndDescription(State.AVAILABLE, serviceDescription);
	}

You could create a private method to avoid code duplication.
2) Update the vm topology.

As in the OSGI component, we are searching the shared service name from the vm topology, we have to add a reference element in the template of our Master component.

...
     "service-templates": [{
        "sharedservice":{
            "client-version": "1.0",
            "name": "poolmanager",
            "required": true
        },
            "type": "masterServiceProvisioner", 
            "name": "master-service-provisioner"
    }],
...

3) Test

Now, we can build and test our new version of the master component. For that we will create a pattern that just contains the master component and we check that the poolmanager is running, if not we launch it and this because we mentioned in the master.vm topology that the poolmanager is required.

We can start the deployment of our pattern and quickly in the console log of the kernel service we will see the following lines.

[26/Feb/2013 03:53:10:737 -0500]  INFO createService() ip:170.225.101.31
[26/Feb/2013 03:53:11:417 -0500]  INFO createService() 
                  PoolManager State Response:
                      {"SharedService":"poolmanager",
                       "returnCode":200,
                       "errorMessage":null,
                       "OperationResults":[
                           {"return_value":"PoolSizeValue:20",
                            "roleNode":"SharedService-PoolManager.11361867560595.PoolManager",
                            "result":"SUCCESS"}
                       ]
                      }
[26/Feb/2013 03:53:12:006 -0500]  INFO createService() 
                  PoolManager POST Response:
                      {"SharedService":"poolmanager",
                       "returnCode":200,
                       "errorMessage":null,
                       "OperationResults":[
                           {"return_value":"PoolSizeValue:19",
                            "roleNode":"SharedService-PoolManager.11361867560595.PoolManager",
                            "result":"SUCCESS"}
                        ]
                       }

and when we delete the pattern, these lines are shown:

[26/Feb/2013 04:12:41:357 -0500]  INFO deleteService() 
                  PoolManager State Response:
                      {"SharedService":"poolmanager",
                       "returnCode":200,
                       "errorMessage":null,
                       "OperationResults":[
                           {"return_value":"PoolSizeValue:19",
                            "roleNode":"SharedService-PoolManager.11361867560595.PoolManager",
                            "result":"SUCCESS"}
                       ]
                      }
[26/Feb/2013 04:12:41:871 -0500]  INFO deleteService() 
                  PoolManager DELETE Response:
                      {"SharedService":"poolmanager",
                       "returnCode":200,
                       "errorMessage":null,
                       "OperationResults":[
                           {"return_value":"PoolSizeValue:20",
                            "roleNode":"SharedService-PoolManager.11361867560595.PoolManager",
                            "result":"SUCCESS"}
                       ]
                      }