KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jfree > base > modules > PackageManager


1 /* ========================================================================
2  * JCommon : a free general purpose class library for the Java(tm) platform
3  * ========================================================================
4  *
5  * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
6  *
7  * Project Info: http://www.jfree.org/jcommon/index.html
8  *
9  * This library is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 of the License, or
12  * (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
17  * License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
22  * USA.
23  *
24  * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
25  * in the United States and other countries.]
26  *
27  * -------------------
28  * PackageManager.java
29  * -------------------
30  * (C)opyright 2003, 2004, by Thomas Morgner and Contributors.
31  *
32  * Original Author: Thomas Morgner;
33  * Contributor(s): David Gilbert (for Object Refinery Limited);
34  *
35  * $Id: PackageManager.java,v 1.10 2006/11/02 13:10:35 taqua Exp $
36  *
37  * Changes
38  * -------
39  * 26-Jun-2003 : Initial version
40  * 07-Jun-2004 : Added JCommon header (DG);
41  *
42  */

43
44 package org.jfree.base.modules;
45
46 import java.io.PrintStream JavaDoc;
47 import java.util.ArrayList JavaDoc;
48 import java.util.Arrays JavaDoc;
49 import java.util.HashMap JavaDoc;
50 import java.util.Iterator JavaDoc;
51
52 import org.jfree.base.AbstractBoot;
53 import org.jfree.base.config.HierarchicalConfiguration;
54 import org.jfree.base.config.PropertyFileConfiguration;
55 import org.jfree.base.log.PadMessage;
56 import org.jfree.util.Configuration;
57 import org.jfree.util.Log;
58 import org.jfree.util.ObjectUtilities;
59
60 /**
61  * The PackageManager is used to load and configure the modules of JFreeReport.
62  * Modules are used to extend the basic capabilities of JFreeReport by providing
63  * a simple plugin-interface.
64  * <p/>
65  * Modules provide a simple capability to remove unneeded functionality from the
66  * JFreeReport system and to reduce the overall code size. The modularisation provides
67  * a very strict way of removing unnecessary dependencies beween the various packages.
68  * <p/>
69  * The package manager can be used to add new modules to the system or to check
70  * the existence and state of installed modules.
71  *
72  * @author Thomas Morgner
73  */

