Skip to main content

By clicking Submit, you agree to the developerWorks terms of use.

The first time you sign into developerWorks, a profile is created for you. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

All information submitted is secure.

  • Close [x]

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerworks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

By clicking Submit, you agree to the developerWorks terms of use.

All information submitted is secure.

  • Close [x]

Resource handlers for CICS assets in a dynamic scripting application

James O'Grady (jamograd@uk.ibm.com), Software Developer, IBM
Photo of James O'Grady
James O'Grady is a System Tester on the CICS TS team at the IBM Software Lab in Hursley, United Kingdom. He has 10 years of CICS experience and has worked for IBM for four years. His areas of expertise include CICS Web services, WS-Security, and dynamic scripting.
Adam Rice (adamrice@uk.ibm.com), Software Developer, IBM
Photo of Adam Rice
Adam Rice is a System Tester on the CICS TS team at the IBM Software Lab in Hursley, United Kingdom. His current focus is on system testing the CICS Dynamic Scripting Feature Pack.

Summary:  The CICS Dynamic Scripting Feature Pack and the JCICS API provide a platform for the agile development and deployment of CICS applications. This article will show you how to access the contents of a KSDS file in a RESTful manner using the JCICS API. A sample application provides logic that will let you access other CICS assets in a similar way.

Date:  27 Oct 2010
Level:  Intermediate
Also available in:   Portuguese

Activity:  3134 views
Comments:  

Introduction

The CICS Dynamic Scripting Feature Pack (hereafter called the Feature Pack) provides a platform for the agile development and deployment of modern Web applications. The runtime environment supports Java, PHP, and Groovy, enabling you to access existing CICS assets using the JCICS API.

Dynamic scripting uses the REST architectural style to access collections. Certain CICS resources that contain data may be used in this manner, including TSQs, TDQs, and files. This article shows you how to implement a resource handler to access the data contained in a KSDS file. The approach used here can be easily modified so that the underlying collection could be another file or a queue.

Before you start

This article assumes that you have previously installed the Feature Pack and are familiar with dynamic scripting applications. If this is not the case, you can Download the Feature Pack and install it, and visit the Project Zero Web site to become more familiar with dynamic scripting.

Before doing the exercises in this article, do the following steps:

  1. Download employeedemo.zip into the applications directory you created when installing the Feature Pack.
  2. Use the command jar -xf employeedemo.zip to unzip the file to create an application folder.
  3. Configure the application to run in your environment, changing the port and applid of the region where appropriate.
  4. Download and unzip the DEFKSDS.zip JCL file to define and populate the VSAM file that will be used by the application. You will need to customise this file in order to create the data set in the correct location.
  5. Define the file in the CICS region in which the application is to run. Ensure that it is given the name KSDS; and that the access attributes ADD, BROWSE, DELETE, READ, and UPDATE are all set to YES.

Resource handler overview

A resource handler is a script found in the app/resources folder of an application that is executed when the application receives an HTTP request. The four HTTP methods GET, PUT, POST, and DELETE map to one of five methods in the script. Each HTTP method can be used against a collection or an element of a collection. The URI specifies whether the method acts on an entire collection or on a member of that collection. If the URI ends with a key for an element of a collection, then the HTTP method acts on that element. If it ends with just the collection name, then the method acts on the collection. Table 1 below shows how the HTTP methods can be used on a collection and how they relate to the methods in the resource handler. This list is not an exhaustive, and is provided to illustrate the relationships for the methods implemented in the sample resource handler.

The example in this article has been written in a scripting language called Groovy, which was chosen because Java syntax is also valid Groovy syntax. Therefore the JCICS API can be called as if this were just another Java class. However, you can write a resource handler in PHP and access the JCICS API via the PHP-Java bridge.


Table 1. Mapping of HTTP methods and URIs to Groovy methods in the resource handler
HTTP MethodURIGroovy MethodDescription
GET/resources/messagesonList()Lists all members of the collection
POST/resources/messagesonCreate()Creates a new member
GET/resources/messages/1onRetrieve()Retrieves one member
PUT/resources/messages/1onUpdate()Updates a member
DELETE/resources/messages/1onDelete()Deletes a member

Resource handler example

The example used in this article is the zero.employee.demo application, which you can download from the Project Zero sample applications page. The original application used a Derby database to store a list of employees, whereas the modified version will use a KSDS file. The file will simulate an existing CICS asset to illustrate how easy it is to use them in new dynamic scripting applications. The file used contains 80-byte records, with the username as a 19 byte key. The remainder of the record contains first name, last name, and phone number.

Defining the record structure

The first section of the resource handler is a class definition that defines the structure of the records in the file, and it makes it easier to work with the data retrieved from the file.


Listing 1. Definition of the record structure in the file

