Profil von Bob NovasBob Novas's WebLogFotosBlogListenMehr Extras Hilfe

Blog


    21 August

    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.
    04 Mai

    Storing Microsoft Office 2007 Groove Events in a Database Table

    Microsoft 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):

    • SequenceNumber (primary key) – an auto-incrementing 64 bit number
    • EventTime – the time that the event was recorded in the database
    • EventSequence – the sequence number for events from this source
    • SubscriptionName – the invariant name given the subscription that produced the event
    • SubscriptionID – the Groove subscription ID, which can change over time
    • EventClass – defines the generic kind of event generator – Calendar, Contact, ContactDirectory, Files, Forms, Members, Messages, Space, and Tool.
    • EventType – Defines the specific event – e.g., Add, Delete, Update, …
    • EventSource – Defines the specific event source – a tool or a space or an account
    • EventDataType – unused, remnant from an earlier version of the code.
    • EventData – The actual event data for the event

    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.

    16 April

    Debugging Applications that use Groove Web Services by reading the Web Service SOAP conversation

    When 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.

    15 April

    Closing tabs in a tabbed view by middle mouse click

    Something 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.

    07 April

    Easily Blogging a PowerPoint 2007 Presentation using Word 2007

    I 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.

    04 April

    Exchanging Office 2007 InfoPath Forms between Office 2007 Groove and SharePoint

    Slide 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

     

     

    20 März

    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.

    16 März

    Crusty Rustic Bread

    Two BaguettesInside the crust

    Well, here it is. My recipe for some really good bread. It’s not actually my recipe – it’s straight out of the King Arthur Flour Baker’s Companion – the recipe for Baguettes on p. 239. My contribution is how to actually cook the stuff. What I’ve found is that making bread is incredibly dependent on the process. Use one process, and you get cake-like white bread – ok, I suppose, but not very inspiring. Put the same dough through a different process, and you get crusty, rough textured peasant bread. That’s the process I want to describe. This is a two day process – it doesn’t take you all that long to do, it just takes a while for the bread.

    All my measurements are by weight. I use a scale, put the bowl on the scale, zero the scale, and put the ingredient in. To start with, sometime in the morning the day before you want to eat the bread, make the poolish. Put 5¼ ounces (by weight) water in a largish bowl. I use bottled water, assuming that it has less stuff that kills yeast in it. Thoroughly mix in a pinch (1/8 teaspoon) of active dry yeast. Now add 5¼ ounces (by weight) of King Arthur bread flour (not all purpose flour, you want the really glutinous stuff), and mix it in with a wisk just enough so the flour and water and yeast are all mixed. Now cover this with plastic wrap, putting the plastic wrap right on top of the poolish, and let this sit all day.

    Sometime before you go to bed, the poolish should have risen and become bubbly sticky goo. Now, take your mixer bowl (empty), put it on your scale and add 5¼ ounces of water (bottled). Again thoroughly mix in 1½ teaspoons of active dry yeast. (I just toss in a little less than the rest of the packet.) Add 10½ ounces bread flour, the poolish and 2 teaspoons of salt. Mix this with a dough hook for less than a minute, until all the flour is all wet and then let it sit for a while. You can let it go for 20 minutes. Now actually knead the dough for a couple of minutes. The result should be very wet dough that’s just starting to ball up. Lightly oil (olive oil) a bowl, put the dough in the bowl, and cover it with plastic wrap, again putting the plastic wrap right on the dough. Now put this in the fridge and leave it over night.

    In the morning take the bowl with the dough in it out of the fridge – it should have risen some. You can fold the dough over a few times and put it back in the bowl, covered with plastic wrap. Let it go a couple of hours and fold it down again. At least 2 hours before you're going to bake the bread, divide the dough into two pieces and roll it into two sticks about 18” to 20” long on a floured surface. Put the sticks into a baker’s couche (burlap) in a concave form and let the bread rise as long as you can stand it – for a couple hours at least.

    This next part of the process is the magic that makes the bread crusty and chewy and coarse grained. An hour before you’re going to bake the bread, turn your oven on as hot as it will go – 500 degrees F at a minimum. Put a baker’s bread stone on the middle shelf of the oven and a large shallow iron skillet on the bottom shelf and let these things get good and hot. 15 minutes before you’re going to bake, get some water boiling.

    Roll the sticks one stick at a time from the couche onto a floured flat aluminum tray, score the top of the bread with a lame if you want to, and slide each stick one at a time onto the bread stone in the oven. The sticks will be flatish and wide – since they’ll sink when you take them out of the form. Pour a cup of the boiling water into the shallow skillet to make steam (watch out!) and use a spray bottle to spray water on the bread and stone. Get a good bit of steam going in the oven and shut the door. Set a timer for 15 minutes. Let the bread bake for a couple of minutes and reduce the oven temperature setting to 475. Then let the bread bake for the remainder of the 15 minutes. The bread should quickly “spring” from the flat shape it started from, and form some good sized baguettes.

    I believe that what gives the bread it's chewy texture and coarse grain is that you bake the bread directly on the bread stone, putting the whole bottom of the bread on the blazing hot surface of the stone. If you bake the bread in a baguette form, that concave metal thing, the bread bakes slowly since the form is cold when you put it in the oven. But if you slide the bread right onto the hot stone, the whole bottom of the bread is rapidly heated, and it's this that causes the bread to rise in a rapid fashion, creating the delicious texture.

    09 März

    Thoughts on Microsoft Office 2007 Groove Events

    Since writing “Using Web Services Helpers to Access Events in Groove 2007”, I noticed a puzzling behavior that I finally understood that I wanted to mention. Also, there have been some upgrades to the Web Service Helpers event mechanism that are noteworthy that I wanted to blog about.

    Subscribing to Events from more than one Event Source over a Long Duration

    Example 3, Aggregated Tool Event Handler, illustrates adding an Aggregated Tool Event Listener for two event classes – Forms and Forms2. There’s no problem with that. The code as shown creates new subscriptions to Forms Events and Forms2 Events whenever the Forms app starts. But suppose you’ve written an application that you want to subscribe to events for an indefinitely long time. Presumably, over that time, the computer will be shut down and re-started. You want your application to receive all Groove events that occur on several event classes, without missing any, even if the user re-starts the computer.

    The Helpers provides a way to subscribe to an existing aggregated event subscription that you would use to do this. The problem is that using this technique naively can lead to undesirable behavior. You would think that you simply call AddAggregatedToolEventListener() using the overload that has an event subscription ID parameter. This would inform the EventManager that you want to use an existing subscription. The problem arises if you make more than one such call to AddAggregatedToolEventListener(), to listen for events from more than one Event Class – like Forms2 events and Files events. The first call to AddAggregatedToolEventListener() starts the EventManager.EventManagerThreadProc() running and reading events from an event queue internal to Groove. It also sets up an EventManager expecting events from the given subscription. The next call to AddAggregatedToolEventListener() would set up an EventManager expecting events from a different subscription. But, at the point when the first call to AddAggregatedToolEventListener() is made, the EventManager for the second subscription isn’t set up yet. So, if there happen to be any events queued for the second subscription, they will be delivered to the EventManagerThreadProc, which will not only discard them, it will add their SubscriptionID to a list of SubscriptionID’s to ignore, from that point forward. This will be reported to System.Diagnostics.Debug, but probably won’t be a happy circumstance for your program.

    The solution is easy. EventManager has a public static EventsEnabled property. You should set the EventManager.EventsEnabled property false before the first call to AddAggregatedToolEventListener(), and set EventManager.EventsEnabled true after the second (e.g., last) call to AddAggregatedToolEventListener(). That prevents the EventManagerThreadProc from running and discarding any events for subscriptions that haven’t been set up yet. EventManager.EventsEnabled should be used in this fashion whenever subscribing to events from more than one event class.

    Maintaining an Event Subscription that Survives a Groove Restart

    The next thing I wanted to mention was how to write an application that consumes Groove Web Services and works even if the user stops Groove and then restarts it, without re-starting the application. This is the case, for example, if the application is written as a Windows Service or a Startup application. Such an application’s lifetime is independent of the lifetime of the Groove client whose web services the application consumes.

    The latest versions of the Groove Web Services V12 Helpers (Release 9 and later) have been modified to address this issue. These versions of the Helpers ensure that web service operations are made with the current Groove Web Services Request Key – this was a problem in earlier versions because the Helpers cached the Web Service headers and hence the Request Key. The Request Key changes whenever Groove re-starts. So unless your application re-starts in synchronicity with Groove, the Helpers used the wrong Request Key and web service operations failed. The newer versions recreate the headers if the Request Key changes. The Helpers also update any subscriptions using the correct header.

    The one thing that the Helpers can’t address is if the Groove client is shutdown so long that the Event Subscription actually expires (Event Subscriptions are created with a declared time-to-live). This is unlikely, but it is possible. In this circumstance, the Helpers EventManager delivers:

    IGrooveWebServicesAdvancedEventCallback.OnSubscriptionUpdated

    (GrooveWebServicesV12Helpers.Subscription)

     

    Where the subscription has a non-null ErrorMsg property. This is the only case when the ErrorMsg property is not null. The message reports

    “Could not find element with ID={0} within the document”

     

    If your code handles OnSubscriptionUpdated() and receives this message, the proper thing to do is to create a new Subscription – the old Subscription has expired and won’t be productive.

    06 März

    Exchanging InfoPath Forms between SharePoint and Groove

    This blog is about exporting an InfoPath form from a Microsoft Office 2007 Groove InfoPath Forms Tool and importing the form to a SharePoint Forms Library. Why would you do this?  This might be useful for people who collect information in Groove Forms Tool records while roaming the field disconnected from any network, and who then connect to a network at night – the information collected on the Form records could be aggregated to a central SharePoint Form Library. Alternately, say you have intermittently connected employees that you want to task using dispatch forms – you could have a central SharePoint site where you submit dispatch forms and the forms trickle out to the field employees who receive them on laptops connected to the Internet via air cards that have only spotty connectivity. A Groove InfoPath Forms tool in a Groove workspace would serve to get the dispatch forms to the employees with no fuss or muss – no VPNs, no logins, just secure forms synchronization from the central site to the laptops running Groove when there is connectivity. Figure 1 illustrates a system schematically.

    Fig-1

    Figure 1 - System Overview

    So here’s the starting point – you have a SharePoint site and an un-designed Groove InfoPath Forms tool and you want to set things up so you can exchange forms from one to the other. Basically you’re going to design a form in InfoPath 2007, and then import this form design (a “template” or .xsn file) into Groove and SharePoint. Then you’re going to create a form in Groove, export the form to a file and import that file into SharePoint, or create a form in SharePoint, export that form to a file and import that file into Groove.

    To begin with, you use Office 2007 InfoPath to design a form and save the form design as an InfoPath Forms template (an .xsn file). There are some restrictions on the design of this form in order to be able to use the form in a Groove InfoPath Forms tool. These are listed in the “Microsoft Office Groove Help” available from the Groove Help menu, under the topic “About Groove InfoPath Forms Tool”.  You may want to design the form specifically for Groove, import the template to Groove and then change some of the restricted settings for SharePoint. No matter what, there’s clearly an issue here – you either wind up with two templates that you have to manually keep in sync or you keep just one template (the Groove one) that when you update, you save a copy for use in Groove and then make the same changes for SharePoint. You’ll have to think this through for your own situation.

    So, let’s say you’ve got a form template suitable for use in Groove. You import this .xsn file into a Groove InfoPath Forms Tool and create a Groove form with the same design. You make some changes to the settings of the form template (but not to the design) to make it more suitable for SharePoint (e.g., where to submit the form) and you create a SharePoint forms library using this template.

    You can now use this form to create records in Groove that represent the filled in values of the form. So the question now is – how to export the xml from the Groove InfoPath Forms tool to files that you can get into the SharePoint Forms Library (or vice-versa).

    I’d like to show how to do this using the Groove Web Services Helpers, available from CodePlex (http://www.codeplex.com/GWSV12Helpers, Release 9 or later). Furthermore, I’m going to use the Groove Web Service Helpers Command Line Utilities, available from the same URL. You’d probably never use the command line utilities for an operational system, but using the command line utilities is a great way to demonstrate step by step how to perform the process. Since the full source code is provided for the utilities, it’s then entirely possible (and up to you!) to program a solution to do the same thing automatically.

    Suppose you have the Helpers command line utilities working; you have a Groove workspace with an InfoPath Forms tool; and, there are some forms entered in the tool. Here’s how to go about exporting the forms from the tool. First you’ll need to get some URI’s to the various pieces. You’re going to need the URI of the workspace, the tool, the form, and the record(s) you want to export. It doesn’t matter which Groove system you get this information from – in a Groove workspace with multiple endpoints, these URIs are the same at every endpoint.

    Probably the trickiest part of getting the command line utilities to work is forming the URI’s to the various pieces correctly. Take a look at Table 1, which shows the URI’s for various Groove objects of interest, all of which are related. The first row shows the URI of a Space. The second row shows the URI of a Tool in that space. The third row shows the URI of a Form in that Forms tool. The fourth row shows the URI of a view in that Forms tool. Finally, the fifth row shows the URI of a record in that Forms tool. These URI’s have a great deal of common information, but there are some important differences.

    Table 1 – URI’s for a Space, Tool, Form, View and Record

    Object

    URI format

    Space

    /GWS/Groove/2.0/Spaces/grooveTelespace/d99raev3ii89vnwxiec5i5pxia2uzmqi3tgnjxs

    Tool

    /GWS/Groove/2.0/Tools/grooveTelespace/d99raev3ii89vnwxiec5i5pxia2uzmqi3tgnjxs/ToolContainer/depnrjn9xqxgq

    Forms2Tool Form

    /GWS/Groove/2.0/Forms2/grooveTelespace/d99raev3ii89vnwxiec5i5pxia2uzmqi3tgnjxs/

    ToolContainer/depnrjn9xqxgq/DataModelDelegate/FormID=-4.41257738669127E+044

    Forms2Tool View

    /GWS/Groove/2.0/Forms2/grooveTelespace/d99raev3ii89vnwxiec5i5pxia2uzmqi3tgnjxs/ToolContainer/depnrjn9xqxgq/DataModelDelegate/ViewID=2.711619205752946E-056

    Forms2Tool Record

    /GWS/Groove/2.0/Forms2/grooveTelespace/d99raev3ii89vnwxiec5i5pxia2uzmqi3tgnjxs/ToolContainer/depnrjn9xqxgq/DataModelDelegate/RecordID=-1.1372659343098586E-067

     

    Note how URI’s are slightly different and longer as we get more specific about what we’re interested in. If you slip up and use the wrong URI (and it’s especially easy to get that 4th field – the one that says “Spaces” or “Tools” or “Forms2”, wrong), the web method will fail with an unhelpful error. That’s another reason it’s nice to have the command line helpers – you can get some better visibility into what works more quickly than writing code.

    The first command line utility we’re going to use is GrooveDir.  GrooveDir has a number of command line arguments (try typing “GrooveDir /?” in a cmd box), but what we want is the command that reports the workspace URI’s.  This command is typed “GrooveDir /s”, which on my test system produces the following output:

    <GrooveDir>

      <Space

         Name='IPFormsTest'

         URI='/GWS/Groove/2.0/Spaces/

              grooveTelespace/d99raev3ii89vnwxiec5i5pxia2uzmqi3tgnjxs'>

      </Space>

    </GrooveDir>

     

    To find the Tool URI, I’ll type “GrooveDir /t”, which gives me:

    <GrooveDir>

      <Tool

         Name='InfoPath'

         URI='/GWS/Groove/2.0/Tools/

              grooveTelespace/d99raev3ii89vnwxiec5i5pxia2uzmqi3tgnjxs/

              ToolContainer/depnrjn9xqxgq'/>

    </GrooveDir>

     

    I’m looking for a tool named “InfoPath” in the same space as the Space named “IPFormsTest, so I have to find a tool named “InfoPath” with a URI that has the same characters after “…/grooveTelespace/”. Note the subtle differences between a space URI and a tool URI (“Spaces” versus “Tools” and “ToolContainer/…”. The thing you’re looking for is the “d99raev3ii89vnwxiec5i5pxia2uzmqi3tgnjxs”.

    Finally, I need the URI’s of the records in the InfoPath Forms Tool. For that, I need the GrooveQueryFormsRecords utility. The command (typed all on one line) is

    GrooveQueryFormsRecords

       /FormsTool=

       ”/GWS/Groove/2.0/Tools/

        grooveTelespace/d99raev3ii89vnwxiec5i5pxia2uzmqi3tgnjxs/

       ToolContainer/depnrjn9xqxgq”

     

    This will dump the xml for all the records in the Forms tool. The URI’s will look something like this:

    <RecordURI>

       /GWS/Groove/2.0/Forms2/

           grooveTelespace/d99raev3ii89vnwxiec5i5pxia2uzmqi3tgnjxs/

           ToolContainer/depnrjn9xqxgq/DataModelDelegate/

           RecordID=-1.1372659343098586E-067

    </RecordURI>

     

    I won’t go into how you might pick a record to export. For this blog, let’s just pick the record noted above and export it. The command to do this (all on one line is):

    GrooveInfoPathForms

       /Export

       /RecordURI=”/GWS/Groove/2.0/Forms2/

                   grooveTelespace/d99raev3ii89vnwxiec5i5pxia2uzmqi3tgnjxs/

                   ToolContainer/depnrjn9xqxgq/DataModelDelegate/

                   RecordID=-1.1372659343098586E-067”

       /Path=C:\Users\robnovas\Documents\Temp\ExportedFromGroove.xml

     

    This will produce a file “ExportedFromGroove.xml”.  Unfortunately, if you double click this file, InfoPath is only able to open this file, at least in its present form, on the system that you exported it from Groove and only if Groove is running. Before opening the file, InfoPath will ask if it’s OK to contact the server. If you allow InfoPath to contact the server, it will open the form, again, constrained as stated.

    If you edit the xml in notepad, you will see the problem, as shown below.

    <?xml version="1.0" encoding="UTF-8"?>

    <?mso-infoPathSolution

    solutionVersion="1.0.0.1"

    productVersion="12.0.0"

    PIVersion="1.0.0.0"

    href="groove://groove[:221746984]C:/Users/robnovas/Documents/GWSV12Helpers/ZZZ-TestIPRoundtrip/TestRun-02/01-BuildIPForm/NewFormTemplate.xsn"

    name="urn:schemas-microsoft-com:office:infopath:NewFormTemplate:-myXSD-2008-03-02T13-25-10221746984" ?>

     

    The schema for this form is in Groove and is not accessible using the URL given in the href and URN in the name.

    The way around this is to fixup the processing instruction so that the exported form references a schema that is accessible. The GrooveInfoPathForms command supports this with the concept of an “example form” – a form that can be used as an example to get the necessary reference.

    Since this form was exported from Groove, it is suitable for use as an example form for importing other forms (of the same schema) back into Groove. Let’s get another example form, suitable for importing a form into SharePoint. Fire up your SharePoint site with the Forms Library, create a form, and export this form (“Save As” to disk). Call this form ExportedFromSharePoint.xml.

    If you edit this form in notepad, you’ll see something like the following:

    <?xml version="1.0" encoding="UTF-8"?>

    <?mso-infoPathSolution

    solutionVersion="1.0.0.3"

    productVersion="12.0.0"

    PIVersion="1.0.0.0"

    href=http://novas-x64/sites/TestIPForm/IPFormLib/Forms/template.xsn

    name="urn:schemas-microsoft-com:office:infopath:IPFormLib:-myXSD-2008-03-02T13-25-10" ?>

     

    That’s what the processing instruction will have to look like to import a form into InfoPath. So now we have two forms, ExportedFromGroove.xml, which is in a form suitable for importing a form into Groove; and ExportedFromSharePoint.xml, which is in a form suitable for importing a form into SharePoint.

    Now we can use the ExportedFromSharePoint.xml form as an example for a form exported from Groove that we will import into SharePoint. The command to export the form from Groove is (all on one line):

    GrooveInfoPathForms

       /Export  

       /RecordURI=”/GWS/Groove/2.0/Forms2/

                   grooveTelespace/d99raev3ii89vnwxiec5i5pxia2uzmqi3tgnjxs/

                   ToolContainer/depnrjn9xqxgq/DataModelDelegate/

                   RecordID=-1.1372659343098586E-067”

       /Path=C:\Users\robnovas\Documents\Temp\ExportedFormDestinedForSharePoint.xml

       /ExamplePath=C:\Users\robnovas\Documents\Temp\ExportedFromSharePoint.xml

     

    ExportedFormDestinedForSharePoint.xml now is properly setup to be able to be uploaded into the SharePoint Forms Library. You should be able to upload this form to your SharePoint Form Library and open it with no trouble.

    Now, let’s import the form exported from SharePoint into Groove.  Just as an experiment, try to import the form without specifying an example form (type the command all on one line):

    GrooveInfoPathForms

       /Import

       /FormURI="/GWS/Groove/2.0/Forms2/

                 grooveTelespace/d99raev3ii89vnwxiec5i5pxia2uzmqi3tgnjxs/

                 ToolContainer/depnrjn9xqxgq/DataModelDelegate/

                 FormID=-4.41257738669127E+044"

       /Path=C:\Users\robnovas\Documents\Temp\ExportedFromSharePoint.xml

     

    You’ll get the following error:

    Unable to set the 'Forms_Tool_IPContents' field because the document content is invalid.

     

    Now, try importing the form again but this time specify the ExportedFromGroove.xml form as an example (type the command all on one line):

    GrooveInfoPathForms

       /Import

       /FormURI="/GWS/Groove/2.0/Forms2/

                 grooveTelespace/d99raev3ii89vnwxiec5i5pxia2uzmqi3tgnjxs/

                 ToolContaner/depnrjn9xqxgq/DataModelDelegate/

                 FormID=-4.41257738669127E+044"

       /Path=C:\Users\robnovas\Documents\Temp\ExportedFromSharePoint.xml

       /ExamplePath=c:\Users\robnovas\Documents\Temp\ExportedFromGroove.xml

     

    This time the command returns the URI of the form created in Groove:

    <GrooveInfoPathForms>

    <Import 

       URI='/GWS/Groove/2.0/Forms2/

            grooveTelespace/d99raev3ii89vnwxiec5i5pxia2uzmqi3tgnjxs/

            ToolContainer/depnrjn9xqxgq/DataModelDelegate/

            RecordID=-1.5660662740611548E-088'/>

    </GrooveInfoPathForms>

     

    Hopefully this will help you get on your way towards exporting and importing Groove InfoPath forms. Another idea I’m kicking around is how to pick which forms to export. What I’m thinking of there is to use views to impose a kind of workflow on forms and to move a form from one view to another as the workflow progresses.  The views would be something like this – Draft, Submitted, Exported, Accepted. A user would create a form in the Draft view. “Submitting” the form would move it to the Submitted view. Exporting the form would move it to the Exported view. And the back end system accepting the form would move it to the Accepted view. More to come.

    Exporting the template from Groove

    You can also recover the InfoPath Form template from the Groove InfoPath Forms tool. To do that, you need the URI of the Form. The command to report the Form URI’s of a Forms tool is:

    GrooveQueryForms

       /FormsTool=”/GWS/Groove/2.0/Tools/

                   grooveTelespace/d99raev3ii89vnwxiec5i5pxia2uzmqi3tgnjxs/

                   ToolContainer/depnrjn9xqxgq”

     

    This produces the xml shown below:

    <?xml version="1.0" encoding="utf-8"?>

    <QueryFormsResult

       URI="/GWS/Groove/2.0/Tools/

            grooveTelespace/d99raev3ii89vnwxiec5i5pxia2uzmqi3tgnjxs/

            ToolContainer/depnrjn9xqxgq"

       Name="InfoPath">

      <Forms>

        <Form

           Name="NewFormTemplate"

           ID="-4.41257738669127E+044"

           URI="/GWS/Groove/2.0/Forms2/

                grooveTelespace/d99raev3ii89vnwxiec5i5pxia2uzmqi3tgnjxs/

                ToolContainer/depnrjn9xqxgq/DataModelDelegate/

                FormID=-4.41257738669127E+044" />

         </Forms>

      <Views>

        <View

           Name="View_1"

           ID="2.711619205752946E-056"

           URI="/GWS/Groove/2.0/Forms2/

                grooveTelespace/d99raev3ii89vnwxiec5i5pxia2uzmqi3tgnjxs/

                ToolContainer/depnrjn9xqxgq/DataModelDelegate/

                ViewID=2.711619205752946E-056" />

      </Views>

    </QueryFormsResult>

     

    What we want is the Form URI for the NewFormTemplate form. We use that URI in another command, as shown next:

    GrooveInfoPathForms

       /Template

       /FormURI=”/GWS/Groove/2.0/Forms2/

                 grooveTelespace/d99raev3ii89vnwxiec5i5pxia2uzmqi3tgnjxs/

                 ToolContainer/depnrjn9xqxgq/DataModelDelegate/

                 FormID=-4.41257738669127E+044”

       /Path=”C:\Users\robnovas\Documents\Temp\ExportedTemplate.xsn”

     

    This creates the ExportedTemplate.xsn file, which is the forms template that was used to design this form in the Groove InfoPath Forms tool.