aboutsummaryrefslogtreecommitdiffstats
path: root/src/documentation/content/xdocs/1.1rc1/events.xml
diff options
context:
space:
mode:
authorPeter Hancock <phancock@apache.org>2012-08-16 13:31:38 +0000
committerPeter Hancock <phancock@apache.org>2012-08-16 13:31:38 +0000
commit80e1071780dacf1235c0f31bf20fe6c6df7ff8e8 (patch)
tree012a241ed426e2d10965ab402f04f84c9827b421 /src/documentation/content/xdocs/1.1rc1/events.xml
parent3e96fb119fcbd9e6e0644092b0e2376bdbbe6c10 (diff)
parent726e1ef3093bedfd6671c2e6471e0d62ef605be5 (diff)
downloadxmlgraphics-fop-80e1071780dacf1235c0f31bf20fe6c6df7ff8e8.tar.gz
xmlgraphics-fop-80e1071780dacf1235c0f31bf20fe6c6df7ff8e8.zip
Merged trunk@1373227
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_RoundedCorners@1373825 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src/documentation/content/xdocs/1.1rc1/events.xml')
-rw-r--r--src/documentation/content/xdocs/1.1rc1/events.xml449
1 files changed, 449 insertions, 0 deletions
diff --git a/src/documentation/content/xdocs/1.1rc1/events.xml b/src/documentation/content/xdocs/1.1rc1/events.xml
new file mode 100644
index 000000000..27daad59b
--- /dev/null
+++ b/src/documentation/content/xdocs/1.1rc1/events.xml
@@ -0,0 +1,449 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- $Id$ -->
+<!DOCTYPE document PUBLIC "-//APACHE//DTD Documentation V2.0//EN" "http://forrest.apache.org/dtd/document-v20.dtd">
+<document>
+ <header>
+ <title>Apache™ FOP: Events/Processing Feedback</title>
+ <version>$Revision$</version>
+ </header>
+ <body>
+ <section id="introduction">
+ <title>Introduction</title>
+ <p>
+ In versions until 0.20.5, Apache™ FOP used
+ <a href="http://excalibur.apache.org/framework/index.html">Avalon-style Logging</a> where
+ it was possible to supply a logger per processing run. During the redesign
+ the logging infrastructure was switched over to
+ <a href="http://commons.apache.org/logging/">Commons Logging</a> which is (like Log4J or
+ java.util.logging) a "static" logging framework (the logger is accessed through static
+ variables). This made it very difficult in a multi-threaded system to retrieve information
+ for a single processing run.
+ </p>
+ <p>
+ With FOP's event subsystem, we'd like to close this gap again and even go further. The
+ first point is to realize that we have two kinds of "logging". Firstly, we have the logging
+ infrastructure for the (FOP) developer who needs to be able to enable finer log messages
+ for certain parts of FOP to track down a certain problem. Secondly, we have the user who
+ would like to be informed about missing images, overflowing lines or substituted fonts.
+ These messages (or events) are targeted at less technical people and may ideally be
+ localized (translated). Furthermore, tool and solution builders would like to integrate
+ FOP into their own solutions. For example, an FO editor should be able to point the user
+ to the right place where a particular problem occurred while developing a document template.
+ Finally, some integrators would like to abort processing if a resource (an image or a font)
+ has not been found, while others would simply continue. The event system allows to
+ react on these events.
+ </p>
+ <p>
+ On this page, we won't discuss logging as such. We will show how the event subsystem can
+ be used for various tasks. We'll first look at the event subsystem from the consumer side.
+ Finally, the production of events inside FOP will be discussed (this is mostly interesting
+ for FOP developers only).
+ </p>
+ </section>
+ <section id="consumer">
+ <title>The consumer side</title>
+ <p>
+ The event subsystem is located in the <code>org.apache.fop.events</code> package and its
+ base is the <code>Event</code> class. An instance is created for each event and is sent
+ to a set of <code>EventListener</code> instances by the <code>EventBroadcaster</code>.
+ An <code>Event</code> contains:
+ </p>
+ <ul>
+ <li>an event ID,</li>
+ <li>a source object (which generated the event),</li>
+ <li>a severity level (Info, Warning, Error and Fatal Error) and</li>
+ <li>a map of named parameters.</li>
+ </ul>
+ <p>
+ The <code>EventFormatter</code> class can be used to translate the events into
+ human-readable, localized messages.
+ </p>
+ <p>
+ A full example of what is shown here can be found in the
+ <code>examples/embedding/java/embedding/events</code> directory in the FOP distribution.
+ The example can also be accessed
+ <a href="http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/examples/embedding/java/embedding/events/">via the web</a>.
+ </p>
+ <section id="write-listener">
+ <title>Writing an EventListener</title>
+ <p>
+ The following code sample shows a very simple EventListener. It basically just sends
+ all events to System.out (stdout) or System.err (stderr) depending on the event severity.
+ </p>
+ <source><![CDATA[import org.apache.fop.events.Event;
+import org.apache.fop.events.EventFormatter;
+import org.apache.fop.events.EventListener;
+import org.apache.fop.events.model.EventSeverity;
+
+/** A simple event listener that writes the events to stdout and stderr. */
+public class SysOutEventListener implements EventListener {
+
+ /** {@inheritDoc} */
+ public void processEvent(Event event) {
+ String msg = EventFormatter.format(event);
+ EventSeverity severity = event.getSeverity();
+ if (severity == EventSeverity.INFO) {
+ System.out.println("[INFO ] " + msg);
+ } else if (severity == EventSeverity.WARN) {
+ System.out.println("[WARN ] " + msg);
+ } else if (severity == EventSeverity.ERROR) {
+ System.err.println("[ERROR] " + msg);
+ } else if (severity == EventSeverity.FATAL) {
+ System.err.println("[FATAL] " + msg);
+ } else {
+ assert false;
+ }
+ }
+}]]></source>
+ <p>
+ You can see that for every event the method <code>processEvent</code> of the
+ <code>EventListener</code> will be called. Inside this method you can do whatever
+ processing you would like including throwing a <code>RuntimeException</code>, if you want
+ to abort the current processing run.
+ </p>
+ <p>
+ The code above also shows how you can turn an event into a human-readable, localized
+ message that can be presented to a user. The <code>EventFormatter</code> class does
+ this for you. It provides additional methods if you'd like to explicitly specify
+ the locale.
+ </p>
+ <p>
+ It is possible to gather all events for a whole processing run so they can be
+ evaluated afterwards. However, care should be taken about memory consumption since
+ the events provide references to objects inside FOP which may themselves have
+ references to other objects. So holding on to these objects may mean that whole
+ object trees cannot be released!
+ </p>
+ </section>
+ <section id="add-listener">
+ <title>Adding an EventListener</title>
+ <p>
+ To register the event listener with FOP, get the <code>EventBroadcaster</code> which
+ is associated with the user agent (<code>FOUserAgent</code>) and add it there:
+ </p>
+ <source><![CDATA[FOUserAgent foUserAgent = fopFactory.newFOUserAgent();
+foUserAgent.getEventBroadcaster().addEventListener(new SysOutEventListener());]]></source>
+ <p>
+ Please note that this is done separately for each processing run, i.e. for each
+ new user agent.
+ </p>
+ </section>
+ <section id="listener-example1">
+ <title>An additional listener example</title>
+ <p>
+ Here's an additional example of an event listener:
+ </p>
+ <p>
+ By default, FOP continues processing even if an image wasn't found. If you have
+ more strict requirements and want FOP to stop if an image is not available, you can
+ do something like the following in the simplest case:
+ </p>
+ <source><![CDATA[public class MyEventListener implements EventListener {
+
+ public void processEvent(Event event) {
+ if ("org.apache.fop.ResourceEventProducer".equals(
+ event.getEventGroupID())) {
+ event.setSeverity(EventSeverity.FATAL);
+ } else {
+ //ignore all other events (or do something of your choice)
+ }
+ }
+
+}]]></source>
+ <p>
+ Increasing the event severity to FATAL will signal the event broadcaster to throw
+ an exception and stop further processing. In the above case, all resource-related
+ events will cause FOP to stop processing.
+ </p>
+ <p>
+ You can also customize the exception to throw (you can may throw a RuntimeException
+ or subclass yourself) and/or which event to respond to:
+ </p>
+ <source><![CDATA[public class MyEventListener implements EventListener {
+
+ public void processEvent(Event event) {
+ if ("org.apache.fop.ResourceEventProducer.imageNotFound"
+ .equals(event.getEventID())) {
+
+ //Get the FileNotFoundException that's part of the event's parameters
+ FileNotFoundException fnfe = (FileNotFoundException)event.getParam("fnfe");
+
+ throw new RuntimeException(EventFormatter.format(event), fnfe);
+ } else {
+ //ignore all other events (or do something of your choice)
+ }
+ }
+
+}]]></source>
+ <p>
+ This throws a <code>RuntimeException</code> with the <code>FileNotFoundException</code>
+ as the cause. Further processing effectively stops in FOP. You can catch the exception
+ in your code and react as you see necessary.
+ </p>
+ </section>
+ </section>
+ <section id="producer">
+ <title>The producer side (for FOP developers)</title>
+ <p>
+ This section is primarily for FOP and FOP plug-in developers. It describes how to use
+ the event subsystem for producing events.
+ </p>
+ <note>
+ The event package has been designed in order to be theoretically useful for use cases
+ outside FOP. If you think this is interesting independently from FOP, please talk to
+ <a href="mailto:fop-dev@xmlgraphics.apache.org">us</a>.
+ </note>
+ <section id="basic-event-production">
+ <title>Producing and sending an event</title>
+ <p>
+ The basics are very simple. Just instantiate an <code>Event</code> object and fill
+ it with the necessary parameters. Then pass it to the <code>EventBroadcaster</code>
+ which distributes the events to the interested listeneners. Here's a code example:
+ </p>
+ <source><![CDATA[Event ev = new Event(this, "complain", EventSeverity.WARN,
+ Event.paramsBuilder()
+ .param("reason", "I'm tired")
+ .param("blah", new Integer(23))
+ .build());
+EventBroadcaster broadcaster = [get it from somewhere];
+broadcaster.broadcastEvent(ev);
+]]></source>
+ <p>
+ The <code>Event.paramsBuilder()</code> is a
+ <a href="http://en.wikipedia.org/wiki/Fluent_interface">fluent interface</a>
+ to help with the build-up of the parameters. You could just as well instantiate a
+ <code>Map</code> (<code>Map&lt;String, Object&gt;</code>) and fill it with values.
+ </p>
+ </section>
+ <section id="event-producer">
+ <title>The EventProducer interface</title>
+ <p>
+ To simplify event production, the event subsystem provides the <code>EventProducer</code>
+ interface. You can create interfaces which extend <code>EventProducer</code>. These
+ interfaces will contain one method per event to be generated. By contract, each event
+ method must have as its first parameter a parameter named "source" (Type Object) which
+ indicates the object that generated the event. After that come an arbitrary number of
+ parameters of any type as needed by the event.
+ </p>
+ <p>
+ The event producer interface does not need to have any implementation. The implementation
+ is produced at runtime by a dynamic proxy created by <code>DefaultEventBroadcaster</code>.
+ The dynamic proxy creates <code>Event</code> instances for each method call against
+ the event producer interface. Each parameter (except "source") is added to the event's
+ parameter map.
+ </p>
+ <p>
+ To simplify the code needed to get an instance of the event producer interface it is
+ suggested to create a public inner provider class inside the interface.
+ </p>
+ <p>
+ Here's an example of such an event producer interface:
+ </p>
+ <source><![CDATA[public interface MyEventProducer extends EventProducer {
+
+ public class Provider {
+
+ public static MyEventProducer get(EventBroadcaster broadcaster) {
+ return (MyEventProducer)broadcaster.getEventProducerFor(MyEventProducer.class);
+ }
+ }
+
+ /**
+ * Complain about something.
+ * @param source the event source
+ * @param reason the reason for the complaint
+ * @param blah the complaint
+ * @event.severity WARN
+ */
+ void complain(Object source, String reason, int blah);
+
+}]]></source>
+ <p>
+ To produce the same event as in the first example above, you'd use the following code:
+ </p>
+ <source><![CDATA[EventBroadcaster broadcaster = [get it from somewhere];
+TestEventProducer producer = TestEventProducer.Provider.get(broadcaster);
+producer.complain(this, "I'm tired", 23);]]></source>
+ </section>
+ <section id="event-model">
+ <title>The event model</title>
+ <p>
+ Inside an invocation handler for a dynamic proxy, there's no information about
+ the names of each parameter. The JVM doesn't provide it. The only thing you know is
+ the interface and method name. In order to properly fill the <code>Event</code>'s
+ parameter map we need to know the parameter names. These are retrieved from an
+ event object model. This is found in the <code>org.apache.fop.events.model</code>
+ package. The data for the object model is retrieved from an XML representation of the
+ event model that is loaded as a resource. The XML representation is generated using an
+ Ant task at build time (<code>ant resourcegen</code>). The Ant task (found in
+ <code>src/codegen/java/org/apache/fop/tools/EventProducerCollectorTask.java</code>)
+ scans FOP's sources for descendants of the <code>EventProducer</code> interface and
+ uses <a href="http://qdox.codehaus.org/">QDox</a> to parse these interfaces.
+ </p>
+ <p>
+ The event model XML files are generated during build by the Ant task mentioned above when
+ running the "resourcegen" task. So just run <code>"ant resourcegen"</code> if you receive
+ a <code>MissingResourceException</code> at runtime indicating that
+ <code>"event-model.xml"</code> is missing.
+ </p>
+ <p>
+ Primarily, the QDox-based collector task records the parameters' names and types.
+ Furthermore, it extracts additional attributes embedded as Javadoc comments from
+ the methods. At the moment, the only such attribute is "@event.severity" which indicates
+ the default event severity (which can be changed by event listeners). The example event
+ producer above shows the Javadocs for an event method.
+ </p>
+ <p>
+ There's one more information that is extracted from the event producer information for
+ the event model: an optional primary exception. The first exception in the "throws"
+ declaration of an event method is noted. It is used to throw an exception from
+ the invocation handler if the event has an event severity of "FATAL" when all
+ listeners have been called (listeners can update the event severity). Please note
+ that an implementation of
+ <code>org.apache.fop.events.EventExceptionManager$ExceptionFactory</code> has to be
+ registered for the <code>EventExceptionManager</code> to be able to construct the
+ exception from an event.
+ </p>
+ <p>
+ For a given application, there can be multiple event models active at the same time.
+ In FOP, each renderer is considered to be a plug-in and provides its own specific
+ event model. The individual event models are provided through an
+ <code>EventModelFactory</code>. This interface is implemented for each event model
+ and registered through the service provider mechanism
+ (see the <a href="#plug-ins">plug-ins section</a> for details).
+ </p>
+ </section>
+ <section id="event-severity">
+ <title>Event severity</title>
+ <p>
+ Four different levels of severity for events has been defined:
+ </p>
+ <ol>
+ <li>INFO: informational only</li>
+ <li>WARN: a Warning</li>
+ <li>ERROR: an error condition from which FOP can recover. FOP will continue processing.</li>
+ <li>FATAL: a fatal error which causes an exception in the end and FOP will stop processing.</li>
+ </ol>
+ <p>
+ Event listeners can choose to ignore certain events based on their event severity.
+ Please note that you may recieve an event "twice" in a specific case: if there is
+ a fatal error an event is generated and sent to the listeners. After that an exception
+ is thrown with the same information and processing stops. If the fatal event is
+ shown to the user and the following exception is equally presented to the user it
+ may appear that the event is duplicated. Of course, the same information is just
+ published through two different channels.
+ </p>
+ </section>
+ <section id="plug-ins">
+ <title>Plug-ins to the event subsystem</title>
+ <p>
+ The event subsystem is extensible. There are a number of extension points:
+ </p>
+ <ul>
+ <li>
+ <strong><code>org.apache.fop.events.model.EventModelFactory</code>:</strong> Provides
+ an event model to the event subsystem.
+ </li>
+ <li>
+ <strong><code>org.apache.fop.events.EventExceptionManager$ExceptionFactory</code>:</strong>
+ Creates exceptions for events, i.e. turns an event into a specific exception.
+ </li>
+ </ul>
+ <p>
+ The names in bold above are used as filenames for the service provider files that
+ are placed in the <code>META-INF/services</code> directory. That way, they are
+ automatically detected. This is a mechanism defined by the
+ <a href="http://java.sun.com/j2se/1.4.2/docs/guide/jar/jar.html#Service%20Provider">JAR file specification</a>.
+ </p>
+ </section>
+ <section id="l10n">
+ <title>Localization (L10n)</title>
+ <p>
+ One goal of the event subsystem was to have localized (translated) event messages.
+ The <code>EventFormatter</code> class can be used to convert an event to a
+ human-readable message. Each <code>EventProducer</code> can provide its own XML-based
+ translation file. If there is none, a central translation file is used, called
+ "EventFormatter.xml" (found in the same directory as the <code>EventFormatter</code>
+ class).
+ </p>
+ <p>
+ The XML format used by the <code>EventFormatter</code> is the same as
+ <a href="ext:cocoon">Apache Cocoon's</a> catalog format. Here's an example:
+ </p>
+ <source><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
+<catalogue xml:lang="en">
+ <message key="locator">
+ [ (See position {loc})| (See {#gatherContextInfo})| (No context info available)]
+ </message>
+ <message key="org.apache.fop.render.rtf.RTFEventProducer.explicitTableColumnsRequired">
+ RTF output requires that all table-columns for a table are defined. Output will be incorrect.{{locator}}
+ </message>
+ <message key="org.apache.fop.render.rtf.RTFEventProducer.ignoredDeferredEvent">
+ Ignored deferred event for {node} ({start,if,start,end}).{{locator}}
+ </message>
+</catalogue>
+]]></source>
+ <p>
+ The example (extracted from the RTF handler's event producer) has message templates for
+ two event methods. The class used to do variable replacement in the templates is
+ <code>org.apache.fop.util.text.AdvancedMessageFormat</code> which is more powerful
+ than the <code>MessageFormat</code> classes provided by the Java class library
+ (<code>java.util.text</code> package).
+ </p>
+ <p>
+ "locator" is a template that is reused by the other message templates
+ by referencing it through "{{locator}}". This is some kind of include command.
+ </p>
+ <p>
+ Normal event parameters are accessed by name inside single curly braces, for example:
+ "{node}". For objects, this format just uses the <code>toString()</code> method to turn
+ the object into a string, unless there is an <code>ObjectFormatter</code> registered
+ for that type (there's an example for <code>org.xml.sax.Locator</code>).
+ </p>
+ <p>
+ The single curly braces pattern supports additional features. For example, it is possible
+ to do this: "{start,if,start,end}". "if" here is a special field modifier that evaluates
+ "start" as a boolean and if that is true returns the text right after the second comma
+ ("start"). Otherwise it returns the text after the third comma ("end"). The "equals"
+ modifier is similar to "if" but it takes as an additional (comma-separated) parameter
+ right after the "equals" modifier, a string that is compared to the value of the variable.
+ An example: {severity,equals,EventSeverity:FATAL,,some text} (this adds "some text" if
+ the severity is not FATAL).
+ </p>
+ <p>
+ Additional such modifiers can be added by implementing the
+ <code>AdvancedMessageFormat$Part</code> and <code>AdvancedMessageFormat$PartFactory</code>
+ interfaces.
+ </p>
+ <p>
+ Square braces can be used to specify optional template sections. The whole section will
+ be omitted if any of the variables used within are unavailable. Pipe (|) characters can
+ be used to specify alternative sub-templates (see "locator" above for an example).
+ </p>
+ <p>
+ Developers can also register a function (in the above example:
+ <code>{#gatherContextInfo})</code>
+ to do more complex information rendering. These functions are implementations of the
+ <code>AdvancedMessageFormat$Function</code> interface. Please take care that this is
+ done in a locale-independent way as there is no locale information available, yet.
+ </p>
+ </section>
+ </section>
+ </body>
+</document> \ No newline at end of file