OpenClient: 2 AppObjects sharing the same connection

Posted by guilmori on 27-Apr-2010 14:25

I have a situation where I instantiate 2 different AppObjects. The problem is that it create 2 different connections to the appserver broker. Moreover, if each AppOpbject create a persistent procedure on the appserver, 2 agents are locked. I would like the 2 AppObjects to share the same connection and lock only one agent. Is it possible ?

I'm sending the same Progress.Open4GL.Proxy.Connection instance to both AppObject constructor, but it seems the Connection object is not *a* connection but more a "settings container" used by the AppObject to create a new connection...

I'm using stateless appserver.

All Replies

Posted by Admin on 27-Apr-2010 14:31

I'm sending the same Progress.Open4GL.Proxy.Connection instance to both AppObject constructor, but it seems the Connection object is not a connection but more a "settings container" used by the AppObject to create a new connection...

I'm using stateless appserver.

Exactly. The Connection Object is more a container for settings. The AppObject is the actual connection.

But anyway, I'd rethink your strategy. Launching persisten procedures from the client is a huge performance killer. And a scalability problem.

I'd rather call onto non persistent procedures that instantiate the PP on the AppServer (proxy procedures).

Posted by bsgruenba on 27-Apr-2010 14:45

It's a bit more complicated than that. The Connection object is really a connection pool descriptor. It describes a connection to an AppServer.

When an AppObject makes a call, it first establishes a connection based on the Connection object. If a connection has not previously been made, the AppObject instantiates a connection pool based upon the Connection object. The size of the connection pool is something you can set. Once the connection pool has been instantiated, it is associated with the Connection object so that the next time you need a connection, the Connection object has a reference to the connection pool.

Your case is a real problem. You are instantiating a persistent procedure which means that the client establishes and holds a connection to the AppServer until you release the persistent procedure. That means that each AppObject will hold a connection until the persistent procedure is released.

Rule #1 with AppServer - You should never have persistent procedures span the AppServer boundary. Always make non-persistent calls to remote .p files.

Posted by guilmori on 27-Apr-2010 15:03

mikefe wrote

Exactly. The Connection Object is more a container for settings. The AppObject is the actual connection.

But anyway, I'd rethink your strategy. Launching persisten procedures from the client is a huge performance killer. And a scalability problem.

I'd rather call onto non persistent procedures that instantiate the PP on the AppServer (proxy procedures).

Thanks Mike.

I've heard about remote persitent procedure being evil many time, but we've used this for quite some time with great success.

Example usage:

     using (ServerLogic logic = new ServerLogic())
     {

          dsEntityA1 = logic.FacadeA.GetEntity1();

          dsEntityA2 = logic.FacadeA.GetEntity2();

          dsEntityB1 = logic.FacadeB.GetEntity1();
          dsMessage = logic.FacadeCommon.GetMessages();               

          dsSettings = logic.FacadeCommon.GetSettings();               
     }

When we create an instance of ServerLogic, an appserver connection is made, and we run a .p persitently on the server to initialize a *server session* (agent is locked).

Then, on first access,  each facade is also run persistently on the server.

The goal of this approach, versus what you suggest, is that it is the client who controls what it needs to call on the server, instead of having dummy procedures on server that exist only to group many calls together.

Grouping many calls inside the same "using ServerLogic" is very performant, since we have a dedicated agent and a direct handle to the facade persistent proc.

Another reason we do this is that we prefer our facades to be library of internal procedures, instead of having hundreds of facades .p ie: one method = one .p.

Posted by guilmori on 28-Apr-2010 07:33

Ok let's assume rule #1 is broken for now :-) (i don't think it is related to my next problem)

What I'm trying to achieve is to extract common facades access code (facade wrapper) from our .net projects into a re-usable standalone project. My first try was to split the AppObject in 2, but it's not gonna work since the simultaneous appserver connections.

So next try is to have only one AppObject, which is used by the "specific" project, and the common project will use the same AppObject, but through the DynamicAPI since it doesn't have access to the proxy .dll. One inconvenient of this approach is the each "specific" project must add common facades .p into the proxygen to generate their AppObject, but that is not so bad.

However, I have a problem with Progress methods that output a table-handle or dataset-handle.

Here is a sample .net code that gives me this error:


  Local static schema is required for result set parameters. (7214)

