<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>Wildfire Plugin Guide</title> <link href="style.css" rel="stylesheet" type="text/css"> </head> <body> <h1>Wildfire Plugin Developer Guide</h1> <a name="top"></a> <h2>Introduction</h2> <p> Plugins enhance the functionality of Wildfire. This document is a developer's guide for creating plugins. </p> <h2>Structure of a Plugin</h2> <p> Plugins live in the <tt>plugins</tt> directory of <tt>wildfireHome</tt>. When a plugin is deployed as a JAR or WAR file, it is automatically expanded into a directory. The files in a plugin directory are as follows: </p> <fieldset> <legend>Plugin Structure</legend> <pre>myplugin/ |- plugin.xml <- Plugin definition file |- readme.html <- Optional readme file for plugin, which will be displayed to end users |- changelog.html <- Optional changelog file for plugin, which will be displayed to end users |- icon_small.gif <- Optional small (16x16) icon associated with the plugin (can also be a .png file) |- icon_large.gif <- Optional large (32x32) icon associated with the plugin (can also be a .png file) |- classes/ <- Resources your plugin needs (i.e., a properties file) |- database/ <- Optional database schema files that your plugin needs |- i18n/ <- Optional i18n files to allow for internationalization of plugins. |- lib/ <- Libraries (JAR files) your plugin needs |- web <- Resources for Admin Console integration, if any |- WEB-INF/ |- web.xml <- Generated web.xml containing compiled JSP entries |- web-custom.xml <- Optional user-defined web.xml for custom servlets |- images/ </pre> </fieldset> <p>The <tt>web</tt> directory exists for plugins that need to add content to the Wildfire Admin Console. Further details are below.</p> <p> The <tt>plugin.xml</tt> file specifies the main Plugin class. A sample file might look like the following: </p> <fieldset> <legend>Sample plugin.xml</legend> <pre class="xml"> <?xml version="1.0" encoding="UTF-8"?> <plugin> <span class="comment"><!-- Main plugin class --></span> <class>org.example.ExamplePlugin</class> <span class="comment"><!-- Plugin meta-data --></span> <name>Example Plugin</name> <description>This is an example plugin.</description> <author>Jive Software</author> <version>1.0</version> <date>07/01/2006</date> <url>http://www.jivesoftware.org/plugins</url> <minServerVersion>3.0.0</minServerVersion> <span class="comment"><!-- Admin console entries --></span> <adminconsole> <span class="comment"><!-- More on this below --></span> </adminconsole> </plugin> </pre> </fieldset> <p>The meta-data fields that can be set in the plugin.xml file: <ul> <li>name -- the name of the plugin.</li> <li>description -- the description of the plugin.</li> <li>author -- the author of the plugin.</li> <li>version -- the version of the plugin.</li> <li>date -- the date the plugin was released. The date must be in the form MM/dd/yyyy, such as 07/01/2006.</li> <li>url -- a URL where additional information about the plugin is available.</li> <li>minServerVersion -- the minimum version of Wildfire required to run the plugin (supported by Wildfire 2.1.2 and later). If the server version is less than the required value, the plugin will not be started.</li> <li>databaseKey -- if the plugin requires it's own database tables, the databaseKey element should be set with a schema key name (often the same name as the plugin). Database schema files for each supported database should then be placed in the <tt>database</tt> directory of the plugin. For example, given the key "foo", schema files would be called "foo_mysql.sql", "foo_oracle.sql", etc. The scripts should make an entry into the jiveVersion table using the key so that schema version information can be tracked, e.g.:<br><br> <tt>INSERT INTO jiveVersion (name, version) VALUES ('foo', 0);</tt><br><br> </li> <li>databaseVersion -- the database schema version (if a database schema is defined). New plugins with a database schema should start at version 0. If future versions of the plugin require updates to the schema, those updates can be defined by creating sub-directories in the <tt>database</tt> directory for each version number. For example, the directories <tt>database/1</tt> and <tt>database/2</tt> would contain scripts such as "foo_mysql.sql" and "foo_oracle.sql" that contain the relevant database changes for each version. Each script should update version information in the jiveVersion table, e.g.:<br><br> <tt>UPDATE jiveVersion set version=1 where name='foo';</tt><br><br> </li> <li>parentPlugin -- the name of the parent plugin (given as "foo" for the "foo.jar" plugin). When a plugin has a parent plugin, the parent plugin's class loader will be used instead of creating a new class loader. This lets plugins work together more closely. A child plugin will not function without its parent present.</li> </ul></p> Several additional files can be present in the plugin to provide additional information to end-users (all placed in the main plugin directory): <ul> <li><tt>readme.html</tt> -- Optional readme file for plugin, which will be displayed to end users.</li> <li><tt>changelog.html</tt> -- Optional changelog file for plugin, which will be displayed to end users.</li> <li><tt>icon_small.png</tt> -- Optional small (16x16) icon associated with the plugin. It can also be a .gif file.</li> <li><tt>icon_large.png</tt> -- Optional large (32x32) icon associated with the plugin. It can also be a .gif file.</li> </ul> <p>Your plugin class must be implement the <tt><a href="javadoc/org/jivesoftware/wildfire/container/Plugin.html">Plugin</a></tt> interface from the <a href="javadoc/index.html">Wildfire API</a> as well as have a default (no argument) contructor. The Plugin interface has methods for initializing and destroying the plugin. </p> <fieldset> <legend>Sample plugin implementation</legend> <pre class="java"> package org.example; import org.jivesoftware.wildfire.container.Plugin; import org.jivesoftware.wildfire.container.PluginManager; import java.io.File; /** * A sample plugin for Wildfire. */ public class ExamplePlugin implements Plugin { public void initializePlugin(PluginManager manager, File pluginDirectory) { <span class="comment">// Your code goes here</span> } public void destroyPlugin() { <span class="comment">// Your code goes here</span> } } </pre> </fieldset> <h2>Modifying the Admin Console</h2> <p>Plugins can add tabs, sections, and pages to the admin console. There are a several steps to accomplishing this: <ul> <li>An <adminconsole/> section must be added to the <tt>plugin.xml</tt> file. </li> <li>JSP files must be compiled and put into the classpath of the plugin. A <tt>web.xml</tt> file containing the compiled JSP servlet entries must be put into the <tt>web/</tt> directory of the plugin. <i>Note:</i> the Wildfire build script can assist with compiling JSPs and creating the web.xml. This is detailed below. </li> <li>Any images required by your JSP pages must live in <tt>web/images/</tt> directory. Only GIF and PNG images are supported. </li> </ul> <p>The <tt><adminconsole /></tt> section of <tt>plugin.xml</tt> defines additional tabs, sections and entries in the Admin Console framework. A sample <tt>plugin.xml</tt> file might look like the following:</p> <fieldset> <legend>Sample plugin.xml</legend> <pre class="xml"> <?xml version="1.0" encoding="UTF-8"?> <plugin> <span class="comment"><!-- Main plugin class --></span> <class>org.example.ExamplePlugin</class> <span class="comment"><!-- Admin console entries --></span> <adminconsole> <tab id="mytab" name="Example" url="my-plugin-admin.jsp" description="Click to manage..."> <sidebar id="mysidebar" name="My Plugin"> <item id="my-plugin" name="My Plugin Admin" url="my-plugin-admin.jsp" description="Click to administer settings for my plugin" /> </sidebar> </tab> </adminconsole> </plugin> </pre> </fieldset> <p> In this example, we've defined a new tab "Example", a sidebar section "My Plugin" and a page "My Plugin Admin". We've registered <tt>my-plugin-admin.jsp</tt> as the page. You can override existing tabs, sections, and items by using the existing id attribute values in your own <tt><adminconsole></tt> defintion. </p> <h3>Admin Console Best Practices</h3> There are several best practices to consider when making changes to the Wildfire admin console via a plugin. The general theme is that plugins should integrate seamlessly: <ul> <li>Integrate into existing tabs and sidebar sections whenever possible instead of creating your own. Only create new tabs for very significant new functionality. <li>Don't use the word "plugin" in names of tabs, sidebars and items. For example, instead of having an item called "Gateway Plugin", it could be called "Gateway Settings". <li>Try to match the UI of the existing admin console in your custom plugin pages. <li>There is no need to create an admin console entry to show plugin meta-data. Instead, let Wildfire inform the user about which plugins are installed and provide plugin management. </ul> <h3>Writing Pages for the Admin Console</h3> <p> Wildfire uses the <a href="http://www.opensymphony.com/sitemesh/" target="_blank">Sitemesh</a> framework to decorate pages in the admin console. A globally-defined decorator is applied to each page in order to render the final output, as in the following diagram:</p> <br> <div align="center"><img src="images/sitemesh.png" width="484" height="372" alt="Sitemesh"></div> <br><br> <p> Creating pages that work with Sitemesh is easy. Simply create valid HTML pages and then use meta tags to send instructions to Sitemesh. When rendering the output, Sitemesh will use the instructions you provide to render the decorator along with any content in the body of your HTML page. The following meta tags can be used: <ul> <li><b>pageID</b> -- the ID of the page, which must match an entry in the admin console XML described above. Either a pageID or subPageID <b>must</b> be specified.</li> <li><b>subPageID</b> -- the ID of the sub-page, which must match an entry in the admin console XML described above. Sub-pages are used for administrative actions related to a parent page ID. For example, editing or deleting a particular group. Either a pageID or subPageID <b>must</b> be specified.</li> <li><b>extraParams</b> (Optional) -- extra parameters that should be passed in to the page. For example, on a page to delete a group it might be the ID of the group. Parameters must be URL encoded.</li> <li><b>decorator</b> (Optional) -- overrides the Sitemesh decorator to use for the page. A decorator named <tt>none</tt> is available that will simply render the page without a decorator.</li> </ul> The following HTML snippet demonstrates a valid page: </p> <fieldset> <legend>Sample HTML</legend> <pre> <html> <head> <title>My Plugin Page</title> <meta name="pageID" content="myPluginPage"/> </head> <body> Body here! </body> </html> </pre> </fieldset> <h4>Using i18n in your Admin Console Pages</h4> <p> To use i18n in your own plugin, you must do the following: <ul> <li>Add i18n directory to root directory of your plugin.</li> <li>Add each resource file using the %name_of_plugin%_i18n "_" language ".properties" naming convention.</li> <li>To use directly within your jsp, use the LocaleUtils class, and pass in the name of the plugin.<br/><b>Example: </b><%= LocaleUtils.getLocalizedString("my.title", "myplugin") %> </li> </ul> </p> <h2>Using the Wildfire Build Script</h2> <p> The Wildfire build script will help you build and develop plugins. It looks for plugin development directories in the following format: </p> <fieldset> <legend>Plugin Structure</legend> <pre>myplugin/ |- plugin.xml <- Plugin definition file |- readme.html <- Optional readme file for plugin |- changelog.html <- Optional changelog file for plugin |- icon_small.gif <- Optional small (16x16) icon associated with the plugin (can also be a .png file) |- icon_large.gif <- Optional large (32x32) icon associated with the plugin (can also be a .png file) |- classes/ <- Resources your plugin needs (i.e., a properties file) |- lib/ <- Libraries your plugin needs |- src/ |- java <- Java source code for your plugin | |- com | |- mycompany | |- *.java |- web |- *.jsp <- JSPs your plugin uses for the admin console |- images/ <- Any images your JSP pages need (optional) |- WEB-INF |- web.xml <- Optional file where custom servlets can be registered </pre> </fieldset> <p>The build script will compile source files and JSPs and create a valid plugin structure and JAR file. Put your plugin directories in the <tt>src/plugins</tt> directory of the source distribution and then use <tt>ant plugins</tt> to build your plugins.</p> <p>Any JAR files your plugin needs during compilation should be put into the <tt>lib</tt> directory. These JAR files will also be copied into the plugin's generated <tt>lib</tt> directory as part of the build process.</p> <p>If you create a src/web/WEB-INF/web.xml file, any servlets registered there will be initialized when the plugin starts up. Only servlet registrations and servlet mappings will be honored from the web.xml file. Note: this feature is implemented by merging your custom web.xml file into the web.xml file generated by the JSP compilation process.</p> <h2>Implementing Your Plugin</h2> <p>Plugins have full access to the Wildfire API. This provides a tremendous amount of flexibility for what plugins can accomplish. However, there are several integration points that are the most common: <ol> <li>Register a plugin as a <a href="javadoc/org/jivesoftware/wildfire/Component.html">Component</a>. Components receive all packets addressed to a particular sub-domain. For example, <tt>test_component.example.com</tt>. So, a packet sent to <tt>joe@test_component.example.com</tt> would be delivered to the component. Note that the sub-domains defined as components are unrelated to DNS entries for sub-domains. All XMPP routing at the socket level is done using the primary server domain (example.com in the example above); sub-domains are only used for routing within the XMPP server. <li>Register a plugin as an <a href="javadoc/org/jivesoftware/wildfire/IQHandler.html">IQHandler</a>. IQ handlers respond to IQ packets with a particular element name and namespace. The following code snippet demonstrates how to register an IQHandler: <pre> IQHandler myHandler = new MyIQHander(); IQRouter iqRouter = XMPPServer.getInstance().getIQRouter(); iqRouter.addHandler(myHandler); </pre> <li>Register a plugin as a <a href="javadoc/org/jivesoftware/wildfire/interceptor/PacketInterceptor.html"> PacketInterceptor</a> to receive all packets being sent through the system and optionally reject them. For example, an interceptor could reject all messages that contained profanity or flag them for review by an administrator.</li> <li>You can store persistent plugin settings as Wildfire properties using the JiveGlobals.getProperty(String) and JiveGlobals.setProperty(String, String) methods. Make your plugin a property listener to listen for changes to its properties by implementing the <tt>org.jivesoftware.wildfire.event.PropertyEventListener</tt> method. You can register your plugin as a listener using the PropertyEventDispatcher.addListener(PropertyEventListener) method. Be sure to unregister your plugin as a listener in your plugin's destroyPlugin() method. </ol> </p> <h2>Plugin FAQ</h2> <b>Can I deploy a plugin as a directory instead of a JAR?</b> <p>No, all plugins must be deployed as JAR or WAR files. When a JAR or WAR is not present for the plugin, Wildfire assumes that the file has been deleted and that the users wants to destroy the plugin, so it also deletes the directory.</p> <b>What license agreement are plugins subject to?</b> <p>Because Wildfire is released under the Open Source GPL license, any plugins developed must also be released under the GPL or a compatible Open Source license. It is a violation of the license agreement to create plugins that are not Open Source. Please visit <a href="http://www.jivesoftware.com/products/wildfire/">Jive Software</a> if you need different licensing terms for Wildfire, including the right to create commercial plugins.</p> <br><br> </body> </html>