<?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; Progress</title>
	<atom:link href="http://www.thesoftwaregorilla.com/tag/progress/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>Exchange Web Services Example &#8211; Part 4 &#8211; Subscriptions and Notifications</title>
		<link>http://www.thesoftwaregorilla.com/2010/07/exchange-web-services-example-part-4-subscriptions-and-notifications/</link>
		<comments>http://www.thesoftwaregorilla.com/2010/07/exchange-web-services-example-part-4-subscriptions-and-notifications/#comments</comments>
		<pubDate>Thu, 08 Jul 2010 06:52:11 +0000</pubDate>
		<dc:creator>Bruce Gruenbaum</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Exchange Web Services]]></category>
		<category><![CDATA[Exchange Web Services Example]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[OpenClient]]></category>
		<category><![CDATA[OpenEdge]]></category>
		<category><![CDATA[OpenEdge AppServer]]></category>
		<category><![CDATA[n-tier Development]]></category>
		<category><![CDATA[Dynamic OpenClient]]></category>
		<category><![CDATA[EWS]]></category>
		<category><![CDATA[Exchange 2007]]></category>
		<category><![CDATA[Exchange 2010]]></category>
		<category><![CDATA[Exchange EWS]]></category>
		<category><![CDATA[Exchange Impersonation]]></category>
		<category><![CDATA[Exchange Server 2007]]></category>
		<category><![CDATA[Exchange Server 2010]]></category>
		<category><![CDATA[Java 6]]></category>
		<category><![CDATA[Java EE 6]]></category>
		<category><![CDATA[Java OpenClient]]></category>
		<category><![CDATA[Microsoft Exchange 2007]]></category>
		<category><![CDATA[Microsoft Exchange 2010]]></category>
		<category><![CDATA[OpenEdge OpenClient]]></category>
		<category><![CDATA[Progress]]></category>
		<category><![CDATA[Progress AppServer]]></category>
		<category><![CDATA[Progress OpenClient]]></category>

		<guid isPermaLink="false">http://www.thesoftwaregorilla.com/?p=892</guid>
		<description><![CDATA[Now that we have succeeded in creating, updating and deleting calendar items and mastered Exchange Impersonation, it's time to turn our attention to having Exchange notify us about what it is doing. Part 4 of this series is going to provide a detailed code walk-through of some code that leverages the Subscription API. 

The example includes two code examples - one for Java programmers and one for OpenEdge programmers. The OpenEdge version writes updates through the OpenClient via the OpenEdge AppServer to an OpenEdge database.]]></description>
			<content:encoded><![CDATA[<p>Now that we have succeeded in creating, updating and deleting calendar items and mastered Exchange Impersonation, it&#39;s time to turn our attention to having Exchange notify us about what it is doing. Part 4 of this series is going to provide a detailed code walk-through of some code that leverages the Subscription API.&nbsp;</p>
<p>I covered a lot of material about the push subscription mechanism in <a href="http://www.thesoftwaregorilla.com/2010/04/exchange-web-services-subscriptions-and-notifications/">an earlier post</a>, and if you haven&#39;t read it, I highly recommend you go back and revisit it as this article builds on that one. It also builds on <a href="http://www.thesoftwaregorilla.com/2010/05/exchange-web-services-example-part-1-introduction-and-set-up/">part 1</a>, <a href="http://www.thesoftwaregorilla.com/2010/05/exchange-web-services-example-part-2-creating-appointments/">part 2</a>, and <a href="http://www.thesoftwaregorilla.com/2010/06/exchange-web-services-example-part-3-exchange-impersonation/">part 3</a> of this series, so you should probably have worked through those, too.&nbsp;</p>
<h3>Goals and Background</h3>
<p>By now, you know that the reason that I have embarked on this project is to integrate Microsoft Exchange with a CRM application that is in Progress OpenEdge. The first part of the exercise was to figure out how to create calendar items based on data entered into the CRM system, and how to automate that for a large number of sales people without having to log in to each mailbox.</p>
<p>Now that we know how to do that, the next step is handling the situation where the sales person makes a change to a calendar item from Outlook or a mobile device. The item change needs to make it back to the CRM application, and for that, the Exchange WebServices Push Subscription API is going to be essential.</p>
<p>When a change is made to an Exchange Server calendar item, the Push Subscription API can send a message to an HTTP target of your choice. As long as the HTTP target knows how to parse the SOAP message that is sent by Exchange, it can react to the events that it are sent.</p>
<p>In this example, we are going to concentrate on 3 events that occur against objects in the default calendar folder for a mailbox; the Created-, Modified-, and DeletedEvents.</p>
<h3>The Subscription Process</h3>
<p><a href="http://www.thesoftwaregorilla.com/wp-content/uploads/2010/04/Exchange-Web-Service-Subscriptions.jpg" target="_blank"><img align="left" alt="The Subscription process" height="206" src="http://www.thesoftwaregorilla.com/wp-content/uploads/2010/04/Exchange-Web-Service-Subscriptions.jpg" width="300" /></a>By now, the diagram to the left is all too familiar, but it clarifies the process really well, so I am going to walk through it again.</p>
<p>The first step (1.1) is to initiate a call to the Java EWS WebService to register a subscription. The EWS WebService then makes a call to Exchange (1.2) to tell it to call back to the Java HTTP Servlet.&nbsp;</p>
<p>When something happens to an item for which we have registered a subscription, Exchange will call the HTTP Servlet (2.1) with a notification message.</p>
<p>Now the order of what happens next is something that you will want to change for production code, because it should happen in the order illustrated in the diagram, but I have decided to take the simple road with the attached code and process everything in a synchronous way. What follows is the order in which I am doing the processing in the sample code.</p>
<p>After parsing the message notification message that was received (2.1), the code determines if there are any items that need to be processed. A notification message only contains the ItemID of the object that has changed. It does not contain the item itself. This means that we need to call the Exchange Server back to get the item&#39;s information (2.3).</p>
<p>Once we have the item&#39;s information, we can react to it, and in the example code, this includes calling OpenEdge (2.4) with information about the appointment that has changed.&nbsp;It is really important to note that only notifications that actually affect an item are passed on to OpenEdge. This significantly reduces the processing that OpenEdge has to perform.</p>
<p>Finally, when we are done with the processing, we respond to Exchange with an acknowledgement message (2.2) so that it will continue to send us messages.</p>
<p>If you want more information about the details of this process, refer to my detailed description in the blog posting entitled <em><a href="http://www.thesoftwaregorilla.com/2010/04/exchange-web-services-subscriptions-and-notifications/#settingupsub" target="_blank">Exchange Web Services &ndash; Subscriptions and Notifications</a>.</em></p>
<p>It is important to remember that Exchange will poll the Java HTTP Servlet at regular intervals to check that it still wants to receive notifications. The frequency is user-defined as <a href="http://www.thesoftwaregorilla.com/2010/04/exchange-web-services-subscriptions-and-notifications/#statusfrequency" target="_blank">explained in the aforementioned article</a>. These StatusEvents do not require any processing, other than an acknowledgement of receipt back to the Exchange Server (2.2). So the Java HTTP Servlet also performs the function of keeping the subscription alive.</p>
<p>I have also provided a mechanism for unsubscribing from the notifications. This mechanism works by keeping track of all the subscriptions that are currently active, and then returning an <em>Unsubscribe</em> response (2.2) to the Exchange Server after a request to unsubscribe has been received in 1.1.</p>
<h3>The Code</h3>
<p>Now that you understand how the process works, let&#39;s take a look at <a href="http://www.thesoftwaregorilla.com/wp-content/uploads/2010/07/tsgewsexample_4.zip">the code</a>.</p>
<p>First, I need to point out that the <a href="http://www.thesoftwaregorilla.com/wp-content/uploads/2010/07/tsgewsexample_4.zip">download</a> contains 2 zip files. If you are a Java developer with no interest in OpenEdge, you should use the file called <em>EWSAPI_JavaOnly.zip</em>. The difference between this code and the <em>EWSAPI_OpenEdge.zip</em> file is that the latter actually makes AppServer calls to an OpenEdge AppServer, whereas the former simply displays the results of processing the messages in the log file.</p>
<p>The <em>EWSAPI_OpenEdge.zip</em> files can also be installed per the regular instructions, but there are a few additional steps that you have to perform. These are documented in a file called <em>OpenEdge_Setup.pdf </em>that is contained in the downloaded zip file. Essentially, you will need to&nbsp;create a database using a definition that is provided,&nbsp;set up an OpenEdge AppServer that has access to the database, and deploy some OpenEdge ABL code to the AppServer so that it can receive the calls. You will also need to copy your OpenClient runtime libraries to the Glassfish installation so that it can make the OpenClient calls. As I said, all of this is covered in <em>OpenEdge_Setup.pdf</em>.</p>
<p>Like the previous articles, you can download the code and <a href="http://www.thesoftwaregorilla.com/2010/05/exchange-web-services-example-part-2-creating-appointments/#codesetup" target="_blank">set up your workspace as described in part 2</a>.</p>
<h4>What&#39;s New?</h4>
<p>The <em>EWSAPI</em> project that is included in both the <em>EWSAPI_JavaOnly.zip</em> and the <em>EWSAPI_OpenEdge.zip</em> files contains the following code changes in the <em>com.tsg.ews</em> root package (filenames in italics are new files):</p>
<ul>
<li><em><strong>EWSListener.java</strong></em> &#8211; This code, which is new, contains the Java code for the Java HTTP Servlet that listens for notifications from Exchange.</li>
<li><strong>EWSImpersonationCalendar.java</strong> &#8211; This code, which is the code for the Impersonation-based calendar WebService, has been modified to support two new operations &#8211; Subscribe and Unsubscribe.</li>
</ul>
<p>In addition, the&nbsp;<em>com.tsg.ews.calendar</em> package contains the following code changes:</p>
<ul>
<li><strong>Appointment.java</strong> &#8211; A minor modification has been made to the <em>retrieve</em> method to support retrieval with a <em>ChangeKey</em>. I also changed the code to use namespaces.</li>
</ul>
<p>Finally, the <em>com.tsg.ews.service</em> package contains a lot of code changes (again, filenames in italics are new files):</p>
<ul>
<li><strong>EWSResponse.java</strong> &#8211; This code, which deserializes responses from the Exchange Web Service, has been altered to support namespaces in the deserialization of the XML. It also now supports a <em>SubscriptionID</em> and a <em>Watermark </em>property.</li>
<li><strong>ExchangeService.java</strong> &#8211; There are a couple of minor changes to expose some methods outside of the class.</li>
</ul>
<p>An enumeration and a new class have been added to enhance some of the XML parsing that I have been doing:&nbsp;</p>
<ul>
<li><strong><em>EWSNamespaces.java</em></strong> &#8211; This is an enumeration of the namespaces used in Exchange WebServices messages.</li>
<li><strong><em>NamespaceContextImpl.java</em></strong> &#8211; This class extends NamespaceContext and is used in namespace resolution of the XPath queries. It makes use of EWSNamespaces to perform this resolution.&nbsp;</li>
</ul>
<p>The following three classes have been added to support the registration and long-term monitoring of subscriptions:</p>
<ul>
<li><em><strong>SubscriptionManager.java</strong></em> &#8211; This class manages all subscriptions.&nbsp;</li>
<li><em><strong>SubcriptionList.java</strong></em> &#8211; This class is a collection of subscriptions and is used for serialization and deserialization of all subscriptions.</li>
<li><em><strong>Subscription.java</strong></em> &#8211; This class is used to register a subscription and serialize it and it keeps track of the watermarks for the subscription.</li>
</ul>
<p>The remaining new classes and enumerations have been added to parse notifications from Exchange Server:</p>
<ul>
<li><em><strong>Notification.java</strong></em> &#8211; This class is the parent class for each notification. It can be instantiated from an Exchange Web Service SOAP Notification message and it will deserialize the message into its internal objects. The notification, itself, has properties for the <em>SubscriptionId</em>, <em>PreviousWatermark</em>, and a collection of <em>SubscriptionEvents</em>. It also has a flag that indicates whether it is just a status event.</li>
<li><em><strong>SubscriptionEvent.java</strong></em> &#8211; A <em>Notification</em> may include one or more <em>SubscriptionEvent</em> objects. These events will be one of the <em>EventType</em>s (see the next bullet point) for which the subscription has been registered (<em>CreatedEvent</em>, <em>ModifiedEvent</em>, <em>DeletedEvent</em>, or <em>StatusEvent</em>). After the event has been parsed, it contains properties for <em>EventType</em>, <em>SubscriptionID</em>, <em>Watermark</em> (each event will have a different watermark), <em>TimeStamp</em>, <em>AffectedItem</em> (the item that actually changed), and <em>ParentFolder</em> (the folder that contains the item that changed). If the <em>AffectedItem</em> is a calendar item, the calendar item will be retrieved (if possible) and stored in the <em>Appointment</em> property.</li>
<li><em><strong>EventType.java</strong></em> &#8211; This is an enumeration of the supported event types.</li>
<li><em><strong>IDHolder.java</strong></em> &#8211; When we receive an event, it comes with two elements; the first is either a <em>FolderId</em> or an <em>ItemId</em>, and the second is a <em>ParentFolderId</em>. These nodes contain two attributes; an <em>ID</em> and a <em>ChangeKey</em>. The <em>IDHolder</em> has three properties, two of which map to the <em>ID</em> and the <em>ChangeKey</em>. The third is an <em>IDType</em>, described in the next bullet.</li>
<li><em><strong>IDType.java</strong></em> &#8211; This enumeration contains items for each possible type of <em>ID</em> that can be contained in the <em>IDHolder</em>.</li>
</ul>
<h4>The OpenEdge pieces</h4>
<p>If you are using the OpenEdge version of the code (<em>EWSAPI_OpenEdge.zip</em>), the EWSAPI project contains the following additional packages:</p>
<ul>
<li><em>com.tsg.common.collections</em></li>
<li><em>com.tsg.common.interfaces</em></li>
<li><em>com.tsg.dynamicopenclient</em></li>
<li><em>com.tsg.dynamicopenclient.data</em></li>
<li><em>com.tsg.dynamicopenclient.valueholders</em></li>
<li><em>com.tsg.exceptions</em></li>
<li><em>com.tsg.ews.openedge</em></li>
</ul>
<p>All but the last package are modified copies of <a href="http://www.thesoftwaregorilla.com/wp-content/uploads/2009/11/DynamicOpenClient.zip">the code that accompanies</a> the <a href="http://www.thesoftwaregorilla.com/2009/11/openedge-dynamic-openclient-java-example/" target="_blank"><em>OpenEdge Dynamic OpenClient Java Example</em></a> article that I wrote back in November 2009.</p>
<p>The last package contains one class, <em>OpenEdgeCallFactory</em>, that is responsible for making the OpenClient call to the AppServer.</p>
<p>The EWSAPI project for OpenEdge also contains an additional folder, <em>oepieces</em>, that contains the ABL procedure (<em>ewscall.p</em>) that it expects to call and a DF file (<em>test.df</em>) that contains the database definitions for the database table that <em>ewscall.p</em> expects to write to.</p>
<p>I&#39;m not planning to cover the OpenEdge part of this in any detail later in the article. All <em>ewscall.p</em> does is accept a set of parameters and write their values directly to a table in an OpenEdge database. The <em>ewscall.p</em> procedure needs to be deployed to an OpenEdge AppServer. The AppServer needs to be running in STATE-FREE mode against a database that contains the <em>ews_update</em> table that is defined in the <em>test.df</em> file.</p>
<h4>Additional Setup</h4>
<p><a href="http://www.thesoftwaregorilla.com/wp-content/uploads/2010/07/Part4_UpdateDefinitions1.jpg" target="_blank"><img align="left" alt="Updating the soapUI WebService Definition" height="188" src="http://www.thesoftwaregorilla.com/wp-content/uploads/2010/07/Part4_UpdateDefinitions1.jpg" width="301" /></a>Once you have set up the code and have it running, you need to update the WebService definition for the CalendarService in soapUI. The screenshot at left should help you do this. Right-click on the EWSImpersonationCalendarPortBinding tree node, and select <em>Update Definition </em>from the context menu.</p>
<p>Two additional nodes will be added &#8211; Subscribe and Unsubscribe. As you will note from the screenshot, I have created two copies of the RegisterServer request and 8 copies of the Subscribe request.</p>
<p>I have two Exchange servers &#8211; One running Exchange 2010 and one running Exchange 2007 SP3. Each Exchange Server has 4 mailboxes that I am using for testing, hence 2 RegisterServer requests and 8 Subscribe requests.</p>
<p>One other thing I would recommend is if you have an MSDN subscription and you can install Outlook 2010, do so. It will make your life a whole lot easier for testing. In Outlook 2010, you can add more than one Exchange mailbox to your list of mailboxes which means that you can work on any of the test mailboxes from just one Windows login. This makes testing much, much easier.</p>
<h3>Establishing a Subscription</h3>
<p>As I have described, there are two parts to the Subscription API &#8211; the Subscription and the Notification. This code walk-through is therefore going to be divided into two main sections covering each of those parts of the API. This section covers setting up the subscription, and the subscription starts at the <em>CalendarService</em> WebService which is in the <em>EWSImpersonationCalendar</em> class in <em>com.tsg.ews</em>.</p>
<h4>EWSImpersonationCalendar</h4>
<p>This code was included in part 3 of this series, but we have added two additional operations in Lines 76 through 98; Subscribe and Unsubscribe:</p>
<pre>@WebMethod(operationName = &quot;Subscribe&quot;)
@WebResult(name = &quot;Response&quot;)
public EWSResponse subscribe(@WebParam(name = &quot;ServerID&quot;) String serverID,
				 @WebParam(name = &quot;UserAccount&quot;) String account,
				 @WebParam(name = &quot;CallbackURL&quot;) String url,
				 @WebParam(name = &quot;OpenEdgeURL&quot;) String oeUrl) {
	Subscription sub = new Subscription(serverID);
	sub.setAccount(account);
	sub.setListenerURL(url);
	sub.setOpenedgeURL(oeUrl);
	EWSResponse resp = sub.subscribe();
	if (!resp.hasError()) {
		SubscriptionManager.getInstance().register(sub);
	}
	return resp;
}

@WebMethod(operationName = &quot;Unsubscribe&quot;)
@WebResult(name = &quot;Response&quot;)
public EWSResponse unsubscribe(@WebParam(name = &quot;SubscriptionID&quot;) String subscriptionID) {
	SubscriptionManager.getInstance().unsubscribe(subscriptionID);
	return new EWSResponse(&quot;NoError&quot;, 0, &quot;Success&quot;);
}
</pre>
<p>These two operations rely heavily on the concept of the <em>SubscriptionManager</em> which keeps track of all active subscriptions and ensures that they are persisted to a local XML file. This means that when a notification is received later, we have the ability to relate that notification back to the subscription that initiated it, even if we shutdown and restart the Glassfish server. We will definitely talk more about this later once you have a chance to understand the concepts that affect it.</p>
<p>Looking at the <em>Subscribe</em> operation/method in lines 76 through 91, the operation expects to receive a <em>ServerID</em> as an input parameter. This <em>ServerID</em> is the <em>ServerID</em> we retrieved from a previous <a href="http://www.thesoftwaregorilla.com/2010/06/exchange-web-services-example-part-3-exchange-impersonation/#registerserver" target="_blank"><em>RegisterServer</em> call</a>.&nbsp;The <em>UserAccount</em> is used for Exchange Impersonation and to specify the mailbox for which the subscription is to be registered. The <em>CallbackURL</em> is the URL of the Java HTTP Servlet that will handle the notifications, and the <em>OpenEdgeURL</em> is the URL that should be used to call the OpenEdge AppServer.&nbsp;</p>
<p>Lines 82 through 85 establish a Subscription object and in line 86, the <em>subscribe</em> method is called. We&#39;ll look at the <em>subscribe</em> method in a little more detail in a moment, but for now just accept that it is responsible for actually making the call to the Exchange WebService and obtaining a <em>SubscriptionID</em>. In production code, the <em>Subscription</em> object would be instantiated by the <em>SubscriptionManager</em>, but it makes for more readable example code to do it the way I have done it here.</p>
<p>Lines 87 and 88 check the error status on the <em>EWSResponse </em>object&nbsp;and register the subscription with the <em>SubscriptionManager </em>if the subscription was successful. Whether the subscription was successful or not, the EWSResponse object is returned from the WebService, and if it was successful, it contains the <em>SubscriptionID</em> and <em>Watermark</em> for the subscription.</p>
<p>The <em>Unsubscribe</em> operation in lines 93 through 98 pass the <em>SubscriptionID</em> to the <em>unsubscribe</em> method on the <em>SubscriptionManager</em>.</p>
<p>There is a special case that the <em>Unsubscribe</em> method will support: If an &quot;*&quot; is passed as the <em>SubscriptionID, </em>all existing subscriptions will be canceled.</p>
<h4>The Subscription Object</h4>
<p>I said we would take a closer look at the <em>subscribe</em> method, and we&#39;ll do that in just a moment. First, though, open the <em>Subscription.java</em> class that is in the <em>com.tsg.ews.service</em> package. For the most part, this object is a set of properties, including the <em>ServiceID</em>, <em>ListenerURL</em>, <em>OpenEdgeURL</em>, <em>Account</em>, <em>SubscriptionID</em> and <em>Watermark</em>. The class has JAXB serialization annotations associated with it so that it can be serialized to and from XML.</p>
<p>There is also a <em>canceled</em> flag. The <em>canceled </em>flag is set to false until the <em>cancel</em> method in line 130 is called.</p>
<p>The <em>subscribe</em> method is in lines 97 through 128. The SOAP message that needs to be sent is built between lines 98 through 118:</p>
<pre>public EWSResponse subscribe() {
	StringBuffer request = new StringBuffer();
        request.append(&quot;&lt;Subscribe \n&quot;);
        request.append(&quot;               xmlns=\&quot;http://schemas.microsoft.com/exchange/services/2006/messages\&quot;\n&quot;);
        request.append(&quot;               xmlns:t=\&quot;http://schemas.microsoft.com/exchange/services/2006/types\&quot;&gt;\n&quot;);
        request.append(&quot;    &lt;PushSubscriptionRequest&gt;\n&quot;);
        request.append(&quot;\n&quot;);
        request.append(&quot;        &lt;t:FolderIds&gt;\n&quot;);
        request.append(&quot;            &lt;t:DistinguishedFolderId Id=\&quot;calendar\&quot;/&gt;&quot;);
        request.append(&quot;        &lt;/t:FolderIds&gt;\n&quot;);
        request.append(&quot;        &lt;t:EventTypes&gt;\n&quot;);
        request.append(&quot;            &lt;t:EventType&gt;CreatedEvent&lt;/t:EventType&gt;\n&quot;);
        request.append(&quot;            &lt;t:EventType&gt;ModifiedEvent&lt;/t:EventType&gt;\n&quot;);
        request.append(&quot;            &lt;t:EventType&gt;DeletedEvent&lt;/t:EventType&gt;\n&quot;);
        request.append(&quot;        &lt;/t:EventTypes&gt;\n&quot;);
        request.append(&quot;        &lt;t:StatusFrequency&gt;3&lt;/t:StatusFrequency&gt;\n&quot;);
        request.append(&quot;        &lt;t:URL&gt;&quot;);
        request.append(this.getListenerURL());
        request.append(&quot;&lt;/t:URL&gt;\n&quot;);
        request.append(&quot;    &lt;/PushSubscriptionRequest&gt;\n&quot;);
        request.append(&quot;&lt;/Subscribe&gt;\n&quot;);
        SOAPRequest message = new SOAPRequest(request.toString(), this.getAccount());
</pre>
<p>The Subscription message has been discussed in some detail in <a href="http://www.thesoftwaregorilla.com/2010/04/exchange-web-services-subscriptions-and-notifications/#subscriptionrequest" target="_blank">my previous post on Subscriptions and Notifications</a>, and this message is largely the same message as that one, with the exception that the user can specify the URL, and the instantiation of the SOAPRequest object in line 118 (the last line) results in the use of Exchange Impersonation to make the request.</p>
<p>The <em>DistinguishedFolderId</em> node that is added in line 105 will result in notifications being generated based on any changes to the folder(s) that is(are) listed &#8211; in this case, just the <em>calendar</em> folder. The following folders are supported by name:</p>
<ul>
<li>calendar</li>
<li>contacts</li>
<li>deleteditems</li>
<li>drafts</li>
<li>inbox</li>
<li>journal</li>
<li>notes</li>
<li>outbox</li>
<li>sentitems</li>
<li>tasks</li>
<li>msgfolderroot</li>
<li>root</li>
<li>junkemail</li>
<li>searchfolders</li>
<li>voicemail</li>
</ul>
<p>Any folder can be monitored, but if it is not one of these folders, you need to supply the <em>FolderID</em> for the folder.</p>
<p>The <em>EventTypes</em> node in lines 107 through 111 defines the list of events for which notifications should be received. This list could include any of the following values:</p>
<ul>
<li>CopiedEvent</li>
<li>CreatedEvent</li>
<li>DeletedEvent</li>
<li>ModifiedEvent</li>
<li>MovedEvent</li>
<li>NewMailEvent</li>
<li>FreeBusyChangedEvent (Exchange 2010 only)</li>
</ul>
<p>The <em>StatusFrequency</em> node is described in a lot more detail <a href="http://www.thesoftwaregorilla.com/2010/04/exchange-web-services-subscriptions-and-notifications/#statusfrequency" target="_blank">in my previous post</a>. In this case, I am asking for Exchange to send a status event to the Java HTTP Servlet every 3 minutes. This gives me the opportunity to cancel the subscription every 3 minutes. I have mixed feelings about status frequencies in terms of how long they should be, and I&#39;ll talk more about this later in this article.</p>
<p>Once the message is built, we call <em>makeRequest</em> on the <em>ExchangeService</em> object with the message in lines 119 through 128:</p>
<pre>        EWSResponse response;
        try {
		response = getService().makeRequest(message);
		subscriptionID = response.getSubscriptionId();
		watermark = response.getWatermark();
	} catch (Exception e) {
		response = new EWSResponse(e);
	}
	return response;
}
</pre>
<p>If the call is successful, the EWSResponse object will contain a unique <em>SubscriptionID</em> and a <em>Watermark</em>. It is important to understand that the <em>SubscriptionID</em> is unique, even if you have already registered a subscription for this mailbox. In other words, it is possible to have more than one subscription active for a mailbox.</p>
<h4>The SubscriptionManager Object</h4>
<p>The SubscriptionManager object is responsible for tracking all active subscriptions. In essence, it is a persistence mechanism for maintaining the state of a subscription over a long period of time. The SubscriptionManager takes care of persisting the subscription details to an XML file and providing a central point through which all changes to a subscription can take place. Any time any object needs information about a subscription, it makes a call to the SubscriptionManager to obtain information about the subscription.</p>
<p>Any changes made to a subscription are persisted to the XML file, so that if the Glassfish server is restarted, and the subscription is not lost, the SubscriptionManager will be able to maintain the context of any subscriptions that are currently active.</p>
<p>Why do we care about this context anyway? For a number of reasons:</p>
<ol>
<li>We associate the OpenEdge URL with a subscription and there is no way to include this information with the information that we submit to Exchange Server so there is no way to know what we need to do when we receive a notification if we don&#39;t persist the context of the subscription;&nbsp;</li>
<li>To be able to specifically cancel a subscription, the SubscriptionManager needs to know about it. Remember that the only ways to cancel a subscription are by responding to a notification with an unsubscribe response, or by not responding and waiting for it to time out. Ideally, we need the ability to do this gracefully, and that means responding to a notification with the unsubscribe response. It may be several minutes between the time that an unsubscribe request is received by the Java EWS WebService and the time a notification is received by the Java HTTP Servlet, so all the SubscriptionManager can do is note that the subscription is canceled. When the next notification comes in, the Java HTTP Servlet can then act on the cancellation;</li>
<li>In a production environment where there may be numerous mailboxes that are all being monitored concurrently, it would be far more efficient to receive all notifications for one mailbox as a set. The problem is that when a subscription is registered, there may be no way of knowing what other subscriptions will be registered for the same mailbox. Although the implementation that I have included does not do this, the SubscriptionManager is the ideal place to maintain a link between subscriptions for the same mailbox so that the load on the Exchange Server can be minimized and delegated to a separate middleware service; and</li>
<li>If the Glassfish server goes down for any reason, context needs to be maintained so that when it is restarted, it can re-establish the subscriptions as they were running before it was terminated. Of course, in a production environment with load balancing in place, it would be unlikely that the Glassfish server farm would not be available, but the notion of more than one Glassfish server immediately means that the subscription context needs to be persisted centrally.</li>
</ol>
<p>So although the SubscriptionManager that is provided with this example is somewhat primitive, it performs a key role in maintaining continuity and performance for subscriptions.&nbsp;</p>
<p>The SubscriptionManager code is not particularly interesting so I am not going to spend any time walking through it. It definitely has holes in it, but it works for the examples that we are using here.</p>
<h4>Establishing a Subscription</h4>
<p>Now that you know how the code works for establishing a subscription, let&#39;s go ahead and register a subscription for one of the mailboxes. Assuming you already have the code installed and running in Eclipse and the Glassfish server running, open soapUI and open a request under the <em>Subscribe</em> operation for the <em>CalendarService</em>:</p>
<p><a href="http://www.thesoftwaregorilla.com/wp-content/uploads/2010/07/Part4_SettingUpSubscription1.jpg" target="_blank"><img alt="Creating the subscription request" height="297" src="http://www.thesoftwaregorilla.com/wp-content/uploads/2010/07/Part4_SettingUpSubscription1.jpg" width="600" /></a></p>
<p>The <em>ServerID</em> element should contain a UUID for a server that was registered using the <em>RegisterServer</em> operation. The <em>UserAccount</em> should contain the e-mail address of the mailbox that you want to monitor.</p>
<p>The <em>CallbackURL</em> is the first new element you need to complete. This is the URL that the Exchange Server should use for all notifications. This URL will be in the form <em>http://&lt;server-name&gt;:8080/EWSAPI/EWSListener</em>&nbsp;where <em>&lt;server-name&gt;</em> is the machine that is running your Glassfish server (your local machine). Note that you cannot use localhost as the server name. Calls to this URL are going to come from your Exchange Server so it needs a DNS-registered name.</p>
<p>The <em>OpenEdgeURL</em> is only of interest to you if you are an OpenEdge developer. If this element contains anything but a string that begins with <em>AppServer:// </em>it will be ignored. If you are using the default asbroker1 AppService, the URL that I have in the screenshot (<em>AppServer://localhost:5162/asbroker1</em>) should work for you, assuming your AppServer is running on the same machine as your Glassfish server.</p>
<p>Once you have filled out the request parameters, submitting the request will get you a response similar to the one in the following screenshot:</p>
<p><a href="http://www.thesoftwaregorilla.com/wp-content/uploads/2010/07/Part4_SettingUpSubscription2.jpg" target="_blank"><img alt="Subscription request response" height="204" src="http://www.thesoftwaregorilla.com/wp-content/uploads/2010/07/Part4_SettingUpSubscription2.jpg" width="600" /></a></p>
<p>The <em>Response</em> node contains 3 attributes. The <em>ErrorState</em> attribute should have a value of &quot;NoError&quot; and should be followed by a <em>SubscriptionID</em> attribute and a <em>Watermark</em> attribute, both with strings of characters that don&#39;t mean anything. The <em>SubscriptionID</em> uniquely identifies the subscription and can be used to unsubscribe through the <em>Unsubscribe</em> operation.</p>
<p>If you switch your focus to the Console view in Eclipse for the Glassfish Server Log, you will see messages start popping up occasionally in the view like the one below:</p>
<pre>INFO: Notification:
=============
	Account: froggf@intangere.internal.intangere.com
	Subscription Id: LQBpbnRhbmdlcmVleHMuaW50YW5nZXJlLmludGVybmFsLmludGFuZ2VyZS5jb20QAAAAdyIIAinbTUGf+zpPjEyTaw==
	Prev Watermark: AQAAANnz8X+xbA5HtnQ8PXPRJfHGLgAAAAAAAAE=
	More Events: false
	Status: true
	Event: StatusEvent	Watermark: AQAAANnz8X+xbA5HtnQ8PXPRJfHGLgAAAAAAAAE=

INFO: &lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;&lt;soap:Envelope ... &lt;/soap:Envelope&gt;
INFO: Total execution time: 205
</pre>
<p>These messages will pop up every 3 minutes and indicate that the Glassfish server is receiving messages from the Exchange Server.</p>
<p>If you switch to Outlook and create a calendar item in Outlook, you will see a message something like the one below about 20 seconds after you create it:</p>
<pre>INFO: Notification:
=============
	Account: froggf@intangere.internal.intangere.com
	Subscription Id: LQBpbnRhbmdlcmVleHMuaW50YW5nZXJlLmludGVybmFsLmludGFuZ2VyZS5jb20QAAAAdyIIAinbTUGf+zpPjEyTaw==
	Prev Watermark: AQAAANnz8X+xbA5HtnQ8PXPRJfHGLgAAAAAAAAE=
	More Events: false
	Status: false
	Event: ModifiedEvent	Watermark: AQAAANnz8X+xbA5HtnQ8PXPRJfHJLgAAAAAAAAE=
			TimeStamp: Wednesday, July 7, 2010 6:39:42 PM PDT
		Affected Item-- Type: FolderId	ID: ...	ChangeKey: AgAAAA==
	Event: ModifiedEvent	Watermark: AQAAANnz8X+xbA5HtnQ8PXPRJfHKLgAAAAAAAAE=
			TimeStamp: Wednesday, July 7, 2010 6:39:42 PM PDT
		Affected Item-- Type: FolderId	ID: ...	ChangeKey: AgAAAA==
	Event: ModifiedEvent	Watermark: AQAAANnz8X+xbA5HtnQ8PXPRJfHLLgAAAAAAAAE=
			TimeStamp: Wednesday, July 7, 2010 6:39:42 PM PDT
		Affected Item-- Type: FolderId	ID: ...	ChangeKey: AgAAAA==
	Event: CreatedEvent	Watermark: AQAAANnz8X+xbA5HtnQ8PXPRJfHRLgAAAAAAAAE=
			TimeStamp: Wednesday, July 7, 2010 6:39:42 PM PDT
		Affected Item-- Type: ItemId	ID: ...	ChangeKey: DwAAAA==
		Appointment-- Subject: New Appointment for Software Gorilla test
			Location: Who cares where! Just make sure it works!!
			Start: Wednesday, July 7, 2010 7:00:00 PM PDT	End: Wednesday, July 7, 2010 8:00:00 PM PDT

INFO: &lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;&lt;soap:Envelope ... &lt;/soap:Envelope&gt;
INFO: Total execution time: 75</pre>
<p>I have edited this slightly so it fits on the screen. This notification informs you that the Exchange Server has called the Java HTTP Servlet which has processed the notification.</p>
<p>If you supplied an OpenEdge URL for the AppServer, and you connect to the database that contains the <em>ews_update</em> table from OpenEdge and execute the following code, you will find entries in the database for each appointment that you created:</p>
<pre>for each ews_update:
    display skip(1)
        ews_update.cEmailAddress skip
        ews_update.cSubject skip
        ews_update.cLocation skip
        ews_update.dTimeStamp skip
        ews_update.dStartTime ews_update.dEndTime skip(2)
      with 1 down side-labels width 100.
end.</pre>
<p><img align="left" alt="Data from the Exchange Notification reaching OpenEdge" height="245" src="http://www.thesoftwaregorilla.com/wp-content/uploads/2010/07/Part4_DataInOpenEdge.jpg" width="415" />There are a couple of important things to note about the data in the OpenEdge database. If you are not living in the UTC timezone, you will see a difference between the time that OpenEdge reports and the time that notification reported. That&#39;s to be expected. Time in the database is reported as UTC. In my case, I am in the PDT (Pacific Daylight Time) timezone.</p>
<p>Second, it looks like the subject and location have been truncated. Again, no surprise. I set the formats in the database fields to X(50) for the subject and location, although the database will store more than that for them.</p>
<p>Looking back to the notification that we received from Exchange, you&#39;ll note that there is a Watermark for each event, and that each Watermark is different (sometimes by only one character). The Watermark provides a way of going back and verifying that you have received all events from Exchange, but it is important to realize that subscribing with an old Watermark can have significant performance impacts on Exchange.</p>
<p>You&#39;ll also notice that there is a Total Execution Time for each notification. This is the time taken (in milliseconds) from the time the notification was received, until the time the response was flushed out to the HTTP stream. In other words, it is the total time taken to process the request. What is interesting about this is that it takes as much as 4 times longer to process a StatusEvent than it does to process a real notification. I have determined that this has to do with the time it takes to read the original notification message off the input stream, and I have not yet figured out how to shorten that time.</p>
<p>Now that you have seen the notification mechanism working, let&#39;s look at what is going on under the covers.&nbsp;</p>
<h3>Receiving Notifications &#8211; EWSListener</h3>
<p>As noted, notifications are received on the&nbsp;<em>http://&lt;server-name&gt;:8080/EWSAPI/EWSListener </em>URL. To achieve this, the EWSListener.java class in com.tsg.ews listens on the URL for incoming requests, so go ahead an open EWSListener.java in Eclipse.</p>
<p>All of the work associated with processing a request occurs in the doPost method that runs between lines 52 and line 170. I have included a lot of comments in the code that is in the zip file, so I am going to extract sections of it here and exclude the comments that are in the source. Line numbers, though, correspond with the source in the zip file.</p>
<h4>Receiving the Notification (2.1 in the diagram above)</h4>
<p>Lines 52 through 63 deal with receiving the notification:</p>
<pre>protected void doPost(HttpServletRequest request,
		HttpServletResponse response) throws ServletException, IOException {
	boolean safeDoc = false;
	boolean canceled = false;

	long startTime = GregorianCalendar.getInstance().getTimeInMillis();
	long endTime;

	try {

		String result = ExchangeService.getResult(request.getInputStream());
		Document doc = loadXML(result);
		safeDoc = true;

		Notification nfcn = null;
		try {
			nfcn = new Notification(doc);
</pre>
<p>The notification XML document is received from Exchange in the <em>request</em> object. The <em>response</em> object will contain our response back to Exchange later.</p>
<p>The <em>safeDoc</em> flag is used to indicate that we have successfully parsed the XML document and that it is indeed a document that we expect. The <em>canceled</em> flag indicates that the response to Exchange should be an <em>unsubscribe</em> message.&nbsp;The <em>startTime</em> and <em>endTime</em> fields are used to calculate the total elapsed time of the call.</p>
<p>In line 63, we make a call to <em>getResult</em> (now a static method) on the <em>ExchangeService</em> class. This method simply reads all the data off an input stream.&nbsp;</p>
<p>Now that we have a character string, it&#39;s time to do something with it, and in line 66, we make a call to the internal loadXML method which parses the XML string into a DOM document.</p>
<p>In line 74, we instantiate a Notification object with the XML document and the Notification object takes care of parsing the the DOM into appropriate objects.&nbsp;</p>
<h4>Parsing the XML</h4>
<p>I&#39;m not going to spend any time walking through the code that parses the XML document, but the following XML is from an actual notification for the creation of a new appointment (I&#39;ve truncated IDs for brevity):</p>
<pre>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;soap11:Header /&gt;
    &lt;soap11:Body&gt;
        &lt;m:SendNotification xmlns:t=&quot;http://schemas.microsoft.com/exchange/services/2006/types&quot;
                               xmlns:m=&quot;http://schemas.microsoft.com/exchange/services/2006/messages&quot;&gt;
            &lt;m:ResponseMessages&gt;
                &lt;m:SendNotificationResponseMessage ResponseClass=&quot;Success&quot;&gt;
                    &lt;m:ResponseCode&gt;NoError&lt;/m:ResponseCode&gt;
                        &lt;m:Notification&gt;
                            &lt;t:SubscriptionId&gt;LwBncnzAg=&lt;/t:SubscriptionId&gt;
                            &lt;t:PreviousWatermark&gt;AQAAAAE=&lt;/t:PreviousWatermark&gt;
                            &lt;t:MoreEvents&gt;false&lt;/t:MoreEvents&gt;
                            &lt;t:ModifiedEvent&gt;
                                &lt;t:Watermark&gt;AQAAAAAE=&lt;/t:Watermark&gt;
                                &lt;t:TimeStamp&gt;2010-07-05T18:06:29Z&lt;/t:TimeStamp&gt;
                                &lt;t:FolderId Id=&quot;AQAoAnnJwAAAx4AAAA=&quot; ChangeKey=&quot;AgAAAA==&quot; /&gt;
                                &lt;t:ParentFolderId Id=&quot;AQAoAAAxIAAAA=&quot; ChangeKey=&quot;AQAAAA==&quot; /&gt;
                            &lt;/t:ModifiedEvent&gt;
                            &lt;t:CreatedEvent&gt;
                                &lt;t:Watermark&gt;AQAAAAE=&lt;/t:Watermark&gt;
                                &lt;t:TimeStamp&gt;2010-07-05T18:06:29Z&lt;/t:TimeStamp&gt;
                                &lt;t:ItemId Id=&quot;AAAoAGF/4YLFkwAAAAQJAgAA&quot; ChangeKey=&quot;DwAAAA==&quot; /&gt;
                                &lt;t:ParentFolderId Id=&quot;AQAoAAAx4AAAA=&quot; ChangeKey=&quot;AQAAAA==&quot; /&gt;
                            &lt;/t:CreatedEvent&gt;
                        &lt;/m:Notification&gt;
                    &lt;/m:SendNotificationResponseMessage&gt;
            &lt;/m:ResponseMessages&gt;
        &lt;/m:SendNotification&gt;
    &lt;/soap11:Body&gt;
&lt;/soap11:Envelope&gt;</pre>
<p>The <em>Notification</em> element contains a <em>SubscriptionId</em>, <em>PreviousWatermark</em> and <em>MoreEvents</em> element.&nbsp;When the code parses this XML document, it creates a <em>Notification</em> object to contain this information.</p>
<p>The <em>Notification</em> element also contains an&nbsp;<em>Event</em> element for each event for which you are receiving a notification. In this case, the first <em>Event</em> element is a <em>ModifiedEvent </em>and it applies to a Folder (in this case, the calendar folder that has been modified by the addition of a new item). The second event, the <em>CreatedEvent</em> element, contains an <em>ItemId</em> element that identifies the calendar item that was created.</p>
<p>The <em>Notification</em> is an iterable collection of <em>SubscriptionEvent</em> objects, so when the code parses these elements, it creates and stores a <em>SubscriptionEvent </em>object to hold each of these <em>Event</em> elements.&nbsp;Each <em>Event</em> node contains a <em>Watermark</em> that is applicable for the event that took place and a&nbsp;<em>TimeStamp</em> &#8211; the time that the event took place. These values are recorded against the <em>SubscriptionEvent</em> object.</p>
<p>An&nbsp;<em>Event</em>&nbsp;element will contain either a&nbsp;<em>FolderId</em>&nbsp;element or an&nbsp;<em>ItemId</em>&nbsp;element which is the object that was affected by the event. This element is parsed and an <em>IDHolder</em> object is created to contain it and it is stored as the <em>AffectedItem</em> property of a <em>SubscriptionEvent</em> object.</p>
<p>There is also a <em>ParentFolderId</em> which is the ID of the folder that contains the object that is affected by the event. When this element is parsed, its data is also stored in an <em>IDHolder</em> object in the <em>ParentFolder</em> property of the <em>SubscriptionEvent</em> object.</p>
<p>In the case of the above notification, one <em>Notification</em> object was created and it contained two <em>SubscriptionEvent</em> objects &#8211; one for the <em>ModifiedEvent</em> and one for the <em>CreatedEvent</em>.&nbsp;</p>
<h4>Marrying the Notification and the Subscription</h4>
<p>Now that we have parsed the notification, we&#39;ll go back to the code in line 77 through 86 and deal with marrying the Notification with a Subscription object:</p>
<pre>	Subscription sub = SubscriptionManager.getInstance().getSubscription(nfcn.getSubscriptionId());
	if (sub != null) {
		nfcn.setAccount(sub.getAccount());

		canceled = sub.isCanceled();
		if (canceled)
			SubscriptionManager.getInstance().clearSubscription(sub);

		if (!nfcn.isStatus()) {
			getAppointments(sub.getService(),nfcn);
		}</pre>
<p>In line 77 we ask the <em>SubscriptionManager</em> for the <em>Subscription</em> object associated with the <em>SubscriptionId</em> in the <em>Notification</em> object. The next set of code only executes if we found a <em>Subscription</em> object.</p>
<p>In line 80, we set the account (e-mail address) for the notification to the account of the subscription and in line 84, we check to see if the subscription has been canceled by a call to the <em>Unsubscribe</em> operation. If it has, we set the <em>canceled</em> flag and remove the subscription from the <em>SubscriptionManager</em> because this is the last notification we will receive for this subscription.</p>
<p>As I mentioned earlier, Exchange will send notifications on a regular basis (as defined by <em>StatusFrequency</em>). These notifications do not contain any data; they are simply status messages (think of them as pings). When we parse the SOAP message into the <em>Notification</em> object, if the notification is a <em>StatusEvent</em>, we set a flag to indicate that this is only a status message. In line 91 we check to see if this is a status message, and if not, we make a call to <em>getAppointments</em> which is responsible for retrieving appointments from the Exchange Server.</p>
<h4>getAppointments (2.3 in the diagram above)</h4>
<p>The getAppointments method is in lines 176 through 209:</p>
<pre>private void getAppointments(ExchangeService service, Notification nfcn) {

	for (SubscriptionEvent se: nfcn) {

		if (se.getEventType() != EventType.STATUS
				&amp;&amp; se.getEventType() != EventType.DELETED
				&amp;&amp; se.getAffectedItem() != null
				&amp;&amp; se.getAffectedItem().getIdType() == IDType.ITEM) {

			Appointment appt = new Appointment(service);
			appt.setEmailAddress(nfcn.getAccount());

			EWSResponse resp = appt.retrieve(se.getAffectedItem().getId(),
                                                  se.getAffectedItem().getChangeKey());
			if (!resp.hasError()) {
				se.setAppointment(appt);
			}
			else {
				if (resp.getResponseCode().equals(&quot;ErrorItemNotFound&quot;)) {
					se.setEventType(EventType.DELETED);
				}
				else {
					System.out.println(...);
				}
			}
		}
	}
}</pre>
<p>As you will recall, the notification that we receive from Exchange only contains the ID of the item that has changed. It does not provide any information about the item itself. In <em>getAppointments </em>we take care of retrieving those items.</p>
<p><em>Notification</em> objects are iterable collections of <em>SubscriptionEvents</em> so we loop through all the <em>SubscriptionEvents</em>&nbsp;(line 179) in the <em>Notification</em> collection, ignoring anything that is not a <em>Created-</em> or <em>ModifiedEvent</em> for an <em>ItemId </em>(lines 183 to 186).</p>
<p>We then instantiate an Appointment object (line 189), set the account for it (line 190), and attempt to retrieve it from the Exchange Server (line 193).</p>
<p>If we&#39;re successful retrieving the appointment, we set the Appointment property of the SubscriptionEvent from the appointment we retrieved (line196).</p>
<p>If we failed with an <em>ErrorItemNotFound</em> message from Exchange, it means that the item has been deleted so we cannot find any information about it. The event type therefore needs to be changed to a <em>DeletedEvent </em>(lines 200 through 202). This may seem strange, but remember that Exchange will move a deleted item to the Deleted Items folder which means we will receive a <em>ModifiedEvent</em>, rather than a <em>DeletedEvent</em>.</p>
<h4>Calling OpenEdge (2.4 in the diagram above)</h4>
<p>Returning to the code in doPost, lines 96 through 103 deal with the call to OpenEdge:</p>
<pre>	System.out.println(nfcn.print());

	// **OESPECIFIC
	// The following code is inactive in the Java zip file version of the code.
	// If the subscription has an OpenEdge AppServer URL in it, let&#39;s call OpenEdge.
	if (sub.getOpenedgeURL().startsWith(&quot;AppServer://&quot;)) {
		OpenEdgeCallFactory.callOpenEdge(sub, nfcn);
	}
</pre>
<p>Line 96 just displays the notification information in human readable form &#8211; it is responsible for the Notification messages in the server log.</p>
<p>Line 101 through 103 checks if the OpenEdge URL for the subscription starts with the string &quot;AppServer://&quot; and, if so, calls the <em>callOpenEdge</em> method which is in lines 68 through 99 of the <em>OpenEdgeCallFactory</em> class:</p>
<pre>public static void callOpenEdge(Subscription sub, Notification nfcn) {
	checkInitialized();
	for (SubscriptionEvent se: nfcn) {
		if (se.getAffectedItem() != null &amp;&amp; se.getAffectedItem().getIdType() == IDType.ITEM) {
			Call call = new Call(OECALL);
			call.setParameterValue(PAR_EMAIL, sub.getAccount());
			call.setParameterValue(PAR_SUBSCRIPTION_ID, nfcn.getSubscriptionId());
			call.setParameterValue(PAR_EVENT_TYPE, se.getEventType().toString());
			call.setParameterValue(PAR_TIME_STAMP, se.getTimeStamp());
			call.setParameterValue(PAR_WATERMARK, se.getWatermark());
			call.setParameterValue(PAR_ITEM_ID, se.getAffectedItem().getId());
			call.setParameterValue(PAR_CHANGE_KEY, se.getAffectedItem().getChangeKey());
			Appointment appt = se.getAppointment();
			if (appt != null) {
				call.setParameterValue(PAR_SUBJECT, appt.getDescription());
				call.setParameterValue(PAR_LOCATION, appt.getLocation());
				call.setParameterValue(PAR_NOTES, appt.getNotes());
				call.setParameterValue(PAR_START_TIME, appt.getStartDate());
				call.setParameterValue(PAR_END_TIME, appt.getEndDate());
				call.setParameterValue(PAR_REMINDER_MINUTES, appt.getReminderMinutes());
				call.setParameterValue(PAR_REMINDER_SET, appt.isReminderSet());
			}
			call.executeCall(sub.getOpenedgeURL());
			if (call.isReturnError()) {
				System.out.println(...);
			}
			else if (call.getFailure() != null) {
				System.out.println(...);
			}
		}
	}
}
</pre>
<p>This method simply instantiates a Call object (line 72), sets the parameters for the call to ewscall.p (lines 73 through 89), and executes the call using the OpenEdge URL (line 90). A successful call to ewscall.p will result in the data being written to the OpenEdge database.&nbsp;If you read my article on the <a href="http://www.thesoftwaregorilla.com/2009/11/openedge-dynamic-openclient-java-example/" target="_blank"><em>OpenEdge Dynamic OpenClient Java Example</em></a>, this code will be reasonably familiar.</p>
<p>At this point, all that is left to do is update the subscription and respond to the Exchange Server.</p>
<h4>Updating the Subscription</h4>
<p>From the information in David Sterling, et al&#39;s book, <em>Inside Microsoft Exchange Server 2007 Web Services</em>, I had understood that the Watermark was associated with a subscription, but in working with this a little more, I have come to the conclusion that the Watermark is associated with the Exchange Server as a whole. I had been hoping to use the Watermark to guarantee that I knew that I had received all events from the Exchange Server, but it does not seem to work the way I had understood. I&#39;m still piecing this together at the time of writing, so the code that is included with the example is predicated on my understanding from Sterling, et al.</p>
<p>Lines 107 through 127 deal with updating the subscription:</p>
<pre>			if (!sub.getWatermark().equals(nfcn.getPreviousWatermark())) {
				System.out.println(...);
			}

			String lastWm = null;
			for (SubscriptionEvent se : nfcn) {
				lastWm = se.getWatermark();
			}

			if (lastWm != null) {
				sub.setWatermark(lastWm);
			}
		}
		else {
			canceled = true;
			System.out.println(...);
		}
	}</pre>
<p>In line 107 through 109, we check to see if the subscription&#39;s current watermark matches the previous watermark of the notification we received. If it doesn&#39;t (and if my understanding of Sterling, et al is correct, it should), we print a warning message to the log.</p>
<p>In lines 112 through 116, we loop through all the <em>SubscriptionEvent</em> objects and store the last watermark and line 117 through 119, we set the Watermark on the <em>Subscription</em> to the Watermark of the last <em>SubscriptionEvent</em>.&nbsp;</p>
<p>In lines 121 through 125 we deal with the situation where we were unable to match the notification with a subscription &#8211; we set the canceled flag to true so that the subscription will terminate.</p>
<h4>Responding to Exchange (2.2 in the diagram above)</h4>
<p>In lines 138 through 170 we deal with responding to Exchange:</p>
<pre>	if (!safeDoc) {
		response.sendError(HttpServletResponse.SC_BAD_REQUEST);
	}
	else {
		String str;

		if (canceled) {
			str = getResponseXML(UNSUBSCRIBE);
		}
		else {
			str = getResponseXML(OK);
		}

		response.setCharacterEncoding(&quot;UTF-8&quot;);
		response.setStatus(HttpServletResponse.SC_OK);
		response.setContentType(&quot;text/xml; charset=UTF-8&quot;);
		response.setContentLength(str.length());

		PrintWriter w = response.getWriter();
		w.print(str);
		w.flush();
	}
	response.flushBuffer();
	endTime = GregorianCalendar.getInstance().getTimeInMillis();
	System.out.println(String.format(&quot;Total execution time: %1$s&quot;, (Long) (endTime - startTime)));
}</pre>
<p>In lines 139 through 141, we handle the situation where we received a bad message from Exchange &#8211; we return an HTTP 400 error.</p>
<p>In lines 143 through 151 we check to see if the canceled flag is set. If it is, we build an Unsubscribe response, otherwise we just build an OK acknowledgement response. Lines 154 through 157 set some properties of the response object and lines 160 through 167 write the response to the output stream and flush the buffers.&nbsp;</p>
<p>Finally, in lines 168 and 169 we determine the amount of time that has elapsed since the notification was received and we display this.</p>
<h3>Summary</h3>
<p>That&#39;s it. Now you have seen how to handle the&nbsp;Exchange Web Services&nbsp;Push Subscription API in Java. Although this may seem like a convoluted piece of code, I have found it to be extraordinarily stable. I have experimented with taking down the Glassfish server and bringing it back up and the notifications keep on coming.</p>
<p>I have left the server running for days with no updates happening on the Exchange Server with no problem at all. I have also exposed it to some pretty serious stress and it has performed with no problems at all on fairly small hardware and software configurations.</p>
<p>Most of the time, notifications, including the call back to Exchange Server and OpenEdge, are being processed in around 100ms. StatusEvents take just over 200ms.</p>
<p>The one thing I am still having trouble with is trying to decide on an appropriate StatusFrequency. I would like to set the StatusFrequency to 10 minutes. That way, it will take 30 minutes for a subscription to expire. The problem is that clean cancellations rely on regular status events, so anything longer than 3 minutes can seem like an eternity. One thing I have definitely concluded is that the StatusFrequency needs to be configurable.</p>
<p>I hope this article has been useful. The next article in this series is going to deal with determining availability, but I am going to be engaged in some pretty serious development work for the next few weeks so it is likely that the next article in this series will only see the light of day toward the end of the Summer.&nbsp;</p>
<p>Let me have your feedback&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.thesoftwaregorilla.com/2010/07/exchange-web-services-example-part-4-subscriptions-and-notifications/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>OpenEdge GUI for .NET &#8211; Testing the Bridge</title>
		<link>http://www.thesoftwaregorilla.com/2010/06/openedge-gui-for-net-testing-the-bridge/</link>
		<comments>http://www.thesoftwaregorilla.com/2010/06/openedge-gui-for-net-testing-the-bridge/#comments</comments>
		<pubDate>Wed, 23 Jun 2010 03:25:12 +0000</pubDate>
		<dc:creator>Bruce Gruenbaum</dc:creator>
				<category><![CDATA[.NET]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[Exchange Web Services]]></category>
		<category><![CDATA[OpenEdge]]></category>
		<category><![CDATA[OpenEdge GUI for .NET]]></category>
		<category><![CDATA[4GL]]></category>
		<category><![CDATA[ABL]]></category>
		<category><![CDATA[EWS]]></category>
		<category><![CDATA[Exchange EWS]]></category>
		<category><![CDATA[Progress]]></category>

		<guid isPermaLink="false">http://www.thesoftwaregorilla.com/?p=922</guid>
		<description><![CDATA[Progress Software Corporation has been running a program to test the OpenEdge GUI for .NET bridge with controls that have no user-interface. My company, Intangere, is very interested in this program because we are looking at releasing a control that would make it possible for OpenEdge developers to communicate directly with a Microsoft Exchange Server and receive mailbox updates for an OpenEdge client. 

Intangere thus signed up for the program and created a control that mimics the behavior of a real Exchange Web Services control. This test has been surprisingly successful, and this article provides some information about the test and the code and a document that describes its use.]]></description>
			<content:encoded><![CDATA[<p>With the Exchange Web Services project, I have found a lot of .NET example code that makes connecting to Exchange&nbsp;from .NET&nbsp;a breeze. It&#39;s orders of magnitude easier than from Java, and that&#39;s to be expected &#8211; Microsoft&#39;s primary focus is on Microsoft. I do, however, want to make it as easy as possible for people to integrate Exchange into their applications, and from a UI point of view, the easiest way to do this is through .NET.</p>
<p>Providing a .NET control that facilitates this as Open Source is definitely on the to-do list for the Exchange Web Services project, but I was not sure how I would do this for OpenEdge as non-UI controls are not supported and most of the work that the control does is communication with the Exchange Web Service in a multi-threaded configuration on background threads.&nbsp;</p>
<p>On <a href="http://communities.progress.com/pcom/thread/26605?tstart=0" target="_blank">May 5th, Progress temporarily raised the restriction</a>&nbsp;to test the OpenEdge GUI for .NET bridge with controls that have no user-interface&nbsp;so that users can experiment with the controls and determine what types of controls should be supported.&nbsp;My company, Intangere, thus signed up for the program and created a control that mimics the behavior of a proposed Exchange Web Services control. This test has been surprisingly successful, and this article provides some information about the test and&nbsp;<a href="http://www.thesoftwaregorilla.com/wp-content/uploads/2010/06/NonUIControlTests.zip" target="_blank">the code and a document that describes its use</a>.</p>
<h3>Architecture</h3>
<p>Progress would have had logistical issues incorporating an Exchange Web Server into their nightly build simply to test the background functionality that this control will provide. To test the functionality, therefore, I needed to provide Progress with an example that replicates the functionality that I expect to use in the Exchange Web Services architecture.</p>
<p><a href="http://www.thesoftwaregorilla.com/wp-content/uploads/2010/06/EWSEmulator.jpg" target="_blank"><img align="left" alt="EWSEmulator conceptual architecture" height="255" src="http://www.thesoftwaregorilla.com/wp-content/uploads/2010/06/EWSEmulator.jpg" width="450" /></a> The model at left graphically demonstrates how the architecture of <a href="http://www.thesoftwaregorilla.com/wp-content/uploads/2010/06/NonUIControlTests.zip">the code</a> works. The model closely mimics the <a href="http://www.thesoftwaregorilla.com/wp-content/uploads/2010/04/Exchange-Web-Service-Subscriptions.jpg" target="_blank">model for the Subscription and Notification API</a> that &nbsp;I discussed in the <a href="http://www.thesoftwaregorilla.com/2010/04/exchange-web-services-subscriptions-and-notifications/" target="_blank">Exchange Web Services &#8211; Subscriptions and Notifications</a> post that I wrote back on April 26th.</p>
<p>Starting from the right of the diagram, Listener.exe is intended to emulate the functions that are performed by the Microsoft Exchange Web Services API. Specifically, it supports registering subscriptions and notifying the client of &quot;updates&quot; on a background thread. All communication between the client and Listener.exe is by means of .NET objects serialized as XML.&nbsp;</p>
<p>The EWSEmulator Control is a .NET control that has no user interface and performs the function <a href="http://www.thesoftwaregorilla.com/wp-content/uploads/2010/04/Exchange-Web-Service-Subscriptions.jpg" target="_blank">that the Java EWS WebService and the Java HTTP Servlet perform</a>. As such, it is derived from System.ComponentModel.Component and it handles the job of communicating with Listener.exe, both in terms of posting subscription requests, and in terms of receiving notifications. The control receives the notifications on a background thread (the Notification Processor) which deserializes the objects and hands them to the event generator which raises a .NET event <em>on the UI thread.</em></p>
<p>Receiving the event on the UI thread is one of the most critical components of this control. OpenEdge is single-threaded which means that if an event is raised on anything but the UI thread, it is likely to cause problems for OpenEdge. So the Event Generator&#39;s sole function is to marshal all notifications and generate a .NET event on the UI thread.</p>
<p>The User Interface Logic is responsible for registering subscriptions via the embedded EWSEmulator Control, shutting down the Listener service and reacting to events raised by the Event Generator.</p>
<h3>So how did the test go?</h3>
<p><img align="left" alt="EWSEmulator User Interface" height="421" src="http://www.thesoftwaregorilla.com/wp-content/uploads/2010/06/EWSEmulatorUI.jpg" width="519" />I&#39;m not going to spend a lot of time telling you about the gory details of the code. You can&nbsp;<a href="http://www.thesoftwaregorilla.com/wp-content/uploads/2010/06/NonUIControlTests.zip">download the code and a document</a>&nbsp;that describes a lot more about my experience and findings, and you can even run it and look at both the ABL and .NET code. The document is a PDF contained in the zip file.&nbsp;</p>
<p>In a nutshell, though, I was able to duplicate the behavior I was looking for, marshal the events onto the UI thread, and get OpenEdge to react to those events without too much trouble. Section 4.2.2 of the document provides a description of how the marshaling works.</p>
<p>More importantly, I was also able to establish that the volume of data that can be handled by the OpenEdge ABL in this kind of communication was significantly greater than I had thought. I was able to generate upwards of 400 messages per second from Listener.exe and the client handled them without losing any.</p>
<p>Of course, no one in their right mind would expect the client to handle that volume of traffic, especially considering that every event had to be marshaled to the UI thread. But it was encouraging to learn that it was possible.</p>
<h3>My Conclusion</h3>
<p>What this test has proven is that the OpenEdge GUI for .NET is more than capable of supporting background controls, provided they conform to the rule that all threads need to be marshaled to the UI thread before an event is raised.</p>
<p>Also, I made extensive use of the XmlSerializer, HttpListener, HttpWebResponse, and several other streaming classes and had no problem at all with OpenEdge.&nbsp;</p>
<p>I have submitted this code and these findings to Progress Software and hopefully they will be able to lift the restriction on use of non-UI controls with OpenEdge GUI for .NET.</p>
<p>Feel free to download the example code and documentation and mess with it, and please let me have your feedback.</p>
<p>Also note that <a href="http://communities.progress.com/pcom/thread/29062?tstart=0" target="_blank">the program with Progress to test the bridge</a> for non-UI controls is open until July 31st, so if you can <a href="mailto:TestingTheBridge@progress.com?subject=I'd%20like%20to%20test%20the%20bridge!">join the program</a> and contribute, please do.&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.thesoftwaregorilla.com/2010/06/openedge-gui-for-net-testing-the-bridge/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Tools of the Trade &#8211; Infrastructure and Process</title>
		<link>http://www.thesoftwaregorilla.com/2010/05/tools-of-the-trade-infrastructure-and-process/</link>
		<comments>http://www.thesoftwaregorilla.com/2010/05/tools-of-the-trade-infrastructure-and-process/#comments</comments>
		<pubDate>Mon, 10 May 2010 02:17:19 +0000</pubDate>
		<dc:creator>Bruce Gruenbaum</dc:creator>
				<category><![CDATA[Commentary]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[Infrastructure]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[OpenEdge]]></category>
		<category><![CDATA[Exchange EWS]]></category>
		<category><![CDATA[Microsoft Exchange 2007]]></category>
		<category><![CDATA[Microsoft Windows]]></category>
		<category><![CDATA[Progress]]></category>
		<category><![CDATA[VMWare]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[Windows 7]]></category>
		<category><![CDATA[Windows 7 64-bit]]></category>

		<guid isPermaLink="false">http://www.thesoftwaregorilla.com/?p=598</guid>
		<description><![CDATA[This post is a follow-up to my last post in which I spoke about the software that I use to build software. In this post, I want to talk about some of the hardware and operating system infrastructure that I have in place and the roll it performs.

As I said in my last post, I do not like to do work that could be automated. A large part of the work that should be automated is the work around the build process. More than anything else, successful software development depends on being able to produce a repeatable build process where the code that is built is thoroughly tested, installed and verified before it is considered stable. To get to the point of understanding how this all works, the hardware and network infrastructure is pretty important. So that's where I am going to start. [...]]]></description>
			<content:encoded><![CDATA[<p>This post is a follow-up to my last <a href="http://www.thesoftwaregorilla.com/2010/05/tools-of-the-trade-what-i-use-for-my-development-work/">post</a>&nbsp;in which I spoke about the software that I use to build software. In this post, I want to talk about some of the hardware and operating system infrastructure that I have in place and the roll it performs.</p>
<p>As I said in my last post, I do not like to do work that could be automated. A large part of the work that should be automated is the work around the build process. More than anything else, successful software development depends on being able to produce a repeatable build process where the code that is built is thoroughly tested, installed and verified before it is considered stable. To get to the point of understanding how this all works, the hardware and network infrastructure is pretty important. So that&#39;s where I am going to start.</p>
<h3>Hardware</h3>
<p><a href="http://www.thesoftwaregorilla.com/wp-content/uploads/2010/05/Network.jpg" target="_blank"><img align="left" alt="Network configuration" height="398" src="http://www.thesoftwaregorilla.com/wp-content/uploads/2010/05/Network.jpg" width="400" /></a>The diagram at left shows the hardware configuration that I have in place. I have a wireless hardware router that is connected to my DSL line to the internet. Connected to that router are several Linksys hubs (there&#39;s only one shown in the diagram because it is the only one that bears upon the discussion), and a Netgear 10/100/1000 switch. Why do I have all of these?</p>
<p>Well I have several machines that are unrelated to my work that are connected to the network. All of our entertainment devices (DIRECTV set-top boxes, Playstation, Wii, and Blue-Ray players), my kids&#39; notebooks and desktop PCs, and my wife&#39;s notebook are all connected to the network, some via wires and others wirelessly.</p>
<p>The Dell Inspiron Laptop and its companion Dell Dimension Desktop are two older machines (they&#39;re both over 5 years old) that I use for all kinds of testing and experimentation. I&#39;m not afraid to replace and reinstall operating systems and software on these machines all the time. The Inspiron Laptop functions as my Linux server when I am presenting and I need a server away from home.</p>
<p>My HP Pavilion notebook is my workhorse workstation. All my blogging, development work, specs, and photography stuff (yes, I enjoy amateur photography in my copious spare time) all happen on my notebook.</p>
<p>One of the most useful and valuable purchase I ever made was the purchase of a <a href="http://www.lacie.com/us/products/product.htm?pid=11471">LaCie 2Big Network</a> drive. It is a network file store that hangs off the Netgear switch at 1Gb. This device contains all our backups, and acts as a file server for all the other devices on the network. I have a few 1TB external drives and once a week I plug in an external drive and back up the LaCie drive and store it off-site. All the other equipment backs up on to this device so we can always go back to it to restore.</p>
<p>The Dell PowerEdge T710 is a relatively new addition to the network. As I mentioned in my previous post, it hosts the virtual machines that I need to get most of my work done. It has 4 physical network cards that are all connected to the Netgear switch.&nbsp;</p>
<p>When I first got the PowerEdge I was worried about how much power it would consume. Surprisingly, it is more cost-efficient than the Dell Dimension Desktop, which is why I was able to free up the Desktop. Until fairly recently, the Desktop performed the role of DNS, DHCP server, NIS server and DMZ. That has now changed. The DMZ has now moved onto the PowerEdge and runs in a separate VM. The DNS, DHCP and NIS server now also run in a separate VM and the firewalls are set up appropriately. My network is also segmented so that the personal network and the work network are completely separate.</p>
<h3>Virtualization</h3>
<p>I have been looking at virtualization for quite a while. When I worked for my last employer, we had a couple of Sun servers that were running OpenEdge AppServers, databases and other software that I was using to diagnose issues with their OpenEdge to Java configuration. I was surprised at how the system coped with the load that I threw at it, even though it was running off the same processors.</p>
<p>When I started work on the Microsoft Exchange Integration project, I knew I was going to need at least 2 Windows 2008 Servers and a couple of Linux boxes. I spent some time with a colleague of mine and discussed the issue and he suggested looking into VMWare again. I had used VMWare Fusion on my Apple Powerbook at my previous employer, but had not really understood how powerful this product really is.</p>
<p>My colleague told me about VMWare ESXi, which is a free version of VMWare that will run on a single machine and allow you to administer that machine as an isolated server. We spec&#39;ed out what we thought I would need for the work I am doing and I ordered the PowerEdge accordingly. When it arrived, I installed VMWare ESXi on it and it has been set up and running now for almost 2 months with no down time.</p>
<p>What has stunned me is how efficiently this machine runs. At any point in time, I have at least 8 machines (3 of which are Windows Servers) running on this machine. The thing that I completely missed out on is how much time the machine spends doing nothing so that there is a lot of overlapping processor downtime that can be exploited. You can easily over-provision this machine and still have it perform very well.&nbsp;</p>
<p>I&#39;m going to need to spend some money on another 16GB of memory and another 2 TB of disk space, but that is a very simple upgrade for this box.&nbsp;</p>
<h3>The Big Score with Virtualization</h3>
<p>The thing that I had not considered about virtualization that has been the biggest score is the fact that everything around the virtual machine management can be scripted. Not only that, it can be scripted from a Linux virtual machine running on the hardware that is running the virtual machine!</p>
<p>Now the reason this is really cool is because of the automation of the build process, and this is where the whole infrastructure discussion culminates.</p>
<p>As a software engineer, I live by the following rule:</p>
<blockquote>
<p>&nbsp;The software does not work until it is packaged, installed, and run on a(set of) machine(s) that is(are) not your development environment.</p>
</blockquote>
<p>In other words, the fact that the code works on my notebook does not mean that it works in production. To prove that it does, I have to check it in, have an automated build kick off and build the code, have the automated unit tests run, deploy the code to the test environment, have it run there, and have the tests all pass. Finally, and this is really important for the Microsoft Exchange Integration project, I need to understand how all of this is likely to perform under load.</p>
<h4>Building and Installing</h4>
<p>With virtualization, I am able to create snapshots of machines, power them up by means of a script, install new software by means of a script, extract the results, power down the machines, revert back to the snapshot, and start again whenever I want to.</p>
<p>By means of one script, I am able to validate how successfully the build ran, and as it is a cron job that runs off my development server on a nightly basis, I have a good build and test report first thing every morning when I get up.</p>
<h4>Stress Testing</h4>
<p>Moreover, the test is able to use my quiet time to run any stress tests that need to be run. Assuming the build is successful (95% of tests pass, and none of the critical tests fail), the build is stored to the LaCie drive. At 8:00am every morning, the stress test kicks off because this is when the network is likely to be the quietest and the others in the house will not be affected. The script powers down any machines that are not needed during the stress test, starts the stress test machines, runs the stress tests during which time the processors are pegged at 95%, and keeps a log of how the stress tests ran.</p>
<p>At the end of the test (at 4:00pm) it e-mails a report for the tests and reverts to the snapshots for each of the virtual machines after it powers them down. Before it reverts to the snapshot, it copies the virtual machines across to the LaCie drive so that I can always restore the tests and see what the logs looked like if there is any question.</p>
<p>The cool thing about the test ending at 4:00pm is that I normally get the e-mail for stress test on my Blackberry as I am driving out of the parking garage at work, so I know what I am in for when I get home that evening.</p>
<h3>The Value of Laziness</h3>
<p>The real value of being lazy is that because I won&#39;t do repetitive things manually, I have put together a fairly stable build configuration that allows me to prove the code thoroughly before it ever makes it out the door. There are several important things that this process relies on, though.</p>
<p>I have to be ruthless about building tests for each of the new features that I build. This does not mean I believe in Test Driven Development &#8211; I don&#39;t. Test Driven Development is about building tests and then coding for the tests. I believe in building the code and then figuring out how to break it, and trust me, I am good at breaking my own code.</p>
<p>I have to make sure I add additional load to stress test additional features where I can. This can be really tedious because I spend a lot of time writing code that generates data.</p>
<p>Finally, I couldn&#39;t do this without software like Hudson CI, JUnit, NUnit, Apache Ant, NAnt, Apache Maven and most of all, VMWare.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.thesoftwaregorilla.com/2010/05/tools-of-the-trade-infrastructure-and-process/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<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>
	</channel>
</rss>
