All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.ibm.g11n.pipeline.client.rb.CloudResourceBundleControl Maven / Gradle / Ivy

Go to download

Java client SDK for Globalization Pipeline on IBM Bluemix providing JDK ResourceBundle integration and Java APIs for Globalization Pipeline's REST endpoints.

The newest version!
/*  
 * Copyright IBM Corp. 2015
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.ibm.g11n.pipeline.client.rb;

import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.ResourceBundle.Control;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import com.ibm.g11n.pipeline.client.ServiceAccount;

/**
 * CloudResourceBundleControl is a concrete subclass of {@link Control}.
 * The implementation looks for a bundle with the specified bundle base name (i.e. baseName argument used in
 * {@link ResourceBundle#getBundle(String, Locale, Control)}) in the IBM Globalization Pipeline service.
 * If there is no matching translation bundle, or there is a matching translation bundle but no matching locale
 * is found in the IBM Globlization Pipeline service instance, then the implementation uses the standard Java
 * implementation. This implementation allows some language bundles such as root stored locally, while other
 * language bundles stored in the IBM Globalization Pipeline service instance.
 * 
 * @author Yoshito Umaoka
 */
public final class CloudResourceBundleControl extends Control {

    private static final String FORMAT_GP_CLOUD_BUNDLE = "gp.cloud.bundle";

    /**
     * Enum for resource bundle lookup modes.
     * 
     * @author Yoshito Umaoka
     */
    public enum LookupMode {
        /**
         * Lookup a bundle in a Globalization Pipeline service instance first.
         * If absent, lookup a standard Java resource bundle in classpath.
         */
        REMOTE_THEN_LOCAL(
                Arrays.asList(FORMAT_GP_CLOUD_BUNDLE, "java.class", "java.properties")),

        /**
         * Lookup a standard Java resource bundle in classpath first.
         * If absent, lookup a bundle in a Globalization Pipeline service instance.
         */
        LOCAL_THEN_REMOTE(
                Arrays.asList("java.class", "java.properties", FORMAT_GP_CLOUD_BUNDLE)),

        /**
         * Lookup a bundle in a Globalization Pipeline service only.
         */
        REMOTE_ONLY(
                Collections.singletonList(FORMAT_GP_CLOUD_BUNDLE)),

        /**
         * Look up a standard Java resource bundle in classpath.
         */
        LOCAL_ONLY(
                Arrays.asList("java.class", "java.properties"));

        private final List formatList;

        LookupMode(List formatList) {
            this.formatList = Collections.unmodifiableList(formatList);
        }

        List getFormatList() {
            return formatList;
        }
    };

    /**
     * Default resource bundle cache expiration time (60000 = 10 minutes)
     */
    public static final long DEFAULT_CACHE_EXPIRATION = 60000L;

    private final ServiceAccount serviceAccount;
    private long ttl;
    private Pattern inclusionPattern;
    private Pattern exclusionPattern;
    private NameMapper nameMapper;
    private LookupMode mode;

    /**
     * The environment variable name for specifying resource bundle lookup mode.
     * The valid values are defined in {@link LookupMode}. By default,
     * {@link LookupMode#REMOTE_THEN_LOCAL REMOTE_THEN_LOCAL} is used. 
     */
    public static final String GP_LOOKUP_MODE = "GP_LOOKUP_MODE";

    private static final LookupMode DEFAULT_LOOKUP_MODE = LookupMode.REMOTE_THEN_LOCAL;

