Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[FIX JENKINS-35845] Internationalisation for Blue Ocean and JDL (#2586)
* Load i18n resource bundles from plugins if not found in jenkins core

Signed-off-by: Thorsten Scherler <scherler@gmail.com>

* Issue 404 response for missing i18n resource bundles

Currently issues a 200 with an "error" response payload. This change still issues the error response payload, but also sets the HTTP response.

Signed-off-by: Thorsten Scherler <scherler@gmail.com>

* [JENKINS-35845] Fix test since we return now a 404

* [JENKINS-35845] add test for getting locale from plugin. fix comments from oleg.

* [JENKINS-35845] Fix description

* [JENKINS-35845] Update PR with comments from Oleg

* [JENKINS-35845] Add feedback from tom

* eslint - formating changes and fix offences

* eslint - formating changes and fix offences

* [JENKINS-35845] remove code concerning 404 response. Fix resourceBundle test by prevent NPE to happen

* [JENKINS-35845] Link to issue on which we introduced the test

(cherry picked from commit ab16e52)
  • Loading branch information
scherler authored and olivergondza committed Nov 9, 2016
1 parent 2b39a38 commit 288a66d
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 4 deletions.
1 change: 1 addition & 0 deletions core/src/main/java/hudson/util/HttpResponses.java
Expand Up @@ -29,6 +29,7 @@
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;

import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import javax.servlet.ServletException;
import java.io.File;
Expand Down
47 changes: 46 additions & 1 deletion core/src/main/java/jenkins/util/ResourceBundleUtil.java
Expand Up @@ -27,12 +27,16 @@
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;

import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import hudson.PluginWrapper;
import java.util.logging.Logger;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.concurrent.ConcurrentHashMap;
import jenkins.model.Jenkins;

/**
* Simple {@link java.util.ResourceBundle} utility class.
Expand All @@ -42,6 +46,7 @@
@Restricted(NoExternalUse.class)
public class ResourceBundleUtil {

private static final Logger logger = Logger.getLogger("jenkins.util.ResourceBundle");
private static final Map<String, JSONObject> bundles = new ConcurrentHashMap<>();

private ResourceBundleUtil() {
Expand Down Expand Up @@ -72,14 +77,54 @@ private ResourceBundleUtil() {
return bundleJSON;
}

ResourceBundle bundle = ResourceBundle.getBundle(baseName, locale);
ResourceBundle bundle = getBundle(baseName, locale, Jenkins.class.getClassLoader());
if (bundle == null) {
// Not in Jenkins core. Check the plugins.
Jenkins jenkins = Jenkins.getInstance(); // will never return null
if (jenkins != null) {
for (PluginWrapper plugin : jenkins.getPluginManager().getPlugins()) {
bundle = getBundle(baseName, locale, plugin.classLoader);
if (bundle != null) {
break;
}
}
}
}
if (bundle == null) {
throw new MissingResourceException("Can't find bundle for base name "
+ baseName + ", locale " + locale, baseName + "_" + locale, "");
}

bundleJSON = toJSONObject(bundle);
bundles.put(bundleKey, bundleJSON);

return bundleJSON;
}

/**
* Get a plugin bundle using the supplied Locale and classLoader
*
* @param baseName The bundle base name.
* @param locale The Locale.
* @param classLoader The classLoader
* @return The bundle JSON.
*/
private static @CheckForNull ResourceBundle getBundle(@Nonnull String baseName, @Nonnull Locale locale, @Nonnull ClassLoader classLoader) {
try {
return ResourceBundle.getBundle(baseName, locale, classLoader);
} catch (MissingResourceException e) {
// fall through and return null.
logger.warning(e.getMessage());
}
return null;
}

/**
* Create a JSON representation of a resource bundle
*
* @param bundle The resource bundle.
* @return The bundle JSON.
*/
private static JSONObject toJSONObject(@Nonnull ResourceBundle bundle) {
JSONObject json = new JSONObject();
for (String key : bundle.keySet()) {
Expand Down
21 changes: 18 additions & 3 deletions test/src/test/java/jenkins/I18nTest.java
Expand Up @@ -23,6 +23,7 @@
*/
package jenkins;

import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
import net.sf.json.JSONObject;
import org.junit.Assert;
import org.junit.Rule;
Expand All @@ -31,6 +32,7 @@
import org.xml.sax.SAXException;

import java.io.IOException;
import org.jvnet.hudson.test.Issue;

/**
* @author <a href="mailto:tom.fennelly@gmail.com">tom.fennelly@gmail.com</a>
Expand All @@ -49,9 +51,22 @@ public void test_baseName_unspecified() throws IOException, SAXException {

@Test
public void test_baseName_unknown() throws IOException, SAXException {
JSONObject response = jenkinsRule.getJSON("i18n/resourceBundle?baseName=com.acme.XyzWhatever").getJSONObject();
Assert.assertEquals("error", response.getString("status"));
Assert.assertTrue(response.getString("message").contains("com.acme.XyzWhatever"));
try {
JSONObject response = jenkinsRule.getJSON("i18n/resourceBundle?baseName=com.acme.XyzWhatever").getJSONObject();
} catch (FailingHttpStatusCodeException e) {
Assert.assertNotNull(e);
Assert.assertTrue(e.getMessage().contains("com.acme.XyzWhatever"));
}
}

@Issue("JENKINS-35270")
@Test
public void test_baseName_plugin() throws IOException, SAXException {
// ssh-slaves plugin is installed by default
JSONObject response = jenkinsRule.getJSON("i18n/resourceBundle?baseName=hudson.plugins.sshslaves.Messages").getJSONObject();
Assert.assertEquals("ok", response.getString("status"));
JSONObject data = response.getJSONObject("data");
Assert.assertEquals("The launch timeout must be a number.", data.getString("SSHConnector.LaunchTimeoutMustBeANumber"));
}

@Test
Expand Down

0 comments on commit 288a66d

Please sign in to comment.