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
generic
BaseEvent 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
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
◦ 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
◦ 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),
Therefore
• 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
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.