com.flexive.shared.exceptions.FxExceptionMessage.java Source code

Java tutorial

Introduction

Here is the source code for com.flexive.shared.exceptions.FxExceptionMessage.java

Source

/***************************************************************
 *  This file is part of the [fleXive](R) framework.
 *
 *  Copyright (c) 1999-2014
 *  UCS - unique computing solutions gmbh (http://www.ucs.at)
 *  All rights reserved
 *
 *  The [fleXive](R) project is free software; you can redistribute
 *  it and/or modify it under the terms of the GNU Lesser General Public
 *  License version 2.1 or higher as published by the Free Software Foundation.
 *
 *  The GNU Lesser General Public License can be found at
 *  http://www.gnu.org/licenses/lgpl.html.
 *  A copy is found in the textfile LGPL.txt and important notices to the
 *  license from the author are found in LICENSE.txt distributed with
 *  these libraries.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  For further information about UCS - unique computing solutions gmbh,
 *  please see the company website: http://www.ucs.at
 *
 *  For further information about [fleXive](R), please see the
 *  project website: http://www.flexive.org
 *
 *
 *  This copyright notice MUST APPEAR in all copies of the file!
 ***************************************************************/
package com.flexive.shared.exceptions;

import com.flexive.shared.FxFormatUtils;
import com.flexive.shared.FxLanguage;
import com.flexive.shared.FxSharedUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.IOException;
import java.io.Serializable;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * Localized Exception message handling
 *
 * @author Markus Plesser (markus.plesser@flexive.com), UCS - unique computing solutions gmbh (http://www.ucs.at)
 */
public class FxExceptionMessage implements Serializable {
    private static final long serialVersionUID = -545689173017222846L;
    private static final Log LOG = LogFactory.getLog(FxExceptionMessage.class);

    /**
     * Default language of a resource if no _locale file is present
     */
    private static final String EXCEPTION_BUNDLE = "FxExceptionMessages";
    private static final String PLUGIN_BUNDLE = "PluginMessages";
    private String key;
    private Object[] values;

    private static final List<FxSharedUtils.BundleReference> resourceBundles = new CopyOnWriteArrayList<FxSharedUtils.BundleReference>();
    private static final ConcurrentMap<String, ResourceBundle> cachedBundles = new ConcurrentHashMap<String, ResourceBundle>();
    private static final ConcurrentMap<FxSharedUtils.MessageKey, String> cachedMessages = new ConcurrentHashMap<FxSharedUtils.MessageKey, String>();
    private static volatile boolean initialized = false;

    /**
     * Initialize the application resource bundles. Scans the classpath for resource bundles
     * for a predefined set of names ({@link #EXCEPTION_BUNDLE} and {@link #PLUGIN_BUNDLE}),
     * and then adds resource references that use {@link java.net.URLClassLoader URLClassLoaders} for loading
     * the associated resource bundles.
     */
    private static synchronized void initialize() {
        if (initialized) {
            return;
        }
        try {
            resourceBundles.addAll(FxSharedUtils.addMessageResources(EXCEPTION_BUNDLE));
            resourceBundles.addAll(FxSharedUtils.addMessageResources(PLUGIN_BUNDLE));
        } catch (IOException e) {
            LOG.error("Failed to initialize plugin message resources: " + e.getMessage(), e);
        } finally {
            initialized = true;
        }
    }

    /**
     * Returns the resource bundle in the default locale.
     *
     * @param key resource key
     * @return the resource bundle value
     */
    public String getResource(String key) {
        return getResource(key, Locale.getDefault());
    }

    /**
     * Returns the resource bundle, which is cached within the request.
     *
     * @param key       resource key
     * @param locale    the requested locale
     * @return the resource bundle value
     */
    public String getResource(String key, Locale locale) {
        if (!initialized) {
            initialize();
        }
        final FxSharedUtils.MessageKey messageKey = new FxSharedUtils.MessageKey(locale, key);
        String cachedMessage = cachedMessages.get(messageKey);
        if (cachedMessage != null) {
            return cachedMessage;
        }
        for (FxSharedUtils.BundleReference bundleReference : resourceBundles) {
            try {
                final ResourceBundle bundle = getResources(bundleReference, locale);
                final String message = bundle.getString(key);
                cachedMessage = cachedMessages.putIfAbsent(messageKey, message);
                return cachedMessage != null ? cachedMessage : message;
            } catch (MissingResourceException e) {
                // continue with next bundle
            }
        }
        if (!locale.equals(Locale.ENGLISH)) {
            //try to find the locale in english as last resort
            //this is a fix for using PropertyResourceBundles which can only handle one locale (have to use them thanks to JBoss 5...)
            for (FxSharedUtils.BundleReference bundleReference : resourceBundles) {
                try {
                    final ResourceBundle bundle = getResources(bundleReference, Locale.ENGLISH);
                    final String message = bundle.getString(key);
                    cachedMessage = cachedMessages.putIfAbsent(messageKey, message);
                    return cachedMessage != null ? cachedMessage : message;
                } catch (MissingResourceException e) {
                    // continue with next bundle
                }
            }
        }
        throw new MissingResourceException("Resource not found", FxExceptionMessage.class.getCanonicalName(), key);
    }

    /**
     * Return the resource bundle in the given locale. Uses caching to speed up
     * lookups.
     *
     * @param bundleReference the bundle reference object
     * @param locale          the requested locale
     * @return the resource bundle in the requested locale
     */
    private ResourceBundle getResources(FxSharedUtils.BundleReference bundleReference, Locale locale) {
        final String key = bundleReference.getCacheKey(locale);
        ResourceBundle bundle = cachedBundles.get(key);
        if (bundle == null) {
            ResourceBundle cachedBundle = cachedBundles.putIfAbsent(key,
                    bundle = bundleReference.getBundle(locale));
            if (cachedBundle != null) {
                return cachedBundle;
            }
        }
        return bundle;
    }

    /**
     * Ctor
     *
     * @param key    resource key
     * @param values optional values for placeholders in key ({x})
     */
    public FxExceptionMessage(String key, Object... values) {
        this.key = key;
        this.values = values != null ? values.clone() : new Object[0];
    }

    /**
     * Getter for the key
     *
     * @return key resource key
     */
    public String getKey() {
        return key;
    }

    /**
     * Getter for the message values (arguments)
     *
     * @return message key arguments
     * @since 3.2.0
     */
    public Object[] getArguments() {
        return values;
    }

    /**
     * Get the localized message for a given language code
     *
     * @param language  the language
     * @return localized message
     */
    public String getLocalizedMessage(FxLanguage language) {
        String result;
        try {
            result = getResource(key, language.getLocale());
            if (values != null && values.length > 0) {
                for (int i = 0, valuesLength = values.length; i < valuesLength; i++) {
                    Object val = values[i];
                    if (val != null && val instanceof String && ((String) val).startsWith("ex.")) {
                        //replace potential resource key with message
                        try {
                            values[i] = getResource((String) val, language.getLocale());
                        } catch (Exception e) {
                            LOG.warn(e);
                        }
                    }
                }
                result = FxFormatUtils.formatResource(result, language.getId(), values);
            }
            return result;
        } catch (MissingResourceException e) {
            LOG.warn("Unknown message key: " + key);
            return "??" + key + "??";
        }
    }

    /**
     * Cleanup cached resources.
     * @since 3.1.4
     */
    public static synchronized void cleanup() {
        cachedBundles.clear();
        resourceBundles.clear();
        cachedMessages.clear();
        initialized = false;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof FxExceptionMessage))
            return false;
        FxExceptionMessage o = (FxExceptionMessage) obj;
        return o.getKey().equals(this.getKey()) && ArrayUtils.isEquals(o.values, this.values);
    }

    @Override
    public int hashCode() {
        int result;
        result = key.hashCode();
        result = 31 * result + (values != null ? Arrays.hashCode(values) : 0);
        return result;
    }
}