Chapter 4. CMIS metadata: types and properties

This chapter covers

  • General metadata concepts
  • CMIS types and property definitions
  • Constraints on property definitions
  • Type discovery using Chemistry
  • Type mutability (CMIS 1.1)
  • Secondary types (CMIS 1.1)

Up to this point, we’ve been working in the realm of data. This chapter will bring us up a level into a discussion of metadata. We’ll start with a brief explanation of what exactly metadata is and how it relates to data in general. Then we’ll cover all of the basic types of CMIS metadata, including how they’re categorized and discovered. Along the way, we’ll go through some exercises that show all of these concepts in action. By the end of this chapter, you’ll have a good understanding of CMIS metadata and the new metadata-related features that are coming in CMIS 1.1.

4.1. What is metadata and why do we need it?

Simply put, metadata is data about data. Perhaps a slightly more helpful definition in this case would be data about the containers of data. If that makes no sense to you, don’t worry. It’ll all be clear in time. If you have a good grasp of metadata already and want to get right to CMIS metadata, you can jump ahead to section 4.2.

A good place for us to start is to relate metadata to what you already know—data. Let’s take a library’s catalog (an old card catalog or a digital database) as an example. Somewhere you have a book sitting on a shelf—the book is the object in this example. The book also has an associated catalog record (physical or digital) that contains data about the object. The data in this record is the metadata, and it includes items like the title of the book, the author’s name, and so on. Finally, there’s an archetype for the catalog records—a catalog record will have to conform to certain requirements regarding the data it contains. In some cases, this description of what the cards in the card catalog should look like may only exist in the mind of the librarian. In other cases, it might be written down in an operations manual. Either way, that archetype can be considered the schema. Figure 4.1 illustrates this relationship.

Figure 4.1. The schema describes metadata, which describes data.

For the rest of this book, when we refer to metadata, we’ll be talking about metadata in content management systems specifically, unless otherwise noted. When we talk about types, we’re using the CMIS name for the schema. In section 4.2, we’ll talk about what this all looks like from a CMIS perspective, which means types and property definitions.

The schema is metadata, too

Remember that because the schema is also data-describing data, the schema is also metadata in the general sense. But this doesn’t mean that all metadata is same as the schema. It’s OK to loosely refer to both metadata and the schema as metadata, in cases where the distinction isn’t important.

Now that we’ve addressed what metadata is, we’re left with the question of why we need it. Metadata is a key part of making objects searchable. In the case of the library, the most important thing you’re going to be doing with those catalog records is using them to find what you want. Compare that with a CMIS example: Say you were trying to find a photo in a CMIS repository that contained 100 million or more objects. Without metadata, you could visually search through them all sequentially until you found the one you were seeking. Maybe you could even sort by filename to help out a bit if you knew something about what the photo was called. But with proper types defined on your repository, finding a specific image can be trivial.

Imagine you want to see all of the images that were created after 2007 and updated sometime between 2009 and 2011. You could further restrict this search to only those images that have a resolution of 1024 by 768 pixels and have a description field that contains the words “elephant” and “Swahili.” Now that 100-million-result set has shrunk by quite a bit. Even if you found a few photos that matched this criteria, it’s likely you wouldn’t have to visually or manually scan very many.

That’s the power of CMIS Query, and that’s precisely what we’ll talk about in detail in chapter 5. But remember, it’s metadata that makes this all possible.

4.2. Metadata in CMIS

Taking the library analogy a bit further, you can imagine that a library might store more types of media than just books. It might have books on tape, CDs, DVDs, magazines, eBooks, and perhaps even microfiche, among others. Each of these different types of objects will have different types of metadata associated with them. This is also typically the case with ECM systems, so it applies to CMIS. ECM repositories generally have a large variety of objects, each with their own associated types. Later in this chapter, you’ll see how CMIS manages the organization, storage, and retrieval of these schema objects.

Type is the name used in the CMIS specification to identify the objects that hold the schema for normal data objects in the repository. Recall that data objects are made up of instances of these types. Folders and documents are the most common examples of data objects. Type objects contain collections of property definitions that define what properties will be present on an object instance of that type, as figure 4.2 shows. This is directly analogous to how data objects contain collections of properties.

