REST easy with kbmMW #3 – SSL

I have in the former two “REST easy with kbmMW” articles shown, how to make a REST server with kbmMW, and how to use that REST server to easily return and store data from/to a database all in less than 30 lines of real code.

This blog post will center around how to ensure that communication with the server stays protected using SSL (Secure Socket Layer). In other words, how to make the REST server talk HTTPS rather than HTTP.

There are multiple ways to secure a kbmMW based application server with SSL, but I will focus on one simple way to do it using OpenSSL.

First we should create a certificate we can use. SSL certificates can be purchased from various places where they sell official certificates, or you can create one that is self signed. A self signed certificate is generally as secure as anything else, but it is not automatically trusted by other servers, which may flag your certificate as unsafe.

For inhouse use however, a self signed certificate is usually fine.

There are many places on the internet explaining the procedure of how to create SSL certificates using OpenSSL. You can click here for one of them.

After you have gotten access to either a bought or a homemade certificate, we can continue integrating it with the REST server.

Add a TIdServerIOHandlerSSLOpenSSL component to the main form (Unit7).

2017-05-21 22_13_49-Project6 - RAD Studio 10.1 Berlin - Unit7.png

You will need to set it’s SSLOptions properties like this:

  • SSLOptions.Mode must be sslmServer
  • Of the supported SSLOptions.SSLVersions I will suggest enabling only sslvTLSv1_2

Leave the remaining properties as is at the moment.

Now double click the OnGetPassword event handler of the IdServerIOHandlerSSLOpenSSL1 component to write some code in an event handler.

procedure TForm7.IdServerIOHandlerSSLOpenSSL1GetPassword(var Password: string);
begin
 Password:='yourCertificatePassword';
end;

The code going in the event should simply return the password you used when you created the private part of the certificate. It is required by OpenSSL to have access to this, to be able to use your private key.

Despite the above example, I would suggest you, not to hardcode the password inside your application, but rather read it from an external configuration file, of security reasons, in case your REST server executable got leaked elsewhere.

But for the current sample, with a homemade sample certificate, we can do with the hardcoded password.

Next we need to tell the kbmMW server side transport to let the OpenSSL code handle the main communication of our data. One part of that is to write an event handler for the OnConnect event of the kbmMWTCPIPIndyServerTransport1 component.

procedure TForm7.kbmMWTCPIPIndyServerTransport1Connect(AContext: TIdContext);
begin
  if AContext.Connection.IOHandler is TIdSSLIOHandlerSocketBase then
     TIdSSLIOHandlerSocketBase(AContext.Connection.IOHandler).
     PassThrough:=false;
end;

To make the compiler happy, we also need to add IdContext to the interface sections uses clause.

uses
 Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
 Vcl.Controls, Vcl.Forms, Vcl.Dialogs, kbmMWCustomTransport, kbmMWServer,
 kbmMWTCPIPIndyServerTransport, kbmMWRESTTransStream,
 kbmMWCustomConnectionPool, kbmMWCustomSQLMetaData, kbmMWSQLiteMetaData,
 kbmMWSQLite, kbmMWORM, IdBaseComponent, IdComponent, IdServerIOHandler, IdSSL,
 IdSSLOpenSSL, IdContext;

And finally we need to link the SSL component to the server transport, and make the certificate files available to OpenSSL.

We do that by writing the following piece of code, for example in the OnCreate event handler of the main form (containing the kbmMW Indy server transport).

procedure TForm7.FormCreate(Sender: TObject);
begin
 ORM:=TkbmMWORM.Create(kbmMWSQLiteConnectionPool1);
 ORM.CreateTable(TContact);

 // Make sure that the server is now listening on port 443 which is 
 // the default port for HTTPS communication.
 kbmMWTCPIPIndyServerTransport1.Bindings.Items[0].Port:=443;
 IdServerIOHandlerSSLOpenSSL1.SSLOptions.CertFile:='YourCertificateFile.cer';
 IdServerIOHandlerSSLOpenSSL1.SSLOptions.KeyFile:='YourPrivateKeyFile.key';

 // Optional root certificate file if purchased certificate or empty if self signed.
 IdServerIOHandlerSSLOpenSSL1.SSLOptions.RootCertFile:='';
 kbmMWTCPIPIndyServerTransport1.IdTCPServer.IOHandler:=IdServerIOHandlerSSLOpenSSL1;

 kbmMWServer1.AutoRegisterServices;
 kbmMWServer1.Active:=true;
end;

Make sure that the files YourCertificateFile.cer and YourPrivateKeyFile.key is available to the REST executable when it runs, but also make sure that they are not accessible for download for anyone else. It’s of high importance that those files (along with your private key password) is kept a secret for anybody else.

