REST and Microservices with Panther Web Application Broker

 

If you already have a JAM or Panther application, you may want to expose some of its functionality to a non-Panther web or mobile application.  Or, even if you already have a Panther Web application, you may want to have your user interface interact with it without having it return an HTML page that replaces the one the user is working with. 

 

For example, you might use a Panther application's JPL procedure that searches for a part or other item in your inventory.  You can expose this functionality as a RESTful service or a microservice that can be seamlessly integrated into your non Panther web application for processing orders.

 

As of version 5.52, Panther Web Application Broker applications can now fully support sending and receiving data using a Content-Type of "application/json" giving you the ability to expose JAM/Panther functionality as microservices or REST APIs even to non-Panther applications or mobile apps.  Several changes to Panther Web make this possible. 

 

Background

 

First, let’s define some terms associated with Panther Web technology:

 

Jserver - A Panther process that runs on the server machine.  It opens Panther screens and executes JPL code, etc.

 

Requester - A light weight process supplied by Panther Web, usually a CGI program or Servlet that works with the HTTP Server.  It receives HTTP requests, passes them to the Jserver, and returns responses from the Jserver.

 

Dispatcher - A process supplied by Panther Web that starts up Jservers as needed and controls the traffic between the Requesters and Jservers.

 

The traditional architecture for a Panther Web application is one in which Panther screens are opened on the Jserver in order to provide both backend functionality and UI components.  Statefullness and application navigation are also typically provided by the Jserver.  The Jserver usually generates HTML documents from Panther Screens, either fully or by means of Panther HTML templates.  The templates contain embedded template tags for which the Jserver generates content.  That content is usually HTML, but may be just the data from Panther widgets or variables.  In fact, Panther HTML templates need not contain any HTML at all.  They may contain JSON or XML, for example, and that JSON or XML may contain embedded template tags, such as {{value:variable}}. This allows the application to embed only the Panther data contained within a widget or variable within the generated JSON or XML document that is the response to the HTTP request.

 

 

What’s new?

 

Panther Web 5.52 allows application code to set both the Status and Content-type headers using the Panther library function, sm_web_set_http_header().  Only if the Content-type header is not already set by application code will the Jserver return a Content-type of 'text/html'.  Prolifics recommends using only the servlet form of the Requester in all cases.

 

The Jserver will now accept PUT requests and performs processing in the same way as for POST.  DELETE requests will now be accepted and perform processing in the same manner as for GET.  Application code may inspect the CGI REQUEST_METHOD header in order to determine what functionality to perform.  In JPL, for example, the built-in variable, @cgi_request_method, can be referenced.

 

Also, in order to better support RESTful style URIs, if the Accept header of the request contains 'application/json', but  not 'text/html', the Requester Servlet will strip off everything in the URI following the screen name and assign it to a new custom CGI header, EXTRA_PATH_INFO, for the Jserver to receive.  The application code can then retrieve and parse this by means of a new built-in variable, @cgi_extra_path_info.  This technique can be used to apply filters to the data resource represented by the screen.  So, for example, if the 'parts' screen represents a table of parts by means of the  grid widget it contains, then a URI ending in 'parts/500' may represent a single part whose part number is 500.  When the HTTP GET operation is performed on that URI, the 'parts' screen is opened, and a screen entry function can find '/500' in the @cgi_extra_path_info  variable.  It should then retrieve the data for only part number 500. 

 

Receiving Data with GET

 

Use a Panther HTML Template for your screen that contains JSON rather than HTML.   The Jserver now allows application code to set both the Content-type and Status headers to be returned to the client.  Previously these would be overridden by Panther before returning data to the client.  Use sm_web_set_http_header() in order to set these HTTP headers.  Set the Content-type to application/json when returning JSON data to the client.  Set an appropriate code for the Status header.

 

 

Sending Data with POST or PUT

 

