JiveGlobals.java 32.5 KB
Newer Older
Matt Tucker's avatar
Matt Tucker committed
1 2 3 4 5
/**
 * $RCSfile$
 * $Revision$
 * $Date$
 *
Matt Tucker's avatar
Matt Tucker committed
6
 * Copyright (C) 2004 Jive Software. All rights reserved.
Matt Tucker's avatar
Matt Tucker committed
7
 *
Matt Tucker's avatar
Matt Tucker committed
8 9
 * This software is published under the terms of the GNU Public License (GPL),
 * a copy of which is included in this distribution.
Matt Tucker's avatar
Matt Tucker committed
10 11 12 13
 */

package org.jivesoftware.messenger;

14 15
import org.jivesoftware.util.*;

Matt Tucker's avatar
Matt Tucker committed
16 17 18 19
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.text.DateFormat;
20 21
import java.util.*;

Matt Tucker's avatar
Matt Tucker committed
22
import org.dom4j.Document;
Matt Tucker's avatar
Matt Tucker committed
23
import org.dom4j.io.SAXReader;
Matt Tucker's avatar
Matt Tucker committed
24

25 26
import javax.naming.InitialContext;

Matt Tucker's avatar
Matt Tucker committed
27
/**
Matt Tucker's avatar
Matt Tucker committed
28 29
 * Controls Jive properties. Jive properties are only meant to be set and retrieved
 * by core Jive classes.
Matt Tucker's avatar
Matt Tucker committed
30
 * <p/>
Matt Tucker's avatar
Matt Tucker committed
31
 * The location of the messengerHome directory should be specified one of
Matt Tucker's avatar
Matt Tucker committed
32 33
 * three ways:
 * <ol>
Matt Tucker's avatar
Matt Tucker committed
34 35 36
 * <li>Set a Java system property named <tt>messengerHome</tt> with the full path to your
 * messengerHome directory.
 * <li>Indicate its value in the <tt>messenger_init.xml</tt> file. This
Matt Tucker's avatar
Matt Tucker committed
37
 * is a simple xml file that should look something like:<br>
Matt Tucker's avatar
Matt Tucker committed
38
 * <tt><messengerHome>c:\JiveMessenger</messengerHome></tt> (Windows) <br>
Matt Tucker's avatar
Matt Tucker committed
39
 * or <br>
Matt Tucker's avatar
Matt Tucker committed
40
 * <tt><messengerHome>/var/JiveMessenger</messengerHome></tt> (Unix) <p>
Matt Tucker's avatar
Matt Tucker committed
41 42
 * <p/>
 * The file must be in your classpath so that it can be loaded by Java's classloader.
Matt Tucker's avatar
Matt Tucker committed
43
 * <li>Use another class in your VM to set the <tt>JiveGlobals.messengerHome</tt> variable.
Matt Tucker's avatar
Matt Tucker committed
44 45 46 47
 * This must be done before the rest of Jive starts up, for example: in a servlet that
 * is set to run as soon as the appserver starts up.
 * </ol>
 * <p/>
Matt Tucker's avatar
Matt Tucker committed
48
 * All property names must be in the form <code>prop.name</code> - parts of the name must
Matt Tucker's avatar
Matt Tucker committed
49 50 51 52
 * be seperated by ".". The value can be any valid String, including strings with line breaks.
 */
public class JiveGlobals {

53 54 55
    private static String JIVE_CONFIG_FILENAME = "jive-messenger.xml";

    private static final String DEFAULT_CHAR_ENCODING = "UTF-8";
Matt Tucker's avatar
Matt Tucker committed
56 57 58 59 60

    /**
     * Location of the jiveHome directory. All configuration files should be
     * located here.
     */
Matt Tucker's avatar
Matt Tucker committed
61
    public static String messengerHome = null;
Matt Tucker's avatar
Matt Tucker committed
62 63 64

    public static boolean failedLoading = false;

65 66
    private static XMLProperties xmlProperties = null;
    private static JiveProperties properties = null;
Matt Tucker's avatar
Matt Tucker committed
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82

    private static Locale locale = null;
    private static TimeZone timeZone = null;
    private static String characterEncoding = null;
    private static DateFormat dateFormat = null;
    private static DateFormat dateTimeFormat = null;

    /**
     * Returns the global Locale used by Jive. A locale specifies language
     * and country codes, and is used for internationalization. The default
     * locale is system dependant - Locale.getDefault().
     *
     * @return the global locale used by Jive.
     */
    public static Locale getLocale() {
        if (locale == null) {
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
            if (properties != null) {
                String language = (String)properties.get("locale.language");
                if (language == null) {
                    language = "";
                }
                String country = (String)properties.get("locale.country");
                if (country == null) {
                    country = "";
                }
                // If no locale info is specified, return the system default Locale.
                if (language.equals("") && country.equals("")) {
                    locale = Locale.getDefault();
                }
                else {
                    locale = new Locale(language, country);
                }
            }
            else {
                return Locale.getDefault();
            }
Matt Tucker's avatar
Matt Tucker committed
103
        }
104
        return locale;
Matt Tucker's avatar
Matt Tucker committed
105 106 107 108 109 110 111 112 113 114 115 116
    }