As you may notice, we change the port number for the first binding from 80 to 443, since we want to support the standard HTTPS port. You may also notice that it is possible to provide a Root certificate file. The Root certificate file typically contains a chain of public certificates that can be used by OpenSSL and browsers to verify that your own certificate is a valid and trusted certificate generated by the entity from which you have the root certificate.

Self signed certificates usually do not need any root certificate files.

Now your REST server is ready serving clients securely over SSL.

REST easy with kbmMW #2 – Database

Building on the “REST easy with kbmMW #1” article, I will now show one way to interact with data from a database.

kbmMW supports multiple ways to access databases, ranging from more complex to use but with absolutely fastest performance, to very easy to use with very good performance.

The good thing is that you can pick and choose the way you want to use, depending on the scenario. In 99.99% of all cases, the very easy way most likely will be the preferred way. In those very few scenarios where you really need to get the highest reachable performance out of your hardware, you may want to go a less abstracted route, which you can do fully supported by kbmMW.

But as the easy way is the preferred way, this is what I will show now.

Our previous REST sample only displayed Hello world in a number of different ways.

It would be more fun to create a contact management REST server as it better resembles typical use cases for REST servers.

First thing is to make the server understand databases. We do that by opening the primary form (Unit7 in our previous blogpost), and adding one of each of these components:

  • TkbmSQLiteConnectionPool
  • TkbmMWSQLMetaData

Doing that, your form will look something similar to this.

2017-05-20 19_21_16-Project6 - RAD Studio 10.1 Berlin - Unit7 [Built].png

Then set the Metadata property of the kbmMWSQLiteConnectionPool1 component to kbmMWSQLiteMetaData1.

And set the Database property of the kbmMWSQLiteConnectionPool1 component to the string ‘.\test.db3′.

Set mwsloAutoCreateDB in the SQLiteOptions property of the kbmMWSQLiteConnectionPool1.

And if the Active property of kbmMWSQLiteConnectionPool1 is true, set it to false.

What we have done, is prepared the server to work with a SQLite database. We do need to make sure that a reasonably new sqlite3.dll (if on Windows) is to be found in the same directory as the executable of this project. You can download SQLite from http://www.sqlite.org

We have also told kbmMW that we want kbmMW to auto create the referenced database, if it do not exist.

Save the project. It will make sure Delphi updates referenced units.

Right now we already have access to the kbmMW’s fast database access methods (the 0.01% use case mentioned above). But as I want to demonstrate the easy database access route, we should do add a little bit of code to the Unit7 main form.

As I now we will need to put a single line of code into the form’s OnDestroy event, let us do that now. Double click the OnDestroy event of the form in the object inspector, and we will be brought to the source code. Add the following line:

     ORM.Free;

In the already existing OnCreate event of the form, we also need to add a line of code:

     ORM:=TkbmMWORM.Create(kbmMWSQLiteConnectionPool1);

In the form’s public interface section we should need to add a new field:

    ORM:TkbmMWORM;

And finally in the uses clause in the interface section we must add the kbmMWORM unit.

The source of your main server form should now look similar to this. The code we have just updated is shown in bold.

unit Unit7;

interface

uses
 Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
 Vcl.Controls, Vcl.Forms, Vcl.Dialogs, kbmMWCustomTransport, kbmMWServer,
 kbmMWTCPIPIndyServerTransport, kbmMWRESTTransStream,
 kbmMWCustomConnectionPool, kbmMWCustomSQLMetaData, kbmMWSQLiteMetaData,
 kbmMWSQLite, kbmMWORM;

type
 TForm7 = class(TForm)
 kbmMWServer1: TkbmMWServer;
 kbmMWTCPIPIndyServerTransport1: TkbmMWTCPIPIndyServerTransport;
 kbmMWSQLiteConnectionPool1: TkbmMWSQLiteConnectionPool;
 kbmMWSQLiteMetaData1: TkbmMWSQLiteMetaData;
 procedure FormCreate(Sender: TObject);
 procedure FormDestroy(Sender: TObject);
 private
 { Private declarations }
 public
 { Public declarations }
 ORM:TkbmMWORM;
 end;

var
 Form7: TForm7;

implementation

{$R *.dfm}

procedure TForm7.FormCreate(Sender: TObject);
begin
 ORM:=TkbmMWORM.Create(kbmMWSQLiteConnectionPool1);
 kbmMWServer1.AutoRegisterServices;
 kbmMWServer1.Active:=true;
end;

procedure TForm7.FormDestroy(Sender: TObject);
begin
 ORM.Free;
end;

end.

Next thing to think about is what are we going to store in the database. As it’s a contact management system, we might want to store contact name, address, zip code, city and a comment.

