Deploying Action Handlers

You can store an action handler as a CodeModule object in a Content Engine object store, or, alternatively, you can specify the action handler in the Java™ Virtual Machine (JVM) classpath of the application server where the Content Engine is running. For testing an action handler, it is generally easier to update it when it is referenced in the JVM classpath. For deploying an action handler in an enterprise production environment, it is generally more efficient to store an action handler and any supporting libraries as a CodeModule object on the Content Engine. Code modules are automatically available when deploying the Content Engine to multiple application server instances, or moving your content metadata from one system to another. If you reference action handlers in the JVM classpath, you must manually distribute them to new systems.

However, in deciding how to deploy an action handler, you need to consider the above generalities along with the following code module characteristics:

This topic shows you how to reference action handlers and supporting libraries in the WebLogic and WebSphere classpaths, and how to create and update a code module that's stored in the Content Engine.

Referencing Action Handlers in the Classpath

For testing action handlers, it's generally easier to reference them in the classpath of your WebSphere or WebLogic application server rather than store them as CodeModule objects in the Content Engine. There are various ways of setting the classpath, as discussed in the documentation for your application server. This section suggests a few standard ways of doing it. However, for your particular application server configuration, you may have a better way of setting the classpath.

The following information for setting the classpath also applies to a common library that supports multiple CodeModule objects. For maintenance purposes, in either a testing or a production environment, you should reference a common library in the classpath rather than duplicate it in multiple CodeModule objects.

For WebLogic, reference an action handler or supporting library in the CLASSPATH variable in the Content Engine launch script, startWebLogic.sh or startWebLogic.cmd. The Content Engine launch script is in the WebLogic domain where you installed the Content Engine, such as FNCEDomain.

For WebSphere, you can reference an action handler or supporting library in the CLASSPATH in any of the following ways:

Restart the application server after updating the classpath.

NOTE The Windows environment has a command line size limit, which can be exceeded by a CLASSPATH variable set to a very long string.

Creating a CodeModule Object

This section shows how to create a CodeModule object that's stored in a Content Engine object store. Not included in this section is what you must do after creating a CodeModule object, which is to set the object on the CodeModule property of an Action subobject. For more information, see the applicable procedure for creating an EventAction, LifecycleAction, or DocumentClassificationAction object.

NOTE If you later modify an action handler that is contained within a CodeModule object, then you must take the following steps to ensure that the most current action handler implementation is contained within the Action subobject. First, update the CodeModule object with the most recent version of the action handler implementation. Second, set the Action subobject's CodeModule property to the current version of the CodeModule object. For details, see Updating a CodeModule Object.

In the Java and C# examples below, EventHandlers.jar containing multiple action handlers is checked into the "CodeModules" folder of the object store. Given that the content of a CodeModule object can consist of class or JAR files, the code tests for the type of Java module in order to set the ContentType property on the ContentTransfer object. Note that you must check in a CodeModule object. If you don't, it's stored as a reservation (in progress) version of a CodeModule object, which will result in an exception when you attempt to create the Action subobject.

Also included in the examples is optional code that demonstrates a point about creating an Action subobject in the same procedure that creates the CodeModule object. Such a procedure would be useful, for example in an event action creation wizard, which also gives users the option to create the code module. If you code a procedure that creates both objects, you should trap for a potential exception in creating the Action subobject, and delete the previously created CodeModule object in the catch block. If you don't, and if an exception occurs, then a copy of the first CodeModule object will be created when the code is rerun. Potential errors that will throw a creation exception are failing to check in a CodeModule object, or setting the Action subobject's ProgId property to the wrong value.

Java Example

...
// Create a File object for the JAR containing the event handlers
File handlerJar = new File("C:\\EclipseWorkspace\\EventHandlers\\EventHandlers.jar");

// Get object for existing folder where JAR will be stored in object store
Folder folder=Factory.Folder.fetchInstance(os, "/CodeModules", null);

// Create ContentTransfer object from JAR content
ContentElementList contentList = Factory.ContentTransfer.createList();
ContentTransfer ctNew;
FileInputStream fileIS;
ctNew = Factory.ContentTransfer.createInstance();
fileIS = new FileInputStream(handlerJar.getAbsolutePath());
ctNew.setCaptureSource(fileIS);

// Set mime type based on whether the Java module is a class or JAR
String fileName = handlerJar.getName();
if (fileName.endsWith(".class"))
{
   ctNew.set_ContentType("application/java-byte-code");
}
else if (fileName.endsWith(".zip") || fileName.endsWith(".jar"))
{
   ctNew.set_ContentType("application/x-zip-compressed");
}

// Add content element to list, which will be added to CodeModule object (below)
contentList.add(ctNew);

