Category Archives: composer

Jar defs and Java libraries

The jars that you use in BOF modules can be delivered in Jar Defs or in Java Libraries. Let’s look at the differences, real and conventional.

Jar Defs (dmc_jar instances in the docbase) are just jar wrappers. They have a nature – implementation or interface – that determines to which class loader they belong. Here’s something that is not obvious – you should not share Jar Defs across modules. We don’t enforce this restriction well enough, but we don’t support this usage. If you want to share jars across modules you should use a Java Library. Read on.

Java Libraries (dmc_java_library instances) are collections of one or more jars. Each Java Library is flagged as being sandboxed or not. All jars in a sandboxed Java Library will belong to the module class loader, just like regular implementation jars. All jars in a non-sandboxed Java Library will belong to the Shared Class Loader. Java Libraries can be shared across modules. So they have two purposes:

  • Sharing jars across modules
  • Collecting jars into an administrable unit

Some good uses for Java Libraries are:

  • third party libraries
  • shared logic across a cohesive set of modules

Be very very careful about marking a Java Library non-sandboxed. Because this associates the jars with the Shared Class Loader all BOF modules will have visibility to these classes. The original plan was not to allow this at all, but the Architect broke under pressure. This is back to the kind of jar hell that BOF tries so hard to eliminate.

If you decide to share jars (via Java Libraries) across modules be sure to understand that a sandboxed Java Library will be associated with the module class loaders. If you intend to communicate between your modules then any interfaces that exist in your shared jars will have a separate identity in each class loader, so if you try to use them across modules you will get ClassCastExceptions. If you want to do this then you need to separate out your shared interfaces from the other classes in your shared jars and put them in a distinct non-sandboxed Java Library. This will allow the interfaces to be used by all of your modules. This is a much better idea than just marking your existing Java Library as non-sandboxed which will have the effect of introducing all your implementation classes into the Shared Class Loader too.

Both Jar Defs and Java Libraries can be hot deployed. Because a Jar Def is associated with only one module its hot deployment has a localized effect. If a Java Library is shared, on the other hand, its hot deployment affects all modules that share the Java Library.

Jar Defs should always belong to the containing module so they will appear in the same Composer project as the module itself.

Java Libraries can be shared and so projects containing consuming modules might need to reference another Composer project in order to access the Java Library. That referenced project might be a “primary” module project, or conceivably a project that contains only the Java Library – the choice is yours. The important thing is that the Java Library must exist in only one Composer project.

The things to consider when making choices are hot deployment issues, class visibility issues, and avoiding redundant jars wherever possible.

Configuring a deployed BOF module

Sometimes you might want a BOF module to be configured externally post deployment. You might want to use an XML file or a properties file for this purpose.

The naive way would be to checkout the implementation jar, unzip it, edit the config file, rezip it, and check in the updated jar. That’s too horrible to contemplate, for too many reasons to list.

There is another way. BOF anticipated the need for external, non-jar files to be accessible in the module classpath. Such external files are called downloadable attachments. Attachments in general are any non-jar files that the module author wants to be deployed with the module – say a javadoc zip, or license files. Typically you want these in the docbase, but you don’t want them in the module classpath. If you flag an attachment as downloadable though, it goes all the way to the class loader.

So, if you want a config file to be externally accessible and yet visible in the classpath you make it a downloadable attachment. In Composer you would go to your module’s “Deployment” tab, add the file as an attachment and check the downloadable option.

Downloadable Attachment in Composer

Downloadable Attachment in Composer

Then, at any time post deployment, you just need to check out, edit, and check back in the config file. The updated version will be hot deployed to the BOF module. You have to control the security of the attachment, but the rest of your module preserves its integrity.

Remember that hot deployment is only revealed to subsequent module instantiations, so don’t hold a cached reference to your module or you won’t see any updates. Prefer newModule or newService on demand – they’re quick.

What’s a Smart Container?

In Documentum 6.5 release there is a new capability called Smart Container. What’s that about?

In many applications there is a need for the repeated construction of composite objects. For example, in a police application there might be an incident folder which contains a subfolder for affidavits, another for officer reports, perhaps a structured interview form. Typically you would program the construction of this composite structure by hand using DFC or DFS primitives. This can take considerable time and requires a strong skillset. Smart Containers offer a way to declaratively specify a template for such composite structures and provides a runtime factory that observes the declarative specification.

In Composer there is a graphical editor with which you can define your template (give it a try). The template is built in terms of objects and relationships. Once you have completed your template you can install it into the docbase using normal Composer install procedures. The result will be an instance of dmc_class whose content is an XML representation of the template. Attached to the object is an aspect that contains the logic to interpret the template. In effect it is a factory object.

In order to construct a new composite, as declared in your template, you would do the following …


IObjectFactory factory = (IObjectFactory) session.getObjectByQualification("dmc_class where object_name='myTemplateName'");
IDfPersistentObject myCompositePrimaryObject = factory.newObject();
myCompositePrimaryObject.save();

That’s it. There is no more code.

You get the primary object returned and all the others are linked (in folders or by dm_relations) according to your declarative template once you save the primary object.

IObjectFactory also has a signature that allows you to provide a Map of parameter values if your template is parameterized. Template parameterization is important to allow for runtime context – the identity of a Captiva-scanned initial incident report maybe.

The IObjectFactory interface is defined in the dmc_class.jar which you can extract from the dmc_class TBO module in the docbase.