    /**
     * Sets the global locale used by Jive. A locale specifies language
     * and country codes, and is used for formatting dates and numbers.
     * The default locale is Locale.US.
     *
     * @param newLocale the global Locale for Jive.
     */
    public static void setLocale(Locale newLocale) {
        locale = newLocale;
        // Save values to Jive properties.
117 118
        setProperty("locale.country", locale.getCountry());
        setProperty("locale.language", locale.getLanguage());
Matt Tucker's avatar
Matt Tucker committed
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
        // Reset the date formatter objects
        dateFormat = DateFormat.getDateInstance(DateFormat.MEDIUM, locale);
        dateTimeFormat = DateFormat.getDateTimeInstance(DateFormat.MEDIUM,
                DateFormat.MEDIUM, locale);
        dateFormat.setTimeZone(timeZone);
        dateTimeFormat.setTimeZone(timeZone);
    }

    /**
     * Returns the character set that Jive uses for encoding. This
     * is used for displaying content in skins, sending email watch
     * updates, etc. The default encoding is ISO-8895-1, which is suitable for
     * most Latin languages. If you need to support double byte character sets
     * such as Chinese or Japanese, it's recommend that you use utf-8
     * as the charset (Unicode). Unicode offers simultaneous support for a
     * large number of languages and is easy to convert into native charsets
     * as necessary. You may also specifiy any other charset that is supported
     * by your JVM. A list of encodings supported by the Sun JVM can be found
     * <a href="http://java.sun.com/j2se/1.3/docs/guide/intl/encoding.doc.html">
     * here.</a><p>
139
     *
Matt Tucker's avatar
Matt Tucker committed
140 141 142 143 144 145 146 147 148
     * In order for a particular encoding to work (such as Unicode), your
     * application server and database may need to be specially configured.
     * Please consult your server documentation for more information. For
     * example, SQLServer has a special column type for Unicode text, and the
     * Resin application server can be configured to use a custom charset by
     * adding a &lt;character-encoding&gt; element to the web.xml/resin.conf
     * file. Any Servlet 2.3 compliant application servers also supports the
     * method HttpServletRequest.setCharacterEncoding(String). A Servlet 2.3
     * Filter called SetCharacterEncodingFilter is installed in the default
149
     * Jive Messenger web.xml file, which  will set the incoming character encoding
Matt Tucker's avatar
Matt Tucker committed
150 151 152 153 154
     * to the one reported by this method.
     *
     * @return the global Jive character encoding.
     */
    public static String getCharacterEncoding() {
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
        if (characterEncoding == null) {
            if (properties != null) {
                String charEncoding = (String)properties.get("locale.characterEncoding");
                if (charEncoding != null) {
                    characterEncoding = charEncoding;
                }
                else {
                    // The default encoding is ISO-8859-1. We use the version of
                    // the encoding name that seems to be most widely compatible.
                    characterEncoding = DEFAULT_CHAR_ENCODING;
                }
            }
            else {
                return DEFAULT_CHAR_ENCODING;
            }
Matt Tucker's avatar
Matt Tucker committed
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
        }
        return characterEncoding;
    }

    /**
     * Sets the character set that Jive uses for encoding. This
     * is used for displaying content in skins, sending email watch
     * updates, etc. The default encoding is ISO-8859-1, which is suitable for
     * most Latin languages. If you need to support double byte character sets
     * such as Chinese or Japanese, it's recommend that you use utf-8
     * as the charset (Unicode). Unicode offers simultaneous support for a
     * large number of languages and is easy to convert into native charsets
     * as necessary. You may also specifiy any other charset that is supported
     * by your JVM. A list of encodings supported by the Sun JVM can be found
     * <a href="http://java.sun.com/j2se/1.3/docs/guide/intl/encoding.doc.html">
     * here.</a><p>
186
     *
Matt Tucker's avatar
Matt Tucker committed
187 188 189 190 191 192 193 194 195
     * In order for a particular encoding to work (such as Unicode), your
     * application server and database may need to be specially configured.
     * Please consult your server documentation for more information. For
     * example, SQLServer has a special column type for Unicode text, and the
     * Resin application server can be configured to use a custom charset by
     * adding a &lt;character-encoding&gt; element to the web.xml/resin.conf
     * file. Any Servlet 2.3 compliant application servers also supports the
     * method HttpServletRequest.setCharacterEncoding(String). A Servlet 2.3
     * Filter called SetCharacterEncodingFilter is installed in the default
196
     * Jive Messenger web.xml file, which  will set the incoming character encoding
Matt Tucker's avatar
Matt Tucker committed
197 198 199 200 201 202
     * to the one reported by this method.
     *
     * @param characterEncoding the global Jive character encoding.
     */
    public static void setCharacterEncoding(String characterEncoding) {
        JiveGlobals.characterEncoding = characterEncoding;
203
        setProperty("locale.characterEncoding", characterEncoding);
Matt Tucker's avatar
Matt Tucker committed
204 205 206 207 208 209 210 211 212
    }

