This tutorial aims to demonstrate the reuse of traditional COBOL programs in a JVM JSP application. The tutorial will use Visual COBOL 2.1. Version 2.5 of the servlet API will be used. The tutorial will be created with Tomcat version 7 in mind though it will be possible to use it with any supported servlet container. To complete this application we will create a JVM COBOL project containing our traditional COBOL program. We will create an Eclipse Web Tool Platform (WTP) Dynamic Web project. This web project will call the COBOL program using the Micro Focus smart linkage facility. It will cover development and debugging of the application. We will create the JSP view and dispatch code in Java. The sole usage of COBOL will be in the original unchanged COBOL program.

There is an attached source code archive for this tutorial. You can acquire it here. You should download this and store it on your machine. The location it is stored at will be referred to as SRC_DIR in this tutorial.

First Time Setup

If this is your first time doing this tutorial you will need to setup your environment to run JVM COBOL JSP applications. You will need to install and setup Micro Focus Visual COBOL for Eclipse 2.1. You need to install your servlet container/Java Enterprise Edition server (Tomcat, JBoss, etc) and setup the Micro Focus JVM COBOL runtime to be accessible in that environment. Finally you will need to setup the Eclipse Web Tools Platform. Links to those steps are below:

Create Workspace

The first thing you need to do is create the workspace that will contain all the projects. Open Micro Focus Visual COBOL for Eclipse and pick an appropriate path as the workspace when prompted (the tutorial will refer to this location as WORKSPACE or just as 'the workspace'). Click Ok and Eclipse will create the workspace at that location automatically if it doesn't already exist.

We will be adding several projects to this workspace to create the application.

JVM COBOL Project

Firstly we will create a project to contain our procedural COBOL program and make it available for execution from a Java program. The steps to do this are as follows:

  1. Click File->New->Other.
  2. Expand the Micro Focus COBOL node and select COBOL JVM Project. Click Next.
  3. Set the Project name field to "CobolBook".
  4. Ensure Use default location is checked.
  5. Ensure that the JRE is set up to use a Java 6 VM.
  6. Click Finish.

This will create a blank JVM COBOL project which can contain our procedural COBOL program and be imported into our web application later. If it asks you to switch to the COBOL perspective then do so. To change perspectives later you can click on the appropriate option in the top right of the Eclipse main window (see the image below). In general any action that uses the CobolBook project will be expected to be executed in the COBOL perspective unless otherwise specified. The dynamic web project that comes later will expect actions to be executed in the Java EE perspective.

The Eclipse Perspective Bar

One aspect of Java it is necessary to adapt our program to is the Java packaging system. By default COBOL programs will be placed in the default root Java package. JVM servlet containers do not always allow root package classes to be loaded so we will need to set up a package for our program. For reference the Java package system is equivalent to the JVM COBOL namespace system. These two terms are interchangeable.

To do set up the package right click on src in the CobolBook project of the COBOL Explorer in the top left of Eclipse. Now select New->COBOL JVM Package. In the dialog insert the text "com.microfocus.book" into the Name text field. Click Finish to create the package. This will create a directory structure in our workspace in which we can store our COBOL program. If you have the source code attached to this project you can copy SRC_DIR/CobolBook/src/com/microfocus/book/book.cbl to the equivalent directory in WORKSPACE. You should also copy SRC_DIR/src/book-rec.cpy to the equivalent location in the workspace.

If you do not have the source archive you can create these files as follows. Right click on src in the CobolBook project in the COBOL Explorer tab and select New->COBOL Copybook. Ensure the Containing project text box contains "CobolBook/src". If not alter it so it does. Then in the New file name text box enter "book-rec.cpy". Click finish to create the copybook. Now replace the entire content of the opened file with the text in book-rec.cpy.

Similarly to create book.cbl right click on src/com/microfocus/book in the CobolBook project. Select New->COBOL Program. Ensure the Containing project text box contains "CobolBook/src" again. The Package text box should contain "com.microfocus.book". Enter "book.cbl" in the Name text box. Click finish to create the COBOL program. Now replace the entire content of the opened program with the text in book.cbl.

Putting the program in the right directory structure is only half the requirement to put it in the right package. You need to alter the program to specify the correct namespace as a qualified program name. Alternatively you can set a compiler directive either globally to the project or in the program using the $set syntax. For this tutorial we will set a global compiler directive. We have only one program so no need for program specific directives. This also allows us to avoid altering our COBOL program. In other situations it may make more sense to make your programs use different namespaces. In that case you should use program local directives or a qualified program name.

Regardless to set compiler directives open the project properties for CobolBook by right clicking on the CobolBook node and clicking Properties. Expand Micro Focus COBOL and choose Build Configuration. At the bottom there is a box which is labelled as Additional directives. Here we will enter our project global directives. Add the line "ilnamespace(com.microfocus.book)" to that text box. Click OK to save the new directives. This will now generate a COBOL program that is in the specified namespace. When you import this into Java the namespace will interact appropriately with Java packages. Now look in the directory bin/com/microfocus/book in the COBOL Explorer. In there you will see a BookLegacy.class and a BookLegacy.cbldat file. These together form your COBOL program specified by book.cbl.

There is one other facility we will use to make life easier when calling the book demo program from Java. That is Micro Focus Smartlinkage. This is a tool that converts between traditional COBOL group item types and Java types. So a pic x(99) will be exposed as a Java String. A comp-5 item will become a Java int. This makes calling COBOL programs from Java much easier. There are three compiler directives we will be adding to support this.

Add the compiler directive "ilsmartlinkage" on a new line in the Build Configuration dialog as before. If you save and close this configuration you will notice additional files in bin/com/microfocus/book. These are LnkBDetails.class, LnkFilename.class and LnkFileStatus.class. These are classes generated to represent the linkage items in our book.cbl program. The BookLegacy class will now have an entry point that takes these wrapper classes rather than just taking a generic reference class that gives direct byte access. This simplifies access.

