Plug-in et chargement dynamique de modules, introspection

Description

Package qui gère le chargement dynamique de modules.
Ces modules se présente sous forme d'un .class.
Les classes sont stockées dans un vecteur, soit sous forme de d'objet (de type java.lang.Class), soit d'instances (type java.lang.Object).
Les classes chargées doivent être utilisables directement, avec seulement un cast. En effet, le Loader ne charge pas les méthodes de la classe, peut-être dans une future évolution.

J'ai mis les deux fichiers du package :
- DynamiqueLoader, qui gère le chargement
- PlugInStorage, qui stocke les classes et permet la recherche par nom.

Source / Exemple :


/*** Fichier DynamicLoader.java ***/

package mylib.dynamicloader;

import java.util.*;
import java.io.*;
//import java.lang.*    //Systematically loaded by java.
/**

  • <p>Class to load dynamic plug-ins using introspection and reflexion.
  • Plug-ins are java class files (compiled files) and they must be stored in the
  • same directory. After loading, classes are associated to an unique name and
  • stored onto an array. You can either store the class definition or objects
  • created from a loaded class. If you use instance option (that build object
  • from the class), your class must have a default constructor
  • (with no parameters).</p>
  • <p>Initially created for the software Patty, where it's used to
  • add aircraft or entity specifications to the program.</p>
  • @author VALENTIN Joachim
  • /
public class DynamicLoader { String PlugInDirectory; //Source directory for plug-ins. String PackagePath; //Package path of the plug-ins. Must be the same that defined with the 'package' command into the plug-ins class. Vector ClassArray; //Class or instance storage. boolean OptionInstance; //If true, the loader create and store an instance of the class, rather than the class itself. public DynamicLoader() { PlugInDirectory = ""; PackagePath = ""; ClassArray = new Vector(); OptionInstance = false; } /**Constructor that initialize the plug-in directory and the loading option.*/ public DynamicLoader(String dir, String pack, boolean option) { PlugInDirectory = new String(dir); SetPackagePath(pack); ClassArray = new Vector(); OptionInstance = option; } /**Defines the plug-in directory.*/ public void SetDirectory(String dir) { PlugInDirectory = new String(dir); } /**Returns the plug-in directory.*/ public String GetDirectory() { return PlugInDirectory; } /**Defines the package path. Test if it ends with a '.'.*/ public void SetPackagePath(String pack) { PackagePath = pack; if (!(pack.endsWith("."))) { PackagePath += "."; } } /**Returns the package path.*/ public String GetPackagePath() { return PackagePath; } /**Defines class instance storage option.*/ public void SetInstanceOption() { OptionInstance = true; } /**Defines class storage option.*/ public void UnsetInstanceOption() { OptionInstance = false; } /**Returns storage option (class or instance).*/ public boolean GetInstanceOption() { return OptionInstance; } /**Defines the plug-in directory and load them.*/ public int LoadPlugIn(String dir) { PlugInDirectory = new String(dir); return LoadPlugIn(); } /**Load plug-ins from the previously defined directory.*/ public int LoadPlugIn() { File directory; FilenameFilter classFilter; String[] classFiles; Class tmp_class; PlugInStorage stored; //Create the file system object for the plug in directory. directory = new File(PlugInDirectory); //Create a filter to get only class files from the directory. classFilter = new FilenameFilter() { public boolean accept(File dir, String name) { return name.endsWith(".class"); } }; //Get the list of all filtered filename of the directory. //In fact, all *.class files. if ((classFiles = directory.list(classFilter)) == null) { System.err.println("Nothing to load."); return -1; } //Get all classes associated with *.class files and put them into the array. for (int index = 0; index < classFiles.length; ++index) { stored = new PlugInStorage(); try { //Remove the extension of filename. classFiles[index] = classFiles[index].substring(0, classFiles[index].indexOf(".class")); //Load the class from the filename. Send ClassNotFoundException or NoClassDefFoundError on error. tmp_class = Class.forName(PackagePath + classFiles[index]); if (OptionInstance) { //Create a new instance of the class. Send Exception on error. stored.Set(tmp_class.newInstance()); } else { stored.Set(tmp_class); } //Add the object to the array. ClassArray.add(stored); } catch (ClassNotFoundException e) { System.err.println("Unable to load class \"" + classFiles[index] + "\". " + e.getMessage()); return -1; } catch (NoClassDefFoundError e) { System.err.println("Unable to find the class into the package : " + PackagePath); return -1; } catch (Exception e) { System.err.println("Unable to create instance of the class \"" + classFiles[index] + "\""); return -1; } } //Lexicographically sort the array of plug-ins. SortPlugIns(); return 0; } /**Returns the instance of the class named 'name'.*/ public Object GetInstance(String name) throws ArrayIndexOutOfBoundsException { if (OptionInstance) { return ((PlugInStorage) ClassArray.get(GetIdFromName(name))).PlugInObject; } else { return null; } } /**Returns the class that is named 'name'.*/ public Object GetClass(String name) throws ArrayIndexOutOfBoundsException { if (!OptionInstance) { return ((Class) ((PlugInStorage) ClassArray.get(GetIdFromName(name))).PlugInObject); } else { return null; } } /**Return the position (index) into the array from the name.*/ public int GetIdFromName(String name) { int lower = 0; int upper = 0; int index = 0; int test = 0; upper = ClassArray.size() - 1; if (upper < 0) { //No elements into the array. return -1; } //Last element of the will never be tested, then we test it first. if (((PlugInStorage) ClassArray.lastElement()).Compare(name) == 0) { return upper; } /**Loop for element seeking. Our array is lexicographically sorted by name (PlugInStorage.ObjectName).
  • See PlugInStorage.Compare() to know its return values.
  • We compare the middle element of the range with the name to reach.
  • Then we know in which part of the range the name is.
  • And we loop unless we find the element or the range is equal to 1.
  • /
do { index = (lower + upper) / 2; //Element to test. test = ((PlugInStorage) ClassArray.get(index)).Compare(name); if (((upper - lower) <= 1) && (test != 0)) { return -1; //name doesn't match anything. } if (test < 0) { //name is in the upper part of the range. lower = index; } else if (test < 0) { //name is in the upper part of the range. upper = index; } } while (test != 0); return index; } /**Sort plug-ins array by name.*/ void SortPlugIns() { int index = 0; //Current element to sort. int index2 = 0; //Current element to test with. for (index = 1; index < ClassArray.size(); ++index) { index2 = index - 1; //Compare the current element (index) to the one at index2. //While the first one lexicographically precede the second one, it continues. while (((PlugInStorage) ClassArray.get(index)).Compare(((PlugInStorage) ClassArray.get(index2)).GetName()) < 0) { if (index2 == 0) { //Stop the loop if we compare with the first element. break; } index2 = index2 - 1; } //We remove it from its previous position... //And we use the returned object to put it to the new position. //(i.e. : Vector.remove(index) return the deleted object.) ClassArray.add(index2, ClassArray.remove(index)); } } }

Conclusion :


J'ai essayé de rendre ces classes le plus robuste possible, mais pour les personnes qui se le sentent, n'hésitez pas à m'informer des bugs afin de continuer à les améliorer.

Codes Sources

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.