Originally posted by Jim Yingst:
Requiring a user config file may seem (mildly) undesirable, but it seems much better than, say, just picking the fist implementation you find, isn't it? That could lead to unpredictable, confusing results. Perhaps in some applications you could pop up a dialog and ask the user which plugin they'd prefer to use. But in general, that seems a poor choice.
I was thinking that one of the methods inside the Plugin interface would have a "knickname" method (like "getKnickName()"). My application is very similar to Ant, in that the plugins are actually processors for individual XML tags (but more specific to generating web page forms). So the processor looks at the tag name, then uses the tag name as the "knickname". Then I have a factory class which, given the knickname, will return an instance of the Plugin class which has that knickname. Currently the factory uses the "plugins.xml" to find the supported plugin class names.
I want the jar files to be as portable as possible - in fact my plan is to host the JAR files in a database so that the website can have an "admin console" to upload new JAR files.
So what i want to move away from is having a central plugin configuration, and instead simply control which JAR files are used in my ClassLoader. I even want the JAR files to allow versioning, so I'll create "JAR Groups" which can then support different versions of a single JAR file.
As for plugins in the actual class path - i think I'm going to host all the 'core' plugin files in a 'core' plugin JAR file (which i'll drop in the plugins directory). In this way, the Plugin factory class can be sure that any Plugin it needs to create will be located in a JAR located in one of the JAR URLs in the "JAR Group" (and definately not in the CLASSPATH).
Possible Solution - - - - - - - - - - - - - - - - - -
So I tried the following approach (which seems to be working):
1) Input to the PluginFactory's constructor will be the URLClassLoader (which contains all the JAR file URLs)
2) Create an ZipInputStream to each of the URLs in the list, search each ZipEntry to find the name - if the name endsWith '.class', then convert the name to a className (replacing '/' with '.' and removing '.class')
3) Instantiate the Class object for this className (using the URLClassLoader) - check if that Class .isAssignableFrom(Plugin.class)
4) If it is a Plugin, then create an instance of that object and invoke the 'getKnickName()' method
5) Store the knickname as a key in some hashMap, the value of which will be the Class object
6) In the main 'createPlugin()' method, take input the knickname - lookup the Class object in the hashmap, and create an instance of that Class object
Concerns with the above - - - - - - - - - - - - - - - - - - - -
Step 2 needs to 'reload' the JAR files through an input stream; so physically the JAR files will be loaded twice - once to check for .class files - and once when the URLClassLoader needs to actually load the class in step 3. (this is not ideal since i would like to only process the JAR file once, if at all possible)
Also - after step 2 & 3 the URLClassLoader will have instantiated and cached every available class in the JAR files. This could be a good thing, or a bad thing - depending on how you look at it.
What do you guys think - can i do better than my solution?
I'm not sure yet what I think about my solution, since I would only accrue the cost of looking at the JAR files 2 times once (when i create the instance of my PluginFactory class for a particular JAR Group).
Or maybe should I just give in and require my Plugin Admin console to request that when you add an additional plugin JAR file at runtime that you, in addition, provide the Class names of all supported Plugins.
[ June 01, 2007: Message edited by: Scott McGhee ]