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 3525 2007-10-16 11:43:48Z tmorgner $
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      {
074        /** The wrapped configuration. */
075        private Configuration wrappedConfiguration;
076    
077        /**
078         * Default constructor.
079         */
080        private UserConfigWrapper ()
081        {
082          this (null);
083        }
084    
085        private UserConfigWrapper (final Configuration config)
086        {
087          this.wrappedConfiguration = config;
088        }
089        /**
090         * Sets a new configuration. This configuration will be inserted into the
091         * report configuration hierarchy. Set this property to null to disable
092         * the user defined configuration.
093         *
094         * @param wrappedConfiguration the wrapped configuration.
095         */
096        public void setWrappedConfiguration (final Configuration wrappedConfiguration)
097        {
098          this.wrappedConfiguration = wrappedConfiguration;
099        }
100    
101        /**
102         * Returns the user supplied global configuration, if exists.
103         *
104         * @return the user configuration.
105         */
106        public Configuration getWrappedConfiguration ()
107        {
108          return wrappedConfiguration;
109        }
110    
111        /**
112         * Returns the configuration property with the specified key.
113         *
114         * @param key the property key.
115         * @return the property value.
116         */
117        public String getConfigProperty (final String key)
118        {
119          if (wrappedConfiguration == null)
120          {
121            return getParentConfig().getConfigProperty(key);
122          }
123    
124          final String retval = wrappedConfiguration.getConfigProperty(key);
125          if (retval != null)
126          {
127            return retval;
128          }
129          return getParentConfig().getConfigProperty(key);
130        }
131    
132        /**
133         * Returns the configuration property with the specified key
134         * (or the specified default value if there is no such property).
135         * <p/>
136         * If the property is not defined in this configuration, the code
137         * will lookup the property in the parent configuration.
138         *
139         * @param key          the property key.
140         * @param defaultValue the default value.
141         * @return the property value.
142         */
143        public String getConfigProperty (final String key, final String defaultValue)
144        {
145          if (wrappedConfiguration == null)
146          {
147            return getParentConfig().getConfigProperty(key, defaultValue);
148          }
149    
150          final String retval = wrappedConfiguration.getConfigProperty(key, null);
151          if (retval != null)
152          {
153            return retval;
154          }
155          return getParentConfig().getConfigProperty(key, defaultValue);
156        }
157    
158        /**
159         * Sets a configuration property.
160         *
161         * @param key   the property key.
162         * @param value the property value.
163         */
164        public void setConfigProperty (final String key, final String value)
165        {
166          if (wrappedConfiguration instanceof ModifiableConfiguration)
167          {
168            final ModifiableConfiguration modConfiguration =
169                    (ModifiableConfiguration) wrappedConfiguration;
170            modConfiguration.setConfigProperty(key, value);
171          }
172        }
173    
174        /**
175         * Returns all defined configuration properties for the report. The enumeration
176         * contains all keys of the changed properties, properties set from files or
177         * the system properties are not included.
178         *
179         * @return all defined configuration properties for the report.
180         */
181        public Enumeration getConfigProperties ()
182        {
183          if (wrappedConfiguration instanceof ModifiableConfiguration)
184          {
185            final ModifiableConfiguration modConfiguration =
186                    (ModifiableConfiguration) wrappedConfiguration;
187            return modConfiguration.getConfigProperties();
188          }
189          return super.getConfigProperties();
190        }
191      }
192    
193      /**
194       * The singleton instance of the Boot class.
195       */
196      private static JFreeReportBoot instance;
197      /**
198       * The project info contains all meta data about the project.
199       */
200      private BootableProjectInfo projectInfo;
201    
202      /**
203       * Holds a possibly empty reference to a user-supplied Configuration
204       * implementation.
205       */
206      private static transient UserConfigWrapper configWrapper =
207              new UserConfigWrapper();
208    
209      /**
210       * Creates a new instance.
211       */
212      private JFreeReportBoot ()
213      {
214        projectInfo = JFreeReportInfo.getInstance();
215      }
216    
217      /**
218       * Returns the singleton instance of the boot utility class.
219       *
220       * @return the boot instance.
221       */
222      public static synchronized JFreeReportBoot getInstance ()
223      {
224        if (instance == null)
225        {
226          // make sure that I am able to debug the package manager ..
227          DefaultLog.installDefaultLog();
228          instance = new JFreeReportBoot();
229    
230          final HierarchicalConfiguration hc = (HierarchicalConfiguration) BaseBoot.getConfiguration();
231          hc.insertConfiguration(new UserConfigWrapper(instance.getGlobalConfig()));
232        }
233        return instance;
234      }
235    
236      /**
237       * Returns the current global configuration as modifiable instance. This
238       * is exactly the same as casting the global configuration into a
239       * ModifableConfiguration instance.
240       * <p/>
241       * This is a convinience function, as all programmers are lazy.
242       *
243       * @return the global config as modifiable configuration.
244       */
245      public ModifiableConfiguration getEditableConfig()
246      {
247        return (ModifiableConfiguration) getGlobalConfig();
248      }
249    
250      /**
251       * Returns the project info.
252       *
253       * @return The project info.
254       */
255      protected BootableProjectInfo getProjectInfo ()
256      {
257        return projectInfo;
258      }
259    
260      /**
261       * Loads the configuration. This will be called exactly once.
262       *
263       * @return The configuration.
264       */
265      protected Configuration loadConfiguration ()
266      {
267        final HierarchicalConfiguration globalConfig = new HierarchicalConfiguration();
268    
269        final PropertyFileConfiguration rootProperty = new PropertyFileConfiguration();
270        rootProperty.load("/org/jfree/report/jfreereport.properties");
271        rootProperty.load("/org/jfree/report/ext/jfreereport-ext.properties");
272        globalConfig.insertConfiguration(rootProperty);
273        globalConfig.insertConfiguration(JFreeReportBoot.getInstance().getPackageManager()
274                .getPackageConfiguration());
275    
276        final PropertyFileConfiguration baseProperty = new PropertyFileConfiguration();
277        baseProperty.load("/jfreereport.properties");
278        globalConfig.insertConfiguration(baseProperty);
279    
280        globalConfig.insertConfiguration(configWrapper);
281    
282        final SystemPropertyConfiguration systemConfig = new SystemPropertyConfiguration();
283        globalConfig.insertConfiguration(systemConfig);
284        return globalConfig;
285      }
286    
287      /**
288       * Performs the actual boot process.
289       */
290      protected void performBoot ()
291      {
292        // Inject JFreeReport's configuration into jcommon.
293        // make sure logging is re-initialized after we injected our configuration.
294        Log.getInstance().init();
295    
296        if (isStrictFP() == false)
297        {
298          Log.warn("The used VM seems to use a non-strict floating point arithmetics");
299          Log.warn("Layouts computed with this Java Virtual Maschine may be invalid.");
300          Log.warn("JFreeReport and the library 'iText' depend on the strict floating point rules");
301          Log.warn("of Java1.1 as implemented by the Sun Virtual Maschines.");
302          Log.warn("If you are using the BEA JRockit VM, start the Java VM with the option");
303          Log.warn("'-Xstrictfp' to restore the default behaviour.");
304        }
305    
306        final PackageManager mgr = getPackageManager();
307    
308        mgr.addModule(JFreeReportCoreModule.class.getName());
309        mgr.load("org.jfree.report.modules.");
310        mgr.load("org.jfree.report.ext.modules.");
311        mgr.load("org.jfree.report.userdefined.modules.");
312    
313        bootAdditionalModules();
314        mgr.initializeModules();
315      }
316    
317      /**
318       * Boots modules, which have been spcified in the "org.jfree.report.boot.Modules"
319       * configuration parameter.
320       */
321      private void bootAdditionalModules ()
322      {
323        try
324        {
325          final String bootModules =
326                  getGlobalConfig().getConfigProperty
327                  ("org.jfree.report.boot.Modules");
328          if (bootModules != null)
329          {
330            final CSVTokenizer csvToken = new CSVTokenizer(bootModules, ",");
331            while (csvToken.hasMoreTokens())
332            {
333              final String token = csvToken.nextToken();
334              getPackageManager().load(token);
335            }
336          }
337        }
338        catch (SecurityException se)
339        {
340          Log.info("Security settings forbid to check the system properties for extension modules.");
341        }
342        catch (Exception se)
343        {
344          Log.error
345                  ("An error occured while checking the system properties for extension modules.", se);
346        }
347      }
348    
349    
350      /**
351       * This method returns true on non-strict floating point systems.
352       * <p/>
353       * Since Java 1.2 Virtual Maschines may implement the floating point arithmetics in a
354       * more performant way, which does not put the old strict constraints on the floating
355       * point types <code>float</code> and <code>double</code>.
356       * <p/>
357       * As iText and this library requires strict (in the sense of Java1.1) floating point
358       * operations, we have to test for that feature here.
359       * <p/>
360       * The only known VM that seems to implement that feature is the JRockit VM. The strict
361       * mode can be restored on that VM by adding the "-Xstrictfp" VM parameter.
362       *
363       * @return true, if the VM uses strict floating points by default, false otherwise.
364       */
365      private static boolean isStrictFP ()
366      {
367        final double d = 8e+307;
368        final double result1 = 4.0 * d * 0.5;
369        final double result2 = 2.0 * d;
370        return (result1 != result2 && (result1 == Double.POSITIVE_INFINITY));
371      }
372    
373    
374      /**
375       * Returns the user supplied global configuration.
376       *
377       * @return the user configuration, if any.
378       */
379      public static Configuration getUserConfig ()
380      {
381        return configWrapper.getWrappedConfiguration();
382      }
383    
384      /**
385       * Defines the global user configuration.
386       *
387       * @param config the user configuration.
388       */
389      public static void setUserConfig (final Configuration config)
390      {
391        configWrapper.setWrappedConfiguration(config);
392      }
393    
394    }