Pages

Tuesday, January 3, 2017

Using custom subclasses of DfSysObject

Sometimes it might be desirable to replace IDfSysObject with a subclass in which some methods are disabled or replaced by custom logic.

Suppose a method has a documentum object as an input argument. The method directly and indirectly passes the object to many others methods that each invokes some exposes by IDfSysObject interface methods of the object. You want that all methods are always successfully executed but, depending on the settings or an input argument e.g. saveEnabled, you do not want to allow linking, saving, checking out or deleting the input object to occur.

One solution would be to add additional boolean argument saveEnabled to all the involved methods and insert if condition blocks before each invocation of save, link, checkout or destroy methods on the input object.

A less intrusive so solution is to use as the argument an instance DfSysObject subclass in which specifically only link, destroy, checkout and save methods are empty and do nothing. Then you would need to add only one if condition verifying whether save is enabled. Furthermore, all the involved methods remain unaltered. You can start from a method optionally generating such objects:

  void createNewObject(IDfSession session, String objectType, boolean saveEnabled) throws DfException {
    IDfSysObject obj;
    if (saveEnabled) {
      obj = (IDfSysObject) session.newObject(objectType);
    } else {
      obj = (IDfSysObject) newCustomObject(session, objectType);
    }
  }

If saveEnabled argument is true, the method will produce regular IDfSysObject, otherwise it will produce an instance of the custom subclass with some methods disabled.

Custom IDfSysObjects are produced by the following method:

  IDfSysObject newCustomObject(IDfSession sessionIDf, String typeName) throws DfException {
    ISession session = (ISession) sessionIDf;

    ILiteType type = session.getLiteType(typeName, null);

    IDfId objectId = session.getDocbase().getObjectIdManager().getNextId(session, type);
    ITypedData typedData = new TypedData(type, objectId);

    return makeCustomObject(session, typedData);
  }

  IDfSysObject makeCustomObject(ISession session, ITypedData typedData) throws DfException {
    DfSysObject object = new CustomDfSysObject();
    object.initialize(session.getObjectManager().getObjectFactory(), typedData, session, session, true);
    return object;
  }

So produced objects are totally valid and capable objects retaining connection to the docbase.

Last, custom subclass CustomDfSysObject have to be defined:

// it's up to you which methods to override
public class CustomDfSysObject extends DfSysObject {

  Logger logger = LoggerFactory.getLogger(getClass().getName());

  @Override
  public void save() throws DfException {
    logger.info("fake save");
  }

  @Override
  public IDfId checkoutEx(final String versionLabel, final String compoundArchValue, final String specialAppValue) throws DfException {
    logger.info("fake checkout");
    return DfId.DF_NULLID;
  }

  @Override
  public void destroy() throws DfException {
    logger.info("fake destroy");
  }

  @Override
  public void link(String folderSpec) throws DfException {
    logger.info("fake link");
  }
}

The objects generated as described above are empty. If you need an object filled with data, i.e. an exact copy of an object saved to the docbase, you can use a similar approach whereby the data is loaded from the docbase.

  IDfSysObject getCustomObject(IDfSession sessionIDf, String objectId) throws DfException {
    ISession session = (ISession) sessionIDf;
    final ITypedData data = session.getDataManager().getData(new DfId(objectId), new DfGetObjectOptions(), true, false);
    return makeCustomObject(session, data);
  }

Now let's test the generated objects:

public class CustomObjectFactoryTest {

  @BeforeClass
  public static void setUp() throws DfException {
    session = ConnectionFactory.getSession();
  }

  @AfterClass
  public static void tearDown() throws DfException {
    session.disconnect();
  }
  CustomObjectFactory i = new CustomObjectFactory();
  static IDfSession session;

  @Test
  public void test() throws DfException {

    String typeName = "testtype";
    // create an empty object of custom class
    IDfSysObject newCustomObj = i.newCustomObject(session, typeName);
    assertEquals(newCustomObj.getClass().getName(), CustomDfSysObject.class.getName());
    // test that the overriden methods do nothing
    newCustomObj.destroy();
    newCustomObj.save();
    newCustomObj.link(null);
    newCustomObj.checkout();

    // now let's retrieve existing object so that it is instantiated as the custom class
    String existingObjectId = "090f4241800c2507";
    // retrieve regular object
    IDfSysObject existingObj = (IDfSysObject) session.getObject(new DfId(existingObjectId));
    // retrieve custom object
    IDfSysObject existingCustomObj = i.getCustomObject(session, existingObjectId);
    // assert that their types are diffent
    assertEquals(existingCustomObj.getClass().getName(), CustomDfSysObject.class.getName());
    assertFalse(existingCustomObj.getClass().getName().equals(existingObj.getClass().getName()));
    // assert that their values are the same
    compareAllAttributeValues(existingObj, existingCustomObj);
    // test that the overriden methods do nothing
    existingCustomObj.destroy();
    existingCustomObj.save();
    existingCustomObj.link(null);
    existingCustomObj.checkout();

    // proof that the custom objects retain connection to the docbase
    String newName = "NewName " + System.nanoTime();
    existingObj.setObjectName(newName);
    existingObj.save();
    assertFalse(existingCustomObj.getObjectName().equals(newName));
    existingCustomObj.fetch(null);
    assertTrue(existingCustomObj.getObjectName().equals(newName));
  }

  void compareAllAttributeValues(IDfSysObject obj1, IDfSysObject obj2) throws DfException {
    for (int i = 0; i < obj1.getAttrCount(); i++) {
      IDfAttr attr = obj1.getAttr(i);
      String attrName = attr.getName();
      String attrValue1, attrValue2;

      if (attr.isRepeating()) {
        attrValue1 = obj1.getAllRepeatingStrings(attrName, "|");
        attrValue2 = obj2.getAllRepeatingStrings(attrName, "|");
      } else {
        attrValue1 = obj1.getString(attrName);
        attrValue2 = obj2.getString(attrName);
      }
      assertEquals(attrValue1, attrValue2);
    }
  }
}

The techniques described above allow optionally generating subclasses of DfSysObject. If you wished to always produce instances of some class for a particular documentum type the solution would be even more simple. There is a registry with all the documentum types and the corresponding java classes. You would need to register you custom class as the java class corresponding to the particular documentum type, so that documentum instantiates you class whenever object of the specified type is created or loaded for the docbase.

No comments:

Post a Comment