OpenEdge Dynamic OpenClient
I forget how long ago it is that the OpenEdge OpenClient was released. It must be easily close on 10 years, but I am willing to accept that may be an exaggeration. OpenClient is a cool technology. For the uninitiated, the OpenClient is a very powerful library of code and a set of tools that allows .NET, Java and WebService access to the OpenEdge AppServer. It allows you to build your business logic in the OpenEdge ABL and access it from whatever client you choose.
In order for the OpenClient to be able to connect to the AppServer, make a call and return the output parameters, there is a certain amount of code that you need on the client (Java or .NET) that wraps the OpenClient library. In an effort to simplify the process of writing that code, OpenEdge includes a utility called "ProxyGen" that parses the OpenEdge ABL code and generates a proxy class that wraps all of the code needed and exposes it as a simple method call. Once you have generated the proxy, you simply include the generated class in your code (either as a .jar or a .NET assembly, or even directly as source code) and you can make whatever call you need.
The problem with ProxyGen is that you have to regenerate the proxy and potentially rebuild the client each time the ABL procedure changes its signature. That would be okay if the ProDataset and Temp-Table definitions were not part of the signature, but they are and adding a field to a temp-table should not cause a proxy to break.
"Hang on, Bruce," I can hear you say, "Why is the signature of the temp-table not a change to the signature to the API?" I'm not saying that it isn't, but I am saying that it is far more difficult to avoid making this mistake. Here's why:
Typically, temp-tables that form part of the in-memory data model are defined in include files and then used in procedures and classes as in the attached diagram.
Let's assume that Procedure1.p and Procedure2.p are deployed to the AppServer and that Procedure1.p accepts the temp-table as an input parameter and Procedure2.p uses it as an input-output parameter.
As a result of a necessary change to the in-memory data model, a field or two are added to the temp-table definition for the purposes of the Class1.cls and Class2.cls. As part of the build, the code is recompiled and the new code deployment to the AppServer is completed successfully. Note that there has been no planned change to the Procedure1.p and Procedure2.p.
The problem that we now face is that the proxies that were generated for the Procedure1.p and Procedure2.p are now invalid. The temp-table definition is no longer correct and a run-time error will now occur for the client when these procedures are called.
Another problem with proxies is the complexity associated with unit-testing and verifying each individual proxy. Each time a proxy is changed, each of the unit tests for each call in the proxy has to be rerun and retested. There is no way to generically test them to verify the Java/.NET code.
In one of the more recent versions of OpenClient, the API that the OpenClient uses is documented so that it is now possible to dynamically construct the proxy calls at run-time. The overarching benefit in this lies in the ability to now define the temp-table definitions on the fly. The Java/.NET code can now define the temp-table dynamically at run-time so that if a change is made to the definition, the client can deal with the change with no impact on the OpenClient source code.
This functionality is, in fact, so powerful that the entire signature, including all parameter definitions, can be discovered and defined at run-time.
This concept becomes even more interesting as you consider using it for WebServices. ProxyGen can generate the WSDL for a procedure that is to be exposed as a WebService, although it suffers from the same issues if the signature changes. The dynamic OpenClient API makes it possible to dynamically generate the WSDL at run-time from the procedure. Obviously there are overheads associated with auto-discovering a procedure's signature. It makes sense, therefore, to build out this functionality so that the signature discovery is done as seldom as possible.
In a future article I will provide some code samples that show you how to use the Dynamic OpenClient.
UPDATE: I have subsequently made this code sample available. You can refer to it here.
loading...
4 Comments
It is an interesting trade off. Generating Proxies does have the issues you speak of, but any issues with the coding will be seen as compile time.
Using the dynamic interface is a good option if your worried about lots of changes to the data structure or the parameters to pass in and out, but there is a lot more heavy lifting in the coding and (from memory) any coding issues will be seen at run time. The advantage of dynamic calling is of course that you can call any procedure not just the ones that proxies have been generated for.
Good article, look forward to reading more!
I’m not sure I agree with you that the issues you see will be seen at compile time.
First off, the proxies are Java or .NET proxies so there is no testing done until runtime.
Second, in my experience, the generated proxies are normally just given to the Java/.NET developers to include in their code. There is no real testing of the proxies themselves as the Progress engineers don’t know how to write Java/.NET code and the Java/.NET engineers don’t care much about the Progress back-end code.
So really, I would venture a guess that in most cases, the problems associated with changes to the back-end procedures will only show up at run-time anyway.
You are right about the heavy lifting in the code, but I would contend that that is code that is written once and built with a strong suite of unit tests. Once it works properly, it is reliably re-usable.
You wrote: “In one of the more recent versions of OpenClient, the API that the OpenClient uses is documented so that it is now possible to dynamically construct the proxy calls at run-time.”
The only documentation I can recall seeing is on the use of the proxy generator. I can and have opened up the generated .dll (.NET proxy) in reflector and looked at the calling code and have on occasion wrote my own as a stop-gap measure, but I don’t remember seeing anything from Progress guaranteeing that this will be supported in the future; nothing to stop them from completely changing the internals in a future release and leaving your code out in the cold. I’d be delighted if you can show me I’m wrong?
Also how can I dynamically discover the parameters, their types, and their i/o mode of a procedure? If I have a persistent procedure I can call the GET-SIGNATURE method on it, but obviously it needs to be running first and that doesn’t help you with its main parameters. Again I can fire up reflector and look in proxygen.exe and notice that there’s a class “PGRCodeParser” which with casual inspection would seem to do what I need, but again, what’s to stop Progress from completely changing it in the future (especially since this is presumably a closed, internal tool)?
The dynamic API is documented in the “.NET OpenClient Manual”. I’m not sure what version point release it was actually included, but I know for sure that 10.1B has it.
Section 8 of the manual – “Using the Open Client .NET OpenAPI to Directly Access the AppServer” – is all about dynamic OpenClient.
I have an example written in Java that I am almost ready to release. I am just finishing off some of the documentation on the sample.
I plan to make the same sample available in .NET within the next month or so.
As far as getting the signature from the rcode file is concerned, there is no way to do it without parsing the rcode file manually. The PGRCodeParser class does not work for any purpose other than generating the proxies. Don’t waste your time chasing that down.
The RCode file structure is actually not that complicated to parse. I have a parser that I wrote myself that I am working to finalize that I plan to make commercially available through my company, Intangere, LLC, in the near future.