#1 2012-09-11 11:23:03

Bascy
Member
From: The Netherlands
Registered: 2012-06-22
Posts: 108

Unregistering a RestServer with TSQLite3HttpServer

I can use TSQLite3HttpServer.AddServer to add a newly created TRestServer to a HttpServer, but how do i remove the RestServer from TSQLite3HttpServer?

Offline

#2 2012-09-11 11:49:44

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,612
Website

Re: Unregistering a RestServer with TSQLite3HttpServer

The only option by now is to release the TSQLite3HttpServer instance, which will release all HTTP server threads, then call AddServer() for the servers to remain.

The underlying THTtpApiServer class do not have a RemoveUrl() method to be called for a specific URI.
It could be defined, but is it really necessary?

Offline

#3 2012-09-11 11:59:13

Bascy
Member
From: The Netherlands
Registered: 2012-06-22
Posts: 108

Re: Unregistering a RestServer with TSQLite3HttpServer

Well ... I had kind of a plan for our new server process that should work as follows:

The server contains a single TSQLite3HttpServer, which is created on startup, and it contains one TSQLRestServerDB. There is an interface registered to this RestServer with the following declaration:

type
  ILCSServer = interface(IInvokable)
  ['{D54E257A-A26D-4F69-8998-4D3646137828}']
    {1 open the database as specified in the connectionstring, return if this was succesfull - return the root URI of
        the associated TSQLRestServer in var param URI - aConnectionstring contains all info needed to connect using
        <protocol>://server/name syntax - if aCreate is true than create the database if it doent exist }
    function OpenDatabase(aConnectionString: RawUTF8; var URI: RawUTF8; aCreate: boolean = false): Boolean;
    function CloseDatabase(const aURI: RawUTF8): boolean;
  end;

When a client connects to server and calls OpenDatabase, a new TSQLRestServerDB is created by the main server thread, and this new TSQLRestServerDB is connected to the database mentioned in the connectionstring. The new TSQLRestServerDB is also registered with the TSQLiteHttpServer so the client that opened the database, can now access this database through a newly created client? not sure here). This way a client can open multiple databases.

When the client is done with the database it should close it, hence the request for an Unregister() method.

Maybe i'm trying to create this all wrong .. if so .. please advise ....

Offline

#4 2012-09-11 12:37:28

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,612
Website

Re: Unregistering a RestServer with TSQLite3HttpServer

I've added THttpApiServer.RemoveUrl() and TSQLite3HttpServer.RemoveServer() methods.
Feedback is welcome about those (I did not write any regression tests for those).
See http://synopse.info/fossil/info/fc139d58f3

The problem with your pattern is that interface-based services expect user authentication to be activated.
So before opening the database, you need to have some users and groups available.
If you do not have any DB yet, server can't retrieve the user/group to make the authentication.

Method-based services have the posilibility to by-pass the authentication.
See TSQLRestServer.ServiceMethodByPassAuthentication() method.
Perhaps you may use it for opening the database.

For your process, you would need one server per client, right?

Offline

#5 2012-09-11 12:51:02

Bascy
Member
From: The Netherlands
Registered: 2012-06-22
Posts: 108

Re: Unregistering a RestServer with TSQLite3HttpServer

No the intention was to have one server process for multiple clients
I was thinking the overall authentication would be in the model that is serviced by the main TSQLRestServerDB. This server has itrs own model, different fomr the one that are opened with the OpenDatabase() method

Offline

#6 2012-09-11 13:15:04

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,612
Website

Re: Unregistering a RestServer with TSQLite3HttpServer

In such case (nested servers in cascade), it will work as expected.

Offline

#7 2012-09-11 14:59:29

Bascy
Member
From: The Netherlands
Registered: 2012-06-22
Posts: 108

Re: Unregistering a RestServer with TSQLite3HttpServer

Thanks for confirming this ;-)

