
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@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@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> - - - -