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    
045    package org.jfree.base;
046    
047    import java.io.IOException;
048    import java.io.InputStream;
049    import java.lang.reflect.Method;
050    import java.net.URL;
051    import java.util.ArrayList;
052    import java.util.Enumeration;
053    
054    import org.jfree.base.config.HierarchicalConfiguration;
055    import org.jfree.base.config.PropertyFileConfiguration;
056    import org.jfree.base.config.SystemPropertyConfiguration;
057    import org.jfree.base.modules.PackageManager;
058    import org.jfree.base.modules.SubSystem;
059    import org.jfree.util.Configuration;
060    import org.jfree.util.ExtendedConfiguration;
061    import org.jfree.util.ExtendedConfigurationWrapper;
062    import org.jfree.util.Log;
063    import 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     */
079    public 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    }