Synopse Open Source - Tag - iOSmORMot MVC / SOA / ORM and friends2024-02-02T17:08:25+00:00urn:md5:cc547126eb580a9adbec2349d7c65274DotclearCross-Platform mORMot Clients - Smart Mobile Studiourn:md5:c91c39eba98fded8fbc28d7f62d2954b2014-08-11T20:44:00+02:002020-07-03T09:29:59+02:00AB4327-GANDImORMot FrameworkAJAXblogCrossPlatformDelphiDocumentationDTOFireMonkeyFreePascalHTMLHTTPHTTPSiOSJavaScriptJSONModelmORMotMustachenativeNextGenOpenSourceORMperformancerecordRestSOASynopseUserInterfaceweb<p>Current version of the main framework units target only <em>Win32</em> and
<em>Win64</em> systems.</p>
<p>It allows to make easy self-hosting of <em>mORMot</em> servers for local
business applications in any corporation, or pay cheap hosting in the Cloud,
since <em>mORMot</em> CPU and RAM expectations are much lower than a regular
<code>IIS-WCF-MSSQL-.Net</code> stack.<br />
But in a <em>Service-Oriented Architecture (SOA)</em>, you would probably need
to create clients for platforms outside the <em>Windows</em> world, especially
mobile devices.<br />
<img src="https://blog.synopse.info?post/public/mORMot/CrossPlatform.jpg" alt="" title="CrossPlatform, Aug 2014" /></p>
<p>A set of cross-platform client units is therefore available in the
<code>CrossPlatform</code> sub-folder of the source code repository. It allows
writing any client in modern <em>object pascal</em> language, for:</p>
<ul>
<li>Any version of <em>Delphi</em>, on any platform (<em>Mac OSX</em>, or any
mobile supported devices);</li>
<li><em>FreePascal</em> Compiler 2.7.1;</li>
<li><em>Smart Mobile Studio</em> 2.1, to create AJAX or mobile applications
(via <em>PhoneGap</em>, if needed).</li>
</ul>
<p>This series of articles will introduce you to <em>mORMot</em>'s
Cross-Platform abilities:</p>
<ul>
<li><a href="https://blog.synopse.info?post/post/2014/08/11/CrossPlatform-Clients/Units-Platforms">Units and
platforms</a>;</li>
<li><a href="https://blog.synopse.info?post/post/2014/08/11/CrossPlatform-Clients/Generate-Code">Generating
the client code wrappers</a>;</li>
<li><a href="https://blog.synopse.info?post/post/2014/08/11/CrossPlatform-Clients/Delphi-FreePascal"><em>Delphi</em> /
<em>FreePascal</em> clients</a>;</li>
<li><a href="https://blog.synopse.info?post/post/2014/08/11/CrossPlatform-Clients/SmartMobileStudio"><em>Smart Mobile
Studio</em> clients</a>.</li>
</ul>
<p>Any feedback is <a href="http://synopse.info/forum/viewtopic.php?id=1939">welcome in our forum</a>, as
usual!</p> <h3>Smart Mobile Studio client samples</h3>
<p>In addition to <em>Delphi</em> and <em>FreePascal</em> clients, our
framework is able to access any <em>mORMot</em> server from HTML5 / AJAX rich
client, thanks to <em>Smart Mobile Studio</em>.</p>
<h3>Adding two numbers in AJAX</h3>
<p>You can find in <code>SQLite3- CrossPlatform ClientsSmartMobileStudio</code>
a simple client for the <code>TServiceCalculator.Add()</code> interface-based
service.<br />
If your <code>Project14ServerHttpWrapper</code> server is running, you can just
point to the supplied <code>wwwhtml</code> file in the sub-folder.<br />
You would then see a web page with a "<code>Server Connect</code>" button, and
if you click on it, you would be able to add two numbers. This a full HTML5 web
application, connecting securely to your <em>mORMot</em> server, which will
work from any desktop browser (on <em>Windows</em>, <em>Mac OS X</em>, or
<em>Linux</em>), or from any mobile device (either <em>iPhone</em> /
<em>iPad</em> / <em>Android</em> / <em>Windows 8 Mobile</em>).</p>
<p>In order to create the application, we just clicked on "<ins>download as
file</ins>" in the <strong>SmartMobileStudio</strong> link in the web page, and
copied the generated file in the source folder of a new <em>Smart Mobile</em>
project.<br />
Of course, we did copy the needed <code>SynCrossPlatform*.pas</code> units from
the <em>mORMot</em> source code tree into the Smart library folder, as stated
above. Just ensure you run <code>CopySynCrossPlatformUnits.bat</code> from the
<code>CrossPlatform</code> folder at least once from the latest revision of the
framework source code.</p>
<p>Then, on the form visual editor, we added a <code>BtnConnect</code> button,
then a <code>PanelCompute</code> panel with two edit fields named
<code>EditA</code> and <code>EditB</code>, and two other buttons, named
<code>BtnComputeAsynch</code> and <code>BtnComputeSynch</code>. A
<code>LabelResult</code> label will be used to display the computation result.
The <code>BtnConnect</code> is a toggle which will show or display the
<code>PanelCompute</code> panel, which is hidden by default, depending on the
connection status.</p>
<p><img src="https://blog.synopse.info?post/public/mORMot/SmartCalculator.png" alt="" title="Smart Mobile Studio Calculator Service Client Sample, Aug 2014" /></p>
<p>In the <code>Form1.pas</code> unit source code side, we added a reference to
our both <code>SynCrossPlatformREST</code> and <code>mORMotClient</code> units,
and some events to the buttons:</p>
<pre>
<strong>unit</strong> Form1;
<strong>interface
uses</strong>
SmartCL.System, SmartCL.Graphics, SmartCL.Components, SmartCL.Forms,
SmartCL.Fonts, SmartCL.Borders, SmartCL.Application, SmartCL.Controls.Panel,
SmartCL.Controls.<strong>Label</strong>, SmartCL.Controls.EditBox, SmartCL.Controls.Button,
<span style="background-color:yellow;">SynCrossPlatformREST, mORMotClient;</span>
<br /><strong>type</strong>
TForm1 = <strong>class</strong>(TW3Form)
<strong>procedure</strong> BtnComputeSynchClick(Sender: TObject);
<strong>procedure</strong> BtnComputeAsynchClick(Sender: TObject);
<strong>procedure</strong> BtnConnectClick(Sender: TObject);
<strong>private</strong>
<em>{$I 'Form1:intf'}</em>
<strong>protected</strong>
<span style="background-color:yellow;">Client: TSQLRestClientURI;</span>
<strong>procedure</strong> InitializeForm; <strong>override</strong>;
<strong>procedure</strong> InitializeObject; <strong>override</strong>;
<strong>procedure</strong> Resize; <strong>override</strong>;
<strong>end</strong>;
</pre>
<p>The <code>BtnConnect</code> event will connect asynchronously to the server,
using <code>'User'</code> as log-on name, and <code>'synopse'</code> as
password (those as the framework defaults).<br />
We just use the <code>GetClient()</code> function, as published in our
generated <code>mORMotClient.pas</code> unit:</p>
<pre>
<em>/// create a TSQLRestClientHTTP instance and connect to the server
// - it will use by default port 888
// - secure connection will be established via TSQLRestServerAuthenticationDefault
// with the supplied credentials
// - request will be asynchronous, and trigger onSuccess or onError event</em>
<strong>procedure</strong> GetClient(<strong>const</strong> aServerAddress, aUserName,aPassword: <strong>string</strong>;
onSuccess, onError: TSQLRestEvent; aServerPort: integer=SERVER_PORT);
</pre>
<p>It uses two callbacks, the first in case of success, and the second
triggered on failure. On success, we will set the global <code>Client</code>
variable with the <code>TSQLRestClientURI</code> instance just created, then
display the two fields and compute buttons:</p>
<pre>
<strong>procedure</strong> TForm1.BtnConnectClick(Sender: TObject);
<strong>begin</strong>
<strong>if</strong> Client=<strong>nil then</strong>
<span style="background-color:yellow;">GetClient('127.0.0.1','User','synopse',</span>
<span style="background-color:yellow;">lambda (aClient: TSQLRestClientURI)</span>
PanelCompute.Visible := true;
W3Label1.Visible := true;
W3Label2.Visible := true;
LabelConnect.Caption := '';
BtnConnect.Caption := 'Disconnect';
LabelResult.Caption := '';
<span style="background-color:yellow;">Client := aClient;</span>
<strong>end</strong>,
lambda
ShowMessage('Impossible to connect to the server!');
<strong>end</strong>)
<strong>else begin</strong>
PanelCompute.Visible := false;
BtnConnect.Caption := 'Server Connect';
Client.Free;
Client := <strong>nil</strong>;
<strong>end</strong>;
<strong>end</strong>;
</pre>
<p>The <code>GetClient()</code> function expects two callbacks, respectively
<code>onSuccess</code> and <code>onError</code>, which are implemented here
with two <em>SmartPascal</em> <code>lambda</code> blocks.</p>
<p>Now that we are connected to the server, let's do some useful
computation!<br />
As you can see in the <code>mORMotClient.pas</code> generated unit, our
interface-based service can be accessed via a <em>SmartPascal</em>
<code>TServiceCalculator</code> class (and not an <code>interface</code>), with
two variations of each methods: one <em>asynchronous</em> method - e.g.
<code>TServiceCalculator.Add()</code> - expecting success/error callbacks, and
one <em>synchronous</em> (blocking) method - e.g.
<code>TServiceCalculator._Add()</code>:</p>
<pre>
<strong>type</strong>
<em>/// service accessible via http://localhost:888/root/Calculator</em>
<em>// - this service will run in sicShared mode</em>
<em>// - synchronous and asynchronous methods are available, depending on use case</em>
<em>// - synchronous _*() methods will block the browser execution, so won't be</em>
<em>// appropriate for long process - on error, they may raise EServiceException</em>
TServiceCalculator = <strong>class</strong>(TServiceClientAbstract)
<strong>public</strong>
<em>/// will initialize an access to the remote service</em>
<strong>constructor</strong> Create(aClient: TSQLRestClientURI); <strong>override</strong>;
<strong>procedure</strong> Add(n1: integer; n2: integer;
onSuccess: <strong>procedure</strong>(Result: integer); onError: TSQLRestEvent);
<strong>function</strong> _Add(<strong>const</strong> n1: integer; <strong>const</strong> n2: integer): integer;
<strong>end</strong>;
</pre>
<p>We can therefore execute asynchronously the <code>Add()</code> service as
such:</p>
<pre>
<strong>procedure</strong> TForm1.BtnComputeAsynchClick(Sender: TObject);
<strong>begin</strong>
TServiceCalculator.Create(Client).Add(
StrToInt(EditA.Text),StrToInt(EditB.Text),
lambda (res: integer)
LabelResult.Caption := format('Result = %d',[res]);
<strong>end</strong>,
lambda
ShowMessage('Error calling the method!');
<strong>end</strong>);
<strong>end</strong>;
</pre>
<p>Or execute synchronously the <code>_Add()</code> service:</p>
<pre>
<strong>procedure</strong> TForm1.BtnComputeSynchClick(Sender: TObject);
<strong>begin</strong>
LabelResult.Caption := format('Result = %d',
[TServiceCalculator.Create(Client)._Add(
StrToInt(EditA.Text),StrToInt(EditB.Text))]);
<strong>end</strong>;
</pre>
<p>Of course, the synchronous code is much easier to follow and maintain. To be
fair, the <em>SmartPascal</em> <code>lambda</code> syntax is not difficult to
read nor write. In the browser debugger, you can easily set a break point
within any <code>lambda</code> block, and debug your code.</p>
<p>Note that if the server is slow to answer, your whole web application will
be unresponsive, and the browser may even complain about the page, proposing
the kill its process!<br />
As a consequence, simple services may be written in a synchronous manner, but
your serious business code should rather use asynchronous callbacks, just as
with any modern AJAX application.</p>
<p>Thanks to the <em>Smart Linking</em> feature of its compiler, only the used
version of the unit will be converted to <em>JavaScript</em> and included in
the final <code>index.html</code> HTML5 file. So having both synchronous and
asynchronous versions of each method at hand is not an issue.</p>
<h3>CRUD/ORM remote access</h3>
<p>If the server did have some ORM model, its <code>TSQLRecord</code> classes
will also be part of the <code>mORMotClient.pas</code> generated unit. All
types, even complex record structures, will be marshaled as expected.</p>
<p>For instance, if you run the <code>RegressionTestsServer.dpr</code> server
(available in the same folder), a much more complete unit could be generated
from <code>http://localhost:888/root/wrapper</code>:</p>
<pre>
<strong>type</strong> <em>// define some enumeration types, used below</em>
TPeopleSexe = (sFemale, sMale);
TRecordEnum = (reOne, reTwo, reLast);
<br /><strong>type</strong> <em>// define some record types, used as properties below</em>
TTestCustomJSONArraySimpleArray = <strong>record</strong>
F: <strong>string</strong>;
G: <strong>array of string</strong>;
H: <strong>record</strong>
H1: integer;
H2: <strong>string</strong>;
H3: <strong>record</strong>
H3a: boolean;
H3b: TSQLRawBlob;
<strong>end</strong>;
<strong>end</strong>;
I: TDateTime;
J: <strong>array of record</strong>
J1: byte;
J2: TGUID;
J3: TRecordEnum;
<strong>end</strong>;
<strong>end</strong>;
<strong>type</strong>
<em>/// service accessible via http://localhost:888/root/Calculator</em>
<em>// - this service will run in sicShared mode</em>
<em>// - synchronous and asynchronous methods are available, depending on use case</em>
<em>// - synchronous _*() methods will block the browser execution, so won't be</em>
<em>// appropriate for long process - on error, they may raise EServiceException</em>
TServiceCalculator = <strong>class</strong>(TServiceClientAbstract)
<strong>public</strong>
<em>/// will initialize an access to the remote service</em>
<strong>constructor</strong> Create(aClient: TSQLRestClientURI); <strong>override</strong>;
<strong>procedure</strong> Add(n1: integer; n2: integer;
onSuccess: <strong>procedure</strong>(Result: integer); onError: TSQLRestEvent);
<strong>function</strong> _Add(<strong>const</strong> n1: integer; <strong>const</strong> n2: integer): integer;
<strong>procedure</strong> ToText(Value: currency; Curr: <strong>string</strong>; Sexe: TPeopleSexe; Name: <strong>string</strong>;
onSuccess: <strong>procedure</strong>(Sexe: TPeopleSexe; Name: <strong>string</strong>); onError: TSQLRestEvent);
<strong>procedure</strong> _ToText(<strong>const</strong> Value: currency; <strong>const</strong> Curr: RawUTF8; <strong>var</strong> Sexe: TPeopleSexe; <strong>var</strong> Name: RawUTF8);
<strong>procedure</strong> RecordToText(Rec: TTestCustomJSONArraySimpleArray;
onSuccess: <strong>procedure</strong>(Rec: TTestCustomJSONArraySimpleArray; Result: <strong>string</strong>); onError: TSQLRestEvent);
<strong>function</strong> _RecordToText(<strong>var</strong> Rec: TTestCustomJSONArraySimpleArray): <strong>string</strong>;
<strong>end</strong>;
<br /> <em>/// map "People" table</em>
TSQLRecordPeople = <strong>class</strong>(TSQLRecord)
<strong>protected</strong>
fFirstName: <strong>string</strong>;
fLastName: <strong>string</strong>;
fData: TSQLRawBlob;
fYearOfBirth: integer;
fYearOfDeath: word;
fSexe: TPeopleSexe;
fSimple: TTestCustomJSONArraySimpleArray;
<em>// those overriden methods will emulate the needed RTTI</em>
<strong>class function</strong> ComputeRTTI: TRTTIPropInfos; <strong>override</strong>;
<strong>procedure</strong> SetProperty(FieldIndex: integer; <strong>const</strong> Value: <strong>variant</strong>); <strong>override</strong>;
<strong>function</strong> GetProperty(FieldIndex: integer): <strong>variant</strong>; <strong>override</strong>;
<strong>public</strong>
<strong>property</strong> FirstName: <strong>string read</strong> fFirstName <strong>write</strong> fFirstName;
<strong>property</strong> LastName: <strong>string read</strong> fLastName <strong>write</strong> fLastName;
<strong>property</strong> Data: TSQLRawBlob <strong>read</strong> fData <strong>write</strong> fData;
<strong>property</strong> YearOfBirth: integer <strong>read</strong> fYearOfBirth <strong>write</strong> fYearOfBirth;
<strong>property</strong> YearOfDeath: word <strong>read</strong> fYearOfDeath <strong>write</strong> fYearOfDeath;
<strong>property</strong> Sexe: TPeopleSexe <strong>read</strong> fSexe <strong>write</strong> fSexe;
<strong>property</strong> Simple: TTestCustomJSONArraySimpleArray <strong>read</strong> fSimple <strong>write</strong> fSimple;
<strong>end</strong>;
</pre>
<p>In the above code, you can see several methods to the
<code>ICalculator</code> service, some involving the complex
<code>TTestCustomJSONArraySimpleArray</code> record type. The
<code>implementation</code> section of the unit will in fact allow
serialization of such records to/from JSON, even with obfuscated
<em>JavaScript</em> field names, via <code>ComputeRTTI() GetProperty()</code>
and <code>SetProperty()</code>.</p>
<p>Some <em>enumerations</em> types are also defined, so will help your
business code be very expressive, thanks to the <em>SmartPascal</em> strong
typing. This is a huge improvement when compared to <em>JavaScript</em> native
weak and dynamic typing.</p>
<p>There is a <code>TSQLRecordPeople</code> class generated, which will map the
following <em>Delphi</em> class type, as defined in the
<code>PeopleServer.pas</code> unit:</p>
<pre>
TSQLRecordPeople = <strong>class</strong>(TSQLRecord)
<strong>protected</strong>
fData: TSQLRawBlob;
fFirstName: RawUTF8;
fLastName: RawUTF8;
fYearOfBirth: integer;
fYearOfDeath: word;
fSexe: TPeopleSexe;
fSimple: TTestCustomJSONArraySimpleArray;
<strong>public</strong>
<strong>class procedure</strong> InternalRegisterCustomProperties(Props: TSQLRecordProperties); <strong>override</strong>;
<strong>published</strong>
<strong>property</strong> FirstName: RawUTF8 <strong>read</strong> fFirstName <strong>write</strong> fFirstName;
<strong>property</strong> LastName: RawUTF8 <strong>read</strong> fLastName <strong>write</strong> fLastName;
<strong>property</strong> Data: TSQLRawBlob <strong>read</strong> fData <strong>write</strong> fData;
<strong>property</strong> YearOfBirth: integer <strong>read</strong> fYearOfBirth <strong>write</strong> fYearOfBirth;
<strong>property</strong> YearOfDeath: word <strong>read</strong> fYearOfDeath <strong>write</strong> fYearOfDeath;
<strong>property</strong> Sexe: TPeopleSexe <strong>read</strong> fSexe <strong>write</strong> fSexe;
<strong>public</strong>
<strong>property</strong> Simple: TTestCustomJSONArraySimpleArray <strong>read</strong> fSimple;
<strong>end</strong>;
</pre>
<p>Here, a complex <code>TTestCustomJSONArraySimpleArray</code> record field
has been published, thanks to a manual
<code>InternalRegisterCustomProperties()</code> registration, as we already
stated above.<br />
You can see that types like <code>RawUTF8</code> were mapped to the standard
<em>SmartPascal</em> <code>string</code> type, as expected, when converted to
the <code>mORMotClient.pas</code> generated unit.</p>
<p>Your AJAX client can then access to this <code>TSQLRecordPeople</code>
content easily, via standard CRUD operations.<br />
See the <code>SQLite3- SmartMobileStudio Client</code> sample, for instance the
following line:</p>
<pre>
<span style="background-color:yellow;">people := new TSQLRecordPeople;</span>
<strong>for</strong> i := 1 <strong>to</strong> 200 <strong>do begin</strong>
<span style="background-color:yellow;">assert(client.Retrieve(i,people));</span>
assert(people.ID=i);
assert(people.FirstName='First'+IntToStr(i));
assert(people.LastName='Last'+IntToStr(i));
assert(people.YearOfBirth=id+1800);
assert(people.YearOfDeath=id+1825);
<strong>end</strong>;
</pre>
<p>Here, the <code>client</code> variable is a <code>TSQLRestClientURI</code>
instance, as returned by the <code>GetClient() onSuccess</code> callback
generated in <code>mORMotClient.pas</code>.<br />
You have <code>Add() Delete() Update() FillPrepare()
CreateAndFillPrepare()</code> and <code>Batch*()</code> methods available,
ready to safely access your data from your AJAX client.</p>
<p>If you update your data model on the server, just re-generate your
<code>mORMotClient.pas</code> unit from
<code>http://localhost:888/root/wrapper</code>, then rebuild your <em>Smart
Mobile Studio</em> project to reflect all changes made to your ORM data model,
or your SOA available services.</p>
<p>Thanks to the <em>SmartPascal</em> strong typing, any breaking change of the
server expectations would immediately be reported at compilation, and not at
runtime, as it would with regular <em>JavaScript</em> clients.</p>Cross-Platform mORMot Clients - Delphi / FreePascalurn:md5:1a7657aad2fbf5869145db279b552ec32014-08-11T20:42:00+02:002020-07-03T09:29:59+02:00AB4327-GANDImORMot FrameworkAJAXblogCrossPlatformDelphiDocumentationDTOFireMonkeyFreePascalHTMLHTTPHTTPSiOSJavaScriptJSONModelmORMotMustachenativeNextGenOpenSourceORMperformancerecordRestSOASynopseUserInterfaceweb<p>Current version of the main framework units target only <em>Win32</em> and
<em>Win64</em> systems.</p>
<p>It allows to make easy self-hosting of <em>mORMot</em> servers for local
business applications in any corporation, or pay cheap hosting in the Cloud,
since <em>mORMot</em> CPU and RAM expectations are much lower than a regular
<code>IIS-WCF-MSSQL-.Net</code> stack.<br />
But in a <em>Service-Oriented Architecture (SOA)</em>, you would probably need
to create clients for platforms outside the <em>Windows</em> world, especially
mobile devices.<br />
<img src="https://blog.synopse.info?post/public/mORMot/CrossPlatform.jpg" alt="" title="CrossPlatform, Aug 2014" /></p>
<p>A set of cross-platform client units is therefore available in the
<code>CrossPlatform</code> sub-folder of the source code repository. It allows
writing any client in modern <em>object pascal</em> language, for:</p>
<ul>
<li>Any version of <em>Delphi</em>, on any platform (<em>Mac OSX</em>, or any
mobile supported devices);</li>
<li><em>FreePascal</em> Compiler 2.7.1;</li>
<li><em>Smart Mobile Studio</em> 2.1, to create AJAX or mobile applications
(via <em>PhoneGap</em>, if needed).</li>
</ul>
<p>This series of articles will introduce you to <em>mORMot</em>'s
Cross-Platform abilities:</p>
<ul>
<li><a href="https://blog.synopse.info?post/post/2014/08/11/CrossPlatform-Clients/Units-Platforms">Units and
platforms</a>;</li>
<li><a href="https://blog.synopse.info?post/post/2014/08/11/CrossPlatform-Clients/Generate-Code">Generating
the client code wrappers</a>;</li>
<li><a href="https://blog.synopse.info?post/post/2014/08/11/CrossPlatform-Clients/Delphi-FreePascal"><em>Delphi</em> /
<em>FreePascal</em> clients</a>;</li>
<li><a href="https://blog.synopse.info?post/post/2014/08/11/CrossPlatform-Clients/SmartMobileStudio"><em>Smart Mobile
Studio</em> clients</a>.</li>
</ul>
<p>Any feedback is <a href="http://synopse.info/forum/viewtopic.php?id=1939">welcome in our forum</a>, as
usual!</p> <h3>Delphi / FreePascal client samples</h3>
<p>The "<code>27 - CrossPlatform ClientsRegressionTests.dpr</code>" sample
creates a <em>mORMot</em> server with its own ORM data model, containing a
<code>TSQLRecordPeople</code> class, and a set of interface-based SOA services,
some including complex types like a record.</p>
<p>Then this sample uses a generated <code>mORMotClient.pas</code>, retrieved
from the "<ins>download as file</ins>" link of the
<strong>CrossPlatform</strong> template above.<br />
Its set of regression tests (written using a small cross-platform
<code>TSynTest</code> unit test class) will then perform remote ORM and SOA
calls to the <code>PeopleServer</code> embedded instance, over all supported
authentication schemes - see <em><a href="https://blog.synopse.info?post/post/2011/05/24/How-to-implement-RESTful-authentication">Authentication</a></em>:</p>
<pre>
Cross Platform Units for mORMot
---------------------------------
<br />1. Running "Iso8601DateTime"
30003 tests passed in 00:00:018
2. Running "Base64Encoding"
304 tests passed in 00:00:000
3. Running "JSON"
18628 tests passed in 00:00:056
4. Running "Model"
1013 tests passed in 00:00:003
5. Running "Cryptography"
4 tests passed in 00:00:000
<br />Tests failed: 0 / 49952
Time elapsed: 00:00:080
<br />Cross Platform Client for mORMot without authentication
---------------------------------------------------------
<br />1. Running "Connection"
2 tests passed in 00:00:010
2. Running "ORM"
4549 tests passed in 00:00:160
3. Running "ORMBatch"
4564 tests passed in 00:00:097
4. Running "Services"
26253 tests passed in 00:00:302
5. Running "CleanUp"
1 tests passed in 00:00:000
<br />Tests failed: 0 / 35369
Time elapsed: 00:00:574
<br />Cross Platform Client for mORMot using TSQLRestServerAuthenticationNone
-------------------------------------------------------------------------
...
<br />Cross Platform Client for mORMot using TSQLRestServerAuthenticationDefault
----------------------------------------------------------------------------
...
</pre>
<p>The generated <code>mORMotClient.pas</code> unit is used for all
"<code>Cross Platform Client</code>" tests above, covering both ORM and SOA
features of the framework.</p>
<h3>Connection to the server</h3>
<p>You could manually connect to a <em>mORMot</em> server as such:</p>
<pre>
<strong>var</strong> Model: TSQLModel;
Client: TSQLRestClientHTTP;
...
Model := TSQLModel.Create([TSQLAuthUser,TSQLAuthGroup,TSQLRecordPeople]);
Client := TSQLRestClientHTTP.Create('localhost',SERVER_PORT,Model);
<strong>if not</strong> Client.Connect <strong>then</strong>
<strong>raise</strong> Exception.Create('Impossible to connect to the server');
<strong>if</strong> Client.ServerTimeStamp=0 <strong>then</strong>
<strong>raise</strong> Exception.Create('Incorrect server');
<strong>if not</strong> Client.SetUser(TSQLRestAuthenticationDefault,'User','synopse') <strong>then</strong>
<strong>raise</strong> Exception.Create('Impossible to authenticate to the server');
...
</pre>
<p>Or you may use the <code>GetClient()</code> function generated in
<code>mORMotClient.pas</code>:</p>
<pre>
<em>/// create a TSQLRestClientHTTP instance and connect to the server
// - it will use by default port 888
// - secure connection will be established via TSQLRestServerAuthenticationDefault
// with the supplied credentials - on connection or authentication error,
// this function will raise a corresponding exception</em>
<strong>function</strong> GetClient(<strong>const</strong> aServerAddress, aUserName,aPassword: <strong>string</strong>;
aServerPort: integer=SERVER_PORT): TSQLRestClientHTTP;
</pre>
<p>Which could be used as such:</p>
<pre>
<strong>var</strong> Client: TSQLRestClientHTTP;
...
Client := GetClient('localhost','User','synopse')
</pre>
<p>The data model and the expected authentication scheme were included in the
<code>GetClient()</code> function, which will raise the expected
<code>ERestException</code> in case of any connection or authentication
issue.</p>
<h3>CRUD/ORM remote access</h3>
<p>Thanks to <code>SynCrossPlatform*</code> units, you could easily perform any
remote ORM operation on your <em>mORMot</em> server, with the usual
<code>TSQLRest</code> CRUD methods.<br />
For instance, the <code>RegressionTests.dpr</code> sample performs the
following operations</p>
<pre>
<span style="background-color:yellow;">fClient.CallBackGet('DropTable',[],Call,TSQLRecordPeople); <em>// call of method-based service</em></span>
check(Call.OutStatus=HTML_SUCCESS);
people := TSQLRecordPeople.Create; <em>// create a record ORM</em>
<strong>try</strong>
<strong>for</strong> i := 1 <strong>to</strong> 200 <strong>do begin</strong>
people.FirstName := 'First'+IntToStr(i);
people.LastName := 'Last'+IntToStr(i);
people.YearOfBirth := i+1800;
people.YearOfDeath := i+1825;
people.Sexe := TPeopleSexe(i <strong>and</strong> 1);
<span style="background-color:yellow;">check(Client.Add(people,true)=i); <em>// add one record</em></span>
<strong>end</strong>;
<strong>finally</strong>
people.Free;
<strong>end</strong>;
...
<span style="background-color:yellow;">people := TSQLRecordPeople.CreateAndFillPrepare(fClient,'',</span>
<span style="background-color:yellow;">'yearofbirth=?',[1900]); <em>// parameterized query returning one or several rows</em></span>
<strong>try</strong>
n := 0;
<span style="background-color:yellow;"><strong>while</strong> people.FillOne <strong>do begin</strong></span>
inc(n);
check(people.ID=100);
check(people.FirstName='First100');
check(people.LastName='Last100');
check(people.YearOfBirth=1900);
check(people.YearOfDeath=1925);
<strong>end</strong>;
check(n=1); <em>// we expected only one record here</em>
<strong>finally</strong>
people.Free;
<strong>end</strong>;
<strong>for</strong> i := 1 <strong>to</strong> 200 <strong>do</strong>
<strong>if</strong> i <strong>and</strong> 15=0 <strong>then</strong>
<span style="background-color:yellow;">fClient.Delete(TSQLRecordPeople,i) <strong>else</strong> <em>// record deletion</em></span>
<strong>if</strong> i <strong>mod</strong> 82=0 <strong>then begin</strong>
people := TSQLRecordPeople.Create;
<strong>try</strong>
id := i+1;
people.ID := i;
people.YearOfBirth := id+1800;
people.YearOfDeath := id+1825;
<span style="background-color:yellow;">check(fClient.Update(people,'YEarOFBIRTH,YEarOfDeath')); <em>// record modification</em></span>
<strong>finally</strong>
people.Free;
<strong>end</strong>;
<strong>end</strong>;
<strong>for</strong> i := 1 <strong>to</strong> 200 <strong>do begin</strong>
<span style="background-color:yellow;">people := TSQLRecordPeople.Create(fClient,i); <em>// retrieve one instance from ID</em></span>
<strong>try</strong>
<strong>if</strong> i <strong>and</strong> 15=0 <strong>then</strong> <em>// was deleted</em>
Check(people.ID=0) <strong>else begin</strong>
<strong>if</strong> i <strong>mod</strong> 82=0 <strong>then</strong>
id := i+1 <strong>else</strong> <em>// was modified</em>
id := i;
Check(people.ID=i);
Check(people.FirstName='First'+IntToStr(i));
Check(people.LastName='Last'+IntToStr(i));
Check(people.YearOfBirth=id+1800);
Check(people.YearOfDeath=id+1825);
Check(ord(people.Sexe)=i <strong>and</strong> 1);
<strong>end</strong>;
<strong>finally</strong>
people.Free;
<strong>end</strong>;
<strong>end</strong>;
</pre>
<p>As we already stated, BATCH mode is also supported, with the classic
<em>mORMot</em> syntax:</p>
<pre>
...
res: TIntegerDynArray;
...
<span style="background-color:yellow;">fClient.BatchStart(TSQLRecordPeople);</span>
people := TSQLRecordPeople.Create;
<strong>try</strong>
<strong>for</strong> i := 1 <strong>to</strong> 200 <strong>do begin</strong>
people.FirstName := 'First'+IntToStr(i);
people.LastName := 'Last'+IntToStr(i);
people.YearOfBirth := i+1800;
people.YearOfDeath := i+1825;
<span style="background-color:yellow;">fClient.BatchAdd(people,true);</span>
<strong>end</strong>;
<strong>finally</strong>
people.Free;
<strong>end</strong>;
<span style="background-color:yellow;">fClient.fBatchSend(res)=HTML_SUCCESS);</span>
check(length(res)=200);
<strong>for</strong> i := 1 <strong>to</strong> 200 <strong>do</strong>
check(res[i-1]=i); <em>// server returned the IDs of the newly created records</em>
</pre>
<p>Those <code>BatchAdd</code> / <code>BatchDelete</code> /
<code>BatchUpdate</code> methods of <code>TSQLRest</code> have the benefit to
introduce at client level:</p>
<ul>
<li>Much higher performance, especially on multi-insertion or multi-update of
data;</li>
<li>Transactional support: <code>TSQLRest.BatchStart()</code> has an optional
<code>AutomaticTransactionPerRow</code> parameter, set to <code>10000</code> by
default, which will create a server-side transaction during the write process,
and an ACID rollback in case of any failure.</li>
</ul>
<p>You can note that all above code has exactly the same structure and methods
than standard <em>mORMot</em> clients.</p>
<p>The generated <code>mORMotClient.pas</code> unit contains all needed
<code>TSQLRecord</code> types, and its used properties, including enumerations
or complex records. The only dependency of this unit are
<code>SynCrossPlatform*</code> units, so would be perfectly cross-platform
(whereas our main <code>SynCommons.pas</code> and <code>mORMot.pas</code> units
do target only <em>Win32</em> and <em>Win64</em>).</p>
<p>As a result, you are able to <em>share</em> server and client code between a
Windows project and any supported platform, even AJAX (see "<em>Smart Mobile
Studio client samples</em>" below). A shared unique code base would eventually
reduce both implementation and debugging time, which is essential to unleash
your business code potential and maximize your ROI.</p>
<h3>Service consumption</h3>
<p>The ultimate goal of the <em>mORMot</em> framework is to publish your
business via a <em>Service-Oriented Architecture (SOA)</em>.<br />
As a consequence, those services should be made available from any kind of
device or platform, even outside the <em>Windows</em> world. The server is able
to generate client wrappers code, which could be used to consume any
<em>Client-Server services via interfaces</em> using any supported
authentication scheme - see <em><a href="https://blog.synopse.info?post/post/2013/06/07/Authentication-and-Authorization">Authentication</a></em>.</p>
<p>Here is an extract of the <code>mORMotClient.pas</code> unit as generated
for the <code>RegressionTests.dpr</code> sample:</p>
<pre>
<strong>type</strong>
<em>/// service implemented by TServiceCalculator</em>
<em>// - you can access this service as such:</em>
<em>// !var aCalculator: ICalculator;</em>
<em>// !begin</em>
<em>// ! aCalculator := TCalculator.Create(aClient);</em>
<em>// ! // now you can use aCalculator methods</em>
<em>// !...</em>
ICalculator = <strong>interface</strong>(IServiceAbstract)
['{9A60C8ED-CEB2-4E09-87D4-4A16F496E5FE}']
<strong>function</strong> Add(<strong>const</strong> n1: integer; <strong>const</strong> n2: integer): integer;
<strong>procedure</strong> ToText(<strong>const</strong> Value: currency; <strong>const</strong> Curr: <strong>string</strong>; <strong>var</strong> Sexe: TPeopleSexe; <strong>var</strong> Name: <strong>string</strong>);
<strong>function</strong> RecordToText(<strong>var</strong> Rec: TTestCustomJSONArraySimpleArray): <strong>string</strong>;
<strong>end</strong>;
<br /> <em>/// implements ICalculator from http://localhost:888/root/Calculator</em>
<em>// - this service will run in sicShared mode</em>
TServiceCalculator = <strong>class</strong>(TServiceClientAbstract,ICalculator)
<strong>public</strong>
<strong>constructor</strong> Create(aClient: TSQLRestClientURI); <strong>override</strong>;
<strong>function</strong> Add(<strong>const</strong> n1: integer; <strong>const</strong> n2: integer): integer;
<strong>procedure</strong> ToText(<strong>const</strong> Value: currency; <strong>const</strong> Curr: <strong>string</strong>; <strong>var</strong> Sexe: TPeopleSexe; <strong>var</strong> Name: <strong>string</strong>);
<strong>function</strong> RecordToText(<strong>var</strong> Rec: TTestCustomJSONArraySimpleArray): <strong>string</strong>;
<strong>end</strong>;
</pre>
<p>As you can see, a dedicated class has been generated to consume the
server-side <code>ICalculator</code> interface-based service, in its own
<code>ICalculator</code> client-side type.<br />
It is able to handle complex types, like enumerations (e.g.
<code>TPeopleSexe</code>) and records (e.g.
<code>TTestCustomJSONArraySimpleArray</code>), which are also defined in the
very same <code>mORMotClient.pas</code> unit.<br />
You can note that the <code>RawUTF8</code> type has been changed into the
standard <em>Delphi</em> / <em>FreePascal</em> <code>string</code> type, since
it is the native type used by our <code>SynCrossPlatformJSON.pas</code> unit
for all its JSON marshaling. Of course, under latest version of <em>Delphi</em>
and <em>FreePascal</em>, this kind of content may be Unicode encoded (either as
UTF-16 for the <code>string</code> = <code>UnicodeString</code> <em>Delphi</em>
type, or as UTF-8 for the <em>FreePascal</em> / <em>Lazarus</em>
<code>string</code> type).</p>
<p>The supplied regression tests show how to use remotely those services:</p>
<pre>
<span style="background-color:yellow;"><strong>var</strong> calc: ICalculator;</span>
i,j: integer;
sex: TPeopleSexe;
name: <strong>string</strong>;
...
<span style="background-color:yellow;">calc := TServiceCalculator.Create(fClient);</span>
check(calc.InstanceImplementation=sicShared);
check(calc.ServiceName='Calculator');
<strong>for</strong> i := 1 <strong>to</strong> 200 <strong>do</strong>
<span style="background-color:yellow;">check(calc.Add(i,i+1)=i*2+1);</span>
<strong>for</strong> i := 1 <strong>to</strong> 200 <strong>do begin</strong>
sex := TPeopleSexe(i <strong>and</strong> 1);
name := 'Smith';
<span style="background-color:yellow;">calc.ToText(i,'$',sex,name);</span>
check(sex=sFemale);
check(name=format('$ %d for %s Smith',[i,SEX_TEXT[i <strong>and</strong> 1]]));
<strong>end</strong>;
...
</pre>
<p>As with regular <em>mORMot</em> client code, a
<code>TServiceCalculator</code> instance is created and is assigned to a
<code>ICalculator</code> local variable. As such, no <code>try ... finally
Calc.Free end</code> block is mandatory here, to avoid any memory leak: the
compiler will create such an hidden block for the <code>Calc:
ICalculator</code> variable scope.</p>
<p>The service-side contract of the <code>ICalculator</code> signature is
retrieved and checked within <code>TServiceCalculator.Create</code>, and would
raise an <code>ERestException</code> if it does not match the contract
identified in <code>mORMotClient.pas</code>.</p>
<p>The cross-platform clients are able to manage the service instance
life-time, especially the <code>sicPerClient</code> mode. In this case, an
implementation class instance will be created on the server for each client,
until the corresponding <code>interface</code> instance will released (i.e. out
of scope or assigned to <code>nil</code>), which will release the server-side
instance - just like with a regular <em>mORMot</em> client code.</p>
<p>Note that all process here is executed <em>synchronously</em>, i.e. in
blocking mode. It is up to you to ensure that your application is able to still
be responsive, even if the server does a lot of process, so may be late to
answer. A dedicated thread may help in this case.</p>Cross-Platform mORMot Clients - Generating Code Wrappersurn:md5:86cc040b427c36a5c9fec81b3bc9a9b32014-08-11T20:41:00+02:002014-08-12T08:54:36+02:00AB4327-GANDImORMot FrameworkAJAXblogCrossPlatformDelphiDocumentationDTOFireMonkeyFreePascalHTMLHTTPHTTPSiOSJavaScriptJSONModelmORMotMustachenativeNextGenOpenSourceORMperformancerecordRestSOASynopseUserInterfaceweb<p>Current version of the main framework units target only <em>Win32</em> and
<em>Win64</em> systems.</p>
<p>It allows to make easy self-hosting of <em>mORMot</em> servers for local
business applications in any corporation, or pay cheap hosting in the Cloud,
since <em>mORMot</em> CPU and RAM expectations are much lower than a regular
<code>IIS-WCF-MSSQL-.Net</code> stack.<br />
But in a <em>Service-Oriented Architecture (SOA)</em>, you would probably need
to create clients for platforms outside the <em>Windows</em> world, especially
mobile devices.<br />
<img src="https://blog.synopse.info?post/public/mORMot/CrossPlatform.jpg" alt="" title="CrossPlatform, Aug 2014" /></p>
<p>A set of cross-platform client units is therefore available in the
<code>CrossPlatform</code> sub-folder of the source code repository. It allows
writing any client in modern <em>object pascal</em> language, for:</p>
<ul>
<li>Any version of <em>Delphi</em>, on any platform (<em>Mac OSX</em>, or any
mobile supported devices);</li>
<li><em>FreePascal</em> Compiler 2.7.1;</li>
<li><em>Smart Mobile Studio</em> 2.1, to create AJAX or mobile applications
(via <em>PhoneGap</em>, if needed).</li>
</ul>
<p>This series of articles will introduce you to <em>mORMot</em>'s
Cross-Platform abilities:</p>
<ul>
<li><a href="https://blog.synopse.info?post/post/2014/08/11/CrossPlatform-Clients/Units-Platforms">Units and
platforms</a>;</li>
<li><a href="https://blog.synopse.info?post/post/2014/08/11/CrossPlatform-Clients/Generate-Code">Generating
the client code wrappers</a>;</li>
<li><a href="https://blog.synopse.info?post/post/2014/08/11/CrossPlatform-Clients/Delphi-FreePascal"><em>Delphi</em> /
<em>FreePascal</em> clients</a>;</li>
<li><a href="https://blog.synopse.info?post/post/2014/08/11/CrossPlatform-Clients/SmartMobileStudio"><em>Smart Mobile
Studio</em> clients</a>.</li>
</ul>
<p>Any feedback is <a href="http://synopse.info/forum/viewtopic.php?id=1939">welcome in our forum</a>, as
usual!</p> <h3>Generating client wrappers</h3>
<p>Even if it feasible to write the client code by hand, your <em>mORMot</em>
server is able to create the source code needed for client access, via a
dedicated method-based service, and set of <em>Mustache</em>-based templates -
see <em><a href="https://blog.synopse.info?post/post/2014/04/28/Mustache-Logic-less-templates-for-Delphi-part-1">Mustache
template engine</a></em>.</p>
<p>The following templates are available in the
<code>CrossPlatformtemplates</code> folder:</p>
<table>
<tbody>
<tr>
<td><strong>Unit Name</strong></td>
<td><strong>Compiler Target</strong></td>
</tr>
<tr>
<td><code>CrossPlatform.pas.mustache</code></td>
<td><em>Delphi</em> / FPC SynCrossPlatform* units</td>
</tr>
<tr>
<td><code>Delphi.pas.mustache</code></td>
<td><em>Delphi</em> Win32/Win64 <em>mORMot</em> units</td>
</tr>
<tr>
<td><code>SmartMobileStudio.pas.mustache</code></td>
<td>Smart Mobile Studio 2.1</td>
</tr>
</tbody>
</table>
<p>In the future, other wrappers may be added. And you can write your own,
which could be included within the framework source! Your input is warmly
welcome, especially if you want to write a template for <em>Java</em> or
<code>C#</code> client. The generated data context already contains the data
types corresponding to those compilers: e.g. a <em>mORMot</em>'s
<code>RawUTF8</code> field or parameter could be identified as
<code>"typeCS":"string"</code> or <code>"typeJava":"String"</code> in addition
to <code>"typeDelphi":"RawUTF8"</code> and
<code>"typePascal":"string"</code>.</p>
<h3>Publishing the code generator</h3>
<p>By default, and for security reasons, the code generation is not embedded to
your <em>mORMot</em> RESTful server. In fact, the
<code>mORMotWrapper.pas</code> unit will link both <code>mORMot.pas</code> and
<code>SynMustache.pas</code> units, and use <em>Mustache</em> templates to
generate code for a given <code>TSQLRestServer</code> instance.</p>
<p>We will start from the interface-based service <em>Sample code</em> as
defined in the<br />
"<code>SQLite3- Interface based services</code>" folder.<br />
After some minor modifications, we copied the server source code into<br />
"<code>SQLite3- CrossPlatform
ClientsProject14ServerHttpWrapper.dpr</code>":</p>
<pre>
<strong>program</strong> Project14ServerHttpWrapper;
<br /><em>{$APPTYPE CONSOLE}</em>
<br /><strong>uses</strong>
SysUtils,
Classes,
SynCommons,
mORMot,
mORMotHttpServer,
<span style="background-color:yellow;">mORMotWrappers,</span>
Project14Interface <strong>in</strong> '..\14 - Interface based services\Project14Interface.pas';
<br /><strong>type</strong>
TServiceCalculator = <strong>class</strong>(TInterfacedObject, ICalculator)
<strong>public</strong>
<strong>function</strong> Add(n1,n2: integer): integer;
<strong>end</strong>;
<br /><strong>function</strong> TServiceCalculator.Add(n1, n2: integer): integer;
<strong>begin</strong>
result := n1+n2;
<strong>end</strong>;
<br /><strong>var</strong>
aModel: TSQLModel;
aServer: TSQLRestServer;
aHTTPServer: TSQLHttpServer;
<strong>begin</strong>
<em>// create a Data Model</em>
aModel := TSQLModel.Create([],ROOT_NAME);
<strong>try</strong>
<em>// initialize a TObjectList-based database engine</em>
aServer := TSQLRestServerFullMemory.Create(aModel,'test.json',false,true);
<strong>try</strong>
<span style="background-color:yellow;"><em>// add the http://localhost:888/root/wrapper code generation web page</em></span>
<span style="background-color:yellow;">AddToServerWrapperMethod(aServer,</span>
<span style="background-color:yellow;">['..\..\..\CrossPlatform\templates','..\..\..\..\CrossPlatform\templates']);</span>
<em>// register our ICalculator service on the server side</em>
aServer.ServiceRegister(TServiceCalculator,[TypeInfo(ICalculator)],sicShared);
<em>// launch the HTTP server</em>
aHTTPServer := TSQLHttpServer.Create(PORT_NAME,[aServer],'+',useHttpApiRegisteringURI);
<strong>try</strong>
aHTTPServer.AccessControlAllowOrigin := '*'; <em>// for AJAX requests to work</em>
writeln(#10'Background server is running.');
writeln('You can test http://localhost:',PORT_NAME,'/wrapper');
writeln(#10'Press [Enter] to close the server.'#10);
readln;
<strong>finally</strong>
aHTTPServer.Free;
<strong>end</strong>;
<strong>finally</strong>
aServer.Free;
<strong>end</strong>;
<strong>finally</strong>
aModel.Free;
<strong>end</strong>;
<strong>end</strong>.
</pre>
<p>As you can see, we just added a reference to the <code>mORMotWrappers</code>
unit, and a call to <code>AddToServerWrapperMethod()</code> in order to publish
the available code generators.</p>
<p>Now, if you run the <code>Project14ServerHttpWrapper</code> server, and
point your favorite browser to <code>http://localhost:888/root/wrapper</code>
you will see the following page:</p>
<blockquote>
<p><strong>Client Wrappers</strong></p>
<p><strong>Available Templates:</strong></p>
<p>* <strong>CrossPlatform<br /></strong><em>mORMotClient.pas</em> -
<ins>download as file</ins> - <ins>see as text</ins> - <ins>see
template</ins></p>
<p>* <strong>Delphi<br /></strong><em>mORMotClient.pas</em> - <ins>download as
file</ins> - <ins>see as text</ins> - <ins>see template</ins></p>
<p>* <strong>SmartMobileStudio<br /></strong><em>mORMotClient.pas</em> -
<ins>download as file</ins> - <ins>see as text</ins> - <ins>see
template</ins></p>
<p>You can also retrieve the corresponding <ins>template context</ins>.</p>
</blockquote>
<p>Each of the <code>*.mustache</code> template available in the specified
folder is listed here. Links above will allow downloading a client source code
unit, or displaying it as text in the browser. The template can also be
displayed un-rendered, for reference. As true <em>Mustache</em> templates, the
source code files are generated from a <em>data context</em>, which can be
displayed, as JSON, from the "<ins>template context</ins>" link. It may help
you when debugging your own templates. Note that if you modify and save a
<code>.mustache</code> template file, just re-load the "<ins>see as text</ins>"
browser page and your modification is taken in account immediately (you do not
need to restart the server).</p>
<p>Generated source code will follow the template name, and here will always be
downloaded as <code>mORMotClient.pas</code>. Of course, you can change the unit
name for your end-user application. It could be even mandatory if the same
client would access to several <em>mORMot</em> servers at once, which could be
the case in a <em>Service-Oriented Architecture (SOA)</em> project.</p>
<p>Just ensure that you will never change the <code>mORMotClient.pas</code>
generated content by hand. If necessary, you can create and customize your own
<em>Mustache</em> template, to be used for your exact purpose. By design, such
automated code generation will require to re-create the client unit each time
the server ORM or SOA structure is modified. In fact, as stated in the
<code>mORMotClient.pas</code> comment, any manual modification of this file may
be lost after regeneration. You have been warned!</p>
<p>If you feel that the current templates have some issues or need some
enhancements, you are very welcome to send us your change requests on our
forums. Once you are used at it, <em>Mustache</em> templates are fairly easy to
work with. Similarly, if you find out that some information is missing in the
generated <em>data context</em>, e.g. for a new platform or language, we would
be pleased to enhance the official <code>mORMotWrapper.pas</code> process.</p>
<h2>Visit our source code repository</h2>
<p>There is nothing better than some real files.</p>
<p>You can take a look at the following files in our source code
repository:</p>
<ul>
<li>The <a href="https://github.com/synopse/mORMot/tree/master/CrossPlatform/templates"><em>.mustache</em>
template files</a>;</li>
<li>The <em><a href="https://github.com/synopse/mORMot/tree/master/CrossPlatform">SynCrossPlatform*.pas</a></em>
units;</li>
<li>A generated <em><a href="https://github.com/synopse/mORMot/blob/master/SQLite3/Samples/27%20-%20CrossPlatform%20Clients/mORMotClient.pas">
mORMotClient.pas</a></em> unit for <em>Delphi</em> / <em>FPC</em> -
generated from <em><a href="https://github.com/synopse/mORMot/tree/master/SQLite3/Samples/27%20-%20CrossPlatform%20Clients">
RegressionTestsServer.dpr</a></em>;</li>
<li>The same <em><a href="https://github.com/synopse/mORMot/blob/master/SQLite3/Samples/29%20-%20SmartMobileStudio%20Client/mORMotClient.pas">
mORMotClient.pas</a></em> unit for <em>Smart Mobile Studio</em> - also
generated from <em><a href="https://github.com/synopse/mORMot/tree/master/SQLite3/Samples/27%20-%20CrossPlatform%20Clients">RegressionTestsServer.dpr</a></em>;</li>
<li>Another simplier <a href="https://github.com/synopse/mORMot/blob/master/SQLite3/Samples/27%20-%20CrossPlatform%20Clients/SmartMobileStudio/mORMotClient.pas">
<em>mORMotClient.pas</em></a> unit for <em>Smart Mobile Studio</em> - generated
from <em><a href="https://github.com/synopse/mORMot/blob/master/SQLite3/Samples/27%20-%20CrossPlatform%20Clients/Project14ServerHttpWrapper.dpr">
Project14ServerHttpWrapper.dpr</a></em>.</li>
</ul>
<div>Enjoy!</div>Cross-Platform mORMot Clients - Units and Platformsurn:md5:150a3105836e67fd082f8e9ff8032f532014-08-11T20:40:00+02:002014-08-12T08:50:34+02:00AB4327-GANDImORMot FrameworkAJAXblogCrossPlatformDelphiDocumentationDTOFireMonkeyFreePascalHTMLHTTPHTTPSiOSJavaScriptJSONModelmORMotMustachenativeNextGenOpenSourceORMperformancerecordRestSOASynopseUserInterfaceweb<p>Current version of the main framework units target only <em>Win32</em> and
<em>Win64</em> systems.</p>
<p>It allows to make easy self-hosting of <em>mORMot</em> servers for local
business applications in any corporation, or pay cheap hosting in the Cloud,
since <em>mORMot</em> CPU and RAM expectations are much lower than a regular
<code>IIS-WCF-MSSQL-.Net</code> stack.<br />
But in a <em>Service-Oriented Architecture (SOA)</em>, you would probably need
to create clients for platforms outside the <em>Windows</em> world, especially
mobile devices.<br />
<img src="https://blog.synopse.info?post/public/mORMot/CrossPlatform.jpg" alt="" title="CrossPlatform, Aug 2014" /></p>
<p>A set of cross-platform client units is therefore available in the
<code>CrossPlatform</code> sub-folder of the source code repository. It allows
writing any client in modern <em>object pascal</em> language, for:</p>
<ul>
<li>Any version of <em>Delphi</em>, on any platform (<em>Mac OSX</em>, or any
mobile supported devices);</li>
<li><em>FreePascal</em> Compiler 2.7.1;</li>
<li><em>Smart Mobile Studio</em> 2.1, to create AJAX or mobile applications
(via <em>PhoneGap</em>, if needed).</li>
</ul>
<p>This series of articles will introduce you to <em>mORMot</em>'s
Cross-Platform abilities:</p>
<ul>
<li><a href="https://blog.synopse.info?post/post/2014/08/11/CrossPlatform-Clients/Units-Platforms">Units and
platforms</a>;</li>
<li><a href="https://blog.synopse.info?post/post/2014/08/11/CrossPlatform-Clients/Generate-Code">Generating
the client code wrappers</a>;</li>
<li><a href="https://blog.synopse.info?post/post/2014/08/11/CrossPlatform-Clients/Delphi-FreePascal"><em>Delphi</em> /
<em>FreePascal</em> clients</a>;</li>
<li><a href="https://blog.synopse.info?post/post/2014/08/11/CrossPlatform-Clients/SmartMobileStudio"><em>Smart Mobile
Studio</em> clients</a>.</li>
</ul>
<p>Any feedback is <a href="http://synopse.info/forum/viewtopic.php?id=1939">welcome in our forum</a>, as
usual!</p> <h2>Involved units</h2>
The units are the following:
<table>
<tbody>
<tr>
<td><strong>Unit Name</strong></td>
<td><strong>Description</strong></td>
</tr>
<tr>
<td><code>SynCrossPlatformREST.pas</code></td>
<td>Main unit, implementing secured ORM and SOA RESTful client</td>
</tr>
<tr>
<td><code>SynCrossPlatformCrypto.pas</code></td>
<td>SHA-256 and crc32 algorithms, used for authentication</td>
</tr>
<tr>
<td><code>SynCrossPlatformJSON.pas</code></td>
<td>Optimized JSON process (not used by <em>Smart</em>)</td>
</tr>
<tr>
<td><code>SynCrossPlatformSpecific.pas</code></td>
<td>System-specific functions, e.g. HTTP clients</td>
</tr>
</tbody>
</table>
<p>This set of units will provide a solid and shared ground for the any kind of
clients:</p>
<ul>
<li>Connection to a <em>mORMot</em> server via HTTP, with full REST
support;</li>
<li>Support of weak or default authentication to secure the transfer - see
<em><a href="https://blog.synopse.info?post/post/2013/06/07/Authentication-and-Authorization">Authentication</a></em>;</li>
<li>Definition of the <code>TSQLRecord</code> class, using RTTI when available
on <em>Delphi</em> or <em>FreePascal</em>, and generated code for <em>Smart
Mobile Studio</em>;</li>
<li>Remote CRUD operations, via JSON and REST, with a
<code>TSQLRestClientURI</code> class, with the same methods as with the
<code>mORMot.pas</code> framework unit;</li>
<li>Optimized <code>TSQLTableJSON</code> class to handle a JSON result table,
as returned by <em>mORMot</em>'s REST server ORM - see <em><a href="https://blog.synopse.info?post/post/2010/07/02/JSON-format-of-a-RESTful-application">JSON (not) expanded
layouts</a></em>;</li>
<li>Batch process - see <em>BATCH sequences for adding/updating/deleting
records</em> - for transactional and high-speed writes;</li>
<li><em>Client-Server services via methods</em> with parameters
marshaling;</li>
<li><em>Client-Server services via interfaces</em> with parameters marshaling
and instance-life time;</li>
<li>Mapping of most supported field types, including e.g. ISO 8601 date/time
encoding, BLOBs and <code>TModTime</code>/<code>TCreateTime</code> - see
<em>TSQLRecord fields definition </em>in the SAD 1.18 pdf;</li>
<li>Complex <code>record</code> types are also exported and consumed via JSON,
on all platforms (for both ORM and SOA methods);</li>
<li>Some cross-platform low-level functions and types definitions, to help
share as much code as possible between your projects.</li>
</ul>
<p>In the future, C# or Java clients may be written.<br />
The <code>CrossPlatform</code> sub-folder code could be used as reference, to
write minimal and efficient clients on any platform. Our REST model is pretty
straightforward and standard, and use of JSON tends to leverage a lot of
potential marshaling issues which may occur with XML or binary formats.</p>
<p>In practice, a code generator embedded in the <em>mORMot</em> server can be
used to create the client wrappers, using the <em>Mustache template engine</em>
included on the server side. With a click, you can generate and download a
client source file for any supported platform. A set of <code>.mustache</code>
templates is available, and can be customized or extended to support any new
platform: any help is welcome, especially for targeting Java or C# clients.</p>
<h2>Available client platforms</h2>
<h3>Delphi FMX / FreePascal FCL cross-platform support</h3>
<p>Latest versions of <em>Delphi</em> include the <em>FireMonkey</em> (FMX)
framework, able to deliver multi-device, true native applications for
<em>Windows</em>, <em>Mac OSX</em>, <em>Android</em> and <em>iOS</em>
(<em>iPhone</em>/<em>iPad</em>).<br />
Our <code>SynCrossPlatform*</code> units are able to easily create clients for
those platforms.</p>
<p>Similarly, these units can be compiled with FreePascal, so that any
<em>mORMot</em> server could be consumed from the numerous supported platforms
of this compiler.</p>
<p>In order to use those units, ensure in your IDE that the
<code>CrossPlatform</code> sub-folder of the <em>mORMot</em> source code
repository is defined in your <em>Library Search Path</em>.</p>
<h3>Cross-platform JSON</h3>
<p>We developed our own cross-platform JSON process unit in
<code>SynCrossPlatformJSON.pas</code>, shared with <em>Delphi</em> and
<em>FreePascal</em>.<br />
In fact, it appears to be easier to use (since it is <code>variant</code>-based
and with <em>late-binding</em> abilities) and run much faster than the official
<code>DBXJSON.pas</code> unit shipped with latest versions of <em>Delphi</em>,
as stated by the "<code>25 - JSON performance</code>" sample:</p>
<pre>
2.2. Table content:
- Synopse crossplatform: 41,135 assertions passed 20.56ms 400,048/s 1.9 MB
- DBXJSON: 41,136 assertions passed 240.84ms 34,159/s 9.9 MB
</pre>
<p>Our <code>TSQLTableJSON</code> class is more than 10 times faster than
standard <code>DBXJSON</code> unit, when processing a list of results as
returned by a <em>mORMot</em> server.<br />
The latest value on each line above is the memory consumption. It should be of
high interest on mobile platforms, where memory allocation tends to be much
slower and sensitive than on Windows (where <em>FastMM4</em> memory manager
does wonders). Our unit consumes 5 times less memory than the RTL's
version.</p>
<p>We did not include <em>XSuperObject</em> here, which is cross-platform, but
performs even worse than <code>DBXJSON</code> in terms of speed. Other
libraries - as <em>SuperObject</em> or <em>dwsJSON</em> - are not
cross-platform.<br />
See <a href="http://blog.synopse.info/post/json-benchmark-delphi-mormot-superobject-dwsjson-dbxjson">
http://blog.synopse.info/post/json-benchmark-delphi-mormot-superobject-dwsjson-dbxjson</a>
for details about this comparison.</p>
<p>A special mention is due to <em>dwsJSON</em>, which performs very well, but
only on Windows, and is slower than <em>mORMot</em>'s implementation:</p>
<pre>
- Synopse ORM loop: 41,135 assertions passed 6.18ms 1,330,153/s 1.1 MB
- Synopse ORM list: 41,135 assertions passed 6.47ms 1,270,775/s 952 KB
- Synopse crossplatform: 41,135 assertions passed 20.56ms 400,048/s 1.9 MB
- Super object properties: 41,136 assertions passed 2.20s 3,739/s 6.3 MB
- dwsJSON: 41,136 assertions passed 32.05ms 256,628/s 4.7 MB
- DBXJSON: 41,136 assertions passed 240.84ms 34,159/s 9.9 MB
</pre>
<p>The "<code>Synopse ORM</code>" lines stand for the
<code>TSQLTableJSON</code> class as implemented in <code>mORMot.pas</code>. It
uses our optimized UTF-8 functions and classes, in-place escaping together with
our <code>RawUTF8</code> custom string type, so that it is 3 times faster than
our cross-platform units, and 40 times than <code>DBXJSON</code>, using much
less memory. Some tricks used by <code>Synopse ORM</code> rely on pointers and
are not compatible with the <em>NextGen</em> compiler or the official
<em>Delphi</em> road-map, so the <code>Synopse crossplatform</code> uses
diverse algorithm, but offers still pretty good performance.</p>
<p>This unit features a <code>TJSONVariantData</code> custom variant type,
similar to <em>TDocVariant custom variant type</em>, available in the main
<em>mORMot</em> framework.<br />
It allows writing such nice and readable code, with late-binding:</p>
<pre>
<strong>var</strong> doc: <strong>variant</strong>;
json,json2: <strong>string</strong>;
...
doc := JSONVariant('{"test":1234,"name":"Joh\"n\r","zero":0.0}');
assert(doc.test=1234);
assert(doc.name='Joh"n'#13);
assert(doc.name2=null);
assert(doc.zero=0);
json := doc; <em>// conversion to JSON text when assigned to a string variable</em>
assert(json='{"test":1234,"name":"Joh\"n\r","zero":0}');
doc.name2 := 3.1415926;
doc.name := 'John';
json := doc;
assert(json='{"test":1234,"name":"John","zero":0,"name2":3.1415926}');
</pre>
<p>The unit is also able to serialize any <code>TPersistent</code> class, i.e.
all published properties could be written or read from a JSON object
representation. It also handles nested objects, stored as
<code>TCollection</code>.<br />
See for instance in the <code>SynCrossPlatformTests</code> unit:</p>
<pre>
<strong>type</strong>
TMainNested = <strong>class</strong>(TCollectionItem)
<strong>private</strong>
fNumber: double;
fIdent: RawUTF8;
<strong>published</strong>
<strong>property</strong> Ident: RawUTF8 <strong>read</strong> fIdent <strong>write</strong> fIdent;
<strong>property</strong> Number: double <strong>read</strong> fNumber <strong>write</strong> fNumber;
<strong>end</strong>;
<br /> TMain = <strong>class</strong>(TPersistent)
<strong>private</strong>
fName: RawUTF8;
fNested: TCollection;
fList: TStringList;
<strong>public</strong>
<strong>constructor</strong> Create;
<strong>destructor</strong> Destroy; <strong>override</strong>;
<strong>published</strong>
<strong>property</strong> Name: RawUTF8 <strong>read</strong> fName <strong>write</strong> fName;
<strong>property</strong> Nested: TCollection <strong>read</strong> fNested;
<strong>property</strong> List: TStringList <strong>read</strong> fList;
<strong>end</strong>;
<br /> obj1 := TMain.Create;
obj2 := TMain.Create;
...
obj1.Name := IntToStr(i);
item := obj1.Nested.Add <strong>as</strong> TMainNested;
item.Ident := obj1.Name;
item.Number := i/2;
obj1.list.Add(obj1.Name);
json := ObjectToJSON(obj1);
<strong>if</strong> i=1 <strong>then</strong>
assert(json='{"Name":"1","Nested":[{"Ident":"1","Number":0.5}],"List":["1"]}');
JSONToObject(obj2,json);
assert(obj2.Nested.Count=i);
json2 := ObjectToJSON(obj2);
assert(json2=json);
...
</pre>
<p>Of course, this serialization feature is used for the
<code>TSQLRecord</code> ORM class.</p>
<p>Due to lack of RTTI, <code>record</code> serialization is supported via some
functions generated by the server with the code wrappers.</p>
<h3>Delphi OSX and NextGen</h3>
<p>In order to be compliant with the <a href="https://blog.synopse.info?post/post/2013/05/11/Delphi-XE4-NextGen-compiler-is-disapointing"><em>NextGen</em></a> revision,
our <code>SynCrossPlatform*</code> units follow the expectations of this new
family of cross-compilers, which targets <em>Android</em> and
<em>iOS</em>.<br />
In particular, we rely only on the <code>string</code> type for text process
and storage, even at JSON level, and we tried to make object allocation
ARC-compatible. Some types have been defined, e.g. <code>THttpBody</code>,
<code>TUTF8Buffer</code> or <code>AnsiChar</code>, to ensure that our units
would compile on all supported platforms.</p>
<p>On <em>Delphi</em>, the <em>Indy</em> library is used for HTTP requests. It
is cross-platform by nature, so should work on any supported system. For SSL
support with <em>iOS</em> and <em>Android</em> clients, please follow
instructions at <a href="http://blog.marcocantu.com/blog/using_ssl_delphi_ios.html">http://blog.marcocantu.com/blog/using_ssl_delphi_ios.html</a>
you may also download the needed <code>libcrypto.a</code> and
<code>libssl.a</code> files from <a href="http://indy.fulgan.com/SSL/OpenSSLStaticLibs.7z">http://indy.fulgan.com/SSL/OpenSSLStaticLibs.7z</a></p>
<p>Feedback is needed for the mobile targets, via FMX.<br />
In fact, we rely for our own projects on <em>Smart Mobile Studio</em> for our
mobile applications, so the <em>Synopse</em> team did not test <em>Delphi
NextGen</em> platforms (i.e. <em>iOS</em> and <em>Android</em>) as deep as
other systems. Your input would be very valuable and welcome, here!</p>
<h3>FreePascal clients</h3>
<p><code>SynCrossPlatform*</code> units support the <em>FreePascal</em>
Compiler, in its 2.7.1 revision.<br />
Most of the code is shared with <em>Delphi</em>, including RTTI support and all
supported types.</p>
<p>Some restrictions apply, though.</p>
<p>Due to a bug in <em>FreePascal</em> implementation of <code>variant</code>
late binding, the following code won't work as expected:</p>
<pre>
doc.name2 := 3.1415926;
doc.name := 'John';
</pre>
<p>Under <em>FreePascal</em>, you have to write:</p>
<pre>
TJSONVariantData(doc)['name2'] := 3.1415926;
TJSONVariantData(doc)['name'] := 'John';
</pre>
<p>In fact, the way late-binding properties are implemented in the
<em>FreePascal</em> RTL forbid to modify the content of the associated
<code>variant</code>. A private copy of the <code>variant</code> is made, which
is not only slower, but disallows modification of its stored value.<br />
Any feedback and help from the <em>FreePascal</em> maintainers may be
welcome!</p>
<p>As a result, direct access to <code>TJSONVariantData</code> instances, and
not a <code>variant</code> variable, would be faster and less error-prone when
using this compiler, until the issue is fixed.</p>
<p>Another issue with the 2.7.1 revision is how the new <code>string</code>
type is implemented.<br />
In fact, if you use a string variable containing an UTF-8 encoded text, then
the following line would reset the result code page to the system code
page:</p>
<pre>
<strong>function</strong> StringToJSON(<strong>const</strong> Text: <strong>string</strong>): <strong>string</strong>;
...
result := '"'+copy(Text,1,j-1); <em>// here FPC 2.7.1 erases UTF-8 encoding</em>
...
</pre>
<p>It sounds like if <code>'"'</code> will force the code page of
<code>result</code> to be not an UTF-8 content.<br />
With <em>Delphi</em>, this kind of statements work as expected, even for
<code>AnsiString</code> values, and <code>'"'</code> constant is handled as
<code>RawByteString</code>. We were not able to find an easy and safe
workaround for FPC yet. Input is welcome in this area, from any expert!</p>
<p>You have to take care of this limitation, if you target the <em>Windows</em>
operating system with FPC (and <em>Lazarus</em>). Under other systems, the
default code page is likely to be UTF-8, so in this case our
<code>SynCrossPlatform*</code> units will work as expected.</p>
<p>We found out the <em>FreePascal</em> compiler to work very well, and result
in small and fast executables. For most common work, timing is comparable with
<em>Delphi</em>. The memory manager is less optimized than <em>FastMM4</em> for
rough simple threaded tests, but is cross-platform and much more efficient in
multi-thread mode: in fact, it has no giant lock, as <em>FastMM4</em>
suffers.</p>
<h2>Smart Mobile Studio support</h2>
<p><em>Smart Mobile Studio</em> - see <a href="http://www.smartmobilestudio.com">http://www.smartmobilestudio.com</a> - is a
complete RAD environment for writing cutting edge HTML5 mobile applications. It
ships with a fully fledged compiler capable of compiling <em>Object Pascal</em>
(in a modern dialect call <em>SmartPascal</em>) into highly optimized and raw
<em>JavaScript</em>.</p>
<p>There are several solutions able to compile to <em>JavaScript</em>.<br />
In fact, we can find several families of compilers:</p>
<ul>
<li><em>JavaScript</em> super-sets, adding optional <em>strong typing</em>, and
classes, close to the <em>ECMAScript Sixth Edition</em>: the current main
language in this category is certainly <em><a href="http://www.typescriptlang.org/">TypeScript</a></em>, designed by Anders
Hejlsberg (father of both the <em>Delphi</em> language and <em>C#</em>), and
published by <em>Microsoft</em>;</li>
<li>New languages, dedicated to make writing <em>JavaScript</em> programs
easier, with an alternative syntax and new concepts (like classes, lambdas,
scoping, splats, comprehensions...): most relevant languages of this family are
<em><a href="http://coffeescript.org/">CoffeeScript</a></em> and <em><a href="https://www.dartlang.org/">Dart</a></em>;</li>
<li>High-level languages, like <em><a href="http://www.gwtproject.org/">Google
Web Toolkit</a></em> (compiling <em>Java</em> code), <em><a href="http://jsil.org/">JSIL</a></em> (from <em>C#</em> via <em>Mono</em>), or
<em><a href="http://smartmobilestudio.com/">Smart Mobile Studio</a></em> (from
<em>object pascal</em>);</li>
<li>Low-level languages, like <em><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Projects/Emscripten">Emscripten</a></em>
(compiling C/C++ from LLVM byte-code, using <em>asm.js</em>).</li>
</ul>
<p>Of course, from our point of view, use of modern <em>object pascal</em> is
of great interest, since it will leverage our own coding skills, and make us
able to share code between client and server sides.</p>
<h3>Beyond JavaScript</h3>
<p>The so-called <em><a href="http://en.wikipedia.org/wiki/The_Smart_Pascal_programming_language">Smart
Pascal</a></em> language brings strong typing, true OOP to <em>JavaScript</em>,
including classes, partial classes, interfaces, inheritance, polymorphism,
virtual and abstract classes and methods, helpers, closures, lambdas,
enumerations and sets, getter/setter expressions, operator overloading,
contract programming. But you can still unleash the power of
<em>JavaScript</em> (some may say "the good parts"), if needed: the
<code>variant</code> type is used to allow dynamic typing, and you can write
some <em>JavaScript</em> code as an <code>asm .. end</code> block.<br />
See <a href="http://en.wikipedia.org/wiki/The_Smart_Pascal_programming_language">http://en.wikipedia.org/wiki/The_Smart_Pascal_programming_language</a></p>
<p>The resulting HTML5 project is self-sufficient with no external
<em>JavaScript</em> library, and is compiled as a single
<code>index.html</code> file (including its <code>css</code>, if needed). The
<em>JavaScript</em> code generated by the compiler (written in <em>Delphi</em>
by Eric Grange), is of very high quality, optimized for best execution
performance (either in JIT or V8), has low memory consumption, and can be
compressed and/or obfuscated.</p>
<p>The <code>SmartCL</code> runtime library encapsulate HTML5 APIs in a set of
pure pascal classes and functions, and an IDE with an integrated form designer
is available. You can debug your application directly within the IDE (since
revision 2.1 - even if it is not yet always stable) or within your browser (IE,
Chrome or FireBug have great debuggers), with step-by-step execution of the
object pascal code (if you define "<em>Add source map (for debugging)</em>" in
<code>Project Options</code> / <code>Linker</code>).</p>
<p>Using a third-party tool like <em>PhoneGap</em> - see <a href="http://phonegap.com">http://phonegap.com</a> - you would be able to supply
your customers with true native <em>iOS</em> or <em>Android</em> applications,
running without any network, and accessing the full power of any modern
<em>Smart Phone</em>. Resulting applications will be much smaller in size than
the one generated by <em>Delphi</em> FMX (a simple <em>Smart</em> RESTful
client with a login form and ORM + SOA tests is zipped as 40 KB), and will work
seamlessly on all HTML5 platforms, including most mobile (like Windows Phone,
Blackberry, Firefox OS, or webOS) or desktop (Windows, Linux, BSD, MacOS)
architectures.</p>
<p><em>Smart Mobile Studio</em> is therefore a great platform for implementing
rich client-side AJAX or <em>Mobile</em> applications, to work with our
client-server <em>mORMot</em> framework.</p>
<h3>Using Smart Mobile Studio with mORMot</h3>
<p>There is no package to be installed within the <em>Smart Mobile Studio</em>
IDE. The client units will be generated directly from the <em>mORMot</em>
server.<br />
Any edition of <em>Smart</em> - see <a href="http://smartmobilestudio.com/feature-matrix">http://smartmobilestudio.com/feature-matrix</a>
- is enough: you do not need to pay for the <em>Enterprise</em> edition to
consume <em>mORMot</em> services. But of course, the <em>Professionnal</em>
edition is recommended, since the <em>Basic</em> edition does not allow to
create forms from the IDE, which is the main point for an AJAX application.</p>
<p>In contrast to the wrappers available in the
<em>Entreprise </em>edition of Smart, for accessing <em>RemObjects</em> or
<em>DataSnap</em> servers, our <em>mORMot</em> clients are 100% written in the
<em>SmartPascal</em> dialect. There is no need to link an external
<code>.js</code> library to your executable, and you will benefit of the
obfuscation and smart linking features of the Smart compiler.</p>
<p>The only requirement is to copy the <em>mORMot</em> cross-platform units to
your <em>Smart Mobile Studio</em> installation. This can be done in three
<code>copy</code> instructions:</p>
<pre>
xcopy SynCrossPlatformSpecific.pas "c:\ProgramData\Optimale Systemer AS\Smart Mobile Studio\Libraries" /Y
xcopy SynCrossPlatformCrypto.pas "c:\ProgramData\Optimale Systemer AS\Smart Mobile Studio\Libraries" /Y
xcopy SynCrossPlatformREST.pas "c:\ProgramData\Optimale Systemer AS\Smart Mobile Studio\Libraries" /Y
</pre>
<p>You can find a corresponding BATCH file in the <code>CrossPlatform</code>
folder, and in <code>SQLite3- SmartMobileStudio
ClientCopySynCrossPlatformUnits.bat</code>.</p>
<p>In fact, the <code>SynCrossPlatformJSON.pas</code> unit is not used under
<em>Smart Mobile Studio</em>: we use the built-in JSON serialization features
of <em>JavaScript</em>, using <code>variant</code> dynamic type, and the
standard <code>JSON.Stringify()</code> and <code>JSON.Parse()</code>
functions.</p>FreePascal Lazarus and Android Native Controlsurn:md5:97a1241f4bb2582b88e32b75164ee97d2013-09-19T11:41:00+02:002013-09-19T11:24:38+02:00AB4327-GANDIPascal ProgrammingAndroidblogCrossPlatformDelphiFireMonkeyFreePascalHTTPiOSJavamORMotSourceUnicodeUserInterface<p>We all know that <a href="http://blog.marcocantu.com/blog/first_delphi_android.html">the first Delphi
for Android was just released</a>...</p>
<p>I just found out <a href="http://blog.naver.com/PostView.nhn?blogId=simonsayz&logNo=120196870149&redirect=Dlog">
an amazing alternative, using native Android controls</a>, and FPC/Lazarus as
compiler and IDE.</p>
<p><img src="http://preyproject.com/up/2010/12/android_platform.png" alt="" /></p>
<p>It creates <em>small</em> .apk file: only 180 KB, from my tests!</p>
<p>It makes use of <a href="https://github.com/alrieckert/lazarus/tree/master/lcl/interfaces/customdrawn/android">
direct LCL access of Android native controls</a>, so it is a great sample.</p> <p>What I like very much is the following:</p>
<ul>
<li>It uses <em>native</em> UI controls, so you do not suffer from <a href="http://qc.embarcadero.com/wc/qcmain.aspx?d=98644">FireMonkey restrictions
about Unicode/RTL languages and such</a>, and have a <em>native</em> look and
fill;</li>
<li>It consists in a set of Java classes, used as <em>glue</em> to the Android
platform, accessed via <a href="http://en.wikipedia.org/wiki/Java_Native_Interface">JNI</a> from a
FPC-compiled library;</li>
<li>It creates very small application files (great for downloading) - the FPC
compiler generates a <em>.so</em> binary library which compresses to a bit more
than 100 KB;</li>
<li>There is some nice low-level units in JNI for bridging the object pascal
code to Java;</li>
<li>Most of the logic is written in Java itself, so you do not have to fight
with event or low-level Java structures translations in object pascal
code;</li>
<li>Framework code is still readable and does not suffer from multi-platform
targeting;</li>
<li>You can re-use you own existing object pascal code, with <a href="https://blog.synopse.info?post/post/2013/05/20/Are-we-the-milch-cow-of-Embarcadero">no problem of
restrictions/regressions due to Delphi <em>NextGen</em> compiler</a>;</li>
<li>As a result, you are encouraged to separate your business logic from your
UI code - which is a good idea;</li>
<li>Sounds to be stable in practice, even in early stage - its "glued" design
sounds easier to debug than the huge FireMonkey, if you are able to read some
limited Java code;</li>
<li>You have a <a href="http://blog.naver.com/simonsayz/120193098772">sibling Native iOS Controls
project using FPC available</a> to target also <em>iPhone/iPad</em>
devices;</li>
<li>It is Open and expandable, so you are able to fork the project if
needed.</li>
</ul>
<div>Drawback is that:</div>
<div>
<ul>
<li>It is more like a proof-of-concept than a whole framework, in its current
stage;</li>
<li>It is not well known nor supported by a big company (worth a new tag in
Stack Overflow?);</li>
<li>It is free so you won't give away your money to Embarcadero.</li>
</ul>
<div>It is definitively one step forward for pushing us in direction to FPC
full support for <em>mORMot</em>!</div>
</div>Our mORMot won't hibernate this winter, thanks to FireMonkeyurn:md5:4b1e8a46cc307e0a0e1700b1fdb4f3872011-08-08T06:41:00+02:002011-08-09T08:37:44+02:00AB4327-GANDImORMot Framework64bitasmBusinessRulesCrossPlatformDatabaseDelphiFireMonkeyFreePascaliOSLinuxMaxOSXmORMotORMSourceSQLite3Unicode<p>Everybody is buzzing about <em>FireMonkey</em>...</p>
<p><img src="http://blogs.embarcadero.com/files/2011/08/firemonkey-medium_4234-300x93.png" /></p>
<p>Our little <em>mORMot</em> will like <em>FireMonkey</em>!<br />
Here is why...</p> <h3>What is FireMonkey?</h3>
<p><em>FireMonkey</em> is a scalable vector graphics based GUI framework
exploiting GPU capabilities for hardware accelerated cross platform GUI’s,
running on Windows, Mac OS X and iOS.</p>
<p>It's based on previous work of Eugene A. Kryukov, who <a href="http://www.ksdev.com/">has been hired by Embarcadero last year</a>.</p>
<p>This previous framework was named <em>DXScene</em>, and you can still browse
a lot of pictures, even on iOS support <a href="http://ksdev.blogspot.com/">on
Eugene's blog</a>.</p>
<h3>What's up, doc?</h3>
<p><em>FireMonkey</em> is the main feature of XE2, but not the only one.</p>
<p>From the compiler point of view, the main features are 64 bit compilation,
and OS X cross-compilation and cross-debugging. You'll need to buy a Mac (or
try some hack to <a href="http://www.redmondpie.com/how-to-install-os-x-snow-leopard-in-vmware-windows-7-9140301/">
install a virtual Mac on your PC</a>) in order to test it, but it's
very promising.</p>
<p>In comparison, the <em>DataSnap</em> enhancements included in XE2 are not so
groovy: client proxy generation – for Android, .NET, Objective-C, Blackberry,
iOS etc.. Should not be so difficult to implement, in a RESTful / JSON
world...</p>
<p><em>LiveBindings</em> – a way to create ‘live” relationships not just
between objects but between individual properties of objects - is a tempting
technology. Sounds more compatible with the n-Tier model than the previous RAD
approach.</p>
<h3>Our mORMot won't hibernate this winter</h3>
<p>Thanks to <em>FireMonkey</em>, our <em>mORMot / SQLite3</em> framework will
be able to stay awake during this winter.<br />
<img src="https://blog.synopse.info?post/public/mORMot.png" alt="" title="mORMot, août 2011" /></p>
<p>Here is why:</p>
<ul>
<li>Delphi XE2 still lacks of a standard ORM, and our framework is just there,
with some unique features (like its Client-Server, SOA architecture);</li>
<li>Its n-Tier design makes it easy to change the presentation layer: the
RESTful server can be accessed, through JSON, by light clients running on OS X
or iOS - our <em>mORMot</em> is just waiting for <em>FireMonkey</em> to
appear at its burrow's entrance - it is already <em>LiveBindings</em>
friendly;</li>
<li>Our kernel database is <em>SQLite3</em>, which is already part of both
<a href="http://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man1/sqlite3.1.html">
OS X</a> and <a href="http://developer.apple.com/technologies/ios/data-management.html">iOS</a>;</li>
<li>Our code was <a href="https://blog.synopse.info?post/post/2010/08/10/Writing-Delphi-code-for-64-bits-compiler">designed to be 64
bit ready</a>, and since the optimized <em>asm</em> functions have their "pure
pascal" version, it will be able to run on ARM - even low-level performance
tricks (<code>PCardinal(...)^=..</code>) will work as expected, since iOS uses
<a href="http://en.wikipedia.org/wiki/Endianness">little-endian</a> ARM mode,
just as x86/x64 CPUs (as Android BTW);</li>
<li>Most of the framework low-level stuff is concentrated in the <em><a href="http://synopse.info/fossil/finfo?name=SynCommons.pas">SynCommons</a></em> unit
- we tried to avoid code duplication where possible, and there is already a
full coverage of regression tests, ready to be run on new Operating Systems /
CPUs;</li>
<li>We don't rely on third-party components, and we manage the full source code
of all our used units;</li>
<li>Some Windows-specific units (like <em><a href="https://blog.synopse.info?post/post/2010/03/20/GIF%2C-TIF%2C-PNG-and-JPG-pictures-TGraphic-read/write-via-GDI">
SynGdiPlus</a></em> or such) are already simple and abstract enough to be
implemented easily on another platform - and some specific classes (like the
<a href="https://blog.synopse.info?post/post/2011/03/11/HTTP-server-using-fast-http.sys-kernel-mode-server"><em>http.sys</em>
kernel server</a>) have their own "generic" version (using plain sockets
or <em>Fastcgi</em>).</li>
</ul>
<p>So we are waiting for the fire to be released... monkeys and marmots on your
mark!</p>
<h3>FPC at the end of the tunnel</h3>
<p>For its ARM back-end, it sounds like if Delphi will use FPC / Lazarus on the
OS X site, within XCode, to create iOS applications. Even if I'm quite sure
Embarcadero will get rid of the FPC compatibility as soon as possible (i.e.
when their ARM compiler back-end will be available), there was certainly some
internal debate in the San Francisco's building to embed or not FPC to Delphi
XE2. I'm quite happy that <a href="https://blog.synopse.info?post/post/2010/08/14/FPC-and-Delphi%3A-toward-a-%22fratricidal-war%22">the war I
spoke about one year ago</a> did not occur.</p>
<p>I don't know if it comes from open-mindedness or from pragmatism,
but it surely put some lights on <a href="http://www.freepascal.org/">Free
Pascal</a>, which deserves it.</p>
<p>And since it will most probably use either <a href="http://developer.apple.com/carbon/">Carbon on MacOS X</a> (this is the
main reason why Delphi XE2 is not able to produce 64 bit applications yet),
or <a href="http://developer.apple.com/library/ios/#referencelibrary/GettingStarted/URL_iPhone_OS_Overview/_index.html#//apple_ref/doc/uid/TP40007592-CH1-SW3">the
C APIs of iOS</a>, you will be able to use the same standard "Delphi" syntax
for all platform, and won't have to use <a href="http://wiki.freepascal.org/FPC_PasCocoa"><em>Objective
Pascal</em> paradigm</a>.</p>
<p>Feedback and comments <a href="http://synopse.info/forum/viewtopic.php?pid=2423#p2423">are welcome on our
forum</a>.</p>