If you open book.cbl and hit F4 you will inline any copy books that are copied into this program. If you navigate down to the linkage section you will note the existence of the group items lnk-filename, lnk-file-status and lnk-b-details. These map directly onto the generated classes mentioned above. However we'd prefer to remove the prefixes commonly used in COBOL programs when working in Java. There is a directive to do this. Add the directives "ilcutprefix(lnk-b-)" and "ilcutprefix(lnk-)" to the Build Configuration. When you save and close the dialog your COBOL will be rebuilt. If you look in bin/com/microfocus/book once more you'll note that the prefixes have been removed from the generated class files.

This finalises the JVM COBOL project and we will now move onto incorporating this into a web application.

Dynamic Web Project

Now we will create the JSP component of the application. This project will import the previous project as a dependency. It will be composed of in total:

  1. The CobolBook project.
  2. A JSP/Bean view that will form our web page.
  3. A wrapper interface that will manage our program's interaction with servlet session life cycles.
  4. A servlet class that will dispatch to that interface based upon the POST method arguments.
  5. A web.xml deployment descriptor.

RunUnits and Sessions

Before we continue it is useful to talk in more detail about point 3. Many COBOL applications may be single user programs. To adapt these programs into a multi user setting like a web application can be problematic. You will get issues like file locking and memory isolation breaking down because suddenly the same resource set is being used by multiple users. Usually fixing this requires a significant rewrite.

Micro Focus has a better solution for this problem. We have a construct known as a RunUnit. This is specified by the JVM COBOL runtime class "com.microfocus.cobol.runtimeservices.RunUnit". Each RunUnit is a new session object. Each contains its own memory space and file lock table. A JVM COBOL program in one RunUnit will not normally access the memory of a program in another RunUnit. There is isolation between them. Also file locks are held per RunUnit. If a program in one RunUnit holds a file lock it will not be accessible to a file handling program in another RunUnit in the same process. This facility makes it easier for us to establish isolation between single user COBOL programs that are being migrated into a multi user environment.

To further this we have the concept of a RunUnitManager. Each such manager is designed to manage the life cycle of RunUnit objects in environments with a notion of a session. So that we can create a RunUnit when a session starts. Retrieve a RunUnit already attached to a session. Finally ensure that a RunUnit is stopped and its resources cleaned up when a session ends. Of particular relevance to this tutorial is the class "com.microfocus.cobol.runtimeservices.servlet.ServletRunUnitManager". This class in the JVM COBOL runtime is set up specifically to handle the life cycle of RunUnits in the context of servlet API HttpSession objects.

The RunUnitManager is not currently part of Visual COBOL but will be added in version 2.2. To run this tutorial we have provided a jar which contains the manager that will work with Visual COBOL 2.1. Click here to get the RunUnitManager. This will need to be included in the project.

Creating the Project

The steps to create the dynamic web project are as follows:

  1. Click File->New->Other.
  2. Expand the Web node and select Dynamic Web Project. Click Next.
  3. Set the Project name field to "JSPBookDemo".
  4. Ensure Use default location is checked.
  5. In the Target runtime field either select an already existing servlet container runtime or set one up by clicking New Runtime. To set up a runtime appropriate to your servlet container see here.
  6. Set the Dynamic web module version to 2.5.
  7. Click Finish.

This may ask you to open the Java EE perspective. Do so. As before all commands involving the JSPBookDemo will be expected in the Java EE perspective.

Now we need to add appropriate references to the previous project. There are two places this must be done: in the build options, so the Java compiler can find the appropriate classes to build against; and in the deployment assembly so that the JVM COBOL program will be exported into the created web archive.

Firstly open the project properties for the JSPBookDemo as with the CobolBook project. Select Java Build Path and the Libraries tab. Click on the Add Class Folder button, check the bin directory in the CobolBook project and click OK. Now select the Deployment Assembly option in the properties dialog. Click Apply if asked to save the build path changes. In the window that is opened you set up exactly what will get exported into the web application when it is deployed. Click Add and then select Java Build Path Entries and click Next. On the next page highlight the bin - \CobolBook option and click finish. This adds an entry to the deployment assembly dialog. This states that the CobolBook project classes will be included in the WEB-INF/classes directory of the web archive. Click OK to exit the Properties dialog.

Now we need to add the RunUnitManager. Unpack the zip archive and put the jar file in the WebContent/WEB-INF/lib directory of the project. This directory will be automatically added to the web application when you run or debug the application.

Creating the View

The standard way to create a view in a JSP application is to have a JSP page that takes a Java bean (which is an ordinary Java data class) as an attribute. The JSP will then insert the properties from that bean into the appropriate places in the output HTML. Lets start with the JSP page. Right click on the WebContent node in the JSPBookDemo project. Select New->JSP File. Set the File name text box to say BookJsp.jsp. Finally click Finish. You can copy the contents of this file from BookJsp.jsp.

If we look at BookJsp.jsp you will see numerous elements that contain text like ${book.stockno}. That states that the JSP will insert the value contained in the stockno property of the book attribute at this point. There are also other such elements like ${rununitid}. These are more direct text attributes that are not directly related to a book entry so have been kept separate. In this case we are passing in the RunUnit ID so we can display it in the title. Once all the ${} entries have been processed the JSP just becomes a simple HTML page containing a form.

We will now create the book bean that goes along with this view. Right click on Java Resources and select New->Class. Enter the package "com.microfocus.book" and the class name "BookBean". Click Finish to create the BookBean class. Now enter the code below into BookBean

package com.microfocus.book;

public class BookBean
{
  private final String _stockno;
  private final String _isbn;
  private final String _title;
  private final String _author;
  private final String _type;
  private final String _price;
  private final String _onhand;
  private final String _sold;
  private final String _stockval;
  
  BookBean(String stockno, String isbn, String title, String author,
      String type, String price, String onhand, String sold, String stockval)
  {
    this._stockno = stockno;
    this._isbn = isbn;      
    this._title = title;     
    this._author = author;    
    this._type = type;      
    this._price = price;     
    this._onhand = onhand;    
    this._sold = sold;      
    this._stockval = stockval;
  }
  
  public String getStockno()
  {
    return _stockno;
  }
  