74 public final class PackageManager {
75     /**
76      * The PackageConfiguration handles the module level configuration.
77      *
78      * @author Thomas Morgner
79      */

80     public static class PackageConfiguration extends PropertyFileConfiguration {
81         /**
82          * DefaultConstructor. Creates a new package configuration.
83          */

84         public PackageConfiguration() {
85             // nothing required
86
}
87
88         /**
89          * The new configuartion will be inserted into the list of report configuration,
90          * so that this configuration has the given report configuration instance as parent.
91          *
92          * @param config the new report configuration.
93          */

94         public void insertConfiguration(final HierarchicalConfiguration config) {
95             super.insertConfiguration(config);
96         }
97     }
98
99
100     /**
101      * An internal constant declaring that the specified module was already loaded.
102      */

103     private static final int RETURN_MODULE_LOADED = 0;
104     /**
105      * An internal constant declaring that the specified module is not known.
106      */

107     private static final int RETURN_MODULE_UNKNOWN = 1;
108     /**
109      * An internal constant declaring that the specified module produced an error while loading.
110      */

111     private static final int RETURN_MODULE_ERROR = 2;
112
113
114     /**
115      * The module configuration instance that should be used to store module
116      * properties. This separates the user defined properties from the implementation
117      * defined properties.
118      */

119     private final PackageConfiguration packageConfiguration;
120
121     /**
122      * A list of all defined modules.
123      */

124     private final ArrayList JavaDoc modules;
125     /**
126      * A list of module name definitions.
127      */

128     private final ArrayList JavaDoc initSections;
129
130     /** The boot implementation for which the modules are managed. */
131     private AbstractBoot booter;
132
133     /** The instances of all modules for all booters. */
134     private static HashMap JavaDoc instances;
135
136     /**
137      * Creates a package manager instance.
138      *
139      * @param booter the booter.
140      * @return A package manager.
141      */

142     public static PackageManager createInstance(final AbstractBoot booter) {
143         PackageManager manager;
144         if (instances == null) {
145             instances = new HashMap JavaDoc();
146             manager = new PackageManager(booter);
147             instances.put(booter, manager);
148             return manager;
149         }
150         manager = (PackageManager) instances.get(booter);
151         if (manager == null) {
152             manager = new PackageManager(booter);
153             instances.put(booter, manager);
154         }
155         return manager;
156     }
157
158     /**
159      * Creates a new package manager.
160      *
161      * @param booter the booter (<code>null</code> not permitted).
162      */

163     private PackageManager(final AbstractBoot booter) {
164         if (booter == null) {
165             throw new NullPointerException JavaDoc();
166         }
167         this.booter = booter;
168         this.packageConfiguration = new PackageConfiguration();
169         this.modules = new ArrayList JavaDoc();
170         this.initSections = new ArrayList JavaDoc();
171     }
172
173     /**
174      * Checks, whether a certain module is available.
175      *
176      * @param moduleDescription the module description of the desired module.
177      * @return true, if the module is available and the version of the module
178      * is compatible, false otherwise.
179      */

180     public boolean isModuleAvailable(final ModuleInfo moduleDescription) {
181         final PackageState[] packageStates =
182             (PackageState[]) this.modules.toArray(new PackageState[this.modules.size()]);
183         for (int i = 0; i < packageStates.length; i++) {
184             final PackageState state = packageStates[i];
185             if (state.getModule().getModuleClass().equals(moduleDescription.getModuleClass())) {
186                 return (state.getState() == PackageState.STATE_INITIALIZED);
187             }
188         }
189         return false;
190     }
191
192     /**
193      * Loads all modules mentioned in the report configuration starting with
194      * the given prefix. This method is used during the boot process of
195      * JFreeReport. You should never need to call this method directly.
196      *
197      * @param modulePrefix the module prefix.
198      */

199     public void load(final String JavaDoc modulePrefix) {
200         if (this.initSections.contains(modulePrefix)) {
201             return;
202         }
203         this.initSections.add(modulePrefix);
204
205         final Configuration config = this.booter.getGlobalConfig();
206         final Iterator JavaDoc it = config.findPropertyKeys(modulePrefix);
207         int count = 0;
208         while (it.hasNext()) {
209             final String JavaDoc key = (String JavaDoc) it.next();
210             if (key.endsWith(".Module")) {
211                 final String JavaDoc moduleClass = config.getConfigProperty(key);
212                 if (moduleClass != null && moduleClass.length() > 0) {
213                     addModule(moduleClass);
214                     count++;
215                 }
216             }
217         }
218         Log.debug("Loaded a total of " + count + " modules under prefix: " + modulePrefix);
219     }
220
221     /**
222      * Initializes all previously uninitialized modules. Once a module is initialized,
223      * it is not re-initialized a second time.
224      */

225     public synchronized void initializeModules() {
226         // sort by subsystems and dependency
227
PackageSorter.sort(this.modules);
228
229         for (int i = 0; i < this.modules.size(); i++) {
230             final PackageState mod = (PackageState) this.modules.get(i);
231             if (mod.configure(this.booter)) {
232                 Log.debug(new Log.SimpleMessage("Conf: ",
233                     new PadMessage(mod.getModule().getModuleClass(), 70),
234                     " [", mod.getModule().getSubSystem(), "]"));
235             }
236         }
237
238         for (int i = 0; i < this.modules.size(); i++) {
239             final PackageState mod = (PackageState) this.modules.get(i);
240             if (mod.initialize(this.booter)) {
241                 Log.debug(new Log.SimpleMessage("Init: ",
242                     new PadMessage(mod.getModule().getModuleClass(), 70),
243                     " [", mod.getModule().getSubSystem(), "]"));
244             }
245         }
246     }
247
248     /**
249      * Adds a module to the package manager.
250      * Once all modules are added, you have to call initializeModules()
251      * to configure and initialize the new modules.
252      *
253      * @param modClass the module class
254      */

255     public synchronized void addModule(final String JavaDoc modClass) {
256         final ArrayList JavaDoc loadModules = new ArrayList JavaDoc();
257         final ModuleInfo modInfo = new DefaultModuleInfo
258             (modClass, null, null, null);
259         if (loadModule(modInfo, new ArrayList JavaDoc(), loadModules, false)) {
260             for (int i = 0; i < loadModules.size(); i++) {
261                 final Module mod = (Module) loadModules.get(i);
262                 this.modules.add(new PackageState(mod));
263             }
264         }
265     }
266
267     /**
268      * Checks, whether the given module is already loaded in either the given
269      * tempModules list or the global package registry. If tmpModules is null,
270      * only the previously installed modules are checked.
271      *
272      * @param tempModules a list of previously loaded modules.
273      * @param module the module specification that is checked.
274      * @return true, if the module is already loaded, false otherwise.
275      */

276     private int containsModule(final ArrayList JavaDoc tempModules, final ModuleInfo module) {
277         if (tempModules != null) {
278             final ModuleInfo[] mods = (ModuleInfo[])
279                 tempModules.toArray(new ModuleInfo[tempModules.size()]);
280             for (int i = 0; i < mods.length; i++) {
281                 if (mods[i].getModuleClass().equals(module.getModuleClass())) {
282                     return RETURN_MODULE_LOADED;
283                 }
284             }
285         }
286
287         final PackageState[] packageStates =
288             (PackageState[]) this.modules.toArray(new PackageState[this.modules.size()]);
289         for (int i = 0; i < packageStates.length; i++) {
290             if (packageStates[i].getModule().getModuleClass().equals(module.getModuleClass())) {
291                 if (packageStates[i].getState() == PackageState.STATE_ERROR) {
292                     return RETURN_MODULE_ERROR;
293                 }
294                 else {
295                     return RETURN_MODULE_LOADED;
296                 }
297             }
298         }
299         return RETURN_MODULE_UNKNOWN;
300     }
301
302     /**
303      * A utility method that collects all failed modules. Such an module caused
304      * an error while being loaded, and is now cached in case it is referenced
305      * elsewhere.
306      *
307      * @param state the failed module.
308      */

309     private void dropFailedModule(final PackageState state) {
310         if (this.modules.contains(state) == false) {
311             this.modules.add(state);
312         }
313     }
314
315     /**
316      * Tries to load a given module and all dependent modules. If the dependency check
317      * fails for that module (or for one of the dependent modules), the loaded modules
318      * are discarded and no action is taken.
319      *
320      * @param moduleInfo the module info of the module that should be loaded.
321      * @param incompleteModules a list of incompletly loaded modules. This are module
322      * specifications which depend on the current module and wait for the module to
323      * be completly loaded.
324      * @param modules the list of previously loaded modules for this module.
325      * @param fatal a flag that states, whether the failure of loading a module should
326      * be considered an error. Root-modules load errors are never fatal, as we try
327      * to load all known modules, regardless whether they are active or not.
328      * @return true, if the module was loaded successfully, false otherwise.
329      */

330     private boolean loadModule(final ModuleInfo moduleInfo, final ArrayList JavaDoc incompleteModules,
331                                final ArrayList JavaDoc modules, final boolean fatal) {
332         try {
333
334             final Class JavaDoc c = ObjectUtilities.getClassLoader(getClass()).loadClass(moduleInfo.getModuleClass());
335             final Module module = (Module) c.newInstance();
336
337             if (acceptVersion(moduleInfo, module) == false) {
338                 // module conflict!
339
Log.warn("Module " + module.getName() + ": required version: "
340                         + moduleInfo + ", but found Version: \n" + module);
341                 final PackageState state = new PackageState(module, PackageState.STATE_ERROR);
342                 dropFailedModule(state);
343                 return false;
344             }
345
346             final int moduleContained = containsModule(modules, module);
347             if (moduleContained == RETURN_MODULE_ERROR) {
348                 // the module caused harm before ...
349
Log.debug("Indicated failure for module: " + module.getModuleClass());
350                 final PackageState state = new PackageState(module, PackageState.STATE_ERROR);
351                 dropFailedModule(state);
352                 return false;
353             }
354             else if (moduleContained == RETURN_MODULE_UNKNOWN) {
355                 if (incompleteModules.contains(module)) {
356                     // we assume that loading will continue ...
357
Log.error(new Log.SimpleMessage
358                         ("Circular module reference: This module definition is invalid: ",
359                             module.getClass()));
360                     final PackageState state = new PackageState(module, PackageState.STATE_ERROR);
361                     dropFailedModule(state);
362                     return false;
363                 }
364                 incompleteModules.add(module);
365                 final ModuleInfo[] required = module.getRequiredModules();
366                 for (int i = 0; i < required.length; i++) {
367                     if (loadModule(required[i], incompleteModules, modules, true) == false) {
368                         Log.debug("Indicated failure for module: " + module.getModuleClass());
369                         final PackageState state = new PackageState(module, PackageState.STATE_ERROR);
370                         dropFailedModule(state);
371                         return false;
372                     }
373                 }
374
375                 final ModuleInfo[] optional = module.getOptionalModules();
376                 for (int i = 0; i < optional.length; i++) {
377                     if (loadModule(optional[i], incompleteModules, modules, true) == false) {
378                         Log.debug(new Log.SimpleMessage("Optional module: ",
379                             optional[i].getModuleClass(), " was not loaded."));
380                     }
381                 }
382                 // maybe a dependent module defined the same base module ...
383
if (containsModule(modules, module) == RETURN_MODULE_UNKNOWN) {
384                     modules.add(module);
385                 }
386                 incompleteModules.remove(module);
387             }
388             return true;
389         }
390         catch (ClassNotFoundException JavaDoc cnfe) {
391             if (fatal) {
392                 Log.warn(new Log.SimpleMessage
393                     ("Unresolved dependency for package: ", moduleInfo.getModuleClass()));
394             }
395             Log.debug(new Log.SimpleMessage("ClassNotFound: ", cnfe.getMessage()));
396             return false;
397         }
398         catch (Exception JavaDoc e) {
399             Log.warn(new Log.SimpleMessage("Exception while loading module: ", moduleInfo), e);
400             return false;
401         }
402     }
403
404     /**
405      * Checks, whether the given module meets the requirements defined in the module
406      * information.
407      *
408      * @param moduleRequirement the required module specification.
409      * @param module the module that should be checked against the specification.
410      * @return true, if the module meets the given specifications, false otherwise.
411      */

412     private boolean acceptVersion(final ModuleInfo moduleRequirement, final Module module) {
413         if (moduleRequirement.getMajorVersion() == null) {
414             return true;
415         }
416         if (module.getMajorVersion() == null) {
417             Log.warn("Module " + module.getName() + " does not define a major version.");
418         }
419         else {
420             final int compare = acceptVersion(moduleRequirement.getMajorVersion(),
421                 module.getMajorVersion());
422             if (compare > 0) {
423                 return false;
424             }
425             else if (compare < 0) {
426                 return true;
427             }
428         }
429
430         if (moduleRequirement.getMinorVersion() == null) {
431             return true;
432         }
433         if (module.getMinorVersion() == null) {
434             Log.warn("Module " + module.getName() + " does not define a minor version.");
435         }
436         else {
437             final int compare = acceptVersion(moduleRequirement.getMinorVersion(),
438                 module.getMinorVersion());
439             if (compare > 0) {
440                 return false;
441             }
442             else if (compare < 0) {
443                 return true;
444             }
445         }
446
447         if (moduleRequirement.getPatchLevel() == null) {
448             return true;
449         }
450         if (module.getPatchLevel() == null) {
451             Log.debug("Module " + module.getName() + " does not define a patch level.");
452         }
453         else {
454             if (acceptVersion(moduleRequirement.getPatchLevel(),
455                 module.getPatchLevel()) > 0) {
456                 Log.debug("Did not accept patchlevel: "
457                         + moduleRequirement.getPatchLevel() + " - "
458                         + module.getPatchLevel());
459                 return false;
460             }
461         }
462         return true;
463
464     }
465
466     /**
467      * Compare the version strings. If the strings have a different length,
468      * the shorter string is padded with spaces to make them compareable.
469      *
470      * @param modVer the version string of the module
471      * @param depModVer the version string of the dependent or optional module
472      * @return 0, if the dependent module version is equal tothe module's required
473      * version, a negative number if the dependent module is newer or a positive
474      * number if the dependent module is older and does not fit.
475      */

476     private int acceptVersion(final String JavaDoc modVer, final String JavaDoc depModVer) {
477         final int mLength = Math.max(modVer.length(), depModVer.length());
478         final char[] modVerArray;
479         final char[] depVerArray;
480         if (modVer.length() > depModVer.length()) {
481             modVerArray = modVer.toCharArray();
482             depVerArray = new char[mLength];
483             final int delta = modVer.length() - depModVer.length();
484             Arrays.fill(depVerArray, 0, delta, ' ');
485             System.arraycopy(depVerArray, delta, depModVer.toCharArray(), 0, depModVer.length());
486         }
487         else if (modVer.length() < depModVer.length()) {
488             depVerArray = depModVer.toCharArray();
489             modVerArray = new char[mLength];
490             final char[] b1 = new char[mLength];
491             final int delta = depModVer.length() - modVer.length();
492             Arrays.fill(b1, 0, delta, ' ');
493             System.arraycopy(b1, delta, modVer.toCharArray(), 0, modVer.length());
494         }
495         else {
496             depVerArray = depModVer.toCharArray();
497             modVerArray = modVer.toCharArray();
498         }
499         return new String JavaDoc(modVerArray).compareTo(new String JavaDoc(depVerArray));
500     }
501
502     /**
503      * Returns the default package configuration. Private report configuration
504      * instances may be inserted here. These inserted configuration can never override
505      * the settings from this package configuration.
506      *
507      * @return the package configuration.
508      */

509     public PackageConfiguration getPackageConfiguration() {
510         return this.packageConfiguration;
511     }
512
513     /**
514      * Returns an array of the currently active modules. The module definition
515      * returned contain all known modules, including buggy and unconfigured
516      * instances.
517      *
518      * @return the modules.
519      */

520     public Module[] getAllModules() {
521         final Module[] mods = new Module[this.modules.size()];
522         for (int i = 0; i < this.modules.size(); i++) {
523             final PackageState state = (PackageState) this.modules.get(i);
524             mods[i] = state.getModule();
525         }
526         return mods;
527     }
528
529     /**
530      * Returns all active modules. This array does only contain modules
531      * which were successfully configured and initialized.
532      *
533      * @return the list of all active modules.
534      */

535     public Module[] getActiveModules() {
536         final ArrayList JavaDoc mods = new ArrayList JavaDoc();
537         for (int i = 0; i < this.modules.size(); i++) {
538             final PackageState state = (PackageState) this.modules.get(i);
539             if (state.getState() == PackageState.STATE_INITIALIZED) {
540                 mods.add(state.getModule());
541             }
542         }
543         return (Module[]) mods.toArray(new Module[mods.size()]);
544     }
545
546     /**
547      * Prints the modules that are used.
548      *
549      * @param p the print stream.
550      */

551     public void printUsedModules(final PrintStream JavaDoc p) {
552         final Module[] allMods = getAllModules();
553         final ArrayList JavaDoc activeModules = new ArrayList JavaDoc();
554         final ArrayList JavaDoc failedModules = new ArrayList JavaDoc();
555
556         for (int i = 0; i < allMods.length; i++) {
557             if (isModuleAvailable(allMods[i])) {
558                 activeModules.add(allMods[i]);
559             }
560             else {
561                 failedModules.add(allMods[i]);
562             }
563         }
564
565         p.print("Active modules: ");
566         p.println(activeModules.size());
567         p.println("----------------------------------------------------------");
568         for (int i = 0; i < activeModules.size(); i++) {
569             final Module mod = (Module) activeModules.get(i);
570             p.print(new PadMessage(mod.getModuleClass(), 70));
571             p.print(" [");
572             p.print(mod.getSubSystem());
573             p.println("]");
574             p.print(" Version: ");
575             p.print(mod.getMajorVersion());
576             p.print("-");
577             p.print(mod.getMinorVersion());
578             p.print("-");
579             p.print(mod.getPatchLevel());
580             p.print(" Producer: ");
581             p.println(mod.getProducer());
582             p.print(" Description: ");
583             p.println(mod.getDescription());
584         }
585     }
586 }
587
Popular Tags