You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by sk...@apache.org on 2004/02/29 06:52:02 UTC
cvs commit: jakarta-commons/digester/src/java/org/apache/commons/digester/plugins package.html
skitching 2004/02/28 21:52:02
Modified: digester/src/java/org/apache/commons/digester/plugins
package.html
Log:
Significant changes; examples now based on the newly-committed code in
src/examples/plugins/pipeline. This is a lot simpler to explain than
the previous "statements" example. Also removed info on limitations
that no longer exist in current plugins implementation.
Revision Changes Path
1.3 +78 -106 jakarta-commons/digester/src/java/org/apache/commons/digester/plugins/package.html
Index: package.html
===================================================================
RCS file: /home/cvs/jakarta-commons/digester/src/java/org/apache/commons/digester/plugins/package.html,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- package.html 5 Oct 2003 19:48:40 -0000 1.2
+++ package.html 29 Feb 2004 05:52:02 -0000 1.3
@@ -22,75 +22,62 @@
<p>
Given the following digester rules in the main "parsing" application:
<pre>
- Digester digester = new Digester();
- PluginRules rules = new PluginRules();
- digester.setRules(rules);
-
- digester.addRule("program/plugin", new PluginDeclarationRule());
-
- String pattern="program/statement";
- Class baseClass = Statement.class;
- Class defaultClass = StmtCompound.class;
- PluginCreateRule pluginRule = new PluginCreateRule(baseClass, defaultClass);
- digester.addRule(pattern, pluginRule);
- digester.addSetNext(pattern, "addStatement", baseClass.getName());
+ Digester digester = new Digester();
+ PluginRules rc = new PluginRules();
+ digester.setRules(rc);
+
+ digester.addObjectCreate("pipeline", Pipeline.class);
+
+ digester.addCallMethod("pipeline/source", "setSource", 1);
+ digester.addCallParam("pipeline/source", 0, "file");
+
+ PluginCreateRule pcr = new PluginCreateRule(Transform.class);
+ digester.addRule("pipeline/transform", pcr);
+ digester.addSetNext("pipeline/transform", "setTransform");
+
+ digester.addCallMethod("pipeline/destination", "setDest", 1);
+ digester.addCallParam("pipeline/destination", 0, "file");
- digester.push(this);
- digester.parse(filename);
+ digester.parse(filename);
</pre>
<p>
the following input can be processed:
<p>
<pre>
- <program>
- <plugin id="print" class="StmtPrint"/>
- <plugin id="block" class="StmtCompound"/>
- <plugin id="if" class="StmtIf"/>
- <plugin id="equal" class="ExprEqual"/>
- <plugin id="notempty" class="ExprNotEmpty"/>
-
- <statement plugin-id="print" stmt-label="1">msg1</statement>
-
- <statement plugin-id="block">
- <statement plugin-id="print" stmt-label="2.1">msg2a</statement>
- <statement plugin-id="print" stmt-label="2.1">msg2b</statement>
- </statement>
-
- <statement plugin-id="if">
- <expr plugin-id="equal">
- <first-param>foo</first-param>
- <second-param>foo</second-param>
- </expr>
- <statement plugin-id="print" stmt-label="foo">foo equals foo</statement>
- </statement>
-
- <statement plugin-id="if">
- <expr plugin-id="notempty">
- <param/>
- </expr>
- <statement plugin-id="block">
- <statement plugin-id="print" stmt-label="bar">never reached part 1</statement>
- <statement plugin-id="print" stmt-label="bar">never reached part 2</statement>
- </statement>
- </statement>
-
- </program>
+ <pipeline>
+ <source file="input.txt"/>
+ <transform plugin-class="SubstituteTransform">
+ <from>changeme</from>
+ <to>changed</to>
+ </transform>
+ <destination file="output.txt"/>
+ </pipeline>
</pre>
<p>
-Note that the original application only defined a rule for "program/statement".
-It is the input file which has defined exactly which class should
-be instantiated when the statement element is encountered, and furthermore
-the "plugin" classes have dynamically added rules for parsing elements
-nested within themselves.
+Note that the "SubstituteTransform" class is not hard-wired into the
+application, and also that this class is configuring itself from the
+same configuration file.
+<p>
+The user can specify any class they like here, and (provided that class follows
+the plugins conventions) it can use any Digester functionality to process
+the configuration data within the transform tag and its subtags.
+<p>
+The original application simply defined a "plugin point" of
+"pipeline/transform" at which user classes could be plugged in. However
+it did not specify what classes were permitted, other than that they
+must implement the Transform interface. It is the input file which has
+defined exactly which class should be instantiated when the transform
+element is encountered, and furthermore the "plugin" class itself has
+dynamically added rules for parsing elements nested within itself.
<p>
A class used as a plugin may dynamically add its own rules to the digester,
in order to process its attributes and any subtags in any manner it wishes.
-This may be done by either:
+This may be done by several mechanisms, including:
<ul>
<li> declaring a method <code>public static void addRules(Digester d, String
pattern)</code> on the class being "plugged in", or</li>
-<li> providing a separate "rule info" class in the plugin declaration,
-somewhat in the spirit of "BeanInfo" classes for java beans.</li>
+<li> providing a separate "rule info" class, somewhat in the spirit of
+"BeanInfo" classes for java beans.</li>
</ul>
If a plugin class has a no-parameter constructor, does not expect any subtags,
and is content to map any attributes on the parent xml tag to
@@ -98,9 +85,24 @@
all; the class can be used as a plugin without any coding.
<p>
In the example above, an end user may create their own classes which implement
-the required Statement interface, then cause these custom classes to be used
+the required Transform interface, then cause these custom classes to be used
instead of, or in addition to, classes distributed with the application.
+<h2> Plugin Declarations </h2>
+
+As well as the syntax shown above, where plugin classnames were defined
+as they were used, plugin classes can be pre-defined (provided the application
+associates a PluginDeclarationRule with a tag for that purpose). Example:
+<p>
+The plugin class can be declared once:
+<pre>
+ <plugin id="widget" class="com.acme.Widget"/>
+</pre>
+and later referenced via the short "id" value:
+<pre>
+ <sometag plugin-id="widget" ... >
+</pre>
+
<h2> Possible Applications </h2>
Any application where user-specific operations may need to be performed
@@ -108,14 +110,9 @@
benefit from this module. The apache projects listed at the top of this page
(log4j, cocoon, ant) are examples.
<p>
-Note that the example above is an extreme example of configurability;
-it is expected that most application would have many rules of which only
-a few would be "PluginCreateRule" rules, i.e. that the structure of the
-input data would be <i>mostly</i> fixed, with only a few points at which
-the end user could control the class being instantiated. Further, the
-"recursive" nature of the plugin example above (where statements can
-contain statements) is not expected to be typical; it is however
-fully supported.
+Note also that plugged-in classes can themselves allow user-defined classes
+to be plugged in within their configuration. This allows a very simple
+framework to be extended almost without limit by the end user.
<h2> Terminology </h2>
@@ -128,19 +125,14 @@
<h2> Limitations </h2>
-The user cannot replace the <i>name</i> of the tag;
+The user cannot replace the <i>name</i> of the tag used as the plugin-point;
<code><statement plugin-id="if"></code> cannot become <if>.
<p>
An instance of "PluginRules" must be used as the Rules implementation
-for the digester (see example). This class implements only the functionality
-provided by the RulesBase class, ie no extended rules matching is available.
-<p>
-No wildcard patterns are allowed to be associated with PluginCreateRule
-instances. For example <code>digester.addRule("*/statement", pcr)</code>
-is not permitted. This is simply because handling "recursive" plugins
-(ones which allow instances of themselves to be plugged in below themselves,
-as in the Statement example) becomes extremely complicated when wildcard
-patterns are involved.
+for the digester (see example). However a PluginRules can use any other Rules
+implementation as its rule-matching engine, so this is not a significant issue.
+Plugged-in classes may only use the default RulesBase matching for the rules
+they add dynamically.
<p>
For technical reasons, a single instance of PluginCreateRule cannot
currently be associated with multiple patterns; multiple instances are
@@ -164,13 +156,14 @@
between xmlrules functionality and plugins functionality are:
<ul>
<li>
-With xmlrules, the full set of parsing rules is exposed. This is
-good for developers, but in most cases both too complex and too dangerous
-to require end users to edit directly.
+With xmlrules, the full set of parsing rules for the whole configuration file
+is exposed. This is good for developers, but in most cases both too complex
+and too dangerous to require end users to edit directly.
</li>
<li>
Using xmlrules requires a fair level of knowledge of the Apache Digester.
-How a user can use plugins can be explained in about 3 paragraphs. </li>
+How an end user (not a plugin developer) can use plugins can be explained in
+about 3 paragraphs. </li>
</ul>
<h2> How to write plugin classes </h2>
@@ -191,46 +184,25 @@
the rule info may have any name of your choice, but the original class +
"RuleInfo" is recommended.
<p>
-Here is the addRules method on class StmtCompound, from the original example:
+Here is the addRules method on class SubstituteTransform, from the example:
<pre>
- public static void addRules(Digester digester, String patternPrefix)
- {
- digester.addSetProperties(patternPrefix);
-
- String pattern = patternPrefix + "/statement";
- Class baseClass = Statement.class;
- PluginCreateRule pluginRule = new PluginCreateRule(baseClass);
- digester.addRule(pattern, pluginRule);
- digester.addSetNext(pattern, "addStmt", baseClass.getName());
+ public static void addRules(Digester d, String patternPrefix) {
+ d.addCallMethod(patternPrefix+"/from", "setFrom", 0);
+ d.addCallMethod(patternPrefix+"/to", "setTo", 0);
}
</pre>
A "rule info" class consists of nothing but a static method defined as above.
<p>
-The plugins module ensures that the addRules method is called exactly once
-for each "plugin point" in the input document. For example, if a Statement
-can be present at /program/statement and /program/statement/statement, then
-the addRules method is called once with each of those patterns as a
-parameter. Note that these calls are made in a "lazy" manner, ie only
-when absolutely necessary, to avoid recursion problems.
-<p>
If a plugin class does not define an "addRules" method, and the plugin
declaration does not associate a rule info class with it, then the
plugins module will define a "SetPropertiesRule" by default. However if
any custom rules are defined for the plugin class, then that implementation
-is required to define a SetPropertiesRule for itself if it desires one,
-hence the addSetProperties call above.
-<p>
-If the StmtCompound wished to define CallMethodRule rules, or any other
-Digester functionality, it could do so. The example above shows how the
-StmtCompound allows a subelement of <statement> at which the user
- may plug in any class derived from Statement.
+is required to define a SetPropertiesRule for itself if it desires one.
<p>
Note that when adding any rules, the pattern passed to the digester
<i>must</i> start with the patternPrefix provided. A plugin cannot
-define rules with absolute paths. And because using plugins requires
-using PluginRules, which does not support internal or trailing
-wildcards, the pattern should not include any wildcard characters either.
-Neither of these are expected to be problems in practice.
+define rules with absolute paths. And as defined in the limitations, the
+pattern should not include any wildcard characters.
<h2> Other features </h2>
---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org