Add auto update and plugins to your Java application

Auto update is a feature every desktop application should have ("real" users NEVER reinstall the latest version). Plugins is also a great way to enhance your application (let people code for you :) ).



There is some existing framework (Eclipse plugin, Netbean platform, Java web start (for the update) ...) which look great, but you have to learned how to use the whole platform (do I have mentioned I'm lazy ?), and you are tied to it. It was a lot more fun to code my own system :)



It's very simple to use and you should be able to have auto update AND plugins on your application with just a few lines of code. Let's look at his example :



@SuppressWarnings("serial")



public class TestProgram extends JFrame {







public TestProgram() {



super();



this.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);



this.setTitle("test program");



this.setSize(200, 200);



}







/**



* @param args



*/




public static void main(String[] args) {



try {



TestProgram test = new TestProgram();



test
.setVisible(true);







} catch (Exception e) {



e
.printStackTrace();



}



}



}



This is a simple boring example app that just display a frame. We're going to make it a little more interesting by adding this jar to the classpath of the app.

The next thing to do is to add this lines of codes to the main method :



public static void main(String[] args) {



try {



TestProgram test = new TestProgram();



PlugEngine.getInstance().init(test);



UpdateDialog.showDialog();



PlugEngine.getInstance().startPluggables();



test
.setVisible(true);







} catch (Exception e) {



e
.printStackTrace();



}



}



PlugEngine.getInstance().init(test) initialize the plugin engine. You can give some parameters that will be given to the plugins when they will be started. Here I give the main class as a parameter, so that the plugins have an entry point in my application, but you could add more :



PlugEngine.getInstance().init(test, "a param", new Long(3));



Then UpdateDialog.showDialog() will display a popup that show the available updates, or do nothing is there is no updates :



PlugEngine.getInstance().startPluggables() will then start all the freshly update plugins.



We can now add a button that will display a popup which allow the user to manage all the installed plugin, and add more plugins :



public TestProgram() {



super();



this.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);



this.setTitle("test program");



this.setSize(200, 200);



JButton button = new JButton("Show plugins");



button
.addActionListener(new ActionListener() {



public void actionPerformed(ActionEvent e) {



PluginDialog.showDialog();



}



});



this.getContentPane().add(button);



}





PluginDialog.showDialog() will open this popup :



That's all for you're app !



Now we can write our first plugin. A plugin is an implementation of Pluggable. You can also extends Plugin, which provide some default implementations of some of the methods from Pluggable :



public interface Pluggable {







/**



* @return the name for this plugin, should not be null



*/




public String getName();







/**



* @return the version for this plugin



*/




public String getVersion();







/**



* @return the description for this plugin



*/




public String getDescription();







/**



* @return the icon for this plugin



*/




public BufferedImage getIcon();







/**



* @return the place where the XML descriptor for this plugin can be found, should not be null



*/




public URI getURI();







/**



* this method will be called when the main program start if this plugin is installed



* @param args arguments given by the main program



*/




public void init(Object... args);







/**



* @return true if has some options to configure by the user



*/




public boolean hasOptions();







/**



* if hasOptions() return true, this method will be called when the user



* want to edit the options from this plugin.



*/




public void openOptions();



}



The plugin should also have an constructor without parameters. The init method will be called with the parameters from the main program when the plugin is launched.

Here an example which allow the user to change the title of the main frame :



public class Plugin1 implements Pluggable {







private String title;



private TestProgram prog;







public void init(Object... args) {



if (args != null && args.length > 0 && args[0] instanceof TestProgram) {



prog
= (TestProgram) args[0];



title
= prog.getTitle();



}



}







public void openOptions() {



title
= JOptionPane.showInputDialog(null, "Choose a new title :", title);



System.out.println(title);



prog
.setTitle(title);



}







public String getName() {



return "Plugin 1";



}







public String getVersion() {



return "v1";



}







public String getDescription() {



return "Allow to change the title of the frame";



}







public URI getURI() {



try {



return new URI("http", "lebesnec.free.fr", "/plugin1.xml", null);







} catch (URISyntaxException e) {



e
.printStackTrace();



return null;



}



}







public boolean hasOptions() {



return true;



}







private BufferedImage icon;







public BufferedImage getIcon() {



try {



if (icon == null) {



URL imageURL
= getClass().getResource("portal.jpg");



icon
= ImageIO.read(imageURL);



}







} catch (Throwable e) {



e
.printStackTrace();



return null;



}



}



}



Once the plugin is written it should be packaged as a JAR file.

The getURI method should return the path to a XML file on the internet which indicate where to download the JAR file, the current version and the main class of the plugin :



xml version="1.0" encoding="UTF-8"?>



<plugengine>



<plugin>



<version>v1version>



<source>file:/C:/eclipse/workspace/plug-engine test plugin/plug1.jarsource>



<class>Plugin1class>



plugin>



plugengine>




So if i made a new version of the plugin I just have to replace the Jar on my website and update the version number in this XML file (and in the java code of the plugin). The plugin will then automatically be displayed in the updates, if you have installed it on the "plugins" windows.



To install a new plugin you have to download this XML file, open the "plugins" windows, choose "Add plugin" and open the file (no need to restart the application).



Ok, now what if i want to make my application "updatable" ? Simply package it as a plugin ! The list of all the installed plugin is stored in a file "plugengine.xml" at the root of your app :



xml version="1.0" encoding="ISO-8859-1"?>



<plugengine>



<plugin enabled="true" hidden="false" update="true">



<source>plugins/plug1.jarsource>



<class>Plugin1class>



plugin>



<plugin enabled="true" hidden="false" update="true">



<source>plugins/plug2.jarsource>



<class>Plugin2class>



plugin>



plugengine>





You can manually add your application as a plugin in this file. Also set hidden to true in this file for your plugin and it will not appear in the "plugins" windows, so users will not be able to remove or disable it.



Voilà your application is now auto-updatable and support plugins !

(If your not a swing user or want to write your own updates and plugins windows, just use the methods from PlugEngine instead of UpdateDialog and PluginDialog). (I will not cover this here but it should be easy, the methods are straightforward)



Remember that this is just experimental for the moment, there is probably a lot of bugs, and I have tested it only under Windows and Java 5. Also, don't forget that the plugins don't run in a sandbox, they have full access to the system like your application. May i just reinventing the wheel here, but it was still very funny to do :)



Here is the test application from the example. Once downloaded, run it (java -classpath test.jar;plug-engine.jar test.TestProgram) and click the button to open the plugins windows. You can then download and install this plugin. It will also appear as an update each time you run the application (until you uncheck it in the update box).



You can also download the sources (from the library, the example application and the example plugin).

And the jar for the plug engine, if you want to use it in your application.



Things to improve/add :

- internationalization

- drag&drop of the xml file in the plugins dialog to install

- use a version number for the main app. If the plugin is designed for a lower version, disabled it

- allow plugin to have more than one JAR file (packaged in zip file for example)





PS : it was incredibly hard to write the custom list component from the plugins and updates dialog, particularly the "extend" effect. I think I will write about this later.