Johan Bijker (Customer) asked a question.

Shared variables in PASOE

We're busy migrating a web application from classic webspeed in 11.7 to PASOE in 12.2.

 

One of the issues we're facing is with shared variables. We have a startup procedure that creates these shared variables, and because this startup procedure is run from web-disp.p in classic Webspeed, every request that comes in from web-disp.p will be in the right context and will have access to those shared variables.

 

In PASOE things are a bit different. The startup procedure still creates these new shared variables, but now if a request comes in you have to create a new set of shared variables from the activate procedure, otherwise the code will fail to run. However, these shared variables are different from the shared variables created by the startup procedure.

 

Luckily it looks like it's possible to define GLOBAL SHARED variables, and if a request comes it will latch on the the ones already started in the startup procedure.

 

However - there's still an issue if you're using a shared buffer. For some reason you can't define a buffer as a GLOBAL SHARED buffer, so there's just no way for this buffer to latch on to the buffer already created in the startup procedure.

 

What we managed to do so far is to change web-handler.p to run a procedure in a handle created by the startup procedure, which in turn does a RUN run-web-object in web-utilities-hdl. The web request will now have access to the same shared variables as created in the startup procedure, including the shared buffer. The problem is this only works for web, but for any other type (rest / soap / appserver) we will have the same issue.

 

