March 11, 2006

javaRB/RBjava CFCs updated

i've added the new messageFormat method to the existing CFCs and re-arranged the versions a bit. you can download this tool here with a simple testbed here and a testbed for the new messageFormat method here.

there are now six versions of the resource bundle (rb) tool, the three major versions include:
  • coreJava: if you don't need other calendars, locales, etc. offered by IBM's ICU4J library. this version uses core Java's Locale and MessageFormat classes. It will operate on any coldfusion host that permits createObject().
  • icu4j: Requires the installation of IBM's ICU4J java library which can be obtained here. this version uses the library installed on cf's classpath. it makes use of ICU4J's ULocale, UResourceBundle, and MessageFormat classes. this allows for more locales than are supported by core Java as well as additional locale "keywords" such as calendar, currency and collation (for example, th_TH@calendar=buddhist).
  • remoteICU4J: also requires the installation of IBM's ICU4J java library. this version uses a slightly modified "remote" classpath technique for installations where you don't have access to the classpath. you will need to specify the full path to a copy of the icu4j.jar file.

In each of these versions you will find two CFCs:
  • javaRB which handles rb files that aren't on coldfusion's classpath (usually deployed on shared hosts)
  • rbJava which uses rb files that are on coldfusion's classpath, this is usually the more robust form of this tool

You will also find:
  • javaRB.cfm a simple testbed for the javaRB CFC
  • rbJava.cfm a simple testbed for the rbJava CFC
  • messageFormat.cfm a simple testbed demonstrating the messageFormat method
  • testJavaRB.properties base rb file
  • testJavaRB_en_US.properties en_US locale rb file
  • testJavaRB_th_TH.properties th_TH locale rb file