Figure 4.2. CMIS metadata types and property definitions describe objects and properties, respectively.

As you can see in figure 4.2, all objects have one type object to describe them. Also, for every property on that object, there’s a corresponding property definition object on the type to describe that property. If you recall, in chapter 2 (section 2.4.2) you saw all of the properties that are present for all objects as well as all of the additional properties that are present on all document objects. The CMIS metadata functions that we’ll discuss in this chapter are what you use to find out which properties you should expect to see on any object.

4.2.1. Type definitions are hierarchical and attributes are inherited

Just like the objects they describe, type objects and property definition objects have properties themselves, but if we also called these properties, we could mistake them for normal properties on regular instance objects, and things would get a little confusing. As if they’re not already, right? To avoid this unfortunate verbal tangle, the CMIS specification refers to properties on metadata objects as attributes. This way it’s clear that if we say property, we’re talking about a value on an object, and if we say attribute, we mean a value on a type or property definition. Attribute is shorter to say than metaproperty anyway.

All of these type objects in CMIS inherit attributes from their parent type objects. Therefore, the document type will inherit all of the attributes that are common to all CMIS objects, and it will then add in additional ones that are only present for documents. The same is true for the attributes on property definitions. Figure 4.3 shows the complete base metadata hierarchy for the five base types and the eight CMIS data types we introduced you to in sections 2.3 and 2.4.

Figure 4.3. CMIS metadata base hierarchy for types and property definitions showing each one’s attributes

A sixth base type (cmis:secondary) isn’t shown in figure 4.3. This is due to its special secondary/optional nature, which will be explained in section 4.4.2.

4.2.2. Try it—view the types and property definitions using Workbench

Now that you have a good picture of what all of this metadata looks like on paper, let’s go into the CMIS Workbench and take a look at the types and property definitions in the test InMemory server. In order to bring up the Types viewer in the Workbench, locate the Types button at the top of the window, as shown in figure 4.4. Clicking this button will load up the Types dialog box that’s shown in figure 4.5.

Figure 4.4. Types button (circled), which launches Workbench’s Types viewer window

Figure 4.5. The CMIS Types window in CMIS Workbench showing the attributes and associated property definitions for cmis:document

Figure 4.5 shows the CMIS Types view with the cmis:document object selected in the left pane, and the cmis:name property selected in the properties pane. Note that the Apache Chemistry InMemory Repository we used in this example has multiple child types of cmis:document, some of which have further subtypes, which the open folder icons indicate for each one. The version of the InMemory Repository that you’re using may have different child types of cmis:document, because the binary package that comes with this book will continue to be updated. Also note that in this property definition view, there’s a separate movable column for each of the attributes on that property definition. This makes it easy to move the columns that you’re most interested in to the left (and more visible) portion of the window.

In the property definitions pane in figure 4.5 (lower right), you can see that the cmis:name property is highlighted. Although only a few of the property’s attributes are shown here (scroll right to see more), you can see that the cmis:name property for all cmis:document objects is of type STRING and its updateability is READWRITE. That is, it’s settable by CMIS clients when they’re creating or editing the object. In contrast, the cmis:objectId property on the line below it has an ID type and shows as READONLY to all clients.

4.3. Type collections and hierarchies

ECM repositories often have a lot of types defined—hundreds in extreme cases. This means that there needs to be a scalable way to organize and retrieve them. Some repository designs treat these type collections as a large flat list, and others treat them as a hierarchy. As you saw earlier in this chapter, CMIS defines a hierarchy to organize all of the type objects. If an underlying repository only has a flat list, it would be exposed through CMIS as a hierarchy with a depth of one.

Custom application developers take heed

This section is of particular importance to custom application developers. We’ll cover type collections and how to navigate them with two code examples that show each of the main methods for retrieving types. Property definitions will be explained, and we’ll also discuss the constraints that CMIS permits on properties, followed by a final example that shows how to access choice lists. You’ll use the techniques you learn here repeatedly as you code more complex CMIS applications in this chapter, as well as in part 2 of this book.