    /**
     * Returns the global TimeZone used by Jive. The default is the VM's
     * default time zone.
     *
     * @return the global time zone used by Jive.
     */
    public static TimeZone getTimeZone() {
213 214 215 216 217 218 219 220 221 222 223 224 225
        if (timeZone == null) {
            if (properties != null) {
                String timeZoneID = (String)properties.get("locale.timeZone");
                if (timeZoneID == null) {
                    timeZone = TimeZone.getDefault();
                }
                else {
                    timeZone = TimeZone.getTimeZone(timeZoneID);
                }
            }
            else {
                return TimeZone.getDefault();
            }
Matt Tucker's avatar
Matt Tucker committed
226 227 228 229 230 231 232 233 234 235 236 237
        }
        return timeZone;
    }

    /**
     * Sets the global time zone used by Jive. The default time zone is the VM's
     * time zone.
     */
    public static void setTimeZone(TimeZone newTimeZone) {
        timeZone = newTimeZone;
        dateFormat.setTimeZone(timeZone);
        dateTimeFormat.setTimeZone(timeZone);
238
        setProperty("locale.timeZone", timeZone.getID());
Matt Tucker's avatar
Matt Tucker committed
239 240 241 242 243 244 245 246 247
    }

    /**
     * Formats a Date object to return a date using the global locale.
     *
     * @param date the Date to format.
     * @return a String representing the date.
     */
    public static String formatDate(Date date) {
248 249 250 251 252 253 254 255 256 257
        if (dateFormat == null) {
            if (properties != null) {
                dateFormat = DateFormat.getDateInstance(DateFormat.MEDIUM, getLocale());
                dateFormat.setTimeZone(getTimeZone());
            }
            else {
                DateFormat instance = DateFormat.getDateInstance(DateFormat.MEDIUM, getLocale());
                instance.setTimeZone(getTimeZone());
                return instance.format(date);
            }
Matt Tucker's avatar
Matt Tucker committed
258 259 260 261 262 263 264 265 266 267 268
        }
        return dateFormat.format(date);
    }

    /**
     * Formats a Date object to return a date and time using the global locale.
     *
     * @param date the Date to format.
     * @return a String representing the date and time.
     */
    public static String formatDateTime(Date date) {
269 270 271 272 273 274 275 276 277 278 279 280
        if (dateTimeFormat == null) {
            if (properties != null) {
                dateTimeFormat = DateFormat.getDateTimeInstance(DateFormat.MEDIUM,
                        DateFormat.MEDIUM, getLocale());
                dateTimeFormat.setTimeZone(getTimeZone());
            }
            else {
                DateFormat instance = DateFormat.getDateTimeInstance(DateFormat.MEDIUM,
                        DateFormat.MEDIUM, getLocale());
                instance.setTimeZone(getTimeZone());
                return instance.format(date);
            }
Matt Tucker's avatar
Matt Tucker committed
281 282 283 284 285
        }
        return dateTimeFormat.format(date);
    }

    /**
Matt Tucker's avatar
Matt Tucker committed
286
     * Returns the location of the <code>messengerHome</code> directory.
Matt Tucker's avatar
Matt Tucker committed
287
     *
Matt Tucker's avatar
Matt Tucker committed
288
     * @return the location of the messengerHome dir.
Matt Tucker's avatar
Matt Tucker committed
289
     */
Matt Tucker's avatar
Matt Tucker committed
290 291
    public static String getMessengerHome() {
        if (messengerHome == null) {
292
            loadSetupProperties();
Matt Tucker's avatar
Matt Tucker committed
293
        }
Matt Tucker's avatar
Matt Tucker committed
294
        return messengerHome;
Matt Tucker's avatar
Matt Tucker committed
295 296 297
    }

    /**
Matt Tucker's avatar
Matt Tucker committed
298 299 300
     * Sets the location of the <code>messengerHome</code> directory. This method
     * is only intended to be used during setup and should <b>not</b> set called
     * in normal Messenger operations.
301
     */
Matt Tucker's avatar
Matt Tucker committed
302
    public static void setMessengerHome(String mHome) {
303 304 305
        properties = null;
        xmlProperties = null;
        failedLoading = false;
Matt Tucker's avatar
Matt Tucker committed
306
        messengerHome = mHome;
307 308 309 310 311 312 313
        locale = null;
        timeZone = null;
        characterEncoding = null;
        dateFormat = null;
        dateTimeFormat = null;

        loadSetupProperties();
Matt Tucker's avatar
Matt Tucker committed
314 315 316
        System.err.println("Warning - messengerHome is being reset to " + mHome +
                "! Resetting the messengerHome is a normal part of the setup process, " +
                "however it should not occur during the normal operations of Jive Messenger.");
317 318 319 320
    }

