You are not logged in.
I can use TSQLite3HttpServer.AddServer to add a newly created TRestServer to a HttpServer, but how do i remove the RestServer from TSQLite3HttpServer?
Offline
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
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
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
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
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
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
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
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
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
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