Unlock your on-premises translated content in the cloud

Use the Cloud Integration add-on in IBM Bluemix for seamless, secure access to systems of record

Enterprise software developers making the transition to cloud computing often ask how they can use the existing translated content in their on-premises databases from the cloud. Find out how to use the Cloud Integration add-on in IBM® Bluemix™ to unlock your translated content from systems of record and access it from the cloud — even if the database is behind a secure firewall.

Steven E. Atkin (atkin@us.ibm.com), Chief Globalization Architect and Senior Technical Staff Member, IBM

Steven AtkinSteven Atkin is the Chief Globalization Architect at IBM and is responsible for establishing the globalization strategy for IBM products. Steven has been working on globalization issues at IBM for 20 years and now focuses on globalization challenges encountered as developers transition to the cloud.



19 August 2014

Sign up for IBM Bluemix
This cloud platform is stocked with free services, runtimes, and infrastructure to help you quickly build and deploy your next mobile or web application.

As organizations make the transition to the cloud, they are often challenged with how they can continue taking advantage of their existing IT investments while embracing the benefits of cloud computing. One typical challenge is to be able to access translated content held in existing systems of record such as DB2™ from the cloud, and specifically from IBM Bluemix.

The Cloud Integration add-on in Bluemix greatly simplifies this task. With this add-on, you can construct a set of RESTful APIs that provide seamless, secure access to DB2 databases even if they are behind firewalls. Once you create these RESTful services, you can use them from any Bluemix buildpack that supports the HTTP protocol.

This article shows how to unlock translated content that's stored in an on-premises database and access it from the Bluemix Liberty for Java™ runtime. My examples further illustrate how the RESTful APIs can be integrated into the Java resource bundle framework, making access to the content nearly transparent from a Java application.

With the Cloud Integration add-on, you can construct a set of RESTful APIs that provide seamless, secure access to DB2 databases even if they are behind firewalls.

Getting your translatable content ready

To get started, examine your existing database schema and locate the tables that contain the translated content that you want to access from your cloud application. Then, determine if the table contains an attribute that can be used to identify the language of the content; in some cases, you might need to add that attribute. The example DB2 table shown as Table 1 already has an attribute to identify the content's language, so no modification is needed.

Table 1. Translatable content table
Column NameColumn TypeCodepageLength
id INTEGER 0 4
key VARCHAR 1208 128
language VARCHAR 1208 12
value VARCHAR 1208 1024

It's important to understand the syntax and semantics of the language identifiers that are used in the database table, because the identifiers must be mapped to the language identifier syntax used by the Java programming language. To simplify the language identifier mapping, the recommended practice is for your database to use the language identifier syntax described in IETF BCP 47.

Another key aspect to note about Table 1 is that all textual content is encoded in codepage 1208. Codepage 1208 is the numeric identifier that DB2 uses to indicate that the text is being stored as Unicode characters and more specifically in UTF-8 format. The use of Unicode makes it possible to store content in any language.


Creating your Cloud Integration RESTful APIs

Once you determine which table you want to access from your cloud application, you must establish a connection to your database and create a RESTful Enterprise API to access it. To complete these tasks, use the Cloud Integration configuration panels in the Bluemix dashboard and install the Cloud Integration Secure Connector on your database system. See the Bluemix Cloud Integration Documentation for a tutorial explaining all the details of how to install, set up, and configure the Cloud Integration add-on.

The RESTful Enterprise API that you create directly maps the attributes from your database table to a JavaScript Object Notation (JSON) object.

Table 2 shows the mapping for a sample API:

Table 2. Attribute-to-JSON mapping
PropertyTypeRequired
idxs:integer yes
languagexs:string yes
keyxs:string yes
valuexs:string yes

The JSON object can be retrieved by calling the GET method over the HTTP protocol:

https://provide.castiron.com/env/DatabaseEP/database/insert your value here/TRANSLATIONS/Tables/CONTENT?
{columnname1=columnvalue1&columnname2=columnvalue2&..}

Listing 1 shows an example JSON response:

Listing 1. JSON response
{
   "rows":
   {
      "row":
      [
         {
            "id": "xs:integer",
            "language": "xs:string",
            "key":   "xs:string",
            "value": "xs:string"
         }
      ]
   }
}

After you have installed the Cloud Integration Secure Connector, established a connection to your database via the Secure Connector, and created a RESTful Enterprise API, you can start to access the translated content in your database.


Using Cloud Integration's RESTful services