    /**
     * The environment variable name for specifying resource bundle cache expiration time.
     * The unit is millisecond. When a constructor without cache expiration is used and
     * this environment environment variable is not set, the default cache expiration time
     * (60000 - 10 minutes) will be used.
     * 

* Following negative values are reserved for special purpose: *

    *
  • -1: The cloud resource bundles are never cached.
  • *
  • -2: The cache for cloud resource bundles will never be expired.
  • *
* Any other negative values are invalid and ignored. */ public static final String GP_CACHE_EXPIRATION = "GP_CACHE_EXPIRATION"; /** * The environment variable name for specifying bundle names included by this * cloud bundle implementation. The value is specified by a regular expression pattern. * For example, com\.acme\.myapp\..* to include all bundles in com.acme.myapp * package. *

* Note: When this environment variable is set, any resources not matching the * pattern won't be processed as a Globalization Pipeline cloud bundle. Also, * the exclusion pattern takes higher priority than the inclusion pattern. * @see #GP_RB_EXCLUSION_PATTERN */ public static final String GP_RB_INCLUSION_PATTERN = "GP_RB_INCLUSION_PATTERN"; /** * The environment variable name for specifying bundle names excluded by this * cloud bundle implementation, in addition to {@link #GP_RB_DEFAULT_EXCLUSION_PATTERN_STRING}. * The value is specified by a regular expression pattern. * For example, com\.acme\.lib\..* to exclude all bundles in com.acme.lib package. * @see #GP_RB_INCLUSION_PATTERN * @see #GP_RB_DEFAULT_EXCLUSION_PATTERN_STRING */ public static final String GP_RB_EXCLUSION_PATTERN = "GP_RB_EXCLUSION_PATTERN"; /** * The default regular expression pattern string specifying bundle names to be * excluded. *

     * (java|javax|org\.ietf|org\.jcp|org\.omg|org\.w3c|org\.xml|com\.sun|sun|jdk)\..*
     * 
* A resource bundle name matching this pattern won't be processed as * cloud bundle. * @see #GP_RB_EXCLUSION_PATTERN */ public static final String GP_RB_DEFAULT_EXCLUSION_PATTERN_STRING = "(java|javax|org\\.ietf|org\\.jcp|org\\.omg|org\\.w3c|org\\.xml|com\\.sun|sun|jdk)\\..*"; private static final Pattern DEFAULT_EXCLUSION = Pattern.compile(GP_RB_DEFAULT_EXCLUSION_PATTERN_STRING); /** * Create an instance of CloudResourceBundleControl with a service account * initialized with environment variables. *

* This method is equivalent to getInstance(ServiceAccount.getIntance()). * * @return An instance of CloundResourceBundleControl or null if there are not sufficient * configuration variables available in the environment. * @see ServiceAccount#getInstance() */ public static CloudResourceBundleControl getInstance() { ServiceAccount serviceAccount = ServiceAccount.getInstance(); if (serviceAccount == null) { return null; } return getInstance(serviceAccount); } /** * Create an instance of CloudResourceBundleControl with the specified * service account. * * @param serviceAccount The service account. * @return An instance of CloundResourceBundleControl. * @throws IllegalArgumentException when serviceAccount is null. */ public static CloudResourceBundleControl getInstance(ServiceAccount serviceAccount) { return getInstance(serviceAccount, initCacheExpiration()); } /** * Create an instance of CloudResourceBundleControl with the specified * service account. * * @param serviceAccount The service account. * @param mode The resource bundle resolution mode, or null for default mode * ({@link LookupMode#REMOTE_THEN_LOCAL}). * @return An instance of CloundResourceBundleControl. * @throws IllegalArgumentException when serviceAccount is null. */ public static CloudResourceBundleControl getInstance(ServiceAccount serviceAccount, LookupMode mode) { return getInstance(serviceAccount, initCacheExpiration(), null, null, null, mode); } /** * Create an instance of CloudResourceBundleControl with the specified * service account and the cache expiration. *

* This method is equivalent to * getInstance(serviceAccount, cacheExpiration, null, null, null). * * @param serviceAccount The service account. * @param cacheExpiration The cache expiration, see the method description for details. * @return An instance of CloudResourceBundleControl. * @throws IllegalArgumentException when serviceAccount is null or cacheExpiration * value is illegal. * @see #getInstance(ServiceAccount, long, String, String, NameMapper) */ public static CloudResourceBundleControl getInstance(ServiceAccount serviceAccount, long cacheExpiration) { return getInstance(serviceAccount, cacheExpiration, null, null); } /** * Create an instance of CloudResourceBundleControl with the specified * service account, cache expiration and bundle inclusion/exclusion name patterns. *

* This method is equivalent to * getInstance(serviceAccount, cacheExpiration, inclusionPattern, exclusionPattern, null). * @param serviceAccount The service account. * @param cacheExpiration The cache expiration. * @param inclusionPattern The regular expression pattern string for specifying resource bundle * names to be included, or null. * @param exclusionPattern The regular expression pattern string for specifying resource bundle * names to be excluded in addition to * {@link #GP_RB_DEFAULT_EXCLUSION_PATTERN_STRING} * @return An instance of CloudResourceBundleControl. * @throws IllegalArgumentException when serviceAccount is null, * or cacheExpiration value is illegal, * or inclusionPattern/exclusionPattern syntax is invalid. * @see #getInstance(ServiceAccount, long, String, String, NameMapper) */ public static CloudResourceBundleControl getInstance(ServiceAccount serviceAccount, long cacheExpiration, String inclusionPattern, String exclusionPattern) { return getInstance(serviceAccount, cacheExpiration, null, null, null); } /** * The callback interface used for mapping a resource bundle base name * to a bundle ID in the IBM Globlization Pipeline service instance. */ public interface NameMapper { /** * Returns the bundle ID used in the IBM Globalization Pipeline service instance for * the specified resource bundle base name. * * @param baseName The base name used in Java * @return The bundle ID used in the IBM Globalization Pipeline service instance, or * null if there is no corresponding bundle. */ String getBundleID(String baseName); } /** * Create an instance of CloudResourceBundleControl with the specified * service account, cache expiration, bundle inclusion/exclusion name pattern and the custom * bundle name mapper. *

* The cache expiration time is in milliseconds * and must be positive except for two special values. *

    *
  • {@link Control#TTL_DONT_CACHE} to disable resource bundle cache
  • *
  • {@link Control#TTL_NO_EXPIRATION_CONTROL} to disable resource bundle cache expiration
  • *
*

* * @param serviceAccount The service account. * @param cacheExpiration The cache expiration, see the method description for details. * @param inclusionPattern The regular expression pattern string for specifying resource bundle * names to be included, or null. * @param exclusionPattern The regular expression pattern string for specifying resource bundle * package names to be excluded in addition to * {@link #GP_RB_DEFAULT_EXCLUSION_PATTERN_STRING}, or null. * @param nameMapper The custom base name to bundle ID mapper, or null if no mapping is necessary. * @return An instance of CloudResourceBundleControl. * @throws IllegalArgumentException when serviceAccount is null, * or cacheExpiration value is illegal, * or inclusionPattern/exclusionPattern syntax is invalid. */ public static CloudResourceBundleControl getInstance(ServiceAccount serviceAccount, long cacheExpiration, String inclusionPattern, String exclusionPattern, NameMapper nameMapper) { return getInstance(serviceAccount, cacheExpiration, inclusionPattern, exclusionPattern, nameMapper, null); } /** * Create an instance of CloudResourceBundleControl with the specified * service account, cache expiration, bundle inclusion/exclusion name pattern, the custom * bundle name mapper and bundle lookup mode. *

* The cache expiration time is in milliseconds * and must be positive except for two special values. *

    *
  • {@link Control#TTL_DONT_CACHE} to disable resource bundle cache
  • *
  • {@link Control#TTL_NO_EXPIRATION_CONTROL} to disable resource bundle cache expiration
  • *
*

* * @param serviceAccount The service account. * @param cacheExpiration The cache expiration, see the method description for details. * @param inclusionPattern The regular expression pattern string for specifying resource bundle * names to be included, or null. * @param exclusionPattern The regular expression pattern string for specifying resource bundle * package names to be excluded in addition to * {@link #GP_RB_DEFAULT_EXCLUSION_PATTERN_STRING}, or null. * @param nameMapper The custom base name to bundle ID mapper, or null if no mapping is necessary. * @param mode The resource bundle lookup mode. If null, and the environment variable * {@link #GP_LOOKUP_MODE} is not set, {@link LookupMode#REMOTE_THEN_LOCAL * REMOTE_THEN_LOCAL} is used. * @return An instance of CloudResourceBundleControl. * @throws IllegalArgumentException when serviceAccount is null, * or cacheExpiration value is illegal, * or inclusionPattern/exclusionPattern syntax is invalid. */ public static CloudResourceBundleControl getInstance(ServiceAccount serviceAccount, long cacheExpiration, String inclusionPattern, String exclusionPattern, NameMapper nameMapper, LookupMode mode) { if (serviceAccount == null) { throw new IllegalArgumentException("serviceAccount is null"); } if (cacheExpiration < 0 && cacheExpiration != Control.TTL_DONT_CACHE && cacheExpiration != Control.TTL_NO_EXPIRATION_CONTROL) { throw new IllegalArgumentException("Illegal cacheExpiration: " + cacheExpiration); } Pattern incPat = null; Pattern excPat = null; if (inclusionPattern != null) { try { incPat = Pattern.compile(inclusionPattern); } catch (PatternSyntaxException e) { throw new IllegalArgumentException("Illegal inclusionPattern: " + inclusionPattern, e); } } if (exclusionPattern != null) { try { excPat = Pattern.compile(exclusionPattern); } catch (PatternSyntaxException e) { throw new IllegalArgumentException("Illegal exclusionPattern: " + exclusionPattern, e); } } return new CloudResourceBundleControl(serviceAccount, mode, cacheExpiration, incPat, excPat, nameMapper); } /** * Package local constructor. * * @param serviceAccount The service account * @param mode The resource bundle lookup mode * @param ttl The cache expiration time in milliseconds. * -1 (Control.TTL_DONT_CACHE) to disable cache, * -2 (Control.TTL_NO_EXPIRATION_CONTROL) to disable cache expiration. * @param inclusionPattern The regular expression pattern specifying the inclusive bundle * base names. * * @param nameMapper The Java base name to IBM Globalization Pipeline bundle ID * mapper, or null if same names are used. */ CloudResourceBundleControl(ServiceAccount serviceAccount, LookupMode mode, long ttl, Pattern inclusionPattern, Pattern exclusionPattern, NameMapper nameMapper) { this.serviceAccount = serviceAccount; this.mode = (mode == null) ? initMode() : mode; this.ttl = ttl; this.inclusionPattern = inclusionPattern; this.exclusionPattern = exclusionPattern; this.nameMapper = nameMapper; } @Override public List getFormats(String baseName) { return mode.getFormatList(); } @Override public long getTimeToLive(String baseName, Locale locale) { return ttl; } @Override public boolean needsReload(String baseName, Locale locale, String format, ClassLoader loader, ResourceBundle bundle, long loadTime) { // We may implement light weight 'last update' check. // For now, always returns true, that means, whenever cached // bundle is expired, reload the contents from the Globalization // Pipeline service instance. return true; } @Override public ResourceBundle newBundle(String baseName, Locale locale, String format, ClassLoader loader, boolean reload) throws IllegalAccessException, InstantiationException, IOException { if (!format.equals(FORMAT_GP_CLOUD_BUNDLE)) { // When requested resource format is not GP cloud bundle, // just delegate the request to the Java's default implementation. return super.newBundle(baseName, locale, format, loader, reload); } if (isExcluded(baseName)) { return null; } if (locale.getLanguage().isEmpty()) { // Globalization Pipeline does not support a locale // with no language code, including root locale return null; } // Map the input baseName to GP's bundleId if NameMapper is available String bundleId = nameMapper != null ? nameMapper.getBundleID(baseName) : baseName; if (bundleId == null) { return null; } // loadBundle returns null if locale is not available return CloudResourceBundle.loadBundle(serviceAccount, bundleId, locale); } private boolean isExcluded(String baseName) { if (DEFAULT_EXCLUSION.matcher(baseName).matches()) { return true; } if (exclusionPattern != null && exclusionPattern.matcher(baseName).matches()) { return true; } if (inclusionPattern != null && !inclusionPattern.matcher(baseName).matches()) { return true; } return false; } private static long initCacheExpiration() { Map env = System.getenv(); String envCacheExp = env.get(GP_CACHE_EXPIRATION); if (envCacheExp != null) { try { long cacheExpiration = Long.parseLong(envCacheExp); if (cacheExpiration >= -2) { return cacheExpiration; } } catch (NumberFormatException e) { // Fall through } } return DEFAULT_CACHE_EXPIRATION; } private static LookupMode initMode() { Map env = System.getenv(); String envMode = env.get(GP_LOOKUP_MODE); LookupMode mode = DEFAULT_LOOKUP_MODE; if (envMode != null) { try { mode = LookupMode.valueOf(envMode.toUpperCase(Locale.ROOT)); } catch (IllegalArgumentException e) { // fall through } } return mode; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy