Thursday, March 29, 2012

Ant task for update the jar files in Documentum


“You do not really understand something unless you can explain it to your grandmother”
Albert Einstein.


This quote pushed me to start this blog. Why I'm writing the blog in English? The answer is quite simple, to improve my English :)
These articles will include two points Requirement (the issue) and Solution (how to solve the issue). So let's begin.


Requirement:
Update the jar files in Documentum repository immediately after it is created.


Solution:
We all know that for update a documentum module TBO, SBO it is enough just to checkout - checkin the jar file inside the module folder, yes there are a lot of mode to do that: DA, WebTop, Composer (that usually fails), ... but the requirement is to update it automatically in repository after it is created. I have decide to put this procedure immediately after the jar is created, of course in Ant task.
The Ant task in build.xml will look like that:

<!-- Update jarfiles in docbase -->
              <checkoutcheckin>
                     <docbaseobject jarfile=".dist/ex_interfaces.jar" jarobject="ex_interfaces.jar" />
                     <docbaseobject jarfile="./dist/ex_impl.jar" jarobject="ex_impl.jar" />
              </checkoutcheckin>

It looks very nice isn’t it?
First of all we need to declare Ant custom CheckOutCheckIn class extends Task class which will do the given job.

public class CheckOutCheckIn extends Task {
      
@Override
       public void execute() throws BuildException {
              // TODO Auto-generated method stub
              super.execute();
       }
}

Yes, you have notice correctly, the execute() method is the method which shall do the job. Our execute() method will be quite simple:

@Override
       public void execute() throws BuildException {
              String classPath = System.getProperties().getProperty("java.class.path", null);
              log("Class Path: " + classPath);
              try {
                     initSession();
                     doCheckout();
                     doCheckin();
              } catch (DfException e) {
                     throw new BuildException(e);
              } catch (Exception e) {
                     throw new BuildException(e);
              } finally {
                     releaseSession();
              }
       }

That’s not enough, we need a way to map the jar files and the docbase object. For that I have create an inner bean class which will contain a jarFile/object pair inside.

       public static class DocBaseObject extends Task {
              private String jarfile;
              private String jarobject;

              public DocBaseObject() {
              }

              public DocBaseObject(String file, String object) {
                     jarfile = file;
                     jarobject = object;
              }

              public String getJarfile() {
                     return jarfile;
              }

              public void setJarfile(String localfile) {
                     this.jarfile = localfile;
              }

              public String getJarobject() {
                     return jarobject;
              }

              public void setJarobject(String docbaseObject) {
                     this.jarobject = docbaseObject;
              }

       }

This class also extends Ant Task class because we will use it like inner parameter to the main Ant task. So our class now looks like this:

public class CheckOutCheckIn extends Task {
      
@Override
       public void execute() throws BuildException {
              String classPath = System.getProperties().getProperty("java.class.path", null);
              log("Class Path: " + classPath);
              try {
                     initSession();
                     doCheckout();
                     doCheckin();
              } catch (DfException e) {
                     throw new BuildException(e);
              } catch (Exception e) {
                     throw new BuildException(e);
              } finally {
                     releaseSession();
              }
       }

       public static class DocBaseObject extends Task {
              private String jarfile;
              private String jarobject;

              public DocBaseObject() {
              }

              public DocBaseObject(String file, String object) {
                     jarfile = file;
                     jarobject = object;
              }

              public String getJarfile() {
                     return jarfile;
              }

              public void setJarfile(String localfile) {
                     this.jarfile = localfile;
              }

              public String getJarobject() {
                     return jarobject;
              }