The Requester Servlet now has special functionality to allow sending JSON data to the Jserver for a PUT or POST request.  Name/value pairs are treated as data for fields whose names match the names used in the JSON data.  JSON arrays of basic types, including string, are received into Panther arrays.  Nesting of JSON objects is flattened such that only the leaf nodes that are name/value pairs are processed.  In other words, structural elements are ignored.  Panther fields can be of any widget type that can receive data with sm_putfield(), including wordwrap Multiline Text Widgets, which will receive data using sm_ww_write(). Thus, string data longer than 255 characters may be sent to a wordwrap Multiline Text Widget.

For example, suppose the client POSTs the following JSON data:

  {

    President:

    {

      "last":"Trump",

      "first":"Donald",

      "address":["1600 Pennsylvania Avenue NW","Washington, DC 20500"]

    }

  }

The data sent from the Requester Servlet to the Jserver (not including the header info), will be,

  mi_1_address=1600+Pensylvania+Avenue+NW&mi_2_address=Washington%2C+DC+2050&mi_1_last=Trump&mi_1_first=Donald

 

The Jserver will fill fields on the target screen named, "first", "last", and "address", where "address" is an array containing two or more occurrences.  Note that the syntax, ‘mi_<number>_<name>’, is new for Panther 5.52, and is used only for sending JSON data to the Jserver.  Also, the name 'President' in the JSON data shown above is ignored, since it is a structural element rather than one containing a simple value.

 

As shown, the internal processing of the Jserver still requires that data be received in the format used for a Content-type of 'application/x-www-form-urlencoded'.  The Panther Requester Servlet automatically converts JSON data (application/json) into this format, as described above, before passing it along to the Jserver.  XML (application/xml) is not supported.  

 

URIs

 

In order to better support RESTful style URIs, if the Accept header of the request contains 'application/json', but  not 'text/html', the Requester Servlet will strip off everything in the URI following the screen name and assign it to a new custom CGI header, EXTRA_PATH_INFO, for the Jserver to receive.  The application code can then retrieve and parse this by means of a new built-in variable, @cgi_extra_path_info.  This technique can be used to apply filters to the data resource represented by the screen. 

 

So, for example, if the 'parts' screen represents a table of parts by means of the  grid widget it contains, then a URI ending in 'parts/500' may represent a single part whose part number is 500.  When the HTTP GET operation is performed on that URI, the 'parts' screen is opened, and a screen entry function can find '/500' in the @cgi_extra_path_info  variable.  It should then retrieve the data for only part number 500.  It may even dynamically change the html_template property of the ‘parts’ screen, if necessary, in order to properly return JSON data for just the one part requested.  Use of this feature is solely at the discretion of the Panther application developer.  Query parameters in the URI may be used to accomplish the same thing.

 

Client State

 

One of the hallmarks of the REST style of architecture is that the server does not maintain client state information from one request to the next.  This presents a hurdle for the migration of typical application code that is designed to function properly only within a particular state that had been achieved by earlier processing, such as, for example, a user having logged into the application.  It is not efficient for each service request to require reauthentication and reauthorization, yet to do otherwise is inconsistent with REST constraints.

 

For its part, a Jserver process is designed to be reusable, and must, therefore, not transfer any state information from one request to the next.   In fact, some care must be taken by the Panther Web application developer that application code does not accidentally do this.  After handling a request from one user, a microsecond later it may handle a request from a different user, and then the next request may be from the initial user once again. 

 

Traditional Panther Web applications usually deal with this by making use of Panther Web's server-side cache mechanism.  Client-side caching is also possible by means of cookies and hidden HTML elements that the Jserver generates, which can then be POSTed back to the Jserver when the user interacts with the Panther generated UI.  The latter is not suitable for a REST and JSON web services implementation, however, and the former violates REST constraints. 

 

In normal processing, the Jserver purges any application state it knows about after responding to a request, in order to be prepared for the next request.  If server-side caching is enabled, and it receives a Panther webid from the client in a new HTTP request, it uses that webid to locate and load the cache containing the previous application state before processing the new request.  For a GET operation, the webid may be passed as a query parameter, like, 'http://...myscreen?@webid=pLCaWhf-1545421892-192525-49-'.  For a POST, the POSTed screen would contain a hidden input element named '__server_data__', whose value contains the webid value.  It looks something like this:

<input name=__server_data__ type=hidden value="pLCaWhf-1545421892-192525-49-">

 

There is nothing to prevent a GET operation from returning the webid within JSON data, and for the following POST of JSON data to provide that same webid in order to trigger the use of a server-side cache, assuming server-side caching is enabled.  The POSTed data could look something like this:

 

{ "HolidayBonus" : 1000000, "__server_data__":"pLCaWhf-1545421892-192525-49-" }

 

External Authentication and Authorization

 

As of Panther Web Application Broker 5.52, a new mechanism to secure access to resources managed by a Panther Web application is supported.  Applications may implement the AuthorizationFactory interface for use with an external authorization mechanism. The external authorization mechanism is specifically for use with Panther's Requester Servlet, and requires the implementation of two Interfaces that are within the com.prolifics.servlet package.

 

These are AuthorizationFactory and Authorization. Classes of the package, com.prolifics.servlet.auth0, provide the implementation for these interfaces for use with Auth0.  Source code for these classes is provided in proauth0.jar.  Javadoc documentation is provided for all classes.  The Javadoc documentation for the AuthorizationFactory interface is reproduced below.  Please see the Javadoc documentation for complete information on all packages and classes.

 

 

public interface AuthorizationFactory

In order to use the AuthorizationFactory interface, the deployment descriptor, web.xml, should contain the servlet property, AuthorizationFactory. The full classname of the implementation class for this interface should be assigned to that property. The Panther Requester Servlet will attempt to load and instantiate an object for the implementation class during initialization of the servlet.

The presence of this servlet property, AuthorizationFactory, causes authorization to be enabled for the Requester Servlet. Otherwise, authorization is not enabled. Thus, if the property is not set, the Requester Servlet will assume that all requests are implicitly authorized. However, this does not preclude the application code running in the Jserver from processing @cgi_http_authorization when it receives the request and denying authorization at that point.

The implementation class for AuthorizationFactory must take a single constructor parameter which is a HashMap. The argument received by the constructor will be a mapping of servlet properties to their values, as given in web.xml. This allows custom properties to be passed to the AuthorizationFactory implementation class.

There is just one method that is required to be implemented for AuthorizationFactory: getAuthorization(). This method takes a single String parameter, and returns an instance of a class that implements the interface, com.prolifics.servlet.Authorization. ProlificsHttpServlet calls getAuthorization() for each request, passing it the value of the HTTP Authorization header that was sent with the request. Use of the factory pattern allows for the implementation to perform caching.

Anything may be used in the Authorization header, but for classes implementing OAuth/OAuth2 security it is typically a bearer token.
getAuthorization() returns an instance of a class that implements the com.prolifics.servlet.Authorization interface.

 

Auth0 Sample

 A sample application demonstrating the use of Auth0 with a REST API that is implemented as a Panther Web application is provided in the ‘auth0’ folder within the ‘samples’ folder.  In addition to demonstrating the use of Auth0, the sample demonstrates the use of a hidden Multiline Text Widget for transferring data to and from the other widgets on a screen in JSON format.   It uses a class, JsonUtils, which extends com.prolifics.jni.ScreenHandlerAdapte, for assistance.  Its screenDataToJSON() method is used to loop through all of the widgets on a Panther screen, format the widget names and values as JSON data, and copy the JSON data into a hidden Multiline Text Widget on the screen.  This allows for a Panther Template for the screen to contain only a single {{raw}} template tag to output only the content of the MLT widget.  Source code is provided.