1
/* ========================================================================
2
* JCommon : a free general purpose class library for the Java(tm) platform
3
* ========================================================================
5
* (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
7
* Project Info: http://www.jfree.org/jcommon/index.html
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.
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.
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,
24
* [Java is a trademark or registered trademark of Sun Microsystems, Inc.
25
* in the United States and other countries.]
30
* (C)opyright 2003, 2004, by Thomas Morgner and Contributors.
32
* Original Author: Thomas Morgner;
33
* Contributor(s): David Gilbert (for Object Refinery Limited);
35
* $Id: PackageManager.java,v 1.8 2005/11/03 09:55:27 mungady Exp $
39
* 26-Jun-2003 : Initial version
40
* 07-Jun-2004 : Added JCommon header (DG);
44
package org.jfree.base.modules;
46
import java.io.PrintStream;
47
import java.util.ArrayList;
48
import java.util.Arrays;
49
import java.util.HashMap;
50
import java.util.Iterator;
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;
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.
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.
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.
72
* @author Thomas Morgner
74
public final class PackageManager {
76
* The PackageConfiguration handles the module level configuration.
78
* @author Thomas Morgner
80
public static class PackageConfiguration extends PropertyFileConfiguration {
82
* DefaultConstructor. Creates a new package configuration.
84
public PackageConfiguration() {
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.
92
* @param config the new report configuration.
94
public void insertConfiguration(final HierarchicalConfiguration config) {
95
super.insertConfiguration(config);
101
* An internal constant declaring that the specified module was already loaded.
103
private static final int RETURN_MODULE_LOADED = 0;
105
* An internal constant declaring that the specified module is not known.
107
private static final int RETURN_MODULE_UNKNOWN = 1;
109
* An internal constant declaring that the specified module produced an error while loading.
111
private static final int RETURN_MODULE_ERROR = 2;
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.
119
private final PackageConfiguration packageConfiguration;
122
* A list of all defined modules.
124
private final ArrayList modules;
126
* A list of module name definitions.
128
private final ArrayList initSections;
130
/** The boot implementation for which the modules are managed. */
131
private AbstractBoot booter;
133
/** The instances of all modules for all booters. */
134
private static HashMap instances;
137
* Creates a package manager instance.
139
* @param booter the booter.
140
* @return A package manager.
142
public static PackageManager createInstance(final AbstractBoot booter) {
143
PackageManager manager;
144
if (instances == null) {
145
instances = new HashMap();
146
manager = new PackageManager(booter);
147
instances.put(booter, manager);
150
manager = (PackageManager) instances.get(booter);
151
if (manager == null) {
152
manager = new PackageManager(booter);
153
instances.put(booter, manager);
159
* Creates a new package manager.
161
* @param booter the booter (<code>null</code> not permitted).
163
private PackageManager(final AbstractBoot booter) {
164
if (booter == null) {
165
throw new NullPointerException();
167
this.booter = booter;
168
this.packageConfiguration = new PackageConfiguration();
169
this.modules = new ArrayList();
170
this.initSections = new ArrayList();
174
* Checks, whether a certain module is available.
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.
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);
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.
197
* @param modulePrefix the module prefix.
199
public void load(final String modulePrefix) {
200
if (this.initSections.contains(modulePrefix)) {
203
this.initSections.add(modulePrefix);
205
final Configuration config = this.booter.getGlobalConfig();
206
final Iterator it = config.findPropertyKeys(modulePrefix);
207
while (it.hasNext()) {
208
final String key = (String) it.next();
209
if (key.endsWith(".Module")) {
210
addModule(config.getConfigProperty(key));
213
Log.debug("Loaded a total of " + this.modules.size() + " modules under prefix: " + modulePrefix);
217
* Initializes all previously uninitialized modules. Once a module is initialized,
218
* it is not re-initialized a second time.
220
public synchronized void initializeModules() {
221
// sort by subsystems and dependency
222
PackageSorter.sort(this.modules);
224
for (int i = 0; i < this.modules.size(); i++) {
225
final PackageState mod = (PackageState) this.modules.get(i);
226
if (mod.configure(this.booter)) {
227
Log.debug(new Log.SimpleMessage("Conf: ",
228
new PadMessage(mod.getModule().getModuleClass(), 70),
229
" [", mod.getModule().getSubSystem(), "]"));
233
for (int i = 0; i < this.modules.size(); i++) {
234
final PackageState mod = (PackageState) this.modules.get(i);
235
if (mod.initialize(this.booter)) {
236
Log.debug(new Log.SimpleMessage("Init: ",
237
new PadMessage(mod.getModule().getModuleClass(), 70),
238
" [", mod.getModule().getSubSystem(), "]"));
244
* Adds a module to the package manager.
245
* Once all modules are added, you have to call initializeModules()
246
* to configure and initialize the new modules.
248
* @param modClass the module class
250
public synchronized void addModule(final String modClass) {
251
final ArrayList loadModules = new ArrayList();
252
final ModuleInfo modInfo = new DefaultModuleInfo
253
(modClass, null, null, null);
254
if (loadModule(modInfo, new ArrayList(), loadModules, false)) {
255
for (int i = 0; i < loadModules.size(); i++) {
256
final Module mod = (Module) loadModules.get(i);
257
this.modules.add(new PackageState(mod));
263
* Checks, whether the given module is already loaded in either the given
264
* tempModules list or the global package registry. If tmpModules is null,
265
* only the previously installed modules are checked.
267
* @param tempModules a list of previously loaded modules.
268
* @param module the module specification that is checked.
269
* @return true, if the module is already loaded, false otherwise.
271
private int containsModule(final ArrayList tempModules, final ModuleInfo module) {
272
if (tempModules != null) {
273
final ModuleInfo[] mods = (ModuleInfo[])
274
tempModules.toArray(new ModuleInfo[tempModules.size()]);
275
for (int i = 0; i < mods.length; i++) {
276
if (mods[i].getModuleClass().equals(module.getModuleClass())) {
277
return RETURN_MODULE_LOADED;
282
final PackageState[] packageStates =
283
(PackageState[]) this.modules.toArray(new PackageState[this.modules.size()]);
284
for (int i = 0; i < packageStates.length; i++) {
285
if (packageStates[i].getModule().getModuleClass().equals(module.getModuleClass())) {
286
if (packageStates[i].getState() == PackageState.STATE_ERROR) {
287
return RETURN_MODULE_ERROR;
290
return RETURN_MODULE_LOADED;
294
return RETURN_MODULE_UNKNOWN;
298
* A utility method that collects all failed modules. Such an module caused
299
* an error while being loaded, and is now cached in case it is referenced
302
* @param state the failed module.
304
private void dropFailedModule(final PackageState state) {
305
if (this.modules.contains(state) == false) {
306
this.modules.add(state);
311
* Tries to load a given module and all dependent modules. If the dependency check
312
* fails for that module (or for one of the dependent modules), the loaded modules
313
* are discarded and no action is taken.
315
* @param moduleInfo the module info of the module that should be loaded.
316
* @param incompleteModules a list of incompletly loaded modules. This are module
317
* specifications which depend on the current module and wait for the module to
318
* be completly loaded.
319
* @param modules the list of previously loaded modules for this module.
320
* @param fatal a flag that states, whether the failure of loading a module should
321
* be considered an error. Root-modules load errors are never fatal, as we try
322
* to load all known modules, regardless whether they are active or not.
323
* @return true, if the module was loaded successfully, false otherwise.
325
private boolean loadModule(final ModuleInfo moduleInfo, final ArrayList incompleteModules,
326
final ArrayList modules, final boolean fatal) {
329
final Class c = ObjectUtilities.getClassLoader(getClass()).loadClass(moduleInfo.getModuleClass());
330
final Module module = (Module) c.newInstance();
332
if (acceptVersion(moduleInfo, module) == false) {
334
Log.warn("Module " + module.getName() + ": required version: "
335
+ moduleInfo + ", but found Version: \n" + module);
336
final PackageState state = new PackageState(module, PackageState.STATE_ERROR);
337
dropFailedModule(state);
341
final int moduleContained = containsModule(modules, module);
342
if (moduleContained == RETURN_MODULE_ERROR) {
343
// the module caused harm before ...
344
Log.debug("Indicated failure for module: " + module.getModuleClass());
345
final PackageState state = new PackageState(module, PackageState.STATE_ERROR);
346
dropFailedModule(state);
349
else if (moduleContained == RETURN_MODULE_UNKNOWN) {
350
if (incompleteModules.contains(module)) {
351
// we assume that loading will continue ...
352
Log.error(new Log.SimpleMessage
353
("Circular module reference: This module definition is invalid: ",
355
final PackageState state = new PackageState(module, PackageState.STATE_ERROR);
356
dropFailedModule(state);
359
incompleteModules.add(module);
360
final ModuleInfo[] required = module.getRequiredModules();
361
for (int i = 0; i < required.length; i++) {
362
if (loadModule(required[i], incompleteModules, modules, true) == false) {
363
Log.debug("Indicated failure for module: " + module.getModuleClass());
364
final PackageState state = new PackageState(module, PackageState.STATE_ERROR);
365
dropFailedModule(state);
370
final ModuleInfo[] optional = module.getOptionalModules();
371
for (int i = 0; i < optional.length; i++) {
372
if (loadModule(optional[i], incompleteModules, modules, true) == false) {
373
Log.debug(new Log.SimpleMessage("Optional module: ",
374
optional[i].getModuleClass(), " was not loaded."));
377
// maybe a dependent module defined the same base module ...
378
if (containsModule(modules, module) == RETURN_MODULE_UNKNOWN) {
381
incompleteModules.remove(module);
385
catch (ClassNotFoundException cnfe) {
387
Log.warn(new Log.SimpleMessage
388
("Unresolved dependency for package: ", moduleInfo.getModuleClass()));
390
Log.debug(new Log.SimpleMessage("ClassNotFound: ", cnfe.getMessage()));
393
catch (Exception e) {
394
Log.warn(new Log.SimpleMessage("Exception while loading module: ", moduleInfo), e);
400
* Checks, whether the given module meets the requirements defined in the module
403
* @param moduleRequirement the required module specification.
404
* @param module the module that should be checked against the specification.
405
* @return true, if the module meets the given specifications, false otherwise.
407
private boolean acceptVersion(final ModuleInfo moduleRequirement, final Module module) {
408
if (moduleRequirement.getMajorVersion() == null) {
411
if (module.getMajorVersion() == null) {
412
Log.warn("Module " + module.getName() + " does not define a major version.");
415
final int compare = acceptVersion(moduleRequirement.getMajorVersion(),
416
module.getMajorVersion());
420
else if (compare < 0) {
425
if (moduleRequirement.getMinorVersion() == null) {
428
if (module.getMinorVersion() == null) {
429
Log.warn("Module " + module.getName() + " does not define a minor version.");
432
final int compare = acceptVersion(moduleRequirement.getMinorVersion(),
433
module.getMinorVersion());
437
else if (compare < 0) {
442
if (moduleRequirement.getPatchLevel() == null) {
445
if (module.getPatchLevel() == null) {
446
Log.debug("Module " + module.getName() + " does not define a patch level.");
449
if (acceptVersion(moduleRequirement.getPatchLevel(),
450
module.getPatchLevel()) > 0) {
451
Log.debug("Did not accept patchlevel: "
452
+ moduleRequirement.getPatchLevel() + " - "
453
+ module.getPatchLevel());
462
* Compare the version strings. If the strings have a different length,
463
* the shorter string is padded with spaces to make them compareable.
465
* @param modVer the version string of the module
466
* @param depModVer the version string of the dependent or optional module
467
* @return 0, if the dependent module version is equal tothe module's required
468
* version, a negative number if the dependent module is newer or a positive
469
* number if the dependent module is older and does not fit.
471
private int acceptVersion(final String modVer, final String depModVer) {
472
final int mLength = Math.max(modVer.length(), depModVer.length());
473
final char[] modVerArray;
474
final char[] depVerArray;
475
if (modVer.length() > depModVer.length()) {
476
modVerArray = modVer.toCharArray();
477
depVerArray = new char[mLength];
478
final int delta = modVer.length() - depModVer.length();
479
Arrays.fill(depVerArray, 0, delta, ' ');
480
System.arraycopy(depVerArray, delta, depModVer.toCharArray(), 0, depModVer.length());
482
else if (modVer.length() < depModVer.length()) {
483
depVerArray = depModVer.toCharArray();
484
modVerArray = new char[mLength];
485
final char[] b1 = new char[mLength];
486
final int delta = depModVer.length() - modVer.length();
487
Arrays.fill(b1, 0, delta, ' ');
488
System.arraycopy(b1, delta, modVer.toCharArray(), 0, modVer.length());
491
depVerArray = depModVer.toCharArray();
492
modVerArray = modVer.toCharArray();
494
return new String(modVerArray).compareTo(new String(depVerArray));
498
* Returns the default package configuration. Private report configuration
499
* instances may be inserted here. These inserted configuration can never override
500
* the settings from this package configuration.
502
* @return the package configuration.
504
public PackageConfiguration getPackageConfiguration() {
505
return this.packageConfiguration;
509
* Returns an array of the currently active modules. The module definition
510
* returned contain all known modules, including buggy and unconfigured
513
* @return the modules.
515
public Module[] getAllModules() {
516
final Module[] mods = new Module[this.modules.size()];
517
for (int i = 0; i < this.modules.size(); i++) {
518
final PackageState state = (PackageState) this.modules.get(i);
519
mods[i] = state.getModule();
525
* Returns all active modules. This array does only contain modules
526
* which were successfully configured and initialized.
528
* @return the list of all active modules.
530
public Module[] getActiveModules() {
531
final ArrayList mods = new ArrayList();
532
for (int i = 0; i < this.modules.size(); i++) {
533
final PackageState state = (PackageState) this.modules.get(i);
534
if (state.getState() == PackageState.STATE_INITIALIZED) {
535
mods.add(state.getModule());
538
return (Module[]) mods.toArray(new Module[mods.size()]);
542
* Prints the modules that are used.
544
* @param p the print stream.
546
public void printUsedModules(final PrintStream p) {
547
final Module[] allMods = getAllModules();
548
final ArrayList activeModules = new ArrayList();
549
final ArrayList failedModules = new ArrayList();
551
for (int i = 0; i < allMods.length; i++) {
552
if (isModuleAvailable(allMods[i])) {
553
activeModules.add(allMods[i]);
556
failedModules.add(allMods[i]);
560
p.print("Active modules: ");
561
p.println(activeModules.size());
562
p.println("----------------------------------------------------------");
563
for (int i = 0; i < activeModules.size(); i++) {
564
final Module mod = (Module) activeModules.get(i);
565
p.print(new PadMessage(mod.getModuleClass(), 70));
567
p.print(mod.getSubSystem());
569
p.print(" Version: ");
570
p.print(mod.getMajorVersion());
572
p.print(mod.getMinorVersion());
574
p.print(mod.getPatchLevel());
575
p.print(" Producer: ");
576
p.println(mod.getProducer());
577
p.print(" Description: ");
578
p.println(mod.getDescription());