Rather than leverage the DFS SDK-based .NET assemblies to consume DFS services, some may prefer to leverage Visual Studio’s ability to proxy WSDL/XSD directly and choose instead to involve the Windows Communication Foundation (WCF) designer via Add Service Reference… In this post, I will discuss just such a sample application.
As in the previous example, we’ll build the same Windows Forms application to accomplish the same task (i.e. object name retrieval given object id); however we’ll ignore the .NET productivity layer and proxy the Object service contract directly using Visual Studio and WCF.
After launching Visual Studio, choosing File | New | Project… and creating a new Windows Forms Application project called ObjectServiceConsumer, go to the Solution Explorer, select the project node, right-click and choose Add Service Reference…
Enter the address for the DFS Object service instance to proxy (e.g. the same instance used in the previous example’s browser-based accessibility test). Click Go, and you should see something similar to the dialog captured above. Set the namespace for this reference and choose OK.
Return back to Visual Studio, double-click Form1.cs, after building the same dialog as in the previous example, double-click on the Retrieve button. This will tell Visual Studio to transition from (Windows Forms) designer mode to code editing mode.
Implement your button click handler as follows:
string repositoryName = txtbxRepoName.Text; string userName = txtbxUserName.Text; string password = txtbxPwd.Text; string objId = txtbxObjId.Text; // e.g. a document id of 09123456789abcde try { ObjectId objectId = new ObjectId(); objectId.id = objId; ObjectIdentity objectIdentity = new ObjectIdentity(); objectIdentity.Item = objectId; objectIdentity.repositoryName = repositoryName; ObjectIdentitySet objectIdentitySet = new ObjectIdentitySet(); objectIdentitySet.Identities = new ObjectIdentity[1]; objectIdentitySet.Identities[0] = objectIdentity; PropertyProfile propertyProfile = new PropertyProfile(); propertyProfile.filterMode = PropertyFilterMode.SPECIFIED_BY_INCLUDE; string[] includeProperties = {"object_name"}; propertyProfile.IncludeProperties = includeProperties; OperationOptions operationOptions = new OperationOptions(); operationOptions.Profiles = new Profile[1]; operationOptions.Profiles[0] = propertyProfile; DataPackage dataPackage = objectService.get(objectIdentitySet, operationOptions); Property[] properties = dataPackage.DataObjects[0].Properties.Properties; foreach (Property property in properties) { if (property.name.Equals("object_name")) { lblObjName.Text = property.ToString(); } } Console.WriteLine("Successfully retrieved object name for object id" + "'" + objId + "': " + lblObjName.Text); } catch (Exception ex) { lblObjName.Text = "<error>"; Console.WriteLine(ex.StackTrace); Console.WriteLine("Failed to retrieve object name with exception " + ex.Message); } finally { Console.WriteLine("Your cleanup logic goes here."); }
A few points concerning the above code:
- There is a private field on the Form class, objectService, that is of type ObjectServicePortClient (i.e. from the WCF-generated proxy code) and is set to null initially.
- In the Form class constructor, after the standard Windows Forms InitializeComponent(), objectService is set as follows: new ObjectServicePortClient(”ObjectServicePort”);
- “ObjectServicePort” is a named binding in the WCF-generated proxy code (i.e. declared within app.config).
- As in the previous example, I’m using a pre-release version of DFS 6.5 for this example, but I’m not using anything that isn’t available in DFS 6.0 SP1–this is a very simple example by design.
- You should notice some cosmetic differences between the code above and the code here (e.g. more verbose, fewer conveniences, etc.).
You should now be able to build your application and run it. When you enter a valid object id for the specified repository connection, you should see the object name replace “<tbd>” on the dialog.
But, you don’t. Why?
Well, first of all, if you are running your application server with a console window for output message capture, you likely saw the following message:
. . . com.emc.documentum.fs.rt.SerializableException: Authorization failed, could not find identities in service context with token “temporary/127
.0.0.1-1216079837407–1167916486618885387″
at com.emc.documentum.fs.services.core.ObjectServiceWebService.get(ObjectServiceWebService.java:268) . . .
Looking at the code above, it’s hopefully clear that the variables userName and password aren’t employed. (If you’re using the JetBrains ReSharper add-in for Visual Studio, the IDE (plugin) actually visually indicates this condition.)
So, let’s do some detective work.
Using a web debugging proxy (e.g. Charles), re-run the above code (i.e. with port forwarding in place). After valid data entry and clicking the Retrieve button, you should see an entry in your web debugging proxy session. Examine the SOAP request message (i.e. “pretty printed” for readability’s sake):
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <get xmlns="http://core.services.fs.documentum.emc.com/"> <forObjects isInternal="false" xmlns=""> <Identities repositoryName="_DOCBASE_" xmlns="http://core.datamodel.fs.documentum.emc.com/"> <ObjectId id="09003023800024ec"/> </Identities> </forObjects> <options xmlns=""> <Profiles xsi:type="q1:PropertyProfile" isProcessIncludedUnknown="false" xmlns="http://core.datamodel.fs.documentum.emc.com/" xmlns:q1="http://profiles.core.datamodel.fs.documentum.emc.com/"> <q1:IncludeProperties>object_name</q1:IncludeProperties> </Profiles> </options> </get> </s:Body> </s:Envelope>
Next, go back to the previous example, and re-run it to capture its SOAP request message. Before you hit Run, though, comment out the following line of code:
context = contextFactory.Register(context);
(We’ll talk more about this in a future post.)
The DFS SDK .NET assemblies-based application issues the following SOAP request message:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> <s:Header> <ServiceContext token="temporary/127.0.0.1-1216195928765-692634301" xmlns="http://context.core.datamodel.fs.documentum.emc.com/"> <Identities xsi:type="RepositoryIdentity" userName="_USER_" password="_PWD_" repositoryName="_DOCBASE_" domain="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/> <RuntimeProperties/> </ServiceContext> </s:Header> <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <get xmlns="http://core.services.fs.documentum.emc.com/"> <forObjects isInternal="false" xmlns=""> <Identities repositoryName="_DOCBASE_" valueType="OBJECT_ID" xmlns="http://core.datamodel.fs.documentum.emc.com/"> <ObjectId id="09003023800037c6"/> </Identities> </forObjects> <options xmlns=""> <Properties xmlns="http://core.datamodel.fs.documentum.emc.com/"/> <Profiles xsi:type="q1:PropertyProfile" filterMode="SPECIFIED_BY_INCLUDE" xmlns="http://core.datamodel.fs.documentum.emc.com/" xmlns:q1="http://profiles.core.datamodel.fs.documentum.emc.com/"> <q1:IncludeProperties>object_name</q1:IncludeProperties> </Profiles> </options> </get> </s:Body> </s:Envelope>
Yep, there’s definitely a missing header in today’s sample, as coded above.
It’s also worth noting a few things before continuing:
- The code above requires SSL to avoid cleartext data being sent over the wire. Of course, SSL–and replacing http://… with https://…–only provides transport-level security that is point-to-point in nature (i.e. compared with message-level security that is end-to-end in nature).
- valueType=”OBJECT_ID” - this is missing on the <Identities> element in today’s example but is present in the previous example
- <Properties xmlns=”http://core.datamodel.fs.documentum.emc.com/”/> - this is missing in today’s example but is present in the previous example
- filterMode=”SPECIFIED_BY_INCLUDE” - this is missing on the <Profiles> element in today’s example but is present in the previous example
- isProcessIncludedUnknown=”false” - this is present in today’s example but is missing in the previous example
Before we solve the case of the missing header, let’s resolve the differences just listed:
... ObjectIdentity objectIdentity = new ObjectIdentity(); objectIdentity.valueType = ObjectIdentityType.OBJECT_ID; //FIX objectIdentity.valueTypeSpecified = true; //FIX ... propertyProfile.filterMode = PropertyFilterMode.SPECIFIED_BY_INCLUDE; propertyProfile.filterModeSpecified = true; //FIX ... OperationOptions operationOptions = new OperationOptions(); ... operationOptions.Properties = new PropertySet(); //FIX ...
As for the isProcessIncludedUnknown-based difference aforementioned, a quick read of the reference-level documentation for PropertyProfile.setProcessIncludedUnknown() is helpful: “If false, ignore any property in the includeProperties list that is not a property of the repository type. If true, throw an exception if such a property is specified in the includeProperties list. Default value is false.” That is, the WCF-generated proxy makes this default explicity in its SOAP messages; so, although the two XML messages have this difference, they are functionally equivalent.
Next: Leveraging WCF extensibility to add the missing header that the DFS Object service expects…