class employee {
        private String username
        private String firstname
        private String lastname
        private String phonenumber
	
        public employee(String username, String firstname, String lastname,
                String phonenumber) {
		
                this.username = username
                this.firstname = firstname
                this.lastname = lastname
                this.phonenumber = phonenumber
        }
		
        public String getUsername(){
                return username
        }
	
        public String getFirstname(){
                return firstname
        }

        public String getLastname(){
	        return lastname
        }

        public String getPhonenumber(){
                return phonenumber
        }
}	

This definition consists of the fields that constitute a record, and a constructor that creates an object representation of each record. Additionally, there are some accessor methods that let you retrieve the attributes of the object.

Importing classes

The next step is to import the classes you will need to browse the file, plus a few other classes for convenience. Add the following import statements to the top of the script:


Listing 2. Imported Java classes in the resource handler

import com.ibm.cics.server.KSDS;
import com.ibm.cics.server.RecordHolder;
import com.ibm.cics.server.KeyHolder;
import com.ibm.cics.server.KeyedFileBrowse;
import com.ibm.cics.server.Task;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import com.ibm.cics.server.CicsConditionException;

Listing the employees

The first method to be implemented is onList(), which retrieves all records in the file, using the JCICS API to browse the file and read each record in turn. First, create an empty array called records to hold all of the records read from the file. Next, initiate a generic browse of the file, which returns a KeyedFileBrowse object that you can use to read records from the file. Once the browse object is obtained, you execute a loop to read each record in turn.

As each record is read, a ByteBuffer is used to extract the individual fields from the record. In order for this to work the method requires the offset and length of each field. As each field is extracted, it is decoded to Unicode and stored in a string. Once all of the fields are retrieved, an employee object is created and stored in the records array. Upon reaching end of the file, an end of file exception is thrown, which ends the execution of the loop.

The final three lines of the method use a view renderer to return the data to the output stream in the JavaScript Object Notation (JSON) format. The first of these lines sets the view renderer to the JSON type. The next line sets the object to be serialised to the output stream and the final line invokes the renderer:


Listing 3. The onList() method of the resource handler

def onList()
{
	def records = []
	KSDS KSDSFile  = new KSDS();
	KSDSFile.setName("KSDS");
  		
	String startKey = new String("");
	KeyedFileBrowse browse = KSDSFile.startGenericBrowse(startKey.getBytes(),19);
      
	RecordHolder record = new RecordHolder();
	KeyHolder keyHolder = new KeyHolder();
    
	Exception eof = null;
	while(eof == null){
		try {
			browse.next(record, keyHolder);
    		
			Charset charset = Charset.forName("IBM1047");
	
			// obtain each of the fields from within the file
			ByteBuffer bb = ByteBuffer.wrap(record.value,0,19)
			String username = charset.decode(bb).toString()
		
			bb = ByteBuffer.wrap(record.value,19,20)
			String firstname = charset.decode(bb).toString()
	
			bb = ByteBuffer.wrap(record.value,39,20)
			String lastname = charset.decode(bb).toString()
	
			bb = ByteBuffer.wrap(record.value,59,20)
			String phonenumber = charset.decode(bb).toString()		
			
			def employee = new employee(username, firstname, lastname,
						phonenumber)
    			records.add(employee)
    		} catch(Exception e){
    			eof = e;
		}
   	}   	  	
	request.view="JSON"
	request.json.output=records
	render()
}

Retrieving a single member

Retrieving just one member of the file is done by the onRetrieve() method. The first line of code obtains the key for the desired record from the URI. Next, a file object is created and the name given is the name used in the CICS resource definition. The format() method of the String class is used to pad the key with spaces to the required length.

Using the same method used previously a ByteBuffer is used to obtain the individual components of a record and store them in strings, ready for the employee object to be created.

Once a record has been successfully read and an employee object created, a view renderer is used as with the onList() method, to return the single employee object in JSON format.

Should the JCICS call to read a record fail for any reason, a CicsConditionException is thrown and passed, along with an error message, to a method to log the error. This method is discussed in more detail below. The error handling in this resource handler is a rudimentary example that illustrates the information available via the JCICS API to make debugging applications easier. After the error message has been processed an HTTP 500 Internal Server error response is returned to the caller to indicate that a problem occurred:


Listing 4. The onRetrieve() method of the resource handler

