OpenText Documentum REST Services CE 21.3 - Development Guide English (EDCPKRST210300-PGD-En-01)
OpenText Documentum REST Services CE 21.3 - Development Guide English (EDCPKRST210300-PGD-En-01)
OpenText Documentum REST Services CE 21.3 - Development Guide English (EDCPKRST210300-PGD-En-01)
Services
Development Guide
EDCPKRST210300-PGD-EN-01
OpenText™ Documentum™ REST Services
Development Guide
EDCPKRST210300-PGD-EN-01
Rev.: 2021-July-05
This documentation has been created for OpenText™ Documentum™ REST Services CE 21.3.
It is also valid for subsequent software releases unless OpenText has made newer documentation available with the product,
on an OpenText website, or by any other means.
Tel: +1-519-888-7111
Toll Free Canada/USA: 1-800-499-6544 International: +800-4996-5440
Fax: +1-519-888-0677
Support: https://support.opentext.com
For more information, visit https://www.opentext.com
One or more patents may cover this product. For more information, please visit https://www.opentext.com/patents.
Disclaimer
Every effort has been made to ensure the accuracy of the features and techniques presented in this publication. However,
Open Text Corporation and its affiliates accept no responsibility and offer no warranty whether expressed or implied, for the
accuracy of this publication.
Table of Contents
1 Overview ................................................................................... 17
1.1 Understanding RESTful programming .............................................. 18
1.2 Relations with other Documentum platform APIs ............................... 18
5 Authentication ......................................................................... 89
5.1 End user tracking ............................................................................ 89
5.2 HTTP basic authentication ............................................................... 90
5.2.1 Enabling HTTP basic authentication ................................................. 92
5.2.2 User credential mapping .................................................................. 92
5.2.3 Known limitations ............................................................................ 93
5.3 Kerberos authentication ................................................................... 93
5.3.1 Authentication workflow ................................................................... 93
5.3.2 Multi domain support within a forest .................................................. 95
5.3.3 Setup SPNEGO-based Kerberos ..................................................... 96
5.3.3.1 Documentum Server configuration for SPNEGO-based Kerberos ....... 96
5.3.3.2 REST server configuration for SPNEGO-based Kerberos .................. 97
5.3.3.2.1 Register and map the Service Principal Name ................................... 97
5.3.3.2.2 JAAS configuration .......................................................................... 98
5.3.3.2.3 Configuring runtime properties ....................................................... 102
5.3.3.2.3. JAAS configuration in WebLogic .................................................... 102
1
5.3.3.3 Recommendations on web browser configuration for SPNEGO-
based Kerberos ............................................................................ 103
5.3.3.3.1 Verify browser settings .................................................................. 105
5.3.3.4 Troubleshooting and diagnostics .................................................... 105
5.3.3.4.1 Retrieve Debug Information ........................................................... 106
5.3.3.4.2 Common Errors ............................................................................ 107
5.4 CAS authentication ....................................................................... 108
5.4.1 Overview ...................................................................................... 108
5.4.1.1 Terminology .................................................................................. 108
5.13.4.1 Algorithms with a Key Size Lager than 128 bits ............................... 177
5.14 Bypassing browser login forms upon HTTP 401 unauthorized .......... 177
Documentum REST Services is a set of RESTful web service interfaces that interact
with the Documentum platform.
• XML
• JSON
• JSON HAL (Hypertext Application Language)
Note: The following are the terminologies used and their explanation:
You can access the source code of Documentum REST Services sample clients on
GitHub wesbsite. These client samples help demonstrate some critical aspects and
programming techniques that can be used to build applications and Web APIs that
consume Documentum REST Services.
Note: The source code of these REST client samples is made available to the
public through the Apache 2 license.
Authorization:
Basic
QWxhZGRpbjpvcGVu
IHNlc2FtZQ==
Or, Kerberos
authentication
Header with the
credential part
encoded, for
example:
Authorization:
Negotiate
YIIZG1hZG1pbjpwY
XNzd29yZ...
Accept Acceptable media Request See “Supported
type for the Response MIME types”
body on page 26
E_INPUT_MESSAGE_
NOT_READABLE
For more
information, see
“Supported MIME
types” on page 26
Content-Length Size of the entity- Request/Response Any number greater
body, in decimal than or equal to 0
number of OCTETs (zero). Negative
sent to the recipient numbers are not
valid values
Location URI of the newly- Response URI
created resource
Set-Cookie Sets an HTTP cookie Response Used by client token
Response
WWW-Authenticate Indicates the Response Used by HTTP basic
authentication authentication and
scheme that should HTTP Kerberos
be used to access the negotiate
requested entity authentication Set-
Cookie
ETag ETag value generated Response String value
by the REST server
If-None-Match Checks whether the Request ETag value in the
resource is changed previous server's
Response
Last-Modified Last modified time of Response HTTP-Date
the resource
If-Modified-Since Checks whether the Request Last-Modified value
resource is changed in the previous
since last modified server's Response
time
Note: Each resource has a limited number of supported parameters. For more
information on a resource’s supported query parameters, see the resource.
In your
production
environme
nt, you can
set this
parameter
to false
for better
performan
ce
For example, if
you set items-
per-page to
200, and page to
2, the operation
returns items 201
to 400
view Specifies the String See “Property default
object properties view”
to retrieve. on page 59.
Caut
ion
This
param
eter
works
only
when
inlin
e is set
to
true
Each sort
specification
consists of a
property (any
non-repeating
property) by
which to sort the
results and its
sort order (DESC
or ASC),
separated by the
whitespace
character ( ).
If any property
with an invalid
name is
specified, an
error is thrown
Example:
sort=r_
modify_date
desc,object_
id asc,title
links Determines boolean • true - return true
whether or not link relations
to return link • false - do
relations in the not return
object link relations
representation
This parameter
works only
when inline is
set to true
Notes
• When the client does not specify the Accept Header for the expecting Response,
the REST server uses the application/vnd.emc.documentum+json MIME
type by default
• When the client does not specify the Content-Type Header for a PUT or POST
Request, the REST server rejects the Request with the HTTP status code 415
• In some scenarios, the MIME types that Documentum REST Services supports are
not limited to the types in this table. For more information about other supported
types, see “Other types” on page 26
• application/home+json
• application/home+xml
When you use a URL extension in a GET operation, the URL extension takes
precedence over the Accept Header. The content type of the return varies with the
URL extension as follows:
URL extensions in POST and PUT operations are ignored. The REST server reads the
media type from the Content-Type Header.
URLs in the resource representations are in the form of an absolute path. When the
REST server is deployed behind a reverse proxy server or a load balancer, you must
configure the proxy rules to replace the backend hostname with the front hostname.
For example, replace the following reverse proxy configuration in the Apache HTTP
server maps http://internal-node1.acme.com:8080/dctm-rest to http://
reverse-proxy-server:80.
ProxyPass / http://internal-node1.acme.com:8080/dctm-rest/
ProxyPassReverse / http://internal-node1.acme.com:8080/dctm-rest/
<Location/>
AddOutputFilterByType SUBSTITUTE application/xml
Substitute "s|internal-node1.acme.com:8080/dctm-rest| reverse-proxy-server |i"
</Location>
<Location/>
AddOutputFilterByType SUBSTITUTE application/json
Substitute "s|internal-node1.acme.com:8080/dctm-rest| reverse-proxy-server |i"
</Location>
<Location/>
AddOutputFilterByType SUBSTITUTE application/atom+xml
Substitute "s|internal-node1.acme.com:8080/dctm-rest| reverse-proxy-server |i"
</Location>
<Location/>
AddOutputFilterByType SUBSTITUTE application/vnd.emc.documentum+xml
Substitute "s|internal-node1.acme.com:8080/dctm-rest| reverse-proxy-server |i"
</Location>
<Location/>
AddOutputFilterByType SUBSTITUTE application/vnd.emc.documentum+json
Substitute "s|internal-node1.acme.com:8080/dctm-rest| reverse-proxy-server |i"
</Location>
<Location/>
AddOutputFilterByType SUBSTITUTE text/html
Substitute "s|internal-node1.acme.com:8080/dctm-rest| reverse-proxy-server |i"
</Location>
For more information about URI encoding in Documentum REST Services, see the
RFC 3986.
Note: Documentum REST Services does not support the <OPTIONS> method
except in Cross-Origin Resource Sharing preflight Requests. For more
information, see the section titled Cross-Origin Resource Sharing (CORS) support
in this guide.
Do not use this method to request a list of available operations from a resource.
The OpenText Documentum REST Services Reference Guide provides detailed
information about the operations that can be performed on each resource.
2.8 Representation
Documentum REST Services supports two representation formats, JSON and XML.
For more information, see the REST Services Data Access API.
JSON supports basic data types. Therefore, Documentum properties are mapped to
JSON data types as shown in the following table:
Table 2-1: Mapping between Documentum property data type and JSON data
type
XML is the other format for resource representation in Documentum REST Services.
Like the JSON format, collection-based resources are presented as feeds, defined by
Atom.
The following sample illustrates a feed (EDAA feed) of the Cabinets resource in
JSON. Note that only a URI of the single resource is presented in this sample as
inline is not enabled.
The following sample illustrates a feed of the Cabinets resource in XML. Note that
only a URI of the single resource is presented in this sample as inline is not
enabled.
"owner_name": "acme01",
"owner_permit": 7,
"group_name": "docu",
"group_permit": 5,
"world_permit": 3,
"log_entry": "",
"acl_domain": "acme01",
"acl_name": "dm_450004d280000100",
"language_code": "",
"r_object_type": "dm_cabinet",
"r_creation_date": "2012-10-15T15:27:30.000+08:00",
"r_modify_date": "2012-10-15T15:27:30.000+08:00",
"a_content_type": "",
"authors": null,
"r_lock_owner": "",
"i_antecedent_id": "0000000000000000",
"i_chronicle_id": "0c0004d280000104",
"i_folder_id": null,
"i_cabinet_id": "0c0004d280000104"
},
"links": [
{
"rel": "self",
"href": "http://localhost:8080/dctm-rest/repositories/acme01/
cabinets/0c0004d280000104"
},
{
"rel": "edit",
"href": "http://localhost:8080/dctm-rest/repositories/acme01/
cabinets/0c0004d280000104"
},
{
"rel": "http://identifiers.emc.com/documentum/linkrel/delete",
"href": "http://localhost:8080/dctm-rest/repositories/acme01/
cabinets/0c0004d280000104"
},
{
"rel": "http://identifiers.emc.com/documentum/linkrel/folders",
"href": "http://localhost:8080/dctm-rest/repositories/acme01/
folders/0c0004d280000104/folders"
},
{
"rel": "http://identifiers.emc.com/documentum/linkrel/documents",
"href": "http://localhost:8080/dctm-rest/repositories/acme01/
folders/0c0004d280000104/documents"
},
{
"rel": "http://identifiers.emc.com/documentum/linkrel/objects",
"href": "http://localhost:8080/dctm-rest/repositories/acme01/
folders/0c0004d280000104/objects"
},
{
"rel": "http://identifiers.emc.com/documentum/linkrel/child-links",
"href": "http://localhost:8080/dctm-rest/repositories/acme01/
folders/0c0004d280000104/child-links"
},
{
"rel": "http://identifiers.emc.com/documentum/linkrel/relations",
"href": "http://localhost:8080/dctm-rest/repositories/acme01/
relations?related-object-id=0c0004d280000104&
related-object-role=any"
}
]
},
"links": [
{
"rel": "edit",
"href": "http://localhost:8080/dctm-rest/repositories/acme01/
cabinets/0c0004d280000104"
}
],
"thumbnail": {
"url": "http://localhost:8081/thumbsrv/getThumbnail?object_type=dm_cabinet
&format=&is_vdm=false&repository=1234"
}
}
]
<dm:link rel="http://identifiers.emc.com/documentum/linkrel/objects"
href= "http://localhost:8080/dctm-rest/repositories/
acme01/folders/0c0004d280000104/objects" />
<dm:link rel="http://identifiers.emc.com/documentum/linkrel/child-links"
href= "http://localhost:8080/dctm-rest/repositories/
acme01/folders/0c0004d280000104/child-links" />
<dm:link rel="http://identifiers.emc.com/documentum/linkrel/relations"
href= "http://localhost:8080/dctm-rest/repositories/acme01/
relations?related-object-id=0c0004d280000104&
related-object-role=any" />
</dm:links>
</dm:cabinet>
</content>
<link rel="edit"
href= "http://localhost:8080/dctm-rest/repositories/acme01/cabinets/
0c0004d280000104" />
<media:thumbnail url="http://localhost:8081/thumbsrv/getThumbnail?object_type=
dm_cabinet&format=&is_vdm=false&repository=1234"/>
</entry>
Not all resources have the same structure of the representation. For example, the
Repository resource representation does not have the type or properties keys.
--314159265358979
Content-Disposition: form-data; name=metadata
Content-Type: application/vnd.emc.documentum+json
--314159265358979
Content-Disposition: form-data; name=metadata
Content-Type: application/vnd.emc.documentum+xml
This is a sample
--314159265358979--
• HTTP Status code (mandatory) - identical to the status code in the Header
• REST Error Message (mandatory) - descriptive message for the error code
• Root Causes (optional) - a set of error code/message mappings of the under layer
• REST Error Id (mandatory) - a unique identifier of the error in the log
Documentum REST Services supports the JSON and XML formats in error
responses.
In a batch request, you can control the transaction behavior of the batch by using the
transactional property.
When performing a POST operation to create a resource, you can use the type
property in JSON or the xsi:type property in XML to specify the object type of the
resource. For Sysobject and its sub types, you can also use the r_object_type property
to specify the object type. When the value of type (or xsi:type) and that of
r_object_type are not consistent, the value of type (or xsi:type) takes precedence.
rest-api-runtime.properties.template
This file holds the default settings for all runtime properties. Do not modify the
content in this file.
rest-api-runtime.properties
Out-of-the-box, this file contains no settings, indicating that all properties use their
default values. When you need to modify the value of a certain runtime property,
add an entry in this file, specifying the name and value of the property. Settings in
this file override the settings in rest-api-runtime.properties.template.
There are two runtime profiles that control how error messages are shown. The two
runtime profiles are development and production. This property specifies which
runtime profile is used for error messages.
For more information, see the Batches and Batch Capabilities section in the OpenText
Documentum REST Services Reference Guide.
Documentum REST Services is certified for use on the following application servers:
• Apache Tomcat
• VMware vFabric tc Server
• Oracle WebLogic Server
• IBM WebSphere
• JBOSS Enterprise Application Platform
Documentum REST Services release 7.3 and later can be deployed within a Docker
container. For detailed information on how to deploy Documentum REST Services,
see the OpenText Documentum Platform and Platform Extensions Installation Guide.
To see all available configuration parameters, see the template file, rest-api-
runtime.properties.template, which is located at <dctm-rest.war>\WEB-INF\
classes\rest-api-runtime.properties.template.
For capturing logs and changing the logging level, use the properties file, log4j.
properties, which is located at <dctm-rest.war>\WEB-INF\classes\log4j.
properties.
Several resources have been improved to allow you to create or import an object and
attach the Aspects and Lifecycle to that object. These include the following:
4.1.1 Request
Request media types
• application/vnd.emc.documentum+xml
• application/vnd.emc.documentum+json
• The creation step (where the object, document, folder, or cabinet is created)
• Attaching Aspects and setting their values
• Attaching a Lifecycle to the newly created object, document, folder, or cabinet
as the case may be
<object_name>test ooo</object_name>
<r_object_type>dm_document</r_object_type>
<rest_aspect.rest_str>new created aspect string</rest_aspect.rest_str>
<rest_aspect.rest_r_int>
<item>123456</item>
</rest_aspect.rest_r_int>
</properties>
<object-lifecycle>
<lifecycle-id>4600000580004d14</lifecycle-id>
<current-state>draft</current-state>
<aliasset-name>doc-as</aliasset-name>
</object-lifecycle>
<object-aspects>
<aspect>rest_aspect</aspect>
</object-aspects>
</object>
• Manually attach the <Lifecycle> to the new object by using the object-lifecycle
resource
• Manually drop the newly created object from the system.
Caution
It is extremely important that you complete one of the two above
procedures. Otherwise, you will leave behind an orphaned object in your
system.
• “Literals” on page 48, which describes the literal formats in filter expressions
• “Filter expression functions” on page 51, which describes the functions in filter
expressions
• “Logical operators” on page 55, which describes the logical operators
supported by filter expressions
• “Comparison operators” on page 55, which describes the comparison operators
supported by filter expressions
Notes
from the type specified by <object-type> parameter. For example, GET requests
to this URL returns all cabinets of sub type <custom_cabinet> with a
<custom_region> sub type attribute that equals <apj>: /repositories/REPO/
cabinets?inline=true&object-type&filter=custom_region='apj'
4.2.1 Literals
Literals are values that are interpreted by the server exactly as they are entered.
Filter expressions introduce three types of literals:
An Integer literal specifies any whole number and is expressed in the following
format:
[+ | -] n
A floating point literal specifies any number that contains a decimal point and is
expressed in the following format:
If a string literal is enclosed in a pair of single quotes, the double quote character (“)
can be included as a part of the literal without any change. For example:
'foo"bar'
Similarly, if a string literal is enclosed in a pair of double quotes, the single quote
character (“) can be included as a part of the literal without any change. For
example:
However, to include a single quote character (') as a part of a literal that is enclosed
in a pair of single quotes, you must include the single quote character twice. For
example:
Similarly, to include a double quote character (") as a part of a literal that is enclosed
in a pair of double quotes, you must include double quote character twice. For
example:
"foo""bar"
• date ("YYYY-MM-DD")
• date ("YYYY-MM-DDThh:mm:ss.[sss][TZD]")
Where:
– If this field is set to the special UTC designator (“Z”) or unspecified, the time
is expressed in UTC (Coordinated Universal Time)
– The time is expressed in local time, together with a time zone offset in hours
and minutes, which represents the difference between the local time and UTC
Notes
• date ("2013-01-01")
Because the time field is not specified. The time 00:00:00 is assumed. This
literal equals to date ("2013-01-01T00:00:00Z").
• date ('2007-07-16T19:20:30.45')
Because the TZD field is not specified, this DateTime literal is expressed in
UTC. This literal equals to date ('2007-07-16T19:20:30.45Z').
• date ("2007-07-16T19:20:30.45Z")
• date ("2007-07-16T19:20:30.45+08:00")
• date ('2007-07-16T19:20:30.45-03:00')
This function is a boolean term that returns True if the specified property starts with
a specified literal string, and False otherwise.
Syntax:
starts-with(<property-name>, "<string-literal>")
Arguments:
• property-name: Specifies the property whose starting string this function tests
• string-literal: String literal with which the starting literal of the specified
property is compared.
starts-with(object_name, "foo")
By using this filter expression, the Request only returns objects whose object_
name starts with foo.
This function is a boolean term that returns True if the property contains the
specified string, and False otherwise.
Syntax:
contains(<property-name>, "<string-literal>")
Arguments:
contains(object_name, 'hello')
By using this filter expression, the Request only returns objects whose object_
name contains hello.
This function is a boolean term that returns True if the property lies between the
specified range, and False otherwise.
Syntax:
Arguments:
– Datetime
– Numeric
• to: The highest value in the range that this function evaluates. This argument can
be one of the following types:
– Datetime
– Numeric
between(r_link_cnt, 1, 5)
By using this filter expression, the Request returns objects whose r_link_cnt is
no less than 1 and no greater than 5.
By using this filter expression, the Request only returns objects whose r_
modify_date is no earlier than 2013-01-16 and no later than 2013-01-18.
This function is a boolean term that returns True if the object is an instance of the
specified type or its subtype, and False otherwise.
Syntax:
type(<type-name>)
Arguments:
type(dm_folder)
By using this filter expression, the Request only returns objects of the dm_
folder type and objects of any subtype of dm_folder.
This function is a boolean term that returns True if the value of the specified
property is null, and False otherwise.
Syntax:
nilled(<property-name>)
Arguments:
nilled(object_name)
By using this filter expression, the Request only returns objects whose object_
name is null.
Syntax:
starts-with(scalar-function(<attribute-name>), <string-literal>)
...
contains(scalar-function(<attribute-name>), <string-literal>)
...
Arguments:
With this filter expression, the Request only returns objects whose object_
name starts with the string hello, case insensitive.
starts-with(upper(object_name), 'HELLO')
With this filter expression, the Request only returns objects whose object_
name contains the string hello, case insensitive.
contains(lower(object_name), 'hello')
With this filter expression, the Request only returns the objects whose object_
name begins with the characters 'c', 'd', or 'e'. These are the ASCII character
numbers, as a range, that the function uses to look for objects.
between(ascii(object_name),99,101)
• not
• and
• or
These operators follow the standard logical semantics. They are listed in order of
precedence, with the highest-precedence one at the top.
object_name eq REST"
object_name ne "REST"
lt < Less than r_modify_date < "2013-01-16"
r_modify_date lt "2013-01-16"
le <= Less than or equal r_full_content_size <= 2000
to
r_full_content_size le 2000
gt > Greater than r_full_content_size > 2000
r_full_content_size gt 2000
ge >= Greater than or r_modify_date >= "2013-01-16"
equal to
r_modify_date ge "2013-01-16"
[1] This operator cannot be used to compare a property with a list of values. For more
information about how to compare the value of a property with a list of values, see
“Comparison with multiple values” on page 56.
Syntax:
Notes
Syntax:
The expression returns True if any value of the repeating property matches the
comparison, and False otherwise.
• keywords/item = "hey"
• keywords/item = ("hello", "world")
• ASCII
• BITAND
• BITCLR
• BITSET
• UPPER
• LOWER
• SUBSTR
Syntax:
scalar-function(<attribute-name>) <comparison-operator> value
This code sample shows you how to use a scalar function (in this case the
upper(string) function) to return only those objects whose object name is
<document>, case insensitive.
The upper function used in this sample takes an object_name as its only
parameter, converts it all to upper case characters, and returns it. This returned
value is then compared to the value DOCUMENT and if it matches, then a boolean
value of true is returned.
upper(object_name) = 'DOCUMENT'
This code sample shows you how to return only those characters that are in the
3rd, 4th, 5th, and 6th position, within the object name, which when
concatenated together, form the word test. We use the substr(<attribute-
name>,starting pos,num of chars) function starting at the third character
position (from a zero index starting point) and return 4 characters to form the
string test.
substr(object_name,3,4) = 'test'
Caution
Embedding a scalar function within another scalar function is not
supported.
With this expression, the Request returns only the instances of dm_document
that were modified between 2013-01-16 and 2013-01-18.
With this expression, the Request filters out those resources whose object_
name starts with foo.
not(starts-with(object_name, "foo"))
With this expression, the Request returns only those resources whose object_
name contains hello or hey. Additionally, the resources that were modified
later than 2013-01-01 are filtered out and therefore excluded from the returned
values.
With this expression, the Request returns only those resources that have the
keyword hello or world. Additionally, resources whose author is Jack are
filtered out and therefore excluded from the returned values.
author != "Jack" and keywords/item = ("hello", "world")
If you use this view expression, the performance may degrade when a
request tries to retrieve a collection resource.
default Returns the default set of properties of the Requested object to the
client. This is the default value if there is no value specified for the
view query parameter.
When a property name, that is specified in the expression, violates any of the
naming rules shown above, the view expression is considered to be invalid and is
therefore rejected. In this case, the server returns an HTTP 400 Bad Request error
code status.
For example, GET requests to a URL that resembles the following examples are
regarded as bad requests:
/repositories/<repository>/folders/<folderID>/documents?inline=true
&view=r_object_id,<custom_property_name>
To retrieve custom properties in a collection resource in versions before 7.3, you can
set the view parameter to :all. Documentum REST Services release 7.3 and later
support tolerant view expressions on most collection resources. The view
parameter's usage on singular resources remains the same.
Note: When the view parameter is used on a single object resource that
supports this parameter (such as the Cabinet resource, Document resource,
etc.). The view attribute names must match the definition of the object data
type. A Bad request (400) error is returned when any unknown attributes
for the object data type are specified in the view parameter.
When the view parameter is used on a collection based resource that supports
this parameter (such as the Cabinets resource, Folder Child Documents
resource, etc.), a comma separated list of view attributes can contain both base
type attribute names and arbitrary extended attribute names.
For example, let’s assume that a folder X has a number of documents in it with
the object type <dm_document> and document sub types <doc_ext1>, <doc_ext2>,
and so on. When a REST client retrieves the folder children, it can specify the
view attributes from both <dm_document> and its sub types.
• Aspect type attributes can also be put into the view when the aspect type is
attachable to the collection base type, such as <dm_document> as shown in the
above sample
• Collection items return the specified attributes only when the attributes exist
in those objects
• Unknown attributes for collection items are ignored.
This example sets the view parameter to the predefined view expression:all,
meaning that all properties of the Requested object are returned.
This example sets the view parameter to the custom view expression object_
name,r_object_type, meaning that only the object_name and r_object_
type properties of the Requested object are returned.
For repeating properties, setting NULL for a property or leaving a property empty
removes all values from the repeating property.
Note: The examples above show how the REST server handles NULL values in
a response. REST clients are free to use any valid format to represent NULL
values in a request. For example, you can use this pattern to represent a NULL
value in XML:
<dm:property_name><dm:property_name/>
For atom feeds, if a property is set to NULL or empty, the property is not displayed
in the Response.
• Cabinets
• Folder Child Documents
• Folder Child Objects
• Folder Child Folders
• Checked Out Objects
For more information about these resources, see the OpenText Documentum REST
Services Reference Guide.
When retrieving these collection resources, you can use the thumbnail query
parameter to determine whether or not to return the thumbnail link for each entry.
• Absolute URI: An absolute URI is a URI that contains both the scheme (i.e. http,
https) and the authority (i.e. host, port)
• Base URI: A base URI is defined by the context of the application. It is usually the
URI with the root path to the application. This type of URI must follow the same
syntax as an absolute URI
• Relative URI: A relative URI is a sub component of the URI relative to the base
URI of an absolute URI. This type of URI usually starts with a slash (/) or an
empty character
Using an absolute URI on the client side allows the HTTP client to use the href
directly without the need for further URI resolution.
However, with the maturity of HTTP and REST development, most client libraries
today support relative URIs as well. The benefits in using relative URIs, as compared
to absolute URIs, are as follows:
• Removing the scheme and authority part from the href simplifies the Response
body rewrite rules in proxy or balancer servers.
• Removing the scheme and authority part from the href separates deployment
and resource identification
• Removing the scheme and authority part from the href reduces the payload bytes
in responses.
Documentum REST Services 16.3 supports relative URIs in resource responses.
When relative URIs have been enabled, the resource Response is similar to the
samples shown below:
{
"name": "Cabinet",
"type": "dm_cabinet",
"definition": "/repositories/REPO/types/dm_cabinet.json",
"properties": {
"object_name": "demo",
"owner_name": "Administrator"
},
"links": [
{
"rel": "self",
"href": "/repositories/REPO/cabinets/0c0000058000211b.json?
view=object_name,owner_name"
},
...
]
}
"id": "/repositories/REPO/cabinets",
"title": "Cabinets",
"author": [
{
"name": "OpenText Documentum"
}
],
"updated": "2017-08-18T02:52:30.556+00:00",
"page": 1,
"items-per-page": 2,
"links": [
{
"rel": "self",
"href": "/repositories/REPO/cabinets.json?items-per-page=2"
},
{
"rel": "next",
"href": "/repositories/REPO/cabinets.json?items-per-page=2&page=2"
},
{
"rel": "first",
"href": "/repositories/REPO/cabinets.json?items-per-page=2&page=1"
}
],
"entries": [
{
"id": "/repositories/REPO/cabinets/0c00000580000105",
"title": "Administrator",
"author": [
{
"name": "REPO_ADMIN",
"uri": "/repositories/REPO/users/REPO_ADMIN.json"
}
],
"summary": "dm_cabinet 0c00000580000105",
"updated": "2015-03-03T10:15:33.000+00:00",
"published": "2015-03-03T10:15:33.000+00:00",
"links": [
{
"rel": "edit",
"href": "/repositories/REPO/cabinets/0c00000580000105.json"
}
],
"content": {
"type": "application/json",
"src": "/repositories/REPO/cabinets/0c00000580000105.json"
}
},
...
]
}
In these resources, both absolute URIs and relative URIs are considered as valid
inputs to identify other resources. The usage of relative URIs in request bodies is not
impacted by the rest.use.relative.url. runtime property setting.
In REST extensibility, external links can be added to responses. These links also
remain as absolute URIs.
Documentum REST Services utilizes the following query parameters for feed
pagination (For detailed information, see “Common HTTP headers” on page 19):
• page
• items-per-page
• include-total
If all results can fit on one page, the Response is not paged. In this case, the Response
does not contain the page and items-per-page fields even if they are specified in
the Request. Also, the pagination link relations, such as first, last, previous and
next, are not available.
For a paged feed, the Response must have at least one of the following link relations:
• first
• This link relation is always available.
• last
• This link relation is available when the size of the entire feed is known. This
condition is met in the following scenarios:
– The client sets the include-total query parameter to calculate the number
of entries of the feed.
• previous
• This link relation is available when the current page is not the first page
• next
• This link relation is available when the current page is not the last page or when
the size of the feed is unknown.
For detailed information about these link relations, see Appendix A, Link relations
on page 459.
• All Versions
For more information about these resources, see the OpenText Documentum REST
Services Reference Guide.
The value of the full text query parameter q is a string that follows the simple search
language syntax.
Note: The parentheses which can be used in search, are not supported in the
parameter q when being appended to the collection resources above.
Search criterion in the simple search language must follow this syntax:
searchExpression: orExpression
orExpression: andExpression (<OR> andExpression)*
andExpression:operandExpression (<AND> operandExpression)*
operandExpression:( parenthesis | implicitExpression )
parenthesis : <PARENTHESIS_START> orExpression <PARENTHESIS_END>
implicitExpression: ( term )+
term: ( positiveTerm | <NOT> positiveTerm)
positiveTerm: ( <WORD> | <PHRASE> )
<DEFAULT> SKIP :
{
<SPACE: ( [" ","\t","\n","\r"] )+ >
}
<DEFAULT> TOKEN :
{
<AND: "and" >
| <OR:"or" >
| <PARENTHESIS_START: "(" >
| <PARENTHESIS_END: ")" >
| <NOT: "not" >
| <WORD: ( ~["(",")","\""," ","\t","\n","\r"] )+ >
| <PHRASE: "\"" ( ~["\""] )+ "\"" >
}
<term> can be a word, or a phrase. It can also be a list of words or phrases, or both,
separated by spaces and quoted appropriately. When used in the Search resource,
<term> can also be enclosed in parentheses to escape boolean operators.
• Words
• Phrases
• Implicit AND
• boolean Operators
• Wildcards
4.10.1 Words
A word is a set of characters with the following exceptions:
• (
• )
• \"
• \t
• \n
• \r
Multiple words enclosed in double quotes are treated as a phrase, such as "foo
bar". When the words are not enclosed, implicit and is applied.
4.10.2 Phrases
A phrase, which contains more than one word separated by spaces, is enclosed in
double quotes. For example:
"foo bar" returns objects that contain the phrase foo bar.
The single quote character (’) can be included as a part of a phrase without any
change. For example: "foo ’ bar"
Note that \" is not supported even when it is enclosed in double quotes. For
example, "foo \" bar" is invalid.
foo bar
This expression, which equals to foo and bar, returns objects that contain both foo
and bar. To search for objects that contain either foo or bar, include or in the
expression explicitly:
foo or bar
• not
• and
• or
These operators follow the standard logical semantics. They are listed in order of
precedence, with the highest- precedence one at the top.
In the Search resource, you can use both parentheses and double quotes to escape
boolean operators. In the full text query parameter, you can only use double-quotes
to escape boolean operators.
To include any of the boolean operators as a string in search criterion, enclose the
operators in double quotes. And thus, the system treats them as phrases. For
example: "and" or "or"
4.10.5 Wildcards
The following wildcard characters are supported in the simple search language.
• r_modifier
• keywords
• r_modify_date
• r_full_content_size
• a_application_type
• r_object_type
• owner_name
To enable the use of facets on other properties, you must modify your xPlore
configuration and re-index.
When you perform a facet search, a facet section appears in the Response feed of the
search result at the same level as the entry element.
<entry>
...
</entry>
...
<entry>
...
</entry>
<dm:facet>
<dm:facet-id>facet_r_modify_date</dm:facet-id>
<dm:facet-label>Modify Date</dm:facet-label>
<dm:facet-value>
<dm:facet-value-id>LAST_YEAR</dm:facet-value-id>
<dm:facet-value-count>23</dm:facet-value-count>
<dm:facet-id>facet_r_modify_date</dm:facet-id>
<dm:facet-value-constraint>
2013-01-01T00:00:00.000\+0000/2014-01-01T00:00:00.000\+0000
</dm:facet-value-constraint>
<link rel="search"
href="http://localhost:8080/dctm-rest/repositories/acme01/
search?q=emc&facet=r_modify_date&
facet-value-constraints=2013-01-01T00:00:00.000%5C%2B0000/
2014-01-01T00:00:00.000%5C%2B0000"/>
</dm:facet-value>
<dm:facet-value>
<dm:facet-value-id>LAST_MONTH</dm:facet-value-id>
<dm:facet-value-count>3</dm:facet-value-count>
<dm:facet-id>facet_r_modify_date</dm:facet-id>
<dm:facet-value-constraint>
2014-04-01T00:00:00.000\+0000/2014-05-01T00:00:00.000\+0000
</dm:facet-value-constraint>
<link rel="search"
href="http://localhost:8080/dctm-rest/repositories/acme01/
search?q=emc&facet=r_modify_date&
facet-value-constraints=2014-04-01T00:00:00.000%5C%2B0000/
2014-05-01T00:00:00.000%5C%2B0000"/>
</dm:facet-value>
</dm:facet>
</feed>
The following table describes the properties in the facet section in detail:
Property Description
facet-id Indicates the property to be used as the facet. When being used in
facet-id, a property is prefixed with facet_. In the example, the
r_modify_date property is used as the facet.
facet-label Label of the property used as the facet. Labels of properties are
defined in DFC interfaces. For example, the label of r_modify_
date is Modify Date.
facet-value Each facet-value section contains data of the results belonging to
one group. In the example, two facet-value sections appear, one
for instances whose r_modify_date falls in last year's date range
and the other for instances whose r_modify_date falls in last
month's date range.
facet-value-id Indicates the value of property that is used as the facet
facet-value-count Indicates the number of results belonging to a certain group
facet-value- Indicates the property constraints expression.
constraint
Property Description
search link Points to corresponding results of a certain group. For example, you
can navigate to instances whose r_modify_date falls in last year's
date range by accessing this link:
1 http://localhost:8080/dctm-rest/repositories/acme01/search?
2 q=emc&facet=r_modify_date&facet-value-
constraints=2013-01-01T00:00:00.000%5C
3 %2B0000/2014-01-01T00:00:00.000%5C%2B0000
When a repeating property is used as the facet, entries in the result set may also
contain a facet block that is comprised of multiple facet value groups, each of which
represents a combination of repeating values (<value_a> + <value_b>). This enables
you to navigate to the results whose repeating property contains both <value_a> and
<value_b> by accessing the corresponding search link.
In addition to the AND operator ( + ), you can also use the OR operator (|), which is
encoded as %7C in URLs, in facet-value-constraints.
Note: These '+' and '|' operators have the same precedence in facet-value-
constraints, and the expression is evaluated from right to left. For example,
the expression a+b|c+d is treated as a+(b|(c+d)).
Below are the facet results. The elements in this set of facet results are similar to the
results from the “Facet search with URL parameters” on page 72 section. However,
the URL for the link relation search has a new element called
facet-id-constraints that contains the key value pairs for each facet id and its
constraint. The id should correspond to the facet id in the AQL. Here is a code
sample that shows you facet results and the facet-id-constraints parameter:
As you can see in the above code sample, the <cabinet> type has five results. You can
navigate into this facet group by using the POST HTTP method with the original
request to the URL in the facet result http://localhost:8080/dctm-rest/
repositories/REPO/search.xml?facet-id-constraints=facet_type%3Ddm_
cabinet.
For more information on using Facets requests with AQL, see the OpenText
Documentum REST Services Reference Guide for version 7.3 or later.
The shareable <supertype> object is called the lightweight <SysObject parent>. The
lightweight SysObject that references the <supertype> is called the child.
Each lightweight SysObject shares the information of its shareable parent object.
Instead of having multiple, nearly identical, rows in the SysObject database tables to
support all instances of the lightweight type, a single parent object exists for
multiple lightweight SysObjects.
Shareable Type
Shareable type instances can share their property values with lightweight types.
Multiple lightweight objects can share the property values of one shareable object.
Lightweight Type
Lightweight object type is a child of shareable object type. It is used to reduce the
space required to store it in a database.
1. Use the filter attribute type_category on the Types resource to get lightweight
object types. Here is an example of the URI:
/repositories/<{repositoryName}>/types?filter=type_category=4
Note: You must include your repository name in the URI. Also notice the
parameter filter=type_category=4, which is used for Lightweight object
types.
2. Use the filter attribute type_category on the Types resource to get sharable
object types. Here is an example of the URI:
/repositories/<{repositoryName}>/types?filter=type_category=2
Note: You must include your repository name in the URI. Also notice the
parameter filter=type_category=2, which is used for shareable object
types.
3. The shareable object types have a lightweight-types link relation that points
to their relative child lightweight object type.
Similarly, the lightweight object types have a link relation parent-sharable-type
that points to their parent shareable object type. The link relation <parent> points
to the super type.
4. The category property has been added to the Type resource version 7.3 and
later to show the type of the category, and the shared-parent property has also
been added to the Type resource version 7.3 and later to show the
shared-parent of the lightweight type. Here's a code sample that shows you
how to use these two properties:
<type label="MyLightweight" name="my_lightweight"
parent="http://localhost:8080/dctm-rest/repositories/REPO/types/my_shareable"
shared-parent="http://localhost:8080/dctm-rest/repositories/REPO/types/
my_shareable"
category="shareable"
xmlns="http://identifiers.emc.com/vocab/documentum"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<properties></properties>
<links>
<link
href="http://localhost:8080/dctm-rest/repositories/REPO/types/
my_lightweight"
rel="self"/>
<link
href="http://localhost:8080/dctm-rest/repositories/REPO/types?
parent-shareable-type=my_shareable" rel="http://identifiers.emc.com/
linkrel/types"/>
<link
href="http://localhost:8080/dctm-rest/repositories/REPO/types/
my_shareable"
rel="parent"/>
</links>
</type>
Generated links are presented at the entry level. By accessing these links, a client
application is able to navigate to the corresponding resources. Typically, if the DQL
query result item has a dedicated resource, the result entry contains an editlink
relation pointing to that resource.
How and what links are generated is delegated to each resources (mostly the types
you specify in the FROM clause, such as a document resource).
Note that not all DQL statements are eligible to generate links in query results as a
DQL query may not return information needed to generate links. For example,
properties such as r_object_id, or user_name, which can be used to identify
resources, are not selected in the DQL statement. The following query does not
generate any link because neither object_name nor r_modify_date is able to
identify a resource.
Sample request:
GET http://localhost:8080/dctm-rest/repositories/REPO?dql=select
r_object_id,object_name,r_object_type,thumbnail_url from dm_sysobject
Sample Response:
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"
xmlns:dm="http://identifiers.emc.com/vocab/documentum"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<id>http://localhost:8080/dctm-rest/repositories/REPO</id>
<title>query results</title>
<updated>2014-05-20T15:09:18.473+08:00</updated>
<author>
<name>OpenText Documentum</name>
</author>
<dm:page>1</dm:page>
<dm:items-per-page>100</dm:items-per-page>
<link
href="http://localhost:8080/dctm-rest/repositories/REPO?
select+r_object_id,object_name,r_object_type,
thumbnail_url+from+dm_sysobject"
rel="self"/>
<link
href="http://localhost:8080/dctm-rest/repositories/REPO?
select+r_object_id,object_name,r_object_type,
thumbnail_url+from+dm_sysobject&items-per-page=100&page=2"
rel="next"/>
<link
href="http://localhost:8080/dctm-rest/repositories/REPO?
select+r_object_id,object_name,r_object_type,
thumbnail_url+from+dm_sysobject&items-per-page=100&page=1"
rel="first"/>
<entry>
<id>http://localhost:8080/dctm-rest/repositories/REPO?
select+r_object_id,object_name,r_object_type,
thumbnail_url+from+dm_sysobject&index=0</id>
<title>080020808000013c</title>
<updated>2014-05-20T15:09:18.763+08:00</updated>
<content>
<dm:query-result
definition="http://localhost:8080/dctm-rest/repositories/REPO/types/
dm_cryptographic_key"
xsi:type="dm:dm_cryptographic_key">
<dm:properties xsi:type="dm:dm_cryptographic_key-properties">
<dm:r_object_id>080020808000013c</dm:r_object_id>
<dm:object_name/>
<dm:r_object_type>dm_cryptographic_key</dm:r_object_type>
<dm:thumbnail_url>http://CS71P01:8081/thumbsrv/getThumbnail?
object_type=dm_cryptographic_key&
format=&
is_vdm=false&
repository=8320
</dm:thumbnail_url>
</dm:properties>
</dm:query-result>
</content>
<link
href="http://localhost:8080/dctm-rest/repositories/REPO/objects/
080020808000013c"
rel="edit"/>
<link
href="http://localhost:8081/thumbsrv/getThumbnail?
object_type=dm_cryptographic_key&
format=&
is_vdm=false&
repository=8320"
rel="icon"/>
</entry>
</feed>
Sample request:
http://localhost:8080/dctm-rest/repositories/documentum1?dql=
select r_object_id,object_name,r_object_type,thumbnail_url from dm_sysobject
Sample Response:
{
id: "http://localhost:8080/dctm-rest/repositories/documentum1"
title: "DQL query results"
updated: "2014-07-04T15:44:00.896+08:00"
author:
{
name: "OpenText Documentum"
}
page: 1
items-per-page: 2
links:
{
rel: "self"
href: "http://localhost:8080/dctm-rest/repositories/documentum1?
dql=select r_object_id,object_name,
r_object_type,
thumbnail_url from dm_sysobject"
}
{
rel: "next"
href: "http://localhost:8080/dctm-rest/repositories/documentum1?
dql=select r_object_id,object_name,
r_object_type,
thumbnail_url from dm_sysobject"
}
{
rel: "first"
href: "http://localhost:8080/dctm-rest/repositories/documentum1?
dql=select r_object_id,object_name,
r_object_type,
thumbnail_url from dm_sysobject"
}
entries: [
{
id: "http://localhost:8080/dctm-rest/repositories/documentum1?
dql=select r_object_id,object_name,
r_object_type,
thumbnail_url from dm_sysobject"
title: "080f42418000013c"
updated: "2014-07-04T15:44:00.896+08:00"
content:
{
name: "query-result"
type: "dm_cryptographic_key"
definition: "http://localhost:8080/dctm-rest/repositories/
documentum1/types/dm_cryptographic_key"
properties:
{
r_object_id: "080f42418000013c"
object_name: ""
r_object_type: "dm_cryptographic_key"
thumbnail_url: "http://localhost:8081/thumbsrv/getThumbnail?
object_type=dm_cryptographic_key&
format=&
is_vdm=false&
repository=1000001"
},
links: [...]
}
links: [
{
rel: "edit"
href: "http://localhost:8080/dctm-rest/repositories/documentum1/
objects/080f42418000013c"
}
{
rel: "icon"
href: "http://localhost:8081/thumbsrv/getThumbnail?
object_type=dm_cryptographic_key&
format=&
is_vdm=false&
repository=1000001"
}
]
},
{
id: "http://localhost:8080/dctm-rest/repositories/documentum1?
dql=select r_object_id,object_name,r_object_type,
thumbnail_url from dm_sysobject"
title: "080f42418000013d"
updated: "2014-07-04T15:44:00.896+08:00"
content:
{
name: "query-result"
type: "dm_public_key_certificate"
definition: "http://localhost:8080/dctm-rest/repositories/
documentum1/types/dm_public_key_certificate"
properties:
{
r_object_id: "080f42418000013d"
object_name: ""
r_object_type: "dm_public_key_certificate"
thumbnail_url: "http://localhost:8081/thumbsrv/getThumbnail?
object_type=dm_public_key_certificate&
format=&
is_vdm=false&
repository=1000001"
}
links: [...]
}
links: [
{
rel: "edit"
href: "http://localhost:8080/dctm-rest/repositories/
documentum1/objects/080f42418000013d"
},
{
rel: "icon"
href: "http://localhost:8081/thumbsrv/getThumbnail?
object_type=dm_public_key_certificate&
format=&
is_vdm=false&
repository=1000001"
}
]
}
]
}
REST services decides where to store persistent data depending on your runtime
environment setting, the user accessing the persistent data, and the resource
generating the data. This section discusses how REST services resolves locations of
persistent data.
The path of the folder storing persistent data consists of two parts, a parent folder
path followed by a sub folder path.
• Parent folder path: This folder path can point to a global location specified in the
rest-api-runtime.properties file, the user’s default folder, or the temp
folder.
For more information, see Resolving Parent Folder.
• Sub folder path: The path of a sub folder follows this pattern: ${user_name}_$
{resource_name}.
Here are some examples of persistent data folder paths. The parent folder path,
which is shown in italic, followed by the sub-folder path:
• /System/Applications/rest-persistence/admin_saved_search
• /testuser_default/testuser_saved_search
• /temp/myuser_myresource
If none of the options is enabled, the system returns a 403 error code to the client
that attempts to generate persistent data.
Global location
The global location is the root folder for all persistence data. With this location
specified, operations generating persistent data create sub folders under the root to
store persistence.
If you specify an invalid path, the system returns a 403 error code. In this case, the
system does not try the two lower precedence options (user default folder or /Temp
folder).
Make sure that you grant users Write permission on this folder as they have to
create sub folders there. To do this, set the basic permission of dm_world to 6 for the
global location folder as shown in the following snippet.
<permission-set
xmlns="http://identifiers.emc.com/vocab/documentum"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<permitted>
<permission accessor="dm_world" basic-permission="6"/>
<permission accessor="dm_owner" basic-permission="7"
extend-permissions="EXECUTE_PROC,CHANGE_LOCATION"/>
</permitted>
<links>
<link rel="self" href="http://localhost:8080/"/>
<link rel="edit" href="http://localhost:8080/"/>
<link rel="http://identifiers.emc.com/linkrel/acl"
href="http://localhost:8080/repositories/REPO/acls/45024c518000110c"/>
</links>
</permission-set>
For security reasons, you may want to hide implementation details. Marking the
global location folder hidden serves the purpose:
<folder
xmlns="http://identifiers.emc.com/vocab/documentum"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="dm_folder"
definition="http://localhost:8080/repositories/REPO/types/dm_folder">
<properties>
<object_name>rest-persistence</object_name>
<r_object_type>dm_folder</r_object_type>
...
<a_is_hidden>true</a_is_hidden>
...
</properties>
</folder>
If a global location for persistent data does not exist, REST Services tries the user’
default folder (specified by default_folder) as an alternative.
Temp folder
Non-administrative users may not have a default folder. In this case, REST services
tries the /Temp folder. Because the /Temp folder can be used as the storage for
persistent data, be cautious when you delete this folder or remove data from the
folder.
• Your REST clients must send email content as multi part form data to the
Documentum REST server.
• Your REST clients must send content to your REST server as an HTTP query
parameter with the format value set to either msg or eml as shown in this
example:
Ex: http://localhost:8090/dctm-rest/repositories/documentum1/folders/
0c0004d280000107/documents?content-count=1&content_length=359424&
format=msg
When the above items have been set, the Folder child documents collection resource
and Folder child objects collection resource will use the Documentum Foundation
Classes (DFC) to invoke the MailApp logic in the REST server side code. The mail
app logic parses the attributes of the email and updates the email SysObject that
must be imported. Once the email import operation is finished, you can find the
email attributes, such as subject, to, from, etc. in the SysObject of the mail
object that is being imported.
MailApp processing depends on the values used in the following properties in the
mail-app.properties file.
• shouldParseMsgFile=true: When this flag is true, then msg files are parsed by
the mail app. Otherwise not.
1. Navigate to a specific document Cabinet or Folder resource. You can do this using
the following GET Request:
GET http://localhost/dctm-rest/repositories/documentum1/cabinets/0c00031280000105
2. Create a Document object in the selected Cabinet or Folder with the following POST
request:
POST
http://localhost:8080/dctm-rest/repositories/documentum1/folders/
0c00031280000105/documents
3. Add content to the new Document object with the following POST request:
POST:http://localhost:8080/dctm-rest/repositories/documentum1/objects/
0900031280002f60/contents
{
"properties": {
"object_name":"newTestDocument1"
}
}
name=content
Content-Type: text/plain This sentence is the text content of your Document object
The following code sample shows you how to create a dm_document (dmObject)
object and set its object_name. Next, we show you how to create a
binaryContentObject, which is added to your new dm_document object as content.
ObjectViewBuilder builder = (ObjectViewBuilder) params[1];
String targetUrl ="http://localhost:8080/dctm-rest/repositories/documentum1/
folders/0c00031280000105/documents";
Object binaryContentObject = "This is used as binary content for your new object
<script>alert('hello world');</script> 1";
String objectName = "test_doc";
Object dmObject = appendDocBasicAttr(builder, objectName).build();
Example 4-45: Creating a Document object and its content with one
request
The following code sample shows you how to create a Document object and its
content with one request:
Item createdObjectWithContent = client.createDocument(targetUrl, dmObject,
binaryContentObject, "text/plain", "format", "crtext");
To complete this task, we show you how to use the Spring RestTemplate to
create the single POST request that is used to send everything to the
Documentum REST Services server:
public <T> ResponseEntity<T> process(String url, HttpMethod method,
HttpEntity<?> requestEntity, Class<T> responseType)
{
return restTemplate.exchange(url, method, requestEntity, responseType);
}
REST Services supports the end user tracking feature from release 21.2. As part of
end user tracking, Documentum REST Services provides the following information
to the Documentum Server using DFC.
• The application used by the end user to log in to the Documentum REST Server.
• The Documentum REST client applications can provide their application name
using the header the X-CLIENT-APPLICATION-NAME. The header value is
considered as the Documentum client application name using which a user tries
to login. If a value is not provided to this header, the application name is taken
from the Documentum REST application context path.
The end user information is logged in the dm_audittrail table when the events,
dm_connect and dm_logon_failure are enabled on Documentum Server. These
events are triggered by Documentum Server when a user tries to get a session from
Documentum Server. The audit event for dm_connect is not enabled by default.
However, the dm_logon_failure event is enabled by default. The Documentum
REST server sends the end user information to DFC if the
dfc.client.should_use_enduserinfo DFC property is set to true.
Note: We strongly recommend that you use HTTPS, which requires a secure
connection, when using HTTP basic authentication because HTTP basic
authentication transmits the username and password in BASE64 encoded plain
text.
If the authentication fails, the REST server responds with the HTTP 401
Unauthorized status code.
Documentum REST Services release 7.2 and later allow you to improve the
efficiency of HTTP Basic authentication by using client tokens. This improvement
allows an authenticated REST client to access the REST server without having to
negotiate a new session ticket in a specified period of time.
The following diagram illustrates the workflow of HTTP Basic authentication with
client tokens:
2. The REST server tries to retrieve a session from Documentum Server with the
credential the client provides. If the credential is valid, the REST server creates a
client token in the DOCUMENTUM-CLIENT-TOKEN cookie of the Response and sends
it back to the client.
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Set-Cookie: DOCUMENTUM-CLIENT-TOKEN= <encrypted_client_token>
Otherwise, the REST server rejects the Request with an HTTP 401 error.
3. When sending subsequent requests, the client includes the client token in the
cookie.
4. The REST server validates the client token. If the client token is valid, the server
returns the Response. If the client token expires or the token is invalid, the REST
server rejects the Request with HTTP 401
5. The client sends a request to log out:
GET /dctm-rest/logout HTTP/1.1
Host: localhost:8080
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.3.1 (java 1.5)
Cookie: DOCUMENTUM-CLIENT-TOKEN= <encrypted_client_token>
Note: <dctm-rest> represents the directory where you extract the dctm-rest.
war file.
In scenario B, we recommend that you use the following mapping rule consistently
for both Documentum Server domain-required and non-domain-required modes.
This mapping is generic for any type of LDAP servers supported by Documentum
Server.
${username} = ${dm_user.user_login_domain}\${dm_user.user_login_name}
${password} = ${LDAP user password}
Notes
Notes
• The workflow may vary because the negotiation protocol allows mutual
authentication that may take several rounds of request/Response to complete
the handshake
Authentication negotiation between the REST client and the REST server
1. The REST client sends a resource request with no credentials to the REST server
(Step 1 in the diagram).
GET /${resource-url} HTTP/1.1
2. The REST server responds with the 401 status error and a Negotiate Header
(Step 2 in the diagram).
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Negotiate
3. The REST client negotiates a SPNEGO-based Kerberos token and re-sends the
resource request (Step 3 to Step 9 in the diagram).
GET /${resource-url} HTTP/1.1
Authorization: Negotiate YIIZG1hZG1pbjpwYXNzd29yZ.....
4. The REST server returns the requested resource. Optionally, a mutual token for
the client to verify can be sent back to the client (Step 10 in the diagram).
HTTP/1.1 200 OK
WWW-Authenticate: Negotiate CXXCBApbjpwYXBSS90B.....
A domain user (A) tries to access a REST resource located on a domain other than
the one where user A logs on.
In this diagram, the REST server is deployed in a different domain than the domain
where users log in:
Caution
Domain names must be entered all in uppercase letters. Otherwise the
operation will fail during the authentication process. This is a requirement
of Documentum Server.
• The logon users come from other domains CORP.ACME.COM and ENG.ACME.COM
• Documentum Server repositories are registered to the sub-trusted domains
under ACME.COM. This can be PUB.ACME.COM, CORP.ACME.COM, ENG.ACME.COM, or
any other sub-trusted domain.
• Repositories must synchronize domain users from the Active Directory. In this
diagram, each repository synchronizes AD users from multiple domains
• Each repository can support multi-domain Kerberos authentication
Note: Before configuring Documentum Server and the REST server for
SPNEGO-based Kerberos, you must set up the domain controllers successfully
in your Active Directory environment.
You must restart the application server after you finish the configurations above.
First, you must determine a Service Principal Name (SPN) for your REST server. The
SPN follows one of the following patterns:
• Pattern 1:
<service-class>/<host>:<port>/<service-name>
• Pattern 2:
HTTP/<FULL-HOST-NAME>
Example: HTTP/RESTSERVER.ACME.COM
We suggest that you use pattern 2 because it is compatible with most web clients.
After you determine your SPN, follow these steps on the domain controller that is
registered as the Key Distribution Center (KDC):
1. In the Active Domain Computers and Users Management console, create a service
account for Documentum Server, for example, webadmin.
Example:
setspn -a HTTP/RESTSERVER.ACME.COM webadmin
3. Run the following command to create a keytab file for the SPN and map the
SPN to the service account:
ktpass /pass <PASSWORD> -out <OUTPUT-KEYTAB-FILE-LOCATION>
-princ <REST-SPN>@<REALM-NAME> -crypto <ENCRYPT-TYPE>
+DumpSalt -ptype KRB5_NT_PRINCIPAL /mapOp set
/mapUser <REST-SERVICE-ACCOUNT>
For more information about the <ktpass> utility, see Ktpass Syntax in the
Mircosoft website.
4. Copy the keytab file to the application server where Documentum REST
Services is deployed.
5. In the Active Domain Computers and Users Management console, select "Trust
this user for delegation to any service (Kerberos only)" for the service
account (for example, webadmin) in the Delegation tab.
Caution
The Delegation tab does not exist until you run the <ktpass> command
to map the principal.
In the Active Domain Computers and Users Management console, select “Trust this
user for delegation to specific services only”, then select “Use Kerberos only” and add
service principals for the Documentum Server repositories.
Constrained Delegation cannot be configured across multiple domains and can
only be used in a single domain scenario. This is a limitation of the MS KDC
configuration.
The JAAS configuration file entry contains JAAS-specific settings such as the
<LoginContext> name (which is also the name of the configuration entry), settings
for the Kerberos login module, the REST server's SPN, and the location of the
*.keytab file.
The location and format of the JAAS configuration settings might be different for
each application server. The JAAS configuration file in most application servers is
named jaas.conf. However, in <WebSphere> application server, the JAAS
configuration file is named wsjaas.conf under the following directory:
<WAS_Installation_path>\AppServer\profiles\<APP_SERVER_NODE_NAME>
\properties.
The location and format of the JAAS configuration settings might be different for
each application server. The JAAS configuration file in most application servers is
named jaas.conf.
<WAS_Installation_path>\AppServer\profiles\<APP_SERVER_NODE_NAME>
\properties.
<JBoss 6.3>: In the <Jboss 6.3> application server, the JAAS configuration must be
specified in the standalone.xml file, which is located in the following directory:
jboss-eap-6.3\standalone\configuration\.
In your web.xml file, you must set the <jaas.config> entry so that it points to the
above standalone.xml file.
<Jboss 6.3> uses JAAS configurations that are included in its standlaone.xml file.
Any configuration settings that are made to any other files are ignored by the <Jboss
6.3> application server.
Element ID Description
<security-domain> Corresponds to the Documentum REST Services web
application’s SPN. You replace separator characters with
hyphen characters and omit the @REALM segment in the SPN.
Element ID Description
<LoginModule> For both single and multi domain, the REST services always
uses the Quest login module.
• com.dstc.security.kerberos .jaas.
KerberosLoginModule
com.dstc.security.kerberos.jaas.
KerberosLoginModule
useTicketCache=true
createTicketCache=trueticketCache=<cache_path>
<SPN> For QUEST login modules, the SPN does not contain the @ character
and the string after that. For example:
HTTP/myhost.mydomain.com:8080
• Authentication mode
• Threshold (in bytes) at which Kerberos authentication cuts over from UDP to
TCP
This file contains detailed instructions on how to set the runtime properties. Follow
the instructions to complete the settings. The file is located in the following directory
of the application server where REST web services is deployed:
<dctm-rest>\WEB-INF\classes
<dctm-rest> represents the directory where you extract the dctm-rest.war file.
Note: The instructions in this section are based on the following assumptions:
• You have finished Kerberos configurations for both the REST server and
Documentum Server correctly
Minimal Requirements
4. In the Add this website to the zone field, enter the following site:
http://*.acme.com
5. Click Add->Close->OK.
Mozilla Firefox
Minimal Requirements:
1. Open Firefox.
8. Restart Firefox.
Google Chrome
Minimal Requirements:
1. Locate Chrome.exe on your system. Typically, the file is located in the following
directory:
C:\Users\tuser\AppData\Local\Google\Chrome\Application\
<chrome-directory>\chrome.exe --auth-schemes="negotiate"
--auth-server-whitelist="*acme.com"
--auth-negotiate-delegate-whitelist="*acme.com"
http://restserver.acme.com:8080/dctm-rest/repositories/acme/
currentuser.xml
When all settings are correct, the Current User resource, which contains the
information of the login user tuser, is returned.
• Domain Controller
– The service account that creates the SPN and maps the keytab must be
configured as “Delegation Trust”.
– The encryption types for Kerberos service tickets must be supported across
the client machines, the REST server machine, and the Documentum Server
machine
– For multi-domain deployment, the domains must be in the same forest, and
two-Way trusts must be activated. You can verify this in the Active Directory
Domains and Trusts console.
• Documentum Server
– The Documentum Server repository SPN format must follow this pattern: CS/
<repository name>.
– The REST Services keytab must be loaded successfully. You can verify this by
running the following command:
%JAVA_HOME%/bin/klist -k -t <keytab-file-path>
– The client session must be within the domain. You can verify this by running
the command klist tgt which checks the client TGT cache
– You must be able to forward the service ticket to issue on the client
To enable REST server Kerberos debugging, perform one or more of the following
operations:
1. Stop docbase.
2. Edit the service parameter and append -otrace_authentication.
3. Start docbase.
• ${Documentum}\dba\logs\<docbase>.log
• ${Documentum}\dba\logs\dm_krb_<docbase>.log
When the Kerberos protocol is not enabled on client machines, the web browser may
alternatively issue a NTLM token to respond the “HTTP 401 Negotiate” challenge.
NTLM authentication is not supported by the REST server.
When this problem occurs, contact your administrator to validate your Kerberos
deployment on the client machine.
When the Kerberos TGT is not configured to forward the tickets it produces, any
ticket that the Kerberos TGT produces (including the service ticket) is forwarded,
this causes the Kerberos authentication to fail.
When this problem occurs, contact your administrator to validate your Kerberos
service ticket generation policy on client machines.
When the service account, the owner of the SPN, is not configured as delegation
trust, the Kerberos authentication fails.
check whether the service account for the SPN has been configured as delegation
trust in KDC, or the service ticket issued on the client side has been configured to
allow it to be forwarded.
When this problem occurs, contact your administrator to validate service account
setting on KDC servers.
When using the CAS authentication scheme, you must install a CAS server (or
clustered CAS servers) to provide the authentication service. Additionally, you must
set up a directory service behind the CAS server and synchronize the directory users
to a Documentum Server repository. Documentum REST Services does not provide
CAS login or validation by itself. Instead, as a CAS client, REST Services forwards
unauthorized Documentum REST Services clients to the CAS server to perform
authentication and it validates the proof of principals on the CAS server by using its
trust relationship with the CAS server. Upon a successful validation, the REST
Services obtains a CAS proxy ticket on behalf of the clients to access the
Documentum Server repository, where a CAS plug-in must be installed to provide
the CAS proxy login capability.
5.4.1.1 Terminology
CAS-related terminologies are defined in the following table:
Term Description
Client Token (CT) Authentication token that the REST server
provides for clients. This token contains an
encrypted Documentum ticket and
additional metadata used for token
expiration. For more information about how
a client token works, see “Client token”
on page 171.
Ticket Granting Ticket (TGT) Ticket indicating that a client has
successfully logged in to the CAS server
Service Ticket (ST) Ticket that the CAS server sends to a service
for identifying that service
Proxy Granting Ticket (PGT) Ticket that the CAS server sends to a service
with valid an ST for requesting Proxy Tickets
Term Description
Proxy Ticket (PT) Ticket that a proxy service uses to access a
target service for multi-tier authentication
The following diagram illustrates the workflow of CAS authentication for browser
clients:
2. The REST server sends back a 302 status code redirection Response asking the
client to authenticate itself on the CAS server:
Response Status code: 302 Moved Temporarily
Location https://casserver:8443/cas/login?service=
https%3A%2F%2F2.gy-118.workers.dev/%3A443%2Fhttp%2F192.168.0.1%3A8080%2Fdctm-rest%2Frepositories%2Facme01
3. The browser client enters the username and password, and submits the Request
to the CAS Server.
4. The CAS Server connects to a directory service to verify the user credential.
5. After the verification, the CAS Server returns a 302 status code redirection
Response, providing the <ST> and <TGT> for the client.
Response Status code: 302 Moved Temporarily
Location http://localhost:8080/dctm-rest/repositories/acme01
?ticket=ST-238-mHMdsK0A9sAhie2T1dep-cas01.example.org
Set-Cookie CASPRIVACY=""; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/cas/
CASTGC=TGT-165-zm6GUY4gFJP3AjtY4EQiXJ7M5lIGJDiIevY6AZTlkOhg2F9J2Bcas01.
example.org;Path=/cas/; Secure
The <ST> is located in the ticket URL parameter in the return message and it
can only be used one time.
6. The client uses the ST obtained in step 5 to access the resource identified by the
Location Header of the Response. The TGT is located in the Set-Cookie Header.
The client uses the TGT cookie to acquire subsequent STs, without a need to
provide the username and password again.
7. The REST server negotiates with the CAS server to obtain the PT. For more
information about this process, see “Proxy Ticket Negotiation between REST
and CAS“ on page 113.
9. The CAS plug-in on Documentum Server calls the CAS server to validate the PT.
https://casserver/cas/proxyValidate
?service=http://localhost:8080/dctm-rest/repositories/acme01
&ticket=ST-957-ZuucXqTZ1YcJw81T3dxf
10. The CAS server validates the PT. If the PT is valid, the CAS server responds
Documentum Server with the proxy address.
<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>
<cas:authenticationSuccess>
<cas:user>testUser</cas:user>
<cas:attribute name="dmCSLdapUserDN" value="CN=testUser,CN=Users,
DC=ACME,DC=COM"/>
<cas:proxies>
<cas:proxy>https://192.168.0.1:8443/dctm-rest/cas/proxy/receptor
</cas:proxy>
</cas:proxies>
</cas:authenticationSuccess>
</cas:serviceResponse>
11. Documentum Server creates a session for the client specified in the CAS
Response and generates a ticket that the client is able to use for subsequent calls.
12. The REST server submits the actual call to Documentum Server by using the
session created in step 11.
13. Documentum Server returns the operation results to the REST server.
14. The REST server returns the results to the client, including a DOCUMENTUM-
CLIENT-TOKEN cookie (see “Client token” on page 171) that the client can use
for future calls.
Response Status code: 200 OK
Content-Typeapplication/json;charset=UTF-8
Set-Cookie DOCUMENTUM-CLIENT-TOKEN="Ym9ibGVlOkRNX1RJQ0tFVD1UMEpL...==";
Version=1; Path=/dctm-rest; HttpOnly
Response Body:
{ "id": 15,"name": "acme01",.....}
The following diagram illustrates the workflow of CAS authentication for non-
browser clients:
2. The REST server rejects the Request because it does not carry any authentication
proof.
If the DOCUMENTUM-NO-CAS-REDIRECT Header in the Request is set to true, the
REST server returns code 401 and puts the CAS RESTful ticket URL in the
Location Header.
=========== Request ===============
GET http://localhost:8080/dctm-rest/repositories/acme01
DOCUMENTUM-NO-CAS-REDIRECT: TRUE
Host: 192.168.0.1:8080
Location: https://casserver:8443/cas/v1/tickets
WWW-Authenticate : CAS realm="com.emc.documentum.rest"
3. The client sends a POST request to the CAS server to obtain a TGT.
=========== Request ===============
POST https://casserver:8443/cas/v1/tickets
Host: casserver:8443
Request Body: username={username}&password={password}
6. The client sends a POST request to the CAS server to obtain an ST for the
resource by using the TGT.
=========== Request ===============
POST https://casserver:8443/cas/v1/tickets/
TGT-166-AHw7Sv5wFnVtWaUQZzxOTRc5YxiGnMJPEVWyai0mFeTccjwnWa-cas01.example.org
Host: casserver:8443
Request Body:
service=https%3A%2F%2F2.gy-118.workers.dev/%3A443%2Fhttp%2F10.37.10.28%3A8080%2Femc-rest%2Frepositories%2Facme01
Note: The value of service in the Request body must to be URL encoded.
7. The client sends a POST request to the REST server to consume the resource,
with the ST appended in the ticket parameter.
=========== Request ===============
GET http://localhost:8080/dctm-rest/repositories/acme01
?ticket=ST-239-rYiYHoQJ2ZhJopdMuxjl-cas01.example.org
Host: 192.168.0.1:8080
8. The rest of the process is the same with steps 7 through 14 in “Authentication
negotiation between a browser REST client and the REST server“ on page 109.
1. To obtain the PGT IOU, the REST server calls the CAS server to validate the ST.
GET http://casserver/cas/serviceValidate
?ticket=ST-238-mHMdsK0A9sAhie2T1dep-cas01.example.org
&service=http://localhost:8080/dctm-rest/repositories/acme01
&pgtUrl=https://192.168.0.1:8443/cas/proxy/receptor
Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
CookieDOCUMENTUM-CLIENT-TOKEN="Ym9ibGVlOkRNX1RJQ0tFVD1UMEpL...=="
2. The CAS server validates the ST and makes a callback to the address specified
in the pgtUrl parameter of the REST request, passing the pgtIou (PGT IOU)
and pgtId parameters.
Note: pgtUrl must use HTTPS. The CAS server checks whether the SSL
certificate of pgtUrl is valid and whether the name matches that of the
Requested service.
3. The callback service (pgtUrl) responds the CAS server with HTTP 200.
4. The CAS server responds to the initial request for the PGT IOU:
<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>
<cas:authenticationSuccess>
<cas:user>halbej</cas:user>
<cas:proxyGrantingTicket>
PGTIOU-85-8PFx8qipjkWYDbuBbNJ1roVu4yeb9WJIRdngg7fzl523Eti2td
</cas:proxyGrantingTicket>
</cas:authenticationSuccess>
</cas:authenticationSuccess>
</cas:serviceResponse>
5. The REST server retrieves the PGT (pgtId) from the callback service by
providing the PGT IOU.
6. In order to obtain a PT, the REST server passes the PGT to the CAS server.
GET https://casserver/cas/proxy
?targetService=http://localhost:8080/dctm-rest/repositories/acme01
&pgt=PGT-330-CSdUc5fCBz3g8KDDiSgO5osXfLMj9sRDAI0xDLg7jPn8gZaDqS
The following workflow explains the single sign-out process in more detail:
1. A client sends a request to the REST server for logout by providing a CT.
GET https://rest-server:8443/cas/logout HTTP/1.1
Cookie : DOCUMENTUM-CLIENT-TOKEN= AYQEVn....DKrdst
2. The REST server validates the CT and resets it, and then redirects the REST
client to the CAS server for logout.
HTTP/1.1 302 302 Moved Temporarily
Set-Cookie: DOCUMENTUM-CLIENT-TOKEN= ""; Expires=Thu, 01-Jan-1970 00:00:10 GMT;
Path=/dctm-rest; HttpOnly; Secure;
Location: https://cas-server:8443/cas/logout
3. The REST client resets the client side CASTGC cookie and access the CAS server
for logout.
GET https://cas-server/cas/logout HTTP/1.1
Cookie : CASTGC= TGT-AYQEVn....DKrdst
4. The CAS server destroys the TGT from its memory entry and sends back HTTP
200 with an empty CASTGC cookie.
HTTP/1.1 200 OK
Set-Cookie: CASTGC= ""; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/cas/
5. The REST client rests its client side TGT cookie, and the single sign-out finishes.
Both TGT and CT are invalidated.
5.4.4 Configuration
CAS proxy authentication on Documentum REST Services requires configurations
on the following servers:
[DM_CAS_AUTH_CONF]
# Server host is the domain or host which is used in connecting to CAS
# server.
server_host=<host_ip>
server_port=<port_number>
#url path used in http requests sent to the CAS server to validate proxy
ticket.
url_path=/<cas_application_name>/proxyValidate
# Target Service name for which the proxy ticket was generated.
service_param=ContentServer
• The “Managing the login ticket key” section in the OpenText Documentum
Server Administration and Configuration Guide
• The “Trusting and trusted repositories” section in the OpenText Documentum
Server Fundamentals Guide
2. Add the following dependencies to the corresponding pom.xml file under \cas-
server-uber-webapp or \cas-server-webapp, depending on which CAS
WAR you modify.
<dependency>
<groupId>org.jasig.cas</groupId>
<artifactId>cas-server-support-ldap</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.jasig.cas</groupId>
<artifactId>cas-server-support-generic</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.jasig.cas</groupId>
<artifactId>cas-server-integration-restlet</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.jasig.cas</groupId>
<artifactId>cas-server-integration-ehcache</artifactId>
<version>${project.version}</version>
</dependency>
4. Add the following servlet mapping into \WEB-INF\web.xml of the CAS WAR
package you built in step 3:
<servlet>
<servlet-name>restlet</servlet-name>
<servlet-class>com.noelios.restlet.ext.spring.
RestletFrameworkServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>restlet</servlet-name>
<url-pattern>/v1/*</url-pattern>
</servlet-mapping>
You must set serviceId to ContentServer. Additionally, you must set the
allowedAttributes property with a list of attributes.
Method 2: Register Documentum Server by using the Service Management application
<bean id="contextSource"
class="org.springframework.ldap.core.support.LdapContextSource">
<property name="pooled" value="false"/>
<property name="url" value="ldap://domainctlr.iigplat.com:389" />
<property name="userDn"
value="CN=Administrator,CN=Users,DC=iigplat,DC=com"/>
<property name="password" value="password"/>
<property name="baseEnvironmentProperties">
<map>
<entry key="com.sun.jndi.ldap.connect.timeout" value="3000" />
<entry key="com.sun.jndi.ldap.read.timeout" value="3000" />
<entry key="java.naming.security.authentication"
value="simple" />
</map>
</property>
</bean>
<property name="resultAttributeMapping">
<map>
<entry value="dmCSLdapUserDN" key="distinguishedName"/>
</map>
</property>
</bean>
Notes
11. Import the CAS server certificate to the CAS server’s JRE keystore with the
following command:keytool -import -keystore <path-of-the-jre-
cacert> -storepass "<password>" -alias "<cas-alias>" -file <path-
of-the-cas-certificate-file>
12. Import the REST server certificate to the CAS server’s JRE truststore with the
following command:keytool -import -trustcacerts -alias "<rest-
alias>" -file<path-of-the-rest-certificate-file> -keystore <path-
of-the-jre-cacert>
13. Enable HTTPS on the web container where you plan to deploy the CAS WAR
file.
2. Import the REST server certificate to the CAS server JRE keystore with the
following command:keytool -import -keystore <path-of-the-jre-
cacert> -storepass "<password>"-alias "<rest-alias>" -file <path-
of-the-rest-certificate-file>
3. Import the CAS server certificate into JRE keystore of the REST server with the
following command:
keytool -import -trustcacerts -alias "<cas-alias>" -file<path-of-
the-cas-certificate-file> -keystore <path-of-the-jre-cacert>
If the REST server is placed behind a reverse proxy server, you do not have to
set SSL on the REST server. The setting can be configured on the proxy server.
For more information about how to configure this setting on a proxy server, see
“Reverse Proxy Server” on page 121.
5. For Linux operating systems, if client tokens are used in your deployment, add
the following option to the startup script the application server where
Documentum REST Services is deployed to achieve better performance:
JAVA_OPTS="$JAVA_OPTS -Djava.security.egd=file:/dev/./urandom"
• rest.security.auth.mode
• rest.security.realm.name
• rest.security.cas.server.url
• rest.security.cas.server.login.url
• rest.security.cas.server.logout.url
• rest.security.cas.server.tickets.url
• rest.security.cas.proxy.service
• rest.security.server.url
• rest.security.cas.callback.service.url
• rest.security.auth.cas.client.pgt.storage
Note: The default settings of these properties work in most cases. Keep the
original settings unless you have special business requirements.
Follow the instructions on the following web site to configure CAS clustering from
the Atlassian website.
Note: If CAS clustering utilizes Ehcache to make all nodes in the cluster
recognize and validate each other’s tickets, make the following modifications:
• In ${CAS}\WEB-INF\spring-configuration\ticketRegistry.xml, set
shared of the cacheManager bean to true
During the PT negotiation between the REST server and CAS server, the CAS server
has to make a callback to the REST server requesting the PGT (see “Proxy Ticket
Negotiation between REST and CAS“ on page 113). If you deploy REST servers in a
cluster, the callback may not find the REST server requesting the PGT. Therefore, all
REST servers must maintain the same PGT IOU/PGT mappings. To do this, REST
servers utilizes Ehcache to perform the replication of PGT IOU/PGT mappings across
the cluster.
To enable Ehcache for PGT IOU/PGT mappings in Documentum REST Services, the
following setting must be configured in rest-api-runtime.properties:
rest.security.auth.cas.client.pgt.storage=ehcache
For more information about this property, see the instruction in rest-api-
runtime.properties.
Additionally, you must follow the instructions at the Ehcache website to configure
Ehcache in <dctm-rest>/WEB-INF/classes/rest-api-common-ehcache.xml.
• For all REST servers in the parent cluster, the callback URL must be set to the
address of the reverse proxy server that is placed in front of the child cluster. (In
the diagram, Reverse Proxy Server 2)
• For all REST servers in the child cluster, the following setting must be configured
in rest-api-runtime.properties:
rest.security.auth.cas.client.pgt.storage=ehcache
• You must configure peer discovery for all REST servers in the child cluster and
all of these servers must be set as peers.
Four main participants in the Documentum REST Services OAuth 2.0 scenario:
• OAuth Server—The server issues access tokens to the client after successfully
authenticating the resource owner and obtaining authorization
• Documentum Client App— The client application that requests to access
protected resources on behalf of the resource owner.
• Documentum REST Services—The protected resources exposed in REST API.
• Documentum Server—The back-end resource server for protected resource
In additional to OAuth 2.0, OpenId Connect (OIDC) 1.0 is a simple identity layer on
top of OAuth 2.0 protocol, which allows the relying parties to exchange and verify
user identities along with OAuth 2.0 tokens. Documentum REST Services also
support to verify OAuth access token by OIDC protocol.
Besides these two protocols, Documentum Client Token has been integrated with
the authentication flow, which simplifies the authentication and authorization for
authenticated Requests.
5.5.1.2 Prerequisites
5.5.1.2.1 Architecture flow
1. The Documentum Server combines with OTDS and shares users and groups
2. The REST server uses the Documentum Server as it’s authentication and
resource server
3. A REST client application sends a token to the REST server who then passes it to
the Documentum Server for authentication
4. The Documentum Server authenticates the token in cooperation with OTDS
5. REST gets the authentication response from Documentum Server.
Before you can proceed, you must have the following in place:
Warning
Avoid using Documentum Server 16.4 P07 and 16.4 P08 for integration with
the Core REST server.
• Active Directory (AD) installed, preferably on a separate machine
• OTDS 16.2.3 or later installed with the otds-admin and the otdsws services
started
Follow these steps to use the otds-admin service as the UI to configure the properties
of OpenText Directory Services (OTDS):
Note: This step only applies to the simplified internal testing environment.
When the environment must use HTTPS/SSL, as such is the case with
OpenText Cloud, then omit this step and do not enable HTTP. Proceed directly
from here to Partitions.
When you want to use OTDS with HTTP, then HTTP must be enabled. Otherwise,
you can skip this step:
5.5.1.3.3 Partitions
1. In OTDS, click on SETUP in the left-hand navigation to open the slide down
menu.
2. In the left-hand slide down menu, click on the Partitions tab to go to the
Partitions screen.
3. In the toolbar at the top of the Partitions screen, click on the Add button and
select New User Synchronized Partition from the flyout menu.
4. Click the Connection Information tab. Enter the Host name or address and Port
of your AD.
5. Click Test Connection and make sure that Connected Successfully is displayed
in the status area above the button.
6. Click on the Authentication tab to view the authentication properties of the 67-
ContentServer164 partition. Fill in the User name and Password of the
Administrator account for your AD.
7. Click the Test Authentication button to make sure that this Administrator
account is working as expected.
8. Click the General tab and enter a value in the Name and Description fields of
this new partition.
Warning
Once the name is set, it cannot be changed.
9. Click the Server Setting tab and set the Server Type and Naming context for the
AD.
10. By default, all groups and users are automatically included in a new partition.
By using the Group Locations and User Locations tabs, you can decide which
groups or users are included in this partition.
Set the groups or users to include in the new partition.
11. Your new OTDS partition has been created. Click the Save button to save the
partition after the configuration settings have been made.
Note: Any tabs that are not explicitly mentioned here can be left with their
default values intact. If an update to these default values must be made,
refer to the OTDS team for detailed configuration instructions.
The users and groups that are imported from the AD can be found by clicking
Actions then selecting View Members.
For more information on how to create a synchronized user partition, see the
OpenText Directory Services Installation and Administration Guide.
5.5.1.4 Resource
Resources can be any ECM server, such as Documentum Server, in this case. They
contain configuration information about the server URL, credentials, OTDS to
docbase attributes mapping of dmotdsrest service in Documentum Server.
Note: The dmotdsrest service is deployed using the war file found here:
<Documentum Server root folder>\wildfly<version number>\server\
DctmServer_MethodServer\deployments\ServerApps.ear\dmotdsrest.
war.
1. Go to the Resources screen and click the Add button to add the dmotdsrest.
war file as a new resource.
Warning
Once the value for Resource Name has been set, it cannot be changed.
3. Click on the Synchronization tab to go to the Synchronization screen.
4. In the Synchronization screen, select the User and group synchronization check
box. This enables a number of other check boxes.
5. In the Synchronization screen, select Delete users and groups from the check
box.
6. While still in the Synchronization screen, in the Synchronization connector box,
select REST (Generic) from the list.
7. Click the Connection Information tab. Enter a value for each of the Base URL,
Username, and Password fields of the dmotdsrest service and its Administrator.
8. Click the Test Connection button and make sure that Connected Successfully is
displayed in the status area above the button.
9. Optional Step: Click the User Attribute Mappings tab to view the OTDS to AD
attributes.
These attributes can be modified according to your specific needs or usage
requirements. oTExternalD3 is used as the value for user_login_name and cn is
used as the value for user_name. For example, [email protected] is used as
the value for user_login_name and otadmin is used as the value for user_name
in Documentum Server.
10. Optional Step: The values of the resource attributes can be set for the attributes
shown on the Group Attribute Mappings tab.
11. Click the Save button to save this resource after you have completed its
configuration.
12. An access role, which is named after the Resource, is automatically created in
Access Roles.
For more information on how to create a synchronized resource, see the OpenText
Directory Services Installation and Administration Guide.
For more information on access role, see the OpenText Directory Services Installation
and Administration Guide.
API> append,c,l,app_server_name
SET> OTDSAuthentication
API> append,c,l,app_server_uri
SET> http://localhost:9080/OTDSAuthentication/servlet/authenticate
API> save,c,l
1. Go to http://<otds-server>:8080/otdsws/rest/systemconfig/
certificate_content to get the certificate content of OTDS.
4. Open the otdsauth.properties file and paste the certificate information after
certificate=.
Note: Erase all spaces and line returns within the certificate content that
you copied to make sure that it is a continuous String.
5. Set http://<otds-server>:8080/otdsws/rest/authentication/
credentials after otds_rest_credential_url=.
Note: Using IAPI, make certain that Documentum Server and OTDS are
completely configured and integrated before using REST Services.
• To make sure that token mode is ready to be worked with, use the following
command when otds_token, ct-otds_token is used as the authentication
mode in runtime properties:
API> connect,<REPO_NAME>,null,dm_otds_ticket=<ACCESS_TOKEN_FROM_OTDS>
• To make sure that password mode is ready to be worked with, use the
following command when otds_password, ct-otds_password is used as the
authentication mode in runtime properties.
API> connect,<REPO_NAME>,<USER_LOGIN_NAME>,dm_otds_password=<PASSWORD_ON_OTDS>
Make sure the proper command, according to your authentication mode, can
display information without any exceptions before you continue.
• Enter the following URL to configure OAuth clients on OTDS and log in with the
default administrator account, [email protected].
https://<otds_ip>:<otds_port>/otds-admin
• Select Redirect URLs, add the REST URL, and save your changes
Using the implicit flow of OAuth 2.0, get an access token from OTDS. This sample
shows you how to get that access token.
1. Send the following Request to retrieve the access token from OTDS:
POST http://otds-server:8080/otdsws/login HTTP/1.1
Content-Type: application/x-www-form-urlencoded;charset=utf-8
Follow these steps to create and send a REST Request that contains an access token:
Copy an existing access token from a former response and use it in one of the two
ways shown here:
• Add the prefix Bearer to the access token and paste it into the Header
Authorization property of your HTTP request.
• Using the GET HTTP method, paste the access token after the URL to use it as a
query parameter value.
GET http://<otds-server>:<otds-port>/dctm-rest/repositories/
REPO?access_token=<paste_access_token_value_here>
The result is that access to the requested URL is given to the REST client.
• The User Agent sends the request to the OAuth2 server to obtain an access token.
• The OAuth2 server forwards the request for user authentication and, if
authenticated, sends back an access token.
• The User Agent sends a REST request to the REST Server along with the access
token.
• The REST Server validates the access token, authenticates the user with
Documentum Server, and then returns a Client Token cookie.
The REST Server does not care how the User Agent goes about getting the OAuth
access token from the OAuth2 server. The REST Server starts to handle the OAuth
2.0 authentication by examining the access token from the Request that it receives
from the User Agent.
1. The User Agent makes a request to the REST Server for a protected resource.
2. The REST Server responds with a 401 Unauthorized if the OAuth access token
or client token is not found within the Request.
HTTP/1.1 401 Unauthorized
Content-Type: application/json;charset=UTF-8
WWW-Authenticate: Bearer acme.com
Location: https://oauth-server.acme.com/login
3. The User Agent attempts to make an OAuth authentication. The user client
application should handle the URL redirection correctly.
4. The OAuth2 server requests that the User Agent provide it with credentials.
5. The User Agent provides the requested credentials to the OAuth2 server. The
above two sequences (marked in red) indicate that the Request and the
Response are out of the scope of the OAuth2 server.
6. The OAuth2 server tries to verify the credentials by communicating with the
backend identity provider.
7. The OAuth2 server Requests for authorization now grant scope. The scope can
limit the authorization range of the User Agent.
8. The User Agent grants scopes for the protected resource on behalf of the
resource owner and asks for the authorization grant.
grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA
&redirect_uri=https%3A%2F%2F2.gy-118.workers.dev/%3A443%2Fhttps%2Fclient%2Eexample%2Ecom%2Fcb
9. The OAuth2 server grants the access token to the User Agent.
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"example",
"expires_in":3600,
"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
"example_parameter":"example_value"
}
10. The User Agent requests the protected resource from the REST Server.
GET localhost:8080/repositories/REPO HTTP/1.1
Content-Type: application/json;charset=UTF-8
Authorization: Bearer 2YotnFZFEjr1zCsicMWpAA
11. The REST Server validates the access token. There are two different ways to
validate the access token; locally or remotely. For more information, see
“Configuration on the REST Server” on page 137.
12. The REST Server extracts the user login name from the access token validation
result and then makes a request to the Documentum Server for principal login.
14. The REST Server confirms login success and saves the security context.
15. The REST Server represents the protected resource for the User Agent. When
REST is configured to support a client token, then REST creates a new
Documentum Client Token and sends it to the User Agent. Then the User Agent
can directly use this client token to request other REST resources without going
through the OAuth 2.0 authorization process again.
5.5.3 Configuration
You must configure the following servers to perform OAuth 2.0 authentication on
Documentum REST Services:
• When you are using MAC, the OAuth Server and the REST Server must have the
same key
• When you are using RSA, the OAuth Server must have the private key and the
REST server must have the public key. You must configure and deploy these
keys
For more information, see the Documentum Server documentation for details.
# The url of login entry point for OAuth 2.0 to indicate user where to send request.
# This value is used as a hint when oauth2 error occurs.
# The default value is empty.
rest.security.oauth2.login.url=
# The key path of a series of keys to extract principal related value from claims.
# Keys should be separate with slashes, i.e, '/'. All spaces are ignored.
# Every value mapped with key is considered as map and passes to next key to handle.
# This value is mandatory so that REST can principal login in Documentum Server.
# The default value is empty.
rest.security.oauth2.principal.key=
# The pattern to extract principle name from value mapping to key in claims.
# The pattern should be standard string version of java.util.regex.Pattern.
# For example, if value is 'CN=admin' and 'admin' is wanted, pattern should be 'CN=(.*)'.
# If multiple matches are found, the first one would be used.
# The default value is .*.
rest.security.oauth2.principal.pattern=
# The userinfo endpoint url provided by Auth Server if it supports Openid Connect.
# This value is mandatory if access token needs to be verified by
endpoint remotely rather than locally as standard JWT.
# UrlValidator is used to validate if this url can be used with local url supported.
# If this value is set, claims will be extract from Response of requesting it by access
token.
rest.security.oauth2.userinfo.endpoint.url=
# The symmetric key for access token to verify signature. Server side should hold the
same key.
# Make sure 'rest.security.oauth2.access_token.jws.type' is set to be 'MAC'.
rest.security.oauth2.access_token.jws.sym_key=
# The asymmetric key for access token to verify signature. Server side should hold the
private key matching with this public key.
# It could be set like '/Users/username/keys/publicKey'.
# Make sure 'rest.security.oauth2.access_token.jws.type' is set to be 'RSA'.
rest.security.oauth2.access_token.jws.public_key.file=
# The encrypt key for access token using JWE. Only symmetric key is supported.
# Make sure 'rest.security.oauth2.access_token.jwe.type' is set to be 'AES'.
# The default value is null.
rest.security.oauth2.access_token.jwe.sym_key=
# The expected value of claim 'iss' in clear-text claims. Usually it varies with Auth
Server.
# This value is mandatory if access token needs to be verified as standard JWT locally.
# This is used to verify token whether is from expected server or not.
# The default value is empty.
rest.security.oauth2.claims.issuer=
Here is a diagram that illustrates how REST handles an access token, which may be
helpful for configuration:
Note: The client does not communicate with the REST server directly
because the REST server is protected by the RSA web agent.
2. The RSA web agent challenges the user credentials when the client does not
provide them in step 1.
RSA Access Manager supports many authentication types, such as, HTTP Basic,
Client Certificate, and Smart Card. You can configure RSA Access Manager to
specify the authentication type to negotiate the credentials between the client
and the RSA server. The REST client must be able to handle the credential
challenge and submit the corresponding credentials to the RSA server.
3. The RSA web agent dispatches the credentials to the RSA Access Manager.
Upon a successful login, The RSA web agent retrieves a list of user attributes
together with a ClearTrust token from the RSA Access Manager.
4. The RSA Access Manager communicates with the back-end user data store to
verify the credentials.
The back-end user data store can be an LDAP server or a database server
depending on the RSA Access Manager configuration.
5. The RSA web agent forwards the original REST resource request to the REST
server.
The RSA ClearTrust token is sent to the REST server as a cookie. Additionally, a
list of user attributes is populated to the REST server as HTTP Headers. Among
these Headers, the remote username Header is required. The REST server uses
this Header to log in to Documentum Server.
6. The REST server uses the RSA token and username to log in to Documentum
Server.
A session is created for the REST server to perform further resource operations.
7. Documentum Server validates the RSA token against the RSA Access Manager.
Upon a successful validation, Documentum Server returns a session to the REST
server. Documentum Server also has to know from which RSA Access Manager
host this token was obtained. Only the hosts on the trusted RSA servers are
allowed.
8. The REST server returns the requested resource to the REST client. When
forwarding the resource back to the client, the RSA web agent appends an RSA
token to the Response as a cookie. Therefore, the client can reuse the RSA token
for further resource requests.
5.6.2 Configuration
RSA authentication on Documentum REST Services requires configurations on the
following servers:
In some cases, an LDAP user that has been authenticated to RSA may encounter a
token validation failure against Documentum Server, and the REST server may
return the following message:
Status code: 401 Unauthorized
Content-Type: <literal>application/json</literal>;charset=UTF-8
WWW-Authenticate: RSA realm="ACME.COM"
{
"status": 401,
"code": "E_BAD_CREDENTIALS_ERROR",
"message": "Authentication failed because an invalid credential was provided.",
"details": "User authentication has been passed in RSA Access Manager
but the clearTrust cookie validation has failed in Documentum Server;
(DM_SESSION_E_AUTH_FAIL) Authentication failed for user tom with
docbase acme01."
}
This problem mainly occurs when the LDAP user is not synchronized to
Documentum Server, or the RSA plug-in is not correctly installed.
ProxyRequests Off
ProxyPreserveHost On
ProxyPass /dctm-rest http://localhost:8080/dctm-rest
ProxyPassReverse /dctm-rest http://localhost:8080/dctm-rest
Optionally, you may need to update the RSA web agent for the REST deployment by
modifying the following parameters in ${RSA_AGENT}\conf\webagent.conf:
cleartrust.agent.cookie_name=
cleartrust.agent.user_header_list=
cleartrust.agent.exported_headers=
cleartrust.agent.cookie_touch_window=
cleartrust.agent.form_based_enabled=False
cleartrust.agent.auth_resource_list=/*=BASIC
The RSA web agent runs as a reverse proxy to filter resource requests and dispatches
authentication requests. Follow the instructions in “Reverse proxy configuration”
on page 169 to configure the RSA web agent.
1. Add a new server for the RSA Web Agent, for example Apache HTTP Server.
3. Add a new resource to the application you created in step 2 with the resource
type URL and set the URL value to dctm-rest/repositories/*
4. Navigate to the Entitlements settings of the user or groups that the user belongs
to, and select Allow Access on the application and resource.
1. A client sends a request to the SiteMinder agent (a reverse proxy server for the
REST server). The agent challenges the client for credentials if the client does
not provide.
3. The policy server validates user credentials according to the information in the
policy store which can be an LDAP server or a database server.
4. After the validation, the username and password token is sent back to the
SiteMinder agent.
5. The SiteMinder agent forwards the client request together with the HTTP
Headers, which include the token, to the REST server.
After the validation of the token, the REST server returns the Response to the
SiteMinder agent. Then, the agent forwards the Response to the client.
5.7.2 Configuration
A SiteMinder environment includes multiple components. This section introduces
the configurations that you must perform on the REST server and Documentum
Server.
For more information about the configurations on the reverse proxy server, see
“Reverse proxy configuration” on page 169.
By default, the SiteMinder web agent protects web sites against Cross-Site Scripting
by setting the CssChecking parameter to yes. In this case, URLs containing the
following characters do not work:
Content-media resources support the cache mechanism by using HTTP Headers (the
etag Header in a response and the if-none-match Header in a request). However,
the default behavior of the SiteMinder web agent removes the cache-related HTTP
Headers from the Request before the web agent passes a request to the REST server,
which causes the REST server to always return HTTP 200 when you try to retrieve a
content-media resource.
Documentum REST Services does not have any restriction on characters used in
URLs. However settings of the BadUrlChars and BadUrlChars parameters may
cause restrictions on characters. Consider modifying these parameters when a
request is rejected for bad URL character reasons.
Documentum REST Services does not limit the length of URLs that a web agent can
handle. However, the MaxUrlSize parameter specifies the maximum size (in bytes)
of a URL, which defaults to 4096. Consider increasing the value of this parameter
when a request is rejected for long URL reasons.
With SAML SSO for Documentum REST Services the roles of each of the three
components are as follows:
2. The <Service Provider> works as a two component (front end / back-end) entity:
• In addition to the <Principal>, the <Identity Provider>, and the <Service Provider>, a
new participant, the <Documentum Server>, is involved in the authentication
workflow. After the Documentum REST Services server successfully verifies the
Response token, it passes the SAML assertion token to the Documentum Server
to create a DFC session. When the token is validated on the Documentum Server,
the Documentum REST Services server receives the DFC session, which can then
be used to execute various operations
• Since the SAML SSO login involves multiple parties to complete its
authentication process, the SAML login is slower than a single pass-through
login. Therefore, to avoid using a SAML login for each Request, Documentum
REST Services for SAML SSO is integrated into the Documentum Client Token.
After the SAML login succeeds, the Documentum REST Server must have the
Documentum Login Ticket from the Documentum Server and the integrated
SAML DFC session to proceed. The Documentum REST Server uses these
elements to produce and set the Client Token in a REST client cookie. All
subsequent REST Requests use that Client Token as their authentication, with
the exception of the SAML login.
During the deployment phase, the REST Server can have one or multiple Identity
Provider metadata files. When more than one Identity Provider is available, the
Identity Provider used to authenticate is determined during the SAML SSO
initialization, also known as the <Identity Provider Discovery>.
In this step, the <User Agent> does not know which <Identity Provider> to use for
authentication.
2. Determines which Identity Provider to use
Since the REST Server has the metadata file of only one Identity Provider, this
becomes the Identity Provider used.
When multiple Identity Provider metadata files are configured on the REST
Server, there is an additional discovery phase where the User Agent and server
determine which Identity Provider to use. The multiple Identity Provider case
will be described later in this document.
3. The REST Server initializes SAML <AuthnRequest> and sends it to the Identity
Provider
The REST Server assembles a SAML <AuthnRequest> and redirects the User
Agent to the Identity Provider.
A <DOCUMENTUM-SAML2-REQUEST-TOKEN>, in the form of a cookie, is sent
back to the REST client to validate the SAML < Response> against the original
SAML Request.
//RESPONSE - Redirect the User Agent to the Identity Provider
// with SAML <AuthnRequest>
HTTP/1.1 302 Found
Set Cookie DOCUMENTUM-SAML2-REQUEST-
TOKEN-a59abj125gb93th3447ae97ie8b1a7d; Secure; HttpOnly
Location https://idp.rest.org/idp.rest.org/idp/profile/SAML2/Redirect/SSO?
SAMLRequest-jVLLbs...sb4h8%3D&RelayState-%2Frepositories%2FREPO&SigAlg
https%3A%2F%2F2.gy-118.workers.dev/%3A443%2Fhttp%2Fwww.w3.org%2F2000%2Fxmldsig%23rsa-sha1&Signature=
HSW...%3D%3D
=8fa4ae6aa43d337ec48fbf355dd21bf1b81d8e3524787f2a6b675872c19f86ea;
Location https://idp.rest.org:443/idp/AuthnEngine
=8fa4ae6aa43d337ec48fbf355dd21bf1b81d8e3524787f2a6b675872c19f86ea
=8fa4ae6aa43d337ec48fbf355dd21bf1b81d8e3524787f2a6b675872c19f86ea
<section>
<label for="username">Username</label>
<input class="form-element form-field" name="j_username" type="text"
value="">
</section>
<section>
<label for="password">Password</label>
<input class="form-element form-field" name="j_password" type="password"
value="">
</section>
<section>
<button class="form-element form-button"
type="submit">Login</button>
</section>
</form>
...
</body>
</html>
j_username=joe&password=passw0rd
5. The Identity Provider issues SAML < Response> to the REST Server
When the login credentials of the Principal are verified by the <Identity Prover>, a
SAML <Response> is created by the Identity Provider and redirected to the
REST Server by the User Agent.
The DOCUMENTUM-SAML2-REQUEST-TOKEN cookie is used to verify that the SAML
<Response> came from the original Request.
//RESPONSE - Redirect the User Agent to the IdP SSO service
HTTP/1.1 302 Found
Set-Cookie _idp_session=MTAuMzIuOTQuMjE%3D%7CLQ%3D%3D
%7CMjg1NWE5MmZiMWQyMjBjNGIxM2FiNzRhM
mZiMTY1ZWQ1ZjI4NzQ4MTg5NjgwMDg1ZTk2OTU1YmQwNzM3MGJmZA%3D%3D
%7CBN8tDDMNFj
soPRouakNi6a%2Bg910%3D;
Version=1;
Path=/idp;
Secure Location https://idp.rest.org:443/idp/profile/SAML2/Redirect/
SSO
_idp_authn_lc_key=8fa4ae6aa43d337ec48fbf355dd21bf1b81d8e3524787f2a6b675872c19f86ea;
_idp_session=MTAuMzIuOTQuMjE%3D%7CLQ%3D%3D
%7CMjg1NWE5MmZiMWQyMjBjNGIxM2FiNzRhMmZiMT
Y1ZWQ1ZjI4NzQ4MTg5NjgwMDg1ZTk2OTU1YmQwNzM3MGJmZA%3D%3D
%7CBN8tDDMNFjsoPRouakNi
6a%2Bg910%3D
When the principal name and the SAML <Response> are verified, Documentum
Server returns a session for the Principal. At this point, the SAML authentication
has been successfully completed.
9. Gets User Login Ticket
The Client Token is used after the SAML authentication succeeds. Using the
SAML login session, the REST Server requests a login ticket for the authenticated
user.
10. Returns User Login Ticket
The Documentum Server returns a user login ticket to the REST Server.
11. Produces a Client Token Cookie
Using the user login ticket, the REST Server produces a Client Token cookie for
the User Agent.
12. Redirects to the Original REST Resource with the Client Token Cookie
When the User Agent has the Client Token cookie set, it can make a request for
the original target resource using Client Token authentication. This process
clears the DOCUMENTUM-SAML2-REQUEST-TOKEN cookie as the SAML
authentication completes.
//RESPONSE - the REST Server redirects the the User Agent to the original REST
resource
HTTP/1.1 302 Found
Set-Cookie: DOCUMENTUM-SAML2-REQUEST-TOKEN=""; Expires=Thu, 01-Jan-1970 00:00:10
GMT;
Path=/dctm-rest
Set-Cookie: DOCUMENTUM-CLIENT-TOKEN="3fVt7mVQ
+ivzpYtp8CV70JLCWJfKiRFhy6w34NUYhaJfZWOxAG1M
sbY2LrkBkwp5sGykIT3MhSiGM9gdOfr8GeUjFvxDC1Z1ah2gQHlz2KkQs3i61cf7KQIcAr0xrl6as+2s
+U8lFP
DaNbuA6Hp8P0ly/ODHaAdkLQJwQ7Ev0ozqdtDdBXyjxUyVZatsak5NzaIDgclBShk2xLeTbifuM/
SL2T4zEtOvl
rsn4hQDkJJi9HmXCLQFuEIBP9b78/
WAEAkPn9RsMQP9xn02NZBd0mcky1wJxGLj9XWVASjDG6xjGvdWp6fMCvnT
732CyaIZmNCdD2vT9/GnK29bMspMImKVHrpwRQ01mhnzrrpjbYXrXQB6g
+3ZWHow1crnkNXtovKMCv7+6g68YWG
JjKQ4gjCz/
SGLwMH1zRmEhorNsjQBETZjg1ixEFYeHXQUisdUkaZ2WAiM3hDlB96+UIWxL736JRXIA7bAwQ+JtP
9TS5g="; Version=1; Max-Age=10000; Expires=Fri, 04-Mar-2016 04:54:31 GMT;
Path=/dctm-rest; Secure; HttpOnly
Location: https://restsp:8443/dctm-rest/repositories/REPO
Summary of steps 13 to 17
Steps 13 to 17 are typical Client Token ticket sign-on steps. The User Agent can
reuse the existing Client Token cookie to execute subsequent REST Requests until
the cookie expires.
//RESPONSE - return the REST resource
HTTP/1.1 200 OK
Content-Type: application/xml;charset=UTF-8
In some cases, the User Agent does not want to be redirected to an Identity Provider
after it makes a request for a REST resource.
Typically the repository resource is first to require authentication. At this point, the
User Agent does not know which Identity Provider will be used to authenticate.
//REQUEST - initial repository resource request
GET https://restsp:8443/dctm-rest/repositories/REPO HTTP/1.1
DOCUMENTUM-NO-SAML2-REDIRECT: true
//RESPONSE - return Unauthorized HTTP status code and header Location with the URL for
sending
SAML authentication request HTTP/1.1 401 Unauthorized
Set-Cookie: Path=/dctm-rest/; Secure; HttpOnly
WWW-Authenticate: SAML2 realm="com.emc.documentum.rest"
Location: https://idp.rest.org/idp/profile/SAML2/Redirect/SSO?SAMLRequest=jVLLbs...
sb4h8%3D&RelayState=%2Frepositories%2FREPO&SigAlg=https%3A%2F%2F2.gy-118.workers.dev/%3A443%2Fhttp%2Fwww.w3.org%2
F2000%2F09%2Fxmldsig%23rsa-sha1&Signature=HSVVv...%3D%3D
Once the User Agent gets the Response with a 401 status code, it can determine
whether to continue the SAML authentication process. If yes, the User Agent can
follow the URL in the Location HTTP Header then all subsequent steps are the
same as the typical case shown.
//REQUEST - send request to the discovery service with the HTTP header
Referer;
the orìginal URL is set in this header
GET /dctm-rest/saml/discovery?entityID=https%3A%2F%2F2.gy-118.workers.dev/%3A443%2Fhttps%2Frestsp%3A8443%2Fdctm
-rest%2Fsaml%2FmetadatareturnIDParam=idp HTTP/1.1
Referer: https://restsp:8443/dctm-rest/repositories/REPO
b. The REST Server sends the URLs of configured Identity Providers (in the
Location HTTP Header) for the User Agent to select.
//REQUEST - request the original REST resource
GET /dctm-rest/repositories/REPO HTTP/1.1
Host: restsp:8443
Referer: https://idp.rest.org/idp/profile/SAML2/Redirect/SSO
Cookie: DOCUMENTUM-CLIENT-TOKEN="3fVt7mVQ+ivzpYtp...JRXIA7bAwQ+JtP9TS5g="
c. In this step, the User Agent selects the URL of a configured Identity
Provider. Now the subsequent steps are same as those shown in the “SAML
SSO with one Identity Provider” on page 148 example.
When the User Agent has already determined the Identity Provider and does not
want to trigger another <Discovery> phase, the User Agent can skip the discovery
phase by appending a URL parameter idp=idp-entity-id to the URL of the REST
resource requested. For example, the URL parameter
idp=https://idp.rest.org/idp/shibboleth is added to the URL of a resource to
skip the <Discovery> phase.
The URL that is specified by the success-url URL parameter must be in the white
list that is defined in the rest.security.sso.login.success.url.whitelist
runtime property.
1. Attempts to Logout
The User Agent sends a logout Request to the REST Server. The logout URI can
be specified using the <rest.security.logout.url> common runtime property.
//REQUEST - initiate the logout
GET /dctm-rest/logout HTTP/1.1
Host restsp:8443
2. a. Resets the cookies and redirects to SAML local logout. At this point, the
Client Token and SAML Request Token cookie are reset
//RESPONSE - clear the cookies and redirect to SAML logout URL
HTTP/1.1 302 Found
Set-Cookie DOCUMENTUM-CLIENT-TOKEN=""; Expires=Thu, 01-Jan-1970 00:00:10 GMT;
Path=/dctm-rest
Set-Cookie DOCUMENTUM-SAML2-REQUEST-TOKEN=""; Expires=Thu, 01-Jan-1970 00:00:10
GMT;
Path=/dctm-rest
Location https://restsp:8443/dctm-rest/saml/logout?local=true
5.8.3 Configuration
Documentum REST Services SAML SSO requires configuration on the following
servers:
• REST Server
• Identity Provider Server
• Documentum Server
keystorePass="password"
sslProtocol="TLS"/>
• <RSA-SHA1>
• <RSA-SHA256>
• <RSA-SHA512>
One of the above supported algorithms must be specified when this runtime
property is not commented out. The
<rest.security.saml2.request.signature.algorithm> property defaults to <RSA-
SHA256>.
rest.security.saml2.ks.file==path-of-the-saml-certificate-file.jks
#specify the alias of key entry used by the SAML Service Provider to sign the SAML
message
rest.security.saml2.ks.entry.alias=alias
#specify the password of the key entry used by the SAML Service Provider to sign
the
SAML message
rest.security.saml2.ks.entry.password=password
#specify the HTTP method used to send SAML request to the Identity Provider
rest.security.saml2.request.binding==HTTP-Redirect
#specify the attributes used to extract principal names from the SAML Response
rest.security.saml2.user.attributes==http://schemas.microsoft.com/ws/2008/06/
identity/claims/windowsaccountname
The metadata file of the Documentum REST Services server can also be used to
setup the Identity Provider trust.
Tip: You can use the same certificate to simplify the configuration of the
service communication and token-signing. However, you can use two different
certificates for these items.
This security requirement can be fulfilled using a WAM solution, which protects
networks, applications, and data while simultaneously providing quick and efficient
access to an increasing number of authorized users.
5.9.1 Workflow
Below is a diagram that shows the workflow of a pre-authenticated login. The
following are constraints for this authentication extension:
1. The User Agent cannot communicate with the REST server. Instead it only
communicates with the Front Service
2. The Front Service authenticates the User Agent using a 3rd Party Authentication
Service. The Front Service does not authenticate the User Agent with the REST
server or the Documentum Server.
3. The Front Service communicates with the REST server according to a predefined
schema, such as http Headers or a cookie.
4. The REST server extracts the principal name based on a predefined pattern, and
uses the principal name to attempt a principal login to Documentum Server
# preauth
# - Support pre-authentication without Client Token cookie supported
# ct-preauth
# - Support pre-authentication with Client Token cookie supported
# The default mode is basic.
# For more information, see the OpenText Documentum REST Services Reference Guide.
#
rest.security.auth.mode=
##########################################################################
## Security Configuration for Pre-authenticated Login ##
##########################################################################
#
# This section contains the security configuration for Pre-authenticated Login to parse
# the principal from the Request by the front service. Configuration in this section
work
# only when preauth scheme is set in the General Security Configuration section.
# More information is found in this guide, including details on the configuration steps.
#
#
# Specifies the names of headers which contain the principal name sent by the front
service.
# The value must be a set of comma separated header names without spaces. The value is
case
# sensitive. The REST server iterates through the headers of the Request and retrieves
the
# first one that is contained in the set of cookie names defined here. For example,
assuming
# [FrontServiceToken1,FrontServiceToken2] is specified here. The HTTP Request has header
# "FrontServiceToken1=principal_name". Therefore "principal_name" is extracted to attempt
# the DFC principal login. When the value is not specified, the REST server won't
extract the
# principal name from the headers. With regards to extracting the principal name, the
priority
# of headers is higher than the priority of settings within cookies.
rest.security.preauth.header.names=
# Specifies the names of cookies which contain the principal name sent by the front
service.
# The value must be comma separated cookie names without spaces. The value is case
sensitive.
# The REST server iterates through the cookies of the Request and retrieves the first one
# that is contained in the set of cookie names defined here. For example,
# [FrontServiceToken1,FrontServiceToken2] is specified here. HTTP request has cookie
# "FrontServiceToken2=principal_name". The "principal_name" is extracted to attempt DFC
# principal login. When the value is not specified, the REST server won't extract
principal
# name from cookie. With regards to extracting the principal name, the priority
# of headers is higher than the priority of cookies.
#
rest.security.preauth.cookie.names=
# Specified the regular expression patterns to extract principal name from the
distinguished
# name by the front service. The value must be dual-pound (##) separated regular
expressions
# without spaces. The value is case sensitive.
# For example, "uid=(.*)?,ou=ecd,dc=emc,dc=com##(.*)" is specified here. If distinguished
# name by the front service is "uid=joe,ou=ecd,dc=emc,dc=com", first regular expression
will
# work and "joe" is parsed out. When the distinguished name by the front service is
"joe", the
# second regular expression works and "joe" is extracted.
# By default, the regular expression is "(.*)" to match the whole string.
#
rest.security.preauth.principal.patterns=
• HTTP Basic with client tokens and Kerberos with client tokens (basic-dual_ct-
kerberos)
When communicating with a REST server that has multiple authentication schemes
configured, a REST client relies on the WWW-Authenticate Header in the server’s
Response to discover the authentication schemes the server offers.
• When the client tries to access a resource with multiple matching Authorization
Headers, the REST server authenticates the client by using the matching scheme
with the highest precedence. The following list shows the authentication schemes
in order of precedence, with the highest-precedence one at the top.
– Client token
– Kerberos or CAS
If the matching authentication scheme with the highest precedence fails, the
REST server returns an authentication failure Response
// request
GET /${resource-url} HTTP/1.1
// Response
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Basic <MY_REALM>
WWW-Authenticate: Negotiate
{
"status":401,
"code":"E_GENERAL_AUTHENTICATION_ERROR",
"message":"Authentication failed.",
"details":"Full authentication is required to access this resource"
}
// Response
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Basic <MY_REALM>
WWW-Authenticate: Negotiate
{
"status":401,
"code":"E_GENERAL_AUTHENTICATION_ERROR",
"message":"Authentication failed.",
"details":"Full authentication is required to access this resource"
}
// Response
HTTP/1.1 200 OK
// resource body
// Response
HTTP/1.1 200 OK
// resource body
// Response
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Basic <MY_REALM>
{
"status":401,
"code":"E_BAD_CREDENTIALS_ERROR",
"message":"Authentication failed because an invalid credential is provided.",
"details":"(DM_SESSION_E_AUTH_FAIL) error: \
"Authentication failed for user badboy with docbase space01.\""
}
// Response
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Negotiate
{
"status":401,
"code":"E_GENERAL_AUTHENTICATION_ERROR",
"message":"Authentication failed.",
"details":"The given token is a NTLM token, not Kerberos token."
}
// Response
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Basic <MY_REALM>
WWW-Authenticate: CAS <MY_REALM>
{
"status":401,
"code":"E_GENERAL_AUTHENTICATION_ERROR",
"message":"Authentication failed.",
"details":"Full authentication is required to access this resource"
}
// Response
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Basic <MY_REALM>
WWW-Authenticate: Negotiate
{
"status":401,
"code":"E_GENERAL_AUTHENTICATION_ERROR",
"message":"Authentication failed.",
"details":"Full authentication is required to access this resource"
}
// Response
HTTP/1.1 200 OK
// resource body
// Response
HTTP/1.1 200 OK
// resource body
// Response
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Basic <MY_REALM>
{
"status":401,
"code":"E_BAD_CREDENTIALS_ERROR",
"message":"Authentication failed because an invalid credential is provided.",
"details":"(DM_SESSION_E_AUTH_FAIL) error: \"Authentication failed for user
badboy with docbase space01.\""
}
Example 5-20: Matching CAS ticket with bad credential (no redirect)
// Request
GET /${resource-url}?ticket=ST-29-HeJcl956dMmTttZhLBPZ++bad++credential HTTP/1.1
DOCUMENUM-NO-CAS-REDIRECT: true
// Response
HTTP/1.1 401 Unauthorized
WWW-Authenticate: CAS <MY_REALM>
{
"status":401,
"code":"E_GENERAL_AUTHENTICATION_ERROR",
"message":"Authentication failed.",
"details":"ticket 'ST-29-HeJcl956dMmTttZhLBPZ++bad++credential' not recognized."
}
// Response
HTTP/1.1 302 Moved
Location: https://cas-server/cas/login
rest.security.sso.fallback.auth.mode=
You can use this property to enable the fallback authentication mode in the event
that the SSO authentication mode fails.
• The proxy server must retain the original the HOST Header when forwarding a
request to the REST server, instead of modifying it.
– If you deploy the proxy server on an IIS server, you must use the URL
Rewrite module to rewrite the URLs the REST server generates to the original
HOST value.
For more information about how to set this Header on other application servers,
refer to the documentation of the application server.
• If the communication between the client and proxy server and that between the
proxy server and rest server uses different protocols (for example, the client uses
HTTPS to communicate with the proxy server while the proxy server uses HTTP
to communicate with the REST server), unify the communication protocol by
using the X-Forwarded-Proto Header on the proxy server.
On Nginx, use the following directive:
proxy_set_Header X-Forwarded-Proto <protocal>
For example, the following directive forces the proxy server to use HTTPS to
communicate with the REST server:
proxy_set_Header X-Forwarded-Proto https
For more information about how to set this Header on other application servers,
refer to the documentation of the application server.
• In most cases, the port is included in the HOST Header. In this case, you do not
need to specify the port on the proxy server. Otherwise, unify the port by using
the X-Forwarded-Port Header on the proxy server if the proxy server and the
REST server use different ports.
On Nginx, use the following directive:
proxy_set_Header X-Forwarded-Port <Port>
For more information about how to set this Header on other application servers,
refer to the documentation of the application server.
• Performance may degrade when many clients send requests concurrently. To
improve concurrent access performance, we recommend that you modify the
multi-processing module of the proxy server according to the related
documentation. For example, if you use an Apache-based proxy server on a
Windows machine, you may need to increase the value of ThreadsPerChild and
MaxRequestsPerChild, or even remove the limitation on the number of requests
that an individual child server handles by setting MaxRequestsPerChild to 0
The following sample shows how to improve concurrent access performance for an
Apache-based proxy server on a Windows machine.
When Client Token is enabled, the REST server generates a Client Token cookie
after a successful authentication, and sends it back to the REST client in the
Response. The purpose of this client token is to provide an authenticated REST client
with a temporary token, that expires, and can be used to access the REST server
without having to validate user credentials, or negotiate SSO tokens each and every
time. The Client Token cookie has no session state stored on the REST server.
Therefore, it can work in clustered environments.
A client token cookie is encrypted and validated by the REST server. The REST client
is not responsible for saving or decrypting the token. A validation failure of the
client token cookie leads to an authentication failure. In this case, the Basic,
Kerberos, or CAS token negotiation must be repeated.
In the current release, the Client Token can be used with the following existing
authentication schemes:
For more information on how to develop your own authentication scheme and
integrate it with the Client Token, see “Authentication extensibility“ on page 385.
Note: On Chrome 80 and later, the default behavior for sending cookies in the
first- and third-party contexts, has changed as follows:
This also means that cross-site or third-party cookies are restricted to secure or
HTTPS connections only.
The possible values are Strict, Lax, and None. The default value is blank, which
means that the browser will determine the third-party access behavior.
rest.security.client.token.cookie.samesite=
1. A REST client sends a resource request with no credentials to the REST server.
GET /${resource-url} HTTP/1.1
2. The REST server responds with the 401 status error and a Negotiate Header.
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Negotiate
3. The REST client negotiates a SPNEGO-based Kerberos token with the Kerberos
domain controller and re-sends the resource request.
GET /${resource-url} HTTP/1.1
Authorization: Negotiate YIIZG1hZG1pbjpwYXNzd29yZ.....
4. The REST server returns the requested resource and a client token cookie for
reuse, identified by the cookie name DOCUMENTUM-CLIENT-TOKEN.
HTTP/1.1 200 OK
Set-Cookie: DOCUMENTUM-CLIENT-TOKEN="H5VaJz8Vn..."
5. The REST client sends a subsequent resource request with the Client Token
cookie received in Step 4.
GET /${other-resource-url} HTTP/1.1
Cookies: DOCUMENTUM-CLIENT-TOKEN="H5VaJz8Vn..."
Documentum REST Services supports the following expiration policies for the client
token cookie:
Policy Description
com.emc.documentum.rest.security.ticket.im The client token expires after a specified
pl.HardTimeoutExpirationPolicy duration.
By convention, only the POST HTTP method is typically supported for signing out of
most web applications. Allowing for the integration with other authentication
schemes, like SAML, by using GET to communicate with REST Services for the
process of logout. Another runtime property is used to specify which HTTP
methods are supported for sing out. When the Request method is not supported, the
status code 405 is returned with an error message in the Response. The runtime
property below allows you to customize logout HTTP method.
#Specifies supported HTTP methods for sign out. Multiple values separated by comma,
# are supported as below.
#Examples:
#rest.security.logout.supported.methods=GET,POST
#Note: the HTTP methods specified in String format are not case sensitive,
# meaning 'get,POST', 'GET,post' or 'Get,Post' all make sense.
#when not specified, the default values GET,POST would be applied.
rest.security.logout.supported.methods=
Notes
• The default cryptographic algorithms that are used for client token
encryption and decryption are strong enough in most cases. Therefore, we
recommend that you keep the original settings unless you have special
business requirements
• When you use a Crypto provider other than JsafeJCE (RSA provider) or BC
(Bouncy Castle provider), you must set the rest.security.crypto.
provider.class parameter after modifying the rest.security.crypto.
provider
• When you deploy Documentum REST Services on IBM WebSphere and use
one of the following combinations of authentication schemes:
– rest.security.crypto.provider=IBMJCE
– rest.security.crypto.provider.class=com.ibm.crypto.provider.IBMJCE
– rest.security.random.algorithm=IBMSecureRandom
• For a multi-node deployment of REST servers, you must set the rest.
security.crypto.key.salt parameter consistently across all REST servers
• Some web applications may contain security providers that share the same
names with the security providers in Documentum REST Services. The
rest.security.crypto.provider.force.replace property determines
whether to replace a security provider in web applications with the one in
Documentum REST Services when the two providers share the same name.
The default value is false, meaning that Documentum REST Services uses
the security provider registered in the web application. The default setting is
recommended.
• Documentum REST Services client token encryption is supported by the Java
7 cryptographic cipher.
• Documentum REST Services also supports third-party encryption providers
such as RSA BSAFE, Crypto-J, and Bouncy Castle.
This behavior may trigger some web browsers to display a login dialog box prompt
for re-authentication. However, if you design your own login screen, it could be
blocked by the one the web browser prompts. To bypass a login dialog box, the
Documentum REST Services server can append a suffix to the authentication scheme
in the WWW-Authenticate Header. This makes web browsers not recognize the
scheme and so that no login dialog boxes are prompted.
To enable this feature, client applications must set the custom Header DOCUMENTUM-
CUSTOM-UNAUTH-SCHEME to true in requests. Optionally, on the REST server, you
can customize the WWW-Authenticate Response Header for HTTP status 401 with
the rest.api.unauthorized.Response.scheme.suffix in the rest-api-
runtime.properties runtime property setting.
6.1 Overview
Docker is an open source containerization platform that's used for developing,
deploying, testing, and running your applications. Packaging your application along
with all of its dependencies in a Docker Image is known as Dockerizing your
application. Since the Docker image contains all of your application's dependencies,
Dockerizing your applications allows you to separate your applications from their
dependencies on hardware or software, which allows you to treat your machine
environment like a managed application. Docker helps simplify the process of
deploying code, running or testing code, and shipping code.
All REST services are stateless, which makes them ideal candidates for Docker
Containers. Using Docker Containers allows you to safely develop and test your
REST services application in a predictable and stable way.
In addition to the above advantages, using the Docker-Compose and Swarm tools
make it easier to set up application clusters, and deploy other sophisticated use
scenarios. When your environment is ready, it can be easily scaled, migrated, and
shipped. Docker employs native security, simple sharing capabilities, integration
with clouds services, such as AWS or Azure, and it supports many orchestration
frameworks too.
Your REST web service application, along with all of its dependencies, can be
packaged into a single deployable Docker Image. The same Docker Container that
uses this image can be run on various operating systems and environments, and
To Dockerize your web application, you must include all of its dependencies when
you create the Docker image. The following table shows the system requirements for
the Docker image.
Note: The entrypoint.sh launch script only manipulates the REST related
configuration files.
Below is an example of a Dockerfile. As you can see, this file creates the image
with the JRE, Tomcat, REST, volume and port definitions, and then it defines the
script to start the REST services. Pay particular attention to the following
configurable environment variables: TOMCAT_MAJOR, TOMCAT_VERSION, and DCTM_
REST_URL.
#!/bin/bash
# Improve tomcat startup performance, http://openjdk.java.net/jeps/123
java_security=$(find /usr/lib/jvm | grep jre | grep java.security$)
if grep -q securerandom.source=file:/dev/random ${java_security}; then
sed -i -e "s/securerandom.source=file:\/dev\/random/securerandom
.source=file:\/dev\/\\.\/urandom/" ${java_security}
elif grep -q securerandom.source=file:/dev/urandom ${java_security}; then
sed -i -e "s/securerandom.source=file:\/dev\/urandom/securerandom
.source=file:\/dev\/\\.\/urandom/" ${java_security}
fi
fi
if [ -n "${COMM_KEY_STORE_TYPE}" ]; then
echo "Setting Tomcat SSL/TLS connector: keystore type..."
sed -i '/$lt;!--$/{N;/Connector port=\"8443\"/{N;N;N;s|\(.*$lt;!--\n\)\(.*\)\(\/>
\n.*-->\)
|\1\2 keystoreType="'"$COMM_KEY_STORE_TYPE"'" \3|}}' ${CATALINA_HOME}/conf/server
else
echo "Communication keystore type is not specified..."
fi
# Start Tomcat
${CATALINA_HOME}/bin/catalina.sh run
• Dockerfile
• entrypoint.sh
1. Create a build folder and move the building files into this folder.
mkdir ~/building
cd ~/building
cp <...>/Dockerfile ./
cp <...>/entrypoint.sh ./
2. The building process automatically retrieves the binaries for Tomcat, JRE, and
REST. You can configure these variables:
Caution
It is possible to apply configuration setting by using container environment
variables, however this is not recommended for Documentum REST
Services.
A host folder can be mounted to a specific container folder so that the REST log files
can be saved to the host, regardless of whether the container has been stopped or
removed. The log4j.properties file directs the log file to this folder, in the above
image you can see its path as /root/rest/logs.
To enable support for SSL, a keystore file can be mounted from the host to the
container. Several environment variables define the keystore location, its type,
password, and the certificate name / password key value pair. The launch script
applies these variables to the Tomcat server for SSL configuration.
Command Description
--name rest The container name, which is rest
-p 8080:8080 Maps the host port 8080 to the container port 8080
-d Run the container as daemon
-v `pwd`/config:/root/ Map the host folder `pwd`/config to the container
rest/config folder /root/rest/config
-v `pwd`/logs:/root/rest/ Map the host folder `pwd`/logs to the container
logs folder /root/rest/logs so that the REST log files
are saved on the host
"href":"http://localhost:8080/dctm-rest/repositories/REPO/users"},
{"rel":"http://identifiers.emc.com/linkrel/groups",
"href":"http://localhost:8080/dctm-rest/repositories/REPO/groups"},
{"rel":"http://identifiers.emc.com/linkrel/formats",
"href":"http://localhost:8080/dctm-rest/repositories/REPO/formats"},
{"rel":"http://identifiers.emc.com/linkrel/network-locations",
"href":"http://localhost:8080/dctm-rest/repositories/REPO/network-locations"},
{"rel":"http://identifiers.emc.com/linkrel/relations",
"href":"http://localhost:8080/dctm-rest/repositories/REPO/relations"},
{"rel":"http://identifiers.emc.com/linkrel/relation-types",
"href":"http://localhost:8080/dctm-rest/repositories/REPO/relation-types"},
{"rel":"http://identifiers.emc.com/linkrel/types",
"href":"http://localhost:8080/dctm-rest/repositories/REPO/types"},
{"rel":"http://identifiers.emc.com/linkrel/aspect-types",
"href":"http://localhost:8080/dctm-rest/repositories/REPO/aspect-types"},
{"rel":"http://identifiers.emc.com/linkrel/dql",
"hreftemplate":"http://localhost:8080/dctm-rest/repositories/REPO{?dql}"},
{"rel":"http://identifiers.emc.com/linkrel/search",
"hreftemplate":"http://localhost:8080/dctm-rest/repositories/REPO/search{?
collections,
facet,include-total,inline,items-per-page,locations,object-type,page,q,sort,
timezone,view}"},
{"rel":"http://identifiers.emc.com/linkrel/saved-searches",
"href":"http://localhost:8080/dctm-rest/repositories/REPO/saved-searches"},
{"rel":"http://identifiers.emc.com/linkrel/search-templates",
"href":"http://localhost:8080/dctm-rest/repositories/REPO/search-templates"},
{"rel":"http://identifiers.emc.com/linkrel/acls",
"href":"http://localhost:8080/dctm-rest/repositories/REPO/acls"},
{"rel":"http://identifiers.emc.com/linkrel/batches",
"href":"http://localhost:8080/dctm-rest/repositories/REPO/batches"},
{"rel":"http://identifiers.emc.com/linkrel/batch-capabilities",
"href":"http://localhost:8080/dctm-rest/repositories/REPO/batch-capabilities"}]
}
5. Modify the configuration file dfc.properties to use CS2. You can modify the
file in the container or the host, either will work.
// Modify the file in container
docker exec -it rest vi/root/rest/config/dfc.properties
OR
curl http://dmadmin:password@localhost:8080/dctm-rest/repositories/REPO
{
"id":5,"name":"REPO","description":"","servers":
[{"name":"REPO","host":"RESTCS72GA",
"version":"7.2.0000.0155 Win64.SQLServer","docbroker":"RESTCS72GA"}],
"links":[{"rel":"self","href":"http://localhost:8080/dctm-rest/repositories/
REPO"},
{"rel":"http://identifiers.emc.com/linkrel/cabinets",
"href":"http://localhost:8080/dctm-rest/repositories/REPO/cabinets"},
{"rel":"http://identifiers.emc.com/linkrel/checked-out-objects",
"href":"http://localhost:8080/dctm-rest/repositories/REPO/checked-out-objects"},
{"rel":"http://identifiers.emc.com/linkrel/current-user",
"href":"http://localhost:8080/dctm-rest/repositories/REPO/currentuser"},
{"rel":"http://identifiers.emc.com/linkrel/current-user-preferences",
"href":"http://localhost:8080/dctm-rest/repositories/REPO/currentuser-
preferences"},
{"rel":"http://identifiers.emc.com/linkrel/users",
"href":"http://localhost:8080/dctm-rest/repositories/REPO/users"},
{"rel":"http://identifiers.emc.com/linkrel/groups",
"href":"http://localhost:8080/dctm-rest/repositories/REPO/groups"},
{"rel":"http://identifiers.emc.com/linkrel/formats",
"href":"http://localhost:8080/dctm-rest/repositories/REPO/formats"},
{"rel":"http://identifiers.emc.com/linkrel/network-locations",
"href":"http://localhost:8080/dctm-rest/repositories/REPO/network-locations"},
{"rel":"http://identifiers.emc.com/linkrel/relations",
"href":"http://localhost:8080/dctm-rest/repositories/REPO/relations"},
{"rel":"http://identifiers.emc.com/linkrel/relation-types",
"href":"http://localhost:8080/dctm-rest/repositories/REPO/relation-types"},
{"rel":"http://identifiers.emc.com/linkrel/types",
"href":"http://localhost:8080/dctm-rest/repositories/REPO/types"},
{"rel":"http://identifiers.emc.com/linkrel/aspect-types",
"href":"http://localhost:8080/dctm-rest/repositories/REPO/aspect-types"},
{"rel":"http://identifiers.emc.com/linkrel/dql",
"hreftemplate":"http://localhost:8080/dctm-rest/repositories/REPO{?dql}"},
{"rel":"http://identifiers.emc.com/linkrel/search",
"hreftemplate":"http://localhost:8080/dctm-rest/repositories/REPO/search{?
collections,
facet,include-total,inline,items-per-page,locations,object-type,page,q,sort,
timezone,view}"},
{"rel":"http://identifiers.emc.com/linkrel/saved-searches",
"href":"http://localhost:8080/dctm-rest/repositories/REPO/saved-searches"},
{"rel":"http://identifiers.emc.com/linkrel/search-templates",
"href":"http://localhost:8080/dctm-rest/repositories/REPO/search-templates"},
{"rel":"http://identifiers.emc.com/linkrel/acls",
"href":"http://localhost:8080/dctm-rest/repositories/REPO/acls"},
{"rel":"http://identifiers.emc.com/linkrel/batches",
"href":"http://localhost:8080/dctm-rest/repositories/REPO/batches"},
{"rel":"http://identifiers.emc.com/linkrel/batch-capabilities",
"href":"http://localhost:8080/dctm-rest/repositories/REPO/batch-capabilities"}]
}
Note: All the configuration files and directories in the host folder ~/rest/
config/ are copied to the container folder <TOMCAT_ROOT>/webapps/dctm-
rest/WEB-INF/classes/ when the container is started. This means that users
have full control over the REST configuration.
The following table displays the attributes that are configurable in the server.xml
file of the Tomcat 8 server.
curl -k https://dmadmin:password@localhost:8443/dctm-rest/repositories/REPO
{"id":5,"name":"REPO","description":"","servers":[{"name":"REPO","host":"RESTCS72GA",
"version":"7.2.0000.0155 Win64.SQLServer","docbroker":"RESTCS72GA"}],
"links":[{"rel":"self","href":"https://localhost:8443/dctm-rest/repositories/REPO"},
{"rel":"http://identifiers.emc.com/linkrel/cabinets",
"href":"https://localhost:8443/dctm-rest/repositories/REPO/cabinets"},
{"rel":"http://identifiers.emc.com/linkrel/checked-out-objects",
"href":"https://localhost:8443/dctm-rest/repositories/REPO/checked-out-objects"},
{"rel":"http://identifiers.emc.com/linkrel/current-user",
"href":"https://localhost:8443/dctm-rest/repositories/REPO/currentuser"},
{"rel":"http://identifiers.emc.com/linkrel/current-user-preferences",
"href":"https://localhost:8443/dctm-rest/repositories/REPO/currentuser-preferences"},
{"rel":"http://identifiers.emc.com/linkrel/users",
"href":"https://localhost:8443/dctm-rest/repositories/REPO/users"},
{"rel":"http://identifiers.emc.com/linkrel/groups",
"href":"https://localhost:8443/dctm-rest/repositories/REPO/groups"},
{"rel":"http://identifiers.emc.com/linkrel/formats",
"href":"https://localhost:8443/dctm-rest/repositories/REPO/formats"},
{"rel":"http://identifiers.emc.com/linkrel/network-locations",
"href":"https://localhost:8443/dctm-rest/repositories/REPO/network-locations"},
{"rel":"http://identifiers.emc.com/linkrel/relations",
"href":"https://localhost:8443/dctm-rest/repositories/REPO/relations"},
{"rel":"http://identifiers.emc.com/linkrel/relation-types",
"href":"https://localhost:8443/dctm-rest/repositories/REPO/relation-types"},
{"rel":"http://identifiers.emc.com/linkrel/types",
"href":"https://localhost:8443/dctm-rest/repositories/REPO/types"},
{"rel":"http://identifiers.emc.com/linkrel/aspect-types",
"href":"https://localhost:8443/dctm-rest/repositories/REPO/aspect-types"},
{"rel":"http://identifiers.emc.com/linkrel/dql",
"hreftemplate":"https://localhost:8443/dctm-rest/repositories/REPO{?dql}"},
{"rel":"http://identifiers.emc.com/linkrel/search",
"hreftemplate":"https://localhost:8443/dctm-rest/repositories/REPO/search{?
collections,
facet,include-total,inline,items-per-page,locations,object-type,page,q,sort,
timezone,view}"},
{"rel":"http://identifiers.emc.com/linkrel/saved-searches",
"href":"https://localhost:8443/dctm-rest/repositories/REPO/saved-searches"},
{"rel":"http://identifiers.emc.com/linkrel/search-templates",
"href":"https://localhost:8443/dctm-rest/repositories/REPO/search-templates"},
{"rel":"http://identifiers.emc.com/linkrel/acls",
"href":"https://localhost:8443/dctm-rest/repositories/REPO/acls"},
{"rel":"http://identifiers.emc.com/linkrel/batches",
"href":"https://localhost:8443/dctm-rest/repositories/REPO/batches"},
{"rel":"http://identifiers.emc.com/linkrel/batch-capabilities",
"href":"https://localhost:8443/dctm-rest/repositories/REPO/batch-capabilities"}]
}
6.4.2 Logging
You can check the console logs by using this Docker command:
docker logs rest
You can save your REST log files in the host in two ways:
• By default, the log files are saved in the container folder located in /root/rest/
logs.
Run the docker inspect rest docker command to see which host folder is
mounted and mapped to the container folder. Here's an example of the path for
the host folder:/var/lib/docker/volumes/
2bf0e2bfc08bde54d187bc74b43fd4466ff94c3c3de81152250ecb25ce2bdf56/
_data. The log file rest-api.log is in this host folder.
"Mounts": [
{
"Source": "/home/<your-username>/rest/all-in-one/config",
"Destination": "/root/rest/config",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
},
{
"Name": "2bf0e2bfc08bde54d187bc74b43fd4466ff94c3c3de81152250ecb25ce2bdf56",
"Source": "/var/lib/docker/volumes/
2bf0e2bfc08bde54d187bc74b43fd4466ff94c3c3de81152250ecb25ce2bdf56/
_data",
"Destination": "/root/rest/logs",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
• The second option is to run the REST container with the volume parameter.
Using the -v 'pwd'/logs/root/rest/logs host folder 'pwd'/logs maps to the
container folder /root/rest/logs.
Example 6-3: Running the REST Container with the Volume Parameter
When the container is started by the command shown below, the logging
file is in the ~/rest/logs host folder.
docker run --name rest -p 8080:8080 -d \
-v `pwd`/config:/root/rest/config \
-v `pwd`/logs:/root/rest/logs \
dctm-rest
The figure below describe the topology of the Documentum REST Services in swarm
mode. The Load Balancer, shown below in green, is a native Docker functionality
that routes Requests from the Client to various swarm nodes. Each swarm node has
many running tasks, and each task can serve as specific job, such as Documentum
REST Services.
Once a REST Request arrives at the Load Balancer, it can be routed to any node on
which Documentum REST Services are running. When there are multiple tasks
serving Documentum REST Services on the selected node, one of the tasks is
automatically selected to handle the incoming Request.
• Setup a swarm. You must create a swarm and add nodes to it. For more
information, see Docker documentation.
• Deploy Documentum REST Services. You must use configuration files to run your
REST services task. However, in a clustered environment the local configuration
files that are in one swarm node are not available to other swarm nodes. One
technique that allows you to use your configuration files across swarm nodes is
to include the configuration files in the REST image.
Follow these steps to include your configuration files in the REST image:
1. Create the REST node image. Prepare the Dockerfile used to create the REST
node image.
The Dockerfile sample below copies the configuration files (such as dfc.
properties or log4j.properties) into the config building context folder
that is in the REST node image.
FROM <REST_BASE_IMAGE>
#config directory containing the necessary configuration files
COPY config/ ${CONFIG_DIR}/
2. Build the REST node image. Use the following command to build the REST
node image:
docker build -t <REST_NODE_IMAGE>
Note: After the REST node image is ready, we recommend that you
push it to a registry so that other swarm nodes can retrieve and use it.
3. Start the Documentum REST Services as a Docker swarm service. The --replicas
argument specifies two instances of the Documentum REST Services that will
be set to run in the swarm.
The two instances may be on the same or different swarm nodes:
docker service create --replicas 2 --name rest-multinodes -p 8080:8080
<REST_NODE_IMAGE>
• Scale Documentum REST Services. An existing Docker service can be scaled using
the command shown in the following sample. In this case, Docker launches
another 3 instances in the swarm.
docker service scale rest-multinodes=5
No matter which containers or swarm nodes are broken down, the swarm engine
continues to maintain the server availability. It does by launching substitution tasks
for broken containers or by migrating tasks from each broken down node to another
living node.
Let's assume that you want to upgrade your REST service from version 7.3 to
version 7.3 P01. Follow these steps:
3. Start 7.3 P01 container with all of the existing configuration files of the 7.3
container
This upgrading process seems similar to the normal upgrading process using a WAR
file. However, all existing troubleshooting steps can also be applied to the
dockerized REST service, which will run exactly the same in different environments
regardless of machine infrastructure.
Resource extensibility
8.1 Overview
Resource extensibility is an API infrastructure that enables you to extend
Documentum REST Services by composing, customizing, and creating new REST
resources. These newly-created resources are finally discoverable from the Home
Document, existing core REST resources, or new custom resources by using
HATEOS relations. By wielding the power of resource extensibility, you can tailor
Documentum REST Services to your needs with limited amount of coding.
• A set of Core REST Java libraries for custom resource development that
enhances the REST services development of the Core REST services. Java
docs and code samples for the shared library are also provided
• A Marshalling Framework to handle XML or JSON message marshalling and
unmarshalling of Core REST resources
• A Maven-based toolkit to manage the build process of custom resource
projects. An archetype is included and you can use it to create a sample
project for your organization
• The
<com.emc.documentum.rest.wire.xml.AnnotatedXmlMessageWriter.setSuggestedNames
pace()> method has been deprecated. Manually suggesting namespaces does not
produce a well-formatted XML representation. Moving forward, the
AnnotatedXmlMessageWriter will automatically examine the XML root type to
determine the default namespace
• The <com.emc.documentum.rest.binding.SerializableField#xmlListItemName> has been
deprecated. It has been replaced with a new annotation called
<com.emc.documentum.rest.binding.SerializableField4XmlList> that defines a Java
List or Array field for custom marshaling and unmarshalling
• The <com.emc.documentum.rest.binding.SerializableField#xmlListUnwrap> has been
deprecated. It has been replace by a new annotation called
<com.emc.documentum.rest.binding.SerializableField4XmlList> that defines a Java
List or Array field for custom marshaling and unmarshalling
• The <com.emc.documentum.rest.binding.SerializableEntry> has been deprecated. This
annotation was used to Marshall to and Unmarshall from a key-value pair. Since
• A core REST Java library and dependencies for REST extensibility development
• Maven pom files that describe the dependencies of the Core REST Java library
The pom files describe the internal and external library dependencies of Core
REST JAR files. You can install these Core REST JAR files into your local Maven
repository as third-party JAR files
• A Maven archetype project for custom resource development
The archetype project can be used as a template project for custom resource
development. This project can be installed in both local and remote repositories
• A script to install the Maven archetype and dependencies into a local Maven
repository
• A script to create a sample project from the Maven archetype
The Maven archetype helps you create a multi-module project that looks like the
following:
System Requirements:
• Java 7 or Java 8 must be installed, and the path location of the Java installation
must be added to the classpath Environment System Variable.
• Maven 3 must be installed and the location of which must be added to the
classpath system variable.
• Windows
dctm-maven-offline-install.bat
• Linux or Mac
bash dctm-maven-offline-install.sh
After the task completes, the core REST archetype is installed to your local Maven
repository. By default, the archetype is under the following directory:
<user_profile>/.m2/repository/com/emc/documentum/rest/extension
After the Maven archetype for Documentum REST Services is installed, a custom
REST project can be created either in an IDE on page 199 or a command-line prompt
on page 200.
The Eclipse IDE has support for Maven archetype projects. In this section, we use
Eclipse to demonstrate how to create a project from the Maven archetype in an IDE.
1. Click File -> New -> Project, select Maven Project, and then click Next. The New
Maven Project wizard appears
2. In the Select project name and location phase, leave Create a simple project
(skip archetype selection) unchecked. Click Next to proceed to the Select an
Archetype phase.
3. In Select an Archetype, click Configure -> Add Local Catalog. The Local
Archetype Catalog box dialog appears
4. In the Catalog File field, click Browse to navigate to your .m2 folder and then
select the archetype-catalog.xml file. In the Description field, enter the
description of the archetype, and then click OK
5. Click OK to go back to Select an Archetype. In the Catalog field, select the
catalog you specified in Step 4, and then select the Include snapshot archetypes
check box. The <documentum-rest-extension-archetype> artifact appears. Select this
artifact and click Next.
6. Enter the information aboutgroup ID, artifact ID, version, and package, and
click Finish. Eclipse starts to build a project from the Maven archetype.
If the working IDE environment is not the Eclipse, you can alternatively set up the
archetype project with command lines. Maven and Java are required to be set into
the system path.
• Windows
dctm-rest-getstarted.bat
• Linux or Mac
bash dctm-rest-getstarted.sh
After the task completes, a new project is created in the directory that is generated.
Out-of-the-box, two custom resources – alias-sets and alias-set are embedded in the
Maven archetype. Follow these steps to build your project, deploy the WAR file to
an application server, and then access the alias-sets resource to verify the project.
4. Access the alias-sets with a GET request to a URL that looks like the following:
http://<localhost:port>/acme-rest/repositories/<repositoryName>/alias-sets
Upon a successful deployment, the operation returns a collection of alias sets in the
repository.
<feed xmlns="http://www.w3.org/2005/Atom"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<id>http://localhost:8080/dctm-rest/repositories/dctm72/alias-sets </id>
<title>Alias Sets</title>
<author>
<name>OpenText Documentum</name>
</author>
<updated>2014-08-20T17:00:36.197+08:00</updated>
<link rel="self"
href="http://localhost:8080/dctm-rest/repositories/dctm72/alias-sets"/>
<entry>
<id>http://localhost:8080/dctm-rest/repositories/dctm72/alias-sets/
6600000b80000105</id>
<title>AdminAccess</title>
<author>
<name>Administrator</name>
<uri>
http://localhost:8080/dctm-rest/repositories/dctm72/users/Administrator
</uri>
</author>
<updated>2014-08-20T17:00:36.197+08:00</updated>
<published>2014-08-20T17:00:36.197+08:00</published>
<content src="http://localhost:8080/dctm-rest/repositories/dctm72/alias-sets/
6600000b80000105"/>
<link rel="self" href="http://localhost:8080/dctm-rest/repositories/dctm72/
alias-sets/6600000b80000105"/>
</entry>
<entry>
<id>
http://localhost:8080/dctm-rest/repositories/dctm72/alias-sets/6600000b8000050a
</id>
<title>auxiliary_alias_set</title>
<author>
<name>dctm72</name>
<uri>
http://localhost:8080/dctm-rest/repositories/dctm72/users/dctm72
</uri>
</author>
<updated>2014-08-20T17:00:36.197+08:00</updated>
<published>2014-08-20T17:00:36.197+08:00</published>
<content src="http://localhost:8080/dctm-rest/repositories/dctm72/alias-sets/
6600000b8000050a"/>
<link rel="self" href="http://localhost:8080/dctm-rest/repositories/dctm72/
alias-sets/6600000b8000050a"/>
</entry>
<entry>...</entry>
...
<entry>...</entry>
</feed>
2. Navigate to the ant-kit folder where the build.xml file is located and then run
the ant all command to create the WAR file.
Enter the application name and version for your web archive when you are
prompted to.
When the command completes, a multi-module project is created together with a
WAR file under the ant-kit/dist directory.
http://host:port/acme-rest/repositories/MyRepository/alias-sets
If both the build creation and WAR file deployment are successful, the operation
returns a collection of alias sets in the repository.
If you want to add more custom models, controllers, and resources, follow the
instructions in ant-kit/readme.txt.
applies to both Core resources and custom resources. In the current release, the
security component is not exposed for customization.
Documentum REST Services leverages Spring Web MVC Framework to build all
REST resources. The Spring Web MVC sets clear separations of roles and makes
implementations of components pluggable. By taking advantages of Spring Web
MVC, Documentum REST Services provides various means of extensibility features,
enabling you to add custom resources or customize Core resources with flexibility
and efficiency. We call this as Documentum REST Services MVC.
On the top of the Documentum REST Services MVC, Documentum REST Services
provides a unique REST annotation framework which helps to Marshall REST Java
resource models into JSON and/or XML representations, and Unmarshall the JSON
and/or XML representations into resource models. When developing custom
resources, you just need to focus on the resource model design. The marshalling and
unmarshalling are done by Documentum REST Services at the framework level.
Under the bottom of the Documentum REST Services MVC, persistence APIs
communicate with Documentum Server repositories. Documentum REST Services
provides a lot of common APIs to manipulate persistent data in the Documentum
Server repositories. Besides, Documentum REST Services provides the hands-on
session APIs that integrate to the Security component for different authentication
schemes and exposes them as the uniform interfaces in the persistence component.
The security filters actually perform the authentication for access to Documentum
Server repositories or third party Single-sign-On entities. When the authentication
procedure is successful, the Persistence layer of the resource implementation
retrieves the authenticated Documentum Foundation Classes (DFC) session
manager. It retrieves the DFC session manager by using the com.emc.documentum.
rest.dfc.RepositorySession and com.emc.documentum.rest.dfc.
RepositorySessionManager APIs. A custom persistence API can extend the com.
emc.documentum.rest.dfc.SessionAwareAbstractManager API to get the
repository session.
The following diagram illustrates the relationship between REST authentication and
the session related APIs:
• Resource controllers that are used in REST MVC call persistence object managers
to perform any persistent object operations.
• Ehcache is used to store the DFC session manager, and the cache key is generated
from the RepositoryContextHolder Java bean
• The model layer is the data holder for a resource where Documentum REST
Services annotations are defined for marshalling and unmarshalling
• The controller layer is the center of the resource implementation where it defines
the Request and Response mappings, as well as calls the persistence to
manipulate the data. The controller implementation mainly uses Spring
annotation @Controller to implement the resource. The controller method
accepts and returns the model instances
• The view layer is a wrapper of the model where representation-related
information, such as links and atom attributes, are resolved. The output of the
view is still the model instance since the model is the only entity for marshalling
and unmarshalling. The view implementation binds to specific models and
controllers
A resource controller can be bound to one or several view definitions. Each view
definition renders a specific model with regard to links and other customizations.
The input of output of a controller method is the model class which is annotated
with Documentum REST Services annotations. Developing Custom Resources
Custom resources may also need to write new persistence APIs or integrate their
existing type-based objects (TBOs) or service-based objects (SBOs) into the REST
implementation. The Custom persistence APIs must be written using the same
pattern as other Core persistence APIs, and they must be loaded with Spring bean
configurations.
Typically, you can develop a new persistence API with following procedure:
Note: For more information on the usage of DFC sessions in the object
manager, see ???.
3. Implement it as a Java bean.
There are two ways to create a Java bean:
You must ensure that the package where you created your custom
configuration class is specified in the rest.context.config.location property
within the rest-api-runtime.properties file. For example, when the
CustomContextConfig class is under the com.acme.context.config,
package, the rest.context.config.location property should be defined in
runtime properties file as shown here:
rest.context.config.location=com.acme.context.config
For more information, see persistence programming for the details of implementing
a persistence API.
8.5.1 Overview
Documentum REST Services provides a simple yet powerful marshalling framework
to facilitate object to object communication. The Documentum REST Services
marshalling framework introduces a set of Java annotations that bind models
directly to your XML or JSON representations. This data binding eliminates the need
to write message converters for custom data models.
By default, core and custom resources use the annotation marshalling framework to
convert XML and JSON messages. However, because Documentum REST Services
also supports Spring message conversions, these messages can be altered by users
who understand how to work with Spring message conversions.
The resource controller returns the annotated class instance directly, leaving all the
message conversion work to Documentum REST Services MVC and the REST
Marshalling Framework.
@Controller
public class MyDaoController extends AbstractController {
@RequestMapping(value = {"/my-dao/{id}"}, method = RequestMethod.GET})
@ResponseBody
public MyDao get(@PathVariable("id") String id, @RequestUri final UriInfo uriInfo) {
MyDao myDao = dfcObjectManager.get(id);
return myDao;
}
...
}
8.5.2 Annotations
This marshalling framework introduces the following annotations:
• @SerializableType
Used on a Java class to serialize an object of the class to a REST structural
representation.
• @SerializableField
Used on a Java class field to serialize the Java object field to a sub element/
property of a REST representation.
• @SerializableField4XmlList
Used on a Java class field to serialize the Java List type field to a sub element/
property of a REST representation.
• @SerializableField4XmlMap
Used on a Java class field to serialize the Java Map type field to a sub element/
property of a REST representation.
8.5.2.1 @SerializableType
The annotation @SerializableType under Java package com.emc.documentum.
rest.binding indicates that a Java object is intended to be serialized to a REST
representation. This annotation provides a number of attributes enabling you to
customize the marshalling and unmarshalling behavior.
Settings of the
annotation
For more
information, see
Example A
on page 216 and
Example B
on page 216.
fieldOrder Specifies the JSON/XML out not set
order of fields in
the REST
representation
for a serialized
object.
For fields
appearing in the
list, but not
serialized, they
are ignored.
For more
information, see
Example C
on page 217.
For more
information, see
Example D
on page 217.
inlineField Indicates that a JSON/XML out not set
non-primitive
type field is
intended to be
moved to an
upper level in
the REST
representation.
For more
information, see
Example E
on page 218.
xmlValueField Specifies a field XML in and out not set
from which the
value will be
taken as the
XML element
value of this
type.
For more
information, see
Example F
on page 218.
For more
information, see
Example G
on page 218.
If
jsonWriteRoo
tAsField is
false and the
annotated type
has a super class
annotated as
well, the JSON
root is
marshaled so
that the type
information
exists in JSON
representation
which is useful
during
unmarshalling.
jsonRootField Specifies the JSON in and out json-root
field where the
JSON root name
is displayed.
xmlNS Specifies the XML out not set
namespace for
XML inherited from
representation. the parent class
For more
information, see
Example H
on page 219.
For more
information, see
Example H
on page 219.
inheritValue Specifies JSON/XML in and out false
whether to reuse
the serializable
name of the
super type.
• true: the
serializable
name of this
type is set to
the
serializable
name of the
super type.O
• nly types that
do not need
to be
unmarshalle
d can have
this attribute
set to true
• false: the
serializable
name of this
type depends
on the value
on page 210
attribute
8.5.2.1.1 Examples
This section lists several examples explaining the detailed usage of the attributes that
are available in the @SerializableType annotation.
In the following example, fields are serialized according to the order specified
in fieldOrder.
In the following example, the inline filed event is moved to an upper level in
the REST representation and only fields in event are serialized.
The unknown elements are ignored during deserialization process. Ignoring the
unknown elements allows the existing elements to be correctly populated. Here's
an example that illustrates this point:
<model>
<fieldA>a</fieldA>
<fieldB>b</fieldB>
<unknownField>x</unknownField>
</model>
<model unknownAttribute="x">
<fieldA>a</fieldA>
<fieldB>b</fieldB>
</model>
{
"fieldA":"a",
"fieldB":"b",
"unknownField":"x"
}
representation with the parent while the attributes belonging to the child are
unknown to the parent. The following example helps to understand this use case:
@SerializableType(value="parent",unknownFieldDeserializers= {})
public abstract class AbstractParent {
private String name;
}
@SerializableType(value="child-a", jsonWriteRootAsField=true,
jsonRootField="parent")
public class ChildA extends AbstractParent {
@SerializableField(xmlAsAttribute=true)
private int age;
}
@SerializableType(value="child-b", jsonWriteRootAsField=true,
jsonRootField="parent")
public class ChildB extends AbstractParent {
private boolean gratuated;
}
@SerializableType(value="group")
public class Group {
List<AbstractParent> children;
}
There are three instances, one parent and two children. Each child has its own
properties. The parent has the empty unknownFieldDeserializers property
defined. The fourth instance has a List of AbstractParent data type. The output
representation may be:
<?xml version='1.0' encoding='UTF-8'?>
<group>
<children>
<child-a age="10">
<name>child a</name>
</child-a>
<child-b>
<graduated>true</graduated>
<name>child b</name>
</child-b>
</children>
</group>
{"children":[
{"parent":"child-a","age":10,"name":"child a"},
{"parent":"child-b","gratuated":true,"name":"child b"}
]}
When deserializing the representation for the Group element, the system tries to
use AbstractParent to parse the child representation. The child contains the
'unknown' elements 'age' and 'graduated' for AbstractParent. With empty
unknownFieldDeserializers, the binding system processes it correctly.
The server throws an exception when you don't set
unknownFieldDeserializers to empty and the default
AnnotatedExceptionalUnknownFieldDeserializer is used.
When you set the AnnotatedIgnoreableUnknownFieldDeserializer,
unknown elements are ignored. The server creates AbstractParent with its own
elements. When AbstractParent is not abstract, the instance can be created.
When it is abstract, which is the case here, the exception is thrown.
The difference between AnnotatedIgnoreableUnknownFieldDeserializer and
empty unknownFieldDeserializersis that
//get/set methods
while(i.hasNext()) {
Attribute attr = (Attribute)i.next();
if(name.equals(attr.getName().getLocalPart())) {
childD.setMyfoo(attr.getValue());
break;
}
}
return null;
}
}
8.5.2.2 @SerializableField
The annotation @SerializableField indicates that a Java object field is intended to
be serialized to a sub element or attribute of a REST representation. This annotation
provides a number of attributes enabling you to customize the marshalling and
unmarshalling behavior.
For more
information. see
Example I
on page 228.
required Specifies XML/JSON out false
whether a field
is required to be
non-null for
marshalling.
A complex type
refers to a
custom type
annotated with
Serializable
Type.
Specifies
whether to write
Serializable
Type.value
instead of
Serializable
Field.value
as the direct
XML child
element for a
complex type.
• true - write
Serializab
leType.
value as the
direct XML
child element
for a complex
type
• false - write
Serializab
leField.
value as the
direct XML
child element
for a complex
type
defaultImpl Specifies the XML/JSON in DEFAULT.class
default
implementation
class of a field
for
unmarshalling
when the field
implements an
interface or
extends an
abstract class.
For more
information, see
Example J
on page 228.
Specifies XML in and out false
Caut whether to
ion unwrap the
Deprec items from a
ated java.lang.
List type field
as the direct
xmlListUnwrap member of the
serialized object.
• When being
unwrapped,
the collection
members of
this field are
moved to an
upper level
to be direct
members of
the serialized
object in
REST
representatio
n
• When not
being
unwrapping,
this field is
marshalled
and
unmarshalle
d as usual
For more
information, see
Example K
on page 229.
For more
information, see
Example L
on page 229.
xmlNS Specifies the XML out not set
namespace for a
certain field in inherited from
XML the parent class
representation. If
the annotated
field is an
instance of a
certain class that
has a different
namespace
specified, the
class namespace
overrides this
setting.
For more
information, see
Example M
on page 230.
For more
information, see
Example M
on page 230.
8.5.2.2.1 Examples
This section lists several examples explaining the detail usage of the attributes in the
@SerializableField annotation.
In the following example, the inEvent list uses the default name item for list
items while the outEvent uses out-event.
8.5.2.3 @SerializableField4XmlList
This annotation provides dedicated support for <java.util.List> in XML. It uses
existing List related attributes from @SerializableField and introduces new
annotations. The @SerializableField4XmlList annotation can only be used with
an XML List.
8.5.2.4 @SerializableField4XmlMap
The @SerializableField4XmlMap annotation has been added to provide dedicated
support for <java.util.List> with XML. This annotation can only be used with an XML
map.
• AtomFeed
• RestError
• Repository
• PersistentObject
8.5.2.6 Deprecations
The following annotations have been deprecated:
• @SerialiableEntry
Java Map support has been added since version 7.3 of Documentum REST
Services. Therefore, @SerializableEntry has become redundant and is marked
as @Deprecated. General key-value support is only available for Map objects.
Caution
Legacy Usage
Extended REST services that use @SerializableEntry,
@SerializableField.xmlListUnwrap and
@SerializableField.xmlListItemName can continue to use them
moving forward. However, enhancements to these attributes will not be
added
The following example illustrates how out-of-box annotated core error objects
are serialized.
The following example illustrates how out-of-box annotated atom feed objects
with scr entries are serialized.
The following example illustrates how out-of-box annotated atom feed objects
with embedded entries are serialized.
Note: The primitive type char is not supported in the marshalling framework.
Instead, the framework supports String to cover all the functionalities that
char provides.
The following diagram illustrates the mapping between Java primitive types and
XML or JSON data types.
Figure 8-6: Java Primitive Types and XML/JSON Data Types Mapping
In JSON representation, the type Number does not distinguish between integer and
float-point values, which means:
• During marshalling, following data types (and their wrapper classes) are
represented in JSON by the Number data type:
– byte
– short
– int
– long
– float
– double
• During unmarshalling, a JSON Number value is converted based on the type
information of the Java class model.
In this process, if a JSON Number value contains a higher precision than what the
Java class model defines, the unmarshalling fails.
For example, if JSON representation contains "score": 9.5, and the model has a
field int score, the unmarshalling fails. In this case, modify the value of score
to an integer, such as 9, or modify the type of score in the model to a type with a
higher precision, such as float.
// Role interface
public interface Role { // interface
String getName();
}
Here's a code sample that shows you how to serialize a complex type:
Caution
Unmarshalling the XML and JSON representations will not succeed
because:
8.5.4.2 Unmarshalling
You must add annotation metadata to the class definition to deserialize the data
class. Adding this data type makes the type writable in XML or JSON.
• For XML
For XML, the serializable fields that have been declared as generic, interface, or
abstract must set @SerializableField#xmlWriteRoot = true. The concrete
data type for the field is written into XML as the XML element name. Therefore,
it can be resolved during the deserialization process.
Caution
To deserialize the generic, interface or abstract field in XML, the data
class cannot have more than one field that contains non-concrete data
types.
@SerializableField(xmlWriteTypeRoot = true)
private T organization;
@SerializableField(xmlWriteTypeRoot = true)
private Role role;
• For JSON
For JSON, the serializable fields that have been declared as generic, interface or
abstract must set @SerializableField#jsonWriteRoot = true. The concrete
data type for the field is written into XML as the XML element name. Therefore,
it can be resolved during the deserialization process.
Here's a code sample that demonstrates how to do this:
// jsonWriteRoot=true
The XML and JSON contain additional type information that allows it be
deserialized. Here's the same code with all the changes:
BusinessObjectGeneric<Institution> origin =
new BusinessObjectGeneric<Institution>(
new Institution(
"knowledge",
new NonGovernmentalOrg(
"wikimedia",
"ubiquitous online encyclopaedia"
)
)
);
<bog>
<institution>
<name>knowledge</name>
<ngo>
<name>wikimedia</name>
<description>ubiquitous online encyclopaedia</description>
</ngo>
</institution>
</bog>
Note: The serializable name institution that is used for the actual data
type <Institution> is written as XML instead of using the generic field name
<organization>.
The serializable name ngo that is used for the actual data type
<NonGovernmentalOrg> is written as XML instead of using the generic field
name <role>.
{
"organization":{
"type":"institution",
"name":"knowledge",
"role":{
"type":"ngo",
"name":"wikimedia",
"description":"ubiquitous online encyclopaedia"
}
}
}
Note: The root information type:institution that is used for the actual
data type <Institution> is written to the JSON object of <organization>.
The root information type: ngo that is used for the actual data type
<NonGovernmentalOrg> is written to the JSON object of <role>.
BusinessObjectGeneric<Institution> origin =
new BusinessObjectGeneric<Institution>(
new Institution(
"funding",
new GovernmentalOrg(
"imf",
8
)
)
);
Note: The serializable name institutionthat is used for the actual data
type <Institution> is written as XML instead of using the generic field name
<organization>.
The serializable name go that is used for the actual data type
<GovernmentalOrg> is written as XML instead of using the generic field
name <role>.
<bog>
<institution>
<name>funding</name>
<go>
<name>wikimedia</name>
<priviledges>8</description>
</go>
</institution>
</bog>
{
"organization":{
"type":"institution",
"name":"funding",
"role":{
"type":"go",
"name":"imf",
"priviledges":8
}
}
}
Note: The root information type:institution that is used for the actual
data type <Institution> is written to the JSON object of <organization>.
The root information type: go that is used for the actual data type
<GovernmentalOrg> is written to the JSON object of <role>.
/*
* Copyright (c) 2016. OpenText Corporation. All Rights Reserved.
*/
package com.emc.documentum.rest.mock;
import org.apache.commons.lang.builder.EqualsBuilder;
import com.emc.documentum.rest.binding.SerializableField;
import com.emc.documentum.rest.binding.SerializableType;
@SerializableType("bog")
public class BusinessObjectGeneric <T> {
@SerializableField(xmlWriteTypeRoot = true)
private T organization;
public BusinessObjectGeneric() {}
public T getOrganization() {
return this.organization;
}
@Override
public boolean equals(Object other) {
return EqualsBuilder.reflectionEquals(this, other);
}
@SerializableType(value = "institution",
jsonWriteRootAsField = true,
jsonRootField = "type")
public static class Institution {
private String name;
@SerializableField(xmlWriteTypeRoot = true)
private Role role;
public Institution() {}
@Override
public boolean equals(Object other) {
return EqualsBuilder.reflectionEquals(this, other);
}
}
@SerializableType(value = "company",
jsonWriteRootAsField = true,
jsonRootField = "type")
public static class Company {
private String name;
@SerializableField(xmlWriteTypeRoot = true)
private Industry industry;
public Company() {}
@Override
public boolean equals(Object other) {
return EqualsBuilder.reflectionEquals(this, other);
}
}
@SerializableType(value = "go",
jsonWriteRootAsField = true,
jsonRootField = "type")
public static class GovernmentalOrg implements Role {
private String name;
private int privilege;
public GovernmentalOrg() {}
@Override
public boolean equals(Object other) {
return EqualsBuilder.reflectionEquals(this, other);
}
}
@SerializableType(value = "ngo",
jsonWriteRootAsField = true,
jsonRootField = "type")
public static class NonGovernmentalOrg implements Role {
private String name;
private String description;
public NonGovernmentalOrg() {}
@Override
public boolean equals(Object other) {
return EqualsBuilder.reflectionEquals(this, other);
}
}
@SerializableType
public static abstract class Industry {
@SerializableField("country")
public String countryCode;
@Override
public boolean equals(Object other) {
return EqualsBuilder.reflectionEquals(this, other);
}
}
@SerializableType(value = "it",
jsonWriteRootAsField = true,
jsonRootField = "type")
public static class InformationTechnology extends Industry {
private String scale;
public InformationTechnology() {}
@Override
public boolean equals(Object other) {
return EqualsBuilder.reflectionEquals(this, other);
}
}
@SerializableType(value = "pharm",
jsonWriteRootAsField = true,
jsonRootField = "type")
public static class Pharmaceuticals extends Industry {
private String field;
public Pharmaceuticals() {
}
@Override
public boolean equals(Object other) {
return EqualsBuilder.reflectionEquals(this, other);
}
}
}
Java Code
@SerializableField
private boolean[] feedCustomizedBooleanArray = {true, false, false};
XML Representation
<feedCustomizedBooleanArray>
<item>true</item>
<item>false</item>
<item>false</item>
</feedCustomizedBooleanArray>
JSON Representation
"feedCustomizedBooleanArray": [
true,
false,
false
],
Moreover, the framework supports the array form of any custom type instances. For
an instance of a custom type, the return of its toString method is used as the value
when being marshaled to XML or JSON.
Java Code
@SerializableField(xmlNS = "http://ns.customization.com/", xmlNSPrefix = "customization")
private Color[] colorArray = {YELLOW, BLUE};
XML Representation
<customization colorArray
xmlns:customization="http://ns.customization.com/">
<customization:item> YELLOW(255,255,0) </customization:item>
<customization:item> BLUE(0,0,255)</customization:item>
</customization:colorArray >
JSON Representation
" colorArray ": [
" YELLOW(255,255,0) ",
" BLUE(0,0,255)",
]
Caution
Runtime check of parameterized data type
8.5.5.4 Exclusions
The following parameterized data types are not supported:
Caution
List item order
Items in a list are marshalled according to their order within the list
Java Code
@SerializableType("boa")
public class BusinessObjectArchive {
private List<String> keywords;
}
BusinessObjectArchive boa = new BusinessObjectArchive();
boa.getKeywords().add("2015");
boa.getKeywords().add("bedrock");
XML Code
<boa>
<keywords>
<item>2015</item>
<item>bedrock</item>
</keywords>
</boa>
Java Code
@SerializableType("boa")
public class BusinessObjectArchive {
private LABEL[] labels;
XML Code
<boa>
<labels>
<item>DEV</item>
<item>SUPPORT</item>
</labels>
</boa>
Java Code
@SerializableType("boa")
public class BusinessObjectArchive {
private List<Role> roles;
@SerializableType(value = "admin-role")
public static class AdminRole implements Role {
private String name;
private int privilege;
}
@SerializableType(value = "consumer-role")
public static class ConsumerRole implements Role {
private String name;
private String description;
}
...
}
BusinessObjectArchive boa = new BusinessObjectArchive();
boa.getRoles().add(new BusinessObjectArchive.AdminRole("admin", 16));
boa.getRoles().add(new BusinessObjectArchive
.ConsumerRole("ios", "iOS mobile device"));
XML Code
<boa>
<roles>
<admin-role>
<name>admin</name>
<privilege>16</privilege>
</admin-role>
<consumer-role>
<name>ios</name>
Java Code
@SerializableType("boa")
public class BusinessObjectArchive {
@SerializableField4XmlList(asPropBag = true, itemName="role")
private List<Role> roles;
@SerializableType(value = "admin-role")
public static class AdminRole implements Role {
private String name;
private int privilege;
}
@SerializableType(value = "consumer-role")
public static class ConsumerRole implements Role {
private String name;
private String description;
}
...
}
BusinessObjectArchive boa = new BusinessObjectArchive();
boa.getRoles().add(new BusinessObjectArchive.AdminRole("admin", 16));
boa.getRoles().add(new BusinessObjectArchive
.ConsumerRole("ios", "iOS mobile device"));
XML Code
<boa>
<roles>
<role>
<name>admin</name>
<privilege>16</privilege>
</role>
<role>
<name>ios</name>
<description>iOS mobile device</description>
</role>
</roles>
</boa>
Java Code
@SerializableType("boa")
public class BusinessObjectArchive {
@SerializableField4XmlList(unwrap = true, itemName="keyword")
private List<String> keywords;
...
}
BusinessObjectArchive boa = new BusinessObjectArchive();
boa.getKeywords().add("2015");
boa.getKeywords().add("bedrock");
XML Code
<boa>
<keyword>2015</keyword>
<keyword>bedrock</keyword>
</boa>
Java Code
@SerializableType("boa")
public class BusinessObjectArchive {
@SerializableField4XmlList(asPropBag = false)
private List<Role> roles;
XML Code
<boa>
<roles>
<am:admin-role xmlns:am="http://acme.org">
<am:name>admin</am:name>
<am:privilege>16</am:privilege>
</am:admin-role>
<d2:consumer-role xmlns:d2="http://d2.org">
<d2:name>ios</d2:name>
<d2:description>iOS mobile device</d2:description>
</d2:consumer-role>
</roles>
</boa>
Java Code
@SerializableType("boa")
public class BusinessObjectArchive {
@SerializableField4XmlList(unwrapped=true, asPropBag = true, itemName="role")
private List<Role> roles;
@SerializableType(value = "admin-role")
public static class AdminRole implements Role {
private String name;
private int privilege;
}
@SerializableType(value = "consumer-role")
public static class ConsumerRole implements Role {
private String name;
private String description;
}
...
}
BusinessObjectArchive boa = new BusinessObjectArchive();
boa.getRoles().add(new BusinessObjectArchive.AdminRole("admin", 16));
boa.getRoles().add(new BusinessObjectArchive
.ConsumerRole("ios", "iOS mobile device"));
XML Code
<boa>
<role>
<name>admin</name>
<privilege>16</privilege>
</role>
<role>
<name>ios</name>
<description>iOS mobile device</description>
</role>
</boa>
Java Code
@SerializableType("boa")
public class BusinessObjectArchive {
private List<String> keywords;
...
}
BusinessObjectArchive boa = new BusinessObjectArchive();
boa.getKeywords().add("2016");
boa.getKeywords().add("Bedrock");
JSON Code
{
"keywords":["2016","Bedrock"]
}
Java Code
@SerializableType("boa")
public class BusinessObjectArchive {
private LABEL[] labels;
JSON Code
{
"labels":["DEV","SUPPORT"]
}
Java Code
@SerializableType("boa")
public class BusinessObjectArchive {
private List<Role> roles;
@SerializableType(value = "admin-role")
public static class AdminRole implements Role {
private String name;
private int privilege;
}
@SerializableType(value = "consumer-role")
public static class ConsumerRole implements Role {
private String name;
private String description;
}
...
}
BusinessObjectArchive boa = new BusinessObjectArchive();
boa.getRoles().add(new BusinessObjectArchive.AdminRole("admin", 16));
boa.getRoles().add(new BusinessObjectArchive
.ConsumerRole("ios", "iOS mobile device"));
JSON Code
{
"roles":[
{"name":"admin","privilege":16},
{"name":"ios","description":"iOS mobile device"}
]
}
Java Code
@SerializableType("boa")
public class BusinessObjectArchive {
private List<Industry> industries;
@SerializableType
public static abstract class Industry {
protected String industry;
}
@SerializableType(value = "healthcare")
public static class Healthcare extends Industry {
@SerializableField("cc")
private int countryCode;
}
@SerializableType(value = "energy")
public static class Energy extends Industry {
private String type;
}
...
}
BusinessObjectArchive boa = new BusinessObjectArchive();
boa.getIndustries().add(new BusinessObjectArchive.Healthcare("hc", 1));
boa.getIndustries().add(new BusinessObjectArchive.Energy("en", "nuclear"));
JSON Code
{
"industries":[
{"cc":1,"industry":"hc"},
{"type":"nuclear","industry":"en"}
]
}
The following data types can only be deserialized using annotation attributes for
XML or JSON:
• Interface
List<IFolder>, List<IUser>
• Abstract class
List<AbstractFolder>, List<AbstractDocument>
To make a List of Interface, Abstract, or Wild card items deserializable, their fields
must be marked as wrapped and non-property bag by setting the
@SerializableField4XmlList.asPropBag and
@SerializableField4XmlList.unwrap annotations.
This code sample shows you how to resolve the preceding limitation so you can
deserialize a complex data type as XML elements:
Java Code
@SerializableType("boa")
public class BusinessObjectArchive {
private List<Role> roles;
@SerializableType(value = "admin-role")
public static class AdminRole implements Role {
private String name;
private int privilege;
}
@SerializableType(value = "consumer-role")
public static class ConsumerRole implements Role {
private String name;
private String description;
}
...
}
BusinessObjectArchive boa = new BusinessObjectArchive();
boa.getRoles().add(new BusinessObjectArchive.AdminRole("admin", 16));
boa.getRoles().add(new BusinessObjectArchive
.ConsumerRole("ios", "iOS mobile device"));
XML Code
<boa>
<roles>
<admin-role>
<name>admin</name>
<privilege>16</privilege>
</admin-role>
<consumer-role>
<name>ios</name>
<description>iOS mobile device</description>
</consumer-role>
</roles>
</boa>
This code sample shows you how to resolve the preceding limitation so you can
deserialize a complex data type as XML elements with namespaces:
XML Code
<boa>
<roles>
<am:admin-role xmlns:am="http://acme.org">
<am:name>admin</am:name>
<am:privilege>16</am:privilege>
</am:admin-role>
<d2:consumer-role xmlns:d2="http://d2.org">
<d2:name>ios</d2:name>
<d2:description>iOS mobile device</d2:description>
</d2:consumer-role>
</roles>
</boa>
• Interfaces
• Abstract classes
To make a List of Interface, Abstract, or Wild card items deserializable, the item
object definition must enable JSON write root. To enable JSON write root, set the
following:
@SerializableType.jsonWriteRootAsField=true
Here's a code sample that shows you how to do that in more detail:
JSON code
{
"roles":[
{"type":"admin-role", "name":"admin","privilege":16},
{"type","consumer-role", "name":"ios","description":"iOS mobile device"}
]
}
Java code
@SerializableType("boa")
public class BusinessObjectArchive {
private List<Role> roles;
@SerializableType(value = "admin-role",
jsonWriteRootAsField = true, jsonRootField = "type")
public static class AdminRole implements Role {
private String name;
@SerializableType(value = "consumer-role",
jsonWriteRootAsField = true, jsonRootField = "type")
public static class ConsumerRole implements Role {
private String name;
private String description;
}
}
BusinessObjectArchive boa = new BusinessObjectArchive();
boa.getRoles().add(new BusinessObjectArchive.AdminRole("admin", 16));
boa.getRoles().add(new BusinessObjectArchive
.ConsumerRole("ios", "iOS mobile device"));
Note: The annotation @SerializableType for field data types have been set as
<jsonWriteRootAsField = true>, <jsonRootField = "type">
JSON objects can be marshalled as JSON key value pairs. For example:
"countryCodes": {
"usa" : "001",
"china" : "086"}
XML objects can also be marshalled using property bags. For example:
<coundtryCodes>
<countryCode>
<country name="usa">001</country>
<country name="china">086</country>
</countryCode>
</countryCodes>
Caution
To be consistent for XML and JSON marshalling, the map key must be a
String data type
8.5.6.1.2 Inclusions
The first argument (key) data type in a Map must have a String data type. The
second argument (value) data type in a Map can be any one of the following data
types:
• Interface
Map<String, IFolder>, Map<String, IUser>
• Abstract class
Map<String, AbstractFolder>, Map<String, AbstractDocument>
Caution
Runtime check of parameterized data type
8.5.6.1.3 Marshalling
Marshalling a Map with XML is more complex than JSON due to the following:
Here are some code samples that show you how to serialize (marshall) with XML:
Java Code
@SerializableType("bom")
public class BusinessObjectMap {
Map<String, String> codes;
...
}
BusinessObjectMap bom = new BusinessObjectMap();
bom.getCodes().put("usa", "001");
bom.getCodes().put("china", "086");
XML Code
<bom>
<codes>
<usa>001</usa>
<china>086</china>
</codes>
</bom>
Java Code
@SerializableType("bom")
public class BusinessObjectMap {
@SerializableField(defaultImpl = TreeMap.class)
Map<String, Industry> industries;
@SerializableType
public static abstract class Industry {
protected String industry;
}
@SerializableType(value = "healthcare")
public static class Healthcare extends Industry {
@SerializableField("cc")
private int countryCode;
}
@SerializableType(value = "energy")
public static class Energy extends Industry {
private String field;
}
...
}
BusinessObjectMap bom = new BusinessObjectMap();
bom.getIndustries().put("s1", new BusinessObjectMap.Healthcare("hc", 1));
bom.getIndustries().put("s2", new BusinessObjectMap.Energy("en", "nuclear"));
XML Code
<bom>
<industries>
<s1>
<healthcare>
<cc>1</cc>
<industry>hc</industry>
</healthcare>
</s1>
<s2>
<energy>
<field>nuclear</field>
<industry>en</industry>
</energy>
</s2>
</industries>
</bom>
Java Code
@SerializableType("bom")
public class BusinessObjectMap {
Map<String, String[]> codes;
...
}
BusinessObjectMap bom = new BusinessObjectMap();
XML Code
<bom>
<codes>
<asia>
<item>086</item>
<item>091</item>
</asia>
<west>
<item>001</item>
<item>044</item>
</west>
</codes>
</bom>
Java Code
@SerializableType("bom")
public class BusinessObjectMap {
Map<String, List<Role>> roles;
@SerializableType(value = "admin-role")
public static class AdminRole implements Role {
private String name;
private int privilege;
}
@SerializableType(value = "consumer-role")
public static class ConsumerRole implements Role {
private String name;
private String description;
}
...
}
BusinessObjectMap bom = new BusinessObjectMap();
bom.getRoles().put("r1", Arrays.asList(
new BusinessObjectMap.AdminRole("admin", 16),
new BusinessObjectMap.ConsumerRole("ios", "iOS mobile device")
));
bom.getRoles().put("r2", Arrays.asList(
new BusinessObjectMap.AdminRole("dbowner", 8),
new BusinessObjectMap.ConsumerRole("android", "Android mobile device")
));
XML Code
<bom>
<roles>
<r1>
<admin-role>
<name>admin</name>
<privilege>16</privilege>
</admin-role>
<consumer-role>
<name>ios</name>
<description>iOS mobile device</description>
</consumer-role>
</r1>
<r2>
<admin-role>
<name>dbowner</name>
<privilege>8</privilege>
</admin-role>
<consumer-role>
<name>android</name>
<description>Android mobile device</description>
</consumer-role>
</r2>
</roles>
</bom>
Java Code
SerializableType("bom")
public class BusinessObjectMap {
@SerializableField4XmlMap(asPropBag = true)
Map<String, String> codes;
...
}
BusinessObjectMap bom = new BusinessObjectMap();
bom.getCodes().put("usa", "001");
bom.getCodes().put("china", "086");
XML Code
<bom>
<codes>
<entry key="usa">001</entry>
<entry key="china">086</entry>
</codes>
</bom>
Java Code
@SerializableType("bom")
public class BusinessObjectMap {
@SerializableField(defaultImpl = TreeMap.class)
@SerializableField4XmlMap(asPropBag = true,
propBagEleName = "industry",
propBagKeyName = "type")
Map<String, Industry> industries;
@SerializableType
public static abstract class Industry {
protected String industry;
}
@SerializableType(value = "healthcare")
public static class Healthcare extends Industry {
@SerializableField("cc")
private int countryCode;
}
@SerializableType(value = "energy")
public static class Energy extends Industry {
XML Code
When marshaling a Map with JSON, all map entries are serialized as key-value pairs
of a JSON object.
Java Code
@SerializableType("bom")
public class BusinessObjectMap {
Map<String, String> codes;
}
BusinessObjectMap bom = new BusinessObjectMap();
bom.getCodes().put("usa", "001");
bom.getCodes().put("china", "086");
JSON Code
{
"codes":{
"usa":"001",
"china":"086"
}
}
@SerializableType("bom")
public class BusinessObjectMap {
Map<String, Industry> industries;
@SerializableType
public static abstract class Industry {
protected String industry;
}
@SerializableType(value = "healthcare")
public static class Healthcare extends Industry {
@SerializableField("cc")
private int countryCode;
}
@SerializableType(value = "energy")
public static class Energy extends Industry {
private String type;
}
...
}
BusinessObjectMap bom = new BusinessObjectMap();
bom.getIndustries().put("s1", new BusinessObjectMap.Healthcare("hc", 1));
bom.getIndustries().put("s2", new BusinessObjectMap.Energy("en", "nuclear"));
JSON Code
{
"industries": {
"s2": {
"type": "nuclear",
"industry": "en"
},
"s1": {
"cc": 1,
"industry": "hc"
}
}
}
JAVA Code
@SerializableType("bom")
public class BusinessObjectMap {
private Map<String, Role> roles;
@SerializableType(value = "admin-role")
public static class AdminRole implements Role {
private String name;
private int privilege;
}
@SerializableType(value = "consumer-role")
public static class ConsumerRole implements Role {
private String name;
private String description;
}
...
}
BusinessObjectMap bom = new BusinessObjectMap();
bom.getRoles().put("r1", Arrays.asList(
new BusinessObjectMap.AdminRole("admin", 16),
new BusinessObjectMap.ConsumerRole("ios", "iOS mobile device")
));
bom.getRoles().put("r2", Arrays.asList(
new BusinessObjectMap.AdminRole("dbowner", 8),
new BusinessObjectMap.ConsumerRole("android", "Android mobile device")
));
{
JSON Code
"roles": {
"r1": [
{
"name": "admin",
"privilege": 16
},
{
"name": "ios",
"description": "iOS mobile device"
}
],
"r2": [
{
"name": "dbowner",
"privilege": 8
},
{
"name": "android",
"description": "Android mobile device"
}
]
}
}
8.5.6.1.4 Unmarshalling
A Map can be deserialized using JSON or XML messages but it has certain
constraints with respect to the class definition.
The following field data types can be deserialized using additional annotation
attributes for XML and JSON, respectively:
• Interface
Map<String, IFolder>, Map<String, IUser>
• Abstract class
Map<String, AbstractFolder>, Map<String, AbstractDocument>
• Generic or wild card data type
Map<String, Object>, Map<String, ?>, Map<String, T>
• Declared List data type with unresolved list item type
Map<String, List<? extends IUser>>
For XML, the Map entry value is always deserializable and there are no constraints.
Messages for the following data types can be deserialized to a Map field:
A List of complex data type cannot be deserialized into JSON fields because the type
information is not determined from either the declared data type such as
Map<String, Role>, or the JSON message such as {name:xx, ..}.
To make a Map of an Interface, Abstract class, and Wild card items deserializable,
the object definition for the item must enable the JSON write root attribute as shown
here:
SerializableType.jsonWriteRootAsField=true
Java
@SerializableType("bom")
public class BusinessObjectMap {
private Map<String, Role> roles;
@SerializableType(value = "admin-role",
jsonWriteRootAsField = true,
jsonRootField = "type")
public static class AdminRole implements Role {
private String name;
private int privilege;
}
@SerializableType(value = "consumer-role",
jsonWriteRootAsField = true,
jsonRootField = "type")
public static class ConsumerRole implements Role {
private String name;
private String description;
}
...
}
BusinessObjectMap bom = new BusinessObjectMap();
bom.getRoles().put("r1", Arrays.asList(
new BusinessObjectMap.AdminRole("admin", 16),
new BusinessObjectMap.ConsumerRole("ios", "iOS mobile device")
));
bom.getRoles().put("r2", Arrays.asList(
new BusinessObjectMap.AdminRole("dbowner", 8),
new BusinessObjectMap.ConsumerRole("android", "Android mobile device")
));
The annotation @SerializableType for the field data types has been set to
jsonWriteRootAsField=true, jsonRootField="type".
JSON
{
"roles": {
"r1": [
{
"type": "admin-role",
"name": "admin",
"privilege": 16
},
{
"type": "consumer-role",
"name": "ios",
"description": "iOS mobile device"
}
],
"r2": [
{
"type": "admin-role",
"name": "dbowner",
"privilege": 8
},
{
"type": "consumer-role",
"name": "android",
"description": "Android mobile device"
}
]
}
}
There are requirements at the field level that cannot be fulfilled by the out-of-box
capability of the framework.
Generally, you can implement a new custom serializer or deserializer in two ways:
Runnable JAR
To use the annotation scanner as a runnable JAR, navigate to tools directory of the
SDK and then run the following command:
java -jar dctm-rest-ext-validator-runnable [TARGET] [option]
Maven Plug-In
1. Navigate to tools directory of the SDK and then run the following command to
install the plug-in to your local repository:
mvn install:install-file -Dfile=documentum-rest-annotation-plugin-<version-
number>.jar -DpomFile=pom
2. Open the pom.xml file of your project and then append the following plug-in to
the plugins block .
<plugin>
<groupId>com.emc.documentum.rest</groupId>
<artifactId>documentum-rest-extension-validating</artifactId>
<version>7.3</version>
<inherited>true</inherited>
<executions>
<execution>
<id>validate-annotation</id>
<phase>verify</phase>
<goals>
<goal>check-annotation</goal>
</goals>
<configuration>
<input>${project.basedir}/target/${scan.artifactId}-${version}.war</
input>
<outputDir>${project.basedir}/target</outputDir>
<debug>false</debug>
<failBuild>true</failBuild>
</configuration>
</execution>
</executions>
</plugin>
When the scan completes, an HTML file named Scan Report of Documentum REST
Services Extensibility Annotation is generated in the output directory. This file
lists all annotation violations and fix recommendations. All issues should be
resolved before you build the custom REST WAR file.
If necessary (though not recommended), you can disable this scanner by removing
the plug-in configuration in the pom.xml file.
Typically, custom resource development can be divided into the following phases:
8.6.1.1 URI
The URI design for a resource is implementation-specific. Theoretically, you can
design any URI pattern for a custom resource. Practically, however, the custom
resource URI pattern should be similar to that of the Core resources. One benefit is
to apply the same authentication schemes as Core resources, because Core REST
security component checks the resource URLs to determine whether authentication
is required on the custom resources. For instance, all repository level resources are
required for authentication by default, so the URI of a custom resource under a
particular repository should follow this pattern: /repositories/
<repositoryName>/<customSegments>.
Note: When the designed resource URI template contains path variables where
their values come from the object properties, there could be URI encoding/
decoding issues if the property values contain URI preserved characters.
Documentum REST Services SDK provides a utility com.emc.documentum.
rest.utils.NameAsPathCoder helping you encode and decode the path
variable values to escape special characters.
Core resources use standard HTTP methods GET, POST, PUT and DELETE in all
places.
8.6.1.3 Representations
Core resources support both JSON and XML representations. For a custom resource,
whether to support one or both representations depends on your business
requirements. Documentum REST Services provides an annotation-based
marshalling framework so that an annotated Java model can support both JSON and
XML representations out of the box. You do not need to handle the message
marshalling or unmarshalling except for the Java model design.
If you want to limit the custom resource to support JSON or XML representation
only, specific media type constraints can be applied to the resource controller to
precisely define the supported representation with the Spring annotation
@RequestMapping.
In both Core and custom resources, all operations support the following media
types:
• application/atom+xml
• application/vnd.emc.documentum+xml
• application/vnd.emc.documentum+json
The GET operation also supports the following two generic media types:
• application/xml
• application/json
The current Documentum REST Services Extensibility feature does not support
custom media types.
It is possible to add new links to Core resources. For more information, see Adding
Links to Core Resources.
• model
Designs annotated Java model classes for custom resources
• persistence
Creates persistence API beans by referencing DFC or other local persistence APIs
• resource
Creates resource controllers for custom resources
With this code structure, the custom model, persistence, and controller
implementations are built as separate JAR files.
For a custom resource that does not require additional models or persistence APIs,
the corresponding module and/or persistence can be omitted.
For each sub module, the code structure is organized as a typical Maven module.
The quick start is to use the Maven archetype project in Documentum REST Services
SDK to create such a project structure.
type = FilterType.CUSTOM,
classes = {
com.emc.documentum.rest.context.ComponentScanExcludeFilter.class
}
)
}
)
You must ensure that the package where you created your custom
configuration class is specified in the rest.context.config.location property
within the rest-api-runtime.properties file.
The com.emc.documentum.rest.context.ComponentScanExcludeFilter
exclude filter is mandatory when you use the @ComponentScan Spring
annotation in your custom defined configuration class. This exclude filter
ensures that the Spring framework loads all of the resources that you have
defined.
The Java beans can be referenced by resource controllers by using the @Autowired
Spring annotation. Here is a code sample of the persistence API reference in a
resource controller:
Note: The complete persistence API docs can be found in the SDK:
documentum-rest-<version>.zip/apidocs/
Some persistence API methods require additional DFC calls, and should be used as
little as possible. Refer to the JavaDoc API for more information.
The default implementations for these two interfaces have been integrated with the
security module of the REST services. Therefore, after a user logs in, the persistence
API retrieves the right user session without the need to explicitly instantiate the DFC
session manager. The SessionAwareAbstractManager persistence API can be used
to extend the com.emc.documentum.rest.dfc interface to get the user session. Here
is a code sample that shows you how to do this:
@Override
public UserObject get(String userName,
AttributeView attributeView) throws DfException {
IDfSession session = null;
try {
// User the method from super class to get a session for the login user
session = getSessionRepository().getSession(false);
IDfUser dfUser = session.getUser(userName);
if(dfUser == null) {
return null;
}
else {
return convert(dfUser, attributeView);
}
}
finally {
// Do not forget to release the session
release(session);
}
}
}
the @Autowired Spring annotation. Here is a code sample that shows you how to do
this:
@Override
public UserObject get(String userName, AttributeView attributeView)
throws DfException {
IDfSession session = null;
try {
session = sessionRepository getSession(false);
IDfUser dfUser = session.getUser(userName);
if(dfUser == null) {
return null;
}
else {
return convert(dfUser, attributeView);
}
}
finally {
release(session);
}
}
If you are using a version of the JDK that is Java 7 or later, you can use Core REST
IDfCloseableSession API to manage the session. This API automatically handles
the session release. Here is a code sample that shows you how to use this API:
if(dfUser == null) {
return null;
}
else {
return convert(dfUser, attributeView);
}
}
}
}
When the Documentum REST Services security module is not used while
performing unit testing of the persistence API, you can set the login username and
password in your test code, which makes the session available to the test code. The
code mocks a user login from a servlet request. Here is a code sample that shows
you how to do this:
Example 8-63: Setting the Username and Password for Unit Testing
com.emc.documentum.rest.config.RepositoryContextHolder.setRepositoryName(...)
com.emc.documentum.rest.config.RepositoryContextHolder.setLoginName(...)
com.emc.documentum.rest.config.RepositoryContextHolder.setPassword(...)
@Override
public <T extends SysObject> T copy(final String objectId, final String folderId)
throws DfException {
// Put a regular operation into the transaction manager
return contextSessionManager.executeWithinTheContextTran
(new SessionCallable<T>() {
public T call(IDfSession session) throws Exception {
return doRegularCopy(objectId, folderId);
}
});
}
}
You can create a custom object collection manager if the data model in the page
does not extend com.emc.documentum.rest.model.PersistentObject. Here
is a code sample that shows you how to do this:
PagedQueryTemplate pagedQueryTemplate = new CustomPagedQueryTemplate(getUsername());
PagedDataRetriever<MyDocument>
pagedDataRetriever = new PagedDataRetriever<MyDocument>(
new MyDocumentCollectionManager(),
pagedQueryTemplate, 1, 5, true, AttributeView.DEFAULT);
Page<MyDocument> page = pagedDataRetriever.get();
Example 8-67: Use the Paginator to produce the page from any generic
collection
For a feed resource implementation where its persistent data does not come
from a simple DQL, you can create your own implementation of the persistence
layer and call the com.emc.documentum.rest.paging.Paginator method to
generate a page from the object collection. Here is a code sample that shows
you how to do this:
String dql = "select * from dm_document";
session = dfcSessionRepository.getSession();
List<IDfTypedObject> results = QueryExecutor.run(session, dql);
List<DocumentObject> docs = convert(results);
List<IDfTypedObject> counts = QueryExecutor
.run(session,
"select count(r_object_id) as total from dm_document");
int itemsPerPage = 5;
Page<DocumentObject>
currentPage = paginator.paginate(pageNumber, itemsPerPage);
if (format == null) {
throw new DfNoMatchException(NameAsPathCoder.decode(formatName));
}
return getRenderedObject(repositoryName, format, param.isLinks(), uriInfo, null);
}
}
The controller class declares the @Controller Spring annotation, which identifies it
as a resource controller. We recommend that you set a unique name for the
@Controller. The name for the controller is used as the code name of this resource,
which is used by configurations and log entries that reference this resource.
The controller class defines its request mapping with the @RequestMapping Spring
annotation. There are some additional mapping rules, however they do not apply to
the URI on this annotation. For more information, refer to the Spring framework
documentation.
The resource method can call its controller super class method getRenderedObject
or getRenderedPage to invoke dynamic view definitions that are used to render the
model instance and produce the links or the other representation customizations.
There are three abstract view classes for view implementations: LinkableView,
EntryableView, and FeedableView .
• LinkableView
A non-collection resource (also called single data object resource) must have a
LinkableView implementation. The extended class implementation must
implement the abstract methods. Optionally, the protected methods can be
overridden
• EntryableView
If the non-collection resource (also called single data object resource) can further
be presented into the inline feed of a collection resource. Its view implementation
instead should extend EntryableView. The extended class implementation must
implement the abstract methods. Optionally, the protected methods can be
overridden
• FeedableView
• com.emc.documentum.rest.view.impl.PersistentLinkableView
• com.emc.documentum.rest.view.impl.DefaultFeedView
There are two view annotations used for the view implementations. A view
implementation class must declare one of the following annotations for the
corresponding usages.
Note: There are more samples for the resource views in the documentum-rest-
<version>.zip/samples/documentum-rest-resource-samples/ SDK.
The annotation has an attribute that you can use to specify which view definitions
the resource controller (or method) uses to render the resource model in the
controller method. You can bind more than one view definition to a controller or a
method (repeating value). However, each view definition must correspond to a
unique resource model type, such as feed, document, folder, etc.
Here are some things to consider when deciding whether to apply the annotation at
the class level or at the method level.
• When the annotation is applied at the class level, all methods in the controller by
default uses the view definitions in the class level annotation
• When the annotation is applied at a class method level, the specific method in the
controller by default uses the view definitions in the method level annotation,
and it overrides the class level annotation.
Here is a code sample that shows you how to use the @ResourceViewBinding
annotation at both class and method levels. The class level supports the feed
representation by default, while the create method returns a single relation resource
as the Response.
Notes
Resource controllers, models, and views can be bound with one another by using the
Java annotations shown in the following diagram:
REST extensibility allows you to write your own view implementation classes for a
specific data model.
In a custom view, you can call the <removeLink(..)> method to hide link relations. You
can manually extend from the Core <View> implementation when the modification
that you want to make is small.
@Override
public void customize() {
// Remove relation-types link relation from the repository
removeLink(CoreLinkRelation.RELATION_TYPES.rel(), null);
}
}
In the custom view, you can call the <clearLinks()>, <makeLink(..)>, or <makeLinkIf(..)>
methods to build arbitrary link relations to a resource model. When a significant
change is required, you can write a completely new view implementation class for a
resource model.
In the following code sample of the <View> implementation for the Repository
resource, the links on the Repository resource are rebuilt in the View class. Take
notice of the following items:
• The link relation current-user and cabinets are static. This means they are
available in any repository resource
• The link relation users is dynamic. This means that only the <admin> or <super
user> can see it
@Override
public String entryTitle() {
return getDataInternal().getName();
}
@Override
public String entrySummary() {
return getDataInternal().getDescription();
}
@Override
public Date entryUpdated() {
return new Date();
}
@Override
public Date entryPublished() {
return new Date();
}
@Override
public List<
AtomAuthor>
entryAuthors() {
return Collections.emptyList();
}
@Override
public List<
Link>
entryLinks() {
return getDefaultEntryLinks();
}
@Override
public void customize() {
customizeLinks();
}
@Override
public String canonicalResourceUri(boolean validate) {
return getUriFactory(validate).repositoryUri(getDataInternal().getName(), null);
}
@Override
protected Map<
String, Object>
resolveUriTemplateVariables(Map<String,
String> valueMapping) {
return Collections.emptyMap();
}
When using a custom view, you can also customize what object properties to return
the client.
Here's a code sample that shows a view implementation on the cabinet resource
model where all internal properties are hidden.
/**
* Customize cabinet resource to remove internal attributes.
*/
public class CabinetViewExtend extends CabinetView {
public CabinetViewExtend(CabinetObject cabinet, UriInfo uriInfo,
String repositoryName, boolean returnLinks,
Map<String, Object> others) {
super(cabinet, uriInfo, repositoryName, returnLinks, others);
}
@Override
public void customize() {
// Remove all internal attributes with prefix "i_"
for (Attribute attr : serializableData.getAttributes()) {
if (attr.getName().startsWith("i_")) {
serializableData.removeAttributeIfExisted(attr.getName());
}
}
}
}
A custom view also allows you to customize the atom feed and entry attributes on a
collection or on a single data object resource.
In the following example of the View implementation on the Cabinets feed resource,
link relations and feed title are modified.
/**
* Customize cabinet resource to add new links and change feed title.
*/
public class CabinetsFeedViewExtend extends CabinetsFeedView {
public CabinetsFeedViewExtend(Page<CabinetObject> cabinets, UriInfo uriInfo,
String repositoryName, boolean returnLinks,
Map<String, Object> others) {
super(cabinets, uriInfo, repositoryName, returnLinks, others);
}
@Override
public String feedTitle() {
// Customize feed title
return "Cabinets in organization: " + getRepositoryName();
}
@Override
public List<Link> feedLinks() {
List<Link> links = super.feedLinks();
// Add about link relation to feed links
links.add(new Link("about", getUriFactory().productInfoUri()));
return links;
}
}
The view implementation classes must be built and packaged in a jar file where the
final REST war file can access it and load the classes into the class path.
The next and last step is to register the custom views to resources. The new view
implementations overrides the default views on resources. The configuration is in
the same YAML file.
---
# RESOURCE VIEW REGISTRY #
##########################
resource-view-registry:
# - resource: repository
# view: [org.acme.view.impl.custom.RepositoryViewExtend]
Here's a code sample that shows you how to register a previous view for resources.
---
# RESOURCE VIEW REGISTRY #
##########################
resource-view-registry:
- resource: repository
view: [org.acme.view.impl.custom.RepositoryViewExtend]
- resource: cabinet
view: [org.acme.view.impl.custom.CabinetViewExtend]
- resource: cabinets
view: [org.acme.view.impl.custom.CabinetsFeedViewExtend]
When multiple views are registered for a resource, each view definition should
correspond to a different data model. There could at most be one custom
<FeedableView> definition for a resource.
Here are some possible error codes or messages for invalid view registry
values:
E_UNKNOWN_RESOURCE_NAME=Unknown resource name is found: {0}.
E_RESOURCE_VIEW_DISALLOWED=Preserved core resources {0} are not allowed for view
customization. Please review your input resources: {1}
E_RESOURCE_VIEW_CLASS_NOT_FOUND=The view definition class is not found in class-path: {0}
The precedence of the view definition used by a controller method is: YAML defined
view > method level @ResourceViewBinding annotation > class level
@ResourceViewBinding annotation.
View Precedence
8.6.3.12.2.2 Troubleshooting
.view.impl
.FolderLinkView|
+ -- child-folder-links -- CLASS -- com.emc.documentum.rest
.view.impl
.FolderLinksFeedView|
com
.emc.documentum.rest
.view.impl
.FolderLinkView|
+ -- child-folder-links -- METHOD:link -- com.emc.documentum.rest
.view.impl
.FolderLinkView|
+ -- content -- CLASS -- com.emc.documentum.rest
.view.impl.ContentView|
+ -- contents -- CLASS -- com.emc.documentum.rest
.view.impl
.ContentsFeedView|com
.emc.documentum.rest
.view.impl.ContentView|
+ -- contents -- METHOD:createContent -- com.emc.documentum.rest
.view.impl.ContentView|
+ -- current-user -- CLASS -- com.emc.documentum.rest
.view.impl.UserView|
+ -- current-user2 -- CLASS -- com.emc.documentum.rest
.view.impl.UserView|
+ -- current-version -- CLASS -- com.emc.documentum.rest
.view.impl.SysObjectVie
w|
+ -- default-folder -- CLASS -- com.emc.documentum.rest
.view.impl.FolderView|
+ -- document -- CLASS -- com.emc.documentum.rest
.view.impl.DocumentView
|
+ -- dql-query -- CLASS -- com.emc.documentum.rest
.view.impl
.QueryResultFeedView|
com
.emc.documentum.rest
.view.impl
.QueryResultItemView|
+ -- folder -- CLASS -- com.emc.documentum.rest
.view.impl.FolderView|
+ -- folder-child-documents -- CLASS -- com.emc.documentum.rest
.view.impl
.DocumentsFeedView|com
.emc.documentum.rest
.view.impl.DocumentView
|
+ -- folder-child-documents -- METHOD:createChildDocument -- com.emc.documentum.rest
.view.impl.DocumentView
|
+ -- folder-child-folders -- CLASS -- com.emc.documentum.rest
.view.impl
.FoldersFeedView|com
.emc.documentum.rest
.view.impl.FolderView|
+ -- folder-child-folders -- METHOD:createChildFolder -- com.emc.documentum.rest
.view.impl.FolderView|
+ -- folder-child-objects -- CLASS -- com.emc.documentum.rest
.view.impl
.SysObjectsFeedView|com
.emc.documentum.rest
.view.impl
.SysObjectView|com.emc
.documentum.rest.view
.impl
.ContentfulObjectView|
+ -- folder-child-objects -- METHOD:postSysObject -- com.emc.documentum.rest
.view.impl
.SysObjectView|com.emc
.documentum.rest.view
.impl
.ContentfulObjectView|
+ -- group -- CLASS -- com.emc.documentum.rest
.view.impl.GroupView|
+ -- group-member-groups -- CLASS -- com.emc.documentum.rest
.view.impl
.GroupsFeedView|
+ -- group-member-users -- CLASS -- com.emc.documentum.rest
.view.impl
.UsersFeedView|
+ -- groups -- CLASS -- com.emc.documentum.rest
.view.impl
.GroupsFeedView|
+ -- lock -- CLASS -- com.emc.documentum.rest
.view.impl
.SysObjectView|
+ -- network-location -- CLASS -- com.emc.documentum.rest
.view.impl
.NetworkLocationView|
+ -- network-locations -- CLASS -- com.emc.documentum.rest
.view.impl
.NetworkLocationsFeedVi
ew|
+ -- object -- CLASS -- com.emc.documentum.rest
.view.impl
.SysObjectView|com.emc
.documentum.rest.view
.impl
.ContentfulObjectView|
+ -- parent-folder-link -- CLASS -- com.emc.documentum.rest
.view.impl
.FolderLinkView|
+ -- parent-folder-links -- CLASS -- com.emc.documentum.rest
.view.impl
.FolderLinksFeedView|
com
.emc.documentum.rest
.view.impl
.FolderLinkView|
+ -- parent-folder-links -- METHOD:link -- com.emc.documentum.rest
.view.impl
.FolderLinkView|
+ -- product-information -- CLASS -- com.emc.documentum.rest
.view.impl
.ProductInfoView|
+ -- relation -- CLASS -- com.emc.documentum.rest
.view.impl.RelationView
|
+ -- relation-type -- CLASS -- com.emc.documentum.rest
.view.impl
.RelationTypeView|
+ -- relation-types -- CLASS -- com.emc.documentum.rest
.view.impl
.RelationTypesFeedView|
+ -- relations -- CLASS -- com.emc.documentum.rest
.view.impl
.RelationsFeedView|com
.emc.documentum.rest
.view.impl.RelationView
|
+ -- relations -- METHOD:createRelation -- com.emc.documentum.rest
.view.impl.RelationView
|
+ -- repositories -- CLASS -- com.emc.documentum.rest
.view.impl
.RepositoriesFeedView|
+ -- repository -- CLASS -- com.emc.documentum.rest
.view.impl
.RepositoryView|
+ -- search -- CLASS -- com.emc.documentum.rest
.search.representation
.view.impl
.SearchFeedView|
+ -- type -- CLASS -- com.emc.documentum.rest
.view.impl.TypeView|
+ -- types -- CLASS -- com.emc.documentum.rest
.view.impl
.TypesFeedView|
+ -- user -- CLASS -- com.emc.documentum.rest
.view.impl.UserView|
+ -- users -- CLASS -- com.emc.documentum.rest
.view.impl
.UsersFeedView|
+ -- versions -- CLASS -- com.emc.documentum.rest
.view.impl
.VersionsFeedView|com
.emc.documentum.rest
.view.impl
.SysObjectView|com.emc
.documentum.rest.view
.impl.DocumentView|com
.emc.documentum.rest
.view.impl
.ContentfulObjectView|
+ -- versions -- METHOD:checkIn -- com.emc.documentum.rest
.view.impl
.SysObjectView|com.emc
.documentum.rest.view
.impl.DocumentView|com
.emc.documentum.rest
.view.impl
.ContentfulObjectView|
+ -- versions -- METHOD:checkInContent -- com.emc.documentum.rest
.view.impl
.SysObjectView|com.emc
.documentum.rest.view
.impl.DocumentView|com
.emc.documentum.rest
.view.impl
.ContentfulObjectView|
+ -- versions -- METHOD:checkInMetadata -- com.emc.documentum.rest
.view.impl
.SysObjectView|com.emc
.documentum.rest.view
.impl.DocumentView|com
.emc.documentum.rest
.view.impl
.ContentfulObjectView|
+ -- NAME -- -- LEVEL -- -- VIEW -- +
+++++++++++++++++++++ RESOURCE DEFAULT VIEW PRINT END ++++++++++++++++++++
LinkableView
By default, the class LinkableView returns the self link for the resource model in
the protected method xselfLinks(). The class provides several abstract or
protected methods for overriding.
• To customize self link generation, you can override the method selfLinks().
• To add additional links, you can implement the method customize(), and call
makeLink(), makeLinkIf(), makeLinkTemplate(), or makeLinkTemplateIf()
in the customize() method.
• To remove a specific link, you can call removeLink() in the customize()
method.
• To clear all default links, you can call clearLinks() in the customize()
method.
The method makeLinkIf() adds a link relation to the resource only when the
condition is met at runtime. This method is useful for adding link relations upon
certain conditions.
EntryableView
FeedableView
By default, the FeedableView class returns the self link and pagination links
(first, next, previous, or last depending on the current page position) for the
atom feed model. You can override the feedLinks() method to modify these link
relations or add other link relations.
// Build custom alias-set resource URI by resource name: <base uri context>/
// repositories/<repoName>/alias-set/1234?view=:all
String href = ResourceUriBuilder
.onResource("acme#alias-set")
.pathVariables("1234")
.queryParam("view", ":all")
.build();
// Build custom alias-set resource URI by resource controller: <base uri context>/
// repositories/<repoName>/alias-set/1234?view=:all
String href = ResourceUriBuilder
.onResource(org.acme.rest.AliasSetController.class)
.pathVariables("1234")
.queryParam("view", ":all")
.build();
// Build custom alias-set resource URI by custom URI template: <base uri context>/
// repositories/<repoName>/alias-set/1234?view=:all
String href = ResourceUriBuilder
.onTemplate("X_ALIAS_SET_URI_TEMPLATE")
.pathVariables("1234")
.queryParam("view", ":all")
.build();
At runtime, the <base uri context> and the format extension are resolved
automatically. In unit testing, you can specify the base URI context and format
extension to verify the full path. Here's a code sample that shows you how to do
that:
UriInfo uriInfo = new UriInfo();
uriInfo.setBaseUri("http://localhost:8080/dctm-rest");
uriInfo.setFormatExtension("");
String href = ResourceUriBuilder.onResource("user")
.repository("ACME")
.uriInfo(uriInfo)
.pathVariable(true, "dmadmin")
.build();
assertEquals("http://localhost:8080/dctm-rest/repositories/ACME/users/
dmadmin", href);
When implementing a resource view, you can use the newly added methods on the
abstract views to get the URI resource builder. Here's a listing of those methods:
• com.emc.documentum.rest.view.LinkableView#uriBuilder(string)
• com.emc.documentum.rest.view.LinkableView#idUriBuilder(string,
string)
• com.emc.documentum.rest.view.LinkableView#nameUriBuilder(string,
string)
• com.emc.documentum.rest.view.FeedableView#uriBuilder(string)
To make the custom resource linkable from the DQL query resource, add query
types on the controller annotation @ResourceViewBinding for your custom resource
controller, as shown in the following sample:
@Controller("acme#alias-set")
@RequestMapping("/repositories/{repositoryName}/alias-sets/{aliasSetId}")
// make query results of type "dm_alias_set" linkable to the acme#alias-set resource
@ResourceViewBinding(value = AliasSetView.class, queryTypes = "dm_alias_set")
public class AliasSetController extends AbstractController { … }
Note: The OpenText Documentum REST Services Reference Guide introduces the
detail rules for all of the DQL expressions that can produce resource links.
• com.emc.documentum.rest.model.batch.annotation.BatchProhibition
• com.emc.documentum.rest.model.batch.annotation.
TransactionProhibition
@BatchProhibition
Besides of adding @BatchProhibition to the class level, you can add this
annotation to one or more methods in the controller class to prevent certain
operations of the resource from being embedded to batch requests.
@Controller
public class MyResourceController {
//...
@RequestMapping (
value = {"/users/{userName}/MyResources/{MyResourceId}"},
method = RequestMethod.GET, produces = {
SupportedMediaTypes.APPLICATION_VND_DCTM_JSON_STRING,
SupportedMediaTypes.APPLICATION_VND_DCTM_XML_STRING,
MediaType.APPLICATION_JSON_VALUE,
MediaType.APPLICATION_XML_VALUE)
@ResponseBody
@ResponseStatus(HttpStatus.OK)
@BatchProhibition
public MyResource getMyResource (...){...}
@RequestMapping(
value = {"/users/{userName}/MyResources/{MyResourceId}"},
method = RequestMethod.DELETE)
@ResponseBody
@ResponseStatus(HttpStatus.NO_CONTENT)
public void deleteMyResource(...){...}
//...
}
This code snippet prevents the GET operation on MyResource from being embedded
in batch requests. However, you can still perform a bulk delete on MyResource in a
batch request because neither the MyResourceController class nor the
deleteMyResource method is annotated with @BatchProhibition.
@TransactionProhibition
This setting prevents the custom resource from being embedded in transactional
batch requests.
For more information about batch requests, see the Batch and Batch Capabilities
sections in the OpenText Documentum REST Services Reference Guide.
This sample model contains a custom feed attribute ‘score’ but will still be
marshaled as the same feed root <feed …/> for XML.
If the custom attributes are added to the atom entry, the view must extend com.emc.
documentum.rest.model.AtomEntry, too. Here is the sample for the extended atom
entry.
@SerializableType(inheritValue = true, xmlNS = ", xmlNSPrefix = "atom")
public class AliasSetEntry extends AtomEntry {
@SerializableField("own-by-login-user")
private boolean ownByLoginUser;
After the model definition, you need to create custom views for the extended feed
and entry to populate the custom attribute data. For a feed view definition, the class
must explicitly declare its binding feed type as the extended feed type. Here is the
feed view example.
@FeedViewBinding(value = AliasSetViewX.class, feedType = AliasSetFeed.class)
public class AliasSetsFeedViewX extends FeedableView <AliasSet> {
public AliasSetsFeedViewX(Page<AliasSet> page, UriInfo uriInfo,
String repositoryName, boolean returnLinks,
Map<String, Object> others) {
super(page, uriInfo, repositoryName, returnLinks, others);
}
@Override
public String feedTitle() {
return "Alias Sets";
}
@Override
public Date feedUpdated() {
return new Date();
}
@Override
protected void customizeFeed(AtomFeed feed) {
AliasSetFeed aliasSetFeed = (AliasSetFeed) feed;
aliasSetFeed.setScore(new SecureRandom().hashCode());
}
}
For an entry view definition, it must explicitly declare the binding entry type is the
extended atom entry type. Here is the entry view example.
@DataViewBinding(queryTypes = "dm_alias_set",
modelType = AliasSet.class, entryType = AliasSetEntry.class)
public class AliasSetViewX extends PersistentLinkableView <AliasSet> {
public AliasSetViewX(AliasSet aliasSet, UriInfo uriInfo,
String repositoryName, boolean returnLinks,
Map<String, Object> others) {
super(aliasSet, uriInfo, repositoryName, returnLinks, others);
}
...
@Override
protected void customizeEntry(AtomEntry atomEntry) {
AliasSetEntry aliasSetEntry = (AliasSetEntry) atomEntry;
aliasSetEntry.setOwnByLoginUser(
RepositoryContextHolder.getUserName().equals
(getDataInternal().getAttributeByName("owner_name")));
}
}
When you extend from <AtomFeed>, the new entry type must be provided, in
addition to adding new fields.
@SerializableType(value = "feed", ...)
public class CustomizedAtomFeed extends AtomeFeed<CustomizedAtomEntry> {
private String customFeedField;
}
A small compatibility issue exists in the 7.2 code. Code that is currently working
with the 7.2 version of <AtomFeed>, <AtomEntry>, or both, may code as shown here:
AtomFeed feed = ...
// Iterate the entries
for(AtomEntry entry : feed.getEntries()) { //compile error
...
}
Due to a limitation of Java generics, the new version of <AtomFeed> in version 7.3
code has compilation issues. Since <AtomFeed> is now defined as a Java generic
class, using it as a normal class causes its generic information to disappear. The
return type of <feed.getEntries()> becomes List<Object> instead of List<AtomEntry> as
expected.
To remedy this issue, you can use generic information with <AtomFeed> or you can
explicitly cast the return type of <feed.getEntries()> to List<AtomEntry>. Here's a code
sample that illustrates this:
AtomFeed<AtomEntry> feed = ...
The extended exception mapping class adds more methods for the exception
mapping with Spring annotation @Exceptionhandler. The output of the method
must be an instance of com.emc.documentum.rest.model.RestError. Here is an
example of the default error mapping for ConversionFailedException.
@ResponseBody
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(ConversionFailedException.class)
public RestError onConversionFailedException(ConversionFailedException e) {
return buildRestError(new GenericExceptionConverter(e,
HttpStatus.BAD_REQUEST, "E_INPUT_ILLEGAL_ARGUMENTS"));
}
Each part of the multipart contents contains Headers and the content stream. These
parts can be read one by one using standard Iterator methods such as hasNext and
next. All parts of the multipart contents must be read sequentially because they are
all in one multipart stream, and the REST server does not cache any part of the
stream.
To make the custom REST services hypermedia driven, there must establish link
relations between Core resources and custom resources. Documentum REST
Services SDK provides an approach to add link relations to Core resources for the
custom links. Refer to the section Adding Links to Core Resources for the details.
For Maven users, the custom resource project can leverage the Maven war overlay
plugin to repackage the REST war file by bundling both Core resources and custom
resources into the single WAR file. Documentum REST Services SDK provides the
sample Maven pom file in its Maven archetype project to illustrate how to build the
custom resource WAR. The war overlay plugin configuration looks similar to the
following.
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.4</version>
<configuration>
<overlays>
<overlay>
<groupId>com.emc.documentum.rest</groupId>
<artifactId>documentum-rest-web</artifactId>
<excludes />
</overlay>
</overlays>
</configuration>
</plugin>
</plugins>
</build>
With the new custom resource WAR file, both Core resources and custom resources
are running in the same server box and can work together smoothly. In your
development environment, you can enable DEBUG level in log4j for the package emc.
emc.documentum.rest. All resource controller and view information is printed out
in the log file or on the server console.
You are free to rename the YAML file, but the file extension must remain .yaml, for
example my-custom-rest.yaml. The rest-api-runtime.properties.template
file, which is located in the same location, has more details on the configuration.
There are two HAL properties that are reserved, they are the _links and the
_embedded properties. These two reserved properties are the key difference
between the JSON ( application/vnd.emc.documentum+json ) media type and the
HAL ( application/hal+json ) media type.
• One link specified for the link relation, then the value of that link’s key is the
JSON object
• More than one link specified for the link relation, then the values of that link are
stored in a JSON array. Each link is one element of the JSON array object and
each link’s title property is used to distinguish different links
The _links reserved property uses link relations to expose links to other resources.
The link relation may link to a link object or to an array of link objects. The link
object has the following properties that are used by Documentum REST Services:
• href
• templated
• title
• type
The definitions for each of the object’s link relation properties is found in the HAL
specification at IETF Tools website.
HAL
{
"...",
"_links": {
"self": {
"href": "xxx"
},
"the_link_rel_with_title" : [
{
"title": "title_1",
"href": "xxx"
},
{
"title": "title_2",
"href": "xxx"
}
]
}
}
JSON
{
"...",
"links": [
{
"href": "xxx",
"rel": "self"
},
{
"href": "xxx",
"title": "title_1",
"rel": "the_link_rel_with_title"
},
{
"href": "xxx",
"title": "title_2",
"rel": "the_link_rel_with_title",
}
]
}
HAL
{
"_embedded" : {
"collection" : [
...
]
}
}
JSON
{
"entries": [
...
]
}
The HAL representation for a single resource is similar to the original application/
vnd.emc.documentum+json media type. However, while the resource’s properties
are not changed, the links for that resource are put within the _links element as
required by the HAL specification.
HAL
{
"other_properties : "...",
"_links":
{
"self" : {
"href": "..."
}
}
}
JSON
{
"other_properties": "xxx",
"links": [
{
"rel": "self",
"href": "xxx"
}
]
}
When using the POST method to create instances for a resource, a request with
Content-Type=application/hal+json has the same format as a request used for
its single resource.
In the HAL media type shown in the code sample below, the src attribute is
removed and its value is merged into the _links element. The following code
sample shows you the Response from a collection resource without inline
items.
HAL
{
"id": "xxx",
"title": "xxx",
"_embedded": {
"collection": [
{
"id": "xxx",
"title": "xxx",
"_links": {
"self": {
"href": "xxx"
}
}
}
]
},
"_links": {
"self": {
"href": "xxx"
}
}
}
JSON
{
"id": "xxx",
"title": "xxx",
"author": [
{
"name": "Open Text Documentum"
}
],
"updated": "2018-05-02T07:26:34.311+00:00",
"page": 1,
"items-per-page": 10,
"total": 100,
"entries": [
{
"id": "xxx",
"title": "xxx",
"summary": "xxx",
"published": "xxx",
"updated": "xxx",
"author": [
{
"name": "Open Text Documentum"
}
],
"links": [
{
"rel": "edit",
"href": "xxx"
}
],
"content": {
"type": "application/vnd.emc.documentum+json",
"src": "xxx"
}
}
],
"links": [
{
"rel": "self",
"href": "xxx"
}
]
}
For inline representation of collection resources, the content is put into the
content keyword element of each object in the collection resource.
The following code sample shows you the format of a response from a
collection resource where its contents is inline (its inline URL parameter is
true). The content keyword element with its inline property items has been
emphasized in bold.
HAL
{
"id": "xxx",
"title": "xxx",
"author": [
{
"name": "Open Text Documentum"
}
],
"updated": "2018-05-02T07:26:34.311+00:00",
"page": 1,
"items-per-page": 10,
"total": 100,
"_embedded": {
"collection": [
{
"id": "xxx",
"title": "xxx",
"updated": "xxx",
"published": "xxx",
"summary": "xxx",
"content": {"xxx": "xxx"},
"_links": {
"self": {"href": "xxx"}
}
}
]
},
"_links": {
"self": {"href": "xxx"}
}
}
JSON
{
"id": "xxx",
"title": "xxx",
"author": [
{
"name": "Open Text Documentum"
}
],
"updated": "2018-05-02T07:26:34.311+00:00",
"page": 1,
"items-per-page": 10,
"total": 100,
"entries": [
{
"id": "xxx",
"title": "xxx",
"summary": "xxx",
"published": "xxx",
"updated": "xxx",
"author": [
{
"name": "Open Text Documentum"
}
],
"links": [
{
"rel": "edit",
"href": "xxx"
}
],
"content": {
"xxx": "xxx",
"links": [
{
"rel": "edit",
"href": "xxx"
}
]
}
}
],
"links": [
{
"rel": "self",
"href": "xxx"
}
]
}
As you can see in the above example, the inline item is embedded into the
content element. However, there is no _links in the content. Instead, all links
are put into the entry level _links element.
{
"resources": {
"http://identifiers.emc.com/linkrel/repositories": {
"href": "http://localhost:8080/dctm-rest/repositories",
"hints": {
"allow": ["GET"],
"representations": [
"application/xml",
"application/json",
"application/vnd.emc.documentum+json",
"application/atom+xml",
"application/hal+json"
]
}
},
"about": {
"href": "http://localhost:8080/dctm-rest/product-info",
"hints": {
"allow": ["GET"],
"representations": [
"application/xml",
"application/json",
"application/vnd.emc.documentum+json",
"application/hal+json",
"application/vnd.emc.documentum+xml"
]
}
}
}
}
8.8.4.2 Folder Child Objects, Folder Child Documents, and All Versions
resources
When the Request is a multipart request, depending on the media type of the first
metadata part, the content type can be any one of the following:
• application/vnd.emc.documentum+xml
• application/vnd.emc.documentum+json
• application/hal+json
The following code sample shows you how to make a multipart request using the
application/hal+json Content-Type.
POST http://localhost/dctm-rest/repositories/myrepo/objects/0c00000c80000105/objects
Content-Type: multipart/form-data; boundary=314159265358979
--314159265358979
Content-Disposition: form-data; name=metadata
Content-Type: application/hal+json
When you use the Batch resource without an attachment and the batch operation has
the Content-Type header defined, its value element can be application/hal
+json. The following code sample shows you how to use the Batch resource to make
a request without attachments:
{
"operations": [
{
"id": "id-001",
"request": {
"method": "POST",
"uri": "/<base URI>/objects",
"headers": [
{
"name": "Content-Type",
"value": "application/hal+json"
}
],
"entity": "{\"properties\":{\"object_name\":\"my test object\"}}"
}
}
]
}
When you make a request using application/hal+json as the metadata, then the
start-info of the request content-type should also be application/hal+json
and the content type of the metadata part is application/jop+json;
type="application/hal+json.
The following code sample shows you how to use the Batch resource to make a
multipart request that uses the application/hal+json media type and has
attachments:
POST http://localhost:8080/dctm-rest/repositories/acme/batches
Content-Type: Multipart/Related;boundary=frontier;type="application/jop+json";
start="batch";start-info="application/hal+json"
Accept: application/hal+json
--frontier
Content-Type: application/jop+json; type="application/hal+json"
Content-ID: batch
Content-disposition: form-data; name=batch
{
"operations" :
[
{
"id" : "id-100",
"request" :
{
"method" : "POST",
"uri" : "/repositories/REPO/folders/0c00208080000107/objects",
"headers" :
[
{
"name" : "Content-Type",
"value" : "application/hal+json"
},
{
"name" : "Accept",
"value" : "application/hal+json"
}
],
"entity" : "{
\"properties\":{\"object_name\":\"my test object\"}
}",
"attachments" : [{
"Include" : {
"href" : "cid:id-100-content"
}
}]
}
},
{
"id" : "id-101",
"request" :
{
"method" : "POST",
"uri" : "/repositories/REPO/folders/0c00208080000107/objects",
"headers" :
[
{
"name" : "Content-Type",
"value" : "application/hal+json"
},
{
"name" : "Accept",
"value" : "application/hal+json"
}
],
"entity" : "{
\"properties\":{\"object_name\":\"my test object\"}
}",
"attachments" : [{
"Include" : {
"href" : "cid:id-101-content"
}
}]
}
}
]
}
--frontier
Content-Type: text/plain
Content-ID: id-100-content
Content-disposition: form-data; name=id-100-content
i'm the content of id-100
--frontier
Content-Type: text/plain
Content-ID: id-101-content
Content-disposition: form-data; name=id-101-content
i'm the content of id-101
--frontier--
The following code sample shows you how to extend the standard AtomFeed to
make a customized feed for HAL. The SearchAtomFeed class shown below has the
facets property as one more property than the standard AtomFeed class.
Here is a code sample that shows you the SearchAtomFeed class with the
declaration of the facets property:
This interface has only one method (the convert method), which is used to convert
customized information into a HalObject object. The HalObject already has pre-
converted information, such as entries and feed information.
Tip: You can also convert your customized information into a totally
customized HAL format.
The Object member of the HalObject class is used for a single resource.
However, it may also be used for feed level information of a collection
resource.
The HalCollection member of the HalObject class is only used for the collection
resource and it refers to the collection element of the _embedded element.
Here the facets parameter should be represented at the same level as the
collection, so a customized HalCollection model, in which the facets object
is defined, is required.
@SerializableType(ignoreNullFields = true, fieldOrder = {"collection", "facets"})
public static class HalSearchCollection extends HalCollection {
@SerializableField4XmlList(unwrap=true, asPropBag=true)
private List<FacetEntry> facets;
...
}
Here is a code sample that shows you the SearchAtomFeed class definition:
public class SearchAtomFeed extends AtomFeed<AtomEntry>
implements HalConvertable {
...
@Override
public void convert(HalObject object) {
// Create a new HalSearchCollection, and set the original embedded
// collection of the HalObject to the new HalSearchCollection.
// Then set the facets within the HalSearchCollection object and
// don't forget to set the original variable facetResults to null.
// Otherwise, it will be in the Response representation.
if(facetResults != null && !facetResults.isEmpty()) {
object.setEmbedded(new HalSearchCollection(object.getEmbedded())
.setFacets(facetResults));
facetResults = null;
}
}
}
For more information on turning off media types, see “Turning off XML, JSON, or
HAL media types” on page 343.
Here is a code snippet that shows you the HAL only registry definition:
media-type-registry:
- media-type: hal
Example:
uri-template-registry:
- name: X_ALIAS_SET_RESOURCE_TEMPLATE
href: '{repositoryUri}/alias-sets/{aliasSetId}{ext}?owner={userName}'
encoding: dual-url-encoding
- name: X_SEARCH_RESOURCE_TEMPLATE
hreftemplate: '{baseUri}/x-search{?q,facet}'
- name: X_MODULE_RESOURCE_TEMPLATE
href: '{baseUri}/global-modules/{moduleId}{ext}'
Note: YAML is very strict with indentation and spaces. Incorrect indentation
and redundant spaces may cause errors. Tab characters (\t) are never allowed
for indentation in YAML.
This configuration enables you to register new URI templates with the shared URI
factory. The system uses these URI templates to resolve new links that are added to
existing resources. A URI template consists of the following elements as key-value
pairs:
Key Value
name * Name of a custom URI template.
Example: X_ALIAS_SET_RESOURCE_TEMPLATE
Key Value
[href|hreftemplate] URI template pattern for a link. The key is either <href> or
* <hreftemplate>. Only one <href> or <hreftemplate> exists in a URI
template.
{repositoryUri}/<item1>/<item2>/<...>?<param1>&
<param2>&<...>{ext}
For an href link, all variables in the URI pattern must be resolved on
the server side, meaning that you must modify value-mapping
correspondingly in the ADD LINKS ON EXISTING RESOURCES section
for non-predefined variables.
Examples of hreftemplates
• <hreftemplate> with no fixed query parameters:
/repositories/{repositoryName}/search?{?q,locations}
• <hreftemplate> with both fixed query parameters and placeholders:
/repositories/{repositoryName}/search?page=10&items-per-
page={numberOfItems}{&q,locations}
Key Value
external Specifies whether this URI template is external or internal. Valid
values are:
• true: Indicates this is an external URI template. External URI
templates are not mapped to any custom resources. The external
URI templates MUST NOT use predefined variables
{<repositoryUri>} or {<ext>}. The accessibility of an external URI
template will not be validated by the REST server.
• false: Indicates this is an internal URI template. The internal URI
template is mapped to a REST resource. The internal URI template
MUST start with predefined variables {<baseUri>}, {<repositoryUri>}
The URI template name must be unique so that other resources used to build link
relations can reference the template. In the code, the URI template can also be used
to create an actual URL using the buildUriByTemplateName method of com.emc.
documentum.rest.http.UriFactory. For example:
String moduleUri = uriFactory.buildUriByTemplateName(
"X_MODULE_RESOURCE_TEMPLATE",
Collections.singletonMap("moduleId", getDataInternal().getId()));
With this feature, the following URI templates are the same:
From Documentum REST Services version 7.3 and later, a complete definition of
which customizations can be applied to a custom URI template have been added.
Here's a code sample that shows you the URI template registry
uri-template-registry:
- name: X_HELP_TEMPLATE
href: '{baseUri}/help{ext}'
- name: X_POLICIES_FEED_TEMPLATE
href: '{repositoryUri}/objects/{objectId}/policies{ext}'
- name: X_POLICY_TEMPLATE
href: '{repositoryUri}/objects/{objectId}/policies/{policyId}{ext}'
- name: X_AUTHOR1_TEMPLATE
href: '{repositoryUri}/users/{userId}{ext}'
encoding: safe-text-encoding
- name: X_AUTHOR2_TEMPLATE
href: '{repositoryUri}/users{ext}?name={userId}'
- name: X_CUSTOM_SEARCH_TEMPLATE
hreftemplate: '{baseUri}/x-search{?q,locations,page,items-per-page}'
- name: X_CUSTOM_LOCATION_SEARCH_TEMPLATE
hreftemplate: '{repositoryUri}/x-search?locations={path}{&q,page,items-per-page}'
- name: X_EXTERNAL_SEARCH_TEMPLATE
href: 'http://www.google.com?q={keyword}'
external: true
With this feature, the following URI templates are the same:
• Appending template parameters {?a,b,c,...} onto a URI where there are no fixed
query parameters
For example: /repositories/{repositoryName}/search?{?q, locations,
...}
• Appending template parameters {&a,b,c,...} onto a URI where there are fixed
query parameters
For example: /repositories/{repositoryName}/search?page=10&items-
per-page={numberOfItems}{&q, locations,...}
---
resource-link-registry:
- resource: document
link-relation: 'http://www.custom.com/sample/linkrel/author'
uri-template: 'X_AUTHOR_TEMPLATE'
value-mapping: [userId:owner_name]
- resource: document
link-relation: 'http://www.custom.com/sample/linkrel/marketing-groups'
uri-template: 'X_AUTHOR_TEMPLATE'
value-mapping: [userId:owner_name,subject:subject]
- resource: document
link-relation: 'http://www.custom.com/sample/linkrel/folder-search'
uri-template: 'X_CUSTOM_LOCATION_SEARCH_TEMPLATE'
value-mapping: [location:r_folder_path]
- resource: document
link-relation: 'http://www.custom.com/sample/linkrel/gsearch'
uri-template: 'X_EXTERNAL_SEARCH_TEMPLATE'
value-mapping: [keyword:keywords]
href: '{baseUri}/help{ext}'
- name: X_POLICIES_FEED_TEMPLATE
href: '{repositoryUri}/objects/{objectId}/policies{ext}'
- name: X_POLICY_TEMPLATE
href: '{repositoryUri}/objects/{objectId}/policies/{policyId}{ext}'
- name: X_AUTHOR1_TEMPLATE
href: '{repositoryUri}/users/{userId}{ext}'
encoding: safe-text-encoding
- name: X_AUTHOR2_TEMPLATE
href: '{repositoryUri}/users{ext}?name={userId}'
- name: X_CUSTOM_SEARCH_TEMPLATE
hreftemplate: '{baseUri}/x-search{?q,locations,page,items-per-page}'
- name: X_CUSTOM_LOCATION_SEARCH_TEMPLATE
hreftemplate: '{repositoryUri}/x-search?locations={path}{&q,page,items-per-page}'
- name: X_EXTERNAL_SEARCH_TEMPLATE
href: 'http://www.google.com?q={keyword}'
external: true
For example, the internal URI template definitions below are equivalent:
• Appending template parameters {&a,b,c,...} onto a URI where there are fixed
query parameters
For example: /repositories/{repositoryName}/search?page=10&items-
per-page={numberOfItems}{&q,locations,...}
Here's a code sample that shows you how to work with URI templates:
---
resource-link-registry:
- resource: document
link-relation: 'http://www.custom.com/sample/linkrel/author'
uri-template: 'X_AUTHOR_TEMPLATE'
value-mapping: [userId:owner_name]
- resource: document
link-relation: 'http://www.custom.com/sample/linkrel/marketing-groups'
uri-template: 'X_AUTHOR_TEMPLATE'
value-mapping: [userId:owner_name,subject:subject]
- resource: document
link-relation: 'http://www.custom.com/sample/linkrel/folder-search'
uri-template: 'X_CUSTOM_LOCATION_SEARCH_TEMPLATE'
value-mapping: [location:r_folder_path]
- resource: document
link-relation: 'http://www.custom.com/sample/linkrel/gsearch'
uri-template: 'X_EXTERNAL_SEARCH_TEMPLATE'
value-mapping: [keyword:keywords]
root-service-registry:
- name: <descriptive information of the resource>
target-resource:<resource name register for this service>
link-relation:<the link relation name of the resource>
uri-template: <the URI template name registered for this service>
allowed-methods:<allowed HTTP methods on this resource>
media-types:<supported media types for this resource>
This configuration enables you to register new resources to the Home Document.
You can specify the following elements (key-value pairs):
Key Value
name Descriptive information of a resource registered to the Home
Document.
Key Value
uri-template URI template registered for this resource. The URI template must exist
in the REGISTER NEW URI TEMPLATES on page 323 section.
Example: [application/vnd.emc.documentum+json,
application/vnd.emc.documentum+xml]
* Required
Once a custom top-level resource is registered, the Core Home document resource
will contain the additional entry for the top-level resource. Here is an example.
For link relations added to the Home Document, only predefined variables and
variables used as placeholders can appear in the URI templates. For example, the
following URI template is not eligible for link relations added to the Home
Document:
Example:
uri-template-registry:
- name: X_ALIAS_SET_RESOURCE_TEMPLATE
hreftemplate:'{repositoryUri}/alias-sets/{aliasSetId}{ext}?owner={userName}'
encoding:dual-url-encoding
external:false
Example:
root-service-registry:
- name: User defined global search resource
link-relation: 'x-search'
uri-template: X_SEARCH_RESOURCE_TEMPLATE
allowed-methods:[POST]
media-types: [application/vnd.emc.documentum+json, application/vnd.emc.documentum
+xml]
3. Deploy the custom resources WAR file after the YAML is updated. The
following sample of Home Document has the custom link relation x-search
added.
<?xml version="1.0" encoding="UTF-8"?>
<resources xmlns="http://identifiers.emc.com/vocab/documentum"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<!-- core top resource -->
<resource rel="http://identifiers.emc.com/linkrel/repositories">
<link href="http://localhost:8080/acme-rest/repositories"/>
<hints>
<allow><i>GET</i></allow>
<representations>
<i>application/xml</i>
<i>application/json</i>
<i>application/atom+xml</i>
<i>application/vnd.emc.documentum+json</i>
</representations>
</hints>
</resource>
<!-- core top resource -->
<resource rel="about">
<link href="http://localhost:8080/acme-rest/product-information"/>
<hints>
<allow><i>GET</i></allow>
<representations><i>application/xml</i>
<i>application/json</i>
<i>application/vnd.emc.documentum+xml</i>
<i>application/vnd.emc.documentum+json</i>
</representations>
</hints>
</resource>
<!-- custom top resource -->
<resource rel="x-search">
<link hreftemplate="http://localhost:8080/acme-rest/x-search{?q,facet}"/>
<hints>
<allow><i>POST</i></allow>
<representations>
<i>application/vnd.emc.documentum+json</i>
<i>application/vnd.emc.documentum+json</i>
</representations>
</hints>
</resource>
</resources>
This configuration allows you to register new links to Core resources. You can
specify the following elements (key-value pairs):
Key Value
resource * The code name of the resource to which the links are added. Each
Core resource has a unique code name. In the link registry,
following resources are supported:[format, user, group, content,
relation, type, object, document, folder, cabinet, network-
location, relation-type, repository]. See Appendix C for all code
names of Core resources.
Key Value
link-relation * Link relation name of the registered resource, which enables you
to locate the resource in the existing resource.
One and only one link relation can be used to refer to a registered
resource.
uri-template * The name of the URI template registered in uri-template-
registry. The URI template can contain variables which need
to be resolved during the generation of the link relation href.
value-mapping Value mappings are used to resolve values of path variables or
query parameters on URI templates defined in uri-template.
[<variableName1>:<propertyName1>[,<variableName2>:<propertyNam
e2>]*
Once the custom root resource is registered, the Core home document resource will
contain the additional entry for the root resource. Here is an example.
resource-link-registry:
- source: object
link-relation:'http://identifiers.emc.com/linkrel/acl'
uri-template: X_ACL_RESOURCE_TEMPLATE
value-mapping:[objectId:r_object_id]
3. Deploy the custom resources WAR file after the YAML is updated. The
following sample of Home Document has the custom link relation http://
identifiers.emc.com/linkrel/acl is added.
Example:
<object xmlns="http://identifiers.emc.com/vocab/documentum"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="dm_cabinet"
definition="http://localhost:8080/acme-rest/repositories/
REPO/types/dm_cabinet">
<propertiesxsi:type="dm_cabinet-properties">
<object_name>a;x.f</object_name>
...
<r_object_id>0c0020808000dfb6</r_object_id>
</properties>
<links>
<!-- core link relations-->
<link rel="self"
href="http://localhost:8080/acme-rest/repositories/REPO/objects/
0c0020808000dfb6"/>
..
<link rel="http://identifiers.emc.com/linkrel/relations"
href="http://localhost:8080/acme-rest/repositories/REPO/relations?
related-object-id=0c0020808000dfb6&related-object-role=any"/>
<!--customer defined link relations-->
<link rel="http://identifiers.emc.com/linkrel/acl"
href="http://localhost:8080/acme-rest/repositories/REPO/objects/
0c0020808000dfb6/acl"/>
</links>
</object>
There are some elements that you may want to reference when developing custom
resources. For more information, see Appendix B, Resource coding index
on page 465.
For example:
• Case 1: Inactive link relations are removed on existing resources because certain
other resources are disabled.
The <user> resource and the <user default folder> resource. When the <user default
folder>resource is disabled, the link relation http://identifiers.emc.com/
linkrel/default-folder on <user> resource is no longer presented. Here's an
image that illustrates this case.
• Case 2: The atom entry edit link is not present when the single object resource is
disabled.
For example, the <users> resource and the <user> resource. When the <user>
resource is disabled, the link relation edit on the <users> resource's entries is no
longer present.
The following diagrams illustrate other cases where disabling a resource has an
impact on another resource.
When the <user> resource is disabled, the atom user URL in the atom feed is
removed. One example is the <cabinets> resource.
When the <type> resource is disabled, the type definition URL in the persistent object
resource is removed. One example is the <cabinet> resource.
When the <root> is disabled, the link relation in the home document is removed. One
example is the <product information> resource.
Some collection based resources support the HTTP POST method to create a new
resource in the collections. When the single resource is disabled, the POST method on
the collection resource fails. One example is the <cabinets> resource and <cabinet>
resource.
8.13.3 Samples
Example 8-94: Disable a Resource
Configuration
Representation
At runtime, both the <formats> and the <format> resources are not accessible.
GET /acme-rest/repositories/REPO/formats HTTP/1.1
GET /acme-rest/repositories/REPO/formats/crtext HTTP/1.1
<id>8320</id>
<name>REPO</name>
<links>
<link rel="self" href="/acme-rest/repositories/REPO"/>
<link rel="http://identifiers.emc.com/linkrel/alias"
href="/acme-rest/repositories/REPO/alias-sets"/>
<link rel="http://identifiers.emc.com/linkrel/users"
href="/acme-rest/repositories/REPO/users"/>
<link rel="http://identifiers.emc.com/linkrel/current-user"
href="/acme-rest/repositories/REPO/currentuser"/>
<link rel="http://identifiers.emc.com/linkrel/groups"
href="/acme-rest/repositories/REPO/groups"/>
<link rel="http://identifiers.emc.com/linkrel/cabinets"
href="/acme-rest/repositories/REPO/cabinets"/>
<link rel="http://identifiers.emc.com/linkrel/network-locations"
href="/acme-rest/repositories/REPO/network-locations"/>
<link rel="http://identifiers.emc.com/linkrel/relations"
href="/acme-rest/repositories/REPO/relations"/>
<link rel="http://identifiers.emc.com/linkrel/relation-types"
href="/acme-rest/repositories/REPO/relation-types"/>
<link rel="http://identifiers.emc.com/linkrel/checked-out-objects"
href="/acme-rest/repositories/REPO/checked-out-objects"/>
<link rel="http://identifiers.emc.com/linkrel/types"
href="/acme-rest/repositories/REPO/types"/>
<link rel="http://identifiers.emc.com/linkrel/dql"
hreftemplate="/acme-rest/repositories/REPO{?dql,page,items-per-page}"/>
<link rel="http://identifiers.emc.com/linkrel/search"
hreftemplate="/acme-rest/repositories/REPO/search{?q,collections,locations,facet,
inline,page,items-per-page,include-total,sort,view}"/>
<link rel="http://identifiers.emc.com/linkrel/batches"
href="/acme-rest/repositories/REPO/batches"/>
<link rel="http://identifiers.emc.com/linkrel/batch-capabilities"
href="/acme-rest/repositories/REPO/batch-capabilities"/>
</links>
...
</repository>
You may be wondering why resources that have been overridden must implement
the same URI patterns as Core resources. This is because the Core REST Link Builder
checks whether the target URI pattern is active before writing that link relation to
the resource. When the new resource URI is not implemented in the same way as the
Core resource, the target URI in the link relation of the sourcing resource is inactive,
and therefore it is not pointing to the new (overridden) resource.
To get around this limitation, you can introduce a new registry entry in YAML and
explicitly tell a resource to override another resource. At runtime, you can also make
the link relation builder understand the overriding relationship between two
resources. Then the link relation targeting the original resource can be redirected
dynamically to the new resource. This approach avoids the need for any hardcoded
URI availability checking logic.
Key Description
new The name of the overriding resource
origin The name of the resource that is overridden
In this example, two new resources (Resource B and Resource C) are incorrectly
configured to override the same resource (resource A). This configuration error
leads to an override config illegal exception when deployed.
• It is no longer in batchable-resources
• It is no longer in non-batchable-resources
The overriding resource can use the <BatchProhibiton> annotation. Here is a listing of
the impact that the <BatchProhibition> annotation has on the resource.
Documentum REST Services supports XML, JSON, and HAL media types out-of-
the-box. Documentum REST Services extensibility allows you to build application
specific REST services. Typically, resources are developed in one of XML, JSON, or
HAL but not all three. This section discusses how to turn off the media type (XML,
JSON, or HAL) that you are not using.
By default, all of XML, JSON, and HAL media types are supported:
• When media-type:xml, the only and default media type is XML. Any request
with a JSON or HAL message body will get a 415 status code, and any request
for a JSON or HAL Response will get a 406 status code. The error message is
always in XML
• When media-type:json, the only and default media type is JSON. Any request
with a XML or HAL message body will get a 415 status code, and any request for
an XML or HAL Response will get a 406 status code. The error message is always
in JSON
• When media-type:hal, the only and default media type is HAL. Any request
with a XML or JSON message body gets a 415 status code, and any request for an
XML or JSON Response will get a 406 status code. The error message is always in
HAL JSON
• When media-type:default, all of XML, JSON, and HAL are supported
Caution
The setting has no impact on resource controller implementations. It just
directs the Spring marshalling framework to choose a supported media
type when resolving an inbound or outbound HTTP message.
• @RequestMapping consumes
• @RequestMapping. produces
This example shows you how to declare all three XML, JSON, and HAL media
types:
@RequestMapping(
method = RequestMethod.POST,
produces ={
SupportedMediaTypes.APPLICATION_VND_DCTM_JSON_STRING,
SupportedMediaTypes.APPLICATION_VND_DCTM_XML_STRING,
SupportedMediaTypes.APPLICATION_VND_DCTM_HAL_STRING,
MediaType.APPLICATION_JSON_VALUE,
MediaType.APPLICATION_XML_VALUE
MediaType.APPLICATION_HAL_VALUE
},
consumes ={
SupportedMediaTypes.APPLICATION_VND_DCTM_JSON_STRING,
SupportedMediaTypes.APPLICATION_VND_DCTM_XML_STRING
SupportedMediaTypes.APPLICATION_VND_DCTM_HAL_STRING
})
@ResponseBody
@ResponseStatus(HttpStatus.CREATED)
public CabinetObject createCabinet()
This example shows you how to declare just the XML media type: Shown in
bold in the code sample is the produces attribute, which sets the supported
media type for messages that are created on the server. In this case, we have
chosen to have XML messages created and sent from the server.
There is also a consumes attribute, which defines the media type. that is
supported by your REST client. In this case, the supported media type is XML
of course.
@RequestMapping(
method = RequestMethod.POST,
produces ={
SupportedMediaTypes.APPLICATION_VND_DCTM_XML_STRING,
MediaType.APPLICATION_XML_VALUE
},
consumes ={
SupportedMediaTypes.APPLICATION_VND_DCTM_XML_STRING
})
@ResponseBody
@ResponseStatus(HttpStatus.CREATED)
public CabinetObject createCabinet()
Tip: The preferred solution is to declare that all three media types (XML, JSON,
and HAL) are supported in resource controllers, and use your YAML file to set
the specific media type supported. There is no additional development cost
when you declare your media type support in resource controllers. However,
you gain the flexibility to use your YAML file to configure specific media type
support.
You must ensure that the package where you create your custom error code
mapping files is specified in the property
rest.ext.error.code.mapping.packages within the
rest-api-runtime.properties file.
Example: rest-myresource-messages-new.properties
Additionally, the package where you create custom message files must be listed in
the rest.extension.message.packages runtime property, which is found within
the rest-api-runtime properties file.
• <gzip>
• <x-gzip>
• <deflate>
Here is a code sample that shows the properties for REST compression
configuration:
#---------------------------------------------------------+
# Compression Service Configuration |
#---------------------------------------------------------+
#
# This section contains the configuration of server Response message compression, the
# Response message includes both the xml/json message and the content binaries. The
# compression service is only triggered when the Request header Accept-Encoding matches
# any one of the following supported compression schemas:
# - gzip
# - x-gzip
# - deflate
#
# In addition to the above, the Request and Response must also match the conditions
listed
# in all of the properties shown below.
#
# This property specifies the size, in bytes, of the smallest Response that will be
# compressed. When less than the value of ${rest.compression.threshold} bytes are written
# to the Response, it is not compressed. In this case, the Response goes back to the
client
# unmodified. When the value for this property is 0, compression always begins
immediately.
# This property defaults to 10485760 bytes (10MB).
rest.compression.threshold=
#
# When specified, this property is treated as a comma-separated list of content types
# For example, application/xml,application/json
# The REST server only attempts to compress responses whose content type is compatible
# with one of the values set for this property.
# This property defaults to all media types.
rest.compression.include.content.types=
#
# This property is the same as {rest.compression.include.content.types}, but specifies a
# list of content types not to compress. By default no content type is excluded.
# However note that any content type that indicates a compressed format, such as the
# following content types are never compressed:
# - application/gzip
# - application/x-compress
# - application/deflate
# - application/x-gzip)
#
# Also note, when any value is specified in both {rest.compression.include.content.types}
# and here in this property, the specific content type is removed from the included
content
# types, which causes the Response not to be compressed.
rest.compression.exclude.content.types=
#
# When specified, this property is treated as a comma-separated list of regular
expressions
# of the types that are accepted by the regular expression pattern. The pattern must
match
# those paths that should be compressed. Anything else will not be compressed.
# This property defaults to all paths.
# "Paths" here means values returned by HttpServletRequest.getRequestURI().
# Note that the regular expression must match the file name exactly.
# For example the regular expression pattern "/static/" does not match everything
# containing the string "/static/". To do that, use ".*/static/.*" instead.
rest.compression.include.path.patterns=
#
# This property is same as {rest.compression.include.path.patterns}, but specifies a list
# of patterns that match paths that should not be compressed. Note the archived-contents
# path pattern is always excluded, specifically the value ".*/archived-contents.*" is
# always set in this property, and when any value is specified both in
# {rest.compression.include.path.patterns} and here simultaneously, the specific path is
# removed from the included paths, and the Response with that path is not compressed.
rest.compression.exclude.path.patterns=
#
# When specified, this property is treated as a comma-separated list of regular
# expressions, and only those Requests with User-Agent headers whose value matches one of
# these regular expressions is compressed.
# This property defaults to all user agents.
rest.compression.include.user.agent.patterns=
#
# Similar to {rest.compression.include.user.agent.patterns}, all requests whose User-
Agent
# header matches one of the patterns set for this property are not compressed. By default
# no user agent is excluded, but when any value is specified in
# {rest.compression.include.user.agent.patterns} and here at the same time, the specific
# user agent is removed from the list of included agents, and the Response with that user
# agent is not compressed.
rest.compression.exclude.user.agent.patterns=
#
# When specified, this property is treated as a comma-separated list of regular
# expressions. A Request with User-Agent headers whose value matches one of these regular
The following code sample shows you how to use the Content-Encoding
Header to upload document content while using a specific compression
schema:
POST /dctm-rest/repositories/REPO/objects/09000005800051ae/contents?
overwrite=true HTTP/1.1
Host: localhost:8080
Authorization: Basic ZG1hZG1pbjpwYXNzd29yZA==
Content-Encoding: gzip
Status 200 OK
Content-Encoding gzip
Content-Type application/octet-stream
Vary Accept-Encoding
ETag W/"K1cw+IwVjnDwltiMr2KGuCm8HyQzcMvm7V1KhtWWtJM="
page,items-per-
page,
include-total,
sort}
The resource supports both XML and JSON representations in conformity with Core
REST representations.
<content type="application/xml"
src="http://localhost:8080/acme-rest/repositories/REPO/alias-sets/
6600208080000105"/>
<link rel="edit" href="http://localhost:8080/acme-rest/repositories/
REPO/alias-sets/6600208080000105"/>
</entry>
...
Note: XML representation for collections must conform to the atom feed; and
JSON representation for collections must conform to EDAA.
With the alias set collection resource in your mind, it’s quite natural to think about
building single alias set resources. The single alias set resource can be embedded in
the feed representation of the alias set collection resource. The alias set resource is
designed as follows:
Just like the alias set collection, single alias set resources support both XML and
JSON representations.
xsi:type="dm_alias_set" definition="http://localhost:8080/acme-rest/
repositories/REPO/types/dm_alias_set">
<properties>
<owner_name>Administrator</owner_name>
<object_name>AdminAccess</object_name>
...
</properties>
<links>
<link rel="self" href="http://localhost:8080/acme-rest/repositories/
REPO/alias-sets/6600208080000105"/>
<link rel="author" href="http://localhost:8080/acme-rest/repositories/
REPO/users/Administrator"/>
</links>
</alias-set>
8.19.2 Quickstart
Documentum REST Services SDK provides you with a convenient way to set up
your first custom resource project: using the Maven archetype. Refer to Get Started
With the Development Kit for details. The code structure we will build for both
resources is shown as follows.
As the diagram shows, we create four modules for the project. The model module
creates the serializable alias set data model. The persistence module creates the
persistence API to get and update the alias set object using DFC. The resource
module creates the resource controllers. And the web module creates the WAR file
for the entire custom services.
The Maven archetype project contains a sample model class AliasSet for the alias set.
This is the only class added to the model module.
This query template is the only implementation class to retrieve a collection of alias
set objects. The SDK provides other APIs to facilitate the object collection retrieval.
The class definition of the AliasSetCollectionQueryTemplate is shown as follows.
/**
* A query template to get alias set collection.
*/
public class AliasSetCollectionQueryTemplate extends PagedQueryTemplate {
@Override
protected List<String> defaultFields() {
return Arrays.asList( "r_object_id", "alias_category",
"alias_name", "alias_value", "object_name", "owner_name");
}
@Override
protected String qualification() { return ""; }
@Override
protected String from() { return "dm_alias_set"; }
@Override
protected List<SortOrder> defaultSorts()
{ return Arrays.asList(new SortOrder("object_name", true)); }
@Override
protected boolean supportRepeatingAttributeQuery() {return true; }
}
The Maven archetype project defines two controllers, for the alias set collection
resource and the alias set resource, respectively. The code structure of the resource
module is shown as follows.
8.19.5.1 AliasSetCollectionController
The AliasSetCollectionController class is the definition for the alias set
collection resource.
/**
* Collection of alias sets in a repository.
*/
@Controller("acme#alias-sets")
@RequestMapping("/repositories/{repositoryName}/alias-sets")
@ResourceViewBinding(AliasSetsFeedView.class)
public class AliasSetCollectionController extends AbstractController {
@RequestMapping(
method = RequestMethod.GET,
produces = {
SupportedMediaTypes.APPLICATION_VND_DCTM_JSON_STRING,
MediaType.APPLICATION_ATOM_XML_VALUE,
MediaType.APPLICATION_JSON_VALUE,
MediaType.APPLICATION_XML_VALUE
})
@ResponseBody
@ResponseStatus(HttpStatus.OK)
public AtomFeed getAliasSets(
@PathVariable("repositoryName") final String repositoryName,
@TypedParam final CollectionParam param,
@RequestUri final UriInfo uriInfo)
throws Exception {
PagedQueryTemplate template = new AliasSetCollectionQueryTemplate()
.filter(param.getFilterQualification())
.order(param.getSortSpec().get())
.page(param.getPagingParam().getPage(),
param.getPagingParam().getItemsPerPage());
PagedDataRetriever<AliasSet> dataRetriever =
new PersistentDataRetriever<AliasSet>(
template,
param.getPagingParam().getPage(),
param.getPagingParam().getItemsPerPage(),
param.getPagingParam().isIncludeTotal(),
param.getAttributeView(),
AliasSet.class);
return getRenderedPage(
repositoryName,
dataRetriever.get(),
param.isLinks(),
param.isInline(),
uriInfo,
null);
}
}
The resource controller uses a number of Java annotations. They are mainly divided
into two categories.
Spring annotations
the same URI, but with different HTTP methods; even that you can define two
controller methods with the same URI and HTTP methods, but with different
HTTP Headers or query parameters
• @ResponseBody and @ResponseStatus - Spring annotations to automatically
resolve the model and view and HTTP status for the resource. @ResponseBody is
mandatory for custom resource development
• @PathVariable – a Spring annotation to extract variables from a path
The Documentum REST Services SDK also provides abstraction, model, and utility
classes for code reuse in custom resource development.
In summary, there aren't many lines to implement an alias set collection resource,
but each piece provides very useful information.
8.19.5.2 AliasSetController
Let’s move on to the controller for the alias set resource. The class definition is
shown as follows.
/**
* An alias set.
*/
@Controller("acme#alias-set")
@RequestMapping("/repositories/{repositoryName}/alias-sets/{aliasSetId}")
@ResourceViewBinding(AliasSetView.class, queryTypes="dm_alias_type")
public class AliasSetController extends AbstractController {
@Autowired
private SysObjectManager sysObjectManager;
@RequestMapping(
value = ALIAS_SET_URI_PATTERN,
method = RequestMethod.GET, produces = {
SupportedMediaTypes.APPLICATION_VND_DCTM_JSON_STRING,
MediaType.APPLICATION_ATOM_XML_VALUE,
MediaType.APPLICATION_JSON_VALUE,
MediaType.APPLICATION_XML_VALUE
})
@ResponseBody
@ResponseStatus(HttpStatus.OK)
public AliasSet getAliasSet(
@PathVariable("repositoryName") final String repositoryName,
@PathVariable("aliasSetId") final String aliasSetId,
@TypedParam final SingleParam param,
@RequestUri final UriInfo uriInfo)
throws Exception {
AliasSet aliasSet = sysObjectManager.getObjectByQualification(
String.format("dm_alias_set where r_object_id='%s'", aliasSetId),
param.getAttributeView(),
AliasSet.class
);
return getRenderedObject(repositoryName, aliasSet, param.isLinks(),
uriInfo, null);
}
}
You must ensure that the package where you created the ExtensionContextConfig
class is specified in the rest.context.config.location property in the rest-api-
runtime.properties file.
expression="com.emc.documentum.rest.context.ComponentScanExcludeFilter"/>
</context:component-scan>
8.19.7.1 AliasSetsFeedView
The AliasSetsFeedView class defines the atom feed attributes and links for the alias
set collection resource.
@Override
public String feedTitle() { return "Alias Sets"; }
@Override
public Date feedUpdated() { return new Date(); }
}
The feed view class must extend FeedableView<T> and implement its own feed
attributes.
FeedableView<T> - the Core REST feed view abstraction. It provides built-in link
relations for an atom feed. By implementing this view, the alias set collection
resource obtains the built-in self link and pagination links. There are also default
implementations for several atom feed attributes, like atom authors, atom ID, and so
on.
8.19.7.2 AliasSetView
The AliasSetView class defines the atom entry attributes and links for the alias set.
@Override
public String entryTitle() { return (String) serializableData.
getMandatoryAttribute("object_name"); }
@Override
public String entrySummary() { return (String) serializableData.
getMandatoryAttribute("object_name"); }
@Override
public Date entryUpdated() { return new Date(); }
@Override
public Date entryPublished() { return new Date(); }
@Override
public List<AtomAuthor> entryAuthors() {
String ownerLink = ResourceUriBuilder
.onResource("user")
.pathVariables((String)serializableData.getAttributeByName("owner_name"));
return Arrays.asList(new AtomAuthor(owner, owerLink, null));
}
@Override
public AliasSet entryContent() { return data(); }
@Override
public String entrySrc() { return canonicalResourceUri(boolean validate); }
@Override
public void customize() {
String ownerLink = ResourceUriBuilder
.onResource("user")
.pathVariables((String)serializableData.getAttributeByName("owner_name"));
}
@Override
public String canonicalResourceUri(boolean validate) {
return getUriFactory(validate).buildUriByTemplateName(
"X_ALIAS_SET_URI_TEMPLATE",
Collections.singletonMap("aliasSetId", serializableData.getId()));
}
}
The domain model view must extend EntryableView<T> and implement its own
entry attributes and resource links. In this sample class, AliasSetView extends
PersistentDataView which provides the default implementation for making links.
When an object is accessible through multiple resource URIs, the canonical resource
URI represents the primary resource URI for this object. The method
canonicalResourceUri returns the canonical resource URI dynamically with a
validation option.
• When validate is true and the corresponding resource for the URI is inactive,
the returned URI is {@link com.emc.documentum.rest.http.
UriFactory#INACTIVE_URL}
• When validate is false, the returned URI is a static URI defined by this
method.
Refer to the section Documentum REST Services MVC for more information about
the Documentum REST Services MVC programming pattern.
application/vnd.
emc.documentum
+json
@RequestMapping(method = RequestMethod.POST,
produces = {
SupportedMediaTypes.APPLICATION_VND_DCTM_JSON_STRING,
SupportedMediaTypes.APPLICATION_VND_DCTM_XML_STRING,
MediaType.APPLICATION_JSON_VALUE,
MediaType.APPLICATION_XML_VALUE
})
@ResponseBody
@ResponseStatus(HttpStatus.CREATED)
@ResourceViewBinding(AliasSetView.class)
public AliasSet createAliasSet(
@PathVariable("repositoryName") final String repositoryName,
@RequestBody final AliasSet aliasSet,
@RequestUri final UriInfo uriInfo)
throws DfException {
AliasSet createdRelation = sysObjectManager.
createObject(aliasSet, false, AttributeView.ALL);
Map<String, Object> others = new HashMap<String, Object>();
others.put(ViewParams.POST_FROM_COLLECTION, true);
return getRenderedObject(repositoryName, createdRelation,
true, uriInfo, others);
}
@Autowired
private SysObjectManager sysObjectManager;
}
Notes
DeleteVersionPolicy.ALL);
}
}
queryTypes makes the alias set resource linkable from DQL query result. Here is an
example.
<?xml version='1.0' encoding='UTF-8'?>
<feed xmlns="http://www.w3.org/2005/Atom"
xmlns:dm="http://identifiers.emc.com/vocab/documentum"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
...
<link rel="self" href="http://localhost:8080/acme-rest/repositories/
REPO?dql=select%20*%20from%20dm_alias_set"/>
<entry>
<id>http://localhost:8080/acme-rest/repositories/REPO?
dql=select%20*%20from%20dm_alias_set&index=0</id>
...
<content>
<dm:query-result definition="http://localhost:8080/acme-rest/
repositories/REPO/types/dm_alias_set">
<dm:properties>
<dm:r_object_id>6600208080000100</dm:r_object_id>
<dm:owner_name>Administrator</dm:owner_name>
<dm:object_name>Smart Container</dm:object_name>
<dm:object_description></dm:object_description>
</dm:properties>
</dm:query-result>
</content>
<link rel="edit" href="http://localhost:8080/acme-rest/repositories/
REPO/alias-sets/6600208080000100"/>
</entry>
...
For the second one, as we have implemented the Location Header on alias set
creation operation, the POST on the alias set collection resource has been able to
return the link for the newly created alias set resource. So what you actually need is
to customize the link relations for the Core repository resource. To do this, simply
add a YAML file under the right path and then modify the YAML file as shown:
The section Adding Links to Core Resources has all the details of this function. Here
is the sample.
By doing this, the repository resource now has one more link relation that points to
the alias set collection resource.
<repository xmlns="http://identifiers.emc.com/vocab/documentum"
xmlns:dm="http://identifiers.emc.com/vocab/documentum"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<id>8320</id>
<name>REPO</name>
<description/>
<server>
<name>REPO</name>
<host>CS71P01</host>
<version xml:space="preserve">7.1.0010.0158Win64.SQLServer</version>
<docbroker>CS71P01</docbroker>
</server>
<links>
<link rel="self" href="http://localhost:8080/acme-rest/repositories/REPO"/>
<link rel="http://identifiers.emc.com/linkrel/alias"
href="http://localhost:8080/acme-rest/repositories/REPO/alias-sets"/>
...
</links>
</repository>
This tutorial does not demonstrate how to manage the batch property of a custom
resource as we want the Alias Set and Alias Set collection resources to stay
batchable. You can find more details on making them non-batchable from Resource:
Deciding Whether to be Batchable.
Here is the WAR overlay plugin configuration sample in the web module:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.4</version>
<configuration>
<overlays>
<overlay>
<groupId>com.emc.documentum.rest</groupId>
<artifactId>documentum-rest-web</artifactId>
<excludes />
</overlay>
</overlays>
</configuration>
</plugin>
</plugins>
</build>
8.19.13 Troubleshooting
During development, you can enable the DEBUG level log4j logging for the package
com.emc.documentum.rest. Resource controller and view information about both
core and custom resources will be printed.
The advantage for this solution is that when multiple BOCS servers are deployed in
the environment, contents of sysobjects can be uploaded to the local geographic
location in the organization. This accelerates the content upload in wide area
networks where the bandwidth is critical.
8.20.1 Link relation for ACS and BOCS content upload URLs
The ACS and BOCS content upload URL is provided to REST clients as a new link
relation http://identifiers.emc.com/linkrel/distributed-upload on a Sysobject
Resource, at the time when the Sysobject is newly created or checked out. The title of
the link relation depends on the location pointed to by the content upload URL.
8.20.2 Resources
These 3 existing resource methods that can produce the ACS and BOCS content
upload URLs for sysobjects.
Constraints to be noted for the ACS and BOCS content upload URLs.
1. REST Client creates a new sysobject by making a POST request to Folder Child
Documents resource or Folder Child Objects resource, in which it requires to get
the ACS or BOCS content upload URL.
POST /dctm-rest/repositories/REPO/folders/0c00000c80000105?require-dc-
write=true&format=text
&content-length=13&network-location=Toronto&async=true HTTP/1.1
Host: localhost:8080
Content-Type: application/vnd.emc.documentum+json
Authorization: Basic ZG1hZG1pbjpwYXNzd29yZA==
{
"properties":{
"object_name": "demodoc.txt"
}
}
2. The newly created sysobject resource is returned with the ACS or BOCS content
upload URL in the link relations. Its server object is locked by the user.
3. REST Client makes another POST request on the content upload URL to complete
the upload.
POST http://<BOCS-SERVER>:9080/ACS/servlet/BOCS?command=write&XXX HTTP/1.1
Content-Type: plain/text
Hello, world!
4. When the content upload is completed, REST Client gets a 302 Moved
Temporarily Response from the ACS or BOCS server, where there is
a Location Header telling the Sysobject resource URL. The server object is
also unlocked upon the success of the content upload.
HTTP/1.1 302 Moved Temporarily
Location: http://localhost:8080/dctm-rest/repositories/REPO/objects/0900000d10000983
1. REST Client creates a new sysobject by making a PUT request to Lock resource, in
which it requires to get the ACS or BOCS content upload URL.
PUT /dctm-rest/repositories/REPO/objects/0900000d10000983/lock?require-dc-
write=true&
format=text&content-length=13&network-location=Toronto&async=true HTTP/1.1
Host: localhost:8080
Authorization: Basic ZG1hZG1pbjpwYXNzd29yZA==
2. The checked out sysobject resource is returned with the ACS or BOCS content
upload URL in the link relations.
Status code: 200 OK
Content-Type: application/vnd.emc.documentum+json;charset=UTF-8
{
"properties":
{
"r_object_id": "0900000d10000983",
"r_lock_owner": "dmadmin",
"r_content_size": 0,
},
"links": [
{
"rel": "self",
"href": "http://localhost:8080/dctm-rest/repositories/REPO/objects/
0900000d10000983/lock"
}
,
...,
{
"rel":"http://identifiers.emc.com/linkrel/distributed-upload",
"title":"BOCS-Toronto",
"href":"http://<BOCS-SERVER>:9080/ACS/servlet/BOCS?
command=write&XXX"
}
]
}
3. REST Client makes another POST request on the content upload URL to complete
the upload.
POST http://<BOCS-SERVER>:9080/ACS/servlet/BOCS?command=write&XXX HTTP/1.1
Content-Type: plain/text
Hello, world!
4. When the content upload is completed, REST Client gets a 302 Moved
Temporarily Response from the ACS or BOCS server, where there is
a Location Header telling the Sysobject resource URL. The server object is
also unlocked upon the success of the content upload.
HTTP/1.1 302 Moved Temporarily
Location: http://localhost:8080/dctm-rest/repositories/REPO/objects/0900000d10000983
• Support client cache: At times, the REST Server does not return the requested
resource to client endpoint again if the identical requested resource is cached
locally and not updated during the last acquisition. Otherwise, the REST Server
handles the client's request as normal. With conditional request mechanism, it
makes sense to keep client's local cache not stale
• Avoid lost update: In this scenario, multiple clients get an identical resource
simultaneously from REST Server. So, when one of the client updates the
resource successfully, the subsequent updates from others intended for this
resource would fail. With conditional request mechanism, one client, without any
acknowledgement that the resource has been modified, would not override
others’ update, avoiding lost update. For more information about conditional
Requests, see RFC 7232.
1. REST Server returns the requested resource with an ETag in the Response for a
GET request
2. Client specifies the HTTP Header, If-None-Match, in a GET request with the
previous obtained ETag value
3. With the Request, the REST Server determines whether the ETag value in the
If-None-Match Header matches the current ETag of the instance of the
requested resource.
• When it’s not matched: 304 (Not Modified) status code is returned without
sending back the resource
• Matched: Send back the resource as normal with a new value in the ETag
Header
1. Multiple clients. Let’s consider client A and B, get the identical resource with the
same ETag value in respective responses at the same time
2. Client A updates the resource successfully because the ETag in the If-Match
HTTP Header of the Request matches. The REST Server generates a new ETag
for the updated instance of the resource and then client A gets this updated ETag
value
3. Now the REST Server receives an update Request from client B with the
previously obtained ETag value specified in the If-Match Header. The REST
server determines that this ETag value is not matched, therefore it fails to update
the resource and returns status code 412 (PreCondition Failed)
• EnableConditionalRequest.java
/**
* Annotation that annotates a controller. Once annotated by
EnableConditionalRequest,
* the controller would handle a HTTP request, mainly GET, PUT or POST, as
conditional
* request.
*
* @see com.emc.documentum.rest.context.ConditionalRequestBodyAdvice
* @see com.emc.documentum.rest.context.ConditionalResponseBodyAdvice
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EnableConditionalRequest {
}
• Digestible.java
/**
* Allows a persistent object to calculate its own digest. In the meanwhile, Enables
to
*update a persistent object conditionally based on if the {@code request digest}is
matched
* with another digest, which is commonly* the current digest of the identical
persistent*
* object with newest version.
* <p>
* {@code request digest}, is obtained from a request with certain mechanism and
stored
* temporarily in an object before real update. By comparing with current digest of
* corresponding persistent object with newest version, conditional update comes
* into realization.
*/
public interface Digestible {
/**
* Calculates digest for a persistent object itself.
* <p>It's up to the persistent model which implements this interface to
determine how
* to calculate
* a distinct digest for a model instance, i.e., making the digest based on
distinct pro * perties.</p>
*
* @return the digest
*/
String calculatesDigest();
/**
* Sets the Request digest.
*
* @param reqDigest
*/
void setReqDigest(String reqDigest);
/**
* Get the Request digest.
*
* @return the Request digest
*/
String getReqDigest();
/**
8.21.4 Examples
Assuming a Model View Controller approach:
return false;
}
}
You can use the Documentum REST API, augmented by the features of the
AppWorks Gateway, to perform the following tasks.
You can:
The hosted Documentum REST Services API has been specifically packaged to allow
hosting by the OpenText AppWorks Gateway. The following figure illustrates the
interactions between integration components.
Note: To get shorter URLs after deployment, you can rename the artifact to
dctm-rest_20.4.zip before you deploy it.
The following classes have been added to the REST API SDK to support the
consumption of AppWorks SDK in custom controllers. These controllers extend
from a default REST API provided controller class called AWSDKControllerBase.
• AwSdkBean
A Java bean that is included in the controller classes to provide access to
AppWorks SDK clients and provide the utility functions required to send emails,
notifications, etc.
• AwSdkControllerBase
A reference implementation of a controller that you can extend to handle URLs.
It is auto wired to provide the AwSdkBean Java bean.
You can edit these settings from the AppWorks Gateway's UI after you deploy the
DCTM-REST service in the Gateway server. This enables you to edit the properties
using the GUI and to avoid modifying the properties files manually. To access this
feature, login to AppWorks Gateway Admin UI, select Services, select the gear icon
under DCTM REST services and select the Settings tab.
A service restarted is needed to see the effect of the changes made. The following
commonly used properties are exposed using the UI:
/**
* @return Instance of the Runtimes registered with the Gateway
*/
RuntimesClient getKnownRuntimes()
/**
* @return Retrieve trusted provider (API) that wants to use a limited subset of the
Gateways APIs
*/
TrustedProviderClient getTrusterProviders()
/**
* @return Instance of Gateway's settings client to create, update, and be notified of
Gateway wide parameters
*/
SettingsClient getSettingsClient()
/**
* @return MailClient to enable sending emails
*/
MailClient getMailClient()
/**
* @return NotificationClient to enable sending notifications to users or apps
*/
NotificationsClient getNotificationsClient()
/**
* @return Instance of Gateway's service client to create a complete deployment and
register connectors
*/
ServiceClient getServiceClient()
Refer to the sample implementation found in the DCTM REST 20.4 SDK. The sample
project is located in the .../samples/resource-appworks-samples folder.
8.22.6 Resources
OpenText AppWorks Developer (https://developer.opentext.com/awd/intro).
1. Add new static resource files into your REST application. An example of this is
adding custom CSS style sheets or JavaScript files
• Your particular REST service may have a built-in login user on the server
side and you want to skip the usual authentication schema that is already
configured
9.1.1 Implementation
You must create a custom Java controller that uses the @AnonymousAccess
annotation to get the functionality that you want. There are two ways to set
anonymous access in your custom REST service:
Note: The two approaches shown here do not need to be implemented at the
same time for one resource. They can take effect individually.
Typically, all REST service functionality is implemented into one controller that uses
the @AnonymousAccess annotation. Your controller can provide anonymous access
functionality to your REST application. The @AnonymousAccess annotation allows:
• contextInitializerClass
This is an implementation of the contextInitializerClass interface. This
interface defines the customized details for your context, such as username and
password
• contextCleanerClass
This field extends from DefaultRepositoryContextCleaner, and is used to
clean up any security related context
• For example, you can use the initialize() method during initialization to set
the repository name, login name and password in RepositoryContextHolder.
The authType can be set to <AuthType.PASSWORD>, which indicates that user
authentication is done using a login name and password
• The default clean() method is used to clean the context in
RepositoryContextHolder. When you have more complex logic for cleaning
context, you can override this method with your own custom code to perform
the cleaning.
In Documentum REST Services 7.3 and later, an XML configuration file is exposed to
allow you to inject custom (non-security) filters before or after
<SpringSecurityFilterChain> is created.
Note: Modifying the web.xml directly in the WAR file is not recommended
because the filter sequence cannot be guaranteed.
Here's a code sample that shows you how to edit the <dctm-rest.war>\WEB-INF\
classes\META-INF\spring\extension\rest-api-custom-filters.xml file for
customizing servlet filters:
<!-- allow users to add custom filters into the REST servlet; all filters are
mapped to the root context '/*' -->
<bean id="restFilterFactory" class="com.emc.documentum.rest.filter.FilterFactory">
<property name="customBeforeSecurityFilters">
<list>
<!-- an ordered list of filters added before springSecurityFilterChain;
empty by default -->
</list>
</property>
<property name="customAfterSecurityFilters">
<list>
<!-- an ordered list of filters added after springSecurityFilterChain;
empty by default -->
</list>
</property>
</bean>
</beans>
In a custom REST application, you can inject filters by updating the rest-api-
custom-filters.xml file. The REST SDK provides a sample that shows you how to
do this.
<!-- allow users to add custom filters into the REST servlet; all filters are
mapped to the root context '/*' -->
<bean id="restFilterFactory" class="com.emc.documentum.rest.filter.FilterFactory">
<property name="customBeforeSecurityFilters">
<list>
<!-- an ordered list of filters added before springSecurityFilterChain;
empty by default -->
<bean
class="com.emc.documentum.rest.sample.filter.RequestCountingFilter"/>
</list>
</property>
<property name="customAfterSecurityFilters">
<list>
<!-- an ordered list of filters added after springSecurityFilterChain;
empty by default -->
<bean class="com.emc.documentum.rest.sample.filter
.RequestCounterCountingFilter"/>
</list>
</property>
</bean>
</beans>
↓
RepositoryNamingFilter
↓
<customBeforeSecurityFilters ..>
↓
SpringSecurityFilterChain
↓
<customAfterSecurityFilters ..>
↓
HiddenHttpMethodFilter
↓
ApplicationFilter
9.3.1 Overview
Making Documentum REST Services the primary authentication handler is useful
when there are several Documentum REST Services resources, that are able to
authenticate requests, deployed on the AppWorks gateway. In this case, requests are
authenticated with the OpenText Directory Service (OTDS) using Documentum
Server.
Making Documentum REST Services the primary authentication handler has the
following advantages:
• You can eliminate the need for any user synchronization between AppWorks
and Documentum Server on OTDS. This is because Documentum REST Services
that are deployed on AppWorks no longer depend on the AppWorks Gateway
for authentication. Among other advantages, you can avoid searching for
Gateway users that may not be present in Documentum Server.
• You can combine multiple existing Documentum REST Services resources while
using Documentum Server as a single resource for all authentication requests.
• Upon a successful login, Documentum REST Services can obtain a user session
from Documentum Server.
• basic: Supports HTTP basic authentication for Documentum Server inline users
and LDAP users synced to Documentum Server.
• otds_password: Supports using a password for OTDS integration with
Documentum Server.
• otds_token: Supports using a token for OTDS integration with Documentum
Server.
The AwAuthRequestHandler class is the main REST handler class that processes
authentication requests and forwards them to the REST authentication layer for
validation. When validation succeeds, a success AuthHandlerResult instance with
user information is returned. This interaction is shown in the following sequence
diagram:
For more information, see “Generic servlet filter customization” on page 387 in
Documentum REST Services to understand how the Spring Security Filter Chain
takes place within the entire servlet filter chain.
Specific to the security filter chain itself, the Spring Security framework puts
multiple HTTP security configurations into a chain called
<SpringSecurityFilterChain>. The high level flow of the security filter chain in Core
REST is as shown:
For any request that is sent to the REST server, the following applies:
• When the Request URL maps to an anonymous access URL pattern, the Request
is bypassed by the resource controller without security handling. Some static
resources, such as js and css files, follow this pattern. The Core REST
authentication framework exposes an extension for you to configure anonymous
access resources or URL patterns
For more information, see “Anonymous access” on page 385
• The out-of-the-box authentication schemes or custom authentications manage the
Request authentication for the resource URL pattern /repositories/** by default
This URL pattern can be configured globally in the rest-api-runtime.
properties file using the <rest.security.auth.root.url> property
• For other cases not shown above, the Request is rejected with a <403> status code
During the deployment phase, usually you are only required to configure REST
runtime properties in order to enable a specific authentication scheme. For example
rest.security.auth.mode=basic
Depending on the authentication type used, the credentials are different for each
logged in user. By default, the following authentication types are supported:
2. Get it from
com.emc.documentum.rest.security.provider.AbstractAuthProvider.
You can get the session manager instance by extending from the abstract
provider
In most cases, you do not need to customize DFC session managers because the
default implementation already has support for most authentication types that are
also supported by Documentum Server. A custom authentication scheme should
first examine the existing authentication types to see whether any of them can be
used for the custom authentication scheme. You can use the CUSTOM authentication
type when none of the default implementations meet your requirements. The default
implementation creates an empty DFC session manager for the authentication type
CUSTOM. You must set up custom identities for the obtained session manager.
// This is a dummy password plugin sample -- set a valid password plugin which is
supported by DFC login.setPassword("DM_PLUGIN=dm_custom/" + "5XHR6fTZ==");
dfSessionManager.setIdentity("ACME", login);
}
Developers often write their own authentication providers, because the provider
must call the Core REST session manager to authenticate the user. Spring Security
provides an interface called AuthenticationProvider, which is used to implement
the provider. Documentum REST Services additionally provides two abstract classes
for extension, the AbstractAuthProvider class and the
AbstractClientTokenAuthProvider class.
}
}
@Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
...
try {
...
AbstractAuthenticationToken authorizedToken = new
UsernamePasswordAuthenticationToken(
authentication.getPrincipal(),
authentication.getCredentials(),
grantedAuthorities);
if (clientTokenSupport) {
authorizedToken = produceRestTicketToken(authorizedToken, false, session);
}
authorizedToken.setDetails(authentication.getDetails());
if (!authorizedToken.isAuthenticated()) {
authorizedToken.setAuthenticated(true);
}
return authorizedToken;
} catch (DfException e) {
...
} finally {
...
}
}
}
You can extend from an existing third party security filter to create your custom
authentication filter, or you can implement your custom filter by using an abstract
class or interface such as javax.servlet.Filter. Here's a diagram of the filter
UML:
In the previous section we said that the authentication provider calls the DFC
session manager to login. The session is actually released immediately after the login
success. It is important that your filter implementation have the logic to store and
clear the security context. The security context is used by the resource controller to
obtain the DFC session manager in the following cases:
• When an authentication succeeds, the authentication filter must store the user
credential into RepositoryContextHolder
Here's a code sample that shows you how to implement a custom security
filter:
// Authenticate the servlet request
@Override
public void doFilter(ServletRequest request, ServletResponse Response, FilterChain chain)
throws IOException, ServletException {
if (!requireHandle(request)) {
chain.doFilter(request, Response);
return;
}
try {
AuthenticationToken authToken = parseAuthToken(request);
Authentication authResult = authenticationManager.authenticate(authToken);
SecurityContextHolder.getContext().setAuthentication(authResult);
onSuccessfulAuthentication(request, Response, authResult);
}
catch (AuthenticationException failed) {
SecurityContextHolder.clearContext();
if (ignoreFailure) {
chain.doFilter(request, Response);
}
else {
authenticationEntryPoint.commence(request, Response, failed);
}
return;
}
chain.doFilter(request, Response);
}
// success handler
protected void onSuccessfulAuthentication(HttpServletRequest request,
HttpServletResponse Response,
Authentication authResult) throws IOException {
RepositoryContextHolder.setLoginName(authResult.getPrincipal().toString());
RepositoryContextHolder.setPassword(authResult.getCredentials());
RepositoryContextHolder.setAuthType(AuthType.PASSWORD);
}
// failure handler
protected void onUnsuccessfulAuthentication(HttpServletRequest request,
HttpServletResponse Response,
AuthenticationException failed) throws IOException {
RepositoryContextHolder.clearUserCredentials();
}
Authentication errors, such as HTTP status 401 for error or status 302 for redirecting,
must be returned to the client in the correct manner. Using an entry point helps to
implement this logic.
An entry point must at least implement the Commence method, and then use that
method to set the Response Header and status, as well as handle the Response body.
Here is a code sample that shows you a custom authentication entry point:
@Override
public void commence(HttpServletRequest request, HttpServletResponse Response,
AuthenticationException authException)
throws IOException, ServletException {
Response.setStatus(HttpStatus.UNAUTHORIZED);
Response.addHeader(ChallengeConfig.WWW_AUTHENTICATE, getChallenge(request,
AuthSchemes.BASIC.value())); exceptionHandlerExceptionResolver
.resolveException(request, Response, null, authException);
}
After receiving the Response, the REST client can access Documentum REST
Services by using the Client Token cookie for subsequent requests. This approach
avoids having to go through the custom authentication scheme to authenticate the
REST client every time. When a custom authentication is used with a Documentum
Client Token cookie, a log out must be set up. This can be achieved by putting Client
Token filters and a custom authentication filter into an ordered filter chain. Below
are the instructions for the filter sequence:
others
ClientTokenPreAuthFilter
ClientTokenPostAuthFilter
others
In this section, we introduce the optimized Java configuration for developing new
authentication schemes.
9.4.3.4.1.1 AbstractWebSecurity
Note: There are two annotations applied to the custom web security:
@AuthSchemeProfile and @Order.
@Override
protected AuthenticationProvider[] authenticationProviders() {
return new AuthenticationProvider[] { customAuthProvider() };
}
@Override
protected AuthenticationEntryPoint entryPoint() {
return customAuthEntryPoint();
}
@Override
protected void configureSecurityFilters(HttpSecurity http) throws Exception {
http.addFilterBefore(customAuthFilter(), BasicAuthenticationFilter.class);
}
@Bean
public CustomAuthFilter customAuthFilter() {
return new CustomAuthFilter(authenticationManagerBean());
}
@Bean
public CustomAuthProvider customAuthProvider() {
return CustomAuthProvider();
}
@Bean
public CustomAuthEntryPoint customAuthEntryPoint() {
return CustomAuthEntryPoint();
}
}
9.4.3.4.1.2 @AuthSchemeProfile
9.4.3.4.1.3 @Order
For client token integration, a web security implementation must create multiple
inner classes. One of those inner classes is used to configure the HTTP logout
security. Here is a code sample that shows you how to do this:
Example 9-12: Custom web security sample with client token integration
/**
* Custom + client token authentication.
*/
@AuthSchemeProfile(schemes = CustomCtCtWebSecurity.MODE)
public class CustomCtCtWebSecurity {
@AuthSchemeProfile(schemes = CustomCtCtWebSecurity.MODE)
@Order(1)
public static class CtLogoutWebSecurity extends AbstractLogoutWebSecurity {
}
@AuthSchemeProfile(schemes = CustomCtCtWebSecurity.MODE)
@Order(2)
public static class CustomAndCtWebSecurity extends AbstractWebSecurity {
@Autowired
protected CtAuthBeans ctBeans;
@Override
protected AuthenticationProvider[] authenticationProviders() {
return new AuthenticationProvider[] {
customAuthProviderWithClientToken(),
ctBeans.clientTokenAuthProvider()
};
}
@Override
protected AuthenticationEntryPoint entryPoint() {
return customEntryPoint();
}
@Override
@Bean
public CustomAuthFilter customAuthFilter() {
return new CustomAuthFilter(authenticationManagerBean());
}
@Bean
public CustomAuthProvider customAuthProvider() {
return CustomAuthProvider();
}
@Bean
public CustomAuthEntryPoint customAuthEntryPoint() {
return CustomAuthEntryPoint();
}
@Bean
public ClientTokenPreAuthFilter clientTokenPreAuthFilter() {
return ctBeans.clientTokenPreAuthFilter(authenticationManagerBean());
}
@Bean
public ClientTokenPostAuthFilter clientTokenPostAuthFilter() {
return ctBeans.clientTokenPostAuthFilter();
}
}
}
Note: Core REST API CtAuthBeans provides client token authentication beans
for you to import into your custom web security configuration.
Caution
For client token integration, when the custom authentication filter wants to
send a redirect URI to the Response after a successful authentication, it
must not call the Response redirect directly. Instead, it must pass the
redirected URL to the authenticated token and let the next filter, the
Here is a code sample that shows you how to set a client token cookie:
// Delegate to Core ClientTokenPostAuthFilter to do redirect
ClientTokenAuthToken authenticated = (ClientTokenAuthToken)
getAuthenticationManager().authenticate(token);
if (authenticated.isAuthenticated()) {
authenticated.setRedirectUri(redirectUrl);
SecurityContextHolder.getContext().setAuthentication(authenticated);
}
/**
* A static place holder to load properties from the file 'rest-api-
runtime.properties'.
*
* @return placeholder configurer
*/
@Bean
public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
/**
* Property value for 'rest.security.custom.key'. Defaults to empty.
*/
@Value("${rest.security.custom.key:}")
public String key;
/**
* Property value for 'rest.security.custom.max_age'. Defaults to 3600.
*/
@Value("${rest.security.custom.max_age:3600}")
public Integer maxAge;
}
In the custom REST war deployment, you can enable the custom authentication
scheme and configure each parameter with the rest-api-runtime.properties file.
For XML configuration, you write the XML file to configure HTTP security for
Documentum custom authentication. The XML file must be packaged with the jar
file. The content of the XML configuration is a Spring <http> configuration and
relevant bean definitions.
For more information on how to configure a custom filter, see the Spring Security
Reference called Adding in Your Own Filters.
Here is a code sample that shows you the <http> configuration for a custom
authentication sample that is the same as the Java configuration
CustomWebSecurity. It is assumed that the custom file name is rest-api-ct-
custom-security.xml.
<beans:value>classpath:rest-api-runtime.properties</beans:value>
</beans:list>
</beans:property>
</beans:bean>
Here is an XML code sample of the <http> configuration for the custom
authentication that is the same as the Java configuration
CustomCtWebSecurity.
<!-- import properties -->
<beans:bean id="propertyConfigurer" class="org.springframework.beans.factory.config.
PropertyPlaceholderConfigurer">
<beans:property name="locations">
<beans:list>
<beans:value>classpath:rest-api-runtime.properties</beans:value>
</beans:list>
</beans:property>
</beans:bean>
<import resource="classpath*:/META-INF/spring/template/rest-api-ct-security"/>
<csrf disabled="true"/>
</http>
<!-- filter definition: customized Client Token sign out filter -->
<beans:bean name="dctmClientTokenSignOutFilter"
class="com.emc.documentum.rest.security.filter.ClientTokenSignOutFilter">
<beans:constructor-arg index="0" name="logoutSuccessUrl"
value="${rest.security.logout.success.url}"/>
</beans:bean>
Note: The rest-api-ct-security.xml file is imported into the XML file. The
rest-api-ct-security.xml file provides beans for client token
authentication, and it is the same as the CtAuthBeans Java configuration class
To register your custom authentication configuration XML file into the REST
services, you must update the ${war}/META-INF/spring/extension/rest-api-
custom-security.xml file.
<beans:beans>
<!-- ===================================================================== -->
<!-- CAUTION: XML CONFIGURATION IS DISCOURAGED.
USE JAVA CONFIGURATION INSTEAD. -->
<!-- ===================================================================== -->
<!-- Legacy XML configuration for adding custom authentication beans -->
<!-- Adds a custom authentication XML configuration in two steps: -->
Similar to the Java configuration, in the custom REST war deployment, you can
enable the custom authentication scheme and configure each parameters through
rest-api-runtime.properties.
9.4.3.7 Samples
The Documentum REST Services SDK provides two samples that show you how to
build custom authentications. One sample is for a custom login form with a client
token, the other sample is for an OAuth2 login.
Refer to the Documentum REST Services SDK for the sample code.
For more information, see Get Started With the Development Kit.
The project structure for the extended authentication scheme is shown here. In the
following sample we give you the Java configuration, and we name the new
authentication scheme ct-custom.
For XML configuration samples, see the Documentum REST Services SDK.
acme-rest-custom-security
This is a new Maven sub module that is added to the Archetype project. It includes
all the Java code and configurations for the custom authentication scheme. The build
output of the module is a jar file.
/**
* Default custom security runtime properties values.
* Properties set in 'rest-api-runtime.properties' will override the default.
*/
@Configuration
@PropertySource("classpath:rest-api-runtime.properties")
public class CustomRuntime {
/**
* A static place holder to load properties from the file 'rest-api-
runtime.properties'.
*
* @return placeholder configurer
*/
@Bean
public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
/**
* Property value for 'rest.security.custom.key'. Defaults to empty.
*/
@Value("${rest.security.custom.key:}")
public String key;
}
CustomWebSecurity.java
CustomEntryPoint.java
Follow the instructions in “Authentication Entry Point” on page 402 to write the
CustomEntryPoint code. HTTP status 401 and 302 are the most often used codes to
indicate an authentication failure.
• According to RFC 7235, when an HTTP status 401 is used, a response Header
WWW-Authenticate <challenge> should also be returned.
The authentication exception must be transformed into an XML or JSON error.
You can access Spring resolver org.springframework.web.servlet.mvc.
method.annotation.ExceptionHandlerExceptionResolver into the
CustomEntryPoint class to resolve the authentication exception in the servlet
Response.
For more information, see the sample code at “Authentication Entry Point”
on page 402.
• When an HTTP status 302 is used to indicate a user login failure, the user must
be redirected to a login page or the other authentication service. Here is a code
sample that shows HTTP redirection:
CustomAuthFilter.java
CustomClientTokenAuthProvider.java
AuthType.PASSWORD);
session = getSession(sm, repositoryId);
Collection<GrantedAuthority>
pom.xml (custom-security)
The custom authentication scheme module must add dependencies on both Core
REST Security and Spring Security. Here is a code sample that shows you the
minimal dependencies.
<dependency>
<groupId>com.emc.documentum.rest</groupId>
<artifactId>documentum-rest-config</artifactId>
</dependency>
<dependency>
<groupId>com.emc.documentum.rest</groupId>
<artifactId>documentum-rest-security-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<scope>provided</scope>
</dependency>
acme-rest-web
It is the existing Maven sub module in the Archetype project. Its purpose is to
overwrite the WAR packaging and settings in the extended REST Services. So after
resources files are put into the module location, the files overwrite the default files in
the same location within the WAR file.
rest-api-runtime.properties
Follow the instructions in “Configure runtime properties” on page 409 to set the
runtime properties for the custom authentication scheme. During the build of the
web module, this file overwrites the default (empty) REST Runtime Properties file in
the WAR file.
pom.xml (web)
The web module must adds dependency for the module acme-rest-custom-
security and the custom authentication jar file packages everything into the WAR
file. Here is a code sample that shows you the pom file:
<overlay>
<groupId>com.emc.documentum.rest</groupId>
<artifactId>documentum-rest-web</artifactId>
<excludes />
</overlay>
</overlays>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>com.acme.rest</groupId>
<artifactId>acme-rest-custom-security</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.emc.documentum.rest</groupId>
<artifactId>documentum-rest-web</artifactId>
<version>${com.emc.documentum.rest.version}</version>
<type>war</type>
<scope>runtime</scope>
</dependency>
<!-- other new dependencies -->
</dependencies>
</project>
pom.xml (root)
This section contains configurations for advanced security configuration items and
features, such as Default Security Headers, Cross Site Request Forgery (CSRF) protection
with Client token, Request Sanitization, and Cross-Origin Resource Sharing or (CORS).
Specifies whether to
include sub domains
for the Strict-
Transport-Security
header. When true,
sub domains should be
considered HSTS
Hosts.
For more
information, see
IETF Tools website
for defaults to true.
rest.security.headers
.hsts.include_sub_dom
ains=
Specifies the X-
Frames-Options
policy. Allowed
values are DENY and
SAMEORIGIN.
Defaults to DENY.
rest.security.headers
.x_frame_options.poli
cy=
Specifies whether to
enable X-XSS-
Protection
explicitly. When
true, the Response
adds the following
header:
- X-XSS-Protection:
1
When false, the
Response adds the
following header:
- X-XSS-Protection:
0
Defaults to true.
rest.security.headers
.xss_protection.expli
cit_enable=
Specifies whether to
set block mode for
the X-XSS-Protection
header. When true,
the Response adds
the block mode to the
X-XSS-Protection
header:
- X-XSS-Protection:
1; mode=block
When false, the
Response does not
adds a mode.
Defaults to true.
rest.security.headers
.xss_protection.block
=
Documentum REST Services supports CSRF protection using a Client Token cookie
for ticked sign on, and this functionality can be integrated with CAS, Kerberos, or a
custom SSO.
The CSRF token is used to protect the Documentum Client Token cookie. The client
must provide the token in a request, and the server validates the token provided.
When validation fails, the server returns a response with a 403 (Permission Denied)
HTTP status code. This can still occur even when the initial authentication succeeds.
1. Using the Client - In authentication schemes that use a Client Token cookie, the
Client sends a defined CSRF token and parameter names to the REST server
during the initial authentication (Basic, SSO, etc.) phase. The REST server sends
back a Client Token cookie that all future requests, that authenticate with Client
Token cookie, must use in their Request Header or Request query parameter
2. Using the Server - In the authentication schemes that use a Client Token cookie,
when clients pass the initial authentication (Basic, SSO, etc.) phase, the REST
server sends back a Client Token cookie together with a CSRF token in the
Response Headers. All subsequent requests that authenticate with a Client
Token cookie must provide the CSRF token together with the Client Token
cookie, either in the Request Header or in the Request query parameter
In client-side CSRF token generation, the client must send a CSRF token and
parameters in the initial authentication phase. Here's some code sample that shows a
client sign on and server Response:
// Client sign on
GET /dctm-rest/repositories/REPO HTTP/1.1
Host: localhost:8080
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.3.1 (java 1.5)
Authorization: Basic ZG1hZG1pbjpwYXNzd29yZA==
DOCUMENTUM-CSRF-HEADER-NAME: {csrfHeaderName}
DOCUMENTUM-CSRF-QUERY-NAME: {csrfQueryName}
DOCUMENTUM-CSRF-TOKEN: {csrfTokenValue}
// Server Response
HTTP/1.1 200 OK
Content-Type: application/vnd.emc.documentum+json
Set-Cookie: DOCUMENTUM-CLIENT-TOKEN="ACa/0u...W58="; ...; HttpOnly
//...
The Request must send the CSRF token together with the Client Token cookie for
authentication. When both Header and query are specified, the client can choose
either the Header or the query to carry the CSRF token. More information is
available in the Token Validation section below.
Here's a code sample that shows you the token generation method:
# Specifies the token generation method; defaults to 'server'
rest.security.csrf.generation.method=client
When the CSRF token is generated by the client, the initial authentication Request
must not provide CSRF token and parameters. When the authentication Request
does not provide the CSRF token or one of the CSRF parameter, the server fails to
authenticate with the following Response status:
– E_LACK_OF_CSRF_TOKEN
– E_LACK_OF_CSRF_KEY_DEFINITION
Generating a server-side token is the default CSRF token generation method. The
initial authentication request remains the same as before, but the Response has a
CSRF token and parameters send back to the client.
// Client sign on
GET /dctm-rest/repositories/REPO HTTP/1.1
Host: localhost:8080
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.3.1 (java 1.5)
Authorization: Basic ZG1hZG1pbjpwYXNzd29yZA==
// Server Response
HTTP/1.1 200 OK
Content-Type: application/vnd.emc.documentum+json
Set-Cookie: DOCUMENTUM-CLIENT-TOKEN="ACa/0u...W58="; ...; HttpOnly
DOCUMENTUM-CSRF-HEADER-NAME: {csrfHeaderName}
DOCUMENTUM-CSRF-QUERY-NAME: {csrfQueryName}
DOCUMENTUM-CSRF-TOKEN: {csrfTokenValue}
// ...
When the CSRF token is generated on the server side, the CSRF token and the Client
Token cookie are generated at the same time, which means that the CSRF token
lifetime is same as the Client Token lifetime. Each time a new Client Token cookie is
sent back to the client, the client must reset the CSRF token by parsing the Response
Header.
# Specifies the 'server' generated CSRF token length in bits; defaults to 256
rest.security.csrf.token.length=
The REST server uses the same random algorithm as the Client Token to produce the
CSRF token for server-side CSRF token generation. It can be customized using the
following runtime property, which affect the Client Token encryption.
rest.security.random.algorithm=
// Client request with the CSRF protection token in the Request headers. This sample
// assumes that 'DOCUMENTUM-CSRF-TOKEN' is set as the CSRF token header name
GET /dctm-rest/repositories/REPO HTTP/1.1
Host: localhost:8080
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.3.1 (java 1.5)
Cookie: DOCUMENTUM-CLIENT-TOKEN="ACa/0u...W58="
DOCUMENTUM-CSRF-TOKEN: {csrfTokenValue}
// Server Response
HTTP/1.1 200 OK
Content-Type: application/vnd.emc.documentum+json
//...
Here's a code sample that shows a client request with CSRF protection in its query
parameter:
// Client request with the CSRF protection token in the query parameters
// this sample assumes 'csrf-token' is set as the CSRF token query parameter name
GET /dctm-rest/repositories/REPO?csrf-token={urlEncodedCsRfTokenValue} HTTP/1.1
Host: localhost:8080
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.3.1 (java 1.5)
Cookie: DOCUMENTUM-CLIENT-TOKEN="ACa/0u...W58="
// Server Response
HTTP/1.1 200 OK
Content-Type: application/vnd.emc.documentum+json
//...
• Forbidden (403)
– E_CSRF_TOKEN_NOT_VALID
– E_CSRF_TOKEN_NOT_PROVIDED
#The amount of time (in seconds) that the results of a preflight request
#can be cached.
#The value is in seconds and the default is 3600 (1 hour).
rest.cors.max.age=3600
}
}
When model field have special behavior against the global configuration, the
@SanitizeConfig annotation can be used in the following two use cases:
You can configure whether to sanitize content, and for what kind of content format
(<dm_format>) . By default, only HTML and <put_html> are sanitized.
To sanitize content, it must be loaded into memory, and converted into a readable
string. When loaded into memory, there is one configuration to set the maximum
content size to be loaded. When the content is larger than the set size, the content is
not sanitized.
Note: When converting the content into a readable string, the charset of the
content is required.
There is one new query parameter called content-charset that is added to the
resources mentioned. This parameter tells the server how to parse the content. When
the content-charset is not provided, the REST server tries to get metadata
information from the content itself.
When the metadata has a valid charset, is used to sanitize the content. When neither
content-charset parameter, nor metadata information can be found, the
configuration default charset is used.
Note: When the charset and the content do not match, then the uploaded
content may have incorrect encoded values.
10.4.4 Configurations
# Determines whether to sanitize the metadata of an object.
# The sanitize will modify the metadata (remove the suspicious part).
# The default value is false.
rest.sanitize.type.metadata=false
You can create a rest-antisamy-html.xml file to define the policy for sanitization
of file content, and rest-antisamy-string.xml to customize the object metadata
sanitization policy.
• rest-antisamy-html.sample.xml
• rest-antisamy-string.sample.xml
You can create one or both of these files to customize your sanitization policy. When
a custom policy file is provided, the system default policy file that would normally
be used is ignored.
This section provides a sample that guides you through some of the basic concepts
and tasks that you can do in Documentum REST Services. The sample focuses on
common tasks involving folders, documents, and contents. By exploring the sample,
you will become familiar with the following tasks:
Notes
11.1 Prerequisites
• The web browser must be able to render the JSON representation automatically
• The web browser must have a REST client plug-in installed.
1. Type the following URL in your web browser to navigate to the service node
(Home Document).
http://localhost:8080/dctm-rest/services.json
The service node contains a list of the available services.
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
{
"resources":
{
"http://identifiers.emc.com/linkrel/repositories": {
"href": "http://localhost:8080/dctm-rest/repositories",
"hints": {
"allow": [
"GET"
],
"representations": [
"application/xml",
"application/json",
"application/atom+xml",
"application/vnd.emc.documentum+json"
]
}
},
"about": {
"href": "http://localhost:8080/dctm-rest/product-information",
"hints": {
"allow": ["GET"],
"representations": [
"application/xml",
"application/json",
"application/vnd.emc.documentum+xml",
"application/vnd.emc.documentum+json"
]
}
}
}
}
Typically, you will see the resources service as the first node of services. In the
link relation http://identifiers.emc.com/linkrel/repositories, note the
URI to the repositories resource that resembles the following:
http://identifiers.emc.com/linkrel/repositories
2. Click the repositories link you got from step 1 to navigate to the list of all
available repositories. Explore the output, and note the information in the
entries element.
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
{
"id": "http://localhost:8080/dctm-rest/repositories",
"title": "Repositories",
"updated": "2013-05-22T14:41:29.672+08:00",
"author":
[
{
"name": "OpenText Documentum"
}
],
"total": 2,
"links":
[
{
"rel": "self",
"href": "http://localhost:8080/dctm-rest/repositories"
}
],
"entries":
[
{
"id": "http://localhost:8080/dctm-rest/repositories/acme01",
"title": "acme01",
"content":
{
"content-type": "application/json",
"src": "http://localhost:8080/dctm-rest/repositories/acme01"
},
"links":
[
{
"rel": "edit",
"href": "http://localhost:8080/dctm-rest/repositories/acme01"
}
]
},
{
"id": "http://localhost:8080/dctm-rest/repositories/acme02",
"title": "acme02",
"content":
{
"content-type": "application/json",
"src": "http://localhost:8080/dctm-rest/repositories/acme02"
},
"links":
[
{
"rel": "edit",
"href": "http://localhost:8080/dctm-rest/repositories/acme02"
}
]
}
]
}
3. Click the href link of the edit link relation of a repository in the entries
element to retrieve the details of the repository. Enter your credentials if you are
prompted for authentication.
GET http://localhost:8080/dctm-rest/repositories/acme01
Authorization: Basic ZG1hZG1pbjpwYXNzd29yZ
{
"id": 1234,
"name": "acme01",
"servers": [
{
"name": "acme01",
"host": "CS70_Main",
"version": "7.2.0000.0000Win64.SQLServer",
"docbroker": "CS70_Main"
}
],
"links": [
{
"rel": "self",
"href":
"http://localhost:8080/dctm-rest/repositories/acme01"},
{
"rel": "http://identifiers.emc.com/linkrel/users",
"href":
"http://localhost:8080/dctm-rest/repositories/acme01/users"},
{
"rel": "http://identifiers.emc.com/linkrel/current-user",
"href":
"http://localhost:8080/dctm-rest/repositories/acme01/currentuser"},
{
"rel": "http://identifiers.emc.com/linkrel/groups",
"href":
"http://localhost:8080/dctm-rest/repositories/acme01/groups"},
{
"rel": "http://identifiers.emc.com/linkrel/cabinets",
"href":
"http://localhost:8080/dctm-rest/repositories/acme01/cabinets"},
{
"rel": "http://identifiers.emc.com/linkrel/formats",
"href":
"http://localhost:8080/dctm-rest/repositories/acme01/formats"},
{
"rel": "http://identifiers.emc.com/linkrel/network-locations",
"href":
"http://localhost:8080/dctm-rest/repositories/acme01/
network-locations"},
{
"rel": "http://identifiers.emc.com/linkrel/relations",
"href":
"http://localhost:8080/dctm-rest/repositories/acme01/relations"},
{
"rel": "http://identifiers.emc.com/linkrel/relation-types",
"href":
"http://localhost:8080/dctm-rest/repositories/acme01/relation-types"},
{
"rel": "http://identifiers.emc.com/linkrel/checked-out-objects",
"href":
"http://localhost:8080/dctm-rest/repositories/acme01/
checked-out-objects"},
{
"rel": "http://identifiers.emc.com/linkrel/types",
"href":
"http://localhost:8080/dctm-rest/repositories/acme01/types"},
{
"rel": "http://identifiers.emc.com/linkrel/dql",
"hreftemplate":
"http://localhost:8080/dctm-rest/repositories/acme01
{?dql,page,items-per-page}"}
]
}
By clicking the link relations in the links element, you can drill down various
resources in the repository.
4. By clicking the link relation http://identifiers.emc.com/linkrel/cabinets
in the links element, you can navigate to the list of all available cabinets.
Explore the output, and note the information in the entries element.
Note: You can set the inline parameter to true, and this retrieves the
entire content of each entry in the collection. In the following output, the
inline parameter uses the default value false so that content of an entry
only contains content-type and src.
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
{
"id": "http://localhost:8080/dctm-rest/repositories/acme01/cabinets",
"title": "Cabinets",
"updated": "2013-05-22T14:55:24.594+08:00",
"author": [
{
"name": "OpenText Documentum"}
],
"links": [
{
"rel": "self",
"href": "http://localhost:8080/dctm-rest/repositories/acme01/cabinets"}
],
"entries": [
{
"id": "http://localhost:8080/dctm-rest/repositories/acme01/objects/
0c0004d280000d1f",
"title": "dmadmin",
"updated": "2012-10-15T23:31:27.000+08:00",
"author": [
{
"name": "dmadmin",
"uri":
"http://localhost:8080/dctm-rest/repositories/acme01/users/dmadmin"}
],
"content": {
"content-type": "application/json",
"src":
"http://localhost:8080/dctm-rest/repositories/acme01/cabinets/
0c0004d280000d1f"},
"links": [
{
"rel": "edit",
"href":
"http://localhost:8080/dctm-rest/repositories/acme01/cabinets/
0c0004d280000d1f"}
]
},
{
"id":
"http://localhost:8080/dctm-rest/repositories/acme01/objects/
0c0004d280000107",
"title": "Temp",
"updated": "2012-10-15T15:27:31.000+08:00",
"author": [
{
"name": "acme01",
"uri":
"http://localhost:8080/dctm-rest/repositories/acme01/users/acme01"}
],
"content": {
"content-type": "application/json",
"src":
"http://localhost:8080/dctm-rest/repositories/acme01/cabinets/
0c0004d280000107"},
"links": [
{
"rel": "edit",
"href":
"http://localhost:8080/dctm-rest/repositories/acme01/cabinets/
0c0004d280000107"}
]
}
]
}
5. Click the src link in the content element of a cabinet in the entries element to
retrieve the details of the cabinet. You will get an output that resembles the
following:
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
{
"name": "object",
"type": "dm_cabinet",
"definition":
"http://localhost:8080/dctm-rest/repositories/acme01/types/dm_cabinet",
"properties":
{
"r_object_id": "0c0004d280000107",
"object_name": "Temp",
"r_object_type": "dm_cabinet",
"title": "Temporary Object Cabinet",
...,
},
"links":
[
{
"rel": "self",
"href":"http://localhost:8080/dctm-rest/repositories/acme01/objects/
0c0004d280000107"
},
{
"rel": "edit",
"href":"http://localhost:8080/dctm-rest/repositories/acme01/objects/
0c0004d280000107"
},
{
"rel": "http://identifiers.emc.com/linkrel/delete",
"href":"http://localhost:8080/dctm-rest/repositories/acme01/objects/
0c0004d280000107"
},
{
"rel": "canonical",
"href":"http://localhost:8080/dctm-rest/repositories/acme01/cabinets/
0c0004d280000107"
},
{
"rel": "http://identifiers.emc.com/linkrel/folders",
"href":"http://localhost:8080/dctm-rest/repositories/acme01/folders/
0c0004d280000107/folders"
},
{
"rel": "http://identifiers.emc.com/linkrel/documents",
"href":"http://localhost:8080/dctm-rest/repositories/acme01/folders/
0c0004d280000107/documents"
},
{
"rel": "http://identifiers.emc.com/linkrel/objects",
"href":"http://localhost:8080/dctm-rest/repositories/acme01/folders/
0c0004d280000107/objects"
},
{
"rel": "http://identifiers.emc.com/linkrel/child-links",
"href":"http://localhost:8080/dctm-rest/repositories/acme01/folders/
0c0004d280000107/child-links"
},
{
"rel": "http://identifiers.emc.com/linkrel/relations",
"href":"http://localhost:8080/dctm-rest/repositories/acme01/relations?
related-object-id=0c0004d280000107&related-object-role=any"
}
]
}
By clicking the link relations in the links element, you can drill down various
resources in the cabinet.
6. Click the href link of link relation http://identifiers.emc.com/linkrel/
folders of the cabinet to retrieve the details of the child folders under the
cabinet. You will get an output that resembles the following:
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
{
"id": "http://localhost:8080/dctm-rest/repositories/acme01/
folders/0c0004d280000107/folders",
"title": "Folders under folder 0c0004d280000107",
"updated": "2013-05-22T15:07:54.156+08:00",
"author": [{"name": "OpenText Documentum"}],
"page": 1,
"items-per-page": 100,
"links":
[
{
"rel": "self",
"href": "http://localhost:8080/dctm-rest/repositories/acme01/folders/
0c0004d280000107/folders"
},
{
"rel": "next",
"href": "http://localhost:8080/dctm-rest/repositories/acme01/folders/
0c0004d280000107/folders?items-per-page=100&page=2"
},
{
"rel": "first",
"href": "http://localhost:8080/dctm-rest/repositories/acme01/folders/
0c0004d280000107/folders?items-per-page=100&page=1"
}
],
"entries":
[
{
"id": "http://localhost:8080/dctm-rest/repositories/acme01/objects/
0b0004d280009646",
"title": "REST-API-TEST-FOLDER43fd1f60-fc9a-4402-9aca-30d86ab9f4b4",
"updated": "2013-05-22T15:07:39.000+08:00",
"author":
[
{
"name": "dave","uri": "http://localhost:8080/dctm-rest/repositories/
acme01/users/dave"
}
],
"content":
{
"content-type": "application/json",
"src": "http://localhost:8080/dctm-rest/repositories/acme01/folders/
0b0004d280009646"
},
"links":
[
{
"rel": "edit","href": "http://localhost:8080/dctm-rest/
repositories/acme01/folders/0b0004d280009646"
}
]
},
{
"id": "http://localhost:8080/dctm-rest/repositories/acme01/objects/
0b0004d280009647",
"title": "REST-API-TEST-FOLDERa0cb9000-2b85-47ea-a427-9108a43c5097",
"updated": "2013-05-22T15:07:39.000+08:00",
"author":
[
{
"name": "dave","uri": "http://localhost:8080/dctm-rest/
repositories/acme01/users/dave"
}
],
"content":
{
"content-type": "application/json",
"src": "http://localhost:8080/dctm-rest/repositories/acme01/folders/
0b0004d280009647"
},
"links":
[
{
"rel": "edit", "href": "http://localhost:8080/dctm-rest/
repositories/acme01/folders/0b0004d280009647"
}
]
},
...
]
}
Click the href link in the next link relation to navigate to the next page.
7. Click the href link of the edit link relation of a folder in the entries element to
retrieve the details of the folder.
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
{
"name": "folder",
"type": "dm_folder",
"definition": "http://localhost:8080/dctm-rest/repositories/acme01/types/
dm_folder",
"properties":
{
"r_object_id": "0b0004d280009646",
"object_name": "REST-API-TEST-FOLDER43fd1f60-fc9a-4402-9aca-30d86ab9f4b4",
"r_object_type": "dm_folder",
...
},
"links":
[
{
"rel": "self",
"href":
"http://localhost:8080/dctm-rest/repositories/acme01/folders/
0b0004d280009646"
},
{
"rel": "edit",
"href": "http://localhost:8080/dctm-rest/repositories/acme01/folders/
0b0004d280009646"
},
{
"rel": "http://identifiers.emc.com/linkrel/delete",
"href":"http://localhost:8080/dctm-rest/repositories/acme01/folders/
0b0004d280009646"
},
{
"rel": "http://identifiers.emc.com/linkrel/parent-links",
"href": "http://localhost:8080/dctm-rest/repositories/acme01/objects/
0b0004d280009646/parent-links"
},
{
"rel": "parent",
"title": "0c0004d280000107",
"href": "http://localhost:8080/dctm-rest/repositories/acme01/folders/
0c0004d280000107"
},
{
"rel": "http://identifiers.emc.com/linkrel/folders",
"href": "http://localhost:8080/dctm-rest/repositories/acme01/folders/
0b0004d280009646/folders"
},
{
"rel": "http://identifiers.emc.com/linkrel/documents",
"href": "http://localhost:8080/dctm-rest/repositories/acme01/folders/
0b0004d280009646/documents"
},
{
"rel": "http://identifiers.emc.com/linkrel/objects",
"href": "http://localhost:8080/dctm-rest/repositories/acme01/folders/
0b0004d280009646/objects"
},
{
"rel": "http://identifiers.emc.com/linkrel/child-links",
"href": "http://localhost:8080/dctm-rest/repositories/acme01/folders/
0b0004d280009646/child-links"
},
{
"rel": "http://identifiers.emc.com/linkrel/cabinet",
"href": "http://localhost:8080/dctm-rest/repositories/acme01/cabinets/
0c0004d280000107"
},
{
"rel": "http://identifiers.emc.com/linkrel/relations",
"href": "http://localhost:8080/dctm-rest/repositories/acme01/
relations?related-object-id=0b0004d280009646&
related-object-role=any"
}
]
}
In the output you received from step 7, click the href link in the http://
identifiers.emc.com/linkrel/documents link relation to navigate to the list
of available documents in this folder. You will notice that the structure of the
Documents resource is similar to that of the Folders resource.
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
{
"id": "http://localhost:8080/dctm-rest/repositories/acme01/folders/
0b0004d280009646/documents",
"title": "Documents under folder 0b0004d280009646",
"updated": "2013-05-22T15:18:41.844+08:00",
"author": [{
"name": "OpenText Documentum"
}
],
"links": [{
"rel": "self",
"href": "http://localhost:8080/dctm-rest/repositories/acme01/folders/
0b0004d280009646/documents"
}
],
"entries": [{
"id": "http://localhost:8080/dctm-rest/repositories/acme01/objects/
090004d280009651",
"title": "REST-API-TEST-DOC",
"updated": "2013-05-22T15:07:39.000+08:00",
"author": [{
"name": "dave",
"uri": "http://localhost:8080/dctm-rest/repositories/acme01/
users/dave"
}
],
"content": {
"content-type": "application/json",
"src": "http://localhost:8080/dctm-rest/repositories/acme01/documents/
090004d280009651"
},
"links": [{
"rel": "edit",
"href": "http://localhost:8080/dctm-rest/repositories/acme01/
documents/
090004d280009651"
}
]
}
]
}
8. Send a POST Request to create a document under the folder with the following
configuration:
• Set the URL to the href link in the documents link relation you got in step 7.
• Set the content type to application/vnd.emc.documentum+json
• Enter the following data in the Request body:
{
"properties":{
"object_name":"Vienna",
"r_object_type":"dm_document"
}
}
Note: The detailed steps may vary depending on the tool you use to send
the Request.
You will receive an HTTP 201 Created status upon a successful document
creation. Also, you will receive the URI pointing to the document in the
Location Header of the Response body. Enter this URI in the web browser to
navigate to the newly-created document. The output resembles the following:
HTTP/1.1 201 OK
Content-Type: application/json;charset=UTF-8
Location: http://localhost:8080/dctm-rest/repositories/acme01/documents/
090004d280009894
{
"name": "document",
"type": "dm_document",
"definition": "http://localhost:8080/dctm-rest/repositories/acme01/
types/dm_document",
"properties": {
"r_object_id": "090004d280009894",
"object_name": "Vienna",
"r_object_type": "dm_document",
...
},
"links": [
{"rel": "self",
"href": "http://localhost:8080/dctm-rest/repositories/acme01/documents/
090004d280009894"},
{"rel": "edit",
"href": "http://localhost:8080/dctm-rest/repositories/acme01/documents/
090004d280009894"},
{"rel": "http://identifiers.emc.com/linkrel/delete",
"href": "http://localhost:8080/dctm-rest/repositories/acme01/documents/
090004d280009894"},
{"rel": "http://identifiers.emc.com/linkrel/parent-links",
"href": "http://localhost:8080/dctm-rest/repositories/acme01/objects/
090004d280009894/parent-links"},
{"rel": "parent",
"title": "0b0004d280009646",
"href": "http://localhost:8080/dctm-rest/repositories/acme01/folders/
0b0004d280009646"},
{"rel": "http://identifiers.emc.com/linkrel/cabinet",
"href": "http://localhost:8080/dctm-rest/repositories/acme01/cabinets/
0c0004d280000107"},
{"rel": "contents",
"href": "http://localhost:8080/dctm-rest/repositories/acme01/objects/
090004d280009894/contents"},
{"rel": "http://identifiers.emc.com/linkrel/primary-content",
"href": "http://localhost:8080/dctm-rest/repositories/acme01/objects/
090004d280009894/contents/content"},
{"rel": "http://identifiers.emc.com/linkrel/checkout",
"href": "http://localhost:8080/dctm-rest/repositories/acme01/objects/
090004d280009894/lock"},
{"rel": "version-history",
"href": "http://localhost:8080/dctm-rest/repositories/acme01/objects/
090004d280009894/versions"},
{"rel": "http://identifiers.emc.com/linkrel/current-version",
"href": "http://localhost:8080/dctm-rest/repositories/acme01/objects/
090004d280009894/versions/current"},
{"rel": "http://identifiers.emc.com/linkrel/relations",
"href": "http://localhost:8080/dctm-rest/repositories/acme01/
relations?related-object-id=090004d280009894&related-object-role=any"}
]
}
9. Send a POST request to create a content for this document with the following
configuration:
• Set the URL to the href link in the contents link relation you got in step 8.
• If the plug-in allows you to set the Request body from a local file, select the
local Content Media that you want to import, and then set the corresponding
content type.
If the plug-in does not allow you to set the Request body from a local file,
input the content file binary to the Request body, and then set the
corresponding value for the content type.
If the plug-in does not allow you to set the Request body from a local file,
input the content file binary to the Request body, and then set the
corresponding value for the content type. For example:
POSThttp://localhost:8080/dctm-rest/repositories/acme01/objects/
090004d280009894/contents
Authorization: Basic ZG1hZG1pbjpwYXNzd29yZ
Content-Type: text/plain
Note: The detailed steps may vary depending on the tool you use to send
the Request.
You will receive anHTTP 201 Created status upon a successful content creation.
HTTP/1.1 201 OK
Content-Type: application/json;charset=UTF-8
Location: http://localhost:8080/dctm-rest/repositories/acme01/objects/
090004d280009894/contents/content?format=atd&modifier=&page=0
{
"name": "content",
"properties": {
"r_object_id": "060004d280004a5f",
"rendition": 0,
…
},
"links": [
{
"rel": "self",
"href": "http://localhost:8080/dctm-rest/repositories/acme01/objects/
090004d280009894/contents/content?format=atd&modifier=&page=0"
},
{
"rel": "http://identifiers.emc.com/linkrel/content-media",
"title": "ACS",
"href": "http://localhost:9080/ACS/servlet/ACS?command=read&version=2.3
&docbaseid=0004d2&basepath=C%3A%5CDocumentum%5Cdata%5Cacme01%5Ccontent_
storage_01%5C000004d2&filepath=80%5C00%5C26%5C0a.atd&objectid=090004d280
009894&cacheid=dAAEAgA%3D%3DCiYAgA%3D%3D&format=atd&pagenum=0&signature=
QQj3oFudCLohPno49lwoVFnPQihxQGNRiv0W7U%2BrMqzCD%2FngiDKM7sBKpsk4S6a%2B%2F
nBPcR6cW1qmXW2SIuP%2FeIbtI4upEs3%2B4aMZVeac9njIJ6zRosgm8yBIYAgm038KhDVOLF
1Bxrb8Wsx%2BvQMSZyZpcHmuMOpovpjZHtwCiJ8%3D&servername=CS70RC2_MAINACS1&mo
de=1×tamp=1369207791&length=38&mime_type=text%2Fplain¶llel_stream
ing=true&expire_delta=360"
},
{
"rel": "parent",
"href": "http://localhost:8080/dctm-rest/repositories/acme01/objects/
090004d280009894"
}
]
}
HTTP/1.1 201 OK
Content-Type: text/plain
Date: Wed, 22 May 2013 07:38:52 GMT
ETag: W/"38-1369207790346"
Expires: 0
Last-Modified: Wed, 22 May 2013 07:29:50 GMT
10. Send a DELETE request to delete the document with the following configuration:
• Set the URL to the URI pointing to the document you got in step 8
• Set the method to DELETE, and then send the Request.
DELETEhttp://localhost:8080/dctm-rest/repositories/acme01/objects/
090004d280009894
Authorization: Basic ZG1hZG1pbjpwYXNzd29yZ
Note: The detailed steps may vary depending on the tool you use to send
the Request.
You will receive an HTTP 204 No Content status upon a successful deletion.
If your REST client does not support the use of a POST multipart HTTP Request, then
you can use a two HTTP Requests to create a document and then to add content to
it. Let’s examine these two steps. First, you must create a contentless document
under the folder:
Second, you must create the new primary content under the document contents
feed. To do this, follow the contents link relation for the document resource. Here
is a code sample to illustrate this point:
The resulting Response from the REST server returns a content metadata
resource for the newly create content. Here is a code sample that shows you the
Response content metadata:
{
"name": "content",
"properties": {
"r_object_id": "0600000180015710",
"rendition": 0,
"parent_count": 1,
"content_size": 15,
"full_format": "atd",
"format": "2700000180000215",
"encoding": "",
"set_time": "2018-10-22T07:57:41.000+00:00",
"full_content_size": 15,
...
},
"links": [
{
"rel": "self",
"href": "http://localhost:8080/dctm-rest/repositories/repo/
objects/090000018000a091/contents/content?
format=atd&modifier=&page=0"
},
{
"rel": "enclosure",
"title": "ACS",
"href": "http://localhost:9080/ACS/servlet/ACS?
command=read&version=2.3&docbaseid=000001&
basepath=C%3A%5CDocumentum%5Cdata%5Crepo%5
Ccontent_storage_01%5C00000001&filepath"
},
{
"rel": "http://identifiers.emc.com/linkrel/content-media",
"title": "ACS",
"href": "http://localhost:9080/ACS/servlet/ACS?command=read&
version=2.3&docbaseid=000001&
basepath=C%3A%5CDocumentum%5Cdata%5Crepo%5C
content_storage_01%5C00000001&filepath"
},
{
"rel": "parent",
"href": "http://localhost:8080/dctm-rest/repositories/repo/
objects/090000018000a091"
}
]
}
The content metadata resource contains both metadata and the media links of the
content.
In the above code sample, note that the format of the content is set to atd according
to its mime type, which is text/plain. This is the case because of one of the
mappings between mime types and formats in the Documentum Server format
types.
When the format is not explicitly set by the REST client, the REST server picks up the
default format for the mime type by name (in ascending order). You can customize
the preferred format name in two ways:
• Update the REST server runtime properties file to specify the default mime-to-
format mapping in the dctm-rest.war/WEB-INF/classes/rest-api-runtime.
properties file. Here is a code sample that shows you how to do that:
rest.mime.format.mapping.enabled=true
*.text/plain=text
my_repository.application/msword=msw12
To create a rendition, you must explicitly specify the format name, page
number, or page modifier. In addition to this, when you create a rendition, it is
mandatory to set primary=false. Here is a code sample to illustrate the above
items, the primary property is shown in bold:
POST http://localhost:8080/dctm-rest/repositories/repo/objects/
090000018000a091/contents?format=crtext&page=0&primary=false
Accept: application/vnd.emc.documentum+json
Content-Type: text/plain
The REST server returns the following rendition. Note that the property
"rendition": 2 means that this content is in fact a rendition. It is shown in
bold in the properties section below:
{
"name": "content",
"properties": {
"r_object_id": "0600000180015711",
"rendition": 2,
"parent_count": 1,
"content_size": 29,
"full_format": "crtext",
"format": "270000018000011b",
...
"parent_id": [
"090000018000a091"
],
...
},
"links": [
{
"rel": "self",
"href": "http://localhost:8080/dctm-rest/repositories/repo/
objects/090000018000a091/contents/content?
format=crtext&modifier=&page=0"
},
{
"rel": "enclosure",
"title": "ACS",
"href": "http://localhost:9080/ACS/servlet/ACS?
command=read&version=2.3&docbaseid=000001&
basepath=C%3A%5CDocumentum%5Cdata%5Crepo%5C
content_storage_01%5C00000001..."
},
{
"rel": "http://identifiers.emc.com/linkrel/content-media",
"title": "ACS",
"href": "http://localhost:9080/ACS/servlet/ACS?
command=read&version=2.3&docbaseid=000001&basepath=C%3A%5C
Documentum%5Cdata%5Crepo%5C
content_storage_01%5C00000001..."
},
{
"rel": "parent",
"href": "http://localhost:8080/dctm-rest/repositories/repo/
objects/090000018000a091"
}
]
}
• Using your REST client, POST a multipart HTTP Request to the feed to create a
contentful document directly with both properties and primary content. Here is
a code sample to illustrate the POST Request. The important parts are shown in
bold.
POST http://localhost:8080/dctm-rest/repositories/repo/folders/0b0000018000416b/
documents
Accept: application/vnd.emc.documentum+json
Content-Type: multipart/form-data;
--WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="object"
{
"properties":{
"r_object_type": "dm_document",
"object_name": "readme.txt"
}
}
--WebKitFormBoundary7MA4YWxkTrZu0gW-
the content
--WebKitFormBoundary7MA4YWxkTrZu0gW--
The above Request creates a new document resource with content. Within
the new document resource you will find two important link relations:
• contents
• http://identifiers.emc.com/linkrel/primary-content
The above two link relations, shown in bold in the following code sample,
are critical because they point to the contents feed and the primary content
of the newly created document.
The contentful document has now been fully created with a single multipart POST
HTTP Request.
Here is a code sample that shows you how to use the DELETE Request on the self
link relation:
• Documentum REST Services starts with the service node as the root
• The service node contains a set of repositories.
• Each repository contains a set of cabinets or folders.
• Either folders or cabinets can contain documents or sysobjects
• A document contains metadata, and can contain a primary content and multiple
renditions
The Documentum REST Services allows clients to traverse these structures using
navigation or queries. The following code enables you to navigate from the service
entry point to a repository named tagsalad. From the tagsalad repository, you can
access the cabinet San Francisco.
import requests
import json
homeResource = "http://example.com/documentum-rest/services"
drel="http://identifiers.emc.com/linkrel/"
repository = "tagsalad"
cabinet = "San Francisco"
credentials = ('tagsalad', 'password')
home = requests.get(homeResource)
home.raise_for_status()
repositories_uri = home()['resources'][drel+'repositories']['href']
repository = get_repository(repositories_uri, repository)
cabinets_uri = get_link(repository, drel+'cabinets')
sanfran = get_atom_entries(cabinets_uri, 'title="San Francisco"')[0]
documents = get_link(sanfran, drel+'documents')
In this code sample, The function get_link returns a link based on a link relation.
The function get_repository gets a repository with a given name. The function
get_atom_entries returns Atom entries from a collection based on their properties.
The rest of this section explains the code in more details, shows the JSON
representation of resources, and discusses some REST design principles.
By running the home.json() method, you get the JSON representation of the Home
Document resource:
{
"resources": {
"http://identifiers.emc.com/linkrel/repositories": {
"hints": {
"allow": [
"GET"
],
"representations": [
"application/xml",
"application/atom+xml",
"application/vnd.emc.documentum+json"
]
},
"href": "http://example.com/documentum-rest/repositories"
},
"about": {
"hints": {
"allow": [
"GET"
],
"representations": [
"application/xml",
"application/vnd.emc.documentum+xml",
"application/vnd.emc.documentum+json"
]
},
"href": "http://localhost:8080/dctm-rest/product-information"
}
}
}
In Python, if home contains the result of a GET request that retrieved the Home
Document resource, the following expression returns the physical address of the
repositories feed:
home()['resources']['http://identifiers.emc.com/linkrel/repositories']['href']
Alternatively, you can use the following code to get the physical address of the
repositories feed.
#Retrieving a link based on a link relation
def get_link(e, linkrel, default=None):
return [ l['href'] for l in e['links'] if l['rel'] == linkrel ][0]
repositories_uri = get_link(home, 'http://identifiers.emc.com/linkrel/ repositories')
Code explanation:
The get_link() function returns the link (physical address) associated with a link
relation in a resource. If the link relation is not present, an error is raised. In Python,
this can be done in a single line, which uses a list comprehension to create a list that
contains all link relations that match the property, and then returns the first entry
(there will never be more than one entry matching a given link relation).
Code explanation:
For collections within a given repository, you can specify conditions that are used to
select results by using “Filter expression” on page 47. For example, a repository
contains cabinets, so we can find the San Francisco cabinet using the following code:
• Home Document resource: Here is the code that retrieves this resource and finds
the repositories URI in it:
homeResource = "http://example.com/documentum-rest/services"
drel="http://identifiers.emc.com/linkrel/"
home = requests.get(homeResource)
home.raise_for_status()
repositories_uri = home()['resources'][drel+'repositories']['href']
• EDAA feed (the JSON representation of an Atom Feed): The following code
retrieves a repository from the JSON representation using the title of the
corresponding entry, and then retrieves the URI of the Cabinets resource:
repository = get_repository(repositories_uri, repository)
cabinets_uri = get_link(repository, drel+'cabinets')
• Single resources (in our tutorial, the Repository resource and the Cabinet
resources). The following code retrieves a cabinet from the cabinets feed, and
then retrieves the URI of the documents feed from it:
sanfran = get_atom_entries(cabinets_uri, 'title="San Francisco"')[0]
documents = get_link(sanfran, drel+'documents')
If all returned documents can fit on one page (by default, 100 entries), and you only
print properties that are present in the feed when inline is set to false, refer to the
following code:
r = requests.get(documents, auth=credentials)
for e in r()['entries']:
print ( e['title'])
Note: The code sample prints the title of the entry instead of the document title
under the properties element, as the properties element is not returned when
inline is set to false.
The following code sample prints two properties that are returned only when
inline is set to true (object_name and title) for each document in the collection.
Additionally, the sample supports pagination by using the next link relation so that
the results can span multiple pages when the number of results is large.
documents = get_link(sanfran, drel+'documents')
while True:
The first part of the while loop is similar to our previous sample. After printing the
items on the given page, the get_link function looks for the next link relation that
contains the URI for the next page. If it does not find a next link relation, it knows
that it has read all pages in the collection.
With this expression, the Request returns resources whose object_name contains
COFFEE. Additionally, resources that were modified earlier than 2012-12-03 are
filtered out. For more examples, see “Examples of the filter expression” on page 58.
A filter always returns an entire resource. However, you can use “Property view”
on page 59 to specify a set of properties that should be returned. The sort order can
also be specified, as can the number of items on a page. For more information, see
“Common query parameters” on page 21.
The following code sample shows how these URI parameters can be combined in the
parameter list.
params = {
'inline' : True,
'sort' : 'object_name',
'view' : 'object_name,title',
'filter' : 'contains(object_name, "COFFEE") and r_modify_date >= date("2012-12-03")',
'items-per-page' : 50
}
Response = requests.get(documents, params=params, auth=credentials)
Response.raise_for_status()
for e in Response()['entries']:
print (prettyprint(e))
At least the object name and the object type must be specified. In a real application,
you may need to create an object type that represents the properties of a given kind
of document. To keep it simple, the following sample only sets the title property.
body = json.dumps(
{
"properties" : {
"object_name" : "Earthquake McGoon's",
"r_object_type" : "dm_document",
"title" : "50 California Street, 94111"
}
}
)
headers = { 'content-type' : 'application/vnd.emc.documentum+json' }
Response = requests.post( documents, data=body, headers=headers, auth=credentials)
If the POST operation succeeds, the status code is 201 CREATED, and the Response
contains the newly-created document resource.
>>> Response = requests.post( documents, data=body, headers=headers, auth=credentials)
>>> Response.status_code
201
>>> Response.raise_for_status()
>>> Response.reason
'Created'
>>> Response()
{
"properties" : {
"object_name" : "Earthquake McGoon's",
"r_object_type" : "dm_document",
"title" : "50 California Street, 94111"
}
}
Note: The code above uses the edit link relation, which is an IANA standard
link relation that allows a resource to be read, updated, or deleted.
If the POST succeeds, the status code is 200 OK, and the Response contains the
updated document resource.
>>> Response.status_code
200
>>> Response.reason
'OK'
>>> Response.raise_for_status()
>>> responsed ()
{
"properties" : {
"object_name" : "Earthquake McGoon's",
"r_object_type" : "dm_document",
"title" : "Kilroy was Here!"
}
}
The Response contains the HTTP status code 204 with no content.
>>> Response.status_code
204
>>> Response.reason
'No Content'
>>> Response.raise_for_status()
>>>
http://identifiers.emc.com/linkrel/
FeedView
1. Model Classes for Core resources are in the package com.emc.documentum.rest.
model
2. View Classes for Core resources are in the package com.emc.documentum.rest.
view.impl