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 profile (name, country/region, and company) is displayed to the public and will accompany any content you post. You may update your IBM account at any time.

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]

developerWorks Community:

  • Close [x]

Build a RESTful service on CICS with PHP

Robin Fernandes (robin_fernandes@uk.ibm.com), Software Developer, IBM
Photo of Robin Fernandes
Robin Fernandes joined IBM's Java Technology Centre in Hursley, United Kingdom as Software Developer after graduating from Imperial College in 2003. His current focus is a Java-based runtime for PHP, which is used in the CA1S SupportPac and WebSphere sMash. He also regularly contributes test cases and patches to php.net and enjoys experimenting with audio software in his spare time.
Jonathan Lawrence (jlawrence@uk.ibm.com), Software Developer, IBM
Photo of Jonathan Lawrence
Jonathan Lawrence joined IBM's Java Technology Centre in Hursley, United Kingdom as a Software Developer in 2006, after 4 years in Hursley's Software Services department where he was a CICS and Cross-platform Integration Specialist. He designed the CICS integration aspects of the CA1S SupportPac.

Summary:  CICS® Transaction Server® (TS) is a powerful transaction manager designed for rapid, high-volume processing. SupportPac CA1S uses technology from IBM WebSphere® sMash to enhance CICS TS with PHP scripting capabilities and Representational state transfer (REST)-related features. This tutorial shows how you can use PHP to quickly and easily work with CICS programs and expose them on the Web. If you are a PHP developer, find out how you can use your skills to interact with enterprise assets in CICS; if you are a CICS developer, see how PHP provides a simple and agile way to manipulate your existing resources.

Date:  21 Apr 2009
Level:  Intermediate PDF:  A4 and Letter (409 KB | 37 pages)Get Adobe® Reader®

Activity:  12723 views
Comments:  

Putting it all together

So far you have:

  • Established how to interact with CICS COMMAREA programs from PHP
  • Learned some of CA1S's conventions and functions that help build RESTful Web services

We will now use this knowledge to expose the library program as a RESTful Web service.

RESTful design

The resource

The first step is to define the resources to which we intend to provide access. In this scenario, we have a single resource type, book, which has four properties:

book {
  string: title,   // The title of the book
  string: author,	 // The author of the book
  boolean: onLoan, // Whether the book is on loan
  string: borrower // The person who borrowed the book
                   // if it is on loan, null otherwise.
}

The methods

Next, let's briefly describe the intent and expected input/output data of each supported method:


Table 6. Requests on the collection URI (/ca1s/resources/book)
HTTP Method Handler method invoked in book.php Description
GET book::onList() Return the complete list of books and all their properties, encoded in JSON.
POST book::onCreate() Add a book to the library.
The request must supply the title and author properties of the new book, encoded in JSON. Returns a message indicating that the book was added.
PUT book::onPutCollection() Not supported
DELETE book::onDeleteCollection() Not supported


Table 7. Requests on a member URI (/ca1s/resources/book/10)
HTTP method Handler method invoked in book.php Description
GET book::onRetrieve() Return a single book and all its properties, encoded in JSON. If an individual property is requested (for example, book/10/title), return just that property, encoded in JSON.
POST book::onPostMember() Not supported
PUT book::onUpdate() Mark a book as borrowed or returned. The request must supply a JSON object containing the onLoan and borrower properties. Updating the author or title is not supported. Returns a message indicating that the book was added.
DELETE book::onDelete() Permanently remove a book from the library. Returns a message indicating that the book was delete.

Any error cases will result in the service returning the appropriate HTTP status code and an error message in a JSON object property named errorMessage. For example, attempting to retrieve a book with ID 909 when there is no such book will return an HTTP 404 response with the following body:

{"errorMessage":"Could not retrieve book 909 as it was not found."}
             


Implementation

Finally, let's implement the resource handler book.php based on the design above. The full script is included in the sample code package under resources/book.php.

Code common to all events

The script book.php contains a class book with LCRUD handler methods, but it also includes code outside of the class definition. This code will be executed for all requests, before the appropriate handler method is invoked.