Recall the default type hierarchy in figure 4.3. This isn’t meant to be the sum of the hierarchy, but rather the tip (as in an iceberg) or a starting point that’s common to all repositories. Each of these base types can have child and grandchild types going down as deep and as wide as is necessary for each application. Later, in part 2 of the book, you’ll use a custom subclass of cmis:document that will be tailored for the metadata in the music mashup example application.

4.3.1. Try it—traversing the type hierarchy

Understanding how all of these types are laid out was the first step. Now that you have that down, it’s time to look at how your application can programmatically discover anything it needs to know about the metadata in a repository. For this, we’re going back to our trusty Groovy console and we’ll show you how simple this seemingly complex operation can be.

In this section, you’ll traverse the hierarchy of objects, looking at their attributes along the way. You’ll even display attributes that are only present on documents so that you can see how easy it is to determine the types of these objects. Although we won’t examine all the attributes that are available in this short example, you’ll notice that all of the attributes listed in the CMIS specification are accessible as getter methods on the various classes of type objects.

Listing 4.1 shows the code for traversing the type hierarchy. For more examples, please see the Javadocs for OpenCMIS (http://chemistry.apache.org/java/0.8.0/maven/apidocs/). In part 2 of the book, you’ll see these attribute values being used in a real application to give them a bit more context.

Listing 4.1. getTypeDescendants code example (type walker)

If you look at the output from listing 4.1 (shown in figure 4.6), you’ll see the same information you saw in figure 4.5 with all of the child levels expanded and each level indented to show the hierarchy visually.

Figure 4.6. Output from the getTypeDescendants code (type walker)

Now that you know something about types and their attributes, let’s move on to the next exercise, where we’ll expand the example to show property definitions and their attributes as well.

4.3.2. Try it—examining property definitions on types

Now you’ll modify the type walker example and add in some code to walk through the property definitions for each type. You’ll display a few key attributes for each type, like each property’s ID, data type, and updateability.

Listing 4.2 shows the modified version of the code, type walker v2. This version adds a new method, printPropDefsForType, that’s called in the type loop. As you can see, it’s trivially easy to get this information from the type object using OpenCMIS.

Listing 4.2. getTypeDescendants with property definitions (type walker v2)

Figure 4.7 shows the output of type walker v2. The figure shows the complete output for the type named audioFile; the other types are omitted for space reasons.

Figure 4.7. Truncated output from getTypeDescendant with property definitions included (type walker v2)

This example has two parts because you can get at the Type and PropertyDefinition objects in OpenCMIS in two different ways. In listing 4.2, you retrieved the types from the types collection and walked the types tree directly. But sometimes it’s more convenient to get the Type object and/or corresponding PropertyDefinition objects for a particular instance object that you have in hand, and not worry about its type’s location in the types hierarchy. Listing 4.3 shows how to do this using the root folder object as a generic example. This technique will work for any CMIS object you encounter.

System and custom properties

When developers talk about properties in CMIS, some will refer to custom and system properties. These terms can have different meanings in different contexts, but in the purest CMIS context, system properties usually refer to those properties that are defined in the specification, namely, the properties that look like cmis:xxx, such as cmis:objectId. Custom properties are everything else. Because custom properties aren’t defined by the specification, they’re repository- and type-specific. For example, later in the book we’ll work with a subclass of cmis:document named audioFile. This type has many custom properties relating to audio tracks, like Album, which is a custom string property that holds the album name. Repository developers should note that you shouldn’t use the cmis: prefix for naming any of your custom repository’s properties. That prefix is reserved for properties defined in the specification.

Listing 4.3. Retrieving type and property definitions directly from the object

If you take a look at the output in figure 4.8, you can see that the number of property definitions that were defined on the cmis:folder type matches the number of properties that were on the instance of the folder object. See the callouts in the example for a discussion of why this isn’t always the case.

Figure 4.8. Output showing type and property definition information retrieved directly from the instance object

Now that you’ve seen how to get to the PropertyDefinition objects, let’s look at all of the types of constraints that are permitted on them.

4.3.3. Constraints on property definitions

The last aspect of property definitions that we need to explore (before we’re ready to talk about the new CMIS 1.1 metadata features) is the concept of constraints. Aside from specifying what type of data the property holds and its cardinality, a property definition may also place constraints on the potential values.

Constraints break down into two main groups, as explained in the next section.

Common constraints on property definitions

Here’s a quick rundown of the constraints that can be present on any of the eight property definition object types. For a more detailed discussion of these, see section 2.1.3.3.2 of the CMIS 1.1 specification.

  • choices—An explicit ordered set of values that are permissible for this property. For example, a string property definition named PrimaryColors might have choices = [Red, Green, Blue]. Each choice includes a displayName and a value. The displayName may be used by clients for presentation purposes.
  • openchoice (boolean)—This attribute is only applicable to properties that provide a value for the choices attribute. If it’s FALSE, the data value for the property must only be one of the values specified in the choices attribute. If it’s TRUE, values other than those included in the choices attribute may be set for the property.
  • defaultvalue—Contains the value that the repository must set for the property if one isn’t provided at object creation time. If a property is set to required and doesn’t have a default value, any attempt to create an object when this property hasn’t been set will result in a constraint exception being thrown.
Property-specific types of constraints

There are four additional types of constraints for specific property types. For a more detailed discussion of these, see sections 2.1.3.3.3–2.1.3.3.5 of the CMIS 1.1 specification. These are the four type-specific constraints:

  • minValue and maxValue—Apply to Integer and Decimal property types only and specify the minimum and maximum values permitted for this property. If an application tries to set this property to a value outside of this range, the repository must throw a constraint exception.
  • maxLength—Applies to String property types only and specifies the maximum length (in characters) allowed for a value of this property. If an application attempts to set the value of this property to a string longer than the specified maximum length, the repository must throw a constraint exception.
  • resolution—This is an enum that applies only to DateTime property definitions. Each value in the following list implies all of the values above it, like bit flags. For example, if the value of time is present, this implies that time, date, and year are persisted. The permitted values for this enum are as follows:

    • year—Year resolution is persisted. The date and time portion of the value should be ignored.
    • date—Date resolution is persisted. The time portion of the value should be ignored.
    • time—Time resolution is persisted.
  • precision—This is an enum that applies to property definitions of Decimal only. The permitted values for this enum are as follows:

    • 32—Use 32-bit precision (“single” as specified in IEEE-754-1985)
    • 64—Use 64-bit precision (“double” as specified in IEEE-754-1985)

Next up, we’ll exercise some of these constraints using the Groovy console in the CMIS Workbench.

4.3.4. Try it—examining constraints on property definitions

Ready to see how this all looks in code? Let’s go back to the CMIS Workbench again and have a look at listing 4.4. It augments the type walker v2 example to also show choice lists, default values, and the integer-specific constraint maxValue.

Listing 4.4. Examining the constraints on property definitions

Figure 4.9 shows the output pane from the Groovy console window.

Figure 4.9. Truncated output from listing 4.4, showing choice lists and default values

4.3.5. Attribute and attribute value inheritance

Before we get to the new CMIS 1.1 metadata features, we need to clarify one more thing related to inheritance and attributes. You may recall (from earlier in this chapter) the hierarchy of the CMIS type definitions and the attributes that are inherited from the base CMIS object type. An object type will inherit all of its parent type’s attributes, but the values of the attribute aren’t inherited.

Let’s consider the versionable attribute of cmis:document to illustrate this. All subtypes of cmis:document in a repository must have the versionable attribute that was introduced at the cmis:document level. But the specific Boolean value of versionable for each of those subtypes is set independently. Therefore, in a particular repository, cmis:document might have versionable=true and still have a subtype named invoiceDocument that has versionable=false.

4.4. CMIS 1.1 metadata features

CMIS 1.1 adds two powerful tools that extend what clients can do with metadata:

  • Type mutabilityAllows CMIS clients to create, read, update, and delete (CRUD) type definitions, which means a CMIS installer application can set up the required types in a repository-agnostic manner. Another way of looking at this is that the manual steps required for an administrator to create a type definition through the repository-specific interfaces are no longer necessary.
  • Secondary typesThese special types can be attached to (or detached from) an object at any point during its life. They allow you to dynamically add or remove lists of additional properties during the lifetime of an object.

We’ll describe these tools in the following sections.

4.4.1. Type mutability

The process for creating and deleting types can be surprisingly simple. Nevertheless, type updates have to follow a strictly defined set of rules (for the detailed list, see section 2.1.10.1 in the CMIS 1.1 specification), which we’ll explain in this section.

The CMIS specification doesn’t allow you to create new base types, only subtypes of existing ones. You can check whether or not a given type allows subtypes by inspecting its type definition.

Constraint (security)

As you might expect, only special users can create types for a given repository. The typeMutability.create flag for a given type isn’t to be interpreted as rights for the current user. Rather, it states whether or not an administrator (or the repository equivalent of the administrator) may create a subtype of this type. This is generally true for all rights associated with type mutability. They refer to the repository as a whole in the context of an administrator. Put another way, typeMutability.create indicates whether the repository permits an administrator to create subtypes.

The type mutability settings for a specific type are shown later in figure 4.12. Each type may have any of these three optional Boolean values set. These flags are defined in the CMIS 1.1 spec (section 2.1.3.2.1, “Attributes common to ALL Object-Type Definitions”) as follows:

  • typeMutability.create—Indicates whether new child types may be created with this type as the parent
  • typeMutability.update—Indicates whether clients may make changes to this type per the constraints defined in this specification
  • typeMutability.delete—Indicates whether or not clients may delete this type if the repository contains no instances of it
Constraint (type and property ID values)

Another point often missed is that the type ID returned by the createType operation might not be the same as what was requested. Because the underlying repository may have other restrictions on the ID value, you may only suggest rather than specify. If the repository can use the ID you suggested, that’s what will be returned. Otherwise it may be slightly modified or even entirely different. The same is true for new property type IDs on new or existing object types. For more on this, see the constraint section later in this chapter about order of the properties returned.

Constraint (new settable attributes )

Section 2.1.3.2.1 of the CMIS 1.1 specification lists the attributes that are common to all object type definitions. As a quick refresher, they are the following:

  • id
  • localName
  • localNamespace
  • displayName
  • queryName
  • description
  • creatable
  • fileable
  • queryable
  • fulltextIndexed
  • includedInSupertypeQuery
  • controllablePolicy
  • controllableACL

It’s important to note that you may not necessarily be able to set all (or any) of these attributes when creating a type. The correct way to find out for certain is to refer to the capabilityNewTypeSettableAttributes list. This will indicate which of the attributes this particular repository will accept for new types. Don’t be surprised if your repository doesn’t allow setting any of these. Often these will be internally generated based on other attributes of (or on inheritance from) the type.

Figure 4.10 shows the capabilityNewTypeSettableAttributes list for the InMemory Repository. If you look towards the bottom of the figure, you’ll see that the repository hasn’t permitted any settable attributes, which isn’t correct. At the time of this writing, the InMemory server wasn’t populating this list.

Figure 4.10. CMIS 1.1 repository information settings related to type mutability (partial)

Constraint (createable property types)

When you’re adding properties to your new type (or adding them to existing types) you must also be aware that a repository may not let you create properties of all of the CMIS-defined property types, even if they’re in use elsewhere in the repository. To make this clear for clients, the repository information will contain a list of capabilityCreatablePropertyTypes. This is a list of all of the CMIS-defined property types (boolean, id, integer, datetime, decimal, html, string, and uri) with an associated Boolean indicating whether or not it’s OK to create properties of each type in object types.

Figure 4.10 shows these settings for the InMemory Repository. If you look at the last line in the figure, you can see that this InMemory Repository supports creating properties for all eight of the CMIS-defined property types.

Constraint (order of returned properties)

The order of property types returned from the server is important. When an object type is created or updated, the repository’s response will return the new type’s properties in the exact same order in which they were listed in the input (the create or update) request. This is necessary so that clients can tell which properties correspond to their requested properties in cases where the IDs are different from what was requested. Remember that earlier we said that the value you pass for the type and property ID is only a suggestion. The repository may change it if necessary, so always use the returned value.

Type creation

To create a type, you have to provide the type definition and all of its new property definitions. Because that’s generally a repetitious, lengthy, and error-prone piece of code, OpenCMIS provides the TypeUtils class, which can read and write type definitions from and to XML and JSON. The XML and JSON format is the same format that’s defined in the specification to send type definitions over the wire. The simplest way to create a new type is to save an existing type as XML or JSON from the CMIS Workbench (by clicking the Save Type Definition button at the top of the Types screen), edit this file, and then create the new type.

To speed things up, we’ll include a working sample that you can use for the upcoming examples, as well as a template for additional types you may want to create as you’re trying things out.

Listing 4.5 shows the XML for a new cmis:document subtype named my-document. It has one additional integer property defined with the IDmy-int.

Listing 4.5. Sample XML to import for a new my-document type
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns3:type xmlns="http://docs.oasis-open.org/ns/cmis/core/200908/"
   xmlns:ns2="http://docs.oasis-open.org/ns/cmis/messaging/200908/"
   xmlns:ns3="http://docs.oasis-open.org/ns/cmis/restatom/200908/"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:type="cmisTypeDocumentDefinitionType">
<id>my-document</id>
<localName>my-document</localName>
<localNamespace>local</localNamespace>
<displayName>CMIS Document</displayName>
<queryName>my-document</queryName>
<description>Description of My Document Type</description>
<baseId>cmis:document</baseId>
<parentId>cmis:document</parentId>
<creatable>true</creatable>
<fileable>true</fileable>
<queryable>true</queryable>
<fulltextIndexed>false</fulltextIndexed>
<includedInSupertypeQuery>true</includedInSupertypeQuery>
<controllablePolicy>false</controllablePolicy>
<controllableACL>true</controllableACL>
<versionable>false</versionable>
<contentStreamAllowed>allowed</contentStreamAllowed>
  <propertyIntegerDefinition>
    <id>my-int</id>
    <localName>my-int</localName>
    <localNamespace>local</localNamespace>
    <displayName>Int</displayName>
    <queryName>my-int</queryName>
    <description>Int</description>
    <propertyType>integer</propertyType>
    <cardinality>single</cardinality>
    <updatability>readwrite</updatability>
    <inherited>false</inherited>
    <required>false</required>
    <queryable>true</queryable>
    <orderable>true</orderable>
    <openChoice>false</openChoice>
  </propertyIntegerDefinition>
</ns3:type>

Now that the XML input file is sorted out, let’s take a look at that code. Listing 4.6 shows the steps for using TypeUtils to parse that XML into a TypeDefinition object and then using the CreateType method to create the type. At the end of the listing, we’ve commented out a section that shows how you’d do the same thing if your type export file was in JSON format.

Listing 4.6. Code for creating a new subtype of cmis:document using TypeUtils
import org.apache.chemistry.opencmis.commons.*
import org.apache.chemistry.opencmis.commons.data.*
import org.apache.chemistry.opencmis.commons.definitions.*
import org.apache.chemistry.opencmis.commons.enums.*
import org.apache.chemistry.opencmis.client.api.*
import org.apache.chemistry.opencmis.client.util.*

if (session.getRepositoryInfo().getCmisVersion() ==
      CmisVersion.CMIS_1_0) {
  println("CMIS 1.0 does not support the creation of types!");
}
else {
  ObjectType parentType = session.getTypeDefinition("cmis:document");
  TypeMutability typeMutability = parentType.getTypeMutability();

  if (typeMutability != null &&
      Boolean.TRUE.equals(typeMutability.canCreate())) {

    // fix your path here
    InputStream stream1 = new FileInputStream("./my-document.xml");
    TypeDefinition type1 = TypeUtils.readFromXML(stream1);
    ObjectType createdType1 = session.createType(type1);

    // if we wanted to use json instead
    //InputStream stream2 = new FileInputStream("./my-document.json");
    //TypeDefinition type2 = TypeUtils.readFromJSON(stream2);
    //ObjectType createdType2 = session.createType(type2);
  }
}

Once the code has completed running, you can restart your Chemistry Workbench (or at least reconnect so that the metadata will be refreshed) and have a look at the new type, which is shown in figure 4.11. The figure shows my-document selected in the type tree and the my-int property highlighted at the bottom of the properties pane.

Figure 4.11. CMIS Types screen showing off our newly minted my-document type

Type deletion

Unused types can be deleted subject to these constraints:

  • The type delete flag in the type definition is set to true.
  • The type has no subtypes currently defined in the repository.
  • No objects (instances) of this type currently exist in the repository.

The first of these constraints is discovered by inspecting the type definition for the object in question. Figure 4.12 shows the Chemistry Workbench type mutability settings for the VersionedType. Note that this type supports create, update, and delete. Recall that we already showed you how to programmatically check this in listing 4.6, where we checked to see if we could create.

Figure 4.12. Type information for VersionedType showing the type mutability options available

To determine if the type has subtypes, you’ll have to navigate the type tree as we showed you earlier in this chapter. Lastly, you can use Query to discover if any objects of a given type currently exist. Alternatively, you can try to do the delete type operation, and if any of these constraints isn’t satisfied, the repository will let you know with the corresponding error.

The following example shows type deletion:

import org.apache.chemistry.opencmis.commons.*
import org.apache.chemistry.opencmis.commons.data.*
import org.apache.chemistry.opencmis.commons.definitions.*
import org.apache.chemistry.opencmis.commons.enums.*
import org.apache.chemistry.opencmis.client.api.*

ObjectType type = session.getTypeDefinition("my:type");
TypeMutability typeMutability = type.getTypeMutability();

if (typeMutability != null &&
     Boolean.TRUE.equals(typeMutability.canDelete())) {
  session.deleteType(type.getId());
}

With deletion covered, we have one more modification operation to go. Update finishes off the set and is up next.

Type updates

The logic behind updating a type is similar to creating a type, so we won’t waste space here with a complete listing. A type definition has to be provided that contains the changes (usually additions) that you wish to have committed. Then you commit the change with the updateType method, as you did with createType in the first type creation example.

The code is simple, but the restrictions on when you can update a type are a bit more complicated. Section 2.1.10.1 of the CMIS 1.1 specification covers all of the constraints for metadata updates. The following list highlights these important items:

  • Inherited properties must not be modified. This includes constraints of any kind.
  • Properties defined by the CMIS specification must not be modified. This includes constraints of any kind.
  • Only leaf types may be modified. That is, if a type already has child types defined, then it (and all of its properties and constraints) must be considered read-only.
  • Any added properties marked as “required” must have a default value.
  • Required properties may be changed to optional.
  • Optional properties must not be changed to required.
  • Property definitions must not be removed.
  • Property choice constraints may be changed in the following ways:

    • Open choice may change from false to true.
    • Open choice must not change from true to false.
    • Choices may be added or removed if open choice is true.
    • Choices must not be removed if open choice is false. Validation constraints (min/max length, min/max value, and so on) on existing properties may be relaxed, but they must not be further restricted.
    For example, an integer property value that originally had a minimum constraint of 100 and a maximum constraint of 1,000 could change as follows:

    • The minimum could be changed to 50 but couldn’t be changed to 150.
    • The maximum could be changed to 1,100 but couldn’t be changed to 900.
  • An existing property type’s data type and cardinality must not be changed. For example, an Integer property type must not be changed to a String.

That covers the basics of create, update, and delete for types. Next up are the new secondary types.

4.4.2. Secondary types

Support for secondary types is new in CMIS 1.1. We’ll first explain what a secondary type is and then talk about how creating secondary types differs from what you already know about creating normal content types. Finally, you’ll see how easy it is to add secondary types to and remove them from the objects in your CMIS repository.

What is a secondary type?

Suppose you’re building a case management system and you’re persisting the documents the system manages into a CMIS repository. If these are legal cases, you might have a content type called complaint and another called deposition transcript. You might also use an image content type for images related to the case, and these content types might appear on different branches of the content type hierarchy. This leads to the question of what you would do if you need to define metadata that’s common across all of these types. To keep it simple, we’ll use a case number as an example.

One option would be to define the property in a common ancestor type, but then you’d end up potentially inheriting that property in places where it isn’t needed. Another option would be to define the property redundantly—every type that needs it would define its own case number property. Neither of these is a great option. To address this problem, some content repositories support the concept of a free-floating type that can be arbitrarily attached to any object in the repository. Different repositories use different names to describe these special types. For example, in Alfresco, they’re called aspects. In CMIS they’re called secondary types.

Using the example of the legal case management system, a document that stores the transcript of a deposition would be created as an instance of a deposition transcript, and because it’s related to a specific case, you can add the case-related secondary type to it. Now the object has all of the metadata defined by the primary type, as well as the case-related secondary type.

Now suppose the CMIS repository will also be used to archive email. Some email might be related to a specific case, and some may not. Email will be created using an email content type, because that’s fundamentally what that object is, and only those emails related to a specific case will be given the case-related secondary type. If someone later decides that an email isn’t case-related, the case-related secondary type can be removed without changing its primary content type.

Later, someone might decide to add a tagging capability to the case management system. A taggable secondary type makes it easy to add tag-related metadata to all of the objects that need to be tagged. Now objects can be both case-related and taggable. In this way, secondary types provide a means to achieve multiple inheritance, which can’t be accomplished with primary content types alone.

Therefore, secondary types are often used to group together properties that define characteristics that many different content types might exhibit, in an effort to simplify or more efficiently implement the content model. They have the added benefit of being easy to add to and remove from an object without altering its fundamental type.

As we mentioned, not all repositories support secondary types. We’ll discuss a special base type called cmis:secondary in the next section. If your repository returns cmis:secondary in the list of type definitions returned by getTypeChildren, your repository supports secondary types.

Creating secondary types

Creating a secondary type is nearly identical to creating a normal content type with CMIS. You define your content type using XML or JSON, and then upload the definition to the repository. An important difference is that the base type must be the special cmis:secondary base type—that’s what distinguishes secondary types from normal types.

Here are the constraints that must be followed when creating secondary types:

  • creatable—Must be set to false. That’s because creating instances of secondary types isn’t allowed. All objects must be instances of primary types.
  • fileable, controllablePolicy, and controllableACL—Must also be set to false. The repository uses these values set on the primary type to decide whether or not an object instance is fileable, controllable by a policy, or controllable by an ACL.
  • parentId—Must not be set. Unlike primary types, secondary types aren’t defined in a hierarchy.
Using secondary types

Once you’ve defined a secondary type in the repository, it’s easy to add it to or remove it from an object. Objects in a repository that support secondary object types have a system property called cmis:secondaryObjectTypeIds. This is a read-write, multivalue field that lists the type IDs of the secondary types present on that specific object.

To add a secondary type to an object, you update the property by adding the desired secondary type’s type ID to the list. Once added, you can set the properties defined by the secondary type as you would any other property. In fact, you can add a secondary type and set the properties it defines simultaneously in a single updateProperties call.

To remove a secondary type from an object, remove the secondary type ID from the list. The properties (and values) will be removed from the object.

Now you know all there is to know about the new type mutability and secondary types features in CMIS 1.1, which brings us to the end of our adventures in the world of metadata.

4.5. Summary

In this chapter, you learned all of the basic concepts of metadata in typical ECM systems, as well as how those concepts map to CMIS terms. In addition, you discovered how to exercise those features programmatically in OpenCMIS. Specifically, you learned about CMIS types and property definitions and the attributes that describe them. This chapter also covered the different types of constraints that can be present on these types. Finally, you walked through the new advanced CMIS 1.1 metadata features: type mutability and secondary types.

Now that you understand these metadata basics, you’re ready to effectively use one of the most powerful features of the entire specification. That feature is Query, and we’ll talk about it in great detail in the next chapter.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset