Bob Novas's profileBob Novas's WebLogPhotosBlogListsMore ![]() | Help |
Bob Novas's WebLog |
|||||
|
August 21 The Balkanization of the Internet and the resurgence of...Radio?
With states controlling Internet traffic and restricting the free flow of information, will there be a resurgence of radio (e.g., Radio Free Europe)?
Just a thought. May 04 Storing Microsoft Office 2007 Groove Events in a Database TableMicrosoft Office 2007 Groove can produce events that represent changes to the data within tools in Groove workspaces (or for a number of other reasons). This blog explores putting Groove events, of any kind, into a database table. This is of interest because from there, you can process the events "at leisure", and you can have a full history of event data to reference. Putting event data into a database table separates a solution into two more easily solved pieces – the piece that handles processing events from Groove, and the piece that processes data from a database, transforms it, and puts it somewhere else. A typical use of such a capability is shown in Figure 1. This shows a Groove Data Bridge (GDB) server participating in a Groove workspace that contains a Discussion Tool. The GDB passes events representing CRUD operations on Discussion entries to a "Groove Event Service" to put into an Events Database Table. A "SharePoint Publisher" piece then removes event data from the Events Table and using a Discussion Entries Table, publishes the data to a SharePoint server where it becomes accessible to SharePoint clients.
Figure 1 - Groove to SharePoint Discussion List Publisher The key thought in this blog is that a single table can contain event data from any class and type of Groove event, for any and all sources of events in Groove. A suitable table format is shown in Figure 2. What I want to discuss is how to transform events received from Groove to insert them into this table format, and how to transform data from this table back into a format that can be easily processed.
Figure 2 - Groove Events Table The Groove Events table has the following fields (with reference to Figure 2):
Using the Groove Web Services Helpers (http://www.codeplex.com/GWSV12Helpers), events are delivered to IGrooveWebServicesEventCallback member ProcessEvent, with the method signature: public void ProcessEvent(GrooveWebServicesV12Helpers.GrooveEventsWebService.Event i_Event)
The Event class is declared in the helpers as:
Clearly, all of the members of this class easily go into the Events table, with the exception of EventData. Here is the serialization of EventData to make it available to insert in the table:
Given the information above, it is easy enough to insert an event into the table shown. Now, how do you get the data, specifically the EventData, out of the table? Here's the code that it takes to "reconstitute" EventData:
Calling Deserialize<t>(Stream i_Stream) with the appropriate type for t returns the original event data. The EventType field indicates the type of the EventData – so you'll have to switch on EventType to process the event, in code like this:
This technique is particularly valuable if you're working through some issues with processing events – since you've got a record of events at hand that you can peruse. Now, just to mention the downside of processing events – Groove events are asynchronous, "after" events. Given the nature of Groove, you could be processing an event a long time after the data change that the event represents actually occurred. Furthermore, events do not convey "bulk" data – Forms tool events don't carry any attachments for the record; Files too events don't carry the file data. These two considerations (the possibly long delay and the "remoteness" of the bulk data) mean that you must be careful to recognize that the underlying data may no longer exist. Given an event sequence like Add, Delete, by the time you process the Add event, the action that caused the Delete event has likely already deleted what the Add added. So you can't rely on data existing. Again, though, having the event data persisted in a database can make it easier to figure out what's going on. April 16 Debugging Applications that use Groove Web Services by reading the Web Service SOAP conversationWhen you are developing an application that uses Groove Web Services to write records to a Groove Forms Tool (e.g., Forms2Tool), you sometimes run into an exception "The remote server returned an error: (500) Internal Server Error", with a null inner exception. You need more information to debug this problem. The best way I've found is to use a TCP trace utility and look at the SOAP conversation. This blog walks through an example. First of all, the trace utility I often use is tcptrace. This utility is simple to use, but requires a little bit of setup to work with GrooveWebServicesV12Helpers (GWSHelpers), which I use for most of my Groove applications. Tcptrace needs to be put "in-line", between Groove and the application that consumes Groove Web Services (GWS). The way to do that depends on whether you're using a Groove client or a Groove Data Bridge (GDB). It's simple if you're using a GDB. Set the application's Host argument passed to the Context object to a port like 9081, so that the application makes GWS calls to port 9081. Then use tcptrace to map 9081 to 9080 (the port where the GDB listens by default), and you're done. The application makes GWS calls (and receives responses) on 9081 to tcptrace. Tcptrace logs both directions of the SOAP conversation and passes the calls to the GDB on 9080 and the responses to the application listening on 9081. If you're using Groove client, it's a bit trickier. Regardless of the port the caller passes to the Context object, if the RequestKey parameter passed to the Context object is null, GWSHelpers will set the port based on the value of $HKCU\Software\Microsoft\Office\12.0\Groove\GrooveHTTPDesiredPort, which will be 9080 by default. So even if you pass 9081 to the Context object (e.g., by passing http://localhost:9081 as the host parameter), the GWSHelpers will use 9080. The workaround is to change the value of $HKCU\Software\Microsoft\Office\12.0\Groove\GrooveHTTPDesiredPort to 9081 and then run your application – GWSHelpers will then use 9081. If you restart Groove with $HKCU\Software\Microsoft\Office\12.0\Groove\GrooveHTTPDesiredPort set to 9081, Groove will use 9081, and you don't want this – so make sure Groove is running before you change the value. Now make your web service call and watch what tcptrace reports. Now you find that this command (not all of the command is shown): HTTP/1.1 User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; MS Web Services Client Protocol 2.0.50727.1434) VsDebuggerCausalityData: uIDPozkWzpT+NWpAojz7KKDMYYgAAAAAicJW37gpvEWxefjLoIaUhbSdimHZlKdJvTmKfVC3LmQACQAA Content-Type: text/xml; charset=utf-8 SOAPAction: "http://www.groove.net/Groove/2.0/Forms2#CreateRecords" Host: localhost:9081 Content-Length: 12102 Expect: 100-continue <?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Header><GrooveRequestHeader xmlns="http://webservices.groove.net/Groove/2.0/Core/"><TimeToLive>0</TimeToLive> <GrooveIdentityURL>grooveIdentity://d5whz9tszbqz7pz4rkqiuaeugvw4vyxr@</GrooveIdentityURL> … Yields the response: HTTP/1.1 500 Server error Content-Type: text/xml; charset=utf-8 Connection: close Content-Length: 1063 <?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:enc="http://schemas.xmlsoap.org/soap/encoding/"><soap:Header><GrooveResponseHeader xmlns="http://webservices.groove.net/Groove/2.0/Core/"><GrooveResponseKey>z2f3kgsf7fvn2784zv7tgc9ydxdrxujiqb2wzcs</GrooveResponseKey></GrooveResponseHeader></soap:Header><soap:Body><soap:Fault><faultcode>soap:Server</faultcode><faultstring>Unable to set the 'ShapeTitle' field due to validation constraints.</faultstring><detail><GrooveError><Result>-2147155457</Result><Message>Unable to set the 'ShapeTitle' field due to validation constraints.</Message><ErrorInfo><Description>Unable to set the 'ShapeTitle' field due to validation constraints.</Description><GUID>{1623F60F-8955-44FE-875B-DC5CCF0DDA75}</GUID><Source>GWS</Source><FailureCode>0</FailureCode><Result>-2147155457</Result></ErrorInfo></GrooveError></detail></soap:Fault></soap:Body></soap:Envelope> As you can see above, the message "Unable to set the 'ShapeTitle' field due to validation constraints.", which unfortunately doesn't make it to the exception reported by the application, provides the clue to what's wrong. Further investigation reveals that the ShapeTitle field has a minimum length constraint that the application is violating. It's often helpful to look at the SOAP conversation when debugging a GWS problem – you can often see what's wrong more easily in the messaging than in the code. April 15 Closing tabs in a tabbed view by middle mouse clickSomething I didn’t know that a colleague clued me on is that you can close any tabbed view by middle mouse clicking on the tab. This is true for MSVS tabs, IE7 tabs, etc. This comes in really handy when you’ve got a ton of tabs open that you want to close. April 07 Easily Blogging a PowerPoint 2007 Presentation using Word 2007I struggled with how to easily blog a PowerPoint. I wanted a way to publish the PowerPoint presentation with both the slides and the notes. I realized that you can use Publish (in PowerPoint 2007) to "Create Handouts in Microsoft Office Word". The choices there include "Notes next to slide", which made the slides too small, and "Notes below slides", which seemed to work out ok. So, if you do that, publish the slides to Word with the "notes below slides", you wind up with a Word document that has slides and notes. The slides are a bit small – but you can stretch them bigger. Now you can use Word 2007's Publish, Blog option to publish the slides to your blog. To publish my blog, I had to specify a separate image provider – I happen to use Comcast which gives me a 1GB store. The only problem I see is that the slide images are all published to the same folder on the image provider – it makes for a lot of slides in one folder if you do this several times. You can figure out what's what based on the names of the slide images – which all start with a month-day-year, but slides won't be segregated into separate folders unless you manage your publish better. But wait... It gets better. You can update the published PowerPoint by publishing an update as a draft, editing the draft and copying the html, editing and pasting the html into the original entry, and deleting the draft and the images associated with the original, edited blog. This is a tricky point - you're using the html from the draft entry. The entry is a throwaway but you want to use the images that it uploaded. So the html that you copy and paste references the images you just loaded - these are the images you need to keep. The images you want to delete are the ones the old html ((that you just replaced) referenced. April 04 Exchanging Office 2007 InfoPath Forms between Office 2007 Groove and SharePointSlide 1
Slide 2
Note – two workflows are shown.
SharePoint Workflow 1 moves forms submitted in Groove to SharePoint (and back to Groove when they're Completed) SharePoint Workflow 2 moves forms through a tasking workflow in SharePoint
Slide 3
Admin fills out an administrative form that identifies a Groove InfoPath Form (e.g., Groove Space, IP Forms Tool, IP Form) and a SharePoint Form Library and submits the form to a "Groove workflow admin list".
Slide 4
Submitting the administrative form initiates a persistent, long duration workflow that exchanges forms between the Groove InfoPath Forms tool and the SharePoint forms library, based on the state of the form (actually, the value of a status field). That workflow is tracked in the admin SharePoint list, as shown above, by the entry created when the "wf 3" form was submitted.
The reason for this is simple – in SharePoint, a workflow has to be associated with an item in a list (a SPListItem). A workflow can't "just" be associated with a list and create items in that list – it has to be associated with an existing item. Hence there are two lists – one for (potentially many) Groove-SharePoint workflows (that you see above) and another for the workflow that kicks off when a user "submits" a form in Groove – that this workflow (the "wf-3" workflow) sticks in another list, which kicks off another workflow. Slide 5
User fills out a form on Groove and "submits" the form.
Slide 6
Slide 7
SharePoint workflow runs and moves form to SharePoint forms library (a different form library). Status of SR-1002 is "InProcess"
Slide 8
Form is routed via a DIFFERENT SharePoint workflow to maintenance and problem is fixed
Slide 9
But not yet updated in Groove…
Slide 10
The SharePoint workflow engine runs and moves the form back to Groove
Slide 11
Slide 12
Slide 13
Slide 14
This workflow program runs for a long duration (as long as the Groove and SharePoint entries are to be connected via workflow ). This could be for months or longer.
The OnTaskChanged activity of this workflow runs periodically, and does all the work.
Slide 15
Slide 16
Slide 17
Slide 18
Slide 19
Slide 20
Updating a task is something a SharePoint workflow can wait for – so there's a program that periodically updates any tasks it finds (in the SharePoint Groove workflow admin list), and that runs the workflow program(s) on the list
March 20 Repetitively Invoking Groove Web Service Operations…I have been writing a lot of code using the Groove Web Services Helpers that follows this pattern: · Event Thread: periodically invoke a Groove Web Service (GWS) Operation to poll a Groove event queue for Groove events and raise events locally that represent the event (data) from Groove. · Main thread: respond to events from Event Thread and invoke GWS operations to do stuff So, every 5 seconds the Event Thread code reads an event queue. Occasionally the read returns data that represents events that happened in Groove. The Event Thread code raises a “real” event locally. The Main Thread reacts to the event and invokes more GWS operations that read or write data from a Groove tool. I’ve been troubled by the behavior of the code – sometimes web service operations fail (with a message like “The request was aborted: The request was cancelled”), or take a long time to complete, and it’s not clear why. I finally stumbled across a description of a problem that seemed pertinent and that suggested a solution. The problem seems to go like this. Web Service connections are re-used by default – this benefits efficient use of a physical link. A connection that is idle for too long or that has timed out is terminated by a FIN or finish signal which can be sent by either client or server end. The problem is that the FIN signal from one end can essentially pass by a legitimate packet from the other end like ships passing in the night. I can’t go much further with this discussion, other than to say that when this happens, it seems to get mishandled, one side uses a connection that the other side terminated, and this leads to web service operations failing unexpectedly. This appears to be especially true when the web service client and server are running on the same PC – as happens with Groove Web Services. Groove is a web services server and the applications that I write consume Groove Web Services and run on the same box as Groove. The situation is likely aggravated by the heavy use of the CPU resource. Now the good news… I tried the solution that was suggested and it seems to work. I’ve added the solution to the Groove Web Services Helpers and will republish them shortly, after I’ve done some more testing. For the record, the solution I implemented is to add the following code to a static constructor for the Context object: static Context() { System.Net.ServicePointManager.MaxServicePointIdleTime = 5000; System.Net.ServicePointManager.DefaultConnectionLimit = 24; }
The use of a static constructor is interesting. A static constructor, also called a Type constructor, initializes a type. The CLR calls a static constructor before the first instance of that type is created or any static members are accessed (Cwalina and Abrams, Framework Design Guidelines, 2007). This means that the two properties of ServicePointManager are set before a web service client application can construct a GWS Helpers Context object. That’s the behavior I needed – to get these defaults set on the web services protocol stack before the client uses any Groove web services. The downside is that these settings apply to all web service operations, regardless of the target. Another problem is that I honestly can't say I understand why making these settings would fix the problem. But, I'm not going to argue with success, and they do appear to work. It’s possible to set a connection limit on a specific URI, using the following XML in app.config: <system.net> <connectionManagement> <add address="http://localhost:9080" maxconnection="24"/> </connectionManagement> </system.net>
But I couldn’t see any way to set the max idle time. So I made the change to the code and I’m testing it. I will say that it seems to really fix a lot of problems I was seeing, so I’m hopeful that this fix will be very beneficial to apps that make repeated calls to web service operations. Thanks for visiting! |
|
||||
|
|