This tutorial will provide an overview of how plugins for LessEntropy can be developed, bundled and deployed. During this tutorial we will develop a simple plugin that adds the original file name (the file name used during indexing) to all files. The plugin provides an editor that displays this file name.
The source code for this plugin is available as a zip file.
It is assumed that you are familiar with Maven and have a Maven 2.0.x installed on your computer.
In order to build plugins it is important to understand the ideas behind the plugin interface.
A plugin is just a container for tags (discussed below). A plugin is a class that implements IPlugin but as plugins are described declaratively you will never need to create a class that implements IPlugin directly. Instead you will rely on support classes. Once the plugin is instantiated by the application it provides access to the contained tags.
A tag is the type of meta information that are linked to a file. You can think of the tag being a class and the meta information being the object. A tag provides methods for instantiating meta information for a given file. A tag is only instantiated once per application run. A tag is implemented using a java class that either implements ITag directly or extends one of the support classes (like TagSupport).
A tag can instantiate meta information which are objects of a class implementing IMetaInformation. Typically there is one meta information class that corresponds to one tag class but there are several instances (one for each file) of the meta information class that correspond to one instance of the tag class.
Keeping this in mind we will now start to build the plugin.
The following figure shows a sample directory structure for a plugin. Since LessEntropy is build using Maven it is recommended that you build your plugin using maven as well. The plugin directory structure therefore obeys to the maven project layout.
-demoplugin | +-src | | | +-main | | | +-java | | | +-resources | | | +-localStrings | | | +-plugin.xml +-pom.xml
The src/main/java folder will contain all java source files for the pluging. The src/main/resources folder will contain the resources. In addition, it stores the very important plugin.xml which describes the plugin.
The pom.xml is the maven build descriptor. One sample descriptor is given below.
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>net.sf.lessentropy</groupId> <artifactId>lessentropy-demo-plugin</artifactId> <packaging>jar</packaging> <version>1.0-SNAPSHOT</version> <name>LessEntropy Demo Plugin</name> <url>http://lessentropy.sourceforge.net</url> <dependencies> <dependency> <groupId>net.sf.lessentropy</groupId> <artifactId>lessentropy-sdk</artifactId> <version>2.0.2</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.5</source> <target>1.5</target> </configuration> </plugin> </plugins> <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> <includes> <include>plugin.xml</include> <include>localString/**/*.xml</include> </includes> </resource> </resources> </build> </project>
It does nothing special but saying that we are using java 5. It also says that we have one important dependency: The lessentropy sdk which contains the interfaces and classes we need to build a plugin. If you don't know maven 2, it's recommended that you take a look at it. Maven is not only suitable for plugins. It can greatly improve the build process for any (java) project you are running.
Every plugin is bundled as a single jar file. This jar file is deployed to the plugins folder of the application. When LessEntropy loads plugins it reads all jar files from the plugins folder and extracts a file named plugin.xml from the root of the jar file. If such a file is not found, the plugin can not be loaded.
The plugin.xml describes the plugin and all tags it provides (remember that a plugin can provied more than one tag, although most plugins will provide only one tag).
The plugin descriptor for the demo plugin is shown below.
<?xml version="1.0" encoding="UTF-8"?> <plugin sdk-version="1.0" id="net.sf.lessentropy.plugin.demo"> <name>DemoPlugin</name> <author>Alexander Metzner</author> <version>1.0-SNAPSHOT</version> <url>http://lessentropy.sourceforge.net/plugins/demo</url> <plugin-class>net.sf.lessentropy.impl.plugin.PluginSupport</plugin-class> <tags> <tag id="net.sf.lessentryop.plugin.demo.demotag"> <name>DemoTag</name> <author>Alexander Metzner</author> <version>1.0</version> <tag-class>net.sf.lessentropy.plugin.demo.DemoTag</tag-class> </tag> </tags> </plugin>
The tags are discussed below
Once you have created the plugin descriptor we can now go on and talk about the java stuff.
All java source files are located in the src/main/java folder. The demo plugin provides three classes:
- net.sf.lessentropy.plugin.demo.DemoTag
- net.sf.lessentropy.plugin.demo.DemoMetaInformation
- net.sf.lessentropy.plugin.demo.editor.DemoTagEditor
The class net.sf.lessentropy.plugin.demo.DemoTag implements the java logic needed to make up the tag. The source code is shown below.
package net.sf.lessentropy.plugin.demo; import java.util.ArrayList; import java.util.Collection; import net.sf.lessentropy.impl.plugin.TagSupport; import net.sf.lessentropy.model.File; import net.sf.lessentropy.model.IMetaInformation; import net.sf.lessentropy.model.MimeType; import net.sf.lessentropy.plugin.TagMismatchException; import net.sf.lessentropy.plugin.demo.editor.DemoTagEditor; import net.sf.lessentropy.plugin.editor.ITagEditor; /** * A tag demonstrating the plugin interface of LessEntropy. * @author Alexander Metzner * @version 1.0 */ public class DemoTag extends TagSupport { private DemoTagEditor editor = new DemoTagEditor(); private String[] propertyNames = { "Demo" }; public String[] getPropertyNames() { return propertyNames; } public String[] getDefaultPropertyNames() { return propertyNames; } public Collection<MimeType> getSupportedMimeType() { ArrayList<MimeType> ret = new ArrayList<MimeType>(); ret.add(MimeType.ALL); return ret; } public boolean supportsCustomEditor() { return true; } public ITagEditor getCustomEditor() { return editor; } public IMetaInformation instantiate(File file, java.io.File physicalPath) throws TagMismatchException { DemoMetaInformation ret = new DemoMetaInformation(); ret.setTag(this); ret.setAbsolutePath(physicalPath.getAbsolutePath()); return ret; } public IMetaInformation recreate(String data) { DemoMetaInformation ret = new DemoMetaInformation(); ret.setTag(this); ret.setAbsolutePath(data); return ret; } }
The class extends TagSupport which is a helper class provided by the LessEntropy SDK. We will now talk about the methods this class implements:
The class net.sf.lessentropy.plugin.demo.DemoMetaInformation implements the meta information that will be attached to our files. The code is shown below.
package net.sf.lessentropy.plugin.demo; import net.sf.lessentropy.model.IMetaInformation; import net.sf.lessentropy.plugin.ITag; /** * {@link IMetaInformation} for the demo plugin. * @author Alexander Metzner * @version 1.0 */ public class DemoMetaInformation implements IMetaInformation { private String absolutePath; private ITag tag; public ITag getTag() { return tag; } public void setTag(ITag tag) { this.tag = tag; } public String getAbsolutePath() { return absolutePath; } public void setAbsolutePath(String absolutePath) { this.absolutePath = absolutePath; } public boolean matches(net.sf.lessentropy.logic.ISearchConstraints arg0) { return false; } public String getPersistentString() { return absolutePath; } }
The methods of this class are described below.
If the tag provides no editor we are ready to launch the plugin. As we want to show the absolute path to the user we need to create one additional class.
The class net.sf.lessentropy.plugin.demo.editor.DemoTagEditor defines the editor used to visualize the meta information. The source code is shown below.
package net.sf.lessentropy.plugin.demo.editor; import java.awt.GridLayout; import java.net.URL; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTextField; import net.sf.lessentropy.impl.util.stringmanager.StringManager; import net.sf.lessentropy.impl.util.stringmanager.XmlContentProvider; import net.sf.lessentropy.model.IMetaInformation; import net.sf.lessentropy.plugin.demo.DemoMetaInformation; import net.sf.lessentropy.plugin.editor.ITagEditor; import org.dom4j.DocumentException; /** * {@link ITagEditor} for the demo plugin's tag. * * @author Alexander Metzner * @version $Revision$ * */ public class DemoTagEditor extends JPanel implements ITagEditor { private JTextField absolutePath; public DemoTagEditor() { try { URL url = getClass().getClassLoader().getResource( "localStrings/demoplugin.xml"); StringManager.initialize(new XmlContentProvider(url)); StringManager sm = StringManager .getStringManager(DemoTagEditor.class); absolutePath = new JTextField(30); setLayout(new GridLayout(1, 2)); add(new JLabel(sm.getString("absolutePath"))); add(absolutePath); } catch (DocumentException e) { throw new RuntimeException(e); } } public JComponent getEditorComponent() { return this; } public void updateData(IMetaInformation metaInformation) { absolutePath.setText(((DemoMetaInformation)metaInformation).getAbsolutePath()); } }
The editor implements the ITagEditor interface. It also extends from JPanel but that is not required. Here is an description of the methods that the editor implements:
The constructor of this class initializes the text field and the layout. Notice the first six lines of the constructor. Here we instantiate a StringManager that provides access to localized messages. The string manager is another class from the LessEntropy SDK that provides an easy way of localizing your layout. It also manages the language the user has chosen from the application and returns appropriate translations if available.
The string manager is configured with a URL. This url points to a resource that should also be located in the src/main/resources folder and is named localStrings/demoplugin.xml. It is good practice to put all your local messages into a folder named localStrings although it is up to you to choose another location as long as you can create a URL to it.
The resource's contents is shown below.
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE stringTable SYSTEM "dtd/stringTable.dtd"> <stringTable> <locale code="en" default="true"> <package name="net.sf.lessentropy.plugin.demo.editor"> <class name="DemoTagEditor"> <key name="absolutePath">Absolute path during indexing:</key> </class> </package> </locale> <locale code="de" default="false"> <package name="net.sf.lessentropy.plugin.demo.editor"> <class name="DemoTagEditor"> <key name="absolutePath">Absoluter Pfad bei der Indizierung:</key> </class> </package> </locale> </stringTable>
This xml file defines one resource for one class: net.sf.lessentropy.plugin.demo.editor.DemoTagEditor. The resource is provided in english and german. The string manager selects the correct translation based on the user's choice.
After having created all the files it's time to build the plugin. One a shell and issue the following command (assuming you have maven 2 installed on your machine).
mvn package
This will compile all the sources, collect the resources and bundle everything together to a single jar that is named lessentropy-demo-plugin-1.0-SNAPSHOT.jar. The file is located in the target folder.
Copy the lessentropy-demo-plugin-1.0-SNAPSHOT.jar file from the target folder the the plugins folder of LessEntropy.
Start LessEntropy and click on the plugins menu. It should contain an entry named Demoplugin.
Congratulations! You successfully build your first plugin.