<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>The Software Gorilla &#187; PVM</title>
	<atom:link href="http://www.thesoftwaregorilla.com/tag/pvm/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.thesoftwaregorilla.com</link>
	<description>The Software Gorilla</description>
	<lastBuildDate>Thu, 08 Jul 2010 07:23:24 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>OpenEdge Dynamic OpenClient Java Example</title>
		<link>http://www.thesoftwaregorilla.com/2009/11/openedge-dynamic-openclient-java-example/</link>
		<comments>http://www.thesoftwaregorilla.com/2009/11/openedge-dynamic-openclient-java-example/#comments</comments>
		<pubDate>Tue, 10 Nov 2009 02:27:48 +0000</pubDate>
		<dc:creator>Bruce Gruenbaum</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[OpenClient]]></category>
		<category><![CDATA[OpenEdge]]></category>
		<category><![CDATA[4GL]]></category>
		<category><![CDATA[ABL]]></category>
		<category><![CDATA[Application Server]]></category>
		<category><![CDATA[AppServer]]></category>
		<category><![CDATA[AVM]]></category>
		<category><![CDATA[Dynamic OpenClient]]></category>
		<category><![CDATA[Java OpenClient]]></category>
		<category><![CDATA[OpenEdge OpenClient]]></category>
		<category><![CDATA[Progress]]></category>
		<category><![CDATA[Progress AppServer]]></category>
		<category><![CDATA[Progress OpenClient]]></category>
		<category><![CDATA[PVM]]></category>

		<guid isPermaLink="false">http://www.thesoftwaregorilla.com/?p=90</guid>
		<description><![CDATA[In a previous post, I said I would post an example that demonstrates the use of the OpenEdge Dynamic OpenClient. Well here is the Java version of it. This post is an extensive discussion of the example and how it is structured. Enjoy.]]></description>
			<content:encoded><![CDATA[<p>In a previous post, I said I would post an example that demonstrates the use of the OpenEdge Dynamic OpenClient. Well <a href="http://www.thesoftwaregorilla.com/wp-content/uploads/2009/11/DynamicOpenClient.zip">here is the Java version of it</a>.</p>
<p>It took me a while to get this done, not so much because of the code, but because the documentation that accompanies it is pretty extensive and I have been in the middle of some significant career changes.</p>
<p>This post is long primarily because I walk through the highlights of the what the code does and how this bolsters what I was saying in my earlier post.</p>
<p>Inside the zip file are 2 zip files and&nbsp;3 PDF files:</p>
<ul>
<li><strong>DynamicOpenClientJar.zip</strong> is the compiled version of the code. It contains a jar file, the input.xml, a batch file, and the Progress source code.</li>
<li><strong>DynamicOpenClientJar.pdf</strong> contains setup instructions to set it up and run it.</li>
<li><strong>DynamicOpenClientSrc.zip</strong> is the source code.</li>
<li><strong>DynamicOpenClientSrc.pdf</strong>&nbsp;contains the instructions that&nbsp;that tells you how to set&nbsp;the source code up to work with Eclipse.</li>
<li><strong>DynamicOpenClient.pdf </strong>is a detailed tour of the code and describes how it works.</li>
</ul>
<h4>This is Sample Code</h4>
<p>Before you dig into the code and say &quot;Wow!! This code sucks!!&quot; it is important to understand that this is sample code. There are a lot of things about it that are ugly. Some of the object model needs serious refining. There are cleaner ways of doing the XML&nbsp;parsing and it is nowhere near as defensive as it should be. There are those who believe that bad code samples lead to bad code in production, but there is another line of thought that goes that if you don&#39;t recognize those bad examples, you should not be coding to start with. The point with this code is that it demonstrates concepts and that&#39;s what it was intended to do.</p>
<h4>Code Limitations</h4>
<p>For the sake of brevity, I&nbsp;have deliberately not covered stuff like supporting supporting ProDataSets and all of the different Progress data types.&nbsp;I have covered the most common ones and temp-tables. ProDataSets are really an extension of temp-tables and you can feel free to go and look at the code in a generated proxy to see how they work. I&nbsp;also stopped short of supporting the building of temp-tables from an input XML&nbsp;file. The extra code to do this did not seem like it would really add that much to the example. This code example also does not support persistent procedures because they are a lot more complicated than this code will allow.</p>
<h4>Set it up and try it</h4>
<p>Having said all of that, I wanted to spend some time in this post elaborating on the comments that I made in the earlier post about why Dynamic OpenClient is such a powerful technology, using the example code that I am presenting here. So I am going to assume for the rest of this article that you have at least set up and run the example and that you have the source code accessible to follow along.</p>
<h4>How it Works</h4>
<p><a href="http://www.thesoftwaregorilla.com/wp-content/uploads/2009/08/Sequence-Diagram.jpg" target="_blank"><img align="right" alt="Dynamic Open Client Utility Sequence Diagram - Click for full size" border="0" height="192" hspace="2" src="http://www.thesoftwaregorilla.com/wp-content/uploads/2009/08/Sequence-Diagram.jpg" vspace="2" width="300" /></a>The Dynamic Open&nbsp;Client code takes an XML&nbsp;file and parses it for AppServer connection parameters, R-Code signatures and calls that need to be made to the Progress/OpenEdge R-Code files. The sequence diagram at right (click on it for a full-size image)&nbsp;shows the process.</p>
<p>Once the XML file has been parsed, the utility executes each of the calls and serializes the results of the call&#39;s execution to an internal XML document. Once all the calls have been executed and serialized, the results are written out to another XML file that is specified in the input.xml file. More information on the XML file&#39;s format is in the DynamicOpenClient.pdf file.</p>
<h4>The Main Components</h4>
<p>To understand how this utility works, you need to understand the core of the class model. So let me start by dividing the class model into its major areas.</p>
<p>You can pretty much ignore the <em>com.tsg.common</em> project.&nbsp;It contains&nbsp;an interface -&nbsp;<em>INamedItem</em> &#8211; used&nbsp;by a few classes that&nbsp;simply has a getName() method declaration.&nbsp;<em>INamedItem</em> is&nbsp;implemented by some of the classes in the <em>com.tsg.dynamicopenclient</em> project. This interface is specifically used for objects that may be stored in a <em>com.tsg.common.collections.OrdinalList</em> &#8211; a specialized list that is ordinal like an ArrayList, but expects uniquely named items in the list.&nbsp;<em>OrdinalList</em> is used&nbsp;for things like field lists and parameter lists where the fields need to be in a specific order, but the names have to be unique.</p>
<p>The <em>com.tsg.dynamicopenclient</em> project contains three packages:<a href="http://www.thesoftwaregorilla.com/wp-content/uploads/2009/08/Class-Model.jpg" target="_blank"><img align="right" alt="Dynamic Open Client Sample Utility Class Model" height="200" hspace="2" src="http://www.thesoftwaregorilla.com/wp-content/uploads/2009/08/Class-Model.jpg" vspace="2" width="300" /></a></p>
<ul>
<li><em>com.tsg.dynamicopenclient </em>- This package contains&nbsp;the code that does most of the work;</li>
<li><em>com.tsg.dynamicopenclient.data </em>- This package&nbsp;contains the&nbsp;class hierarchy for parameter and other&nbsp;data element&nbsp;definitions;</li>
<li><em>com.tsg.dynamicopenclient.valueholders </em>- This package contains&nbsp;the class hierarchy for all the value holders that&nbsp;are used to contain&nbsp;the values of input and&nbsp;output parameters.</li>
</ul>
<h5>Data Types</h5>
<p>One of the most&nbsp;important aspects of the&nbsp;communication between the&nbsp;OpenClient and the AppServer, is the mapping of data types from Java to Progress. To&nbsp;make sure that this is done consistently, there is a&nbsp;<em>DataType</em> <em><strong><a href="http://java.sun.com/docs/books/tutorial/java/javaOO/enum.html" target="_blank">enumeration</a></strong></em> in&nbsp;<em>com.tsg.dynamicopenclient</em>.&nbsp;If you have not worked with Java 5, enumerations may be a new concept to you and this one is particularly more so than others. An enumeration is a great way to deal with hard-coded constants and it is particularly useful where a map of constants is necessary. In this&nbsp;particular case we have to map an OpenClient numeric constant to a string representation of the data type, a Java class that can be used to map to that type and a valueholder class that can be used to contain the Java type. The following definition of the INTEGER data type demonstrates the mechanism:</p>
<pre>INTEGER
  (Parameter.PRO_INTEGER, // Progress numeric constant
   Parameter.PRO_INTEGER_NAME, // Progress character constant
   &quot;java.lang.Integer&quot;, // Java class to be used to map
   &quot;com.tsg.dynamicopenclient.valueholders.IntegerValueHolder&quot;),&nbsp;// Dynamic OpenClient valueholder
</pre>
<p>The DataType enumeration has several methods associated with it to retrieve a constant based on the string name of the data type or to obtain the Java class, value holder class&nbsp;or Progress data type constant. It also contains code that will instantiate an object of either the Java class to contain the data type or the value holder. This enumeration truly is at the core of the code.</p>
<h5>Data Elements</h5>
<p>The <em>com.tsg.dynamicopenclient.data</em> package contains a set of classes that define the behavior of data elements. A data element is any item that has a name and a datatype associated with it. Thus, fields and parameters are all data elements. Data elements only store the definition of the element. They do not store the value. The value is stored in a <a href="#ValueHolder">value holder</a>. The abstract base <em>DataElement</em> class contains all the behavior for parsing parameter and field definition information from an XML node.</p>
<p>The <em>Parameter</em> class extends it by providing support for parameter modes (INPUT, INPUT-OUTPUT and OUTPUT)&nbsp;which are mapped in&nbsp;an enumeration called <em>ParameterMode</em> in <em>com.tsg.dynamicopenclient</em>.</p>
<p>The <em>TempTableParameter</em> class extends the <em>Parameter</em> class further by providing support for parsing temp-table definitions from the source code and supporting the construction of ProDataGraphMetaData objects that are used to build the ProDataGraph to transport temp-tables between AppServer and client.</p>
<h5><a name="ValueHolder"></a>Value Holders</h5>
<p>The <em>com.tsg.dynamicopenclient.valueholders</em> package contains a set of classes and an interface that are used to standardize the way that data types are dealt with. Looking at the class model, the value holder hierarchy is to the right of the diagram. This hierarchy leverages the concept of <strong><em><a href="http://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf" target="_blank">generics</a></em></strong> that was introduced in Java 5.</p>
<p>The <em>IValueHolder</em> interface is the base of the hierarchy and the <em>ValueHolder</em> abstract class contains the generic behavior for value holders. These classes provide support for the Progress UNKNOWN value as well as reasonable default values that match the Progress defaults for its data types.</p>
<p>Value holders are used to set the value of input parameters and to contain the&nbsp;values of output parameters.</p>
<h5>DynamicOpenClient Class</h5>
<p>The DynamicOpenClient class in <em>com.tsg.dynamicopenclient</em> contains the <em>main()</em> method&nbsp;- the entry point to the utility. It starts out by parsing the parameters to determine where the XML file is that contains the signatures and input parameters for the calls. The input.xml file&#39;s structure is detailed in the DynamicOpenClient.pdf file that accompanies the source code. In essence, though, there are 4 types of nodes that are important:</p>
<ul>
<li><strong>dynamicoc</strong> &#8211; This is the root node for the document and it contains an OutputFile attribute that has the name of the file that should be used for the results of the calls.</li>
<li><strong>appserver</strong> &#8211; This node contains the signature and call nodes and a set of attributes that define the connection parameters for the AppServer.</li>
<li><strong>signature</strong> &#8211; This node contains the signature of a call that will be executed against the AppServer. The signature node may contain a set of parameter nodes which will be all of the parameters for a procedure. These parameters must be specified in the order that they are specified in the ABL source code. If the parameter&#39;s&nbsp;DataType attribute&nbsp;is TABLE, the node will contain child nodes that are the field and indexes for the table.</li>
<li><strong>call</strong> &#8211; This node contains a list of calls that need to be executed against the AppServer. Each of them should refer (by means of the Procedure attribute) to a previously specified signature. call Nodes may contain subordinate parametervalue nodes that reference a parameter in the signature by name and specify the value to be used as the input value for that parameter.</li>
</ul>
<p>The following code extract&nbsp;comes from&nbsp;the <em>main()</em> method.</p>
<pre>//Extracted from <strong>main()</strong> method.
//

//Instantiate the CallManager
CallManager manager = CallManager.getInstance();

//Parse the XML file.
try {
	parse(fileName);
}
catch (Exception e) {
	System.out.println(e.getMessage());
	return;
}
.
.
.
Iterator&lt;Call&gt; iter = manager.iterator();
while (iter.hasNext()) {
	Call call = iter.next();&nbsp; //Get the call
	call.executeCall();&nbsp;&nbsp; //Execute the call
	outRoot.appendChild(call.serializeResults(outputDocument));
	iter.remove();
}
.
.
.
try {
	writeResults();&nbsp; // Write the XML results file out to disk.
}
catch (Exception e) {
	System.out.println(e.getMessage());
	return;
}</pre>
<p>Before doing anything else, the code instantiates the Singleton CallManager to hold onto all the data that will be parsed from the XML file.</p>
<p>The <em>main() </em>method calls the static <em>parse() </em>method with the name of the XML file to be parsed. This method parses the XML document and iterates through the four element nodes mentioned above. It also sets up a DOM document that will be used as the output document for the results of the calls.</p>
<p>For each element, it instantiates an object of the appropriate class and passes it the XML node that needs to be instantiated. This&nbsp;object is then added to the CallManager where it will be retrieved later.</p>
<pre>//Extracted from <strong>parse()</strong> method.
//
Node elementNode = null;
while((elementNode = iterator.nextNode()) != null) { // Loop through elements

	// Handle root node.
	if (elementNode.getNodeName().equalsIgnoreCase(XMLStrings.INPUT_ROOT_NODE.toString())) { 	

		// Setup the output file.
		String attrValue = XMLStrings.getAttribute(elementNode, XMLStrings.OUTPUT_FILE_ATTR.toString());
		if (attrValue != null) {
			outputFileName = attrValue;
		}
	}

	// Handle appserver node
	if (elementNode.getNodeName().equalsIgnoreCase(XMLStrings.APPSERVER_NODE.toString()) ) {
		manager.setAppServerConnection(new AppServerConnection(elementNode));
	}

	// Handle signature node
	if (elementNode.getNodeName().equalsIgnoreCase(XMLStrings.SIGNATURE_NODE.toString()) ) {
		manager.setSignature(new Signature(elementNode));
	}

&nbsp;	// Handle call node
	if (elementNode.getNodeName().equalsIgnoreCase(XMLStrings.CALL_NODE.toString()) ) {
		manager.addCall(new Call(elementNode));&nbsp;
 	}
}</pre>
<p>The <em>CallManager</em> implements the <em>Iterable</em> interface for the <em>Call</em> class.&nbsp;Back in the <em>main()</em> method, the next thing we do is iterate through all&nbsp;of the <em>Call</em> objects in the <em>CallManager</em> and we execute the call. This is where the real work happens and a description of it is below. When execution is complete, the parameter values will have been stored in the <em>Call</em> object and a call to <em>serializeResults()</em> will return an XML node that can be appended to the output document. The <em>Call</em> object is then deleted from the list of calls to free up the memory that any temp-tables will occupy.</p>
<p>Once all the calls have been executed, a call is made to writeResults() which writes the output XML document to disk.</p>
<h4>Executing the Call</h4>
<p>A&nbsp;call&nbsp;is actually executed by&nbsp;a call to&nbsp;<em>executeCall()</em>&nbsp;on the <em>Call</em> object. The <em>executeCall()</em> method simply builds a parameter array, calls the <em>runProcedure() </em>method on the AppServerConnection object, builds the output parameter value set and handles any exceptions. The thing that makes this client dynamic is the code in <em>buildParamArray()</em> and <em>buildOutputValues()</em>.</p>
<h5>buildParamArray()</h5>
<p>In order to make a call to the AppServer, the OpenClient needs a procedure name to call and a parameter array (<em>com.progress.openABL.javaproxy.ParamArray</em>) for the call. The parameter array needs to contain a full description of each parameter as well as the value to be set for the parameter. The <em>Call</em> object has a procedure name and it has the values of the input parameters. The name of the procedure maps to a <em>Signature</em> object which can be obtained from the <em>CallManager</em> and it contains the definitions of all of the parameters that are needed for the call. Thus, <em>buildParamArray()</em> obtains the <em>Signature</em> object and builds a <em>ParamArray</em> object with each of the parameter definitions in the <em>Signature</em> object. As it attempts to add each parameter to the <em>ParamArray</em>, it obtains a IValueHolder object to contain the parameter&#39;s input or output parameter value.&nbsp;It then&nbsp;checks for a corresponding input value in the <em>Call</em> object. If it finds a corresponding value it sets the input value in the <em>ParamArray</em> from this value. This is how the <em>Call</em> information in the XML file can specify the parameter values by name in any order and only specify the ones that have values associated with them.</p>
<pre>//Extracted from <strong>buildParamArray()</strong> method.
//

CallManager manager = CallManager.getInstance();
Signature sig = manager.getSignature(getProcedureName()); //Get the signature for this call
if (sig == null) {
	throw new DynamicOpenClientException(...);
}
ParamArray params = new ParamArray(sig.getParameterCount()); //Instantiate a ParamArray object

int idx = 0;

// Iterate through all the parameters in the signature.
for (Parameter param : sig) { 

	// Instantiate an IValueHolder for this data type.
	IValueHolder&lt;?&gt; valueHolder = DataType.getValueHolder(param.getDataType());
	paramValues.add(valueHolder); // Put the value holder into the paramValues list.

	// If we can find a parameter value that was set for this call,
	// set the value of the IValueHolder to the input value.
	if (inputValues.containsKey(param.getName())) {&nbsp;
		valueHolder.setObject(inputValues.get(param.getName()));
	}</pre>
<p>Now that the data we&nbsp;need is available (ie, the parameter type information and input values have been set), we can go about setting the values, but we need to treat the&nbsp;TABLE and TABLE-HANDLE types differently from the the other data types. The reason is that they need a ProDataGraph and its metadata. The code for setting the values is therefore as follows:</p>
<pre>Object value = null;
switch(param.getDataType()) {
	case TABLE:
	case TABLE_HANDLE:
		TempTableParameter ttParam = (TempTableParameter) param;
		ProDataGraphValueHolder graphHolder = (ProDataGraphValueHolder) valueHolder;
		ProDataGraphMetaData metaData = null;
		switch(param.getMode()) {
			case OUTPUT:
				// For a table we need the table structure. TABLE_HANDLE needs null.
				if (param.getDataType() == DataType.TABLE) {
					metaData = ttParam.getProDataGraphMetaData();
				}
				break;
			default:
				value = graphHolder.getProDataGraph();
				metaData = graphHolder.getProDataGraph().getMetaData();
				break;
		}

		// Add a parameter to the ParamArray with the appropriate value.
		params.addParameter(idx, value, ttParam.getMode().getModeCode(),
			ttParam.getDataType().getTypeCode(),
			ttParam.getExtent(),
			metaData);
		break;
	default:
		// Now get the object from the IValueHolder that has the value in it.
		value = valueHolder.get(); 

		// Add a parameter to the ParamArray with the appropriate value.
		params.addParameter(idx, value, param.getMode().getModeCode(),
			param.getDataType().getTypeCode(),
			param.getExtent(),
			(ProDataGraphMetaData) null);
		break;
}
</pre>
<p>The outer switch in this code handles the difference between TABLE, TABLE-HANDLE and other parameters. So skip the first cases and look at the default case (the last 11 lines of this code block from the word &quot;default:&quot; on.&nbsp;All this code does is grab the object that&nbsp;is in&nbsp;<em>ValueHolder</em> and&nbsp;assign it to the value variable. It then adds a parameter specifying the&nbsp;ordinal position (idx), the value, the&nbsp;parameter&nbsp;mode (INPUT, OUTPUT or&nbsp;INPUT-OUTPUT), the Progress DataType Code from the <em>DataType</em>, the extent and a null for the <em>ProDataGraphMetaData</em>.</p>
<p>The TABLE and TABLE-HANDLE&nbsp;cases are slightly more complicated. For a TABLE&nbsp;or TABLE-HANDLE if the parameter is either an INPUT or INPUT-OUTPUT parameter, the value has to be set to the <em>ProDataGraph</em> that will&nbsp;be&nbsp;used to contain the return value. The <em>ProDataGraphMetaData</em> needs to be specified to supply the table structure. In the case of an OUTPUT parameter, a TABLE parameter requires <em>ProDataGraphMetaData</em> but null in the value parameter and in the case of a TABLE-HANDLE, both the metadata and the value need to be null. In both cases, the <em>Parameter</em> object will have been specialized as a <em>TempTableParameter</em> object so that it could build the temp-table structure.</p>
<p>Once the parameters have all been added to the <em>ParamArray</em>, it is returned to the caller (executeCall) and is now ready to invoke the call.</p>
<h5>runProcedure()</h5>
<p>The <em>runProcedure()</em> method call is run on the <em>AppServerConnection</em> object that is contained in the <em>CallManager</em>. The AppServerConnection class performs 2 functions:</p>
<ol>
<li>It stores the AppServer connection parameter values; and</li>
<li>It executes calls against the AppServer.</li>
</ol>
<p>The first thing <em>runProcedure()</em> does is call <em>obtainAppObject()</em>. The <em>OpenAppObject </em>class provides access to AppServer connections from the connection pool and supports all communication with the AppServer. The <em>obtainAppObject()</em> call instantiates a <em>Connection</em> object and then an <em>OpenAppObject</em> if they have not already been instantiated. This establishes the connection to the AppServer.</p>
<p>Once the connection is established, the <em>runProc()</em> method on the <em>OpenAppObject</em> is run with the&nbsp;<em>ParamArray</em> that was passed in and any exceptions are thrown back to the caller.&nbsp;If this code completes successfully, the AppServer call has succeeded.</p>
<h5>buildOutputValues()</h5>
<p>After the call has completed, <em>executeCall()</em> calls <em>buildOutputValues() </em>with the <em>ParamArray</em> that was used&nbsp;in&nbsp;<em>runProcedure()</em>.&nbsp;</p>
<pre>//Extracted from <strong>buildOutputValues()</strong>
//
returnValue = params.getProcReturnString();
if (returnValue == null) {
	returnValue = &quot;null/Unknown (?)&quot;;
}
int idx = 0;
for (Parameter param : sig) { // Loop through each parameter in the signature.
	switch (param.getMode()) { // Check the parameter mode.
		case INPUT_OUTPUT:
		case OUTPUT:

			// Get the value of the parameter from the paramArray.
			Object outputValue = params.getOutputParameter(idx); 

			// Set the value of the parameter in our internal array to be the result
			paramValues.get(idx).setObject(outputValue);
	}
	idx++;
}</pre>
<p>The buildOutputValues() method&nbsp;does 2 things:</p>
<ol>
<li>It obtains the value of the RETURN-VALUE string from Progress; and</li>
<li>It loops through all OUTPUT or INPUT-OUTPUT parameters and sets the value in the <em>IValueHolder</em> from the value in the&nbsp;<em>ParamArray.</em></li>
</ol>
<p>The output parameter values are thus available to the <em>Call</em> object for serialization.&nbsp;</p>
<h4>Why this is so powerful&nbsp;</h4>
<p>That is the crux of the code in the Dynamic OpenClient Sample Utility. It&#39;s not really that complicated and although this is a crude example, it does show the relative ease with which one can build a&nbsp;client that invokes calls for which all signature information is obtained at runtime.</p>
<p>If you work through the associated document that contains a description of running the utility, you will notice how easy it is to edit the ABL code and change temp-table structures or any other signature related information, modify the corresponding signature in the XML file and re-run the utility without changing the Java code.</p>
<p>If you go ahead and automate the generation of the XML signatures into a post-compile operation and post it on a web-server or some other centralized resource, there is never a need to change the Java code unless you discover a bug in it or Progress adds a data type.</p>
<p>There&#39;s another interesting side effect to this. It would not take much effort beyond this to build an automated mechanism for dynamic WSDL generation so that you dynamically expose any ABL procedure as a WebService. Taking it further, exposing it as an EJB or J2EE container or a JMS endpoint would be fairly simple too and would mean very little would ever need to be changed in the plumbing between the client and the server. You see, as far as I am concerned, everything related to ProxyGen is plumbing that should never require a recompile unless the underpinnings change. ProxyGen forces that each time a temp-table definition is changed.</p>
<p>Hopefully this example is useful. I am planning a .NET equivalent later on.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.thesoftwaregorilla.com/2009/11/openedge-dynamic-openclient-java-example/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>OpenEdge Dynamic OpenClient</title>
		<link>http://www.thesoftwaregorilla.com/2009/07/openedge-dynamic-openclient/</link>
		<comments>http://www.thesoftwaregorilla.com/2009/07/openedge-dynamic-openclient/#comments</comments>
		<pubDate>Wed, 29 Jul 2009 20:06:47 +0000</pubDate>
		<dc:creator>Bruce Gruenbaum</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[OpenClient]]></category>
		<category><![CDATA[OpenEdge]]></category>
		<category><![CDATA[n-tier Development]]></category>
		<category><![CDATA[4GL]]></category>
		<category><![CDATA[ABL]]></category>
		<category><![CDATA[Application Server]]></category>
		<category><![CDATA[AppServer]]></category>
		<category><![CDATA[AVM]]></category>
		<category><![CDATA[Dynamic OpenClient]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Java OpenClient]]></category>
		<category><![CDATA[OpenEdge AppServer]]></category>
		<category><![CDATA[OpenEdge OpenClient]]></category>
		<category><![CDATA[Progress]]></category>
		<category><![CDATA[Progress AppServer]]></category>
		<category><![CDATA[Progress OpenClient]]></category>
		<category><![CDATA[PVM]]></category>

		<guid isPermaLink="false">http://www.thesoftwaregorilla.com/?p=75</guid>
		<description><![CDATA[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.]]></description>
			<content:encoded><![CDATA[<p>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.&nbsp;&nbsp;For the&nbsp;uninitiated, the OpenClient is&nbsp;a very powerful&nbsp;library of&nbsp;code and&nbsp;a set of tools that allows&nbsp;.NET, Java and WebService access to the OpenEdge AppServer. It allows you to build&nbsp;your business logic in the OpenEdge ABL&nbsp;and access it from whatever client you choose.</p>
<p>In order for the OpenClient to be able to connect to the AppServer, make a call and return the output parameters, there is a&nbsp;certain amount of code that you need&nbsp;on the client (Java or .NET) that wraps the OpenClient library.&nbsp;In an effort to simplify the&nbsp;process of writing that code,&nbsp;OpenEdge includes&nbsp;a utility called &quot;ProxyGen&quot;&nbsp;that parses the OpenEdge ABL&nbsp;code and generates a&nbsp;proxy class&nbsp;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.</p>
<p>The problem with ProxyGen is that you have to regenerate the proxy and potentially rebuild the client each time the ABL&nbsp;procedure changes its signature. That would be&nbsp;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.</p>
<p>&quot;Hang on, Bruce,&quot; I can hear you say, &quot;Why is the signature of the temp-table not a change to the signature to the API?&quot; I&#39;m not saying that it isn&#39;t, but I&nbsp;am saying that it is far more difficult to avoid making this mistake. Here&#39;s why:</p>
<p><img align="right" alt="Temp-Table Definitions being included in classes and procedures" height="193" src="http://www.thesoftwaregorilla.com/wp-content/uploads/2009/07/TempTableInclude.jpg" width="500" />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&nbsp;attached diagram.</p>
<p>Let&#39;s assume that Procedure1.p and Procedure2.p are deployed to the AppServer and that&nbsp;Procedure1.p accepts the temp-table as an input parameter and Procedure2.p uses it as an input-output parameter.</p>
<p>As a result of a necessary change to the in-memory data model, a field or two are added to&nbsp;the&nbsp;temp-table definition for the purposes of the Class1.cls and Class2.cls.&nbsp; 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.</p>
<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&nbsp;error will now occur for the client when these procedures are called.</p>
<p>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.</p>
<p>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&nbsp;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.</p>
<p>This functionality is, in fact, so powerful that the entire signature, including all&nbsp;parameter definitions,&nbsp;can be discovered and defined at run-time.</p>
<p>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&nbsp;the WSDL at run-time from the procedure. Obviously there are overheads associated with auto-discovering a procedure&#39;s signature. It makes sense, therefore, to build out this functionality so that the signature discovery is done as seldom as possible.</p>
<p>In a future article I will provide some code samples that show you how to use the Dynamic OpenClient.</p>
<p><strong>UPDATE: </strong>I have subsequently made this code sample available. You can refer to it <a href="/2009/11/openedge-dynamic-openclient-java-example/">here</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.thesoftwaregorilla.com/2009/07/openedge-dynamic-openclient/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>OpenEdge AppServer &#8211; Exposing Progress OpenEdge</title>
		<link>http://www.thesoftwaregorilla.com/2009/04/openedge-appserver-exposing-progress-openedge/</link>
		<comments>http://www.thesoftwaregorilla.com/2009/04/openedge-appserver-exposing-progress-openedge/#comments</comments>
		<pubDate>Fri, 17 Apr 2009 00:00:35 +0000</pubDate>
		<dc:creator>Bruce Gruenbaum</dc:creator>
				<category><![CDATA[Commentary]]></category>
		<category><![CDATA[4GL]]></category>
		<category><![CDATA[ABL]]></category>
		<category><![CDATA[Application Server]]></category>
		<category><![CDATA[AppServer]]></category>
		<category><![CDATA[AVM]]></category>
		<category><![CDATA[Dynamic OpenClient]]></category>
		<category><![CDATA[Java OpenClient]]></category>
		<category><![CDATA[OpenEdge]]></category>
		<category><![CDATA[OpenEdge AppServer]]></category>
		<category><![CDATA[OpenEdge OpenClient]]></category>
		<category><![CDATA[Progress]]></category>
		<category><![CDATA[Progress AppServer]]></category>
		<category><![CDATA[Progress OpenClient]]></category>
		<category><![CDATA[PVM]]></category>

		<guid isPermaLink="false">http://www.intangere.com/tsg/?p=27</guid>
		<description><![CDATA[In 1999 Progress held a user conference in Boston where they showed the any-any-any model. Progress was going to become open. You could connect any client on any platform to any database using a Progress AppServer to handle your business logic. This was a really good idea. The preceding 10 years had taught me that Progress was an outstanding platform for writing the business logic that controlled your application and ensured your data integrity. I was sold. 
]]></description>
			<content:encoded><![CDATA[<p>The last 20 years of my life have ben spent working with <a href="http://www.progress.com/openedge" target="_blank">Progress Software</a> in one form or another. I started work with Progress (now OpenEdge)&nbsp;in 1989 in Progress version 4. I transitioned to it from MicroFocus COBOL after having cut my teeth programming in Basic and COBOL.</p>
<p>I was introduced to Progress while I was working for a small software house in South Africa, <a href="http://www.rdata.co.za/" target="_blank">R-Data</a>. R-Data had split from a company called Independent Software (no relationship to Independent Software in the USA as far as I know) and we re-developed the local government financial system in Progress to provide significantly greater functionality. When we finally rolled out the initial release of the product&nbsp;sometime in late 1990, we were using Progress version 6. I was involved in implementations of this system at several local governments throughout South Africa and also at the Ministry of Works in Malawi on a World Bank development project.</p>
<p>As I sit back and reflect on those days now, the thing that strikes me is that R-Data was based in Cape Town, several thousand miles from Malawi and a long way away from most of its South African customers. Despite this we were able to maintain these systems by means of 2400 baud modems with no on-site technical staff. Progress was just so solid and reliable that we had no problems with it.</p>
<p>Through the rest of the 1990s I&nbsp;did several consulting jobs&nbsp;using Progress and finally, in 1997, I&nbsp;went to work full time for a&nbsp;company that had an interesting&nbsp;problem to deal with. They had satellite offices all&nbsp;around South Africa and a centralized data&nbsp;center. The best&nbsp;network connection&nbsp;they&nbsp;could get for the remote offices&nbsp;was a 64K&nbsp;diginet line. Progress, now in version 8, had released the first version of the Progress AppServer. We decided to see if we could improve the application&#39;s performance using the AppServer. After some work re-architecting the way that we called procedures from the application, we rolled out the changes using the AppServer in late 1997.</p>
<p>I remember getting a phone call from one of the users the next morning. She had just&nbsp;performed an operation that had previously had a 30 &#8211; 45 second latency and was convinced something was wrong because the operation completed in less than a second. That has to be one of my favorite triumphs. The lady was ecstatic when I told her that I had fixed the problem she had been complaining about. The company that I was working for was able to save itself the cost of upgrading its diginet lines &#8211; at that time a very expensive operation in South Africa.</p>
<p>In 1998 I presented our experience at a Progress User Conference in London. Progress released version 9 shortly thereafter and I very quickly realized that the AppServer could be used in conjunction with Java. In 1999 Progress held a user conference in Boston where they showed the any-any-any model. Progress was going to become open. You could connect any client on any platform to any database using a Progress AppServer to handle your business logic. This was a really good idea. The preceding 10 years had taught me that Progress was an outstanding platform for writing the business logic that controlled your application and ensured your data integrity. I was sold.&nbsp;</p>
<p>Then I was offered a job at Progress Software Corporation in the United States and my family and I moved to the US&nbsp;at the end of 1999. The AppServer and its future seemed to be something that I was going to be tied to.</p>
<p>Within 2 weeks of moving to the US, I went to the VP of development at Progress and told him that I believed Progress needed to forget about its focus on the client and concentrate on improving and growing the AppServer that could be used to build the best business applications in the world. Progress needed to provide tools and support to help Java and (then) VB developers to build applications based on the&nbsp;OpenEdge AppServer. I was shot down in flames. Who was this upstart anyway?</p>
<p>Over the next 7 years I worked at Progress on several efforts in their tools development group to help improve the process of building strong business applications. A framework called Progress Dynamics was released. While it held great promise on the business side, the user interface was based on Progress and was clunky. The ideas were revolutionary &#8211; store the UI definition in a repository and you could render it for any platform &#8211; client-server, the web, a mobile device, etc. Unfortunately the product was not well marketed and explained. Initial releases had significant performance problems that were improved later, but it tried to do too much. I still believe that Dynamics had a strong potential future, but it needed better vision to see it through.</p>
<p>Of course, Dynamics was not the only initiative. Progress then decided to use Eclipse to build their equivalent of the JDT &#8211; the PDT &#8211; for developing Progress applications. The PDT was (and is) sold as OpenEdge Architect and while it does a good job of helping you build&nbsp;Progress code, it&nbsp;does little or nothing to help you build applications that use the AppServer.</p>
<p>Eclipse has recognized that the JDT is not enough for Java.&nbsp;You need&nbsp;an&nbsp;Eclipse for Java EE to&nbsp;build&nbsp;enterprise class&nbsp;applications and in the same way that Java EE provides&nbsp;Glassfish as its application server, so the Progress AppServer&nbsp;needs its own set of tools around it so that application development against an AppServer is not that complex.</p>
<p>In&nbsp;2006 Progress embarked on a project to&nbsp;integrate the .NET user interface into the Progress&nbsp;execution&nbsp;environment to provide an improved user interface for Progress.&nbsp;Progress would host the .NET CLR and the user interface would be able to interact directly with Progress objects. At the user conference later that&nbsp;year I presented a preview of what were looking to build in conjunction with&nbsp;the lead architect on the project &#8211; a lady for whom I have&nbsp;immense respect.</p>
<p>The problem, though, with&nbsp;hosting the CLR&nbsp;is that Progress takes on the responsibility of keeping up with what Microsoft is doing and it ties itself to Microsoft platforms.&nbsp;Moreover, no matter how much Progress tries to hide the internals of&nbsp;.NET from its customers, exposing the&nbsp;.NET UI forces those&nbsp;customers to learn something about .NET and its control mechanism.</p>
<p>Progress does not have the budget to spend on UI development that Microsoft does. It&nbsp;does not have the&nbsp;open source community to build out the&nbsp;Rich Client&nbsp;Platform the way that Eclipse does. It also does not have the&nbsp;authority to drive the&nbsp;industry&#39;s UI direction that either Java&nbsp;or .NET do.&nbsp;At the time that I&nbsp;became involved in the .NET integration project, I was of the opinion that Progress should concentrate its resources on exposing the business logic&nbsp;components to Microsoft and Java objects inside the Progress execution environment and&nbsp;let developers choose which of these it wanted to use to&nbsp;build the front-ends to their&nbsp;applications. Ideally, a set of extensions to the Progress OpenClient that would allow easier AppServer-based development.</p>
<p>In terms of development effort from Progress&#39; point of view, the&nbsp;OpenEdge AppServer is all but forgotten. Like WebSpeed before it, Progress&nbsp;has done little to improve and enhance this product so that it can be used in&nbsp;a large, scalable&nbsp;environment. There has been&nbsp;little done&nbsp;to its basic functionality since&nbsp;the&nbsp;last major changes&nbsp;to support a state-free operating mode for WebServices and I believe the release of that was in around 2004.</p>
<p>After I left Progress in July 2006, I joined a company that is using the OpenEdge AppServer in a reasonable sized configuration. I would not call it a big installation, but it is big enough to demonstrate the successes and flaws of the OpenEdge AppServer. In this environment I was forced to contend with communication with the AppServer from a Progress, Java and .NET source.&nbsp;I have learned a lot about the&nbsp;OpenEdge AppServer in this process.</p>
<p>Although the technology is maligned by many for its lack of scalability, I have found that it provides&nbsp;acceptable&nbsp;performance if the application is properly designed to leverage the AppServer. It also provides a very simple way to expose Progress code to&nbsp;other technologies and the communication between Java&nbsp;or .NET and the AppServer&nbsp;is solid &#8211; solid enough to build&nbsp; a commercial application with.&nbsp;</p>
<p>In one of the&nbsp;later versions of OpenEdge 10 (I believe&nbsp;it was 10.0B ), Progress introduced the dynamic OpenClient which does away with the need for a&nbsp;tool called&nbsp;&quot;Proxygen&quot;. Proxygen requires the generation of a Java/.NET class that&nbsp;is incorporated into your Java/.NET code as a library in order to access the remote 4GL/ABL procedure. With the dynamic OpenClient this is no longer necessary and if you build a truly dynamic client, the AppServer becomes an easily accessible tool for deploying business applications.</p>
<p>As a result of the dynamic OpenClient, I am now able to make use of the&nbsp;OpenEdge AppServer and can&nbsp;expose it to Java, .NET, JSP, ASP.NET,&nbsp;&nbsp;OpenESB and&nbsp;PHP. These are just the technologies that I am using right now. Moreover, I can get at the&nbsp;OpenEdge AppServer from Eclipse, Eclipse RCP applications, Eclipse eRCP and Eclipse RAP. This gives me the portability to move my user interface to&nbsp;virtually any modern&nbsp;desktop and I can&nbsp;avoid having to&nbsp;be tied into Microsoft Vista. Heck, one of the&nbsp;applications I have built runs on my Mac.&nbsp;</p>
<p>What&#39;s interesting is that the tools that I have built for myself to be able to do this have ended up being plugins to Eclipse or add-ins to Visual Studio. There are not a lot of them, but they greatly simplify my ability to build applications based on the Progress AppServer &#8211; the technology that Progress saw as its strategic direction in 1999.</p>
<p>Ten years later, it seems that Progress has never realized that vision.&nbsp;The thing that Progress developers need to do is build better business applications. Their&nbsp;focus&nbsp;is the business logic. The business logic runs&nbsp;on an AppServer. Developing, testing and deploying&nbsp;code&nbsp;for the AppServer is still really hard for&nbsp;a Progress developer to do &#8211; much harder than it is for a Java developer building business logic to run on Glassfish.&nbsp;True, any client on any platform can talk to any database&nbsp;through the Progress AppServer, but the technology to do it is still in its infancy ten years later.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.thesoftwaregorilla.com/2009/04/openedge-appserver-exposing-progress-openedge/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>