  public String getIsbn()
  {
    return _isbn;
  }
  
  public String getTitle()
  {
    return _title;
  }
  
  public String getAuthor()
  {
    return _author;
  }
  
  public String getType()
  {
    return _type;
  }
  
  public String getPrice()
  {
    return _price;
  }
  
  public String getOnhand()
  {
    return _onhand;
  }
  
  public String getSold()
  {
    return _sold;
  }
  
  public String getStockval()
  {
    return _stockval;
  }
}
    

This class will contain the information that will be displayed in our JSP view. The dispatch servlet will bind an appropriate bean to the attribute "book" before calling the JSP. Later on we will expand this class to enable it to interact better with both the CobolBook program and the dispatch servlet.

Converting Status Codes to Exceptions

Error handling is very different in Java to procedural languages like COBOL. In Java it is normal to use exceptions rather than error codes to deal with failure and unexpected conditions. Given that our book demo returns a status code we need to convert this into an exception to make it a more natural fit for the JVM. Let's create our exception. As with the bean create a new Java class. Use the same package but call it JavaBookException. Change the superclass to be "java.lang.Exception". Enter the code in JavaBookException.

This class is relatively simple. The serialVersionUId is used by the Java serialization mechanism to distinguish between different versions of a class. It has no impact on our program. We have a constructor that takes a String. The status item in the COBOL is a pic xx which will be mapped to a String in Java. So we are taking the status code directly in the constructor when converting the error. We have a Map<String, String> which relates known status codes to appropriate error messages. We also have a static String for unknown error in case something beyond the expected errors occurs. We pass on the appropriate error message to the superclass constructor and store the status code in a field.

Creating the BookInterface

The following class really is the most relevant to accessing JVM COBOL from a JSP web application. It will handle the session management and provide a clean interface to Java applications that wish to call the program. It will also handle marshalling between the view bean and the smart linkage Details class from the COBOL program. Firstly lets update the BookBean to deal with this marshalling. Add the import declaration "import com.microfocus.cobol.program.ScaledInteger;" below the package statement in the BookBean class. Now add the two methods below to the bottom of the BookBean class.

  public void toDetails(Details details)
  {
    details.setStockno(_stockno);
    details.setIsbn(Long.parseLong(_isbn));
    details.setTitle(_title);
    details.setAuthor(_author);
    details.setType(_type);
    details.setRetail(ScaledInteger.parseScaledInteger(_price));
    
    int onHandInt = Integer.parseInt(_onhand);
    if(onHandInt < 0)
      throw new RuntimeException("The number of books on hand must be 0 or positive");
    details.setOnhand(onHandInt);
    
    int soldInt = Integer.parseInt(_sold);
    if(soldInt < 0)
      throw new RuntimeException("The number of books sold must be 0 or positive");
    details.setSold(soldInt);
  }
  
  public static BookBean fromDetails(Details details)
  {
    String stockno = details.getStockno().trim();
    String isbn = "" + details.getIsbn();
    String title = details.getTitle().trim();
    String author = details.getAuthor().trim();
    String type = details.getType().trim();
    String price = details.getRetail().toString();
    String onhand = "" + details.getOnhand();
    String sold = "" + details.getSold();
    ScaledInteger stockvalInt = details.getRetail().multiply(new ScaledInteger(details.getOnhand(), 0));
    String stockval = stockvalInt.toString();
    
    return new BookBean(stockno, isbn, title, author, type, price, onhand, sold, stockval);
  }
    

These two methods handle the conversion between the Details object generated by smartlinkage and the BookBean that is used by our JSP view. It will enable us to easily convert the COBOL calls into Java methods.

Now create a new Java class as before. Put it in the "com.microfocus.book" package. Give it the name BookInterface. Add the contents from BookInterface. This class is what will handle our session management and integration into a Java environment. Of particular interest are the two constructors listed below.

  public final IRunUnit runUnit;
  public final BookLegacy bookLegacy;
  
  public BookInterface(HttpSession session)
  {
    this(ServletRunUnitManager.getManager().GetSessionRunUnit(session));
  }
  
  public BookInterface(IRunUnit runUnit)
  {
    this.runUnit = runUnit;
    BookLegacy bookLegacy = (BookLegacy) runUnit.GetInstance(BookLegacy.class);
    
    if(bookLegacy == null)
    {
      bookLegacy = new BookLegacy();
      runUnit.Add(bookLegacy);
    }
    
    this.bookLegacy = bookLegacy;
  }
    

These constructors handle two separate issues. The lower one handles tying this interface class into a RunUnit. It stores away the RunUnit and gets the program instance of BookLegacy. The upper constructor deals with the servlet's HttpSession object and the ServletRunUnitManager. The interaction here is quite simple. Get the ServletRunUnitManager (which is a singleton) and then call GetSessionRunUnit with the session object. The manager will create a RunUnit if this is the first call with this particular session. It will also manage the shutdown handling routines so that when this session is invalidated the manager will call StopRun on the RunUnit attached to that session. In this short section of code we've managed both the issue of program isolation and that of session life cycles. Not bad for a few lines of code in two constructors.

The rest of the code is pretty straight forward. We've split the function parameter into separate method calls on this interface. We also have some short code to handle whether we have an error or not.

Creating the Servlet

The last functional piece of code that needs to be written for this tutorial is the servlet that interprets the incoming POST and GET requests and dispatches to the appropriate interface method. It will then bind the output BookBean into the request and forward that request to the JSP for response. Before we go any further we need to add the methods below to BookBean.

  public static BookBean blankBook()
  {
    return msgBook("*************************************");
  }
  
  public static BookBean msgBook(String msg)
  {
    String stockno    = "****";                               
    String isbn       = "*************";                      
    String title      = msg;
    String author     = "*************************************";
    String type       = "****";                               
    String price      = "****";                               
    String onhand     = "****";                               
    String sold       = "****";                               
    String stockval   = "****";                               
    
    return new BookBean(stockno, isbn, title, author, type, price, onhand, sold, stockval);
  }
    

These are primarily used by the servlet to produce output books which represent either errors or a blank book.

