001    /**
002     * ========================================
003     * JFreeReport : a free Java report library
004     * ========================================
005     *
006     * Project Info:  http://reporting.pentaho.org/
007     *
008     * (C) Copyright 2000-2007, by Object Refinery Limited, Pentaho Corporation and Contributors.
009     *
010     * This library is free software; you can redistribute it and/or modify it under the terms
011     * of the GNU Lesser General Public License as published by the Free Software Foundation;
012     * either version 2.1 of the License, or (at your option) any later version.
013     *
014     * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
015     * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
016     * See the GNU Lesser General Public License for more details.
017     *
018     * You should have received a copy of the GNU Lesser General Public License along with this
019     * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
020     * Boston, MA 02111-1307, USA.
021     *
022     * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
023     * in the United States and other countries.]
024     *
025     * ------------
026     * $Id: JFreeReportBoot.java,v 1.15 2007/04/01 18:49:23 taqua Exp $
027     * ------------
028     * (C) Copyright 2000-2005, by Object Refinery Limited.
029     * (C) Copyright 2005-2007, by Pentaho Corporation.
030     */
031    package org.jfree.report;
032    
033    import java.util.Enumeration;
034    
035    import org.jfree.base.AbstractBoot;
036    import org.jfree.base.BaseBoot;
037    import org.jfree.base.BootableProjectInfo;
038    import org.jfree.base.config.HierarchicalConfiguration;
039    import org.jfree.base.config.ModifiableConfiguration;
040    import org.jfree.base.config.PropertyFileConfiguration;
041    import org.jfree.base.config.SystemPropertyConfiguration;
042    import org.jfree.base.log.DefaultLog;
043    import org.jfree.base.modules.PackageManager;
044    import org.jfree.report.util.CSVTokenizer;
045    import org.jfree.util.Configuration;
046    import org.jfree.util.Log;
047    
048    /**
049     * An utility class to safely boot and initialize the JFreeReport library. This class
050     * should be called before using the JFreeReport classes, to make sure that all subsystems
051     * are initialized correctly and in the correct order.
052     * <p/>
053     * Application developers should make sure, that the booting is done, before JFreeReport
054     * objects are used. Although the boot process will be started automaticly if needed, this
055     * automated start may no longer guarantee the module initialization order.
056     * <p/>
057     * Additional modules can be specified by defining the system property
058     * <code>"org.jfree.report.boot.Modules"</code>. The property expects a comma-separated
059     * list of {@link org.jfree.base.modules.Module} implementations.
060     * <p/>
061     * Booting should be done by aquirering a new boot instance using {@link
062     * JFreeReportBoot#getInstance()} and then starting the boot process with {@link
063     * JFreeReportBoot#start()}.
064     *
065     * @author Thomas Morgner
066     */
067    public class JFreeReportBoot extends AbstractBoot
068    {
069      /**
070       * A wrappper around the user supplied global configuration.
071       */
072      private static class UserConfigWrapper extends HierarchicalConfiguration
073              implements Configuration
074      {
075        /** The wrapped configuration. */
076        private Configuration wrappedConfiguration;
077    
078        /**
079         * Default constructor.
080         */
081        public UserConfigWrapper ()
082        {
083          this (null);
084        }
085    
086        public UserConfigWrapper (Configuration config)
087        {
088          this.wrappedConfiguration = config;
089        }
090        /**
091         * Sets a new configuration. This configuration will be inserted into the
092         * report configuration hierarchy. Set this property to null to disable
093         * the user defined configuration.
094         *
095         * @param wrappedConfiguration the wrapped configuration.
096         */
097        public void setWrappedConfiguration (final Configuration wrappedConfiguration)
098        {
099          this.wrappedConfiguration = wrappedConfiguration;
100        }
101    
102        /**
103         * Returns the user supplied global configuration, if exists.
104         *
105         * @return the user configuration.
106         */
107        public Configuration getWrappedConfiguration ()
108        {
109          return wrappedConfiguration;
110        }
111    
112        /**
113         * Returns the configuration property with the specified key.
114         *
115         * @param key the property key.
116         * @return the property value.
117         */
118        public String getConfigProperty (final String key)
119        {
120          if (wrappedConfiguration == null)
121          {
122            return getParentConfig().getConfigProperty(key);
123          }
124    
125          final String retval = wrappedConfiguration.getConfigProperty(key);
126          if (retval != null)
127          {
128            return retval;
129          }
130          return getParentConfig().getConfigProperty(key);
131        }
132    
133        /**
134         * Returns the configuration property with the specified key
135         * (or the specified default value if there is no such property).
136         * <p/>
137         * If the property is not defined in this configuration, the code
138         * will lookup the property in the parent configuration.
139         *
140         * @param key          the property key.
141         * @param defaultValue the default value.
142         * @return the property value.
143         */
144        public String getConfigProperty (final String key, final String defaultValue)
145        {
146          if (wrappedConfiguration == null)
147          {
148            return getParentConfig().getConfigProperty(key, defaultValue);
149          }
150    
151          final String retval = wrappedConfiguration.getConfigProperty(key, null);
152          if (retval != null)
153          {
154            return retval;
155          }
156          return getParentConfig().getConfigProperty(key, defaultValue);
157        }
158    
159        /**
160         * Sets a configuration property.
161         *
162         * @param key   the property key.
163         * @param value the property value.
164         */
165        public void setConfigProperty (final String key, final String value)
166        {
167          if (wrappedConfiguration instanceof ModifiableConfiguration)
168          {
169            final ModifiableConfiguration modConfiguration =
170                    (ModifiableConfiguration) wrappedConfiguration;
171            modConfiguration.setConfigProperty(key, value);
172          }
173        }
174    
175        /**
176         * Returns all defined configuration properties for the report. The enumeration
177         * contains all keys of the changed properties, properties set from files or
178         * the system properties are not included.
179         *
180         * @return all defined configuration properties for the report.
181         */
182        public Enumeration getConfigProperties ()
183        {
184          if (wrappedConfiguration instanceof ModifiableConfiguration)
185          {
186            final ModifiableConfiguration modConfiguration =
187                    (ModifiableConfiguration) wrappedConfiguration;
188            return modConfiguration.getConfigProperties();
189          }
190          return super.getConfigProperties();
191        }
192      }
193    
194      /**
195       * The singleton instance of the Boot class.
196       */
197      private static JFreeReportBoot instance;
198      /**
199       * The project info contains all meta data about the project.
200       */
201      private BootableProjectInfo projectInfo;
202    
203      /**
204       * Holds a possibly empty reference to a user-supplied Configuration
205       * implementation.
206       */
207      private static transient UserConfigWrapper configWrapper =
208              new UserConfigWrapper();
209    
210      /**
211       * Creates a new instance.
212       */
213      private JFreeReportBoot ()
214      {
215        projectInfo = JFreeReportInfo.getInstance();
216      }
217    
218      /**
219       * Returns the singleton instance of the boot utility class.
220       *
221       * @return the boot instance.
222       */
223      public static synchronized JFreeReportBoot getInstance ()
224      {
225        if (instance == null)
226        {
227          // make sure that I am able to debug the package manager ..
228          DefaultLog.installDefaultLog();
229          instance = new JFreeReportBoot();
230    
231          HierarchicalConfiguration hc = (HierarchicalConfiguration) BaseBoot.getConfiguration();
232          hc.insertConfiguration(new UserConfigWrapper(instance.getGlobalConfig()));
233        }
234        return instance;
235      }
236    
237      /**
238       * Returns the current global configuration as modifiable instance. This
239       * is exactly the same as casting the global configuration into a
240       * ModifableConfiguration instance.
241       * <p/>
242       * This is a convinience function, as all programmers are lazy.
243       *
244       * @return the global config as modifiable configuration.
245       */
246      public ModifiableConfiguration getEditableConfig()
247      {
248        return (ModifiableConfiguration) getGlobalConfig();
249      }
250    
251      /**
252       * Returns the project info.
253       *
254       * @return The project info.
255       */
256      protected BootableProjectInfo getProjectInfo ()
257      {
258        return projectInfo;
259      }
260    
261      /**
262       * Loads the configuration. This will be called exactly once.
263       *
264       * @return The configuration.
265       */
266      protected Configuration loadConfiguration ()
267      {
268        HierarchicalConfiguration globalConfig = new HierarchicalConfiguration();
269    
270        final PropertyFileConfiguration rootProperty = new PropertyFileConfiguration();
271        rootProperty.load("/org/jfree/report/jfreereport.properties");
272        rootProperty.load("/org/jfree/report/ext/jfreereport-ext.properties");
273        globalConfig.insertConfiguration(rootProperty);
274        globalConfig.insertConfiguration(JFreeReportBoot.getInstance().getPackageManager()
275                .getPackageConfiguration());
276    
277        final PropertyFileConfiguration baseProperty = new PropertyFileConfiguration();
278        baseProperty.load("/jfreereport.properties");
279        globalConfig.insertConfiguration(baseProperty);
280    
281        globalConfig.insertConfiguration(configWrapper);
282    
283        final SystemPropertyConfiguration systemConfig = new SystemPropertyConfiguration();
284        globalConfig.insertConfiguration(systemConfig);
285        return globalConfig;
286      }
287    
288      /**
289       * Performs the actual boot process.
290       */
291      protected void performBoot ()
292      {
293        // Inject JFreeReport's configuration into jcommon.
294        // make sure logging is re-initialized after we injected our configuration.
295        Log.getInstance().init();
296    
297        if (isStrictFP() == false)
298        {
299          Log.warn("The used VM seems to use a non-strict floating point arithmetics");
300          Log.warn("Layouts computed with this Java Virtual Maschine may be invalid.");
301          Log.warn("JFreeReport and the library 'iText' depend on the strict floating point rules");
302          Log.warn("of Java1.1 as implemented by the Sun Virtual Maschines.");
303          Log.warn("If you are using the BEA JRockit VM, start the Java VM with the option");
304          Log.warn("'-Xstrictfp' to restore the default behaviour.");
305        }
306    
307        final PackageManager mgr = getPackageManager();
308    
309        mgr.addModule(JFreeReportCoreModule.class.getName());
310        mgr.load("org.jfree.report.modules.");
311        mgr.load("org.jfree.report.ext.modules.");
312        mgr.load("org.jfree.report.userdefined.modules.");
313    
314        bootAdditionalModules();
315        mgr.initializeModules();
316      }
317    
318      /**
319       * Boots modules, which have been spcified in the "org.jfree.report.boot.Modules"
320       * configuration parameter.
321       */
322      private void bootAdditionalModules ()
323      {
324        try
325        {
326          final String bootModules =
327                  getGlobalConfig().getConfigProperty
328                  ("org.jfree.report.boot.Modules");
329          if (bootModules != null)
330          {
331            final CSVTokenizer csvToken = new CSVTokenizer(bootModules, ",");
332            while (csvToken.hasMoreTokens())
333            {
334              final String token = csvToken.nextToken();
335              getPackageManager().load(token);
336            }
337          }
338        }
339        catch (SecurityException se)
340        {
341          Log.info("Security settings forbid to check the system properties for extension modules.");
342        }
343        catch (Exception se)
344        {
345          Log.error
346                  ("An error occured while checking the system properties for extension modules.", se);
347        }
348      }
349    
350    
351      /**
352       * This method returns true on non-strict floating point systems.
353       * <p/>
354       * Since Java 1.2 Virtual Maschines may implement the floating point arithmetics in a
355       * more performant way, which does not put the old strict constraints on the floating
356       * point types <code>float</code> and <code>double</code>.
357       * <p/>
358       * As iText and this library requires strict (in the sense of Java1.1) floating point
359       * operations, we have to test for that feature here.
360       * <p/>
361       * The only known VM that seems to implement that feature is the JRockit VM. The strict
362       * mode can be restored on that VM by adding the "-Xstrictfp" VM parameter.
363       *
364       * @return true, if the VM uses strict floating points by default, false otherwise.
365       */
366      private static boolean isStrictFP ()
367      {
368        final double d = 8e+307;
369        final double result1 = 4.0 * d * 0.5;
370        final double result2 = 2.0 * d;
371        return (result1 != result2 && (result1 == Double.POSITIVE_INFINITY));
372      }
373    
374    
375      /**
376       * Returns the user supplied global configuration.
377       *
378       * @return the user configuration, if any.
379       */
380      public static Configuration getUserConfig ()
381      {
382        return configWrapper.getWrappedConfiguration();
383      }
384    
385      /**
386       * Defines the global user configuration.
387       *
388       * @param config the user configuration.
389       */
390      public static void setUserConfig (final Configuration config)
391      {
392        configWrapper.setWrappedConfiguration(config);
393      }
394    
395    }