[logback-dev] svn commit: r1775 - in logback/trunk: logback-examples/src/main/java/chapter3 logback-site/src/site/pages logback-site/src/site/pages/manual
noreply.ceki at qos.ch
noreply.ceki at qos.ch
Sun Aug 24 00:25:55 CEST 2008
Author: ceki
Date: Sun Aug 24 00:25:55 2008
New Revision: 1775
Added:
logback/trunk/logback-examples/src/main/java/chapter3/containingConfig.xml
- copied, changed from r1772, /logback/trunk/logback-examples/src/main/java/chapter3/redirectConfig.xml
logback/trunk/logback-site/src/site/pages/joran.html
Removed:
logback/trunk/logback-examples/src/main/java/chapter3/redirectConfig.xml
Modified:
logback/trunk/logback-examples/src/main/java/chapter3/includedConfig.xml
logback/trunk/logback-examples/src/main/java/chapter3/variableSubstitution1.xml
logback/trunk/logback-examples/src/main/java/chapter3/variableSubstitution2.xml
logback/trunk/logback-examples/src/main/java/chapter3/variableSubstitution3.xml
logback/trunk/logback-examples/src/main/java/chapter3/variableSubstitution4.xml
logback/trunk/logback-examples/src/main/java/chapter3/variables1.properties
logback/trunk/logback-examples/src/main/java/chapter3/variables2.properties
logback/trunk/logback-site/src/site/pages/documentation.html
logback/trunk/logback-site/src/site/pages/manual/joran.html
Log:
- ongoing work on the documentation
Chapter3 has been split into two parts, the fist part on logback configuration and a second part on Joran as a standalone framework
Copied: logback/trunk/logback-examples/src/main/java/chapter3/containingConfig.xml (from r1772, /logback/trunk/logback-examples/src/main/java/chapter3/redirectConfig.xml)
==============================================================================
--- /logback/trunk/logback-examples/src/main/java/chapter3/redirectConfig.xml (original)
+++ logback/trunk/logback-examples/src/main/java/chapter3/containingConfig.xml Sun Aug 24 00:25:55 2008
@@ -2,6 +2,11 @@
<configuration>
- <include file="path/to/configuration/file" />
+ <include file="src/main/java/chapter3/includedConfig.xml" />
+
+ <root level="DEBUG">
+ <appender-ref ref="includedConsole" />
+ </root>
+
</configuration>
Modified: logback/trunk/logback-examples/src/main/java/chapter3/includedConfig.xml
==============================================================================
--- logback/trunk/logback-examples/src/main/java/chapter3/includedConfig.xml (original)
+++ logback/trunk/logback-examples/src/main/java/chapter3/includedConfig.xml Sun Aug 24 00:25:55 2008
@@ -2,16 +2,10 @@
<included>
- <appender name="redirectConsole"
- class="ch.qos.logback.core.ConsoleAppender">
+ <appender name="includedConsole" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<param name="Pattern" value="%d - %m%n" />
</layout>
</appender>
- <root>
- <level value="DEBUG" />
- <appender-ref ref="redirectConsole" />
- </root>
-
</included>
\ No newline at end of file
Modified: logback/trunk/logback-examples/src/main/java/chapter3/variableSubstitution1.xml
==============================================================================
--- logback/trunk/logback-examples/src/main/java/chapter3/variableSubstitution1.xml (original)
+++ logback/trunk/logback-examples/src/main/java/chapter3/variableSubstitution1.xml Sun Aug 24 00:25:55 2008
@@ -1,9 +1,9 @@
<configuration>
- <substitutionProperty name="user.home.dir" value="/home/sebastien" />
+ <substitutionProperty name="USER_HOME" value="/home/sebastien" />
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
- <file>${user.home.dir}/myApp.log</file>
+ <file>${USER_HOME}/myApp.log</file>
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>%msg%n</Pattern>
</layout>
Modified: logback/trunk/logback-examples/src/main/java/chapter3/variableSubstitution2.xml
==============================================================================
--- logback/trunk/logback-examples/src/main/java/chapter3/variableSubstitution2.xml (original)
+++ logback/trunk/logback-examples/src/main/java/chapter3/variableSubstitution2.xml Sun Aug 24 00:25:55 2008
@@ -1,18 +1,9 @@
<configuration>
- <appender name="FILE"
- class="ch.qos.logback.core.rolling.RollingFileAppender">
- <file>${user.home.dir}/myApp.log</file>
- <rollingPolicy
- class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
- <FileNamePattern>
- myApp-%d{yyyy-MM-dd-HH}.log
- </FileNamePattern>
- </rollingPolicy>
+ <appender name="FILE" class="ch.qos.logback.core.FileAppender">
+ <file>${USER_HOME}/myApp.log</file>
<layout class="ch.qos.logback.classic.PatternLayout">
- <Pattern>
- %date %level [%thread] %logger{10} [%file : %line] %msg%n
- </Pattern>
+ <Pattern>%msg%n</Pattern>
</layout>
</appender>
Modified: logback/trunk/logback-examples/src/main/java/chapter3/variableSubstitution3.xml
==============================================================================
--- logback/trunk/logback-examples/src/main/java/chapter3/variableSubstitution3.xml (original)
+++ logback/trunk/logback-examples/src/main/java/chapter3/variableSubstitution3.xml Sun Aug 24 00:25:55 2008
@@ -2,19 +2,10 @@
<substitutionProperty file="variables1.properties" />
- <appender name="FILE"
- class="ch.qos.logback.core.rolling.RollingFileAppender">
- <file>${user.home.dir}/myApp.log</file>
- <rollingPolicy
- class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
- <FileNamePattern>
- myApp-%d{yyyy-MM-dd-HH}.log
- </FileNamePattern>
- </rollingPolicy>
+ <appender name="FILE" class="ch.qos.logback.core.FileAppender">
+ <file>${USER_HOME}/myApp.log</file>
<layout class="ch.qos.logback.classic.PatternLayout">
- <Pattern>
- %date %level [%thread] %logger{10} [%file : %line] %msg%n
- </Pattern>
+ <Pattern>%msg%n</Pattern>
</layout>
</appender>
Modified: logback/trunk/logback-examples/src/main/java/chapter3/variableSubstitution4.xml
==============================================================================
--- logback/trunk/logback-examples/src/main/java/chapter3/variableSubstitution4.xml (original)
+++ logback/trunk/logback-examples/src/main/java/chapter3/variableSubstitution4.xml Sun Aug 24 00:25:55 2008
@@ -2,19 +2,10 @@
<substitutionProperty file="variables2.properties" />
- <appender name="FILE"
- class="ch.qos.logback.core.rolling.RollingFileAppender">
- <file>${destination}</file>
- <rollingPolicy
- class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
- <FileNamePattern>
- myApp-%d{yyyy-MM-dd-HH}.log
- </FileNamePattern>
- </rollingPolicy>
+ <appender name="FILE" class="ch.qos.logback.core.FileAppender">
+ <file>${destination}/myApp.log</file>
<layout class="ch.qos.logback.classic.PatternLayout">
- <Pattern>
- %date %level [%thread] %logger{10} [%file : %line] %msg%n
- </Pattern>
+ <Pattern>%msg%n</Pattern>
</layout>
</appender>
Modified: logback/trunk/logback-examples/src/main/java/chapter3/variables1.properties
==============================================================================
--- logback/trunk/logback-examples/src/main/java/chapter3/variables1.properties (original)
+++ logback/trunk/logback-examples/src/main/java/chapter3/variables1.properties Sun Aug 24 00:25:55 2008
@@ -1 +1 @@
-user.home.dir=/Users/seb
\ No newline at end of file
+USER_HOME=/home/sebastien
\ No newline at end of file
Modified: logback/trunk/logback-examples/src/main/java/chapter3/variables2.properties
==============================================================================
--- logback/trunk/logback-examples/src/main/java/chapter3/variables2.properties (original)
+++ logback/trunk/logback-examples/src/main/java/chapter3/variables2.properties Sun Aug 24 00:25:55 2008
@@ -1,3 +1,3 @@
-user.home.dir=/Users/seb
+USER_HOME=/home/sebastien
fileName=myApp.log
-destination=${user.home.dir}/${fileName}
\ No newline at end of file
+destination=${USER_HOME}/${fileName}
\ No newline at end of file
Modified: logback/trunk/logback-site/src/site/pages/documentation.html
==============================================================================
--- logback/trunk/logback-site/src/site/pages/documentation.html (original)
+++ logback/trunk/logback-site/src/site/pages/documentation.html Sun Aug 24 00:25:55 2008
@@ -38,7 +38,11 @@
<li>
<a href="dependencies.html">Dependencies per module</a>
</li>
-
+
+ <li>
+ <a href="joran.html">Using Joran in your applications</a>
+ </li>
+
<li>
<a href="jmxConfig.html">How to use the logback JMX Configurator</a>
</li>
Added: logback/trunk/logback-site/src/site/pages/joran.html
==============================================================================
--- (empty file)
+++ logback/trunk/logback-site/src/site/pages/joran.html Sun Aug 24 00:25:55 2008
@@ -0,0 +1,512 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
+<title>Using Joran</title>
+<link rel="stylesheet" type="text/css" media="screen" href="css/site.css" />
+<link rel="stylesheet" type="text/css" media="print" href="css/print.css" />
+
+</head>
+<body>
+ <script type="text/javascript">prefix='./'</script>
+ <script type="text/javascript" src="templates/header.js"></script>
+<div id="left">
+ <script type="text/javascript" src="templates/left.js"></script>
+</div>
+<div id="right"><script type="text/javascript" src="templates/right.js"></script></div>
+<div id="content">
+
+ <h2>Using Joran in your own applications</h2>
+
+ <p>As we've seen, logback relies on Joran, a mature, flexible and
+ powerful configuration framework. Many of the capabilities offered
+ by logback modules are possible with the help of Joran.
+ </p>
+
+ <p>Joran is actually a generic configuration system which can be
+ used independently of logging. To emphaises this point, we should
+ mention that the logback-core module does not have a notion of
+ loggers. In that spirit, many of the examples related to this
+ tutorial, have nothing to do with loggers, appenders or layouts.
+ </p>
+
+ <p class="red" style="text-decoration: line-through;">The examples
+ for this chapter can be found under
+ <em>LOGBACK_HOME/logback-examples/src/main/java/chapter3</em>.
+ </p>
+
+ <p>To install joran, simply <a href="../download.html">download</a>
+ logback and add <em>logback-core-${version}.jar</em> to your
+ classpath.</p>
+
+ <h2>Historical perspective</h2>
+
+ <p>One of the most powerful features of the Java language is
+ reflection. Reflection makes it possible to configure software
+ systems declaratively. For example, many important properties of an
+ EJB are configured with the <em>ejb.xml</em> file. While EJBs are
+ written in Java, many of their properties are specified within the
+ <em>ejb.xml</em> file. Similarly, logback settings can be specified
+ in a configuration file, expressed in XML format.
+ </p>
+
+ <p>In log4j, logback's predecessor, <code>DOMConfigurator</code>
+ which shipped with log4j version 1.2.x could also parse
+ configuration files written in XML. The <code>DOMConfigurator</code>
+ was written in a way that forced to tweak it each time the structure
+ of the configuration file changed. The modified code had to be
+ recompiled and redeployed. Just as importantly, the code of the
+ DOMConfigurator consists of loops dealing with children elements
+ containing many interspersed if/else statements. One could not help
+ but notice that that particular code reeked of redundancy. The <a
+ href="http://jakarta.apache.org/commons/digester/">digester
+ project</a> has shown that it is possible to parse XML files using
+ pattern matching rules. At parse time, digester will apply the rules
+ that match previously stated patterns. Rule classes are usually
+ quite small and specialized. Consequently, they are relatively easy
+ to understand and to maintain.
+ </p>
+
+ <p>Joran is heavily inspired by the commons-digester project but
+ uses a slightly different terminology. In commons-digester, a rule
+ can be seen as consisting of a pattern and a rule, as shown by the
+ <code>Digester.addRule(String pattern, Rule rule)</code> method. We
+ find it unnecessarily confusing to have a rule to consist of itself,
+ not recursively but with a different meaning. In Joran, a rule
+ consists of a pattern and an action. An action is invoked when a
+ match occurs for the corresponding pattern. This relation between
+ patterns and actions lies at the core of Joran. Quite remarkably,
+ one can deal with quite complex requirements by using simple
+ patterns, or more precisely with exact matches and wildcard
+ matches. For example, the pattern <em>a/b</em> will match a
+ <code><b></code> element nested within an <code><a></code>
+ element but not a <code><c></code> element, even if nested within
+ a <code><b></code> element. It is also possible to match a
+ particular XML element, regardless of its nesting level, by using
+ the <em>*</em> wildcard character. For example, the pattern
+ <em>*/a</em> will match an <code><a></code> element at any
+ nesting position within the document. Other types of patterns, for
+ example <em>a/*</em>, are not currently supported by Joran.
+ </p>
+
+ <h2>SAX or DOM?</h2>
+
+ <p>Due to the event-based architecture of the SAX API, a tool based
+ on SAX cannot easily deal with forward references, that is,
+ references to elements which are defined later than the current
+ element being processed. Elements with cyclical references are
+ equally problematic. More generally, the DOM API allows the user to
+ perform searches on all the elements and make forward jumps.
+ </p>
+
+ <p>This extra flexibility initially led us to choose the DOM API as
+ the underlying parsing API for Joran. After some experimentation, it
+ quickly became clear that dealing with jumps to distant elements
+ while parsing the DOM tree did not make sense when the
+ interpretation rules were expressed in the form of patterns and
+ actions. <em>Joran only needs to be given the elements in the XML
+ document in a sequential, depth-first order.</em>
+ </p>
+
+ <p>Joran was first implemented in DOM. However, the author migrated
+ to SAX in order to benefit location information, available only with
+ the SAX API. Location information allows Joran to display the exact
+ line and column number where an error occured, which comes in quite
+ handy when hunting down problems.
+ </p>
+
+
+ <h2>Actions</h2>
+
+ <p>Actions extend the
+ <code>ch.qos.logback.core.joran.action.Action</code> class which
+ consists of the following abstract methods.
+ </p>
+
+
+<div class="source"><pre>package ch.qos.logback.core.joran.action;
+
+import org.xml.sax.Attributes;
+import ch.qos.logback.core.joran.spi.ExecutionContext;
+
+public abstract class Action {
+
+
+ /**
+ * Called when the parser first encounters an element.
+ */
+ public abstract void begin(ExecutionContext ec,
+ String name,
+ Attributes attributes);
+
+ /**
+ * Called when the parser encounters the element end. At
+ * this stage, we can assume that child elements, if any,
+ * have been processed.
+ */
+ public abstract void end(ExecutionContext ec, String name);
+}</pre></div>
+
+ <p>Thus, every action must implement the begin and end methods.</p>
+
+
+ <h2>Execution context</h2>
+
+ <p>To allow various actions to collaborate, the invocation of begin
+ and end methods include an execution context as the first
+ parameter. The execution context includes an object stack, an object
+ map, an error list and a reference to the Joran interpreter invoking
+ the action. Please see the
+ <code>ch.qos.logback.core.joran.spi.ExecutionContext</code> class
+ for the exact list of fields contained in the execution context.
+ </p>
+
+ <p>Actions can collaborate together by fetching, pushing or popping
+ objects from the common object stack, or by putting and fetching
+ keyed objects on the common object map. Actions can report any error
+ conditions by adding error items on the execution context's
+ <code>StatusManager</code>.
+ </p>
+
+ <a name="helloWorld"></a>
+ <h3>A hello world example</h3>
+
+ <p>The <em>logback-examples/src/main/java/chapter3/helloWorld/</em>
+ directory includes a trivial action and Joran interpreter setup
+ which just displays <em>Hello World</em> when a <hello-world>
+ element is encountered in an XML file. It also includes the basic
+ steps which are necessary to set up and invoke a Joran interpreter.
+ </p>
+
+ <p>The <em>hello.xml</em> file contains only one element, without
+ any other nested elements. The <a
+ href="../xref/chapter3/helloWorld/HelloWorldAction.html">
+ <code>HelloWorldAction</code></a> class is a trivial implementation:
+ it only prints "Hello World" in the console when it's
+ <code>begin()</code> method is called.
+ </p>
+
+ <p> <a
+ href="../xref/chapter3/helloWorld/HelloWorld.html"><code>HelloWorld</code></a>
+ is a class that sets up the Joran interpreter, with the minimal
+ steps necessary:
+ </p>
+
+ <ul>
+ <li>It creates a <code>RuleStore</code> and a <code>Context</code></li>
+ <li>It adds the <em>hello-world</em> pattern, with it's corresponding action</li>
+ <li>It creates a Joran interpreter, and passes the <code>RuleStore</code></li>
+ <li>It creates a SAX parser and parses the given file, specifying the newly created
+ Joran interpreter as the <code>ContentHandler</code></li>
+ </ul>
+
+ <p>It's last step is to print the content of the
+ <code>Context</code>. Since Joran uses logback's powerfull
+ <code>Status</code> objects for error reporting, one can have a good
+ feedback on what happened during the parsing.
+ </p>
+
+ <p>In this example, the parsing is rather simple. The
+ <em>hello-world</em> element will activate
+ <code>HelloWorldAction</code>'s <code>begin()</code> and
+ <code>end()</code> methods. In the first method, a simple call to
+ <code>System.out.println()</code> will be issued, displaying
+ <em>Hello World</em> in the console.
+ </p>
+
+ <a name="calculator"></a>
+ <h3>Collaborating actions</h3>
+
+ <p>The <em>logback-examples/src/main/java/joran/calculator/</em>
+ directory includes several actions which collaborate together
+ through the common object stack in order to accomplish simple
+ computations.
+ </p>
+
+ <p>The <em>calculator1.xml</em> file contains a <code>computation</code>
+ element, with a nested <code>literal</code> element.
+ </p>
+
+ <p>In the <a href="../xref/chapter3/calculator/Calculator1.html">
+ <code>Calculator1</code></a> class, we declare various patterns and
+ actions, that will collaborate and calculate a result based on the
+ xml file. The simple <em>calculator1.xml</em> file only creates a
+ computation and declares a literal value. The resulting parsing is
+ pretty simple:
+ </p>
+
+ <ul>
+ <li>The <a href="../xref/chapter3/calculator/ComputationAction1.html">
+ <code>ComputationAction1</code></a> class' <code>begin()</code> method
+ is called</li>
+ <li>The <a href="../xref/chapter3/calculator/LiteralAction.html">
+ <code>LiteralAction</code></a> class' <code>begin()</code> and <code>end()</code>
+ methods are called</li>
+ <li>The <a href="../xref/chapter3/calculator/ComputationAction1.html">
+ <code>ComputationAction1</code></a> class' <code>end()</code> method
+ is called</li>
+ </ul>
+ <p>What is interesting here is the way that the Actions collaborate.
+ The <code>LiteralAction</code> reads a literal value and pushes it
+ in the object stack maintained by the
+ <code>ExecutionContext</code>. Once done, any other action can pop
+ the value to read or modify it. Here, the <code>end()</code> method
+ of the <code>ComputationAction1</code> class pops the value from the
+ stack and prints it.
+ </p>
+
+ <p>The <em>calculator2.xml</em> file is a bit more complex, but much
+ more interesting.</p>
+
+ <p>It contains the following elements:</p>
+
+ <em>Example 3.<span class="autoEx"/>: Calculator configuration file (logback-examples/src/main/java/chapter3/calculator/calculator2.xml)</em>
+ <div class="source"><pre><computation name="toto">
+ <literal value="7"/>
+ <literal value="3"/>
+ <add/>
+ <literal value="3"/>
+ <multiply/>
+</computation></pre></div>
+
+ <p>Here, there are obviously more actions that will be part of the
+ computation.
+ </p>
+
+ <p>When called, the <a
+ href="../xref/chapter3/calculator/AddAction.html">
+ <code>AddAction</code></a> class will remove the two integers at the
+ bottom of the stack, add them and push the resulting integer at the
+ top of the stack, for further use.</p>
+
+ <p>Later in the computation, the <a
+ href="../xref/chapter3/calculator/MultiplyAction.html">
+ <code>MultiplyAction</code></a> class will be called. It will take
+ the last two integers from the stack, multiply them and push the
+ result in the stack.</p>
+
+ <p>We have here two examples of action whose <code>begin()</code>
+ method behaves in a certain, predictable way, but whose
+ <code>end()</code> methods are empty.</p>
+
+ <p>Finally, a <em>calculator3.xml</em> is also provided, to
+ demonstrate the possibility elements that contain instances of the
+ same element. Here's the content of <em>calculator3.xml</em>:</p>
+
+ <em>Example 3.<span class="autoEx"/>: Calculator configuration file
+ (logback-examples/src/main/java/chapter3/calculator/calculator3.xml)</em>
+
+<div class="source"><pre><computation name="toto">
+ <computation>
+ <literal value="7"/>
+ <literal value="3"/>
+ <add/>
+ </computation>
+
+ <literal value="3"/>
+ <multiply/>
+</computation></pre></div>
+
+ <p>Much like the use of parentheses in an algebrical equation, the
+ presence of a <code>computation</code> element nested in another is
+ managed by the <a
+ href="../xref/chapter3/calculator/ComputationAction2.html">
+ <code>ComputationAction2</code></a> class using an internal
+ stack. The well-formedness of XML will guarantee that a value saved
+ by one <code>begin()</code> will be consumed only by the matching
+ <code>end()</code> method.</p>
+
+ <a name="newRule"></a>
+
+ <h3>New-rule action</h3>
+
+ <p>Joran includes an action which allows the Joran interpreter to
+ lean new rules on the fly while interpreting the XML file containing
+ the new rules. See the
+ <em>logback-examples/src/main/java/joran/newRule/</em> directory for
+ sample code.
+ </p>
+
+ <p>In this package, the <a
+ href="../xref/chapter3/newRule/NewRuleCalculator.html">
+ <code>NewRuleCalculator</code></a> class contains the same setup as
+ we have seen so far, but for one line:</p>
+
+ <p class="source">ruleStore.addRule(new
+ Pattern("/computation/new-rule"), new NewRuleAction());</p>
+
+ <p>By adding this line, we ask Joran to allow new rules to be learnt
+ at parsing time. It works pretty much like the other rules: it has a
+ <code>begin()</code> and <code>end()</code> method, and is called each time
+ the parser finds a <em>new-rule</em> element.</p>
+
+ <p>When called, the <code>begin()</code> method looks for a
+ <em>pattern</em> and a <em>actionClass</em> attribute. The action
+ class is then instanciated and added to the <code>RuleStore</code>,
+ along with its corresponding pattern.</p>
+
+ <p>Here is how new rules can be declared in an xml file:</p>
+
+<div class="source"><pre><new-rule pattern="*/computation/literal"
+actionClass="chapter3.calculator.LiteralAction"/></pre></div>
+
+ <p>Using new rule declarations, the preceding example, involving the
+ calculation, could be expressed this way:</p>
+
+ <em>Example 3.<span class="autoEx"/>: Configuration file using new
+ rules on the fly
+ (logback-examples/src/main/java/chapter3/newrule/new-rule.xml)</em>
+
+<div class="source"><pre><computation name="toto">
+ <new-rule pattern="*/computation/literal"
+ actionClass="chapter3.calculator.LiteralAction"/>
+ <new-rule pattern="*/computation/add"
+ actionClass="chapter3.calculator.AddAction"/>
+ <new-rule pattern="*/computation/multiply"
+ actionClass="chapter3.calculator.MultiplyAction"/>
+
+ <computation>
+ <literal value="7"/>
+ <literal value="3"/>
+ <add/>
+ </computation>
+
+ <literal value="3"/>
+ <multiply/>
+</computation></pre></div>
+
+ <a name="implicit"></a>
+ <h3>Implicit actions </h3>
+
+ <p>The rules defined thus far are called explicit rules because they
+ require an explicit pattern, hence fixing the tag name of the
+ elements for which they apply.
+ </p>
+
+ <p>In highly extensible systems, the number and type of components
+ to handle are innumerable so that it would become very tedious or
+ even impossible to list all the applicable patterns by name.
+ </p>
+
+ <p>At the same time, even in highly extensible systems one can
+ observe well-defined patterns linking the various parts
+ together. Implicit rules come in very handy when processing
+ components composed of sub-components unknown ahead of time. For
+ example, Apache Ant is capable of handling tasks which contain tags
+ unknown at compile time by looking at methods whose names start with
+ <em>add</em>, as in <code>addFile</code>, or
+ <code>addClassPath</code>. When Ant encounters an embedded tag
+ within a task, it simply instantiates an object that matches the
+ signature of the task class' add method and attaches the resulting
+ object to the parent.
+ </p>
+
+ <p>Joran includes similar capability in the form of implicit
+ actions. Joran keeps a list of implicit actions which can be applied
+ if no explicit pattern matches the current XML element. However,
+ applying an implicit action may not be always appropriate. Before
+ executing the implicit action, Joran asks an implicit action whether
+ it is appropriate in the current context. Only if the action replies
+ affirmatively does Joran interpreter invoke the (implicit)
+ action. This extra step makes it possible to support multiple
+ implicit actions or obviously none, if no implicit action is
+ appropriate for a given situation.
+ </p>
+
+ <p>For example, the <a
+ href="../xref/ch/qos/logback/core/joran/action/NestedComponentIA.html">
+ <code>NestedComponentIA</code></a> extending <a
+ href="../xref/ch/qos/logback/core/joran/action/ImplicitAction.html">
+ <code>ImplicitAction</code></a> , will instantiate the class
+ specified in a nested component and attach it to the parent
+ component by using setter method of the parent component and the
+ nested element's name. Under certain circumstances, a nested action
+ needs to be applied to an element say <a> and also to another
+ element <b> nested within <a>. The current implementation of
+ <code>NestedComponentIA</code> is capable of handling multiply
+ nested elements requiring intervention by the same implicit action.
+ </p>
+
+ <p>Both <code>ImplicitAction</code> and
+ <code>NestedComponentIA</code> are located in the
+ <code>ch.qos.logback.core.joran.action</code> package.
+ </p>
+
+ <p>Refer to the
+ <em>logback-examples/src/main/java/joran/implicit</em> directory for
+ an example of an implicit action.
+ </p>
+
+ <p>In that directory, you will find two actions classes, one xml
+ file and one class containing the setup of Joran.</p>
+
+ <p>The <a href="../xref/chapter3/implicit/NOPAction.html">
+ <code>NOPAction</code></a> class does nothing. It is used to set the
+ context of the <em>foo</em> element, using this line:</p>
+
+ <p class="source">ruleStore.addRule(new Pattern("*/foo"), new NOPAction());</p>
+
+ <p>After that, the implicit action, namely <a
+ href="../xref/chapter3/implicit/PrintMeImplicitAction.html">
+ <code>PrintMeImplicitAction</code></a>, is added to the
+ <code>RuleStore</code>. This is done by simply adding a new instance
+ of the action to the <code>Joran interpreter</code></p>
+
+ <p class="source">ji.addImplicitAction(new
+ PrintMeImplicitAction());</p>
+
+ <p>When called, the <code>isApplicable()</code> method of
+ <code>PrintMeImplicitAction</code> checks the value of the
+ <em>printme</em> attribute. If the value is <code>true</code>, the
+ implicit action is applicable: its <code>begin()</code> method will
+ be called.</p>
+
+ <p>The <em>implicit1.xml</em> file contains the following lines:</p>
+
+ <em>Example 3.<span class="autoEx"/>: Usage of implicit rules
+ (logback-examples/src/main/java/chapter3/implicit/implicit1.xml)</em>
+<div class="source"><pre><foo>
+
+ <xyz printme="true">
+ <abc printme="true"/>
+ </xyz>
+
+ <xyz/>
+
+ <foo printme="true"/>
+
+</foo></pre></div>
+
+ <p>As you can see, the first element will be printed, since it has a
+ <em>printme</em> attribute, which bears the value
+ <code>true</code>.</p>
+
+ <p>The second element will not be printed, because no <em>printme</em> attibute is present.</p>
+
+ <p>The last element will not be printed, although the required
+ attribute is present. This is because implicit rules are called
+ only if no explicit rules are defined. Since we added a
+ <code>NOPAction</code> with the <em>*/foo</em> pattern, it will be
+ used instead of the <code>PrintMeImplicitAction</code>.</p>
+
+ <p>Running the example yields the following output:</p>
+
+<div class="source"><pre>Element <xyz> asked to be printed.
+Element <abc> asked to be printed.
+ERROR in ch.qos.logback.core.joran.spi.ExecutionContext at 1c5c1 - no applicable action \
+for <xyz>, current pattern is [/foo/xyz]</pre></div>
+
+ <p>The last line was printed because of a call to
+ <code>StatusPrinter</code> at the end of the main class.</p>
+
+ <h3>Non goals</h3>
+
+ <p>The Joran API is not intended to be used to parse documents with
+ thousands of elements.
+ </p>
+
+
+<script src="templates/footer.js"></script>
+</div>
+</body>
+</html>
Modified: logback/trunk/logback-site/src/site/pages/manual/joran.html
==============================================================================
--- logback/trunk/logback-site/src/site/pages/manual/joran.html (original)
+++ logback/trunk/logback-site/src/site/pages/manual/joran.html Sun Aug 24 00:25:55 2008
@@ -5,7 +5,7 @@
<title>Chapter3: Logback configuration & Joran</title>
<link rel="stylesheet" type="text/css" media="screen" href="../css/site.css" />
<link rel="stylesheet" type="text/css" media="print" href="../css/print.css" />
-<link type="text/css" rel="stylesheet" href="../ghighlighter/Styles/SyntaxHighlighter.css"></link>
+
</head>
<body>
@@ -881,24 +881,22 @@
<h4>Variable substitution</h4>
- <p>All option <em>values</em> admit variable substitution. The
- syntax of variable substitution is similar to that of Unix
- shells. The string between an opening <em>${</em> and closing
- <em>}</em> is interpreted as a key. The value of the substituted
- variable can be defined as a system property in the configuration
- file itself or in a separate file linked to the configuration
- file. The value of the key is first searched in configuration file
- or linked properties file, and if not found there, it is then
- searched in system properties. The corresponding value replaces
- <em>${aKey}</em> sequence. For example, if <em>java.home.dir</em>
- system property is set to <em>/home/xyz</em>, then every occurrence
- of the sequence <em>${java.home.dir}</em> will be interpreted as
- <em>/home/xyz</em>.
- </p>
-
- <p>The first example shows a declared property at the beginning of
- the configuration file. It is then used further down the file to
- specify the place to create the output file.
+ <p>In principle, variable substitution can occur at any point where
+ a value can be specified. The syntax of variable substitution is
+ similar to that of Unix shells. The string between an opening
+ <em>${</em> and closing <em>}</em> is interpreted as a key. The
+ value of the substituted variable can be defined in the
+ configuration file itself, in an external properties file or as a
+ system property. The corresponding value replaces <em>${aKey}</em>
+ sequence. For example, if <em>java.home.dir</em> system property is
+ set to <em>/home/xyz</em>, then every occurrence of the sequence
+ <em>${java.home.dir}</em> will be interpreted as <em>/home/xyz</em>.
+ </p>
+
+ <p>The next example shows a variable, a.k.a. a substitution
+ property, declared the beginning of the configuration file. It is
+ then used further down the file to specify the location of the
+ output file.
</p>
<em>Example 3.<span class="autoEx"/>: Simple Variable substitution
@@ -907,21 +905,13 @@
<div class="source"><pre><configuration>
- <b><substitutionProperty name="user.home.dir" value="/Users/seb" /></b>
+ <b><substitutionProperty name="USER_HOME" value="/home/sebastien" /></b>
<appender name="FILE"
- class="ch.qos.logback.core.rolling.RollingFileAppender">
- <b><file>${user.home.dir}/myApp.log</file></b>
- <rollingPolicy
- class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
- <FileNamePattern>
- myApp-%d{yyyy-MM-dd-HH}.log
- </FileNamePattern>
- </rollingPolicy>
+ class="ch.qos.logback.core.FileAppender">
+ <b><file>${USER_HOME}/myApp.log</file></b>
<layout class="ch.qos.logback.classic.PatternLayout">
- <Pattern>
- %date %level [%thread] %logger{10} [%file : %line] %msg%n
- </Pattern>
+ <Pattern>%msg%n</Pattern>
</layout>
</appender>
@@ -932,9 +922,13 @@
</configuration></pre></div>
- <p>The next example shows the use of a System property to achieve the same result. The
- property is not declared anywhere, thus logback will look for it in the System properties.
+ <p>The next example shows the use of a System property to achieve
+ the same result. The property is not declared in the configuration
+ file, thus logback will look for it in the System properties. Java
+ system properties can be set on the command line.
</p>
+
+ <p class="source">java -DUSER_HOME="/home/sebastien" MyApp2</p>
<em>Example 3.<span class="autoEx"/>: System Variable substitution
(logback-examples/src/main/java/chapter3/variableSubstitution2.xml)
@@ -943,18 +937,10 @@
<div class="source"><pre><configuration>
<appender name="FILE"
- class="ch.qos.logback.core.rolling.RollingFileAppender">
- <b><file>${user.home.dir}/myApp.log</file></b>
- <rollingPolicy
- class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
- <FileNamePattern>
- myApp-%d{yyyy-MM-dd-HH}.log
- </FileNamePattern>
- </rollingPolicy>
+ class="ch.qos.logback.core.FileAppender">
+ <b><file>${USER_HOME}/myApp.log</file></b>
<layout class="ch.qos.logback.classic.PatternLayout">
- <Pattern>
- %date %level [%thread] %logger{10} [%file : %line] %msg%n
- </Pattern>
+ <Pattern>%msg%n</Pattern>
</layout>
</appender>
@@ -964,7 +950,8 @@
</root>
</configuration></pre></div>
- <p>When many variables are used, it is often more convenient to
+
+ <p>When multiple variables are needed, it may be more convenient to
create a separate file that will contain all the variables. Here is
how one can do such a setup.
</p>
@@ -978,19 +965,11 @@
<b><substitutionProperty file="variables1.properties" /></b>
- <appender name="FILE"
- class="ch.qos.logback.core.rolling.RollingFileAppender">
- <b><file>${user.home.dir}/myApp.log</file></b>
- <rollingPolicy
- class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
- <FileNamePattern>
- myApp-%d{yyyy-MM-dd-HH}.log
- </FileNamePattern>
- </rollingPolicy>
+ <appender name="FILE"
+ class="ch.qos.logback.core.FileAppender">
+ <b><file>${USER_HOME}/myApp.log</file></b>
<layout class="ch.qos.logback.classic.PatternLayout">
- <Pattern>
- %date %level [%thread] %logger{10} [%file : %line] %msg%n
- </Pattern>
+ <Pattern>%msg%n</Pattern>
</layout>
</appender>
@@ -1000,56 +979,53 @@
</root>
</configuration></pre></div>
-<p>
-This configuration file contains a link to another file called <em>variables1.properties</em>.
-The variables contained in that other file will be read and will thus be available to the
-logback configuration file. Here is what the <em>variable.properties</em> file looks like.
-</p>
-
-<em>Example 3.<span class="autoEx"/>: Variable file (logback-examples/src/main/java/chapter3/variables1.properties)</em>
-<div class="source"><pre>user.home.dir=/Users/seb</pre></div>
-
-<p>
-Nothing more is needed to declare the variable.
-</p>
-
-<p>
-Recursive subsitution is also available. If the user wants to use variables to
-specify not only the destination directory but also the file name, here is what she
-would write in her <em>variables1.properties</em> file.
-</p>
-
-
-<em>Example 3.<span class="autoEx"/>: Recursive use of variables
-(logback-examples/src/main/java/chapter3/variables2.properties)</em>
-<div class="source"><pre>user.home.dir=/Users/seb
+ <p>This configuration file contains a reference to a file named
+ <em>variables1.properties</em>. The variables contained in that
+ file will be read defined within the context of the logback
+ configuration file. Here is what the <em>variable.properties</em>
+ file might look like.
+ </p>
+
+ <em>Example 3.<span class="autoEx"/>: Variable file
+ (logback-examples/src/main/java/chapter3/variables1.properties)</em>
+
+ <div class="source"><pre>USER_HOME=/home/sebastien</pre></div>
+
+ <p>Nested variabled subsitution is also supported. By nested, we
+ mean that the value definition of a variable contains referenced to
+ other variables. Here is an example. Suppose you wish to use
+ variables to specify not only the destination directory but also
+ the file name, and combine those variable in a third variable
+ called "destination". The properties file shown below gives an
+ example.
+ </p>
+
+
+ <em>Example 3.<span class="autoEx"/>: Recursive use of variables
+ (logback-examples/src/main/java/chapter3/variables2.properties)</em>
+
+ <div class="source"><pre>USER_HOME=/home/sebastien
fileName=myApp.log
-destination=${user.home.dir}/${fileName}</pre></div>
+<b>destination=${USER_HOME}/${fileName}</b></pre></div>
- <p>In the configuration file, only the last variable,
- <em>${destination}</em> will be used, as shown below:
+ <p>Note that in the properties file above, "destination" is
+ composed out of two other variables, namely "USER_HOME" and
+ "fileName".
</p>
-
- <em>Example 3.<span class="autoEx"/>: Variable substitution using a separate file
+
+ <em>Example 3.<span class="autoEx"/>: Variable substitution using
+ a separate file
(logback-examples/src/main/java/chapter3/variableSubstitution4.xml)</em>
<div class="source"><pre><configuration>
- <substitutionProperty file="variables1.properties" />
+ <substitutionProperty file="variables2.properties" />
<appender name="FILE"
- class="ch.qos.logback.core.rolling.RollingFileAppender">
+ class="ch.qos.logback.core.FileAppender">
<b><file>${destination}</file></b>
- <rollingPolicy
- class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
- <FileNamePattern>
- myApp-%d{yyyy-MM-dd-HH}.log
- </FileNamePattern>
- </rollingPolicy>
<layout class="ch.qos.logback.classic.PatternLayout">
- <Pattern>
- %date %level [%thread] %logger{10} [%file : %line] %msg%n
- </Pattern>
+ <Pattern>%msg%n</Pattern>
</layout>
</appender>
@@ -1059,546 +1035,62 @@
</root>
</configuration></pre></div>
+ <h3>Default substitution values for variables</h3>
+
+ <p>Under certain circumstances, it may be desirable for a variable
+ to have a default value in case it is not declared or its value is
+ null. As in the <a
+ href="http://tldp.org/LDP/abs/html/parameter-substitution.html">Bash
+ shell</a>, default values can be specified using the <b>":-"</b>
+ operator. For example, assuming <em>aKey</em> is not defined,
+ <code>"${aKey<b>:-golden</b>}"</code> will be interpreted as
+ "golden".</p>
+
+
<a name="Include"></a>
<h3>File inclusion</h3>
- <p> It is possible to include configuration elements into a
- <em>logback.xml</em> configuration file. This is done by using a
- <em>include</em> element, as shown in the example below:
+ <p>Joran supports including parts of a configuration file from
+ another file. This is done by declaring a <code><include></code>
+ element, as shown below:
</p>
<em>Example 3.<span class="autoEx"/>: File include
- (logback-examples/src/main/java/chapter3/redirectConfig.xml)</em>
+ (logback-examples/src/main/java/chapter3/containingConfig.xml)</em>
<div class="source"><pre><configuration>
- <b><include file="path/to/configuration/file"/></b>
- </configuration></pre></div>
+ <b><include file="src/main/java/chapter3/includedConfig.xml"/></b>
- <p>The target file must have its elements nested inside an
- <em>included</em> element. For example, a
- <code>ConsoleAppender</code> would be declared this way:
+ <root level="DEBUG">
+ <appender-ref ref="includedConsole" />
+ </root>
+
+</configuration></pre></div>
+
+ <p>The target file MUST have its elements nested inside an
+ <code><included></code> element. For example, a
+ <code>ConsoleAppender</code> could be declared as:
</p>
<em>Example 3.<span class="autoEx"/>: File include
(logback-examples/src/main/java/chapter3/includedConfig.xml)</em>
- <div class="source"><pre><included>
-
- <appender name="redirectConsole"
- class="ch.qos.logback.core.ConsoleAppender">
+ <div class="source"><pre><b class="green"><included></b>
+ <appender name="includedConsole" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
- <param name="Pattern" value="%d - %m%n" />
+ <Pattern>"%d - %m%n"</Pattern>
</layout>
</appender>
+<b class="green"></included></b></pre></div>
- <root>
- <level value="DEBUG" />
- <appender-ref ref="redirectConsole" />
- </root>
-
-</included></pre></div>
-
- <p>The file to be included can be referenced as a URL or as a
- resource. To reference a URL use the <code>url</code> attribute
- instead of the file attribute in the previous example. To reference
- a resource, use the <code>resource</code> attribute instead of the
- file attribute in the previous example.
- </p>
-
-
- <a name="Joran"></a>
- <h2>Using Joran in your own application</h2>
-
- <p>As we've seen, logback relies on Joran, a mature, flexible and
- powerful configuration framework. Many of the capabilities offered
- by logback modules are possible with the help of Joran.
- </p>
-
- <p>Joran is actually a generic configuration system which can be
- used independently of logging. To emphaises this point, we should
- mention that the logback-core module does not have a notion of
- loggers. In that spirit, many of the examples related to this
- tutorial, have nothing to do with loggers, appenders or layouts.
- </p>
-
- <p>The examples for this chapter can be found under
- <em>LOGBACK_HOME/logback-examples/src/main/java/chapter3</em>.
- </p>
-
- <p>To install joran, simply <a href="../download.html">download</a>
- logback and add <em>logback-core-VERSION.jar</em> to your classpath.</p>
-
- <h2>Historical perspective</h2>
-
- <p>One of the most powerful features of the Java language is
- reflection. Reflection makes it possible to configure software systems
- declaratively. For example, many important properties of an EJB are
- configured with the <em>ejb.xml</em> file. While EJBs are written in Java, many
- of their properties are specified within the <em>ejb.xml</em> file. Similarly,
- logback settings can be specified in a configuration file, expressed
- in XML format.
- </p>
-
- <p>In log4j, logback's predecessor, <code>DOMConfigurator</code>
- that shipped with log4j version 1.2.x can parse configuration files
- written in XML. The <code>DOMConfigurator</code> was written in a
- way that forced to tweak it each time the structure of the
- configuration file changed. The modified code had to be recompiled
- and redeployed. Just as importantly, the code of the DOMConfigurator
- consists of loops dealing with children elements containing many
- interspersed if/else statements. One could not help but notice that
- that particular code reeked of redundancy. The <a
- href="http://jakarta.apache.org/commons/digester/">digester
- project</a> has shown that it is possible to parse XML files using
- pattern matching rules. At parse time, digester will apply the rules
- that match previously stated patterns. Rule classes are usually
- quite small and specialized. Consequently, they are relatively easy
- to understand and to maintain.
- </p>
-
- <p>Joran is heavily inspired by the commons-digester project but
- uses a slightly different terminology. In commons-digester, a rule
- can be seen as consisting of a pattern and a rule, as shown by the
- <code>Digester.addRule(String pattern, Rule rule)</code> method. We
- find it unnecessarily confusing to have a rule to consist of itself,
- not recursively but with a different meaning. In Joran, a rule
- consists of a pattern and an action. An action is invoked when a
- match occurs for the corresponding pattern. This relation between
- patterns and actions lies at the core of Joran. Quite remarkably,
- one can deal with quite complex requirements by using simple
- patterns, or more precisely with exact matches and wildcard
- matches. For example, the pattern <em>a/b</em> will match a
- <code><b></code> element nested within an <code><a></code>
- element but not a <code><c></code> element, even if nested within
- a <code><b></code> element. It is also possible to match a
- particular XML element, regardless of its nesting level, by using
- the <em>*</em> wildcard character. For example, the pattern
- <em>*/a</em> will match an <code><a></code> element at any
- nesting position within the document. Other types of patterns, for
- example <em>a/*</em>, are not currently supported by Joran.
- </p>
-
- <h2>SAX or DOM?</h2>
-
- <p>Due to the event-based architecture of the SAX API, a tool based
- on SAX cannot easily deal with forward references, that is,
- references to elements which are defined later than the current
- element being processed. Elements with cyclical references are
- equally problematic. More generally, the DOM API allows the user to
- perform searches on all the elements and make forward jumps.
- </p>
-
- <p>This extra flexibility initially led us to choose the DOM API as
- the underlying parsing API for Joran. After some experimentation, it
- quickly became clear that dealing with jumps to distant elements
- while parsing the DOM tree did not make sense when the
- interpretation rules were expressed in the form of patterns and
- actions. <em>Joran only needs to be given the elements in the XML
- document in a sequential, depth-first order.</em>
- </p>
-
- <p>Joran was first implemented in DOM. However, the author migrated
- to SAX in order to benefit form the location information provided to
- the user, that is, to an
- <code>org.w3.sax.ContentHandler</code>. With the help of location
- information, it becomes possible to display essential error reports
- to the user which include exact line and column. This extra
- information turns out to be handy in hunting down problems.
- </p>
-
-
- <h2>Actions</h2>
-
- <p>Actions extend the
- <code>ch.qos.logback.core.joran.action.Action</code> class which
- consists of the following abstract methods.
+ <p>The file to be included can be referenced as a file, as a URL or
+ as a resource. To reference a file use the <span
+ class="attr">file</span> attribute. To reference a URL use the <span
+ class="attr">url</span> attribute. To reference a resource, use the
+ <span class="attr">resource</span> attribute.
</p>
-<div class="source"><pre>package ch.qos.logback.core.joran.action;
-
-import org.xml.sax.Attributes;
-import ch.qos.logback.core.joran.spi.ExecutionContext;
-
-public abstract class Action {
-
-
- /**
- * Called when the parser first encounters an element.
- */
- public abstract void begin(ExecutionContext ec,
- String name,
- Attributes attributes);
-
- /**
- * Called when the parser encounters the element end. At
- * this stage, we can assume that child elements, if any,
- * have been processed.
- */
- public abstract void end(ExecutionContext ec, String name);
-}</pre></div>
-
- <p>Thus, every action must implement the begin and end methods.</p>
-
-
- <h2>Execution context</h2>
-
- <p>To allow various actions to collaborate, the invocation of begin
- and end methods include an execution context as the first
- parameter. The execution context includes an object stack, an object
- map, an error list and a reference to the Joran interpreter invoking
- the action. Please see the
- <code>ch.qos.logback.core.joran.spi.ExecutionContext</code> class
- for the exact list of fields contained in the execution context.
- </p>
-
- <p>Actions can collaborate together by fetching, pushing or popping
- objects from the common object stack, or by putting and fetching
- keyed objects on the common object map. Actions can report any error
- conditions by adding error items on the execution context's
- <code>StatusManager</code>.
- </p>
-
- <a name="helloWorld"></a>
- <h3>A hello world example</h3>
-
- <p>The <em>logback-examples/src/main/java/chapter3/helloWorld/</em>
- directory includes a trivial action and Joran interpreter setup
- which just displays <em>Hello World</em> when a <hello-world>
- element is encountered in an XML file. It also includes the basic
- steps which are necessary to set up and invoke a Joran interpreter.
- </p>
-
- <p>The <em>hello.xml</em> file contains only one element, without
- any other nested elements. The <a
- href="../xref/chapter3/helloWorld/HelloWorldAction.html">
- <code>HelloWorldAction</code></a> class is a trivial implementation:
- it only prints "Hello World" in the console when it's
- <code>begin()</code> method is called.
- </p>
-
- <p> <a
- href="../xref/chapter3/helloWorld/HelloWorld.html"><code>HelloWorld</code></a>
- is a class that sets up the Joran interpreter, with the minimal
- steps necessary:
- </p>
-
- <ul>
- <li>It creates a <code>RuleStore</code> and a <code>Context</code></li>
- <li>It adds the <em>hello-world</em> pattern, with it's corresponding action</li>
- <li>It creates a Joran interpreter, and passes the <code>RuleStore</code></li>
- <li>It creates a SAX parser and parses the given file, specifying the newly created
- Joran interpreter as the <code>ContentHandler</code></li>
- </ul>
-
- <p>It's last step is to print the content of the
- <code>Context</code>. Since Joran uses logback's powerfull
- <code>Status</code> objects for error reporting, one can have a good
- feedback on what happened during the parsing.
- </p>
-
- <p>In this example, the parsing is rather simple. The
- <em>hello-world</em> element will activate
- <code>HelloWorldAction</code>'s <code>begin()</code> and
- <code>end()</code> methods. In the first method, a simple call to
- <code>System.out.println()</code> will be issued, displaying
- <em>Hello World</em> in the console.
- </p>
-
- <a name="calculator"></a>
- <h3>Collaborating actions</h3>
-
- <p>The <em>logback-examples/src/main/java/joran/calculator/</em>
- directory includes several actions which collaborate together
- through the common object stack in order to accomplish simple
- computations.
- </p>
-
- <p>The <em>calculator1.xml</em> file contains a <code>computation</code>
- element, with a nested <code>literal</code> element.
- </p>
-
- <p>In the <a href="../xref/chapter3/calculator/Calculator1.html">
- <code>Calculator1</code></a> class, we declare various patterns and
- actions, that will collaborate and calculate a result based on the
- xml file. The simple <em>calculator1.xml</em> file only creates a
- computation and declares a literal value. The resulting parsing is
- pretty simple:
- </p>
-
- <ul>
- <li>The <a href="../xref/chapter3/calculator/ComputationAction1.html">
- <code>ComputationAction1</code></a> class' <code>begin()</code> method
- is called</li>
- <li>The <a href="../xref/chapter3/calculator/LiteralAction.html">
- <code>LiteralAction</code></a> class' <code>begin()</code> and <code>end()</code>
- methods are called</li>
- <li>The <a href="../xref/chapter3/calculator/ComputationAction1.html">
- <code>ComputationAction1</code></a> class' <code>end()</code> method
- is called</li>
- </ul>
- <p>What is interesting here is the way that the Actions collaborate.
- The <code>LiteralAction</code> reads a literal value and pushes it
- in the object stack maintained by the
- <code>ExecutionContext</code>. Once done, any other action can pop
- the value to read or modify it. Here, the <code>end()</code> method
- of the <code>ComputationAction1</code> class pops the value from the
- stack and prints it.
- </p>
-
- <p>The <em>calculator2.xml</em> file is a bit more complex, but much
- more interesting.</p>
-
- <p>It contains the following elements:</p>
-
- <em>Example 3.<span class="autoEx"/>: Calculator configuration file (logback-examples/src/main/java/chapter3/calculator/calculator2.xml)</em>
- <div class="source"><pre><computation name="toto">
- <literal value="7"/>
- <literal value="3"/>
- <add/>
- <literal value="3"/>
- <multiply/>
-</computation></pre></div>
-
- <p>Here, there are obviously more actions that will be part of the
- computation.
- </p>
-
- <p>When called, the <a
- href="../xref/chapter3/calculator/AddAction.html">
- <code>AddAction</code></a> class will remove the two integers at the
- bottom of the stack, add them and push the resulting integer at the
- top of the stack, for further use.</p>
-
- <p>Later in the computation, the <a
- href="../xref/chapter3/calculator/MultiplyAction.html">
- <code>MultiplyAction</code></a> class will be called. It will take
- the last two integers from the stack, multiply them and push the
- result in the stack.</p>
-
- <p>We have here two examples of action whose <code>begin()</code>
- method behaves in a certain, predictable way, but whose
- <code>end()</code> methods are empty.</p>
-
- <p>Finally, a <em>calculator3.xml</em> is also provided, to
- demonstrate the possibility elements that contain instances of the
- same element. Here's the content of <em>calculator3.xml</em>:</p>
-
- <em>Example 3.<span class="autoEx"/>: Calculator configuration file
- (logback-examples/src/main/java/chapter3/calculator/calculator3.xml)</em>
-
-<div class="source"><pre><computation name="toto">
- <computation>
- <literal value="7"/>
- <literal value="3"/>
- <add/>
- </computation>
-
- <literal value="3"/>
- <multiply/>
-</computation></pre></div>
-
- <p>Much like the use of parentheses in an algebrical equation, the
- presence of a <code>computation</code> element nested in another is
- managed by the <a
- href="../xref/chapter3/calculator/ComputationAction2.html">
- <code>ComputationAction2</code></a> class using an internal
- stack. The well-formedness of XML will guarantee that a value saved
- by one <code>begin()</code> will be consumed only by the matching
- <code>end()</code> method.</p>
-
- <a name="newRule"></a>
-
- <h3>New-rule action</h3>
-
- <p>Joran includes an action which allows the Joran interpreter to
- lean new rules on the fly while interpreting the XML file containing
- the new rules. See the
- <em>logback-examples/src/main/java/joran/newRule/</em> directory for
- sample code.
- </p>
-
- <p>In this package, the <a
- href="../xref/chapter3/newRule/NewRuleCalculator.html">
- <code>NewRuleCalculator</code></a> class contains the same setup as
- we have seen so far, but for one line:</p>
-
- <p class="source">ruleStore.addRule(new
- Pattern("/computation/new-rule"), new NewRuleAction());</p>
-
- <p>By adding this line, we ask Joran to allow new rules to be learnt
- at parsing time. It works pretty much like the other rules: it has a
- <code>begin()</code> and <code>end()</code> method, and is called each time
- the parser finds a <em>new-rule</em> element.</p>
-
- <p>When called, the <code>begin()</code> method looks for a
- <em>pattern</em> and a <em>actionClass</em> attribute. The action
- class is then instanciated and added to the <code>RuleStore</code>,
- along with its corresponding pattern.</p>
-
- <p>Here is how new rules can be declared in an xml file:</p>
-
-<div class="source"><pre><new-rule pattern="*/computation/literal"
-actionClass="chapter3.calculator.LiteralAction"/></pre></div>
-
- <p>Using new rule declarations, the preceding example, involving the
- calculation, could be expressed this way:</p>
-
- <em>Example 3.<span class="autoEx"/>: Configuration file using new
- rules on the fly
- (logback-examples/src/main/java/chapter3/newrule/new-rule.xml)</em>
-
-<div class="source"><pre><computation name="toto">
- <new-rule pattern="*/computation/literal"
- actionClass="chapter3.calculator.LiteralAction"/>
- <new-rule pattern="*/computation/add"
- actionClass="chapter3.calculator.AddAction"/>
- <new-rule pattern="*/computation/multiply"
- actionClass="chapter3.calculator.MultiplyAction"/>
-
- <computation>
- <literal value="7"/>
- <literal value="3"/>
- <add/>
- </computation>
-
- <literal value="3"/>
- <multiply/>
-</computation></pre></div>
-
- <a name="implicit"></a>
- <h3>Implicit actions </h3>
-
- <p>The rules defined thus far are called explicit rules because they
- require an explicit pattern, hence fixing the tag name of the
- elements for which they apply.
- </p>
-
- <p>In highly extensible systems, the number and type of components
- to handle are innumerable so that it would become very tedious or
- even impossible to list all the applicable patterns by name.
- </p>
-
- <p>At the same time, even in highly extensible systems one can
- observe well-defined patterns linking the various parts
- together. Implicit rules come in very handy when processing
- components composed of sub-components unknown ahead of time. For
- example, Apache Ant is capable of handling tasks which contain tags
- unknown at compile time by looking at methods whose names start with
- <em>add</em>, as in <code>addFile</code>, or
- <code>addClassPath</code>. When Ant encounters an embedded tag
- within a task, it simply instantiates an object that matches the
- signature of the task class' add method and attaches the resulting
- object to the parent.
- </p>
-
- <p>Joran includes similar capability in the form of implicit
- actions. Joran keeps a list of implicit actions which can be applied
- if no explicit pattern matches the current XML element. However,
- applying an implicit action may not be always appropriate. Before
- executing the implicit action, Joran asks an implicit action whether
- it is appropriate in the current context. Only if the action replies
- affirmatively does Joran interpreter invoke the (implicit)
- action. This extra step makes it possible to support multiple
- implicit actions or obviously none, if no implicit action is
- appropriate for a given situation.
- </p>
-
- <p>For example, the <a
- href="../xref/ch/qos/logback/core/joran/action/NestedComponentIA.html">
- <code>NestedComponentIA</code></a> extending <a
- href="../xref/ch/qos/logback/core/joran/action/ImplicitAction.html">
- <code>ImplicitAction</code></a> , will instantiate the class
- specified in a nested component and attach it to the parent
- component by using setter method of the parent component and the
- nested element's name. Under certain circumstances, a nested action
- needs to be applied to an element say <a> and also to another
- element <b> nested within <a>. The current implementation of
- <code>NestedComponentIA</code> is capable of handling multiply
- nested elements requiring intervention by the same implicit action.
- </p>
-
- <p>Both <code>ImplicitAction</code> and
- <code>NestedComponentIA</code> are located in the
- <code>ch.qos.logback.core.joran.action</code> package.
- </p>
-
- <p>Refer to the
- <em>logback-examples/src/main/java/joran/implicit</em> directory for
- an example of an implicit action.
- </p>
-
- <p>In that directory, you will find two actions classes, one xml
- file and one class containing the setup of Joran.</p>
-
- <p>The <a href="../xref/chapter3/implicit/NOPAction.html">
- <code>NOPAction</code></a> class does nothing. It is used to set the
- context of the <em>foo</em> element, using this line:</p>
-
- <p class="source">ruleStore.addRule(new Pattern("*/foo"), new NOPAction());</p>
-
- <p>After that, the implicit action, namely <a
- href="../xref/chapter3/implicit/PrintMeImplicitAction.html">
- <code>PrintMeImplicitAction</code></a>, is added to the
- <code>RuleStore</code>. This is done by simply adding a new instance
- of the action to the <code>Joran interpreter</code></p>
-
- <p class="source">ji.addImplicitAction(new
- PrintMeImplicitAction());</p>
-
- <p>When called, the <code>isApplicable()</code> method of
- <code>PrintMeImplicitAction</code> checks the value of the
- <em>printme</em> attribute. If the value is <code>true</code>, the
- implicit action is applicable: its <code>begin()</code> method will
- be called.</p>
-
- <p>The <em>implicit1.xml</em> file contains the following lines:</p>
-
- <em>Example 3.<span class="autoEx"/>: Usage of implicit rules
- (logback-examples/src/main/java/chapter3/implicit/implicit1.xml)</em>
-<div class="source"><pre><foo>
-
- <xyz printme="true">
- <abc printme="true"/>
- </xyz>
-
- <xyz/>
-
- <foo printme="true"/>
-
-</foo></pre></div>
-
- <p>As you can see, the first element will be printed, since it has a
- <em>printme</em> attribute, which bears the value
- <code>true</code>.</p>
-
- <p>The second element will not be printed, because no <em>printme</em> attibute is present.</p>
-
- <p>The last element will not be printed, although the required
- attribute is present. This is because implicit rules are called
- only if no explicit rules are defined. Since we added a
- <code>NOPAction</code> with the <em>*/foo</em> pattern, it will be
- used instead of the <code>PrintMeImplicitAction</code>.</p>
-
- <p>Running the example yields the following output:</p>
-
-<div class="source"><pre>Element <xyz> asked to be printed.
-Element <abc> asked to be printed.
-ERROR in ch.qos.logback.core.joran.spi.ExecutionContext at 1c5c1 - no applicable action \
-for <xyz>, current pattern is [/foo/xyz]</pre></div>
-
- <p>The last line was printed because of a call to
- <code>StatusPrinter</code> at the end of the main class.</p>
-
- <h3>Non goals</h3>
-
- <p>The Joran API is not intended to be used to parse documents with
- thousands of elements.
- </p>
-
-
-
-
More information about the logback-dev
mailing list