You are not logged in. Click here to log in.

codebeamer Application Lifecycle Management (ALM)

Search In Project

Search inClear

This functionality can only be used in conjunction with Intland professional services. To become part of the early release community please contact sales@intland.com.

codebeamer Listener API

The codebeamer server internally has a mechanism for dispatching entity lifecycle Events to registered Listeners.

Events

The protocol by which events are communicated, is defined via entity specific Java interfaces, where each type of event is represented as a method.

E.g. The interface, to communicate Project lifecycle events, such as

  • Creating a new project or
  • Updating or Removing an existing project.



public interface ProjectListener extends EventListener {
    /**
     * This method gets called when a new project is created.
     * The {@link BaseEvent#getSource()} is the newly created project
     * @throws VetoException if the listener wishes the change to be rolled back.
     */
    default void projectCreated(BaseEvent<ProjectDto,Void,Void> event) throws VetoException {}

    /**
     * This method gets called when a project is updated.
     * The {@link BaseEvent#getSource()} is the new/updated project state
     * The {@link BaseEvent#getSecondarySource()} is the old project state before the update
     * @param event contains the "new" state as "source" and the "old" state as "secondarySource", so you have access to both.
     * @throws VetoException if the listener wishes the change to be rolled back.
     */
    default void projectUpdated(BaseEvent<ProjectDto,ProjectDto,Void> event) throws VetoException {}

    /**
     * This method gets called when a project is deleted.
     * The {@link BaseEvent#getSource()} is the deleted project
     * @throws VetoException if the listener wishes the change to be rolled back.
     */
    default void projectDeleted(BaseEvent<ProjectDto,Void,Void> event) throws VetoException {}

    /**
     * This method gets called when the daily project statistics are built (typically daily once a night).
     * The {@link BaseEvent#getSource()} is the project the statistics of which is to be built
     * The {@link BaseEvent#getSecondarySource()} is the statistics anchor date.
     * The {@link BaseEvent#getData()} are the {@link DailyProjectStatsDto}, but only in the post event notification.
     * @throws VetoException if the listener wishes not to build statistics or this project.
     * @since CB-6.0
     */
    default void projectDailyStatistics(BaseEvent<ProjectDto,Date,DailyProjectStatsDto> event) throws VetoException {}

    /**
     * This method gets called when a user requested to join a project.
     * The {@link BaseEvent#getSource()} is the project where the {@link BaseEvent#getUser()} wants to join
     * The {@link BaseEvent#getSecondarySource()} is the comment of the user for the join request
     * @param event
     * @throws VetoException
     */
    default void userJoinRequested (BaseEvent<ProjectDto,String,Void> event) throws VetoException {}

    /**
     * This method gets called when a project join request was accepted.
     * The {@link BaseEvent#getSource()} is the project where the join was accepted by the {@link BaseEvent#getUser()}
     * The {@link BaseEvent#getSecondarySource()} is the comment of the user who accepted the join
     * The {@link BaseEvent#getData()} is the user whose join request was accepted.
     * @param event
     * @throws VetoException
     */
    default void userJoinAccepted (BaseEvent<ProjectDto,String,UserDto> event) throws VetoException {}

    /**
     * This method gets called when a project join request was rejected
     * The {@link BaseEvent#getSource()} is the project where the join was rejected by the {@link BaseEvent#getUser()}
     * The {@link BaseEvent#getSecondarySource()} is the comment of the user who rejected the join
     * The {@link BaseEvent#getData()} is the user whose join request was rejected.
     * @param event
     * @throws VetoException
     */
    default void userJoinRejected (BaseEvent<ProjectDto,String,UserDto> event) throws VetoException {}
}


Information about the event is passed as method parameters, typically in form of a genericBaseEvent object which carries entity and event specific information:

  • request
    The HTTP Servlet request which triggered the event
  • user
    The codebeamer user which made the request
  • source
    The entity, the event is about
  • secondarySource
    An optional second entity involved in this event
  • data
    Any optional additional event specific data
  • preEvent
    Whether this is the notification before the underlying actual lifecycle operation is performed (true) or the notification after the lifecycle operation was executed successfully (false)


Any entity lifecycle event, if not stated otherwise, is communicated twice:

  • Before the actual lifecycle operation is performed (preEvent == true):
    In this phase, the entity object (e.g. ProjectDto) passed as source is writable, allowing the listeners to
    • Validate and even Modify the entity object
    • Reject the intended lifecycle operation by throwing a VetoException with some explanatory message.
  • After the actual lifecycle operation was successfully executed (preEvent == false):
    Please note that if the execution of a lifecycle operation was vetoed (see above) or has failed, there will be no post notification!
    This phase will typically be used, to publish the event externally, e.g. by sending an appropriate notification email to registered subscribers (see Default Listenersbelow):
    • Modifications to the entity object are no longer possible!
    • A VetoException thrown in this phase will be ignored!


All event communication occurs

therefore runtimeexceptions during listener notification will cause a transactionrollback !

Listing