    /**
     * Returns a local property. Local properties are stored in the file
Matt Tucker's avatar
Matt Tucker committed
321
     * <tt>jive-messenger.xml</tt> that exists in the <tt>jiveMessenger/conf</tt> directory.
Matt Tucker's avatar
Matt Tucker committed
322 323 324 325 326 327 328 329 330 331 332 333 334
     * Properties are always specified as "foo.bar.prop", which would map to
     * the following entry in the XML file:
     * <pre>
     * &lt;foo&gt;
     *     &lt;bar&gt;
     *         &lt;prop&gt;some value&lt;/prop&gt;
     *     &lt;/bar&gt;
     * &lt;/foo&gt;
     * </pre>
     *
     * @param name the name of the property to return.
     * @return the property value specified by name.
     */
335 336 337
    public static String getXMLProperty(String name) {
        if (xmlProperties == null) {
            loadSetupProperties();
Matt Tucker's avatar
Matt Tucker committed
338 339
        }

Matt Tucker's avatar
Matt Tucker committed
340
        // messengerHome not loaded?
341
        if (xmlProperties == null) {
Matt Tucker's avatar
Matt Tucker committed
342 343 344
            return null;
        }

345
        return xmlProperties.getProperty(name);
Matt Tucker's avatar
Matt Tucker committed
346 347
    }

Matt Tucker's avatar
Matt Tucker committed
348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383
    /**
     * Returns a local property. Local properties are stored in the file
     * <tt>jive-messenger.xml</tt> that exists in the <tt>jiveMessenger/conf</tt> directory.
     * Properties are always specified as "foo.bar.prop", which would map to
     * the following entry in the XML file:
     * <pre>
     * &lt;foo&gt;
     *     &lt;bar&gt;
     *         &lt;prop&gt;some value&lt;/prop&gt;
     *     &lt;/bar&gt;
     * &lt;/foo&gt;
     * </pre>
     *
     * If the specified property can't be found, the <tt>defaultValue</tt> will be returned.
     *
     * @param name the name of the property to return.
     * @param defaultValue the default value for the property.
     * @return the property value specified by name.
     */
    public static String getXMLProperty(String name, String defaultValue) {
        if (xmlProperties == null) {
            loadSetupProperties();
        }

        // messengerHome not loaded?
        if (xmlProperties == null) {
            return null;
        }

        String value = xmlProperties.getProperty(name);
        if (value == null) {
            value = defaultValue;
        }
        return value;
    }

Matt Tucker's avatar
Matt Tucker committed
384
    /**
385 386
     * Returns an integer value local property. Local properties are stored in the file
     * <tt>jive_forums.xml</tt> that exists in the <tt>jiveHome</tt> directory.
Matt Tucker's avatar
Matt Tucker committed
387 388 389 390 391 392 393 394 395 396
     * Properties are always specified as "foo.bar.prop", which would map to
     * the following entry in the XML file:
     * <pre>
     * &lt;foo&gt;
     *     &lt;bar&gt;
     *         &lt;prop&gt;some value&lt;/prop&gt;
     *     &lt;/bar&gt;
     * &lt;/foo&gt;
     * </pre>
     *
397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430
     * If the specified property can't be found, or if the value is not a number, the
     * <tt>defaultValue</tt> will be returned.
     *
     * @param name the name of the property to return.
     * @param defaultValue value returned if the property could not be loaded or was not
     *      a number.
     * @return the property value specified by name or <tt>defaultValue</tt>.
     */
    public static int getXMLProperty(String name, int defaultValue) {
        String value = getProperty(name);
        if (value != null) {
            try {
                return Integer.parseInt(value);
            }
            catch (NumberFormatException nfe) { }
        }
        return defaultValue;
    }

    /**
     * Sets a local property. If the property doesn't already exists, a new
     * one will be created. Local properties are stored in the file
     * <tt>jive_forums.xml</tt> that exists in the <tt>jiveHome</tt> directory.
     * Properties are always specified as "foo.bar.prop", which would map to
     * the following entry in the XML file:
     * <pre>
     * &lt;foo&gt;
     *     &lt;bar&gt;
     *         &lt;prop&gt;some value&lt;/prop&gt;
     *     &lt;/bar&gt;
     * &lt;/foo&gt;
     * </pre>
     *
     * @param name the name of the property being set.
Matt Tucker's avatar
Matt Tucker committed
431 432
     * @param value the value of the property being set.
     */
433 434 435
    public static void setXMLProperty(String name, String value) {
        if (xmlProperties == null) {
            loadSetupProperties();
Matt Tucker's avatar
Matt Tucker committed
436 437
        }

438 439 440
        // jiveHome not loaded?
        if (xmlProperties != null) {
            xmlProperties.setProperty(name, value);
Matt Tucker's avatar
Matt Tucker committed
441 442 443 444
        }
    }