Does anyone have some other ideas we can try to make it work across all types of requests?


  • Peter Judge (Progress Software)

    Ideally you'd want to move away from shared variables, but they do exist in reality, and changing application code may take some time.

     

    As @Johan Bijker (Customer)​  notes, using SHARED (non-GLOBAL) variables in PASOE has a couple of challenges, all related to the fact that in order to access the values, the code must be in the call stack of the code that defined them as NEW. In Classic WebSpeed, all ABL code is run the web-disp.p call stack, and Life Is Good. In PASOE, though, there are a couple-three stacks: the session startup procedure, the activate procedure, and the CompatibilityHandler's run of web-handler (which effectively replaces web-disp.p).

     

    In the PASOE webhandler model there is no simple equivalent: the webhandler (class) is run and it runs the webspeed code via our "web-handler.p". Ideally applications could define and set the values in a webhandler that overrides the CompatibilityHandler, but since you cannot set shared variables from classes (because you can't define them in a class), they can't do this.

     

    Some have worked around this by using global shared variables, set in the session startup. Using globals may have other side effects.

     

    I've found another workaround which is to "extend" the shipped web-handler.p, in the manner below. Variables can be defined and set once, and since web-handler.p is included and is part of the callstack, which runs the classic webspeed code, shared variables are available to the application code.

    1. // customer-web-handler.p
    2. define new shared variable cHello as character no-undo.
    3.  
    4. assign cHello = "howdy parner @ " + iso-date(now).
    5. message "Ran webstart: " cHello.
    6.  
    7. {web/objects/web-handler.p}

     

    The next step is to replace the use of the CompatibilityHandler with a class that does

    1. class CustomerCompatHandler inherits OpenEdge.Web.CompatibilityHandler :
    2. method override protected handle StartProcedure():
    3. define variable procHdl as handle no-undo.
    4. run customer-web-handler.p persistent set procHdl.
    5. return procHdl.
    6. end method.
    7. end class.
    8.  
    9.  

    Now the application's shared variables are available "downstream".

     

    Note that you should test this thoroughly, if you decide to use it.

     

    One issue with this is that if the CompatibilityHandler is not run as a singleton in a session (such as when the srvDebug property is enabled, the setup code will run on each request.

     

     

     

     

     

    Selected as Best
  • While we do not use shared variables, we do have a set of super procedures that are initiated by the startup procedure - this is available for any subsequent call by any client.

  • Torben Christensen (IT Universitetet i København)

    We have implemented a solution where we use a class with static properties to share data between multiple classes / procedures in the same session.

    Across multiple requests (sessions) we serialize - deserialize these properties

  • Thomas Mercer-Hursh (Computing Integrity Inc.)

    Stefan and Torben have given you three ideas ... but let me stress that, regardless of what you can make work, you should be doing what you can to get away from shared variables ... and *especially* global shared variables. They are a horrid violation of encapsulation and an invitation of horrid debugging problems.

    • George Potemkin (Progress Technologies)

      Global shared variables are the only data that will survive if a session is disconnected from a database. They can be used to handle the disconnect. But, of course, it's a very special requirement  for the sessions

  • Peter Judge (Progress Software)

    Ideally you'd want to move away from shared variables, but they do exist in reality, and changing application code may take some time.

     

    As @Johan Bijker (Customer)​  notes, using SHARED (non-GLOBAL) variables in PASOE has a couple of challenges, all related to the fact that in order to access the values, the code must be in the call stack of the code that defined them as NEW. In Classic WebSpeed, all ABL code is run the web-disp.p call stack, and Life Is Good. In PASOE, though, there are a couple-three stacks: the session startup procedure, the activate procedure, and the CompatibilityHandler's run of web-handler (which effectively replaces web-disp.p).

     

    In the PASOE webhandler model there is no simple equivalent: the webhandler (class) is run and it runs the webspeed code via our "web-handler.p". Ideally applications could define and set the values in a webhandler that overrides the CompatibilityHandler, but since you cannot set shared variables from classes (because you can't define them in a class), they can't do this.

     

    Some have worked around this by using global shared variables, set in the session startup. Using globals may have other side effects.

     

    I've found another workaround which is to "extend" the shipped web-handler.p, in the manner below. Variables can be defined and set once, and since web-handler.p is included and is part of the callstack, which runs the classic webspeed code, shared variables are available to the application code.

    1. // customer-web-handler.p
    2. define new shared variable cHello as character no-undo.
    3.  
    4. assign cHello = "howdy parner @ " + iso-date(now).
    5. message "Ran webstart: " cHello.
    6.  
    7. {web/objects/web-handler.p}

     

    The next step is to replace the use of the CompatibilityHandler with a class that does

    1. class CustomerCompatHandler inherits OpenEdge.Web.CompatibilityHandler :
    2. method override protected handle StartProcedure():
    3. define variable procHdl as handle no-undo.
    4. run customer-web-handler.p persistent set procHdl.
    5. return procHdl.
    6. end method.
    7. end class.
    8.  
    9.  

    Now the application's shared variables are available "downstream".

     

    Note that you should test this thoroughly, if you decide to use it.

     

    One issue with this is that if the CompatibilityHandler is not run as a singleton in a session (such as when the srvDebug property is enabled, the setup code will run on each request.

     

     

     

     

     

    Selected as Best
  • Peter Judge (Progress Software)

    In PASOE the context in the session startup is not 'carried through' to the activate/run procedure.

     

    I' don't think that the activate procedure is ever the top of the run context so I don't think NEW SHARED variables defined there would be available in the program run ie the flow isn't that the AVM runs activate.p which runs soap-proc.p but rather the AVM runs activate.p then soap-proc.p and and so the contexts are different.

     

     

  • Chris Koster (Customer)

    Thanks to all the input so far. Yes, we understand and share the views of shared variables. Unfortunately, we are dealing with a 30+ year legacy application that is slowly being reworked. We have pretty much done what you suggested with web-handler.p, @Peter Judge (Progress Software)​ - and Life is Good 😀. The only problem is that the flow of requests (in terms of context) for AppServer is pretty much the same as is for web requests in our environment. Is there an equivalent of soap-proc.p for AppServers and rest? If there is, we can sort ourselves for now.

    • Peter Judge (Progress Software)

      @Chris Koster (Customer)​  For APSV (ABL clients), it doesn't look like there's a behaviour change between Classic and PASOE: If I have a simple activate procedure that defines a new shared variable (and sets a value), and a runme.p that reads that value, I get the same error in both agent logs when I run the .p from the client.

      [20/11/19@10:16:32.877-0500] P-031876 T-026632 1 AS -- runme.p Shared variable HW has not yet been created. (392)

       

      Classic is a Stateless server, for what that's worth.

       

      APSV and SOAP require the program that's run to be a procedure, and so that doesn't change things. REST can be a procedure or a class, and WEB always calls classes. I would think that the primary place you'dsee issues would be with WebSpeed apps, since PASOE moved from the first thing being run being a procedure to a class, which would mess up shared variables.

       

      Or am I missing something obvious here?

       

       

       

      • Johan Bijker (Customer)

        Thanks @Peter Judge (Progress Software)​ . I've tested this on classic Appserver and you're correct - it has the same issue as on PAS, so nothing was changed here.