Now for the servlet. Create a new Java class as before. Put it in the "com.microfocus.book" package. Give it the name BookServlet. Add the contents from BookServlet.

There are four interesting parts to this class. The interpretation of the appropriate POST parameters. The resulting calls into BookInterface. The binding of an output BookBean into the request attributes and finally forwarding the request onto our JSP so it can create the response. Lets look at some code in the doProcessing method.

    String subValue = req.getParameter(SUBMIT_PARAMETER);
      
    ...
      
    if(subValue.equals(READ_PARAMETER))
    {
      performRead(req, res);
    }
    else if(subValue.equals(ADD_PARAMETER))
    {
      performAdd(req, res);
    }
    else if(subValue.equals(DELETE_PARAMETER))
    {
      performDelete(req, res);
    }
    

This code simply grabs the value of the submit parameter from the request object. Then we compare the submit parameter to various known values that perform the appropriate functionality for each function the application provides. Now look in some detail at performRead.

    BookInterface bookInterface = getBookInterface(req.getSession());
    String bookId = getStockNo(req, res);
    
    try
    {
      BookBean book = bookInterface.readBook(bookId);
      
      outputBook(req, res, book);
    }
    catch(JavaBookException e)
    {
      outputBookException(req, res, e);
    }
    catch (Exception e)
    {
      outputException(req, res, e);
    }
    

This creates a BookInterface by passing in the session object from the request object. Then it gets the stock number from the request parameters. Then it makes a call to readBook on our interface. Finally outputting the BookBean object it gets back by calling outputBook. Lets examine that method.

    if(book != null)
    {
          req.setAttribute("book", book);
    }
    else
    {
          req.setAttribute("book", BookBean.msgBook("ERROR! book is null in output book"));
    }
    

This very simply binds the book as a request attribute. Binding it to the name book which was the name used in our JSP view. The JSP will unpack this book object and output its values as described previously.

The final part of interest is back in doProcessing

    RequestDispatcher dispatcher = getServletContext().getRequestDispatcher(VIEW_URL);
    try
    {
      dispatcher.forward(req, res);
    }
    catch(Exception e)
    {
      throw new RuntimeException(e);
    }
    

This gets a dispatch object for our view which is the BookJsp.jsp. Then it forwards the request to it. With all the necessary attributes set up this can output our book record correctly.

The Deployment Descriptor (web.xml)

There is one last file we need to modify before we have a complete web application. The deployment descriptor is the file used by the servlet container to define which servlets match up with which URLs. It also defines which servlet or resource provides the landing page for the root of the service.

If it doesn't already exist create the file web.xml in the JSPBookDemo project. To do so right click on WebContent->WEB-INF then click New->File. Enter "web.xml" in the Filename text field and click Finish. Open the file and ensure it is in source mode by clicking the tab at the bottom left hand side of the editor windor. Enter the content below.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         version="2.5">
  <display-name>JSPBookDemo</display-name>
  <servlet>
    <servlet-name>BookServlet</servlet-name>
    <servlet-class>com.microfocus.book.BookServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>BookServlet</servlet-name>
    <url-pattern>/view</url-pattern>
  </servlet-mapping>
  <welcome-file-list>
    <welcome-file>view</welcome-file>
  </welcome-file-list>
</web-app>
    

This is a straight forward servlet deployment descriptor. The attributes for the web-app element are used to define the specific version of the servlet API in use. The display-name defines the internal name of this web application that will appear in the servlet container management system. The servlet element binds a name to a particular servlet class. In this case our BookServlet. The servlet-mapping element binds a particular servlet to a URL from the root of our servlet. So any call to say "localhost:8080/JSPBookDemo/view" will be directed to BookServlet. The last element is the welcome-file-list. This just defines a set of files that will be used as the landing page for the servlet. In this case we are setting the view to be our landing page.

Running and Debugging

Now that we have a complete application it is time to run it. To run it right click on JSPBookDemo in the explorer and select Run As->Run on Server. Eclipse will give you a window containing the servlet containers it knows about. Select the one you want to deploy to and click Finish. This will launch the servlet container, build our web application and deploy it to the server. It will then open up a web browser page displaying our application. To test it type in stock number "1111" and click Read.

To debug the application we will need to add the COBOL source to the debug configuration. We need to do this so that the debugger can find the COBOL source and thus step between Java and COBOL seamlessly. To do this right click on JSPBookDemo and click Debug As->Debug Configurations. Double click on the option appropriate to your servlet container on the left to create a new debug configuration with that container. Pick your server in the drop down box. Name the configuration as "JSPBookDemo Debug". Click on the Source tab and click Add. Select COBOL Project and click OK. Then select CobolBook and click OK. Finally click Apply and then Close.

Now we have a debug configuration we should set some break points. To set a breakpoint double click in the left hand margin of Eclipse next to a line you want to stop at. Set a break point in the servlet in the doRead call. Set another in the readBook method of the BookInterface. Set a third in book.cbl in the do-read-record section. To launch the debugger click the drop down arrow next to the debug icon on the tool bar (see image below). Then select the debug configuration we created earlier. If it asks to open the debug perspective let it do so.

The Eclipse Debug Button

When you get the interface popped up try to read book "1111" and it should hit your break points. Hit F8 to continue running. It should hit the following break points and eventually go from Java into the COBOL. Click the red box at the top left to stop the session.

Appendix

