    001/* ========================================================================
    002 * JCommon : a free general purpose class library for the Java(tm) platform
    003 * ========================================================================
    004 *
    005 * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
    006 *
    007 * Project Info:  http://www.jfree.org/jcommon/index.html
    008 *
    009 * This library is free software; you can redistribute it and/or modify it
    010 * under the terms of the GNU Lesser General Public License as published by
    011 * the Free Software Foundation; either version 2.1 of the License, or
    012 * (at your option) any later version.
    013 *
    014 * This library is distributed in the hope that it will be useful, but
    015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
    016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
    017 * License for more details.
    018 *
    019 * You should have received a copy of the GNU Lesser General Public
    020 * License along with this library; if not, write to the Free Software
    021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
    022 * USA.
    023 *
    024 * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
    025 * in the United States and other countries.]
    026 *
    027 * -----------------
    028 * AbstractBoot.java
    029 * -----------------
    030 * (C)opyright 2004, 2005, by Thomas Morgner and Contributors.
    031 *
    032 * Original Author:  Thomas Morgner;
    033 * Contributor(s):   David Gilbert (for Object Refinery Limited);
    034 *
    035 * $Id: AbstractBoot.java,v 1.21 2008/09/10 09:22:57 mungady Exp $
    036 *
    037 * Changes
    038 * -------
    039 * 07-Jun-2004 : Added source headers (DG);
    040 * 18-Aug-2005 : Added casts to suppress compiler warnings, as suggested in
    041 *               patch 1260622 (DG);
    042 *
    043 */
    044
    045package org.jfree.base;
    046
    047import java.io.IOException;
    048import java.io.InputStream;
    049import java.lang.reflect.Method;
    050import java.net.URL;
    051import java.util.ArrayList;
    052import java.util.Enumeration;
    053
    054import org.jfree.base.config.HierarchicalConfiguration;
    055import org.jfree.base.config.PropertyFileConfiguration;
    056import org.jfree.base.config.SystemPropertyConfiguration;
    057import org.jfree.base.modules.PackageManager;
    058import org.jfree.base.modules.SubSystem;
    059import org.jfree.util.Configuration;
    060import org.jfree.util.ExtendedConfiguration;
    061import org.jfree.util.ExtendedConfigurationWrapper;
    062import org.jfree.util.Log;
    063import org.jfree.util.ObjectUtilities;
    064
    065/**
    066 * The common base for all Boot classes.
    067 * <p>
    068 * This initializes the subsystem and all dependent subsystems.
    069 * Implementors of this class have to provide a public static
    070 * getInstance() method which returns a singleton instance of the
    071 * booter implementation.
    072 * <p>
    073 * Further creation of Boot object should be prevented using
    074 * protected or private constructors in that class, or proper
    075 * initialzation cannot be guaranteed.
    076 *
    077 * @author Thomas Morgner
    078 */
    079public abstract class AbstractBoot implements SubSystem {
    080
    081    /** The configuration wrapper around the plain configuration. */
    082    private ExtendedConfigurationWrapper extWrapper;
    083
    084    /** A packageManager instance of the package manager. */
    085    private PackageManager packageManager;
    086
    087    /** Global configuration. */
    088    private Configuration globalConfig;
    089
    090    /** A flag indicating whether the booting is currenly in progress. */
    091    private boolean bootInProgress;
    092
    093    /** A flag indicating whether the booting is complete. */
    094    private boolean bootDone;
    095
    096    /**
    097     * Default constructor.
    098     */
    099    protected AbstractBoot() {
    100    }
    101
    102    /**
    103     * Returns the packageManager instance of the package manager.
    104     *
    105     * @return The package manager.
    106     */
    107    public synchronized PackageManager getPackageManager() {
    108        if (this.packageManager == null) {
    109            this.packageManager = PackageManager.createInstance(this);
    110        }
    111        return this.packageManager;
    112    }
    113
    114    /**
    115     * Returns the global configuration.
    116     *
    117     * @return The global configuration.
    118     */
    119    public synchronized Configuration getGlobalConfig() {
    120        if (this.globalConfig == null) {
    121            this.globalConfig = loadConfiguration();
    122        }
    123        return this.globalConfig;
    124    }
    125
    126    /**
    127     * Checks, whether the booting is in progress.
    128     *
    129     * @return true, if the booting is in progress, false otherwise.
    130     */
    131    public final synchronized boolean isBootInProgress() {
    132        return this.bootInProgress;
    133    }
    134
    135    /**
    136     * Checks, whether the booting is complete.
    137     *
    138     * @return true, if the booting is complete, false otherwise.
    139     */
    140    public final synchronized boolean isBootDone() {
    141        return this.bootDone;
    142    }
    143
    144    /**
    145     * Loads the configuration. This will be called exactly once.
    146     *
    147     * @return The configuration.
    148     */
    149    protected abstract Configuration loadConfiguration();
    150
    151    /**
    152     * Starts the boot process.
    153     */
    154    public final void start() {
    155
    156        synchronized (this) {
    157            if (isBootDone()) {
    158                return;
    159            }
    160            while (isBootInProgress()) {
    161              try {
    162                wait();
    163              }
    164              catch (InterruptedException e) {
    165                // ignore ..
    166              }
    167            }
    168            if (isBootDone()) {
    169                return;
    170            }
    171            this.bootInProgress = true;
    172        }
    173
    174        // boot dependent libraries ...
    175        final BootableProjectInfo info = getProjectInfo();
    176        if (info != null) {
    177            final BootableProjectInfo[] childs = info.getDependencies();
    178            for (int i = 0; i < childs.length; i++) {
    179                final AbstractBoot boot = loadBooter(childs[i].getBootClass());
    180                if (boot != null) {
    181                    // but we're waiting until the booting is complete ...
    182                    synchronized(boot) {
    183                      boot.start();
    184                      while (boot.isBootDone() == false) {
    185                        try {
    186                          boot.wait();
    187                        }
    188                        catch (InterruptedException e) {
    189                          // ignore it ..
    190                        }
    191                      }
    192                    }
    193                }
    194            }
    195        }
    196
    197        performBoot();
    198        if (info != null)
    199        {
    200          Log.info (info.getName() + " " + info.getVersion() + " started.");
    201        }
    202        else
    203        {
    204          Log.info (getClass() + " started.");
    205        }
    206
    207        synchronized (this) {
    208            this.bootInProgress = false;
    209            this.bootDone = true;
    210            notifyAll();
    211        }
    212    }
    213
    214    /**
    215     * Performs the boot.
    216     */
    217    protected abstract void performBoot();
    218
    219    /**
    220     * Returns the project info.
    221     *
    222     * @return The project info.
    223     */
    224    protected abstract BootableProjectInfo getProjectInfo();
    225
    226    /**
    227     * Loads the specified booter implementation.
    228     *
    229     * @param classname  the class name.
    230     *
    231     * @return The boot class.
    232     */
    233    protected AbstractBoot loadBooter(final String classname) {
    234        if (classname == null) {
    235            return null;
    236        }
    237        try {
    238            final Class c = ObjectUtilities.getClassLoader(
    239                    getClass()).loadClass(classname);
    240            final Method m = c.getMethod("getInstance", (Class[]) null);
    241            return (AbstractBoot) m.invoke(null, (Object[]) null);
    242        }
    243        catch (Exception e) {
    244            Log.info ("Unable to boot dependent class: " + classname);
    245            return null;
    246        }
    247    }
    248
    249    /**
    250     * Creates a default configuration setup, which loads its settings from
    251     * the static configuration (defaults provided by the developers of the
    252     * library) and the user configuration (settings provided by the deployer).
    253     * The deployer's settings override the developer's settings.
    254     *
    255     * If the parameter <code>addSysProps</code> is set to true, the system
    256     * properties will be added as third configuration layer. The system
    257     * properties configuration allows to override all other settings.
    258     *
    259     * @param staticConfig the resource name of the developers configuration
    260     * @param userConfig the resource name of the deployers configuration
    261     * @param addSysProps a flag defining whether to include the system
    262     *                    properties into the configuration.
    263     * @return the configured Configuration instance.
    264     */
    265    protected Configuration createDefaultHierarchicalConfiguration
    266        (final String staticConfig, final String userConfig,
    267                final boolean addSysProps)
    268    {
    269      return createDefaultHierarchicalConfiguration
    270          (staticConfig, userConfig, addSysProps, PropertyFileConfiguration.class);
    271    }
    272
    273    /**
    274     * Creates a default hierarchical configuration.
    275     *
    276     * @param staticConfig  the static configuration.
    277     * @param userConfig  the user configuration.
    278     * @param addSysProps  additional system properties.
    279     * @param source  the source.
    280     *
    281     * @return The configuration.
    282     */
    283    protected Configuration createDefaultHierarchicalConfiguration
    284        (final String staticConfig, final String userConfig,
    285         final boolean addSysProps, final Class source)
    286    {
    287        final HierarchicalConfiguration globalConfig
    288            = new HierarchicalConfiguration();
    289
    290        if (staticConfig != null) {
    291          final PropertyFileConfiguration rootProperty
    292              = new PropertyFileConfiguration();
    293          rootProperty.load(staticConfig, getClass());
    294          globalConfig.insertConfiguration(rootProperty);
    295          globalConfig.insertConfiguration(
    296                  getPackageManager().getPackageConfiguration());
    297        }
    298        if (userConfig != null) {
    299          String userConfigStripped;
    300          if (userConfig.startsWith("/")) {
    301            userConfigStripped = userConfig.substring(1);
    302          }
    303          else {
    304            userConfigStripped = userConfig;
    305          }
    306          try {
    307            final Enumeration userConfigs = ObjectUtilities.getClassLoader
    308                            (getClass()).getResources(userConfigStripped);
    309            final ArrayList configs = new ArrayList();
    310            while (userConfigs.hasMoreElements()) {
    311              final URL url = (URL) userConfigs.nextElement();
    312              try {
    313                final PropertyFileConfiguration baseProperty =
    314                        new PropertyFileConfiguration();
    315                final InputStream in = url.openStream();
    316                baseProperty.load(in);
    317                in.close();
    318                configs.add(baseProperty);
    319              }
    320              catch(IOException ioe) {
    321                Log.warn ("Failed to load the user configuration at " + url, ioe);
    322              }
    323            }
    324
    325            for (int i = configs.size() - 1; i >= 0; i--) {
    326              final PropertyFileConfiguration baseProperty =
    327                      (PropertyFileConfiguration) configs.get(i);
    328              globalConfig.insertConfiguration(baseProperty);
    329            }
    330          }
    331          catch (IOException e) {
    332            Log.warn ("Failed to lookup the user configurations.", e);
    333          }
    334        }
    335        if (addSysProps) {
    336          final SystemPropertyConfiguration systemConfig
    337              = new SystemPropertyConfiguration();
    338          globalConfig.insertConfiguration(systemConfig);
    339        }
    340        return globalConfig;
    341    }
    342
    343    /**
    344     * Returns the global configuration as extended configuration.
    345     *
    346     * @return the extended configuration.
    347     */
    348    public synchronized ExtendedConfiguration getExtendedConfig ()
    349    {
    350      if (this.extWrapper == null) {
    351          this.extWrapper = new ExtendedConfigurationWrapper(getGlobalConfig());
    352      }
    353      return this.extWrapper;
    354    }
    355}