Here is a listing of all codebeamer listener interfaces from package com.intland.codebeamer.event in alphabetic order, including their communicated events:

  • ArtifactListener
    • artifactCreated
    • artifactUpdated
    • artifactDeleted
    • artifactOpened
  • AssociationListener
    • associationCreated
    • associationUpdated
    • associationDeleted
  • FileUploadListener (CB-9.5 and newer)
    • fileUploaded
  • ProjectListener
    • projectCreated
    • projectUpdated
    • projectDeleted
    • projectDailyStatistics
    • userJoinRequested
    • userJoinAccepted
    • userJoinRejected
  • RoleListener
    • roleCreated
    • roleUpdated
    • roleDeleted
  • ScmListener
    • repositoryCreated
    • repositoryUpdated
    • repositoryDeleted
    • repositorySynchronized
    • changeSetCommitted
  • TrackerItemListener
    • trackerItemCreated
    • trackerItemUpdated
    • trackerItemRemoved
    • trackerItemDeleted
    • trackerItemEscalated
    • attachmentAdded
    • attachmentUpdated
    • attachmentRemoved
  • TrackerListener
    • trackerCreated
    • trackerUpdated
    • trackerDeleted
  • TrackerSynchronizationListener (CB-10.0 and newer)
    • trackerSynchronizationConfigured
    • trackerSynchronizationRemoved
    • trackerSynchronized
  • UserListener
    • userCreated
    • userUpdated
    • userDeleted
  • WorkflowListener
    • transitionTaken

Please refer to the documentation of a specific entity listener interface method (= event), to see what actual event information is provided.

Listeners

A codebeamer Event Listener is a Java class that implements one or more listener interfaces.

There can be any number of Listeners for the same event, or none at all.

All appropriate Listeners will be called

Therefore


Default Listeners

codebeamer includes a set of default Listeners in the com.intland.codebeamer.event.impl package.

These default listeners are responsible for sending notification emails about specific events to a dedicated audience:

  • <bean id="defaultUserListener" class="com.intland.codebeamer.event.impl.DefaultUserListener" />
    • userCreated
      • user_created_notification_email_[subject|body].vm
    • userUpdated
      • user_activated_confirmation_email_[subject|body].vm
  • <bean id="defaultProjectListener" class="com.intland.codebeamer.event.impl.DefaultProjectListener" />
    • projectCreated
      • project_created_notification_email_[subject|body].vm
    • userJoinRequested
      • project_join_requested_notification_email_[subject|body].vm
    • userJoinAccepted
      • project_join_accepted_notification_email_[subject|body].vm
    • userJoinRejected
      • project_join_rejected_notification_email_[subject|body].vm
  • <bean id="defaultArtifactListener" class="com.intland.codebeamer.event.impl.DefaultArtifactListener" />
    • artifactCreated
      • [artifact|wikipage][_comment]_created_notification_email_[subject|body].vm
    • artifactUpdated
      • [artifact|wikipage][_comment]_updated_notification_email_[subject|body].vm
    • artifactDeleted
      • [artifact|wikipage][_comment]_deleted_notification_email_[subject|body].vm
    • artifactOpened
      • [artifact|wikipage]_opened_notification_email_[subject|body].vm
  • <bean id="defaultAssociationListener" class="com.intland.codebeamer.event.impl.DefaultAssociationListener" />
    • associationCreated
      • tracker_item_association_created_notification_email_[subject|body].vm
  • <bean id="defaultScmListener" class="com.intland.codebeamer.event.impl.DefaultScmListener"/>
    • changeSetCommitted
      • scc_modification_committed_notification_email_[subject|body].vm
    • repositorySynchronized
      • scm_checkout_finished_email_[subject|body].vm
  • <bean id="defaultTrackerItemListener" class="com.intland.codebeamer.event.impl.DefaultTrackerItemListener"/>
    • trackerItemCreated
      • [tracker_item|pull_request]_created_notification_email_[subject|body].vm
    • trackerItemUpdated
      • [tracker_item|pull_request]_updated_notification_email_[subject|body].vm
    • trackerItemEscalated
      • tracker_item_escalated_notification_email_[subject|body].vm
    • attachmentAdded
      • tracker_item_[attachment|comment]_created_notification_email_[subject|body].vm


The Velocity templates for the email subject and body are located in: ~/CB-../tomcat/webapps/cb/config/templates/email.

To replace a default listener, you must add a <bean> configuration for your extended/custom version, with the same id than the default listener, to ~/CB-../tomcat/webapps/cb/WEB-INF/classes/my-ApplicationContext.xml.

Custom Listeners

The package of your custom Listener class should be a sub-package of com.intland.codebeamer.event.impl.

You can use any package name, but when using a sub-package of com.intland.codebeamer, your Listener can be automatically detected and deployed by the Component scan during codebeamer startup.

E.g. A sample ProjectListener implementation:


package com.intland.codebeamer.event.impl.sample;

import org.springframework.stereotype.Component;
import com.intland.codebeamer.event.ProjectListener;

@Component("exampleProjectListener")
public class ExampleProjectListener implements ProjectListener {

}

Unless you want to replace a Default Listener (see above) with an extended/custom version, you must not