...
m_Facade = AppObject.CreatePO("facade/CommonFacade.p", new ParameterSet(0));
...



public DataTable FindMessageList(string viList)
{
   var parms = new ParameterSet(2);
   parms.setStringParameter(1, viList, ParameterSet.INPUT);
   parms.setDynDataTableParameter(2, null, ParameterSet.OUTPUT);
   m_Facade.runProcedure("FindMessageByNoList", parms); // error is raised here
   DataTable dt = parms.getOutputParameter(2) as DataTable;
          
   return dt;
}


PROCEDURE FindMessageByNoList:
   DEF INPUT PARAM viList AS CHAR NO-UNDO.
   DEF OUTPUT PARAM TABLE-HANDLE hoTT.
   ...
END PROCEDURE.

I must be doing something wrong since it would by quite ridiculous that a dynamic table parameter would need to by statically defined...

Posted by guilmori on 28-Apr-2010 08:21

I manage to receive a table-handle using the OpenAPI instead.

var pa = new ParamArray(2);
pa.AddCharacter(0, viList, ParamArrayMode.INPUT);
pa.AddTableHandle(1, null, ParamArrayMode.OUTPUT, null);
(m_Facade as OpenProcObject).RunProc("FindMessageByNoList", pa);
var dt = pa.GetOutputParameter(1) as DataTable;

However, since an AppObject type is not castable to an OpenAppObject type, I had to create a new OpenAppObject to gain access to the OpenAPI, but this brings me back to my initial problem since it create another appserver connection...

Nonetheless, this demostrates that it is possible to receive a table-handle using the OpenAPI.

So, is there a bug with the DynamicAPI ?

Posted by guilmori on 28-Apr-2010 12:11

Bruce, where do you get all this information regarding OpenClient ?

PSC documentation is miserable...

Posted by Brian K. Maher on 11-Jun-2010 13:08

Hi Guillaume,

My name is Brian Maher and I am the Technical Support Engineer you have been working with behind the scenes on this issue (the call was opened by David Gilbert).

I wanted to take a moment and clear up some misinformation you have gotten on this thread.  Specifically, the Connection object is just a "settings" object if you are connecting to the AppServer in either State-Reset, State-Aware or Stateless operating modes.  Bruce's comments only apply when you are connecting to the AppServer in a State-Free (also known as Session Free) operating mode.  Given that you are connecting in a Stateless operating mode (as per your posts) you can safely ignore Bruce's otherwise accurate comment.

Now on to the next part of the issue....

I just got an update from David Gilbert stating that you are using the Dynamic API instead of either the OpenAPI or the proxies generated by ProxyGen.

Is this correct?

If so, there is (unfortunately) a very limited amount of help we can provide because the direct use of the Dynamic API is not supported (that is why it is not documented).  We only support the use of the OpenAPI and the proxies generated by ProxyGen.  I am asking this because based on your posts I get the impression that you may have confused the terms and what you are calling the "Dynamic API" is really the generated proxies from ProxyGen.

Can you please confirm which method you are actually using so I can try to provide some kind of a resolution?

Sincerely,

Brian K. Maher

Principal Technical Support Engineer

Posted by bsgruenba on 11-Jun-2010 13:45

Actually, Guillame, the documentation on this is not too bad at all. If you go to the URL http://communities.progress.com/pcom/docs/DOC-103525, you will find a section entitled "OpenClient" that contains several publications, including "OpenClient Introduction and Programming" and "Java Open Clients".

Section 11 of the latter document includes a discussion on the OpenAPI that can be used to directly access the AppServer. This section was renamed in version 10.2 and it is what I have referred to as Dynamic OpenClient in the past. That's where I got the information from.

Posted by Brian K. Maher on 11-Jun-2010 13:47

Hi Bruce,

Actually the OpenAPI is not the same thing as the Dynamic API.  The OpenAPI is abstracted from the Dynamic API (i.e. uses the Dynamic API under the covers) to provide an easy to use "prebuilt" means to call programs on the AppServer without having to generate a Proxy.  OpenAPI <> Dynamic API.  Dynamic API is undocumented and unsupported for customer use.

Sincerely, Brian Maher

Posted by guilmori on 14-Jun-2010 10:25

Hello Brian,

It is indeed the Dynamic API I'm trying to use, not the OpenAPI.