As with other Bluemix services and add-ons, the credentials and service URL for the Cloud Integration add-on must be extracted from the VCAP_SERVICES runtime environment variable. The CloudIntegration section of the VCAP_SERVICES environment variable includes a subsection labeled apis. As shown in Listing 2, the apis subsection contains the names and URLs of the RESTful services that you create and configure during the activation and installation of the Cloud Integration add-on.

Listing 2. CloudIntegration section of VCAP_SERVICES
{
   "CloudIntegration": [
      {
         "name": "Cloud Integration-ec",
         "label": "CloudIntegration",
         "plan": "cloudintegrationplan",
         "credentials": {
            "userid": "your userid",
            "password": "your password",
            "apis": [
               {
                  "name": "translation",
                  "desc": "This connects to the systems of record",
                  "url": "TRANSLATIONS/Tables/CONTENT",
                  "API_SECRET": "this is your secret"
               }
            ]
         }
      }
   ]
}

The sample code fragment in Listing 3 illustrates how the Cloud Integration credentials can be parsed from the VCAP_SERVICES environment variable and how the RESTful APIs created earlier can be parsed.

Listing 3. Parsing the Cloud Integration service from VCAP_SERVICES
If key.startsWith("CloudIntegration")) {
    JSONArray val = (JSONArray)obj.get(key)!=null?(JSONArray)obj.get(key):null;
     if(val!=null){
        JSONObject service = val.get(0)!=null?(JSONObject)val.get(0):null;
    JSONObject credentials = service!=null?(service.get("credentials")!=null?
      (JSONObject)service.get("credentials"):null):null;
     userid = credentials.get("userid") !=null?(String) credentials.get("userid"):"";
     password = String) credentials.get("password") !=null?
      (String)credentials.get("password"):"";
               
      // get the URLs to the APIs
      JSONArray apis = credentials.get("apis")!=null?(JSONArray) credentials.get("apis"):null;
       if(apis!=null) {
           for(Iterator<?> api = apis.iterator(); api.hasNext();) {
           JSONObject apiAttr = (JSONObject) api.next();
            String name = apiAttr.get("name") != null?(String) apiAttr.get("name"):"";
          if(name.equals("translation")) {
           url = apiAttr.get("url") !=null?(String) apiAttr.get("url"):"";
           apiSecret = apiAttr.get("API_SECRET") !=null?(String) apiAttr.get("API_SECRET"):"";
          }
           }
           }
                     
      connection = new CloudDataConnection(userid, password, url, apiSecret);
      foundService = true;
      break;
    }
}

In this example. the credentials and URL to the RESTful API named translation will be used later when the translated content in the database needs to be accessed.


Mapping locales and language codes

For the example, I created a special cloud resource bundle framework that calls my RESTful API to access the translated content from a simple Java servlet running in Liberty for Java on Bluemix. You can run the servlet via a URL from a client-side web browser.

Before I show how to use the special cloud resource bundle, you need to understand how to map the language codes that are used in browsers to a Java locale identifier so that translated content can be extracted from the database.

When a browser accesses a Java servlet on a server, it sets the user's desired language in the Accept-Language header as part of the HTTP request. Note that the values that appear in the Accept-Language header found in HTTP requests from browsers can vary among browsers, even for the same language and country. This situation requires a little extra care to ensure that you always retrieve the correct language and create the correct Java locale no matter which browser is used to connect to the Java servlet.

Most developers typically obtain the language and locale by calling the getLocale method from the HTTPServletRequest object that is passed into the doGet method of the servlet. This technique, however, does not always yield correct results. To address this situation, I created a method, shown in Listing 4, that parses the Accept-Language header and determines the proper Java locale that best matches the user's desired language setting.

Listing 4. Method to parse Accept-Language header
public Locale getLocaleFromHeader(String header) {
    Locale locale = null;
       
    if(!header.isEmpty()) {
   String[] languages = header.split(",");
         
   if(languages.length > 0) {
      // Grab the first language in the list and ignore the q value 
      String[] lang = languages[0].split(";");
            
      // Split out the parts of the language tag
      String[] elements = lang[0].split("-");
      switch(elements.length) {
         case 1: locale = new Locale(elements[0]); break;
         case 2: locale = new Locale(elements[0], elements[1]); break;
         /* Internet explorer 10 and above specifies the script as the second element
          *  in the tag and needs to be placed as the third argument in the 
          *  Locale constructor 
         */
         case 3: locale = new Locale(elements[0], elements[2], elements[1]); break;
         default: locale = Locale.getDefault(); break;
      }
   }
         
   }
       else {
          locale = Locale.getDefault();
    }
       return locale;
}

