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:
CodeModule
object are only visible to the classes contained in that CodeModule
object; they are not visible to classes contained in other CodeModule
objects. For example, the same library required by two separate CodeModule
objects would have to be included separately in the two objects.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.
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:
Application servers > server1 > Process Definition > Java Virtual Machine
.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.
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); }
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); }