#1 2013-04-14 15:08:43

LoPiTaL
Member
Registered: 2013-01-04
Posts: 30

How to use sicPerUser, sicPerSession, sicPerGroup in the client?

Hi everyone!
I have been playing a bit with interface-based services using a Delphi client. But I was always doing tests with sicShared (the default creation mode).
Now I am trying to stop "playing", and start to do actual work, so now I need an interface-based service with sicPerUser mode instead.

How can I work with this from the Delphi client? I am trying to do the same as with sicShared, but the result is always False:

  //With sicShared works fine, with sicPerUser returns always false
  if FClient.Services.GUID(IMyINterfaceCreatedWithSicPerUser).Get(LMyInterface) then begin
    //Work with interface
  end;

I have seen that the "Get" procedure returns True only if the creation mode is sicShared, sicClientDriven or sicSingle. With any other creation mode, it returns False. So, which is the correct way to work with this creation modes?

Best regards,
LoPiTaL

Offline

#2 2013-04-15 07:02:37

h.hasenack
Member
From: Nijmegen, Netherlands
Registered: 2012-08-01
Posts: 173
Website

Re: How to use sicPerUser, sicPerSession, sicPerGroup in the client?

Did you change the sharing type on both the server and client side? Failing to do so causes an interface contract error!

I am a little confused too about the types of sharing. Maybe Arnaud can say which one I need (my guess is perUserGroup)

1) 1st client userA connects, starts a simulation interface
2) 2nd client userB connects, starts a simulation interface
3) Connection if UserA Is dropped unexpectedly (powerloss, cable out, clean logout, whatever)
4) UserA reconnects.

Upon step 2 U want user B to use the same simulation object instance as UserA
Upon step4 I want user A to reconnect to the same simulation object instance as the one that was created in step 1

Last edited by h.hasenack (2013-04-15 07:05:29)

Offline

#3 2013-04-15 07:51:38

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

Re: How to use sicPerUser, sicPerSession, sicPerGroup in the client?

@Hans

If I understand well, perhaps UserA and UserB may be part of the same group, then the instance be sicPerGroup, so that in step 2 user B will use the same simulation object as UserA.
At reconnection, it will use the same.

Or if ALL users have to share the same simulation interface, using sicShared may be the good option.

@ LoPiTal

I will take a look at the client side implementation.
In all cases, BOTH client and server side MUST share the same setting, as Hans stated. Otherwise there will be a contract mismatch.
So if you have any problem, please share some small .dpr code able to reproduce the issue.

Please take a look at the SAD pdf, about the possible instance life time.
There is also some tables in the pdf, which shows the possible use cases.
Feel free to give feedback about the current documentation - source is in the repository, in the .pro file, so you can even submit patches!
smile

Offline

#4 2013-04-15 12:51:01

LoPiTaL
Member
Registered: 2013-01-04
Posts: 30

Re: How to use sicPerUser, sicPerSession, sicPerGroup in the client?

Hi all!
I have both client and server in sicPerUser. The error is not a contract mismatch.

The problem is when calling the Get function from the service factory, which always returns False.

I copy and paste here the code of that function, in the "mORMot.pas" file, so I can show you:

function TServiceFactoryClient.Get(out Obj): Boolean;
var O: TInterfacedObjectFake;
begin
  result := false;
  if Self=nil then
    exit;
  case fInstanceCreation of
  sicShared:
    O := TInterfacedObjectFake(fSharedInstance);
  sicSingle, sicClientDriven:
    O := TInterfacedObjectFake(CreateFakeInstance);
  else exit;
  end;
  pointer(Obj) := @O.fVTable;
  O._AddRef;
  result := true;
end;

In it, it is clear that it will return an interface only when the instance creation is of type sicShared, sicSingle or sicClientDriven. Any other option will go to the "else" part of the case and return False.

My code to get the interface is as follows, as I stated in the first post:

 //With sicShared works fine, with sicPerUser returns always false
  if FClient.Services.GUID(IMyINterfaceCreatedWithSicPerUser).Get(LMyInterface) then begin
    //Work with interface
  end;

Note that if I change to sicShared (of course, in both client and server) it works fine, so the register of the interface is correctly done both, in the server and in the client.

So I am guessing that I should retrieve the interface to work with with another method, but it is not stated in the documentation nor I have found it around the code.

Best regards,
LoPiTaL

Last edited by LoPiTaL (2013-04-15 12:53:55)

Offline

#5 2013-04-15 14:42:16

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

Re: How to use sicPerUser, sicPerSession, sicPerGroup in the client?

Sounds weird.

We need to fix it, and add some associated regression tests.

Could you create a ticket?

Offline

#6 2013-04-15 16:32:52

LoPiTaL
Member
Registered: 2013-01-04
Posts: 30

Re: How to use sicPerUser, sicPerSession, sicPerGroup in the client?

Ok, ticket created:

http://synopse.info/fossil/tktview?name=3fafb53be4

Best regards,
LoPiTaL

Offline

#7 2013-04-15 17:55:17

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

Re: How to use sicPerUser, sicPerSession, sicPerGroup in the client?

Should be fixed now.

Including several other fixes and regression tests.

Thanks for the report!

Offline

#8 2013-04-16 17:28:14

LoPiTaL
Member
Registered: 2013-01-04
Posts: 30

Re: How to use sicPerUser, sicPerSession, sicPerGroup in the client?

Thank you! It works fine now.

Another question (I don't open a new thread since it is related).

When creating the perUser instance, the ServiceContext is still not initialized, so it is not possible to retrieve the context information at creation time.
This can be solved by initializing SErviceContext before creating the instance in the "TServiceFactoryServer.ExecuteMethod", moving the following lines:

    ThreadServer := @ServiceContext;
    //WR := TJSONSerializer.CreateOwnedStream;  //This one is not related, and should be kept where it is
    try
      with ThreadServer^ do begin
        Factory := self;
        Session := @Ctxt;
      end; // RunningThread is already set at thread initialization

to the beginning of the method, and the following to the end:

    finally
      with ThreadServer^ do begin
        Factory := nil;
        Session := nil;
      end;
      //WR.Free; //This one is not related, and should be kept where it is
    end;

This can be done? Or there is any other consideration?

If so, then it may be usefull to have the context info at instance creation time, i.e. for initializing data for caching, and using it without the need of checking in all the interface methods if the data is nil or not.

Best regards,
LoPiTaL

Last edited by LoPiTaL (2013-04-16 17:38:33)

Offline

#9 2013-04-16 18:22:47

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

Re: How to use sicPerUser, sicPerSession, sicPerGroup in the client?

It won't work with option to execute in main thread, for instance.

So you should better use an initalization method and put nothing in the constructor or AfterCreate.
See sample 16.

Offline

Board footer

Powered by FluxBB