diff options
author | Peter Hancock <phancock@apache.org> | 2012-08-16 13:31:38 +0000 |
---|---|---|
committer | Peter Hancock <phancock@apache.org> | 2012-08-16 13:31:38 +0000 |
commit | 80e1071780dacf1235c0f31bf20fe6c6df7ff8e8 (patch) | |
tree | 012a241ed426e2d10965ab402f04f84c9827b421 /src/documentation/content/xdocs/1.1rc1/events.xml | |
parent | 3e96fb119fcbd9e6e0644092b0e2376bdbbe6c10 (diff) | |
parent | 726e1ef3093bedfd6671c2e6471e0d62ef605be5 (diff) | |
download | xmlgraphics-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.xml | 449 |
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<String, Object></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 |