    /**
445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516
     * Sets multiple local properties at once. If a property doesn't already exists, a new
     * one will be created. Local properties are stored in the file
     * <tt>jive_forums.xml</tt> that exists in the <tt>jiveHome</tt> directory.
     * Properties are always specified as "foo.bar.prop", which would map to
     * the following entry in the XML file:
     * <pre>
     * &lt;foo&gt;
     *     &lt;bar&gt;
     *         &lt;prop&gt;some value&lt;/prop&gt;
     *     &lt;/bar&gt;
     * &lt;/foo&gt;
     * </pre>
     *
     * @param propertyMap a map of properties, keyed on property name.
     */
    public static void setXMLProperties(Map propertyMap) {
        if (xmlProperties == null) {
            loadSetupProperties();
        }

        if (xmlProperties != null) {
            xmlProperties.setProperties(propertyMap);
        }
    }

    /**
     * Return all immediate children property values of a parent local property as a list of strings,
     * or an empty list if there are no children. For example, given
     * the properties <tt>X.Y.A</tt>, <tt>X.Y.B</tt>, <tt>X.Y.C</tt> and <tt>X.Y.C.D</tt>, then
     * the immediate child properties of <tt>X.Y</tt> are <tt>A</tt>, <tt>B</tt>, and
     * <tt>C</tt> (the value of <tt>C.D</tt> would not be returned using this method).<p>
     *
     * Local properties are stored in the file <tt>jive_forums.xml</tt> that exists
     * in the <tt>jiveHome</tt> directory. Properties are always specified as "foo.bar.prop",
     * which would map to the following entry in the XML file:
     * <pre>
     * &lt;foo&gt;
     *     &lt;bar&gt;
     *         &lt;prop&gt;some value&lt;/prop&gt;
     *     &lt;/bar&gt;
     * &lt;/foo&gt;
     * </pre>
     *
     *
     * @param parent the name of the parent property to return the children for.
     * @return all child property values for the given parent.
     */
    public static List getXMLProperties(String parent) {
        if (xmlProperties == null) {
            loadSetupProperties();
        }

        // jiveHome not loaded?
        if (xmlProperties == null) {
            return Collections.EMPTY_LIST;
        }

        String[] propNames = xmlProperties.getChildrenProperties(parent);
        List values = new ArrayList();
        for (int i = 0; i < propNames.length; i++) {
            String propName = propNames[i];
            String value = getProperty(parent + "." + propName);
            if (value != null) {
                values.add(value);
            }
        }

        return values;
    }

    /**
     * Deletes a locale property. If the property doesn't exist, the method
Matt Tucker's avatar
Matt Tucker committed
517 518 519 520
     * does nothing.
     *
     * @param name the name of the property to delete.
     */
521 522 523 524 525 526
    public static void deleteXMLProperty(String name) {
        if (xmlProperties == null) {
            loadSetupProperties();
        }
        xmlProperties.deleteProperty(name);
    }
Matt Tucker's avatar
Matt Tucker committed
527

528 529 530 531 532 533 534 535
    /**
     * Returns a Jive property.
     *
     * @param name the name of the property to return.
     * @return the property value specified by name.
     */
    public static String getProperty(String name) {
        if (properties == null) {
Matt Tucker's avatar
Matt Tucker committed
536 537 538
            if (isSetupMode()) {
                return null;
            }
539 540 541 542 543 544 545 546 547 548 549 550 551 552 553
            properties = JiveProperties.getInstance();
        }
        return (String)properties.get(name);
    }

    /**
     * Returns a Jive property. If the specified property doesn't exist, the
     * <tt>defaultValue</tt> will be returned.
     *
     * @param name the name of the property to return.
     * @param defaultValue value returned if the property doesn't exist.
     * @return the property value specified by name.
     */
    public static String getProperty(String name, String defaultValue) {
        if (properties == null) {
Matt Tucker's avatar
Matt Tucker committed
554 555 556
            if (isSetupMode()) {
                return defaultValue;
            }
557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629
            properties = JiveProperties.getInstance();
        }
        String value = (String)properties.get(name);
        if (value != null) {
            return value;
        }
        else {
            return defaultValue;
        }
    }

