Listener API
This section is about Listener API.
* 
This functionality can only be used in conjunction with PTC professional services. To become part of the early release community contact PTC Sales.
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.
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 Listeners below):
Modifications to the entity object are no longer possible!
A VetoException thrown in this phase will be ignored!
All event communication occurs
within the lifecycle operation's transaction,
therefore runtime exceptions during listener notification will cause a transaction rollback !
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
Sequentially (in no defined order),
Within the transaction of the lifecycle operation.
Therefore
Uncaught exceptions during listener invocation will cause a transaction rollback, and
Inefficient listener implementations will have negative effects on the performance of the triggering lifecycle operation!
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
Derive custom listeners from Default Listeners
Re-use the class name of Default Listeners
Re-use the Component name/ <bean id> of Default Listeners
If a Listener class 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.
And here is an example of a FileUploadListener.
Java IDE
Developing a custom Listener class in Java requires the supported Java version or newer Standard Edition (SE) Java Development Kit (JDK) plus the following frameworks/libraries:
Framework/Library
Component
Version
3.4
spring-core
5.1.x
spring-context
5.1.x
spring-beans
5.1.x
spring-tx
5.1.x
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.
* 
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.
Was this helpful?