In our scenario, it is useful to import the Java classes representing the COMMAREA and the program constants outside of the class definition, because they will most likely be used regardless of the event. We also set the response content-type to text/json using the PHP function header(), because we know that all requests will return data in JSON format:


Listing 7. Preamble code used by all request types


// Load the Java classes
java_import('library.Library_Commarea');
java_import('library.Library_Constants');
// All responses will be JSON, so always set response content-type to text/json.
header('Content-Type: text/json');
             

We also wrap the program link operation and its exception handling in a global function. This function will be available to all event handlers. Should the library program terminate abnormally, the execution of the script will be halted and an error message will be returned to the client.


Listing 8. Wrapper function for program link
/**
 * Invoke the library program with the supplied COMMAREA.
 * Notify the client if an error occurs.
 */
function runLibraryProgram($COMMAREA) {
  $program = new CICSProgram('LIBRARY');
  try {
    $program->link($COMMAREA);
  } catch (CICSException $e) {
    header('HTTP/1.1 500 Internal Server Error');
    $error['errorMessage'] = 
              'Exeption when linking with CICS program:' . $e->getMessage();
    echo json_encode($error);
    exit;
  }
}
             

Finally, the class constructor will also be invoked before any handler, so we can use it to set up properties that are likely to be used by all methods.


Listing 9. The book resource handler constructor
  /**
   * The constructor is invoked before event handlers.
   */
  function __construct() {
    $this->COMMAREA = new Library_Commarea();
    $this->constants = new Library_Constants();
  }
             

Next, let's take a look at the implementation of each of the event handlers.

onList

The onList() handler implements similar logic to the library.php script that we used to learn about calling COMMAREA programs from PHP: it invokes the library program with a LIST command, then iterates over the collection of books. However, rather than just printing the title and author, this version builds up an associative array representing the books, then serializes it to JSON. It also checks the return code of the library invocation and sends an error message to the client if something went wrong.


Listing 10. The onList handler
  /**
   * Respond with a JSON-encoded list of books.
   */
  function onList() {
    // Attempt to get book list
    $this->COMMAREA->setLibRequestType('LIST');
    runLibraryProgram($this->COMMAREA);

    // Process return code
    switch ($this->COMMAREA->getLibReturnCode()) {
      case $this->constants->getLibraryOk():
        // Return the list of books
        $books = array();
        for ($i = 0; $i<$this->COMMAREA->getLibItemCount(); $i++) {
          $CICSbook = $this->COMMAREA->getLibBookItem($i);
          $bookId = $CICSbook->getBookItemRef();
          $books[$bookId]['title'] = trim($CICSbook->getBookTitle());
          $books[$bookId]['author'] = trim($CICSbook->getBookAuthor());
          $books[$bookId]['onLoan'] = $CICSbook->isBookOnloan();
          $books[$bookId]['borrower'] = $books[$bookId]['onLoan'] ?
          trim($CICSbook->getBookBorrower()) : null;
        }
        echo json_encode($books);
        break;
      default:
        // Notify client of internal error
        header('HTTP/1.1 500 Internal Server Error');
        $error['errorMessage'] = 'Unexpected return code when listing books: ' 
                                        . $this->COMMAREA->getLibReturnCode();
        echo json_encode($error);
    }
  }

onCreate

The onCreate() handler includes an initial step to retrieve and verify the input data from the request, then follows a similar pattern to onList(). The PHP function header() (see Resources) is used to set the appropriate HTTP response code depending on the success of the request.


