kbmMemTable v. 7.77.30 Standard and Professional Edition released

This is a bugfix release fixing the following issues:

  • Fixed compilation in D2009/D2010.
  • Fixed ftString PopulateField and CompareFields bug affecting NextGen compilers (locate/sort).
  • Fixed order of destruction of FRowOrderIndex.

kbmMemTable is the premier high performance, high functionality in
memory dataset for Delphi and C++Builder with kbmMemTable Professional
topping the scales as being the worlds fastest!

If you have an up to date Service and Update (SAU) subscription, then
you can immediately visit https://portal.components4developers.com to
download the latest kbmMemTable release.

If not, please visit our shop at http://www.components4developers.com
and extend your SAU with another 12 months.

kbmMW Scheduler Tidbits #5 -Synchronize multiple parallel jobs

Next release of kbmMW further improves on the TkbmMWScheduler. The scheduler supports adding jobs to run given a quite extensive set of time conditions. It also already supports running jobs in the background, and when they are done, handle some foreground update of the GUI in a safe and consistent way.

However what if you want to run a number of jobs in parallel in the background and only call the job a success when all the background sub jobs has finished.

kbmMW will allow you to do that in a simple way in next release. Here is some example code.

procedure TForm1.Button10Click(Sender: TObject);
var
  d1:int64;
begin
  d1:=TkbmMWTiming.GetTimeUS;
  Scheduler.Run([
   procedure(const AScheduledEvent:IkbmMWScheduledEvent)
   var
      i,n:integer;
      d:int64;
   begin
        AScheduledEvent.Data:=0;
        n:=500;
        d:=TkbmMWTiming.GetTimeUS;
        for i:=0 to n-1 do
        begin
             if AScheduledEvent.Terminating then
                break;
             sleep(10);
        end;
        d:=TkbmMWTiming.Diff(d);
        AScheduledEvent.Data:=d;
   end,
   procedure(const AScheduledEvent:IkbmMWScheduledEvent)
   var
      i,n:integer;
      d:int64;
   begin
        AScheduledEvent.Data:=0;
        n:=400;
        d:=TkbmMWTiming.GetTimeUS;
        for i:=0 to n-1 do
        begin
             if AScheduledEvent.Terminating then
                break;
             sleep(10);
        end;
        d:=TkbmMWTiming.Diff(d);
        AScheduledEvent.Data:=d;
   end]
  )
  .NamedAs('MultipleWithSync')
  .SynchronizedAfterRun(
   procedure(const AScheduledEvent:IkbmMWScheduledEvent)
   begin
        mData.Lines.Add('Run multiple with sync - anonym (uS):'+VarToStr(AScheduledEvent.ChildEvent[0].Data)+','+VarToStr(AScheduledEvent.ChildEvent[1].Data));
        mData.Lines.Add(' Total passed time (uS):'+IntToStr(TkbmMWTiming.Diff(d1)));
   end)
  .Activate;
end;

One interesting part is the Run method, which now takes an array of procedures or functions to run. What happens internally, is that a scheduled event is defined, which in turn defines a number of child scheduled events that each process the given jobs.

Only when all jobs are done, the AfterRun method is executed.

Another interesting thing to notice, is that the AfterRun method can tap into data provided by any of the child events. In this case the child events time how long time they ran. The AfterRun method displays that, along with the total run time.

In this case, the total time is only slightly longer than the time for the longest running job because the jobs ran in parallel.

A few more fluent methods has also been added:

WithObject(AObject:TObject) and WithInterface(AInterface:IInterface) which allows you to tag on an object or an interface to an event. It could be used to provide data for the job, or to receive data from the job.

Only remember, that jobs are run multithreaded. Thus your object or interface must be threadaware.

Finally this example shows how a long running job should check for termination. Termination happens when the Terminate method is called on the running scheduled event, or when the scheduler is closing down, for example due to the application being shutdown.

That’s all for now.

kbmMemTable 7.77.20 Std/Pro bugfix release and kbmFMX 1.03.00 Tokyo support release

We are happy to announce a bugfix release for kbmMemTable Standard and Professional Edition, fixing a couple of bugs reported by users:

Whats new in 7.77.20 Oct 1 2017
---------------------------------
 - Fixed offset bug in PopulateField on 10.2 (Tokyo) IOS/Android.
 - Fixed Unicode comparison issues in SQL.
 - Fixed LIKE incorrect match bug in SQL.
 - Added support for ftFMTBcd in PopulateField.

Further we have released a RAD Studio 10.2 (Tokyo) compatible version of kbmFMX, which is our high performance Firemonkey controls for Windows, IOS, Android and OSX, which includes:

  • TkbmFMXDBGrid – A high performance, readonly, TDataset dataaware grid.
  • TkbmFMXDBMemo – A TDataset dataaware memo control.
  • TkbmFMXDBImage – A TDataset dataware image control.
  • TkbmFMXImageControl – A super versatile image control with build in support for pan, zoom (pinch and mousewheel) and rotate. In addition it supports adding active hotspots (zones) on the image.
  • TkbmFMXNumberEdit – A simple to use numeric editor control, which visually formats the number according to FormatSettings.
  • TkbmFMXVerticalLayout – A layout that automatically resizes vertically to contain all child controls. Useful in combo with a scrollbox.
  • TkbmFMXMemo – A regular TMemo, which correctly supports caret position.
  • TkbmFMXPaintBox – A paintbox, great for capturing signatures.
  • TkbmFMXProgress – A progress overlay.

kbmFMX is available as a free bundle with kbmMemTable Standard Edition, kbmMemTable Professional Edition, kbmMW Professional Edition and kbmMW Enterprise Edition.