def onRetrieve()
{        // obtain the key passed in via the URI
        String rec = request.params.myKSDSId[]
        
        // create the file object
        KSDS KSDSFile  = new KSDS();
        KSDSFile.setName("KSDS");	
		
        String paddedKey = String.format("%-" + 19 + "s", rec)
		
        Charset charset = Charset.forName("IBM285");
        ByteBuffer bb = ByteBuffer.wrap(paddedKey.getBytes(),0,19)
        rec = charset.decode(bb).toString()
	
        // read the record from the file
        RecordHolder record = new RecordHolder();
        try {
                KSDSFile.read(paddedKey.toString().getBytes(),record);
	
                // obtain each of the fields from within the file	
                bb = ByteBuffer.wrap(record.value,0,19)
                String username = charset.decode(bb).toString()
	
                bb = ByteBuffer.wrap(record.value,19,20)
                String firstname = charset.decode(bb).toString()
	
                bb = ByteBuffer.wrap(record.value,39,20)
                String lastname = charset.decode(bb).toString()
	
                bb = ByteBuffer.wrap(record.value,59,20)
                String phonenumber = charset.decode(bb).toString()
	
                // create and return the employee object
                if(record != null) {
                        // Use ViewEngine JSON rendering
                        def employee = new employee(username.trim(), firstname.trim(),
                                       lastname.trim(), phonenumber.trim())
                        request.view = 'JSON'
                        request.json.output = employee
                        render()
                }
        } catch(CicsConditionException e){
                logError("ERROR in onRetrieve().", e)
                request.status = 500;
                request.view = 'JSON'
                render()
        }
}

Adding a new member

To add a new member to a collection, use the HTTP POST method on the collection URI, which causes the execution of the onCreate() method in the resource handler. This method uses the format() method in the String class to pad each of the fields to the required length with spaces. Then a StringBuilder is used to build up the record, before writing it to the file:


Listing 5. The onCreate() method of the resource handler

def onCreate()
{
 	// Convert entity to JSON object
	def record = zero.json.Json.decode(request.input[])
	
	String username = record.username;
	String firstname = record.firstname;
	String lastname = record.lastname;
	String phonenumber = record.phonenumber;
	
	username = String.format("%-" + 19 + "s", username)
	firstname = String.format("%-" + 20 + "s", firstname)
	lastname = String.format("%-" + 20 + "s", lastname)
	phonenumber = String.format("%-" + 20 + "s", phonenumber)
	
	def emp = new employee(username, firstname, lastname, phonenumber)
	
	// Use a string builder to build up the record
	StringBuilder recordBuilder = new StringBuilder(79);
	recordBuilder.insert(0, username);
	recordBuilder.insert(19, firstname);
	recordBuilder.insert(39, lastname);
	recordBuilder.insert(59, phonenumber);
		 	
	KSDS KSDSFile  = new KSDS();
        KSDSFile.setName("KSDS");
	try{		
		KSDSFile.write(recordBuilder.substring(0,19).getBytes(),
                               recordBuilder.toString().getBytes());
		request.status = 201;
		request.view = 'JSON'
		request.json.output = emp
		render()
	} catch(CicsConditionException e){
		logError("ERROR in onCreate().", e)
		request.status = 500;
		request.view = 'JSON'
		render()		
	}
}

Updating an existing member

To change an existing member, the key of the existing record is specified in the URI of the HTTP request using the PUT method. This causes the onUpdate() method of the resource handler to be executed. The key of the record is obtained in the same way as above in the onRetrieve() method. In the same way as a new record is created, a StringBuilder is used to build up the record that will be written to the file.

Whilst this method is largely similar to the onCreate() method there are some differences. The rewrite() method is used to write the record to the file, instead of the write() method used previously. This takes an extra parameter that is the key of the record being written as well as a byte array of the data itself. Additionally, before the rewrite() method can be used, the readForUpdate() method must be called to lock the record to allow it to be written to:


Listing 6. The onUpdate() method of the resource handler

def onUpdate() 
{
        // Convert entity to JSON object
	def record = zero.json.Json.decode(request.input[])
	String id = request.params.myKSDSId[]
	
	String username = record.username;
	String firstname = record.firstname;
	String lastname = record.lastname;
	String phonenumber = record.phonenumber;
	
	username = String.format("%-" + 19 + "s", username)
	firstname = String.format("%-" + 20 + "s", firstname)
	lastname = String.format("%-" + 20 + "s", lastname)
	phonenumber = String.format("%-" + 20 + "s", phonenumber)
	
	def emp = new employee(username, firstname, lastname, phonenumber)
	
	// Use a string builder to build up the record
	StringBuilder recordBuilder = new StringBuilder(79);	
	
	recordBuilder.insert(0, username);
	recordBuilder.insert(19, firstname);
	recordBuilder.insert(39, lastname);
	recordBuilder.insert(59, phonenumber);
	 	
	KSDS KSDSFile  = new KSDS();
	KSDSFile.setName("KSDS");
	try{
		// Must perform a read for update before doing a rewrite
		RecordHolder recordHolder = new RecordHolder();
		KSDSFile.readForUpdate(recordBuilder.substring(0,19).getBytes(),
                                       recordHolder);	
		
		// update the record in the file	
		KSDSFile.rewrite(recordBuilder.toString().getBytes());
		request.status = 201;
		request.view = 'JSON'
		request.json.output = emp
		render()
	} catch(CicsConditionException e){
		logError("ERROR in onUpdate().", e)
		request.status = 500;
		request.view = 'JSON'
		render()	
	}
}

