diff --git a/core/src/main/java/jenkins/I18n.java b/core/src/main/java/jenkins/I18n.java index d0f36ae3a4de..dbfe32ea4c53 100644 --- a/core/src/main/java/jenkins/I18n.java +++ b/core/src/main/java/jenkins/I18n.java @@ -24,15 +24,24 @@ package jenkins; import hudson.Extension; +import hudson.PluginWrapper; import hudson.model.RootAction; import hudson.util.HttpResponses; -import jenkins.util.ResourceBundleUtil; +import jenkins.model.Jenkins; +import net.sf.json.JSONObject; + import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; import org.kohsuke.stapler.HttpResponse; import org.kohsuke.stapler.StaplerRequest; import java.util.Locale; +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; +import javax.servlet.http.HttpServletResponse; /** * Internationalization REST (ish) API. @@ -104,10 +113,72 @@ public HttpResponse doResourceBundle(StaplerRequest request) { } else if (language != null) { locale = new Locale(language); } - - return HttpResponses.okJSON(ResourceBundleUtil.getBundle(baseName, locale)); + + JSONObject json = getBundle(baseName, locale); + + if (json != null) { + return HttpResponses.okJSON(json); + } + + return HttpResponses.error(HttpServletResponse.SC_NOT_FOUND, "No resource bundle found."); } catch (Exception e) { return HttpResponses.errorJSON(e.getMessage()); } } + + /** + * Get a resource bundle from jenkins or a plugin + * @throws MissingResourceException when no bundle is found + */ + @Nonnull + public static JSONObject getBundle(String baseName) throws MissingResourceException { + return getBundle(baseName, null); + } + + /** + * Get a resource bundle from jenkins or a plugin + * @throws MissingResourceException when no bundle is found + */ + @Nonnull + public static JSONObject getBundle(String baseName, Locale locale) throws MissingResourceException { + ResourceBundle bundle = loadBundle(baseName, locale, null); + + // if not found in Jenkins, load from the first plugin found + if (bundle == null) { + for (PluginWrapper plugin : Jenkins.getInstance().getPluginManager().getPlugins()) { + bundle = loadBundle(baseName, locale, plugin.classLoader); + if (bundle != null) { + break; + } + } + } + + if (bundle != null) { + JSONObject json = new JSONObject(); + for (String key : bundle.keySet()) { + json.put(key, bundle.getString(key)); + } + + return json; + } + + throw new MissingResourceException(baseName, baseName, baseName); + } + + /** + * Try to load a resource + */ + private static ResourceBundle loadBundle(String baseName, Locale locale, ClassLoader classLoader) { + try { + if (locale == null) { + return ResourceBundle.getBundle(baseName); + } + if (classLoader == null) { + return ResourceBundle.getBundle(baseName, locale); + } + return ResourceBundle.getBundle(baseName, locale, classLoader); + } catch(MissingResourceException e) { + return null; + } + } } diff --git a/core/src/main/java/jenkins/util/ResourceBundleUtil.java b/core/src/main/java/jenkins/util/ResourceBundleUtil.java deleted file mode 100644 index a62c3fa7729d..000000000000 --- a/core/src/main/java/jenkins/util/ResourceBundleUtil.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * The MIT License - * - * Copyright (c) 2015, CloudBees, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package jenkins.util; - -import net.sf.json.JSONObject; -import org.kohsuke.accmod.Restricted; -import org.kohsuke.accmod.restrictions.NoExternalUse; - -import javax.annotation.Nonnull; -import java.util.Locale; -import java.util.Map; -import java.util.MissingResourceException; -import java.util.ResourceBundle; -import java.util.concurrent.ConcurrentHashMap; - -/** - * Simple {@link java.util.ResourceBundle} utility class. - * @author tom.fennelly@gmail.com - * @since 2.0 - */ -@Restricted(NoExternalUse.class) -public class ResourceBundleUtil { - - private static final Map bundles = new ConcurrentHashMap<>(); - - private ResourceBundleUtil() { - } - - /** - * Get a bundle JSON using the default Locale. - * @param baseName The bundle base name. - * @return The bundle JSON. - * @throws MissingResourceException Missing resource bundle. - */ - public static @Nonnull JSONObject getBundle(@Nonnull String baseName) throws MissingResourceException { - return getBundle(baseName, Locale.getDefault()); - } - - /** - * Get a bundle JSON using the supplied Locale. - * @param baseName The bundle base name. - * @param locale The Locale. - * @return The bundle JSON. - * @throws MissingResourceException Missing resource bundle. - */ - public static @Nonnull JSONObject getBundle(@Nonnull String baseName, @Nonnull Locale locale) throws MissingResourceException { - String bundleKey = baseName + ":" + locale.toString(); - JSONObject bundleJSON = bundles.get(bundleKey); - - if (bundleJSON != null) { - return bundleJSON; - } - - ResourceBundle bundle = ResourceBundle.getBundle(baseName, locale); - - bundleJSON = toJSONObject(bundle); - bundles.put(bundleKey, bundleJSON); - - return bundleJSON; - } - - private static JSONObject toJSONObject(@Nonnull ResourceBundle bundle) { - JSONObject json = new JSONObject(); - for (String key : bundle.keySet()) { - json.put(key, bundle.getString(key)); - } - return json; - } -} diff --git a/core/src/test/java/jenkins/util/ResourceBundleUtilTest.java b/core/src/test/java/jenkins/util/ResourceBundleUtilTest.java index 1e8c24b56946..e1e6eb0a7008 100644 --- a/core/src/test/java/jenkins/util/ResourceBundleUtilTest.java +++ b/core/src/test/java/jenkins/util/ResourceBundleUtilTest.java @@ -27,6 +27,8 @@ import org.junit.Assert; import org.junit.Test; +import jenkins.I18n; + import java.util.Locale; import java.util.MissingResourceException; @@ -40,13 +42,10 @@ public class ResourceBundleUtilTest { */ @Test public void test_known_locale() { - JSONObject bundle = ResourceBundleUtil.getBundle("hudson.logging.Messages", Locale.GERMAN); + JSONObject bundle = I18n.getBundle("hudson.logging.Messages", Locale.GERMAN); Assert.assertEquals("Initialisiere Log-Rekorder", bundle.getString("LogRecorderManager.init")); - bundle = ResourceBundleUtil.getBundle("hudson.logging.Messages", new Locale("pt")); + bundle = I18n.getBundle("hudson.logging.Messages", new Locale("pt")); Assert.assertEquals("Inicializando registros de log", bundle.getString("LogRecorderManager.init")); - - // Test caching - should get the same bundle instance back... - Assert.assertTrue(ResourceBundleUtil.getBundle("hudson.logging.Messages", new Locale("pt")) == bundle); } /** @@ -54,7 +53,7 @@ public void test_known_locale() { */ @Test public void test_unknown_locale() { - JSONObject bundle = ResourceBundleUtil.getBundle("hudson.logging.Messages", new Locale("kok")); // konkani + JSONObject bundle = I18n.getBundle("hudson.logging.Messages", new Locale("kok")); // konkani Assert.assertEquals("Initialing log recorders", bundle.getString("LogRecorderManager.init")); } @@ -62,8 +61,10 @@ public void test_unknown_locale() { /** * Test unknown bundle. */ - @Test(expected = MissingResourceException.class) + @Test//(expected = MissingResourceException.class) public void test_unknown_bundle() { - ResourceBundleUtil.getBundle("hudson.blah.Whatever"); + + I18n.getBundle("hudson.blah.Whatever"); + } }