Table of Contents
Objective: To learn about what triggers events to be fired in the Nuxeo system and how to take actions in Java code to handle these events. Learn how to test Java code that is part of a bundle inside Eclipse.
In historic events, the so-called great men are labels giving names to events, and like labels they have but the smallest connection with the event itself. -- L. Tolstoy, Russian novelist (1828-1910)
If you have any comments, questions, or general-purpose harassment you would like give us about this book, then please use the comment form at the bottom of each page! We promise that we will try to incorporate any feedback you give (minus the profanity, of course), will respond to your questions, and credit you appropriately.
In the lessons to this point, we have not required the reader to write Java code. The reasons for this "XML first" approach (with apologies to Winston Churchill) were two fold. First, it allowed us to show many features and functions of the platform that you can access without Java code. Second, it allowed the introduction of many key concepts with minimal "overhead" during the explanation. The reader should now be quite familiar with concepts like manifests, extension points, schemas, and document types so detailed explanations will not be necessary as we use these concepts in the Java code.
The editor is not convinced that the construction of sets of XML files that correctly "implement" desired functionality, such as we have done with the Upcoming document type, is not "coding." Many authors would argue that the ability to configure Nuxeo via XML files, such as the reader has done to this point, shows that Nuxeo's feature set (and power) can be used "without coding," but this phrase has been deliberately omitted in this book.
Up to this point, we have encouraged the reader to use Eclipse
only as a text editor-for the XML files-and to use Maven to build
the bundles. Again, this decision was made to simplify the
explanations needed. Now, the rubber meets the road in the battle
for supremency of the build tools. As before, please check out the
project named lesson-events from the
SVN repository. Again, you should use maven from the command line
to construct the eclipse project files with mvn eclipse:eclipse. This time, however,
you will also need to a script that is suppied with the lesson like
this:
/nuxeo/workspace/lesson-events$ mvn eclipse:clean [INFO] Scanning for projects... [INFO] Searching repository for plugin with prefix: 'eclipse'. [INFO] ------------------------------------------------------------------------ [INFO] Building Events [INFO] task-segment: [eclipse:clean] [INFO] ------------------------------------------------------------------------ [INFO] [eclipse:clean] [INFO] Deleting file: .project [INFO] Deleting file: .classpath [INFO] Deleting file: .wtpmodules [INFO] Deleting file: .component [INFO] Deleting file: org.eclipse.wst.common.component [INFO] Deleting file: org.eclipse.wst.common.project.facet.core.xml [INFO] Deleting file: org.eclipse.jdt.core.prefs [INFO] Deleting directory: .settings [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESSFUL [INFO] ------------------------------------------------------------------------ [INFO] Total time: 1 second [INFO] Finished at: Fri Feb 27 12:26:40 CET 2009 [INFO] Final Memory: 14M/340M [INFO] ------------------------------------------------------------------------ /nuxeo/workspace/lesson-events$ $ mvn eclipse:eclipse [INFO] Scanning for projects... [INFO] Searching repository for plugin with prefix: 'eclipse'. [INFO] ------------------------------------------------------------------------ [INFO] Building Events [INFO] task-segment: [eclipse:eclipse] [INFO] ------------------------------------------------------------------------ [INFO] Preparing eclipse:eclipse [INFO] [buildnumber:create {execution: default}] [INFO] Storing buildNumber: 20090227-122648 at timestamp: 1235734008892 [INFO] [apt:execute {execution: generate-bindings}] [INFO] [nuxeo:eclipse-version {execution: eclipsize-version}] [INFO] eclipseVersion:0.0.1.0 [INFO] [eclipse:eclipse] [INFO] Using source status cache: /nuxeo/workspace/lesson-events/target/mvn-eclipse-cache.properties [INFO] Wrote settings to /nuxeo/workspace/lesson-events/.settings/org.eclipse.jdt.core.prefs [INFO] Wrote Eclipse project for "lesson-events" to /nuxeo/workspace/lesson-events. [INFO] [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESSFUL [INFO] ------------------------------------------------------------------------ [INFO] Total time: 6 seconds [INFO] Finished at: Fri Feb 27 12:26:53 CET 2009 [INFO] Final Memory: 45M/410M [INFO] ------------------------------------------------------------------------ /nuxeo/workspace/lesson-events$ ./fixeclipse Fixing eclipse classpath to use bin output directory instead of target... ./.classpath Done. Replacing missing or badly generated files with .*.ok files... Done.
If you read the listing above, you will notice that it starts
not with mvn
eclipse:eclipse, but with mvn eclipse:clean. This invocation of
maven insures that all previous changes that you might have made to
the eclipse configuration for this project are deleted (cleaned).
Then we use mvn
eclipse:eclipse to create the necessary files for
Eclipse. Finally, we use ./fixeclipse to "fix" some things about
the interaction between eclipse and maven. If you look at the
output of the fixeclipse script, you will it changes the Eclipse
output directory to bin, instead of
the maven standard one of target. If
you were to not do this, you will find that eclipse and maven will
interact badly with one another.
In a previous lesson when we described how to set up your
development environment, we warned against using any of the "maven
plugins" within eclipse. We did this because a common one,
m2eclipse, attempts to "merge" all the output directories into
target and the merging does not work
well with Nuxeo + Eclipse. We know because some of the authors use
it! Now that you have run the fixeclipse script, you actually can
use the m2eclipse successfully. m2eclipse, however, may not have
the result you anticipated since building within Eclipse now sends
its output to bin and not to target, so you must explicitly invoke
Maven from the Eclipse interface to do a build or install. We will
continue to assume in the text that you are using maven from the
command line.
We have also had some feedback that other Maven plugins for Eclipse, notably Maven4MyEclipse from Genuitec, does not have any of the problems we have experienced, so it is likely to function properly without the use of the fixeclipse script.
You should now open the project in Eclipse and select the project name then choose and then . You will see a dialog like this:

There are several critical things to note in this screen shot.
First, the default output folder (at the bottom) is shown to be
lesson-events/bin/main and this is
the change that was made by fixeclipse. Further, there are now five
directories in the source path, including lesson-events/src/main/resources and lesson-events/src/test/resources (not
pictured).
If you do not have these two directories in your source path in eclipse (the Source tab above), your bundle(s) will be unavailable inside Eclipse! The contents of these directories-with the exception of Java code-is copied directly into the output directory, including all directories. Java code is compiled first, then the resulting class files are copied into the output directory. Thus the name Source is actually a bit deceptive since Java source code is the only thing not copied wholesale into the resulting classpath of your application!
Besides the two directories we just mentioned, there are two
directories that will actually contain Java source code,
lesson-events/src/main/java and
lesson-events/src/test/java. These
two directories contain the java source code for your bundle, and
the java source code for testing your bundle, respectively. There
is a final directory, lesson-events/target/generated, that is at the
bottom of this list that you should remove from your source path by
clicking on the name of the directory in the dialog above then
clicking on Remove. This directory is, as you would expect, the
output directory of some tools used in Nuxeo generate source code.
Since we are not using these, you can remove this item for now; it
is probably listed as "missing" in your display anyway.
If you switch to the Libraries tab, you will get an eyefull! This shows you the real purpose of the mvn eclipse:eclipse command, to generate a list of libraries needed by the program and place them all into eclipse:

The error shown at the top of this dialog means that the
snapshot was taken before I followed the directions above to remove
target/generated subdirectory from
the list of directories in the
tab!
This list is quite extensive and it is only those libraries need
for this fairly simple lesson! The variable M2_REPO that you see above on each line evaluates
to your current home directory, say /home/ismith, and then to the .m2/repository directory within that. To the
extreme right of the list, not shown above, Eclipse will display
the full path used for each item in this list.
Using the skeleton provided to you, open up the test directory
for java source code (src/test/java),
the package org.nuxeo.upcoming.test, and
select the file EventTest.java. The
top of this When we warned you in a previous lesson to not use any
of ef is shown in the following capture:

Depending on exactly how you have your editor preferences, you may see different colors, no line numbers, or no print margin line at the 80th column.
In Java code in this book, we use two distinct types of
comments. The "//" type indicate commentary about the code
immediately following the comment. In the screenshot above, you can
see at line 29 a comment that explains that we are searching for
the EventProducer service; right afterwards, we use JUnit's
(www.junit.org) function assertNotNull to test that the value producer is not null. If the value turns out to be
null, the test will immediately halt with an error. The text "Found
the event producer ok?" will be displayed as part of the failure
message. We find that writing this explanatory text as a
question-that explains what the assertNull or other function is
testing-to be a nice way understand the errors produced. If you see
your text in an error message, then you can assume that some part
of the question turned out to not have the answer you expected. You
should go back an examine your assumptions!
We will discuss the particulars of Events, EventListeners in the next couple of sections. However, for now, you should try to run the tests and make sure that they pass! You can do this by making sure EventTest.java is selected in Eclipse's Package Explorer, then use the context menu (usually bound to the right mouse button) to choose from the menu. This creates, if you don't have it already, a JUnit tab behind the Package Explorer tab in Eclipse. Assuming everything went ok, you should see the fabled and much discussed "green bar" in the upper left region of your display:

For most Java programmers that use JUnit this is the sign that "the world is ok" because all the tests are passing!
For fun, try changing the source the assertNotNull shown in the screenshot above (line
31) to assertNull; you will probably
see something more like this:

The dreaded "red bar" is shown in this shot! This means that
something is broken. You can see from this snap a lot of
information about the failure: You can see that it happened in the
test function testEventIsHandled
(because of the 'x' next to that test, as opposed to the check mark
by testDocumentCreationHandled). In
the lower section you have a stacktrace of the place where the
failure occurred: you can click on items in this stack trace to zip
to that location in the source code! You can also see part of the
text comment of the test that was being attempted, "Found the
even..." Be sure to put your test back to the way it was, with
assertNotNull, or you'll be seeing
that red bar forever!
You should probably read the two tests in EventTest.java, testEventIsHandled and testDocumentCreationHandled, follow along with
the commentary, and look at the Nuxeo and JUnit APIs used. The text
of the book does not cover each line of the code in detail!
We will start with a high level description of the purpose of testEventIsHandled. The objective of this test is very simple: to prove that if we create an event, any event, and send it through the Nuxeo infrastructure, our code under test will get called. The code to be tested for correctness can be seen by opening up the src/main/java directory in Eclipse and
With the preliminaries now over, let us proceed with the point
of this lesson! Events in Nuxeo are notifications, or messages,
that something interesting has happened. In this respect, Nuxeo is
identical to many of the other Java-based systems, such as Java's
Swing, that use Events to indicate to some part of a program that,
well, an event has occurred. The code that receives the
notification is referred to as an EventListener. Nuxeo has dozens
of different Events that a program may be interested in: A common
type are events that generated when there are changes to the
repository, such a documents being created, destroyed, or changing
versions. Let us look at a simple EventListener (this is an interface in Nuxeo) that is
contained in the skeleton for this lesson. You should open up the
privary Java source code folder (src/main/java) and then look in the package
org.nuxeo.upcoming. There is only one
file in there at this point, DocumentCreationListener.java:

This code, when put in our bundle, is going to create a title on a document automatically whenever a document of type Upcoming is created. It listens for an event indicating that a document has been created to know when to do its job. This listener is careful to not do so when other documents get created or when other events (that are not document creation events) are sent to it. The title generated for Upcoming documents is "Event XXX" where XXX is the date of the Event, displayed in a locale-specific way. It would be good for the reader to read through the comments in this file that begin with "//" and see the Nuxeo APIs used. The other type of comment, starting with "/**" and terminating with "**/", is used to enclose parts of the code that display interesting information. You can uncomment these and watch the console output to get more insight into how Nuxeo-in this case a Nuxeo EventListener-works. For instance, one of these commented sections in DocumentCreationListener will walk through all the properties that are part of the event and many readers may find this informative.
A key resource for any technical issue with Nuxeo is the Nuxeo Book. Events are covered in Chapter 28; if you reading this book you most likely can ignore all the content in that chapter about "old style" events since this book only covers the "new style." In addition, now that you are using the Java API of Nuxeo 5, you will most likely want to access the JavaDoc to see all the methods and types that you have available in the API.
A key Nuxeo notion seen in DocumentCreationListener is reified in the Java type EventContext and it's descendent type DocumentEventContext. This is contextual information that is associated with an event and the specific type of the context depends on the particular event type. As you can see from the snapshot above (lines 32-34), this Listener ignores any Event that does not have a context that meets the interface DocumentEventContext. This should not be surprise, we are only interested in Documents being created!
Also, critical to most event handlers, including the one shown here is to determine the "source" of the event-in other words, what object logically "caused" the event. Here is the snippet from the DocumentCreationListener.
//this gets the document that is the "cause" of the event, critical!
DocumentModel model = context.getSourceDocument();
This returns a reference to the DocumentModel that has just been created. Most of the time, event listeners will need to ask for information from or operate on the source of the event that they are interested in.
Finally, readers familiar with the more strongly-typed event system of Java Swing or other, similar systems should notice that Nuxeo events do not have "subtypes" as the Swing events do (MouseEvent, KeyboardEvent). The way to differentiate between the various types of Nuxeo events is by examining the name field (event.getName) and, if necessary, the type of the EventContext object. The example code given in this lesson does both of these.
Since this is the first lesson with Java code, most readers are likely to be more interested in the APIs used by the test code since these are more varied than the actual code under test. We will discuss the test code in some detail, since it introduces many new concepts.
Here is a listing of the body of the first test function,
testEventIsHandled:
public void testEventIsHandled() throws Exception {
//check for this name, if you want, using the debugger on the event listener
String eventName = "my event: there are many like it but this one is mine";
//get the event production service, so we can send an event
EventProducer producer=Framework.getService(EventProducer.class);
assertNotNull("Found the event producer ok?",producer);
//create a fake event object with some bogus values
Map<String, Serializable> props = new HashMap<String, Serializable>();
props.put("test-name", "testEventBasic");
//the event context is a really an "EventGenerator" in some sense
EventContext context=new InlineEventContext(null,props);
Event syntheticEvent = context.newEvent(eventName);
//the event service is the center of all event processing...
EventService service=Framework.getService(EventService.class);
assertNotNull("Found event service ok?",service);
//EventListenerHelper is the wrapper around our DocumentCreationListener
service.addEventListener(new EventListenerHelper());
//send the event and make sure it got handled!
assertFalse("No events handled yet?",props.containsKey(DocumentCreationListener.HANDLED_EVENT));
producer.fireEvent(syntheticEvent);
assertTrue("handled our event?",props.containsKey(DocumentCreationListener.HANDLED_EVENT));
}
This test does one, seemingly simple, thing: It creates an event
from scratch (syntheticEvent) and
transmits it to the code under test that expects events (see
previous section). To be sure that the code under test actually
ran, we create a map of properties (props) that the EventListener modifies in an particular way and we
test that this modification is observed. This has no functional
value, it just allows us to test "did the code under test run at
all."
In two places, the Nuxeo framework is asked to look up and
return a "service." This done through Framework.getService and the two services
retrieved are the EventProducer and the
EventService. Almost all of Nuxeo's
functionality is organized into services that offer various
functions to their clients. It is required that one access these
services via the Framework.getService
mechanism shown in this example rather than declaring an instance
of these types directly. The reasons for this are many and varied,
but the simplest one is that this allows the services to not be
created until they are first needed, if they are created at
all.
Each service is used once (we are doing a simple test!). The EventProducer is used to transmit the event and this is the "preferred" way for Events to be generated by code that wishes to send events; Nuxeo itself uses this same mechanism when it generates events internally. The EventService is used to "hook up" our event listener to the Event stream. We created a small helper class, EventListenerHelper, that is a thin wrapper around the true class DocumentCreationListener. This wrapper is normally generated by Nuxeo as it reads the XML file that contributes an EventListener to the proper extension point. To avoid the need for deploying our bundle-and to create the simplest possible test from a logical point of view-we created the small wrapper ourselves and added it to the EventService manually.
The code for the other test, that does use the normal Nuxeo bundling mechanisms:
public void testDocumentCreationHandled() throws Exception {
String path="mydoc";
//send our upcoming bundle to the infrastructure
deployBundle("org.nuxeo.book.upcoming");
//initialize the repository and session. these are implemented in base class
openRepository();
CoreSession session=getCoreSession();
//get a document type that represents our code, just to be sure we are ok
DocumentType upcoming = session.getDocumentType("Upcoming");
assertNotNull("Does our type exist?",upcoming);
//create the object that represents the new document
DocumentModel modelDesired=new DocumentModelImpl("/",path,"Upcoming");
//setup the properties that make it an upcoming event:
//one man show, today, by Bob Newhart (comedian) at a cost of 25.00
modelDesired.setProperty("upcoming", "occursOn", new Date());
modelDesired.setProperty("upcoming", "presenter", "bob newhart");
modelDesired.setProperty("upcoming", "admissionPrice", new Float(25.00f));
//create the document in the repository
DocumentModel modelResult = session.createDocument(modelDesired);
//make sure that the path is the parent path (/) plus our new path
assertEquals("path is same?","/"+path,modelResult.getPathAsString());
assertEquals("path is same? (sanity)", "/"+path, modelDesired.getPathAsString());
//document is created ok, let's see if event handler ran
assertNotSame("the result object is different than the desired object?",
modelDesired,modelResult);
String titleFromDublinCore = (String)modelDesired.getProperty("dublincore","title");
assertNull("document handler did not run on desired?",
titleFromDublinCore);
//make sure our handler computed everything correctly
titleFromDublinCore = (String)modelResult.getProperty("dublincore","title");
assertTrue("document handler ran ok on result?",
titleFromDublinCore.startsWith("Event"));
//don't bother saving any of these documents... normally you want to do
//coreSession.save() but since this is a test, let's not bother
coreSession.cancel();
}
Most test code that would be written for a new Nuxeo bundle will have this type of structure: deploy the bundle or bundles needed for the test, open the repository and get the core session, and then create the document or documents needed for the test. The CoreSession object is perhaps the most central object to the Nuxeo API; it is going to be used in one way or another by almost any Nuxeo application or bundle since it holds the means of accessing the repository, and thus documents.
The process of programmatically creating a document in Nuxeo can
also be seen in this test. The process is to create a new
DocumentModelImpl to implement the
DocumentModel that is desired, shown as
modelDesired. The DocumentModel implementation needs to know who the
parent is (like a directory in a normal file system) and the
document's type, "Upcoming" in our example here. After the desired
basics are set up, one uses the CoreSession method createDocument to create a "real" DocumentModel, called modelResult. In the case we have here, we set the
properties of the upcoming schema on the
desired document model so that the result DocumentModel would also have these properties.
However, the creation of the modelResult is an action that Nuxeo generates an
Event for and our EventListener should be
called and should generate a title for the document. This is
verified at the end of the test function by examining the
We have been a bit sloppy in this chapter and blurred the distinction between logical "documents" and the Nuxeo types used to manipulate Documents through the API. This has, unfortunately, been necessary because Nuxeo actually has several different Java types that can be used to manipulate "documents" depending on the situation. The most common for test code is DocumentModel which is lightweight representation of a document, including all the document's properties. A DocumentModel's properties and content are loaded only when accessed (it is lazy) and this optimization is extremely valuable for many applications. A true Nuxeo Document can be very expensive to manipulate, depending on exactly what state the document is in and what features of the system interact with it. For now, it is not terribly wrong for the reader to consider DocumentModel to be the basic "document type" in Nuxeo.
Because we wanted to focus this lesson on developing Java code and testing it in Eclipse, we did not mention earlier that there are other important files in the skeleton provided. We provided them in already working form, unlike previous lessons, because we wanted your tests to work "out of the box."
If you notice, we have added a new extension point contribution
src/main/resources/OSGI-INF/event-contrib.xml and
added to the MANIFEST.MF to reflect
this new contribution. The contribution looks like this:
<?xml version="1.0"?>
<component name="org.nuxeo.upcoming.event.documentCreationListener">
<extension target="org.nuxeo.ecm.core.event.EventServiceComponent"
point="listener">
<listener name="documentCreationListener" async="false" postCommit="false"
class="org.nuxeo.upcoming.DocumentCreationListener" priority="140">
</listener>
</extension>
</component>
This is the pattern for a simple event listener; the critical
part in the listener tag is to
get the correct class name that implements your listener. The
attributes async, postCommit, and priority are ways that you can modify
under what circumstances your event listener gets called. These
are, for now, unlikely to be of much interest other than possibly
priority. EventListeners are
called in an order based on their priority, with the lower values
having precedence. We do not recommend creating EventListeners that
have an ordering dependency, but sometimes it is unavoidable and if
you have two listener contributions, you can control which is
called first with this attribute. Choosing a priority that is high,
such as the 140 above, is a good practice when you don't really
care about the order of the calls.
For the first time in these lessons, we have now created a test
resource. This test resource is located in the file src/test/resources/log4j.properties. This file,
like the contents of src/main/resources, will be copied into the
bin subdirectory by Eclipse and the
target subdirectory by Maven. As a
result of the copying, log4j.properties is in the classpath of the
running tests. This file controls the amount of log data output
when running the tests. It
has no effect on normal operation and is not packaged into the
resulting bundle by Maven, since it has no function other than
during the running of the tests. As you can see from the listing
below, we have set the logging level to ERROR so we will only get output when a test
fails. If you need to debug a bundle that has failing tests, you
probably want to change the log level to INFO or DEBUG.
log4j.rootLogger=ERROR, CONSOLE
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{HH:mm:ss,SSS} %-5p [%C{1}] %m%n
Eclipse, as we have seen so far in this lesson, is used when you want to run tests and get "quick feedback" about the passing or failing of tests (the green bar). As you can see from the screenshots in this lesson, it usually takes only a few seconds to get the feedback from your tests. When all the tests are passing (and not before!) you should use maven to build, test (again), and package up your bundle. Let's try that for the skeleton in this lesson:
/nuxeo/workspace/lesson-events$ mvn clean install [INFO] Scanning for projects... -- lines elided for clarity -- [INFO] [compiler:testCompile] [INFO] Compiling 1 source file to /nuxeo/workspace/lesson-events/target/test-classes [INFO] [surefire:test] [INFO] Surefire report directory: /nuxeo/workspace/lesson-events/target/surefire-reports ------------------------------------------------------- T E S T S ------------------------------------------------------- Running org.nuxeo.upcoming.test.EventTest Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 3.751 sec Results : Tests run: 2, Failures: 0, Errors: 0, Skipped: 0 -- lines elided for clarity -- [INFO] Building jar: /nuxeo/workspace/lesson-events/target/lesson-events-0.0.1-sources.jar [INFO] [install:install] [INFO] Installing /nuxeo/workspace/lesson-events/target/lesson-events-0.0.1.jar to /home/ismith/.m2/repository/org/nuxeo/community/book/lesson-events/0.0.1/lesson-events-0.0.1.jar [INFO] Installing /nuxeo/workspace/lesson-events/target/lesson-events-0.0.1-sources.jar to /home/ismith/.m2/repository/org/nuxeo/community/book/lesson-events/0.0.1/lesson-events-0.0.1-sources.jar [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESSFUL [INFO] ------------------------------------------------------------------------ [INFO] Total time: 13 seconds [INFO] Finished at: Sat Feb 28 12:12:12 CET 2009 [INFO] Final Memory: 55M/408M [INFO] ------------------------------------------------------------------------
We have shown two key things in this example. Now that we have
some test code in src/test/java, it
is found by Maven during the build process and the tests are
executed. It is good practice to check that maven runs all the
tests successfully; there are cases where the classpath of Eclipse
can include extra libraries that are not in the maven classpath and
thus the maven tests will fail. This is a critical indicator,
because the maven classpath is the same as the "real" one that will
be used as your bundle is loaded into Nuxeo server. You can see a
summary of the tests in the line above that starts with "Tests
run:"... if there are failures, you can find the information about
the failures in files in the directory target/surefire-tests.
The other thing to note is that the lesson-events-0.0.1.jar gets built and placed in
the directory target as well as some
other places. However, if you inspect that file carefully (using
jar or your
filesystem browser) it does not contain the test code or test
libraries (such as JUnit). Maven is smart enough to not package the
parts you need only for running the tests! You can see that there
is only the EventListener class in our
bundle like this:
/nuxeo/workspace/lesson-events/target$ jar tf lesson-events-0.0.1.jar | grep .class
org/nuxeo/upcoming/DocumentCreationListener.class
As before, you should copy your bundle from the target directory of your workspace to the
plugins directory of your Nuxeo 5
server. Then create a new document of type Upcoming, as in shown in
this screen snap:

After we hit the Create button, we get a list of the document's properties and "No Chance" indeed did not have a chance!

So, we have now seen the output of the EventListener in action!