I do, however run into a problem that i cannot seem to fix :-( but its a little bit difficult to explain ...

I have two executables, Server and TestClient.

The server is started with the following code:

var
  lModel : TLCSServerModel; //Descendant of TSQLModel, nothing new here
  lConnProp: TSQLDBNexusDBConnectionProperties;
begin
  lModel := TLCSServerModel.Create(BASE_MODEL_URI);
  lConnProp := TSQLDBNexusDBConnectionProperties.Create('', '.\ServerDB', '', '');
  VirtualTableExternalRegisterAll(lModel, lConnProp);

  if not lConnProp.DatabaseExists then
    lConnProp.CreateDatabase;

  inherited Create(lModel, 'ServerDB.db3', True);
  CreateMissingTables;

  FServerModel := lModel;
  FServerConnProp := lConnProp;


  FRestServers := TDictionary<RawUTF8,TSQLRestServerDB>.Create;

  FHttpServer := TSQLite3HttpServer.Create(cPort, [Self]);
  ServiceRegister(TLCSServer,[TypeInfo(ILCSServer)],sicShared);

The server has got the following implementation of the ILCSServer interface method Opendatabase:

function TLCSServer.OpenDatabase(aConnectionString: RawUTF8; var URI: RawUTF8; aCreate: boolean = false): Boolean;
var
  lConProp: TSQLDBConnectionProperties;
  lRestServerDB: TSQLRestServerDB;
  lModel: TLCSModel;
  iCreateDB: ICanCreateDatabase;
begin
  Result := False;
  if aConnectionString <> '' then
  begin

    //Determine database type and protocol
    lConProp := CreateConnectionProperties(aConnectionString);

    if Supports(lConProp, ICanCreatedatabase, ICreateDB) then
      if not ICreateDB.DatabaseExists then
        if aCreate then
          ICreateDB.CreateDatabase
        else
          Exit;

    if lConProp <> nil then
    begin
      //Create separate TSQLRestSeverDB object for this database

      lModel := TLCSModel.Create(URI);
      VirtualTableExternalRegisterAll(lModel, lConProp);
      lRestServerDB := TLCSRestDB.Create(lModel, UTF8ToString(URI) + '.db3', True);
      lModel.Owner := lRestServerDB; //Make Restserver destroy model

      lRestServerDB.CreateMissingTables;

      //Register the new restServer with the http server, using a new URI, and add it to list of RestServers
      LCSRestServer.RegisterDatabase(lRestServerDB);

      Result := true;
    end;
  end;
end; {- TLCSServer.OpenDatabase }

Now the testclient gets started separately of course, executing the following test method:

procedure TLCSServerTest.TestOpenDatabaseAccess;
var
  lDBUri: RawUTF8;
  lWB: TSQLWorkbaseObject;
  lClient :  TSQLite3HttpClient;
  lModel: TLCSModel;
const
  cNewFolder = '..\..\UnitTest\NexusDBServerTest12';
begin
  FModel := TLCSModel.Create(BASE_MODEL_URI);
  FClient := TSQLite3HttpClient.Create('localhost','888',FModel);
  CheckTrue(FClient.SetUser('Admin', 'synopse'), 'Authentication failed');
  FClient.ServiceRegister([TypeInfo(ILCSServer)],sicShared);

  FClient.Services[cLCSServerURI].Get(FLcsInterface);
  CheckNotNull(FLcsInterface, 'LCS Interface not retrieved from server');
  lDBUri := 'TestDB12';
  lClient := nil;
  lModel := nil;
  try
    CheckTrue(FLcsInterface.OpenDatabase(cNewFolder, lDBUri, True));
    lModel := TLCSModel.Create(lDBUri);

    lClient := TSQLite3HttpClient.Create('localhost', '888', lModel);
    CheckTrue(lCLient.SetUser('Admin', 'synopse'));

    lWB := TSQLWorkbaseObject.Create;
    try
      lWB.ID := 0;
      lWB.Name := 'Blabla';
      Check( lClient.Add(lWB, True) > 0, ' Adding workbaseobject failed.');
    finally
      lWB.Free;
    end;

  finally
    Delete_Tree(cNewFolder);
    lClient.Free;
    lModel.Free;
  end;
end;

If i execute this client ... it always fails with an access violation in TSQLRestServerStaticExternal.ExecuteDirect((line 1022) when it tries to execute the statement:

  Query := fProperties.NewThreadSafeStatementPrepared(SQLFormat,Args,ExpectResults);

If you inspect the value of fProperties it says Inaccesible Value

This is the callstack after the access violation:

SynDB.TSQLDBConnectionProperties.NewThreadSafeStatementPrepared('select max(ID) from %',(...),True)
SQLite3DB.TSQLRestServerStaticExternal.ExecuteDirect('select max(ID) from %',(...),(...),True)
SQLite3DB.TSQLRestServerStaticExternal.EngineLockedNextID
SQLite3DB.TSQLRestServerStaticExternal.ExecuteFromJSON('{"Name":"Blabla","UserID":""}',0)
SQLite3DB.TSQLRestServerStaticExternal.EngineAdd(TSQLWorkbaseObject,'{"Name":"Blabla","UserID":""}')
SQLite3Commons.TSQLRestServer.URI('TestDB12/WorkbaseObject?session_signature=0000004C000688DA500964C9','POST','{"Name":"Blabla","UserID":""}','','',$4B8FBE7)
SQLite3HttpServer.TSQLite3HttpServer.Request('/TestDB12/WorkbaseObject?session_signature=0000004C000688DA500964C9','POST','Cache-Control: no-cache'#$D#$A'Connection: Keep-Alive'#$D#$A'Pragma: no-cache'#$D#$A'Content-Length: 29'#$D#$A'Content-Type: application/json; charset=UTF-8'#$D#$A'Accept: */*'#$D#$A'Accept-Encoding: synlz'#$D#$A'Host: localhost:888'#$D#$A'User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows; FREE)'#$D#$A'RemoteIP: ::1'#$D#$A,'{"Name":"Blabla","UserID":""}','application/json; charset=UTF-8','','','')
SynCrtSock.THttpServerGeneric.Request('/TestDB12/WorkbaseObject?session_signature=0000004C000688DA500964C9','POST','Cache-Control: no-cache'#$D#$A'Connection: Keep-Alive'#$D#$A'Pragma: no-cache'#$D#$A'Content-Length: 29'#$D#$A'Content-Type: application/json; charset=UTF-8'#$D#$A'Accept: */*'#$D#$A'Accept-Encoding: synlz'#$D#$A'Host: localhost:888'#$D#$A'User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows; FREE)'#$D#$A'RemoteIP: ::1'#$D#$A,'{"Name":"Blabla","UserID":""}','application/json; charset=UTF-8','','','')
SynCrtSock.THttpApiServer.Execute

Do you have any idea what i am doing wrong here ...???

Offline

#8 2012-09-11 16:57:32

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,612
Website

Re: Unregistering a RestServer with TSQLite3HttpServer

If you try with an external SQLite3 properties?

How are the models created? Perhaps two tables are marked as external with diverse db and there is an issue...

Offline

#9 2012-09-12 06:06:54

Bascy
Member
From: The Netherlands
Registered: 2012-06-22
Posts: 108

Re: Unregistering a RestServer with TSQLite3HttpServer

ab wrote:

If you try with an external SQLite3 properties?
How are the models created? Perhaps two tables are marked as external with diverse db and there is an issue...

What are external SQLite3 properties?

Offline

#10 2012-09-12 06:12:19

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,612
Website

Re: Unregistering a RestServer with TSQLite3HttpServer

I meant, an external table defined by a TSQLDBSQLite3ConnectionProperties instance (unit SynDBSQLite3).

But perhaps your issue comes from the fact that TSQLRecordProperties is used to store ExternalTableName and ExternalDatabase, common for all server instances.
I suppose it may be the cause of your problem.
I'll move those two fields from the TSQLRecordProperties, and add them to the TSQLModel, which is specific to a TSQLRest instance.

Huge code refactoring may be needed here.
In any application sharing TSQLRecord classes among several models (like in your case), current implementation sounds too optimistic.
A domain-driven designed project may use several layers (at least one for application, either for the domain), with shared TSQLRecord instances.
At least, TSQLAuthGroup and TSQLAuthUser are always shared among servers or clients (if you use authentication), and their ORM persistence settings should be preserved among the corresponding TSQLRest instance, and its associated TSQLModel.

I would expect TSQLRecordProperties to contain only raw RTTI information, then a new TSQLModel.Props[] array containing all associated ORM information for a every TSQLRecord class, in the context of this ORM model.
That is, a new TSQLModelRecordProperties class will be defined and used in SQLite3Commons.pas.
Since the same TSQLRecord can be defined in several models, with diverse implementation patterns (e.g. internal in one, external in another), this TSQLModelRecordProperties class will be used to store all model-specific settings, like SQL pre-generated patterns, external DB properties, or even filters/validators.

Minor end-user code changes should be mandatory, to specify the associated TSQLModel instance to be used.
But I think that most of this change won't break end-user code.
I'm working on it.

Offline

#11 2012-09-14 06:32:20

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,612
Website

Re: Unregistering a RestServer with TSQLite3HttpServer

I've done my home work.

Now http://synopse.info/forum/viewtopic.php?id=840 should fix this issue.

You are now able to share TSQLRecord definitions among several TSQLModel: external DB properties and pre-computed SQL statements will be linked to each model.

Sounds like no change in user code is expected, finally (unless you access directly to the properties which moved from TSQLRecordProperties to TSQLModelRecordProperties - which I hope you are not).
I just changed the internal implementation of the ORM core.

Offline

#12 2012-09-14 06:44:49

Bascy
Member
From: The Netherlands
Registered: 2012-06-22
Posts: 108

Re: Unregistering a RestServer with TSQLite3HttpServer

Great!! and again on such short notice!

I noticed one small breaking change:

  Model.TableProps[0].SQLTableName

needs to be changed into

  Model.TableProps[0].ExternalTableName

Offline

#13 2012-09-14 07:13:05

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,612
Website

Re: Unregistering a RestServer with TSQLite3HttpServer

It should better changed into

 Model.TableProps[0].Props.SQLTableName

If you want the same value: the TableName at SQLite3 / ORM level.

You can access the TSQLRecordProperties value from TSQLModelRecordProperties.Props.

ExternalTableName is at external database level, and may have been customized.

Offline

Board footer

Powered by FluxBB