              public void setJarobject(String docbaseObject) {
                     this.jarobject = docbaseObject;
              }

       }

}
It remains only to add implementation to all methods. Let’s start with inititSession() method:

              private void initSession() throws Exception {
              URL urlPath = null;
              //getting ClassLoader for load dfc.properties
              ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
              if (classLoader != null) {
                     urlPath = classLoader.getResource("dfc.properties");
              }
              if (urlPath == null) {
                     urlPath = CheckOutCheckIn.class.getResource("dfc.properties");
              }
              if (urlPath == null) {
                     urlPath = CheckOutCheckIn.class.getClassLoader().getResource("dfc.properties");
              }
              log("dfc.properties path:" + urlPath.getPath());
             
              //set the dfc.properties.file parameter
              System.setProperty("dfc.properties.file", urlPath.getPath());
             
              //get config.properties file for initiate user name, password and docbase name
              ResourceBundle ownerBundle = ResourceBundle.getBundle("config");
              String docbaseName = ownerBundle.getString("docbase");
              String userName = ownerBundle.getString("username");
              String password = ownerBundle.getString("password");
              log("Creating session manager docbase: " + docbaseName + " ...");
              sMgr = createSessionManager(docbaseName, userName, password);
              log("Getting session from docbase " + docbaseName + " (might take some seconds) ...");
              session = sMgr.getSession(docbaseName);
              log("Session oppened.");
       }

The files config.properties and dfc.properties should be in class path, this can be done when the Ant task is declared in build.xml.

       <taskdef name="checkoutcheckin" classname="com.example.ant.CheckOutCheckIn">
              <classpath>
                     <pathelement location="${proj.location}/config/${build.env}" />
                     <pathelement location="${proj.location}/dist/checkoutcheckin.jar" />
              </classpath>
       </taskdef>

The parameter ${build.env} can has the values (dev,test …) like you can see below.
Folder structure in Project:

















The content of property file config.properties might be something like that:

docbase=DEV
username=dmadmin
password=dmadmin

The content of dfc.properties file I think is not necessary to show. :)
I have chosen this method to set environment because in our project there are already these folders.
Let’s go back to class, we have to add implementation to all methods. Next method is called doCheckout():

       private void doCheckout() throws DfException {
              log("Checkout start ...");
              IDfClientX clientx = new DfClientX();
              IDfCheckoutOperation operation = clientx.getCheckoutOperation();
              //iterate over all <docbaseobject> inner elements from Ant task
              for (DocBaseObject docbaseObj : docbaseobjects) {
                     IDfSysObject sysObj = (IDfSysObject) session.getObjectByQualification("dmc_jar where object_name='" + docbaseObj.getJarobject() + "'");
                     if (sysObj == null) {
                           log("Object: " + docbaseObj.getJarobject() + " can not be found.");
                           continue;
                     }
                     if (sysObj.isCheckedOut() == true) {
                           log("Object: " + docbaseObj.getJarobject() + " is already checked out.");
                           continue;
                     }
                     operation.add(sysObj);
                     log("Object: " + docbaseObj.getJarobject() + " added to Checkout Operation.");
              }
              executeOperation(operation);
              log("Checkout end.");
       }

       private void executeOperation(IDfOperation operation) throws DfException {
              boolean executeFlag = operation.execute();
              //if the operation fails then log all errors
              if (!executeFlag) {
                     IDfList errorList = operation.getErrors();
                     String message = "";
                     IDfOperationError error = null;

                     for (int i = 0; i < errorList.getCount(); i++) {
                           error = (IDfOperationError) errorList.get(i);
                           message += error.getMessage();
                     }
                     log("Errors:" + message);
              } else {
                     log("Checkout Operation ends successful");
              }
       }

I choose IDfCheckoutOperation to checkout all the objects simultaneously. But for doCheckin() operation I do not choose this way because the jar files might be in different folders. So the doCheckin() method is:

       private void doCheckin() throws DfException {
              log("Checkin start ...");
              //iterate over all <docbaseobject> inner elements
              for (DocBaseObject docbaseObj : docbaseobjects) {
                     IDfSysObject sysObj = (IDfSysObject) session.getObjectByQualification("dmc_jar where object_name='" + docbaseObj.getJarobject() + "'");
                     if (sysObj == null) {
                           log("Object: " + docbaseObj.getJarobject() + " can not be found.");
                           continue;
                     }
                     if (sysObj.isCheckedOut() == false) {
                           log("Object: " + docbaseObj.getJarobject() + " is not checked out.");
                           continue;
                     }
                     sysObj.setFile(docbaseObj.getJarfile());
                     sysObj.checkin(false, "CURRENT");
                     log("Object: " + docbaseObj.getJarobject() + ", file: " + docbaseObj.getJarfile() + " checked in.");
              }
              log("Checkin end.");
       }

