Groovy is great!

It is not only great mix of useful and convenient language constructs of Python, Ruby and other languages but it also turned out that it is a very good code obfuscator 🙂 Here is an example, code below:


    public static IDfSession getSession(String user,
        String password, String docbase) throws DfException {

        IDfClientX clientx = new DfClientX();
        IDfClient client = clientx.getLocalClient();
        IDfLoginInfo li = new DfLoginInfo();
        li.setUser(user);
        li.setPassword(password);
        return client.newSession(docbase, li);
    }

was translated to:


    public static IDfSession getSession(String user, String password, String docbase)
        throws DfException
    {
        CallSite acallsite[] = $getCallSiteArray();
        com.documentum.com.IDfClientX clientx = ((com.documentum.com.IDfClientX) (acallsite[0].callConstructor($get$$class$com$documentum$com$DfClientX())));
        IDfClient client = (IDfClient)ScriptBytecodeAdapter.castToType(acallsite[1].call(clientx), $get$$class$com$documentum$fc$client$IDfClient());
        com.documentum.fc.common.IDfLoginInfo li = ((com.documentum.fc.common.IDfLoginInfo) (acallsite[2].callConstructor($get$$class$com$documentum$fc$common$DfLoginInfo())));
        acallsite[3].call(li, user);
        acallsite[4].call(li, password);
        return (IDfSession)ScriptBytecodeAdapter.castToType(acallsite[5].call(client, docbase, li), $get$$class$com$documentum$fc$client$IDfSession());
    }

Awesome 🙂

“Fix Groovy Imports” in NetBeans

I was very frustrated with the fact that the Groovy & Grails plugin for NetBeans does not have the “Fix Imports” functionality: the option in the menu is simply grayed out. I started looking into the issue (and the NetBeans code…) and it turned out that the feature is actually implemented, it is just incorrectly connected to the menu option.

In order to fix it you have to define a new shortcut which will call the”Fix Groovy Imports” function. Just navigate to Tools/Options/Keymap and then change the shortcut as shown below:

That’s all 🙂

Data importer for CMIS repositories

When developing Stamper I missed a tool which would allow me to easily import some files into a repository. Alfresco provides import/export functionality through ACP files which can be used for that purpose but what about other repositories? What about Nuxeo, Documentum? Documentum has Composer/Application Builder, Nuxeo perhaps also has it’s own mechanisms, but I wanted one tool which would work with all those repositories without need to create a separate installation package for each system.

Hopefully there is a CMIS standard which can make this happen.

CMISetuper (working name :-)) will be a tool which will be able to:

  • connect to any repository which supports CMIS. This will be possible thanks to the OpenCMIS library.
  • import files
  • create folders
  • validate presence of types, objects, folders
  • modify content (replace, version etc.)
  • link and unlink objects from folders

Those actions will be described by a declarative language named SDL – Simple Declarative Language. Below is a fragment of a script:


execute {
    import-files "/myFiles" {
        "image1.jpg" cm_name="Some title" metasys_taken_on_date=2009/10/05
        "image2.jpg" metasys_color=false metasys_taken_on_date=2011/01/05
    }

    import-file "/Documents" "someotherfile.doc"
    delete-file "/Old document.doc" all-versions="true"

    replace-content "/Pictures/picture.jpg" "/tmp/newContent.jpg"
    update-properties "/Documents/someotherfile.doc" cm_title="another title"
    link-to-folder "/myHouse.jpg" "/myFiles"
    unlink-from-folder "myHouse.jpg" "/myFiles"
}

Basically the tool will execute scripts in a few stages:

  • pre-validate stage – this stage can check presence of types, files, folders etc. in repository and fail when they are present (or not).
  • prepare stage – at this stage some preparations can take place, for example creation of folders for which objects will be imported.
  • execute stage – this is the main stage which takes care of actual data importing, linking etc.
  • validation stage – the last stage which checks whether everything was imported correctly

Some global settings will be available which will control overwrite mode as well as whether to stop on errors.
I have also plan to implement rollback mode but perhaps not in the first version 🙂

At this moment I would say that around 60% of above functionality is implemented and I hope to have rest ready by end of this week, so stay tuned for updates!

If you have some new ideas please share them with me.

Disabling TBO (BOF v2) in Documentum 5.3+

Sometimes during development it is useful to disable a Typed Based Object (TBO). In Documentum 5.3 (and older) when Business Object Framework was at version 1 it was easy, all that has to be done was commenting out the TBO definition line in $DOCUMENTUM/config/dbor.properties. From now on the TBO will be disabled in that particular DFC instance.

In BOF version 2 things have complicated because dbor.properties is no longer used (or at least recommended), instead all BOF objects are defined in the repository as dmc_module object instances. There is an easy way to disable a TBO globally by renaming the dmc_module object in /System/Modules/TBO but it will disable that TBO for all users and sometimes when doing troubleshooting on, for example, production system it is desirable to disable TBO only on one client.

In order to disable TBO on a single client machine we have to do a trick. Each DFC instance has it’s own BOF cache, which usually is in $DOCUMENTUM/cache. We have to locate the dmc_jar implementing the TBO and replace it with a dummy one which will not contain any business logic. How to locate the right dmc_jar? Well, it requires some investigation, you can either use DQL to look for the r_object_id of dmc_jar or just check files in the cache folder and look for JAR which will contain class implementing given TBO.

Next step is to prepare a dummy TBO. Dummy TBO is in all cases almost the same, what changes is the name of the class and package name. Sometimes different TBO objects are implementing different interfaces (for example IDfDynamicInheritance is not a required one) so in worst case you can look it up in the sources (or decompile the original TBO JAR file) and see how it was declared.

Typical Java code is following:


package com.documentum.cvp.common;

import com.documentum.fc.client.DfDocument;
import com.documentum.fc.client.IDfBusinessObject;
import com.documentum.fc.common.DfException;
import com.documentum.fc.common.IDfDynamicInheritance;

public class CvpDocument extends DfDocument
    implements IDfBusinessObject, IDfDynamicInheritance
{

    public CvpDocument()
    {
    }

    public String getVersion()
    {
        return "1.0";
    }

    public String getVendorString()
    {
        return "Documentum";
    }

    public boolean isCompatible(String version)
    {
        return version.compareTo("1.0") == 0;
    }

    public boolean supportsFeature(String feature)
    {
        return false;
    }
}

When dealing with DfFolder just change DfDocument to DfFolder and it should be fine.

After compiling the Java class and replacing the old JAR in the cache client application has to be restarted. It is also worth mentioning that the cache is periodically refreshed so it is a good idea to check whether our dummy TBO implementation was not overwritten by the real one. From my experience I can say that it happens very rarely.

I have tested this approach and I can confirm that it works, it saved me a lot of troubles during data migration between systems using FirstDoc.