book.cbl

      ********************************************************************************************************
      *
      *  Copyright (C) Micro Focus (IP) Limited 2011.
      *  All rights reserved.
      *
      *  This sample code is supplied for demonstration purposes only on an "as is" basis and "is for use at
      *  your own risk".
      *
      ********************************************************************************************************
      $set retrylock
       program-id. BookLegacy.

       environment division.
       input-output section.
       file-control.
           select bookfile assign to filename
               file status is ls-file-status
               organization is indexed
               access mode is dynamic
               record key is b-stockno
               alternate record key is b-title with duplicates
               alternate record key is b-author with duplicates
               .

       data division.
       file section.
       FD bookfile.
       copy "book-rec.cpy" replacing ==(prefix)== by ==b==.

       working-storage section.
       01 ls-file-status   pic xx.
       01 ls-call-status   pic x(2) comp-5.

       linkage section.
       01 lnk-filename       pic x(256).
       01 lnk-function       pic x.
           88 read-record    value "1".
           88 add-record     value "2".
           88 delete-record  value "3".
           88 next-record    value "4".
       01 lnk-file-status    pic xx.
       78 no-key-error       value "B1".
       copy "book-rec.cpy" replacing ==(prefix)== by ==lnk-b==.

       procedure division using by value lnk-function
                                by reference lnk-b-details
                                by reference lnk-file-status.
       main section.

           call "CBL_TOUPPER" using lnk-b-text-details
                              by value length lnk-b-text-details
                              returning ls-call-status

           evaluate true
               when read-record
                   perform do-read-record

               when add-record
                   perform do-add-record

               when delete-record
                   perform do-delete-record

               when next-record
                   perform do-next-record

           end-evaluate
           exit program
           .

       do-read-record section.
           open input bookfile
           if ls-file-status <> "00"
               initialize lnk-b-details
               move all '*' to lnk-b-text-details

               move ls-file-status to lnk-file-status 
               exit section
           end-if
           evaluate true
                when lnk-b-stockno <> spaces
                    move lnk-b-stockno to b-stockno
                    read bookfile

                when lnk-b-title <> spaces
                    move lnk-b-title to b-title
                    read bookfile key is b-title

                when lnk-b-author <> spaces
                    move lnk-b-author to b-author
                    read bookfile key is b-author

                when other
      *>------------No key specified - return unsuccessful read
                     move no-key-error to  ls-file-status

           end-evaluate
           move ls-file-status to lnk-file-status
           if ls-file-status = "00"
               move b-title to lnk-b-title
               move b-type to lnk-b-type
               move b-author to lnk-b-author
               move b-stockno to lnk-b-stockno
               move b-isbn to lnk-b-isbn
               move b-retail to lnk-b-retail
               move b-onhand to lnk-b-onhand
               move b-sold to lnk-b-sold
           else
               initialize lnk-b-details
               move all '*' to lnk-b-text-details
           end-if
           close bookfile
           .

       do-next-record section.
           open input bookfile
           if ls-file-status <> "00"
               initialize lnk-b-details
               move all '*' to lnk-b-text-details

               move ls-file-status to lnk-file-status
               exit section
           end-if

           move lnk-b-stockno to b-stockno
           start bookfile key > b-stockno
           read bookfile next

           move ls-file-status to lnk-file-status
           if ls-file-status = "00"
               move b-title to lnk-b-title
               move b-type to lnk-b-type
               move b-author to lnk-b-author
               move b-stockno to lnk-b-stockno
               move b-isbn to lnk-b-isbn
               move b-retail to lnk-b-retail
               move b-onhand to lnk-b-onhand
               move b-sold to lnk-b-sold
           else
               initialize lnk-b-details
               move all '*' to lnk-b-text-details
           end-if
           close bookfile
           .

       do-add-record section.
           open i-o bookfile
           evaluate ls-file-status
               when "05"
      *>-------File not created yet
               when "00"
                   continue

               when other
                   move ls-file-status to lnk-file-status
                   exit section
           end-evaluate

           move lnk-b-stockno to b-stockno
           read bookfile
           if ls-file-status = "00"
      * Record already exists - so error
               move "99" to ls-file-status
           else
               move lnk-b-isbn to b-isbn
               move lnk-b-title to b-title
               move lnk-b-type to b-type
               move lnk-b-author to b-author
               move lnk-b-retail to b-retail
               move lnk-b-onhand to b-onhand
               move lnk-b-sold to b-sold
               write b-details
           end-if

           move ls-file-status to lnk-file-status
           close bookfile
           .

       do-delete-record section.
           open i-o bookfile
           if ls-file-status <> "00"
               move ls-file-status to lnk-file-status
               exit section
           end-if

           evaluate true
               when lnk-b-stockno <> spaces
                   move lnk-b-stockno to b-stockno
                   read bookfile
                   delete bookfile record

               when lnk-b-title <> spaces
                   move lnk-b-title to b-title
                   read bookfile key is b-title
                   delete bookfile record

               when lnk-b-author <> spaces
                   move lnk-b-author to b-author
                   read bookfile key is b-author
                   delete bookfile record

               when other
      *>------------No key specified - return unsuccessful read
                   move no-key-error to ls-file-status

           end-evaluate

           move ls-file-status to lnk-file-status
           close bookfile
           .
       
       set-filename section.
       entry "SET_FILENAME" using lnk-filename.
           move lnk-filename to filename
           
           goback.
    

book-rec.cpy

      ******************************************************************
      *
      *  Copyright (C) Micro Focus 2010-2012.  All rights reserved.
      *
      *  This sample code is supplied for demonstration purposes only
      *  on an "as is" basis and is for use at your own risk.
      *
      ******************************************************************
       
       01 (prefix)-details.
           03 (prefix)-text-details.
             05 (prefix)-title     pic x(50).
             05 (prefix)-type      pic x(20).
             05 (prefix)-author    pic x(50).

           03 (prefix)-stockno     pic x(4).
           03 (prefix)-isbn          pic 9(13).
           03 (prefix)-retail      pic 99v99.
           03 (prefix)-onhand      pic 9(5).
           03 (prefix)-sold          pic 9(5) comp-3.

    

BookBean.java

package com.microfocus.book;

import com.microfocus.cobol.program.ScaledInteger;

public class BookBean
{
  private final String _stockno;
  private final String _isbn;
  private final String _title;
  private final String _author;
  private final String _type;
  private final String _price;
  private final String _onhand;
  private final String _sold;
  private final String _stockval;
  
  BookBean(String stockno, String isbn, String title, String author,
      String type, String price, String onhand, String sold, String stockval)
  {
    this._stockno = stockno;
    this._isbn = isbn;      
    this._title = title;     
    this._author = author;    
    this._type = type;      
    this._price = price;     
    this._onhand = onhand;    
    this._sold = sold;      
    this._stockval = stockval;
  }
  
