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.16 2006/04/14 13:00:56 taqua 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 (isBootInProgress() || isBootDone()) {
158                    return;
159                }
160                this.bootInProgress = true;
161            }
162    
163            // boot dependent libraries ...
164            final BootableProjectInfo info = getProjectInfo();
165            if (info != null) {
166                Log.info (info.getName() + " " + info.getVersion());
167                final BootableProjectInfo[] childs = info.getDependencies();
168                for (int i = 0; i < childs.length; i++) {
169                    final AbstractBoot boot = loadBooter(childs[i].getBootClass());
170                    if (boot != null) {
171                        // that call is not synchronized, ...
172                        boot.start();
173                        // but we're waiting until the booting is complete ...
174                        synchronized(boot) {
175                          while (boot.isBootDone() == false) {
176                            try {
177                              boot.wait();
178                            }
179                            catch (InterruptedException e) {
180                              // ignore it ..
181                            }
182                          }
183                        }
184                    }
185                }
186            }
187            performBoot();
188    
189            synchronized (this) {
190                this.bootInProgress = false;
191                this.bootDone = true;
192                notifyAll();
193            }
194        }
195    
196        /**
197         * Performs the boot.
198         */
199        protected abstract void performBoot();
200    
201        /**
202         * Returns the project info.
203         *
204         * @return The project info.
205         */
206        protected abstract BootableProjectInfo getProjectInfo();
207    
208        /**
209         * Loads the specified booter implementation.
210         * 
211         * @param classname  the class name.
212         * 
213         * @return The boot class.
214         */
215        protected AbstractBoot loadBooter(final String classname) {
216            if (classname == null) {
217                return null;
218            }
219            try {
220                final Class c = ObjectUtilities.getClassLoader(
221                        getClass()).loadClass(classname);
222                final Method m = c.getMethod("getInstance", (Class[]) null);
223                return (AbstractBoot) m.invoke(null, (Object[]) null);
224            }
225            catch (Exception e) {
226                Log.info ("Unable to boot dependent class: " + classname);
227                return null;
228            }
229        }
230    
231        /**
232         * Creates a default configuration setup, which loads its settings from
233         * the static configuration (defaults provided by the developers of the
234         * library) and the user configuration (settings provided by the deployer).
235         * The deployer's settings override the developer's settings.
236         *
237         * If the parameter <code>addSysProps</code> is set to true, the system
238         * properties will be added as third configuration layer. The system 
239         * properties configuration allows to override all other settings.
240         *
241         * @param staticConfig the resource name of the developers configuration
242         * @param userConfig the resource name of the deployers configuration
243         * @param addSysProps a flag defining whether to include the system 
244         *                    properties into the configuration.
245         * @return the configured Configuration instance.
246         */
247        protected Configuration createDefaultHierarchicalConfiguration
248            (final String staticConfig, final String userConfig, 
249                    final boolean addSysProps)
250        {
251            final HierarchicalConfiguration globalConfig 
252                = new HierarchicalConfiguration();
253    
254            if (staticConfig != null) {
255              final PropertyFileConfiguration rootProperty 
256                  = new PropertyFileConfiguration();
257              rootProperty.load(staticConfig);
258              globalConfig.insertConfiguration(rootProperty);
259              globalConfig.insertConfiguration(
260                      getPackageManager().getPackageConfiguration());
261            }
262            if (userConfig != null)
263            {
264              String userConfigStripped;
265              if (userConfig.startsWith("/"))
266              {
267                userConfigStripped = userConfig.substring(1);
268              }
269              else
270              {
271                userConfigStripped = userConfig;
272              }
273              try
274              {
275                final Enumeration userConfigs = ObjectUtilities.getClassLoader
276                                (getClass()).getResources(userConfigStripped);
277                final ArrayList configs = new ArrayList();
278                while (userConfigs.hasMoreElements())
279                {
280                  final URL url = (URL) userConfigs.nextElement();
281                  try
282                  {
283                    final PropertyFileConfiguration baseProperty =
284                            new PropertyFileConfiguration();
285                    final InputStream in = url.openStream();
286                    baseProperty.load(in);
287                    in.close();
288                    configs.add(baseProperty);
289                  }
290                  catch(IOException ioe)
291                  {
292                    Log.warn ("Failed to load the user configuration at " + url, ioe);
293                  }
294                }
295    
296                for (int i = configs.size() - 1; i >= 0; i--)
297                {
298                  final PropertyFileConfiguration baseProperty =
299                          (PropertyFileConfiguration) configs.get(i);
300                  globalConfig.insertConfiguration(baseProperty);
301                }
302              }
303              catch (IOException e)
304              {
305                Log.warn ("Failed to lookup the user configurations.", e);
306              }
307            }
308            final SystemPropertyConfiguration systemConfig 
309                = new SystemPropertyConfiguration();
310            globalConfig.insertConfiguration(systemConfig);
311            return globalConfig;
312        }
313    
314        /**
315         * Returns the global configuration as extended configuration.
316         *
317         * @return the extended configuration.
318         */
319        public synchronized ExtendedConfiguration getExtendedConfig ()
320        {
321          if (extWrapper == null)
322          {
323            extWrapper = new ExtendedConfigurationWrapper(getGlobalConfig());
324          }
325          return extWrapper;
326        }
327    }