For some contacts we may not know the address, or city, or we may not have specified a comment. So our contact storage must be able to differentiate between data and no data for any of the stored values. Fortunately that is how most SQL type databases operate. They understand a state of NULL for each field defined in them. That state can be checked to see if the field contains a value or not. An empty string is considered a value.

So let’s make a Delphi object that can contain this information.

Create a new unit by clicking File – New – Unit in Delphi.

This gives us an empty source only unit. The intention with it, is that it will hold the definition of our contact object.

The basic TContact class could look like this

 TContact = class
 private
  FID:kbmMWNullable<string>;
  FName:kbmMWNullable<string>;
  FAddress:kbmMWNullable<string>;
  FZipCode:kbmMWNullable<integer>;
  FCity:kbmMWNullable<string>;
  FComments:kbmMWNullable<string>;
 public
  property ID:kbmMWNullable<string> read FID write FID;
  property Name:kbmMWNullable<string> read FName write FName;
  property Address:kbmMWNullable<string> read FAddress write FAddress;
  property ZipCode:kbmMWNullable<integer> read FZipCode write FZipCode;
  property City:kbmMWNullable<string> read FCity write FCity;
  property Comments:kbmMWNullable<string> read FComments write FComments;
 end;

You will notice that there is an additional field named FID (with its property description). The idea is that we want to be able to identify a specific contact uniquely later on, without having to use the name or other information type fields (since the contents of those could change by the end user).

There are multiple ways to define a unique identifier. Some may have heard about auto incrementing fields. Some may have heard about sequencers or generators (which is sort of the same as auto incrementing fields, just in a different packing), and then some may have heard about using GUID‘s (Globally Unique IDentifiers) which often are represented as 38 character strings.

I personally prefer using GUID’s for unique identifiers, because it allows me to import the database to another system, and even to integrate data across different database systems, without risking accidental renumbering of auto incrementing identifiers.

Performance wise GUID’s are slightly slower and takes up additional space in a database, but that is usually only an issue to consider if you know you will have tens of millions of records in a table. If that’s not the case I recommend using GUIDs. I will show in a moment how.

But first I will talk about the other weird thing in the object… the kbmMWNullable generic type.

It allow for a field variable to contain both the value (for name a string), and an indication about if the variable is null or not null.  In addition it also keeps track of if the value in the field has been modified or not, which can be very practical to know about  when the object is to be used to magically make the contents of the object be part of the database.

In fact, every time you have data in an object you would like to put into a database, wrap its fields with kbmMWNullable. The only exception is if your field is of type TkbmMWDataTime (which is a kbmMW TDateTime like type that also understands timezones and more), since it by itself is null and modification aware.

Back to the GUID. We want the ID property to be used as the primary identification for our contact. So let’s tell kbmMW that by adding an attribute to it.

 [kbmMW_Field('name:id, primary:true, generator:shortGuid',ftString,40)]
 property ID:kbmMWNullable<string> read FID write FID;

We have told kbmMW to handle this property as a field named ‘id’ in a data storage (like our database) and that should be stored as the datatype ftString with the size of max 40 characters. In addition the field is to be considered the primary point of access to data in the database for the TContact type information.

We have also told kbmMW that we want the actual contents of this field to be automatically generated using the shortGUID generator which produces string GUID’s without dashes and curly braces.

Now let us create similar attributes for the remaining properties, so kbmMW knows what to do about them in relation to a database.

And finally we might also want to tell kbmMW that this TContact object is to be stored in a container (usually a table) with a specific name in the database.

As in blog post #1 we also need to register the class to kbmMW. The registration can be done any time, as long as it is before first time the object is to be used by kbmMW. Typically registration is put into the initialization section of the unit. And a couple of units, DB, kbmMWNullable, kbmMWRTTI and kbmMWORM must be added to the uses clause. Now we have defined the skeleton around what a contact is, and we have a place to put the information about a single contact. But what about multiple contacts? That’s easy.. we consider multiple contacts as being a list of contacts. In Delphi code terms using generics: TObjectList<TContact>

This object list also needs to be registered with kbmMW and the Delphi generics list unit (System.Generics.Collections) must be added. The resulting unit will look like this:

unit Unit9;

interface

uses
 DB,
 System.Generics.Collections,
 kbmMWRTTI,
 kbmMWORM,
 kbmMWNullable;

type

[kbmMW_Table('name:contact')]
TContact = class
private
 FID:kbmMWNullable<string>;
 FName:kbmMWNullable<string>;
 FAddress:kbmMWNullable<string>;
 FZipCode:kbmMWNullable<integer>;
 FCity:kbmMWNullable<string>;
 FComments:kbmMWNullable<string>;