If a Listenerclass needs access to other codebeamer APIs, it should simply declare appropriate @Autowired variables, e.g.


package com.intland.codebeamer.event.impl.sample;

import org.springframework.stereotype.Component;
import com.intland.codebeamer.event.ProjectListener;
import com.intland.codebeamer.manager.ProjectManager;

@Component("exampleProjectListener")
public class ExampleProjectListener implements ProjectListener {

   @Autowired
   private ProjectManager projectManager;

   ...
}


Although this implementation compiles, it is still useless, because it does not address any Event yet.

In order to do so, we must override the appropriate method, e.g.


package com.intland.codebeamer.event.impl.sample;

import org.springframework.stereotype.Component;

import com.intland.codebeamer.event.BaseEvent;
import com.intland.codebeamer.event.ProjectListener;
import com.intland.codebeamer.event.util.VetoException;
import com.intland.codebeamer.persistence.dto.ProjectDto;

@Component("exampleProjectListener")
public class ExampleProjectListener implements ProjectListener {

    @Override
    public void projectCreated(BaseEvent<ProjectDto, Void, Void> event) throws VetoException {
        ProjectDto project = event.getSource();

        if (event.isPreEvent() && project.getCategoryReference() == null) {
            throw new VetoException("Each project must have a category!");
        }
    }

}

In CB-10.1 and newer, you only need to override the methods for the events you want to address. Other events are ignored by default.

Add users as project administrators to each newly created project is another example of a ProjectListener.

And here is an example of a FileUploadListener.

Java IDE

Developing a custom Listener class in Java requires a Java 8 or newer Standard Edition (SE) Java Development Kit (JDK) plus the following frameworks/libraries:

Framework/Library Component Version
org.apache.commons commons-lang3 3.4
org.springframework spring-core 5.1.x
org.springframework spring-context 5.1.x
org.springframework spring-beans 5.1.x
org.springframework spring-tx 5.1.x
org.mybatis mybatis-spring 2.0.x


You also need cb.jar and cb-common.jar from the directory ~/CB-../tomcat/webapps/cb/WEB-INF/lib of the codebeamer 10.0 or newer installation, where you want to deploy your listener to.

Deployment

To deploy your custom Listener class, the Java code must be compiled and the resulting *.class files must be uploaded to the appropriate sub-directory under ~/CB-.../tomcat/webapps/cb/WEB-INF/classes/... on your codebeamer server, where the sub-directory is the equivalent of the extension's package name.

If you have multiple custom listeners, you may choose to pack all your custom classes into one Java archive (*.jar) and put that into ~/CB-.../tomcat/webapps/cb/WEB-INF/lib.

For Listener classes not under com.intland.codebeamer, or not annotated as @Component, you must also provide extra <bean> configurations in

~/CB-../tomcat/webapps/cb/WEB-INF/classes/my-ApplicationContext.xml


Finally you have to restart codebeamer, for your newly deployed Listeners to get loaded.

Please note:

  • Deployed Listeners run in the codebeamer server JVM and have unrestricted access to all internal functions and data!
    • You should therefore only deploy custom Listeners, where you know exactly, what they are doing, and that have been carefully tested

Utils

GlobalTypeFacade (utils for shared fields/global types)

codebeamer has a utility class GlobalTypeFacade in the com.intland.codebeamer.facade package for global types (shared fields) with the following methods:

  • Map<String, Integer> getGlobalTypeNameToFieldIdMap(Integer trackerId) - To get the map of tracker field IDs by global type name for a tracker.


  • Optional<Object> getGlobalTypeValue(String globalTypeNameOrId, TrackerItemDto item, TrackerItemDto originalItem) throws GlobalTypeNotFoundException - To get the value of global type in an item.
    Optional originalItem parameter is used to check whether the current item is the same as the original (persisted) item (contains all the references). If not, it gets reloaded from cache.


  • Optional<TrackerLayoutLabelDto> getFieldByGlobalTypeNameOrId(String globalTypeNameOrId, Integer trackerId) - To get the tracker field by global type name or ID.


  • Optional<GlobalTypeDto> findGlobalTypeById(UserDto user, Integer id) - To find a global type by its ID.


  • Optional<GlobalTypeDto> findGlobalTypeByName(UserDto user, String globalTypeName) - To find a global type by its name.

Frequently Asked Questions

  • What should I do in the event handlers that are insignificant for me, but which I am forced to implement?

You can simply leave those method bodies empty, it has no side-effects at all.

  • Can I safely override the default listeners shipped with codebeamer?

No. codebeamer relies on actively using its own listeners to implement several mechanisms. Disabling the default listeners might result in undefined behavior and data failure.

  • Is the order of the listeners calls defined?

No, there is absolutely no guarantee for which order they receive the notifications. Please do not rely on this.

  • If a listener vetoes an operation, will the other listeners get any kind of notification about this?

No. If the listeners are normally notified in this order A, B, C, D and B vetoes the operation, then:

  • A will not know that B vetoed it
  • C and D will not be called at all

This is a design decision to keep the behavior simple, but powerful enough for most real-life applications.