public methods in the CFCs:
  • getResourceBundle returns a structure containing all key/messages value pairs in a given resource bundle file. required argument is rbFile containing absolute path to resource bundle file. optional argument is rbLocale to indicate which locale's resource bundle to use, defaults to us_EN (american english)
  • getRBKeys returns an array holding all keys in given resource bundle. required argument is rbFile containing absolute path to resource bundle file. optional argument is rbLocale to indicate which locale's resource bundle to use, defaults to us_EN (american english)
  • getRBString returns string containing the text for a given key in a given resource bundle. required arguments are rbFile containing absolute path to resource bundle file and rbKey a string holding the required key. optional argument is rbLocale to indicate which locale's resource bundle to use, defaults to us_EN (american english)
  • formatRBString returns string w/dynamic values substituted. performs messageFormat like operation on compound rb string: "You owe me {1}. Please pay by {2} or I will be forced to shoot you with {3} bullets." this function will replace the place holders {1}, etc. with values from the passed in array (or a single value, if that's all there are). required arguments are rbString, the string containing the placeholders, and substitute. Values either an array or a single value containing the values to be substituted. note that the values are substituted sequentially, all {1} placeholders will be substituted using the first element in substitute. Values, {2} with the second, etc. DEPRECATED. only retained for backwards compatibility. please use messageFormat method instead
  • messageFormat returns string w/dynamic values substituted. performs MessageFormat operation on compound rb string. required arguments: pattern string to use as pattern for formatting, args array of "objects" to use as substitution values. optional argument is locale, java style locale ID, "th_TH", default is "en_US". for details about format options please see http://java.sun.com/j2se/1.4.2/docs/api/java/text/MessageFormat.html
  • verifyPattern verifies MessageFormat pattern. required argument is pattern a string holding the MessageFormat pattern to test. returns a boolean indicating if the pattern is ok or not

In addition, the remoteICU4J CFCs also have another public method:
  • getAvailableLocales returns an array of available locales. note that this method is only supplied as a convenience


PS: i've finally added a license.

17 Comments:

At 5/06/2006 12:03 PM, Anonymous Anonymous said...

Hi Paul,
You are not supporting the simple version that didn't require Java any longer, are you?

 
At 5/06/2006 12:50 PM, Blogger Paul Hastings said...

nope except as part of ray's blog. its kind of simple in any case.

why? are you having some kind of issue?

 
At 5/07/2006 2:05 AM, Anonymous Anonymous said...

Well, we are writing a blog engine, and I didn't want to require Java to run it. I was going to defer it to future versions, but I am already needing it for my own blog in Spanish :(
One of the things I wanted to use was the messageFormat method with the array of values that also formats dates, numbers, etc.
It would probably be good if it used the standard file format like the java one. I don't think the one for Ray's blog does that?

I don't know... what do you suggest?

 
At 5/07/2006 12:01 PM, Blogger Paul Hastings said...

no, ray's blog (and several other apps that use rb) uses the utf-8 version that i can't really recommend any longer. it's ok if you're just managing a few keys but gets out of hand when stuff gets complex. notepad isn't a good rb management tool.

the java that rbJava/javaRB CFCs uses can be core java (ie all the host needs is createobject permission). if you don't want to use the escaped unicode rb format, you can still use the messageFormat method from those CFCs, it accepts utf-8 strings fine. besides being a standard method of handling this kind of problem, it's super fast (at least i can't reliabliy measure how long it takes). i guess i could build a pure cf version but it would be a bit of work & probably not as fast as the java MessageFormat version. the only tricky bit w/using the java based version is that you have to cast cf numbers to acceptable java numeric datatypes (the CFC's messageFormat does that for you).

 
At 12/21/2007 4:21 AM, Blogger Code Fusion, LLC (Kevin Penny) said...

Something I just noticed and wondered if anyone else has run into it -

WHen i'm using the javaRB package - and specifically the 'messageFormat' function - and i'm passing in a variable that I've gotten from say a properties file or from the 'getRBString' call which contains a single quote in the value from the parameters file - my messageFormat leaves all {1} {2} placeholders in the string instead of converting them into their equivalent from the args array -

Once I 'escape' them, i.e. double them up in the properties file, then all is good, and the messageFormat does the necessary conversions just fine -

 
At 12/21/2007 2:57 PM, Blogger Paul Hastings said...

can you send me a simple rb & code snippet that illustrates this (i *think* i accounted for single & double quotes but maybe my logic wasn't good enough).

paul {at} sustainableGIS.com

and i'll see what i can do.

 
At 1/31/2008 5:36 AM, Blogger Code Fusion, LLC (Kevin Penny) said...

Hey Paul - Thanks for putting this out there to help me get moving on localization etc -

One thing I'm noticing in my implementation is is some instantiation errors at random intervals - and it seems to occur during heavy/stress testing. I'll get errors when attempting a call to the getRBString 'n exception occurred when instantiating a java object. " An exception occurred when instantiating a java object. The cause of this exception was that: ."
In particular this line:
rB.init(fis);

I have my properties files at '-F:\websites\jobs2web.com\model\resourcebundle\default_jobs2web.properties'
' and am wondering if the case is reading the files under heavy load - I notied there are no locks on these reads -
I'll be doing some more looking but thought if there was anything I should look out for -

I think i'm also going to install your test harness and hit that with the same stress levels to see if that does a similar thing or not (I'm using fusebox and coldspring to handle my cfc's, so i'm not quite sure who to point the finger at just yet - but will do some more digging).

 
At 1/31/2008 1:56 PM, Blogger Paul Hastings said...

while we did some load testing on this stuff, most apps just stuff the rb into app scope at some initialization. so we never really pounded it into submission.

 
At 2/01/2008 12:11 AM, Blogger Code Fusion, LLC (Kevin Penny) said...

Here's what we've found - and this could be other factors involved but thought I'd share :
Fusebox 4.1 app
Coldspring handling cfcs

javaRB called from a cfm page using application scope call - to the getRBString and messageformat functions.

Application started logging errors at the '13.65' requests per second timeframe - the Prior test at '6.97' rps showed no signs of issues.

So for most apps, this may be plenty well and good -

I'm going to test side by side with the creation of the object each page hit, vs. app caching of that to see the difference, but I'd hope the app cache would be much better.

Thanks again Paul - You Rock!

 
At 2/01/2008 10:35 AM, Blogger Paul Hastings said...

at that kind of loading i'd really load the rb into the app scope.

also what cf version? pre-cf8 CFC creation wasn't the fastest, another reason to load the rb into a shared scope.

there's no locks on the rb reads because this is a simple file/read, nothing should be changed by any callers.

 
At 2/08/2008 12:23 AM, Blogger Code Fusion, LLC (Kevin Penny) said...

@Paul -

yeah I have the javaRB in application scope - I've modified the javaRB file to initialize itself by having me pass in the initial 'resource bundle base properties file' into it. So then w/in the javaRB I use a 'getRBFile()' call when I need to get access to that base file - instead of having to pass it into each method like getRBString() (the rbFile argument is now not required and defalts to the getRBFile() call.

I understand the no need for a lock for file access now thanks -

This would be an example of the call now - (application.resourceBundle is what I'm calling the reference to the javaRB file)

application.resourceBundle.getRBString(rbKey:'lblLocation',rbLocale:'#attributes.locale#')

Seems to work most all the time - but I have a cftry catch around the getRBString method, which I'm catching and logging to a cflog. Cfcatch.details shows: "An exception occurred when instantiating a java object. The cause of this exception was that: ."

Seems to bet at rB.init(fis) - but its not consistent

 
At 2/08/2008 12:24 AM, Blogger Code Fusion, LLC (Kevin Penny) said...

@Paul
- sorry yeah - the environment is mx 7.02 multi instance install jrun enterprise.

 
At 10/12/2010 6:40 AM, Blogger Unknown said...

I'm getting a "wrong number of arguments" error when trying to run the messageFormat.cfm page.

I looks like it's bugging out in the "getICU4JLocale" method when it tries to return "variables.uLocale.newInstance(objArray)"

Any ideas?

 
At 10/12/2010 7:03 AM, Blogger Unknown said...

Nevermind, I figured it out, the version i'm using has variables.uLocale and variables.msgFormat reversed. Guess they changed after all.

 
At 10/12/2010 7:14 AM, Blogger Paul Hastings said...

@walnut

which version are you using? the "plain" or "remote" icu4j one?

 
At 10/15/2010 6:04 AM, Blogger Unknown said...

I tried both while I was testing. I am using icu4j-4_4_1_1.jar

After exploring a bit I reversed the array position of the constructors c[1] and c[2] it works fine now.

Thanks for all your work on this by the way.

 
At 1/20/2011 5:04 AM, Anonymous Walnut said...

The Remote One

 

Post a Comment

<< Home