Deleting a member

To delete a member from a collection, the HTTP DELETE method is used specifying the key of the record in the URI. This causes the onDelete() method in the resource handler to be executed. As in previous methods the key is obtained from the URI and then used in the delete() method on the KSDS file:


Listing 7. The onDelete() method of the resource handler

def onDelete()
{
	String rec = request.params.myKSDSId[]
	
	KSDS KSDSFile  = new KSDS();
        KSDSFile.setName("KSDS");	
	KSDSFile.delete(rec.getBytes());
	
	request.status = HttpURLConnection.HTTP_NO_CONTENT
}

In this article all five methods have been implemented to illustrate the logic required, however only the methods required for an application need to be implemented. For example, a dynamic scripting application may be developed simply as a way to view the contents of files and to deny users of the application a way of modifying them. Another reason for omitting implementations of certain methods is where they would be illogical or invalid. For example, if the collection being manipulated is a TSQ, the onDelete() method would not be able to delete individual members of the collection.

If the application receives an HTTP request that would cause an unimplemented method to be executed, the request receives an HTTP 405 Method not allowed response.

Error handling

The methods discussed previously used a method called logError() to report any errors that occurred during the JCICS calls. This method itself uses JCICS to obtain more information regarding the nature of the error.


Listing 8. The logError() method

def logError(String msg, CicsConditionException e)
{
	String tranID = Task.getTask().getTransactionName();
	int taskNo = Task.getTask().getTaskNumber(); 
	System.err.println(msg + " " + e.getMessage() + 
	". Resp 2 value: " + e.getRESP2() + " TranID: " + tranID + " Task No: " + taskNo);
}

This method forms an error message that is printed to the error log for the JVM, located in the application directory. The message reported contains some useful details about the error.


Listing 9. Example of an error message from a failed onRetrieve()

ERROR in onRetrieve(). CICS NOTFND Condition. Resp 2 value: 80 TranID: CPIH Task No: 4598

The CICS condition that was thrown as a result of the error reported is included along with the resp 2 value. Additional information includes the transaction ID and the task number from which the error originated.

Testing

The application can be tested by opening it in a Web browser. As it is based on the employee demo, the user interface remains largely unchanged. Interaction with the application takes place via the four buttons below the data grid. Attempting to add or update the data causes a form to be displayed to allow new data to be input. To verify that the file is being modified it must first be closed in CICS. Once it is closed it is possible to view the contents via ISPF:


Figure 1. The user interface to the application
Screen shot of the application in a Web browser

Conclusion

In this article you have written a resource handler that will provide RESTful access to a CICS resource, in this case a KSDS file. Using a similar approach, you can easily modify this resource handler to use other CICS resources, such as TSQs, TDQs, ESDS files, and RRDS files. As you investigate the various applications that can be created with the Dynamic Scripting Feature Pack, you will learn how powerful this functionality is and how quickly you can create a a Web interface to interact with existing CICS assets.



Downloads

DescriptionNameSizeDownload method
Code sampleemployeedemo.zip72 KBHTTP
Code sampleDEFKSDS.zip1 KBHTTP

Information about download methods


Resources

About the authors

Photo of James O'Grady

James O'Grady is a System Tester on the CICS TS team at the IBM Software Lab in Hursley, United Kingdom. He has 10 years of CICS experience and has worked for IBM for four years. His areas of expertise include CICS Web services, WS-Security, and dynamic scripting.

Photo of Adam Rice

Adam Rice is a System Tester on the CICS TS team at the IBM Software Lab in Hursley, United Kingdom. His current focus is on system testing the CICS Dynamic Scripting Feature Pack.

Report abuse help

Report abuse

Thank you. This entry has been flagged for moderator attention.


Report abuse help

Report abuse

Report abuse submission failed. Please try again later.


developerWorks: Sign in


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

Choose your display name

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


Rate this article

Comments

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=WebSphere
ArticleID=559729
ArticleTitle=Resource handlers for CICS assets in a dynamic scripting application
publish-date=10272010
author1-email=jamograd@uk.ibm.com
author1-email-cc=
author2-email=adamrice@uk.ibm.com
author2-email-cc=

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

For articles in technology zones (such as Java technology, Linux, Open source, XML), Popular tags shows the top tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), Popular tags shows the top tags for just that product zone.

For articles in technology zones (such as Java technology, Linux, Open source, XML), My tags shows your tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), My tags shows your tags for just that product zone.

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Try IBM PureSystems. No charge.

Special offers