public
 [kbmMW_Field('primary:true, generator:shortGuid',ftString,40)]
 property ID:kbmMWNullable<string> read FID write FID;

 [kbmMW_Field('name:name',ftString,50)] 
 property Name:kbmMWNullable<string> read FName write FName;

 [kbmMW_Field('name:address',ftString,80)]
 property Address:kbmMWNullable<string> read FAddress write FAddress;

 [kbmMW_Field('name:zipCode',ftInteger)]
 property ZipCode:kbmMWNullable<integer> read FZipCode write FZipCode;

 [kbmMW_Field('name:city',ftString,50)]
 property City:kbmMWNullable<string> read FCity write FCity;

 [kbmMW_Field('name:comments',ftMemo)]
 property Comments:kbmMWNullable<string> read FComments write FComments;
end;

implementation

initialization
 kbmMWRegisterKnownClasses([TContact,TObjectList<TContact>]);
end.

 

So we now have the understanding of a single contact and a list of contacts. That should cover our data type needs for now.

Save all.

Next we need to establish a place in the SQLite database where this data can be stored. This is fortunately very easy to do. Open up your main form, and add this new data unit (Unit9) to the uses clause of the main form’s implementation section like this:

...
var
 Form7: TForm7;

implementation

uses Unit9;
...

And a good place to tell kbmMW where to store TContact type data, could be in the form’s OnCreate event.

procedure TForm7.FormCreate(Sender: TObject);
begin
 ORM:=TkbmMWORM.Create(kbmMWSQLiteConnectionPool1);
 ORM.CreateTable(TContact);
 kbmMWServer1.AutoRegisterServices;
 kbmMWServer1.Active:=true;
end;

kbmMW’s easy to use ORM (Object Relational Manager), automatically detects if the database already contains TContact type data or not. If it does not, kbmMW prepares the database to hold such information.