    /**
     * Returns an integer value Jive property. If the specified property doesn't exist, the
     * <tt>defaultValue</tt> will be returned.
     *
     * @param name the name of the property to return.
     * @param defaultValue value returned if the property doesn't exist or was not
     *      a number.
     * @return the property value specified by name or <tt>defaultValue</tt>.
     */
    public static int getIntProperty(String name, int defaultValue) {
        String value = getProperty(name);
        if (value != null) {
            try {
                return Integer.parseInt(value);
            }
            catch (NumberFormatException nfe) { }
        }
        return defaultValue;
    }

    /**
     * Returns a boolean value Jive property.
     *
     * @param name the name of the property to return.
     * @return true if the property value exists and is set to <tt>"true"</tt> (ignoring case).
     *      Otherwise <tt>false</tt> is returned.
     */
    public static boolean getBooleanProperty(String name) {
        return Boolean.valueOf(getProperty(name)).booleanValue();
    }

    /**
     * Returns a boolean value Jive property. If the property doesn't exist, the <tt>defaultValue</tt>
     * will be returned.
     *
     * If the specified property can't be found, or if the value is not a number, the
     * <tt>defaultValue</tt> will be returned.
     *
     * @param name the name of the property to return.
     * @param defaultValue value returned if the property doesn't exist.
     * @return true if the property value exists and is set to <tt>"true"</tt> (ignoring case).
     *      Otherwise <tt>false</tt> is returned.
     */
    public static boolean getBooleanProperty(String name, boolean defaultValue) {
        String value = getProperty(name);
        if (value != null) {
            return Boolean.valueOf(getProperty(name)).booleanValue();
        }
        else {
            return defaultValue;
        }
    }

    /**
     * Return all immediate children property names of a parent Jive property as a list of strings,
     * or an empty list if there are no children. For example, given
     * the properties <tt>X.Y.A</tt>, <tt>X.Y.B</tt>, <tt>X.Y.C</tt> and <tt>X.Y.C.D</tt>, then
     * the immediate child properties of <tt>X.Y</tt> are <tt>A</tt>, <tt>B</tt>, and
     * <tt>C</tt> (<tt>C.D</tt> would not be returned using this method).<p>
     *
     * @return a List of all immediate children property names (Strings).
     */
Matt Tucker's avatar
Matt Tucker committed
630
    public static List<String> getPropertyNames(String parent) {
631
        if (properties == null) {
Matt Tucker's avatar
Matt Tucker committed
632 633 634
            if (isSetupMode()) {
                return new ArrayList<String>();
            }
635 636
            properties = JiveProperties.getInstance();
        }
Matt Tucker's avatar
Matt Tucker committed
637
        return new ArrayList<String>(properties.getChildrenNames(parent));
638 639 640 641 642 643 644 645 646 647 648 649
    }

    /**
     * Return all immediate children property values of a parent Jive property as a list of strings,
     * or an empty list if there are no children. For example, given
     * the properties <tt>X.Y.A</tt>, <tt>X.Y.B</tt>, <tt>X.Y.C</tt> and <tt>X.Y.C.D</tt>, then
     * the immediate child properties of <tt>X.Y</tt> are <tt>X.Y.A</tt>, <tt>X.Y.B</tt>, and
     * <tt>X.Y.C</tt> (the value of <tt>X.Y.C.D</tt> would not be returned using this method).<p>
     *
     * @param parent the name of the parent property to return the children for.
     * @return all child property values for the given parent.
     */
650
    public static List<String> getProperties(String parent) {
Matt Tucker's avatar
Matt Tucker committed
651
        if (properties == null) {
Matt Tucker's avatar
Matt Tucker committed
652 653 654
            if (isSetupMode()) {
                return new ArrayList<String>();
            }
655 656 657
            properties = JiveProperties.getInstance();
        }

658 659
        Collection<String> propertyNames = properties.getChildrenNames(parent);
        List<String> values = new ArrayList<String>();
660 661 662 663 664 665
        for (Iterator i=propertyNames.iterator(); i.hasNext(); ) {
            String propName = (String)i.next();
            String value = getProperty(propName);
            if (value != null) {
                values.add(value);
            }
Matt Tucker's avatar
Matt Tucker committed
666
        }
667 668 669 670 671 672 673 674 675

        return values;
    }

    /**
     * Returns all Jive property names.
     *
     * @return a List of all property names (Strings).
     */
Matt Tucker's avatar
Matt Tucker committed
676
    public static List<String> getPropertyNames() {
677
        if (properties == null) {
Matt Tucker's avatar
Matt Tucker committed
678 679 680
            if (isSetupMode()) {
                return new ArrayList<String>();
            }
681
            properties = JiveProperties.getInstance();
Matt Tucker's avatar
Matt Tucker committed
682
        }
Matt Tucker's avatar
Matt Tucker committed
683
        return new ArrayList<String>(properties.getPropertyNames());
684 685 686 687 688 689 690 691 692 693 694
    }

