ID-INFO blog
Web services on the IBM i
You’ve probably heard of web services, but what exactly are they?
If you consult your favorite search engine, you’ll learn that a web service is an interface protocol that enables two applications to talk to each other. These rely on web technologies to enable two applications, which may be completely different, to exchange information.
As a result, web services are often used in distributed environments, where numerous heterogeneous applications need to communicate with each other. What’s more, these tools echo the principles of service-oriented architecture (SOA), which are becoming increasingly common in our information systems, enabling us to be far more flexible in terms of the solutions we can provide.
Today’s web services rely on the Hypertext Transfer Protocol (HTPP) to convey information. Using this standard has the enormous advantage of avoiding a huge number of connectivity problems between our two applications.
Web services are based on client/server exchanges. In practice, the application providing the service (often referred to as “exposing” the service) remains attentive to any solicitations triggered by customers. The request is interpreted by the server, processed, then the response is transmitted to the client application in a form it can understand.
There are 2 main families of web services:
- SOAP web services: Simple Object Access Protocol
- REST web services: Representational State Transfer
The first are based on the protocol of the same name. This has been around for a long time: the initial version defined by Microsoft and iBM dates back 23 years, then became a standard following a W3C recommendation. The protocol relies entirely on the XML language to formally describe exchanges between clients and the server. This formalism (via XML documents called WSDL) has the advantage of providing implicit documentation when using this type of service. Unfortunately, the family suffers from a few drawbacks: the XML language is particularly verbose, and using this type of web service can quickly become complicated. On the other hand, the formalism implies that the client and server are strongly coupled, which, in an SOA approach where flexibility is always a priority, is not necessarily desirable.
These drawbacks led to the emergence of the REST family of web services. These are based on the architectural concepts used in the Web to manipulate remote resources in text form. Unlike SOAP web services, the REST family of web services doesn’t just use XML to exchange information between two client/server applications: formats such as JSON are also commonly used today. To manipulate remote resources, these web services use http methods : POST, GET, PUT, DELETE, … that echo the CRUD development model, which explains why these web services are frequently used to build complete APIs (Application Programming Interface) to interact with application components.
But what about our IBM i? Can it too fit into these distributed environments?
Let me reassure you right away: our favorite platform offers a wealth of tools for exposing and consuming web services. Like many other platforms, we can implement these services in a variety of ways and make them available to our various customers.
To illustrate our point, let’s consider an existing application developed in RPGIV.
To do this, let’s start by creating an environment with a few tables and data. Fortunately for us, IBM has provided us with a one-line SQL stored procedure for this purpose:
CALL QSYS.CREATE_SQL_SAMPLE(‘RHDB’); |
So let’s open our SQL script execution tool in ACS and get started.
As you may have guessed from the name of the collection we’ve created, we’ll be working with human resources data.
A quick tour of the database explorer shows us the tables created.
Let’s take a look at the EMPLOYEE table and write a small program to retrieve information about an employee.
Let’s imagine we have a service program that extracts information about an employee.
/if not defined(IMPORT_PROTOTYPES)
ctl-opt option ( *nodebugio : *srcstmt )
debug
nomain
pgminfo
(
*PCML
:
*MODULE
);
* Parametres SQL.
exec sql
set option commit
=
*none,
closqlcsr
=
*endmod
;
*======================================================================= *
* *
* ATTENTION : *
* *
* ---------------------------------------------------------------------- *
* FONCTION : Module RH. *
* DESCRIPTION : Manipulation donénes du personnel. *
* *
* Creation ... : 25/10/2021 par L. KIEFFER (Notos-Id Info) *
* ---------------------------------------------------------------------- *
* Modification : *
* JJ/MM/AAAA - XXXXXX XXXXXXX - XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX. *
* * * ---------------------------------------------------------------------- *
/endif
*----------------------------------------------------------------------
* Prototype verification sous systeme.
* ---------------------------------------------------------------------
// Information employé.
dcl-ds MODULERH_employe template qualified;
prenom varchar(12);
nom varchar(15);
service varchar(36);
end-ds;
// Recherche d'un employé.
dcl-pr MODULERH_rchEmp;
iNumEmp char( 6 ) const;
oInfEmp likeds ( MODULERH_employe );
oErrHTTP int(5);
oErrMsg varchar(40);
end-pr
;
* Fin des elements exportes.
/if defined(IMPORT_PROTOTYPES)
/eof
/endif
*----------------------------------------------------------------------
* Declarations globales
* ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Recherche d'employé.
// ---------------------------------------------------------------------
dcl-proc MODULERH_rchEmp export;
dcl-pi *n;
iNumEmp char( 6 ) const;
oInfEmp likeds ( MODULERH_employe );
oErrHTTP int(5);
oErrMsg varchar(40);
end-pi
;
// Recherche de l'employé.
exec sql
Select FIRSTNME, LASTNAME, DEPTNAME
into :oInfEmp.prenom, :oInfEmp.nom, :oInfEmp.service
From EMPLOYEE
Join DEPARTMENT on DEPTNO = WORKDEPT
Where EMPNO = :iNumEmp;
if sqlCode < 0;
oErrHTTP = 500;
oErrMsg = 'Erreur recherche de l''employé.';
return;
endif;
if sqlCode = 100;
oErrHTTP = 404;
oErrMsg = 'Employé inconnu.';
return;
endif;
// Tout s'est bien passé.
oErrHTTP = 200;
oErrMsg = *blanks;
return;
end-proc;
Make a note of the highlighted instructions, as we’ll come back to them later when we explain our web service.
To continue, we’ll need a web services server on which to host our services. Fortunately for us, IBM provides one for the IBM i: IWS (Integrated Web Services server).
To access it, use the following url: http: //mon_ibm_i:2001/HTTPAdmin
Where my_ibm_i is your partition’s IP address or DNS name.
WARNING : If the page is not displayed, it’s probably because the web administration application has not been started correctly. Run the following command on a 5250 command line:
STRTCPSVR SERVER(*HTTP) HTTPSVR(*ADMIN) |
Log in using a profile with *IOSYSCFG rights.
On the new page that appears, click on the “Create Web Services Server” link and let the wizard guide you.
Name your server as you like. By default, an Apache instance will be created.
Select the ports used by the server.
You can customize the server job by providing it with a specific jobd.
You can also choose the user profile used by the server job. Don’t forget to define it carefully. A good idea is to set this profile to the owner profile of the database you are going to manipulate. Please note that if you choose the default user QWSERVICE, you will need to assign a password to this user.
Finally, validate the server creation by clicking on the “Finish” button.
Now that we have a server, let’s showcase our first service. To do this, select the previously created server and click on the “Manage Deployed Service” link.
Click on the “Manage Deployed Service” button. Then click on the “Deploy” button.
The first page of the wizard lets you select the executable to implement your service. Since V7.3, you can also use the sql language to implement the service.
For RPG IV implementations, only executable components can be selected (programs and service programs). In our case, let’s choose our MODULERH service program.
The next screen allows us to choose a resource name. In our case, let’s call it “employes” since we’re handling employee data.
The following page lets you restrict access to the web service to a secure port (via SSL). Since we haven’t secured our http server, let’s answer “No”.
We can also request authentication for this web service.
In this case, only “basic” authentication is available. In this case, the client must provide a user and password valid on the system in the “Authorization” http header.
The next step in the case of service programs is to select the exported procedure implementing the service (in this case modulateh_rchemp).
The assistant will then ask you how to notify the customer that the service has been carried out as planned or with an error. Here you can change the default return codes and error messages.
The next page is the most important. It describes how customers will access your service.
We can define :
- The http verb used (here GET)
- The url model used for access
This allows us to define where our parameters are placed when they are supplied directly in the URL. The syntax to use is: /{nom_parametre}
Please note that you can also add a regular expression to the syntax to check the validity of the parameter entered (here \d+ for a numeric string of at least one digit).
- An output variable to feed back http code (must be defined as an integer in the RPG implementing the service).
- An alphanumeric output variable that replaces the default error message.
- Accepted input data types (*All corresponds to all supported types: xml, json, form data, … => in this case, the client must specify in which type of information it provides for the server to understand it).
- Returned data types: the tool can return data in xml or json format. Here again, if the server can provide both, the customer will have to specify which one he prefers.
- How input and output data are presented: encapsulated or by name.
Finally, this screen allows you to indicate where the input parameters will be retrieved from. In our case, the url using the num identifier (remember this is the one defined in the url template).
The next screen lets you define a specific user to run this web service. The general server profile must have access to this web service-specific profile.
The next screen allows you to define the libraries to be added to the runtime environment.
The penultimate screen lets you configure the elements to be passed to the program implementing the service.
Finally, the last screen summarizes the elements entered. Click the “Finish” button to deploy and start the web service.
But how do you test our famous web service?
Since the web service uses the GET verb, a simple browser will allow us to test it.
For more complex cases, you can turn to more comprehensive tools such as :
- CURL (if command lines don’t put you off): this allows you to configure your entire request (authentication, http headers, etc.).
- PostMan: graphics tool just as complete as CURL.
A little example with PostMan: let’s add an http header indicating that we only want JSON for the response (remember, our web service can provide xml or json).
The ‘R’ of our CRUD API is now in place, and all that’s left is to create the other procedures implementing the missing functions. Don’t forget the importance of http verbs when setting all this up:
C => POST
R => READ
U => PUT
And D => DELETE.
I hope this post has clarified the subject a little. We’ll see you next time, when we’ll be able to project ourselves into the customer’s shoes rather than the waiter’s.
Do you have any questions about web services or your IBM i platform? Then contact us on 01 88 32 12 34, or via the contact form.