Finally the class will be:

package com.example.ant;

import java.net.URL;
import java.util.List;
import java.util.ResourceBundle;
import java.util.Vector;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;

import com.documentum.com.DfClientX;
import com.documentum.com.IDfClientX;
import com.documentum.fc.client.IDfClient;
import com.documentum.fc.client.IDfSession;
import com.documentum.fc.client.IDfSessionManager;
import com.documentum.fc.client.IDfSysObject;
import com.documentum.fc.common.DfException;
import com.documentum.fc.common.IDfList;
import com.documentum.fc.common.IDfLoginInfo;
import com.documentum.operations.IDfCheckoutOperation;
import com.documentum.operations.IDfOperation;
import com.documentum.operations.IDfOperationError;

public class CheckOutCheckIn extends Task {

       private List<DocBaseObject> docbaseobjects = new Vector<DocBaseObject>();
       public static IDfSessionManager sMgr = null;
       public static IDfSession session = null;

       public void addDocbaseobject(DocBaseObject docbaseobject) {
              docbaseobjects.add(docbaseobject);
       }

       @Override
       public void execute() throws BuildException {
              String classPath = System.getProperties().getProperty("java.class.path", null);
              log("Class Path: " + classPath);
              try {
                     initSession();
                     doCheckout();
                     doCheckin();
              } catch (DfException e) {
                     throw new BuildException(e);
              } catch (Exception e) {
                     throw new BuildException(e);
              } finally {
                     releaseSession();
              }
       }

       private void initSession() throws Exception {
              URL urlPath = null;
              //getting ClassLoader for load dfc.properties
              ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
              if (classLoader != null) {
                     urlPath = classLoader.getResource("dfc.properties");
              }
              if (urlPath == null) {
                     urlPath = CheckOutCheckIn.class.getResource("dfc.properties");
              }
              if (urlPath == null) {
                     urlPath = CheckOutCheckIn.class.getClassLoader().getResource("dfc.properties");
              }
              log("dfc.properties path:" + urlPath.getPath());

              //set the dfc.properties.file parameter
              System.setProperty("dfc.properties.file", urlPath.getPath());

              //get config.properties file for initiate user name, password and docbase name
              ResourceBundle ownerBundle = ResourceBundle.getBundle("config");
              String docbaseName = ownerBundle.getString("docbase");
              String userName = ownerBundle.getString("username");
              String password = ownerBundle.getString("password");
              log("Creating session manager docbase: " + docbaseName + " ...");
              sMgr = createSessionManager(docbaseName, userName, password);
              log("Getting session from docbase " + docbaseName + " (might take some seconds) ...");
              session = sMgr.getSession(docbaseName);
              log("Session oppened.");
       }

       private void releaseSession() {
              sMgr.release(session);
              log("Session closed.");
       }

       private IDfSessionManager createSessionManager(String docbase, String user, String pass) throws Exception {
              IDfClientX clientx = new DfClientX();
              IDfClient client = clientx.getLocalClient();
              IDfSessionManager sMgr = client.newSessionManager();
              IDfLoginInfo loginInfoObj = clientx.getLoginInfo();
              loginInfoObj.setUser(user);
              loginInfoObj.setPassword(pass);
              loginInfoObj.setDomain(null);
              sMgr.setIdentity(docbase, loginInfoObj);
              return sMgr;
       }

       private void doCheckout() throws DfException {
              log("Checkout start ...");
              IDfClientX clientx = new DfClientX();
              IDfCheckoutOperation operation = clientx.getCheckoutOperation();
              //iterate over all <docbaseobject> inner elements
              for (DocBaseObject docbaseObj : docbaseobjects) {
                     IDfSysObject sysObj = (IDfSysObject) session.getObjectByQualification("dmc_jar where object_name='" + docbaseObj.getJarobject() + "'");
                     if (sysObj == null) {
                           log("Object: " + docbaseObj.getJarobject() + " can not be found.");
                           continue;
                     }
                     if (sysObj.isCheckedOut() == true) {
                           log("Object: " + docbaseObj.getJarobject() + " is already checked out.");
                           continue;
                     }
                     operation.add(sysObj);
                     log("Object: " + docbaseObj.getJarobject() + " added to Checkout Operation.");
              }
              executeOperation(operation);
              log("Checkout end.");
       }

