Chapter 8. Events And Event Listeners [DRAFT]

Table of Contents

8.1. This is a DRAFT! Give your opinion!
8.2. Java Coding With Nuxeo EP
8.2.1. The Maven v. Eclipse battle, round 2
8.2.2. What Eclipse sees
8.3. Testing Java code
8.4. Events and Event Listeners
8.4.1. Key resources
8.4.2. Highlights of the EventListener code
8.5. The test code
8.5.1. Highlights of testEventIsHandled
8.5.2. Highlights of testDocumentCreationHandled
8.5.3. Why DocumentModel and not Document?
8.6. Other files
8.6.1. The EventListener contribution
8.6.2. log4j.properties
8.7. Back to Maven
8.8. Verifying the event handler in the Nuxeo UI
8.9. Exercises

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)

8.1. This is a DRAFT! Give your opinion!

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.

8.2. Java Coding With Nuxeo EP

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.

Note

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.

8.2.1. The Maven v. Eclipse battle, round 2

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.

Note

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.

8.2.2. What Eclipse sees

You should now open the project in Eclipse and select the project name then choose Project > Properties and then Java Build Path. 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).

Warning

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 Source 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.

8.3. Testing Java code

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 Run > As JUnit Test 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

8.4. Events and Event Listeners

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.

8.4.1. Key resources

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.

8.4.2. Highlights of the EventListener code

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.

8.5. The test code

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.

8.5.1. Highlights of testEventIsHandled

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.

8.5.2. Highlights of testDocumentCreationHandled

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

8.5.3. Why DocumentModel and not Document?

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.

8.6. Other files

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."

8.6.1. The EventListener contribution

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.

8.6.2. log4j.properties

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

8.7. Back to Maven

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

8.8. Verifying the event handler in the Nuxeo UI

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!

8.9. Exercises

  1. Since the title is now overwritten by our event handler, change the UI so that the title and description are neither requested when creating or editing a document, nor displayed when showing the document's properties.