|
Persisting Objects to Documentum using Annotations
| January 2008 | |
| Contributed by - Harjit Singh |
Abstract
Persisting objects using Java 5 annotations helps developers reduce the complexity of code as compared to using traditional methods. This approach promotes re-usability and better design. With this class, you can extend your classes which map to Documentum types and get the functionality of persisting the class to a Documentum object type with a single call.
The Problem
On a recent client engagement, I was writing code to create an object and set some attributes on it. I had to write the same code in a WDK Component and also in my JMS methods. To my surprise, I found that the most repetitive form of code is where developers have to create new objects and set some attributes on them. Whether it’s a WDK component or a Java method, the code is repetitive and there was no better way. I wanted to solve this problem.
In most of the situations I came across, the developers also try to create a class which more or less matches the actual Documentum object type, or they will implement a DAO layer to do the same. The dilemma here is that there is no direct way to map the fields with the attributes of the Documentum object type. The developer would actually add some comments which would describe what a field mapped to. What if the developer forgot (which is quite natural given the dead-lines and multiple tasks which we work under)? For example, the class in this situation would be defined some thing like this:
public class MyDctmTypeNoAnnotations {
// maps to my_id
private String attr1;
IDfSession session;
public MyDctmTypeNoAnnotations() {
}
public void setSession(IDfSession aSession) {
this.setSession(aSession);
}
public void save() throws DfException, IllegalAccessException {
String newObjectId = this.ourSave();
// perform some other operations on this object like setting the ACL
}
private String ourSave() throws DfException{
IDfSysObject sysobject = (IDfSysObject) this.session.newObject("our_type");
// Set the attribute on the dctm type
sysobject.setString("my_id",this.getAttr1());
sysobject.save();
return sysobject.getObjectId().toString();
}
public String getAttr1() {
return attr1;
}
public void setAttr1(String attr1) {
this.attr1 = attr1;
}
}
|
The Solution
After doing little bit of reading and research, I found a better way of handling this. The solution uses Annotations which were introduced in Java 5. Using annotations one can describe what the class maps to and also what each field maps to. I’m not going to go in detail regarding annotations, as there are many articles that explain what annotations are and what they do. If we were to use Annotations our class would look something like:
@DctmType(documentumType = "our_type")
public class MyDctmTypeWithAnnotations extends ourDctmObject{
@DctmAttribute(attributeName="our_id",attributeType=DctmAttribute.DctmDataType.STRING,isRepeating=false)
private String attr1;
public MyDctmTypeWithAnnotations() {
super();
}
public void setSession(IDfSession aSession) {
super.setSession(aSession);
}
public String save() throws DfException, IllegalAccessException {
String newObjectId = super.ourSave(this);
return newObjectId;
}
public String getAttr1() {
return attr1;
}
public void setAttr1(String attr1) {
this.attr1 = attr1;
}
}
|
How the code works
Let's take a look at what we are doing here.
This annotation is on a class level and it tells us what Documentum type this class maps to.
@DctmType(documentumType = "our_type")
|
The definition for this annotation is :
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface DctmType {
String documentumType();
}
|
Next we map the fields of the class to the attribute of the Documentum object type “our_type" which was defined on the top of the class:
@DctmAttribute(attributeName="our_id",attributeType=DctmAttribute.DctmDataType.STRING,isRepeating=false)
private String attr1;
|
This annotation on the field level tells us what attribute of the type does this field map to. Let's dissect this further:
attributeName – tells us that the name of the attribute this maps to
attributeType - Tell us what data type the attribute is
isRepeating - Tell us whether this value is repeating or single
If the value is a repeating value, this would be represented as an array.
For example
@DctmAttribute(attributeName="our_repeating",attributeType=DctmAttribute.DctmDataType.STRING,isRepeating=true)
private String [] attr2;
|
The definition of this annotation is as follows:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface DctmAttribute {
// Documentum attribute
public enum DctmDataType{ INT,STRING,DOUBLE,ID,TIME};
// name of the attribute
String attributeName();
// Type of data for the attribute
DctmDataType attributeType() default DctmDataType.STRING;
// is repeating or single
boolean isRepeating();
}
|
Back to the code. If you closely look at the class, it extends from ourDctmObject. This class has all the functionality of iterating over the annotations and fields, using the reflection api to achieve this. This class also defines a version of save which actually saves the attributes on the object and returns an objectID, which can be later used by any downstream methods to apply some other attributes like the ACL or the owner. Essentially by using annotations we have reduced the code of the save from this:
sysobject.setString("my_id",this.getAttr1());
sysobject.save();
|
to this:
String newObjectId = super.ourSave(this);
|
We are passing the instance of our object to ourDctmObject. Although right now we have used only one attribute on our custom type, let's assume I had 60 attributes to be save on a object. How would the former approach look ? I don’t want to go there as we know how it would have looked, and don’t forget all the repetitions in all the other places. Also imagine what happens if you have an attribute change. With the former approach you have to do a search and replace on all your code base. With the latter approach it's just one line where you change the annotation. The other advantage of this code is that the complexity and the persistence is housed in one place. This way any bugs fixes or enhancements can be done more easily. One last advantage of this approach is that if a new attribute is added to the new type, its just couple of lines of code to add the attribute and define the mapping via annotations. We don’t have to write any other piece because persisting this value is already taken care off by the method ourSave().
The snippet of ourSave() follows. This is the only piece of code you will be need throughout out the system for persisting types to the Docbase.
protected String ourSave(Object ourClass) throws DfException,
IllegalAccessException {
String objectID = null;
Class<?> c = ourClass.getClass();
IDfSysObject objectWithOurType = null;
boolean classAnnotationFound = false;
Field[] arrayOfFields = c.getDeclaredFields();
Annotation[] classAnnotations = c.getAnnotations();
// check for Anotations
if (classAnnotations.length > 0) {
Annotation classAnnotation = classAnnotations[0];
// Get the Documentum object type from the class annotation
// we only process the annotation of this type. We don't care for
// others
if (classAnnotation instanceof DctmType) {
DctmType type = (DctmType) classAnnotation;
objectWithOurType = this.createourType(type.documentumType());
classAnnotationFound = true;
}
}
// Only execute this if we found a annotation for the class which maps
// to a documetum object type
if (classAnnotationFound) {
// get all the fields of the class
for (Field f : arrayOfFields) {
Annotation[] annotations = f.getAnnotations();
// set this to true so that you can access the values of private
// fields
f.setAccessible(true);
// iterate over the annotations to get the attributes
for (Annotation a : annotations) {
// we are for only DctmAttribute
if (a instanceof DctmAttribute) {
DctmAttribute dctmAttributeAnon = (DctmAttribute) a;
// check if it is repeating
if (!dctmAttributeAnon.isRepeating()) {
// check the type
if (dctmAttributeAnon.attributeType().equals(
DctmAttribute.DctmDataType.STRING)) {
// get the value of the field and set it
objectWithOurType.setString(dctmAttributeAnon
.attributeName(), f.get(ourClass)
.toString());
}
if (dctmAttributeAnon.attributeType().equals(
DctmAttribute.DctmDataType.DOUBLE)) {
// get the value of the field and set it
objectWithOurType
.setDouble(dctmAttributeAnon
.attributeName(), f
.getDouble(ourClass));
}
if (dctmAttributeAnon.attributeType().equals(
DctmAttribute.DctmDataType.INT)) {
// get the value of the field and set it
objectWithOurType.setInt(dctmAttributeAnon
.attributeName(), f.getInt(ourClass));
}
} else {
// for non-repeating fields
if (dctmAttributeAnon.attributeType().equals(
DctmAttribute.DctmDataType.STRING)) {
setDataOnObject(dctmAttributeAnon, f,
objectWithOurType, ourClass);
}
if (dctmAttributeAnon.attributeType().equals(
DctmAttribute.DctmDataType.DOUBLE)) {
setDataOnObject(dctmAttributeAnon, f,
objectWithOurType, ourClass);
}
if (dctmAttributeAnon.attributeType().equals(
DctmAttribute.DctmDataType.INT)) {
setDataOnObject(dctmAttributeAnon, f,
objectWithOurType, ourClass);
}
if (dctmAttributeAnon.attributeType().equals(
DctmAttribute.DctmDataType.ID)) {
setDataOnObject(dctmAttributeAnon, f,
objectWithOurType, ourClass);
}
}
} else {
continue;
}
}
}
}
if (objectWithOurType != null) {
objectWithOurType.save();
System.out.println(objectWithOurType.dump());
objectID = objectWithOurType.getObjectId().toString();
}
return objectID;
}
|
Download
I have included all the code with this article. Please feel free to modify it to suit your needs. Click here to download the code samples.
Enhancements
I didn’t have time to implement the persistence of datetime attributes on a Documentum object. It would be a good enhancement to the project because at this moment that’s the only missing piece to the project before this can be used in production
Software Requirements
- JDK 5.0 or above
- Documentum DFC 5.3 SP4 or above
References
About the Author
Harjit Singh has over 10 years experience as a software engineer, and has been assisting customers with technical and business solutions based on Documentum for over 3 years. His expertise includes Java, C++, J2EE,WDK,MTS,DFC,Server Methods and Web Services.Harjit currently works as an independent consultant, focusing on ECM solutions. His clients have included some of the leading names in finance, health and publishing. Harjit Singh has also contributed an extension to the Repository Interrogation Utility on EDN. He can be reached at har_jit@yahoo.com.
Discuss
Click here to discuss this article
|