I did send you a reply with more details about this problem.

I'll paste it here also.

First I'll try to explain the current situation.

On the .Net side, let's say we have 2 solutions, with the following project dll:
ApplicationA
  - ApplicationA.Visual.dll
  - ApplicationA.DataAccess.dll
ApplicationB
  - ApplicationB.Visual.dll
  - ApplicationB.DataAccess.dll

We have these ProxyGenenrated .dlls, which contains the following persistent  .p:
ApplicationAProxy.dll
  - ApplicationALibrary.p
  - CommonLibrary.p
ApplicationBProxy.dll
  - ApplicationBLibrary.p
  - CommonLibrary.p

CommonLibrary.p is Progress code that can be used by any applications.

Then we have the Proxy.dll referenced into the DataAccess.dll of its corresponding application:
ApplicationA
  - ApplicationA.Visual.dll
  - ApplicationA.DataAccess.dll
     -> reference to ApplicationAProxy.dll
ApplicationB
  - ApplicationB.Visual.dll
  - ApplicationB.DataAccess.dll
     -> reference to ApplicationBProxy.dll

The DataAccess.dll projects contains a wrapper methods for each procedures defined in the referenced Proxy.dll. This is to encapsulate the ProxyGen access, so all what Visual.dll care about is the DataAccess.dll interface.

Moreover, this wrapper methods may also contains some code to ease the communication between C# and Progress ex: SetBImageFlag, set default string value to string.empty, ...

Here comes the problem, we need to duplicate CommonLibrary.p wrapper methods into each different .Net Applications (many more than 2 in our real situation).
So what I'm trying to do to solve this issue is to create a stand alone .Net Project ie: Common.DataAccess.dll, which will contains all the wrapper methods to the CommonLibrary.p.

ApplicationA
  - ApplicationA.Visual.dll
  - ApplicationA.DataAccess.dll
     -> reference to ApplicationAProxy.dll
     -> reference to Common.DataAccess.dll
ApplicationB
  - ApplicationB.Visual.dll
  - ApplicationB.DataAccess.dll
     -> reference to ApplicationBProxy.dll
     -> reference to Common.DataAccess.dll

Now the problem is that Common.DataAccess.dll has no reference to the Proxy.dll,  since the Proxy.dll is different for each application.
So how can it make the call to the procedure on the appserver ?
My idea was that Common.DataAccess.dll could dynamically make call into the existing "ProxyGened" AppObject instance without knowing about the proxy .dll, hence the DynamicAPI usage. But then I get stuck with the problem of OUTPUT table/dataset handle.

I must say that my first attempt to solve this issue was to create another Proxy.dll as the following:
ApplicationAProxy.dll
  - ApplicationALibrary.p
ApplicationBProxy.dll
  - ApplicationBLibrary.p
CommonProxy.dll
  - CommonLibrary.p

And then add a reference to CommonProxy.dll into Common.DataAccess.dll, ex:
ApplicationA
  - ApplicationA.Visual.dll
  - ApplicationA.DataAccess.dll
     -> reference to ApplicationAProxy.dll
     -> reference to Common.DataAccess.dll
        -> reference to CommonProxy.dll

But when for example ApplicationA.Visual.dll make a call to both an  ApplicationALibrary.p and CommonLibrary.p procedure (through the ApplicationA.DataAccess.dll interface), this created 2 different connections and locked 2 different agents, which isn't acceptable in our case.

Posted by Brian K. Maher on 14-Jun-2010 11:14

Hi Guillaume,

If I am understanding you correctly what you are really trying to do is be able to have different applications call programs in different proxies all using a single connection to the AppServer.

Is that correct?

Assuming that is correct, why don't you simply create a single proxy containing all of the programs for each application where each application's specific programs are defined as part of a SubAppObject?  This would allow easy access to any application's programs from a single connection without having the overhead of having all of the class objects of the resulting DLL being loaded into memory until you actually need them (by instantiating the SubAppObject).  Using a SubAppObject would also allow you to have the same program names (.p) in different locations on the AppServer without a naming conflict.  For example, assume a program called bob.r that exists in an AccountsPayable and in an AccountsReceivable directory on the AppServer where the directory that both of these directories are located inside of is part of the AppServers PROPATH.  You could create a SubAppObject called AP and add the bob.r located in the AccountsPayable directory into it.  You could then do the same thing for the AccountsReceivable directory.  When you invoke the bob method (in whichever SubAppObject) it would then issue a 4GL RUN statement like RUN AccountsPayable\bob.r or RUN AccountsReceivable\bob.r and the correct program would be run.

Do you think that would work for you?

Also, the first thing you need to do is to STOP using the Dynamic API.  This is not supported and you will not be able to obtain any help with it.  You need to look for supported ways to resolve things.

Sincerely, Brian Maher

Posted by guilmori on 15-Jun-2010 08:43

Thanks Brian, the idea of one big proxy with many SubAppObjects could work.

I'll have to investigate more.

Some early concerns about this approach:

  - We currently deploy some proxygen .dll to external partner applications, for them to access our logic/data. Having one global proxy means they will see all of our applications facade, instead of only see what they need to see. Not a big problem, but i did prefer having my proxies logically divided.

  - As we discussed, global .p are added into the AppObject, and each specific .p (facades) are added into the SubAppObject. However, way may have some case were a same physically located facade.p must be added into more than one SubAppObject, without being made global in the AppObject. This cannot be accomplish since the generation fails stating duplicate ProcObject name.

The other alternative would be to ditch the ProxyGen, and use exclusively the OpenAPI. This require more coding in my .Net DataAccess projects, but I would be able to share the same OpenAppObject instance without adding dependencies.

I'm currently pulled away from this issue, so it will take a couple of weeks before I can complete the solution.

Posted by Brian K. Maher on 15-Jun-2010 08:59

Hi Guillaume,

>> We currently deploy some proxygen .dll to external partner  applications, for them to access our logic/data. Having one global proxy  means they will see all of our

>> applications facade, instead of only see  what they need to see. Not a big problem, but i did prefer having my  proxies logically divided.

There is no real problem with deploying separate proxies to those external customers or even creating your own .NET DLL that exposes only the things you want by encapsulating either the generated proxy (if you go that way) or bt using the OpenAPI.  This is really a design decision more than anything.

>> As we discussed, global .p are added into the AppObject, and each  specific .p (facades) are added into the SubAppObject. However, way may  have some case were

>> a same physically located facade.p must be added  into more than one SubAppObject, without being made global in the  AppObject. This cannot be accomplish since

>> the generation fails stating  duplicate ProcObject name.

This makes no sense.  If the "facade" is the same physical .r and it is always in the same physical location on the AppServer then it cannot be logically "different" therefore I don't see any reason why this program "needs" to be in multiple SubAppObjects.  It should be in the AppObject.  Can you please explain your reasoning as to why you believe you need to have this in multiple SubAppObjects?  I think you may be misunderstanding something about how Open Client or the AppServer works.

>> The other alternative would be to ditch the ProxyGen, and use  exclusively the OpenAPI. This require more coding in my .Net DataAccess  projects, but I would be able to

>> share the same OpenAppObject instance  without adding dependencies.

Yes, this was my initial response to the person who opened the support call with us.

There are multiple acceptable ways to resolve this design issue.  You simply need to sit down and determine what your true requirements are and then based on those requirements take another look at the options I have given you for doing things and come to a decision.

Sincerely, Brian Maher

Posted by guilmori on 15-Jun-2010 09:41

maher wrote:

>> As we discussed, global .p are added into the AppObject, and each  specific .p (facades) are added into the SubAppObject. However, way may  have some case were

>> a same physically located facade.p must be added  into more than one SubAppObject, without being made global in the  AppObject. This cannot be accomplish since

>> the generation fails stating  duplicate ProcObject name.

This makes no sense.  If the "facade" is the same physical .r and it is always in the same physical location on the AppServer then it cannot be logically "different" therefore I don't see any reason why this program "needs" to be in multiple SubAppObjects.  It should be in the AppObject.  Can you please explain your reasoning as to why you believe you need to have this in multiple SubAppObjects?  I think you may be misunderstanding something about how Open Client or the AppServer works.

Let's say I have a SubAppObject, App1, that contains facadeA.p and facadeB.p. At that time, I really think facadeA.p and facadeB.p are specific to App1, so no need to put them in global AppObject.
Then later down the road, I create a new SubAppObject, App2, that need facadeC.p and, surprise, also facadeA.p. I cannot just add facadeA.p to App2.
I need to remove facadeA.p from SubAppObject App1, and place it instead in the global AppObject. This require code change in App1 that must now access facadeA.p through the AppObject instead of its SubAppObject. Not good.

Moreover it may be only App1 and App2 that need facadeA.p, App3-App99 doesn't need it, so it adds unnecessary overhead for them to have facadeA.p in the global AppObject.

Is my understanding correct ?

Posted by Brian K. Maher on 15-Jun-2010 12:08

Hi Guillaume,

Given what you stated, no sort of statically defined proxy will really work for you because you have no idea what ABL programs your application(s) will or will not need to call on the AppServer.

With those sort of implementation limitations being imposed then realistically you need to just convert everything over to the Open API and forget about using ProxyGen completely.

Sincerely, Brian Maher

Posted by guilmori on 15-Jun-2010 12:30

maher wrote:

Given what you stated, no sort of statically defined proxy will really work for you because you have no idea what ABL programs your application(s) will or will not need to call on the AppServer.

I'm not sure what do you mean by that. I do know what ABL programs each of my application will need to work with.

ex:

App1

  - FacadeCommon.p

  - FacadeA.p

  - FacadeB.p

App2

  - FacadeCommon.p

  - FacadeA.p

  - FacadeC.p

App3

  - FacadeCommon.p

  - FacadeD.p

How would you build your proxy/ies for this example ?

Posted by Brian K. Maher on 15-Jun-2010 13:03

Hi Guillaume,

When I talked to you the other day you mentioned that putting the common routines in the AppObject instead of any SubAppObject would work just fine for you.

Later on you mentioned that this may not work for you because you ship the proxies to external customers and you didn't like the fact that they would see things that did not apply to them.

Further on from that you mentioned that splitting each "application" into its own SubAppObject would work great except that you cannot put the same "program" in multiple SubAppObjects, that you didn't like the idea of putting "common" routines into the AppObject only and that some programs (specific to a given "application") will in fact need to be accessed by different "applications".

What I have been trying to get across to you is that YOU need to look at your ENTIRE set of needs (across all "applications" and all external customers) and come up with a set of requirements (i.e. stuff sent to external customers cannot expose things they should not have access to, etc, etc, etc).  It is not possible to come up with a resolution for you when I give you a possible answer only to have the requirements change out from underneath me.

Come up with a set of requirements, post those requirements and then I can analyze them and give you a way to proceed.

Sincerely, Brian Maher

Posted by guilmori on 15-Jun-2010 14:56

You suggest new alternatives that brings new issues, that's it.
When we talked about the SubAppObject, I had very little time to take a look at it. And I said it could work ( != would) but I would have to investigate more.
I cannot guess what new limitation SubAppObject/OpenAPI brings before trying.

Sorry if it is not your typical question-->answer-->case closed support scenario.

Posted by Brian K. Maher on 15-Jun-2010 15:06

Hi Guillaume,

OK.  Let me try again as I am obviously not making myself understood. 

Right now you are focused on a technical answer to your problem.

However, what I am trying to have you do is take a step back from the technical perspective and instead focus on what you want to do from a business perspective (leaving all of the technical stuff out of the picture for now).  Focusing on the technical is obviously getting us nowhere at this point because (from my perspective) the business problem has not been fully understood (please note that it is possible that you fully understand the business needs, however, it is very clear to me that I do not understand them or I would have been able to provide you a usable answer by now).

Are you willing to take that step back away from the technical perspective and instead focus on providing me with a business perspective?  If you can do this then I have no doubt that I can provide you with a way to accomplish everything you want to do.

Please let me know what you decide.

Sincerely, Brian Maher

Posted by guilmori on 15-Jun-2010 16:55

Thanks for your patience Brian, and sorry if I do not make my requirement clear enough.

Before trying to re-formulate my requirement from a business perspective as you say, I just wish to have some more time to explore the alternative where I would be using exclusively the OpenAPI. I want to verify if this is solving my issue, without adding too much code change and extra work required by the dynamic nature of the API.

I'll keep you updated.

Posted by Brian K. Maher on 16-Jun-2010 12:50

Hi Guillaume,

No problem.  Take all the time you need.  I just want to ensure that at the end of all of this I get you an answer which fully solves your problem.

Cheers, Brian

This thread is closed