// Create CodeModule object
CodeModule newCM = Factory.CodeModule.createInstance(os, "CodeModule"); 
// Set DocumentTitle property
String propertyName = "DocumentTitle";
newCM.getProperties().putValue(propertyName, "Java Event Handlers");
// Add content element to CodeModule object
newCM.set_ContentElements(contentList);

// Check in CodeModule object
newCM.checkin(AutoClassify.DO_NOT_AUTO_CLASSIFY, CheckinType.MAJOR_VERSION);
newCM.save(RefreshMode.REFRESH);

// File CodeModule object and save
DynamicReferentialContainmentRelationship drcr 
   = (DynamicReferentialContainmentRelationship)folder.file((IndependentlyPersistableObject)newCM,
      AutoUniqueName.AUTO_UNIQUE,
      "Java Event Handlers",
      DefineSecurityParentage.DO_NOT_DEFINE_SECURITY_PARENTAGE);
drcr.save(RefreshMode.NO_REFRESH);
//////////////////////////////////////////////
// The following code is optional. It demonstrates creating an Action subobject,
// in this case, EventAction, with exception handling code.
try
{
   EventAction eventAction = Factory.EventAction.createInstance(os, null);
   eventAction.set_ProgId("RenameActionHandler");
   eventAction.set_CodeModule(newCM);
   eventAction.set_DisplayName("EventActionWithCodeModule");
   eventAction.save(RefreshMode.REFRESH);
}
catch (Exception e)
{
   System.out.println("EventAction creation failed: " + e.getMessage());
   newCM.delete();
   newCM.save(RefreshMode.REFRESH);
}

C# Example

...
// Create a FileInfo object for the JAR containing the event handlers
FileInfo handlerJar = new FileInfo (@"C:\\EclipseWorkspace\\EventHandlers\\EventHandlers.jar");

// Get object for existing folder where JAR will be stored in object store
IFolder folder = Factory.Folder.FetchInstance(os, "/CodeModules", null);

// Create ContentTransfer object from JAR content
IContentElementList contentList = Factory.ContentTransfer.CreateList();
IContentTransfer ctNew;
FileStream fileS = handlerJar.OpenRead();
ctNew = Factory.ContentTransfer.CreateInstance();
ctNew.SetCaptureSource(fileS);

//Set mime type based on whether the Java module is a class or JAR
string filename = handlerJar.Name;
if (filename.EndsWith(".class"))
{
   ctNew.ContentType = "application/java-byte-code";
}
else if (filename.EndsWith(".zip") || filename.EndsWith(".jar"))
{
   ctNew.ContentType = "application/x-zip-compressed";
}

// Add content element to list, which will be added to CodeModule object (below)
contentList.Add(ctNew);

// Create CodeModule object
ICodeModule newCM = Factory.CodeModule.CreateInstance(os, "CodeModule");

// Set DocumentTitle property
newCM.Properties["DocumentTitle"] = "Java Event Handlers");

// Add content element to CodeModule object
newCM.ContentElements = contentList;

// Check in CodeModule object
newCM.Checkin(AutoClassify.DO_NOT_AUTO_CLASSIFY, CheckinType.MAJOR_VERSION);
newCM.Save(RefreshMode.REFRESH);

// File CodeModule object and save
IDynamicReferentialContainmentRelationship drcr
   = (IDynamicReferentialContainmentRelationship) folder.File((IIndependentlyPersistableObject)newCM,
      AutoUniqueName.AUTO_UNIQUE,
      "Java Event Handlers",
      DefineSecurityParentage.DO_NOT_DEFINE_SECURITY_PARENTAGE);
drcr.Save(RefreshMode.NO_REFRESH);
//////////////////////////////////////////////
// The following code is optional. It demonstrates creating an IAction subobject,
// in this case, IEventAction, with exception handling code.
try
{
   IEventAction eventAction = Factory.EventAction.CreateInstance(os, null);
   eventAction.ProgId = "RenameActionHandler";
   eventAction.CodeModule = newCM;
   eventAction.DisplayName = "EventActionWithCodeModule";
   eventAction.Save(RefreshMode.REFRESH);
}
catch (Exception e)
{
   System.Console.WriteLine("EventAction creation failed: " + e.Message );
   newCM.Delete();
   newCM.Save(RefreshMode.REFRESH);
}

Updating a CodeModule Object

If you modify an action handler that is contained within a CodeModule object, you must first update the CodeModule object with the new version of the action handler. Then you must update any Action subobjects that reference the code module that's been updated. The easiest way to update a CodeModule object and Action subobjects is with Enterprise Manager, as described in Update Event Action with a New Code Module Version. However, you can update these objects programmatically, as described in this section.

The Java and C# examples below illustrate the update process for an event handler that's been previously archived in EventHandlers.jar, which is contained in a CodeModule object. Once the handler has been modified and re-archived in EventHandlers.jar, the CodeModule object is ready to be updated with the new version of EventHandlers.jar.

