<?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; OpenEdge OpenClient</title>
	<atom:link href="http://www.thesoftwaregorilla.com/tag/openedge-openclient/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.thesoftwaregorilla.com</link>
	<description>The Software Gorilla</description>
	<lastBuildDate>Wed, 20 Oct 2010 19:56:09 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.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[n-tier Development]]></category>
		<category><![CDATA[OpenClient]]></category>
		<category><![CDATA[OpenEdge]]></category>
		<category><![CDATA[OpenEdge AppServer]]></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>11</slash:comments>
		</item>
		<item>
		<title>Exchange Web Services &#8211; Subscriptions and Notifications</title>
		<link>http://www.thesoftwaregorilla.com/2010/04/exchange-web-services-subscriptions-and-notifications/</link>
		<comments>http://www.thesoftwaregorilla.com/2010/04/exchange-web-services-subscriptions-and-notifications/#comments</comments>
		<pubDate>Tue, 27 Apr 2010 01:15:00 +0000</pubDate>
		<dc:creator>Bruce Gruenbaum</dc:creator>
				<category><![CDATA[Commentary]]></category>
		<category><![CDATA[Exchange Web Services]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[n-tier Development]]></category>
		<category><![CDATA[OpenClient]]></category>
		<category><![CDATA[OpenEdge]]></category>
		<category><![CDATA[OpenEdge AppServer]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[.NET]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[Dynamic OpenClient]]></category>
		<category><![CDATA[EWS]]></category>
		<category><![CDATA[Exchange EWS]]></category>
		<category><![CDATA[Java 5]]></category>
		<category><![CDATA[Java OpenClient]]></category>
		<category><![CDATA[Microsoft Exchange 2007]]></category>
		<category><![CDATA[OpenEdge OpenClient]]></category>
		<category><![CDATA[Progress OpenClient]]></category>

		<guid isPermaLink="false">http://www.thesoftwaregorilla.com/?p=301</guid>
		<description><![CDATA[It's been a really busy week since I posted my first post on Exchange Web Services. I have learned a lot in that short period of time that I want to share with you. Whether you are an OpenEdge, Java or .NET developer, I think this post is going to have some information for all of you.

In my first post, I told you about the background story - I need to enable an OpenEdge CRM application to create, modify and delete calendar and task items in Microsoft Exchange. I also need Exchange to let me know any time a calendar or task item is changed so that I can update the OpenEdge database accordingly. Simple use cases.

When I left off last week, my next step was to get Exchange subscriptions working, and, boy, what a trip that has been.
]]></description>
			<content:encoded><![CDATA[<p>It&#39;s been a really busy week since I posted <a href="http://www.thesoftwaregorilla.com/2010/04/exchange-web-services-starting-out/">my first post on Exchange Web Services (EWS)</a>. I have learned a lot in that short period of time that I want to share with you. Whether you are an OpenEdge, Java or .NET developer, I think this post is going to have some information for all of you.</p>
<p>In <a href="http://www.thesoftwaregorilla.com/2010/04/exchange-web-services-starting-out/">my first post</a>, I told you about the background story &#8211; I need to enable an OpenEdge CRM application to create, modify and delete calendar and task items in Microsoft Exchange. I also need Exchange to let me know any time a calendar or task item is changed so that I can update the OpenEdge database accordingly. Simple use cases.</p>
<p>When I left off last week, my next step was to get Exchange subscriptions working, and, boy, what a trip that has been.</p>
<h3>Types of Subscriptions</h3>
<p>There are two types of subscription models available with Exchange:</p>
<ol>
<li><strong>Pull Subscriptions</strong> &#8211; You set up a subscription with Exchange and it gives you an indicator (called a Watermark) that keeps track of what events you have already received. You pole the server at an interval and it gives you a response with all events that have happened since that last Watermark.</li>
<li><strong>Push Subscriptions</strong> &#8211; You set up a subscription with Exchange and tell it to call you back on another WebService every time something happens. The WebService has to acknowledge receipt of the message in order to keep the subscription alive. The message you get contains a list of items that have been affected since the last time a message was acknowledged.</li>
</ol>
<p>I&#39;m not interested in polling the server so I have chosen to go with a Push Subscription. Much of the information that follows applies to both subscriptions, but the Push Subscription differs in that you do not poll the server for the changes.&nbsp;</p>
<h3><a name="settingupsub"></a>Setting up a Push Subscription</h3>
<p>To set up a Push Subscription, you send a SOAP message to EWS, authenticating against the Active Directory as you would with any other message to EWS. The message contains a list of folders that you want to know about, the list of event types that you want to be notified of, the URL that you should be called back on, and a StatusFrequency node that tells it how often to poll the URL &#8211; more about this later.&nbsp;</p>
<p>You can subscribe to virtually any update on virtually any folder in a mailbox, including the calendar, tasks, contacts, and mail folders.</p>
<p><a href="http://www.thesoftwaregorilla.com/wp-content/uploads/2010/04/Exchange-Web-Service-Subscriptions.jpg" target="_blank"><img align="left" alt="Exchange Web Service Push Subscription" height="275" src="http://www.thesoftwaregorilla.com/wp-content/uploads/2010/04/Exchange-Web-Service-Subscriptions.jpg" width="400" /></a></p>
<p>The diagram to the left shows the architecture that I finally ended up with. It is a little more complicated than it would be if OpenEdge were not in the picture, but it serves to explain the process, nonetheless. The numbers in parenthesis below refer to the numbers in the diagram.</p>
<p>OpenEdge initiates the subscription by sending a message (1.1) to a Java WebService that is deployed on a Glassfish Application Server. The WebService forwards that subscription request (1.2) on to the IIS server that is running on the Windows 2008 Server that hosts the Microsoft Exchange Client Access Server (CAS). IIS passes the message onto the EWS API which registers the subscription with the CAS. CAS responds with a message that contains the Subscription ID and the Watermark which is tied to the last event that was sent.</p>
<p>As I mentioned, the message that is sent to the EWS API (1.2) contains the URL for the service that is to receive all event notifications. It also contains the StatusFrequency node.</p>
<p>I set up a servlet on my Glassfish Application Server to receive and respond to the notifications. The following is a sample subscription request message:</p>
<p><a name="subscriptionrequest"></a></p>
<pre>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
    &lt;soap:Envelope xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
           xmlns:xsd=&quot;http://www.w3.org/2001/XMLSchema&quot;
           xmlns:soap=&quot;http://schemas.xmlsoap.org/soap/envelope/&quot;
           xmlns=&quot;http://schemas.microsoft.com/exchange/services/2006/messages&quot;
           xmlns:t=&quot;http://schemas.microsoft.com/exchange/services/2006/types&quot;&gt;
        &lt;soap:Body&gt;
            &lt;Subscribe xmlns=&quot;http://schemas.microsoft.com/exchange/services/2006/messages&quot;
                    xmlns:t=&quot;http://schemas.microsoft.com/exchange/services/2006/types&quot;&gt;
                &lt;PushSubscriptionRequest&gt;
                    &lt;t:FolderIds&gt;
                        &lt;t:DistinguishedFolderId Id=&quot;calendar&quot;/&gt;
                    &lt;/t:FolderIds&gt;
                    &lt;t:EventTypes&gt;
                         &lt;t:EventType&gt;CreatedEvent&lt;/t:EventType&gt;
                         &lt;t:EventType&gt;ModifiedEvent&lt;/t:EventType&gt;
                         &lt;t:EventType&gt;DeletedEvent&lt;/t:EventType&gt;
                    &lt;/t:EventTypes&gt;
                    &lt;t:StatusFrequency&gt;3&lt;/t:StatusFrequency&gt;
                    &lt;t:URL&gt;http://server.example.com/ewsCalTest/NotificationService&lt;/t:URL&gt;
                &lt;/PushSubscriptionRequest&gt;
            &lt;/Subscribe&gt;
        &lt;/soap:Body&gt;
    &lt;/soap:Envelope&gt;</pre>
<h3><a name="statusfrequency"></a>The StatusFrequency Node</h3>
<p>The StatusFrequency Node tells EWS to send a message every x minutes to verify that the subscription is still active. This serves three purposes:</p>
<ol>
<li>It allows Exchange to figure out if the server that is listening is still around and cancel any dead subscriptions if necessary;</li>
<li>It allows the server that is being called to determine that it is still getting messages from the Exchange Server; and</li>
<li>It allows the server that is being called to unsubscribe from any future messages.</li>
</ol>
<p>Exchange needs to carefully manage resources, so canceling any unnecessary subscriptions is an important way to do that. To maintain a subscription, the server that is receiving the notifications has to respond with a SOAP message that includes a SubscriptionStatus node with the value &quot;OK&quot;.&nbsp;</p>
<p>The expected response is as follows:</p>
<pre>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;soap:Envelope xmlns:soap=&quot;http://schemas.xmlsoap.org/soap/envelope&quot;&gt;
  &lt;soap:Body&gt;
    &lt;SendNotificationResult xmlns=&quot;http://schemas.microsoft.com/exchange/services/2006/messages&quot;&gt;
      &lt;SubscriptionStatus&gt;OK&lt;/SubscriptionStatus&gt;
    &lt;/SendNotificationResult&gt;
  &lt;/soap:Body&gt;
&lt;/soap:Envelope&gt;
</pre>
<p>If a subscriber does not respond appropriately, EWS goes into retry mode. In other words, if EWS does not get a SOAP message formatted exactly the way it expects, it assumes the server has not received the message. It waits for 30 seconds and tries again. If it still receives no response, it waits for a minute and tries again. EWS will continue retrying, doubling the wait time, until the wait time exceeds the StatusFrequency. So in my case, when I first set this up, I set the StatusFrequency to 3 minutes. It tried to send the notification and waited 30, then 60, then 120 seconds and canceled the subscription because, if it had gone to 240 seconds, it would have exceeded the 3 minutes set as the StatusFrequency.&nbsp;</p>
<p>Less obvious, though, is the reason behind purposes 2 and 3.&nbsp;Remember that Exchange is only going to send messages when something changes. Well, some users have very quiet mail boxes. They may be out of the office, or it may simply be after hours where nothing is going on. If Exchange does not let the other server know that it is still sending messages, how is that server to know that the subscription is still valid?</p>
<p>The other thing that is important is that there is no way to send a message to Exchange to tell it to cancel a Push Subscription. The only way to cancel the subscription is by responding to a status message with the SubscriptionStatus set to &quot;Unsubscribe&quot;.</p>
<h3>Receiving Notifications</h3>
<p>Now that you understand how to subscribe, the next step is to start receiving messages from EWS. We&#39;ll pick up where we left off in the diagram above.</p>
<p>EWS starts off by sending a notification (2.1) to the server. The notification could be one of two things:</p>
<ol>
<li>A notification of events that have taken place on the server; or</li>
<li>A status message.</li>
</ol>
<p>If this is a status message, all you need to do is respond with the OK response (2.2) I discussed above. In fact, you need to respond with the OK message if you want to carry on receiving notifications no matter what, but responding with an OK has an additional effect if you receive a notification of events.</p>
<p>First let&#39;s understand a notification of events. An event notification message contains a list of events that have taken place against the items that you are monitoring. It does not contain the details of what changed &#8211; just the fact that something did. If you want to know what changed, you have to call the GetItem method of the EWS to get the item and look at its changes. That&#39;s what we do in step 2.3.</p>
<p>Each notification includes a PreviousWatermark for the whole message and a Watermark for each event. By keeping track of the watermarks, it is possible to restart the subscription from the last event that was processed. When you acknowledge the event notification with an OK, Exchange moves the subscription to the next watermark automatically.</p>
<p>Once I have processed the event notification and received the event, I am making a Dynamic OpenClient call (2.4) to the OpenEdge AppServer to notify the AppServer of the change. The ABL code on the AppServer can then do what it needs to do to respond to the event.</p>
<h3>Performance</h3>
<p>Once I got this working, I decided to test it out to see what the performance looked like. Now bear in mind, all of the machines shown in the diagram are different virtual machines on one Dell Poweredge T710 running VMWare 4.0 ESXi. The T710 has 2&#215;4 core processors and 16GB of memory. It has 4 network cards connected to a 1GB switch. The Windows Server is on a separate network card to the other three machines, which are all Centos 64-bit boxes.&nbsp;</p>
<p>The round trip for establishing the subscription takes an average of 82ms.</p>
<p>I then ran a test harness that generates 100,000 calendar items across 80 mailboxes. It took a little over 2 hours for the test harness to complete. I tracked performance for each of the following pieces:</p>
<ul>
<li>The OpenEdge client takes an average of 100 milliseconds to make the call through the Java WebService to EWS to create the appointment.</li>
<li>From the time I receive the acknowledgement for the appointment creation to the time that the notification has been received (2.1) and acknowledged (2.2) and the details of the notification have been received (2.3) takes an average of 150 milliseconds.</li>
<li>The call across the Dynamic OpenClient to the AppServer, which is a call to a procedure that has no temp-tables or XML documents, averages 80 milliseconds.</li>
</ul>
<p>All told, this means that from the time the request for the appointment is initiated to the time the notification of its update is received by the AppServer takes an average of 330 milliseconds &#8211; less than half a second.</p>
<p>Now clearly, my environment has very little traffic and I have a lot of horsepower backing it, but it if this is properly tuned and the additional processing that is to be added is not excessive, a one second response time is well within the realms of possibility.</p>
<h3>Problems and Challenges</h3>
<p>In getting this to work, I ran into a couple of nasty obstacles.&nbsp;</p>
<p>First, Microsoft&#39;s API is really not a good example of a WebServices API. Neither Java nor OpenEdge will parse the WSDLs without manual editing and even if you manage to generate the code you need from the WSDL, it really doesn&#39;t work very well. I have been forced to manually code the interaction with the EWS API and that is a lot of work. Having said that, it performs very well and I get to write the XML exactly the way it needs to be for EWS to accept it.</p>
<p>That brings me to the second thing about this API. Microsoft&#39;s error reporting is shockingly bad for commercial software. Compared to the kind of error reporting I get from Tomcat, Glassfish, ActiveMQ, and several other Open Source solutions, it&#39;s incredible that commercial software can be this badly engineered. It is so hard to figure out what is wrong when something goes bad that you will spend hours on things like malformed or unrecognized XML. EWS simply returns a 500 http error code (Internal Server Error) with absolutely no indication of what caused it, and there is nothing in any of the logs to help you.</p>
<hr />
<p><strong>Update:</strong> Since I wrote this article, I have found (and Kris C. has also pointed this out in the comments) that making a call to the getErrorStream() method on the connection object will provide more detailed information on what failed, and you get an entire SOAP message back that describes details of the 500 error. I therefore eat my words about error reporting.</p>
<p>I still think that there is room for improvement because it is very hard to diagnose exactly which node is out of order if you submit a request where the XML nodes are not in the exact order specified by the EWS API, but it&#39;s nowhere near as bad as what my original post probably led you to believe.</p>
<p>There is more information in <a href="http://www.thesoftwaregorilla.com/2010/06/exchange-web-services-example-part-3-exchange-impersonation/">Part 3</a> of the series and there is even sample code that demonstrates exception retrieval.</p>
<hr />
<p>The third thing that really cost me a lot of time is that you cannot call a regular Java or OpenEdge WebService from EWS because it won&#39;t look at the WSDL. I was therefore forced to manually code the Java servlet that accepts an http post.</p>
<p>These problems effectively sidelined OpenEdge as a direct endpoint for EWS notifications. The architecture that I am building will definitely rely on all communication with EWS going via Java services.</p>
<h3>Where to next?</h3>
<p>Now that I have the prototype code working, my next step is to fix up some of the security issues I need to resolve. NTLMv2 authentication remains a real problem for Java in communicating with EWS, and I am looking for a solution around that. I also need to deal with some of the certificate related issues that I have.</p>
<p>I have also done some work on the Exchange Impersonation stuff, so my next blog post will probably talk about this.</p>
<p>Finally, I&#39;m planning a walkthrough of some example code that actually demonstrates the functionality, like I did with the <a href="http://www.thesoftwaregorilla.com/2009/11/openedge-dynamic-openclient-java-example/">Dynamic OpenClient code late last year</a>.&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.thesoftwaregorilla.com/2010/04/exchange-web-services-subscriptions-and-notifications/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>Exchange Web Services &#8211; Starting out</title>
		<link>http://www.thesoftwaregorilla.com/2010/04/exchange-web-services-starting-out/</link>
		<comments>http://www.thesoftwaregorilla.com/2010/04/exchange-web-services-starting-out/#comments</comments>
		<pubDate>Tue, 20 Apr 2010 12:28:25 +0000</pubDate>
		<dc:creator>Bruce Gruenbaum</dc:creator>
				<category><![CDATA[Commentary]]></category>
		<category><![CDATA[Exchange Web Services]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[n-tier Development]]></category>
		<category><![CDATA[OpenClient]]></category>
		<category><![CDATA[OpenEdge]]></category>
		<category><![CDATA[OpenEdge AppServer]]></category>
		<category><![CDATA[SOA]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[ABL]]></category>
		<category><![CDATA[Apache ActiveMQ]]></category>
		<category><![CDATA[Application Server]]></category>
		<category><![CDATA[AppServer]]></category>
		<category><![CDATA[EWS]]></category>
		<category><![CDATA[Exchange EWS]]></category>
		<category><![CDATA[Glassfish AppServer]]></category>
		<category><![CDATA[Java OpenClient]]></category>
		<category><![CDATA[Microsoft Exchange 2007]]></category>
		<category><![CDATA[OpenEdge OpenClient]]></category>
		<category><![CDATA[Progress AppServer]]></category>
		<category><![CDATA[Progress OpenClient]]></category>

		<guid isPermaLink="false">http://www.thesoftwaregorilla.com/?p=290</guid>
		<description><![CDATA[A couple of months back, a gentleman who has now become a friend and business partner, came to me and asked me if there was any way to get at all the calendar items in his sales organization's calendars with the intention of integrating it with his Progress OpenEdge CRM system. Jim is using Exchange 2007 for his e-mail and calendaring solutions.

I was aware that Microsoft had released a new API for Exchange in Exchange 2007 called Exchange Web Services (EWS), and so I said that I needed to do a little research on the API, but I was pretty sure that it was possible. Sure enough, MSDN has some documentation of the API and Microsoft is touting it as the replacement for all APIs that communicate with Exchange. Web Services - how hard can it be?
]]></description>
			<content:encoded><![CDATA[<p>Many people make use of different mechanisms for creating e-mail messages. Most of them use some kind of SMTP client to do so. For the more adventurous, there is MAPI that allows you access to a Microsoft Mail based client to do a few things over and above just plain mail. These are things that people do on a daily basis.</p>
<p>A couple of months back, a gentleman who has now become a friend and business partner, Jim Ford of <a href="http://www.fordav.com/" target="_blank">Ford Audio-Visual</a>, came to me and asked me if there was any way to get at all the calendar items in his sales organization&#39;s calendars with the intention of integrating it with his Progress OpenEdge CRM system. Jim is using Exchange 2007 for his e-mail and calendaring solutions.</p>
<p>I was aware that Microsoft had released a new API for Exchange in Exchange 2007 called Exchange Web Services (EWS), and so I said that I needed to do a little research on the API, but I was pretty sure that it was possible. Sure enough, MSDN has&nbsp;some documentation of the API and Microsoft is touting it as the replacement for all APIs that communicate with Exchange. Web Services &#8211; how hard can it be?</p>
<h3>The Production Environment</h3>
<p>In the&nbsp;production environment, an OpenEdge client running on a Unix server&nbsp;needs to initiate a WebService call to Exchange to create, update and delete appointments for sales people. These appointments will be created in the user interface for the CRM application.</p>
<p>Sales people then need to be able to&nbsp;change and&nbsp;delete these appointments from their Outlook clients or their Blackberry/iPhones. Any changes to the appointments need to be recorded as history updates in the CRM system, so Exchange needs to notify the OpenEdge CRM system of these changes.</p>
<p>So the updates to appointments need to happen from both directions and the communication is going to happen cross-platform (Unix to Windows and vice-versa).</p>
<h3>Initial Idea</h3>
<p>My initial plan was to create a prototype that did&nbsp;the two-way&nbsp;communication directly from OpenEdge to EWS. I just wanted to prove that it was possible. The first step was to build an OpenEdge WebServices Client that created a calendar item in the Exchange store. Unfortunately, for various reasons, this is much harder than I thought it would be.</p>
<p>First, the EWS WSDL is not a standard WSDL. EWS is installed as part of the Client Access Server (CAS)&nbsp;component of Exchange. In production, you can have multiple Mailbox Servers and the CAS has to discover which mailbox server services a client. As a result, the URL that is used to actually call EWS is different depending on which Mailbox Server hosts the mailbox. The WSDL therefore does not contain a &quot;service&quot; node and the OpenEdge WSDL parser cannot parse the WSDL. I tried working around that by creating a dummy WSDL with a service node inside it.</p>
<p>The second problem was&nbsp;that I ran into all kinds of problems with the complex type structure that exist in the WSDL. The types and messages live in a separate XSD file and the WSDL parser did not like some of the&nbsp;message definitions as they included optional types.</p>
<p>The last straw was that the connection to EWS is an SSL connection that, by default, wants to use NTLM authentication and I battled for hours to get this to work. I would probably have figured the problem out earlier if I had had some more clear indications from OpenEdge of exactly what the problem was, but I finally decided to go and try the connection from Java.</p>
<h3>The Bible</h3>
<p>Before I started on the Java example, I decided that I should probably test that I could even make this happen in .NET. Even that is no small feat. The examples that come with the Exchange SDK are about as useful as an udder on a bull, and I eventually decided to bite the bullet and look for a book on the subject.</p>
<p>I normally avoid Microsoft Press books because, more often than not, they simply regurgitate the standard Microsoft examples and contribute very little additional value. That is not the case with <a href="http://www.amazon.com/Inside-Microsoft-Exchange-Server-Services/dp/0735623929/ref=sr_1_1?ie=UTF8&amp;s=books&amp;qid=1271763925&amp;sr=8-1" target="_blank"><em>Inside Microsoft Exchange 2007 Web Services</em></a>. Authors David Sterling, Benjamin Spain, Michael Mainer, Mark Taylor, and Huw Upshall have done an outstanding job of creating a book that really gets to the heart of the technology. They are all members of the Exchange development team, and it shows. The book provides enough background information that it is essential reading for anyone trying to do this stuff.</p>
<h3>Java to EWS</h3>
<p>Armed with my new reference, I now set about building a Java client that could create an appointment in Exchange, figuring that I could expose the Java client as a Web Service that OpenEdge could consume.</p>
<p>Now, before you throw your toys out your crib and tell me that that is a very expensive way to do things from a performance point of view, let me just clarify that I would have ended up going down this road eventually, anyway. Ultimately, the solution will be exposed as a service on an ESB, and that ESB is likely to be based on a JMS MOM, like Apache ActiveMQ, or SonicMQ. So this is not a wasted exercise. The reason that I need this on an&nbsp;ESB is that the&nbsp;events that flow in each direction need to be available to other services, too.</p>
<p>Java to EWS was not a lot easier to get working. I still had to work around the WSDL not having a &quot;service&quot; node issue, but Java gives you finer-grained control of the parsing of the WSDL. Moreover, in my initial implementation, I did not use JAX-WS or any other WebServices technologies like Axis. I simply used an HTTP request to the WebService to get through the issues. This made life a whole lot easier.</p>
<p>The SSL problem reared its ugly head again, but this time I quickly learned 2 things:</p>
<ol>
<li>Exchange creates a default certificate for you when you install it. Either you have to install the certificate as a trusted certificate in the certificate store, or you have to set up a certificate authority on your Windows Server and build signed certificates. I went with the former idea and that resolved the first part of the SSL problem.</li>
<li>Exchange is installed to support only NTLM authentication, but NTLM authentication is a pain to work with from non-Microsoft technologies. You can change the settings of the EWS virtual directory on the Microsoft IIS Server that runs on the CAS to support basic and digest authentication. For testing,&nbsp;I set this up as basic, but I now have digest working. I will probably go back and revisit NTLM again later, but it is very hard to find any information to help with solving problems, so I may simply require digest authentication.</li>
</ol>
<p>Once I got past these issues, creating the calendar item was actually very simple.</p>
<h3>OpenEdge through the&nbsp;Java WebService</h3>
<p>The next step&nbsp;was to take what I had created and expose it as a Java Web Service. I went with Glassfish V3 as the application server just to test this and in about a half hour I had the Web Service up and running and <a href="http://www.soapui.org" target="_blank">soapUI</a> was creating appointments through the Java Web Service without any problems.</p>
<p>I then went back to OpenEdge and used its WSDL analyzer to&nbsp;analyze the WSDL. It worked really well. I then built a quick client that called the Java Web Service from OpenEdge and it worked first time.</p>
<h3>Next Steps</h3>
<p>Although this is now a working prototype, it is exactly that &#8211; a prototype. There are several pieces that still have to be proven:</p>
<ol>
<li>I need to&nbsp;subscribe to change updates from Exchange.</li>
<li>I need to&nbsp;get to updates for all sales person&nbsp;mailboxes. This means I need to use Exchange Impersonation and that has a number of security risks associated with it.</li>
<li>I need to make sure these updates get through to OpenEdge efficiently which means I need a test harness that can generate several thousand appointments a minute and have them update the OpenEdge AppServer without breaking it.</li>
</ol>
<h3>Conclusion</h3>
<p>Having said that, I am really excited and very optimistic about how this is turning out. I have learned more about Windows Server, Microsoft Exchange, Active Directory, Exchange Web Services, OpenEdge Web Services, and Java Web Services than I had ever imagined possible.</p>
<p>In the next few weeks I will be providing an update on where I finally ended up with this from an architectural point of view.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.thesoftwaregorilla.com/2010/04/exchange-web-services-starting-out/feed/</wfw:commentRss>
		<slash:comments>8</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[n-tier 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]]></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>

