Content Management and Capture

Content Management and Capture

Come for answers. Stay for best practices. All we’re missing is you.

 View Only
  • 1.  Need Guidance on Creating & Deploying Custom Java Event Actions in FileNet

    Posted 5 days ago

    Dear IBM Community,

    Following IBM Support's recommendation, we need guidance on implementing a custom Java Event Action in IBM FileNet to control document version growth.

    Our use case:
    Large documents (100–200 MB) are edited using IBM Content Navigator Edit Client. Since versioning creates full copies, total storage grows rapidly. Business does not require historical versions. However, disabling versioning blocks Edit Client editing. Support advised that a custom Event Action can manage version behavior.

    We request clear documentation or steps on:

    1. How to develop a custom Event Action

      • Required CE JARs and development setup

      • Implementing EventActionHandler

      • Accessing and modifying versions/content in the code

    2. How to deploy the Event Action

      • Packing the JAR

      • Registering it in ACCE

      • Configuring event subscriptions

      • Any security or restart considerations

    3. Best practices or IBM-recommended patterns for safely managing document versions through custom code.

    Any sample code or official references would help us implement this in a supportable way.

    Thank you.



    ------------------------------
    Wasif Khan
    ------------------------------


  • 2.  RE: Need Guidance on Creating & Deploying Custom Java Event Actions in FileNet

    Posted 2 days ago

    Hi Wasif,

    As you have likely noticed, document versioning must be enabled for the ICN Edit Client to work properly. The Edit Client automatically creates a new version each time the document is updated from the client side.

    Regarding documentation, IBM provides sample code for Java event actions, and the Redbook "Developing Applications with IBM FileNet P8 API" is an excellent reference. It includes practical guidance on designing custom event action handlers, packaging and deployment approaches, and best practices.

    On the storage growth concern, there are several ways to address it:

    1. Custom Event Action
      Implement an event handler (for example on check-in) that retains only the most recent version and deletes superseded versions. This keeps Edit Client compatibility while limiting version sprawl.

    2. Custom Sweep Job
      Build a scheduled job that identifies documents exceeding a defined version threshold and removes the oldest versions. This approach is often easier to control operationally and can be applied in batches.

    3. Storage Policy Strategy
      Apply tiered storage policies (e.g., warm/cold) for older content or less frequently accessed documents, reducing cost impact while preserving access if retaining some history is still desirable.

    Depending on your retention and compliance constraints, a combination of the sweep-job approach plus targeted event handling for the largest documents can be a pragmatic and supportable solution.

    Hope this helps.



    ------------------------------
    Olivier Baltus
    NSI Luxembourg
    ------------------------------



  • 3.  RE: Need Guidance on Creating & Deploying Custom Java Event Actions in FileNet

    Posted 2 days ago
    Edited by David Alfredson 2 days ago

    Hi Wasif,

    Rather than the overhead of using a code module and sweeping, here's a javascript event action that will keep your document versions pruned to a configurable number of versions of a configurable age.

    1. You create an event action and paste the javascript into the event action.

    2. Create a subscription that links the event action to the class to which you want it to apply.  Be careful, you need to configure it sensibly so you do it for the classes you want, not everything, unless you specifically want to do it for everything.

    3. Configure the subscription using the user string property to provide the limits you want the code to use.  It filters the version series by age (date last modified) and count (the maximum number of versions you want to keep).  For example, LIMIT_AGE_MONTHS=-1;LIMIT_COUNT=3 means ignore the age limit, always keep 3 versions.  LIMIT_AGE_MONTHS=12;LIMIT_COUNT=5 means keep at least 5 versions, but only delete versions that are older than 12 months.  You can attach a different rule in different subscriptions to different classes.

    Test it does what you want.  It explicitly excludes the current version so it shouldn't be that destructive.  There's no warranty of fitness to purpose with the code. 

    You can enable tracing in ACCE for the Handler at summary level to get a trace of what the script is doing.

    importPackage(java.lang);
    importPackage(java.util);
    importPackage(Packages.com.filenet.api.action);
    importPackage(Packages.com.filenet.api.engine);
    importPackage(Packages.com.filenet.api.core);
    importPackage(Packages.com.filenet.api.constants);
    importPackage(Packages.com.filenet.api.collection);
    importPackage(Packages.com.filenet.api.property);
    importPackage(Packages.com.filenet.api.util);
    
    var VERSION_LIMIT_COUNT = 5;
    var VERSION_LIMIT_AGE_MONTHS = 3;
    
    function onEvent(event, subscriptionId) {
    
        var hcc = HandlerCallContext.getInstance();
        if (hcc.isSummaryTraceEnabled()) {
            hcc.traceSummary("start of remove excess versions " + subscriptionId);
        }
    
    	var objectStore = event.getObjectStore();
    	updateDefaults(objectStore, subscriptionId);
    
        var document = objectStore.fetchObject(event.get_SourceClassId(), event.get_SourceObjectId(), getPropertyFilter());
    
        var calendar = Calendar.getInstance();
        calendar.add(Calendar.MONTH, -VERSION_LIMIT_AGE_MONTHS);
        var olderThan = calendar.getTime();
    
        var keepAtLeast = VERSION_LIMIT_COUNT;
    
        if (hcc.isSummaryTraceEnabled()) {
            hcc.traceSummary("removing versions from document " + document.getObjectReference() + " older than " + VERSION_LIMIT_AGE_MONTHS + " months, keeping at least " + VERSION_LIMIT_COUNT + " versions");
        }
     
        deleteSupersededVersions(hcc, document, olderThan, keepAtLeast);
    
        if (hcc.isSummaryTraceEnabled()) {
            hcc.traceSummary("end of remove excess versions " + subscriptionId + " for " + document.getObjectReference());
        }
    
    }
    
    function hasValue(value) {
        return value != null && value != "";
    }
    
    function getPropertyFilter() {
        var propertyFilter = new PropertyFilter();
    
        var propertiesToFetch = PropertyNames.VERSION_SERIES + " " + 
                                PropertyNames.VERSIONS + " " + 
                                PropertyNames.DATE_CREATED + " " + 
                                PropertyNames.DATE_LAST_MODIFIED + " " + 
                                PropertyNames.IS_CURRENT_VERSION;
        var filterElement = new FilterElement(null, null, null, propertiesToFetch, null);
        propertyFilter.addIncludeProperty(filterElement);
    
        return propertyFilter; // return the filter!
    }
    
    function updateDefaults(objectStore, subscriptionId) {
    
       var subscription = Factory.Subscription.fetchInstance(objectStore, subscriptionId, null);
       var userString = subscription.get_UserString();
    
       if (hasValue(userString)) {
           var entries = userString.split(';');
           for (var index=0; index<entries.length; index++) {
               var entry = entries[index];
               if (entry.startsWith("LIMIT_COUNT=")) {
                   VERSION_LIMIT_COUNT = parseInt(entry.substring(12), 10);
               } else if (entry.startsWith("LIMIT_AGE_MONTHS=")) {
                   VERSION_LIMIT_AGE_MONTHS = parseInt(entry.substring(17), 10);
               }
           }
       }
    }
    
    function deleteSupersededVersions(hcc, document, olderThanDate, keepAtLeast) {
    
    	// versionSet is from the most recent to the oldest version
    	var versionSet = document.get_Versions();
    	var versions = versionSet.iterator();
    
    	var candidateSet = new LinkedHashSet();
    
    	var count = 0;
    	while (versions.hasNext()) {
    		var version = versions.next();
    		var isCurrentVersion = ( version.get_IsCurrentVersion() == true);
    		var lastModified = version.get_DateLastModified();
    		var isOlderThan = (lastModified == null || lastModified.before(olderThanDate));
    
    		if (isCurrentVersion == false && isOlderThan == true && count >= keepAtLeast) {
    			candidateSet.add(version);
    		}
    		count++;
    	}
    
        var candidates = candidateSet.iterator();
    	while (candidates.hasNext()) {
    		version = candidates.next();
    	    if (hcc.isSummaryTraceEnabled()) {
    	        hcc.traceSummary("deleting superseded version " + version.getObjectReference());
    	    }
    		version.delete();
    		version.save(RefreshMode.NO_REFRESH);
    	}
    }



    ------------------------------
    David Alfredson
    ------------------------------



  • 4.  RE: Need Guidance on Creating & Deploying Custom Java Event Actions in FileNet

    Posted 2 days ago
    Edited by Olivier Baltus 2 days ago

    Hi David,

    Thanks for sharing your code. JavaScript is indeed a valid option, but I would mainly use it for simple cases or a quick POC, and then move to Java for a production-grade solution that can manage version growth safely at scale.

     In short: JavaScript to move fast, Java to sleep well :-)


    ------------------------------
    Olivier Baltus
    NSI Luxembourg
    ------------------------------



  • 5.  RE: Need Guidance on Creating & Deploying Custom Java Event Actions in FileNet

    Posted 2 hours ago

    Hi,

    Thank you very much for your help. We have fixed the issue for now using the JavaScript solution, and it is working according to our requirements.

    In the future, we also plan to implement a Java-based event action using a class handler. However, the reference book provided does not explicitly explain how to deploy the Java class, and there is also an option related to the Code Module. If someone could clarify this, we can proceed with formulating a Java-based solution as well.

    Thanks and regards,
    Wasif Khan



    ------------------------------
    Wasif Khan
    ------------------------------



  • 6.  RE: Need Guidance on Creating & Deploying Custom Java Event Actions in FileNet

    Posted 52 minutes ago
    Edited by Olivier Baltus 44 minutes ago

    Hi,

    You can deploy a Java-based event action in FileNet by packaging your handler into a JAR, storing that JAR (and optionally its dependencies) inside a Code Module in the object store via ACCE, then creating an Event Action that points to the handler class and references the Code Module, and finally binding it to the desired events through a Subscription. 

    Practically:

    1. Implement the Content Engine event action handler interface (the Event Action is the registration for this Java class). 
    2. Compile and package your code into a JAR. Decide how you want to handle dependencies:
    • Option A – Multiple JARs in one Code Module
      Your main handler JAR + each dependency as separate JARs, all added as separate content elements in the same Code Module.
    • Option B – One "uber/fat" JAR
      A single shaded JAR that embeds dependencies. This can simplify deployment and avoid missing-library issues at runtime.

    In FileNet Content Engine:

    • Code Module is a dedicated document class. When you create the object in ACCE, you must select Class = "Code Module" and create it with content.

    • There is also a dedicated folder in the object store for these artifacts, typically Root Folder > CodeModules in ACCE. This is the default location where IBM expects you to create and manage Code Module documents. 

    Last but not least, The Event handler code is updated by versioning the Code Module using check-out/check-in, replacing the JAR content and checking it back in. » 

    For dependencies, make sure you don’t bundle libraries already provided by the FileNet/WebSphere runtime. Using Maven helps control this cleanly. For example, avoid packaging JACE in your JAR/uberjar by declaring it with scope=provided, to prevent version/classloading conflicts.



    ------------------------------
    Olivier Baltus
    NSI Luxembourg
    ------------------------------