When I'm ready to call my RESTful API to access the translated content, I'll need to map the Java locale to an appropriate BCP 47 language tag that matches the language identifiers that I used earlier in the database. This method takes a Java locale and returns its corresponding BCP 47 language tag, as shown in Listing 5.

Listing 5. Method to map Java locale to BCP 47 language tag
private String mapLocaleToLanguage(Locale locale) {
   String language;
         
   if (locale.equals(Locale.CHINA) || 
          locale.equals(Locale.PRC) || 
          locale.equals(Locale.CHINESE) || 
      locale.equals(Locale.SIMPLIFIED_CHINESE))
         language = "zh-Hans";
   else if (locale.equals(Locale.TAIWAN) || 
      locale.equals(Locale.TRADITIONAL_CHINESE))
         language = "zh-Hant";
   else if (locale.equals(Locale.JAPAN) || locale.equals(Locale.JAPANESE))
         language = "ja";
   else {
      language = locale.getLanguage();
   }
         
   return language;
}

Using the cloud resource bundle

To make access to the translated content in the database simple, I created a cloud resource bundle framework. Like regular Java resource bundles, I can use this framework to access the translated content from the database through the Cloud Integration RESTful API, thereby making integration into cloud applications simple.

The cloud resource bundle is designed to keep latency low by making only one call to the Cloud Integration RESTful API to retrieve all the available languages from the database. After the initial call is made, the returned results are converted to Java property objects as needed and then cached for subsequent reuse. The cloud resource bundle in the article's sample code uses the Java Jackson and Apache HttpComponents libraries.

To access the translated content in the database, the RESTful GET method is called along with the security credentials, and API_SECRET is included in the header. When you handle the response from the GET method, make sure that the JSON object is interpreted as UTF-8, as shown in Listing 6, because JSON objects are encoded in UTF-8.

Listing 6. Calling the RESTful GET method
private static Rows getServerResponse(CloudDataConnection connect) throws Exception{
   Rows rows = null;
      
   CredentialsProvider credsProvider = new BasicCredentialsProvider();
        credsProvider.setCredentials(
           new AuthScope("provide.castiron.com", 443),
             new UsernamePasswordCredentials(connect.getUserid(), connect.getPassword()));
       CloseableHttpClient httpclient = HttpClients.custom()
             .setDefaultCredentialsProvider(credsProvider)
              .build();
      
        try {
           // Call the service and get all the strings for all the languages
           HttpGet httpget = new HttpGet(connect.getURL());
               httpget.addHeader("API_SECRET", connect.getSecret());
               CloseableHttpResponse response = httpclient.execute(httpget);
            
            try {
               InputStream in = response.getEntity().getContent();
              ObjectMapper mapper = new ObjectMapper();
             rows = mapper.readValue(new InputStreamReader(in, "UTF-8"), Rows.class);
                EntityUtils.consume(response.getEntity());
            }
            finally {
             response.close();
            }  
        }
        finally {
            httpclient.close();
        }
      return rows;
}

Conclusion

Unlocking existing translated content from your on-premises systems of record is no longer a limiting factor in starting to transition applications to the cloud. The Bluemix Cloud Integration add-on greatly simplifies the task of accessing your database securely even if it is behind a firewall. The cloud resource bundle sample code further accelerates cloud integration by providing near-seamless integration with the Liberty for Java runtime.

To get started using the sample code, be sure to include the Java Jackson and Apache HttpComponents libraries in your application. The Bluemix Java Web Starter boilerplate includes code that shows how to parse the VCAP_SERVICES environment variable, and it includes libraries that help with converting Java objects to and from JSON format.

Resources

  • Getting started with Cloud Integration service: This tutorial gets you started with the Cloud Integration add-on in Bluemix.
  • Tags for Identifying Languages: See this document for the structure, content, construction, and semantics of language tags for indicating the language used in an information object.
  • Jackson: Visit the Jackson website to learn more about this Java library for JSON processing.
  • Apache HttpComponents: Learn more about the Apache HttpComponents project: a toolset of low-level Java components focused on HTTP and associated protocols.

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Cloud computing on developerWorks


  • Bluemix Developers Community

    Get samples, articles, product docs, and community resources to help build, deploy, and manage your cloud apps.

  • Cloud digest

    Complete cloud software, infrastructure, and platform knowledge.

  • DevOps Services

    Software development in the cloud. Register today to create a project.

  • Try SoftLayer Cloud

    Deploy public cloud instances in as few as 5 minutes. Try the SoftLayer public cloud instance for one month.

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Cloud computing, Java technology
ArticleID=980865
ArticleTitle=Unlock your on-premises translated content in the cloud
publish-date=08192014