Please visit https://portal.components4developers.com to download the
latest and greatest versions of our paid for and free products.

 

kbmMemTable SQL new features coming

In the upcoming release of kbmMemTable, a number of improvements of the optional SQL parser is included.

Now the following syntax is also supported (see examples further down):

  • CASE … WHEN … THEN … ELSE… END
  • SELECT … INTO… FROM….
  • CREATE TABLE
  • CREATE INDEX
  • DROP INDEX
  • ALTER TABLE… ADD COLUMN…
  • ALTER TABLE… DROP COLUMN…
  • ALTER TABLE… MODIFY COLUMN…
  • LIST TABLES
  • LIST INDEXES ON …
  • DESCRIBE TABLE …
  • DESCRIBE INDEX …

Further multiple statements separated by semicolon is now also supported. All will be run in order.

The DESCRIBE functions will return SQL 2003 style metadata descriptions.

In addition kbmMemTable SQL now supports simple inner joins.

<TEASER>

Many of these new features will be utilized by next version of kbmMW, which supports extensive SQL rewriting for major databases. Hence you will be able to write most SQL in the standard kbmMemTable SQL way which is SQL 92 compatible, and kbmMW will optionally automatically rewrite it to match the actual database, making supporting different databases a breeze. More about that in another blogpost.

</TEASER>

Examples:

SELECT fld1, fld2,
CASE
WHEN fld2<100 THEN ‘LOW’
WHEN fld2>=100 AND fld2<200 THEN ‘MEDIUM’
ELSE ‘HIGH’
END AS FLD3 INTO table7
FROM Table1

SELECT fld1, fld2,
CASE fld2
WHEN 10 THEN 99999
WHEN 20 THEN 22222
ELSE -1
END
FROM Table1

CREATE TABLE table9 (
id VARCHAR(40),
fld1 VARCHAR(50),
fld2 VARCHAR(5) NOT NULL,
fld3 BLOB NOT NULL,
fld4 INTEGER,
fld5 FLOAT,
fld6 TIMESTAMP,
PRIMARY KEY (id))

DROP TABLE table3

CREATE INDEX idx5 ON table1 (fld2 DESC)

DROP INDEX idx5 ON table1

CREATE TABLE (id INT, fld7 CLOB, PRIMARY KEY (id) )

CREATE TABLE (
id INT PRIMARY KEY,
fld2 VARCHAR(10),
fld7 CLOB ) ; CREATE INDEX idx5 (fld2 DESC) ; DROP INDEX idx5

ALTER TABLE table1 ADD COLUMN NewField VARCHAR(30)

ALTER TABLE table1 DROP COLUMN Fld2

ALTER TABLE table1 ALTER COLUMN Fld1 VARCHAR(30)

ALTER TABLE table3 MODIFY COLUMN Fld8 VARCHAR(30)

ALTER TABLE table3 MODIFY COLUMN Fld8 INTEGER

ALTER TABLE table1 ADD NewField VARCHAR(30)

ALTER TABLE table1 DROP Fld2

ALTER TABLE table1 ALTER Fld1 VARCHAR(30)

ALTER TABLE table3 MODIFY Fld8 VARCHAR(30)

ALTER TABLE table3 MODIFY Fld8 INTEGER

LIST TABLES

LIST INDEXES ON TABLE table1

LIST INDEXES ON table1

LIST INDEXES FOR TABLE table1

LIST INDEXES FOR table1

LIST INDEXES table1

DESC TABLE table1

DESCRIBE TABLE table1

DESCRIBE INDEX iDescend ON table1

DESCRIBE INDEX iDescend FOR table1

DESCRIBE INDEX iDescend ON TABLE table1

NB: If you like our blog posts, feel free to share then with your social network!

Access to www.components4developers.com

Our site may have limited access at the moment.

It seems that NetNames, who is our DNS/web forwarder, is having some issues at the moment, resulting in lack of access to our website from some countries in the world of some currently unknown reason.

And no… our domain subscriptions has not lapsed. They are paid up and current for the next couple of years. 🙂

If you want to visit our homepage, you can do so directly on http://66.85.163.250 until the issue has been solved.

The portal at https://portal.components4developers.com is continuing to be accessible.

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 #1

REST easy with kbmMW #2 – Database

REST easy with kbmMW #4 – Access management

REST easy with kbmMW #5 – Logging

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.

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 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;
  FName:kbmMWNullable;
  FAddress:kbmMWNullable;
  FZipCode:kbmMWNullable;
  FCity:kbmMWNullable;
  FComments:kbmMWNullable;
 public
  property ID:kbmMWNullable read FID write FID;
  property Name:kbmMWNullable read FName write FName;
  property Address:kbmMWNullable read FAddress write FAddress;
  property ZipCode:kbmMWNullable read FZipCode write FZipCode;
  property City:kbmMWNullable read FCity write FCity;
  property Comments:kbmMWNullable 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 in 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 GUID’s. 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 allows 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 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

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;
 FName:kbmMWNullable;
 FAddress:kbmMWNullable;
 FZipCode:kbmMWNullable;
 FCity:kbmMWNullable;
 FComments:kbmMWNullable;
public
 [kbmMW_Field('primary:true, generator:shortGuid',ftString,40)]
 property ID:kbmMWNullable read FID write FID;

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

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

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

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

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

implementation

initialization
 kbmMWRegisterKnownClasses([TContact,TObjectList]);
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 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;

   [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;
begin
 Result:=Form7.ORM.QueryList;
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([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 easy with kbmMW #3 – SSL

REST easy with kbmMW #4 – Access management

REST easy with kbmMW #5 – Logging