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}