  public String getStockno()
  {
    return _stockno;
  }
  
  public String getIsbn()
  {
    return _isbn;
  }
  
  public String getTitle()
  {
    return _title;
  }
  
  public String getAuthor()
  {
    return _author;
  }
  
  public String getType()
  {
    return _type;
  }
  
  public String getPrice()
  {
    return _price;
  }
  
  public String getOnhand()
  {
    return _onhand;
  }
  
  public String getSold()
  {
    return _sold;
  }
  
  public String getStockval()
  {
    return _stockval;
  }
  
  public void toDetails(Details details)
  {
    details.setStockno(_stockno);
    details.setIsbn(Long.parseLong(_isbn));
    details.setTitle(_title);
    details.setAuthor(_author);
    details.setType(_type);
    details.setRetail(ScaledInteger.parseScaledInteger(_price));
    
    int onHandInt = Integer.parseInt(_onhand);
    if(onHandInt < 0)
      throw new RuntimeException("The number of books on hand must be 0 or positive");
    details.setOnhand(onHandInt);
    
    int soldInt = Integer.parseInt(_sold);
    if(soldInt < 0)
      throw new RuntimeException("The number of books sold must be 0 or positive");
    details.setSold(soldInt);
  }
  
  public static BookBean fromDetails(Details details)
  {
    String stockno = details.getStockno().trim();
    String isbn = "" + details.getIsbn();
    String title = details.getTitle().trim();
    String author = details.getAuthor().trim();
    String type = details.getType().trim();
    String price = details.getRetail().toString();
    String onhand = "" + details.getOnhand();
    String sold = "" + details.getSold();
    ScaledInteger stockvalInt = details.getRetail().multiply(new ScaledInteger(details.getOnhand(), 0));
    String stockval = stockvalInt.toString();
    
    return new BookBean(stockno, isbn, title, author, type, price, onhand, sold, stockval);
  }
  
  public static BookBean blankBook()
  {
    return msgBook("*************************************");
  }
  
  public static BookBean msgBook(String msg)
  {
    String stockno    = "****";                               
    String isbn       = "*************";                      
    String title      = msg;
    String author     = "*************************************";
    String type       = "****";                               
    String price      = "****";                               
    String onhand     = "****";                               
    String sold       = "****";                               
    String stockval   = "****";                               
    
    return new BookBean(stockno, isbn, title, author, type, price, onhand, sold, stockval);
  }
}

    

BookInterface.java

package com.microfocus.book;

import javax.servlet.http.HttpSession;

import com.microfocus.cobol.program.IObjectControl;
import com.microfocus.cobol.runtimeservices.IRunUnit;
import com.microfocus.cobol.runtimeservices.servlet.ServletRunUnitManager;

public class BookInterface
{
  public static final String READ_RECORD   = "1";
    public static final String ADD_RECORD    = "2";
    public static final String DELETE_RECORD = "3";
    public static final String NEXT_RECORD   = "4";
  
  public final IRunUnit runUnit;
  public final BookLegacy bookLegacy;
  
  public BookInterface(HttpSession session)
  {
    this(ServletRunUnitManager.getManager().GetSessionRunUnit(session));
  }
  
  public BookInterface(IRunUnit runUnit)
  {
    this.runUnit = runUnit;
    BookLegacy bookLegacy = (BookLegacy) runUnit.GetInstance(BookLegacy.class);
    
    if(bookLegacy == null)
    {
      bookLegacy = new BookLegacy();
      runUnit.Add(bookLegacy);
    }
    
    this.bookLegacy = bookLegacy;
  }
  
  public BookBean readBook(String stockNo) throws JavaBookException
  {
    Details details = getObject(Details.class);
    FileStatus status = getObject(FileStatus.class);
    

    details.setStockno(stockNo);
    bookLegacy.BookLegacy(READ_RECORD, details, status);
    
    throwExceptionIfError(status);
    
    return BookBean.fromDetails(details);
  }
  
  public BookBean addBook(BookBean book) throws JavaBookException
  {
    Details details = getObject(Details.class);
    FileStatus status = getObject(FileStatus.class);
    
    book.toDetails(details);
    bookLegacy.BookLegacy(ADD_RECORD, details, status);
    
    throwExceptionIfError(status);
    
    return BookBean.fromDetails(details);
  }
  
  public BookBean deleteBook(String stockNo) throws JavaBookException
  {
    Details details = getObject(Details.class);
    FileStatus status = getObject(FileStatus.class);
    
    details.setStockno(stockNo);
    bookLegacy.BookLegacy(DELETE_RECORD, details, status);
    
    throwExceptionIfError(status);
    
    return BookBean.fromDetails(details);
  }
  
  public BookBean nextBook(String stockNo) throws JavaBookException
  {
    Details details = getObject(Details.class);
    FileStatus status = getObject(FileStatus.class);
    
    details.setStockno(stockNo);
    bookLegacy.BookLegacy(NEXT_RECORD, details, status);
    
    throwExceptionIfError(status);
    
    return BookBean.fromDetails(details);
  }
  
  public void setFileName(String filename)
  {
    bookLegacy.SET_FILENAME(getFilenameObject(filename));
  }
  
  private static void throwExceptionIfError(FileStatus statusCode) throws JavaBookException
  {
    throwExceptionIfError(statusCode.getFileStatus().trim());
  }
  
  private static void throwExceptionIfError(String statusCode) throws JavaBookException
  {
    if(!"00".equals(statusCode) && !"02".equals(statusCode))
    {
      throw new JavaBookException(statusCode);
    }
  }
  
  private Filename getFilenameObject(String filename)
  {
    Filename output = getObject(Filename.class);
    
    output.setFilename(filename);
    
    return output;
  }
  
  private <T extends IObjectControl> T getObject(Class<T> cls)
  {
    try
    {
      T output = cls.newInstance();
      runUnit.Add(output);
      
      return output;
    }
    catch (Throwable t)
    {
      throw new RuntimeException(t);
    }
  }
}

    