    /**
     * Sets a Jive property. If the property doesn't already exists, a new
     * one will be created.
     *
     * @param name the name of the property being set.
     * @param value the value of the property being set.
     */
    public static void setProperty(String name, String value) {
        if (properties == null) {
Matt Tucker's avatar
Matt Tucker committed
695 696 697
            if (isSetupMode()) {
                return;
            }
698 699 700 701 702 703 704 705 706 707 708 709 710
            properties = JiveProperties.getInstance();
        }
        properties.put(name, value);
    }

   /**
     * Sets multiple Jive properties at once. If a property doesn't already exists, a new
     * one will be created.
     *
     * @param propertyMap a map of properties, keyed on property name.
     */
    public static void setProperties(Map propertyMap) {
        if (properties == null) {
Matt Tucker's avatar
Matt Tucker committed
711 712 713
            if (isSetupMode()) {
                return;
            }
714 715 716 717 718 719 720 721 722 723 724 725 726 727
            properties = JiveProperties.getInstance();
        }

        properties.putAll(propertyMap);
    }

    /**
     * Deletes a Jive property. If the property doesn't exist, the method
     * does nothing. All children of the property will be deleted as well.
     *
     * @param name the name of the property to delete.
     */
    public static void deleteProperty(String name) {
        if (properties == null) {
Matt Tucker's avatar
Matt Tucker committed
728 729 730
            if (isSetupMode()) {
                return;
            }
731 732 733 734 735 736 737 738 739 740 741 742 743
            properties = JiveProperties.getInstance();;
        }
        properties.remove(name);
    }

   /**
    * Allows the name of the local config file name to be changed. The
    * default is "jive_startup.xml".
    *
    * @param configName the name of the config file.
    */
    public static void setConfigName(String configName) {
        JIVE_CONFIG_FILENAME = configName;
Matt Tucker's avatar
Matt Tucker committed
744 745
    }

Matt Tucker's avatar
Matt Tucker committed
746 747 748 749 750 751 752 753 754
    /**
     * Returns true if in setup mode.
     *
     * @return true if in setup mode.
     */
    private static boolean isSetupMode() {
        return !(Boolean.valueOf(JiveGlobals.getXMLProperty("setup")).booleanValue());
    }

Matt Tucker's avatar
Matt Tucker committed
755 756
    /**
     * Loads properties if necessary. Property loading must be done lazily so
Matt Tucker's avatar
Matt Tucker committed
757
     * that we give outside classes a chance to set <tt>messengerHome</tt>.
Matt Tucker's avatar
Matt Tucker committed
758
     */
759
    private synchronized static void loadSetupProperties() {
Matt Tucker's avatar
Matt Tucker committed
760 761 762
        if (failedLoading) {
            return;
        }
763 764 765 766
        if (xmlProperties == null) {
            // If jiveHome is still null, no outside process has set it and
            // we have to attempt to load the value from jive_init.xml,
            // which must be in the classpath.
Matt Tucker's avatar
Matt Tucker committed
767 768
            if (messengerHome == null) {
                messengerHome = new InitPropLoader().getMessengerHome();
Matt Tucker's avatar
Matt Tucker committed
769
            }
770
            // If that failed, try loading it from JNDI
Matt Tucker's avatar
Matt Tucker committed
771
            if (messengerHome == null) {
Matt Tucker's avatar
Matt Tucker committed
772
                try {
773
                    InitialContext context = new InitialContext();
Matt Tucker's avatar
Matt Tucker committed
774
                    messengerHome = (String)context.lookup("java:comp/env/messengerHome");
Matt Tucker's avatar
Matt Tucker committed
775
                }
776
                catch (Exception e) { }
Matt Tucker's avatar
Matt Tucker committed
777
            }
778
            // Finally, try to load it jiveHome as a system property.
Matt Tucker's avatar
Matt Tucker committed
779 780
            if (messengerHome == null) {
                messengerHome = System.getProperty("messengerHome");
Matt Tucker's avatar
Matt Tucker committed
781
            }
782 783 784 785

            if(messengerHome == null){
                try {
                    messengerHome = new File("..").getCanonicalPath();
786 787 788 789 790 791 792 793 794 795 796 797
                    if(!new File(messengerHome, "conf/jive-messenger.xml").exists()){
                        messengerHome = null;
                    }
                }
                catch (IOException e) {
                    e.printStackTrace();
                }
            }

             if(messengerHome == null){
                try {
                    messengerHome = new File("").getCanonicalPath();
798 799 800 801 802 803 804 805 806
                    if(!new File(messengerHome, "conf/jive-messenger.xml").exists()){
                        messengerHome = null;
                    }
                }
                catch (IOException e) {
                    e.printStackTrace();
                }
            }

Matt Tucker's avatar
Matt Tucker committed
807 808
            // If still null, finding messengerHome failed.
            if (messengerHome == null) {
Matt Tucker's avatar
Matt Tucker committed
809
                failedLoading = true;
810
                StringBuffer msg = new StringBuffer();
Matt Tucker's avatar
Matt Tucker committed
811
                msg.append("Critical Error! The messengerHome directory could not be loaded, \n");
812
                msg.append("which will prevent the application from working correctly.\n\n");
Matt Tucker's avatar
Matt Tucker committed
813 814 815 816 817 818 819 820
                msg.append("You must set messengerHome in one of four ways:\n");
                msg.append("    1) Set a servlet init parameter named messengerHome.\n");
                msg.append("    2) Add a messenger_init.xml file to your classpath, which points \n ");
                msg.append("       to messengerHome. Normally, this file will be in WEB-INF/classes.\n");
                msg.append("    3) Set the JNDI value \"java:comp/env/messengerHome\" with a String \n");
                msg.append("       that points to your messengerHome directory. \n");
                msg.append("    4) Set the Java system property \"messengerHome\".\n\n");
                msg.append("Further instructions for setting messengerHome can be found in the \n");
821 822
                msg.append("installation documentation.");
                System.err.println(msg.toString());
Matt Tucker's avatar
Matt Tucker committed
823 824
                return;
            }
825 826 827
            // Create a manager with the full path to the xml config file.
            try {
                // Do a permission check on the jiveHome directory:
Matt Tucker's avatar
Matt Tucker committed
828 829 830
                File mh = new File(messengerHome);
                if (!mh.exists()) {
                    Log.error("Error - the specified messengerHome directory does not exist (" + messengerHome + ")");
Matt Tucker's avatar
Matt Tucker committed
831
                }
832
                else {
Matt Tucker's avatar
Matt Tucker committed
833
                    if (!mh.canRead() || !mh.canWrite()) {
834
                        Log.error("Error - the user running this Jive application can not read and write to the "
Matt Tucker's avatar
Matt Tucker committed
835
                                + "specified jiveHome directory (" + messengerHome + "). Please grant the executing user "
836 837
                                + "read and write perms.");
                    }
Matt Tucker's avatar
Matt Tucker committed
838
                }
Matt Tucker's avatar
Matt Tucker committed
839 840
                xmlProperties = new XMLProperties(messengerHome + File.separator + "conf" +
                        File.separator + JIVE_CONFIG_FILENAME);
Matt Tucker's avatar
Matt Tucker committed
841
            }
842 843 844 845
            catch (IOException ioe) {
                Log.error(ioe);
                failedLoading = true;
                return;
Matt Tucker's avatar
Matt Tucker committed
846 847 848 849 850 851 852 853 854 855 856 857
            }
        }
    }
}