Listing 11. The onCreate handler
  /**
   * Add book to library based on JSON data in the request body.
   */
  function onCreate() {
    // Check input data for new book
    $book = json_decode(zget('/request/input/transcoded'), true);
    if (!is_array($book) || !isset($book['title'], $book['author'])) {
      header('HTTP/1.1 400 Bad Request');
      $error['errorMessage'] = 'Bad book data: ' . zget('/request/input/transcoded') 
                                        . '. Please specify an author and a title.';
      echo json_encode($error);
      return;
    }

    // Attempt to create book
    $this->COMMAREA->setLibRequestType('ADD');
    $this->COMMAREA->getLibBookItem(0)->setBookTitle($book['title']);
    $this->COMMAREA->getLibBookItem(0)->setBookAuthor($book['author']);
    runLibraryProgram($this->COMMAREA);

    // Process return code
    switch ($this->COMMAREA->getLibReturnCode()) {
      case $this->constants->getLibraryOk():
        header('HTTP/1.1 201 Created');
        $bookId = $this->COMMAREA->getLibBookItem(0)-> getBookItemRef();
        $status['statusMessage'] = "Successfully created book $bookId.";
        echo json_encode($status);
        break;
      case $this->constants->getLibraryFull():
        header('HTTP/1.1 400 Bad Request');
        $error['errorMessage'] = 'Could not create book : Library is full.';
        echo json_encode($error);
        break;
      default:
        header('HTTP/1.1 500 Internal Server Error');
        $error['errorMessage'] = 'Unexpected return code when creating book ' 
                                       . $this->COMMAREA->getLibReturnCode();
        echo json_encode($error);
        break;
    }
  }

onRetrieve

onRetrieve() is similar to onList(), but accesses a single book rather than the full list. Furthermore, it includes logic to access an individual property of a book for requests to sub-paths like book/10/title. The requested property is determined with zget('/event/pathInfo').


Listing 12. The onRetrieve handler
  /**
   * Respond with a JSON representation of an individual book, or a specific attribute
   * of a book.
   */
  function onRetrieve() {
    $bookId = zget('/request/params/bookId');
    $this->COMMAREA->setLibRequestType('QUERY');
    $this->COMMAREA->getLibBookItem(0)->setBookItemRef($bookId);
    runLibraryProgram($this->COMMAREA);

    // Process return code
    switch ($this->COMMAREA->getLibReturnCode()) {
      case $this->constants->getLibraryOk():
        $book['title'] = trim($this->COMMAREA->getLibBookItem(0)->getBookTitle());
        $book['author'] = trim($this->COMMAREA->getLibBookItem(0)->getBookAuthor());
        $book['onLoan'] = $this->COMMAREA->getLibBookItem(0)->isBookOnloan();
        $book['borrower'] = $book['onLoan'] ?
                trim($this->COMMAREA->getLibBookItem(0)->getBookBorrower()) : null;
        break;
      case $this->constants->getLibraryNotfound():
        header('HTTP/1.1 404 Not Found');
        $error['errorMessage'] = "Could not retrieve book $bookId as it was not found.";
        echo json_encode($error);
        return;
      default:
        header('HTTP/1.1 500 Internal Server Error');
        $error['errorMessage'] = "Unexpected return code deleting book $bookId: " 
                                           . $this->COMMAREA->getLibReturnCode();
        echo json_encode($error);
        return;
    }

    // Send back appropriate info about the book
    $requestedInfo = zget('/event/pathInfo');
    switch($requestedInfo) {
      case null:
        // return the whole book
        echo json_encode($book);
        break;
      case '/title':
      case '/author':
      case '/onLoan':
      case '/borrower':
        // return just the relevant info
        $requestedInfo = substr($requestedInfo, 1);
        echo json_encode(array($requestedInfo => $book[$requestedInfo]));
        break;
      default:
        header('HTTP/1.1 404 Not Found');
        $error['errorMessage'] = "Could not retrieve book detail $requestedInfo 
                          about book $bookId: don't know what $requestedInfo is.";
        echo json_encode($error);
    }
  }
               

onUpdate

The onUpdate() handler checks the correctness of the input data, which should contain properties onLoan and borrower. It then marks books as "borrowed" or "returned" by invoking the CICS program as appropriate. As with previous handlers, the program's return code is checked and error cases are processed as appropriate. Unlike OnRetrieve(), this handler doesn't support operations on individual property sub-paths (for example, book/10/onLoan); feel free to add this function if you'd like to experiment further!