BookServlet.java

package com.microfocus.book;

import java.io.PrintWriter;
import java.io.StringWriter;

import javax.servlet.RequestDispatcher;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import com.microfocus.cobol.runtimeservices.IRunUnit;
import com.microfocus.cobol.runtimeservices.servlet.ServletRunUnitManager;

public class BookServlet extends HttpServlet
{
  private static final long serialVersionUID = -3563065100184185678L;
  
  public static final String STOCK_NO_ATTRIBUTE   = "stockno";
  public static final String TITLE_ATTRIBUTE      = "title";
  public static final String AUTHOR_ATTRIBUTE     = "author";
  public static final String TYPE_ATTRIBUTE     = "type";
  public static final String ISBN_ATTRIBUTE     = "isbn";
  public static final String PRICE_ATTRIBUTE      = "price";
  public static final String ONHAND_ATTRIBUTE     = "onhand";
  public static final String SOLD_ATTRIBUTE     = "sold";
  public static final String STATUS_ATTRIBUTE     = "status";
  public static final String RUN_UNIT_ID_ATTRIBUTE  = "rununitid";
  
  public static final String SUBMIT_PARAMETER     = "submit";
  public static final String READ_PARAMETER     = "Read";
  public static final String ADD_PARAMETER      = "Add";
  public static final String DELETE_PARAMETER     = "Delete";
  public static final String NEXT_PARAMETER     = "Next";
  public static final String END_PARAMETER      = "End Session";
  public static final String ERROR_VALUE        = "ERROR";
  public static final String DEFAULT_VALUE      = "DEFAULT";
  public static final String VIEW_URL         = "/BookJsp.jsp";
  
  public static final String BOOK_FILE        = "C:/work/garethm/JavaWebServicesDemos/COBOLJSPDemo/CobolBook/bookfile.dat";

  
  protected void doProcessing(HttpServletRequest req, HttpServletResponse res, boolean isGet)
  {
    String subValue = req.getParameter(SUBMIT_PARAMETER);
    
    if(subValue == null)
    {
      subValue = DEFAULT_VALUE;
    }
    
    setRunUnitId(req);
    
  if(subValue.equals(READ_PARAMETER))
  {
    performRead(req, res);
  }
  else if(subValue.equals(ADD_PARAMETER))
  {
    performAdd(req, res);
  }
  else if(subValue.equals(DELETE_PARAMETER))
  {
    performDelete(req, res);
  }
  else if(subValue.equals(NEXT_PARAMETER))
  {
    performNext(req, res);
  }
  else if(subValue.equals(END_PARAMETER))
  {
    performEndSession(req, res);
  }
  else
  {
    outputBlankBook(req, res);
  }
  
    RequestDispatcher dispatcher = getServletContext().getRequestDispatcher(VIEW_URL);
    try
  {
    dispatcher.forward(req, res);
  }
  catch(Exception e)
  {
    throw new RuntimeException(e);
  }
  }

  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse res)
  {
    doProcessing(req, res, true);
  }
  
  @Override
  protected void doPost(HttpServletRequest req, HttpServletResponse res)
  {
    doProcessing(req, res, false);
  }

  private void performRead(HttpServletRequest req, HttpServletResponse res)
  {
    BookInterface bookInterface = getBookInterface(req.getSession());
    String bookId = getStockNo(req, res);
    
    if(bookId == null)
    {
      bookId = ERROR_VALUE;
    }
    
    try
    {
      bookInterface.setFileName(BOOK_FILE);
      BookBean book = bookInterface.readBook(bookId);
      
      outputBook(req, res, book);
    }
    catch(JavaBookException e)
    {
      outputBookException(req, res, e);
    }
    catch (Exception e)
    {
      outputException(req, res, e);
    }
  }

  private void performAdd(HttpServletRequest req, HttpServletResponse res)
  {
    BookInterface bookInterface = getBookInterface(req.getSession());
    
    try
    {
      BookBean book = getBook(req, res);

      bookInterface.setFileName(BOOK_FILE);
      book = bookInterface.addBook(book);

      outputBook(req, res, book);
    }
    catch(JavaBookException e)
    {
      outputBookException(req, res, e);
    }
    catch (Exception e)
    {
      outputException(req, res, e);
    }
  }

  private void performDelete(HttpServletRequest req, HttpServletResponse res)
  {
    BookInterface bookInterface = getBookInterface(req.getSession());
    String bookId = getStockNo(req, res);
    
    if(bookId == null)
    {
      bookId = ERROR_VALUE;
    }
    
    try
    {
      bookInterface.setFileName(BOOK_FILE);
      BookBean book = bookInterface.deleteBook(bookId);
      
      outputBook(req, res, book);
    }
    catch(JavaBookException e)
    {
      outputBookException(req, res, e);
    }
    catch (Exception e)
    {
      outputException(req, res, e);
    }
  }

  private void performNext(HttpServletRequest req, HttpServletResponse res)
  {
    BookInterface bookInterface = getBookInterface(req.getSession());
    String bookId = getStockNo(req, res);
    
    if(bookId == null)
    {
      bookId = ERROR_VALUE;
    }
    
    try
    {
      bookInterface.setFileName(BOOK_FILE);
      BookBean book = bookInterface.nextBook(bookId);
      
      outputBook(req, res, book);
    }
    catch(JavaBookException e)
    {
      outputBookException(req, res, e);
    }
    catch (Exception e)
    {
      outputException(req, res, e);
    }
  }
    
  private void performEndSession(HttpServletRequest req, HttpServletResponse res)
  {
      HttpSession session = req.getSession();
      session.invalidate();
      
      outputError(req, res, "Session invalidated");
  }


  private void outputBlankBook(HttpServletRequest req, HttpServletResponse res)
  {   
        outputBook(req, res, BookBean.blankBook());
  }

  private void outputBookException(HttpServletRequest req, HttpServletResponse res, JavaBookException jbe)
  {
        outputError(req, res, jbe.getMessage());
  }

  private void outputException(HttpServletRequest req, HttpServletResponse res, Exception e)
  {
    String msg = e.getClass().getName() + " [" + e.getMessage() + "]";
    StringWriter strWriter = new StringWriter();
    PrintWriter printWriter = new PrintWriter(strWriter);
    e.printStackTrace(printWriter);
    req.setAttribute(STATUS_ATTRIBUTE, strWriter.toString());
    outputError(req, res, msg);
  }

  private void outputBook(HttpServletRequest req, HttpServletResponse res, BookBean book)
  {
    if(book != null)
    {
      req.setAttribute("book", book);
    }
    else
    {
      req.setAttribute("book", BookBean.msgBook("ERROR! book is null in output book"));
    }
  }

  private void outputError(HttpServletRequest req, HttpServletResponse res, String msg)
  {
    outputBook(req, res, BookBean.msgBook(msg));
  }
  
  private BookBean getBook(HttpServletRequest req, HttpServletResponse res)
  {
    return new BookBean
    (
      getStockNo(req, res),
      getIsbn(req, res),
      getTitle(req, res),
      getAuthor(req, res),
      getType(req, res),
      getPrice(req, res),
      getOnhand(req, res),
      getSold(req, res),
      ""
    );
  }
  
  private String getAttribute(HttpServletRequest req, HttpServletResponse res, String attribute)
  {
    String stockNoStr;
    stockNoStr = req.getParameter(attribute);
    if(stockNoStr == null)
    {
        stockNoStr = ERROR_VALUE;
    }
    
    return stockNoStr;
  }
  
  private String getStockNo(HttpServletRequest req, HttpServletResponse res)
  {
    return getAttribute(req, res, STOCK_NO_ATTRIBUTE);
  }
  
  private String getTitle(HttpServletRequest req, HttpServletResponse res)
  {
    return getAttribute(req, res, TITLE_ATTRIBUTE);
  }
  
  private String getAuthor(HttpServletRequest req, HttpServletResponse res)
  {
    return getAttribute(req, res, AUTHOR_ATTRIBUTE);
  }
  
  private String getType(HttpServletRequest req, HttpServletResponse res)
  {
    return getAttribute(req, res, TYPE_ATTRIBUTE);
  }
  
  private String getIsbn(HttpServletRequest req, HttpServletResponse res)
  {
    return getAttribute(req, res, ISBN_ATTRIBUTE);
  }
  
  private String getPrice(HttpServletRequest req, HttpServletResponse res)
  {
    return getAttribute(req, res, PRICE_ATTRIBUTE);
  }
  
  private String getOnhand(HttpServletRequest req, HttpServletResponse res)
  {
    return getAttribute(req, res, ONHAND_ATTRIBUTE);
  }
  
  private String getSold(HttpServletRequest req, HttpServletResponse res)
  {
    return getAttribute(req, res, SOLD_ATTRIBUTE);
  }
  
  private IRunUnit getRunUnit(HttpSession session)
  {
    return ServletRunUnitManager.getManager().GetSessionRunUnit(session);
  }

  private void setRunUnitId(HttpServletRequest req)
  {
    HttpSession session = req.getSession();
    IRunUnit runUnit = getRunUnit(session);
    String ruid = "" +  runUnit.getRunUnitID();
    req.setAttribute(RUN_UNIT_ID_ATTRIBUTE, ruid);
  }
  
  private BookInterface getBookInterface(HttpSession session)
  {
    BookInterface output = new BookInterface(session);
    
    return output;
  }
}

    