       private void executeOperation(IDfOperation operation) throws DfException {
              boolean executeFlag = operation.execute();
              //if the operation fails then log all errors
              if (!executeFlag) {
                     IDfList errorList = operation.getErrors();
                     String message = "";
                     IDfOperationError error = null;

                     for (int i = 0; i < errorList.getCount(); i++) {
                           error = (IDfOperationError) errorList.get(i);
                           message += error.getMessage();
                     }
                     log("Errors:" + message);
              } else {
                     log("Checkout Operation ends successful");
              }
       }

       private void doCheckin() throws DfException {
              log("Checkin start ...");
              //iterate over all <docbaseobject> inner elements
              for (DocBaseObject docbaseObj : docbaseobjects) {
                     IDfSysObject sysObj = (IDfSysObject) session.getObjectByQualification("dmc_jar where object_name='" + docbaseObj.getJarobject() + "'");
                     if (sysObj == null) {
                           log("Object: " + docbaseObj.getJarobject() + " can not be found.");
                           continue;
                     }
                     if (sysObj.isCheckedOut() == false) {
                           log("Object: " + docbaseObj.getJarobject() + " is not checked out.");
                           continue;
                     }
                     sysObj.setFile(docbaseObj.getJarfile());
                     sysObj.checkin(false, "CURRENT");
                     log("Object: " + docbaseObj.getJarobject() + ", file: " + docbaseObj.getJarfile() + " checked in.");
              }
              log("Checkin end.");
       }

       public static class DocBaseObject extends Task {
              private String jarfile;
              private String jarobject;

              public DocBaseObject() {
              }

              public DocBaseObject(String file, String object) {
                     jarfile = file;
                     jarobject = object;
              }

              public String getJarfile() {
                     return jarfile;
              }

              public void setJarfile(String localfile) {
                     this.jarfile = localfile;
              }

              public String getJarobject() {
                     return jarobject;
              }

              public void setJarobject(String docbaseObject) {
                     this.jarobject = docbaseObject;
              }

       }
}

The last thing we should do is to package this in jar file “checkoutcheckin.jar” to declare the task in build.xml and “voila” the job is done. ;)
Here an example how I use it.

<!-- DFC classpath -->
       <path id="dfc.classpath">
              <pathelement location="${dfc.path}/dfc.jar" />
             
              …
       </path>

Class com.saipem.dams.ant.CheckOutCheckIn is using DFC classes, so we need to add dfc classes to classpath too.

<!—Checkin checkout task definition-->
       <taskdef name="checkoutcheckin" classname="com.saipem.dams.ant.CheckOutCheckIn">
              <classpath>
                     <pathelement location="${dams_web.location}/config/${build.env}" />
                     <pathelement location="${dams_tools.location}/dist/checkoutcheckin.jar" />
              </classpath>
<classpath refid="dfc.classpath" />
       </taskdef>

       <!-- Compiles SBO interfaces-->
       <target name="compile-sbo-interfaces">
              <echo message="Compiling SBO interfaces ..." />
              <delete dir="${sbo.location}/build/interfaces" />
              <mkdir dir="${sbo.location}/build/interfaces" />
              <javac destdir="${sbo.location}/build/interfaces" excludes="com/example/sbo/impl/*.java" debug="true" debuglevel="${debuglevel}" source="${source}" target="${target}" encoding="8859_1">
                     <src path="${sbo.location}/src" />
                     <classpath refid="dfc.classpath" />
              </javac>
              <delete dir="${sbo.location}/dist/interfaces" />
              <mkdir dir="${sbo.location}/dist/interfaces" />
              <zip destfile="${sbo.location}/dist/interfaces/interfaces.jar" encoding="UTF8" duplicate="preserve">
                     <zipfileset dir="${sbo.location}/build/interfaces" includes="**/*.class" />
              </zip>

              <checkoutcheckin>
                     <docbaseobject jarfile="${sbo.location}/dist/interfaces/interfaces.jar " jarobject="SBO_example_interfaces" />
              </checkoutcheckin>

       </target>

Thank you for your attention, will see on next article ;) Feel free to comment.