001    /*
002    // $Id: //open/mondrian-release/3.2/src/main/mondrian/olap/MondrianProperties.java#3 $
003    // This software is subject to the terms of the Eclipse Public License v1.0
004    // Agreement, available at the following URL:
005    // http://www.eclipse.org/legal/epl-v10.html.
006    // Copyright (C) 2001-2002 Kana Software, Inc.
007    // Copyright (C) 2001-2009 Julian Hyde and others
008    // All Rights Reserved.
009    // You must accept the terms of that agreement to use this software.
010    //
011    // jhyde, 22 December, 2002
012    */
013    package mondrian.olap;
014    
015    import org.apache.log4j.Logger;
016    import org.eigenbase.util.property.*;
017    
018    import java.io.*;
019    import java.net.MalformedURLException;
020    import java.net.URL;
021    import java.net.URLConnection;
022    import java.util.Enumeration;
023    import java.util.Properties;
024    
025    /**
026     * <code>MondrianProperties</code> contains the properties which determine the
027     * behavior of a mondrian instance.
028     *
029     * <p>There is a method for property valid in a
030     * <code>mondrian.properties</code> file. Although it is possible to retrieve
031     * properties using the inherited {@link Properties#getProperty(String)}
032     * method, we recommend that you use methods in this class.
033     *
034     * <h2>Note to developers</h2>
035     *
036     * If you add a property, you must:<ul>
037     *
038     * <li>Add a property definition to this class</li>
039     *
040     * <li>Modify the default <code>mondrian.properties</code> file checked into
041     * source control, with a description of the property and its default
042     * value.</li>
043     *
044     * <li>Modify the
045     * <a target="_top" href="{@docRoot}/../../documentation/configuration.php#Property_list">
046     * Configuration Specification</a>.</li>
047     * </ul>
048     *
049     * <p>Similarly if you update or delete a property.
050     *
051     * @author jhyde
052     * @version $Id: //open/mondrian-release/3.2/src/main/mondrian/olap/MondrianProperties.java#3 $
053     * @since 22 December, 2002
054     */
055    public class MondrianProperties extends TriggerableProperties {
056    
057        private final PropertySource propertySource;
058        private int populateCount;
059    
060        private static final Logger LOGGER =
061                Logger.getLogger(MondrianProperties.class);
062    
063        /**
064         * Properties, drawn from {@link System#getProperties}, plus the contents
065         * of "mondrian.properties" if it exists. A singleton.
066         */
067        private static MondrianProperties instance;
068        private static final String mondrianDotProperties = "mondrian.properties";
069    
070        /**
071         * Returns the singleton.
072         *
073         * @return Singleton instance
074         */
075        public static synchronized MondrianProperties instance() {
076            if (instance == null) {
077                instance = new MondrianProperties();
078                instance.populate();
079            }
080            return instance;
081        }
082    
083        public MondrianProperties() {
084            this.propertySource =
085                new FilePropertySource(new File(mondrianDotProperties));
086        }
087    
088        public boolean triggersAreEnabled() {
089            return EnableTriggers.get();
090        }
091    
092        /**
093         * Represents a place that properties can be read from, and remembers the
094         * timestamp that we last read them.
095         */
096        public interface PropertySource {
097            /**
098             * Opens an input stream from the source.
099             *
100             * <p>Also checks the 'last modified' time, which will determine whether
101             * {@link #isStale()} returns true.
102             *
103             * @return input stream
104             */
105            InputStream openStream();
106    
107            /**
108             * Returns true if the source exists and has been modified since last
109             * time we called {@link #openStream()}.
110             *
111             * @return whether source has changed since it was last read
112             */
113            boolean isStale();
114    
115            /**
116             * Returns the description of this source, such as a filename or URL.
117             *
118             * @return description of this PropertySource
119             */
120            String getDescription();
121        }
122    
123        /**
124         * Implementation of {@link PropertySource} which reads from a
125         * {@link File}.
126         */
127        static class FilePropertySource implements PropertySource {
128            private final File file;
129            private long lastModified;
130    
131            FilePropertySource(File file) {
132                this.file = file;
133                this.lastModified = 0;
134            }
135    
136            public InputStream openStream() {
137                try {
138                    this.lastModified = file.lastModified();
139                    return new FileInputStream(file);
140                } catch (FileNotFoundException e) {
141                    throw Util.newInternal(
142                        e,
143                        "Error while opening properties file '" + file + "'");
144                }
145            }
146    
147            public boolean isStale() {
148                return file.exists()
149                    && file.lastModified() > this.lastModified;
150            }
151    
152            public String getDescription() {
153                return "file=" + file.getAbsolutePath()
154                    + " (exists=" + file.exists() + ")";
155            }
156        }
157    
158        /**
159         * Implementation of {@link PropertySource} which reads from a {@link URL}.
160         */
161        static class UrlPropertySource implements PropertySource {
162            private final URL url;
163            private long lastModified;
164    
165            UrlPropertySource(URL url) {
166                this.url = url;
167            }
168    
169            private URLConnection getConnection() {
170                try {
171                    return url.openConnection();
172                } catch (IOException e) {
173                    throw Util.newInternal(
174                            e,
175                            "Error while opening properties file '" + url + "'");
176                }
177            }
178    
179            public InputStream openStream() {
180                try {
181                    final URLConnection connection = getConnection();
182                    this.lastModified = connection.getLastModified();
183                    return connection.getInputStream();
184                } catch (IOException e) {
185                    throw Util.newInternal(
186                            e,
187                            "Error while opening properties file '" + url + "'");
188                }
189            }
190    
191            public boolean isStale() {
192                final long lastModified = getConnection().getLastModified();
193                return lastModified > this.lastModified;
194            }
195    
196            public String getDescription() {
197                return url.toExternalForm();
198            }
199        }
200    
201        /**
202         * Loads this property set from: the file "$PWD/mondrian.properties" (if it
203         * exists); the "mondrian.properties" in the CLASSPATH; and from the system
204         * properties.
205         */
206        public void populate() {
207            // Read properties file "mondrian.properties", if it exists. If we have
208            // read the file before, only read it if it is newer.
209            loadIfStale(propertySource);
210    
211            URL url = null;
212            File file = new File(mondrianDotProperties);
213            if (file.exists() && file.isFile()) {
214                // Read properties file "mondrian.properties" from PWD, if it
215                // exists.
216                try {
217                    url = file.toURI().toURL();
218                } catch (MalformedURLException e) {
219                    LOGGER.warn(
220                        "Mondrian: file '"
221                        + file.getAbsolutePath()
222                        + "' could not be loaded", e);
223                }
224            } else {
225                // Then try load it from classloader
226                url =
227                    MondrianProperties.class.getClassLoader().getResource(
228                        mondrianDotProperties);
229            }
230    
231            if (url != null) {
232                load(new UrlPropertySource(url));
233            } else {
234                LOGGER.warn(
235                    "mondrian.properties can't be found under '"
236                    + new File(".").getAbsolutePath() + "' or classloader");
237            }
238    
239            // copy in all system properties which start with "mondrian."
240            int count = 0;
241            for (Enumeration keys = System.getProperties().keys();
242                 keys.hasMoreElements();)
243            {
244                String key = (String) keys.nextElement();
245                String value = System.getProperty(key);
246                if (key.startsWith("mondrian.")) {
247                    // NOTE: the super allows us to bybase calling triggers
248                    // Is this the correct behavior?
249                    if (LOGGER.isDebugEnabled()) {
250                        LOGGER.debug("populate: key=" + key + ", value=" + value);
251                    }
252                    super.setProperty(key, value);
253                    count++;
254                }
255            }
256            if (populateCount++ == 0) {
257                LOGGER.info(
258                    "Mondrian: loaded " + count + " system properties");
259            }
260        }
261    
262        /**
263         * Reads properties from a source.
264         * If the source does not exist, or has not changed since we last read it,
265         * does nothing.
266         *
267         * @param source Source of properties
268         */
269        private void loadIfStale(PropertySource source) {
270            if (source.isStale()) {
271                if (LOGGER.isDebugEnabled()) {
272                    LOGGER.debug("Mondrian: loading " + source.getDescription());
273                }
274                load(source);
275            }
276        }
277    
278        /**
279         * Tries to load properties from a URL. Does not fail, just prints success
280         * or failure to log.
281         *
282         * @param source Source to read properties from
283         */
284        private void load(final PropertySource source) {
285            try {
286                load(source.openStream());
287                if (populateCount == 0) {
288                    LOGGER.info(
289                        "Mondrian: properties loaded from '"
290                        + source.getDescription()
291                        + "'");
292                }
293            } catch (IOException e) {
294                LOGGER.error(
295                    "Mondrian: error while loading properties "
296                    + "from '" + source.getDescription() + "' (" + e + ")");
297            }
298        }
299    
300        /**
301         * Maximum number of simultaneous queries the system will allow.
302         *
303         * <p>Oracle fails if you try to run more than the 'processes' parameter in
304         * init.ora, typically 150. The throughput of Oracle and other databases
305         * will probably reduce long before you get to their limit.</p>
306         */
307        public transient final IntegerProperty QueryLimit =
308            new IntegerProperty(
309                this, "mondrian.query.limit", 40);
310    
311        /**
312         * Property containing a list of JDBC drivers to load automatically.
313         * Must be a comma-separated list of class names, and the classes must be
314         * on the class path.
315         */
316        public transient final StringProperty JdbcDrivers =
317            new StringProperty(
318                this,
319                "mondrian.jdbcDrivers",
320                "sun.jdbc.odbc.JdbcOdbcDriver,"
321                + "org.hsqldb.jdbcDriver,"
322                + "oracle.jdbc.OracleDriver,"
323                + "com.mysql.jdbc.Driver");
324    
325        /**
326         * Integer property that, if set to a value greater than zero, limits the
327         * maximum size of a result set.
328         */
329        public transient final IntegerProperty ResultLimit =
330            new IntegerProperty(
331                this, "mondrian.result.limit", 0);
332    
333        /**
334         * Property that establishes the amount of chunks for querying cells
335         * involving high-cardinality dimensions.
336         * Should prime with {@link #ResultLimit mondrian.result.limit}.
337         */
338        public transient final IntegerProperty HighCardChunkSize =
339            new IntegerProperty(this, "mondrian.result.highCardChunkSize", 1);
340    
341    
342        // mondrian.test properties
343    
344        /**
345         * String property that determines which tests are run.
346         *
347         * <p>This is a regular expression as defined by
348         * {@link java.util.regex.Pattern}.
349         * If this property is specified, only tests whose names match the pattern
350         * in its entirety will be run.</p>
351         *
352         * @see #TestClass
353         */
354        public transient final StringProperty TestName =
355            new StringProperty(
356                this, "mondrian.test.Name", null);
357    
358        /**
359         * String property that determines which test class to run.
360         *
361         * <p>This is the name of the class which either implements
362         * {@code junit.framework.Test} or has a method
363         * {@code public [static] junit.framework.Test suite()}.</p>
364         *
365         * <p>Example:
366         * <blockquote><code>
367         * mondrian.test.Class=mondrian.test.FoodMartTestCase
368         * </code></blockquote>
369         *
370         * @see #TestName
371         */
372        public transient final StringProperty TestClass =
373            new StringProperty(
374                this, "mondrian.test.Class", null);
375    
376        /**
377         * Property containing the connect string which regresssion tests should
378         * use to connect to the database.
379         * Format is specified in {@link Util#parseConnectString(String)}.
380         */
381        public transient final StringProperty TestConnectString =
382            new StringProperty(
383                this, "mondrian.test.connectString", null);
384        /**
385         * Property containing a list of dimensions in the Sales cube that should
386         * be treated as high-cardinality dimensions by the testing infrastructure.
387         * This allows us to run the full suite of tests with high-cardinality
388         * functionality enabled.
389         */
390        public transient final StringProperty TestHighCardinalityDimensionList =
391            new StringProperty(
392                this, "mondrian.test.highCardDimensions", null);
393    
394        // miscellaneous
395    
396        /**
397         * Property containing the JDBC URL of the FoodMart database.
398         * The default value is to connect to an ODBC data source called
399         * "MondrianFoodMart".
400         */
401        public transient final StringProperty FoodmartJdbcURL =
402            new StringProperty(
403                this, "mondrian.foodmart.jdbcURL", "jdbc:odbc:MondrianFoodMart");
404    
405        /**
406         * Property containing the JDBC URL of a test database.
407         * It does not default.
408         */
409        public transient final StringProperty TestJdbcURL =
410            new StringProperty(
411                this, "mondrian.test.jdbcURL", null);
412    
413        /**
414         * Property containing the JDBC user of a test database.
415         * The default value is null, to cope with DBMSs that don't need this.
416         */
417        public transient final StringProperty TestJdbcUser =
418            new StringProperty(
419                this, "mondrian.test.jdbcUser", null);
420    
421        /**
422         * Property containing the JDBC password of a test database.
423         * The default value is null, to cope with DBMSs that don't need this.
424         */
425        public transient final StringProperty TestJdbcPassword =
426            new StringProperty(
427                this, "mondrian.test.jdbcPassword", null);
428    
429        /**
430         * Property that, with {@link #SparseSegmentDensityThreshold}, determines
431         * whether to choose a sparse or dense representation when storing
432         * collections of cell values in memory.
433         *
434         * <p>When storing collections of cell values, Mondrian has to choose
435         * between a sparse and a dense representation, based upon the
436         * <code>possible</code> and <code>actual</code> number of values.
437         * The <code>density</code> is <code>actual / possible</code>.
438         *
439         * <p>We use a sparse representation if
440         * <code>(possible -
441         * {@link #SparseSegmentCountThreshold countThreshold}) *
442         * {@link #SparseSegmentDensityThreshold densityThreshold} &gt;
443         * actual</code>
444         *
445         * <p>For example, at the default values
446         * ({@link #SparseSegmentCountThreshold countThreshold} = 1000,
447         * {@link #SparseSegmentDensityThreshold} = 0.5),
448         * we use a dense representation for<ul>
449         * <li>(1000 possible, 0 actual), or
450         * <li>(2000 possible, 500 actual), or
451         * <li>(3000 possible, 1000 actual).
452         * </ul>
453         * Any fewer actual values, or any more
454         * possible values, and Mondrian will use a sparse representation.
455         */
456        public transient final IntegerProperty SparseSegmentCountThreshold =
457            new IntegerProperty(
458                this, "mondrian.rolap.SparseSegmentValueThreshold", 1000);
459    
460        /**
461         * Property that, with {@link #SparseSegmentCountThreshold},
462         * determines whether to choose a sparse or dense representation when
463         * storing collections of cell values in memory.
464         */
465        public transient final DoubleProperty SparseSegmentDensityThreshold =
466            new DoubleProperty(
467                this, "mondrian.rolap.SparseSegmentDensityThreshold", 0.5);
468    
469        /**
470         * Property that defines
471         * a pattern for which test XML files to run.  Pattern has to
472         * match a file name of the form:
473         * <code>query<i>whatever</i>.xml</code> in the directory.
474         *
475         * <p>Example:
476         * <blockquote><code>
477         * mondrian.test.QueryFilePattern=queryTest_fec[A-Za-z0-9_]*.xml
478         * </code></blockquote>
479         */
480        public transient final StringProperty QueryFilePattern =
481            new StringProperty(
482                this, "mondrian.test.QueryFilePattern", null);
483    
484        /**
485         * Property defining
486         * where the test XML files are.
487         */
488        public transient final StringProperty QueryFileDirectory =
489            new StringProperty(
490                this, "mondrian.test.QueryFileDirectory", null);
491    
492        /**
493         * todo:
494         */
495        public transient final IntegerProperty Iterations =
496            new IntegerProperty(
497                this, "mondrian.test.Iterations", 1);
498    
499        /**
500         * todo:
501         */
502        public transient final IntegerProperty VUsers =
503            new IntegerProperty(
504                this, "mondrian.test.VUsers", 1);
505    
506        /**
507         * Property that returns the time limit for the test run in seconds.
508         * If the test is running after that time, it is terminated.
509         */
510        public transient final IntegerProperty TimeLimit =
511            new IntegerProperty(
512                this, "mondrian.test.TimeLimit", 0);
513    
514        /**
515         * Property that indicates whether this is a "warmup test".
516         */
517        public transient final BooleanProperty Warmup =
518            new BooleanProperty(
519                this, "mondrian.test.Warmup", false);
520    
521        /**
522         * Property that contains the URL of the catalog to be used by
523         * {@link mondrian.tui.CmdRunner} and XML/A Test.
524         */
525        public transient final StringProperty CatalogURL =
526            new StringProperty(
527                this, "mondrian.catalogURL", null);
528    
529        /**
530         * Property that controls
531         * whether aggregation cache hit / miss counters will be enabled
532         */
533        public transient final BooleanProperty EnableCacheHitCounters =
534            new BooleanProperty(
535                this, "mondrian.rolap.agg.enableCacheHitCounters", false);
536    
537        /**
538         * Property that controls if warning messages should be printed if a sql
539         * comparison tests do not contain expected sqls for the specified
540         * dialect. The tests are skipped if no expected sqls are
541         * found for the current dialect.
542         *
543         * <p>Possible values are the following:
544         * "NONE": no warning (default)
545         * "ANY": any dialect
546         * "ACCESS"
547         * "DERBY"
548         * "LUCIDDB"
549         * "MYSQL"
550         *  ...and any Dialect enum in SqlPattern.Dialect
551         *
552         * <p>Specific tests can overwrite the default setting. The priority is:<ul>
553         * <li>Settings besides "ANY" in mondrian.properties file
554         * <li>&lt; Any setting in the test
555         * <li>&lt; "ANY"
556         * </ul>
557         */
558        public transient final StringProperty WarnIfNoPatternForDialect =
559            new StringProperty(
560                this, "mondrian.test.WarnIfNoPatternForDialect", "NONE");
561    
562        //////////////////////////////////////////////////////////////////////////
563        //
564        // properties relating to aggregates
565        //
566    
567        /**
568         * Boolean property that controls whether Mondrian uses aggregate tables.
569         *
570         * <p>If true, then Mondrian uses aggregate tables. This property is
571         * queried prior to each aggregate query so that changing the value of this
572         * property dynamically (not just at startup) is meaningful.
573         *
574         * <p>Aggregates can be read from the database using the
575         * {@link #ReadAggregates} property but will not be used unless this
576         * property is set to true.
577         */
578        public transient final BooleanProperty UseAggregates =
579            new BooleanProperty(
580                this, "mondrian.rolap.aggregates.Use", false);
581    
582        /**
583         * Boolean property that determines whether Mondrian should read aggregate
584         * tables.
585         *
586         * <p>If set to true, then Mondrian scans the database for aggregate tables.
587         * Unless mondrian.rolap.aggregates.Use is set to true, the aggregates
588         * found will not be used.
589         */
590        public transient final BooleanProperty ReadAggregates =
591            new BooleanProperty(
592                this, "mondrian.rolap.aggregates.Read", false);
593    
594    
595        /**
596         * Boolean property that controls whether aggregate tables
597         * are ordered by their volume or row count.
598         *
599         * <p>If true, Mondrian uses the aggregate table with the smallest volume
600         * (number of rows multiplied by number of columns); if false, Mondrian
601         * uses the aggregate table with the fewest rows.
602         */
603        public transient final BooleanProperty ChooseAggregateByVolume =
604            new BooleanProperty(
605                this, "mondrian.rolap.aggregates.ChooseByVolume", false);
606    
607        /**
608         * String property containing the name of the file which defines the rules
609         * for recognizing an aggregate table. Can be either a resource in the
610         * Mondrian jar or a URL.
611         *
612         * <p>The default value is "/DefaultRules.xml", which is in the
613         * mondrian.rolap.aggmatcher package in Mondrian.jar.
614         *
615         * <p>Normally, this property is not set by a user.
616         */
617        public transient final StringProperty AggregateRules =
618            new StringProperty(
619                this, "mondrian.rolap.aggregates.rules", "/DefaultRules.xml");
620    
621        /**
622         * String property that is the AggRule element's tag value.
623         *
624         * <p>Normally, this property is not set by a user.
625         */
626        public transient final StringProperty AggregateRuleTag =
627            new StringProperty(
628                this, "mondrian.rolap.aggregates.rule.tag", "default");
629    
630        /**
631         * Boolean property that controls whether to print the SQL code
632         * generated for aggregate tables.
633         *
634         * <p>If set, then as each aggregate request is processed, both the lost
635         * and collapsed dimension create and insert sql code is printed.
636         * This is for use in the CmdRunner allowing one to create aggregate table
637         * generation sql.
638         */
639        public transient final BooleanProperty GenerateAggregateSql =
640            new BooleanProperty(
641                this, "mondrian.rolap.aggregates.generateSql", false);
642    
643        //
644        //////////////////////////////////////////////////////////////////////////
645    
646        /**
647         * Boolean property that controls whether a RolapStar's
648         * aggregate data cache is cleared after each query.
649         * If true, no RolapStar will cache aggregate data from one
650         * query to the next (the cache is cleared after each query).
651         */
652        public transient final BooleanProperty DisableCaching =
653            new BooleanProperty(
654                this, "mondrian.rolap.star.disableCaching", false);
655    
656        /**
657         * Boolean property that controls whether to notify the Mondrian system
658         * when a {@link MondrianProperties property value} changes.
659         *
660         * <p>This allows objects dependent on Mondrian properties to react (that
661         * is, reload), when a given property changes via, say,
662         * <code>MondrianProperties.instance().populate(null)</code> or
663         * <code>MondrianProperties.instance().QueryLimit.set(50)</code>.
664         */
665        public transient final BooleanProperty EnableTriggers =
666            new BooleanProperty(
667                this, "mondrian.olap.triggers.enable", true);
668    
669        /**
670         * Boolean property that controls pretty-print mode.
671         * If set to true, the all SqlQuery SQL strings
672         * will be generated in pretty-print mode, formatted for ease of reading.
673         */
674        public transient final BooleanProperty GenerateFormattedSql =
675            new BooleanProperty(
676                this, "mondrian.rolap.generate.formatted.sql", false);
677    
678        /**
679         * Boolean property that controls whether each query axis implicit has the
680         * NON EMPTY option set. The default is false.
681         */
682        public transient final BooleanProperty EnableNonEmptyOnAllAxis =
683            new BooleanProperty(
684                this, "mondrian.rolap.nonempty", false);
685    
686        /**
687         * When looking for native evaluation of an expression, expand non native
688         * subexpressions into MemberLists.
689         */
690        public transient final BooleanProperty ExpandNonNative =
691            new BooleanProperty(
692                this, "mondrian.native.ExpandNonNative", false);
693    
694        /**
695         * Boolean property that controls whether sibling members are
696         * compared according to order key value fetched from their ordinal
697         * expression.  The default is false (only database ORDER BY is used).
698         */
699        public transient final BooleanProperty CompareSiblingsByOrderKey =
700            new BooleanProperty(
701                this, "mondrian.rolap.compareSiblingsByOrderKey", false);
702    
703        /**
704         * Boolean property that controls whether to use a cache for frequently
705         * evaluated expressions. With the cache disabled, an expression like
706         * <code>Rank([Product].CurrentMember,
707         * Order([Product].MEMBERS, [Measures].[Unit Sales]))</code> would perform
708         * many redundant sorts. The default is true.
709         */
710        public transient final BooleanProperty EnableExpCache =
711            new BooleanProperty(
712                this, "mondrian.expCache.enable", true);
713    
714        /**
715         * Integer property that controls whether to test operators' dependencies,
716         * and how much time to spend doing it.
717         *
718         * <p>If this property is positive, Mondrian's test framework allocates an
719         * expression evaluator which evaluates each expression several times, and
720         * makes sure that the results of the expression are independent of
721         * dimensions which the expression claims to be independent of.
722         *
723         * <p>The default is 0.
724         */
725        public transient final IntegerProperty TestExpDependencies =
726            new IntegerProperty(
727                this, "mondrian.test.ExpDependencies", 0);
728    
729        /**
730         * Seed for random number generator used by some of the tests.
731         *
732         * <p>Any value besides 0 or -1 gives deterministic behavior.
733         * The default value is 1234: most users should use this.
734         * Setting the seed to a different value can increase coverage, and
735         * therefore may uncover new bugs.
736         *
737         * <p>If you set the value to 0, the system will generate its own
738         * pseudo-random seed.
739         *
740         * <p>If you set the value to -1, Mondrian uses the next seed from an
741         * internal random-number generator. This is a little more deterministic
742         * than setting the value to 0.
743         */
744        public transient final IntegerProperty TestSeed =
745            new IntegerProperty(
746                this, "mondrian.test.random.seed", 1234);
747    
748        /**
749         * String property that holds the
750         * name of the class whose resource bundle is to be used to for this
751         * schema. For example, if the class is {@code com.acme.MyResource},
752         * mondrian will look for a resource bundle called
753         * {@code com/acme/MyResource_<i>locale</i>.properties} on the class path.
754         * (This property has a confusing name because in a previous release it
755         * actually held a file name.)
756         *
757         * <p>Used for the {@link mondrian.i18n.LocalizingDynamicSchemaProcessor};
758         * see <a href="{@docRoot}/../../documentation/schema.php#I18n">Internationalization</a>
759         * for more details.</td>
760         *
761         * <p>Default value is null.
762         */
763        public transient final StringProperty LocalePropFile =
764            new StringProperty(
765                this, "mondrian.rolap.localePropFile", null);
766    
767        /**
768         * if enabled some NON EMPTY CrossJoin will be computed in SQL
769         */
770        public transient final BooleanProperty EnableNativeCrossJoin =
771            new BooleanProperty(
772                this, "mondrian.native.crossjoin.enable", true);
773    
774        /**
775         * if enabled some TopCount will be computed in SQL
776         */
777        public transient final BooleanProperty EnableNativeTopCount =
778            new BooleanProperty(
779                this, "mondrian.native.topcount.enable", true);
780    
781        /**
782         * if enabled some Filter() will be computed in SQL
783         */
784        public transient final BooleanProperty EnableNativeFilter =
785            new BooleanProperty(
786                this, "mondrian.native.filter.enable", true);
787    
788        /**
789         * some NON EMPTY set operations like member.children, level.members and
790         * member descendants will be computed in SQL
791         */
792        public transient final BooleanProperty EnableNativeNonEmpty =
793            new BooleanProperty(
794                this, "mondrian.native.nonempty.enable", true);
795    
796        /**
797         * Alerting action to take in case native evaluation of a function is
798         * enabled but not supported for that function's usage in a particular
799         * query.  (No alert is ever raised in cases where native evaluation would
800         * definitely have been wasted effort.)
801         *
802         *
803         *
804         * Recognized actions:
805         *
806         * <ul>
807         *
808         * <li><code>OFF</code>:  do nothing (default action, also used if
809         * unrecognized action is specified)
810         *
811         * <li><code>WARN</code>:  log a warning to RolapUtil logger
812         *
813         * <li><code>ERROR</code>:  throw an instance of
814         * {@link NativeEvaluationUnsupportedException}
815         *
816         * </ul>
817         */
818        public transient final StringProperty AlertNativeEvaluationUnsupported =
819            new StringProperty(this, "mondrian.native.unsupported.alert", "OFF");
820    
821        /**
822         * If enabled, first row in the result of an XML/A drill-through request
823         * will be filled with the total count of rows in underlying database.
824         */
825        public transient final BooleanProperty EnableTotalCount =
826            new BooleanProperty(
827                this, "mondrian.xmla.drillthroughTotalCount.enable", true);
828    
829        /**
830         * Boolean property that controls whether the MDX parser resolves uses
831         * case-sensitive matching when looking up identifiers. The default is
832         * false.
833         */
834        public transient final BooleanProperty CaseSensitive = new BooleanProperty(
835            this, "mondrian.olap.case.sensitive", false);
836    
837    
838        /**
839         * Property that defines
840         * limit on the number of rows returned by XML/A drill through request.
841         */
842        public transient final IntegerProperty MaxRows =
843            new IntegerProperty(
844                this, "mondrian.xmla.drillthroughMaxRows", 1000);
845    
846        /**
847         * Max number of constraints in a single `IN' SQL clause.
848         *
849         * <p>This value may be variant among database prodcuts and their runtime
850         * settings. Oracle, for example, gives the error "ORA-01795: maximum
851         * number of expressions in a list is 1000".
852         *
853         * <p>Recommended values:<ul>
854         * <li>Oracle: 1,000
855         * <li>DB2: 2,500
856         * <li>Other: 10,000</ul>
857         */
858        public transient final IntegerProperty MaxConstraints =
859            new IntegerProperty(
860                this, "mondrian.rolap.maxConstraints", 1000);
861    
862        /**
863         * Boolean property that determines whether Mondrian optimizes predicates.
864         */
865        public transient final BooleanProperty OptimizePredicates =
866            new BooleanProperty(
867                this, "mondrian.rolap.aggregates.optimizePredicates", true);
868    
869        /**
870         * Boolean property that defines the
871         * maximum number of passes allowable while evaluating an MDX expression.
872         *
873         * <p>If evaluation exceeds this depth (for example, while evaluating a
874         * very complex calculated member), Mondrian will throw an error.
875         */
876        public transient final IntegerProperty MaxEvalDepth =
877            new IntegerProperty(
878                this, "mondrian.rolap.evaluate.MaxEvalDepth", 10);
879    
880        /**
881         * Property that defines the JdbcSchema factory class which
882         * determines the list of tables and columns of a specific datasource.
883         * @see mondrian.rolap.aggmatcher.JdbcSchema
884         */
885        public transient final StringProperty JdbcFactoryClass =
886            new StringProperty(
887                this, "mondrian.rolap.aggregates.jdbcFactoryClass", null);
888    
889        /**
890         * Property that defines
891         * the name of the plugin class that resolves data source names to
892         * {@link javax.sql.DataSource} objects. The class must implement the
893         * {@link mondrian.spi.DataSourceResolver} interface. If not specified,
894         * the default implementation uses JNDI to perform resolution.
895         */
896        public transient final StringProperty DataSourceResolverClass =
897            new StringProperty(
898                this, "mondrian.spi.dataSourceResolverClass", null);
899    
900        /**
901         * Property that defines
902         * the timeout value (in seconds) for queries; 0, the default, indicates no
903         * timeout.
904         */
905        public transient final IntegerProperty QueryTimeout = new IntegerProperty(
906            this, "mondrian.rolap.queryTimeout", 0);
907    
908        /**
909         * Property that defines
910         * whether non-existent member errors should be ignored during schema
911         * load.
912         */
913        public transient final BooleanProperty IgnoreInvalidMembers =
914            new BooleanProperty(
915                this, "mondrian.rolap.ignoreInvalidMembers", false);
916    
917        /**
918         * Property that defines
919         * whether non-existent member errors should be ignored during query
920         * validation.
921         */
922        public transient final BooleanProperty IgnoreInvalidMembersDuringQuery =
923            new BooleanProperty(
924                this, "mondrian.rolap.ignoreInvalidMembersDuringQuery", false);
925    
926        /**
927         * Property that determines how a null member value is represented in the
928         * result output.
929         * <p>AS 2000 shows this as empty value
930         * <p>AS 2005 shows this as "(null)" value
931         */
932        public transient final StringProperty NullMemberRepresentation =
933            new StringProperty(
934                this, "mondrian.olap.NullMemberRepresentation", "#null");
935    
936        /**
937         * Property that defines
938         * the iteration limit when computing an aggregate; 0 indicates unlimited.
939         */
940        public transient final IntegerProperty IterationLimit =
941            new IntegerProperty(
942                this, "mondrian.rolap.iterationLimit", 0);
943    
944        /**
945         * Property that defines
946         * whether the <code>MemoryMonitor</code> should be enabled. By
947         * default for Java5 and above it is not enabled.
948         */
949        public transient final BooleanProperty MemoryMonitor =
950            new BooleanProperty(
951                this, "mondrian.util.memoryMonitor.enable", false);
952    
953        /**
954         * Property that defines
955         * the default <code>MemoryMonitor</code> percentage threshold.
956         */
957        public transient final IntegerProperty MemoryMonitorThreshold =
958            new IntegerProperty(
959                this, "mondrian.util.memoryMonitor.percentage.threshold", 90);
960    
961        /**
962         * Property that defines
963         * the name of the class used as a memory monitor.
964         *
965         * <p>If the value is
966         * non-null, it is used by the <code>MemoryMonitorFactory</code>
967         * to create the implementation.
968         */
969        public transient final StringProperty MemoryMonitorClass =
970            new StringProperty(
971                this, "mondrian.util.MemoryMonitor.class", null);
972    
973        /**
974         * Property that defines
975         * the name of the class used to compile scalar expressions.
976         *
977         * <p>If the value is
978         * non-null, it is used by the <code>ExpCompiler.Factory</code>
979         * to create the implementation.
980         */
981        public transient final StringProperty ExpCompilerClass = new StringProperty(
982            this, "mondrian.calc.ExpCompiler.class", null);
983    
984        /**
985         * <p>Property that defines the name of the factory class used
986         * to create maps of member properties to their respective values.</p>
987         *
988         * <p>If the value is
989         * non-null, it is used by the <code>PropertyValueFactory</code>
990         * to create the implementation.  If unset,
991         * {@link mondrian.rolap.RolapMemberBase.DefaultPropertyValueMapFactory}
992         * will be used. </p>
993         */
994        public transient final StringProperty PropertyValueMapFactoryClass =
995            new StringProperty(
996                this,
997                "mondrian.rolap.RolapMember.PropertyValueMapFactory.class",
998                null);
999    
1000        /**
1001         * <p>Property that defines the name of the class used in SqlMemberSource
1002         * to pool common values.</p>
1003         *
1004         * <p>If the value is non-null, it is used by the
1005         * <code>SqlMemberSource.ValueMapFactory</code>
1006         * to create the implementation.  If it is not set, then
1007         * {@link mondrian.rolap.SqlMemberSource.NullValuePoolFactory}
1008         * will be used, meaning common values will not be pooled.</p>
1009         */
1010        public transient final StringProperty SqlMemberSourceValuePoolFactoryClass =
1011            new StringProperty(
1012                this,
1013                "mondrian.rolap.SqlMemberSource.ValuePoolFactory.class",
1014                null);
1015    
1016        /**
1017         * Property that defines
1018         * when to apply the crossjoin optimization algorithm.
1019         *
1020         * <p>If a crossjoin input list's size is larger than this property's
1021         * value and the axis has the "NON EMPTY" qualifier, then
1022         * the crossjoin non-empty optimizer is applied.
1023         * Setting this value to '0' means that for all crossjoin
1024         * input lists in non-empty axes will have the optimizer applied.
1025         * On the other hand, if the value is set larger than any possible
1026         * list, say <code>Integer.MAX_VALUE</code>, then the optimizer
1027         * will never be applied.
1028         */
1029        public transient final IntegerProperty CrossJoinOptimizerSize =
1030            new IntegerProperty(
1031                this, "mondrian.olap.fun.crossjoin.optimizer.size", 0);
1032    
1033        /**
1034         * Property that defines
1035         * the behavior of division if the denominator evaluates to zero.
1036         *
1037         * <p>If a division has a non-null numerator and a null denominator,
1038         * it evaluates to "Infinity", which conforms to MSAS behavior. However,
1039         * the old semantics of evaluating this to NULL (non MSAS-conforming), is
1040         * useful in some applications. This property controls whether the
1041         * result should be NULL if the denominator is Null.
1042         */
1043        public transient final BooleanProperty NullDenominatorProducesNull =
1044            new BooleanProperty(
1045                this, "mondrian.olap.NullDenominatorProducesNull", false);
1046    
1047        /**
1048         * Property that defines
1049         * whether to generate SQL queries using the <code>GROUPING SETS</code>
1050         * construct for rollup. By default it is not enabled.
1051         *
1052         * <p>Ignored on databases which do not support the
1053         * <code>GROUPING SETS</code> construct (see
1054         * {@link mondrian.spi.Dialect#supportsGroupingSets}).
1055         */
1056        public transient final BooleanProperty EnableGroupingSets =
1057            new BooleanProperty(
1058                this, "mondrian.rolap.groupingsets.enable", false);
1059    
1060        /**
1061         * Property that defines whether to ignore measure when non joining
1062         * dimension is in the tuple during aggregation.
1063         *
1064         * <p>If there are unrelated dimensions to a measure in context during
1065         * aggregation, the measure is ignored in the evaluation context. This
1066         * behaviour kicks in only if the cubeusage for this measure has
1067         * IgnoreUnrelatedDimensions attribute set to false.
1068         *
1069         * <p>For example, Gender doesn't join with [Warehouse Sales] measure.
1070         *
1071         * <p>With mondrian.olap.agg.IgnoreMeasureForNonJoiningDimension=true
1072         * Warehouse Sales gets eliminated and is ignored in the aggregate value.
1073         * <blockquote>
1074         * <p>                                    [Store Sales] + [Warehouse Sales]
1075         * SUM({Product.members * Gender.members})    7,913,333.82
1076         * </blockquote>
1077         * <p>With mondrian.olap.agg.IgnoreMeasureForNonJoiningDimension=false
1078         * Warehouse Sales with Gender All level member contributes to the aggregate
1079         * value.
1080         * <blockquote>
1081         * <p>                                     [Store Sales] + [Warehouse Sales]
1082         * SUM({Product.members * Gender.members})    9,290,730.03
1083         * </blockquote>
1084         * <p>On a report where Gender M, F and All members exist a user will see a
1085         * large aggregated value compared to the aggregated value that can be
1086         * arrived at by suming up values against Gender M and F. This can be
1087         * confusing to the user. This feature can be used to eliminate such a
1088         * situation.
1089         */
1090        public transient final BooleanProperty IgnoreMeasureForNonJoiningDimension =
1091            new BooleanProperty(
1092                this,
1093                "mondrian.olap.agg.IgnoreMeasureForNonJoiningDimension",
1094                false);
1095    
1096        /**
1097         * Property determines if elements of dimension (levels, hierarchies,
1098         * members) need to be prefixed with dimension name in MDX query.
1099         *
1100         * <p>For example when the property is true, the following queries
1101         * will error out. The same queries will work when this property
1102         * is set to false.
1103         * <blockquote>
1104         * <p>
1105         * select {[M]} on 0 from sales
1106         * <p>
1107         * select {[USA]} on 0 from sales
1108         * <p>
1109         * select {[USA].[CA].[Santa Monica]}  on 0 from sales
1110         * </blockquote>
1111         * <p>
1112         * When the property is set to true, any query where elements are
1113         * prefixed with dimension name as below will work
1114         * <blockquote>
1115         * <p>
1116         * select {[Gender].[F]} on 0 from sales
1117         * <p>
1118         * select {[Customers].[Santa Monica]} on 0 from sales
1119         * </blockquote>
1120         * <p>
1121         * Please note that this property does not govern the behaviour where in
1122         * <blockquote>
1123         * <p>
1124         * [Gender].[M]
1125         * </blockquote>
1126         * <p>
1127         * is resolved into a fully qualified
1128         * <blockquote>
1129         * <p>
1130         * [Gender].[M]
1131         * </blockquote>
1132         *
1133         * <p> In a scenario where the schema is very large and dimensions have
1134         * large number of members a MDX query that has a invalid member in it will
1135         * cause mondrian to to go through all the dimensions, levels, hierarchies,
1136         * members and properties trying to resolve the element name. This behavior
1137         * consumes considerable time and resources on the server. Setting this
1138         * property to true will make it fail fast in a scenario where it is
1139         * desirable.
1140         */
1141        public transient final BooleanProperty NeedDimensionPrefix =
1142            new BooleanProperty(
1143                this, "mondrian.olap.elements.NeedDimensionPrefix", false);
1144    
1145        /**
1146         * Property that determines whether to cache RolapCubeMember objects,
1147         * each of which associates a member of a shared hierarchy with a
1148         * particular cube in which it is being used.
1149         *
1150         * <p>The default is {@code true}, that is, use a cache. If you wish to use
1151         * the member cache control aspects of {@link mondrian.olap.CacheControl},
1152         * you must set this property to {@code false}.</p>
1153         *
1154         * <p>RolapCubeMember has recently become more lightweight to
1155         * construct, and we may obsolete this cache and this
1156         * property.</p>
1157         */
1158        public transient final BooleanProperty EnableRolapCubeMemberCache =
1159            new BooleanProperty(
1160                this, "mondrian.rolap.EnableRolapCubeMemberCache", true);
1161    
1162        /**
1163         * Property that controls the behavior of
1164         * {@link Property#SOLVE_ORDER solve order} of calculated members and sets.
1165         *
1166         * <p>Valid values are "absolute" and "scoped" (the default). See
1167         * {@link SolveOrderModeEnum} for details.</p>
1168         */
1169        public transient final StringProperty SolveOrderMode =
1170            new StringProperty(
1171                this,
1172                "mondrian.rolap.SolveOrderMode",
1173                SolveOrderModeEnum.ABSOLUTE.name());
1174    
1175        /**
1176         */
1177        public transient final IntegerProperty NativizeMinThreshold =
1178            new IntegerProperty(
1179                    this,
1180                    "mondrian.native.NativizeMinThreshold",
1181                    100000);
1182    
1183        /**
1184         */
1185        public transient final IntegerProperty NativizeMaxResults =
1186            new IntegerProperty(
1187                    this,
1188                    "mondrian.native.NativizeMaxResults",
1189                    150000);
1190    
1191        /**
1192         * Strategies for applying solve order, exposed via the property
1193         * {@link MondrianProperties#SolveOrderMode}.
1194         */
1195        public enum SolveOrderModeEnum {
1196    
1197            /**
1198             * The SOLVE_ORDER value is absolute regardless of
1199             * where it is defined; e.g. a query defined calculated
1200             * member with a SOLVE_ORDER of 1 always takes precedence
1201             * over a cube defined value of 2.
1202             *
1203             * <p>Compatible with Analysis Services 2000, and default behavior
1204             * up to mondrian-3.0.3.
1205             */
1206            ABSOLUTE,
1207    
1208            /**
1209             * Cube calculated members are resolved before any session
1210             * scope calculated members, and session scope members are
1211             * resolved before any query defined calculation.  The
1212             * SOLVE_ORDER value only applies within the scope in which
1213             * it was defined.
1214             *
1215             * <p>Compatible with Analysis Services 2005, and default behavior
1216             * from mondrian-3.0.4 and later.
1217             */
1218            SCOPED
1219        }
1220    
1221        /**
1222         * Property that defines
1223         * whether to enable new naming behavior.
1224         *
1225         * <p>If true, hierarchies are named [Dimension].[Hierarchy]; if false,
1226         * [Dimension.Hierarchy].
1227         */
1228        public transient final BooleanProperty SsasCompatibleNaming =
1229            new BooleanProperty(
1230                this, "mondrian.olap.SsasCompatibleNaming", false);
1231    }
1232    
1233    // End MondrianProperties.java