This article provides a practical example-based introduction to client programming with the out-of-the-box DFS services.
Blue Fish Development Group
701 Brazos St. #700
Austin, TX 78701
(512) 469-9300
This article provides a practical example-based introduction to client programming with the out-of-the-box DFS services.
With the release of Documentum D6, developers now have access to a new programming interface to the Content Server: DFS Web services. Regardless of whether you are a veteran of Web services programming or new to the technology, you will no doubt find DFS powerful and easy to use. Developers who are already familiar with Web services will appreciate having all of the usual benefits of a service-based architecture: flexibility, scalability, reusability, “one-stop shopping”, and so forth. Newcomers to web services, with little ramp-up, should find this model at least as natural to develop with as conventional DFC programming. The purpose of this article is to provide a practical example-based introduction to client programming with the out-of-the-box DFS services.
The first section of the article gives an overview of Web services in general and some tips for making sure your DFS environment is properly configured and running. The latter section of the article contains an example-based introduction to client-side programming with the DFS Web services ObjectService using the java client library.
Over the last few years ‘Web services’ have matured beyond buzzword status and gained wide acceptance in enterprise architectures mainly because they ease technology integration headaches. The goal of service oriented architectures (SOAs) is to provide a one-stop-shop for business service clients by wrapping the proprietary protocols of heterogeneous systems into easy-to-use web services that all speak the same language, namely SOAP (Simple Object Access Protocol) over HTTP. The SOA model allows for rapid client application development because software designers and developers don’t have to ramp-up on the specifics of lower-level proprietary technologies.
Because of their growing position in the corporate enterprise, EMC/Documentum made the logical decision to expose almost all platform functionalities in out-of-the-box Web services.
There are four “core” DFS services that provide access to content repository and other fundamental Documentum platform features:
The DFS architecture includes an object hierarchy specifically designed to efficiently handle the transfer of multiple repository objects among clients and services. For example, when a query result set is returned to a client application by a DFS service, the objects are packaged using this data model. Likewise, when a client creates or modifies repository objects, the new or modified objects are packaged using this data model prior to transfer to the server. The data model also has features which allow it to be tuned so that packages contain only the desired content and metadata.
The DFS data model is comprised of the following elements:
The ServiceFactory is used to construct service instance. As parameters, the ServiceFactory requires the class name of the service to create, the URL to the service WSDL, and a populated ServiceContext.
The ServiceContext object provides stateful information such as repository credentials to a service instance. Once created, ServiceContext instances can be optionally registered with the ContextRegistryService which allows them to be shared among multiple service instances
A ServiceContext instance must be associated with at least one RepositoryIdentity instance. A RepositoryIdentity instance stores a repository name and its login credentials for the service
SOAP (Simple Object Access Protocol) - This is an XML protocol designed specifically for interacting with Web services over HTTP. DFS uses SOAP for all client-server communications. Developer’s may choose to implement DFS clients using SOAP directly, but are more likely to use the provided Java Client Library which abstracts the SOAP request and responses into a friendlier object-oriented programming model. In fact, developers using the Java Client Library need not be versed in SOAP technology at all (and might never even know they’re using it).
WSDL (Web Services Description Language) - WSDL is an XML-based protocol that service consumers can use to ‘discover’ any SOAP-based service’s available requests and responses. It can be said that WSDL provides the service-to-client ‘contract’ for a given service.
DFS publishes WSDL for the core services by default on port 9080 of the content server. For example, on a content server machine named “D6prepp”, the WSDLs for the four “core” DFS services can be found at the following URLs:
Indeed, by directing an open source SOAP utility at the core service URL, we can browse through all of the information published in the DFS WSDLs as shown below:
Figure 1: soapUI Connected to the DFS WSDL
Attaching a third-party client tool to the DFS WSDL is a good way to verify that your Web services are running and available. WSDL requests will also display as XML in a web browser that is directed to the WSDL’s URL.
Setting up a DFS development environment is fairly simple. The services install by default with the content server. For your DFS client project, you’ll just need to reference a few DFS client jar files.
No special installation is needed to deploy DFS on the content server. DFS is installed as part of the base Content Server installation. By default, the WSDLs are published on port 9080.
To setup a DFS client development environment on a machine other than the content server, you need simply unzip the DFS installation package somewhere on your hard drive and add all of the jar files listed in “<unzip root> \client-jars.txt” to your project java build path
Note: Ensure that other versions of the jaxws-related jar files are not listed in your build path. DFS is only compatible with the versions of the files that ship with it.
The following code example shows the end-to-end process of connecting and authenticating with DFS and then retrieving a result set of objects (DataPackages) from the repository and displaying each object’s properties. This example shows the complete end-to-end process of DFS service instantiation, authentication, and interaction in one simple class - an example the author was unable to find anywhere else.
Note: Although not available at the time of writing, a .Net client library is forthcoming from EMC.
This code queries the repository for all documents that have an object name that starts with ‘DFS’ and then outputs their subject and creation date attribute values.
package com.bluefishgroup.sopapilla;
import java.util.Iterator;
import com.emc.documentum.fs.datamodel.core.*;
import com.emc.documentum.fs.rt.ServiceInvocationException;
import com.emc.documentum.fs.rt.context.ContextFactory;
import com.emc.documentum.fs.rt.context.IServiceContext;
import com.emc.documentum.fs.rt.context.ServiceFactory;
import com.emc.documentum.fs.services.core.client.IObjectService;
public class SimpleClient {
public static void main(String [] args) throws ServiceInvocationException {
// Set important variables
String repositoryName = “d6_test1″;
String userName = “userName”;
String userPassword = “passWord”;
try
{
// Get a ContextFactory so that we can
// create a ServiceContext
System.out.println(”Creating ContextFactory”);
ContextFactory contextFactory = ContextFactory.getInstance();
// Use the ContextFactory to create
// a ServiceContext for a service
System.out.println(”Creating ServiceContext”);
IServiceContext serviceContext = contextFactory.newContext();
// Create a RepositoryIdentity to store the
// repository credentials on the ServiceContext
System.out.println(”Creating RepositoryIdentity”);
RepositoryIdentity repoId = new RepositoryIdentity();
// Populate the repository credentials
System.out.println(”Populating RepositoryIdentity”);
repoId.setRepositoryName(repositoryName);
repoId.setUserName(userName);
repoId.setPassword(userPassword);
// Add the populated RepositoryIdentity to the ServiceContext
System.out.println(”Adding RepositoryIdentity to ServiceContext”);
serviceContext.addIdentity(repoId);
// Get an ObjectService from the ServiceFactory
IObjectService objectService = ServiceFactory.getInstance().getRemoteService(IObjectService.class,
serviceContext,
“core”,
“http://d6prepp:9080/services”);
// Query the repository for objects matching our criteria
// Build object qualifications using the criteria expressed in DQL syntax
Qualification qualification1 = new Qualification(”dm_document where object_name = ‘DFS Test 1′”);
Qualification qualification2 = new Qualification(”dm_document where object_name = ‘DFS Test 2′”);
// Create ObjectIdentity objects with our DQL qualifications and repository name
ObjectIdentity targetObjectIdentity1 = new ObjectIdentity(qualification1, repositoryName);
ObjectIdentity targetObjectIdentity2 = new ObjectIdentity(qualification2, repositoryName);
// Create an ObjectIdentitySet that contains the ObjectIdentity objects
ObjectIdentitySet objIdSet = new ObjectIdentitySet();
objIdSet.addIdentity(targetObjectIdentity1);
objIdSet.addIdentity(targetObjectIdentity2);
// Create an empty OperationOptions object
OperationOptions opts = new OperationOptions();
// Create an empty DataPackage to hold the resulting objects
DataPackage dataPackage = new DataPackage();
// Create an empty PropertySet to hold the resulting object properties
PropertySet properties = new PropertySet();
// Execute ObjectService.get() which hopefully
// returns a populated DataPackage
dataPackage = objectService.get(objIdSet, opts);
// Iterate through the DataObject objects in the result DataPackage
Iterator iterator = dataPackage.getDataObjects().iterator();
while(iterator.hasNext()){
System.out.println(”######## GETTING OBJECT PROPERTIES #########”);
DataObject thisDataObject = (DataObject) iterator.next();
// Write out some properties for this DataObject
properties = thisDataObject.getProperties();
System.out.println(”Object Name: ” + properties.get(”object_name”).getValueAsString());
System.out.println(”Creation Date: ” + properties.get(”r_creation_date”).getValueAsString());
}
}
catch (Exception e)
{
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
This is the output generated by the above example java program. Two documents were found and returned based on the specified criteria:
Creating ContextFactory
Creating ServiceContext
Creating RepositoryIdentity
Populating RepositoryIdentity
Adding RepositoryIdentity to ServiceContext
######## GETTING OBJECT PROPERTIES #########
Object Name: DFS Test 1
Creation Date: Mon Aug 06 18:56:26 CDT 2007
######## GETTING OBJECT PROPERTIES #########
Object Name: DFS Test 2
Creation Date: Mon Aug 06 18:58:07 CDT 2007
The first part of the example program creates an instance of a ServiceContext object and populates it with the name and credentials of the target repository. A ServiceContext is always necessary when creating a DFS service because it stores all of the service’s stateful information like repository credentials. ServiceContext objects are created via a ContextFactory:
// Get a ContextFactory so that we can
// create a ServiceContext
ContextFactory contextFactory = ContextFactory.getInstance();
// Use the ContextFactory to create
// a ServiceContext for a service
IServiceContext serviceContext = contextFactory.newContext();
Once the ServiceContext instance has been created, it can be populated with the repository credentials that will be used to authenticate to the repository. The client library provides the RepositoryIdentity object for storing repository credentials. A ServiceContext may be associated with multiple RepositoryIdentity objects, but only one per repository. Here we set the repository name, user name, and password:
// Create a RepositoryIdentity to store the
// repository credentials on the ServiceContext
RepositoryIdentity repoId = new RepositoryIdentity();
// Populate the repository credentials
repoId.setRepositoryName(repositoryName);
repoId.setUserName(userName);
repoId.setPassword(userPassword);
Now that the RepositoryIdentity has been populated, it is added to the ServiceContext:
// Add the populated RepositoryIdentity to the ServiceContext
serviceContext.addIdentity(repoId);
Now that the ServiceContext has been populated, it can be passed to the ServiceFactory for creation of a service instance, in this case an ObjectService. These are the arguments to the getRemoteService() method:
// Get an ObjectService from the ServiceFactory
IObjectService objectService =
ServiceFactory.getInstance().getRemoteService(IObjectService.class,
serviceContext,
"core",
"http://d6prepp:9080/services");
The ObjectService instance is now instantiated and can be used to get repository objects. Identifying the repository objects requires a Qualification to define the identification criteria. In this case, we will use DQL qualifications to find documents having known object names.
// Build object Qualifications using the criteria expressed in DQL syntax
// Note that each qualification should match only one object in the repository!
Qualification qualification1 = new Qualification("dm_document where object_name = 'DFS Test 1'");
Qualification qualification2 = new Qualification("dm_document where object_name = 'DFS Test 2'");
In order to pass the qualifications to the ObjectService, we must first associate them with ObjectIdentity instances and then add ObjectIdentity instances to the ObjectIdentitySet (which is the first argument to the ObjectService’s get() command):
// Create ObjectIdentity objects with our DQL qualifications and repository name
ObjectIdentity targetObjectIdentity1 = new ObjectIdentity(qualification1, repositoryName);
ObjectIdentity targetObjectIdentity2 = new ObjectIdentity(qualification2, repositoryName);
// Create an ObjectIdentitySet that contains the ObjectIdentity objects
ObjectIdentitySet objIdSet = new ObjectIdentitySet();
objIdSet.addIdentity(targetObjectIdentity1);
objIdSet.addIdentity(targetObjectIdentity2);
Now we’ll create three empty object instances in preparation for the ObjectService get() call:
// Create an empty OperationOptions object
OperationOptions opts = new OperationOptions();
// Create an empty DataPackage to hold the resulting objects
DataPackage dataPackage = new DataPackage();
// Create an empty PropertySet to hold the resulting object properties
PropertySet properties = new PropertySet();
Finally, it’s time to execute the ObjectService get() call:
// Execute ObjectService.get() which hopefully
// returns a populated DataPackage
dataPackage = objectService.get(objIdSet, opts);
The call to the ObjectService returns a DataPackage which must be iterated through to access each contained DataObject. Each DataObject contains a PropertSet which an be used to access individual property values, in this case by using the getValueAsString() method. Here we iterate through the returned DataObjects and write out the object name and creation date for each:
// Iterate through the DataObject objects in the result DataPackage
Iterator iterator = dataPackage.getDataObjects().iterator();
while(iterator.hasNext()){
System.out.println("######## GETTING OBJECT PROPERTIES #########");
DataObject thisDataObject = (DataObject) iterator.next();
// Write out some properties for this DataObject
properties = thisDataObject.getProperties();
System.out.println("Object Name: " + properties.get("object_name").getValueAsString());
System.out.println("Creation Date: " + properties.get("r_creation_date").getValueAsString());
}
In this article, we’ve taken an introductory look at Web services and client programming with DFS. Now that you’ve got the basics under your belt, try exploring DFS on your own. For more information about developing with DFS, please see the EMC document, DFS Development Guide.
The code example in this article was developed and tested by the author. If you have any problems or questions about this article or its example code, please contact the author at gtarrant@bluefishgroup.com.
Thanks for this. There is always one document that levels out the learning curve enough to actually see some direction. This one did it for me.
Thanks again!
Steve Davidson | November 13th, 2007 2:02 pm
I get some errors using this code:
he first:
Get object failed because multiple objects qualify for the predicate “dm_folder” in docbase
Happens when the qualification is “dm_folder”.
The second comes when I target a single folder by selecting on object_name, I don’t retrieve the r_ properties.
Rob Au | January 4th, 2008 4:04 am
In follow up to my previous post; if all the properties should be returned, this has to be added to a propertiesprofile in the operation options:
PropertyProfile propertyProfile = new PropertyProfile();
propertyProfile.setFilterMode(PropertyFilterMode.ALL);
opts.setPropertyProfile(propertyProfile);
Rob Au | January 4th, 2008 4:16 am
What an excellent introduction to a new technology area!
Andrew Webster | January 15th, 2008 6:06 am
I got an error on the code too. It is the same error as reported by Rob Au -
Get object failed because multiple objects qualify for the predicate “dm_document where object_name = Target.doc”
I set my qualification as “dm_document where object_name = ‘Target.doc’”.
I have multiple Target.doc objects in my docbase.
When I delete all except for one Target.doc object, then the code runs without problem.
Is there something obvious I’m missing?
Dumdee Foo | February 29th, 2008 12:35 pm
I get the following error trying to run this code:
Caused by: java.lang.ClassCastException: com.sun.xml.bind.v2.runtime.JAXBContextImpl cannot be cast to com.sun.xml.internal.bind.api.JAXBRIContext
at com.sun.xml.internal.ws.fault.SOAPFaultBuilder.(Unknown Source)
Marc Bryant | April 14th, 2008 4:16 pm
You must be logged in to post a comment.
Subscribe to our newsletter to be notified when new articles are posted. You can unsubscribe at any time.
Nice article ,gives highlevel overiew on D6-DFS ,expounded all features in brief with good example.
Ram Nagi Reddy | November 13th, 2007 5:28 am