| <!-- $Id: package.html,v 1.5.2.2 2004/08/18 19:59:29 rdonkin Exp $ |
| |
| Copyright 2001-2004 The Apache Software Foundation. |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| --> |
| <html> |
| <head> |
| <title>An overview of the Digester Plugins module.</title> |
| </head> |
| <body> |
| <p>Provides an easy mechanism whereby new digestion rules |
| can be added dynamically during a digestion.</p> |
| <h2> Introduction. </h2> |
| Many applications have xml configuration files which are "extensible". |
| Some examples of this are: |
| <ul> |
| <li>Apache log4j allows user-provided "Appender" classes to be specified in |
| its configuration file</li> |
| <li>Apache Avalon allows "components" of a user-specified class</li> |
| <li>Apache Ant allows custom tasks to be defined</li> |
| </ul> |
| The Digester "plugins" module can be used to add this kind of functionality |
| to your own applications. |
| <p> |
| <h2> An Example </h2> |
| Let's start off with an example. |
| <p> |
| Given the following digester rules in the main "parsing" application: |
| <pre> |
| 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.parse(filename); |
| </pre> |
| <p> |
| the following input can be processed: |
| <p> |
| <pre> |
| <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 "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 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, somewhat in the spirit of |
| "BeanInfo" classes for java beans, or</li> |
| <li> providing an xmlrules file which defines the associated parsing rules.</li> |
| </ul> |
| If a plugin class has a no-parameter constructor, does not expect any subtags, |
| and is satisfied with mapping any attributes on the parent xml tag to |
| bean-property-setter methods on itself, then no rules need to be defined at |
| 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 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-declared (provided the application |
| associates a <code>PluginDeclarationRule</code> 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> Suggested Applications </h2> |
| |
| Any application where user-specific operations may need to be performed |
| that cannot be known in advance by the initial application developer may |
| benefit from this module. Applications in the style of the Apache projects |
| listed at the top of this page (Log4j, Cocoon, Ant) are examples. |
| <p> |
| 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> |
| |
| The term "plugin declaration" refers to an xml element which matches a |
| PluginDeclarationRule, where the user specifies an id-to-class mapping. |
| <p> |
| The term "plugin point" refers to a pattern associated with a PluginCreateRule. |
| An xml element matching that pattern is expected to have a plugin-id attribute |
| (but see note on "default plugins" elsewhere in this document). |
| |
| <h2> Limitations </h2> |
| |
| 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). 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 |
| required. This is not expected to be a problem. |
| <p> |
| |
| <h2> Performance </h2> |
| |
| For patterns which do not involve "plugin points" there is minimal |
| performance impact when adding rules to the Digester, and none when |
| processing input data. |
| <p> |
| Processing elements which match patterns added dynamically by plugin classes |
| does have a performance impact, but not excessively so. |
| |
| <h2> Alternatives </h2> |
| |
| The "xmlrules" digester module allows modification of parsing rules |
| without code changes or recompilation. However this feature is aimed |
| at the developer, not the end user of an application. The differences |
| between xmlrules functionality and plugins functionality are: |
| <ul> |
| <li> |
| 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 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> |
| |
| In order to be useful, the problem domain needs to involve a base class or |
| interface which can have multiple implementations. This section assumes that |
| this is the case, that you have already created a concrete implementation |
| of that base class or interface, and are wondering what changes need to |
| be made to that class to make it suitable for a "plugin". |
| <p> |
| Well, if the class has a no-argument constuctor, and only simple configuration |
| needs that can be met by a SetPropertiesRule, then no changes need to be |
| made at all. |
| <p> |
| In other circumstances, you may either define an "addRules" method on the |
| class which adds any necessary rules to the digester, a separate class |
| containing that information, or write an xmlrules-format file defining the |
| necessary rules. In the "separate rule info class" approach, the class containing |
| 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 SubstituteTransform, from the example: |
| <pre> |
| public static void addRules(Digester d, String pathPrefix) { |
| d.addCallMethod(pathPrefix+"/from", "setFrom", 0); |
| d.addCallMethod(pathPrefix+"/to", "setTo", 0); |
| } |
| </pre> |
| A "rule info" class consists of nothing but a static method defined as above. |
| <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. |
| <p> |
| Note that when adding any rules, the pattern passed to the digester |
| <i>must</i> start with the pathPrefix provided. A plugin cannot |
| define rules with absolute paths. And as defined in the limitations, the |
| pattern should not include any wildcard characters. |
| |
| <h2> Other features </h2> |
| |
| Multiple plugin declarations are permitted; the latest simply overrides |
| earlier ones. |
| <p> |
| In situations where a user <i>might</i> want to specify a custom class, |
| but will often want "default" behaviour, a PluginCreateRule can specify |
| a default class. If the user then omits the "plugin-id" attribute on |
| the matching xml element, an instance of the default class will be |
| created. |
| </body> |
| </html> |