The document management environment in IBM® WebSphere® Portal 6 includes the Document Manager portlet and Manage Document Libraries portlet, applications used for managing documents, folders, and document libraries. Although these applications provide rich functionality, including document sharing, versioning, workflow, and locking, the need may arise to develop a custom application to work with this data.
This article shows you how to use the Content Model Access builder, introduced in IBM WebSphere Portlet Factory 6.0.1, to create services that perform create, retrieve, update and delete operations on documents and folders stored in Portal's Document Manager. The article also discusses and includes a working sample that you can import into Portlet Factory and run against a document library configured in Portal 6. To aid in understanding the Content Model Access builder and the sample, we'll start with a brief overview of Portal's Content Model.
Overview of Content Model in WebSphere Portal 6
WebSphere Portal 6 introduced the Content Model, a programming model that provides applications with access to collaborative content. Though you do not have to deal with it directly, the underlying repository is an implementation of JSR 170 (Java Content Repository, or JCR).
Data managed by the Content Model has a type, and the Content Model defines common metadata across content types (such as title, description and createdDate) as well as common aggregation patterns (folder type can contain folders and documents).
Since the applications you will build will typically perform CRUD operations on documents, folders, and document libraries, you will deal with these three Content Model types:
|Description ||Content Model Type |
|CollaborativeDocument ||Represents documents stored in the Document Manager |
|Folder ||Container type representing folders stored in the Document Manager |
|DocumentLibrary ||Container type representing a document library |
As you will soon see, you will add a separate Content Model Access builder to your model for each Content Model type you wish to operate on in your application. For instance, the provider model in this sample contains two Content Model Access builder calls: one for operating on documents and the other for operating on folders.
For links to other articles on Portal's Content Model, see the Resources section below.
Since we now have a general background of Portal's Content Model, we can now look at the Content Model Access Builder and the sample application.
Overview of the Content Model Access Builder
As mentioned earlier, you will add a Content Model Access builder to your model for each type of data you wish to operate on. The "Document Type" input allows you to select the type of data you wish to work with.
Given the type, the builder contains a Search Criteria section that allows you to further refine how the data will be retrieved. These inputs can be overridden as builder inputs or retrieved from a variable at runtime.
The builder also allows you to control how the binary content of a CollaborativeDocument should be handled for document search results. For example, when displaying a list of documents, you will typically only display a document's metadata, such as title, description, and author; the binary data of the document is not needed until you view the details of the document. The builder allows you to omit the binary data of the document in the search results.
To download the binary content of a document, the builder provides a downloadDocumentContent() method. Using the document id set in the builderNameGetCriteria variable, this method will stream the binary content of the document to the browser.
To aid in uploading documents into the Document Manager, you can leverage Portlet Factory's existing File Upload builder. Simply specify the tag name you used in the File Upload builder in the Content Model Access builder's "File Upload Tag" input, and the builder will automatically handle storing the binary on the document.
The builder will create the following data services:
- findDocuments - Retrieves a list of items (including documents and folders) from the Content Model.
- createDocument - Creates a new item in the Content Model
- createDocumentWithCriteria - Same as createDocument, but with a single input parameter that encapsulates the two input parameters for createDocument. If you expose a service of this type in a Service Provider model, use the createDocumentWithCriteria service rather than the createDocument service.
- getDocument - Retrieves an item from the Content Model using the id stored in the Detail variable
- updateDocument - Updates an item in the Content Model
- deleteDocument - Deletes an item in the Content Model
The sample accesses the default document library that is automatically configured in Portal; therefore, you can deploy and run it with no configuration.
As with any service provider model, the Service Definition and Service Operation builders are used to define the create, retrieve, update and delete services for content.
The sample application, shown in Figure 1 and Figure 2, demonstrates browsing, creating, retrieving, updating, and deleting documents and folders in a Document Library.
Figure 1: Screenshot of the consumer model displaying folders and documents in a document library
Figure 2: Screenshot of displaying the metadata of a document. Clicking the Download Doc Content button will stream the contents of the file to the browser.
Techniques illustrated in this sample
The following techniques are illustrated in this sample:
Using the Content Model Access builder to create a service with create, retrieve, update, and delete (CRUD) functionality.
The service provider contains operations for each of the CRUD operations. The inputs to these operations are exposed as service operation input parameters.
Streaming the binary contents of a document to the browser
In addition to metadata, the CollaborativeDocument type in Document Manager contains a property for storing the binary content of a document. Typically, upon a specified action such as clicking a link or button, you'll want to stream the binary contents of a document to the browser. The downloadDocumentContent LJO method provided by the Content Model Access builder is responsible for this.
Using the Input Form and File Upload builders for creating new documents.
The Input Form builder, new in Portlet Factory 6.0.1, is used in the sample to generate a form for entering the metadata for a new document. The input variable used for generating the input fields is set to a schema typed variable representing the document.
Since much of the metadata of a document is managed by the Content Model and should not be modified, a Rich Data Definition builder hides some fields or sets them to read-only. For metadata, this sample allows the user to specify the title, description, and mime type of the document, as shown in Figure 3.
The sample also uses Portlet Factory's File Upload builder to upload a document to the Content Model. The File Upload control is placed beneath the metadata table on the page created by the Input Form builder called newDocument_InputPage.
Figure 3: Screenshot of the form for creating a new document.
Abstracting retrieval of folders and documents with a single service call
Document and folder types in the Document Manager share mostly common metadata, including title, description, createdDate, and path. Since this metadata is common, it is possible to create a single service call that returns both folders and documents.
This section shows how this was accomplished in the sample by defining a common schema for documents and folders, a schema typed variable to store the results of the service call, a method that retrieves document and folders, and a method that merges the results into a common result structure.
Creating a common schema type
As shown in Figure 4, the provider model contains a Schema builder that defines a new schema, ItemSchema, which contains the metadata common to documents and folders. It was created by copying and pasting elements from the schemas generated by the Content Model Access builder.
It is not easy to differentiate between folders and documents since much of the metadata is common for documents and folders. The consumer model needs the ability to differentiate between folders and documents. For example, the consumer model in this sample needs to display a folder icon next to folders and a document icon next to documents. To differentiate between folders and documents, an additional element, isDocument, was manually added to ItemSchema. As you'll see shortly, we'll set the value of this element to differentiate between folders and documents.
Figure 4: Creating a schema type common to documents and folders.
Creating a variable to hold the results
The provider model contains ItemSearchResults, a variable of type ItemSchema. This variable holds the results of the common service to retrieve folders and documents.
Merging the document and folder search results into a single result structure
To retrieve the documents and folders and merge the results into a common result variable of type ItemSchema, the provider model defines an Action List called getItemsInternal, shown in Figure 5. This Action List is the target of the service operation. For both of the Content Model Access builders in the model, the Action List sets their search criteria variable and calls their respective findDocument data services. Next it calls a merge method (shown shortly) to combine the folder and document results into a single ItemSearchResults variable. Finally, it returns the ItemSearchResults variable.
Figure 5: getItemsInternal sets the search criteria inputs, makes the service calls to retrieve documents and folders, merges the results into a single result structure, and returns the results
The following code sample shows the merge method, defined in a Method builder. The code walks the results of service calls to retrieve folders and documents. For each result, it sets the isDocument element appropriately and adds the result to the ItemSearchResults variable.
IXml itemSearchResults = (IXml) webAppAccess.getVariables().getVariable("ItemSearchResults").getValue();
// For each folder result, copy over basic properties. Add isDocument element and set to false.
IXml folderResults = (IXml) webAppAccess.getVariables().getVariable("FolderSearchResults").getValue();
for (Iterator folderIter = folderResults.getChildren().iterator(); folderIter.hasNext(); )
IXml itemResult = itemSearchResults.addChildElement("Item");
IXml elem = null;
IXml folder = (IXml) folderIter.next();
if ( ( elem = folder.findElement("id")) != null ) itemResult.addChildElement( elem );
if ( ( elem = folder.findElement("path")) != null ) itemResult.addChildElement( elem );
if ( ( elem = folder.findElement("label")) != null ) itemResult.addChildElement( elem );
if ( ( elem = folder.findElement("title")) != null ) itemResult.addChildElement( elem );
if ( ( elem = folder.findElement("description")) != null ) itemResult.addChildElement( elem );
// For each doc result, copy over basic properties. Add isDocument element and set to true.
IXml docResults = (IXml) webAppAccess.getVariables().getVariable("CollaborativeDocumentSearchResults").getValue();
for (Iterator docIter = docResults.getChildren().iterator(); docIter.hasNext(); )
IXml itemResult = itemSearchResults.addChildElement("Item");
IXml elem = null;
IXml doc = (IXml) docIter.next();
if ( ( elem = doc.findElement("id")) != null ) itemResult.addChildElement( elem );
if ( ( elem = doc.findElement("path")) != null ) itemResult.addChildElement( elem );
if ( ( elem = doc.findElement("label")) != null ) itemResult.addChildElement( elem );
if ( ( elem = doc.findElement("title")) != null ) itemResult.addChildElement( elem );
if ( ( elem = doc.findElement("description")) != null ) itemResult.addChildElement( elem );
Exposing a common service operation to retrieve documents and folders
Finally, the getItems Service Operation exposes the common service call. It delegates responsibility to the getItemsInternal Action List. Also, since the operation returns the common result structure, ItemSearchResults is selected for the Result Schema input.
Using isDocument schema element to determine the appropriate service operation to call
In addition to determining what icon image to show next to a result, the consumer uses the added isDocument element of the custom ItemSchema to determine what action to call when the title of a result link is clicked. This is demonstrated in the openItem Action List in the consumer model.
If isDocument is true, the getDocument service operation in the provider model is called and the document details page is displayed. Otherwise, the result is a folder. The current path variable is reset to the path of the folder, getItems is called again, and the view page is displayed.
Notes on running the sample and prerequisites
This sample requires:
- WebSphere Portlet Factory Version 184.108.40.206. (The sample will not work on Portlet Factory 6.0.1 and earlier)
- WebSphere Portal 6.0 or later
You should have a basic familiarity with Portlet Factory and be able to create and run Portlet Factory models. You should also be familiar with the Document Manager in Portal 6. For information on the Document Manager, see the IBM WebSphere Portal Version 6.0 Information Center.
Also, please read the Content Model Access builder help.
To run the sample application:
- Create a new Portlet Factory project.
- During project creation, add the Integration Extensions \ Content Model Extension feature set.
- For deployment configuration, specify a valid Portal Server Deployment Configuration.
- Important: when prompted to deploy the new project, select yes. The project must be deployed before importing the sample.
- Download the sample ZIP file and import it into the project using the File > Import > WebSphere Portlet Factory Archive. Your project will now contain a consumer model and provider model located in models/samples/content_model_service.
- In your project's cluster.properties or override.properties, set bowstreet.upload.enabled=true. Also, specify an appropriate value for bowstreet.upload.maxFileSizeK. These settings are required by the File Upload Builder used in the sample.
- Rebuild and redeploy the Portlet WAR.
- Log in to WebSphere Portal as the administrator and deploy the "Sample Content Model Service" portlet to a page.
- Visit the page to run the consumer application.
The following resources may be helpful in the context of this article:
Introducing the WebSphere Portal V6.0 Content API
IBM WebSphere Portal Version 6.0 Information Center