10 responses so far ↓
1 Developing with DFS in an IDE | Craig's Musings // Sep 3, 2008 at 7:27 am
[...] Essentially we’re working together to promote more standard developer workflow and development lifecycle by embracing tools developers already use to build services. While WTP assumes Eclipse and Composer is indeed powered by Eclipse, that doesn’t mean that Eclipse is the only IDE for Enterprise Content Service development. We’re working on changing the nature of the DFS SDK, too, to better emphasize, for example, how to use Visual Studio and Windows Communication Foundation directly to build service consumers. [...]
2 fsudrew // Oct 9, 2008 at 1:21 pm
A project I am on is trying to implement services using WCF and is having some difficulty getting it to view a document by object_id. The documentation examples are limited in using WCF with DFS. It would be great if you or EMC could post more examples using WCF.
3 Craig Randall // Oct 9, 2008 at 1:32 pm
Thanks, Drew, for your comment. Did you find this 2-part, WCF-based post helpful (i.e. you’re asking for more examples like this)? To be clear, service implementation development using DFS is limited today to Java. That is, you can currently only use WCF for service consumer development. Are you writing a service consumer (application) ? Are you trying to compose a solution that integrates DFS-based services and external WCF-based services? Other?
4 fsudrew // Oct 10, 2008 at 6:41 am
I did find it helpful. I knew that all the service implementation development is currently limited to java which is fine. We are just using the WCF for service consumer development such as calling get and view to build our application. All were trying to do is call the out of the box services from Documentum, but we are having trouble with the view service call within the WCF framework.
5 CorneliaDavis // Nov 11, 2008 at 5:27 pm
Craig, I’m afraid that I might be missing some subtlety. Why did you first craft the code at top and then fix it below? In other words, why not have written it correctly right from the onset? Or is the point that the initial code is what you could write if using the productivity layer but in this case have to make up for not using it with the “FIX”es?
6 Craig Randall // Nov 11, 2008 at 6:13 pm
No subtlety was intended. I suspect that it’s a matter of context (i.e. whether the series of three posts resonates or not). My original intent was to show an example of service consumption using the productivity layer (PL) and then show the same example sans PL. I decided to break the second example into two parts as a way of promoting what the PL does and therefore what the consumer developer must do instead when there is no PL. Although the “2b” post “fixes” the “2a” post, I wouldn’t literally look at the changes as fixes per se. Instead, I characterize PL-vs.-no-PL as follows: PL is all-inclusive service on behalf of the consumer developer while no-PL requires the consumer developer to understand the a la carte “menu” and take responsibility for realizing what’s needed to consume services. Thanks for the feedback.
7 CorneliaDavis // Nov 11, 2008 at 10:01 pm
Thanks Craig. One follow up - I realize that #2b “fixed” #2a but what I was really referring to were the fixes at the bottom of #2a. The code at the top of #2a was not taken from the prior post, right? It was crafted anew just for post #2a, right?
8 Craig Randall // Nov 11, 2008 at 10:09 pm
That’s right. The top code above (i.e. after “Implement your button click handler as follows:”) is different than the code after “Implement your button click handler as follows:” in the preceding post. So the “fixes” captured to the end of this post, plus the code in “2b” post are what is captured in the download available here. Cheers…
9 jgerven // Nov 19, 2008 at 1:33 am
Craig,
Can i install DFS 6.5 on a content server 6.0 SP1? and use the extra services, or do i also have to install content server 6.5 itself?
10 Craig Randall // Nov 19, 2008 at 11:41 am
Hi John. You don’t install DFS onto Content Server per se. You install DFS into a JEE-compliant application server. DFS 6.5 supports Content Server versions 5.3, 6.0 and 6.5, including service packs. In order to maintain separation between Content Server methods running in the Content Server’s embedded application server (i.e. Java Method Server) and also to independently scale your business services based on consumer demand, it’s recommended that you deploy DFS into its own application server or server farm. So, while you can deploy DFS as part of a Content Server installation, you will typically deploy DFS via its standalone, cluster-ready installer that leverages the JBoss application server, or you will deploy DFS into your JEE-compliant application server of choice (e.g. emc-dfs.ear into WebLogic, WebSphere, etc.). Cheers…
You must log in to post a comment.