I started to work in translating a Java program which has tens of dialog classes to deal with. I wonder how is it possible to create a parameter that is understood in all of those classes, without having to declare it in each one.
Actualy are two parameters. Tried with something like:
public Locale currentLocale = new Locale("en");
public ResourceBundle text = ResourceBundle.getBundle("MessagesBundle", currentLocale);
in the launching class of the program but had no luck.
Any tip?
without having to declare it in each one.
There are two general approaches:
Singleton pattern.
Store the reference in the thread using ThreadLocal.
Either way, you need to take a lot of caveats into account. Singletons doesn't work well in environments with multiple classloaders/JVM's and ThreadLocals doesn't work well when you spawn multiple threads yourself to process the business task. You need to understand those caveats very well before continuing.
The safe approach would be to create the object only once during application's startup in some front controller class and pass it as argument into the business/model objects whenever needed.
Set a Parent ResourceBundle
Use the ResourceBundle.setParent() method. If a specific key cannot be found in the current ResourceBundle it will search the parent ResourceBundle.
getBundle will automatically search for parents
As the JavaDoc for ResourceBundle.getBundle(String, Locale, ClassLoader) states, this automatically happens when you load a resource bundle for a specific Locale and a bundle with the same base name can be found without the locale suffix.
In your example the ResourceBundle will be loaded from a file (.properties extension is optional) named:
MessagesBundle_en.properties
But it will also look for a generic
MessagesBundle.properties
and set this as the parent. If you provide this generic file as well, it will be used as a default whenever a key in a specific locale bundle cannot be found.
Related
Eclipse calls the default (zero-argument) constructor when instantiating an extension point. I want to provide some arguments. I found a recommendation to use IExecutableExtension#setInitializationData but that appears to require specifying the argument values statically in XML. I need them to be dynamic. Another recommendation was to implement IExecutableExtensionFactory but that seems heavy handed. (The interface also seems pointless, as all it contains is a create() method.) I could add a method to set the values after creation but my class won't work correctly without them and they shouldn't be changed after creation, so forcing them to be provided at object creation time is preferable. This can't be a unique situation. What's the standard way of handling this?
If the arguments need to be "dynamic," where would they come from? How would Eclipse know what values to use? Extension point objects are created when the plugin is activated, so there is not much context available at that point.
I think the best option for you is to use IExecutableExtensionFactory after all. Your factory can implement IExecutableExtension to receive the XML configuration data and then be coded to create the objects based on that and any other context you can make available to it.
Depending on your needs, you could use Dynamic String Substitution Variables to insert certain context into your factory. See also Externalizing strings in plugin.xml for Eclipse plugin
I've got a "normal" Java PropertyResourceBundle that's based on a stack of .properties files. In a few places, it would be much more convenient to operate on a Properties object based on the correct translated .properties file instead of the ResourceBundle. Is there a convient way to "cast" the ResourceBundle to a Properties?
What'd I'd like is something like this:
Locale currentLocale = MagicLocaleFactory.getLocale();
ResourceBundle myResources = ResourceBundle.getBundle("MyResources", currentLocale);
Properties myProperties = myResources.magicallyProduceAPropertiesObject();
So afterwords, the myProperties object behaves as if it had been instantiated from the same .properties file that ResourceBundle.getBundle() went and found.
There are a few ways to do this "by hand"; for example, iterating though the set of key-value pairs of the ResourceBundle and installing them in a fresh Properties object, but I was hoping there was a better, or at least shorter, way to do it.
Edit:
To answer the obvious "but, WHY?" question, the case is this: we're retrofitting a long-existing desktop java program for i18n. The program is already pulling strings from a Properties object backed by a single .properties file. We're replacing the single file with multiple files and "promoting" the Properties object to a resource bundle. The problem, such as it is, is that the method to get a key on Properties is getProperty, whereas for ResourceBundle it's getString or getObject. Changing where the Properties object comes from before it gets passed around the program and has strings pulled out is very easy. Changing the actual method call is... less easy. Sure, you can pretty much search and replace for that, but then instead of just changing the one class that loads the properties, we have to touch, essentially, every single source file. (Yeah, there's a lot of text in this thing.) We were hoping we had missed a way to to use the ResourceBundle's multiple .properties with fallback mechanism without having to rewire the entire app. (Gratuitous Gandalf quote: "There never was much hope. Just a fool's hope.")
There is no built-in way to do what you want, but as you said, it isn't difficult to do it yourself: the basically have the same interface, and copying the keys and values from the bundle to the properties is easy.
There is a caveat, though. Although the interface is similar, the implementation is not: the resource bundle has an inheritance model which allows returning the value in foo.properties if it isn't defined in foo_fr_FR.properties and foo_fr.properties. So copying all the entries won't get you the same properties as it they had been loaded from the properties file. It will get the entries of the properties file and the properties of all its parents. But I guess you knew that since you're talking about a stack of properties files.
You haven't stated why you would prefer a Properties over a ResourceBundle, so it's hard to give you a better answer.
Another solution would be to simply load the properties yourself:
Properties props = new Properties();
properties.load(MyClass.class.getResourceAsStream("MyResources_" + locale + ".properties")
This way, you would get only the entries from the specific properties file.
There in none and for the good reason: ResourceBundle is meant to contain properties in different languages, so that it would load *and fall-back appropriate file – when you ask for let's say messages with de-AT locale (German, Austria), it will fall-back to German if it is able to find one or to your base properties file (messages.properties) in other case.
I don't really know what is your use case here (maybe it is simply achievable in a different way?) but it seems that the only way is to read directly from properties file and unfortunately imitate the mechanism I mentioned above (the tricky part are Portuguese Brazil and Chinese Traditional which should not fall back to their "default" languages ("pt" and "zh" respectively)).
Another solution is iterate on keys and puts all pair in a properties object, You can use convertBundleToProperties:
static Properties convertBundleToProperties(ResourceBundle resource) {
Properties properties = new Properties();
Enumeration<String> keys = resource.getKeys();
while (keys.hasMoreElements()) {
String key = keys.nextElement();
properties.put(key, resource.getString(key));
}
return properties;
}
What is the prefered way to manage and distribute configurations to many different class instances of differing class types (in Java)?
In other words, I have an Application class that stores config options in a Configuration class in a static variable. This allows all the other classes in the application to simply call Application.config to fetch config values.
I'd like to allow there to be multiple instances of Application with associated Configuration's per Application instance. This is were I am lost... Is my only option to cascade a Configuration reference to every other class (via constructors and setters)? Or is there another way?
Seems like a single configuration for an Application instance and all it's supporting instances isn't unreasonable, but the requirement to cascade the single configuration reference just seems like such a burden.
Thanks,
Chenz
It sounds like you just found out that what you thought was global state is not really global.
There are several solutions,
the one you outlined,
use a dependency injection framework like Guice to scope configuration.
use ClassLoader voodoo to create multiple "global" configurations in the same VM.
The best solution is really to thread your configuration through manually or via DI, but since you asked for other options, the third solution is to change your main class to create a UrlClassLoader per-instance of the application that points to your jars. This allows you to have multiple different versions of the Application class and its associated Condiguration class side-by-side in the same VM.
EDIT:
To do the classloader voodoo, you main looks something like this
URLClassLoader application1Loader = new ClassLoader(/* URLs to common Jars and the config files for instance 1*/);
URLClassLoader application2Loader = new ClassLoader(/* URLs to common Jars and the config files for instance 2*/);
// Assuming you have a wrapper for your application that you can run() to get it going
// in its own thread via reflection.
Class<? extends Runnable> app1Class = application1Loader.loadClass("my.pkg.MyApplicaion")
.asSubclass(Runnable.class);
// newInstance() is problematic but I want this code to be brief.
app1Class.newInstance().run();
Class<? extends Runnable> app2Class = application2Loader.loadClass("my.pkg.MyApplicaion")
.asSubclass(Runnable.class);
app2Class.newInstance().run();
UPDATE: The my.pkg.MyApplicaion class must not be in the JVM's classpath.
I generally use Spring and define my config in associated XML files, which I load wherever needed using the spring API's.
A quick and dirty method to pass around app config without rewriting all your API's would be to use the singleton pattern. You could get a reference to the config using AppConfig.getInstance() where ever required.
if your objects know what Application they belong to, no problem: just make configuration be non-static.
If your Application instances are separate threads, no problem: put configuration on ThreadLocal
If there is some kind of namespace associated with each Application, and child objects know about it, no problem: store a static Map somewhere
if none of the above holds, you're kinda screwed...
I'm at the point in my first real application where I am adding in the user settings. I'm using Java and being very OO (and trying to keep it that way) so here are my ideas:
Load everything in the main() and
pass it all 'down the line' to the
required objects (array)
Same as above, but just pass the
object that contains the data down
the line
Load each individual setting as
needed within the various classes.
I understand some of the basic pros and cons to each method (i.e. time vs. size) but I'm looking for some outside input as to what practices they've successfully used in the past.
Someone should stand up for the purported Java standard, the Preferences API... and it's most recent incarnation in JDK6. Edited to add, since the author seems to savvy XML, this is more appropriate than before. Thought I believe you can work XML juju with Properties too, should the spirit take you.
Related on SO: Preferences API vs. Apache solution, Is a master preferences class a good idea?
(well, that's about all the standing up I'm willing to do.)
Use a SettingsManager class or something similar that is used to abstract getting all settings data. At each point in the code where you need a setting you query the SettingsManager class - something like:
int timeout = SettingsManager.GetSetting("TimeoutSetting");
You then delegate all of the logic for how settings are fetched to this single manager class, whose implementation you can change / optimize as needed. For instance, you could implement the SettingsManager to fetch settings from a config file, or a database, or some other data store, periodically refresh the settings, handle caching of settings that are expensive to retrieve, etc. The code using the settings remains blissfully unaware of all of these implementaton decisions.
For maximum flexibility you can use an interface instead of an actual class, and have different setting managers implement the interface: you can swap them in and out as needed at some central point without having to change the underlying code at all.
In .NET there is a fairly rich set of existing configuration classes (in the System.Configuration) namespace that provide this sort of thing, and it works out quite well.
I'm not sure of the Java equivalent, but it's a good pattern.
Since configuration / settings are typically loaded once (at startup; or maybe a few times during the program's runtime. In any way, we're not talking about a very frequent / time-consuming process), I would prefer simplicity over efficiency.
That rules out option number (3). Configuration-loading will be scattered all over the place.
I'm not entirely sure what the difference is between (1) and (2) in your list. Does (1) mean "passing discreet parameters" and (2) mean "passing an object containing the entire configuration"? If so, I'd prefer (2) over (1).
The rule of thumb here is that you should keep things simple and concentrated. The advantage of reading configuration in one place is that it gives you better control in case the source of the configuration changes at some point.
Here is a tutorial on the Properties class. From the Javadocs (Properties):
The Properties class represents a
persistent set of properties. The
Properties can be saved to a stream or
loaded from a stream. Each key and its
corresponding value in the property
list is a string.
A property list can contain another
property list as its "defaults"; this
second property list is searched if
the property key is not found in the
original property list.
The tutorial gives the following example instantiation for a typical usage:
. . .
// create and load default properties
Properties defaultProps = new Properties();
FileInputStream in = new FileInputStream("defaultProperties");
defaultProps.load(in);
in.close();
// create application properties with default
Properties applicationProps = new Properties(defaultProps);
// now load properties from last invocation
in = new FileInputStream("appProperties");
applicationProps.load(in);
in.close();
. . .
You could, of course, also roll your own system fairly directly using a file-based store and an XML or YAML parser. Good luck!
We have recently started using JSR-330 dependency injection (using Guice from SVN) and found that it was possible to read in a Properties file (or any other map) and bind it inside Guice in the module in the startup code so that the
#Inject #Named("key") String value
string was injected with the value corresponding to the key when that particular code was called. This is the most elegant way I have ever seen for solving this problem!
You do not have to haul configuration objects around your code or sprinkle all kinds of magic method calls in each and every corner of the code to get the values - you just mention to Guice you need it, and it is there.
Note: I've had a look at Guice, Weld (Seam-based) and Spring which all provide injection, because we want JSR-330 in our own code, and I like Guice the best currently. I think the reason is because Guice is the clearest in its bindings as opposed to the under-the-hood magic happening with Weld.
We had a lot of strings which contained the same sub-string, from sentences about checking the log or how to contact support, to branding-like strings containing the company or product name. The repetition was causing a few issues for ourselves (primarily typos or copy/paste errors) but it also causes issues in that it increases the amount of text our translator has to translate.
The solution I came up with went something like this:
public class ExpandingResourceBundleControl extends ResourceBundle.Control {
public static final ResourceBundle.Control EXPANDING =
new ExpandingResourceBundleControl();
private ExpandingResourceBundleControl() { }
#Override
public ResourceBundle newBundle(String baseName, Locale locale, String format,
ClassLoader loader, boolean reload)
throws IllegalAccessException, InstantiationException, IOException {
ResourceBundle inner = super.newBundle(baseName, locale, format, loader, reload);
return inner == null ? null : new ExpandingResourceBundle(inner, loader);
}
}
ExpandingResourceBundle delegates to the real resource bundle but performs conversion of {{this.kind.of.thing}} to look up the key in the resources.
Every time you want to get one of these, you have to go:
ResourceBundle.getBundle("com/acme/app/Bundle", EXPANDING);
And this works fine -- for a while.
What eventually happens is that some new code (in our case autogenerated code which was spat out of Matisse) looks up the same resource bundle without specifying the custom control. This appears to be non-reproducible if you write a simple unit test which calls it with and then without, but it occurs when the application is run for real. Somehow the cache inside ResourceBundle ejects the good value and replaces it with the broken one. I am yet to figure out why and Sun's jar files were compiled without debug info so debugging it is a chore.
My questions:
Is there some way of globally setting the default ResourceBundle.Control that I might not be aware of? That would solve everything rather elegantly.
Is there some other way of handling this kind of thing elegantly, perhaps without tampering with the ResourceBundle classes at all?
I think this is a fundamental flaw in the way ResourceBundles are designed to function: keys that reference other keys automatically violate the DRY (don't repeat yourself) principle. The way I got around this was similar to your method: create a ReflectiveResourceBundle class that allows you to specify Resource keys in the messages using EL notation.
THE WRONG WAY:
my.name.first=Bob
my.name.last=Smith
my.name.full=Bob Smith
THE RIGHT WAY:
my.name.first=Bob
my.name.last=Smith
my.name.full=${my.name.first} ${my.name.last}
I've uploaded the code to GitHub so you or anyone else can download it. Additionally, I've added some sample code for anyone using the Stripes Framework (http://www.stripesframework.org/) to get you quickly up-and-running.
The trick to getting this to work with standard JSTL fmt taglibs was to set up an interceptor that replaced the HttpServletRequest's resource with our own. The code looks something like this:
ResourceBundle bundle = MyStaticResourceHoldingTheBundle.getBundle();
Config.set(request, Config.FMT_LOCALIZATION_CONTEXT, new LocalizationContext(bundle, locale));
Take a look at the stripes.interceptor package in the link above for more details.
If the string repetitions are localized in the sense that you know a certain string will be repeated but only within the same project such that sharing resource bundles is not a design nightmare, then you might consider breaking the strings down into multiple key-value parts. Separate the parts that repeat from those that do not and reuse the repeated parts. For example, lets say you have the following two strings you need to display:
"The Red-capped Robin is a small passerine bird native to Australia. "
"The Red-capped Robin is found in dryer regions across much of the continent."
The resource bundle could be as follows:
robin.name=The Red-capped Robin
robin.native=is a small passerine bird native to Australia.
robin.region=is found in dryer regions across much of the continent.
and then combine the required parts where needed bundle.getString("robin.name")+bundle.getString(robin.native).
One thing you need to be careful about though is that the grammar rules like subject predicate order etc. might not be the same in all languages. So you would need to be a little careful when splitting sentences.