001    /*
002    // $Id: //open/mondrian/src/main/mondrian/olap/MondrianProperties.java#100 $
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-2008 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}/../configuration.html#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/src/main/mondrian/olap/MondrianProperties.java#100 $
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        /**
332         * Property that establishes the amount of chunks for querying cells
333         * involving high-cardinality dimensions.
334         * Should prime with {@link #ResultLimit mondrian.result.limit}.
335         */
336        public transient final IntegerProperty HighCardChunkSize =
337            new IntegerProperty(this, "mondrian.result.highCardChunkSize", 1);
338    
339    
340        // mondrian.test properties
341    
342        /**
343         * String property that determines which tests are run.
344         *
345         * <p>This is a regular expression as defined by
346         * {@link java.util.regex.Pattern}.
347         * If this property is specified, only tests whose names match the pattern
348         * in its entirety will be run.</p>
349         *
350         * @see #TestClass
351         */
352        public transient final StringProperty TestName =
353            new StringProperty(
354                this, "mondrian.test.Name", null);
355    
356        /**
357         * String property that determines which test class to run.
358         *
359         * <p>This is the name of the class which either implements
360         * {@code junit.framework.Test} or has a method
361         * {@code public [static] junit.framework.Test suite()}.</p>
362         *
363         * <p>Example:
364         * <blockquote><code>mondrian.test.Class=mondrian.test.FoodMartTestCase</code></blockquote>
365         *
366         * @see #TestName
367         */
368        public transient final StringProperty TestClass =
369            new StringProperty(
370                this, "mondrian.test.Class", null);
371    
372        /**
373         * Property containing the connect string which regresssion tests should
374         * use to connect to the database.
375         * Format is specified in {@link Util#parseConnectString(String)}.
376         */
377        public transient final StringProperty TestConnectString =
378                new StringProperty(
379                        this, "mondrian.test.connectString", null);
380        /**
381         * Property containing a list of dimensions in the Sales cube that should
382         * be treated as high-cardinality dimensions by the testing infrastructure.
383         * This allows us to run the full suite of tests with high-cardinality
384         * functionality enabled.
385         */
386        public transient final StringProperty TestHighCardinalityDimensionList =
387            new StringProperty(
388                this, "mondrian.test.highCardDimensions", null);
389    
390        // miscellaneous
391    
392        /**
393         * Property containing the JDBC URL of the FoodMart database.
394         * The default value is to connect to an ODBC data source called
395         * "MondrianFoodMart".
396         */
397        public transient final StringProperty FoodmartJdbcURL = new StringProperty(
398                this, "mondrian.foodmart.jdbcURL", "jdbc:odbc:MondrianFoodMart");
399    
400        /**
401         * Property containing the JDBC URL of a test database.
402         * It does not default.
403         */
404        public transient final StringProperty TestJdbcURL = new StringProperty(
405                this, "mondrian.test.jdbcURL", null);
406    
407        /**
408         * Property containing the JDBC user of a test database.
409         * The default value is null, to cope with DBMSs that don't need this.
410         */
411        public transient final StringProperty TestJdbcUser = new StringProperty(
412                this, "mondrian.test.jdbcUser", null);
413    
414        /**
415         * Property containing the JDBC password of a test database.
416         * The default value is null, to cope with DBMSs that don't need this.
417         */
418        public transient final StringProperty TestJdbcPassword = new StringProperty(
419                this, "mondrian.test.jdbcPassword", null);
420    
421        /**
422         * Property that, with {@link #SparseSegmentDensityThreshold}, determines
423         * whether to choose a sparse or dense representation when storing
424         * collections of cell values in memory.
425         *
426         * <p>When storing collections of cell values, Mondrian has to choose
427         * between a sparse and a dense representation, based upon the
428         * <code>possible</code> and <code>actual</code> number of values.
429         * The <code>density</code> is <code>actual / possible</code>.
430         *
431         * <p>We use a sparse representation if
432         * <code>(possible -
433         * {@link #SparseSegmentCountThreshold countThreshold}) *
434         * {@link #SparseSegmentDensityThreshold densityThreshold} &gt;
435         * actual</code>
436         *
437         * <p>For example, at the default values
438         * ({@link #SparseSegmentCountThreshold countThreshold} = 1000,
439         * {@link #SparseSegmentDensityThreshold} = 0.5),
440         * we use a dense representation for<ul>
441         * <li>(1000 possible, 0 actual), or
442         * <li>(2000 possible, 500 actual), or
443         * <li>(3000 possible, 1000 actual).
444         * </ul>
445         * Any fewer actual values, or any more
446         * possible values, and Mondrian will use a sparse representation.
447         */
448        public transient final IntegerProperty SparseSegmentCountThreshold =
449                new IntegerProperty(
450                        this, "mondrian.rolap.SparseSegmentValueThreshold", 1000);
451    
452        /**
453         * Property that, with {@link #SparseSegmentCountThreshold},
454         * determines whether to choose a sparse or dense representation when
455         * storing collections of cell values in memory.
456         */
457        public transient final DoubleProperty SparseSegmentDensityThreshold =
458                new DoubleProperty(
459                        this, "mondrian.rolap.SparseSegmentDensityThreshold", 0.5);
460    
461        /**
462         * Property that defines
463         * a pattern for which test XML files to run.  Pattern has to
464         * match a file name of the form:
465         * <code>query<i>whatever</i>.xml</code> in the directory.
466         *
467         * <p>Example:
468         * <blockquote><code>mondrian.test.QueryFilePattern=queryTest_fec[A-Za-z0-9_]*.xml</code></blockquote>
469         *
470         */
471        public transient final StringProperty QueryFilePattern =
472                new StringProperty(
473                        this, "mondrian.test.QueryFilePattern", null);
474    
475        /**
476         * Property defining
477         * where the test XML files are.
478         */
479        public transient final StringProperty QueryFileDirectory =
480                new StringProperty(
481                        this, "mondrian.test.QueryFileDirectory", null);
482    
483        /**
484         * todo:
485         */
486        public transient final IntegerProperty Iterations = new IntegerProperty(
487                this, "mondrian.test.Iterations", 1);
488    
489        /**
490         * todo:
491         */
492        public transient final IntegerProperty VUsers = new IntegerProperty(
493                this, "mondrian.test.VUsers", 1);
494    
495        /**
496         * Property that returns the time limit for the test run in seconds.
497         * If the test is running after that time, it is terminated.
498         */
499        public transient final IntegerProperty TimeLimit = new IntegerProperty(
500                this, "mondrian.test.TimeLimit", 0);
501    
502        /**
503         * Property that indicates whether this is a "warmup test".
504         */
505        public transient final BooleanProperty Warmup = new BooleanProperty(
506                this, "mondrian.test.Warmup", false);
507    
508        /**
509         * Property that contains the URL of the catalog to be used by
510         * {@link mondrian.tui.CmdRunner} and XML/A Test.
511         */
512        public transient final StringProperty CatalogURL = new StringProperty(
513                this, "mondrian.catalogURL", null);
514    
515        /**
516         * Property that controls
517         * whether aggregation cache hit / miss counters will be enabled
518         */
519        public transient final BooleanProperty EnableCacheHitCounters =
520            new BooleanProperty(
521                this, "mondrian.rolap.agg.enableCacheHitCounters", false);
522    
523        /**
524         * Property that controls if warning messages should be printed if a sql
525         * comparison tests do not contain expected sqls for the specified
526         * dialect. The tests are skipped if no expected sqls are
527         * found for the current dialect.
528         *
529         * Possible values are the following:
530         * "NONE": no warning (default)
531         * "ANY": any dialect
532         * "ACCESS"
533         * "DERBY"
534         * "LUCIDDB"
535         * "MYSQL"
536         *  ...and any Dialect enum in SqlPattern.Dialect
537         *
538         * Specific tests can overwrite the default setting. The priority is
539         * Settings besides "ANY" in mondrian.properties file < Any setting in the test < "ANY"
540         *
541         */
542        public transient final StringProperty WarnIfNoPatternForDialect =
543            new StringProperty(
544                    this, "mondrian.test.WarnIfNoPatternForDialect", "NONE");
545    
546        //////////////////////////////////////////////////////////////////////////
547        //
548        // properties relating to aggregates
549        //
550    
551        /**
552         * Boolean property that controls whether Mondrian uses aggregate tables.
553         *
554         * <p>If true, then Mondrian uses aggregate tables. This property is
555         * queried prior to each aggregate query so that changing the value of this
556         * property dynamically (not just at startup) is meaningful.
557         *
558         * <p>Aggregates can be read from the database using the
559         * {@link #ReadAggregates} property but will not be used unless this
560         * property is set to true.
561         */
562        public transient final BooleanProperty UseAggregates =
563                new BooleanProperty(
564                        this, "mondrian.rolap.aggregates.Use", false);
565    
566        /**
567         * Boolean property that determines whether Mondrian should read aggregate
568         * tables.
569         *
570         * <p>If set to true, then Mondrian scans the database for aggregate tables.
571         * Unless mondrian.rolap.aggregates.Use is set to true, the aggregates
572         * found will not be used.
573         */
574        public transient final BooleanProperty ReadAggregates =
575                new BooleanProperty(
576                        this, "mondrian.rolap.aggregates.Read", false);
577    
578    
579        /**
580         * Boolean property that controls whether aggregate tables
581         * are ordered by their volume or row count.
582         *
583         * <p>If true, Mondrian uses the aggregate table with the smallest volume
584         * (number of rows multiplied by number of columns); if false, Mondrian
585         * uses the aggregate table with the fewest rows.
586         */
587        public transient final BooleanProperty ChooseAggregateByVolume =
588                new BooleanProperty(
589                        this, "mondrian.rolap.aggregates.ChooseByVolume", false);
590    
591        /**
592         * String property containing the name of the file which defines the rules
593         * for recognizing an aggregate table. Can be either a resource in the
594         * Mondrian jar or a URL.
595         *
596         * <p>The default value is "/DefaultRules.xml", which is in the
597         * mondrian.rolap.aggmatcher package in Mondrian.jar.
598         *
599         * <p>Normally, this property is not set by a user.
600         */
601        public transient final StringProperty AggregateRules =
602                new StringProperty(
603                        this, "mondrian.rolap.aggregates.rules", "/DefaultRules.xml");
604    
605        /**
606         * String property that is the AggRule element's tag value.
607         *
608         * <p>Normally, this property is not set by a user.
609         */
610        public transient final StringProperty AggregateRuleTag =
611                new StringProperty(
612                        this, "mondrian.rolap.aggregates.rule.tag", "default");
613    
614        /**
615         * Boolean property that controls whether to print the SQL code
616         * generated for aggregate tables.
617         *
618         * <p>If set, then as each aggregate request is processed, both the lost
619         * and collapsed dimension create and insert sql code is printed.
620         * This is for use in the CmdRunner allowing one to create aggregate table
621         * generation sql.
622         */
623        public transient final BooleanProperty GenerateAggregateSql =
624                new BooleanProperty(
625                        this, "mondrian.rolap.aggregates.generateSql", false);
626    
627        //
628        //////////////////////////////////////////////////////////////////////////
629    
630        /**
631         * Boolean property that controls whether a RolapStar's
632         * aggregate data cache is cleared after each query.
633         * If true, no RolapStar will cache aggregate data from one
634         * query to the next (the cache is cleared after each query).
635         */
636        public transient final BooleanProperty DisableCaching =
637                new BooleanProperty(
638                        this, "mondrian.rolap.star.disableCaching", false);
639    
640        /**
641         * Boolean property that controls whether to notify the Mondrian system
642         * when a {@link MondrianProperties property value} changes.
643         *
644         * <p>This allows objects dependent on Mondrian properties to react (that
645         * is, reload), when a given property changes via, say,
646         * <code>MondrianProperties.instance().populate(null)</code> or
647         * <code>MondrianProperties.instance().QueryLimit.set(50)</code>.
648         */
649        public transient final BooleanProperty EnableTriggers =
650                new BooleanProperty(
651                        this, "mondrian.olap.triggers.enable", true);
652    
653        /**
654         * Boolean property that controls pretty-print mode.
655         * If set to true, the all SqlQuery SQL strings
656         * will be generated in pretty-print mode, formatted for ease of reading.
657         */
658        public transient final BooleanProperty GenerateFormattedSql =
659                new BooleanProperty(
660                        this, "mondrian.rolap.generate.formatted.sql", false);
661    
662        /**
663         * Boolean property that controls whether each query axis implicit has the
664         * NON EMPTY option set. The default is false.
665         */
666        public transient final BooleanProperty EnableNonEmptyOnAllAxis =
667                new BooleanProperty(
668                        this, "mondrian.rolap.nonempty", false);
669    
670        /**
671         * When looking for native evaluation of an expression, expand non native
672         * subexpressions into MemberLists.
673         */
674        public transient final BooleanProperty ExpandNonNative =
675                new BooleanProperty(
676                        this, "mondrian.native.ExpandNonNative", false);
677    
678        /**
679         * Boolean property that controls whether sibling members are
680         * compared according to order key value fetched from their ordinal
681         * expression.  The default is false (only database ORDER BY is used).
682         */
683        public transient final BooleanProperty CompareSiblingsByOrderKey =
684                new BooleanProperty(
685                        this, "mondrian.rolap.compareSiblingsByOrderKey", false);
686    
687        /**
688         * Boolean property that controls whether to use a cache for frequently
689         * evaluated expressions. With the cache disabled, an expression like
690         * <code>Rank([Product].CurrentMember,
691         * Order([Product].MEMBERS, [Measures].[Unit Sales]))</code> would perform
692         * many redundant sorts. The default is true.
693         */
694        public transient final BooleanProperty EnableExpCache =
695                new BooleanProperty(
696                        this, "mondrian.expCache.enable", true);
697    
698        /**
699         * Integer property that controls whether to test operators' dependencies,
700         * and how much time to spend doing it.
701         *
702         * <p>If this property is positive, Mondrian's test framework allocates an
703         * expression evaluator which evaluates each expression several times, and
704         * makes sure that the results of the expression are independent of
705         * dimensions which the expression claims to be independent of.
706         *
707         * <p>The default is 0.
708         */
709        public transient final IntegerProperty TestExpDependencies =
710                new IntegerProperty(
711                        this, "mondrian.test.ExpDependencies", 0);
712    
713        /**
714         * Seed for random number generator used by some of the tests.
715         *
716         *
717         * Any value besides 0 or -1 gives deterministic behavior.
718         * The default value is 1234: most users should use this.
719         * Setting the seed to a different value can increase coverage, and
720         * therefore may uncover new bugs.
721         *
722         * <p>If you set the value to 0, the system will generate its own
723         * pseudo-random seed.
724         *
725         * <p>If you set the value to -1, Mondrian uses the next seed from an
726         * internal random-number generator. This is a little more deterministic
727         * than setting the value to 0.
728         */
729        public transient final IntegerProperty TestSeed =
730                new IntegerProperty(
731                        this, "mondrian.test.random.seed", 1234);
732    
733        /**
734         * Name of locale property file.
735         *
736         * <p>Used for the {@link mondrian.i18n.LocalizingDynamicSchemaProcessor};
737         * see <a href="{@docRoot}/../schema.html#I18n">Internationalization</a>
738         * for more details.</td>
739         *
740         * <p>Default value is null.
741         */
742        public transient final StringProperty LocalePropFile =
743                new StringProperty(
744                        this, "mondrian.rolap.localePropFile", null);
745    
746        /**
747         * if enabled some NON EMPTY CrossJoin will be computed in SQL
748         */
749        public transient final BooleanProperty EnableNativeCrossJoin =
750                new BooleanProperty(
751                        this, "mondrian.native.crossjoin.enable", true);
752    
753        /**
754         * if enabled some TopCount will be computed in SQL
755         */
756        public transient final BooleanProperty EnableNativeTopCount =
757                new BooleanProperty(
758                        this, "mondrian.native.topcount.enable", true);
759    
760        /**
761         * if enabled some Filter() will be computed in SQL
762         */
763        public transient final BooleanProperty EnableNativeFilter =
764                new BooleanProperty(
765                        this, "mondrian.native.filter.enable", true);
766    
767        /**
768         * some NON EMPTY set operations like member.children, level.members and
769         * member descendants will be computed in SQL
770         */
771        public transient final BooleanProperty EnableNativeNonEmpty =
772                new BooleanProperty(
773                        this, "mondrian.native.nonempty.enable", true);
774    
775        /**
776         * Alerting action to take in case native evaluation of a function is
777         * enabled but not supported for that function's usage in a particular
778         * query.  (No alert is ever raised in cases where native evaluation would
779         * definitely have been wasted effort.)
780         *
781         *
782         *
783         * Recognized actions:
784         *
785         * <ul>
786         *
787         * <li><code>OFF</code>:  do nothing (default action, also used if
788         * unrecognized action is specified)
789         *
790         * <li><code>WARN</code>:  log a warning to RolapUtil logger
791         *
792         * <li><code>ERROR</code>:  throw an instance of
793         * {@link NativeEvaluationUnsupportedException}
794         *
795         * </ul>
796         */
797        public transient final StringProperty AlertNativeEvaluationUnsupported =
798                new StringProperty(this, "mondrian.native.unsupported.alert", "OFF");
799    
800        /**
801         * If enabled, first row in the result of an XML/A drill-through request
802         * will be filled with the total count of rows in underlying database.
803         */
804        public transient final BooleanProperty EnableTotalCount =
805                new BooleanProperty(
806                        this, "mondrian.xmla.drillthroughTotalCount.enable", true);
807    
808        /**
809         * Boolean property that controls whether the MDX parser resolves uses
810         * case-sensitive matching when looking up identifiers. The default is
811         * false.
812         */
813        public transient final BooleanProperty CaseSensitive = new BooleanProperty(
814                this, "mondrian.olap.case.sensitive", false);
815    
816    
817        /**
818         * Property that defines
819         * limit on the number of rows returned by XML/A drill through request.
820         */
821        public transient final IntegerProperty MaxRows = new IntegerProperty(
822                this, "mondrian.xmla.drillthroughMaxRows", 1000);
823    
824        /**
825         * Max number of constraints in a single `IN' SQL clause.
826         *
827         * <p>This value may be variant among database prodcuts and their runtime
828         * settings. Oracle, for example, gives the error "ORA-01795: maximum
829         * number of expressions in a list is 1000".
830         *
831         * <p>Recommended values:<ul>
832         * <li>Oracle: 1,000
833         * <li>DB2: 2,500
834         * <li>Other: 10,000</ul>
835         */
836        public transient final IntegerProperty MaxConstraints = new IntegerProperty(
837                this, "mondrian.rolap.maxConstraints", 1000);
838    
839        /**
840         * Boolean property that determines whether Mondrian optimizes predicates.
841         */
842        public transient final BooleanProperty OptimizePredicates =
843                new BooleanProperty(this,
844                        "mondrian.rolap.aggregates.optimizePredicates",
845                        true);
846    
847        /**
848         * Boolean property that defines the
849         * maximum number of passes allowable while evaluating an MDX expression.
850         *
851         * <p>If evaluation exceeds this depth (for example, while evaluating a
852         * very complex calculated member), Mondrian will throw an error.
853         */
854        public transient final IntegerProperty MaxEvalDepth =
855                new IntegerProperty(
856                        this, "mondrian.rolap.evaluate.MaxEvalDepth", 10);
857    
858        /**
859         * Property that defines the JdbcSchema factory class which
860         * determines the list of tables and columns of a specific datasource.
861         * @see mondrian.rolap.aggmatcher.JdbcSchema
862         */
863        public transient final StringProperty JdbcFactoryClass =
864                new StringProperty(
865                        this, "mondrian.rolap.aggregates.jdbcFactoryClass", null);
866    
867        /**
868         * Property that defines
869         * the timeout value (in seconds) for queries; 0, the default, indicates no
870         * timeout.
871         */
872        public transient final IntegerProperty QueryTimeout = new IntegerProperty(
873                this, "mondrian.rolap.queryTimeout", 0);
874    
875        /**
876         * Property that defines
877         * whether non-existent member errors should be ignored during schema
878         * load.
879         */
880        public transient final BooleanProperty IgnoreInvalidMembers =
881                new BooleanProperty(
882                        this, "mondrian.rolap.ignoreInvalidMembers", false);
883    
884        /**
885         * Property that defines
886         * whether non-existent member errors should be ignored during query
887         * validation.
888         */
889        public transient final BooleanProperty IgnoreInvalidMembersDuringQuery =
890                new BooleanProperty(
891                        this, "mondrian.rolap.ignoreInvalidMembersDuringQuery", false);
892    
893        /**
894         * Property that determines how a null member value is represented in the
895         * result output.
896         * <p>AS 2000 shows this as empty value
897         * <p>AS 2005 shows this as "(null)" value
898         */
899        public transient final StringProperty NullMemberRepresentation =
900                new StringProperty(this, "mondrian.olap.NullMemberRepresentation",
901                        "#null");
902    
903        /**
904         * Property that defines
905         * the iteration limit when computing an aggregate; 0 indicates unlimited.
906         */
907        public transient final IntegerProperty IterationLimit =
908                new IntegerProperty(
909                        this, "mondrian.rolap.iterationLimit", 0);
910    
911        /**
912         * Property that defines
913         * whether the <code>MemoryMonitor</code> should be enabled. By
914         * default for Java5 and above it is not enabled.
915         */
916        public transient final BooleanProperty MemoryMonitor =
917                new BooleanProperty(
918                        this, "mondrian.util.memoryMonitor.enable", false);
919    
920        /**
921         * Property that defines
922         * the default <code>MemoryMonitor</code> percentage threshold.
923         */
924        public transient final IntegerProperty MemoryMonitorThreshold =
925                new IntegerProperty(
926                        this, "mondrian.util.memoryMonitor.percentage.threshold", 90);
927    
928        /**
929         * Property that defines
930         * the name of the class used as a memory monitor.
931         *
932         * <p>If the value is
933         * non-null, it is used by the <code>MemoryMonitorFactory</code>
934         * to create the implementation.
935         */
936        public transient final StringProperty MemoryMonitorClass =
937                new StringProperty(
938                        this, "mondrian.util.MemoryMonitor.class", null);
939    
940        /**
941         * Property that defines
942         * the name of the class used to compile scalar expressions.
943         *
944         * <p>If the value is
945         * non-null, it is used by the <code>ExpCompiler.Factory</code>
946         * to create the implementation.
947         */
948        public transient final StringProperty ExpCompilerClass = new StringProperty(
949                this, "mondrian.calc.ExpCompiler.class", null);
950    
951        /**
952         * Property that defines
953         * when to apply the crossjoin optimization algorithm.
954         *
955         * <p>If a crossjoin input list's size is larger than this property's
956         * value and the axis has the "NON EMPTY" qualifier, then
957         * the crossjoin non-empty optimizer is applied.
958         * Setting this value to '0' means that for all crossjoin
959         * input lists in non-empty axes will have the optimizer applied.
960         * On the other hand, if the value is set larger than any possible
961         * list, say <code>Integer.MAX_VALUE</code>, then the optimizer
962         * will never be applied.
963         */
964        public transient final IntegerProperty CrossJoinOptimizerSize =
965                new IntegerProperty(
966                        this, "mondrian.olap.fun.crossjoin.optimizer.size", 0);
967    
968        /**
969         * Property that defines
970         * the behavior of division if the denominator evaluates to zero.
971         *
972         * <p>If a division has a non-null numerator and a null denominator,
973         * it evaluates to "Infinity", which conforms to MSAS behavior. However,
974         * the old semantics of evaluating this to NULL (non MSAS-conforming), is
975         * useful in some applications. This property controls whether the
976         * result should be NULL if the denominator is Null.
977         */
978        public transient final BooleanProperty NullDenominatorProducesNull =
979                new BooleanProperty(
980                        this, "mondrian.olap.NullDenominatorProducesNull", false);
981    
982        /**
983         * Property that defines
984         * whether to generate SQL queries using the <code>GROUPING SETS</code>
985         * construct for rollup. By default it is not enabled.
986         *
987         * <p>Ignored on databases which do not support the
988         * <code>GROUPING SETS</code> construct (see
989         * {@link mondrian.rolap.sql.SqlQuery.Dialect#supportsGroupingSets}).
990         */
991        public transient final BooleanProperty EnableGroupingSets =
992                new BooleanProperty(
993                        this, "mondrian.rolap.groupingsets.enable", false);
994    
995        /**
996         * Property that defines whether to ignore measure when non joining
997         * dimension is in the tuple during aggregation.
998         *
999         * <p>If there are unrelated dimensions to a measure in context during
1000         * aggregation, the measure is ignored in the evaluation context. This
1001         * behaviour kicks in only if the cubeusage for this measure has
1002         * IgnoreUnrelatedDimensions attribute set to false.
1003         *
1004         * <p>For example, Gender doesn't join with [Warehouse Sales] measure.
1005         *
1006         * <p>With mondrian.olap.agg.IgnoreMeasureForNonJoiningDimension=true
1007         * Warehouse Sales gets eliminated and is ignored in the aggregate value.
1008         * <blockquote>
1009         * <p>                                        [Store Sales] + [Warehouse Sales]
1010         * SUM({Product.members * Gender.members})    7,913,333.82
1011         * </blockquote>
1012         * <p>With mondrian.olap.agg.IgnoreMeasureForNonJoiningDimension=false
1013         * Warehouse Sales with Gender All level member contributes to the aggregate
1014         * value.
1015         * <blockquote>
1016         * <p>                                        [Store Sales] + [Warehouse Sales]
1017         * SUM({Product.members * Gender.members})    9,290,730.03
1018         * </blockquote>
1019         * <p>On a report where Gender M, F and All members exist a user will see a
1020         * large aggregated value compared to the aggregated value that can be
1021         * arrived at by suming up values against Gender M and F. This can be
1022         * confusing to the user. This feature can be used to eliminate such a
1023         * situation.
1024         */
1025        public transient final BooleanProperty IgnoreMeasureForNonJoiningDimension =
1026                new BooleanProperty(
1027                        this,
1028                        "mondrian.olap.agg.IgnoreMeasureForNonJoiningDimension",
1029                        false);
1030    
1031        /**
1032         * Property determines if elements of dimension (levels, hierarchies, members)
1033         * need to be prefixed with dimension name in MDX query.
1034         * For example when the property is true, the following queries
1035         * will error out. The same queries will work when this property
1036         * is set to false.
1037         * <blockquote>
1038         * <p>
1039         * select {[M]} on 0 from sales
1040         * <p>
1041         * select {[USA]} on 0 from sales
1042         * <p>
1043         * select {[USA].[CA].[Santa Monica]}  on 0 from sales
1044         * </blockquote>
1045         * <p>
1046         * When the property is set to true, any query where elements are
1047         * prefixed with dimension name as below will work
1048         * <blockquote>
1049         * <p>
1050         * select {[Gender].[F]} on 0 from sales
1051         * <p>
1052         * select {[Customers].[Santa Monica]} on 0 from sales
1053         * </blockquote>
1054         * <p>
1055         * Please note that this property does not govern the behaviour where in
1056         * <blockquote>
1057         * <p>
1058         * [Gender].[M]
1059         * </blockquote>
1060         * <p>
1061         * is resolved into a fully qualified
1062         * <blockquote>
1063         * <p>
1064         * [Gender].[All Gender].[M]
1065         * </blockquote>
1066         * <p>
1067         * In a scenario where the schema is very large and dimensions have large
1068         * number of members a MDX query that has a invalid member in it will cause
1069         * mondrian to to go through all the dimensions, levels, hierarchies, members
1070         * and properties trying to resolve the element name. This behaviour consumes
1071         * considerable time and resources on the server. Setting this property to
1072         * true will make it fail fast in a scenario where it is desirable
1073         */
1074        public transient final BooleanProperty NeedDimensionPrefix =
1075            new BooleanProperty(
1076                this, "mondrian.olap.elements.NeedDimensionPrefix", false);
1077    
1078        /**
1079         * Property that determines whether to cache RolapCubeMember objects,
1080         * each of which associates a member of a shared hierarchy with a
1081         * particular cube in which it is being used.
1082         *
1083         * <p>The default is {@code true}, that is, use a cache. If you wish to use
1084         * the member cache control aspects of {@link mondrian.olap.CacheControl},
1085         * you must set this property to {@code false}.</p>
1086         *
1087         * <p>In future, we plan to make RolapCubeMember more lightweight to
1088         * construct, and we will probably obsolete this cache and this
1089         * property.</p>
1090         */
1091        public transient final BooleanProperty EnableRolapCubeMemberCache =
1092            new BooleanProperty(
1093                this, "mondrian.rolap.EnableRolapCubeMemberCache", true);
1094    
1095        /**
1096         * Property that controls the behavior of
1097         * {@link Property#SOLVE_ORDER solve order} of calculated members and sets.
1098         *
1099         * <p>Valid values are "absolute" and "scoped" (the default). See
1100         * {@link SolveOrderModeEnum} for details.</p>
1101         */
1102        public transient final StringProperty SolveOrderMode =
1103            new StringProperty(
1104                this, "mondrian.rolap.SolveOrderMode", SolveOrderModeEnum.ABSOLUTE.name());
1105    
1106        /**
1107         * Strategies for applying solve order, exposed via the property
1108         * {@link MondrianProperties#SolveOrderMode}.
1109         */
1110        public enum SolveOrderModeEnum {
1111    
1112            /**
1113             * The SOLVE_ORDER value is absolute regardless of
1114             * where it is defined; e.g. a query defined calculated
1115             * member with a SOLVE_ORDER of 1 always takes precedence
1116             * over a cube defined value of 2.
1117             *
1118             * <p>Compatible with Analysis Services 2000, and default behavior
1119             * up to mondrian-3.0.3.
1120             */
1121            ABSOLUTE,
1122    
1123            /**
1124             * Cube calculated members are resolved before any session
1125             * scope calculated members, and session scope members are
1126             * resolved before any query defined calculation.  The
1127             * SOLVE_ORDER value only applies within the scope in which
1128             * it was defined.
1129             *
1130             * <p>Compatible with Analysis Services 2005, and default behavior
1131             * from mondrian-3.0.4 and later.
1132             */
1133            SCOPED;
1134        }
1135    }
1136    
1137    // End MondrianProperties.java