Listing 13. The onUpdate handler
  /**
   * Update the status of a book to mark it as borrowed or returned.
   */
  function onUpdate() {
    // Check input data
    $bookId = zget('/request/params/bookId');
    $updateData = json_decode(zget('/request/input/transcoded'), true);
    if (!isset($updateData['onLoan'])) {
      header('HTTP/1.1 400 Bad Request');
      $error['errorMessage'] = 'Bad book update data: ' .
      zget('/request/input/transcoded') . '. Please specify onLoan.';
      echo json_encode($error);
      return;
    }
    if ($updateData['onLoan'] && empty($updateData['borrower'])) {
      header('HTTP/1.1 400 Bad Request');
      $error['errorMessage'] = 'Bad book update data: ' .
      zget('/request/input/transcoded') . '. Please specify a borrower.';
      echo json_encode($error);
      return;
    }

    // Attempt to update book status
    $this->COMMAREA->setLibRequestType($updateData['onLoan'] ? 'BORROW' : 'RETURN');
    $this->COMMAREA->getLibBookItem(0)->setBookItemRef($bookId);
    $this->COMMAREA->getLibBookItem(0)->setBookLoanStatus($updateData['onLoan']);
    if (isset($updateData['borrower'])) {
      $this->COMMAREA->getLibBookItem(0)->setBookBorrower($updateData['borrower']);
    }
    runLibraryProgram($this->COMMAREA);

    // Process return code
    switch ($this->COMMAREA->getLibReturnCode()) {
      case $this->constants->getLibraryOk():
        $status['statusMessage'] =  "Successfully updated status of book $bookId.";
        echo json_encode($status);
        break;
      case $this->constants->getLibraryNotfound():
        header('HTTP/1.1 404 Not Found');
        $error['errorMessage'] = "Could not update book $bookId as it was not found.";
        echo json_encode($error);
        break;
      default:
        header('HTTP/1.1 500 Internal Server Error');
        $error['errorMessage'] = 
        "Unexpected return code when updating status of book $bookId: " 
                                 . $this->COMMAREA->getLibReturnCode();
        echo json_encode($error);
    }
  }
             

onDelete

Lastly, the onDelete() handler invokes the CICS program to perform a 'DELETE' operation on the book ID supplied in the request URI.


Listing 14. The onDelete handler
  /**
   * Delete a book from the library.
   */
  function onDelete() {
    // Attempt to Delete book
    $bookId = zget('/request/params/bookId');
    $this->COMMAREA->setLibRequestType('DELETE');
    $this->COMMAREA->getLibBookItem(0)->setBookItemRef($bookId);
    runLibraryProgram($this->COMMAREA);

    // Process return code
    switch ($this->COMMAREA->getLibReturnCode()) {
      case $this->constants->getLibraryOk():
        $status['statusMessage'] =  "Successfully deleted book $bookId.";
        echo json_encode($status);
        break;
      case $this->constants->getLibraryNotfound():
        header('HTTP/1.1 404 Not Found');
        $error['errorMessage'] = "Could not delete book $bookId as it was not found.";
        echo json_encode($error);
        break;
      default:
        header('HTTP/1.1 500 Internal Server Error');
        $error['errorMessage'] = "Unexpected return code deleting book $bookId: " 
                                           . $this->COMMAREA->getLibReturnCode();
        echo json_encode($error);
    }
  }
             


Consuming the Web service

Now that the library application is exposed through a consistent interface and communicates using clearly defined JSON data structures, it can easily be accessed by a wide range of clients. The simplest way to test the service is with a lightweight REST client like the Poster add-on for Firefox (see Resources).


Figure 5. Testing your service with a simple REST client
The REST client is shown in action using the Poster add-on for                   Firefox.

In the sample code, we include an example HTML and Javascript page that issues Ajax requests to the service. You can try it out by copying library/scripts/ajaxLibrary.php to your CICS system under /ca1s/work/scripts/ajaxLibrary.php (and, if you haven't done so already, transfer resources/book.php to /ca1s/work/resources/book.php) then access it in your browser.


Figure 6. An Ajax front-end for your service
A screenshot of the Example Library application showing a columnar view of several sample books.

The library service could also be mashed-up with other services using a platform like WebSphere sMash.

5 of 9 | Previous | Next

Comments



static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Web development, Open source
ArticleID=383593
TutorialTitle=Build a RESTful service on CICS with PHP
publish-date=04212009
author1-email=robin_fernandes@uk.ibm.com
author1-email-cc=
author2-email=jlawrence@uk.ibm.com
author2-email-cc=