Composer on a Mac

Composer is not officially supported on the Mac, but since I develop on a Mac I make sure it works. However, there is no installation prebuilt, so you have to set it up manually.

Here is my recommendation for how to do this…

  1. Download Eclipse IDE for Java EE Developers from the Eclipse 3.4 site (Ganymede – not SR1)
  2. Go to Help/Software Updates, go to the Ganymede update site and install Enabling Features/EMF Validation Framework
  3. Unzip your Windows zip of Composer
  4. Copy all com.emc.* jars and folders from its plugins folder into your Eclipse’s dropins folder (not plugins)
  5. You are now ready to configure your instance as if it were a regular installation (except that the plugins are in the dropins folder)

Note that there is restricted functionality because no command line executables work (like dmbasic), but you can do all design time activities just fine. Also note that the Core Project won’t be read only, but you should treat it as such. You are not allowed to modify it – there be dragons.

Incidentally, these instructions work for integration of Composer plugins into any Eclipse 3.4 platform – not just the Mac.

Why would I use BOF aspects?

Aspects come in three flavors – behavior only, state only, and a combination of behavior and state.

The behavior can override methods of DfSysObject or of other aspects and/or TBOs. It can also add orthogonal interfaces to an object.

The state is like a mini-type, with attributes defined similarly. Once attached, these attributes are accessible using a fully qualified attribute name of the form <aspect-name>.<attribute-name>. Note that the aspect name must conform to Documentum type naming conventions.

An aspect can have either or both of behavior and state, but it must have one! By the way, for a stateful aspect with no behavior you must still include a “null” aspect module artifact in Composer. This is counterintuitive but necessary. The reverse is not true. If you have a behavior only aspect you must not include an aspect type artifact. So the rule is: an aspect module artifact is always required, and an aspect type artifact must only exist for a stateful aspect.

Aspects are fundamentally an instance-based mechanism. An aspect is attached to a persistent object instance. There is a way to associate an aspect with every instance of a type, but the mechanism is still an instance-based one. The name of each aspect that is attached to an instance is stored in a repeating string attribute – r_aspect_name.

Now there are two fundamental types of usage for aspects. The first is to exploit its instance orientation by attaching behavior and/or state to particular instances. A classic example of this would be retention.

The second type of use is to build up types by composition rather than solely inheritance. Using the means to associate default aspects with types you can build up a type from orthogonal aspects. We have gone to some trouble to make this as efficient as possible, with non-qualifiable attributes and so forth, but you should still be aware of the overheads. Qualifiable attributes (ones that can be used in a DQL where clause) require joins on update, and each default aspect name appears in the repeating r_aspect_name attribute.

We are encouraging people to exploit type composition going forward. Despite the overheads it has many advantages, such as avoiding deep or contrived or redundant type hierarchies. Consider using type composition whenever the performance profile is adequate.

Why do I have to package other module’s interfaces in BOF?

If you are writing a BOF module (TBO, SBO, aspect or simple module) that uses another module directly then you have to include the used module’s interface jars in your own module definition (in Composer). This doesn’t feel right does it?

It shouldn’t – it’s unfortunate – but you have to do it nonetheless. The reason is that BOF modules are downloaded on demand. If you don’t package them within your own module, and your module is requested before the used module (unpredictable) then the interfaces would not be visible and your module would fail to load (NoClassDefFoundException). Since BOF doesn’t support transparent versioning of interfaces (only implementations) this isn’t as much of a problem as it looks. It’s just a nuisance. It’s no worse than regular client code, for example, which always has to package the interfaces it uses.

You have to be diligent about this though. It might not turn up in testing if the used module is serendipitously requested first by some other player.

BOF jars and class loading

BOF uses a custom class loader system which relies very much on correct packaging of jars in the module definition.

Jars must be categorized in Composer as interface jars, implementation jars or mixed jars (don’t use this category). Interface jars are loaded into a common class loader, which makes its classes visible to all other modules (but nobody else). Implementation and mixed jars are always loaded into module-specific class loaders and that class loader is a “parent last” class loader – that is, it prefers its own classes to those in any other class loader. If you miscategorize a jar, or put a class in an inappropriate jar your module won’t work. Fortunately, it’s easy to explain what belongs where.

Typically, most code belongs in one or more implementation jars, in particular the primary class. This will be hidden from the rest of the world. This has the advantage that you cannot screw up anybody else by including classes in implementation jars.

That leaves interface jars – what goes in them? Not all interfaces need go into an interface jar – but all public interfaces should. Also any exceptions or factories that appear in your module’s public interface. You should be careful, in exceptions, to avoid transitively exposing other classes. Interface jars are loaded into a common (per DFC instance) class loader that is visible to every module, but not to your client code. These same interface jars must be packaged with any true client code that accesses your module. Note that not all modules require a public interface at all – for example a TBO might just override a method from DfSysObject and not have an interface at all.

So, you must get into the habit of packaging your code into interface and implementation jars. You might also want to use java libraries. These are just collections of jars that can be shared across modules. Each java library can be flagged as sandboxed or not. Jars from a sandboxed java library are loaded into the module-specific class loaders whereas jars from a non-sandboxed java library are loaded into the same class loader as the interface jars (see BOF class loaders).

In order to facilitate the packaging of classes into interface and implementation jars we recommend that you use separate source directories. The alternative of using ant scripts to do the packaging on a class by class basis is fraught with risk and difficult to diagnose errors.