If we would run the application now, a file called test.db3 is created and using a SQLite database exploration tool like kbmMWSQLiteMan (which can be downloaded for free after registering on our portal at https://portal.components4developers.com), you can see that the database has been prepared with an empty table.

2017-05-20 20_48_19-kbmSQLiteMan v.1.6.3.2.png

Now we can start to write some REST functionality to access and manipulate the contacts data.

Open Unit8 (the kbmMW smart service).

We will add a functions to return all contacts known to the system, to add a contact only providing name, to add a contact providing name, address, zipcode and city, and to delete a contact by it’s id.

The service will then look like this:

 

 [kbmMW_Service('name:MyREST, flags:[listed]')]
 [kbmMW_Rest('path:/MyREST')]
 TkbmMWCustomSmartService8 = class(TkbmMWCustomSmartService)
 public
   [kbmMW_Rest('method:get, path:helloworld, anonymousResult:true')]
   [kbmMW_Method]
   function HelloWorld:TMyResult;

   [kbmMW_Rest('method:get, path:contacts, anonymousResult:true')]
   function GetContacts:TObjectList<TContact>;

   [kbmMW_Rest('method:put, path:addcontact')]
   function AddContact(
     [kbmMW_Rest('value:"{$name}"')] const AName:string;
     [kbmMW_Rest('value:"{$address}"')] const AAddress:string;
     [kbmMW_Rest('value:"{$zipcode}"')] const AZipCode:string;
     [kbmMW_Rest('value:"{$city}"')] const ACity:string):string; overload;

   [kbmMW_Rest('method:get, path:"addcontact/{name}"')]
   function AddContact([kbmMW_Rest('value:"{name}"')] const AName:string):string; overload;

   [kbmMW_Rest('method:delete, path:"contact/{id}"')]
   function DeleteContact([kbmMW_Rest('value:"{id}"')] const AID:string):boolean;
 end;

You can see a number of new functions.

  • GetContacts method which will return a TObjectList of TContact. We have told kbmMW that we want it to be called when people use this URL to perform a HTTP GET request: http://localhost/MyREST/contacts
    The result will be a JSON array of contact information.
  • AddContact method which will take a name, address, zipcode and city and return a string (the id of the newly created contact). It will be activated the moment someone makes an HTTP PUT request on http://localhost/MyREST/addcontact
    The caller should include HTML form fields named name, address, zipcode and city. kbmMW will automatically attempt to pair the provided formfields with the arguments for the AddContacts method according to the mapping shown.
  • AddContact method which will take a name only, and return a string (the id of the newly created contact). It will be called when someone makes an HTTP GET request on http://localhost/MyREST/addcontact/somename
    The somename part should be the name of the contact to add.
  • DeleteContact method which will take an id, and return a boolean true/false for if the operation succeeded. It will be called when someone makes an HTTP DELETE request on http://localhost/MyREST/contact/someid
    The someid part should be the id of the contact to delete.

The implementation of these functions is very simple. Make sure that Unit7 (the main form unit) is added to the uses clause of the interface section of Unit8 (the kbmMW smart service module), and add the following code:

function TkbmMWCustomSmartService8.GetContacts:TObjectList<TContact>;
begin
 Result:=Form7.ORM.QueryList<TContact>;
end;

function TkbmMWCustomSmartService8.AddContact(const AName:string;
 const AAddress:string;
 const AZipCode:string;
 const ACity:string):string;
var
 c:TContact;
begin
 c:=TContact.Create;
 c.Name:=AName;
 c.Address:=AAddress;
 c.ZipCode:=AZipCode;
 c.City:=ACity;
 Form7.ORM.Persist(c);
 Result:=c.ID;
end;

function TkbmMWCustomSmartService8.AddContact(const AName:string):string;
var
 c:TContact;
begin
 c:=TContact.Create;
 c.Name:=AName;
 Form7.ORM.Persist(c);
 Result:=c.ID;
end;

function TkbmMWCustomSmartService8.DeleteContact(const AID:string):boolean;
begin
 Form7.ORM.Delete<TContact>([AID]);
end;

That is really all that is needed to enable returning a list of contacts, adding new contacts and deleting a contact. kbmMW’s ORM makes all the work for us automatically.

Basically a REST enabled contact manager in only 30 lines of actual code and a definition of a TContact class.

This obviously can be expanded on in many ways, but I will leave that up to the reader.

Feel free to share, retweet, repost, comment and like this post! 🙂

 

 

REST easy with kbmMW #1

REST servers are very easy to make using kbmMW.

First we make a server application (or service… it’s up to you).

In this case we will add a simple form both providing a GUI and a place for our kbmMW components.

In Delphi click File – New – VCL Forms application

Add one of each of the following kbmMW components to the form:

  • TkbmMWServer
  • TkbmMWTCPIPIndyServerTransport

2017-05-20 14_50_54-Project6 - RAD Studio 10.1 Berlin - Unit7.png

Set the Server property of kbmMWTCPIPIndyServerTransport1 to kbmMWServer1.

Double click the Bindings property of kbmMWTCPIPIndyServerTransport1 to open its editor. Add a binding for 0.0.0.0 port 80, which is the default HTTP server port. You can choose any other binding you want, but make sure to tell your REST users which port to access.2017-05-20 14_53_34-Project6 - RAD Studio 10.1 Berlin - Unit7.png

Set the Streamformat property of kbmMWTCPIPIndyTransport1 to REST.2017-05-20 14_56_15-Project6 - RAD Studio 10.1 Berlin - Unit7.png

Save your project. Saving it will prompt Delphi to update your uses section with required units.

Double click on the form, to create a form OnCreate event handler.

Enter two lines of code so it looks like this:

procedure TForm7.FormCreate(Sender: TObject);
begin
 kbmMWServer1.AutoRegisterServices;
 kbmMWServer1.Active:=true;
end;

Locate the interface section’s uses clause and add an additional unit kbmMWRESTTransStream.

Save again.

Now your unit code should look similar to this:

unit Unit7;

interface

uses
 Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
 Vcl.Controls, Vcl.Forms, Vcl.Dialogs, kbmMWCustomTransport, kbmMWServer,
 kbmMWTCPIPIndyServerTransport, kbmMWRESTTransStream;

type
 TForm7 = class(TForm)
 kbmMWServer1: TkbmMWServer;
 kbmMWTCPIPIndyServerTransport1: TkbmMWTCPIPIndyServerTransport;
 procedure FormCreate(Sender: TObject);
 private
 { Private declarations }
 public
 { Public declarations }
 end;

var
 Form7: TForm7;

implementation

{$R *.dfm}

procedure TForm7.FormCreate(Sender: TObject);
begin
 kbmMWServer1.AutoRegisterServices;
 kbmMWServer1.Active:=true;
end;

end.

Basically we now have the foundation for a REST capable web server.

Let’s add some functionality that can be called from any REST client.

In Delphi, Click FileNewOtherComponents4Developers Wizards and select the kbmMW Service Wizard. Click OK.

2017-05-20 15_09_18-Project6 - RAD Studio 10.1 Berlin - Unit7.png

Before continuing to select the type of kbmMW service we will add, we need to decide what type of REST server we want to create. It can be a pure REST server, which only serves data from your own code, or it can be a regular web server, which will also be able to serve data from files on disk, like html templates, images, CSS files etc.

For our purpose we just want to make a pure REST server, so we select
Smart service/kbmMW_1.0.

If you wanted it to be able to serve files from disk, and even proxy requests on to other FastCGI compatible servers (like PHP etc) you would have chosen HTTP Smart service.

2017-05-20 15_13_56-kbmMW service wizard.png

Click the funny looking next button. Type in the default name your REST service should be known as. In this sample, I’ve called it MyREST.

2017-05-20 15_15_16-Project6 - RAD Studio 10.1 Berlin - Unit7.png

Click next until you get to this page, then click the green checkmark button

2017-05-20 15_16_42-kbmMW service wizard.png

Now an almost empty service has been generated for you.

On the surface it looks like a regular TDataModule, and as such can contain any component that you can put on a TDataModule. But right now we are more interested in its code. Press F12 to switch to code view.

Browse past the explanatory remarks at the top, until you get to the actual code.

type

[kbmMW_Service('name:MyREST, flags:[listed]')]
[kbmMW_Rest('path:/MyREST')]
 // Access to the service can be limited using the [kbmMW_Auth..] attribute.
 // [kbmMW_Auth('role:[SomeRole,SomeOtherRole], grant:true')]

TkbmMWCustomSmartService8 = class(TkbmMWCustomSmartService)
 private
 { Private declarations }
 protected
 { Protected declarations }
 public
 { Public declarations }
 // HelloWorld function callable from both a regular client,
 // due to the optional [kbmMW_Method] attribute,
 // and from a REST client due to the optional [kbmMW_Rest] attribute.
 // The access path to the function from a REST client (like a browser)+
 // is in this case relative to the services path.
 // In this example: http://.../MyREST/helloworld
 // Access to the function can be limited using the [kbmMW_Auth..] attribute.
 // [kbmMW_Auth('role:[SomeRole,SomeOtherRole], grant:true')]
 [kbmMW_Rest('method:get, path:helloworld')]
 [kbmMW_Method]
 function HelloWorld:string;
 end;

implementation

uses kbmMWExceptions;

{$R *.dfm}


// Service definitions.
//---------------------

function TkbmMWCustomSmartService8.HelloWorld:string;
begin
 Result:='Hello world';
end;

The interesting bits are shown above in bold.

If you compile and run your application now, you have a REST only capable webserver which have one function… HelloWorld taking no arguments, and that returns a string.

Open up your favorite web browser and lets test the function by typing this in the address field:

http://localhost/MyREST/helloworld

Make sure that case is correct, since the HTTP standard describes that the URL part of the address must be case sensitive. If you would write http://localhost/MyREST/HelloWorld instead you would be told that the request is invalid.

This is all nice… but my REST client expect to receive a JSON object, not just simple text.

Ok.. I’ll show 3 ways to do that… the very manual way, the semi automated way and the fully automated way.

The manual way. Change the HelloWorld function to look like this:

function TkbmMWCustomSmartService8.HelloWorld:string;
begin
 Result:='{''result'':''Hello world''}';
end;

The REST client will receive an anonymous object with a property called result, containing “Hello world”.

The semi automated way:

uses kbmMWExceptions
 ,kbmMWObjectNotation
 ,kbmMWJSON;

{$R *.dfm}


// Service definitions.
//---------------------

function TkbmMWCustomSmartService8.HelloWorld:string;
var
 o:TkbmMWONObject;
 jsonstreamer:TkbmMWJSONStreamer;
begin
 o:=TkbmMWONObject.Create;
 jsonstreamer:=TkbmMWJSONStreamer.Create;

 o.AsString['result']:='Hello world';
 Result:=jsonstreamer.SaveToUTF16String(o);

 jsonstreamer.Free;
 o.Free;
end;

This allows you to create complex JSON documents pretty easily. The cool part is that since we use kbmMW’s object notation framework, we could have chosen to stream it as XML or YAML or BSON or MessagePack instead by simply instantiating the appropriate streamer.

The automated way:

type

TMyResult = class
private
 FResult:string;
public
 property Result:string read FResult write FResult;
end;

 [kbmMW_Service('name:MyREST, flags:[listed]')]
 [kbmMW_Rest('path:/MyREST')]
 // Access to the service can be limited using the [kbmMW_Auth..] attribute.
 // [kbmMW_Auth('role:[SomeRole,SomeOtherRole], grant:true')]

TkbmMWCustomSmartService8 = class(TkbmMWCustomSmartService)
 private
 [kbmMW_Rest('method:get, path:helloworld, anonymousResult:true')]
 [kbmMW_Method]
 function HelloWorld:TMyResult;
 end;

implementation

uses kbmMWExceptions;

{$R *.dfm}

// Service definitions.
//---------------------

function TkbmMWCustomSmartService8.HelloWorld:TMyResult;
begin
 Result:=TMyResult.Create;
 Result.Result:='Hello world';
end;

initialization
 TkbmMWRTTI.EnableRTTI(TkbmMWCustomSmartService8);
 kbmMWRegisterKnownClasses([TMyResult]);
end.

The automated way simply means returning an object with the desired information. kbmMW will automatically convert the object to JSON (because we are using the REST streamformat).

To make sure that kbmMW knows about the object type, we register it via the kbmMWRegisterKnownClasses. If we didn’t, kbmMW would complain that it do not know about the object.

Do not worry about the TMyResult instance being leaked. kbmMW will automatically free it when it goes out of scope. If you specifically do not want the returned object to be freed by kbmMW, you can tell so by including freeResult:false in the kbmMW_Rest attribute for the HelloWorld method.

Also notice that the kbmMW_Rest attribute now includes anonymousResult:true.

This tells kbmMW that we want the resulting JSON to be anonymous. If we didn’t include that attribute setting, the result would have looked like this:

{"TMyResult":{"Result":"Hello world"}}

Which is not necessarily wrong, but different.

There are lots of control options of how the object should be streamed by setting various attributes on it. One can for example choose that the Result property should be returned under a different name etc.

kbmMW also understands returning TkbmMemTable instances, arrays and many other types of information, so it is really easy to REST’ify your kbmMW business functionality with almost no lines of additional code.

As a final comment, since the HelloWorld method is also tagged with the attribute [kbmMW_Method], it is also callable by native kbmMW clients.

Feel free to share, comment and like this post 🙂

 

Getting a free kbmMemTable and kbmMW

How do I get a free kbmMemTable or kbmMW?

We provide a free license which can also be used for commercial applications. What you should look for is kbmMW CodeGear Edition.

The installer of kbmMW CodeGear Edition includes kbmMemTable CodeGear Edition, which thus installs automatically along with kbmMW CodeGear Edition.

Get kbmMW CodeGear Edition by logging in on https://portal.components4developers.com

If you have never logged in on our download portal before, you will need to register yourself first. We do not verify the information you provide for name etc, but please, as a courtesy to us, type in your real name and accessible email address. as a minimum.

When you have logged in, you will have direct access to all the free products we currently offer, and in all the versions we have made available.

kbmMW CodeGear Edition do not include source code, so you will have to choose a version matching the development environment you have. CodeGear Edition only supports Delphi.

Further kbmMW CodeGear Edition is updated less regular than our paid versions of kbmMemTable and kbmMW. Latest version released of CodeGear Edition will only match latest version of Delphi. Thus the older Delphi version you will want to download for, the older version of kbmMW CodeGear Edition you will need to download.

What do you get with kbmMW CodeGear Edition?

  • A complete application server framework
  • Standard TCP/IP based communication channel
  • Remote dataset support with resolving features
  • Server side support for FireDAC, DBExpress,  IBExpress and SQLite
  • Advanced logging framework
  • Native XML support
  • Native JSON support
  • ISAPI communication support
  • Support for a subset of compression libraries
  • Support for a subset of hashing methods
  • Advanced DateTime with Timezone support
  • Server and client side dataset caching
  • Advanced actor, role, resource, condition based authorization manager
  • kbmMemTable with without source, but with indexing, filtering, ordering, SQL and more support

What  do you not get with kbmMW CodeGear Edition?

  • Source
  • Latest version
  • No limitations in server and client side caching
  • No limitations in number of concurrent server service instances
  • 35+ more database adapters
  • YAML, BSON, MessagePack support
  • Loadbalancing and failover
  • Messaging
  • High quality encryption functionality including AES and many other algorithms
  • High quality hashing functionality including SHA256/512 and many other algorithms
  • High quality readable password generators
  • High quality random generator algorithms
  • High performance compression algorithms
  • C++ support
  • Smart REST support for boilerplate free code
  • Smart services and clients for boilerplate free code
  • File service and client support
  • Advanced job scheduler support
  • XSD importer support
  • Apache Message Queue Protocol support
  • Advanced debugging support
  • HTTPSys high performance transport for REST
  • Integration support with .Net, Java, Apache Flex, Adobe Flash, PHP, K&R C, OCX
  • Most complete and performant multi monitor remote desktop support
  • Boilerplate free ORM support
  • And many more things

The above features are all available in kbmMW Enterprise Edition. kbmMW Professional Edition contains a subset of them. Both kbmMW Enterprise Edition and kbmMW Professional Edition includes source.

Please check our site for detailed feature matrix.

 

kbmMW Scheduler Tidbits #4

The kbmMW Scheduler schedules jobs. The jobs may be one time or recurrent jobs.

Some jobs may not be critical related to the exactness of their executing time on the mSec (like generating a report, doing a backup, importing some data and other such housekeeping and maintenance), while others may require as accurate as possible execution, for example sampling data, screen grabbing or stuff like that.

The scheduler can do both.

When defining a recurrent job, you typically also tells it with which interval it should run. The scheduler contains multiple fluent expressions to define the interval, like EveryMinute, EveryHour, EveryDay, EverySecond, EveryMSecond etc. which all takes floating point values.

Hence you can easily schedule (and activate) a job to run every 1.5 hour, by writing this:

Scheduler.Schedule(somejob).EveryHour(1.5).Activate;

kbmMW default assumes that jobs scheduled with an interval less than 2 seconds should be considered “precise” jobs, while others should be considered “relaxed” jobs.

A “Relaxed” job is a job, that is being assigned a thread from a thread pool, when it’s about to execute. If there are no threads available, the job is put on hold until a thread is free.

Thus Relaxed jobs often share one or a few threads to run, and thus are very gentle to the system resources. But the downside is that they may not execute completely on time. Their execution may be delayed a few mSecs to even minutes or hours, depending on the available number of threads in the relaxed thread pool, and the load of the scheduled relaxed jobs. Typically any delays will be minimal, but there is no guarantee.

A “Precise” job is a job that from time of defining the job, will have its own thread assigned to it, and thus will run independently of all other jobs.

As mentioned kbmMW automatically decides, upon scheduling time, which mode the job is to take, relaxed or precise. But you can override this manually by specifically stating either Relaxed or Precise as part of the fluent expression. E.g.

// Schedule a job in its own thread, running every 1.5 hour.
Scheduler.Schedule(somejob).EveryHour(1.5).Precise.Activate;

// Schedule a job to be run every 100 msecs, from a shared thread from the relaxed threadpool.
Scheduler.Schedule(somejob).EveryMSecond(100).Relaxed.Activate;

 

In a previous Scheduler Tidbit blog post, I mentioned the new CronTab scheduling style. Since UNIX cron only has an interval resolution of 1 minute, scheduling such a job using kbmMW’s scheduler also will predefine the use of a one minute interval, and thus all cron jobs will default be scheduled as relaxed jobs.

However you can choose a different interval resolution by using any of the Every…. fluent expressions before the Cron expression. Eg.

// This will schedule a job to be run every minute from 5:00 to 5:59 every day.
// The job will default be considered a relaxed scheduled job.
Scheduler.Schedule(somejob).Cron('* 5 * * *').Activate;
// This will schedule a job to be run every second from 5:00 to 5:59 every day.
// The job will default be considered a precise scheduled job.
Scheduler.Schedule(somejob).EverySecond(1).Cron('* 5 * * *').Activate;

If you state the Every… expression after the Cron expression, it will add to the default 1 minute cron schedule. Eg.

// This will schedule a job to be run every 1.5 minute from 5:00 to 5:59 every day.
Scheduler.Schedule(somejob).Cron('* 5 * * *').EverySecond(30).Activate;

kbmMW Scheduler Tidbits #3

What happens if a scheduled repeating job is taking longer time to finish than expected, thus missing next scheduled interval?

Depending on the amount of the delay and criterias for the schedule, the next execution of the job may either be delayed or completely cancelled, if the window of running the job has passed.

The kbmMW Scheduler contains some nice statistics to follow, which can be used to track if that happens, how often and provides some figures that tells about the time of executing.

The statistics can be obtained directly from the scheduled event instance you receive as result when you use Scheduler.Schedule…. But you can also iterate over all scheduled events. The following example iterates through the scheduled events, triggered by a TTimer, to be able to update a TMemo with statistics,.

procedure TForm1.Timer1Timer(Sender: TObject);
var
 i:integer;
 lst:TList<IkbmMWScheduledEvent>;
 e:IkbmMWScheduledEvent;
begin
 if not chbStats.Checked then
 exit;
 lst:=Scheduler.Events.Events.BeginRead;
 try
   mStats.Clear;
   for i:=0 to lst.Count-1 do
   begin
     e:=lst.Items[i];
     mStats.Lines.Add(inttostr(i)+') ID='+e.ID+' Name='+e.Name+' Runs='+inttostr(e.Runs)+' Stalls='+inttostr(e.Stalls)+' TotalStalls='+inttostr(e.TotalStalls)+' LastRunTaken='+inttostr(e.LastRunTaken)+' TotalTimeTaken='+inttostr(e.TotalTimeTaken));
   end;
 finally
   Scheduler.Events.Events.EndRead;
 end;

The scheduled events have a number of properties to access for statistics:

  • Runs indicate how many times the event has run in total.
  • Stalls indicate how many times the event should have run, but was unable to due to either still running or the thread pool exhausted at the time, since last time a successful run happened.
  • TotalStalls counts total number of stalls.
  • LastRunTaken shows how many mSecs last run lasted.

When TotalStalls increase fairly regularly it indicates that either too many events are defined compared to size of the relaxed thread pool (increase RelaxedPoolSize which is default 20), or some events are taking longer time than expected to run and thus can’t be timely rescheduled.