/**
 * A very small class to load the messenger_init.properties file. The class is
 * needed since loading files from the classpath in a static context often
 * fails.
 */
class InitPropLoader {

Matt Tucker's avatar
Matt Tucker committed
858 859
    public String getMessengerHome() {
        String messengerHome = null;
Matt Tucker's avatar
Matt Tucker committed
860 861 862 863
        InputStream in = null;
        try {
            in = getClass().getResourceAsStream("/messenger_init.xml");
            if (in != null) {
Matt Tucker's avatar
Matt Tucker committed
864 865
                SAXReader reader = new SAXReader();
                Document doc = reader.read(in);
Matt Tucker's avatar
Matt Tucker committed
866
                messengerHome = doc.getRootElement().getText();
Matt Tucker's avatar
Matt Tucker committed
867 868 869
            }
        }
        catch (Exception e) {
Matt Tucker's avatar
Matt Tucker committed
870
            Log.error("Error loading messenger_init.xml to find messengerHome.", e);
Matt Tucker's avatar
Matt Tucker committed
871 872
        }
        finally {
Matt Tucker's avatar
Matt Tucker committed
873 874
            try { if (in != null) { in.close(); } }
            catch (Exception e) { }
Matt Tucker's avatar
Matt Tucker committed
875
        }
Matt Tucker's avatar
Matt Tucker committed
876 877
        if (messengerHome != null) {
            messengerHome = messengerHome.trim();
Matt Tucker's avatar
Matt Tucker committed
878
            // Remove trailing slashes.
Matt Tucker's avatar
Matt Tucker committed
879 880
            while (messengerHome.endsWith("/") || messengerHome.endsWith("\\")) {
                messengerHome = messengerHome.substring(0, messengerHome.length() - 1);
Matt Tucker's avatar
Matt Tucker committed
881 882
            }
        }
Matt Tucker's avatar
Matt Tucker committed
883 884
        if ("".equals(messengerHome)) {
            messengerHome = null;
Matt Tucker's avatar
Matt Tucker committed
885
        }
Matt Tucker's avatar
Matt Tucker committed
886
        return messengerHome;
Matt Tucker's avatar
Matt Tucker committed
887
    }
888
}