JavaBookException.java

package com.microfocus.book;

import java.util.*;

public class JavaBookException extends Exception
{

  /**
   * 
   */
  private static final long serialVersionUID = -3882735817601888938L;

  private static final Map<String, String> messages;
  private static final String unknownErrorMessage = "Unknown Error: ";

  public final String statusCode;

  public JavaBookException(String statusCode)
  {
    super(messages.containsKey(statusCode) ? messages.get(statusCode)
        : unknownErrorMessage + statusCode);
    this.statusCode = statusCode;
  }

  static
  {
    messages = new HashMap<String, String>();
    messages.put("35", "Error: Data file not found");
    messages.put("23", "Error: Stock item not found");
    messages.put("46", "No more items left");
    messages.put("99", "Error: Item already exists");
    messages.put("01", "Error: File error");
    messages.put("B1", "Error: No key entered");
  }
}

    

BookJsp.jsp

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>JSP Book Demo - Run Unit ID: ${rununitid}</title>
</head>
<body>
    <form name="input" action="/JSPBookDemo/view" method="post">
      <table border="0">
        <tr><td>Stock Number:</td><td colspan="7"><input type="text" name="stockno" value="${book.stockno}"/></td></tr>
        <tr><td>ISBN:</td><td colspan="7"><input type="text" name="isbn" value="${book.isbn}"/></td></tr>
        <tr><td>Title:</td><td colspan="7"><input type="text" size="120" name="title" value="${book.title}"/></td></tr>
        <tr><td>Author:</td><td colspan="7"><input type="text" size="120" name="author" value="${book.author}"/></td></tr>
        <tr><td>Type:</td><td colspan="7"><input type="text" size="120" name="type" value="${book.type}"/></td></tr>
        <tr>
          <td>Price:</td> <td><input type="text" name="price" value="${book.price}"/></td>
          <td>On Hand:</td> <td><input type="text" name="onhand" value="${book.onhand}"/></td>
          <td>Sold:</td> <td><input type="text" name="sold" value="${book.sold}"/></td>
          <td>Stock Value:</td> <td><input type="text" readonly="readonly" name="stockval" value="${book.stockval}"/></td>
        </tr>
        <tr>
          <td colspan="8">
            <input type="submit" name="submit" value="Read" />
            <input type="submit" name="submit" value="Add" />
            <input type="submit" name="submit" value="Delete" />
            <input type="submit" name="submit" value="Next" />
            <input type="submit" name="submit" value="End Session" />
          </td>
        </tr>
        <!-- <tr><td>Status:</td><td colspan="7">${status}</td></tr> -->
      </table>
    </form>
</body>
</html>