First, the content of the new JAR is retrieved as a ContentTransfer object and stored in a ContentElementList. Next, the current version of the CodeModule object is checked out, a reservation object is retrieved, and the ContentElementList is set on the reservation object. The reservation object is checked in, becoming the new version of the CodeModule object.

The CodeModule object that's been updated is referenced by an EventAction object, and it must be updated to reference the current version of the CodeModule object. As shown in the second part of the examples, the EventAction object's CodeModule property is set to the current version of the CodeModule object.

Java Example

...
   // Create a File object for the JAR containing the updated event handler
   File newHandlerVersion = new File("C:\\EclipseWorkspace\\EventHandlers\\EventHandlers.jar");
           
   // Create ContentTransfer object from updated JAR content
   ContentElementList contentList = Factory.ContentTransfer.createList();
   ContentTransfer ctNew;
   FileInputStream fileIS;
   ctNew = Factory.ContentTransfer.createInstance();
   fileIS = new FileInputStream(newHandlerVersion.getAbsolutePath());
   ctNew.setCaptureSource(fileIS);
   ctNew.set_ContentType("application/x-zip-compressed");
   contentList.add(ctNew);

   // Check out current version of CodeModule object
   CodeModule cm = Factory.CodeModule.getInstance(os, "/CodeModules/EventHandlers.jar");
   cm.checkout(com.filenet.api.constants.ReservationType.EXCLUSIVE, null, null, null);
   cm.save(RefreshMode.REFRESH);

   // Get reservation object from the checked-out code module.
   // This will become the new version of the code module.
   CodeModule cmUpdate = (CodeModule) cm.get_Reservation();
   
      
   // Set the new content on the reservation object
   cmUpdate.set_ContentElements(contentList);   
   
   // Check in the new version of the code module
   cmUpdate.checkin(AutoClassify.DO_NOT_AUTO_CLASSIFY, CheckinType.MAJOR_VERSION);
   cmUpdate.save(RefreshMode.REFRESH);
       
   /////////////////////////////////////////////
   // Get event action to update its CodeModule property
   PropertyFilter pf = new PropertyFilter();
   pf.addIncludeType(1, null, null, FilteredPropertyType.SINGLETON_OBJECT, new Integer(1) );
   pf.addIncludeProperty(0, null, Boolean.TRUE, PropertyNames.CODE_MODULE, new Integer(1) );
   EventAction eventAction = Factory.EventAction.fetchInstance(os, new Id("{94AF1BB3-A4A7-4851-9067-D6F88005C470}"), pf); 

   // Set CodeModule property
   eventAction.set_CodeModule(cmUpdate);

   eventAction.save(RefreshMode.REFRESH); 
}

C# Example

...
   // Create a Stream object for the JAR containing the updated event handler
   Stream fileStream = File.OpenRead(@"C:\\EclipseWorkspace\\EventHandlers\\EventHandlers.jar");

   // Create IContentTransfer object from updated JAR content
   IContentElementList contentList = Factory.ContentTransfer.CreateList();
   IContentTransfer ctNew;
   ctNew = Factory.ContentTransfer.CreateInstance();
   ctNew.SetCaptureSource(fileStream);
   ctNew.ContentType = "application/x-zip-compressed";
   contentList.Add(ctNew);

   // Check out current version of CodeModule object
   ICodeModule cm = Factory.CodeModule.GetInstance(os, "/CodeModules/EventHandlers.jar");
   cm.Checkout(ReservationType.EXCLUSIVE, null, null, null);
   cm.Save(RefreshMode.REFRESH);

   // Get reservation object from the checked-out code module.
   // This will become the new version of the code module.
   ICodeModule cmUpdate = (ICodeModule)cm.Reservation;

   // Set the new content on the reservation object
   cmUpdate.ContentElements = contentList;

   // Check in the new version of the code module
   cmUpdate.Checkin(AutoClassify.DO_NOT_AUTO_CLASSIFY, CheckinType.MAJOR_VERSION);
   cmUpdate.Save(RefreshMode.REFRESH);

   /////////////////////////////////////////////
   // Get event action to update its CodeModule property
   PropertyFilter pf = new PropertyFilter();
   pf.AddIncludeType(1, null, null, FilteredPropertyType.SINGLETON_OBJECT, 1);
   pf.AddIncludeProperty(0, null, true, PropertyNames.CODE_MODULE, 1);
   IEventAction eventAction = Factory.EventAction.FetchInstance(os, new Id("{94AF1BB3-A4A7-4851-9067-D6F88005C470}"), pf);

   // Set CodeModule property
   eventAction.CodeModule = cmUpdate;

   eventAction.Save(RefreshMode.REFRESH);
}