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